(****************************************************************************)
(*                 The Calculus of Inductive Constructions                  *)
(*                                                                          *)
(*                                Projet Coq                                *)
(*                                                                          *)
(*                     INRIA        LRI-CNRS        ENS-CNRS                *)
(*              Rocquencourt         Orsay          Lyon                    *)
(*                                                                          *)
(*                                 Coq V6.2                                 *)
(*                               May 1st 1998                               *)
(*                                                                          *)
(****************************************************************************)
(* Certification of Imperative Programs                                     *)
(* Jean-Christophe Filliātre                                                *)
(****************************************************************************)
(*                                 Handbook.v                               *)
(****************************************************************************)

(* This file contains proofs of programs taken from the
 * "Handbook of Theoretical Computer Science", volume B,
 * chapter "Methods and Logics for Proving Programs", by P. Cousot,
 * pp 841--993, Edited by J. van Leeuwen (c) Elsevier Science Publishers B.V.
 * 1990.
 * 
 * Programs are refered to by numbers and pages.
 *)

Require Programs.

Require Omega.
Require Zcomplements.
Require Zpower.

(****************************************************************************)

(* program (2) page 853 to compute x^y (annotated version is (25) page 860) *)

(* en attendant... *)
Parameter Zdiv2  : Z->Z.

Parameter Zeven_odd_dec : (x:Z){`x=2*(Zdiv2 x)`}+{`x=2*(Zdiv2 x)+1`}.
Definition Zodd_dec := [z:Z](sumbool_not ? ? (Zeven_odd_dec z)).

Axiom axiom1 : (x,y:Z) `y>0` -> `x*(Zpower x (Zpred y)) = (Zpower x y)`.
Axiom axiom2 : (x:Z)`x>0` -> `(Zdiv2 x)<x`.
Axiom axiom3 : (x,y:Z) `y>=0` -> `(Zpower (x*x) (Zdiv2 y)) = (Zpower x y)`.

Global Variable X : Z ref.
Global Variable Y : Z ref.
Global Variable Z : Z ref.

Correctness pgm25
  { `Y >= 0` }
  begin
    Z := 1;
    while !Y <> 0 do
      { invariant `Y >= 0` /\ `Z * (Zpower X Y) = (Zpower X@0 Y@0)`
        variant Y }
      if (Zodd_dec !Y) then begin
      	Y := (Zpred !Y);
	Z := (Zmult !Z !X)
      end else begin
      	Y := (Zdiv2 !Y);	
	X := (Zmult !X !X)
      end
    done
  end
  { Z = (Zpower X@ Y@) }.
Proof.
Split.
Unfold Zpred; Unfold Zwf; Omega.
Split.
Unfold Zpred; Omega.
Decompose [and] Pre1.
Rewrite <- H1.
Replace `Z1*X0*(Zpower X0 (Zpred Y0))` with `Z1*(X0*(Zpower X0 (Zpred Y0)))`.
Apply f_equal with f := (Zmult Z1).
Apply axiom1.
Omega.

Auto.
Symmetry.
Apply Zmult_assoc_r.

Split.
Unfold Zwf.
Repeat (Apply conj).
Omega.

Omega.

Apply axiom2. Omega.

Split.
Omega.

Decompose [and] Pre1.
Rewrite <- H1.
Apply f_equal with f:=(Zmult Z1).
Apply axiom3. Omega.

Omega.

Decompose [and] Post4.
Rewrite <- H2.
Rewrite H1.
Simpl.
Omega.

Save.


(****************************************************************************)

(* program (178) page 934 to compute the factorial using global variables
 * annotated version is (185) page 939
 *)

Parameter Zfact : Z -> Z.

Axiom axiom4 : `(Zfact 0) = 1`.
Axiom axiom5 : (x:Z) `x>0` -> `(Zfact (x-1))*x=(Zfact x)`.

