Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

experiment with named classes in operations #265

Merged
merged 35 commits into from
Apr 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
a795b93
PowImplFP experiment
erikerlandson Mar 31, 2022
a933042
study1 and study2
erikerlandson Apr 1, 2022
b6a9de2
pow
erikerlandson Apr 1, 2022
57324c9
implicit conversions
erikerlandson Apr 2, 2022
7c0c568
named unit conversion classes
erikerlandson Apr 2, 2022
8106a6c
named simplify class
erikerlandson Apr 2, 2022
833338b
unused import
erikerlandson Apr 2, 2022
4a436e9
named add
erikerlandson Apr 2, 2022
721ee81
named sub
erikerlandson Apr 2, 2022
6f7b5db
named mul
erikerlandson Apr 2, 2022
24509e0
named div
erikerlandson Apr 2, 2022
3e4661a
named tquot and tpow
erikerlandson Apr 2, 2022
6d3dc7c
named ord
erikerlandson Apr 2, 2022
fcd5dc1
move simplification
erikerlandson Apr 2, 2022
f2ca846
named deltasub
erikerlandson Apr 2, 2022
5aa2abe
named deltasubq
erikerlandson Apr 2, 2022
1cdd9b4
named deltaaddq
erikerlandson Apr 2, 2022
d7e3a9d
named deltaord
erikerlandson Apr 2, 2022
5ad20dd
named neg
erikerlandson Apr 2, 2022
06671bf
clean up rational.typeexpr
erikerlandson Apr 2, 2022
d4dea8a
AddNC SubNC MulNC DivNC
erikerlandson Apr 5, 2022
a2871f4
clean up implicit conversion defs using SAM/lambdas
erikerlandson Apr 5, 2022
c2d3615
increase benchmark iteration time for more stability
erikerlandson Apr 5, 2022
817be07
experiment with named class for unit defs
erikerlandson Apr 5, 2022
e93247d
neg with SAM/lambda
erikerlandson Apr 5, 2022
ee70e03
TQuotNC
erikerlandson Apr 5, 2022
92c63fa
PowNC, TPowNC
erikerlandson Apr 5, 2022
72795b8
Ord using SAM/lambda
erikerlandson Apr 5, 2022
cbcd64a
DeltaSubNC
erikerlandson Apr 5, 2022
463e3bc
DeltaSubQNC
erikerlandson Apr 5, 2022
b189c48
DeltaAddQNC
erikerlandson Apr 5, 2022
26ed1ee
DeltaOrd with SAM/lambdas
erikerlandson Apr 5, 2022
e84558b
VRNC
erikerlandson Apr 5, 2022
b0872d8
policy cleanup
erikerlandson Apr 6, 2022
658f02a
fix benchmark imports
erikerlandson Apr 6, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions benchmarks/src/main/scala/coulomb/benchmarks/quantity.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,16 @@ import org.openjdk.jmh.annotations.*
@Fork(1)
@BenchmarkMode(Array(Mode.Throughput))
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Warmup(iterations = 3, time = 1)
@Measurement(iterations = 10, time = 1)
@Warmup(iterations = 3, time = 2)
@Measurement(iterations = 10, time = 2)
class QuantityBenchmark:
import scala.language.implicitConversions
import coulomb.*
import coulomb.testing.units.{*, given}
import algebra.instances.all.given
import coulomb.ops.algebra.all.given
import coulomb.ops.standard.given
import coulomb.ops.resolution.standard.given
import coulomb.conversion.standard.all.given

import coulomb.policy.standard.given

var data: Vector[Quantity[Double, Meter]] = Vector.empty[Quantity[Double, Meter]]

Expand Down
19 changes: 5 additions & 14 deletions core/src/main/scala/coulomb/conversion/standard/scala.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,35 +26,26 @@ object scala:
// https://docs.scala-lang.org/scala3/reference/contextual/conversions.html

