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

Cleans up intTimes #131

Merged
merged 1 commit into from Feb 26, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -21,7 +21,9 @@ import scala.annotation.tailrec
* This is for the case where your Ring[T] is a Rng (i.e. there is no unit). * 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 * @see http://en.wikipedia.org/wiki/Pseudo-ring#Adjoining_an_identity_element
*/ */
case class AdjoinedUnit[T](ones: BigInt, get: T) case class AdjoinedUnit[T](ones: BigInt, get: T) {
def unwrap: Option[T] = if (ones == 0) Some(get) else None
}


object AdjoinedUnit { object AdjoinedUnit {
def apply[T](item: T): AdjoinedUnit[T] = new AdjoinedUnit[T](BigInt(0), item) def apply[T](item: T): AdjoinedUnit[T] = new AdjoinedUnit[T](BigInt(0), item)
Expand All @@ -33,7 +35,7 @@ class AdjoinedUnitRing[T](implicit ring: Ring[T]) extends Ring[AdjoinedUnit[T]]
val zero = AdjoinedUnit[T](ring.zero) val zero = AdjoinedUnit[T](ring.zero)


override def isNonZero(it: AdjoinedUnit[T]) = override def isNonZero(it: AdjoinedUnit[T]) =
(it.ones != 0) && (ring.isNonZero(it.get)) (it.ones != 0) || ring.isNonZero(it.get)


def plus(left: AdjoinedUnit[T], right: AdjoinedUnit[T]) = def plus(left: AdjoinedUnit[T], right: AdjoinedUnit[T]) =
AdjoinedUnit(left.ones + right.ones, ring.plus(left.get, right.get)) AdjoinedUnit(left.ones + right.ones, ring.plus(left.get, right.get))
Expand All @@ -43,36 +45,13 @@ class AdjoinedUnitRing[T](implicit ring: Ring[T]) extends Ring[AdjoinedUnit[T]]
override def minus(left: AdjoinedUnit[T], right: AdjoinedUnit[T]) = 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, ring.minus(left.get, right.get))


final def intTimes(i: BigInt, v: T): T = {
if(i < 0) {
intTimes(i, ring.negate(v))
}
else if (i == 0) {
ring.zero
}
else if(i == 1) {
v
}
else {
// i * v == ((i/2) * v + (i/2)*v) + (1/0)*v
val half = i / 2
val rem = i % 2
val ht = intTimes(half, v)
val twoV = ring.plus(ht, ht)
if (rem == 0) {
twoV
}
else {
ring.plus(twoV, v)
}
}
}

def times(left: AdjoinedUnit[T], right: AdjoinedUnit[T]) = { def times(left: AdjoinedUnit[T], right: AdjoinedUnit[T]) = {
// (n1, g1) * (n1, g2) = (n1*n2, (n2*g1) + (n2*g1) + g1*g2)) // (n1, g1) * (n1, g2) = (n1*n2, (n2*g1) + (n2*g1) + g1*g2))
import Group.intTimes

val ones = left.ones * right.ones val ones = left.ones * right.ones
val part0 = intTimes(left.ones, right.get) val part0 = intTimes(left.ones, right.get)(ring)
val part1 = intTimes(right.ones, left.get) val part1 = intTimes(right.ones, left.get)(ring)
val part2 = ring.times(left.get, right.get) val part2 = ring.times(left.get, right.get)
val nonUnit = ring.plus(part0, ring.plus(part1, part2)) val nonUnit = ring.plus(part0, ring.plus(part1, part2))


Expand Down
15 changes: 15 additions & 0 deletions algebird-core/src/main/scala/com/twitter/algebird/Group.scala
Expand Up @@ -19,6 +19,7 @@ import java.lang.{Integer => JInt, Short => JShort, Long => JLong, Float => JFlo
import java.util.{List => JList, Map => JMap} import java.util.{List => JList, Map => JMap}


import scala.annotation.implicitNotFound import scala.annotation.implicitNotFound
import scala.math.Equiv
/** /**
* Group: this is a monoid that also has subtraction (and negation): * Group: this is a monoid that also has subtraction (and negation):
* So, you can do (a-b), or -a (which is equal to 0 - a). * So, you can do (a-b), or -a (which is equal to 0 - a).
Expand Down Expand Up @@ -50,6 +51,19 @@ object Group extends GeneratedGroupImplicits {
// This pattern is really useful for typeclasses // This pattern is really useful for typeclasses
def negate[T](x : T)(implicit grp : Group[T]) = grp.negate(x) 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) def minus[T](l : T, r : T)(implicit grp : Group[T]) = grp.minus(l,r)
// nonZero and subtraction give an equiv, useful for Map[K,V]
def equiv[T](implicit grp: Group[T]): Equiv[T] = Equiv.fromFunction[T] { (a, b) =>
!grp.isNonZero(grp.minus(a, b))
}
/** Same as v + v + v .. + v (i times in total) */
def intTimes[T](i: BigInt, v: T)(implicit grp: Group[T]): T =
if(i < 0) {
Monoid.intTimes(-i, grp.negate(v))
}
else {
Monoid.intTimes(i, v)(grp)
}



implicit val nullGroup : Group[Null] = NullGroup implicit val nullGroup : Group[Null] = NullGroup
implicit val unitGroup : Group[Unit] = UnitGroup implicit val unitGroup : Group[Unit] = UnitGroup
Expand All @@ -60,6 +74,7 @@ object Group extends GeneratedGroupImplicits {
implicit val shortGroup : Group[Short] = ShortRing implicit val shortGroup : Group[Short] = ShortRing
implicit val jshortGroup : Group[JShort] = JShortRing implicit val jshortGroup : Group[JShort] = JShortRing
implicit val longGroup : Group[Long] = LongRing implicit val longGroup : Group[Long] = LongRing
implicit val bigIntGroup : Group[BigInt] = BigIntRing
implicit val jlongGroup : Group[JLong] = JLongRing implicit val jlongGroup : Group[JLong] = JLongRing
implicit val floatGroup : Group[Float] = FloatField implicit val floatGroup : Group[Float] = FloatField
implicit val jfloatGroup : Group[JFloat] = JFloatField implicit val jfloatGroup : Group[JFloat] = JFloatField
Expand Down
16 changes: 15 additions & 1 deletion algebird-core/src/main/scala/com/twitter/algebird/Monoid.scala
Expand Up @@ -45,7 +45,7 @@ trait Monoid[@specialized(Int,Long,Float,Double) T] extends Semigroup[T] {
None None
} }
} }
@deprecated("Just use Monoid.sum") // Override this if there is a more efficient means to implement this
def sum(vs: TraversableOnce[T]): T = Monoid.sum(vs)(this) def sum(vs: TraversableOnce[T]): T = Monoid.sum(vs)(this)
} }


