Module Intervals

Require Import
  Coqlib Integers
  Utf8 DLib
  LatticeSignatures AbEnvironment
  AbNumDomain
  IntervalDomain.

Instance: (signed) interval domain.
Definition botlift_backop {A} op (x y: A+⊥) :=
  match x, y with
    | NotBot x', NotBot y' => op x' y'
    | _, _ => (@Bot A, @Bot A)
  end.

Definition bb {A B C} (f: ABC) (a: A) (b: B) : C+⊥ := NotBot (f a b).

Instance itv_num : num_dom Interval.itv := Build_num_dom
  Interval.signed_itv_adom
  Interval.meet
  Interval.signed
  Interval.booleans
  (fun i s => NotBot (match s with Signed => i | Unsigned => Interval.reduc2unsigned i end))
  (@NotBot _Interval.neg) (@NotBot _Interval.not)
  (@NotBot _Interval.notbool)
  (@NotBot _Interval.boolval)
  (bb Interval.add) (bb Interval.sub)
  (bb Interval.mult) (bb Interval.div) (bb Interval.shl)
  (bb Interval.and) (bb Interval.or)
  (bb Interval.xor)
  (fun m M x => Interval.is_in_interval (Int.signed m) (Int.signed M) x)
  Interval.backward_boolval
  Interval.backward_notbool
  Interval.backward_neg
  Interval.backward_not
  Interval.backward_add
  Interval.backward_sub
  Interval.backward_cmp
  Interval.backward_cmpu
  Interval.backward_and
  Interval.backward_or
.

Instance itv_num_correct : num_dom_correct itv_num.
Proof.
  split.
  intros; apply Interval.meet_correct; intuition.
  intros i; simpl. apply Interval.signed_correct.
  apply Interval.bool_correct_zero.
  apply Interval.bool_correct_one.
  intros; split; simpl; auto. apply Interval.reduc2unsigned_correct; auto.
  unfold neg. simpl. intros x a (j & K & L); subst. apply Interval.neg_correct. auto.
  unfold not. simpl. intros x a (j & K & L); subst. apply Interval.not_correct. auto.
  intros x. simpl. apply Interval.notbool_correct.
  intros x. simpl. apply Interval.boolval_correct.
  intros x y a (j & k & J & K & ->). simpl. apply Interval.add_correct; auto.
  intros x y a (j & k & J & K & ->). simpl. apply Interval.sub_correct; auto.
  intros x y a (j & k & J & K & ->). simpl. apply Interval.mul_correct; auto.
  intros x y a (j & k & J & K & ->). simpl. apply Interval.divs_correct; auto.
  intros x y a (j & k & J & K & ->). simpl. apply Interval.shl_correct; auto.
  intros x y a (j & k & J & K & ->). simpl. apply Interval.and_correct; auto.
  intros x y a (j & k & J & K & ->). simpl. apply Interval.or_correct; auto.
  intros x y a (j & k & J & K & ->). simpl. apply Interval.xor_correct; auto.
  intros m M x.
  intros K i H. pose proof (Interval.is_in_interval_correct _ _ _ K i H). unfold Int.lt. split; bif; auto; intuition.
  intro; apply Interval.backward_boolval_correct.
  intro; apply Interval.backward_notbool_correct.
  intro; apply Interval.backward_neg_correct.
  intro; apply Interval.backward_not_correct.
  intro; apply Interval.backward_add_correct.
  intro; apply Interval.backward_sub_correct.
  repeat intro. apply Interval.backward_cmp_correct; auto.
  repeat intro. apply Interval.backward_cmpu_correct; auto.
  intro; apply Interval.backward_and_correct.
  intro; apply Interval.backward_or_correct.
Qed.

