Module AbNumDomain

Require Import
  Coqlib Utf8
  Integers DLib
  LatticeSignatures
  AbEnvironment
  IntervalDomain.

Set Implicit Arguments.

Ltac bot :=
  repeat match goal with |- context[match ?a with Bot => _ | NotBot _ => _ end] => case_eq a end.

Specifications of the numerical operations on sets of concrete values.
Definition lift_unop (op: intint) (A: ℘ int) : ℘ int :=
  fun i => ∃ j, A ji = (op j).

Definition lift_binop (op: intintint) (A B: ℘ int) : ℘ int :=
  fun i => ∃ a, ∃ b, A aB bi = (op a b).


Signature of an abstract numerical domain.
Unset Elimination Schemes.
Class num_dom (t:Type) : Type :=
{ as_adom :> adom t int

; meet: tt+⊥ → t+⊥

; const: intt
; booleans: t
; range: tsign_flagInterval.itv+⊥

; neg: tt+⊥
; not: tt+⊥
; notbool: tt+⊥
; boolval: tt+⊥

; add: ttt+⊥
; sub: ttt+⊥
; mul: ttt+⊥
; divs: ttt+⊥
; shl: ttt+⊥

; and: ttt+⊥
; or: ttt+⊥
; xor: ttt+⊥

; is_in_itv: intinttbool

; backward_boolval: ttt+⊥
; backward_notbool: ttt+⊥
; backward_neg: ttt+⊥
; backward_not: ttt+⊥

; backward_add: tttt+⊥ * t+⊥
; backward_sub: tttt+⊥ * t+⊥
; backward_cmp: comparisontttt+⊥ * t+⊥
; backward_cmpu: comparisontttt+⊥ * t+⊥
; backward_and: tttlist (t+⊥ * t+⊥)
; backward_or: tttlist (t+⊥ * t+⊥)
}.

Definition backward_unop_correct (t:Type) `{adom t int}
           (aop: ttt+⊥)
           (op : intint) : Prop :=
  ∀ x y : t,
  ∀ i : int,
    γ x i
    γ y (op i) →
    γ (aop x y) i.

Definition backward_binop_correct (t:Type) `{adom t int}
           (aop: tttt+⊥ * t+⊥)
           (op : intintint) : Prop :=
  ∀ x y z : t,
  ∀ i j : int,
    γ x i
    γ y j
    γ z (op i j) →
    let '(u,v) := aop x y z in
    γ u i ∧ γ v j.

Definition backward_binop_list_correct (t:Type) `{adom t int}
           (aop: tttlist (t+⊥ * t+⊥))
           (op : intintint) : Prop :=
  ∀ x y z : t,
  ∀ i j : int,
    γ x i
    γ y j
    γ z (op i j) →
    ∃ ab, In ab (aop x y z) ∧
    γ (fst ab) i ∧ γ (snd ab) j.

Specification of an abstract numerical domain.
Class num_dom_correct t (D: num_dom t) : Prop :=
{ meet_correct: ∀ x y, (γ x) ∩ (γ y) ⊆ γ (meet x y)
; const_correct: ∀ n: int, γ (const n) n
; booleans_correct0: γ booleans Int.zero
; booleans_correct1: γ booleans Int.one
; range_correct: ∀ x:t, γ xints_in_range (range x)
; neg_correct: ∀ x: t, lift_unop Int.negx) ⊆ γ (neg x)
; not_correct: ∀ x: t, lift_unop Int.notx) ⊆ γ (not x)
; notbool_correct: ∀ x: t, ∀ i, γ x i → γ (notbool x) (Interval.of_bool (Int.eq i Int.zero))
; boolval_correct: ∀ x: t, ∀ i, γ x i → γ (boolval x) (Interval.of_bool (negb (Int.eq i Int.zero)))
; add_correct: ∀ x y: t, lift_binop Int.addx) (γ y) ⊆ γ (add x y)
; sub_correct: ∀ x y: t, lift_binop Int.subx) (γ y) ⊆ γ (sub x y)
; mul_correct: ∀ x y: t, lift_binop Int.mulx) (γ y) ⊆ γ (mul x y)
; divs_correct: ∀ x y: t, lift_binop Int.divsx) (γ y) ⊆ γ (divs x y)
; shl_correct: ∀ x y: t, lift_binop Int.shlx) (γ y) ⊆ γ (shl x y)
; and_correct: ∀ x y: t, lift_binop Int.andx) (γ y) ⊆ γ (and x y)
; or_correct: ∀ x y: t, lift_binop Int.orx) (γ y) ⊆ γ (or x y)
; xor_correct: ∀ x y: t, lift_binop Int.xorx) (γ y) ⊆ γ (xor x y)

; is_in_itv_correct (m M:int) : ∀ x: t, is_in_itv m M x = true
    ∀ i, γ x iInt.lt M i = falseInt.lt i m = false

; backward_boolval_correct: backward_unop_correct (t:=t) backward_boolval
    (fun i => (Interval.of_bool (negb (Int.eq i Int.zero))))
; backward_notbool_correct: backward_unop_correct (t:=t) backward_notbool
    (fun i => (Interval.of_bool (Int.eq i Int.zero)))
; backward_neg_correct: backward_unop_correct (t:=t) backward_neg Int.neg
; backward_not_correct: backward_unop_correct (t:=t) backward_not Int.not

; backward_add_correct: backward_binop_correct (t:=t) backward_add Int.add
; backward_sub_correct: backward_binop_correct (t:=t) backward_sub Int.sub
; backward_cmp_correct: ∀ c, backward_binop_correct (t:=t) (backward_cmp c)
    (fun i j => (Interval.of_bool (Int.cmp c i j)))
; backward_cmpu_correct: ∀ c, backward_binop_correct (t:=t) (backward_cmpu c)
    (fun i j => (Interval.of_bool (Int.cmpu c i j)))

; backward_and_correct: backward_binop_list_correct (t:=t) backward_and Int.and
; backward_or_correct: backward_binop_list_correct (t:=t) backward_or Int.or
}.

Class reduction A B : Type := { ρ: A+⊥ → B+⊥ → (A * B)+⊥ }.
Class reduction_correct A B t (Ha:adom A t) (Hb:adom B t) `{reduction A B} : Prop :=
{ reduction_spec a b:
    γ a ∩ γ b
    match ρ a b with
      | Bot => ∅
      | NotBot (u,v) => γ u ∩ γ v
    end
}.

