Module IdealEnvToMachineEnv


Require Import Utf8.
Require Import Coqlib FastArith.
Require Import AbIdealEnv IdealExpr.
Require Import AbMachineEnv.
Require Import Integers.
Require Import FloatLib FloatIntervals ZIntervals IdealIntervals.
Require Import ZArith.
Require Import Cminor Ctypes.
Require Import AdomLib.
Require Import Maps ShareTree TreeAl Util Hash ToString Sets.
Require Import FactMsgHelpers.

Open Scope Z_scope.

Definition var0 := Cells.cell.

Section Functor.
  Context (abt abt_iter:Type) {IdD : ab_ideal_env_nochan abt abt_iter}.

  Inductive related : mach_num -> ideal_num -> Prop :=
  | rel_int : forall x, related (MNint (Int.repr x)) (INz x)
  | rel_long : forall x, related (MNint64 (Int64.repr x)) (INz x)
  | rel_float : forall x, related (MNfloat x) (INf x)
  | rel_single : forall x, related (MNsingle x) (INf (Float.of_single x)).

  Hint Constructors related.

  Lemma related_unsigned:
    forall x, related (MNint x) (INz (Int.unsigned x)).
Proof.
    intros. rewrite <- Int.repr_unsigned with (i:=x) at 1. constructor.
  Qed.

  Lemma related_signed:
    forall x, related (MNint x) (INz (Int.signed x)).
Proof.
    intros. rewrite <- Int.repr_signed with (i:=x) at 1. constructor.
  Qed.

  Lemma related_unsigned_long:
    forall x, related (MNint64 x) (INz (Int64.unsigned x)).
Proof.
    intros. rewrite <- Int64.repr_unsigned with (i:=x) at 1. constructor.
  Qed.

  Lemma related_signed_long:
    forall x, related (MNint64 x) (INz (Int64.signed x)).
Proof.
    intros. rewrite <- Int64.repr_signed with (i:=x) at 1. constructor.
  Qed.

  Instance gamma_mach : gamma_op abt (var0 -> mach_num) :=
    fun v ρ =>
      ∃ ρ':var -> ideal_num,
        ρ' ∈ γ v ∧ ∀ id, relatedid) (ρ' (Real id)).

  Instance gamma_mach_iter : gamma_op abt_iter (var0 -> mach_num) :=
    fun v ρ =>
      ∃ ρ':var -> ideal_num,
        ρ' ∈ γ v ∧ ∀ id, relatedid) (ρ' (Real id)).

  Definition typesmap := Cells.ACTree.t (option signedness * mach_num_ty).

  Instance gamma_typs : gamma_op typesmap (var0 -> mach_num) :=
    fun typs ρ =>
      ∀ v t, Cells.ACTree.get v typs = Some t ->
             match snd t with MNTfloat | MNTsingle => fst t = None | _ => True end /\
             ρ v ∈ γ (snd t).

  Record ghosts_info : Type := {
    next_g: positive;
    typs_g: PTree.t ideal_num_ty
  }.

  Definition init_ghost_info := {| next_g := 1; typs_g := PTree.empty _ |}.

  Definition compat_gh (gi:ghosts_info) (ρ1 ρ2:var -> ideal_num) :=
    (∀ id, ρ1 (Real id) = ρ2 (Real id)) /\
    (∀ g, (g < gi.(next_g))%positive -> ρ1 (Ghost g) = ρ2 (Ghost g)).

  Lemma compat_gh_refl:
    ∀ next_g ρ, compat_gh next_g ρ ρ.
Proof.
    intros. split; auto.
  Qed.
  Hint Resolve compat_gh_refl.

  Lemma compat_gh_trans:
    ∀ gi1 gi2 ρ1 ρ2 ρ3,
      (gi1.(next_g) <= gi2.(next_g))%positive ->
      compat_gh gi1 ρ1 ρ2 ->
      compat_gh gi2 ρ2 ρ3 ->
      compat_gh gi1 ρ1 ρ3.
Proof.
    split; intros; (etransitivity; [apply H0|apply H1]).
    auto. Psatz.lia.
  Qed.
  Hint Resolve compat_gh_trans.

  Instance gamma_gh : gamma_op ghosts_info (var -> ideal_num) :=
    fun gi ρ =>
      ∀ p ty, PTree.get p gi.(typs_g) = Some ty -> ρ (Ghost p) ∈ γ ty.

  Lemma gamma_gh_init:
    ∀ ρ, ρ ∈ γ init_ghost_info.
Proof.
    repeat intro. rewrite PTree.gempty in H. discriminate.
  Qed.
  Hint Resolve gamma_gh_init.

  Definition signed_itv := ZITV Int.min_signed Int.max_signed.

  Lemma signed_itv_correct:
    ∀ x, Int.signed x ∈ γ signed_itv.
Proof.
    simpl. apply Int.signed_range.
  Qed.

  Definition unsigned_itv :=
    ZITV F0 Int.max_unsigned.

  Lemma unsigned_itv_correct:
    ∀ x, Int.unsigned x ∈ γ unsigned_itv.
Proof.
    intros. simpl. apply Int.unsigned_range_2.
  Qed.

  Definition signed64_itv :=
    ZITV Int64.min_signed Int64.max_signed.

  Lemma signed64_itv_correct:
    ∀ x, Int64.signed x ∈ γ signed64_itv.
Proof.
    simpl. apply Int64.signed_range.
  Qed.

  Definition unsigned64_itv :=
    ZITV F0 Int64.max_unsigned.

  Lemma unsigned64_itv_correct:
    ∀ x, Int64.unsigned x ∈ γ unsigned64_itv.
