All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.io.nacular.measured.units2.Measure2.kt Maven / Gradle / Ivy

There is a newer version: 0.4.1
Show newest version
package io.nacular.measured.units2

import io.nacular.measured.units.*
import kotlin.jvm.JvmName
import kotlin.math.roundToInt

/**
 * Created by Nicholas Eddy on 6/14/20.
 */
interface Measure2 {
    val amount: Double

    infix fun  `as`(other: A): Measure2

    infix fun  `in`(other: A): Double

    operator fun unaryMinus(): Measure2

    /**
     * Multiply this by a scalar value, used for things like "double this distance",
     * "1.5 times the speed", etc
     */
    operator fun times(other: Number): Measure2

    /**
     * Divide this by a scalar, used for things like "halve the speed"
     */
    operator fun div(other: Number): Measure2

    fun roundToInt(): Measure2

    fun print(units: T): String
}

class ConcreteMeasure(override val amount: Double, val units: T): Measure2 {
    /**
     * Convert this type into another compatible type.
     * Type must share parent
     * (eg Mile into Kilometer, because they both are made from Distance)
     */
    override infix fun  `as`(other: A): Measure2 = if (units == other) this else ConcreteMeasure(this `in` other, other)

    override infix fun  `in`(other: A): Double = if (units == other) amount else  amount * (units.ratio / other.ratio)

    override operator fun unaryMinus(): Measure2 = ConcreteMeasure(-amount, units)

    /**
     * Multiply this by a scalar value, used for things like "double this distance",
     * "1.5 times the speed", etc
     */
    override operator fun times(other: Number): Measure2 = ConcreteMeasure(amount * other.toDouble(), units)

    /**
     * Divide this by a scalar, used for things like "halve the speed"
     */
    override operator fun div(other: Number): Measure2 = ConcreteMeasure(amount / other.toDouble(), units)

    override fun roundToInt(): Measure2 = ConcreteMeasure(amount.roundToInt().toDouble(), units)

//    override fun equals(other: Any?): Boolean {
//        if (this === other) return true
//        if (other !is Measure2<*>) return false
////        if (this.amount == 0.0 && other.amount == 0.0) return true TODO: Should this be true?
//
//        val resultUnits = minOf(Units, (other as Measure2).Units)
//
//        val a = this  `in` resultUnits
//        val b = other `in` resultUnits
//
//        return a == b
//    }

    override fun hashCode(): Int {
        return (amount * units.ratio).hashCode()
    }

    override fun print(units: T) = "$amount${this.units.measureSuffix()}"

    override fun toString(): String = print(units)

    fun inline(): InlineMeasure = InlineMeasure(amount * units.ratio)
}

inline class InlineMeasure(override val amount: Double): Measure2 {
    override infix fun  `as`(other: A): Measure2 = InlineMeasure(amount / other.ratio)

    override infix fun  `in`(other: A): Double = amount / other.ratio

    override fun unaryMinus(): Measure2 = InlineMeasure(-amount)

    override fun times(other: Number): Measure2 = InlineMeasure(amount * other.toDouble())

    override fun div(other: Number): Measure2 = InlineMeasure(amount / other.toDouble())

    override fun roundToInt(): Measure2 = InlineMeasure(amount.roundToInt().toDouble())

    override fun print(units: T) = ConcreteMeasure(amount / units.ratio, units).toString()
}

/** A * (B / A) */                        operator fun  A.times(other: UnitsRatio): ConcreteMeasure         = this.ratio / other.denominator.ratio * other.numerator
/** A / (A / B) == A * (B / A) */         operator fun  A.div  (other: UnitsRatio): ConcreteMeasure         = this * other.reciprocal

/** (A * B) / A */       @JvmName("div1") operator fun            UnitsProduct.div(other: A                 ): ConcreteMeasure                = first.ratio  / other.ratio * second
/** (A * B) / B */       @JvmName("div2") operator fun            UnitsProduct.div(other: B                 ): ConcreteMeasure                = second.ratio / other.ratio * first
/** (A * A) / A */       @JvmName("div3") operator fun                      UnitsProduct.div(other: A                 ): ConcreteMeasure                = first.ratio  / other.ratio * second
/** (A * B) / (B * B) */ @JvmName("div8") operator fun            UnitsProduct.div(other: UnitsProduct): ConcreteMeasure> = second.ratio / other.second.ratio * (first / other.first)

/** (A / B) * B */        @JvmName("times1") operator fun                      UnitsRatio.                 times(other: B               ): ConcreteMeasure                                         = other.ratio / denominator.ratio * numerator
/** (A / (B * C)) * B */  @JvmName("times2") operator fun            UnitsRatio>.times(other: B               ): ConcreteMeasure>                          = other.ratio / denominator.first.ratio * (numerator / denominator.second)

