Require Export Setoid Coq.Classes.Morphisms.
Require Import EqDec Computable Util LengthEq AutoIndTac Get LengthEq.
Require Export CSet Containers.SetDecide.
Require Export MapBasics MapLookup MapAgreement.

Set Implicit Arguments.

Section MapUpdate.
  Open Scope fmap_scope.
  Variable X : Type.
  Context `{OrderedType X}.
  Variable Y : Type.

  Fixpoint update_list (m:X -> Y) (f:X -> Y) (L:list X) :=
    match L with
      | nil => m
      | x::L => (update_list m f L) [x <- f x]
    end.

  Lemma update_list_agree_minus {R} `{Symmetric Y R} `{Transitive Y R} lv (E E' f:X -> Y) XL
  :  agree_on R lv E' E
     -> agree_on R (lv\of_list XL) (update_list E' f XL) E.
  Proof.
    intros. general induction XL; simpl.
    rewrite minus_empty. eassumption.
    rewrite add_union_singleton. rewrite union_comm. rewrite <- minus_union.
    eapply agree_on_update_dead. cset_tac.
    eauto using agree_on_incl, incl_minus.
  Qed.

  Corollary update_list_agree_self {R} `{Equivalence Y R} lv (E:X -> Y) L f
    : agree_on R (lv\of_list L) (update_list E f L) E.
  Proof.
    eapply update_list_agree_minus. reflexivity.
  Qed.

  Lemma update_list_no_upd (m:X -> Y) f L x
    : x ∉ of_list L
    -> (update_list m f L) x === m x.
  Proof.
    intros. general induction L; simpl; eauto. lud.
    + exfalso. eapply H0. simpl in * |- *. rewrite e. eapply add_1; eauto.
    + assert (~x ∈ of_list L).
      - intro. eapply H0. simpl. eapply add_2; eauto.
      - eauto.
  Qed.

  Lemma update_list_upd R `{Equivalence Y R} (m:X -> Y) f L x
    : x ∈ of_list L
    -> Proper (_eq ==> R) f
    -> (update_list m f L) x === f x.
  Proof.
    intros. general induction L; simpl; eauto.
    + lud.
      - rewrite e; eauto.
      - eapply IHL; eauto. eapply zadd_3; eauto.
  Qed.

End MapUpdate.

Section MapUpdateList.
  Variable X : Type.
  Context `{OrderedType X}.
  Variable Y : Type.

  Open Scope fmap_scope.

  Fixpoint update_with_list XL YL (m:X -> Y) :=
    match XL, YL with
      | x::XL, y::YL => (update_with_list XL YL m)[x <- y]
      | _, _ => m
    end.

  Notation "f [ w <-- x ]" := (update_with_list w x f) (at level 29, left associativity).

  Lemma update_nodup_commute `{OrderedType Y} (XL:list X) (VL:list Y) E D x y
  : length XL = length VL
    -> NoDupA _eq (x::XL)
    -> agree_on _eq D (E [x <- y] [XL <-- VL]) (E [XL <-- VL] [x <- y]).
  Proof.
    intros. eapply length_length_eq in H1.
    general induction H1; simpl in * |- *; dcr; simpl in *; eauto.
    hnf; intros. lud; invt NoDupA.
    - exfalso; eauto.
    - invt NoDupA.
      etransitivity; [eapply IHlength_eq|]; eauto; lud.
    - invt NoDupA.
      etransitivity; [eapply IHlength_eq|]; eauto; lud.
  Qed.

  Lemma update_nodup_commute_eq (XL:list X) (VL:list Y) E D x y
  : length XL = length VL
    -> NoDupA _eq (x::XL)
    -> agree_on eq D (E [x <- y] [XL <-- VL]) (E [XL <-- VL] [x <- y]).
  Proof.
    intros LEN UNIQ. length_equify.
    general induction LEN; simpl in * |- *; dcr; simpl in *; eauto.
    hnf; intros. lud; invt NoDupA.
    - exfalso; eauto.
    - invt NoDupA; etransitivity; [eapply IHLEN|]; eauto; lud.
    - invt NoDupA; etransitivity; [eapply IHLEN|]; eauto; lud.
  Qed.

  Lemma update_with_list_no_update (E:X -> Y) Y' Z x
    : x ∉ of_list Z
    -> (E [ Z <-- Y' ]) x = E x.
  Proof.
    intros. general induction Z; simpl in *; destruct Y'; eauto.
    lud; cset_tac.
  Qed.

  Context `{Equivalence Y}.

  Lemma update_with_list_agree lv (E E':X -> Y) XL YL
    : agree_on R (lv\of_list XL) E E'
    -> length XL = length YL
    -> agree_on R lv (E [ XL <-- YL]) (E' [ XL <-- YL ]).
  Proof.
    intros. eapply length_length_eq in H2.
    general induction XL; simpl in * |- *.
    - rewrite (@minus_empty _ _ lv) in H1; eauto.
    - eapply agree_on_update_same. reflexivity.
      eapply IHXL; eauto.
      eapply agree_on_incl; eauto. cset_tac; intuition.
  Qed.

  Lemma update_with_list_agree_preserve lv (E E':X -> Y) XL YL
    : agree_on R lv E E'
    -> agree_on R lv (E [ XL <-- YL]) (E' [ XL <-- YL ]).
  Proof.
    intros.
    general induction XL; destruct YL; simpl in * |- *; eauto.
    eapply agree_on_update_same; eauto. reflexivity.
    eapply agree_on_incl; eauto.
  Qed.

  Lemma update_with_list_agree_minus lv (E E':X -> Y) XL YL
    : agree_on R lv E' E
    -> agree_on R (lv\of_list XL) (E' [ XL <-- YL ]) E.
  Proof.
    intros.
    general induction XL; simpl.
    - rewrite minus_empty. eassumption.
    - destruct YL; simpl.
      + eapply agree_on_incl; eauto.
      + rewrite add_union_singleton. rewrite union_comm. rewrite <- minus_union.
        eapply agree_on_update_dead. cset_tac.
        eauto using agree_on_incl, incl_minus.
  Qed.

  Hint Resolve minus_single_singleton minus_single_singleton' minus_single_singleton''.

  Lemma update_with_list_agree_inv lv (E E':X -> Y) XL YL
  : length XL = length YL
    -> agree_on R lv (E' [ XL <-- YL ]) E
    -> agree_on R (lv\of_list XL) E' E.
  Proof.
    intros. eapply length_length_eq in H1.
    general induction H1; simpl in * |- *.
    - eapply agree_on_incl; eauto.
    - rewrite add_union_singleton. rewrite <- minus_union.
      eapply IHlength_eq; eauto.
      eapply agree_on_update_inv; eauto.
  Qed.

  Lemma update_with_list_agree_self  `{Defaulted X} lv (E:X -> Y) XL YL
    : agree_on R (lv\of_list XL) (E [ XL <-- YL]) E.
  Proof.
    general induction XL; simpl. rewrite minus_empty. reflexivity.
    destruct YL. reflexivity.
    rewrite add_union_singleton.
    rewrite union_comm. rewrite <- minus_union. eapply agree_on_update_dead.
    cset_tac.
    eapply agree_on_incl. eapply IHXL; eauto.
    instantiate (1:=lv). eapply incl_minus.
  Qed.

  Lemma update_id `{OrderedType Y} (m:X -> Y) x `{Proper _ (_eq ==> _eq) m}
    : @feq _ _ _eq (m [x <- m x])  m.
  Proof.
    intros y. lud. rewrite e; eauto.
  Qed.

End MapUpdateList.

Notation "f [ w <-- x ]" := (update_with_list w x f) (at level 29, left associativity).

Lemma update_with_list_lookup_in_list X `{OrderedType X} B E
      (Z:list X) (Y:list B) z
: length Z = length Y
  -> z ∈ of_list Z
  -> exists n y z', get Z n z' /\ get Y n y /\ E [Z <-- Y] z === y /\ z === z'.
Proof.
  intros. eapply length_length_eq in H0.
  general induction H0; simpl in *.
  decide (z === x).
  - exists 0, y, x; repeat split; eauto using get. lud.
  - cset_tac'.
    edestruct (IHlength_eq E z) as [? [? ]]; eauto; dcr.
    exists (S x0), x1, x2. eexists; repeat split; eauto using get.
    lud. exfalso; eauto.
Qed.

Instance update_inst X `{OrderedType X} Y `{OrderedType Y} :
  Proper ((@feq _ _ _eq) ==> _eq ==> _eq ==> (@feq _ _ _eq)) (@update X Y _).
Proof.
  unfold respectful, Proper, update, feq; intros.
  repeat cases; eqs; eauto.
  exfalso. eapply H7. eapply H2.
  exfalso. eapply H8. eapply H2.
Qed.

Instance update_inst_eq X `{OrderedType X} Y
  : Proper ((@feq X Y eq) ==> eq ==> eq ==> (@feq _ _ eq)) (@update X Y _).
Proof.
  unfold respectful, Proper, update, feq; intros.
  repeat cases; eqs; eauto; congruence.
Qed.

Lemma update_with_list_id X `{OrderedType X} (l:list X)
  : @feq _ _ _eq (update_with_list l l id) id.
Proof.
  general induction l; simpl. reflexivity.
  rewrite IHl. change a with (id a) at 2.
  pose proof update_id. specialize (H0 X _ X _ id a).
  eapply H0. firstorder.
Qed.

Lemma update_with_list_lookup_in {X} `{OrderedType X} {Y} `{OrderedType Y} (f:X->Y) Z Z' x
  : length Z = length Z'
  -> x ∈ of_list Z
  -> f [ Z <-- Z' ] x ∈ of_list Z'.
Proof.
  intros L. eapply length_length_eq in L.
  general induction L; simpl in * |- *.
  eapply add_iff in H1. destruct H1.
  lud; try eapply add_1; eauto.
  lud. eapply add_1; eauto.
  eapply add_iff. right. eapply IHL; eauto.
Qed.

Instance proper_update X `{OrderedType X} Y `{OrderedType Y} (ϱ:X -> Y) (x : X) (y :Y)
: Proper (_eq ==> _eq) ϱ
  -> Proper (_eq ==> _eq) (update ϱ x y) | 5.
Proof.
  unfold Proper, respectful; intros; lud; intuition.
Qed.

Instance proper_update_with_list X `{OrderedType X} Y `{OrderedType Y} (f:X->Y) Z Z'
: Proper (_eq ==> _eq) f
  -> Proper (_eq ==> _eq) (f [Z <-- Z']).
Proof.
  intros.
  general induction Z; eauto.
  - destruct Z'; eauto. simpl.
    eapply proper_update. eauto.
Qed.

Hint Resolve proper_update proper_update_with_list.

Lemma lookup_set_update_not_in_Z' X `{OrderedType X} Y `{OrderedType Y}
  Z Z' (f: X-> Y) x
  : f [Z <-- Z'] x ∉ of_list Z' -> f [Z <-- Z'] x = f x.
Proof.
  general induction Z; simpl in *; eauto.
  destruct Z'; eauto.
  lud. simpl in *; exfalso. cset_tac.
  eapply IHZ; eauto. simpl in *. cset_tac.
Qed.

Lemma lookup_set_update_not_in_Z X `{OrderedType X} Y
  Z Z' (f: X-> Y) x
  : x ∉ of_list Z -> f [Z <-- Z'] x = f x.
Proof.
  general induction Z; simpl in *; eauto.
  destruct Z'; eauto.
  lud. simpl in *; cset_tac.
  eapply IHZ; eauto. cset_tac.
Qed.

Lemma lookup_set_update_not_in_Z'_not_in_Z {X} `{OrderedType X} {Y} `{OrderedType Y}
  (f: X-> Y) x Z Z'
: length Z = length Z'
  -> f [Z <-- Z'] x ∉ of_list Z'
  -> Proper (_eq ==> _eq) f
  -> x ∉ of_list Z.
Proof.
  intros. eapply length_length_eq in H1.
  general induction H1; simpl in *; eauto.
  - cset_tac; intuition.
  - lud.
    + exfalso; cset_tac.
    + cset_tac; eauto.
Qed.

Lemma update_with_list_lookup_not_in {X} `{OrderedType X} {Y} `{OrderedType Y}
      (f:X->Y) (Z:list X) (Z':list Y) x y
  : x ∉ of_list Z
    -> f [ Z <-- Z' ] x === y
    -> f x === y.
Proof.
  general induction Z; simpl in * |- *; eauto.
  destruct Z'; eauto. lud.
  - rewrite add_iff in H1;
    exfalso; intuition.
  - eapply IHZ; eauto. cset_tac.
Qed.

Lemma update_with_list_lookup_set_incl {X} `{OrderedType X} {Y} `{OrderedType Y}
(f:X->Y) Z Z' D
  : length Z = length Z'
    -> D ⊆ of_list Z
    -> Proper (_eq ==> _eq) f
    -> lookup_set (f [ Z <-- Z' ]) D ⊆ of_list Z'.
Proof.
  intros. hnf; intros.
  eapply lookup_set_spec in H4; try eapply proper_update_with_list; eauto.
  destruct H4; dcr. rewrite H6. eapply update_with_list_lookup_in; eauto.
Qed.

Instance update_list_morphism {X} `{OrderedType X} {Y} `{OrderedType Y}
  : Proper (@feq X _ _eq ==> @feq X _ _eq ==> eq ==> (@feq _ _ _eq)) (@update_list X _ Y).
Proof.
  unfold Proper, respectful; intros. subst.
  general induction y1. simpl. eauto. simpl.
  rewrite IHy1; eauto. hnf; intros; lud. eapply H2.
Qed.

Lemma lookup_set_add_update {X} `{OrderedType X} {Y} `{OrderedType Y} (ϱ:X->Y) D x y
      `{Proper _ (_eq ==> _eq) ϱ}
: x ∉ D
  -> lookup_set (update ϱ x y) {x; D} [=] {y; lookup_set ϱ D}.
Proof.
  intros. hnf; intros. lset_tac; lud; eauto.
  - eexists x; lud; eauto.
  - eexists x0; lud; eauto.
Qed.

Lemma lookup_update_same X `{OrderedType X} x Z (ϱ:X->X)
: x ∈ of_list Z
  -> ϱ [Z <-- Z] x === x.
Proof.
  general induction Z; simpl.
  lud; eauto.
  eapply IHZ. simpl in *. cset_tac.
Qed.

Lemma lookup_set_update_union_minus X `{OrderedType X} Y `{OrderedType Y}
 (f: X->Y) D x y `{Proper _ (_eq ==> _eq) f}
  : lookup_set (update f x y) D \ {{y}} [=] lookup_set f (D \ {{x}}) \ {{ y }}.
Proof.
  split; intros; lset_tac.
  - lud; cset_tac; eauto.
  - eexists x0; lud; cset_tac.
Qed.

Lemma lookup_set_update_union_minus_single X `{OrderedType X} Y `{OrderedType Y}
 (f: X->Y) D x y `{Proper _ (_eq ==> _eq) f}
  : lookup_set (update f x y) D \ singleton y [=] lookup_set f (D \ singleton x) \ singleton y.
Proof.
  split; intros; lset_tac; eauto.
  - lud; cset_tac; eauto.
  - eexists x0; lud; cset_tac.
Qed.

Lemma lookup_set_update_union_minus_list X `{OrderedType X} Y `{OrderedType Y}
 (f:X->Y) D Z Z' `{Proper _ (_eq ==> _eq) f}
: length Z = length Z'
  -> lookup_set (f[ Z <-- Z']) D \ of_list Z' [=] lookup_set f (D \ of_list Z) \ of_list Z'.
Proof.
  split; intros; lset_tac; eauto.
  - rewrite H7 in H5.
    eexists x; cset_tac'.
    + eapply lookup_set_update_not_in_Z'_not_in_Z in H5; eauto.
    + eapply lookup_set_update_not_in_Z' in H5; eauto.
      rewrite <- H5; eauto.
  - lud; cset_tac'.
    eexists x; cset_tac'; eauto.
    erewrite lookup_set_update_not_in_Z; eauto.
Qed.

Lemma update_with_list_lookup_list_eq {X} `{OrderedType X} {Y} `{OrderedType Y}
      (f:X->Y) `{Proper _ (_eq ==> _eq) f} (Z:list X) Z' L
: ❬Z❭ = ❬L❭
  -> NoDupA _eq Z
  -> of_list Z [=] Z'
  -> lookup_set (f [ Z <-- L ]) Z' [=] of_list L.
Proof.
  intros Len ND EQ. rewrite <- EQ. clear EQ.
  general induction Len; simpl in * |- *; dcr.
  - rewrite lookup_set_empty; eauto.
  - rewrite lookup_set_add; eauto. lud.
    + rewrite lookup_set_agree.
      rewrite IHLen; eauto. eauto. eauto. eauto.
      eapply agree_on_update_dead; eauto.
    + exfalso; eapply H2; reflexivity.
Qed.

Lemma update_with_list_lookup_list {X} `{OrderedType X} {Y} `{OrderedType Y} (f:X->Y)
      `{Proper _ (_eq ==> _eq) f} Z Z'
: length Z = length Z'
  -> NoDupA _eq Z
  -> lookup_set (f [ Z <-- Z' ]) (of_list Z) ⊆ of_list Z'.
Proof.
  intros L. eapply length_length_eq in L.
  general induction L; simpl in * |- *; dcr.
  - rewrite lookup_set_empty; eauto.
  - rewrite lookup_set_add; eauto. lud.
    + rewrite lookup_set_agree.
      rewrite IHL; eauto.  eauto. eauto.
      eapply agree_on_update_dead; eauto.
    + exfalso; eapply H3; reflexivity.
Qed.

Lemma lookup_set_update_disj {X} `{OrderedType X} {Y} `{OrderedType Y} (f:X->Y)
      `{Proper _ (_eq ==> _eq) f} Z Z' G
: disj (of_list Z) G
  -> lookup_set (f [ Z <-- Z' ]) G [=] lookup_set f G.
Proof.
  intros. hnf; intros.
  rewrite lookup_set_spec; eauto.
  split; intros; dcr.
  - rewrite H6. rewrite lookup_set_update_not_in_Z; eauto with cset.
  - eapply lookup_set_spec in H3; eauto; dcr.
    eexists x.
    rewrite lookup_set_update_not_in_Z; eauto.
Qed.

Instance update_with_list_inst X `{OrderedType X} Y `{OrderedType Y} :
  Proper (eq ==> (list_eq (option_eq eq)) ==> (@feq X (option Y) eq ) ==> (@feq _ _ eq))
         (@update_with_list X _ (option Y)).
Proof.
  unfold respectful, Proper; intros. subst.
  general induction H2; simpl; eauto.
  - destruct y; simpl; eauto.
  - destruct y; simpl; eauto.
    hnf; intros. lud.
    inv H1; eauto.
    eapply IHlist_eq; eauto.
Qed.

Lemma agree_on_update_list_dead X `{OrderedType X} Y (L:list X) (L':list Y) (V:X->Y)
      V' D
  :  agree_on eq D V V'
     -> disj (of_list L) D
     -> agree_on eq D V (V'[L <-- L']).
Proof.
  intros. hnf; intros.
  rewrite lookup_set_update_not_in_Z; eauto.
Qed.

Lemma agree_on_update_list_dead_slot X `{OrderedType X} Y (L:list X) (L':list Y) (V:X->Y) (f:X->X)
      `{Proper _ (_eq ==> _eq) f} V' D
  :  agree_on eq D V (fun x => V' (f x))
     -> disj (of_list L) (map f D)
     -> agree_on eq D V (fun x => V'[L <-- L'] (f x)).
Proof.
  intros. hnf; intros.
  rewrite lookup_set_update_not_in_Z; eauto.
  intro. eauto with cset.
Qed.

Lemma update_with_list_lookup_in_list_first X `{OrderedType X} B E n
      (Z:list X) (Y:list B) z
: length Z = length Y
  -> get Z n z
  -> (forall n' z', n' < n -> get Z n' z' -> z' =/= z)
  -> exists y, get Y n y /\ E [Z <-- Y] z = y.
Proof.
  intros. eapply length_length_eq in H0.
  general induction H0; simpl in *; isabsurd.
  inv H1.
  - exists y; repeat split; eauto using get. lud. exfalso; eauto.
  - edestruct (IHlength_eq E n0 z) as [? [? ]]; eauto using get; dcr.
    + intros. eapply (H2 (S n')); eauto using get. omega.
    + exists x0. eexists; repeat split; eauto using get.
      exploit (H2 0); eauto using get; try omega.
      lud.
Qed.

Lemma list_lookup_in_list_first X `{OrderedType X} B E
      (Z:list X) (Y:list B) x y
: length Z = length Y
  -> (E [Z <-- Y]) x = y
  -> x ∈ of_list Z
  -> exists n y', get Y n y' /\ y === y' /\ (forall n' x', n' < n -> get Z n' x' -> x' =/= x).
Proof.
  intros. length_equify.
  general induction H0; simpl in *; isabsurd. decide (x0 === x).
  - exists 0, y; repeat split; eauto using get. lud. intros; exfalso; omega.
  - cset_tac'.
    edestruct (IHlength_eq E x0) as [? [? ]]; eauto using get; dcr.
    + exists (S x1), x2. repeat split; eauto using get. lud; intuition.
      intros. inv H4. intro; intuition. eapply H6; eauto. omega.
Qed.

Lemma lookup_set_update_with_list {X} `{OrderedType X} {Y} `{OrderedType Y}
  Z Z' (f:X->Y) D `{Proper _ (_eq ==> _eq) f}
  : of_list Z ⊆ D
    -> NoDupA _eq Z
    -> length Z = length Z'
    -> of_list Z' ⊆ lookup_set (update_with_list Z Z' f) (of_list Z).
Proof.
  intros; hnf; intros.
  lset_tac.
  length_equify.
  general induction H4; simpl.
  - simpl in *. cset_tac'.
    + eexists x. lud; split; eauto.
    + edestruct IHlength_eq; eauto; dcr.
      * invt NoDupA.
        eexists x0. lud; eauto. cset_tac.
Qed.

Set Implicit Arguments.

Section MapUpdate'.
  Open Scope fmap_scope.
  Variable X : Type.
  Context `{OrderedType X}.
  Variable Y : Type.

  Fixpoint update_with_list' XL YL (m:X -> Y) :=
    match XL, YL with
      | x::XL, y::YL => update_with_list' XL YL (m[x <- y])
      | _, _ => m
    end.

  Lemma update_with_list_agree' (XL:list X) (VL:list Y) E D
  : length XL = length VL
    -> NoDupA _eq XL
    -> agree_on eq D (E [XL <-- VL]) (update_with_list' XL VL E).
  Proof.
    intros LEN UNIQ. length_equify.
    general induction LEN; simpl in *; eauto.
    - etransitivity. symmetry.
      eapply update_nodup_commute_eq; eauto using length_eq_length.
      eapply IHLEN; eauto.
  Qed.
End MapUpdate'.

Notation "f [ w <-/- x ]" := (update_with_list' w x f) (at level 29, left associativity).


Load LFindLoad.
From lfind Require Import LFind.
Require Import Arith.
From adtind Require Import goal40.

Set Printing Depth 1000.
Definition lfind_eval  x n:=
Cons n x.

Compute lfind_eval  (Nil) (Zero).

Compute lfind_eval  (Cons Zero (Cons (Succ (Succ Zero)) Nil)) (Zero).

Compute lfind_eval  (Cons Zero (Cons (Succ (Succ (Succ (Succ Zero)))) (Cons (Succ Zero) (Cons Zero (Cons (Succ (Succ Zero)) Nil))))) (Succ Zero).

Compute lfind_eval  (Nil) (Succ Zero).

Compute lfind_eval  (Cons (Succ Zero) Nil) (Succ (Succ Zero)).

Compute lfind_eval  (Cons (Succ (Succ (Succ Zero))) Nil) (Succ Zero).

Compute lfind_eval  (Cons Zero (Cons Zero Nil)) (Succ Zero).

Compute lfind_eval  (Cons Zero Nil) (Zero).

Compute lfind_eval  (Cons Zero (Cons (Succ (Succ Zero)) Nil)) (Zero).

Compute lfind_eval  (Nil) (Succ Zero).

Compute lfind_eval  (Nil) (Zero).

Compute lfind_eval  (Cons (Succ Zero) Nil) (Zero).

Compute lfind_eval  (Cons (Succ Zero) (Cons Zero Nil)) (Succ (Succ Zero)).

Compute lfind_eval  (Cons Zero (Cons (Succ Zero) (Cons Zero Nil))) (Zero).

Compute lfind_eval  (Cons (Succ (Succ (Succ Zero))) (Cons Zero (Cons Zero Nil))) (Succ (Succ (Succ Zero))).

Compute lfind_eval  (Cons (Succ Zero) Nil) (Succ Zero).

Compute lfind_eval  (Nil) (Succ (Succ Zero)).

Compute lfind_eval  (Cons (Succ (Succ Zero)) (Cons Zero Nil)) (Succ (Succ Zero)).

Compute lfind_eval  (Cons (Succ Zero) Nil) (Succ Zero).

Compute lfind_eval  (Cons Zero Nil) (Succ Zero).

Compute lfind_eval  (Cons Zero Nil) (Succ Zero).

Compute lfind_eval  (Nil) (Succ Zero).

Compute lfind_eval  (Cons Zero (Cons Zero Nil)) (Succ Zero).

Compute lfind_eval  (Cons Zero (Cons Zero Nil)) (Succ (Succ Zero)).

Compute lfind_eval  (Nil) (Succ Zero).

Compute lfind_eval  (Cons Zero Nil) (Succ (Succ (Succ Zero))).

Compute lfind_eval  (Nil) (Zero).

Compute lfind_eval  (Nil) (Succ Zero).

Compute lfind_eval  (Cons (Succ Zero) Nil) (Zero).

Compute lfind_eval  (Nil) (Succ (Succ (Succ Zero))).

Compute lfind_eval  (Cons Zero (Cons (Succ (Succ Zero)) Nil)) (Zero).

Compute lfind_eval  (Nil) (Zero).


From Hammer Require Import Hammer.

Set Warnings "-notation-overridden,-parsing".
From Coq Require Import Bool.Bool.
From PLF Require Import Maps.
From PLF Require Import Smallstep.
From PLF Require Import Stlc.
From PLF Require MoreStlc.

Module STLCTypes.
Export STLC.

Fixpoint eqb_ty (T1 T2:ty) : bool :=
match T1,T2 with
| Bool, Bool =>
true
| Arrow T11 T12, Arrow T21 T22 =>
andb (eqb_ty T11 T21) (eqb_ty T12 T22)
| _,_ =>
false
end.

Lemma eqb_ty_refl : forall T1,
eqb_ty T1 T1 = true.
Proof. hammer_hook "Typechecking" "Typechecking.STLCTypes.eqb_ty_refl".
intros T1. induction T1; simpl.
reflexivity.
rewrite IHT1_1. rewrite IHT1_2. reflexivity.  Qed.

Lemma eqb_ty__eq : forall T1 T2,
eqb_ty T1 T2 = true -> T1 = T2.
Proof with auto. hammer_hook "Typechecking" "Typechecking.STLCTypes.eqb_ty__eq".
intros T1. induction T1; intros T2 Hbeq; destruct T2; inversion Hbeq.
-
reflexivity.
-
rewrite andb_true_iff in H0. inversion H0 as [Hbeq1 Hbeq2].
apply IHT1_1 in Hbeq1. apply IHT1_2 in Hbeq2. subst...  Qed.
End STLCTypes.

Module FirstTry.
Import STLCTypes.

Fixpoint type_check (Gamma : context) (t : tm) : option ty :=
match t with
| var x =>
Gamma x
| abs x T11 t12 =>
match type_check (update Gamma x T11) t12 with
| Some T12 => Some (Arrow T11 T12)
| _ => None
end
| app t1 t2 =>
match type_check Gamma t1, type_check Gamma t2 with
| Some (Arrow T11 T12),Some T2 =>
if eqb_ty T11 T2 then Some T12 else None
| _,_ => None
end
| tru =>
Some Bool
| fls =>
Some Bool
| test guard t f =>
match type_check Gamma guard with
| Some Bool =>
match type_check Gamma t, type_check Gamma f with
| Some T1, Some T2 =>
if eqb_ty T1 T2 then Some T1 else None
| _,_ => None
end
| _ => None
end
end.

End FirstTry.

Notation " x <- e1 ;; e2" := (match e1 with
| Some x => e2
| None => None
end)
(right associativity, at level 60).

Notation " 'return' e "
:= (Some e) (at level 60).

Notation " 'fail' "
:= None.

Module STLCChecker.
Import STLCTypes.

Fixpoint type_check (Gamma : context) (t : tm) : option ty :=
match t with
| var x =>
match Gamma x with
| Some T => return T
| None   => fail
end
| abs x T11 t12 =>
T12 <- type_check (update Gamma x T11) t12 ;;
return (Arrow T11 T12)
| app t1 t2 =>
T1 <- type_check Gamma t1 ;;
T2 <- type_check Gamma t2 ;;
match T1 with
| Arrow T11 T12 =>
if eqb_ty T11 T2 then return T12 else fail
| _ => fail
end
| tru =>
return Bool
| fls =>
return Bool
| test guard t1 t2 =>
Tguard <- type_check Gamma guard ;;
T1 <- type_check Gamma t1 ;;
T2 <- type_check Gamma t2 ;;
match Tguard with
| Bool =>
if eqb_ty T1 T2 then return T1 else fail
| _ => fail
end
end.

Theorem type_checking_sound : forall Gamma t T,
type_check Gamma t = Some T -> has_type Gamma t T.
Proof with eauto. hammer_hook "Typechecking" "Typechecking.STLCChecker.type_checking_sound".
intros Gamma t. generalize dependent Gamma.
induction t; intros Gamma T Htc; inversion Htc.
-  rename s into x. destruct (Gamma x) eqn:H.
rename t into T'. inversion H0. subst. eauto. solve_by_invert.
-
remember (type_check Gamma t1) as TO1.
destruct TO1 as [T1|]; try solve_by_invert;
destruct T1 as [|T11 T12]; try solve_by_invert;
remember (type_check Gamma t2) as TO2;
destruct TO2 as [T2|]; try solve_by_invert.
destruct (eqb_ty T11 T2) eqn: Heqb.
apply eqb_ty__eq in Heqb.
inversion H0; subst...
inversion H0.
-
rename s into x. rename t into T1.
remember (update Gamma x T1) as G'.
remember (type_check G' t0) as TO2.
destruct TO2; try solve_by_invert.
inversion H0; subst...
-  eauto.
-  eauto.
-
remember (type_check Gamma t1) as TOc.
remember (type_check Gamma t2) as TO1.
remember (type_check Gamma t3) as TO2.
destruct TOc as [Tc|]; try solve_by_invert.
destruct Tc; try solve_by_invert;
destruct TO1 as [T1|]; try solve_by_invert;
destruct TO2 as [T2|]; try solve_by_invert.
destruct (eqb_ty T1 T2) eqn:Heqb;
try solve_by_invert.
apply eqb_ty__eq in Heqb.
inversion H0. subst. subst...
Qed.

Theorem type_checking_complete : forall Gamma t T,
has_type Gamma t T -> type_check Gamma t = Some T.
Proof with auto. hammer_hook "Typechecking" "Typechecking.STLCChecker.type_checking_complete".
intros Gamma t T Hty.
induction Hty; simpl.
-  destruct (Gamma x0) eqn:H0; assumption.
-  rewrite IHHty...
-
rewrite IHHty1. rewrite IHHty2.
rewrite (eqb_ty_refl T11)...
-  eauto.
-  eauto.
-  rewrite IHHty1. rewrite IHHty2.
rewrite IHHty3. rewrite (eqb_ty_refl T)...
Qed.

End STLCChecker.

Module TypecheckerExtensions.

Definition manual_grade_for_type_checking_sound : option (nat*string) := None.

Definition manual_grade_for_type_checking_complete : option (nat*string) := None.
Import MoreStlc.
Import STLCExtended.

Fixpoint eqb_ty (T1 T2 : ty) : bool :=
match T1,T2 with
| Nat, Nat =>
true
| Unit, Unit =>
true
| Arrow T11 T12, Arrow T21 T22 =>
andb (eqb_ty T11 T21) (eqb_ty T12 T22)
| Prod T11 T12, Prod T21 T22 =>
andb (eqb_ty T11 T21) (eqb_ty T12 T22)
| Sum T11 T12, Sum T21 T22 =>
andb (eqb_ty T11 T21) (eqb_ty T12 T22)
| List T11, List T21 =>
eqb_ty T11 T21
| _,_ =>
false
end.

Lemma eqb_ty_refl : forall T1,
eqb_ty T1 T1 = true.
Proof. hammer_hook "Typechecking" "Typechecking.TypecheckerExtensions.eqb_ty_refl".
intros T1.
induction T1; simpl;
try reflexivity;
try (rewrite IHT1_1; rewrite IHT1_2; reflexivity);
try (rewrite IHT1; reflexivity).  Qed.

Lemma eqb_ty__eq : forall T1 T2,
eqb_ty T1 T2 = true -> T1 = T2.
Proof. hammer_hook "Typechecking" "Typechecking.TypecheckerExtensions.eqb_ty__eq".
intros T1.
induction T1; intros T2 Hbeq; destruct T2; inversion Hbeq;
try reflexivity;
try (rewrite andb_true_iff in H0; inversion H0 as [Hbeq1 Hbeq2];
apply IHT1_1 in Hbeq1; apply IHT1_2 in Hbeq2; subst; auto);
try (apply IHT1 in Hbeq; subst; auto).
Qed.

Fixpoint type_check (Gamma : context) (t : tm) : option ty :=
match t with
| var x =>
match Gamma x with
| Some T => return T
| None   => fail
end
| abs x1 T1 t2 =>
T2 <- type_check (update Gamma x1 T1) t2 ;;
return (Arrow T1 T2)
| app t1 t2 =>
T1 <- type_check Gamma t1 ;;
T2 <- type_check Gamma t2 ;;
match T1 with
| Arrow T11 T12 =>
if eqb_ty T11 T2 then return T12 else fail
| _ => fail
end
| const _ =>
return Nat
| scc t1 =>
T1 <- type_check Gamma t1 ;;
match T1 with
| Nat => return Nat
| _ => fail
end
| prd t1 =>
T1 <- type_check Gamma t1 ;;
match T1 with
| Nat => return Nat
| _ => fail
end
| mlt t1 t2 =>
T1 <- type_check Gamma t1 ;;
T2 <- type_check Gamma t2 ;;
match T1, T2 with
| Nat, Nat => return Nat
| _,_        => fail
end
| test0 guard t f =>
Tguard <- type_check Gamma guard ;;
T1 <- type_check Gamma t ;;
T2 <- type_check Gamma f ;;
match Tguard with
| Nat => if eqb_ty T1 T2 then return T1 else fail
| _ => fail
end

| tlcase t0 t1 x21 x22 t2 =>
match type_check Gamma t0 with
| Some (List T) =>
match type_check Gamma t1,
type_check (update (update Gamma x22 (List T)) x21 T) t2 with
| Some T1', Some T2' =>
if eqb_ty T1' T2' then Some T1' else None
| _,_ => None
end
| _ => None
end

| _ => None
end.

Ltac invert_typecheck Gamma t T :=
remember (type_check Gamma t) as TO;
destruct TO as [T|];
try solve_by_invert; try (inversion H0; eauto); try (subst; eauto).

Ltac analyze T T1 T2 :=
destruct T as [T1 T2| |T1 T2|T1| |T1 T2]; try solve_by_invert.

Ltac fully_invert_typecheck Gamma t T T1 T2 :=
let TX := fresh T in
remember (type_check Gamma t) as TO;
destruct TO as [TX|]; try solve_by_invert;
destruct TX as [T1 T2| |T1 T2|T1| |T1 T2];
try solve_by_invert; try (inversion H0; eauto); try (subst; eauto).

Ltac case_equality S T :=
destruct (eqb_ty S T) eqn: Heqb;
inversion H0; apply eqb_ty__eq in Heqb; subst; subst; eauto.

Theorem type_checking_sound : forall Gamma t T,
type_check Gamma t = Some T -> has_type Gamma t T.
Proof with eauto. hammer_hook "Typechecking" "Typechecking.TypecheckerExtensions.type_checking_sound".
intros Gamma t. generalize dependent Gamma.
induction t; intros Gamma T Htc; inversion Htc.
-  rename s into x. destruct (Gamma x) eqn:H.
rename t into T'. inversion H0. subst. eauto. solve_by_invert.
-
invert_typecheck Gamma t1 T1.
invert_typecheck Gamma t2 T2.
analyze T1 T11 T12.
case_equality T11 T2.
-
rename s into x. rename t into T1.
remember (update Gamma x T1) as Gamma'.
invert_typecheck Gamma' t0 T0.
-  eauto.
-
rename t into t1.
fully_invert_typecheck Gamma t1 T1 T11 T12.
-
rename t into t1.
fully_invert_typecheck Gamma t1 T1 T11 T12.
-
invert_typecheck Gamma t1 T1.
invert_typecheck Gamma t2 T2.
analyze T1 T11 T12; analyze T2 T21 T22.
inversion H0. subst. eauto.
-
invert_typecheck Gamma t1 T1.
invert_typecheck Gamma t2 T2.
invert_typecheck Gamma t3 T3.
destruct T1; try solve_by_invert.
case_equality T2 T3.

-
rename s into x31. rename s0 into x32.
fully_invert_typecheck Gamma t1 T1 T11 T12.
invert_typecheck Gamma t2 T2.
remember (update (update Gamma x32 (List T11)) x31 T11) as Gamma'2.
invert_typecheck Gamma'2 t3 T3.
case_equality T2 T3.

Qed.

Theorem type_checking_complete : forall Gamma t T,
has_type Gamma t T -> type_check Gamma t = Some T.
Proof. hammer_hook "Typechecking" "Typechecking.TypecheckerExtensions.type_checking_complete".
intros Gamma t T Hty.
induction Hty; simpl;
try (rewrite IHHty);
try (rewrite IHHty1);
try (rewrite IHHty2);
try (rewrite IHHty3);
try (rewrite (eqb_ty_refl T));
try (rewrite (eqb_ty_refl T1));
try (rewrite (eqb_ty_refl T2));
eauto.
- destruct (Gamma x); try solve_by_invert. eauto.
Admitted.

End TypecheckerExtensions.

Module StepFunction.
Import MoreStlc.
Import STLCExtended.

Fixpoint stepf (t : tm) : option tm
. Admitted.

Theorem sound_stepf : forall t t',
stepf t = Some t'  ->  t --> t'.
Proof. hammer_hook "Typechecking" "Typechecking.StepFunction.sound_stepf".  Admitted.

Theorem complete_stepf : forall t t',
t --> t'  ->  stepf t = Some t'.
Proof. hammer_hook "Typechecking" "Typechecking.StepFunction.complete_stepf".  Admitted.

End StepFunction.

Module StlcImpl.
Import StepFunction.

End StlcImpl.


(** * LibSepFmap: Appendix - Finite Maps *)

Set Implicit Arguments.
From SLF Require Import LibCore.

(* ################################################################# *)
(** * Representation of Finite Maps *)

(** This file provides a representation of finite maps, which may be used
    to represent the memory state of a program.

    - It implements basic operations such as creation of a singleton map,
       union of maps, read and update operations.
    - It includes predicates to assert disjointness of two maps (predicate
      [disjoint]), and coherence of two maps on the intersection of their
      domain (predicate [agree]).
    - It comes with a tactic for [fmap_eq] proving equalities modulo
      commutativity and associativity of map union.

    The definition of the type [fmap] is slightly technical in that it
    involves a dependent pair to pack the partial function of type
    [A -> option B] that represents the map, together with a proof of
    finiteness of the domain of this map. One useful lemma established
    in this file is the existence of fresh keys: for any finite map whose
    keys are natural numbers, there exists a natural number that does not
    already belong to the domain of that map. *)

(* ================================================================= *)
(** ** Representation of Potentially-Infinite Maps as Partial Functions *)

(* ----------------------------------------------------------------- *)
(** *** Representation *)

(** Type of partial functions from A to B *)

Definition map (A B : Type) : Type :=
  A -> option B.

(* ----------------------------------------------------------------- *)
(** *** Operations *)

(** Disjoint union of two partial functions *)

Definition map_union (A B : Type) (f1 f2 : map A B) : map A B :=
  fun (x:A) => match f1 x with
           | Some y => Some y
           | None => f2 x
           end.

(** Removal from a partial functions *)

Definition map_remove (A B : Type) (f : map A B) (k:A) : map A B :=
  fun (x:A) => If x = k then None else f x.

(** Finite domain of a partial function *)

Definition map_finite (A B : Type) (f : map A B) :=
  exists (L : list A), forall (x:A), f x <> None -> mem x L.

(** Disjointness of domain of two partial functions *)

Definition map_disjoint (A B : Type) (f1 f2 : map A B) :=
  forall (x:A), f1 x = None \/ f2 x = None.

(** Compatibility of two partial functions on the intersection
    of their domains (only for Separation Logic with RO-permissions) *)

Definition map_agree (A B : Type) (f1 f2 : map A B) :=
  forall x v1 v2,
  f1 x = Some v1 ->
  f2 x = Some v2 ->
  v1 = v2.

(** Domain of a map (as a predicate) *)

Definition map_indom (A B : Type) (f1 : map A B) : (A->Prop) :=
  fun (x:A) => f1 x <> None.

(** Filter the bindings of a map *)

Definition map_filter A B (F:A->B->Prop) (f:map A B) : map A B :=
  fun (x:A) => match f x with
    | None => None
    | Some y => If F x y then Some y else None
    end.

(** Map a function on the values of a map *)

Definition map_map A B1 B2 (F:A->B1->B2) (f:map A B1) : map A B2 :=
  fun (x:A) => match f x with
    | None => None
    | Some y => Some (F x y)
    end.

(* ----------------------------------------------------------------- *)
(** *** Properties *)

Section MapOps.
Variables (A B : Type).
Implicit Types f : map A B.

(** Symmetry of disjointness *)

Lemma map_disjoint_sym :
  sym (@map_disjoint A B).
Proof using.
  introv H. unfolds map_disjoint. intros z. specializes H z. intuition.
Qed.

(** Commutativity of disjoint union *)

Lemma map_union_comm : forall f1 f2,
  map_disjoint f1 f2 ->
  map_union f1 f2 = map_union f2 f1.
Proof using.
  introv H. unfold map.
  extens. intros x. unfolds map_disjoint, map_union.
  specializes H x. cases (f1 x); cases (f2 x); auto. destruct H; false.
Qed.

(** Finiteness of union *)

Lemma map_union_finite : forall f1 f2,
  map_finite f1 ->
  map_finite f2 ->
  map_finite (map_union f1 f2).
Proof using.
  introv [L1 F1] [L2 F2]. exists (L1 ++ L2). intros x M.
  specializes F1 x. specializes F2 x. unfold map_union in M.
  apply mem_app. destruct~ (f1 x).
Qed.

(** Finiteness of removal *)

Definition map_remove_finite : forall x f,
  map_finite f ->
  map_finite (map_remove f x).
Proof using.
  introv [L F]. exists L. intros x' M.
  specializes F x'. unfold map_remove in M. case_if~.
Qed.

(** Finiteness of filter *)

Definition map_filter_finite : forall (F:A->B->Prop) f,
  map_finite f ->
  map_finite (map_filter F f).
Proof using.
  introv [L N]. exists L. intros x' M.
  specializes N x'. unfold map_filter in M.
  destruct (f x'); tryfalse. case_if. applys N; auto_false.
Qed.

(** Finiteness of map *)

Definition map_map_finite : forall C (F:A->B->C) f,
  map_finite f ->
  map_finite (map_map F f).
Proof using.
  introv [L N]. exists L. intros x' M.
  specializes N x'. unfold map_map in M.
  destruct (f x'); tryfalse. applys N; auto_false.
Qed.

End MapOps.

(* ================================================================= *)
(** ** Representation of Finite Maps as Dependent Pairs *)

(* ----------------------------------------------------------------- *)
(** *** Representation *)

Inductive fmap (A B : Type) : Type := make {
  fmap_data :> map A B;
  fmap_finite : map_finite fmap_data }.

Arguments make [A] [B].

(* ----------------------------------------------------------------- *)
(** *** Operations *)

Declare Scope fmap_scope.

(** Domain of a fmap (as a predicate) *)

Definition indom (A B: Type) (h:fmap A B) : (A->Prop) :=
  map_indom h.

(** Empty fmap *)

Program Definition empty (A B : Type) : fmap A B :=
  make (fun l => None) _.
Next Obligation. exists (@nil A). auto_false. Qed.

Arguments empty {A} {B}.

(** Singleton fmap *)

Program Definition single A B (x:A) (v:B) : fmap A B :=
  make (fun x' => If x = x' then Some v else None) _.
Next Obligation.
  exists (x::nil). intros. case_if. subst~.
Qed.

(** Union of fmaps *)

Program Definition union A B (h1 h2:fmap A B) : fmap A B :=
  make (map_union h1 h2) _.
Next Obligation. destruct h1. destruct h2. apply~ map_union_finite. Qed.

Notation "h1 \+ h2" := (union h1 h2)
   (at level 51, right associativity) : fmap_scope.

Open Scope fmap_scope.

(** Update of a fmap *)

Definition update A B (h:fmap A B) (x:A) (v:B) : fmap A B :=
  union (single x v) h.
  (* Note: the union operation first reads in the first argument. *)

(** Read in a fmap *)

Definition read (A B : Type) {IB:Inhab B} (h:fmap A B) (x:A) : B :=
  match fmap_data h x with
  | Some y => y
  | None => arbitrary
  end.

(** Removal from a fmap *)

Program Definition remove A B (h:fmap A B) (x:A) : fmap A B :=
  make (map_remove h x) _.
Next Obligation. destruct h. apply~ map_remove_finite. Qed.

(** Filter from a fmap *)

Program Definition filter A B (F:A->B->Prop) (h:fmap A B) : fmap A B :=
  make (map_filter F h) _.
Next Obligation. destruct h. apply~ map_filter_finite. Qed.

(** Map a function onto the keys of a fmap *)

Program Definition map_ A B1 B2 (F:A->B1->B2) (h:fmap A B1) : fmap A B2 :=
  make (map_map F h) _.
Next Obligation. destruct h. apply~ map_map_finite. Qed.

(** Inhabited type [fmap] *)

Global Instance Inhab_fmap A B : Inhab (fmap A B).
Proof using. intros. applys Inhab_of_val (@empty A B). Qed.

(* ================================================================= *)
(** ** Predicates on Finite Maps *)

(** Compatible fmaps (only for Separation Logic with RO-permissions) *)

Definition agree A B (h1 h2:fmap A B) :=
  map_agree h1 h2.

(** Disjoint fmaps *)

Definition disjoint A B (h1 h2 : fmap A B) : Prop :=
  map_disjoint h1 h2.

(** Three disjoint fmaps (not needed for basic separation logic) *)

Definition disjoint_3 A B (h1 h2 h3 : fmap A B) :=
     disjoint h1 h2
  /\ disjoint h2 h3
  /\ disjoint h1 h3.

(** Notation for disjointness *)

Module NotationForFmapDisjoint.

Notation "\# h1 h2" := (disjoint h1 h2)
  (at level 40, h1 at level 0, h2 at level 0, no associativity) : fmap_scope.

Notation "\# h1 h2 h3" := (disjoint_3 h1 h2 h3)
  (at level 40, h1 at level 0, h2 at level 0, h3 at level 0, no associativity)
  : fmap_scope.

End NotationForFmapDisjoint.

Import NotationForFmapDisjoint.

(* ################################################################# *)
(** * Poperties of Operations on Finite Maps *)

Section FmapProp.
Variables (A B : Type).
Implicit Types f g h : fmap A B.
Implicit Types x : A.
Implicit Types v : B.

(* ================================================================= *)
(** ** Equality *)

Lemma make_eq : forall (f1 f2:map A B) F1 F2,
  (forall x, f1 x = f2 x) ->
  make f1 F1 = make f2 F2.
Proof using.
  introv H. asserts: (f1 = f2). { unfold map. extens~. }
  subst. fequals. (* note: involves proof irrelevance *)
Qed.

Lemma eq_inv_fmap_data_eq : forall h1 h2,
  h1 = h2 ->
  forall x, fmap_data h1 x = fmap_data h2 x.
Proof using. intros. fequals. Qed.

Lemma fmap_extens : forall h1 h2,
  (forall x, fmap_data h1 x = fmap_data h2 x) ->
  h1 = h2.
Proof using. intros [f1 F1] [f2 F2] M. simpls. applys~ make_eq. Qed.

(* ================================================================= *)
(** ** Disjointness *)

Lemma disjoint_sym : forall h1 h2,
  \# h1 h2 ->
  \# h2 h1.
Proof using. intros [f1 F1] [f2 F2]. apply map_disjoint_sym. Qed.

Lemma disjoint_comm : forall h1 h2,
  \# h1 h2 = \# h2 h1.
Proof using. lets: disjoint_sym. extens*. Qed.

Lemma disjoint_empty_l : forall h,
  \# empty h.
Proof using. intros [f F] x. simple~. Qed.

Lemma disjoint_empty_r : forall h,
  \# h empty.
Proof using. intros [f F] x. simple~. Qed.

Hint Resolve disjoint_sym disjoint_empty_l disjoint_empty_r.

Lemma disjoint_union_eq_r : forall h1 h2 h3,
  \# h1 (h2 \+ h3) =
  (\# h1 h2 /\ \# h1 h3).
Proof using.
  intros [f1 F1] [f2 F2] [f3 F3].
  unfolds disjoint, union. simpls.
  unfolds map_disjoint, map_union. extens. iff M [M1 M2].
  split; intros x; specializes M x;
   destruct (f2 x); intuition; tryfalse.
  intros x. specializes M1 x. specializes M2 x.
   destruct (f2 x); intuition.
Qed.

Lemma disjoint_union_eq_l : forall h1 h2 h3,
  \# (h2 \+ h3) h1 =
  (\# h1 h2 /\ \# h1 h3).
Proof using.
  intros. rewrite disjoint_comm.
  apply disjoint_union_eq_r.
Qed.

Lemma disjoint_single_single : forall (x1 x2:A) (v1 v2:B),
  x1 <> x2 ->
  \# (single x1 v1) (single x2 v2).
Proof using.
  introv N. intros x. simpls. do 2 case_if; auto.
Qed.

Lemma disjoint_single_single_same_inv : forall (x:A) (v1 v2:B),
  \# (single x v1) (single x v2) ->
  False.
Proof using.
  introv D. specializes D x. simpls. case_if. destruct D; tryfalse.
Qed.

Lemma disjoint_3_unfold : forall h1 h2 h3,
  \# h1 h2 h3 = (\# h1 h2 /\ \# h2 h3 /\ \# h1 h3).
Proof using. auto. Qed.

Lemma disjoint_single_set : forall h l v1 v2,
  \# (single l v1) h ->
  \# (single l v2) h.
Proof using.
  introv M. unfolds disjoint, single, map_disjoint; simpls.
  intros l'. specializes M l'. case_if~. destruct M; auto_false.
Qed.

(* ================================================================= *)
(** ** Union *)

Lemma union_self : forall h,
  h \+ h = h.
Proof using.
  intros [f F]. apply~ make_eq. simpl. intros x.
  unfold map_union. cases~ (f x).
Qed.

Lemma union_empty_l : forall h,
  empty \+ h = h.
Proof using.
  intros [f F]. unfold union, map_union, empty. simpl.
  apply~ make_eq.
Qed.

Lemma union_empty_r : forall h,
  h \+ empty = h.
Proof using.
  intros [f F]. unfold union, map_union, empty. simpl.
  apply make_eq. intros x. destruct~ (f x).
Qed.

Lemma union_eq_empty_inv_l : forall h1 h2,
  h1 \+ h2 = empty ->
  h1 = empty.
Proof using.
  intros (f1&F1) (f2&F2) M. inverts M as M.
  applys make_eq. intros l.
  unfolds map_union.
  lets: fun_eq_1 l M.
  cases (f1 l); auto_false.
Qed.

Lemma union_eq_empty_inv_r : forall h1 h2,
  h1 \+ h2 = empty ->
  h2 = empty.
Proof using.
  intros (f1&F1) (f2&F2) M. inverts M as M.
  applys make_eq. intros l.
  unfolds map_union.
  lets: fun_eq_1 l M.
  cases (f1 l); auto_false.
Qed.

Lemma union_comm_of_disjoint : forall h1 h2,
  \# h1 h2 ->
  h1 \+ h2 = h2 \+ h1.
Proof using.
  intros [f1 F1] [f2 F2] H. simpls. apply make_eq. simpl.
  intros. rewrite~ map_union_comm.
Qed.

Lemma union_comm_of_agree : forall h1 h2,
  agree h1 h2 ->
  h1 \+ h2 = h2 \+ h1.
Proof using.
  intros [f1 F1] [f2 F2] H. simpls. apply make_eq. simpl.
  intros l. specializes H l. unfolds map_union. simpls.
  cases (f1 l); cases (f2 l); auto. fequals. applys* H.
Qed.

Lemma union_assoc : forall h1 h2 h3,
  (h1 \+ h2) \+ h3 = h1 \+ (h2 \+ h3).
Proof using.
  intros [f1 F1] [f2 F2] [f3 F3]. unfolds union. simpls.
  apply make_eq. intros x. unfold map_union. destruct~ (f1 x).
Qed.

Lemma union_eq_inv_of_disjoint : forall h2 h1 h1',
  \# h1 h2 ->
  \# h1' h2 ->
  h1 \+ h2 = h1' \+ h2 ->
  h1 = h1'.
Proof using.
  intros [f2 F2] [f1' F1'] [f1 F1] D D' E.
  applys make_eq. intros x. specializes D x. specializes D' x.
  lets E': eq_inv_fmap_data_eq (rm E) x. simpls.
  unfolds map_union.
  cases (f1' x); cases (f1 x);
    destruct D; try congruence;
    destruct D'; try congruence.
Qed.

(* ================================================================= *)
(** ** Compatibility *)

Lemma agree_refl : forall h,
  agree h h.
Proof using.
  intros h. introv M1 M2. congruence.
Qed.

Lemma agree_sym : forall f1 f2,
  agree f1 f2 ->
  agree f2 f1.
Proof using.
  introv M. intros l v1 v2 E1 E2.
  specializes M l E1.
Qed.

Lemma agree_of_disjoint : forall h1 h2,
  disjoint h1 h2 ->
  agree h1 h2.
Proof using.
  introv HD. intros l v1 v2 M1 M2. destruct (HD l); false.
Qed.

Lemma agree_empty_l : forall h,
  agree empty h.
Proof using. intros h l v1 v2 E1 E2. simpls. false. Qed.

Lemma agree_empty_r : forall h,
  agree h empty.
Proof using.
  hint agree_sym, agree_empty_l. eauto.
Qed.

Lemma agree_union_l : forall f1 f2 f3,
  agree f1 f3 ->
  agree f2 f3 ->
  agree (f1 \+ f2) f3.
Proof using.
  introv M1 M2. intros l v1 v2 E1 E2.
  specializes M1 l. specializes M2 l.
  simpls. unfolds map_union. cases (fmap_data f1 l).
  { inverts E1. applys* M1. }
  { applys* M2. }
Qed.

Lemma agree_union_r : forall f1 f2 f3,
  agree f1 f2 ->
  agree f1 f3 ->
  agree f1 (f2 \+ f3).
Proof using.
  hint agree_sym, agree_union_l. eauto.
Qed.

Lemma agree_union_lr : forall f1 g1 f2 g2,
  agree g1 g2 ->
  \# f1 f2 (g1 \+ g2) ->
  agree (f1 \+ g1) (f2 \+ g2).
Proof using.
  introv M1 (M2a&M2b&M2c).
  rewrite disjoint_union_eq_r in *.
  applys agree_union_l; applys agree_union_r;
    try solve [ applys* agree_of_disjoint ].
  auto.
Qed.

Lemma agree_union_ll_inv : forall f1 f2 f3,
  agree (f1 \+ f2) f3 ->
  agree f1 f3.
Proof using.
  introv M. intros l v1 v2 E1 E2.
  specializes M l. simpls. unfolds map_union.
  rewrite E1 in M. applys* M.
Qed.

Lemma agree_union_rl_inv : forall f1 f2 f3,
  agree f1 (f2 \+ f3) ->
  agree f1 f2.
Proof using.
  hint agree_union_ll_inv, agree_sym. eauto.
Qed.

Lemma agree_union_lr_inv_agree_agree : forall f1 f2 f3,
  agree (f1 \+ f2) f3 ->
  agree f1 f2 ->
  agree f2 f3.
Proof using.
  introv M D. rewrite~ (@union_comm_of_agree f1 f2) in M.
  applys* agree_union_ll_inv.
Qed.

Lemma agree_union_rr_inv_agree : forall f1 f2 f3,
  agree f1 (f2 \+ f3) ->
  agree f2 f3 ->
  agree f1 f3.
Proof using.
  hint agree_union_lr_inv_agree_agree, agree_sym. eauto.
Qed.

Lemma agree_union_l_inv : forall f1 f2 f3,
  agree (f1 \+ f2) f3 ->
  agree f1 f2 ->
     agree f1 f3
  /\ agree f2 f3.
Proof using.
  introv M1 M2. split.
  { applys* agree_union_ll_inv. }
  { applys* agree_union_lr_inv_agree_agree. }
Qed.

Lemma agree_union_r_inv : forall f1 f2 f3,
  agree f1 (f2 \+ f3) ->
  agree f2 f3 ->
     agree f1 f2
  /\ agree f1 f3.
Proof using.
  hint agree_sym.
  intros. forwards~ (M1&M2): agree_union_l_inv f2 f3 f1.
Qed.

(* ================================================================= *)
(** ** Domain *)

Lemma indom_single : forall x v,
  indom (single x v) x.
Proof using.
  intros. hnf. simpl. case_if. auto_false.
Qed.

Lemma indom_union_l : forall h1 h2 x,
  indom h1 x ->
  indom (union h1 h2) x.
Proof using.
  intros. hnf. unfold union, map_union. simpl.
  case_eq (fmap_data h1 x); auto_false.
Qed.

Lemma indom_union_r : forall h1 h2 x,
  indom h2 x ->
  indom (union h1 h2) x.
Proof using.
  intros. hnf. unfold union, map_union. simpl.
  case_eq (fmap_data h1 x); auto_false.
Qed.

(* ================================================================= *)
(** ** Disjoint and Domain *)

Lemma disjoint_eq_not_indom_both : forall h1 h2,
  disjoint h1 h2 = (forall x, indom h1 x -> indom h2 x -> False).
Proof using.
  extens. iff D E.
  { introv M1 M2. destruct (D x); false*. }
  { intros x. specializes E x. unfolds indom, map_indom.
    applys not_not_inv. intros N. rew_logic in N. false*. }
Qed.

Lemma disjoint_of_not_indom_both : forall h1 h2,
  (forall x, indom h1 x -> indom h2 x -> False) ->
  disjoint h1 h2.
Proof using. introv M. rewrite~ disjoint_eq_not_indom_both. Qed.

Lemma disjoint_inv_not_indom_both : forall h1 h2 x,
  disjoint h1 h2 ->
  indom h1 x ->
  indom h2 x ->
  False.
Proof using. introv. rewrite* disjoint_eq_not_indom_both. Qed.

Lemma disjoint_single_of_not_indom : forall h x v,
  ~ indom h x ->
  disjoint (single x v) h.
Proof using.
  introv N. unfolds disjoint, map_disjoint. unfolds single, indom, map_indom.
  simpl. rew_logic in N. intros l'. case_if; subst; autos*.
Qed.

(** Note that the reciprocal of the above lemma is a special instance of
   [disjoint_inv_not_indom_both] *)

(* ================================================================= *)
(** ** Read *)

Lemma read_single : forall {IB:Inhab B} x v,
  read (single x v) x = v.
Proof using.
  intros. unfold read, single. simpl. case_if~.
Qed.

Lemma read_union_l : forall {IB:Inhab B} h1 h2 x,
  indom h1 x ->
  read (union h1 h2) x = read h1 x.
Proof using.
  intros. unfold read, union, map_union. simpl.
  case_eq (fmap_data h1 x); auto_false.
Qed.

Lemma read_union_r : forall {IB:Inhab B} h1 h2 x,
  ~ indom h1 x ->
  read (union h1 h2) x = read h2 x.
Proof using.
  intros. unfold read, union, map_union. simpl.
  case_eq (fmap_data h1 x).
  { intros v Hv. false H. auto_false. }
  { auto_false. }
Qed.

(* ================================================================= *)
(** ** Update *)

Lemma update_eq_union_single : forall h x v,
  update h x v = union (single x v) h.
Proof using. auto. Qed.

Lemma update_single : forall x v w,
  update (single x v) x w = single x w.
Proof using.
  intros. rewrite update_eq_union_single.
  applys make_eq. intros y.
  unfold map_union, single. simpl. case_if~.
Qed.

Lemma update_union_l : forall h1 h2 x v,
  indom h1 x ->
  update (union h1 h2) x v = union (update h1 x v) h2.
Proof using.
  intros. do 2 rewrite update_eq_union_single.
  applys make_eq. intros y.
  unfold map_union, union, map_union. simpl. case_if~.
Qed.

Lemma update_union_r : forall h1 h2 x v,
  ~ indom h1 x ->
  update (union h1 h2) x v = union h1 (update h2 x v).
Proof using.
  introv M. asserts IB: (Inhab B). { applys Inhab_of_val v. }
  do 2 rewrite update_eq_union_single.
  applys make_eq. intros y.
  unfold map_union, union, map_union. simpl. case_if~.
  { subst. case_eq (fmap_data h1 y); auto_false.
    { intros w Hw. false M. auto_false. } }
Qed.

(* ================================================================= *)
(** ** Removal *)

Lemma remove_union_single_l : forall h x v,
  ~ indom h x ->
  remove (union (single x v) h) x = h.
Proof using.
  introv M. applys fmap_extens. intros y.
  unfold remove, map_remove, union, map_union, single. simpls. case_if.
  { destruct h as [f F]. unfolds indom, map_indom. simpls. subst. rew_logic~ in M. }
  { case_if~. }
Qed.

End FmapProp.

(** Fixing arguments *)

Arguments union_assoc [A] [B].
Arguments union_comm_of_disjoint [A] [B].
Arguments union_comm_of_agree [A] [B].

(* ################################################################# *)
(** * Tactics for Finite Maps *)

(* ================================================================= *)
(** ** Tactic [disjoint] for proving disjointness results *)

(** [disjoint] proves goals of the form [\# h1 h2] and
    [\# h1 h2 h3] by expanding all hypotheses into binary forms
    [\# h1 h2] and then exploiting symmetry and disjointness
    with [empty]. *)

Module Export DisjointHints.
Hint Resolve disjoint_sym disjoint_empty_l disjoint_empty_r.
End DisjointHints.

Hint Rewrite
  disjoint_union_eq_l
  disjoint_union_eq_r
  disjoint_3_unfold : rew_disjoint.

Tactic Notation "rew_disjoint" :=
  autorewrite with rew_disjoint in *.
Tactic Notation "rew_disjoint" "*" :=
  rew_disjoint; auto_star.

Ltac fmap_disjoint_pre tt :=
  subst; rew_disjoint; jauto_set.

Tactic Notation "fmap_disjoint" :=
  solve [ fmap_disjoint_pre tt; auto ].

Lemma disjoint_demo : forall A B (h1 h2 h3 h4 h5:fmap A B),
  h1 = h2 \+ h3 ->
  \# h2 h3 ->
  \# h1 h4 h5 ->
  \# h3 h2 h5 /\ \# h4 h5.
Proof using.
  intros. dup 2.
  { subst. rew_disjoint. jauto_set. auto. auto. auto. auto. }
  { fmap_disjoint. }
Qed.

(* ================================================================= *)
(** ** Tactic [rew_map] for Normalizing Expressions *)

Hint Rewrite
  union_assoc
  union_empty_l
  union_empty_r : rew_fmap rew_fmap_for_fmap_eq.

Tactic Notation "rew_fmap" :=
  autorewrite with rew_fmap in *.

Tactic Notation "rew_fmap" "~" :=
  rew_fmap; auto_tilde.

Tactic Notation "rew_fmap" "*" :=
  rew_fmap; auto_star.

(* ================================================================= *)
(** ** Tactic [fmap_eq] for Proving Equalities *)

Section StateEq.
Variables (A B : Type).
Implicit Types h : fmap A B.

(** [eq] proves equalities between unions of fmaps, of the form
    [h1 \+ h2 \+ h3 \+ h4 = h1' \+ h2' \+ h3' \+ h4']
    It attempts to discharge the disjointness side-conditions.
    Disclaimer: it cancels heaps at depth up to 4, but no more. *)

Lemma union_eq_cancel_1 : forall h1 h2 h2',
  h2 = h2' ->
  h1 \+ h2 = h1 \+ h2'.
Proof using. intros. subst. auto. Qed.

Lemma union_eq_cancel_1' : forall h1,
  h1 = h1.
Proof using. intros. auto. Qed.

Lemma union_eq_cancel_2 : forall h1 h1' h2 h2',
  \# h1 h1' ->
  h2 = h1' \+ h2' ->
  h1 \+ h2 = h1' \+ h1 \+ h2'.
Proof using.
  intros. subst. rewrite <- union_assoc.
  rewrite (union_comm_of_disjoint h1 h1').
  rewrite~ union_assoc. auto.
Qed.

Lemma union_eq_cancel_2' : forall h1 h1' h2,
  \# h1 h1' ->
  h2 = h1' ->
  h1 \+ h2 = h1' \+ h1.
Proof using.
  intros. subst. apply~ union_comm_of_disjoint.
Qed.

Lemma union_eq_cancel_3 : forall h1 h1' h2 h2' h3',
  \# h1 (h1' \+ h2') ->
  h2 = h1' \+ h2' \+ h3' ->
  h1 \+ h2 = h1' \+ h2' \+ h1 \+ h3'.
Proof using.
  intros. subst.
  rewrite <- (union_assoc h1' h2' h3').
  rewrite <- (union_assoc h1' h2' (h1 \+ h3')).
  apply~ union_eq_cancel_2.
Qed.

Lemma union_eq_cancel_3' : forall h1 h1' h2 h2',
  \# h1 (h1' \+ h2') ->
  h2 = h1' \+ h2' ->
  h1 \+ h2 = h1' \+ h2' \+ h1.
Proof using.
  intros. subst.
  rewrite <- (union_assoc h1' h2' h1).
  apply~ union_eq_cancel_2'.
Qed.

Lemma union_eq_cancel_4 : forall h1 h1' h2 h2' h3' h4',
  \# h1 ((h1' \+ h2') \+ h3') ->
  h2 = h1' \+ h2' \+ h3' \+ h4' ->
  h1 \+ h2 = h1' \+ h2' \+ h3' \+ h1 \+ h4'.
Proof using.
  intros. subst.
  rewrite <- (union_assoc h1' h2' (h3' \+ h4')).
  rewrite <- (union_assoc h1' h2' (h3' \+ h1 \+ h4')).
  apply~ union_eq_cancel_3.
Qed.

Lemma union_eq_cancel_4' : forall h1 h1' h2 h2' h3',
  \# h1 (h1' \+ h2' \+ h3') ->
  h2 = h1' \+ h2' \+ h3' ->
  h1 \+ h2 = h1' \+ h2' \+ h3' \+ h1.
Proof using.
  intros. subst.
  rewrite <- (union_assoc h2' h3' h1).
  apply~ union_eq_cancel_3'.
Qed.

End StateEq.

Ltac fmap_eq_step tt :=
  match goal with | |- ?G => match G with
  | ?h1 = ?h1 => apply union_eq_cancel_1'
  | ?h1 \+ ?h2 = ?h1 \+ ?h2' => apply union_eq_cancel_1
  | ?h1 \+ ?h2 = ?h1' \+ ?h1 => apply union_eq_cancel_2'
  | ?h1 \+ ?h2 = ?h1' \+ ?h1 \+ ?h2' => apply union_eq_cancel_2
  | ?h1 \+ ?h2 = ?h1' \+ ?h2' \+ ?h1 => apply union_eq_cancel_3'
  | ?h1 \+ ?h2 = ?h1' \+ ?h2' \+ ?h1 \+ ?h3' => apply union_eq_cancel_3
  | ?h1 \+ ?h2 = ?h1' \+ ?h2' \+ ?h3' \+ ?h1 => apply union_eq_cancel_4'
  | ?h1 \+ ?h2 = ?h1' \+ ?h2' \+ ?h3' \+ ?h1 \+ ?h4' => apply union_eq_cancel_4
  end end.

Tactic Notation "fmap_eq" :=
  subst;
  autorewrite with rew_fmap_for_fmap_eq;
  repeat (fmap_eq_step tt);
  try match goal with
  | |- \# _ _ => fmap_disjoint
  | |- \# _ _ _ => fmap_disjoint
  end.

Tactic Notation "fmap_eq" "~" :=
  fmap_eq; auto_tilde.

Tactic Notation "fmap_eq" "*" :=
  fmap_eq; auto_star.

Lemma fmap_eq_demo : forall A B (h1 h2 h3 h4 h5:fmap A B),
  \# h1 h2 h3 ->
  \# (h1 \+ h2 \+ h3) h4 h5 ->
  h1 = h2 \+ h3 ->
  h4 \+ h1 \+ h5 = h2 \+ h5 \+ h4 \+ h3.
Proof using.
  intros. dup 2.
  { subst. rew_fmap.
    fmap_eq_step tt. fmap_disjoint.
    fmap_eq_step tt.
    fmap_eq_step tt. fmap_disjoint. auto. }
  { fmap_eq. }
Qed.

(* ################################################################# *)
(** * Existence of Fresh Locations *)

(* ================================================================= *)
(** ** Consecutive Locations *)

(** The notion of "consecutive locations" is useful for reasoning about
    records and arrays. *)

Fixpoint conseq (B:Type) (vs:list B) (l:nat) : fmap nat B :=
  match vs with
  | nil => empty
  | v::vs' => (single l v) \+ (conseq vs' (S l))
  end.

Lemma conseq_nil : forall B (l:nat),
  conseq (@nil B) l = empty.
Proof using. auto. Qed.

Lemma conseq_cons : forall B (l:nat) (v:B) (vs:list B),
  conseq (v::vs) l = (single l v) \+ (conseq vs (S l)).
Proof using. auto. Qed.

Lemma conseq_cons' : forall B (l:nat) (v:B) (vs:list B),
  conseq (v::vs) l = (single l v) \+ (conseq vs (l+1)).
Proof using. intros. math_rewrite (l+1 = S l)%nat. applys conseq_cons. Qed.

Global Opaque conseq.

Hint Rewrite conseq_nil conseq_cons : rew_listx.

(* ================================================================= *)
(** ** Existence of Fresh Locations *)

Definition loc_fresh_gen (L : list nat) :=
  (1 + fold_right plus O L)%nat.

Lemma loc_fresh_ind : forall l L,
  mem l L ->
  (l < loc_fresh_gen L)%nat.
Proof using.
  intros l L. unfold loc_fresh_gen.
  induction L; introv M; inverts M; rew_listx.
  { math. }
  { forwards~: IHL. math. }
Qed.

Lemma loc_fresh_nat_ge : forall (L:list nat),
  exists (l:nat), forall (i:nat), ~ mem (l+i)%nat L.
Proof using.
  intros L. exists (loc_fresh_gen L). intros i M.
  lets: loc_fresh_ind M. math.
Qed.

(** For any finite list of locations (implemented as [nat]), there exists
    one location not in that list. *)

Lemma loc_fresh_nat : forall (L:list nat),
  exists (l:nat), ~ mem l L.
Proof using.
  intros L. forwards (l&P): loc_fresh_nat_ge L.
  exists l. intros M. applys (P 0%nat). applys_eq M. math.
Qed.

Section FmapFresh.
Variables (B : Type).
Implicit Types h : fmap nat B.

(** For any heap, there exists one (non-null) location not already in the
    domain of that heap. *)

Lemma exists_fresh : forall null h,
  exists l, ~ indom h l /\ l <> null.
Proof using.
  intros null (m&(L&M)). unfold indom, map_indom. simpl.
  lets (l&F): (loc_fresh_nat (null::L)).
  exists l. split.
  { simpl. intros l'. forwards~ E: M l. }
  { intro_subst. applys~ F. }
Qed.

(** For any heap [h], there exists one (non-null) location [l] such that
    the singleton map that binds [l] to a value [v] is disjoint from [h]. *)

Lemma single_fresh : forall null h v,
  exists l, \# (single l v) h /\ l <> null.
Proof using.
  intros. forwards (l&F&N): exists_fresh null h.
  exists l. split~. applys* disjoint_single_of_not_indom.
Qed.

Lemma conseq_fresh : forall null h vs,
  exists l, \# (conseq vs l) h /\ l <> null.
Proof using.
  intros null (m&(L&M)) vs.
  unfold disjoint, map_disjoint. simpl.
  lets (l&F): (loc_fresh_nat_ge (null::L)).
  exists l. split.
  { intros l'. gen l. induction vs as [|v vs']; intros.
    { simple~. }
    { rewrite conseq_cons. destruct (IHvs' (S l)%nat) as [E|?].
      { intros i N. applys F (S i). applys_eq N. math. }
      { simpl. unfold map_union. case_if~.
        { subst. right. applys not_not_inv. intros H. applys F 0%nat.
          constructor. math_rewrite (l'+0 = l')%nat. applys~ M. } }
      { auto. } } }
  { intro_subst. applys~ F 0%nat. rew_nat. auto. }
Qed.

Lemma disjoint_single_conseq : forall B l l' L (v:B),
  (l < l')%nat \/ (l >= l'+length L)%nat ->
  \# (single l v) (conseq L l').
Proof using.
  introv N. gen l'. induction L as [|L']; intros.
  { rewrite~ conseq_nil. }
  { rew_list in N. rewrite conseq_cons. rew_disjoint. split.
    { applys disjoint_single_single. destruct N; math. }
    { applys IHL. destruct N. { left. math. } { right. math. } } }
Qed.

(* ================================================================= *)
(** ** Existence of a Smallest Fresh Locations *)

(** The notion of smallest fresh location is useful for setting up
    deterministic semantics. *)

Definition fresh (null:nat) (h:fmap nat B) (l:nat) : Prop :=
  ~ indom h l /\ l <> null.

Definition smallest_fresh (null:nat) (h:fmap nat B) (l:nat) : Prop :=
  fresh null h l /\ (forall l', l' < l -> ~ fresh null h l').

Lemma exists_smallest_fresh : forall null h,
  exists l, smallest_fresh null h l.
Proof using.
  intros.
  cuts M: (forall l0, fresh null h l0 ->
            exists l, fresh null h l
                   /\ (forall l', l' < l -> ~ fresh null h l')).
  { lets (l0&F&N): exists_fresh null h. applys M l0. split*. }
  intros l0. induction_wf IH: wf_lt l0. intros F.
  tests C: (forall l', l' < l0 -> ~ fresh null h l').
  { exists* l0. }
  { destruct C as (l0'&K). rew_logic in K. destruct K as (L&F').
    applys* IH l0'. }
Qed.

(* ================================================================= *)
(** ** Existence of Nonempty Heaps *)

(** The existence of nonempty (nat-indexed) finite maps is useful
    for constructing counterexamples. *)

Lemma exists_not_empty : forall `{IB:Inhab B},
  exists (h:fmap nat B), h <> empty.
Proof using.
  intros. sets l: 0%nat. sets h: (single l (arbitrary (A:=B))).
  exists h. intros N.
  sets h': (empty:fmap nat B).
  asserts M1: (indom h l).
  { applys indom_single. }
  asserts M2: (~ indom h' l).
  { unfolds @indom, map_indom, @empty. simpls. auto_false. }
  rewrite N in M1. false*.
Qed.

End FmapFresh.

(* 2021-08-11 15:24 *)


(*
MacBook-Air:~ billw$ /Applications/CoqIDE_8.4pl5.app/Contents/Resources/bin/coqtop
Welcome to Coq 8.4pl5 (October 2014)

Coq < Section Addition_A.

Coq < Goal forall s t:Prop, s -> (s \/ ~t).
1 subgoal

  ============================
   forall s t : Prop, s -> s \/ ~ t

Unnamed_thm < intros.
1 subgoal

  s : Prop
  t : Prop
  H : s
  ============================
   s \/ ~ t

Unnamed_thm < left.
1 subgoal

  s : Prop
  t : Prop
  H : s
  ============================
   s

Unnamed_thm < exact H.
No more subgoals.

Unnamed_thm < Qed.
intros.
left.
exact H.

Unnamed_thm is defined

Coq <

*)

Section Addition_A.
Goal forall s t:Prop, s -> (s \/ ~t).
intros.
left.
exact H.
Qed.


(** * ImpCEvalFun: Evaluation Function for Imp *)

(* $Date: 2013-07-01 18:48:47 -0400 (Mon, 01 Jul 2013) $ *)

(* #################################### *)
(** * Evaluation Function *)

Require Import Imp.

(** Here's a first try at an evaluation function for commands,
    omitting [WHILE]. *)

Fixpoint ceval_step1 (st : state) (c : com) : state :=
  match c with
    | SKIP =>
        st
    | l ::= a1 =>
        update st l (aeval st a1)
    | c1 ;; c2 =>
        let st' := ceval_step1 st c1 in
        ceval_step1 st' c2
    | IFB b THEN c1 ELSE c2 FI =>
        if (beval st b)
          then ceval_step1 st c1
          else ceval_step1 st c2
    | WHILE b1 DO c1 END =>
        st  (* bogus *)
  end.

(** In a traditional functional programming language like ML or
    Haskell we could write the WHILE case as follows:
<<
    | WHILE b1 DO c1 END =>
        if (beval st b1)
          then ceval_step1 st (c1;; WHILE b1 DO c1 END)
          else st
>>
    Coq doesn't accept such a definition ([Error: Cannot guess
    decreasing argument of fix]) because the function we want to
    define is not guaranteed to terminate. Indeed, the changed
    [ceval_step1] function applied to the [loop] program from [Imp.v] would
    never terminate. Since Coq is not just a functional programming
    language, but also a consistent logic, any potentially
    non-terminating function needs to be rejected. Here is an
    invalid(!) Coq program showing what would go wrong if Coq allowed
    non-terminating recursive functions:
<<
     Fixpoint loop_false (n : nat) : False := loop_false n.
>>
    That is, propositions like [False] would become
    provable (e.g. [loop_false 0] would be a proof of [False]), which
    would be a disaster for Coq's logical consistency.

    Thus, because it doesn't terminate on all inputs, the full version
    of [ceval_step1] cannot be written in Coq -- at least not
    without one additional trick... *)

(** Second try, using an extra numeric argument as a "step index" to
    ensure that evaluation always terminates. *)

Fixpoint ceval_step2 (st : state) (c : com) (i : nat) : state :=
  match i with
  | O => empty_state
  | S i' =>
    match c with
      | SKIP =>
          st
      | l ::= a1 =>
          update st l (aeval st a1)
      | c1 ;; c2 =>
          let st' := ceval_step2 st c1 i' in
          ceval_step2 st' c2 i'
      | IFB b THEN c1 ELSE c2 FI =>
          if (beval st b)
            then ceval_step2 st c1 i'
            else ceval_step2 st c2 i'
      | WHILE b1 DO c1 END =>
          if (beval st b1)
          then let st' := ceval_step2 st c1 i' in
               ceval_step2 st' c i'
          else st
    end
  end.

(** _Note_: It is tempting to think that the index [i] here is
    counting the "number of steps of evaluation."  But if you look
    closely you'll see that this is not the case: for example, in the
    rule for sequencing, the same [i] is passed to both recursive
    calls.  Understanding the exact way that [i] is treated will be
    important in the proof of [ceval__ceval_step], which is given as
    an exercise below. *)

(** Third try, returning an [option state] instead of just a [state]
    so that we can distinguish between normal and abnormal
    termination. *)

Fixpoint ceval_step3 (st : state) (c : com) (i : nat)
                    : option state :=
  match i with
  | O => None
  | S i' =>
    match c with
      | SKIP =>
          Some st
      | l ::= a1 =>
          Some (update st l (aeval st a1))
      | c1 ;; c2 =>
          match (ceval_step3 st c1 i') with
          | Some st' => ceval_step3 st' c2 i'
          | None => None
          end
      | IFB b THEN c1 ELSE c2 FI =>
          if (beval st b)
            then ceval_step3 st c1 i'
            else ceval_step3 st c2 i'
      | WHILE b1 DO c1 END =>
          if (beval st b1)
          then match (ceval_step3 st c1 i') with
               | Some st' => ceval_step3 st' c i'
               | None => None
               end
          else Some st
    end
  end.

(** We can improve the readability of this definition by introducing a
    bit of auxiliary notation to hide the "plumbing" involved in
    repeatedly matching against optional states. *)

Notation "'LETOPT' x <== e1 'IN' e2"
   := (match e1 with
         | Some x => e2
         | None => None
       end)
   (right associativity, at level 60).

Fixpoint ceval_step (st : state) (c : com) (i : nat)
                    : option state :=
  match i with
  | O => None
  | S i' =>
    match c with
      | SKIP =>
          Some st
      | l ::= a1 =>
          Some (update st l (aeval st a1))
      | c1 ;; c2 =>
          LETOPT st' <== ceval_step st c1 i' IN
          ceval_step st' c2 i'
      | IFB b THEN c1 ELSE c2 FI =>
          if (beval st b)
            then ceval_step st c1 i'
            else ceval_step st c2 i'
      | WHILE b1 DO c1 END =>
          if (beval st b1)
          then LETOPT st' <== ceval_step st c1 i' IN
               ceval_step st' c i'
          else Some st
    end
  end.

Definition test_ceval (st:state) (c:com) :=
  match ceval_step st c 500 with
  | None    => None
  | Some st => Some (st X, st Y, st Z)
  end.

(* Eval compute in
     (test_ceval empty_state
         (X ::= ANum 2;;
          IFB BLe (AId X) (ANum 1)
            THEN Y ::= ANum 3
            ELSE Z ::= ANum 4
          FI)).
   ====>
      Some (2, 0, 4)   *)

(** **** Exercise: 2 stars (pup_to_n) *)
(** Write an Imp program that sums the numbers from [1] to
   [X] (inclusive: [1 + 2 + ... + X]) in the variable [Y].  Make sure
   your solution satisfies the test that follows. *)

Definition pup_to_n : com :=
  (* FILL IN HERE *) admit.

(*
Example pup_to_n_1 :
  test_ceval (update empty_state X 5) pup_to_n
  = Some (0, 15, 0).
Proof. reflexivity. Qed.
*)
(** [] *)

(** **** Exercise: 2 stars, optional (peven) *)
(** Write a [While] program that sets [Z] to [0] if [X] is even and
    sets [Z] to [1] otherwise.  Use [ceval_test] to test your
    program. *)

(* FILL IN HERE *)
(** [] *)

(* ################################################################ *)
(** * Equivalence of Relational and Step-Indexed Evaluation *)

(** As with arithmetic and boolean expressions, we'd hope that
    the two alternative definitions of evaluation actually boil down
    to the same thing.  This section shows that this is the case.
    Make sure you understand the statements of the theorems and can
    follow the structure of the proofs. *)

Theorem ceval_step__ceval: forall c st st',
      (exists i, ceval_step st c i = Some st') ->
      c / st || st'.
Proof.
  intros c st st' H.
  inversion H as [i E].
  clear H.
  generalize dependent st'.
  generalize dependent st.
  generalize dependent c.
  induction i as [| i' ].

  Case "i = 0 -- contradictory".
    intros c st st' H. inversion H.

  Case "i = S i'".
    intros c st st' H.
    com_cases (destruct c) SCase;
           simpl in H; inversion H; subst; clear H.
      SCase "SKIP". apply E_Skip.
      SCase "::=". apply E_Ass. reflexivity.

      SCase ";;".
        destruct (ceval_step st c1 i') eqn:Heqr1.
        SSCase "Evaluation of r1 terminates normally".
          apply E_Seq with s.
            apply IHi'. rewrite Heqr1. reflexivity.
            apply IHi'. simpl in H1. assumption.
        SSCase "Otherwise -- contradiction".
          inversion H1.

      SCase "IFB".
        destruct (beval st b) eqn:Heqr.
        SSCase "r = true".
          apply E_IfTrue. rewrite Heqr. reflexivity.
          apply IHi'. assumption.
        SSCase "r = false".
          apply E_IfFalse. rewrite Heqr. reflexivity.
          apply IHi'. assumption.

      SCase "WHILE". destruct (beval st b) eqn :Heqr.
        SSCase "r = true".
         destruct (ceval_step st c i') eqn:Heqr1.
          SSSCase "r1 = Some s".
            apply E_WhileLoop with s. rewrite Heqr. reflexivity.
            apply IHi'. rewrite Heqr1. reflexivity.
            apply IHi'. simpl in H1. assumption.
          SSSCase "r1 = None".
            inversion H1.
        SSCase "r = false".
          inversion H1.
          apply E_WhileEnd.
          rewrite <- Heqr. subst. reflexivity.  Qed.

(** **** Exercise: 4 stars (ceval_step__ceval_inf) *)
(** Write an informal proof of [ceval_step__ceval], following the
    usual template.  (The template for case analysis on an inductively
    defined value should look the same as for induction, except that
    there is no induction hypothesis.)  Make your proof communicate
    the main ideas to a human reader; do not simply transcribe the
    steps of the formal proof.

(* FILL IN HERE *)
[]
*)

Theorem ceval_step_more: forall i1 i2 st st' c,
  i1 <= i2 ->
  ceval_step st c i1 = Some st' ->
  ceval_step st c i2 = Some st'.
Proof.
induction i1 as [|i1']; intros i2 st st' c Hle Hceval.
  Case "i1 = 0".
    simpl in Hceval. inversion Hceval.
  Case "i1 = S i1'".
    destruct i2 as [|i2']. inversion Hle.
    assert (Hle': i1' <= i2') by omega.
    com_cases (destruct c) SCase.
    SCase "SKIP".
      simpl in Hceval. inversion Hceval.
      reflexivity.
    SCase "::=".
      simpl in Hceval. inversion Hceval.
      reflexivity.
    SCase ";;".
      simpl in Hceval. simpl.
      destruct (ceval_step st c1 i1') eqn:Heqst1'o.
      SSCase "st1'o = Some".
        apply (IHi1' i2') in Heqst1'o; try assumption.
        rewrite Heqst1'o. simpl. simpl in Hceval.
        apply (IHi1' i2') in Hceval; try assumption.
      SSCase "st1'o = None".
        inversion Hceval.

    SCase "IFB".
      simpl in Hceval. simpl.
      destruct (beval st b); apply (IHi1' i2') in Hceval; assumption.

    SCase "WHILE".
      simpl in Hceval. simpl.
      destruct (beval st b); try assumption.
      destruct (ceval_step st c i1') eqn: Heqst1'o.
      SSCase "st1'o = Some".
        apply (IHi1' i2') in Heqst1'o; try assumption.
        rewrite -> Heqst1'o. simpl. simpl in Hceval.
        apply (IHi1' i2') in Hceval; try assumption.
      SSCase "i1'o = None".
        simpl in Hceval. inversion Hceval.  Qed.

(** **** Exercise: 3 stars (ceval__ceval_step) *)
(** Finish the following proof.  You'll need [ceval_step_more] in a
    few places, as well as some basic facts about [<=] and [plus]. *)

Theorem ceval__ceval_step: forall c st st',
      c / st || st' ->
      exists i, ceval_step st c i = Some st'.
Proof.
  intros c st st' Hce.
  ceval_cases (induction Hce) Case.
  (* FILL IN HERE *) Admitted.
(** [] *)

Theorem ceval_and_ceval_step_coincide: forall c st st',
      c / st || st'
  <-> exists i, ceval_step st c i = Some st'.
Proof.
  intros c st st'.
  split. apply ceval__ceval_step. apply ceval_step__ceval.
Qed.

(* ####################################################### *)
(** * Determinism of Evaluation (Simpler Proof) *)

(** Here's a slicker proof showing that the evaluation relation is
    deterministic, using the fact that the relational and step-indexed
    definition of evaluation are the same. *)

Theorem ceval_deterministic' : forall c st st1 st2,
     c / st || st1  ->
     c / st || st2 ->
     st1 = st2.
Proof.
  intros c st st1 st2 He1 He2.
  apply ceval__ceval_step in He1.
  apply ceval__ceval_step in He2.
  inversion He1 as [i1 E1].
  inversion He2 as [i2 E2].
  apply ceval_step_more with (i2 := i1 + i2) in E1.
  apply ceval_step_more with (i2 := i1 + i2) in E2.
  rewrite E1 in E2. inversion E2. reflexivity.
  omega. omega.  Qed.


Require Import Coq.Bool.Bool.
Require Coq.Classes.Equivalence.
Require Import ExtLib.Tactics.Consider.

Ltac think' ext solver :=
  repeat (match goal with
            | [ H : Some _ = Some _ |- _ ] => inversion H ; clear H ; subst
            | [ H : inl _ = inr _ |- _ ] => inversion H ; clear H ; subst
            | [ H : inr _ = inr _ |- _ ] => inversion H ; clear H ; subst
            | [ H : _ |- _ ] => erewrite H in * |- by solver
            | [ H : _ |- _ ] => erewrite H by solver
            | [ H : andb _ _ = true |- _ ] =>
              apply Bool.andb_true_iff in H ; destruct H
            | [ H : orb _ _ = false |- _ ] =>
              apply Bool.orb_false_iff in H ; destruct H
            | [ H : Equivalence.equiv _ _ |- _ ] =>
              unfold Equivalence.equiv in H ; subst
            | [ H : _ /\ _ |- _ ] => destruct H
            | [ H : exists x, _ |- _ ] => destruct H
            | [ H : context [ match ?X with _ => _ end ] |- _ ] =>
              ((consider X ; try congruence); [ intros ]) ||
                (consider X ; solve [ congruence | solver ])
          end || (progress ext)).

Ltac think := think' idtac ltac:(eauto).

(** Lemmas About Lists **)
Require Import List.
Lemma nth_error_None_length : forall (T : Type) (ls : list T) (n : nat),
  nth_error ls n = None -> length ls <= n.
Proof.
  induction ls; destruct n; simpl; intros; think.
  constructor.
  apply Le.le_0_n.
  inversion H.
  eapply IHls in H. apply Le.le_n_S. assumption.
Qed.

Lemma map_nth_error_full : forall T U (F : T -> U) ls n,
  nth_error (map F ls) n = match nth_error ls n with
                             | None => None
                             | Some v => Some (F v)
                           end.
Proof.
  induction ls; destruct n; simpl; intros; think; auto.
Qed.


Require Export P01.

(* Check leb_n_Sn. *)
Theorem count_member_nonzero : forall (s : bag),
  1 <=? (count 1 (1 :: s)) = true.
Proof.
  exact FILL_IN_HERE. Qed.


Require Import List.
Import ListNotations.

Require Import Util.Class.

Inductive Color := White | Black.

Instance ColorEnum : Enum Color := {|
  enum := [White;Black]
  |}.
Proof.
  destruct x; simpl; auto.
Defined.

Instance ColorEq : Eq Color.
  derive_eq.
Defined.

Definition flip : Color -> Color :=
  fun c => match c with
           | White => Black
           | Black => White
           end.

Lemma neq_flip : forall c c', c <> c' -> flip c' = c.
Proof.
  intros; destruct c,c'; auto; contradiction.
Qed.

Lemma flip_flip : forall c, flip (flip c) = c.
Proof.
  destruct c; auto.
Qed.


Set Implicit Arguments.

Add Rec LoadPath "." as Top.

Require Import Regex Automata2.
Require Import List ListSet Bool Arith.
Import ListNotations.

Definition bool_list_dec : forall x y: list bool, {x = y} + {x <> y} := list_eq_dec bool_dec.

Section Nae.

Variable A St: Type.
Variable Seq_dec: forall x y:St, {x = y} + {x <> y}.

Definition word := list A.

Definition nae := nfa (option A) St.

(*Non recurisif sur un argument, on  ajoute donc un entier qui sera 2 fois la taille de la liste,
  on considère qu'il y a une altérnence entre transition None et transition Some*)
Fixpoint accepts_eps_from (nae : nae) (l: nat) s w :=
  match l with
  | 0 => set_mem Seq_dec s (fin nae)
  | S n =>
      let aux l :=
        fun b s =>
          b || accepts_eps_from nae n s l
      in
      if set_fold_left (aux w) (next nae (None) s) false then
        true
      else
        match w with
        | [] => set_mem Seq_dec s (fin nae)
        | h::q => set_fold_left (aux q) (next nae (Some h) s) false
        end
  end.

Definition accepts_eps (nae: nae) w := accepts_eps_from nae (length w * 2)(start nae) w.

(*
(*On utilise steps car mieux pour raisonner après, blocage avec l'autre version de accepts_eps_from*)
Inductive steps_nae nae : list A -> Ensemble (S * S) :=
  | In_steps_nil : forall p q, rtrancl (eps nae) (p, q) -> steps_nae nae [] (p, q)
  | In_steps_cons :
      forall h q sa sb sc sd,
        rtrancl (eps nae) (sa, sb) ->
        step nae (Some h) (sb, sc) ->
        steps_nae nae q (sc, sd) ->
          steps_nae nae (h::q) (sa, sd).

Inductive accepts_eps_from (nae: nae) (s: S) : list A -> Prop :=
  | acc_nil : In _ (fin nae) s -> accepts_eps_from nae s []
  | acc_cons : forall e h q, In _ (next nae (Some h) s) e -> accepts_eps_from nae e q -> accepts_eps_from nae s (h::q)
  | acc_none : forall e w, In _ (next nae None s) e -> accepts_eps_from nae e w -> accepts_eps_from nae s w.

Definition accepts_eps nae w : Prop :=
  exists k, In _ (fin nae) k /\ In _ (steps_nae nae w) (start nae, k).
*)
End Nae.

Section BitsNAe.

Variable A : Type.
Variable Aeq_dec : forall x y:A, {x = y} + {x <> y}.

Definition bitsNAe := nae A (list bool).

Fixpoint atom_next (a: A) oa lb: set (list bool) :=
  match lb with
  | [true] =>
    match oa with
    | Some x =>
      if Aeq_dec x a then [[false]] else []
    | _ => []
    end
  | _ => []
  end.

Definition atom (a: A): bitsNAe :=
  mkNfa
    ([true])
    (atom_next a)
    ([[false]]).

Definition prepend_list_set (b: bool) (ens: set (list bool)) :=
  set_map bool_list_dec (fun l => b::l) ens.

(*Inductive alt_next (nae1 nae2: bitsNAe): option A -> list bool -> Ensemble (list bool) :=
  | nextL : forall a q s, In _ (next nae1 a s) q -> alt_next nae1 nae2 a (true::q) s
  | nextR : forall a q s, In _ (next nae2 a s) q -> alt_next nae1 nae2 a (false::q) s.

Inductive alt_next (nae1 nae2: bitsNAe): option A -> list bool -> Ensemble (list bool) :=
  | nextNL : alt_next nae1 nae2 None [] (true::start nae1)
  | nextNR : alt_next nae1 nae2 None [] (false::start nae2)
  | nextL : forall a q s, In _ (next nae1 a q) s -> alt_next nae1 nae2 a (true::q) (true::s)
  | nextR : forall a q s, In _ (next nae2 a q) s -> alt_next nae1 nae2 a (false::q) (true::s).

Inductive alt_fin (nae1 nae2: bitsNAe): Ensemble (list bool) :=
  | finL : forall s, In _ (fin nae1) s -> alt_fin nae1 nae2 (true::s)
  | finR : forall s, In _ (fin nae1) s -> alt_fin nae1 nae2 (false::s).*)

Fixpoint alt_next (nae1 nae2: bitsNAe) oa lb: set (list bool) :=
  match oa with
  | None =>
      match lb with
      | [] => [true::start nae1; false::start nae2]
      | _ => []
      end
  | Some x =>
      match lb with
      | true::q => prepend_list_set true (next nae1 oa q)
      | false::q => prepend_list_set false (next nae2 oa q)
      | _ => []
      end
  end.

Definition alt (nae1: bitsNAe) (nae2: bitsNAe): bitsNAe :=
  let start1 := start nae1 in
  let start2 := start nae2 in

  let next1 := next nae1 in
  let next2 := next nae2 in

  let fin1 := fin nae1 in
  let fin2 := fin nae2 in

  mkNfa
    ([])
    (alt_next nae1 nae2)
    (set_union bool_list_dec (prepend_list_set true fin1) (prepend_list_set false fin2)).

(*Definition conc (nae1: bitsNAe) (nae2: bitsNAe): bitsNAe :=
  let start1 := start nae1 in
  let start2 := start nae2 in

  let next1 := next nae1 in
  let next2 := next nae2 in

  let fin1 := fin nae1 in
  let fin2 := fin nae2 in

  mkNfa
    (true::start1)
    (fun t s =>
      match s with
      | [] => empty_set
      | true::q => prepend_list_set true (next1 t q)
      | false::q => prepend_list_set false (next2 t q)
      end)
    (prepend_list_set false fin2).

Fixpoint to_nfae (rexp: rexp A): bitsNAe :=
  match rexp with
  | Empty _ => mkNfa ([]) (fun s t => empty_set) (fun s => s = [])
  | Atom a => atom a
  | Alt r1 r2 => alt (to_nfae r1) (to_nfae r2)
  | Conc r1 r2 => conc (to_nfae r1) (to_nfae r2)
  | Star r => mkNfa ([]) (fun s t => empty_set) (fun s => s = [])
  end.*)

End BitsNAe.

Section Test.

Definition rexp1 := Atom 1.
Definition nfae1 := atom eq_nat_dec 1.

Lemma acc_nfae1 : accepts_eps bool_list_dec nfae1 [1] = true.
  auto.
Qed.

Definition nfae2 := alt (atom eq_nat_dec 1) (atom eq_nat_dec 2).

Goal accepts_eps bool_list_dec nfae2 [1] = true.
Proof.
  auto.
Qed.

Goal forall (A : Type) (nae1 nae2 : bitsNAe A) w,
  accepts_eps bool_list_dec nae1 w = true -> accepts_eps bool_list_dec (alt nae1 nae2) w = true.
Proof.
  intros A nae1 nae2 w.
  intros H.
  induction w.
  compute.
Qed.

Variable A: Type.

Goal forall nae: bitsNAe A, accepts_eps nae [] ->
        exists s: list bool, In _ (fin nae) s -> rtrancl (eps nae) (start nae, s).

  intros nae H.
  unfold accepts_eps in H.
  destruct H.
  destruct H as (H, H').
  exists x.
  intros _.
  inversion H'.
  assumption.
Qed.

Lemma step_imp_next:
  forall (na : bitsNAe A) (a : option A) (p q : list bool),
    In _ (step na a) (p, q) -> In _ (next na a p) q.
Proof.
  intros na a p q H.
  inversion_clear H.
  assumption.
Qed.

(*Bon ça prouve le goal suivant mais ça à l'air faux quand meme hein*)
Lemma rtrancl_imp_step :
  forall (na : bitsNAe A) (a : option A) (p q : list bool),
    rtrancl (step na a) (p, q) -> step na a (p, q).
Admitted.

Goal forall nae1 nae2: bitsNAe A, accepts_eps nae1 [] -> accepts_eps (alt nae1 nae2) [].
intros nae1 nae2 H.
induction H.
destruct H as (H, H').
unfold accepts_eps.
exists (true::x).
split.
apply finL.
assumption.
simpl.
Print In_steps_nil.
apply In_steps_nil.
unfold eps.
inversion_clear H'.
Print rtrancl.
apply rtrancl_trans with (b := true::start nae1).
apply rtrancl_base.
apply In_step.
simpl.
apply nextNL.
apply rtrancl_base.
apply In_step.
Print nextL.
simpl.
apply nextL.
induction H0.
Admitted.

Goal forall (nae1 nae2 : bitsNAe A),
  next (alt nae1 nae2) None [] (true::start nae1).
Proof.
  intros nae1 nae2.
  apply nextNL.
Qed.

Goal forall h q (nae1 nae2: bitsNAe A), ((accepts_eps nae1 q) -> accepts_eps (alt nae1 nae2) q) -> accepts_eps nae1 (h::q) -> accepts_eps (alt nae1 nae2) (h::q).
Proof.
  intros h q nae1 nae2 IH H.
  induction H.
  destruct H as (H, H').
  exists (true::x).
  split.
  * apply finL.
  assumption.
  * simpl.
  inversion_clear H'.
  apply In_steps_cons with (sb := true::sb) (sc := true::sc).
  apply rtrancl_trans with (true::start nae1).
  apply rtrancl_base.
  apply In_step.
  apply nextNL.
  apply rtrancl_base.
  apply In_step.
  apply nextL.

Qed.

Goal forall nae1 nae2: bitsNAe A, accepts_eps nae1 [] -> accepts_eps (alt nae1 nae2) [].
  intros nae1 nae2 H.
  inversion H as (x, H').
  destruct H' as (H', H'').
  inversion_clear H''.
  unfold accepts_eps.
  exists (true::x).
  split; simpl; try apply finL; try assumption.
  apply In_steps_nil.
  apply rtrancl_intro_rtrancl with (b := true::start nae1); try apply rtrancl_refl.
  apply rtrancl_intro_rtrancl with (b := []); try apply rtrancl_refl.
  apply In_step.
  apply nextNL.
  unfold eps.
  apply In_step.
  apply nextL.
  apply step_imp_next.
  induction H0.
  assumption.
Qed.

Goal forall (w : list A) (nae1 nae2 : bitsNAe A),
        accepts_eps nae1 w -> accepts_eps (alt nae1 nae2) w.
  intros w nae1 nae2.
  induction 1.
  destruct H as (H, H').
  unfold accepts_eps.
  exists (true::x).
  split.
  *
    simpl.
    apply finL.
    assumption.
  *
  induction w.
  **
    apply In_steps_nil.
    simpl.
    Print rtrancl.
    apply rtrancl_intro_rtrancl with (b := true::start nae1).
    ***
      apply rtrancl_intro_rtrancl with (b := []); try apply rtrancl_refl.
      unfold eps.
      apply In_step.
      simpl.
      unfold In.
      Print alt_next.
      apply nextNL.
    ***
    unfold eps.
    apply In_step.
    simpl.
    Print alt_next.
Qed.

(*Blocage....*)
Goal forall (w : list A) (nae1 nae2 : bitsNAe A),
        accepts_eps nae1 w -> accepts_eps nae2 w -> accepts_eps (alt nae1 nae2) w.

  unfold accepts_eps.
  intros w nae1 nae2 H H'.
  simpl.
  apply acc_none with (e := (false::(start nae2))).
  *
    unfold In.
    simpl.
    apply nextNR.
  *
  Print accepts_eps_from.
  induction w.

Goal accepts_eps nfae2 [1].
  Print acc_cons.
  apply acc_none with (e := [true; true]).
  unfold In.
  simpl.
  Print nextNL.
  apply nextNL.
  Print accepts_eps_from.
  apply acc_cons with (e := .

Goal forall x : nat, accepts (to_nfae (Atom x)) [Some x].
  intro x.
  compute.
  exists [false].
  tauto.

Goal accepts nfae1 [Some 1].
Proof.
  compute.
  exists [false].
  tauto.
Qed.

Goal accepts nfae1 = L rexp1.

Definition rexp2 := Conc (Atom 1) (Atom 2).
Definition nfae2 := to_nfae rexp2.

Goal accepts nfae2 [Some 1; Some 3].
Proof.
  unfold accepts.
  simpl.
  unfold prepend_list_set.
  exists [true; false].
  split.
  tauto.
  exists [false; false].
  split; tauto.
Qed.

Definition rexp3 := Alt (Atom 1) (Atom 2).
Definition nfae3 := to_nfae rexp3.

Goal accepts nfae3 [Some ] /\ accepts nfae3 [Some 2].
Proof.
  split; compute.
  *
  exists [true; false].
  split.
  intro.
  discriminate H.
  reflexivity.
Qed.

End Test.


(*
    The functions to implement the algorithm,
    dependencies and declarations are discribed in Types.v

    The top-level function is find_path
*)

From mathcomp Require Import all_ssreflect.
Require Import Coq.Strings.String Coq.Bool.Bool Coq.Lists.List.
Import ListNotations.
From Coq.Arith Require Import Arith EqNat.
Open Scope string_scope.
Open Scope list_scope.
Require Import Coq.Program.Equality.
Require Import Arith.
Require Import Coq.Program.Tactics.
From Taxiway Require Export Types.

(* ============ comparision functions ============*)

(*
    check whether the Arc arc is on the next ATC command in state cur_s
*)
Definition if_on_next_taxiway (cur_s : State_type) (arc :  Arc_type) : bool :=
    match hd_error cur_s@3 with
    | None => false
    | Some t => t =? arc.2
    end.

(*
    check whether the Arc arc is on current taxiway of state cur_s
*)
Definition if_on_current_taxiway (cur_s : State_type) (arc :  Arc_type) : bool :=
    cur_s@2 =? arc.2.

Lemma on_current_taxiway_lemma : forall (s:State_type) (arc:Arc_type),
    if_on_current_taxiway s arc -> (arc.2 =? s@2).
Proof. intros s arc H. unfold if_on_current_taxiway in H.
    rewrite -> String.eqb_sym. assumption. Qed.

(*
    check whether current state reaches the endpoint
*)
Definition if_reach_endpoint (cur_s : State_type) (end_v : Vertex) : bool :=
    match hd_error cur_s@1 with
    | None => false (*will never reach*)
    | Some arc => (eqv arc.1.2.1 end_v) && (eqn (List.length cur_s@3) 0)
    end.

(*
    check whether an Arc starts from current node
*)
Definition next_Arcs (current : Node_type) (arc :  Arc_type) : bool :=
    (eqv current.1 arc.1.1.1) && (eqv current.2 arc.1.1.2).

(* ============ find Arcs ============*)

(*
    filter on the graph to find all Arcs start from current noe
*)
Definition find_Arc (current : Node_type) (D : C_Graph_type) : list Arc_type :=
    filter (next_Arcs current) D.

(* ============ step functions ============*)

(*
    step_state_by_arc takes a state and an Arc as input,
        it checks whether the Arc arc is valid for next step,
        if is valid return a list of one corresponding state,
        if not, return an empty list
*)
Definition step_state_by_arc (cur_s : State_type) (arc :  Arc_type) : list State_type :=
    if if_on_current_taxiway cur_s arc
    then [State (arc::cur_s@1) cur_s@2 cur_s@3 cur_s@4]
    else if if_on_next_taxiway cur_s arc (* on the next taxiway *)
        then [State (arc::cur_s@1) arc.2 (tail cur_s@3) (cur_s@2 :: cur_s@4)]
        else [].

(*
    step_states takes a state and a graph as input,
        it will return all possible states after a valid step

    step_states may find other possible paths to endpoint
*)
Definition step_states (cur_s : State_type) (D : C_Graph_type) : list State_type :=
    match hd_error cur_s@1 with
    | None => []
    | Some arc => flat_map (step_state_by_arc cur_s) (find_Arc arc.1.2 D)
    end.

(* ============ main functions ============*)

(*
    find_path_aux takes an endpoint, a graph, bound and a state,
        it will return a list of possible paths

    find_path_aux first checks whether state reaches endpoint,
        and find all possible states for next step (no matter reach end or not),
        then recursive on all states returned

    find_path_aux may return zero or more than one path,
        it requires a sufficient large bound
*)
Fixpoint find_path_aux (end_v : Vertex) (D : C_Graph_type) (bound : nat) (cur_s : State_type) : list (list Arc_type) :=
    match bound with
    | 0 => []
    | S n =>
        (if if_reach_endpoint cur_s end_v  (*reach endpoint*)
        then [rev cur_s@1]
        else []) ++
        (flat_map (find_path_aux end_v D n) (step_states cur_s D))
    end.

(*
    hardcoded input vertex. if a vertex is start_vertex in the naive graph,
    we encode input Node in the complete graph to be ((start_vertex, input), (start_vertex, input))
*)
Definition input : Vertex := index 0.

(*
    find_path is the top-level function
    find_path packs original input into initial state and call find_path_aux
*)
Definition find_path (start_v : Vertex) (end_v : Vertex) (ATC : list string) (D : C_Graph_type) : option (list (list Arc_type)) :=
    match ATC with
    | [] => None (* ATC error *)
    | t :: rest => Some (find_path_aux end_v D 100 (State [(((start_v, input), (start_v, input)), t)] t rest []))
    end.


(* This example, submitted by A. Appel, checks the efficiency of
   injection (see bug #1173) *)
(* Expected time < 1.50s *)

Set Implicit Arguments.

Record joinable (t: Type) : Type := Joinable {
   is_empty: t -> Prop;
   join: t -> t -> t -> Prop;
   join_com: forall a b c, join a b c -> join b a c;
   join_empty: forall e a b, is_empty e -> join e a b -> a=b;
   exists_empty: forall a, exists e, is_empty e /\ join e a a;
   join_empty2: forall a b c, join a b c ->  is_empty c -> is_empty a;
   join_empty3: forall e a, join e a a -> is_empty e;
   join_assoc: forall a b c d e, join a b d -> join d c e ->
                    exists f, join b c f /\ join a f e;
   join_eq: forall x y z z', join x y z -> join x y z' -> z = z';
   cancellation: forall a1 a2 b c, join a1 b c -> join a2 b c -> a1=a2
}.

Record joinmap (key: Type) (t: Type) (j : joinable t) : Type
 := Joinmap {
    jm_t : Type;
    jm_j : joinable jm_t;
    lookup: jm_t -> key -> t;
    prim : forall (f: key -> t) (e: t),
                  (forall k, j.(join) e (f k) (f k)) ->
                  jm_t;
    join_rule: forall s1 s2 s,
                jm_j.(join) s1 s2 s <->
                forall x, j.(join) (lookup s1 x) (lookup s2 x) (lookup s x);
    empty_rule: forall e x, jm_j.(is_empty) e -> j.(is_empty) (lookup e x);
    prim_rule: forall f e pf k, lookup (prim f e pf) k = f k;
    ext: forall s1 s2,
        (forall x, lookup s1 x = lookup s2 x) <-> s1 = s2;
    can_join: forall s1 s2,
            (forall x, exists v,
                j.(join) (lookup s1 x) (lookup s2 x) v) ->
            exists s3, jm_j.(join) s1 s2 s3;
    can_split: forall s1 s3,
            (forall x, exists v,
                j.(join) (lookup s1 x) v (lookup s3 x)) ->
            exists s2, jm_j.(join) s1 s2 s3
}.

Parameter mkJoinmap :   forall (key: Type) (t: Type) (j: joinable t),
joinmap key j.

Parameter ADMIT: forall p: Prop, p.
Arguments ADMIT [p].

Module Share.
Parameter jb : joinable bool.
Definition jm: joinmap nat jb := mkJoinmap nat jb.
Definition t := jm.(jm_t).
Definition j := jm.(jm_j).
Parameter nonempty: t -> Prop.
End Share.

Section Own.

Variable inv : Type.

Inductive own : Type :=
  | NO
  | VAL' : forall sh, Share.nonempty sh -> own
  | LK : forall sh, Share.nonempty sh -> Share.t -> inv -> own
  | CT : forall sh, Share.nonempty sh -> own
  | FUN: forall sh, Share.nonempty sh -> inv -> own.

Definition own_join (a b c: own) : Prop :=
 match a , b , c with
  | NO , _ , _ =>  b=c
  | _ , NO , _ =>  a=c
  | @VAL' sa _, @VAL' sb _, @VAL' sc _ => Share.j.(join) sa sb sc
  | @LK sa pa ha fa, @LK sb pb hb fb, @LK sc pc hc fc =>
      Share.j.(join) sa sb sc /\
      Share.j.(join) ha hb hc /\
      fa=fc /\
      fb=fc
  | @CT sa pa , @CT sb pb, @CT sc pc => Share.j.(join) sa sb sc
  | @FUN sa pa fa, @FUN sb pb fb, @FUN sc pc fc =>
        Share.j.(join) sa sb sc /\ fa=fc /\ fb=fc
  | _ , _ , _ => False
 end.

Definition own_is_empty (a: own) := a=NO.

Definition jown : joinable own :=
  Joinable own_is_empty own_join
     ADMIT ADMIT  ADMIT  ADMIT  ADMIT  ADMIT  ADMIT  ADMIT .
End Own.

Fixpoint sinv (n: nat) : Type :=
  match n with
   | O => unit
   | S n => prodT (sinv n) (own (sinv n) -> unit -> Prop)
 end.

Parameter address: Set.

Definition jm (n: nat) := mkJoinmap address (jown (sinv n)).

Definition worldfun (n: nat) := (jm n).(jm_t).

Inductive world : Type :=
  mk_world: forall n, worldfun n -> world.

Lemma test: forall n1 w1 n2 w2, mk_world n1 w1 = mk_world n2 w2 ->
         n1 = n2.
Proof.
intros.
Timeout 10 Time injection H.


From Domains Require Import Preamble Preorder Poset Dcpo.

Section Map.
  Context (D E : Dcpo.type).

  Definition map := {f : D → E | is_continuous f}.
  Definition ap (f : map) : D → E := proj1_sig f.
  Definition ap_cont (f : map) : is_continuous (ap f) := proj2_sig f.

  Lemma map_ext : ∀ f g, ap f = ap g → f = g.
  Proof. by rewrite /map => f g fg; apply: eq_sig. Qed.

  Definition map_lt (f g : map) : Prop :=
    ∀ x, ap f x ≤ ap g x.

  Lemma map_ltR : ∀ f, map_lt f f.
  Proof. by move=>?. Qed.

  Lemma map_ltT : ∀ f g h, map_lt f g → map_lt g h → map_lt f h.
  Proof.
    move=> f g h fg gh x.
    by apply: ltT; [apply: fg | apply: gh].
  Qed.

  HB.instance Definition map_preorder_axioms := PreorderOfType.Build map map_lt map_ltR map_ltT.

  Lemma map_ltE : ∀ f g : map, f ≤ g → g ≤ f → f = g.
  Proof.
    move=> f g fg gf.
    apply/map_ext/funext=>x.
    by apply: ltE; [apply: fg | apply: gf].
  Qed.

  HB.instance Definition map_poset_axioms := PosetOfPreorder.Build map map_ltE.

  Section Lub.

    Context (A : Family map).

    Lemma push_ap_directed : ∀ (x : D), is_directed A → is_directed (push_fam (λ f, ap f x) A).
    Proof.
      move=> x dir; split; first by apply: nonempty dir.
      move=> //= i j.
      case: (predirected A dir i j) => k [ij jk].
      by exists k; split; [apply: ij | apply: jk].
    Qed.

    Section Map.
      Context (dir : is_directed A).

      Definition dlub_fun : D → E :=
        λ x,
        dlub (push_fam (λ f, ap f x) A) (push_ap_directed x dir).

      Lemma dlub_fun_continuous : is_continuous dlub_fun.
      Proof.
        apply: preserves_dlub_cont.
        move=> F dirF; split.
        - move=> //= i.
          apply: above_lub; first by apply: dlub_is_lub.
          move=> //= z.
          apply: ltT.
          + by apply: cont_mono; [apply: ap_cont | apply: dlub_is_ub].
          + by apply: (dlub_is_ub (push_fam _ A)).
        - move=> z //= H.
          apply: dlub_least => //= x.
          apply: lub_univ; first by [apply: ap_cont; eauto].
          move=> //= y.
          apply: ltT'; first by apply: H.
          by apply: (dlub_is_ub (push_fam _ A)).
      Qed.

      Lemma map_ltHasDLubs : ∃ f, is_lub A f.
      Proof.
        unshelve esplit.
        - by exists dlub_fun; apply: dlub_fun_continuous.
        - split=>/=.
          + move=> i; move=> ?.
            by apply: (dlub_is_ub (push_fam _ _)).
          + move=> f Hf; move=> ?.
            apply: dlub_least => ?.
            by apply: Hf.
      Qed.
    End Map.
  End Lub.

  HB.instance Definition map_dcpo_axioms := DcpoOfPoset.Build map map_ltHasDLubs.

End Map.

Arguments ap [D] [E].