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

Lemma copy_u_u_triple rk rx ry tmp ytmp i : uniq(rk, ry, rx, tmp, ytmp, i, r0) ->
  forall X Y nk, size X = nk -> size Y = nk ->
    forall ny, u2Z ny + 4 * Z_of_nat nk < Zbeta 1 ->
  {{ fun s h =>
    [ry]_s = ny /\
    u2Z [rk]_s = Z_of_nat nk /\
    (var_e rx |--> X ** var_e ry |--> Y) s h }}
  copy_u_u rk ry rx tmp ytmp i
  {{ fun s h =>
    [ry]_s = ny /\
    u2Z [rk]_s = Z_of_nat nk /\
    (var_e rx |--> X ** var_e ry |--> X) s h }}.
Proof.
move=> Hset X Y nk len_X len_Y ny Hfit; rewrite /copy_u_u.

addiu ytmp ry zero16 ;

apply mips_contrib.hoare_addiu with (fun s h =>
  [ry]_s = ny /\
  u2Z [rk ]_ s = Z_of_nat nk /\
  [ytmp]_s = [ry]_s /\
  (var_e rx |--> X ** var_e ry |--> Y) s h).

move=> s h [r_y [r_k mem]].
rewrite /mips_seplog.wp_addiu.
repeat Reg_upd; repeat (split; trivial).
by rewrite sext_Z2u // addi0.
by Assert_upd.

addiu i r0 zero16 ;

apply mips_contrib.hoare_addiu with (fun s h =>
  [ry]_s = ny /\
  u2Z ([rk ]_ s) = Z_of_nat nk /\
  [ytmp]_s = [ry]_s /\
  [i]_s = zero32 /\
  (var_e rx |--> X ** var_e ry |--> Y) s h).