Proof.
    intros. simpl. apply Int64.unsigned_range_2.
  Qed.

  Definition alias_sz : Z := 100000.
  Opaque alias_sz.

  Definition simplify_expr_alias (v:abt+⊥) (e:iexpr) (gi:ghosts_info):
    iexpr*abt+⊥*ghosts_info :=
    let e := simplify_expr e in
    if Z.leb (iexpr_sz e) alias_sz then (e, v, gi)
    else
      (IEvar (iexpr_ty e) (Ghost gi.(next_g)),
       do v <- v;
       idnc_assign (Ghost gi.(next_g)) e v,
       {| next_g := Pos.succ gi.(next_g);
          typs_g := PTree.set gi.(next_g) (iexpr_ty e) gi.(typs_g) |}).

  Lemma simplify_expr_alias_correct:
    ∀ v e gi,
    ∀ e' v' gi', simplify_expr_alias v e gi = (e', v', gi') ->
    ∀ ρ x, ρ ∈ γ v -> ρ ∈ γ gi ->
          (∀ ρ', compat_gh gi ρ ρ' -> eval_iexpr ρ' e x ) ->
    ∃ ρ', compat_gh gi ρ ρ' /\ ρ' ∈ γ v' /\ ρ' ∈ γ gi' /\
          (∀ ρ'', compat_gh gi' ρ' ρ'' -> eval_iexpr ρ'' e' x).
Proof.
    unfold simplify_expr_alias. intros.
    pose proof simplify_expr_correct e.
    destruct Z.leb; inv H; eauto 10.
    eexists (ρ+[Ghost gi.(next_g) => x]). split; [|split;[|split]].
    - split. symmetry. apply upd_o. discriminate.
      intros. unfold upd. destruct @eq_dec. inv e0. Psatz.lia. auto.
    - eapply botbind_spec with ρ. 2:now auto. intros.
      eapply idnc_assign_correct; now auto.
    - repeat intro. unfold upd. simpl in H. rewrite PTree.gsspec in H.
      destruct @eq_dec, peq; try congruence. 2:now apply H1, H.
      inv e0; inv H. eapply iexpr_ty_correct, H3; eauto.
    - constructor. rewrite <- (proj2 H) by (simpl; Psatz.lia). apply upd_s.
      eapply iexpr_ty_correct, H3, H2. eauto.
  Qed.

  Lemma simplify_expr_alias_monotone:
    ∀ v e gi,
    ∀ e' v' gi',
      simplify_expr_alias v e gi = (e', v', gi') ->
      (gi.(next_g) <= gi'.(next_g))%positive.
Proof.
    unfold simplify_expr_alias. intros.
    destruct Z.leb; inv H; simpl in *; Psatz.lia.
  Qed.

  Definition itv_normalizer (i:zitv+⊤+⊥) (min max:Zfast) (logdiv:Zfast): option Zfast :=
    match i with
    | NotBot (Just (ZITV a b)) =>
      if Zfasteqb (Zfastshr (Zfastsub b a) logdiv) F0 then
        let q := Zfastshr (Zfastsub a min) logdiv in
        if Zfastleb (Zfastsub b (Zfastshl q logdiv)) max
        then Some q
        else None
      else None
    | _ => None
    end.

  Lemma itv_normalizer_correct:
    ∀ i (min max logdiv:Z), 0 < logdiv ->
    ∀ q, itv_normalizer i min max logdiv = Some q ->
    ∀ n, n ∈ γ i -> min <= n - q * 2 ^ logdiv <= max.
Proof.
    intros [|[|[[a] [b]]]] min max logdiv Hlogdiv q Hq n Hn; try discriminate Hq;
    simpl in *; fastunwrap.
    rewrite Z.shiftl_mul_pow2, !Z.shiftr_div_pow2 in Hq by omega.
    simpl in Hq. destruct (Z.eqb_spec ((b-a) / (2 ^ logdiv)) 0). 2:discriminate.
    match goal with
      | H:context [Zle_bool ?x ?y] |- _ =>
        assert (HZle:= Zle_cases x y); destruct (Zle_bool x y)
    end; inv Hq.

    assert (Hlt:2^logdiv > 0). apply Z.lt_gt, Z.pow_pos_nonneg; omega.
    assert (Hmodeq:=Zmod_eq (a-min) _ Hlt).
    assert (Hmodlt:=Z_mod_lt (a-min) _ Hlt).
    destruct Hn. fastunwrap. omega.
  Qed.

  Definition normalize_expr (v:abt+⊥) (e:iexpr) (min max:Zfast)
                            (logdiv:Zfast) (gi:ghosts_info):
    iexpr*abt+⊥*ghosts_info*bool :=
    let '(e, v, gi) := simplify_expr_alias v e gi in
    match v with
    | NotBot v =>
      let itv :=
        do x <- (idnc_get_query_chan v).(AbIdealEnv.get_itv) e;
        in_z x
      in
      match itv_normalizer itv min max logdiv with
      | Some q =>
        let e :=
          if Zfasteqb q F0 then e
          else
            let shift := Zfastshl q logdiv in
            IEbinop IOsub e (IEconst (INz shift))
        in
        (e, NotBot v, gi, true)
      | None => (e, NotBot v, gi, false)
      end
    | Bot => (e, v, gi, false)
    end.

  Lemma normalize_expr_correct:
    ∀ v e (min max logdiv:Z) gi, 0 < logdiv ->
    ∀ e' v' gi' b, normalize_expr v e min max logdiv gi = (e', v', gi', b) ->
    ∀ ρ (x:Z), ρ ∈ γ v -> ρ ∈ γ gi ->
      (∀ ρ', compat_gh gi ρ ρ' -> eval_iexpr ρ' e (INz x)) ->
      ∃ ρ', compat_gh gi ρ ρ' /\ ρ' ∈ γ v' /\ ρ' ∈ γ gi' /\
        if b then
          ∃ (x':Z), min <= x' <= maxInt.eqmod (two_p logdiv) x x' /\
                (∀ ρ'', compat_gh gi' ρ' ρ'' -> eval_iexpr ρ'' e' (INz x'))
        else
          ∀ ρ'', compat_gh gi' ρ' ρ'' -> eval_iexpr ρ'' e' (INz x).
Proof.
    unfold normalize_expr.
    intros v e min max logdiv gi Hlogdiv e' v' gi' b Heq ρ x Hγ Hgi EVAL.
    pose proof simplify_expr_alias_correct v e gi.
    destruct simplify_expr_alias as [[? [|]] ?]. inv Heq. now auto.
    edestruct H as (ρ' & Hρρ' & Hρ' & Hgi' & EVAL'); eauto. clear H.
    assert (Hnorm:=itv_normalizer_correct (do x <- (idnc_get_query_chan x0).(AbIdealEnv.get_itv) i;
                                           in_z x) min max logdiv Hlogdiv).
    destruct v. contradiction.
    destruct itv_normalizer; inv Heq. 2:now eauto.
    eexists. split. eauto. split. auto. split. auto.
    eexists. split. apply Hnorm. auto.
    eapply botbind_spec; eauto. eapply AbIdealEnv.get_itv_correct; eauto.
    apply idnc_get_query_chan_correct; eauto.
    split. exists z. rewrite two_p_equiv. simpl. ring.
    destruct z. fastunwrap. simpl. destruct (Z.eqb_spec x2 0). subst.
    simpl. replace (x-0) with x by ring. auto.
    intros. econstructor; eauto.
    rewrite Z.shiftl_mul_pow2 by (zify; omega). constructor.
  Qed.

  Lemma normalize_expr_next_g_monotone:
    ∀ v e min max logdiv gi e' v' gi' b,
      normalize_expr v e min max logdiv gi = (e', v', gi', b) ->
      (gi.(next_g) <= gi'.(next_g))%positive.
Proof.
    intros. unfold normalize_expr in H.
    pose proof (simplify_expr_alias_monotone v e gi).
    destruct simplify_expr_alias as [[? []]?]. inv H. eauto.
    destruct itv_normalizer; inv H; eauto.
  Qed.

  Definition intrange := (Int.min_signed:Zfast, Int.max_signed:Zfast).
  Definition intrangeu := (F0:Zfast, Int.max_unsigned:Zfast).
  Definition intzwordsize : Zfast := Int.zwordsize.
  Definition normalize_expr_signedness (v:abt+⊥) (e:iexpr) (f:signedness) (gi:ghosts_info):
    iexpr * abt+⊥ * ghosts_info :=
    let '(min, max) :=
      match f with Signed => intrange | Unsigned => intrangeu end
    in
    match normalize_expr v e min max intzwordsize gi with
    | (e, v, ng, true) => (e, v, ng)
    | _ => (IEZitv (ZITV min max), v, gi)
    end.

  Lemma normalize_expr_signedness_correct:
    ∀ v e f,
    ∀ ρ (x:Z) gi, ρ ∈ γ v -> ρ ∈ γ gi ->
    (∀ ρ', compat_gh gi ρ ρ' -> eval_iexpr ρ' e (INz x)) ->
    ∀ e' v' gi', normalize_expr_signedness v e f gi = (e', v', gi') ->
    ∃ ρ', compat_gh gi ρ ρ' /\ ρ' ∈ γ v' /\ ρ' ∈ γ gi' /\
    ∃ (x':Z),
      x' ∈ γ (match f with Signed => signed_itv | Unsigned => unsigned_itv end) ∧
      Int.eqm x x' ∧
      (∀ ρ'', compat_gh gi' ρ' ρ'' -> eval_iexpr ρ'' e' (INz x')).
Proof.
    intros v e [] ρ x gi Hρ Hgi EVAL e' v' gi' EQ;
    unfold normalize_expr_signedness, intrange, intrangeu, intzwordsize in EQ.
    - destruct (normalize_expr v e Int.min_signed Int.max_signed Int.zwordsize gi) as [[[? ?]?]?]eqn:NORM.
      eapply normalize_expr_correct in NORM; eauto. 2:reflexivity.
      destruct b0; inv EQ. unfold Int.eqm. rewrite Int.modulus_power. apply NORM.
      eexists. split. eauto. split. eauto. split. auto.
      eexists. split. apply signed_itv_correct. split.
      eapply Int.eqm_trans, Int.eqm_sym, Int.eqm_signed_unsigned. apply Int.eqm_unsigned_repr.
      econstructor. apply signed_itv_correct.
    - destruct (normalize_expr v e F0 Int.max_unsigned Int.zwordsize gi) as [[[? ?]?]?]eqn:NORM.
      eapply normalize_expr_correct in NORM; eauto. 2:reflexivity.
      destruct b0; inv EQ. unfold Int.eqm. rewrite Int.modulus_power. apply NORM.
      eexists. split. eauto. split. eauto. split. auto.
      eexists. split. apply unsigned_itv_correct. split.
      apply Int.eqm_unsigned_repr.
      econstructor. apply unsigned_itv_correct.
  Qed.

  Lemma normalize_expr_signedness_next_g_monotone:
    ∀ v e f gi e' v' gi',
      normalize_expr_signedness v e f gi = (e', v', gi') ->
      (gi.(next_g) <= gi'.(next_g))%positive.
Proof.
    intros. unfold normalize_expr_signedness in H.
    match type of H with
    | (let '(_, _) := ?P in _) = _ => destruct P
    end.
    pose proof normalize_expr_next_g_monotone v e z z0 intzwordsize gi.
    destruct normalize_expr as [[[? ?]?][]]; inv H. eauto. Psatz.lia.
  Qed.

  Definition int64range := (Int64.min_signed:Zfast, Int64.max_signed:Zfast).
  Definition int64rangeu := (F0:Zfast, Int64.max_unsigned:Zfast).
  Definition int64zwordsize : Zfast := Int64.zwordsize.
  Definition normalize_expr_signedness64 (v:abt+⊥) (e:iexpr) (f:signedness) (gi:ghosts_info):
    iexpr * abt+⊥ * ghosts_info :=
    let '(min, max) :=
      match f with Signed => int64range | Unsigned => int64rangeu end
    in
    match normalize_expr v e min max int64zwordsize gi with
    | (e, v, ng, true) => (e, v, ng)
    | _ => (IEZitv (ZITV min max), v, gi)
    end.

  Lemma normalize_expr_signedness64_correct:
    ∀ v e f,
    ∀ ρ (x:Z) gi, ρ ∈ γ v -> ρ ∈ γ gi ->
              (∀ ρ', compat_gh gi ρ ρ' -> eval_iexpr ρ' e (INz x)) ->
    ∀ e' v' gi', normalize_expr_signedness64 v e f gi = (e', v', gi') ->
    ∃ ρ', compat_gh gi ρ ρ' /\ ρ' ∈ γ v' /\ ρ' ∈ γ gi' /\
    ∃ (x':Z),
      x' ∈ γ (match f with Signed => signed64_itv | Unsigned => unsigned64_itv end) ∧
      Int64.eqm x x' ∧
      (∀ ρ'', compat_gh gi' ρ' ρ'' -> eval_iexpr ρ'' e' (INz x')).
Proof.
    intros v e [] ρ x gi Hρ Hgi EVAL e' v' gi' EQ;
    unfold normalize_expr_signedness64, int64range, int64rangeu, int64zwordsize in EQ.
    - destruct (normalize_expr v e Int64.min_signed Int64.max_signed Int64.zwordsize gi) as [[[? ?]?]?]eqn:NORM.
      eapply normalize_expr_correct in NORM; eauto. 2:reflexivity.
      destruct b0; inv EQ. unfold Int64.eqm. rewrite Int64.modulus_power. apply NORM.
      eexists. split. eauto. split. eauto. split. auto.
      eexists. split. apply signed64_itv_correct. split.
      eapply Int64.eqm_trans, Int64.eqm_sym, Int64.eqm_signed_unsigned. apply Int64.eqm_unsigned_repr.
      econstructor. apply signed64_itv_correct.
    - destruct (normalize_expr v e F0 Int64.max_unsigned Int64.zwordsize gi) as [[[? ?]?]?]eqn:NORM.
      eapply normalize_expr_correct in NORM; eauto. 2:reflexivity.
      destruct b0; inv EQ. unfold Int64.eqm. rewrite Int64.modulus_power. apply NORM.
      eexists. split. eauto. split. eauto. split. auto.
      eexists. split. apply unsigned64_itv_correct. split.
      apply Int64.eqm_unsigned_repr.
      econstructor. apply unsigned64_itv_correct.
  Qed.

  Lemma normalize_expr_signedness64_next_g_monotone:
    ∀ v e f gi e' v' gi',
      normalize_expr_signedness64 v e f gi = (e', v', gi') ->
      (gi.(next_g) <= gi'.(next_g))%positive.
Proof.
    intros. unfold normalize_expr_signedness64 in H.
    match type of H with
    | (let '(_, _) := ?P in _) = _ => destruct P
    end.
    pose proof normalize_expr_next_g_monotone v e z z0 int64zwordsize gi.
    destruct normalize_expr as [[[? ?]?][]]; inv H. eauto. Psatz.lia.
  Qed.

  Definition assume_cmp (c:comparison) (e:iexpr) (z:Zfast) (v:abt+⊥): abt+⊥ :=
    do v <- v;
    idnc_assume (IEbinop (IOcmp c) e (IEconst (INz z))) true v.

  Lemma assume_cmp_correct:
    ∀ c e (x:Z) z ρ ab,
      ρ ∈ γ ab ->
      INz xeval_iexpr ρ e ->
      Zcmp c x z = true ->
      ρ ∈ γ (assume_cmp c e z ab).
Proof.
    intros. apply botbind_spec with ρ; auto.
    intros. apply idnc_assume_correct. auto.
    econstructor. eauto. constructor.
    replace (F1:Zfast) with (if Zcmp c x z return Zfast then F1 else F0). constructor.
    rewrite H1. auto.
  Qed.

  Definition check_finitef (e:iexpr) (v:abt+⊥): bool :=
    match v with
    | Bot => true
    | NotBot v =>
      is_bot (idnc_assume
                (IEbinop (IOcmpf Cgt) e (IEconst (INf (B754_infinity true)))) false v) &&
      is_bot (idnc_assume
                (IEbinop (IOcmpf Clt) e (IEconst (INf (B754_infinity false)))) false v)
    end.

  Lemma check_finitef_correct:
    ∀ e x ρ ab,
      check_finitef e ab = true ->
      ρ ∈ γ ab ->
      INf xeval_iexpr ρ e ->
      is_finite x = true.
Proof.
    intros. destruct ab. contradiction. simpl in H.
    rewrite Bool.andb_true_iff in H. destruct H.
    destruct x as [|[]| |]; auto; exfalso.
    - rewrite (@is_bot_spec _ _ _ ρ) in H. discriminate.
      apply idnc_assume_correct. auto. econstructor. eauto. constructor. constructor.
    - rewrite (@is_bot_spec _ _ _ ρ) in H2. discriminate.
      apply idnc_assume_correct. auto. econstructor. eauto. constructor. constructor.
    - rewrite (@is_bot_spec _ _ _ ρ) in H. discriminate.
      apply idnc_assume_correct. auto. econstructor. eauto. constructor. constructor.
  Qed.

  Fixpoint convert_expr (e:mexpr var0) (v:abt+⊥) (nerr:bool) (gi:ghosts_info) :
    (iexpr * abt+⊥ * bool * ghosts_info) :=
    match e with
    | MEvar (MNTint | MNTint64) x => (IEvar INTz (Real x), v, nerr, gi)
    | MEvar (MNTfloat | MNTsingle) x => (IEvar INTf (Real x), v, nerr, gi)
    | MEconst (MNint i) => (IEconst (INz (Int.signed i)), v, nerr, gi)
    | MEconst (MNint64 i) => (IEconst (INz (Int64.signed i)), v, nerr, gi)
    | MEconst (MNfloat f) => (IEconst (INf f), v, nerr, gi)
    | MEconst (MNsingle f) => (IEconst (INf (Float.of_single f)), v, nerr, gi)

    | MEunop op e =>
      let '(e, v, nerr, gi) := convert_expr e v nerr gi in
      match op with
      | Ocast8unsigned =>
        match normalize_expr v e F0 F255 F8 gi with
          | (e, v, gi, true) => (e, v, nerr, gi)
          | _ => (IEZitv (ZITV F0 F255), v, nerr, gi)
        end
      | Ocast8signed =>
        match normalize_expr v e (-128) 127 F8 gi with
          | (e, v, gi, true) => (e, v, nerr, gi)
          | _ => (IEZitv (ZITV (-128) 127), v, nerr, gi)
        end
      | Ocast16unsigned =>
        match normalize_expr v e F0 65535 F16 gi with
          | (e, v, gi, true) => (e, v, nerr, gi)
          | _ => (IEZitv (ZITV F0 65535), v, nerr, gi)
        end
      | Ocast16signed =>
        match normalize_expr v e (-32768) 32767 F16 gi with
          | (e, v, gi, true) => (e, v, nerr, gi)
          | _ => (IEZitv (ZITV (-32768) 32767), v, nerr, gi)
        end
      | Olongofint =>
        let '(e, v, gi) := normalize_expr_signedness v e Signed gi in
        (e, v, nerr, gi)
      | Olongofintu =>
        let '(e, v, gi) := normalize_expr_signedness v e Unsigned gi in
        (e, v, nerr, gi)
      | Ointoflong => (e, v, nerr, gi)

      | Onegint => (IEunop IOneg e, v, nerr, gi)
      | Onotint => (IEunop IOnot e, v, nerr, gi)

      | Onegl => (IEunop IOneg e, v, nerr, gi)
      | Onotl => (IEunop IOnot e, v, nerr, gi)

      | Onegf | Onegfs => (IEunop IOnegf e, v, nerr, gi)
      | Oabsf | Oabsfs => (IEunop IOabsf e, v, nerr, gi)
      | Ofloatofsingle => (e, v, nerr, gi)
      | Osingleoffloat => (IEunop IOsingleoffloat e, v, nerr, gi)
      | Ointoffloat | Ointofsingle =>
        let '(e, v, gi) := simplify_expr_alias v e gi in
        let e' := IEunop IOzoffloat e in
        let nerr := nerr &&
          check_finitef e v &&
          is_bot (assume_cmp Clt e' (fst intrange) v) &&
          is_bot (assume_cmp Cgt e' (snd intrange) v)
        in
        (e', v, nerr, gi)
      | Ointuoffloat | Ointuofsingle =>
        let '(e, v, gi) := simplify_expr_alias v e gi in
        let e' := IEunop IOzoffloat e in
        let nerr := nerr &&
          check_finitef e v &&
          is_bot (assume_cmp Clt e' F0 v) &&
          is_bot (assume_cmp Cgt e' (snd intrangeu) v)
        in
        (e', v, nerr, gi)
      | Ofloatofint =>
        let '(e, v, gi) := normalize_expr_signedness v e Signed gi in
        (IEunop IOfloatofz e, v, nerr, gi)
      | Osingleofint =>
        let '(e, v, gi) := normalize_expr_signedness v e Signed gi in
        (IEunop IOsingleofz e, v, nerr, gi)
      | Ofloatofintu =>
        let '(e, v, gi) := normalize_expr_signedness v e Unsigned gi in
        (IEunop IOfloatofz e, v, nerr, gi)
      | Osingleofintu =>
        let '(e, v, gi) := normalize_expr_signedness v e Unsigned gi in
        (IEunop IOsingleofz e, v, nerr, gi)
      | Olongoffloat | Olongofsingle =>
        let '(e, v, gi) := simplify_expr_alias v e gi in
        let e' := IEunop IOzoffloat e in
        let nerr := nerr &&
          check_finitef e v &&
          is_bot (assume_cmp Clt e' (fst int64range) v) &&
          is_bot (assume_cmp Cgt e' (snd int64range) v)
        in
        (e', v, nerr, gi)
      | Olonguoffloat | Olonguofsingle =>
        let '(e, v, gi) := simplify_expr_alias v e gi in
        let e' := IEunop IOzoffloat e in
        let nerr := nerr &&
          check_finitef e v &&
          is_bot (assume_cmp Clt e' F0 v) &&
          is_bot (assume_cmp Cgt e' (snd int64rangeu) v)
        in
        (e', v, nerr, gi)
      | Ofloatoflong =>
        let '(e, v, gi) := normalize_expr_signedness64 v e Signed gi in
        (IEunop IOfloatofz e, v, nerr, gi)
      | Ofloatoflongu =>
        let '(e, v, gi) := normalize_expr_signedness64 v e Unsigned gi in
        (IEunop IOfloatofz e, v, nerr, gi)
      | Osingleoflong =>
        let '(e, v, gi) := normalize_expr_signedness64 v e Signed gi in
        (IEunop IOsingleofz e, v, nerr, gi)
      | Osingleoflongu =>
        let '(e, v, gi) := normalize_expr_signedness64 v e Unsigned gi in
        (IEunop IOsingleofz e, v, nerr, gi)
      end

    | MEbinop op e1 e2 =>
      let '(e1, v, nerr, gi) := convert_expr e1 v nerr gi in
      let '(e2, v, nerr, gi) := convert_expr e2 v nerr gi in
      match op with
      | Ocmp c =>
        let '(e1, v, gi) := normalize_expr_signedness v e1 Signed gi in
        let '(e2, v, gi) := normalize_expr_signedness v e2 Signed gi in
        (IEbinop (IOcmp c) e1 e2, v, nerr, gi)
      | Ocmpu c =>
        let '(e1, v, gi) := normalize_expr_signedness v e1 Unsigned gi in
        let '(e2, v, gi) := normalize_expr_signedness v e2 Unsigned gi in
        (IEbinop (IOcmp c) e1 e2, v, nerr, gi)
      | Ocmpl c =>
        let '(e1, v, gi) := normalize_expr_signedness64 v e1 Signed gi in
        let '(e2, v, gi) := normalize_expr_signedness64 v e2 Signed gi in
        (IEbinop (IOcmp c) e1 e2, v, nerr, gi)
      | Ocmplu c =>
        let '(e1, v, gi) := normalize_expr_signedness64 v e1 Unsigned gi in
        let '(e2, v, gi) := normalize_expr_signedness64 v e2 Unsigned gi in
        (IEbinop (IOcmp c) e1 e2, v, nerr, gi)

      | Oaddf => (IEbinop IOaddf e1 e2, v, nerr, gi)
      | Oaddfs => (IEunop IOsingleoffloat (IEbinop IOaddf e1 e2), v, nerr, gi)
      | Osubf => (IEbinop IOsubf e1 e2, v, nerr, gi)
      | Osubfs => (IEunop IOsingleoffloat (IEbinop IOsubf e1 e2), v, nerr, gi)
      | Omulf => (IEbinop IOmulf e1 e2, v, nerr, gi)
      | Omulfs => (IEunop IOsingleoffloat (IEbinop IOmulf e1 e2), v, nerr, gi)
      | Odivf => (IEbinop IOdivf e1 e2, v, nerr, gi)
      | Odivfs => (IEunop IOsingleoffloat (IEbinop IOdivf e1 e2), v, nerr, gi)
      | Ocmpf c | Ocmpfs c => (IEbinop (IOcmpf c) e1 e2, v, nerr, gi)
      | Oadd | Oaddl => (IEbinop IOadd e1 e2, v, nerr, gi)
      | Osub | Osubl => (IEbinop IOsub e1 e2, v, nerr, gi)
      | Omul | Omull => (IEbinop IOmul e1 e2, v, nerr, gi)

      | Odiv =>
        let '(e1, v, gi) := normalize_expr_signedness v e1 Signed gi in
        let '(e2, v, gi) := normalize_expr_signedness v e2 Signed gi in
        let nerr := nerr && is_bot (assume_cmp Ceq e2 F0 v) &&
          is_bot (assume_cmp Cle e1 (fst intrange) (assume_cmp Ceq e2 Fm1 v))
        in
        (IEbinop IOdiv e1 e2, v, nerr, gi)
      | Odivl =>
        let '(e1, v, gi) := normalize_expr_signedness64 v e1 Signed gi in
        let '(e2, v, gi) := normalize_expr_signedness64 v e2 Signed gi in
        let nerr := nerr && is_bot (assume_cmp Ceq e2 F0 v) &&
          is_bot (assume_cmp Cle e1 (fst int64range) (assume_cmp Ceq e2 Fm1 v))
        in
        (IEbinop IOdiv e1 e2, v, nerr, gi)
      | Odivu =>
        let '(e1, v, gi) := normalize_expr_signedness v e1 Unsigned gi in
        let '(e2, v, gi) := normalize_expr_signedness v e2 Unsigned gi in
        let nerr := nerr && is_bot (assume_cmp Ceq e2 F0 v) in
        (IEbinop IOdiv e1 e2, v, nerr, gi)
      | Odivlu =>
        let '(e1, v, gi) := normalize_expr_signedness64 v e1 Unsigned gi in
        let '(e2, v, gi) := normalize_expr_signedness64 v e2 Unsigned gi in
        let nerr := nerr && is_bot (assume_cmp Ceq e2 F0 v) in
        (IEbinop IOdiv e1 e2, v, nerr, gi)
      | Omod =>
        let '(e1, v, gi) := normalize_expr_signedness v e1 Signed gi in
        let '(e2, v, gi) := normalize_expr_signedness v e2 Signed gi in
        let nerr := nerr && is_bot (assume_cmp Ceq e2 F0 v) &&
          is_bot (assume_cmp Cle e1 (fst intrange) (assume_cmp Ceq e2 Fm1 v))
        in
        (IEbinop IOmod e1 e2, v, nerr, gi)
      | Omodl =>
        let '(e1, v, gi) := normalize_expr_signedness64 v e1 Signed gi in
        let '(e2, v, gi) := normalize_expr_signedness64 v e2 Signed gi in
        let nerr := nerr && is_bot (assume_cmp Ceq e2 F0 v) &&
          is_bot (assume_cmp Cle e1 (fst int64range) (assume_cmp Ceq e2 Fm1 v))
        in
        (IEbinop IOmod e1 e2, v, nerr, gi)
      | Omodu =>
        let '(e1, v, gi) := normalize_expr_signedness v e1 Unsigned gi in
        let '(e2, v, gi) := normalize_expr_signedness v e2 Unsigned gi in
        let nerr := nerr && is_bot (assume_cmp Ceq e2 F0 v) in
        (IEbinop IOmod e1 e2, v, nerr, gi)
      | Omodlu =>
        let '(e1, v, gi) := normalize_expr_signedness64 v e1 Unsigned gi in
        let '(e2, v, gi) := normalize_expr_signedness64 v e2 Unsigned gi in
        let nerr := nerr && is_bot (assume_cmp Ceq e2 F0 v) in
        (IEbinop IOmod e1 e2, v, nerr, gi)

      | Oand | Oandl => (IEbinop IOand e1 e2, v, nerr, gi)
      | Oor | Oorl => (IEbinop IOor e1 e2, v, nerr, gi)
      | Oxor | Oxorl => (IEbinop IOxor e1 e2, v, nerr, gi)
      | Oshl =>
        let '(e2, v, gi) := normalize_expr_signedness v e2 Unsigned gi in
        let nerr := nerr && is_bot (assume_cmp Cge e2 intzwordsize v) in
        (IEbinop IOshl e1 e2, v, nerr, gi)
      | Oshll =>
        let '(e2, v, gi) := normalize_expr_signedness v e2 Unsigned gi in
        let nerr := nerr && is_bot (assume_cmp Cge e2 int64zwordsize v) in
        (IEbinop IOshl e1 e2, v, nerr, gi)
      | Oshr =>
        let '(e1, v, gi) := normalize_expr_signedness v e1 Signed gi in
        let '(e2, v, gi) := normalize_expr_signedness v e2 Unsigned gi in
        let nerr := nerr && is_bot (assume_cmp Cge e2 intzwordsize v) in
        (IEbinop IOshr e1 e2, v, nerr, gi)
      | Oshrl =>
        let '(e1, v, gi) := normalize_expr_signedness64 v e1 Signed gi in
        let '(e2, v, gi) := normalize_expr_signedness v e2 Unsigned gi in
        let nerr := nerr && is_bot (assume_cmp Cge e2 int64zwordsize v) in
        (IEbinop IOshr e1 e2, v, nerr, gi)
      | Oshru =>
        let '(e1, v, gi) := normalize_expr_signedness v e1 Unsigned gi in
        let '(e2, v, gi) := normalize_expr_signedness v e2 Unsigned gi in
        let nerr := nerr && is_bot (assume_cmp Cge e2 intzwordsize v) in
        (IEbinop IOshr e1 e2, v, nerr, gi)
      | Oshrlu =>
        let '(e1, v, gi) := normalize_expr_signedness64 v e1 Unsigned gi in
        let '(e2, v, gi) := normalize_expr_signedness v e2 Unsigned gi in
        let nerr := nerr && is_bot (assume_cmp Cge e2 int64zwordsize v) in
        (IEbinop IOshr e1 e2, v, nerr, gi)
      end
    end.

  Lemma convert_expr_next_g_monotone:
    ∀ e v nerr gi e' v' nerr' gi',
      convert_expr e v nerr gi = (e', v', nerr', gi') ->
      (gi.(next_g) <= gi'.(next_g))%positive.
Proof.
    induction e; simpl; intros.
    - destruct m; inv H; Psatz.lia.
    - destruct m; try destruct t; inv H; Psatz.lia.
    - specialize (IHe v nerr gi).
      destruct (convert_expr e v nerr gi) as [[[e'0 v'0] nerr'0] next_g'0].
      specialize (IHe _ _ _ _ eq_refl).
      destruct u;
      repeat match type of H with
      | context [simplify_expr_alias ?A ?B ?C] =>
        let SIMPL_MONO := fresh "SIMPL_MONO" in
        assert (SIMPL_MONO := simplify_expr_alias_monotone A B C);
        destruct (simplify_expr_alias A B C) as [[? ?] ?];
        specialize (SIMPL_MONO _ _ _ eq_refl)
      | context [normalize_expr ?A ?B ?C ?D ?E ?F] =>
        let NORM_MONO := fresh "NORM_MONO" in
        assert (NORM_MONO:=normalize_expr_next_g_monotone A B C D E F);
        destruct (normalize_expr A B C D E F) as [[[? ?] ?][]];
        specialize (NORM_MONO _ _ _ _ eq_refl)
      | context [normalize_expr_signedness ?A ?B ?C ?D] =>
        let NORM_MONO := fresh "NORM_MONO" in
        assert (NORM_MONO:=normalize_expr_signedness_next_g_monotone A B C D);
        destruct (normalize_expr_signedness A B C D) as [[? ?] ?];
        specialize (NORM_MONO _ _ _ eq_refl)
      | context [normalize_expr_signedness64 ?A ?B ?C ?D] =>
        let NORM_MONO := fresh "NORM_MONO" in
        assert (NORM_MONO:=normalize_expr_signedness64_next_g_monotone A B C D);
        destruct (normalize_expr_signedness64 A B C D) as [[? ?] ?];
        specialize (NORM_MONO _ _ _ eq_refl)
      end;
      inv H;
      Psatz.lia.
    - specialize (IHe1 v nerr gi).
      destruct (convert_expr e1 v nerr gi) as [[[e'0 v'0] nerr'0] gi'0].
      specialize (IHe1 _ _ _ _ eq_refl).
      specialize (IHe2 v'0 nerr'0 gi'0).
      destruct (convert_expr e2 v'0 nerr'0 gi'0) as [[[e'1 v'1] nerr'1] gi'1].
      specialize (IHe2 _ _ _ _ eq_refl).
      destruct b;
      repeat match type of H with
      | context [normalize_expr ?A ?B ?C ?D ?E ?F] =>
        let NORM_MONO := fresh "NORM_MONO" in
        assert (NORM_MONO:=normalize_expr_next_g_monotone A B C D E F);
        destruct (normalize_expr A B C D E F) as [[[? ?] ?][]];
        specialize (NORM_MONO _ _ _ _ eq_refl); simpl in H
      | context [normalize_expr_signedness ?A ?B ?C ?D] =>
        let NORM_MONO := fresh "NORM_MONO" in
        assert (NORM_MONO:=normalize_expr_signedness_next_g_monotone A B C D);
        destruct (normalize_expr_signedness A B C D) as [[? ?] ?];
        specialize (NORM_MONO _ _ _ eq_refl)
      | context [normalize_expr_signedness64 ?A ?B ?C ?D] =>
        let NORM_MONO := fresh "NORM_MONO" in
        assert (NORM_MONO:=normalize_expr_signedness64_next_g_monotone A B C D);
        destruct (normalize_expr_signedness64 A B C D) as [[? ?] ?];
        specialize (NORM_MONO _ _ _ eq_refl)
      end;
      inv H; Psatz.lia.
  Qed.

  Lemma convert_expr_ok:
    ∀ e ρ x, eval_mexpr ρ e x ->
    ∀ v ρ'1 gi, ρ'1 ∈ γ v -> ρ'1 ∈ γ gi -> (∀ id, relatedid) (ρ'1 (Real id))) ->
    ∀ nerr e' v' nerr' gi', convert_expr e v nerr gi = (e', v', nerr', gi') ->
    ∃ ρ'2,
      ρ'2 ∈ γ v' /\ ρ'2 ∈ γ gi' /\
      compat_gh gi ρ'1 ρ'2 /\
    ∃ x',
      (∀ ρ'3, compat_gh gi' ρ'2 ρ'3 -> eval_iexpr ρ'3 e' x') ∧
      related x x'.
Proof.
    induction 1; simpl convert_expr;
    intros ab ρ'1 gi Hρ'1 Hgi Hρρ'1 nerr e' v' nerr' gi' EQ.

    - specialize (Hρρ'1 id). exists ρ'1.
      split; [|split;[|split;[|exists (ρ'1 (Real id)); split]]].
      + destruct ty; congruence.
      + destruct ty; inv EQ; auto.
      + auto.
      + destruct ty; inv EQ; inv H0; inv Hρρ'1; try congruence; repeat constructor;
        rewrite H2; symmetry; apply H0.
      + congruence.

    - exists ρ'1. split. destruct n; congruence. split.
      destruct n; inv EQ; auto. split. auto.
      destruct n; inv EQ; eexists;
      (split; [|auto using related_signed, related_signed_long]);
      constructor; constructor.

    - specialize (IHeval_mexpr _ _ _ Hρ'1 Hgi Hρρ'1 nerr).
      assert (MONOe1:=convert_expr_next_g_monotone a1 ab nerr gi).
      destruct (convert_expr a1 ab nerr gi) as [[[e2 ab2] nerr2] gi''].
      specialize (MONOe1 _ _ _ _ eq_refl).
      specialize (IHeval_mexpr _ _ _ _ eq_refl).
      destruct IHeval_mexpr as (ρ'2 & Hab2 & Hgi'' & Hρ'1ρ'2 & x' & EVAL & Hx').
      fastunwrap. simpl Z.of_N in *.
      destruct H0; inv Hx';
      repeat (match type of EQ with
      | context [simplify_expr_alias ?ab ?e ?gi] =>
        let SIMPL := fresh "SIMPL" in let SIMPL_MONO := fresh "SIMPL_MONO" in
        destruct (simplify_expr_alias ab e gi) as [[] ?] eqn:SIMPL;
        assert (SIMPL_MONO:=SIMPL);
        apply simplify_expr_alias_monotone in SIMPL_MONO;
        (eapply simplify_expr_alias_correct in SIMPL;[|now eauto|now eauto|now eauto]);
        destruct SIMPL as (? & ? & ? & ? & ?)
      | context [normalize_expr ?ab ?e ?min ?max ?logmod ?gi] =>
        let NORM := fresh "NORM" in let NORM_MONO := fresh "NORM_MONO" in
        destruct (normalize_expr ab e min max logmod gi) as [[[] ?][]] eqn:NORM;
        assert (NORM_MONO:=NORM);
        apply normalize_expr_next_g_monotone in NORM_MONO;
        (eapply normalize_expr_correct in NORM;[|reflexivity|now eauto|now eauto|now eauto]);
        [destruct NORM as (? & ? & ? & ? & ? & ? & ? & ?)|destruct NORM as (? & ? & ? & ?)]
      | context [normalize_expr_signedness ?ab ?e ?sgn ?gi] =>
        let NORM := fresh "NORM" in let NORM_MONO := fresh "NORM_MONO" in
        destruct (normalize_expr_signedness ab e sgn gi) as [[] ?] eqn:NORM;
        assert (NORM_MONO:=NORM);
        apply normalize_expr_signedness_next_g_monotone in NORM_MONO;
        eapply normalize_expr_signedness_correct in NORM;
        [destruct NORM as (? & ? & ? & ? & ? & ? & ? & ?)|now eauto|now eauto|now eauto]
      | context [normalize_expr_signedness64 ?ab ?e ?sgn ?gi] =>
        let NORM := fresh "NORM" in let NORM_MONO := fresh "NORM_MONO" in
        destruct (normalize_expr_signedness64 ab e sgn gi) as [[] ?] eqn:NORM;
        assert (NORM_MONO:=NORM);
        apply normalize_expr_signedness64_next_g_monotone in NORM_MONO;
        eapply normalize_expr_signedness64_correct in NORM;
        [destruct NORM as (? & ? & ? & ? & ? & ? & ? & ?)|now eauto|now eauto|now eauto]
      end);
      inv EQ.

      + eexists. split. eauto. split. eauto. split. eauto. eexists. split. eauto.
        replace (Int.zero_ext 8 (Int.repr x)) with (Int.repr x1). constructor.
        rewrite <- Int.repr_unsigned with (Int.zero_ext 8 (Int.repr x)). f_equal.
        assert (8 < Int.zwordsize) by reflexivity.
        apply Int.eqmod_small_eq with 256. 2:omega. 2:apply Int.zero_ext_range; omega.
        eapply Int.eqmod_trans. eapply Int.eqmod_sym, H4.
        eapply Int.eqmod_trans, Int.eqmod_sym, Int.eqmod_zero_ext. 2:omega.
        eapply Int.eqmod_divides. eapply Int.eqm_unsigned_repr.
        apply Z.mod_divide. discriminate. reflexivity.
      + eexists. split. eauto. split. auto. split. auto. eexists.
        split. 2:apply related_unsigned. constructor.
        destruct (Int.zero_ext_range 8 (Int.repr x)). compute; split; congruence.
        change (two_p 8) with 256 in H5. split; simpl; omega.

      + eexists. split. eauto. split. eauto. split. eauto. eexists. split. eauto.
        replace (Int.sign_ext 8 (Int.repr x)) with (Int.repr x1). constructor.
        rewrite <- Int.repr_signed with (Int.sign_ext 8 (Int.repr x)). f_equal.
        assert (8 < Int.zwordsize) by reflexivity.
        apply Z.add_cancel_r with (p:=128).
        apply Int.eqmod_small_eq with 256. 2:omega.
        apply Int.eqmod_add. 2:apply Int.eqmod_refl.
        eapply Int.eqmod_trans. eapply Int.eqmod_sym, H4.
        eapply Int.eqmod_trans, Int.eqmod_sym, Int.eqmod_sign_ext. 2:omega.
        eapply Int.eqmod_divides. eapply Int.eqm_unsigned_repr.
        apply Z.mod_divide. discriminate. reflexivity.
        destruct (Int.sign_ext_range 8 (Int.repr x)). omega.
        change (two_p (8-1)) with 128 in H7, H8. omega.
      + eexists. split. eauto. split. auto. split. auto. eexists.
        split. 2:apply related_signed. constructor.
        destruct (Int.sign_ext_range 8 (Int.repr x)). compute; split; congruence.
        change (two_p (8-1)) with 128 in H4, H5. split; simpl; omega.

      + eexists. split. eauto. split. eauto. split. eauto. eexists. split. eauto.
        replace (Int.zero_ext 16 (Int.repr x)) with (Int.repr x1). constructor.
        rewrite <- Int.repr_unsigned with (Int.zero_ext 16 (Int.repr x)). f_equal.
        assert (16 < Int.zwordsize) by reflexivity.
        apply Int.eqmod_small_eq with 65536. 2:omega. 2:apply Int.zero_ext_range; omega.
        eapply Int.eqmod_trans. eapply Int.eqmod_sym, H4.
        eapply Int.eqmod_trans, Int.eqmod_sym, Int.eqmod_zero_ext. 2:omega.
        eapply Int.eqmod_divides. eapply Int.eqm_unsigned_repr.
        apply Z.mod_divide. discriminate. reflexivity.
      + eexists. split. eauto. split. auto. split. auto. eexists.
        split. 2:apply related_unsigned. constructor.
        destruct (Int.zero_ext_range 16 (Int.repr x)). compute; split; congruence.
        change (two_p 16) with 65536 in H5. split; simpl; omega.

      + eexists. split. eauto. split. eauto. split. eauto. eexists. split. eauto.
        replace (Int.sign_ext 16 (Int.repr x)) with (Int.repr x1). constructor.
        rewrite <- Int.repr_signed with (Int.sign_ext 16 (Int.repr x)). f_equal.
        assert (16 < Int.zwordsize) by reflexivity.
        apply Z.add_cancel_r with (p:=32768).
        apply Int.eqmod_small_eq with 65536. 2:omega.
        apply Int.eqmod_add. 2:apply Int.eqmod_refl.
        eapply Int.eqmod_trans. eapply Int.eqmod_sym, H4.
        eapply Int.eqmod_trans, Int.eqmod_sym, Int.eqmod_sign_ext. 2:omega.
        eapply Int.eqmod_divides. eapply Int.eqm_unsigned_repr.
        apply Z.mod_divide. discriminate. reflexivity.
        destruct (Int.sign_ext_range 16 (Int.repr x)). omega.
        change (two_p (16-1)) with 32768 in H7, H8. omega.
      + eexists. split. eauto. split. auto. split. auto. eexists.
        split. 2:apply related_signed. constructor.
        destruct (Int.sign_ext_range 16 (Int.repr x)). compute; split; congruence.
        change (two_p (16-1)) with 32768 in H4, H5. split; simpl; omega.

      + eexists. split. eauto. split. auto. split. auto. eexists. split. eauto.
        rewrite Int.neg_repr. constructor.

      + eexists. split. eauto. split. auto. split. auto. eexists. split. eauto.
        replace (Int.not (Int.repr x)) with (Int.repr (Z.lnot x)). constructor.
        apply Int.same_bits_eq. intros i Hi.
        rewrite Int.bits_not, !Int.testbit_repr, Z.lnot_spec; intuition.

      + eauto 10.

      + eexists. split. eauto. split. auto. split. auto. eexists. split. 2:constructor.
        Transparent Float.neg Float32.neg Float.of_single.
        replace (Float.of_single (Float32.neg f))
        with (Float.neg (Float.of_single f))
          by (apply Bconv_Bopp; auto).
        econstructor; eauto.

      + eauto 10.

      + eexists. split. eauto. split. auto. split. auto. eexists. split. 2:constructor.
        Transparent Float.abs Float32.abs Float.of_single.
        replace (Float.of_single (Float32.abs f))
        with (Float.abs (Float.of_single f))
          by (apply Bconv_Babs; auto).
        econstructor; eauto.

      + eauto 10.

      + eauto 10.

      Transparent Float.to_int.
      + eexists. split. eauto. split. auto. split. eauto. exists (INz (Int.signed i)).
        split. 2:apply related_signed.
        intros. apply eval_IEunop with (n:=INf f). auto. constructor.
        unfold Float.to_int, ZofB_range in H0.
        destruct (ZofB f) eqn:?; [|discriminate].
        destruct ((Int.min_signed <=? z) && (z <=? Int.max_signed))%bool eqn:?; inv H0.
        rewrite Bool.andb_true_iff, <- !Zle_is_le_bool in Heqb.
        rewrite Int.signed_repr by auto. auto.

      Transparent Float32.to_int.
      + eexists. split. eauto. split. auto. split. eauto. exists (INz (Int.signed i)).
        split. 2:apply related_signed.
        intros. apply eval_IEunop with (n:=INf (Float.of_single f)). auto. constructor.
        unfold Float32.to_int, ZofB_range in H0.
        destruct (ZofB f) eqn:?; [|discriminate].
        destruct ((Int.min_signed <=? z) && (z <=? Int.max_signed))%bool eqn:?; inv H0.
        rewrite Bool.andb_true_iff, <- !Zle_is_le_bool in Heqb.
        rewrite Int.signed_repr by auto.
        rewrite ZofB_correct in *.
        destruct (floatofsingle_exact f), (is_finite f); inv Heqo. rewrite H6, H0; auto.

      Transparent Float.to_intu.
      + eexists. split. eauto. split. auto. split. eauto. exists (INz (Int.unsigned i)).
        split. 2:apply related_unsigned.
        intros. apply eval_IEunop with (n:=INf f). auto. constructor.
        unfold Float.to_intu, ZofB_range in H0.
        destruct (ZofB f) eqn:?; [|discriminate].
        destruct ((0 <=? z) && (z <=? Int.max_unsigned))%bool eqn:?; inv H0.
        rewrite Bool.andb_true_iff, <- !Zle_is_le_bool in Heqb.
        rewrite Int.unsigned_repr by auto. auto.

      Transparent Float32.to_intu.
      + eexists. split. eauto. split. auto. split. eauto. exists (INz (Int.unsigned i)).
        split. 2:apply related_unsigned.
        intros. apply eval_IEunop with (n:=INf (Float.of_single f)). auto. constructor.
        unfold Float32.to_intu, ZofB_range in H0.
        destruct (ZofB f) eqn:?; [|discriminate].
        destruct ((0 <=? z) && (z <=? Int.max_unsigned))%bool eqn:?; inv H0.
        rewrite Bool.andb_true_iff, <- !Zle_is_le_bool in Heqb.
        rewrite Int.unsigned_repr by auto.
        rewrite ZofB_correct in *.
        destruct (floatofsingle_exact f), (is_finite f); inv Heqo. rewrite H6, H0; auto.

      + Transparent Float.of_int.
        eexists. split. eauto. split. eauto. split. eauto. eexists. split. 2:constructor.
        rewrite !Int.eqm_samerepr with (x:=x) (y:=x1) by trivial.
        unfold Float.of_int.
        rewrite Int.signed_repr by auto.
        econstructor. eauto. constructor.

      + Transparent Float32.of_int.
        eexists. split. eauto. split. eauto. split. eauto. eexists. split. 2:constructor.
        rewrite !Int.eqm_samerepr with (x:=x) (y:=x1) by trivial.
        unfold Float32.of_int.
        rewrite Int.signed_repr by auto.
        econstructor. eauto. constructor.

      + Transparent Float.of_intu.
        eexists. split. eauto. split. eauto. split. eauto. eexists. split. 2:constructor.
        rewrite !Int.eqm_samerepr with (x:=x) (y:=x1) by trivial.
        unfold Float.of_intu.
        rewrite Int.unsigned_repr by auto.
        econstructor. eauto. constructor.

      + Transparent Float32.of_intu.
        eexists. split. eauto. split. eauto. split. eauto. eexists. split. 2:constructor.
        rewrite !Int.eqm_samerepr with (x:=x) (y:=x1) by trivial.
        unfold Float32.of_intu.
        rewrite Int.unsigned_repr by auto.
        econstructor. eauto. constructor.

      + eexists. split. eauto. split. eauto. split. eauto. eexists.
        split; eauto. rewrite Int64.neg_repr. constructor.

      + eexists. split. eauto. split. eauto. split. eauto. eexists. split; eauto.
        replace (Int64.not (Int64.repr x)) with (Int64.repr (Z.lnot x)). constructor.
        apply Int64.same_bits_eq. intros i Hi.
        rewrite Int64.bits_not, !Int64.testbit_repr, Z.lnot_spec; intuition.

      + eexists. split. eauto. split. eauto. split. eauto. eexists. split; eauto.
        replace (Int64.loword (Int64.repr x)) with (Int.repr x). constructor.
        eapply Int.eqm_samerepr, Int.eqmod_divides. apply Int64.eqm_unsigned_repr.
        exists Int.modulus. reflexivity.

      + eexists. split. eauto. split. eauto. split. eauto. eexists. split; eauto.
        rewrite !Int.eqm_samerepr with (x:=x) (y:=x1) by trivial.
        rewrite Int.signed_repr by auto.
        auto.

      + eexists. split. eauto. split. eauto. split. eauto. eexists. split; eauto.
        rewrite !Int.eqm_samerepr with (x:=x) (y:=x1) by trivial.
        rewrite Int.unsigned_repr by auto.
        auto.

      + Transparent Float.to_long.
        eexists. split. eauto. split. eauto. split. eauto. exists (INz (Int64.signed i)).
        split. 2:apply related_signed_long. econstructor. eauto. constructor.
        unfold Float.to_long, ZofB_range in H0.
        destruct (ZofB f) eqn:?; [|discriminate].
        destruct ((Int64.min_signed <=? z) && (z <=? Int64.max_signed))%bool eqn:?; inv H0.
        rewrite Bool.andb_true_iff, <- !Zle_is_le_bool in Heqb.
        rewrite Int64.signed_repr by auto. auto.

      + Transparent Float.to_longu.
        eexists. split. eauto. split. eauto. split. eauto. exists (INz (Int64.unsigned i)).
        split. 2:apply related_unsigned_long. econstructor. eauto. constructor.
        unfold Float.to_longu, ZofB_range in H0.
        destruct (ZofB f) eqn:?; [|discriminate].
        destruct ((0 <=? z) && (z <=? Int64.max_unsigned))%bool eqn:?; inv H0.
        rewrite Bool.andb_true_iff, <- !Zle_is_le_bool in Heqb.
        rewrite Int64.unsigned_repr by auto. auto.

      + Transparent Float32.to_long.
        eexists. split. eauto. split. eauto. split. eauto. exists (INz (Int64.signed i)).
        split. 2:apply related_signed_long. econstructor. eauto. constructor.
        unfold Float32.to_long, ZofB_range in H0.
        destruct (ZofB f) eqn:?; [|discriminate].
        destruct ((Int64.min_signed <=? z) && (z <=? Int64.max_signed))%bool eqn:?; inv H0.
        rewrite Bool.andb_true_iff, <- !Zle_is_le_bool in Heqb.
        rewrite Int64.signed_repr by auto.
        rewrite ZofB_correct in *.
        destruct (is_finite f) eqn:?.
        rewrite (proj2 (floatofsingle_exact _)), (proj1 (floatofsingle_exact _)); auto.
        discriminate.

      + Transparent Float32.to_longu.
        eexists. split. eauto. split. eauto. split. eauto. exists (INz (Int64.unsigned i)).
        split. 2:apply related_unsigned_long. econstructor. eauto. constructor.
        unfold Float32.to_longu, ZofB_range in H0.
        destruct (ZofB f) eqn:?; [|discriminate].
        destruct ((0 <=? z) && (z <=? Int64.max_unsigned))%bool eqn:?; inv H0.
        rewrite Bool.andb_true_iff, <- !Zle_is_le_bool in Heqb.
        rewrite Int64.unsigned_repr by auto.
        rewrite ZofB_correct in *.
        destruct (is_finite f) eqn:?.
        rewrite (proj2 (floatofsingle_exact _)), (proj1 (floatofsingle_exact _)); auto.
        discriminate.

      + Transparent Float.of_long.
        eexists. split. eauto. split. eauto. split. eauto. eexists. split; eauto.
        rewrite !Int64.eqm_samerepr with (x:=x) (y:=x1) by trivial.
        unfold Float.of_long. rewrite Int64.signed_repr by auto. econstructor.

      + Transparent Float32.of_long.
        eexists. split. eauto. split. eauto. split. eauto. eexists. split; eauto.
        rewrite !Int64.eqm_samerepr with (x:=x) (y:=x1) by trivial.
        unfold Float32.of_long. rewrite Int64.signed_repr by auto. econstructor.

      + Transparent Float.of_longu.
        eexists. split. eauto. split. eauto. split. eauto. eexists. split; eauto.
        rewrite !Int64.eqm_samerepr with (x:=x) (y:=x1) by trivial.
        unfold Float.of_longu. rewrite Int64.unsigned_repr by auto. econstructor.

      + Transparent Float32.of_longu.
        eexists. split. eauto. split. eauto. split. eauto. eexists. split; eauto.
        rewrite !Int64.eqm_samerepr with (x:=x) (y:=x1) by trivial.
        unfold Float32.of_longu. rewrite Int64.unsigned_repr by auto. econstructor.

    - specialize (IHeval_mexpr1 _ _ _ Hρ'1 Hgi Hρρ'1 nerr).
      assert (MONO1:=convert_expr_next_g_monotone a1 ab nerr gi).
      destruct (convert_expr a1 ab nerr gi) as [[[e1' ab'] nerr1] gi''].
      specialize (IHeval_mexpr1 _ _ _ _ eq_refl). specialize (MONO1 _ _ _ _ eq_refl).
      edestruct IHeval_mexpr1 as (ρ'e1 & Hρ'e1 & Hgi'' & Hρ'1ρ'e1 & x'e1 & EVAL1 & Hx'e1).
      assert (Hρρ'e1 : ∀ id : var0, relatedid) (ρ'e1 (Real id))).
      { intros. rewrite <- (proj1 Hρ'1ρ'e1). auto. }
      specialize (IHeval_mexpr2 _ _ _ Hρ'e1 Hgi'' Hρρ'e1 nerr1).
      assert (MONO2:=convert_expr_next_g_monotone a2 ab' nerr1 gi'').
      destruct (convert_expr a2 ab' nerr1 gi'') as [[[e2' ab''] nerr2] gi'''].
      specialize (IHeval_mexpr2 _ _ _ _ eq_refl). specialize (MONO2 _ _ _ _ eq_refl).
      edestruct IHeval_mexpr2 as (ρ'e2 & Hρ'e2 & Hgi'''& Hρ'e1ρ'e2 & x'e2 & EVAL2 & Hx'e2).

      destruct H1; inv Hx'e1; inv Hx'e2;
      repeat (match type of EQ with
        | context [normalize_expr ?ab ?e ?min ?max ?logmod ?gi] =>
          let NORM := fresh "NORM" in let NORM_MONO := fresh "NORM_MONO" in
          destruct (normalize_expr ab e min max logmod gi) as [[[] ?][]] eqn:NORM;
          assert (NORM_MONO:=NORM);
          apply normalize_expr_next_g_monotone in NORM_MONO;
          (eapply normalize_expr_correct in NORM; [|reflexivity|now eauto|now eauto|now eauto]);
          [destruct NORM as (? & ? & ? & ? & ? & ? & ? & ?)|destruct NORM as (? & ? & ? & ?)];
          simpl in EQ
        | context [normalize_expr_signedness ?ab ?e ?sgn ?gi] =>
          let NORM := fresh "NORM" in let NORM_MONO := fresh "NORM_MONO" in
          destruct (normalize_expr_signedness ab e sgn gi) as [[] ?] eqn:NORM;
          assert (NORM_MONO:=NORM);
          apply normalize_expr_signedness_next_g_monotone in NORM_MONO;
          eapply normalize_expr_signedness_correct in NORM;
          [destruct NORM as (? & ? & ? & ? & ? & ? & ? & ?)|now eauto|now eauto|now eauto]
        | context [normalize_expr_signedness64 ?ab ?e ?sgn ?gi] =>
          let NORM := fresh "NORM" in let NORM_MONO := fresh "NORM_MONO" in
          destruct (normalize_expr_signedness64 ab e sgn gi) as [[] ?] eqn:NORM;
          assert (NORM_MONO:=NORM);
          apply normalize_expr_signedness64_next_g_monotone in NORM_MONO;
          eapply normalize_expr_signedness64_correct in NORM;
          [destruct NORM as (? & ? & ? & ? & ? & ? & ? & ?)|now eauto|now eauto|now eauto]
      end).

      + inv EQ. eexists. split. eauto. split. eauto. split. eauto. eexists. split. eauto.
        rewrite Int.add_unsigned.
        erewrite Int.eqm_samerepr. constructor. apply Int.eqm_add; eauto with ints.

      + inv EQ. eexists. split. eauto. split. eauto. split. eauto. eexists. split. eauto.
        unfold Int.sub.
        erewrite Int.eqm_samerepr. constructor. apply Int.eqm_sub; eauto with ints.

      + inv EQ. eexists. split. eauto. split. eauto. split. eauto. eexists. split. eauto.
        unfold Int.mul. erewrite Int.eqm_samerepr. constructor. apply Int.eqm_mult; eauto with ints.

      + inv EQ. eexists. split. eauto. split. eauto. split. eauto. eexists. split.
        econstructor; eauto. constructor.
        * intro. subst. rewrite Int.eqm_samerepr with (x:=x0) (y:=0) in H1 by trivial. discriminate.
        * rewrite !Int.eqm_samerepr with (x:=x) (y:=x2) by trivial.
          rewrite !Int.eqm_samerepr with (x:=x0) (y:=x4) by trivial.
          unfold Int.divs. rewrite !Int.signed_repr; eauto.

      + inv EQ. eexists. split. eauto. split. eauto. split. eauto. eexists. split.
        econstructor; eauto. constructor.
        * intro. subst. rewrite Int.eqm_samerepr with (x:=x0) (y:=0) in H1 by trivial. discriminate.
        * rewrite !Int.eqm_samerepr with (x:=x) (y:=x2) by trivial.
          rewrite !Int.eqm_samerepr with (x:=x0) (y:=x4) in * by trivial.
          unfold Int.divu. rewrite !Int.unsigned_repr; eauto.
          rewrite Int.Zquot_Zdiv. destruct zlt. simpl in *. omega. constructor.
          cut (x4 <> 0). simpl in *. omega. intro. subst. discriminate.

      + inv EQ. eexists. split. eauto. split. eauto. split. eauto. eexists. split.
        econstructor; eauto. constructor.
        * intro. subst. rewrite Int.eqm_samerepr with (x:=x0) (y:=0) in H1 by trivial. discriminate.
        * rewrite !Int.eqm_samerepr with (x:=x) (y:=x2) by trivial.
          rewrite !Int.eqm_samerepr with (x:=x0) (y:=x4) by trivial.
          unfold Int.mods. rewrite !Int.signed_repr; eauto.

      + inv EQ. eexists. split. eauto. split. eauto. split. eauto. eexists. split.
        econstructor; eauto. constructor.
        * intro. subst. rewrite Int.eqm_samerepr with (x:=x0) (y:=0) in H1 by trivial. discriminate.
        * rewrite !Int.eqm_samerepr with (x:=x) (y:=x2) by trivial.
          rewrite !Int.eqm_samerepr with (x:=x0) (y:=x4) in * by trivial.
          unfold Int.modu. rewrite !Int.unsigned_repr; eauto.
          rewrite Z.rem_mod_nonneg. constructor. simpl in *. omega.
          cut (x4 <> 0). simpl in *. omega. intro. subst. discriminate.

      + inv EQ. eexists. split. eauto. split. eauto. split. eauto. eexists. split. eauto.
        replace (Int.and (Int.repr x) (Int.repr x0)) with (Int.repr (Z.land x x0)).
        constructor.
        apply Int.same_bits_eq. intros.
        rewrite Int.bits_and, !Int.testbit_repr, Z.land_spec by trivial.
        reflexivity.

      + inv EQ. eexists. split. eauto. split. eauto. split. eauto. eexists. split. eauto.
        replace (Int.or (Int.repr x) (Int.repr x0)) with (Int.repr (Z.lor x x0)).
        constructor.
        apply Int.same_bits_eq. intros.
        rewrite Int.bits_or, !Int.testbit_repr, Z.lor_spec by trivial.
        reflexivity.

      + inv EQ. eexists. split. eauto. split. eauto. split. eauto. eexists. split. eauto.
        replace (Int.xor (Int.repr x) (Int.repr x0)) with (Int.repr (Z.lxor x x0)).
        constructor.
        apply Int.same_bits_eq. intros.
        rewrite Int.bits_xor, !Int.testbit_repr, Z.lxor_spec by trivial.
        reflexivity.

      + inv EQ. eexists. split. eauto. split. eauto. split. eauto. eexists. split.
        econstructor; eauto.
        rewrite !Int.eqm_samerepr with (x:=x0) (y:=x2) by trivial.
        replace (Int.shl (Int.repr x) (Int.repr x2)) with (Int.repr (Z.shiftl x x2)).
        constructor.
        apply Int.same_bits_eq. intros.
        rewrite Int.bits_shl by trivial.
        rewrite Int.unsigned_repr by trivial.
        destruct zlt.
        rewrite Int.testbit_repr, Z.shiftl_spec by omega.
        apply Z.testbit_neg_r. omega.
        simpl in H5. rewrite !Int.testbit_repr, Z.shiftl_spec by omega. auto.

      + inv EQ. eexists. split. eauto. split. eauto. split. eauto. eexists. split.
        econstructor; eauto.
        rewrite !Int.eqm_samerepr with (x:=x0) (y:=x4) by trivial.
        rewrite !Int.eqm_samerepr with (x:=x) (y:=x2) by trivial.
        unfold Int.shr. rewrite Int.signed_repr, Int.unsigned_repr by trivial. constructor.

      + inv EQ. eexists. split. eauto. split. eauto. split. eauto. eexists. split.
        econstructor; eauto.
        rewrite !Int.eqm_samerepr with (x:=x0) (y:=x4) by trivial.
        rewrite !Int.eqm_samerepr with (x:=x) (y:=x2) by trivial.
        unfold Int.shru. rewrite !Int.unsigned_repr by trivial. constructor.

      + inv EQ. eauto 15.

      + inv EQ. eauto 15.

      + inv EQ. eauto 15.

      + inv EQ. eauto 15.

      + inv EQ. eexists. split. eauto. split. eauto. split. eauto. eexists. split. eauto 10.
        rewrite plus_double_round. auto.

      + inv EQ. eexists. split. eauto. split. eauto. split. eauto. eexists. split. eauto 10.
        rewrite sub_double_round. auto.

      + inv EQ. eexists. split. eauto. split. eauto. split. eauto. eexists. split. eauto 10.
        rewrite mult_double_round. auto.

      + inv EQ. eexists. split. eauto. split. eauto. split. eauto. eexists. split. eauto 10.
        rewrite div_double_round. auto.

      + inv EQ. eexists. split. eauto. split. eauto. split. eauto. eexists. split. eauto 10.
        rewrite Int64.add_unsigned.
        erewrite Int64.eqm_samerepr. constructor. apply Int64.eqm_add; eauto with ints.

      + inv EQ. eexists. split. eauto. split. eauto. split. eauto. eexists. split. eauto 10.
        unfold Int64.sub.
        erewrite Int64.eqm_samerepr. constructor. apply Int64.eqm_add; eauto with ints.

      + inv EQ. eexists. split. eauto. split. eauto. split. eauto. eexists. split. eauto. unfold Int64.mul.
        erewrite Int64.eqm_samerepr. econstructor. apply Int64.eqm_mult; eauto with ints.

      + inv EQ. eexists. split. eauto. split. eauto. split. eauto. eexists. split.
        econstructor; eauto. constructor.
        * intro. subst. rewrite Int64.eqm_samerepr with (x:=x0) (y:=0) in H1 by trivial. discriminate.
        * rewrite !Int64.eqm_samerepr with (x:=x) (y:=x2) by trivial.
          rewrite !Int64.eqm_samerepr with (x:=x0) (y:=x4) by trivial.
          unfold Int64.divs. rewrite !Int64.signed_repr; eauto.

      + inv EQ. eexists. split. eauto. split. eauto. split. eauto. eexists. split.
        econstructor; eauto. constructor.
        * intro. subst. rewrite Int64.eqm_samerepr with (x:=x0) (y:=0) in H1 by trivial. discriminate.
        * rewrite !Int64.eqm_samerepr with (x:=x) (y:=x2) by trivial.
          rewrite !Int64.eqm_samerepr with (x:=x0) (y:=x4) in * by trivial.
          unfold Int64.divu. rewrite !Int64.unsigned_repr; eauto.
          rewrite Int64.Zquot_Zdiv. destruct zlt. simpl in *. omega. constructor.
          cut (x4 <> 0). simpl in *. omega. intro. subst. discriminate.

      + inv EQ. eexists. split. eauto. split. eauto. split. eauto. eexists. split.
        econstructor; eauto. constructor.
        * intro. subst. rewrite Int64.eqm_samerepr with (x:=x0) (y:=0) in H1 by trivial. discriminate.
        * rewrite !Int64.eqm_samerepr with (x:=x) (y:=x2) by trivial.
          rewrite !Int64.eqm_samerepr with (x:=x0) (y:=x4) by trivial.
          unfold Int64.mods. rewrite !Int64.signed_repr; eauto.

      + inv EQ. eexists. split. eauto. split. eauto. split. eauto. eexists. split.
        econstructor; eauto. constructor.
        * intro. subst. rewrite Int64.eqm_samerepr with (x:=x0) (y:=0) in H1 by trivial. discriminate.
        * rewrite !Int64.eqm_samerepr with (x:=x) (y:=x2) by trivial.
          rewrite !Int64.eqm_samerepr with (x:=x0) (y:=x4) in * by trivial.
          unfold Int64.modu. rewrite !Int64.unsigned_repr; eauto.
          rewrite Z.rem_mod_nonneg. constructor. simpl in *. omega.
          cut (x4 <> 0). simpl in *. omega. intro. subst. discriminate.

      + inv EQ. eexists. split. eauto. split. eauto. split. eauto. eexists. split. eauto.
        replace (Int64.and (Int64.repr x) (Int64.repr x0)) with (Int64.repr (Z.land x x0)).
        constructor.
        apply Int64.same_bits_eq. intros.
        rewrite Int64.bits_and, !Int64.testbit_repr, Z.land_spec by trivial.
        reflexivity.

      + inv EQ. eexists. split. eauto. split. eauto. split. eauto. eexists. split. eauto.
        replace (Int64.or (Int64.repr x) (Int64.repr x0)) with (Int64.repr (Z.lor x x0)).
        constructor.
        apply Int64.same_bits_eq. intros.
        rewrite Int64.bits_or, !Int64.testbit_repr, Z.lor_spec by trivial.
        reflexivity.

      + inv EQ. eexists. split. eauto. split. eauto. split. eauto. eexists. split. eauto.
        replace (Int64.xor (Int64.repr x) (Int64.repr x0)) with (Int64.repr (Z.lxor x x0)).
        constructor.
        apply Int64.same_bits_eq. intros.
        rewrite Int64.bits_xor, !Int64.testbit_repr, Z.lxor_spec by trivial.
        reflexivity.

      + inv EQ. eexists. split. eauto. split. eauto. split. eauto. eexists. split.
        econstructor; eauto.
        rewrite !Int.eqm_samerepr with (x:=x0) (y:=x2) by trivial.
        replace (Int64.shl' (Int64.repr x) (Int.repr x2)) with (Int64.repr (Z.shiftl x x2)).
        constructor.
        apply Int64.same_bits_eq. intros.
        rewrite Int64.bits_shl' by trivial.
        rewrite Int.unsigned_repr by trivial.
        destruct zlt.
        rewrite Int64.testbit_repr, Z.shiftl_spec by omega.
        apply Z.testbit_neg_r. omega.
        simpl in H5. rewrite !Int64.testbit_repr, Z.shiftl_spec by omega. auto.

      + inv EQ. eexists. split. eauto. split. eauto. split. eauto. eexists. split.
        econstructor; eauto.
        rewrite !Int.eqm_samerepr with (x:=x0) (y:=x4) by trivial.
        rewrite !Int64.eqm_samerepr with (x:=x) (y:=x2) by trivial.
        unfold Int64.shr'. rewrite Int64.signed_repr, Int.unsigned_repr by trivial. constructor.

      + inv EQ. eexists. split. eauto. split. eauto. split. eauto. eexists. split.
        econstructor; eauto.
        rewrite !Int.eqm_samerepr with (x:=x0) (y:=x4) by trivial.
        rewrite !Int64.eqm_samerepr with (x:=x) (y:=x2) by trivial.
        unfold Int64.shru'. rewrite Int64.unsigned_repr, Int.unsigned_repr by trivial. constructor.

      + inv EQ. eexists. split. eauto. split. eauto. split. eauto.
        exists (INz (if Int.cmp c (Int.repr x) (Int.repr x0) then F1 else F0)).
        split. econstructor; eauto. 2:destruct Int.cmp; constructor.
        rewrite !Int.eqm_samerepr with (x:=x) (y:=x2) by trivial.
        rewrite !Int.eqm_samerepr with (x:=x0) (y:=x4) by trivial.
        replace (Int.cmp c (Int.repr x2) (Int.repr x4)) with (Zcmp c x2 x4).
        constructor.
        unfold Int.cmp, Int.lt.
        rewrite !Int.signed_repr by eauto.
        pose proof (Int.eq_spec (Int.repr x2) (Int.repr x4)).
        destruct c; simpl.
        * destruct Int.eq; destruct (Z.compare_spec x2 x4); try constructor; exfalso.
          assert (Int.signed (Int.repr x2) = Int.signed (Int.repr x4)) by congruence.
          rewrite !Int.signed_repr in H16 by trivial. omega.
          assert (Int.signed (Int.repr x2) = Int.signed (Int.repr x4)) by congruence.
          rewrite !Int.signed_repr in H16 by trivial. omega.
          congruence.
        * destruct Int.eq; destruct (Z.compare_spec x2 x4); try constructor; exfalso.
          assert (Int.signed (Int.repr x2) = Int.signed (Int.repr x4)) by congruence.
          rewrite !Int.signed_repr in H16 by trivial. omega.
          assert (Int.signed (Int.repr x2) = Int.signed (Int.repr x4)) by congruence.
          rewrite !Int.signed_repr in H16 by trivial. omega.
          congruence.
        * destruct Coqlib.zlt; destruct (Z.compare_spec x2 x4); try constructor; exfalso; omega.
        * destruct Coqlib.zlt; destruct (Z.compare_spec x2 x4); try constructor; exfalso; omega.
        * destruct Coqlib.zlt; destruct (Z.compare_spec x2 x4); try constructor; exfalso; omega.
        * destruct Coqlib.zlt; destruct (Z.compare_spec x2 x4); try constructor; exfalso; omega.

      + inv EQ. eexists. split. eauto. split. eauto. split. eauto.
        exists (INz (if Int.cmpu c (Int.repr x) (Int.repr x0) then F1 else F0)).
        split. econstructor; eauto. 2:destruct Int.cmpu; constructor.
        rewrite !Int.eqm_samerepr with (x:=x) (y:=x2) by trivial.
        rewrite !Int.eqm_samerepr with (x:=x0) (y:=x4) by trivial.
        replace (Int.cmpu c (Int.repr x2) (Int.repr x4)) with (Zcmp c x2 x4).
        constructor.
        unfold Int.cmpu, Int.ltu.
        rewrite !Int.unsigned_repr by auto.
        pose proof (Int.eq_spec (Int.repr x2) (Int.repr x4)).
        destruct c; simpl.
        * destruct Int.eq; destruct (Z.compare_spec x2 x4); try constructor; exfalso.
          assert (Int.unsigned (Int.repr x2) = Int.unsigned (Int.repr x4)) by congruence.
          rewrite !Int.unsigned_repr in H16 by trivial. omega.
          assert (Int.unsigned (Int.repr x2) = Int.unsigned (Int.repr x4)) by congruence.
          rewrite !Int.unsigned_repr in H16 by trivial. omega.
          congruence.
        * destruct Int.eq; destruct (Z.compare_spec x2 x4); try constructor; exfalso.
          assert (Int.unsigned (Int.repr x2) = Int.unsigned (Int.repr x4)) by congruence.
          rewrite !Int.unsigned_repr in H16 by trivial. omega.
          assert (Int.unsigned (Int.repr x2) = Int.unsigned (Int.repr x4)) by congruence.
          rewrite !Int.unsigned_repr in H16 by trivial. omega.
          congruence.
        * destruct Coqlib.zlt; destruct (Z.compare_spec x2 x4); try constructor; exfalso; omega.
        * destruct Coqlib.zlt; destruct (Z.compare_spec x2 x4); try constructor; exfalso; omega.
        * destruct Coqlib.zlt; destruct (Z.compare_spec x2 x4); try constructor; exfalso; omega.
        * destruct Coqlib.zlt; destruct (Z.compare_spec x2 x4); try constructor; exfalso; omega.

      + inv EQ. eexists. split. eauto. split. eauto. split. eauto.
        exists (INz (if Int64.cmp c (Int64.repr x) (Int64.repr x0) then F1 else F0)).
        split. econstructor; eauto. 2:destruct Int64.cmp; constructor.
        rewrite !Int64.eqm_samerepr with (x:=x) (y:=x2) by trivial.
        rewrite !Int64.eqm_samerepr with (x:=x0) (y:=x4) by trivial.
        replace (Int64.cmp c (Int64.repr x2) (Int64.repr x4)) with (Zcmp c x2 x4).
        constructor.
        unfold Int64.cmp, Int64.lt.
        rewrite !Int64.signed_repr by eauto.
        pose proof (Int64.eq_spec (Int64.repr x2) (Int64.repr x4)).
        destruct c; simpl.
        * destruct Int64.eq; destruct (Z.compare_spec x2 x4); try constructor; exfalso.
          assert (Int64.signed (Int64.repr x2) = Int64.signed (Int64.repr x4)) by congruence.
          rewrite !Int64.signed_repr in H16 by trivial. omega.
          assert (Int64.signed (Int64.repr x2) = Int64.signed (Int64.repr x4)) by congruence.
          rewrite !Int64.signed_repr in H16 by trivial. omega.
          congruence.
        * destruct Int64.eq; destruct (Z.compare_spec x2 x4); try constructor; exfalso.
          assert (Int64.signed (Int64.repr x2) = Int64.signed (Int64.repr x4)) by congruence.
          rewrite !Int64.signed_repr in H16 by trivial. omega.
          assert (Int64.signed (Int64.repr x2) = Int64.signed (Int64.repr x4)) by congruence.
          rewrite !Int64.signed_repr in H16 by trivial. omega.
          congruence.
        * destruct Coqlib.zlt; destruct (Z.compare_spec x2 x4); try constructor; exfalso; omega.
        * destruct Coqlib.zlt; destruct (Z.compare_spec x2 x4); try constructor; exfalso; omega.
        * destruct Coqlib.zlt; destruct (Z.compare_spec x2 x4); try constructor; exfalso; omega.
        * destruct Coqlib.zlt; destruct (Z.compare_spec x2 x4); try constructor; exfalso; omega.

      + inv EQ. eexists. split. eauto. split. eauto. split. eauto.
        exists (INz (if Int64.cmpu c (Int64.repr x) (Int64.repr x0) then F1 else F0)).
        split. econstructor; eauto. 2:destruct Int64.cmpu; constructor.
        rewrite !Int64.eqm_samerepr with (x:=x) (y:=x2) by trivial.
        rewrite !Int64.eqm_samerepr with (x:=x0) (y:=x4) by trivial.
        replace (Int64.cmpu c (Int64.repr x2) (Int64.repr x4)) with (Zcmp c x2 x4).
        constructor.
        unfold Int64.cmpu, Int64.ltu.
        rewrite !Int64.unsigned_repr by auto.
        pose proof (Int64.eq_spec (Int64.repr x2) (Int64.repr x4)).
        destruct c; simpl.
        * destruct Int64.eq; destruct (Z.compare_spec x2 x4); try constructor; exfalso.
          assert (Int64.unsigned (Int64.repr x2) = Int64.unsigned (Int64.repr x4)) by congruence.
          rewrite !Int64.unsigned_repr in H16 by trivial. omega.
          assert (Int64.unsigned (Int64.repr x2) = Int64.unsigned (Int64.repr x4)) by congruence.
          rewrite !Int64.unsigned_repr in H16 by trivial. omega.
          congruence.
        * destruct Int64.eq; destruct (Z.compare_spec x2 x4); try constructor; exfalso.
          assert (Int64.unsigned (Int64.repr x2) = Int64.unsigned (Int64.repr x4)) by congruence.
          rewrite !Int64.unsigned_repr in H16 by trivial. omega.
          assert (Int64.unsigned (Int64.repr x2) = Int64.unsigned (Int64.repr x4)) by congruence.
          rewrite !Int64.unsigned_repr in H16 by trivial. omega.
          congruence.
        * destruct Coqlib.zlt; destruct (Z.compare_spec x2 x4); try constructor; exfalso; omega.
        * destruct Coqlib.zlt; destruct (Z.compare_spec x2 x4); try constructor; exfalso; omega.
        * destruct Coqlib.zlt; destruct (Z.compare_spec x2 x4); try constructor; exfalso; omega.
        * destruct Coqlib.zlt; destruct (Z.compare_spec x2 x4); try constructor; exfalso; omega.

      + inv EQ. eexists. split. eauto. split. eauto. split. eauto. eexists. split. eauto.
        destruct Float.cmp; constructor.

      + inv EQ. eexists. split. eauto. split. eauto. split. eauto. eexists. split. eauto.
        rewrite Float32.cmp_double. unfold Float32.to_double.
        destruct Float.cmp; constructor.
  Qed.

  Lemma convert_expr_nerr_monotone:
    ∀ e v gi e' v' gi',
      ~convert_expr e v false gi = (e', v', true, gi').
Proof.
    induction e; simpl; intros.
    - destruct m; congruence.
    - destruct m; congruence.
    - specialize (IHe v gi).
      destruct (convert_expr e v false gi) as [[[e'0 v'0] []] next_g'0].
      edestruct IHe; now eauto.
      destruct u;
      repeat match goal with
      | |- context [simplify_expr_alias ?A ?B ?C] =>
        destruct (simplify_expr_alias A B C) as [[? ?] ?]
      | |- context [normalize_expr ?A ?B ?C ?D ?E ?F] =>
        destruct (normalize_expr A B C D E F) as [[[? ?] ?][]]
      | |- context [normalize_expr_signedness ?A ?B ?C ?D] =>
        destruct (normalize_expr_signedness A B C D) as [[? ?] ?]
      | |- context [normalize_expr_signedness64 ?A ?B ?C ?D] =>
        destruct (normalize_expr_signedness64 A B C D) as [[? ?] ?]
      end; simpl; congruence.
    - specialize (IHe1 v gi).
      destruct (convert_expr e1 v false gi) as [[[e'0 v'0] []] gi'0].
      edestruct IHe1; now eauto.
      specialize (IHe2 v'0 gi'0).
      destruct (convert_expr e2 v'0 false gi'0) as [[[e'1 v'1] []] gi'1].
      edestruct IHe2; now eauto.
      destruct b;
      repeat match goal with
      | |- context [normalize_expr ?A ?B ?C ?D ?E ?F] =>
        destruct (normalize_expr A B C D E F) as [[[? ?] ?][]]
      | |- context [normalize_expr_signedness ?A ?B ?C ?D] =>
        destruct (normalize_expr_signedness A B C D) as [[? ?] ?]
      | |- context [normalize_expr_signedness64 ?A ?B ?C ?D] =>
        destruct (normalize_expr_signedness64 A B C D) as [[? ?] ?]
      end; simpl; congruence.
  Qed.

  Ltac leb_case H :=
    match type of H with
    | context [?A <=? ?B] => pose proof Zle_cases A B; destruct (A <=? B)
    end.

  Lemma convert_expr_noerror:
    ∀ ρ e v ρ'1 gi, ρ'1 ∈ γ v -> ρ'1 ∈ γ gi ->
      (∀ id, relatedid) (ρ'1 (Real id))) ->
    ∀ e' v' gi', convert_expr e v true gi = (e', v', true, gi') ->
      ¬error_mexpr ρ e.
Proof.
    induction e; intros ab ρ'1 gi Hρ'1 Hgi Hρρ'1 e' ab' gi' EQCVT ERR;
    inv ERR; simpl convert_expr in EQCVT.
    - destruct (convert_expr e ab true gi) as [[[e0 ab0] nerr0] gi''] eqn:EQCVT'.
      eapply convert_expr_ok in EQCVT'; eauto.
      destruct EQCVT' as (ρ'2 & Hρ'2 & Hgi'' & Hρ'1ρ'2 & x' & EVAL & REL).
      destruct H2; inv REL;
      match type of EQCVT with
      | context [simplify_expr_alias ?ab ?e ?gi] =>
        let SIMPL := fresh "SIMPL" in let SIMPL_MONO := fresh "SIMPL_MONO" in
        destruct (simplify_expr_alias ab e gi) as [[] ?] eqn:SIMPL;
        assert (SIMPL_MONO:=SIMPL);
        apply simplify_expr_alias_monotone in SIMPL_MONO;
        (eapply simplify_expr_alias_correct in SIMPL;[|now eauto|now eauto|now eauto]);
        destruct SIMPL as (? & ? & ? & ? & ?)
      end;
      inv EQCVT.
      + rewrite !Bool.andb_true_iff in H8; destruct H8 as [[[] ?] ?].
        unfold Float.to_int, ZofB_range in H.
        assert (∃ z, ZofB f = Some z).
        { erewrite ZofB_correct, check_finitef_correct by eauto. eauto. }
        destruct H9. rewrite H9 in H. leb_case H. leb_case H. discriminate.
        * rewrite (is_bot_spec x) in H8. discriminate.
          eapply assume_cmp_correct; eauto 3. econstructor. eauto. constructor. eauto.
          simpl. destruct (Z.compare_spec x0 (Int.max_signed)); try Psatz.lia. auto.
        * rewrite (is_bot_spec x) in H7. discriminate.
          eapply assume_cmp_correct; eauto 3. econstructor. eauto. constructor. eauto.
          simpl. destruct (Z.compare_spec x0 (Int.min_signed)); try Psatz.lia. auto.
      + rewrite !Bool.andb_true_iff in H8; destruct H8 as [[[] ?] ?].
        unfold Float.to_intu, ZofB_range in H.
        assert (∃ z, ZofB f = Some z).
        { erewrite ZofB_correct, check_finitef_correct by eauto. eauto. }
        destruct H9. rewrite H9 in H. leb_case H. leb_case H. discriminate.
        * rewrite (is_bot_spec x) in H8. discriminate.
          eapply assume_cmp_correct; eauto 3. econstructor. eauto. constructor. eauto.
          simpl. destruct (Z.compare_spec x0 (Int.max_unsigned)); try Psatz.lia. auto.
        * rewrite (is_bot_spec x) in H7. discriminate.
          eapply assume_cmp_correct; eauto 3. econstructor. eauto. constructor. eauto.
          simpl. destruct (Z.compare_spec x0 0); try Psatz.lia. auto.
      + rewrite !Bool.andb_true_iff in H8; destruct H8 as [[[] ?] ?].
        unfold Float.to_long, ZofB_range in H.
        assert (∃ z, ZofB f = Some z).
        { erewrite ZofB_correct, check_finitef_correct by eauto. eauto. }
        destruct H9. rewrite H9 in H. leb_case H. leb_case H. discriminate.
        * rewrite (is_bot_spec x) in H8. discriminate.
          eapply assume_cmp_correct; eauto 3. econstructor. eauto. constructor. eauto.
          simpl. destruct (Z.compare_spec x0 (Int64.max_signed)); try Psatz.lia. auto.
        * rewrite (is_bot_spec x) in H7. discriminate.
          eapply assume_cmp_correct; eauto 3. econstructor. eauto. constructor. eauto.
          simpl. destruct (Z.compare_spec x0 (Int64.min_signed)); try Psatz.lia. auto.
      + rewrite !Bool.andb_true_iff in H8; destruct H8 as [[[] ?] ?].
        unfold Float.to_longu, ZofB_range in H.
        assert (∃ z, ZofB f = Some z).
        { erewrite ZofB_correct, check_finitef_correct by eauto. eauto. }
        destruct H9. rewrite H9 in H. leb_case H. leb_case H. discriminate.
        * rewrite (is_bot_spec x) in H8. discriminate.
          eapply assume_cmp_correct; eauto 3. econstructor. eauto. constructor. eauto.
          simpl. destruct (Z.compare_spec x0 (Int64.max_unsigned)); try Psatz.lia. auto.
        * rewrite (is_bot_spec x) in H7. discriminate.
          eapply assume_cmp_correct; eauto 3. econstructor. eauto. constructor. eauto.
          simpl. destruct (Z.compare_spec x0 0); try Psatz.lia. auto.
      + rewrite !Bool.andb_true_iff in H8; destruct H8 as [[[] ?] ?].
        unfold Float32.to_int, ZofB_range in H.
        assert (∃ z, ZofB (Float.of_single f) = Some z).
        { erewrite ZofB_correct, check_finitef_correct by eauto. eauto. }
        destruct H9.
        assert (ZofB (Float.of_single f) = ZofB f).
        { rewrite !ZofB_correct. destruct (floatofsingle_exact f), f; auto. rewrite H10, H11; auto. }
        rewrite <- H10, H9 in H. leb_case H. leb_case H. discriminate.
        * rewrite (is_bot_spec x) in H8. discriminate.
          eapply assume_cmp_correct; eauto 3. econstructor. eauto. constructor. eauto.
          simpl. destruct (Z.compare_spec x0 (Int.max_signed)); try Psatz.lia. auto.
        * rewrite (is_bot_spec x) in H7. discriminate.
          eapply assume_cmp_correct; eauto 3. econstructor. eauto. constructor. eauto.
          simpl. destruct (Z.compare_spec x0 (Int.min_signed)); try Psatz.lia. auto.
      + rewrite !Bool.andb_true_iff in H8; destruct H8 as [[[] ?] ?].
        unfold Float32.to_intu, ZofB_range in H.
        assert (∃ z, ZofB (Float.of_single f) = Some z).
        { erewrite ZofB_correct, check_finitef_correct by eauto. eauto. }
        destruct H9.
        assert (ZofB (Float.of_single f) = ZofB f).
        { rewrite !ZofB_correct. destruct (floatofsingle_exact f), f; auto. rewrite H10, H11; auto. }
        rewrite <- H10, H9 in H. leb_case H. leb_case H. discriminate.
        * rewrite (is_bot_spec x) in H8. discriminate.
          eapply assume_cmp_correct; eauto 3. econstructor. eauto. constructor. eauto.
          simpl. destruct (Z.compare_spec x0 (Int.max_unsigned)); try Psatz.lia. auto.
        * rewrite (is_bot_spec x) in H7. discriminate.
          eapply assume_cmp_correct; eauto 3. econstructor. eauto. constructor. eauto.
          simpl. destruct (Z.compare_spec x0 0); try Psatz.lia. auto.
      + rewrite !Bool.andb_true_iff in H8; destruct H8 as [[[] ?] ?].
        unfold Float32.to_long, ZofB_range in H.
        assert (∃ z, ZofB (Float.of_single f) = Some z).
        { erewrite ZofB_correct, check_finitef_correct by eauto. eauto. }
        destruct H9.
        assert (ZofB (Float.of_single f) = ZofB f).
        { rewrite !ZofB_correct. destruct (floatofsingle_exact f), f; auto. rewrite H10, H11; auto. }
        rewrite <- H10, H9 in H. leb_case H. leb_case H. discriminate.
        * rewrite (is_bot_spec x) in H8. discriminate.
          eapply assume_cmp_correct; eauto 3. econstructor. eauto. constructor. eauto.
          simpl. destruct (Z.compare_spec x0 (Int64.max_signed)); try Psatz.lia. auto.
        * rewrite (is_bot_spec x) in H7. discriminate.
          eapply assume_cmp_correct; eauto 3. econstructor. eauto. constructor. eauto.
          simpl. destruct (Z.compare_spec x0 (Int64.min_signed)); try Psatz.lia. auto.
      + rewrite !Bool.andb_true_iff in H8; destruct H8 as [[[] ?] ?].
        unfold Float32.to_longu, ZofB_range in H.
        assert (∃ z, ZofB (Float.of_single f) = Some z).
        { erewrite ZofB_correct, check_finitef_correct by eauto. eauto. }
        destruct H9.
        assert (ZofB (Float.of_single f) = ZofB f).
        { rewrite !ZofB_correct. destruct (floatofsingle_exact f), f; auto. rewrite H10, H11; auto. }
        rewrite <- H10, H9 in H. leb_case H. leb_case H. discriminate.
        * rewrite (is_bot_spec x) in H8. discriminate.
          eapply assume_cmp_correct; eauto 3. econstructor. eauto. constructor. eauto.
          simpl. destruct (Z.compare_spec x0 (Int64.max_unsigned)); try Psatz.lia. auto.
        * rewrite (is_bot_spec x) in H7. discriminate.
          eapply assume_cmp_correct; eauto 3. econstructor. eauto. constructor. eauto.
          simpl. destruct (Z.compare_spec x0 0); try Psatz.lia. auto.
    - destruct (convert_expr e ab true gi) as [[[e0 ab0] nerr0] gi''] eqn:EQCVT'.
      assert (nerr0 = true).
      { destruct u; try congruence;
        try (destruct normalize_expr as [[[? ?] ?][]]; congruence);
        try (destruct normalize_expr_signedness as [[? ?] ?]; congruence);
        try (destruct normalize_expr_signedness64 as [[? ?] ?]; congruence);
        destruct (simplify_expr_alias ab0 e0 gi'') as [[? ?] ?];
        injection EQCVT; destruct nerr0; simpl; congruence. }
      subst. eapply IHe; eauto.
    - destruct (convert_expr e1 ab true gi) as [[[e01 ab01] nerr01] gi''] eqn:EQCVT'1.
      eapply convert_expr_ok in EQCVT'1; eauto.
      destruct EQCVT'1 as (ρ'e1 & Hρ'e1 & Hgi'' & Hρ'1ρ'e1 & x'e1 & EVAL1 & REL1).
      assert (MONO2:=convert_expr_next_g_monotone e2 ab01 nerr01 gi'').
      destruct (convert_expr e2 ab01 nerr01 gi'') as [[[e02 ab02] nerr02] gi'''] eqn:EQCVT'2.
      specialize (MONO2 _ _ _ _ eq_refl).
      assert (Hρρ'e1 : ∀ id, relatedid) (ρ'e1 (Real id))).
      { intros. rewrite <- (proj1 (Hρ'1ρ'e1)). auto. }
      eapply convert_expr_ok in EQCVT'2; eauto.
      destruct EQCVT'2 as (ρ'e2 & Hρ'e2 & Hgi''' & Hρ'e1ρ'e2 & x'e2 & EVAL2 & REL2).
      assert (EVAL1':∀ ρ'', compat_gh gi''' ρ'e2 ρ'' -> eval_iexpr ρ'' e01 x'e1).
      { intros. apply EVAL1. split; intros; etransitivity.
        apply Hρ'e1ρ'e2. apply H.
        apply Hρ'e1ρ'e2. auto. apply H. Psatz.lia. }
      fastunwrap. simpl Z.of_N in *.
      destruct H4; inv REL1; inv REL2;
      repeat (match type of EQCVT with
      | context [simplify_expr_alias ?ab ?e ?gi] =>
        let SIMPL := fresh "SIMPL" in let SIMPL_MONO := fresh "SIMPL_MONO" in
        destruct (simplify_expr_alias ab e gi) as [[] ?] eqn:SIMPL;
        assert (SIMPL_MONO:=SIMPL);
        apply simplify_expr_alias_monotone in SIMPL_MONO;
        (eapply simplify_expr_alias_correct in SIMPL;[|now eauto|now eauto|now eauto]);
        destruct SIMPL as (? & ? & ? & ? & ?)
      | context [normalize_expr ?ab ?e ?min ?max ?logmod ?gi] =>
        let NORM := fresh "NORM" in let NORM_MONO := fresh "NORM_MONO" in
        destruct (normalize_expr ab e min max logmod gi) as [[[] ?][]] eqn:NORM;
        assert (NORM_MONO:=NORM);
        apply normalize_expr_next_g_monotone in NORM_MONO;
        (eapply normalize_expr_correct in NORM; [|reflexivity|now eauto|now eauto|now eauto]);
        [destruct NORM as (? & ? & ? & ? & ? & ? & ? & ?)|destruct NORM as (? & ? & ? & ?)];
        simpl in EQCVT
      | context [normalize_expr_signedness ?ab ?e ?sgn ?gi] =>
        let NORM := fresh "NORM" in let NORM_MONO := fresh "NORM_MONO" in
        destruct (normalize_expr_signedness ab e sgn gi) as [[] ?] eqn:NORM;
        assert (NORM_MONO:=NORM);
        apply normalize_expr_signedness_next_g_monotone in NORM_MONO;
        eapply normalize_expr_signedness_correct in NORM;
        [destruct NORM as (? & ? & ? & ? & ? & ? & ? & ?)|now eauto|now eauto|now eauto]
      | context [normalize_expr_signedness64 ?ab ?e ?sgn ?gi] =>
        let NORM := fresh "NORM" in let NORM_MONO := fresh "NORM_MONO" in
        destruct (normalize_expr_signedness64 ab e sgn gi) as [[] ?] eqn:NORM;
        assert (NORM_MONO:=NORM);
        apply normalize_expr_signedness64_next_g_monotone in NORM_MONO;
        eapply normalize_expr_signedness64_correct in NORM;
        [destruct NORM as (? & ? & ? & ? & ? & ? & ? & ?)|now eauto|now eauto|now eauto]
      end);
      inv EQCVT;
      repeat match goal with
      | HH : _ &&_ = true |- _ =>
        rewrite Bool.andb_true_iff in HH; destruct HH
      end.
      + change (Int.unsigned (Int.repr x0) = 0) in H0.
        rewrite <- Int.signed_eq_unsigned in H0 by (rewrite H0; compute; congruence).
        erewrite Int.eqm_samerepr, Int.signed_repr in H0 by eauto. subst.
        erewrite (is_bot_spec x3) in H16. discriminate.
        eauto using assume_cmp_correct.
      + change (Int.unsigned (Int.repr x) = Int.unsigned (Int.repr (Int.min_signed))) in H0.
        change (Int.unsigned (Int.repr x0) = Int.unsigned (Int.repr (-1))) in H1.
        eapply f_equal with (f:=Int.signedInt.repr) in H0.
        eapply f_equal with (f:=Int.signedInt.repr) in H1.
        rewrite !Int.repr_unsigned in H0, H1.
        erewrite Int.eqm_samerepr, Int.signed_repr in H0, H1 by eauto. subst.
        erewrite (is_bot_spec x3) in H16. discriminate.
        eauto using assume_cmp_correct.
      + change (Int.unsigned (Int.repr x0) = 0) in H0.
        erewrite Int.eqm_samerepr, Int.unsigned_repr in H0 by eauto. subst.
        erewrite (is_bot_spec x3) in H15. discriminate.
        eauto using assume_cmp_correct.
      + change (Int.unsigned (Int.repr x0) = 0) in H0.
        rewrite <- Int.signed_eq_unsigned in H0 by (rewrite H0; compute; congruence).
        erewrite Int.eqm_samerepr, Int.signed_repr in H0 by eauto. subst.
        erewrite (is_bot_spec x3) in H16. discriminate.
        eauto using assume_cmp_correct.
      + change (Int.unsigned (Int.repr x) = Int.unsigned (Int.repr (Int.min_signed))) in H0.
        change (Int.unsigned (Int.repr x0) = Int.unsigned (Int.repr (-1))) in H1.
        eapply f_equal with (f:=Int.signedInt.repr) in H0.
        eapply f_equal with (f:=Int.signedInt.repr) in H1.
        rewrite !Int.repr_unsigned in H0, H1.
        erewrite Int.eqm_samerepr, Int.signed_repr in H0, H1 by eauto. subst.
        erewrite (is_bot_spec x3) in H16. discriminate.
        eauto using assume_cmp_correct.
      + change (Int.unsigned (Int.repr x0) = 0) in H0.
        erewrite Int.eqm_samerepr, Int.unsigned_repr in H0 by eauto. subst.
        erewrite (is_bot_spec x3) in H15. discriminate.
        eauto using assume_cmp_correct.
      + rewrite (is_bot_spec x1) in H9. discriminate.
        eapply assume_cmp_correct; eauto 2.
        erewrite Int.eqm_samerepr in H by eauto.
        unfold Int.ltu in H. rewrite Int.unsigned_repr in H by auto. destruct zlt. discriminate.
        change (x2 >= Int.zwordsize) in g.
        simpl. destruct (Z.compare_spec x2 Int.zwordsize); try omega; auto.
      + rewrite (is_bot_spec x3) in H15. discriminate.
        eapply assume_cmp_correct; eauto 2.
        erewrite Int.eqm_samerepr in H by eauto.
        unfold Int.ltu in H. rewrite Int.unsigned_repr in H by auto. destruct zlt. discriminate.
        change (x4 >= Int.zwordsize) in g0.
        simpl. destruct (Z.compare_spec x4 Int.zwordsize); try omega; auto.
      + rewrite (is_bot_spec x3) in H15. discriminate.
        eapply assume_cmp_correct; eauto 2.
        erewrite Int.eqm_samerepr in H by eauto.
        unfold Int.ltu in H. rewrite Int.unsigned_repr in H by auto. destruct zlt. discriminate.
        change (x4 >= Int.zwordsize) in g0.
        simpl. destruct (Z.compare_spec x4 Int.zwordsize); try omega; auto.
      + change (Int64.unsigned (Int64.repr x0) = 0) in H0.
        rewrite <- Int64.signed_eq_unsigned in H0 by (rewrite H0; compute; congruence).
        erewrite Int64.eqm_samerepr, Int64.signed_repr in H0 by eauto. subst.
        rewrite (is_bot_spec x3) in H16. discriminate.
        eauto using assume_cmp_correct.
      + change (Int64.unsigned (Int64.repr x) = Int64.unsigned (Int64.repr (Int64.min_signed))) in H0.
        change (Int64.unsigned (Int64.repr x0) = Int64.unsigned (Int64.repr (-1))) in H1.
        eapply f_equal with (f:=Int64.signedInt64.repr) in H0.
        eapply f_equal with (f:=Int64.signedInt64.repr) in H1.
        rewrite !Int64.repr_unsigned in H0, H1.
        erewrite Int64.eqm_samerepr, Int64.signed_repr in H0, H1 by eauto. subst.
        rewrite (is_bot_spec x3) in H16. discriminate.
        eauto using assume_cmp_correct.
      + change (Int64.unsigned (Int64.repr x0) = 0) in H0.
        erewrite Int64.eqm_samerepr, Int64.unsigned_repr in H0 by eauto. subst.
        rewrite (is_bot_spec x3) in H15. discriminate.
        eauto using assume_cmp_correct.
      + change (Int64.unsigned (Int64.repr x0) = 0) in H0.
        rewrite <- Int64.signed_eq_unsigned in H0 by (rewrite H0; compute; congruence).
        erewrite Int64.eqm_samerepr, Int64.signed_repr in H0 by eauto. subst.
        rewrite (is_bot_spec x3) in H16. discriminate.
        eauto using assume_cmp_correct.
      + change (Int64.unsigned (Int64.repr x) = Int64.unsigned (Int64.repr (Int64.min_signed))) in H0.
        change (Int64.unsigned (Int64.repr x0) = Int64.unsigned (Int64.repr (-1))) in H1.
        eapply f_equal with (f:=Int64.signedInt64.repr) in H0.
        eapply f_equal with (f:=Int64.signedInt64.repr) in H1.
        rewrite !Int64.repr_unsigned in H0, H1.
        erewrite Int64.eqm_samerepr, Int64.signed_repr in H0, H1 by eauto. subst.
        rewrite (is_bot_spec x3) in H16. discriminate.
        eauto using assume_cmp_correct.
      + change (Int64.unsigned (Int64.repr x0) = 0) in H0.
        erewrite Int64.eqm_samerepr, Int64.unsigned_repr in H0 by eauto. subst.
        rewrite (is_bot_spec x3) in H15. discriminate.
        eauto using assume_cmp_correct.
      + rewrite (is_bot_spec x1) in H9. discriminate.
        eapply assume_cmp_correct; eauto 2.
        erewrite Int.eqm_samerepr in H by eauto.
        unfold Int.ltu in H. rewrite Int.unsigned_repr in H by auto. destruct zlt. discriminate.
        change (x2 >= Int64.zwordsize) in g.
        simpl. destruct (Z.compare_spec x2 Int64.zwordsize); try omega; auto.
      + rewrite (is_bot_spec x3) in H15. discriminate.
        eapply assume_cmp_correct; eauto 2.
        erewrite Int.eqm_samerepr in H by eauto.
        unfold Int.ltu in H. rewrite Int.unsigned_repr in H by auto. destruct zlt. discriminate.
        change (x4 >= Int64.zwordsize) in g0.
        simpl. destruct (Z.compare_spec x4 Int64.zwordsize); try omega; auto.
      + rewrite (is_bot_spec x3) in H15. discriminate.
        eapply assume_cmp_correct; eauto 2.
        erewrite Int.eqm_samerepr in H by eauto.
        unfold Int.ltu in H. rewrite Int.unsigned_repr in H by auto. destruct zlt. discriminate.
        change (x4 >= Int64.zwordsize) in g0.
        simpl. destruct (Z.compare_spec x4 Int64.zwordsize); try omega; auto.

    - destruct (convert_expr e1 ab true gi) as [[[e01 ab01] nerr01] gi''] eqn:EQCVT'1.
      destruct (convert_expr e2 ab01 nerr01 gi'') as [[[e02 ab02] nerr02] g'''] eqn:EQCVT'2.
      assert (nerr01 = true).
      { destruct nerr01, nerr02; auto.
        apply convert_expr_nerr_monotone in EQCVT'2. contradiction.
        destruct b; simpl in EQCVT;
        repeat (match type of EQCVT with
          | context [normalize_expr ?ab ?e ?min ?max ?logmod ?gi] =>
            destruct (normalize_expr ab e min max logmod gi) as [[[] ?][]]
          | context [normalize_expr_signedness ?ab ?e ?sgn ?gi] =>
            destruct (normalize_expr_signedness ab e sgn gi) as [[] ?]
          | context [normalize_expr_signedness64 ?ab ?e ?sgn ?gi] =>
            destruct (normalize_expr_signedness64 ab e sgn gi) as [[] ?]
        end);
        congruence. }
      subst. eapply IHe1; eauto.
    - destruct (convert_expr e1 ab true gi) as [[[e01 ab01] nerr01] gi''] eqn:EQCVT'1.
      edestruct convert_expr_ok as (ρ'e1 & Hρ'e1 & Hgi'' & Hρ'1ρ'e1 & x'e1 & EVAL & Hx'e1); eauto.
      destruct (convert_expr e2 ab01 nerr01 gi'') as [[[e02 ab02] nerr02] gi'''] eqn:EQCVT'2.
      assert (nerr01 = true /\ nerr02 = true).
      { destruct nerr01, nerr02; auto;
        try (destruct b; simpl in EQCVT;
        repeat (match type of EQCVT with
          | context [normalize_expr ?ab ?e ?min ?max ?logmod ?gi] =>
            destruct (normalize_expr ab e min max logmod gi) as [[[] ?][]]
          | context [normalize_expr_signedness ?ab ?e ?sgn ?gi] =>
            destruct (normalize_expr_signedness ab e sgn gi) as [[] ?]
          | context [normalize_expr_signedness64 ?ab ?e ?sgn ?gi] =>
            destruct (normalize_expr_signedness64 ab e sgn gi) as [[] ?]
        end); congruence).
        apply convert_expr_nerr_monotone in EQCVT'2. contradiction. }
      destruct H. subst. eapply IHe2; eauto.
      intros. rewrite <- (proj1 Hρ'1ρ'e1). auto.
  Qed.

  Definition forget_temps (v:abt) (gi:ghosts_info) : abt+⊥ :=
    positive_rec _ (NotBot v)
      (fun x v => do v <- v; idnc_forget (Ghost x) v)
      gi.(next_g).

  Lemma forget_temps_correct:
    ∀ ab (ρ:var -> ideal_num) gi, ρ ∈ γ ab ->
    ρ ∈ γ (forget_temps ab gi).
Proof.
    unfold forget_temps. intros ab ρ gi.
    change (ρ ∈ γ ab) with (ρ ∈ γ (NotBot ab)).
    revert ρ. generalize (NotBot ab). clear ab.
    induction gi.(next_g) using Pos.peano_ind; intros.
    + rewrite positive_rec_base. auto.
    + rewrite positive_rec_succ.
      apply botbind_spec with ρ. intros. 2:now auto.
      replace ρ with (ρ+[Ghost p => ρ (Ghost p)]).
      eapply idnc_forget_correct, H0.
      apply Axioms.extensionality. intros. unfold upd. destruct @eq_dec; congruence.
  Qed.

  Definition get_itv (e:mexpr var0) (v:abt * typesmap) : mach_num_itv+⊥ :=
    let '(ie, v, _, gi) := convert_expr e (NotBot (fst v)) false init_ghost_info in
    do v <- v;
    do itv <- (idnc_get_query_chan v).(AbIdealEnv.get_itv) (simplify_expr ie);
      match itv, mexpr_ty e with
      | Just (Az ((ZITV a b) as itv')), MNTint =>
        NotBot (MNIint
           (match itv_normalizer (NotBot (Just itv'))
                                 (fst intrange) (snd intrangeu) intzwordsize with
           | Some q => Just (ZITV (Zfastsub a (Zfastshl q intzwordsize))
                                  (Zfastsub b (Zfastshl q intzwordsize)))
           | None => All
           end))
      | All, MNTint => NotBot (MNIint All)
      | Just (Az ((ZITV a b) as itv')), MNTint64 =>
        NotBot (MNIint64
           (match itv_normalizer (NotBot (Just itv'))
                                 (fst int64range) (snd int64rangeu) int64zwordsize with
           | Some q => Just (ZITV (Zfastsub a (Zfastshl q int64zwordsize))
                                  (Zfastsub b (Zfastshl q int64zwordsize)))
           | None => All
           end))
      | All, MNTint64 => ret (MNIint64 All)
      | Just (Af itv), MNTfloat => ret (MNIfloat (Just itv))
      | Just (Af itv), MNTsingle => ret (MNIsingle (Just itv))
      | All, MNTfloat => ret (MNIfloat All)
      | All, MNTsingle => ret (MNIsingle All)
      | _, _ => Bot
      end.

  Lemma get_itv_correct:
    ∀ e ρ ab, ρ ∈ γ ab -> eval_mexpr ρ e ⊆ γ (get_itv e ab).
Proof.
    intros e ρ [ab typs] [[ρ' [Hγρ' Hρρ']] Htyps] x EVAL. unfold get_itv. fastunwrap.
    pose proof (convert_expr_ok _ _ _ EVAL (NotBot ab) _ _ Hγρ' (gamma_gh_init _ ) Hρρ' false). unfold fst.
    destruct convert_expr as [[[e' ab'] nerr'] gi].
    edestruct H as (ρ'2 & Hab' & Hgi & Hρ'ρ'2 & x' & EVAL' & REL); eauto. clear H.
    assert (TY:=mexpr_ty_correct EVAL).
    revert TY. generalize (mexpr_ty e) as ty. intros.
    clear Hγρ' EVAL e ab typs Htyps.
    eapply botbind_spec with ρ'2; eauto. intros.
    eapply botbind_spec with x';
      [|eapply AbIdealEnv.get_itv_correct, simplify_expr_correct]; eauto.
    2:apply idnc_get_query_chan_correct; auto.
    clear EVAL' e' ρ ρ'2 ρ' Hρρ' Hρ'ρ'2 Hab' H Hgi.
    intros. destruct a0 as [|[]], x'; try contradiction; destruct TY; inv REL;
    try (constructor; auto).
    - destruct z as [[] []].
      destruct itv_normalizer; [|now repeat constructor].
      rewrite Int.eqm_samerepr with (y:=x-Int.modulus * z).
      rewrite Int.Zshiftl_mul_two_p by discriminate.
      change (two_p intzwordsize) with Int.modulus.
      destruct H; repeat constructor; fastunwrap; Psatz.lia.
      exists z. ring.
    - destruct z as [[] []].
      destruct itv_normalizer; [|now repeat constructor].
      rewrite Int64.eqm_samerepr with (y:=x-Int64.modulus * z).
      rewrite Int.Zshiftl_mul_two_p by discriminate.
      change (two_p int64zwordsize) with Int64.modulus.
      destruct H; repeat constructor; fastunwrap; Psatz.lia.
      exists z. ring.
  Qed.

  Definition forget (x:var0) (v:abt * typesmap) : (abt * typesmap)+⊥ :=
    do fstres <- idnc_forget (Real x) (fst v);
    ret (fstres, Cells.ACTree.remove x (snd v)).

  Lemma forget_correct:
    ∀ x ρ, ∀ n ab,
      ρ ∈ γ ab ->
      (upd ρ x n) ∈ γ (forget x ab).
Proof.
    intros x ρ n ab Hρ.
    destruct Hρ as [[ρ' [Hρ' Hρρ']] Htyps].
    eapply botbind_spec with (upd ρ' (Real x) (match n with MNint _ => _ | _ => _ end)).
    2:apply idnc_forget_correct; now auto. intros. split.
    - eexists. split. apply H.
      intro. unfold upd. destruct @eq_dec. inv e. rewrite eq_dec_true.
      destruct n. apply related_signed. apply related_signed_long. constructor. constructor.
      destruct @eq_dec. congruence. auto.
    - simpl. intros y s Hx. erewrite Cells.ACTree.grspec in Hx; eauto.
      unfold upd, eq_dec.
      destruct (Cells.ACTree.elt_eq y x), (Cells.ACellDec y x); try congruence. auto.
  Qed.

  Fixpoint infer_var_types (e:mexpr var0) (ctx:option signedness) (typs:typesmap)
  : typesmap :=
    match e with
    | MEvar nvt x =>
      match ctx with
      | None =>
        match Cells.ACTree.get x typs with
        | None =>
          match nvt with
          | MNTfloat | MNTsingle => typs
          | _ => Cells.ACTree.set x (None, nvt) typs
          end
        | Some _ => typs
        end
      | Some _ => Cells.ACTree.set x (ctx, nvt) typs
      end
    | MEconst _ => typs
    | MEunop op e =>
      let ctx :=
        match op with
        | (Ofloatofint | Ofloatoflong | Olongofint | Osingleofint | Osingleoflong |
           Ocast16signed | Ocast8signed) => Some Signed
        | (Ofloatofintu | Ofloatoflongu | Olongofintu | Osingleofintu | Osingleoflongu |
           Ocast16unsigned | Ocast8unsigned) => Some Unsigned
        | _ => None
        end
      in
      infer_var_types e ctx typs
    | MEbinop op e1 e2 =>
      let ctx1 :=
        match op with
        | (Odiv | Omod | Odivl | Omodl | Ocmp _ | Ocmpl _ | Oshr | Oshrl) =>
          Some Signed
        | (Odivu | Omodu | Odivlu | Omodlu | Ocmpu _ | Ocmplu _ | Oshru | Oshrlu) =>
          Some Unsigned
        | _ => None
        end
      in
      let ctx2 :=
        match op with
        | (Odiv | Omod | Odivl | Omodl | Ocmp _ | Ocmpl _) => Some Signed
        | (Odivu | Omodu | Odivlu | Omodlu | Ocmpu _ | Ocmplu _ |
           Oshl | Oshr | Oshru | Oshll | Oshrl | Oshrlu) => Some Unsigned
        | _ => None
        end
      in
      (infer_var_types e2 ctx2 (infer_var_types e1 ctx1 typs))
    end.

  Lemma infer_var_types_consistent :
    ∀ ρ e nv ctx typs,
      eval_mexpr ρ e nv ->
      ρ ∈ γ typs ->
      (match nv with MNfloat _ | MNsingle _ => ctx = None | _ => True end) ->
      ρ ∈ γ (infer_var_types e ctx typs).
Proof.
    induction e; simpl; intros nv ctx typs H Htyps Hfl v' [s ty] Hmap; inv H; simpl.
    - destruct ctx.
      + erewrite Cells.ACTree.gsspec in Hmap; eauto.
        destruct Cells.ACTree.elt_eq.
        inv Hmap. split; auto. inv H4; try easy.
        rewrite <- H1 in Hfl. destruct Hfl. congruence.
        rewrite <- H1 in Hfl. destruct Hfl. congruence.
        apply Htyps in Hmap. auto.
      + destruct (Cells.ACTree.get v typs) eqn:?. apply Htyps in Hmap. auto.
        destruct m.
        * erewrite Cells.ACTree.gsspec in Hmap; eauto.
          destruct Cells.ACTree.elt_eq.
          inv Hmap. auto. apply Htyps in Hmap. auto.
        * erewrite Cells.ACTree.gsspec in Hmap; eauto.
          destruct Cells.ACTree.elt_eq.
          inv Hmap. auto. apply Htyps in Hmap. auto.
        * apply Htyps in Hmap. auto.
        * apply Htyps in Hmap. auto.
    - apply Htyps in Hmap. auto.
    - match goal with Hmap : context [infer_var_types e ?ctx' typs] |- _ => set ctx' in Hmap end.
      edestruct (IHe _ o typs H2); eauto. destruct H4; auto.
    - match goal with Hmap : context [infer_var_types e1 ?ctx' typs] |- _ => set ctx' in Hmap end.
      match goal with Hmap : context [infer_var_types e2 ?ctx' _] |- _ => set ctx' in Hmap end.
      edestruct (fun typs => IHe2 _ o0 typs H5). 3:eauto. 2:destruct H6; auto. 2:auto.
      intros v [s' ty'] Hmap'.
      edestruct (IHe1 _ o typs H3); eauto. destruct H6; auto.
  Qed.

  Definition assign_vars_to_top (e:mexpr var0) (v:abt * typesmap) : (abt * typesmap)+⊥ :=
    let types := infer_var_types e None (Cells.ACTree.empty _) in
    Cells.ACTree.fold (fun ab v sty =>
      do (ab, typs) <- ab;
      match sty with
      | (Some s, ty) =>
        let typs := Cells.ACTree.set v sty typs in
        match get_itv (MEvar ty v) (ab, typs) with
        | NotBot (MNIint All) | NotBot (MNIint64 All) =>
          do ab <- (idnc_assign (Real v) (IEZitv
                 (match s, ty with
                  | Signed, MNTint => signed_itv
                  | Unsigned, MNTint => unsigned_itv
                  | Signed, MNTint64 => signed64_itv
                  | Unsigned, MNTint64 => unsigned64_itv
                  | _, _ => signed_itv
                  end)) ab);
          ret (ab, typs)
        | _ => ret (ab, typs)
        end
      | (None, _) =>
        let typs :=
          match Cells.ACTree.get v typs with
          | None => Cells.ACTree.set v sty typs
          | Some _ => typs
          end
        in
        ret (ab, typs)
      end)
     types (NotBot v).

  Lemma assign_vars_to_top_correct:
    forall ab e ρ v,
      eval_mexpr ρ e v ->
      γ ab ρ -> γ (assign_vars_to_top e ab) ρ.
Proof.
    unfold assign_vars_to_top. intros.
    pose proof infer_var_types_consistent ρ e _ None (Cells.ACTree.empty _) H.
    lapply H1. 2:intro; rewrite Cells.ACTree.gempty; discriminate.
    clear H1. intro. lapply H1. 2:destruct v; now auto. clear H1.
    change (γ (NotBot ab) ρ) in H0. revert H0. generalize (NotBot ab). clear ab.
    generalize (infer_var_types e None). clear e v H.
    intros. revert ρ H0 H.
    assert (∀ v s0, Cells.ACTree.get v (t (Cells.ACTree.empty _)) = Some s0 <->
                    List.In (v, s0) (Cells.ACTree.elements (t (Cells.ACTree.empty _)))).
    { split. apply Cells.ACTree.elements_correct. apply Cells.ACTree.elements_complete. }
    simpl. unfold gamma_typs. setoid_rewrite H. clear H.
    rewrite Cells.ACTree.fold_spec, <- fold_left_rev_right. setoid_rewrite in_rev.
    induction (rev (Cells.ACTree.elements (t (Cells.ACTree.empty _)))). auto. simpl. intros.
    eapply botbind_spec with ρ; [|apply IHl]; eauto.
    intros. destruct a. specialize (H0 _ _ (or_introl eq_refl)). destruct H0, p.
    destruct a0, H1 as [[? []] ?].
    destruct o.
    2:simpl; destruct (Cells.ACTree.get e t0); (split; [eexists; split; now eauto|]); auto;
      simpl; intro; rewrite Cells.ACTree.gsspec; destruct Cells.ACTree.elt_eq; [|now apply H4];
      intros s0 Hs0; inv Hs0; auto. simpl.
    assert (eval_mexpr ρ (MEvar m e) (ρ e)) by (constructor; auto).
    assert (γ (Cells.ACTree.set e (Some s, m) t0) ρ).
    { intro. simpl. rewrite Cells.ACTree.gsspec. destruct Cells.ACTree.elt_eq.
      - intros. inv H6. auto.
      - apply H4. }
    unfold snd in H2. inv H2; try discriminate.
    + assert (ASS:=idnc_assign_correct (Real e)
                (e:=IEZitv (match s with
                       | Signed => signed_itv
                       | Unsigned => unsigned_itv
                     end))
                (n:=INz (Zwrap (match s with
                                  | Signed => Int.signed i
                                  | Unsigned => Int.unsigned i
                                  end))) a H1).
      lapply ASS.
      2:constructor; destruct s;
      [apply signed_itv_correct|apply unsigned_itv_correct].
      clear ASS; intro.
      change Cells.ACTree.elt with var0 in *.
      destruct idnc_assign. contradiction.
      assert (γ (NotBot(x0, Cells.ACTree.set e (Some s, MNTint) t0)) ρ).
      { split. eexists. split. now eauto.
        intro. unfold upd. destruct @eq_dec; auto. inv e0. rewrite <- H9.
        destruct s. apply related_signed. apply related_unsigned.
        intro. simpl. rewrite Cells.ACTree.gsspec. destruct Cells.ACTree.elt_eq.
        intros. inv H7. split. auto. rewrite <- H9. constructor. auto. }
      destruct get_itv as [|[[]|[]| |]]; (try now (econstructor; eauto)); eauto;
      (split; [eexists; eauto |auto]).
    + assert (ASS:=idnc_assign_correct (Real e)
                (e:=IEZitv (match s with
                       | Signed => signed64_itv
                       | Unsigned => unsigned64_itv
                     end))
                (n:=INz (Zwrap (match s with
                                  | Signed => Int64.signed i
                                  | Unsigned => Int64.unsigned i
                                  end))) a H1).
      lapply ASS.
      2:constructor; destruct s;
      [apply signed64_itv_correct|apply unsigned64_itv_correct].
      clear ASS; intro.
      change Cells.ACTree.elt with var0 in *.
      destruct idnc_assign. contradiction.
      assert (γ (NotBot(x0, Cells.ACTree.set e (Some s, MNTint64) t0)) ρ).
      { split. eexists. split. now eauto.
        intro. unfold upd. destruct @eq_dec; auto. inv e0. rewrite <- H9.
        destruct s. apply related_signed_long. apply related_unsigned_long.
        intro. simpl. rewrite Cells.ACTree.gsspec. destruct Cells.ACTree.elt_eq.
        intros. inv H7. split. auto. rewrite <- H9. constructor. auto. }
      destruct get_itv as [|[[]|[]| |]]; (try now (econstructor; eauto)); eauto;
      (split; [eexists; eauto |auto]).
  Qed.

  Definition assign (x:var0) (e:mexpr var0) (v:abt*typesmap) : (abt*typesmap)+⊥ :=
    let v := assign_vars_to_top e v in
    do (v, typs) <- v;
    let '(e', v, _, gi) := convert_expr e (NotBot v) false init_ghost_info in
    do v <- v;
    let ty := mexpr_ty e in
    let typs := Cells.ACTree.set x (None, ty) typs in
    let '(v, gi) :=
    match ty with
    | MNTint =>
      match normalize_expr (NotBot v) e'
                           (fst intrange) (snd intrangeu) intzwordsize gi with
      | (e, v, gi, true) =>
        (do v <- v; idnc_assign (Real x) e v, gi)
      | _ =>
        (idnc_forget (Real x) v, gi)
      end
    | MNTint64 =>
      match normalize_expr (NotBot v) e'
                           (fst int64range) (snd int64rangeu) int64zwordsize gi with
      | (e, v, gi, true) =>
        (do v <- v; idnc_assign (Real x) e v, gi)
      | _ =>
        (idnc_forget (Real x) v, gi)
      end
    | MNTfloat | MNTsingle =>
      (let e := simplify_expr e' in idnc_assign (Real x) e v, gi)
    end
    in
    do v <- v;
    do v <- forget_temps v gi;
    ret (v, typs).

  Lemma assign_correct:
    ∀ x e ρ, ∀ n ab,
      ρ ∈ γ ab ->
      neval_mexpr ρ e ->
      (upd ρ x n) ∈ γ (assign x e ab).
Proof.
    intros x e ρ n ab0 Hρ EVAL.
    unfold assign, intrange, int64range, intrangeu, int64rangeu,
           intzwordsize, int64zwordsize, fst, snd.
    apply botbind_spec with ρ.
    2:eauto using assign_vars_to_top_correct; eauto. clear Hρ.
    intros [ab typs] [[ρ' [Hρ' Hρρ']] Htyps].
    pose proof (convert_expr_ok _ _ _ EVAL (NotBot ab) _ _ Hρ' (gamma_gh_init _) Hρρ' false).
    destruct convert_expr as [[[e' v'] nerr] gi].
    edestruct H as (ρ'2 & Hρ'2 & Hgi & Hρ'ρ'2 & x' & Hx' & Hnx'); auto. clear H.
    apply botbind_spec with ρ'2. 2:now eauto. intros.
    pose proof (mexpr_ty_correct EVAL).
    destruct H0; inv Hnx'.
    - destruct (normalize_expr (NotBot a) e' Int.min_signed Int.max_unsigned
                               Int.zwordsize gi) as [[[e1 ab1] gi'][]] eqn:EQNORM.
      edestruct normalize_expr_correct with (b:=true)as
          (ρ'3 & Hρ'2ρ'3 & Hρ'3 & Hgi' & x'0 & Hx'0bounded &Hx'x'0 & EVALx'0); eauto.
      reflexivity.
      { eapply botbind_spec with (upd ρ'3 (Real x) (INz x'0)).
        - intros. eapply botbind_spec with (upd ρ'3 (Real x) (INz x'0)).
          2:now apply forget_temps_correct, H0. split.
          + eexists. split. apply H1. intro. unfold upd.
            destruct (eq_dec id x), (eq_dec (Real id) (Real x)); try congruence.
            erewrite Int.eqm_samerepr. econstructor. auto.
            rewrite <- (proj1 Hρ'2ρ'3), <- (proj1 Hρ'ρ'2). auto.
          + intros y typsy. simpl. rewrite Cells.ACTree.gsspec. unfold upd, eq_dec, Cells.ACellDec.
            destruct Cells.ACTree.elt_eq. intros eq. inv eq. split; constructor. auto.
        - eapply botbind_spec with ρ'3, Hρ'3.
          intros. apply idnc_assign_correct; auto. }
      { eapply botbind_spec with (upd ρ'2 (Real x) (INz x0)).
        - intros. eapply botbind_spec with (upd ρ'2 (Real x) (INz x0)).
          2:now apply forget_temps_correct, H0. split.
          + eexists. split. apply H1. intro. unfold upd.
            destruct (eq_dec id x), (eq_dec (Real id) (Real x)); try congruence. auto.
            rewrite <- (proj1 Hρ'ρ'2). auto.
          + intros y typsy. simpl. rewrite Cells.ACTree.gsspec.
            unfold upd, eq_dec, Cells.ACellDec. destruct Cells.ACTree.elt_eq.
            intros eq. inv eq. split; constructor. auto.
        - apply idnc_forget_correct; auto. }
    - destruct (normalize_expr (NotBot a) e' Int64.min_signed Int64.max_unsigned
                               Int64.zwordsize gi) as [[[e1 ab1] gi'][]] eqn:EQNORM.
      edestruct normalize_expr_correct with (b:=true)as
          (ρ'3 & Hρ'2ρ'3 & Hρ'3 & Hgi' & x'0 & Hx'0bounded &Hx'x'0 & EVALx'0); eauto. reflexivity.
      { eapply botbind_spec with (upd ρ'3 (Real x) (INz x'0)).
        - intros. eapply botbind_spec with (upd ρ'3 (Real x) (INz x'0)).
          2:now apply forget_temps_correct, H0. split.
          + eexists. split. apply H1. intro. unfold upd.
            destruct (eq_dec id x), (eq_dec (Real id) (Real x)); try congruence.
            erewrite Int64.eqm_samerepr. econstructor. auto.
            rewrite <- (proj1 Hρ'2ρ'3), <- (proj1 Hρ'ρ'2). auto.
          + intros y typsy. simpl. rewrite Cells.ACTree.gsspec.
            unfold upd, eq_dec, Cells.ACellDec. destruct Cells.ACTree.elt_eq.
             intros eq. inv eq. split; constructor. auto.
        - eapply botbind_spec with ρ'3, Hρ'3.
          intros. apply idnc_assign_correct; auto. }
      { eapply botbind_spec with (upd ρ'2 (Real x) (INz x0)).
        - intros. eapply botbind_spec with (upd ρ'2 (Real x) (INz x0)).
          2:now apply forget_temps_correct, H0. split.
          + eexists. split. apply H1. intro. unfold upd.
            destruct (eq_dec id x), (eq_dec (Real id) (Real x)); try congruence. auto.
            rewrite <- (proj1 Hρ'ρ'2). auto.
          + intros y typsy. simpl. rewrite Cells.ACTree.gsspec.
            unfold upd, eq_dec, Cells.ACellDec. destruct Cells.ACTree.elt_eq.
            intros eq. inv eq. split; constructor. auto.
        - apply idnc_forget_correct; auto. }
    - eapply botbind_spec with (upd ρ'2 (Real x) (INf f)).
      + intros. eapply botbind_spec with (upd ρ'2 (Real x) (INf f)).
        2:now apply forget_temps_correct, H0. split.
        * eexists. split. apply H1. intro. unfold upd.
          destruct (eq_dec id x), (eq_dec (Real id) (Real x)); try congruence. auto.
          rewrite <- (proj1 Hρ'ρ'2). auto.
        * intros y typsy. simpl. rewrite Cells.ACTree.gsspec.
          unfold upd, eq_dec, Cells.ACellDec. destruct Cells.ACTree.elt_eq.
          intro. inv H2. split. reflexivity. constructor. auto.
      + apply idnc_assign_correct, simplify_expr_correct; auto.
    - eapply botbind_spec with (upd ρ'2 (Real x) (INf (Float.of_single f))).
      + intros. eapply botbind_spec with (upd ρ'2 (Real x) (INf (Float.of_single f))).
        2:now apply forget_temps_correct, H0. split.
        * eexists. split. apply H1. intro. unfold upd.
          destruct (eq_dec id x), (eq_dec (Real id) (Real x)); try congruence. auto.
          rewrite <- (proj1 Hρ'ρ'2). auto.
        * intros y typsy. simpl. rewrite Cells.ACTree.gsspec.
          unfold upd, eq_dec, Cells.ACellDec. destruct Cells.ACTree.elt_eq.
          intro. inv H2. split. reflexivity. constructor. auto.
      + apply idnc_assign_correct, simplify_expr_correct; auto.
  Qed.

  Definition assume (e:mexpr var0) (b:bool) (v:abt * typesmap) : (abt*typesmap)+⊥ :=
    let v := assign_vars_to_top e v in
    do (v, typs) <- v;
    let '(e, v, _, gi) := convert_expr e (NotBot v) false init_ghost_info in
    do v <- v;
    match normalize_expr (NotBot v) e (fst intrange) (snd intrangeu) intzwordsize gi with
    | (e, v, gi, true) =>
      do v <- v;
      do v <- idnc_assume e b v;
      do v <- forget_temps v gi;
      ret (v, typs)
    | _ =>
      do v <- forget_temps v gi;
      ret (v, typs)
    end.

  Lemma assume_correct:
    ∀ e ρ ab b,
      ρ ∈ γ ab ->
      of_bool beval_mexpr ρ e ->
      ρ ∈ γ (assume e b ab).
Proof.
    intros e ρ ab b Hρ EVAL. unfold assume, intrange, intrangeu, intzwordsize, fst, snd.
    eapply botbind_spec with ρ. 2:eauto using assign_vars_to_top_correct.
    intros. destruct a, H as [[ρ' [Hρ' Hρρ']] Htyps].
    pose proof (convert_expr_ok _ _ _ EVAL (NotBot a) _ _ Hρ' (gamma_gh_init _) Hρρ' false).
    destruct convert_expr as [[[e' v'] nerr'] gi].
    edestruct H as (ρ'2 & Hρ'2 & Hgi & Hρ'ρ'2 & x' & EVAL2 & REL); auto. clear H.
    eapply botbind_spec with ρ'2; eauto.
    assert (∃ z:Z, x' = INz z) by (destruct b; inv REL; eauto).
    destruct H; subst. intros.
    destruct (normalize_expr (NotBot a0) e' Int.min_signed Int.max_unsigned
                               Int.zwordsize gi) as [[[e1 ab1] gi'][]] eqn:EQNORM.
    edestruct normalize_expr_correct with (b:=true) as (ρ'3 & Hρ'2ρ'3 & Hρ'3 & Hgi' & x'0 & Hx'0bounded &Hx'x'0 & EVALx'0); eauto. reflexivity.
    - eapply botbind_spec with ρ'3, Hρ'3. intros. eapply botbind_spec with ρ'3. intros.
      + eapply botbind_spec with ρ'3, forget_temps_correct, H1.
        split. exists ρ'3. split. auto.
        intros. rewrite <- (proj1 Hρ'2ρ'3), <- (proj1 Hρ'ρ'2). auto. auto.
      + apply idnc_assume_correct; eauto.
        replace (if b return Zfast then F1 else F0) with (x'0:Zfast). auto.
        inv REL. 2:destruct b; discriminate.
        apply Int.eqm_samerepr in Hx'x'0. simpl in Hx'x'0. rewrite Hx'x'0 in H2.
        assert (Int.repr x'0 = if b then Int.one else Int.zero) by
            (destruct b; compute in H2; unfold Int.one, Int.zero; congruence).
        destruct (Coqlib.zlt x'0 0).
        * exfalso. apply (f_equal Int.signed) in H1.
          rewrite Int.signed_repr in H1. subst x'0. destruct b; discriminate.
          split. now intuition. assert (0 < Int.max_signed) by reflexivity. omega.
        * apply (f_equal Int.unsigned) in H1.
          rewrite Int.unsigned_repr in H1 by intuition. fastunwrap.
          destruct b; f_equal; apply H1.
    - eapply botbind_spec with ρ'2, forget_temps_correct, H. split.
      exists ρ'2. split. eauto. intros. rewrite <- (proj1 Hρ'ρ'2). auto. auto.
  Qed.

  Definition noerror (e:mexpr var0) (v:abt * typesmap) : bool :=
    let '(_, _, nerr, _) := convert_expr e (NotBot (fst v)) true init_ghost_info in
    nerr.

  Lemma noerror_correct:
    ∀ e ρ ab,
      ρ ∈ γ ab ->
      noerror e ab = true ->
      error_mexpr ρ e ->
      False.
Proof.
    unfold noerror. intros.
    destruct H as [[? [? ?]] ?].
    destruct (convert_expr e (NotBot (fst ab)) true init_ghost_info) as [[[] ?] ?] eqn:EQ. subst.
    eapply convert_expr_noerror with (v:=NotBot (fst ab)); eauto.
  Qed.

  Function zitv_concretize_int_aux (from: Z) (nb: N) (acc: ZSet.t) {measure N.to_nat nb} : ZSet.t :=
    match nb with
    | N0 => acc
    | Npos p =>
      let nb' := Pos.pred_N p in
      zitv_concretize_int_aux (Z.succ from) nb' (ZSet.add from acc)
    end.
Proof.
    intros from nb acc p ->. rewrite N.pos_pred_spec, Nnat.N2Nat.inj_pred. zify. intuition.
  Defined.

  Definition zitv_concretize_int (max: Nfast) (i: zitv) : ZSet.t+⊤+⊥ :=
    match i with
    | ZITV m M =>
      if Zfastleb m M then
        let n := Nfast_of_Zfast (Zfastsub M m) in
        if Nfastleb n max
        then NotBot (Just (zitv_concretize_int_aux m (N.succ n) ZSet.empty))
        else NotBot All
      else Bot
    end.

  Lemma zitv_concretize_int_correct:
    ∀ itv max z, γ itv z ->
      match zitv_concretize_int max itv with
      | NotBot (Just ab) => ZSet.mem z ab
      | NotBot All => True
      | Bot => False
      end.
Proof.
    intros [a b] max z ITV. unfold zitv_concretize_int. fastunwrap.
    destruct ITV as [A B]. destruct (Z.leb_spec a b). 2:omega.
    destruct N.leb. 2:exact I.
    remember ZSet.empty as init.
    assert (ZSet.mem z init \/ a <= z) by auto.
    clear Heqinit max A.
    remember (N.succ (Z.to_N (b-a))).
    assert (b-a+1= Z.of_N n).
    { subst n. rewrite N2Z.inj_succ, Z2N.id. Psatz.lia. omega. }
    clear Heqn H.
    functional induction (zitv_concretize_int_aux a n init).
    - destruct H0. apply H. zify; omega.
    - eapply IHt.
      rewrite ZSet.mem_add. destruct (Z.eq_dec z from). auto.
      destruct H0. auto. right. omega.
      rewrite N.pos_pred_spec, N2Z.inj_pred_max. zify; omega.
  Qed.

  Definition concretize_int (max: N) (e: mexpr var0) (ab: abt*typesmap) : ZSet.t+⊤+⊥ :=
    let ab := fst ab in
    let '(e, ab, _, gi) := convert_expr e (NotBot ab) false init_ghost_info in
    do ab <- ab;
    let e := simplify_expr e in
    let c := idnc_get_query_chan ab in
    do cong <- c.(get_congr) e;
    let '{| ZCongruences_defs.zc_rem := rem; ZCongruences_defs.zc_mod := modn |} := cong in
    let modz := Zfast_of_Nfast modn in
    if Nfastleb modn F0 then ret (Just (ZSetOp.singleton (Int.unsigned (Int.repr rem))))
    else
      do itv <- c.(AbIdealEnv.get_itv) e;
      do itv <- in_z itv;
      match itv with
      | All => ret All
      | Just (ZITV a b) =>
        let modn := Zfast_of_Nfast modn in
        let itvdiv := ZITV (Zfastopp (Zfastdiv (Zfastsub rem a) modn))
                           (Zfastdiv (Zfastsub b rem) modn) in
        do set <- zitv_concretize_int max itvdiv;
        ret (fmap (ZSetOp.map
           (fun x => Int.unsigned (Int.repr (Zfastadd (Zfastmul x modn) rem)))) set)
      end.

  Lemma concretize_int_correct :
    ∀ max e ρ ab, ρ ∈ γ ab ->
      (eval_mexpr ρ eMNint) ⊆ γ (concretize_int max e ab).
Proof.
    intros. unfold concretize_int. fastunwrap.
    simpl in H0. destruct H as [[ρ' [Hρ' REL]] _].
    destruct (convert_expr e (NotBot (fst ab)) false init_ghost_info) as [[[e' ab'] ?] ?] eqn:EQ.
    edestruct convert_expr_ok with (v:=NotBot (fst ab)) as (ρ'2 & Hρ'2 & Hgi & Hρ'ρ'2 & z & EVAL & RELz); eauto.
    inv RELz.
    eapply botbind_spec with ρ'2, Hρ'2. intros.
    eapply botbind_spec.
    2:eapply get_congr_correct, simplify_expr_correct; eauto;
    apply idnc_get_query_chan_correct; auto.
    intros [[zc_rem] [zc_mod]] (q & Hq). simpl in Hq.
    destruct zc_mod.
    - apply ZSetOp.mem_singleton. f_equal. f_equal. subst zc_rem. simpl. ring.
    - eapply botbind_spec.
      2:eapply AbIdealEnv.get_itv_correct, simplify_expr_correct;
        [apply idnc_get_query_chan_correct; eauto|eauto]; fail.
      intros. eapply botbind_spec; [|apply in_z_correct; eauto].
      intros. destruct a1 as [|[[] []]]. exact I.
      assert ((-q) ∈ γ (ZITV (- ((zc_rem - x0) / Z.pos p)) ((x1 - zc_rem) / Z.pos p))).
      { destruct H2. split; [apply -> Z.opp_le_mono|]; apply Z.div_le_lower_bound;
        unfold Zunwrap in *; Psatz.lia. }
      pose proof (zitv_concretize_int_correct _ max _ H3). fastunwrap. unfold Z.of_N.
      destruct zitv_concretize_int as [|[|set]]. auto. exact I. simpl.
      replace x with ((-q) * Z.pos p + zc_rem).
      eapply (ZSetOp.map_in (fun x0 => _)). auto. rewrite Hq. simpl. ring.
  Qed.

  Program Definition transfer_types (v2:abt) (typs1 typs2:typesmap) : abt :=
    let typs := Cells.ACTree.shcombine_diff (fun _ s1 s2 =>
      match s1, s2 return _ with
      | Some (Some s, MNTint), Some (None, MNTint) =>
        Some (Some s, MNTint)
      | Some (Some s, MNTint64), Some (None, MNTint64) =>
        Some (Some s, MNTint64)
      | _, _ => None
      end) _ typs1 typs2
    in
    Cells.ACTree.fold (fun ab x sty =>
              match sty return _ with
              | (Some s, ty) =>
                match get_itv (MEvar ty x) (ab, typs2) return _ with
                | (NotBot (MNIint All)) | NotBot (MNIint64 All) =>
                  match idnc_assign (Real x) (IEZitv
                               (match s, ty return _ with
                                | Signed, MNTint => signed_itv
                                | Unsigned, MNTint => unsigned_itv
                                | Signed, MNTint64 => signed64_itv
                                | Unsigned, MNTint64 => unsigned64_itv
                                | _, _ => signed_itv
                                end)) ab
                        return _
                  with Bot => ab
                  | NotBot ab => ab
                  end
                | _ => ab
                end
              | _ => ab
              end)
           typs v2.
Next Obligation.
destruct v as [[[] []]|]; auto. Qed.

  Lemma transfer_types_correct:
    ∀ typs1 typs2 v2 (ρ:var0 -> mach_num),
      ρ ∈ γ (v2, typs2) ->
      ρ ∈ γ (transfer_types v2 typs1 typs2).
Proof.
    intros. unfold transfer_types.
    match goal with |- context [Cells.ACTree.shcombine_diff ?f' ?Hf' typs1 typs2] => set (f:=f'); set (Hf:=Hf') end.
    assert (ρ ∈ γ (Cells.ACTree.shcombine_diff f Hf typs1 typs2)).
    { intros x s. rewrite Cells.ACTree.gshcombine_diff by auto.
      destruct H. red in H0.
      destruct (Cells.ACTree.get x typs1) as [[[] []]|] eqn:?, (Cells.ACTree.get x typs2) as [[[] []]|] eqn:?; simpl;
        intro eq; inv eq; (split; [constructor|apply (H0 _ _ Heqo0)]). }
    revert H0.
    assert (∀ v s0, Cells.ACTree.get v (Cells.ACTree.shcombine_diff f Hf typs1 typs2) = Some s0 <->
                    List.In (v, s0) (Cells.ACTree.elements (Cells.ACTree.shcombine_diff f Hf typs1 typs2))).
    { split. apply Cells.ACTree.elements_correct. apply Cells.ACTree.elements_complete. }
    simpl. unfold gamma_typs. setoid_rewrite H0. clear H0.
    rewrite Cells.ACTree.fold_spec, <- fold_left_rev_right. setoid_rewrite in_rev.
    match goal with
      | |- appcontext [fold_right ?f] => set (F:=f)
    end.
    induction (rev (Cells.ACTree.elements (Cells.ACTree.shcombine_diff f Hf typs1 typs2))); intros. apply H.
    lapply IHl. 2:intros; apply H0; simpl; auto.
    clear IHl. intro IHl.
    destruct a as [? [[] ?]]. 2:now auto. simpl. unfold F at 1. simpl.
    edestruct H0. left. eauto.
    pose proof (get_itv_correct (MEvar m e) ρ (_, typs2) (conj IHl (proj2 H)) (ρ e)
                                (eval_MEvar _ _ eq_refl H2)).
    pose proof IHl. destruct IHl as (ρ' & Hρ' & Hρρ').
    change Cells.ACTree.elt with var0 in *.
    destruct (get_itv (MEvar m e) (fold_right F v2 l, typs2)) as [|[[]|[]| |]]; auto;
    simpl in H2; inv H2; try discriminate; simpl;
    match goal with |- context [idnc_assign (Real e) ?e' ?ab] =>
      refine (let ASS' :=
        let val := match s with Signed => _ | _ => _ end in
        @idnc_assign_correct _ _ _ (Real e) e' ρ' val ab Hρ' _
              in (fun ASS => _) ASS'); [
                        (destruct s; constructor;
                         [apply signed_itv_correct|apply unsigned_itv_correct]) ||
                        (destruct s; constructor;
                         [apply signed64_itv_correct|apply unsigned64_itv_correct])
                       |clear ASS'; intros; destruct (idnc_assign (Real e) e' ab)]
    end; auto;
    eexists; (split; [apply ASS|]);
    intro y; unfold upd; (destruct @eq_dec; [|now auto]); inv e0; destruct s;
    rewrite <- H7;
    apply related_signed || apply related_unsigned ||
          apply related_signed_long || apply related_unsigned_long.
  Qed.

  Program Definition join_typs (typs1 typs2:typesmap) : typesmap :=
    Cells.ACTree.shcombine (fun _ s1 s2 =>
      match s1, s2 return _ with
      | Some (Some Signed, MNTint), Some (Some Signed | None, MNTint)
      | Some (Some Signed, MNTint64), Some (Some Signed | None, MNTint64)
      | Some (Some Unsigned, MNTint), Some (Some Unsigned | None, MNTint)
      | Some (Some Unsigned, MNTint64), Some (Some Unsigned | None, MNTint64)
        => s1
      | Some (None, MNTint), Some (Some Signed, MNTint)
      | Some (None, MNTint64), Some (Some Signed, MNTint64)
      | Some (None, MNTint), Some (Some Unsigned, MNTint)
      | Some (None, MNTint64), Some (Some Unsigned, MNTint64)
        => s2
      | Some (Some _, MNTfloat), Some (Some _, MNTfloat)
      | Some (Some _, MNTsingle), Some (Some _, MNTsingle)
      | Some (None, MNTint), Some (None, MNTint)
      | Some (None, MNTint64), Some (None, MNTint64)
      | Some (None, MNTfloat), Some (None, MNTfloat)
      | Some (None, MNTsingle), Some (None, MNTsingle)
        => s1
      | _, _ => None
      end) _ typs1 typs2.
Next Obligation.
destruct v as [[[[]|] []]|]; auto. Qed.

  Lemma join_typs_correct:
    ∀ a b, γ a ∪ γ b ⊆ γ (join_typs a b).
Proof.
    unfold join_typs. intros a b ρ Hρ k. rewrite Cells.ACTree.gshcombine.
    destruct Hρ as [Hρ|Hρ]; specialize (Hρ k);
    destruct (Cells.ACTree.get k a) as [[[[]|] []]|],
             (Cells.ACTree.get k b) as [[[[]|] []]|];
      try discriminate;
      destruct (Hρ _ eq_refl) as [? ?];
      try discriminate;
      intros ? eq; inv eq; simpl; auto.
  Qed.

  Instance top_ietme : top_op ((abt * typesmap)+⊥) :=
    do t <- top;
    ret (t, Cells.ACTree.empty _).

  Program Instance leb_ietme : leb_op (abt * typesmap) := fun ab1 ab2 =>
    (fst ab1) ⊑ (fst ab2) &&
    Cells.ACTree.shforall2 (fun _ s1 s2 =>
                   match s1, s2 return _ with
                   | _, None
                   | Some (_, MNTint), Some (_, MNTint)
                   | Some (_, MNTint64), Some (_, MNTint64)
                   | Some (_, MNTsingle), Some (None, MNTsingle)
                   | Some (_, MNTfloat), Some (None, MNTfloat)
                   | Some (Some _, MNTsingle), Some (Some _, MNTsingle)
                   | Some (Some _, MNTfloat), Some (Some _, MNTfloat) => true
                   | _, _ => false
                   end) _
      (snd ab1) (snd ab2).
Next Obligation.
destruct v as [[[] []]|]; auto. Qed.

  Instance join_ietme : join_op (abt * typesmap) ((abt * typesmap)+⊥) := fun ab1 ab2 =>
    let ab1' := transfer_types (fst ab1) (snd ab2) (snd ab1) in
    let ab2' := transfer_types (fst ab2) (snd ab1) (snd ab2) in
    do j <- ab1' ⊔ ab2';
    ret (j, join_typs (snd ab1) (snd ab2)).

  Instance widen_ietme : widen_op (abt_iter * typesmap+⊥) ((abt * typesmap)+⊥) :=
    {| widen ab1 ab2 :=
         let '(w1, w2) := fst ab1do (ab2, _) <- ab2; ret (ab2) in
         let typs :=
           match ab1 with
           | (_, Bot) => do (_, typs2) <- ab2; ret typs2
           | (_, NotBot typs1) =>
             match ab2 with
             | Bot => NotBot typs1
             | NotBot (_, typs2) => NotBot (join_typs typs1 typs2)
             end
           end
         in
         ((w1, typs),
          do w <- w2;
          do typs <- typs;
          ret (w, typs));
       init_iter x :=
         match x with
         | Bot => (init_iter Bot, Bot)
         | NotBot x => (init_iter (NotBot (fst x)), NotBot (snd x))
        end |}.

  Instance top_ietme_correct : top_op_correct ((abt * typesmap)+⊥) (var0->mach_num).
Proof.
    intro ρ. unfold top, top_ietme. eapply botbind_spec with (x:=_:var->ideal_num).
    2:apply top_correct.
    - split.
      exists (fun id =>
                match id with
                | Real id =>
                  match ρ id with
                  | MNint i => INz (Int.unsigned i)
                  | MNint64 i => INz (Int64.unsigned i)
                  | MNfloat f => INf f
                  | MNsingle f => INf (Float.of_single f)
                  end
                | Ghost _ => INz 0
                end).
      split. apply H.
      intro. destructid). apply related_unsigned. apply related_unsigned_long. constructor. constructor.
      intros x s. simpl. erewrite Cells.ACTree.gempty. discriminate.
  Qed.

  Instance leb_ietme_correct : leb_op_correct (abt * typesmap) (var0->mach_num).
Proof.
    unfold leb_op_correct. intros. unfold leb, leb_ietme in H.
    rewrite Bool.andb_true_iff in H. destruct H.
    destruct H0 as [[ρ []] ?]. split.
    - eexists ρ. split. eapply leb_correct; eauto. auto.
    - intros v ab Hvab. rewrite Cells.ACTree.shforall2_correct in H1. specialize (H1 v).
      unfold typesmap in *. rewrite Hvab in H1.
      specialize (H3 v).
      destruct ab as [? []], (Cells.ACTree.get v (snd a1)) as [[[] []]|]; try easy;
      destruct (H3 _ eq_refl), o; inv H4; inv H1; simpl in *; subst; auto.
  Qed.

  Instance join_ietme_correct : join_op_correct (abt * typesmap) ((abt * typesmap)+⊥) (var0->mach_num).
Proof.
    intros [x typsx] [y typsy] ρ [Hρ|Hρ]; assert (Hρtr:=Hρ);
    eapply transfer_types_correct in Hρtr;
    destruct Hρtr as (ρ' & Hρ' & Hρρ');
    eapply (botbind_spec _ (_:var->ideal_num)).
    - eexists. eexists ρ'. split. apply H. auto. apply join_typs_correct. left. apply Hρ.
    - apply join_correct. eauto.
    - eexists. eexists ρ'. split. apply H. auto. apply join_typs_correct. right. apply Hρ.
    - apply join_correct. eauto.
  Qed.

  Instance widen_ietme_correct :
    widen_op_correct (abt_iter * typesmap+⊥) ((abt * typesmap)+⊥) (var0->mach_num).
Proof.
    split.
    - destruct x. contradiction. destruct 1. split.
      destruct H as (? & ? & ?). eexists. split. apply init_iter_correct. eauto. auto. auto.
    - simpl. intros [x typsx] ytypsy ρ [(ρ' & Hρ' & Hρρ') Htyps].
      match goal with
      | |- context [?A ▽ ?B] =>
        pose proof (widen_incr A B ρ' Hρ'); destruct (AB)
      end.
      destruct typsx. contradiction.
      split. split. eexists. split. apply H. auto.
      + destruct ytypsy. apply Htyps. destruct x1. apply join_typs_correct. left. apply Htyps.
      + apply botbind_spec with ρ'. 2:apply H. intros.
        apply botbind_spec with ρ. intros. split. eexists. split. apply H0. auto. apply H1.
        destruct ytypsy. apply Htyps. destruct x1. apply join_typs_correct. left. apply Htyps.
  Qed.

  Instance idealenv_to_machineenv:
    ab_machine_env (var:=var0) (abt * typesmap) (abt_iter * typesmap+⊥) :=
    { assign := assign
    ; forget := forget
    ; assume := assume
    ; get_itv := fun e v => do v <- assign_vars_to_top e v; get_itv e v
    ; concretize_int := concretize_int
    ; noerror := noerror }.
Proof.
  - apply assign_correct.
  - apply forget_correct.
  - apply assume_correct.
  - intros e ρ ab H a E.
    generalize (assign_vars_to_top_correct _ _ _ _ E H).
    destruct (assign_vars_to_top _ _) as [ | v ]. intros ().
    intros K. exact (get_itv_correct _ _ _ K _ E).
  - apply concretize_int_correct.
  - apply noerror_correct.
  Defined.

End Functor.

Instance typesmap_TS : ToString typesmap :=
  fun _ => ""%string.

Existing Instances idealenv_to_machineenv.