Instance: (unsigned) interval domain.
Module UInterval.

  Import Interval.

  Definition const (i: int) : itv :=
    ITV (Int.unsigned i) (Int.unsigned i).

  Definition neg (x: itv) : itv :=
    if zeq 0 (max x)
    then x
    else if zle (min x) 0
         then utop
         else ITV (Int.modulus - max x) (Int.modulus - min x).

  Definition repr (x: itv) : itv :=
    if xutop then x else utop.

  Definition add (x y: itv) : itv :=
    let m := min x + min y in
    let M := max x + max y in
    if zle m Int.max_unsigned
    then if zle M Int.max_unsigned
         then ITV m M
         else utop
    else ITV (m - Int.modulus) (M - Int.modulus).

  Definition not (x: itv) : itv :=
    add (neg x) (ITV Int.max_unsigned Int.max_unsigned).

  Definition sub (x y: itv) : itv :=
    repr (ITV (min x - max y) (max x - min y)).

  Definition notbool (i:itv) : itv :=
    if zlt 0 (min i) || zlt (max i) 0
    then ITV 0 0
    else if zeq 0 (min i) && zeq 0 (max i)
         then ITV 1 1
         else ITV 0 1.

  Definition todo (x y: itv) : itv := utop.

  Definition back_id1 (x y: itv) : itv+⊥ := NotBot x.
  Definition back_id2 (x y z: itv) : itv+⊥ * itv+⊥ := (NotBot x, NotBot y).
  Definition back_id_list (x y z: itv) : list (itv+⊥ * itv+⊥) := (NotBot x, NotBot y)::nil.

  Definition backward_ltu (i1 i2:itv) : itv+⊥ * itv+⊥ :=
    (i1 ⊓ (reduce 0 ((max i2)-1)),
     i2 ⊓ (reduce ((min i1)+1) Int.max_unsigned)).

  Definition backward_ne (x y:itv) : itv+⊥ * itv+⊥ :=
    let '(x', y') := backward_ltu x y in
    let '(y'', x'') := backward_ltu y x in
      (x' ⊔ x'', y' ⊔ y'').

  Definition backward_leu (i1 i2:itv) : itv+⊥ * itv+⊥ :=
    (i1 ⊓ (reduce 0 (max i2)),
     i2 ⊓ (reduce (min i1) Int.max_unsigned)).

  Definition backward_cmpu (c:comparison) (ab1 ab2 res:itv) : itv+⊥ * itv+⊥ :=
    match is_singleton res with
      | None => (NotBot ab1,NotBot ab2)
      | Some i =>
        if Int.eq i Int.one then
          match c with
            | Ceq => let ab := ab1 ⊓ (NotBot ab2) in (ab,ab)
            | Cne => backward_ne ab1 ab2
            | Clt => backward_ltu ab1 ab2
            | Cle => backward_leu ab1 ab2
            | Cgt => swap (backward_ltu ab2 ab1)
            | Cge => swap (backward_leu ab2 ab1)
          end
        else if Int.eq i Int.zero then
               match c with
                 | Ceq => backward_ne ab1 ab2
                 | Cne => let ab := ab1 ⊓ (NotBot ab2) in (ab,ab)
                 | Cge => backward_ltu ab1 ab2
                 | Cgt => backward_leu ab1 ab2
                 | Cle => swap (backward_ltu ab2 ab1)
                 | Clt => swap (backward_leu ab2 ab1)
               end
             else (NotBot ab1,NotBot ab2)
    end.

Instance itvu_num : num_dom itv := Build_num_dom
  unsigned_itv_adom
  meet
  const
  booleans
  (fun i s => NotBot (match s with Signed => reduc2signed i | Unsigned => i end))
  (@NotBot _neg) (@NotBot _not)
  (@NotBot _notbool)
  (@NotBot _boolval)
  (bb add) (bb sub)
  (bb todo) (bb todo)
  (bb todo) (bb todo)
  (bb todo) (bb todo)
  (fun _ _ _ => false)
  back_id1 back_id1
  back_id1 back_id1
  back_id2 back_id2
  (fun _ => back_id2)
  (backward_cmpu)
  back_id_list back_id_list
.

