(* *********************************************************************)
(*                                                                     *)
(*              The Compcert verified compiler                         *)
(*                                                                     *)
(*          Xavier Leroy, INRIA Paris-Rocquencourt                     *)
(*                                                                     *)
(*  Copyright Institut National de Recherche en Informatique et en     *)
(*  Automatique.  All rights reserved.  This file is distributed       *)
(*  under the terms of the GNU General Public License as published by  *)
(*  the Free Software Foundation, either version 2 of the License, or  *)
(*  (at your option) any later version.  This file is also distributed *)
(*  under the terms of the INRIA Non-Commercial License Agreement.     *)
(*                                                                     *)
(* *********************************************************************)

(** Applicative finite maps are the main data structure used in this
  project.  A finite map associates data to keys.  The two main operations
  are [set k d m], which returns a map identical to [m] except that [d]
  is associated to [k], and [get k m] which returns the data associated
  to key [k] in map [m].  In this library, we distinguish two kinds of maps:
- Trees: the [get] operation returns an option type, either [None]
  if no data is associated to the key, or [Some d] otherwise.
- Maps: the [get] operation always returns a data.  If no data was explicitly
  associated with the key, a default data provided at map initialization time
  is returned.

  In this library, we provide efficient implementations of trees and
  maps whose keys range over the type [positive] of binary positive
  integers or any type that can be injected into [positive].  The
  implementation is based on radix-2 search trees (uncompressed
  Patricia trees) and guarantees logarithmic-time operations.  An
  inefficient implementation of maps as functions is also provided.
*)

Require Import Equivalence EquivDec.
Require Import Coqlib.

(* To avoid useless definitions of inductors in extracted code. *)
Local Unset Elimination Schemes.
Local Unset Case Analysis Schemes.

Set Implicit Arguments.

(** * The abstract signatures of trees *)

Module Type TREE.
  Parameter elt: Type.
  Parameter elt_eq: forall (a b: elt), {a = b} + {a <> b}.
  Parameter t: Type -> Type.
  Parameter empty: forall (A: Type), t A.
  Parameter get: forall (A: Type), elt -> t A -> option A.
  Parameter set: forall (A: Type), elt -> A -> t A -> t A.
  Parameter remove: forall (A: Type), elt -> t A -> t A.

  (** The ``good variables'' properties for trees, expressing
    commutations between [get], [set] and [remove]. *)
  Axiom gempty:
    forall (A: Type) (i: elt), get i (empty A) = None.
  Axiom gss:
    forall (A: Type) (i: elt) (x: A) (m: t A), get i (set i x m) = Some x.
  Axiom gso:
    forall (A: Type) (i j: elt) (x: A) (m: t A),
    i <> j -> get i (set j x m) = get i m.
  Axiom gsspec:
    forall (A: Type) (i j: elt) (x: A) (m: t A),
    get i (set j x m) = if elt_eq i j then Some x else get i m.
  (* We could implement the following, but it's not needed for the moment.
  Hypothesis gsident:
    forall (A: Type) (i: elt) (m: t A) (v: A),
    get i m = Some v -> set i v m = m.
  Hypothesis grident:
    forall (A: Type) (i: elt) (m: t A) (v: A),
    get i m = None -> remove i m = m.
  *)
  Axiom grs:
    forall (A: Type) (i: elt) (m: t A), get i (remove i m) = None.
  Axiom gro:
    forall (A: Type) (i j: elt) (m: t A),
    i <> j -> get i (remove j m) = get i m.
  Axiom grspec:
    forall (A: Type) (i j: elt) (m: t A),
    get i (remove j m) = if elt_eq i j then None else get i m.

  (** Extensional equality between trees. *)
  Parameter beq: forall (A: Type), (A -> A -> bool) -> t A -> t A -> bool.
  Axiom beq_correct:
    forall (A: Type) (eqA: A -> A -> bool) (t1 t2: t A),
    beq eqA t1 t2 = true <->
    (forall (x: elt),
     match get x t1, get x t2 with
     | None, None => True
     | Some y1, Some y2 => eqA y1 y2 = true
     | _, _ => False
    end).

  (** Applying a function to all data of a tree. *)
  Parameter map:
    forall (A B: Type), (elt -> A -> B) -> t A -> t B.
  Axiom gmap:
    forall (A B: Type) (f: elt -> A -> B) (i: elt) (m: t A),
    get i (map f m) = option_map (f i) (get i m).

  (** Same as [map], but the function does not receive the [elt] argument. *)
  Parameter map1:
    forall (A B: Type), (A -> B) -> t A -> t B.
  Axiom gmap1:
    forall (A B: Type) (f: A -> B) (i: elt) (m: t A),
    get i (map1 f m) = option_map f (get i m).

  (** Applying a function pairwise to all data of two trees. *)
  Parameter combine:
    forall (A B C: Type), (option A -> option B -> option C) -> t A -> t B -> t C.
  Axiom gcombine:
    forall (A B C: Type) (f: option A -> option B -> option C),
    f None None = None ->
    forall (m1: t A) (m2: t B) (i: elt),
    get i (combine f m1 m2) = f (get i m1) (get i m2).

  (** Enumerating the bindings of a tree. *)
  Parameter elements:
    forall (A: Type), t A -> list (elt * A).
  Axiom elements_correct:
    forall (A: Type) (m: t A) (i: elt) (v: A),
    get i m = Some v -> In (i, v) (elements m).
  Axiom elements_complete:
    forall (A: Type) (m: t A) (i: elt) (v: A),
    In (i, v) (elements m) -> get i m = Some v.
  Axiom elements_keys_norepet:
    forall (A: Type) (m: t A),
    list_norepet (List.map (@fst elt A) (elements m)).
  Axiom elements_extensional:
    forall (A: Type) (m n: t A),
    (forall i, get i m = get i n) ->
    elements m = elements n.
  Axiom elements_remove:
    forall (A: Type) i v (m: t A),
    get i m = Some v ->
    exists l1 l2, elements m = l1 ++ (i,v) :: l2 /\ elements (remove i m) = l1 ++ l2.

  (** Folding a function over all bindings of a tree. *)
  Parameter fold:
    forall (A B: Type), (B -> elt -> A -> B) -> t A -> B -> B.
  Axiom fold_spec:
    forall (A B: Type) (f: B -> elt -> A -> B) (v: B) (m: t A),
    fold f m v =
    List.fold_left (fun a p => f a (fst p) (snd p)) (elements m) v.
  (** Same as [fold], but the function does not receive the [elt] argument. *)
  Parameter fold1:
    forall (A B: Type), (B -> A -> B) -> t A -> B -> B.
  Axiom fold1_spec:
    forall (A B: Type) (f: B -> A -> B) (v: B) (m: t A),
    fold1 f m v =
    List.fold_left (fun a p => f a (snd p)) (elements m) v.
End TREE.

(** * The abstract signatures of maps *)

Module Type MAP.
  Parameter elt: Type.
  Parameter elt_eq: forall (a b: elt), {a = b} + {a <> b}.
  Parameter t: Type -> Type.
  Parameter init: forall (A: Type), A -> t A.
  Parameter get: forall (A: Type), elt -> t A -> A.
  Parameter set: forall (A: Type), elt -> A -> t A -> t A.
  Axiom gi:
    forall (A: Type) (i: elt) (x: A), get i (init x) = x.
  Axiom gss:
    forall (A: Type) (i: elt) (x: A) (m: t A), get i (set i x m) = x.
  Axiom gso:
    forall (A: Type) (i j: elt) (x: A) (m: t A),
    i <> j -> get i (set j x m) = get i m.
  Axiom gsspec:
    forall (A: Type) (i j: elt) (x: A) (m: t A),
    get i (set j x m) = if elt_eq i j then x else get i m.
  Axiom gsident:
    forall (A: Type) (i j: elt) (m: t A), get j (set i (get i m) m) = get j m.
  Parameter map: forall (A B: Type), (A -> B) -> t A -> t B.
  Axiom gmap:
    forall (A B: Type) (f: A -> B) (i: elt) (m: t A),
    get i (map f m) = f(get i m).
End MAP.

(** * An implementation of trees over type [positive] *)

Module PTree <: TREE.
  Definition elt := positive.
  Definition elt_eq := peq.

  Inductive tree (A : Type) : Type :=
    | Leaf : tree A
    | Node : tree A -> option A -> tree A -> tree A.

  Arguments Leaf [A].
  Arguments Node [A].
  Scheme tree_ind := Induction for tree Sort Prop.

  Definition t := tree.

  Definition empty (A : Type) := (Leaf : t A).

(* Goal: None *)
(* Goal: None *)
  Fixpoint get (A : Type) (i : positive) (m : t A) {struct i} : option A :=
    match m with
    | Leaf => None
    | Node l o r =>
        match i with
        | xH => o
        | xO ii => get ii l
        | xI ii => get ii r
        end
    end.

(* Goal: None *)
(* Goal: None *)
  Fixpoint set (A : Type) (i : positive) (v : A) (m : t A) {struct i} : t A :=
    match m with
    | Leaf =>
        match i with
        | xH => Node Leaf (Some v) Leaf
        | xO ii => Node (set ii v Leaf) None Leaf
        | xI ii => Node Leaf None (set ii v Leaf)
        end
    | Node l o r =>
        match i with
        | xH => Node l (Some v) r
        | xO ii => Node (set ii v l) o r
        | xI ii => Node l o (set ii v r)
        end
    end.

(* Goal: None *)
(* Goal: None *)
  Fixpoint remove (A : Type) (i : positive) (m : t A) {struct i} : t A :=
    match i with
    | xH =>
        match m with
        | Leaf => Leaf
        | Node Leaf o Leaf => Leaf
        | Node l o r => Node l None r
        end
    | xO ii =>
        match m with
        | Leaf => Leaf
        | Node l None Leaf =>
            match remove ii l with
            | Leaf => Leaf
            | mm => Node mm None Leaf
            end
        | Node l o r => Node (remove ii l) o r
        end
    | xI ii =>
        match m with
        | Leaf => Leaf
        | Node Leaf None r =>
            match remove ii r with
            | Leaf => Leaf
            | mm => Node Leaf None mm
            end
        | Node l o r => Node l o (remove ii r)
        end
    end.

  Theorem gempty:
    forall (A: Type) (i: positive), get i (empty A) = None.
  Proof.
(* Goal: forall (A : Type) (i : positive), @eq (option A) (@get A i (empty A)) (@None A) *)
    induction i; simpl; auto.
  Qed.

  Theorem gss:
    forall (A: Type) (i: positive) (x: A) (m: t A), get i (set i x m) = Some x.
  Proof.
(* Goal: forall (A : Type) (i : positive) (x : A) (m : t A), @eq (option A) (@get A i (@set A i x m)) (@Some A x) *)
    induction i; destruct m; simpl; auto.
  Qed.

    Lemma gleaf : forall (A : Type) (i : positive), get i (Leaf : t A) = None.
    Proof. exact gempty. Qed.

  Theorem gso:
    forall (A: Type) (i j: positive) (x: A) (m: t A),
    i <> j -> get i (set j x m) = get i m.
  Proof.
    induction i; intros; destruct j; destruct m; simpl;
(* Goal: @eq (option A) (@Some A v) (@None A) *)
       try rewrite <- (gleaf A i); auto; try apply IHi; congruence.
  Qed.

  Theorem gsspec:
    forall (A: Type) (i j: positive) (x: A) (m: t A),
    get i (set j x m) = if peq i j then Some x else get i m.
  Proof.
(* Goal: None *)
    intros.
(* Goal: @eq (option A) (@get A i (@set A j x m)) (if peq i j then @Some A x else @get A i m) *)
    destruct (peq i j); [ rewrite e; apply gss | apply gso; auto ].
  Qed.

  Theorem gsident:
    forall (A: Type) (i: positive) (m: t A) (v: A),
    get i m = Some v -> set i v m = m.
  Proof.
(* Goal: forall (A : Type) (i : positive) (m : t A) (v : A) (_ : @eq (option A) (@get A i m) (@Some A v)), @eq (t A) (@set A i v m) m *)
    induction i; intros; destruct m; simpl; simpl in H; try congruence.
