diff --git a/libtree/src/binary_search_tree.rs b/libtree/src/binary_search_tree.rs new file mode 100644 index 0000000..18f81c0 --- /dev/null +++ b/libtree/src/binary_search_tree.rs @@ -0,0 +1,237 @@ +use core::fmt::Display; +use crate::Stats; + +#[derive(Clone)] +pub struct BinarySearchTreeNode { + pub key: u64, + pub left: BST, + pub right: BST, +} + +impl BinarySearchTreeNode { + pub fn new(key: u64) -> Self { + Self { + key, + left: BST(None), + right: BST(None), + } + } + + pub fn height(&self) -> usize { + match (&self.left.0, &self.right.0) { + (None, None) => + 1, + (None, Some(right)) => + 1 + right.height(), + (Some(left), None) => + 1 + left.height(), + (Some(left), Some(right)) => + 1 + (left.height()).max(right.height()), + } + } + + // https://stackoverflow.com/a/66661638 + fn replace_nth_char_ascii(s: &mut str, idx: usize, newchar: char) { + let s_bytes: &mut [u8] = unsafe { s.as_bytes_mut() }; + assert!(idx < s_bytes.len()); + assert!(s_bytes[idx].is_ascii()); + assert!(newchar.is_ascii()); + // we've made sure this is safe. + s_bytes[idx] = newchar as u8; + } + + fn _print(&self, f: &mut std::fmt::Formatter<'_>, depth: usize, prefix: char, left_trace: &mut String, right_trace: &mut String) -> std::fmt::Result { + if let Some(left) = &self.left.0 { + left._print(f, depth + 1, '/', left_trace, right_trace)?; + } + if prefix == '/' { + Self::replace_nth_char_ascii(left_trace, depth - 1, '|'); + } + if prefix == '\\' { + Self::replace_nth_char_ascii(right_trace, depth - 1, ' '); + } + if depth == 0 { + write!(f, "-")?; + } + if depth > 0 { + write!(f, " ")?; + } + + + if depth > 0 { + for i in 0..(depth - 1) { + if left_trace.chars().nth(i).unwrap() == '|' || right_trace.chars().nth(i).unwrap() == '|' { + write!(f, "| ")?; + } else { + write!(f, " ")?; + } + } + write!(f, "{}-", prefix)?; + } + writeln!(f, "[{}]", self.key)?; + Self::replace_nth_char_ascii(left_trace, depth, ' '); + + if let Some(right) = &self.right.0 { + Self::replace_nth_char_ascii(right_trace, depth, '|'); + right._print(f, depth + 1, '\\', left_trace, right_trace)?; + } + + Ok(()) + } +} + +#[derive(Clone)] +pub struct BST(Option>); + +impl BST { + pub fn insert(&mut self, key: u64) -> (bool, Stats) { + let mut stats = Stats::new(); + + if self.0.is_none() { + stats.writes += 1; + self.0 = Some(Box::new(BinarySearchTreeNode::new(key))); + return (true, stats); + } + + let mut current = self; + while let Some(ref mut node) = current.0 { + stats.reads += 1; + use std::cmp::Ordering::*; + stats.comparisons += 1; + match node.key.cmp(&key) { + Greater => { + stats.reads += 1; + current = &mut node.left; + }, + Less => { + stats.reads += 1; + current = &mut node.right; + }, + Equal => return (false, stats), + } + } + + stats.writes += 1; + current.0 = Some(Box::new(BinarySearchTreeNode::new(key))); + + (true, stats) + } + + pub fn delete(&mut self, key: u64) -> (bool, Stats) { + let mut current: *mut BST = self; + + let mut stats = Stats::default(); + + unsafe { + while let Some(ref mut node) = (*current).0 { + stats.reads += 1; + use std::cmp::Ordering::*; + stats.comparisons += 1; + match node.key.cmp(&key) { + Greater => { + stats.reads += 1; + current = &mut node.left; + }, + Less => { + stats.reads += 1; + current = &mut node.right; + }, + Equal => { + match (node.left.0.as_mut(), node.right.0.as_mut()) { + (None, None) => { + stats.writes += 1; + (*current).0 = None; + }, + (None, Some(_)) => { + stats.writes += 1; + (*current).0 = node.right.0.take(); + }, + (Some(_), None) => { + stats.writes += 1; + (*current).0 = node.left.0.take(); + }, + (Some(_), Some(_)) => { + (*current).0.as_mut().unwrap().key = { + let mut to_return = None; + + stats.reads += 1; + if node.right.0.is_some() { + stats.reads += 1; + let mut current = &mut node.right; + + stats.reads += 1; + while current.0.as_ref().unwrap().left.0.is_some() { + stats.writes += 1; + current = &mut current.0.as_mut().unwrap().left; + } + + let node = current.0.take().unwrap(); + to_return = Some(node.key); + stats.writes += 1; + current.0 = node.right.0; + } + + to_return.unwrap() + }; + }, + } + return (true, stats); + }, + } + } + } + + (false, stats) + } +} + +pub struct BinarySearchTree { + pub root: BST, + pub size: usize, + print_capacity: usize, + pub stats: Stats, +} + +impl BinarySearchTree { + pub fn new(print_capacity: usize) -> Self { + Self { + root: BST(None), + size: 0, + print_capacity, + stats: Stats::default(), + } + } + + pub fn height(&self) -> usize { + match &self.root.0 { + Some(root) => root.height(), + None => 0, + } + } + + pub fn insert(&mut self, key: u64) -> bool { + let (ret, stats) = self.root.insert(key); + self.stats += stats; + ret + } + + pub fn delete(&mut self, key: u64) -> bool { + let (ret, stats) = self.root.delete(key); + self.stats += stats; + ret + } + + pub fn reset_stats(&mut self) { + self.stats = Stats::default(); + } +} + +impl Display for BinarySearchTree { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.root.0 { + Some(root) => root._print(f, 0, '-', &mut " ".repeat(self.print_capacity), &mut " ".repeat(self.print_capacity)), + None => Ok(()), + } + + } +} diff --git a/libtree/src/lib.rs b/libtree/src/lib.rs index cfbe58a..bf08b62 100644 --- a/libtree/src/lib.rs +++ b/libtree/src/lib.rs @@ -1,4 +1,6 @@ -use std::fmt::Display; +mod binary_search_tree; + +pub use binary_search_tree::*; #[derive(Clone, Default)] pub struct Stats { @@ -24,238 +26,3 @@ impl std::ops::AddAssign for Stats { self.writes += rhs.writes; } } - -#[derive(Clone)] -pub struct Node { - pub key: u64, - pub left: Tree, - pub right: Tree, -} - -impl Node { - pub fn new(key: u64) -> Self { - Self { - key, - left: Tree(None), - right: Tree(None), - } - } - - pub fn height(&self) -> usize { - match (&self.left.0, &self.right.0) { - (None, None) => - 1, - (None, Some(right)) => - 1 + right.height(), - (Some(left), None) => - 1 + left.height(), - (Some(left), Some(right)) => - 1 + (left.height()).max(right.height()), - } - } - - // https://stackoverflow.com/a/66661638 - fn replace_nth_char_ascii(s: &mut str, idx: usize, newchar: char) { - let s_bytes: &mut [u8] = unsafe { s.as_bytes_mut() }; - assert!(idx < s_bytes.len()); - assert!(s_bytes[idx].is_ascii()); - assert!(newchar.is_ascii()); - // we've made sure this is safe. - s_bytes[idx] = newchar as u8; - } - - fn _print(&self, f: &mut std::fmt::Formatter<'_>, depth: usize, prefix: char, left_trace: &mut String, right_trace: &mut String) -> std::fmt::Result { - if let Some(left) = &self.left.0 { - left._print(f, depth + 1, '/', left_trace, right_trace)?; - } - if prefix == '/' { - Self::replace_nth_char_ascii(left_trace, depth - 1, '|'); - } - if prefix == '\\' { - Self::replace_nth_char_ascii(right_trace, depth - 1, ' '); - } - if depth == 0 { - write!(f, "-")?; - } - if depth > 0 { - write!(f, " ")?; - } - - - if depth > 0 { - for i in 0..(depth - 1) { - if left_trace.chars().nth(i).unwrap() == '|' || right_trace.chars().nth(i).unwrap() == '|' { - write!(f, "| ")?; - } else { - write!(f, " ")?; - } - } - write!(f, "{}-", prefix)?; - } - writeln!(f, "[{}]", self.key)?; - Self::replace_nth_char_ascii(left_trace, depth, ' '); - - if let Some(right) = &self.right.0 { - Self::replace_nth_char_ascii(right_trace, depth, '|'); - right._print(f, depth + 1, '\\', left_trace, right_trace)?; - } - - Ok(()) - } -} - -#[derive(Clone)] -pub struct Tree(Option>); - -impl Tree { - pub fn insert(&mut self, key: u64) -> (bool, Stats) { - let mut stats = Stats::new(); - - if self.0.is_none() { - stats.writes += 1; - self.0 = Some(Box::new(Node::new(key))); - return (true, stats); - } - - let mut current = self; - while let Some(ref mut node) = current.0 { - stats.reads += 1; - use std::cmp::Ordering::*; - stats.comparisons += 1; - match node.key.cmp(&key) { - Greater => { - stats.reads += 1; - current = &mut node.left; - }, - Less => { - stats.reads += 1; - current = &mut node.right; - }, - Equal => return (false, stats), - } - } - - stats.writes += 1; - current.0 = Some(Box::new(Node::new(key))); - - (true, stats) - } - - pub fn delete(&mut self, key: u64) -> (bool, Stats) { - let mut current: *mut Tree = self; - - let mut stats = Stats::default(); - - unsafe { - while let Some(ref mut node) = (*current).0 { - stats.reads += 1; - use std::cmp::Ordering::*; - stats.comparisons += 1; - match node.key.cmp(&key) { - Greater => { - stats.reads += 1; - current = &mut node.left; - }, - Less => { - stats.reads += 1; - current = &mut node.right; - }, - Equal => { - match (node.left.0.as_mut(), node.right.0.as_mut()) { - (None, None) => { - stats.writes += 1; - (*current).0 = None; - }, - (None, Some(_)) => { - stats.writes += 1; - (*current).0 = node.right.0.take(); - }, - (Some(_), None) => { - stats.writes += 1; - (*current).0 = node.left.0.take(); - }, - (Some(_), Some(_)) => { - (*current).0.as_mut().unwrap().key = { - let mut to_return = None; - - stats.reads += 1; - if node.right.0.is_some() { - stats.reads += 1; - let mut current = &mut node.right; - - stats.reads += 1; - while current.0.as_ref().unwrap().left.0.is_some() { - stats.writes += 1; - current = &mut current.0.as_mut().unwrap().left; - } - - let node = current.0.take().unwrap(); - to_return = Some(node.key); - stats.writes += 1; - current.0 = node.right.0; - } - - to_return.unwrap() - }; - }, - } - return (true, stats); - }, - } - } - } - - (false, stats) - } -} - -pub struct BinarySearchTree { - pub root: Tree, - pub size: usize, - print_capacity: usize, - pub stats: Stats, -} - -impl BinarySearchTree { - pub fn new(print_capacity: usize) -> Self { - Self { - root: Tree(None), - size: 0, - print_capacity, - stats: Stats::default(), - } - } - - pub fn height(&self) -> usize { - match &self.root.0 { - Some(root) => root.height(), - None => 0, - } - } - - pub fn insert(&mut self, key: u64) -> bool { - let (ret, stats) = self.root.insert(key); - self.stats += stats; - ret - } - - pub fn delete(&mut self, key: u64) -> bool { - let (ret, stats) = self.root.delete(key); - self.stats += stats; - ret - } - - pub fn reset_stats(&mut self) { - self.stats = Stats::default(); - } -} - -impl Display for BinarySearchTree { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match &self.root.0 { - Some(root) => root._print(f, 0, '-', &mut " ".repeat(self.print_capacity), &mut " ".repeat(self.print_capacity)), - None => Ok(()), - } - - } -}