move bst into separate module

This commit is contained in:
jacekpoz 2024-06-05 21:09:18 +02:00
parent bcf9a3db19
commit 64bf67e67d
Signed by: poz
SSH key fingerprint: SHA256:JyLeVWE4bF3tDnFeUpUaJsPsNlJyBldDGV/dIKSLyN8
2 changed files with 240 additions and 236 deletions

View file

@ -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<Box<BinarySearchTreeNode>>);
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(()),
}
}
}

View file

@ -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<Box<Node>>);
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(()),
}
}
}