Skip to content
This repository has been archived by the owner on Feb 8, 2022. It is now read-only.

Combination of Signed/TruncatedDivision/GCDRing/EuclideanRing #248

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
45 changes: 42 additions & 3 deletions core/src/main/scala/algebra/instances/bigInt.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,22 @@ package algebra
package instances

import algebra.ring._
import cats.kernel.instances.BigIntOrder
import cats.kernel.{Hash, UnboundedEnumerable}

package object bigInt extends BigIntInstances

trait BigIntInstances extends cats.kernel.instances.BigIntInstances {
implicit val bigIntAlgebra: BigIntAlgebra =
new BigIntAlgebra
private val instance: BigIntAlgebra = new BigIntAlgebra

implicit def bigIntAlgebra: EuclideanRing[BigInt] = instance

implicit def bigIntTruncatedDivision: TruncatedDivision[BigInt] = instance
}

class BigIntAlgebra extends CommutativeRing[BigInt] with Serializable {
class BigIntAlgebra extends EuclideanRing[BigInt] with TruncatedDivision.forCommutativeRing[BigInt] with Serializable {

override def compare(x: BigInt, y: BigInt): Int = x.compare(y)

val zero: BigInt = BigInt(0)
val one: BigInt = BigInt(1)
Expand All @@ -25,4 +32,36 @@ class BigIntAlgebra extends CommutativeRing[BigInt] with Serializable {

override def fromInt(n: Int): BigInt = BigInt(n)
override def fromBigInt(n: BigInt): BigInt = n

def tquot(x: BigInt, y: BigInt): BigInt = x / y
def tmod(x: BigInt, y: BigInt): BigInt = x % y
override def tquotmod(x: BigInt, y: BigInt): (BigInt, BigInt) = x /% y

override def lcm(a: BigInt, b: BigInt)(implicit ev: Eq[BigInt]): BigInt =
if (a.signum == 0 || b.signum == 0) zero else (a / a.gcd(b)) * b
override def gcd(a: BigInt, b: BigInt)(implicit ev: Eq[BigInt]): BigInt = a.gcd(b)

def euclideanFunction(a: BigInt): BigInt = a.abs

override def equotmod(a: BigInt, b: BigInt): (BigInt, BigInt) = {
val (qt, rt) = a /% b // truncated quotient and remainder
if (rt.signum >= 0) (qt, rt)
else if (b.signum > 0) (qt - 1, rt + b)
else (qt + 1, rt - b)
}

def equot(a: BigInt, b: BigInt): BigInt = {
val (qt, rt) = a /% b // truncated quotient and remainder
if (rt.signum >= 0) qt
else if (b.signum > 0) qt - 1
else qt + 1
}

def emod(a: BigInt, b: BigInt): BigInt = {
val rt = a % b // truncated remainder
if (rt.signum >= 0) rt
else if (b > 0) rt + b
else rt - b
}

}
22 changes: 22 additions & 0 deletions core/src/main/scala/algebra/ring/DivisionRing.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package algebra
package ring

import scala.{specialized => sp}

trait DivisionRing[@sp(Byte, Short, Int, Long, Float, Double) A] extends Any with Ring[A] with MultiplicativeGroup[A] {
self =>

def fromDouble(a: Double): A = Field.defaultFromDouble[A](a)(self, self)

}

trait DivisionRingFunctions[F[T] <: DivisionRing[T]] extends RingFunctions[F] with MultiplicativeGroupFunctions[F] {
def fromDouble[@sp(Int, Long, Float, Double) A](n: Double)(implicit ev: F[A]): A =
ev.fromDouble(n)
}

object DivisionRing extends DivisionRingFunctions[DivisionRing] {

@inline final def apply[A](implicit f: DivisionRing[A]): DivisionRing[A] = f

}
59 changes: 59 additions & 0 deletions core/src/main/scala/algebra/ring/EuclideanRing.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package algebra
package ring

import scala.annotation.tailrec
import scala.{specialized => sp}

/**
* EuclideanRing implements a Euclidean domain.
*
* The formal definition says that every euclidean domain A has (at
* least one) euclidean function f: A -> N (the natural numbers) where:
*
* (for every x and non-zero y) x = yq + r, and r = 0 or f(r) < f(y).
*
* This generalizes the Euclidean division of integers, where f represents
* a measure of length (or absolute value), and the previous equation
* represents finding the quotient and remainder of x and y. So:
*
* quot(x, y) = q
* mod(x, y) = r
*/
trait EuclideanRing[@sp(Int, Long, Float, Double) A] extends Any with GCDRing[A] { self =>
def euclideanFunction(a: A): BigInt
def equot(a: A, b: A): A
def emod(a: A, b: A): A
def equotmod(a: A, b: A): (A, A) = (equot(a, b), emod(a, b))
def gcd(a: A, b: A)(implicit ev: Eq[A]): A =
EuclideanRing.euclid(a, b)(ev, self)
def lcm(a: A, b: A)(implicit ev: Eq[A]): A =
if (isZero(a) || isZero(b)) zero else times(equot(a, gcd(a, b)), b)
// def xgcd(a: A, b: A)(implicit ev: Eq[A]): (A, A, A) =
}

trait EuclideanRingFunctions[R[T] <: EuclideanRing[T]] extends GCDRingFunctions[R] {
def euclideanFunction[@sp(Int, Long, Float, Double) A](a: A)(implicit ev: R[A]): BigInt =
ev.euclideanFunction(a)
def equot[@sp(Int, Long, Float, Double) A](a: A, b: A)(implicit ev: R[A]): A =
ev.equot(a, b)
def emod[@sp(Int, Long, Float, Double) A](a: A, b: A)(implicit ev: R[A]): A =
ev.emod(a, b)
def equotmod[@sp(Int, Long, Float, Double) A](a: A, b: A)(implicit ev: R[A]): (A, A) =
ev.equotmod(a, b)
}

object EuclideanRing extends EuclideanRingFunctions[EuclideanRing] {

@inline final def apply[A](implicit e: EuclideanRing[A]): EuclideanRing[A] = e

/**
* Simple implementation of Euclid's algorithm for gcd
*/
@tailrec final def euclid[@sp(Int, Long, Float, Double) A: Eq: EuclideanRing](a: A, b: A): A = {
if (EuclideanRing[A].isZero(b)) a else euclid(b, EuclideanRing[A].emod(a, b))
}

/* @tailrec final def extendedEuclid[@sp(Int, Long, Float, Double) A: Eq: EuclideanRing](a: A, b: A): (A, A, A) = {
if (EuclideanRing[A].isZero(b)) a else euclid(b, EuclideanRing[A].emod(a, b))*/

}
17 changes: 15 additions & 2 deletions core/src/main/scala/algebra/ring/Field.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,20 @@ package ring

import scala.{ specialized => sp }

trait Field[@sp(Int, Long, Float, Double) A] extends Any with CommutativeRing[A] with MultiplicativeCommutativeGroup[A] { self =>
trait Field[@sp(Int, Long, Float, Double) A] extends Any with EuclideanRing[A] with MultiplicativeCommutativeGroup[A] { self =>

// default implementations for GCD

override def gcd(a: A, b: A)(implicit eqA: Eq[A]): A =
if (isZero(a) && isZero(b)) zero else one
override def lcm(a: A, b: A)(implicit eqA: Eq[A]): A = times(a, b)

// default implementations for Euclidean division in a field (as every nonzero element is a unit!)

def euclideanFunction(a: A): BigInt = BigInt(0)
def equot(a: A, b: A): A = div(a, b)
def emod(a: A, b: A): A = zero
override def equotmod(a: A, b: A): (A, A) = (div(a, b), zero)

/**
* This is implemented in terms of basic Field ops. However, this is
Expand All @@ -17,7 +30,7 @@ trait Field[@sp(Int, Long, Float, Double) A] extends Any with CommutativeRing[A]

}

trait FieldFunctions[F[T] <: Field[T]] extends RingFunctions[F] with MultiplicativeGroupFunctions[F] {
trait FieldFunctions[F[T] <: Field[T]] extends EuclideanRingFunctions[F] with MultiplicativeGroupFunctions[F] {
def fromDouble[@sp(Int, Long, Float, Double) A](n: Double)(implicit ev: F[A]): A =
ev.fromDouble(n)
}
Expand Down
41 changes: 41 additions & 0 deletions core/src/main/scala/algebra/ring/GCDRing.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package algebra
package ring

import scala.{specialized => sp}

/**
* GCDRing implements a GCD ring.
*
* For two elements x and y in a GCD ring, we can choose two elements d and m
* such that:
*
* d = gcd(x, y)
* m = lcm(x, y)
*
* d * m = x * y
*
* Additionally, we require:
*
* gcd(0, 0) = 0
* lcm(x, 0) = lcm(0, x) = 0
*
* and commutativity:
*
* gcd(x, y) = gcd(y, x)
* lcm(x, y) = lcm(y, x)
*/
trait GCDRing[@sp(Int, Long, Float, Double) A] extends Any with CommutativeRing[A] {
def gcd(a: A, b: A)(implicit ev: Eq[A]): A
def lcm(a: A, b: A)(implicit ev: Eq[A]): A
}

trait GCDRingFunctions[R[T] <: GCDRing[T]] extends RingFunctions[R] {
def gcd[@sp(Int, Long, Float, Double) A](a: A, b: A)(implicit ev: R[A], eqA: Eq[A]): A =
ev.gcd(a, b)(eqA)
def lcm[@sp(Int, Long, Float, Double) A](a: A, b: A)(implicit ev: R[A], eqA: Eq[A]): A =
ev.lcm(a, b)(eqA)
}

object GCDRing extends GCDRingFunctions[GCDRing] {
@inline final def apply[A](implicit ev: GCDRing[A]): GCDRing[A] = ev
}
141 changes: 141 additions & 0 deletions core/src/main/scala/algebra/ring/Signed.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
package algebra
package ring

import scala.{specialized => sp}

/**
* A trait that expresses the existence of signs and absolute values on linearly ordered additive commutative monoids
* (i.e. types with addition and a zero).
*
* The following laws holds:
*
* (1) if `a <= b` then `a + c <= b + c` (linear order),
* (2) `signum(x) = -1` if `x < 0`, `signum(x) = 1` if `x > 0`, `signum(x) = 0` otherwise,
*
* Negative elements only appear when the scalar is taken from a additive abelian group. Then:
*
* (3) `abs(x) = -x` if `x < 0`, or `x` otherwise,
*
* Laws (1) and (2) lead to the triange inequality:
*
* (4) `abs(a + b) <= abs(a) + abs(b)`
*
* Signed should never be extended in implementations, rather the [[Signed.forAdditiveCommutativeMonoid]] and
* [[Signed.forAdditiveCommutativeGroup subtraits]].
*
* It's better to have the Eq/PartialOrder/Order/Signed hierarchy separate from the Ring hierarchy, so that
* we do not end up with duplicate implicits. At the same time, we cannot use self-types to express
* the constraint that Signed must be an [[AdditiveCommutativeMonoid]], due to interaction with specialization.
*/
trait Signed[@sp(Byte, Short, Int, Long, Float, Double) A] extends Any with Order[A] {

/**
* Returns Zero if `a` is 0, Positive if `a` is positive, and Negative is `a` is negative.
*/
def sign(a: A): Signed.Sign = Signed.Sign(signum(a))

/**
* Returns 0 if `a` is 0, 1 if `a` is positive, and -1 is `a` is negative.
*/
def signum(a: A): Int

/**
* An idempotent function that ensures an object has a non-negative sign.
*/
def abs(a: A): A

def isSignZero(a: A): Boolean = signum(a) == 0
def isSignPositive(a: A): Boolean = signum(a) > 0
def isSignNegative(a: A): Boolean = signum(a) < 0

def isSignNonZero(a: A): Boolean = signum(a) != 0
def isSignNonPositive(a: A): Boolean = signum(a) <= 0
def isSignNonNegative(a: A): Boolean = signum(a) >= 0
}

trait SignedFunctions[S[T] <: Signed[T]] extends cats.kernel.OrderFunctions[S] {
def sign[@sp(Int, Long, Float, Double) A](a: A)(implicit ev: S[A]): Signed.Sign =
ev.sign(a)
def signum[@sp(Int, Long, Float, Double) A](a: A)(implicit ev: S[A]): Int =
ev.signum(a)
def abs[@sp(Int, Long, Float, Double) A](a: A)(implicit ev: S[A]): A =
ev.abs(a)
def isSignZero[@sp(Int, Long, Float, Double) A](a: A)(implicit ev: S[A]): Boolean =
ev.isSignZero(a)
def isSignPositive[@sp(Int, Long, Float, Double) A](a: A)(implicit ev: S[A]): Boolean =
ev.isSignPositive(a)
def isSignNegative[@sp(Int, Long, Float, Double) A](a: A)(implicit ev: S[A]): Boolean =
ev.isSignNegative(a)
def isSignNonZero[@sp(Int, Long, Float, Double) A](a: A)(implicit ev: S[A]): Boolean =
ev.isSignNonZero(a)
def isSignNonPositive[@sp(Int, Long, Float, Double) A](a: A)(implicit ev: S[A]): Boolean =
ev.isSignNonPositive(a)
def isSignNonNegative[@sp(Int, Long, Float, Double) A](a: A)(implicit ev: S[A]): Boolean =
ev.isSignNonNegative(a)
}

object Signed extends SignedFunctions[Signed] {

/** Signed implementation for additive commutative monoids */
trait forAdditiveCommutativeMonoid[A] extends Any with Signed[A] with AdditiveCommutativeMonoid[A] {
def signum(a: A): Int = {
val c = compare(a, zero)
if (c < 0) -1
else if (c > 0) 1
else 0
}
}

/** Signed implementation for additive commutative groups */
trait forAdditiveCommutativeGroup[A] extends Any with forAdditiveCommutativeMonoid[A] with AdditiveCommutativeGroup[A] {
def abs(a: A): A = if (compare(a, zero) < 0) negate(a) else a
}

def apply[A](implicit s: Signed[A]): Signed[A] = s

/**
* A simple ADT representing the `Sign` of an object.
*/
sealed abstract class Sign(val toInt: Int) {
def unary_- : Sign = this match {
case Positive => Negative
case Negative => Positive
case Zero => Zero
}

def *(that: Sign): Sign = Sign(this.toInt * that.toInt)

def **(that: Int): Sign = this match {
case Positive => Positive
case Zero if that == 0 => Positive
case Zero => Zero
case Negative if (that % 2) == 0 => Positive
case Negative => Negative
}
}

case object Zero extends Sign(0)
case object Positive extends Sign(1)
case object Negative extends Sign(-1)

object Sign {
implicit def sign2int(s: Sign): Int = s.toInt

def apply(i: Int): Sign =
if (i == 0) Zero else if (i > 0) Positive else Negative

private val instance: CommutativeMonoid[Sign] with MultiplicativeCommutativeMonoid[Sign] with Eq[Sign] =
new CommutativeMonoid[Sign] with MultiplicativeCommutativeMonoid[Sign] with Eq[Sign] {
def eqv(x: Sign, y: Sign): Boolean = x == y
def empty: Sign = Positive
def combine(x: Sign, y: Sign): Sign = x*y
def one: Sign = Positive
def times(x: Sign, y: Sign): Sign = x*y
}

implicit final def signMultiplicativeMonoid: MultiplicativeCommutativeMonoid[Sign] = instance
implicit final def signMonoid: CommutativeMonoid[Sign] = instance
implicit final def signEq: Eq[Sign] = instance
}

}
Loading