dsa_rust/associative/
hash_lib.rs

1/*! Hashing & compression functions
2
3The utility functions in this module can be used to implement various hashing and compression algorithms
4
5*/
6
7// HOME-ROLLED HASHING
8//////////////////////
9
10/** Illustrates the concept of bit shifting;
11Takes a string a prints the bit and integer value of each character before
12performing a bit shift operation and printing the result as bit and integer values */
13pub fn visual_bit_shift(value: &str) {
14    for mut v in value.bytes() {
15        print!("{v:08b} ({v}) -> ");
16        //v = (v << 5) | (v >> 3);
17        // clippy wants a function
18        v = v.rotate_right(3);
19        println!("{v:08b} ({v})");
20    }
21}
22
23/** Calculates a bit-shifted hash code;
24The function initializes a 32-bit hash code integer to 0,
25then loops over each character in the input string;
26Each loop adds the next character in the string to the hash code
27as an integer value with wrapping; This ensures consistency
28across architectures; The next operation in each loop performs
29a cyclic bit shift on the hash code, and the process repeats */
30pub fn visual_hash_code(key: &str) -> u32 {
31    let mut hash: u32 = 0;
32    for word in key.bytes() {
33        print!("{word:08b} -> ");
34        hash = hash.wrapping_add(word as u32);
35        //hash = (hash << 5) | (hash >> 27);
36        // clippy wants a function
37        hash = hash.rotate_left(5);
38        println!("{hash:032b}");
39    }
40    hash
41}
42#[test]
43fn hash_code_test() {
44    let v = visual_hash_code("Peter");
45    assert_eq!(v, 2794168896);
46
47    //let v = visual_hash_code("This block overflows the value");
48    //assert_eq!(v, 3862340559);
49}
50
51// STANDARD LIBRARY HASHING
52///////////////////////////
53
54// Explores Rust's default hashing functionality
55use std::collections::hash_map::DefaultHasher;
56use std::fmt::Debug;
57use std::hash::{Hash, Hasher};
58
59/// Takes a reference to a type `T` and uses Rust's default hasher to return a
60/// 64-bit digest.
61pub fn hash<T: Hash + Debug + ?Sized>(key: &T) -> usize {
62    let mut hasher = DefaultHasher::new();
63    key.hash(&mut hasher); // Hash::hash()
64    let digest = hasher.finish(); // Hasher::finish()
65    digest as usize
66}
67
68/** Does the same thing as hash() but feeds individual bytes which produces a
69slightly less efficient (and different) digest */
70pub fn hash_1(key: &str) -> u64 {
71    let mut hasher = DefaultHasher::new();
72    for e in key.bytes() {
73        hasher.write_u8(e)
74    }
75    hasher.finish()
76}
77
78// COMPRESSION
79//////////////
80
81/// Super simple division compression as `i mod N`.
82/// Produces a deterministic output, works best when `n` (len) is prime.
83pub fn division_compression(key: usize, len: usize) -> usize {
84    key % len
85}
86
87/// Efficient primality test using the 6k ± 1 rule for numbers >3 by trial.
88fn is_prime(n: usize) -> bool {
89    // Only positive numbers >1 need apply
90    if n < 2 {
91        return false;
92    }
93    // The lil guys get some love
94    if n == 2 || n == 3 {
95        return true;
96    }
97    // Skips evens and numbers divisible by 3
98    if n % 2 == 0 || n % 3 == 0 {
99        return false;
100    }
101    // 6k ± 1 rule check
102    let mut i = 5;
103    while i * i <= n {
104        if n % i == 0 || n % (i + 2) == 0 {
105            return false;
106        }
107        i += 6;
108    }
109    true
110}
111#[test]
112fn prime_test() {
113    // Prime
114    assert!(is_prime(7));
115    assert!(is_prime(23));
116    assert!(is_prime(1103));
117    assert!(is_prime(50411));
118    assert!(is_prime(120569));
119    // Not prime
120    assert!(!is_prime(21));
121    assert!(!is_prime(39));
122    assert!(!is_prime(98));
123    assert!(!is_prime(1208));
124    assert!(!is_prime(445));
125}
126
127/** Finds the next prime by brute force in O(n) time */
128//fn next_prime(n: u64) -> u64 {
129//    let mut candidate = n + 1;
130//    while !is_prime(candidate) {e
131//        candidate += 1;
132//    }
133//    candidate
134//}
135/** Finds the next prime in O(n/2) time by skipping evens */
136pub fn next_prime(n: usize) -> usize {
137    if n < 2 {
138        return 2;
139    }
140    if n == 2 {
141        return 3;
142    }
143    let mut candidate = if n % 2 == 0 { n + 1 } else { n + 2 }; // Ensure candidate is odd
144    while !is_prime(candidate) {
145        candidate += 2; // Skip even numbers
146    }
147    candidate
148}
149#[test]
150fn next_prime_test() {
151    assert_eq!(next_prime(3), 5);
152    assert_eq!(next_prime(19), 23);
153    assert_eq!(next_prime(8868), 8887);
154    assert_eq!(next_prime(117760), 117763);
155    assert_eq!(next_prime(1899498), 1899503);
156}
157
158use rand::Rng;
159
160/** Implements MAD compression as `[(ai + b) mod p] mod N`
161Relies on `is_prime` and `next_prime` functions */
162// `c(h(k)) = ((a * h(k) + b) mod p) mod N`
163pub fn mad_compression(key: usize, len: usize) -> usize {
164    // Finds a prime >len, starting much larger to ensure even spread
165    let p = next_prime(len.pow(3));
166
167    let mut rng = rand::rng(); // Thread-local RNG
168    let a = rng.random_range(1..=p - 1);
169    let b = rng.random_range(0..=p - 1);
170
171    // Raw-dogging the algorithm may cause overflow
172    //(((key * a) + b) % p) % len
173
174    // Apply wrapping * and + to prevent overflow
175    (key.wrapping_mul(a))
176        .wrapping_add(b)
177        .wrapping_rem(p)
178        .wrapping_rem(len)
179}
180pub fn mad_compression_1(
181    hash: usize,
182    prime: usize,
183    scale: usize,
184    shift: usize,
185    capacity: usize,
186) -> usize {
187    (hash.wrapping_mul(scale)).wrapping_add(shift) % (prime) % (capacity)
188}