Library multi_halve_s_triple

From mathcomp Require Import ssreflect ssrfun ssrbool eqtype seq ssrnat.
Require Import ssrZ ZArith_ext seq_ext ssrnat_ext uniq_tac machine_int multi_int.
Import MachineInt.
Require Import mips_seplog mips_contrib mips_frame mips_mint mips_tactics.
Import expr_m.
Import assert_m.
Require Import multi_halve_s_prg multi_is_zero_u_triple multi_zero_s_triple.
Require Import abs_triple multi_halve_u_triple multi_incr_u_triple.
Require Import multi_is_even_u_triple multi_halve_s_noneucl_triple.

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

Lemma multi_halve_s_triple_gen rk a a0 i tmp prev next : uniq(a, a0, i, tmp, prev, next, rk, r0) ->
  forall nk, Z_of_nat nk < 2 ^^ 31 ->
    forall va, u2Z va + 4 * 2 < \B^1 ->
    forall ptr, u2Z ptr + 4 * Z_of_nat nk < \B^1 ->
      forall slen, s2Z slen = Z.sgn (s2Z slen) * Z_of_nat nk ->
        forall A, size A = nk ->
          Z.sgn (s2Z slen) = Z.sgn (Z.sgn (s2Z slen) * \S_{ nk } A) ->
      {{ fun s h => [ a ]_s = va /\
        (var_e a |--> slen :: ptr :: nil ** int_e ptr |--> A) s h }}
      multi_halve_s a a0 i tmp prev next rk
      {{ fun s h => exists slen' A',
        size A' = nk /\
        s2Z slen' = Z.sgn (s2Z slen') * Z_of_nat nk /\
        Z.sgn (s2Z slen') = Z.sgn (Z.sgn (s2Z slen') * \S_{ nk } A') /\
        [ a ]_s = va /\
        (var_e a |--> slen' :: ptr :: nil ** int_e ptr |--> A') s h /\
        2 * Z.sgn (s2Z slen') * \S_{ nk } A' + u2Z ([prev]_s `>> 31) = Z.sgn (s2Z slen) * \S_{ nk } A
      }}.
Proof.
move=> Hnodup nk nk_231 va va_fit ptr ptr_fit slen slen_nk A A_nk slen_A.
rewrite /multi_halve_s.

lw rk zero16 a ;

apply hoare_lw_back_alt'' with (
  !(fun s => [ rk ]_s = slen) **
  (fun s h => ([a ]_ s) = va /\
    ((var_e a |--> slen :: ptr :: nil) ** (int_e ptr |--> A)) s h)).

move=> s h [Ha Hmem].
exists slen; split.
  move: Hmem.
  rewrite -mapsto2_mapstos.
  rewrite conAE.
  apply monotony => // h1.
  apply mapsto_ext => //=.
  by rewrite sext_Z2u // addi0.

rewrite /update_store_lw.
apply store_upd_con.
apply con_and_bang_L; repeat Reg_upd => //.
split; first by [].
by Assert_upd.

apply hoare_ifte_bang.


  apply hoare_addiu' => s h H.
  apply con_and_bang_inv_R in H.
  case: H => H.
  rewrite [in X in X -> _]/=.
  move/eqP/u2Z_inj.
  rewrite store.get_r0 => Hr.
  case: H => h1 [h2 [h1dh2 [h1Uh2 [[Hh1 ?] [a_va Hh2]]]]]; subst h1.
  clear h1dh2.
  rewrite heap.unioneh in h1Uh2; subst h2.
  rewrite /wp_addiu.
  subst slen.
  exists zero32, A.
  repeat Reg_upd.
  split; first by [].
  rewrite !s2Z_u2Z_pos' // ?Z2uK //; last by rewrite Hr Z2uK.
  split; first by Assert_upd.
  split; first by reflexivity.
  split; first by [].
  split.
    rewrite -Hr.
    by Assert_upd.
  by rewrite sext_Z2u // addi0 Hr shrl_Z2u_0 Z2uK // addZ0.


apply hoare_prop_m.hoare_stren with (
  !(fun s => slen != zero32) **
  !(fun s => ([rk ]_ s) = slen) **
  (fun s h =>
    ([a ]_ s) = va /\
    (var_e a |--> slen :: ptr :: nil **
      int_e ptr |--> A) s h)).

move=> s h H.
apply con_and_bang_inv_R in H.
case: H => H H2.
apply con_and_bang_inv_L in H.
case: H => H3 H.
apply con_and_bang_L => //.
apply con_and_bang_L => //.
rewrite /= in H2.
move: H2.
rewrite H3.
rewrite store.get_r0 Z2uK //.
apply contra.
move/eqP => ->.
by rewrite Z2uK.

apply pull_out_bang => slen0.

lw a0 four16 a

apply hoare_lw_back_alt'' with (
  (fun s h => [a ]_ s = va /\
    [rk]_s = slen /\
    (var_e a |--> slen :: ptr :: nil ** int_e ptr |--> A) s h /\
    [a0]_s = ptr)).

move=> s h H.
exists ptr.
rewrite conCE in H.
rewrite -mapsto2_mapstos /= in H.
case: H => h1 [h2 [h1dh2 [h1Uh2 [Hh1 Hh2]]]].
case: Hh1 => a_va Hh1.
case: Hh2 => ? Hh2; subst h2.
clear h1dh2.
rewrite heap.unionhe in h1Uh2; subst h1.
split.
  rewrite conAE in Hh1.
  rewrite conCE conAE in Hh1.
  move: Hh1.
  apply monotony => // h'.
  apply mapsto_ext => //=.
  by rewrite sext_Z2u.
rewrite /update_store_lw.
repeat Reg_upd.
split; first by [].
split; first by [].
split; last by reflexivity.
Assert_upd.
by rewrite -mapsto2_mapstos.

while.ifte (bgtz rk)

apply hoare_ifte_bang.

apply hoare_prop_m.hoare_stren with ((fun s h =>
      [a ]_ s = va /\
      [rk ]_ s = slen /\
      (var_e a |--> slen :: ptr :: nil ** int_e ptr |--> A) s h /\
      [a0 ]_ s = ptr) ** !(fun s : store.t => 0 < s2Z slen)).
move=> s h H.
apply con_and_bang_inv_R in H.
case : H => H Hrk.
apply con_and_bang_R => //.
rewrite /= in Hrk.
move/ltZP in Hrk.
case: H => Ha [Hb [Hc Hd]].
by rewrite Hb in Hrk.

rewrite assert_m.conCE.
apply pull_out_bang => Hslen.

apply while.hoare_seq with ((fun s h => exists A',
    size A' = nk /\
    [a0 ]_ s = ptr /\
    u2Z [rk ]_ s = Z_of_nat nk /\
    (var_e a0 |--> A') s h /\
    2 * \S_{ nk } A' + u2Z ([prev ]_ s `>> 31) = \S_{ nk } A) **
  ((var_e a |--> slen :: ptr :: nil) ** !(fun s => [a]_s = va))).

eapply (before_frame ((var_e a |--> slen :: ptr :: nil) ** !(fun s => [a]_s = va))
_
(fun s h => exists A', size A' = nk /\
    [a0 ]_ s = ptr /\
    u2Z [rk ]_ s = Z_of_nat nk /\
    (var_e a0 |--> A') s h /\
    2 * \S_{ nk } A' + u2Z ([prev ]_ s `>> 31) = \S_{ nk } A)).

apply mips_frame.frame_rule_R.
  eapply multi_halve_u_triple => //.
  by Uniq_uniq r0.
  rewrite [modified_regs _]/=.
  by Inde.
  by [].

move=> s h [Ha [Hb [Hc Hd]]].
rewrite assert_m.conCE in Hc.
move: Hc.
apply monotony => // h' Hh'.
split => //.
split; last by move: Hh'; exact/mapstos_ext.
rewrite Hb -s2Z_u2Z_pos; last by lia.
apply Zsgn_pos in Hslen.
by rewrite slen_nk Hslen mul1Z.

by apply con_and_bang_R.

by apply hoare_prop_m.entails_id.

apply exists_conC_hoare.
apply pull_out_exists => A'.

rewrite con_bangE_R.
rewrite conAE.
apply pull_out_bang => A'_nk.

apply while.hoare_seq with ((fun s h =>
  u2Z ([rk ]_ s) = Z_of_nat nk /\
  ([a0 ]_ s) = ptr /\
  (var_e a0 |--> A') s h /\
  (0 = \S_{ nk } A' -> ([next ]_ s) = one32) /\
  (0 < \S_{ nk } A' -> ([next ]_ s) = zero32)) **
!(fun s0 => 2 * \S_{ nk } A' + u2Z (([prev ]_ s0) `>> 31) = \S_{ nk } A) **
(var_e a |--> slen :: ptr :: nil) **
!(fun s => [a]_s = va)).

eapply hoare_prop_m.hoare_stren; last first.
  apply mips_frame.frame_rule_R.
  apply multi_is_zero_u_triple => //.
  by Uniq_uniq r0.
  rewrite [modified_regs _]/=.
  Inde.
  apply inde_u2Z_b_get_R with (b := fun x => @shrl x 32).
  by Uniq_not_In.
  by [].

move=> s h H.
rewrite -assert_m.conAE.
move: H; apply monotony => // h' H.
apply con_and_bang_R; tauto.

while.ifte (bne next r0)

apply while.hoare_ifte.

have : uniq(a, r0) by Uniq_uniq r0.
move/multi_zero_s_triple.
move/(_ A' ptr slen) => Htmp.
eapply (before_frame !(fun s0 =>
  [a ]_ s0 = va /\
  u2Z ([rk ]_ s0) = Z_of_nat nk /\
  [a0 ]_ s0 = ptr /\
  0 = \S_{ nk } A' /\
  2 * \S_{ nk } A' + u2Z (([prev ]_ s0) `>> 31) = \S_{ nk } A /\
  u2Z ([a ]_ s0) + 4 * 2 < \B^1)).
apply frame_rule_R.
  by apply Htmp.
  simpl modified_regs; by apply inde_nil.
by [].

move=> s h [H Htest].

  apply con_and_bang_R.
  rewrite -2!assert_m.conAE in H.
  apply con_and_bang_inv_R in H.
  case: H => H a_va.
  rewrite conCE in H.
  move: H; apply monotony => // h' Hh'.
  apply con_and_bang_inv_R in Hh'.
  case: Hh' => [[Ha [Hb [Hc Hd]]] He].
  move: Hc.
  by apply mapstos_ext.

  rewrite -2!assert_m.conAE in H.
  apply con_and_bang_inv_R in H.
  case: H => H a_va.
  case: H => h1 [h2 [h1dh2 [h1Uh2 [Hh1 Hh2]]]].
  apply con_and_bang_inv_R in Hh1.
  case: Hh1 => Hh1 Hshift.
  case: Hh1 => [Ha [Hb [Hc [Hd He]]]].
  repeat (split => //).
  rewrite /= store.get_r0 /= Z2uK // in Htest.
  case/leZ_eqVlt : (min_lSum nk A') => // abs.
  apply He in abs.
  by rewrite abs Z2uK // in Htest.
  by rewrite a_va.

move=> s h H.

apply con_and_bang_inv_R in H.
case: H => [Ha [Hb [Hc [Hd [He [Hf Hg]]]]]].

have [X|X] : u2Z (([prev ]_ s) `>> 31) = 0 \/
  u2Z (([prev ]_ s) `>> 31) = 1.
  move: (shrl_lt ([prev ]_ s) 31) => /= X1.
  move: (min_u2Z (([prev ]_ s) `>> 31)) => ?; lia.

exists zero32.
exists A'.
split; first by [].
rewrite s2Z_u2Z_pos'; last by rewrite Z2uK.
rewrite Z2uK //.
split; first by [].
split; first by [].
split; first by [].
split; first by [].
rewrite X -Hf -He X; ring.

exists zero32, A'.
split; first by [].
rewrite s2Z_u2Z_pos'; last by rewrite Z2uK.
rewrite Z2uK //.
split; first by [].
split; first by [].
split; first by [].
split; first by [].
rewrite X -Hf X -He.
apply Zsgn_pos in Hslen.
rewrite Hslen; ring.

apply hoare_nop'.
move=> s h H.
case: H => H Htest.
rewrite /= in Htest.
move/negPn : Htest.
rewrite store.get_r0 Z2uK //.
move/eqP=> Htest.
case: H => h1 [h2 [h1dh2 [h1Uh2 [Hh1 Hh2]]]].
apply con_and_bang_inv_L in Hh2.
case: Hh2 => Hshift Hh2.

exists slen, A'.
apply con_and_bang_inv_R in Hh2.
case: Hh2 => Hh2 a_va.
apply Zsgn_pos in Hslen.
split; first by [].
split; first by [].
case: Hh1 => Hrk [a0_ptr [Hh1 [HSum1 HSum2]]].
rewrite Hslen !mul1Z.
case/leZ_eqVlt : (min_lSum nk A') => // abs.
- apply HSum1 in abs.
  by rewrite abs Z2uK in Htest.
- split.
    by apply/esym/Zsgn_pos.
  split; first by [].
  split.
    rewrite assert_m.conCE.
    exists h1, h2; repeat (split => //).
    by move: Hh1; apply mapstos_ext.
  by rewrite mulZ1.

apply hoare_prop_m.hoare_stren with ((fun s h =>
  [a ]_ s = va /\
  [rk ]_ s = slen /\
  (var_e a |--> slen :: ptr :: nil ** int_e ptr |--> A) s h /\
  [a0 ]_ s = ptr) ** !(fun s : store.t => s2Z slen < 0)).

move=> s h H.
apply con_and_bang_inv_R in H.
case: H => [[H1 [H2 [H3 H4]]] Htest].
rewrite /= in Htest.
move/ltZP in Htest.
apply con_and_bang_R => //.
move: Htest; rewrite H2 => /leZNgt/leZ_eqVlt[|] // /eqP abs.
suff : s2Z slen != 0 by rewrite abs.
apply: contra slen0 => /eqP.
rewrite (_ : 0 = s2Z (Z2u 32 0)).
by move/s2Z_inj/eqP.
by rewrite s2Z_u2Z_pos' // Z2uK.

rewrite assert_m.conCE.
apply pull_out_bang => Hslen.

abs_prg.abs rk i ;

apply while.hoare_seq with
  (!(fun s => s2Z ([rk ]_ s) = Z.abs (s2Z slen)) **
  (fun s h => ([a ]_ s) = va /\
    (var_e a |--> slen :: ptr :: nil ** int_e ptr |--> A) s h /\
    [a0]_s = ptr)).

have Hslen' : s2Z slen <> - 2 ^ 31.
  move=> H.
  apply Zsgn_neg in Hslen.
  rewrite slen_nk Hslen mulN1Z eqZ_opp in H *.
  exact/ltZ_eqF.
have : uniq(rk, i, r0) by Uniq_uniq r0.
move/abs_triple_bang/(_ _ Hslen') => Htmp.

eapply hoare_prop_m.hoare_stren; last first.
  apply mips_frame.frame_rule_R.
  apply Htmp.
  rewrite [modified_regs _]/=; by Inde.
  by [].
move=> s h H.
rewrite [_ |--> _]lock -lock in H.
apply con_and_bang_L; tauto.

apply while.hoare_seq with (fun s h =>
  u2Z [rk ]_ s = Z_of_nat nk /\
  [a ]_ s = va /\
  (var_e a |--> slen :: ptr :: nil ** int_e ptr |--> A) s h /\
  [a0 ]_ s = ptr /\
  (Zeven (\S_{ nk } A) -> [tmp]_s = one32) /\
  (Zodd (\S_{ nk } A) -> [tmp]_s = zero32)).

have : uniq(rk, a0, tmp, r0) by Uniq_uniq r0.
move/multi_is_even_u_triple/(_ nk A ptr A_nk) => Htmp.
eapply (before_frame (fun s h => [a ]_ s = va /\ (var_e a |--> slen :: ptr :: nil) s h)).
apply mips_frame.frame_rule_R.
  apply Htmp.
  rewrite [modified_regs _]/=; by Inde.
by [].

move=> s h H.
apply con_and_bang_inv_L in H.
case: H => H1 [H2 [H3 H4]].
rewrite assert_m.conCE in H3.
move: H3.
apply monotony => // h' Hh'.
split.
  by apply auxili with slen.
split => //.
move: Hh'.
by apply mapstos_ext.

move=> s h H.
case: H => h1 [h2 [h1dh2 [h1Uh2 [Hh1 Hh2]]]].
rewrite assert_m.conCE.
split; first by tauto.
split; first by tauto.
split.
  exists h1, h2; repeat (split => //).
  2: tauto.
  case: Hh1 => H1 [H2 [H3 [H4 H5]]].
  move: H3.
  by apply mapstos_ext.
  tauto.

apply while.hoare_ifte.

apply hoare_prop_m.hoare_stren with (fun s h =>
  Zeven (\S_{ nk } A) /\
  u2Z [rk ]_ s = Z_of_nat nk /\
  [a ]_ s = va /\
  (var_e a |--> slen :: ptr :: nil ** int_e ptr |--> A) s h /\
  [a0 ]_ s = ptr).

move=> s h [H Htest].
rewrite /= store.get_r0 Z2uK // in Htest .
split; last by tauto.
case: H => _ [_ [_ [_ [Heven Hodd]]]].
case: (Zeven_odd_dec (\S_{ nk } A)) => //.
move/Hodd => abs.
by rewrite abs !Z2uK // in Htest.

apply (hoare_prop_m.pull_out_conjunction' hoare0_false) => Heven.

have : uniq(rk, a0, i, tmp, prev, next, r0) by Uniq_uniq r0.
move/multi_halve_u_triple/(_ _ _ ptr_fit _ A_nk) => Htmp.
eapply (before_frame (fun s h => [a ]_ s = va /\ (var_e a |--> slen :: ptr :: nil) s h)).
apply mips_frame.frame_rule_R.
  by apply Htmp.
  rewrite [modified_regs _]/=; by Inde.
by [].

clear Htmp.
move=> s h H.
case: H => nk_rk [a_va [Hh a0_ptr]].
case: Hh => h1 [h2 [h1dh2 [h1Uh2 [Hh1 Hh2]]]].
rewrite assert_m.conCE.
exists h1, h2; (repeat (split => //)).
move: Hh2.
by apply mapstos_ext.

clear Htmp.
move=> s h H.
case: H => h1 [h2 [h1dh2 [h1Uh2 [Hh1 [a_va Hh2]]]]].
case: Hh1 => A' [A'_nk [a0_ptr [rk_nk [Hh1 HSum]]]].
have no_overflow : u2Z ([prev ]_ s `>> 31) = 0.
  rewrite -HSum in Heven.
  apply Zeven_plus_inv in Heven.
  case: Heven => Heven.
    case: Heven => _ Heven.
    have [Hoverflow|Hoverflow] : u2Z ([prev ]_ s `>> 31) = 0 \/ u2Z ([prev ]_ s `>> 31) = 1.
      move: (shrl_lt ([prev ]_ s) 31) => /= X1.
      move: (min_u2Z (([prev ]_ s) `>> 31)) => ?; lia.
      assumption.
    rewrite Hoverflow in Heven .
    by inversion Heven.
  case: Heven => abs _.
  apply Zodd_not_Zeven in abs.
  exfalso.
  by apply/abs/Zeven_2.

case/leZ_eqVlt : (min_lSum nk A') => // abs.
- rewrite -HSum -abs no_overflow mulZ0 addZ0 mulZ0 /= in slen_A.
  apply Zsgn_neg in Hslen.
  by rewrite Hslen in slen_A.
- exists slen, A'.
  split; first by [].
  split; first by [].
  split.
    rewrite no_overflow addZ0 in HSum.
    apply Zsgn_pos in abs.
    by rewrite Zsgn_Zmult ZsgnK abs mulZ1.
  split; first by [].
  split.
    rewrite assert_m.conCE.
    exists h1, h2; repeat (split => //).
    move: Hh1.
    by apply mapstos_ext.
  rewrite no_overflow -HSum no_overflow 2!addZ0; ring.

apply hoare_prop_m.hoare_stren with (fun s h =>
  Zodd (\S_{ nk } A) /\
  u2Z [rk ]_ s = Z_of_nat nk /\
  [a ]_ s = va /\
  (var_e a |--> slen :: ptr :: nil ** int_e ptr |--> A) s h /\
  [a0 ]_ s = ptr).

move=> s h [H Htest].
rewrite /= store.get_r0 Z2uK // in Htest .
split; last tauto.
case: H => _ [_ [_ [_ [Heven Hodd]]]].
case: (Zeven_odd_dec (\S_{ nk } A)) => //.
move/Heven => abs.
by rewrite abs !Z2uK // in Htest.

apply (hoare_prop_m.pull_out_conjunction' hoare0_false) => Hodd.

apply while.hoare_seq with (fun s h =>
  exists A', size A' = nk /\
  u2Z [rk ]_ s = Z_of_nat nk /\
  [a ]_ s = va /\
  (var_e a |--> slen :: ptr :: nil ** int_e ptr |--> A') s h /\
  [a0 ]_ s = ptr /\
  2 * \S_{ nk } A' + u2Z ([prev]_s `>> 31) = \S_{ nk } A).

have : uniq(rk, a0, i, tmp, prev, next, r0) by Uniq_uniq r0.
move/multi_halve_u_triple/(_ _ _ ptr_fit _ A_nk) => Htmp.
eapply (before_frame (fun s h => (var_e a |--> slen :: ptr :: nil) s h /\ [a ]_s = va)).
apply mips_frame.frame_rule_R.
  by apply Htmp.
  rewrite [modified_regs _]/=; by Inde.
by [].

move=> s h H.
case: H => rk_nk [a_va [Hh a0_ptr]].
rewrite assert_m.conCE.
case: Hh => h1 [h2 [h1dh2 [h1Uh2 [Hh1 Hh2]]]].
exists h1, h2; repeat (split => //).
move: Hh2.
by apply assert_m.mapstos_ext.

move=> s h H.
case: H => h1 [h2 [h1dh2 [h1Uh2 [Hh1 [Hh2 va_a]]]]].
case: Hh1 => A' [a'_nk [a0_ptr [rk_nk [Hh1 HSum]]]].
exists A'.
repeat (split => //).
rewrite assert_m.conCE.
exists h1, h2; repeat (split => //).
move: Hh1; by apply mapstos_ext.

apply pull_out_exists => A'.
apply (hoare_prop_m.pull_out_conjunction' hoare0_false) => A'_nk.

apply hoare_addiu with (fun s h =>
  u2Z [rk ]_ s = Z_of_nat nk /\
  [a ]_ s = va /\
  (var_e a |--> slen :: ptr :: nil ** int_e ptr |--> A') s h /\
  [a0 ]_ s = ptr /\ 2 * \S_{ nk } A' + 1 = \S_{ nk } A /\
  [next]_ s = one32).

move=> s h H.
case: H => rk_nk [a_va [Hh [a0_ptr HSum]]].
rewrite /wp_addiu.
repeat Reg_upd.
repeat (split => //).
by Assert_upd.

rewrite -HSum in Hodd.

have [Hoverflow|Hoverflow] : u2Z ([prev ]_ s `>> 31) = 0 \/ u2Z ([prev ]_ s `>> 31) = 1.
  move: (shrl_lt ([prev ]_ s) 31) => /= X1.
  move: (min_u2Z (([prev ]_ s) `>> 31)) => ?; lia.
- rewrite Hoverflow addZ0 in Hodd.
  apply Zodd_not_Zeven in Hodd.
  exfalso.
  by apply Hodd, Zeven_2.
- congruence.

by rewrite sext_Z2u // add0i.

apply while.hoare_seq with (fun s h => exists A',
  size A' = nk /\
  [next]_s = one32 /\
  u2Z [rk ]_ s = Z_of_nat nk /\
  [a ]_ s = va /\
  (var_e a |--> slen :: ptr :: nil ** int_e ptr |--> A') s h /\
  [a0 ]_ s = ptr /\ 2 * \S_{ nk } A' = \S_{ nk } A + 1).

have : uniq(rk, next, a0, i, tmp, prev, r0) by Uniq_uniq r0.
move/multi_incr_u_triple/(_ _ _ ptr_fit _ A'_nk) => Htmp.
eapply (before_frame (fun s h =>
  u2Z [rk ]_ s = Z_of_nat nk /\
  [a ]_ s = va /\
  (var_e a |--> slen :: ptr :: nil) s h /\
  2 * \S_{ nk } A' + 1 = \S_{ nk } A)).
apply mips_frame.frame_rule_R.
  by apply Htmp.
  rewrite [modified_regs _]/=; by Inde.
by [].

move=> s h H.
case: H => nk_rk [a_va [Hh [a0_ptr [HSum Hnext]]]].
case: Hh => h1 [h2 [h1dh2 [h1Uh2 [Hh1 Hh2]]]].
rewrite assert_m.conCE.
exists h1, h2; repeat (split => //).
move: Hh2.
by apply assert_m.mapstos_ext.

move=> s h H.
case: H => h1 [h2 [h1dh2 [h1Uh2 [Hh1 Hh2]]]].
case: Hh1 => A'' [A''_nk [Hnext [a0_ptr [Hh1 [Hlo HSum]]]]].
case: Hh2 => rk_nk [a_va [Hh2 HSum']].
destruct nk => //.
rewrite [(if beq_nat nk.+1 0 then 0 else 1)]/= in HSum.
have no_overflow : u2Z (store.lo s) = 0.
  have [X|X] : u2Z (store.lo s) = 0 \/ u2Z (store.lo s) = 1.
    move: (min_u2Z (store.lo s)) => ?; lia.
  by [].
  rewrite X mul1Z in HSum.
  have A'_bound : \S_{ nk.+1 } A' <= 2 ^^ (nk.+1 * 32 - 1).
    have H1 : 2 * \S_{ nk.+1 } A' + 1 < \B^nk.+1.
      rewrite HSum'.
      by apply max_lSum.
    have {}H1 : 2 * \S_{ nk.+1 } A' <= \B^nk.+1 by lia.
    rewrite ZbetaE in H1.
    rewrite (_ : (nk.+1 * 32 = (nk.+1 * 32 - 1).+1)%nat) in H1; last by rewrite mulSn -subSn.
    rewrite (_ : 2 ^^ (nk.+1 * 32 - 1).+1 = 2 * 2 ^^ (nk.+1 * 32 - 1) ) in H1; last by [].
    do 2 rewrite (mulZC 2) in H1.
    by apply Zmult_le_reg_r in H1.
  have H1 : \S_{ nk.+1 } A'' <= 2 ^^ (nk.+1 * 32 - 1) + 1 - \B^nk.+1 by lia.
  have H2 : 2 ^^ (nk.+1 * 32 - 1) + 1 - \B^nk.+1 <= 0.
    have H2 : 2 ^^ (nk.+1 * 32 - 1) + 1 <= \B^nk.+1.
      rewrite ZbetaE.
      have ? : 2 ^^ (nk.+1 * 32 - 1) < 2 ^^ (nk.+1 * 32).
        apply/expZ_2_lt; by rewrite subn1 prednK.
      lia.
    lia.
  have H3 : \S_{ nk.+1 } A'' = 0 by move: (min_lSum nk.+1 A'') => ?; lia.
  have H4 : \S_{ nk.+1 } A' = \B^nk.+1 - 1 by lia.
  rewrite H4 in HSum'.
  rewrite (_ : 2 * (\B^nk.+1 - 1) + 1 = 2 * \B^nk.+1 - 1) in HSum'; last by lia.
  have H5 : 2 * \B^nk.+1 <= \B^nk.+1.
    suff H5 : 2 * \B^nk.+1 - 1 < \B^nk.+1 by lia.
    apply: leZ_ltZ_trans; last by apply max_lSum.
    rewrite HSum'; exact/leZZ.
  move/leZP in H5; rewrite {1}(_ : 2 = 2 ^^ 1) // -ZpowerD Zpower_2_le in H5.
  ssromega.

rewrite no_overflow mul0Z addZ0 in HSum.
exists A''.
repeat (split; first by []).
split.
  rewrite assert_m.conCE.
  exists h1, h2; repeat (split => //).
  move: Hh1.
  by apply assert_m.mapstos_ext.
split; first by [].
rewrite HSum -HSum'; ring.

apply hoare_sll' => s h [A'' [A''_nk [Hone [H1 [a_va [Hmem [a0_ptr A''_A]]]]]]].
rewrite /wp_sll.
exists slen, A''.
repeat Reg_upd.
repeat (split; first by []).
apply Zsgn_neg in Hslen.
rewrite Hslen.
rewrite !mulN1Z.
split.
  symmetry.
  apply Zsgn_neg.
  case/leZ_eqVlt : (min_lSum nk A'') => abs.
  - rewrite -abs mulZ0 in A''_A.
    move: (min_lSum nk A) => ?; lia.
  - lia.
split; first by [].
split; first by Assert_upd.
rewrite Z2uK // Hone (_ : '| 31 | = 31)%nat //.
move: (@shl_shrl 32 one32 1).
move=> ->.
  rewrite Z2uK //; lia.
by rewrite Z2uK.
Qed.

Lemma multi_halve_s_triple rk a a0 i tmp prev next : uniq(a, a0, i, tmp, prev, next, rk, r0) ->
  forall nk va, Z_of_nat nk < 2 ^^ 31 ->
    forall val : Z,
      {{ fun s h => [ a ]_s = va /\ (var_signed nk a val) s h }}
      multi_halve_s a a0 i tmp prev next rk
      {{ fun s h => exists val',
        (var_signed nk a val') s h /\ 2 * val' + u2Z ([prev]_s `>> 31) = val }}.
Proof.
move=> Hnodup nk va nk_231 val.

apply hoare_prop_m.hoare_stren with (fun s h => exists slen ptr A,
  SignMagn slen nk A val /\
  u2Z ptr + 4 * Z_of_nat nk < \B^1 /\
  u2Z va + 4 * 2 < \B^1 /\
  [a ]_ s = va /\
  ((var_e a |--> slen :: ptr :: nil) ** (int_e ptr |--> A)) s h).

move=> s h [Ha Hmem].
case: Hmem => slen ptr A a_fit encodingA ptr_fit Hmem.
exists slen, ptr, A.
repeat (split; first by []).
split; by [congruence | ].

apply pull_out_exists => slen.
apply pull_out_exists => ptr.
apply pull_out_exists => A.
apply (hoare_prop_m.pull_out_conjunction' hoare0_false) => encoding.
apply (hoare_prop_m.pull_out_conjunction' hoare0_false) => ptr_fit.
apply (hoare_prop_m.pull_out_conjunction' hoare0_false) => va_fit.

case: encoding => H1 H2 H3 H4.
move: (multi_halve_s_triple_gen _ _ _ _ _ _ _ Hnodup _ nk_231 _ va_fit _ ptr_fit _ H2 _ H1).
rewrite {1}H3 -{1}H4.
move/(_ (refl_equal _)) => Htmp.

eapply hoare_prop_m.hoare_weak; last by apply Htmp.
move=> s h [slen' [A' [A'_nk [slen'_nk [slen'_A' [a_va [Hmem H]]]]]]].
exists (Z.sgn (s2Z slen') * \S_{ nk } A').
split.
  apply mkVarSigned with slen' ptr A' => //.
  congruence.
by rewrite mulZA H.
Qed.