given ctx_Quantity_Conversion_1V1U[V, U]: Conversion[Quantity[V, U], Quantity[V, U]] =
new Conversion[Quantity[V, U], Quantity[V, U]]:
def apply(q: Quantity[V, U]): Quantity[V, U] = q
(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]] =
new Conversion[Quantity[V, UF], Quantity[V, UT]]:
def apply(q: Quantity[V, UF]): Quantity[V, UT] =
uc(q.value).withUnit[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]] =
new Conversion[Quantity[VF, U], Quantity[VT, U]]:
def apply(q: Quantity[VF, U]): Quantity[VT, U] =
vc(q.value).withUnit[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]] =
new Conversion[Quantity[VF, UF], Quantity[VT, UT]]:
def apply(q: Quantity[VF, UF]): Quantity[VT, UT] =
uc(vc(q.value)).withUnit[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]
): Conversion[DeltaQuantity[VF, UF, B], DeltaQuantity[VT, UT, B]] =
new Conversion[DeltaQuantity[VF, UF, B], DeltaQuantity[VT, UT, B]]:
def apply(q: DeltaQuantity[VF, UF, B]): DeltaQuantity[VT, UT, B] =
uc(vc(q.value)).withDeltaUnit[UT, B]
(q: DeltaQuantity[VF, UF, B]) => uc(vc(q.value)).withDeltaUnit[UT, B]
88 changes: 47 additions & 41 deletions core/src/main/scala/coulomb/conversion/standard/unit.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,61 +22,67 @@ object unit:
import coulomb.rational.Rational

inline given ctx_UC_Rational[UF, UT]: UnitConversion[Rational, UF, UT] =
val c = coefficientRational[UF, UT]
new UnitConversion[Rational, UF, UT]:
def apply(v: Rational): Rational = c * v
new infra.RationalUC[UF, UT](coefficientRational[UF, UT])

inline given ctx_UC_Double[UF, UT]: UnitConversion[Double, UF, UT] =
val c = coefficientDouble[UF, UT]
new UnitConversion[Double, UF, UT]:
def apply(v: Double): Double = c * v
new infra.DoubleUC[UF, UT](coefficientDouble[UF, UT])

inline given ctx_UC_Float[UF, UT]: UnitConversion[Float, UF, UT] =
val c = coefficientFloat[UF, UT]
new UnitConversion[Float, UF, UT]:
def apply(v: Float): Float = c * v
new infra.FloatUC[UF, UT](coefficientFloat[UF, UT])

inline given ctx_TUC_Long[UF, UT]: TruncatingUnitConversion[Long, UF, UT] =
val nc = coefficientNumDouble[UF, UT]
val dc = coefficientDenDouble[UF, UT]
// using nc and dc is more efficient than using Rational directly in the conversion function
// but still gives us 53 bits of integer precision for exact rational arithmetic, and also
// graceful loss of precision if nc*v exceeds 53 bits
new TruncatingUnitConversion[Long, UF, UT]:
def apply(v: Long): Long = ((nc * v) / dc).toLong
new infra.LongTUC[UF, UT](coefficientNumDouble[UF, UT], coefficientDenDouble[UF, UT])

inline given ctx_TUC_Int[UF, UT]: TruncatingUnitConversion[Int, UF, UT] =
val nc = coefficientNumDouble[UF, UT]
val dc = coefficientDenDouble[UF, UT]
new TruncatingUnitConversion[Int, UF, UT]:
def apply(v: Int): Int = ((nc * v) / dc).toInt
new infra.IntTUC[UF, UT](coefficientNumDouble[UF, UT], coefficientDenDouble[UF, UT])

inline given ctx_DUC_Double[B, UF, UT]: DeltaUnitConversion[Double, B, UF, UT] =
val c = coefficientDouble[UF, UT]
val df = deltaOffsetDouble[UF, B]
val dt = deltaOffsetDouble[UT, B]
new DeltaUnitConversion[Double, B, UF, UT]:
def apply(v: Double): Double = ((v + df) * c) - dt
new infra.DoubleDUC[B, UF, UT](
coefficientDouble[UF, UT], deltaOffsetDouble[UF, B], deltaOffsetDouble[UT, B])