@JvmName("div1")   operator fun                      UnitsRatio, UnitsProduct>.div(other: A               ): ConcreteMeasure>>         = numerator.first.ratio / other.ratio * (numerator.second / denominator)
@JvmName("div2")   operator fun            UnitsRatio, UnitsProduct>.div(other: A               ): ConcreteMeasure>>         = numerator.first.ratio / other.ratio * (numerator.second / denominator)
@JvmName("div3")   operator fun  UnitsRatio, UnitsProduct>.div(other: A               ): ConcreteMeasure>>         = numerator.first.ratio / other.ratio * (numerator.second / denominator)


// m/s * (s2/m) => s
operator fun  UnitsRatio.div(other: UnitsRatio>): ConcreteMeasure = numerator.ratio / other.numerator.ratio * (other.denominator / denominator)

@JvmName("times1") operator fun  ConcreteMeasure.                                times(other: ConcreteMeasure): ConcreteMeasure> = amount * other.amount * (units * other.units)
@JvmName("times2") operator fun  ConcreteMeasure.                                times(other: ConcreteMeasure>): ConcreteMeasure = amount * other.amount * (units * other.units)
@JvmName("times7") operator fun  ConcreteMeasure.                                times(other: ConcreteMeasure>): ConcreteMeasure> = amount * other.amount * (units * other.units)
@JvmName("times3") operator fun  ConcreteMeasure>.                 times(other: ConcreteMeasure): ConcreteMeasure = amount * other.amount * (units * other.units)
@JvmName("times4") operator fun  ConcreteMeasure>.                 times(other: ConcreteMeasure>): ConcreteMeasure, UnitsProduct>> = amount * other.amount * (units * other.units)
@JvmName("times5") operator fun  ConcreteMeasure>>.times(other: ConcreteMeasure): ConcreteMeasure> = amount * other.amount * (units * other.units)
@JvmName("times6") operator fun  ConcreteMeasure>>.times(other: ConcreteMeasure): ConcreteMeasure, UnitsProduct>> = amount * other.amount * (units * other.units)

@JvmName("times1") operator fun  ConcreteMeasure.                                times(other: B               ): ConcreteMeasure> = amount * (units * other)
@JvmName("times2") operator fun  ConcreteMeasure.                                times(other: UnitsRatio): ConcreteMeasure = amount * (units * other)
@JvmName("times7") operator fun  ConcreteMeasure.                                times(other: InverseUnits ): ConcreteMeasure> = amount * (units * other)
@JvmName("times3") operator fun  ConcreteMeasure>.                 times(other: B               ): ConcreteMeasure = amount * (units * other)
@JvmName("times4") operator fun  ConcreteMeasure>.                 times(other: UnitsRatio): ConcreteMeasure, UnitsProduct>> = amount * (units * other)
@JvmName("times5") operator fun  ConcreteMeasure>>.times(other: B               ): ConcreteMeasure> = amount * (units * other)
@JvmName("times6") operator fun  ConcreteMeasure>>.times(other: D               ): ConcreteMeasure, UnitsProduct>> = amount * (units * other)

// TODO: Kapt code generation possible?
operator fun  ConcreteMeasure.rem(other: ConcreteMeasure): Double = amount % other.amount * (units.ratio % other.units.ratio)

@JvmName("div16") operator fun  ConcreteMeasure.                 div(other: ConcreteMeasure): Double                    = amount / other.amount * (units.ratio / other.units.ratio)
@JvmName("div16") operator fun  ConcreteMeasure.                 div(other: ConcreteMeasure): ConcreteMeasure> = amount / other.amount * (units / other.units)
@JvmName("div1" ) operator fun  ConcreteMeasure>.div(other: ConcreteMeasure): ConcreteMeasure = amount / other.amount * (units / other.units)
@JvmName("div2" ) operator fun  ConcreteMeasure>.div(other: ConcreteMeasure): ConcreteMeasure = amount / other.amount * (units / other.units)
@JvmName("div3" ) operator fun  ConcreteMeasure>.div(other: ConcreteMeasure>): ConcreteMeasure> = amount / other.amount * (units / other.units)
@JvmName("div4" ) operator fun  ConcreteMeasure>.div(other: ConcreteMeasure>): ConcreteMeasure> = amount / other.amount * (units / other.units)
@JvmName("div5" ) operator fun  ConcreteMeasure>.div(other: ConcreteMeasure>): ConcreteMeasure> = amount / other.amount * (units / other.units)
@JvmName("div6" ) operator fun  ConcreteMeasure>.div(other: ConcreteMeasure>): ConcreteMeasure> = amount / other.amount * (units / other.units)
@JvmName("div7" ) operator fun  ConcreteMeasure>.div(other: ConcreteMeasure>): ConcreteMeasure> = amount / other.amount * (units / other.units)
@JvmName("div8" ) operator fun  ConcreteMeasure>.div(other: ConcreteMeasure>): ConcreteMeasure> = amount / other.amount * (units / other.units)

