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]

Reply via email to