From f4aad5edbec71ebb95900b31bea6c1efeeefbe26 Mon Sep 17 00:00:00 2001 From: Oscar Boykin Date: Sat, 14 May 2016 14:42:17 -1000 Subject: [PATCH 01/12] First work towards using standard algebra types --- .../scala/com/twitter/algebird/Group.scala | 25 ++++++++++++++++++- .../scala/com/twitter/algebird/Monoid.scala | 17 +++++++++++-- .../com/twitter/algebird/Semigroup.scala | 21 ++++++++++++++-- build.sbt | 5 +++- 4 files changed, 62 insertions(+), 6 deletions(-) diff --git a/algebird-core/src/main/scala/com/twitter/algebird/Group.scala b/algebird-core/src/main/scala/com/twitter/algebird/Group.scala index 016010a60..4b08a15a7 100755 --- a/algebird-core/src/main/scala/com/twitter/algebird/Group.scala +++ b/algebird-core/src/main/scala/com/twitter/algebird/Group.scala @@ -15,6 +15,7 @@ limitations under the License. */ package com.twitter.algebird +import algebra.{ Group => AGroup } import java.lang.{ Integer => JInt, Short => JShort, Long => JLong, Float => JFloat, Double => JDouble, Boolean => JBool } import java.util.{ List => JList, Map => JMap } @@ -82,7 +83,29 @@ class ArrayGroup[T: ClassTag](implicit grp: Group[T]) }.toArray } -object Group extends GeneratedGroupImplicits with ProductGroups { +/** + * Group can't extend AGroup because Field extends Group and it already has + * a method named inverse + */ +class FromAlgebraGroup[T](m: AGroup[T]) extends FromAlgebraMonoid(m) with Group[T] { + override def negate(t: T): T = m.inverse(t) + override def minus(r: T, l: T): T = m.remove(r, l) +} + +class ToAlgebraGroup[T](g: Group[T]) extends AGroup[T] { + override def empty: T = g.zero + override def combine(l: T, r: T): T = g.plus(l, r) + override def combineAll(ts: TraversableOnce[T]): T = g.sum(ts) + override def combineAllOption(ts: TraversableOnce[T]): Option[T] = g.sumOption(ts) + override def remove(l: T, r: T): T = g.minus(l, r) + override def inverse(v: T): T = g.negate(v) +} + +trait FromAlgebraGroupImplicit { + implicit def fromAlgebraGroup[T](m: AGroup[T]): Group[T] = new FromAlgebraGroup(m) +} + +object Group extends GeneratedGroupImplicits with ProductGroups with FromAlgebraGroupImplicit { // This pattern is really useful for typeclasses def negate[T](x: T)(implicit grp: Group[T]) = grp.negate(x) def minus[T](l: T, r: T)(implicit grp: Group[T]) = grp.minus(l, r) diff --git a/algebird-core/src/main/scala/com/twitter/algebird/Monoid.scala b/algebird-core/src/main/scala/com/twitter/algebird/Monoid.scala index 1fffa0a6e..568fa806f 100755 --- a/algebird-core/src/main/scala/com/twitter/algebird/Monoid.scala +++ b/algebird-core/src/main/scala/com/twitter/algebird/Monoid.scala @@ -15,6 +15,7 @@ limitations under the License. */ package com.twitter.algebird +import algebra.{ Monoid => AMonoid } import scala.annotation.implicitNotFound import scala.math.Equiv import scala.reflect.ClassTag @@ -31,7 +32,7 @@ import scala.collection.{ Map => ScMap } */ @implicitNotFound(msg = "Cannot find Monoid type class for ${T}") -trait Monoid[@specialized(Int, Long, Float, Double) T] extends Semigroup[T] { +trait Monoid[@specialized(Int, Long, Float, Double) T] extends Semigroup[T] with AMonoid[T] { def zero: T //additive identity def isNonZero(v: T): Boolean = (v != zero) def assertNotZero(v: T) { @@ -49,6 +50,9 @@ trait Monoid[@specialized(Int, Long, Float, Double) T] extends Semigroup[T] { } // Override this if there is a more efficient means to implement this def sum(vs: TraversableOnce[T]): T = sumOption(vs).getOrElse(zero) + + final override def empty: T = zero + final override def combineAll(t: TraversableOnce[T]): T = sum(t) } // For Java interop so they get the default methods @@ -222,7 +226,16 @@ object AndValMonoid extends Monoid[AndVal] { else Some(AndVal(its.forall(_.get))) } -object Monoid extends GeneratedMonoidImplicits with ProductMonoids { +class FromAlgebraMonoid[T](m: AMonoid[T]) extends FromAlgebraSemigroup(m) with Monoid[T] { + override def sum(ts: TraversableOnce[T]): T = m.combineAll(ts) + override def zero: T = m.empty +} + +trait FromAlgebraMonoidImplicit { + implicit def fromAlgebraMonoid[T](m: AMonoid[T]): Monoid[T] = new FromAlgebraMonoid(m) +} + +object Monoid extends GeneratedMonoidImplicits with ProductMonoids with FromAlgebraMonoidImplicit { // This pattern is really useful for typeclasses def zero[T](implicit mon: Monoid[T]) = mon.zero // strictly speaking, same as Semigroup, but most interesting examples diff --git a/algebird-core/src/main/scala/com/twitter/algebird/Semigroup.scala b/algebird-core/src/main/scala/com/twitter/algebird/Semigroup.scala index b7a1f8fbf..3b207a4e7 100755 --- a/algebird-core/src/main/scala/com/twitter/algebird/Semigroup.scala +++ b/algebird-core/src/main/scala/com/twitter/algebird/Semigroup.scala @@ -15,6 +15,7 @@ limitations under the License. */ package com.twitter.algebird +import algebra.{ Semigroup => ASemigroup } import java.lang.{ Integer => JInt, Short => JShort, Long => JLong, Float => JFloat, Double => JDouble, Boolean => JBool } import java.util.{ List => JList, Map => JMap } @@ -29,13 +30,16 @@ import macros.caseclass._ * This is a class with a plus method that is associative: a+(b+c) = (a+b)+c */ @implicitNotFound(msg = "Cannot find Semigroup type class for ${T}") -trait Semigroup[@specialized(Int, Long, Float, Double) T] extends java.io.Serializable { +trait Semigroup[@specialized(Int, Long, Float, Double) T] extends ASemigroup[T] { def plus(l: T, r: T): T /** * override this if there is a faster way to do this sum than reduceLeftOption on plus */ def sumOption(iter: TraversableOnce[T]): Option[T] = iter.reduceLeftOption { plus(_, _) } + + final override def combine(l: T, r: T): T = plus(l, r) + final override def combineAllOption(iter: TraversableOnce[T]): Option[T] = sumOption(iter) } // For Java interop so they get the default sumOption @@ -70,7 +74,20 @@ class EitherSemigroup[L, R](implicit semigroupl: Semigroup[L], semigroupr: Semig } } -object Semigroup extends GeneratedSemigroupImplicits with ProductSemigroups { +class FromAlgebraSemigroup[T](sg: ASemigroup[T]) extends Semigroup[T] { + override def plus(l: T, r: T): T = sg.combine(l, r) + override def sumOption(ts: TraversableOnce[T]): Option[T] = sg.combineAllOption(ts) +} + +/** + * An Algebra semigroup can be an Algebird semigroup + */ +trait FromAlgebraSemigroupImplicit { + implicit def fromAlgebraSemigroup[T](implicit sg: ASemigroup[T]): Semigroup[T] = + new FromAlgebraSemigroup(sg) +} + +object Semigroup extends GeneratedSemigroupImplicits with ProductSemigroups with FromAlgebraSemigroupImplicit { // This pattern is really useful for typeclasses def plus[T](l: T, r: T)(implicit semi: Semigroup[T]) = semi.plus(l, r) // Left sum: (((a + b) + c) + d) diff --git a/build.sbt b/build.sbt index 2e133bad9..efcb13779 100644 --- a/build.sbt +++ b/build.sbt @@ -10,6 +10,8 @@ val paradiseVersion = "2.0.1" val quasiquotesVersion = "2.0.1" val bijectionVersion = "0.9.0" val utilVersion = "6.20.0" +val algebraVersion = "0.4.0" +val javaEwahVersion = "0.6.6" def scalaBinaryVersion(scalaVersion: String) = scalaVersion match { case version if version startsWith "2.10" => "2.10" @@ -158,7 +160,8 @@ lazy val algebirdCore = module("core").settings( import com.twitter.algebird._ """.stripMargin('|'), libraryDependencies <++= (scalaVersion) { scalaVersion => - Seq("com.googlecode.javaewah" % "JavaEWAH" % "0.6.6", + Seq("com.googlecode.javaewah" % "JavaEWAH" % javaEwahVersion, + "org.spire-math" %% "algebra" % algebraVersion, "org.scala-lang" % "scala-reflect" % scalaVersion) ++ { if (isScala210x(scalaVersion)) Seq("org.scalamacros" %% "quasiquotes" % quasiquotesVersion) From c896d0ae50da8602e4c5fbf915c7cd839c45fcdd Mon Sep 17 00:00:00 2001 From: Oscar Boykin Date: Mon, 16 May 2016 07:30:58 -1000 Subject: [PATCH 02/12] Upgrade sbt to get build to pass --- sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sbt b/sbt index 713c48653..49927dbcd 100755 --- a/sbt +++ b/sbt @@ -4,7 +4,7 @@ # Author: Paul Phillips # todo - make this dynamic -declare -r sbt_release_version="0.13.9" +declare -r sbt_release_version="0.13.11" declare -r sbt_unreleased_version="0.13.9-M1" declare -r buildProps="project/build.properties" From 31b573654b27e72cdabf34032f9d6ee66438b92a Mon Sep 17 00:00:00 2001 From: Oscar Boykin Date: Fri, 3 Jun 2016 16:16:29 -1000 Subject: [PATCH 03/12] Remove field --- .../bijection/AlgebirdBijections.scala | 13 +-- .../com/twitter/algebird/Applicative.scala | 16 +-- .../scala/com/twitter/algebird/Field.scala | 99 ------------------- .../scala/com/twitter/algebird/Group.scala | 48 ++++----- .../com/twitter/algebird/JavaMonoids.scala | 9 +- .../scala/com/twitter/algebird/Monoid.scala | 32 +++--- .../com/twitter/algebird/Operators.scala | 5 - .../scala/com/twitter/algebird/Ring.scala | 78 ++++++++++----- .../com/twitter/algebird/Semigroup.scala | 32 +++--- .../com/twitter/algebird/VectorSpace.scala | 15 +-- .../com/twitter/algebird/BaseProperties.scala | 9 -- .../algebird/ApplicativeProperties.scala | 5 - .../com/twitter/algebird/JavaBoxedTests.scala | 8 -- .../algebird/NumericSpecification.scala | 14 --- .../com/twitter/algebird/OperatorTest.scala | 3 - .../twitter/algebird/util/UtilAlgebras.scala | 2 - 16 files changed, 119 insertions(+), 269 deletions(-) delete mode 100755 algebird-core/src/main/scala/com/twitter/algebird/Field.scala diff --git a/algebird-bijection/src/main/scala/com/twitter/algebird/bijection/AlgebirdBijections.scala b/algebird-bijection/src/main/scala/com/twitter/algebird/bijection/AlgebirdBijections.scala index d3f666893..331c1a3cd 100644 --- a/algebird-bijection/src/main/scala/com/twitter/algebird/bijection/AlgebirdBijections.scala +++ b/algebird-bijection/src/main/scala/com/twitter/algebird/bijection/AlgebirdBijections.scala @@ -16,7 +16,7 @@ limitations under the License. package com.twitter.algebird.bijection -import com.twitter.algebird.{ Field, Group, Monoid, Ring, Semigroup } +import com.twitter.algebird.{ Group, Monoid, Ring, Semigroup } import com.twitter.bijection.{ AbstractBijection, Bijection, ImplicitBijection, Conversion, Reverse } import Conversion.asMethod // "as" syntax @@ -51,11 +51,6 @@ class BijectedRing[T, U](implicit val ring: Ring[T], bij: ImplicitBijection[T, U ring.product(iter map { _.as[T] }).as[U] } -class BijectedField[T, U](implicit val field: Field[T], bij: ImplicitBijection[T, U]) extends BijectedRing[T, U] with Field[U] { - override def div(l: U, r: U): U = field.div(l.as[T], r.as[T]).as[U] - override def inverse(u: U): U = field.inverse(u.as[T]).as[U] -} - trait AlgebirdBijections { implicit def semigroupBijection[T, U](implicit bij: ImplicitBijection[T, U]): Bijection[Semigroup[T], Semigroup[U]] = new AbstractBijection[Semigroup[T], Semigroup[U]] { @@ -80,12 +75,6 @@ trait AlgebirdBijections { override def apply(ring: Ring[T]) = new BijectedRing[T, U]()(ring, bij) override def invert(ring: Ring[U]) = new BijectedRing[U, T]()(ring, Reverse(bij.bijection)) } - - implicit def fieldBijection[T, U](implicit bij: ImplicitBijection[T, U]): Bijection[Field[T], Field[U]] = - new AbstractBijection[Field[T], Field[U]] { - override def apply(field: Field[T]) = new BijectedField[T, U]()(field, bij) - override def invert(field: Field[U]) = new BijectedField[U, T]()(field, Reverse(bij.bijection)) - } } object AlgebirdBijections extends AlgebirdBijections diff --git a/algebird-core/src/main/scala/com/twitter/algebird/Applicative.scala b/algebird-core/src/main/scala/com/twitter/algebird/Applicative.scala index 6332b92ca..c3b9de2d6 100644 --- a/algebird-core/src/main/scala/com/twitter/algebird/Applicative.scala +++ b/algebird-core/src/main/scala/com/twitter/algebird/Applicative.scala @@ -138,7 +138,7 @@ class ApplicativeMonoid[T, M[_]](implicit app: Applicative[M], mon: Monoid[T]) } /** - * Group, Ring, and Field ARE NOT AUTOMATIC. You have to check that the laws hold for your + * Group and Ring ARE NOT AUTOMATIC. You have to check that the laws hold for your * Applicative. If your M[_] is a wrapper type (Option[_], Some[_], Try[_], Future[_], etc...) * this generally works. */ @@ -149,7 +149,7 @@ class ApplicativeGroup[T, M[_]](implicit app: Applicative[M], grp: Group[T]) } /** - * Group, Ring, and Field ARE NOT AUTOMATIC. You have to check that the laws hold for your + * Group and Ring ARE NOT AUTOMATIC. You have to check that the laws hold for your * Applicative. If your M[_] is a wrapper type (Option[_], Some[_], Try[_], Future[_], etc...) * this generally works. */ @@ -158,15 +158,3 @@ class ApplicativeRing[T, M[_]](implicit app: Applicative[M], ring: Ring[T]) lazy val one = app(ring.one) def times(l: M[T], r: M[T]) = app.joinWith(l, r)(ring.times) } - -/** - * Group, Ring, and Field ARE NOT AUTOMATIC. You have to check that the laws hold for your - * Applicative. If your M[_] is a wrapper type (Option[_], Some[_], Try[_], Future[_], etc...) - * this generally works. - */ -class ApplicativeField[T, M[_]](implicit app: Applicative[M], fld: Field[T]) - extends ApplicativeRing[T, M] with Field[M[T]] { - override def inverse(v: M[T]) = app.map(v)(fld.inverse) - override def div(l: M[T], r: M[T]) = app.joinWith(l, r)(fld.div) -} - diff --git a/algebird-core/src/main/scala/com/twitter/algebird/Field.scala b/algebird-core/src/main/scala/com/twitter/algebird/Field.scala deleted file mode 100755 index 2402e0a5a..000000000 --- a/algebird-core/src/main/scala/com/twitter/algebird/Field.scala +++ /dev/null @@ -1,99 +0,0 @@ -/* -Copyright 2012 Twitter, Inc. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -package com.twitter.algebird - -import scala.annotation.tailrec -import scala.annotation.implicitNotFound - -import java.lang.{ Integer => JInt, Short => JShort, Long => JLong, Float => JFloat, Double => JDouble, Boolean => JBool } -import java.util.{ List => JList, Map => JMap } - -/** - * Field: Ring + division. It is a generalization of Ring and adds support for inversion and - * multiplicative identity. - */ - -@implicitNotFound(msg = "Cannot find Field type class for ${T}") -trait Field[@specialized(Int, Long, Float, Double) T] extends Ring[T] { - // default implementation uses div YOU MUST OVERRIDE ONE OF THESE - def inverse(v: T): T = { - assertNotZero(v) - div(one, v) - } - // default implementation uses inverse: - def div(l: T, r: T): T = { - assertNotZero(r) - times(l, inverse(r)) - } -} - -// For Java interop so they get the default methods -abstract class AbstractField[T] extends Field[T] - -object FloatField extends Field[Float] { - override def one = 1.0f - override def zero = 0.0f - override def negate(v: Float) = -v - override def plus(l: Float, r: Float) = l + r - override def minus(l: Float, r: Float) = l - r - override def times(l: Float, r: Float) = l * r - override def div(l: Float, r: Float) = { - assertNotZero(r) - l / r - } -} - -object DoubleField extends Field[Double] { - override def one = 1.0 - override def zero = 0.0 - override def negate(v: Double) = -v - override def plus(l: Double, r: Double) = l + r - override def minus(l: Double, r: Double) = l - r - override def times(l: Double, r: Double) = l * r - override def div(l: Double, r: Double) = { - assertNotZero(r) - l / r - } -} - -object BooleanField extends Field[Boolean] { - override def one = true - override def zero = false - override def negate(v: Boolean) = v - override def plus(l: Boolean, r: Boolean) = l ^ r - override def minus(l: Boolean, r: Boolean) = l ^ r - override def times(l: Boolean, r: Boolean) = l && r - override def inverse(l: Boolean) = { - assertNotZero(l) - true - } - override def div(l: Boolean, r: Boolean) = { - assertNotZero(r) - l - } -} - -object Field { - // This pattern is really useful for typeclasses - def div[T](l: T, r: T)(implicit fld: Field[T]) = fld.div(l, r) - - implicit val boolField: Field[Boolean] = BooleanField - implicit val jboolField: Field[JBool] = JBoolField - implicit val floatField: Field[Float] = FloatField - implicit val jfloatField: Field[JFloat] = JFloatField - implicit val doubleField: Field[Double] = DoubleField - implicit val jdoubleField: Field[JDouble] = JDoubleField -} diff --git a/algebird-core/src/main/scala/com/twitter/algebird/Group.scala b/algebird-core/src/main/scala/com/twitter/algebird/Group.scala index 4b08a15a7..cb44405f3 100755 --- a/algebird-core/src/main/scala/com/twitter/algebird/Group.scala +++ b/algebird-core/src/main/scala/com/twitter/algebird/Group.scala @@ -29,10 +29,13 @@ import scala.math.Equiv */ @implicitNotFound(msg = "Cannot find Group type class for ${T}") -trait Group[@specialized(Int, Long, Float, Double) T] extends Monoid[T] { +trait Group[@specialized(Int, Long, Float, Double) T] extends AGroup[T] with Monoid[T] { // must override negate or minus (or both) def negate(v: T): T = minus(zero, v) def minus(l: T, r: T): T = plus(l, negate(r)) + + override def remove(l: T, r: T): T = minus(l, r) + override def inverse(v: T): T = negate(v) } // For Java interop so they get the default methods @@ -83,24 +86,11 @@ class ArrayGroup[T: ClassTag](implicit grp: Group[T]) }.toArray } -/** - * Group can't extend AGroup because Field extends Group and it already has - * a method named inverse - */ class FromAlgebraGroup[T](m: AGroup[T]) extends FromAlgebraMonoid(m) with Group[T] { override def negate(t: T): T = m.inverse(t) override def minus(r: T, l: T): T = m.remove(r, l) } -class ToAlgebraGroup[T](g: Group[T]) extends AGroup[T] { - override def empty: T = g.zero - override def combine(l: T, r: T): T = g.plus(l, r) - override def combineAll(ts: TraversableOnce[T]): T = g.sum(ts) - override def combineAllOption(ts: TraversableOnce[T]): Option[T] = g.sumOption(ts) - override def remove(l: T, r: T): T = g.minus(l, r) - override def inverse(v: T): T = g.negate(v) -} - trait FromAlgebraGroupImplicit { implicit def fromAlgebraGroup[T](m: AGroup[T]): Group[T] = new FromAlgebraGroup(m) } @@ -121,21 +111,21 @@ object Group extends GeneratedGroupImplicits with ProductGroups with FromAlgebra Monoid.intTimes(i, v)(grp) } - implicit val nullGroup: Group[Null] = NullGroup - implicit val unitGroup: Group[Unit] = UnitGroup - implicit val boolGroup: Group[Boolean] = BooleanField - implicit val jboolGroup: Group[JBool] = JBoolField - implicit val intGroup: Group[Int] = IntRing - implicit val jintGroup: Group[JInt] = JIntRing - implicit val shortGroup: Group[Short] = ShortRing - implicit val jshortGroup: Group[JShort] = JShortRing - implicit val longGroup: Group[Long] = LongRing - implicit val bigIntGroup: Group[BigInt] = BigIntRing - implicit val jlongGroup: Group[JLong] = JLongRing - implicit val floatGroup: Group[Float] = FloatField - implicit val jfloatGroup: Group[JFloat] = JFloatField - implicit val doubleGroup: Group[Double] = DoubleField - implicit val jdoubleGroup: Group[JDouble] = JDoubleField + implicit def nullGroup: Group[Null] = NullGroup + implicit def unitGroup: Group[Unit] = UnitGroup + implicit def boolGroup: Group[Boolean] = BooleanRing + implicit def jboolGroup: Group[JBool] = JBoolRing + implicit def intGroup: Group[Int] = IntRing + implicit def jintGroup: Group[JInt] = JIntRing + implicit def shortGroup: Group[Short] = ShortRing + implicit def jshortGroup: Group[JShort] = JShortRing + implicit def longGroup: Group[Long] = LongRing + implicit def bigIntGroup: Group[BigInt] = BigIntRing + implicit def jlongGroup: Group[JLong] = JLongRing + implicit def floatGroup: Group[Float] = FloatRing + implicit def jfloatGroup: Group[JFloat] = JFloatRing + implicit def doubleGroup: Group[Double] = DoubleRing + implicit def jdoubleGroup: Group[JDouble] = JDoubleRing implicit def optionGroup[T: Group] = new OptionGroup[T] implicit def indexedSeqGroup[T: Group]: Group[IndexedSeq[T]] = new IndexedSeqGroup[T] implicit def mapGroup[K, V](implicit group: Group[V]) = new MapGroup[K, V]()(group) diff --git a/algebird-core/src/main/scala/com/twitter/algebird/JavaMonoids.scala b/algebird-core/src/main/scala/com/twitter/algebird/JavaMonoids.scala index 280d40b53..a054039b2 100644 --- a/algebird-core/src/main/scala/com/twitter/algebird/JavaMonoids.scala +++ b/algebird-core/src/main/scala/com/twitter/algebird/JavaMonoids.scala @@ -47,34 +47,31 @@ object JLongRing extends Ring[JLong] { override def times(x: JLong, y: JLong) = x * y } -object JFloatField extends Field[JFloat] { +object JFloatRing extends Ring[JFloat] { override val zero = JFloat.valueOf(0.0f) override val one = JFloat.valueOf(1.0f) override def plus(x: JFloat, y: JFloat) = x + y override def negate(x: JFloat): JFloat = -x override def minus(x: JFloat, y: JFloat) = x - y override def times(x: JFloat, y: JFloat) = x * y - override def div(x: JFloat, y: JFloat) = { assertNotZero(y); x / y } } -object JDoubleField extends Field[JDouble] { +object JDoubleRing extends Ring[JDouble] { override val zero = JDouble.valueOf(0.0) override val one = JDouble.valueOf(1.0) override def plus(x: JDouble, y: JDouble) = x + y override def negate(x: JDouble): JDouble = -x override def minus(x: JDouble, y: JDouble) = x - y override def times(x: JDouble, y: JDouble) = x * y - override def div(x: JDouble, y: JDouble) = { assertNotZero(y); x / y } } -object JBoolField extends Field[JBool] { +object JBoolRing extends Ring[JBool] { override val zero = JBool.FALSE override val one = JBool.TRUE override def plus(x: JBool, y: JBool) = JBool.valueOf(x.booleanValue ^ y.booleanValue) override def negate(x: JBool) = x override def minus(x: JBool, y: JBool) = plus(x, y) override def times(x: JBool, y: JBool) = JBool.valueOf(x.booleanValue & y.booleanValue) - override def div(x: JBool, y: JBool) = { assertNotZero(y); x } } /** diff --git a/algebird-core/src/main/scala/com/twitter/algebird/Monoid.scala b/algebird-core/src/main/scala/com/twitter/algebird/Monoid.scala index 568fa806f..d582856fd 100755 --- a/algebird-core/src/main/scala/com/twitter/algebird/Monoid.scala +++ b/algebird-core/src/main/scala/com/twitter/algebird/Monoid.scala @@ -274,22 +274,22 @@ object Monoid extends GeneratedMonoidImplicits with ProductMonoids with FromAlge } } - implicit val nullMonoid: Monoid[Null] = NullGroup - implicit val unitMonoid: Monoid[Unit] = UnitGroup - implicit val boolMonoid: Monoid[Boolean] = BooleanField - implicit val jboolMonoid: Monoid[JBool] = JBoolField - implicit val intMonoid: Monoid[Int] = IntRing - implicit val jintMonoid: Monoid[JInt] = JIntRing - implicit val shortMonoid: Monoid[Short] = ShortRing - implicit val jshortMonoid: Monoid[JShort] = JShortRing - implicit val bigIntMonoid: Monoid[BigInt] = BigIntRing - implicit val longMonoid: Monoid[Long] = LongRing - implicit val jlongMonoid: Monoid[JLong] = JLongRing - implicit val floatMonoid: Monoid[Float] = FloatField - implicit val jfloatMonoid: Monoid[JFloat] = JFloatField - implicit val doubleMonoid: Monoid[Double] = DoubleField - implicit val jdoubleMonoid: Monoid[JDouble] = JDoubleField - implicit val stringMonoid: Monoid[String] = StringMonoid + implicit def nullMonoid: Monoid[Null] = NullGroup + implicit def unitMonoid: Monoid[Unit] = UnitGroup + implicit def boolMonoid: Monoid[Boolean] = BooleanRing + implicit def jboolMonoid: Monoid[JBool] = JBoolRing + implicit def intMonoid: Monoid[Int] = IntRing + implicit def jintMonoid: Monoid[JInt] = JIntRing + implicit def shortMonoid: Monoid[Short] = ShortRing + implicit def jshortMonoid: Monoid[JShort] = JShortRing + implicit def bigIntMonoid: Monoid[BigInt] = BigIntRing + implicit def longMonoid: Monoid[Long] = LongRing + implicit def jlongMonoid: Monoid[JLong] = JLongRing + implicit def floatMonoid: Monoid[Float] = FloatRing + implicit def jfloatMonoid: Monoid[JFloat] = JFloatRing + implicit def doubleMonoid: Monoid[Double] = DoubleRing + implicit def jdoubleMonoid: Monoid[JDouble] = JDoubleRing + implicit def stringMonoid: Monoid[String] = StringMonoid implicit def optionMonoid[T: Semigroup]: Monoid[Option[T]] = new OptionMonoid[T] implicit def listMonoid[T]: Monoid[List[T]] = new ListMonoid[T] implicit def seqMonoid[T]: Monoid[Seq[T]] = new SeqMonoid[T] diff --git a/algebird-core/src/main/scala/com/twitter/algebird/Operators.scala b/algebird-core/src/main/scala/com/twitter/algebird/Operators.scala index 44681ad32..48fa12ade 100644 --- a/algebird-core/src/main/scala/com/twitter/algebird/Operators.scala +++ b/algebird-core/src/main/scala/com/twitter/algebird/Operators.scala @@ -19,7 +19,6 @@ object Operators { implicit def toPlus[T: Semigroup](t: T) = new PlusOp(t) implicit def toMinus[T: Group](t: T) = new MinusOp(t) implicit def toTimes[T: Ring](t: T) = new TimesOp(t) - implicit def toDiv[T: Field](t: T) = new DivOp(t) implicit def toRichTraversable[T](t: TraversableOnce[T]) = new RichTraversable(t) } @@ -35,10 +34,6 @@ class TimesOp[T: Ring](t: T) { def *(other: T) = implicitly[Ring[T]].times(t, other) } -class DivOp[T: Field](t: T) { - def /(other: T) = implicitly[Field[T]].div(t, other) -} - class RichTraversable[T](t: TraversableOnce[T]) { def sumByKey[K, V](implicit ev: <:<[T, (K, V)], sg: Semigroup[V]): Map[K, V] = MapAlgebra.sumByKey(t.map { _.asInstanceOf[(K, V)] }) diff --git a/algebird-core/src/main/scala/com/twitter/algebird/Ring.scala b/algebird-core/src/main/scala/com/twitter/algebird/Ring.scala index 9d8c51a09..023fd9ecb 100755 --- a/algebird-core/src/main/scala/com/twitter/algebird/Ring.scala +++ b/algebird-core/src/main/scala/com/twitter/algebird/Ring.scala @@ -43,10 +43,11 @@ import scala.annotation.implicitNotFound @implicitNotFound(msg = "Cannot find Ring type class for ${T}") trait Ring[@specialized(Int, Long, Float, Double) T] extends Group[T] { - def one: T // Multiplicative identity - def times(l: T, r: T): T - // Left product: (((a * b) * c) * d) - def product(iter: TraversableOnce[T]): T = Ring.product(iter)(this) + def one: T + def times(a: T, b: T): T + def product(iter: TraversableOnce[T]): T = + if (iter.isEmpty) one // avoid hitting one as some have abused Ring for Rng + else iter.reduce(times) } // For Java interop so they get the default methods @@ -88,8 +89,39 @@ object LongRing extends Ring[Long] { override def times(l: Long, r: Long) = l * r } +object FloatRing extends Ring[Float] { + override def one = 1.0f + override def zero = 0.0f + override def negate(v: Float) = -v + override def plus(l: Float, r: Float) = l + r + override def minus(l: Float, r: Float) = l - r + override def times(l: Float, r: Float) = l * r +} + +object DoubleRing extends Ring[Double] { + override def one = 1.0 + override def zero = 0.0 + override def negate(v: Double) = -v + override def plus(l: Double, r: Double) = l + r + override def minus(l: Double, r: Double) = l - r + override def times(l: Double, r: Double) = l * r +} + +object BooleanRing extends Ring[Boolean] { + override def one = true + override def zero = false + override def negate(v: Boolean) = v + override def plus(l: Boolean, r: Boolean) = l ^ r + override def minus(l: Boolean, r: Boolean) = l ^ r + override def times(l: Boolean, r: Boolean) = l && r +} + object BigIntRing extends NumericRing[BigInt] +trait NumericRingProvider { + implicit def numericRing[T: Numeric]: Ring[T] = new NumericRing[T] +} + object Ring extends GeneratedRingImplicits with ProductRings { // This pattern is really useful for typeclasses def one[T](implicit rng: Ring[T]) = rng.one @@ -97,29 +129,27 @@ object Ring extends GeneratedRingImplicits with ProductRings { def asTimesMonoid[T](implicit ring: Ring[T]): Monoid[T] = Monoid.from[T](ring.one)(ring.times _) // Left product: (((a * b) * c) * d) - def product[T](iter: TraversableOnce[T])(implicit ring: Ring[T]) = { - // avoid touching one unless we need to (some items are pseudo-rings) - if (iter.isEmpty) ring.one - else iter.reduceLeft(ring.times _) - } + def product[T](iter: TraversableOnce[T])(implicit ring: Ring[T]) = + ring.product(iter) + // If the ring doesn't have a one, or you want to distinguish empty cases: def productOption[T](it: TraversableOnce[T])(implicit rng: Ring[T]): Option[T] = - it.reduceLeftOption(rng.times _) + if (it.isEmpty) None + else Some(rng.product(it)) - implicit def numericRing[T: Numeric]: Ring[T] = new NumericRing[T] - implicit val boolRing: Ring[Boolean] = BooleanField - implicit val jboolRing: Ring[JBool] = JBoolField - implicit val intRing: Ring[Int] = IntRing - implicit val jintRing: Ring[JInt] = JIntRing - implicit val shortRing: Ring[Short] = ShortRing - implicit val jshortRing: Ring[JShort] = JShortRing - implicit val longRing: Ring[Long] = LongRing - implicit val bigIntRing: Ring[BigInt] = BigIntRing - implicit val jlongRing: Ring[JLong] = JLongRing - implicit val floatRing: Ring[Float] = FloatField - implicit val jfloatRing: Ring[JFloat] = JFloatField - implicit val doubleRing: Ring[Double] = DoubleField - implicit val jdoubleRing: Ring[JDouble] = JDoubleField + implicit def boolRing: Ring[Boolean] = BooleanRing + implicit def jboolRing: Ring[JBool] = JBoolRing + implicit def intRing: Ring[Int] = IntRing + implicit def jintRing: Ring[JInt] = JIntRing + implicit def shortRing: Ring[Short] = ShortRing + implicit def jshortRing: Ring[JShort] = JShortRing + implicit def longRing: Ring[Long] = LongRing + implicit def bigIntRing: Ring[BigInt] = BigIntRing + implicit def jlongRing: Ring[JLong] = JLongRing + implicit def floatRing: Ring[Float] = FloatRing + implicit def jfloatRing: Ring[JFloat] = JFloatRing + implicit def doubleRing: Ring[Double] = DoubleRing + implicit def jdoubleRing: Ring[JDouble] = JDoubleRing implicit def indexedSeqRing[T: Ring]: Ring[IndexedSeq[T]] = new IndexedSeqRing[T] implicit def mapRing[K, V](implicit ring: Ring[V]) = new MapRing[K, V]()(ring) implicit def scMapRing[K, V](implicit ring: Ring[V]) = new ScMapRing[K, V]()(ring) diff --git a/algebird-core/src/main/scala/com/twitter/algebird/Semigroup.scala b/algebird-core/src/main/scala/com/twitter/algebird/Semigroup.scala index 3b207a4e7..7de587347 100755 --- a/algebird-core/src/main/scala/com/twitter/algebird/Semigroup.scala +++ b/algebird-core/src/main/scala/com/twitter/algebird/Semigroup.scala @@ -145,22 +145,22 @@ object Semigroup extends GeneratedSemigroupImplicits with ProductSemigroups with } } - implicit val nullSemigroup: Semigroup[Null] = NullGroup - implicit val unitSemigroup: Semigroup[Unit] = UnitGroup - implicit val boolSemigroup: Semigroup[Boolean] = BooleanField - implicit val jboolSemigroup: Semigroup[JBool] = JBoolField - implicit val intSemigroup: Semigroup[Int] = IntRing - implicit val jintSemigroup: Semigroup[JInt] = JIntRing - implicit val shortSemigroup: Semigroup[Short] = ShortRing - implicit val jshortSemigroup: Semigroup[JShort] = JShortRing - implicit val longSemigroup: Semigroup[Long] = LongRing - implicit val bigIntSemigroup: Semigroup[BigInt] = BigIntRing - implicit val jlongSemigroup: Semigroup[JLong] = JLongRing - implicit val floatSemigroup: Semigroup[Float] = FloatField - implicit val jfloatSemigroup: Semigroup[JFloat] = JFloatField - implicit val doubleSemigroup: Semigroup[Double] = DoubleField - implicit val jdoubleSemigroup: Semigroup[JDouble] = JDoubleField - implicit val stringSemigroup: Semigroup[String] = StringMonoid + implicit def nullSemigroup: Semigroup[Null] = NullGroup + implicit def unitSemigroup: Semigroup[Unit] = UnitGroup + implicit def boolSemigroup: Semigroup[Boolean] = BooleanRing + implicit def jboolSemigroup: Semigroup[JBool] = JBoolRing + implicit def intSemigroup: Semigroup[Int] = IntRing + implicit def jintSemigroup: Semigroup[JInt] = JIntRing + implicit def shortSemigroup: Semigroup[Short] = ShortRing + implicit def jshortSemigroup: Semigroup[JShort] = JShortRing + implicit def longSemigroup: Semigroup[Long] = LongRing + implicit def bigIntSemigroup: Semigroup[BigInt] = BigIntRing + implicit def jlongSemigroup: Semigroup[JLong] = JLongRing + implicit def floatSemigroup: Semigroup[Float] = FloatRing + implicit def jfloatSemigroup: Semigroup[JFloat] = JFloatRing + implicit def doubleSemigroup: Semigroup[Double] = DoubleRing + implicit def jdoubleSemigroup: Semigroup[JDouble] = JDoubleRing + implicit def stringSemigroup: Semigroup[String] = StringMonoid implicit def optionSemigroup[T: Semigroup]: Semigroup[Option[T]] = new OptionMonoid[T] implicit def listSemigroup[T]: Semigroup[List[T]] = new ListMonoid[T] implicit def seqSemigroup[T]: Semigroup[Seq[T]] = new SeqMonoid[T] diff --git a/algebird-core/src/main/scala/com/twitter/algebird/VectorSpace.scala b/algebird-core/src/main/scala/com/twitter/algebird/VectorSpace.scala index 94deae3c1..f997facff 100644 --- a/algebird-core/src/main/scala/com/twitter/algebird/VectorSpace.scala +++ b/algebird-core/src/main/scala/com/twitter/algebird/VectorSpace.scala @@ -27,25 +27,26 @@ import scala.annotation.implicitNotFound */ object VectorSpace { def scale[F, C[_]](v: F, c: C[F])(implicit vs: VectorSpace[F, C]): C[F] = vs.scale(v, c) - def from[F, C[_]](scaleFn: (F, C[F]) => C[F])(implicit fField: Field[F], cGroup: Group[C[F]]) = new VectorSpace[F, C] { - def field = fField + def from[F, C[_]](scaleFn: (F, C[F]) => C[F])(implicit r: Ring[F], cGroup: Group[C[F]]) = new VectorSpace[F, C] { + def ring = r def group = cGroup - def scale(v: F, c: C[F]) = if (field.isNonZero(v)) scaleFn(v, c) else cGroup.zero + def scale(v: F, c: C[F]) = if (r.isNonZero(v)) scaleFn(v, c) else cGroup.zero } // Implicits - implicit def indexedSeqSpace[T: Field] = + implicit def indexedSeqSpace[T: Ring] = from[T, IndexedSeq]{ (s, seq) => seq.map(Ring.times(s, _)) } - implicit def mapSpace[K, T: Field] = + implicit def mapSpace[K, T: Ring] = from[T, ({ type x[a] = Map[K, a] })#x] { (s, m) => m.mapValues(Ring.times(s, _)) } // TODO: add implicits for java lists, arrays, and options } -@implicitNotFound(msg = "Cannot find VectorSpace type class for Container: ${C} and Field: ${F}") +@implicitNotFound(msg = "Cannot find VectorSpace type class for Container: ${C} and Ring: ${F}") trait VectorSpace[F, C[_]] extends java.io.Serializable { - implicit def field: Field[F] + implicit def ring: Ring[F] + def field: Ring[F] = ring // this is for compatibility with older versions implicit def group: Group[C[F]] def scale(v: F, c: C[F]): C[F] } diff --git a/algebird-test/src/main/scala/com/twitter/algebird/BaseProperties.scala b/algebird-test/src/main/scala/com/twitter/algebird/BaseProperties.scala index 1436e55de..d38abc8cd 100644 --- a/algebird-test/src/main/scala/com/twitter/algebird/BaseProperties.scala +++ b/algebird-test/src/main/scala/com/twitter/algebird/BaseProperties.scala @@ -164,13 +164,4 @@ object BaseProperties { isNonZeroWorksRing[T] def ringLaws[T: Ring: Arbitrary] = validOne[T] && pseudoRingLaws[T] - - def hasMultiplicativeInverse[T: Field: Arbitrary] = 'hasMultiplicativeInverse |: forAll { (a: T) => - val fld = implicitly[Field[T]] - (!fld.isNonZero(a)) || { - val inva = fld.inverse(a) - (fld.times(inva, a) == fld.one) && (fld.times(a, inva) == fld.one) - } - } - def fieldLaws[T: Field: Arbitrary] = ringLaws[T] && hasMultiplicativeInverse[T] } diff --git a/algebird-test/src/test/scala/com/twitter/algebird/ApplicativeProperties.scala b/algebird-test/src/test/scala/com/twitter/algebird/ApplicativeProperties.scala index 0704913ec..88caa6bc9 100644 --- a/algebird-test/src/test/scala/com/twitter/algebird/ApplicativeProperties.scala +++ b/algebird-test/src/test/scala/com/twitter/algebird/ApplicativeProperties.scala @@ -88,9 +88,4 @@ class ApplicativeProperties extends CheckProperties { implicit val optSg = new ApplicativeRing[Int, Some] ringLaws[Some[Int]] } - - property("Applicative Field") { - implicit val optSg = new ApplicativeField[Boolean, Some] - fieldLaws[Some[Boolean]] - } } diff --git a/algebird-test/src/test/scala/com/twitter/algebird/JavaBoxedTests.scala b/algebird-test/src/test/scala/com/twitter/algebird/JavaBoxedTests.scala index e53182ff0..1bcd74793 100644 --- a/algebird-test/src/test/scala/com/twitter/algebird/JavaBoxedTests.scala +++ b/algebird-test/src/test/scala/com/twitter/algebird/JavaBoxedTests.scala @@ -25,14 +25,6 @@ class JavaBoxedTests extends CheckProperties { yield JLong.valueOf(v) } - property("Boolean is a Field") { - fieldLaws[Boolean] - } - - property("JBoolean is a Field") { - fieldLaws[JBool] - } - property("Int is a Ring") { ringLaws[Int] } diff --git a/algebird-test/src/test/scala/com/twitter/algebird/NumericSpecification.scala b/algebird-test/src/test/scala/com/twitter/algebird/NumericSpecification.scala index 022545dfe..5dd90dda4 100644 --- a/algebird-test/src/test/scala/com/twitter/algebird/NumericSpecification.scala +++ b/algebird-test/src/test/scala/com/twitter/algebird/NumericSpecification.scala @@ -131,18 +131,4 @@ class NumericSpecification extends PropSpec with PropertyChecks with Matchers { property("Float times") { timesNumericProp[Float] } - - property("Double div") { - forAll { (a: Double, b: Double) => - val fld = implicitly[Field[Double]] - assert((!fld.isNonZero(b)) || ((a / b) == fld.div(a, b))) - } - } - - property("Float div") { - forAll { (a: Float, b: Float) => - val fld = implicitly[Field[Float]] - assert((!fld.isNonZero(b)) || ((a / b) == fld.div(a, b))) - } - } } diff --git a/algebird-test/src/test/scala/com/twitter/algebird/OperatorTest.scala b/algebird-test/src/test/scala/com/twitter/algebird/OperatorTest.scala index aac649ec1..fb75a6827 100644 --- a/algebird-test/src/test/scala/com/twitter/algebird/OperatorTest.scala +++ b/algebird-test/src/test/scala/com/twitter/algebird/OperatorTest.scala @@ -17,8 +17,5 @@ class OperatorTest extends WordSpec with Matchers { assert(Map(1 -> 3) * Map(2 -> 4) == Map[Int, Int]()) assert(Map(1 -> 3) * Map(1 -> 4) == Map(1 -> 12)) } - "allow /" in { - assert(true / true == true) - } } } diff --git a/algebird-util/src/main/scala/com/twitter/algebird/util/UtilAlgebras.scala b/algebird-util/src/main/scala/com/twitter/algebird/util/UtilAlgebras.scala index 6293d6804..f597d6c86 100644 --- a/algebird-util/src/main/scala/com/twitter/algebird/util/UtilAlgebras.scala +++ b/algebird-util/src/main/scala/com/twitter/algebird/util/UtilAlgebras.scala @@ -52,11 +52,9 @@ object UtilAlgebras { implicit def futureMonoid[T: Monoid]: Monoid[Future[T]] = new ApplicativeMonoid[T, Future] implicit def futureGroup[T: Group]: Group[Future[T]] = new ApplicativeGroup[T, Future] implicit def futureRing[T: Ring]: Ring[Future[T]] = new ApplicativeRing[T, Future] - implicit def futureField[T: Field]: Field[Future[T]] = new ApplicativeField[T, Future] implicit def trySemigroup[T: Semigroup]: Semigroup[Try[T]] = new ApplicativeSemigroup[T, Try] implicit def tryMonoid[T: Monoid]: Monoid[Try[T]] = new ApplicativeMonoid[T, Try] implicit def tryGroup[T: Group]: Group[Try[T]] = new ApplicativeGroup[T, Try] implicit def tryRing[T: Ring]: Ring[Try[T]] = new ApplicativeRing[T, Try] - implicit def tryField[T: Field]: Field[Try[T]] = new ApplicativeField[T, Try] } From 053fd81dafbd88982cad3b9b5c8839ab16ceb399 Mon Sep 17 00:00:00 2001 From: Oscar Boykin Date: Mon, 6 Jun 2016 11:12:52 -1000 Subject: [PATCH 04/12] use additive to get good algebra compatibility --- .../main/scala/com/twitter/algebird/Group.scala | 11 ++++++----- .../main/scala/com/twitter/algebird/Monoid.scala | 15 +++++++++------ .../main/scala/com/twitter/algebird/Ring.scala | 5 +++-- .../scala/com/twitter/algebird/Semigroup.scala | 14 ++++++++++---- build.sbt | 1 + 5 files changed, 29 insertions(+), 17 deletions(-) diff --git a/algebird-core/src/main/scala/com/twitter/algebird/Group.scala b/algebird-core/src/main/scala/com/twitter/algebird/Group.scala index cb44405f3..0b8e03320 100755 --- a/algebird-core/src/main/scala/com/twitter/algebird/Group.scala +++ b/algebird-core/src/main/scala/com/twitter/algebird/Group.scala @@ -16,6 +16,7 @@ limitations under the License. package com.twitter.algebird import algebra.{ Group => AGroup } +import algebra.ring.AdditiveGroup import java.lang.{ Integer => JInt, Short => JShort, Long => JLong, Float => JFloat, Double => JDouble, Boolean => JBool } import java.util.{ List => JList, Map => JMap } @@ -29,11 +30,11 @@ import scala.math.Equiv */ @implicitNotFound(msg = "Cannot find Group type class for ${T}") -trait Group[@specialized(Int, Long, Float, Double) T] extends AGroup[T] with Monoid[T] { - // must override negate or minus (or both) - def negate(v: T): T = minus(zero, v) - def minus(l: T, r: T): T = plus(l, negate(r)) - +trait Group[@specialized(Int, Long, Float, Double) T] extends AGroup[T] with Monoid[T] with AdditiveGroup[T] { + /* + * This are from algebra.Group + */ + override def additive: AGroup[T] = this override def remove(l: T, r: T): T = minus(l, r) override def inverse(v: T): T = negate(v) } diff --git a/algebird-core/src/main/scala/com/twitter/algebird/Monoid.scala b/algebird-core/src/main/scala/com/twitter/algebird/Monoid.scala index d582856fd..756a94b9d 100755 --- a/algebird-core/src/main/scala/com/twitter/algebird/Monoid.scala +++ b/algebird-core/src/main/scala/com/twitter/algebird/Monoid.scala @@ -16,6 +16,7 @@ limitations under the License. package com.twitter.algebird import algebra.{ Monoid => AMonoid } +import algebra.ring.{ AdditiveMonoid } import scala.annotation.implicitNotFound import scala.math.Equiv import scala.reflect.ClassTag @@ -32,8 +33,7 @@ import scala.collection.{ Map => ScMap } */ @implicitNotFound(msg = "Cannot find Monoid type class for ${T}") -trait Monoid[@specialized(Int, Long, Float, Double) T] extends Semigroup[T] with AMonoid[T] { - def zero: T //additive identity +trait Monoid[@specialized(Int, Long, Float, Double) T] extends Semigroup[T] with AMonoid[T] with AdditiveMonoid[T] { def isNonZero(v: T): Boolean = (v != zero) def assertNotZero(v: T) { if (!isNonZero(v)) { @@ -48,11 +48,14 @@ trait Monoid[@specialized(Int, Long, Float, Double) T] extends Semigroup[T] with None } } - // Override this if there is a more efficient means to implement this - def sum(vs: TraversableOnce[T]): T = sumOption(vs).getOrElse(zero) + override def sum(vs: TraversableOnce[T]): T = sumOption(vs).getOrElse(zero) - final override def empty: T = zero - final override def combineAll(t: TraversableOnce[T]): T = sum(t) + /** + * These are from algebra.Monoid + */ + override def additive: AMonoid[T] = this + override def empty: T = zero + override def combineAll(t: TraversableOnce[T]): T = sum(t) } // For Java interop so they get the default methods diff --git a/algebird-core/src/main/scala/com/twitter/algebird/Ring.scala b/algebird-core/src/main/scala/com/twitter/algebird/Ring.scala index 023fd9ecb..82caec8bb 100755 --- a/algebird-core/src/main/scala/com/twitter/algebird/Ring.scala +++ b/algebird-core/src/main/scala/com/twitter/algebird/Ring.scala @@ -16,6 +16,7 @@ limitations under the License. package com.twitter.algebird import java.lang.{ Integer => JInt, Short => JShort, Long => JLong, Float => JFloat, Double => JDouble, Boolean => JBool } +import algebra.ring.{ Ring => ARing } import scala.annotation.implicitNotFound /** @@ -42,10 +43,10 @@ import scala.annotation.implicitNotFound */ @implicitNotFound(msg = "Cannot find Ring type class for ${T}") -trait Ring[@specialized(Int, Long, Float, Double) T] extends Group[T] { +trait Ring[@specialized(Int, Long, Float, Double) T] extends Group[T] with ARing[T] { def one: T def times(a: T, b: T): T - def product(iter: TraversableOnce[T]): T = + override def product(iter: TraversableOnce[T]): T = if (iter.isEmpty) one // avoid hitting one as some have abused Ring for Rng else iter.reduce(times) } diff --git a/algebird-core/src/main/scala/com/twitter/algebird/Semigroup.scala b/algebird-core/src/main/scala/com/twitter/algebird/Semigroup.scala index 7de587347..af6f8da6f 100755 --- a/algebird-core/src/main/scala/com/twitter/algebird/Semigroup.scala +++ b/algebird-core/src/main/scala/com/twitter/algebird/Semigroup.scala @@ -16,6 +16,7 @@ limitations under the License. package com.twitter.algebird import algebra.{ Semigroup => ASemigroup } +import algebra.ring.{ AdditiveSemigroup } import java.lang.{ Integer => JInt, Short => JShort, Long => JLong, Float => JFloat, Double => JDouble, Boolean => JBool } import java.util.{ List => JList, Map => JMap } @@ -30,16 +31,21 @@ import macros.caseclass._ * This is a class with a plus method that is associative: a+(b+c) = (a+b)+c */ @implicitNotFound(msg = "Cannot find Semigroup type class for ${T}") -trait Semigroup[@specialized(Int, Long, Float, Double) T] extends ASemigroup[T] { - def plus(l: T, r: T): T +trait Semigroup[@specialized(Int, Long, Float, Double) T] extends ASemigroup[T] with AdditiveSemigroup[T] { /** * override this if there is a faster way to do this sum than reduceLeftOption on plus */ def sumOption(iter: TraversableOnce[T]): Option[T] = iter.reduceLeftOption { plus(_, _) } - final override def combine(l: T, r: T): T = plus(l, r) - final override def combineAllOption(iter: TraversableOnce[T]): Option[T] = sumOption(iter) + /* + * These are methods from algebra + */ + override def trySum(iter: TraversableOnce[T]): Option[T] = sumOption(iter) + + override def additive: ASemigroup[T] = this + override def combine(l: T, r: T): T = plus(l, r) + override def combineAllOption(iter: TraversableOnce[T]): Option[T] = sumOption(iter) } // For Java interop so they get the default sumOption diff --git a/build.sbt b/build.sbt index efcb13779..dec110087 100644 --- a/build.sbt +++ b/build.sbt @@ -162,6 +162,7 @@ lazy val algebirdCore = module("core").settings( libraryDependencies <++= (scalaVersion) { scalaVersion => Seq("com.googlecode.javaewah" % "JavaEWAH" % javaEwahVersion, "org.spire-math" %% "algebra" % algebraVersion, + "org.spire-math" %% "algebra-ring" % algebraVersion, "org.scala-lang" % "scala-reflect" % scalaVersion) ++ { if (isScala210x(scalaVersion)) Seq("org.scalamacros" %% "quasiquotes" % quasiquotesVersion) From 93f1b28aec894130aa2b273d21f7bd9fe7e20557 Mon Sep 17 00:00:00 2001 From: Oscar Boykin Date: Wed, 7 Sep 2016 11:51:42 -1000 Subject: [PATCH 05/12] disable mima, as this is a big binary break --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index e889c9c20..8eff8bd90 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,8 @@ matrix: script: ./sbt ++$TRAVIS_SCALA_VERSION clean test - scala: 2.11.8 - script: ./sbt ++$TRAVIS_SCALA_VERSION clean coverage test coverageReport mimaReportBinaryIssues + #script: ./sbt ++$TRAVIS_SCALA_VERSION clean coverage test coverageReport mimaReportBinaryIssues + script: ./sbt ++$TRAVIS_SCALA_VERSION clean coverage test coverageReport after_success: - bash <(curl -s https://codecov.io/bash) From 78e994b655ef7e122962b4a0ec7b14bd48e0cc4c Mon Sep 17 00:00:00 2001 From: Oscar Boykin Date: Sun, 18 Sep 2016 15:17:13 -1000 Subject: [PATCH 06/12] Add implicit resolution tests, Field compatiblity --- .../scala/com/twitter/algebird/Group.scala | 10 +++++++--- .../scala/com/twitter/algebird/Monoid.scala | 11 ++++++++--- .../main/scala/com/twitter/algebird/Ring.scala | 18 +++++++++++++++++- .../scala/com/twitter/algebird/Semigroup.scala | 12 +++++++++--- .../scala/com/twitter/algebird/package.scala | 6 ++++++ build.sbt | 13 +++++++------ 6 files changed, 54 insertions(+), 16 deletions(-) diff --git a/algebird-core/src/main/scala/com/twitter/algebird/Group.scala b/algebird-core/src/main/scala/com/twitter/algebird/Group.scala index 0b8e03320..0998375ce 100755 --- a/algebird-core/src/main/scala/com/twitter/algebird/Group.scala +++ b/algebird-core/src/main/scala/com/twitter/algebird/Group.scala @@ -92,11 +92,15 @@ class FromAlgebraGroup[T](m: AGroup[T]) extends FromAlgebraMonoid(m) with Group[ override def minus(r: T, l: T): T = m.remove(r, l) } -trait FromAlgebraGroupImplicit { - implicit def fromAlgebraGroup[T](m: AGroup[T]): Group[T] = new FromAlgebraGroup(m) +private[algebird] trait FromAlgebraGroupImplicit1 { + implicit def fromAlgebraAdditiveGroup[T](implicit m: AdditiveGroup[T]): Group[T] = + new FromAlgebraGroup(m.additive) +} +private[algebird] trait FromAlgebraGroupImplicit0 extends FromAlgebraGroupImplicit1 { + implicit def fromAlgebraGroup[T](implicit m: AGroup[T]): Group[T] = new FromAlgebraGroup(m) } -object Group extends GeneratedGroupImplicits with ProductGroups with FromAlgebraGroupImplicit { +object Group extends GeneratedGroupImplicits with ProductGroups with FromAlgebraGroupImplicit0 { // This pattern is really useful for typeclasses def negate[T](x: T)(implicit grp: Group[T]) = grp.negate(x) def minus[T](l: T, r: T)(implicit grp: Group[T]) = grp.minus(l, r) diff --git a/algebird-core/src/main/scala/com/twitter/algebird/Monoid.scala b/algebird-core/src/main/scala/com/twitter/algebird/Monoid.scala index 756a94b9d..7ab6a3137 100755 --- a/algebird-core/src/main/scala/com/twitter/algebird/Monoid.scala +++ b/algebird-core/src/main/scala/com/twitter/algebird/Monoid.scala @@ -234,11 +234,16 @@ class FromAlgebraMonoid[T](m: AMonoid[T]) extends FromAlgebraSemigroup(m) with M override def zero: T = m.empty } -trait FromAlgebraMonoidImplicit { - implicit def fromAlgebraMonoid[T](m: AMonoid[T]): Monoid[T] = new FromAlgebraMonoid(m) +private[algebird] trait FromAlgebraMonoidImplicit1 { + implicit def fromAlgebraAdditiveMonoid[T](implicit m: AdditiveMonoid[T]): Monoid[T] = + new FromAlgebraMonoid(m.additive) } -object Monoid extends GeneratedMonoidImplicits with ProductMonoids with FromAlgebraMonoidImplicit { +private[algebird] trait FromAlgebraMonoidImplicit0 extends FromAlgebraMonoidImplicit1 { + implicit def fromAlgebraMonoid[T](implicit m: AMonoid[T]): Monoid[T] = new FromAlgebraMonoid(m) +} + +object Monoid extends GeneratedMonoidImplicits with ProductMonoids with FromAlgebraMonoidImplicit0 { // This pattern is really useful for typeclasses def zero[T](implicit mon: Monoid[T]) = mon.zero // strictly speaking, same as Semigroup, but most interesting examples diff --git a/algebird-core/src/main/scala/com/twitter/algebird/Ring.scala b/algebird-core/src/main/scala/com/twitter/algebird/Ring.scala index 6cc624927..fb1deb9d7 100755 --- a/algebird-core/src/main/scala/com/twitter/algebird/Ring.scala +++ b/algebird-core/src/main/scala/com/twitter/algebird/Ring.scala @@ -147,7 +147,23 @@ trait NumericRingProvider { implicit def numericRing[T: Numeric]: Ring[T] = new NumericRing[T] } -object Ring extends GeneratedRingImplicits with ProductRings { +class FromAlgebraRing[T](r: ARing[T]) extends Ring[T] { + override def zero: T = r.zero + override def one: T = r.one + override def plus(a: T, b: T): T = r.plus(a, b) + override def negate(t: T): T = r.negate(t) + override def minus(a: T, b: T): T = r.minus(a, b) + override def sum(ts: TraversableOnce[T]): T = r.sum(ts) + override def sumOption(ts: TraversableOnce[T]): Option[T] = r.trySum(ts) + override def times(a: T, b: T): T = r.times(a, b) +} + +private[algebird] trait RingImplicits0 extends NumericRingProvider { + implicit def fromAlgebraRing[T](implicit r: ARing[T]): Ring[T] = + new FromAlgebraRing(r) +} + +object Ring extends GeneratedRingImplicits with ProductRings with RingImplicits0 { // This pattern is really useful for typeclasses def one[T](implicit rng: Ring[T]) = rng.one def times[T](l: T, r: T)(implicit rng: Ring[T]) = rng.times(l, r) diff --git a/algebird-core/src/main/scala/com/twitter/algebird/Semigroup.scala b/algebird-core/src/main/scala/com/twitter/algebird/Semigroup.scala index 7c2cd08de..078f0edab 100755 --- a/algebird-core/src/main/scala/com/twitter/algebird/Semigroup.scala +++ b/algebird-core/src/main/scala/com/twitter/algebird/Semigroup.scala @@ -88,12 +88,17 @@ class FromAlgebraSemigroup[T](sg: ASemigroup[T]) extends Semigroup[T] { /** * An Algebra semigroup can be an Algebird semigroup */ -trait FromAlgebraSemigroupImplicit { +private[algebird] trait FromAlgebraSemigroupImplicit1 { + implicit def fromAlgebraAdditiveSemigroup[T](implicit sg: AdditiveSemigroup[T]): Semigroup[T] = + new FromAlgebraSemigroup(sg.additive) +} + +private[algebird] trait FromAlgebraSemigroupImplicit0 extends FromAlgebraSemigroupImplicit1 { implicit def fromAlgebraSemigroup[T](implicit sg: ASemigroup[T]): Semigroup[T] = new FromAlgebraSemigroup(sg) } -object Semigroup extends GeneratedSemigroupImplicits with ProductSemigroups with FromAlgebraSemigroupImplicit { +object Semigroup extends GeneratedSemigroupImplicits with ProductSemigroups with FromAlgebraSemigroupImplicit0 { def maybePlus[T](opt: Option[T], t: T)(implicit sg: Semigroup[T]): T = opt match { case None => t @@ -110,7 +115,8 @@ object Semigroup extends GeneratedSemigroupImplicits with ProductSemigroups with def sumOption[T](iter: TraversableOnce[T])(implicit sg: Semigroup[T]): Option[T] = sg.sumOption(iter) - def from[T](associativeFn: (T, T) => T): Semigroup[T] = new Semigroup[T] { def plus(l: T, r: T) = associativeFn(l, r) } + def from[T](associativeFn: (T, T) => T): Semigroup[T] = + new Semigroup[T] { def plus(l: T, r: T) = associativeFn(l, r) } /** * Same as v + v + v .. + v (i times in total) diff --git a/algebird-core/src/main/scala/com/twitter/algebird/package.scala b/algebird-core/src/main/scala/com/twitter/algebird/package.scala index 24bbf7d42..56771318a 100644 --- a/algebird-core/src/main/scala/com/twitter/algebird/package.scala +++ b/algebird-core/src/main/scala/com/twitter/algebird/package.scala @@ -24,4 +24,10 @@ package object algebird { */ private[algebird] abstract class AbstractIterable[T] extends Iterable[T] private[algebird] abstract class AbstractIterator[T] extends Iterator[T] + + /** + * To keep code using algebird.Field compiling, we export algebra Field + */ + type Field[V] = algebra.ring.Field[V] + val Field = algebra.ring.Field } diff --git a/build.sbt b/build.sbt index d84818700..f3c27561c 100644 --- a/build.sbt +++ b/build.sbt @@ -6,12 +6,13 @@ import com.typesafe.tools.mima.plugin.MimaPlugin.mimaDefaultSettings import pl.project13.scala.sbt.JmhPlugin import scalariform.formatter.preferences._ +val algebraVersion = "0.5.1" +val bijectionVersion = "0.9.0" +val javaEwahVersion = "0.6.6" val paradiseVersion = "2.1.0" val quasiquotesVersion = "2.1.0" -val bijectionVersion = "0.9.0" +val scalaTestVersion = "2.2.4" val utilVersion = "6.20.0" -val algebraVersion = "0.5.1" -val javaEwahVersion = "0.6.6" def scalaBinaryVersion(scalaVersion: String) = scalaVersion match { case version if version startsWith "2.10" => "2.10" @@ -162,14 +163,14 @@ def module(name: String) = { } lazy val algebirdCore = module("core").settings( - test := { }, // All tests reside in algebirdTest initialCommands := """ import com.twitter.algebird._ """.stripMargin('|'), libraryDependencies <++= (scalaVersion) { scalaVersion => Seq("com.googlecode.javaewah" % "JavaEWAH" % javaEwahVersion, "org.typelevel" %% "algebra" % algebraVersion, - "org.scala-lang" % "scala-reflect" % scalaVersion) ++ { + "org.scala-lang" % "scala-reflect" % scalaVersion, + "org.scalatest" %% "scalatest" % scalaTestVersion % "test") ++ { if (isScala210x(scalaVersion)) Seq("org.scalamacros" %% "quasiquotes" % quasiquotesVersion) else @@ -185,7 +186,7 @@ lazy val algebirdTest = module("test").settings( testOptions in Test ++= Seq(Tests.Argument(TestFrameworks.ScalaCheck, "-verbosity", "4")), libraryDependencies <++= (scalaVersion) { scalaVersion => Seq("org.scalacheck" %% "scalacheck" % "1.12.5", - "org.scalatest" %% "scalatest" % "2.2.4") ++ { + "org.scalatest" %% "scalatest" % scalaTestVersion) ++ { if (isScala210x(scalaVersion)) Seq("org.scalamacros" %% "quasiquotes" % quasiquotesVersion) else From ff1b1d0d3de27c198ab4ef73c7b829c6dc90e3d3 Mon Sep 17 00:00:00 2001 From: Oscar Boykin Date: Sun, 18 Sep 2016 17:08:29 -1000 Subject: [PATCH 07/12] Add Ring.product --- algebird-core/src/main/scala/com/twitter/algebird/Ring.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/algebird-core/src/main/scala/com/twitter/algebird/Ring.scala b/algebird-core/src/main/scala/com/twitter/algebird/Ring.scala index fb1deb9d7..b1015495f 100755 --- a/algebird-core/src/main/scala/com/twitter/algebird/Ring.scala +++ b/algebird-core/src/main/scala/com/twitter/algebird/Ring.scala @@ -156,6 +156,7 @@ class FromAlgebraRing[T](r: ARing[T]) extends Ring[T] { override def sum(ts: TraversableOnce[T]): T = r.sum(ts) override def sumOption(ts: TraversableOnce[T]): Option[T] = r.trySum(ts) override def times(a: T, b: T): T = r.times(a, b) + override def product(ts: TraversableOnce[T]): T = r.product(ts) } private[algebird] trait RingImplicits0 extends NumericRingProvider { From 9b37eff0686fb4cb81cf401dfd5ef17aec09a9bc Mon Sep 17 00:00:00 2001 From: Oscar Boykin Date: Fri, 23 Sep 2016 14:53:39 -0700 Subject: [PATCH 08/12] Add Field compatibility and tests --- .../scala/com/twitter/algebird/field.scala | 105 ++++++++++++++++++ .../algebird/AlgebraResolutionTest.scala | 46 ++++++++ 2 files changed, 151 insertions(+) create mode 100644 algebird-core/src/main/scala/com/twitter/algebird/field.scala create mode 100644 algebird-core/src/test/scala/com/twitter/algebird/AlgebraResolutionTest.scala diff --git a/algebird-core/src/main/scala/com/twitter/algebird/field.scala b/algebird-core/src/main/scala/com/twitter/algebird/field.scala new file mode 100644 index 000000000..6c62715d7 --- /dev/null +++ b/algebird-core/src/main/scala/com/twitter/algebird/field.scala @@ -0,0 +1,105 @@ +package com.twitter.algebird + +import java.lang.{ Float => JFloat, Double => JDouble } +/** + * This is here to ease transition to using algebra.Field as the field + * type. Intended use is to do: + * + * {code} import com.twitter.algebird.field._ {/code} + * + * Note, this are not strictly lawful since floating point + * arithmetic using IEEE-754 is only approximately associative + * and distributive. + */ +object field { + implicit object ForFloat extends Field[Float] { + override def one: Float = 1.0f + override def zero: Float = 0.0f + override def negate(v: Float): Float = -v + override def plus(l: Float, r: Float): Float = l + r + override def sum(v: TraversableOnce[Float]): Float = { + var sum = 0.0f + v.foreach { sum += _ } + return sum + } + override def trySum(v: TraversableOnce[Float]): Option[Float] = + if (v.isEmpty) None else Some(sum(v)) + override def minus(l: Float, r: Float): Float = l - r + override def times(l: Float, r: Float): Float = l * r + override def div(l: Float, r: Float): Float = l / r + override def quot(l: Float, r: Float): Float = l / r + override def mod(l: Float, r: Float): Float = zero + } + implicit object ForJFloat extends Field[JFloat] { + override val one: JFloat = JFloat.valueOf(1.0f) + override val zero: JFloat = JFloat.valueOf(0.0f) + override def negate(v: JFloat): JFloat = -v + override def plus(l: JFloat, r: JFloat): JFloat = l + r + override def sum(v: TraversableOnce[JFloat]): JFloat = { + var sum = 0.0f + v.foreach { sum += _ } + return sum + } + override def trySum(v: TraversableOnce[JFloat]): Option[JFloat] = + if (v.isEmpty) None else Some(sum(v)) + override def minus(l: JFloat, r: JFloat): JFloat = l - r + override def times(l: JFloat, r: JFloat): JFloat = l * r + override def div(l: JFloat, r: JFloat): JFloat = l / r + override def quot(l: JFloat, r: JFloat): JFloat = l / r + override def mod(l: JFloat, r: JFloat): JFloat = zero + } + implicit object ForDouble extends Field[Double] { + override def one: Double = 1.0 + override def zero: Double = 0.0 + override def negate(v: Double): Double = -v + override def plus(l: Double, r: Double): Double = l + r + override def sum(v: TraversableOnce[Double]): Double = { + var sum = 0.0 + v.foreach { sum += _.floatValue } + return sum + } + override def trySum(v: TraversableOnce[Double]): Option[Double] = + if (v.isEmpty) None else Some(sum(v)) + override def minus(l: Double, r: Double): Double = l - r + override def times(l: Double, r: Double): Double = l * r + override def div(l: Double, r: Double): Double = l / r + override def quot(l: Double, r: Double): Double = l / r + override def mod(l: Double, r: Double): Double = zero + } + implicit object ForJDouble extends Field[JDouble] { + override val one: JDouble = JDouble.valueOf(1.0) + override val zero: JDouble = JDouble.valueOf(0.0) + override def negate(v: JDouble): JDouble = -v + override def plus(l: JDouble, r: JDouble): JDouble = l + r + override def sum(v: TraversableOnce[JDouble]): JDouble = { + var sum = 0.0 + v.foreach { sum += _.doubleValue } + return sum + } + override def trySum(v: TraversableOnce[JDouble]): Option[JDouble] = + if (v.isEmpty) None else Some(sum(v)) + override def minus(l: JDouble, r: JDouble): JDouble = l - r + override def times(l: JDouble, r: JDouble): JDouble = l * r + override def div(l: JDouble, r: JDouble): JDouble = l / r + override def quot(l: JDouble, r: JDouble): JDouble = l / r + override def mod(l: JDouble, r: JDouble): JDouble = zero + } + + /** + * These methods were originally on algebird.Field, but are not present on + * algebra.Field + */ + implicit class AlgebirdFieldEnrichments[T](val field: Field[T]) extends AnyVal { + def assertNotZero(t: T): Unit = + if (t == field.zero) throw new IllegalArgumentException(s"found $t, require ${field.zero}") + else () + + def nonZeroOption(t: T): Option[T] = + if (t == field.zero) None + else Some(t) + + def isNonZero(v: T): Boolean = v != field.zero + + def inverse(t: T): T = field.reciprocal(t) + } +} diff --git a/algebird-core/src/test/scala/com/twitter/algebird/AlgebraResolutionTest.scala b/algebird-core/src/test/scala/com/twitter/algebird/AlgebraResolutionTest.scala new file mode 100644 index 000000000..2783f5f14 --- /dev/null +++ b/algebird-core/src/test/scala/com/twitter/algebird/AlgebraResolutionTest.scala @@ -0,0 +1,46 @@ +package com.twitter.algebird + +import org.scalatest.FunSuite + +/** + * This is just a compilation test that we can resolve + * algebird types from implicit algebra instances. + */ +class AlgebraResolutionTest extends FunSuite { + // A type with no built in algebird algebras + trait Empty { } + test("algebra.Semigroup") { + implicit def fakeSemigroup[T]: algebra.Semigroup[T] = null + implicitly[Semigroup[Empty]] + } + test("algebra.ring.AdditiveSemigroup") { + implicit def fakeAdditiveSemigroup[T]: algebra.ring.AdditiveSemigroup[T] = + Semigroup.from[T] { (a, b) => a } + + implicitly[Semigroup[Empty]] + } + test("algebra.Monoid") { + implicit def fakeMonoid[T]: algebra.Monoid[T] = null + implicitly[Monoid[Empty]] + } + test("algebra.ring.AdditiveMonoid") { + implicit def fakeAdditiveMonoid[T]: algebra.ring.AdditiveMonoid[T] = + Monoid.from[T](null.asInstanceOf[T]) { (a, b) => a } + + implicitly[Monoid[Empty]] + } + test("algebra.Group") { + implicit def fakeGroup[T]: algebra.Group[T] = null + implicitly[Group[Empty]] + } + test("algebra.ring.AdditiveGroup") { + implicit def fakeAdditiveGroup[T]: algebra.ring.AdditiveGroup[T] = + implicitly[Group[Null]].asInstanceOf[Group[T]] + + implicitly[Group[Empty]] + } + test("algebra.ring.Ring") { + implicit def fakeRing[T]: algebra.ring.Ring[T] = null + implicitly[Ring[Empty]] + } +} From 24e2df89ea7fe2e5ca456d7e11ac9a172d0576b9 Mon Sep 17 00:00:00 2001 From: "P. Oscar Boykin" Date: Sun, 6 Nov 2016 13:08:40 -1000 Subject: [PATCH 09/12] Use Rng in AdjoinedUnitRing --- .../twitter/algebird/AdjoinedUnitRing.scala | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/algebird-core/src/main/scala/com/twitter/algebird/AdjoinedUnitRing.scala b/algebird-core/src/main/scala/com/twitter/algebird/AdjoinedUnitRing.scala index ccbbebe68..164bc6809 100644 --- a/algebird-core/src/main/scala/com/twitter/algebird/AdjoinedUnitRing.scala +++ b/algebird-core/src/main/scala/com/twitter/algebird/AdjoinedUnitRing.scala @@ -17,6 +17,8 @@ package com.twitter.algebird import scala.annotation.tailrec +import algebra.ring.Rng + /** * This is for the case where your Ring[T] is a Rng (i.e. there is no unit). * @see http://en.wikipedia.org/wiki/Pseudo-ring#Adjoining_an_identity_element @@ -27,33 +29,36 @@ case class AdjoinedUnit[T](ones: BigInt, get: T) { object AdjoinedUnit { def apply[T](item: T): AdjoinedUnit[T] = new AdjoinedUnit[T](BigInt(0), item) - implicit def ring[T](implicit ring: Ring[T]): Ring[AdjoinedUnit[T]] = new AdjoinedUnitRing[T] + implicit def ring[T](implicit ring: Rng[T]): Ring[AdjoinedUnit[T]] = new AdjoinedUnitRing[T] } -class AdjoinedUnitRing[T](implicit ring: Ring[T]) extends Ring[AdjoinedUnit[T]] { - val one = AdjoinedUnit[T](BigInt(1), ring.zero) - val zero = AdjoinedUnit[T](ring.zero) +class AdjoinedUnitRing[T](implicit rng: Rng[T]) extends Ring[AdjoinedUnit[T]] { + val one = AdjoinedUnit[T](BigInt(1), rng.zero) + val zero = AdjoinedUnit[T](rng.zero) + + private[this] val group: Group[T] = + new FromAlgebraGroup(rng.additive) override def isNonZero(it: AdjoinedUnit[T]) = - (it.ones != 0) || ring.isNonZero(it.get) + (it.ones != 0) || group.isNonZero(it.get) def plus(left: AdjoinedUnit[T], right: AdjoinedUnit[T]) = - AdjoinedUnit(left.ones + right.ones, ring.plus(left.get, right.get)) + AdjoinedUnit(left.ones + right.ones, rng.plus(left.get, right.get)) override def negate(it: AdjoinedUnit[T]) = - AdjoinedUnit(-it.ones, ring.negate(it.get)) + AdjoinedUnit(-it.ones, rng.negate(it.get)) override def minus(left: AdjoinedUnit[T], right: AdjoinedUnit[T]) = - AdjoinedUnit(left.ones - right.ones, ring.minus(left.get, right.get)) + AdjoinedUnit(left.ones - right.ones, rng.minus(left.get, right.get)) def times(left: AdjoinedUnit[T], right: AdjoinedUnit[T]) = { // (n1, g1) * (n2, g2) = (n1*n2, (n1*g1) + (n2*g1) + (g1*g2)) import Group.intTimes val ones = left.ones * right.ones - val part0 = intTimes(left.ones, right.get)(ring) - val part1 = intTimes(right.ones, left.get)(ring) - val part2 = ring.times(left.get, right.get) - val nonUnit = ring.plus(part0, ring.plus(part1, part2)) + val part0 = intTimes(left.ones, right.get)(group) + val part1 = intTimes(right.ones, left.get)(group) + val part2 = rng.times(left.get, right.get) + val nonUnit = rng.plus(part0, rng.plus(part1, part2)) AdjoinedUnit(ones, nonUnit) } From 9ee238f6daa746a0c4b494b52205d6e19b4bdf5d Mon Sep 17 00:00:00 2001 From: "P. Oscar Boykin" Date: Sun, 6 Nov 2016 15:40:30 -1000 Subject: [PATCH 10/12] Fix an issue with Ring not extending CommutativeGroup --- .../main/scala/com/twitter/algebird/Group.scala | 1 + .../main/scala/com/twitter/algebird/Monoid.scala | 1 + .../src/main/scala/com/twitter/algebird/Ring.scala | 14 +++++++++++--- .../com/twitter/algebird/AdJoinedUnitRing.scala | 4 ++++ 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/algebird-core/src/main/scala/com/twitter/algebird/Group.scala b/algebird-core/src/main/scala/com/twitter/algebird/Group.scala index 0eae41e26..e3b05e356 100755 --- a/algebird-core/src/main/scala/com/twitter/algebird/Group.scala +++ b/algebird-core/src/main/scala/com/twitter/algebird/Group.scala @@ -126,6 +126,7 @@ object Group extends GeneratedGroupImplicits with ProductGroups with FromAlgebra implicit def jshortGroup: Group[JShort] = JShortRing implicit def longGroup: Group[Long] = LongRing implicit def bigIntGroup: Group[BigInt] = BigIntRing + implicit def bigDecimalGroup: Group[BigDecimal] = implicitly[Ring[BigDecimal]] implicit def jlongGroup: Group[JLong] = JLongRing implicit def floatGroup: Group[Float] = FloatRing implicit def jfloatGroup: Group[JFloat] = JFloatRing diff --git a/algebird-core/src/main/scala/com/twitter/algebird/Monoid.scala b/algebird-core/src/main/scala/com/twitter/algebird/Monoid.scala index 7ab6a3137..b069b8407 100755 --- a/algebird-core/src/main/scala/com/twitter/algebird/Monoid.scala +++ b/algebird-core/src/main/scala/com/twitter/algebird/Monoid.scala @@ -291,6 +291,7 @@ object Monoid extends GeneratedMonoidImplicits with ProductMonoids with FromAlge implicit def shortMonoid: Monoid[Short] = ShortRing implicit def jshortMonoid: Monoid[JShort] = JShortRing implicit def bigIntMonoid: Monoid[BigInt] = BigIntRing + implicit def bigDecimalMonoid: Monoid[BigDecimal] = implicitly[Ring[BigDecimal]] implicit def longMonoid: Monoid[Long] = LongRing implicit def jlongMonoid: Monoid[JLong] = JLongRing implicit def floatMonoid: Monoid[Float] = FloatRing diff --git a/algebird-core/src/main/scala/com/twitter/algebird/Ring.scala b/algebird-core/src/main/scala/com/twitter/algebird/Ring.scala index 7133adb5e..d7cf9a4f4 100755 --- a/algebird-core/src/main/scala/com/twitter/algebird/Ring.scala +++ b/algebird-core/src/main/scala/com/twitter/algebird/Ring.scala @@ -17,6 +17,7 @@ package com.twitter.algebird import java.lang.{ Integer => JInt, Short => JShort, Long => JLong, Float => JFloat, Double => JDouble, Boolean => JBool } import algebra.ring.{ Ring => ARing } +import algebra.CommutativeGroup import scala.annotation.implicitNotFound /** @@ -43,7 +44,7 @@ import scala.annotation.implicitNotFound */ @implicitNotFound(msg = "Cannot find Ring type class for ${T}") -trait Ring[@specialized(Int, Long, Float, Double) T] extends Group[T] with ARing[T] { +trait Ring[@specialized(Int, Long, Float, Double) T] extends Group[T] with CommutativeGroup[T] with ARing[T] { def one: T def times(a: T, b: T): T override def product(iter: TraversableOnce[T]): T = @@ -169,8 +170,15 @@ object Ring extends GeneratedRingImplicits with ProductRings with RingImplicits0 // This pattern is really useful for typeclasses def one[T](implicit rng: Ring[T]) = rng.one def times[T](l: T, r: T)(implicit rng: Ring[T]) = rng.times(l, r) - def asTimesMonoid[T](implicit ring: Ring[T]): Monoid[T] = - Monoid.from[T](ring.one)(ring.times _) + def asTimesMonoid[T](implicit ring: Ring[T]): Monoid[T] = new Monoid[T] { + def zero = ring.one + def plus(a: T, b: T): T = ring.times(a, b) + override def sumOption(ts: TraversableOnce[T]): Option[T] = + if (ts.isEmpty) None + else Some(ring.product(ts)) + override def sum(ts: TraversableOnce[T]): T = + ring.product(ts) + } // Left product: (((a * b) * c) * d) def product[T](iter: TraversableOnce[T])(implicit ring: Ring[T]) = ring.product(iter) diff --git a/algebird-test/src/test/scala/com/twitter/algebird/AdJoinedUnitRing.scala b/algebird-test/src/test/scala/com/twitter/algebird/AdJoinedUnitRing.scala index f47c0594e..016077fe8 100644 --- a/algebird-test/src/test/scala/com/twitter/algebird/AdJoinedUnitRing.scala +++ b/algebird-test/src/test/scala/com/twitter/algebird/AdJoinedUnitRing.scala @@ -16,6 +16,8 @@ limitations under the License. package com.twitter.algebird +import algebra.ring.Rng + import org.scalacheck.Arbitrary import org.scalacheck.Prop._ @@ -32,6 +34,8 @@ class AdjoinedRingSpecification extends CheckProperties { } } + implicit def rng[T: Ring]: Rng[T] = implicitly[Ring[T]] + property("AdjoinedUnit[Int] is a Ring") { ringLaws[AdjoinedUnit[Int]] } From a081a2caf279304bb102bb0c5b8b164751a03103 Mon Sep 17 00:00:00 2001 From: Sam Ritchie Date: Fri, 2 Dec 2016 15:19:47 -0700 Subject: [PATCH 11/12] bump algebra version --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 8ef283de5..ed7e47b1e 100644 --- a/build.sbt +++ b/build.sbt @@ -8,7 +8,7 @@ import pl.project13.scala.sbt.JmhPlugin import sbtunidoc.Plugin.UnidocKeys._ import scalariform.formatter.preferences._ -val algebraVersion = "0.5.1" +val algebraVersion = "0.6.0" val bijectionVersion = "0.9.0" val javaEwahVersion = "0.6.6" val paradiseVersion = "2.1.0" From 5c5d11a428b4a26b4ca8f611c9ca2e97dc9da0cf Mon Sep 17 00:00:00 2001 From: Sam Ritchie Date: Fri, 2 Dec 2016 15:41:24 -0700 Subject: [PATCH 12/12] remove quot and mod in field --- .../src/main/scala/com/twitter/algebird/field.scala | 8 -------- 1 file changed, 8 deletions(-) diff --git a/algebird-core/src/main/scala/com/twitter/algebird/field.scala b/algebird-core/src/main/scala/com/twitter/algebird/field.scala index 6c62715d7..29297c36b 100644 --- a/algebird-core/src/main/scala/com/twitter/algebird/field.scala +++ b/algebird-core/src/main/scala/com/twitter/algebird/field.scala @@ -27,8 +27,6 @@ object field { override def minus(l: Float, r: Float): Float = l - r override def times(l: Float, r: Float): Float = l * r override def div(l: Float, r: Float): Float = l / r - override def quot(l: Float, r: Float): Float = l / r - override def mod(l: Float, r: Float): Float = zero } implicit object ForJFloat extends Field[JFloat] { override val one: JFloat = JFloat.valueOf(1.0f) @@ -45,8 +43,6 @@ object field { override def minus(l: JFloat, r: JFloat): JFloat = l - r override def times(l: JFloat, r: JFloat): JFloat = l * r override def div(l: JFloat, r: JFloat): JFloat = l / r - override def quot(l: JFloat, r: JFloat): JFloat = l / r - override def mod(l: JFloat, r: JFloat): JFloat = zero } implicit object ForDouble extends Field[Double] { override def one: Double = 1.0 @@ -63,8 +59,6 @@ object field { override def minus(l: Double, r: Double): Double = l - r override def times(l: Double, r: Double): Double = l * r override def div(l: Double, r: Double): Double = l / r - override def quot(l: Double, r: Double): Double = l / r - override def mod(l: Double, r: Double): Double = zero } implicit object ForJDouble extends Field[JDouble] { override val one: JDouble = JDouble.valueOf(1.0) @@ -81,8 +75,6 @@ object field { override def minus(l: JDouble, r: JDouble): JDouble = l - r override def times(l: JDouble, r: JDouble): JDouble = l * r override def div(l: JDouble, r: JDouble): JDouble = l / r - override def quot(l: JDouble, r: JDouble): JDouble = l / r - override def mod(l: JDouble, r: JDouble): JDouble = zero } /**