Module Sexpr

Symbolic expressions

Require Import Extra.
Require Op.
Import Utf8 Coqlib.
Import AST Globalenvs.
Import Integers Values.

Set Implicit Arguments.
Unset Elimination Schemes.
Local Coercion is_true : bool >-> Sortclass.

Section EVAL.
  Context (find_symbol: identoption block) (sp: val).
  Definition symbol_address (s: ident) (o: int) : val :=
    match find_symbol s with
    | Some b => Vptr b o
    | None => Vundef
    end.
  Context (valid_pointer : blockZbool).

Definition eval_condition (cond: Op.condition) (args: list val) : option bool :=
  match cond, args with
  | Op.Ccomp c, v1 :: v2 :: nil => Val.cmp_bool c v1 v2
  | Op.Ccompu c, v1 :: v2 :: nil => Val.cmpu_bool valid_pointer c v1 v2
  | Op.Ccompimm c n, v1 :: nil => Val.cmp_bool c v1 (Vint n)
  | Op.Ccompuimm c n, v1 :: nil => Val.cmpu_bool valid_pointer c v1 (Vint n)
  | Op.Ccompf c, v1 :: v2 :: nil => Val.cmpf_bool c v1 v2
  | Op.Cnotcompf c, v1 :: v2 :: nil => option_map negb (Val.cmpf_bool c v1 v2)
  | Op.Ccompfs c, v1 :: v2 :: nil => Val.cmpfs_bool c v1 v2
  | Op.Cnotcompfs c, v1 :: v2 :: nil => option_map negb (Val.cmpfs_bool c v1 v2)
  | Op.Cmaskzero n, v1 :: nil => Val.maskzero_bool v1 n
  | Op.Cmasknotzero n, v1 :: nil => option_map negb (Val.maskzero_bool v1 n)
  | _, _ => None
  end.

  Remark op_eval_condition m :
    (∀ b o, Memory.Mem.valid_pointer m b o = valid_pointer b o) →
    ∀ cond args,
      eval_condition cond args = Op.eval_condition cond args m.
Proof.
    intros E.
    destruct cond; try reflexivity;
    intros [ | α args ]; try reflexivity;
    destruct args as [ | β args ]; try reflexivity;
    try (destruct args as [ | γ args ]; try reflexivity);
    destruct α; try reflexivity;
    try (destruct β; try reflexivity);
    simpl; rewrite ! E; reflexivity.
  Qed.

Definition eval_addressing
    (addr: Op.addressing) (vl: list val) : option val :=
  match addr, vl with
  | Op.Aindexed n, v1::nil =>
      Some (Val.add v1 (Vint n))
  | Op.Aindexed2 n, v1::v2::nil =>
      Some (Val.add (Val.add v1 v2) (Vint n))
  | Op.Ascaled sc ofs, v1::nil =>
      Some (Val.add (Val.mul v1 (Vint sc)) (Vint ofs))
  | Op.Aindexed2scaled sc ofs, v1::v2::nil =>
      Some(Val.add v1 (Val.add (Val.mul v2 (Vint sc)) (Vint ofs)))
  | Op.Aglobal s ofs, nil =>
      Some (symbol_address s ofs)
  | Op.Abased s ofs, v1::nil =>
      Some (Val.add (symbol_address s ofs) v1)
  | Op.Abasedscaled sc s ofs, v1::nil =>
      Some (Val.add (symbol_address s ofs) (Val.mul v1 (Vint sc)))
  | Op.Ainstack ofs, nil =>
      Some(Val.add sp (Vint ofs))
  | _, _ => None
  end.