Expand Down Expand Up @@ -133,6 +133,19 @@ object Monoid extends GeneratedMonoidImplicits {
def plus(l:T, r:T) = associativeFn(l,r) def plus(l:T, r:T) = associativeFn(l,r)
} }


/** Same as v + v + v .. + v (i times in total)
* requires i >= 0, wish we had NonnegativeBigInt as a class
*/
def intTimes[T](i: BigInt, v: T)(implicit mon: Monoid[T]): T = {
require(i >= 0, "Cannot do negative products with a Monoid, try Group.intTimes")
if (i == 0) {
mon.zero
}
else {
Semigroup.intTimes(i, v)(mon)
}
}

implicit val nullMonoid : Monoid[Null] = NullGroup implicit val nullMonoid : Monoid[Null] = NullGroup
implicit val unitMonoid : Monoid[Unit] = UnitGroup implicit val unitMonoid : Monoid[Unit] = UnitGroup
implicit val boolMonoid : Monoid[Boolean] = BooleanField implicit val boolMonoid : Monoid[Boolean] = BooleanField
Expand All @@ -141,6 +154,7 @@ object Monoid extends GeneratedMonoidImplicits {
implicit val jintMonoid : Monoid[JInt] = JIntRing implicit val jintMonoid : Monoid[JInt] = JIntRing
implicit val shortMonoid : Monoid[Short] = ShortRing implicit val shortMonoid : Monoid[Short] = ShortRing
implicit val jshortMonoid : Monoid[JShort] = JShortRing implicit val jshortMonoid : Monoid[JShort] = JShortRing
implicit val bigIntMonoid : Monoid[BigInt] = BigIntRing
implicit val longMonoid : Monoid[Long] = LongRing implicit val longMonoid : Monoid[Long] = LongRing
implicit val jlongMonoid : Monoid[JLong] = JLongRing implicit val jlongMonoid : Monoid[JLong] = JLongRing
implicit val floatMonoid : Monoid[Float] = FloatField implicit val floatMonoid : Monoid[Float] = FloatField
Expand Down
10 changes: 10 additions & 0 deletions algebird-core/src/main/scala/com/twitter/algebird/Ring.scala
Expand Up @@ -62,6 +62,15 @@ object LongRing extends Ring[Long] {
override def times(l : Long, r : Long) = l * r override def times(l : Long, r : Long) = l * r
} }


