(* This file is distributed under the terms of the MIT License, also known as the X11 Licence.  A copy of this license is in the README file that accompanied the original distribution of this file.  Based on code written by: Brian Aydemir Aaron Bohannon Arthur Charg\'eraud Stephanie Weirich *)
(** remove printing ~ *)
(* *********************************************************************** *)
(** * Implementation *)
(* ********************************************************************* *)
(** ** Basic definitions *)
(** This section defines the following basic operations and predicates on association lists.  - [one]: Constructs an association list consisting of exactly one binding. - [map]: Maps a function over the values in an association list. - [dom]: Computes the domain of an association list, i.e., the set consisting of its keys. - [disjoint]: Binary predicate that holds when the domains of two association lists are disjoint. - [binds]: Ternary predicate that holds when a key-value pair appears somewhere in an association list. - [uniq]: Unary predicate that holds when an association list binds any given key at most once.  Note that [uniq_push] is defined in terms of [one], due to our normal form for association lists.  Implicit arguments are declared by default for these definitions.  We define some local notations to make the definition of [uniq] easier to read and to be consistent with the notations used in the rest of this library. *)
Section Definitions.
Set Implicit Arguments.
Variables A B C : Set.
Definition one (C : Set) (item : C) : list C := cons item nil.
Inductive asn (A:Set) (B:Set) : Set :=
| VarAsn : atom -> A -> asn A B
| AltAsn : B         -> asn A B
.
Definition map (f1 : A -> A) (f2: B -> B) (E : list (asn A B)) : list (asn A B) :=
List.map (fun x => match x with
| VarAsn _ x a => VarAsn B x (f1 a)
| AltAsn _ b   => AltAsn A (f2 b)
end) E.
Fixpoint dom (A: Set) (B: Set) (E : list (asn A B)) {struct E} : atoms :=
match E with
| nil => empty
| (VarAsn _ x _) :: E' => add x (dom E')
| _ :: E' => dom E'
end.
Definition disjoint (E : list (asn A B)) (F : list (asn A B)) : Prop :=
Subset (inter (dom E) (dom F)) empty.
Definition binds (x : atom) (a : A) (E : list (asn A B)) : Prop :=
List.In (VarAsn  B x a) E.
Definition bindsAlt (b : B) (E : list (asn A B)) : Prop :=
List.In (AltAsn A b) E.
Local Notation "x ~~ a" := (one (VarAsn B x a)) (at level 68).
Local Notation "x `notin` E" := (~ In x E) (at level 70).
Inductive uniq : list (asn A B) -> Prop :=
| uniq_nil :
uniq nil
| uniq_push : forall x a E,
uniq E ->
x `notin` dom E ->
uniq ((x ~~ a) ++ E)
| uniq_alt : forall b E,
uniq E ->
uniq (one (AltAsn A b) ++ E).
Fixpoint erase (E : list (asn A B)) : list B :=
match E with
| nil => nil
| (AltAsn _ b) :: E => b :: erase E
| (VarAsn _ x a) :: E => erase E
end.
Unset Implicit Arguments.
End Definitions.
(* ********************************************************************* *)
(** ** Local notations *)
(** We make a local notation for [one], and for operations and predicate on finite sets, in order to make the statements of the lemmas below more readable.  The notations are local so that users of this functor may choose their own notations. *)
Local Notation "[ i ]" := (one i).
(*  Local Notation "x ~~ T" := (one (VarAsn B x T)) (at level 68). *)
Local Notation "E `union` F" :=
(union E F)
(at level 65, right associativity).
Local Notation "x `in` E" :=
(In x E)
(at level 70).
Local Notation "x `notin` E" :=
(~ In x E)
(at level 70).
Local Notation "E [=] F" :=
(Equal E F)
(at level 70, no associativity).
Local Notation "E [<=] F" :=
(Subset E F)
(at level 70, no associativity).
(* ********************************************************************* *)
(** ** List properties *)
(** The following block of properties is used mainly for rewriting association lists into the normal form described above.  See the [simpl_alist] and [rewrite_alist] tactics below. *)
Section ListProperties.
Variables X : Set.
Variables x y : X.
Variables l l1 l2 l3 : list X.

(* Specifications: *)
Lemma cons_app_one :
cons x l = [ x ] ++ l.