(* 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] *)

Lemma Zgcdn_worst_is_fibonacci : forall n a b,
0 < a < b ->
Zis_gcd a b (Zgcdn (S n) a b) ->
Zgcdn n a b <> Zgcdn (S n) a b ->
fibonacci (S n) <= a /\
fibonacci (S (S n)) <= b.