Skip to content

Commit

Permalink
Add Quantity and Option[Quantity].
Browse files Browse the repository at this point in the history
  • Loading branch information
neuralagent committed Jan 14, 2017
1 parent 8413d06 commit 9ad9cdb
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,7 @@ trait CanAddMeasure[A1, A2]
def plus(addend1: A1, addend2: A2): R
}

object CanAddMeasure
{
type Aux[A1, A2, R0] = CanAddMeasure[A1, A2] { type R = R0 }
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ import scala.language.higherKinds
* @author Araik Grigoryan
*/
@implicitNotFound("Cannot find CanAddQuantity implementation that adds ${Q1} and ${Q2}.")
trait CanAddQuantity[N, M1, Q1[_, _], M2, Q2[_, _], RM] extends CanAddMeasure[M1, M2]
trait CanAddQuantity[N, M1, Q1[_, _], M2, Q2, RM]
{
type QR

def plus(addend1: Q1[N, M1], addend2: Q2[N, M2])(implicit cc1: CanConvert[M1, RM], cc2: CanConvert[M2, RM]): QR
def plus(addend1: Q1[N, M1], addend2: Q2)(implicit cc1: CanConvert[M1, RM], cc2: CanConvert[M2, RM]): QR
}

Original file line number Diff line number Diff line change
Expand Up @@ -35,18 +35,30 @@ case class Quantity[N, M](value: N, measure: M)(implicit qn: QuasiNumeric[N])
def /(constant: Double) = Quantity(qn.divideByConstant(value, constant), measure)

/**
* Adds another quantity. CanAddQuantity instance allows addition of apples and oranges to obtain bananas.
* Adds another Quantity. CanAddQuantity instance allows addition of apples and oranges to obtain bananas.
*/
def +[M2](quantity: Quantity[N, M2])
(implicit caq: CanAddQuantity[N, M, Quantity, M2, Quantity, M],
cc1: CanConvert[M, M], cc2: CanConvert[M2, M]): caq.QR = caq.plus(this, quantity)
(implicit caq: CanAddQuantity[N, M, Quantity, M2, Quantity[N, M2], M], cc1: CanConvert[M, M], cc2: CanConvert[M2, M]): caq.QR = caq.plus(this, quantity)

/**
* Subtracts another quantity. CanAddQuantity instance allows addition of apples and oranges to obtain bananas.
* Adds another Option[Quantity]. CanAddQuantity instance allows addition of apples and oranges to obtain bananas.
*/
def +[M2](quantity: Option[Quantity[N, M2]])
(implicit caq: CanAddQuantity[N, M, Quantity, M2, Option[Quantity[N, M2]], M], cc1: CanConvert[M, M], cc2: CanConvert[M2, M]): caq.QR =
caq.plus(this, quantity)

/**
* Subtracts another Quantity. CanAddQuantity instance allows subtraction of apples and oranges to obtain bananas.
*/
def -[M2](quantity: Quantity[N, M2])
(implicit caq: CanAddQuantity[N, M, Quantity, M2, Quantity, M],
cc1: CanConvert[M, M], cc2: CanConvert[M2, M]): caq.QR = caq.plus(this, -quantity)
(implicit caq: CanAddQuantity[N, M, Quantity, M2, Quantity[N, M2], M], cc1: CanConvert[M, M], cc2: CanConvert[M2, M]): caq.QR = caq.plus(this, -quantity)

/**
* Subtracts another Option[Quantity]. CanAddQuantity instance allows subtraction of apples and oranges to obtain bananas.
*/
def -[M2](quantity: Option[Quantity[N, M2]])
(implicit caq: CanAddQuantity[N, M, Quantity, M2, Option[Quantity[N, M2]], M], cc1: CanConvert[M, M], cc2: CanConvert[M2, M]): caq.QR =
caq.plus(this, quantity.map(_.unary_-()))

/**
* Divides by another quantity.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,24 +31,20 @@ package object arithmetic
override def pow(base: B, exponent: Double): ExponentialMeasure[B] = ExponentialMeasure(base, exponent)
}

implicit def lhsCanAdd[M1 <: Measure[M1], M2 <: Measure[M2]] = new CanAddMeasure[M1, M2]
implicit def lhsCanAddMeasure[M1 <: Measure[M1], M2 <: Measure[M2]] = new CanAddMeasure[M1, M2]
{
type R = M1

override def plus(addend1: M1, addend2: M2): R = addend1
}

implicit def lhsCanAddQuantity[N, M <: Measure[M]](implicit qn: QuasiNumeric[N]) = new CanAddQuantity[N, M, Quantity, M, Quantity, M]
implicit def lhsCanAddQuantity[N, M <: Measure[M]](implicit qn: QuasiNumeric[N], cam: CanAddMeasure.Aux[M, M, M]) = new CanAddQuantity[N, M, Quantity, M, Quantity[N, M], M]
{
type R = M

type QR = Option[Quantity[N, M]]

override def plus(addend1: M, addend2: M): M = addend1

override def plus(addend1: Quantity[N, M], addend2: Quantity[N, M])(implicit cc1: CanConvert[M, M], cc2: CanConvert[M, M]): QR =
{
val targetMeasure = plus(addend1.measure, addend2.measure)
val targetMeasure = cam.plus(addend1.measure, addend2.measure)

val a1 = cc1.convert(addend1.measure, targetMeasure).map(cf => qn.timesConstant(addend1.value, cf))
val a2 = cc2.convert(addend2.measure, targetMeasure).map(cf => qn.timesConstant(addend2.value, cf))
Expand All @@ -57,6 +53,23 @@ package object arithmetic
}
}

implicit def lhsCanAddOptionQuantity[N, M <: Measure[M]](implicit qn: QuasiNumeric[N], cam: CanAddMeasure.Aux[M, M, M]) = new CanAddQuantity[N, M, Quantity, M, Option[Quantity[N, M]], M]
{
type QR = Option[Quantity[N, M]]

override def plus(addend1: Quantity[N, M], addend2: Option[Quantity[N, M]])(implicit cc1: CanConvert[M, M], cc2: CanConvert[M, M]): QR =
{
import cats.implicits._

val targetMeasure = addend2.map(m2 => cam.plus(addend1.measure, m2.measure))

val a1 = targetMeasure.flatMap(tm => cc1.convert(addend1.measure, tm).map(cf => qn.timesConstant(addend1.value, cf)))
val a2 = (addend2 |@| targetMeasure map ((a2, tm) => cc2.convert(a2.measure, tm).map(cf => qn.timesConstant(a2.value, cf)))).flatten

a1 |@| a2 |@| targetMeasure map ((a1, a2, tm) => Quantity(qn.plus(a1, a2), tm))
}
}

implicit def canDivideQuantity[N, M1 <: Measure[M1], M2 <: Measure[M2]](implicit qn: QuasiNumeric[N]) =
new CanDivideQuantity[N, M1, Quantity, M2, Quantity, RatioMeasure[M1, M2]]
{
Expand Down Expand Up @@ -93,17 +106,15 @@ package object arithmetic

object unsafe extends SafeArithmeticImplicits
{
implicit def lhsCanAddQuantityUnsafe[N, M <: Measure[M]](implicit qn: QuasiNumeric[N]) = new CanAddQuantity[N, M, Quantity, M, Quantity, M]
implicit def lhsCanAddQuantityUnsafe[N, M <: Measure[M]](implicit qn: QuasiNumeric[N], cam: CanAddMeasure.Aux[M, M, M]) = new CanAddQuantity[N, M, Quantity, M, Quantity[N, M], M]
{
type R = M

type QR = Quantity[N, M]

override def plus(addend1: M, addend2: M): M = addend1

override def plus(addend1: Quantity[N, M], addend2: Quantity[N, M])(implicit cc1: CanConvert[M, M], cc2: CanConvert[M, M]): QR =
{
val targetMeasure = plus(addend1.measure, addend2.measure)
val targetMeasure = cam.plus(addend1.measure, addend2.measure)

val a1 = cc1.convert(addend1.measure, targetMeasure).map(cf => qn.timesConstant(addend1.value, cf))
val a2 = cc2.convert(addend2.measure, targetMeasure).map(cf => qn.timesConstant(addend2.value, cf))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,11 +117,6 @@ package object measure extends DefaultDimensions

trait SafeArithmeticImplicits
{
implicit def exponentialCanExponentiate = new CanExponentiateMeasure[AnyMeasure, AnyMeasure]
{
override def pow(base: AnyMeasure, exponent: Double): AnyMeasure = AnyExponentialMeasure(base, exponent)
}

implicit def productCanMultiply = new CanMultiplyMeasure[AnyMeasure, AnyMeasure, AnyMeasure]
{
override def times(multiplicand: AnyMeasure, multiplier: AnyMeasure): AnyMeasure = AnyProductMeasure(multiplicand, multiplier)
Expand All @@ -132,18 +127,27 @@ package object measure extends DefaultDimensions
override def divide(numerator: AnyMeasure, denominator: AnyMeasure): AnyMeasure = AnyRatioMeasure(numerator, denominator)
}

implicit def lhsCanAddQuantity[N](implicit qn: QuasiNumeric[N]) = new CanAddQuantity[N, AnyMeasure, Quantity, AnyMeasure, Quantity, AnyMeasure]
implicit def lhsCanAddMeasure = new CanAddMeasure[AnyMeasure, AnyMeasure]
{
type R = AnyMeasure

type QR = Option[Quantity[N, AnyMeasure]]
override def plus(addend1: AnyMeasure, addend2: AnyMeasure): R = addend1
}

override def plus(addend1: AnyMeasure, addend2: AnyMeasure): AnyMeasure = addend1
implicit def exponentialCanExponentiate = new CanExponentiateMeasure[AnyMeasure, AnyMeasure]
{
override def pow(base: AnyMeasure, exponent: Double): AnyMeasure = AnyExponentialMeasure(base, exponent)
}

implicit def lhsCanAddQuantity[N](implicit qn: QuasiNumeric[N], cam: CanAddMeasure.Aux[AnyMeasure, AnyMeasure, AnyMeasure]) =
new CanAddQuantity[N, AnyMeasure, Quantity, AnyMeasure, Quantity[N, AnyMeasure], AnyMeasure]
{
type QR = Option[Quantity[N, AnyMeasure]]

override def plus(addend1: Quantity[N, AnyMeasure], addend2: Quantity[N, AnyMeasure])
(implicit cc1: CanConvert[AnyMeasure, AnyMeasure], cc2: CanConvert[AnyMeasure, AnyMeasure]): QR =
{
val targetMeasure = plus(addend1.measure, addend2.measure)
val targetMeasure = cam.plus(addend1.measure, addend2.measure)

val a1 = cc1.convert(addend1.measure, targetMeasure).map(cf => qn.timesConstant(addend1.value, cf))
val a2 = cc2.convert(addend2.measure, targetMeasure).map(cf => qn.timesConstant(addend2.value, cf))
Expand Down Expand Up @@ -186,18 +190,15 @@ package object measure extends DefaultDimensions

object unsafe extends SafeArithmeticImplicits
{
implicit def lhsCanAddQuantityUnsafe[N](implicit qn: QuasiNumeric[N]) = new CanAddQuantity[N, AnyMeasure, Quantity, AnyMeasure, Quantity, AnyMeasure]
implicit def lhsCanAddQuantityUnsafe[N](implicit qn: QuasiNumeric[N], cam: CanAddMeasure.Aux[AnyMeasure, AnyMeasure, AnyMeasure]) =
new CanAddQuantity[N, AnyMeasure, Quantity, AnyMeasure, Quantity[N, AnyMeasure], AnyMeasure]
{
type R = AnyMeasure

type QR = Quantity[N, AnyMeasure]

override def plus(addend1: AnyMeasure, addend2: AnyMeasure): AnyMeasure = addend1

override def plus(addend1: Quantity[N, AnyMeasure], addend2: Quantity[N, AnyMeasure])
(implicit cc1: CanConvert[AnyMeasure, AnyMeasure], cc2: CanConvert[AnyMeasure, AnyMeasure]): QR =
{
val targetMeasure = plus(addend1.measure, addend2.measure)
val targetMeasure = cam.plus(addend1.measure, addend2.measure)

val a1 = cc1.convert(addend1.measure, targetMeasure).map(cf => qn.timesConstant(addend1.value, cf))
val a2 = cc2.convert(addend2.measure, targetMeasure).map(cf => qn.timesConstant(addend2.value, cf))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ class QuantitySpec extends FlatSpec with Matchers
(10.kg - 3.kg) should equal(Some(7 kg))
(10.kg + 3.lb) should equal(Some(11.360775642116007 kg))
(10.lb - 3.kg) should equal(Some(3.386125 lb))

10.kg + (3.lb to kg) should equal(Some(11.360775642116007.kg))
10.kg - (3.lb to kg) should equal(Some(8.639224357883993.kg))
}

it should "be passable to typesafe method" in
Expand Down

0 comments on commit 9ad9cdb

Please sign in to comment.