move=> s h [r_y [r_k [r_ytmp mem]]].
rewrite /mips_seplog.wp_addiu.
repeat Reg_upd; repeat (split => //).
by rewrite sext_Z2u // addi0.
by Assert_upd.

While bne i rk r_k. - exact/min_u2Z. rewrite {1HX' in mem. rewrite (mapstos.decompose_equiv _ HlenX1) in mem. rewrite !assert_m.conAE assert_m.conCE !assert_m.conAE in mem. move: mem; apply assert_m.monotony => h' //. apply assert_m.mapsto_ext => //. rewrite /=. f_equal. apply u2Z_inj. have Htmp : u2Z (i s) < 2 ^^ 30. apply (@leZ_ltZ_trans (Z_of_nat nk)) => //. apply (@ltZ_pmul2l 4) => //. rewrite (_ : 4 = 2 ^^ 2) // -ZpowerD //. rewrite -Zbeta1E _ ^^ _/=. move: (min_u2Z (ry_s)); rewrite r_y => ?; lia. rewrite Z2uK; last first. split; first exact: Zle_0_nat. rewrite inj_mult. simpl Z_of_nat. rewrite {2}(_ : 32 = 2 + 30)

apply mips_contrib.hoare_sw_back'' with (fun s h =>
  [ry]_s = ny /\
  u2Z [rk ]_ s = Z_of_nat nk /\
  u2Z [ytmp]_s = u2Z [ry]_s + u2Z [i]_s * 4 /\
  u2Z [i]_s < Z_of_nat nk /\
  (var_e rx |--> X **
    var_e ry |--> take '|u2Z [i ]_ s + 1| X ++
      drop '|u2Z [i ]_ s + 1| Y) s h).

move=> s h [r_y [r_k [r_ytmp [r_i [r_tmp mem]]]]].
set ni := '|u2Z ([i]_s)| in mem.
exists (int_e ((drop ni Y) `32_ 0)).

Decompose_32 (take ni X ++ drop ni Y) '|u2Z ([i ]_ s)| XY1 XY2 HlenXY1 HXY'; last first.
  rewrite size_cat size_takel //; last first.
    apply/leP/Nat2Z.inj_le.
    rewrite Z_of_nat_Zabs_nat; [lia | exact/min_u2Z].
  rewrite size_drop len_Y addnBA; last first.
    apply/leP/Nat2Z.inj_le.
    rewrite Z_of_nat_Zabs_nat; [lia | exact/min_u2Z].
  rewrite addnC addnK.
  apply/ltP/Nat2Z.inj_lt.
  rewrite Z_of_nat_Zabs_nat //; exact/min_u2Z.

have Htmp : [var_e ry \+ int_e (Z2u 32 (Z_of_nat (4 * ni))) ]e_ s =
   [var_e ytmp \+ int_e (Z2u (16 + 16) 0) ]e_ s.
  rewrite /=.
  apply u2Z_inj.
  rewrite addi0 r_ytmp u2Z_add_Z2u //; last 2 first.
    exact: Zle_0_nat.
    rewrite inj_mult [Z_of_nat _]/= Z_of_nat_Zabs_nat //; last exact/min_u2Z.
    rewrite r_y -Zbeta1E; lia.
  rewrite inj_mult [Z_of_nat _]/= Z_of_nat_Zabs_nat //; [ring | exact/min_u2Z].

rewrite HXY' /= in mem.
rewrite (mapstos.decompose_equiv _ _ _ _ _ HlenXY1) in mem.
do 2 rewrite assert_m.conCE !assert_m.conAE in mem.
move: mem.
apply assert_m.monotony => // h'.
apply assert_m.mapsto_ext => //.
by rewrite sext_Z2u //.

rewrite nth_cat size_takel; last first.
  apply/leP/Nat2Z.inj_le.
  rewrite /ni Z_of_nat_Zabs_nat; by [lia | apply min_u2Z].
by rewrite ltnn subnn.

apply assert_m.currying => h'' Hh''.
repeat (split => //).
rewrite !assert_m.conAE assert_m.conCE !assert_m.conAE in Hh''.
move: Hh''; apply assert_m.monotony => h''' //.
have -> : '|u2Z ([i ]_ s) + 1| = S ni.
  rewrite Zabs_nat_Zplus //.
  by rewrite plus_comm.
  exact/min_u2Z.
rewrite (take_nth zero32); last first.
  rewrite len_X.
  apply/ltP/Nat2Z.inj_lt.
  rewrite /ni Z_of_nat_Zabs_nat //; exact/min_u2Z.
rewrite -cats1.
rewrite -catA (mapstos.decompose_equiv _ ni); last first.
  rewrite size_takel //.
  apply/leP/Nat2Z.inj_le.
  rewrite /ni Z_of_nat_Zabs_nat; [lia | exact: min_u2Z].
move/eqP : HXY'.
rewrite eqseq_cat; last first.
  rewrite size_takel // len_X.
  apply/leP/Nat2Z.inj_le.
  rewrite /ni Z_of_nat_Zabs_nat; [lia | exact: min_u2Z].
case/andP => /eqP H1 /eqP H2.
apply assert_m.monotony => h4 //.
  by rewrite -H1.
apply assert_m.monotony => h5r //.
  apply assert_m.mapsto_ext => //.
  by rewrite /= sext_Z2u // addi0.

suff : XY2 = drop (S ni) Y by move=> ->.
by rewrite dropS H2 /= drop0.

addiu i i one16 ;

apply mips_contrib.hoare_addiu with (fun s h =>
  [ry]_s = ny /\
  u2Z [rk ]_ s = Z_of_nat nk /\
  u2Z [ytmp]_s = u2Z [ry]_s + (u2Z [i]_s - 1) * 4 /\
  u2Z [i]_s <= Z_of_nat nk /\
  (var_e rx |--> X **
    var_e ry |--> take '|u2Z [i ]_ s| X ++ drop '|u2Z [i ]_ s| Y) s h).

move=> s h [r_y [r_k [r_ytmp [r_i mem]]]].
rewrite /mips_seplog.wp_addiu.
repeat Reg_upd; repeat (split; trivial).
rewrite r_ytmp.
f_equal.
rewrite sext_Z2u // u2Z_add_Z2u //.
ring.
rewrite -Zbeta1E.
  move: (min_u2Z ([ry]_s)); rewrite r_y => ?; lia.
rewrite sext_Z2u // u2Z_add_Z2u //.
lia.
rewrite -Zbeta1E.
  move: (min_u2Z ([ry]_s)); rewrite r_y => ?; lia.
rewrite sext_Z2u // u2Z_add_Z2u //; last first.
rewrite -Zbeta1E.
  move: (min_u2Z ([ry]_s)); rewrite r_y => ?; lia.
by Assert_upd.

addiu ytmp ytmp four16

apply mips_contrib.hoare_addiu'.

move=> s h [r_y [r_k [r_ytmp [i_k mem]]]].
rewrite /mips_seplog.wp_addiu.
repeat Reg_upd; repeat (split; trivial).
rewrite sext_Z2u // u2Z_add_Z2u //; last first.
  rewrite r_ytmp r_y -Zbeta1E -{2}(mul1Z 4) -addZA -mulZDl mulZC; lia.
rewrite r_ytmp; ring.
by Assert_upd.
Qed.