NB: This Coq documentation contains a revised version of the Coq implementation of these papers [1], [2], [3], [4], and is also the support for ongoing research. A partial archive (14/01/2001) is available at here. Drop us a line if you are interested in a complete, up-to-date archive.

Library begcd

Require Import Psatz.
Require Import ssreflect ssrbool eqtype.
Require Import Arith_ext ZArith_ext Lists_ext.
Require Import integral_type nodup.

Ltac myomega := first [lia | omega].

Require Import simu.
Import simu.simu_m.

Ltac local_Var_unchanged v st :=
  match goal with
    | |- context [syntax_m.seplog_m.assert_m.expr_m.store.get v ?st'] =>
      cutrewrite (syntax_m.seplog_m.assert_m.expr_m.store.get v st' =
        syntax_m.seplog_m.assert_m.expr_m.store.get v st);
        [ idtac |
          symmetry;
          Var_unchanged ;
          rewrite [syntax_m.seplog_m.modified_vars _]/= ;
          Nodup_not_In ; fail ]
  end.

Ltac Not_In_dom2list :=
  match goal with
    | |- ~ In _ (seq_ext.s2l (assoc.dom _)) =>
      let hypo := fresh in
      assoc.From_dom_to_list hypo ;
      simpl in hypo ;
      apply (Permutation_notin _ _ _ hypo) ;
      clear hypo
    | |- ~ In _ (seq_ext.s2l (assoc.cdom _)) =>
      let hypo := fresh in
      assoc.From_cdom_to_list hypo ;
      simpl in hypo ;
      apply (Permutation_notin _ _ _ hypo) ;
      clear hypo
  end.

Ltac Disj_f_cdom2list K :=
  match goal with
    | |- Lists_ext.disj _ (?f (seq_ext.s2l (assoc.cdom _))) =>
      let hypo := fresh in
      assoc.From_cdom_to_list hypo ;
      simpl in hypo ;
      let subgoal := fresh in
      cut ( (Permutation_preserving f) );
        [ intro subgoal ;
          apply subgoal in hypo ;
          apply (Permutation_disj_R _ _ _ hypo) ;
          clear hypo ;
          simpl f
          |
            exact K
        ]
    | |- Lists_ext.disj (?f (seq_ext.s2l (assoc.cdom _))) _ =>
      let hypo := fresh in
      assoc.From_cdom_to_list hypo ;
      simpl in hypo ;
      let subgoal := fresh in
      cut (Permutation_preserving f) ;
        [ intro subgoal ;
          apply subgoal in hypo ;
          apply (Permutation_disj_L _ _ _ hypo) ;
          clear hypo ;
          simpl f
          |
            exact K
        ]
  end.

Module EGCD.

Import syntax_m.seplog_m.assert_m.expr_m.
Import syntax_m.seplog_m.assert_m.
Import syntax_m.seplog_m.

Local Open Scope seplog_cmd_scope.
Local Open Scope seplog_expr_scope.
Local Open Scope nodup_scope.
Local Open Scope zarith_ext_scope.

Lemma div_e_exact_full_2 : forall e s,
  [ e ]e_ s mod 2 = 0 -> 2 * [ e ./e nat_e 2 ]e_s = [ e ]e_s.

prelude of binary gcd algorithms
Definition prelude x y g :=
  while.while (var_e x mode nat_e 2 =e nat_e O &e var_e y mode nat_e 2 =e nat_e O) (
    x <- var_e x ./e nat_e 2 ;
    y <- var_e y ./e nat_e 2 ;
    g <- var_e g *e nat_e 2 ).

Local Open Scope seplog_hoare_scope.

binary gcd algorithm
Module BGCD_HAC.

Definition bgcd g x y t :=
  g <- nat_e 1 ;
  prelude x y g ;
  while.while (var_e x >> nat_e 0) (
    while.while (var_e x mode nat_e 2 =e nat_e 0) ( x <- var_e x ./e nat_e 2 ) ;
    while.while (var_e y mode nat_e 2 =e nat_e 0) ( y <- var_e y ./e nat_e 2 ) ;
    t <- (uop_e valabs_e (var_e x .-e var_e y)) ./e nat_e 2 ;
    ifte (var_e x >>= var_e y) thendo
      (x <- var_e t)
    elsedo
      (y <- var_e t)
  ).

Section bgcd_hac.

Variables x y g t : ssrnat.nat_eqType.
Variables vx vy : Z.

Lemma prelude_verif : nodup(x, y, g) -> 0 <= vx -> 0 <= vy ->
  {{ fun s _ => 0 <= [ x ]_s /\ 0 <= [ y ]_s /\ 0 <= [ g ]_s /\
    [ x ]_ s * [ g ]_ s = vx /\ [ y ]_ s * [ g ]_ s = vy }}
  prelude x y g
  {{ fun s _ => 0 <= [ x ]_ s /\ 0 <= [ y ]_ s /\ 0 <= [ g ]_ s /\
    [ x ]_ s * [ g ]_ s = vx /\ [ y ]_ s * [ g ]_ s = vy /\
    ~~ [ (var_e x mode nat_e 2 =e nat_e 0) &e (var_e y mode nat_e 2 =e nat_e 0) ]b_ s }}.
Proof.
move=> Hset Hvx Hvy.
apply hoare_prop_m.hoare_while_invariant with (fun s _ =>
  0 <= [ x ]_ s /\ 0 <= [ y ]_ s /\ 0 <= [ g ]_ s /\
  [ x ]_ s * [g ]_ s = vx /\ [ y ]_ s * [ g ]_ s = vy ).
by move=> s h /= [H1 [H2 [H3 [H4 H5]]]].
by move=> s h /= [[H1 [H2 [H3 [H4 H5]]]] H6].
x <- var_e x ./e nat_e 2
apply hoare_assign with (fun s h => 0 <= [ x ]_s /\ 0 <= [ y ]_s /\ 0 <= [ g ]_s /\
  2 * [ x ]_s * [ g ]_s = vx /\ [ y ]_s * [ g ]_s = vy /\ [ y ]_s mod 2 = 0).
move=> s h [[H1 [H2 [H3 [H4 H5]]]] H6].
rewrite /wp_assign.
repeat Store_upd => //.
split; first by apply Z_div_pos.
repeat (split; first by assumption).
split.
- rewrite div_e_exact_full_2 //; by rewrite /hoare_m.eval_b in H6; omegab.
  split; first by assumption.
- by rewrite /hoare_m.eval_b in H6; omegab.
y <- var_e y ./e nat_e 2;
apply hoare_assign with (fun s h => 0 <= [ x ]_s /\ 0 <= [ y ]_s /\ 0 <= [ g ]_s /\
  2 * [ x ]_s * [ g ]_s = vx /\ 2 * [ y ]_s * [ g ]_ s = vy).
move=> s h [H1 [H2 [H3 [H4 [H5 H6]]]]].
rewrite /wp_assign; repeat Store_upd => //.
split; first by assumption.
split; first by apply Z_div_pos.
repeat (split; first by assumption).
by rewrite div_e_exact_full_2.
g <- nat_e 2 *e var_e g
apply hoare_assign'.
move=> s h [H1 [H2 [H3 [H4 H5]]]].
rewrite /wp_assign; repeat Store_upd => //.
repeat (split; first by assumption).
split; first by apply Zmult_le_0_compat.
split; rewrite /= -?H4 -?H5 /ZIT.t_mult; ring.
Qed.

Lemma bgcd_verif : nodup(x, y, g, t) -> 0 <= vx -> 0 <= vy ->
  {{ fun s h => [x]_s = vx /\ [y]_s = vy }}
  bgcd g x y t
  {{ fun s h => 0 <= [ x ]_s /\ 0 <= [ y ]_s /\ 0 <= [ g ]_s /\
    Zis_gcd vx vy ([ g ]_s * [y]_s)}}.
Proof.
move=> Hset Hvx Hvy.
rewrite /bgcd.
g <- nat_e 1 ;
apply hoare_assign with (fun s _ => [x ]_ s = vx /\ [y ]_ s = vy /\ [g ]_ s = 1).
move=> s h; rewrite /wp_assign; move=> [Hx Hy].
by repeat Store_upd => //.
prelude x y g
apply while.hoare_seq with (fun s _ =>
  0 <= [x]_s /\ 0 <= [y]_s /\ 0 <= [g]_s /\
  Zgcd vx vy = [g]_s * Zgcd ([x]_s) ([y]_s) /\
  ~ [ ((var_e x mode nat_e 2) =e nat_e O) &e ((var_e y mode nat_e 2) =e nat_e O) ]b_ s).
have Hset' : nodup(x, y, g).
  rewrite [Equality.sort _]/= in Hset *; by Nodup_nodup O.
eapply while.hoare_conseq; last by apply (prelude_verif Hset' Hvx Hvy).
- move=> s h /= [H1 [H2 [H3 [H4 [H5 H6]]]]].
  repeat (split => //).
  + rewrite -H4 -H5.
    apply Zis_gcd_gcd; last first.
      rewrite (Zmult_comm [x]_s) (Zmult_comm [y]_s).
      by apply Zis_gcd_mult, Zgcd_is_gcd.
    apply Zmult_le_0_compat => //; by apply Zgcd_is_pos.
  + by move/negP: H6.
- move=> s h /= [-> [-> ->]]; by rewrite 2!Zmult_1_r.
while (nat_e x >> nat_e 0)
apply hoare_prop_m.hoare_while_invariant with (fun s _ => 0 <= [x]_s /\ 0 <= [y]_s /\ 0 <= [g]_s /\
  (Zodd [x]_s \/ Zodd [y]_s) /\
  Zgcd vx vy = [g]_s * Zgcd ([x]_s) ([y]_s)).
- move=> s h [Hx_pos [Hy_pos [Hg_pos [H1 H2]]]].
  repeat (split => //).
  rewrite /= in H2.
  move/negP : H2.
  rewrite negb_and.
  case/orP ;
    move/Zeq_boolP ;
    move/not_Zmod_2_Zodd ;
    by auto.
- move=> s h [[Hx_pos [Hy_pos [Hg_pos [Hparity Hgcd]]]] Hx].
  repeat (split; first by assumption).
  have {Hx} : [x ]_ s = 0.
    move: Hx.
    rewrite /hoare_m.eval_b /= ZIT.t_gt_t_ge.
    move/ZIT.t_geb_true.
    rewrite /ZIT.t_ge => ?; by myomega.
  rewrite /= => Hx.
  rewrite Hx /= Zabs_eq // in Hgcd.
  rewrite -Hgcd.
  by apply Zgcd_is_gcd.
while (var_e x mode nat_e 2 =e nat_e 0)
apply while.hoare_seq with (fun s _ => 0 <= [x ]_ s /\ 0 <= [y ]_ s /\ 0 <= [g ]_ s /\
  Zodd [x]_s /\
  Zgcd vx vy = [g ]_ s * Zgcd [x ]_ s [y ]_ s).
- apply hoare_prop_m.hoare_while_invariant with (fun s _ =>
    0 <= [x ]_ s /\ 0 <= [y ]_ s /\ 0 <= [g ]_ s /\
    (Zodd [x ]_ s \/ Zodd [y ]_ s) /\
    Zgcd vx vy = [g ]_ s * Zgcd [x ]_ s [y ]_ s).
  + by move=> s h /= [[H2 [H3 [H4 [H5 H6]]]] H1].
  + move=> s h /= [[H2 [H3 [H4 [H5 H6]]]]] //.
    move/Zeq_boolP.
    by move/not_Zmod_2_Zodd.
  + apply hoare_assign' => s h /= [[H2 [H3 [H4 [H5 H6]]]] H1].
    rewrite /wp_assign; repeat Store_upd => //.
    split; first by apply Z_div_pos.
    do 2 split => //.
    case: H5 => H5.
    * move/Zeq_boolP : H1.
      move/Zmod_2_Zeven.
      by move/Zeven_not_Zodd.
    * split; first by auto.
      rewrite /= -Zgcd_even_odd //.
      move/Zeq_boolP : H1.
      by move/Zmod_2_Zeven.
- apply while.hoare_seq with (fun s _ => 0 <= [x ]_ s /\ 0 <= [y ]_ s /\ 0 <= [g ]_ s /\
  Zodd [x ]_ s /\ Zodd [y]_s /\
  Zgcd vx vy = [g ]_ s * Zgcd [x ]_ s [y ]_ s).
  + apply hoare_prop_m.hoare_while_invariant with (fun s _ =>
    0 <= [x ]_ s /\ 0 <= [y ]_ s /\ 0 <= [g ]_ s /\
    Zodd [x ]_ s /\ Zgcd vx vy = [g ]_ s * Zgcd [x ]_ s [y ]_ s) => //.
    * move=> s h /= [[H2 [H3 [H4 [H5 H6]]]] H1].
      repeat (split => //).
      move/Zeq_boolP : H1.
      by move/not_Zmod_2_Zodd.
    * apply hoare_assign' => s h /= [[H2 [H3 [H4 [H5 H6]]]] H1].
      rewrite /wp_assign; repeat Store_upd => //.
      split; first by assumption.
      split; first by apply Z_div_pos.
      repeat (split; first by assumption).
      rewrite /= (Zgcd_sym [x]_s) -Zgcd_even_odd //.
      by rewrite (Zgcd_sym [y]_s).
      move/Zeq_boolP : H1; by move/Zmod_2_Zeven.
+ apply hoare_assign with (fun s _ => (0 <= [x ]_ s /\ 0 <= [y ]_ s /\ 0 <= [g ]_ s /\
    Zodd [x ]_ s /\ Zodd [y]_s /\
    Zgcd vx vy = [g ]_ s * Zgcd [x ]_ s [y ]_ s /\
    [t]_s = Zabs ([x]_s - [y]_s) / 2)).
  move=> s h /= [Hx_pos [Hy_pos [Hg_pos [Hx [Hy Hgcd]]]]].
  by rewrite /wp_assign; repeat Store_upd.
  apply while.hoare_ifte.
  + apply hoare_assign'.
    move=> s h /= [[Hx_pos [Hy_pos [Hg_pos [Hx [Hy [Hgcd Ht]]]]]] Hcond].
    rewrite /wp_assign; repeat Store_upd => //.
    split.
    * rewrite /= Ht.
      apply Z_div_pos => //; by apply Zabs_pos.
    * repeat (split; first by assumption).
      split; first by right.
      rewrite /= Ht Zabs_eq; last first.
        rewrite /ZIT.t_geb in Hcond.
        move/Zge_bool_true : Hcond => ?; by myomega.
      rewrite -Zgcd_even_odd //; last first.
        apply Zodd_plus_Zodd => //; by apply Zodd_opp.
      by rewrite -Zgcd_minus.
  - apply hoare_assign' => s h /= [[Hx_pos [Hy_pos [Hg_pos [Hx [Hy [Hgcd Ht]]]]]] Hcond].
    rewrite /wp_assign; repeat Store_upd => //.
    split; first by done.
    split.
    * rewrite /= Ht.
      apply Z_div_pos => //; by apply Zabs_pos.
    * split; first by done.
      split; first by left.
      rewrite /= Ht Zabs_non_eq; last first.
        rewrite /ZIT.t_geb in Hcond.
        move/negbTE : Hcond.
        move/Zge_bool_false => ?; by myomega.
      rewrite Zopp_plus_distr Zopp_involutive Zplus_comm (Zgcd_sym [x]_s) -Zgcd_even_odd //; last first.
        apply Zodd_plus_Zodd => //; by apply Zodd_opp.
      by rewrite -Zgcd_minus (Zgcd_sym [y]_s).
Qed.

End bgcd_hac.

End BGCD_HAC.

extended binary gcd algorithm from the Handbook of Applied Cryptography
Module BEGCD_HAC.

Definition inner_loop u x y A B :=
  while.while (var_e u mode nat_e 2 =e nat_e O) (
    u <- var_e u ./e nat_e 2 ;
    ifte (var_e A mode nat_e 2 =e nat_e O &e var_e B mode nat_e 2 =e nat_e O) thendo
     ( A <- var_e A ./e nat_e 2 ;
       B <- var_e B ./e nat_e 2 )
    elsedo
     ( A <- (var_e A +e var_e y) ./e nat_e 2 ;
       B <- (var_e B .-e var_e x) ./e nat_e 2 )
    ).

Definition branch u v A B C D :=
  u <- var_e u .-e var_e v ;
  A <- var_e A .-e var_e C ;
  B <- var_e B .-e var_e D.

Definition outer_loop u v x y A B C D :=
  while.while (var_e u >> nat_e 0) (
    inner_loop u x y A B;
    inner_loop v x y C D;
    ifte (var_e u >>= var_e v) thendo
      branch u v A B C D
    elsedo
      branch v u C D A B
  ).

Definition begcd g x y u v A B C D :=
  g <- nat_e 1 ;
  prelude x y g ;
  u <- var_e x ;
  v <- var_e y ;
  A <- nat_e 1 ;
  B <- nat_e O ;
  C <- nat_e O ;
  D <- nat_e 1 ;
  outer_loop u v x y A B C D.

Lemma inner_loop_verif : forall (x y g u v : ssrnat.nat_eqType) vx vy (A B C D : bipl.var.v),
  nodup(x, y, g, u, v, A, B, C, D) ->
  {{fun s _ => 0 <= [x ]_ s /\ 0 <= [y ]_ s /\ 0 <= [g ]_ s /\ 0 <= [u ]_ s /\ 0 <= [v ]_ s /\
    [ x ]_ s * [ g ]_ s = vx /\ [y ]_ s * [g ]_ s = vy /\
    [ A ]_ s * [ x ]_ s + [B ]_ s * [y ]_ s = [u ]_ s /\
    [ C ]_ s * [ x ]_ s + [D ]_ s * [y ]_ s = [v ]_ s /\
    Zgcd vx vy = [g ]_ s * Zgcd [u ]_ s [v ]_ s /\
    (Zodd [x]_s \/ Zodd [y]_s) /\
    (Zodd [u]_s \/ Zodd [v]_s) }}
  inner_loop u x y A B
  {{fun s _ => 0 <= [x ]_ s /\ 0 <= [y ]_ s /\ 0 <= [g ]_ s /\ 0 <= [u ]_ s /\ 0 <= [v ]_ s /\
     [x ]_ s * [g ]_ s = vx /\ [y ]_ s * [g ]_ s = vy /\
    [ A ]_ s * [ x ]_ s + [B ]_ s * [y ]_ s = [u ]_ s /\
    [ C ]_ s * [ x ]_ s + [ D ]_ s * [y ]_ s = [v ]_ s /\
    Zgcd vx vy = [g ]_ s * Zgcd [u ]_ s [v ]_ s /\
    (Zodd [ x ]_s \/ Zodd [ y ]_s) /\
    Zodd [ u ]_s }}.
Proof.
move=> x y g u v vx vy A B C D Hset.
apply hoare_prop_m.hoare_while_invariant with (fun s _ =>
  0 <= [x ]_ s /\ 0 <= [y ]_ s /\ 0 <= [g ]_ s /\ 0 <= [u ]_ s /\ 0 <= [v ]_ s /\
  [x ]_ s * [g ]_ s = vx /\ [y ]_ s * [g ]_ s = vy /\
  [A ]_ s * [x ]_ s + [B ]_ s * [y ]_ s = [u ]_ s /\
  [C ]_ s * [x ]_ s + [D ]_ s * [y ]_ s = [v ]_ s /\
  Zgcd vx vy = [g ]_ s * Zgcd [u ]_ s [v ]_ s /\
  (Zodd [x]_s \/ Zodd [y]_s) /\
  (Zodd [u]_s \/ Zodd [v]_s)).
move=> s h /= [H2 [H3 [H4 [H5 [H6 [H7 [H8 [H9 [H10 [H11 [H12 H13]]]]]]]]]]].
by intuition.
move=> s h /= [[H2 [H3 [H4 [H5 [H6 [H7 [H8 [H9 [H10 [H11 [H12 H13]]]]]]]]]]] H1].
repeat (split => //).
move/ZIT.tP : H1.
by move/not_Zmod_2_Zodd.
u <- var_e u ./e nat_e 2;
apply hoare_assign with (fun s _ =>
  0 <= [x ]_ s /\ 0 <= [y ]_ s /\ 0 <= [g ]_ s /\ 0 <= [u ]_ s /\ 0 <= [v ]_ s /\
  [x ]_ s * [g ]_ s = vx /\ [y ]_ s * [g ]_ s = vy /\
  [A ]_ s * [x ]_ s + [B ]_ s * [y ]_ s = [u ]_ s * 2 /\
  [C ]_ s * [x ]_ s + [D ]_ s * [y ]_ s = [v ]_ s /\
  Zgcd vx vy = [g ]_ s * Zgcd [u ]_ s [v ]_ s /\
  (Zodd [x]_s \/ Zodd [y]_s) /\
  Zodd [v]_s).
move=> s h /= [[H2 [H3 [H4 [H5 [H6 [H7 [H8 [H9 [H10 [H11 [H12 H13]]]]]]]]]]] H1].
rewrite /wp_assign; repeat Store_upd.
move/Zeq_boolP : H1; move/Zmod_2_Zeven => H1.
repeat (split => //).
by apply Z_div_pos.
rewrite /= /ZIT.t_div.
case/Zeven_ex : H1 => u' H1.
by rewrite H1 (Zmult_comm 2) Z_div_mult_full // -(Zmult_comm 2) H9.
rewrite -Zgcd_even_odd //.
case: H13 => //.
by move/Zeven_not_Zodd.
case: H13 => //.
by move/Zeven_not_Zodd.
ifte (var_e A mode nat_e 2 =e nat_e 0 &e var_e B mode nat_e 2 =e nat_e 0)
apply while.hoare_ifte.
-
A <- var_e A ./e nat_e 2;
  apply hoare_assign with (fun s _ =>
    0 <= [x ]_ s /\ 0 <= [y ]_ s /\ 0 <= [g ]_ s /\ 0 <= [u ]_ s /\ 0 <= [v ]_ s /\
    [x ]_ s * [g ]_ s = vx /\ [y ]_ s * [g ]_ s = vy /\
    [A ]_ s * [x ]_ s * 2 + [B ]_ s * [y ]_ s = [u ]_ s * 2 /\
    [C ]_ s * [x ]_ s + [D ]_ s * [y ]_ s = [v ]_ s /\
    Zgcd vx vy = [g ]_ s * Zgcd [u ]_ s [v ]_ s /\
    (Zodd [x]_s \/ Zodd [y]_s) /\
    Zodd [v ]_ s /\
    eval_b (var_e B mode nat_e 2 =e nat_e 0) s).
  move=> s h /= [[H2 [H3 [H4 [H5 [H6 [H7 [H8 [H9 [H10 [H11 [H12 H13]]]]]]]]]]] H1].
  rewrite /wp_assign.
  repeat Store_upd.
  repeat (split=> //).
  case/andP : H1.
  move/ZIT.tP; move/Zmod_2_Zeven; case/Zeven_ex=> a H1a.
  move/ZIT.tP; move/Zmod_2_Zeven; case/Zeven_ex=> b H1b.
  by rewrite /= /ZIT.t_div H1a (Zmult_comm 2) Z_div_mult_full //
    -H9 -(Zmult_comm 2) Zmult_assoc -H1a.
  by case/andP : H1.
B <- var_e B ./e nat_e 2
  apply hoare_assign'.
  move=> s h /= [H2 [H3 [H4 [H5 [H6 [H7 [H8 [H9 [H10 [H11 [H12 [H13 H14]]]]]]]]]]]].
  rewrite /wp_assign.
  repeat Store_upd.
  repeat (split => //).
  move/ZIT.tP : H14; move/Zmod_2_Zeven; case/Zeven_ex=> a H14.
  rewrite H14 in H9.
  ring_simplify in H9.
  rewrite -!Zmult_assoc -Zmult_plus_distr_r in H9.
  rewrite /= /ZIT.t_div H14 (Zmult_comm 2) Z_div_mult_full //.
  by eapply Zmult_reg_l; eauto.
  by right.
-
A <- (var_e A +e var_e y) ./e nat_e 2;
apply hoare_assign with (fun s _ =>
    0 <= [x ]_ s /\ 0 <= [y ]_ s /\ 0 <= [g ]_ s /\ 0 <= [u ]_ s /\ 0 <= [v ]_ s /\
    [x ]_ s * [g ]_ s = vx /\ [y ]_ s * [g ]_ s = vy /\
    [A ]_ s * [x ]_ s * 2 - [y]_s * [x]_s + [B ]_ s * [y ]_ s = [u ]_ s * 2 /\
    [C ]_ s * [x ]_ s + [D ]_ s * [y ]_ s = [v ]_ s /\
    Zgcd vx vy = [g ]_ s * Zgcd [u ]_ s [v ]_ s /\
    (Zodd [x]_s \/ Zodd [y]_s) /\
    Zodd [v ]_ s /\
    (Zodd ([A]_s *2 - [y]_s) \/ Zodd ([B]_s))).
  move=> s h /= [[H2 [H3 [H4 [H5 [H6 [H7 [H8 [H9 [H10 [H11 [H12 H13]]]]]]]]]]] H1].
  rewrite /wp_assign; repeat Store_upd.
  repeat (split => //).
  + rewrite /= /ZIT.t_div /ZIT.t_plus.
    rewrite negb_and in H1.
    case/orP : H1.
    * move/ZIT.tP; move/not_Zmod_2_Zodd => H1.
      rewrite -H9.
      ring_simplify.
      f_equal.
      rewrite -Z_div_exact_full_2 //; first by ring.
      rewrite (Zmult_comm _ 2) in H9.
      move/Zmult_2_Zeven : H9.
      move/Zeven_plus_inv.
      case; case => Y1 Y2.
      - case: H12 => H12.
        + move: (Zodd_mult_Zodd _ _ H1 H12); by move/Zeven_not_Zodd.
        + by apply Zeven_Zmod_2, Zodd_plus_Zodd.
      - apply Zeven_Zmod_2.
        case: H12 => H12.
        + case/Zodd_mult_inv : Y2 => Y2 Y3; by apply Zodd_plus_Zodd.
        + by apply Zodd_plus_Zodd.
    * move/ZIT.tP. move/not_Zmod_2_Zodd => H1.
      rewrite -H9.
      ring_simplify.
      f_equal.
      rewrite -Z_div_exact_full_2 //; first by ring.
      rewrite (Zmult_comm _ 2) in H9.
      move/Zmult_2_Zeven : H9.
      move/Zeven_plus_inv.
      case; case=> Y1 Y2.
      - case: H12 => H12.
        + case/Zeven_mult_inv : Y1 => Y1; last by move/Zeven_not_Zodd : Y1.
          case/Zeven_mult_inv : Y2 => Y2; first by move/Zeven_not_Zodd : Y2.
          by apply Zeven_Zmod_2, Zeven_plus_Zeven.
        + move: (Zodd_mult_Zodd _ _ H1 H12).
          by move/Zodd_not_Zeven.
      - case: H12 => H12.
        + case/Zodd_mult_inv : Y1 => Y1 _.
          case/Zodd_mult_inv : Y2 => _ Y2'.
          by apply Zeven_Zmod_2, Zodd_plus_Zodd.
        + case/Zodd_mult_inv : Y1 => Y1 _.
          by apply Zeven_Zmod_2, Zodd_plus_Zodd.
  + rewrite negb_and in H1.
    rewrite /= /ZIT.t_div /ZIT.t_plus.
    case/orP : H1.
    - move/ZIT.tP. move/not_Zmod_2_Zodd => H1.
      case: H12 => H12.
      * case: (Zeven_odd_dec [B]_s) => X; auto.
        rewrite (Zmult_comm _ 2) in H9.
        move/Zmult_2_Zeven : H9.
        case/Zeven_plus_inv.
        - case=> Y1 Y2.
          move: (Zodd_mult_Zodd _ _ H1 H12).
          by move/Zodd_not_Zeven.
        - case=> Y1.
          case/Zodd_mult_inv.
          by move/Zodd_not_Zeven.
      * left.
        apply Zeven_plus_Zodd.
        - rewrite Zmult_comm; by apply Zeven_2p.
        - by apply Zodd_opp.
    - move/ZIT.tP; by move/not_Zmod_2_Zodd; auto.
B <- (var_e B .-e var_e x) ./e nat_e 2
apply hoare_assign'.
move=> s h /= [H2 [H3 [H4 [H5 [H6 [H7 [H8 [H9 [H10 [H11 [H12 [H13 H14]]]]]]]]]]]].
rewrite /wp_assign; repeat Store_upd.
repeat (split => //).
2: by right.
rewrite /= /ZIT.t_minus /ZIT.t_div.
have {H9}H9 : ([B ]_ s - [x ]_ s) * [y ]_ s = [u ]_ s * 2 - [A ]_ s * [x ]_ s * 2.
  rewrite -H9; ring.
rewrite -Zmult_minus_distr_r -(Zmult_comm 2) in H9.
case: H12 => H12.
+ case: (Zeven_odd_dec [y]_s) => X.
  * case: H14 => H14.
    - move: (Zeven_plus_Zeven _ _ (Zeven_2p [A]_s) (Zeven_opp _ X)).
      rewrite Zmult_comm.
      by move/Zeven_not_Zodd.
    - have Y : Zeven ([B]_s - [x]_s).
        apply Zodd_plus_Zodd => //.
        by apply Zodd_opp.
      case/Zeven_ex : Y => b' Y.
      rewrite Y -Zmult_assoc in H9.
      apply Zmult_reg_l in H9 => //.
      rewrite Y (Zmult_comm 2) Z_div_mult_full // H9; ring.
  * case: (Zeven_odd_dec ([B]_s - [x]_s)) => Y.
    - case/Zeven_ex : Y => b' Y.
      rewrite Y -Zmult_assoc in H9.
      apply Zmult_reg_l in H9 => //.
      rewrite Y (Zmult_comm 2) Z_div_mult_full // H9; ring.
    - have : Zodd ( ([B ]_ s - [x ]_ s) * [y ]_ s ).
      apply Zodd_mult_Zodd => //.
      rewrite H9.
      move: (Zeven_2p (([u ]_ s - [A ]_ s * [x ]_ s))).
      by move/Zeven_not_Zodd.
+ case: (Zeven_odd_dec ([B]_s - [x]_s)) => Y.
  * case/Zeven_ex : Y => b' Y.
    rewrite Y -Zmult_assoc in H9.
    apply Zmult_reg_l in H9 => //.
    rewrite Y (Zmult_comm 2) Z_div_mult_full // H9; ring.
  * have : Zodd ( ([B ]_ s - [x ]_ s) * [y ]_ s ) by apply Zodd_mult_Zodd.
    rewrite H9.
    move: (Zeven_2p (([u ]_ s - [A ]_ s * [x ]_ s))).
    by move/Zeven_not_Zodd.
Qed.

Lemma outer_loop_verif : forall (g x y u v A B C D :ssrnat.nat_eqType) vx vy,
  nodup(g, x, y, u, v, A, B, C, D) -> 0 <= vx -> 0 <= vy ->
  {{ (fun s _ => 0 <= [x ]_ s /\ 0 <= [y ]_ s /\ 0 <= [g ]_ s /\
      0 <= [u ]_ s /\ 0 <= [v ]_ s /\
      [x ]_ s * [g ]_ s = vx /\ [y ]_ s * [g ]_ s = vy /\
      [A ]_ s * [x ]_ s + [B ]_ s * [y ]_ s = [u ]_ s /\
      [C ]_ s * [x ]_ s + [D ]_ s * [y ]_ s = [v ]_ s /\
      Zgcd vx vy = [g ]_ s * Zgcd [u ]_ s [v ]_ s /\
      (Zodd [u ]_ s \/ Zodd [v ]_ s) /\ (Zodd [x ]_ s \/ Zodd [y ]_ s))}}
    outer_loop u v x y A B C D
    {{ (fun s h => (0 <= [x ]_ s /\ 0 <= [y ]_ s /\ 0 <= [g ]_ s /\
     0 <= [u ]_ s /\ 0 <= [v ]_ s /\
     [x ]_ s * [g ]_ s = vx /\ [y ]_ s * [g ]_ s = vy /\
     [A ]_ s * [x ]_ s + [B ]_ s * [y ]_ s = [u ]_ s /\
     [C ]_ s * [x ]_ s + [D ]_ s * [y ]_ s = [v ]_ s /\
     Zgcd vx vy = [g ]_ s * Zgcd [u ]_ s [v ]_ s /\
     (Zodd [u ]_ s \/ Zodd [v ]_ s) /\ (Zodd [x ]_ s \/ Zodd [y ]_ s)) /\
    ~~ hoare_m.eval_b (var_e u >> nat_e 0) (s, h)) }}.
Proof.
move=> g x y u v A B C D vx vy Hset Hvx Hvy.
rewrite /outer_loop.
apply while.hoare_while.
while (var_e u mode nat_e 2 =e nat_e 0)
apply while.hoare_seq with (fun s _ =>
  0 <= [x ]_ s /\ 0 <= [y ]_ s /\ 0 <= [g ]_ s /\ 0 <= [u ]_ s /\ 0 <= [v ]_ s /\
  [x ]_ s * [g ]_ s = vx /\
  [y ]_ s * [g ]_ s = vy /\
  [A ]_ s * [x ]_ s + [B ]_ s * [y ]_ s = [u ]_ s /\
  [C ]_ s * [x ]_ s + [D ]_ s * [y ]_ s = [v ]_ s /\
  Zgcd vx vy = [g ]_ s * Zgcd [u ]_ s [v ]_ s /\
  (Zodd [x]_s \/ Zodd [y]_s) /\
  Zodd [u]_s).
eapply hoare_prop_m.hoare_stren; last first.
have Hset' : nodup(x, y, g, u, v, A, B, C, D).
  rewrite [Equality.sort _]/= in Hset *. by Nodup_nodup O.
by apply (inner_loop_verif x y g u v vx vy A B C D Hset').
move=> s h /= [[H2 [H3 [H4 [H5 [H6 [H7 [H8 [H9 [H10 [H11 H12]]]]]]]]]] H1]; by intuition.
apply hoare_prop_m.hoare_stren with (fun s _ =>
  (0 <= [x ]_ s /\ 0 <= [y ]_ s /\ 0 <= [g ]_ s /\ 0 <= [v ]_ s /\ 0 <= [u ]_ s /\
    [x ]_ s * [g ]_ s = vx /\ [y ]_ s * [g ]_ s = vy /\
    [C ]_ s * [x ]_ s + [D ]_ s * [y ]_ s = [v ]_ s /\
    [A ]_ s * [x ]_ s + [B ]_ s * [y ]_ s = [u ]_ s /\
    Zgcd vx vy = [g ]_ s * Zgcd [v ]_ s [u ]_ s) /\
  (Zodd [x]_s \/ Zodd [y]_s) /\
  (Zodd [v]_s \/ Zodd [u]_s)).
move=> s h /=.
rewrite (Zgcd_sym [u]_s); by intuition.
have {Hset}Hset' : nodup(x, y, g, v, u, C, D, A, B).
  rewrite [Equality.sort _]/= in Hset *; by Nodup_nodup O.
move: (inner_loop_verif x y g v u vx vy C D A B Hset') => Hi.
eapply while.hoare_seq.
eapply hoare_prop_m.hoare_stren; last by apply Hi.
move=> s h /=; by intuition.
apply while.hoare_ifte.
-
u <- var_e u .-e var_e v;
  apply hoare_assign with (fun s _ =>
    0 <= [x ]_ s /\ 0 <= [y ]_ s /\ 0 <= [g ]_ s /\ 0 <= [v ]_ s /\ 0 <= [u ]_ s /\
    [x ]_ s * [g ]_ s = vx /\ [y ]_ s * [g ]_ s = vy /\
    ([A ]_ s - [C]_s)* [x ]_ s + ([B ]_ s - [D]_s)* [y ]_ s = [u ]_ s /\
    [C ]_ s * [x ]_ s + [D ]_ s * [y ]_ s = [v ]_ s /\
    Zgcd vx vy = [g ]_ s * Zgcd [u ]_ s [v ]_ s /\
    (Zodd [x]_s \/ Zodd [y]_s) /\
    (Zodd [v]_s \/ Zodd [u]_s)).
  move=> s h /= [[H3 [H4 [H5 [H6 [H7 [H8 [H9 [H10 [H11 [H12 [H13 H14]]]]]]]]]]] H2].
  rewrite /wp_assign; repeat Store_upd => //.
  repeat (split => //).
  move/Zge_bool_true : H2 => H2.
  rewrite /=; by omegab.
  rewrite /= /ZIT.t_minus -H10 -H11; ring.
  by rewrite /= /ZIT.t_minus -Zgcd_minus (Zgcd_sym [u]_s).
  rewrite /= /ZIT.t_minus.
  case: (Zeven_odd_dec [v]_s) => Htmp; by auto.
A <- var_e A .-e var_e C;
  apply hoare_assign with (fun s _ =>
    (0 <= [x ]_ s /\ 0 <= [y ]_ s /\ 0 <= [g ]_ s /\ 0 <= [v ]_ s /\ 0 <= [u ]_ s /\
      [x ]_ s * [g ]_ s = vx /\ [y ]_ s * [g ]_ s = vy /\
      ([A ]_ s)* [x ]_ s + ([B ]_ s - [D]_s)* [y ]_ s = [u ]_ s /\
      [C ]_ s * [x ]_ s + [D ]_ s * [y ]_ s = [v ]_ s /\
      Zgcd vx vy = [g ]_ s * Zgcd [u ]_ s [v ]_ s /\
      (Zodd [x]_s \/ Zodd [y]_s) /\
      (Zodd [v ]_ s \/ Zodd [u ]_ s))).
  move=> s h /= [H2 [H3 [H4 [H5 [H6 [H7 [H8 [H9 [H10 [H11 H12]]]]]]]]]].
  by rewrite /wp_assign; repeat Store_upd.
B <- var_e B .-e var_e D
  apply hoare_assign'.
  move=> s h /= [H2 [H3 [H4 [H5 [H6 [H7 [H8 H9]]]]]]].
  rewrite /wp_assign; repeat Store_upd => //.
  tauto.
-
v <- var_e v .-e var_e u;
  apply hoare_assign with (fun s _ =>
      0 <= [x ]_ s /\ 0 <= [y ]_ s /\ 0 <= [g ]_ s /\ 0 <= [v ]_ s /\ 0 <= [u ]_ s /\
      [x ]_ s * [g ]_ s = vx /\ [y ]_ s * [g ]_ s = vy /\
      [A ]_ s * [x ]_ s + [B ]_ s * [y ]_ s = [u ]_ s /\
      ([C ]_ s - [A]_s) * [x ]_ s + ([D ]_ s - [B]_s) * [y ]_ s = [v ]_ s /\
      Zgcd vx vy = [g ]_ s * Zgcd [u ]_ s [v ]_ s /\
      (Zodd [x]_s \/ Zodd [y]_s) /\
      (Zodd [v ]_ s \/ Zodd [u ]_ s)).
  move=> s h /= [[H3 [H4 [H5 [H6 [H7 [H8 [H9 [H10 [H11 [H12 [H13 H14]]]]]]]]]]] H2].
  rewrite /wp_assign; repeat Store_upd => //.
  repeat (split => //).
  move/negbTE : H2; move/Zge_bool_false=> H2.
  rewrite /=; by omegab.
  rewrite /= /ZIT.t_minus -H10 -H11; ring.
  by rewrite /= /ZIT.t_minus (Zgcd_sym [u]_s) -Zgcd_minus.
  case: (Zeven_odd_dec [u]_s) => Htmp; last by right.
  left; apply Zodd_plus_Zeven => //.
  by destruct ([u]_s).
C <- var_e C .-e var_e A;
  apply hoare_assign with (fun s _ =>
    0 <= [x ]_ s /\ 0 <= [y ]_ s /\ 0 <= [g ]_ s /\ 0 <= [v ]_ s /\ 0 <= [u ]_ s /\
    [x ]_ s * [g ]_ s = vx /\ [y ]_ s * [g ]_ s = vy /\
    [A ]_ s * [x ]_ s + [B ]_ s * [y ]_ s = [u ]_ s /\
    [C ]_ s * [x ]_ s + ([D ]_ s - [B]_s) * [y ]_ s = [v ]_ s /\
    Zgcd vx vy = [g ]_ s * Zgcd [u ]_ s [v ]_ s /\
    (Zodd [x]_s \/ Zodd [y]_s) /\
    (Zodd [v ]_ s \/ Zodd [u ]_ s)).
  move=> s h /= [H2 [H3 [H4 [H5 [H6 [H7 [H8 [H9 [H10 [H11 [H12 H13]]]]]]]]]]].
  by rewrite /wp_assign; repeat Store_upd.
D <- var_e D .-e var_e B
  apply hoare_assign' => s h /= [H2 [H3 [H4 [H5 [H6 [H7 [H8 H9]]]]]]].
  rewrite /wp_assign; repeat Store_upd => //; by intuition.
Qed.

Lemma begcd_verif : forall (g x y u v A B C D : ssrnat.nat_eqType),
  nodup(g, x, y, u, v, A, B, C, D) ->
  forall vx vy, 0 <= vx -> 0 <= vy ->
  {{ fun s h => [x]_s = vx /\ [y]_s = vy }}
  begcd g x y u v A B C D
  {{ fun s h => [C]_s * vx + [D]_s * vy = [g]_s * [v]_s /\
    Zis_gcd vx vy ([g]_s * [v]_s)}}.
Proof.
move=> g x y u v A B C D Hset vx vy Hvx Hvy.
rewrite /begcd.
g <- nat_e 1 ;
apply hoare_assign with (fun s h => [x]_s = vx /\ [y]_s = vy /\ [g]_s = 1).
move=> s h [Hx Hy].
by rewrite /wp_assign; repeat Store_upd => //.
prelude x y g ;
apply while.hoare_seq with (fun s _ =>
  0 <= [x]_s /\ 0 <= [y]_s /\ 0 <= [g]_s /\
  [x]_ s * [g]_s = vx /\ [y]_s * [g]_s = vy /\
  Zgcd vx vy = [g]_s * Zgcd ([x]_s) ([y]_s) /\
  ~ eval_b ((var_e x mode nat_e 2 =e nat_e O) &e (var_e y mode nat_e 2) =e nat_e O) s).
have Hset' : nodup(x, y, g).
  rewrite [Equality.sort _]/= in Hset *; by Nodup_nodup O.
eapply while.hoare_conseq; last by apply (BGCD_HAC.prelude_verif _ _ _ _ _ Hset' Hvx Hvy).
- move=> s h /= [H1 [H2 [H3 [x_g [y_g H6]]]]].
  repeat (split; first by done).
  split; last by move/negP: H6.
  rewrite -x_g -y_g.
  apply Zis_gcd_gcd; last first.
    rewrite (Zmult_comm [x]_s) (Zmult_comm [y]_s).
    by apply Zis_gcd_mult, Zgcd_is_gcd.
  apply Zmult_le_0_compat => //; by apply Zgcd_is_pos.
- move=> s h /= [-> [-> ->]]; by rewrite 2!Zmult_1_r.
u <- var_e x ;
apply hoare_assign with (fun s _ =>
  0 <= [x]_s /\ 0 <= [y]_s /\ 0 <= [g]_s /\
  [x]_ s * [g]_s = vx /\ [y]_s * [g]_s = vy /\
  Zgcd vx vy = [g]_s * Zgcd ([x]_s) ([y]_s) /\
  ~ eval_b (((var_e x mode nat_e 2) =e nat_e O) &e ((var_e y mode nat_e 2) =e nat_e O)) s /\
  [u]_s = [x]_s).
move=> s h /= [H1 [H2 [H3 [Hxg [Hyg [H4 H5]]]]]]; by rewrite /wp_assign; repeat Store_upd.
v <- var_e y ;
apply hoare_assign with (fun s _ =>
  0 <= [x]_s /\ 0 <= [y]_s /\ 0 <= [g]_s /\
  [x]_ s * [g]_s = vx /\ [y]_s * [g]_s = vy /\
  Zgcd vx vy = [g]_s * Zgcd ([x]_s) ([y]_s) /\
  ~ eval_b (((var_e x mode nat_e 2) =e nat_e O) &e ((var_e y mode nat_e 2) =e nat_e O)) s /\
  [u]_s = [x]_s /\ [v]_s = [y]_s).
move=> s h /= [H1 [H2 [H3 [Hxg [Hyg [H4 [H5 Hu]]]]]]]; by rewrite /wp_assign; repeat Store_upd.
A <- nat_e 1 ;
apply hoare_assign with (fun s _ =>
  0 <= [x]_s /\ 0 <= [y]_s /\ 0 <= [g]_s /\
  [x]_ s * [g]_s = vx /\ [y]_s * [g]_s = vy /\
  Zgcd vx vy = [g]_s * Zgcd ([x]_s) ([y]_s) /\
  ~ eval_b (((var_e x mode nat_e 2) =e nat_e O) &e ((var_e y mode nat_e 2) =e nat_e O)) s /\
  [u]_s = [x]_s /\ [v]_s = [y]_s /\ [A]_s = 1).
move=> s h /= [H1 [H2 [H3 [Hxg [Hyg [H4 [H5 [Hu Hv]]]]]]]]; by rewrite /wp_assign; repeat Store_upd.
B <- nat_e O ;
apply hoare_assign with (fun s _ =>
  0 <= [x]_s /\ 0 <= [y]_s /\ 0 <= [g]_s /\
  [x]_ s * [g]_s = vx /\ [y]_s * [g]_s = vy /\
  Zgcd vx vy = [g]_s * Zgcd ([x]_s) ([y]_s) /\
  ~ [ ((var_e x mode nat_e 2) =e nat_e O) &e ((var_e y mode nat_e 2) =e nat_e O) ]b_ s /\
  [u]_s = [x]_s /\ [v]_s = [y]_s /\ [A]_s = 1 /\ [B]_s = 0).
move=> s h /= [H1 [H2 [H3 [Hxg [Hyg [H4 [H5 [Hu [Hv HA]]]]]]]]]; by rewrite /wp_assign; repeat Store_upd.
C <- nat_e O ;
apply hoare_assign with (fun s _ =>
  0 <= [x]_s /\ 0 <= [y]_s /\ 0 <= [g]_s /\
  [x]_ s * [g]_s = vx /\ [y]_s * [g]_s = vy /\
  Zgcd vx vy = [g]_s * Zgcd ([x]_s) ([y]_s) /\
  ~ [ ((var_e x mode nat_e 2) =e nat_e O) &e ((var_e y mode nat_e 2) =e nat_e O) ]b_ s /\
  [u]_s = [x]_s /\ [v]_s = [y]_s /\ [A]_s = 1 /\ [B]_s = 0 /\ [C]_s = 0).
move=> s h /= [H1 [H2 [H3 [Hxg [Hyg [H4 [H5 [Hu [Hv [HA HB]]]]]]]]]]; by rewrite /wp_assign; repeat Store_upd.
D <- nat_e 1 ;
apply hoare_assign with (fun s _ =>
  0 <= [x]_s /\ 0 <= [y]_s /\ 0 <= [g]_s /\
  [x]_ s * [g]_s = vx /\ [y]_s * [g]_s = vy /\
  Zgcd vx vy = [g]_s * Zgcd ([x]_s) ([y]_s) /\
  ~ eval_b (((var_e x mode nat_e 2) =e nat_e O) &e ((var_e y mode nat_e 2) =e nat_e O)) s /\
  [u]_s = [x]_s /\ [v]_s = [y]_s /\ [A]_s = 1 /\ [B]_s = 0 /\ [C]_s = 0 /\ [D]_s = 1).
move=> s h /= [H1 [H2 [H3 [Hxg [Hyg [H4 [H5 [Hu [Hv [HA [HB HC]]]]]]]]]]]; by rewrite /wp_assign; repeat Store_upd.
outer_loop
eapply while.hoare_conseq; last by apply (outer_loop_verif g x y u v A B C D vx vy Hset Hvx Hvy).
- move=> s h /= [[Hx [Hy [Hg [Hu_pos [Hv_pos [Hxg [Hyg [HAB [HCD [Hgcd Hparity]]]]]]]]]] Heq].
  have {Heq}Heq : [u]_s = 0.
    move: Heq.
    rewrite /hoare_m.eval_b /= ZIT.t_gt_t_ge.
    move/ZIT.t_geb_true.
    rewrite /ZIT.t_ge => ?; by myomega.
  rewrite Heq /= Zabs_eq // in Hgcd.
  split.
  rewrite -Hxg -Hyg -HCD; ring.
  rewrite -Hgcd; by apply Zgcd_is_gcd.
- move=> s h /= [Hx [Hy [Hg [Hxg [Hyg [Hgcd [Hcond [Hu [Hv [HA [HB [HC HD]]]]]]]]]]]].
  rewrite HA HB HC HD 2!Zmult_1_l 2!Zmult_0_l Zplus_0_r Zplus_0_l Hu Hv.
  move/negP : Hcond.
  rewrite negb_and.
  case/orP; move/ZIT.tP; move/not_Zmod_2_Zodd; by intuition.
Qed.

End BEGCD_HAC.

Definition uivi_init u v u1 u2 u3 v1 v2 v3 s :=
  [u1]_s = 1 /\ [u2]_s = 0 /\[u3]_s = [u]_s /\
  [v1]_s = [v]_s /\ [v2]_s = 1 - [u]_s /\ [v3]_s = [v]_s.

Definition ti_init u v t1 t2 t3 s :=
  (Zodd [u]_s -> [t1]_s = 0 /\ [t2]_s = -1 /\ [t3]_s = -[v]_s) /\
  (Zeven [u]_s -> [t1]_s = 1 /\ [t2]_s = 0 /\ [t3]_s = [u]_s).

Definition uv_init vu vv u v s := [u]_s = vu /\ [v]_s = vv.

Definition C1 vu vv u v g s := uv_init vu vv u v s /\ [g]_s = 1.

Definition C2 vu vv u v g s := 0 < [u]_s /\ 0 < [v]_s /\ 0 < [g]_s /\
  [u]_ s * [g]_s = vu /\ [v]_s * [g]_s = vv.

Definition C3 vu vv u v g s :=
  Zgcd vu vv = [g]_s * Zgcd [u]_s [v]_s /\
  ~ [ (var_e u mode nat_e 2 =e nat_e O) &e (var_e v mode nat_e 2) =e nat_e O ]b_ s.

Definition C4 u3 v3 t3 s := exists k, ([t3]_s * 2 ^^ k = - [v3]_s /\ Zodd [u3]_s) \/
    ([t3]_s * 2 ^^ k = [u3]_s /\ Zodd [v3]_s) \/
    ([t3]_s * 2 ^^ k = ([u3]_s - [v3]_s) /\ (Zodd [v3]_s \/ Zodd [u3]_s)).

Definition C5 u3 v3 s := 0 < [u3]_s /\ 0 < [v3]_s.

Definition CVectors u v u1 u2 u3 v1 v2 v3 t1 t2 t3 s :=
  [u ]_ s * [t1 ]_ s + [v ]_ s * [t2 ]_ s = [t3 ]_ s /\
  [u ]_ s * [u1 ]_ s + [v ]_ s * [u2 ]_ s = [u3 ]_ s /\
  [u ]_ s * [v1 ]_ s + [v ]_ s * [v2 ]_ s = [v3 ]_ s.

binary extended gcd algorithm from The Art of Computer Programming
Module TAOCP.

Definition init u v u1 u2 u3 v1 v2 v3 t1 t2 t3 :=
  (u1 <- nat_e 1 ;
  u2 <- nat_e 0 ;
  u3 <- var_e u ;
  v1 <- var_e v ;
  v2 <- nat_e 1 .-e var_e u ;
  v3 <- var_e v) ;
  ifte var_e u mode nat_e 2 =e nat_e 1 thendo
    (t1 <- nat_e 0 ;
    t2 <- cst_e (-1) ;
    t3 <- .--e var_e v)
  elsedo
    (t1 <- nat_e 1 ;
    t2 <- nat_e 0 ;
    t3 <- var_e u).

Definition halve u v t1 t2 t3 :=
  ifte (var_e t1 mode nat_e 2 =e nat_e 0 &e var_e t2 mode nat_e 2 =e nat_e 0) thendo
    (t1 <- var_e t1 ./e nat_e 2 ;
     t2 <- var_e t2 ./e nat_e 2 ;
     t3 <- var_e t3 ./e nat_e 2)
  elsedo
    (t1 <- (var_e t1 +e var_e v) ./e nat_e 2 ;
     t2 <- (var_e t2 .-e var_e u) ./e nat_e 2 ;
     t3 <- var_e t3 ./e nat_e 2).

Definition reset u v u1 u2 u3 v1 v2 v3 t1 t2 t3 :=
  ifte (var_e t3 >>= nat_e 0) thendo
    (u1 <- var_e t1 ;
     u2 <- var_e t2 ;
     u3 <- var_e t3)
  elsedo
    (v1 <- var_e v .-e var_e t1 ;
     v2 <- .--e (var_e u +e var_e t2) ;
     v3 <- .--e var_e t3 ).

Definition subtract u v u1 u2 u3 v1 v2 v3 t1 t2 t3 :=
  (t1 <- var_e u1 .-e var_e v1 ;
   t2 <- var_e u2 .-e var_e v2 ;
   t3 <- var_e u3 .-e var_e v3) ;
  ifte (nat_e 0 >>= var_e t1) thendo
    (t1 <- var_e t1 +e var_e v ;
     t2 <- var_e t2 .-e var_e u)
  elsedo
    skip.

Definition begcd g u v u1 u2 u3 v1 v2 v3 t1 t2 t3 :=
  g <- nat_e 1 ;
  prelude u v g ;
  init u v u1 u2 u3 v1 v2 v3 t1 t2 t3 ;
  while.while (var_e t3 <>e nat_e 0) (
    while.while (var_e t3 mode nat_e 2 =e nat_e O) (
      halve u v t1 t2 t3) ;
    reset u v u1 u2 u3 v1 v2 v3 t1 t2 t3 ;
    subtract u v u1 u2 u3 v1 v2 v3 t1 t2 t3).

Section taocp.

Variables g u v u1 u2 u3 v1 v2 v3 t1 t2 t3 : ssrnat.nat_eqType.
Variables vv vu : Z.

Lemma begcd_verif_intermediate :
  nodup(g, u, v, u1, u2, u3, v1, v2, v3, t1, t2, t3) -> 0 < vu -> 0 < vv ->
  {{ fun s _ => C2 vu vv u v g s /\
    C3 vu vv u v g s /\
    uivi_init u v u1 u2 u3 v1 v2 v3 s /\
    ti_init u v t1 t2 t3 s }}
  while.while (var_e t3 <>e nat_e 0)
  (while.while (var_e t3 mode nat_e 2 =e nat_e 0) (halve u v t1 t2 t3);
    reset u v u1 u2 u3 v1 v2 v3 t1 t2 t3;
    subtract u v u1 u2 u3 v1 v2 v3 t1 t2 t3)
  {{ fun s _ => vu * [u1 ]_ s + vv * [u2 ]_ s = [g ]_ s * [u3 ]_ s /\
    Zgcd vu vv = [g ]_ s * [u3 ]_ s }}.
Proof.
move=> Hset Hvv Hvu.
while.while (var_e t3 <>e nat_e 0)
apply hoare_prop_m.hoare_while_invariant with (fun s _ =>
  C2 vu vv u v g s /\
  (Zodd [u]_s \/ Zodd [v]_s) /\
  CVectors u v u1 u2 u3 v1 v2 v3 t1 t2 t3 s /\
  C4 u3 v3 t3 s /\
  C5 u3 v3 s /\
  Zgcd [u]_s [v]_s = Zgcd [u3]_s [v3]_s).
rewrite /while.entails => s h.
case => [[Hu [Hv [Hg [u_g v_g]]] [[Hgcd Hcond] [[Hu1 [Hu2 [Hu3 [Hv1 [Hv2 Hv3]]]]]]]] Ht].
rewrite /CVectors Hu1 Hu2 Hu3 Hv1 Hv2 Hv3.
repeat (split; first by tauto).
case: (Zeven_odd_dec [u]_s) => u_parity.
- case: Ht => _.
  move/(_ u_parity) => [Ht1 [Ht2 Ht3]].
  rewrite /C2 /C4 /C5 Ht1 Ht2 Ht3 Hu3 Hv3 Zmult_1_r Zmult_0_r Zplus_0_r.
  split; first by done.
  split.
    move/negP : Hcond => /=.
    rewrite negb_and /ZIT.t_eqb /ZIT.t_mod /=.
    case/orP; move/Zeq_boolP => X.
    + left; by apply not_Zmod_2_Zodd.
    + right; by apply not_Zmod_2_Zodd.
  split.
    repeat (split; first by tauto).
    ring.
  split; last by done.
  exists O; rewrite [Zpower _ _]/= !Zmult_1_r; right; left.
  split; first by reflexivity.
  move/negP : Hcond => /=.
  rewrite negb_and /ZIT.t_eqb /ZIT.t_mod.
  case/orP; move/Zeq_boolP.
  + move/not_Zmod_2_Zodd; by move/Zodd_not_Zeven.
  + by move/not_Zmod_2_Zodd.
- case: Ht.
  move/(_ u_parity) => [Ht1 [Ht2 Ht3]] _.
  rewrite /C2 /C4 /C5 Ht1 Ht2 Ht3 Hu3 Hv3 Zmult_1_r Zmult_0_r Zplus_0_l Zmult_0_r Zplus_0_r.
  split; first by done.
  split.
    move/negP : Hcond => /=.
    rewrite negb_and /ZIT.t_eqb /ZIT.t_mod /=.
    case/orP; move/Zeq_boolP => X.
    * left; by apply not_Zmod_2_Zodd.
    * right; by apply not_Zmod_2_Zodd.
  split.
    split; first by ring.
    split; [done | ring].
  split; last by done.
  exists O; rewrite [Zpower _ _]/= !Zmult_1_r; tauto.
- rewrite /while.entails => s h.
  case => [[[Hu [Hv [Hg [u_g v_g]]]] [H6 [[Hti [Hui Hvi]] [H10 [[Hu3 Hv3] H14]]]]] H1].
  split; first by rewrite -u_g -v_g -Hui; ring.
  rewrite /hoare_m.eval_b /= /ZIT.t_eqb in H1.
  move/negPn : H1; move/Zeq_boolP => H1.
  case: H10 => k [H10|[H10 | H10]].
  + have abs : [v3 ]_ s = 0.
      move: (Zpower_0 k) => X.
      case: H10 => H10 H10'.
      rewrite H1 Zmult_0_l in H10; by myomega.
    rewrite abs Zgcd_0 Zabs_eq in H14; last by myomega.
    rewrite -H14 -Zgcd_mult; last by myomega.
    by rewrite Zmult_comm u_g Zmult_comm v_g.
  + have abs : [u ]_ s = 0.
      rewrite H1 Zmult_0_l in H10; by myomega.
    rewrite abs in Hu; by apply Zlt_irrefl in Hu.
  + have abs : [u3 ]_ s = [v3 ]_ s.
      rewrite H1 Zmult_0_l in H10; by myomega.
    rewrite -abs Zgcd_same in H14; last by myomega.
    rewrite -H14 -u_g -v_g -Zgcd_mult; last by myomega.
    by rewrite (Zmult_comm [u ]_ s) (Zmult_comm [v ]_ s).

apply while.hoare_seq with (fun s h =>
  C2 vu vv u v g s /\
  (Zodd [u ]_ s \/ Zodd [v ]_ s) /\
  CVectors u v u1 u2 u3 v1 v2 v3 t1 t2 t3 s /\
  C4 u3 v3 t3 s /\
  C5 u3 v3 s /\
  Zgcd vu vv = [g ]_ s * Zgcd [u3 ]_ s [v3 ]_ s /\
  Zodd [t3]_s ).

rewrite /halve.
apply hoare_prop_m.hoare_while_invariant with (fun s h =>
  C2 vu vv u v g s /\
  (Zodd [u ]_ s \/ Zodd [v ]_ s) /\
  CVectors u v u1 u2 u3 v1 v2 v3 t1 t2 t3 s /\
  C4 u3 v3 t3 s /\
  C5 u3 v3 s /\
  Zgcd [u ]_ s [v ]_ s = Zgcd [u3 ]_ s [v3 ]_ s /\
  [t3]_s <> 0).

move=> s h /=.
case => [[[Hu [Hv [Hg [u_g v_g]]]] [H6 [[Hti [Hui Hvi]] [H10 [[Hu3 Hv3] H14]]]]] H1].
repeat (split => //).
by move/Zeq_boolP : H1.

move=> s h /=.
case => [[[Hu [Hv [Hg [u_g v_g]]]] [H6 [[Hti [Hui Hvi]] [H10 [[Hu3 Hv3] [H14 H15]]]]]] H1].
repeat (split; first by done).
split.
  rewrite /C2 -u_g -v_g -H14 -Zgcd_mult; last by myomega.
  f_equal; by rewrite Zmult_comm.
rewrite /hoare_m.eval_b /= /ZIT.t_eqb /ZIT.t_mod in H1.
move/Zeq_boolP : H1.
by move/not_Zmod_2_Zodd.

apply while.hoare_ifte.
- apply hoare_assign with (fun s h => C2 vu vv u v g s /\
  (Zodd [u ]_ s \/ Zodd [v ]_ s) /\
  ([u ]_ s * 2 * [t1 ]_ s + [v ]_ s * [t2 ]_ s = [t3 ]_ s /\
   [u ]_ s * [u1 ]_ s + [v ]_ s * [u2 ]_ s = [u3 ]_ s /\
   [u ]_ s * [v1 ]_ s + [v ]_ s * [v2 ]_ s = [v3 ]_ s) /\
  C4 u3 v3 t3 s /\
  C5 u3 v3 s /\
  Zgcd [u ]_ s [v ]_ s = Zgcd [u3 ]_ s [v3 ]_ s /\
  [t3 ]_ s <> 0 /\
  Zeven [t3]_s /\
  Zeven [t2]_s).
  move=> s h /=.
  case => [[[[Hu [Hv [Hg [u_g v_g]]]] [H7 [[Hti [Hui Hvi]] [H11 [[Hu3 Hv3] [H14 H16]]]]]] H3] H1].
  rewrite /wp_assign /C2 /C4 /C5.
  repeat Store_upd.
  repeat (split; first by tauto).
  split.
    rewrite -Zmult_assoc div_e_exact_full_2 //.
    rewrite /hoare_m.eval_b /= in H1.
    case/andP : H1 => H1 _.
    rewrite /ZIT.t_eqb /ZIT.t_mod /= in H1.
    by move/Zeq_boolP : H1.
  repeat (split; first by done).
  rewrite /hoare_m.eval_b /= /ZIT.t_eqb /ZIT.t_mod /= in H1, H3.
  move/Zeq_boolP : H3 => H3.
  case/andP : H1 => _.
  move/Zeq_boolP => H1.
  split; by apply Zmod_2_Zeven.

apply hoare_assign with (fun s _ => C2 vu vv u v g s /\
  (Zodd [u ]_ s \/ Zodd [v ]_ s) /\
  ([u ]_ s * 2 * [t1 ]_ s + [v ]_ s * 2 * [t2 ]_ s = [t3 ]_ s /\
   [u ]_ s * [u1 ]_ s + [v ]_ s * [u2 ]_ s = [u3 ]_ s /\
   [u ]_ s * [v1 ]_ s + [v ]_ s * [v2 ]_ s = [v3 ]_ s) /\
  C4 u3 v3 t3 s /\
  C5 u3 v3 s /\
  Zgcd [u ]_ s [v ]_ s = Zgcd [u3 ]_ s [v3 ]_ s /\
  [t3 ]_ s <> 0 /\ Zeven [t3 ]_ s ).

move=> s h.
case => [[Hu [Hv [Hg [u_g v_g]]]] [H5 [[Hti [Hui Hvi]] [H9 [[Hu3 Hv3] [H12 [H13 [H14 H16]]]]]]]].
rewrite /wp_assign /C2 /C4 /C5.
repeat Store_upd.
repeat (split; first by tauto).
split; last by done.
rewrite -(Zmult_assoc [v]_s) div_e_exact_full_2 //; by apply Zeven_Zmod_2.

apply hoare_assign' => s h.
case => [[Hu [Hv [Hg [u_g v_g]]] [H5 [[Hti [Hui Hvi] [H9 [[Hu3 Hv3] [H15 [H16 H17]]]]]]]]].
rewrite /wp_assign /C2 /C4 /C5 /CVectors.
repeat Store_upd.
repeat (split; first by tauto).
split.
  rewrite /= -Hti /ZIT.t_div.
  set tmp := _ + [v]_s * 2 * _ .
  have -> : tmp = ([u ]_ s * [t1 ]_ s + [v ]_ s * [t2 ]_ s) * 2 by rewrite /tmp; ring.
  by rewrite Z_div_mult_full.
repeat (split; first by assumption).
split.
  case/Zeven_ex : H17 => m H17.
  rewrite [eval _ _]/= H17 (Zmult_comm 2) /ZIT.t_div Z_div_mult_full //.
  case: H9 => k [ [H9 H9'] | ].
  + exists (S k); left.
    by rewrite Zpower_S Zmult_assoc -(Zmult_comm 2) -H17.
  + case ; exists (S k); right.
    * left; by rewrite Zpower_S Zmult_assoc -(Zmult_comm 2) -H17.
    * right; by rewrite Zpower_S Zmult_assoc -(Zmult_comm 2) -H17.
repeat (split; first by done).
case/Zeven_ex : H17 => m H17.
rewrite /= H17 (Zmult_comm 2) /ZIT.t_div Z_div_mult_full //; by myomega.

apply hoare_assign with (fun s h => C2 vu vv u v g s /\
  (Zodd [u ]_ s \/ Zodd [v ]_ s) /\
  ([u ]_ s * (2 * [t1 ]_ s - [v]_s) + [v ]_ s * [t2 ]_ s = [t3 ]_ s /\
   [u ]_ s * [u1 ]_ s + [v ]_ s * [u2 ]_ s = [u3 ]_ s /\
   [u ]_ s * [v1 ]_ s + [v ]_ s * [v2 ]_ s = [v3 ]_ s) /\
  C4 u3 v3 t3 s /\
  C5 u3 v3 s /\
  Zgcd [u ]_ s [v ]_ s = Zgcd [u3 ]_ s [v3 ]_ s /\
  [t3 ]_ s <> 0 /\ Zeven [t3]_s /\
  (Zodd (2 * [t1]_s - [v]_s) \/ Zodd [t2]_s)).

move=> s h.
case => [[[[Hu [Hv [Hg [u_g v_g]]]] [H7 [[Hti [Hui Hvi]] [H11 [[Hu3 Hv3] [H14 H16]]]]]] H3] H1].
rewrite /wp_assign /C2 /C4 /C5.
repeat Store_upd.
rewrite /hoare_m.eval_b /= /ZIT.t_eqb /ZIT.t_mod in H1 H3.
rewrite negb_and in H1.

have t1_v : [var_e t1 +e var_e v ]e_ s mod 2 = 0.
  rewrite /= /ZIT.t_plus.
  case/orP: H1; move/Zeq_boolP => H1.
  - apply Zeven_Zmod_2, Zodd_plus_Zodd.
    by apply not_Zmod_2_Zodd.
    case: H7 => H7; last by exact H7.
    move/Zeq_boolP : H3.
    move/Zmod_2_Zeven.
    rewrite -Hti.
    case/Zeven_plus_inv.
    + case=> H31 H32.
      have : Zodd ([u ]_ s * [t1 ]_ s).
        apply Zodd_mult_Zodd => //; by apply not_Zmod_2_Zodd.
      by move/Zodd_not_Zeven.
    + case => _.
      by case/Zodd_mult_inv=> ? _.
  - case: H7 => H7.
    + move/Zeq_boolP : H3.
      move/Zmod_2_Zeven.
      rewrite -Hti.
      case/Zeven_plus_inv => [[u_t1 v_t2]|[]].
      * case/Zeven_mult_inv : v_t2 => v_t2.
        - case/Zeven_mult_inv : u_t1 => u_t1.
          + by move/Zodd_not_Zeven : H7.
          + by apply Zeven_Zmod_2, Zeven_plus_Zeven.
        - move/not_Zmod_2_Zodd : H1.
          by move/Zodd_not_Zeven.
      * case/Zodd_mult_inv=> _ odd_t1.
        case/Zodd_mult_inv=> odd_v _.
        by apply Zeven_Zmod_2, Zodd_plus_Zodd.
    + move/Zeq_boolP : H3.
      move/Zmod_2_Zeven.
      rewrite -Hti.
      case/Zeven_plus_inv => [[u_t1 v_t2]| []].
      * have : Zodd ([v ]_ s * [t2 ]_ s).
          apply Zodd_mult_Zodd => //; by apply not_Zmod_2_Zodd.
        by move/Zodd_not_Zeven.
      * case/Zodd_mult_inv=> _ odd_t1 _.
        by apply Zeven_Zmod_2, Zodd_plus_Zodd.

split; first by done.
repeat (split; first by assumption).
split.
  split; [rewrite div_e_exact_full_2 //= /ZIT.t_plus -Hti; ring | done].
repeat (split; first by done).
split; first by apply Zmod_2_Zeven; by apply/Zeq_boolP.
case/orP : H1 => H1.
- left.
  rewrite div_e_exact_full_2 //.
  move/Zeq_boolP : H1.
  move/not_Zmod_2_Zodd.
  suff : [var_e t1 +e var_e v ]e_ s - [v ]_ s = [t1]_s by move=> ->.
  rewrite /= /ZIT.t_plus; ring.
- right.
  move/Zeq_boolP : H1.
  by move/not_Zmod_2_Zodd.

apply hoare_assign with (fun s h => C2 vu vv u v g s /\
  (Zodd [u ]_ s \/ Zodd [v ]_ s) /\
  ([u ]_ s * (2 * [t1 ]_ s - [v]_s) + [v ]_ s * (2 * [t2 ]_ s + [u]_s) = [t3 ]_ s /\
   [u ]_ s * [u1 ]_ s + [v ]_ s * [u2 ]_ s = [u3 ]_ s /\
   [u ]_ s * [v1 ]_ s + [v ]_ s * [v2 ]_ s = [v3 ]_ s) /\
  C4 u3 v3 t3 s /\
  C5 u3 v3 s /\
  Zgcd [u ]_ s [v ]_ s = Zgcd [u3 ]_ s [v3 ]_ s /\
  [t3 ]_ s <> 0 /\ Zeven [t3]_s).

move=> s h H.
rewrite (Zmult_comm 2) /= -(Zmult_comm 2) in H.
case: H => [[Hu [Hv [Hg [u_g v_g]]]] [H5 [[Hti [Hui Hvi]] [H9 [[Hu3 Hv3] [H12 [H13 [H14 H16]]]]]]]].
rewrite /C2 /wp_assign /C4 /C5.
repeat Store_upd.
have t2_u : [var_e t2 .-e var_e u ]e_ s mod 2 = 0.
  rewrite /= /ZIT.t_minus.
  apply Zeven_Zmod_2.
  case: H5 => [odd_u | odd_v].
  - case: H16 => H16.
    + move: H14.
      rewrite -Hti.
      case/Zeven_plus_inv.
      * case.
        case/Zeven_mult_inv; by move/Zeven_not_Zodd.
      * case.
        case/Zodd_mult_inv=> K1 _.
        case/Zodd_mult_inv=> _ K4.
        apply Zodd_plus_Zodd => //; by apply Zodd_opp.
    + apply Zodd_plus_Zodd => //; by apply Zodd_opp.
  - case: H16 => H16.
    + move: H14.
      rewrite -Hti.
      case/Zeven_plus_inv.
      * case.
        case/Zeven_mult_inv => [u_even | ].
        - case/Zeven_mult_inv => [ | even_t2].
          by move/Zeven_not_Zodd.
          apply Zeven_plus_Zeven => //; by apply Zeven_opp.
        - by move/Zeven_not_Zodd.
      * case.
        case/Zodd_mult_inv=> odd_u _.
        case/Zodd_mult_inv=> _ odd_t2.
        apply Zodd_plus_Zodd => //; by apply Zodd_opp.
    + move: H14.
      rewrite -Hti.
      case/Zeven_plus_inv.
      * case.
        case/Zeven_mult_inv => [u_even | _]; case/Zeven_mult_inv; by move/Zeven_not_Zodd.
      * case.
        case/Zodd_mult_inv=> odd_u _ _.
        apply Zodd_plus_Zodd => //; by apply Zodd_opp.

repeat (split; first by done).
split; last by done.
split; [rewrite div_e_exact_full_2 // /ZIT.t_plus -Hti (Zmult_comm 2) /= /ZIT.t_minus; ring | done].

apply hoare_assign' => s h H.
rewrite !(Zmult_comm 2) in H.
case: H => [[Hu [Hv [Hg [u_g v_g]]]] [H5 [[Hti [Hui Hvi]] [H9 [[Hu3 Hv3] [H12 [H13 H15]]]]]]].
rewrite /C2 /wp_assign /C4 /C5 /CVectors.
repeat Store_upd.
repeat (split; first by tauto).
split.
  rewrite /= -Hti /ZIT.t_div.
  have : ([u ]_ s * ([t1 ]_ s * 2 - [v ]_ s) + [v ]_ s * ([t2 ]_ s * 2 + [u ]_ s)) =
    ([u ]_ s * [t1 ]_ s + [v ]_ s * [t2 ]_ s) * 2 by ring.
  move=> ->; by rewrite Z_div_mult_full.
repeat (split; first by done).
split.
  case/Zeven_ex : H15 => m H15.
  rewrite [eval _ _]/= H15 (Zmult_comm 2) /ZIT.t_div Z_div_mult_full //.
  case: H9 => k [[H9 H9']|].
  - exists (S k); left.
    by rewrite Zpower_S Zmult_assoc -(Zmult_comm 2) -H15.
  - case=> H9; exists (S k); right.
    + left; by rewrite Zpower_S Zmult_assoc -(Zmult_comm 2) -H15.
    + right; by rewrite Zpower_S Zmult_assoc -(Zmult_comm 2) -H15.
repeat (split; first by assumption).
case/Zeven_ex : H15 => m H15 /=.
rewrite H15 Zmult_comm /ZIT.t_div Z_div_mult_full //; by myomega.

apply while.hoare_seq with (fun s _ => C2 vu vv u v g s /\
  (Zodd [u ]_ s \/ Zodd [v ]_ s) /\
  CVectors u v u1 u2 u3 v1 v2 v3 t1 t2 t3 s /\
  (0 <= [t3]_s -> Zgcd vu vv = [g ]_ s * Zgcd [t3 ]_ s [v3 ]_ s /\ Zodd [u3]_s /\
    [u3]_s = [t3]_s ) /\
  ([t3]_s < 0 -> Zgcd vu vv = [g ]_ s * Zgcd [u3 ]_ s [t3 ]_ s /\ Zodd [v3]_s /\
    [v3]_s = - [t3]_s ) /\
  C5 u3 v3 s).

rewrite /reset.
apply while.hoare_ifte.

apply hoare_assign with (fun s h => C2 vu vv u v g s /\
  (Zodd [u ]_ s \/ Zodd [v ]_ s) /\
  ([u ]_ s * [t1 ]_ s + [v ]_ s * [t2 ]_ s = [t3 ]_ s /\
   [u ]_ s * [u1 ]_ s + [v ]_ s * [t2 ]_ s = [t3 ]_ s /\
   [u ]_ s * [v1 ]_ s + [v ]_ s * [v2 ]_ s = [v3 ]_ s) /\
  C4 u3 v3 t3 s /\
  Zgcd vu vv = [g ]_ s * Zgcd [u3 ]_ s [v3 ]_ s /\ Zodd [t3 ]_ s /\
  C5 u3 v3 s /\
  0 < [t3]_s).

move=> s h /=.
case => [[[Hu [Hv [Hg [u_g v_g]]]] [H6 [[Hti [Hui Hvi]] [H10 [[Hu3 Hv3] [H13 H15]]]]]] H1].
rewrite /wp_assign /C2 /C4 /C5.
repeat Store_upd.
rewrite /hoare_m.eval_b /= /ZIT.t_geb in H1.
move/Zge_boolP : H1 => H1.
repeat (split; first by tauto).
move/Zge_le : H1.
case/Zle_lt_or_eq => H1 //.
by rewrite -H1 in H15.

apply hoare_assign with (fun s h => C2 vu vv u v g s /\
  (Zodd [u ]_ s \/ Zodd [v ]_ s) /\
  ([u ]_ s * [t1 ]_ s + [v ]_ s * [t2 ]_ s = [t3 ]_ s /\
   [u ]_ s * [u1 ]_ s + [v ]_ s * [u2 ]_ s = [t3 ]_ s /\
   [u ]_ s * [v1 ]_ s + [v ]_ s * [v2 ]_ s = [v3 ]_ s) /\
  C4 u3 v3 t3 s /\
  Zgcd vu vv = [g ]_ s * Zgcd [u3 ]_ s [v3 ]_ s /\
  Zodd [t3 ]_ s /\ C5 u3 v3 s /\ 0 < [t3 ]_ s).

move=> s h /= H.
rewrite /wp_assign /C2 /C4 /C5.
repeat Store_upd.
tauto.

apply hoare_assign' => s h.
case => [[Hu [Hv [Hg [u_g v_g]]]] [H5 [[Hti [Hui Hvi]] [H9 [H10 [H11 [[Hu3 Hv3] H15]]]]]]].
rewrite /wp_assign /C2 /C4 /C5 /CVectors.
repeat Store_upd.
repeat (split; first by tauto).
split.
  move=> Ht3.
  case: H9 => k [ [H9 H9'] | [ [H9 H9'] | [H9 H9']]].
  - suff : False by done.
    have abs : -[v3]_s < 0 by myomega.
    rewrite -H9 in abs.
    move/Zlt_not_le : abs.
    apply.
    apply Zmult_le_0_compat => //; by apply Zpower_0'.
  - rewrite -H9 in H10.
    by rewrite Zgcd_Zpower_odd // in H10.
  - have Htmp : [u3 ]_ s = [t3 ]_ s * 2 ^^ k + [v3 ]_ s by myomega.
    rewrite Htmp in H10.
    move: (Zgcd_for_euclid ([t3 ]_ s * 2 ^^ k) [v3 ]_ s 1) => X.
    rewrite Zmult_1_l in X.
    rewrite {}X in H10.
    destruct k as [|k].
    - by rewrite /= Zmult_1_r in H10.
    - case: H9' => // H9'.
      by rewrite Zgcd_Zpower_odd // in H10.
      rewrite Zgcd_Zpower_odd // in H10.
      have -> : [v3 ]_ s = [u3 ]_ s - [t3 ]_ s * 2 ^^ S k by myomega.
      apply Zodd_plus_Zeven => //.
      by apply Zeven_opp, Zeven_mult_Zeven_r, Zeven_power.
split; last by done.
move=> Ht3.
suff : False by done.
by myomega.

apply hoare_assign with (fun s h => C2 vu vv u v g s /\
  (Zodd [u ]_ s \/ Zodd [v ]_ s) /\
  ([u ]_ s * [t1 ]_ s + [v ]_ s * [t2 ]_ s = [t3 ]_ s /\
   [u ]_ s * [u1 ]_ s + [v ]_ s * [u2 ]_ s = [u3 ]_ s /\
   [u ]_ s * [v1]_s + [v]_s * (- [u]_s - [t2]_s ) = - [t3 ]_ s) /\
  C4 u3 v3 t3 s /\
  C5 u3 v3 s /\
  Zgcd vu vv = [g ]_ s * Zgcd [u3 ]_ s [v3 ]_ s /\ Zodd [t3 ]_ s /\
  [t3]_s < 0).

move=> s h /= H.
case : H => [[[Hu [Hv [Hg [u_g v_g]]]] [H6 [[Hti [Hui Hvi]] [H10 [[Hu3 Hv3] [H13 H15]]]]]] H1].
rewrite /wp_assign /C2 /C4 /C5.
repeat Store_upd.
repeat (split; first by done).
split.
  split; first by rewrite -Hti /= /ZIT.t_minus; ring.
  split; [done | rewrite -Hti /= /ZIT.t_minus; ring].
repeat (split; first by done).
rewrite /hoare_m.eval_b /= /ZIT.t_geb in H1.
move/Zge_boolP : H1 => ?; by myomega.

apply hoare_assign with (fun s _ => C2 vu vv u v g s /\
  (Zodd [u ]_ s \/ Zodd [v ]_ s) /\
  ([u ]_ s * [t1 ]_ s + [v ]_ s * [t2 ]_ s = [t3 ]_ s /\
   [u ]_ s * [u1 ]_ s + [v ]_ s * [u2 ]_ s = [u3 ]_ s /\
   [u ]_ s * [v1]_s + [v]_s * [v2]_s = - [t3 ]_ s) /\
  C4 u3 v3 t3 s /\
  C5 u3 v3 s /\
  Zgcd vu vv = [g ]_ s * Zgcd [u3 ]_ s [v3 ]_ s /\
  Zodd [t3 ]_ s /\ [t3 ]_ s < 0).
move=> s h.
case => [[Hu [Hv [Hg [u_g v_g]]]] [H6 [[Hti [Hui Hvi]] [H10 [H11 [H12 [H13 H15]]]]]]].
rewrite /wp_assign /C2 /C4 /C5.
repeat Store_upd.
repeat (split => //).
rewrite /= /ZIT.t_plus -Hvi; ring.

apply hoare_assign' => s h.
case => [[Hu [Hv [Hg [u_g v_g]]]] [H6 [[Hti [Hui Hvi]] [H10 [[Hu3 Hv3] [H13 [H15 H16]]]]]]].
rewrite /wp_assign /C2 /C4 /C5 /CVectors.
repeat Store_upd.
split; first by done.
repeat (split; first by done).
rewrite /=.
split; first by move/Zle_not_lt.
split; last by myomega.
move=> _.
  rewrite /=.
  case: H10 => k [ [H10 H10'] | [ [H10 H10'] | [H10 H10'] ] ].
  - have X : [v3 ]_ s = - ([t3 ]_ s * 2 ^^ k) by myomega.
    rewrite {}X Zgcd_opp in H13.
    symmetry in H13.
    rewrite Zgcd_sym Zgcd_Zpower_odd // in H13.
    split; first by symmetry; rewrite Zgcd_sym.
    split; [by apply Zodd_opp | done].
  - rewrite -H10 in Hu3.
    move: (Zmult_lt_0_reg_r _ _ (Zpower_0 k) Hu3) => abs.
    suff : False by done. by myomega.
  - have Htmp : [v3]_s = [u3 ]_ s - [t3 ]_ s * 2 ^^ k by myomega.
    rewrite Htmp in H13.
    move: (Zgcd_for_euclid (-[t3]_s * 2 ^^ k) ([u3]_s) 1).
    rewrite Zmult_1_l Zplus_comm -Zopp_mult_distr_l Zplus_Zopp Zgcd_sym.
    move=> X.
    rewrite X (Zgcd_sym _ [u3]_s) Zgcd_opp (Zgcd_sym [u3]_s) in H13.
    destruct k.
    + rewrite /= Zmult_1_r in H13.
      split; first by rewrite (Zgcd_sym [u3]_s).
      split; [by apply Zodd_opp | done].
    + rewrite Zgcd_Zpower_odd // in H13; last first.
        have -> : [u3 ]_ s = [v3 ]_ s + [t3 ]_ s * 2 ^^ S k by myomega.
        apply Zodd_plus_Zeven; last by apply Zeven_mult_Zeven_r, Zeven_power.
        case: H10' => H10' //.
        rewrite Htmp.
        apply Zodd_plus_Zeven => //.
        by apply Zeven_opp, Zeven_mult_Zeven_r, Zeven_power.
      split; first by rewrite (Zgcd_sym [u3]_s).
      split; [by apply Zodd_opp | done].

rewrite /subtract.

apply while.hoare_seq with (fun s _ => C2 vu vv u v g s /\
  (Zodd [u ]_ s \/ Zodd [v ]_ s) /\
  CVectors u v u1 u2 u3 v1 v2 v3 t1 t2 t3 s /\
  [t3]_s = [u3]_s - [v3]_s /\
  Zgcd vu vv = [g ]_ s * Zgcd [t3 ]_ s [v3 ]_ s /\
  (Zodd [v3 ]_ s \/ Zodd [u3 ]_ s) /\
  C5 u3 v3 s).

apply hoare_assign with (fun s _ => C2 vu vv u v g s /\
  (Zodd [u ]_ s \/ Zodd [v ]_ s) /\
  ([u ]_ s * [t1 ]_ s + [v ]_ s * ([u2]_s - [v2 ]_ s) = [u3 ]_ s - [v3]_s /\
   [u ]_ s * [u1 ]_ s + [v ]_ s * [u2 ]_ s = [u3 ]_ s /\
   [u ]_ s * [v1 ]_ s + [v ]_ s * [v2 ]_ s = [v3 ]_ s) /\
  (0 <= [t3]_s -> Zgcd vu vv = [g ]_ s * Zgcd [t3 ]_ s [v3 ]_ s /\ Zodd [u3]_s /\
    [u3]_s = [t3]_s ) /\
  ([t3]_s < 0 -> Zgcd vu vv = [g ]_ s * Zgcd [u3 ]_ s [t3 ]_ s /\ Zodd [v3]_s /\
    [v3]_s = - [t3]_s) /\
  C5 u3 v3 s).

move=> s h.
case => [[Hu [Hv [Hg [u_g v_g]]]] [H5 [[Hti [Hui Hvi]] [H9 [H10 [Hu3 Hv3]]]]]].
rewrite /wp_assign /C2 /C5 /CVectors.
repeat Store_upd.
repeat (split; first by done).
split; last by done.
split; [rewrite /= /ZIT.t_minus -Hui -Hvi; ring | done].

apply hoare_assign with (fun s _ => C2 vu vv u v g s /\
  (Zodd [u ]_ s \/ Zodd [v ]_ s) /\
  ([u ]_ s * [t1 ]_ s + [v ]_ s * [t2]_s = [u3 ]_ s - [v3]_s/\
   [u ]_ s * [u1 ]_ s + [v ]_ s * [u2 ]_ s = [u3 ]_ s /\
   [u ]_ s * [v1 ]_ s + [v ]_ s * [v2 ]_ s = [v3 ]_ s) /\
  (0 <= [t3]_s -> Zgcd vu vv = [g ]_ s * Zgcd [t3 ]_ s [v3 ]_ s /\ Zodd [u3]_s /\
    [u3]_s = [t3]_s) /\
  ([t3]_s < 0 -> Zgcd vu vv = [g ]_ s * Zgcd [u3 ]_ s [t3 ]_ s /\ Zodd [v3]_s /\
    [v3]_s = - [t3]_s) /\
  C5 u3 v3 s).

move=> s h.
case => [[Hu [Hv [Hg [u_g v_g]]]] [H5 [[Hti [Hui Hvi]] [H9 [H10 [Hu3 Hv3]]]]]].
rewrite /wp_assign /C2 /C5.
by repeat Store_upd.

apply hoare_assign'.
move=> s h [[Hu [Hv [Hg [u_g v_g]]]] [H5 [[Hti [Hui Hvi]] [H9 [H10 [Hu3 Hv3]]]]]].
rewrite /wp_assign /C2 /C5 /CVectors.
repeat Store_upd.
repeat (split; first by tauto).
split.
  case: (Z_lt_le_dec [t3]_s 0).
  - move/H10 => H10'.
    move: (Zgcd_for_euclid [u3]_s [v3]_s (-1)).
    rewrite Zopp_eq_mult_neg_1_L Zplus_Zopp => ->.
    case: H10' => H10' [H10'' H10'''].
    rewrite H10''' Zgcd_opp; tauto.
  - move/H9.
    case => H9' [H9'' H9'''].
    rewrite /= H9'''.
    move: (Zgcd_for_euclid [t3]_s [v3]_s (-1)).
    by rewrite Zopp_eq_mult_neg_1_L Zplus_Zopp => ->.
split; last by done.
case: (Z_lt_le_dec [t3]_s 0).
- move/H10; tauto.
- move/H9; tauto.

apply while.hoare_ifte.

t1 <- svar t1 +e svar v

apply hoare_assign with (fun s h => C2 vu vv u v g s /\
  (Zodd [u ]_ s \/ Zodd [v ]_ s) /\
  ([u ]_ s * [t1 ]_ s + [v ]_ s * ([t2]_s - [u]_s) = [t3 ]_ s /\
   [u ]_ s * [u1 ]_ s + [v ]_ s * [u2 ]_ s = [u3 ]_ s /\
   [u ]_ s * [v1 ]_ s + [v ]_ s * [v2 ]_ s = [v3 ]_ s) /\
  [t3 ]_ s = [u3 ]_ s - [v3 ]_ s /\
  Zgcd vu vv = [g ]_ s * Zgcd [t3 ]_ s [v3 ]_ s /\
  (Zodd [v3 ]_ s \/ Zodd [u3 ]_ s) /\
  C5 u3 v3 s).

move=> s h.
case => [[[Hu [Hv [Hg [u_g v_g]]]] [H6 [[Hti [Hui Hvi]] [H10 [H11 [H12 [Hu3 Hv3]]]]]]] H1].
rewrite /wp_assign /C2 /C5.
repeat Store_upd.
repeat (split; first by done).
split; last by done.
split; [rewrite /= /ZIT.t_plus -Hti; ring | done].

apply hoare_assign' => s h.
case=> [[Hu [Hv [Hg [u_g v_g]]]] [H5 [[Hti [Hui Hvi]] [H9 [H10 [H11 [Hu3 Hv3]]]]]]].
rewrite /wp_assign /C2 /C4 /C5 /CVectors.
repeat Store_upd.
repeat (split; first by done).
split; first by exists O; right; right; rewrite /= Zmult_1_r.
repeat (split; first by assumption).
rewrite -(Zgcd_for_euclid [u3]_s [v3]_s (-1)) Zopp_eq_mult_neg_1_L Zplus_Zopp.
rewrite H9 -u_g -v_g -2!(Zmult_comm [g]_s) Zgcd_mult in H10; last by myomega.
apply Zmult_reg_l in H10 => //; by myomega.

apply hoare_skip' => s h.
case => [[[Hu [Hv [Hg [u_g v_g]]]] [H6 [[Hti [Hui Hvi]] [H10 [H11 [H12 [Hu3 Hv3]]]]]]] H1].
rewrite /wp_assign /C2 /C4.
repeat Store_upd.
repeat (split; first by done).
split; first by exists O; right; right; rewrite /= Zmult_1_r.
repeat (split; first by assumption).
rewrite -(Zgcd_for_euclid [u3]_s [v3]_s (-1)) Zopp_eq_mult_neg_1_L Zplus_Zopp.
rewrite H10 -v_g -u_g -2!(Zmult_comm [g]_s) Zgcd_mult in H11; last by myomega.
apply Zmult_reg_l in H11 => //; by myomega.
Qed.

Lemma prelude_verif2 : nodup(u, v, g) ->
  0 < vu -> 0 < vv ->
  {{ fun s _ => C2 vu vv u v g s }}
  prelude u v g
  {{ fun s _ => C2 vu vv u v g s /\
    ~~ [ (var_e u mode nat_e 2 =e nat_e 0) &e (var_e v mode nat_e 2 =e nat_e 0) ]b_ s }}.
Proof.
move=> Hset Hvu Hvv.
apply hoare_prop_m.hoare_while_invariant with (fun s _ => C2 vu vv u v g s).
by move=> s h /= [H1 [H2 [H3 [H4 H5]]]].
by move=> s h /= [[H1 [H2 [H3 [H4 H5]]]] H6].
u <- var_e u ./e nat_e 2
apply hoare_assign with (fun s h => 0 < [ u ]_s /\ 0 < [ v ]_s /\ 0 < [ g ]_s /\
  2 * [ u ]_s * [ g ]_s = vu /\ [ v ]_s * [ g ]_s = vv /\ [ v ]_s mod 2 = 0).
move=> s h [[H1 [H2 [H3 [H4 H5]]]] H6].
rewrite /hoare_m.eval_b /= in H6; case/andP : H6. rewrite /ZIT.t_mod.
move/ZIT.t_eqb_eq => even_u. move/ZIT.t_eqb_eq => even_v.
rewrite /wp_assign; repeat Store_upd => //.
split.
  rewrite /=.
  apply Zlt_0_Zdiv => //.
  rewrite -> Zmod_divides in even_u; last by auto.
  case: even_u => k Hk; by myomega.
repeat (split; first by assumption).
split; last by done.
rewrite div_e_exact_full_2 //; by rewrite /hoare_m.eval_b in H6; omegab.
v <- var_e v ./e nat_e 2;
apply hoare_assign with (fun s h => 0 < [ u ]_s /\ 0 < [ v ]_s /\ 0 < [ g ]_s /\
  2 * [ u ]_s * [ g ]_s = vu /\ 2 * [ v ]_s * [ g ]_ s = vv).
move=> s h [H1 [H2 [H3 [H4 [H5 H6]]]]].
rewrite /wp_assign; repeat Store_upd => //.
split; first by assumption.
split.
  rewrite /=; apply Zlt_0_Zdiv => //.
  rewrite -> Zmod_divides in H6; last by auto.
  case: H6 => k Hk; by myomega.
repeat (split; first by assumption).
by rewrite div_e_exact_full_2.
g <- nat_e 2 *e var_e g
apply hoare_assign' => s h [H1 [H2 [H3 [H4 H5]]]].
rewrite /wp_assign /C2; repeat Store_upd => //.
repeat (split; first by assumption).
split; first by apply Zmult_lt_0_compat.
rewrite /= -H4 -H5 /ZIT.t_mult; split; ring.
Qed.

Lemma begcd_verif_init0 : nodup(g, u, v, u1, u2, u3, v1, v2, v3, t1, t2, t3) ->
  0 < vu -> 0 < vv ->
  {{fun s _ => C2 vu vv u v g s /\ C3 vu vv u v g s }}
  u1 <- nat_e 1;
  u2 <- nat_e 0;
  u3 <- var_e u;
  v1 <- var_e v;
  v2 <- nat_e 1 .-e var_e u;
  v3 <- var_e v
  {{fun s _ => C2 vu vv u v g s /\ C3 vu vv u v g s /\ uivi_init u v u1 u2 u3 v1 v2 v3 s}}.

Lemma begcd_verif_init1 : nodup(g,u,v,u1,u2,u3,v1,v2,v3,t1,t2,t3) ->
  0 < vu -> 0 < vv ->
  {{fun s _ => C2 vu vv u v g s /\ C3 vu vv u v g s /\ uivi_init u v u1 u2 u3 v1 v2 v3 s}}
  ifte var_e u mode nat_e 2 =e nat_e 1 thendo
    t1 <- nat_e 0;
    t2 <- cst_e (-1);
    t3 <- .--e var_e v
  elsedo (
    t1 <- nat_e 1;
    t2 <- nat_e 0;
    t3 <- var_e u)
  {{fun s _ => C2 vu vv u v g s /\ C3 vu vv u v g s /\
    uivi_init u v u1 u2 u3 v1 v2 v3 s /\ ti_init u v t1 t2 t3 s}}.

Lemma begcd_verif_init : nodup(g, u, v, u1, u2, u3, v1, v2, v3, t1, t2, t3) ->
  0 < vu -> 0 < vv ->
  {{fun s _ => C2 vu vv u v g s /\ C3 vu vv u v g s }}
  init u v u1 u2 u3 v1 v2 v3 t1 t2 t3
  {{fun s _ => C2 vu vv u v g s /\ C3 vu vv u v g s /\
    uivi_init u v u1 u2 u3 v1 v2 v3 s /\ ti_init u v t1 t2 t3 s}}.

Lemma begcd_verif_prelude_init : nodup(g, u, v, u1, u2, u3, v1, v2, v3, t1, t2, t3) ->
  0 < vu -> 0 < vv ->
  {{ fun s h => [u]_s = vu /\ [v]_s = vv }}
  g <- nat_e 1;
  prelude u v g;
  init u v u1 u2 u3 v1 v2 v3 t1 t2 t3
  {{ fun s _ => C2 vu vv u v g s /\ C3 vu vv u v g s /\
    uivi_init u v u1 u2 u3 v1 v2 v3 s /\
    ti_init u v t1 t2 t3 s }}.
Proof.
move=> Hset Hvu Hvv.
g <- nat_e 1 ;
apply hoare_assign with (fun s h => C1 vu vv u v g s).
move=> s h [Hu Hv].
by rewrite /wp_assign /C1 /uv_init; repeat Store_upd.
prelude u v g ;
apply while.hoare_seq with (fun s _ => C2 vu vv u v g s /\ C3 vu vv u v g s).
have Hset' : nodup(u, v, g) by rewrite [Equality.sort _]/= in Hset *; Nodup_nodup O.

eapply while.hoare_conseq; last by apply (prelude_verif2 Hset' Hvu Hvv).
- move=> s h /= [[H1 [H2 [H3 [H4 H5]]]] H6].
  rewrite /C2 /C3.
  repeat (split; first by myomega).
  split; last by move/negP: H6.
  rewrite -H4 -H5.
  apply Zis_gcd_gcd; last first.
    rewrite (Zmult_comm [u]_s) (Zmult_comm [v]_s).
    by apply Zis_gcd_mult, Zgcd_is_gcd.
  apply Zmult_le_0_compat => //; first by myomega.
  by apply Zgcd_is_pos.
- rewrite /C1 /uv_init /C2 => s h /= [[-> ->] ->]; by rewrite 2!Zmult_1_r.
by apply begcd_verif_init.
Qed.

NB: functional correctness only
Lemma begcd_verif:
  nodup(g, u, v, u1, u2, u3, v1, v2, v3, t1, t2, t3) -> 0 < vu -> 0 < vv ->
  {{ fun s h => [u]_s = vu /\ [v]_s = vv }}
  begcd g u v u1 u2 u3 v1 v2 v3 t1 t2 t3
  {{ fun s h => vu * [u1]_s + vv * [u2]_s = [g]_s * [u3]_s /\
    Zgcd vu vv = ([g]_s * [u3]_s)}}.

End taocp.

End TAOCP.

Definition uivi_bounds u v u1 v1 u2 v2 u3 v3 s :=
  0 <= [u1 ]_ s <= [v ]_ s /\ 0 <= [v1 ]_ s <= [v ]_ s /\
  - [u ]_ s <= [u2 ]_ s <= 0 /\ - [u ]_ s <= [v2 ]_ s <= 0 /\
  0 < [u3 ]_ s <= [u ]_ s /\ 0 < [v3 ]_ s <= [v ]_ s.

Definition ti_bounds u v t1 t2 t3 s :=
  0 <= [t1 ]_ s <= [v ]_ s /\
  - [u ]_ s <= [t2 ]_ s <= 0 /\
  - [v ]_ s <= [t3 ]_ s <= [u ]_ s.

Lemma init_bounds : forall u v u1 u2 u3 v1 v2 v3 t1 t2 t3 s,
  0 < [u]_s -> 0 < [v]_s ->
  uivi_init u v u1 u2 u3 v1 v2 v3 s ->
  ti_init u v t1 t2 t3 s ->
  uivi_bounds u v u1 v1 u2 v2 u3 v3 s /\ ti_bounds u v t1 t2 t3 s.

Module TAOCP2.

Section taocp2.

Variables g u v u1 u2 u3 v1 v2 v3 t1 t2 t3 : ssrnat.nat_eqType.
Variables vu vv : Z.

Lemma begcd_verif_with_bounds_init :
  nodup(g, u, v, u1, u2, u3, v1, v2, v3, t1, t2, t3) -> 0 < vu -> 0 < vv ->
  {{ fun s h => uv_init vu vv u v s }}
  g <- nat_e 1;
  prelude u v g;
  TAOCP.init u v u1 u2 u3 v1 v2 v3 t1 t2 t3
  {{fun s _ => C2 vu vv u v g s /\ C3 vu vv u v g s /\
    uivi_init u v u1 u2 u3 v1 v2 v3 s /\
    ti_init u v t1 t2 t3 s /\
    uivi_bounds u v u1 v1 u2 v2 u3 v3 s /\
    ti_bounds u v t1 t2 t3 s}}.

Lemma begcd_while_halve_invariant : nodup(g, u, v, u1, u2, u3, v1, v2, v3, t1, t2, t3) ->
  0 < vu -> 0 < vv ->
  {{ fun s h => (C2 vu vv u v g s /\
    (Zodd [u ]_ s \/ Zodd [v ]_ s) /\
    CVectors u v u1 u2 u3 v1 v2 v3 t1 t2 t3 s /\
    C4 u3 v3 t3 s /\
    C5 u3 v3 s /\
    Zgcd [u ]_ s [v ]_ s = Zgcd [u3 ]_ s [v3 ]_ s /\
    uivi_bounds u v u1 v1 u2 v2 u3 v3 s /\
    ti_bounds u v t1 t2 t3 s /\ [t3 ]_ s <> 0) /\
  hoare_m.eval_b (var_e t3 mode nat_e 2 =e nat_e 0) (s, h)}}
  ifte var_e t1 mode nat_e 2 =e nat_e 0 &e var_e t2 mode nat_e 2 =e nat_e 0 thendo
    t1 <- var_e t1 ./e nat_e 2;
    t2 <- var_e t2 ./e nat_e 2;
    t3 <- var_e t3 ./e nat_e 2
  elsedo
    (t1 <- (var_e t1 +e var_e v) ./e nat_e 2;
     t2 <- (var_e t2 .-e var_e u) ./e nat_e 2;
     t3 <- var_e t3 ./e nat_e 2)
  {{ fun s _ => C2 vu vv u v g s /\
    (Zodd [u ]_ s \/ Zodd [v ]_ s) /\
    CVectors u v u1 u2 u3 v1 v2 v3 t1 t2 t3 s /\
    C4 u3 v3 t3 s /\
    C5 u3 v3 s /\
    Zgcd [u ]_ s [v ]_ s = Zgcd [u3 ]_ s [v3 ]_ s /\
    uivi_bounds u v u1 v1 u2 v2 u3 v3 s /\
    ti_bounds u v t1 t2 t3 s /\ [t3 ]_ s <> 0 }}.

Lemma begcd_while_halve_invariant_stren :
  nodup(g, u, v, u1, u2, u3, v1, v2, v3, t1, t2, t3) -> 0 < vu -> 0 < vv ->
  while.entails hoare_m.store hoare_m.heap
  (fun s h => (C2 vu vv u v g s /\
      (Zodd [u ]_ s \/ Zodd [v ]_ s) /\
      CVectors u v u1 u2 u3 v1 v2 v3 t1 t2 t3 s /\
      C4 u3 v3 t3 s /\
      C5 u3 v3 s /\
      Zgcd [u ]_ s [v ]_ s = Zgcd [u3 ]_ s [v3 ]_ s /\
      uivi_bounds u v u1 v1 u2 v2 u3 v3 s /\ ti_bounds u v t1 t2 t3 s) /\
    hoare_m.eval_b (var_e t3 <>e nat_e 0) (s, h))
  (fun s _ => C2 vu vv u v g s /\
    (Zodd [u ]_ s \/ Zodd [v ]_ s) /\
      CVectors u v u1 u2 u3 v1 v2 v3 t1 t2 t3 s /\
      C4 u3 v3 t3 s /\
      C5 u3 v3 s /\
      Zgcd [u ]_ s [v ]_ s = Zgcd [u3 ]_ s [v3 ]_ s /\
      uivi_bounds u v u1 v1 u2 v2 u3 v3 s /\
      ti_bounds u v t1 t2 t3 s /\ [t3 ]_ s <> 0).

Lemma begcd_while_halve_invariant_weak :
  nodup(g, u, v, u1, u2, u3, v1, v2, v3, t1, t2, t3) -> 0 < vu -> 0 < vv ->
while.entails hoare_m.store hoare_m.heap
(fun s h => (C2 vu vv u v g s /\
    (Zodd [u ]_ s \/ Zodd [v ]_ s) /\
    CVectors u v u1 u2 u3 v1 v2 v3 t1 t2 t3 s /\
    C4 u3 v3 t3 s /\
    C5 u3 v3 s /\
    Zgcd [u ]_ s [v ]_ s = Zgcd [u3 ]_ s [v3 ]_ s /\
    uivi_bounds u v u1 v1 u2 v2 u3 v3 s /\
    ti_bounds u v t1 t2 t3 s /\ [t3 ]_ s <> 0) /\
  ~~ hoare_m.eval_b (var_e t3 mode nat_e 2 =e nat_e 0) (s, h))
(fun s _ => C2 vu vv u v g s /\
  (Zodd [u ]_ s \/ Zodd [v ]_ s) /\
  CVectors u v u1 u2 u3 v1 v2 v3 t1 t2 t3 s /\
  C4 u3 v3 t3 s /\
  C5 u3 v3 s /\
  Zgcd vu vv = [g ]_ s * Zgcd [u3 ]_ s [v3 ]_ s /\
  uivi_bounds u v u1 v1 u2 v2 u3 v3 s /\
  ti_bounds u v t1 t2 t3 s /\ Zodd [t3 ]_ s).

Lemma begcd_while_halve :
  nodup(g, u, v, u1, u2, u3, v1, v2, v3, t1, t2, t3) -> 0 < vu -> 0 < vv ->
{{ fun s h => (C2 vu vv u v g s /\
  (Zodd [u ]_ s \/ Zodd [v ]_ s) /\
  CVectors u v u1 u2 u3 v1 v2 v3 t1 t2 t3 s /\
  C4 u3 v3 t3 s /\
  C5 u3 v3 s /\
  Zgcd [u ]_ s [v ]_ s = Zgcd [u3 ]_ s [v3 ]_ s /\
  uivi_bounds u v u1 v1 u2 v2 u3 v3 s /\ ti_bounds u v t1 t2 t3 s) /\
  hoare_m.eval_b (var_e t3 <>e nat_e 0) (s, h) }}
while.while (var_e t3 mode nat_e 2 =e nat_e 0) (TAOCP.halve u v t1 t2 t3)
{{ fun s h => C2 vu vv u v g s /\
  (Zodd [u ]_ s \/ Zodd [v ]_ s) /\
  CVectors u v u1 u2 u3 v1 v2 v3 t1 t2 t3 s /\
  C4 u3 v3 t3 s /\
  C5 u3 v3 s /\
  Zgcd vu vv = [g ]_ s * Zgcd [u3 ]_ s [v3 ]_ s /\
  uivi_bounds u v u1 v1 u2 v2 u3 v3 s /\
  ti_bounds u v t1 t2 t3 s /\ Zodd [t3 ]_ s}}.

Lemma begcd_reset :
  nodup(g, u, v, u1, u2, u3, v1, v2, v3, t1, t2, t3) -> 0 < vu -> 0 < vv ->
  {{fun s _ => C2 vu vv u v g s /\
    (Zodd [u ]_ s \/ Zodd [v ]_ s) /\
    CVectors u v u1 u2 u3 v1 v2 v3 t1 t2 t3 s /\
    C4 u3 v3 t3 s /\
    C5 u3 v3 s /\
    Zgcd vu vv = [g ]_ s * Zgcd [u3 ]_ s [v3 ]_ s /\
    uivi_bounds u v u1 v1 u2 v2 u3 v3 s /\
    ti_bounds u v t1 t2 t3 s /\ Zodd [t3 ]_ s}}
  TAOCP.reset u v u1 u2 u3 v1 v2 v3 t1 t2 t3
  {{fun s _ => C2 vu vv u v g s /\
    (Zodd [u ]_ s \/ Zodd [v ]_ s) /\
     CVectors u v u1 u2 u3 v1 v2 v3 t1 t2 t3 s /\
     (0 <= [t3 ]_ s ->
       Zgcd vu vv = [g ]_ s * Zgcd [t3 ]_ s [v3 ]_ s /\
       Zodd [u3 ]_ s /\ [u3 ]_ s = [t3 ]_ s) /\
     ([t3 ]_ s < 0 ->
       Zgcd vu vv = [g ]_ s * Zgcd [u3 ]_ s [t3 ]_ s /\
       Zodd [v3 ]_ s /\ [v3 ]_ s = - [t3 ]_ s) /\
     uivi_bounds u v u1 v1 u2 v2 u3 v3 s /\
     ti_bounds u v t1 t2 t3 s /\ C5 u3 v3 s}}.

Lemma begcd_subtract_part1 : nodup(g, u, v, u1, u2, u3, v1, v2, v3, t1, t2, t3) -> 0 < vu -> 0 < vv ->
   {{fun s _ => C2 vu vv u v g s /\
     (Zodd [u ]_ s \/ Zodd [v ]_ s) /\
     CVectors u v u1 u2 u3 v1 v2 v3 t1 t2 t3 s /\
     (0 <= [t3 ]_ s ->
      Zgcd vu vv = [g ]_ s * Zgcd [t3 ]_ s [v3 ]_ s /\
      Zodd [u3 ]_ s /\ [u3 ]_ s = [t3 ]_ s) /\
     ([t3 ]_ s < 0 ->
      Zgcd vu vv = [g ]_ s * Zgcd [u3 ]_ s [t3 ]_ s /\
      Zodd [v3 ]_ s /\ [v3 ]_ s = - [t3 ]_ s) /\
     uivi_bounds u v u1 v1 u2 v2 u3 v3 s /\
     ti_bounds u v t1 t2 t3 s /\ C5 u3 v3 s}}
   t1 <- var_e u1 .-e var_e v1;
   t2 <- var_e u2 .-e var_e v2; t3 <- var_e u3 .-e var_e v3
   {{fun s _ => C2 vu vv u v g s /\
     (Zodd [u ]_ s \/ Zodd [v ]_ s) /\
     CVectors u v u1 u2 u3 v1 v2 v3 t1 t2 t3 s /\
     [t3 ]_ s = [u3 ]_ s - [v3 ]_ s /\
     Zgcd vu vv = [g ]_ s * Zgcd [t3 ]_ s [v3 ]_ s /\
     [t1 ]_ s = [u1 ]_ s - [v1 ]_ s /\
     uivi_bounds u v u1 v1 u2 v2 u3 v3 s /\
     ((0 < [t1]_s -> ti_bounds u v t1 t2 t3 s) /\
      ([t1]_s <= 0 ->
       0 <= [t1 ]_ s + [v ]_ s <= [v ]_ s /\
       - [u ]_ s <= [t2 ]_ s - [u ]_ s <= 0 /\
       - [v ]_ s <= [t3 ]_ s <= [u ]_ s)) /\
     (Zodd [v3 ]_ s \/ Zodd [u3 ]_ s) /\ C5 u3 v3 s}}.
Proof.
move=> Hset Hvu Hvv.

t1 <- var_e u1 .-e var_e v1;

apply hoare_assign with (fun s _ => C2 vu vv u v g s /\
  (Zodd [u ]_ s \/ Zodd [v ]_ s) /\
  ([u ]_ s * [t1 ]_ s + [v ]_ s * ([u2]_s - [v2 ]_ s) = [u3 ]_ s - [v3]_s /\
   [u ]_ s * [u1 ]_ s + [v ]_ s * [u2 ]_ s = [u3 ]_ s /\
   [u ]_ s * [v1 ]_ s + [v ]_ s * [v2 ]_ s = [v3 ]_ s) /\
  (0 <= [t3 ]_ s -> Zgcd vu vv = [g ]_ s * Zgcd [t3 ]_ s [v3 ]_ s/\ Zodd [u3 ]_ s
    /\ [u3 ]_ s = [t3 ]_ s) /\
  ([t3 ]_ s < 0 -> Zgcd vu vv = [g ]_ s * Zgcd [u3 ]_ s [t3 ]_ s /\ Zodd [v3 ]_ s /\
    [v3 ]_ s = - [t3 ]_ s) /\
  [t1]_s = [u1]_s - [v1]_s /\
  uivi_bounds u v u1 v1 u2 v2 u3 v3 s /\
  (([v1]_s < [u1]_s -> ti_bounds u v t1 t2 t3 s) /\
   ([u1]_s <= [v1]_s -> 0 <= [t1 ]_ s + [v]_s <= [v ]_ s /\
                        - [u ]_ s <= [t2 ]_ s <= 0 /\
                        - [v ]_ s <= [t3 ]_ s <= [u ]_ s)) /\
  C5 u3 v3 s).

move=> s h.
case => [[Hu [Hv [Hg [u_g v_g]]]] [H5 [[Hti [Hui Hvi]] [H9 [H10 [Hinvar1 [Hinvar2 [Hu3 Hv3]]]]]]]].
rewrite /wp_assign /C2.
repeat Store_upd.
repeat (split; first by done).
split.
  split; [rewrite /= /ZIT.t_minus -Hui -Hvi; ring | done].
repeat (split; first by done).
split.
  rewrite /uivi_bounds; by repeat Store_upd.
split.
  split=> u1_v1.
    rewrite /ti_bounds in Hinvar2 *.
    repeat Store_upd.
    split; last by tauto.
    rewrite /= /ZIT.t_minus.
    rewrite /uivi_bounds in Hinvar1.
    by myomega.
  rewrite /ti_bounds in Hinvar2 *.
  repeat Store_upd.
  split; last by tauto.
  rewrite /= /ZIT.t_minus.
  rewrite /uivi_bounds in Hinvar1.
  by myomega.
rewrite /C5; by repeat Store_upd.

t2 <- svar u2 .-e svar v2;

apply hoare_assign with (fun s _ => C2 vu vv u v g s /\
  (Zodd [u ]_ s \/ Zodd [v ]_ s) /\
  ([u ]_ s * [t1 ]_ s + [v ]_ s * [t2]_s = [u3 ]_ s - [v3]_s/\
   [u ]_ s * [u1 ]_ s + [v ]_ s * [u2 ]_ s = [u3 ]_ s /\
   [u ]_ s * [v1 ]_ s + [v ]_ s * [v2 ]_ s = [v3 ]_ s) /\
  (0 <= [t3 ]_ s ->
    Zgcd vu vv = [g ]_ s * Zgcd [t3 ]_ s [v3 ]_ s/\ Zodd [u3 ]_ s /\
    [u3 ]_ s = [t3 ]_ s) /\
  ([t3 ]_ s < 0 ->
    Zgcd vu vv = [g ]_ s * Zgcd [u3 ]_ s [t3 ]_ s/\ Zodd [v3 ]_ s /\
    [v3 ]_ s = - [t3 ]_ s) /\
  [t1]_s = [u1]_s - [v1]_s /\
  uivi_bounds u v u1 v1 u2 v2 u3 v3 s /\
  (([v1]_s < [u1]_s -> ti_bounds u v t1 t2 t3 s) /\
   ([u1]_s <= [v1]_s -> 0 <= [t1 ]_ s + [v]_s <= [v ]_ s /\
                        - [u ]_ s <= [t2 ]_ s - [u]_s <= 0 /\
                        - [v ]_ s <= [t3 ]_ s <= [u ]_ s)) /\
  C5 u3 v3 s).

move=> s h.
case => [[Hu [Hv [Hg [u_g v_g]]]] [H5 [[Hti [Hui Hvi]] [H9 [H10 [tuv1 [Hinvar1 [Hinvar2 [Hu3 Hv3]]]]]]]]].
rewrite /wp_assign /C2.
repeat Store_upd.
repeat (split; first by done).
split.
  rewrite /uivi_bounds; by repeat Store_upd.
split.
  split=> u1_v1.
    rewrite /ti_bounds in Hinvar2 *.
    repeat Store_upd.
    split; first by tauto.
    split; last by tauto.
    rewrite /= /ZIT.t_minus.
    move: {Hinvar2}(proj1 Hinvar2 u1_v1) => Hinvar2.
    rewrite /uivi_bounds in Hinvar1.
    split.
    by myomega.
    have Htmp : [v3]_s + [v]_s * ([u2]_s - [v2]_s) <= 0.
      have -> : [v3]_s + [v]_s * ([u2]_s - [v2]_s) = [u3 ]_ s - [u ]_ s * ([u1 ]_ s - [v1 ]_ s).
        rewrite !Zmult_minus_distr_l in Hti *.
        by myomega.
      have Htmp : [u1 ]_ s - [v1 ]_ s > 0.
        by myomega.
      apply Zle_trans with ([u ]_ s - [u ]_ s * ([u1 ]_ s - [v1 ]_ s)).
        apply Zminus_le_compat.
        by myomega.
        have Htmp2 : [u ]_ s <= [u ]_ s * ([u1 ]_ s - [v1 ]_ s).
        apply Zle_scale.
        by myomega.
        by apply Zgt_lt.
      by myomega.
      apply Zle_plus_0_inv in Htmp; last by myomega.
      apply Zle_mult_0_inv in Htmp; last by myomega.
      done.
  rewrite /ti_bounds in Hinvar2 *.
  repeat Store_upd.
  split; first by tauto.
  split; last by tauto.
  rewrite /= /ZIT.t_minus.
  rewrite /uivi_bounds in Hinvar1.
  split; last by myomega.
  move: {Hinvar2}(proj2 Hinvar2 u1_v1) => Hinvar2.
  suff : 0 <= [u2 ]_ s - [v2 ]_ s by move=> ?; by myomega.
  have Htmp : - [v]_s * ([u2]_s - [v2]_s) - [v3]_s < 0.
    have -> : - [v ]_ s * ([u2 ]_ s - [v2 ]_ s) - [v3 ]_ s =
      [u]_s * ([u1]_s - [v1]_s) - [u3]_s.
      have Htmp : [u3 ]_ s = [u ]_ s * [t1 ]_ s + [v ]_ s * ([u2 ]_ s - [v2 ]_ s) + [v3 ]_ s.
        by myomega.
      rewrite Htmp tuv1; ring.
    have Htmp : [u1 ]_ s - [v1 ]_ s <= 0 by myomega.
    have {Htmp}Htmp : [u ]_ s * ([u1 ]_ s - [v1 ]_ s) <= 0.
      apply Zmult_sign => //.
      by myomega.
    by myomega.
  have {Htmp}Htmp : - [v ]_ s * ([u2 ]_ s - [v2 ]_ s) < [v3 ]_ s.
    by myomega.
  apply Znot_gt_le => abs.
  have {Htmp}Htmp : - [v ]_ s * ([u2 ]_ s - [v2 ]_ s) < [v]_s.
    by myomega.
  rewrite Zmult_opp_comm in Htmp.
  apply Zmult_lt_compat3 in Htmp; last 2 first.
    by myomega.
    by myomega.
    by apply Zlt_irrefl in Htmp.
    by myomega.
rewrite /C5; by repeat Store_upd.

t3 <- var_e u3 .-e var_e v3

apply hoare_assign'.
move=> s h.
case => [[Hu [Hv [Hg [u_g v_g]]]] [H5 [[Hti [Hui Hvi]] [H9 [H10 [tuv1 [Hinvar1 [Hinvar2 [Hu3 Hv3]]]]]]]]].
rewrite /wp_assign /C2 /CVectors.
repeat Store_upd.
repeat (split; first by tauto).
split.
  case: (Z_lt_le_dec [t3]_s 0).
  - move/H10 => H10'.
    move: (Zgcd_for_euclid [u3]_s [v3]_s (-1)).
    rewrite Zopp_eq_mult_neg_1_L Zplus_Zopp => ->.
    case: H10' => H10' [H10'' H10'''].
    rewrite H10''' Zgcd_opp; tauto.
  - move/H9.
    case => H9' [H9'' H9'''].
    rewrite /= H9'''.
    move: (Zgcd_for_euclid [t3]_s [v3]_s (-1)).
    by rewrite Zopp_eq_mult_neg_1_L Zplus_Zopp => ->.
split; first by done.
split.
  rewrite /uivi_bounds; by repeat Store_upd.
split.
  split=> v1_u1.
    rewrite /ti_bounds in Hinvar2 *.
    repeat Store_upd.
    repeat (split; first by tauto).
    rewrite /= /ZIT.t_minus.
    rewrite /uivi_bounds in Hinvar1.
    by myomega.
  rewrite /ti_bounds in Hinvar2 *.
  repeat Store_upd.
  repeat (split; first by tauto).
  rewrite /= /ZIT.t_minus.
  rewrite /uivi_bounds in Hinvar1.
  by myomega.
split; last by rewrite /C5; repeat Store_upd.
case: (Z_lt_le_dec [t3]_s 0).
- move/H10; tauto.
- move/H9; tauto.
Qed.

Lemma begcd_subtract :
  nodup(g, u, v, u1, u2, u3, v1, v2, v3, t1, t2, t3) -> 0 < vu -> 0 < vv ->
  {{ fun s _ => C2 vu vv u v g s /\
     (Zodd [u ]_ s \/ Zodd [v ]_ s) /\
     CVectors u v u1 u2 u3 v1 v2 v3 t1 t2 t3 s /\
     (0 <= [t3 ]_ s ->
      Zgcd vu vv = [g ]_ s * Zgcd [t3 ]_ s [v3 ]_ s /\
      Zodd [u3 ]_ s /\ [u3 ]_ s = [t3 ]_ s) /\
     ([t3 ]_ s < 0 ->
      Zgcd vu vv = [g ]_ s * Zgcd [u3 ]_ s [t3 ]_ s /\
      Zodd [v3 ]_ s /\ [v3 ]_ s = - [t3 ]_ s) /\
     uivi_bounds u v u1 v1 u2 v2 u3 v3 s /\
     ti_bounds u v t1 t2 t3 s /\ C5 u3 v3 s}}
   TAOCP.subtract u v u1 u2 u3 v1 v2 v3 t1 t2 t3
   {{ fun s _ => C2 vu vv u v g s /\
     (Zodd [u ]_ s \/ Zodd [v ]_ s) /\
     CVectors u v u1 u2 u3 v1 v2 v3 t1 t2 t3 s /\
     C4 u3 v3 t3 s /\
     C5 u3 v3 s /\
     Zgcd [u ]_ s [v ]_ s = Zgcd [u3 ]_ s [v3 ]_ s /\
     uivi_bounds u v u1 v1 u2 v2 u3 v3 s /\ ti_bounds u v t1 t2 t3 s}}.

Lemma begcd_verif_intermediate_with_bounds_invariant :
  nodup(g, u, v, u1, u2, u3, v1, v2, v3, t1, t2, t3) -> 0 < vu -> 0 < vv ->
  {{ fun s h => (C2 vu vv u v g s /\
    (Zodd [u ]_ s \/ Zodd [v ]_ s) /\
    CVectors u v u1 u2 u3 v1 v2 v3 t1 t2 t3 s /\
    C4 u3 v3 t3 s /\
    C5 u3 v3 s /\
    Zgcd [u ]_ s [v ]_ s = Zgcd [u3 ]_ s [v3 ]_ s /\
    uivi_bounds u v u1 v1 u2 v2 u3 v3 s /\ ti_bounds u v t1 t2 t3 s) /\
  hoare_m.eval_b (var_e t3 <>e nat_e 0) (s, h)}}
  while.while (var_e t3 mode nat_e 2 =e nat_e 0) (TAOCP.halve u v t1 t2 t3);
  TAOCP.reset u v u1 u2 u3 v1 v2 v3 t1 t2 t3;
  TAOCP.subtract u v u1 u2 u3 v1 v2 v3 t1 t2 t3
  {{ fun s h => C2 vu vv u v g s /\
    (Zodd [u ]_ s \/ Zodd [v ]_ s) /\
    CVectors u v u1 u2 u3 v1 v2 v3 t1 t2 t3 s /\
    C4 u3 v3 t3 s /\
    C5 u3 v3 s /\
    Zgcd [u ]_ s [v ]_ s = Zgcd [u3 ]_ s [v3 ]_ s /\
    uivi_bounds u v u1 v1 u2 v2 u3 v3 s /\ ti_bounds u v t1 t2 t3 s }}.

Lemma begcd_verif_intermediate_with_bounds_invariant_stren :
  nodup(g, u, v, u1, u2, u3, v1, v2, v3, t1, t2, t3) -> 0 < vu -> 0 < vv ->
  while.entails hoare_m.store hoare_m.heap
  (fun s _ =>
    C2 vu vv u v g s /\
    C3 vu vv u v g s /\
    uivi_init u v u1 u2 u3 v1 v2 v3 s /\
    ti_init u v t1 t2 t3 s /\
    uivi_bounds u v u1 v1 u2 v2 u3 v3 s /\ ti_bounds u v t1 t2 t3 s)
  (fun s _ =>
    C2 vu vv u v g s /\
    (Zodd [u ]_ s \/ Zodd [v ]_ s) /\
    CVectors u v u1 u2 u3 v1 v2 v3 t1 t2 t3 s /\
    C4 u3 v3 t3 s /\
    C5 u3 v3 s /\
    Zgcd [u ]_ s [v ]_ s = Zgcd [u3 ]_ s [v3 ]_ s /\
    uivi_bounds u v u1 v1 u2 v2 u3 v3 s /\ ti_bounds u v t1 t2 t3 s).

Lemma begcd_verif_intermediate_with_bounds_invariant_weak :
  nodup(g, u, v, u1, u2, u3, v1, v2, v3, t1, t2, t3) -> 0 < vu -> 0 < vv ->
  while.entails hoare_m.store hoare_m.heap
  (fun s h => (C2 vu vv u v g s /\
    (Zodd [u ]_ s \/ Zodd [v ]_ s) /\
    CVectors u v u1 u2 u3 v1 v2 v3 t1 t2 t3 s /\
    C4 u3 v3 t3 s /\
    C5 u3 v3 s /\
    Zgcd [u ]_ s [v ]_ s = Zgcd [u3 ]_ s [v3 ]_ s /\
    uivi_bounds u v u1 v1 u2 v2 u3 v3 s /\ ti_bounds u v t1 t2 t3 s) /\
  ~~ hoare_m.eval_b (var_e t3 <>e nat_e 0) (s, h))
  (fun s _ =>
    vu * [u1 ]_ s + vv * [u2 ]_ s = [g ]_ s * [u3 ]_ s /\
    Zgcd vu vv = [g ]_ s * [u3 ]_ s /\
    uivi_bounds u v u1 v1 u2 v2 u3 v3 s /\ ti_bounds u v t1 t2 t3 s).

Lemma begcd_verif_intermediate_with_bounds :
  nodup(g, u, v, u1, u2, u3, v1, v2, v3, t1, t2, t3) -> 0 < vu -> 0 < vv ->
  ({{fun s _ => C2 vu vv u v g s /\ C3 vu vv u v g s /\
    uivi_init u v u1 u2 u3 v1 v2 v3 s /\
    ti_init u v t1 t2 t3 s /\
    uivi_bounds u v u1 v1 u2 v2 u3 v3 s /\
    ti_bounds u v t1 t2 t3 s }}
  while.while (var_e t3 <>e nat_e 0)
  (while.while (var_e t3 mode nat_e 2 =e nat_e 0) (TAOCP.halve u v t1 t2 t3);
    TAOCP.reset u v u1 u2 u3 v1 v2 v3 t1 t2 t3;
    TAOCP.subtract u v u1 u2 u3 v1 v2 v3 t1 t2 t3)
  {{fun s _ =>
    vu * [u1 ]_ s + vv * [u2 ]_ s = [g ]_ s * [u3 ]_ s /\
    Zgcd vu vv = [g ]_ s * [u3 ]_ s /\
    uivi_bounds u v u1 v1 u2 v2 u3 v3 s /\
    ti_bounds u v t1 t2 t3 s }})%seplog_hoare.

Lemma begcd_verif_with_bounds :
  nodup(g, u, v, u1, u2, u3, v1, v2, v3, t1, t2, t3) -> 0 < vu -> 0 < vv ->
  ({{ fun s h => uv_init vu vv u v s }}
  TAOCP.begcd g u v u1 u2 u3 v1 v2 v3 t1 t2 t3
  {{ fun s h =>
    vu * [u1]_s + vv * [u2]_s = [g]_s * [u3]_s /\
    Zgcd vu vv = ([g]_s * [u3]_s) /\
    uivi_bounds u v u1 v1 u2 v2 u3 v3 s /\
    ti_bounds u v t1 t2 t3 s
  }})%seplog_hoare.

End taocp2.

End TAOCP2.

End EGCD.