Module IdealExpr


Require Import Utf8 Coqlib ToString DebugCminor Util TreeAl.
Require Import Integers Floats FastArith.
Require Import AdomLib Hash ShareTree.
Require Import ZIntervals Cells.

Inductive var :=
| Real: cell -> var
| Ghost: positive -> var.

Module var_EQ : TYPE_EQ
  with Definition s := var
  with Definition t := (cell + positive)%type.

  Definition s := var.
  Definition t := (cell + positive)%type.

  Definition t_of_s (x:s) : t :=
    match x with
    | Real x => inl x
    | Ghost x => inr x
    end.

  Definition s_of_t (x:t) : s :=
    match x with
    | inl x => Real x
    | inr x => Ghost x
    end.

  Lemma s_of_t_of_s : forall x : s, s_of_t (t_of_s x) = x.
Proof.
intros []; reflexivity. Qed.

  Lemma t_of_s_of_t: forall x : t, t_of_s (s_of_t x) = x.
Proof.
intros []; reflexivity. Qed.

  Instance eq : EqDec s.
Proof.
unfold EqDec. decide equality; apply eq_dec. Defined.
End var_EQ.

Instance hash_var : hashable var :=
  fun h x =>
    match x with
    | Real x => hash (MIX h F0) x
    | Ghost x => hash (MIX h F1) x
    end.

Instance ToString_var : ToString cell -> ToString var :=
  fun _ x =>
    match x with
    | Real x => to_string x
    | Ghost x => ("$" ++ to_string x ++ "$")%string
    end.

Module STPT := SumShareTree(ACTree)(PShareTree).
Module T := BijShareTree(var_EQ)(STPT).
Instance var_eq : EqDec var := T.elt_eq.

Inductive ideal_num :=
| INf : float -> ideal_num
| INz : Zfast -> ideal_num.

Inductive ideal_num_ty : Type :=
| INTz | INTf.

Inductive ideal_num_ty_gamma : gamma_op ideal_num_ty ideal_num :=
| INTSz : forall i, ideal_num_ty_gamma INTz (INz i)
| INTSf : forall f, ideal_num_ty_gamma INTf (INf f).
Existing Instance ideal_num_ty_gamma.

Inductive i_binary_operation : Type :=
| IOadd | IOsub | IOmul | IOdiv | IOmod
| IOand | IOor | IOxor | IOshl | IOshr
| IOcmp : comparison -> i_binary_operation
| IOaddf | IOsubf | IOmulf | IOdivf
| IOcmpf : comparison -> i_binary_operation.

Inductive i_unary_operation :=
| IOneg | IOnot
| IOnegf | IOabsf
| IOsingleoffloat | IOfloatofz | IOsingleofz | IOzoffloat.

Inductive iexpr : Type :=
| IEvar : ideal_num_ty -> var -> iexpr
| IEconst : ideal_num -> iexpr
| IEZitv : zitv -> iexpr
| IEunop : i_unary_operation -> iexpr -> iexpr
| IEbinop : i_binary_operation -> iexpr -> iexpr -> iexpr.

Inductive ite_iexpr : Type :=
| Leaf : iexpr -> ite_iexpr
| Ite : iexpr -> ite_iexpr -> ite_iexpr -> ite_iexpr.

