From 371e14b3d5b809333f3ca2ea491261785deb66a9 Mon Sep 17 00:00:00 2001 From: rodrigo ramos xochiteotzin Date: Tue, 11 Feb 2025 20:27:25 -0600 Subject: [PATCH 1/7] feat: gh-12 scaffolding hash table --- README.md | 5 +++-- src/hash_table/README.md | 0 src/hash_table/mod.rs | 28 ++++++++++++++++++++++++++++ src/lib.rs | 5 +++++ 4 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 src/hash_table/README.md create mode 100644 src/hash_table/mod.rs diff --git a/README.md b/README.md index 1767730..120aad0 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,9 @@ This is a library of data structures implemented in Rust. The goal of this project is to provide a collection of data structures that are not available in the Rust standard library. The library is designed to be easy to use and efficient. This crate still in early stage of development, but the intended objective is to provide the following data structures: -* [Linked List](src/linked_list/README.md) -* [Double Linked List](src/double_linked_list/README.md) +* [Linked List](src/linked_lists/README.md) +* [Double Linked List](src/linked_lists/README.md) +* [Hash Table](src/hash_table/README.md) * Stack * Queue * Tree diff --git a/src/hash_table/README.md b/src/hash_table/README.md new file mode 100644 index 0000000..e69de29 diff --git a/src/hash_table/mod.rs b/src/hash_table/mod.rs new file mode 100644 index 0000000..2040ddf --- /dev/null +++ b/src/hash_table/mod.rs @@ -0,0 +1,28 @@ +use std::fmt::Debug; + +type KeyPointer = Option; + +#[derive(Debug)] +pub struct Table { + elements: Vec>, +} + +impl Table { + pub fn new() -> Self { + Self { + elements: Vec::new(), + } + } +} + +// region: --- Tests +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_hash_table_ops() { + // let table = Table::new(); + } +} +// endregion: --- Tests diff --git a/src/lib.rs b/src/lib.rs index 1321090..a648400 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,4 +2,9 @@ //! //! `rust_ds` is a collection of data structures utilities to use in Rust. +use hash_table::HashTable; +use linked_lists::Double; +use linked_lists::Singly; + +pub mod hash_table; pub mod linked_lists; From f734fbcac52b6b910e953a634db25a08adaa430d Mon Sep 17 00:00:00 2001 From: rodrigo ramos xochiteotzin Date: Thu, 13 Feb 2025 19:38:04 -0600 Subject: [PATCH 2/7] feat: gh-12 main README updated, tags and refs added --- Cargo.toml | 3 ++- README.md | 18 +++++++++++++++++- src/lib.rs | 3 ++- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 53c2e14..475ce99 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,4 +5,5 @@ edition = "2021" authors = ["raa "] description = "Custom data structures crate for Rust" license = "MIT" -repository = "https://github.com/raa-dev/rust-ds" \ No newline at end of file +repository = "https://github.com/raa-dev/rust-ds" +categories = ["data structures", "algorithms"] \ No newline at end of file diff --git a/README.md b/README.md index 120aad0..92b507d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,8 @@ # Data Structures Crate +[github](https://github.com/raa-dev/rust-ds) +[crates.io](https://crates.io/crates/rust-ds) +[docs.rs](https://docs.rs/rust-ds) + This is a library of data structures implemented in Rust. The goal of this project is to provide a collection of data structures that are not available in the Rust standard library. The library is designed to be easy to use and efficient. This crate still in early stage of development, but the intended objective is to provide the following data structures: @@ -9,4 +13,16 @@ This crate still in early stage of development, but the intended objective is to * Queue * Tree * Graph -* Heap \ No newline at end of file +* Heap + +## Usage +Add this dependency to your `Cargo.toml` +```toml +[dependencies] +rust-ds = "0.1.1" +``` + +## License + +Licensed under +- MIT license ([LICENSE-MIT](https://github.com/Amanieu/parking_lot/blob/HEAD/LICENSE-MIT) or https://opensource.org/licenses/MIT) \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index a648400..98c1407 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,7 +2,8 @@ //! //! `rust_ds` is a collection of data structures utilities to use in Rust. -use hash_table::HashTable; +#[warn(unused_imports)] +use hash_table::Table; use linked_lists::Double; use linked_lists::Singly; From 21dd2500a1ee20e2a94dd50e5dae1ab4a80b12a7 Mon Sep 17 00:00:00 2001 From: rodrigo ramos xochiteotzin Date: Thu, 13 Feb 2025 19:38:32 -0600 Subject: [PATCH 3/7] feat: gh-12 default method added and scaffolding ds README --- src/hash_table/README.md | 3 +++ src/hash_table/mod.rs | 8 +++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/hash_table/README.md b/src/hash_table/README.md index e69de29..83c299a 100644 --- a/src/hash_table/README.md +++ b/src/hash_table/README.md @@ -0,0 +1,3 @@ +# Hash Table + +## Operations \ No newline at end of file diff --git a/src/hash_table/mod.rs b/src/hash_table/mod.rs index 2040ddf..7e406e2 100644 --- a/src/hash_table/mod.rs +++ b/src/hash_table/mod.rs @@ -4,11 +4,17 @@ type KeyPointer = Option; #[derive(Debug)] pub struct Table { - elements: Vec>, + pub elements: Vec>, } impl Table { pub fn new() -> Self { + Self::default() + } +} + +impl Default for Table { + fn default() -> Self { Self { elements: Vec::new(), } From f2ec1b88b2f3f0e01b2b19a0ff00b4b482b7d125 Mon Sep 17 00:00:00 2001 From: rodrigo ramos xochiteotzin Date: Thu, 13 Feb 2025 20:36:50 -0600 Subject: [PATCH 4/7] feat: gh-12 common ops implemented, error mod added --- src/hash_table/README.md | 14 +++++- src/hash_table/errors.rs | 24 +++++++++++ src/hash_table/mod.rs | 93 +++++++++++++++++++++++++++++++++++----- 3 files changed, 119 insertions(+), 12 deletions(-) create mode 100644 src/hash_table/errors.rs diff --git a/src/hash_table/README.md b/src/hash_table/README.md index 83c299a..e75446e 100644 --- a/src/hash_table/README.md +++ b/src/hash_table/README.md @@ -1,3 +1,15 @@ # Hash Table -## Operations \ No newline at end of file +A Hash Table orders the data as pairs of keys and values. This enables +efficient data retrieval based on the key. Each key is hashed to produce +an index at which the corresponding value is stored. This allows for +average-case constant time complexity, O(1), for both insertion and +lookup operations. Hash tables are widely used in various applications +such as database indexing, caches, and sets. + +## Operations +- **Insert**: Add a key-value pair to the hash table. +- **Remove**: Remove a key-value pair from the hash table. +- **Get**: Retrieve the value associated with a given key. +- **Update**: Modify the value associated with a given key. +- **Resize**: Adjust the size of the hash table to maintain efficient operations. \ No newline at end of file diff --git a/src/hash_table/errors.rs b/src/hash_table/errors.rs new file mode 100644 index 0000000..f4feff1 --- /dev/null +++ b/src/hash_table/errors.rs @@ -0,0 +1,24 @@ +use std::fmt::{Debug, Display, Formatter, Result as FmtResult}; + +pub type Result = core::result::Result; + +#[derive(Debug)] +pub enum Error { + EmptyTable, + ValueNotFound, + IndexOutOfBounds, +} + +impl Display for Error { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + match self { + Error::EmptyTable => write!(f, "Operation failed: Table is empty"), + Error::ValueNotFound => { + write!(f, "Operation failed: Value not found in table") + } + Error::IndexOutOfBounds => { + write!(f, "Index is out of bounds") + } + } + } +} diff --git a/src/hash_table/mod.rs b/src/hash_table/mod.rs index 7e406e2..8f3f8fa 100644 --- a/src/hash_table/mod.rs +++ b/src/hash_table/mod.rs @@ -1,23 +1,85 @@ +mod errors; + +pub(super) use errors::{Error, Result}; +use std::collections::hash_map::DefaultHasher; use std::fmt::Debug; +use std::hash::{Hash, Hasher}; -type KeyPointer = Option; +type KeyPointer = Option<(K, V)>; #[derive(Debug)] -pub struct Table { - pub elements: Vec>, +pub struct Table +where + K: Clone, + V: Clone, +{ + pub elements: Vec>, + capacity: usize, } -impl Table { - pub fn new() -> Self { - Self::default() +impl Table +where + K: Hash + Eq + Debug + Clone, + V: Debug + Clone, +{ + pub fn new(capacity: usize) -> Self { + Self { + elements: vec![None; capacity], + capacity, + } + } + + fn hash(&self, key: &Q) -> usize + where + K: std::borrow::Borrow, + Q: Hash + ?Sized, + { + let mut hasher = DefaultHasher::new(); + key.hash(&mut hasher); + (hasher.finish() as usize) % self.capacity + } + + pub fn insert(&mut self, key: K, value: V) { + let index = self.hash(&key); + self.elements[index] = Some((key, value)); + } + + pub fn get(&self, key: &K) -> Option<&V> { + let index = self.hash(key); + self.elements[index].as_ref().map(|(_, v)| v) + } + + pub fn remove(&mut self, key: &K) -> Option { + let index = self.hash(key); + if let Some((_, value)) = self.elements[index].take() { + Some(value) + } else { + None + } + } + + pub fn update(&mut self, key: &K) -> Option<&mut V> { + let index = self.hash(key); + self.elements[index].as_mut().map(|(_, v)| v) + } + + pub fn resize(&mut self, new_capacity: usize) { + let mut new_table = Table::new(new_capacity); + for element in self.elements.iter().filter_map(|e| e.as_ref()) { + new_table.insert(element.0.clone(), element.1.clone()); + } + self.elements = new_table.elements; + self.capacity = new_capacity; } } -impl Default for Table { +impl Default for Table +where + K: Hash + Eq + Debug + Clone, + V: Debug + Clone, +{ fn default() -> Self { - Self { - elements: Vec::new(), - } + Self::new(64) } } @@ -28,7 +90,16 @@ mod tests { #[test] fn test_hash_table_ops() { - // let table = Table::new(); + let mut table = Table::new(16); + table.insert("key1", "value1"); + assert_eq!(table.get(&"key1"), Some(&"value1")); + table.remove(&"key1"); + assert_eq!(table.get(&"key1"), None); + table.insert("key2", "value2"); + if let Some(v) = table.update(&"key2") { + *v = "value3"; + } + assert_eq!(table.get(&"key2"), Some(&"value3")); } } // endregion: --- Tests From d62fbc645ab78e3b993ab6386834aa456004a49c Mon Sep 17 00:00:00 2001 From: rodrigo ramos xochiteotzin Date: Thu, 13 Feb 2025 23:21:37 -0600 Subject: [PATCH 5/7] feat: gh-12 hash table and error struct updated --- src/hash_table/README.md | 3 +-- src/hash_table/errors.rs | 12 +++++----- src/hash_table/mod.rs | 52 ++++++++++++++++++++++++++++++---------- 3 files changed, 46 insertions(+), 21 deletions(-) diff --git a/src/hash_table/README.md b/src/hash_table/README.md index e75446e..fa8abed 100644 --- a/src/hash_table/README.md +++ b/src/hash_table/README.md @@ -4,8 +4,7 @@ A Hash Table orders the data as pairs of keys and values. This enables efficient data retrieval based on the key. Each key is hashed to produce an index at which the corresponding value is stored. This allows for average-case constant time complexity, O(1), for both insertion and -lookup operations. Hash tables are widely used in various applications -such as database indexing, caches, and sets. +lookup operations. ## Operations - **Insert**: Add a key-value pair to the hash table. diff --git a/src/hash_table/errors.rs b/src/hash_table/errors.rs index f4feff1..7750213 100644 --- a/src/hash_table/errors.rs +++ b/src/hash_table/errors.rs @@ -5,19 +5,19 @@ pub type Result = core::result::Result; #[derive(Debug)] pub enum Error { EmptyTable, - ValueNotFound, - IndexOutOfBounds, + KeyNotFound, + InvalidCapacity, } impl Display for Error { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { match self { Error::EmptyTable => write!(f, "Operation failed: Table is empty"), - Error::ValueNotFound => { - write!(f, "Operation failed: Value not found in table") + Error::KeyNotFound => { + write!(f, "Operation failed: Key not found in table") } - Error::IndexOutOfBounds => { - write!(f, "Index is out of bounds") + Error::InvalidCapacity => { + write!(f, "Operation failed: Invalid capacity") } } } diff --git a/src/hash_table/mod.rs b/src/hash_table/mod.rs index 8f3f8fa..fc360a6 100644 --- a/src/hash_table/mod.rs +++ b/src/hash_table/mod.rs @@ -44,32 +44,49 @@ where self.elements[index] = Some((key, value)); } - pub fn get(&self, key: &K) -> Option<&V> { + pub fn get(&self, key: &K) -> Result<&V> { let index = self.hash(key); - self.elements[index].as_ref().map(|(_, v)| v) + self.elements[index] + .as_ref() + .map(|(_, v)| v) + .ok_or(Error::KeyNotFound) } - pub fn remove(&mut self, key: &K) -> Option { + pub fn remove(&mut self, key: &K) -> Result { + if self.capacity == 0 { + return Err(Error::EmptyTable); + } let index = self.hash(key); if let Some((_, value)) = self.elements[index].take() { - Some(value) + Ok(value) } else { - None + Err(Error::KeyNotFound) } } - pub fn update(&mut self, key: &K) -> Option<&mut V> { + pub fn update(&mut self, key: &K) -> Result<&mut V> { + if self.capacity == 0 { + return Err(Error::EmptyTable); + } let index = self.hash(key); - self.elements[index].as_mut().map(|(_, v)| v) + self.elements[index] + .as_mut() + .map(|(_, v)| v) + .ok_or(Error::KeyNotFound) } - pub fn resize(&mut self, new_capacity: usize) { + pub fn resize(&mut self, new_capacity: usize) -> Result<()> { + if new_capacity == 0 { + return Err(Error::InvalidCapacity); + } + let mut new_table = Table::new(new_capacity); for element in self.elements.iter().filter_map(|e| e.as_ref()) { new_table.insert(element.0.clone(), element.1.clone()); } self.elements = new_table.elements; self.capacity = new_capacity; + Ok(()) } } @@ -92,14 +109,23 @@ mod tests { fn test_hash_table_ops() { let mut table = Table::new(16); table.insert("key1", "value1"); - assert_eq!(table.get(&"key1"), Some(&"value1")); - table.remove(&"key1"); - assert_eq!(table.get(&"key1"), None); + assert_eq!(table.get(&"key1").unwrap(), &"value1"); + table.remove(&"key1").unwrap(); + assert!(table.get(&"key1").is_err()); table.insert("key2", "value2"); - if let Some(v) = table.update(&"key2") { + if let Ok(v) = table.update(&"key2") { *v = "value3"; } - assert_eq!(table.get(&"key2"), Some(&"value3")); + assert_eq!(table.get(&"key2").unwrap(), &"value3"); + } + + #[test] + fn test_hash_table_errors() { + let mut table: Table<&str, &str> = Table::new(16); + assert!(table.get(&"key1").is_err()); + assert!(table.remove(&"key1").is_err()); + assert!(table.update(&"key1").is_err()); + assert!(table.resize(0).is_err()); } } // endregion: --- Tests From b79931d2a800374dc04a1d8d36667d7441a7c10a Mon Sep 17 00:00:00 2001 From: rodrigo ramos xochiteotzin Date: Thu, 13 Feb 2025 23:33:22 -0600 Subject: [PATCH 6/7] feat: gh-12 docs updatged --- src/hash_table/README.md | 30 +++++++++++++++++++++++++++++- src/hash_table/mod.rs | 16 +++++++++------- src/lib.rs | 5 ----- 3 files changed, 38 insertions(+), 13 deletions(-) diff --git a/src/hash_table/README.md b/src/hash_table/README.md index fa8abed..57df322 100644 --- a/src/hash_table/README.md +++ b/src/hash_table/README.md @@ -11,4 +11,32 @@ lookup operations. - **Remove**: Remove a key-value pair from the hash table. - **Get**: Retrieve the value associated with a given key. - **Update**: Modify the value associated with a given key. -- **Resize**: Adjust the size of the hash table to maintain efficient operations. \ No newline at end of file +- **Resize**: Adjust the size of the hash table to maintain efficient operations. + +## Usage + +```rust +use rust_ds::hash_table::Table; + +fn main() { + let mut table = Table::new(4); // You can use default instead to use a 64 length capacity + + // Insert key-value pairs + table.insert("key1", "value1"); + table.insert("key2", "value2"); + + // Retrieve a value + if let Some(value) = table.get("key1") { + println!("The value for 'key1' is {}", value); + } + + // Update a value + table.update("key1", "new_value1"); + + // Remove a key-value pair + table.remove("key2"); + + // Resize the hash table + table.resize(32); +} +``` \ No newline at end of file diff --git a/src/hash_table/mod.rs b/src/hash_table/mod.rs index fc360a6..4be7709 100644 --- a/src/hash_table/mod.rs +++ b/src/hash_table/mod.rs @@ -7,6 +7,7 @@ use std::hash::{Hash, Hasher}; type KeyPointer = Option<(K, V)>; +/// `Table` is a simple hash table implementation. #[derive(Debug)] pub struct Table where @@ -22,13 +23,14 @@ where K: Hash + Eq + Debug + Clone, V: Debug + Clone, { + /// Create a new `Table` with the given capacity. pub fn new(capacity: usize) -> Self { Self { elements: vec![None; capacity], capacity, } } - + /// Hash the key and return the index. fn hash(&self, key: &Q) -> usize where K: std::borrow::Borrow, @@ -38,12 +40,12 @@ where key.hash(&mut hasher); (hasher.finish() as usize) % self.capacity } - + /// Insert a new key-value pair into the table. pub fn insert(&mut self, key: K, value: V) { let index = self.hash(&key); self.elements[index] = Some((key, value)); } - + /// Get the value for the given key. pub fn get(&self, key: &K) -> Result<&V> { let index = self.hash(key); self.elements[index] @@ -51,7 +53,7 @@ where .map(|(_, v)| v) .ok_or(Error::KeyNotFound) } - + /// Remove the key-value pair from the table. pub fn remove(&mut self, key: &K) -> Result { if self.capacity == 0 { return Err(Error::EmptyTable); @@ -63,7 +65,7 @@ where Err(Error::KeyNotFound) } } - + /// Update the value for the given key. pub fn update(&mut self, key: &K) -> Result<&mut V> { if self.capacity == 0 { return Err(Error::EmptyTable); @@ -74,7 +76,7 @@ where .map(|(_, v)| v) .ok_or(Error::KeyNotFound) } - + /// Resize the table to the new capacity. pub fn resize(&mut self, new_capacity: usize) -> Result<()> { if new_capacity == 0 { return Err(Error::InvalidCapacity); @@ -89,7 +91,7 @@ where Ok(()) } } - +/// Default implementation for `Table`. impl Default for Table where K: Hash + Eq + Debug + Clone, diff --git a/src/lib.rs b/src/lib.rs index 98c1407..6d2b937 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,10 +2,5 @@ //! //! `rust_ds` is a collection of data structures utilities to use in Rust. -#[warn(unused_imports)] -use hash_table::Table; -use linked_lists::Double; -use linked_lists::Singly; - pub mod hash_table; pub mod linked_lists; From 7fa65a7eae5466920ef9b2dcdeee61e92a6203b2 Mon Sep 17 00:00:00 2001 From: rodrigo ramos xochiteotzin Date: Thu, 13 Feb 2025 23:37:04 -0600 Subject: [PATCH 7/7] feat: gh-12 docs updated --- src/linked_lists/README.md | 59 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/src/linked_lists/README.md b/src/linked_lists/README.md index 0cbcb2e..ea72a31 100644 --- a/src/linked_lists/README.md +++ b/src/linked_lists/README.md @@ -9,3 +9,62 @@ The following operations are defined for a linked list: - **Search**: Searches for an element in the list. - **Update**: Updates an element in the list. - **Get**: Returns the element at a given index. + +## Usage +### Singly Linked List Example + +```rust +use linked_lists::Singly; + +fn main() { + let mut list = Singly::new(); + list.append(1); + list.append(2); + list.append(3); + println!("List after appending elements: {:?}", list); + + list.pop(); + println!("List after popping an element: {:?}", list); + + list.remove(1); + println!("List after removing an element: {:?}", list); + + let found = list.search(2); + println!("Element 2 found: {}", found); + + list.update(0, 4); + println!("List after updating an element: {:?}", list); + + let element = list.get(0); + println!("Element at index 0: {:?}", element); +} +``` + +### Doubly Linked List Example + +```rust +use linked_lists::Double; + +fn main() { + let mut list = Double::new(); + list.append(1); + list.append(2); + list.append(3); + println!("List after appending elements: {:?}", list); + + list.pop(); + println!("List after popping an element: {:?}", list); + + list.remove(1); + println!("List after removing an element: {:?}", list); + + let found = list.search(2); + println!("Element 2 found: {}", found); + + list.update(0, 4); + println!("List after updating an element: {:?}", list); + + let element = list.get(0); + println!("Element at index 0: {:?}", element); +} +```