From 6fb7cda8f9e9a8f73ce1fa51c1e893dce8f04b8d Mon Sep 17 00:00:00 2001 From: tison Date: Tue, 11 Aug 2026 10:55:29 +0800 Subject: [PATCH 1/2] docs: standardize public API documentation style --- CONTRIBUTING.md | 7 + datasketches/src/bloom/builder.rs | 28 ++-- datasketches/src/bloom/sketch.rs | 52 ++++--- datasketches/src/codec/encode.rs | 4 +- datasketches/src/common/num_std_dev.rs | 12 +- datasketches/src/common/resize.rs | 27 ++-- datasketches/src/countmin/sketch.rs | 36 ++--- datasketches/src/cpc/sketch.rs | 16 +-- datasketches/src/cpc/union.rs | 10 +- datasketches/src/cpc/wrapper.rs | 6 +- datasketches/src/error.rs | 18 +-- datasketches/src/frequencies/mod.rs | 16 +-- datasketches/src/frequencies/sketch.rs | 22 +-- .../src/hash/value/canonical_float.rs | 4 +- datasketches/src/hash/value/mod.rs | 8 +- datasketches/src/hash/value/natural_extend.rs | 12 +- datasketches/src/hash/value/raw_bytes.rs | 8 +- datasketches/src/hash/value/sign_extend.rs | 12 +- datasketches/src/hll/mod.rs | 18 +-- datasketches/src/hll/sketch.rs | 52 ++++--- datasketches/src/hll/union.rs | 58 ++++---- datasketches/src/tdigest/mod.rs | 6 +- datasketches/src/tdigest/sketch.rs | 128 +++++++++--------- datasketches/src/thetafamily/theta/a_not_b.rs | 2 +- .../src/thetafamily/theta/hash_table.rs | 6 +- datasketches/src/thetafamily/theta/mod.rs | 6 +- datasketches/src/thetafamily/theta/sketch.rs | 84 ++++++------ datasketches/src/thetafamily/theta/union.rs | 32 ++--- datasketches/src/thetafamily/tuple/a_not_b.rs | 2 +- .../src/thetafamily/tuple/hash_table.rs | 12 +- .../src/thetafamily/tuple/intersection.rs | 2 +- datasketches/src/thetafamily/tuple/mod.rs | 8 +- datasketches/src/thetafamily/tuple/sketch.rs | 38 +++--- datasketches/src/thetafamily/tuple/union.rs | 10 +- 34 files changed, 377 insertions(+), 385 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4942e6ee..9b3e17df 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -60,6 +60,13 @@ Lint: cargo x lint ``` +## Public API documentation + +- Describe types with noun phrases and API behavior with third-person present-tense verbs such as `Creates`, `Updates`, and `Returns`. +- End summary sentences with punctuation, and format Rust identifiers, literals, and numeric ranges as inline code. +- Use `lg_k` for the algorithm parameter in prose. Use a different name only when referring to an exact Rust identifier or an external serialization format. +- When applicable, order sections as `# Examples`, `# Errors`, and `# Panics`, followed by compatibility notes. Include only sections that describe an actual contract. + ## Integration test layout Integration tests for the `datasketches` crate live under `datasketches/tests` and use two entry-point patterns. diff --git a/datasketches/src/bloom/builder.rs b/datasketches/src/bloom/builder.rs index 67f62b2d..c52aa464 100644 --- a/datasketches/src/bloom/builder.rs +++ b/datasketches/src/bloom/builder.rs @@ -52,12 +52,8 @@ impl BloomFilterBuilder { /// /// # Arguments /// - /// * `max_items`: Maximum expected number of distinct items - /// * `fpp`: Target false positive probability (e.g., 0.01 for 1%) - /// - /// # Panics - /// - /// Panics if `max_items` is 0 or `fpp` is not in (0.0, 1.0]. + /// * `max_items`: Maximum expected number of distinct items. + /// * `fpp`: Target false positive probability (for example, `0.01` for `1%`). /// /// # Examples /// @@ -69,6 +65,10 @@ impl BloomFilterBuilder { /// .seed(42) /// .build(); /// ``` + /// + /// # Panics + /// + /// Panics if `max_items` is `0` or `fpp` is outside `(0.0, 1.0]`. pub fn with_accuracy(max_items: u64, fpp: f64) -> Self { assert!(max_items > 0, "max_items must be greater than 0"); assert!( @@ -96,14 +96,8 @@ impl BloomFilterBuilder { /// /// # Arguments /// - /// * `num_bits`: Total number of bits in the filter - /// * `num_hashes`: Number of hash functions to use - /// - /// # Panics - /// - /// Panics if any of: - /// * `num_bits` < [`Self::MIN_NUM_BITS`] or `num_bits` > [`Self::MAX_NUM_BITS`] - /// * `num_hashes` < [`Self::MIN_NUM_HASHES`] or `num_hashes` > [`Self::MAX_NUM_HASHES`] + /// * `num_bits`: Total number of bits in the filter. + /// * `num_hashes`: Number of hash functions to use. /// /// # Examples /// @@ -112,6 +106,12 @@ impl BloomFilterBuilder { /// /// let filter = BloomFilterBuilder::with_size(10_000, 7).build(); /// ``` + /// + /// # Panics + /// + /// Panics if any of: + /// * `num_bits < Self::MIN_NUM_BITS` or `num_bits > Self::MAX_NUM_BITS`. + /// * `num_hashes < Self::MIN_NUM_HASHES` or `num_hashes > Self::MAX_NUM_HASHES`. pub fn with_size(num_bits: u64, num_hashes: u16) -> Self { assert!( (Self::MIN_NUM_BITS..=Self::MAX_NUM_BITS).contains(&num_bits), diff --git a/datasketches/src/bloom/sketch.rs b/datasketches/src/bloom/sketch.rs index 8b0543f7..6e4c1c1e 100644 --- a/datasketches/src/bloom/sketch.rs +++ b/datasketches/src/bloom/sketch.rs @@ -52,11 +52,10 @@ pub struct BloomFilter { } impl BloomFilter { - /// Tests whether an item is possibly in the set. + /// Returns `true` if an item is possibly in the set. /// - /// Returns: - /// * `true`: Item was **possibly** inserted (or false positive) - /// * `false`: Item was **definitely not** inserted + /// A `false` result means the item was definitely not inserted; a `true` result may be a false + /// positive. /// /// # Examples /// @@ -78,9 +77,8 @@ impl BloomFilter { self.check_bits(h0, h1) } - /// Tests and inserts an item in a single operation. + /// Returns `true` if an item was possibly present before inserting it. /// - /// Returns whether the item was possibly already in the set before insertion. /// This is more efficient than calling `contains()` then `insert()` separately. /// /// # Examples @@ -152,11 +150,6 @@ impl BloomFilter { /// After merging, this filter will recognize items from either filter /// (plus any false positives from either). /// - /// # Panics - /// - /// Panics if the filters are not compatible (different size, hashes, or seed). - /// Use [`is_compatible()`](Self::is_compatible) to check first. - /// /// # Examples /// /// ``` @@ -176,6 +169,11 @@ impl BloomFilter { /// assert!(f1.contains(&"a")); /// assert!(f1.contains(&"b")); /// ``` + /// + /// # Panics + /// + /// Panics if the filters are not compatible (different size, hashes, or seed). + /// Use [`is_compatible()`](Self::is_compatible) to check first. pub fn union(&mut self, other: &BloomFilter) { assert!( self.is_compatible(other), @@ -196,10 +194,6 @@ impl BloomFilter { /// After intersection, this filter will recognize only items present in both /// filters (plus false positives). /// - /// # Panics - /// - /// Panics if the filters are not compatible (different size, hashes, or seed). - /// /// # Examples /// /// ``` @@ -221,6 +215,10 @@ impl BloomFilter { /// assert!(f1.contains(&"b")); // In both /// // "a" and "c" likely return false now /// ``` + /// + /// # Panics + /// + /// Panics if the filters are not compatible (different size, hashes, or seed). pub fn intersect(&mut self, other: &BloomFilter) { assert!( self.is_compatible(other), @@ -294,7 +292,7 @@ impl BloomFilter { self.num_bits_set as f64 / self.capacity() as f64 } - /// Estimates the current false positive probability. + /// Returns the estimated current false positive probability. /// /// Uses the approximation: `load_factor^k` /// where: @@ -312,7 +310,7 @@ impl BloomFilter { load.powf(k) } - /// Checks if two filters are compatible for merging. + /// Returns `true` if two filters are compatible for merging. /// /// Filters are compatible if they have the same: /// * Capacity (number of bits) @@ -326,8 +324,6 @@ impl BloomFilter { /// Serializes the filter to a byte vector. /// - /// The format is compatible with other Apache DataSketches implementations. - /// /// # Examples /// /// ``` @@ -341,6 +337,8 @@ impl BloomFilter { /// let restored = BloomFilter::deserialize(&bytes).unwrap(); /// assert!(restored.contains(&"test")); /// ``` + /// + /// The format is compatible with other Apache DataSketches implementations. pub fn serialize(&self) -> Vec { let is_empty = self.is_empty(); let preamble_longs = if is_empty { @@ -386,13 +384,6 @@ impl BloomFilter { /// Deserializes a filter from bytes. /// - /// # Errors - /// - /// Returns an error if: - /// * The data is truncated or corrupted - /// * The family ID doesn't match (not a Bloom filter) - /// * The serial version is unsupported - /// /// # Examples /// /// ``` @@ -405,6 +396,13 @@ impl BloomFilter { /// let restored = BloomFilter::deserialize(&bytes).unwrap(); /// assert_eq!(original, restored); /// ``` + /// + /// # Errors + /// + /// Returns an error if: + /// * The data is truncated or corrupted. + /// * The family ID does not identify a Bloom filter. + /// * The serial version is unsupported. pub fn deserialize(bytes: &[u8]) -> Result { let mut cursor = SketchSlice::new(bytes); @@ -572,7 +570,7 @@ impl BloomFilter { } } - /// Returns the estimated size of the filter in bytes + /// Returns the estimated size of the filter in bytes. pub fn estimated_size(&self) -> usize { size_of::() + self.bit_array.len() * size_of::() } diff --git a/datasketches/src/codec/encode.rs b/datasketches/src/codec/encode.rs index 60e85c8f..20ac0eae 100644 --- a/datasketches/src/codec/encode.rs +++ b/datasketches/src/codec/encode.rs @@ -21,14 +21,14 @@ pub struct SketchBytes { } impl SketchBytes { - /// Constructs an empty `SketchBytes` with at least the specified capacity. + /// Creates an empty `SketchBytes` with at least the specified capacity. pub fn with_capacity(capacity: usize) -> Self { Self { bytes: Vec::with_capacity(capacity), } } - /// Consumes the `SketchBytes` and returns the underlying `Vec`. + /// Returns the underlying `Vec`, consuming the `SketchBytes`. pub fn into_bytes(self) -> Vec { self.bytes } diff --git a/datasketches/src/common/num_std_dev.rs b/datasketches/src/common/num_std_dev.rs index 441a07bf..2aa908f8 100644 --- a/datasketches/src/common/num_std_dev.rs +++ b/datasketches/src/common/num_std_dev.rs @@ -28,7 +28,7 @@ static DELTA_OF_NUM_STD_DEVS: [f64; 4] = [ 0.0013498126861731796, // = 0.5 (1 + erf((-3/sqrt(2)))) ]; -/// Number of standard deviations for confidence bounds +/// Number of standard deviations for confidence bounds. /// /// This enum specifies the number of standard deviations to use when computing /// upper and lower bounds for cardinality estimates. Higher values provide wider @@ -37,21 +37,21 @@ static DELTA_OF_NUM_STD_DEVS: [f64; 4] = [ #[repr(u8)] #[derive(Debug, Clone, Copy, PartialEq)] pub enum NumStdDev { - /// One standard deviation (\~68% confidence interval) + /// One standard deviation (\~68% confidence interval). One = 1, - /// Two standard deviations (\~95% confidence interval) + /// Two standard deviations (\~95% confidence interval). Two = 2, - /// Three standard deviations (\~99.7% confidence interval) + /// Three standard deviations (\~99.7% confidence interval). Three = 3, } impl NumStdDev { - /// Returns the tail probability (delta) for this confidence level + /// Returns the tail probability (delta) for this confidence level. pub const fn tail_probability(&self) -> f64 { DELTA_OF_NUM_STD_DEVS[*self as usize] } - /// Returns the number of standard deviations as an `u8`. + /// Returns the number of standard deviations as a `u8`. pub const fn as_u8(&self) -> u8 { *self as u8 } diff --git a/datasketches/src/common/resize.rs b/datasketches/src/common/resize.rs index 5fa4742d..60e41f73 100644 --- a/datasketches/src/common/resize.rs +++ b/datasketches/src/common/resize.rs @@ -15,23 +15,22 @@ // specific language governing permissions and limitations // under the License. -/// For the Families that accept this configuration parameter, it controls the size multiple that -/// affects how fast the internal cache grows, when more space is required. +/// Controls internal cache growth for sketch families that support resizing. /// -/// For Theta Sketches, the Resize Factor is a dynamic, speed performance vs. memory size tradeoff. -/// The sketches created on-heap and configured with a Resize Factor of > X1 start out with an -/// internal hash table size that is the smallest submultiple of the target Nominal Entries +/// For Theta sketches, the resize factor provides a dynamic trade-off between update speed and +/// memory use. Sketches configured with a resize factor greater than `X1` start with an internal +/// hash table size that is the smallest submultiple of the target nominal entries /// and larger than the minimum required hash table size for that sketch. /// -/// When the sketch needs to be resized larger, then the Resize Factor is used as a multiplier of +/// When the sketch needs to grow, the resize factor is used as a multiplier for /// the current sketch cache array size. /// -/// "X1" means no resizing is allowed and the sketch will be initialized at full size. +/// `X1` means no resizing is allowed and the sketch will be initialized at full size. /// -/// "X2" means the internal cache will start very small and double in size until the target size is +/// `X2` means the internal cache will start very small and double in size until the target size is /// reached. /// -/// Similarly, "X4" is a factor of 4 and "X8" is a factor of 8. +/// Similarly, `X4` is a factor of `4` and `X8` is a factor of `8`. /// /// # Examples /// @@ -44,18 +43,18 @@ /// ``` #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ResizeFactor { - /// Do not resize. Sketch will be configured to full size. + /// Does not resize; configures the sketch at full size. X1, - /// Resize by factor of 2 + /// Resizes by a factor of `2`. X2, - /// Resize by factor of 4 + /// Resizes by a factor of `4`. X4, - /// Resize by factor of 8 + /// Resizes by a factor of `8`. X8, } impl ResizeFactor { - /// Returns the Log-base 2 of the Resize Factor + /// Returns the base-2 logarithm of the resize factor. pub fn lg_value(self) -> u8 { match self { ResizeFactor::X1 => 0, diff --git a/datasketches/src/countmin/sketch.rs b/datasketches/src/countmin/sketch.rs index 8c382843..1e354c78 100644 --- a/datasketches/src/countmin/sketch.rs +++ b/datasketches/src/countmin/sketch.rs @@ -57,11 +57,6 @@ pub struct CountMinSketch { impl CountMinSketch { /// Creates a new CountMin sketch with the default seed. /// - /// # Panics - /// - /// Panics if `num_hashes` is 0, `num_buckets` is less than 3, or the - /// total table size exceeds the supported limit. - /// /// # Examples /// /// ``` @@ -70,20 +65,17 @@ impl CountMinSketch { /// let sketch = CountMinSketch::::new(4, 128); /// assert_eq!(sketch.num_buckets(), 128); /// ``` + /// + /// # Panics + /// + /// Panics if `num_hashes` is `0`, `num_buckets` is less than `3`, or the + /// total table size exceeds the supported limit. pub fn new(num_hashes: u8, num_buckets: u32) -> Self { Self::with_seed(num_hashes, num_buckets, DEFAULT_UPDATE_SEED) } /// Creates a new CountMin sketch with the provided seed. /// - /// # Panics - /// - /// Panics if any of: - /// * `num_hashes` is 0 - /// * `num_buckets` is less than 3 - /// * the total table size exceeds the supported limit - /// * the computed seed hash is zero - /// /// # Examples /// /// ``` @@ -92,6 +84,14 @@ impl CountMinSketch { /// let sketch = CountMinSketch::::with_seed(4, 64, 42); /// assert_eq!(sketch.seed(), 42); /// ``` + /// + /// # Panics + /// + /// Panics if any of: + /// * `num_hashes` is `0`. + /// * `num_buckets` is less than `3`. + /// * The total table size exceeds the supported limit. + /// * The computed seed hash is zero. pub fn with_seed(num_hashes: u8, num_buckets: u32, seed: u64) -> Self { let entries = entries_for_config(num_hashes, num_buckets); Self::make(num_hashes, num_buckets, seed, entries) @@ -122,7 +122,7 @@ impl CountMinSketch { std::f64::consts::E / self.num_buckets as f64 } - /// Returns true if the sketch has not seen any updates. + /// Returns `true` if the sketch has not seen any updates. pub fn is_empty(&self) -> bool { self.total_weight == T::ZERO } @@ -233,10 +233,6 @@ impl CountMinSketch { /// Merges another sketch into this one. /// - /// # Panics - /// - /// Panics if the sketches have incompatible configurations. - /// /// # Examples /// /// ``` @@ -251,6 +247,10 @@ impl CountMinSketch { /// left.merge(&right); /// assert!(left.estimate("banana") >= 2); /// ``` + /// + /// # Panics + /// + /// Panics if the sketches have incompatible configurations. pub fn merge(&mut self, other: &CountMinSketch) { if std::ptr::eq(self, other) { panic!("Cannot merge a sketch with itself."); diff --git a/datasketches/src/cpc/sketch.rs b/datasketches/src/cpc/sketch.rs index c90b875d..622b6a15 100644 --- a/datasketches/src/cpc/sketch.rs +++ b/datasketches/src/cpc/sketch.rs @@ -128,7 +128,7 @@ impl CpcSketch { } } - /// Return the parameter lg_k. + /// Returns the configured `lg_k`. pub fn lg_k(&self) -> u8 { self.lg_k } @@ -165,12 +165,12 @@ impl CpcSketch { ) } - /// Returns true if the sketch is empty. + /// Returns `true` if the sketch is empty. pub fn is_empty(&self) -> bool { self.num_coupons == 0 } - /// Update the sketch with a hashable value. + /// Updates the sketch with a hashable value. /// /// You may use [`hash::value`](crate::hash::value) wrappers when another DataSketches /// implementation requires a specific value hashing strategy. @@ -452,7 +452,7 @@ impl CpcSketch { matrix } - /// Returns the estimated size of the sketch in bytes + /// Returns the estimated size of the sketch in bytes. pub fn estimated_size(&self) -> usize { let heap_size = self.sliding_window.capacity() + self @@ -466,7 +466,7 @@ impl CpcSketch { } impl CpcSketch { - /// Serializes this CpcSketch to bytes. + /// Serializes this `CpcSketch` to bytes. pub fn serialize(&self) -> Vec { let mut bytes = SketchBytes::with_capacity(256); @@ -526,12 +526,12 @@ impl CpcSketch { bytes.into_bytes() } - /// Deserializes a CpcSketch from bytes. + /// Deserializes a `CpcSketch` from bytes. pub fn deserialize(bytes: &[u8]) -> Result { Self::deserialize_with_seed(bytes, DEFAULT_UPDATE_SEED) } - /// Deserializes a CpcSketch from bytes with the provided seed. + /// Deserializes a `CpcSketch` from bytes with the provided seed. pub fn deserialize_with_seed(bytes: &[u8], seed: u64) -> Result { let mut cursor = SketchSlice::new(bytes); let preamble_ints = cursor @@ -716,7 +716,7 @@ impl CpcSketch { // testing methods impl CpcSketch { - /// Validate this sketch is valid. + /// Returns `true` if the sketch's internal state is valid. /// /// This is primarily for testing and validation purposes. pub fn validate(&self) -> bool { diff --git a/datasketches/src/cpc/union.rs b/datasketches/src/cpc/union.rs index 5cf74d78..63198ca3 100644 --- a/datasketches/src/cpc/union.rs +++ b/datasketches/src/cpc/union.rs @@ -69,7 +69,7 @@ use crate::cpc::determine_correct_offset; use crate::cpc::pair_table::PairTable; use crate::hash::DEFAULT_UPDATE_SEED; -/// The union (merge) operation for the CPC sketches. +/// Union operator for CPC sketches. #[derive(Debug, Clone)] pub struct CpcUnion { // immutable config variables @@ -108,15 +108,15 @@ impl CpcUnion { Self { lg_k, seed, state } } - /// Return the parameter lg_k. + /// Returns the current `lg_k`. /// - /// Note that due to merging with source sketches that may have a lower value of lg_k, this + /// Note that due to merging with source sketches that may have a lower `lg_k`, this /// value can be less than what the union object was configured with. pub fn lg_k(&self) -> u8 { self.lg_k } - /// Get the union result as a new sketch. + /// Returns the union result as a new sketch. /// /// # Examples /// @@ -207,7 +207,7 @@ impl CpcUnion { } } - /// Update this union with a CpcSketch. + /// Updates this union with a `CpcSketch`. /// /// # Panics /// diff --git a/datasketches/src/cpc/wrapper.rs b/datasketches/src/cpc/wrapper.rs index 51f9f9dc..c0ee9453 100644 --- a/datasketches/src/cpc/wrapper.rs +++ b/datasketches/src/cpc/wrapper.rs @@ -34,7 +34,7 @@ use crate::cpc::serialization::SERIAL_VERSION; use crate::cpc::serialization::make_preamble_ints; use crate::error::Error; -/// A read-only view of a serialized image of a CpcSketch. +/// A read-only view of a serialized `CpcSketch` image. #[derive(Debug, Clone)] pub struct CpcWrapper { lg_k: u8, @@ -131,7 +131,7 @@ impl CpcWrapper { }) } - /// Return the parameter lg_k. + /// Returns the configured `lg_k`. pub fn lg_k(&self) -> u8 { self.lg_k } @@ -168,7 +168,7 @@ impl CpcWrapper { ) } - /// Returns true if the sketch is empty. + /// Returns `true` if the sketch is empty. pub fn is_empty(&self) -> bool { self.num_coupons == 0 } diff --git a/datasketches/src/error.rs b/datasketches/src/error.rs index 094ab2df..3f22effe 100644 --- a/datasketches/src/error.rs +++ b/datasketches/src/error.rs @@ -15,22 +15,22 @@ // specific language governing permissions and limitations // under the License. -//! Error types for datasketches operations +//! Error types for DataSketches operations. use std::fmt; -/// ErrorKind is all kinds of Error of datasketches. +/// Categories of errors returned by DataSketches operations. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] #[non_exhaustive] pub enum ErrorKind { /// The argument provided is invalid. InvalidArgument, - /// The sketch data deserializing is malformed. + /// The serialized sketch data is malformed. InvalidData, } impl ErrorKind { - /// Convert this error kind instance into static str. + /// Converts this error kind into a static string. pub const fn into_static(self) -> &'static str { match self { ErrorKind::InvalidArgument => "InvalidArgument", @@ -45,7 +45,7 @@ impl fmt::Display for ErrorKind { } } -/// Error is the error struct returned by all datasketches functions. +/// Error returned by a DataSketches operation. /// /// # Examples /// @@ -64,7 +64,7 @@ pub struct Error { } impl Error { - /// Create a new Error with error kind and message. + /// Creates a new `Error` with the given kind and message. pub fn new(kind: ErrorKind, message: impl Into) -> Self { Self { kind, @@ -73,18 +73,18 @@ impl Error { } } - /// Add more context in error. + /// Adds context to the error. pub fn with_context(mut self, key: &'static str, value: impl ToString) -> Self { self.context.push((key, value.to_string())); self } - /// Return error's kind. + /// Returns the error kind. pub fn kind(&self) -> ErrorKind { self.kind } - /// Return error's message. + /// Returns the error message. pub fn message(&self) -> &str { self.message.as_str() } diff --git a/datasketches/src/frequencies/mod.rs b/datasketches/src/frequencies/mod.rs index 7dc15ec7..86b5f4e9 100644 --- a/datasketches/src/frequencies/mod.rs +++ b/datasketches/src/frequencies/mod.rs @@ -27,15 +27,15 @@ //! [`Hash`](std::hash::Hash), with optional associated counts (`T` item, `u64` count) that are //! members of a multiset. The true frequency of an item is the sum of its associated counts. //! -//! This implementation provides the following capabilities: -//! * Estimate the frequency of an item. -//! * Return upper and lower bounds of any item, such that the true frequency is always between the -//! upper and lower bounds. -//! * Return a global maximum error that holds for all items in the stream. -//! * Return an array of frequent items that qualify either [`ErrorType::NoFalsePositives`] or +//! This implementation: +//! * Estimates the frequency of an item. +//! * Returns upper and lower bounds for any item, such that the true frequency is always between +//! the upper and lower bounds. +//! * Returns a global maximum error that holds for all items in the stream. +//! * Returns an array of frequent items that qualify either [`ErrorType::NoFalsePositives`] or //! [`ErrorType::NoFalseNegatives`]. -//! * Merge itself with another sketch created from this module. -//! * Serialize to bytes, or deserialize from bytes, for storage or transmission. +//! * Merges itself with another sketch created from this module. +//! * Serializes to bytes and deserializes from bytes for storage or transmission. //! //! # Accuracy //! diff --git a/datasketches/src/frequencies/sketch.rs b/datasketches/src/frequencies/sketch.rs index 8c0c378c..e5a5a47e 100644 --- a/datasketches/src/frequencies/sketch.rs +++ b/datasketches/src/frequencies/sketch.rs @@ -47,9 +47,9 @@ const LOAD_FACTOR_DENOMINATOR: usize = 4; /// Error guarantees for frequent item queries. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ErrorType { - /// Include items if upper bound exceeds threshold (no false negatives). + /// Includes items if the upper bound exceeds the threshold (no false negatives). NoFalseNegatives, - /// Include items if lower bound exceeds threshold (no false positives). + /// Includes items if the lower bound exceeds the threshold (no false positives). NoFalsePositives, } @@ -108,10 +108,6 @@ impl FrequentItemsSketch { /// The maximum map capacity is `0.75 * max_map_size`, and the internal map grows /// from a small starting size up to the maximum as needed. /// - /// # Panics - /// - /// Panics if `max_map_size` is not a power of two. - /// /// # Examples /// /// ``` @@ -122,6 +118,10 @@ impl FrequentItemsSketch { /// sketch.update(2); /// assert_eq!(sketch.num_active_items(), 2); /// ``` + /// + /// # Panics + /// + /// Panics if `max_map_size` is not a power of two. pub fn new(max_map_size: usize) -> Self { assert!( max_map_size.is_power_of_two(), @@ -131,7 +131,7 @@ impl FrequentItemsSketch { Self::with_lg_map_sizes(lg_max_map_size, LG_MIN_MAP_SIZE) } - /// Returns true if the sketch has no active items. + /// Returns `true` if the sketch has no active items. /// /// A purge can remove all active items while retaining a non-zero total weight and /// maximum error. Use [`Self::total_weight`] to distinguish that state from a newly created @@ -211,12 +211,12 @@ impl FrequentItemsSketch { self.offset } - /// Returns epsilon for this sketch. + /// Returns the epsilon error parameter for this sketch. pub fn epsilon(&self) -> f64 { Self::epsilon_for_lg(self.lg_max_map_size) } - /// Returns epsilon for a sketch configured with `lg_max_map_size`. + /// Returns the epsilon error parameter for the given `lg_max_map_size`. pub fn epsilon_for_lg(lg_max_map_size: u8) -> f64 { EPSILON_FACTOR / (1u64 << lg_max_map_size) as f64 } @@ -240,12 +240,12 @@ impl FrequentItemsSketch { self.cur_map_cap } - /// Returns the configured log2 maximum map size. + /// Returns the configured `lg_max_map_size`. pub fn lg_max_map_size(&self) -> u8 { self.lg_max_map_size } - /// Returns the current map size in log2. + /// Returns the current `lg_cur_map_size`. pub fn lg_cur_map_size(&self) -> u8 { self.hash_map.lg_length() } diff --git a/datasketches/src/hash/value/canonical_float.rs b/datasketches/src/hash/value/canonical_float.rs index 6ed93875..0feb5263 100644 --- a/datasketches/src/hash/value/canonical_float.rs +++ b/datasketches/src/hash/value/canonical_float.rs @@ -38,7 +38,7 @@ pub type CanonicalFloat = Value; #[doc(hidden)] pub struct CanonicalFloatStrategy; -/// Create a canonical hashable value from a `f32` value. +/// Creates a canonical hashable value from an `f32` value. /// /// `f32` values are converted to `f64` before hashing. Values that are not exactly representable /// in `f32` may hash differently from the corresponding `f64` value. Signed zero values hash the @@ -68,7 +68,7 @@ pub fn from_f32(v: f32) -> CanonicalFloat { CanonicalFloat::new(v) } -/// Create a canonical hashable value from a `f64` value. +/// Creates a canonical hashable value from an `f64` value. /// /// Signed zero values hash the same, and all NaN values use one canonical NaN bit pattern. /// diff --git a/datasketches/src/hash/value/mod.rs b/datasketches/src/hash/value/mod.rs index 24559656..ee42e19a 100644 --- a/datasketches/src/hash/value/mod.rs +++ b/datasketches/src/hash/value/mod.rs @@ -29,7 +29,7 @@ //! //! This strategy is the same as how other datasketches implementations hash floating-point numbers. //! -//! Read the docs of concrete value wrapper for more details and examples. +//! The concrete wrapper documentation provides more details and examples. //! //! * [`canonical_float::from_f32`] //! * [`canonical_float::from_f64`] @@ -40,7 +40,7 @@ //! integers. This strategy is the same as how datasketches-cpp hashes short integers for //! `HllSketch` and `CpcSketch`. //! -//! Read the docs of concrete value wrapper for more details and examples. +//! The concrete wrapper documentation provides more details and examples. //! //! * [`sign_extend::from_i8`], [`sign_extend::from_u8`] //! * [`sign_extend::from_i16`], [`sign_extend::from_u16`] @@ -50,7 +50,7 @@ //! `u64`, and then hashes the resulting integers. This strategy is the same as how datasketches-cpp //! hashes short integers for `BloomFilter`. //! -//! Read the docs of concrete value wrapper for more details and examples. +//! The concrete wrapper documentation provides more details and examples. //! //! * [`natural_extend::from_i8`], [`natural_extend::from_u8`] //! * [`natural_extend::from_i16`], [`natural_extend::from_u16`] @@ -65,7 +65,7 @@ //! empty strings before hashing, so check `is_empty` before updating a sketch when that behavior //! matters. //! -//! Read the docs of concrete value wrapper for more details and examples. +//! The concrete wrapper documentation provides more details and examples. //! //! * [`raw_bytes::from_vec`] //! * [`raw_bytes::from_string`] diff --git a/datasketches/src/hash/value/natural_extend.rs b/datasketches/src/hash/value/natural_extend.rs index 375fcad9..919060e6 100644 --- a/datasketches/src/hash/value/natural_extend.rs +++ b/datasketches/src/hash/value/natural_extend.rs @@ -37,7 +37,7 @@ pub type NaturalExtend = Value; #[doc(hidden)] pub struct NaturalExtendStrategy; -/// Create a naturally extended hashable value from an `i8` value. +/// Creates a naturally extended hashable value from an `i8` value. /// /// # Examples /// @@ -52,7 +52,7 @@ pub fn from_i8(v: i8) -> NaturalExtend { NaturalExtend::new(v) } -/// Create a naturally extended hashable value from a `u8` value. +/// Creates a naturally extended hashable value from a `u8` value. /// /// # Examples /// @@ -67,7 +67,7 @@ pub fn from_u8(v: u8) -> NaturalExtend { NaturalExtend::new(v) } -/// Create a naturally extended hashable value from an `i16` value. +/// Creates a naturally extended hashable value from an `i16` value. /// /// # Examples /// @@ -82,7 +82,7 @@ pub fn from_i16(v: i16) -> NaturalExtend { NaturalExtend::new(v) } -/// Create a naturally extended hashable value from a `u16` value. +/// Creates a naturally extended hashable value from a `u16` value. /// /// # Examples /// @@ -97,7 +97,7 @@ pub fn from_u16(v: u16) -> NaturalExtend { NaturalExtend::new(v) } -/// Create a naturally extended hashable value from an `i32` value. +/// Creates a naturally extended hashable value from an `i32` value. /// /// # Examples /// @@ -112,7 +112,7 @@ pub fn from_i32(v: i32) -> NaturalExtend { NaturalExtend::new(v) } -/// Create a naturally extended hashable value from a `u32` value. +/// Creates a naturally extended hashable value from a `u32` value. /// /// # Examples /// diff --git a/datasketches/src/hash/value/raw_bytes.rs b/datasketches/src/hash/value/raw_bytes.rs index 18477116..55e79ad2 100644 --- a/datasketches/src/hash/value/raw_bytes.rs +++ b/datasketches/src/hash/value/raw_bytes.rs @@ -38,7 +38,7 @@ pub type RawBytes = Value; #[doc(hidden)] pub struct RawBytesStrategy; -/// Create a raw-byte hashable value from a byte vector. +/// Creates a raw-byte hashable value from a byte vector. /// /// This hashes the vector contents without Rust's slice length prefix. /// @@ -60,7 +60,7 @@ pub fn from_vec(v: Vec) -> RawBytes> { RawBytes::new(v) } -/// Create a raw-byte hashable value from a string. +/// Creates a raw-byte hashable value from a string. /// /// This hashes the UTF-8 bytes of the string without Rust's string length prefix. /// @@ -82,7 +82,7 @@ pub fn from_string(v: String) -> RawBytes { RawBytes::new(v) } -/// Create a raw-byte hashable value from a byte slice. +/// Creates a raw-byte hashable value from a byte slice. /// /// This hashes the slice contents without Rust's slice length prefix. /// @@ -104,7 +104,7 @@ pub fn from_slice(v: &[u8]) -> RawBytes<&[u8]> { RawBytes::new(v) } -/// Create a raw-byte hashable value from a string slice. +/// Creates a raw-byte hashable value from a string slice. /// /// This hashes the UTF-8 bytes of the string slice without Rust's string length prefix. /// diff --git a/datasketches/src/hash/value/sign_extend.rs b/datasketches/src/hash/value/sign_extend.rs index 0672f93a..060072b8 100644 --- a/datasketches/src/hash/value/sign_extend.rs +++ b/datasketches/src/hash/value/sign_extend.rs @@ -37,7 +37,7 @@ pub type SignExtend = Value; #[doc(hidden)] pub struct SignExtendStrategy; -/// Create a sign-extended hashable value from an `i8` value. +/// Creates a sign-extended hashable value from an `i8` value. /// /// # Examples /// @@ -54,7 +54,7 @@ pub fn from_i8(v: i8) -> SignExtend { SignExtend::new(v) } -/// Create a sign-extended hashable value from a `u8` value. +/// Creates a sign-extended hashable value from a `u8` value. /// /// `255u8` sign-extends like `-1i8`, not like `255u64`. /// @@ -73,7 +73,7 @@ pub fn from_u8(v: u8) -> SignExtend { SignExtend::new(v) } -/// Create a sign-extended hashable value from an `i16` value. +/// Creates a sign-extended hashable value from an `i16` value. /// /// # Examples /// @@ -93,7 +93,7 @@ pub fn from_i16(v: i16) -> SignExtend { SignExtend::new(v) } -/// Create a sign-extended hashable value from a `u16` value. +/// Creates a sign-extended hashable value from a `u16` value. /// /// `65535u16` sign-extends like `-1i16`, not like `65535u64`. /// @@ -115,7 +115,7 @@ pub fn from_u16(v: u16) -> SignExtend { SignExtend::new(v) } -/// Create a sign-extended hashable value from an `i32` value. +/// Creates a sign-extended hashable value from an `i32` value. /// /// # Examples /// @@ -135,7 +135,7 @@ pub fn from_i32(v: i32) -> SignExtend { SignExtend::new(v) } -/// Create a sign-extended hashable value from a `u32` value. +/// Creates a sign-extended hashable value from a `u32` value. /// /// `4294967295u32` sign-extends like `-1i32`, not like `4294967295u64`. /// diff --git a/datasketches/src/hll/mod.rs b/datasketches/src/hll/mod.rs index 90d0465c..8f0ece63 100644 --- a/datasketches/src/hll/mod.rs +++ b/datasketches/src/hll/mod.rs @@ -136,19 +136,15 @@ pub use self::union::HllUnion; /// See [module level documentation](self) for more details. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum HllType { - /// Uses a 4-bit field per HLL bucket and for large counts may require the use of a - /// small internal auxiliary array for storing statistical exceptions, which are rare. - /// For the values of lgConfigK > 13 (K = 8192), this additional array adds about 3% - /// to the overall storage. + /// Uses 4 bits per HLL bucket and has the smallest storage footprint. /// - /// It is generally the slowest in terms of update time, but has the smallest storage - /// footprint of about K/2 * 1.03 bytes. + /// It is generally the slowest representation to update. Hll4, - /// Uses a 6-bit field per HLL bucket. It is generally the next fastest in terms - /// of update time with a storage footprint of about 3/4 * K bytes. + /// Uses 6 bits per HLL bucket and provides a middle ground for storage and update speed. Hll6, - /// Uses an 8-bit byte per HLL bucket. It is generally the fastest in terms of update - /// time but has the largest storage footprint of about K bytes. + /// Uses 8 bits per HLL bucket and has the largest storage footprint. + /// + /// It is generally the fastest representation to update. Hll8, } @@ -211,7 +207,7 @@ impl Coupon { self.0 } - /// Compute the HLL coupon for a hashable value. + /// Computes the HLL coupon for a hashable value. /// /// You may use [`hash::value`](crate::hash::value) wrappers when another DataSketches /// implementation requires a specific value hashing strategy. diff --git a/datasketches/src/hll/sketch.rs b/datasketches/src/hll/sketch.rs index 9efd6ff4..946d0df1 100644 --- a/datasketches/src/hll/sketch.rs +++ b/datasketches/src/hll/sketch.rs @@ -65,19 +65,15 @@ pub struct HllSketch { } impl HllSketch { - /// Create a new HLL sketch + /// Creates a new HLL sketch. /// /// # Arguments /// - /// * `lg_config_k`: Log2 of the number of buckets (K). Must be in `[4, 21]`. - /// * lg_k=4: 16 buckets, ~26% relative error - /// * lg_k=12: 4096 buckets, ~1.6% relative error (common choice) - /// * lg_k=21: 2M buckets, ~0.4% relative error - /// * `hll_type`: Target HLL array type (Hll4, Hll6, or Hll8) - /// - /// # Panics - /// - /// If lg_config_k is not in range `[4, 21]` + /// * `lg_config_k`: The `lg_k` value in `[4, 21]`, which controls the number of buckets. + /// * `lg_k = 4`: 16 buckets, ~26% relative error. + /// * `lg_k = 12`: 4096 buckets, ~1.6% relative error (common choice). + /// * `lg_k = 21`: 2M buckets, ~0.4% relative error. + /// * `hll_type`: Target HLL array type (`Hll4`, `Hll6`, or `Hll8`). /// /// # Examples /// @@ -88,6 +84,10 @@ impl HllSketch { /// let sketch = HllSketch::new(12, HllType::Hll8); /// assert_eq!(sketch.lg_config_k(), 12); /// ``` + /// + /// # Panics + /// + /// Panics if `lg_config_k` is outside `[4, 21]`. pub fn new(lg_config_k: u8, hll_type: HllType) -> Self { assert!( (4..=21).contains(&lg_config_k), @@ -130,7 +130,7 @@ impl HllSketch { &mut self.mode } - /// Check if the sketch is empty (no values have been added) + /// Returns `true` if no values have been added to the sketch. pub fn is_empty(&self) -> bool { match &self.mode { Mode::List { list, .. } => list.container().is_empty(), @@ -141,7 +141,7 @@ impl HllSketch { } } - /// Get the target HLL type for this sketch + /// Returns the target HLL type for this sketch. pub fn target_type(&self) -> HllType { match &self.mode { Mode::List { hll_type, .. } => *hll_type, @@ -152,12 +152,12 @@ impl HllSketch { } } - /// Get the configured lg_config_k + /// Returns the configured `lg_k`. pub fn lg_config_k(&self) -> u8 { self.lg_config_k } - /// Update the sketch with a value. + /// Updates the sketch with a value. /// /// Accepts any type that implements [`Hash`]. The value is hashed and converted to /// an internal coupon, which is then inserted into the sketch. @@ -189,15 +189,15 @@ impl HllSketch { self.update_with_coupon(Coupon::from_value(value)); } - /// Update the sketch with a pre-computed [`Coupon`]. + /// Updates the sketch with a pre-computed [`Coupon`]. /// /// A [`Coupon`] encodes both the HLL bucket index (low 26 bits) and the register /// value (high 6 bits) derived from hashing an input. Accepting a pre-computed /// coupon makes it possible to pay the hashing cost once and fan the result out to /// many independent sketches — see [`Coupon`] for a worked example. /// - /// Handles all internal bookkeeping, including automatic mode transitions - /// (List → Set → HLL array) and estimator state updates. + /// All internal bookkeeping, including representation transitions and estimator state updates, + /// is handled automatically. /// /// # Examples /// @@ -242,7 +242,7 @@ impl HllSketch { } } - /// Get the current cardinality estimate + /// Returns the current cardinality estimate. /// /// # Examples /// @@ -264,10 +264,9 @@ impl HllSketch { } } - /// Get upper bound for cardinality estimate + /// Returns the upper confidence bound for the cardinality estimate. /// - /// Returns the upper confidence bound for the cardinality estimate based on - /// the number of standard deviations requested. + /// The bound is based on the requested number of standard deviations. pub fn upper_bound(&self, num_std_dev: NumStdDev) -> f64 { match &self.mode { Mode::List { list, .. } => list.container().upper_bound(num_std_dev), @@ -278,10 +277,9 @@ impl HllSketch { } } - /// Get lower bound for cardinality estimate + /// Returns the lower confidence bound for the cardinality estimate. /// - /// Returns the lower confidence bound for the cardinality estimate based on - /// the number of standard deviations requested. + /// The bound is based on the requested number of standard deviations. pub fn lower_bound(&self, num_std_dev: NumStdDev) -> f64 { match &self.mode { Mode::List { list, .. } => list.container().lower_bound(num_std_dev), @@ -292,7 +290,7 @@ impl HllSketch { } } - /// Deserializes an HLL sketch from bytes + /// Deserializes an HLL sketch from bytes. /// /// # Examples /// @@ -409,7 +407,7 @@ impl HllSketch { Ok(HllSketch { lg_config_k, mode }) } - /// Serializes the HLL sketch to bytes + /// Serializes the HLL sketch to bytes. /// /// # Examples /// @@ -434,7 +432,7 @@ impl HllSketch { } } - /// Returns the estimated size of the sketch in bytes + /// Returns the estimated size of the sketch in bytes. pub fn estimated_size(&self) -> usize { let heap_size = match &self.mode { Mode::List { list, .. } => list.container().estimated_size(), diff --git a/datasketches/src/hll/union.rs b/datasketches/src/hll/union.rs index a710fd9b..acf2f3bf 100644 --- a/datasketches/src/hll/union.rs +++ b/datasketches/src/hll/union.rs @@ -39,7 +39,7 @@ use crate::hll::array6::Array6; use crate::hll::array8::Array8; use crate::hll::mode::Mode; -/// An HLL Union for combining multiple HLL sketches. +/// An HLL union for combining multiple HLL sketches. /// /// The union accumulates the distinct values represented by all input sketches and automatically /// handles sketches with different configurations. @@ -59,17 +59,12 @@ pub struct HllUnion { } impl HllUnion { - /// Create a new HLL Union + /// Creates a new HLL union. /// /// # Arguments /// - /// * `lg_max_k`: Maximum log2 of the number of buckets. Must be in `[4, 21]`. This determines - /// the maximum precision the union can handle. Input sketches with larger lg_k will be - /// down-sampled. - /// - /// # Panics - /// - /// Panics if `lg_max_k` is not in the range `[4, 21]`. + /// * `lg_max_k`: Maximum `lg_k` in `[4, 21]`. This determines the maximum precision the union + /// can handle. Input sketches with a larger `lg_k` are downsampled. /// /// # Examples /// @@ -82,6 +77,10 @@ impl HllUnion { /// let result = union.to_sketch(HllType::Hll8); /// assert_eq!(result.estimate(), 1.0); /// ``` + /// + /// # Panics + /// + /// Panics if `lg_max_k` is outside `[4, 21]`. pub fn new(lg_max_k: u8) -> Self { assert!( (4..=21).contains(&lg_max_k), @@ -95,7 +94,7 @@ impl HllUnion { Self { lg_max_k, gadget } } - /// Update the union's gadget with a value + /// Updates the union with a hashable value. /// /// This accepts any type that implements `Hash`. The value is hashed /// and converted to a coupon, which is then inserted into the sketch. @@ -115,12 +114,10 @@ impl HllUnion { self.gadget.update(value); } - /// Update the union with another sketch + /// Updates the union with another sketch. /// - /// Merges the input sketch into the union's internal gadget, handling: - /// * Sketches with different `lg_k` values (resizes/downsamples as needed) - /// * Sketches in different modes (List, Set, Array4/6/8) - /// * Sketches with different target HLL types + /// The input is merged while accommodating different `lg_k` configurations and target HLL + /// types. The union's effective `lg_k` may decrease as a result. /// /// # Examples /// @@ -246,14 +243,14 @@ impl HllUnion { self.gadget = HllSketch::from_mode(final_lg_k, Mode::Array8(new_array)); } - /// Get the union result as a new sketch. + /// Returns the union result as a new sketch. /// - /// Returns a copy of the internal gadget sketch with the specified target HLL type. - /// If the requested type differs from the gadget's type, conversion is performed. + /// The returned sketch uses the specified target HLL type. If the requested type differs from + /// the current representation, the result is converted. /// /// # Arguments /// - /// * `hll_type`: The target HLL type for the result sketch (Hll4, Hll6, or Hll8) + /// * `hll_type`: Target HLL type for the result sketch (`Hll4`, `Hll6`, or `Hll8`). /// /// # Examples /// @@ -297,46 +294,43 @@ impl HllUnion { } } - /// Get the current lg_config_k of the internal gadget + /// Returns the union's current effective `lg_k`. pub fn lg_config_k(&self) -> u8 { self.gadget.lg_config_k() } - /// Get the maximum lg_k this union can handle + /// Returns the maximum configured `lg_k`. pub fn lg_max_k(&self) -> u8 { self.lg_max_k } - /// Check if the union is empty + /// Returns `true` if the union is empty. pub fn is_empty(&self) -> bool { self.gadget.is_empty() } - /// Reset the union to its initial empty state + /// Resets the union to its initial empty state. /// - /// Clears all data from the internal gadget, allowing the union to be reused - /// for a new set of operations. + /// This clears all accumulated data so the union can be reused. pub fn reset(&mut self) { self.gadget = HllSketch::new(self.lg_max_k, HllType::Hll8); } - /// Get the current cardinality estimate of the union + /// Returns the union's current cardinality estimate. pub fn estimate(&self) -> f64 { self.gadget.estimate() } - /// Get upper bound for cardinality estimate of the union + /// Returns the upper confidence bound for the union's cardinality estimate. /// - /// Returns the upper confidence bound for the cardinality estimate based on - /// the number of standard deviations requested. + /// The bound is based on the requested number of standard deviations. pub fn upper_bound(&self, num_std_dev: NumStdDev) -> f64 { self.gadget.upper_bound(num_std_dev) } - /// Get lower bound for cardinality estimate of the union + /// Returns the lower confidence bound for the union's cardinality estimate. /// - /// Returns the lower confidence bound for the cardinality estimate based on - /// the number of standard deviations requested. + /// The bound is based on the requested number of standard deviations. pub fn lower_bound(&self, num_std_dev: NumStdDev) -> f64 { self.gadget.lower_bound(num_std_dev) } diff --git a/datasketches/src/tdigest/mod.rs b/datasketches/src/tdigest/mod.rs index 5590c754..1f670870 100644 --- a/datasketches/src/tdigest/mod.rs +++ b/datasketches/src/tdigest/mod.rs @@ -23,9 +23,9 @@ //! The implementation in this library has a few differences from the reference implementation //! associated with that paper: //! -//! * Merge does not modify the input -//! * Deserialization similar to other sketches in this library, although reading the reference -//! implementation format is supported +//! * Merging does not modify the input. +//! * Deserialization is similar to other sketches in this library, although reading the reference +//! implementation format is supported. //! //! Unlike all other algorithms in the library, t-digest is empirical and has no mathematical //! basis for estimating its error and its results are dependent on the input data. However, diff --git a/datasketches/src/tdigest/sketch.rs b/datasketches/src/tdigest/sketch.rs index 3f26b007..f3fabfbb 100644 --- a/datasketches/src/tdigest/sketch.rs +++ b/datasketches/src/tdigest/sketch.rs @@ -66,14 +66,10 @@ impl Default for TDigestMut { } impl TDigestMut { - /// Creates a tdigest instance with the given value of k. + /// Creates a mutable t-digest with the given `k` value. /// /// The fallible version of this method is [`TDigestMut::try_new`]. /// - /// # Panics - /// - /// Panics if k is less than 10 - /// /// # Examples /// /// ``` @@ -82,6 +78,10 @@ impl TDigestMut { /// let sketch = TDigestMut::new(100); /// assert_eq!(sketch.k(), 100); /// ``` + /// + /// # Panics + /// + /// Panics if `k` is less than `10`. pub fn new(k: u16) -> Self { Self::make( k, @@ -94,14 +94,10 @@ impl TDigestMut { ) } - /// Creates a tdigest instance with the given value of k. + /// Creates a mutable t-digest with the given `k` value. /// /// The panicking version of this method is [`TDigestMut::new`]. /// - /// # Errors - /// - /// If k is less than 10. - /// /// # Examples /// /// ``` @@ -110,6 +106,10 @@ impl TDigestMut { /// let sketch = TDigestMut::try_new(20).unwrap(); /// assert_eq!(sketch.k(), 20); /// ``` + /// + /// # Errors + /// + /// Returns an error if `k` is less than `10`. pub fn try_new(k: u16) -> Result { if k < 10 { return Err(Error::invalid_argument(format!( @@ -158,7 +158,7 @@ impl TDigestMut { } } - /// Update this TDigest with the given value. + /// Updates this t-digest with the given value. /// /// [f64::NAN], [f64::INFINITY], and [f64::NEG_INFINITY] values are ignored. /// @@ -185,17 +185,17 @@ impl TDigestMut { self.max = self.max.max(value); } - /// Returns parameter k (compression) that was used to configure this TDigest. + /// Returns the compression parameter `k` used to configure this t-digest. pub fn k(&self) -> u16 { self.k } - /// Returns true if TDigest has not seen any data. + /// Returns `true` if this t-digest has not seen any data. pub fn is_empty(&self) -> bool { self.centroids.is_empty() && self.buffer.is_empty() } - /// Returns minimum value seen by TDigest; `None` if TDigest is empty. + /// Returns the minimum value seen by this t-digest, or `None` if it is empty. pub fn min_value(&self) -> Option { if self.is_empty() { None @@ -204,7 +204,7 @@ impl TDigestMut { } } - /// Returns maximum value seen by TDigest; `None` if TDigest is empty. + /// Returns the maximum value seen by this t-digest, or `None` if it is empty. pub fn max_value(&self) -> Option { if self.is_empty() { None @@ -213,12 +213,12 @@ impl TDigestMut { } } - /// Returns total weight. + /// Returns the total weight. pub fn total_weight(&self) -> u64 { self.centroids_weight + self.buffer.len() as u64 } - /// Merge the given TDigest into this one + /// Merges the given t-digest into this one. /// /// # Examples /// @@ -258,7 +258,7 @@ impl TDigestMut { self.do_merge(tmp, self.buffer.len() as u64 + other.total_weight()) } - /// Freezes this TDigest into an immutable one. + /// Converts this mutable t-digest into an immutable one. /// /// # Examples /// @@ -292,7 +292,7 @@ impl TDigestMut { } } - /// See [`TDigest::cdf`]. + /// Returns the cumulative distribution approximation described by [`TDigest::cdf`]. /// /// # Examples /// @@ -316,7 +316,7 @@ impl TDigestMut { self.view().cdf(split_points) } - /// See [`TDigest::pmf`]. + /// Returns the probability mass approximation described by [`TDigest::pmf`]. /// /// # Examples /// @@ -340,7 +340,7 @@ impl TDigestMut { self.view().pmf(split_points) } - /// See [`TDigest::rank`]. + /// Returns the normalized rank described by [`TDigest::rank`]. /// /// # Examples /// @@ -374,7 +374,7 @@ impl TDigestMut { self.view().rank(value) } - /// See [`TDigest::quantile`]. + /// Returns the quantile described by [`TDigest::quantile`]. /// /// # Examples /// @@ -398,7 +398,7 @@ impl TDigestMut { self.view().quantile(rank) } - /// Serializes this TDigest to bytes. + /// Serializes this mutable t-digest to bytes. /// /// # Examples /// @@ -483,15 +483,7 @@ impl TDigestMut { bytes.into_bytes() } - /// Deserializes a TDigest from bytes. - /// - /// Supports reading compact format with (float, int) centroids as opposed to (double, long) to - /// represent (mean, weight). [^1] - /// - /// Supports reading format of the reference implementation (auto-detected) [^2]. - /// - /// [^1]: This is to support reading the `tdigest` format from the C++ implementation. - /// [^2]: + /// Deserializes a mutable t-digest from bytes. /// /// # Examples /// @@ -505,6 +497,14 @@ impl TDigestMut { /// let decoded = TDigestMut::deserialize(&bytes, false).unwrap(); /// assert_eq!(decoded.max_value(), Some(2.0)); /// ``` + /// + /// Supports reading compact format with (float, int) centroids as opposed to (double, long) to + /// represent (mean, weight). [^1] + /// + /// Supports reading format of the reference implementation (auto-detected) [^2]. + /// + /// [^1]: This is to support reading the `tdigest` format from the C++ implementation. + /// [^2]: pub fn deserialize(bytes: &[u8], is_f32: bool) -> Result { let mut cursor = SketchSlice::new(bytes); @@ -814,7 +814,7 @@ impl TDigestMut { self.buffer.clear(); } - /// Returns the estimated size of the sketch in bytes + /// Returns the estimated size of the sketch in bytes. pub fn estimated_size(&self) -> usize { size_of::() + self.centroids.capacity() * size_of::() @@ -837,17 +837,17 @@ pub struct TDigest { } impl TDigest { - /// Returns parameter k (compression) that was used to configure this TDigest. + /// Returns the compression parameter `k` used to configure this t-digest. pub fn k(&self) -> u16 { self.k } - /// Returns true if TDigest has not seen any data. + /// Returns `true` if this t-digest has not seen any data. pub fn is_empty(&self) -> bool { self.centroids.is_empty() } - /// Returns minimum value seen by TDigest; `None` if TDigest is empty. + /// Returns the minimum value seen by this t-digest, or `None` if it is empty. pub fn min_value(&self) -> Option { if self.is_empty() { None @@ -856,7 +856,7 @@ impl TDigest { } } - /// Returns maximum value seen by TDigest; `None` if TDigest is empty. + /// Returns the maximum value seen by this t-digest, or `None` if it is empty. pub fn max_value(&self) -> Option { if self.is_empty() { None @@ -865,7 +865,7 @@ impl TDigest { } } - /// Returns total weight. + /// Returns the total weight. pub fn total_weight(&self) -> u64 { self.centroids_weight } @@ -895,12 +895,7 @@ impl TDigest { /// This can be viewed as array of ranks of the given split points plus one more value that /// is always 1. /// - /// Returns `None` if TDigest is empty. - /// - /// # Panics - /// - /// Panics if `split_points` is not unique, not monotonically increasing, or contains `NaN` - /// values. + /// Returns `None` if this t-digest is empty. /// /// # Examples /// @@ -915,6 +910,11 @@ impl TDigest { /// let cdf = digest.cdf(&[1.5]).unwrap(); /// assert_eq!(cdf.len(), 2); /// ``` + /// + /// # Panics + /// + /// Panics if `split_points` is not unique, not monotonically increasing, or contains `NaN` + /// values. pub fn cdf(&self, split_points: &[f64]) -> Option> { self.view().cdf(split_points) } @@ -932,12 +932,7 @@ impl TDigest { /// An array of m+1 doubles each of which is an approximation to the fraction of the input /// stream values (the mass) that fall into one of those intervals. /// - /// Returns `None` if TDigest is empty. - /// - /// # Panics - /// - /// Panics if `split_points` is not unique, not monotonically increasing, or contains `NaN` - /// values. + /// Returns `None` if this t-digest is empty. /// /// # Examples /// @@ -952,17 +947,18 @@ impl TDigest { /// let pmf = digest.pmf(&[1.5]).unwrap(); /// assert_eq!(pmf.len(), 2); /// ``` + /// + /// # Panics + /// + /// Panics if `split_points` is not unique, not monotonically increasing, or contains `NaN` + /// values. pub fn pmf(&self, split_points: &[f64]) -> Option> { self.view().pmf(split_points) } - /// Compute approximate normalized rank (from 0 to 1 inclusive) of the given value. - /// - /// Returns `None` if TDigest is empty. - /// - /// # Panics + /// Computes the approximate normalized rank in `[0.0, 1.0]` of the given value. /// - /// Panics if the value is `NaN`. + /// Returns `None` if this t-digest is empty. /// /// # Examples /// @@ -977,18 +973,18 @@ impl TDigest { /// let rank = digest.rank(2.0).unwrap(); /// assert!((0.0..=1.0).contains(&rank)); /// ``` + /// + /// # Panics + /// + /// Panics if the value is `NaN`. pub fn rank(&self, value: f64) -> Option { assert!(!value.is_nan(), "value must not be NaN"); self.view().rank(value) } - /// Compute approximate quantile value corresponding to the given normalized rank. - /// - /// Returns `None` if TDigest is empty. - /// - /// # Panics + /// Computes the approximate quantile for the given normalized rank. /// - /// Panics if rank is not in [0.0, 1.0]. + /// Returns `None` if this t-digest is empty. /// /// # Examples /// @@ -1003,12 +999,16 @@ impl TDigest { /// let q = digest.quantile(0.5).unwrap(); /// assert!((1.0..=3.0).contains(&q)); /// ``` + /// + /// # Panics + /// + /// Panics if `rank` is outside `[0.0, 1.0]`. pub fn quantile(&self, rank: f64) -> Option { assert!((0.0..=1.0).contains(&rank), "rank must be in [0.0, 1.0]"); self.view().quantile(rank) } - /// Converts this immutable TDigest into a mutable one. + /// Converts this immutable t-digest into a mutable one. /// /// # Examples /// @@ -1034,7 +1034,7 @@ impl TDigest { ) } - /// Returns the estimated size of the sketch in bytes + /// Returns the estimated size of the sketch in bytes. pub fn estimated_size(&self) -> usize { size_of::() + self.centroids.capacity() * size_of::() } diff --git a/datasketches/src/thetafamily/theta/a_not_b.rs b/datasketches/src/thetafamily/theta/a_not_b.rs index 29ea2f8f..d8edbd1a 100644 --- a/datasketches/src/thetafamily/theta/a_not_b.rs +++ b/datasketches/src/thetafamily/theta/a_not_b.rs @@ -72,7 +72,7 @@ impl ThetaANotB { /// Computes `a and not b`. /// /// The result retains every key of `a` (below the combined theta) that is not present in `b`. - /// If `ordered` is true, the retained entries are sorted ascending by hash. + /// If `ordered` is `true`, the retained entries are sorted ascending by hash. /// /// # Errors /// diff --git a/datasketches/src/thetafamily/theta/hash_table.rs b/datasketches/src/thetafamily/theta/hash_table.rs index d7dd23b4..6cd17353 100644 --- a/datasketches/src/thetafamily/theta/hash_table.rs +++ b/datasketches/src/thetafamily/theta/hash_table.rs @@ -42,7 +42,7 @@ impl ThetaEntry { Self { hash } } - /// Return the hash used as this entry's key. + /// Returns the hash used as this entry's key. pub fn hash(&self) -> u64 { self.hash.get() } @@ -57,7 +57,7 @@ impl SketchEntry for ThetaEntry { impl ThetaHashTable { /// Hashes and inserts a value into the table. /// - /// Returns true if the value was inserted (new), false otherwise. + /// Returns `true` if the value was inserted, or `false` otherwise. pub fn try_insert(&mut self, value: T) -> bool { let hash = self.hash(value); self.try_insert_hash(hash) @@ -65,7 +65,7 @@ impl ThetaHashTable { /// Inserts a pre-hashed value into the table. /// - /// Returns true if the value was inserted (new), false otherwise. + /// Returns `true` if the value was inserted, or `false` otherwise. pub fn try_insert_hash(&mut self, hash: u64) -> bool { self.upsert_entry(hash, |existing| { if existing.is_some() { diff --git a/datasketches/src/thetafamily/theta/mod.rs b/datasketches/src/thetafamily/theta/mod.rs index e63fffec..6a70a6b3 100644 --- a/datasketches/src/thetafamily/theta/mod.rs +++ b/datasketches/src/thetafamily/theta/mod.rs @@ -20,15 +20,15 @@ //! Theta sketch is a generalization of the Kth Minimum Value (KMV) sketch that uses //! a hash table to store retained entries and a theta parameter (sampling threshold) //! to control memory usage. When the hash table reaches capacity, theta is reduced -//! to maintain the nominal size k. +//! to maintain the capacity configured by `lg_k`. //! //! # Overview //! //! Theta sketches provide approximate distinct count (cardinality) estimation with //! configurable accuracy and memory usage. The implementation supports: //! -//! * **ThetaSketch**: Mutable sketch for building from input data -//! * **CompactThetaSketch**: Immutable sketch with compact memory layout +//! * [`ThetaSketch`]: Mutable sketch for building from input data. +//! * [`CompactThetaSketch`]: Immutable sketch with a compact memory layout. //! //! # Usage //! diff --git a/datasketches/src/thetafamily/theta/sketch.rs b/datasketches/src/thetafamily/theta/sketch.rs index 2c3bf566..501e0d72 100644 --- a/datasketches/src/thetafamily/theta/sketch.rs +++ b/datasketches/src/thetafamily/theta/sketch.rs @@ -198,7 +198,7 @@ impl<'a> From<&'a CompactThetaSketch> for ThetaSketchView<'a> { } } -/// Mutable theta sketch for building from input data +/// Mutable theta sketch for building from input data. #[derive(Debug)] pub struct ThetaSketch { table: ThetaHashTable, @@ -210,7 +210,7 @@ impl ThetaSketch { self.into() } - /// Update the sketch with a hashable value. + /// Updates the sketch with a hashable value. /// /// You may use [`hash::value`](crate::hash::value) wrappers when another DataSketches /// implementation requires a specific value hashing strategy. @@ -233,7 +233,7 @@ impl ThetaSketch { self.table.try_insert(value); } - /// Return cardinality estimate + /// Returns the cardinality estimate. /// /// # Examples /// @@ -253,52 +253,52 @@ impl ThetaSketch { num_retained / theta } - /// Return theta as a fraction (0.0 to 1.0) + /// Returns theta as a fraction in `[0.0, 1.0]`. pub fn theta(&self) -> f64 { self.table.theta() as f64 / MAX_THETA as f64 } - /// Return theta as u64 + /// Returns theta as a `u64`. pub fn theta64(&self) -> u64 { self.table.theta() } - /// Return 16-bit seed hash. + /// Returns the 16-bit seed hash. pub fn seed_hash(&self) -> u16 { self.table.seed_hash() } - /// Check if sketch is empty + /// Returns `true` if the sketch is empty. pub fn is_empty(&self) -> bool { self.table.is_empty() } - /// Check if sketch is in estimation mode + /// Returns `true` if the sketch is in estimation mode. pub fn is_estimation_mode(&self) -> bool { self.table.theta() < MAX_THETA } - /// Return number of retained entries + /// Returns the number of retained entries. pub fn num_retained(&self) -> usize { self.table.num_retained() } - /// Return lg_k + /// Returns the configured `lg_k`. pub fn lg_k(&self) -> u8 { self.table.lg_nom_size() } - /// Trim the sketch to nominal size k + /// Trims the sketch to the capacity configured by `lg_k`. pub fn trim(&mut self) { self.table.trim(); } - /// Reset the sketch to empty state + /// Resets the sketch to its empty state. pub fn reset(&mut self) { self.table.reset(); } - /// Return iterator over retained entries. + /// Returns an iterator over retained entries. /// /// # Examples /// @@ -314,9 +314,9 @@ impl ThetaSketch { self.table.iter_entries().copied() } - /// Return this sketch in compact (immutable) form. + /// Returns this sketch in compact, immutable form. /// - /// If `ordered` is true, retained hash values are sorted in ascending order. + /// If `ordered` is `true`, retained hash values are sorted in ascending order. /// /// # Examples /// @@ -343,7 +343,7 @@ impl ThetaSketch { ) } - /// Returns the approximate lower error bound given the specified number of Standard Deviations. + /// Returns the approximate lower error bound for the specified number of standard deviations. /// /// # Arguments /// @@ -377,7 +377,7 @@ impl ThetaSketch { .expect("theta should always be valid") } - /// Returns the approximate upper error bound given the specified number of Standard Deviations. + /// Returns the approximate upper error bound for the specified number of standard deviations. /// /// # Arguments /// @@ -416,7 +416,7 @@ impl ThetaSketch { .expect("theta should always be valid") } - /// Returns the estimated size of the sketch in bytes + /// Returns the estimated size of the sketch in bytes. pub fn estimated_size(&self) -> usize { size_of::() + self.table.estimated_size() } @@ -470,22 +470,22 @@ impl CompactThetaSketch { num_retained / theta } - /// Returns theta as a fraction (0.0 to 1.0). + /// Returns theta as a fraction in `[0.0, 1.0]`. pub fn theta(&self) -> f64 { self.theta as f64 / MAX_THETA as f64 } - /// Returns theta as u64. + /// Returns theta as a `u64`. pub fn theta64(&self) -> u64 { self.theta } - /// Returns true if this sketch is empty. + /// Returns `true` if this sketch is empty. pub fn is_empty(&self) -> bool { self.empty } - /// Returns true if this sketch is in estimation mode. + /// Returns `true` if this sketch is in estimation mode. pub fn is_estimation_mode(&self) -> bool { self.theta < MAX_THETA } @@ -495,7 +495,7 @@ impl CompactThetaSketch { self.entries.len() } - /// Returns true if retained entries are ordered (sorted ascending). + /// Returns `true` if retained entries are ordered (sorted ascending). pub fn is_ordered(&self) -> bool { self.ordered } @@ -505,12 +505,12 @@ impl CompactThetaSketch { self.seed_hash } - /// Return iterator over retained entries. + /// Returns an iterator over retained entries. pub fn iter(&self) -> impl Iterator + '_ { self.entries.iter().copied().map(ThetaEntry::new) } - /// Returns the approximate lower error bound given the specified number of Standard Deviations. + /// Returns the approximate lower error bound for the specified number of standard deviations. pub fn lower_bound(&self, num_std_dev: NumStdDev) -> f64 { if !self.is_estimation_mode() { return self.num_retained() as f64; @@ -519,7 +519,7 @@ impl CompactThetaSketch { .expect("compact theta should always be valid") } - /// Returns the approximate upper error bound given the specified number of Standard Deviations. + /// Returns the approximate upper error bound for the specified number of standard deviations. pub fn upper_bound(&self, num_std_dev: NumStdDev) -> f64 { if !self.is_estimation_mode() { return self.num_retained() as f64; @@ -979,13 +979,13 @@ impl CompactThetaSketch { }) } - /// Returns the estimated size of the sketch in bytes + /// Returns the estimated size of the sketch in bytes. pub fn estimated_size(&self) -> usize { size_of::() + self.entries.capacity() * size_of::() } } -/// Builder for ThetaSketch +/// Builder for [`ThetaSketch`]. #[derive(Debug)] pub struct ThetaSketchBuilder { lg_k: u8, @@ -1006,11 +1006,7 @@ impl Default for ThetaSketchBuilder { } impl ThetaSketchBuilder { - /// Set lg_k (log2 of nominal size k). - /// - /// # Panics - /// - /// If lg_k is not in range [5, 26] + /// Sets `lg_k`, the base-2 logarithm of the nominal capacity. /// /// # Examples /// @@ -1020,6 +1016,10 @@ impl ThetaSketchBuilder { /// let sketch = ThetaSketchBuilder::default().lg_k(12).build(); /// assert_eq!(sketch.lg_k(), 12); /// ``` + /// + /// # Panics + /// + /// Panics if `lg_k` is outside `[5, 26]`. pub fn lg_k(mut self, lg_k: u8) -> Self { assert!( (MIN_LG_K..=MAX_LG_K).contains(&lg_k), @@ -1032,20 +1032,16 @@ impl ThetaSketchBuilder { self } - /// Set resize factor. + /// Sets the resize factor. pub fn resize_factor(mut self, factor: ResizeFactor) -> Self { self.resize_factor = factor; self } - /// Set sampling probability p. + /// Sets the sampling probability. /// /// The sampling probability controls the fraction of hashed values that are retained. - /// Must be greater than 0 to ensure valid theta values for bound calculations. - /// - /// # Panics - /// - /// Panics if p is not in range `(0.0, 1.0]` + /// It must be greater than `0.0` to ensure valid theta values for bound calculations. /// /// # Examples /// @@ -1056,6 +1052,10 @@ impl ThetaSketchBuilder { /// .sampling_probability(0.5) /// .build(); /// ``` + /// + /// # Panics + /// + /// Panics if `probability` is outside `(0.0, 1.0]`. pub fn sampling_probability(mut self, probability: f32) -> Self { assert!( (0.0..=1.0).contains(&probability) && probability > 0.0, @@ -1065,7 +1065,7 @@ impl ThetaSketchBuilder { self } - /// Set hash seed. + /// Sets the hash seed. /// /// # Examples /// @@ -1079,7 +1079,7 @@ impl ThetaSketchBuilder { self } - /// Build the ThetaSketch. + /// Builds the [`ThetaSketch`]. /// /// # Examples /// diff --git a/datasketches/src/thetafamily/theta/union.rs b/datasketches/src/thetafamily/theta/union.rs index 82341bc4..3eedfdec 100644 --- a/datasketches/src/thetafamily/theta/union.rs +++ b/datasketches/src/thetafamily/theta/union.rs @@ -41,13 +41,13 @@ impl UnionMergePolicy for NoopUnionPolicy { } impl ThetaUnion { - /// Update this union with a given sketch. + /// Updates this union with the given sketch. pub fn update<'a>(&mut self, sketch: impl Into>) -> Result<(), Error> { let sketch = sketch.into(); self.state.update(sketch) } - /// Return this union as a compact sketch. + /// Returns this union as a compact sketch. pub fn to_sketch(&self, ordered: bool) -> CompactThetaSketch { let parts = self.state.to_compact_parts(ordered); CompactThetaSketch::from_parts( @@ -63,7 +63,7 @@ impl ThetaUnion { ) } - /// Reset the union to empty state. + /// Resets the union to its empty state. pub fn reset(&mut self) { self.state.reset(); } @@ -95,11 +95,7 @@ impl Default for ThetaUnionBuilder { } impl ThetaUnionBuilder { - /// Set lg_k (log2 of nominal size k). - /// - /// # Panics - /// - /// If lg_k is not in range [5, 26] + /// Sets `lg_k`, the base-2 logarithm of the nominal capacity. /// /// # Examples /// @@ -108,6 +104,10 @@ impl ThetaUnionBuilder { /// /// ThetaUnionBuilder::default().lg_k(12).build(); /// ``` + /// + /// # Panics + /// + /// Panics if `lg_k` is outside `[5, 26]`. pub fn lg_k(mut self, lg_k: u8) -> Self { assert!( (MIN_LG_K..=MAX_LG_K).contains(&lg_k), @@ -117,17 +117,13 @@ impl ThetaUnionBuilder { self } - /// Set resize factor. + /// Sets the resize factor. pub fn resize_factor(mut self, factor: ResizeFactor) -> Self { self.resize_factor = factor; self } - /// Set sampling probability. - /// - /// # Panics - /// - /// Panics if probability is not in range `(0.0, 1.0]` + /// Sets the sampling probability. /// /// # Examples /// @@ -138,6 +134,10 @@ impl ThetaUnionBuilder { /// .sampling_probability(0.5) /// .build(); /// ``` + /// + /// # Panics + /// + /// Panics if `probability` is outside `(0.0, 1.0]`. pub fn sampling_probability(mut self, probability: f32) -> Self { assert!( (0.0..=1.0).contains(&probability) && probability > 0.0, @@ -147,7 +147,7 @@ impl ThetaUnionBuilder { self } - /// Set hash seed. + /// Sets the hash seed. /// /// # Examples /// @@ -161,7 +161,7 @@ impl ThetaUnionBuilder { self } - /// Build the ThetaUnion. + /// Builds the [`ThetaUnion`]. /// /// # Examples /// diff --git a/datasketches/src/thetafamily/tuple/a_not_b.rs b/datasketches/src/thetafamily/tuple/a_not_b.rs index f14ebf30..dbe6c3b6 100644 --- a/datasketches/src/thetafamily/tuple/a_not_b.rs +++ b/datasketches/src/thetafamily/tuple/a_not_b.rs @@ -77,7 +77,7 @@ impl TupleANotB { /// /// The result retains every key of `a` (below the combined theta) that is not present in `b`, /// keeping the summaries from `a`. Summary values in `b` are ignored and need not be - /// cloneable. If `ordered` is true, the retained entries are sorted ascending by hash. + /// cloneable. If `ordered` is `true`, the retained entries are sorted ascending by hash. /// /// # Errors /// diff --git a/datasketches/src/thetafamily/tuple/hash_table.rs b/datasketches/src/thetafamily/tuple/hash_table.rs index 00e0fec8..04b26f6e 100644 --- a/datasketches/src/thetafamily/tuple/hash_table.rs +++ b/datasketches/src/thetafamily/tuple/hash_table.rs @@ -45,7 +45,7 @@ impl TupleEntry { Self { hash, summary } } - /// Return the hash used as this entry's key. + /// Returns the hash used as this entry's key. pub fn hash(&self) -> u64 { self.hash.get() } @@ -72,9 +72,9 @@ impl SketchEntry for TupleEntry { impl TupleHashTable { /// Hashes a key and inserts or updates its summary via a single callback. /// - /// See [`try_insert_hash`](Self::try_insert_hash) for the callback contract. Returns true if a - /// new entry was created, false if the key already existed or the hash was screened out by - /// theta. + /// See [`try_insert_hash`](Self::try_insert_hash) for the callback contract. Returns `true` if + /// a new entry was created, or `false` if the key already existed or the hash was screened + /// out by theta. pub fn try_insert(&mut self, key: T, f: F) -> bool where T: Hash, @@ -86,8 +86,8 @@ impl TupleHashTable { /// Inserts or updates the summary slot for a pre-hashed key. /// - /// Returns true if a new entry was created, false otherwise (existing key, declined insertion, - /// or a hash screened out by theta). + /// Returns `true` if a new entry was created, or `false` otherwise (existing key, declined + /// insertion, or a hash screened out by theta). pub fn try_insert_hash(&mut self, hash: u64, f: F) -> bool where F: FnOnce(Option<&mut S>) -> Option, diff --git a/datasketches/src/thetafamily/tuple/intersection.rs b/datasketches/src/thetafamily/tuple/intersection.rs index 273d7957..a93712cf 100644 --- a/datasketches/src/thetafamily/tuple/intersection.rs +++ b/datasketches/src/thetafamily/tuple/intersection.rs @@ -140,7 +140,7 @@ where /// Returns the intersection result as a compact Tuple sketch. /// - /// If `ordered` is true, retained entries are sorted ascending by hash. + /// If `ordered` is `true`, retained entries are sorted ascending by hash. /// /// # Panics /// diff --git a/datasketches/src/thetafamily/tuple/mod.rs b/datasketches/src/thetafamily/tuple/mod.rs index fa90d215..c159d354 100644 --- a/datasketches/src/thetafamily/tuple/mod.rs +++ b/datasketches/src/thetafamily/tuple/mod.rs @@ -17,10 +17,10 @@ //! Tuple sketch implementation. //! -//! A Tuple sketch is an extension of the Theta sketch: in addition to the retained -//! hash values it keeps a user-defined summary associated with every retained key. The hash table -//! mechanics (theta screening, resize, rebuild to nominal size k) mirror the Theta sketch, with the -//! added requirement that colliding keys merge their summaries. +//! A Tuple sketch is an extension of the Theta sketch: in addition to the retained hash values it +//! keeps a user-defined summary associated with every retained key. The hash table mechanics +//! (theta screening, resize, rebuild to the capacity configured by `lg_k`) mirror the Theta sketch, +//! with the added requirement that colliding keys merge their summaries. //! //! Custom summary behavior is supplied externally through policy objects: [`SummaryPolicy`] //! creates summaries, while [`SummaryUpdatePolicy`] folds update values into them. Summaries that diff --git a/datasketches/src/thetafamily/tuple/sketch.rs b/datasketches/src/thetafamily/tuple/sketch.rs index aafb0c32..f4c7319c 100644 --- a/datasketches/src/thetafamily/tuple/sketch.rs +++ b/datasketches/src/thetafamily/tuple/sketch.rs @@ -300,12 +300,12 @@ where num_retained / theta } - /// Returns theta as a fraction (0.0 to 1.0). + /// Returns theta as a fraction in `[0.0, 1.0]`. pub fn theta(&self) -> f64 { self.table.theta() as f64 / MAX_THETA as f64 } - /// Returns theta as `u64`. + /// Returns theta as a `u64`. pub fn theta64(&self) -> u64 { self.table.theta() } @@ -315,12 +315,12 @@ where self.table.seed_hash() } - /// Returns true if the sketch is empty. + /// Returns `true` if the sketch is empty. pub fn is_empty(&self) -> bool { self.table.is_empty() } - /// Returns true if the sketch is in estimation mode. + /// Returns `true` if the sketch is in estimation mode. pub fn is_estimation_mode(&self) -> bool { self.table.theta() < MAX_THETA } @@ -330,12 +330,12 @@ where self.table.num_retained() } - /// Returns lg_k (log2 of the nominal size k). + /// Returns the configured `lg_k`. pub fn lg_k(&self) -> u8 { self.table.lg_nom_size() } - /// Trims the sketch to the nominal size k. + /// Trims the sketch to the capacity configured by `lg_k`. pub fn trim(&mut self) { self.table.trim(); } @@ -384,9 +384,9 @@ where P: SummaryPolicy, P::Summary: Clone, { - /// Returns this sketch in compact (immutable) form. + /// Returns this sketch in compact, immutable form. /// - /// If `ordered` is true, retained entries are sorted by hash in ascending order. + /// If `ordered` is `true`, retained entries are sorted by hash in ascending order. /// /// # Examples /// @@ -471,12 +471,12 @@ impl CompactTupleSketch { self.theta } - /// Returns true if the sketch is empty. + /// Returns `true` if the sketch is empty. pub fn is_empty(&self) -> bool { self.empty } - /// Returns true if the sketch is in estimation mode. + /// Returns `true` if the sketch is in estimation mode. pub fn is_estimation_mode(&self) -> bool { self.theta < MAX_THETA } @@ -486,7 +486,7 @@ impl CompactTupleSketch { self.entries.len() } - /// Returns true if retained entries are ordered (sorted ascending by hash). + /// Returns `true` if retained entries are ordered (sorted ascending by hash). pub fn is_ordered(&self) -> bool { self.ordered } @@ -543,10 +543,6 @@ impl CompactTupleSketch { /// Serializes this sketch into the compact Tuple binary format. /// - /// Each summary is encoded by its [`TupleSummaryValue`] implementation. The layout matches the - /// Java/C++ Tuple sketches, so the output can be read by those implementations given a - /// compatible summary encoding. - /// /// # Examples /// /// ``` @@ -559,6 +555,10 @@ impl CompactTupleSketch { /// let bytes = sketch.compact(true).serialize(); /// assert!(!bytes.is_empty()); /// ``` + /// + /// Each summary is encoded by its [`TupleSummaryValue`] implementation. The layout matches the + /// Java/C++ Tuple sketches, so the output can be read by those implementations given a + /// compatible summary encoding. pub fn serialize(&self) -> Vec where S: TupleSummaryValue, @@ -765,11 +765,11 @@ where } } - /// Sets lg_k (log2 of the nominal size k). + /// Sets `lg_k`, the base-2 logarithm of the nominal capacity. /// /// # Panics /// - /// Panics if lg_k is not in range [5, 26]. + /// Panics if `lg_k` is outside `[5, 26]`. pub fn lg_k(mut self, lg_k: u8) -> Self { assert!( (MIN_LG_K..=MAX_LG_K).contains(&lg_k), @@ -785,11 +785,11 @@ where self } - /// Sets the sampling probability p. + /// Sets the sampling probability. /// /// # Panics /// - /// Panics if p is not in range `(0.0, 1.0]`. + /// Panics if `probability` is outside `(0.0, 1.0]`. pub fn sampling_probability(mut self, probability: f32) -> Self { assert!( (0.0..=1.0).contains(&probability) && probability > 0.0, diff --git a/datasketches/src/thetafamily/tuple/union.rs b/datasketches/src/thetafamily/tuple/union.rs index 788d4180..25bef18c 100644 --- a/datasketches/src/thetafamily/tuple/union.rs +++ b/datasketches/src/thetafamily/tuple/union.rs @@ -99,7 +99,7 @@ where /// Returns the union as a [`CompactTupleSketch`]. /// - /// If `ordered` is true, retained entries are sorted ascending by hash. + /// If `ordered` is `true`, retained entries are sorted ascending by hash. pub fn to_sketch(&self, ordered: bool) -> CompactTupleSketch where P::Summary: Clone, @@ -168,11 +168,11 @@ where } } - /// Sets lg_k (log2 of the nominal size k). + /// Sets `lg_k`, the base-2 logarithm of the nominal capacity. /// /// # Panics /// - /// Panics if lg_k is not in range [5, 26]. + /// Panics if `lg_k` is outside `[5, 26]`. pub fn lg_k(mut self, lg_k: u8) -> Self { assert!( (MIN_LG_K..=MAX_LG_K).contains(&lg_k), @@ -188,11 +188,11 @@ where self } - /// Sets the sampling probability p. + /// Sets the sampling probability. /// /// # Panics /// - /// Panics if p is not in range `(0.0, 1.0]`. + /// Panics if `probability` is outside `(0.0, 1.0]`. pub fn sampling_probability(mut self, probability: f32) -> Self { assert!( (0.0..=1.0).contains(&probability) && probability > 0.0, From 953fa7aee053e881942daf7ea5dfeeb91b4c0c6b Mon Sep 17 00:00:00 2001 From: tison Date: Tue, 11 Aug 2026 11:16:45 +0800 Subject: [PATCH 2/2] docs: address rustdoc review feedback --- CONTRIBUTING.md | 3 +- datasketches/src/bloom/builder.rs | 20 +++--- datasketches/src/bloom/sketch.rs | 36 +++++------ datasketches/src/countmin/sketch.rs | 42 ++++++------ datasketches/src/frequencies/sketch.rs | 8 +-- datasketches/src/hll/sketch.rs | 8 +-- datasketches/src/hll/union.rs | 8 +-- datasketches/src/tdigest/sketch.rs | 68 ++++++++++---------- datasketches/src/thetafamily/theta/sketch.rs | 16 ++--- datasketches/src/thetafamily/theta/union.rs | 16 ++--- datasketches/src/thetafamily/tuple/sketch.rs | 8 +-- 11 files changed, 116 insertions(+), 117 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9b3e17df..4b2e84e9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -64,8 +64,7 @@ cargo x lint - Describe types with noun phrases and API behavior with third-person present-tense verbs such as `Creates`, `Updates`, and `Returns`. - End summary sentences with punctuation, and format Rust identifiers, literals, and numeric ranges as inline code. -- Use `lg_k` for the algorithm parameter in prose. Use a different name only when referring to an exact Rust identifier or an external serialization format. -- When applicable, order sections as `# Examples`, `# Errors`, and `# Panics`, followed by compatibility notes. Include only sections that describe an actual contract. +- Put contract sections and compatibility notes before examples. When applicable, order sections as `# Errors`, `# Panics`, and `# Examples`. Include only sections that describe an actual contract. ## Integration test layout diff --git a/datasketches/src/bloom/builder.rs b/datasketches/src/bloom/builder.rs index c52aa464..6fece08b 100644 --- a/datasketches/src/bloom/builder.rs +++ b/datasketches/src/bloom/builder.rs @@ -55,6 +55,10 @@ impl BloomFilterBuilder { /// * `max_items`: Maximum expected number of distinct items. /// * `fpp`: Target false positive probability (for example, `0.01` for `1%`). /// + /// # Panics + /// + /// Panics if `max_items` is `0` or `fpp` is outside `(0.0, 1.0]`. + /// /// # Examples /// /// ``` @@ -65,10 +69,6 @@ impl BloomFilterBuilder { /// .seed(42) /// .build(); /// ``` - /// - /// # Panics - /// - /// Panics if `max_items` is `0` or `fpp` is outside `(0.0, 1.0]`. pub fn with_accuracy(max_items: u64, fpp: f64) -> Self { assert!(max_items > 0, "max_items must be greater than 0"); assert!( @@ -99,6 +99,12 @@ impl BloomFilterBuilder { /// * `num_bits`: Total number of bits in the filter. /// * `num_hashes`: Number of hash functions to use. /// + /// # Panics + /// + /// Panics if any of: + /// * `num_bits < Self::MIN_NUM_BITS` or `num_bits > Self::MAX_NUM_BITS`. + /// * `num_hashes < Self::MIN_NUM_HASHES` or `num_hashes > Self::MAX_NUM_HASHES`. + /// /// # Examples /// /// ``` @@ -106,12 +112,6 @@ impl BloomFilterBuilder { /// /// let filter = BloomFilterBuilder::with_size(10_000, 7).build(); /// ``` - /// - /// # Panics - /// - /// Panics if any of: - /// * `num_bits < Self::MIN_NUM_BITS` or `num_bits > Self::MAX_NUM_BITS`. - /// * `num_hashes < Self::MIN_NUM_HASHES` or `num_hashes > Self::MAX_NUM_HASHES`. pub fn with_size(num_bits: u64, num_hashes: u16) -> Self { assert!( (Self::MIN_NUM_BITS..=Self::MAX_NUM_BITS).contains(&num_bits), diff --git a/datasketches/src/bloom/sketch.rs b/datasketches/src/bloom/sketch.rs index 6e4c1c1e..e8734e73 100644 --- a/datasketches/src/bloom/sketch.rs +++ b/datasketches/src/bloom/sketch.rs @@ -150,6 +150,11 @@ impl BloomFilter { /// After merging, this filter will recognize items from either filter /// (plus any false positives from either). /// + /// # Panics + /// + /// Panics if the filters are not compatible (different size, hashes, or seed). + /// Use [`is_compatible()`](Self::is_compatible) to check first. + /// /// # Examples /// /// ``` @@ -169,11 +174,6 @@ impl BloomFilter { /// assert!(f1.contains(&"a")); /// assert!(f1.contains(&"b")); /// ``` - /// - /// # Panics - /// - /// Panics if the filters are not compatible (different size, hashes, or seed). - /// Use [`is_compatible()`](Self::is_compatible) to check first. pub fn union(&mut self, other: &BloomFilter) { assert!( self.is_compatible(other), @@ -194,6 +194,10 @@ impl BloomFilter { /// After intersection, this filter will recognize only items present in both /// filters (plus false positives). /// + /// # Panics + /// + /// Panics if the filters are not compatible (different size, hashes, or seed). + /// /// # Examples /// /// ``` @@ -215,10 +219,6 @@ impl BloomFilter { /// assert!(f1.contains(&"b")); // In both /// // "a" and "c" likely return false now /// ``` - /// - /// # Panics - /// - /// Panics if the filters are not compatible (different size, hashes, or seed). pub fn intersect(&mut self, other: &BloomFilter) { assert!( self.is_compatible(other), @@ -324,6 +324,8 @@ impl BloomFilter { /// Serializes the filter to a byte vector. /// + /// The format is compatible with other Apache DataSketches implementations. + /// /// # Examples /// /// ``` @@ -337,8 +339,6 @@ impl BloomFilter { /// let restored = BloomFilter::deserialize(&bytes).unwrap(); /// assert!(restored.contains(&"test")); /// ``` - /// - /// The format is compatible with other Apache DataSketches implementations. pub fn serialize(&self) -> Vec { let is_empty = self.is_empty(); let preamble_longs = if is_empty { @@ -384,6 +384,13 @@ impl BloomFilter { /// Deserializes a filter from bytes. /// + /// # Errors + /// + /// Returns an error if: + /// * The data is truncated or corrupted. + /// * The family ID does not identify a Bloom filter. + /// * The serial version is unsupported. + /// /// # Examples /// /// ``` @@ -396,13 +403,6 @@ impl BloomFilter { /// let restored = BloomFilter::deserialize(&bytes).unwrap(); /// assert_eq!(original, restored); /// ``` - /// - /// # Errors - /// - /// Returns an error if: - /// * The data is truncated or corrupted. - /// * The family ID does not identify a Bloom filter. - /// * The serial version is unsupported. pub fn deserialize(bytes: &[u8]) -> Result { let mut cursor = SketchSlice::new(bytes); diff --git a/datasketches/src/countmin/sketch.rs b/datasketches/src/countmin/sketch.rs index 1e354c78..84bbfed3 100644 --- a/datasketches/src/countmin/sketch.rs +++ b/datasketches/src/countmin/sketch.rs @@ -57,6 +57,11 @@ pub struct CountMinSketch { impl CountMinSketch { /// Creates a new CountMin sketch with the default seed. /// + /// # Panics + /// + /// Panics if `num_hashes` is `0`, `num_buckets` is less than `3`, or the + /// total table size exceeds the supported limit. + /// /// # Examples /// /// ``` @@ -65,17 +70,20 @@ impl CountMinSketch { /// let sketch = CountMinSketch::::new(4, 128); /// assert_eq!(sketch.num_buckets(), 128); /// ``` - /// - /// # Panics - /// - /// Panics if `num_hashes` is `0`, `num_buckets` is less than `3`, or the - /// total table size exceeds the supported limit. pub fn new(num_hashes: u8, num_buckets: u32) -> Self { Self::with_seed(num_hashes, num_buckets, DEFAULT_UPDATE_SEED) } /// Creates a new CountMin sketch with the provided seed. /// + /// # Panics + /// + /// Panics if any of: + /// * `num_hashes` is `0`. + /// * `num_buckets` is less than `3`. + /// * The total table size exceeds the supported limit. + /// * The computed seed hash is zero. + /// /// # Examples /// /// ``` @@ -84,14 +92,6 @@ impl CountMinSketch { /// let sketch = CountMinSketch::::with_seed(4, 64, 42); /// assert_eq!(sketch.seed(), 42); /// ``` - /// - /// # Panics - /// - /// Panics if any of: - /// * `num_hashes` is `0`. - /// * `num_buckets` is less than `3`. - /// * The total table size exceeds the supported limit. - /// * The computed seed hash is zero. pub fn with_seed(num_hashes: u8, num_buckets: u32, seed: u64) -> Self { let entries = entries_for_config(num_hashes, num_buckets); Self::make(num_hashes, num_buckets, seed, entries) @@ -233,6 +233,10 @@ impl CountMinSketch { /// Merges another sketch into this one. /// + /// # Panics + /// + /// Panics if the sketches have incompatible configurations. + /// /// # Examples /// /// ``` @@ -247,10 +251,6 @@ impl CountMinSketch { /// left.merge(&right); /// assert!(left.estimate("banana") >= 2); /// ``` - /// - /// # Panics - /// - /// Panics if the sketches have incompatible configurations. pub fn merge(&mut self, other: &CountMinSketch) { if std::ptr::eq(self, other) { panic!("Cannot merge a sketch with itself."); @@ -455,6 +455,10 @@ impl CountMinSketch { /// Values are truncated toward zero after multiplication; choose `decay` in `(0, 1]`. /// The total weight is scaled by the same factor to keep bounds consistent. /// + /// # Panics + /// + /// Panics if `decay` is not finite or is outside `(0, 1]`. + /// /// # Examples /// /// ``` @@ -465,10 +469,6 @@ impl CountMinSketch { /// sketch.decay(0.5); /// assert!(sketch.estimate("apple") >= 1); /// ``` - /// - /// # Panics - /// - /// Panics if `decay` is not finite or is outside `(0, 1]`. pub fn decay(&mut self, decay: f64) { assert!(decay > 0.0 && decay <= 1.0, "decay must be within (0, 1]"); for c in &mut self.counts { diff --git a/datasketches/src/frequencies/sketch.rs b/datasketches/src/frequencies/sketch.rs index e5a5a47e..41b1ac7a 100644 --- a/datasketches/src/frequencies/sketch.rs +++ b/datasketches/src/frequencies/sketch.rs @@ -108,6 +108,10 @@ impl FrequentItemsSketch { /// The maximum map capacity is `0.75 * max_map_size`, and the internal map grows /// from a small starting size up to the maximum as needed. /// + /// # Panics + /// + /// Panics if `max_map_size` is not a power of two. + /// /// # Examples /// /// ``` @@ -118,10 +122,6 @@ impl FrequentItemsSketch { /// sketch.update(2); /// assert_eq!(sketch.num_active_items(), 2); /// ``` - /// - /// # Panics - /// - /// Panics if `max_map_size` is not a power of two. pub fn new(max_map_size: usize) -> Self { assert!( max_map_size.is_power_of_two(), diff --git a/datasketches/src/hll/sketch.rs b/datasketches/src/hll/sketch.rs index 946d0df1..21f5d53b 100644 --- a/datasketches/src/hll/sketch.rs +++ b/datasketches/src/hll/sketch.rs @@ -75,6 +75,10 @@ impl HllSketch { /// * `lg_k = 21`: 2M buckets, ~0.4% relative error. /// * `hll_type`: Target HLL array type (`Hll4`, `Hll6`, or `Hll8`). /// + /// # Panics + /// + /// Panics if `lg_config_k` is outside `[4, 21]`. + /// /// # Examples /// /// ``` @@ -84,10 +88,6 @@ impl HllSketch { /// let sketch = HllSketch::new(12, HllType::Hll8); /// assert_eq!(sketch.lg_config_k(), 12); /// ``` - /// - /// # Panics - /// - /// Panics if `lg_config_k` is outside `[4, 21]`. pub fn new(lg_config_k: u8, hll_type: HllType) -> Self { assert!( (4..=21).contains(&lg_config_k), diff --git a/datasketches/src/hll/union.rs b/datasketches/src/hll/union.rs index acf2f3bf..df86272d 100644 --- a/datasketches/src/hll/union.rs +++ b/datasketches/src/hll/union.rs @@ -66,6 +66,10 @@ impl HllUnion { /// * `lg_max_k`: Maximum `lg_k` in `[4, 21]`. This determines the maximum precision the union /// can handle. Input sketches with a larger `lg_k` are downsampled. /// + /// # Panics + /// + /// Panics if `lg_max_k` is outside `[4, 21]`. + /// /// # Examples /// /// ``` @@ -77,10 +81,6 @@ impl HllUnion { /// let result = union.to_sketch(HllType::Hll8); /// assert_eq!(result.estimate(), 1.0); /// ``` - /// - /// # Panics - /// - /// Panics if `lg_max_k` is outside `[4, 21]`. pub fn new(lg_max_k: u8) -> Self { assert!( (4..=21).contains(&lg_max_k), diff --git a/datasketches/src/tdigest/sketch.rs b/datasketches/src/tdigest/sketch.rs index f3fabfbb..8ae557f3 100644 --- a/datasketches/src/tdigest/sketch.rs +++ b/datasketches/src/tdigest/sketch.rs @@ -70,6 +70,10 @@ impl TDigestMut { /// /// The fallible version of this method is [`TDigestMut::try_new`]. /// + /// # Panics + /// + /// Panics if `k` is less than `10`. + /// /// # Examples /// /// ``` @@ -78,10 +82,6 @@ impl TDigestMut { /// let sketch = TDigestMut::new(100); /// assert_eq!(sketch.k(), 100); /// ``` - /// - /// # Panics - /// - /// Panics if `k` is less than `10`. pub fn new(k: u16) -> Self { Self::make( k, @@ -98,6 +98,10 @@ impl TDigestMut { /// /// The panicking version of this method is [`TDigestMut::new`]. /// + /// # Errors + /// + /// Returns an error if `k` is less than `10`. + /// /// # Examples /// /// ``` @@ -106,10 +110,6 @@ impl TDigestMut { /// let sketch = TDigestMut::try_new(20).unwrap(); /// assert_eq!(sketch.k(), 20); /// ``` - /// - /// # Errors - /// - /// Returns an error if `k` is less than `10`. pub fn try_new(k: u16) -> Result { if k < 10 { return Err(Error::invalid_argument(format!( @@ -485,6 +485,14 @@ impl TDigestMut { /// Deserializes a mutable t-digest from bytes. /// + /// Supports reading compact format with (float, int) centroids as opposed to (double, long) to + /// represent (mean, weight). [^1] + /// + /// Supports reading format of the reference implementation (auto-detected) [^2]. + /// + /// [^1]: This is to support reading the `tdigest` format from the C++ implementation. + /// [^2]: + /// /// # Examples /// /// ``` @@ -497,14 +505,6 @@ impl TDigestMut { /// let decoded = TDigestMut::deserialize(&bytes, false).unwrap(); /// assert_eq!(decoded.max_value(), Some(2.0)); /// ``` - /// - /// Supports reading compact format with (float, int) centroids as opposed to (double, long) to - /// represent (mean, weight). [^1] - /// - /// Supports reading format of the reference implementation (auto-detected) [^2]. - /// - /// [^1]: This is to support reading the `tdigest` format from the C++ implementation. - /// [^2]: pub fn deserialize(bytes: &[u8], is_f32: bool) -> Result { let mut cursor = SketchSlice::new(bytes); @@ -897,6 +897,11 @@ impl TDigest { /// /// Returns `None` if this t-digest is empty. /// + /// # Panics + /// + /// Panics if `split_points` is not unique, not monotonically increasing, or contains `NaN` + /// values. + /// /// # Examples /// /// ``` @@ -910,11 +915,6 @@ impl TDigest { /// let cdf = digest.cdf(&[1.5]).unwrap(); /// assert_eq!(cdf.len(), 2); /// ``` - /// - /// # Panics - /// - /// Panics if `split_points` is not unique, not monotonically increasing, or contains `NaN` - /// values. pub fn cdf(&self, split_points: &[f64]) -> Option> { self.view().cdf(split_points) } @@ -934,6 +934,11 @@ impl TDigest { /// /// Returns `None` if this t-digest is empty. /// + /// # Panics + /// + /// Panics if `split_points` is not unique, not monotonically increasing, or contains `NaN` + /// values. + /// /// # Examples /// /// ``` @@ -947,11 +952,6 @@ impl TDigest { /// let pmf = digest.pmf(&[1.5]).unwrap(); /// assert_eq!(pmf.len(), 2); /// ``` - /// - /// # Panics - /// - /// Panics if `split_points` is not unique, not monotonically increasing, or contains `NaN` - /// values. pub fn pmf(&self, split_points: &[f64]) -> Option> { self.view().pmf(split_points) } @@ -960,6 +960,10 @@ impl TDigest { /// /// Returns `None` if this t-digest is empty. /// + /// # Panics + /// + /// Panics if the value is `NaN`. + /// /// # Examples /// /// ``` @@ -973,10 +977,6 @@ impl TDigest { /// let rank = digest.rank(2.0).unwrap(); /// assert!((0.0..=1.0).contains(&rank)); /// ``` - /// - /// # Panics - /// - /// Panics if the value is `NaN`. pub fn rank(&self, value: f64) -> Option { assert!(!value.is_nan(), "value must not be NaN"); self.view().rank(value) @@ -986,6 +986,10 @@ impl TDigest { /// /// Returns `None` if this t-digest is empty. /// + /// # Panics + /// + /// Panics if `rank` is outside `[0.0, 1.0]`. + /// /// # Examples /// /// ``` @@ -999,10 +1003,6 @@ impl TDigest { /// let q = digest.quantile(0.5).unwrap(); /// assert!((1.0..=3.0).contains(&q)); /// ``` - /// - /// # Panics - /// - /// Panics if `rank` is outside `[0.0, 1.0]`. pub fn quantile(&self, rank: f64) -> Option { assert!((0.0..=1.0).contains(&rank), "rank must be in [0.0, 1.0]"); self.view().quantile(rank) diff --git a/datasketches/src/thetafamily/theta/sketch.rs b/datasketches/src/thetafamily/theta/sketch.rs index 501e0d72..ed91d976 100644 --- a/datasketches/src/thetafamily/theta/sketch.rs +++ b/datasketches/src/thetafamily/theta/sketch.rs @@ -1008,6 +1008,10 @@ impl Default for ThetaSketchBuilder { impl ThetaSketchBuilder { /// Sets `lg_k`, the base-2 logarithm of the nominal capacity. /// + /// # Panics + /// + /// Panics if `lg_k` is outside `[5, 26]`. + /// /// # Examples /// /// ``` @@ -1016,10 +1020,6 @@ impl ThetaSketchBuilder { /// let sketch = ThetaSketchBuilder::default().lg_k(12).build(); /// assert_eq!(sketch.lg_k(), 12); /// ``` - /// - /// # Panics - /// - /// Panics if `lg_k` is outside `[5, 26]`. pub fn lg_k(mut self, lg_k: u8) -> Self { assert!( (MIN_LG_K..=MAX_LG_K).contains(&lg_k), @@ -1043,6 +1043,10 @@ impl ThetaSketchBuilder { /// The sampling probability controls the fraction of hashed values that are retained. /// It must be greater than `0.0` to ensure valid theta values for bound calculations. /// + /// # Panics + /// + /// Panics if `probability` is outside `(0.0, 1.0]`. + /// /// # Examples /// /// ``` @@ -1052,10 +1056,6 @@ impl ThetaSketchBuilder { /// .sampling_probability(0.5) /// .build(); /// ``` - /// - /// # Panics - /// - /// Panics if `probability` is outside `(0.0, 1.0]`. pub fn sampling_probability(mut self, probability: f32) -> Self { assert!( (0.0..=1.0).contains(&probability) && probability > 0.0, diff --git a/datasketches/src/thetafamily/theta/union.rs b/datasketches/src/thetafamily/theta/union.rs index 3eedfdec..d10b1d24 100644 --- a/datasketches/src/thetafamily/theta/union.rs +++ b/datasketches/src/thetafamily/theta/union.rs @@ -97,6 +97,10 @@ impl Default for ThetaUnionBuilder { impl ThetaUnionBuilder { /// Sets `lg_k`, the base-2 logarithm of the nominal capacity. /// + /// # Panics + /// + /// Panics if `lg_k` is outside `[5, 26]`. + /// /// # Examples /// /// ``` @@ -104,10 +108,6 @@ impl ThetaUnionBuilder { /// /// ThetaUnionBuilder::default().lg_k(12).build(); /// ``` - /// - /// # Panics - /// - /// Panics if `lg_k` is outside `[5, 26]`. pub fn lg_k(mut self, lg_k: u8) -> Self { assert!( (MIN_LG_K..=MAX_LG_K).contains(&lg_k), @@ -125,6 +125,10 @@ impl ThetaUnionBuilder { /// Sets the sampling probability. /// + /// # Panics + /// + /// Panics if `probability` is outside `(0.0, 1.0]`. + /// /// # Examples /// /// ``` @@ -134,10 +138,6 @@ impl ThetaUnionBuilder { /// .sampling_probability(0.5) /// .build(); /// ``` - /// - /// # Panics - /// - /// Panics if `probability` is outside `(0.0, 1.0]`. pub fn sampling_probability(mut self, probability: f32) -> Self { assert!( (0.0..=1.0).contains(&probability) && probability > 0.0, diff --git a/datasketches/src/thetafamily/tuple/sketch.rs b/datasketches/src/thetafamily/tuple/sketch.rs index f4c7319c..8cb9c53b 100644 --- a/datasketches/src/thetafamily/tuple/sketch.rs +++ b/datasketches/src/thetafamily/tuple/sketch.rs @@ -543,6 +543,10 @@ impl CompactTupleSketch { /// Serializes this sketch into the compact Tuple binary format. /// + /// Each summary is encoded by its [`TupleSummaryValue`] implementation. The layout matches the + /// Java/C++ Tuple sketches, so the output can be read by those implementations given a + /// compatible summary encoding. + /// /// # Examples /// /// ``` @@ -555,10 +559,6 @@ impl CompactTupleSketch { /// let bytes = sketch.compact(true).serialize(); /// assert!(!bytes.is_empty()); /// ``` - /// - /// Each summary is encoded by its [`TupleSummaryValue`] implementation. The layout matches the - /// Java/C++ Tuple sketches, so the output can be read by those implementations given a - /// compatible summary encoding. pub fn serialize(&self) -> Vec where S: TupleSummaryValue,