Section eval_iexpr.
  Variable ρ: var -> ideal_num.

  Inductive eval_ibinop : i_binary_operation -> ideal_num -> ideal_num -> ideal_num -> Prop :=
  | eval_ibinop_IOadd : forall i1 i2:Z,
    eval_ibinop IOadd (INz i1) (INz i2) (INz (i1+i2))
  | eval_ibinop_IOsub : forall i1 i2:Z,
    eval_ibinop IOsub (INz i1) (INz i2) (INz (i1-i2))
  | eval_ibinop_IOmul : forall i1 i2:Z,
    eval_ibinop IOmul (INz i1) (INz i2) (INz (i1*i2))
  | eval_ibinop_IOdiv : forall i1 i2:Z,
    i2 <> 0%Z ->
    eval_ibinop IOdiv (INz i1) (INz i2) (INz (Z.quot i1 i2))
  | eval_ibinop_IOmod : forall i1 i2:Z,
    i2 <> 0%Z ->
    eval_ibinop IOmod (INz i1) (INz i2) (INz (Z.rem i1 i2))
  | eval_ibinop_IOand : forall i1 i2:Z,
    eval_ibinop IOand (INz i1) (INz i2) (INz (Z.land i1 i2))
  | eval_ibinop_IOor : forall i1 i2:Z,
    eval_ibinop IOor (INz i1) (INz i2) (INz (Z.lor i1 i2))
  | eval_ibinop_IOxor : forall i1 i2:Z,
    eval_ibinop IOxor (INz i1) (INz i2) (INz (Z.lxor i1 i2))
  | eval_ibinop_IOshl : forall i1 i2:Z,
    eval_ibinop IOshl (INz i1) (INz i2) (INz (Z.shiftl i1 i2))
  | eval_ibinop_IOshr : forall i1 i2:Z,
    eval_ibinop IOshr (INz i1) (INz i2) (INz (Z.shiftr i1 i2))
  | eval_ibinop_IOcmp : forall c (i1 i2:Z),
    eval_ibinop (IOcmp c) (INz i1) (INz i2)
                (INz (if (ZIntervals.Zcmp c i1 i2) then F1 else F0))
  | eval_ibinop_IOaddf : forall f1 f2,
    eval_ibinop IOaddf (INf f1) (INf f2) (INf (Float.add f1 f2))
  | eval_ibinop_IOsubf : forall f1 f2,
    eval_ibinop IOsubf (INf f1) (INf f2) (INf (Float.sub f1 f2))
  | eval_ibinop_IOmulf : forall f1 f2,
    eval_ibinop IOmulf (INf f1) (INf f2) (INf (Float.mul f1 f2))
  | eval_ibinop_IOdivf : forall f1 f2,
    eval_ibinop IOdivf (INf f1) (INf f2) (INf (Float.div f1 f2))
  | eval_ibinop_IOcmpf : forall c f1 f2,
    eval_ibinop (IOcmpf c) (INf f1) (INf f2)
                (INz (if (Float.cmp c f1 f2) then F1 else F0)).

  Inductive eval_iunop : i_unary_operation -> ideal_num -> ideal_num -> Prop :=
  | eval_iunop_IOneg : forall i:Z,
    eval_iunop IOneg (INz i) (INz (-i))
  | eval_iunop_IOnot : forall i:Z,
    eval_iunop IOnot (INz i) (INz (Z.lnot i))
  | eval_iunop_IOnegf : forall f,
    eval_iunop IOnegf (INf f) (INf (Float.neg f))
  | eval_iunop_IOabsf : forall f,
    eval_iunop IOabsf (INf f) (INf (Float.abs f))
  | eval_iunop_IOzoffloat : forall f (i:Z),
    Fappli_IEEE_extra.ZofB f = Some i ->
    eval_iunop IOzoffloat (INf f) (INz i)
  | eval_iunop_IOfloatofz : forall i:Z,
    eval_iunop IOfloatofz (INz i)
          (INf (Fappli_IEEE_extra.BofZ 53 1024 (@eq_refl _ Lt) (@eq_refl _ Lt) i))
  | eval_iunop_IOsingleofz : forall i:Z,
    eval_iunop IOsingleofz (INz i)
               (INf (Float.of_single (Fappli_IEEE_extra.BofZ 24 128 (@eq_refl _ Lt) (@eq_refl _ Lt) i)))
  | eval_iunop_IOsingleoffloat : forall f,
    eval_iunop IOsingleoffloat (INf f) (INf (Float.of_single (Float.to_single f))).

  Inductive eval_iexpr: iexpr -> ideal_num -> Prop :=
  | eval_IEvar: forall id i ty,
    ρ id = i ->
    i ∈ γ ty ->
    eval_iexpr (IEvar ty id) i
  | eval_IEconst: forall n,
    eval_iexpr (IEconst n) n
  | eval_IEZitv: forall itv (x:Z),
    x ∈ γ itv ->
    eval_iexpr (IEZitv itv) (INz x)
  | eval_IEunop: forall op a n r,
    eval_iexpr a n ->
    eval_iunop op n r->
    eval_iexpr (IEunop op a) r
  | eval_IEbinop: forall op a1 a2 n1 n2 n,
    eval_iexpr a1 n1 ->
    eval_iexpr a2 n2 ->
    eval_ibinop op n1 n2 n ->
    eval_iexpr (IEbinop op a1 a2) n.

  Inductive eval_ite_iexpr: ite_iexpr -> ideal_num -> Prop :=
  | eval_Leaf : forall e v,
    eval_iexpr e v ->
    eval_ite_iexpr (Leaf e) v
  | eval_Ite_0 : forall c et ef v,
    eval_iexpr c (INz 0) ->
    eval_ite_iexpr ef v ->
    eval_ite_iexpr (Ite c et ef) v
  | eval_Ite_1 : forall c et ef v,
    eval_iexpr c (INz 1) ->
    eval_ite_iexpr et v ->
    eval_ite_iexpr (Ite c et ef) v.

