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

Move typelevel/algebra into cats repo #3877

Closed
wants to merge 18 commits into from
Closed
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
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ functionality, you can pick-and-choose from amongst these modules
* `cats-kernel`: Small set of basic type classes (*required*).
* `cats-core`: Most core type classes and functionality (*required*).
* `cats-laws`: Laws for testing type class instances.
* `cats-algebra`: Type classes to represent algebraic structures.
* `cats-free`: Free structures such as the free monad, and supporting type classes.
* `cats-testkit`: lib for writing tests for type class instances using laws.
* `alleycats-core`: Cats instances and classes which are not lawful.
Expand All @@ -140,10 +141,11 @@ Links:
2. ScalaDoc: [typelevel.org/cats/api/](https://typelevel.org/cats/api/)
3. Type classes: [typelevel.org/cats/typeclasses](https://typelevel.org/cats/typeclasses.html)
4. Data types: [typelevel.org/cats/datatypes.html](https://typelevel.org/cats/datatypes.html)
5. Glossary: [typelevel.org/cats/nomenclature.html](https://typelevel.org/cats/nomenclature.html)
6. Resources for Learners: [typelevel.org/cats/resources_for_learners.html](https://typelevel.org/cats/resources_for_learners.html)
7. FAQ: [typelevel.org/cats/faq.html](https://typelevel.org/cats/faq.html)
8. The Typelevel Ecosystem: [typelevel.org/cats/typelevelEcosystem.html](https://typelevel.org/cats/typelevelEcosystem.html)
5. Algebra overview: [typelevel.org/cats/algebra.html](https://typelevel.org/cats/algebra.html)
6. Glossary: [typelevel.org/cats/nomenclature.html](https://typelevel.org/cats/nomenclature.html)
7. Resources for Learners: [typelevel.org/cats/resources_for_learners.html](https://typelevel.org/cats/resources_for_learners.html)
8. FAQ: [typelevel.org/cats/faq.html](https://typelevel.org/cats/faq.html)
9. The Typelevel Ecosystem: [typelevel.org/cats/typelevelEcosystem.html](https://typelevel.org/cats/typelevelEcosystem.html)

### Community

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package cats
package algebra.instances

import scala.annotation.tailrec

object StaticMethods {

/**
* Exponentiation function, e.g. x^y
*
* If base^ex doesn't fit in a Long, the result will overflow (unlike
* Math.pow which will return +/- Infinity).
*/
final def pow(base: Long, exponent: Long): Long = {
@tailrec def loop(t: Long, b: Long, e: Long): Long =
if (e == 0L) t
else if ((e & 1) == 1) loop(t * b, b * b, e >>> 1L)
else loop(t, b * b, e >>> 1L)

if (exponent >= 0L) loop(1L, base, exponent)
else {
if (base == 0L) throw new ArithmeticException("zero can't be raised to negative power")
else if (base == 1L) 1L
else if (base == -1L) if ((exponent & 1L) == 0L) -1L else 1L
else 0L
}
}
}
22 changes: 22 additions & 0 deletions algebra-core/src/main/scala/cats/algebra/instances/all.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package cats
package algebra
package instances

object all extends AllInstances

trait AllInstances
extends ArrayInstances
with BigDecimalInstances
with BigIntInstances
with BitSetInstances
with BooleanInstances
with ByteInstances
with DoubleInstances
with FloatInstances
with IntInstances
with LongInstances
with MapInstances
with SetInstances
with ShortInstances
with TupleInstances
with UnitInstances
71 changes: 71 additions & 0 deletions algebra-core/src/main/scala/cats/algebra/instances/array.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package cats
package algebra
package instances

import scala.{specialized => sp}

package object array extends ArrayInstances

trait ArrayInstances {
implicit def catsAlgebraStdEqForArray[@sp A: Eq]: Eq[Array[A]] =
new ArrayEq[A]
implicit def catsAlgebraStdOrderForArray[@sp A: Order]: Order[Array[A]] =
new ArrayOrder[A]
implicit def catsAlgebraStdPartialOrderForArray[@sp A: PartialOrder]: PartialOrder[Array[A]] =
new ArrayPartialOrder[A]
}

private object ArraySupport {

private def signum(x: Int): Int =
if (x < 0) -1
else if (x > 0) 1
else 0

def eqv[@sp A](x: Array[A], y: Array[A])(implicit ev: Eq[A]): Boolean = {
var i = 0
if (x.length != y.length) return false
while (i < x.length && i < y.length && ev.eqv(x(i), y(i))) i += 1
i == x.length
}

def compare[@sp A](x: Array[A], y: Array[A])(implicit ev: Order[A]): Int = {
var i = 0
while (i < x.length && i < y.length) {
val cmp = ev.compare(x(i), y(i))
if (cmp != 0) return cmp
i += 1
}
signum(x.length - y.length)
}

def partialCompare[@sp A](x: Array[A], y: Array[A])(implicit ev: PartialOrder[A]): Double = {
var i = 0
while (i < x.length && i < y.length) {
val cmp = ev.partialCompare(x(i), y(i))
// Double.NaN is also != 0.0
if (cmp != 0.0) return cmp
i += 1
}
signum(x.length - y.length).toDouble
}
}

final private class ArrayEq[@sp A: Eq] extends Eq[Array[A]] with Serializable {
def eqv(x: Array[A], y: Array[A]): Boolean =
ArraySupport.eqv(x, y)
}

final private class ArrayOrder[@sp A: Order] extends Order[Array[A]] with Serializable {
override def eqv(x: Array[A], y: Array[A]): Boolean =
ArraySupport.eqv(x, y)
def compare(x: Array[A], y: Array[A]): Int =
ArraySupport.compare(x, y)
}

final private class ArrayPartialOrder[@sp A: PartialOrder] extends PartialOrder[Array[A]] with Serializable {
override def eqv(x: Array[A], y: Array[A]): Boolean =
ArraySupport.eqv(x, y)
override def partialCompare(x: Array[A], y: Array[A]): Double =
ArraySupport.partialCompare(x, y)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package cats
package algebra
package instances

import java.math.MathContext

import cats.algebra.ring._

object bigDecimal extends BigDecimalInstances

trait BigDecimalInstances {
implicit val catsAlgebraStdAlgebraForBigDecimal: BigDecimalAlgebra = new BigDecimalAlgebra()
}

class BigDecimalAlgebra(mc: MathContext) extends Field[BigDecimal] with Serializable {
def this() = this(MathContext.UNLIMITED)

val zero: BigDecimal = BigDecimal(0, mc)
val one: BigDecimal = BigDecimal(1, mc)

def plus(a: BigDecimal, b: BigDecimal): BigDecimal = a + b
def negate(a: BigDecimal): BigDecimal = -a
override def minus(a: BigDecimal, b: BigDecimal): BigDecimal = a - b

def times(a: BigDecimal, b: BigDecimal): BigDecimal = a * b
def div(a: BigDecimal, b: BigDecimal): BigDecimal = a / b

override def pow(a: BigDecimal, k: Int): BigDecimal = a.pow(k)

override def fromInt(n: Int): BigDecimal = BigDecimal(n, mc)
override def fromBigInt(n: BigInt): BigDecimal = BigDecimal(n, mc)
}
63 changes: 63 additions & 0 deletions algebra-core/src/main/scala/cats/algebra/instances/bigInt.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package cats
package algebra
package instances

import cats.algebra.ring._

object bigInt extends BigIntInstances

trait BigIntInstances {
private val instance: BigIntAlgebra = new BigIntAlgebra
implicit def catsAlgebraStdAlgebraForBigInt: EuclideanRing[BigInt] = instance
implicit def catsAlgebraStdTruncatedDivisionForBigInt: TruncatedDivision[BigInt] = instance
}

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

val zero: BigInt = BigInt(0)
val one: BigInt = BigInt(1)

def plus(a: BigInt, b: BigInt): BigInt = a + b
def negate(a: BigInt): BigInt = -a
override def minus(a: BigInt, b: BigInt): BigInt = a - b

def times(a: BigInt, b: BigInt): BigInt = a * b

override def pow(a: BigInt, k: Int): BigInt = a.pow(k)

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

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
}

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

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

override def order: Order[BigInt] = cats.kernel.instances.bigInt.catsKernelStdOrderForBigInt
}
22 changes: 22 additions & 0 deletions algebra-core/src/main/scala/cats/algebra/instances/bitSet.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package cats
package algebra
package instances

import scala.collection.immutable.BitSet

import cats.algebra.lattice._

object bitSet extends BitSetInstances

trait BitSetInstances {
implicit val catsAlgebraStdAlgebraForBitSet: BitSetAlgebra =
new BitSetAlgebra
}

class BitSetAlgebra extends GenBool[BitSet] with Serializable {
val zero: BitSet = BitSet.empty
def and(a: BitSet, b: BitSet): BitSet = a & b
def or(a: BitSet, b: BitSet): BitSet = a | b
def without(a: BitSet, b: BitSet): BitSet = a -- b
override def xor(a: BitSet, b: BitSet): BitSet = a ^ b
}
43 changes: 43 additions & 0 deletions algebra-core/src/main/scala/cats/algebra/instances/boolean.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package cats
package algebra
package instances

import cats.algebra.lattice.Bool
import cats.algebra.ring.BoolRing
import cats.algebra.ring.CommutativeRig

object boolean extends BooleanInstances

trait BooleanInstances {
implicit val catsAlgebraStdAlgebraForBoolean: BooleanAlgebra =
new BooleanAlgebra

val catsAlgebraStdRingForBoolean: BoolRing[Boolean] = new BoolRing[Boolean] {
def zero: Boolean = false
def one: Boolean = true
def plus(x: Boolean, y: Boolean): Boolean = x ^ y
def times(x: Boolean, y: Boolean): Boolean = x && y
}
}

/**
* This commutative rig is different than the one obtained from GF(2).
*
* It uses || for plus, and && for times.
*/
class BooleanAlgebra extends Bool[Boolean] with CommutativeRig[Boolean] {

def zero: Boolean = false
def one: Boolean = true

override def isZero(x: Boolean)(implicit ev: Eq[Boolean]): Boolean = !x
override def isOne(x: Boolean)(implicit ev: Eq[Boolean]): Boolean = x

def and(x: Boolean, y: Boolean): Boolean = x && y
def or(x: Boolean, y: Boolean): Boolean = x || y
def complement(x: Boolean): Boolean = !x

def plus(a: Boolean, b: Boolean): Boolean = a || b
override def pow(a: Boolean, b: Int): Boolean = a
override def times(a: Boolean, b: Boolean): Boolean = a && b
}
33 changes: 33 additions & 0 deletions algebra-core/src/main/scala/cats/algebra/instances/byte.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package cats
package algebra
package instances

import cats.algebra.lattice._
import cats.algebra.ring._

object byte extends ByteInstances

trait ByteInstances {
implicit val catsAlgebraStdAlgebraForByte: ByteAlgebra = new ByteAlgebra

val catsAlgebraStdMinMaxLatticeForByte: BoundedDistributiveLattice[Byte] =
BoundedDistributiveLattice.minMax[Byte](Byte.MinValue, Byte.MaxValue)
}

class ByteAlgebra extends CommutativeRing[Byte] with Serializable {

def zero: Byte = 0
def one: Byte = 1

def plus(x: Byte, y: Byte): Byte = (x + y).toByte
def negate(x: Byte): Byte = (-x).toByte
override def minus(x: Byte, y: Byte): Byte = (x - y).toByte

def times(x: Byte, y: Byte): Byte = (x * y).toByte

override def pow(x: Byte, y: Int): Byte =
Math.pow(x.toDouble, y.toDouble).toByte

override def fromInt(n: Int): Byte = n.toByte
override def fromBigInt(n: BigInt): Byte = n.toByte
}
46 changes: 46 additions & 0 deletions algebra-core/src/main/scala/cats/algebra/instances/double.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package cats
package algebra
package instances

import cats.algebra.lattice.DistributiveLattice
import cats.algebra.ring.Field

import java.lang.Math

object double extends DoubleInstances

trait DoubleInstances {
implicit val catsAlgebraStdAlgebraForDouble: Field[Double] =
new DoubleAlgebra

// This is not Bounded due to the presence of NaN
val catsAlgebraStdMinMaxLatticeForDouble: DistributiveLattice[Double] =
DistributiveLattice.minMax[Double]
}

/**
* Due to the way floating-point equality works, this instance is not
* lawful under equality, but is correct when taken as an
* approximation of an exact value.
*
* If you would prefer an absolutely lawful fractional value, you'll
* need to investigate rational numbers or more exotic types.
*/
class DoubleAlgebra extends Field[Double] with Serializable {

def zero: Double = 0.0
def one: Double = 1.0

def plus(x: Double, y: Double): Double = x + y
def negate(x: Double): Double = -x
override def minus(x: Double, y: Double): Double = x - y

def times(x: Double, y: Double): Double = x * y
def div(x: Double, y: Double): Double = x / y
override def reciprocal(x: Double): Double = 1.0 / x
override def pow(x: Double, y: Int): Double = Math.pow(x, y.toDouble)

override def fromInt(x: Int): Double = x.toDouble
override def fromBigInt(n: BigInt): Double = n.toDouble
override def fromDouble(x: Double): Double = x
}
Loading