dsa_rust/associative/
probing_hash_table.rs

1/*! Safe, open addressing hash table with MAD compression and quadratic probing
2
3# About
4This hash map implementation uses open-addressing to take advantage of cache locality, and relies on a "multiply, add, and divide" (MAD) compression algorithm with quadratic probing to handle collisions.
5
6This structure provides the basis for several other structures in this library, including the [PriorityQueue<K, P>](crate::composite::priority_queue) and [HashSet\<T>](crate::associative::hash_set) implementations.
7
8# Design
9This structure uses Rust's [default hasher](std::hash::DefaultHasher) to hash keys. The default hasher currently represents a cryptographically strong [SipHash-1-3](https://en.wikipedia.org/wiki/SipHash) design as of the publication date (9/2025). In practice, hashing small key objects <= 128 bytes provide similar performance as ULID generation, but hashing arbitrarily large key objects may incur performance penalties.
10
11The primary [HashMap] struct uses a private byte mask (as a "control" list) to track positions with valid ("live"), empty, and removed ("tombstone") entries. Using a conventional free list and overwriting data for a marked slot would break the quadratic probing logic.
12
13```text
14+-------------------------------------------------+
15| ctrl | Option<Entry<K, V>>                      |
16|------+------------------------------------------|
17|    0 | None                                     |
18|    0 | None                                     |
19|    0 | None                                     |
20|    0 | None                                     |
21|    0 | None                                     |
22|    1 | Some(Entry { key: "Dingus", value: 18 }) |
23|    1 | Some(Entry { key: "Brain", value: 37 })  |
24|    0 | None                                     |
25|    0 | None                                     |
26|    0 | None                                     |
27|    0 | None                                     |
28|    0 | None                                     |
29|    0 | None                                     |
30|    0 | None                                     |
31|    1 | Some(Entry { key: "Remus", value: 22 })  |
32|  187 | None                                     |
33|    0 | None                                     |
34|    1 | Some(Entry { key: "Bobson", value: 36 }) |
35|    0 | None                                     |
36|    0 | None                                     |
37|    0 | None                                     |
38|  187 | None                                     |
39+-------------------------------------------------+
40```
41
42## Insertion
43The structure contains two primary ways to insert entries into the map.
44
45The [put()](crate::associative::probing_hash_table::HashMap::put) operation overwrites values for existing keys, but does not mutate the existing key. This keeps with the standard library's implementation where two keys can be `==` without being identical.
46
47```rust
48    use dsa_rust::associative::probing_hash_table::HashMap;
49
50    // Build a map with put()
51    let mut map = HashMap::<&str, u8>::new();
52    let mut names: Vec<&str> = vec!["Peter", "Brain", "Remus", "Bobson", "Dingus", "Dangus"];
53    let values: Vec<u8> = vec![39, 37, 22, 36, 18, 27];
54    for (k, v) in names.iter().zip(values.into_iter()) {
55        map.put(k, v);
56    }
57```
58
59The structure also provides a [mut_val_or()](crate::associative::probing_hash_table::HashMap::mut_val_or) operation for mutating the value(s) of the map or inserting a default value. This is useful with the structures iterators to make linear passes over source data during mapping.
60
61
62```rust
63    use dsa_rust::associative::probing_hash_table::HashMap;
64
65    // Build a map with mut_val_or()
66    let mut map = HashMap::<char, u8>::new();
67    let word: &str = "Hello, sickos!";
68    for k in word.chars() {
69        map.mut_val_or(k, |x| *x += 1, 1);
70    }
71
72```
73
74## Removal & Rehashing
75The structure contains a [remove()](crate::associative::probing_hash_table::HashMap::remove) operation that returns an owned entry if it exists in the map. The `remove()` operation marks the index with a tombstone, and uses [std::mem::take] to remove and return the value. Even though `take()` swaps out a `None` variant the index remains unusable until the calling code invokes a rehash operation. This is done in order to retain probing logic. For long-lived maps with many removals/tombstones, the structure provides two re-hashing operations. The [rehash()](crate::associative::probing_hash_table::HashMap::rehash) operation rehashes the entire map (freeing up tombstones) _at the current underlying structure's capacity_. This essentially allows you to permanently remove deleted entries and reduce the map's load factor. Conversely, the [shrink_to_fit()](crate::associative::probing_hash_table::HashMap::shrink_to_fit) operation rehashes the map (freeing up tombstones) down to the minimum backing capacity that still provides a load factor of _<= .5_. Both processes necessarily require _O(n)_ time where _n_ is the number of live entries in the map.
76
77Example of a map that has had many entries removed:
78
79```text
80+-------------------------------------------------+
81| ctrl | Option<Entry<K, V>>                      |
82|------+------------------------------------------|
83|    1 | Some(Entry { key: 'o', value: 2 })       |
84|  187 | None                                     |
85|    0 | None                                     |
86|  187 | None                                     |
87|    1 | Some(Entry { key: 'l', value: 2 })       |
88|  187 | None                                     |
89|    0 | None                                     |
90|    0 | None                                     |
91|    0 | None                                     |
92|  187 | None                                     |
93|  187 | None                                     |
94|    0 | None                                     |
95|    0 | None                                     |
96|    0 | None                                     |
97|    0 | None                                     |
98|    1 | Some(Entry { key: 'H', value: 1 })       |
99|    0 | None                                     |
100|  187 | None                                     |
101|    0 | None                                     |
102|    0 | None                                     |
103|    1 | Some(Entry { key: 'e', value: 1 })       |
104|  187 | None                                     |
105|    0 | None                                     |
106+-------------------------------------------------+
107```
108
109This example illustrates how the map might look after a `rehash()`:
110
111```text
112+-------------------------------------------------+
113| ctrl | Option<Entry<K, V>>                      |
114|------+------------------------------------------|
115|    1 | Some(Entry { key: 'H', value: 1 })       |
116|    1 | Some(Entry { key: 'o', value: 2 })       |
117|    0 | None                                     |
118|    0 | None                                     |
119|    0 | None                                     |
120|    0 | None                                     |
121|    0 | None                                     |
122|    0 | None                                     |
123|    0 | None                                     |
124|    0 | None                                     |
125|    0 | None                                     |
126|    0 | None                                     |
127|    1 | Some(Entry { key: 'l', value: 2 })       |
128|    0 | None                                     |
129|    0 | None                                     |
130|    0 | None                                     |
131|    0 | None                                     |
132|    0 | None                                     |
133|    0 | None                                     |
134|    0 | None                                     |
135|    1 | Some(Entry { key: 'e', value: 1 })       |
136|    0 | None                                     |
137|    0 | None                                     |
138+-------------------------------------------------+
139```
140
141This example illustrates how the map might look after a `shrink_to_fit()`:
142
143```text
144+-------------------------------------------------+
145| ctrl | Option<Entry<K, V>>                      |
146|------+------------------------------------------|
147|    1 | Some(Entry { key: 'H', value: 1 })       |
148|    0 | None                                     |
149|    0 | None                                     |
150|    1 | Some(Entry { key: 'l', value: 2 })       |
151|    1 | Some(Entry { key: 'o', value: 2 })       |
152|    0 | None                                     |
153|    0 | None                                     |
154|    1 | Some(Entry { key: 'e', value: 1 })       |
155|    0 | None                                     |
156|    0 | None                                     |
157|    0 | None                                     |
158+-------------------------------------------------+
159```
160
161# Example
162
163```rust
164
165    use dsa_rust::associative::probing_hash_table::HashMap;
166
167    //Creates a new hash map with str keys and u8 values
168    let mut map = HashMap::<&str, u8>::new();
169
170    println!("Map stats: size: {}, capacity: {}, active entries: {}",
171        map.occupied(),
172        map.capacity(),
173        map.len()
174    );
175
176    // Puts some entries into the map
177    println!("Building the map...");
178    let mut names: Vec<&str> = vec!["Peter", "Brain", "Remus", "Bobson", "Dingus", "Dangus"];
179    let values: Vec<u8> = vec![39, 37, 22, 36, 18, 27];
180    for (k, v) in names.iter().zip(values.into_iter()) {
181        map.put(k, v);
182    }
183
184    // Checks that the map contains what we'd expect
185    if map.contains("Peter") == false {
186        panic!()
187    };
188    let val = map.get("Peter").unwrap();
189    println!("Peter is {val}");
190
191    // Replaces a value for a given key and
192    // checks that the new value took
193    let new = 41;
194
195    //let old = map.put("Peter", new).unwrap().value;
196    let old = map.get("Peter").unwrap().clone();
197    map.put("Peter", new);
198
199    println!("[Peter's age increased from {old} to {new}]");
200    let val = map.get("Peter").unwrap();
201    println!("Uhhhhh, I meant Peter is {val}");
202
203    // Shows the map and its data
204    println!(
205        "\nMap stats: size: {}, capacity: {}, active entries: {}",
206        map.occupied(),
207        map.capacity(),
208        map.len()
209    );
210    map.contents();
211
212    // Illustrates removing entries
213    println!("\nThere can be only one!");
214    names.remove(0);
215    for e in names {
216        let removed = map.remove(&e);
217        if let Some(entry) = removed {
218            println!("Remove: {}", entry.key());
219        }
220    }
221
222    // The final result
223    println!("\nMap stats: size: {}, capacity: {}, active entries: {}",
224        map.occupied(),
225        map.capacity(),
226        map.len()
227    );
228    map.contents();
229```
230
231*/
232
233use crate::associative::hash_lib;
234//use crate::maps::traits;
235use rand::Rng;
236use std::collections::hash_map::DefaultHasher;
237use std::fmt::Debug;
238use std::hash::{Hash, Hasher};
239
240#[derive(Debug, PartialEq)]
241// Contains the actual key:value pair
242pub struct Entry<K, V> {
243    key: K,
244    value: V,
245}
246impl<K, V> Entry<K, V>
247where
248    K: Debug + Hash + PartialEq,
249    V: Debug,
250{
251    /// Constructs a new Entry
252    fn new(key: K, value: V) -> Entry<K, V> {
253        Entry { key, value }
254    }
255
256    /// Returns the key from an Entry
257    pub fn key(&self) -> &K {
258        &self.key
259    }
260
261    /// Returns the value from an Entry
262    pub fn value(&self) -> &V {
263        &self.value
264    }
265}
266impl<K, V> Default for Entry<K, V>
267where
268    K: Default + Debug + Hash + PartialEq,
269    V: Debug + Default,
270{
271    fn default() -> Self {
272        Self {
273            key: K::default(),
274            value: V::default(),
275        }
276    }
277}
278
279// Byte masks were deemed more performant, but this is valuable historic pattern
280//#[derive(Debug, PartialEq)]
281//pub enum Entry<K, V> {
282//    Live { key: K, value: V },
283//    Tombstone,
284//}
285//impl<K, V> Entry<K, V>
286//where
287//    K: Debug + Hash + PartialEq,
288//    V: PartialEq,
289//{
290//    /// Constructs a new Entry
291//    fn new(key: K, value: V) -> Entry<K, V> {
292//        Entry::Live { key, value }
293//    }
294//
295//    /// Returns the key from an Entry
296//    pub fn key(&self) -> Option<&K> {
297//        match self {
298//            Entry::Live { key, .. } => Some(key),
299//            Entry::Tombstone => None,
300//        }
301//    }
302//
303//    /// Returns the value from an Entry
304//    pub fn value(&self) -> Option<&V> {
305//        match self {
306//            Entry::Live { value, .. } => Some(value),
307//            Entry::Tombstone => None,
308//        }
309//    }
310//
311//    /// Entry adapter that takes a closure to mutate the value of an entry, if it exists.
312//    pub fn mut_val<F>(&mut self, f: F)
313//    where
314//        F: FnOnce(&mut V),
315//    {
316//        if let Entry::Live { value, .. } = self {
317//            f(value);
318//        } else {
319//            // No-op: Entry is a Tombstone and that would break the probing logic.
320//        }
321//    }
322//}
323
324#[derive(Debug)]
325pub struct HashMap<K, V> {
326    data: Vec<Option<Entry<K, V>>>, // The primary memory backing
327    // pub data: Vec<MaybeUninit<Entry<K, V>>>, // Gets rid of Option alignment bloat
328    ctrl: Vec<u8>, // A byte mask to identify available positions
329    size: usize,   // The total number of entries in the map (live + deleted)
330    live: usize,   // The number of "live" entries in the map
331
332    // NOTE: Prime, scale, and shift are used by the MAD compression algorithm.
333    // These randomly-generated values must remain static for the
334    // lifetime of the backing structure; The grow() operation changes these values
335    prime: usize,
336    scale: usize,
337    shift: usize,
338}
339impl<K, V> Default for HashMap<K, V>
340where
341    K: Debug + Default + Eq + Hash + PartialEq,
342    V: Default + PartialEq + std::fmt::Debug,
343{
344    fn default() -> Self {
345        Self::new()
346    }
347}
348impl<K, V> HashMap<K, V>
349where
350    K: Debug + Eq + Hash + PartialEq,
351    V: std::fmt::Debug,
352{
353    /// Constructor for an empty map with a default capacity of 2.
354    pub fn new() -> Self {
355        let new_capacity = 2;
356        let mut table = Self {
357            data: Vec::with_capacity(new_capacity),
358            ctrl: Vec::with_capacity(new_capacity),
359            prime: 13,
360            scale: 5,
361            shift: 7,
362            size: 0,
363            live: 0,
364        };
365        // Initialize storage to ensure access
366        table.data.resize_with(new_capacity, || None);
367        table.ctrl.resize_with(new_capacity, || 0x00);
368        table
369    }
370
371    /// Constructor for an empty map with a given capacity.
372    pub fn new_with_capacity(size: usize) -> Self {
373        let mut table = Self {
374            data: Vec::with_capacity(size),
375            ctrl: Vec::with_capacity(size),
376            prime: 13,
377            scale: 5,
378            shift: 7,
379            size: 0,
380            live: 0,
381        };
382        // Initialize storage to ensure access
383        table.data.resize_with(size, || None);
384        table.ctrl.resize_with(size, || 0x00);
385        table
386    }
387
388    /// Returns the number of active entries in the map in _O(1)_ time.
389    pub fn len(&self) -> usize {
390        self.live
391    }
392
393    /// Returns the total number of entries in the map (active + deleted entries) in _O(n)_ time.
394    pub fn occupied(&self) -> usize {
395        let mut occupied = 0;
396        for e in self.ctrl.iter() {
397            if *e == 1 || *e == 187 {
398                occupied += 1
399            }
400        }
401        occupied
402    }
403
404    /// Returns the total number of _deleted_ entries in the map in _O(n)_ time.
405    pub fn deleted(&self) -> usize {
406        let mut occupied = 0;
407        for e in self.ctrl.iter() {
408            if *e == 187 {
409                occupied += 1
410            }
411        }
412        occupied
413    }
414
415    /// Returns the total number of available slots in the map in _O(1)_ time.
416    ///
417    /// NOTE: The load factor is the quotient of `len() / capacity()`.
418    pub fn capacity(&self) -> usize {
419        self.data.len()
420    }
421
422    /// Returns true if the map is either empty or contains only empty slots and deleted entries.
423    pub fn is_empty(&self) -> bool {
424        self.live == 0 || self.data.is_empty()
425    }
426
427    /// Pretty-prints the map's contents.
428    pub fn contents(&self) {
429        for (e, m) in self.data.iter().zip(self.ctrl.iter()) {
430            println!("  {m:>3}: {e:?}")
431        }
432    }
433
434    /// Takes a key as a reference and returns a Boolean indicating whether its in
435    /// the map. The expected temporal complexity is _O(1)_, as the map
436    /// maintains a [laod factor](https://www.headyimage.com/cs/dsa/maps/#collision-handling-schemes) of <=.5.
437    pub fn contains<Q>(&self, key: &Q) -> bool
438    where
439        K: std::borrow::Borrow<Q>,
440        Q: Debug + Hash + Eq + ?Sized,
441    {
442        self.find_index(key) >= 0
443    }
444
445    /// Returns a reference to the value associated with a key, if the key exists.
446    pub fn get<Q>(&self, key: &Q) -> Option<&V>
447    //pub fn get(&self, key: &K) -> Option<&V> {
448    where
449        K: std::borrow::Borrow<Q>,
450        Q: Debug + Hash + Eq + ?Sized,
451    {
452        // find_index() uses quadratic probing
453        let location = self.find_index(key);
454        if location >= 0 {
455            let value = &self.data[location as usize].as_ref().unwrap().value;
456            Some(value)
457        } else {
458            None
459        }
460    }
461
462    /// Adds entry `(k, v)`, overwriting any value `v` associated with an
463    /// existing key `k`, returns old value. If a new addition increases
464    /// the map's load factor above the designated threshhold of 0.5
465    /// the map resizes.
466    pub fn put(&mut self, key: K, value: V) -> Option<Entry<K, V>>
467//where 
468    //    K: std::default::Default,
469    {
470        // Checks if the addition will bring the load factor above threshold
471        if ((self.size) as f64 + 1.0) / self.data.len() as f64 >= 0.5 {
472            self.grow();
473        }
474
475        let location = self.find_index(&key);
476
477        // Creates a new Entry and inserts it
478        let entry = Entry::new(key, value);
479        let mut old_entry: Option<Entry<K, V>> = None;
480        if location >= 0 {
481            // Replace an entry
482            //println!("COLLISION!!!! {:?}", &entry.key);
483            old_entry = self.data[location as usize].take();
484            self.data[location as usize] = Some(entry);
485        } else {
486            // Add a new entry
487            self.data[-(location + 1) as usize] = Some(entry);
488            self.ctrl[-(location + 1) as usize] = 0x01;
489            self.size += 1;
490            self.live += 1;
491        };
492        old_entry
493    }
494
495    // Attempts to add entry `(k, v)` to the map, if key `k` does not
496    // already exist. If a new addition increases
497    // the map's load factor above the designated threshhold of 0.5
498    // the map resizes.
499    pub fn try_put(&mut self, key: K, value: V) {
500        // Checks if the addition will bring the load factor above threshold
501        if ((self.size) as f64 + 1.0) / self.data.len() as f64 >= 0.5 {
502            self.grow();
503        }
504
505        let location = self.find_index(&key);
506
507        // Avoids duplicates all together
508        if location < 0 {
509            let entry = Entry::new(key, value);
510            self.data[-(location + 1) as usize] = Some(entry);
511            self.ctrl[-(location + 1) as usize] = 0x01;
512            self.size += 1;
513            self.live += 1;
514        }
515    }
516
517    /// Takes a key, a closure, and a default value. If the key is in the map, applies the closure
518    /// to the corresponding entry's value. If the key is not in the map, creates a new entry with
519    /// the provided value.
520    ///
521    /// Example:
522    /// ```rust
523    /// use dsa_rust::associative::probing_hash_table::HashMap;
524    /// let mut count: HashMap<char, u32> = HashMap::new();
525    /// let word: &str = "Hello";
526    /// for k in word.chars() {
527    ///     count.mut_val_or(k, |x| *x += 1, 1);
528    /// }
529    /// ```
530    pub fn mut_val_or<F>(&mut self, key: K, f: F, default: V)
531    where
532        F: FnOnce(&mut V),
533        K: std::default::Default,
534    {
535        // Find the appropriate index
536        let index = self.find_index(&key);
537
538        if index >= 0 {
539            // Found existing key, mutate value
540            if let Some(Entry { value, .. }) = self.data[index as usize].as_mut() {
541                f(value);
542            }
543        } else {
544            // Create new entry
545            self.put(key, default);
546        }
547    }
548
549    /// Removes and returns an entry from the map for a given key, if it exists in the map.
550    pub fn remove<Q>(&mut self, key: &Q) -> Option<Entry<K, V>>
551    where
552        K: std::borrow::Borrow<Q>,
553        Q: Debug + Hash + Eq + ?Sized,
554    {
555        //pub fn remove(&mut self, key: K) -> Option<Entry<K, V>> {
556        let location = self.find_index(key);
557
558        let mut entry = None;
559        if location >= 0 {
560            //entry = self.data[location as usize].replace(Entry::default());
561            entry = self.data[location as usize].take();
562            self.ctrl[location as usize] = 0xBB; // Mark with tombstone
563            self.live -= 1;
564        }
565        entry
566    }
567
568    /// Consumes self and returns a new map of the same size, but without any tombstones.
569    /// Works like a cleanup operation in _O(capacity)_ time because `into_iter` checks all positions.
570    pub fn rehash(self) -> Self {
571        let mut new = Self::new_with_capacity(self.data.len());
572        for (k, v) in self.into_iter() {
573            new.put(k, v);
574        }
575        new
576    }
577
578    /// Rebuilds the map to eliminate accumulated tombstones thereby reducing
579    /// spatial bloat. This operation runs in _O(n)_ time and is intended for
580    /// long-lived maps that have undergone many deletions.
581    ///
582    /// The operation consumes the existing map and returns a new `HashMap`
583    /// with the same live entries.
584    pub fn shrink_to_fit(self) -> Self {
585        let mut new = Self::new();
586        for (k, v) in self.into_iter() {
587            new.put(k, v);
588        }
589        new
590    }
591
592    /// Returns an iterator over borrowed values. The resulting values
593    /// appear in random order.
594    ///
595    /// Example use:
596    /// ```rust
597    /// use dsa_rust::associative::probing_hash_table::HashMap;
598    /// let mut count: HashMap<char, u8> = HashMap::new();
599    /// let mut v = Vec::new();
600    /// for e in count.iter() {
601    ///     v.push(*e.0);
602    /// }
603    /// ```
604    pub fn iter(&self) -> Iter<'_, K, V> {
605        Iter {
606            iter: self.data.iter(),
607        }
608    }
609
610    /// Returns an iterator over borrowed keys only. The keys
611    /// appear in random order.
612    ///
613    /// Example use:
614    /// ```text
615    /// for e in map.keys() {
616    ///     println!("{e}");
617    /// }
618    /// ```
619    pub fn keys(&self) -> Keys<'_, K, V> {
620        Keys {
621            iter: self.data.iter(),
622        }
623    }
624
625    /// Returns an iterator over borrowed values only. The values
626    /// appear in random order.
627    ///
628    /// Example use:
629    /// ```text
630    /// for e in map.values() {
631    ///     println!("{e}");
632    /// }
633    /// ```
634    pub fn values(&self) -> Values<'_, K, V> {
635        Values {
636            iter: self.data.iter(),
637        }
638    }
639
640    // UTILITY FUNCTIONS
641    ////////////////////
642
643    /// Finds the correct insertion location using probing
644    /// Searches the map for key:
645    /// if `>= 0`, overwrite the location and return the old Entry,
646    /// if `< 0`, insert new entry at that location, return None
647    fn find_index<Q>(&self, key: &Q) -> isize
648    where
649        K: std::borrow::Borrow<Q>,
650        Q: Debug + Hash + Eq + ?Sized,
651    {
652        let mut i: usize = 1;
653        let hash = Self::hash(&key);
654        let mut current_index = self.compress(hash);
655
656        // Quadratic probing logic
657        loop {
658            match &self.data[current_index] {
659                Some(val) => {
660                    if val.key.borrow() == key {
661                        return current_index as isize;
662                    }
663                }
664                None => {
665                    if self.ctrl[current_index] != 0xBB {
666                        return -(current_index as isize + 1);
667                    }
668                }
669            }
670            current_index = (current_index + i.pow(2)) % self.data.len();
671            i += 1;
672        }
673        //while let Some(entry) = &self.data[current_index] {
674        //    if entry.key.borrow() == key && self.ctrl[current_index] != 0xBB {
675        //        return current_index as isize;
676        //    } else {
677        //        current_index = (current_index + i.pow(2)) % self.data.len();
678        //        i += 1;
679        //    }
680        //}
681        //-(current_index as isize + 1)
682    }
683
684    /// Takes a reference to a type `T` and uses Rust's default hasher to return a
685    /// 64-bit digest.
686    fn hash<T: Hash + Debug + ?Sized>(key: &T) -> usize {
687        let mut hasher = DefaultHasher::new();
688        key.hash(&mut hasher); // Hash::hash()
689        hasher.finish() as usize // Hasher::finish()
690    }
691
692    // Compresses the hash using the MAD algorithm
693    fn compress(&self, hash: usize) -> usize {
694        (hash.wrapping_mul(self.scale)).wrapping_add(self.shift) % (self.prime) % (self.data.len())
695    }
696
697    /// Grows the base (storage) vector to the next prime larger than
698    /// double the length of the original vector, rehashes and compresses
699    /// hashes for new distribution.
700    fn grow(&mut self) {
701        // Create a new base vector with_capacity() and resize_with()
702        // to ensure all indexes exist, otherwise you could push to an
703        // index that doesn't exist causing a panic.
704        // NOTE: Vec::resize_with() may result in "hidden allocation"
705        // despite description that indicates that the function resizes
706        // "in place", initializes new_base with all None values.
707        let new_capacity = hash_lib::next_prime(self.data.len() * 2);
708        let mut new_base: Vec<Option<Entry<K, V>>> = Vec::with_capacity(new_capacity);
709        new_base.resize_with(new_capacity, || None);
710        let mut new_ctrl: Vec<u8> = Vec::with_capacity(new_capacity);
711        new_ctrl.resize_with(new_capacity, || 0x00);
712
713        //println!("Growing from {} to {}", self.data.len(), new_capacity);
714
715        // Reset the instance values for MAD compression
716        // Finds a prime > len, starting much larger to ensure even spread
717        self.prime = hash_lib::next_prime(new_capacity * 2);
718        let mut rng = rand::rng(); // Thread-local RNG
719        self.scale = rng.random_range(1..=self.prime - 1);
720        self.shift = rng.random_range(0..=self.prime - 1);
721
722        // Move entries from self.data into new_base
723        // For each Some entry in old table, calculate new location
724        // via hash/compression and insert the entry into new_base[location]
725        for e in &mut self.data {
726            if let Some(v) = e.take() {
727                // MAD compression algorithm
728                let mut location: usize = ((hash_lib::hash(&v.key)).wrapping_mul(self.scale))
729                    //let mut location: usize = ((hash_lib::hash(&v.key().unwrap())).wrapping_mul(self.scale))
730                    .wrapping_add(self.shift)
731                    % (self.prime)
732                    % (new_capacity);
733
734                // Handle potential collisions for new vec insert
735                let mut i: usize = 0;
736                while new_base[location].is_some() {
737                    location = (location + i.pow(2)) % new_capacity;
738                    i += 1;
739                }
740                new_base[location] = Some(v);
741                new_ctrl[location] = 0x01;
742            }
743        }
744
745        //Update the struct instance to the new collections
746        self.data = new_base;
747        self.ctrl = new_ctrl;
748    }
749}
750
751// Returns a tuple of borrowed key:value pairs (&K, &V)
752//pub struct Iter<'a, K, V> {
753//    iter: std::slice::Iter<'a, Option<Entry<K, V>>>,
754//}
755//impl<'a, K, V> Iterator for Iter<'a, K, V>
756//    where
757//        K: Debug + Hash + PartialEq,
758//        V: PartialEq,
759//{
760//
761//    type Item = (&'a K, &'a V);
762//
763//    fn next(&mut self) -> Option<Self::Item> {
764//        for opt_entry in self.iter.by_ref() {
765//            if let Some(entry) = opt_entry {
766//                return Some((&entry.key, &entry.value));
767//            }
768//        }
769//        None
770//    }
771//}
772pub struct Iter<'a, K, V> {
773    iter: std::slice::Iter<'a, Option<Entry<K, V>>>,
774}
775
776impl<'a, K, V> Iterator for Iter<'a, K, V>
777where
778    K: Debug + Hash + PartialEq,
779    V: PartialEq,
780{
781    type Item = (&'a K, &'a V);
782
783    fn next(&mut self) -> Option<Self::Item> {
784        self.iter
785            .find_map(|opt_entry| opt_entry.as_ref().map(|entry| (&entry.key, &entry.value)))
786    }
787}
788
789// Returns a tuple of owned key:value pairs (K, V)
790pub struct IntoIter<K, V> {
791    inner: std::iter::Zip<std::vec::IntoIter<Option<Entry<K, V>>>, std::vec::IntoIter<u8>>,
792}
793impl<K, V> Iterator for IntoIter<K, V> {
794    type Item = (K, V);
795
796    fn next(&mut self) -> Option<Self::Item> {
797        for (slot, tag) in self.inner.by_ref() {
798            if tag == 1 {
799                if let Some(entry) = slot {
800                    return Some((entry.key, entry.value));
801                }
802            }
803        }
804        None
805    }
806}
807
808// Allows caller to consume HashMap as an iterator
809impl<K, V> IntoIterator for HashMap<K, V> {
810    type Item = (K, V);
811    type IntoIter = IntoIter<K, V>;
812
813    fn into_iter(self) -> Self::IntoIter {
814        IntoIter {
815            inner: self.data.into_iter().zip(self.ctrl),
816        }
817    }
818}
819
820pub struct Keys<'a, K, V> {
821    iter: std::slice::Iter<'a, Option<Entry<K, V>>>,
822}
823impl<'a, K, V> Iterator for Keys<'a, K, V> {
824    type Item = &'a K;
825
826    fn next(&mut self) -> Option<Self::Item> {
827        if let Some(entry) = self.iter.by_ref().flatten().next() {
828            return Some(&entry.key);
829        }
830        None
831    }
832}
833
834pub struct Values<'a, K, V> {
835    iter: std::slice::Iter<'a, Option<Entry<K, V>>>,
836}
837impl<'a, K, V> Iterator for Values<'a, K, V> {
838    type Item = &'a V;
839
840    fn next(&mut self) -> Option<Self::Item> {
841        if let Some(entry) = self.iter.by_ref().flatten().next() {
842            return Some(&entry.value);
843        }
844        None
845    }
846}
847
848// Unit tests
849/////////////
850
851#[test]
852// Generic type test
853fn probing_hash_table_test() {
854    //Creates a new hash map
855    let mut map = HashMap::<&str, u8>::new();
856
857    assert_eq!(map.data.len(), 2);
858    assert_eq!(map.ctrl.len(), 2);
859
860    // Illustrates that put() and get() work
861    map.put("Peter", 40);
862    assert_eq!(map.size, 1);
863    assert_eq!(map.data.len(), 5);
864    assert_eq!(map.ctrl.len(), 5);
865
866    let fetch = map.get("Peter").unwrap();
867    //assert_eq!(*fetch, 40 as u8);
868    assert_eq!(*fetch, 40);
869
870    // Illustrates that the map grows correctly
871    map.put("Brain", 39); // Grows the map
872    assert_eq!(map.data.len(), 5);
873    map.put("Remus", 22);
874    map.put("Bobson", 36); // Grows the map
875    assert_eq!(map.data.len(), 11);
876    map.put("Dingus", 18);
877    map.put("Dangus", 27); // Grows the map
878    assert_eq!(map.size, 6);
879    assert_eq!(map.data.len(), 23);
880
881    // Illustrates that contains() works as intended
882    assert!(map.contains("Dingus"));
883
884    // Illustrates that put() returns old values and
885    // overwrites existing values upon collision...
886    let collision = map.put("Peter", 41).unwrap();
887    assert_eq!(collision.value, 40_u8);
888    //assert_eq!(*collision.value().unwrap(), 40_u8);
889    let new_val = map.get("Peter").unwrap();
890    //assert_eq!(*new_val, 41 as u8);
891    assert_eq!(*new_val, 41);
892    // Without increasing the list size
893    assert_eq!(map.size, 6);
894
895    // Illustrates that removes entries by key and returns the value
896    assert!(map.contains("Dangus"));
897    let removed = map.remove(&"Dangus").unwrap();
898    assert_eq!(
899        removed,
900        Entry {
901            key: "Dangus",
902            value: 27
903        }
904    );
905    assert!(!map.contains("Dangus"));
906}
907
908#[test]
909// Tests the custom update value function
910fn mut_val_test() {
911    //Creates a new hash map
912    let mut count = HashMap::<char, u8>::new();
913    let phrase: &str = "Hello, sickos";
914
915    // Seeds the map with just the characters and a default value
916    for char in phrase.chars() {
917        count.put(char, 0);
918    }
919
920    eprintln!("\nInitial state:");
921    count.contents();
922
923    // Iterates through the map again, updating each value based on its occurence
924    // NOTE: Can also be used as initial mapping operation with the same code,
925    // but was split here for illustrative purposes
926    for char in phrase.chars() {
927        count.mut_val_or(char, |x| *x += 1, 1);
928    }
929
930    // Pretty-prints the contents of the map
931    eprintln!("\nModified:");
932    count.contents();
933
934    // Uncomment to trigger debug print
935    //assert!(count.is_empty());
936}
937
938#[test]
939// Tests that the structure is iterable
940fn iter_test() {
941    //Creates a new hash map of the frequence letters in the given phrase
942    let mut map = HashMap::<char, u8>::new();
943    let phrase: &str = "Hello, sickos";
944    for char in phrase.chars() {
945        map.mut_val_or(char, |x| *x += 1, 1);
946    }
947
948    eprintln!("\nInitial map state:");
949    map.contents();
950
951    // Iterates through the map, pushing entries
952    // to a Vec as tuples returned from the iterator
953    eprintln!("\nCompact map: ");
954    let mut vec = Vec::new();
955    for e in map.iter() {
956        vec.push(*e.0);
957        eprintln!("{e:?}")
958    }
959
960    eprintln!("\nFinal map state:");
961    map.contents();
962
963    eprintln!("\nFinal vec state:");
964    for e in vec.iter() {
965        eprintln!("{:?}", *e)
966    }
967
968    // Proves that all entries are still valid
969    // Vec contains only keys
970    assert!(vec.contains(&'s'));
971    assert!(vec.contains(&'i'));
972    assert!(vec.contains(&'c'));
973    assert!(vec.contains(&'k'));
974    assert!(vec.contains(&'o'));
975
976    // Map contains whole entries
977    assert!(map.contains(&'s'));
978    assert!(map.contains(&'i'));
979    assert!(map.contains(&'c'));
980    assert!(map.contains(&'k'));
981    assert!(map.contains(&'o'));
982
983    eprintln!("\nPrinting just the keys:");
984    for key in map.keys() {
985        eprintln!("{key}");
986    }
987
988    eprintln!("\nPrinting just the values:");
989    for value in map.values() {
990        eprintln!("{value}");
991    }
992
993    // Consume the map and transfer it to a new map
994    let mut new_map = HashMap::<char, u8>::new();
995    for e in map.into_iter() {
996        new_map.put(e.0, e.1);
997    }
998    // Illegal, map is moved/consumed!
999    //assert!(map.is_empty());
1000
1001    // But new_map contains data now
1002    assert!(new_map.contains(&'s'));
1003    assert!(new_map.contains(&'i'));
1004    assert!(new_map.contains(&'c'));
1005    assert!(new_map.contains(&'k'));
1006    assert!(new_map.contains(&'o'));
1007
1008    // Uncomment to trigger debug print
1009    //assert!(map.is_empty());
1010}
1011
1012#[test]
1013// Tests that into_iter consumes the map,
1014// and that rehash and shrink_to_fit delete removed entries
1015fn rehash_test() {
1016    //Creates a new hash map
1017    let mut map = HashMap::<char, u8>::new();
1018    let word: &str = "Hello, sickos!";
1019    for k in word.chars() {
1020        map.mut_val_or(k, |x| *x += 1, 1);
1021    }
1022
1023    // Prints initial state of the map
1024    // and illustrates that values exist
1025    eprintln!("\nInitial state: ");
1026    map.contents();
1027    assert!(map.contains(&','));
1028    assert!(map.contains(&' '));
1029    assert!(map.contains(&'!'));
1030    assert!(map.contains(&'s'));
1031    assert_eq!(map.len(), 11);
1032    assert_eq!(map.data.len(), 23);
1033
1034    // Removes the values
1035    map.remove(&',');
1036    map.remove(&' ');
1037    map.remove(&'!');
1038    map.remove(&'s');
1039    map.remove(&'i');
1040    map.remove(&'c');
1041    map.remove(&'k');
1042
1043    // Checks that the values are removed
1044    eprintln!("\nModified: ");
1045    map.contents();
1046    assert!(!map.contains(&','));
1047    assert!(!map.contains(&' '));
1048    assert!(!map.contains(&'!'));
1049    assert!(!map.contains(&'s'));
1050    assert_eq!(map.len(), 4);
1051    assert_eq!(map.data.len(), 23);
1052
1053    // Rehashes to get rid of deleted items
1054    // map goes out of scope here
1055    let new_map = map.rehash();
1056
1057    // Illegal operation! into_iter has consumed map!
1058    // assert!(map.contains(&'H'));
1059
1060    // Checks that the values are removed
1061    // but the capacity remains the same as before
1062    eprintln!("\nRehash: ");
1063    new_map.contents();
1064    assert!(!new_map.contains(&','));
1065    assert!(!new_map.contains(&' '));
1066    assert!(!new_map.contains(&'!'));
1067    assert!(!new_map.contains(&'s'));
1068    assert_eq!(new_map.len(), 4);
1069    assert_eq!(new_map.data.len(), 23);
1070
1071    // Repeats the process for shrink_to_fit
1072    //
1073    //Creates a new hash map
1074    let mut map = HashMap::<char, u8>::new();
1075    let word: &str = "Hello, sickos!";
1076    for k in word.chars() {
1077        map.mut_val_or(k, |x| *x += 1, 1);
1078    }
1079
1080    // Prints initial state of the map
1081    // and illustrates that values exist
1082    eprintln!("\nInitial state: ");
1083    map.contents();
1084    assert!(map.contains(&','));
1085    assert!(map.contains(&' '));
1086    assert!(map.contains(&'!'));
1087    assert!(map.contains(&'s'));
1088    assert_eq!(map.len(), 11);
1089    assert_eq!(map.data.len(), 23);
1090
1091    // Removes the values
1092    map.remove(&',');
1093    map.remove(&' ');
1094    map.remove(&'!');
1095    map.remove(&'s');
1096    map.remove(&'i');
1097    map.remove(&'c');
1098    map.remove(&'k');
1099
1100    // Checks that the values are removed
1101    eprintln!("\nModified: ");
1102    map.contents();
1103    assert!(!map.contains(&','));
1104    assert!(!map.contains(&' '));
1105    assert!(!map.contains(&'!'));
1106    assert!(!map.contains(&'s'));
1107    assert_eq!(map.len(), 4);
1108    assert_eq!(map.data.len(), 23);
1109
1110    // Rehashes to get rid of deleted items
1111    // map goes out of scope here
1112    let new_map = map.shrink_to_fit();
1113
1114    // Checks that the values are removed
1115    // but the capacity remains the same as before
1116    eprintln!("\nRehash (shrink to fit): ");
1117    new_map.contents();
1118    assert!(!new_map.contains(&','));
1119    assert!(!new_map.contains(&' '));
1120    assert!(!new_map.contains(&'!'));
1121    assert!(!new_map.contains(&'s'));
1122    assert_eq!(new_map.len(), 4);
1123    assert_eq!(new_map.data.len(), 11);
1124
1125    // Uncomment to trigger debug print
1126    //panic!("MANUAL TEST FAILURE");
1127}
1128
1129// TODO: This belongs in either an external runner or in an integration test module
1130pub fn example() {
1131    //Creates a new hash map
1132    let mut map = HashMap::<&str, u8>::new();
1133
1134    let s = format!(
1135        "Map stats: size: {}, capacity: {}, active entries: {}",
1136        map.size,
1137        map.data.len(),
1138        map.live
1139    );
1140    let l = "=".repeat(s.len());
1141    println!("{l}\n{s}");
1142
1143    // Puts some entries into the map
1144    println!("Building the map...");
1145    let mut names: Vec<&str> = vec!["Peter", "Brain", "Remus", "Bobson", "Dingus", "Dangus"];
1146    let values: Vec<u8> = vec![39, 37, 22, 36, 18, 27];
1147    for (k, v) in names.iter().zip(values.into_iter()) {
1148        map.put(k, v);
1149    }
1150
1151    // Checks that the map contains what we'd expect
1152    if !map.contains("Peter") {
1153        panic!()
1154    };
1155    let val = map.get("Peter").unwrap();
1156    println!("Peter is {val}");
1157
1158    // Replaces a value for a given key and
1159    // checks that the new value took
1160    let new = 41;
1161    let old = map.put("Peter", new).unwrap().value;
1162    println!("[Peter's age increased from {old} to {new}]");
1163    let val = map.get("Peter").unwrap();
1164    println!("Uhhhhh, I meant Peter is {val}");
1165
1166    // Shows the map and its data
1167    println!(
1168        "\nMap stats: size: {}, capacity: {}, active entries: {}",
1169        map.size,
1170        map.data.len(),
1171        map.live
1172    );
1173    for (e, m) in map.data.iter().zip(map.ctrl.iter()) {
1174        println!("\t{m:>3}: {e:?}")
1175    }
1176
1177    // Illustrates removing entries
1178    println!("\nThere can be only one!");
1179    names.remove(0);
1180    for e in names {
1181        println!("Remove: {}", map.remove(&e).unwrap().key);
1182    }
1183
1184    // The final result
1185    let s = format!(
1186        "\nMap stats: size: {}, capacity: {}, active entries: {}",
1187        map.size,
1188        map.data.len(),
1189        map.live
1190    );
1191    println!("{s}");
1192    for (e, m) in map.data.iter().zip(map.ctrl.iter()) {
1193        println!("\t{m:>3}: {e:?}")
1194    }
1195
1196    let mut count: HashMap<char, u8> = HashMap::new();
1197    let word: &str = "The quick brown fox jumps over the lazy dog";
1198    for k in word.chars() {
1199        count.mut_val_or(k, |x| *x += 1, 1);
1200    }
1201    println!(
1202        "\n\nString: {word:?}
1203\nNOTE: There are 28 entries because T != t, and the space literal ' ' is included.
1204\nMap stats: size: {}, capacity: {}, active entries: {}",
1205        count.occupied(), // Total number of elements
1206        count.capacity(),
1207        count.len() // Active entries
1208    );
1209    for (e, m) in count.data.iter().zip(count.ctrl.iter()) {
1210        println!("\t{m:>3}: {e:?}")
1211    }
1212
1213    let l = "=".repeat(s.len());
1214    println!("\n{l}");
1215}