Module BoxDomain

Require Import Coqlib Utf8.
Require Import Maps.
Require Import AST.
Require Import Integers.
Require Import Floats.
Require Import Values.
Require Import Globalenvs.
Require Import Op.
Require Import Registers.
Require Import Cminor.
Require Import Memory.
Require Import DLib.
Require Import LatticeSignatures.
Require Import AbEnvironment.
Require Import AbNumDomain.
Require Import IntervalDomain.

Module Box.

  Module AbTree := WPFun PTree.

  Section S.
    Context {Val: Type} (Dval: num_dom Val).
    Context (Dval_cor: num_dom_correct Dval).
    Local Notation var := positive.

  Definition t := @AbTree.t Val.
  Instance numd : Adom.adom Val int := @as_adom _ Dval.
  Instance adom : Adom.adom AbTree.t (var->int) := AbTree.makebot _.

  Definition ab_eval_constant (cst:nconstant) : Val+⊥ :=
    match cst with
      | NOintconst i => NotBot (const i)
      | NOintunknown => ⊤
    end.

  Lemma ab_eval_constant_correct: forall cst n,
    eval_nconstant cst n ->
    γ (ab_eval_constant cst) n.
Proof.
    induction 1; simpl; auto.
    apply const_correct.
  Qed.

  Definition cast_int7 : Val -> bool := is_in_itv Int.zero (Int.repr 127).
  Definition cast_int8s : Val -> bool := is_in_itv (Int.repr (-128)) (Int.repr 127).
  Definition cast_int8u : Val -> bool := is_in_itv Int.zero (Int.repr 255).
  Definition cast_int15 : Val -> bool := is_in_itv Int.zero (Int.repr 32767).
  Definition cast_int16s : Val -> bool := is_in_itv (Int.repr (-32768)) (Int.repr 32767).
  Definition cast_int16u : Val -> bool := is_in_itv Int.zero (Int.repr 65535).

  Definition eval_nunop (op:unary_operation) (v:Val) : Val+⊥ :=
    match op with
      | Ocast8unsigned => if cast_int8u v then NotBot v else
      | Ocast8signed => if cast_int8s v then NotBot v else
      | Ocast16unsigned => if cast_int16u v then NotBot v else
      | Ocast16signed => if cast_int16s v then NotBot v else
      | Onotbool => notbool v
      | Oboolval => boolval v
      | Onegint => neg v
      | Onotint => not v
      | Onegf
      | Oabsf
      | Osingleoffloat
      | Ointoffloat
      | Ointuoffloat
      | Ofloatofint
      | Ofloatofintu => ⊤
    end.

  Lemma ab_eval_nunop_correct: forall op n1 n ab,
    AbEnvironment.eval_nunop op n1 n ->
    γ ab n1 ->
    γ (eval_nunop op ab) n.
Proof.
    pose proof (is_in_itv_correct) as K.
    induction 1; simpl; auto.
    > case_eq (cast_int8u ab); unfold cast_int8u; intros U V; auto.
      specialize (K _ _ _ U _ V).
      rewrite (@zero_ext_same 8%nat 24%nat); auto with zarith.
      split.
      apply ltlt'. vm_compute. split; discriminate. apply K.
      apply ltlt. vm_compute. split; discriminate. apply K.
    > case_eq (cast_int8s ab); unfold cast_int8s; intros U V; auto.
      specialize (K _ _ _ U _ V).
      rewrite (@sign_ext_same 7%nat 24%nat); auto with zarith.
      split.
      apply ltlt'. vm_compute. split; discriminate. apply K.
      apply ltlt. vm_compute. split; discriminate. apply K.
    > case_eq (cast_int16u ab); unfold cast_int16u; intros U V; auto.
      specialize (K _ _ _ U _ V).
      rewrite (@zero_ext_same 16%nat 16%nat); auto with zarith.
      split.
      apply ltlt'. vm_compute. split; discriminate. apply K.
      apply ltlt. vm_compute. split; discriminate. apply K.
    > case_eq (cast_int16s ab); unfold cast_int16s; intros U V; auto.
      specialize (K _ _ _ U _ V).
      rewrite (@sign_ext_same 15%nat 16%nat); auto with zarith.
      split.
      apply ltlt'. vm_compute. split; discriminate. apply K.
      apply ltlt. vm_compute. split; discriminate. apply K.
    > intros H. apply boolval_correct. auto.
    > intros. apply neg_correct. econstructor; intuition eauto.
    > intros. apply notbool_correct. auto.
    > intros. apply not_correct. econstructor; intuition eauto.
  Qed.

  Definition eval_nbinop (op:binary_operation) (v1 v2:Val) : Val+⊥ :=
    match op with
      | Oadd => add v1 v2
      | Osub => sub v1 v2
      | Omul => mul v1 v2
      | Odiv => divs v1 v2
      | Odivu => ⊤
      | Omod => ⊤
      | Omodu => ⊤
      | Oshl => if is_in_itv Int.zero (Int.sub Int.iwordsize Int.one) v2 then shl v1 v2 else
      | Oshr => ⊤
      | Oshru => ⊤
      | Ocmp c => NotBot booleans
      | Ocmpu c => NotBot booleans
      | Oand => and v1 v2
      | Oor => or v1 v2
      | Oxor => xor v1 v2
      | Oaddf
      | Osubf
      | Omulf
      | Odivf
      | Ocmpf _ => ⊤
    end.

  Lemma ab_eval_nbinop_correct: forall op n1 n2 n ab1 ab2,
    AbEnvironment.eval_nbinop op n1 n2 n ->
    γ ab1 n1 ->
    γ ab2 n2 ->
    γ (eval_nbinop op ab1 ab2) n.