Reduced product of two abstract numerical domains.
Definition botbind {A B} (f: AB+⊥) (a: A+⊥) : B+⊥ :=
  match a with
    | Bot => Bot
    | NotBot x => f x
  end.

Definition red_prod_unop {A B} ρ (opa: AA+⊥) (opb: BB+⊥) : A*B → (A*B)+⊥ :=
  fun x => match x with (a,b) => ρ (opa a) (opb b) end.

Definition botbind2 {A B C} (f: ABC+⊥) (a: A+⊥) (b: B+⊥) : C+⊥ :=
  match a, b with
    | Bot, _
    | _, Bot => Bot
    | NotBot x, NotBot y => f x y
  end.

Definition red_prod_binop {A B} ρ (opa: AAA+⊥) (opb: BBB+⊥)
  : (A*B) → (A*B) → (A*B)+⊥ :=
  fun x y => match x,y with (a,b), (c,d) =>
                            ρ (opa a c) (opb b d)
             end.

Definition red_prod_backop_bin {A B} ρ (opa: AAAA+⊥ * A+⊥) (opb: BBBB+⊥ * B+⊥)
  : (A*B) → (A*B) → (A*B) → (A*B)+⊥ * (A*B)+⊥ :=
  fun x y z => match x,y,z with
               | (a,b), (c,d), (e,f) =>
                 let '(a',c') := opa a c e in
                 let '(b',d') := opb b d f in
                 (ρ a' b', ρ c' d')
             end.

Definition red_prod_backop_bin_list {A B} ρ
  (opa: AAAlist (A+⊥ * A+⊥)) (opb: BBBlist (B+⊥ * B+⊥))
  : (A*B) → (A*B) → (A*B) → list ((A*B)+⊥ * (A*B)+⊥) :=
  fun x y z => match x,y,z with
               | (a,b), (c,d), (e,f) =>
                 let l1 := opa a c e in
                   match opb b d f with
                     | (b',d') :: nil =>
                       map (fun ab:(A+⊥ * A+⊥) => let (a',c') := ab in
                             (ρ a' b', ρ c' d')) l1
                     | _ => (NotBot x, NotBot y) :: nil
                   end
             end.

Definition red_prod_backop_un {A B} ρ (opa: AAA+⊥) (opb: BBB+⊥)
  : (A*B) → (A*B) → (A*B)+⊥ :=
  fun x y => match x,y with
               | (a,b), (c,d) =>
                 let a' := opa a c in
                 let b' := opb b d in
                 (ρ a' b')
             end.

