#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub(crate) struct MultiplicativeHash {
multiplier: u64,
addend: u64,
}
#[inline(always)]
const fn reduce(x: u64, domain: usize) -> usize {
((domain as u128 * x as u128) >> 64) as usize
}
impl MultiplicativeHash {
pub const fn new(multiplier: u64, addend: u64) -> MultiplicativeHash {
MultiplicativeHash {
multiplier: multiplier | 1,
addend,
}
}
pub const fn new_keyed(key: &[u8]) -> MultiplicativeHash {
use extendhash::sha256;
let hash = sha256::compute_hash(key);
let multiplier = [
hash[0], hash[1], hash[2], hash[3], hash[4], hash[5], hash[6], hash[7],
];
let addend = [
hash[8], hash[9], hash[10], hash[11], hash[12], hash[13], hash[14], hash[15],
];
MultiplicativeHash::new(u64::from_le_bytes(multiplier), u64::from_le_bytes(addend))
}
#[cfg(test)]
pub fn new_random() -> MultiplicativeHash {
use rand::Rng;
let mut rnd = rand::thread_rng();
MultiplicativeHash::new(rnd.gen(), rnd.gen())
}
#[inline(always)]
pub const fn mix(&self, value: u64) -> u64 {
value
.wrapping_mul(self.multiplier)
.wrapping_add(self.addend)
}
#[inline(always)]
pub const fn map(&self, value: u64, range: usize) -> usize {
reduce(self.mix(value), range)
}
}
#[test]
fn test_reduce() {
assert_eq!(reduce(0, 0), 0);
assert_eq!(reduce(u64::MAX, 0), 0);
assert_eq!(reduce(0, 17), 0);
assert_eq!(reduce(u64::MAX / 17, 17), 0);
assert_eq!(reduce(1 + u64::MAX / 17, 17), 1);
assert_eq!(reduce(u64::MAX, 17), 16);
}
#[test]
fn test_reduce_power_of_two() {
assert_eq!(reduce(10 << 33, 1 << 32), 10 << 1);
assert_eq!(reduce(15 << 60, 1 << 8), 15 << 4);
}
#[test]
fn test_mix() {
let h1 = MultiplicativeHash::new_keyed(b"h1");
let h2 = MultiplicativeHash::new_keyed(b"h2");
assert!(h1 != h2);
assert!(h1.mix(0) != h2.mix(0));
assert!(h1.mix(1) != h2.mix(1));
assert!(h1.mix(42) != h2.mix(42));
assert!(h1.mix(u64::MAX) != h2.mix(u64::MAX));
}
#[test]
fn test_random_mix() {
let h1 = MultiplicativeHash::new_random();
let h2 = MultiplicativeHash::new_random();
assert!(h1 != h2);
assert!(h1.mix(0) != h2.mix(0));
assert!(h1.mix(1) != h2.mix(1));
assert!(h1.mix(42) != h2.mix(42));
assert!(h1.mix(u64::MAX) != h2.mix(u64::MAX));
}
#[test]
fn test_map() {
let h1 = MultiplicativeHash::new_keyed(b"h1");
let h2 = MultiplicativeHash::new_keyed(b"h2");
assert!(h1 != h2);
assert!(h1.map(0, 1024) != h2.map(0, 1024));
assert!(h1.map(1, 1234) != h2.map(1, 1234));
assert!(h1.map(42, 4567) != h2.map(42, 4567));
assert!(h1.map(u64::MAX, 789) != h2.map(u64::MAX, 789));
}
#[test]
fn test_new_keyed() {
const H: MultiplicativeHash = MultiplicativeHash::new_keyed(b"asdfg");
assert_eq!(H.mix(0), 7162733811001658625);
assert_eq!(H.addend, 7162733811001658625);
assert_eq!(H.mix(1), 14551484392748644090);
assert_eq!(H.multiplier, 7388750581746985465);
assert_eq!(
H,
MultiplicativeHash::new(7388750581746985465, 7162733811001658625)
);
assert_eq!(
H.mix(42),
42u64
.wrapping_mul(7388750581746985465)
.wrapping_add(7162733811001658625)
);
assert_eq!(
H.mix(u64::MAX),
u64::MAX
.wrapping_mul(7388750581746985465)
.wrapping_add(7162733811001658625)
);
}