Definition eval_operation (op: Op.operation) (args: list val) : option val :=
  match op, args with
  | Op.Omove, v1::nil => Some v1
  | Op.Ointconst n, nil => Some (Vint n)
  | Op.Ofloatconst n, nil => Some (Vfloat n)
  | Op.Osingleconst n, nil => Some (Vsingle n)
  | Op.Oindirectsymbol id, nil => Some (symbol_address id Int.zero)
  | Op.Ocast8signed, v1 :: nil => Some (Val.sign_ext 8 v1)
  | Op.Ocast8unsigned, v1 :: nil => Some (Val.zero_ext 8 v1)
  | Op.Ocast16signed, v1 :: nil => Some (Val.sign_ext 16 v1)
  | Op.Ocast16unsigned, v1 :: nil => Some (Val.zero_ext 16 v1)
  | Op.Oneg, v1::nil => Some (Val.neg v1)
  | Op.Osub, v1::v2::nil => Some (Val.sub v1 v2)
  | Op.Omul, v1::v2::nil => Some (Val.mul v1 v2)
  | Op.Omulimm n, v1::nil => Some (Val.mul v1 (Vint n))
  | Op.Omulhs, v1::v2::nil => Some (Val.mulhs v1 v2)
  | Op.Omulhu, v1::v2::nil => Some (Val.mulhu v1 v2)
  | Op.Odiv, v1::v2::nil => Val.divs v1 v2
  | Op.Odivu, v1::v2::nil => Val.divu v1 v2
  | Op.Omod, v1::v2::nil => Val.mods v1 v2
  | Op.Omodu, v1::v2::nil => Val.modu v1 v2
  | Op.Oand, v1::v2::nil => Some(Val.and v1 v2)
  | Op.Oandimm n, v1::nil => Some (Val.and v1 (Vint n))
  | Op.Oor, v1::v2::nil => Some(Val.or v1 v2)
  | Op.Oorimm n, v1::nil => Some (Val.or v1 (Vint n))
  | Op.Oxor, v1::v2::nil => Some(Val.xor v1 v2)
  | Op.Oxorimm n, v1::nil => Some (Val.xor v1 (Vint n))
  | Op.Onot, v1::nil => Some(Val.notint v1)
  | Op.Oshl, v1::v2::nil => Some (Val.shl v1 v2)
  | Op.Oshlimm n, v1::nil => Some (Val.shl v1 (Vint n))
  | Op.Oshr, v1::v2::nil => Some (Val.shr v1 v2)
  | Op.Oshrimm n, v1::nil => Some (Val.shr v1 (Vint n))
  | Op.Oshrximm n, v1::nil => Val.shrx v1 (Vint n)
  | Op.Oshru, v1::v2::nil => Some (Val.shru v1 v2)
  | Op.Oshruimm n, v1::nil => Some (Val.shru v1 (Vint n))
  | Op.Ororimm n, v1::nil => Some (Val.ror v1 (Vint n))
  | Op.Oshldimm n, v1::v2::nil => Some (Val.or (Val.shl v1 (Vint n))
                                               (Val.shru v2 (Vint (Int.sub Int.iwordsize n))))
  | Op.Olea addr, _ => eval_addressing addr args
  | Op.Onegf, v1::nil => Some(Val.negf v1)
  | Op.Oabsf, v1::nil => Some(Val.absf v1)
  | Op.Oaddf, v1::v2::nil => Some(Val.addf v1 v2)
  | Op.Osubf, v1::v2::nil => Some(Val.subf v1 v2)
  | Op.Omulf, v1::v2::nil => Some(Val.mulf v1 v2)
  | Op.Odivf, v1::v2::nil => Some(Val.divf v1 v2)
  | Op.Onegfs, v1::nil => Some(Val.negfs v1)
  | Op.Oabsfs, v1::nil => Some(Val.absfs v1)
  | Op.Oaddfs, v1::v2::nil => Some(Val.addfs v1 v2)
  | Op.Osubfs, v1::v2::nil => Some(Val.subfs v1 v2)
  | Op.Omulfs, v1::v2::nil => Some(Val.mulfs v1 v2)
  | Op.Odivfs, v1::v2::nil => Some(Val.divfs v1 v2)
  | Op.Osingleoffloat, v1::nil => Some(Val.singleoffloat v1)
  | Op.Ofloatofsingle, v1::nil => Some(Val.floatofsingle v1)
  | Op.Ointoffloat, v1::nil => Val.intoffloat v1
  | Op.Ofloatofint, v1::nil => Val.floatofint v1
  | Op.Ointofsingle, v1::nil => Val.intofsingle v1
  | Op.Osingleofint, v1::nil => Val.singleofint v1
  | Op.Omakelong, v1::v2::nil => Some(Val.longofwords v1 v2)
  | Op.Olowlong, v1::nil => Some(Val.loword v1)
  | Op.Ohighlong, v1::nil => Some(Val.hiword v1)
  | Op.Ocmp c, _ => Some(Val.of_optbool (eval_condition c args))
  | _, _ => None
  end.


End EVAL.

Section REGISTER.

Variable reg : Type.
Context (reg_dec: EqDec reg).

Inductive sexpr :=
| Reg `(reg)
| Name `(ident)
| Oper (op: Op.operation) (args: list sexpr)
.

Theorem sexpr_ind (P: sexprProp) :
  (∀ r, P (Reg r)) →
  (∀ n, P (Name n)) →
  (∀ op args, (∀ a, In a argsP a) → P (Oper op args)) →
  ∀ e, P e.
Proof.
  intros Hr Hn Ho.
  refine (
      fix sexpr_ind e : P e :=
        match e with
        | Reg r => Hr r
        | Name n => Hn n
        | Oper op args => Ho op args _
        end).
  revert args. clear - sexpr_ind.
  refine (
      fix args_ind args : ∀ a, In a argsP a :=
        match args returna, In a argsP a with
        | nil => λ _ X, False_ind _ X
        | b :: args' => λ a IN,
                        match IN with
                        | or_introl EQ => eq_ind b P (sexpr_ind b) a EQ
                        | or_intror IN' => args_ind _ _ IN'
                        end
        end).
Defined.