Definition pair_of_prod {A B} (x: (A*B)+⊥) : A+⊥ * B+⊥ :=
  match x with
    | NotBot (a,b) => (NotBot a, NotBot b)
    | Bot => (Bot, Bot)
  end.

Instance reduced_product_num_dom A B `{Da:num_dom A} `{Db:num_dom B}
         (Ρ: reduction A B)
  : num_dom (A*B) :=
{| as_adom := WProd.make _ _
 ; meet x := botbind (fun y => ρ (meet (fst x) (NotBot (fst y))) (meet (snd x) (NotBot (snd y))))
 ; const n := (const n, const n)
 ; booleans := (booleans, booleans)
 ; range x s := Interval.meet' (range (fst x) s) (range (snd x) s)
 ; neg := red_prod_unop ρ neg neg
 ; not := red_prod_unop ρ not not
 ; notbool := red_prod_unop ρ notbool notbool
 ; boolval := red_prod_unop ρ boolval boolval
 ; add := red_prod_binop ρ add add
 ; sub := red_prod_binop ρ sub sub
 ; mul := red_prod_binop ρ mul mul
 ; divs := red_prod_binop ρ divs divs
 ; shl := red_prod_binop ρ shl shl
 ; and := red_prod_binop ρ and and
 ; or := red_prod_binop ρ or or
 ; xor := red_prod_binop ρ xor xor
 ; is_in_itv m M x := let '(a,b) := x in is_in_itv m M a || is_in_itv m M b

 ; backward_boolval := red_prod_backop_un ρ backward_boolval backward_boolval
 ; backward_notbool := red_prod_backop_un ρ backward_notbool backward_notbool
 ; backward_neg := red_prod_backop_un ρ backward_neg backward_neg
 ; backward_not := red_prod_backop_un ρ backward_not backward_not

 ; backward_add := red_prod_backop_bin ρ backward_add backward_add
 ; backward_sub := red_prod_backop_bin ρ backward_sub backward_sub
 ; backward_cmp := (fun c => red_prod_backop_bin ρ (backward_cmp c) (backward_cmp c))
 ; backward_cmpu := (fun c => red_prod_backop_bin ρ (backward_cmpu c) (backward_cmpu c))
 ; backward_and := red_prod_backop_bin_list ρ backward_and backward_and
 ; backward_or := red_prod_backop_bin_list ρ backward_or backward_or
|}.

This reducted product yields a correct abstract numerical domain.
Instance reduced_product_num_dom_correct A B `{Da:num_dom A} `{Db:num_dom B}
         (Das: num_dom_correct Da) (Dbs: num_dom_correct Db)
         (Ρ: reduction A B) (Ρs: reduction_correct (@as_adom _ Da) (@as_adom _ Db))
  : num_dom_correct (reduced_product_num_dom A B Ρ).
Proof.
split.
intros (a,b) [|(c,d)]. intuition. intros i [(L&R) (L'&R')]. simpl. apply reduction_spec. intuition auto using @meet_correct.
simpl. intuition auto using @const_correct.
split; eapply booleans_correct0.
split; eapply booleans_correct1.
intros (a,b) i (L&R). unfold ints_in_range. simpl. split.
  apply Interval.meet'_correct; apply range_correct; auto.
  exploit range_correct. eapply L. unfold ints_in_range. intros.
  unfold Interval.meet'. destruct (range a Unsigned); intros; autorw'. intuition.
  apply Interval.meet_correctu. split. 2: apply range_correct; auto. intuition.
