use rapidhash::v3::rapidhash_v3;
pub const HLL_REGISTERS: usize = 16384;
pub const HLL_DENSE_SIZE: usize = 12288;
pub const HLL_REGISTER_COUNT_POW: usize = 14;
pub const HLL_REGISTER_COUNT_MASK: u64 = (1 << HLL_REGISTER_COUNT_POW) - 1;
pub const HLL_HASH_BIT_COUNT: usize = 50;
pub const HLL_REGISTER_BITS: usize = 6;
pub const HLL_REGISTER_MAX: u8 = (1 << HLL_REGISTER_BITS) - 1;
pub const HLL_ALPHA_INF: f64 = 0.721_347_520_444_481_7;
pub const HLL_SEGMENT_COUNT: usize = 16;
pub const HLL_SEGMENT_REGISTERS: usize = 1024;
pub const HLL_SEGMENT_BYTES: usize = 768;
pub const HLL_HASH_SEED: u32 = 0xadc8_3b19;
#[inline]
pub fn rapid_hash(bytes: &[u8]) -> u64 {
rapidhash_v3(bytes)
}
#[inline]
pub fn extract_dense_hll_result(hash: u64) -> (usize, u8) {
let index = (hash & HLL_REGISTER_COUNT_MASK) as usize;
let shifted = (hash >> HLL_REGISTER_COUNT_POW) | (1u64 << HLL_HASH_BIT_COUNT);
let count = (shifted.trailing_zeros() + 1) as u8;
(index, count)
}
#[inline]
pub fn hll_sigma(x: f64) -> f64 {
if x <= 0.0 || x.is_nan() {
return 0.0;
}
if x >= 1.0 {
return f64::INFINITY;
}
let mut x = x;
let mut y = 1.0;
let mut z = x;
loop {
x *= x;
let z_prime = z;
z += x * y;
y += y;
if z_prime == z {
break;
}
}
z
}
#[inline]
pub fn hll_tau(x: f64) -> f64 {
if x <= 0.0 || x >= 1.0 || x.is_nan() {
return 0.0;
}
let mut x = x;
let mut y = 1.0;
let mut z = 1.0 - x;
loop {
x = x.sqrt();
let z_prime = z;
y *= 0.5;
let diff = 1.0 - x;
z -= diff * diff * y;
if z_prime == z {
break;
}
}
z / 3.0
}
#[inline]
pub fn hll_estimate_from_histo(reghisto: &[usize; 64]) -> u64 {
let m = HLL_REGISTERS as f64;
let mut z = m * hll_tau((m - reghisto[HLL_HASH_BIT_COUNT + 1] as f64) / m);
for j in (1..=HLL_HASH_BIT_COUNT).rev() {
z += reghisto[j] as f64;
z *= 0.5;
}
z += m * hll_sigma(reghisto[0] as f64 / m);
if z <= 0.0 || z.is_nan() || z.is_infinite() {
0
} else {
(HLL_ALPHA_INF * m * m / z).round() as u64
}
}