Module LatticeSignatures

Lattice signatures.

Require Import Utf8.
Require Import List Maps Coqlib.

Definition incl {A} (P1 P2:A->Prop) :=
  forall a, P1 a -> P2 a.
Notation "P1P2" := (forall a, P1 a -> P2 a) (at level 20).
Notation "xP" := (P x) (at level 19, only parsing).
Notation "'℘' A" := (A -> Prop) (at level 0).
Notation "fg" := (fun x => f (g x)) (at level 99, left associativity).
Notation "XY" := (fun x => X xY x) (at level 19).
Notation "XY" := (fun x => X xY x) (at level 19).
Notation "∅" := (fun _ => False).

Module Adom.
Unset Elimination Schemes.
Class adom (A:Type) (B:Type) : Type := {
  leb: A -> A -> bool;
  top: A;
  join: A -> A -> A;
  widen: A -> A -> A;
  
  gamma: A -> ℘ B;
  gamma_monotone: forall {a1} {a2},
    leb a1 a2 = true -> gamma a1gamma a2;
  gamma_top: forall x, xgamma top;
  join_sound: ∀ x y, gamma xgamma ygamma (join x y)
}.
End Adom.
Export Adom.
Hint Resolve @gamma_monotone @gamma_top.

Set Implicit Arguments.
Inductive botlift (A:Type) : Type := Bot | NotBot (x:A).
Implicit Arguments Bot [A].
Notation "t +⊥" := (botlift t) (at level 39).

Definition bot {A B: Type} (l:adom (A +⊥) B) : A+⊥ := Bot.

Notation "xy" := (leb x y) (at level 39).
Notation "xy" := (join x y) (at level 40).
Notation "xy" := (widen x y) (at level 40).
Notation "⊤" := top (at level 40).
Notation γ := gamma.

Instance lift_bot {A B:Type} (l:adom A B) : adom (A+⊥) B :=
{ leb :=
    (fun x y =>
      match x, y with
        | Bot, _ => true
        | NotBot x, NotBot y => leb x y
        | _, _ => false
      end);
  top := (NotBot top);
  join :=
    (fun x y =>
      match x, y with
        | Bot, _ => y
          |_ , Bot => x
        | NotBot x, NotBot y => NotBot (join x y)
      end);
  widen :=
    (fun x y =>
      match x, y with
        | Bot, _ => y
          |_ , Bot => x
        | NotBot x, NotBot y => NotBot (widen x y)
      end);
  gamma :=
    (fun x =>
      match x with
      | Bot => fun _ => False
        | NotBot x => gamma x
      end)
}.
Proof.
    destruct a1; destruct a2; simpl; intuition; try congruence.
    apply (gamma_monotone H); auto.
    intros; apply gamma_top.
    intros [|x] [|y]; simpl; try now intuition.
    apply join_sound.
  Defined.

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

Definition botlift_fun2 {A B C:Type} (f:A->B->C) (x:A+⊥) (y:B+⊥) : C+⊥ :=
  match x, y with
    | Bot, _
    | _, Bot => Bot
    | NotBot x, NotBot y => NotBot (f x y)
  end.

Definition botlift_prop {A B: Type} (f: A → ℘ B) : A+⊥ → ℘ B :=
  fun a => match a with Bot => ∅ | NotBot a' => f a' end.

Section union_list.
Context {A B:Type}.
Variable bot : A.
Variable union : A -> A -> A.
Variable gamma : A -> B -> Prop.
Variable union_correct : forall ab1 ab2 m,
  gamma ab1 m \/ gamma ab2 m -> gamma (union ab1 ab2) m.

Fixpoint union_list (l:list A) : A :=
  match l with
    | nil => bot
    | a::nil => a
    | a::q => union a (union_list q)
  end.

Lemma union_list_correct : forall l ab m,
  In ab l ->
  mgamma ab ->
  mgamma (union_list l).
Proof.
  induction l; simpl.
  intuition.
  intros ab m [->|H] K; destruct l; intuition eauto.
  destruct H.
Qed.

End union_list.