@JvmName("div16") operator fun  ConcreteMeasure.                 div(other: A                 ): Double                    = amount * (units.ratio / other.ratio)
@JvmName("div16") operator fun  ConcreteMeasure.                 div(other: B                 ): ConcreteMeasure> = amount * (units / other)
@JvmName("div1" ) operator fun  ConcreteMeasure>.div(other: A                 ): ConcreteMeasure = amount * (units / other)
@JvmName("div2" ) operator fun  ConcreteMeasure>.div(other: B                 ): ConcreteMeasure = amount * (units / other)
@JvmName("div3" ) operator fun  ConcreteMeasure>.div(other: UnitsProduct): ConcreteMeasure> = amount * (units / other)
@JvmName("div4" ) operator fun  ConcreteMeasure>.div(other: UnitsProduct): ConcreteMeasure> = amount * (units / other)
@JvmName("div5" ) operator fun  ConcreteMeasure>.div(other: UnitsProduct): ConcreteMeasure> = amount * (units / other)
@JvmName("div6" ) operator fun  ConcreteMeasure>.div(other: UnitsProduct): ConcreteMeasure> = amount * (units / other)
@JvmName("div7" ) operator fun  ConcreteMeasure>.div(other: UnitsProduct): ConcreteMeasure> = amount * (units / other)
@JvmName("div8" ) operator fun  ConcreteMeasure>.div(other: UnitsProduct): ConcreteMeasure> = amount * (units / other)

@JvmName("div9" ) operator fun  ConcreteMeasure>.                                  div(other: ConcreteMeasure): ConcreteMeasure>> = amount / other.amount * (units / other.units)
@JvmName("div10") operator fun  ConcreteMeasure>.                                  div(other: ConcreteMeasure>): ConcreteMeasure, UnitsProduct>> = amount / other.amount * (units / other.units)
@JvmName("div11") operator fun  ConcreteMeasure>.                                  div(other: ConcreteMeasure>>): ConcreteMeasure = amount / other.amount * (units / other.units)
@JvmName("div12") operator fun  ConcreteMeasure, UnitsProduct>>.div(other: ConcreteMeasure): ConcreteMeasure>> = amount / other.amount * (units / other.units)
@JvmName("div13") operator fun  ConcreteMeasure, UnitsProduct>>.div(other: ConcreteMeasure): ConcreteMeasure>> = amount / other.amount * (units / other.units)
@JvmName("div14") operator fun  ConcreteMeasure, UnitsProduct>>.div(other: ConcreteMeasure): ConcreteMeasure>> = amount / other.amount * (units / other.units)
@JvmName("div15") operator fun  ConcreteMeasure.                                                 div(other: ConcreteMeasure>): ConcreteMeasure = amount / other.amount * (units / other.units)

@JvmName("div9" ) operator fun  ConcreteMeasure>.                                  div(other: B                       ): ConcreteMeasure>> = amount * (units / other)
@JvmName("div10") operator fun  ConcreteMeasure>.                                  div(other: UnitsRatio): ConcreteMeasure, UnitsProduct>> = amount * (units / other)
@JvmName("div11") operator fun  ConcreteMeasure>.                                  div(other: UnitsRatio>): ConcreteMeasure = amount * (units / other)
@JvmName("div12") operator fun  ConcreteMeasure, UnitsProduct>>.div(other: A                       ): ConcreteMeasure>> = amount * (units / other)
@JvmName("div13") operator fun  ConcreteMeasure, UnitsProduct>>.div(other: A                       ): ConcreteMeasure>> = amount * (units / other)
@JvmName("div14") operator fun  ConcreteMeasure, UnitsProduct>>.div(other: A                       ): ConcreteMeasure>> = amount * (units / other)
@JvmName("div15") operator fun  ConcreteMeasure.                                                 div(other: UnitsRatio): ConcreteMeasure = amount * (units / other)


private infix fun  Number.into(unit: T): ConcreteMeasure = ConcreteMeasure(this.toDouble(), unit)

operator fun  Number.times(Units: T): ConcreteMeasure               = this into Units
operator fun  Number.div  (Units: T): ConcreteMeasure> = this into InverseUnits(Units)

operator fun  T.times (value: Number): ConcreteMeasure = value into this
operator fun  T.invoke(value: Number): ConcreteMeasure = value into this

operator fun  Number.times(measure: ConcreteMeasure): ConcreteMeasure = ConcreteMeasure(measure.amount * this.toDouble(), measure.units)