Instance sexpr_dec : EqDec sexpr.
Proof.
  refine (fix sexpr_dec x x' :=
            match x, x' with
            | Reg r, Reg r' =>
              match reg_dec r r' return { Reg r = Reg r' } + { Reg rReg r' } with
              | left EQ => left (f_equal Reg EQ)
              | right NE => right _
              end
            | Name n, Name n' =>
              match Pos.eq_dec n n' with
              | left EQ => left (f_equal Name EQ)
              | right NE => right _
              end
            | Oper op ar, Oper op' ar' =>
              match Op.eq_operation op op' with
              | left EQo =>
                match list_eq_dec sexpr_dec ar ar' with
                | left EQa => left (f_equal2 Oper EQo EQa)
                | right NE => right _
                end
              | right NE => right _
              end
            | _, _ => right _
            end); (clear -NE || clear); abstract congruence.
Defined.

  Definition oper op args : sexpr :=
  match op, args with
  | Op.Omove, a :: nil => a
  | Op.Osub, x :: Oper (Op.Ointconst n) nil :: nil =>
    Oper (Op.Olea (Op.Aindexed (Int.neg n))) (x :: nil)
  | _, _ => Oper op args
  end.

  Fixpoint map_sexpr fr (e: sexpr) : sexpr :=
    match e with
    | Reg r => fr r e
    | Name _ => e
    | Oper op args => oper op (List.map (map_sexpr fr) args)
    end.

  Definition subst_reg (r: reg) (n: sexpr) (e: sexpr) : sexpr :=
    map_sexprx e, if reg_dec x r then n else e) e.

  Definition comp {X} c (f: Xreg) a : sexpr :=
    oper (Op.Ocmp c) (List.mapr, Reg (f r)) a).

  Definition addr {X} p (f: Xreg) a : sexpr :=
    oper (Op.Olea p) (List.mapr, Reg (f r)) a).

  Set Elimination Schemes.
  Inductive assertion : Type :=
  | True
  | Assert `(sexpr)
  | AssertEQ (_ _: sexpr)
  | Implies (_ _: assertion)
  | Negate `(assertion)
  | ForAll `(reg) `(assertion)
  .

  Global Instance assertion_eq_dec : EqDec assertion.
