#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Input {
I32(i32),
U32(u32),
I64(i64),
U64(u64),
I128(i128),
U128(u128),
}
impl From<i32> for Input {
fn from(i: i32) -> Self {
Input::I32(i)
}
}
impl From<u32> for Input {
fn from(u: u32) -> Self {
Input::U32(u)
}
}
impl From<i64> for Input {
fn from(i: i64) -> Self {
Input::I64(i)
}
}
impl From<u64> for Input {
fn from(u: u64) -> Self {
Input::U64(u)
}
}
impl From<i128> for Input {
fn from(i: i128) -> Self {
Input::I128(i)
}
}
impl From<u128> for Input {
fn from(u: u128) -> Self {
Input::U128(u)
}
}
pub fn hash(input: Input) -> Input {
match input {
Input::I32(i) => Input::I32(internal::hash(i)),
Input::U32(u) => Input::U32(internal::hash(u as i32) as u32),
Input::I64(i) => {
Input::I64(hash_64(i as u64) as i64)
},
Input::U64(u) => {
Input::U64(hash_64(u))
},
Input::I128(i) => {
let out1 = hash_64(i as u64);
let out2 = hash_64((i >> 64) as u64);
Input::I128(((out2 as i128) << 64) | (out1 as i128 & 0xFFFFFFFFFFFFFFFF))
},
Input::U128(u) => {
let out1 = hash_64(u as u64);
let out2 = hash_64((u >> 64) as u64);
Input::U128(((out2 as u128) << 64) | (out1 as u128 & 0xFFFFFFFFFFFFFFFF))
},
}
}
fn hash_64(u: u64) -> u64 {
let out1 = internal::hash(u as i32);
let out2 = internal::hash((u >> 32) as i32);
((out2 as u64) << 32) | (out1 as u64 & 0xFFFFFFFF)
}
mod internal {
pub(crate) fn hash(input: i32) -> i32 {
let mut l1 = (input >> 16) & 65535;
let mut r1 = input & 65535;
let mut i = 0;
while i < 3 {
let l2 = r1;
let r2 = l1 ^ ((((1366 * r1 + 150889) % 714025) as f64 / 714025.0 * 32767.0) as i32);
l1 = l2;
r1 = r2;
i += 1;
}
(r1 << 16) + l1
}
}
#[cfg(test)]
macro_rules! create_tests {
($type:ty) => {
paste::item! {
#[test]
fn [<it_works_$type>]() {
let input: $type = rand::random();
let input = input.into();
let result = hash(input);
assert_ne!(result, 0.into());
assert_ne!(input, result);
}
#[test]
fn [<bidirectional_$type>]() {
let input: $type = rand::random();
let input = input.into();
let result = hash(input);
assert_ne!(result, 0.into());
assert_ne!(input, result);
assert_eq!(hash(result), input);
}
}
};
}
#[cfg(test)]
mod tests {
#![allow(unused_imports)] use rand::random;
use super::*;
create_tests!(i32);
create_tests!(u32);
create_tests!(i64);
create_tests!(u64);
create_tests!(i128);
create_tests!(u128);
}