Module WProd.
Section lat.
  Context {t1 t2 B: Type}.
  Variable lat1 : adom t1 B.
  Variable lat2 : adom t2 B.
  
  Definition A: Type := (t1*t2)%type.
  
  Definition leb: A -> A -> bool :=
    fun x y =>
      let (x1,x2) := x in
      let (y1,y2) := y in
        (leb x1 y1
      &&
        leb x2 y2)%bool.
  
  Definition top: A :=
    (top, top).
  
  Definition join: A -> A -> A :=
    fun x y =>
      let (x1,x2) := x in
      let (y1,y2) := y in
        (join x1 y1,
          join x2 y2).
  
  Definition widen: A -> A -> A :=
    fun x y =>
      let (x1,x2) := x in
      let (y1,y2) := y in
        (widen x1 y1,
          widen x2 y2).
  
  Definition gamma: A -> B -> Prop :=
    fun x =>
      let (x1,x2) := x in
      gamma x1gamma x2.

  Lemma gamma_monotone: forall a1 a2,
    leb a1 a2 = true -> gamma a1gamma a2.
Proof.
    destruct a1; destruct a2; simpl.
    rewrite Bool.andb_true_iff in *.
    destruct 1.
    intros b [T1 T2].
    split; eapply gamma_monotone; eauto.
  Qed.
  
  Lemma gamma_top: forall x, gamma top x.
Proof.
    simpl; split; auto.
  Qed.

  Lemma join_sound: forall x y, gamma xgamma ygamma (join x y).
Proof.
    intros (a,b)(c,d) q. simpl. intuition auto using @join_sound.
  Qed.

  Instance make : adom A B :=
   {leb := leb;
    top := top;
    join := join;
    widen := widen;
    gamma := gamma;
    gamma_monotone := gamma_monotone;
    gamma_top := gamma_top
   ;join_sound := join_sound
   }.

End lat.
End WProd.

Module WPFun (P:TREE).
Module PP_Prop := Tree_Properties P.

Section lat.
Context {t0 B: Type}.
Variable lat : adom t0 B.

Definition A := P.t t0.
Definition t := (P.t t0)+⊥.

Definition bot : t := Bot.

Definition topA : A := (P.empty _).
Definition top : t := NotBot topA.

Definition getA (m: A) (r: P.elt) : t0 :=
  match P.get r m with
    | None => ⊤
    | Some i => i
  end.

Definition get (app: t) (r: P.elt) : t0+⊥ :=
  match app with
    | Bot => Bot
    | NotBot m =>
      match P.get r m with
        | None => NotBot (⊤)
        | Some i => NotBot i
      end
  end.

Definition set (app: t) (r: P.elt) (i:t0+⊥) : t :=
  match i with
    | Bot => Bot
    | NotBot i =>
      match app with
        | Bot => Bot
        | NotBot m => NotBot (P.set r i m)
      end
  end.

Definition p_forall {A:Type} (m:P.t A) (f:P.elt->A->bool) : bool :=
  P.fold (fun b0 p a => b0 && f p a)%bool m true.


Lemma p_forall_correct1 : forall A (f:P.elt->A->bool) m,
  p_forall m f = true ->
  forall n ins, P.get n m = Some ins -> f n ins = true.
Proof.
  intros A f m; unfold p_forall.
  apply PP_Prop.fold_rec
    with (P:=fun m b => b = true -> forall n ins, P.get n m = Some ins -> f n ins = true); intros.
  apply H0; auto.
  rewrite H; auto.
  rewrite P.gempty in H0; congruence.
  rewrite P.gsspec in H3; destruct P.elt_eq.
  elim andb_prop with (1:=H2); auto. congruence.
  elim andb_prop with (1:=H2); auto.
Qed.

Lemma p_forall_correct2 : forall A (f:P.elt->A->bool) m,
  (forall n ins, P.get n m = Some ins -> f n ins = true) ->
  p_forall m f = true.
Proof.
  intros A f m; unfold p_forall.
  apply PP_Prop.fold_rec
    with (P:=fun m b => (forall n ins, P.get n m = Some ins -> f n ins = true) -> b = true ); intros; auto.
  apply H0; auto.
  intros n ins; rewrite H; auto.
  rewrite H1; simpl.
  apply H2.
  rewrite P.gss; auto.
  intros; apply H2.
  rewrite P.gsspec; destruct P.elt_eq; auto; congruence.
Qed.

Lemma p_forall_correct : forall A (f:P.elt->A->bool) m,
  p_forall m f = true <->
  (forall n ins, P.get n m = Some ins -> f n ins = true).
Proof.
  split; eauto using p_forall_correct1, p_forall_correct2.
Qed.


Definition leb0 (m1 m2:A) : bool :=
    p_forall m2
    (fun x i2 =>
          match get (NotBot m1) x with
            | Bot => true
            | NotBot i1 => leb i1 i2
          end).

  Lemma leb_le: forall m1 m2,
    leb0 m1 m2 = true -> (forall p, (get (NotBot m1) p) ⊑ (get (NotBot m2) p) = true
                                \/ get (NotBot m2) p = ⊤).