Correctness pgm178
let rec F (u:unit) : unit { variant X } =
  { `X>=0` }
  (if !X = 0 then
    Y := 1
  else begin
    label L;
    X := (Zpred !X);
    (F tt);
    X := (Zs !X);
    Y := (Zmult !Y !X)
  end)
  { `X=X@` /\ `Y=(Zfact X@)` }.
Proof.
Rewrite Test1. Rewrite axiom4. Auto.
Unfold Zwf. Unfold Zpred. Omega.
Unfold Zpred. Omega.
Unfold Zs. Unfold Zpred in Post2. Split.
Omega.
Decompose [and] Post2.
Rewrite H0.
Replace `X0+(-1)+1` with X0.
Rewrite H1.
Replace `X0+(-1)` with `X0-1`.
Apply axiom5.
Omega.
Omega.
Omega.
Save.


(****************************************************************************)

(* program (186) page 939 "showing the usefulness of auxiliary variables" ! *)

Global Variable N : Z ref.
Global Variable S : Z ref.

Correctness pgm186
let rec F (u:unit) : unit { variant N } =
  { `N>=0` }
  (if !N > 0 then begin
    label L;
    N := (Zpred !N);
    (F tt) (* `N=(Zpred N@L)` /\ `S=S@L+(Zpower 2 (Zpred N@L))-1` *);
    S := (Zs !S);
    (F tt) (* `N=(Zpred N@L)` 
           /\ `S=S@L+(Zpower 2 (Zpred N@L))+(Zpower 2 (Zpred N@L))-1` *);
    N := (Zs !N)
  end)
  { `N=N@` /\ `S=S@+(Zpower 2 N@)-1` }.
Proof.
Unfold Zwf. Unfold Zpred. Omega.
Unfold Zpred. Omega.
Decompose [and] Post4. Rewrite H0. Unfold Zwf. Unfold Zpred. Omega.
Decompose [and] Post4. Rewrite H0. Unfold Zpred. Omega.
Split.
Unfold Zpred in Post4. Omega.
Decompose [and] Post3. Rewrite H1. 
Decompose [and] Post4. Rewrite H3. Rewrite H2. 
Replace `(Zpower 2 N0)` with `2*(Zpower 2 (Zpred N0))`. Omega.
Symmetry.
Replace `(Zpower 2 N0)` with `(Zpower 2 (1+(Zpred N0)))`.
Replace `2*(Zpower 2 (Zpred N0))` with `(Zpower 2 1)*(Zpower 2 (Zpred N0))`.
Apply Zpower_exp.
Omega.
Unfold Zpred. Omega.
Auto.
Replace `(1+(Zpred N0))` with N0; [ Auto | Unfold Zpred; Omega ].
Split.
Auto.
Replace N0 with `0`; Simpl; Omega.
Save.


(****************************************************************************)

(* program (196) page 944 (recursive factorial procedure with value-result
 * parameters)
 *)

Correctness pgm196
let rec F (U:Z) (V:Z ref) : unit { variant U } =
  { `U>=0` }
  (if U = 0 then
    V := 1
  else begin
    (F (Zpred U) V);	
    V := (Zmult !V U)
  end)
  { `V = (Zfact U)` }.
Proof.
Symmetry. Rewrite Test1. Apply axiom4.
Unfold Zwf. Unfold Zpred. Omega.
Unfold Zpred. Omega.
Rewrite Post2. 
Unfold Zpred. Replace `U0+(-1)` with `U0-1`. Apply axiom5.
Omega.
Omega.
Save.

(****************************************************************************)

(* program (197) page 945 (L_4 subset of Pascal) *)

(*
procedure P(X:Z; procedure Q(Z:Z));
  procedure L(X:Z); begin Q(X-1) end;
  begin if X>0 then P(X-1,L) else Q(X) end;

procedure M(N:Z);
  procedure R(X:Z); begin writeln(X) (* => RES := !X *) end;
  begin P(N,R) end.
*)