(* Goal: @eq (t A) (@Node A m1 o (@set A i v m2)) (@Node A m1 o m2) *)
(* Goal: @eq (t A) (@Node A (@set A i v m1) o m2) (@Node A m1 o m2) *)
     rewrite (IHi m2 v H); congruence.
(* Goal: @eq (t A) (@Node A (@set A i v m1) o m2) (@Node A m1 o m2) *)
     rewrite (IHi m1 v H); congruence.
  Qed.

  Theorem set2:
    forall (A: Type) (i: elt) (m: t A) (v1 v2: A),
    set i v2 (set i v1 m) = set i v2 m.
  Proof.
(* Goal: forall (A : Type) (i : elt) (m : t A) (v1 v2 : A), @eq (t A) (@set A i v2 (@set A i v1 m)) (@set A i v2 m) *)
    induction i; intros; destruct m; simpl; try (rewrite IHi); auto.
  Qed.

  Lemma rleaf : forall (A : Type) (i : positive), remove i (Leaf : t A) = Leaf.
  Proof. destruct i; simpl; auto. Qed.

  Theorem grs:
    forall (A: Type) (i: positive) (m: t A), get i (remove i m) = None.
  Proof.
(* Goal: forall (A : Type) (i : positive) (m : t A), @eq (option A) (@get A i (@remove A i m)) (@None A) *)
    induction i; destruct m.
(* Goal: @eq (option A) (@get A xH (@remove A xH (@Leaf A))) (@None A) *)
(* Goal: @eq (option A) (@get A xH (@remove A xH (@Node A m1 o m2))) (@None A) *)
     simpl; auto.
(* Goal: @eq (option A) (@get A xH (@remove A xH (@Leaf A))) (@None A) *)
(* Goal: @eq (option A) (@get A xH (@remove A xH (@Node A m1 o m2))) (@None A) *)
     destruct m1; destruct o; destruct m2 as [ | ll oo rr]; simpl; auto.
(* Goal: @eq (option A) match match @remove A i (@Leaf A) with | Leaf => @Leaf A | Node t o t0 => @Node A (@Node A t o t0) (@None A) (@Leaf A) end with | Leaf => @None A | Node l o r => @get A i l end (@None A) *)
(* Goal: @eq (option A) match match @remove A i (@Node A ll oo rr) with | Leaf => @Leaf A | Node t o t0 => @Node A (@Node A t o t0) (@None A) (@Leaf A) end with | Leaf => @None A | Node l o r => @get A i l end (@None A) *)
(* Goal: @eq (option A) (@get A xH (@remove A xH (@Leaf A))) (@None A) *)
(* Goal: @eq (option A) (@get A xH (@remove A xH (@Node A m1 o m2))) (@None A) *)
      rewrite (rleaf A i); auto.
(* Goal: @eq (option A) match match @remove A i (@Node A ll oo rr) with | Leaf => @Leaf A | Node t o t0 => @Node A (@Node A t o t0) (@None A) (@Leaf A) end with | Leaf => @None A | Node l o r => @get A i l end (@None A) *)
(* Goal: @eq (option A) (@get A xH (@remove A xH (@Leaf A))) (@None A) *)
(* Goal: @eq (option A) (@get A xH (@remove A xH (@Node A m1 o m2))) (@None A) *)
      cut (get i (remove i (Node ll oo rr)) = None).
(* Goal: forall _ : @eq (option A) (@get A i (@remove A i (@Node A ll oo rr))) (@None A), @eq (option A) match match @remove A i (@Node A ll oo rr) with | Leaf => @Leaf A | Node t o t0 => @Node A (@Node A t o t0) (@None A) (@Leaf A) end with | Leaf => @None A | Node l o r => @get A i l end (@None A) *)
(* Goal: @eq (option A) (@get A i (@remove A i (@Node A ll oo rr))) (@None A) *)
(* Goal: @eq (option A) (@get A xH (@remove A xH (@Leaf A))) (@None A) *)
(* Goal: @eq (option A) (@get A xH (@remove A xH (@Node A m1 o m2))) (@None A) *)
        destruct (remove i (Node ll oo rr)); auto; apply IHi.
(* Goal: @eq (option A) (@get A i (@remove A i (@Node A ll oo rr))) (@None A) *)
(* Goal: @eq (option A) (@get A xH (@remove A xH (@Leaf A))) (@None A) *)
(* Goal: @eq (option A) (@get A xH (@remove A xH (@Node A m1 o m2))) (@None A) *)
        apply IHi.
(* Goal: @eq (option A) (@get A xH (@remove A xH (@Leaf A))) (@None A) *)
(* Goal: @eq (option A) (@get A xH (@remove A xH (@Node A m1 o m2))) (@None A) *)
     simpl; auto.
(* Goal: @eq (option A) (@get A xH (@remove A xH (@Leaf A))) (@None A) *)
(* Goal: @eq (option A) (@get A xH (@remove A xH (@Node A m1 o m2))) (@None A) *)
     destruct m1 as [ | ll oo rr]; destruct o; destruct m2; simpl; auto.
(* Goal: @eq (option A) match match @remove A i (@Leaf A) with | Leaf => @Leaf A | Node t o t0 => @Node A (@Node A t o t0) (@None A) (@Leaf A) end with | Leaf => @None A | Node l o r => @get A i l end (@None A) *)
(* Goal: @eq (option A) match match @remove A i (@Node A ll oo rr) with | Leaf => @Leaf A | Node t o t0 => @Node A (@Node A t o t0) (@None A) (@Leaf A) end with | Leaf => @None A | Node l o r => @get A i l end (@None A) *)
(* Goal: @eq (option A) (@get A xH (@remove A xH (@Leaf A))) (@None A) *)
(* Goal: @eq (option A) (@get A xH (@remove A xH (@Node A m1 o m2))) (@None A) *)
      rewrite (rleaf A i); auto.
(* Goal: @eq (option A) match match @remove A i (@Node A ll oo rr) with | Leaf => @Leaf A | Node t o t0 => @Node A (@Node A t o t0) (@None A) (@Leaf A) end with | Leaf => @None A | Node l o r => @get A i l end (@None A) *)
(* Goal: @eq (option A) (@get A xH (@remove A xH (@Leaf A))) (@None A) *)
(* Goal: @eq (option A) (@get A xH (@remove A xH (@Node A m1 o m2))) (@None A) *)
      cut (get i (remove i (Node ll oo rr)) = None).
(* Goal: forall _ : @eq (option A) (@get A i (@remove A i (@Node A ll oo rr))) (@None A), @eq (option A) match match @remove A i (@Node A ll oo rr) with | Leaf => @Leaf A | Node t o t0 => @Node A (@Node A t o t0) (@None A) (@Leaf A) end with | Leaf => @None A | Node l o r => @get A i l end (@None A) *)
(* Goal: @eq (option A) (@get A i (@remove A i (@Node A ll oo rr))) (@None A) *)
(* Goal: @eq (option A) (@get A xH (@remove A xH (@Leaf A))) (@None A) *)
(* Goal: @eq (option A) (@get A xH (@remove A xH (@Node A m1 o m2))) (@None A) *)
        destruct (remove i (Node ll oo rr)); auto; apply IHi.
(* Goal: @eq (option A) (@get A i (@remove A i (@Node A ll oo rr))) (@None A) *)
(* Goal: @eq (option A) (@get A xH (@remove A xH (@Leaf A))) (@None A) *)
(* Goal: @eq (option A) (@get A xH (@remove A xH (@Node A m1 o m2))) (@None A) *)
        apply IHi.
(* Goal: @eq (option A) (@get A xH (@remove A xH (@Leaf A))) (@None A) *)
(* Goal: @eq (option A) (@get A xH (@remove A xH (@Node A m1 o m2))) (@None A) *)
     simpl; auto.
(* Goal: @eq (option A) (@get A xH (@remove A xH (@Leaf A))) (@None A) *)
(* Goal: @eq (option A) (@get A xH (@remove A xH (@Node A m1 o m2))) (@None A) *)
     destruct m1; destruct m2; simpl; auto.
  Qed.

  Theorem gro:
    forall (A: Type) (i j: positive) (m: t A),
    i <> j -> get i (remove j m) = get i m.
  Proof.
    induction i; intros; destruct j; destruct m;
        try rewrite (rleaf A (xI j));
        try rewrite (rleaf A (xO j));
        try rewrite (rleaf A 1); auto;
        destruct m1; destruct o; destruct m2;
        simpl;
        try apply IHi; try congruence;
        try rewrite (rleaf A j); auto;
(* Goal: None *)
        try rewrite (gleaf A i); auto.
     cut (get i (remove j (Node m2_1 o m2_2)) = get i (Node m2_1 o m2_2));
        [ destruct (remove j (Node m2_1 o m2_2)); try rewrite (gleaf A i); auto
        | apply IHi; congruence ].
     destruct (remove j (Node m1_1 o0 m1_2)); simpl; try rewrite (gleaf A i);
(* Goal: None *)
        auto.
     destruct (remove j (Node m2_1 o m2_2)); simpl; try rewrite (gleaf A i);
(* Goal: None *)
        auto.
     cut (get i (remove j (Node m1_1 o0 m1_2)) = get i (Node m1_1 o0 m1_2));
        [ destruct (remove j (Node m1_1 o0 m1_2)); try rewrite (gleaf A i); auto
        | apply IHi; congruence ].
     destruct (remove j (Node m2_1 o m2_2)); simpl; try rewrite (gleaf A i);
(* Goal: None *)
        auto.
     destruct (remove j (Node m1_1 o0 m1_2)); simpl; try rewrite (gleaf A i);
(* Goal: None *)
        auto.
  Qed.

  Theorem grspec:
    forall (A: Type) (i j: elt) (m: t A),
    get i (remove j m) = if elt_eq i j then None else get i m.
  Proof.
(* Goal: None *)
    intros. destruct (elt_eq i j). subst j. apply grs. apply gro; auto.
  Qed.

  Section BOOLEAN_EQUALITY.

    Variable A: Type.
    Variable beqA: A -> A -> bool.

    Fixpoint bempty (m: t A) : bool :=
      match m with
      | Leaf => true
      | Node l None r => bempty l && bempty r
      | Node l (Some _) r => false
      end.

(* Goal: None *)
(* Goal: None *)
    Fixpoint beq (m1 m2: t A) {struct m1} : bool :=
      match m1, m2 with
      | Leaf, _ => bempty m2
      | _, Leaf => bempty m1
      | Node l1 o1 r1, Node l2 o2 r2 =>
          match o1, o2 with
          | None, None => true
          | Some y1, Some y2 => beqA y1 y2
          | _, _ => false
          end
          && beq l1 l2 && beq r1 r2
      end.

    Lemma bempty_correct:
      forall m, bempty m = true <-> (forall x, get x m = None).
    Proof.
(* Goal: forall m : t A, iff (@eq bool (bempty m) true) (forall x : positive, @eq (option A) (@get A x m) (@None A)) *)
      induction m; simpl.
(* Goal: None *)
      split; intros. apply gleaf. auto.
(* Goal: None *)
      destruct o; split; intros.
(* Goal: @eq (option A) (@Some A v) (@None A) *)
      congruence.
(* Goal: @eq (option A) (@Some A v) (@None A) *)
      generalize (H xH); simpl; congruence.
(* Goal: match @get A x (@Node A m1_1 o m1_2) with | Some y1 => match @get A x (@Node A m2_1 o0 m2_2) with | Some y2 => @eq bool (beqA y1 y2) true | None => False end | None => match @get A x (@Node A m2_1 o0 m2_2) with | Some a => False | None => True end end *)
      destruct (andb_prop _ _ H). rewrite IHm1 in H0. rewrite IHm2 in H1.
(* Goal: @eq (option A) (@get A xH (@remove A xH (@Leaf A))) (@None A) *)
(* Goal: @eq (option A) (@get A xH (@remove A xH (@Node A m1 o m2))) (@None A) *)
      destruct x; simpl; auto.
(* Goal: @eq bool (andb (bempty m1) (bempty m2)) true *)
      apply andb_true_intro; split.
