diff --git a/design_doc_draft.txt b/design_doc_draft.txt new file mode 100644 index 0000000..b457b72 --- /dev/null +++ b/design_doc_draft.txt @@ -0,0 +1,110 @@ +A design document outlining: +- Major innovations – Additional to the project specification. +- A detailed rationale for your augmented decisions with regard to the above design +questions. +- A list of known errors, faults, defects, missing functionality, etc. Telling us about +your system’s limitations will score better than letting us find them! o A user manual + + +Design Rationale + +We designed our implementation of red-black trees and AVL trees with the goal of providing a reusable crates that could be used as a means of something idk. In particular, one of the major innovations of our design involved the user traits, reusable code, and encapsulating functionality associated with smart pointers and their respective operations. [need more detail here on crate stuff, and we actually need to do the crate stuff] + +>> Talk about the traits + struct stuff and impl blocks here like using traits to extend and implement commonalities between the RBT and AVL + +>> Talk about the different operations here + - inserting - start with binary search insert for both + - deleting - start with binary search delete for both, but with slight modification in each binary search (talk about what's a lil different in avl vs rbt) + - leaf counting (this is the same for both) + - height of a tree (same code for both) + - in-order print traversal (same code, only difference is indicating color) + - is empty? (Same code for both, just a wrapper around root.is_none()) + +Our decision to have wrapper functions around operations that involved dealing with smart pointers was ..... Talk about encapsulating functionality with handling/managing smart pointers here because it made code easier to read, maintain, and cleaner. Helped with reducing the amount of repeated code that would have had to be written without. Also talk about how we handled borrows and reference counting stuff with the wrapper functions to access the different nodes of a tree/subtree. + + +- Talk about how we specifically chose to implement a top-down approach for insertion in both types of trees so that we could re-use code. Talk about how bottom-up approach maybe for AVL Tree insertion could improve run-time speed with regards to benchmarking. + +>> Talk about command line interface - why we do it the way we have it? +- CLI only takes in numerical values - we can talk about how this decision was made for simple usage but the CLI can be extended to any type of generic value and this could include characters/strings/etc that can be partially ordered, depending on the traits said type implements. + + +ANSWER FOLLOWING QUESTIONS (maybe combine them with design rationale above?): +- What does a red-black tree provide that cannot be accomplished with ordinary binary +search trees? + + +- Do you need to apply any kind of error handling in your system (e.g., panic macro, +Option, Result, etc..) + +Unless somehow erroneous data is injected into the system, for the most part error handling is not necessarily required. Incorrect or erroneous data that is normally inputted to the system will typically be rejected and would not be allowed to be used in the system, particularly when performing operations on either tree, attempts to insert erroneous data simply will not be handled and will just be rejected/prompt user to attempt to correct themselves. + +- What components do the Red-black tree and AVL tree have in common? Don’t Repeat +Yourself! Never, ever repeat yourself – a fundamental idea in programming. + +Many of the required components to have had been implemented were identical. The main difference between red-black tree nodes and AVL tree nodes are one field unique to either one. For red-black tree, the color of each node is kept track of, while in AVL, each node keeps track of its height with respect to its depth/distance from the root node. Other than this, both nodes maintain values, and said node's parent and left and right children. Additionally, many of operations that were implemented were simply reused for both tree types. Particularly, counting the number of leaves in a tree, checking if a tree was empty and in-order traversal were essentially identical operations. And although the insert and delete operations for both trees did differ, both implementations required a typical binary search insert and delete respectively, with only a slight modification to either tree at the end which has to do with how both trees self-balance (in red-black tree's case, the color is fixed at the end of inserting/deleting, while for AVL tree, the height of each node is recursively checked to ensure a maintained balance). + +- How do we construct our design to “allow it to be efficiently and effectively extended”? For example. Could your code be reused to build a 2-3-4 tree or B tree? + +We can construct our design by having the code implement functionality and/or fields that are common between various types of search trees. In the case of binary search trees, the code could be constructed and designed in such a way that the typical binary search insertion and deletion operations are generic and could be extendable such that if there a property/field in a certain type of tree that is to be maintained, that at these operations could still be called/re-used and maintaining whatever said property that is could simply be done after calling the respective binary search operation. This could also be said for the other operations binary search trees have in common (i.e. counting leaves, checking if the tree is empty, and printing via in-order traversal). + +- 2-3-4 trees are essentially an alternate form of red-black trees (i.e. that is, they are equivalent data structures and there is at least 1 analogous red-black tree for every 2-3-4 tree implementation). That being said, it could be possible that our red-black tree code could very much so be re-used to implement a 2-3-4 tree though this could prove to be much more difficult (read more on wikipedia page later lol) +- B trees are unlikely to be able to use our code. Since B trees are a generalized binary search trees in that something about there being allowed to have >2 child nodes. + +PART 3 + +RED BLACK TREE BENCHMARK RESULTS: +Elapsed time for 10000: 6 ms +Elapsed time for 40000: 31 ms +Elapsed time for 70000: 57 ms +Elapsed time for 100000: 87 ms +Elapsed time for 130000: 121 ms +== +Elapsed time for 10000: 6 ms +Elapsed time for 40000: 29 ms +Elapsed time for 70000: 58 ms +Elapsed time for 100000: 86 ms +Elapsed time for 130000: 116 ms +== +Elapsed time for 10000: 6 ms +Elapsed time for 40000: 31 ms +Elapsed time for 70000: 58 ms +Elapsed time for 100000: 88 ms +Elapsed time for 130000: 114 ms +== +Elapsed time for 10000: 6 ms +Elapsed time for 40000: 30 ms +Elapsed time for 70000: 57 ms +Elapsed time for 100000: 87 ms +Elapsed time for 130000: 113 ms + +AVL TREE BENCHMARK RESULTS: +Elapsed time for 10000: 8 ms +Elapsed time for 40000: 37 ms +Elapsed time for 70000: 65 ms +Elapsed time for 100000: 101 ms +Elapsed time for 130000: 129 ms +== +Elapsed time for 10000: 7 ms +Elapsed time for 40000: 34 ms +Elapsed time for 70000: 64 ms +Elapsed time for 100000: 98 ms +Elapsed time for 130000: 140 ms +== +Elapsed time for 10000: 7 ms +Elapsed time for 40000: 34 ms +Elapsed time for 70000: 64 ms +Elapsed time for 100000: 98 ms +Elapsed time for 130000: 130 ms +== +Elapsed time for 10000: 7 ms +Elapsed time for 40000: 34 ms +Elapsed time for 70000: 64 ms +Elapsed time for 100000: 98 ms +Elapsed time for 130000: 129 ms + +- AVL is probably going to be more efficient because of height rebalancing factor. +- AVL apparently not as efficient (likely to due top-bottom implementation) +- Other test cases should be accommodated including random insertions of varying numbers instead of in-order insertion. Could also consider decreasing numbers for inserting. Also consider deleting operations as a means of measuring efficiency. +- Yes probably good idea to include other data structures as a baseline. BST might be good baseline since it is essentially an RBT/AVL Tree that like doesn't balance itself or whatever we can figure out what to say later. + diff --git a/src/avl_tree.rs b/src/avl_tree.rs index e69de29..be340e3 100644 --- a/src/avl_tree.rs +++ b/src/avl_tree.rs @@ -0,0 +1,539 @@ +use std::cell::RefCell; +use std::rc::Rc; +use std::cmp::max; +use crate::tree::{TreeBase, NodeTraits}; + +static ROTATE_LEFT: bool = true; +static ROTATE_RIGHT: bool = false; + +pub type AVLTreeNode = Option>>>; + +#[derive(Clone, Debug, PartialEq)] +pub struct AVLNode { + value: Option, + parent: AVLTreeNode, + left: AVLTreeNode, + right: AVLTreeNode, + height: isize, +} + +pub trait AVLNodeTraits { + // helper functions + fn new(val: T) -> AVLTreeNode; + fn unwrapped(&self) -> Rc>>; + fn compare(&self, node: &AVLTreeNode) -> bool; + fn find_node(&self, value: T) -> AVLTreeNode; + + // AVL specific helper functions + fn recalc_height(&mut self); + fn get_balance(&self) -> isize; + + // getters for node properties and family members + fn parent(&self) -> AVLTreeNode; + fn left(&self) -> AVLTreeNode; + fn right(&self) -> AVLTreeNode; + fn grandparent(&self) -> AVLTreeNode; + fn uncle(&self) -> AVLTreeNode; + fn sibling(&self) -> AVLTreeNode; + fn height(&self) -> isize; // AVL specific node property + + // setters for node properties + fn set_parent(&mut self, parent: AVLTreeNode); + fn set_left(&mut self, left: AVLTreeNode); + fn set_right(&mut self, right: AVLTreeNode); + fn set_height(&self, value: isize); // AVL specific node property +} + + +impl NodeTraits for AVLTreeNode where T: Copy + PartialOrd + std::fmt::Debug { + + // print in-order traversal of tree + fn print_traversal(&self) { + if self.is_some() && self.value().is_some() { + self.left().print_traversal(); + print!("{:?} ", self.value().unwrap()); + self.right().print_traversal(); + } + } + + // count number of leaves in a tree + fn count_leaves(&self) -> usize { + if self.value().is_none() { + 0 + } else if self.left().value().is_none() && self.right().value().is_none() { + 1 + } else { + self.left().count_leaves() + self.right().count_leaves() + } + } + + // retrieve a vector containing each node in a tree with respective depth + fn get_depth_vec(&self) -> Vec<(T, usize)> { + let mut vec: Vec<(T, usize)> = Vec::new(); + self.calc_depth(0, &mut vec); + vec.sort_by(|a, b| b.1.cmp(&a.1)); // sort by depth + vec.dedup_by(|a, b| a.0 == b.0); // remove duplicate nodes + vec + } + + // calculate the depth of each node and store them in a given vector + fn calc_depth(&self, depth: usize, vec: &mut Vec<(T, usize)>) { + match self.value() { + Some(_) => { + vec.push((self.value().unwrap(), depth)); + self.left().calc_depth(depth+1, vec); + self.right().calc_depth(depth+1, vec) + } + None => {}, + } + } + + // required getters for node properties + fn value(&self) -> Option { + match self { + Some(tree_node) => tree_node.borrow().value, + None => None + } + } + + // setters for node properties + fn set_value(&self, value: T) { + self.unwrapped().borrow_mut().value = Some(value); + } +} + +impl AVLNodeTraits for AVLTreeNode where T: Copy + PartialOrd + std::fmt::Debug { + + // return new instance of an AVL tree node + fn new(val: T) -> AVLTreeNode { + let tree_node = Some(Rc::new(RefCell::new(AVLNode { + value: Some(val), + parent: None, + left: None, + right: None, + height: 1, + }))); + tree_node.left().set_parent(tree_node.clone()); + tree_node.right().set_parent(tree_node.clone()); + + tree_node + } + + // return unwrapped tree node (reference counter) + fn unwrapped(&self) -> Rc>> { + match self { + Some(tree_node) => Rc::clone(&tree_node), + None => panic!("Error unwrapping tree node") + } + } + + // compare if two nodes are the same or not + fn compare(&self, node: &AVLTreeNode) -> bool { + if self.is_none() || node.is_none() { + return false + } + Rc::ptr_eq(&self.unwrapped(), &node.unwrapped()) + } + + // search for a node with given value + fn find_node(&self, value: T) -> AVLTreeNode { + match self.value() { + Some(_) => { + if value == self.value().unwrap() { + self.clone() + } else if value < self.value().unwrap() { + self.left().find_node(value) + } else if value > self.value().unwrap() { + self.right().find_node(value) + } + else { + None + } + }, + None => None + } + } + + // relcalculate the height of a node + fn recalc_height(&mut self) { + let left = self.left(); + let right = self.right(); + self.set_height(1 + max(left.height(), right.height())); + } + + // calculate the balance of a node with respect to its height + fn get_balance(&self) -> isize { + if self.is_none() { + 0 + } else { + self.left().height() - self.right().height() + } + } + + // return the parent of a node + fn parent(&self) -> AVLTreeNode { + match self { + Some(tree_node) => tree_node.borrow().parent.clone(), + None => None + } + } + + // return the left child of a node + fn left(&self) -> AVLTreeNode { + match self { + Some(tree_node) => tree_node.borrow().left.clone(), + None => None + } + } + + // return the right child of a node + fn right(&self) -> AVLTreeNode { + match self { + Some(tree_node) => tree_node.borrow().right.clone(), + None => None + } + } + + // return the parent of a node's parent + fn grandparent(&self) -> AVLTreeNode { + self.parent().parent() + } + + // return a parent node's sibling + fn uncle(&self) -> AVLTreeNode { + if self.grandparent().left().is_none() || self.grandparent().right().is_none() { + None + } else if self.parent().compare(&self.grandparent().left()) { + self.grandparent().right() + } else { + self.grandparent().left() + } + } + + // return a node's sibling + fn sibling(&self) -> AVLTreeNode { + match self.compare(&self.parent().left()) { + true => self.parent().right(), + false => self.parent().left(), + } + } + + // return the height of a node + fn height(&self) -> isize { + match self { + Some(tree_node) => tree_node.borrow().height, + None => 0 + } + } + + // set the height of a node + fn set_height(&self, value: isize) { + self.unwrapped().borrow_mut().height = value; + } + + // set the parent of a node + fn set_parent(&mut self, parent: AVLTreeNode) { + match self { + Some(tree_node) => tree_node.borrow_mut().parent = parent, + None => *self = Some(Rc::new(RefCell::new(AVLNode { + value: self.value(), + parent: parent, + left: self.left(), + right: self.right(), + height: self.height(), + }))) + } + } + + // set the left child of a node + fn set_left(&mut self, left: AVLTreeNode) { + match self { + Some(tree_node) => tree_node.borrow_mut().left = left, + None => *self = Some(Rc::new(RefCell::new(AVLNode { + value: self.value(), + parent: self.parent(), + left: left, + right: self.right(), + height: self.height(), + }))) + } + } + + // set the right child of a node + fn set_right(&mut self, right: AVLTreeNode) { + match self { + Some(tree_node) => tree_node.borrow_mut().right = right, + None => *self = Some(Rc::new(RefCell::new(AVLNode { + value: self.value(), + parent: self.parent(), + left: self.left(), + right: right, + height: self.height(), + }))) + } + } +} + +/******************** AVL Tree Helpers ********************/ +#[derive(Clone, Debug, PartialEq)] +pub struct AVLTree { + root: AVLTreeNode, + len: usize +} + +impl AVLTree { + pub fn new() -> AVLTree { + AVLTree { + root: None, + len: 0 + } + } +} + +pub trait AVLTreeTraits { + // methods required to be implemented for a fully functional AVL tree + fn search(&self, value: T) -> AVLTreeNode; + fn rotate(&mut self, node: &AVLTreeNode, direction: bool); + fn rebalance(&mut self, node: &mut AVLTreeNode); +} + +impl TreeBase for AVLTree where T: Copy + PartialOrd + std::fmt::Debug { + + // get the height of a tree + fn height(&self) -> usize { + self.root.height() as usize + } + + // check if a tree is empty + fn is_empty(&self) -> bool { + self.root.is_none() + } + + // get the size of a tree + fn size(&self) -> usize { + self.len + } + + // check if a tree contains a value + fn contains(&self, value: T) -> bool { + match self.search(value) { + Some(_) => true, + None => false + } + } + + // count the number of leaves in a tree + fn count_leaves(&self) -> usize { + self.root.count_leaves() + } + + // insert a node into a tree + fn insert_node(&mut self, value: T) { + // REGULAR BINARY SEARCH TREE INSERTION + let mut new_node = AVLTreeNode::new(value); + + if self.is_empty() { + self.root = new_node.clone(); + self.len += 1; + return + } + + else if self.search(value).is_some() { + println!("Value already exists!"); + return; + } + else { + let mut curr_node = self.root.clone(); + let mut curr_node_parent: AVLTreeNode = None; + let mut is_left_child = true; + + // find empty node + while curr_node.value().is_some() { + curr_node_parent = curr_node.clone(); + if value < curr_node.value().unwrap() { + curr_node = curr_node.left(); + is_left_child = true; + } else { + curr_node = curr_node.right(); + is_left_child = false; + } + } + + // place new_node in found spot + if curr_node_parent.is_some() { + new_node.set_parent(curr_node_parent); + if is_left_child { + new_node.parent().set_left(new_node.clone()); + } else { + new_node.parent().set_right(new_node.clone()); + } + } else { + panic!("Current node has no parent!"); + } + } + + self.rebalance(&mut new_node); + self.len += 1; + } + + // remove/delete a node from a tree + fn delete_node(&mut self, value: T) { + let mut node = self.search(value); + + if node.is_none() { + println!("Node does not exist!"); + return; + } + + let mut unbalanced = node.left(); + if node.left().value().is_none() { + unbalanced = match node.right().value() { + None => node.parent(), + Some(_) => node.right(), + }; + } + + if node.left().value().is_some() && node.right().value().is_some() { + let mut larger = node.left(); + + while larger.right().value().is_some() { + larger = larger.right(); + } + + node.set_value(larger.value().unwrap()); + node = larger.clone(); + } + + // set node to null sibling + let mut child = match node.left().value() { + Some(_) => node.left(), + None => node.right() + }; + + if !node.compare(&self.root) && node.parent().is_some() { + child.set_parent(node.parent()); + if node.compare(&node.parent().left()) { + node.parent().set_left(child.clone()); + } else { + node.parent().set_right(child.clone()); + } + } else if child.is_none() { + // empty tree if child is None + self.root = None; + self.len -= 1; + return; + } else { + // set root to child + self.root = child.clone(); + child.set_parent(None); + } + + self.rebalance(&mut unbalanced); + self.len -= 1; + } + + // print the in-order traversal in a tree + fn print(&self) { + if self.is_empty() { + println!("Tree is empty. Nothing to print."); + } else { + println!("Root: {:?}", self.root.value().unwrap()); + self.root.print_traversal(); + println!(); + } + } + + // return all the values in an AVL tree by depth + fn get_depth_vec(&self) -> Vec<(T, usize)> { + self.root.get_depth_vec() + } +} + +impl AVLTreeTraits for AVLTree where T: Copy + PartialOrd + std::fmt::Debug { + + // search for a node with given value in a tree + fn search(&self, value: T) -> AVLTreeNode { + self.root.find_node(value) + } + + // rotate a subtree/node either LEFT or RIGHT + fn rotate(&mut self, node: &AVLTreeNode, direction: bool) { + let mut parent = node.parent().clone(); + let mut grandparent = node.grandparent().clone(); + let mut node = node.clone(); + + if parent.compare(&self.root) { + self.root = node.clone(); + } else { + node.set_parent(grandparent.clone()); + if parent.compare(&grandparent.left()) { + grandparent.set_left(node.clone()); + } else { + grandparent.set_right(node.clone()); + } + } + + parent.set_parent(node.clone()); + if direction == ROTATE_LEFT { + if node.left().is_some() { + node.left().set_parent(parent.clone()); + } + parent.set_right(node.left()); + parent.recalc_height(); + node.set_left(parent); + } else { + if node.right().is_some() { + node.right().set_parent(parent.clone()); + } + parent.set_left(node.right()); + parent.recalc_height(); + node.set_right(parent); + } + + node.recalc_height(); + } + + // rebalance a tree following insertion or deletion + fn rebalance(&mut self, node: &mut AVLTreeNode) { + node.recalc_height(); + let balance = node.get_balance(); + + // Left Cases + if balance > 1 { + // LEFT RIGHT + if node.left().right().height() > node.left().left().height() { + self.rotate(&node.left().right(), ROTATE_LEFT); + self.rotate(&node.left(), ROTATE_RIGHT); + self.root.recalc_height(); + return + } + // LEFT LEFT + else if node.left().right().height() <= node.left().left().height() { + self.rotate(&node.left(), ROTATE_RIGHT); + self.root.recalc_height(); + return; + } + } + + // RIGHT Cases + if balance < -1 { + // RIGHT LEFT + if node.right().left().height() > node.right().right().height() { + self.rotate(&node.right().left(), ROTATE_RIGHT); + self.rotate(&node.right(), ROTATE_LEFT); + self.root.recalc_height(); + return; + } + // RIGHT RIGHT + else if node.right().left().height() <= node.right().right().height() { + self.rotate(&node.right(), ROTATE_LEFT); + self.root.recalc_height(); + return; + } + } + + if node.parent().is_some() && !node.compare(&self.root) { + self.rebalance(&mut node.parent()); + } + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..954b9af --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,3 @@ +pub mod red_black_tree; +pub mod avl_tree; +pub mod tree; \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 4a2a5d7..3efb9af 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,188 @@ mod avl_tree; mod red_black_tree; +mod tests; +mod tree; + +use red_black_tree::*; +use avl_tree::*; +use tests::*; +use crate::tree::*; + +use std::io::{stdin, stdout, Write}; + +static MAIN_MENU: &str = " +1. Red Black Tree +2. AVL Tree +3. Run Benchmark Tests +4. Exit +"; + +static BENCHMARK_MENU: &str = " +BENCHMARK TESTS +1. Red Black Tree Tests +2. AVL Tree Tests +3. Return to main menu +"; + +static RBT_MENU: &str = " +RED-BLACK TREE +1. Insert a node to the red-black tree +2. Delete a node from the red-black tree +3. Count the number of leaves in the tree +4. Return the height of the tree +5. Print in-order traversal of the tree +6. Check if tree is empty +7. Return to main menu +"; + +static AVL_MENU: &str = " +AVL TREE +1. Insert a node to the AVL tree +2. Delete a node from the AVL tree +3. Count the number of leaves in the tree +4. Return the height of the tree +5. Print in-order traversal of the tree +6. Check if tree is empty +7. Return to main menu +"; + +fn get_input(prompt: &str) -> u32 { + loop { + print!("{} ", prompt); + stdout().flush().unwrap(); + let mut value = String::new(); + stdin().read_line(&mut value).expect("Failed to read line"); + + match value.trim().parse::() { + Ok(num) => return num, + Err(_) => { + println!("Invalid input! Please enter a numerical value."); + continue; + } + } + } +} + +fn avl() { + let mut avl: AVLTree = AVLTree::new(); + loop { + println!("{}", AVL_MENU); + let choice = get_input(">"); + match choice { + 1 => { + let value = get_input("Value to insert:"); + let old_size = avl.size(); + avl.insert_node(value); + if old_size < avl.size() { + println!("Successfully inserted {}", value); + } else { + println!("Error inserting value"); + } + }, + 2 => { + let value = get_input("Value to delete:"); + let old_size = avl.size(); + avl.delete_node(value); + if old_size > avl.size() { + println!("Successfully deleted {}", value); + } else { + println!("Error deleting value"); + } + }, + 3 => println!("Number of leaves: {}", avl.count_leaves()), + 4 => println!("Height: {}", avl.height()), + 5 => avl.print(), + 6 => { + if avl.is_empty() { + println!("Tree is empty."); + } else { + println!("Nah tree is full of leaves"); + } + }, + 7 => break, + _ => println!("Invalid input!") + } + } +} + +fn rbt() { + let mut rbt: RBTree = RBTree::new(); + loop { + println!("{}", RBT_MENU); + let choice = get_input(">"); + match choice { + 1 => { + let value = get_input("Value to insert:"); + let old_size = rbt.size(); + rbt.insert_node(value); + if old_size < rbt.size() { + println!("Successfully inserted {}", value); + } else { + println!("Error inserting value"); + } + }, + 2 => { + let value = get_input("Value to delete:"); + let old_size = rbt.size(); + rbt.delete_node(value); + if old_size > rbt.size() { + println!("Successfully deleted {}", value); + } else { + println!("Error deleting value"); + } + }, + 3 => println!("Number of leaves: {}", rbt.count_leaves()), + 4 => println!("Height: {}", rbt.height()), + 5 => rbt.print(), + 6 => { + if rbt.is_empty() { + println!("Tree is empty."); + } else { + println!("Nah tree is full of leaves"); + } + } + 7 => break, + _ => println!("Invalid input!") + } + } +} + +fn benchmark_tests() { + loop { + println!("{}", BENCHMARK_MENU); + let choice = get_input(">"); + match choice { + 1 => { + for tree_size in vec![10_000, 40_000, 70_000, 100_000, 130_000] { + let tree: red_black_tree::RBTree = red_black_tree::RBTree::new(); + benchmark_insert_search(tree, tree_size); + } + }, + 2 => { + for tree_size in vec![10_000, 40_000, 70_000, 100_000, 130_000] { + let tree: avl_tree::AVLTree = avl_tree::AVLTree::new(); + benchmark_insert_search(tree, tree_size); + } + }, + 3 => break, + _ => println!("Invalid input!") + } + } +} fn main() { - println!("Hello, world!"); + loop { + println!("{}", MAIN_MENU); + let choice = get_input(">"); + match choice { + 1 => rbt(), + 2 => avl(), + 3 => benchmark_tests(), + 4 => { + println!("ok bye"); + break; + }, + _ => println!("Invalid input!") + } + } } diff --git a/src/red_black_tree.rs b/src/red_black_tree.rs index 2e57f7d..c1458a3 100644 --- a/src/red_black_tree.rs +++ b/src/red_black_tree.rs @@ -1,190 +1,622 @@ use std::cell::RefCell; use std::rc::Rc; +use crate::tree::{TreeBase, NodeTraits}; + +static ROTATE_LEFT: bool = true; +static ROTATE_RIGHT: bool = false; #[derive(Clone, Debug, PartialEq)] -enum NodeColor { +/******************** TreeNode Helpers ********************/ + +pub enum NodeColor { Red, Black, } -// type Tree = Rc>>; -// type RedBlackTree= Option; +pub type RBTreeNode = Option>>>; + +#[derive(Debug)] +pub struct RBTNode { + color: NodeColor, + value: Option, + parent: RBTreeNode, + left: RBTreeNode, + right: RBTreeNode +} + +pub trait RBTNodeTraits { + // helper functions + fn new(val: T) -> RBTreeNode; + fn unwrapped(&self) -> Rc>>; + fn compare(&self, node: &RBTreeNode) -> bool; + fn find_node(&self, value: T) -> RBTreeNode; + fn node_height(&self) -> usize; + + // RBT specific helper functions + fn is_red(&self) -> bool; + fn is_black(&self) -> bool; + + // getters for family members and node property + fn parent(&self) -> RBTreeNode; + fn left(&self) -> RBTreeNode; + fn right(&self) -> RBTreeNode; + fn grandparent(&self) -> RBTreeNode; + fn uncle(&self) -> RBTreeNode; + fn sibling(&self) -> RBTreeNode; + fn color(&self) -> NodeColor; // RBT specific node property + + // setters for node properties + fn set_parent(&mut self, parent: RBTreeNode); + fn set_left(&mut self, left: RBTreeNode); + fn set_right(&mut self, right: RBTreeNode); + fn set_color(&self, color: NodeColor); // RBT specific node property +} + +impl NodeTraits for RBTreeNode where T: Copy + PartialOrd + std::fmt::Debug { + + // print in-order traversal of tree + fn print_traversal(&self) { + if self.is_some() && self.value().is_some() { + self.left().print_traversal(); + if self.is_red() { + print!("<{:?}>", self.value().unwrap()); + } else { + print!("[{:?}]", self.value().unwrap()); + } + self.right().print_traversal(); + } + } + + // count number of leaves in a tree + fn count_leaves(&self) -> usize { + if self.value().is_none() { + 0 + } else if self.left().value().is_none() && self.right().value().is_none() { + 1 + } else { + self.left().count_leaves() + self.right().count_leaves() + } + } + + // retrieve a vector containing each node in a tree with respective depth + fn get_depth_vec(&self) -> Vec<(T, usize)> { + let mut vec: Vec<(T, usize)> = Vec::new(); + self.calc_depth(0, &mut vec); + vec.sort_by(|a, b| b.1.cmp(&a.1)); // sort by depth + vec.dedup_by(|a, b| a.0 == b.0); // remove duplicate nodes + vec + } + + // calculate the depth of each node and store them in a given vector + fn calc_depth(&self, depth: usize, vec: &mut Vec<(T, usize)>) { + match self.value() { + Some(_) => { + vec.push((self.value().unwrap(), depth)); + self.left().calc_depth(depth+1, vec); + self.right().calc_depth(depth+1, vec) + } + None => {}, + } + } -struct TreeNode { - pub color: NodeColor, - pub value: Option, - pub parent: Option>>>, - left: Option>>>, - right: Option>>> + // required getters for node properties + fn value(&self) -> Option { + match self { + Some(tree_node) => tree_node.borrow().value, + None => None + } + } + + // setters for node properties + fn set_value(&self, value: T) { + self.unwrapped().borrow_mut().value = Some(value); + } } -impl TreeNode { - fn new(val: T) -> TreeNode { - TreeNode { - color: NodeColor::Black, +impl RBTNodeTraits for RBTreeNode where T: Copy + PartialOrd + std::fmt::Debug { + + // return new instance of a red-black tree node + fn new(val: T) -> RBTreeNode { + let tree_node = Some(Rc::new(RefCell::new(RBTNode { + color: NodeColor::Red, value: Some(val), parent: None, left: None, right: None + }))); + tree_node.left().set_parent(tree_node.clone()); + tree_node.right().set_parent(tree_node.clone()); + + tree_node + } + + // return unwrapped tree node (reference counter) + fn unwrapped(&self) -> Rc>> { + match self { + Some(tree_node) => Rc::clone(&tree_node), + None => panic!("Error unwrapping tree node") } } - fn is_some(& self) -> bool { - match self.value { - Some(_) => true, - None => false, + // compare if two nodes are the same or not + fn compare(&self, node: &RBTreeNode) -> bool { + if self.is_none() || node.is_none() { + return false } + Rc::ptr_eq(&self.unwrapped(), &node.unwrapped()) } - fn new_wrapped(val: T) -> Option>>> { - Some(Rc::new(RefCell::new(TreeNode::new(val)))) + // search for a node with given value + fn find_node(&self, value: T) -> RBTreeNode { + match self.value() { + Some(_) => { + if value == self.value().unwrap() { + self.clone() + } else if value < self.value().unwrap() { + self.left().find_node(value) + } else { + self.right().find_node(value) + } + }, + None => None + } + } + + // get the height of a node + fn node_height(&self) -> usize { + match self.value() { + Some(_) => { + let left_height = self.left().node_height(); + let right_height = self.right().node_height(); + + if left_height > right_height { + left_height + 1 + } else { + right_height + 1 + } + }, + None => 0 + } } - // fn unwrap(node: Option>>>) -> TreeNode { - // node.as_ref().unwrap().as_ref().get_mut() - // } + // determine if a node is red + fn is_red(&self) -> bool { + self.color() == NodeColor::Red + } + + // determine if a node is black + fn is_black(&self) -> bool { + self.color() == NodeColor::Black + } + + // return the parent of a node + fn parent(&self) -> RBTreeNode { + match self { + Some(tree_node) => tree_node.borrow().parent.clone(), + None => None + } + } + + // return the left child of a node + fn left(&self) -> RBTreeNode { + match self { + Some(tree_node) => tree_node.borrow().left.clone(), + None => None + } + } + + // return the right child of a node + fn right(&self) -> RBTreeNode { + match self { + Some(tree_node) => tree_node.borrow().right.clone(), + None => None + } + } + + // return the parent of a node's parent + fn grandparent(&self) -> RBTreeNode { + self.parent().parent() + } + + // return a parent node's sibling + fn uncle(&self) -> RBTreeNode { + if self.grandparent().left().is_none() || self.grandparent().right().is_none() { + None + } else if self.parent().compare(&self.grandparent().left()) { + self.grandparent().right() + } else { + self.grandparent().left() + } + } + + // return a node's sibling + fn sibling(&self) -> RBTreeNode { + match self.compare(&self.parent().left()) { + true => self.parent().right(), + false => self.parent().left(), + } + } + + // return the color of a node + fn color(&self) -> NodeColor { + match self { + Some(tree_node) => tree_node.borrow().color.clone(), + None => NodeColor::Black // nil nodes are black + } + } + + // set the color of a node + fn set_color(&self, color: NodeColor) { + self.unwrapped().borrow_mut().color = color; + } + + // set the parent of a node + fn set_parent(&mut self, parent: RBTreeNode) { + match self { + Some(tree_node) => tree_node.borrow_mut().parent = parent, + None => *self = Some(Rc::new(RefCell::new(RBTNode { + color: self.color(), + value: self.value(), + parent: parent, + left: self.left(), + right: self.right() + }))) + } + } + + // set the left child of a node + fn set_left(&mut self, left: RBTreeNode) { + match self { + Some(tree_node) => tree_node.borrow_mut().left = left, + None => *self = Some(Rc::new(RefCell::new(RBTNode { + color: self.color(), + value: self.value(), + parent: self.parent(), + left: left, + right: self.right() + }))) + } + } + + // set the right child of a node + fn set_right(&mut self, right: RBTreeNode) { + match self { + Some(tree_node) => tree_node.borrow_mut().right = right, + None => *self = Some(Rc::new(RefCell::new(RBTNode { + color: self.color(), + value: self.value(), + parent: self.parent(), + left: self.left(), + right: right + }))) + } + } } -struct RBTree { - root: Option>>>, +/******************** RBTree Helpers ********************/ + +pub struct RBTree { + root: RBTreeNode, len: usize } -impl RBTree - where T: Ord + PartialEq + PartialOrd { - // fn rotate() { +impl RBTree { + pub fn new() -> RBTree { + RBTree { + root: None, + len: 0 + } + } +} + +pub trait RBTreeTraits { + // methods required to be implemented for a fully functional red-black tree + fn search(&self, value: T) -> RBTreeNode; + fn rotate(&mut self, node: &RBTreeNode, direction: bool); + fn fix_insert_color(&mut self, node: &RBTreeNode); + fn fix_delete_color(&mut self, node: &RBTreeNode); +} - // } - - // fn fixDelColor() { - - // } +impl TreeBase for RBTree where T: Copy + PartialOrd + std::fmt::Debug { - fn fixInsColor() { - + // get the height of a tree + fn height(&self) -> usize { + self.root.node_height() } - - pub fn delete_node(&mut self) { - self.len -= 1; + + // check if a tree is empty + fn is_empty(&self) -> bool { + self.root.is_none() } - - pub fn height(&self) -> usize { - // TODO: add match statements for left and right heights - // need to borrow and match stuff. see commented code below - - // let left_height = self.root.left; - // let right_height = self.root.right; - - // let left_height = match *self.left.borrow() { - // Some(ref left) => left.height(), - // None => 0, - // }; - // let right_height = match *self.right.borrow() { - // Some(ref right) => right.height(), - // None => 0, - // }; - // if left_height > right_height { - // left_height += 1; - // } else { - // right_height += 1; - // } - 0 - } - - pub fn insert_node2(&mut self, value: T) { - if self.len == 0 { - self.root = TreeNode::new_wrapped(value); - } else { - let root_unwrapped = self.root.unwrap().borrow(); - let mut new_node = TreeNode::new(value); - let mut subroot = root_unwrapped; - let is_left_child = true; - while new_node.value != root_unwrapped.value && new_node.parent.is_none() { + // get the size of a tree + fn size(&self) -> usize { + self.len + } - if !subroot.is_some() { + // check if a tree contains a value + fn contains(&self, value: T) -> bool { + match self.search(value) { + Some(_) => true, + None => false + } + } - // insert at empty node - new_node.parent = subroot.parent; - let mut unwrapped_parent = subroot.parent.unwrap().borrow(); + // count the number of leaves in a tree + fn count_leaves(&self) -> usize { + self.root.count_leaves() + } - if is_left_child { - unwrapped_parent.left = TreeNode::new_wrapped(value); - } else { - unwrapped_parent.right = TreeNode::new_wrapped(value); - } + // insert a node into a tree + fn insert_node(&mut self, value: T) { + let mut new_node = RBTreeNode::new(value); - } else if value < subroot.value.unwrap() { - subroot = subroot.left.unwrap().borrow(); + if self.is_empty() { + self.root = new_node.clone(); + } else if self.search(value).is_some() { + // sticking with printing an err msg because panic causes the terminal to exit the program + println!("Value already exists!"); + return; + } else { + let mut curr_node = self.root.clone(); + let mut curr_node_parent: RBTreeNode = None; + let mut is_left_child = true; + + // find empty node + while curr_node.value().is_some() { + curr_node_parent = curr_node.clone(); + if value < curr_node.value().unwrap() { + curr_node = curr_node.left(); is_left_child = true; - - } else { //value > curr_node_unwrapped.value { - subroot = subroot.right.unwrap().borrow(); + } else { + curr_node = curr_node.right(); is_left_child = false; } } - - // rebalance the tree here i guess and fix colors - fixInsColor(); + + // place new_node in found spot + if curr_node_parent.is_some() { + new_node.set_parent(curr_node_parent); + if is_left_child { + new_node.parent().set_left(new_node.clone()); + } else { + new_node.parent().set_right(new_node.clone()); + } + } else { + panic!("Current node has no parent!"); + } } + + self.fix_insert_color(&new_node); + self.len += 1; } + // remove/delete a node from a tree + fn delete_node(&mut self, value: T) { + let mut node = self.search(value); - // pub fn insert_node(&mut self, value: T) { - // if self.len == 0 { - // self.root = TreeNode::new_wrapped(value); - // } else { - // let curr_node = &self.root; - // let curr_node_unwrapped = curr_node.as_ref().unwrap().as_ref().get_mut(); + if node.is_none() { + println!("Node does not exist!"); + return; + } - // if value == curr_node_unwrapped.value { - // return - // } + if node.left().value().is_some() && node.right().value().is_some() { + let mut larger = node.left(); + while larger.right().value().is_some() { // larger.left() + larger = larger.right(); // larger.left() + } + node.set_value(larger.value().unwrap()); + node = larger.clone(); + } - // // loop {} - // let curr_node = if value < curr_node_unwrapped.value { - // curr_node_unwrapped.left - // } else { - // curr_node_unwrapped.right - // }; + // set node to null sibling + let mut child = match node.left().value() { + Some(_) => node.left(), + None => node.right() + }; + + if !node.compare(&self.root) && node.parent().is_some() { + child.set_parent(node.parent()); + if node.compare(&node.parent().left()) { + node.parent().set_left(child.clone()); + } else { + node.parent().set_right(child.clone()); + } + } else if child.is_none() { + // empty tree if child is None + self.root = None; + self.len -= 1; + return; + } else { + // set root to child + self.root = child.clone(); + child.set_parent(None); + } + + if node.is_black() { + if child.is_red() { + child.set_color(NodeColor::Black); + } else { + self.fix_delete_color(&child); + } + } - // let new_node = TreeNode::new(value); - // new_node.parent = curr_node_unwrapped.parent; + self.len -= 1; + } - // match curr_node { - // // _ => Some(Rc::new(RefCell::new(new_node))), - // Some(sub_node) => self.insert_node(value), - // None => { - // // let temp = Some(Rc::new(RefCell::new(TreeNode::new(value)))); - // // *curr_node = temp; - // *curr_node = Some(Rc::new(RefCell::new(new_node))); - // }, - // // &mut Some(ref mut sub_node) => sub_node.insert_node(value), - // // &mut None => { - // // let temp = TreeNode { value: value, left_child: None, right_child: None}; - // // let boxed_node = Some(Box::new(temp)); - // // *new_node = boxed_node; - // // } - // } + // print the in-order traversal in a tree + fn print(&self) { + if self.is_empty() { + println!("Tree is empty. Nothing to print."); + } else { + println!("Root: {:?}", self.root.value().unwrap()); + self.root.print_traversal(); + println!(); + } + } - // // balance the tree here i guess. need to pass in node tho - // fixInsColor(); - // } - // self.len += 1; - // } - - pub fn is_empty(&self) -> bool { - self.len == 0 + // return all the values in a red-black tree by depth + fn get_depth_vec(&self) -> Vec<(T, usize)> { + self.root.get_depth_vec() } +} + +impl RBTreeTraits for RBTree where T: Copy + PartialOrd + std::fmt::Debug { - pub fn print() { - + // search for a node with given value in a tree + fn search(&self, value: T) -> RBTreeNode { + self.root.find_node(value) } - pub fn size(&self) -> usize { - self.len + // rotate a subtree/node either LEFT or RIGHT + fn rotate(&mut self, node: &RBTreeNode, direction: bool) { + let mut parent = node.parent().clone(); + let mut grandparent = node.grandparent().clone(); + let mut node = node.clone(); + + if parent.compare(&self.root) { + self.root = node.clone(); + } else { + node.set_parent(grandparent.clone()); + if parent.compare(&grandparent.left()) { + grandparent.set_left(node.clone()); + } else { + grandparent.set_right(node.clone()); + } + } + + parent.set_parent(node.clone()); + if direction == ROTATE_LEFT { + if node.left().is_some() { + node.left().set_parent(parent.clone()); + } + parent.set_right(node.left()); + node.set_left(parent); + } else { + if node.right().is_some() { + node.right().set_parent(parent.clone()); + } + parent.set_left(node.right()); + node.set_right(parent); + } + } + + // rebalance a tree by fixing the colors of nodes after inserting a node + fn fix_insert_color(&mut self, node: &RBTreeNode) { + // CASE 1 + if node.compare(&self.root) { + node.set_color(NodeColor::Black); + return; + } + + // CASE 2 + let parent = node.parent().clone(); + if parent.is_black() { + return; + } + + // CASE 3 + let uncle = node.uncle().clone(); + let grandparent = node.grandparent().clone(); + if uncle.is_some() && uncle.is_red() { + parent.set_color(NodeColor::Black); + uncle.set_color(NodeColor::Black); + grandparent.set_color(NodeColor::Red); + self.fix_insert_color(&grandparent); + return; + } + + // CASE 4 + let mut node = node.clone(); + if node.compare(&parent.right()) && parent.compare(&grandparent.left()) { + self.rotate(&node, ROTATE_LEFT); + node = node.left(); + } else if node.compare(&parent.left()) && parent.compare(&grandparent.right()) { + self.rotate(&node, ROTATE_RIGHT); + node = node.right(); + } + + // CASE 5 + let parent = node.parent().clone(); + let grandparent = node.grandparent().clone(); + if node.compare(&parent.left()) { + self.rotate(&parent, ROTATE_RIGHT); + } else { + self.rotate(&parent, ROTATE_LEFT); + } + parent.set_color(NodeColor::Black); + grandparent.set_color(NodeColor::Red); + } + + // rebalance a tree by fixing the colors of nodes after deleting a node + fn fix_delete_color(&mut self, node: &RBTreeNode) { + // CASE 1: node is root so done (it’s already black) + if node.compare(&self.root) { + return; + } + + let mut sibling = node.sibling(); + + // CASE 2 + if sibling.is_red() { + sibling.parent().set_color(NodeColor::Red); + sibling.set_color(NodeColor::Black); + + if node.compare(&node.parent().left()) { + self.rotate(&sibling, ROTATE_LEFT); + } else { + self.rotate(&sibling, ROTATE_RIGHT); + } + + // update sibling after rotating + sibling = node.sibling(); + } + + // CASE 3 + if node.parent().is_black() && sibling.is_black() && + sibling.left().is_black() && sibling.right().is_black() { + sibling.set_color(NodeColor::Red); + self.fix_delete_color(&node.parent()); + return; + } + + // CASE 4 + if node.parent().is_red() && sibling.is_black() && + sibling.left().is_black() && sibling.right().is_black() { + sibling.set_color(NodeColor::Red); + node.parent().set_color(NodeColor::Black); + return; + } + + // CASE 5 + if sibling.is_black() { + sibling.set_color(NodeColor::Red); + if node.compare(&node.parent().left()) && + sibling.left().is_red() && sibling.right().is_black() { + sibling.left().set_color(NodeColor::Black); + self.rotate(&sibling.left(), ROTATE_RIGHT); + } else if node.compare(&node.parent().right()) && + sibling.left().is_black() && sibling.right().is_red() { + sibling.right().set_color(NodeColor::Black); + self.rotate(&sibling.left(), ROTATE_LEFT); + } + + // update sibling + sibling = node.sibling(); + } + + // CASE 6 + sibling.set_color(node.parent().color()); + node.parent().set_color(NodeColor::Black); + if node.compare(&node.parent().left()) { + sibling.right().set_color(NodeColor::Black); + self.rotate(&sibling, ROTATE_LEFT); + } else { + sibling.left().set_color(NodeColor::Black); + self.rotate(&sibling, ROTATE_RIGHT); + } } } \ No newline at end of file diff --git a/src/tests.rs b/src/tests.rs new file mode 100644 index 0000000..9907826 --- /dev/null +++ b/src/tests.rs @@ -0,0 +1,263 @@ +#[allow(unused_imports)] +use super::*; +use crate::tree::*; + +pub fn benchmark_insert_search(in_tree: impl TreeBase, tree_size: u32) { + + let mut tree = in_tree; + + let time = std::time::Instant::now(); + for i in 0..tree_size { + tree.insert_node(i); + } + + for i in 0..tree_size/10 { + match tree.contains(i) { + true => { continue; }, + false => println!("nope"), + } + } + + println!("Elapsed time for tree size of {}: {} ms", tree_size, time.elapsed().as_millis()); +} + +#[allow(dead_code)] +pub fn insert_search_deepest(in_tree: impl TreeBase, tree_size: u32) { + + let mut tree = in_tree; + + let time = std::time::Instant::now(); + for i in 0..tree_size { + tree.insert_node(i); + } + + let depth = tree.get_depth_vec(); + + for i in 0..tree_size/10 { + match tree.contains(depth[i as usize].0) { + true => { continue; }, + false => println!("nope"), + } + } + + println!("Elapsed time for tree size of {}: {} ms", tree_size, time.elapsed().as_millis()); +} + + +#[test] +#[ignore] +pub fn benchmark_insert_search_deepest_nodes() { + + for tree_size in vec![10_000, 40_000, 70_000, 100_000, 130_000] { + let tree: red_black_tree::RBTree = red_black_tree::RBTree::new(); + insert_search_deepest(tree, tree_size); + } + + for tree_size in vec![10_000, 40_000, 70_000, 100_000, 130_000] { + let tree: avl_tree::AVLTree = avl_tree::AVLTree::new(); + insert_search_deepest(tree, tree_size); + } +} + +#[test] +pub fn test_treenode() { + let treenode: red_black_tree::RBTreeNode = red_black_tree::RBTreeNode::new(5); + + assert_eq!(treenode.value().unwrap(), 5); + assert_eq!(treenode.color(), NodeColor::Red); + + treenode.set_color(red_black_tree::NodeColor::Black); + assert_eq!(treenode.color(), NodeColor::Black); + + treenode.set_value(10); + assert_eq!(treenode.value().unwrap(), 10); +} + +#[test] +pub fn test_insert_delete_rbt() { + let mut rbt2: red_black_tree::RBTree = red_black_tree::RBTree::new(); + let mut vec_in = vec![30, 20, 40, 10, 50]; + for &x in vec_in.iter() { + rbt2.insert_node(x); + assert!(rbt2.contains(x)); + } + + assert_eq!(rbt2.height(), 3); + assert_eq!(rbt2.size(), 5); + assert_eq!(rbt2.count_leaves(), 2); + + vec_in = vec![10, 20, 30]; + for &x in vec_in.iter() { + rbt2.delete_node(x); + assert!(!rbt2.contains(x)); + } + + assert_eq!(rbt2.height(), 2); + assert_eq!(rbt2.size(), 2); + assert_eq!(rbt2.count_leaves(), 1); +} + +#[test] +pub fn test_insert_rbt_all_cases() { + let mut rbt: red_black_tree::RBTree = red_black_tree::RBTree::new(); + + let vec_in = vec![5, 70, 35, 8, 100, 60, 120, 1, 84, 17]; + for &x in vec_in.iter() { + rbt.insert_node(x); + } + + assert_eq!(rbt.height(), 4); + assert_eq!(rbt.size(), 10); + assert_eq!(rbt.count_leaves(), 5); + +} + +#[test] +fn test_rbt_search_contains() { + let mut rbt: red_black_tree::RBTree = red_black_tree::RBTree::new(); + let vec_in = vec![35, 5, 1, 8, 17, 60, 70, 84, 100, 120]; + + for &x in vec_in.iter() { + rbt.insert_node(x); + assert!(rbt.search(x).is_some()); + assert!(rbt.contains(x)); + } + + assert_eq!(rbt.height(), 5); + assert_eq!(rbt.size(), 10); + assert_eq!(rbt.count_leaves(), 5); + + assert!(rbt.search(10).is_none()); + assert!(!rbt.contains(10)); +} + +#[test] +fn test_delete_rbt_all() { + let mut rbt: red_black_tree::RBTree = red_black_tree::RBTree::new(); + + assert!(rbt.is_empty() == true); + + for x in 1..=10 { + rbt.insert_node(x); + assert!(rbt.contains(x)); + } + + assert!(rbt.is_empty() == false); + assert_eq!(rbt.height(), 5); + assert_eq!(rbt.size(), 10); + assert_eq!(rbt.count_leaves(), 5); + rbt.print(); + + let vec_in = vec![6, 4, 2, 8, 9, 7, 3, 1, 5, 10]; + for &x in vec_in.iter() { + rbt.delete_node(x); + assert!(!rbt.contains(x)); + } + + assert!(rbt.is_empty() == true); + assert_eq!(rbt.height(), 0); + assert_eq!(rbt.size(), 0); + assert_eq!(rbt.count_leaves(), 0); +} + +#[test] +pub fn test_delete_rbt_cases_1_3_5_6() { + let mut rbt: red_black_tree::RBTree = red_black_tree::RBTree::new(); + + assert!(rbt.is_empty()); + for x in 1..=10 { + rbt.insert_node(x); + assert!(rbt.contains(x)); + } + assert!(!rbt.is_empty()); + assert_eq!(rbt.height(), 5); + assert_eq!(rbt.size(), 10); + assert_eq!(rbt.count_leaves(), 5); + + let vec_in: Vec = vec![4, 6, 8, 1, 5, 9, 2]; + for &x in vec_in.iter() { + rbt.delete_node(x); + assert!(!rbt.contains(x)); + } + assert!(!rbt.is_empty()); + assert_eq!(rbt.height(), 2); + assert_eq!(rbt.size(), 3); + assert_eq!(rbt.count_leaves(), 2); +} + +#[test] +pub fn test_delete_rbt_cases_2_4() { + + let mut rbt: red_black_tree::RBTree = red_black_tree::RBTree::new(); + let vec_in: Vec = vec![5, 70, 35, 8, 98, 60, 99, 99, 1, 84, 17]; + + assert!(rbt.is_empty()); + for &x in vec_in.iter() { + rbt.insert_node(x); + assert!(rbt.contains(x)); + } + assert!(!rbt.is_empty()); + assert_eq!(rbt.height(), 4); + assert_eq!(rbt.size(), 10); + assert_eq!(rbt.count_leaves(), 5); + + let to_delete = vec![17, 84, 99, 5, 1, 60]; + for &x in to_delete.iter() { + rbt.delete_node(x); + assert!(!rbt.contains(x)); + } + assert!(!rbt.is_empty()); + assert_eq!(rbt.height(), 3); + assert_eq!(rbt.size(), 4); + assert_eq!(rbt.count_leaves(), 2); +} + +#[test] +pub fn avl_test_insert_delete_some() { + + let mut avl: avl_tree::AVLTree = avl_tree::AVLTree::new(); + let mut vec_in: Vec = vec![5, 70, 35, 8, 98, 60, 99, 99, 1, 84, 17]; + + for &x in vec_in.iter() { + avl.insert_node(x); + assert!(avl.contains(x)); + } + + assert_eq!(avl.height(), 4); + assert_eq!(avl.size(), 10); + assert_eq!(avl.count_leaves(), 5); + + vec_in = vec![17, 84, 99, 5, 1, 60]; + for &x in vec_in.iter() { + avl.delete_node(x); + assert!(!avl.contains(x)); + } + + assert_eq!(avl.height(), 3); + assert_eq!(avl.size(), 4); + assert_eq!(avl.count_leaves(), 2); +} + +#[test] +pub fn delete_all_avl() { + let mut avl: avl_tree::AVLTree = avl_tree::AVLTree::new(); + + for x in 1..=10 { + avl.insert_node(x); + assert!(avl.contains(x)); + } + + assert_eq!(avl.height(), 4); + assert_eq!(avl.size(), 10); + assert_eq!(avl.count_leaves(), 5); + + let vec_in = vec![4, 5, 8, 7, 3, 2, 1, 9, 6, 10]; + for &x in vec_in.iter() { + avl.delete_node(x); + assert!(!avl.contains(x)); + } + + assert_eq!(avl.height(), 0); + assert_eq!(avl.size(), 0); + assert_eq!(avl.count_leaves(), 0); +} \ No newline at end of file diff --git a/src/tree.rs b/src/tree.rs new file mode 100644 index 0000000..9c912d2 --- /dev/null +++ b/src/tree.rs @@ -0,0 +1,26 @@ +pub trait NodeTraits { + // required/default helper functions + fn print_traversal(&self); + fn count_leaves(&self) -> usize; + fn get_depth_vec(&self) -> Vec<(T, usize)>; + fn calc_depth(&self, dep: usize, vec: &mut Vec<(T, usize)>); + + // required getters for node properties + fn value(&self) -> Option; + + // setters for node properties + fn set_value(&self, value: T); +} + +pub trait TreeBase { + fn height(&self) -> usize; + fn is_empty(&self) -> bool; + fn size(&self) -> usize; + fn contains(&self, value: T) -> bool; + fn count_leaves(&self) -> usize; + fn insert_node(&mut self, value: T); + fn delete_node(&mut self, value: T); + fn print(&self); + fn get_depth_vec(&self) -> Vec<(T, usize)>; +} + diff --git a/test.txt b/test.txt new file mode 100644 index 0000000..e69de29 diff --git a/testhistory.txt b/testhistory.txt new file mode 100644 index 0000000..5293d0b --- /dev/null +++ b/testhistory.txt @@ -0,0 +1,132 @@ +diff --git a/src/tests.rs b/src/tests.rs +index 8e6d1fa..c37f23c 100644 +--- a/src/tests.rs ++++ b/src/tests.rs +@@ -127,63 +127,99 @@ pub fn it_works() { + + println!("\n==== Start Testing DELETE RBTree Here ====\n"); + let mut rbt2: red_black_tree::RBTree = red_black_tree::RBTree::new(); ++ let mut vec_in: Vec = vec![30, 20, 40, 10]; ++ ++ for &x in vec_in.iter() { ++ println!("inserting {} ...", x); ++ rbt2.insert_node(x); ++ println!("size is now = {}", rbt2.size()); ++ rbt2.print(); ++ println!("height = {}", rbt2.height()); ++ println!("leaves = {}", rbt2.count_leaves()); ++ println!(); ++ } + +- println!("Inserting 30 ..."); +- rbt2.insert_node(30); ++ println!("Deleting 10 ..."); ++ rbt2.delete_node(10); + println!("size = {}", rbt2.size()); + rbt2.print(); + println!("height = {}", rbt2.height()); +- println!("leaves = {}", rbt2.count_leaves()); ++ println!("num leaves = {}", rbt2.count_leaves()); + println!(); + +- println!("Inserting 20 ..."); +- rbt2.insert_node(20); ++ println!("Inserting 50 ..."); ++ rbt2.insert_node(50); + println!("size = {}", rbt2.size()); + rbt2.print(); + println!("height = {}", rbt2.height()); + println!("leaves = {}", rbt2.count_leaves()); + println!(); + +- println!("Inserting 40 ..."); +- rbt2.insert_node(40); ++ println!("Deleting 20 ..."); ++ rbt2.delete_node(20); + println!("size = {}", rbt2.size()); + rbt2.print(); + println!("height = {}", rbt2.height()); +- println!("leaves = {}", rbt2.count_leaves()); ++ println!("num leaves = {}", rbt2.count_leaves()); + println!(); + +- println!("Inserting 10 ..."); +- rbt2.insert_node(10); +- println!("size = {}", rbt2.size()); +- rbt2.print(); +- println!("height = {}", rbt2.height()); +- println!("leaves = {}", rbt2.count_leaves()); +- println!(); ++ vec_in = vec![51, 52, 53, 54]; + +- println!("Deleting 10 ..."); +- rbt2.delete_node(10); +- println!("size = {}", rbt2.size()); +- rbt2.print(); +- println!("height = {}", rbt2.height()); +- println!("num leaves = {}", rbt2.count_leaves()); +- println!(); ++ for &x in vec_in.iter() { ++ println!("inserting {} ...", x); ++ rbt2.insert_node(x); ++ println!("size is now = {}", rbt2.size()); ++ rbt2.print(); ++ println!("height = {}", rbt2.height()); ++ println!("leaves = {}", rbt2.count_leaves()); ++ println!(); ++ } + +- println!("Inserting 50 ..."); +- rbt2.insert_node(50); ++ println!("Deleting 52 ..."); ++ rbt2.delete_node(52); + println!("size = {}", rbt2.size()); + rbt2.print(); + println!("height = {}", rbt2.height()); +- println!("leaves = {}", rbt2.count_leaves()); ++ println!("num leaves = {}", rbt2.count_leaves()); + println!(); + +- println!("Deleting 20 ..."); +- rbt2.delete_node(20); ++ println!("Deleting 53 ..."); ++ rbt2.delete_node(53); + println!("size = {}", rbt2.size()); + rbt2.print(); + println!("height = {}", rbt2.height()); + println!("num leaves = {}", rbt2.count_leaves()); + println!(); ++ println!("{:?}", rbt2.root.value()); + ++} ++ +pub fn ray_delete_test() { + println!("\n==== Start Testing DELETE RBTree Here ====\n"); + + let mut rbt: red_black_tree::RBTree = red_black_tree::RBTree::new(); + let mut vec_in: Vec = vec![5, 70, 35, 8, 98, 60, 99, 99, 1, 84, 17]; + + for &x in vec_in.iter() { + println!("inserting {} ...", x); + rbt.insert_node(x); + println!("size is now = {}", rbt.size()); + rbt.print(); + println!("height = {}", rbt.height()); + println!("leaves = {}", rbt.count_leaves()); + println!(); + } + + vec_in = vec![17, 84, 99, 5, 1]; + for &x in vec_in.iter() { + println!("deleting {} ...", x); + rbt.delete_node(x); + println!("size is now = {}", rbt.size()); + rbt.print(); + println!("height = {}", rbt.height()); + println!("leaves = {}", rbt.count_leaves()); + println!(); + } + + + } +\ No newline at end of file