From 03250c0c1c3b0f1efaa651c9c166edb5cb1d0ca2 Mon Sep 17 00:00:00 2001 From: Eli Bendersky Date: Tue, 3 Sep 2024 06:53:09 -0700 Subject: [PATCH] Implemented verify --- btree/btree.go | 19 ++++++++-------- btree/verify.go | 60 ++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 64 insertions(+), 15 deletions(-) diff --git a/btree/btree.go b/btree/btree.go index 4afcbc6..4ee6d64 100644 --- a/btree/btree.go +++ b/btree/btree.go @@ -19,14 +19,16 @@ type BTree[K, V any] struct { const defaultTee = 10 +// node is a node in the BTree. +// +// Key ordering invariants (defining n=len(keys)): +// +// keys[i].key <= keys[i+1].key for each i in 0..n-2 +// +// There are n+1 elements in children (0..n) +// if kj is any key in children[j], then: keys[j-1].key <= kj <= keys[j].key +// Boundaries: k0 <= keys[0].key and keys[n-1].key <= kn type node[K, V any] struct { - // Key ordering invariants (defining n=len(keys)): - // - // keys[i].key <= keys[i+1].key for each i in 0..n-2 - // - // There are n+1 elements in children (0..n) - // if kj is any key in children[j], then: keys[j-1].key <= kj <= keys[j].key - // Boundaries: k0 <= keys[0].key and keys[n-1].key <= kn keys []nodeKey[K, V] children []*node[K, V] leaf bool @@ -187,9 +189,6 @@ func (bt *BTree[K, V]) nodeKeyCmp(a, b nodeKey[K, V]) int { return bt.cmp(a.key, b.key) } -// TODO: add "verify" method that verifies all invariants: min/max number -// of keys per node, num keys vs. num children, height, and ordering invariants -// of each node. use it in tests // TODO: add deletion // nodesPreOrder returns an iterator over the nodes of bt in pre-order. diff --git a/btree/verify.go b/btree/verify.go index 6377cae..f42c95c 100644 --- a/btree/verify.go +++ b/btree/verify.go @@ -3,6 +3,7 @@ package btree import ( "errors" "fmt" + "slices" ) // verify checks B-tree invariants on bt and returns an error combining all @@ -10,28 +11,77 @@ import ( func (bt *BTree[K, V]) verify() error { var errs []error + // Verify invariants on each node separately for n := range bt.nodesPreOrder() { if err := bt.verifyNode(n); err != nil { errs = append(errs, err) } } - if len(errs) > 0 { return errors.Join(errs...) } + + // Recursive visit to calculate height of all leaf nodes in the tree. + var heights []int + var visit func(n *node[K, V], h int) + visit = func(n *node[K, V], h int) { + if n.leaf { + heights = append(heights, h) + return + } + + for _, c := range n.children { + visit(c, h+1) + } + } + visit(bt.root, 0) + + cp := slices.Compact(heights) + if len(cp) != 1 { + return fmt.Errorf("different leaf heights: %v", cp) + } + return nil } func (bt *BTree[K, V]) verifyNode(n *node[K, V]) error { if len(n.keys) > 2*bt.tee-1 { - return fmt.Errorf("node %p has too many keys: %d", n, len(n.keys)) + return fmt.Errorf("node %p: has too many keys: %d", n, len(n.keys)) } if n != bt.root && len(n.keys) < bt.tee-1 { - return fmt.Errorf("node %p has too few keys: %d", n, len(n.keys)) + return fmt.Errorf("node %p: has too few keys: %d", n, len(n.keys)) } - if !n.leaf && len(n.children) != len(n.keys)+1 { - return fmt.Errorf("internal node %p has %d keys but %d children", n, len(n.keys), len(n.children)) + if n == bt.root && len(n.keys) < 1 { + return fmt.Errorf("node %p: root has 0 keys", n) + } + + if !slices.IsSortedFunc(n.keys, bt.nodeKeyCmp) { + return fmt.Errorf("node %p: keys not sorted", n) + } + + if !n.leaf { + if len(n.children) != len(n.keys)+1 { + return fmt.Errorf("internal node %p: %d keys but %d children", n, len(n.keys), len(n.children)) + } + + for j, k := range n.keys { + // Verify ordering invariant (see type node's comment). + + // 1: ensure that kj <= keys[j].key + for ci, kj := range n.children[j].keys { + if bt.nodeKeyCmp(kj, k) > 0 { + return fmt.Errorf("node %p: key %v of child [%d] >= key %v of node[%d]", n, kj.key, j, k.key, ci) + } + } + + // 2: ensure that keys[j-1].key <= kj + for ci, kj := range n.children[j+1].keys { + if bt.nodeKeyCmp(k, kj) > 0 { + return fmt.Errorf("node %p: key %v of child [%d] <= key %v of node[%d]", n, kj.key, j, k.key, ci) + } + } + } } return nil