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

coulomb.deltaquantity.scala Maven / Gradle / Ivy

There is a newer version: 0.9.0-RC1
Show newest version
/*
 * Copyright 2022 Erik Erlandson
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package coulomb

import coulomb.syntax.*
import coulomb.ops.*
import coulomb.conversion.*

package syntax {
    // this has to be in a separated namespace:
    // https://github.com/lampepfl/dotty/issues/15255
    extension [V](v: V)
        /**
         * Lift a raw value into a delta-unit quantity
         * @tparam U
         *   the desired unit type
         * @tparam B
         *   base unit to anchor with
         * @return
         *   a DeltaQuantity with given value and unit type
         *   {{{
         * val date = (1.0).withDeltaUnit[Day, Second]
         *   }}}
         */
        inline def withDeltaUnit[U, B]: DeltaQuantity[V, U, B] =
            DeltaQuantity[U, B](v)
}

/**
 * Represents a value with an associated unit type and "delta" offset, for
 * example [[coulomb.units.temperature.Temperature]] or
 * [[coulomb.units.time.EpochTime]]
 * @tparam V
 *   the raw value type
 * @tparam U
 *   the unit type
 * @tparam B
 *   base unit type (the base unit of U)
 */
opaque type DeltaQuantity[V, U, B] = V