Proof.
    simpl; intros;
      unfold leb0 in *; auto.
    rewrite p_forall_correct in H.
    case_eq (P.get p m2); auto.
    intros.
    left. now eapply H.
  Qed.

  Definition joinA : A -> A -> A :=
    P.combine
     (fun x y =>
       match x, y with
         | None, _ => None
           |_ , None => None
         | Some x, Some y => Some (join x y)
       end).

  Definition widenA: A -> A -> A :=
    P.combine
    (fun x y =>
      match x, y with
        | None, _ => None
          |_ , None => None
        | Some x, Some y => Some (widen x y)
      end).

    Definition gamma (m: A) (rs: P.elt -> B) : Prop :=
    forall r, gamma (get (NotBot m) r) (rs r).
  
  Lemma gamma_monotone: forall x y,
    leb0 x y = true ->
    gamma xgamma y.
Proof.
    unfold gamma. intros x y H a H0 r.
    destruct (leb_le H r) as [K|K].
    apply @gamma_monotone with (get (NotBot x) r); eauto.
    rewrite K; auto.
  Qed.

  Section join_correct.
    Variable Ljoin_correct: forall x y:t0, forall b,
      γ x b \/ γ y b -> γ (xy) b.

    Lemma join_correct : forall (x y:A) b,
      gamma x b \/ gamma y b -> gamma (joinA x y) b.
Proof.
      unfold gamma, get; simpl; intros; unfold joinA.
      rewrite P.gcombine; auto.
        destruct H; generalize (H r);
          destruct (P.get r x); destruct (P.get r y); simpl; auto.
    Qed.
  End join_correct.

  Instance make : adom A (P.elt -> B) :=
   {leb := leb0;
    top := topA;
    join := joinA;
    widen := widenA;
    gamma := gamma;
    gamma_monotone := gamma_monotone
   ;join_sound := join_correct join_sound
   }.
Proof.
    unfold gamma, topA; simpl; intros.
    rewrite P.gempty.
    eauto.
  Defined.

  Instance makebot: adom t (P.elt -> B) := lift_bot make.

Lemma gamma_set1 : forall (app:t) rs ab x,
  γ app rs ->
  γ ab (rs x) ->
  γ (set app x ab) rs.
Proof.
  unfold gamma, set; intros.
  destruct ab; simpl; unfold gamma; auto.
  destruct app; simpl; intros.
  elim H.
  rewrite P.gsspec; destruct P.elt_eq; auto.
  subst; apply H0.
  apply (H r).
Qed.

Lemma gamma_set2 : forall (app:t) rs ab v x,
  γ app rs ->
  γ ab v ->
  γ (set app x ab) (fun p => if P.elt_eq p x then v else rs p).
Proof.
  unfold gamma, set; intros.
  destruct ab; simpl; unfold gamma; auto.
  destruct app; simpl; intros.
  elim H.
  rewrite P.gsspec; destruct P.elt_eq; auto.
  apply H.
Qed.

Definition forget (app: t) (r: P.elt) : t :=
  match app with
    | Bot => Bot
    | NotBot m => NotBot (P.remove r m)
  end.

Lemma gamma_forget : forall (app:t) rs rs' x,
  γ app rs ->
  (forall y, x<>y -> rs y = rs' y) ->
  γ (forget app x) rs'.
Proof.
  simpl; unfold gamma, forget; intros.
  destruct app; simpl; intros.
  intuition.
  generalize (H r); clear H; simpl in *.
  rewrite P.grspec; destruct P.elt_eq; auto.
  rewrite <- H0; auto.
Qed.

Lemma gamma_app: forall (m: t) (rs: P.elt -> B),
  γ m rs ->
  forall r, γ (get m r) (rs r).
Proof.
  destruct m; simpl; auto.
Qed.
Hint Resolve gamma_app.

End lat.

End WPFun.

Class EqDec (T: Type) := eq_dec : ∀ x y : T, { x = y } + { xy }.
Instance PosEqDec : EqDec positive := peq.

Definition upd `{X: Type} `{EqDec X} {Y} (f: XY) (p: X) (v:Y) :=
  fun q => if eq_dec p q then v else f q.

Lemma upd_s `{X: Type} `{EqDec X} {Y} (f: XY) p v:
  (upd f p v) p = v.
Proof.
unfold upd. case (eq_dec p p); tauto. Qed.

Lemma upd_o `{X: Type} `{EqDec X} {Y} (f: XY) p v p' :
  pp' → (upd f p v) p' = f p'.
Proof.
unfold upd. case (eq_dec p p'); tauto. Qed.