Skip to content

Commit

Permalink
lol everything is demolished
Browse files Browse the repository at this point in the history
  • Loading branch information
erikerlandson committed Apr 6, 2024
1 parent 03d2cc4 commit 0b38639
Show file tree
Hide file tree
Showing 21 changed files with 525 additions and 873 deletions.
3 changes: 2 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ ThisBuild / tlSitePublishBranch := Some("scala3")
ThisBuild / githubWorkflowJavaVersions := Seq(JavaSpec.temurin("17"))

ThisBuild / resolvers ++= Resolver.sonatypeOssRepos("snapshots")
ThisBuild / crossScalaVersions := Seq("3.4.1")
// ThisBuild / crossScalaVersions := Seq("3.3.1")
ThisBuild / crossScalaVersions := Seq("3.4.2-RC1-bin-20240308-a9bb881-NIGHTLY")

// run tests sequentially for easier failure debugging
Test / parallelExecution := false
Expand Down
82 changes: 46 additions & 36 deletions core/src/main/scala/coulomb/conversion/coefficients.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

package coulomb.conversion

import spire.math.Rational
import spire.math.*

object coefficients:
inline def coefficientRational[U1, U2]: Rational = ${
Expand All @@ -25,21 +25,17 @@ object coefficients:
inline def coefficientDouble[U1, U2]: Double = ${
meta.coefficientDouble[U1, U2]
}
inline def coefficientDoubleJ[U1, U2]: java.lang.Double = ${
meta.coefficientDoubleJ[U1, U2]
}
inline def coefficientFloat[U1, U2]: Float = ${
meta.coefficientFloat[U1, U2]
}
inline def coefficientBigDecimal[UF, UT]: BigDecimal = ${
meta.coefficientBigDecimal[UF, UT]
}

inline def coefficientNumDouble[U1, U2]: Double = ${
meta.coefficientNumDouble[U1, U2]
inline def coefficientFloatJ[U1, U2]: java.lang.Float = ${
meta.coefficientFloatJ[U1, U2]
}
inline def coefficientDenDouble[U1, U2]: Double = ${
meta.coefficientDenDouble[U1, U2]
inline def coefficientDoubleJ[U1, U2]: java.lang.Double = ${
meta.coefficientDoubleJ[U1, U2]
}

inline def deltaOffsetRational[U, B]: Rational = ${
Expand All @@ -58,10 +54,33 @@ object coefficients:
import scala.quoted.*
import coulomb.infra.meta.{*, given}

given ctx_JavaDoubleToExpr: ToExpr[java.lang.Double] with
def apply(v: java.lang.Double)(using Quotes): Expr[java.lang.Double] =
given g_JavaIntToExpr: ToExpr[java.lang.Integer] with
def apply(v: java.lang.Integer)(using
Quotes
): Expr[java.lang.Integer] =
val vd: Int = v
'{ java.lang.Integer.valueOf(${ Expr(vd) }) }

given g_JavaLongToExpr: ToExpr[java.lang.Long] with
def apply(v: java.lang.Long)(using
Quotes
): Expr[java.lang.Long] =
val vd: Long = v
'{ java.lang.Long.valueOf(${ Expr(vd) }) }

given g_JavaFloatToExpr: ToExpr[java.lang.Float] with
def apply(v: java.lang.Float)(using
Quotes
): Expr[java.lang.Float] =
val vd: Float = v
'{ java.lang.Float.valueOf(${ Expr(vd) }) }

given g_JavaDoubleToExpr: ToExpr[java.lang.Double] with
def apply(v: java.lang.Double)(using
Quotes
): Expr[java.lang.Double] =
val vd: Double = v
'{ java.lang.Double.valueOf(${Expr(vd)}) }
'{ java.lang.Double.valueOf(${ Expr(vd) }) }

def coefficientRational[U1, U2](using
Quotes,
Expand All @@ -72,6 +91,15 @@ object coefficients:
val c = coef(TypeRepr.of[U1], TypeRepr.of[U2])
Expr(c)

def coefficientFloat[U1, U2](using
Quotes,
Type[U1],
Type[U2]
): Expr[Float] =
import quotes.reflect.*
val c = coef(TypeRepr.of[U1], TypeRepr.of[U2])
Expr(c.toFloat)

def coefficientDouble[U1, U2](using
Quotes,
Type[U1],
Expand All @@ -81,23 +109,23 @@ object coefficients:
val c = coef(TypeRepr.of[U1], TypeRepr.of[U2])
Expr(c.toDouble)

def coefficientDoubleJ[U1, U2](using
def coefficientFloatJ[U1, U2](using
Quotes,
Type[U1],
Type[U2]
): Expr[java.lang.Double] =
): Expr[java.lang.Float] =
import quotes.reflect.*
val c = coef(TypeRepr.of[U1], TypeRepr.of[U2])
Expr(java.lang.Double.valueOf(c.toDouble))
Expr(java.lang.Float.valueOf(c.toFloat))

def coefficientFloat[U1, U2](using
def coefficientDoubleJ[U1, U2](using
Quotes,
Type[U1],
Type[U2]
): Expr[Float] =
): Expr[java.lang.Double] =
import quotes.reflect.*
val c = coef(TypeRepr.of[U1], TypeRepr.of[U2])
Expr(c.toFloat)
Expr(java.lang.Double.valueOf(c.toDouble))

def coefficientBigDecimal[UF, UT](using
Quotes,
Expand All @@ -111,24 +139,6 @@ object coefficients:
)
Expr(bd)

def coefficientNumDouble[U1, U2](using
Quotes,
Type[U1],
Type[U2]
): Expr[Double] =
import quotes.reflect.*
val c = coef(TypeRepr.of[U1], TypeRepr.of[U2])
Expr(c.numerator.toDouble)

def coefficientDenDouble[U1, U2](using
Quotes,
Type[U1],
Type[U2]
): Expr[Double] =
import quotes.reflect.*
val c = coef(TypeRepr.of[U1], TypeRepr.of[U2])
Expr(c.denominator.toDouble)

def deltaOffsetRational[U, B](using
Quotes,
Type[U],
Expand Down
181 changes: 167 additions & 14 deletions core/src/main/scala/coulomb/conversion/conversion.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,32 +20,185 @@ import scala.annotation.implicitNotFound

/** conversion of value types, assuming some constant unit type */
@implicitNotFound("No value conversion in scope for value types ${VF} => ${VT}")
abstract class ValueConversion[VF, VT] extends (VF => VT)
abstract class ValueConversion[VF, VT] extends (VF => VT):
def apply(v: VF): VT

@implicitNotFound(
"No truncating value conversion in scope for value types ${VF} => ${VT}"
)
abstract class TruncatingValueConversion[VF, VT] extends (VF => VT)
object ValueConversion:
import scala.compiletime.*
import scala.util.NotGiven

import spire.math.*

import coulomb.infra.typeexpr

private inline def applyShim[VF, VT](v: VF)(using
vc: ValueConversion[VF, VT]
): VT =
vc(v)

inline def apply[VF, VT](vf: VF): VT =
if (typeexpr.teq[VF, VT])
vf.asInstanceOf[VT]
else
applyShim[VF, VT](vf)(using summonInline[ValueConversion[VF, VT]])

// coulomb's standard conversions are based on the typelevel/spire
// typeclasses ConvertableTo and ConvertableFrom
//
// spire's system includes definitions for scala's native
// numeric types, including BigDecimal and BigInt
//
// extending this conversion system to other non-spire value
// types can be accomplished by defining ConvertableFrom,
// ConvertableTo appropriately for such non-spire types.

given g_ValueConversion[VF, VT](using
cf: ConvertableFrom[VF],
ct: ConvertableTo[VT]
): ValueConversion[VF, VT] =
(v: VF) => ct.fromType(v)

// native types can generate efficient code via inlining

given ValueConversion[Int, Int] with
inline def apply(v: Int): Int = v

given ValueConversion[Int, Long] with
inline def apply(v: Int): Long = v.toLong

given ValueConversion[Int, Float] with
inline def apply(v: Int): Float = v.toFloat

given ValueConversion[Int, Double] with
inline def apply(v: Int): Double = v.toDouble

given ValueConversion[Long, Int] with
inline def apply(v: Long): Int = v.toInt

given ValueConversion[Long, Long] with
inline def apply(v: Long): Long = v

given ValueConversion[Long, Float] with
inline def apply(v: Long): Float = v.toFloat

given ValueConversion[Long, Double] with
inline def apply(v: Long): Double = v.toDouble

given ValueConversion[Float, Int] with
inline def apply(v: Float): Int = v.toInt

given ValueConversion[Float, Long] with
inline def apply(v: Float): Long = v.toLong

given ValueConversion[Float, Float] with
inline def apply(v: Float): Float = v

given ValueConversion[Float, Double] with
inline def apply(v: Float): Double = v.toDouble

given ValueConversion[Double, Int] with
inline def apply(v: Double): Int = v.toInt

given ValueConversion[Double, Long] with
inline def apply(v: Double): Long = v.toLong

given ValueConversion[Double, Float] with
inline def apply(v: Double): Float = v.toFloat

given ValueConversion[Double, Double] with
inline def apply(v: Double): Double = v

/** Convert a value of type V from implied units UF to UT */
@implicitNotFound(
"No unit conversion in scope for value type ${V}, unit types ${UF} => ${UT}"
)
abstract class UnitConversion[V, UF, UT] extends (V => V)

@implicitNotFound(
"No truncating unit conversion in scope for value type ${V}, unit types ${UF} => ${UT}"
)
abstract class TruncatingUnitConversion[V, UF, UT] extends (V => V)
object UnitConversion:
import scala.compiletime.*
import algebra.ring.*
import spire.math.*
import coulomb.infra.typeexpr
import coulomb.conversion.coefficients.*

/**
* Obtain the coefficient of conversion from one unit expression to another
* @tparam UF
* the input unit expression
* @tparam UT
* the output unit expression
* @tparam V
* the value type to return
* @return
* the coefficient of conversion from UF to UT If UF and UT are not
* convertible, causes a compilation failure.
*/
inline def coefficient[V, UF, UT]: V =
inline erasedValue[V] match
case _: Float => coefficientFloat[UF, UT].asInstanceOf[V]
case _: Double => coefficientDouble[UF, UT].asInstanceOf[V]
case _: BigDecimal => coefficientBigDecimal[UF, UT].asInstanceOf[V]
case _: Rational => coefficientRational[UF, UT].asInstanceOf[V]
case _: java.lang.Float =>
coefficientFloatJ[UF, UT].asInstanceOf[V]
case _: java.lang.Double =>
coefficientDoubleJ[UF, UT].asInstanceOf[V]
case _ =>
summonFrom {
case _: Fractional[V] =>
ValueConversion[Rational, V](coefficientRational[UF, UT])
case _ =>
error("Cannot instantiate the conversion coefficient of a non-fractional type")
}

private inline def applyShim[V, UF, UT](v: V)(using
alg: MultiplicativeSemigroup[V]
): V =
alg.times(coefficient[V, UF, UT], v)

inline def apply[V, UF, UT](v: V): V =
if (typeexpr.teq[UF, UT]) v
else
// shimming it through this function via 'using' picks up
// the inline 'times' method, for some reason
// calling summonInline directly does not
applyShim[V, UF, UT](v)(using
summonInline[MultiplicativeSemigroup[V]]
)

inline given g_UnitConversion[V, UF, UT](using
MultiplicativeSemigroup[V]
): UnitConversion[V, UF, UT] =
new G_UC[V, UF, UT](coefficient[V, UF, UT])

class G_UC[V, UF, UT](coef: V)(using alg: MultiplicativeSemigroup[V])
extends UnitConversion[V, UF, UT]:
def apply(v: V): V = alg.times(coef, v)

/** Convert a value of type V from implied delta units UF to UT */
@implicitNotFound(
"No unit conversion in scope for value type ${V}, unit types ${UF} => ${UT}"
)
abstract class DeltaUnitConversion[V, B, UF, UT] extends (V => V)

/** Convert a value of type V from implied delta units UF to UT */
@implicitNotFound(
"No truncating unit conversion in scope for value type ${V}, unit types ${UF} => ${UT}"
)
abstract class TruncatingDeltaUnitConversion[V, B, UF, UT] extends (V => V)
object ops:
import scala.compiletime.*
import coulomb.infra.typeexpr

final class ImplicitConversions

object policy:
given g_ImplicitConversions: ImplicitConversions =
new ImplicitConversions

inline def alignU[V, UF, UT](v: V): V =
summonFrom {
case _: ImplicitConversions =>
UnitConversion[V, UF, UT](v)
case _ =>
// strict mode - no implicit conversions are considered
if (typeexpr.teq[UF, UT])
v
else
error("Implicit coulomb conversions are not enabled")
}
17 changes: 0 additions & 17 deletions core/src/main/scala/coulomb/conversion/standard/scala.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,30 +22,13 @@ object scala:
import coulomb.*
import coulomb.syntax.*

// Enable the compiler to implicitly convert Quantity[V1, U1] -> Quantity[V2, U2],
// whenever a valid conversion exists:
// https://docs.scala-lang.org/scala3/reference/contextual/conversions.html

given ctx_Quantity_Conversion_1V1U[V, U]
: Conversion[Quantity[V, U], Quantity[V, U]] =
(q: Quantity[V, U]) => q

given ctx_Quantity_Conversion_1V2U[V, UF, UT](using
uc: UnitConversion[V, UF, UT]
): Conversion[Quantity[V, UF], Quantity[V, UT]] =
(q: Quantity[V, UF]) => uc(q.value).withUnit[UT]

given ctx_Quantity_Conversion_2V1U[U, VF, VT](using
vc: ValueConversion[VF, VT]
): Conversion[Quantity[VF, U], Quantity[VT, U]] =
(q: Quantity[VF, U]) => vc(q.value).withUnit[U]

given ctx_Quantity_Conversion_2V2U[VF, UF, VT, UT](using
vc: ValueConversion[VF, VT],
uc: UnitConversion[VT, UF, UT]
): Conversion[Quantity[VF, UF], Quantity[VT, UT]] =
(q: Quantity[VF, UF]) => uc(vc(q.value)).withUnit[UT]

given ctx_DeltaQuantity_conversion_2V2U[B, VF, UF, VT, UT](using
vc: ValueConversion[VF, VT],
uc: DeltaUnitConversion[VT, B, UF, UT]
Expand Down
Loading

0 comments on commit 0b38639

Please sign in to comment.