// -----------------------------------------------------------------
// Computing the greatest common divisor by the Euclidean Algorithm
// -----------------------------------------------------------------

// -----------------------------------------------------------------
// 1. the domain
// -----------------------------------------------------------------

val N: ℕ;        // the domain size
type nat = ℕ[N]; // the domain itself

// -----------------------------------------------------------------
// 2. the theory 
// -----------------------------------------------------------------

// the predicate "m divides n"
pred divides(m:nat,n:nat) ⇔ ∃p:nat. m⋅p = n;

// the predicate "g is a gcd of m and n"
pred isgcd(g:nat,m:nat,n:nat) ⇔
  // g is a common divisor of m and n
  divides(g,m) ∧ divides(g,n) ∧
  // g is the greatest such value
  ¬∃g0:nat. divides(g0,m) ∧ divides(g0,n) ∧ g0 > g;
    
// the implicitly defined mathematical function whose result g is
// a gcd of m and n (is there always one and only one such g?)
fun gcd(m:nat,n:nat): nat
  requires m ≠ 0 ∨ n ≠ 0;
  ensures isgcd(result,m,n);
= choose g:nat with isgcd(g,m,n);

// -----------------------------------------------------------------
// 3. the knowledge derived from the theory 
// -----------------------------------------------------------------

// the core knowledge on which Euclid's algorithm is based
// are these theorems valid (i.e., true for all m and n)?
theorem gcd0(m:nat) ⇔ m≠0 ⇒ gcd(m,0) = m;
theorem gcd1(m:nat,n:nat) ⇔ m ≠ 0 ∨ n ≠ 0 ⇒ gcd(m,n) = gcd(n,m);
theorem gcd2(m:nat,n:nat) ⇔ 1 ≤ n ∧ n ≤ m ⇒ gcd(m,n) = gcd(m%n,n);

// -----------------------------------------------------------------
// 4. the algorithm derived from the knowledge
// -----------------------------------------------------------------

// Euclid's algorithm as a recursive function
// is the algorithm correct with respect to its specification?
fun gcdf(m:nat,n:nat): nat
  requires m≠0 ∨ n≠0;        // the precondition
  ensures isgcd(result,m,n); // the postcondition
  decreases m+n;             // the termination measure
= if m = 0 then n
  else if n = 0 then m
  else if m > n then gcdf(m%n,n)
  else gcdf(m,n%m);
  
// Euclid's algorithm as an iterative procedure
// is the algorithm correct with respect to its specification?
proc gcdp(m:nat,n:nat): nat
  requires m≠0 ∨ n≠0;        // the precondition
  ensures isgcd(result,m,n); // the postcondition
{
  var a:nat ≔ m;
  var b:nat ≔ n;
  while a > 0 ∧ b > 0 do
    invariant a ≠ 0 ∨ b ≠ 0;                       // a loop invariant
    invariant ∀r:nat. isgcd(r,a,b) ⇔ isgcd(r,m,n); // a loop invariant
    decreases a+b;                                 // the termination measure
  {
    if a > b then
      a ≔ a%b;
    else
      b ≔ b%a;
  }
  return if a = 0 then b else a;
}

// Euclid's algorith as a transition system
pred init(a:nat,b:nat) ⇔ a≠0 ∨ b≠0;
pred next(a:nat,b:nat,a0:nat,b0:nat) ⇔
  if a > b then
    a0 = a%b ∧ b0 = b
  else
    a0 = a ∧ b0 = b%a;
proc gcds(m:nat,n:nat): nat
  requires init(m,n);
  ensures isgcd(result,m,n);
{
  var a:nat = m;
  var b:nat = n;
  while a > 0 ∧ b > 0 do
  {
    choose a0:nat,b0:nat with next(a,b,a0,b0);
    a ≔ a0;
    b ≔ b0;
  }
  return if a = 0 then b else a;
}

// -----------------------------------------------------------------
// end of file
// -----------------------------------------------------------------



