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

coulomb.ops.ops.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.ops

import scala.annotation.implicitNotFound

import coulomb.*

@implicitNotFound("Negation not defined in scope for Quantity[${V}, ${U}]")
abstract class Neg[V, U] extends (Quantity[V, U] => Quantity[V, U])

@implicitNotFound(
    "Addition not defined in scope for Quantity[${VL}, ${UL}] and Quantity[${VR}, ${UR}]"
)
abstract class Add[VL, UL, VR, UR]:
    type VO
    type UO
    val eval: (Quantity[VL, UL], Quantity[VR, UR]) => Quantity[VO, UO]

@implicitNotFound(
    "Subtraction not defined in scope for Quantity[${VL}, ${UL}] and Quantity[${VR}, ${UR}]"
)
abstract class Sub[VL, UL, VR, UR]:
    type VO
    type UO
    val eval: (Quantity[VL, UL], Quantity[VR, UR]) => Quantity[VO, UO]

@implicitNotFound(
    "Multiplication not defined in scope for Quantity[${VL}, ${UL}] and Quantity[${VR}, ${UR}]"
)
abstract class Mul[VL, UL, VR, UR]:
    type VO
    type UO
    val eval: (Quantity[VL, UL], Quantity[VR, UR]) => Quantity[VO, UO]

@implicitNotFound(
    "Division not defined in scope for Quantity[${VL}, ${UL}] and Quantity[${VR}, ${UR}]"
)
abstract class Div[VL, UL, VR, UR]:
    type VO
    type UO
    val eval: (Quantity[VL, UL], Quantity[VR, UR]) => Quantity[VO, UO]

@implicitNotFound(
    "Truncating Division not defined in scope for Quantity[${VL}, ${UL}] and Quantity[${VR}, ${UR}]"
)
abstract class TQuot[VL, UL, VR, UR]:
    type VO
    type UO
    val eval: (Quantity[VL, UL], Quantity[VR, UR]) => Quantity[VO, UO]

@implicitNotFound("Power not defined in scope for Quantity[${V}, ${U}] ^ ${P}")
abstract class Pow[V, U, P]:
    type VO
    type UO
    val eval: Quantity[V, U] => Quantity[VO, UO]

@implicitNotFound(
    "Truncating Power not defined in scope for Quantity[${V}, ${U}] ^ ${P}"
)
abstract class TPow[V, U, P]:
    type VO
    type UO
    val eval: Quantity[V, U] => Quantity[VO, UO]

@implicitNotFound(
    "Ordering not defined in scope for Quantity[${VL}, ${UL}] and Quantity[${VR}, ${UR}]"
)
abstract class Ord[VL, UL, VR, UR]
    extends ((Quantity[VL, UL], Quantity[VR, UR]) => Int)

@implicitNotFound(
    "Subtraction not defined in scope for DeltaQuantity[${VL}, ${UL}] and DeltaQuantity[${VR}, ${UR}]"
)
abstract class DeltaSub[B, VL, UL, VR, UR]:
    type VO
    type UO
    val eval: (
        DeltaQuantity[VL, UL, B],
        DeltaQuantity[VR, UR, B]
    ) => Quantity[VO, UO]

@implicitNotFound(
    "Subtraction not defined in scope for DeltaQuantity[${VL}, ${UL}] and Quantity[${VR}, ${UR}]"
)
abstract class DeltaSubQ[B, VL, UL, VR, UR]:
    type VO
    type UO
    val eval: (
        DeltaQuantity[VL, UL, B],
        Quantity[VR, UR]
    ) => DeltaQuantity[VO, UO, B]

@implicitNotFound(
    "Addition not defined in scope for DeltaQuantity[${VL}, ${UL}] and Quantity[${VR}, ${UR}]"
)
abstract class DeltaAddQ[B, VL, UL, VR, UR]:
    type VO
    type UO
    val eval: (
        DeltaQuantity[VL, UL, B],
        Quantity[VR, UR]
    ) => DeltaQuantity[VO, UO, B]

@implicitNotFound(
    "Ordering not defined in scope for DeltaQuantity[${VL}, ${UL}] and DeltaQuantity[${VR}, ${UR}]"
)
abstract class DeltaOrd[B, VL, UL, VR, UR]
    extends ((DeltaQuantity[VL, UL, B], DeltaQuantity[VR, UR, B]) => Int)

@implicitNotFound("Unable to simplify unit type ${U}")
abstract class SimplifiedUnit[U]:
    type UO

object SimplifiedUnit:
    import scala.quoted.*

    transparent inline given ctx_SimplifiedUnit[U]: SimplifiedUnit[U] =
        ${ simplifiedUnit[U] }

    class NC[U, UOp] extends SimplifiedUnit[U]:
        type UO = UOp

    private def simplifiedUnit[U](using
        Quotes,
        Type[U]
    ): Expr[SimplifiedUnit[U]] =
        import quotes.reflect.*
        coulomb.infra.meta.simplify(TypeRepr.of[U]).asType match
            case '[uo] => '{ new NC[U, uo] }

