Skip to main content

apache_datasketches/tuple/generic/
builder.rs

1use super::{summary::TupleSummary, TupleSketch};
2use crate::error::SketchError;
3use crate::tuple::ResizeFactor;
4use std::marker::PhantomData;
5
6/// Builder for [`TupleSketch`], mirroring upstream's
7/// `update_tuple_sketch::builder`. `lg_k` defaults to `12`, `resize_factor`
8/// to [`ResizeFactor::X8`], and `p` to `1.0` (no sampling). The seed is never
9/// exposed.
10pub struct TupleSketchBuilder<S: TupleSummary> {
11    lg_k: u8,
12    resize_factor: ResizeFactor,
13    p: f32,
14    _marker: PhantomData<fn() -> S>,
15}
16
17// Hand-written rather than `#[derive(..)]`: deriving `Debug`/`Clone`/`Copy`
18// on a generic struct adds an `S: Debug`/`S: Clone`/`S: Copy` bound to the
19// impl, even though every field here (`u8`, `ResizeFactor`, `f32`,
20// `PhantomData<fn() -> S>`) is unconditionally `Debug + Clone + Copy`
21// regardless of `S`. A derive would make this builder undocumentedly
22// unusable for any `S` that isn't itself `Copy` -- which most summaries,
23// like this crate's own smoke-test `Sum`, are not.
24impl<S: TupleSummary> std::fmt::Debug for TupleSketchBuilder<S> {
25    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26        f.debug_struct("TupleSketchBuilder")
27            .field("lg_k", &self.lg_k)
28            .field("resize_factor", &self.resize_factor)
29            .field("p", &self.p)
30            .finish()
31    }
32}
33
34impl<S: TupleSummary> Clone for TupleSketchBuilder<S> {
35    fn clone(&self) -> Self {
36        *self
37    }
38}
39
40impl<S: TupleSummary> Copy for TupleSketchBuilder<S> {}
41
42impl<S: TupleSummary> Default for TupleSketchBuilder<S> {
43    fn default() -> Self {
44        Self {
45            lg_k: 12,
46            resize_factor: ResizeFactor::X8,
47            p: 1.0,
48            _marker: PhantomData,
49        }
50    }
51}
52
53impl<S: TupleSummary> TupleSketchBuilder<S> {
54    /// Creates a builder with default settings (`lg_k = 12`,
55    /// `resize_factor = X8`, `p = 1.0`).
56    pub fn new() -> Self {
57        Self::default()
58    }
59
60    /// Sets the base-2 log of the target number of retained entries.
61    pub fn lg_k(mut self, lg_k: u8) -> Self {
62        self.lg_k = lg_k;
63        self
64    }
65
66    /// Sets the hash table's growth [`ResizeFactor`].
67    pub fn resize_factor(mut self, resize_factor: ResizeFactor) -> Self {
68        self.resize_factor = resize_factor;
69        self
70    }
71
72    /// Sets the sampling probability. `1.0` (the default) disables sampling.
73    pub fn p(mut self, p: f32) -> Self {
74        self.p = p;
75        self
76    }
77
78    /// Builds the sketch. Returns [`SketchError::InvalidConfig`] if `lg_k` is
79    /// out of range or `p` is outside `(0, 1]`.
80    pub fn build(self) -> Result<TupleSketch<S>, SketchError> {
81        TupleSketch::from_parts(self.lg_k, self.resize_factor, self.p)
82    }
83}
84
85/// Converts the safe enum to the literal multiplier this bridge passes as a
86/// `u8`. See the task note on why this bridge does not share a cxx enum.
87pub(crate) fn resize_factor_multiplier(rf: ResizeFactor) -> u8 {
88    match rf {
89        ResizeFactor::X1 => 1,
90        ResizeFactor::X2 => 2,
91        ResizeFactor::X4 => 4,
92        ResizeFactor::X8 => 8,
93    }
94}