1use core::fmt::Debug;
7
8#[cfg(feature = "std")]
9use std::{string::String, vec::Vec};
10
11#[cfg(not(feature = "std"))]
12extern crate alloc;
13#[cfg(not(feature = "std"))]
14use alloc::{string::String, vec::Vec};
15
16#[derive(Debug, Clone, PartialEq, Eq)]
18pub enum MergeError {
19 IncompatibleConfig {
21 expected: String,
22 found: String,
23 },
24 VersionMismatch {
26 expected: u32,
27 found: u32,
28 },
29}
30
31impl core::fmt::Display for MergeError {
32 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
33 match self {
34 MergeError::IncompatibleConfig { expected, found } => {
35 write!(f, "incompatible config: expected {}, found {}", expected, found)
36 }
37 MergeError::VersionMismatch { expected, found } => {
38 write!(f, "version mismatch: expected {}, found {}", expected, found)
39 }
40 }
41 }
42}
43
44#[cfg(feature = "std")]
45impl std::error::Error for MergeError {}
46
47#[derive(Debug, Clone, PartialEq, Eq)]
49pub enum DecodeError {
50 BufferTooShort { expected: usize, found: usize },
52 InvalidHeader,
54 UnsupportedVersion(u32),
56 Corrupted(String),
58}
59
60impl core::fmt::Display for DecodeError {
61 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
62 match self {
63 DecodeError::BufferTooShort { expected, found } => {
64 write!(f, "buffer too short: expected {}, found {}", expected, found)
65 }
66 DecodeError::InvalidHeader => write!(f, "invalid header"),
67 DecodeError::UnsupportedVersion(v) => write!(f, "unsupported version: {}", v),
68 DecodeError::Corrupted(msg) => write!(f, "corrupted data: {}", msg),
69 }
70 }
71}
72
73#[cfg(feature = "std")]
74impl std::error::Error for DecodeError {}
75
76#[derive(Debug, Clone, Copy, PartialEq)]
78pub struct ErrorBounds {
79 pub lower: f64,
81 pub estimate: f64,
83 pub upper: f64,
85 pub confidence: f64,
87}
88
89impl ErrorBounds {
90 pub fn new(lower: f64, estimate: f64, upper: f64, confidence: f64) -> Self {
92 Self {
93 lower,
94 estimate,
95 upper,
96 confidence,
97 }
98 }
99
100 pub fn contains(&self, value: f64) -> bool {
102 value >= self.lower && value <= self.upper
103 }
104
105 pub fn width(&self) -> f64 {
107 self.upper - self.lower
108 }
109
110 pub fn relative_width(&self) -> f64 {
112 if self.estimate == 0.0 {
113 0.0
114 } else {
115 self.width() / self.estimate
116 }
117 }
118}
119
120pub trait Sketch: Clone + Debug {
122 type Item: ?Sized;
124
125 fn update(&mut self, item: &Self::Item);
127
128 fn merge(&mut self, other: &Self) -> Result<(), MergeError>;
132
133 fn clear(&mut self);
135
136 fn size_bytes(&self) -> usize;
138
139 fn count(&self) -> u64;
141
142 fn is_empty(&self) -> bool {
144 self.count() == 0
145 }
146}
147
148pub trait CardinalitySketch: Sketch {
150 fn estimate(&self) -> f64;
152
153 fn error_bounds(&self, confidence: f64) -> ErrorBounds;
155
156 fn relative_error(&self) -> f64;
160
161 fn estimate_with_bounds(&self) -> ErrorBounds {
163 self.error_bounds(0.95)
164 }
165}
166
167pub trait FrequencySketch: Sketch {
169 fn estimate_frequency(&self, item: &Self::Item) -> u64;
171
172 fn exceeds_threshold(&self, item: &Self::Item, threshold: u64) -> bool {
174 self.estimate_frequency(item) >= threshold
175 }
176}
177
178pub trait HeavyHitters: FrequencySketch
180where
181 Self::Item: Sized + Clone,
182{
183 fn heavy_hitters(&self, threshold: f64) -> Vec<(Self::Item, u64)>;
187
188 fn top_k(&self, k: usize) -> Vec<(Self::Item, u64)>;
190}
191
192pub trait QuantileSketch: Sketch {
194 type Value: PartialOrd + Clone;
196
197 fn add(&mut self, value: Self::Value);
199
200 fn quantile(&self, rank: f64) -> Option<Self::Value>;
204
205 fn rank(&self, value: &Self::Value) -> f64;
207
208 fn cdf(&self, value: &Self::Value) -> f64 {
210 self.rank(value)
211 }
212
213 fn min(&self) -> Option<Self::Value>;
215
216 fn max(&self) -> Option<Self::Value>;
218
219 fn median(&self) -> Option<Self::Value> {
221 self.quantile(0.5)
222 }
223
224 fn quantiles(&self, ranks: &[f64]) -> Vec<Option<Self::Value>> {
226 ranks.iter().map(|&r| self.quantile(r)).collect()
227 }
228}
229
230pub trait MembershipSketch: Sketch {
232 fn contains(&self, item: &Self::Item) -> bool;
237
238 fn false_positive_rate(&self) -> f64;
240
241 fn len(&self) -> usize;
243
244 fn is_filter_empty(&self) -> bool {
246 self.len() == 0
247 }
248}
249
250pub trait SetSketch: Sketch {
252 fn union(&self, other: &Self) -> Self;
254
255 fn intersection(&self, other: &Self) -> Self;
257
258 fn difference(&self, other: &Self) -> Self;
260
261 fn jaccard_similarity(&self, other: &Self) -> f64;
263}
264
265pub trait SamplingSketch: Sketch
267where
268 Self::Item: Sized + Clone,
269{
270 fn sample(&self) -> &[Self::Item];
272
273 fn capacity(&self) -> usize;
275
276 fn sample_size(&self) -> usize {
278 self.sample().len()
279 }
280}
281
282#[cfg(test)]
283mod tests {
284 use super::*;
285
286 #[test]
287 fn test_error_bounds() {
288 let bounds = ErrorBounds::new(90.0, 100.0, 110.0, 0.95);
289
290 assert!(bounds.contains(100.0));
291 assert!(bounds.contains(90.0));
292 assert!(bounds.contains(110.0));
293 assert!(!bounds.contains(89.0));
294 assert!(!bounds.contains(111.0));
295
296 assert_eq!(bounds.width(), 20.0);
297 assert!((bounds.relative_width() - 0.2).abs() < 0.001);
298 }
299}