Theory StronglyJoinableAVL
section "Strongly Joinable AVL Trees"
theory StronglyJoinableAVL
imports
"StronglyJoinable"
"HOL-Data_Structures.AVL_Set"
begin
text ‹
As a proof of concept, the @{locale StronglyJoinable} framework is instantiated for
the AVL trees of the standard library, with the height as rank and balance constants
$c_l = 1$ and $c_u = 2$. Instantiating the abstract results then yields fully
numeric running-time bounds for union, intersection and difference on AVL trees.
›
subsection "Join for AVL Trees"
text ‹
Following \<^cite>‹blelloch2022joinable›, ‹join› walks down the spine of the taller tree until it
reaches a subtree whose height is within one of the smaller tree, attaches the
smaller tree together with the pivot there, and rebalances on the way back up with
the standard AVL rotations (@{const balL}, @{const balR}).
›
fun joinR where
"joinR L a R = (case L of Node l (k,_) r ⇒
if ht r ≤ ht R + 1
then balR l k (node r a R)
else balR l k (joinR r a R))"
declare joinR.simps[simp del]
fun joinL where
"joinL L a R = (case R of Node l (k,_) r ⇒
if ht l ≤ ht L + 1
then balL (node L a l) k r
else balL (joinL L a l) k r)"
declare joinL.simps[simp del]
fun join where
"join l a r =
(if ht l > ht r + 1 then joinR l a r
else if ht r > ht l + 1 then joinL l a r
else node l a r)"
subsection "Proof of Correctness"
text ‹
The @{locale Set2_Join} interpretation requires the set semantics of ‹join›,
preservation of the search-tree order, and preservation of the AVL invariant.
›
subsubsection "Set Preservation"
lemma balR_set:"set_tree (balR l a r) = set_tree
l ∪ {a} ∪ set_tree r"
by (auto simp: balR_def node_def split!: if_splits tree.splits)
lemma joinR_set:"ht l > ht r + 1 ⟹ set_tree (joinR l a r) = set_tree
l ∪ {a} ∪ set_tree r"
proof(induction l a r rule: joinR.induct)
case (1 L a R)
then show ?case
by (auto simp: joinR.simps[of L a R] balR_set node_def split!: if_splits tree.splits)
qed
lemma balL_set:"set_tree (balL l a r) = set_tree
l ∪ {a} ∪ set_tree r"
by (auto simp: balL_def node_def split!: if_splits tree.splits)
lemma joinL_set:"ht r > ht l + 1 ⟹ set_tree (joinL l a r) = set_tree
l ∪ {a} ∪ set_tree r"
proof(induction l a r rule: joinL.induct)
case (1 L a R)
then show ?case
by (auto simp: joinL.simps[of L a R] balL_set node_def split!: if_splits tree.splits)
qed
corollary set_join:"set_tree (join l a r) = set_tree l ∪ {a} ∪ set_tree r"
by(auto simp: node_def joinR_set joinL_set split!: if_splits)
subsubsection "BST Preservation"
lemma balR_bst:"⟦bst l; bst r; ∀x∈set_tree l. x < a; ∀y∈set_tree r. a < y⟧
⟹ bst (balR l a r)"
by (auto simp: balR_def node_def split!: if_splits tree.splits)
lemma joinR_bst:"⟦ht l > ht r + 1; bst l; bst r; ∀x∈set_tree l. x < a; ∀y∈set_tree r. a < y⟧
⟹ bst (joinR l a r)"
proof(induction l a r arbitrary: b rule: joinR.induct)
case (1 L a R)
then show ?case
by (auto simp: joinR.simps[of L a R] node_def balR_bst joinR_set ball_Un
intro!: balR_bst split!: if_splits tree.splits)
qed
lemma balL_bst:"⟦bst l; bst r; ∀x∈set_tree l. x < a; ∀y∈set_tree r. a < y⟧
⟹ bst (balL l a r)"
by (auto simp: balL_def node_def split!: if_splits tree.splits)
lemma joinL_bst:"⟦ht r > ht l + 1; bst l; bst r; ∀x∈set_tree l. x < a; ∀y∈set_tree r. a < y⟧
⟹ bst (joinL l a r)"
proof(induction l a r arbitrary: b rule: joinL.induct)
case (1 L a R)
then show ?case
by (auto simp: joinL.simps[of L a R] node_def balL_bst joinL_set ball_Un
intro!: balL_bst split!: if_splits tree.splits)
qed
corollary bst_join:"bst (Node l (a, b) r) ⟹ bst (join l a r)"
by(auto simp: node_def joinR_bst joinL_bst split!: if_splits)
subsubsection "Preservation of AVL Predicate"
lemma avl_joinR_height:"⟦avl l; avl r; height l > height r + 1⟧
⟹ avl (joinR l a r) ∧ height (joinR l a r) ∈ {height l, height l + 1}"
proof(induction l a r rule: joinR.induct)
case (1 L a R)
then show ?case
by(fastforce simp: joinR.simps[of L a R] balR_def node_def max_absorb2 split!: if_splits tree.splits)
qed
lemma avl_joinL_height:"⟦avl l; avl r; height r > height l + 1⟧
⟹ avl (joinL l a r) ∧ height (joinL l a r) ∈ {height r, height r + 1}"
proof(induction l a r rule: joinL.induct)
case (1 L a R)
then show ?case
by(fastforce simp: joinL.simps[of L a R] balL_def node_def max_absorb2 split!: if_splits tree.splits)
qed
corollary inv_join:"⟦avl l; avl r⟧ ⟹ avl (join l a r)"
by(auto simp: node_def avl_joinR_height avl_joinL_height split: if_splits)
text ‹To finish the proof of functional correctness, instantiate the ‹Set2_Join› locale.›