From cec829338273c9be465a86a1c24b662d043acbfe Mon Sep 17 00:00:00 2001 From: jacekpoz Date: Wed, 5 Jun 2024 23:23:44 +0200 Subject: [PATCH] init zad3 --- lab4/zad3/.gitignore | 1 + lab4/zad3/Cargo.lock | 87 ++++++ lab4/zad3/Cargo.toml | 8 + lab4/zad3/src/main.rs | 34 +++ libtree/src/lib.rs | 2 + libtree/src/red_black_tree.rs | 540 ++++++++++++++++++++++++++++++++++ 6 files changed, 672 insertions(+) create mode 100644 lab4/zad3/.gitignore create mode 100644 lab4/zad3/Cargo.lock create mode 100644 lab4/zad3/Cargo.toml create mode 100644 lab4/zad3/src/main.rs create mode 100644 libtree/src/red_black_tree.rs diff --git a/lab4/zad3/.gitignore b/lab4/zad3/.gitignore new file mode 100644 index 0000000..eb5a316 --- /dev/null +++ b/lab4/zad3/.gitignore @@ -0,0 +1 @@ +target diff --git a/lab4/zad3/Cargo.lock b/lab4/zad3/Cargo.lock new file mode 100644 index 0000000..448d0c4 --- /dev/null +++ b/lab4/zad3/Cargo.lock @@ -0,0 +1,87 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "libgen" +version = "0.1.0" +dependencies = [ + "rand", +] + +[[package]] +name = "libtree" +version = "0.1.0" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "zad3" +version = "0.1.0" +dependencies = [ + "libgen", + "libtree", +] diff --git a/lab4/zad3/Cargo.toml b/lab4/zad3/Cargo.toml new file mode 100644 index 0000000..b9aeadd --- /dev/null +++ b/lab4/zad3/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "zad3" +version = "0.1.0" +edition = "2021" + +[dependencies] +libgen = { path = "../../libgen" } +libtree = { path = "../../libtree" } diff --git a/lab4/zad3/src/main.rs b/lab4/zad3/src/main.rs new file mode 100644 index 0000000..e6a7f0d --- /dev/null +++ b/lab4/zad3/src/main.rs @@ -0,0 +1,34 @@ +use libgen::{gen_asc, gen_rand}; +use libtree::RedBlackTree; + +fn main() { + println!("1. insert 50 ascending; delete 50 random"); + let mut tree1 = RedBlackTree::new(50); + + for i in gen_asc(50) { + println!("insert {i}"); + tree1.insert(i); + println!("{}", tree1); + } + + for i in gen_rand(50) { + println!("delete {i}"); + tree1.delete(i); + println!("{}", tree1); + } + + println!("2. insert 50 random; delete 50 random"); + let mut tree2 = RedBlackTree::new(50); + + for i in gen_rand(50) { + println!("insert {i}"); + tree2.insert(i); + println!("{}", tree2); + } + + for i in gen_rand(50) { + println!("delete {i}"); + tree2.delete(i); + println!("{}", tree2); + } +} diff --git a/libtree/src/lib.rs b/libtree/src/lib.rs index bf08b62..ccae51a 100644 --- a/libtree/src/lib.rs +++ b/libtree/src/lib.rs @@ -1,6 +1,8 @@ mod binary_search_tree; +mod red_black_tree; pub use binary_search_tree::*; +pub use red_black_tree::*; #[derive(Clone, Default)] pub struct Stats { diff --git a/libtree/src/red_black_tree.rs b/libtree/src/red_black_tree.rs new file mode 100644 index 0000000..c41814a --- /dev/null +++ b/libtree/src/red_black_tree.rs @@ -0,0 +1,540 @@ +use std::{cmp::Ordering, fmt::Display, ptr}; + +#[derive(Clone, Copy, PartialEq)] +pub enum Colour { Red, Black } + +#[derive(Clone)] +pub struct RedBlackTreeNode { + key: u64, + colour: Colour, + left: RBT, + right: RBT, + parent: RBT, +} + +struct RBT(*mut RedBlackTreeNode); + +impl Clone for RBT { + fn clone(&self) -> Self { + Self(self.0) + } +} + +impl Copy for RBT {} + +impl Ord for RBT { + fn cmp(&self, other: &RBT) -> Ordering { + unsafe { (*self.0).key.cmp(&(*other.0).key) } + } +} + +impl PartialOrd for RBT { + fn partial_cmp(&self, other: &RBT) -> Option { + unsafe { Some((*self.0).key.cmp(&(*other.0).key)) } + } +} + +impl PartialEq for RBT { + fn eq(&self, other: &RBT) -> bool { + self.0 == other.0 + } +} + +impl Eq for RBT {} + +impl RBT { + fn new(key: u64) -> Self { + Self(Box::into_raw(Box::new(RedBlackTreeNode { + key, + colour: Colour::Black, + left: Self::null(), + right: Self::null(), + parent: Self::null(), + }))) + } + + unsafe fn deep_clone(&self) -> Self { + let mut node = Self::new((*self.0).key); + if !self.left().is_null() { + node.set_left(self.left().deep_clone()); + node.left().set_parent(node); + } + if !self.right().is_null() { + node.set_right(self.right().deep_clone()); + node.right().set_parent(node); + } + node + } + + fn minimum(self) -> Self { + let mut temp = self.clone(); + while !temp.left().is_null() { + temp = temp.left(); + } + return temp; + } + + fn set_colour(&mut self, colour: Colour) { + if !self.is_null() { + unsafe { (*self.0).colour = colour; } + } + } + + fn colour(&self) -> Colour { + match self.is_null() { + true => Colour::Black, + false => unsafe { (*self.0).colour } + } + } + + fn set_parent(&mut self, parent: Self) { + if !self.is_null() { + unsafe { (*self.0).parent = parent; } + } + } + + fn set_right(&mut self, right: Self) { + if !self.is_null() { + unsafe { (*self.0).right = right; } + } + } + + fn set_left(&mut self, left: Self) { + if !self.is_null() { + unsafe { (*self.0).left = left; } + } + } + + fn parent(&self) -> Self { + match self.is_null() { + true => Self::null(), + false => unsafe { (*self.0).parent.clone() } + } + } + + fn right(&self) -> Self { + match self.is_null() { + true => Self::null(), + false => unsafe { (*self.0).right.clone() } + } + } + + fn left(&self) -> Self { + match self.is_null() { + true => Self::null(), + false => unsafe { (*self.0).left.clone() } + } + } + + fn null() -> Self { + Self(ptr::null_mut()) + } + + fn is_null(&self) -> bool { + self.0.is_null() + } + + // 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 self.is_null() { + return Ok(()); + } + + if !self.left().is_null() { + self.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)?; + } + match self.colour() { + Colour::Red => writeln!(f, "({})", unsafe { (*self.0).key })?, + Colour::Black => writeln!(f, "[{}]", unsafe { (*self.0).key })?, + } + Self::replace_nth_char_ascii(left_trace, depth, ' '); + + if !self.right().is_null() { + Self::replace_nth_char_ascii(right_trace, depth, '|'); + self.right()._print(f, depth + 1, '\\', left_trace, right_trace)?; + } + + Ok(()) + } +} + +pub struct RedBlackTree { + root: RBT, + pub size: usize, + print_capacity: usize, +} + +impl Clone for RedBlackTree { + fn clone(&self) -> Self { + unsafe { + let mut ret = RedBlackTree::new(self.print_capacity); + ret.root = self.root.deep_clone(); + ret.size = self.size; + ret + } + } +} + +impl RedBlackTree { + pub fn new(print_capacity: usize) -> Self { + Self { + root: RBT::null(), + size: 0, + print_capacity, + } + } + + unsafe fn rotate_left(&mut self, mut node: RBT) { + let mut right = node.right(); + node.set_right(right.left()); + + if !right.left().is_null() { + right.left().set_parent(node.clone()); + } + + right.set_parent(node.parent()); + + if node == self.root { + self.root = right.clone(); + } else if node == node.parent().left() { + node.parent().set_left(right.clone()); + } else if node == node.parent().right() { + node.parent().set_right(right.clone()); + } + + right.set_left(node.clone()); + node.set_parent(right.clone()); + } + + unsafe fn rotate_right(&mut self, mut node: RBT) { + let mut left = node.left(); + node.set_left(left.right()); + + if !left.right().is_null() { + left.right().set_parent(node.clone()); + } + + left.set_parent(node.parent()); + + if node == self.root { + self.root = left.clone(); + } else if node == node.parent().left() { + node.parent().set_left(left.clone()); + } else if node == node.parent().right() { + node.parent().set_right(left.clone()); + } + + left.set_right(node.clone()); + node.set_parent(left.clone()); + } + + unsafe fn insert_fixup(&mut self, mut node: RBT) { + use Colour::*; + + let mut parent; + let mut grandparent; + + while node.parent().colour() == Red { + parent = node.parent(); + grandparent = parent.parent(); + + if parent == grandparent.left() { + let mut uncle = grandparent.right(); + if !uncle.is_null() && uncle.colour() == Red { + uncle.set_colour(Black); + parent.set_colour(Black); + grandparent.set_colour(Red); + node = grandparent; + continue; + } + + if parent.right() == node { + self.rotate_left(parent); + let temp = parent; + parent = node; + node = temp; + } + + parent.set_colour(Black); + grandparent.set_colour(Red); + self.rotate_right(grandparent); + } else { + let mut uncle = grandparent.left(); + if !uncle.is_null() && uncle.colour() == Red { + uncle.set_colour(Black); + parent.set_colour(Black); + grandparent.set_colour(Black); + node = grandparent; + continue; + } + + if parent.left() == node { + self.rotate_right(parent); + let temp = parent; + parent = node; + node = temp; + } + + parent.set_colour(Black); + grandparent.set_colour(Red); + self.rotate_left(grandparent); + } + } + self.root.set_colour(Black); + } + + pub fn insert(&mut self, key: u64) { + self.size += 1; + let mut node = RBT::new(key); + let mut y = RBT::null(); + let mut x = self.root; + + while !x.is_null() { + y = x; + match node.cmp(&&mut x) { + Ordering::Less => { + x = x.left(); + } + _ => { + x = x.right(); + } + }; + } + node.set_parent(y); + + if y.is_null() { + self.root = node; + } else { + match node.cmp(&&mut y) { + Ordering::Less => { + y.set_left(node); + } + _ => { + y.set_right(node); + } + }; + } + + node.set_colour(Colour::Red); + unsafe { self.insert_fixup(node); } + } + + fn find_node(&self, key: u64) -> RBT { + if self.root.is_null() { + return RBT::null(); + } + let mut temp = &self.root; + unsafe { + loop { + let next = match key.cmp(&(*temp.0).key) { + Ordering::Less => &mut (*temp.0).left, + Ordering::Greater => &mut (*temp.0).right, + Ordering::Equal => return *temp, + }; + if next.is_null() { + break; + } + temp = next; + } + } + + RBT::null() + } + + unsafe fn delete_fixup(&mut self, mut node: RBT, mut parent: RBT) { + use Colour::*; + + let mut other; + while node != self.root && node.colour() == Black { + if parent.left() == node { + other = parent.right(); + + if other.colour() == Red { + other.set_colour(Black); + parent.set_colour(Red); + self.rotate_left(parent); + other = parent.right(); + } + + if other.left().colour() == Black && other.right().colour() == Black { + other.set_colour(Red); + node = parent; + parent = node.parent(); + } else { + if other.right().colour() == Black { + other.left().set_colour(Black); + other.set_colour(Red); + self.rotate_right(other); + other = parent.right(); + } + + other.set_colour(parent.colour()); + parent.set_colour(Black); + other.right().set_colour(Black); + self.rotate_left(parent); + node = self.root; + break; + } + } else { + other = parent.left(); + + if other.colour() == Red { + other.set_colour(Black); + parent.set_colour(Red); + self.rotate_right(parent); + other = parent.left(); + } + + if other.left().colour() == Black && other.right().colour() == Black { + other.set_colour(Red); + node = parent; + parent = node.parent(); + } else { + if other.left().colour() == Black { + other.right().set_colour(Black); + other.set_colour(Red); + self.rotate_left(other); + other = parent.left(); + } + + other.set_colour(parent.colour()); + parent.set_colour(Black); + other.left().set_colour(Black); + self.rotate_right(parent); + node = self.root; + break; + } + } + } + + node.set_colour(Black); + } + + unsafe fn delete_node(&mut self, node: RBT) -> u64 { + let mut child; + let mut parent; + let color; + + self.size -= 1; + + if !node.left().is_null() && !node.right().is_null() { + let mut replace = node.right().minimum(); + if node == self.root { + self.root = replace; + } else { + if node.parent().left() == node { + node.parent().set_left(replace); + } else { + node.parent().set_right(replace); + } + } + + child = replace.right(); + parent = replace.parent(); + color = replace.colour(); + if parent == node { + parent = replace; + } else { + if !child.is_null() { + child.set_parent(parent); + } + parent.set_left(child); + replace.set_right(node.right()); + node.right().set_parent(replace); + } + + replace.set_parent(node.parent()); + replace.set_colour(node.colour()); + replace.set_left(node.left()); + node.left().set_parent(replace); + + if color == Colour::Black { + self.delete_fixup(child, parent); + } + + let obj = Box::from_raw(node.0); + return obj.key; + } + + if !node.left().is_null() { + child = node.left(); + } else { + child = node.right(); + } + + parent = node.parent(); + color = node.colour(); + if !child.is_null() { + child.set_parent(parent); + } + + if self.root == node { + self.root = child + } else { + if parent.left() == node { + parent.set_left(child); + } else { + parent.set_right(child); + } + } + + if color == Colour::Black { + self.delete_fixup(child, parent); + } + + let obj = Box::from_raw(node.0); + + obj.key + } + + pub fn delete(&mut self, key: u64) -> bool { + let node = self.find_node(key); + + if node.is_null() { + return false; + } + + unsafe { self.delete_node(node); } + + true + } +} + +impl Display for RedBlackTree { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.root._print(f, 0, '-', &mut " ".repeat(self.print_capacity), &mut " ".repeat(self.print_capacity)) + } +}