Module FactMsgHelpers


Require Import Coqlib Util Utf8 IdealExpr AbIdealEnv AdomLib Integers FastArith.
Require Import IdealIntervals ZIntervals.

Fixpoint simplify_bool_expr_aux (e:iexpr) : iexpr * bool :=
  match e with
  | IEvar _ _ | IEconst _ | IEZitv _ => (e, false)
  | IEunop op e => (IEunop op (simplify_expr e), false)
  | IEbinop (IOcmp Ceq) (e as e1) (IEconst (INz z) as e2)
  | IEbinop (IOcmp Ceq) (IEconst (INz z) as e1) (e as e2) =>
    if Zfasteqb z F0 then
      let '(e', b) := simplify_bool_expr_aux e in
      (e', negb b)
    else (IEbinop (IOcmp Ceq) (simplify_expr e1) (simplify_expr e2), false)
  | IEbinop (IOcmp Cne) (e as e1) (IEconst (INz z) as e2)
  | IEbinop (IOcmp Cne) (IEconst (INz z) as e1) (e as e2) =>
    if Zfasteqb z F0 then simplify_bool_expr_aux e
    else (IEbinop (IOcmp Cne) (simplify_expr e1) (simplify_expr e2), false)
  | IEbinop (IOcmp c) e1 e2 =>
    (IEbinop (IOcmp c) (simplify_expr e1) (simplify_expr e2), false)
  | IEbinop (IOcmpf c) e1 e2 =>
    (IEbinop (IOcmpf c) (simplify_expr e1) (simplify_expr e2), false)
  | IEbinop op e1 e2 =>
    (IEbinop op (simplify_expr e1) (simplify_expr e2), false)
  end
with simplify_expr (e:iexpr) : iexpr :=
  let e0 := IEconst (INz F0) in
  match e with
  | IEvar _ _ | IEconst _ | IEZitv _ => e
  | IEunop op e => IEunop op (simplify_expr e)
  | IEbinop (IOcmp Ceq as op) (e as e1) (IEconst (INz z) as e2)
  | IEbinop (IOcmp Ceq as op) (IEconst (INz z) as e1) (e as e2) =>
    if Zfasteqb z F0 then
      match simplify_bool_expr_aux e with
      | (e', true) => IEbinop (IOcmp Cne) e' e0
      | (e', false) => IEbinop (IOcmp Ceq) e' e0
      end
    else IEbinop op (simplify_expr e1) (simplify_expr e2)
  | IEbinop (IOcmp Cne as op) (e as e1) (IEconst (INz z) as e2)
  | IEbinop (IOcmp Cne as op) (IEconst (INz z) as e1) (e as e2) =>
    if Zfasteqb z F0 then
      match simplify_bool_expr_aux e with
      | (e', false) => IEbinop (IOcmp Cne) e' e0
      | (e', true) => IEbinop (IOcmp Ceq) e' e0
      end
    else IEbinop op (simplify_expr e1) (simplify_expr e2)
  | IEbinop op e1 e2 => IEbinop op (simplify_expr e1) (simplify_expr e2)
  end.

Lemma simplify_expr_correct_aux:
  forall e,
    (forall ρ n, eval_iexpr ρ e (INz n) ->
     forall e' b, simplify_bool_expr_aux e = (e', b) ->
       exists n', eval_iexpr ρ e' (INz n') /\ xorb (Zcmp Ceq n 0) (Zcmp Ceq n' 0) = b)
      /\
    (forall ρ n, eval_iexpr ρ e n -> eval_iexpr ρ (simplify_expr e) n).
Proof.
  induction e; simpl; split; intros; inv H.
  - inv H0. eexists. split. constructor; eauto. apply xorb_nilpotent.
  - constructor; auto.
  - inv H0. eexists. split. constructor; eauto. apply xorb_nilpotent.
  - constructor.
  - inv H0. eexists. split. constructor; eauto. apply xorb_nilpotent.
  - constructor; auto.
  - destruct IHe as [IHe IHe'].
    inv H0. eexists. split. econstructor; eauto. apply xorb_nilpotent.
  - destruct IHe as [IHe IHe'].
    econstructor; eauto.
  - destruct IHe1 as [IHe1 IHe1']. destruct IHe2 as [IHe2 IHe2'].
    inv H7; try now (inv H0; eexists; split;
                   [econstructor; eauto; econstructor|apply xorb_nilpotent]).
    assert (forall A B (a a':A) (b b':B),
              (a, b) = (a', b') -> a = a' /\ b = b') by (split; congruence).
    destruct (simplify_bool_expr_aux e1) as (e1', b1),
             (simplify_bool_expr_aux e2) as (e2', b2).
    edestruct IHe1 as ([n'1] & EVAL1 & Hn'1); eauto. clear IHe1.
    edestruct IHe2 as ([n'2] & EVAL2 & Hn'2); eauto. clear IHe2.
    specialize (IHe1' _ _ H4). specialize (IHe2' _ _ H6).
    assert (forall n, eval_iexpr ρ (IEconst (INz 0)) (INz n) -> n = 0).
    { intros. clear - H1. inv H1. auto. }
    destruct c;
      (try now (apply H in H0; destruct H0; subst; eexists; split;
        [econstructor; [apply IHe1'|apply IHe2'|econstructor]|apply xorb_nilpotent]));
    destruct e1 as [|[|[[]]]| | |], e2 as [|[|[[]]]| | |];
    fastunwrap; simpl Z.eqb in H0; cbv iota in H0;
    apply H in H0; destruct H0; subst;
    (try now (eexists; split; [econstructor; [apply IHe1'|apply IHe2'|econstructor]|apply xorb_nilpotent]));
    unfold Zcmp in *;
    (try (apply H1 in H4; inv H4; rewrite (Z.compare_antisym i2 0)));
    (try (apply H1 in H6; inv H6));
    (eexists; split; [now eauto|]; symmetry; simpl;
     destruct Z.compare, Z.compare; auto).
  - destruct IHe1 as [IHe1 IHe1']. destruct IHe2 as [IHe2 IHe2'].
    assert (eval_iexpr ρ (IEbinop i (simplify_expr e1) (simplify_expr e2)) n)
      by (econstructor; eauto).
    assert (forall n, eval_iexpr ρ (IEconst (INz 0)) (INz n) -> n = 0).
    { intros. inv H0. auto. }
    destruct H6; auto.
    destruct (simplify_bool_expr_aux e1) as (e1', b1),
             (simplify_bool_expr_aux e2) as (e2', b2).
    edestruct IHe1 as ([n'1] & EVAL1 & Hn'1); eauto. clear IHe1.
    edestruct IHe2 as ([n'2] & EVAL2 & Hn'2); eauto. clear IHe2.
    specialize (IHe1' _ _ H3). specialize (IHe2' _ _ H5).
    assert (eval_iexpr ρ (IEconst (INz 0)) (INz 0)).
    { econstructor. }
    destruct c; auto.
    + assert (eval_iexpr ρ (IEbinop (IOcmp Ceq) e1' (IEconst (INz 0)))
                         (INz (if Zcmp Ceq n'1 0 then F1 else F0))).
      { econstructor; eauto. }
      assert (eval_iexpr ρ (IEbinop (IOcmp Ceq) e2' (IEconst (INz 0)))
                         (INz (if Zcmp Ceq n'2 0 then F1 else F0))).
      { econstructor; eauto. }
      assert (eval_iexpr ρ (IEbinop (IOcmp Cne) e1' (IEconst (INz 0)))
                         (INz (if Zcmp Ceq n'1 0 then F0 else F1))).
      { econstructor; eauto.
        replace (if Zcmp Ceq n'1 0 return Zfast then F0 else F1)
        with (if Zcmp Cne n'1 0 return Zfast then F1 else F0)
          by (unfold Zcmp; destruct Z.compare; auto).
        constructor. }
      assert (eval_iexpr ρ (IEbinop (IOcmp Cne) e2' (IEconst (INz 0)))
                         (INz (if Zcmp Ceq n'2 0 then F0 else F1))).
      { econstructor; eauto.
        replace (if Zcmp Ceq n'2 0 return Zfast then F0 else F1)
        with (if Zcmp Cne n'2 0 return Zfast then F1 else F0)
          by (unfold Zcmp; destruct Z.compare; auto).
        constructor. }
      destruct e1 as [|[|[[]]]| | |], e2 as [|[|[[]]]| | |]; auto;
      fastunwrap; simpl Z.eqb; cbv iota;
      (try (apply H0 in H3; inv H3;
            replace (Zcmp Ceq 0 i2) with (Zcmp Ceq i2 0)
              by (unfold Zcmp; rewrite Z.compare_antisym;
                  destruct Z.compare; auto)));
      (try (apply H0 in H5; inv H5));
      destruct (Zcmp Ceq n'1 0), (Zcmp Ceq n'2 0);
      (try destruct (Zcmp Ceq i1 0));
      (try destruct (Zcmp Ceq i2 0));
      eauto.
    + assert (eval_iexpr ρ (IEbinop (IOcmp Ceq) e1' (IEconst (INz 0)))
                         (INz (if Zcmp Ceq n'1 0 then F1 else F0))).
      { econstructor; eauto. }
      assert (eval_iexpr ρ (IEbinop (IOcmp Ceq) e2' (IEconst (INz 0)))
                         (INz (if Zcmp Ceq n'2 0 then F1 else F0))).
      { econstructor; eauto. }
      assert (eval_iexpr ρ (IEbinop (IOcmp Cne) e1' (IEconst (INz 0)))
                         (INz (if Zcmp Ceq n'1 0 then F0 else F1))).
      { econstructor; eauto.
        replace (if Zcmp Ceq n'1 0 return Zfast then F0 else F1)
        with (if Zcmp Cne n'1 0 return Zfast then F1 else F0)
          by (unfold Zcmp; destruct Z.compare; auto).
        constructor. }
      assert (eval_iexpr ρ (IEbinop (IOcmp Cne) e2' (IEconst (INz 0)))
                         (INz (if Zcmp Ceq n'2 0 then F0 else F1))).
      { econstructor; eauto.
        replace (if Zcmp Ceq n'2 0 return Zfast then F0 else F1)
        with (if Zcmp Cne n'2 0 return Zfast then F1 else F0)
          by (unfold Zcmp; destruct Z.compare; auto).
        constructor. }
      destruct e1 as [|[|[[]]]| | |], e2 as [|[|[[]]]| | |]; auto; simpl Zunwrap in *;
      (try (apply H0 in H3; inv H3;
            replace (Zcmp Cne 0 i2) with (Zcmp Cne i2 0)
              by (unfold Zcmp; rewrite Z.compare_antisym;
                  destruct Z.compare; auto)));
      (try (apply H0 in H5; inv H5));
      unfold Zcmp in *;
      destruct (n'1 ?= 0), (n'2 ?= 0);
      (try destruct (i1 ?= 0));
      (try destruct (i2 ?= 0));
      eauto.
Qed.

Lemma simplify_expr_correct:
  forall e ρ n, eval_iexpr ρ e n -> eval_iexpr ρ (simplify_expr e) n.
Proof.
apply simplify_expr_correct_aux. Qed.

Definition is_bool_expr (e:iexpr) (c:query_chan) : bool :=
  match e with
  | IEbinop (IOcmp _ | IOcmpf _) _ _ => true
  | IEvar _ _ => c.(get_itv) eNotBot (Just (Az (ZITV F0 F1)))
  | IEconst (INz z) => Zfasteqb z F0 || Zfasteqb z F1
  | _ => false
  end.

Lemma is_bool_expr_correct:
  forall e ρ n c,
    eval_iexpr ρ e (INz n) -> ρ ∈ γ c ->
    is_bool_expr e c = true ->
    n = 0 \/ n = 1.
Proof.
  intros. destruct e; try discriminate.
  - simpl in H1. destruct n as [n].
    apply (leb_correct (get_itv c (IEvar i v))) with (a:=INz n) in H1.
    simpl in *. assert (n=0 \/ n=1) by omega. clear -H2. intuition congruence.
    eapply get_itv_correct; eauto.
  - simpl in H1. inv H. destruct n as [[|[]|]]; try discriminate; auto.
  - inv H. inv H1. inv H8; try discriminate.
    destruct Zcmp; auto. destruct Floats.Float.cmp; auto.
Qed.

Definition simplify_bool_expr (e:iexpr) (c:query_chan) : iexpr * bool :=
  let '(e, b) := simplify_bool_expr_aux e in
  if is_bool_expr e c then (e, b)
  else (IEbinop (IOcmp (if b then Ceq else Cne)) e (IEconst (INz F0)), false).

Lemma simplify_bool_expr_correct:
  ∀ e ρ n, eval_iexpr ρ e (INz n) ->
  ∀ c, ρ ∈ γ c ->
  ∀ e' b, simplify_bool_expr e c = (e', b) ->
    eval_iexpr ρ e' (INz (if xorb (n =? 0) b then F0 else F1)).
Proof.
  unfold simplify_bool_expr. intros.
  destruct (simplify_bool_expr_aux e) eqn:EQ.
  destruct (simplify_expr_correct_aux e) as [? _]. eapply H2 in EQ; eauto. clear H2.
  destruct EQ as (n' & Hn' & XOR).
  pose proof (is_bool_expr_correct _ _ _ _ Hn' H0). destruct is_bool_expr; inv H1.
  - destruct H2; subst; auto; rewrite <- xorb_assoc_reverse, Z.eqb_compare, xorb_nilpotent; auto.
  - econstructor. eauto. constructor. destruct n as [[]], n' as [[]]; econstructor.
Qed.

Section assume_unfold.

  Context {A:Type} {J:join_op A (A+⊥)} (assume:iexpr -> bool -> A * query_chan -> A+⊥).

  Definition trivial_ite_assume (e:ite_iexpr) (b:bool) (ab:A) : A+⊥ :=
    match e, b with
    | Leaf (IEconst (INz z)), false => if Zfasteqb z F1 then Bot else NotBot ab
    | Leaf (IEconst (INz z)), true => if Zfasteqb z F0 then Bot else NotBot ab
    | _, _ => NotBot ab
    end.

  Fixpoint ite_assume (fuel:nat) (e:ite_iexpr) (b:bool) (ab:A * query_chan) : A+⊥ :=
    match fuel with
    | O => ret (fst ab)
    | S fuel =>
      match e with
      | Leaf e =>
        let '(e, b') := simplify_bool_expr e (snd ab) in
        let b := xorb b b' in
        do ab' <- assume e b ab;
        match e with
          | IEvar _ x =>
            match (snd ab).(get_eq_expr) x with
              | None => ret ab'
              | Some e' => ite_assume fuel e' b (ab', snd ab)
            end
          | _ => ret ab'
        end
      | Ite c et ef =>
        let abtrue :=
          do abtrue <- trivial_ite_assume et b (fst ab);
          do abtrue <- trivial_ite_assume (Leaf c) true abtrue;
          do abtrue <- ite_assume fuel (Leaf c) true (abtrue, snd ab);
          ite_assume fuel et b (abtrue, snd ab)
        in
        let abfalse :=
          do abfalse <- trivial_ite_assume ef b (fst ab);
          do abfalse <- trivial_ite_assume (Leaf c) false abfalse;
          do abfalse <- ite_assume fuel (Leaf c) false (abfalse, snd ab);
          ite_assume fuel ef b (abfalse, snd ab)
        in
        match abtrue, abfalse with
          | Bot, x | x, Bot => x
          | NotBot ab1, NotBot ab2 => ab1ab2
        end
      end
    end.

  Definition ite_assume_fuel := 20%nat.
  Global Opaque ite_assume_fuel.

  Definition assume_unfold e := ite_assume ite_assume_fuel (Leaf e).

  ContextA:gamma_op A (var -> ideal_num))
          (ρ:var -> ideal_num)
          (join_correct_ρ:forall x y:A, ρ ∈ (γ x ∪ γ y) -> ρ ∈ γ ((xy):_+⊥))
          (assume_sound_ρ:forall e (b:bool) x,
                            ρ ∈ γ x ->
                            eval_iexpr ρ e (INz (if b then F1 else F0)) ->
                            ρ ∈ γ (assume e b x)).

  Lemma trivial_ite_assume_correct:
    forall e (b:bool) x,
      ρ ∈ γ x ->
      eval_ite_iexpr ρ e (INz (if b then F1 else F0)) ->
      ρ ∈ γ (trivial_ite_assume e b x).
Proof.
    destruct e; auto. destruct i; auto. destruct b, i as [|[[|[]|]]]; auto.
    intros. inv H0. inv H2. intros. inv H0. inv H2.
  Qed.

  Lemma ite_assume_correct:
    forall fuel e (b:bool) ab,
      ρ ∈ γ ab ->
      eval_ite_iexpr ρ e (INz (if b then F1 else F0)) ->
      ρ ∈ γ (ite_assume fuel e b ab).
Proof.
    induction fuel. simpl; intros. apply H.
    destruct e; simpl; intros; inv H0.
    - pose proof (simplify_bool_expr_correct i _ _ H2 (snd ab) (proj2 H)).
      destruct (simplify_bool_expr i (snd ab)). specialize (H0 _ _ eq_refl).
      replace (xorb (Zunwrap (if b return Zfast then F1 else F0) =? 0) b0) with (negb (xorb b b0)) in H0
        by (destruct b, b0; easy).
      rewrite if_negb in H0.
      eapply botbind_spec, assume_sound_ρ; eauto.
      inv H0; auto.
      pose proof (get_eq_expr_correct (proj2 H) id).
      destruct (get_eq_expr (snd ab) id); auto. specialize (H0 _ eq_refl).
      intros. apply IHfuel. split. auto. apply H. congruence.
    - destruct H.
      apply trivial_ite_assume_correct with (e:=e2) (b:=b) in H; auto.
      apply eval_Leaf in H5. eapply botbind_spec in H.
      2:intros; eapply botbind_spec, trivial_ite_assume_correct with (b:=false), H5. 3:eauto.
      2:intros; eapply botbind_spec, (IHfuel (Leaf i) false), H5. 3:instantiate (1:=(_, _)); split; eauto.
      2:intros; apply (IHfuel e2), H6. 2:instantiate (1:=(_, _)); split; eauto.
      simpl in *. destruct (bind (M:=botlift)), (bind (M:=botlift)); auto. destruct H.
    - destruct H.
      apply trivial_ite_assume_correct with (e:=e1) (b:=b) in H; auto.
      apply eval_Leaf in H5. eapply botbind_spec in H.
      2:intros; eapply botbind_spec, trivial_ite_assume_correct with (b:=true), H5. 3:eauto.
      2:intros. 2:eapply botbind_spec, (IHfuel (Leaf i) true), H5. 3:instantiate (1:=(_, _)); split; eauto.
      2:intros; apply (IHfuel e1), H6. 2:instantiate (1:=(_, _)); split; eauto.
      simpl in *. destruct (bind (M:=botlift)), (bind (M:=botlift)); auto. destruct H.
  Qed.

  Lemma assume_unfold_correct:
    forall e (b:bool) ab,
      ρ ∈ γ ab ->
      eval_iexpr ρ e (INz (if b then F1 else F0)) ->
      ρ ∈ γ (assume_unfold e b ab).
Proof.
intros. apply ite_assume_correct. auto. constructor. auto. Qed.

End assume_unfold.