Proof.
    induction 1; simpl; auto.
    > intros. apply add_correct. do 2 econstructor. eauto.
    > intros; apply sub_correct. repeat econstructor (eauto).
    > intros; apply mul_correct; repeat econstructor (eauto).
    > intros; apply divs_correct; repeat econstructor (eauto).
    > intros; apply and_correct; repeat econstructor (eauto).
    > intros; apply or_correct; repeat econstructor (eauto).
    > intros; apply xor_correct; repeat econstructor (eauto).
    > case_eq_bool_in_goal; intros; auto.
      apply shl_correct; repeat econstructor (eauto).
    > case_eq_bool_in_goal; intros; auto.
      apply False_ind.
      exploit is_in_itv_correct; eauto.
      destruct 1.
      unfold Int.ltu, Int.lt in *.
      repeat (destruct zlt; try congruence).
      assert (Int.signed i2 = Int.unsigned i2).
        unfold Int.signed, Int.unsigned in *.
        rewrite zlt_true in z0; [idtac|compute; auto].
        destruct zlt; auto.
        rewrite zlt_true in z1; [idtac|compute; auto].
        pose proof (Int.intrange i2).
        assert (Int.intval Int.zero = 0) by reflexivity.
        omega.
      assert (Int.unsigned Int.iwordsize = 32) by reflexivity.
      assert (Int.signed (Int.sub Int.iwordsize Int.one) = 31) by reflexivity.
      omega.
    > destruct Int.cmp; simpl; intros.
      apply booleans_correct1.
      apply booleans_correct0.
    > destruct Int.cmpu; simpl; intros.
      apply booleans_correct1.
      apply booleans_correct0.
  Qed.

  Fixpoint eval_expr (e:nexpr var) (ab:t) : Val+⊥ :=
    match e with
      | NEvar x => AbTree.get _ ab x
      | NEconst cst => ab_eval_constant cst
      | NEunop op e => botbind (eval_nunop op) (eval_expr e ab)
      | NEbinop op e1 e2 =>
        botbind2 (eval_nbinop op) (eval_expr e1 ab) (eval_expr e2 ab)
      | NEcondition b l r =>
        botbind
          (fun k =>
             if is_in_itv Nfalse Nfalse k then eval_expr r ab
             else match meet k (NotBot (const Nfalse)) with
                    | Bot => eval_expr l ab
                    | NotBot _ => eval_expr r abeval_expr l ab
                  end)
          (eval_expr b ab)
    end.

  Lemma eval_expr_correct : forall ρ ab e n,
    γ ab ρ ->
    eval_nexpr ρ e n ->
    γ (eval_expr e ab) n.
