use crate::proto::sketchlib::SketchEnvelope;
#[inline]
pub fn effective_sample_p(env: &SketchEnvelope) -> f64 {
sample_p_or_default(env.sample_p)
}
#[inline]
pub fn sample_p_or_default(p: f64) -> f64 {
if p > 0.0 && p <= 1.0 {
p
} else {
1.0
}
}
#[inline]
pub fn rescale_count(raw: f64, p: f64) -> f64 {
let p = sample_p_or_default(p);
raw / p
}
#[inline]
pub fn rescale_count_with_env(raw: f64, env: &SketchEnvelope) -> f64 {
rescale_count(raw, effective_sample_p(env))
}
#[inline]
pub const fn is_quantile_scale_invariant() -> bool {
true
}
#[cfg(test)]
mod tests {
use super::*;
use crate::proto::sketchlib::{HyperLogLogState, SketchEnvelope, sketch_envelope::SketchState};
fn env_with_p(p: f64) -> SketchEnvelope {
SketchEnvelope {
format_version: 1,
producer: None,
hash_spec: None,
sample_p: p,
sketch_state: Some(SketchState::Hll(HyperLogLogState {
variant: 2,
precision: 14,
registers: Vec::new(),
hip_kxq0: 0.0,
hip_kxq1: 0.0,
hip_est: 0.0,
registers_sparse: None,
})),
}
}
#[test]
fn dual_read_zero_means_one() {
assert_eq!(effective_sample_p(&env_with_p(0.0)), 1.0);
assert_eq!(sample_p_or_default(0.0), 1.0);
}
#[test]
fn out_of_range_falls_back_to_one() {
assert_eq!(sample_p_or_default(-0.5), 1.0);
assert_eq!(sample_p_or_default(1.5), 1.0);
assert_eq!(sample_p_or_default(f64::NAN), 1.0);
}
#[test]
fn valid_p_passes_through() {
assert_eq!(effective_sample_p(&env_with_p(0.1)), 0.1);
assert_eq!(sample_p_or_default(1.0), 1.0);
}
#[test]
fn rescale_count_inverts_probability() {
assert!((rescale_count(10_000.0, 0.1) - 100_000.0).abs() < 1e-6);
assert_eq!(rescale_count(123.0, 1.0), 123.0);
assert_eq!(rescale_count(123.0, 0.0), 123.0);
}
#[test]
fn rescale_with_env() {
assert!((rescale_count_with_env(5_000.0, &env_with_p(0.05)) - 100_000.0).abs() < 1e-6);
assert_eq!(rescale_count_with_env(42.0, &env_with_p(0.0)), 42.0);
}
#[test]
fn quantiles_are_scale_invariant() {
assert!(is_quantile_scale_invariant());
}
}