From 5605bee8649b48559c2b630f281aa3edd91c6d0e Mon Sep 17 00:00:00 2001 From: Seggan Date: Wed, 28 Feb 2024 19:38:02 -0500 Subject: [PATCH] Update to new units --- .../addoncommunity/galactifun/Constants.kt | 3 +- .../galactifun/api/objects/CelestialObject.kt | 6 +-- .../galactifun/api/objects/PlanetaryObject.kt | 15 ++++--- .../galactifun/api/objects/Star.kt | 2 +- .../api/objects/properties/Orbit.kt | 40 ++++++++++++------- .../galactifun/base/BaseUniverse.kt | 4 +- .../galactifun/base/objects/earth/Earth.kt | 4 +- .../galactifun/base/objects/earth/Moon.kt | 4 +- .../addoncommunity/galactifun/units/Angle.kt | 2 +- .../galactifun/units/UnitsOfMeasure.kt | 10 +++-- .../test/api/objects/properties/OrbitTest.kt | 16 ++++---- .../io/github/seggan/uom/UomProcessor.kt | 15 ++++--- .../main/kotlin/io/github/seggan/uom/UoM.kt | 33 ++++++++++++--- 13 files changed, 98 insertions(+), 56 deletions(-) diff --git a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/Constants.kt b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/Constants.kt index 44f2a62..bd2a225 100644 --- a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/Constants.kt +++ b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/Constants.kt @@ -1,5 +1,6 @@ package io.github.addoncommunity.galactifun +import io.github.addoncommunity.galactifun.units.Acceleration.Companion.metersPerSecondSquared import org.bukkit.Location import org.bukkit.World @@ -10,7 +11,7 @@ object Constants { const val KM_PER_AU = 1.495978707e8 const val GRAVITATIONAL_CONSTANT = 6.674e-11 - const val EARTH_GRAVITY = 9.81 + val EARTH_GRAVITY = 9.81.metersPerSecondSquared /** * The maximum radix for the [Int.toString] and [String.toInt] functions. diff --git a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/api/objects/CelestialObject.kt b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/api/objects/CelestialObject.kt index c507697..9e0e987 100644 --- a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/api/objects/CelestialObject.kt +++ b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/api/objects/CelestialObject.kt @@ -5,6 +5,7 @@ import io.github.addoncommunity.galactifun.api.objects.properties.Orbit import io.github.addoncommunity.galactifun.units.Angle.Companion.degrees import io.github.addoncommunity.galactifun.units.Distance import io.github.addoncommunity.galactifun.units.Mass +import io.github.addoncommunity.galactifun.units.Velocity.Companion.metersPerSecond import io.github.addoncommunity.galactifun.util.LazyDouble import io.github.thebusybiscuit.slimefun4.libraries.dough.items.CustomItemStack import io.github.thebusybiscuit.slimefun4.utils.ChatUtils @@ -22,18 +23,17 @@ sealed class CelestialObject(name: String, baseItem: ItemStack) { abstract fun distanceTo(other: CelestialObject, time: Instant): Distance - abstract val mass: Mass abstract val radius: Distance val gravitationalParameter by LazyDouble { Constants.GRAVITATIONAL_CONSTANT * mass.kilograms } - val escapeVelocity by LazyDouble { sqrt(2 * Constants.GRAVITATIONAL_CONSTANT * mass.kilograms / radius.meters) } + val escapeVelocity by lazy { sqrt(2 * Constants.GRAVITATIONAL_CONSTANT * mass.kilograms / radius.meters).metersPerSecond } val parkingOrbit: Orbit by lazy { Orbit( parent = this, semimajorAxis = radius * 1.1, eccentricity = Orbit.TINY_ECCENTRICITY, - longitudeOfPeriapsis = 0.0.degrees, + longitudeOfPeriapsis = 0.degrees, timeOfPeriapsis = Instant.fromEpochMilliseconds(0) ) } diff --git a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/api/objects/PlanetaryObject.kt b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/api/objects/PlanetaryObject.kt index aed7f23..91b05ef 100644 --- a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/api/objects/PlanetaryObject.kt +++ b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/api/objects/PlanetaryObject.kt @@ -8,12 +8,14 @@ import io.github.addoncommunity.galactifun.api.objects.properties.visVivaEquatio import io.github.addoncommunity.galactifun.core.managers.PlanetManager import io.github.addoncommunity.galactifun.units.Distance import io.github.addoncommunity.galactifun.units.Distance.Companion.meters +import io.github.addoncommunity.galactifun.units.Velocity +import io.github.addoncommunity.galactifun.units.Velocity.Companion.metersPerSecond +import io.github.addoncommunity.galactifun.units.abs import io.github.addoncommunity.galactifun.units.cos import io.github.seggan.kfun.location.plus import kotlinx.datetime.Instant import org.bukkit.Location import org.bukkit.inventory.ItemStack -import kotlin.math.abs import kotlin.math.sqrt abstract class PlanetaryObject(name: String, baseItem: ItemStack) : CelestialObject(name, baseItem) { @@ -35,7 +37,7 @@ abstract class PlanetaryObject(name: String, baseItem: ItemStack) : CelestialObj } override fun distanceTo(other: CelestialObject, time: Instant): Distance { - if (other == this) return 0.0.meters + if (other == this) return 0.meters if (other is Star) { if (star == other) { var dist = orbit.radius(time) @@ -62,8 +64,8 @@ abstract class PlanetaryObject(name: String, baseItem: ItemStack) : CelestialObj } } - fun getDeltaVForTransferTo(other: PlanetaryObject, time: Instant): Double { - if (this == other) return 0.0 + fun getDeltaVForTransferTo(other: PlanetaryObject, time: Instant): Velocity { + if (this == other) return 0.metersPerSecond val thisParents = generateSequence(this as CelestialObject) { if (it is PlanetaryObject) it.orbit.parent else null }.toList() @@ -75,7 +77,7 @@ abstract class PlanetaryObject(name: String, baseItem: ItemStack) : CelestialObj }.toList() if (this in otherParents) { var height = other.parkingOrbit - var dV = 0.0 + var dV = 0.metersPerSecond for (obj in otherParents) { if (obj == this) break dV += abs( @@ -90,7 +92,8 @@ abstract class PlanetaryObject(name: String, baseItem: ItemStack) : CelestialObj dV += height.hohmannTransfer(parkingOrbit, time) return dV } else { - val commonParent = thisParents.firstOrNull { it in otherParents } ?: return Double.POSITIVE_INFINITY + val commonParent = thisParents.firstOrNull { it in otherParents } + ?: return Double.POSITIVE_INFINITY.metersPerSecond val thisSibling = thisParents[thisParents.indexOf(commonParent) - 1] as PlanetaryObject val otherSibling = otherParents[otherParents.indexOf(commonParent) - 1] as PlanetaryObject val thisDeltaV = abs( diff --git a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/api/objects/Star.kt b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/api/objects/Star.kt index 9d42fba..e098f8e 100644 --- a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/api/objects/Star.kt +++ b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/api/objects/Star.kt @@ -17,7 +17,7 @@ class Star( override fun distanceTo(other: CelestialObject, time: Instant): Distance { return when (other) { - this -> 0.0.meters + this -> 0.meters is Star -> position.distanceTo(other.position) else -> other.distanceTo(this, time) } diff --git a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/api/objects/properties/Orbit.kt b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/api/objects/properties/Orbit.kt index 32fdc52..cbc1b17 100644 --- a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/api/objects/properties/Orbit.kt +++ b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/api/objects/properties/Orbit.kt @@ -6,6 +6,7 @@ import io.github.addoncommunity.galactifun.pluginInstance import io.github.addoncommunity.galactifun.units.* import io.github.addoncommunity.galactifun.units.Angle.Companion.radians import io.github.addoncommunity.galactifun.units.Distance.Companion.meters +import io.github.addoncommunity.galactifun.units.Velocity.Companion.metersPerSecond import io.github.addoncommunity.galactifun.units.coordiantes.CartesianVector import io.github.addoncommunity.galactifun.units.coordiantes.PolarVector import io.github.addoncommunity.galactifun.util.Either @@ -13,7 +14,6 @@ import io.github.addoncommunity.galactifun.util.LazyDouble import kotlinx.datetime.Instant import java.util.* import kotlin.math.PI -import kotlin.math.abs import kotlin.math.atan2 import kotlin.math.sqrt import kotlin.time.Duration @@ -98,18 +98,23 @@ data class Orbit( return ((meanAnomalyB - meanAnomalyA).radians / meanMotion).seconds } - fun hohmannTransfer(target: Orbit, time: Instant): Double { + fun hohmannTransfer(target: Orbit, time: Instant): Velocity { val parkingR = radius(time) val targetR = target.radius(time) val transferA = (parkingR + targetR) / 2.0 val mu = parent.gravitationalParameter - val firstManeuver = abs(visVivaEquation(mu, parkingR, transferA) - visVivaEquation(mu, parkingR, semimajorAxis)) - val secondManeuver = - abs(visVivaEquation(mu, targetR, target.semimajorAxis) - visVivaEquation(mu, targetR, transferA)) + val firstManeuver = abs( + visVivaEquation(mu, parkingR, transferA) + - visVivaEquation(mu, parkingR, semimajorAxis) + ) + val secondManeuver = abs( + visVivaEquation(mu, targetR, target.semimajorAxis) - + visVivaEquation(mu, targetR, transferA) + ) return firstManeuver + secondManeuver } - fun arbitraryTransfer(targetOrbit: Orbit, time: Instant): Double { + fun arbitraryTransfer(targetOrbit: Orbit, time: Instant): Velocity { require(parent == targetOrbit.parent) { "Both orbits must have the same parent" } val parking = position(time) @@ -146,7 +151,7 @@ data class Orbit( val transferOrbit = transfer.value val transferVel = transferOrbit.velocity(targetTime) val targetVel = targetOrbit.velocity(targetTime) - val burn2 = transferVel.distanceTo(targetVel).meters + val burn2 = transferVel.distanceTo(targetVel).meters.metersPerSecond val burn1 = abs( visVivaEquation(parent.gravitationalParameter, parking.radius, transferOrbit.semimajorAxis) - @@ -177,7 +182,7 @@ data class Orbit( ) val intersectTrueAnomaly = intersectLongitude - transferOrbit.longitudeOfPeriapsis val mean = meanAnomalyFromTrueAnomaly(transferOrbit.eccentricity, intersectTrueAnomaly) - return Transfer(Either.Left(transferOrbit), transferOrbit.timeOfFlight(0.0.radians, mean)) + return Transfer(Either.Left(transferOrbit), transferOrbit.timeOfFlight(0.radians, mean)) } else { val distance = parking.distanceTo(target) val transferBrachistochrone = brachistochroneTransfer(distance) @@ -188,7 +193,12 @@ data class Orbit( private const val MAX_ITERATIONS = 512 -private fun getAnomalyDifference(transfer: Transfer, time: Instant, targetOrbit: Orbit, intersectLongitude: Angle): Angle { +private fun getAnomalyDifference( + transfer: Transfer, + time: Instant, + targetOrbit: Orbit, + intersectLongitude: Angle +): Angle { val targetTime = time + transfer.tof val target = targetOrbit.position(targetTime) return abs(target.angle + targetOrbit.longitudeOfPeriapsis - intersectLongitude).standardForm @@ -199,8 +209,8 @@ private tailrec fun kelpersEquation(m: Angle, e: Double, guessE: Angle = m): Ang return if (abs(nextGuess - guessE).radians < 1e-6) nextGuess else kelpersEquation(m, e, nextGuess) } -fun visVivaEquation(mu: Double, r: Distance, a: Distance): Double = - sqrt(mu * (2 / r.meters - 1 / a.meters)) +fun visVivaEquation(mu: Double, r: Distance, a: Distance): Velocity = + sqrt(mu * (2 / r.meters - 1 / a.meters)).metersPerSecond // https://math.stackexchange.com/a/407425/1291722 // https://www.desmos.com/calculator/mdifr3y167 @@ -225,14 +235,14 @@ private fun oneTangentTransferOrbit( private fun brachistochroneTransfer( distance: Distance, - acceleration: Double = Constants.EARTH_GRAVITY / 16 + acceleration: Acceleration = Constants.EARTH_GRAVITY / 16 ): BrachistochroneTransfer { - val time = 2 * sqrt(distance.meters / acceleration) - return BrachistochroneTransfer(acceleration * time, time.seconds) + val time = 2.seconds * sqrt(distance.meters / acceleration.metersPerSecondSquared) + return BrachistochroneTransfer(acceleration * time, time) } private data class BrachistochroneTransfer( - val deltaV: Double, + val deltaV: Velocity, val time: Duration ) diff --git a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/base/BaseUniverse.kt b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/base/BaseUniverse.kt index 05e3159..11d0c5d 100644 --- a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/base/BaseUniverse.kt +++ b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/base/BaseUniverse.kt @@ -14,9 +14,9 @@ object BaseUniverse { val sun = Star( name = "Sun", mass = 1.989e30.kilograms, - radius = 695700.0.kilometers, + radius = 695700.kilometers, // The sun is at the center of the universe, yay! - position = SphericalVector(0.0.radians, 0.0.radians, 0.0.meters) + position = SphericalVector(0.radians, 0.radians, 0.meters) ) val earth = Earth() diff --git a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/base/objects/earth/Earth.kt b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/base/objects/earth/Earth.kt index 84909e7..c177197 100644 --- a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/base/objects/earth/Earth.kt +++ b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/base/objects/earth/Earth.kt @@ -20,13 +20,13 @@ class Earth : PlanetaryWorld("Earth", ItemStack(Material.GRASS_BLOCK)) { override val orbit = Orbit( parent = BaseUniverse.sun, - semimajorAxis = 1.0.au, + semimajorAxis = 1.au, eccentricity = 0.0167, longitudeOfPeriapsis = 288.1.degrees, timeOfPeriapsis = Instant.parse("2024-01-01T00:00:00Z") ) override val mass = 5.972e24.kilograms - override val radius = 6371.0.kilometers + override val radius = 6371.kilometers override val dayCycle = DayCycle.EARTH_LIKE override val atmosphere = Atmosphere.EARTH_LIKE diff --git a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/base/objects/earth/Moon.kt b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/base/objects/earth/Moon.kt index f72008a..9b22bcc 100644 --- a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/base/objects/earth/Moon.kt +++ b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/base/objects/earth/Moon.kt @@ -20,9 +20,9 @@ class Moon : AlienWorld("Moon", ItemStack(Material.END_STONE)) { override val dayCycle = DayCycle(29.days + 12.hours) override val orbit = Orbit( parent = BaseUniverse.earth, - semimajorAxis = 384399.0.kilometers, + semimajorAxis = 384399.kilometers, eccentricity = 0.0549, - longitudeOfPeriapsis = 0.0.degrees, // the argument changes over the course of the year + longitudeOfPeriapsis = 0.degrees, // the argument changes over the course of the year timeOfPeriapsis = Instant.parse("2024-01-13T10:36:00Z") ) override val mass = 7.346e22.kilograms diff --git a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/units/Angle.kt b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/units/Angle.kt index 3703e5d..aebed5b 100644 --- a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/units/Angle.kt +++ b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/units/Angle.kt @@ -6,7 +6,7 @@ import io.github.seggan.uom.Measure import kotlin.math.PI @Measure(base = "radians") -@AlternateUnit(name = "degrees", ratio = 180.0 / Math.PI) +@AlternateUnit(unit = "degrees", ratio = 180.0 / Math.PI) private class AAngle val Angle.standardForm: Angle diff --git a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/units/UnitsOfMeasure.kt b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/units/UnitsOfMeasure.kt index 904f1f2..cf8e4bf 100644 --- a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/units/UnitsOfMeasure.kt +++ b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/units/UnitsOfMeasure.kt @@ -13,13 +13,13 @@ import io.github.seggan.uom.MultipliesTo import kotlin.time.Duration @Measure(base = "meters") -@AlternateUnit(name = "lightYears", ratio = Constants.KM_PER_LY * 1000) -@AlternateUnit(name = "kilometers", ratio = 1000.0) -@AlternateUnit(name = "au", ratio = Constants.KM_PER_AU * 1000) +@AlternateUnit(unit = "lightYears", ratio = Constants.KM_PER_LY * 1000) +@AlternateUnit(unit = "kilometers", ratio = 1000.0) +@AlternateUnit(unit = "au", ratio = Constants.KM_PER_AU * 1000) private class ADistance @Measure(base = "kilograms") -@AlternateUnit(name = "pounds", ratio = 2.20462) +@AlternateUnit(unit = "pounds", ratio = 2.20462) private class AMass @Measure(base = "metersPerSecond") @@ -34,5 +34,7 @@ operator fun Velocity.div(time: Duration): Acceleration = (metersPerSecond / tim operator fun Acceleration.times(time: Duration): Velocity = (metersPerSecondSquared * time.doubleSeconds).metersPerSecond @Measure(base = "newtons") +@AlternateUnit(unit = "kilonewtons", ratio = 1000.0) +@AlternateUnit(unit = "meganewtons", ratio = 1_000_000.0) @DividesTo(other = AAcceleration::class, quotient = AMass::class) private class AForce \ No newline at end of file diff --git a/plugin/src/test/kotlin/io/github/addoncommunity/test/api/objects/properties/OrbitTest.kt b/plugin/src/test/kotlin/io/github/addoncommunity/test/api/objects/properties/OrbitTest.kt index a34a26e..0376587 100644 --- a/plugin/src/test/kotlin/io/github/addoncommunity/test/api/objects/properties/OrbitTest.kt +++ b/plugin/src/test/kotlin/io/github/addoncommunity/test/api/objects/properties/OrbitTest.kt @@ -25,9 +25,9 @@ class OrbitTest : CommonTest() { fun setUpOrbit() { orbit = Orbit( parent = BaseUniverse.sun, - semimajorAxis = 1.0.au, + semimajorAxis = 1.au, eccentricity = Orbit.TINY_ECCENTRICITY, - longitudeOfPeriapsis = 0.0.degrees, + longitudeOfPeriapsis = 0.degrees, timeOfPeriapsis = EPOCH ) Orbit.TIME_SCALE = 1.0 @@ -53,21 +53,21 @@ class OrbitTest : CommonTest() { orbit.parent.gravitationalParameter, orbit.radius(orbit.timeOfPeriapsis), orbit.semimajorAxis - ) + ).metersPerSecond vel.polar.angle.standardForm.degrees shouldBeRoughly expectedAngle.degrees } - testVelocityAt(orbit.timeOfPeriapsis, 90.0.degrees) - testVelocityAt(orbit.timeOfPeriapsis + orbit.period, 90.0.degrees) - testVelocityAt(orbit.timeOfPeriapsis + orbit.period / 2, 270.0.degrees) - testVelocityAt(orbit.timeOfPeriapsis + orbit.period / 4, 180.0.degrees) + testVelocityAt(orbit.timeOfPeriapsis, 90.degrees) + testVelocityAt(orbit.timeOfPeriapsis + orbit.period, 90.degrees) + testVelocityAt(orbit.timeOfPeriapsis + orbit.period / 2, 270.degrees) + testVelocityAt(orbit.timeOfPeriapsis + orbit.period / 4, 180.degrees) } @Test fun testTimeOfFlight() { val time = orbit.timeOfPeriapsis val timeOfFlight = orbit.timeOfFlight( - 0.0.radians, + 0.radians, orbit.meanAnomaly(time + orbit.period / 4) ) timeOfFlight.inWholeDays shouldBe 91 diff --git a/uom-processor/src/main/kotlin/io/github/seggan/uom/UomProcessor.kt b/uom-processor/src/main/kotlin/io/github/seggan/uom/UomProcessor.kt index 9d0edab..e1ff184 100644 --- a/uom-processor/src/main/kotlin/io/github/seggan/uom/UomProcessor.kt +++ b/uom-processor/src/main/kotlin/io/github/seggan/uom/UomProcessor.kt @@ -261,12 +261,15 @@ private fun TypeSpec.Builder.addScalarOperator( baseUnit: String, className: ClassName ): TypeSpec.Builder { - val operatorFun = FunSpec.builder(operator) - .addModifiers(KModifier.OPERATOR) - .addParameter("scalar", Double::class) - .returns(className) - .addStatement("return %T(%L %L scalar)", className, baseUnit, symbol) - return addFunction(operatorFun.build()) + for (type in listOf(Double::class, Int::class)) { + val operatorFun = FunSpec.builder(operator) + .addModifiers(KModifier.OPERATOR) + .addParameter("scalar", type) + .returns(className) + .addStatement("return %T(%L %L scalar)", className, baseUnit, symbol) + addFunction(operatorFun.build()) + } + return this } fun KSDeclaration.getBaseUnit(): String? { diff --git a/uom/src/main/kotlin/io/github/seggan/uom/UoM.kt b/uom/src/main/kotlin/io/github/seggan/uom/UoM.kt index cd84a28..601bd98 100644 --- a/uom/src/main/kotlin/io/github/seggan/uom/UoM.kt +++ b/uom/src/main/kotlin/io/github/seggan/uom/UoM.kt @@ -2,17 +2,40 @@ package io.github.seggan.uom import kotlin.reflect.KClass -@Repeatable +/** + * A measure. + * + * @param base The base unit for this measure. + */ @Target(AnnotationTarget.CLASS) -annotation class MultipliesTo(val other: KClass<*>, val product: KClass<*>) +annotation class Measure(val base: String) +/** + * An alternate unit for a measure. + * + * @param unit The alternate unit. + * @param ratio How many alternate units are in the base unit. + */ @Repeatable @Target(AnnotationTarget.CLASS) -annotation class DividesTo(val other: KClass<*>, val quotient: KClass<*>) +annotation class AlternateUnit(val unit: String, val ratio: Double) +/** + * A measure that multiplies to another measure. + * + * @param other The other measure. + * @param product The product measure. + */ +@Repeatable @Target(AnnotationTarget.CLASS) -annotation class Measure(val base: String) +annotation class MultipliesTo(val other: KClass<*>, val product: KClass<*>) +/** + * A measure that divides to another measure. + * + * @param other The other measure. + * @param quotient The quotient measure. + */ @Repeatable @Target(AnnotationTarget.CLASS) -annotation class AlternateUnit(val name: String, val ratio: Double) +annotation class DividesTo(val other: KClass<*>, val quotient: KClass<*>)