Lemma neg_correct: ∀ x i,
  ugamma x i
  ugamma (neg x) (Int.neg i).
Proof.
  pose proof Int.modulus_pos as P.
  intros (a&B) u H.
  unfold ugamma, min, max in H.
  unfold neg, Int.neg.
  bif.
    unfold ugamma, min, max in *; subst.
    pose proof (Int.unsigned_range u).
    assert (Int.unsigned u = 0) as K2 by intuition.
    rewrite K2 in *. intuition.
  bif.
    apply (ugamma_top (Int.repr (- Int.unsigned u))).
  unfold ugamma, min, max in *.
  destruct u as (u & ?).
  unfold Int.repr, Int.unsigned, Int.intval in *.
  replace (- u mod Int.modulus) with (Int.modulus - u).
  intuition.
  rewrite Zmod_eq; auto.
  replace (- u / Int.modulus) with
  (((-u + Int.modulus) + (- 1 * Int.modulus)) / Int.modulus) by (f_equal; ring).
  rewrite Z_div_plus; auto.
  rewrite Zdiv_small. ring. intuition.
Qed.

Lemma reduce_correct (m M: Z) (i:int) :
  m <= Int.unsigned i <= M
  γ (reduce m M) i.
Proof.
  unfold reduce. destruct zle. 2: intuition. simpl. auto.
Qed.

  Lemma add_correct x y i j:
    ugamma x i
    ugamma y j
    ugamma (add x y) (Int.add i j).
Proof.
    unfold ugamma, add.
    pose proof (Int.unsigned_range i).
    pose proof (Int.unsigned_range j).
    repeat bif; simpl.
    > intros. rewrite Zmod_small. intuition.
      unfold Int.max_unsigned in *. intuition.
    > pose proof (Z_mod_lt (Int.unsigned i + Int.unsigned j) Int.modulus Int.modulus_pos). intros _ _. unfold Int.max_unsigned. intuition.
    > unfold Int.max_unsigned in *.
      intros.
      rewrite mod_sub; intuition.
  Qed.

  Lemma backward_ltu_correct x y i j :
    γ x i
    γ y j
    Int.unsigned i < Int.unsigned j
    let '(u,v) := backward_ltu x y in
    γ u i ∧ γ v j.
Proof.
    intros I J K. simpl.
  unfold backward_ltu. split; apply meet_correctu; split; auto; apply reduce_correct; intuition.
  apply Int.unsigned_range. apply Zle_trans with (Int.unsigned j - 1). intuition. inv J. intuition.
  apply Zle_trans with (Int.unsigned i + 1). inv I. intuition. intuition.
  unfold Int.max_unsigned. generalize (Int.unsigned_range j). intuition.
  Qed.

  Lemma backward_ne_correct x y i j :
    γ x i
    γ y j
    Int.unsigned iInt.unsigned j
    let '(u,v) := backward_ne x y in
    γ u i ∧ γ v j.
Proof.
    intros I J K. repeat red.
    destruct (Ztrichotomy (Int.unsigned i) (Int.unsigned j)) as [H|[H|H]]. 2: contradiction.
    split; apply join_sound; left; eapply backward_ltu_correct; eauto.
    split; apply join_sound; right; eapply backward_ltu_correct; eauto; intuition.
  Qed.

  Lemma backward_leu_correct x y i j :
    γ x i
    γ y j
    Int.unsigned i <= Int.unsigned j
    let '(u,v) := backward_leu x y in
    γ u i ∧ γ v j.
Proof.
    intros I J K. simpl.
    split; apply meet_correctu; split; auto; apply reduce_correct; intuition.
    apply Int.unsigned_range. apply Zle_trans with (Int.unsigned j). intuition. inv J. intuition.
  apply Zle_trans with (Int.unsigned i). inv I. intuition. intuition.
  unfold Int.max_unsigned. generalize (Int.unsigned_range j). intuition.
  Qed.

Instance itvu_num_correct : num_dom_correct itvu_num.
Proof.
  split.