/** Defines DeltaQuantity constructors and extension methods */
object DeltaQuantity:
    /**
     * Lift a raw value of type V into a unit quantity
     * @tparam U
     *   the desired unit type
     * @tparam B
     *   base unit type of U
     * @return
     *   a DeltaQuantity with given value and unit type
     *   {{{
     * val temp = DeltaQuantity[Celsius, Kelvin](100.0)
     *   }}}
     */
    def apply[U, B](using a: Applier[U, B]) = a

    /** A shim class for DeltaQuantity companion object constructors */
    class Applier[U, B]:
        def apply[V](v: V): DeltaQuantity[V, U, B] = v
    object Applier:
        given [U, B]: Applier[U, B] = new Applier[U, B]

    extension [VL, UL, B](ql: DeltaQuantity[VL, UL, B])
        /**
         * extract the raw value of a delta-unit quantity
         * @return
         *   the underlying value, stripped of its unit information
         *   {{{
         * val t = (37.0).withTemperature[Celsius]
         * t.value // => 37.0
         * val d = (1.0).withEpochTime[Week]
         * d.value // => 1.0
         *   }}}
         */
        inline def value: VL = ql

        /**
         * returns a string representing this DeltaQuantity, using unit
         * abbreviations
         * @example
         *   {{{
         * val t = (37.0).withTemperature[Celsius]
         * t.show // => "37.0 °C"
         *   }}}
         */
        inline def show: String = s"${ql.value.toString} ${showUnit[UL]}"

        /**
         * returns a string representing this DeltaQuantity, using full unit
         * names
         * @example
         *   {{{
         * val t = (37.0).withTemperature[Celsius]
         * t.showFull // => "37.0 celsius"
         *   }}}
         */
        inline def showFull: String =
            s"${ql.value.toString} ${showUnitFull[UL]}"

        /**
         * convert a delta-quantity to a new value type
         * @tparam V
         *   the new value type to use
         * @return
         *   a new `DeltaQuantity` having value type `V`
         * @example
         *   {{{
         * val t = 37.withTemperature[Celsius]
         * t.toValue[Float] // => Temperature[Float, Celsius](37.0)
         *   }}}
         */
        inline def toValue[V](using
            conv: ValueConversion[VL, V]
        ): DeltaQuantity[V, UL, B] =
            conv(ql.value).withDeltaUnit[UL, B]

        /**
         * convert a delta-quantity to a new unit type
         * @tparam U
         *   the new unit type
         * @return
         *   a new `DeltaQuantity` having unit type `U`
         * @note
         *   attempting to convert to an incompatible unit will result in a
         *   compile error
         * @example
         *   {{{
         * val t = 37d.withTemperature[Celsius]
         * t.toUnit[Fahrenheit] // => Temperature[Double, Fahrenheit](98.6)
         *   }}}
         */
        inline def toUnit[U](using
            conv: DeltaUnitConversion[VL, B, UL, U]
        ): DeltaQuantity[VL, U, B] =
            conv(ql.value).withDeltaUnit[U, B]

        /**
         * convert a delta-quantity from a fractional value type to an integer
         * type
         * @tparam V
         *   the new value type to use
         * @return
         *   a new `DeltaQuantity` having value type `V`
         * @example
         *   {{{
         * val t = (98.6).withTemperature[Fahrenheit]
         * t.tToValue[Int] // => Temperature[Int, Fahrenheit](98)
         *   }}}
         */
        inline def tToValue[V](using
            conv: TruncatingValueConversion[VL, V]
        ): DeltaQuantity[V, UL, B] =
            conv(ql.value).withDeltaUnit[UL, B]

        /**
         * convert a delta-quantity to a new unit type, using an integer value
         * type
         * @tparam U
         *   the new unit type
         * @return
         *   a new `DeltaQuantity` having unit type `U`
         * @note
         *   attempting to convert to an incompatible unit will result in a
         *   compile error
         * @example
         *   {{{
         * val t = 37.withTemperature[Celsius]
         * t.tToUnit[Fahrenheit] // => Temperature[Int, Fahrenheit](98)
         *   }}}
         */
        inline def tToUnit[U](using
            conv: TruncatingDeltaUnitConversion[VL, B, UL, U]
        ): DeltaQuantity[VL, U, B] =
            conv(ql.value).withDeltaUnit[U, B]

        /**
         * subtract another delta-quantity from this one
         * @tparam VR
         *   right hand value type
         * @tparam UR
         *   right hand unit type
         * @param qr
         *   right hand delta-quantity
         * @return
         *   the result of subtracting `qr` from this, as a Quantity value
         * @example
         *   {{{
         * val t1 = 14.withEpochTime[Day]
         * val t2 = (1.0).withEpochTime[Week]
         * t1 - t2 // => Quantity[Double, Day](7.0)
         *   }}}
         * @note
         *   unit types `UL` and `UR` must be convertable
         * @note
         *   result may depend on what algebras, policies, and other typeclasses
         *   are in scope
         */
        transparent inline def -[VR, UR](qr: DeltaQuantity[VR, UR, B])(using
            sub: DeltaSub[B, VL, UL, VR, UR]
        ): Quantity[sub.VO, sub.UO] =
            sub.eval(ql, qr)

        /**
         * subtract quantity from this delta-quantity
         * @tparam VR
         *   right hand value type
         * @tparam UR
         *   right hand unit type
         * @param qr
         *   right hand quantity
         * @return
         *   the result of subtracting `qr` from this, as a DeltaQuantity value
         * @example
         *   {{{
         * val t1 = 14.withEpochTime[Day]
         * val q = (1.0).withUnit[Week]
         * t1 - q // => EpochTime[Double, Day](7.0)
         *   }}}
         * @note
         *   unit types `UL` and `UR` must be convertable
         * @note
         *   result may depend on what algebras, policies, and other typeclasses
         *   are in scope
         */
        transparent inline def -[VR, UR](qr: Quantity[VR, UR])(using
            sub: DeltaSubQ[B, VL, UL, VR, UR]
        ): DeltaQuantity[sub.VO, sub.UO, B] =
            sub.eval(ql, qr)

        /**
         * add a quantity to this delta-quantity
         * @tparam VR
         *   right hand value type
         * @tparam UR
         *   right hand unit type
         * @param qr
         *   right hand quantity
         * @return
         *   the result of adding `qr` to this, as a DeltaQuantity value
         * @example
         *   {{{
         * val t1 = 14.withEpochTime[Day]
         * val q = (1.0).withUnit[Week]
         * t1 + q // => EpochTime[Double, Day](21.0)
         *   }}}
         * @note
         *   unit types `UL` and `UR` must be convertable
         * @note
         *   result may depend on what algebras, policies, and other typeclasses
         *   are in scope
         */
        transparent inline def +[VR, UR](qr: Quantity[VR, UR])(using
            add: DeltaAddQ[B, VL, UL, VR, UR]
        ): DeltaQuantity[add.VO, add.UO, B] =
            add.eval(ql, qr)

        /**
         * test this delta-quantity for equality with another
         * @tparam VR
         *   value type of the right hand quantity
         * @tparam UR
         *   unit type of the right hand quantity
         * @param qr
         *   the right hand quantity
         * @return
         *   true if right hand value equals the left (after any conversions),
         *   false otherwise
         * @example
         *   {{{
         * val t1 = (14.0).withEpochTime[Day]
         * val t2 = 2.withEpochTime[Week]
         * t1 === t2 // => true
         *   }}}
         * @note
         *   result may depend on what algebras, policies, and other typeclasses
         *   are in scope
         */
        inline def ===[VR, UR](qr: DeltaQuantity[VR, UR, B])(using
            ord: DeltaOrd[B, VL, UL, VR, UR]
        ): Boolean =
            ord(ql, qr) == 0

        /**
         * test this delta-quantity for inequality with another
         * @tparam VR
         *   value type of the right hand quantity
         * @tparam UR
         *   unit type of the right hand quantity
         * @param qr
         *   the right hand quantity
         * @return
         *   true if right hand value does not equal the left (after any
         *   conversions), false otherwise
         * @example
         *   {{{
         * val t1 = (14.0).withEpochTime[Day]
         * val t2 = 2.withEpochTime[Week]
         * t1 =!= t2 // => false
         *   }}}
         * @note
         *   result may depend on what algebras, policies, and other typeclasses
         *   are in scope
         */
        inline def =!=[VR, UR](qr: DeltaQuantity[VR, UR, B])(using
            ord: DeltaOrd[B, VL, UL, VR, UR]
        ): Boolean =
            ord(ql, qr) != 0

        /**
         * test if this delta-quantity is less than another
         * @tparam VR
         *   value type of the right hand quantity
         * @tparam UR
         *   unit type of the right hand quantity
         * @param qr
         *   the right hand quantity
         * @return
         *   true if left-hand value is less than the right (after any
         *   conversions), false otherwise
         * @example
         *   {{{
         * val t1 = (14.0).withEpochTime[Day]
         * val t2 = 3.withEpochTime[Week]
         * t1 < t2 // => true
         *   }}}
         * @note
         *   result may depend on what algebras, policies, and other typeclasses
         *   are in scope
         */
        inline def <[VR, UR](qr: DeltaQuantity[VR, UR, B])(using
            ord: DeltaOrd[B, VL, UL, VR, UR]
        ): Boolean =
            ord(ql, qr) < 0

        /**
         * test if this delta-quantity is less than or equal to than another
         * @tparam VR
         *   value type of the right hand quantity
         * @tparam UR
         *   unit type of the right hand quantity
         * @param qr
         *   the right hand quantity
         * @return
         *   true if left-hand value is less than or equal to the right (after
         *   any conversions), false otherwise
         * @example
         *   {{{
         * val t1 = (14.0).withEpochTime[Day]
         * val t2 = 3.withEpochTime[Week]
         * t1 <= t2 // => true
         *   }}}
         * @note
         *   result may depend on what algebras, policies, and other typeclasses
         *   are in scope
         */
        inline def <=[VR, UR](qr: DeltaQuantity[VR, UR, B])(using
            ord: DeltaOrd[B, VL, UL, VR, UR]
        ): Boolean =
            ord(ql, qr) <= 0

        inline def >[VR, UR](qr: DeltaQuantity[VR, UR, B])(using
            ord: DeltaOrd[B, VL, UL, VR, UR]
        ): Boolean =
            ord(ql, qr) > 0

        inline def >=[VR, UR](qr: DeltaQuantity[VR, UR, B])(using
            ord: DeltaOrd[B, VL, UL, VR, UR]
        ): Boolean =
            ord(ql, qr) >= 0




© 2015 - 2024 Weber Informatics LLC | Privacy Policy