Library multi_halve_u_simu

From mathcomp Require Import ssreflect ssrfun ssrbool eqtype ssrnat seq.
Require Import ssrZ ZArith_ext seq_ext uniq_tac machine_int multi_int.
Require Import encode_decode integral_type.
Import MachineInt.
Require Import mips_bipl mips_cmd mips_tactics mips_syntax mips_mint.
Import mips_bipl.expr_m.
Require Import simu.
Import simu.simu_m.
Require Import multi_halve_u_prg multi_halve_u_triple.

Local Open Scope heap_scope.
Local Open Scope assoc_scope.
Local Open Scope uniq_scope.
Local Open Scope machine_int_scope.
Local Open Scope asm_expr_scope.
Local Open Scope simu_scope.
Local Open Scope zarith_ext_scope.

x <- x / 2, x unsigned

Lemma pfwd_sim_halve_u P (x : bipl.var.v) d (rk rx a0 a1 a2 a3 : reg) :
  uniq(rk, rx, a0, a1, a2, a3, r0) ->
  disj (mints_regs (assoc.cdom d)) (a0 :: a1 :: a2 :: a3 :: nil) ->
  x \notin assoc.dom d ->
  unsign rk rx \notin assoc.cdom d ->
  (x <- var_e x ./e nat_e 2)%pseudo_expr%pseudo_cmd
    <=p( state_mint (x |=> unsign rk rx \U+ d), P )
  multi_halve_u rk rx a0 a1 a2 a3.
Proof.
move=> Haux Hd Hd' Hd''.
rewrite /pfwd_sim.
move=> s st h [s_st_h _] s' exec_pseudo st' h' exec_mips.
have Hd_unchanged : forall v r, assoc.get v d = Some r ->
  disj (mint_regs r) (mips_frame.modified_regs (multi_halve_u rk rx a0 a1 a2 a3)).
  move=> v r Hvr; rewrite [mips_frame.modified_regs _]/=; Disj_remove_dup.
  apply (disj_incl_LR Hd); last by apply incl_refl_Permutation; PermutProve.
  exact/incP/inc_mint_regs/(assoc.get_Some_in_cdom _ v).
set nk := '|u2Z ([rk ]_ st)|.

move/multi_halve_u_triple : (Haux).
move/(_ nk [rx ]_ st (state_mint_head_unsign_fit _ _ _ _ _ _ _ s_st_h) (Z2ints 32 nk ([ x ]_ s)%pseudo_expr)).
rewrite size_Z2ints.
move/(_ Logic.eq_refl) => Hhoare_multi_halve_u.

have [st'' [h'' exec_mips_proj]] : exists st'' h'',
  (Some (st, heap.proj h (heap.dom (heap_mint (unsign rk rx) st h))) --
    multi_halve_u rk rx a0 a1 a2 a3 ---> Some (st'', h''))%mips_cmd.
  exists st', (heap.proj h' (heap.dom (heap_mint (unsign rk rx) st h))).
  apply (mips_syntax.triple_exec_proj _ _ _ Hhoare_multi_halve_u) => {Hhoare_multi_halve_u} //.
  split; first by reflexivity.
  split; first by rewrite Z_of_nat_Zabs_nat //; apply min_u2Z.
  rewrite /heap_cut heap.proj_dom_proj.
  apply (state_mint_var_mint _ _ _ _ x (unsign rk rx)) in s_st_h; last by assoc_get_Some.
  rewrite /var_mint in s_st_h; case: s_st_h; tauto.