Proof.
    induction 2; simpl eval_expr.
    > rewrite <- H0; auto.
      destruct ab; try (elim H; fail); auto.
    > simpl; eapply ab_eval_constant_correct; auto.
    > destruct eval_expr; try (elim IHeval_nexpr; fail).
      simpl; eapply ab_eval_nunop_correct; eauto.
    > destruct eval_expr; try (elim IHeval_nexpr1; fail).
      destruct eval_expr; try (elim IHeval_nexpr2; fail).
      simpl; eapply ab_eval_nbinop_correct; eauto.
    > destruct eval_expr. intuition. simpl in IHeval_nexpr1.
      destruct bv; simpl.
      case_eq (is_in_itv Nfalse Nfalse x).
      intros X.
      pose proof (is_in_itv_correct _ _ _ X _ IHeval_nexpr1) as [K L].
      unfold Int.lt in *. destruct zlt; try now intuition.
      destruct zlt; try now intuition.
      assert (Int.signed k = 0). change (Int.signed Nfalse) with 0 in *. intuition.
      apply False_ind.
      apply (signed_inj k Nfalse); intuition.
      intros _.
      case_eq (meet x (NotBot (const Nfalse))). auto.
      intros u U.
      destruct eval_expr; destruct eval_expr; auto. intuition.
      apply join_sound; auto.
      case_eq (is_in_itv Nfalse Nfalse x). intros X.
      pose proof (is_in_itv_correct _ _ _ X _ IHeval_nexpr1) as [K L]. auto.
      intros _.
      case_eq (meet x (NotBot (const Nfalse))).
      intros U.
      pose proof (meet_correct x (NotBot (const Nfalse))) as V. rewrite U in V.
      eelim V. split. eauto.
      destruct (Int.eq_dec Nfalse k). subst.
      simpl. apply const_correct.
      intuition.
      intros u U.
      destruct eval_expr; destruct eval_expr; auto. intuition.
      apply join_sound; auto.
  Qed.

  Definition get_itv e (ab: AbTree.t) (s: sign_flag) :=
    botbind (fun x => range x s) (eval_expr e ab).

  Lemma get_itv_correct: forall e ρ (ab: AbTree.t),
    γ ab ρ ->
    eval_nexpr ρ eints_in_range (get_itv e ab).
Proof.
    intros.
    exploit eval_expr_correct; eauto.
    case_eq (eval_expr e ab). intuition.
    intros x H1 H2. simpl in H2.
    unfold get_itv. rewrite H1. simpl.
    apply range_correct; auto.
  Qed.

  Definition assign (x:var) (e:nexpr var) (ab:t) : t :=
    AbTree.set ab x (eval_expr e ab).

  Existing Instance adom.
  Lemma assign_correct : forall x e ρ n ab,
    ρ ∈ γ ab ->
    eval_nexpr ρ e n ->
    (upd ρ x n) ∈ γ (assign x e ab).