inline given ctx_DUC_Float[B, UF, UT]: DeltaUnitConversion[Float, B, UF, UT] =
val c = coefficientFloat[UF, UT]
val df = deltaOffsetFloat[UF, B]
val dt = deltaOffsetFloat[UT, B]
new DeltaUnitConversion[Float, B, UF, UT]:
def apply(v: Float): Float = ((v + df) * c) - dt
new infra.FloatDUC[B, UF, UT](
coefficientFloat[UF, UT], deltaOffsetFloat[UF, B], deltaOffsetFloat[UT, B])

inline given ctx_TDUC_Long[B, UF, UT]: TruncatingDeltaUnitConversion[Long, B, UF, UT] =
val nc = coefficientNumDouble[UF, UT]
val dc = coefficientDenDouble[UF, UT]
val df = deltaOffsetDouble[UF, B]
val dt = deltaOffsetDouble[UT, B]
new TruncatingDeltaUnitConversion[Long, B, UF, UT]:
def apply(v: Long): Long = (((nc * (v + df)) / dc) - dt).toLong
new infra.LongTDUC[B, UF, UT](
coefficientNumDouble[UF, UT], coefficientDenDouble[UF, UT],
deltaOffsetDouble[UF, B], deltaOffsetDouble[UT, B])

inline given ctx_TDUC_Int[B, UF, UT]: TruncatingDeltaUnitConversion[Int, B, UF, UT] =
val nc = coefficientNumDouble[UF, UT]
val dc = coefficientDenDouble[UF, UT]
val df = deltaOffsetDouble[UF, B]
val dt = deltaOffsetDouble[UT, B]
new TruncatingDeltaUnitConversion[Int, B, UF, UT]:
new infra.IntTDUC[B, UF, UT](
coefficientNumDouble[UF, UT], coefficientDenDouble[UF, UT],
deltaOffsetDouble[UF, B], deltaOffsetDouble[UT, B])

object infra:
class RationalUC[UF, UT](c: Rational) extends UnitConversion[Rational, UF, UT]:
def apply(v: Rational): Rational = c * v

class DoubleUC[UF, UT](c: Double) extends UnitConversion[Double, UF, UT]:
def apply(v: Double): Double = c * v

class FloatUC[UF, UT](c: Float) extends UnitConversion[Float, UF, UT]:
def apply(v: Float): Float = c * v

class LongTUC[UF, UT](nc: Double, dc: Double) extends TruncatingUnitConversion[Long, UF, UT]:
// using nc and dc is more efficient than using Rational directly in the conversion function
// but still gives us 53 bits of integer precision for exact rational arithmetic, and also
// graceful loss of precision if nc*v exceeds 53 bits
def apply(v: Long): Long = ((nc * v) / dc).toLong

class IntTUC[UF, UT](nc: Double, dc: Double) extends TruncatingUnitConversion[Int, UF, UT]:
def apply(v: Int): Int = ((nc * v) / dc).toInt

class DoubleDUC[B, UF, UT](c: Double, df: Double, dt: Double) extends DeltaUnitConversion[Double, B, UF, UT]:
def apply(v: Double): Double = ((v + df) * c) - dt

class FloatDUC[B, UF, UT](c: Float, df: Float, dt: Float) extends DeltaUnitConversion[Float, B, UF, UT]:
def apply(v: Float): Float = ((v + df) * c) - dt

class LongTDUC[B, UF, UT](nc: Double, dc: Double, df: Double, dt: Double) extends
TruncatingDeltaUnitConversion[Long, B, UF, UT]:
def apply(v: Long): Long = (((nc * (v + df)) / dc) - dt).toLong