apply meet_correctu.
intros i. split; simpl; apply Zle_refl.
  simpl. split; simpl; intuition.
  simpl. split; simpl; intuition.
  split; simpl. apply reduc2signed_correct; auto. auto.
  simpl. intros a j (i & H & ->). now apply neg_correct.
  simpl. intros a j (i & H & ->).
  rewrite Int.not_neg. unfold not. apply add_correct. apply neg_correct. auto.
  vm_compute. split; discriminate.
  intros x i. simpl. unfold notbool, of_bool.
  intros (A & B).
  generalize (Int.eq_spec i Int.zero).
  bif. intros ->. change (Int.unsigned Int.zero) with 0 in *.
    repeat (destruct zlt); try now intuition. simpl orb.
    red. repeat destruct zeq; try now intuition.
  intros H. repeat destruct zlt; try (now intuition); simpl orb; red;
          try (vm_compute; split; discriminate).
  repeat destruct zeq; try now intuition.
  absurd (Int.unsigned i = 0). intros K.
  pose proof (Int.repr_unsigned i) as Q. rewrite K in Q. subst. intuition.
  intuition.
  intros (a & b) i. simpl. unfold boolval, is_singleton.
  destruct zeq. subst.
  intros [A B]. generalize (Int.eq_spec i Int.zero). destruct Int.eq. intros ->.
  change (Int.unsigned Int.zero) with 0 in *. assert (b = 0). simpl in *; intuition. subst.
  vm_compute; split; discriminate.
  intros K.
  rewrite zeq_false.
  vm_compute; split; discriminate.
  intros H. assert (b = Int.unsigned i). simpl in *; intuition. pose proof (Int.unsigned_range i).
  subst.
  simpl in *. destruct i as (i & I). unfold Int.unsigned in H. rewrite Zmod_small in H; auto.
  simpl in H. subst. elim K.
  unfold Int.zero, Int.repr. f_equal. apply Axioms.proof_irr.
  intros [A B]. generalize (Int.eq_spec i Int.zero). destruct Int.eq. intros ->.
  simpl. repeat destruct zle; try now intuition. apply bool_correct_zero.
  intros _. simpl. bif. apply bool_correct_one. vm_compute; split; discriminate.
  intros x y i (u & v & A); simpl in *. intuition. subst. now apply add_correct.
  intros x y i (u & v & A & B & ->).
  destruct A as [A A']. destruct B as [B B'].
  simpl; unfold sub, repr, Int.sub. simpl.
  destruct zle. destruct zle.
  unfold ugamma; simpl. rewrite Zmod_small. omega. unfold Int.max_unsigned in *. intuition.
  apply ugamma_top.
  apply ugamma_top.
  intros x y i (u & v & A); simpl in *; intuition. unfold todo. apply ugamma_top.
  intros x y i (u & v & A); simpl in *; intuition. unfold todo. apply ugamma_top.
  intros x y i (u & v & A); simpl in *; intuition. unfold todo. apply ugamma_top.
  intros x y i (u & v & A); simpl in *; intuition. unfold todo. apply ugamma_top.
  intros x y i (u & v & A); simpl in *; intuition. unfold todo. apply ugamma_top.
  intros x y i (u & v & A); simpl in *; intuition. unfold todo. apply ugamma_top.
  simpl. intuition.
  intros ? ? ? H _; exact H.
  intros ? ? ? H _; exact H.
  intros ? ? ? H _; exact H.
  intros ? ? ? H _; exact H.
  intros ? ? ? ? ? H K _; exact (conj H K).
  intros ? ? ? ? ? H K _; exact (conj H K).
  intros ? ? ? ? ? ? H K _; exact (conj H K).
  Opaque meet.
  simpl. unfold backward_cmpu.
  intros c x y (rl,rr) i j I J R. unfold is_singleton. simpl in I,J,R.
  destruct zeq. 2: simpl; intuition.
  subst.
  generalize (Int.eq_spec (of_bool (Int.cmpu c i j)) Int.one).
  destruct Int.eq; intros C.
  autorw'.
  assert (rr = 1) by (destruct rr as [|p|p]; vm_compute in R; try destruct p; intuition). subst.
  clear R. repeat red.
  destruct c; simpl in C.
  generalize (Int.eq_spec i j). destruct Int.eq. intros ->.
  split; simpl; apply meet_correctu; intuition.
  discriminate.
  generalize (Int.eq_spec i j). destruct Int.eq. intros ->. discriminate. intros.
  apply backward_ne_correct; auto. apply unsigned_inj; auto.
  unfold Int.ltu in C. destruct zlt. 2: discriminate.
  eapply backward_ltu_correct; auto.
  unfold Int.ltu in C. destruct zlt. discriminate.
  eapply backward_leu_correct; auto. intuition.
  unfold Int.ltu in C. destruct zlt. 2: discriminate.
  repeat red; apply and_comm. eapply backward_ltu_correct; auto.
  unfold Int.ltu in C. destruct zlt. discriminate.
  repeat red; apply and_comm. eapply backward_leu_correct; auto. intuition.
  assert (rr = 0). unfold of_bool in R. destruct Int.cmpu. elim C; reflexivity.
  destruct rr; vm_compute in R; intuition.
  subst. clear R. repeat red.
  destruct c; simpl in C.
  generalize (Int.eq_spec i j). destruct Int.eq. intros ->. elim C; reflexivity.
  intros. apply backward_ne_correct; auto. apply unsigned_inj; auto.
  generalize (Int.eq_spec i j). destruct Int.eq. intros ->.
  simpl; split; apply meet_correctu; intuition.
  elim C. reflexivity.
  unfold Int.ltu in C. destruct zlt. elim C; reflexivity.
  repeat red. apply and_comm. apply backward_leu_correct; intuition.
  unfold Int.ltu in C. destruct zlt. 2: elim C; reflexivity.
  repeat red. apply and_comm. apply backward_ltu_correct; intuition.
  unfold Int.ltu in C. destruct zlt. elim C; reflexivity.
  apply backward_leu_correct; intuition.
  unfold Int.ltu in C. destruct zlt. 2: elim C; reflexivity.
  apply backward_ltu_correct; intuition.
  intros ? ? ? ? ? H K _. eexists. split. left. reflexivity. exact (conj H K).
  intros ? ? ? ? ? H K _. eexists. split. left. reflexivity. exact (conj H K).
