Library Stdlib.MSets.MSetAVL
MSetAVL : Implementation of MSetInterface via AVL trees
This module implements finite sets using AVL trees.
It follows the implementation from Ocaml's standard library,
All operations given here expect and produce well-balanced trees
(in the ocaml sense: heights of subtrees shouldn't differ by more
than 2), and hence has low complexities (e.g. add is logarithmic
in the size of the set). But proving these balancing preservations
is in fact not necessary for ensuring correct operational behavior
and hence fulfilling the MSet interface. As a consequence,
balancing results are not part of this file anymore, they can
now be found in
MSetFullAVL.
Four operations (
union,
subset,
compare and
equal) have
been slightly adapted in order to have only structural recursive
calls. The precise ocaml versions of these operations have also
been formalized (thanks to Function+measure), see
ocaml_union,
ocaml_subset,
ocaml_compare and
ocaml_equal in
MSetFullAVL. The structural variants compute faster in Coq,
whereas the other variants produce nicer and/or (slightly) faster
code after extraction.
Generic trees instantiated with integer height
We reuse a generic definition of trees where the information
parameter is a
Int.t. Functions like mem or fold are also
provided by this generic functor.
Helper functions
create l x r creates a node, assuming
l and
r
to be balanced and
|height l - height r| <= 2.
bal l x r acts as create, but performs one step of
rebalancing if necessary, i.e. assumes |height l - height r| <= 3.
Join
Same as
bal but does not assume anything regarding heights
of
l and
r.
Extraction of minimum element
Morally,
remove_min is to be applied to a non-empty tree
t = Node h l x r. Since we can't deal here with
assert false
for
t=Leaf, we pre-unpack
t (and forget about
h).
Merging two trees
merge t1 t2 builds the union of
t1 and
t2 assuming all elements
of
t1 to be smaller than all elements of
t2, and
|height t1 - height t2| <= 2.
Concatenation
Same as
merge but does not assume anything about heights.
Splitting
split x s returns a triple
(l, present, r) where
- l is the set of elements of s that are < x
- r is the set of elements of s that are > x
- present is true if and only if s contains x.
Record triple :=
mktriple {
t_left:
t;
t_in:
bool;
t_right:
t }.
Notation "<< l , b , r >>" := (
mktriple l b r) (
at level 9).
Fixpoint split x s :
triple :=
match s with
|
Leaf =>
<< Leaf, false, Leaf >>
|
Node _ l y r =>
match X.compare x y with
|
Lt =>
let (
ll,
b,
rl) :=
split x l in << ll, b, join rl y r >>
|
Eq =>
<< l, true, r >>
|
Gt =>
let (
rl,
b,
rr) :=
split x r in << join l y rl, b, rr >>
end
end.
Union
In ocaml, heights of
s1 and
s2 are compared each time in order
to recursively perform the split on the smaller set.
Unfortunately, this leads to a non-structural algorithm. The
following code is a simplification of the ocaml version: no
comparison of heights. It might be slightly slower, but
experimentally all the tests I've made in ocaml have shown this
potential slowdown to be non-significant. Anyway, the exact code
of ocaml has also been formalized thanks to Function+measure, see
ocaml_union in
MSetFullAVL.
MakeRaw
Functor of pure functions + a posteriori proofs of invariant
preservation
Generic definition of binary-search-trees and proofs of
specifications for generic functions such as mem or fold.
Automation and dedicated tactics
Local Hint Immediate MX.eq_sym :
core.
Local Hint Unfold In lt_tree gt_tree Ok :
core.
Local Hint Constructors InT bst :
core.
Local Hint Resolve MX.eq_refl MX.eq_trans MX.lt_trans ok :
core.
Local Hint Resolve lt_leaf gt_leaf lt_tree_node gt_tree_node :
core.
Local Hint Resolve lt_tree_not_in lt_tree_trans gt_tree_not_in gt_tree_trans :
core.
Local Hint Resolve elements_spec2 :
core.
Tactic Notation "factornode"
ident(
s) :=
try clear s;
match goal with
| |-
context [
Node ?
l ?
x ?
r ?
h] =>
set (
s:=
Node l x r h)
in *;
clearbody s;
clear l x r h
|
_ :
context [
Node ?
l ?
x ?
r ?
h] |-
_ =>
set (
s:=
Node l x r h)
in *;
clearbody s;
clear l x r h
end.
Inductions principles for some of the set operators
#[
local]
Ltac caseq :=
match goal with [ |-
context [
match ?
t with _ =>
_ end] ] =>
let cmp :=
fresh in
let H :=
fresh in
remember t as cmp eqn:
H;
symmetry in H;
destruct cmp
end.
Lemma bal_ind [
P :
t -> X.t -> t -> tree -> Prop] :
(forall (
l :
t) (
x :
X.t) (
r :
t),
let hl :=
height l in
let hr :=
height r in (hr + 2
<? hl) = true -> l = Leaf -> P Leaf x r (
assert_false l x r)
) ->
(forall (
l :
t) (
x :
X.t) (
r :
t),
let hl :=
height l in
let hr :=
height r in
(hr + 2
<? hl) = true ->
forall (
_x :
I.t) (
ll :
tree) (
lx :
X.t) (
lr :
tree),
l = Node _x ll lx lr ->
(height lr <=? height ll) = true -> P (
Node _x ll lx lr)
x r (
create ll lx (
create lr x r))
) ->
(forall (
l :
t) (
x :
X.t) (
r :
t),
let hl :=
height l in
let hr :=
height r in
(hr + 2
<? hl) = true ->
forall (
_x :
I.t) (
ll :
tree) (
lx :
X.t) (
lr :
tree),
l = Node _x ll lx lr ->
(height lr <=? height ll) = false ->
lr = Leaf -> P (
Node _x ll lx Leaf)
x r (
assert_false l x r)
) ->
(forall (
l :
t) (
x :
X.t) (
r :
t),
let hl :=
height l in
let hr :=
height r in
(hr + 2
<? hl) = true ->
forall (
_x :
I.t) (
ll :
tree) (
lx :
X.t) (
lr :
tree),
l = Node _x ll lx lr ->
(height lr <=? height ll) = false ->
forall (
_x0 :
I.t) (
lrl :
tree) (
lrx :
X.t) (
lrr :
tree),
lr = Node _x0 lrl lrx lrr ->
P (
Node _x ll lx (
Node _x0 lrl lrx lrr))
x r (
create (
create ll lx lrl)
lrx (
create lrr x r))
) ->
(forall (
l :
t) (
x :
X.t) (
r :
t),
let hl :=
height l in
let hr :=
height r in
(hr + 2
<? hl) = false ->
(hl + 2
<? hr) = true -> r = Leaf -> P l x Leaf (
assert_false l x r)
) ->
(forall (
l :
t) (
x :
X.t) (
r :
t),
let hl :=
height l in
let hr :=
height r in
(hr + 2
<? hl) = false ->
(hl + 2
<? hr) = true ->
forall (
_x :
I.t) (
rl :
tree) (
rx :
X.t) (
rr :
tree),
r = Node _x rl rx rr ->
(height rl <=? height rr) = true -> P l x (
Node _x rl rx rr) (
create (
create l x rl)
rx rr)
) ->
(forall (
l :
t) (
x :
X.t) (
r :
t),
let hl :=
height l in
let hr :=
height r in
(hr + 2
<? hl) = false ->
(hl + 2
<? hr) = true ->
forall (
_x :
I.t) (
rl :
tree) (
rx :
X.t) (
rr :
tree),
r = Node _x rl rx rr ->
(height rl <=? height rr) = false ->
rl = Leaf -> P l x (
Node _x Leaf rx rr) (
assert_false l x r)
) ->
(forall (
l :
t) (
x :
X.t) (
r :
t),
let hl :=
height l in
let hr :=
height r in
(hr + 2
<? hl) = false ->
(hl + 2
<? hr) = true ->
forall (
_x :
I.t) (
rl :
tree) (
rx :
X.t) (
rr :
tree),
r = Node _x rl rx rr ->
(height rl <=? height rr) = false ->
forall (
_x0 :
I.t) (
rll :
tree) (
rlx :
X.t) (
rlr :
tree),
rl = Node _x0 rll rlx rlr ->
P l x (
Node _x (
Node _x0 rll rlx rlr)
rx rr) (
create (
create l x rll)
rlx (
create rlr rx rr))
) ->
(forall (
l :
t) (
x :
X.t) (
r :
t),
let hl :=
height l in
let hr :=
height r in
(hr + 2
<? hl) = false -> (hl + 2
<? hr) = false -> P l x r (
create l x r)
) ->
forall (
l :
t) (
x :
X.t) (
r :
t),
P l x r (
bal l x r).
Lemma remove_min_ind [
P :
tree -> elt -> t -> t * elt -> Prop] :
(forall (
l :
tree) (
x :
elt) (
r :
t),
l = Leaf -> P Leaf x r (r, x)) ->
(forall (
l :
tree) (
x :
elt) (
r :
t) (
_x :
I.t) (
ll :
tree) (
lx :
X.t) (
lr :
tree),
l = Node _x ll lx lr ->
P ll lx lr (
remove_min ll lx lr)
->
forall (
l' :
t) (
m :
elt),
remove_min ll lx lr = (l', m) -> P (
Node _x ll lx lr)
x r (bal l' x r, m)) ->
forall (
l :
tree) (
x :
elt) (
r :
t),
P l x r (
remove_min l x r).
Lemma merge_ind [
P :
tree -> tree -> tree -> Prop] :
(forall s1 s2 :
tree,
s1 = Leaf -> P Leaf s2 s2) ->
(forall (
s1 s2 :
tree) (
_x :
I.t) (
_x0 :
tree) (
_x1 :
X.t) (
_x2 :
tree),
s1 = Node _x _x0 _x1 _x2 -> s2 = Leaf -> P (
Node _x _x0 _x1 _x2)
Leaf s1) ->
(forall (
s1 s2 :
tree) (
_x :
I.t) (
_x0 :
tree) (
_x1 :
X.t) (
_x2 :
tree),
s1 = Node _x _x0 _x1 _x2 ->
forall (
_x3 :
I.t) (
l2 :
tree) (
x2 :
X.t) (
r2 :
tree),
s2 = Node _x3 l2 x2 r2 ->
forall (
s2' :
t) (
m :
elt),
remove_min l2 x2 r2 = (s2', m) -> P (
Node _x _x0 _x1 _x2) (
Node _x3 l2 x2 r2) (
bal s1 m s2')
) ->
forall s1 s2 :
tree,
P s1 s2 (
merge s1 s2).
Lemma concat_ind [
P :
tree -> tree -> tree -> Prop] :
(forall s1 s2 :
tree,
s1 = Leaf -> P Leaf s2 s2) ->
(forall (
s1 s2 :
tree) (
_x :
I.t) (
_x0 :
tree) (
_x1 :
X.t) (
_x2 :
tree),
s1 = Node _x _x0 _x1 _x2 -> s2 = Leaf -> P (
Node _x _x0 _x1 _x2)
Leaf s1) ->
(forall (
s1 s2 :
tree) (
_x :
I.t) (
_x0 :
tree) (
_x1 :
X.t) (
_x2 :
tree),
s1 = Node _x _x0 _x1 _x2 ->
forall (
_x3 :
I.t) (
l2 :
tree) (
x2 :
X.t) (
r2 :
tree),
s2 = Node _x3 l2 x2 r2 ->
forall (
s2' :
t) (
m :
elt),
remove_min l2 x2 r2 = (s2', m) -> P (
Node _x _x0 _x1 _x2) (
Node _x3 l2 x2 r2) (
join s1 m s2')
) ->
forall s1 s2 :
tree,
P s1 s2 (
concat s1 s2).
Lemma inter_ind [
P :
tree -> tree -> tree -> Prop] :
(forall s1 s2 :
tree,
s1 = Leaf -> P Leaf s2 Leaf) ->
(forall (
s1 s2 :
tree) (
_x :
I.t) (
l1 :
tree) (
x1 :
X.t) (
r1 :
tree),
s1 = Node _x l1 x1 r1 -> s2 = Leaf -> P (
Node _x l1 x1 r1)
Leaf Leaf) ->
(forall (
s1 s2 :
tree) (
_x :
I.t) (
l1 :
tree) (
x1 :
X.t) (
r1 :
tree),
s1 = Node _x l1 x1 r1 ->
forall (
_x0 :
I.t) (
_x1 :
tree) (
_x2 :
X.t) (
_x3 :
tree),
s2 = Node _x0 _x1 _x2 _x3 ->
forall (
l2' :
t) (
pres :
bool) (
r2' :
t),
split x1 s2 = << l2', pres, r2' >> ->
pres = true ->
P l1 l2' (
inter l1 l2')
->
P r1 r2' (
inter r1 r2')
->
P (
Node _x l1 x1 r1) (
Node _x0 _x1 _x2 _x3) (
join (
inter l1 l2')
x1 (
inter r1 r2'))
) ->
(forall (
s1 s2 :
tree) (
_x :
I.t) (
l1 :
tree) (
x1 :
X.t) (
r1 :
tree),
s1 = Node _x l1 x1 r1 ->
forall (
_x0 :
I.t) (
_x1 :
tree) (
_x2 :
X.t) (
_x3 :
tree),
s2 = Node _x0 _x1 _x2 _x3 ->
forall (
l2' :
t) (
pres :
bool) (
r2' :
t),
split x1 s2 = << l2', pres, r2' >> ->
pres = false ->
P l1 l2' (
inter l1 l2')
->
P r1 r2' (
inter r1 r2')
->
P (
Node _x l1 x1 r1) (
Node _x0 _x1 _x2 _x3) (
concat (
inter l1 l2') (
inter r1 r2'))
) ->
forall s1 s2 :
tree,
P s1 s2 (
inter s1 s2).
Lemma diff_ind [
P :
tree -> tree -> tree -> Prop] :
(forall s1 s2 :
tree,
s1 = Leaf -> P Leaf s2 Leaf) ->
(forall (
s1 s2 :
tree) (
_x :
I.t) (
l1 :
tree) (
x1 :
X.t) (
r1 :
tree),
s1 = Node _x l1 x1 r1 -> s2 = Leaf -> P (
Node _x l1 x1 r1)
Leaf s1) ->
(forall (
s1 s2 :
tree) (
_x :
I.t) (
l1 :
tree) (
x1 :
X.t) (
r1 :
tree),
s1 = Node _x l1 x1 r1 ->
forall (
_x0 :
I.t) (
_x1 :
tree) (
_x2 :
X.t) (
_x3 :
tree),
s2 = Node _x0 _x1 _x2 _x3 ->
forall (
l2' :
t) (
pres :
bool) (
r2' :
t),
split x1 s2 = << l2', pres, r2' >> ->
pres = true ->
P l1 l2' (
diff l1 l2')
->
P r1 r2' (
diff r1 r2')
->
P (
Node _x l1 x1 r1) (
Node _x0 _x1 _x2 _x3) (
concat (
diff l1 l2') (
diff r1 r2'))
) ->
(forall (
s1 s2 :
tree) (
_x :
I.t) (
l1 :
tree) (
x1 :
X.t) (
r1 :
tree),
s1 = Node _x l1 x1 r1 ->
forall (
_x0 :
I.t) (
_x1 :
tree) (
_x2 :
X.t) (
_x3 :
tree),
s2 = Node _x0 _x1 _x2 _x3 ->
forall (
l2' :
t) (
pres :
bool) (
r2' :
t),
split x1 s2 = << l2', pres, r2' >> ->
pres = false ->
P l1 l2' (
diff l1 l2')
->
P r1 r2' (
diff r1 r2')
->
P (
Node _x l1 x1 r1) (
Node _x0 _x1 _x2 _x3) (
join (
diff l1 l2')
x1 (
diff r1 r2'))
) ->
forall s1 s2 :
tree,
P s1 s2 (
diff s1 s2).
Lemma union_ind [
P :
tree -> tree -> tree -> Prop] :
(forall s1 s2 :
tree,
s1 = Leaf -> P Leaf s2 s2) ->
(forall (
s1 s2 :
tree) (
_x :
I.t) (
l1 :
tree) (
x1 :
X.t) (
r1 :
tree),
s1 = Node _x l1 x1 r1 -> s2 = Leaf -> P (
Node _x l1 x1 r1)
Leaf s1) ->
(forall (
s1 s2 :
tree) (
_x :
I.t) (
l1 :
tree) (
x1 :
X.t) (
r1 :
tree),
s1 = Node _x l1 x1 r1 ->
forall (
_x0 :
I.t) (
_x1 :
tree) (
_x2 :
X.t) (
_x3 :
tree),
s2 = Node _x0 _x1 _x2 _x3 ->
forall (
l2' :
t) (
_x4 :
bool) (
r2' :
t),
split x1 s2 = << l2', _x4, r2' >> ->
P l1 l2' (
union l1 l2')
->
P r1 r2' (
union r1 r2')
->
P (
Node _x l1 x1 r1) (
Node _x0 _x1 _x2 _x3) (
join (
union l1 l2')
x1 (
union r1 r2'))
) ->
forall s1 s2 :
tree,
P s1 s2 (
union s1 s2).
Notations and helper lemma about pairs and triples
Declare Scope pair_scope.
Notation "s #1" := (
fst s) (
at level 9,
format "s '#1'") :
pair_scope.
Notation "s #2" := (
snd s) (
at level 9,
format "s '#2'") :
pair_scope.
Notation "t #l" := (
t_left t) (
at level 9,
format "t '#l'") :
pair_scope.
Notation "t #b" := (
t_in t) (
at level 9,
format "t '#b'") :
pair_scope.
Notation "t #r" := (
t_right t) (
at level 9,
format "t '#r'") :
pair_scope.
Local Open Scope pair_scope.
Join
Function/Functional Scheme can't deal with internal fix.
Let's do its job by hand:
Ltac join_tac :=
let l :=
fresh "l"
in
intro l;
induction l as [|
lh ll _ lx lr Hlr];
[ |
intros x r;
induction r as [|
rh rl Hrl rx rr _];
unfold join;
[ |
destruct (
(rh+2
) <? lh)
eqn:
LT;
[
match goal with |-
context b [
bal ?
a ?
b ?
c] =>
replace (
bal a b c)
with (
bal ll lx (
join lr x (
Node rh rl rx rr))); [ |
auto]
end
|
destruct (
(lh+2
) <? rh)
eqn:
LT';
[
match goal with |-
context b [
bal ?
a ?
b ?
c] =>
replace (
bal a b c)
with (
bal (
join (
Node lh ll lx lr)
x rl)
rx rr); [ |
auto]
end
| ] ] ] ];
intros.
Lemma join_spec :
forall l x r y,
InT y (
join l x r)
<-> X.eq y x \/ InT y l \/ InT y r.
#[
global]
Instance join_ok :
forall l x r `(
Ok l,
Ok r,
lt_tree x l,
gt_tree x r),
Ok (
join l x r).
Extraction of minimum element
Encapsulation
Now, in order to really provide a functor implementing
S, we
need to encapsulate everything into a type of binary search trees.
They also happen to be well-balanced, but this has no influence
on the correctness of operations, so we won't state this here,
see
MSetFullAVL if you need more than just the MSet interface.