class IntTDUC[B, UF, UT](nc: Double, dc: Double, df: Double, dt: Double) extends
TruncatingDeltaUnitConversion[Int, B, UF, UT]:
def apply(v: Int): Int = (((nc * (v + df)) / dc) - dt).toInt
6 changes: 6 additions & 0 deletions core/src/main/scala/coulomb/define/define.scala
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ abstract class BaseUnit[U, Name, Abbv] extends NamedUnit[Name, Abbv]
* @tparam Abbv unit abbreviation
*/
abstract class DerivedUnit[U, D, Name, Abbv] extends NamedUnit[Name, Abbv]
object DerivedUnit:
given ctx_unit_to_DU[U, D, Name, Abbv]: scala.Conversion[Unit, DerivedUnit[U, D, Name, Abbv]] =
(_: Unit) => new infra.DU[U, D, Name, Abbv]
object infra:
class DU[U, D, Name, Abbv] extends DerivedUnit[U, D, Name, Abbv]

/**
* Delta Units represent units with an offset in their transforms, for example temperatures or times
Expand All @@ -47,3 +52,4 @@ abstract class DerivedUnit[U, D, Name, Abbv] extends NamedUnit[Name, Abbv]
* @tparam Abbv unit abbreviation
*/
abstract class DeltaUnit[U, D, O, Name, Abbv] extends DerivedUnit[U, D, Name, Abbv]

6 changes: 3 additions & 3 deletions core/src/main/scala/coulomb/deltaquantity.scala
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,13 @@ object deltaquantity:
conv(ql.value).withDeltaUnit[U, B]

transparent inline def -[VR, UR](qr: DeltaQuantity[VR, UR, B])(using sub: DeltaSub[B, VL, UL, VR, UR]): Quantity[sub.VO, sub.UO] =
sub(ql, qr)
sub.eval(ql, qr)

transparent inline def -[VR, UR](qr: Quantity[VR, UR])(using sub: DeltaSubQ[B, VL, UL, VR, UR]): DeltaQuantity[sub.VO, sub.UO, B] =
sub(ql, qr)
sub.eval(ql, qr)

transparent inline def +[VR, UR](qr: Quantity[VR, UR])(using add: DeltaAddQ[B, VL, UL, VR, UR]): DeltaQuantity[add.VO, add.UO, B] =
add(ql, qr)
add.eval(ql, qr)

inline def ===[VR, UR](qr: DeltaQuantity[VR, UR, B])(using ord: DeltaOrd[B, VL, UL, VR, UR]): Boolean =
ord(ql, qr) == 0
Expand Down
48 changes: 0 additions & 48 deletions core/src/main/scala/coulomb/infra/meta.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ package coulomb.infra
import coulomb.rational.Rational
import coulomb.*
import coulomb.define.*
import coulomb.ops.*

object meta:
import scala.quoted.*
Expand All @@ -32,48 +31,6 @@ object meta:
case v if (v == 1) => '{ Rational.const1 }
case _ => '{ Rational(${Expr(r.n)}, ${Expr(r.d)}) }

def teToRational[E](using Quotes, Type[E]): Expr[Rational] =
import quotes.reflect.*
val rationalTE(v) = TypeRepr.of[E]
Expr(v)

def teToBigInt[E](using Quotes, Type[E]): Expr[BigInt] =
import quotes.reflect.*
val bigintTE(v) = TypeRepr.of[E]
Expr(v)

def teToDouble[E](using Quotes, Type[E]): Expr[Double] =
import quotes.reflect.*
val rationalTE(v) = TypeRepr.of[E]
Expr(v.toDouble)

def teToNonNegInt[E](using Quotes, Type[E]): Expr[coulomb.rational.typeexpr.NonNegInt[E]] =
import quotes.reflect.*
val rationalTE(v) = TypeRepr.of[E]
if ((v.d == 1) && (v.n >= 0) && (v.n.isValidInt)) then
'{ new coulomb.rational.typeexpr.NonNegInt[E] { val value = ${Expr(v.n.toInt)} } }
else
report.error(s"type expr ${typestr(TypeRepr.of[E])} is not a non-negative Int")
'{ new coulomb.rational.typeexpr.NonNegInt[E] { val value = 0 } }