Qed.

End UInterval.

Instance: reduction for signed/unsigned intervals.
Import Interval.
Instance itv_red : reduction itv itv :=
{| ρ := botbind2 (fun s u =>
     let s' := reduc2signed u in
     let u' := reduc2unsigned s in
     match sNotBot s', uNotBot u' with
       | Bot, _ | _, Bot => Bot
       | NotBot a, NotBot b => NotBot (a,b)
     end)
|}.

Instance itv_red_correct : reduction_correct signed_itv_adom unsigned_itv_adom.
Proof.
  Hint Resolve reduc2unsigned_correct reduc2signed_correct.
  split.
  intros [|a] [|b]; try now simpl.
  intros i (A & B).
  unfold ρ, itv_red, botbind2.
  pose proof (meet_correct a (NotBot (reduc2signed b))) as X.
  pose proof (@meet_correctu b (NotBot (reduc2unsigned a))) as Y.
  bot;
  intros; autorw'; try discriminate.
  destruct a. simpl in *; eauto.
  destruct b. simpl in *. eauto.
  inj.
  split. apply X; simpl; auto.
  apply Y; simpl; auto.
Qed.

Instance intervals : num_dom _ := @reduced_product_num_dom _ _ itv_num UInterval.itvu_num itv_red.
Instance intervals_correct : num_dom_correct intervals :=
  reduced_product_num_dom_correct _ _ itv_num_correct UInterval.itvu_num_correct _ _.