(* This program is free software; you can redistribute it and/or      *)
(* modify it under the terms of the GNU Lesser General Public License *)
(* as published by the Free Software Foundation; either version 2.1   *)
(* of the License, or (at your option) any later version.             *)
(*                                                                    *)
(* This program is distributed in the hope that it will be useful,    *)
(* but WITHOUT ANY WARRANTY; without even the implied warranty of     *)
(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the      *)
(* GNU General Public License for more details.                       *)
(*                                                                    *)
(* You should have received a copy of the GNU Lesser General Public   *)
(* License along with this program; if not, write to the Free         *)
(* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA *)
(* 02110-1301 USA                                                     *)
Open Scope Z_scope.
(** * Euclid gcd algorithm *)
(** This was previously part of Coq standard library (in Znumtheory). But the new binary algorithm used there is much faster and also easier to prove correct (for the variant that is executable in coq). *)
(** In this first variant, we use an explicit measure in [nat], and we prove later that using [2(d+1)] is enough, where [d] is the number of binary digits of the first argument. *)
Fixpoint Zgcdn (n:nat) : Z -> Z -> Z := fun a b =>
match n with
| O => 1 (* arbitrary, since n should be big enough *)
| S n => match a with
| Z0 => Zabs b
| Zpos _ => Zgcdn n (Zmod b a) a
| Zneg a => Zgcdn n (Zmod b (Zpos a)) (Zpos a)
end
end.
(* For technical reason, we don't use [Ndigit.Psize] but this ad-hoc version: [Psize p = S (Psiz p)]. *)
Fixpoint Psiz (p:positive) : nat :=
match p with
| xH => O
| xI p => S (Psiz p)
| xO p => S (Psiz p)
end.
Definition Zgcd_bound (a:Z) := match a with
| Z0 => S O
| Zpos p => let n := Psiz p in S (S (n+n))
| Zneg p => let n := Psiz p in S (S (n+n))
end.
Definition Zgcd a b := Zgcdn (Zgcd_bound a) a b.
(** A first obvious fact : [Zgcd a b] is positive. *)
(** We now prove that Zgcd is indeed a gcd. *)
(** 1) We prove a weaker & easier bound. *)
(** 2) For Euclid's algorithm, the worst-case situation corresponds to Fibonacci numbers. Let's define them: *)
Fixpoint fibonacci (n:nat) : Z :=
match n with
| O => 1
| S O => 1
| S (S n as p) => fibonacci p + fibonacci n
end.
(** 3) We prove that fibonacci numbers are indeed worst-case: for a given number [n], if we reach a conclusion about [gcd(a,b)] in exactly [n+1] loops, then [fibonacci (n+1)<=a /\ fibonacci(n+2)<=b] *)
(** 3b) We reformulate the previous result in a more positive way. *)
(** 4) The proposed bound leads to a fibonacci number that is big enough. *)
(* 5) the end: we glue everything together and take care of situations not corresponding to [0<a<b]. *)
(** A generalized gcd: it additionnally keeps track of the divisors. *)
Fixpoint Zggcdn (n:nat) : Z -> Z -> (Z*(Z*Z)) := fun a b =>
match n with
| O => (1,(a,b))   (*(Zabs b,(0,Zsgn b))*)
| S n => match a with
| Z0 => (Zabs b,(0,Zsgn b))
| Zpos _ =>
let (q,r) := Zdiv_eucl b a in   (* b = q*a+r *)
let (g,p) := Zggcdn n r a in
let (rr,aa) := p in        (* r = g *rr /\ a = g * aa *)
(g,(aa,q*aa+rr))
| Zneg a =>
let (q,r) := Zdiv_eucl b (Zpos a) in  (* b = q*(-a)+r *)
let (g,p) := Zggcdn n r (Zpos a) in
let (rr,aa) := p in         (* r = g*rr /\ (-a) = g * aa *)
(g,(-aa,q*aa+rr))
end
end.
Definition Zggcd a b : Z * (Z * Z) := Zggcdn (Zgcd_bound a) a b.
(** The first component of [Zggcd] is [Zgcd] *)
(** [Zggcd] always returns divisors that are coherent with its first output. *)

Lemma Zggcd_correct_divisors : forall a b,
let (g,p) := Zggcd a b in
let (aa,bb):=p in
a=g*aa /\ b=g*bb.