├── project ├── build.properties └── plugins.sbt ├── core └── src │ ├── main │ └── scala │ │ └── kronecker │ │ ├── package.scala │ │ ├── instances │ │ ├── function.scala │ │ ├── option.scala │ │ ├── map.scala │ │ ├── float.scala │ │ ├── double.scala │ │ ├── set.scala │ │ ├── rational.scala │ │ ├── either.scala │ │ ├── hlist.scala │ │ ├── coproduct.scala │ │ └── list.scala │ │ ├── TableFunction.scala │ │ ├── Predicate.scala │ │ ├── Testing.scala │ │ ├── Bounded.scala │ │ ├── Intervals.scala │ │ ├── Permutation.scala │ │ ├── Coding.scala │ │ ├── Card.scala │ │ ├── Diagonal.scala │ │ └── Countable.scala │ └── test │ └── scala │ └── kronecker │ ├── GenericTests.scala │ ├── CountableTests.scala │ ├── CardLaws.scala │ ├── Testing.scala │ ├── PermutationTests.scala │ ├── IntervalTests.scala │ ├── DiagonalTests.scala │ └── CountableLaws.scala ├── .gitignore ├── refined └── src │ ├── test │ └── scala │ │ └── kronecker │ │ └── refined │ │ └── DemoTest.scala │ └── main │ └── scala │ └── kronecker │ └── refined │ ├── implicits.scala │ └── Predicate.scala └── README.md /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.4.2 2 | -------------------------------------------------------------------------------- /core/src/main/scala/kronecker/package.scala: -------------------------------------------------------------------------------- 1 | package object kronecker { 2 | type Z = spire.math.SafeLong 3 | val Z = spire.math.SafeLong 4 | } 5 | -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.3.1") 2 | addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.6.1") 3 | addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.0.0") 4 | -------------------------------------------------------------------------------- /core/src/main/scala/kronecker/instances/function.scala: -------------------------------------------------------------------------------- 1 | package kronecker 2 | package instances 3 | 4 | object CFunction1 { 5 | def apply[A, B](eva: Indexable[A], evb: Countable[B]): Countable[A => B] = { 6 | lazy val zero: B = evb.get(0).get 7 | val cm = CMap(eva, evb.drop(1)) 8 | cm.translate(m => TableFunction(m, zero)) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | project/boot 2 | target 3 | .ensime 4 | .ensime_lucene 5 | .ensime_cache 6 | TAGS 7 | \#*# 8 | *~ 9 | .#* 10 | .lib 11 | .history 12 | .*.swp 13 | .idea 14 | .idea/* 15 | .idea_modules 16 | .DS_Store 17 | .sbtrc 18 | *.sublime-project 19 | *.sublime-workspace 20 | tests.iml 21 | # Auto-copied by sbt-microsites 22 | docs/src/main/tut/contributing.md 23 | docs/src/main/tut/index.md 24 | -------------------------------------------------------------------------------- /core/src/main/scala/kronecker/TableFunction.scala: -------------------------------------------------------------------------------- 1 | package kronecker 2 | 3 | /** 4 | * Table-based Function1 definition. 5 | * 6 | * The `default` value MUST NOT appear in `table`, or else countable 7 | * instances using this will be broken. 8 | * 9 | * Instances of this class have stable equality/hashCode based on the 10 | * table and default value. 11 | */ 12 | final case class TableFunction[A, B](table: Map[A, B], default: B) extends Function1[A, B] { 13 | def apply(a: A): B = table.getOrElse(a, default) 14 | } 15 | -------------------------------------------------------------------------------- /core/src/main/scala/kronecker/instances/option.scala: -------------------------------------------------------------------------------- 1 | package kronecker 2 | package instances 3 | 4 | // None, Some(0), Some(1), ... 5 | class COption[A](ev: Countable[A]) extends Countable[Option[A]] { 6 | def cardinality: Card = ev.cardinality + Card.one 7 | def get(index: Z): Option[Option[A]] = 8 | if (index == 0) Some(None) else ev.get(index - 1).map(Some(_)) 9 | } 10 | 11 | class NOption[A](ev: Indexable[A]) extends COption(ev) with Indexable[Option[A]] { 12 | def index(o: Option[A]): Z = 13 | o match { 14 | case None => Z.zero 15 | case Some(a) => ev.index(a) + Z.one 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /refined/src/test/scala/kronecker/refined/DemoTest.scala: -------------------------------------------------------------------------------- 1 | package kronecker 2 | 3 | import org.scalacheck.Properties 4 | import org.typelevel.claimant.Claim 5 | 6 | class DemoTest extends Properties("Demo") { 7 | 8 | property("demo") = { 9 | 10 | import eu.timepit.refined._ 11 | import eu.timepit.refined.api.Refined 12 | import eu.timepit.refined.{boolean => b} 13 | import eu.timepit.refined.{numeric => n} 14 | import spire.implicits._ 15 | import kronecker.refined.implicits._ 16 | val c = Countable[Refined[Int, b.And[n.Greater[W.`5`.T], n.Less[W.`20`.T]]]] 17 | 18 | val size = 20 - 5 - 1 19 | val p0 = Claim(c.cardinality == Card(size)) 20 | val ps = (0 until size).map(i => Claim(c.get(i).get.value == 6 + i)) 21 | ps.foldLeft(p0)(_ && _) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /core/src/test/scala/kronecker/GenericTests.scala: -------------------------------------------------------------------------------- 1 | package kronecker 2 | 3 | import org.scalacheck.Properties 4 | 5 | object GenericTests extends Properties("Generic") { 6 | 7 | sealed trait Qux 8 | object Qux { 9 | case object LQ extends Qux 10 | case object RQ extends Qux 11 | } 12 | 13 | sealed trait IEither[A, B] 14 | object IEither { 15 | case class ILeft[A, B](a: A) extends IEither[A, B] 16 | case class IRight[A, B](b: B) extends IEither[A, B] 17 | case class IBoth[A, B](a: A, b: B) extends IEither[A, B] 18 | } 19 | 20 | case class Pair[A](first: A, second: A) 21 | 22 | Indexable[Qux] 23 | Indexable[Set[Qux]] 24 | Indexable[List[Qux]] 25 | Indexable[Either[Qux, Qux]] 26 | Indexable[Pair[Z]] 27 | Indexable[Pair[Qux]] 28 | Indexable[Pair[Set[Qux]]] 29 | } 30 | -------------------------------------------------------------------------------- /core/src/test/scala/kronecker/CountableTests.scala: -------------------------------------------------------------------------------- 1 | package kronecker 2 | 3 | import org.scalacheck.Properties 4 | import org.scalacheck.Prop.forAll 5 | 6 | import Testing._ 7 | 8 | object FixedCountableTests extends Properties("FixedCountableTests") { 9 | 10 | property("oneOf(c) = c") = { 11 | val c0 = Countable[Z] 12 | val c1 = Countable.oneOf(c0) 13 | forAll { (index: Z) => 14 | c1.get(index) == c0.get(index) 15 | } 16 | } 17 | 18 | property("c.drop(k).get(i) = c.get(i + k)") = 19 | forAll { (k: Byte, indices: Set[Z]) => 20 | val c0 = Countable[Z] 21 | val n = k & 0xff 22 | val c1 = c0.drop(n) 23 | indices.forall(i => c1.get(i) == c0.get(i + n)) 24 | } 25 | 26 | property("iterator") = { 27 | val c = Countable[Z] 28 | c.iterator() 29 | .take(1000) 30 | .zipWithIndex 31 | .forall { case (n, i) => c.get(i) == Some(n) } 32 | } 33 | 34 | // property("iterator ~ toStream") = 35 | // forAll { (k: Short) => 36 | // val c = Countable[Z] 37 | // val n = k & 0xffff 38 | // c.iterator.take(n).toList == c.stream.take(n).toList 39 | // } 40 | } 41 | -------------------------------------------------------------------------------- /core/src/main/scala/kronecker/Predicate.scala: -------------------------------------------------------------------------------- 1 | // package kronecker 2 | // 3 | // import spire.algebra.Bool 4 | // import spire.implicits._ 5 | // 6 | // abstract class Predicate[-A] { lhs => 7 | // 8 | // def apply(a: A): Boolean 9 | // 10 | // def contramap[Z](f: Z => A): Predicate[Z] = 11 | // Predicate(z => lhs(f(z))) 12 | // } 13 | // 14 | // object Predicate { 15 | // 16 | // def apply[A](p: A => Boolean): Predicate[A] = 17 | // new Predicate[A] { 18 | // def apply(a: A): Boolean = p(a) 19 | // } 20 | // 21 | // implicit def booleanPredicate[A]: Bool[Predicate[A]] = 22 | // new Bool[Predicate[A]] { 23 | // def zero: Predicate[A] = False 24 | // def one: Predicate[A] = True 25 | // def and(x: Predicate[A], y: Predicate[A]): Predicate[A] = 26 | // Predicate(a => x(a) && y(a)) 27 | // def or(x: Predicate[A], y: Predicate[A]): Predicate[A] = 28 | // Predicate(a => x(a) || y(a)) 29 | // def complement(x: Predicate[A]): Predicate[A] = 30 | // Predicate(a => !x(a)) 31 | // } 32 | // 33 | // val True: Predicate[Any] = 34 | // Predicate(_ => true) 35 | // 36 | // val False: Predicate[Any] = 37 | // Predicate(_ => false) 38 | // } 39 | -------------------------------------------------------------------------------- /core/src/test/scala/kronecker/CardLaws.scala: -------------------------------------------------------------------------------- 1 | package kronecker 2 | 3 | import org.scalacheck.Properties 4 | import org.scalacheck.Prop.forAll 5 | 6 | import Testing._ 7 | import Card.{zero, one} 8 | 9 | class CardLaws extends Properties("Card") { 10 | 11 | property("x + 0 = 0 + x = x") = 12 | forAll { (x: Card) => 13 | ((x + zero) == x) && ((zero + x) == x) 14 | } 15 | 16 | property("(x + y) + z = x + (y + z)") = 17 | forAll { (x: Card, y: Card, z: Card) => 18 | ((x + y) + z) == (x + (y + z)) 19 | } 20 | 21 | property("x * 0 = 0 * x = 0") = 22 | forAll { (x: Card) => 23 | ((x * zero) == zero) && ((zero * x) == zero) 24 | } 25 | 26 | property("x * 1 = 1 * x = x") = 27 | forAll { (x: Card) => 28 | ((x * one) == x) && ((one * x) == x) 29 | } 30 | 31 | property("(x * y) * z = x * (y * z)") = 32 | forAll { (x: Card, y: Card, z: Card) => 33 | ((x * y) * z) == (x * (y * z)) 34 | } 35 | 36 | // NOTE: not currently testing distribution since we'll need to add 37 | // a bunch of cases to get it right for the free construction. 38 | 39 | property("(x ^ y) ^ z = x ^ (y * z)") = 40 | forAll { (x: Card, y: Card, z: Card) => 41 | ((x ** y) ** z) == (x ** (y * z)) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /refined/src/main/scala/kronecker/refined/implicits.scala: -------------------------------------------------------------------------------- 1 | package kronecker 2 | package refined 3 | 4 | import eu.timepit.refined.api.Refined 5 | import spire.algebra.Order 6 | 7 | object implicits { 8 | 9 | /** 10 | * Provides Countable instances for supported Refinement types. 11 | * 12 | * Currently the only predicates supported are those which slice 13 | * interval types into intervals, such as: 14 | * 15 | * import eu.timepit.refined._ 16 | * import eu.timepit.refined.api.Refined 17 | * import eu.timepit.refined.{boolean => b} 18 | * import eu.timepit.refined.{numeric => n} 19 | * Countable[Refined[Int, b.And[n.Greater[W.`5`.T], n.Less[W.`20`.T]]]] 20 | * 21 | * Adding more supported number types requires (at least) instances 22 | * for Bounded[A] and Order[A]. In particular, floating point types 23 | * are not likely to be easy to support using the current strategy. 24 | */ 25 | implicit def countableForRefined[A, R]( 26 | implicit p: Predicate[A, R], 27 | b: Bounded[A], 28 | o: Order[A] 29 | ): Countable[Refined[A, R]] = 30 | new Countable[Refined[A, R]] { 31 | val c = Intervals.CountableIntervalSeq(p.toIntervalSeq) 32 | def cardinality = 33 | c.cardinality 34 | def get(index: Z): Option[Refined[A, R]] = 35 | c.get(index).map(Refined.unsafeApply) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /core/src/test/scala/kronecker/Testing.scala: -------------------------------------------------------------------------------- 1 | package kronecker 2 | 3 | import org.scalacheck.{Arbitrary, Gen} 4 | import org.scalacheck.Arbitrary.arbitrary 5 | import spire.math.{Rational, SafeLong} 6 | 7 | object Testing { 8 | 9 | val genZ: Gen[Z] = 10 | arbitrary[BigInt].map(n => Z(n.abs)) 11 | 12 | val genFinite: Gen[Card] = 13 | genZ.map(Card(_)) 14 | 15 | val genInfinite: Gen[Card] = 16 | Gen.const(Card.infinite) 17 | 18 | val freqs: List[(Int, Gen[Card])] = 19 | (10 -> genFinite) :: (5 -> genInfinite) :: Nil 20 | 21 | def genCard(n: Int): Gen[Card] = 22 | if (n > 0) { 23 | val g = genCard(n - 1) 24 | val g1 = for { x <- g; y <- g } yield x + y 25 | val g2 = for { x <- g; y <- g } yield x * y 26 | val g3 = for { x <- g; y <- g } yield x ** y 27 | val pairs = (n, g1) :: (n, g2) :: (n, g3) :: freqs 28 | Gen.frequency(pairs: _*) 29 | } else { 30 | Gen.frequency(freqs: _*) 31 | } 32 | 33 | implicit val arbitraryZ: Arbitrary[Z] = 34 | Arbitrary(genZ) 35 | 36 | implicit val arbitraryRatinoal: Arbitrary[Rational] = { 37 | val g = Gen.choose(1L, Long.MaxValue).map(SafeLong(_)) 38 | Arbitrary(for { 39 | s <- Gen.frequency(15 -> 1L, 14 -> -1L, 1 -> 0L) 40 | nx <- g 41 | ny <- g 42 | dx <- g 43 | dy <- g 44 | } yield Rational(nx * ny, dx * dy) * s) 45 | } 46 | 47 | implicit val arbitraryCard: Arbitrary[Card] = 48 | Arbitrary(genCard(5)) 49 | } 50 | -------------------------------------------------------------------------------- /core/src/main/scala/kronecker/instances/map.scala: -------------------------------------------------------------------------------- 1 | package kronecker 2 | package instances 3 | 4 | import scala.annotation.tailrec 5 | 6 | object CMap { 7 | def apply[K, V](evk: Countable[K], evv: Countable[V]): Countable[Map[K, V]] = 8 | (evk.cardinality.value, evv.cardinality.value) match { 9 | case (_, Some(szv)) => 10 | val n = szv + 1 11 | new CMap(evk, evv, i => i /% n) 12 | case (_, None) => 13 | val k = 4 14 | val mask = (1 << k) - 1 15 | new CMap(evk, evv, i => Coding.read(i, mask, k)) 16 | } 17 | } 18 | 19 | class CMap[K, V](evk: Countable[K], evv: Countable[V], splitIndex: Z => (Z, Z)) extends Countable[Map[K, V]] { 20 | val evo: Countable[Option[V]] = new COption(evv) 21 | val cardinality: Card = evo.cardinality ** evk.cardinality 22 | 23 | def get(index: Z): Option[Map[K, V]] = { 24 | @tailrec def loop(index0: Z, keyIndex: Z, m0: Map[K, V]): Map[K, V] = 25 | if (index0.isZero) { 26 | m0 27 | } else if (evk.cardinality.isMax(keyIndex)) { 28 | evo.get(index0).get match { 29 | case Some(v) => m0.updated(evk.get(keyIndex).get, v) 30 | case None => m0 31 | } 32 | } else { 33 | val (index1, valIndex) = splitIndex(index0) 34 | loop(index1, keyIndex + 1, evo.get(valIndex).get match { 35 | case Some(v) => m0.updated(evk.get(keyIndex).get, v) 36 | case None => m0 37 | }) 38 | } 39 | if (cardinality.contains(index)) { 40 | Some(loop(index, Z.zero, Map.empty)) 41 | } else None 42 | } 43 | } 44 | 45 | // TODO Indexable[Map[K, V]] 46 | -------------------------------------------------------------------------------- /core/src/main/scala/kronecker/Testing.scala: -------------------------------------------------------------------------------- 1 | // package kronecker 2 | // 3 | // import scala.annotation.tailrec 4 | // 5 | // abstract class Strategy { self => 6 | // type State 7 | // def init: State 8 | // def next(st: State): (State, Z) 9 | // 10 | // def iterator: Iterator[Z] = 11 | // new Iterator[Z] { 12 | // var st0: State = self.init 13 | // def hasNext(): Boolean = true 14 | // def next(): Z = { 15 | // val (st1, index1) = self.next(st0) 16 | // st0 = st1 17 | // index1 18 | // } 19 | // } 20 | // } 21 | // 22 | // object Strategy { 23 | // 24 | // object Increment extends Strategy { 25 | // type State = Z 26 | // def init: Z = Z.zero 27 | // def next(st: Z): (Z, Z) = (st + 1, st) 28 | // } 29 | // 30 | // object Fibonacci extends Strategy { 31 | // type State = (Z, Z) 32 | // def init: (Z, Z) = (Z.zero, Z.one) 33 | // def next(st: (Z, Z)): ((Z, Z), Z) = { 34 | // val (x, y) = st 35 | // ((y, x + y), x) 36 | // } 37 | // } 38 | // } 39 | // 40 | // object Testing { 41 | // 42 | // sealed trait Result 43 | // case class Success(indices: List[Z]) extends Result 44 | // case class Failure(failure: Z) extends Result 45 | // 46 | // def test[A](c: Countable[A], p: Predicate[A], s: Strategy, n: Int): Result = { 47 | // @tailrec def loop(tries: Int, st0: s.State, passed: List[Z]): Result = 48 | // if (tries >= n) { 49 | // Success(passed) 50 | // } else { 51 | // val (st1, i) = s.next(st0) 52 | // c.get(i) match { 53 | // case None => Success(passed) 54 | // case Some(a) if p(a) => loop(tries + 1, st1, i :: passed) 55 | // case Some(_) => Failure(i) 56 | // } 57 | // } 58 | // loop(0, s.init, Nil) 59 | // } 60 | // } 61 | -------------------------------------------------------------------------------- /core/src/main/scala/kronecker/instances/float.scala: -------------------------------------------------------------------------------- 1 | package kronecker 2 | package instances 3 | 4 | import java.lang.Float.{floatToRawIntBits, intBitsToFloat} 5 | 6 | object IndexableFloat extends Indexable[Float] { 7 | 8 | // reverse lower 23 bits 9 | private def reverseBits23(n: Int): Int = { 10 | var input = n 11 | var output = 0 12 | var i = 0 13 | while (i < 23) { 14 | if ((input & 1) != 0) output |= (1 << (22 - i)) 15 | input = input >>> 1 16 | i += 1 17 | } 18 | output 19 | } 20 | 21 | val cardinality: Card = 22 | Card(2) ** Card(32) 23 | 24 | private def exponent(i: Int): Int = 25 | if ((i & 1) == 0) 127 - (i / 2) else 128 + (i / 2) 26 | 27 | private def mantissa(i: Int): Int = 28 | reverseBits23(i) 29 | 30 | // the normal layout for 32-bit floats is: 31 | // 32 | // seeeeeee emmmmmmm mmmmmmmm mmmmmmmm 33 | // 34 | // the encoding we use from index to float is: 35 | // 36 | // mmmmmmmm mmmmmmmm mmmmmmme eeeeeees 37 | // 38 | // for the (m)antissa, (e)exponent, and (s)sign 39 | // 40 | // we reverse the mantissa (so most signficant bits are lowest), and 41 | // translate the exponent so that 127 comes first and then we 42 | // alternate up and down from there (i.e. 127, 128, 126, 129, ...) 43 | 44 | def get(index: Z): Option[Float] = 45 | if (cardinality.contains(index)) { 46 | val i = index.toInt 47 | val s = i & 1 // 1 bit 48 | val e = exponent((i >>> 1) & 0xff) // 8 bits 49 | val m = mantissa((i >>> 9) & 0x7fffff) // 23 bits 50 | val bits = (s << 31) | (e << 23) | m 51 | Some(intBitsToFloat(bits)) 52 | } else { 53 | None 54 | } 55 | 56 | def index(x: Float): Z = { 57 | val bits = floatToRawIntBits(x) 58 | val s = (bits >>> 31) & 0x1 // 1 bit 59 | val e = (bits >>> 23) & 0xff // 8 bits 60 | val m = bits & 0x7fffff // 23 bits 61 | val ee = if (e <= 127) (127 - e) * 2 else (e - 128) * 2 + 1 62 | val mm = reverseBits23(m) 63 | (Z(mm) << 9) | (Z(ee) << 1) | Z(s) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /refined/src/main/scala/kronecker/refined/Predicate.scala: -------------------------------------------------------------------------------- 1 | package kronecker 2 | package refined 3 | 4 | import eu.timepit.refined.{boolean => b} 5 | import eu.timepit.refined.{generic => g} 6 | import eu.timepit.refined.{numeric => n} 7 | 8 | import spire.algebra.Order 9 | import spire.math.extras.interval.IntervalSeq 10 | 11 | /** 12 | * 13 | */ 14 | sealed trait Predicate[T, P] { 15 | def toIntervalSeq(implicit ev: Order[T]): IntervalSeq[T] = { 16 | def recurse(r: Predicate[T, _]): IntervalSeq[T] = 17 | r match { 18 | case Predicate.Equal(wn) => IntervalSeq.point(wn.snd) 19 | case Predicate.Less(wn) => IntervalSeq.below(wn.snd) 20 | case Predicate.Greater(wn) => IntervalSeq.above(wn.snd) 21 | case Predicate.Not(r) => ~recurse(r) 22 | case Predicate.And(rp, rq) => recurse(rq) & recurse(rp) 23 | case Predicate.Or(rp, rq) => recurse(rq) | recurse(rp) 24 | } 25 | recurse(this) 26 | } 27 | } 28 | 29 | object Predicate { 30 | 31 | import eu.timepit.refined.internal.{WitnessAs => W} 32 | 33 | case class Equal[T, N](wn: W[N, T]) extends Predicate[T, g.Equal[N]] 34 | case class Less[T, N](wn: W[N, T]) extends Predicate[T, n.Less[N]] 35 | case class Greater[T, N](wn: W[N, T]) extends Predicate[T, n.Greater[N]] 36 | case class Not[T, P](r: Predicate[T, P]) extends Predicate[T, b.Not[P]] 37 | case class And[T, P, Q](rp: Predicate[T, P], rq: Predicate[T, Q]) extends Predicate[T, b.And[P, Q]] 38 | case class Or[T, P, Q](rp: Predicate[T, P], rq: Predicate[T, Q]) extends Predicate[T, b.Or[P, Q]] 39 | 40 | implicit def forLess[T, N](implicit wn: W[N, T]): Predicate[T, n.Less[N]] = 41 | Less(wn) 42 | 43 | implicit def forGreater[T, N](implicit wn: W[N, T]): Predicate[T, n.Greater[N]] = 44 | Greater(wn) 45 | 46 | implicit def forNot[T, P](implicit r: Predicate[T, P]): Predicate[T, b.Not[P]] = 47 | Not(r) 48 | 49 | implicit def forAnd[T, P, Q](implicit rp: Predicate[T, P], rq: Predicate[T, Q]): Predicate[T, b.And[P, Q]] = 50 | And(rp, rq) 51 | 52 | implicit def forOr[T, P, Q](implicit rp: Predicate[T, P], rq: Predicate[T, Q]): Predicate[T, b.Or[P, Q]] = 53 | Or(rp, rq) 54 | } 55 | -------------------------------------------------------------------------------- /core/src/main/scala/kronecker/instances/double.scala: -------------------------------------------------------------------------------- 1 | package kronecker 2 | package instances 3 | 4 | import java.lang.Double.{doubleToRawLongBits, longBitsToDouble} 5 | 6 | object IndexableDouble extends Indexable[Double] { 7 | 8 | // reverse lower 52 bits 9 | private def reverseBits52(n: Long): Long = { 10 | var input = n 11 | var output = 0L 12 | var i = 0 13 | while (i < 52) { 14 | if ((input & 1) != 0) output |= (1L << (51 - i)) 15 | input = input >>> 1 16 | i += 1 17 | } 18 | output 19 | } 20 | 21 | val cardinality: Card = 22 | Card(2) ** Card(64) 23 | 24 | private def exponent(i: Long): Long = 25 | if ((i & 1) == 0L) 1023L - (i / 2) else 1024L + (i / 2) 26 | 27 | private def mantissa(i: Long): Long = 28 | reverseBits52(i) 29 | 30 | // the normal layout for 64-bit floats is: 31 | // 32 | // seeeeeee eeeemmmm mmmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm 33 | // 34 | // the encoding we use from index to float is: 35 | // 36 | // mmmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm mmmmeeee eeeeeees 37 | // 38 | // for the (m)antissa, (e)exponent, and (s)sign 39 | // 40 | // we reverse the mantissa (so most signficant bits are lowest), and 41 | // translate the exponent so that 1023 comes first and then we 42 | // alternate up and down from there (i.e. 1023, 1024, 1022, 1025, ...) 43 | 44 | def get(index: Z): Option[Double] = 45 | if (cardinality.contains(index)) { 46 | val i = index.toLong 47 | val s = i & 1L // 1 bit 48 | val e = exponent((i >>> 1) & 0x7ffL) // 11 bits 49 | val m = mantissa((i >>> 12) & 0xfffffffffffffL) // 52 bits 50 | val bits = (s << 63) | (e << 52) | m 51 | Some(longBitsToDouble(bits)) 52 | } else { 53 | None 54 | } 55 | 56 | def index(x: Double): Z = { 57 | val bits = doubleToRawLongBits(x) 58 | val s = (bits >>> 63) & 0x1L // 1 bit 59 | val e = (bits >>> 52) & 0x7ffL // 11 bits 60 | val m = bits & 0xfffffffffffffL // 52 bits 61 | val ee = if (e <= 1023L) (1023L - e) * 2L else (e - 1024L) * 2L + 1L 62 | val mm = reverseBits52(m) 63 | (Z(mm) << 12) | (Z(ee) << 1) | Z(s) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /core/src/test/scala/kronecker/PermutationTests.scala: -------------------------------------------------------------------------------- 1 | package kronecker 2 | 3 | import org.scalacheck.{Gen, Properties} 4 | import org.scalacheck.Prop.forAll 5 | 6 | object PermutationTests extends Properties("Diagonal") { 7 | 8 | // size of the set we will be permutating 9 | val n = 100 10 | 11 | val num: Gen[Int] = Gen.choose(1, n) 12 | val pair: Gen[(Int, Int)] = Gen.zip(num, num) 13 | val pairs: Gen[List[(Int, Int)]] = Gen.listOf(pair) 14 | 15 | val p0: Permutation[Int] = Permutation.identity[Int] 16 | 17 | val perm: Gen[Permutation[Int]] = 18 | pairs.map(_.foldLeft(p0) { case (p, (x, y)) => p.swap(x, y) }) 19 | 20 | def isValid(p: Permutation[Int]): Boolean = 21 | (1 to n).map(p(_)).toSet.size == n 22 | 23 | property("valid permutation") = 24 | forAll(perm)(isValid) 25 | 26 | property("(x = y) = (p(x) = p(y))") = 27 | forAll(perm, num, num) { (p, x, y) => 28 | (x == y) == (p(x) == p(y)) 29 | } 30 | 31 | property("p.swap(x, y) = p andThen Permutation(x, y)") = 32 | forAll(perm, num, num) { (p, x, y) => 33 | p.swap(x, y) == (p andThen Permutation(x, y)) 34 | } 35 | 36 | property("p0 andThen p1 is valid") = 37 | forAll(perm, perm) { (p0, p1) => 38 | isValid(p0 andThen p1) 39 | } 40 | 41 | property("p.reverse.reverse = p") = 42 | forAll(perm) { (p) => 43 | p.reverse.reverse == p 44 | } 45 | 46 | property("(p0 andThen p1).reverse = (p1.reverse andThen p0.reverse)") = 47 | forAll(perm, perm) { (p0, p1) => 48 | val x = (p0 andThen p1).reverse 49 | val y = p1.reverse andThen p0.reverse 50 | x == y 51 | } 52 | 53 | property("(p0 andThen p1) = (p1 compose p0)") = 54 | forAll(perm, perm) { (p0, p1) => 55 | (p0 andThen p1) == (p1 compose p0) 56 | } 57 | 58 | property("p andThen p.reverse = identity") = 59 | forAll(perm) { p => 60 | val p1 = (p andThen p.reverse) 61 | if (p1 != p0) println((p1, p0)) 62 | p1 == p0 63 | } 64 | 65 | property("(p0 = p1) = (forall x: p0(x) = p1(x))") = 66 | forAll(perm, perm) { (p0, p1) => 67 | val a = p0 == p1 68 | val b = (1 to 100).forall(x => p0(x) == p1(x)) 69 | a == b 70 | } 71 | 72 | property("p.swap(x, y).swap(x, y) = p") = 73 | forAll(perm, num, num) { (p, x, y) => 74 | p.swap(x, y).swap(x, y) == p 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /core/src/main/scala/kronecker/instances/set.scala: -------------------------------------------------------------------------------- 1 | package kronecker 2 | package instances 3 | 4 | import scala.annotation.tailrec 5 | 6 | object CSet { 7 | def apply[A](ev: Countable[A]): Countable[Set[A]] = 8 | ev.cardinality.value match { 9 | case Some(sz) => new CFSet(ev, sz) 10 | case None => new CISet(ev) 11 | } 12 | 13 | // Set(), Set(0), Set(1), Set(0, 1), Set(2), Set(0, 2), Set(1, 2), 14 | // Set(0, 1, 2), Set(3), ... 15 | class CFSet[A](ev: Countable[A], sz: Z) extends Countable[Set[A]] { 16 | val cardinality: Card = Card.two ** ev.cardinality 17 | def get(index: Z): Option[Set[A]] = { 18 | @tailrec def loop(rem: Z, index: Z, s0: Set[A]): Set[A] = 19 | if (rem.isZero || index >= sz) s0 20 | else { 21 | val s1 = if (rem.isOdd) s0 + ev.get(index).get else s0 22 | loop(rem >> 1, index + 1, s1) 23 | } 24 | if (cardinality.contains(index)) { 25 | Some(loop(index, Z.zero, Set.empty)) 26 | } else { 27 | None 28 | } 29 | } 30 | } 31 | 32 | // Set(), Set(0), Set(1), Set(0, 1), Set(2), Set(0, 2), Set(1, 2), 33 | // Set(0, 1, 2), Set(3), ... 34 | class CISet[A](ev: Countable[A]) extends Countable[Set[A]] { 35 | val cardinality: Card = Card.two ** ev.cardinality 36 | def get(index: Z): Option[Set[A]] = { 37 | @tailrec def loop(rem: Z, index: Z, s0: Set[A]): Set[A] = 38 | if (rem.isZero) s0 39 | else if (rem.isOdd) loop(rem >> 1, index + 1, s0 + ev.get(index).get) 40 | else loop(rem >> 1, index + 1, s0) 41 | Some(loop(index, Z.zero, Set.empty)) 42 | } 43 | } 44 | } 45 | 46 | object NSet { 47 | def apply[A](ev: Indexable[A]): Indexable[Set[A]] = 48 | ev.cardinality.value match { 49 | case Some(sz) => new NFSet(ev, sz) 50 | case None => new NISet(ev) 51 | } 52 | 53 | @tailrec def leftShift(n: Z, k: Z): Z = 54 | if (k.isValidInt) n << k.toInt 55 | else leftShift(n << Int.MaxValue, k - Int.MaxValue) 56 | 57 | class NFSet[A](ev: Indexable[A], sz: Z) extends CSet.CFSet(ev, sz) with Indexable[Set[A]] { 58 | def index(set: Set[A]): Z = 59 | set.foldLeft(Z.zero)((n, a) => n | NSet.leftShift(Z.one, ev.index(a))) 60 | } 61 | 62 | class NISet[A](ev: Indexable[A]) extends CSet.CISet(ev) with Indexable[Set[A]] { 63 | def index(set: Set[A]): Z = 64 | set.foldLeft(Z.zero)((n, a) => n | NSet.leftShift(Z.one, ev.index(a))) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /core/src/main/scala/kronecker/instances/rational.scala: -------------------------------------------------------------------------------- 1 | package kronecker 2 | package instances 3 | 4 | import spire.math.Rational 5 | 6 | /* 7 | */ 8 | 9 | object IndexableRational extends Indexable[Rational] { 10 | 11 | def cardinality: Card = 12 | Card.Infinite 13 | 14 | 15 | def get(index: Z): Option[Rational] = { 16 | 17 | // given a 1-indexed position in the sequence, return the 18 | // corresponding rational. 19 | // 20 | // sequence is: 1, 1/2, 2, 1/3, 3/2, 2/3, 3, 1/4, ... 21 | // 22 | // inverse of index/find. 23 | def find(k: Z): Rational = { 24 | 25 | // most significant byte is first 26 | val bytes: Array[Byte] = k.toBigInt.bigInteger.toByteArray 27 | 28 | // find the starting 1 digit and skip it 29 | var i = if (bytes(0) == 0) 1 else 0 30 | var mask = 128 31 | while (mask > 0 && (bytes(i) & mask) == 0) mask >>= 1 32 | mask >>= 1 33 | 34 | // now do the algorithm, i.e. keep branching smaller (on 0) or 35 | // bigger (on 1) until we run out of binary digits. 36 | var a = Z.one 37 | var b = Z.one 38 | while (i < bytes.length) { 39 | val byte = bytes(i) 40 | while (mask > 0) { 41 | if ((byte & mask) == 0) { 42 | b = a + b 43 | } else { 44 | a = a + b 45 | } 46 | mask >>= 1 47 | } 48 | mask = 128 49 | i += 1 50 | } 51 | Rational(a, b) 52 | } 53 | 54 | // 0 is a sentinel, then odd indices are positive rationals, and 55 | // even indices are negative rationals. 56 | if (index.isZero) Some(Rational.zero) 57 | else if ((index & 1) == 1) Some(find((index + 1) / 2)) 58 | else Some(-find(index / 2)) 59 | } 60 | 61 | def index(r: Rational): Z = { 62 | 63 | // inverse of get/find, only handles positive rationals, returns 64 | // 1-indexed position in the sequence. 65 | def find(n: Z, d: Z): Z = { 66 | var a = n 67 | var b = d 68 | var result = Z.zero 69 | var mask = Z.one 70 | while (!(a.isOne && b.isOne)) { 71 | if (a > b) { 72 | result += mask 73 | a -= b 74 | } else { 75 | b -= a 76 | } 77 | mask = mask << 1 78 | } 79 | result + mask 80 | } 81 | 82 | if (r.isZero) Z.zero 83 | else if (r.signum > 0) find(r.numerator, r.denominator) * 2 - 1 84 | else find(-r.numerator, r.denominator) * 2 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /core/src/test/scala/kronecker/IntervalTests.scala: -------------------------------------------------------------------------------- 1 | package kronecker 2 | 3 | import cats.kernel.Eq 4 | import spire.math.Interval 5 | import spire.math.extras.interval.IntervalSeq 6 | 7 | import kronecker.Intervals.{CountableInterval, CountableIntervalSeq} 8 | 9 | object IntervalTests { 10 | case class A(value: Z) 11 | case class B(value: Z) 12 | case class C(value: Z) 13 | case class D(value: Z) 14 | case class E(value: Z) 15 | case class F(value: Z) 16 | case class G(value: Z) 17 | case class H(value: Z) 18 | case class I(value: Z) 19 | case class J(value: Z) 20 | case class K(value: Z) 21 | case class L(value: Z) 22 | 23 | lazy val iva = Interval.closed(Z(-100), Z(100)) 24 | lazy val ivb = Interval.open(Z(300), Z(400)) 25 | lazy val ivc = Interval.openLower(Z(-500), Z(-400)) 26 | lazy val ivd = Interval.below(Z(-1000)) 27 | lazy val ive = Interval.above(Z(1000)) 28 | lazy val ivf = Interval.all[Z] 29 | lazy val isg = IntervalSeq(iva) | IntervalSeq(ivb) | IntervalSeq(ivc) 30 | lazy val ish = isg | IntervalSeq(ivd) 31 | lazy val isi = ish | IntervalSeq(ive) 32 | lazy val isj = IntervalSeq.empty[Z] 33 | 34 | implicit lazy val ca: Countable[A] = CountableInterval(iva).translate(A(_)) 35 | implicit lazy val cb: Countable[B] = CountableInterval(ivb).translate(B(_)) 36 | implicit lazy val cc: Countable[C] = CountableInterval(ivc).translate(C(_)) 37 | implicit lazy val cd: Countable[D] = CountableInterval(ivd).translate(D(_)) 38 | implicit lazy val ce: Countable[E] = CountableInterval(ive).translate(E(_)) 39 | implicit lazy val cf: Countable[F] = CountableInterval(ivf).translate(F(_)) 40 | implicit lazy val cg: Countable[G] = CountableIntervalSeq(isg).translate(G(_)) 41 | implicit lazy val ch: Countable[H] = CountableIntervalSeq(ish).translate(H(_)) 42 | implicit lazy val ci: Countable[I] = CountableIntervalSeq(isi).translate(I(_)) 43 | implicit lazy val cj: Countable[J] = CountableIntervalSeq(isj).translate(J(_)) 44 | 45 | implicit def univEq[T]: Eq[T] = 46 | Eq.fromUniversalEquals[T] 47 | } 48 | 49 | import IntervalTests._ 50 | 51 | object CountableIntervalA extends CountableTests[A] 52 | object CountableIntervalB extends CountableTests[B] 53 | object CountableIntervalC extends CountableTests[C] 54 | object CountableIntervalD extends CountableTests[D] 55 | object CountableIntervalE extends CountableTests[E] 56 | //object CountableIntervalF extends CountableTests[F] 57 | object CountableIntervalG extends CountableTests[G] 58 | object CountableIntervalH extends CountableTests[H] 59 | object CountableIntervalI extends CountableTests[I] 60 | object CountableIntervalJ extends CountableTests[J] 61 | -------------------------------------------------------------------------------- /core/src/main/scala/kronecker/instances/either.scala: -------------------------------------------------------------------------------- 1 | package kronecker 2 | package instances 3 | 4 | object CEither { 5 | 6 | def rev[A, B]: Either[A, B] => Either[B, A] = 7 | _ match { 8 | case Left(a) => Right(a) 9 | case Right(b) => Left(b) 10 | } 11 | 12 | def apply[A, B](eva: Countable[A], evb: Countable[B]): Countable[Either[A, B]] = 13 | (eva.cardinality.value, evb.cardinality.value) match { 14 | case (Some(sz), _) => 15 | new CFEither(eva, sz, evb) 16 | case (None, Some(sz)) => 17 | new CFEither(evb, sz, eva).translate(rev) 18 | case (None, None) => 19 | new CIEither(eva, evb) 20 | } 21 | 22 | // Left(0), Left(1), ... Left(sz - 1), Right(0), Right(1), ... 23 | class CFEither[A, B](eva: Countable[A], sz: Z, evb: Countable[B]) extends Countable[Either[A, B]] { 24 | val cardinality = eva.cardinality + evb.cardinality 25 | def get(index: Z): Option[Either[A, B]] = 26 | if (index < sz) eva.get(index).map(Left(_)) 27 | else evb.get(index - sz).map(Right(_)) 28 | } 29 | 30 | // Left(0), Right(0), Left(1), Right(1), ... 31 | class CIEither[A, B](eva: Countable[A], evb: Countable[B]) extends Countable[Either[A, B]] { 32 | val cardinality = eva.cardinality + evb.cardinality 33 | def get(index: Z): Option[Either[A, B]] = 34 | if (index.isEven) eva.get(index >> 1).map(Left(_)) 35 | else evb.get(index >> 1).map(Right(_)) 36 | } 37 | } 38 | 39 | object NEither { 40 | 41 | import CEither.rev 42 | 43 | def apply[A, B](eva: Indexable[A], evb: Indexable[B]): Indexable[Either[A, B]] = 44 | (eva.cardinality.value, evb.cardinality.value) match { 45 | case (Some(sz), _) => 46 | new NFEither(eva, sz, evb) 47 | case (None, Some(sz)) => 48 | new NFEither(evb, sz, eva).imap(rev)(rev) 49 | case (None, None) => 50 | new NIEither(eva, evb) 51 | } 52 | 53 | // Left(0), Left(1), ..., Right(0), Right(1), ... 54 | class NFEither[A, B](eva: Indexable[A], sz: Z, evb: Indexable[B]) 55 | extends CEither.CFEither[A, B](eva, sz, evb) with Indexable[Either[A, B]] { 56 | def index(e: Either[A, B]): Z = 57 | e match { 58 | case Left(a) => eva.index(a) 59 | case Right(b) => evb.index(b) + sz 60 | } 61 | } 62 | 63 | // Left(0), Right(0), Left(1), Right(1), ... 64 | class NIEither[A, B](eva: Indexable[A],evb: Indexable[B]) 65 | extends CEither.CIEither[A, B](eva, evb) with Indexable[Either[A, B]] { 66 | def index(e: Either[A, B]): Z = 67 | e match { 68 | case Left(a) => eva.index(a) * 2 69 | case Right(b) => evb.index(b) * 2 + 1 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /core/src/main/scala/kronecker/Bounded.scala: -------------------------------------------------------------------------------- 1 | package kronecker 2 | 3 | import spire.math.SafeLong 4 | 5 | trait Bounded[A] { 6 | def countable: Countable[A] 7 | def upper: Option[A] 8 | def lower: Option[A] 9 | def next(a: A): A 10 | def prev(a: A): A 11 | def toZ(a: A): Z 12 | def offset(first: A, distance: Z): A 13 | } 14 | 15 | object Bounded { 16 | 17 | implicit def boundedForByte: Bounded[Byte] = 18 | new Bounded[Byte] { 19 | def countable: Countable[Byte] = Countable[Byte] 20 | def upper: Option[Byte] = Some(Byte.MaxValue) 21 | def lower: Option[Byte] = Some(Byte.MinValue) 22 | def next(n: Byte): Byte = (n + 1).toByte 23 | def prev(n: Byte): Byte = (n - 1).toByte 24 | def toZ(n: Byte): Z = Z(n) 25 | def offset(first: Byte, distance: Z): Byte = (first + distance.toInt).toByte 26 | } 27 | 28 | implicit def boundedForShort: Bounded[Short] = 29 | new Bounded[Short] { 30 | def countable: Countable[Short] = Countable[Short] 31 | def upper: Option[Short] = Some(Short.MaxValue) 32 | def lower: Option[Short] = Some(Short.MinValue) 33 | def next(n: Short): Short = (n + 1).toShort 34 | def prev(n: Short): Short = (n - 1).toShort 35 | def toZ(n: Short): Z = Z(n) 36 | def offset(first: Short, distance: Z): Short = (first + distance.toInt).toShort 37 | } 38 | 39 | implicit def boundedForInt: Bounded[Int] = 40 | new Bounded[Int] { 41 | def countable: Countable[Int] = Countable[Int] 42 | def upper: Option[Int] = Some(Int.MaxValue) 43 | def lower: Option[Int] = Some(Int.MinValue) 44 | def next(n: Int): Int = n + 1 45 | def prev(n: Int): Int = n - 1 46 | def toZ(n: Int): Z = Z(n) 47 | def offset(first: Int, distance: Z): Int = first + distance.toInt 48 | } 49 | 50 | implicit def boundedForLong: Bounded[Long] = 51 | new Bounded[Long] { 52 | def countable: Countable[Long] = Countable[Long] 53 | def upper: Option[Long] = Some(Long.MaxValue) 54 | def lower: Option[Long] = Some(Long.MinValue) 55 | def next(n: Long): Long = n + 1L 56 | def prev(n: Long): Long = n - 1L 57 | def toZ(n: Long): Z = Z(n) 58 | def offset(first: Long, distance: Z): Long = first + distance.toLong 59 | } 60 | 61 | implicit def boundedForSafeLong: Bounded[SafeLong] = 62 | new Bounded[SafeLong] { 63 | def countable: Countable[SafeLong] = Countable[SafeLong] 64 | def upper: Option[SafeLong] = None 65 | def lower: Option[SafeLong] = None 66 | def next(n: SafeLong): SafeLong = n + 1L 67 | def prev(n: SafeLong): SafeLong = n - 1L 68 | def toZ(n: SafeLong): Z = n 69 | def offset(first: SafeLong, distance: Z): SafeLong = first + distance 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /core/src/test/scala/kronecker/DiagonalTests.scala: -------------------------------------------------------------------------------- 1 | package kronecker 2 | 3 | import org.scalacheck.{Gen, Properties} 4 | import org.scalacheck.Prop.forAll 5 | import scala.annotation.tailrec 6 | 7 | object DiagonalTests extends Properties("Diagonal") { 8 | 9 | val tiny: Gen[Int] = Gen.choose(1, 5) 10 | val small: Gen[Int] = Gen.choose(1, 10) 11 | val medium: Gen[Int] = Gen.choose(1, 100) 12 | 13 | val prettyBig: Gen[Z] = Gen.choose(0, Int.MaxValue).map(Z(_)) 14 | val big: Gen[Z] = Gen.choose(0, Long.MaxValue).map(Z(_)) 15 | val veryBig: Gen[Z] = for { x <- big; y <- big } yield x * y 16 | 17 | property("widthAtDepth") = 18 | forAll(small, medium) { (dim: Int, depth0: Int) => 19 | val w0 = Diagonal.widthAtDepth(dim, depth0) 20 | val w1 = Diagonal.widthAtDepth(dim, depth0 + 1) 21 | val w2 = Diagonal.widthAtDepth(dim, depth0 + 2) 22 | 23 | if (dim == 1) { 24 | (w0 == 1) && (w1 == w0) && (w2 == w1) 25 | } else { 26 | (w0 > 0) && (w1 > w0) && (w2 > w1) 27 | } 28 | } 29 | 30 | property("decompose") = 31 | forAll(small, medium) { (dim: Int, index: Int) => 32 | val got = Diagonal.decompose(dim, Z(index)) 33 | val expected = oldDecompose(dim, Z(index)) 34 | if (got != expected) println((got, expected)) 35 | got == expected 36 | } 37 | 38 | case class Info(nextTerm: Z, num: Z, denom: Z) 39 | 40 | def infoAtDepth(dim: Int, depth: Z): Info = { 41 | @tailrec def loop(i: Int, term: Z, num: Z, denom: Z): Info = 42 | if (i <= 1) Info(term, num, denom) 43 | else loop(i - 1, term + 1, (depth + term) * num, denom * term) 44 | loop(dim, Z.one, Z.one, Z.one) 45 | } 46 | 47 | /** 48 | * Decompose an index into a position and depth. 49 | */ 50 | def oldDecompose(dim: Int, index: Z): (Z, Z) = 51 | if (dim == 1) { 52 | (Z.zero, index) 53 | } else if (dim == 2) { 54 | var i = index 55 | var depth = Z.zero 56 | var num = Z.one 57 | while (i >= num) { 58 | i -= num 59 | depth += 1 60 | num += 1 61 | } 62 | (i, depth) 63 | } else { 64 | val info = infoAtDepth(dim, 0) 65 | var i = index * info.denom 66 | var depth = Z.zero 67 | var num = info.num 68 | var oldestTerm = Z.one 69 | var nextTerm = info.nextTerm 70 | while (i >= num) { 71 | i -= num 72 | depth += 1 73 | num = (num * nextTerm) / oldestTerm 74 | oldestTerm += 1 75 | nextTerm += 1 76 | } 77 | 78 | (i / info.denom, depth) 79 | } 80 | 81 | val smallList: Gen[List[Z]] = 82 | small.flatMap(n => Gen.listOfN(n, medium)).map(_.map(n => Z(n))) 83 | 84 | property("atIndex(e.size, fromElem(e)) = e") = 85 | forAll(smallList) { e => 86 | val i = Diagonal.fromElem(e) 87 | Diagonal.atIndex(e.size, i) == e 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /core/src/main/scala/kronecker/Intervals.scala: -------------------------------------------------------------------------------- 1 | package kronecker 2 | 3 | import spire.math.{Interval, Searching} 4 | import spire.math.extras.interval.IntervalSeq 5 | 6 | object Intervals { 7 | 8 | case class AtOrAbove[A](x: A, b: Bounded[A]) extends Countable[A] { 9 | val cardinality: Card = b.upper match { 10 | case None => Card.infinite 11 | case Some(max) => Card(b.toZ(max) - b.toZ(x) + 1) 12 | } 13 | def get(index: Z): Option[A] = Some(b.offset(x, index)) 14 | } 15 | 16 | case class AtOrBelow[A](y: A, b: Bounded[A]) extends Countable[A] { 17 | val cardinality: Card = b.lower match { 18 | case None => Card.infinite 19 | case Some(min) => Card(b.toZ(y) - b.toZ(min) + 1) 20 | } 21 | def get(index: Z): Option[A] = Some(b.offset(y, -index)) 22 | } 23 | 24 | case class Within[A](first: A, last: A, b: Bounded[A]) extends Countable[A] { 25 | require(b.toZ(first) <= b.toZ(last)) 26 | val cardinality: Card = Card(b.toZ(last) - b.toZ(first) + 1) 27 | def get(index: Z): Option[A] = 28 | if (cardinality.contains(index)) Some(b.offset(first, index)) else None 29 | } 30 | 31 | object CountableInterval { 32 | def apply[A](iv: Interval[A])(implicit b: Bounded[A]): Countable[A] = 33 | if (iv.isEmpty) { 34 | Countable.empty[A] 35 | } else { 36 | import spire.math.interval.{Closed, Open, Unbound, EmptyBound} 37 | val ofirst = iv.lowerBound match { 38 | case Closed(x) => Some(x) 39 | case Open(x) => Some(b.next(x)) 40 | case Unbound() => None 41 | case EmptyBound() => sys.error("impossible!") 42 | } 43 | val olast = iv.upperBound match { 44 | case Closed(y) => Some(y) 45 | case Open(y) => Some(b.prev(y)) 46 | case Unbound() => None 47 | case EmptyBound() => sys.error("impossible!") 48 | } 49 | (ofirst, olast) match { 50 | case (Some(x), Some(y)) => Within(x, y, b) 51 | case (Some(x), None) => AtOrAbove(x, b) 52 | case (None, Some(y)) => AtOrBelow(y, b) 53 | case (None, None) => b.countable 54 | } 55 | } 56 | } 57 | 58 | class CountableIntervalSeq[A](size: Z, ivs: Array[Countable[A]], offsets: Array[Z]) extends Countable[A] { 59 | val cardinality: Card = 60 | Card(size) 61 | 62 | def get(index: Z): Option[A] = 63 | if (cardinality.contains(index)) { 64 | val i = Searching.search(offsets, index) 65 | val j = if (i >= 0) i else -(i + 1) - 1 66 | val c = ivs(j) 67 | val k = index - offsets(j) 68 | c.get(k) 69 | } else { 70 | None 71 | } 72 | } 73 | 74 | object CountableIntervalSeq { 75 | def apply[A](seq: IntervalSeq[A])(implicit b: Bounded[A]): Countable[A] = { 76 | val cs = seq.intervalIterator 77 | .filter(_.nonEmpty) 78 | .map(CountableInterval(_)) 79 | .toList 80 | Countable.oneOf(cs: _*) 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /core/src/main/scala/kronecker/Permutation.scala: -------------------------------------------------------------------------------- 1 | package kronecker 2 | 3 | /** 4 | * Permutation represents a rearrangement of ordered values. 5 | * 6 | * For example, given the list [a, b, c], and p = Permutation.empty, 7 | * here are the following permutations: 8 | * 9 | * - p [a, b, c] 10 | * - p.swap(a, b) [b, a, c] 11 | * - p.swap(a, c) [c, b, a] 12 | * - p.swap(a, c).swap(a, b) [b, c, a] 13 | * 14 | * Internally, permutation has the invariant that its remappings must 15 | * form a cycle; for any x0 one of the following must be true: 16 | * 17 | * - p(x0) = x0 18 | * - p(x0) = x1, p(x1) = x0 19 | * - p(x0) = x1, p(x1) = x2, p(x2) = x0 20 | * - and so on... 21 | * 22 | * The permutation preserves the elements in the underlying list, but 23 | * just changes their positions. Using a permutation to reorder a 24 | * list, it should be impossible to "lose" any values. 25 | */ 26 | case class Permutation[A] private (m: Map[A, A]) { 27 | 28 | /** 29 | * Apply the permutation to a particular apply. 30 | * 31 | * Apply is a bijection. 32 | */ 33 | def apply(a0: A): A = 34 | m.get(a0) match { 35 | case Some(a1) => a1 36 | case None => a0 37 | } 38 | 39 | /** 40 | * Compose two permutations. 41 | * 42 | * This is equivalent to treating permutations as functions and 43 | * combining their apply methods with `compose`. 44 | * 45 | * x compose y = y andThen x 46 | */ 47 | def compose(p: Permutation[A]): Permutation[A] = 48 | p andThen this 49 | 50 | /** 51 | * Compose two permutations. 52 | * 53 | * This is equivalent to treating permutations as functions and 54 | * combining their apply methods with `andThen`. 55 | * 56 | * x compose y = y andThen x 57 | */ 58 | def andThen(p: Permutation[A]): Permutation[A] = 59 | Permutation(p.m.foldLeft(m) { case (m0, (k, v)) => up(m0, k, this(v)) }) 60 | 61 | /** 62 | * Reverse a permutation. 63 | * 64 | * Reversing a permutation is equivalent to inverting its apply 65 | * method. 66 | */ 67 | def reverse: Permutation[A] = 68 | Permutation(m.map { case (a0, a1) => (a1, a0) }) 69 | 70 | /** 71 | * Product a new permutation while preserving the invariant that all 72 | * mappings must form a loop. 73 | */ 74 | def swap(x0: A, y0: A): Permutation[A] = 75 | if (x0 == y0) this 76 | else Permutation(up(up(m, x0, this(y0)), y0, this(x0))) 77 | 78 | private def up(m: Map[A, A], k: A, v: A): Map[A, A] = 79 | if (k == v) m - k 80 | else m.updated(k, v) 81 | } 82 | 83 | object Permutation { 84 | 85 | /** 86 | * Return the identity permutation. 87 | */ 88 | def identity[A]: Permutation[A] = 89 | Permutation(Map.empty) 90 | 91 | /** 92 | * Build a permutation that swaps exactly two values. 93 | * 94 | * The permutation is otherwise equivalent to the identity 95 | * function. 96 | */ 97 | def apply[A](a0: A, a1: A): Permutation[A] = 98 | identity[A].swap(a0, a1) 99 | } 100 | -------------------------------------------------------------------------------- /core/src/main/scala/kronecker/Coding.scala: -------------------------------------------------------------------------------- 1 | package kronecker 2 | 3 | import scala.annotation.tailrec 4 | 5 | /** 6 | * Provides codings for embedding maps in indices. 7 | * 8 | * The basic idea here is that in some cases we need to be able to 9 | * encode multiple logical indices into a single carrier index. Our 10 | * coding needs to be able to represent arbitrary lists of natural 11 | * numbers, without any "gaps" (lists of natural numbers that aren't 12 | * representable) or "duplicates" (lists of natural numbers with 13 | * multiple possible codings). 14 | * 15 | * CODING 16 | * 17 | * The strategy here is parameterized on a bit size k (where k > 1), 18 | * and the derived constant N = 2^k - 1. 19 | * 20 | * To code any logical number we first split it into digits base-N, 21 | * stored in k-bits. Each digit is then represented using k-bits, with 22 | * the adjustment that zero is coded with k 1-bits, instead of k 23 | * 0-bits. We do this because k 0-bits is used to mark the end of the 24 | * number. 25 | * 26 | * We also use carry rules to prevent duplicate representations 27 | * involving encoded zeros. If k 1-bits are seen, we set a carry flag 28 | * to true. If we reach the end of the number (k 0-bits) with the 29 | * carry flag set, we treat the final zero as a one. We will preserve 30 | * the carry flag as long as we see encoded zeros (k 1-bits) or ones 31 | * (just the one bit set). 32 | * 33 | * The digits are encoded in little-endian order, and the list 34 | * elements are also little-endian (i.e. the first logical index is 35 | * the smallest part of the carrier index). 36 | * 37 | * For lists with a maximum possible length, the final element is 38 | * treated as uncoded, to ensure we "consume" the remaining bits. 39 | * 40 | * CASE STUDY 41 | * 42 | * To support Countable[Map[K, V]] we need to be able to represent 43 | * each distinct map as a distinct index. We choose to do that by 44 | * representing any given map as a sequence of Option[V] values, one 45 | * value per possible key (using the enumeration from Countable[K] to 46 | * order the array). The maximum length of the sequence is the 47 | * cardinality of K. 48 | * 49 | * More specifically, every Map[Boolean, Z] value can be encoded as 50 | * two Option[Z] values (for the false key, then the true key), 51 | * which can then be represented as indices: 52 | * 53 | * MAP VALUE | VALUE SEQUENCE | INDICES 54 | * Map() | None, None | 0, 0 55 | * Map(false -> 99) | Some(99), None | 198, 0 56 | * Map(false -> 7) | Some(7), None | 15, 0 57 | * Map(true -> -1) | None, Some(-1) | 0, 3 58 | * Map(true -> -5, false -> 4) | Some(4), Some(-5) | 8, 11 59 | * Map(false -> 33, true -> 44) | Some(33), Some(44) | 67, 89 60 | * 61 | * Our coding will then encode every index except the last, and 62 | * combine them. For example, given k=4, the above indices would be 63 | * coded as: 64 | * 65 | * 0, 0 | 0; [0] | [0000].0000 | 0 66 | * 198, 0 | (13, 3); [0] | [0000].0000.1101.0011 | 211 67 | * 15, 0 | (1, 0); [0] | [0000].0000.0000.1111 | 15 68 | * 0, 3 | 0; [3] | [0011].0000 | 48 69 | * 8, 11 | 8; [11] | [1011].0000.1000 | 2824 70 | * 67, 89 | (4, 7); [89] | [0101.1001].0000.0100.0111 | 364616 71 | * 72 | * (The final, uncoded index is surrounded in square brackets.) 73 | * 74 | * Codings are not needed when V is finite: in those cases we can just 75 | * use a single digit base-C, where C is the cardinality of V. 76 | */ 77 | object Coding { 78 | 79 | /** 80 | * Read an unbounded natural number in the given byte coding. 81 | * 82 | * The return value is `(indexAfterReading, valueRead)`. 83 | */ 84 | def read(index: Z, mask: Int, k: Int): (Z, Z) = { 85 | @tailrec def loop(index0: Z, mult: Z, counter0: Z, carry0: Boolean): (Z, Z) = 86 | if (index0.isZero) { 87 | (index0, if (carry0) counter0 + mult else counter0) 88 | } else { 89 | val bits = (index0 & mask).toInt 90 | val index1 = index0 >> k 91 | if (bits == 0) { 92 | (index1, if (carry0) mult + counter0 else counter0) 93 | } else { 94 | val n = if (bits == mask) 0 else bits 95 | val counter1 = if (n > 0) counter0 + (mult * n) else counter0 96 | val carry1 = n == 0 || (carry0 && n == 1) 97 | loop(index1, mult * mask, counter1, carry1) 98 | } 99 | } 100 | loop(index, Z.one, Z.zero, false) 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /core/src/main/scala/kronecker/instances/hlist.scala: -------------------------------------------------------------------------------- 1 | package kronecker 2 | package instances 3 | 4 | import shapeless._ 5 | 6 | sealed trait HCountable[H <: HList] { 7 | type FAux <: HList 8 | type IAux <: HList 9 | def card: Card 10 | def infArity: Int 11 | def fbuild(index: Z): (Z, FAux) 12 | def ibuild(elem: List[Z]): IAux 13 | def combine(faux: FAux, iaux: IAux): H 14 | final def build(index: Z): H = { 15 | val (q, faux) = fbuild(index) 16 | val elem = if (infArity == 0) Nil else Diagonal.atIndex(infArity, q) 17 | val iaux = ibuild(elem) 18 | combine(faux, iaux) 19 | } 20 | } 21 | 22 | object HCountable { 23 | 24 | sealed class ToCountable[H <: HList](evh: HCountable[H]) extends Countable[H] { 25 | def cardinality: Card = 26 | evh.card 27 | def get(index: Z): Option[H] = 28 | if (evh.card.contains(index)) Some(evh.build(index)) else None 29 | } 30 | 31 | sealed trait CHNil extends HCountable[HNil] { 32 | type FAux = HNil 33 | type IAux = HNil 34 | val card: Card = Card.one 35 | def infArity: Int = 0 36 | def fbuild(index: Z): (Z, FAux) = (index, HNil) 37 | def ibuild(elem: List[Z]): IAux = if (elem.isEmpty) HNil else sys.error("!") 38 | def combine(faux: FAux, iaux: IAux): HNil = HNil 39 | } 40 | 41 | sealed class Bounded[A, H <: HList](eva: Countable[A], sz: Z, val evh: HCountable[H]) extends HCountable[A :: H] { 42 | type FAux = A :: evh.FAux 43 | type IAux = evh.IAux 44 | def card: Card = eva.cardinality * evh.card 45 | def infArity: Int = evh.infArity 46 | def fbuild(index: Z): (Z, A :: evh.FAux) = { 47 | val (q0, m0) = index /% sz 48 | val (q1, h) = evh.fbuild(q0) 49 | (q1, eva.get(m0).get :: h) 50 | } 51 | def ibuild(elem: List[Z]): evh.IAux = 52 | evh.ibuild(elem) 53 | def combine(faux: A :: evh.FAux, iaux: evh.IAux): A :: H = 54 | faux.head :: evh.combine(faux.tail, iaux) 55 | } 56 | 57 | sealed class Unbounded[A, H <: HList](eva: Countable[A], val evh: HCountable[H]) extends HCountable[A :: H] { 58 | type FAux = evh.FAux 59 | type IAux = A :: evh.IAux 60 | def card: Card = 61 | Card.infinite 62 | def infArity: Int = 63 | evh.infArity + 1 64 | def fbuild(index: Z): (Z, evh.FAux) = 65 | evh.fbuild(index) 66 | def ibuild(elem: List[Z]): A :: evh.IAux = 67 | eva.get(elem.head).get :: evh.ibuild(elem.tail) 68 | def combine(faux: evh.FAux, iaux: A :: evh.IAux): A :: H = 69 | iaux.head :: evh.combine(faux, iaux.tail) 70 | } 71 | 72 | implicit object CHNil extends CHNil 73 | 74 | implicit def hcons[A, H <: HList](implicit eva: Countable[A], evh: HCountable[H]): HCountable[A :: H] = 75 | eva.cardinality.value match { 76 | case Some(sz) => new Bounded(eva, sz, evh) 77 | case None => new Unbounded(eva, evh) 78 | } 79 | } 80 | 81 | sealed trait HIndexable[H <: HList] extends HCountable[H] { 82 | def split(h: H): (FAux, IAux) 83 | def funbuild(faux: FAux): (Z, Z) 84 | def iunbuild(iaux: IAux): List[Z] 85 | 86 | final def unbuild(h: H): Z = { 87 | val (faux, iaux) = split(h) 88 | val (i, n) = funbuild(faux) 89 | val j = Diagonal.fromElem(iunbuild(iaux)) 90 | i + (j * n) 91 | } 92 | } 93 | 94 | object HIndexable { 95 | 96 | final class ToIndexable[H <: HList](evh: HIndexable[H]) extends HCountable.ToCountable(evh) with Indexable[H] { 97 | def index(h: H): Z = evh.unbuild(h) 98 | } 99 | 100 | sealed trait IHNil extends HCountable.CHNil with HIndexable[HNil] { 101 | def split(h: HNil): (FAux, IAux) = (HNil, HNil) 102 | def funbuild(faux: FAux): (Z, Z) = (Z.zero, Z.one) 103 | def iunbuild(iaux: IAux): List[Z] = Nil 104 | } 105 | 106 | final class Bounded[A, H <: HList](eva: Indexable[A], sz: Z, override val evh: HIndexable[H]) 107 | extends HCountable.Bounded[A, H](eva, sz, evh) with HIndexable[A :: H] { 108 | def split(elem: A :: H): (FAux, IAux) = { 109 | val (fx, ix) = evh.split(elem.tail) 110 | (elem.head :: fx, ix) 111 | } 112 | def funbuild(faux: FAux): (Z, Z) = { 113 | val (i, n) = evh.funbuild(faux.tail) 114 | val j = eva.index(faux.head) + (sz * i) 115 | (j, n * sz) 116 | } 117 | def iunbuild(iaux: IAux): List[Z] = 118 | evh.iunbuild(iaux) 119 | } 120 | 121 | final class Unbounded[A, H <: HList](eva: Indexable[A], override val evh: HIndexable[H]) 122 | extends HCountable.Unbounded[A, H](eva, evh) with HIndexable[A :: H] { 123 | def split(elem: A :: H): (FAux, IAux) = { 124 | val (fx, ix) = evh.split(elem.tail) 125 | (fx, elem.head :: ix) 126 | } 127 | def funbuild(faux: FAux): (Z, Z) = 128 | evh.funbuild(faux) 129 | def iunbuild(iaux: IAux): List[Z] = 130 | eva.index(iaux.head) :: evh.iunbuild(iaux.tail) 131 | } 132 | 133 | implicit object IHNil extends IHNil 134 | 135 | implicit def hcons[A, H <: HList](implicit eva: Indexable[A], evh: HIndexable[H]): HIndexable[A :: H] = 136 | eva.cardinality.value match { 137 | case Some(sz) => new Bounded(eva, sz, evh) 138 | case None => new Unbounded(eva, evh) 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /core/src/main/scala/kronecker/instances/coproduct.scala: -------------------------------------------------------------------------------- 1 | package kronecker 2 | 3 | import shapeless._ 4 | 5 | sealed trait CCountable[C <: Coproduct] { 6 | type FAux <: Coproduct 7 | type IAux <: Coproduct 8 | 9 | def card: Card 10 | def finiteSize: Z 11 | def infArity: Int 12 | def fbuild(index: Z): C 13 | def ibuild(index: Z, i: Int): C 14 | 15 | final def build(index: Z): C = 16 | if (index < finiteSize) fbuild(index) 17 | else { 18 | val (q, m) = (index - finiteSize) /% infArity 19 | ibuild(q, m.toInt) 20 | } 21 | } 22 | 23 | object CCountable { 24 | 25 | sealed class ToCountable[C <: Coproduct](evc: CCountable[C]) extends Countable[C] { 26 | def cardinality: Card = 27 | evc.card 28 | def get(index: Z): Option[C] = 29 | if (evc.card.contains(index)) Some(evc.build(index)) else None 30 | } 31 | 32 | sealed trait CCNil extends CCountable[CNil] { 33 | type FAux = CNil 34 | type IAux = CNil 35 | 36 | def card: Card = Card.zero 37 | def finiteSize: Z = Z.zero 38 | def infArity: Int = 0 39 | def fbuild(index: Z): CNil = sys.error("impossible") 40 | def ibuild(index: Z, i: Int): CNil = sys.error("impossible") 41 | } 42 | 43 | class Bounded[A, C <: Coproduct](eva: Countable[A], sz: Z, val evc: CCountable[C]) extends CCountable[A :+: C] { 44 | type FAux = A :+: evc.FAux 45 | type IAux = evc.IAux 46 | val card: Card = eva.cardinality + evc.card 47 | val finiteSize: Z = sz + evc.finiteSize 48 | val infArity: Int = evc.infArity 49 | def fbuild(index: Z): A :+: C = 50 | if (index < sz) Inl(eva.get(index).get) 51 | else Inr(evc.fbuild(index - sz)) 52 | def ibuild(index: Z, i: Int): A :+: C = 53 | Inr(evc.ibuild(index, i)) 54 | } 55 | 56 | class Unbounded[A, C <: Coproduct](eva: Countable[A], val evc: CCountable[C]) extends CCountable[A :+: C] { 57 | type FAux = evc.FAux 58 | type IAux = A :+: evc.IAux 59 | val card = Card.infinite 60 | val finiteSize: Z = evc.finiteSize 61 | val infArity: Int = 1 + evc.infArity 62 | def fbuild(index: Z): A :+: C = 63 | Inr(evc.fbuild(index)) 64 | def ibuild(index: Z, i: Int): A :+: C = 65 | if (i == 0) Inl(eva.get(index).get) else Inr(evc.ibuild(index, i - 1)) 66 | } 67 | 68 | implicit object CCNil extends CCNil 69 | 70 | implicit def ccons[A, C <: Coproduct](implicit eva: Countable[A], evc: CCountable[C]): CCountable[A :+: C] = 71 | eva.cardinality.value match { 72 | case Some(sz) => new Bounded(eva, sz, evc) 73 | case None => new Unbounded(eva, evc) 74 | } 75 | 76 | } 77 | 78 | sealed trait CIndexable[C <: Coproduct] extends CCountable[C] { 79 | def split(c: C): Either[FAux, IAux] 80 | def funbuild(faux: FAux): Z 81 | def iunbuild(iaux: IAux): (Z, Int) 82 | 83 | final def unbuild(c: C): Z = 84 | split(c) match { 85 | case Left(faux) => 86 | funbuild(faux) 87 | case Right(iaux) => 88 | val (index, i) = iunbuild(iaux) 89 | (index * infArity + i) + finiteSize 90 | } 91 | } 92 | 93 | object CIndexable { 94 | 95 | final class ToIndexable[C <: Coproduct](evc: CIndexable[C]) 96 | extends CCountable.ToCountable(evc) with Indexable[C] { 97 | def index(c: C): Z = evc.unbuild(c) 98 | } 99 | 100 | final class Bounded[A, C <: Coproduct](eva: Indexable[A], sz: Z, override val evc: CIndexable[C]) 101 | extends CCountable.Bounded[A, C](eva, sz, evc) with CIndexable[A :+: C] { 102 | def split(elem: A :+: C): Either[FAux, IAux] = 103 | elem match { 104 | case Inl(a) => 105 | Left(Inl(a)) 106 | case Inr(c) => 107 | evc.split(c) match { 108 | case Left(faux) => Left(Inr(faux)) 109 | case Right(iaux) => Right(iaux) 110 | } 111 | } 112 | def funbuild(faux: FAux): Z = 113 | faux match { 114 | case Inl(a) => eva.index(a) 115 | case Inr(fs) => evc.funbuild(fs) + sz 116 | } 117 | def iunbuild(iaux: IAux): (Z, Int) = 118 | evc.iunbuild(iaux) 119 | } 120 | 121 | final class Unbounded[A, C <: Coproduct](eva: Indexable[A], override val evc: CIndexable[C]) 122 | extends CCountable.Unbounded[A, C](eva, evc) with CIndexable[A :+: C] { 123 | def split(elem: A :+: C): Either[FAux, IAux] = 124 | elem match { 125 | case Inl(a) => 126 | Right(Inl(a)) 127 | case Inr(c) => 128 | evc.split(c) match { 129 | case Left(faux) => Left(faux) 130 | case Right(iaux) => Right(Inr(iaux)) 131 | } 132 | } 133 | def funbuild(faux: FAux): Z = 134 | evc.funbuild(faux) 135 | def iunbuild(iaux: IAux): (Z, Int) = 136 | iaux match { 137 | case Inl(a) => 138 | (eva.index(a), 0) 139 | case Inr(fs) => 140 | val (index, i) = evc.iunbuild(fs) 141 | (index, i + 1) 142 | } 143 | } 144 | 145 | implicit object ICNil extends CCountable.CCNil with CIndexable[CNil] { 146 | def split(elem: CNil): Either[FAux, IAux] = sys.error("impossible") 147 | def funbuild(faux: FAux): Z = sys.error("impossible") 148 | def iunbuild(iaux: IAux): (Z, Int) = sys.error("impossible") 149 | } 150 | 151 | implicit def icons[A, C <: Coproduct](implicit eva: Indexable[A], evc: CIndexable[C]): CIndexable[A :+: C] = 152 | eva.cardinality.value match { 153 | case Some(sz) => new Bounded(eva, sz, evc) 154 | case None => new Unbounded(eva, evc) 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /core/src/main/scala/kronecker/Card.scala: -------------------------------------------------------------------------------- 1 | package kronecker 2 | 3 | import spire.algebra.Rig 4 | 5 | /** 6 | * Represents the cardinality of a set. 7 | * 8 | * Ideally, there would be two subtypes of Card: Finite and 9 | * Infinite. However, in practice we often find ourselves dealing with 10 | * cardinalities that are too large to compute but still finite 11 | * (e.g. 2^(2^100)). To support this, we have Semifinite values which 12 | * use a free representation (Plus, Times, and Pow) to represent 13 | * values that aren't infinite but which are too large to compute. 14 | * 15 | * We treat these values as "effectively infinite" in the sense that 16 | * we can't produce any index values large enough to exceed them. 17 | */ 18 | sealed trait Card { lhs => 19 | 20 | import Card._ 21 | 22 | def value: Option[Z] = 23 | this match { 24 | case Finite(n) => Some(n) 25 | case _ => None 26 | } 27 | 28 | def contains(i: Z): Boolean = 29 | if (i < 0) false 30 | else this match { 31 | case Finite(n) => i < n 32 | case _ => true 33 | } 34 | 35 | def isMax(i: Z): Boolean = 36 | this match { 37 | case Finite(n) => i == n - 1 38 | case _ => false 39 | } 40 | 41 | def +(rhs: Card): Card = 42 | (lhs, rhs) match { 43 | case (_, Infinite) => Infinite 44 | case (Infinite, _) => Infinite 45 | case (x: Semifinite, y: Semifinite) => plus(x, y) 46 | } 47 | 48 | // unsafe 49 | def -(rhs: Int): Card = 50 | (lhs, rhs) match { 51 | case (Finite(n), rhs) if Z(rhs) >= n => Card.zero 52 | case (Finite(n), rhs) => Finite(n - rhs) 53 | case (card, _) => card 54 | } 55 | 56 | def *(rhs: Card): Card = 57 | (lhs, rhs) match { 58 | case (Zero, _) => Zero 59 | case (_, Zero) => Zero 60 | case (_, Infinite) => Infinite 61 | case (Infinite, _) => Infinite 62 | case (x: Semifinite, y: Semifinite) => times(x, y) 63 | } 64 | 65 | def **(rhs: Card): Card = 66 | (lhs, rhs) match { 67 | case (_, Zero) => One 68 | case (Zero, _) => Zero 69 | case (_, One) => lhs 70 | case (One, _) => One 71 | case (_, Infinite) => Infinite 72 | case (Infinite, _) => Infinite 73 | case (x: Semifinite, y: Semifinite) => pow(x, y) 74 | } 75 | 76 | def partialCompare(rhs: Card): Double = 77 | (lhs, rhs) match { 78 | case (x, y) if x == y => 0.0 79 | case (_, Infinite) => -1.0 80 | case (Infinite, _) => 1.0 81 | case (Finite(x), Finite(y)) => (x compare y).toDouble 82 | case (Finite(_), _) => -1.0 83 | case (_, Finite(_)) => 1.0 84 | case (_, _) => Double.NaN 85 | } 86 | } 87 | 88 | object Card { 89 | 90 | def apply(n: Z): Card = Finite(n) 91 | 92 | case object Infinite extends Card { 93 | override def toString: String = "∞" 94 | } 95 | 96 | // 2^6553400 has 1972770 decimal digits 97 | val MaxExponent: Z = Z(6553400) 98 | 99 | sealed abstract class Semifinite extends Card { 100 | override def toString: String = 101 | this match { 102 | case Finite(x) => x.toString 103 | case Plus(x, y) => s"($x + $y)" 104 | case Times(x, y) => s"($x * $y)" 105 | case Pow(x, y) => s"($x ** $y)" 106 | } 107 | } 108 | 109 | case class Plus(x: Semifinite, y: Semifinite) extends Semifinite 110 | case class Times(x: Semifinite, y: Semifinite) extends Semifinite 111 | case class Pow(x: Semifinite, y: Semifinite) extends Semifinite 112 | 113 | case class Finite private (size: Z) extends Semifinite { 114 | require(size >= 0) 115 | } 116 | 117 | val Zero: Finite = Finite(Z.zero) 118 | val One: Finite = Finite(Z.one) 119 | val Two: Finite = Finite(Z(2)) 120 | 121 | def zero: Card = Zero 122 | def one: Card = One 123 | def two: Card = Two 124 | def infinite: Card = Infinite 125 | 126 | def plus(lhs: Semifinite, rhs: Semifinite): Semifinite = 127 | (lhs, rhs) match { 128 | case (Zero, _) => rhs 129 | case (_, Zero) => lhs 130 | case (Finite(x), Finite(y)) => Finite(x + y) 131 | case (Plus(x, y), z) => Plus(x, plus(y, z)) 132 | case (Finite(x), Plus(Finite(y), z)) => Plus(Finite(x + y), z) 133 | case (x, y) => Plus(x, y) 134 | } 135 | 136 | def times(lhs: Semifinite, rhs: Semifinite): Semifinite = 137 | (lhs, rhs) match { 138 | case (Zero, _) => Zero 139 | case (_, Zero) => Zero 140 | case (One, _) => rhs 141 | case (_, One) => lhs 142 | case (Finite(x), Finite(y)) => Finite(x * y) 143 | case (Times(x, y), z: Semifinite) => Times(x, times(y, z)) 144 | case (Finite(x), Times(Finite(y), z)) => Times(Finite(x * y), z) 145 | case (x, y) => Times(x, y) 146 | } 147 | 148 | def pow(lhs: Semifinite, rhs: Semifinite): Semifinite = 149 | (lhs, rhs) match { 150 | case (Finite(x), Finite(y)) => Card.semipow(x, y) 151 | case (_, Zero) => One 152 | case (Zero, _) => Zero 153 | case (_, One) => lhs 154 | case (One, _) => Zero 155 | case (Pow(x, y), z) => Pow(x, times(y, z)) 156 | case (x: Semifinite, y: Semifinite) => Pow(x, y) 157 | } 158 | 159 | def semipow(base: Z, exponent: Z): Semifinite = 160 | if (exponent.isZero) Card.One 161 | else if (base.isZero) Card.Zero 162 | else if (base.isOne) Card.One 163 | else { 164 | // (2 ** k) approximates (base ** exponent) 165 | val k = exponent * base.bitLength 166 | if (k <= MaxExponent) Card.Finite(base.pow(exponent.toInt)) 167 | else Pow(Card.Finite(base), Card.Finite(exponent)) 168 | } 169 | 170 | implicit object CardAlgebra extends Rig[Card] { 171 | def zero: Card = Zero 172 | def one: Card = One 173 | def plus(x: Card, y: Card): Card = x + y 174 | def times(x: Card, y: Card): Card = x * y 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Kronecker 2 | 3 | ### Preamble 4 | 5 | ``` 6 | "Die ganzen Zahlen hat der liebe Gott gemacht, alles andere ist Menschenwerk." 7 | 8 | -- Leopold Kronecker 9 | 10 | (God made the integers, everything else is human work.) 11 | ``` 12 | 13 | ### Overview 14 | 15 | Kronecker is for enumerating the distinct values of a given type. 16 | 17 | The `Countable[A]` type class provides a `get` method, which takes an 18 | index (given as a `spire.math.SafeLong`) and returns an `Option[A]`. 19 | The countable instance must be able to generate any possible `A` value 20 | in principle (although the necessary index number and/or the resulting 21 | value might exceed the avilable memory of the JVM). 22 | 23 | The `Indexable[A]` type class extends `Countable[A]`, providing an 24 | additional `index` method, which takes an `A` value and returns its 25 | corresponding index such that `get(index(a)) == Some(a)`. 26 | 27 | ### Example 28 | 29 | Here's an example REPL session that demonstrates some basic 30 | functionality: 31 | 32 | ```scala 33 | import kronecker._ 34 | 35 | val c0 = Countable[List[Byte]] 36 | 37 | c0.get(0) // Some(List()) 38 | c0.get(1) // Some(List(0)) 39 | c0.get(2) // Some(List(1)) 40 | c0.get(1000) // Some(List(2, -25)) 41 | c0.get(1000000) // Some(List(14, 65, 63)) 42 | c0.get(1000000000) // Some(List(58, -103, -56, -1)) 43 | 44 | val i = Z(20).pow(64) 45 | // 184467440737095516160000000000000000000000000000000000000000000000000000000000000000 46 | 47 | c0.get(i) 48 | // Some(List(23, 78, 2, -24, 62, -8, -13, -39, -90, -106, -20, 109, 55, 49 | // -20, 99, -66, 105, 29, -1, -2, -2, -2, -2, -2, -2, -2, -2, 50 | // -2, -2, -2, -2, -2, -2, -2, -1)) 51 | 52 | case class Foo(x: Boolean, y: List[Int], z: String) 53 | 54 | val c1 = Countable[(Z, Z, Z, Z, Z, Z, Z, Z, Z)] 55 | 56 | c1.get(0) // Some((0,0,0,0,0,0,0,0,0)) 57 | c1.get(1) // Some((1,0,0,0,0,0,0,0,0)) 58 | c1.get(2) // Some((0,1,0,0,0,0,0,0,0)) 59 | c1.get(1000) // Some((1,0,-2,0,0,0,0,0,0)) 60 | c1.get(1000000) // Some((-1,0,2,1,2,-1,0,0,-2)) 61 | c1.get(1000000000) // Some((2,1,0,4,-5,-1,-2,3,3)) 62 | 63 | val j = Z(20).pow(32) 64 | // 429496729600000000000000000000000000000000 65 | 66 | c1.get(j) // Some((25689,-10621,1664,-1809,-14874,22207,-1114,9471,172)) 67 | ``` 68 | 69 | After the JVM is warm, the above examples run in near-instant time on 70 | the author's machine. However it's not hard to increment the exponents 71 | used until the library takes "too long" to return (for example, 72 | `c1.get(i)` takes longer than you'll care to wait). 73 | 74 | ### Laws 75 | 76 | The laws for `ev: Countable[A]` are as follows: 77 | 78 | * for every `a: A` there is an `i` such that `ev.get(i) = Some(a)` 79 | * `ev.get(i)` returns `Some(_)` for all `i < ev.cardinality` 80 | * `ev.get(i)` returns `None` for all `i >= ev.cardinality` 81 | * `ev.get(i) = Some(_) = ev.get(j)` if and only if `i = j` 82 | 83 | The laws for `ev: Indexable[A]` are the above laws and also: 84 | 85 | * if `ev.get(i)` returns `Some(a)`, then `ev.index(a) = i` 86 | * for all `a`, `0 <= index(a) < ev.cardinality` 87 | * for all `a`, `get(index(a)) = Some(a)` 88 | * `index(a1) = index(a2)` if and only if `a1 = a2`. 89 | 90 | In all these laws `i` is assumed to be a non-negative, unbounded 91 | integer (i.e. a `spire.math.SafeLong`, aliased as `Z` in Kronecker). 92 | 93 | ### Details 94 | 95 | Some people might take issue with the finite/infinite terminology 96 | (especially in a library named for Kronecker). The terms stand in for 97 | bounded/unbounded. `Card.Finite` represents a finite, definite 98 | quantity that we can compute with, whereas `Card.Infinite` represents 99 | an unbounded cardinality (a set whose members can't be exhaustively 100 | enumerated). The other `Card.Semifinite` members (`Plus`, `Times`, 101 | `Pow`) represent quantities that are bounded, but which can't be 102 | computed within the current runtime (they are effectively unbounded). 103 | 104 | ### Known issues 105 | 106 | There are numbers that are too big to compute arithmetically and which 107 | will cause your JVM to appear to hang, or to crash. Kronecker's API 108 | may encourage you to dance dangerously close to the chasm of 109 | non-termination: consider yourself warned. 110 | 111 | For example, here's how the size of an integer in a set affects the 112 | index returned by `Indexable[Set[Int]]`: 113 | 114 | ```scala 115 | val ev = kronecker.Indexable[Set[Int]] 116 | ev.index(Set(1)) // 1 digit 117 | ev.index(Set(10)) // 4 digits 118 | ev.index(Set(100)) // 31 digits 119 | ev.index(Set(1000)) // 302 digits 120 | ev.index(Set(10000)) // 3,011 digits 121 | ev.index(Set(100000)) // 30,103 digits (feels instant) 122 | ev.index(Set(1000000)) // 301,030 digits (some delay) 123 | ev.index(Set(10000000)) // 3,010,300 digits (takes ~3 seconds) 124 | ev.index(Set(100000000)) // 30,103,000 digits (takes ~3 minutes) 125 | ev.index(Set(Int.MaxValue)) // crashes instantly with ArithmeticException 126 | ``` 127 | 128 | The underlying issue here is that if the cardinality of `A` is *x*, 129 | then the cardinality of a `Set[A]` is *2ˣ*. 130 | 131 | ### Future work 132 | 133 | We're missing `Indexable` instances for `Map`. 134 | 135 | The functions we generate are just glorified maps that are "mostly 136 | zero" everywhere else. There are too many functions to ever get "good" 137 | performance over large cardinalities, but we could experiment with 138 | different orderings. 139 | 140 | Since our indices are never negative, it's arguable we should be using 141 | a natural number type instead of `SafeLong`. For performance reasons 142 | I'm not eager to make this change. 143 | 144 | There is some work-in-progress around actually using `Countable[A]` to 145 | power property-based tests (e.g. ScalaCheck). This might end up being 146 | kind of cool if it works well. 147 | 148 | In terms of space-age work, using some model of *codata* to bound how 149 | much work we're willing to do (and to catch situations where the JVM 150 | might appear to hang before it does so) would be pretty radical. 151 | 152 | ### Copyright and License 153 | 154 | All code is available to you under the Apache 2 license, available at 155 | https://opensource.org/licenses/Apache-2.0. 156 | 157 | Copyright Erik Osheim, 2018. 158 | -------------------------------------------------------------------------------- /core/src/main/scala/kronecker/Diagonal.scala: -------------------------------------------------------------------------------- 1 | package kronecker 2 | 3 | import scala.annotation.tailrec 4 | import spire.implicits._ 5 | 6 | /** 7 | * Utilities for doing diagonalization in N dimensions. 8 | * 9 | * The goal here is to be able to support diagonalizations for 10 | * arbitrary tuples, e.g. Tuple2, Tuple3, Tuple9, etc. The "dimension" 11 | * (or "dim") represents the arity of the tuple: dim=2 would 12 | * correspond to a Tuple2. 13 | * 14 | * We model each generated tuple as an "element" (a list of integers). 15 | * The individual integers in the element correspond to positions in 16 | * underlying streams of values, and the element corresponds to a 17 | * particular tuple. 18 | * 19 | * For example, if we have a type Ascii which corresponds to ASCII 20 | * strings, we might choose to represent the stream of all distinct 21 | * Ascii values as: 22 | * 23 | * "a", "b", ... "z", "aa", "ab", ... "ba", ... "zz", "aaa", ... 24 | * 25 | * In this case, we could represent the stream of all distinct 26 | * Tuple2[Ascii, Ascii] values as: 27 | * 28 | * ("a", "a"), ("b", "a"), ("a", "b"), ("c", "a"), ("b", "b"), ... 29 | * 30 | * This would correspond to the elements: 31 | * 32 | * (0, 0), (1, 0), (0, 1), (2, 0), (1, 1), (0, 2), ... 33 | * 34 | * (where the integers index into the original stream of ASCII string 35 | * values above.) 36 | * 37 | * Here are some worked examples to get an idea of what is going on 38 | * with the indices. Each row of text here represents a "depth" 39 | * (depth=0 is the first row), and each logical column represents a 40 | * "position" at that depth. The "width" at a given depth is the 41 | * number of columns at that depth; for dim=1, width is always 1. 42 | * 43 | * dim=1 44 | * 0 45 | * 1 46 | * 2 47 | * 3 48 | * 49 | * dim=2 50 | * (0 0) 51 | * (1 0) (0 1) 52 | * (2 0) (1 1) (0 2) 53 | * (3 0) (2 1) (1 2) (0 3) 54 | * ... 55 | * 56 | * dim=3 57 | * (0 0 0) 58 | * (1 0 0) (0 1 0) (0 0 1) 59 | * (2 0 0) (1 1 0) (1 0 1) (0 2 0) (0 1 1) (0 0 2) 60 | * (3 0 0) (2 1 0) (2 0 1) (1 2 0) (1 1 1) (1 0 2) (0 3 0) (0 2 1) (0 1 2) (0 0 3) 61 | * ... 62 | * 63 | * and so on. 64 | * 65 | * The advantage of using Diagonal.atDepth is that it doesn't require 66 | * calculating all the preceeding elements in order to calculate an 67 | * element. This doesn't seem like a big deal until you consider that 68 | * at depth 30 a 20-tuple has over 47 trillion distinct elements. 69 | */ 70 | object Diagonal { 71 | 72 | type Elem = List[Z] 73 | 74 | /** 75 | * Determine how many elements of dimension `dim` exist at a given 76 | * tree `depth`. 77 | * 78 | * widthAtDepth(1, d) = 1 79 | * widthAtDepth(2, d) = d + 1 80 | * widthAtDepth(3, d) = ((d+1) * (d+2)) / 2 81 | * widthAtDepth(4, d) = ((d+1) * (d+2) * (d+3)) / 6 82 | * ... 83 | * 84 | * The sequences of these values are: 85 | * 86 | * dim=1: 1 1 1 1 1 1 1 1 1 87 | * dim=2: 1 2 3 4 5 6 7 8 9 88 | * dim=3: 1 3 6 10 15 21 28 36 45 89 | * dim=4: 1 4 10 20 35 56 84 120 165 90 | * dim=5: 1 5 15 35 70 126 210 330 495 91 | * ... 92 | * 93 | * Notice that the kth value at dimenion d is equal to the sum of 94 | * the (0 to kth) values at dimension (d-1). 95 | */ 96 | def widthAtDepth(dim: Int, depth: Z): Z = { 97 | @tailrec def loop(i: Int, term: Z, num: Z, denom: Z): Z = 98 | if (i <= 1) num / denom 99 | else loop(i - 1, term + 1, (depth + term) * num, denom * term) 100 | 101 | if (dim == 1) Z.one else loop(dim, Z.one, Z.one, Z.one) 102 | } 103 | 104 | /** 105 | * Find a dimension `dim` element at the given `index`. 106 | * 107 | * This method decomposes the index into a depth and position and 108 | * then delegates the actual work to the `atDepth` method. 109 | */ 110 | def atIndex(dim: Int, index: Z): Elem = { 111 | require(index >= 0) 112 | val (pos, depth) = decompose(dim, index) 113 | atDepth(dim, depth, pos) 114 | } 115 | 116 | /** 117 | * Find a dimension `dim` element at the given `depth` denoted by 118 | * position `pos`. 119 | * 120 | * Requirement: 0 <= pos < widthAtDepth(dim, depth) 121 | */ 122 | def atDepth(dim: Int, depth: Z, pos: Z): Elem = 123 | dim match { 124 | case 1 => 125 | depth :: Nil 126 | case d => 127 | val (pos2, depth2) = decompose(dim - 1, pos) 128 | val elem = depth - depth2 129 | elem :: atDepth(dim - 1, depth2, pos2) 130 | } 131 | 132 | /** 133 | * Find the largest integer where the given property holds. 134 | * 135 | * This method does a binary search up the powers of 2 to find an 136 | * upper bound (an integer where the property fails), and then works 137 | * its way back through the remaining bits to figure out the largest 138 | * value where the property is still true. 139 | */ 140 | def search(p: Z => Boolean): Z = { 141 | 142 | // search up to find the smallest power of 2 where p fails. 143 | @tailrec def ascend(i: Int): Int = 144 | if (p(Z.one << i)) ascend(i + 1) else i 145 | 146 | // we know that p(1 << ceil) is false, but that p(1 << (ceil - 1)) 147 | // is true. so let's start adding bits back in (from highest to 148 | // lowest) to construct the largest value where p is still true 149 | @tailrec def descend(x: Z, i: Int): Z = 150 | if (i < 0) x else { 151 | val y = x | (Z.one << i) 152 | descend(if (p(y)) y else x, i - 1) 153 | } 154 | 155 | val ceil: Int = ascend(0) 156 | if (ceil == 0) Z.zero else descend(Z.zero, ceil - 1) 157 | } 158 | 159 | /** 160 | * Decompose an index into a position and depth. 161 | */ 162 | def decompose(dim: Int, index: Z): (Z, Z) = 163 | if (dim == 1) { 164 | (Z.zero, index) 165 | } else { 166 | 167 | // it turns out that widthAtDepth(dim+1, d) equals: 168 | // (0 to d).map(i => widthAtDepth(dim, i)).sum 169 | // 170 | // we want to calculate: 171 | // (0 to (d-1)).map(i => widthAtDepth(dim, i)).sum 172 | // 173 | // so we use: widthAtDepth(dim+1, d-1) 174 | 175 | val k = search(n => widthAtDepth(dim + 1, n - 1) <= index) 176 | (index - widthAtDepth(dim + 1, k - 1), k) 177 | } 178 | 179 | /** 180 | * Given an element, determine the index it corresponds to. 181 | */ 182 | def fromElem(elem: Elem): Z = { 183 | // returns (dim, depth, pos) 184 | def recur(elem: Elem): (Int, Z, Z) = 185 | elem match { 186 | case x :: Nil => 187 | (1, x, Z.zero) 188 | case x :: ys => 189 | val (dim0, depth0, pos0) = recur(ys) 190 | val dim1 = dim0 + 1 191 | val depth1 = x + depth0 192 | val pos1 = if (depth0.isZero) Z.zero else widthAtDepth(dim0 + 1, depth0 - 1) + pos0 193 | (dim1, depth1, pos1) 194 | case Nil => 195 | sys.error("impossible") 196 | } 197 | // TODO: could probably be more efficient than repeated widthAtDepth calls :/ 198 | elem match { 199 | case Nil => 200 | Z.zero 201 | case xs => 202 | val (dim, depth, pos) = recur(xs) 203 | if (depth.isZero) Z.zero else widthAtDepth(dim + 1, depth - 1) + pos 204 | } 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /core/src/main/scala/kronecker/instances/list.scala: -------------------------------------------------------------------------------- 1 | package kronecker 2 | package instances 3 | 4 | import scala.annotation.tailrec 5 | 6 | object CList { 7 | 8 | /** 9 | * Build Countable[List[A]] using the appropriate strategy. 10 | * 11 | * See CFList for how we handle finite A types, and CIList for 12 | * infinite A types. 13 | */ 14 | def apply[A](ev: Countable[A]): Countable[List[A]] = 15 | ev.cardinality.value match { 16 | case Some(sz) if sz.isZero => Countable.singleton(Nil) 17 | case Some(sz) => new CFList(ev, sz) 18 | case None => new CIList(ev) 19 | } 20 | } 21 | 22 | /** 23 | * Countable[List[A]] for finite A types. 24 | * 25 | * We encode these lists element-by-element in base-c digits, where c 26 | * is the cardinality of A, offset by 1 (the first element is always 27 | * the empty list). This ends up being equivalent to using 28 | * lexicographic order to generate the lists. 29 | * 30 | * For example, for List[Boolean] we have: 31 | * 32 | * 0: List() 33 | * 1: List(false) 34 | * 2: List(true) 35 | * 3: List(false, false) 36 | * 4: List(true, false) 37 | * 5: List(false, true) 38 | * 6: List(true, true) 39 | * 7: List(false, false, false) 40 | * 8: List(true, false, false) 41 | * 9: List(false, true, false) 42 | * 10: List(true, true, false) 43 | * 44 | * This strategy only works if A is finite, since for an infinite type 45 | * you won't ever finish enumerating the lists of length one. 46 | * 47 | * (This method assumes the cardinality of A is >= 1. If the 48 | * cardinality is zero, only the singleton list can be generated.) 49 | */ 50 | class CFList[A](ev: Countable[A], sz: Z) extends Countable[List[A]] { 51 | 52 | def cardinality: Card = 53 | Card.infinite 54 | 55 | def get(index: Z): Option[List[A]] = { 56 | val bldr = List.newBuilder[A] 57 | var curr = index 58 | while (!curr.isZero) { 59 | val (q, r) = (curr - 1) /% sz 60 | bldr += ev.get(r).get 61 | curr = q 62 | } 63 | Some(bldr.result()) 64 | } 65 | } 66 | 67 | /** 68 | * Countable[List[A]] for infinite A types. 69 | * 70 | * For an infinite type Countable[A] implies a bijection with the 71 | * natural numbers, so we can explain how this instance works in terms 72 | * of natural numbers. 73 | * 74 | * The first element in the enumeration is the empty list (as in 75 | * CFList). For non-zero indices, we split the index into hexadecimal 76 | * (base-16) "list digits" to be processed, starting with the least 77 | * significant digit. (Hexadecimal digits are prefixed with 0x.) 78 | * 79 | * We need a way of deciding how many elements are in our list, so we 80 | * reserve 0x0 as a delimiter between separate elements' indices. 81 | * 82 | * Since we've reserved 0x0, we have 15 possible values to use to 83 | * encode each element's index (0x1 through 0xf), meaning these 84 | * indices are expressed in pentadecimal (base-15), which we will 85 | * prefix with 0p. We also need to translate our values (1-15) into 86 | * the appropriate pentadecimal range (0-14), which we do by treating 87 | * 0xf as 0p0, leaving other digits unchanged (so 0x1 is 0p1, 0x9 is 88 | * 0p9, and so on). 89 | * 90 | * Broadly-speaking, you can look at the hex representation of the 91 | * input and visually parse how to split things: 92 | * 93 | * 0x3e0f9013 -> 0x3e / 0xf9 / 0x13 -> 0p3e / 0p09 / 0p13 94 | * 95 | * However, there is a problem. 0p09 is equivalent to 0p9, but this 96 | * would mean that we have two ways of representing the same list 97 | * element. This would break our bijection, since we are required to 98 | * have exactly one way to represent every possible list. 99 | * 100 | * To fix this, we treat a leading 0p0 digit as though it came after a 101 | * 0p1 digit (we call this "carrying"). This means that 0xf9 would be 102 | * treated as 0p109 instead of 0p09, which fixes the problem with 103 | * leading 0xf, but creates a problem with leading 0x1f, since 0x1f9 104 | * would _also_ be interpreted as 0p109. To handle this, we will 105 | * continue carrying across any 0p1 digits we see after 0p0. This 106 | * means 0x1f9 is treated as 0p1109, 0x11f9 is treated as 0p11109, and 107 | * so on. 108 | * 109 | * The only other detail of carrying is that when dealing with the 110 | * "last" element index (i.e. the largest significant digits of the 111 | * hexadecimal index), we have a special treatment 0xf, 0x1f, 0x11f, 112 | * and so on. In those cases, we ignore the carried 1 digit. This is 113 | * needed because of the fact that we need to treat 0xf0... as zero, 114 | * since 0x0... is equivalent to 0x..., i.e. leading zeros are lost. 115 | * 116 | * The order of the list is the order we parse the element indices 117 | * in: least significant digits first. 118 | * 119 | * Here are some worked examples: 120 | * 121 | * INDEX HEX-INDEX PENTA-ELEMS RESULT 122 | * 0 0x0 . Nil 123 | * 1 0x1 0p1 List(1) 124 | * 14 0xe 0pe List(14) 125 | * 15 0xf 0p0 List(0) 126 | * 16 0x10 0p1,0p0 List(0, 1) 127 | * 17 0x11 0p11 List(16) 128 | * 31 0x1f 0p10 List(15) 129 | * 32 0x20 0p2,0p0 List(0, 2) 130 | * 255 0xff 0p100 List(225) 131 | * 256 0x100 0p1,0p0,0p0 List(0, 0, 1) 132 | * 257 0x101 0p1,0p1 List(1, 1) 133 | * 510 0x1fe 0p110e List(3614) 134 | * 511 0x1ff 0p1100 List(3600) 135 | * 512 0x200 0p2,0p0,0p0 List(0, 0, 2) 136 | * 513 0x201 0p2,0p1 List(1, 2) 137 | * 130821 0x1ff05 0p1100,0p5 List(3600,5) 138 | * 395016 0x60708 0p6,0p7,0p8 List(8, 7, 6) 139 | * 1122053 0x111f05 0p11110,0p5 List(5, 3615) 140 | * 1041207315 0x3e0f9013 0p3e,0p109,0p13 List(18, 234, 59) 141 | * 142 | * (Use Countable[List[Natural]] to see this in action.) 143 | */ 144 | class CIList[A](ev: Countable[A]) extends Countable[List[A]] { 145 | 146 | protected[this] final val k = 4 147 | protected[this] final val mask = 0xf 148 | 149 | val cardinality: Card = Card.infinite 150 | 151 | /** 152 | * Get a list given its index. 153 | * 154 | * Since get(0) returns an empty list, we repeatedly extract 155 | * elements out of the given `index` until it is zero. 156 | */ 157 | def get(index: Z): Option[List[A]] = { 158 | val bldr = List.newBuilder[A] 159 | var n = index 160 | while (!n.isZero) { 161 | val (valIndex, rest) = decode(n) 162 | bldr += ev.get(valIndex).get 163 | n = rest 164 | } 165 | Some(bldr.result()) 166 | } 167 | 168 | /* 169 | * A-D are encode/decode states: 170 | * 171 | * encode transitions occur on m, 1, or x (wildcard). 172 | * all states other than A are possible end states. 173 | * 174 | * 1 175 | * ┏━┓ 176 | * ▼ ┃ 177 | * m ┏━━━━━━┻━━━━━┓ m 178 | * ┏━━━━━▶┃B.semi-carry┣━━━┓ 179 | * ┃ ┗━┳━━━━━━━━━━┛ ┃ m,1 180 | * ┃ ┃ ▼ ┏━━┓ 181 | * ┏━━┻━━━━┓ ┃ x x ┏━━━━━━┻┓ ┃ 182 | * ━▶┃A.start┃ ┗━━┓ ┏━━━━━┫D.carry┃ ┃ 183 | * ┗━━┳━━━━┛ ┃ ┃ ┗━━━━━━━┛ ┃ 184 | * ┃ ▼ ▼ ▲ ▲ ┃ 185 | * ┃ 1,x ┏━━━━━━━━━━┓ m ┃ ┗━━┛ 186 | * ┗━━━━━▶┃C.continue┣━━━━━┛ 187 | * ┗━━━━━━┳━━━┛ 188 | * ▲ ┃1,x 189 | * ┗━┛ 190 | * 191 | * (the decode diagram is the same, except 0 replaces m.) 192 | */ 193 | final val A = 1 194 | final val B = 2 195 | final val C = 3 196 | final val D = 4 197 | 198 | /** 199 | * Decode a particular element out of the list's encoded index. 200 | * 201 | * This returns the element's index followed by the index of the 202 | * rest of the list. 203 | * 204 | * The input (`n`) is expected to be non-zero (since the list at 205 | * index zero has no elements, all non-zero list indices have at 206 | * least one element). 207 | */ 208 | def decode(n: Z): (Z, Z) = { 209 | 210 | // loop through the blocks reading this element index. a zero 211 | // block means we're done with this element. 212 | // 213 | // curr is the current index being parsed and acc is the 214 | // accumulated element index. mult is the multiplier used for the 215 | // currently parsed digit, and state tracks out internal state 216 | // machine (A-D) which handles carrying. 217 | // 218 | // A. start: on m goto B, else goto C 219 | // B. semi-carry: on 1 stay in B, on m goto D, else goto C 220 | // C. continue: on m goto D, else stay in C 221 | // D. carry: on m or 1 stay in D, else goto C 222 | // 223 | // the only difference between states B and D is that on the very 224 | // last element to decode (i.e. the most signficiant digits of the 225 | // input), D will include a carried 1 digit whereas B will not. on 226 | // elements that aren't the last element both B and D will include 227 | // a carried 1 leading digit. 228 | @tailrec def loop(curr: Z, acc: Z, mult: Z, state: Int): (Z, Z) = { 229 | val q = curr >> k // the unread parts of the index 230 | val r = (curr & mask).toInt // the digit to read 231 | val isLast = q.isZero // is this the last element of the list? 232 | val isEnd = r == 0 // are we done reading the element? 233 | 234 | if (isEnd) { 235 | (if (state == B || state == D) acc + mult else acc, q) 236 | } else { 237 | val (acc2, st2) = 238 | if (r == mask) (acc, if (state == A) B else D) 239 | else if (r == 1) (acc + mult, if (state == B) B else if (state == D) D else C) 240 | else (acc + (r * mult), C) 241 | 242 | val mult2 = mult * mask 243 | 244 | if (isLast) { 245 | (if (st2 == D) acc2 + mult2 else acc2, Z.zero) 246 | } else { 247 | loop(q, acc2, mult2, st2) 248 | } 249 | } 250 | } 251 | loop(n, Z.zero, Z.one, A) 252 | } 253 | } 254 | 255 | /** 256 | * Build Indexable[List[A]] using the appropriate strategy. 257 | * 258 | * Like CList, this depends on the cardinality of A. 259 | */ 260 | object NList { 261 | def apply[A](ev: Indexable[A]): Indexable[List[A]] = 262 | ev.cardinality.value match { 263 | case Some(sz) if sz.isZero => Countable.singleton(Nil) 264 | case Some(sz) => new NFList(ev, sz) 265 | case None => new NIList(ev) 266 | } 267 | } 268 | 269 | /** 270 | * Indexable[List[A]] for finite A types. 271 | * 272 | * NFList is the complement to CFList when A is also indexable. See 273 | * CFList for more information about the mapping between indices and 274 | * values. 275 | */ 276 | class NFList[A](ev: Indexable[A], sz: Z) extends CFList(ev, sz) with Indexable[List[A]] { 277 | def index(lst: List[A]): Z = { 278 | def loop(as: List[A], acc: Z, mult: Z): Z = 279 | as match { 280 | case head :: tail => 281 | loop(tail, acc + (ev.index(head) + 1) * mult, mult * sz) 282 | case Nil => 283 | acc 284 | } 285 | if (lst.isEmpty) Z.zero 286 | else loop(lst, Z.zero, Z.one) 287 | } 288 | } 289 | 290 | /** 291 | * Indexable[List[A]] for infinite A types. 292 | * 293 | * NIList is the complement to CIList when A is also indexable. See 294 | * CIList for more information about the mapping between indices and 295 | * values. 296 | */ 297 | class NIList[A](ev: Indexable[A]) extends CIList(ev) with Indexable[List[A]] { 298 | 299 | private[this] val zmask = Z(mask) 300 | 301 | def index(lst: List[A]): Z = { 302 | @tailrec def loop(index: Z, shift: Int, as: List[A]): Z = 303 | as match { 304 | case Nil => 305 | index 306 | case head :: tail => 307 | val (part2, shift2) = encode(head, shift, tail.isEmpty) 308 | loop(index + part2, shift2, tail) 309 | } 310 | loop(Z.zero, 0, lst) 311 | } 312 | 313 | 314 | def encode(a: A, shift0: Int, isLast: Boolean): (Z, Int) = { 315 | // in the loop, we step through the current value, looking at its 316 | // least-significant digit base-mask, and encoding it in 317 | // base-(mask+1). we need to make sure zero digits are encoding as 318 | // the mask, since actual zeros are used to signal the end of a 319 | // particular element (value-index). 320 | // 321 | // state transitions mirror those from decode, substituting zero 322 | // for m: 323 | // 324 | // A: on 0 goto B, else goto C 325 | // B: on 1 stay in B, on 0 goto D, else goto C 326 | // C: on 0 goto D, else stay in C 327 | // D: on 0 or 1 stay in D, else goto C 328 | // 329 | // D means we will carry no matter what, B means we'll only carry 330 | // if we're not on the final element, and C means we won't carry. 331 | // (it should not be possible to end up in state A when we're done 332 | // with this element.) 333 | 334 | @tailrec def loop(curr: Z, acc: Z, shift: Int, state: Int): (Z, Int) = { 335 | val (q, r) = curr /% zmask 336 | val (digit, st) = 337 | if (r.isZero) (zmask, if (state == A) B else D) 338 | else if (r.isOne) (r, if (state == B) B else if (state == D) D else C) 339 | else (r, C) 340 | 341 | if (q.isZero) { 342 | if (st == D || (!isLast && st == B)) (acc, shift + k) 343 | else (acc + (digit << shift), shift + k + k) 344 | } else { 345 | loop(q, acc + (digit << shift), shift + k, st) 346 | } 347 | } 348 | 349 | val vindex = ev.index(a) 350 | if (vindex.isZero) { 351 | // our value-index is zero, so we'll either use the record 352 | // terminator (zero) or a logical zero (mask) to signal that. 353 | if (isLast) { 354 | // we need to use a mask digit, since a zero digit isn't 355 | // meaningful in the "most significant" position. 356 | (zmask << shift0, shift0 + k) 357 | } else { 358 | // we're not at the end yet, so use a record terminator. 359 | (Z.zero, shift0 + k) 360 | } 361 | } else { 362 | // we have a non-zero value-index, so go ahead and encode it. 363 | loop(vindex, Z.zero, shift0, A) 364 | } 365 | } 366 | } 367 | -------------------------------------------------------------------------------- /core/src/main/scala/kronecker/Countable.scala: -------------------------------------------------------------------------------- 1 | package kronecker 2 | 3 | import kronecker.instances._ 4 | import scala.collection.mutable 5 | import scala.reflect.ClassTag 6 | import shapeless._ 7 | import spire.math.{Natural, Rational, Searching} 8 | 9 | /** 10 | * Countable[A] represents an ordering of every possible A value. 11 | * 12 | * Particular values can be accessed by their index in this ordering. 13 | * Thus, get(0) returns the "first" A value, get(1) returns the 14 | * second, and so on. 15 | * 16 | * The values of type A might be a finite site, or an infinite set 17 | * (i.e. an unbounded set). In the latter case, this class must 18 | * diagonize the values so that every particular value has an index. 19 | * 20 | * It's possible to define "invalid" instances of Countable which 21 | * "miss" particular A values (and doesn't give a particular value 22 | * multiple indices). We have no way of verifying that an instance is 23 | * "valid" although the instances provided are believed to be valid. 24 | * We use law-checking to try to catch broken implementations. 25 | */ 26 | trait Countable[A] { self => 27 | 28 | /** 29 | * Return the "size" of the set of all A values. 30 | */ 31 | def cardinality: Card 32 | 33 | /** 34 | * Return the A value at the given index (if any). 35 | */ 36 | def get(index: Z): Option[A] 37 | 38 | /** 39 | * Iterator over all A values. 40 | * 41 | * This iterator is mutable -- you should create a new iterator 42 | * every time you wish to step through all A values. 43 | */ 44 | def iterator(): Iterator[A] = 45 | Iterator.from(0).map(self.get(_)).takeWhile(_.isDefined).map(_.get) 46 | 47 | // /** 48 | // * Lazy stream of all A values. 49 | // * 50 | // * Be careful! Scala memoizes streams, so if you hang onto the head 51 | // * of this stream you could end up using a lot of memory. 52 | // */ 53 | // def stream: Stream[A] = 54 | // Stream.from(0).map(self.get(_)).takeWhile(_.isDefined).map(_.get) 55 | 56 | /** 57 | * Produce a new Countable[B] instance by translating this one using 58 | * the given function `f`. 59 | * 60 | * This function is not called `map` since in order to produce 61 | * "valid" Countable[B] values, the function `f` must be a 62 | * bijection. We have no way of verifying this, so the name 63 | * `translate` is used to hint that something unusual is going on. 64 | */ 65 | def translate[B](f: A => B): Countable[B] = 66 | new Countable[B] { 67 | def cardinality: Card = self.cardinality 68 | def get(index: Z): Option[B] = self.get(index).map(f) 69 | } 70 | 71 | /** 72 | * Drop the first k items from the countable instance. 73 | * 74 | * Note that the result is not a lawful countable instance, unless 75 | * the subset of A that remains is translated onto a new type. 76 | */ 77 | def drop(k: Int): Countable[A] = 78 | new Countable[A] { 79 | require(k >= 0) 80 | def cardinality: Card = self.cardinality - k 81 | def get(index: Z): Option[A] = self.get(index + k) 82 | } 83 | 84 | /** 85 | * Permuate this countable's enumeration into a different 86 | * enumeration. 87 | * 88 | * This method uses a permutation on A values (the values being 89 | * counted). 90 | */ 91 | def permute(p: Permutation[A]): Countable[A] = 92 | new Countable[A] { 93 | def cardinality: Card = 94 | self.cardinality 95 | def get(index: Z): Option[A] = 96 | self.get(index).map(p(_)) 97 | } 98 | 99 | /** 100 | * Permuate this countable's enumeration into a different 101 | * enumeration. 102 | * 103 | * This method uses a permutation on numbers (the indices being 104 | * enumerated). 105 | */ 106 | def copermute(p: Permutation[Z]): Countable[A] = 107 | new Countable[A] { 108 | def cardinality: Card = 109 | self.cardinality 110 | def get(index: Z): Option[A] = 111 | self.get(p(index)) 112 | } 113 | } 114 | 115 | object Countable extends Countable1 { 116 | 117 | /** 118 | * Summon an implicit Countable[A] instance. 119 | */ 120 | def apply[A](implicit ev: Countable[A]): Countable[A] = ev 121 | 122 | // 0, 1, -1, 2, -2, ... 123 | implicit val cz: Indexable[Z] = 124 | new Indexable[Z] { 125 | 126 | def cardinality: Card = Card.Infinite 127 | 128 | def get(index: Z): Option[Z] = { 129 | val (quot, mod) = (index + 1) /% Z.two 130 | val sign = Z.one - (mod * Z.two) 131 | Some(sign * quot) 132 | } 133 | 134 | val minusTwo = Z(-2) 135 | 136 | def index(n: Z): Z = 137 | n.signum match { 138 | case 0 => Z.zero 139 | case x if x > 0 => n * Z.two - Z.one 140 | case _ => n * minusTwo 141 | } 142 | } 143 | 144 | /** 145 | * Produce an "empty" countable with cardinality zero. 146 | */ 147 | def empty[A]: Indexable[A] = 148 | new Indexable[A] { 149 | def cardinality: Card = Card.zero 150 | def get(index: Z): Option[A] = None 151 | def index(a: A): Z = sys.error("impossible") 152 | } 153 | 154 | /** 155 | * Produce a countable with exactly one value. 156 | */ 157 | def singleton[A](a: A): Indexable[A] = 158 | new Indexable[A] { 159 | def cardinality: Card = Card.one 160 | def get(index: Z): Option[A] = if (index.isZero) Some(a) else None 161 | def index(a: A): Z = Z.zero 162 | } 163 | 164 | /** 165 | * Given a collection of non-overlapping countable instances, build 166 | * a new countable instance which includes all values produced by 167 | * any of the given instances. 168 | * 169 | * The given countable instances must be non-overlapping; this means 170 | * that they should not ever produce the same A values. If instances 171 | * were to overlap, the cardinality of the resulting instance would 172 | * be too large, and it would produce the same value for two or more 173 | * indices, violating the laws. 174 | */ 175 | def oneOf[A](cs: Countable[A]*): Countable[A] = 176 | if (cs.isEmpty) { 177 | Countable.empty[A] 178 | } else if (cs.size == 1) { 179 | cs(0) 180 | } else { 181 | var ctot = Z.zero 182 | val cbuf = mutable.ArrayBuffer.empty[Z] 183 | val fbuf = mutable.ArrayBuffer.empty[Countable[A]] 184 | val ibuf = mutable.ArrayBuffer.empty[Countable[A]] 185 | 186 | cs.foreach { c => 187 | c.cardinality match { 188 | case Card.Finite(n) => 189 | ctot += n 190 | cbuf.append(ctot) 191 | fbuf.append(c) 192 | case _ => 193 | ibuf.append(c) 194 | } 195 | } 196 | 197 | def allFinite: Countable[A] = 198 | new Countable[A] { 199 | val cardinality: Card = Card(ctot) 200 | val cutoffs: Array[Z] = cbuf.toArray 201 | val cs: Array[Countable[A]] = fbuf.toArray 202 | def get(index: Z): Option[A] = 203 | if (cardinality.contains(index)) { 204 | val i: Int = Searching.search(cutoffs, index) 205 | val j: Int = if (i < 0) -(i + 1) else i + 1 206 | val k: Z = if (j > 0) cutoffs(j - 1) else Z.zero 207 | cs(j).get(index - k) 208 | } else { 209 | None 210 | } 211 | } 212 | 213 | def allInfinite: Countable[A] = 214 | new Countable[A] { 215 | val cs = ibuf.toArray 216 | def cardinality: Card = Card.infinite 217 | def get(index: Z): Option[A] = { 218 | val (q, r) = index /% cs.length 219 | cs(r.toInt).get(q) 220 | } 221 | } 222 | 223 | (ibuf.nonEmpty, fbuf.nonEmpty) match { 224 | case (true, true) => 225 | Countable.ceither(allFinite, allInfinite).translate { 226 | case Left(a) => a 227 | case Right(a) => a 228 | } 229 | case (false, true) => 230 | allFinite 231 | case (true, false) => 232 | allInfinite 233 | case (false, false) => 234 | Countable.empty[A] 235 | } 236 | } 237 | 238 | // . 239 | implicit val cnothing: Indexable[Nothing] = 240 | empty[Nothing] 241 | 242 | // (). 243 | implicit val cunit: Indexable[Unit] = 244 | Indexable.UnsignedRange(Card.one, _ => ())(_ => Z.zero) 245 | 246 | // false, true. 247 | implicit val cboolean: Indexable[Boolean] = 248 | Indexable.UnsignedRange(Card.two, _ == Z.one)(b => if (b) Z.one else Z.zero) 249 | 250 | // TODO: the signed streams could be in a nicer order; right now the 251 | // negatives only come after you exhaust the positives. 252 | 253 | // 0, 1, 2, ..., 127, -128, -127, ..., -1. 254 | implicit val cbyte: Indexable[Byte] = 255 | Indexable.SignedRange(Card(Z.one << 8), _.toByte)(Z(_)) 256 | 257 | // 0, 1, 2, ..., 65535, -65536, -65535, ..., -1. 258 | implicit val cshort: Indexable[Short] = 259 | Indexable.SignedRange(Card(Z.one << 16), _.toShort)(Z(_)) 260 | 261 | // 0, 1, 2, ..., 65535, -65536, -65535, ..., -1. 262 | implicit val cchar: Indexable[Char] = 263 | Indexable.UnsignedRange(Card(Z.one << 16), _.toChar)(Z(_)) 264 | 265 | // 0, 1, 2, ... -1. 266 | implicit val cint: Indexable[Int] = 267 | Indexable.SignedRange(Card(Z.one << 32), _.toInt)(Z(_)) 268 | 269 | // 0, 1, 2, ... -1. 270 | implicit val clong: Indexable[Long] = 271 | Indexable.SignedRange(Card(Z.one << 64), _.toLong)(Z(_)) 272 | 273 | implicit def cfloat: Indexable[Float] = 274 | //cint.imap(intBitsToFloat)(floatToRawIntBits) 275 | IndexableFloat 276 | 277 | implicit def cdouble: Indexable[Double] = 278 | //clong.imap(longBitsToDouble)(doubleToRawLongBits) 279 | IndexableDouble 280 | 281 | implicit val cbigInt: Indexable[BigInt] = 282 | cz.imap(_.toBigInt)(Z(_)) 283 | 284 | implicit val cbigInteger: Indexable[java.math.BigInteger] = 285 | cz.imap(_.toBigInt.bigInteger)(Z(_)) 286 | 287 | // Option: could also be provided generically 288 | implicit def coption[A](implicit ev: Countable[A]): Countable[Option[A]] = 289 | new COption(ev) 290 | implicit def noption[A](implicit ev: Indexable[A]): Indexable[Option[A]] = 291 | new NOption(ev) 292 | 293 | // Either: could also be provided generically 294 | implicit def ceither[A, B](implicit eva: Countable[A], evb: Countable[B]): Countable[Either[A, B]] = 295 | CEither(eva, evb) 296 | implicit def neither[A, B](implicit eva: Indexable[A], evb: Indexable[B]): Indexable[Either[A, B]] = 297 | NEither(eva, evb) 298 | 299 | // Set 300 | implicit def cset[A](implicit ev: Countable[A]): Countable[Set[A]] = 301 | CSet(ev) 302 | implicit def nset[A](implicit ev: Indexable[A]): Indexable[Set[A]] = 303 | NSet(ev) 304 | 305 | // Map 306 | implicit def cmap[K, V](implicit evk: Countable[K], evv: Countable[V]): Countable[Map[K, V]] = 307 | CMap(evk, evv) 308 | 309 | // Function1 310 | implicit def cfunction1[A, B](implicit eva: Indexable[A], evb: Countable[B]): Countable[A => B] = 311 | CFunction1(eva, evb) 312 | 313 | // List 314 | implicit def clist[A](implicit ev: Countable[A]): Countable[List[A]] = 315 | CList(ev) 316 | implicit def nlist[A](implicit ev: Indexable[A]): Indexable[List[A]] = 317 | NList(ev) 318 | 319 | // Vector 320 | implicit def cvector[A](implicit ev: Countable[A]): Countable[Vector[A]] = 321 | CList(ev).translate(_.toVector) 322 | implicit def nvector[A](implicit ev: Indexable[A]): Indexable[Vector[A]] = 323 | NList(ev).imap(_.toVector)(_.toList) 324 | 325 | // // Stream 326 | // implicit def cstream[A](implicit ev: Countable[A]): Countable[Stream[A]] = 327 | // CList(ev).translate(_.toStream) 328 | // implicit def nstream[A](implicit ev: Indexable[A]): Indexable[Stream[A]] = 329 | // NList(ev).imap(_.toStream)(_.toList) 330 | 331 | // Array 332 | implicit def carray[A: ClassTag](implicit ev: Countable[A]): Countable[Array[A]] = 333 | CList(ev).translate(_.toArray) 334 | implicit def narray[A: ClassTag](implicit ev: Indexable[A]): Indexable[Array[A]] = 335 | NList(ev).imap(_.toArray)(_.toList) 336 | 337 | // String 338 | implicit val nstring: Indexable[String] = 339 | Indexable[List[Char]].imap(_.mkString)(_.toList) 340 | 341 | // Natural 342 | implicit val nnatural: Indexable[Natural] = 343 | new Indexable[Natural] { 344 | def cardinality: Card = Card.infinite 345 | def get(index: Z): Option[Natural] = Some(Natural(index.toBigInt)) 346 | def index(n: Natural): Z = Z(n.toBigInt) 347 | } 348 | 349 | // Rational 350 | implicit def nrational: Indexable[Rational] = 351 | IndexableRational 352 | } 353 | 354 | abstract class Countable1 extends Countable0 { 355 | 356 | implicit def ihgeneric[A, H <: HList](implicit gen: Generic.Aux[A, H], evh: HIndexable[H]): Indexable[A] = 357 | ihlist(evh).imap(gen.from)(gen.to) 358 | 359 | implicit def ihlist[H <: HList](implicit evh: HIndexable[H]): Indexable[H] = 360 | new HIndexable.ToIndexable(evh) 361 | 362 | implicit def icgeneric[A, C <: Coproduct](implicit gen: Generic.Aux[A, C], evc: CIndexable[C]): Indexable[A] = 363 | icoproduct(evc).imap(gen.from)(gen.to) 364 | 365 | implicit def icoproduct[C <: Coproduct](implicit evc: CIndexable[C]): Indexable[C] = 366 | new CIndexable.ToIndexable(evc) 367 | } 368 | 369 | abstract class Countable0 { 370 | 371 | implicit def chgeneric[A, H <: HList](implicit gen: Generic.Aux[A, H], evh: HCountable[H]): Countable[A] = 372 | chlist(evh).translate(gen.from) 373 | 374 | implicit def chlist[H <: HList](implicit evh: HCountable[H]): Countable[H] = 375 | new HCountable.ToCountable(evh) 376 | 377 | implicit def ccgeneric[A, C <: Coproduct](implicit gen: Generic.Aux[A, C], evc: CCountable[C]): Countable[A] = 378 | ccoproduct(evc).translate(gen.from) 379 | 380 | implicit def ccoproduct[C <: Coproduct](implicit evc: CCountable[C]): Countable[C] = 381 | new CCountable.ToCountable(evc) 382 | } 383 | 384 | /** 385 | * Indexable[A] represents Countable[A] instances which are 386 | * invertible. 387 | * 388 | * In theory any Countable[A] instance should be invertible (we should 389 | * be able to reverse the procedure). In practice, some values we 390 | * generate are too large or opaque to be able to reverse in general 391 | * (for example functions). In other cases the user may not be able to 392 | * take the time to define the instance. 393 | * 394 | * Indexable instances are very useful for testing the correctness of 395 | * Countable instances, so users are encouraged to prefer Indexable 396 | * where possible. 397 | */ 398 | trait Indexable[A] extends Countable[A] { self => 399 | def index(a: A): Z 400 | 401 | /** 402 | * Map an Indexable[A] into an Indexable[B]. 403 | * 404 | * We require a bijection (represented as `f` and `g`), i.e. an 405 | * invertible function. 406 | */ 407 | def imap[B](f: A => B)(g: B => A): Indexable[B] = 408 | new Indexable[B] { 409 | def cardinality: Card = self.cardinality 410 | def get(index: Z): Option[B] = self.get(index).map(f) 411 | def index(b: B): Z = self.index(g(b)) 412 | } 413 | 414 | /** 415 | * Drop the first k items from the indexable instance. 416 | * 417 | * Note that the result is not a lawful countable instance, unless 418 | * the subset of A that remains is translated onto a new type. 419 | */ 420 | override def drop(k: Int): Indexable[A] = 421 | new Indexable[A] { 422 | require(k >= 0) 423 | def cardinality: Card = self.cardinality - k 424 | def get(index: Z): Option[A] = self.get(index + k) 425 | def index(a: A): Z = self.index(a) - k 426 | } 427 | 428 | /** 429 | * Permuate this indexable's enumeration into a different 430 | * enumeration. 431 | * 432 | * This method uses a permutation on A values (the values being 433 | * counted). 434 | */ 435 | override def permute(p: Permutation[A]): Indexable[A] = 436 | new Indexable[A] { 437 | def cardinality: Card = 438 | self.cardinality 439 | def get(index: Z): Option[A] = 440 | self.get(index).map(p(_)) 441 | def index(a: A): Z = 442 | self.index(p(a)) 443 | } 444 | 445 | /** 446 | * Permuate this indexable's enumeration into a different 447 | * enumeration. 448 | * 449 | * This method uses a permutation on numbers (the indices being 450 | * enumerated). 451 | */ 452 | override def copermute(p: Permutation[Z]): Indexable[A] = 453 | new Indexable[A] { 454 | def cardinality: Card = 455 | self.cardinality 456 | def get(index: Z): Option[A] = 457 | self.get(p(index)) 458 | def index(a: A): Z = 459 | p(self.index(a)) 460 | } 461 | } 462 | 463 | object Indexable { 464 | 465 | def apply[A](implicit ev: Indexable[A]): Indexable[A] = ev 466 | 467 | case class SignedRange[A](cardinality: Card, f: Z => A)(g: A => Z) extends Indexable[A] { 468 | def get(index: Z): Option[A] = 469 | if (!cardinality.contains(index)) None 470 | else Countable.cz.get(index).map(n => f(-n)) 471 | def index(a: A): Z = 472 | Countable.cz.index(-g(a)) 473 | } 474 | 475 | // helpful class for defining finite instances derived from integer ranges. 476 | case class UnsignedRange[A](cardinality: Card, f: Z => A)(g: A => Z) extends Indexable[A] { 477 | def get(index: Z): Option[A] = 478 | if (!cardinality.contains(index)) None 479 | else Some(f(index)) 480 | def index(a: A): Z = 481 | g(a) 482 | } 483 | } 484 | -------------------------------------------------------------------------------- /core/src/test/scala/kronecker/CountableLaws.scala: -------------------------------------------------------------------------------- 1 | package kronecker 2 | 3 | import cats.kernel.Eq 4 | import java.lang.Double.doubleToRawLongBits 5 | import java.lang.Float.floatToRawIntBits 6 | import org.scalacheck.{Arbitrary, Prop, Properties} 7 | import org.scalacheck.Prop.{forAllNoShrink => forAll} 8 | import scala.reflect.runtime.universe.TypeTag 9 | import shapeless._ 10 | import spire.math.Rational 11 | 12 | // we need to avoid the long/double instances 13 | import cats.kernel.instances.boolean._ 14 | import cats.kernel.instances.byte._ 15 | import cats.kernel.instances.char._ 16 | import cats.kernel.instances.either._ 17 | import cats.kernel.instances.int._ 18 | import cats.kernel.instances.list._ 19 | import cats.kernel.instances.long._ 20 | import cats.kernel.instances.map._ 21 | import cats.kernel.instances.option._ 22 | import cats.kernel.instances.set._ 23 | import cats.kernel.instances.short._ 24 | import cats.kernel.instances.string._ 25 | import cats.kernel.instances.tuple._ 26 | import cats.kernel.instances.unit._ 27 | import cats.kernel.instances.vector._ 28 | 29 | import Testing._ 30 | 31 | trait CountableLaws[A] { self: Properties => 32 | 33 | implicit def equiv: Eq[A] 34 | 35 | def ev: Countable[A] 36 | def tt: TypeTag[A] 37 | 38 | def eqv[B](b1: B, b2: B)(implicit eb: Eq[B]): Boolean = 39 | eb.eqv(b1, b2) 40 | 41 | val card = ev.cardinality 42 | 43 | property("get(i) is defined iff i < cardinality") = 44 | forAll { (i: Z) => 45 | card.contains(i) == ev.get(i).isDefined 46 | } 47 | 48 | property("get(i) = get(j) iff i = j") = { 49 | forAll { (i1: Z, i2: Z) => 50 | val (o1, o2) = (ev.get(i1), ev.get(i2)) 51 | (card.contains(i1), card.contains(i2)) match { 52 | case (true, true) => 53 | val lhs = eqv(o1.get, o2.get) 54 | val rhs = (i1 == i2) 55 | val res = lhs == rhs 56 | if (!res) println(s"card=$card i1=$i1 i2=$i2 o1=$o1 o2=$o2 lhs=$lhs rhs=$rhs res=$res") 57 | res 58 | case (true, false) => 59 | val lhs = o1.isDefined 60 | val rhs = o2.isEmpty 61 | val res = lhs && rhs 62 | if (!res) println(s"card=$card i1=$i1 i2=$i2 o1=$o1 o2=$o2 lhs=$lhs rhs=$rhs res=$res") 63 | res 64 | case (false, true) => 65 | val lhs = o1.isEmpty 66 | val rhs = o2.isDefined 67 | val res = lhs && rhs 68 | if (!res) println(s"card=$card i1=$i1 i2=$i2 o1=$o1 o2=$o2 lhs=$lhs rhs=$rhs res=$res") 69 | res 70 | case (false, false) => 71 | val lhs = o1.isEmpty 72 | val rhs = o2.isEmpty 73 | val res = lhs && rhs 74 | if (!res) println(s"card=$card i1=$i1 i2=$i2 o1=$o1 o2=$o2 lhs=$lhs rhs=$rhs res=$res") 75 | res 76 | } 77 | } 78 | } 79 | } 80 | 81 | trait WeakIndexableLaws[A] extends CountableLaws[A] { self: Properties => 82 | 83 | def ev: Indexable[A] 84 | 85 | def repr(n0: Z): String = { 86 | if (n0 == 0) return "0" 87 | var s = "" 88 | var n = n0 89 | while (n != 0) { 90 | val (q, r) = n /% 4 91 | s = r.toString + s 92 | n = q 93 | } 94 | s 95 | } 96 | 97 | property("sanity check") = { 98 | val ns = (0 to 100) 99 | .flatMap { i => ev.get(Z(i)).map(a => (i, a, ev.index(a))) } 100 | .filter { case (i, a, n) => i != n } 101 | .map { case (i, a, n) => 102 | val o = ev.get(n) 103 | s"i=$i (${repr(i)}) a=$a -> i2=$n (${repr(n)}) a2=$o" 104 | } 105 | .toList 106 | Prop(ns.isEmpty) :| s"$ns should be empty" 107 | } 108 | 109 | 110 | property("get(i).forall(index(_) == i)") = 111 | forAll { (i: Z) => 112 | ev.get(i) match { 113 | case None => 114 | Prop(true) 115 | case Some(a) => 116 | val j = ev.index(a) 117 | Prop(j == i) :| s"$j == $i" 118 | } 119 | } 120 | } 121 | 122 | trait StrongIndexableLaws[A] extends WeakIndexableLaws[A] { self: Properties => 123 | 124 | implicit def arb: Arbitrary[A] 125 | 126 | property("0 <= index(a) < cardinality") = 127 | forAll { (a: A) => 128 | card.contains(ev.index(a)) 129 | } 130 | 131 | property("get(index(a)) = Some(a)") = 132 | forAll { (a: A) => 133 | val i = ev.index(a) 134 | val o = ev.get(i) 135 | //val ok = o == Option(a) 136 | val ok = eqv(o, Option(a)) 137 | if (!ok) println(s"a=$a i=$i o=$o") 138 | ok 139 | } 140 | 141 | property("index(a1) = index(a2) iff a1 = a2") = 142 | forAll { (a1: A, a2: A) => 143 | val i1 = ev.index(a1) 144 | val i2 = ev.index(a2) 145 | //val ok = (i1 == i2) == (a1 == a2) 146 | val ok = (i1 == i2) == eqv(a1, a2) 147 | if (!ok) println(s"a1=$a1, a2=$a2, i1=$i1, i2=$i2") 148 | ok 149 | } 150 | } 151 | 152 | abstract class CountableTests[A](implicit val equiv: Eq[A], val ev: Countable[A], val tt: TypeTag[A]) 153 | extends Properties(s"CountableTests[${tt.tpe}]") with CountableLaws[A] 154 | 155 | abstract class WeakIndexableTests[A](implicit val equiv: Eq[A], val ev: Indexable[A], val tt: TypeTag[A]) 156 | extends Properties(s"WeakIndexableTests[${tt.tpe}]") with WeakIndexableLaws[A] 157 | 158 | abstract class StrongIndexableTests[A](implicit val equiv: Eq[A], val ev: Indexable[A], val arb: Arbitrary[A], val tt: TypeTag[A]) 159 | extends Properties(s"StrongIndexableTests[${tt.tpe}]") with StrongIndexableLaws[A] 160 | 161 | object EqInstances { 162 | 163 | // we want particular bit patterns of NaNs to equal themselves 164 | 165 | implicit val nanEqDouble: Eq[Double] = 166 | new Eq[Double] { 167 | def eqv(x: Double, y: Double): Boolean = 168 | doubleToRawLongBits(x) == doubleToRawLongBits(y) 169 | } 170 | 171 | implicit val nanEqFloat: Eq[Float] = 172 | new Eq[Float] { 173 | def eqv(x: Float, y: Float): Boolean = 174 | floatToRawIntBits(x) == floatToRawIntBits(y) 175 | } 176 | 177 | implicit val eqNothing: Eq[Nothing] = 178 | Eq.fromUniversalEquals 179 | 180 | implicit val eqHNil: Eq[HNil] = 181 | new Eq[HNil] { 182 | def eqv(x: HNil, y: HNil): Boolean = true 183 | } 184 | 185 | implicit def eqHCons[A, H <: HList](implicit eqa: Eq[A], eqh: Eq[H]): Eq[A :: H] = 186 | new Eq[A :: H] { 187 | def eqv(x: A :: H, y: A :: H): Boolean = 188 | eqa.eqv(x.head, y.head) && eqh.eqv(x.tail, y.tail) 189 | } 190 | 191 | implicit val eqCNil: Eq[CNil] = 192 | new Eq[CNil] { 193 | def eqv(x: CNil, y: CNil): Boolean = true 194 | } 195 | 196 | implicit def eqCCons[A, C <: Coproduct](implicit eqa: Eq[A], eqc: Eq[C]): Eq[A :+: C] = 197 | new Eq[A :+: C] { 198 | def eqv(x: A :+: C, y: A :+: C): Boolean = 199 | (x, y) match { 200 | case (Inl(xa), Inl(ya)) => eqa.eqv(xa, ya) 201 | case (Inr(xc), Inr(yc)) => eqc.eqv(xc, yc) 202 | case _ => false 203 | } 204 | } 205 | 206 | implicit def eqFn1[A, B]: Eq[A => B] = 207 | new Eq[A => B] { 208 | def eqv(x: A => B, y: A => B): Boolean = x == y //FIXME 209 | } 210 | 211 | implicit val eqCountable0: Eq[Types.IsCountable0] = Eq.fromUniversalEquals 212 | implicit val eqCountable1: Eq[Types.IsCountable1] = Eq.fromUniversalEquals 213 | implicit val eqCountable256: Eq[Types.IsCountable256] = Eq.fromUniversalEquals 214 | implicit val eqCountableZ: Eq[Types.IsCountableZ] = Eq.fromUniversalEquals 215 | 216 | implicit val eqIndexable0: Eq[Types.IsIndexable0] = Eq.fromUniversalEquals 217 | implicit val eqIndexable1: Eq[Types.IsIndexable1] = Eq.fromUniversalEquals 218 | implicit val eqIndexable256: Eq[Types.IsIndexable256] = Eq.fromUniversalEquals 219 | implicit val eqIndexableZ: Eq[Types.IsIndexableZ] = Eq.fromUniversalEquals 220 | } 221 | 222 | import EqInstances._ 223 | 224 | object CNothingLaws extends CountableTests[Nothing] 225 | object CUnitLaws extends StrongIndexableTests[Unit] 226 | object CBooleanLaws extends StrongIndexableTests[Boolean] 227 | object CByteLaws extends StrongIndexableTests[Byte] 228 | object CShortLaws extends StrongIndexableTests[Short] 229 | object CCharLaws extends StrongIndexableTests[Char] 230 | object CIntLaws extends StrongIndexableTests[Int] 231 | object CLongLaws extends StrongIndexableTests[Long] 232 | object CFloatLaws extends StrongIndexableTests[Float] 233 | object CDoubleLaws extends StrongIndexableTests[Double] 234 | object CZLaws extends StrongIndexableTests[Z] 235 | object CRationalLaws extends StrongIndexableTests[Rational] 236 | object CStringLaws extends StrongIndexableTests[String] 237 | 238 | object COptionUnitLaws extends StrongIndexableTests[Option[Unit]] 239 | object COptionBooleanLaws extends StrongIndexableTests[Option[Boolean]] 240 | object COptionIntLaws extends StrongIndexableTests[Option[Int]] 241 | object CEitherUnitUnit extends StrongIndexableTests[Either[Unit, Unit]] 242 | object CEitherIntByte extends StrongIndexableTests[Either[Int, Byte]] 243 | object CTupleBooleanBoolean extends WeakIndexableTests[(Boolean, Boolean)] 244 | object CTupleLongIntShortByte extends WeakIndexableTests[(Long, Int, Short, Byte)] 245 | object CHListBBBB extends WeakIndexableTests[Byte :: Byte :: Byte :: Byte :: HNil] 246 | 247 | object COptionStringLaws extends StrongIndexableTests[Option[String]] 248 | object CEitherByteZ extends StrongIndexableTests[Either[Byte, Z]] 249 | object CEitherZByte extends StrongIndexableTests[Either[Z, Byte]] 250 | object CEitherZZ extends StrongIndexableTests[Either[Z, Z]] 251 | object CSetBoolean extends StrongIndexableTests[Set[Boolean]] 252 | object CSetByte extends StrongIndexableTests[Set[Byte]] 253 | //object CSetShort extends StrongIndexableTests[Set[Short]] // passes, but is slow 254 | object CSetShort extends WeakIndexableTests[Set[Short]] 255 | object CSetInt extends WeakIndexableTests[Set[Int]] 256 | object CSetZ extends WeakIndexableTests[Set[Z]] 257 | object CListBoolean extends StrongIndexableTests[List[Boolean]] 258 | object CListByte extends StrongIndexableTests[List[Byte]] 259 | object CListInt extends StrongIndexableTests[List[Int]] 260 | object CListZ extends StrongIndexableTests[List[Z]] 261 | object CTupleZZZ extends WeakIndexableTests[(Z, Z, Z)] 262 | object CTupleZByteByte extends WeakIndexableTests[(Z, Byte, Byte)] 263 | 264 | object CMapBB extends CountableTests[Map[Byte, Byte]] 265 | object CMapBS extends CountableTests[Map[Byte, String]] 266 | object CMapSB extends CountableTests[Map[String, Byte]] 267 | object CMapSS extends CountableTests[Map[String, String]] 268 | 269 | object CFunction22 extends CountableTests[Boolean => Boolean] 270 | object CFunctionBB extends CountableTests[Byte => Byte] 271 | object CFunctionZB extends CountableTests[Z => Byte] 272 | 273 | object Types { 274 | 275 | sealed abstract class CCompanion[A](c: Countable[A]) { 276 | implicit val instance: Countable[A] = c 277 | } 278 | 279 | sealed abstract class ICompanion[A](i: Indexable[A]) { 280 | implicit val instance: Indexable[A] = i 281 | } 282 | 283 | sealed trait IsCountable0 284 | case class IsCountable1() 285 | case class IsCountable256(value: Byte) 286 | case class IsCountableZ(value: Z) 287 | 288 | object IsCountable0 extends CCompanion(Countable.empty[IsCountable0]) 289 | object Countable1 extends CCompanion(Countable.singleton(IsCountable1())) 290 | object Countable256 extends CCompanion(Countable[Byte].translate(IsCountable256(_))) 291 | object CountableZ extends CCompanion(Countable[Z].translate(IsCountableZ(_))) 292 | 293 | sealed trait IsIndexable0 294 | case class IsIndexable1() 295 | case class IsIndexable256(value: Byte) 296 | case class IsIndexableZ(value: Z) 297 | 298 | object IsIndexable0 extends ICompanion(Countable.empty[IsIndexable0]) 299 | object Indexable1 extends ICompanion(Countable.singleton(IsIndexable1())) 300 | object Indexable256 extends ICompanion(Indexable[Byte].imap(IsIndexable256(_))(_.value)) 301 | object IndexableZ extends ICompanion(Indexable[Z].imap(IsIndexableZ(_))(_.value)) 302 | } 303 | 304 | import Types._ 305 | 306 | // generic countable 307 | 308 | object IsCountable0 extends CountableTests[IsCountable0] 309 | object OptionIsCountable0 extends CountableTests[Option[IsCountable0]] 310 | object ListIsCountable0 extends CountableTests[List[IsCountable0]] 311 | object SetIsCountable0 extends CountableTests[Set[IsCountable0]] 312 | object VectorIsCountable0 extends CountableTests[Vector[IsCountable0]] 313 | object MapKeyIsCountable0 extends CountableTests[Map[IsCountable0, Z]] 314 | object MapValfIsCountable0 extends CountableTests[Map[Z, IsCountable0]] 315 | object EitherIsCountable0 extends CountableTests[Either[Unit, IsCountable0]] 316 | object PairIsCountable0 extends CountableTests[(IsCountable0, IsCountable0)] 317 | 318 | object IsCountable1 extends CountableTests[IsCountable1] 319 | object OptionIsCountable1 extends CountableTests[Option[IsCountable1]] 320 | // object ListIsCountable1 extends CountableTests[List[IsCountable1]] // slow for large indices 321 | object SetIsCountable1 extends CountableTests[Set[IsCountable1]] 322 | // object VectorIsCountable1 extends CountableTests[Vector[IsCountable1]] // slow for large indices 323 | object MapKeyIsCountable1 extends CountableTests[Map[IsCountable1, Z]] 324 | object MapValfIsCountable1 extends CountableTests[Map[Z, IsCountable1]] 325 | object EitherIsCountable1 extends CountableTests[Either[Unit, IsCountable1]] 326 | object PairIsCountable1 extends CountableTests[(IsCountable1, IsCountable1)] 327 | 328 | object IsCountable256 extends CountableTests[IsCountable256] 329 | object OptionIsCountable256 extends CountableTests[Option[IsCountable256]] 330 | object ListIsCountable256 extends CountableTests[List[IsCountable256]] 331 | object SetIsCountable256 extends CountableTests[Set[IsCountable256]] 332 | object VectorIsCountable256 extends CountableTests[Vector[IsCountable256]] 333 | object MapKeyIsCountable256 extends CountableTests[Map[IsCountable256, Z]] 334 | object MapValfIsCountable256 extends CountableTests[Map[Z, IsCountable256]] 335 | object EitherIsCountable256 extends CountableTests[Either[Unit, IsCountable256]] 336 | object PairIsCountable256 extends CountableTests[(IsCountable256, IsCountable256)] 337 | 338 | object IsCountableZ extends CountableTests[IsCountableZ] 339 | object OptionIsCountableZ extends CountableTests[Option[IsCountableZ]] 340 | object ListIsCountableZ extends CountableTests[List[IsCountableZ]] 341 | object SetIsCountableZ extends CountableTests[Set[IsCountableZ]] 342 | object VectorIsCountableZ extends CountableTests[Vector[IsCountableZ]] 343 | object MapKeyIsCountableZ extends CountableTests[Map[IsCountableZ, Z]] 344 | object MapValfIsCountableZ extends CountableTests[Map[Z, IsCountableZ]] 345 | object EitherIsCountableZ extends CountableTests[Either[Unit, IsCountableZ]] 346 | object PairIsCountableZ extends CountableTests[(IsCountableZ, IsCountableZ)] 347 | 348 | object CCoproduct0 extends CountableTests[CNil] 349 | object CCoproduct1 extends CountableTests[IsCountable256 :+: CNil] 350 | object CCoproduct2 extends CountableTests[IsCountableZ :+: CNil] 351 | object CCoproduct3 extends CountableTests[IsCountable256 :+: IsCountableZ :+: CNil] 352 | object CCoproduct4 extends CountableTests[IsCountableZ :+: IsCountable256 :+: CNil] 353 | 354 | object CHList0 extends CountableTests[HNil] 355 | object CHList1 extends CountableTests[IsCountable256 :: HNil] 356 | object CHList2 extends CountableTests[IsCountableZ :: HNil] 357 | object CHList3 extends CountableTests[IsCountable256 :: IsCountableZ :: HNil] 358 | object CHList4 extends CountableTests[IsCountableZ :: IsCountable256 :: HNil] 359 | 360 | // generic indexable 361 | 362 | object IsIndexable0 extends WeakIndexableTests[IsIndexable0] 363 | object OptionIsIndexable0 extends WeakIndexableTests[Option[IsIndexable0]] 364 | object ListIsIndexable0 extends WeakIndexableTests[List[IsIndexable0]] 365 | object SetIsIndexable0 extends WeakIndexableTests[Set[IsIndexable0]] 366 | object VectorIsIndexable0 extends WeakIndexableTests[Vector[IsIndexable0]] 367 | // object MapKeyIsIndexable0 extends WeakIndexableTests[Map[IsIndexable0, Z]] 368 | // object MapValfIsIndexable0 extends WeakIndexableTests[Map[Z, IsIndexable0]] 369 | object EitherIsIndexable0 extends WeakIndexableTests[Either[Unit, IsIndexable0]] 370 | object PairIsIndexable0 extends WeakIndexableTests[(IsIndexable0, IsIndexable0)] 371 | 372 | object IsIndexable1 extends WeakIndexableTests[IsIndexable1] 373 | object OptionIsIndexable1 extends WeakIndexableTests[Option[IsIndexable1]] 374 | // object ListIsIndexable1 extends WeakIndexableTests[List[IsIndexable1]] // slow for large indices 375 | object SetIsIndexable1 extends WeakIndexableTests[Set[IsIndexable1]] 376 | // object VectorIsIndexable1 extends WeakIndexableTests[Vector[IsIndexable1]] // slow for large indices 377 | // object MapKeyIsIndexable1 extends WeakIndexableTests[Map[IsIndexable1, Z]] 378 | // object MapValfIsIndexable1 extends WeakIndexableTests[Map[Z, IsIndexable1]] 379 | object EitherIsIndexable1 extends WeakIndexableTests[Either[Unit, IsIndexable1]] 380 | object PairIsIndexable1 extends WeakIndexableTests[(IsIndexable1, IsIndexable1)] 381 | 382 | object IsIndexable256 extends WeakIndexableTests[IsIndexable256] 383 | object OptionIsIndexable256 extends WeakIndexableTests[Option[IsIndexable256]] 384 | object ListIsIndexable256 extends WeakIndexableTests[List[IsIndexable256]] 385 | object SetIsIndexable256 extends WeakIndexableTests[Set[IsIndexable256]] 386 | object VectorIsIndexable256 extends WeakIndexableTests[Vector[IsIndexable256]] 387 | // object MapKeyIsIndexable256 extends WeakIndexableTests[Map[IsIndexable256, Z]] 388 | // object MapValfIsIndexable256 extends WeakIndexableTests[Map[Z, IsIndexable256]] 389 | object EitherIsIndexable256 extends WeakIndexableTests[Either[Unit, IsIndexable256]] 390 | object PairIsIndexable256 extends WeakIndexableTests[(IsIndexable256, IsIndexable256)] 391 | 392 | object IsIndexableZ extends WeakIndexableTests[IsIndexableZ] 393 | object OptionIsIndexableZ extends WeakIndexableTests[Option[IsIndexableZ]] 394 | object ListIsIndexableZ extends WeakIndexableTests[List[IsIndexableZ]] 395 | object SetIsIndexableZ extends WeakIndexableTests[Set[IsIndexableZ]] 396 | object VectorIsIndexableZ extends WeakIndexableTests[Vector[IsIndexableZ]] 397 | // object MapKeyIsIndexableZ extends WeakIndexableTests[Map[IsIndexableZ, Z]] 398 | // object MapValfIsIndexableZ extends WeakIndexableTests[Map[Z, IsIndexableZ]] 399 | object EitherIsIndexableZ extends WeakIndexableTests[Either[Unit, IsIndexableZ]] 400 | object PairIsIndexableZ extends WeakIndexableTests[(IsIndexableZ, IsIndexableZ)] 401 | 402 | object ICoproduct0 extends WeakIndexableTests[CNil] 403 | object ICoproduct1 extends WeakIndexableTests[IsIndexable256 :+: CNil] 404 | object ICoproduct2 extends WeakIndexableTests[IsIndexableZ :+: CNil] 405 | object ICoproduct3 extends WeakIndexableTests[IsIndexable256 :+: IsIndexableZ :+: CNil] 406 | object ICoproduct4 extends WeakIndexableTests[IsIndexableZ :+: IsIndexable256 :+: CNil] 407 | 408 | object IHList0 extends WeakIndexableTests[HNil] 409 | object IHList1 extends WeakIndexableTests[IsIndexable256 :: HNil] 410 | object IHList2 extends WeakIndexableTests[IsIndexableZ :: HNil] 411 | object IHList3 extends WeakIndexableTests[IsIndexable256 :: IsIndexableZ :: HNil] 412 | object IHList4 extends WeakIndexableTests[IsIndexableZ :: IsIndexable256 :: HNil] 413 | --------------------------------------------------------------------------------