def teToPosInt[E](using Quotes, Type[E]): Expr[coulomb.rational.typeexpr.PosInt[E]] =
import quotes.reflect.*
val rationalTE(v) = TypeRepr.of[E]
if ((v.d == 1) && (v.n > 0) && (v.n.isValidInt)) then
'{ new coulomb.rational.typeexpr.PosInt[E] { val value = ${Expr(v.n.toInt)} } }
else
report.error(s"type expr ${typestr(TypeRepr.of[E])} is not a positive Int")
'{ new coulomb.rational.typeexpr.PosInt[E] { val value = 0 } }

def teToInt[E](using Quotes, Type[E]): Expr[coulomb.rational.typeexpr.AllInt[E]] =
import quotes.reflect.*
val rationalTE(v) = TypeRepr.of[E]
if ((v.d == 1) && (v.n.isValidInt)) then
'{ new coulomb.rational.typeexpr.AllInt[E] { val value = ${Expr(v.n.toInt)} } }
else
report.error(s"type expr ${typestr(TypeRepr.of[E])} is not an Int")
'{ new coulomb.rational.typeexpr.AllInt[E] { val value = 0 } }

object rationalTE:
def unapply(using Quotes)(tr: quotes.reflect.TypeRepr): Option[Rational] =
import quotes.reflect.*
Expand Down Expand Up @@ -233,11 +190,6 @@ object meta:
val (nsig, dsig) = sortsig(tail)
if (e > 0) ((u, e) :: nsig, dsig) else (nsig, (u, -e) :: dsig)

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

def simplify(using Quotes)(u: quotes.reflect.TypeRepr): quotes.reflect.TypeRepr =
import quotes.reflect.*
val (un, ud) = sortsig(stdsig(u))
Expand Down
33 changes: 13 additions & 20 deletions core/src/main/scala/coulomb/ops/ops.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,76 +21,73 @@ import scala.annotation.implicitNotFound
import coulomb.*

@implicitNotFound("Negation not defined in scope for Quantity[${V}, ${U}]")
abstract class Neg[V, U]:
def apply(q: Quantity[V, U]): 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
def apply(ql: Quantity[VL, UL], qr: Quantity[VR, UR]): Quantity[VO, 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
def apply(ql: Quantity[VL, UL], qr: Quantity[VR, UR]): Quantity[VO, 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
def apply(ql: Quantity[VL, UL], qr: Quantity[VR, UR]): Quantity[VO, 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
def apply(ql: Quantity[VL, UL], qr: Quantity[VR, UR]): Quantity[VO, 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
def apply(ql: Quantity[VL, UL], qr: Quantity[VR, UR]): Quantity[VO, 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
def apply(q: Quantity[V, U]): Quantity[VO, 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
def apply(q: Quantity[V, U]): Quantity[VO, 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]:
def apply(ql: Quantity[VL, UL], qr: Quantity[VR, UR]): Int
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
def apply(ql: DeltaQuantity[VL, UL, B], qr: DeltaQuantity[VR, UR, B]): Quantity[VO, 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
def apply(ql: DeltaQuantity[VL, UL, B], qr: Quantity[VR, UR]): DeltaQuantity[VO, UO, B]
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
def apply(ql: DeltaQuantity[VL, UL, B], qr: Quantity[VR, UR]): DeltaQuantity[VO, UO, B]
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]:
def apply(ql: DeltaQuantity[VL, UL, B], qr: DeltaQuantity[VR, UR, B]): Int
abstract class DeltaOrd[B, VL, UL, VR, UR] extends ((DeltaQuantity[VL, UL, B], DeltaQuantity[VR, UR, B]) => Int)

/** 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}")
Expand All @@ -100,7 +97,3 @@ abstract class ValueResolution[VL, VR]:
@implicitNotFound("Unable to simplify unit type ${U}")
abstract class SimplifiedUnit[U]:
type UO

object SimplifiedUnit:
transparent inline given ctx_SimplifiedUnit[U]: SimplifiedUnit[U] =
${ coulomb.infra.meta.simplifiedUnit[U] }
Loading