I'd like to use dafny to prove the following lemma about GCD: For all k natural numbers, if k|a and k|b, then k|gcd(a,b). I have the following code so far:
// Euclid's algorithm for computing the greatest common divisor
function gcd(a: nat, b: nat): nat
requires a > 0 && b > 0
{
if a == b then a else
if b > a then gcd(a, b - a) else
gcd(a - b, b)
}
predicate divides(a: nat, b:nat)
requires a > 0
{
exists k: nat :: b == k * a
}
lemma dividesLemma(a: nat, b: nat)
//k|a && k|b ==> k|gcd(a,b)
requires a > 0 && b > 0
ensures gcd(a,b) > 0
ensures forall k: nat :: divides(a,k) && divides(b,k) ==> divides(gcd(a,b),k)
{
if(a == b) {
assert a * 1 == gcd(a,b);
assert b * 1 == gcd(a,b);
} else if b > a {
if(divides(a, b)) {
assert divides(a,a);
assert divides(a,b);
assert divides(a, gcd(a,b));
} else {
dividesLemma(a, b - a);
}
} else {
if(divides(b, a)) {
assert divides(b,b);
assert divides(b,a);
assert divides(b, gcd(a,b));
} else {
dividesLemma(a, a - b);
}
}
}
I know how to do the proof for this by hand. I would consider the prime factorization of a and b and say that gcd(a,b) was the combined prime factorization such that we take the minimal number of primes from each prime factorization. For instance if a = 9 and b = 15, the prime factorization of 9 = 3x3 and the prime factorization of 15 = 3x5, so the gcd(9,5) = 3 since that's the minimal combination of their prime factoizations. Using this fact it should be clear that if k|b and k|a, k must contain those minimal primes. How can I express this using dafny? Currently, I'm considering the base case if a=b and if a|b or b|a, but not sure how to incorporate the fact that it's possible for a and b to not share common primes in their prime factorizations.
Any help would be much appreciated for this!
There is problem in how divides
is being called. I think
in ensures clauses you meant divides(k, a)
instead of divides(a, k)
similarly for divides(b, k)
and divides(gcd(a, b), k)
.
One way to go about this after recursive call to dividesLemma(a, b - a)
is
to use postcondition of method. Here we know forall k
such that k
divides a
and k
divides b - a
implies k
divides gcd(a, b-a)
. Using this information we try to prove required postcondition (code or proof is straightforward to follow)
dividesLemma(a, b - a);
assert gcd(a, b) == gcd(a, b-a);
assert forall k : nat :: k > 0 && divides(k, a) && divides(k, b-a) ==> divides(k, gcd(a, b));
forall k : nat | k > 0 && divides(k, a) && divides(k, b) ensures divides(k, gcd(a, b)) {
var m :| a == m * k;
var n :| b == n * k;
assert (b - a) == (n - m) * k;
assume n >= m;
assert divides(k, a);
assert divides(k, b-a);
// Implied from last assert forall
assert divides(k, gcd(a, b));
}
Here I am assuming n >= m
because divides
requires n-m
to be nat
, which can proved separately.
Also second recursive call should be dividesLemma(a - b, b)
.
function gcd(a: nat, b: nat): nat
requires a > 0 && b > 0
{
if a == b
then a
else if a < b
then gcd(a, b-a)
else gcd(a-b, b)
}
predicate divides(a: nat, b: nat)
requires a > 0 && b > 0
{
exists k: nat :: b == k * a
}
lemma helper(a: nat, b: nat, k : nat)
requires a > 0 && b > 0 && k > 0
requires divides(k, a) && divides(k, b)
requires b >= a
ensures exists m, n :: a == m * k && b == n * k && m <= n;
{ }
lemma dividesLemma(a: nat, b: nat)
decreases a + b
requires a > 0 && b > 0
ensures gcd(a, b) > 0
ensures forall k: nat :: k > 0 && divides(k, a) && divides(k, b) ==> divides(k, gcd(a, b))
{
if (a == b){
}
else if (b > a){
dividesLemma(a, b - a);
assert gcd(a, b) == gcd(a, b-a);
assert forall k : nat :: k > 0 && divides(k, a) && divides(k, b-a) ==> divides(k, gcd(a, b));
forall k : nat | k > 0 && divides(k, a) && divides(k, b) ensures divides(k, gcd(a, b)) {
helper(a, b, k);
var m, n :| a == m * k && b == n * k && m <= n;
assert b - a == (n - m) * k;
assert divides(k, b-a);
}
}
else {
dividesLemma(a - b, b);
assert gcd(a, b) == gcd(a - b, b);
assert forall k : nat :: k > 0 && divides(k, a-b) && divides(k, b) ==> divides(k, gcd(a, b));
forall k : nat | k > 0 && divides(k, a) && divides(k, b) ensures divides(k, gcd(a, b)) {
helper(b, a, k);
var m, n :| b == m * k && a == n * k && m <= n;
assert a - b == (n - m) * k;
assert divides(k, a-b);
}
}
}