Binary Search Tree-ness
Due: Thursday, February 27, 00:00AM
We implemented binary search trees in class and proved the following theorem:
Axiom search_insert : forall k v t, search k (insert k v t) = Some v.
However, this theorem doesn’t tell us that t
truly is a binary search tree. For example, if insert
always inserted at the head and search
searched the whole tree, the theorem would still be provable.
A key part of the problem is that values of type tree
are freely-generated binary trees, and not just binary search tress.
Your task for this assignment is to define a predicate that defines binary search trees:
Axiom BST : tree -> Prop.
And use this predicate to strengthen the type of insert:
Axiom insert : nat -> A -> forall (t : tree), BST t -> {r : tree | BST r}
This is not total correctness for insert
(why not?), but a step in that direction.
Hints
-
For the proof, you cannot define
insert
directly. You need an auxiliary function, sayins
, with a stronger return type.To insert into the left-hand side of a node, you need to know that if all keys in
t
are less thanj
andi < j
then all keys in(ins i v t)
are also less thanj
.You need to know something similar to insert into the right-hand side. Both these propositions need to added to the proposition in the return type of
ins
(use/\
). -
Use
refine
to define your functions so that you can fill in the proof terms using tactics. -
A simple way to do the proofs is to manually apply
inversion
andsubst
to some hypotheses. E.g., if you have a hypothesisH: BST (Node lhs k v rhs)
, you’ll need toinversion H
to revealBST lhs
andBST rhs
. Once you’ve revealed enough information,intuition
can tackle the rest. -
Define the auxiliary
ins
function like this:Fixpoint ins (k : nat) (v : A) (t : tree) { struct t } : BST t -> { r:tree | BST r /\ ... }. refine (match t with | Leaf => fun isBST => ... | Node lhs j w rhs => fun isBST => ... end). (* Proof obligations *) ... Defined.
Notice that each case of the
match
expression is a function that takes theBST t
argument. Section 6.1 of CPDT explains why this is necessary and why the obvious definition doesn’t work.
Binary trees (from class)
For your reference, here is the code we developed in class. You may wish to use the comparison operation and the definition of trees.
Set Implicit Arguments.
Require Import Cpdt.CpdtTactics.
Require Import List Arith Bool.
Section Compare.
Inductive compare (x y : nat) : Set :=
| EQ : x = y -> compare x y
| LT : y > x -> compare x y
| GT : x > y -> compare x y.
Lemma compare_impl :
forall x y, compare x y -> compare (S x) (S y).
intros.
destruct H.
apply EQ. crush.
apply LT. crush.
apply GT. crush.
Qed.
Fixpoint cmp (x y : nat) : compare x y :=
match x, y with
| O, O => EQ (eq_refl O)
| S x', O => GT (lt_O_Sn x')
| O, S y' => LT (lt_O_Sn y')
| S x', S y' => compare_impl (cmp x' y')
end.
End Compare.
Module BinaryTree.
Variable A : Set.
Inductive tree : Set :=
| Leaf : tree
| Node : tree -> nat -> A -> tree -> tree.
Fixpoint insert (k : nat) (v : A) (t : tree) :=
match t with
| Leaf => Node Leaf k v Leaf
| Node lhs k' v' rhs =>
match cmp k k' with
| EQ _ => Node lhs k' v rhs
| LT _ => Node (insert k v lhs) k' v' rhs
| GT _ => Node lhs k' v' (insert k v rhs)
end
end.
Fixpoint search (k : nat) (t : tree) :=
match t with
| Leaf => None
| Node lhs k' v rhs =>
match cmp k k' with
| EQ _ => Some v
| LT _ => search k lhs
| GT _ => search k rhs
end
end.
Theorem search_insert :
forall k v t,
search k (insert k v t) = Some v.
Proof.
induction t.
+ simpl.
destruct (cmp k k); crush.
+ simpl.
destruct (cmp k n).
- crush.
destruct (cmp n n); crush.
- crush.
destruct (cmp k n); crush.
- simpl.
destruct (cmp k n); crush.
Qed.
End BinaryTree.