import $ivy.`io.github.siddhartha-gadgil::provingground-core-jvm:0.1.1-SNAPSHOT`
To efficiently manipulate expressions in natural numbers, or more generally rings (and fields), proving-ground has special HoTT types wrapping scala types that are Rings, Rigs, Fields etc in the spire library. As a consequence:
The ring of natural numbers is an object NatRing. This has
import provingground._, induction._, scalahott._
import NatRing._
val n = "n" :: NatTyp
val m = "m" :: NatTyp
val k = "k" :: NatTyp
Spire implicits let us use the addition and multiplication operations.
import spire.math._
import spire.algebra._
import spire.implicits._
A sum gives a SigmaTerm, which only stores a set of terms being added.
n + m
(n + m) + n
Addition is commutative and associative, even when it involves repeated terms.
n + m == m + n
(n + m) + k == n + (m + k)
(n + n) + m == (n + m) + n
Similarly, multiplication is commutative and associative, and distributes over addition. Multiplication gives Pi-terms with parameter a map to exponents.
n * m == m * n
n * (m * k)
(n * m) * k
n * (m + k)
n *(m + k) == (n * m) + (n * k)
n + 1
1 + n
(1 + n) + 2
n * n
We can use the expressions from these functions in lambdas. For this we need correct substitution.
import HoTT._
val fn = lmbda(n)(n * n)
fn(3)
fn(k)
We can define a function f recursively on natural numbers, given the value f(0) and given f(n+1) as a (curryed) function of (n+1) and f(n). This expands for literals.
val m = lmbda(n)(prod(n + 1))
val factorial = Rec(1: Nat, m)
factorial(3)
factorial(5)
factorial(n)
val g = lmbda(k)(factorial(k * k))
g(3)
factorial(9)
If we apply a recursive function to a sum n+k with k a literal (say k = 2), then the result simplifies as much as possible by expanding tail recursively in the literal.
factorial(n + 1)
val fn = lmbda(n)(factorial(n + 1))
fn(1)
fn(4)
(n + 2) * (n + 1)
(3 * n) * n
n * (n * n)
(n * k) * k
k * (n * k)
(n * n) * n
factorial(n + 2)
Recursive expansion: We see an example of expansion as much as possible.
val func = lmbda(n)(factorial(n+ 2))
func(3)
func(k) == factorial(k) * (k + 2) * (k + 1)
1 + 2
findFactor(Literal(2), Literal(4))
findDivisibilty(Literal(2), Literal(4))
findDivisibilty(Literal(2), Literal(4)).map(_.typ)
findFactor(n *2, n* 4)
findFactor(n * 2, n * 2 * k)
findFactor(n * k, n * n * k)
findFactor(n * 2, n * 4 * k)
findFactor(n * 2, n * 7 * k)
findFactor(n * 2, n * 4 * k) == Some(k * 2)
findDivisibilty(n * 2, n * 4 * k)
divides(Literal(2))(Literal(3))
findDivisibilty(2, 4)
findDifference(n+ 4, n + 2)
findDifference(4, 2)
findDifferenceFlip(4, 2)
findLEQ(2, 4)
LEQ.unapply(leq(n)(k))
LEQ.unapply(leq(Literal(2))(Literal(4)))
val sg = leq(Literal(2))(Literal(4))
findDifference(n + 2, 2)
val x = NatTyp.Var
val eqn = sg.fibers(x).asInstanceOf[IdentityTyp[Nat]]
eqn.dom == NatTyp
eqn.lhs
findDifference(eqn.lhs, x)
x + 2
findDifference(x + 2, 2)
eqn.lhs == x + 2
findDifference(x + 2, x)
DIV.unapply(divides(n)(k))
divides(n)(k)
findFactor(n * k, n)
findFactor(k, n * k)
DIV.unapply(divides(n)(Literal(7)))