(****************************************************************************)
(*                 The Calculus of Inductive Constructions                  *)
(*                                                                          *)
(*                                Projet Coq                                *)
(*                                                                          *)
(*                     INRIA        LRI-CNRS        ENS-CNRS                *)
(*              Rocquencourt         Orsay          Lyon                    *)
(*                                                                          *)
(*                                 Coq V6.3                                 *)
(*                               July 1st 1999                              *)
(*                                                                          *)
(****************************************************************************)
(* Certification of Imperative Programs                                     *)
(* Jean-Christophe Filliātre                                                *)
(****************************************************************************)
(*                                fact_int.v                                *)
(****************************************************************************)

(* Proof of an imperative program computing the factorial (over type Z) *)

Require Programs.
Require Omega.
Require ZArithRing.

(* We define the factorial as a relation... *)

Inductive fact : Z -> Z -> Prop :=
    fact_0 : (fact `0` `1`)
  | fact_S : (z,f:Z) (fact z f) -> (fact (Zs z) (Zmult (Zs z) f)).

(* ...and then we prove that it contains a function *)

Lemma fact_function : (z:Z) `0 <= z` -> (EX f:Z | (fact z f)).
Proof.
Intros.
Apply natlike_ind with P:=[z:Z](EX f:Z | (fact z f)).
Split with `1`.
Exact fact_0.

Intros.
Elim H1.
Intros.
Split with `(Zs x)*x0`.
Exact (fact_S x x0 H2).

Assumption.
Save.

(* This lemma should belong to the ZArith library *)

Lemma Z_mult_1 : (x,y:Z)`x>=1`->`y>=1`->`x*y>=1`.
Proof.
Intros.
Generalize H.
Apply natlike_ind with P:=[x:Z]`x >= 1`->`x*y >= 1`.
Omega.

Intros.
Simpl.
Elim (Z_le_lt_eq_dec `0` x0 H1).
Simpl.
Unfold Zs.
Replace `(x0+1)*y` with `x0*y+y`.
Generalize H2.
Generalize `x0*y`.
Intro.
Intros.
Omega.

Ring.

Intros.
Rewrite <- y0.
Omega.

Omega.
Save.

(* (fact x f) implies x>=0 and f>=1 *)

Lemma fact_pos : (x,f:Z)(fact x f)-> `x>=0` /\ `f>=1`.
Proof.
Intros.
(Elim H; Auto).
Omega.

Intros.
(Split; Try Omega).
(Apply Z_mult_1; Try Omega).
Save.

(* (fact 0 x) implies x=1 *)

Lemma fact_0_1 : (x:Z)(fact `0` x) -> `x=1`.
Proof.
Intros.
Inversion H.
Reflexivity.

Elim (fact_pos z f H1).
Intros.
(Absurd `z >= 0`; Omega).
Save.


(* We define the loop invariant : y * x! = x0! *)

Inductive invariant [y,x,x0:Z] : Prop :=
  c_inv : (f,f0:Z)(fact x f)->(fact x0 f0)->(Zmult y f)=f0
       -> (invariant y x x0).

(* The following lemma is used to prove the preservation of the invariant *)

Lemma fact_rec : (x0,x,y:Z)`0 < x` ->
    (invariant y x x0) 
 -> (invariant `x*y` (Zpred x) x0).
Proof.
Intros x0 x y H H0.
Elim H0.
Intros.
Generalize H H0 H3.
Elim H1.
Intros.
Absurd `0 < 0`; Omega.

Intros.
Apply c_inv with f:=f1 f0:=f0.
Cut `z+1+-1 = z`. Intro eq_z. Rewrite <- eq_z in H4.
Assumption.

Omega.

Assumption.

Rewrite (Zmult_sym (Zs z) y).                     
Rewrite (Zmult_assoc_r y (Zs z) f1).
Auto.
Save.


(* This one is used to prove the proof obligation at the exit of the loop *)

Lemma invariant_0 : (x,y:Z)(invariant y `0` x)->(fact x y).
Proof.
Intros.
Elim H.
Intros.
Generalize (fact_0_1 f H0).
Intro.
Rewrite H3 in H2.
Simpl in H2.
Replace y with `y*1`.
Rewrite H2.
Assumption.

Omega.
Save.


(* At last we come to the proof itself *************************************)

(* we declare two variable x and y *)

Global Variable x : Z ref.
Global Variable y : Z ref.

(* and we give the annotated program *)

Correctness factorielle 
  { `0 <= x` }
  begin
    y := 1;
    while !x <> 0 do
      { invariant `0 <= x` /\ (invariant y x x@0) as I
        variant x for (Zwf ZERO) }
      y := (Zmult !x !y);
      x := (Zpred !x)
    done
  end
  { (fact x@0 y) }.
Proof.
Split.
(* decreasing *)
Unfold Zwf. Unfold Zpred. Omega.
(* preservation of the invariant *)
Split.
  Unfold Zpred; Omega.
  Cut `0 < x0`. Intro Hx0.
  Decompose [and] I.
  Exact (fact_rec x x0 y1 Hx0 H1).
  Omega.
(* entrance of the loop *)
Split; Auto.
Elim (fact_function x Pre1). Intros.
Apply c_inv with f:=x0 f0:=x0; Auto.
Omega.
(* exit of the loop *)
Decompose [and] I.
Rewrite H1 in H2.
Exact (invariant_0 x y1 H2).
Save.


(* $Id: fact_int.v,v 1.12 1999/06/29 07:48:27 loiseleu Exp $ *)
