Library multi_double_u_triple

From mathcomp Require Import ssreflect ssrfun ssrbool eqtype ssrnat seq.
Require Import ssrZ ZArith_ext seq_ext ssrnat_ext uniq_tac machine_int.
Require Import multi_int.
Import MachineInt.
Require Import mips_seplog mips_tactics mips_contrib mapstos.
Require Import multi_double_u_prg.
Import expr_m.
Import assert_m.

Local Open Scope machine_int_scope.
Local Open Scope heap_scope.
Local Open Scope zarith_ext_scope.
Local Open Scope uniq_scope.
Local Open Scope mips_expr_scope.
Local Open Scope mips_assert_scope.
Local Open Scope mips_cmd_scope.
Local Open Scope mips_hoare_scope.
Local Open Scope multi_int_scope.

Section multi_double_u.

Variables k a i tmp prev next : reg.

Lemma multi_double_u_triple : uniq(k, a, i, tmp, prev, next, r0) ->
forall nk va, u2Z va + 4 * Z_of_nat nk < \B^1 ->
forall A, size A = nk ->
  {{ fun s h => [a]_s = va /\ u2Z [k]_s = Z_of_nat nk /\ (var_e a |--> A) s h }}
  multi_double_u k a i tmp prev next
  {{ fun s h => exists A', size A' = nk /\ [a]_s = va /\ u2Z [k]_s = Z_of_nat nk /\
     (var_e a |--> A') s h /\ \S_{ nk } A' + u2Z [prev]_s * 2 ^^ (nk * 32) = 2 * \S_{ nk } A }}.
Proof.
move=> Hset nk va Hna A HA; rewrite /multi_double_u.

addiu i r0 zero16;

apply hoare_addiu with (fun s h => [a]_s = va /\ u2Z [k]_s = Z_of_nat nk /\
  u2Z [i]_s = 0 /\ (var_e a |--> A) s h).

move=> s h [Hra [Hri Hmem]]; rewrite /wp_addiu.
repeat Reg_upd; repeat (split; trivial).
by rewrite sext_0 addi0 Z2uK.
by Assert_upd.

addiu prev r0 zero16;

apply hoare_addiu with (fun s h => [a]_s = va /\ u2Z [k]_s = Z_of_nat nk /\
  u2Z [i]_s = 0 /\ [prev]_s = zero32 /\ (var_e a |--> A) s h).

move=> s h [Hra [Hri [Hrk Hmem]]]; rewrite /wp_addiu.
repeat Reg_upd; repeat (split; trivial).
by rewrite sext_0 addi0.
by Assert_upd.

while (bne i k)

apply hoare_prop_m.hoare_while_invariant with (fun s h => exists A' ni, size A' = nk /\
  [a ]_ s = va /\ u2Z [k]_s = Z_of_nat nk /\
  u2Z [i ]_ s = Z_of_nat ni /\
  ([prev]_s = zero32 \/ [prev]_s = one32) /\
  (ni <= nk)%nat /\
  (var_e a |--> A') s h /\
  drop ni A' = drop ni A /\
  \S_{ ni } (take ni A') + u2Z [prev]_s * 2 ^^ (ni * 32) = 2 * \S_{ ni } (take ni A)).

move=> s h [Hra [Hrk [Hri [Hrprev Hmem]]]].
exists A, O; repeat (split; trivial).
by rewrite Hrprev; left.
by rewrite /= Hrprev Z2uK.

move=> s h [[A' [ni [HlenA' [Hra [Hrk [Hri [Hprev [Hik [Hmem [Hinv1 Hinv2]]]]]]]]]] Hri'].
rewrite /= in Hri'. move/negPn/eqP in Hri'.
have {Hri'} ? : ni = nk by rewrite /= Hrk Hri in Hri'; apply Z_of_nat_inj.
subst ni.
rewrite take_oversize // in Hinv2; last by rewrite HlenA'.
rewrite take_oversize // in Hinv2; last by rewrite HA.
by exists A'.

lwxs tmp i a;

apply hoare_lwxs_back_alt'' with (fun s h => exists A' ni, size A' = nk /\
  [a ]_ s = va /\ u2Z [k]_s = Z_of_nat nk /\
  u2Z [i ]_ s = Z_of_nat ni /\
  ([prev]_s = zero32 \/ [prev]_s = one32) /\
  (ni < nk)%nat /\
  (var_e a |--> A') s h /\
  drop ni A' = drop ni A /\
  \S_{ ni } (take ni A') + u2Z [prev ]_ s * 2 ^^ (ni * 32) = 2 * \S_{ ni } (take ni A) /\
  [tmp]_s = A' `32_ ni).

move=> s h [[A' [ni [HlenA' [Hra [Hrk [Hri [Hprev [Hik [Hmem [Hinv1 Hinv2]]]]]]]]]] Hik'].
rewrite /= in Hik'; move/eqP in Hik'.
have {}Hik' : (ni < nk)%nat.
  rewrite ltnNge; apply/negP; contradict Hik'.
  by rewrite Hrk Hri; f_equal; apply/eqP; rewrite eqn_leq Hik.
exists (A' `32_ ni); split.
Decompose_32 A' ni A1 A2 HlenA1 HA'; last by rewrite HlenA'.
rewrite HA' (decompose_equiv _ _ _ _ _ HlenA1) in Hmem.
rewrite conCE !conAE in Hmem.
move: Hmem; apply monotony => // h'.
apply mapsto_ext => //.
by rewrite /= shl_Z2u inj_mult Hri [Zmult]lock /= -lock mulZC.

rewrite /update_store_lwxs.
exists A', ni; repeat Reg_upd; repeat (split; trivial).
by Assert_upd.

srl next tmp thirtyone5;

apply hoare_srl with (fun s h => exists A' ni, size A' = nk /\
  [a ]_ s = va /\ u2Z [k]_s = Z_of_nat nk /\
  u2Z [i ]_ s = Z_of_nat ni /\
  ([prev]_s = zero32 \/ [prev]_s = one32) /\
  (ni < nk)%nat /\
  (var_e a |--> A') s h /\
  drop ni A' = drop ni A /\
  \S_{ ni } (take ni A') + u2Z ([prev ]_ s) * 2 ^^ (ni * 32) = 2 * \S_{ ni } (take ni A) /\
  [tmp]_s = A' `32_ ni /\
  [next]_s = (A' `32_ ni) `>> 31).

move=> s h [A' [ni [HlenA' [Hra [Hrk [Hri [Hprev [Hik [Hmem [Hinv1 [Hinv2 Htmp]]]]]]]]]]].
exists A', ni; repeat Reg_upd; repeat (split; trivial).
by Assert_upd.
by rewrite Htmp Z2uK.

sll tmp tmp one5;

apply hoare_sll with (fun s h => exists A' ni, size A' = nk /\
  [a ]_ s = va /\ u2Z [k]_s = Z_of_nat nk /\
  u2Z [i ]_ s = Z_of_nat ni /\
  ([prev]_s = zero32 \/ [prev]_s = one32) /\
  (ni < nk)%nat /\
  (var_e a |--> A') s h /\
  drop ni A' = drop ni A /\
  \S_{ ni } (take ni A') + u2Z ([prev ]_ s) * 2 ^^ (ni * 32) = 2 * \S_{ ni } (take ni A) /\
  [tmp]_s = (A' `32_ ni) `<< 1 /\
  [next]_s = (A' `32_ ni) `>> 31).

move=> s h [A' [ni [HlenA' [Hra [Hrk [Hri [Hprev [Hik [Hmem [Hinv1 [Hinv2 [Htmp Hnext]]]]]]]]]]]].
exists A', ni; repeat Reg_upd; repeat (split; trivial).
by Assert_upd.
by rewrite Htmp Z2uK.

cmd_or tmp tmp prev;

apply hoare_or with (fun s h => exists A' ni, size A' = nk /\
  [a ]_ s = va /\ u2Z [k]_s = Z_of_nat nk /\
  u2Z [i ]_ s = Z_of_nat ni /\
  ([prev]_s = zero32 \/ [prev]_s = one32) /\
  (ni < nk)%nat /\
  (var_e a |--> A') s h /\
  drop ni A' = drop ni A /\
  \S_{ ni } (take ni A') + u2Z ([prev ]_ s) * 2 ^^ (ni * 32) = 2 * \S_{ ni } (take ni A) /\
  [tmp]_s = ((A' `32_ ni) `<< 1) `|` [prev]_s /\
  [next]_s = (A' `32_ ni) `>> 31).

move=> s h [A' [ni [HlenA' [Hra [Hrk [Hri [Hprev [Hik [Hmem [Hinv1 [Hinv2 [Htmp Hnext]]]]]]]]]]]].
exists A', ni; repeat Reg_upd; repeat (split; trivial).
by Assert_upd.
by rewrite Htmp.

addiu prev next zero16;

apply hoare_addiu with (fun s h => exists A' ni, size A' = nk /\
  [a ]_ s = va /\ u2Z [k]_s = Z_of_nat nk /\
  u2Z [i ]_ s = Z_of_nat ni /\
  ([prev]_s = zero32 \/ [prev]_s = one32) /\
  (ni < nk)%nat /\
  (var_e a |--> A') s h /\
  drop ni A' = drop ni A /\
  \S_{ ni } (take ni A') + u2Z ([tmp]_s `% 1) * 2 ^^ (ni * 32) = 2 * \S_{ ni } (take ni A) /\
  [tmp]_s `>> 1 = (A' `32_ ni `<< 1) `>> 1 /\
  [prev]_s = A' `32_ ni `>> 31).

move=> s h [A' [ni [HlenA' [Hra [Hrk [Hri [Hprev [Hik [Hmem [Hinv1 [Hinv2 [Htmp Hnext]]]]]]]]]]]].
rewrite /wp_addiu; exists A', ni.
repeat Reg_upd; repeat (split; trivial).
- rewrite sext_0 addi0 Hnext.
  move: (shrl_lt (A' `32_ ni) 31) => /=.
  move X : (u2Z _) => x Hx.
  have [Y|Y] : x = 0 \/ x = 1.
    move: (min_u2Z (A' `32_ ni `>> 31)) => Hy; lia.
  subst x.
  rewrite (_ : 0 = u2Z (Z2u 32 0)) in Y; last by rewrite Z2uK.
  apply u2Z_inj in Y.
  rewrite Y; by auto.
  subst x.
  rewrite (_ : 1 = u2Z (Z2u 32 1)) in Y; last by rewrite Z2uK.
  move/u2Z_inj : Y => ->; by right.
- by Assert_upd.
- rewrite Htmp.
  have X : u2Z (((A' `32_ ni `<< 1) `|` [prev ]_ s) `% 1) = u2Z ([prev ]_ s).
    rewrite rem_distr_or shl_rem_m; last by repeat constructor.
    rewrite int_orC int_or_0 u2Z_rem' //.
    case: Hprev => ->; by rewrite Z2uK.
  by rewrite X.
- rewrite Htmp shrl_distr_or (@shrl_overflow _ ([prev]_s) 1).
  by rewrite int_or_0.
  by case: Hprev => ->; rewrite Z2uK.
- by rewrite sext_Z2u // addi0.

sll next i two5;

apply hoare_sll with (fun s h => exists A' ni, size A' = nk /\
  [a ]_ s = va /\ u2Z [k]_s = Z_of_nat nk /\
  u2Z [i ]_ s = Z_of_nat ni /\
  ([prev]_s = zero32 \/ [prev]_s = one32) /\
  (ni < nk)%nat /\
  (var_e a |--> A') s h /\
  drop ni A' = drop ni A /\
  \S_{ ni } (take ni A') + u2Z ([tmp]_s `% 1) * 2 ^^ (ni * 32) = 2 * \S_{ ni } (take ni A) /\
  [tmp]_s `>> 1 = ((A' `32_ ni) `<< 1) `>> 1 /\
  [prev]_s = (A' `32_ ni) `>> 31 /\
  u2Z [next]_s = 4 * Z_of_nat ni).

move=> s h [A' [ni [HlenA' [Hra [Hrk [Hri [Hprev [Hik [Hmem [Hinv1 [Hinv2 [Htmp Hnext]]]]]]]]]]]].
exists A', ni; repeat Reg_upd; repeat (split; trivial).
by Assert_upd.
rewrite Z2uK // (_ : '|2| = 2%nat) // (@u2Z_shl 2 _ _ 30) //.
- rewrite Hri [_ ^^ _]/=; ring.
- rewrite (_ : \B^1 = 4 * 2 ^^ 30) in Hna; last by rewrite Zbeta1E.
  move: (min_u2Z va) => ?; ssromega.

addu next a next;

apply hoare_addu with (fun s h => exists A' ni, size A' = nk /\
  [a ]_ s = va /\ u2Z [k]_s = Z_of_nat nk /\
  u2Z [i ]_ s = Z_of_nat ni /\
  ([prev]_s = zero32 \/ [prev]_s = one32) /\
  (ni < nk)%nat /\
  (var_e a |--> A') s h /\
  drop ni A' = drop ni A /\
  \S_{ ni } (take ni A') + u2Z ([tmp]_s `% 1) * 2 ^^ (ni * 32) = 2 * \S_{ ni } (take ni A) /\
  [tmp]_s `>> 1 = ((A' `32_ ni) `<< 1) `>> 1 /\
  [prev]_s = A' `32_ ni `>> 31 /\
  u2Z [next]_s = u2Z va + 4 * Z_of_nat ni).

move=> s h [A' [ni [HlenA' [Hra [Hrk [Hri [Hprev [Hik [Hmem [Hinv1 [Hinv2 [Htmp [Hprev2 Hnext]]]]]]]]]]]]].
exists A', ni; repeat Reg_upd; repeat (split; trivial).
- by Assert_upd.
- rewrite u2Z_add.
  + rewrite Hra Hnext; ring.
  + rewrite (_ : \B^1 = 4 * 2 ^^ 30) in Hna; last by rewrite Zbeta1E.
    rewrite (_ : 2 ^^ 32 = 4 * 2 ^^ 30) // Hnext Hra; ssromega.

sw tmp zero16 next

apply hoare_sw_back'' with (fun s h => exists A' ni, size A' = nk /\
  [a ]_ s = va /\ u2Z [k]_s = Z_of_nat nk /\
  u2Z [i ]_ s = Z_of_nat ni /\
  ([prev]_s = zero32 \/ [prev]_s = one32) /\
  (ni < nk)%nat /\
  (var_e a |--> A') s h /\
  drop ni.+1 A' = drop ni.+1 A /\
  \S_{ni.+1} (take ni.+1 A') + u2Z ([prev]_s) * 2 ^^ (ni.+1 * 32) = 2 * \S_{ni.+1} (take ni.+1 A) /\
  u2Z [next]_s = u2Z va + 4 * Z_of_nat ni).

move=> s h [A' [ni [HlenA' [Hra [Hrk [Hri [Hprev [Hik [Hmem [Hinv1 [Hinv2 [Hrtmp [Hprev2 Hrnext]]]]]]]]]]]]].

have Htmp : [ var_e next \+ int_e (sext 16 zero16) ]e_ s = [ var_e a \+ int_e (Z2u 32 (Z_of_nat (4 * ni))) ]e_ s.
  rewrite sext_0 /= addi0.
  apply u2Z_inj.
  rewrite u2Z_add_Z_of_nat.
  rewrite inj_mult Hra Hrnext //; ring.
  rewrite inj_mult -Zbeta1E Hra; simpl Z_of_nat; ssromega.

Decompose_32 A' ni A'1 A'2 HlenA1 HA'; last by rewrite HlenA'.
rewrite HA' (decompose_equiv _ _ _ _ _ HlenA1) in Hmem.
rewrite conCE !conAE in Hmem.
exists (int_e (A' `32_ ni)).
move: Hmem; apply monotony => ht.
exact: mapsto_ext.
apply currying => h' H'; simpl app in H'.
exists (upd_nth A' ni [tmp]_s), ni.
repeat (split; trivial).

- exact: size_upd_nth.
- rewrite HA' upd_nth_cat HlenA1 // subnn /= (decompose_equiv _ _ _ _ _ HlenA1).
  rewrite cat0s in H'.
  assoc_comm H'.
  by move: H'; apply mapsto_ext.
- by rewrite drop_upd_nth // dropS [in RHS]dropS Hinv1.
- rewrite (take_nth zero32); last by rewrite (@size_upd_nth _ nk).
  rewrite -cats1 lSum_cut_last; last first.
    by rewrite size_cat addn1 size_take (@size_upd_nth _ nk) // Hik.
  rewrite subn1 [_.+1.-1]/= nth_upd_nth; last by rewrite HlenA'.
  rewrite take_upd_nth (take_nth zero32); last by rewrite HA.
  rewrite -cats1 (lSum_cut_last _ (take ni A)) //; last first.
    by rewrite size_cat /= addnC /= size_take HA Hik add1n.
  rewrite subn1 [_.+1.-1]/= mulZDr -Hinv2 -!addZA.
  congr (_ + _).
  have X : u2Z ([tmp ]_ s) = u2Z (([tmp]_s `% 1)) + u2Z (([tmp]_s `>> 1) `<< 1).
    rewrite {1}(@or_sh_rem 32 ([tmp]_s) 1 (refl_equal _)).
    have X1 : u2Z (@cast_subnK 1 32 Logic.eq_refl (zext 31 ([tmp ]_ s `% 1))) < 2 ^^ 1.
      rewrite u2Z_cast u2Z_zext -int_and_rem_1.
      case: (Zeven_odd_dec (u2Z ([tmp ]_ s))) => X.
      + apply int_even_and_1 in X; by rewrite X Z2uK.
      + apply int_odd_and_1 in X; by rewrite X Z2uK.
    have X2 : (([tmp ]_ s `>> 1) `<< 1) `% 1 = Z2u 1 0 by rewrite shl_rem_m.
    rewrite (@or_concat 32 _ _ 1 Logic.eq_refl X1 X2) /cast_subnK u2Z_cast u2Z_concat addZC.
    f_equal.
    by rewrite u2Z_rem' // u2Z_cast u2Z_zext.
    by rewrite u2Z_shr_shrink' // shl_rem_m // Z2uK // subZ0.
  rewrite X mulZDr -!addZA.
  f_equal; first exact: mulZC.
  rewrite Hrtmp Hprev2.
  have -> : ((A' `32_ ni `<< 1) `>> 1) `<< 1 = A' `32_ ni `<< 1 by rewrite shrl_shl // shl_rem_m.
  have <- : A `32_ ni = A' `32_ ni.
    rewrite (drop_nth zero32) in Hinv1; last by rewrite HlenA'.
    symmetry in Hinv1.
    rewrite (drop_nth zero32) in Hinv1; last by rewrite HA.
    by case: Hinv1.
  have U : 2 * u2Z (A `32_ ni) = u2Z (A `32_ ni `<< 1) + 2 ^^ 32 * u2Z (A `32_ ni `>> 31).
    case: (Z_lt_le_dec (u2Z (A `32_ ni)) (2 ^^ 31)) => V.
    * by rewrite (@u2Z_shl 1 _ _ 31) // shrl_overflow // Z2uK // mulZ0 addZ0 mulZC.
    * rewrite -(@u2Z_shrl _ (A `32_ ni) 31) // mulZDr addZC; congr (_ + _).
      - by rewrite u2Z_shl_rem.
      - by rewrite mulZC -mulZA mulZC -mulZA.
  symmetry.
  rewrite mulZC -mulZA -(mulZC 2) U mulZDr mulSn addnC ZpowerD; ring.

apply hoare_addiu'.
move=> s m [A' [ni [HlenA' [Hra [Hrk [Hri [Hprev [Hik [Hmem [Hinv1 [Hinv2 Hnext]]]]]]]]]]].
rewrite /wp_addiu.
exists A', ni.+1.
rewrite sext_Z2u //.
repeat Reg_upd; repeat (split; trivial).
- rewrite u2Z_add_Z2u //.
  + by rewrite Z_S Hri.
  + rewrite Hri -Zbeta1E.
    move: (min_u2Z va) => ?; ssromega.
- by Assert_upd.
Qed.

End multi_double_u.