use histogram::{Error, Histogram, Histogram32, Quantile};
macro_rules! check_width {
($module:ident, $histogram:ty, $count:ty) => {
mod $module {
use super::*;
#[test]
fn ranks_at_scan_boundaries_and_zero_runs() {
let mut h = <$histogram>::new(5, 6).unwrap();
for (value, count) in [
(0, 1),
(7, 2),
(8, 1),
(16, 4),
(31, 2),
(32, 1),
(55, 3),
(63, 2),
] {
h.add(value, count).unwrap();
}
let mut qs: Vec<_> = (0..=16).rev().map(|rank| rank as f64 / 16.0).collect();
qs.extend([0.5, 0.0, 1.0]);
let result = h.quantiles(&qs).unwrap().unwrap();
assert_eq!(result.total_count(), 16);
assert_eq!(result.entries().len(), 17);
assert_eq!(result.min().range(), 0..=0);
assert_eq!(result.min().count(), 1);
assert_eq!(result.max().range(), 63..=63);
assert_eq!(result.max().count(), 2);
let expected = [
(0, 1),
(0, 1),
(7, 2),
(7, 2),
(8, 1),
(16, 4),
(16, 4),
(16, 4),
(16, 4),
(31, 2),
(31, 2),
(32, 1),
(55, 3),
(55, 3),
(55, 3),
(63, 2),
(63, 2),
];
for (rank, (value, count)) in expected.into_iter().enumerate() {
let bucket = result
.get(&Quantile::new(rank as f64 / 16.0).unwrap())
.unwrap();
assert_eq!(bucket.range(), value..=value);
assert_eq!(bucket.count(), count);
}
}
#[test]
fn singleton_at_every_position_including_short_tails() {
for (gp, max) in [(0, 1), (0, 8), (1, 7), (5, 6)] {
let mut h = <$histogram>::new(gp, max).unwrap();
for index in 0..h.as_slice().len() {
h.as_mut_slice().fill(0);
h.as_mut_slice()[index] = 3;
let result = h.quantiles(&[1.0, 0.5, 0.0]).unwrap().unwrap();
let expected = h.iter().nth(index).unwrap();
assert_eq!(result.total_count(), 3);
assert_eq!(result.min(), &expected);
assert_eq!(result.max(), &expected);
for bucket in result.entries().values() {
assert_eq!(bucket, &expected);
}
}
}
}
#[test]
fn reports_match_independently_sorted_observations() {
let mut seed = 42u64;
for gp in [0, 3, 7, 10] {
let mut h = <$histogram>::new(gp, 16).unwrap();
let mut values = Vec::new();
for _ in 0..2048 {
seed ^= seed << 13;
seed ^= seed >> 7;
seed ^= seed << 17;
let value = seed & 65535;
h.increment(value).unwrap();
values.push(value);
}
values.sort_unstable();
let qs: Vec<_> = (0..=128).map(|i| i as f64 / 128.0).collect();
let result = h.quantiles(&qs).unwrap().unwrap();
assert_eq!(result.total_count(), 2048);
assert!(result.min().range().contains(&values[0]));
assert!(result.max().range().contains(&values[2047]));
for (i, q) in qs.into_iter().enumerate() {
let bucket = result.get(&Quantile::new(q).unwrap()).unwrap();
assert!(bucket.range().contains(&values[(i * 16).max(1) - 1]));
assert_eq!(
bucket.count(),
values.iter().filter(|v| bucket.range().contains(v)).count() as u64
);
}
}
}
#[test]
fn empty_requests_validation_and_counter_mutation() {
let mut h = <$histogram>::new(5, 6).unwrap();
for populated in [false, true] {
if populated {
h.add(17, 4).unwrap();
}
for invalid in [f64::NAN, f64::INFINITY, f64::NEG_INFINITY, -0.01, 1.01] {
assert_eq!(h.quantiles(&[0.5, invalid]), Err(Error::InvalidQuantile));
}
let result = h.quantiles(&[]).unwrap();
if populated {
let result = result.unwrap();
assert!(result.entries().is_empty());
assert_eq!(result.total_count(), 4);
assert_eq!(result.min().range(), 17..=17);
assert_eq!(result.max().range(), 17..=17);
} else {
assert!(result.is_none());
}
}
h.as_mut_slice().fill(0);
assert!(h.quantile(0.5).unwrap().is_none());
h.as_mut_slice()[63] = 2;
let result = h.quantile(0.5).unwrap().unwrap();
assert_eq!(result.total_count(), 2);
assert_eq!(result.min().range(), 63..=63);
assert_eq!(result.max().range(), 63..=63);
}
#[test]
fn block_sums_exceed_counter_width() {
let mut h = <$histogram>::new(5, 6).unwrap();
let count: $count = 1 << (<$count>::BITS - 1);
h.as_mut_slice()[1..=16].fill(count);
let result = h.quantiles(&[0.0, 0.5, 1.0]).unwrap().unwrap();
assert_eq!(result.total_count(), count as u128 * 16);
for (q, value) in [(0.0, 1), (0.5, 8), (1.0, 16)] {
let bucket = result.get(&Quantile::new(q).unwrap()).unwrap();
assert_eq!(bucket.range(), value..=value);
assert_eq!(bucket.count(), count as u64);
}
}
}
};
}
check_width!(wide, Histogram, u64);
check_width!(narrow, Histogram32, u32);