Skip to content

Instantly share code, notes, and snippets.

@non
Last active March 24, 2019 14:25
Show Gist options
  • Save non/8396462 to your computer and use it in GitHub Desktop.
Save non/8396462 to your computer and use it in GitHub Desktop.
Type-level integers. Like shapeless' Nat, but supports negative numbers. Oh, and I've never actually run the code (but it compiles).
package quantity
// like Nat, but for integers
trait Z
// Pos means positive-or-zero, and Neg means negative-or-zero
trait Pos extends Z { type P <: Pos }
trait Neg extends Z { type N <: Neg }
class Zero extends Pos with Neg { type N = Zero }
case class PlusOne[P <: Pos]() extends Pos { type X = PlusOne[P] }
case class MinusOne[N <: Neg]() extends Neg { type X = MinusOne[N] }
object Z {
type PlusTwo[A <: Pos] = PlusOne[PlusOne[A]]
type _0 = Zero
type _1 = PlusOne[_0]
type _2 = PlusOne[_1]
type _3 = PlusOne[_2]
type _4 = PlusOne[_3]
type _5 = PlusOne[_4]
type _6 = PlusOne[_5]
type _7 = PlusOne[_6]
type _8 = PlusOne[_7]
type _9 = PlusOne[_8]
type _M1 = MinusOne[_0]
type _M2 = MinusOne[_M1]
type _M3 = MinusOne[_M2]
type _M4 = MinusOne[_M3]
type _M5 = MinusOne[_M4]
type _M6 = MinusOne[_M5]
type _M7 = MinusOne[_M6]
type _M8 = MinusOne[_M7]
type _M9 = MinusOne[_M8]
// C is the sum of A and B
trait Sum[A <: Z, B <: Z] { type Out <: Z }
object Sum {
def apply[A <: Z, B <: Z](implicit sum: Sum[A, B]): Aux[A, B, sum.Out] = sum
type Aux[A <: Z, B <: Z, C <: Z] = Sum[A, B] { type Out = C }
//implicit def plusZero[A <: Z]: Aux[A, Zero, A] = new Sum[A, Zero] { type Out = A }
implicit def zeroPlus[B <: Z]: Aux[Zero, B, B] = new Sum[Zero, B] { type Out = B }
implicit def posPlusPos[A <: Pos, B <: Pos]
(implicit sum: Sum[A, PlusOne[B]]): Aux[PlusOne[A], B, sum.Out] = new Sum[PlusOne[A], B] { type Out = sum.Out }
implicit def negPlusNeg[A <: Neg, B <: Neg]
(implicit sum: Sum[A, MinusOne[B]]): Aux[MinusOne[A], B, sum.Out] = new Sum[MinusOne[A], B] { type Out = sum.Out }
implicit def posPlusNeg[A <: Pos, B <: Neg]
(implicit sum: Sum[A, B]): Aux[PlusOne[A], MinusOne[B], sum.Out] = new Sum[PlusOne[A], MinusOne[B]] { type Out = sum.Out }
implicit def negPlusPos[A <: Neg, B <: Pos]
(implicit sum: Sum[A, B]): Aux[MinusOne[A], PlusOne[B], sum.Out] = new Sum[MinusOne[A], PlusOne[B]] { type Out = sum.Out }
}
// C is the difference of A and B
trait Diff[A <: Z, B <: Z] { type Out <: Z }
object Diff {
def apply[A <: Z, B <: Z](implicit diff: Diff[A, B]): Aux[A, B, diff.Out] = diff
type Aux[A <: Z, B <: Z, C <: Z] = Diff[A, B] { type Out = C }
implicit def minusZero[A <: Z]: Aux[A, Zero, A] = new Diff[A, Zero] { type Out = A }
implicit def posMinusPos[A <: Pos, B <: Pos](implicit diff: Diff[A, B]): Aux[PlusOne[A], PlusOne[B], diff.Out] =
new Diff[PlusOne[A], PlusOne[B]] { type Out = diff.Out }
implicit def negMinusNeg[A <: Neg, B <: Neg](implicit diff: Diff[A, B]): Aux[MinusOne[A], MinusOne[B], diff.Out] =
new Diff[MinusOne[A], MinusOne[B]] { type Out = diff.Out }
implicit def posMinusNeg[A <: Pos, B <: Neg](implicit diff: Diff[PlusOne[A], B]): Aux[A, MinusOne[B], diff.Out] =
new Diff[A, MinusOne[B]] { type Out = diff.Out }
implicit def negMinusPos[A <: Neg, B <: Pos](implicit diff: Diff[MinusOne[A], B]): Aux[A, PlusOne[B], diff.Out] =
new Diff[A, PlusOne[B]] { type Out = diff.Out }
}
// B is the negation of A
trait Negate[A <: Z] { type Out <: Z }
object Negate {
def apply[A <: Z](implicit n: Negate[A]): Aux[A, n.Out] = n
type Aux[A <: Z, B <: Z] = Negate[A] { type Out = B }
implicit def int[A <: Z](implicit diff: Diff[Zero, A]): Aux[A, diff.Out] =
new Negate[A] { type Out = diff.Out }
}
// C is the product of A and B
trait Prod[A <: Z, B <: Z] { type Out <: Z }
object Prod {
def apply[A <: Z, B <: Z](implicit prod: Prod[A, B]): Aux[A, B, prod.Out] = prod
type Aux[A <: Z, B <: Z, C <: Z] = Prod[A, B] { type Out = C }
implicit def timesZero[A <: Z]: Aux[A, Zero, Zero] = new Prod[A, Zero] { type Out = Zero }
implicit def zeroTimes[B <: Z]: Aux[Zero, B, Zero] = new Prod[Zero, B] { type Out = Zero }
implicit def posTimes[A <: Pos, B <: Z, C <: Z]
(implicit prod: Prod.Aux[A, B, C], sum: Sum[C, B]): Aux[PlusOne[A], B, sum.Out] =
new Prod[PlusOne[A], B] { type Out = sum.Out }
implicit def negTimes[A <: Neg, B <: Z, C <: Z]
(implicit prod: Prod.Aux[A, B, C], diff: Diff[C, B]): Aux[MinusOne[A], B, diff.Out] =
new Prod[MinusOne[A], B] { type Out = diff.Out }
}
trait Quot[A <: Z, B <: Z] { type Out <: Z }
object Quot {
def apply[A <: Z, B <: Z](implicit div: Quot[A, B]): Aux[A, B, div.Out] = div
type Aux[A <: Z, B <: Z, C <: Z] = Quot[A, B] { type Out = C }
// zero / pos,neg
implicit def zeroDivPos[A <: Pos]: Aux[Zero, PlusOne[A], Zero] =
new Quot[Zero, PlusOne[A]] { type Out = Zero }
implicit def zeroDivNeg[A <: Neg]: Aux[Zero, MinusOne[A], Zero] =
new Quot[Zero, MinusOne[A]] { type Out = Zero }
// pos / pos
implicit def posDivPos1[A <: Pos, B <: Pos]
(implicit lt: LT[A, B]): Aux[PlusOne[A], PlusOne[B], Zero] =
new Quot[PlusOne[A], PlusOne[B]] { type Out = Zero }
implicit def posDivPosN[A <: Pos, B <: Pos, C <: Pos, D <: Pos]
(implicit diff: Diff.Aux[PlusOne[A], B, C], div: Quot.Aux[C, B, D]): Aux[PlusOne[A], B, PlusOne[D]] =
new Quot[PlusOne[A], B] { type Out = PlusOne[D] }
// neg / pos
implicit def negDivPos1[A <: Neg, B <: Pos, C <: Pos]
(implicit na: Negate.Aux[A, C], lt: LT[C, B]): Aux[MinusOne[A], PlusOne[B], Zero] =
new Quot[MinusOne[A], PlusOne[B]] { type Out = Zero }
implicit def negDivPosN[A <: Neg, B <: Pos, C <: Neg, D <: Neg]
(implicit sum: Sum.Aux[MinusOne[A], B, C], div: Quot.Aux[C, B, D]): Aux[MinusOne[A], B, MinusOne[D]] =
new Quot[MinusOne[A], B] { type Out = MinusOne[D] }
// pos / neg
implicit def posDivNeg1[A <: Pos, B <: Neg, C <: Pos]
(implicit nb: Negate.Aux[B, C], lt: LT[A, C]): Aux[PlusOne[A], MinusOne[B], Zero] =
new Quot[PlusOne[A], MinusOne[B]] { type Out = Zero }
implicit def posDivNegN[A <: Pos, B <: Neg, C <: Pos, D <: Neg]
(implicit diff: Sum.Aux[PlusOne[A], B, C], div: Quot.Aux[C, B, D]): Aux[PlusOne[A], B, MinusOne[D]] =
new Quot[PlusOne[A], B] { type Out = MinusOne[D] }
// neg / neg
implicit def negDivNeg1[A <: Neg, B <: Neg]
(implicit lt: LT[B, A]): Aux[MinusOne[A], MinusOne[B], Zero] =
new Quot[MinusOne[A], MinusOne[B]] { type Out = Zero }
implicit def negDivNegN[A <: Neg, B <: Neg, C <: Neg, D <: Pos]
(implicit diff: Diff.Aux[MinusOne[A], B, C], div: Quot.Aux[C, B, D]): Aux[MinusOne[A], B, PlusOne[D]] =
new Quot[MinusOne[A], B] { type Out = PlusOne[D] }
}
// Out is the remainder of A divided by B
trait Mod[A <: Z, B <: Z] { type Out <: Z }
object Mod {
def apply[A <: Z, B <: Z](implicit mod: Mod[A, B]): Aux[A, B, mod.Out] = mod
type Aux[A <: Z, B <: Z, C <: Z] = Mod[A, B] { type Out = C }
implicit def modAux[A <: Z, B <: Z, C <: Z, D <: Z, E <: Z]
(implicit div: Quot.Aux[A, B, C], prod: Prod.Aux[C, B, D], diff: Diff.Aux[A, D, E]): Aux[A, B, E] =
new Mod[A, B] { type Out = E }
}
// Out is the GCD of A and B (currently broken when there is no gcd)
trait Gcd[A <: Z, B <: Z] { type Out <: Z }
object Gcd {
def apply[A <: Z, B <: Z](implicit gcd: Gcd[A, B]): Aux[A, B, gcd.Out] = gcd
type Aux[A <: Z, B <: Z, C <: Z] = Gcd[A, B] { type Out = C }
implicit def gcdZero[A <: Pos]: Aux[PlusOne[A], Zero, PlusOne[A]] =
new Gcd[PlusOne[A], Zero] { type Out = PlusOne[A] }
implicit def gcdN[A <: Pos, B <: Pos, C <: Pos, D <: Pos]
(implicit gcd: Aux[PlusOne[B], D, C], mod: Mod.Aux[PlusOne[A], PlusOne[B], D]): Aux[PlusOne[A], PlusOne[B], C] =
new Gcd[PlusOne[A], PlusOne[B]] { type Out = C }
}
// A is less than B
trait LT[A <: Z, B <: Z]
object LT {
def apply[A <: Z, B <: Z](implicit lt: A < B) = lt
type <[A <: Z, B <: Z] = LT[A, B]
implicit def lt1[B <: Pos] = new <[Zero, PlusOne[B]] {}
implicit def lt2[A <: Neg] = new <[MinusOne[A], Zero] {}
implicit def lt3[A <: Neg, B <: Pos] = new <[MinusOne[A], PlusOne[B]] {}
implicit def lt4[A <: Pos, B <: Pos](implicit lt : A < B) = new <[PlusOne[A], PlusOne[B]] {}
implicit def lt5[A <: Neg, B <: Neg](implicit lt : A < B) = new <[MinusOne[A], MinusOne[B]] {}
}
// A is less-than-or-equal-to B
trait LTEq[A <: Z, B <: Z]
object LTEq {
def apply[A <: Z, B <: Z](implicit lteq: A <= B) = lteq
type <=[A <: Z, B <: Z] = LTEq[A, B]
implicit def ltEq1 = new <=[Zero, Zero] {}
implicit def ltEq2[B <: Pos] = new <=[Zero, PlusOne[B]] {}
implicit def ltEq3[A <: Neg] = new <=[MinusOne[A], Zero] {}
implicit def ltEq4[A <: Pos, B <: Pos](implicit lteq : A <= B) = new <=[PlusOne[A], PlusOne[B]] {}
implicit def ltEq5[A <: Neg, B <: Neg](implicit lteq : A <= B) = new <=[MinusOne[A], MinusOne[B]] {}
}
// B is the absolute value of A
trait Abs[A <: Z] { type Out <: Pos }
object Abs {
def apply[A <: Z](implicit abs: Abs[A]): Aux[A, abs.Out] = abs
type Aux[A <: Z, B <: Pos] = Abs[A] { type Out = B }
implicit def posAbs[A <: Pos]: Aux[A, A] = new Abs[A] { type Out = A }
implicit def negAbs[A <: Neg, B <: Pos](implicit abs: Abs.Aux[A, B]): Aux[MinusOne[A], PlusOne[B]] =
new Abs[MinusOne[A]] { type Out = PlusOne[abs.Out] }
}
trait ToInt[N <: Z] { def apply(): Int }
object ToInt {
def apply[N <: Z](implicit toInt: ToInt[N]): ToInt[N] = toInt
implicit val toIntZero = new ToInt[Zero] { def apply() = 0 }
implicit def toIntPos[N <: Pos](implicit toIntN: ToInt[N]) =
new ToInt[PlusOne[N]] { def apply() = toIntN() + 1 }
implicit def toIntNeg[N <: Neg](implicit toIntN: ToInt[N]) =
new ToInt[MinusOne[N]] { def apply() = toIntN() - 1 }
}
val Zero: Zero = new Zero
val One: PlusOne[Zero] = new PlusOne[Zero]
val MinusOne: MinusOne[Zero] = new MinusOne[Zero]
}
@pnosko
Copy link

pnosko commented Feb 3, 2014

I am wondering, is this limited in size? Say I wanted a type-level nat 2^50, I don't suppose this would work, would it? I suppose the only way is to use singleton types, am I correct? Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment