Module AbMachineEnv


Require Import Utf8.
Require Import Util.
Require Import Coqlib.
Require Import Ctypes.
Require Import Values.
Require Import Integers.
Require Import Floats.
Require Import Cminor.
Require Import AdomLib.
Require Import Sets.
Require Import FloatIntervals.
Require Import ZIntervals.

Set Implicit Arguments.
Section mexpr.
  Variable var : Type.

  Inductive mach_num : Type :=
  | MNint : int -> mach_num
  | MNint64 : int64 -> mach_num
  | MNfloat : float -> mach_num
  | MNsingle : float32 -> mach_num.

  Inductive mach_num_ty : Type :=
  | MNTint | MNTint64 | MNTfloat | MNTsingle.

  Inductive mach_num_ty_gamma : gamma_op mach_num_ty mach_num :=
  | MNTSint : ∀ i, mach_num_ty_gamma MNTint (MNint i)
  | MNTSint64 : ∀ i, mach_num_ty_gamma MNTint64 (MNint64 i)
  | MNTSfloat : ∀ f, mach_num_ty_gamma MNTfloat (MNfloat f)
  | MNTSsingle : ∀ f, mach_num_ty_gamma MNTsingle (MNsingle f).
  Global Existing Instance mach_num_ty_gamma.

  Hint Constructors mach_num_ty_gamma.

  Inductive mexpr : Type :=
  | MEvar : mach_num_ty -> var -> mexpr
  | MEconst : mach_num -> mexpr
  | MEunop : unary_operation -> mexpr -> mexpr
  | MEbinop : binary_operation -> mexpr -> mexpr -> mexpr.

  Fixpoint me_free_var (me: mexpr) : ℘(var) :=
    match me with
    | MEvar _ v => eq v
    | MEconst _ => ∅
    | MEunop _ me' => me_free_var me'
    | MEbinop _ meme₂ => me_free_var me₁ ∪ me_free_var me
    end.

  Section eval_mexpr.

    Variable ρ: var -> mach_num.

    Inductive is_bool : mach_num -> Prop :=
    | is_bool_false: is_bool (MNint Int.zero)
    | is_bool_true: is_bool (MNint Int.one).

    Definition Ntrue: mach_num := MNint Int.one.
    Definition Nfalse: mach_num := MNint Int.zero.

    Definition of_bool (b: bool): mach_num :=
      if b then Ntrue else Nfalse.

    Inductive eval_munop: unary_operation -> mach_num -> mach_num -> Prop :=
    | eval_munop_Ocast8unsigned: ∀ i,
      eval_munop Ocast8unsigned (MNint i) (MNint (Int.zero_ext 8 i))
    | eval_munop_Ocast8signed: ∀ i,
      eval_munop Ocast8signed (MNint i) (MNint (Int.sign_ext 8 i))
    | eval_munop_Ocast16unsigned: ∀ i,
      eval_munop Ocast16unsigned (MNint i) (MNint (Int.zero_ext 16 i))
    | eval_munop_Ocast16signed: ∀ i,
      eval_munop Ocast16signed (MNint i) (MNint (Int.sign_ext 16 i))
    | eval_munop_Onegint : ∀ i,
      eval_munop Onegint (MNint i) (MNint (Int.neg i))
    | eval_munop_Onotint : ∀ i,
      eval_munop Onotint (MNint i) (MNint (Int.not i))
    | eval_munop_Onegf : ∀ f,
      eval_munop Onegf (MNfloat f) (MNfloat (Float.neg f))
    | eval_munop_Onegfs : ∀ f,
      eval_munop Onegfs (MNsingle f) (MNsingle (Float32.neg f))
    | eval_munop_Oabsf : ∀ f,
      eval_munop Oabsf (MNfloat f) (MNfloat (Float.abs f))
    | eval_munop_Oabsfs : ∀ f,
      eval_munop Oabsfs (MNsingle f) (MNsingle (Float32.abs f))
    | eval_munop_Osingleoffloat : ∀ f,
      eval_munop Osingleoffloat (MNfloat f) (MNsingle (Float.to_single f))
    | eval_munop_Ofloatofsingle : ∀ f,
      eval_munop Ofloatofsingle (MNsingle f) (MNfloat (Float.of_single f))
    | eval_munop_Ointoffloat : ∀ f i,
      Float.to_int f = Some i ->
      eval_munop Ointoffloat (MNfloat f) (MNint i)
    | eval_munop_Ointofsingle : ∀ f i,
      Float32.to_int f = Some i ->
      eval_munop Ointofsingle (MNsingle f) (MNint i)
    | eval_munop_Ointuoffloat : ∀ f i,
      Float.to_intu f = Some i ->
      eval_munop Ointuoffloat (MNfloat f) (MNint i)
    | eval_munop_Ointuofsingle : ∀ f i,
      Float32.to_intu f = Some i ->
      eval_munop Ointuofsingle (MNsingle f) (MNint i)
    | eval_munop_Ofloatofint : ∀ i,
      eval_munop Ofloatofint (MNint i) (MNfloat (Float.of_int i))
    | eval_munop_Osingleofint : ∀ i,
      eval_munop Osingleofint (MNint i) (MNsingle (Float32.of_int i))
    | eval_munop_Ofloatofintu : ∀ i,
      eval_munop Ofloatofintu (MNint i) (MNfloat (Float.of_intu i))
    | eval_munop_Osingleofintu : ∀ i,
      eval_munop Osingleofintu (MNint i) (MNsingle (Float32.of_intu i))
    | eval_munop_Onegl : ∀ i,
      eval_munop Onegl (MNint64 i) (MNint64 (Int64.neg i))
    | eval_munop_Onotl : ∀ i,
      eval_munop Onotl (MNint64 i) (MNint64 (Int64.not i))
    | eval_munop_Ointoflong : ∀ i,
      eval_munop Ointoflong (MNint64 i) (MNint (Int64.loword i))
    | eval_munop_Olongofint : ∀ i,
      eval_munop Olongofint (MNint i) (MNint64 (Int64.repr (Int.signed i)))
    | eval_munop_Olongofintu : ∀ i,
      eval_munop Olongofintu (MNint i) (MNint64 (Int64.repr (Int.unsigned i)))
    | eval_munop_Olongoffloat : ∀ f i,
      Float.to_long f = Some i ->
      eval_munop Olongoffloat (MNfloat f) (MNint64 i)
    | eval_munop_Olonguoffloat : ∀ f i,
      Float.to_longu f = Some i ->
      eval_munop Olonguoffloat (MNfloat f) (MNint64 i)
    | eval_munop_Olongofsingle : ∀ f i,
      Float32.to_long f = Some i ->
      eval_munop Olongofsingle (MNsingle f) (MNint64 i)
    | eval_munop_Olonguofsingle : ∀ f i,
      Float32.to_longu f = Some i ->
      eval_munop Olonguofsingle (MNsingle f) (MNint64 i)
    | eval_munop_Ofloatoflong : ∀ i,
      eval_munop Ofloatoflong (MNint64 i) (MNfloat (Float.of_long i))
    | eval_munop_Osingleoflong : ∀ i,
      eval_munop Osingleoflong (MNint64 i) (MNsingle (Float32.of_long i))
    | eval_munop_Ofloatoflongu : ∀ i,
      eval_munop Ofloatoflongu (MNint64 i) (MNfloat (Float.of_longu i))
    | eval_munop_Osingleoflongu : ∀ i,
      eval_munop Osingleoflongu (MNint64 i) (MNsingle (Float32.of_longu i)).

    Inductive eval_mbinop : binary_operation -> mach_num -> mach_num -> mach_num -> Prop :=
    | eval_mbinop_Oadd : ∀ i1 i2,
      eval_mbinop Oadd (MNint i1) (MNint i2) (MNint (Int.add i1 i2))
    | eval_mbinop_Osub : ∀ i1 i2,
      eval_mbinop Osub (MNint i1) (MNint i2) (MNint (Int.sub i1 i2))
    | eval_mbinop_Omul : ∀ i1 i2,
      eval_mbinop Omul (MNint i1) (MNint i2) (MNint (Int.mul i1 i2))
    | eval_mbinop_Odiv : ∀ i1 i2,
      Int.eq i2 Int.zero = false ->
      (Int.eq i1 (Int.repr Int.min_signed) = false
        \/ Int.eq i2 Int.mone = false) ->
      eval_mbinop Odiv (MNint i1) (MNint i2) (MNint (Int.divs i1 i2))
    | eval_mbinop_Odivu : ∀ i1 i2,
      Int.eq i2 Int.zero = false ->
      eval_mbinop Odivu (MNint i1) (MNint i2) (MNint (Int.divu i1 i2))
    | eval_mbinop_Omod : ∀ i1 i2,
      Int.eq i2 Int.zero = false ->
      (Int.eq i1 (Int.repr Int.min_signed) = false \/
        Int.eq i2 Int.mone = false) ->
      eval_mbinop Omod (MNint i1) (MNint i2) (MNint (Int.mods i1 i2))
    | eval_mbinop_Omodu : ∀ i1 i2,
      Int.eq i2 Int.zero = false ->
      eval_mbinop Omodu (MNint i1) (MNint i2) (MNint (Int.modu i1 i2))
    | eval_mbinop_Oand : ∀ i1 i2,
      eval_mbinop Oand (MNint i1) (MNint i2) (MNint (Int.and i1 i2))
    | eval_mbinop_Oor : ∀ i1 i2,
      eval_mbinop Oor (MNint i1) (MNint i2) (MNint (Int.or i1 i2))
    | eval_mbinop_Oxor : ∀ i1 i2,
      eval_mbinop Oxor (MNint i1) (MNint i2) (MNint (Int.xor i1 i2))
    | eval_mbinop_Oshl : ∀ i1 i2,
      Int.ltu i2 Int.iwordsize = true ->
      eval_mbinop Oshl (MNint i1) (MNint i2) (MNint (Int.shl i1 i2))
    | eval_mbinop_Oshr : ∀ i1 i2,
      Int.ltu i2 Int.iwordsize = true ->
      eval_mbinop Oshr (MNint i1) (MNint i2) (MNint (Int.shr i1 i2))
    | eval_mbinop_Oshru : ∀ i1 i2,
      Int.ltu i2 Int.iwordsize = true ->
      eval_mbinop Oshru (MNint i1) (MNint i2) (MNint (Int.shru i1 i2))

    | eval_mbinop_Oaddf : ∀ f1 f2,
      eval_mbinop Oaddf (MNfloat f1) (MNfloat f2) (MNfloat (Float.add f1 f2))
    | eval_mbinop_Osubf : ∀ f1 f2,
      eval_mbinop Osubf (MNfloat f1) (MNfloat f2) (MNfloat (Float.sub f1 f2))
    | eval_mbinop_Omulf : ∀ f1 f2,
      eval_mbinop Omulf (MNfloat f1) (MNfloat f2) (MNfloat (Float.mul f1 f2))
    | eval_mbinop_Odivf : ∀ f1 f2,
      eval_mbinop Odivf (MNfloat f1) (MNfloat f2) (MNfloat (Float.div f1 f2))

    | eval_mbinop_Oaddfs : ∀ f1 f2,
      eval_mbinop Oaddfs (MNsingle f1) (MNsingle f2) (MNsingle (Float32.add f1 f2))
    | eval_mbinop_Osubfs : ∀ f1 f2,
      eval_mbinop Osubfs (MNsingle f1) (MNsingle f2) (MNsingle (Float32.sub f1 f2))
    | eval_mbinop_Omulfs : ∀ f1 f2,
      eval_mbinop Omulfs (MNsingle f1) (MNsingle f2) (MNsingle (Float32.mul f1 f2))
    | eval_mbinop_Odivfs : ∀ f1 f2,
      eval_mbinop Odivfs (MNsingle f1) (MNsingle f2) (MNsingle (Float32.div f1 f2))

    | eval_mbinop_Oaddl : ∀ i1 i2,
      eval_mbinop Oaddl (MNint64 i1) (MNint64 i2) (MNint64 (Int64.add i1 i2))
    | eval_mbinop_Osubl : ∀ i1 i2,
      eval_mbinop Osubl (MNint64 i1) (MNint64 i2) (MNint64 (Int64.sub i1 i2))
    | eval_mbinop_Omull : ∀ i1 i2,
      eval_mbinop Omull (MNint64 i1) (MNint64 i2) (MNint64 (Int64.mul i1 i2))
    | eval_mbinop_Odivl : ∀ i1 i2,
      Int64.eq i2 Int64.zero = false ->
      (Int64.eq i1 (Int64.repr Int64.min_signed) = false
        \/ Int64.eq i2 Int64.mone = false) ->
      eval_mbinop Odivl (MNint64 i1) (MNint64 i2) (MNint64 (Int64.divs i1 i2))
    | eval_mbinop_Odivlu : ∀ i1 i2,
      Int64.eq i2 Int64.zero = false ->
      eval_mbinop Odivlu (MNint64 i1) (MNint64 i2) (MNint64 (Int64.divu i1 i2))
    | eval_mbinop_Omodl : ∀ i1 i2,
      Int64.eq i2 Int64.zero = false ->
      (Int64.eq i1 (Int64.repr Int64.min_signed) = false \/
        Int64.eq i2 Int64.mone = false) ->
      eval_mbinop Omodl (MNint64 i1) (MNint64 i2) (MNint64 (Int64.mods i1 i2))
    | eval_mbinop_Omodlu : ∀ i1 i2,
      Int64.eq i2 Int64.zero = false ->
      eval_mbinop Omodlu (MNint64 i1) (MNint64 i2) (MNint64 (Int64.modu i1 i2))
    | eval_mbinop_Oandl : ∀ i1 i2,
      eval_mbinop Oandl (MNint64 i1) (MNint64 i2) (MNint64 (Int64.and i1 i2))
    | eval_mbinop_Oorl : ∀ i1 i2,
      eval_mbinop Oorl (MNint64 i1) (MNint64 i2) (MNint64 (Int64.or i1 i2))
    | eval_mbinop_Oxorl : ∀ i1 i2,
      eval_mbinop Oxorl (MNint64 i1) (MNint64 i2) (MNint64 (Int64.xor i1 i2))
    | eval_mbinop_Oshll : ∀ i1 i2,
      Int.ltu i2 Int64.iwordsize' = true ->
      eval_mbinop Oshll (MNint64 i1) (MNint i2) (MNint64 (Int64.shl' i1 i2))
    | eval_mbinop_Oshrl : ∀ i1 i2,
      Int.ltu i2 Int64.iwordsize' = true ->
      eval_mbinop Oshrl (MNint64 i1) (MNint i2) (MNint64 (Int64.shr' i1 i2))
    | eval_mbinop_Oshrlu : ∀ i1 i2,
      Int.ltu i2 Int64.iwordsize' = true ->
      eval_mbinop Oshrlu (MNint64 i1) (MNint i2) (MNint64 (Int64.shru' i1 i2))

    | eval_mbinop_Ocmp : ∀ c i1 i2,
      eval_mbinop (Ocmp c) (MNint i1) (MNint i2) (of_bool (Int.cmp c i1 i2))
    | eval_mbinop_Ocmpu : ∀ c i1 i2,
      eval_mbinop (Ocmpu c) (MNint i1) (MNint i2) (of_bool (Int.cmpu c i1 i2))
    | eval_mbinop_Ocmpl : ∀ c i1 i2,
      eval_mbinop (Ocmpl c) (MNint64 i1) (MNint64 i2) (of_bool (Int64.cmp c i1 i2))
    | eval_mbinop_Ocmplu : ∀ c i1 i2,
      eval_mbinop (Ocmplu c) (MNint64 i1) (MNint64 i2) (of_bool (Int64.cmpu c i1 i2))
    | eval_mbinop_Ocmpf : ∀ c f1 f2,
      eval_mbinop (Ocmpf c) (MNfloat f1) (MNfloat f2) (of_bool (Float.cmp c f1 f2))
    | eval_mbinop_Ocmpfs : ∀ c f1 f2,
      eval_mbinop (Ocmpfs c) (MNsingle f1) (MNsingle f2) (of_bool (Float32.cmp c f1 f2)).

    Inductive eval_mexpr: mexpr -> mach_num -> Prop :=
    | eval_MEvar : ∀ id i ty,
      ρ id = i -> γ ty i ->
      eval_mexpr (MEvar ty id) i
    | eval_MEconst: ∀ n,
      eval_mexpr (MEconst n) n
    | eval_MEunop: ∀ op a1 n1 n,
      eval_mexpr a1 n1 ->
      eval_munop op n1 n ->
      eval_mexpr (MEunop op a1) n
    | eval_Ebinop: ∀ op a1 a2 n1 n2 n,
      eval_mexpr a1 n1 ->
      eval_mexpr a2 n2 ->
      eval_mbinop op n1 n2 n ->
      eval_mexpr (MEbinop op a1 a2) n.

    Inductive error_munop: unary_operation -> mach_num -> Prop :=
    | error_munop_Ointoffloat: ∀ f, Float.to_int f = None ->
      error_munop Ointoffloat (MNfloat f)
    | error_munop_Ointuoffloat: ∀ f, Float.to_intu f = None ->
      error_munop Ointuoffloat (MNfloat f)
    | error_munop_Olongoffloat: ∀ f, Float.to_long f = None ->
      error_munop Olongoffloat (MNfloat f)
    | error_munop_Olonguoffloat: ∀ f, Float.to_longu f = None ->
      error_munop Olonguoffloat (MNfloat f)
    | error_munop_Ointofsingle: ∀ f, Float32.to_int f = None ->
      error_munop Ointofsingle (MNsingle f)
    | error_munop_Ointuofsingle: ∀ f, Float32.to_intu f = None ->
      error_munop Ointuofsingle (MNsingle f)
    | error_munop_Olongofsingle: ∀ f, Float32.to_long f = None ->
      error_munop Olongofsingle (MNsingle f)
    | error_munop_Olonguofsingle: ∀ f, Float32.to_longu f = None ->
      error_munop Olonguofsingle (MNsingle f).

    Inductive error_mbinop: binary_operation -> mach_num -> mach_num -> Prop :=
    | error_mbinop_Odiv_0: ∀ i,
      error_mbinop Odiv (MNint i) (MNint Int.zero)
    | error_mbinop_Odiv_minint:
      error_mbinop Odiv (MNint (Int.repr Int.min_signed)) (MNint Int.mone)
    | error_mbinop_Odivu_0: ∀ i,
      error_mbinop Odivu (MNint i) (MNint Int.zero)
    | error_mbinop_Omod_0: ∀ i,
      error_mbinop Omod (MNint i) (MNint Int.zero)
    | error_mbinop_Omod_minint:
      error_mbinop Omod (MNint (Int.repr Int.min_signed)) (MNint Int.mone)
    | error_mbinop_Omodu_0: ∀ i,
      error_mbinop Omodu (MNint i) (MNint Int.zero)
    | error_mbinop_Oshl: ∀ i1 i2,
      Int.ltu i2 Int.iwordsize = false ->
      error_mbinop Oshl (MNint i1) (MNint i2)
    | error_mbinop_Oshr: ∀ i1 i2,
      Int.ltu i2 Int.iwordsize = false ->
      error_mbinop Oshr (MNint i1) (MNint i2)
    | error_mbinop_Oshru: ∀ i1 i2,
      Int.ltu i2 Int.iwordsize = false ->
      error_mbinop Oshru (MNint i1) (MNint i2)
    | error_mbinop_Odivl_0: ∀ i,
      error_mbinop Odivl (MNint64 i) (MNint64 Int64.zero)
    | error_mbinop_Odivl_minint:
      error_mbinop Odivl (MNint64 (Int64.repr Int64.min_signed)) (MNint64 Int64.mone)
    | error_mbinop_Odivlu_0: ∀ i,
      error_mbinop Odivlu (MNint64 i) (MNint64 Int64.zero)
    | error_mbinop_Omodl_0: ∀ i,
      error_mbinop Omodl (MNint64 i) (MNint64 Int64.zero)
    | error_mbinop_Omodl_minint:
      error_mbinop Omodl (MNint64 (Int64.repr Int64.min_signed)) (MNint64 Int64.mone)
    | error_mbinop_Omodlu_0: ∀ i,
      error_mbinop Omodlu (MNint64 i) (MNint64 Int64.zero)
    | error_mbinop_Oshll: ∀ i1 i2,
      Int.ltu i2 Int64.iwordsize' = false ->
      error_mbinop Oshll (MNint64 i1) (MNint i2)
    | error_mbinop_Oshrl: ∀ i1 i2,
      Int.ltu i2 Int64.iwordsize' = false ->
      error_mbinop Oshrl (MNint64 i1) (MNint i2)
    | error_mbinop_Oshrlu: ∀ i1 i2,
      Int.ltu i2 Int64.iwordsize' = false ->
      error_mbinop Oshrlu (MNint64 i1) (MNint i2).

    Inductive error_mexpr: mexpr -> Prop :=
    | error_MEunop: ∀ op a1 n1,
      eval_mexpr a1 n1 ->
      error_munop op n1 ->
      error_mexpr (MEunop op a1)
    | error_MEunop_rec: ∀ op a1,
      error_mexpr a1 ->
      error_mexpr (MEunop op a1)
    | error_MEbinop: ∀ op a1 n1 a2 n2,
      eval_mexpr a1 n1 ->
      eval_mexpr a2 n2 ->
      error_mbinop op n1 n2 ->
      error_mexpr (MEbinop op a1 a2)
    | error_MEbinop_rec1: ∀ op a1 a2,
      error_mexpr a1 ->
      error_mexpr (MEbinop op a1 a2)
    | error_MEbinop_rec2: ∀ op a1 n1 a2,
      eval_mexpr a1 n1 ->
      error_mexpr a2 ->
      error_mexpr (MEbinop op a1 a2).

  End eval_mexpr.

  Lemma eval_mexpr_fv ρ ρ' me :
    (∀ x, xme_free_var me → ρ x = ρ' x) →
    eval_mexpr ρ meeval_mexpr ρ' me.
Proof.
    induction me; simpl; intros H q Hq; inv Hq; econstructor (eauto).
    specialize (H _ eq_refl). easy.
  Qed.

  Definition mexpr_ty e : mach_num_ty :=
    match e with
      | MEvar ty _ => ty
      | MEconst (MNint _) => MNTint
      | MEconst (MNint64 _) => MNTint64
      | MEconst (MNfloat _) => MNTfloat
      | MEconst (MNsingle _) => MNTsingle
      | MEunop (Ocast8unsigned | Ocast8signed | Ocast16unsigned | Ocast16signed |
                Onegint | Onotint | Ointoffloat | Ointuoffloat |
                Ointofsingle | Ointuofsingle | Ointoflong) _ =>
        MNTint
      | MEunop (Onegf | Oabsf | Ofloatofsingle | Ofloatofint | Ofloatofintu |
                Ofloatoflong | Ofloatoflongu) _ =>
        MNTfloat
      | MEunop (Onegfs | Oabsfs | Osingleoffloat | Osingleofint | Osingleofintu |
                Osingleoflong | Osingleoflongu) _ =>
        MNTsingle
      | MEunop (Onegl | Onotl | Olongofint | Olongofintu |
                Olongoffloat | Olonguoffloat | Olongofsingle | Olonguofsingle) _ =>
        MNTint64
      | MEbinop (Oadd | Osub | Omul | Odiv | Odivu | Omod | Omodu | Oand | Oor |
                 Oxor | Oshl | Oshr | Oshru |
                 Ocmp _ | Ocmpu _ | Ocmpf _ | Ocmpfs _ | Ocmpl _ | Ocmplu _ ) _ _ =>
        MNTint
      | MEbinop (Oaddf | Osubf | Omulf | Odivf) _ _ => MNTfloat
      | MEbinop (Oaddfs | Osubfs | Omulfs | Odivfs) _ _ => MNTsingle
      | MEbinop (Oaddl | Osubl | Omull | Odivl | Odivlu |
                 Omodl | Omodlu | Oandl | Oorl | Oxorl | Oshll | Oshrl | Oshrlu) _ _ =>
        MNTint64
    end.

  Lemma mexpr_ty_correct:
    ∀ n e ρ, eval_mexpr ρ e n -> γ (mexpr_ty e) n.
Proof.
    destruct 1.
    - destruct H0; constructor.
    - destruct n; constructor.
    - destruct H0; constructor.
    - destruct H1; try constructor; simpl;
      match goal with |- context [of_bool ?b] => destruct b end; constructor.
  Qed.

  Definition me_const_i (i: int) : mexpr := MEconst (MNint i).
  Definition me_const_l (i: int64) : mexpr := MEconst (MNint64 i).
  Definition me_const_f (f: float) : mexpr := MEconst (MNfloat f).
  Definition me_const_s (f: float32) : mexpr := MEconst (MNsingle f).

End mexpr.


Inductive mach_num_itv : Type :=
| MNIint : zitv+⊤ -> mach_num_itv
| MNIint64 : zitv+⊤ -> mach_num_itv
| MNIfloat : fitv+⊤ -> mach_num_itv
| MNIsingle : fitv+⊤ -> mach_num_itv.

Inductive mach_num_itv_gamma : gamma_op mach_num_itv mach_num :=
| MNIint_ok : ∀ itv i,
    i ∈ γ itv -> mach_num_itv_gamma (MNIint itv) (MNint (Int.repr i))
| MNIint64_ok : ∀ itv i,
    i ∈ γ itv -> mach_num_itv_gamma (MNIint64 itv) (MNint64 (Int64.repr i))
| MNIfloat_ok : ∀ itv f,
    f ∈ γ itv ->
    mach_num_itv_gamma (MNIfloat itv) (MNfloat f)
| MNIsingle_ok : ∀ itv f,
    Float.of_single f ∈ γ itv ->
    mach_num_itv_gamma (MNIsingle itv) (MNsingle f).
Existing Instance mach_num_itv_gamma.

Unset Elimination Schemes.
Domain of environments using machine numbers.

Class ab_machine_env (var t iter_t: Type) `{EqDec var} : Type := {
  mach_top:> top_op (t+⊥);
  mach_leb:> leb_op t;
  mach_join:> join_op t (t+⊥);
  mach_widen:> widen_op iter_t (t+⊥);

  assign: var -> mexpr var -> t -> t+⊥;
  forget: var -> t -> t+⊥;
  assume: mexpr var -> bool -> t -> t+⊥;
  get_itv: mexpr var -> t -> mach_num_itv+⊥;
  concretize_int: N -> mexpr var -> t -> ZSet.t+⊤+⊥;
  noerror: mexpr var -> t -> bool;

  mach_gamma:> gamma_op t (var -> mach_num);
  mach_gamma_iter:> gamma_op iter_t (var -> mach_num);

  mach_top_correct:> top_op_correct (t+⊥) (var -> mach_num);
  mach_leb_correct:> leb_op_correct t (var -> mach_num);
  mach_join_correct:> join_op_correct t (t+⊥) (var -> mach_num);
  mach_widen_correct:> widen_op_correct iter_t (t+⊥) (var -> mach_num);
  assign_correct: ∀ x e ρ n ab,
    ρ ∈ γ ab ->
    neval_mexpr ρ e ->
    ρ +[x => n] ∈ γ (assign x e ab);
  forget_correct: ∀ x ρ n ab,
    ρ ∈ γ ab ->
    ρ +[x => n] ∈ γ (forget x ab);
  assume_correct: ∀ e ρ ab b,
    ρ ∈ γ ab ->
    of_bool beval_mexpr ρ e ->
    ρ ∈ γ (assume e b ab);
  get_itv_correct: ∀ e ρ ab,
    ρ ∈ γ ab ->
    (eval_mexpr ρ e) ⊆ γ (get_itv e ab);
  concretize_int_correct: ∀ n e ρ ab,
    ρ ∈ γ ab ->
    (eval_mexpr ρ eMNint) ⊆ γ (concretize_int n e ab);
  noerror_correct: ∀ e ρ ab,
    ρ ∈ γ ab ->
    noerror e ab = true ->
    error_mexpr ρ e ->
    False
}.

Section MORE.

Context (var t iter_t: Type) `(EQ: EqDec var) `(D: @ab_machine_env var t iter_t EQ).

Lemma forget_correct':
  ∀ (x: var) (ρ ρ': varmach_num) (ab: t),
    ρ ∈ γ(ab) →
    (∀ y, yx → ρ'(y) = ρ(y)) →
    ρ' ∈ γ (forget x ab).
Proof.
  intros x ρ ρ' ab H3 H2.
  erewrite (Axioms.extensionality ρ').
  apply forget_correct; eauto.
  intros z. unfold upd.
  destruct @eq_dec as [ -> | ME ]. reflexivity.
  apply H2, ME.
Qed.

Lemma assume_bot:
  ∀ e b (ab: t),
    is_bot (assume e b ab) = true
    ∀ ρ, ρ ∈ γ(ab) →
         ¬ of_bool(b) ∈ eval_mexpr ρ e.
Proof.
  intros e b ab B ρ G K.
  erewrite is_bot_spec in B. discriminate.
  apply assume_correct; eauto.
Qed.

Definition is_null (ab: t) (me: mexpr var) : bool :=
  is_bot (assume (MEbinop (Ocmpu Ceq) me (me_const_i _ Int.zero)) false ab).

Lemma is_null_correct (ab: t) ρ (G: ρ ∈ γ ab) me :
  is_null ab me = true
  ∀ i, MNint ieval_mexpr ρ mei = Int.zero.
Proof.
  unfold is_null.
  intros N i Hi. generalize (assume_bot false ab N G). clear N.
  simpl. intros K.
  destruct (Int.cmpu Ceq i Int.zero) eqn: Hz.
  simpl in Hz. generalize (Int.eq_spec i Int.zero). rewrite Hz. exact id.
  elim K. econstructor; eauto. vauto.
  change Nfalse with (of_bool false). rewrite <- Hz. vauto.
Qed.

End MORE.