Proof.
    unfold assign, upd. intros x e ρ n ab H0 H.
    exploit eval_expr_correct; eauto; intros.
    destruct (eval_expr e ab); try (elim H1; fail).
    destruct ab; try (elim H0; fail).
    intros y; simpl.
    rewrite PTree.gsspec; destruct peq; destruct eq_dec; eauto.
    congruence.
    apply (H0 y).
  Qed.

  Definition lift1 {A B:Type} (f:A->B+⊥) (a:A+⊥) : B+⊥ :=
    match a with
      | Bot => Bot
      | NotBot a => f a
    end.

  Definition lift2 {A B:Type} (f:A->A->B+⊥) (a:(A+⊥)*(A+⊥)) : B+⊥ :=
    match a with
      | (Bot, _) => Bot
      | (_, Bot) => Bot
      | (NotBot a1, NotBot a2) => f a1 a2
    end.


  Instance numdb : Adom.adom (Val+⊥) int := lift_bot numd.

  Fixpoint backward_expr (e:nexpr var) (ab:t) (itv:Val) : t :=
    match e with
      | NEvar x =>
        AbTree.set ab x (meet itv (AbTree.get numd ab x))
      | NEconst cst =>
        if leb (meet itv (ab_eval_constant cst)) Bot
          then Bot
          else ab
      | NEunop op e =>
        lift1
        (fun i =>
          match op with
            | Oboolval => lift1 (backward_expr e ab) (backward_boolval i itv)
            | Onegint => lift1 (backward_expr e ab) (backward_neg i itv)
            | Onotbool => lift1 (backward_expr e ab) (backward_notbool i itv)
            | Onotint => lift1 (backward_expr e ab) (backward_not i itv)
            | _ => ab
          end)
        (eval_expr e ab)
      | NEbinop op e1 e2 =>
        lift2
        (fun i1 i2 =>
          match op with
            | Oadd =>
              lift2 (fun itv1 itv2 =>
                backward_expr e2 (backward_expr e1 ab itv1) itv2)
              (backward_add i1 i2 itv)
            | Osub =>
              lift2 (fun itv1 itv2 =>
                backward_expr e2 (backward_expr e1 ab itv1) itv2)
              (backward_sub i1 i2 itv)
            | Ocmp c =>
              lift2 (fun itv1 itv2 =>
                backward_expr e2 (backward_expr e1 ab itv1) itv2)
              (backward_cmp c i1 i2 itv)
            | Ocmpu c =>
              lift2 (fun itv1 itv2 =>
                backward_expr e2 (backward_expr e1 ab itv1) itv2)
              (backward_cmpu c i1 i2 itv)
            | Oand =>
              union_list Bot (join (adom:=adom))
              (map
                (lift2 (fun itv1 itv2 =>
                  backward_expr e2 (backward_expr e1 ab itv1) itv2))
                (backward_and i1 i2 itv))
            | Oor =>
              union_list Bot (join (adom:=adom))
              (map
                (lift2 (fun itv1 itv2 =>
                  backward_expr e2 (backward_expr e1 ab itv1) itv2))
                (backward_or i1 i2 itv))
            | _ => ab
          end)
        (eval_expr e1 ab, eval_expr e2 ab)
      | NEcondition b l r =>
          backward_expr b (backward_expr r ab itv) (const Nfalse)
        ⊔ lift1 (fun i => lift1
                            (backward_expr b (backward_expr l ab itv))
                            (backward_boolval i (const Ntrue)))
                (eval_expr b ab)
    end.

  Lemma backward_expr_correct: forall ρ e i,
    eval_nexpr ρ e i -> forall ab,
    γ ab ρ -> forall itv,
    γ itv i ->
    γ (backward_expr e ab itv) ρ.