/** Resolve the operator output type for left and right argument types */
@implicitNotFound(
    "No output type resolution in scope for argument value types ${VL} and ${VR}"
)
abstract class ValueResolution[VL, VR]:
    type VO

object ValueResolution:
    transparent inline given ctx_VR_XpX[V]: ValueResolution[V, V] =
        new NC[V, V, V]
    transparent inline given ctx_VR_LpR[VL, VR](using
        ValuePromotion[VL, VR]
    ): ValueResolution[VL, VR] = new NC[VL, VR, VR]
    transparent inline given ctx_VR_RpL[VL, VR](using
        ValuePromotion[VR, VL]
    ): ValueResolution[VL, VR] = new NC[VL, VR, VL]
    class NC[VL, VR, VOp] extends ValueResolution[VL, VR]:
        type VO = VOp

final class ValuePromotion[VF, VT]

object ValuePromotion:
    import scala.quoted.*
    import scala.language.implicitConversions

    import coulomb.infra.meta.typestr

    final type &:[H, T]
    final type TNil

    transparent inline given ctx_VP_Path[VF, VT]: ValuePromotion[VF, VT] = ${
        vpPath[VF, VT]
    }

    private type VppSet[T] = scala.collection.mutable.HashSet[T]
    private val VppSet = scala.collection.mutable.HashSet

    private def vpPath[VF, VT](using
        Quotes,
        Type[VF],
        Type[VT]
    ): Expr[ValuePromotion[VF, VT]] =
        import quotes.reflect.*
        // Dealiasing is important because I am working with string type names.
        // Ability to hash, == or < directly on TypeRepr objects might allow me to
        // use Set[TypeRepr] but not sure if it is possible.
        val (vf, vt) = (TypeRepr.of[VF].dealias, TypeRepr.of[VT].dealias)
        if (pathexists(vf.typeSymbol.fullName, vt.typeSymbol.fullName, getvpp))
            '{ new ValuePromotion[VF, VT] }
        else
            report.error(s"no promotion from ${typestr(vf)} => ${typestr(vt)}")
            '{ new ValuePromotion[VF, VT] }

    private def getvpp(using Quotes): VppSet[(String, String)] =
        import quotes.reflect.*
        Implicits.search(
            TypeRepr.of[ValuePromotionPolicy].appliedTo(List(TypeBounds.empty))
        ) match
            case iss: ImplicitSearchSuccess =>
                val AppliedType(_, List(vppt)) =
                    iss.tree.tpe.baseType(
                        TypeRepr.of[ValuePromotionPolicy].typeSymbol
                    ): @unchecked
                vpp2str(vppt)
            case _ =>
                report.error("no ValuePromotionPolicy was found in scope")
                VppSet.empty[(String, String)]

    private def vpp2str(using Quotes)(
        vpp: quotes.reflect.TypeRepr
    ): VppSet[(String, String)] =
        import quotes.reflect.*
        vpp match
            case t if (t =:= TypeRepr.of[TNil]) =>
                VppSet.empty[(String, String)]
            case AppliedType(v, List(AppliedType(t2, List(vf, vt)), tail))
                if ((v =:= TypeRepr.of[&:]) && (t2 =:= TypeRepr.of[Tuple2])) =>
                val vppset = vpp2str(tail)
                vppset.add(
                    (
                        vf.dealias.typeSymbol.fullName,
                        vt.dealias.typeSymbol.fullName
                    )
                )
                vppset
            case _ =>
                report.error(
                    s"type ${typestr(vpp)} is not a valid value promotion policy"
                )
                VppSet.empty[(String, String)]

    private def pathexists(
        vf: String,
        vt: String,
        edges: VppSet[(String, String)]
    ): Boolean =
        val reachable = VppSet(vf)
        var haspath = false
        var done = false
        // print(s"\n\nvf= $vf   vt= $vt\n")
        while (!done) do
            val prevsize = reachable.size
            val next = reachable.flatMap(r =>
                edges.filter { (f, _) => f == r }.map { (_, t) => t }
            )
            reachable.addAll(next)
            // print(s"reachable= $reachable\n")
            if (reachable.contains(vt))
                haspath = true
                done = true
            if (reachable.size == prevsize)
                done = true
        haspath

final class ValuePromotionPolicy[Pairs]
object ValuePromotionPolicy:
    def apply[P](): ValuePromotionPolicy[P] = new ValuePromotionPolicy[P]

final case class ShowUnit[U](value: String)
object ShowUnit:
    inline given ctx_ShowUnit[U]: ShowUnit[U] = ShowUnit[U](coulomb.showUnit[U])

final case class ShowUnitFull[U](value: String)
object ShowUnitFull:
    inline given ctx_ShowUnitFull[U]: ShowUnitFull[U] =
        ShowUnitFull[U](coulomb.showUnitFull[U])




© 2015 - 2024 Weber Informatics LLC | Privacy Policy