(* Goal: @list_forall2 (prod positive A) (prod positive B) (fun (i_x : prod positive A) (i_y : prod positive B) => and (@eq positive (@fst positive A i_x) (@fst positive B i_y)) (R (@snd positive A i_x) (@snd positive B i_y))) (@xelements A m1 (xO p) (@nil (prod positive A))) (@xelements B n1 (xO p) (@nil (prod positive B))) *)
(* Goal: @list_forall2 (prod positive A) (prod positive B) (fun (i_x : prod positive A) (i_y : prod positive B) => and (@eq positive (@fst positive A i_x) (@fst positive B i_y)) (R (@snd positive A i_x) (@snd positive B i_y))) match o with | Some v => @cons (prod positive A) (@pair positive A (prev p) v) (@nil (prod positive A)) | None => @nil (prod positive A) end match o' with | Some v => @cons (prod positive B) (@pair positive B (prev p) v) (@nil (prod positive B)) | None => @nil (prod positive B) end *)
(* Goal: @list_forall2 (prod positive A) (prod positive B) (fun (i_x : prod positive A) (i_y : prod positive B) => and (@eq positive (@fst positive A i_x) (@fst positive B i_y)) (R (@snd positive A i_x) (@snd positive B i_y))) (@xelements A m2 (xI p) (@nil (prod positive A))) (@xelements B n2 (xI p) (@nil (prod positive B))) *)
      apply IHm1. intros; apply (H (xO x)).
(* Goal: @list_forall2 (prod positive A) (prod positive B) (fun (i_x : prod positive A) (i_y : prod positive B) => and (@eq positive (@fst positive A i_x) (@fst positive B i_y)) (R (@snd positive A i_x) (@snd positive B i_y))) (@xelements A m2 (xI p) (@nil (prod positive A))) (@xelements B n2 (xI p) (@nil (prod positive B))) *)
      apply IHm2. intros; apply (H (xI x)).
    Qed.

    Lemma beq_correct:
      forall m1 m2,
      beq m1 m2 = true <->
      (forall (x: elt),
       match get x m1, get x m2 with
       | None, None => True
       | Some y1, Some y2 => beqA y1 y2 = true
       | _, _ => False
       end).
    Proof.
(* Goal: None *)
      induction m1; intros.
(* Goal: None *)
    - simpl. rewrite bempty_correct. split; intros.
(* Goal: None *)
      rewrite gleaf. rewrite H. auto.
(* Goal: None *)
      generalize (H x). rewrite gleaf. destruct (get x m2); tauto.
    - destruct m2.
(* Goal: None *)
      + unfold beq. rewrite bempty_correct. split; intros.
(* Goal: None *)
        rewrite H. rewrite gleaf. auto.
(* Goal: None *)
        generalize (H x). rewrite gleaf. destruct (get x (Node m1_1 o m1_2)); tauto.
(* Goal: None *)
      + simpl. split; intros.
(* Goal: match @get A x (@Node A m1_1 o m1_2) with | Some y1 => match @get A x (@Node A m2_1 o0 m2_2) with | Some y2 => @eq bool (beqA y1 y2) true | None => False end | None => match @get A x (@Node A m2_1 o0 m2_2) with | Some a => False | None => True end end *)
        * destruct (andb_prop _ _ H). destruct (andb_prop _ _ H0).
(* Goal: match @get A x (@Node A m1_1 o m1_2) with | Some y1 => match @get A x (@Node A m2_1 o0 m2_2) with | Some y2 => @eq bool (beqA y1 y2) true | None => False end | None => match @get A x (@Node A m2_1 o0 m2_2) with | Some a => False | None => True end end *)
          rewrite IHm1_1 in H3. rewrite IHm1_2 in H1.
(* Goal: None *)
          destruct x; simpl. apply H1. apply H3.
(* Goal: @eq (option A) (@Some A v) (@None A) *)
          destruct o; destruct o0; auto || congruence.
        * apply andb_true_intro. split. apply andb_true_intro. split.
(* Goal: None *)
          generalize (H xH); simpl. destruct o; destruct o0; tauto.
(* Goal: forall x : elt, match @get A x m1_1 with | Some y1 => match @get A x m2_1 with | Some y2 => @eq bool (beqA y1 y2) true | None => False end | None => match @get A x m2_1 with | Some a => False | None => True end end *)
(* Goal: @eq bool (beq m1_2 m2_2) true *)
          apply IHm1_1. intros; apply (H (xO x)).
(* Goal: forall x : elt, match @get A x m1_2 with | Some y1 => match @get A x m2_2 with | Some y2 => @eq bool (beqA y1 y2) true | None => False end | None => match @get A x m2_2 with | Some a => False | None => True end end *)
          apply IHm1_2. intros; apply (H (xI x)).
    Qed.

  End BOOLEAN_EQUALITY.

(* Goal: None *)
(* Goal: None *)
  Fixpoint prev_append (i j: positive) {struct i} : positive :=
    match i with
      | xH => j
      | xI i' => prev_append i' (xI j)
      | xO i' => prev_append i' (xO j)
    end.

  Definition prev (i: positive) : positive :=
    prev_append i xH.

  Lemma prev_append_prev i j:
    prev (prev_append i j) = prev_append j i.
  Proof.
    revert j. unfold prev.
    induction i as [i IH|i IH|]. 3: reflexivity.
(* Goal: None *)
    intros j. simpl. rewrite IH. reflexivity.
(* Goal: None *)
    intros j. simpl. rewrite IH. reflexivity.
  Qed.

  Lemma prev_involutive i :
    prev (prev i) = i.
  Proof (prev_append_prev i xH).

  Lemma prev_append_inj i j j' :
    prev_append i j = prev_append i j' -> j = j'.
  Proof.
(* Goal: forall _ : @eq positive (prev_append i j) (prev_append i j'), @eq positive j j' *)
    revert j j'.
    induction i as [i Hi|i Hi|]; intros j j' H; auto;
(* Goal: @eq (option A) (@Some A v) (@None A) *)
    specialize (Hi _ _ H); congruence.
  Qed.

    Fixpoint xmap (A B : Type) (f : positive -> A -> B) (m : t A) (i : positive)
(* Goal: None *)
(* Goal: None *)
             {struct m} : t B :=
      match m with
      | Leaf => Leaf
      | Node l o r => Node (xmap f l (xO i))
                           (match o with None => None | Some x => Some (f (prev i) x) end)
                           (xmap f r (xI i))
      end.

  Definition map (A B : Type) (f : positive -> A -> B) m := xmap f m xH.

    Lemma xgmap:
      forall (A B: Type) (f: positive -> A -> B) (i j : positive) (m: t A),
      get i (xmap f m j) = option_map (f (prev (prev_append i j))) (get i m).
    Proof.
(* Goal: @eq (option A) (@get A xH (@remove A xH (@Leaf A))) (@None A) *)
(* Goal: @eq (option A) (@get A xH (@remove A xH (@Node A m1 o m2))) (@None A) *)
      induction i; intros; destruct m; simpl; auto.
    Qed.

  Theorem gmap:
    forall (A B: Type) (f: positive -> A -> B) (i: positive) (m: t A),
    get i (map f m) = option_map (f i) (get i m).
  Proof.
(* Goal: forall (A B : Type) (f : forall (_ : positive) (_ : A), B) (i : positive) (m : t A), @eq (option B) (@get B i (@map A B f m)) (@option_map A B (f i) (@get A i m)) *)
    intros A B f i m.
(* Goal: @eq (option B) (@get B i (@map A B f m)) (@option_map A B (f i) (@get A i m)) *)
    unfold map.
(* Goal: @eq (option B) (@get B i (@xmap A B f m xH)) (@option_map A B (f i) (@get A i m)) *)
    rewrite xgmap. repeat f_equal. exact (prev_involutive i).
  Qed.

  Fixpoint map1 (A B: Type) (f: A -> B) (m: t A) {struct m} : t B :=
    match m with
    | Leaf => Leaf
    | Node l o r => Node (map1 f l) (option_map f o) (map1 f r)
    end.

  Theorem gmap1:
    forall (A B: Type) (f: A -> B) (i: elt) (m: t A),
    get i (map1 f m) = option_map f (get i m).
  Proof.
(* Goal: @eq (option A) (@get A xH (@remove A xH (@Leaf A))) (@None A) *)
(* Goal: @eq (option A) (@get A xH (@remove A xH (@Node A m1 o m2))) (@None A) *)
    induction i; intros; destruct m; simpl; auto.
  Qed.

  Definition Node' (A: Type) (l: t A) (x: option A) (r: t A): t A :=
    match l, x, r with
    | Leaf, None, Leaf => Leaf
    | _, _, _ => Node l x r
    end.

  Lemma gnode':
    forall (A: Type) (l r: t A) (x: option A) (i: positive),
    get i (Node' l x r) = get i (Node l x r).
  Proof.
(* Goal: None *)
    intros. unfold Node'.
(* Goal: None *)
    destruct l; destruct x; destruct r; auto.
(* Goal: None *)
    destruct i; simpl; auto; rewrite gleaf; auto.
  Qed.

  Fixpoint filter1 (A: Type) (pred: A -> bool) (m: t A) {struct m} : t A :=
    match m with
    | Leaf => Leaf
    | Node l o r =>
        let o' := match o with None => None | Some x => if pred x then o else None end in
        Node' (filter1 pred l) o' (filter1 pred r)
    end.

  Theorem gfilter1:
    forall (A: Type) (pred: A -> bool) (i: elt) (m: t A),
    get i (filter1 pred m) =
    match get i m with None => None | Some x => if pred x then Some x else None end.
  Proof.
(* Goal: None *)
    intros until m. revert m i. induction m; simpl; intros.
(* Goal: None *)
    rewrite gleaf; auto.
(* Goal: @eq (option C) (@get C i (@Node C (combine m1_1 m2_1) (f o o0) (combine m1_2 m2_2))) (f (@get A i (@Node A m1_1 o m1_2)) (@get B i (@Node B m2_1 o0 m2_2))) *)
    rewrite gnode'. destruct i; simpl; auto. destruct o; auto.
  Qed.

  Section COMBINE.

  Variables A B C: Type.
  Variable f: option A -> option B -> option C.
  Hypothesis f_none_none: f None None = None.

(* Goal: None *)
(* Goal: None *)
  Fixpoint xcombine_l (m : t A) {struct m} : t C :=
      match m with
      | Leaf => Leaf
      | Node l o r => Node' (xcombine_l l) (f o None) (xcombine_l r)
      end.

  Lemma xgcombine_l :
          forall (m: t A) (i : positive),
          get i (xcombine_l m) = f (get i m) None.
    Proof.
(* Goal: None *)
      induction m; intros; simpl.
(* Goal: None *)
      repeat rewrite gleaf. auto.
(* Goal: @eq (option C) (@get C i (@Node C (combine m1_1 m2_1) (f o o0) (combine m1_2 m2_2))) (f (@get A i (@Node A m1_1 o m1_2)) (@get B i (@Node B m2_1 o0 m2_2))) *)
      rewrite gnode'. destruct i; simpl; auto.
    Qed.

(* Goal: None *)
(* Goal: None *)
  Fixpoint xcombine_r (m : t B) {struct m} : t C :=
      match m with
      | Leaf => Leaf
      | Node l o r => Node' (xcombine_r l) (f None o) (xcombine_r r)
      end.

  Lemma xgcombine_r :
          forall (m: t B) (i : positive),
          get i (xcombine_r m) = f None (get i m).
    Proof.
(* Goal: None *)
      induction m; intros; simpl.
(* Goal: None *)
      repeat rewrite gleaf. auto.
(* Goal: @eq (option C) (@get C i (@Node C (combine m1_1 m2_1) (f o o0) (combine m1_2 m2_2))) (f (@get A i (@Node A m1_1 o m1_2)) (@get B i (@Node B m2_1 o0 m2_2))) *)
      rewrite gnode'. destruct i; simpl; auto.
    Qed.

(* Goal: None *)
(* Goal: None *)
  Fixpoint combine (m1: t A) (m2: t B) {struct m1} : t C :=
    match m1 with
    | Leaf => xcombine_r m2
    | Node l1 o1 r1 =>
        match m2 with
        | Leaf => xcombine_l m1
        | Node l2 o2 r2 => Node' (combine l1 l2) (f o1 o2) (combine r1 r2)
        end
    end.

  Theorem gcombine:
      forall (m1: t A) (m2: t B) (i: positive),
      get i (combine m1 m2) = f (get i m1) (get i m2).
  Proof.
(* Goal: None *)
    induction m1; intros; simpl.
(* Goal: @eq (option C) (@get C i (@Node' C (xcombine_l m1_1) (f o (@None B)) (xcombine_l m1_2))) (f (@get A i (@Node A m1_1 o m1_2)) (@get B i (@Leaf B))) *)
(* Goal: @eq (option C) (@get C i (@Node' C (combine m1_1 m2_1) (f o o0) (combine m1_2 m2_2))) (f (@get A i (@Node A m1_1 o m1_2)) (@get B i (@Node B m2_1 o0 m2_2))) *)
    rewrite gleaf. apply xgcombine_r.
(* Goal: None *)
    destruct m2; simpl.
(* Goal: None *)
    rewrite gleaf. rewrite <- xgcombine_l. auto.
(* Goal: @eq (option C) (@get C i (@Node C (combine m1_1 m2_1) (f o o0) (combine m1_2 m2_2))) (f (@get A i (@Node A m1_1 o m1_2)) (@get B i (@Node B m2_1 o0 m2_2))) *)
    repeat rewrite gnode'. destruct i; simpl; auto.
  Qed.

  End COMBINE.

  Lemma xcombine_lr :
    forall (A B: Type) (f g : option A -> option A -> option B) (m : t A),
    (forall (i j : option A), f i j = g j i) ->
    xcombine_l f m = xcombine_r g m.
    Proof.
(* Goal: @eq (option A) (@get A xH (@remove A xH (@Leaf A))) (@None A) *)
(* Goal: @eq (option A) (@get A xH (@remove A xH (@Node A m1 o m2))) (@None A) *)
      induction m; intros; simpl; auto.
(* Goal: None *)
      rewrite IHm1; auto.
(* Goal: None *)
      rewrite IHm2; auto.
(* Goal: None *)
      rewrite H; auto.
    Qed.

  Theorem combine_commut:
    forall (A B: Type) (f g: option A -> option A -> option B),
    (forall (i j: option A), f i j = g j i) ->
    forall (m1 m2: t A),
    combine f m1 m2 = combine g m2 m1.
  Proof.
(* Goal: forall (A B : Type) (f g : forall (_ : option A) (_ : option A), option B) (_ : forall i j : option A, @eq (option B) (f i j) (g j i)) (m1 m2 : t A), @eq (t B) (@combine A A B f m1 m2) (@combine A A B g m2 m1) *)
    intros A B f g EQ1.
(* Goal: forall m1 m2 : t A, @eq (t B) (@combine A A B f m1 m2) (@combine A A B g m2 m1) *)
    assert (EQ2: forall (i j: option A), g i j = f j i).
(* Goal: None *)
      intros; auto.
    induction m1; intros; destruct m2; simpl;
      try rewrite EQ1;
      repeat rewrite (xcombine_lr f g);
      repeat rewrite (xcombine_lr g f);
(* Goal: None *)
      auto.
(* Goal: @eq (t B) (@Node' B (@combine A A B f m1_1 m2_1) (g o0 o) (@combine A A B f m1_2 m2_2)) (@Node' B (@combine A A B g m2_1 m1_1) (g o0 o) (@combine A A B g m2_2 m1_2)) *)
     rewrite IHm1_1.
(* Goal: @eq (t B) (@Node' B (@combine A A B g m2_1 m1_1) (g o0 o) (@combine A A B f m1_2 m2_2)) (@Node' B (@combine A A B g m2_1 m1_1) (g o0 o) (@combine A A B g m2_2 m1_2)) *)
     rewrite IHm1_2.
(* Goal: None *)
     auto.
  Qed.

    Fixpoint xelements (A : Type) (m : t A) (i : positive)
                       (k: list (positive * A)) {struct m}
                       : list (positive * A) :=
      match m with
      | Leaf => k
      | Node l None r =>
          xelements l (xO i) (xelements r (xI i) k)
      | Node l (Some x) r =>
          xelements l (xO i)
            ((prev i, x) :: xelements r (xI i) k)
      end.

  Definition elements (A: Type) (m : t A) := xelements m xH nil.

  Remark xelements_append:
    forall A (m: t A) i k1 k2,
    xelements m i (k1 ++ k2) = xelements m i k1 ++ k2.
  Proof.
(* Goal: None *)
    induction m; intros; simpl.
(* Goal: None *)
  - auto.
(* Goal: None *)
  - destruct o; rewrite IHm2; rewrite <- IHm1; auto.
  Qed.

  Remark xelements_leaf:
    forall A i, xelements (@Leaf A) i nil = nil.
  Proof.
(* Goal: @equiv A eqA eqAeq a a *)
    intros; reflexivity.
  Qed.

  Remark xelements_node:
    forall A (m1: t A) o (m2: t A) i,
    xelements (Node m1 o m2) i nil =
       xelements m1 (xO i) nil
    ++ match o with None => nil | Some v => (prev i, v) :: nil end
    ++ xelements m2 (xI i) nil.
  Proof.
(* Goal: None *)
    intros. simpl. destruct o; simpl; rewrite <- xelements_append; auto.
  Qed.

    Lemma xelements_incl:
      forall (A: Type) (m: t A) (i : positive) k x,
      In x k -> In x (xelements m i k).
    Proof.
(* Goal: None *)
      induction m; intros; simpl.
(* Goal: None *)
      auto.
(* Goal: @In (prod positive A) x match o with | Some x => @xelements A m1 (xO i) (@cons (prod positive A) (@pair positive A (prev i) x) (@xelements A m2 (xI i) k)) | None => @xelements A m1 (xO i) (@xelements A m2 (xI i) k) end *)
      destruct o.
(* Goal: None *)
      apply IHm1. simpl; right; auto.
(* Goal: None *)
      auto.
    Qed.

    Lemma xelements_correct:
      forall (A: Type) (m: t A) (i j : positive) (v: A) k,
      get i m = Some v -> In (prev (prev_append i j), v) (xelements m j k).
    Proof.
(* Goal: None *)
      induction m; intros.
(* Goal: @eq (option A) (@Some A v) (@None A) *)
       rewrite (gleaf A i) in H; congruence.
(* Goal: @In (prod positive A) (@pair positive A (prev (prev_append i j)) v) (@xelements A (@Node A m1 o m2) j k) *)
       destruct o; destruct i; simpl; simpl in H.
(* Goal: None *)
        apply xelements_incl. right. auto.
(* Goal: None *)
        auto.
(* Goal: @equiv A eqA eqAeq a a *)
        inv H. apply xelements_incl. left. reflexivity.
(* Goal: None *)
        apply xelements_incl. auto.
(* Goal: None *)
        auto.
(* Goal: None *)
(* Goal: None *)
        inv H.
    Qed.

  Theorem elements_correct:
    forall (A: Type) (m: t A) (i: positive) (v: A),
    get i m = Some v -> In (i, v) (elements m).
  Proof.
(* Goal: forall (A : Type) (m : t A) (i : positive) (v : A) (_ : @In (prod positive A) (@pair positive A i v) (@xelements A m xH (@nil (prod positive A)))), @eq (option A) (@get A i m) (@Some A v) *)
    intros A m i v H.
(* Goal: @In (prod positive A) (@pair positive A i v) (@elements A m) *)
    generalize (xelements_correct m i xH nil H). rewrite prev_append_prev. exact id.
  Qed.

  Lemma in_xelements:
    forall (A: Type) (m: t A) (i k: positive) (v: A) ,
    In (k, v) (xelements m i nil) ->
    exists j, k = prev (prev_append j i) /\ get j m = Some v.
  Proof.
(* Goal: None *)
    induction m; intros.
  - rewrite xelements_leaf in H. contradiction.
  - rewrite xelements_node in H. rewrite ! in_app_iff in H. destruct H as [P | [P | P]].
(* Goal: None *)
    + exploit IHm1; eauto. intros (j & Q & R). exists (xO j); auto.
(* Goal: None *)
    + destruct o; simpl in P; intuition auto. inv H. exists xH; auto.
(* Goal: None *)
    + exploit IHm2; eauto. intros (j & Q & R). exists (xI j); auto.
  Qed.

  Theorem elements_complete:
    forall (A: Type) (m: t A) (i: positive) (v: A),
    In (i, v) (elements m) -> get i m = Some v.
  Proof.
(* Goal: None *)
    unfold elements. intros A m i v H. exploit in_xelements; eauto. intros (j & P & Q).
(* Goal: @eq (option A) (@get A i m) (@Some A v) *)
    rewrite prev_append_prev in P. change i with (prev_append 1 i) in P.
(* Goal: None *)
    exploit prev_append_inj; eauto. intros; congruence.
  Qed.

  Definition xkeys (A: Type) (m: t A) (i: positive) :=
    List.map (@fst positive A) (xelements m i nil).

  Remark xkeys_leaf:
    forall A i, xkeys (@Leaf A) i = nil.
  Proof.
(* Goal: @equiv A eqA eqAeq a a *)
    intros; reflexivity.
  Qed.

  Remark xkeys_node:
    forall A (m1: t A) o (m2: t A) i,
    xkeys (Node m1 o m2) i =
       xkeys m1 (xO i)
    ++ match o with None => nil | Some v => prev i :: nil end
    ++ xkeys m2 (xI i).
  Proof.
(* Goal: None *)
    intros. unfold xkeys. rewrite xelements_node. rewrite ! map_app. destruct o; auto.
  Qed.

  Lemma in_xkeys:
    forall (A: Type) (m: t A) (i k: positive),
    In k (xkeys m i) ->
    (exists j, k = prev (prev_append j i)).
  Proof.
(* Goal: None *)
    unfold xkeys; intros.
    apply (list_in_map_inv) in H. destruct H as ((j, v) & -> & H).
(* Goal: None *)
    exploit in_xelements; eauto. intros (k & P & Q). exists k; auto.
  Qed.

  Lemma xelements_keys_norepet:
    forall (A: Type) (m: t A) (i: positive),
    list_norepet (xkeys m i).
  Proof.
(* Goal: None *)
    induction m; intros.
  - rewrite xkeys_leaf; constructor.
  - assert (NOTIN1: ~ In (prev i) (xkeys m1 (xO i))).
(* Goal: None *)
    { red; intros. exploit in_xkeys; eauto. intros (j & EQ).
      rewrite prev_append_prev in EQ. simpl in EQ. apply prev_append_inj in EQ. discriminate. }
    assert (NOTIN2: ~ In (prev i) (xkeys m2 (xI i))).
(* Goal: None *)
    { red; intros. exploit in_xkeys; eauto. intros (j & EQ).
      rewrite prev_append_prev in EQ. simpl in EQ. apply prev_append_inj in EQ. discriminate. }
    assert (DISJ: forall x, In x (xkeys m1 (xO i)) -> In x (xkeys m2 (xI i)) -> False).
(* Goal: None *)
    { intros. exploit in_xkeys. eexact H. intros (j1 & EQ1).
      exploit in_xkeys. eexact H0. intros (j2 & EQ2).
      rewrite prev_append_prev in *. simpl in *. rewrite EQ2 in EQ1. apply prev_append_inj in EQ1. discriminate. }
(* Goal: None *)
    rewrite xkeys_node. apply list_norepet_append. auto.
(* Goal: @eq (option A) (@get A xH (@remove A xH (@Leaf A))) (@None A) *)
(* Goal: @eq (option A) (@get A xH (@remove A xH (@Node A m1 o m2))) (@None A) *)
    destruct o; simpl; auto. constructor; auto.
(* Goal: None *)
    red; intros. red; intros; subst y. destruct o; simpl in H0.
(* Goal: None *)
    destruct H0. subst x. tauto. eauto. eauto.
  Qed.

  Theorem elements_keys_norepet:
    forall (A: Type) (m: t A),
    list_norepet (List.map (@fst elt A) (elements m)).
  Proof.
(* Goal: None *)
    intros. apply (xelements_keys_norepet m xH).
  Qed.

  Remark xelements_empty:
    forall (A: Type) (m: t A) i, (forall i, get i m = None) -> xelements m i nil = nil.
  Proof.
(* Goal: None *)
    induction m; intros.
(* Goal: None *)
    auto.
(* Goal: None *)
    rewrite xelements_node. rewrite IHm1, IHm2. destruct o; auto.
(* Goal: @eq (option A) (@Some A v) (@None A) *)
    generalize (H xH); simpl; congruence.
(* Goal: None *)
    intros. apply (H (xI i0)).
(* Goal: None *)
    intros. apply (H (xO i0)).
  Qed.

  Theorem elements_canonical_order':
    forall (A B: Type) (R: A -> B -> Prop) (m: t A) (n: t B),
    (forall i, option_rel R (get i m) (get i n)) ->
    list_forall2
      (fun i_x i_y => fst i_x = fst i_y /\ R (snd i_x) (snd i_y))
      (elements m) (elements n).
  Proof.
(* Goal: forall _ : forall i : positive, @option_rel A B R (@get A i m) (@get B i n), @list_forall2 (prod positive A) (prod positive B) (fun (i_x : prod positive A) (i_y : prod positive B) => and (@eq positive (@fst positive A i_x) (@fst positive B i_y)) (R (@snd positive A i_x) (@snd positive B i_y))) (@elements A m) (@elements B n) *)
    intros until n. unfold elements. generalize 1%positive. revert m n.
(* Goal: None *)
    induction m; intros.
  - simpl. rewrite xelements_empty. constructor.
(* Goal: None *)
    intros. specialize (H i). rewrite gempty in H. inv H; auto.
  - destruct n as [ | n1 o' n2 ].
  + rewrite (xelements_empty (Node m1 o m2)). simpl; constructor.
(* Goal: None *)
    intros. specialize (H i). rewrite gempty in H. inv H; auto.
  + rewrite ! xelements_node. repeat apply list_forall2_app.
(* Goal: None *)
    apply IHm1. intros. apply (H (xO i)).
(* Goal: @list_forall2 (prod positive A) (prod positive B) (fun (i_x : prod positive A) (i_y : prod positive B) => and (@eq positive (@fst positive A i_x) (@fst positive B i_y)) (R (@snd positive A i_x) (@snd positive B i_y))) match o with | Some v => @cons (prod positive A) (@pair positive A (prev p) v) (@nil (prod positive A)) | None => @nil (prod positive A) end match o' with | Some v => @cons (prod positive B) (@pair positive B (prev p) v) (@nil (prod positive B)) | None => @nil (prod positive B) end *)
(* Goal: @list_forall2 (prod positive A) (prod positive B) (fun (i_x : prod positive A) (i_y : prod positive B) => and (@eq positive (@fst positive A i_x) (@fst positive B i_y)) (R (@snd positive A i_x) (@snd positive B i_y))) (@xelements A m2 (xI p) (@nil (prod positive A))) (@xelements B n2 (xI p) (@nil (prod positive B))) *)
    generalize (H xH); simpl; intros OR; inv OR.
(* Goal: @option_rel A B R (@None A) (@None B) *)
    constructor.
(* Goal: None *)
    constructor. auto. constructor.
(* Goal: None *)
    apply IHm2. intros. apply (H (xI i)).
  Qed.

  Theorem elements_canonical_order:
    forall (A B: Type) (R: A -> B -> Prop) (m: t A) (n: t B),
    (forall i x, get i m = Some x -> exists y, get i n = Some y /\ R x y) ->
    (forall i y, get i n = Some y -> exists x, get i m = Some x /\ R x y) ->
    list_forall2
      (fun i_x i_y => fst i_x = fst i_y /\ R (snd i_x) (snd i_y))
      (elements m) (elements n).
  Proof.
(* Goal: None *)
    intros. apply elements_canonical_order'.
(* Goal: None *)
    intros. destruct (get i m) as [x|] eqn:GM.
(* Goal: None *)
    exploit H; eauto. intros (y & P & Q). rewrite P; constructor; auto.
(* Goal: @option_rel A B R (@None A) (@get B i n) *)
    destruct (get i n) as [y|] eqn:GN.
(* Goal: None *)
    exploit H0; eauto. intros (x & P & Q). congruence.
(* Goal: @option_rel A B R (@None A) (@None B) *)
    constructor.
  Qed.

  Theorem elements_extensional:
    forall (A: Type) (m n: t A),
    (forall i, get i m = get i n) ->
    elements m = elements n.
  Proof.
(* Goal: None *)
    intros.
(* Goal: @eq (list (prod positive A)) (@elements A m) (@elements A n) *)
    exploit (@elements_canonical_order' _ _ (fun (x y: A) => x = y) m n).
(* Goal: None *)
    intros. rewrite H. destruct (get i n); constructor; auto.
(* Goal: None *)
    induction 1. auto. destruct a1 as [a2 a3]; destruct b1 as [b2 b3]; simpl in *.
(* Goal: @eq (option A) (@Some A v) (@None A) *)
    destruct H0. congruence.
  Qed.

  Lemma xelements_remove:
    forall (A: Type) v (m: t A) i j,
    get i m = Some v ->
    exists l1 l2,
    xelements m j nil = l1 ++ (prev (prev_append i j), v) :: l2
    /\ xelements (remove i m) j nil = l1 ++ l2.
  Proof.
(* Goal: None *)
    induction m; intros.
  - rewrite gleaf in H; discriminate.
  - assert (REMOVE: xelements (remove i (Node m1 o m2)) j nil =
                    xelements (match i with
                               | xH => Node m1 None m2
                               | xO ii => Node (remove ii m1) o m2
                               | xI ii => Node m1 o (remove ii m2) end)
                              j nil).
(* Goal: None *)
(* Goal: None *)
    {
(* Goal: @eq (list (prod positive A)) (@xelements A (@remove A i (@Node A m1 o m2)) j (@nil (prod positive A))) (@xelements A match i with | xI ii => @Node A m1 o (@remove A ii m2) | xO ii => @Node A (@remove A ii m1) o m2 | xH => @Node A m1 (@None A) m2 end j (@nil (prod positive A))) *)
      destruct i; simpl remove.
(* Goal: None *)
      destruct m1; auto. destruct o; auto. destruct (remove i m2); auto.
(* Goal: None *)
      destruct o; auto. destruct m2; auto. destruct (remove i m1); auto.
(* Goal: None *)
      destruct m1; auto. destruct m2; auto.
    }
(* Goal: @ex (list (prod positive A)) (fun l1 : list (prod positive A) => @ex (list (prod positive A)) (fun l2 : list (prod positive A) => and (@eq (list (prod positive A)) (@xelements A (@Node A m1 o m2) j (@nil (prod positive A))) (@app (prod positive A) l1 (@cons (prod positive A) (@pair positive A (prev (prev_append i j)) v) l2))) (@eq (list (prod positive A)) (@xelements A (@remove A i (@Node A m1 o m2)) j (@nil (prod positive A))) (@app (prod positive A) l1 l2)))) *)
    rewrite REMOVE. destruct i; simpl in H.
    + destruct (IHm2 i (xI j) H) as (l1 & l2 & EQ & EQ').
      exists (xelements m1 (xO j) nil ++
              match o with None => nil | Some x => (prev j, x) :: nil end ++
              l1);
(* Goal: and (@eq bool a true) (@eq bool (f k v) true) *)
      exists l2; split.
(* Goal: None *)
      rewrite xelements_node, EQ, ! app_ass. auto.
(* Goal: None *)
      rewrite xelements_node, EQ', ! app_ass. auto.
    + destruct (IHm1 i (xO j) H) as (l1 & l2 & EQ & EQ').
      exists l1;
      exists (l2 ++
              match o with None => nil | Some x => (prev j, x) :: nil end ++
              xelements m2 (xI j) nil);
(* Goal: and (@eq bool a true) (@eq bool (f k v) true) *)
      split.
(* Goal: None *)
      rewrite xelements_node, EQ, ! app_ass. auto.
(* Goal: None *)
      rewrite xelements_node, EQ', ! app_ass. auto.
    + subst o. exists (xelements m1 (xO j) nil); exists (xelements m2 (xI j) nil); split.
(* Goal: None *)
      rewrite xelements_node. rewrite prev_append_prev. auto.
(* Goal: None *)
      rewrite xelements_node; auto.
  Qed.

  Theorem elements_remove:
    forall (A: Type) i v (m: t A),
    get i m = Some v ->
    exists l1 l2, elements m = l1 ++ (i,v) :: l2 /\ elements (remove i m) = l1 ++ l2.
  Proof.
(* Goal: None *)
    intros. exploit xelements_remove. eauto. instantiate (1 := xH).
(* Goal: None *)
    rewrite prev_append_prev. auto.
  Qed.

  Fixpoint xfold (A B: Type) (f: B -> positive -> A -> B)
(* Goal: None *)
(* Goal: None *)
                 (i: positive) (m: t A) (v: B) {struct m} : B :=
    match m with
    | Leaf => v
    | Node l None r =>
        let v1 := xfold f (xO i) l v in
        xfold f (xI i) r v1
    | Node l (Some x) r =>
        let v1 := xfold f (xO i) l v in
        let v2 := f v1 (prev i) x in
        xfold f (xI i) r v2
    end.

  Definition fold (A B : Type) (f: B -> positive -> A -> B) (m: t A) (v: B) :=
    xfold f xH m v.

  Lemma xfold_xelements:
    forall (A B: Type) (f: B -> positive -> A -> B) m i v l,
    List.fold_left (fun a p => f a (fst p) (snd p)) l (xfold f i m v) =
    List.fold_left (fun a p => f a (fst p) (snd p)) (xelements m i l) v.
  Proof.
(* Goal: None *)
    induction m; intros.
(* Goal: None *)
    simpl. auto.
(* Goal: None *)
    destruct o; simpl.
(* Goal: None *)
    rewrite <- IHm1. simpl. rewrite <- IHm2. auto.
(* Goal: None *)
    rewrite <- IHm1. rewrite <- IHm2. auto.
  Qed.

  Theorem fold_spec:
    forall (A B: Type) (f: B -> positive -> A -> B) (v: B) (m: t A),
    fold f m v =
    List.fold_left (fun a p => f a (fst p) (snd p)) (elements m) v.
  Proof.
(* Goal: None *)
    intros. unfold fold, elements. rewrite <- xfold_xelements. auto.
  Qed.

  Fixpoint fold1 (A B: Type) (f: B -> A -> B) (m: t A) (v: B) {struct m} : B :=
    match m with
    | Leaf => v
    | Node l None r =>
        let v1 := fold1 f l v in
        fold1 f r v1
    | Node l (Some x) r =>
        let v1 := fold1 f l v in
        let v2 := f v1 x in
        fold1 f r v2
    end.

  Lemma fold1_xelements:
    forall (A B: Type) (f: B -> A -> B) m i v l,
    List.fold_left (fun a p => f a (snd p)) l (fold1 f m v) =
    List.fold_left (fun a p => f a (snd p)) (xelements m i l) v.
  Proof.
(* Goal: None *)
    induction m; intros.
(* Goal: None *)
    simpl. auto.
(* Goal: None *)
    destruct o; simpl.
(* Goal: None *)
    rewrite <- IHm1. simpl. rewrite <- IHm2. auto.
(* Goal: None *)
    rewrite <- IHm1. rewrite <- IHm2. auto.
  Qed.

  Theorem fold1_spec:
    forall (A B: Type) (f: B -> A -> B) (v: B) (m: t A),
    fold1 f m v =
    List.fold_left (fun a p => f a (snd p)) (elements m) v.
  Proof.
(* Goal: None *)
    intros. apply fold1_xelements with (l := @nil (positive * A)).
  Qed.

End PTree.

(** * An implementation of maps over type [positive] *)

Module PMap <: MAP.
  Definition elt := positive.
  Definition elt_eq := peq.

  Definition t (A : Type) : Type := (A * PTree.t A)%type.

  Definition init (A : Type) (x : A) :=
    (x, PTree.empty A).

  Definition get (A : Type) (i : positive) (m : t A) :=
    match PTree.get i (snd m) with
    | Some x => x
    | None => fst m
    end.

  Definition set (A : Type) (i : positive) (x : A) (m : t A) :=
    (fst m, PTree.set i x (snd m)).

  Theorem gi:
    forall (A: Type) (i: positive) (x: A), get i (init x) = x.
  Proof.
(* Goal: None *)
    intros. unfold init. unfold get. simpl. rewrite PTree.gempty. auto.
  Qed.

  Theorem gss:
    forall (A: Type) (i: positive) (x: A) (m: t A), get i (set i x m) = x.
  Proof.
(* Goal: None *)
    intros. unfold get. unfold set. simpl. rewrite PTree.gss. auto.
  Qed.

  Theorem gso:
    forall (A: Type) (i j: positive) (x: A) (m: t A),
    i <> j -> get i (set j x m) = get i m.
  Proof.
(* Goal: None *)
    intros. unfold get. unfold set. simpl. rewrite PTree.gso; auto.
  Qed.

  Theorem gsspec:
    forall (A: Type) (i j: positive) (x: A) (m: t A),
    get i (set j x m) = if peq i j then x else get i m.
  Proof.
(* Goal: None *)
    intros. destruct (peq i j).
(* Goal: None *)
     rewrite e. apply gss. auto.
(* Goal: None *)
     apply gso. auto.
  Qed.

  Theorem gsident:
    forall (A: Type) (i j: positive) (m: t A),
    get j (set i (get i m) m) = get j m.
  Proof.
(* Goal: None *)
    intros. destruct (peq i j).
(* Goal: None *)
     rewrite e. rewrite gss. auto.
(* Goal: None *)
     rewrite gso; auto.
  Qed.

  Definition map (A B : Type) (f : A -> B) (m : t A) : t B :=
    (f (fst m), PTree.map1 f (snd m)).

  Theorem gmap:
    forall (A B: Type) (f: A -> B) (i: positive) (m: t A),
    get i (map f m) = f(get i m).
  Proof.
(* Goal: None *)
    intros. unfold map. unfold get. simpl. rewrite PTree.gmap1.
(* Goal: None *)
    unfold option_map. destruct (PTree.get i (snd m)); auto.
  Qed.

  Theorem set2:
    forall (A: Type) (i: elt) (x y: A) (m: t A),
    set i y (set i x m) = set i y m.
  Proof.
(* Goal: None *)
    intros. unfold set. simpl. decEq. apply PTree.set2.
  Qed.

End PMap.

(** * An implementation of maps over any type that injects into type [positive] *)

Module Type INDEXED_TYPE.
  Parameter t: Type.
  Parameter index: t -> positive.
  Axiom index_inj: forall (x y: t), index x = index y -> x = y.
  Parameter eq: forall (x y: t), {x = y} + {x <> y}.
End INDEXED_TYPE.

Module IMap(X: INDEXED_TYPE).

  Definition elt := X.t.
  Definition elt_eq := X.eq.
  Definition t : Type -> Type := PMap.t.
  Definition init (A: Type) (x: A) := PMap.init x.
  Definition get (A: Type) (i: X.t) (m: t A) := PMap.get (X.index i) m.
  Definition set (A: Type) (i: X.t) (v: A) (m: t A) := PMap.set (X.index i) v m.
  Definition map (A B: Type) (f: A -> B) (m: t A) : t B := PMap.map f m.

  Lemma gi:
    forall (A: Type) (x: A) (i: X.t), get i (init x) = x.
  Proof.
(* Goal: None *)
    intros. unfold get, init. apply PMap.gi.
  Qed.

  Lemma gss:
    forall (A: Type) (i: X.t) (x: A) (m: t A), get i (set i x m) = x.
  Proof.
(* Goal: None *)
    intros. unfold get, set. apply PMap.gss.
  Qed.

  Lemma gso:
    forall (A: Type) (i j: X.t) (x: A) (m: t A),
    i <> j -> get i (set j x m) = get i m.
  Proof.
(* Goal: None *)
    intros. unfold get, set. apply PMap.gso.
(* Goal: None *)
    red. intro. apply H. apply X.index_inj; auto.
  Qed.

  Lemma gsspec:
    forall (A: Type) (i j: X.t) (x: A) (m: t A),
    get i (set j x m) = if X.eq i j then x else get i m.
  Proof.
(* Goal: None *)
    intros. unfold get, set.
    rewrite PMap.gsspec.
    case (X.eq i j); intro.
(* Goal: @eq (option A) (@get A i (@remove A j m)) (@None A) *)
(* Goal: @eq (option A) (@get A i (@remove A j m)) (@get A i m) *)
    subst j. rewrite peq_true. reflexivity.
(* Goal: @equiv A eqA eqAeq a a *)
    rewrite peq_false. reflexivity.
(* Goal: None *)
    red; intro. elim n. apply X.index_inj; auto.
  Qed.

  Lemma gmap:
    forall (A B: Type) (f: A -> B) (i: X.t) (m: t A),
    get i (map f m) = f(get i m).
  Proof.
(* Goal: None *)
    intros. unfold map, get. apply PMap.gmap.
  Qed.

  Lemma set2:
    forall (A: Type) (i: elt) (x y: A) (m: t A),
    set i y (set i x m) = set i y m.
  Proof.
(* Goal: None *)
    intros. unfold set. apply PMap.set2.
  Qed.

End IMap.

Module ZIndexed.
  Definition t := Z.
  Definition index (z: Z): positive :=
    match z with
    | Z0 => xH
    | Zpos p => xO p
    | Zneg p => xI p
    end.
  Lemma index_inj: forall (x y: Z), index x = index y -> x = y.
  Proof.
    unfold index; destruct x; destruct y; intros;
(* Goal: @equiv A eqA eqAeq a a *)
    try discriminate; try reflexivity.
(* Goal: @eq (option A) (@Some A v) (@None A) *)
    congruence.
(* Goal: @eq (option A) (@Some A v) (@None A) *)
    congruence.
  Qed.
  Definition eq := zeq.
End ZIndexed.

Module ZMap := IMap(ZIndexed).

Module NIndexed.
  Definition t := N.
  Definition index (n: N): positive :=
    match n with
    | N0 => xH
    | Npos p => xO p
    end.
  Lemma index_inj: forall (x y: N), index x = index y -> x = y.
  Proof.
    unfold index; destruct x; destruct y; intros;
(* Goal: @equiv A eqA eqAeq a a *)
    try discriminate; try reflexivity.
(* Goal: @eq (option A) (@Some A v) (@None A) *)
    congruence.
  Qed.
  Lemma eq: forall (x y: N), {x = y} + {x <> y}.
  Proof.
(* Goal: forall x y : N, sumbool (@eq N x y) (not (@eq N x y)) *)
    decide equality. apply peq.
  Qed.
End NIndexed.

Module NMap := IMap(NIndexed).

(** * An implementation of maps over any type with decidable equality *)

Module Type EQUALITY_TYPE.
  Parameter t: Type.
  Parameter eq: forall (x y: t), {x = y} + {x <> y}.
End EQUALITY_TYPE.

Module EMap(X: EQUALITY_TYPE) <: MAP.

  Definition elt := X.t.
  Definition elt_eq := X.eq.
  Definition t (A: Type) := X.t -> A.
  Definition init (A: Type) (v: A) := fun (_: X.t) => v.
  Definition get (A: Type) (x: X.t) (m: t A) := m x.
  Definition set (A: Type) (x: X.t) (v: A) (m: t A) :=
    fun (y: X.t) => if X.eq y x then v else m y.
  Lemma gi:
    forall (A: Type) (i: elt) (x: A), init x i = x.
  Proof.
(* Goal: None *)
    intros. reflexivity.
  Qed.
  Lemma gss:
    forall (A: Type) (i: elt) (x: A) (m: t A), (set i x m) i = x.
  Proof.
(* Goal: None *)
    intros. unfold set. case (X.eq i i); intro.
(* Goal: None *)
    reflexivity. tauto.
  Qed.
  Lemma gso:
    forall (A: Type) (i j: elt) (x: A) (m: t A),
    i <> j -> (set j x m) i = m i.
  Proof.
(* Goal: None *)
    intros. unfold set. case (X.eq i j); intro.
(* Goal: @eq (option A) (@Some A v) (@None A) *)
    congruence. reflexivity.
  Qed.
  Lemma gsspec:
    forall (A: Type) (i j: elt) (x: A) (m: t A),
    get i (set j x m) = if elt_eq i j then x else get i m.
  Proof.
(* Goal: None *)
    intros. unfold get, set, elt_eq. reflexivity.
  Qed.
  Lemma gsident:
    forall (A: Type) (i j: elt) (m: t A), get j (set i (get i m) m) = get j m.
  Proof.
(* Goal: None *)
    intros. unfold get, set. case (X.eq j i); intro.
(* Goal: @eq (option A) (@Some A v) (@None A) *)
    congruence. reflexivity.
  Qed.
  Definition map (A B: Type) (f: A -> B) (m: t A) :=
    fun (x: X.t) => f(m x).
  Lemma gmap:
    forall (A B: Type) (f: A -> B) (i: elt) (m: t A),
    get i (map f m) = f(get i m).
  Proof.
(* Goal: None *)
    intros. unfold get, map. reflexivity.
  Qed.
End EMap.

(** * A partial implementation of trees over any type that injects into type [positive] *)

Module ITree(X: INDEXED_TYPE).

  Definition elt := X.t.
  Definition elt_eq := X.eq.
  Definition t : Type -> Type := PTree.t.

  Definition empty (A: Type): t A := PTree.empty A.
  Definition get (A: Type) (k: elt) (m: t A): option A := PTree.get (X.index k) m.
  Definition set (A: Type) (k: elt) (v: A) (m: t A): t A := PTree.set (X.index k) v m.
  Definition remove (A: Type) (k: elt) (m: t A): t A := PTree.remove (X.index k) m.

  Theorem gempty:
    forall (A: Type) (i: elt), get i (empty A) = None.
  Proof.
(* Goal: None *)
    intros. apply PTree.gempty.
  Qed.
  Theorem gss:
    forall (A: Type) (i: elt) (x: A) (m: t A), get i (set i x m) = Some x.
  Proof.
(* Goal: None *)
    intros. apply PTree.gss.
  Qed.
  Theorem gso:
    forall (A: Type) (i j: elt) (x: A) (m: t A),
    i <> j -> get i (set j x m) = get i m.
  Proof.
(* Goal: None *)
    intros. apply PTree.gso. red; intros; elim H; apply X.index_inj; auto.
  Qed.
  Theorem gsspec:
    forall (A: Type) (i j: elt) (x: A) (m: t A),
    get i (set j x m) = if elt_eq i j then Some x else get i m.
  Proof.
(* Goal: None *)
    intros. destruct (elt_eq i j). subst j; apply gss. apply gso; auto.
  Qed.
  Theorem grs:
    forall (A: Type) (i: elt) (m: t A), get i (remove i m) = None.
  Proof.
(* Goal: None *)
    intros. apply PTree.grs.
  Qed.
  Theorem gro:
    forall (A: Type) (i j: elt) (m: t A),
    i <> j -> get i (remove j m) = get i m.
  Proof.
(* Goal: None *)
    intros. apply PTree.gro. red; intros; elim H; apply X.index_inj; auto.
  Qed.
  Theorem grspec:
    forall (A: Type) (i j: elt) (m: t A),
    get i (remove j m) = if elt_eq i j then None else get i m.
  Proof.
(* Goal: None *)
    intros. destruct (elt_eq i j). subst j; apply grs. apply gro; auto.
  Qed.

  Definition beq: forall (A: Type), (A -> A -> bool) -> t A -> t A -> bool := PTree.beq.
  Theorem beq_sound:
    forall (A: Type) (eqA: A -> A -> bool) (t1 t2: t A),
    beq eqA t1 t2 = true ->
    forall (x: elt),
     match get x t1, get x t2 with
     | None, None => True
     | Some y1, Some y2 => eqA y1 y2 = true
     | _, _ => False
    end.
  Proof.
(* Goal: None *)
    unfold beq, get. intros. rewrite PTree.beq_correct in H. apply H.
  Qed.

  Definition combine: forall (A B C: Type), (option A -> option B -> option C) -> t A -> t B -> t C := PTree.combine.
  Theorem gcombine:
    forall (A B C: Type) (f: option A -> option B -> option C),
    f None None = None ->
    forall (m1: t A) (m2: t B) (i: elt),
    get i (combine f m1 m2) = f (get i m1) (get i m2).
  Proof.
(* Goal: None *)
    intros. apply PTree.gcombine. auto.
  Qed.
End ITree.

Module ZTree := ITree(ZIndexed).

(** * Additional properties over trees *)

Module Tree_Properties(T: TREE).

(** An induction principle over [fold]. *)

Section TREE_FOLD_IND.

Variables V A: Type.
Variable f: A -> T.elt -> V -> A.
Variable P: T.t V -> A -> Prop.
Variable init: A.
Variable m_final: T.t V.

Hypothesis P_compat:
  forall m m' a,
  (forall x, T.get x m = T.get x m') ->
  P m a -> P m' a.

Hypothesis H_base:
  P (T.empty _) init.

Hypothesis H_rec:
  forall m a k v,
  T.get k m = None -> T.get k m_final = Some v -> P m a -> P (T.set k v m) (f a k v).

Let f' (a: A) (p : T.elt * V) := f a (fst p) (snd p).

Let P' (l: list (T.elt * V)) (a: A) : Prop :=
  forall m, list_equiv l (T.elements m) -> P m a.

Remark H_base':
  P' nil init.
Proof.
(* Goal: None *)
  red; intros. apply P_compat with (T.empty _); auto.
(* Goal: None *)
  intros. rewrite T.gempty. symmetry. case_eq (T.get x m); intros; auto.
(* Goal: None *)
  assert (In (x, v) nil). rewrite (H (x, v)). apply T.elements_correct. auto.
(* Goal: None *)
(* Goal: None *)
  contradiction.
Qed.

Remark H_rec':
  forall k v l a,
  ~In k (List.map (@fst T.elt V) l) ->
  In (k, v) (T.elements m_final) ->
  P' l a ->
  P' (l ++ (k, v) :: nil) (f a k v).
Proof.
(* Goal: None *)
  unfold P'; intros.
(* Goal: P m (f a k v) *)
  set (m0 := T.remove k m).
(* Goal: P m (f a k v) *)
  apply P_compat with (T.set k v m0).
(* Goal: None *)
    intros. unfold m0. rewrite T.gsspec. destruct (T.elt_eq x k).
    symmetry. apply T.elements_complete. rewrite <- (H2 (x, v)).
(* Goal: @eq (option A) (@Some A v) (@None A) *)
    apply in_or_app. simpl. intuition congruence.
(* Goal: None *)
    apply T.gro. auto.
(* Goal: None *)
  apply H_rec. unfold m0. apply T.grs. apply T.elements_complete. auto.
(* Goal: @eq bool a true *)
  apply H1. red. intros [k' v'].
(* Goal: None *)
  split; intros.
(* Goal: None *)
(* Goal: None *)
  apply T.elements_correct. unfold m0. rewrite T.gro. apply T.elements_complete.
(* Goal: None *)
  rewrite <- (H2 (k', v')). apply in_or_app. auto.
(* Goal: None *)
  red; intro; subst k'. elim H. change k with (fst (k, v')). apply in_map. auto.
(* Goal: None *)
  assert (T.get k' m0 = Some v'). apply T.elements_complete. auto.
(* Goal: @eq (option A) (@Some A v) (@None A) *)
  unfold m0 in H4. rewrite T.grspec in H4. destruct (T.elt_eq k' k). congruence.
(* Goal: None *)
  assert (In (k', v') (T.elements m)). apply T.elements_correct; auto.
(* Goal: None *)
  rewrite <- (H2 (k', v')) in H5. destruct (in_app_or _ _ _ H5). auto.
(* Goal: @eq (option A) (@Some A v) (@None A) *)
  simpl in H6. intuition congruence.
Qed.

Lemma fold_rec_aux:
  forall l1 l2 a,
  list_equiv (l2 ++ l1) (T.elements m_final) ->
  list_disjoint (List.map (@fst T.elt V) l1) (List.map (@fst T.elt V) l2) ->
  list_norepet (List.map (@fst T.elt V) l1) ->
  P' l2 a -> P' (l2 ++ l1) (List.fold_left f' l1 a).
Proof.
(* Goal: None *)
  induction l1; intros; simpl.
(* Goal: None *)
  rewrite <- List.app_nil_end. auto.
  destruct a as [k v]; simpl in *. inv H1.
  change ((k, v) :: l1) with (((k, v) :: nil) ++ l1). rewrite <- List.app_ass. apply IHl1.
(* Goal: None *)
  rewrite app_ass. auto.
(* Goal: None *)
  red; intros. rewrite map_app in H3. destruct (in_app_or _ _ _ H3). apply H0; auto with coqlib.
(* Goal: @eq (option A) (@Some A v) (@None A) *)
  simpl in H4. intuition congruence.
(* Goal: None *)
  auto.
(* Goal: None *)
  unfold f'. simpl. apply H_rec'; auto. eapply list_disjoint_notin; eauto with coqlib.
(* Goal: None *)
  rewrite <- (H (k, v)). apply in_or_app. simpl. auto.
Qed.

Theorem fold_rec:
  P m_final (T.fold f m_final init).
Proof.
(* Goal: None *)
  intros. rewrite T.fold_spec. fold f'.
  assert (P' (nil ++ T.elements m_final) (List.fold_left f' (T.elements m_final) init)).
(* Goal: None *)
(* Goal: None *)
    apply fold_rec_aux.
(* Goal: None *)
    simpl. red; intros; tauto.
(* Goal: None *)
    simpl. red; intros. elim H0.
(* Goal: None *)
(* Goal: None *)
    apply T.elements_keys_norepet.
(* Goal: None *)
(* Goal: None *)
    apply H_base'.
(* Goal: None *)
  simpl in H. red in H. apply H. red; intros. tauto.
Qed.

End TREE_FOLD_IND.

(** A nonnegative measure over trees *)

Section MEASURE.

Variable V: Type.

Definition cardinal (x: T.t V) : nat := List.length (T.elements x).

Theorem cardinal_remove:
  forall x m y, T.get x m = Some y -> (cardinal (T.remove x m) < cardinal m)%nat.
Proof.
(* Goal: None *)
  unfold cardinal; intros.
(* Goal: None *)
  exploit T.elements_remove; eauto. intros (l1 & l2 & P & Q).
(* Goal: None *)
  rewrite P, Q. rewrite ! app_length. simpl. omega.
Qed.

Theorem cardinal_set:
  forall x m y, T.get x m = None -> (cardinal m < cardinal (T.set x y m))%nat.
Proof.
(* Goal: None *)
  intros. set (m' := T.set x y m).
(* Goal: lt (cardinal m) (cardinal m') *)
  replace (cardinal m) with (cardinal (T.remove x m')).
(* Goal: None *)
(* Goal: None *)
  apply cardinal_remove with y. unfold m'; apply T.gss.
(* Goal: None *)
  unfold cardinal. f_equal. apply T.elements_extensional.
(* Goal: None *)
  intros. unfold m'. rewrite T.grspec, T.gsspec.
(* Goal: None *)
  destruct (T.elt_eq i x); auto. congruence.
Qed.

End MEASURE.

(** Forall and exists *)

Section FORALL_EXISTS.

Variable A: Type.

Definition for_all (m: T.t A) (f: T.elt -> A -> bool) : bool :=
  T.fold (fun b x a => b && f x a) m true.

Lemma for_all_correct:
  forall m f,
  for_all m f = true <-> (forall x a, T.get x m = Some a -> f x a = true).
Proof.
(* Goal: None *)
  intros m0 f.
(* Goal: None *)
  unfold for_all. apply fold_rec; intros.
- (* Extensionality *)
(* Goal: None *)
  rewrite H0. split; intros. rewrite <- H in H2; auto. rewrite H in H2; auto.
- (* Base case *)
(* Goal: None *)
  split; intros. rewrite T.gempty in H0; congruence. auto.
- (* Inductive case *)
(* Goal: None *)
  split; intros.
(* Goal: @eq bool (f x a0) true *)
(* Goal: @eq bool (andb a (f k v)) true *)
  destruct (andb_prop _ _ H2). rewrite T.gsspec in H3. destruct (T.elt_eq x k).
(* Goal: None *)
  inv H3. auto.
(* Goal: None *)
  apply H1; auto.
(* Goal: @eq bool (andb a (f k v)) true *)
  apply andb_true_intro. split.
(* Goal: None *)
  rewrite H1. intros. apply H2. rewrite T.gso; auto. congruence.
(* Goal: @eq bool (f k v) true *)
  apply H2. apply T.gss.
Qed.

Definition exists_ (m: T.t A) (f: T.elt -> A -> bool) : bool :=
  T.fold (fun b x a => b || f x a) m false.

Lemma exists_correct:
  forall m f,
  exists_ m f = true <-> (exists x a, T.get x m = Some a /\ f x a = true).
Proof.
(* Goal: None *)
  intros m0 f.
(* Goal: None *)
  unfold exists_. apply fold_rec; intros.
- (* Extensionality *)
(* Goal: @eq (option A) (@Some A v) (@None A) *)
  rewrite H0. split; intros (x0 & a0 & P & Q); exists x0; exists a0; split; auto; congruence.
- (* Base case *)
(* Goal: None *)
  split; intros. congruence. destruct H as (x & a & P & Q). rewrite T.gempty in P; congruence.
- (* Inductive case *)
(* Goal: None *)
  split; intros.
(* Goal: None *)
(* Goal: @eq bool (orb a (f k v)) true *)
  destruct (orb_true_elim _ _ H2).
(* Goal: None *)
(* Goal: None *)
(* Goal: @eq bool (orb a (f k v)) true *)
  rewrite H1 in e. destruct e as (x1 & a1 & P & Q).
(* Goal: None *)
  exists x1; exists a1; split; auto. rewrite T.gso; auto. congruence.
(* Goal: None *)
  exists k; exists v; split; auto. apply T.gss.
  destruct H2 as (x1 & a1 & P & Q). apply orb_true_intro.
(* Goal: or (@eq bool a true) (@eq bool (f k v) true) *)
  rewrite T.gsspec in P. destruct (T.elt_eq x1 k).
(* Goal: None *)
  inv P. right; auto.
(* Goal: None *)
  left. apply H1. exists x1; exists a1; auto.
Qed.

Remark exists_for_all:
  forall m f,
  exists_ m f = negb (for_all m (fun x a => negb (f x a))).
Proof.
(* Goal: None *)
  intros. unfold exists_, for_all. rewrite ! T.fold_spec.
(* Goal: None *)
  change false with (negb true). generalize (T.elements m) true.
(* Goal: None *)
  induction l; simpl; intros.
(* Goal: None *)
  auto.
  rewrite <- IHl. f_equal.
(* Goal: @equiv A eqA eqAeq a a *)
  destruct b; destruct (f (fst a) (snd a)); reflexivity.
Qed.

Remark for_all_exists:
  forall m f,
  for_all m f = negb (exists_ m (fun x a => negb (f x a))).
Proof.
(* Goal: None *)
  intros. unfold exists_, for_all. rewrite ! T.fold_spec.
(* Goal: None *)
  change true with (negb false). generalize (T.elements m) false.
(* Goal: None *)
  induction l; simpl; intros.
(* Goal: None *)
  auto.
  rewrite <- IHl. f_equal.
(* Goal: @equiv A eqA eqAeq a a *)
  destruct b; destruct (f (fst a) (snd a)); reflexivity.
Qed.

Lemma for_all_false:
  forall m f,
  for_all m f = false <-> (exists x a, T.get x m = Some a /\ f x a = false).
Proof.
(* Goal: None *)
  intros. rewrite for_all_exists.
(* Goal: None *)
  rewrite negb_false_iff. rewrite exists_correct.
(* Goal: None *)
  split; intros (x & a & P & Q); exists x; exists a; split; auto.
(* Goal: None *)
  rewrite negb_true_iff in Q. auto.
(* Goal: None *)
  rewrite Q; auto.
Qed.

Lemma exists_false:
  forall m f,
  exists_ m f = false <-> (forall x a, T.get x m = Some a -> f x a = false).
Proof.
(* Goal: None *)
  intros. rewrite exists_for_all.
(* Goal: None *)
  rewrite negb_false_iff. rewrite for_all_correct.
(* Goal: None *)
  split; intros. apply H in H0. rewrite negb_true_iff in H0. auto. rewrite H; auto.
Qed.

End FORALL_EXISTS.

(** More about [beq] *)

Section BOOLEAN_EQUALITY.

Variable A: Type.
Variable beqA: A -> A -> bool.

Theorem beq_false:
  forall m1 m2,
  T.beq beqA m1 m2 = false <->
  exists x, match T.get x m1, T.get x m2 with
            | None, None => False
            | Some a1, Some a2 => beqA a1 a2 = false
            | _, _ => True
            end.
Proof.
(* Goal: None *)
  intros; split; intros.
- (* beq = false -> existence *)
(* Goal: None *)
  set (p1 := fun x a1 => match T.get x m2 with None => false | Some a2 => beqA a1 a2 end).
(* Goal: None *)
  set (p2 := fun x a2 => match T.get x m1 with None => false | Some a1 => beqA a1 a2 end).
(* Goal: None *)
  destruct (for_all m1 p1) eqn:F1; [destruct (for_all m2 p2) eqn:F2 | idtac].
(* Goal: @eq (option A) (@Some A v) (@None A) *)
  + cut (T.beq beqA m1 m2 = true). congruence.
(* Goal: None *)
    rewrite for_all_correct in *. rewrite T.beq_correct; intros.
(* Goal: None *)
    destruct (T.get x m1) as [a1|] eqn:X1.
(* Goal: @eq (option A) (@Some A v) (@None A) *)
    generalize (F1 _ _ X1). unfold p1. destruct (T.get x m2); congruence.
(* Goal: None *)
    destruct (T.get x m2) as [a2|] eqn:X2; auto.
(* Goal: @eq (option A) (@Some A v) (@None A) *)
    generalize (F2 _ _ X2). unfold p2. rewrite X1. congruence.
  + rewrite for_all_false in F2. destruct F2 as (x & a & P & Q).
(* Goal: None *)
    exists x. rewrite P. unfold p2 in Q. destruct (T.get x m1); auto.
  + rewrite for_all_false in F1. destruct F1 as (x & a & P & Q).
(* Goal: None *)
    exists x. rewrite P. unfold p1 in Q. destruct (T.get x m2); auto.
- (* existence -> beq = false *)
(* Goal: None *)
  destruct H as [x P].
(* Goal: None *)
  destruct (T.beq beqA m1 m2) eqn:E; auto.
(* Goal: @eq bool true false *)
  rewrite T.beq_correct in E.
(* Goal: @eq (option A) (@Some A v) (@None A) *)
  generalize (E x). destruct (T.get x m1); destruct (T.get x m2); tauto || congruence.
Qed.

End BOOLEAN_EQUALITY.

(** Extensional equality between trees *)

Section EXTENSIONAL_EQUALITY.

Variable A: Type.
Variable eqA: A -> A -> Prop.
Hypothesis eqAeq: Equivalence eqA.

Definition Equal (m1 m2: T.t A) : Prop :=
  forall x, match T.get x m1, T.get x m2 with
                | None, None => True
                | Some a1, Some a2 => a1 === a2
                | _, _ => False
            end.

Lemma Equal_refl: forall m, Equal m m.
Proof.
(* Goal: None *)
  intros; red; intros. destruct (T.get x m); auto. reflexivity.
Qed.

Lemma Equal_sym: forall m1 m2, Equal m1 m2 -> Equal m2 m1.
Proof.
(* Goal: None *)
  intros; red; intros. generalize (H x). destruct (T.get x m1); destruct (T.get x m2); auto. intros; symmetry; auto.
Qed.

Lemma Equal_trans: forall m1 m2 m3, Equal m1 m2 -> Equal m2 m3 -> Equal m1 m3.
Proof.
(* Goal: None *)
  intros; red; intros. generalize (H x) (H0 x).
  destruct (T.get x m1); destruct (T.get x m2); try tauto;
(* Goal: None *)
  destruct (T.get x m3); try tauto.
(* Goal: None *)
  intros. transitivity a0; auto.
Qed.

(* Goal: None *)
(* Goal: None *)
Instance Equal_Equivalence : Equivalence Equal := {
  Equivalence_Reflexive := Equal_refl;
  Equivalence_Symmetric := Equal_sym;
  Equivalence_Transitive := Equal_trans
}.

Hypothesis eqAdec: EqDec A eqA.

Program Definition Equal_dec (m1 m2: T.t A) : { m1 === m2 } + { m1 =/= m2 } :=
  match T.beq (fun a1 a2 => proj_sumbool (a1 == a2)) m1 m2 with
  | true => left _
  | false => right _
  end.
Next Obligation.
(* Goal: Equal m1 m2 *)
  rename Heq_anonymous into B.
(* Goal: Equal m1 m2 *)
  symmetry in B. rewrite T.beq_correct in B.
(* Goal: None *)
  red; intros. generalize (B x).
(* Goal: None *)
  destruct (T.get x m1); destruct (T.get x m2); auto.
(* Goal: None *)
  intros. eapply proj_sumbool_true; eauto.
Qed.
Next Obligation.
(* Goal: False *)
  assert (T.beq (fun a1 a2 => proj_sumbool (a1 == a2)) m1 m2 = true).
(* Goal: None *)
  apply T.beq_correct; intros.
(* Goal: None *)
(* Goal: False *)
  generalize (H x).
(* Goal: None *)
  destruct (T.get x m1); destruct (T.get x m2); try tauto.
(* Goal: None *)
  intros. apply proj_sumbool_is_true; auto.
(* Goal: @eq (option A) (@Some A v) (@None A) *)
  unfold equiv, complement in H0. congruence.
Qed.

Instance Equal_EqDec : EqDec (T.t A) Equal := Equal_dec.

End EXTENSIONAL_EQUALITY.

(** Creating a tree from a list of (key, value) pairs. *)

Section OF_LIST.

Variable A: Type.

Let f := fun (m: T.t A) (k_v: T.elt * A) => T.set (fst k_v) (snd k_v) m.

Definition of_list (l: list (T.elt * A)) : T.t A :=
  List.fold_left f l (T.empty _).

Lemma in_of_list:
  forall l k v, T.get k (of_list l) = Some v -> In (k, v) l.
Proof.
  assert (REC: forall k v l m,
           T.get k (fold_left f l m) = Some v -> In (k, v) l \/ T.get k m = Some v).
(* Goal: None *)
  { induction l as [ | [k1 v1] l]; simpl; intros.
(* Goal: None *)
  - tauto.
  - apply IHl in H. unfold f in H. simpl in H. rewrite T.gsspec in H.
(* Goal: None *)
    destruct H; auto.
(* Goal: None *)
    destruct (T.elt_eq k k1). inv H. auto. auto.
  }
(* Goal: None *)
  intros. apply REC in H. rewrite T.gempty in H. intuition congruence.
Qed.

Lemma of_list_dom:
  forall l k, In k (map fst l) -> exists v, T.get k (of_list l) = Some v.
Proof.
  assert (REC: forall k l m,
            In k (map fst l) \/ (exists v, T.get k m = Some v) ->
            exists v, T.get k (fold_left f l m) = Some v).
(* Goal: None *)
  { induction l as [ | [k1 v1] l]; simpl; intros.
(* Goal: None *)
  - tauto.
  - apply IHl. unfold f; rewrite T.gsspec. simpl. destruct (T.elt_eq k k1).
(* Goal: None *)
    right; econstructor; eauto.
(* Goal: @eq (option A) (@Some A v) (@None A) *)
    intuition congruence.
  }
(* Goal: None *)
  intros. apply REC. auto.
Qed.

Remark of_list_unchanged:
  forall k l m, ~In k (map fst l) -> T.get k (List.fold_left f l m) = T.get k m.
Proof.
(* Goal: None *)
  induction l as [ | [k1 v1] l]; simpl; intros.
(* Goal: None *)
- auto.
(* Goal: None *)
- rewrite IHl by tauto. unfold f; apply T.gso; intuition auto.
Qed.

Lemma of_list_unique:
  forall k v l1 l2,
  ~In k (map fst l2) -> T.get k (of_list (l1 ++ (k, v) :: l2)) = Some v.
Proof.
(* Goal: None *)
  intros. unfold of_list. rewrite fold_left_app. simpl.
(* Goal: None *)
  rewrite of_list_unchanged by auto. unfold f; apply T.gss.
Qed.

Lemma of_list_norepet:
  forall l k v, list_norepet (map fst l) -> In (k, v) l -> T.get k (of_list l) = Some v.
Proof.
  assert (REC: forall k v l m,
            list_norepet (map fst l) ->
            In (k, v) l ->
            T.get k (fold_left f l m) = Some v).
(* Goal: None *)
  { induction l as [ | [k1 v1] l]; simpl; intros.
(* Goal: None *)
(* Goal: None *)
    contradiction.
(* Goal: None *)
(* Goal: None *)
    inv H. destruct H0.
(* Goal: None *)
    inv H. rewrite of_list_unchanged by auto. apply T.gss.
(* Goal: None *)
    apply IHl; auto.
  }
(* Goal: None *)
  intros; apply REC; auto.
Qed.

Lemma of_list_elements:
  forall m k, T.get k (of_list (T.elements m)) = T.get k m.
Proof.
(* Goal: None *)
  intros. destruct (T.get k m) as [v|] eqn:M.
(* Goal: None *)
- apply of_list_norepet. apply T.elements_keys_norepet. apply T.elements_correct; auto.
(* Goal: None *)
- destruct (T.get k (of_list (T.elements m))) as [v|] eqn:M'; auto.
(* Goal: @eq (option A) (@Some A v) (@None A) *)
  apply in_of_list in M'. apply T.elements_complete in M'. congruence.
Qed.

End OF_LIST.

Lemma of_list_related:
  forall (A B: Type) (R: A -> B -> Prop) k l1 l2,
  list_forall2 (fun ka kb => fst ka = fst kb /\ R (snd ka) (snd kb)) l1 l2 ->
  option_rel R (T.get k (of_list l1)) (T.get k (of_list l2)).
Proof.
(* Goal: None *)
  intros until k. unfold of_list.
(* Goal: None *)
  set (R' := fun ka kb => fst ka = fst kb /\ R (snd ka) (snd kb)).
  set (fa := fun (m : T.t A) (k_v : T.elt * A) => T.set (fst k_v) (snd k_v) m).
  set (fb := fun (m : T.t B) (k_v : T.elt * B) => T.set (fst k_v) (snd k_v) m).
  assert (REC: forall l1 l2, list_forall2 R' l1 l2 ->
               forall m1 m2, option_rel R (T.get k m1) (T.get k m2) ->
               option_rel R (T.get k (fold_left fa l1 m1)) (T.get k (fold_left fb l2 m2))).
(* Goal: None *)
  { induction 1; intros; simpl.
(* Goal: None *)
  - auto.
  - apply IHlist_forall2. unfold fa, fb. rewrite ! T.gsspec.
(* Goal: None *)
    destruct H as [E F]. rewrite E. destruct (T.elt_eq k (fst b1)).
(* Goal: None *)
    constructor; auto.
(* Goal: None *)
    auto. }
(* Goal: None *)
  intros. apply REC; auto. rewrite ! T.gempty. constructor.
Qed.

End Tree_Properties.

Module PTree_Properties := Tree_Properties(PTree).

(** * Useful notations *)

Notation "a ! b" := (PTree.get b a) (at level 1).
Notation "a !! b" := (PMap.get b a) (at level 1).
