Mercurial > lbo > hg > btreex
changeset 3:a94eac64aee8
BTree set functionality is working
author | Lewin Bormann <lbo@spheniscida.de> |
---|---|
date | Tue, 05 Jul 2022 08:09:38 -0700 |
parents | df21e58157f3 |
children | 48536f1240fc |
files | Cargo.toml src/lib.rs |
diffstat | 2 files changed, 88 insertions(+), 55 deletions(-) [+] |
line wrap: on
line diff
--- a/Cargo.toml Tue Jul 05 07:08:06 2022 -0700 +++ b/Cargo.toml Tue Jul 05 08:09:38 2022 -0700 @@ -7,3 +7,5 @@ [dependencies] ptree = "0.4" +rand = "0.8.5" +time-test = "0.2.3"
--- a/src/lib.rs Tue Jul 05 07:08:06 2022 -0700 +++ b/src/lib.rs Tue Jul 05 08:09:38 2022 -0700 @@ -2,21 +2,19 @@ use std::cmp::Ordering; // Min. order: 3 -const ORDER: usize = 4; +const ORDER: usize = 5; #[derive(Debug)] struct Node<T> { entries: [Option<T>; ORDER - 1], children: [Option<usize>; ORDER], - parent: Option<usize>, } impl<T: Ord> Node<T> { - fn new(parent: Option<usize>) -> Node<T> { + fn new() -> Node<T> { Node { entries: Default::default(), children: Default::default(), - parent: parent, } } } @@ -42,6 +40,13 @@ .map(|e| if e.is_some() { 0 } else { 1 }) .sum() } + fn occupied(&self, node: usize) -> usize { + self.v[node] + .entries + .iter() + .map(|e| if e.is_some() { 1 } else { 0 }) + .sum() + } fn get(&self, node: usize, elem: usize) -> &Option<T> { &self.v[node].entries[elem] } @@ -55,6 +60,29 @@ &mut self.v[node].children[child] } + fn count(&self) -> usize { + self.count_from(self.root) + } + fn count_from(&self, node: usize) -> usize { + let mut s = self.occupied(node); + for i in 0..ORDER { + if let Some(ch) = self.v[node].children[i] { + s += self.count_from(ch); + } + } + s + } + + fn find<'a>(&'a self, value: &T) -> &'a T { + panic!(); + } + fn find_from<'a>(&'a self, node: usize, value: &T) -> &'a T { + for i in 0..ORDER { + if i < ORDER - 1 {} + } + panic!(); + } + /// Insert a value into an existing node, preserving the order. /// Requires that at least one slot be free. fn insert_inline( @@ -65,8 +93,6 @@ right: Option<usize>, ) -> usize { assert!(self.free(node) >= 1); - let nodeix = node; - let mut loc = ORDER - 2; let node = &mut self.v[node]; @@ -91,26 +117,17 @@ node.children[loc] = left; node.children[loc + 1] = right; - if let Some(left) = left { - self.v[left].parent = Some(nodeix) - } - if let Some(right) = right { - self.v[right].parent = Some(nodeix); - } loc } /// Split a node, returning the IDs of the two new nodes and the pivot value. - /// Parent pointers need to be updated by caller. fn split(&mut self, node: usize) -> (usize, T, usize) { assert!(self.free(node) <= 1); // `node` will become left node. let leftix = node; let rightix = self.v.len(); - println!("Starting split: {:?}", self.v[node]); - - let mut right = Node::<T>::new(None); // Parent updated by caller! + let mut right = Node::<T>::new(); let pivotix = (ORDER - 1) / 2; let pivot = self.get_mut(node, pivotix).take(); @@ -126,45 +143,22 @@ } self.v.push(right); - println!( - "Finishing split: left {:?} right {:?} pivot {:?}", - self.v[leftix], self.v[rightix], pivot - ); (leftix, pivot.unwrap(), rightix) } fn is_leaf(&self, node: usize) -> bool { self.v[node].children.iter().all(|c| c.is_none()) } - fn update_right_child(&mut self, node: usize, ch: usize) { - for i in 0..ORDER - 1 { - if self.get(node, i).is_none() { - if let Some(subch) = self.get_child(node, i) { - self.update_right_child(*subch, ch); - return; - } else { - *self.get_child_mut(node, i) = Some(ch); - return; - } - } - } - // This means that even the last child slot was used so far. - self.update_right_child(self.get_child(node, ORDER - 1).unwrap(), ch); - } - /// Insert & root splitting. fn insert(&mut self, value: T) -> bool { if let Some((l, pivot, r)) = self.insert_into(self.root, value) { // Split root - println!("Splitting root with new children {}, {}", l, r); - let mut newroot = Node::new(None); + let mut newroot = Node::new(); let rootix = self.v.len(); newroot.children[0] = Some(l); newroot.children[1] = Some(r); newroot.entries[0] = Some(pivot); - self.v[l].parent = Some(rootix); - self.v[r].parent = Some(rootix); self.root = rootix; self.v.push(newroot); @@ -178,21 +172,18 @@ fn insert_into(&mut self, node: usize, value: T) -> Option<(usize, T, usize)> { // BTree initialized? if self.v.len() == 0 { - println!("Initializing"); - self.v.push(Node::new(None)); + self.v.push(Node::new()); self.insert_inline(self.root, value, None, None); return None; } // Leaf node with free space? We are finished. if self.is_leaf(node) && self.free(node) >= 1 { - println!("Leaf {} with free space", node); self.insert_inline(node, value, None, None); return None; } else if self.is_leaf(node) && self.free(node) < 1 { // Leaf node but needs to be split. let (l, pivot, r) = self.split(node); // Full leaf node? - println!("Splitting leaf {} into {} {:?} {}", node, l, pivot, r); // Insert value into left or right subtree. match value.cmp(&pivot) { @@ -203,16 +194,21 @@ return Some((l, pivot, r)); } else { // Descend into node. - println!("Scanning node {}", node); let mut split = None; // Find correct child. - for i in 0..ORDER - 1 { - if i < ORDER - 2 { + for i in 0..ORDER { + if i < ORDER - 1 { if let Some(el) = self.get(node, i) { - if value.cmp(el) == Ordering::Less { - split = self.insert_into(self.get_child(node, i).unwrap(), value); - break; + match value.cmp(el) { + Ordering::Less => { + split = self.insert_into(self.get_child(node, i).unwrap(), value); + break; + } + Ordering::Equal => { + panic!("Attempting to insert duplicate element {:?}", value) + } + Ordering::Greater => continue, } } else { if let Some(ch) = self.get_child(node, i) { @@ -243,7 +239,6 @@ } } - println!("Split necessary? {:?}", split); // Merge split if one happened below us. if let Some((l, pivot, r)) = split { if self.free(node) >= 1 { @@ -291,20 +286,56 @@ #[cfg(test)] mod tests { use super::BTree; + use rand::{rngs::StdRng, RngCore, SeedableRng}; #[test] fn basic_test() { let mut bt = BTree::<i32>::new(16); - let ns = &[15, 2, 21, 35, 4, 62, 31, 27, 88, 1, 12, 45, 11, 16, 23]; + let ns = &[ + 15, 2, 21, 35, 4, 62, 31, 27, 88, 1, 12, 45, 11, 16, 23, 20, 22, 24, 25, 26, 28, 29, + 30, 31, 32, + ]; for n in ns { bt.insert(*n); - println!("Inserting {}", n); - println!("root: {}", bt.root); - println!("{}", bt.debug_print()); } for (i, e) in bt.v.iter().enumerate() { println!("{} {:?}", i, e); } + assert_eq!(bt.count(), ns.len()); + } + + #[test] + fn randomized_all_reachable() { + time_test::time_test!("10 x 100 inserts"); + for seed in &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] { + let mut rng = StdRng::seed_from_u64(*seed); + let N = 1000; + let mut bt = BTree::<u32>::new(N / 2); + for i in 0..N { + let rn = rng.next_u32(); + bt.insert(rn); + if i + 1 != bt.count() { + println!("inserting {}", rn); + println!("seed = {}\n{}", seed, bt.debug_print()); + } + assert_eq!(i + 1, bt.count()); + } + } + } + + #[test] + fn randomized_stdbtree_test() { + time_test::time_test!("10 x 100 inserts"); + for seed in &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] { + let mut rng = StdRng::seed_from_u64(*seed); + let N = 1000; + let mut bt = std::collections::BTreeSet::new(); + for i in 0..N { + let rn = rng.next_u32(); + bt.insert(rn); + assert_eq!(i + 1, bt.len()); + } + } } }