Section EXAMPLES.
Variables v2 v4 v5 : V.
Eval compute in insert 5 v5 (insert 2 v2 (insert 4 v5 empty_tree)).
(*      = T (T E 2 v2 E) 4 v5 (T E 5 v5 E) *)
Eval compute in lookup 5 (T (T E 2 v2 E) 4 v5 (T E 5 v5 E)).
(*      = v5 *)
Eval compute in lookup 3 (T (T E 2 v2 E) 4 v5 (T E 5 v5 E)).
(*      = default *)
Eval compute in elements (T (T E 2 v2 E) 4 v5 (T E 5 v5 E)).
(*      = [(2, v2); (4, v5); (5, v5)] *)
End EXAMPLES.
(* ################################################################# *)
(** * What Should We Prove About Search trees? *)
(** Search trees are meant to be an implementation of maps. That is, they have an [insert] function that corresponds to the [update] function of a map, and a [lookup] function that corresponds to applying the map to an argument.  To prove the correctness of a search-tree algorithm, we can prove: - Any search tree corresponds to some map, using a function or relation that we demonstrate. - The lookup function gives the same result as applying the map - The insert function returns a corresponding map. - Maps have the properties we actually wanted.  It would do no good to prove that searchtrees correspond to some abstract type X, if X didn't have useful properties!  What properties do we want searchtrees to have?  If I insert the binding [(k,v)] into a searchtree [t], then look up [k], I should get [v]. If I look up [k'] in [insert (k,v) t], where [k'<>k], then I should get the same result as [lookup k t].  There are several more properties. Fortunately, all these properties are already proved about [total_map] in the [Maps] module: *)
Check t_update_eq. (*  : forall (A : Type) (m : total_map A) (x : id) (v : A),
t_update m x v x = v   *)
Check t_update_neq.  (* : forall (X : Type) (v : X) (x1 x2 : id) (m : total_map X),
x1 <> x2 -> t_update m x1 v x2 = m x2    *)
Check t_update_shadow.  (* : forall (A : Type) (m : total_map A) (v1 v2 : A) (x : id),
t_update (t_update m x v1) x v2 = t_update m x v2    *)
Check t_update_same.  (* : forall (X : Type) (x : id) (m : total_map X),
t_update m x (m x) = m    *)
Check t_update_permute. (* forall (X : Type) (v1 v2 : X) (x1 x2 : id) (m : total_map X),
x2 <> x1 ->
t_update (t_update m x2 v2) x1 v1 =
t_update (t_update m x1 v1) x2 v2    *)
Check t_apply_empty. (* : forall (A : Type) (x : id) (v : A),
t_empty v x = v *)
(** So, if we like those properties that [total_map] is proved to have, and we can prove that searchtrees behave like maps, then we don't have to reprove each individual property about searchtrees.  More generally:  a job worth doing is worth doing well. It does no good to prove the "correctness" of a program, if you prove that it satisfies a wrong or useless specification. *)
(* ################################################################# *)
(** * Efficiency of Search Trees *)
(** We use binary search trees because they are efficient.  That is, if there are [N] elements in a (reasonably well balanced) BST, each insertion or lookup takes about logN time.  What could go wrong?  1.  The search tree might not be balanced.  In that case, each insertion or lookup will take as much as linear time.  - SOLUTION: use an algorithm, such as "red-black trees", that ensures the trees stay balanced.  We'll do that in Chapter [RedBlack].  2.  Our keys are natural numbers, and Coq's [nat] type takes linear time _per comparison_.  That is, computing (j <? k) takes time proportional to the _value_ of [k-j].  - SOLUTION: represent keys by a data type that has a more efficient comparison operator.  We just use [nat] in this chapter because it's something you're already familiar with.  3.  There's no notion of "run time" in Coq.  That is, we can't say what it means that a Coq function "takes N steps to evaluate."  Therefore, we can't prove that binary search trees are efficient.  - SOLUTION 1: Don't prove (in Coq) that they're efficient; just prove that they are correct.  Prove things about their efficiency the old-fashioned way, on pencil and paper.  - SOLUTION 2: Prove in Coq some facts about the height of the trees, which have direct bearing on their efficiency. We'll explore that in later chapters.  4.  Our functions in Coq aren't real implementations; they are just pretend models of real implementations.  What if there are bugs in the correspondence between the Coq function and the real implementation?  - SOLUTION: Use Coq's [extraction] feature to derive the real implementation (in Ocaml or Haskell) automatically from the Coq function.  Or, use Coq's [vm_compute] or [native_compute] feature to compile and run the programs efficiently inside Coq.  We'll explore [extraction] in a later chapter. *)
(* ################################################################# *)
(** * Proof of Correctness *)
(** We claim that a [tree] "corresponds" to a [total_map]. So we must exhibit an "abstraction relation" [Abs: tree -> total_map V -> Prop].  The idea is that [Abs t m] says that tree [t] is a representation of map [m]; or that map [m] is an abstraction of tree [t].  How should we define this abstraction relation?  The empty tree is easy:   [Abs E (fun x => default)].  Now, what about this tree?: *)
Definition example_tree (v2 v4 v5 : V) :=
T (T E 2 v2 E) 4 v4 (T E 5 v5 E).
(** **** Exercise: 2 stars (example_map)  *)
(* Fill in the definition of example_map with a total_map that you think example_tree should correspond to.  Use [t_update] and [(t_empty default)]. *)
Definition example_map (v2 v4 v5: V) : total_map V
(* REPLACE THIS LINE WITH ":= _your_definition_ ." *). Admitted. (** [] *)
(** To build the [Abs] relation, we'll use these two auxiliary functions that construct maps: *)
Definition combine {A} (pivot: key) (m1 m2: total_map A) : total_map A :=
fun x => if x <? pivot  then m1 x else m2 x.
(** [combine pivot a b] uses the map [a] on any input less than [pivot], and uses map [b] on any input [ >= pivot]. *)
Inductive Abs:  tree -> total_map V -> Prop :=
| Abs_E: Abs E (t_empty default)
| Abs_T: forall a b l k v r,
Abs l a ->
Abs r b ->
Abs (T l k v r)  (t_update (combine k a b) k v).
(** **** Exercise: 3 stars (check_example_map)  *)
(** Prove that your [example_map] is the right one. If it isn't, go back and fix your definition of [example_map]. You will probably need the [bdestruct] tactic, and [omega]. *)
(** Before we prove that [elements] is correct, let's consider a simpler version. *)
Fixpoint slow_elements (s: tree) : list (key * V) :=
match s with
| E => nil
| T a k v b => slow_elements a ++ [(k,v)] ++ slow_elements b
end.
(** This one is easier to understand than the [elements] function, because it doesn't carry the [base] list around in its recursion. Unfortunately, its running time is quadratic, because at each of the [T] nodes it does a linear-time list-concatentation.  The original [elements] function takes linear time overall; that's much more efficient.  To prove correctness of [elements], it's actually easier to first prove that it's equivalent to [slow_elements], then prove the correctness of [slow_elements].  We don't care that [slow_elements] is quadratic, because we're never going to really run it; it's just there to support the proof. *)
(** The question is, why did we not need the representation invariant in the proof of [lookup_relate]?  The answer is that our particular Abs relation is much more clever than necessary:  *)
Print Abs.
(* Inductive Abs : tree -> total_map V -> Prop := Abs_E : Abs E (t_empty default) | Abs_T : forall (a b: total_map V) (l: tree) (k: key) (v: V) (r: tree), Abs l a -> Abs r b -> Abs (T l k v r) (t_update (combine k a b) (Id k) v) *)
(** Because the [combine] function is chosen very carefully, it turns out that this abstraction relation even works on bogus trees! *)
Remark abstraction_of_bogus_tree:
forall v2 v3,
Abs (T (T E 3 v3 E) 2 v2 E) (t_update (t_empty default) 2 v2).
(** Step through the proof to [LOOK HERE], and notice what's going on. Just when it seems that [(T (T E 3 v3 E) 2 v2 E)] is about to produce [v3] while [(t_update (t_empty default) (Id 2) v2)] is about to produce [default], [omega] finds a contradiction.  What's happening is that [combine 2] is careful to ignore any keys >= 2 in the left-hand subtree.  For that reason, [Abs] matches the _actual_ behavior of [lookup], even on bogus trees.  But that's a really strong condition!  We should not have to care about the behavior of [lookup] (and [insert]) on bogus trees.  We should not need to prove anything about it, either.  Sure, it's convenient in this case that the abstraction relation is able to cope with ill-formed trees.  But in general, when proving correctness of abstract-data-type (ADT) implementations, it may be a lot of extra effort to make the abstraction relation as heavy-duty as that. It's often much easier for the abstraction relation to assume that the representation is well formed.  Thus, the general statement of our correctness theorems will be more like [lookup_relate'] than like [lookup_relate].  *)
(* ################################################################# *)
(** * Every Well-Formed Tree Does Actually Relate to an Abstraction *)
(** We're not quite done yet.  We would like to know that _every tree that satisfies the representation invariant, means something_.  So as a general sanity check, we need the following theorem: *)
(** But it's not so easy to prove that [lookup] and [insert] respect this relation.  For example, the following claim is not true. *)

Theorem naive_lookup_relateX:
forall k t cts ,
AbsX t cts -> lookup k t =  cts k.