This is an automated email from the ASF dual-hosted git repository. tisonkun pushed a commit to branch codex/kll-api-and-performance in repository https://gitbox.apache.org/repos/asf/datasketches-rust.git
commit 1610ba7ed8ce79861935f70296afeac7505e5741 Author: tison <[email protected]> AuthorDate: Wed Sep 2 12:11:54 2026 +0800 refactor(kll): align quantile query APIs --- datasketches/src/common/mod.rs | 2 + .../src/common/{mod.rs => search_criteria.rs} | 18 +-- datasketches/src/kll/mod.rs | 5 +- datasketches/src/kll/sketch.rs | 66 +++++--- datasketches/src/kll/sorted_view.rs | 51 +++--- datasketches/src/req/mod.rs | 11 +- tests-integration/tests/kll_test/sketch.rs | 177 ++++++++++++++------- 7 files changed, 214 insertions(+), 116 deletions(-) diff --git a/datasketches/src/common/mod.rs b/datasketches/src/common/mod.rs index 6d4c6c6..918c57b 100644 --- a/datasketches/src/common/mod.rs +++ b/datasketches/src/common/mod.rs @@ -19,8 +19,10 @@ mod num_std_dev; mod resize; +mod search_criteria; pub use self::num_std_dev::NumStdDev; pub use self::resize::ResizeFactor; +pub use self::search_criteria::SearchCriteria; #[cfg(any(feature = "cpc", feature = "hll"))] pub(crate) mod inv_pow2; diff --git a/datasketches/src/common/mod.rs b/datasketches/src/common/search_criteria.rs similarity index 68% copy from datasketches/src/common/mod.rs copy to datasketches/src/common/search_criteria.rs index 6d4c6c6..7f480c7 100644 --- a/datasketches/src/common/mod.rs +++ b/datasketches/src/common/search_criteria.rs @@ -15,12 +15,12 @@ // specific language governing permissions and limitations // under the License. -//! Data structures and functions that may be used across all the sketch families. - -mod num_std_dev; -mod resize; -pub use self::num_std_dev::NumStdDev; -pub use self::resize::ResizeFactor; - -#[cfg(any(feature = "cpc", feature = "hll"))] -pub(crate) mod inv_pow2; +/// Selects the rank definition used by rank, quantile, PMF, and CDF queries. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +pub enum SearchCriteria { + /// Define rank as the fraction of values less than or equal to the boundary. + #[default] + Inclusive, + /// Define rank as the fraction of values strictly less than the boundary. + Exclusive, +} diff --git a/datasketches/src/kll/mod.rs b/datasketches/src/kll/mod.rs index cf7679d..8b8e563 100644 --- a/datasketches/src/kll/mod.rs +++ b/datasketches/src/kll/mod.rs @@ -28,11 +28,11 @@ //! # Usage //! //! ```rust -//! # use datasketches::kll::KllSketch; +//! # use datasketches::kll::{KllSketch, SearchCriteria}; //! let mut sketch = KllSketch::<f64>::new(200).unwrap(); //! sketch.update(1.0); //! sketch.update(2.0); -//! let q = sketch.quantile(0.5, true).unwrap(); +//! let q = sketch.quantile(0.5, SearchCriteria::Inclusive).unwrap(); //! assert!(q >= 1.0 && q <= 2.0); //! ``` @@ -47,6 +47,7 @@ pub use self::order::KllComparator; pub use self::order::NaturalOrder; pub use self::sketch::KllSketch; pub use self::value::KllValue; +pub use crate::common::SearchCriteria; /// Default value of parameter k. const DEFAULT_K: u16 = 200; diff --git a/datasketches/src/kll/sketch.rs b/datasketches/src/kll/sketch.rs index 8153bd0..ae9045f 100644 --- a/datasketches/src/kll/sketch.rs +++ b/datasketches/src/kll/sketch.rs @@ -43,6 +43,7 @@ use crate::codec::SketchSlice; use crate::codec::assert::ensure_serial_version_is; use crate::codec::assert::insufficient_data; use crate::codec::family::Family; +use crate::common::SearchCriteria; use crate::error::Error; /// KLL sketch for estimating quantiles and ranks. @@ -217,49 +218,78 @@ impl<T: Clone, C: KllComparator<T>> KllSketch<T, C> { } /// Returns the normalized rank of the given item. - pub fn rank(&self, item: &T, inclusive: bool) -> Option<f64> { + /// + /// # Errors + /// + /// Returns an error if the sketch is empty or `item` is outside the comparator's ordered + /// domain. + pub fn rank(&self, item: &T, criteria: SearchCriteria) -> Result<f64, Error> { if self.is_empty() { - return None; + return Err(Error::invalid_argument("cannot query an empty sketch")); + } + if !self.comparator.accepts(item) { + return Err(Error::invalid_argument( + "item must belong to the comparator's ordered domain", + )); } let view = build_sorted_view(&self.levels, self.comparator.clone()); - Some(view.rank(item, inclusive)) + Ok(view.rank(item, criteria)) } /// Returns the quantile for the given normalized rank. /// - /// # Panics + /// # Errors /// - /// Panics if rank is not in [0.0, 1.0]. - pub fn quantile(&self, rank: f64, inclusive: bool) -> Option<T> { + /// Returns an error if the sketch is empty or `rank` is outside `[0.0, 1.0]`. + pub fn quantile(&self, rank: f64, criteria: SearchCriteria) -> Result<T, Error> { if self.is_empty() { - return None; + return Err(Error::invalid_argument("cannot query an empty sketch")); + } + if !(0.0..=1.0).contains(&rank) { + return Err(Error::invalid_argument(format!( + "rank must be in [0.0, 1.0], got {rank}" + ))); } - assert!((0.0..=1.0).contains(&rank), "rank must be in [0.0, 1.0]"); let view = build_sorted_view(&self.levels, self.comparator.clone()); - Some(view.quantile(rank, inclusive)) + Ok(view.quantile(rank, criteria)) } /// Returns the approximate CDF for the given split points. - pub fn cdf(&self, split_points: &[T], inclusive: bool) -> Option<Vec<f64>> { + /// + /// # Errors + /// + /// Returns an error if the sketch is empty, a split point is outside the comparator's ordered + /// domain, or the split points are not unique and strictly increasing. + pub fn cdf(&self, split_points: &[T], criteria: SearchCriteria) -> Result<Vec<f64>, Error> { if self.is_empty() { - return None; + return Err(Error::invalid_argument("cannot query an empty sketch")); } let view = build_sorted_view(&self.levels, self.comparator.clone()); - Some(view.cdf(split_points, inclusive)) + view.cdf(split_points, criteria) } /// Returns the approximate PMF for the given split points. - pub fn pmf(&self, split_points: &[T], inclusive: bool) -> Option<Vec<f64>> { + /// + /// # Errors + /// + /// Returns an error if the sketch is empty, a split point is outside the comparator's ordered + /// domain, or the split points are not unique and strictly increasing. + pub fn pmf(&self, split_points: &[T], criteria: SearchCriteria) -> Result<Vec<f64>, Error> { if self.is_empty() { - return None; + return Err(Error::invalid_argument("cannot query an empty sketch")); } let view = build_sorted_view(&self.levels, self.comparator.clone()); - Some(view.pmf(split_points, inclusive)) + view.pmf(split_points, criteria) + } + + /// Returns the normalized single-sided rank error for the configured k. + pub fn normalized_rank_error(&self) -> f64 { + normalized_rank_error(self.min_k, false) } - /// Returns normalized rank error for the configured k. - pub fn normalized_rank_error(&self, pmf: bool) -> f64 { - normalized_rank_error(self.min_k, pmf) + /// Returns the normalized double-sided rank error for PMF queries for the configured k. + pub fn normalized_pmf_error(&self) -> f64 { + normalized_rank_error(self.min_k, true) } } diff --git a/datasketches/src/kll/sorted_view.rs b/datasketches/src/kll/sorted_view.rs index 4ab8838..5c8cd5b 100644 --- a/datasketches/src/kll/sorted_view.rs +++ b/datasketches/src/kll/sorted_view.rs @@ -18,6 +18,8 @@ use std::cmp::Ordering; use super::order::KllComparator; +use crate::common::SearchCriteria; +use crate::error::Error; #[derive(Debug, Clone)] pub struct SortedView<T: Clone, C: KllComparator<T>> { @@ -47,12 +49,12 @@ impl<T: Clone, C: KllComparator<T>> SortedView<T, C> { } } - pub fn rank(&self, item: &T, inclusive: bool) -> f64 { + pub fn rank(&self, item: &T, criteria: SearchCriteria) -> f64 { if self.entries.is_empty() { return 0.0; } - let idx = if inclusive { + let idx = if criteria == SearchCriteria::Inclusive { upper_bound(&self.entries, item, &self.comparator) } else { lower_bound(&self.entries, item, &self.comparator) @@ -65,14 +67,14 @@ impl<T: Clone, C: KllComparator<T>> SortedView<T, C> { weight as f64 / self.total_weight as f64 } - pub fn quantile(&self, rank: f64, inclusive: bool) -> T { - let weight = if inclusive { + pub fn quantile(&self, rank: f64, criteria: SearchCriteria) -> T { + let weight = if criteria == SearchCriteria::Inclusive { (rank * self.total_weight as f64).ceil() as u64 } else { (rank * self.total_weight as f64) as u64 }; - let idx = if inclusive { + let idx = if criteria == SearchCriteria::Inclusive { lower_bound_by_weight(&self.entries, weight) } else { upper_bound_by_weight(&self.entries, weight) @@ -84,22 +86,22 @@ impl<T: Clone, C: KllComparator<T>> SortedView<T, C> { self.entries[idx].item.clone() } - pub fn cdf(&self, split_points: &[T], inclusive: bool) -> Vec<f64> { - check_split_points(split_points, &self.comparator); + pub fn cdf(&self, split_points: &[T], criteria: SearchCriteria) -> Result<Vec<f64>, Error> { + check_split_points(split_points, &self.comparator)?; let mut ranks = Vec::with_capacity(split_points.len() + 1); for item in split_points { - ranks.push(self.rank(item, inclusive)); + ranks.push(self.rank(item, criteria)); } ranks.push(1.0); - ranks + Ok(ranks) } - pub fn pmf(&self, split_points: &[T], inclusive: bool) -> Vec<f64> { - let mut buckets = self.cdf(split_points, inclusive); + pub fn pmf(&self, split_points: &[T], criteria: SearchCriteria) -> Result<Vec<f64>, Error> { + let mut buckets = self.cdf(split_points, criteria)?; for i in (1..buckets.len()).rev() { buckets[i] -= buckets[i - 1]; } - buckets + Ok(buckets) } } @@ -123,18 +125,23 @@ pub fn build_sorted_view<T: Clone, C: KllComparator<T>>( SortedView::new(entries, comparator) } -#[track_caller] -fn check_split_points<T, C: KllComparator<T>>(split_points: &[T], comparator: &C) { - assert!( - split_points.iter().all(|point| comparator.accepts(point)), - "split_points must belong to the comparator's ordered domain" - ); +fn check_split_points<T, C: KllComparator<T>>( + split_points: &[T], + comparator: &C, +) -> Result<(), Error> { + if !split_points.iter().all(|point| comparator.accepts(point)) { + return Err(Error::invalid_argument( + "split points must belong to the comparator's ordered domain", + )); + } for pair in split_points.windows(2) { - assert!( - comparator.compare(&pair[0], &pair[1]) == Ordering::Less, - "split_points must be unique and monotonically increasing" - ); + if comparator.compare(&pair[0], &pair[1]) != Ordering::Less { + return Err(Error::invalid_argument( + "split points must be unique and monotonically increasing", + )); + } } + Ok(()) } fn lower_bound<T, C: KllComparator<T>>(entries: &[Entry<T>], item: &T, comparator: &C) -> usize { diff --git a/datasketches/src/req/mod.rs b/datasketches/src/req/mod.rs index 8714f5e..41d8f40 100644 --- a/datasketches/src/req/mod.rs +++ b/datasketches/src/req/mod.rs @@ -61,6 +61,7 @@ pub use self::sketch::ReqSketch; pub use self::sorted_view::SortedView; pub use self::value::ReqFloat; pub use self::value::ReqValue; +pub use crate::common::SearchCriteria; /// Default value of `k` if not specified. Roughly 1% relative error at 95% confidence. const DEFAULT_K: u16 = 12; @@ -79,16 +80,6 @@ pub enum RankAccuracy { LowRank, } -/// Selects the rank definition used by rank, quantile, PMF, and CDF queries. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] -pub enum SearchCriteria { - /// Define rank as the fraction of values less than or equal to the boundary. - #[default] - Inclusive, - /// Define rank as the fraction of values strictly less than the boundary. - Exclusive, -} - /// Number of sections in a newly created compactor. The section count and size /// determine its capacity and compaction range; the count doubles as its state grows. const INITIAL_SECTIONS_PER_COMPACTOR: u8 = 3; diff --git a/tests-integration/tests/kll_test/sketch.rs b/tests-integration/tests/kll_test/sketch.rs index 7fcca7e..afc5090 100644 --- a/tests-integration/tests/kll_test/sketch.rs +++ b/tests-integration/tests/kll_test/sketch.rs @@ -20,6 +20,7 @@ use std::cmp::Ordering; use datasketches::error::ErrorKind; use datasketches::kll::KllComparator; use datasketches::kll::KllSketch; +use datasketches::kll::SearchCriteria; const DEFAULT_K: u16 = 200; const MIN_K: u16 = 8; @@ -35,7 +36,7 @@ fn assert_approx_eq(actual: f64, expected: f64, tolerance: f64) { } fn rank_eps(sketch: &KllSketch<f32>) -> f64 { - sketch.normalized_rank_error(false) + sketch.normalized_rank_error() } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -74,18 +75,20 @@ fn test_empty() { assert_eq!(sketch.num_retained(), 0); assert!(sketch.min_item().is_none()); assert!(sketch.max_item().is_none()); - assert!(sketch.rank(&0.0, true).is_none()); - assert!(sketch.quantile(0.5, true).is_none()); - assert!(sketch.pmf(&[0.0f32], true).is_none()); - assert!(sketch.cdf(&[0.0f32], true).is_none()); + assert!(sketch.rank(&0.0, SearchCriteria::Inclusive).is_err()); + assert!(sketch.quantile(0.5, SearchCriteria::Inclusive).is_err()); + assert!(sketch.pmf(&[0.0f32], SearchCriteria::Inclusive).is_err()); + assert!(sketch.cdf(&[0.0f32], SearchCriteria::Inclusive).is_err()); } #[test] -#[should_panic(expected = "rank must be in [0.0, 1.0]")] -fn test_quantile_out_of_range_panics() { +fn test_quantile_out_of_range_returns_error() { let mut sketch = KllSketch::<f32>::new(DEFAULT_K).unwrap(); sketch.update(0.0); - sketch.quantile(-1.0, true); + let error = sketch + .quantile(-1.0, SearchCriteria::Inclusive) + .unwrap_err(); + assert_eq!(error.kind(), ErrorKind::InvalidArgument); } #[test] @@ -96,12 +99,15 @@ fn test_one_item() { assert!(!sketch.is_estimation_mode()); assert_eq!(sketch.n(), 1); assert_eq!(sketch.num_retained(), 1); - assert_eq!(sketch.rank(&1.0, false), Some(0.0)); - assert_eq!(sketch.rank(&1.0, true), Some(1.0)); - assert_eq!(sketch.rank(&2.0, false), Some(1.0)); + assert_eq!(sketch.rank(&1.0, SearchCriteria::Exclusive).unwrap(), 0.0); + assert_eq!(sketch.rank(&1.0, SearchCriteria::Inclusive).unwrap(), 1.0); + assert_eq!(sketch.rank(&2.0, SearchCriteria::Exclusive).unwrap(), 1.0); assert_eq!(sketch.min_item().cloned(), Some(1.0)); assert_eq!(sketch.max_item().cloned(), Some(1.0)); - assert_eq!(sketch.quantile(0.5, true), Some(1.0)); + assert_eq!( + sketch.quantile(0.5, SearchCriteria::Inclusive).unwrap(), + 1.0 + ); } #[test] @@ -111,12 +117,18 @@ fn test_duplicate_items_follow_inclusive_and_exclusive_semantics() { sketch.update(item); } - assert_eq!(sketch.rank(&1.0, false), Some(0.0)); - assert_eq!(sketch.rank(&1.0, true), Some(0.5)); - assert_eq!(sketch.rank(&2.0, false), Some(0.5)); - assert_eq!(sketch.rank(&2.0, true), Some(1.0)); - assert_eq!(sketch.quantile(0.5, true), Some(1.0)); - assert_eq!(sketch.quantile(0.5, false), Some(2.0)); + assert_eq!(sketch.rank(&1.0, SearchCriteria::Exclusive).unwrap(), 0.0); + assert_eq!(sketch.rank(&1.0, SearchCriteria::Inclusive).unwrap(), 0.5); + assert_eq!(sketch.rank(&2.0, SearchCriteria::Exclusive).unwrap(), 0.5); + assert_eq!(sketch.rank(&2.0, SearchCriteria::Inclusive).unwrap(), 1.0); + assert_eq!( + sketch.quantile(0.5, SearchCriteria::Inclusive).unwrap(), + 1.0 + ); + assert_eq!( + sketch.quantile(0.5, SearchCriteria::Exclusive).unwrap(), + 2.0 + ); } #[test] @@ -141,15 +153,27 @@ fn test_many_items_exact_mode() { assert!(!sketch.is_estimation_mode()); assert_eq!(sketch.num_retained(), n); assert_eq!(sketch.min_item().cloned(), Some(1.0)); - assert_eq!(sketch.quantile(0.0, true), Some(1.0)); + assert_eq!( + sketch.quantile(0.0, SearchCriteria::Inclusive).unwrap(), + 1.0 + ); assert_eq!(sketch.max_item().cloned(), Some(n as f32)); - assert_eq!(sketch.quantile(1.0, true), Some(n as f32)); + assert_eq!( + sketch.quantile(1.0, SearchCriteria::Inclusive).unwrap(), + n as f32 + ); for i in 1..=n { let inclusive_rank = i as f64 / n as f64; - assert_eq!(sketch.rank(&(i as f32), true), Some(inclusive_rank)); + assert_eq!( + sketch.rank(&(i as f32), SearchCriteria::Inclusive).unwrap(), + inclusive_rank + ); let exclusive_rank = (i - 1) as f64 / n as f64; - assert_eq!(sketch.rank(&(i as f32), false), Some(exclusive_rank)); + assert_eq!( + sketch.rank(&(i as f32), SearchCriteria::Exclusive).unwrap(), + exclusive_rank + ); } } @@ -159,10 +183,22 @@ fn test_ten_items_quantiles() { for i in 1..=10 { sketch.update(i as f32); } - assert_eq!(sketch.quantile(0.0, true), Some(1.0)); - assert_eq!(sketch.quantile(0.5, true), Some(5.0)); - assert_eq!(sketch.quantile(0.99, true), Some(10.0)); - assert_eq!(sketch.quantile(1.0, true), Some(10.0)); + assert_eq!( + sketch.quantile(0.0, SearchCriteria::Inclusive).unwrap(), + 1.0 + ); + assert_eq!( + sketch.quantile(0.5, SearchCriteria::Inclusive).unwrap(), + 5.0 + ); + assert_eq!( + sketch.quantile(0.99, SearchCriteria::Inclusive).unwrap(), + 10.0 + ); + assert_eq!( + sketch.quantile(1.0, SearchCriteria::Inclusive).unwrap(), + 10.0 + ); } #[test] @@ -171,11 +207,26 @@ fn test_hundred_items_quantiles() { for i in 0..100 { sketch.update(i as f32); } - assert_eq!(sketch.quantile(0.0, true), Some(0.0)); - assert_eq!(sketch.quantile(0.01, true), Some(0.0)); - assert_eq!(sketch.quantile(0.5, true), Some(49.0)); - assert_eq!(sketch.quantile(0.99, true), Some(98.0)); - assert_eq!(sketch.quantile(1.0, true), Some(99.0)); + assert_eq!( + sketch.quantile(0.0, SearchCriteria::Inclusive).unwrap(), + 0.0 + ); + assert_eq!( + sketch.quantile(0.01, SearchCriteria::Inclusive).unwrap(), + 0.0 + ); + assert_eq!( + sketch.quantile(0.5, SearchCriteria::Inclusive).unwrap(), + 49.0 + ); + assert_eq!( + sketch.quantile(0.99, SearchCriteria::Inclusive).unwrap(), + 98.0 + ); + assert_eq!( + sketch.quantile(1.0, SearchCriteria::Inclusive).unwrap(), + 99.0 + ); } #[test] @@ -193,7 +244,7 @@ fn test_many_items_estimation_mode_rank_error() { let rank_eps = rank_eps(&sketch); for i in (0..n).step_by(10) { let true_rank = i as f64 / n as f64; - let rank = sketch.rank(&(i as f32), false).unwrap(); + let rank = sketch.rank(&(i as f32), SearchCriteria::Exclusive).unwrap(); assert_approx_eq(rank, true_rank, rank_eps); } @@ -210,12 +261,12 @@ fn test_rank_cdf_pmf_consistency() { values.push(i as f32); } - let ranks = sketch.cdf(&values, false).unwrap(); - let pmf = sketch.pmf(&values, false).unwrap(); + let ranks = sketch.cdf(&values, SearchCriteria::Exclusive).unwrap(); + let pmf = sketch.pmf(&values, SearchCriteria::Exclusive).unwrap(); let mut subtotal = 0.0; for i in 0..n { - let rank = sketch.rank(&values[i], false).unwrap(); + let rank = sketch.rank(&values[i], SearchCriteria::Exclusive).unwrap(); assert_eq!(rank, ranks[i]); subtotal += pmf[i]; assert!( @@ -224,12 +275,12 @@ fn test_rank_cdf_pmf_consistency() { ); } - let ranks = sketch.cdf(&values, true).unwrap(); - let pmf = sketch.pmf(&values, true).unwrap(); + let ranks = sketch.cdf(&values, SearchCriteria::Inclusive).unwrap(); + let pmf = sketch.pmf(&values, SearchCriteria::Inclusive).unwrap(); let mut subtotal = 0.0; for i in 0..n { - let rank = sketch.rank(&values[i], true).unwrap(); + let rank = sketch.rank(&values[i], SearchCriteria::Inclusive).unwrap(); assert_eq!(rank, ranks[i]); subtotal += pmf[i]; assert!( @@ -240,21 +291,25 @@ fn test_rank_cdf_pmf_consistency() { } #[test] -#[should_panic(expected = "split_points must be unique and monotonically increasing")] -fn test_out_of_order_split_points_panics() { +fn test_out_of_order_split_points_return_error() { let mut sketch = KllSketch::<f32>::new(DEFAULT_K).unwrap(); sketch.update(0.0); let split_points = [1.0, 0.0]; - let _ = sketch.cdf(&split_points, true); + let error = sketch + .cdf(&split_points, SearchCriteria::Inclusive) + .unwrap_err(); + assert_eq!(error.kind(), ErrorKind::InvalidArgument); } #[test] -#[should_panic(expected = "split_points must belong to the comparator's ordered domain")] -fn test_nan_split_point_panics() { +fn test_nan_split_point_returns_error() { let mut sketch = KllSketch::<f32>::new(DEFAULT_K).unwrap(); sketch.update(0.0); let split_points = [f32::NAN]; - let _ = sketch.cdf(&split_points, true); + let error = sketch + .cdf(&split_points, SearchCriteria::Inclusive) + .unwrap_err(); + assert_eq!(error.kind(), ErrorKind::InvalidArgument); } #[test] @@ -278,7 +333,7 @@ fn test_merge() { assert_eq!(sketch1.n(), (2 * n) as u64); assert_eq!(sketch1.min_item().cloned(), Some(0.0)); assert_eq!(sketch1.max_item().cloned(), Some((2 * n - 1) as f32)); - let median = sketch1.quantile(0.5, true).unwrap(); + let median = sketch1.quantile(0.5, SearchCriteria::Inclusive).unwrap(); let rank_eps = rank_eps(&sketch1); assert_approx_eq(median as f64, n as f64, n as f64 * rank_eps); } @@ -299,14 +354,14 @@ fn test_merge_lower_k() { assert_eq!(sketch1.min_item().cloned(), Some(0.0)); assert_eq!(sketch1.max_item().cloned(), Some((2 * n - 1) as f32)); assert_eq!( - sketch1.normalized_rank_error(false), - sketch2.normalized_rank_error(false) + sketch1.normalized_rank_error(), + sketch2.normalized_rank_error() ); assert_eq!( - sketch1.normalized_rank_error(true), - sketch2.normalized_rank_error(true) + sketch1.normalized_pmf_error(), + sketch2.normalized_pmf_error() ); - let median = sketch1.quantile(0.5, true).unwrap(); + let median = sketch1.quantile(0.5, SearchCriteria::Inclusive).unwrap(); let rank_eps = rank_eps(&sketch1); assert_approx_eq(median as f64, n as f64, n as f64 * rank_eps); } @@ -320,14 +375,14 @@ fn test_merge_exact_mode_lower_k() { sketch1.update(i as f32); } - let err_before = sketch1.normalized_rank_error(true); + let err_before = sketch1.normalized_pmf_error(); sketch1.merge(&sketch2); - assert_eq!(sketch1.normalized_rank_error(true), err_before); + assert_eq!(sketch1.normalized_pmf_error(), err_before); assert_eq!(sketch1.n(), n as u64); assert_eq!(sketch1.min_item().cloned(), Some(0.0)); assert_eq!(sketch1.max_item().cloned(), Some((n - 1) as f32)); - let median = sketch1.quantile(0.5, true).unwrap(); + let median = sketch1.quantile(0.5, SearchCriteria::Inclusive).unwrap(); let rank_eps = rank_eps(&sketch1); assert_approx_eq(median as f64, (n / 2) as f64, (n as f64 / 2.0) * rank_eps); } @@ -386,7 +441,13 @@ fn test_custom_comparator_roundtrip() { assert_eq!(sketch.min_item().map(String::as_str), Some("1")); assert_eq!(sketch.max_item().map(String::as_str), Some("10")); - assert_eq!(sketch.quantile(0.5, true).as_deref(), Some("2")); + assert_eq!( + sketch + .quantile(0.5, SearchCriteria::Inclusive) + .as_deref() + .unwrap(), + "2" + ); let bytes = sketch.serialize(); let decoded = KllSketch::<String, NumericStringOrder>::deserialize_with_comparator( @@ -397,5 +458,11 @@ fn test_custom_comparator_roundtrip() { assert_eq!(decoded.n(), sketch.n()); assert_eq!(decoded.min_item().map(String::as_str), Some("1")); assert_eq!(decoded.max_item().map(String::as_str), Some("10")); - assert_eq!(decoded.quantile(0.5, true).as_deref(), Some("2")); + assert_eq!( + decoded + .quantile(0.5, SearchCriteria::Inclusive) + .as_deref() + .unwrap(), + "2" + ); } --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
