Euclidean algorithm for ℕ #
This file sets up a version of the Euclidean algorithm that only works with natural numbers.
Given 0 < a, b
, it computes the unique (w, x, y, z, d)
such that the following identities hold:
a = (w + x) d
b = (y + z) d
w * z = x * y + 1
d
is then the gcd ofa
andb
, anda' := a / d = w + x
andb' := b / d = y + z
are coprime.
This story is closely related to the structure of SL₂(ℕ) (as a free monoid on two generators) and the theory of continued fractions.
Main declarations #
XgcdType
: Helper type in defining the gcd. Encapsulates(wp, x, y, zp, ap, bp)
. wherewp
zp
,ap
,bp
are the variables getting changed through the algorithm.IsSpecial
: Stateswp * zp = x * y + 1
IsReduced
: Statesap = a ∧ bp = b
Notes #
See Nat.Xgcd
for a very similar algorithm allowing values in ℤ
.
A term of XgcdType
is a system of six naturals. They should
be thought of as representing the matrix
[[w, x], [y, z]] = [[wp + 1, x], [y, zp + 1]]
together with the vector [a, b] = [ap + 1, bp + 1].
- wp : ℕ
wp
is a variable which changes through the algorithm. - x : ℕ
- y : ℕ
- zp : ℕ
zp
is a variable which changes through the algorithm. - ap : ℕ
ap
is a variable which changes through the algorithm. - bp : ℕ
bp
is a variable which changes through the algorithm.
Instances For
Equations
- PNat.instInhabitedXgcdType = { default := { wp := default, x := default, y := default, zp := default, ap := default, bp := default } }
Equations
- PNat.XgcdType.instSizeOfXgcdType = { sizeOf := fun (u : PNat.XgcdType) => u.bp }
The Repr
instance converts terms to strings in a way that
reflects the matrix/vector interpretation as above.
Equations
- One or more equations did not get rendered due to their size.
Equations
- PNat.XgcdType.w u = Nat.succPNat u.wp
Instances For
Equations
- PNat.XgcdType.z u = Nat.succPNat u.zp
Instances For
Equations
- PNat.XgcdType.a u = Nat.succPNat u.ap
Instances For
Equations
- PNat.XgcdType.b u = Nat.succPNat u.bp
Instances For
Equations
- PNat.XgcdType.r u = (u.ap + 1) % (u.bp + 1)
Instances For
Equations
- PNat.XgcdType.q u = (u.ap + 1) / (u.bp + 1)
Instances For
Equations
- PNat.XgcdType.qp u = PNat.XgcdType.q u - 1
Instances For
v = [sp + 1, tp + 1]
, check vp
Equations
- PNat.XgcdType.v u = (↑(PNat.XgcdType.w u) * ↑(PNat.XgcdType.a u) + u.x * ↑(PNat.XgcdType.b u), u.y * ↑(PNat.XgcdType.a u) + ↑(PNat.XgcdType.z u) * ↑(PNat.XgcdType.b u))
Instances For
IsSpecial
holds if the matrix has determinant one.
Instances For
IsSpecial'
is an alternative of IsSpecial
.
Equations
- PNat.XgcdType.IsSpecial' u = (PNat.XgcdType.w u * PNat.XgcdType.z u = Nat.succPNat (u.x * u.y))
Instances For
IsReduced
holds if the two entries in the vector are the
same. The reduction algorithm will produce a system with this
property, whose product vector is the same as for the original
system.
Equations
- PNat.XgcdType.IsReduced u = (u.ap = u.bp)
Instances For
flip
flips the placement of variables during the algorithm.
Equations
- PNat.XgcdType.flip u = { wp := u.zp, x := u.y, y := u.x, zp := u.wp, ap := u.bp, bp := u.ap }
Instances For
Properties of division with remainder for a / b.
The following function provides the starting point for our algorithm. We will apply an iterative reduction process to it, which will produce a system satisfying IsReduced. The gcd can be read off from this final system.
Equations
- PNat.XgcdType.start a b = { wp := 0, x := 0, y := 0, zp := 0, ap := ↑a - 1, bp := ↑b - 1 }
Instances For
finish
happens when the reducing process ends.
Equations
- PNat.XgcdType.finish u = { wp := u.wp, x := (u.wp + 1) * PNat.XgcdType.qp u + u.x, y := u.y, zp := u.y * PNat.XgcdType.qp u + u.zp, ap := u.bp, bp := u.bp }
Instances For
This is the main reduction step, which is used when u.r ≠ 0, or equivalently b does not divide a.
Equations
- PNat.XgcdType.step u = { wp := u.y * PNat.XgcdType.q u + u.zp, x := u.y, y := (u.wp + 1) * PNat.XgcdType.q u + u.x, zp := u.wp, ap := u.bp, bp := PNat.XgcdType.r u - 1 }
Instances For
We will apply the above step recursively. The following result is used to ensure that the process terminates.
The reduction step does not change the product vector.
We can now define the full reduction function, which applies step as long as possible, and then applies finish. Note that the "have" statement puts a fact in the local context, and the equation compiler uses this fact to help construct the full definition in terms of well-founded recursion. The same fact needs to be introduced in all the inductive proofs of properties given below.
Equations
- PNat.XgcdType.reduce u = if x : PNat.XgcdType.r u = 0 then PNat.XgcdType.finish u else PNat.XgcdType.flip (PNat.XgcdType.reduce (PNat.XgcdType.step u))
Instances For
Extended Euclidean algorithm
Equations
- PNat.xgcd a b = PNat.XgcdType.reduce (PNat.XgcdType.start a b)
Instances For
Final value of a / d
Equations
- PNat.gcdA' a b = Nat.succPNat ((PNat.xgcd a b).wp + (PNat.xgcd a b).x)
Instances For
Final value of b / d
Equations
- PNat.gcdB' a b = Nat.succPNat ((PNat.xgcd a b).y + (PNat.xgcd a b).zp)