#![allow(clippy::module_name_repetitions)]
#![allow(clippy::cast_possible_truncation)]
pub trait HashStrategy: Send + Sync {
fn generate_indices(&self, h1: u64, h2: u64, h3: u64, k: usize, m: usize) -> Vec<usize>;
fn required_hashes(&self) -> usize;
fn name(&self) -> &'static str;
}
#[derive(Debug, Clone, Copy, Default)]
pub struct DoubleHashing;
impl HashStrategy for DoubleHashing {
#[inline]
fn generate_indices(&self, h1: u64, h2: u64, _h3: u64, k: usize, m: usize) -> Vec<usize> {
let m_u64 = m as u64;
let mut indices = Vec::with_capacity(k);
for i in 0..k {
let i_u64 = i as u64;
let hash = h1.wrapping_add(i_u64.wrapping_mul(h2));
indices.push((hash % m_u64) as usize);
}
indices
}
#[inline]
fn required_hashes(&self) -> usize {
2
}
#[inline]
fn name(&self) -> &'static str {
"DoubleHashing"
}
}
#[derive(Debug, Clone, Copy, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct EnhancedDoubleHashing;
impl HashStrategy for EnhancedDoubleHashing {
#[inline]
fn generate_indices(&self, h1: u64, h2: u64, _h3: u64, k: usize, m: usize) -> Vec<usize> {
let m_u64 = m as u64;
let mut indices = Vec::with_capacity(k);
for i in 0..k {
let i_u64 = i as u64;
let quadratic_term = (i_u64.wrapping_mul(i_u64.wrapping_add(1))) >> 1;
let hash = h1
.wrapping_add(i_u64.wrapping_mul(h2))
.wrapping_add(quadratic_term);
indices.push((hash % m_u64) as usize);
}
indices
}
#[inline]
fn required_hashes(&self) -> usize {
2
}
#[inline]
fn name(&self) -> &'static str {
"EnhancedDoubleHashing"
}
}
#[derive(Debug, Clone, Copy)]
pub struct TripleHashing;
impl HashStrategy for TripleHashing {
#[inline]
fn generate_indices(&self, h1: u64, h2: u64, h3: u64, k: usize, m: usize) -> Vec<usize> {
let m_u64 = m as u64;
let mut indices = Vec::with_capacity(k);
for i in 0..k {
let i_u64 = i as u64;
let i_squared = i_u64.wrapping_mul(i_u64);
let hash = h1
.wrapping_add(i_u64.wrapping_mul(h2))
.wrapping_add(i_squared.wrapping_mul(h3));
indices.push((hash % m_u64) as usize);
}
indices
}
#[inline]
fn required_hashes(&self) -> usize {
3
}
#[inline]
fn name(&self) -> &'static str {
"TripleHashing"
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum HashStrategyKind {
Double,
#[default]
EnhancedDouble,
Triple,
}
impl HashStrategyKind {
#[must_use]
pub fn name(&self) -> &'static str {
match self {
Self::Double => "DoubleHashing",
Self::EnhancedDouble => "EnhancedDoubleHashing",
Self::Triple => "TripleHashing",
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_double_hashing_basic() {
let strategy = DoubleHashing;
let indices = strategy.generate_indices(12345, 67890, 0, 7, 1000);
assert_eq!(indices.len(), 7);
assert!(indices.iter().all(|&idx| idx < 1000));
}
#[test]
fn test_enhanced_double_hashing_basic() {
let strategy = EnhancedDoubleHashing;
let indices = strategy.generate_indices(12345, 67890, 0, 7, 1000);
assert_eq!(indices.len(), 7);
assert!(indices.iter().all(|&idx| idx < 1000));
}
#[test]
fn test_triple_hashing_basic() {
let strategy = TripleHashing;
let indices = strategy.generate_indices(12345, 67890, 11111, 7, 1000);
assert_eq!(indices.len(), 7);
assert!(indices.iter().all(|&idx| idx < 1000));
}
#[test]
fn test_required_hashes() {
assert_eq!(DoubleHashing.required_hashes(), 2);
assert_eq!(EnhancedDoubleHashing.required_hashes(), 2);
assert_eq!(TripleHashing.required_hashes(), 3);
}
#[test]
fn test_strategy_names() {
assert_eq!(DoubleHashing.name(), "DoubleHashing");
assert_eq!(EnhancedDoubleHashing.name(), "EnhancedDoubleHashing");
assert_eq!(TripleHashing.name(), "TripleHashing");
}
#[test]
fn test_double_hashing_deterministic() {
let strategy = DoubleHashing;
let indices1 = strategy.generate_indices(12345, 67890, 0, 10, 1000);
let indices2 = strategy.generate_indices(12345, 67890, 0, 10, 1000);
assert_eq!(indices1, indices2);
}
#[test]
fn test_enhanced_double_hashing_deterministic() {
let strategy = EnhancedDoubleHashing;
let indices1 = strategy.generate_indices(12345, 67890, 0, 10, 1000);
let indices2 = strategy.generate_indices(12345, 67890, 0, 10, 1000);
assert_eq!(indices1, indices2);
}
#[test]
fn test_triple_hashing_deterministic() {
let strategy = TripleHashing;
let indices1 = strategy.generate_indices(12345, 67890, 11111, 10, 1000);
let indices2 = strategy.generate_indices(12345, 67890, 11111, 10, 1000);
assert_eq!(indices1, indices2);
}
#[test]
fn test_strategies_produce_different_sequences() {
let h1 = 0x123456789abcdef0;
let h2 = 0xfedcba9876543210;
let h3 = 0x1111111111111111;
let k = 20;
let m = 10000;
let double_indices = DoubleHashing.generate_indices(h1, h2, 0, k, m);
let enhanced_indices = EnhancedDoubleHashing.generate_indices(h1, h2, 0, k, m);
let triple_indices = TripleHashing.generate_indices(h1, h2, h3, k, m);
assert_ne!(
double_indices, enhanced_indices,
"Double and Enhanced should differ"
);
assert_ne!(
enhanced_indices, triple_indices,
"Enhanced and Triple should differ"
);
assert_ne!(
double_indices, triple_indices,
"Double and Triple should differ"
);
}
#[test]
fn test_enhanced_differs_from_double_for_large_k() {
let h1 = 12345;
let h2 = 67890;
let m = 1000;
let double_indices = DoubleHashing.generate_indices(h1, h2, 0, 15, m);
let enhanced_indices = EnhancedDoubleHashing.generate_indices(h1, h2, 0, 15, m);
let differences = double_indices
.iter()
.zip(enhanced_indices.iter())
.filter(|(a, b)| a != b)
.count();
assert!(
differences >= 12,
"Enhanced should differ significantly from double for large k. Only {} of 15 differ",
differences
);
}
#[test]
fn test_single_hash_function() {
let strategy = DoubleHashing;
let indices = strategy.generate_indices(12345, 67890, 0, 1, 1000);
assert_eq!(indices.len(), 1);
assert!(indices[0] < 1000);
}
#[test]
fn test_large_k() {
let strategy = EnhancedDoubleHashing;
let indices = strategy.generate_indices(12345, 67890, 0, 100, 10000);
assert_eq!(indices.len(), 100);
assert!(indices.iter().all(|&idx| idx < 10000));
}
#[test]
fn test_small_filter_size() {
let strategy = DoubleHashing;
let indices = strategy.generate_indices(12345, 67890, 0, 10, 10);
assert_eq!(indices.len(), 10);
assert!(indices.iter().all(|&idx| idx < 10));
}
#[test]
fn test_power_of_two_filter_size() {
let strategy = DoubleHashing;
let indices = strategy.generate_indices(12345, 67890, 0, 7, 1024);
assert_eq!(indices.len(), 7);
assert!(indices.iter().all(|&idx| idx < 1024));
}
#[test]
fn test_double_hashing_distribution() {
let strategy = DoubleHashing;
let m = 100;
let k = 10;
let mut buckets = vec![0usize; m];
for seed in 0u64..1000 {
let mixed = seed
.wrapping_mul(0x9e3779b97f4a7c15)
.wrapping_add(0x517cc1b727220a95);
let h1 = mixed ^ (mixed >> 33);
let h2 = h1.wrapping_mul(0x85ebca77c2b2ae63) ^ (h1 >> 29);
let indices = strategy.generate_indices(h1, h2, 0, k, m);
for idx in indices {
buckets[idx] += 1;
}
}
let expected = (1000 * k) / m;
let tolerance = expected / 2;
let mut outliers = 0;
for &count in &buckets {
if count < expected.saturating_sub(tolerance) || count > expected + tolerance {
outliers += 1;
}
}
assert!(
outliers <= m / 10,
"Distribution is poor: {} of {} buckets are outliers",
outliers,
m
);
}
#[test]
fn test_enhanced_double_hashing_distribution() {
let strategy = EnhancedDoubleHashing;
let m = 100;
let k = 10;
let mut buckets = vec![0usize; m];
for seed in 0u64..1000 {
let mixed = seed
.wrapping_mul(0x9e3779b97f4a7c15)
.wrapping_add(0x517cc1b727220a95);
let h1 = mixed ^ (mixed >> 33);
let h2 = h1.wrapping_mul(0x85ebca77c2b2ae63) ^ (h1 >> 29);
let indices = strategy.generate_indices(h1, h2, 0, k, m);
for idx in indices {
buckets[idx] += 1;
}
}
let expected = (1000 * k) / m;
let tolerance = expected / 2;
let mut outliers = 0;
for &count in &buckets {
if count < expected.saturating_sub(tolerance) || count > expected + tolerance {
outliers += 1;
}
}
assert!(
outliers <= m / 10,
"Enhanced distribution is poor: {} of {} buckets are outliers",
outliers,
m
);
}
#[test]
fn test_chi_square_double_hashing() {
let strategy = DoubleHashing;
let m = 100;
let k = 10;
let num_trials: usize = 1000;
let mut buckets = vec![0usize; m];
for seed in 0..num_trials {
let s = seed as u64;
let mixed = s
.wrapping_mul(0x9e3779b97f4a7c15)
.wrapping_add(0x517cc1b727220a95);
let h1 = mixed ^ (mixed >> 33);
let h2 = h1.wrapping_mul(0x85ebca77c2b2ae63) ^ (h1 >> 29);
let indices = strategy.generate_indices(h1, h2, 0, k, m);
for idx in indices {
buckets[idx] += 1;
}
}
let expected = (num_trials * k) as f64 / m as f64;
let chi_squared: f64 = buckets
.iter()
.map(|&observed| {
let diff = observed as f64 - expected;
(diff * diff) / expected
})
.sum();
assert!(
chi_squared < 150.0,
"Chi-square test failed: χ² = {:.2} (critical value ≈ 123.2)",
chi_squared
);
}
#[test]
fn test_chi_square_enhanced_double_hashing() {
let strategy = EnhancedDoubleHashing;
let m = 100;
let k = 10;
let num_trials: usize = 1000;
let mut buckets = vec![0usize; m];
for seed in 0..num_trials {
let s = seed as u64;
let mixed = s
.wrapping_mul(0x9e3779b97f4a7c15)
.wrapping_add(0x517cc1b727220a95);
let h1 = mixed ^ (mixed >> 33);
let h2 = h1.wrapping_mul(0x85ebca77c2b2ae63) ^ (h1 >> 29);
let indices = strategy.generate_indices(h1, h2, 0, k, m);
for idx in indices {
buckets[idx] += 1;
}
}
let expected = (num_trials * k) as f64 / m as f64;
let chi_squared: f64 = buckets
.iter()
.map(|&observed| {
let diff = observed as f64 - expected;
(diff * diff) / expected
})
.sum();
assert!(
chi_squared < 150.0,
"Enhanced chi-square test failed: χ² = {:.2}",
chi_squared
);
}
#[test]
fn test_double_hashing_wrapping_behavior() {
let strategy = DoubleHashing;
let h1 = u64::MAX;
let h2 = u64::MAX;
let indices = strategy.generate_indices(h1, h2, 0, 10, 1000);
assert_eq!(indices.len(), 10);
assert!(indices.iter().all(|&idx| idx < 1000));
}
#[test]
fn test_enhanced_double_hashing_wrapping_behavior() {
let strategy = EnhancedDoubleHashing;
let h1 = u64::MAX;
let h2 = u64::MAX;
let indices = strategy.generate_indices(h1, h2, 0, 10, 1000);
assert_eq!(indices.len(), 10);
assert!(indices.iter().all(|&idx| idx < 1000));
}
#[test]
fn test_triple_hashing_wrapping_behavior() {
let strategy = TripleHashing;
let h1 = u64::MAX;
let h2 = u64::MAX;
let h3 = u64::MAX;
let indices = strategy.generate_indices(h1, h2, h3, 10, 1000);
assert_eq!(indices.len(), 10);
assert!(indices.iter().all(|&idx| idx < 1000));
}
#[test]
fn test_trait_object_usage() {
let strategies: Vec<Box<dyn HashStrategy>> = vec![
Box::new(DoubleHashing),
Box::new(EnhancedDoubleHashing),
Box::new(TripleHashing),
];
for strategy in strategies {
let indices = strategy.generate_indices(12345, 67890, 11111, 7, 1000);
assert_eq!(indices.len(), 7);
assert!(indices.iter().all(|&idx| idx < 1000));
}
}
#[test]
fn test_default_trait() {
let _strategy1 = DoubleHashing;
let _strategy2 = EnhancedDoubleHashing;
let indices = DoubleHashing.generate_indices(12345, 67890, 0, 7, 1000);
assert_eq!(indices.len(), 7);
}
}