Skip to content

Commit b0c1ced

Browse files
committed
More laws for Signed
Implements typelevel/algebra#248
1 parent 7ab3d90 commit b0c1ced

File tree

3 files changed

+41
-4
lines changed

3 files changed

+41
-4
lines changed

algebra-core/src/main/scala/cats/algebra/ring/Signed.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import scala.{specialized => sp}
1717
*
1818
* (3) `abs(x) = -x` if `x < 0`, or `x` otherwise,
1919
*
20-
* Laws (1) and (2) lead to the triange inequality:
20+
* Laws (1) and (2) lead to the triangle inequality:
2121
*
2222
* (4) `abs(a + b) <= abs(a) + abs(b)`
2323
*
@@ -27,7 +27,7 @@ import scala.{specialized => sp}
2727
* It's better to have the Signed hierarchy separate from the Ring/Order hierarchy, so that
2828
* we do not end up with duplicate implicits.
2929
*/
30-
trait Signed[@sp(Byte, Short, Int, Long, Float, Double) A] extends Any {
30+
trait Signed[@sp(Byte, Short, Int, Long, Float, Double) A] extends Any with Serializable {
3131

3232
def additiveCommutativeMonoid: AdditiveCommutativeMonoid[A]
3333
def order: Order[A]

algebra-laws/shared/src/main/scala/cats/algebra/laws/SignedTests.scala

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
package cats.algebra.laws
22

3-
import cats.algebra.ring.{CommutativeRing, Signed, TruncatedDivision}
3+
import cats.algebra.ring.{
4+
AdditiveCommutativeGroup,
5+
CommutativeRing,
6+
GCDRing,
7+
Signed,
8+
TruncatedDivision
9+
}
410
import cats.kernel._
511
import org.scalacheck.{Arbitrary, Cogen, Prop}
612
import org.scalacheck.Prop._
@@ -30,7 +36,37 @@ trait SignedTests[A] extends OrderTests[A] {
3036
"abs non-negative" -> forAll((x: A) => A.sign(A.abs(x)) != Signed.Negative),
3137
"abs never less than" -> forAll((x: A) => A.order.gteqv(A.abs(x), x)),
3238
"signum returns -1/0/1" -> forAll((x: A) => A.signum(A.abs(x)) <= 1),
33-
"signum is sign.toInt" -> forAll((x: A) => A.signum(x) == A.sign(x).toInt)
39+
"signum is sign.toInt" -> forAll((x: A) => A.signum(x) == A.sign(x).toInt),
40+
"linear order" -> forAll { (x: A, y: A, z: A) =>
41+
A.order.lteqv(x, y) ==> A.order.lteqv(A.additiveCommutativeMonoid.plus(x, z),
42+
A.additiveCommutativeMonoid.plus(y, z)
43+
)
44+
},
45+
"triangle inequality" -> forAll { (x: A, y: A) =>
46+
A.order.lteqv(A.abs(A.additiveCommutativeMonoid.plus(x, y)), A.additiveCommutativeMonoid.plus(A.abs(x), A.abs(y)))
47+
}
48+
)
49+
50+
def signedAdditiveCommutativeGroup(implicit signedA: Signed[A], A: AdditiveCommutativeGroup[A]) = new DefaultRuleSet(
51+
name = "signedAdditiveAbGroup",
52+
parent = Some(signed),
53+
"abs(x) equals abs(-x)" -> forAll { (x: A) =>
54+
signedA.abs(x) ?== signedA.abs(A.negate(x))
55+
}
56+
)
57+
58+
// more a convention: as GCD is defined up to a unit, so up to a sign,
59+
// on an ordered GCD ring we require gcd(x, y) >= 0, which is the common
60+
// behavior of computer algebra systems
61+
def signedGCDRing(implicit signedA: Signed[A], A: GCDRing[A]) = new DefaultRuleSet(
62+
name = "signedGCDRing",
63+
parent = Some(signedAdditiveCommutativeGroup),
64+
"gcd(x, y) >= 0" -> forAll { (x: A, y: A) =>
65+
signedA.isSignNonNegative(A.gcd(x, y))
66+
},
67+
"gcd(x, 0) === abs(x)" -> forAll { (x: A) =>
68+
A.gcd(x, A.zero) ?== signedA.abs(x)
69+
}
3470
)
3571

3672
def truncatedDivision(implicit ring: CommutativeRing[A], A: TruncatedDivision[A]) = new DefaultRuleSet(

algebra-laws/shared/src/test/scala/cats/algebra/laws/LawTests.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ class LawTests extends munit.DisciplineSuite {
8181

8282
checkAll("BigInt", RingTests[BigInt].euclideanRing)
8383
checkAll("BigInt", SignedTests[BigInt].truncatedDivision)
84+
checkAll("BigInt", SignedTests[BigInt].signedGCDRing)
8485

8586
checkAll("FPApprox[Float]", RingTests[FPApprox[Float]].approxField)
8687
checkAll("FPApprox[Double]", RingTests[FPApprox[Double]].approxField)

0 commit comments

Comments
 (0)