1use std::{
2 cmp::{self, Ordering},
3 mem,
4};
5
6use ordered_float::OrderedFloat;
7use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error};
8use serde_with::{DeserializeAs, SerializeAs, serde_as};
9use snafu::Snafu;
10use vector_common::byte_size_of::ByteSizeOf;
11use vector_config::configurable_component;
12
13use crate::{
14 event::{Metric, MetricValue, metric::Bucket},
15 float_eq,
16};
17
18const AGENT_DEFAULT_BIN_LIMIT: u16 = 4096;
19const AGENT_DEFAULT_EPS: f64 = 1.0 / 128.0;
20const AGENT_DEFAULT_MIN_VALUE: f64 = 1.0e-9;
21
22const UV_INF: i16 = i16::MAX;
23const MAX_KEY: i16 = UV_INF;
24
25const INITIAL_BINS: u16 = 128;
26const MAX_BIN_WIDTH: u16 = u16::MAX;
27
28#[inline]
29fn log_gamma(gamma_ln: f64, v: f64) -> f64 {
30 v.ln() / gamma_ln
31}
32
33#[inline]
34fn pow_gamma(gamma_v: f64, y: f64) -> f64 {
35 gamma_v.powf(y)
36}
37
38#[inline]
39fn lower_bound(gamma_v: f64, bias: i32, k: i16) -> f64 {
40 if k < 0 {
41 return -lower_bound(gamma_v, bias, -k);
42 }
43
44 if k == MAX_KEY {
45 return f64::INFINITY;
46 }
47
48 if k == 0 {
49 return 0.0;
50 }
51
52 pow_gamma(gamma_v, f64::from(i32::from(k) - bias))
53}
54
55#[derive(Debug, Snafu)]
56pub enum MergeError {
57 #[snafu(display("cannot merge two sketches with mismatched configuration parameters"))]
58 MismatchedConfigs,
59}
60
61#[derive(Copy, Clone, Debug, PartialEq)]
62pub struct Config {
63 bin_limit: u16,
64 gamma_v: f64,
66 gamma_ln: f64,
67 norm_min: f64,
76 norm_bias: i32,
78}
79
80impl Config {
81 #[allow(clippy::cast_possible_truncation)]
82 pub(self) fn new(mut eps: f64, min_value: f64, bin_limit: u16) -> Self {
83 assert!(eps > 0.0 && eps < 1.0, "eps must be between 0.0 and 1.0");
84 assert!(min_value > 0.0, "min value must be greater than 0.0");
85 assert!(bin_limit > 0, "bin limit must be greater than 0");
86
87 eps *= 2.0;
88 let gamma_v = 1.0 + eps;
89 let gamma_ln = eps.ln_1p();
90
91 let norm_eff_min = log_gamma(gamma_ln, min_value).floor() as i32;
94 let norm_bias = -norm_eff_min + 1;
95
96 let norm_min = lower_bound(gamma_v, norm_bias, 1);
97
98 assert!(
99 norm_min <= min_value,
100 "norm min should not exceed min_value"
101 );
102
103 Self {
104 bin_limit,
105 gamma_v,
106 gamma_ln,
107 norm_min,
108 norm_bias,
109 }
110 }
111
112 #[allow(dead_code)]
113 pub(crate) fn relative_accuracy(&self) -> f64 {
114 (self.gamma_v - 1.0) / 2.0
116 }
117
118 pub fn bin_lower_bound(&self, k: i16) -> f64 {
120 lower_bound(self.gamma_v, self.norm_bias, k)
121 }
122
123 #[allow(clippy::cast_possible_truncation)]
128 pub fn key(&self, v: f64) -> i16 {
129 if v < 0.0 {
130 return -self.key(-v);
131 }
132
133 if v == 0.0 || (v > 0.0 && v < self.norm_min) || (v < 0.0 && v > -self.norm_min) {
134 return 0;
135 }
136
137 let rounded = round_to_even(self.log_gamma(v)) as i32;
141 let key = rounded.wrapping_add(self.norm_bias);
142
143 key.clamp(1, i32::from(MAX_KEY)) as i16
146 }
147
148 pub fn log_gamma(&self, v: f64) -> f64 {
149 log_gamma(self.gamma_ln, v)
150 }
151}
152
153impl Default for Config {
154 fn default() -> Self {
155 Config::new(
156 AGENT_DEFAULT_EPS,
157 AGENT_DEFAULT_MIN_VALUE,
158 AGENT_DEFAULT_BIN_LIMIT,
159 )
160 }
161}
162
163#[derive(Clone, Copy, Debug, Eq, PartialEq)]
165pub struct Bin {
166 k: i16,
168
169 n: u16,
171}
172
173impl Bin {
174 #[allow(clippy::cast_possible_truncation)]
175 fn increment(&mut self, n: u32) -> u32 {
176 let next = n + u32::from(self.n);
177 if next > u32::from(MAX_BIN_WIDTH) {
178 self.n = MAX_BIN_WIDTH;
179 return next - u32::from(MAX_BIN_WIDTH);
180 }
181
182 self.n = next as u16;
185 0
186 }
187}
188
189#[serde_as]
212#[configurable_component]
213#[derive(Clone, Debug)]
214pub struct AgentDDSketch {
215 #[serde(skip)]
216 config: Config,
217
218 #[serde_as(as = "BinMap")]
220 bins: Vec<Bin>,
221
222 count: u32,
224
225 min: f64,
227
228 max: f64,
230
231 sum: f64,
233
234 avg: f64,
236}
237
238impl AgentDDSketch {
239 pub fn with_agent_defaults() -> Self {
242 let config = Config::default();
243 let initial_bins = cmp::max(INITIAL_BINS, config.bin_limit) as usize;
244
245 Self {
246 config,
247 bins: Vec::with_capacity(initial_bins),
248 count: 0,
249 min: f64::MAX,
250 max: f64::MIN,
251 sum: 0.0,
252 avg: 0.0,
253 }
254 }
255
256 pub fn from_raw(
264 count: u32,
265 min: f64,
266 max: f64,
267 sum: f64,
268 avg: f64,
269 keys: &[i16],
270 counts: &[u16],
271 ) -> Option<AgentDDSketch> {
272 let bin_map = BinMap {
273 keys: keys.into(),
274 counts: counts.into(),
275 };
276 bin_map.into_bins().map(|bins| Self {
277 config: Config::default(),
278 bins,
279 count,
280 min,
281 max,
282 sum,
283 avg,
284 })
285 }
286
287 #[cfg(feature = "generate-fixtures")]
293 pub fn set_sum_avg(&mut self, sum: f64, avg: f64) {
294 self.sum = sum;
295 self.avg = avg;
296 }
297
298 pub fn gamma(&self) -> f64 {
299 self.config.gamma_v
300 }
301
302 pub fn bin_index_offset(&self) -> i32 {
303 self.config.norm_bias
304 }
305
306 #[allow(dead_code)]
307 fn bin_count(&self) -> usize {
308 self.bins.len()
309 }
310
311 pub fn bins(&self) -> &[Bin] {
312 &self.bins
313 }
314
315 pub fn bin_map(&self) -> BinMap {
316 BinMap::from_bins(&self.bins)
317 }
318
319 pub fn config(&self) -> &Config {
320 &self.config
321 }
322
323 pub fn is_empty(&self) -> bool {
325 self.count == 0
326 }
327
328 pub fn count(&self) -> u32 {
330 self.count
331 }
332
333 pub fn min(&self) -> Option<f64> {
337 if self.is_empty() {
338 None
339 } else {
340 Some(self.min)
341 }
342 }
343
344 pub fn max(&self) -> Option<f64> {
348 if self.is_empty() {
349 None
350 } else {
351 Some(self.max)
352 }
353 }
354
355 pub fn sum(&self) -> Option<f64> {
359 if self.is_empty() {
360 None
361 } else {
362 Some(self.sum)
363 }
364 }
365
366 pub fn avg(&self) -> Option<f64> {
370 if self.is_empty() {
371 None
372 } else {
373 Some(self.avg)
374 }
375 }
376
377 pub fn clear(&mut self) {
379 self.count = 0;
380 self.min = f64::MAX;
381 self.max = f64::MIN;
382 self.avg = 0.0;
383 self.sum = 0.0;
384 self.bins.clear();
385 }
386
387 fn adjust_basic_stats(&mut self, v: f64, n: u32) {
388 if v < self.min {
389 self.min = v;
390 }
391
392 if v > self.max {
393 self.max = v;
394 }
395
396 self.count += n;
397 self.sum += v * f64::from(n);
398
399 if n == 1 {
400 self.avg += (v - self.avg) / f64::from(self.count);
401 } else {
402 self.avg = self.avg + (v - self.avg) * f64::from(n) / f64::from(self.count);
405 }
406 }
407
408 fn insert_key_counts(&mut self, mut counts: Vec<(i16, u32)>) {
409 counts.sort_unstable_by(|(k1, _), (k2, _)| k1.cmp(k2));
411
412 let mut temp = Vec::new();
413
414 let mut bins_idx = 0;
415 let mut key_idx = 0;
416 let bins_len = self.bins.len();
417 let counts_len = counts.len();
418
419 while bins_idx < bins_len && key_idx < counts_len {
428 let bin = self.bins[bins_idx];
429 let vk = counts[key_idx].0;
430 let kn = counts[key_idx].1;
431
432 match bin.k.cmp(&vk) {
433 Ordering::Greater => {
434 generate_bins(&mut temp, vk, kn);
435 key_idx += 1;
436 }
437 Ordering::Less => {
438 temp.push(bin);
439 bins_idx += 1;
440 }
441 Ordering::Equal => {
442 generate_bins(&mut temp, bin.k, u32::from(bin.n) + kn);
443 bins_idx += 1;
444 key_idx += 1;
445 }
446 }
447 }
448
449 temp.extend_from_slice(&self.bins[bins_idx..]);
450
451 while key_idx < counts_len {
452 let vk = counts[key_idx].0;
453 let kn = counts[key_idx].1;
454 generate_bins(&mut temp, vk, kn);
455 key_idx += 1;
456 }
457
458 trim_left(&mut temp, self.config.bin_limit);
459
460 self.bins = temp;
463 }
464
465 fn insert_keys(&mut self, mut keys: Vec<i16>) {
466 assert!(
469 keys.len()
470 <= TryInto::<usize>::try_into(u32::MAX).expect("we don't support 16-bit systems")
471 );
472
473 keys.sort_unstable();
474
475 let mut temp = Vec::new();
476
477 let mut bins_idx = 0;
478 let mut key_idx = 0;
479 let bins_len = self.bins.len();
480 let keys_len = keys.len();
481
482 while bins_idx < bins_len && key_idx < keys_len {
491 let bin = self.bins[bins_idx];
492 let vk = keys[key_idx];
493
494 match bin.k.cmp(&vk) {
495 Ordering::Greater => {
496 let kn = buf_count_leading_equal(&keys, key_idx);
497 generate_bins(&mut temp, vk, kn);
498 key_idx += kn as usize;
499 }
500 Ordering::Less => {
501 temp.push(bin);
502 bins_idx += 1;
503 }
504 Ordering::Equal => {
505 let kn = buf_count_leading_equal(&keys, key_idx);
506 generate_bins(&mut temp, bin.k, u32::from(bin.n) + kn);
507 bins_idx += 1;
508 key_idx += kn as usize;
509 }
510 }
511 }
512
513 temp.extend_from_slice(&self.bins[bins_idx..]);
514
515 while key_idx < keys_len {
516 let vk = keys[key_idx];
517 let kn = buf_count_leading_equal(&keys, key_idx);
518 generate_bins(&mut temp, vk, kn);
519 key_idx += kn as usize;
520 }
521
522 trim_left(&mut temp, self.config.bin_limit);
523
524 self.bins = temp;
527 }
528
529 pub fn insert(&mut self, v: f64) {
530 self.adjust_basic_stats(v, 1);
533
534 let key = self.config.key(v);
535 self.insert_keys(vec![key]);
536 }
537
538 pub fn insert_many(&mut self, vs: &[f64]) {
539 let mut keys = Vec::with_capacity(vs.len());
542 for v in vs {
543 self.adjust_basic_stats(*v, 1);
544 keys.push(self.config.key(*v));
545 }
546 self.insert_keys(keys);
547 }
548
549 pub fn insert_n(&mut self, v: f64, n: u32) {
550 self.adjust_basic_stats(v, n);
553
554 let key = self.config.key(v);
555 self.insert_key_counts(vec![(key, n)]);
556 }
557
558 fn insert_interpolate_bucket(&mut self, lower: f64, upper: f64, count: u32) {
559 let lower_key = self.config.key(lower);
562 let upper_key = self.config.key(upper);
563 let keys = (lower_key..=upper_key).collect::<Vec<_>>();
564
565 let mut key_counts = Vec::new();
566 let mut remaining_count = count;
567 let distance = upper - lower;
568 let mut start_idx = 0;
569 let mut end_idx = 1;
570 let mut lower_bound = self.config.bin_lower_bound(keys[start_idx]);
571 let mut remainder = 0.0;
572
573 while end_idx < keys.len() && remaining_count > 0 {
574 let upper_bound = self.config.bin_lower_bound(keys[end_idx]);
578 let fkn = ((upper_bound - lower_bound) / distance) * f64::from(count);
579 if fkn > 1.0 {
580 remainder += fkn - fkn.trunc();
581 }
582
583 #[allow(clippy::cast_possible_truncation)]
586 let mut kn = fkn as u32;
587 if remainder > 1.0 {
588 kn += 1;
589 remainder -= 1.0;
590 }
591
592 if kn > 0 {
593 if kn > remaining_count {
594 kn = remaining_count;
595 }
596
597 self.adjust_basic_stats(lower_bound, kn);
598 key_counts.push((keys[start_idx], kn));
599
600 remaining_count -= kn;
601 start_idx = end_idx;
602 lower_bound = upper_bound;
603 }
604
605 end_idx += 1;
606 }
607
608 if remaining_count > 0 {
609 let last_key = keys[start_idx];
610 lower_bound = self.config.bin_lower_bound(last_key);
611 self.adjust_basic_stats(lower_bound, remaining_count);
612 key_counts.push((last_key, remaining_count));
613 }
614
615 self.insert_key_counts(key_counts);
616 }
617
618 pub fn insert_interpolate_buckets(
622 &mut self,
623 mut buckets: Vec<Bucket>,
624 ) -> Result<(), &'static str> {
625 buckets.sort_by(|a, b| {
628 let oa = OrderedFloat(a.upper_limit);
629 let ob = OrderedFloat(b.upper_limit);
630
631 oa.cmp(&ob)
632 });
633
634 let mut lower = f64::NEG_INFINITY;
635
636 if buckets
637 .iter()
638 .any(|bucket| bucket.count > u64::from(u32::MAX))
639 {
640 return Err("bucket size greater than u32::MAX");
641 }
642
643 for bucket in buckets {
644 let mut upper = bucket.upper_limit;
645 if upper.is_sign_positive() && upper.is_infinite() {
646 upper = lower;
647 } else if lower.is_sign_negative() && lower.is_infinite() {
648 lower = upper;
649 }
650
651 let count = u32::try_from(bucket.count)
656 .unwrap_or_else(|_| unreachable!("count range has already been checked."));
657
658 self.insert_interpolate_bucket(lower, upper, count);
659 lower = bucket.upper_limit;
660 }
661
662 Ok(())
663 }
664
665 #[allow(dead_code)]
671 pub(crate) fn insert_raw_bin(&mut self, k: i16, n: u16) {
672 let v = self.config.bin_lower_bound(k);
673 self.adjust_basic_stats(v, u32::from(n));
674 self.bins.push(Bin { k, n });
675 }
676
677 pub fn quantile(&self, q: f64) -> Option<f64> {
678 if self.count == 0 {
679 return None;
680 }
681
682 if q <= 0.0 {
683 return Some(self.min);
684 }
685
686 if q >= 1.0 {
687 return Some(self.max);
688 }
689
690 let mut n = 0.0;
691 let mut estimated = None;
692 let wanted_rank = rank(self.count, q);
693
694 for (i, bin) in self.bins.iter().enumerate() {
695 n += f64::from(bin.n);
696 if n <= wanted_rank {
697 continue;
698 }
699
700 let weight = (n - wanted_rank) / f64::from(bin.n);
701 let mut v_low = self.config.bin_lower_bound(bin.k);
702 let mut v_high = v_low * self.config.gamma_v;
703
704 if i == self.bins.len() {
705 v_high = self.max;
706 } else if i == 0 {
707 v_low = self.min;
708 }
709
710 estimated = Some(v_low * weight + v_high * (1.0 - weight));
711 break;
712 }
713
714 estimated
715 .map(|v| v.clamp(self.min, self.max))
716 .or(Some(f64::NAN))
717 }
718
719 pub fn merge(&mut self, other: &AgentDDSketch) -> Result<(), MergeError> {
730 if self.config != other.config {
731 return Err(MergeError::MismatchedConfigs);
732 }
733
734 self.count += other.count;
736 if other.max > self.max {
737 self.max = other.max;
738 }
739 if other.min < self.min {
740 self.min = other.min;
741 }
742 self.sum += other.sum;
743 self.avg =
744 self.avg + (other.avg - self.avg) * f64::from(other.count) / f64::from(self.count);
745
746 let mut temp = Vec::new();
748
749 let mut bins_idx = 0;
750 for other_bin in &other.bins {
751 let start = bins_idx;
752 while bins_idx < self.bins.len() && self.bins[bins_idx].k < other_bin.k {
753 bins_idx += 1;
754 }
755
756 temp.extend_from_slice(&self.bins[start..bins_idx]);
757
758 if bins_idx >= self.bins.len() || self.bins[bins_idx].k > other_bin.k {
759 temp.push(*other_bin);
760 } else if self.bins[bins_idx].k == other_bin.k {
761 generate_bins(
762 &mut temp,
763 other_bin.k,
764 u32::from(other_bin.n) + u32::from(self.bins[bins_idx].n),
765 );
766 bins_idx += 1;
767 }
768 }
769
770 temp.extend_from_slice(&self.bins[bins_idx..]);
771 trim_left(&mut temp, self.config.bin_limit);
772
773 self.bins = temp;
774
775 Ok(())
776 }
777
778 pub fn transform_to_sketch(mut metric: Metric) -> Result<Metric, &'static str> {
794 let sketch = match metric.data_mut().value_mut() {
795 MetricValue::Distribution { samples, .. } => {
796 let mut sketch = AgentDDSketch::with_agent_defaults();
797 for sample in samples {
798 sketch.insert_n(sample.value, sample.rate);
799 }
800 Some(sketch)
801 }
802 MetricValue::AggregatedHistogram { buckets, .. } => {
803 let delta_buckets = mem::take(buckets);
804 let mut sketch = AgentDDSketch::with_agent_defaults();
805 sketch.insert_interpolate_buckets(delta_buckets)?;
806 Some(sketch)
807 }
808 _ => None,
810 };
811
812 match sketch {
813 None => Ok(metric),
815 Some(sketch) => Ok(metric.with_value(sketch.into())),
817 }
818 }
819}
820
821impl PartialEq for AgentDDSketch {
822 fn eq(&self, other: &Self) -> bool {
823 self.count == other.count
832 && float_eq(self.min, other.min)
833 && float_eq(self.max, other.max)
834 && float_eq(self.sum, other.sum)
835 && float_eq(self.avg, other.avg)
836 && self.bins == other.bins
837 }
838}
839
840impl Eq for AgentDDSketch {}
841
842impl ByteSizeOf for AgentDDSketch {
843 fn allocated_bytes(&self) -> usize {
844 self.bins.len() * mem::size_of::<Bin>()
845 }
846}
847
848#[configurable_component]
857#[derive(Clone, Debug)]
858pub struct BinMap {
859 #[serde(rename = "k")]
861 pub keys: Vec<i16>,
862
863 #[serde(rename = "n")]
865 pub counts: Vec<u16>,
866}
867
868impl BinMap {
869 pub fn from_bins<B>(bins: B) -> BinMap
870 where
871 B: AsRef<[Bin]>,
872 {
873 let (keys, counts) =
874 bins.as_ref()
875 .iter()
876 .fold((Vec::new(), Vec::new()), |(mut keys, mut counts), bin| {
877 keys.push(bin.k);
878 counts.push(bin.n);
879
880 (keys, counts)
881 });
882
883 BinMap { keys, counts }
884 }
885
886 pub fn into_parts(self) -> (Vec<i16>, Vec<u16>) {
887 (self.keys, self.counts)
888 }
889
890 pub fn into_bins(self) -> Option<Vec<Bin>> {
891 if self.keys.len() == self.counts.len() {
892 Some(
893 self.keys
894 .into_iter()
895 .zip(self.counts)
896 .map(|(k, n)| Bin { k, n })
897 .collect(),
898 )
899 } else {
900 None
901 }
902 }
903}
904
905impl SerializeAs<Vec<Bin>> for BinMap {
906 fn serialize_as<S>(source: &Vec<Bin>, serializer: S) -> Result<S::Ok, S::Error>
907 where
908 S: Serializer,
909 {
910 let bin_map = BinMap::from_bins(source);
911 bin_map.serialize(serializer)
912 }
913}
914
915impl<'de> DeserializeAs<'de, Vec<Bin>> for BinMap {
916 fn deserialize_as<D>(deserializer: D) -> Result<Vec<Bin>, D::Error>
917 where
918 D: Deserializer<'de>,
919 {
920 let bin_map: BinMap = Deserialize::deserialize(deserializer)?;
921 bin_map
922 .into_bins()
923 .ok_or("keys and counts must match in length")
924 .map_err(D::Error::custom)
925 }
926}
927
928fn rank(count: u32, q: f64) -> f64 {
929 round_to_even(q * f64::from(count - 1))
930}
931
932#[allow(clippy::cast_possible_truncation)]
933fn buf_count_leading_equal(keys: &[i16], start_idx: usize) -> u32 {
934 if start_idx == keys.len() - 1 {
935 return 1;
936 }
937
938 let mut idx = start_idx;
939 while idx < keys.len() && keys[idx] == keys[start_idx] {
940 idx += 1;
941 }
942
943 (idx - start_idx) as u32
946}
947
948fn trim_left(bins: &mut Vec<Bin>, bin_limit: u16) {
949 let bin_limit = bin_limit as usize;
952 if bin_limit == 0 || bins.len() < bin_limit {
953 return;
954 }
955
956 let num_to_remove = bins.len() - bin_limit;
957 let mut missing = 0;
958 let mut overflow = Vec::new();
959
960 for bin in bins.iter().take(num_to_remove) {
961 missing += u32::from(bin.n);
962
963 if missing > u32::from(MAX_BIN_WIDTH) {
964 overflow.push(Bin {
965 k: bin.k,
966 n: MAX_BIN_WIDTH,
967 });
968
969 missing -= u32::from(MAX_BIN_WIDTH);
970 }
971 }
972
973 let bin_remove = &mut bins[num_to_remove];
974 missing = bin_remove.increment(missing);
975 if missing > 0 {
976 generate_bins(&mut overflow, bin_remove.k, missing);
977 }
978
979 let overflow_len = overflow.len();
980 let (_, bins_end) = bins.split_at(num_to_remove);
981 overflow.extend_from_slice(bins_end);
982
983 overflow.truncate(bin_limit + overflow_len);
986
987 mem::swap(bins, &mut overflow);
988}
989
990#[allow(clippy::cast_possible_truncation)]
991fn generate_bins(bins: &mut Vec<Bin>, k: i16, n: u32) {
992 if n < u32::from(MAX_BIN_WIDTH) {
993 bins.push(Bin { k, n: n as u16 });
995 } else {
996 let overflow = n % u32::from(MAX_BIN_WIDTH);
997 if overflow != 0 {
998 bins.push(Bin {
999 k,
1000 n: overflow as u16,
1002 });
1003 }
1004
1005 for _ in 0..(n / u32::from(MAX_BIN_WIDTH)) {
1006 bins.push(Bin {
1007 k,
1008 n: MAX_BIN_WIDTH,
1009 });
1010 }
1011 }
1012}
1013
1014#[allow(clippy::cast_possible_truncation)]
1015#[inline]
1016fn capped_u64_shift(shift: u64) -> u32 {
1017 if shift >= 64 {
1018 u32::MAX
1019 } else {
1020 shift as u32
1022 }
1023}
1024
1025fn round_to_even(v: f64) -> f64 {
1026 const MASK: u64 = 0x7ff;
1081 const BIAS: u64 = 1023;
1082 const SHIFT: u64 = 64 - 11 - 1;
1083 const SIGN_MASK: u64 = 1 << 63;
1084 const FRAC_MASK: u64 = (1 << SHIFT) - 1;
1085 #[allow(clippy::unreadable_literal)]
1086 const UV_ONE: u64 = 0x3FF0000000000000;
1087 const HALF_MINUS_ULP: u64 = (1 << (SHIFT - 1)) - 1;
1088
1089 let mut bits = v.to_bits();
1090 let mut e = (bits >> SHIFT) & MASK;
1091 if e >= BIAS {
1092 e = e.wrapping_sub(BIAS);
1093 let shift_amount = SHIFT.wrapping_sub(e);
1094 let shifted = bits.wrapping_shr(capped_u64_shift(shift_amount));
1095 let plus_ulp = HALF_MINUS_ULP + (shifted & 1);
1096 bits += plus_ulp.wrapping_shr(capped_u64_shift(e));
1097 bits &= !(FRAC_MASK.wrapping_shr(capped_u64_shift(e)));
1098 } else if e == BIAS - 1 && bits & FRAC_MASK != 0 {
1099 bits = bits & SIGN_MASK | UV_ONE; } else {
1102 bits &= SIGN_MASK; }
1105 f64::from_bits(bits)
1106}
1107
1108#[cfg(test)]
1109mod tests {
1110 use super::{AGENT_DEFAULT_EPS, AgentDDSketch, Config, MAX_KEY, round_to_even};
1111 use crate::event::metric::Bucket;
1112
1113 const FLOATING_POINT_ACCEPTABLE_ERROR: f64 = 1.0e-10;
1114
1115 #[cfg(ddsketch_extended)]
1116 fn generate_pareto_distribution() -> Vec<OrderedFloat<f64>> {
1117 use ordered_float::OrderedFloat;
1118 use rand::rng;
1119 use rand_distr::{Distribution, Pareto};
1120
1121 let distribution = Pareto::new(1.0, 1.0).expect("pareto distribution should be valid");
1127 let mut samples = distribution
1128 .sample_iter(rng())
1129 .map(|n| n * 10_000.0)
1131 .filter(|n| *n > 15_000.0 && *n < 10_000_000.0)
1132 .map(OrderedFloat)
1133 .take(1000)
1134 .collect::<Vec<_>>();
1135
1136 samples.sort();
1138
1139 samples
1140 }
1141
1142 #[test]
1143 fn test_ddsketch_config_key_lower_bound_identity() {
1144 let config = Config::default();
1145 for k in (-MAX_KEY + 1)..MAX_KEY {
1146 assert_eq!(k, config.key(config.bin_lower_bound(k)));
1147 }
1148 }
1149
1150 #[test]
1151 fn test_ddsketch_basic() {
1152 let mut sketch = AgentDDSketch::with_agent_defaults();
1153 assert!(sketch.is_empty());
1154 assert_eq!(sketch.count(), 0);
1155 assert_eq!(sketch.min(), None);
1156 assert_eq!(sketch.max(), None);
1157 assert_eq!(sketch.sum(), None);
1158 assert_eq!(sketch.avg(), None);
1159
1160 sketch.insert(3.15);
1161 assert!(!sketch.is_empty());
1162 assert_eq!(sketch.count(), 1);
1163 assert_eq!(sketch.min(), Some(3.15));
1164 assert_eq!(sketch.max(), Some(3.15));
1165 assert_eq!(sketch.sum(), Some(3.15));
1166 assert_eq!(sketch.avg(), Some(3.15));
1167
1168 sketch.insert(2.28);
1169 assert!(!sketch.is_empty());
1170 assert_eq!(sketch.count(), 2);
1171 assert_eq!(sketch.min(), Some(2.28));
1172 assert_eq!(sketch.max(), Some(3.15));
1173 assert_eq!(sketch.sum(), Some(5.43));
1174 assert_eq!(sketch.avg(), Some(2.715));
1175 }
1176
1177 #[test]
1178 fn test_ddsketch_clear() {
1179 let sketch1 = AgentDDSketch::with_agent_defaults();
1180 let mut sketch2 = AgentDDSketch::with_agent_defaults();
1181
1182 assert_eq!(sketch1, sketch2);
1183 assert!(sketch1.is_empty());
1184 assert!(sketch2.is_empty());
1185
1186 sketch2.insert(3.15);
1187 assert_ne!(sketch1, sketch2);
1188 assert!(!sketch2.is_empty());
1189
1190 sketch2.clear();
1191 assert_eq!(sketch1, sketch2);
1192 assert!(sketch2.is_empty());
1193 }
1194
1195 #[test]
1196 fn test_ddsketch_neg_to_pos() {
1197 let start = -1.0;
1199 let end = 1.0;
1200 let delta = 0.0002;
1201
1202 let mut sketch = AgentDDSketch::with_agent_defaults();
1203
1204 let mut v = start;
1205 while v <= end {
1206 sketch.insert(v);
1207
1208 v += delta;
1209 }
1210
1211 let min = sketch.quantile(0.0).expect("should have value");
1212 let median = sketch.quantile(0.5).expect("should have value");
1213 let max = sketch.quantile(1.0).expect("should have value");
1214
1215 assert_eq!(start, min);
1216 assert!(median.abs() < FLOATING_POINT_ACCEPTABLE_ERROR);
1217 assert!((end - max).abs() < FLOATING_POINT_ACCEPTABLE_ERROR);
1218 }
1219
1220 #[test]
1221 fn test_out_of_range_buckets_error() {
1222 let mut sketch = AgentDDSketch::with_agent_defaults();
1223
1224 let buckets = vec![
1225 Bucket {
1226 upper_limit: 5.4,
1227 count: 32,
1228 },
1229 Bucket {
1230 upper_limit: 5.8,
1231 count: u64::from(u32::MAX) + 1,
1232 },
1233 Bucket {
1234 upper_limit: 9.2,
1235 count: 320,
1236 },
1237 ];
1238
1239 assert_eq!(
1240 Err("bucket size greater than u32::MAX"),
1241 sketch.insert_interpolate_buckets(buckets)
1242 );
1243
1244 assert_eq!(sketch, AgentDDSketch::with_agent_defaults());
1246 }
1247
1248 #[test]
1249 fn test_merge() {
1250 let mut all_values = AgentDDSketch::with_agent_defaults();
1251 let mut odd_values = AgentDDSketch::with_agent_defaults();
1252 let mut even_values = AgentDDSketch::with_agent_defaults();
1253 let mut all_values_many = AgentDDSketch::with_agent_defaults();
1254
1255 let mut values = Vec::new();
1256 for i in -50..=50 {
1257 let v = f64::from(i);
1258
1259 all_values.insert(v);
1260
1261 if i & 1 == 0 {
1262 odd_values.insert(v);
1263 } else {
1264 even_values.insert(v);
1265 }
1266
1267 values.push(v);
1268 }
1269
1270 all_values_many.insert_many(&values);
1271
1272 assert!(odd_values.merge(&even_values).is_ok());
1273 let merged_values = odd_values;
1274
1275 assert_eq!(all_values.bin_count(), values.len());
1277
1278 let low_end = all_values
1280 .quantile(0.01)
1281 .expect("should have estimated value");
1282 let high_end = all_values
1283 .quantile(0.99)
1284 .expect("should have estimated value");
1285 assert_eq!(high_end, -low_end);
1286
1287 let target_bin_count = all_values.bin_count();
1288 for sketch in &[all_values, all_values_many, merged_values] {
1289 assert_eq!(sketch.quantile(0.5), Some(0.0));
1290 assert_eq!(sketch.quantile(0.0), Some(-50.0));
1291 assert_eq!(sketch.quantile(1.0), Some(50.0));
1292
1293 for p in 0..50 {
1294 let q = f64::from(p) / 100.0;
1295 let positive = sketch
1296 .quantile(q + 0.5)
1297 .expect("should have estimated value");
1298 let negative = -sketch
1299 .quantile(0.5 - q)
1300 .expect("should have estimated value");
1301
1302 assert!(
1303 (positive - negative).abs() <= 1.0e-6,
1304 "positive vs negative difference too great ({positive} vs {negative})",
1305 );
1306 }
1307
1308 assert_eq!(target_bin_count, sketch.bin_count());
1309 }
1310 }
1311
1312 #[test]
1313 fn test_merge_different_configs() {
1314 let mut first = AgentDDSketch::with_agent_defaults();
1315 let mut second = AgentDDSketch::with_agent_defaults();
1316
1317 second.config.norm_bias += 1;
1319
1320 assert!(first.merge(&second).is_err());
1321 }
1322
1323 #[test]
1324 #[cfg(ddsketch_extended)]
1325 fn test_ddsketch_pareto_distribution() {
1326 use ndarray::{Array, Axis};
1327 use ndarray_stats::{QuantileExt, interpolate::Midpoint};
1328 use noisy_float::prelude::N64;
1329
1330 let samples = generate_pareto_distribution();
1342
1343 let mut sketch = AgentDDSketch::with_agent_defaults();
1345
1346 let relative_accuracy = AGENT_DEFAULT_EPS;
1347 for sample in &samples {
1348 sketch.insert(sample.into_inner());
1349 }
1350
1351 let mut array = Array::from_iter(samples);
1352
1353 for p in 1..=100 {
1358 let q = p as f64 / 100.0;
1359 let x = sketch.quantile(q);
1360 assert!(x.is_some());
1361
1362 let estimated = x.unwrap();
1363 let actual = array
1364 .quantile_axis_mut(Axis(0), N64::unchecked_new(q), &Midpoint)
1365 .expect("quantile should be in range")
1366 .get(())
1367 .expect("quantile value should be present")
1368 .clone()
1369 .into_inner();
1370
1371 let _err = (estimated - actual).abs() / actual;
1372 assert!(
1373 err <= relative_accuracy,
1374 "relative accuracy out of bounds: q={}, estimate={}, actual={}, target-rel-acc={}, actual-rel-acc={}, bin-count={}",
1375 q,
1376 estimated,
1377 actual,
1378 relative_accuracy,
1379 err,
1380 sketch.bin_count()
1381 );
1382 }
1383 }
1384
1385 #[test]
1386 fn test_relative_accuracy_fast() {
1387 let config = Config::default();
1396 let min_value = 1.0;
1397 #[allow(clippy::cast_possible_truncation)]
1399 let max_value = config.gamma_v.powf(5.0) as f32;
1400
1401 test_relative_accuracy(config, AGENT_DEFAULT_EPS, min_value, max_value);
1402 }
1403
1404 #[test]
1405 #[cfg(ddsketch_extended)]
1406 fn test_relative_accuracy_slow() {
1407 let config = Config::default();
1419 let min_value = 1.0e-6;
1420 let max_value = i64::MAX as f32;
1421
1422 test_relative_accuracy(config, AGENT_DEFAULT_EPS, min_value, max_value)
1423 }
1424
1425 fn parse_sketch_from_string_bins(layout: &str) -> AgentDDSketch {
1426 layout
1427 .split(' ')
1428 .map(|pair| pair.split(':').map(ToOwned::to_owned).collect::<Vec<_>>())
1429 .fold(
1430 AgentDDSketch::with_agent_defaults(),
1431 |mut sketch, mut kn| {
1432 let k = kn.remove(0).parse::<i16>().unwrap();
1433 let n = kn.remove(0).parse::<u16>().unwrap();
1434
1435 sketch.insert_raw_bin(k, n);
1436 sketch
1437 },
1438 )
1439 }
1440
1441 fn compare_sketches(actual: &AgentDDSketch, expected: &AgentDDSketch, allowed_err: f64) {
1442 let actual_sum = actual.sum().unwrap();
1443 let expected_sum = expected.sum().unwrap();
1444 let actual_avg = actual.avg().unwrap();
1445 let expected_avg = expected.avg().unwrap();
1446 let sum_delta = (actual_sum - expected_sum).abs();
1447 let avg_delta = (actual_avg - expected_avg).abs();
1448 assert!(sum_delta <= allowed_err);
1449 assert!(avg_delta <= allowed_err);
1450 assert_eq!(actual.min(), expected.min());
1451 assert_eq!(actual.max(), expected.max());
1452 assert_eq!(actual.count(), expected.count());
1453 assert_eq!(actual.bins(), expected.bins());
1454 }
1455
1456 #[test]
1457 fn test_histogram_interpolation_agent_similarity() {
1458 #[derive(Clone)]
1459 struct Case {
1460 lower: f64,
1461 upper: f64,
1462 count: u32,
1463 allowed_err: f64,
1464 expected: &'static str,
1465 }
1466
1467 let check_result = |actual: &AgentDDSketch, case: &Case| {
1468 let expected = parse_sketch_from_string_bins(case.expected);
1469
1470 assert_eq!(expected.count(), case.count);
1471 assert_eq!(actual.count(), case.count);
1472 assert_eq!(actual.bins(), expected.bins());
1473 compare_sketches(actual, &expected, case.allowed_err);
1474
1475 let actual_count: u32 = actual.bins.iter().map(|b| u32::from(b.n)).sum();
1476 assert_eq!(actual_count, case.count);
1477 };
1478
1479 let cases = &[
1480 Case {
1481 lower: 0.0,
1482 upper: 10.0,
1483 count: 2,
1484 allowed_err: 0.0,
1485 expected: "0:1 1442:1",
1486 },
1487 Case {
1488 lower: 10.0,
1489 upper: 20.0,
1490 count: 4,
1491 allowed_err: 0.0,
1492 expected: "1487:1 1502:1 1514:1 1524:1",
1493 },
1494 Case {
1495 lower: -10.0,
1496 upper: 10.0,
1497 count: 4,
1498 allowed_err: 0.0,
1499 expected: "-1487:1 -1442:1 -1067:1 1442:1",
1500 },
1501 Case {
1502 lower: 0.0,
1503 upper: 10.0,
1504 count: 100,
1505 allowed_err: 0.0,
1506 expected: "0:1 1190:1 1235:1 1261:1 1280:1 1295:1 1307:1 1317:1 1326:1 1334:1 1341:1 1347:1 1353:1 1358:1 1363:1 1368:1 1372:2 1376:1 1380:1 1384:1 1388:1 1391:1 1394:1 1397:2 1400:1 1403:1 1406:2 1409:1 1412:1 1415:2 1417:1 1419:1 1421:1 1423:1 1425:1 1427:1 1429:2 1431:1 1433:1 1435:2 1437:1 1439:2 1441:1 1443:2 1445:2 1447:1 1449:2 1451:2 1453:2 1455:2 1457:2 1459:1 1460:1 1461:1 1462:1 1463:1 1464:1 1465:1 1466:1 1467:2 1468:1 1469:1 1470:1 1471:1 1472:2 1473:1 1474:1 1475:1 1476:2 1477:1 1478:2 1479:1 1480:1 1481:2 1482:1 1483:2 1484:1 1485:2 1486:1",
1507 },
1508 Case {
1509 lower: 1_000.0,
1510 upper: 100_000.0,
1511 count: 1_000_000 - 1,
1512 allowed_err: 0.0,
1513 expected: "1784:158 1785:162 1786:164 1787:166 1788:170 1789:171 1790:175 1791:177 1792:180 1793:183 1794:185 1795:189 1796:191 1797:195 1798:197 1799:201 1800:203 1801:207 1802:210 1803:214 1804:217 1805:220 1806:223 1807:227 1808:231 1809:234 1810:238 1811:242 1812:245 1813:249 1814:253 1815:257 1816:261 1817:265 1818:270 1819:273 1820:278 1821:282 1822:287 1823:291 1824:295 1825:300 1826:305 1827:310 1828:314 1829:320 1830:324 1831:329 1832:335 1833:340 1834:345 1835:350 1836:356 1837:362 1838:367 1839:373 1840:379 1841:384 1842:391 1843:397 1844:403 1845:409 1846:416 1847:422 1848:429 1849:435 1850:442 1851:449 1852:457 1853:463 1854:470 1855:478 1856:486 1857:493 1858:500 1859:509 1860:516 1861:525 1862:532 1863:541 1864:550 1865:558 1866:567 1867:575 1868:585 1869:594 1870:603 1871:612 1872:622 1873:632 1874:642 1875:651 1876:662 1877:672 1878:683 1879:693 1880:704 1881:716 1882:726 1883:738 1884:749 1885:761 1886:773 1887:785 1888:797 1889:809 1890:823 1891:835 1892:848 1893:861 1894:875 1895:889 1896:902 1897:917 1898:931 1899:945 1900:960 1901:975 1902:991 1903:1006 1904:1021 1905:1038 1906:1053 1907:1071 1908:1087 1909:1104 1910:1121 1911:1138 1912:1157 1913:1175 1914:1192 1915:1212 1916:1231 1917:1249 1918:1269 1919:1290 1920:1309 1921:1329 1922:1351 1923:1371 1924:1393 1925:1415 1926:1437 1927:1459 1928:1482 1929:1506 1930:1529 1931:1552 1932:1577 1933:1602 1934:1626 1935:1652 1936:1678 1937:1704 1938:1731 1939:1758 1940:1785 1941:1813 1942:1841 1943:1870 1944:1900 1945:1929 1946:1959 1947:1990 1948:2021 1949:2052 1950:2085 1951:2117 1952:2150 1953:2184 1954:2218 1955:2253 1956:2287 1957:2324 1958:2360 1959:2396 1960:2435 1961:2472 1962:2511 1963:2550 1964:2589 1965:2631 1966:2671 1967:2714 1968:2755 1969:2799 1970:2842 1971:2887 1972:2932 1973:2978 1974:3024 1975:3071 1976:3120 1977:3168 1978:3218 1979:3268 1980:3319 1981:3371 1982:3423 1983:3477 1984:3532 1985:3586 1986:3643 1987:3700 1988:3757 1989:3816 1990:3876 1991:3936 1992:3998 1993:4060 1994:4124 1995:4188 1996:4253 1997:4320 1998:4388 1999:4456 2000:4526 2001:4596 2002:4668 2003:4741 2004:4816 2005:4890 2006:4967 2007:5044 2008:5124 2009:5203 2010:5285 2011:5367 2012:5451 2013:5536 2014:5623 2015:5711 2016:5800 2017:5890 2018:5983 2019:6076 2020:6171 2021:6267 2022:6365 2023:6465 2024:6566 2025:6668 2026:6773 2027:6878 2028:6986 2029:7095 2030:7206 2031:7318 2032:7433 2033:7549 2034:7667 2035:7786 2036:7909 2037:8032 2038:8157 2039:8285 2040:8414 2041:8546 2042:8679 2043:8815 2044:8953 2045:9092 2046:9235 2047:9379 2048:9525 2049:9675 2050:9825 2051:9979 2052:10135 2053:10293 2054:10454 2055:10618 2056:10783 2057:10952 2058:11123 2059:11297 2060:11473 2061:11653 2062:11834 2063:12020 2064:12207 2065:12398 2066:12592 2067:12788 2068:12989 2069:13191 2070:13397 2071:13607 2072:13819 2073:14036 2074:14254 2075:14478 2076:14703 2077:14933 2078:15167 2079:15403 2080:8942",
1514 },
1515 Case {
1516 lower: 1_000.0,
1517 upper: 10_000.0,
1518 count: 10_000_000 - 1,
1519 allowed_err: 0.00001,
1520 expected: "1784:17485 1785:17758 1786:18035 1787:18318 1788:18604 1789:18894 1790:19190 1791:19489 1792:19794 1793:20103 1794:20418 1795:20736 1796:21061 1797:21389 1798:21724 1799:22063 1800:22408 1801:22758 1802:23113 1803:23475 1804:23841 1805:24215 1806:24592 1807:24977 1808:25366 1809:25764 1810:26165 1811:26575 1812:26990 1813:27412 1814:27839 1815:28275 1816:28717 1817:29165 1818:29622 1819:30083 1820:30554 1821:31032 1822:31516 1823:32009 1824:32509 1825:33016 1826:33533 1827:34057 1828:34589 1829:35129 1830:35678 1831:36235 1832:36802 1833:37377 1834:37961 1835:38554 1836:39156 1837:39768 1838:40390 1839:41020 1840:41662 1841:42312 1842:42974 1843:43645 1844:44327 1845:45020 1846:45723 1847:46438 1848:47163 1849:47900 1850:48648 1851:49409 1852:50181 1853:50964 1854:51761 1855:52570 1856:53391 1857:54226 1858:55072 1859:55934 1860:56807 1861:57695 1862:58596 1863:59512 1864:60441 1865:61387 1866:62345 1867:63319 1868:64309 1869:65314 1870:799 1870:65535 1871:1835 1871:65535 1872:2889 1872:65535 1873:3957 1873:65535 1874:5043 1874:65535 1875:6146 1875:65535 1876:7266 1876:65535 1877:8404 1877:65535 1878:9559 1878:65535 1879:10732 1879:65535 1880:11923 1880:65535 1881:13135 1881:65535 1882:14363 1882:65535 1883:15612 1883:65535 1884:16879 1884:65535 1885:18168 1885:65535 1886:19475 1886:65535 1887:20803 1887:65535 1888:22153 1888:65535 1889:23523 1889:65535 1890:24914 1890:65535 1891:26327 1891:65535 1892:27763 1892:65535 1893:29221 1893:65535 1894:30701 1894:65535 1895:32205 1895:65535 1896:33732 1896:65535 1897:35283 1897:65535 1898:36858 1898:65535 1899:38458 1899:65535 1900:40084 1900:65535 1901:41733 1901:65535 1902:43409 1902:65535 1903:45112 1903:65535 1904:46841 1904:65535 1905:48596 1905:65535 1906:50380 1906:65535 1907:52191 1907:65535 1908:54030 1908:65535 1909:55899 1909:65535 1910:57796 1910:65535 1911:59723 1911:65535 1912:61680 1912:65535 1913:63668 1913:65535 1914:152 1914:65535 1914:65535 1915:2202 1915:65535 1915:65535 1916:4285 1916:65535 1916:65535 1917:6399 1917:65535 1917:65535 1918:8547 1918:65535 1918:65535 1919:10729 1919:65535 1919:65535 1920:12945 1920:65535 1920:65535 1921:15195 1921:65535 1921:65535 1922:17480 1922:65535 1922:65535 1923:19801 1923:65535 1923:65535 1924:22158 1924:65535 1924:65535 1925:24553 1925:65535 1925:65535 1926:26985 1926:65535 1926:65535 1927:29453 1927:65535 1927:65535 1928:31963 1928:65535 1928:65535 1929:34509 1929:65535 1929:65535 1930:37097 1930:65535 1930:65535 1931:39724 1931:65535 1931:65535 1932:17411",
1521 },
1522 ];
1523
1524 let double_insert_cases = &[Case {
1525 lower: 1_000.0,
1526 upper: 10_000.0,
1527 count: 10_000_000 - 1,
1528 allowed_err: 0.0002,
1529 expected: "1784:34970 1785:35516 1786:36070 1787:36636 1788:37208 1789:37788 1790:38380 1791:38978 1792:39588 1793:40206 1794:40836 1795:41472 1796:42122 1797:42778 1798:43448 1799:44126 1800:44816 1801:45516 1802:46226 1803:46950 1804:47682 1805:48430 1806:49184 1807:49954 1808:50732 1809:51528 1810:52330 1811:53150 1812:53980 1813:54824 1814:55678 1815:56550 1816:57434 1817:58330 1818:59244 1819:60166 1820:61108 1821:62064 1822:63032 1823:64018 1824:65018 1825:497 1825:65535 1826:1531 1826:65535 1827:2579 1827:65535 1828:3643 1828:65535 1829:4723 1829:65535 1830:5821 1830:65535 1831:6935 1831:65535 1832:8069 1832:65535 1833:9219 1833:65535 1834:10387 1834:65535 1835:11573 1835:65535 1836:12777 1836:65535 1837:14001 1837:65535 1838:15245 1838:65535 1839:16505 1839:65535 1840:17789 1840:65535 1841:19089 1841:65535 1842:20413 1842:65535 1843:21755 1843:65535 1844:23119 1844:65535 1845:24505 1845:65535 1846:25911 1846:65535 1847:27341 1847:65535 1848:28791 1848:65535 1849:30265 1849:65535 1850:31761 1850:65535 1851:33283 1851:65535 1852:34827 1852:65535 1853:36393 1853:65535 1854:37987 1854:65535 1855:39605 1855:65535 1856:41247 1856:65535 1857:42917 1857:65535 1858:44609 1858:65535 1859:46333 1859:65535 1860:48079 1860:65535 1861:49855 1861:65535 1862:51657 1862:65535 1863:53489 1863:65535 1864:55347 1864:65535 1865:57239 1865:65535 1866:59155 1866:65535 1867:61103 1867:65535 1868:63083 1868:65535 1869:65093 1869:65535 1870:1598 1870:65535 1870:65535 1871:3670 1871:65535 1871:65535 1872:5778 1872:65535 1872:65535 1873:7914 1873:65535 1873:65535 1874:10086 1874:65535 1874:65535 1875:12292 1875:65535 1875:65535 1876:14532 1876:65535 1876:65535 1877:16808 1877:65535 1877:65535 1878:19118 1878:65535 1878:65535 1879:21464 1879:65535 1879:65535 1880:23846 1880:65535 1880:65535 1881:26270 1881:65535 1881:65535 1882:28726 1882:65535 1882:65535 1883:31224 1883:65535 1883:65535 1884:33758 1884:65535 1884:65535 1885:36336 1885:65535 1885:65535 1886:38950 1886:65535 1886:65535 1887:41606 1887:65535 1887:65535 1888:44306 1888:65535 1888:65535 1889:47046 1889:65535 1889:65535 1890:49828 1890:65535 1890:65535 1891:52654 1891:65535 1891:65535 1892:55526 1892:65535 1892:65535 1893:58442 1893:65535 1893:65535 1894:61402 1894:65535 1894:65535 1895:64410 1895:65535 1895:65535 1896:1929 1896:65535 1896:65535 1896:65535 1897:5031 1897:65535 1897:65535 1897:65535 1898:8181 1898:65535 1898:65535 1898:65535 1899:11381 1899:65535 1899:65535 1899:65535 1900:14633 1900:65535 1900:65535 1900:65535 1901:17931 1901:65535 1901:65535 1901:65535 1902:21283 1902:65535 1902:65535 1902:65535 1903:24689 1903:65535 1903:65535 1903:65535 1904:28147 1904:65535 1904:65535 1904:65535 1905:31657 1905:65535 1905:65535 1905:65535 1906:35225 1906:65535 1906:65535 1906:65535 1907:38847 1907:65535 1907:65535 1907:65535 1908:42525 1908:65535 1908:65535 1908:65535 1909:46263 1909:65535 1909:65535 1909:65535 1910:50057 1910:65535 1910:65535 1910:65535 1911:53911 1911:65535 1911:65535 1911:65535 1912:57825 1912:65535 1912:65535 1912:65535 1913:61801 1913:65535 1913:65535 1913:65535 1914:304 1914:65535 1914:65535 1914:65535 1914:65535 1915:4404 1915:65535 1915:65535 1915:65535 1915:65535 1916:8570 1916:65535 1916:65535 1916:65535 1916:65535 1917:12798 1917:65535 1917:65535 1917:65535 1917:65535 1918:17094 1918:65535 1918:65535 1918:65535 1918:65535 1919:21458 1919:65535 1919:65535 1919:65535 1919:65535 1920:25890 1920:65535 1920:65535 1920:65535 1920:65535 1921:30390 1921:65535 1921:65535 1921:65535 1921:65535 1922:34960 1922:65535 1922:65535 1922:65535 1922:65535 1923:39602 1923:65535 1923:65535 1923:65535 1923:65535 1924:44316 1924:65535 1924:65535 1924:65535 1924:65535 1925:49106 1925:65535 1925:65535 1925:65535 1925:65535 1926:53970 1926:65535 1926:65535 1926:65535 1926:65535 1927:58906 1927:65535 1927:65535 1927:65535 1927:65535 1928:63926 1928:65535 1928:65535 1928:65535 1928:65535 1929:3483 1929:65535 1929:65535 1929:65535 1929:65535 1929:65535 1930:8659 1930:65535 1930:65535 1930:65535 1930:65535 1930:65535 1931:13913 1931:65535 1931:65535 1931:65535 1931:65535 1931:65535 1932:34822",
1530 }];
1531
1532 for case in cases {
1533 let mut sketch = AgentDDSketch::with_agent_defaults();
1534 assert!(sketch.is_empty());
1535
1536 sketch.insert_interpolate_bucket(case.lower, case.upper, case.count);
1537 check_result(&sketch, case);
1538 }
1539
1540 for case in double_insert_cases {
1541 let mut sketch = AgentDDSketch::with_agent_defaults();
1542 assert!(sketch.is_empty());
1543
1544 sketch.insert_interpolate_bucket(case.lower, case.upper, case.count);
1545 sketch.insert_interpolate_bucket(case.lower, case.upper, case.count);
1546
1547 let mut case = case.clone();
1548 case.count *= 2;
1549 check_result(&sketch, &case);
1550 }
1551 }
1552
1553 fn test_relative_accuracy(config: Config, rel_acc: f64, min_value: f32, max_value: f32) {
1554 let max_observed_rel_acc = check_max_relative_accuracy(config, min_value, max_value);
1555 assert!(
1556 max_observed_rel_acc <= rel_acc + FLOATING_POINT_ACCEPTABLE_ERROR,
1557 "observed out of bound max relative acc: {max_observed_rel_acc}, target rel acc={rel_acc}",
1558 );
1559 }
1560
1561 fn compute_relative_accuracy(target: f64, actual: f64) -> f64 {
1562 assert!(
1563 !(target < 0.0 || actual < 0.0),
1564 "expected/actual values must be greater than 0.0; target={target}, actual={actual}",
1565 );
1566
1567 if target == actual {
1568 0.0
1569 } else if target == 0.0 {
1570 if actual == 0.0 { 0.0 } else { f64::INFINITY }
1571 } else if actual < target {
1572 (target - actual) / target
1573 } else {
1574 (actual - target) / target
1575 }
1576 }
1577
1578 fn check_max_relative_accuracy(config: Config, min_value: f32, max_value: f32) -> f64 {
1579 assert!(
1580 min_value < max_value,
1581 "min_value must be less than max_value"
1582 );
1583
1584 let mut v = min_value;
1585 let mut max_relative_acc = 0.0;
1586 while v < max_value {
1587 let target = f64::from(v);
1588 let actual = config.bin_lower_bound(config.key(target));
1589
1590 let relative_acc = compute_relative_accuracy(target, actual);
1591 if relative_acc > max_relative_acc {
1592 max_relative_acc = relative_acc;
1593 }
1594
1595 v = f32::from_bits(v.to_bits() + 1);
1596 }
1597
1598 let actual = config.bin_lower_bound(config.key(f64::from(max_value)));
1600 let relative_acc = compute_relative_accuracy(f64::from(max_value), actual);
1601 if relative_acc > max_relative_acc {
1602 max_relative_acc = relative_acc;
1603 }
1604
1605 max_relative_acc
1606 }
1607
1608 #[test]
1609 fn test_round_to_even() {
1610 let alike = |a: f64, b: f64| -> bool {
1611 if a.is_nan() && b.is_nan() {
1612 true
1613 } else if a == b {
1614 a.is_sign_positive() == b.is_sign_positive()
1615 } else {
1616 false
1617 }
1618 };
1619
1620 #[allow(clippy::unreadable_literal)]
1621 let test_cases = &[
1622 (f64::NAN, f64::NAN),
1623 (0.5000000000000001, 1.0), (0.0, 0.0),
1625 (1.390671161567e-309, 0.0), (0.49999999999999994, 0.0), (0.5, 0.0),
1628 (-1.5, -2.0),
1629 (-2.5, -2.0),
1630 (f64::INFINITY, f64::INFINITY),
1631 (2251799813685249.5, 2251799813685250.0), (2251799813685250.5, 2251799813685250.0),
1633 (4503599627370495.5, 4503599627370496.0), (4503599627370497.0, 4503599627370497.0), ];
1636
1637 for (input, expected) in test_cases {
1638 let actual = round_to_even(*input);
1639 assert!(
1640 alike(actual, *expected),
1641 "input -> {}, expected {}, got {}",
1642 *input,
1643 *expected,
1644 actual
1645 );
1646 }
1647 }
1648}