Proof.
    induction 1; intros.
    > apply AbTree.gamma_set1; auto.
      subst; apply meet_correct; auto.
      split; auto.
      apply AbTree.gamma_app; auto.
    > unfold backward_expr.
      case_eq_bool_in_goal; intros; auto.
      exploit (gamma_monotone H2).
      apply meet_correct; split; eauto.
      simpl; apply ab_eval_constant_correct; auto.
      simpl; intuition.
    > simpl backward_expr.
      exploit (eval_expr_correct ρ ab a1 n1); auto.
      case_eq (eval_expr a1 ab); intros.
      elim H4.
      simpl.
      inv H0; auto.
      > exploit (backward_boolval_correct x itv n1); eauto.
        destruct (backward_boolval x itv); simpl; intuition.
        apply IHeval_nexpr; auto.
      > exploit (backward_neg_correct x itv n1); eauto.
        destruct (backward_neg x itv); simpl; intuition.
        apply IHeval_nexpr; auto.
      > exploit (backward_notbool_correct x itv n1); eauto.
        destruct (backward_notbool x itv); simpl; intuition.
        apply IHeval_nexpr; auto.
      > exploit (backward_not_correct x itv n1); eauto.
        destruct (backward_not x itv); simpl; intuition.
        apply IHeval_nexpr; auto.
    > exploit (eval_expr_correct ρ ab a1 n1); auto.
      exploit (eval_expr_correct ρ ab a2 n2); auto.
      case_eq (eval_expr a1 ab); try (intuition; fail).
      case_eq (eval_expr a2 ab); try (intuition; fail).
      intros itv2 Hi2 itv1 Hi1 G2 G1.
      simpl backward_expr; rewrite Hi1; rewrite Hi2.
      inv H1; auto.
      > exploit (backward_add_correct itv1 itv2 itv n1 n2); auto.
        destruct (backward_add itv1 itv2 itv) as [I1 I2].
        destruct I1; destruct I2; try (simpl; intuition; fail); simpl.
        destruct 1; apply IHeval_nexpr2; auto.
      > exploit (backward_sub_correct itv1 itv2 itv n1 n2); auto.
        destruct (backward_sub itv1 itv2 itv) as [I1 I2].
        destruct I1; destruct I2; try (simpl; intuition; fail); simpl.
        destruct 1; apply IHeval_nexpr2; auto.
      > exploit (backward_and_correct itv1 itv2 itv); try (simpl in *; eauto; fail).
        intros ((itv3,itv4) & I1 & I2 & I3).
        eapply union_list_correct.
        intros.
        pose proof (join_sound ab1 ab2). auto.
        apply in_map; eauto.
        destruct itv3; try (elim I2; fail).
        destruct itv4; try (elim I3; fail).
        simpl; apply IHeval_nexpr2; auto.
      > exploit (backward_or_correct itv1 itv2 itv); try (simpl in *; eauto; fail).
        intros ((itv3,itv4) & I1 & I2 & I3).
        eapply union_list_correct.
        intros. pose proof (join_sound ab1 ab2). auto.
        apply in_map; eauto.
        destruct itv3; try (elim I2; fail).
        destruct itv4; try (elim I3; fail).
        simpl; apply IHeval_nexpr2; auto.
      > exploit (backward_cmp_correct c itv1 itv2 itv n1 n2); auto.
        destruct (backward_cmp c itv1 itv2 itv) as [I1 I2].
        destruct I1; destruct I2; try (simpl; intuition; fail); simpl.
        destruct 1; apply IHeval_nexpr2; auto.
      > exploit (backward_cmpu_correct c itv1 itv2 itv n1 n2); auto.
        destruct (backward_cmpu c itv1 itv2 itv) as [I1 I2].
        destruct I1; destruct I2; try (simpl; intuition; fail); simpl.
        destruct 1; apply IHeval_nexpr2; auto.
        Opaque join. Opaque gamma.
      > simpl. apply join_sound.
        destruct bv;[right|left].
        pose proof (eval_expr_correct ρ ab b k H2) as HB.
        destruct (eval_expr b ab) as [|B]. intuition.
        simpl.
        assert (Int.eq k Int.zero = false).
        generalize (Int.eq_spec k Int.zero). destruct Int.eq; intuition.
        pose proof (backward_boolval_correct B (const Ntrue) k (HB H)) as BB.
        simpl in BB. autorw'. specialize (BB (@const_correct _ _ Dval_cor Int.one)).
        destruct backward_boolval. intuition.
        apply IHeval_nexpr1; intuition.
        destruct (Int.eq_dec k Nfalse). 2: intuition. subst.
        destruct Dval_cor; intuition.
    Qed.

  Definition assume_once (e:nexpr var) (ab:t) : t :=
    backward_expr e ab (const Int.one).

  Fixpoint assume' (fuel: nat) (e: nexpr var) (ab: t) : t :=
    match fuel with
      | O => ab
      | S fuel' =>
        let ab' := assume_once e ab in
        if leb ab ab' then ab else assume' fuel' e ab'
    end.

  Definition assume := assume' 12.

  Lemma assume_once_correct: forall e ρ ab,
    eval_nexpr ρ e Ntrue ->
    γ ab ρ ->
    γ (assume_once e ab) ρ.
Proof.
    unfold assume_once; intros.
    assert (γ (const Int.one) Ntrue).
      apply const_correct.
    eapply backward_expr_correct; eauto.
  Qed.

  Lemma assume'_correct: forall fuel e ρ ab,
    ρ ∈ γ ab ->
    Ntrueeval_nexpr ρ e ->
    ρ ∈ γ (assume' fuel e ab).
Proof.
    induction fuel; simpl; auto.
    intros e ρ [|ab] H0 H. intuition.
    case_eq (assume_once e (NotBot ab)).
    intros.
    pose proof (assume_once_correct e ρ (NotBot ab) H H0). autorw'. intuition.
    intros x H1.
    bif. apply IHfuel; auto. rewrite <- H1. apply assume_once_correct; auto.
  Qed.

  Lemma assume_correct: forall e ρ ab,
    ρ ∈ γ ab ->
    Ntrueeval_nexpr ρ e ->
    ρ ∈ γ (assume e ab).
Proof.
apply assume'_correct. Qed.

  Definition abdom : int_dom t := Build_int_dom _
    adom
    get_itv
    assign
    assume
    get_itv_correct
    assign_correct
    assume_correct.

End S.

End Box.