(* SCW: Make an instance of EqDef_eq for X so that we can use the "==" in [get] below. *)
Instance EqDec_of_X : @EqDec X.t eq eq_equivalence.Instance EqDec_eq_of_X: @EqDec_eq X.t.Module Import D := CoqFSetDecide.WDecide_fun X KeySet.Module KeySetProperties := FSetProperties.WProperties_fun X KeySet.Module KeySetFacts := FSetFacts.WFacts_fun X KeySet.(** [one] constructs an association list consisting of exactly one
    binding.  We define an infix notation for it and ensure that the
    arguments to [app] are interpreted in the right scope, i.e.,
    [list_scope].

    Implementation note: The level associated with the notation gives
    it a higher precedence than the "++" notation for [app].  This
    should eliminate the need for parentheses.  See the definition
    below of [uniq], for example. *)

Definition one (C : Type) (item : C) : list C := cons item nil.Notation "x ~ a" := (one (x, a)) (at level 50) : list_scope.(** [dom] computes the domain of an association list, i.e., the
    set consisting of its keys. *)

Fixpoint dom
  (C : Type) (E : list (X.t*C))
  : KeySet.t :=
  match E with
    | nil => empty
    | (x, _) :: E' => add x (dom E')
  end.(** [get] looks up a key in an association list. *)

Fixpoint get
  (C : Type) (x : X.t) (E : list (X.t*C))
  : option C :=
  match E with
    | nil => None
    | (y, c) :: F => if (x == y) then Some c else get x F
  end.(** [binds] is a ternary predicate that holds when a key-value pair
    appears somewhere in the given association list. *)

Definition binds
  (A : Type) (x : X.t) (a : A) (E : list (X.t*A))
  : Prop :=
  List.In (x, a) E.(** [maps] is a ternary predicate that holds when the first binding in
    the list for the given key is to the given value. *)

Definition maps
  (A : Type) (x : X.t) (a : A) (E : list (X.t*A))
  : Prop :=
  get x E = Some a.(** [disjoint] is a binary predicate that holds when the domains of
    two association lists are disjoint. *)

Definition disjoint
  (A B : Type) (E : list (X.t*A)) (F : list (X.t*B))
  : Prop :=
  inter (dom E) (dom F) [<=] empty.(** [map] applies a function to each of the values in an association
    list. *)

Definition map
  (A B : Type) (f : A -> B) (E : list (X.t*A))
  : list (X.t*B) :=
  List.map (fun b => match b with (x, a) => (x, f a) end) E.(** [uniq] is unary predicate that holds if and only if each key is
    bound at most once in the given association list.  Note that
    [uniq] is defined in terms of [one], not [cons]. *)

Inductive uniq (A : Type) : list (X.t*A) -> Prop :=
  | uniq_nil :
      uniq nil
  | uniq_push : forall x a E,
      uniq E ->
      ~ In x (dom E) ->
      uniq (x ~ a ++ E).(** [uniq] is unary predicate that holds if and only if each key is
    bound at most once in the given association list.  Note that
    [uniq] is defined in terms of [one], not [cons]. *)

Inductive uniq (A : Type) : list (X.t*A) -> Prop :=
  | uniq_nil :
      uniq nil
  | uniq_push : forall x a E,
      uniq E ->
      ~ In x (dom E) ->
      uniq (x ~ a ++ E).(** [uniq] is unary predicate that holds if and only if each key is
    bound at most once in the given association list.  Note that
    [uniq] is defined in terms of [one], not [cons]. *)

Inductive uniq (A : Type) : list (X.t*A) -> Prop :=
  | uniq_nil :
      uniq nil
  | uniq_push : forall x a E,
      uniq E ->
      ~ In x (dom E) ->
      uniq (x ~ a ++ E).

(* Specifications: *)
Lemma cons_app_one : cons x l = one x ++ l.
Lemma cons_app_assoc : (cons x l1) ++ l2 = cons x (l1 ++ l2).
Lemma app_assoc : (l1 ++ l2) ++ l3 = l1 ++ (l2 ++ l3).
Lemma app_nil_1 : nil ++ l = l.
Lemma app_nil_2 : l ++ nil = l.
Lemma in_nil_iff : List.In x nil <-> False.
Lemma in_one_iff : List.In x (one y) <-> x = y.
Lemma in_app_iff : List.In x (l1 ++ l2) <-> List.In x l1 \/ List.In x l2.
Lemma map_nil : map f (@nil (X.t*A)) = nil.
Lemma map_one : map f (x ~ b) = (x ~ f b).
Lemma map_cons : map f ((x, b) :: E) = x ~ f b ++ map f E.
Lemma map_app : map f (E ++ F) = map f E ++ map f F.
Lemma dom_nil : dom (@nil (X.t*A)) = empty.
Lemma dom_one : dom (x ~ b) [=] singleton x.
Lemma dom_cons : dom ((x, b) :: E) [=] union (singleton x) (dom E).
Lemma dom_app : dom (E ++ F) [=] union (dom E) (dom F).
Lemma dom_map : dom (map f E) [=] dom E.