set postcond := (fun s h => exists _, _) in Hhoare_multi_halve_u.
have {Hhoare_multi_halve_u} hoare_triple_post_condition : (
  postcond ** assert_m.TT)%asm_assert st' h'.
  move: (mips_frame.frame_rule_R _ _ _ Hhoare_multi_halve_u assert_m.TT (assert_m.inde_TT _) (mips_frame.inde_cmd_mult_TT _)).
  move/mips_seplog.hoare_prop_m.soundness.
  rewrite /while.hoare_semantics.
  move/(_ st h) => Hhalve_u.
  lapply Hhalve_u; last first.
    exists (heap_mint (unsign rk rx) st h), (h \D\ heap.dom (heap_mint (unsign rk rx) st h)).
    split; first by apply heap.disj_difs', inc_refl.
    split.
      apply heap.union_difsK => //; by apply heap.inclu_proj.
    split; last by reflexivity.
    split; first by reflexivity.
    split; first by rewrite Z_of_nat_Zabs_nat //; apply min_u2Z.
    move: (state_mint_var_mint _ s st h x (unsign rk rx) s_st_h).
    rewrite assoc.get_union_sing_eq.
    by case/(_ (refl_equal _)).
  move=> {Hhalve_u} [Hhalve_u Hhalve_u'].
  by move: {Hhalve_u'}(Hhalve_u' _ _ exec_mips).
split.
- move=> y ry y_ry.
  have [yx | yx] : y \in assoc.dom d \/ y = x.
    case/assoc.get_union_Some_inv : y_ry => y_ry.
    * by case/assoc.get_sing_inv : y_ry => -> _; right.
    * by apply assoc.get_Some_in_dom in y_ry; left.
  + have x_y : y <> x.
      move=> ?; subst y; by rewrite yx in Hd'.
    move: {s_st_h}(proj1 s_st_h _ _ y_ry) (proj2 s_st_h) => s_st_h1 s_st_h2.
    have x'_unchanged : ([ y ]_s = [ y ]_s')%pseudo_expr.
      Var_unchanged. rewrite /= mem_seq1; exact/negP/eqP.
    case: (mips_syntax.exec_deter_proj _ _ _ _ _ exec_mips
      (heap.dom (heap_mint (unsign rk rx) st h)) _ _ exec_mips_proj) => H4 [H5 H_h_h'].
    have <- : heap_mint ry st h = heap_mint ry st' h'.
      apply (heap_mint_state_invariant (heap_mint (unsign rk rx) st h) y s) => //.
      move=> rx0 Hrx0; Reg_unchanged.
      apply (@disj_not_In _ (mint_regs ry)); last by [].
      apply/disj_sym/(Hd_unchanged y).
      by rewrite -y_ry assoc.get_union_sing_neq.
      apply s_st_h2 with y x => //.
      by rewrite assoc.get_union_sing_eq.
    move: s_st_h1; apply var_mint_invariant.
    move=> rx0 Hrx0; Reg_unchanged.
    apply (@disj_not_In _ (mint_regs ry)); last by [].
    apply/disj_sym/(Hd_unchanged y) => //.
    by rewrite -y_ry assoc.get_union_sing_neq.
    exact x'_unchanged.
  + subst y.
    have ? : ry = unsign rk rx.
      rewrite assoc.get_union_sing_eq in y_ry.
      by case: y_ry.
    subst ry.

    set vx := u2Z ([ rx ]_ st).
    set nk' := '|u2Z (store.get rk st')|.
    set vx' := u2Z (store.get rx st').
    move: (state_mint_var_mint _ s st h x (unsign rk rx) s_st_h).
    rewrite assoc.get_union_sing_eq.
    move/(_ (refl_equal _)).
    case=> X1 X2 X3.

    rewrite /postcond in hoare_triple_post_condition.
    case: hoare_triple_post_condition => [h1 [h2 [Hdisj [Hunion [[A' [Hdiv2_1 [Hdiv2_2 [Hdiv2_3 [Hdiv2_4 Hdiv2_5]]]]] HTT]]]]].
    have Hnknk' : nk = nk'.
      rewrite /nk /nk'; do 2 f_equal.
      Reg_unchanged. simpl mips_frame.modified_regs. by Uniq_not_In.
    subst nk'.
    have Hvxvx' : vx = vx'.
      rewrite /vx /vx'; f_equal.
      Reg_unchanged. simpl mips_frame.modified_regs. by Uniq_not_In.
    subst vx'.
    apply mkVarUnsign.
    - by rewrite -Hvxvx' -Hnknk'.
    - split.
      - move/syntax_m.seplog_m.semop_prop_m.exec_cmd0_inv : exec_pseudo.
        case/syntax_m.seplog_m.exec0_assign_inv => _ -> /=.
        syntax_m.seplog_m.assert_m.expr_m.Store_upd.
        apply Z_div_pos => //; tauto.
      - move/syntax_m.seplog_m.semop_prop_m.exec_cmd0_inv : exec_pseudo.
        case/syntax_m.seplog_m.exec0_assign_inv=> _ -> /=.
        syntax_m.seplog_m.assert_m.expr_m.Store_upd.
        rewrite /ZIT.div.
        apply (@leZ_ltZ_trans ([ x ]_s)%pseudo_expr) => //.
        apply Zdiv_le_upper_bound => //; lia.
        rewrite -Hnknk'; tauto.
    - move: (Hdiv2_4) => Hdiv2_4_save.
      apply mapstos_inv_list2heap in Hdiv2_4 => //; last first.
        by rewrite [eval _ _]/= Hdiv2_1 -Hvxvx'.
      rewrite u2Z_shrl' in Hdiv2_4; last by repeat constructor.
      rewrite /= -Hvxvx' in Hdiv2_4.
      rewrite /heap_cut Hunion Hdiv2_4.
      move/syntax_m.seplog_m.semop_prop_m.exec_cmd0_inv : exec_pseudo.
      case/syntax_m.seplog_m.exec0_assign_inv=> _ -> /=.
      syntax_m.seplog_m.assert_m.expr_m.Store_upd.
      rewrite lSum_Z2ints_pos // in Hdiv2_5.
      rewrite -Hdiv2_5 mulZC /ZIT.div Z_div_plus_full_l //.
      rewrite (Zdiv_small (u2Z ([a2]_ st' `>> 31))).
      rewrite addZ0 -Hnknk' -Z2ints_lSum //.
      rewrite /heap_cut.
      have <- : heap.dom (list2heap '|(vx / 4)| A') =
        ((iota '|u2Z ([rx ]_ st') / 4| nk) : list assoc.l).
        by rewrite dom_list2heap Hdiv2_1 Hvxvx'.
      rewrite heap.proj_union_L_dom; last by rewrite -Hdiv2_4.
      rewrite heap.proj_itself.
      rewrite -Hdiv2_4; exact Hdiv2_4_save.
      split; first by apply min_u2Z.
      by apply (shrl_lt ([ a2 ]_ st') 31).
- apply (state_mint_part2_one_variable_unsign _ _ _ _ _ _ _ _ _ s_st_h).
  + move=> t x0 Ht Hx0.
    Reg_unchanged. simpl mips_frame.modified_regs.
    case/assoc.in_cdom_union_inv : Ht => Ht.
    * rewrite assoc.cdom_sing /= seq.mem_seq1 in Ht.
      move/eqP : Ht => Ht; subst t.
      apply (@disj_not_In _ (mint_regs (unsign rk rx))); last by [].
      Disj_remove_dup.
      rewrite /=.
      apply uniq_disj.
      simpl cat.
      by Uniq_uniq r0.
    * apply (@disj_not_In _ (mint_regs t)); last by [].
      Disj_remove_dup.
      apply disj_sym.
      apply (disj_incl_LR Hd); last by apply incl_refl_Permutation; PermutProve.
      exact/incP/inc_mint_regs.
  + move: (mips_syntax.exec_deter_proj _ _ _ _ _ exec_mips _ _ _ exec_mips_proj); tauto.
  + exact: (mips_syntax.dom_heap_invariant _ _ _ _ _ exec_mips).
Qed.