Library multi_halve_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 multi_halve_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.

Lemma multi_halve_u_triple k a i tmp prev next : 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_halve_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 /\ 2 * \S_{ nk } A' + u2Z ([prev]_s `>> 31) = \S_{ nk } A }}.
Proof.
move=> Hset nk va Hna A HA; rewrite /multi_halve_u.

addiu i k zero16;

apply hoare_addiu with (fun s h => [a]_s = va /\ u2Z [k]_s = Z_of_nat nk /\
  u2Z [i]_s = Z_of_nat nk /\ (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.
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 = Z_of_nat nk /\ [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 r0)

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 = Z2u 32 (2 ^^ 31)) /\
  (ni <= nk)%nat /\
  (var_e a |--> A') s h /\
  take ni A' = take ni A /\
  2 * \S_{ nk - ni } (drop ni A') + u2Z ([prev]_s `>> 31) = \S_{ nk - ni } (drop ni A)).

move=> s h [Hra [Hrk [Hri [Hrprev Hmem]]]].
exists A, nk; repeat (split; trivial).
by rewrite Hrprev; left.
rewrite (_ : (_ - _ = O)%nat) /=; last by rewrite subnn.
by rewrite Hrprev shrl_Z2u_0 // 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 = O.
  apply Z_of_nat_inj; by rewrite -Hri Hri' /= store.get_r0 Z2uK.
subst ni.
rewrite subn0 2!drop0 in Hinv2.
exists A'; by repeat (split; trivial).

addiu i i mone16;

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 = Z2u 32 (2 ^^ 31)) /\
  (ni < nk)%nat /\
  (var_e a |--> A') s h /\
  take ni.+1 A' = take ni.+1 A /\
  2 * \S_{ nk - ni.+1} (drop ni.+1 A') + u2Z ([prev ]_ s `>> 31) = \S_{ nk - ni.+1} (drop ni.+1 A)).

move=> s h [[A' [ni [HlenA' [Hra [Hrk [Hri [Hprev [Hik [Hmem [Hinv1 Hinv2]]]]]]]]]] Hri'].
rewrite /= store.get_r0 Z2uK // in Hri'.
move/eqP in Hri'.
exists A', (ni - 1)%nat.
repeat Reg_upd.
rewrite Hri (_ : 0 = Z_of_nat 0) // in Hri'; apply Z_of_nat_inj_neq, not_eq_sym in Hri'.
rewrite -subSn; last by rewrite lt0n; apply/eqP; auto.
rewrite subSS subn0.
repeat (split; trivial).
- rewrite sext_Z2s // u2Z_add_Z2s //; last first.
    rewrite Hri -(mul1Z (-1)) mulZN1 leZ_subRL addZ0 (_ : 1 = Z<=nat 1) //.
    exact/inj_le/neq_0_lt.
  rewrite inj_minus1 //; by [rewrite Hri | apply neq_0_lt].
- by Assert_upd.

lwxs tmp i a;

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

move=> s h [A' [ni [HlenA' [Hra [Hrk [Hri [Hprev [Hik [Hmem [Hinv1 Hinv2]]]]]]]]]].
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.

andi next tmp one16;

apply hoare_andi 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 = Z2u 32 (2 ^^ 31)) /\
  (ni < nk)%nat /\
  (var_e a |--> A') s h /\
  take ni.+1 A' = take ni.+1 A /\
  2 * \S_{nk - ni.+1} (drop ni.+1 A') + u2Z ([prev ]_ s `>> 31) = \S_{nk - ni.+1} (drop ni.+1 A) /\
  [tmp]_s = A' `32_ ni /\
  [next]_s = (A' `32_ ni) `& one32).

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 zext_Z2u.

srl tmp tmp one5;

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 = Z2u 32 (2 ^^ 31)) /\
  (ni < nk)%nat /\
  (var_e a |--> A') s h /\
  take ni.+1 A' = take ni.+1 A /\
  2 * \S_{nk - ni.+1} (drop ni.+1 A') + u2Z ([prev ]_ s `>> 31) = \S_{nk - ni.+1} (drop ni.+1 A) /\
  [tmp]_s = (A' `32_ ni) `>> 1 /\
  [next]_s = (A' `32_ ni) `& one32).

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 = Z2u 32 (2 ^^ 31)) /\
  (ni < nk)%nat /\
  (var_e a |--> A') s h /\
  take ni.+1 A' = take ni.+1 A /\
  2 * \S_{nk - ni.+1} (drop ni.+1 A') + u2Z ([prev ]_ s `>> 31) = \S_{nk - ni.+1} (drop ni.+1 A) /\
  [tmp]_s = ((A' `32_ ni) `>> 1) `|` [prev]_s /\
  [next]_s = (A' `32_ ni) `& one32).

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 /\
  take ni.+1 A' = take ni.+1 A /\
  2 ^^ 33 * \S_{nk - ni.+1} (drop ni.+1 A') + 2 * u2Z ([tmp]_s) + u2Z ([prev ]_ s) = \S_{ nk - ni } (drop ni A) /\
  [tmp]_s `% 31 = ((A' `32_ ni) `>> 1) `% 31 /\
  [next]_s = (A' `32_ ni) `& one32).

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.
  case: (Zeven_odd_dec (u2Z (A' `32_ ni))).
  + move/int_even_and_1; by left.
  + move/int_odd_and_1; by right.
- by Assert_upd.
- rewrite (@drop_nth _ zero32 ni) ; last by rewrite HA.
  rewrite (_ : nk - ni = (nk - ni.+1).+1)%nat; last by rewrite -subSn.
  rewrite lSum_S Hnext Htmp.
  have -> : 2 * u2Z ((A' `32_ ni `>> 1) `|` [prev ]_ s) =
    2 * u2Z (A' `32_ ni `>> 1) + 2 * u2Z ([prev ]_ s).
    case: Hprev => Hprev.
    - by rewrite Hprev Z2uK // mulZ0 addZ0 int_or_0 //.
    - rewrite int_orC (@or_concat 32 _ _ 31 Logic.eq_refl); last 2 first.
        by move: (shrl_lt (A' `32_ ni) 1).
        apply u2Z_inj.
        move: (@u2Z_rem'' _ ([prev ]_ s) 30 1).
        rewrite {1}Hprev Z2uK // => -> //; by rewrite Z2uK.
      rewrite /cast_subnK u2Z_cast u2Z_concat u2Z_shr_shrink' //.
      rewrite Hprev !Z2uK // rem_Zpower // !Z2uK //.
      ring_simplify.
      rewrite u2Z_rem' //.
      by move: (shrl_lt (A' `32_ ni) 1).
  have -> : u2Z (A `32_ ni) = 2 * u2Z (A' `32_ ni `>> 1) + u2Z (A' `32_ ni `& one32).
    rewrite -(@u2Z_shrl 32 _ 1); last by repeat constructor.
    rewrite [_ ^^ _]/=.
    ring_simplify.
    rewrite (take_nth zero32) in Hinv1; last by rewrite HlenA'.
    rewrite (take_nth zero32) in Hinv1; last by rewrite HA.
    move/eqP : Hinv1.
    rewrite eqseq_rcons => /andP [/eqP Hinv' /eqP Hinv''].
    by rewrite /nth' Hinv'' int_and_rem_1.
  rewrite -Hinv2.
  suff -> : 2 * u2Z ([prev ]_ s) = 2 ^^ 32 * u2Z ([prev ]_ s `>> 31).
    rewrite mulZDr ZpowerS sext_0 addi0; ring.
- case: Hprev => Hprev.
  + by rewrite Hprev Z2uK // mulZ0 shrl_Z2u_0 Z2uK.
  + by rewrite Hprev Z2uK // shrl_Zpower // ssrnat.subnn Z2uK.
- rewrite Htmp.
  case: Hprev => ->.
  + by rewrite int_or_0.
  + by rewrite rem_distr_or rem_Zpower // int_or_0.

sll prev prev thirtyone5;

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 = Z2u 32 (2 ^^ 31)) /\
  (ni < nk)%nat /\
  (var_e a |--> A') s h /\
  take ni.+1 A' = take ni.+1 A /\
  2 ^^ 33 * \S_{nk - ni.+1} (drop ni.+1 A') + 2 * u2Z ([tmp]_s) + u2Z ([prev]_ s `>> 31) =
  \S_{ nk - ni} (drop ni A) /\
  [tmp]_s `% 31 = ((A' `32_ ni) `>> 1) `% 31 /\
  [next]_s = (A' `32_ ni) `& one32).

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).
- case : Hprev => ->.
  + rewrite shl_zero; by left.
  + rewrite Z2uK // shl_1 //; by right.
- by Assert_upd.
- case : Hprev => Hprev.
  + by rewrite Hprev shl_zero shrl_Z2u_0 Z2uK // -Hinv2 Hprev Z2uK.
  + rewrite Z2uK // (_ : '|31| = 31%nat) //.
    by rewrite Hprev (@shl_1 32 31 erefl) shrl_Zpower // -Hinv2 ssrnat.subnn [2 ^^ 0]/= Hprev.

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 = Z2u 32 (2^^31)) /\
  (ni < nk)%nat /\
  (var_e a |--> A') s h /\
  take ni.+1 A' = take ni.+1 A /\
  2 ^^ 33 * \S_{nk - ni.+1} (drop ni.+1 A') + 2 * u2Z ([tmp]_s) + u2Z ([prev]_ s `>> 31) =
  \S_{ nk - ni } (drop ni A) /\
  [tmp]_s `% 31 = (A' `32_ ni `>> 1) `% 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) //.
- by rewrite Hri [_ ^^ _]/= mulZC.
- 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 = Z2u 32 (2 ^^ 31)) /\
  (ni < nk)%nat /\
  (var_e a |--> A') s h /\
  take ni.+1 A' = take ni.+1 A /\
  2 ^^ 33 * \S_{nk - ni.+1} (drop ni.+1 A') + 2 * u2Z ([tmp]_s) + u2Z ([prev]_ s `>> 31) =
  \S_{ nk - ni } (drop ni A) /\
  [tmp]_s `% 31 = (A' `32_ ni `>> 1) `% 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 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'.

move=> s h [A' [ni [HlenA' [Hra [Hrk [Hri [Hprev [Hik [Hmem [Hinv1 [Hinv2 [Hrtmp 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).
- by apply size_upd_nth.
- by apply ltnW.
- rewrite HA' upd_nth_cat HlenA1 // (_ : (ni - ni = 0)%nat); last by rewrite subnn.
  rewrite /= (decompose_equiv _ _ _ _ _ HlenA1).
  rewrite cat0s in H'.
  assoc_comm H'.
  move: H'; by apply mapsto_ext.
- rewrite HA' upd_nth_cat; last by rewrite HlenA1.
  rewrite take_size_cat //.
  rewrite (take_nth zero32) in Hinv1; last by rewrite HlenA'.
  rewrite (take_nth zero32) in Hinv1; last by rewrite HA.
  move/eqP : Hinv1.
  rewrite eqseq_rcons => /andP [/eqP <- _].
  by rewrite HA' take_size_cat.
- rewrite -Hinv2.
  f_equal.
  rewrite HA' upd_nth_cat; last by rewrite HlenA1.
  rewrite drop_size_cat // HlenA1 subnn [upd_nth _ _ _]/=.
  have -> : (nk - ni = (nk - ni.+1).+1)%nat by rewrite -subSn.
  rewrite lSum_S -cat1s catA drop_size_cat; last by rewrite size_cat /= HlenA1 addnC.
  by ring_simplify.
Qed.