Skip to content

Commit

Permalink
Add tests for Signed/TruncatedDivision
Browse files Browse the repository at this point in the history
Finishes merging typelevel/algebra#248
  • Loading branch information
armanbilge committed Jun 19, 2021
1 parent 2943b54 commit 5c4d170
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 1 deletion.
104 changes: 104 additions & 0 deletions algebra-laws/shared/src/main/scala/algebra/laws/OrderLaws.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ import org.scalacheck.{Arbitrary, Cogen, Prop}
import org.scalacheck.Prop._

import cats.kernel.instances.all._
import algebra.ring.Signed
import algebra.ring.CommutativeRing
import algebra.ring.TruncatedDivision
import algebra.ring.AdditiveCommutativeGroup
import algebra.ring.GCDRing

@deprecated("Provided by cats.kernel.laws", since = "2.7.0")
object OrderLaws {
Expand Down Expand Up @@ -114,6 +119,105 @@ trait OrderLaws[A] extends Laws {
}
)

def signed(implicit A: Signed[A]) = new OrderProperties(
name = "signed",
parent = Some(order(A.order)),
"abs non-negative" -> forAll((x: A) => A.sign(A.abs(x)) != Signed.Negative),
"signum returns -1/0/1" -> forAll((x: A) => A.signum(A.abs(x)) <= 1),
"signum is sign.toInt" -> forAll((x: A) => A.signum(x) == A.sign(x).toInt),
"ordered group" -> forAll { (x: A, y: A, z: A) =>
A.order.lteqv(x, y) ==> A.order.lteqv(A.additiveCommutativeMonoid.plus(x, z),
A.additiveCommutativeMonoid.plus(y, z)
)
},
"triangle inequality" -> forAll { (x: A, y: A) =>
A.order.lteqv(A.abs(A.additiveCommutativeMonoid.plus(x, y)), A.additiveCommutativeMonoid.plus(A.abs(x), A.abs(y)))
}
)

def signedAdditiveCommutativeGroup(implicit signedA: Signed[A], A: AdditiveCommutativeGroup[A]) = new DefaultRuleSet(
name = "signedAdditiveAbGroup",
parent = Some(signed),
"abs(x) equals abs(-x)" -> forAll { (x: A) =>
signedA.abs(x) ?== signedA.abs(A.negate(x))
}
)

// more a convention: as GCD is defined up to a unit, so up to a sign,
// on an ordered GCD ring we require gcd(x, y) >= 0, which is the common
// behavior of computer algebra systems
def signedGCDRing(implicit signedA: Signed[A], A: GCDRing[A]) = new DefaultRuleSet(
name = "signedGCDRing",
parent = Some(signedAdditiveCommutativeGroup),
"gcd(x, y) >= 0" -> forAll { (x: A, y: A) =>
signedA.isSignNonNegative(A.gcd(x, y))
},
"gcd(x, 0) === abs(x)" -> forAll { (x: A) =>
A.gcd(x, A.zero) ?== signedA.abs(x)
}
)

def truncatedDivision(implicit ring: CommutativeRing[A], A: TruncatedDivision[A]) = new DefaultRuleSet(
name = "truncatedDivision",
parent = Some(signed),
"division rule (tquotmod)" -> forAll { (x: A, y: A) =>
A.isSignNonZero(y) ==> {
val (q, r) = A.tquotmod(x, y)
x ?== ring.plus(ring.times(y, q), r)
}
},
"division rule (fquotmod)" -> forAll { (x: A, y: A) =>
A.isSignNonZero(y) ==> {
val (q, r) = A.fquotmod(x, y)
x ?== ring.plus(ring.times(y, q), r)
}
},
"|r| < |y| (tmod)" -> forAll { (x: A, y: A) =>
A.isSignNonZero(y) ==> {
val r = A.tmod(x, y)
A.order.lt(A.abs(r), A.abs(y))
}
},
"|r| < |y| (fmod)" -> forAll { (x: A, y: A) =>
A.isSignNonZero(y) ==> {
val r = A.fmod(x, y)
A.order.lt(A.abs(r), A.abs(y))
}
},
"r = 0 or sign(r) = sign(x) (tmod)" -> forAll { (x: A, y: A) =>
A.isSignNonZero(y) ==> {
val r = A.tmod(x, y)
A.isSignZero(r) || (A.sign(r) ?== A.sign(x))
}
},
"r = 0 or sign(r) = sign(y) (fmod)" -> forAll { (x: A, y: A) =>
A.isSignNonZero(y) ==> {
val r = A.fmod(x, y)
A.isSignZero(r) || (A.sign(r) ?== A.sign(y))
}
},
"tquot" -> forAll { (x: A, y: A) =>
A.isSignNonZero(y) ==> {
A.tquotmod(x, y)._1 ?== A.tquot(x, y)
}
},
"tmod" -> forAll { (x: A, y: A) =>
A.isSignNonZero(y) ==> {
A.tquotmod(x, y)._2 ?== A.tmod(x, y)
}
},
"fquot" -> forAll { (x: A, y: A) =>
A.isSignNonZero(y) ==> {
A.fquotmod(x, y)._1 ?== A.fquot(x, y)
}
},
"fmod" -> forAll { (x: A, y: A) =>
A.isSignNonZero(y) ==> {
A.fquotmod(x, y)._2 ?== A.fmod(x, y)
}
}
)

class OrderProperties(
name: String,
parent: Option[RuleSet],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ object FPApprox {
def exact[A: Rng: Order](a: A): FPApprox[A] = FPApprox(a, abs(a), 0)
def approx[A: Rng: Order](a: A): FPApprox[A] = FPApprox(a, abs(a), 1)

trait Epsilon[A] {
trait Epsilon[A] extends Serializable {
def minValue: A
def epsilon: A
def isFinite(a: A): Boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,9 @@ class LawTests extends munit.DisciplineSuite {
checkAll("Long", RingLaws[Long].commutativeRing)
checkAll("Long", LatticeLaws[Long].boundedDistributiveLattice)

checkAll("BigInt", OrderLaws[BigInt].truncatedDivision)
checkAll("BigInt", RingLaws[BigInt].euclideanRing)
checkAll("BigInt", OrderLaws[BigInt].signedGCDRing)

checkAll("FPApprox[Float]", RingLaws[FPApprox[Float]].approxField)
checkAll("FPApprox[Double]", RingLaws[FPApprox[Double]].approxField)
Expand Down

0 comments on commit 5c4d170

Please sign in to comment.