From 4d3fdcb35677daca12b20fcec31451b3db8af498 Mon Sep 17 00:00:00 2001 From: bronku Date: Sat, 6 Dec 2025 20:26:29 +0100 Subject: [PATCH] record --- Cargo.lock | 203 ++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 2 + src/btree.rs | 63 ++++++++++++++++ src/main.rs | 167 ++++------------------------------------- src/node.rs | 85 +++++++++++++++++++++ src/record.rs | 30 ++++++++ 6 files changed, 397 insertions(+), 153 deletions(-) create mode 100644 src/btree.rs create mode 100644 src/node.rs create mode 100644 src/record.rs diff --git a/Cargo.lock b/Cargo.lock index 0cf8360..e90a554 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,209 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "bincode" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36eaf5d7b090263e8150820482d5d93cd964a81e4019913c972f4edcc6edb740" +dependencies = [ + "bincode_derive", + "serde", + "unty", +] + +[[package]] +name = "bincode_derive" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf95709a440f45e986983918d0e8a1f30a9b1df04918fc828670606804ac3c09" +dependencies = [ + "virtue", +] + [[package]] name = "btree" version = "0.1.0" +dependencies = [ + "bincode", + "rand", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", +] + +[[package]] +name = "libc" +version = "0.2.178" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "syn" +version = "2.0.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "unty" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae" + +[[package]] +name = "virtue" +version = "0.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "051eb1abcf10076295e815102942cc58f9d5e3b4560e46e53c21e8ff6f3af7b1" + +[[package]] +name = "wasip2" +version = "1.0.1+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" + +[[package]] +name = "zerocopy" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml index 4b63a77..5cbed41 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,3 +4,5 @@ version = "0.1.0" edition = "2024" [dependencies] +bincode = "2.0.1" +rand = "0.9.2" diff --git a/src/btree.rs b/src/btree.rs new file mode 100644 index 0000000..f5ba0f2 --- /dev/null +++ b/src/btree.rs @@ -0,0 +1,63 @@ +use crate::node::Node; +use crate::record::Record; + +#[derive(Debug)] +pub struct BTree { + root: Option>, +} + +impl BTree { + pub fn new() -> Self { + BTree { root: None } + } + + pub fn insert(&mut self, rec: Record) { + match &mut self.root { + None => { + let mut root = Box::new(Node::new(true)); + root.keys.push(rec); + self.root = Some(root); + } + Some(root) => { + if root.is_full() { + let mut new_root = Box::new(Node::new(false)); + let old_root = self.root.take().unwrap(); + new_root.children.push(old_root); + new_root.split_child(0); + new_root.insert_non_full(rec); + self.root = Some(new_root); + } else { + root.insert_non_full(rec); + } + } + } + } + + pub fn search(&self, key: i32) -> Option { + match &self.root { + None => None, + Some(root) => root.search(key), + } + } + + pub fn print_tree(&self) { + if let Some(root) = &self.root { + Self::print_node(root, 0); + } else { + println!("Empty tree"); + } + } + + fn print_node(node: &Node, level: usize) { + print!("{}", " ".repeat(level)); + + let key_values: Vec = node.keys.iter().map(|r| r.key).collect(); + println!("Keys: {:?}", key_values); + + if !node.is_leaf { + for child in &node.children { + Self::print_node(child, level + 1); + } + } + } +} diff --git a/src/main.rs b/src/main.rs index 51c0f88..799f8db 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,166 +1,27 @@ -const MIN_DEGREE: usize = 3; // Minimum degree (minimum children per node) +mod btree; +mod node; +mod record; -#[derive(Debug)] -pub struct BTree { - root: Option>>, -} - -#[derive(Debug)] -struct Node { - keys: Vec, - children: Vec>>, - is_leaf: bool, -} - -impl Node { - fn new(is_leaf: bool) -> Self { - Node { - keys: Vec::new(), - children: Vec::new(), - is_leaf, - } - } - - fn is_full(&self) -> bool { - self.keys.len() >= 2 * MIN_DEGREE - 1 - } - - fn search(&self, key: &K) -> bool { - let mut i = 0; - while i < self.keys.len() && key > &self.keys[i] { - i += 1; - } - - if i < self.keys.len() && key == &self.keys[i] { - return true; - } - - if self.is_leaf { - return false; - } - - self.children[i].search(key) - } - - fn insert_non_full(&mut self, key: K) { - let mut i = self.keys.len(); - - if self.is_leaf { - self.keys.push(key); - i = self.keys.len() - 1; - while i > 0 && self.keys[i] < self.keys[i - 1] { - self.keys.swap(i, i - 1); - i -= 1; - } - } else { - while i > 0 && key < self.keys[i - 1] { - i -= 1; - } - - if self.children[i].is_full() { - self.split_child(i); - if key > self.keys[i] { - i += 1; - } - } - self.children[i].insert_non_full(key); - } - } - - fn split_child(&mut self, i: usize) { - let t = MIN_DEGREE; - let full_child = &mut self.children[i]; - let mut new_child = Box::new(Node::new(full_child.is_leaf)); - - // Move the second half of keys to new child - new_child.keys = full_child.keys.split_off(t); - let median = full_child.keys.pop().unwrap(); - - // Move the second half of children if not a leaf - if !full_child.is_leaf { - new_child.children = full_child.children.split_off(t); - } - - // Insert median key and new child into parent - self.keys.insert(i, median); - self.children.insert(i + 1, new_child); - } -} - -impl BTree { - pub fn new() -> Self { - BTree { root: None } - } - - pub fn insert(&mut self, key: K) { - match &mut self.root { - None => { - let mut root = Box::new(Node::new(true)); - root.keys.push(key); - self.root = Some(root); - } - Some(root) => { - if root.is_full() { - let mut new_root = Box::new(Node::new(false)); - let old_root = self.root.take().unwrap(); - new_root.children.push(old_root); - new_root.split_child(0); - new_root.insert_non_full(key); - self.root = Some(new_root); - } else { - root.insert_non_full(key); - } - } - } - } - - pub fn search(&self, key: &K) -> bool { - match &self.root { - None => false, - Some(root) => root.search(key), - } - } - - pub fn print_tree(&self) { - if let Some(root) = &self.root { - Self::print_node(root, 0); - } else { - println!("Empty tree"); - } - } - - fn print_node(node: &Node, level: usize) - where - K: std::fmt::Debug, - { - print!("{}", " ".repeat(level)); - println!("Keys: {:?}", node.keys); - - if !node.is_leaf { - for child in &node.children { - Self::print_node(child, level + 1); - } - } - } -} +use btree::BTree; +use record::Record; fn main() { let mut btree = BTree::new(); - // Insert some values - let values = vec![10, 20, 5, 6, 12, 30, 7, 17, 3, 16, 21, 22, 23, 24]; + let mut values: Vec = vec![]; - println!("Inserting values: {:?}", values); - for val in values { - btree.insert(val); + println!("Inserting values"); + for _ in 0..10 { + let rec = Record::random(); + values.push(rec.key); + btree.insert(rec); } println!("\nB-Tree structure:"); btree.print_tree(); - // Search for some values println!("\nSearch results:"); - println!("Search for 6: {}", btree.search(&6)); - println!("Search for 15: {}", btree.search(&15)); - println!("Search for 21: {}", btree.search(&21)); + println!("Search for 6: {:?}", btree.search(values[0])); + println!("Search for 15: {:?}", btree.search(values[4])); + println!("Search for 21: {:?}", btree.search(100)); } diff --git a/src/node.rs b/src/node.rs new file mode 100644 index 0000000..ff4208e --- /dev/null +++ b/src/node.rs @@ -0,0 +1,85 @@ +use crate::record::Record; + +const MIN_DEGREE: usize = 3; // Minimum degree (minimum children per node) + +#[derive(Debug)] +pub struct Node { + pub keys: Vec, + pub children: Vec>, + pub is_leaf: bool, +} + +impl Node { + pub fn new(is_leaf: bool) -> Self { + Node { + keys: Vec::new(), + children: Vec::new(), + is_leaf, + } + } + + pub fn is_full(&self) -> bool { + self.keys.len() >= 2 * MIN_DEGREE - 1 + } + + pub fn search(&self, key: i32) -> Option { + let mut i = 0; + while i < self.keys.len() && key > self.keys[i].key { + i += 1; + } + + if i < self.keys.len() && key == self.keys[i].key { + return Some(self.keys[i]); + } + + if self.is_leaf { + return None; + } + + self.children[i].search(key) + } + + pub fn insert_non_full(&mut self, rec: Record) { + let mut i = self.keys.len(); + + if self.is_leaf { + self.keys.push(rec); + i = self.keys.len() - 1; + while i > 0 && self.keys[i] < self.keys[i - 1] { + self.keys.swap(i, i - 1); + i -= 1; + } + } else { + while i > 0 && rec < self.keys[i - 1] { + i -= 1; + } + + if self.children[i].is_full() { + self.split_child(i); + if rec > self.keys[i] { + i += 1; + } + } + self.children[i].insert_non_full(rec); + } + } + + pub fn split_child(&mut self, i: usize) { + let t = MIN_DEGREE; + let full_child = &mut self.children[i]; + let mut new_child = Box::new(Node::new(full_child.is_leaf)); + + // Move the second half of keys to new child + new_child.keys = full_child.keys.split_off(t); + let median = full_child.keys.pop().unwrap(); + + // Move the second half of children if not a leaf + if !full_child.is_leaf { + new_child.children = full_child.children.split_off(t); + } + + // Insert median key and new child into parent + self.keys.insert(i, median); + self.children.insert(i + 1, new_child); + } +} diff --git a/src/record.rs b/src/record.rs new file mode 100644 index 0000000..bfb8cc2 --- /dev/null +++ b/src/record.rs @@ -0,0 +1,30 @@ +use bincode::{Decode, Encode}; +use rand::Rng; + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Encode, Decode)] +pub struct Record { + pub key: i32, + pub a: [i32; 5], + pub x: i32, +} + +impl Record { + pub fn new(a: [i32; 5], x: i32) -> Self { + let mut key: i32 = 0; + let mut x_n: i32 = 1; + for i in 0..5 { + // rust is annyoing like that, and panics on overflow in arithmetic operations + key = key.wrapping_add(a[i].wrapping_mul(x_n)); + x_n = x_n.wrapping_mul(x); + } + + Self { key, a, x } + } + + pub fn random() -> Self { + let mut rng = rand::rng(); + let a = rng.random::<[i32; 5]>(); + let x = rng.random::(); + Self::new(a, x) + } +}