use crate::{HllError, HyperLogLog};
pub fn estimate_union(a: &HyperLogLog, b: &HyperLogLog) -> Result<f64, HllError> {
if a.precision() != b.precision() {
return Err(HllError::PrecisionMismatch {
left: a.precision(),
right: b.precision(),
});
}
let mut merged = HyperLogLog::new(a.precision());
let ra = a.registers();
let rb = b.registers();
merged.apply_paired_max(ra, rb);
Ok(merged.estimate())
}
pub fn estimate_intersect(a: &HyperLogLog, b: &HyperLogLog) -> Result<f64, HllError> {
let ea = a.estimate();
let eb = b.estimate();
let union = estimate_union(a, b)?;
let inter = ea + eb - union;
Ok(inter.max(0.0))
}
pub fn intersect_error_bound(a: &HyperLogLog, b: &HyperLogLog) -> Result<f64, HllError> {
if a.precision() != b.precision() {
return Err(HllError::PrecisionMismatch {
left: a.precision(),
right: b.precision(),
});
}
Ok(a.standard_error() * (a.estimate() + b.estimate()))
}
impl HyperLogLog {
pub(crate) fn apply_paired_max(&mut self, a: &[u8], b: &[u8]) {
debug_assert_eq!(a.len(), self.registers().len());
debug_assert_eq!(b.len(), self.registers().len());
for (i, (x, y)) in a.iter().zip(b.iter()).enumerate() {
let m = (*x).max(*y);
if m > self.registers[i] {
self.registers[i] = m;
}
}
}
}
#[cfg(test)]
#[path = "union_intersect_tests.rs"]
mod tests;