End eval_iexpr.

Definition iexpr_ty e : ideal_num_ty :=
  match e with
  | IEvar ty _ => ty
  | IEconst (INz _) => INTz
  | IEconst (INf _) => INTf
  | IEZitv _ => INTz
  | IEunop (IOneg | IOnot | IOzoffloat) _ => INTz
  | IEunop (IOnegf | IOabsf | IOsingleoffloat | IOfloatofz | IOsingleofz) _ => INTf
  | IEbinop (IOadd | IOsub | IOmul | IOdiv | IOmod | IOand | IOor | IOxor |
             IOshl | IOshr | IOcmp _ | IOcmpf _) _ _ => INTz
  | IEbinop (IOaddf | IOsubf | IOmulf | IOdivf) _ _ => INTf
  end.

Lemma iexpr_ty_correct:
  forall n e ρ, eval_iexpr ρ e n -> γ (iexpr_ty e) n.
Proof.
  destruct 1.
  - destruct H0; constructor.
  - destruct n; constructor.
  - constructor.
  - destruct H0; constructor.
  - destruct H1; constructor.
Qed.

Instance iexpr_eq : EqDec var -> EqDec iexpr.
Proof.
  unfold EqDec. intros. decide equality. decide equality.
  decide equality. apply Floats.Float.eq_dec. apply eq_dec.
  decide equality. apply eq_dec. apply eq_dec.
  decide equality. decide equality. decide equality. decide equality.
Defined.

Fixpoint iexpr_sz (e:iexpr) : Z :=
  match e with
  | IEvar _ _ | IEconst _ | IEZitv _ => 1
  | IEunop _ e => iexpr_sz e+1
  | IEbinop _ e1 e2 => iexpr_sz e1+iexpr_sz e2+1
  end.

Fixpoint ite_iexpr_sz (e:ite_iexpr) : Z :=
  match e with
  | Leaf e => iexpr_sz e
  | Ite c e1 e2 => iexpr_sz c+ite_iexpr_sz e1+ite_iexpr_sz e2+1
  end.

Fixpoint iexpr_det (e:iexpr) :=
  match e with
  | IEvar _ _ | IEconst _ => true
  | IEZitv _ => false
  | IEunop _ e => iexpr_det e
  | IEbinop _ e1 e2 => iexpr_det e1 && iexpr_det e2
  end.

Lemma iexpr_det_ok:
  forall ρ e n, eval_iexpr ρ e n ->
    forall m, eval_iexpr ρ e m ->
    iexpr_det e = true ->
    n = m.
Proof.
  induction 1; intros m EV'; inv EV'; simpl; intros DET.
  - auto.
  - auto.
  - discriminate.
  - specialize (IHeval_iexpr _ H3 DET). subst.
    destruct H5; inv H0; congruence.
  - apply Bool.andb_true_iff in DET. destruct DET.
    specialize (IHeval_iexpr1 _ H5 H2).
    specialize (IHeval_iexpr2 _ H7 H3).
    subst.
    destruct H8; inv H1; congruence.
Qed.

Local Open Scope N.