object BigIntRing extends Ring[BigInt] {
override val zero = BigInt(0)
override val one = BigInt(1)
override def negate(v : BigInt) = -v
override def plus(l : BigInt, r : BigInt) = l + r
override def minus(l : BigInt, r : BigInt) = l - r
override def times(l : BigInt, r : BigInt) = l * r
}

object Ring extends GeneratedRingImplicits { object Ring extends GeneratedRingImplicits {
// This pattern is really useful for typeclasses // This pattern is really useful for typeclasses
def one[T](implicit rng : Ring[T]) = rng.one def one[T](implicit rng : Ring[T]) = rng.one
Expand All @@ -85,6 +94,7 @@ object Ring extends GeneratedRingImplicits {
implicit val shortRing : Ring[Short] = ShortRing implicit val shortRing : Ring[Short] = ShortRing
implicit val jshortRing : Ring[JShort] = JShortRing implicit val jshortRing : Ring[JShort] = JShortRing
implicit val longRing : Ring[Long] = LongRing implicit val longRing : Ring[Long] = LongRing
implicit val bigIntRing : Ring[BigInt] = BigIntRing
implicit val jlongRing : Ring[JLong] = JLongRing implicit val jlongRing : Ring[JLong] = JLongRing
implicit val floatRing : Ring[Float] = FloatField implicit val floatRing : Ring[Float] = FloatField
implicit val jfloatRing : Ring[JFloat] = JFloatField implicit val jfloatRing : Ring[JFloat] = JFloatField
Expand Down
Expand Up @@ -18,7 +18,8 @@ package com.twitter.algebird
import java.lang.{Integer => JInt, Short => JShort, Long => JLong, Float => JFloat, Double => JDouble, Boolean => JBool} import java.lang.{Integer => JInt, Short => JShort, Long => JLong, Float => JFloat, Double => JDouble, Boolean => JBool}
import java.util.{List => JList, Map => JMap} import java.util.{List => JList, Map => JMap}


import scala.annotation.implicitNotFound import scala.collection.mutable.{Map => MMap}
import scala.annotation.{implicitNotFound, tailrec}


/** /**
* Semigroup: * Semigroup:
Expand Down Expand Up @@ -71,6 +72,57 @@ object Semigroup extends GeneratedSemigroupImplicits {


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)
* requires i > 0, wish we had PositiveBigInt as a class
*/
def intTimes[T](i: BigInt, v: T)(implicit sg: Semigroup[T]): T = {
require(i > 0, "Cannot do non-positive products with a Semigroup, try Monoid/Group.intTimes")
intTimesRec(i-1, v, 0, (v, Vector[T]()))
}

@tailrec
private def intTimesRec[T](i: BigInt, v: T, pow: Int, vaccMemo: (T,Vector[T]))(implicit sg: Semigroup[T]): T = {
if(i == 0) {
vaccMemo._1
}
else {
/* i2 = i % 2
* 2^pow(i*v) + acc == 2^(pow+1)((i/2)*v) + (acc + 2^pow i2 * v)
*/
val half = i / 2
val rem = i % 2
val newAccMemo = if (rem == 0) vaccMemo else {
val (res, newMemo) = timesPow2(pow, v, vaccMemo._2)
(sg.plus(vaccMemo._1, res), newMemo)
}
intTimesRec(half, v, pow + 1, newAccMemo)
}
}

// Returns (2^power) * v = (2^(power - 1) v + 2^(power - 1) v)
private def timesPow2[T](power: Int, v: T, memo: Vector[T])(implicit sg: Semigroup[T]): (T, Vector[T]) = {
val size = memo.size
require(power >= 0, "power cannot be negative")
if(power == 0) {
(v, memo)
}
else if (power <= size) {
(memo(power-1), memo)
}
else {
var item = if(size == 0) v else memo.last
var pow = size
var newMemo = memo
while(pow < power) {
// x = 2*x
item = sg.plus(item, item)
pow += 1
newMemo = newMemo :+ item
}
(item, newMemo)
}
}

implicit val nullSemigroup : Semigroup[Null] = NullGroup implicit val nullSemigroup : Semigroup[Null] = NullGroup
implicit val unitSemigroup : Semigroup[Unit] = UnitGroup implicit val unitSemigroup : Semigroup[Unit] = UnitGroup
implicit val boolSemigroup : Semigroup[Boolean] = BooleanField implicit val boolSemigroup : Semigroup[Boolean] = BooleanField
Expand All @@ -80,6 +132,7 @@ object Semigroup extends GeneratedSemigroupImplicits {
implicit val shortSemigroup : Semigroup[Short] = ShortRing implicit val shortSemigroup : Semigroup[Short] = ShortRing
implicit val jshortSemigroup : Semigroup[JShort] = JShortRing implicit val jshortSemigroup : Semigroup[JShort] = JShortRing
implicit val longSemigroup : Semigroup[Long] = LongRing implicit val longSemigroup : Semigroup[Long] = LongRing
implicit val bigIntSemigroup : Semigroup[BigInt] = BigIntRing
implicit val jlongSemigroup : Semigroup[JLong] = JLongRing implicit val jlongSemigroup : Semigroup[JLong] = JLongRing
implicit val floatSemigroup : Semigroup[Float] = FloatField implicit val floatSemigroup : Semigroup[Float] = FloatField
implicit val jfloatSemigroup : Semigroup[JFloat] = JFloatField implicit val jfloatSemigroup : Semigroup[JFloat] = JFloatField
Expand Down
Expand Up @@ -18,7 +18,7 @@ package com.twitter.algebird


import org.scalacheck.{ Arbitrary, Properties } import org.scalacheck.{ Arbitrary, Properties }
import org.scalacheck.Prop.forAll import org.scalacheck.Prop.forAll

import scala.math.Equiv
/** /**
* Base properties useful for all tests using Algebird's typeclasses. * Base properties useful for all tests using Algebird's typeclasses.
*/ */
Expand All @@ -43,6 +43,17 @@ object BaseProperties {
isAssociativeEq[T](eqfn) && isCommutativeEq[T](eqfn) isAssociativeEq[T](eqfn) && isCommutativeEq[T](eqfn)
def commutativeSemigroupLaws[T : Semigroup : Arbitrary] = commutativeSemigroupLawsEq[T](defaultEq _) def commutativeSemigroupLaws[T : Semigroup : Arbitrary] = commutativeSemigroupLawsEq[T](defaultEq _)


def isNonZeroWorksMonoid[T:Monoid:Arbitrary:Equiv] = forAll { (a: T, b: T) =>
val aIsLikeZero = Equiv[T].equiv(Monoid.plus(a,b), b)
Monoid.isNonZero(a) || aIsLikeZero
}

def isNonZeroWorksRing[T:Ring:Arbitrary] = forAll { (a: T, b: T) =>
implicit val monT: Monoid[T] = implicitly[Ring[T]]
val prodZero = !monT.isNonZero(Ring.times(a,b))
(Monoid.isNonZero(a) && Monoid.isNonZero(b)) || prodZero
}

def weakZero[T : Monoid : Arbitrary] = forAll { (a : T) => def weakZero[T : Monoid : Arbitrary] = forAll { (a : T) =>
val mon = implicitly[Monoid[T]] val mon = implicitly[Monoid[T]]
val zero = mon.zero val zero = mon.zero
Expand All @@ -57,7 +68,7 @@ object BaseProperties {
} }
def validZero[T : Monoid : Arbitrary] = validZeroEq[T](defaultEq _) def validZero[T : Monoid : Arbitrary] = validZeroEq[T](defaultEq _)


def monoidLaws[T : Monoid : Arbitrary] = validZero[T] && isAssociative[T] def monoidLaws[T : Monoid : Arbitrary] = validZero[T] && isAssociative[T] && isNonZeroWorksMonoid[T]
def monoidLawsEq[T : Monoid : Arbitrary](eqfn : (T,T) => Boolean) = def monoidLawsEq[T : Monoid : Arbitrary](eqfn : (T,T) => Boolean) =
validZeroEq[T](eqfn) && isAssociativeEq[T](eqfn) validZeroEq[T](eqfn) && isAssociativeEq[T](eqfn)
def commutativeMonoidLawsEq[T : Monoid : Arbitrary](eqfn : (T,T) => Boolean) = def commutativeMonoidLawsEq[T : Monoid : Arbitrary](eqfn : (T,T) => Boolean) =
Expand Down Expand Up @@ -94,12 +105,14 @@ object BaseProperties {
rng.times(a, rng.times(b,c)) == rng.times(rng.times(a,b),c) rng.times(a, rng.times(b,c)) == rng.times(rng.times(a,b),c)
} }
def pseudoRingLaws[T:Ring:Arbitrary] = def pseudoRingLaws[T:Ring:Arbitrary] =
isDistributive[T] && timesIsAssociative[T] && groupLaws[T] && isCommutative[T] isDistributive[T] && timesIsAssociative[T] && groupLaws[T] && isCommutative[T] &&
isNonZeroWorksRing[T]


def semiringLaws[T:Ring:Arbitrary] = def semiringLaws[T:Ring:Arbitrary] =
isDistributive[T] && timesIsAssociative[T] && isDistributive[T] && timesIsAssociative[T] &&
validOne[T] && commutativeMonoidLaws[T] && validOne[T] && commutativeMonoidLaws[T] &&
zeroAnnihilates[T] zeroAnnihilates[T] &&
isNonZeroWorksRing[T]


def ringLaws[T : Ring : Arbitrary] = validOne[T] && pseudoRingLaws[T] def ringLaws[T : Ring : Arbitrary] = validOne[T] && pseudoRingLaws[T]


Expand Down
Expand Up @@ -18,6 +18,7 @@ package com.twitter.algebird


import org.scalacheck.Arbitrary import org.scalacheck.Arbitrary
import org.scalacheck.Arbitrary.arbitrary import org.scalacheck.Arbitrary.arbitrary
import org.scalacheck.Prop.forAll
import org.scalacheck.Properties import org.scalacheck.Properties
import org.scalacheck.Gen.choose import org.scalacheck.Gen.choose


Expand All @@ -27,7 +28,10 @@ object AdjoinedRingSpecification extends Properties("AdjoinedRing") {
implicit def adjoined[T:Arbitrary]: Arbitrary[AdjoinedUnit[T]] = Arbitrary { implicit def adjoined[T:Arbitrary]: Arbitrary[AdjoinedUnit[T]] = Arbitrary {
implicitly[Arbitrary[T]].arbitrary.map { t => AdjoinedUnit(t) } implicitly[Arbitrary[T]].arbitrary.map { t => AdjoinedUnit(t) }
} }

// AdjoinedUnit requires this method to be correct, so it is tested here:
property("intTimes works correctly") = forAll { (bi0: BigInt, bi1: BigInt) =>
Group.intTimes(bi0, bi1) == (bi0 * bi1)
}
property("AdjoinedUnit[Int] is a Ring") = ringLaws[AdjoinedUnit[Int]] property("AdjoinedUnit[Int] is a Ring") = ringLaws[AdjoinedUnit[Int]]
property("AdjoinedUnit[Long] is a Ring") = ringLaws[AdjoinedUnit[Long]] property("AdjoinedUnit[Long] is a Ring") = ringLaws[AdjoinedUnit[Long]]
} }