Imports
import MathlibCLRS Section 13.1 - Red-black trees
This section starts the red-black-tree development with local invariants and rotation facts. It does not yet formalize the full insertion or deletion algorithms. Instead it proves reusable lemmas that those algorithms will need: rotations preserve membership, repainting the root black preserves the no-red-red property, repainting the root preserves membership, and repainting the root preserves child black-height balance. It also proves red-red local repair certificates and bundles the local red-black shape invariants into one reusable predicate.
Main results:
-
Theorem
RBTree.inTree_rotateLeft_iff: left rotation preserves tree membership. -
Theorem
RBTree.inTree_rotateRight_iff: right rotation preserves tree membership. -
Theorem
RBTree.noRedRed_repaint_black: repainting the root black preserves the no-red-red invariant. -
Theorem
RBTree.inTree_repaintRoot_iff: repainting the root preserves membership. -
Theorem
RBTree.balancedBlackHeight_repaintRoot: repainting the root preserves balanced child black heights. -
Theorem
RBTree.balancedBlackHeight_rotateLeft_red_red: left rotation across a red-red edge preserves child black-height balance. -
Theorem
RBTree.balancedBlackHeight_rotateRight_red_red: right rotation across a red-red edge preserves child black-height balance. -
Theorem
RBTree.redBlackShape_repaint_rotateLeft_red_red: the left red-red rotation case followed by repainting the new root black establishes the bundled local red-black shape invariant. -
Theorem
RBTree.redBlackShape_repaint_rotateRight_red_red: the symmetric right red-red rotation case followed by repainting the new root black establishes the bundled local red-black shape invariant. -
Theorem
RBTree.redBlackShape_repaint_black: repainting the root black establishes the bundled local red-black shape invariant. -
Theorems
RBTree.redBlackShape_insertFixup_leftLeft,RBTree.redBlackShape_insertFixup_leftRight,RBTree.redBlackShape_insertFixup_rightLeft, andRBTree.redBlackShape_insertFixup_rightRight: four localRB-INSERT-FIXUProtation/recoloring cases establish the bundled shape invariant. -
Theorems
RBTree.blackHeight_insertFixup_leftLeft,RBTree.blackHeight_insertFixup_leftRight,RBTree.blackHeight_insertFixup_rightLeft, andRBTree.blackHeight_insertFixup_rightRight: the same four local insertion-fixup rewrites preserve the subtree black height needed by a parent context. -
Theorems
RBTree.insertFixupLocal_leftLeft_certificate,RBTree.insertFixupLocal_leftRight_certificate,RBTree.insertFixupLocal_rightLeft_certificate, andRBTree.insertFixupLocal_rightRight_certificate: the four branch-specific facts are bundled behind one local dispatcher and certificate interface.
Current gaps:
-
The executable CLRS
RB-INSERT,RB-INSERT-FIXUP,RB-DELETE, andRB-DELETE-FIXUPalgorithms remain future work.
namespace CLRSnamespace Chapter13Colored tree model
The two colors used by a red-black tree node.
inductive Color where
| red
| black
deriving Repr, DecidableEqA colored binary tree of natural-number keys.
inductive RBTree where
| empty : RBTree
| node : Color → RBTree → Nat → RBTree → RBTree
deriving Repr, DecidableEqnamespace RBTreeMembership of a key in a colored binary tree.
def InTree (x : Nat) : RBTree → Prop
| empty => False
| node _ left key right => x = key ∨ InTree x left ∨ InTree x rightThe root is black; empty trees count as black leaves.
def RootBlack : RBTree → Prop
| empty => True
| node color _ _ _ => color = Color.blackNo red node has a red child.
def NoRedRed : RBTree → Prop
| empty => True
| node color left _ right =>
NoRedRed left ∧ NoRedRed right ∧
(color = Color.red → RootBlack left ∧ RootBlack right)
The black height measured along the left spine. This is meaningful together
with BalancedBlackHeight, which states that both child subtrees have the
same black height at every node.
def blackHeight : RBTree → Nat
| empty => 0
| node color left _ _ =>
blackHeight left + if color = Color.black then 1 else 0Every node has left and right subtrees with equal black height.
def BalancedBlackHeight : RBTree → Prop
| empty => True
| node _ left _ right =>
BalancedBlackHeight left ∧ BalancedBlackHeight right ∧
blackHeight left = blackHeight rightThe local red-black shape invariant used by this first model: the root is black, there is no red-red edge, and child black heights are balanced at every node.
def RedBlackShape (t : RBTree) : Prop :=
RootBlack t ∧ NoRedRed t ∧ BalancedBlackHeight tRotations preserve membership
The local left rotation used by red-black tree balancing.
def rotateLeft : RBTree → RBTree
| node color a x (node rightColor b y c) =>
node rightColor (node color a x b) y c
| t => tThe local right rotation used by red-black tree balancing.
def rotateRight : RBTree → RBTree
| node color (node leftColor a x b) y c =>
node leftColor a x (node color b y c)
| t => tLeft rotation preserves membership of keys.
theorem inTree_rotateLeft_iff (x : Nat) (t : RBTree) :
InTree x (rotateLeft t) ↔ InTree x t :=
cases t with
cases right with
Right rotation preserves membership of keys.
theorem inTree_rotateRight_iff (x : Nat) (t : RBTree) :
InTree x (rotateRight t) ↔ InTree x t :=
cases t with
cases left with
Local red-black invariants
Repaint the root of a nonempty tree, leaving empty trees unchanged.
def repaintRoot (color : Color) : RBTree → RBTree
| empty => empty
| node _ left key right => node color left key rightRepainting the root preserves membership of keys.
theorem inTree_repaintRoot_iff (color : Color) (x : Nat) (t : RBTree) :
InTree x (repaintRoot color t) ↔ InTree x t :=
A red node satisfying NoRedRed has black children.
theorem red_node_children_black {left right : RBTree} {key : Nat}
(h : NoRedRed (node Color.red left key right)) :
RootBlack left ∧ RootBlack right :=
Repainting the root black preserves the no-red-red invariant.
theorem noRedRed_repaint_black {t : RBTree}
(h : NoRedRed t) : NoRedRed (repaintRoot Color.black t) :=
cases t with
Repainting the root preserves balanced child black heights.
theorem balancedBlackHeight_repaintRoot (color : Color) {t : RBTree}
(h : BalancedBlackHeight t) :
BalancedBlackHeight (repaintRoot color t) :=
cases t with
A left rotation across a red-red edge preserves child black-height balance.
theorem balancedBlackHeight_rotateLeft_red_red
{a b c : RBTree} {x y : Nat}
(h : BalancedBlackHeight
(node Color.red a x (node Color.red b y c))) :
BalancedBlackHeight
(rotateLeft (node Color.red a x (node Color.red b y c))) :=
A right rotation across a red-red edge preserves child black-height balance.
theorem balancedBlackHeight_rotateRight_red_red
{a b c : RBTree} {x y : Nat}
(h : BalancedBlackHeight
(node Color.red (node Color.red a x b) y c)) :
BalancedBlackHeight
(rotateRight (node Color.red (node Color.red a x b) y c)) :=
Repainting the root black makes the root black.
theorem rootBlack_repaint_black (t : RBTree) :
RootBlack (repaintRoot Color.black t) :=
Repainting the root black establishes the bundled local red-black shape invariant, provided the no-red-red and black-height invariants already hold.
theorem redBlackShape_repaint_black {t : RBTree}
(hNoRed : NoRedRed t) (hBalanced : BalancedBlackHeight t) :
RedBlackShape (repaintRoot Color.black t) :=
The local left-rotation red-red repair case: when the three fringe subtrees are already red-black shaped and have matching black heights, rotating across the red-red edge and repainting the new root black establishes the bundled shape invariant.
theorem redBlackShape_repaint_rotateLeft_red_red
{a b c : RBTree} {x y : Nat}
(ha : RedBlackShape a) (hb : RedBlackShape b) (hc : RedBlackShape c)
(hab : blackHeight a = blackHeight b)
(hbc : blackHeight b = blackHeight c) :
RedBlackShape
(repaintRoot Color.black
(rotateLeft (node Color.red a x (node Color.red b y c)))) :=
The symmetric right-rotation red-red repair case: when the three fringe subtrees are already red-black shaped and have matching black heights, rotating across the red-red edge and repainting the new root black establishes the bundled shape invariant.
theorem redBlackShape_repaint_rotateRight_red_red
{a b c : RBTree} {x y : Nat}
(ha : RedBlackShape a) (hb : RedBlackShape b) (hc : RedBlackShape c)
(hab : blackHeight a = blackHeight b)
(hbc : blackHeight b = blackHeight c) :
RedBlackShape
(repaintRoot Color.black
(rotateRight (node Color.red (node Color.red a x b) y c))) :=
Local insertion-fixup cases
The left-left red-red insertion-fixup shape.
def insertFixupLeftLeft : RBTree → RBTree
| node Color.black (node Color.red (node Color.red a w b) x c) y d =>
node Color.black (node Color.red a w b) x (node Color.red c y d)
| t => tThe left-right red-red insertion-fixup shape.
def insertFixupLeftRight : RBTree → RBTree
| node Color.black (node Color.red a w (node Color.red b x c)) y d =>
node Color.black (node Color.red a w b) x (node Color.red c y d)
| t => tThe right-left red-red insertion-fixup shape.
def insertFixupRightLeft : RBTree → RBTree
| node Color.black a w (node Color.red (node Color.red b x c) y d) =>
node Color.black (node Color.red a w b) x (node Color.red c y d)
| t => tThe right-right red-red insertion-fixup shape.
def insertFixupRightRight : RBTree → RBTree
| node Color.black a w (node Color.red b x (node Color.red c y d)) =>
node Color.black (node Color.red a w b) x (node Color.red c y d)
| t => tThe four local CLRS insertion-fixup branch orientations.
inductive InsertFixupCase where
| leftLeft
| leftRight
| rightLeft
| rightRight
deriving Repr, DecidableEqUnified dispatcher for the four local insertion-fixup rewrites. The explicit case parameter records the branch chosen by the surrounding insertion-fixup algorithm; the raw local tree shape alone is not enough to disambiguate every overlapping pattern.
def insertFixupLocal : InsertFixupCase → RBTree → RBTree
| InsertFixupCase.leftLeft, t => insertFixupLeftLeft t
| InsertFixupCase.leftRight, t => insertFixupLeftRight t
| InsertFixupCase.rightLeft, t => insertFixupRightLeft t
| InsertFixupCase.rightRight, t => insertFixupRightRight tThe reusable local certificate needed by a future executable insertion-fixup: the local rewrite preserves membership for a query, preserves subtree black height, and establishes the bundled local red-black shape invariant.
structure InsertFixupLocalCertificate
(q : Nat) (before after : RBTree) : Prop where
membership : InTree q after ↔ InTree q before
blackHeight_eq : blackHeight after = blackHeight before
shape : RedBlackShape afterA black root with two red children is locally red-black shaped when the four fringe subtrees are red-black shaped and have matching black heights.
theorem redBlackShape_black_with_red_children
{a b c d : RBTree} {w x y : Nat}
(ha : RedBlackShape a) (hb : RedBlackShape b)
(hc : RedBlackShape c) (hd : RedBlackShape d)
(hab : blackHeight a = blackHeight b)
(hbc : blackHeight b = blackHeight c)
(hcd : blackHeight c = blackHeight d) :
RedBlackShape
(node Color.black (node Color.red a w b) x (node Color.red c y d)) :=
The left-left insertion-fixup case preserves membership on its local shape.
theorem inTree_insertFixup_leftLeft_iff
(q : Nat) (a b c d : RBTree) (w x y : Nat) :
InTree q
(insertFixupLeftLeft
(node Color.black (node Color.red (node Color.red a w b) x c) y d)) ↔
InTree q
(node Color.black (node Color.red (node Color.red a w b) x c) y d) :=
The left-right insertion-fixup case preserves membership on its local shape.
theorem inTree_insertFixup_leftRight_iff
(q : Nat) (a b c d : RBTree) (w x y : Nat) :
InTree q
(insertFixupLeftRight
(node Color.black (node Color.red a w (node Color.red b x c)) y d)) ↔
InTree q
(node Color.black (node Color.red a w (node Color.red b x c)) y d) :=
The right-left insertion-fixup case preserves membership on its local shape.
theorem inTree_insertFixup_rightLeft_iff
(q : Nat) (a b c d : RBTree) (w x y : Nat) :
InTree q
(insertFixupRightLeft
(node Color.black a w (node Color.red (node Color.red b x c) y d))) ↔
InTree q
(node Color.black a w (node Color.red (node Color.red b x c) y d)) :=
The right-right insertion-fixup case preserves membership on its local shape.
theorem inTree_insertFixup_rightRight_iff
(q : Nat) (a b c d : RBTree) (w x y : Nat) :
InTree q
(insertFixupRightRight
(node Color.black a w (node Color.red b x (node Color.red c y d)))) ↔
InTree q
(node Color.black a w (node Color.red b x (node Color.red c y d))) :=
The left-left insertion-fixup case preserves local black height.
theorem blackHeight_insertFixup_leftLeft
(a b c d : RBTree) (w x y : Nat) :
blackHeight
(insertFixupLeftLeft
(node Color.black (node Color.red (node Color.red a w b) x c) y d)) =
blackHeight
(node Color.black (node Color.red (node Color.red a w b) x c) y d) :=
The left-right insertion-fixup case preserves local black height.
theorem blackHeight_insertFixup_leftRight
(a b c d : RBTree) (w x y : Nat) :
blackHeight
(insertFixupLeftRight
(node Color.black (node Color.red a w (node Color.red b x c)) y d)) =
blackHeight
(node Color.black (node Color.red a w (node Color.red b x c)) y d) :=
The right-left insertion-fixup case preserves local black height.
theorem blackHeight_insertFixup_rightLeft
(a b c d : RBTree) (w x y : Nat) :
blackHeight
(insertFixupRightLeft
(node Color.black a w (node Color.red (node Color.red b x c) y d))) =
blackHeight
(node Color.black a w (node Color.red (node Color.red b x c) y d)) :=
The right-right insertion-fixup case preserves local black height.
theorem blackHeight_insertFixup_rightRight
(a b c d : RBTree) (w x y : Nat) :
blackHeight
(insertFixupRightRight
(node Color.black a w (node Color.red b x (node Color.red c y d)))) =
blackHeight
(node Color.black a w (node Color.red b x (node Color.red c y d))) :=
The left-left local insertion-fixup case establishes red-black shape.
theorem redBlackShape_insertFixup_leftLeft
{a b c d : RBTree} {w x y : Nat}
(ha : RedBlackShape a) (hb : RedBlackShape b)
(hc : RedBlackShape c) (hd : RedBlackShape d)
(hab : blackHeight a = blackHeight b)
(hbc : blackHeight b = blackHeight c)
(hcd : blackHeight c = blackHeight d) :
RedBlackShape
(insertFixupLeftLeft
(node Color.black (node Color.red (node Color.red a w b) x c) y d)) :=
The left-right local insertion-fixup case establishes red-black shape.
theorem redBlackShape_insertFixup_leftRight
{a b c d : RBTree} {w x y : Nat}
(ha : RedBlackShape a) (hb : RedBlackShape b)
(hc : RedBlackShape c) (hd : RedBlackShape d)
(hab : blackHeight a = blackHeight b)
(hbc : blackHeight b = blackHeight c)
(hcd : blackHeight c = blackHeight d) :
RedBlackShape
(insertFixupLeftRight
(node Color.black (node Color.red a w (node Color.red b x c)) y d)) :=
The right-left local insertion-fixup case establishes red-black shape.
theorem redBlackShape_insertFixup_rightLeft
{a b c d : RBTree} {w x y : Nat}
(ha : RedBlackShape a) (hb : RedBlackShape b)
(hc : RedBlackShape c) (hd : RedBlackShape d)
(hab : blackHeight a = blackHeight b)
(hbc : blackHeight b = blackHeight c)
(hcd : blackHeight c = blackHeight d) :
RedBlackShape
(insertFixupRightLeft
(node Color.black a w (node Color.red (node Color.red b x c) y d))) :=
The right-right local insertion-fixup case establishes red-black shape.
theorem redBlackShape_insertFixup_rightRight
{a b c d : RBTree} {w x y : Nat}
(ha : RedBlackShape a) (hb : RedBlackShape b)
(hc : RedBlackShape c) (hd : RedBlackShape d)
(hab : blackHeight a = blackHeight b)
(hbc : blackHeight b = blackHeight c)
(hcd : blackHeight c = blackHeight d) :
RedBlackShape
(insertFixupRightRight
(node Color.black a w (node Color.red b x (node Color.red c y d)))) :=
Unified local certificate for the left-left insertion-fixup branch.
theorem insertFixupLocal_leftLeft_certificate
(q : Nat) {a b c d : RBTree} {w x y : Nat}
(ha : RedBlackShape a) (hb : RedBlackShape b)
(hc : RedBlackShape c) (hd : RedBlackShape d)
(hab : blackHeight a = blackHeight b)
(hbc : blackHeight b = blackHeight c)
(hcd : blackHeight c = blackHeight d) :
InsertFixupLocalCertificate q
(node Color.black (node Color.red (node Color.red a w b) x c) y d)
(insertFixupLocal InsertFixupCase.leftLeft
(node Color.black (node Color.red (node Color.red a w b) x c) y d)) :=
exact ⟨
,
,
⟩Unified local certificate for the left-right insertion-fixup branch.
theorem insertFixupLocal_leftRight_certificate
(q : Nat) {a b c d : RBTree} {w x y : Nat}
(ha : RedBlackShape a) (hb : RedBlackShape b)
(hc : RedBlackShape c) (hd : RedBlackShape d)
(hab : blackHeight a = blackHeight b)
(hbc : blackHeight b = blackHeight c)
(hcd : blackHeight c = blackHeight d) :
InsertFixupLocalCertificate q
(node Color.black (node Color.red a w (node Color.red b x c)) y d)
(insertFixupLocal InsertFixupCase.leftRight
(node Color.black (node Color.red a w (node Color.red b x c)) y d)) :=
exact ⟨
,
,
⟩Unified local certificate for the right-left insertion-fixup branch.
theorem insertFixupLocal_rightLeft_certificate
(q : Nat) {a b c d : RBTree} {w x y : Nat}
(ha : RedBlackShape a) (hb : RedBlackShape b)
(hc : RedBlackShape c) (hd : RedBlackShape d)
(hab : blackHeight a = blackHeight b)
(hbc : blackHeight b = blackHeight c)
(hcd : blackHeight c = blackHeight d) :
InsertFixupLocalCertificate q
(node Color.black a w (node Color.red (node Color.red b x c) y d))
(insertFixupLocal InsertFixupCase.rightLeft
(node Color.black a w (node Color.red (node Color.red b x c) y d))) :=
exact ⟨
,
,
⟩Unified local certificate for the right-right insertion-fixup branch.
theorem insertFixupLocal_rightRight_certificate
(q : Nat) {a b c d : RBTree} {w x y : Nat}
(ha : RedBlackShape a) (hb : RedBlackShape b)
(hc : RedBlackShape c) (hd : RedBlackShape d)
(hab : blackHeight a = blackHeight b)
(hbc : blackHeight b = blackHeight c)
(hcd : blackHeight c = blackHeight d) :
InsertFixupLocalCertificate q
(node Color.black a w (node Color.red b x (node Color.red c y d)))
(insertFixupLocal InsertFixupCase.rightRight
(node Color.black a w (node Color.red b x (node Color.red c y d)))) :=
exact ⟨
,
,
⟩end RBTreeend Chapter13end CLRS