Proof.
    intros a. elim a.
    - intros (); [ left; reflexivity | .. ]; right; abstract congruence.
    - intros s (); try (right; abstract congruence).
      intros s'. case (eq_dec s s'). intros EQ. left. exact (f_equal Assert EQ).
      intros NE. right. abstract congruence.
    - intros sl sr (); try (right; abstract congruence).
      intros sl' sr'.
      case (eq_dec sl sl'). 2: intros NE; right; abstract congruence. intros EQl.
      case (eq_dec sr sr'). 2: intros NE; right; abstract congruence. intros EQr.
      left. exact (f_equal2 AssertEQ EQl EQr).
    - intros p IHp q IHq.
      intros (); try (right; abstract congruence).
      intros p' q'.
      case (IHp p'). 2: intros NE; right; abstract congruence. intros EQp.
      case (IHq q'). 2: intros NE; right; abstract congruence. intros EQq.
      left. exact (f_equal2 Implies EQp EQq).
    - intros b IH.
      intros (); try (right; abstract congruence).
      intros b'.
      case (IH b'). 2: intros NE; right; abstract congruence. intros EQ.
      left. exact (f_equal Negate EQ).
    - intros r p IHp.
      intros (); try (right; abstract congruence).
      intros r' p'.
      case (eq_dec r r'). 2: intros NE; right; abstract congruence. intros EQr.
      case (IHp p'). 2: intros NE; right; abstract congruence. intros EQp.
      left. exact (f_equal2 ForAll EQr EQp).
  Defined.
  
  Fixpoint is_free (x: reg) (e: sexpr) {struct e} : bool :=
    match e with
    | Reg r => reg_dec r x
    | Name _ => false
    | Oper _ args => list_exists (is_free x) args
    end.

  Fixpoint is_free' (x: reg) (a: assertion) : bool :=
    match a with
    | True => false
    | Assert e => is_free x e
    | AssertEQ e e' => if is_free x e then true else is_free x e'
    | Implies p q => if is_free' x p then true else is_free' x q
    | Negate p => is_free' x p
    | ForAll x' p => if reg_dec x' x then false else is_free' x p
    end.

  Variable (reg_max: regregreg).

  Fixpoint sexpr_max_reg (m: reg) (e: sexpr) : reg :=
    match e with
    | Reg r => reg_max r m
    | Name _ => m
    | Oper _ args => List.fold_left sexpr_max_reg args m
    end.

  Fixpoint assertion_max_reg (m: reg) (a: assertion) : reg :=
    match a with
    | True => m
    | Assert e => sexpr_max_reg m e
    | AssertEQ x y => sexpr_max_reg (sexpr_max_reg m x) y
    | Implies p q => assertion_max_reg (assertion_max_reg m p) q
    | Negate p => assertion_max_reg m p
    | ForAll x p => assertion_max_reg (reg_max x m) p
    end.

  Lemma pos_max_le : ∀ x y z, Pos.le z xPos.le z yPos.le z (Pos.max x y).
Proof.
clear. intros. Psatz.lia. Qed.

  Lemma pos_succ_le_ne : ∀ x y, Pos.le x yxPos.succ y.
Proof.
clear. intros. Psatz.lia. Qed.

  Variable reg_succ: regreg.
  Variable (reg_le: regregProp).
  Hypothesis reg_le_refl : ∀ x, reg_le x x.
  Hypothesis reg_le_trans : ∀ x y z, reg_le x yreg_le y zreg_le x z.
  Hypothesis reg_max_le : ∀ x y z, reg_le z xreg_le z yreg_le z (reg_max x y).
  Hypothesis reg_succ_le_ne : ∀ x y, reg_le x yxreg_succ y.

  Lemma sexpr_max_reg_le e :
    ∀ r x, is_free x ereg_le x rreg_le x (sexpr_max_reg r e).
Proof.
    elim e; clear e.
    - intros r' r x []; simpl.
      + case reg_dec. intros -> _. apply reg_max_le; left; apply reg_le_refl.
        intros ? H; exact (false_not_true H _).
      + intros H. apply reg_max_le; right; exact H.
    - intros ? r x []. intros H; exact (false_not_true H _). exact id.
    - intros op args IH r x; revert r IH.
      elim args; clear args.
      intros r _ []. intros H; exact (false_not_true H _). exact id.
      intros a args IH r Hargs []; simpl in *.
      + destruct (is_free x a) eqn: Hfree; auto.
      + auto.
  Qed.
  Arguments sexpr_max_reg_le : clear implicits.

  Lemma assertion_max_reg_le a :
    ∀ r x, is_free' x areg_le x rreg_le x (assertion_max_reg r a).
Proof.
    elim a; clear a.
    - intros r x []. exactH, false_not_true H _). exact id.
    - intros e r x []; simpl; intros H; apply sexpr_max_reg_le; auto.
    - intros p q r x []; simpl.
      + destruct (is_free _ p) eqn: Hfree.
        * intros _. apply sexpr_max_reg_le. right. apply sexpr_max_reg_le. left. exact Hfree.
        * intros Hfree'. apply sexpr_max_reg_le. left. exact Hfree'.
      + intros H. apply sexpr_max_reg_le. right. apply sexpr_max_reg_le. right. exact H.
    - intros a Ha a' Ha' r x []; simpl.
      + destruct (is_free' _ a) eqn: Hfree; auto.
      + intros H. apply Ha'. right. apply Ha. right. exact H.
    - intros p IH r x []; simpl; intros H; apply IH; auto.
    - intros n p IH r x []; simpl.
      + case reg_dec. exact_ H, false_not_true H _). auto.
      + auto.
  Qed.
  Arguments assertion_max_reg_le : clear implicits.

  Definition fresh (r : reg) (e : sexpr) (a: assertion) : reg :=
    reg_succ (assertion_max_reg (sexpr_max_reg r e) a).

  Lemma fresh_correct r e a :
    let x := fresh r e a in
    xris_free x e = falseis_free' x a = false.
Proof.
    refine (conj _ (conj _ _)).
    - apply not_eq_sym, reg_succ_le_ne, assertion_max_reg_le. right. apply sexpr_max_reg_le. right. apply reg_le_refl.
    - destruct (is_free _ _) eqn: Hf; auto.
      exfalso.
      unfold fresh in *.
      pose proofq, sexpr_max_reg_le _ q _ (or_introl Hf)) as H.
      eapplyq z Q, reg_succ_le_ne (reg_le_trans (z := z) (H q) Q)).
      2: f_equal.
      apply assertion_max_reg_le. right. apply reg_le_refl.
    - destruct (is_free' _ _) eqn: Hf; auto.
      exfalso.
      unfold fresh in *.
      pose proofq, assertion_max_reg_le _ q _ (or_introl Hf)) as H.
      eapplyq, reg_succ_le_ne (H q)).
      reflexivity.
  Defined.
  Opaque fresh.

  Fixpoint assign_measure (a: assertion) : nat :=
    match a with
    | True
    | Assert _
    | AssertEQ _ _ => O
    | Implies p q => S (Peano.max (assign_measure p) (assign_measure q))
    | ForAll _ p
    | Negate p => S (assign_measure p)
    end.

  Definition assign_rel x y : Prop :=
    (assign_measure x < assign_measure y)%nat.

  Definition assign_rel_w x y : bool :=
    NPeano.leb (assign_measure y) (assign_measure x).

  Remark assign_rel_wf : well_founded assign_rel.
Proof.
    exact (well_founded_lt_compat _ assign_measure assign_relx y, id)).
  Qed.

  Fixpoint assign (r: reg) (n: sexpr) (a: assertion) (H: Acc assign_rel a) {struct H} : sig (assign_rel_w a).
  refine (
    let 'Acc_intro H'' := H in
    match a return (∀ y : assertion, assign_rel y aAcc assign_rel y) → sig (assign_rel_w a) with
    | True => λ H', exist _ True _
    | Assert e => λ H', exist _ (Assert (subst_reg r n e)) _
    | AssertEQ x y => λ H', exist _ (AssertEQ (subst_reg r n x) (subst_reg r n y)) _
    | Implies p q =>
      λ H',
      let 'exist p' Hp' := assign r n p _ in
      let 'exist q' Hq' := assign r n q _ in
      exist _ (Implies p' q') _
    | Negate p =>
      λ H',
      let 'exist p' Hp' := assign r n p _ in
      exist _ (Negate p') _
    | ForAll x p =>
      λ H',
      if reg_dec x r
      then exist _ (ForAll x p) _
      else
        if is_free x n
        then
          let x' := fresh r n p in
          let 'exist p' Hp' := assign x (Reg x') p _ in
          let 'exist q Hq := assign r n p' _ in
          exist _ (ForAll x' q) _
        else
          let 'exist q Hq := assign r n p _ in
          exist _ (ForAll x q) _
    end H'');
    try exact eq_refl.
Proof.
    - apply H', le_n_S, Max.le_max_l.
    - apply H', le_n_S, Max.le_max_r.
    - apply NPeano.leb_le, le_n_S, NPeano.Nat.max_le_compat.
      apply NPeano.leb_le, Hp'.
      apply NPeano.leb_le, Hq'.
    - apply H', le_refl.
    - exact Hp'.
    - apply NPeano.leb_le, le_refl.
    - apply H', lt_n_Sn.
    - apply H', le_lt_n_Sm, NPeano.leb_le, Hp'.
    - apply NPeano.leb_le, le_n_S.
      etransitivity; apply NPeano.leb_le; eassumption.
    - apply H', lt_n_Sn.
    - apply NPeano.leb_le, le_n_S, NPeano.leb_le, Hq.
  Defined.

  Definition assign' r n a :=
    if is_free' r a
    then proj1_sig (@assign r n a (assign_rel_wf a))
    else a.

  Definition assert_eq_reg (p q: reg) : assertion :=
    AssertEQ (Reg p) (Reg q).

  Definition boolexpr c x :=
      let cnz := Op.Ccompuimm Cne Int.zero in
      if Op.eq_condition c cnz
      then x
      else oper (Op.Ocmp cnz) (x :: nil).

  Definition assert_iff (x x': sexpr) : option assertion :=
    match x, x' with
    | Oper (Op.Ocmp c) _, Oper (Op.Ocmp c') _ =>
      Some (AssertEQ (boolexpr c x) (boolexpr c' x'))
    | _, _ => None
    end.

  Context (find_symbol: identoption block) (sp: val).
  Let env : Type := regval.

  Section EVAL.
  Context (valid_pointer : blockZbool).
  Context (ε: env).

  Fixpoint eval_sexpr (e: sexpr) : option val :=
    match e with
    | Reg x => Somex)
    | Name n =>
      do b <- find_symbol n;
      Some (Vptr b Int.zero)
    | Oper op args =>
      do vargs <- map_o eval_sexpr args;
      eval_operation find_symbol sp valid_pointer op vargs
    end.

  Lemma oper_eval op args :
    eval_sexpr (oper op args) = eval_sexpr (Oper op args).
Proof.
    elim op; clear op; try reflexivity;
    destruct args as [ | α args ]; try reflexivity;
    destruct args as [ | β args ]; try reflexivity;
    simpl.
    - case (eval_sexpr _); intros; reflexivity.
    - case β; try reflexivity.
      intros (); try reflexivity.
      intros n [ | ? ? ]. 2: reflexivity.
      destruct args as [ | ? ? ]. 2: reflexivity.
      simpl. case eval_sexpr; try reflexivity.
      intros v. simpl.
      now rewrite <- Val.sub_opp_add, Int.neg_involutive.
  Qed.

  Lemma eval_boolexpr {X} cond (f: X_) args :
    eval_sexpr (boolexpr cond (comp cond f args)) = Some (Val.of_optbool (eval_condition valid_pointer cond (map ε (map f args)))).
Proof.
    assert (map_o eval_sexpr (mapx, Reg (f x)) args) = Some (map ε (map f args))) as E.
    {
      elim args; clear args.
      reflexivity.
      intros x args IH. simpl. rewrite IH. reflexivity.
    }
    unfold boolexpr.
    case Op.eq_condition.
    - intros ->. simpl. rewrite E. reflexivity.
    - intros NE.
      rewrite oper_eval. simpl. rewrite E. simpl.
      case (eval_condition); try intros (); reflexivity.
  Qed.


  End EVAL.

  Lemma eval_condition_m vp' vp cond args :
    (∀ b o, vp b o = vp' b o) →
    eval_condition vp cond args = eval_condition vp' cond args.
Proof.
    intros E.
    destruct cond; try reflexivity;
    destruct args as [ | α args ]; try reflexivity;
    destruct args as [ | β args ]; try reflexivity;
    try (destruct args as [ | γ args ]; try reflexivity);
    destruct α; try reflexivity;
    try (destruct β; try reflexivity);
    simpl; rewrite ! E; reflexivity.
  Qed.

  Lemma eval_operation_m vp' vp op args :
    (∀ b o, vp b o = vp' b o) →
    eval_operation find_symbol sp vp op args = eval_operation find_symbol sp vp' op args.
Proof.
    intros E.
    destruct op; try reflexivity.
    simpl. f_equal. f_equal. apply eval_condition_m, E.
  Qed.

  Fixpoint eval_assertion vp (ε: env) (a: assertion) : Prop :=
    match a with
    | True => Logic.True
    | Assert b =>
      eval_sexpr vp ε b = Some Vtrue
    | AssertEQ x x' => eval_sexpr vp ε x = eval_sexpr vp ε x'
    | Implies x y => eval_assertion vp ε xeval_assertion vp ε y
    | Negate x => ¬ eval_assertion vp ε x
    | ForAll k x => ∀ v, eval_assertion vp (ε +[ k => v ]) x
    end.


  Fixpoint simplify_measure (a: assertion) : nat :=
    match a with
    | True
    | Assert _
    | AssertEQ _ _ => O
    | ForAll _ p
    | Implies _ p => S (simplify_measure p)
    | Negate p => simplify_measure p
    end.

  Fixpoint simplify_measure_assign r e p H :
    simplify_measure (proj1_sig (@assign r e p H)) = simplify_measure p.
Proof.
    Local Ltac q rec :=
      repeat match goal with |- appcontext[ @assign ?r ?e ?p ?H] =>
                      generalize (rec r e _ H);
                      destruct (assign _ _ H) end.
    destruct H as [H'].
    destruct p; try reflexivity.
    - unfold simplify_measure; fold simplify_measure.
      simpl; fold assign_measure.
      q simplify_measure_assign; simpl; congruence.
    - unfold simplify_measure; fold simplify_measure.
      simpl; fold assign_measure.
      q simplify_measure_assign; simpl; congruence.
    - unfold simplify_measure; fold simplify_measure.
      simpl; fold assign_measure.
      case reg_dec. intros _; reflexivity.
      intros NE.
      case (is_free _ _);
      q simplify_measure_assign; simpl; congruence.
  Qed.

  Corollary simplify_measure_assign' r e p :
    simplify_measure (assign' r e p) = simplify_measure p.
Proof.
    unfold assign'.
    case is_free'.
    exact (simplify_measure_assign _ _ _).
    exact eq_refl.
  Qed.

  Function simplify (a: assertion) {measure simplify_measure a} : assertion :=
    match a with
    | Assert e => a
    | AssertEQ x y => if sexpr_dec x y then True else a
    | Implies (AssertEQ (Reg r) e) p => simplify (assign' r e p)
    | Implies _ p => match simplify p with True => True | _ => a end
    | ForAll _ p => match simplify p with True => True | _ => a end
    | _ => a
    end.
Proof.
    intros; apply lt_n_Sn.
    intros; apply lt_n_Sn.
    intros; simpl; rewrite simplify_measure_assign'; apply lt_n_Sn.
    intros; apply lt_n_Sn.
    intros; apply lt_n_Sn.
    intros; apply lt_n_Sn.
    intros; apply lt_n_Sn.
    intros; apply lt_n_Sn.
    intros; apply lt_n_Sn.
  Defined.

  Lemma eval_sexpr_m {ε ε': env} :
    (∀ r, ε r = ε' r) →
    ∀ vp e, eval_sexpr vp ε e = eval_sexpr vp ε' e.
Proof.
    intros EXT vp e.
    elim e; clear e; try reflexivity.
    - intros r. apply (f_equal Some), (EXT r).
    - intros op args IH. simpl.
      cut (map_o (eval_sexpr vp ε) args = map_o (eval_sexpr vp ε') args).
      intros ->. reflexivity.
      revert IH. clear.
      elim args; clear args.
      reflexivity.
      intros a args IH EXT.
      simpl.
      rewrite (EXT _ (or_introl _ eq_refl)).
      case eval_sexpr. 2: reflexivity.
      intros v. rewrite IH. reflexivity.
      intros a' Ha'. apply EXT, or_intror, Ha'.
  Qed.

  Lemma eval_assertion_m {ε ε': env} :
    (∀ r, ε r = ε' r) →
    ∀ vp p, eval_assertion vp ε peval_assertion vp ε' p.
Proof.
    intros EXT vp p.
    revert ε ε' EXT.
    elim p; clear p; try reflexivity.
    - intros e. simpl. intros ε ε' EXT. rewrite (eval_sexpr_m EXT). reflexivity.
    - intros e e'. simpl. intros ε ε' EXT. rewrite ! (eval_sexpr_m EXT). reflexivity.
    - intros a Ha a' Ha'. simpl. intros ε ε' EXT. rewrite (Ha ε ε' EXT), (Ha' ε ε' EXT). reflexivity.
    - intros a Ha. simpl. intros ε ε' EXT. rewrite (Ha ε ε' EXT). reflexivity.
    - intros r a Ha. simpl. intros ε ε' EXT.
      assert (∀ v r', ε+[r => v] r' = ε'+[r => v] r') as EXT'.
      { intros v r'. unfold upd. case eq_dec. reflexivity. intros _. apply EXT. }
      split; intros X v; generalize (X v); rewrite (Ha _ _ (EXT' v)); exact id.
  Qed.

  Lemma map_simpl {A B} (f: AB) (m: list A) :
    map f m = match m with nil => nil | a :: m' => f a :: map f m' end.
Proof.
case m; reflexivity. Qed.

  Lemma map_o_simpl {A B} (f: Aoption B) (m: list A) :
    map_o f m = match m with nil => Some nil | a :: m' => do b <- f a; do bs <- map_o f m'; Some (b :: bs) end.
Proof.
case m; reflexivity. Qed.

  Lemma eval_subst_reg {vp ε e v} :
    eval_sexpr vp ε e = Some v
    ∀ r e',
    eval_sexpr vp ε (subst_reg r e e') = eval_sexpr vp (ε +[r => v]) e'.
Proof.
    intros Hv r e'; revert Hv.
    elim e'; clear e'.
    - intros r' Hv. simpl. unfold subst_reg. simpl. unfold upd, eq_dec. now case reg_dec.
    - easy.
    - intros op args IH Hv. unfold subst_reg. simpl.
      rewrite oper_eval. simpl.
      repeat
        match goal with |- appcontext[ map_o ?f ?x ] =>
                        let y := fresh "x" in set (y := map_o f x)
        end.
      cut (x = x0). intros ->; reflexivity.
      subst x x0.
      cut (∀ a, In a argseval_sexpr vp ε (subst_reg r e a) = eval_sexpr vp (ε +[r => v]) a).
      2: eauto.
      clear. elim args; clear args.
      reflexivity.
      intros a args IH Hargs.
      specialize (IHa' H, Hargs a' (or_intror _ H))).
      rewrite map_simpl.
      rewrite map_o_simpl. rewrite IH. clear IH.
      rewrite (map_o_simpl _ (a :: _)).
      rewrite <- (Hargs _ (or_introl _ eq_refl)). clear Hargs.
      reflexivity.
  Qed.

  Lemma upd_stutter {A B} `{EqDec A} (f: AB) x v v' y :
    f +[x => v'] +[x => v] y = f +[x => v] y.
Proof.
unfold upd. case eq_dec; auto. Qed.

  Lemma upd_comm {A B} `{EqDec A} (f: AB) x v x' v' :
    xx' →
    ∀ y,
    f +[x' => v'] +[x => v] y = f +[x => v] +[x' => v'] y.
Proof.
intros NE y. unfold upd. case eq_dec; auto. intros ->. case eq_dec; congruence. Qed.

  Lemma eval_sexpr_is_free x e :
    is_free x e = false
    ∀ vp ε v, eval_sexpr vp (ε +[x => v]) e = eval_sexpr vp ε e.
Proof.
    intros H vp ε v.
    revert H.
    elim e; clear e; simpl.
    - intros r. unfold upd, eq_dec. case reg_dec. simpl. intros; eq_some_inv. reflexivity.
    - reflexivity.
    - intros op args IH H.
      cut (map_o (eval_sexpr vp (ε +[x => v])) args = map_o (eval_sexpr vp ε) args).
      intros ->; reflexivity.
      revert IH H. elim args; clear args.
      reflexivity.
      intros a args IH Hargs.
      simpl.
      destruct (is_free _ _) eqn: Hfree. intro; eq_some_inv.
      intros H.
      rewrite (Hargs _ (or_introl _ eq_refl) Hfree).
      case (eval_sexpr _ _ a). 2: reflexivity.
      intros y.
      specialize (IHa IN F, Hargs a (or_intror _ IN) F) H).
      clear H Hargs.
      rewrite IH. reflexivity.
  Qed.

  Lemma eval_assertion_is_free x a :
    is_free' x a = false
    ∀ vp ε v, eval_assertion vp (ε +[x => v]) aeval_assertion vp ε a.
Proof.
    elim a; clear a.
    - reflexivity.
    - intros r H vp ε v. simpl. rewrite eval_sexpr_is_free. reflexivity. exact H.
    - intros e e' H vp ε v. simpl in *.
      assert (is_free x e = false) as Hx.
      { revert H. case is_free. exact id. reflexivity. }
      rewrite Hx in H.
      rewrite ! eval_sexpr_is_free; auto. reflexivity.
    - intros a Ha a' Ha' H vp ε v.
      simpl in *.
      assert (is_free' x a = false) as Hx.
      { revert H. case is_free'. exact id. reflexivity. }
      rewrite Hx in H.
      rewrite Ha, Ha'. reflexivity. easy. easy.
    - intros p IH H vp ε v.
      simpl. rewrite IH. reflexivity. exact H.
    - intros r p IH H vp ε v.
      simpl in *.
      destruct reg_dec. subst.
      split; intros X v'; generalize (X v'); apply eval_assertion_m;
      intros r; rewrite upd_stutter; reflexivity.
      specialize (IH H vp).
      split; intros X v'; specialize (X v').
      apply eval_assertion_m in X. 2: apply upd_comm; auto.
      apply IH in X. exact X.
      eapply eval_assertion_m. apply upd_comm; auto.
      apply IH, X.
  Qed.

  Section S.
  Context (vp : blockZbool).
  Fixpoint eval_assertion_assign r e p H :
    ∀ ε v,
      eval_sexpr vp ε e = Some v
      (eval_assertion vp ε (proj1_sig (@assign r e p H)) ↔ eval_assertion vp (ε+[ r => v]) p).
Proof.
    destruct H as [H'].
    destruct p.
    - simpl; reflexivity.
    - simpl.
      intros ε v Hv. now rewrite (eval_subst_reg Hv).
    - intros ε v Hv. simpl. now rewrite ! (eval_subst_reg Hv).
    - intros ε v Hv.
      simpl.
      q eval_assertion_assign. simpl.
      intros H H0.
      specialize (H ε _ Hv).
      specialize (H0 ε _ Hv).
      tauto.
    - intros ε v Hv. simpl. q eval_assertion_assign. simpl. intros H. specialize (H _ _ Hv). tauto.
    - intros ε v Hv. simpl.
      case reg_dec.
      + intros ->. simpl.
        split; intros X v'; eapply eval_assertion_m.
        apply upd_stutter. apply X.
        symmetry. apply upd_stutter. apply X.
      + intros NE.
        destruct (is_free _ _) eqn: Hfree;
          q eval_assertion_assign; simpl;
          clear eval_assertion_assign.
        {
          intros H0 H1.
          specialize (λ ε, H1 ε _ eq_refl).
          set (x' := fresh r e p). fold x' in H1.
          destruct (fresh_correct r e p) as (Hx'r & Hx'e & Hx'p).
          split; intros X v'; specialize (X v').
          * rewrite <- (eval_assertion_is_free x' _ Hx'p vp _ v').
            refine
              (proj2 (@eval_assertion_m _ ((ε+[ r => v ] +[x' => v']) +[ H => (ε+[ r => v ] +[x' => v']) x' ]) _ _ _) _).
            { clear. intros y. unfold upd. repeat case eq_dec; congruence. }
            apply H1.
            eapply eval_assertion_m. apply upd_comm, Hx'r.
            apply H0. rewrite eval_sexpr_is_free; eauto. exact X.
          * rewrite H0. 2: rewrite eval_sexpr_is_free; eauto. clear H0 Hx'e.
            apply H1. clear H1.
            rewrite upd_o. 2: auto. rewrite upd_s.
            eapply eval_assertion_m.
            2: apply (eval_assertion_is_free x'); eassumption.
            clear -NE Hx'r. fold x' in Hx'r.
            intros y. unfold upd. repeat case eq_dec; reflexivity || congruence.
        }
        {
          intros H0.
          split; intros X v'; specialize (X v').
          revert X. rewrite H0. apply eval_assertion_m.
          apply upd_comm. exact NE.
          rewrite eval_sexpr_is_free; eassumption.
          revert X. rewrite H0. apply eval_assertion_m.
          apply upd_comm. auto.
          rewrite eval_sexpr_is_free; eassumption.
        }
  Qed.
  End S.

  Corollary eval_assertion_assign' vp ε r e p :
    ∀ v,
      eval_sexpr vp ε e = Some v
      (eval_assertion vp ε (assign' r e p) ↔ eval_assertion vp (ε+[ r => v]) p).
Proof.
    unfold assign'.
    destruct (is_free' _ _) eqn: Hfree.
    apply eval_assertion_assign.
    intros v _. symmetry. apply eval_assertion_is_free, Hfree.
  Qed.

  Lemma simplify_correct a :
    ∀ vp ε, eval_assertion vp ε (simplify a) → eval_assertion vp ε a.
Proof.
    functional induction (simplify a).
 - intros vp ε _. exact (trivial_correct _ e1 vp ε). *)    - exact_ _, id).
    - intros vp ε _. reflexivity.
    - exact_ _, id).
    - intros vp ε H.
      specialize (IHa0 _ _ H). clear H.
      intros He.
      fold (eval_assertion).
      simpl in He.
      generalize (proj1 (eval_assertion_assign' vp ε r e p (eq_sym He)) IHa0).
      apply eval_assertion_m.
      intros r'. unfold upd. case eq_dec; congruence.
    - intros vp ε _ Hx. fold eval_assertion in *.
      apply IHa0. rewrite e0. exact I.
    - exact_ _, id).
    - intros vp ε _ v. fold eval_assertion. apply IHa0. rewrite e0. exact I.
    - exact_ _, id).
    - exact_ _, id).
  Qed.

End REGISTER.

Arguments Reg {reg} _.
Arguments Name {reg} _.
Arguments True {reg}.