Instance hash_iexpr : hashable iexpr :=
  fix hash_iexpr (h:Nfast) (e:iexpr) :=
  match e with
  | IEvar _ x => hash (MIX h F1) x
  | IEconst (INz a) => hash (MIX h F2) a
  | IEconst (INf x) => hash (MIX h F3) (Fappli_IEEE_bits.bits_of_b64 x:Zfast)
  | IEunop op e =>
    let key :=
      match op with
      | IOneg => F4 | IOnot => F5 | IOnegf => F6
      | IOabsf => F7 | IOsingleoffloat => F8 | IOfloatofz => F9
      | IOsingleofz => F10 | IOzoffloat => F11
      end%N in
    hash_iexpr (MIX h key) e
  | IEbinop op e1 e2 =>
    let key :=
      match op with
      | IOadd => F12 | IOsub => F13 | IOmul => F14
      | IOdiv => F15 | IOmod => F16 | IOand => F17
      | IOor => F18 | IOxor => F19 | IOshl => F20
      | IOshr => F21 | IOcmp Ceq => F22 | IOcmp Cne => F23
      | IOcmp Clt => F24 | IOcmp Cle => F25 | IOcmp Cgt => F26
      | IOcmp Cge => F27 | IOaddf => F28 | IOsubf => F29
      | IOmulf => F30 | IOdivf => F31 | IOcmpf Ceq => F32
      | IOcmpf Cne => F33 | IOcmpf Clt => F34 | IOcmpf Cle => F35
      | IOcmpf Cgt => F36 | IOcmpf Cge => F37
      end%N in
    hash_iexpr (hash_iexpr (MIX h key) e1) e2
  | IEZitv (ZITV a b) => hash (hash (MIX h F38) a) b
  end.

Instance hash_ite_iexpr : hashable ite_iexpr :=
  fix hash_ite_iexpr (h:Nfast) (e:ite_iexpr) :=
  match e with
  | Leaf e => hash h e
  | Ite e1 e2 e3 =>
    hash_ite_iexpr (hash_ite_iexpr (hash (MIX h F39) e1) e2) e3
  end.

Local Open Scope string.

Definition string_of_ideal_num_ty (ty:ideal_num_ty) : string :=
  match ty with
  | INTz => "%z"
  | INTf => "%f"
  end.

Definition string_of_unop (op:i_unary_operation) :=
  match op with
  | IOneg => "-"
  | IOnot => "~"
  | IOnegf => "-."
  | IOabsf => "absf"
  | IOsingleoffloat => "singleoffloat"
  | IOfloatofz => "floatofz"
  | IOsingleofz => "singleofz"
  | IOzoffloat => "zoffloat"
  end.

Definition string_of_binop (op:i_binary_operation) :=
  match op with
  | IOadd => "+"
  | IOsub => "-"
  | IOmul => "*"
  | IOdiv => "/"
  | IOmod => "%"
  | IOand => "&"
  | IOor => "|"
  | IOxor => "^"
  | IOshl => "<<"
  | IOshr => ">>"
  | IOcmp c => string_of_comparison c
  | IOaddf => "+."
  | IOsubf => "-."
  | IOmulf => "*."
  | IOdivf => "/."
  | IOcmpf c => string_of_comparison c
  end.

Fixpoint string_of_iexpr `{ToString var} (e: iexpr) {struct e} : string :=
  match e with
  | IEvar ty v => to_string v ++ string_of_ideal_num_ty ty
  | IEconst (INz z) => to_string z
  | IEconst (INf x) => to_string x
  | IEZitv itv => to_string itv
  | IEunop op e' => string_of_unop op ++ "(" ++ string_of_iexpr e' ++ ")"
  | IEbinop op x y =>
    "(" ++ string_of_iexpr x ++ ")" ++ string_of_binop op ++
        "(" ++ string_of_iexpr y ++ ")"
  end.

Instance IExprToString : ToString var -> ToString iexpr := @string_of_iexpr.

Fixpoint string_of_iteiexpr `{ToString var} (e: ite_iexpr) {struct e} : string :=
  match e with
  | Leaf e => to_string e
  | Ite c et ef => "(" ++ string_of_iexpr c ++ ") ? (" ++
                       string_of_iteiexpr et ++ ") : (" ++
                       string_of_iteiexpr ef ++ ")"
  end.

Instance IteIExprToString : ToString var -> ToString ite_iexpr := @string_of_iteiexpr.

Hint Constructors eval_ibinop.
Hint Constructors eval_iunop.
Hint Constructors eval_iexpr.
Hint Constructors ideal_num_ty_gamma.