intros (a,b) i (j & (R&L) & ->). simpl. apply reduction_spec; split; apply neg_correct; eexists; split; eauto.
intros (a,b) i (j & (R&L) & ->). simpl. apply reduction_spec; split; apply not_correct; eexists; split; eauto.
intros (a,b) i (L&R). simpl. apply reduction_spec. split; apply notbool_correct; auto.
intros (a,b) i (L&R). simpl. apply reduction_spec. split; apply boolval_correct; auto.
intros (a,b) (c,d) i (j & k & (?&?) & (?&?) &->). simpl.
apply reduction_spec; split; apply add_correct; eexists; eexists; intuition; eauto.
intros (a,b) (c,d) i (j & k & (?&?) & (?&?) &->). simpl.
apply reduction_spec; split; apply sub_correct; eexists; eexists; intuition; eauto.
intros (a,b) (c,d) i (j & k & (?&?) & (?&?) &->). simpl.
apply reduction_spec; split; apply mul_correct; eexists; eexists; intuition; eauto.
intros (a,b) (c,d) i (j & k & (?&?) & (?&?) &->). simpl.
apply reduction_spec; split; apply divs_correct; eexists; eexists; intuition; eauto.
intros (a,b) (c,d) i (j & k & (?&?) & (?&?) &->). simpl.
apply reduction_spec; split; apply shl_correct; eexists; eexists; intuition; eauto.
intros (a,b) (c,d) i (j & k & (?&?) & (?&?) &->). simpl.
apply reduction_spec; split; apply and_correct; eexists; eexists; intuition; eauto.
intros (a,b) (c,d) i (j & k & (?&?) & (?&?) &->). simpl.
apply reduction_spec; split; apply or_correct; eexists; eexists; intuition; eauto.
intros (a,b) (c,d) i (j & k & (?&?) & (?&?) &->). simpl.
apply reduction_spec; split; apply xor_correct; eexists; eexists; intuition; eauto.
intros m M (a,b). simpl. rewrite orb_true_iff.
destruct 1;intros i (U&V).
  eapply is_in_itv_correct; eauto.
  apply is_in_itv_correct with b; eauto.
intros (a,b)(c,d) i (?&?)(?&?). simpl. apply reduction_spec; split; apply backward_boolval_correct; auto.
intros (a,b)(c,d) i (?&?)(?&?). simpl. apply reduction_spec; split; apply backward_notbool_correct; auto.
intros (a,b)(c,d) i (?&?)(?&?). simpl. apply reduction_spec; split; apply backward_neg_correct; auto.
intros (a,b)(c,d) i (?&?)(?&?). simpl. apply reduction_spec; split; apply backward_not_correct; auto.
intros (a,b)(c,d)(e,f) i j (?&?)(?&?)(?&?).
pairs. simpl. pairs. intros L R ?. inj.
generalize (backward_add_correct a c e i j).
generalize (backward_add_correct b d f i j).
autorw'.
split; apply reduction_spec; intuition.
intros (a,b)(c,d)(e,f) i j (?&?)(?&?)(?&?).
pairs. simpl. pairs. intros L R ?. inj.
generalize (backward_sub_correct a c e i j).
generalize (backward_sub_correct b d f i j).
autorw'.
split; apply reduction_spec; intuition.
intros X (a,b)(c,d)(e,f) i j (?&?)(?&?)(?&?).
pairs. simpl. pairs. intros L R ?. inj.
generalize (backward_cmp_correct X a c e i j).
generalize (backward_cmp_correct X b d f i j).
autorw'.
split; apply reduction_spec; intuition.
intros X (a,b)(c,d)(e,f) i j (?&?)(?&?)(?&?).
pairs. simpl. pairs. intros L R ?. inj.
generalize (backward_cmpu_correct X a c e i j).
generalize (backward_cmpu_correct X b d f i j).
autorw'.
split; apply reduction_spec; intuition.

intros (a,b)(c,d)(e,f) i j (?&?)(?&?)(?&?).
generalize (backward_and_correct a c e i j).
generalize (backward_and_correct b d f i j).
intros ();auto. intros (r1&r2) (U&?&?).
intros ();auto. intros (l1&l2) (V&?&?).
unfold backward_and, reduced_product_num_dom, red_prod_backop_bin_list.
case_eq (backward_and b d f). intros ?. autorw'. intuition.
intros (?,?) l ?. autorw'. destruct l. inv U. inj.
set (q := fun ab => let '(a,c) := ab ina r1, ρ c r2)).
exists (q (l1,l2)). split.
2: simpl; split; eapply reduction_spec; intuition.
apply in_map. auto.
intuition.
eexists. split. left. reflexivity. simpl. intuition.

intros (a,b)(c,d)(e,f) i j (?&?)(?&?)(?&?).
generalize (backward_or_correct a c e i j).
generalize (backward_or_correct b d f i j).
intros ();auto. intros (r1&r2) (U&?&?).
intros ();auto. intros (l1&l2) (V&?&?).
unfold backward_or, reduced_product_num_dom, red_prod_backop_bin_list.
case_eq (backward_or b d f). intros ?. autorw'. intuition.
intros (?,?) l ?. autorw'. destruct l. inv U. inj.
set (q := fun ab => let '(a,c) := ab ina r1, ρ c r2)).
exists (q (l1,l2)). split.
2: simpl; split; eapply reduction_spec; intuition.
apply in_map. auto.
intuition.
eexists. split. left. reflexivity. simpl. intuition.
Qed.