B-tree deletion: rotations preserve Sorted and ChildBounded
This submodule collects the ordering and key-range preservation lemmas for the
two sibling rotations of CLRS B-TREE-DELETE case 3a: rotateRight
(the underflowing left child borrows the separator and the right sibling's
first child) and rotateLeft (the symmetric borrow from the left
sibling). For each rotation, both result nodes are shown to preserve the
Sorted invariant and the ChildBounded key-range invariant, given
the corresponding invariants on the two input siblings plus the
separator-ordering facts (hL_le, hR_ge) supplied by the parent's
ChildBounded invariant.
The hypothesis shape and proof technique mirror mergeNodes_sorted and
mergeNodes_childBounded: pairwise ordering of concatenated key lists,
and per-child bound transfer via getElem/getElem? index arithmetic over
the split and joined child lists.
namespace CLRSnamespace Chapter18namespace BTree
rotateRight: the new left node preserves Sorted
rotateRight new-left node is Sorted. The borrowed separator becomes
the new last key of the left child; hL_le (every key of the left sibling is
at most sep) is exactly what keeps the extended key list pairwise ordered.
The moved child rCh[0] inherits Sorted from the right sibling.
lemma rotateRight_sorted_left
{lKeys rTail : List Nat} {lCh rCh : List BTree} {sep rHead : Nat}
(hL_s : Sorted (node lKeys lCh)) (hR_s : Sorted (node (rHead :: rTail) rCh))
(hL_le : ∀ k ∈ keysOf (node lKeys lCh), k ≤ sep) (hsep : sep ≤ rHead) :
Sorted (node (lKeys ++ [sep]) (lCh ++ rCh.take 1)) := by
unfold Sorted at hL_s hR_s ⊢
obtain ⟨hL_pw, hL_ch⟩ := hL_s
obtain ⟨hR_pw, hR_ch⟩ := hR_s
refine ⟨?_, ?_⟩
· -- Pairwise (lKeys ++ [sep])
rw [List.pairwise_append]
refine ⟨hL_pw, by simp, ?_⟩
intro a ha b hb
simp only [List.mem_singleton] at hb
subst hb
exact hL_le a (by simp [keysOf, ha])
· -- children inherit Sorted from the two siblings
intro c hc
rw [List.mem_append] at hc
rcases hc with hc | hc
· exact hL_ch c hc
· exact hR_ch c (List.mem_of_mem_take hc)
rotateRight new-right node is Sorted. Dropping the first key and the
first child of the right sibling keeps both components of Sorted.
lemma rotateRight_sorted_right {rHead : Nat} {rTail : List Nat} {rCh : List BTree}
(hR_s : Sorted (node (rHead :: rTail) rCh)) :
Sorted (node rTail (rCh.drop 1)) := by
unfold Sorted at hR_s ⊢
obtain ⟨hR_pw, hR_ch⟩ := hR_s
exact ⟨(List.pairwise_cons.mp hR_pw).2,
fun c hc => hR_ch c ((List.drop_subset 1 rCh) hc)⟩
rotateRight: the new nodes preserve ChildBounded
rotateRight new-left node is ChildBounded. The appended child
rCh[0] sits between the new last key sep (lower bound, from hR_ge) and
no upper key; every other child keeps its original neighboring keys.
lemma rotateRight_childBounded_left
{lKeys rTail : List Nat} {lCh rCh : List BTree} {sep rHead : Nat}
(hL_cb : ChildBounded (node lKeys lCh))
(hR_cb : ChildBounded (node (rHead :: rTail) rCh))
(hshape : (lCh = []) ↔ (rCh = []))
(hL_le : ∀ k ∈ keysOf (node lKeys lCh), k ≤ sep)
(hR_ge : ∀ k ∈ keysOf (node (rHead :: rTail) rCh), sep ≤ k) :
ChildBounded (node (lKeys ++ [sep]) (lCh ++ rCh.take 1)) := by
unfold ChildBounded at hL_cb hR_cb ⊢
obtain ⟨hL_rel, hL_bounds, hL_sub⟩ := hL_cb
obtain ⟨hR_rel, hR_bounds, hR_sub⟩ := hR_cb
have hL_len : lCh = [] ∨ lCh.length = lKeys.length + 1 := by
rcases hL_rel with hLe | hLlen
· left; cases lCh with | nil => rfl | cons x xs => simp at hLe
· right; exact hLlen
have hR_len : rCh = [] ∨ rCh.length = (rHead :: rTail).length + 1 := by
rcases hR_rel with hRe | hRlen
· left; cases rCh with | nil => rfl | cons x xs => simp at hRe
· right; exact hRlen
refine ⟨?_, ?_, ?_⟩
· -- component 1: children count
rcases hL_len with hl | hLlen
· have hr : rCh = [] := hshape.mp hl
subst hl; subst hr; left; rfl
· rcases hR_len with hr | hRlen
· have hl0 : lCh = [] := hshape.mpr hr
rw [hl0] at hLlen; simp at hLlen
· right
simp only [List.length_append, List.length_take, List.length_cons,
List.length_nil, hLlen]
simp only [List.length_cons] at hRlen
omega
· -- component 2: per-child key bounds
intro i hi
by_cases hlCh : lCh = []
· have hrCh : rCh = [] := hshape.mp hlCh
subst hlCh; subst hrCh; simp at hi
· have hrCh : rCh ≠ [] := fun h => hlCh (hshape.mpr h)
have hLlen : lCh.length = lKeys.length + 1 := by
rcases hL_len with h | h
· exact absurd h hlCh
· exact h
have hRpos : 0 < rCh.length := by
rcases hR_len with h | h
· exact absurd h hrCh
· simp only [List.length_cons] at h; omega
have htake1 : (rCh.take 1).length = 1 := by
rw [List.length_take]; omega
refine ⟨?_, ?_⟩
· -- lower bound: (lKeys ++ [sep])[i-1]? bounds child i from below
rcases Nat.eq_zero_or_pos i with hi0 | hipos
· exact Or.inl hi0
· right
by_cases hiL : i < lCh.length
· -- child in the left segment: lower key is lKeys[i-1]
have hi1 : i - 1 < lKeys.length := by omega
have hchild : (lCh ++ rCh.take 1).get ⟨i, hi⟩ = lCh.get ⟨i, hiL⟩ :=
List.getElem_append_left hiL
have heq : (lKeys ++ [sep])[i-1]? = lKeys[i-1]? :=
List.getElem?_append_left (by omega)
rw [heq, List.getElem?_eq_getElem hi1]
have hb := (hL_bounds i hiL).1
rcases hb with h0 | hb
· omega
· simp only [List.getElem?_eq_getElem hi1] at hb
intro k hk
rw [hchild] at hk
exact hb k hk
· -- child is the moved rCh[0]: lower key is the separator
have hlo : (lKeys ++ [sep])[i-1]? = some sep := by
have e : i - 1 = lKeys.length := by
have hi' := hi
rw [List.length_append, htake1] at hi'
omega
rw [e, List.getElem?_append_right (Nat.le_refl _)]
simp
rw [hlo]
intro k hk
have hchild : (lCh ++ rCh.take 1).get ⟨i, hi⟩ = rCh.get ⟨0, hRpos⟩ := by
have hpi : i - lCh.length < (rCh.take 1).length := by
rw [htake1]
have hi' := hi
rw [List.length_append, htake1] at hi'
omega
have h1 : (lCh ++ rCh.take 1).get ⟨i, hi⟩ =
(rCh.take 1).get ⟨i - lCh.length, hpi⟩ :=
List.getElem_append_right (Nat.le_of_not_lt hiL)
rw [h1]
have hopt : (rCh.take 1)[i - lCh.length]? = rCh[0]? := by
have e : i - lCh.length = 0 := by
have hi' := hi
rw [List.length_append, htake1] at hi'
omega
rw [e, List.getElem?_take_of_lt Nat.zero_lt_one]
have ha := List.getElem?_eq_getElem hpi
have hb := List.getElem?_eq_getElem hRpos
rw [hopt] at ha
rw [ha] at hb
exact Option.some.inj hb
rw [hchild] at hk
have hmem : k ∈ keysOf (node (rHead :: rTail) rCh) := by
simp only [keysOf, List.mem_append, List.mem_flatMap]
exact Or.inr ⟨rCh.get ⟨0, hRpos⟩, List.getElem_mem _, hk⟩
exact hR_ge k hmem
· -- upper bound: (lKeys ++ [sep])[i]? bounds child i from above
by_cases hiL : i < lCh.length
· -- child in the left segment
have hchild : (lCh ++ rCh.take 1).get ⟨i, hi⟩ = lCh.get ⟨i, hiL⟩ :=
List.getElem_append_left hiL
by_cases hiK : i < lKeys.length
· -- upper key is lKeys[i]
have heq : (lKeys ++ [sep])[i]? = lKeys[i]? :=
List.getElem?_append_left hiK
rw [heq, List.getElem?_eq_getElem hiK]
have hub := (hL_bounds i hiL).2
simp only [List.getElem?_eq_getElem hiK] at hub
intro k hk
rw [hchild] at hk
exact hub k hk
· -- child is lCh[lKeys.length]: upper key is the separator
have hieq : i = lKeys.length := by omega
have heq : (lKeys ++ [sep])[i]? = some sep := by
rw [hieq, List.getElem?_append_right (Nat.le_refl _)]
simp
rw [heq]
intro k hk
rw [hchild] at hk
have hmem : k ∈ keysOf (node lKeys lCh) := by
simp only [keysOf, List.mem_append, List.mem_flatMap]
exact Or.inr ⟨lCh.get ⟨i, hiL⟩, List.getElem_mem _, hk⟩
exact hL_le k hmem
· -- child is the moved rCh[0]: no upper key
have hnone : (lKeys ++ [sep])[i]? = none := by
apply List.getElem?_eq_none
have hieq : i = lCh.length := by
have hi' := hi
rw [List.length_append, htake1] at hi'
omega
simp only [List.length_append, List.length_cons, List.length_nil]
omega
rw [hnone]
exact trivial
· -- component 3: recursive ChildBounded on children
intro c hc
rw [List.mem_append] at hc
rcases hc with hc | hc
· exact hL_sub c hc
· exact hR_sub c (List.mem_of_mem_take hc)
rotateRight new-right node is ChildBounded. Dropping the first key
and first child is the d = 1 case of childBounded_drop_of_full; the leaf
case keeps an empty child list.
lemma rotateRight_childBounded_right {rHead : Nat} {rTail : List Nat} {rCh : List BTree}
(hR_cb : ChildBounded (node (rHead :: rTail) rCh)) :
ChildBounded (node rTail (rCh.drop 1)) := by
by_cases hr : rCh = []
· subst hr
exact childBounded_node_nil rTail
· have hlen : rCh.length = (rHead :: rTail).length + 1 :=
(childBounded_children_rel hR_cb).resolve_left hr
have h1 : 1 < rCh.length := by
simp only [List.length_cons] at hlen; omega
exact childBounded_drop_of_full hR_cb (d := 1) (by omega) h1
rotateLeft: the new nodes preserve Sorted
rotateLeft new-left node is Sorted. The left sibling loses its last
key and last child; truncating preserves both components of Sorted.
lemma rotateLeft_sorted_left {lHead : Nat} {lTail : List Nat} {lCh : List BTree}
(hL_s : Sorted (node (lHead :: lTail) lCh)) :
Sorted (node (lHead :: lTail).dropLast (lCh.take (lCh.length - 1))) := by
unfold Sorted at hL_s ⊢
obtain ⟨hL_pw, hL_ch⟩ := hL_s
exact ⟨hL_pw.sublist (List.dropLast_sublist _),
fun c hc => hL_ch c (List.mem_of_mem_take hc)⟩
rotateLeft new-right node is Sorted. The separator becomes the new
first key of the right child; hR_ge (every key of the right sibling is at
least sep) keeps the extended key list pairwise ordered. The moved child
inherits Sorted from the left sibling.
lemma rotateLeft_sorted_right
{lHead : Nat} {lTail rKeys : List Nat} {lCh rCh : List BTree} {sep : Nat}
(hL_s : Sorted (node (lHead :: lTail) lCh)) (hR_s : Sorted (node rKeys rCh))
(_hL_le : ∀ k ∈ keysOf (node (lHead :: lTail) lCh), k ≤ sep)
(hR_ge : ∀ k ∈ keysOf (node rKeys rCh), sep ≤ k) :
Sorted (node (sep :: rKeys) (lCh.drop (lCh.length - 1) ++ rCh)) := by
unfold Sorted at hL_s hR_s ⊢
obtain ⟨hL_pw, hL_ch⟩ := hL_s
obtain ⟨hR_pw, hR_ch⟩ := hR_s
refine ⟨?_, ?_⟩
· -- Pairwise (sep :: rKeys)
apply List.Pairwise.cons
· intro k hk
exact hR_ge k (by simp [keysOf, hk])
· exact hR_pw
· -- children inherit Sorted from the two siblings
intro c hc
rw [List.mem_append] at hc
rcases hc with hc | hc
· exact hL_ch c ((List.drop_subset _ _) hc)
· exact hR_ch c hc
rotateLeft: the new nodes preserve ChildBounded
rotateLeft new-left node is ChildBounded. Dropping the last key and
last child is truncation to m = lTail.length, i.e. childBounded_take_of_full
with dropLast = take (length - 1); the leaf case keeps an empty child list.
lemma rotateLeft_childBounded_left {lHead : Nat} {lTail : List Nat} {lCh : List BTree}
(hL_cb : ChildBounded (node (lHead :: lTail) lCh)) :
ChildBounded (node (lHead :: lTail).dropLast (lCh.take (lCh.length - 1))) := by
by_cases hl : lCh = []
· subst hl
simp only [List.take_nil]
exact childBounded_node_nil _
· have hlen : lCh.length = (lHead :: lTail).length + 1 :=
(childBounded_children_rel hL_cb).resolve_left hl
rw [List.dropLast_eq_take]
simp only [List.length_cons] at hlen
have e1 : (lHead :: lTail).length - 1 = lTail.length := by simp
have e2 : lCh.length - 1 = lTail.length + 1 := by omega
rw [e1, e2]
exact childBounded_take_of_full hL_cb (by simp only [List.length_cons]; omega)
rotateLeft new-right node is ChildBounded. The prepended child (the
left sibling's last child) sits between no lower key and the new first key
sep (upper bound, from hL_le); every other child keeps its original
neighboring keys, shifted by one.
lemma rotateLeft_childBounded_right
{lHead : Nat} {lTail rKeys : List Nat} {lCh rCh : List BTree} {sep : Nat}
(hL_cb : ChildBounded (node (lHead :: lTail) lCh))
(hR_cb : ChildBounded (node rKeys rCh))
(hshape : (lCh = []) ↔ (rCh = []))
(hL_le : ∀ k ∈ keysOf (node (lHead :: lTail) lCh), k ≤ sep)
(hR_ge : ∀ k ∈ keysOf (node rKeys rCh), sep ≤ k) :
ChildBounded (node (sep :: rKeys) (lCh.drop (lCh.length - 1) ++ rCh)) := by
unfold ChildBounded at hL_cb hR_cb ⊢
obtain ⟨hL_rel, hL_bounds, hL_sub⟩ := hL_cb
obtain ⟨hR_rel, hR_bounds, hR_sub⟩ := hR_cb
have hL_len : lCh = [] ∨ lCh.length = (lHead :: lTail).length + 1 := by
rcases hL_rel with hLe | hLlen
· left; cases lCh with | nil => rfl | cons x xs => simp at hLe
· right; exact hLlen
have hR_len : rCh = [] ∨ rCh.length = rKeys.length + 1 := by
rcases hR_rel with hRe | hRlen
· left; cases rCh with | nil => rfl | cons x xs => simp at hRe
· right; exact hRlen
refine ⟨?_, ?_, ?_⟩
· -- component 1: children count
rcases hL_len with hl | hLlen
· have hr : rCh = [] := hshape.mp hl
subst hl; subst hr; left; rfl
· rcases hR_len with hr | hRlen
· have hl0 : lCh = [] := hshape.mpr hr
rw [hl0] at hLlen; simp at hLlen
· right
have hdroplen1 : (lCh.drop (lCh.length - 1)).length = 1 := by
rw [List.length_drop]
simp only [List.length_cons] at hLlen
omega
simp only [List.length_append, List.length_cons, hdroplen1]
omega
· -- component 2: per-child key bounds
intro i hi
by_cases hlCh : lCh = []
· have hrCh : rCh = [] := hshape.mp hlCh
subst hlCh; subst hrCh; simp at hi
· have hrCh : rCh ≠ [] := fun h => hlCh (hshape.mpr h)
have hLlen : lCh.length = lTail.length + 2 := by
rcases hL_len with h | h
· exact absurd h hlCh
· simp only [List.length_cons] at h; omega
have hRlen : rCh.length = rKeys.length + 1 := by
rcases hR_len with h | h
· exact absurd h hrCh
· exact h
have hdroplen : (lCh.drop (lCh.length - 1)).length = 1 := by
rw [List.length_drop]; omega
refine ⟨?_, ?_⟩
· -- lower bound: (sep :: rKeys)[i-1]? bounds child i from below
rcases Nat.eq_zero_or_pos i with hi0 | hipos
· exact Or.inl hi0
· right
have hj : i - 1 < rCh.length := by
have hi' := hi
rw [List.length_append, hdroplen] at hi'
omega
have hchild : (lCh.drop (lCh.length - 1) ++ rCh).get ⟨i, hi⟩ =
rCh.get ⟨i - 1, hj⟩ := by
have hopt : (lCh.drop (lCh.length - 1) ++ rCh)[i]? = rCh[i - 1]? := by
rw [List.getElem?_append_right (by rw [hdroplen]; omega), hdroplen]
have ha := List.getElem?_eq_getElem hi
have hb := List.getElem?_eq_getElem hj
rw [hopt] at ha
rw [ha] at hb
exact Option.some.inj hb
by_cases hj0 : i - 1 = 0
· -- child is rCh[0]: lower key is the separator
have h0 : (sep :: rKeys)[i-1]? = some sep := by simp [hj0]
rw [h0]
intro k hk
rw [hchild] at hk
have hmem : k ∈ keysOf (node rKeys rCh) := by
simp only [keysOf, List.mem_append, List.mem_flatMap]
exact Or.inr ⟨rCh.get ⟨i - 1, hj⟩, List.getElem_mem _, hk⟩
exact hR_ge k hmem
· -- lower key is rKeys[i-2]
have hj1 : i - 1 - 1 < rKeys.length := by omega
have hcons : (sep :: rKeys)[i-1]? = rKeys[i-1-1]? := by
conv_lhs => rw [show i - 1 = (i - 1 - 1) + 1 from by omega]
exact List.getElem?_cons_succ
rw [hcons, List.getElem?_eq_getElem hj1]
have hb := (hR_bounds (i - 1) hj).1
rcases hb with h0' | hb
· omega
· simp only [List.getElem?_eq_getElem hj1] at hb
intro k hk
rw [hchild] at hk
exact hb k hk
· -- upper bound: (sep :: rKeys)[i]? bounds child i from above
by_cases hi0 : i = 0
· -- child is the moved lCh-last child: upper key is the separator
subst hi0
have h0 : (sep :: rKeys)[0]? = some sep := by simp
rw [h0]
intro k hk
have hpos : lCh.length - 1 < lCh.length := by omega
have hchild : (lCh.drop (lCh.length - 1) ++ rCh).get ⟨0, hi⟩ =
lCh.get ⟨lCh.length - 1, hpos⟩ := by
have hopt : (lCh.drop (lCh.length - 1) ++ rCh)[0]? = lCh[lCh.length - 1]? := by
rw [List.getElem?_append_left (by rw [hdroplen]; omega),
List.getElem?_drop, Nat.add_zero]
have ha := List.getElem?_eq_getElem hi
have hb := List.getElem?_eq_getElem hpos
rw [hopt] at ha
rw [ha] at hb
exact Option.some.inj hb
rw [hchild] at hk
have hmem : k ∈ keysOf (node (lHead :: lTail) lCh) := by
simp only [keysOf, List.mem_append, List.mem_flatMap]
exact Or.inr ⟨lCh.get ⟨lCh.length - 1, hpos⟩, List.getElem_mem _, hk⟩
exact hL_le k hmem
· -- child is rCh[i-1]: upper key is rKeys[i-1]
have hj : i - 1 < rCh.length := by
have hi' := hi
rw [List.length_append, hdroplen] at hi'
omega
have hchild : (lCh.drop (lCh.length - 1) ++ rCh).get ⟨i, hi⟩ =
rCh.get ⟨i - 1, hj⟩ := by
have hopt : (lCh.drop (lCh.length - 1) ++ rCh)[i]? = rCh[i - 1]? := by
rw [List.getElem?_append_right (by rw [hdroplen]; omega), hdroplen]
have ha := List.getElem?_eq_getElem hi
have hb := List.getElem?_eq_getElem hj
rw [hopt] at ha
rw [ha] at hb
exact Option.some.inj hb
have heq : (sep :: rKeys)[i]? = rKeys[i-1]? := by
conv_lhs => rw [show i = (i - 1) + 1 from by omega]
exact List.getElem?_cons_succ
rw [heq]
by_cases hjK : i - 1 < rKeys.length
· rw [List.getElem?_eq_getElem hjK]
have hub := (hR_bounds (i - 1) hj).2
simp only [List.getElem?_eq_getElem hjK] at hub
intro k hk
rw [hchild] at hk
exact hub k hk
· -- child is rCh[rKeys.length]: no upper key
have hnone : rKeys[i-1]? = none := List.getElem?_eq_none (by omega)
rw [hnone]
exact trivial
· -- component 3: recursive ChildBounded on children
intro c hc
rw [List.mem_append] at hc
rcases hc with hc | hc
· exact hL_sub c ((List.drop_subset _ _) hc)
· exact hR_sub c hcend BTreeend Chapter18end CLRS