Skip to main content

apache_datasketches/tuple/
intersection.rs

1use super::input::ArrayOfDoublesInput;
2use super::CompactArrayOfDoublesSketch;
3use crate::error::SketchError;
4use apache_datasketches_sys::array_of_doubles_input::ArrayOfDoublesInputRef;
5use apache_datasketches_sys::array_of_doubles_intersection::ffi as sys;
6use cxx::UniquePtr;
7
8/// Computes the intersection of ArrayOfDoubles sketches fed via
9/// [`Self::update`]. Values are summed per index for keys present in every
10/// input.
11///
12/// Unlike [`super::ArrayOfDoublesUnion`] there is no builder — upstream's
13/// `array_of_doubles_intersection` has a plain constructor, and the
14/// intersecting universe is defined entirely by the sketches passed to
15/// `update`. Only `num_values` (which the combine policy needs at
16/// construction time) must be supplied up front.
17///
18/// Upstream ships no default combine policy for this type; v1 uses
19/// sum-on-collision, mirroring the union's policy. Additional policies
20/// (min/max, etc.) can be added later without changing this type's shape.
21pub struct ArrayOfDoublesIntersection {
22    inner: UniquePtr<sys::ArrayOfDoublesIntersectionShim>,
23    num_values: u8,
24}
25
26unsafe impl Send for ArrayOfDoublesIntersection {}
27
28impl ArrayOfDoublesIntersection {
29    /// Creates a new intersection accumulator for sketches carrying
30    /// `num_values` values per entry, with no result yet — call
31    /// [`Self::update`] at least once before [`Self::get_result`].
32    ///
33    /// Returns [`SketchError::InvalidConfig`] if `num_values` is `0`.
34    pub fn new(num_values: u8) -> Result<Self, SketchError> {
35        if num_values == 0 {
36            return Err(SketchError::InvalidConfig(
37                "num_values must be at least 1".to_string(),
38            ));
39        }
40        Ok(Self {
41            inner: sys::new_array_of_doubles_intersection(num_values),
42            num_values,
43        })
44    }
45
46    /// Narrows this intersection's running result to also require membership
47    /// in the given sketch. The first call establishes the initial universe;
48    /// each subsequent call intersects further.
49    ///
50    /// Returns [`SketchError::InvalidConfig`] if the sketch's `num_values`
51    /// differs from this intersection's — upstream does not validate this
52    /// itself, and mismatched widths would read and write out of bounds.
53    pub fn update(&mut self, input: &impl ArrayOfDoublesInput) -> Result<(), SketchError> {
54        let actual = input.get_num_values();
55        if actual != self.num_values {
56            return Err(SketchError::InvalidConfig(format!(
57                "num_values mismatch: intersection has {}, input has {actual}",
58                self.num_values
59            )));
60        }
61        match input.as_input() {
62            ArrayOfDoublesInputRef::Sketch(s) => self.inner.pin_mut().update_with_sketch(s),
63            ArrayOfDoublesInputRef::Compact(c) => self.inner.pin_mut().update_with_compact(c),
64        }
65        Ok(())
66    }
67
68    /// Returns the current intersection result as a
69    /// [`CompactArrayOfDoublesSketch`], or
70    /// [`SketchError::EmptyIntersection`] if [`Self::update`] has never been
71    /// called. If `ordered` is `true`, the result's entries are sorted by
72    /// hash value.
73    pub fn get_result(&self, ordered: bool) -> Result<CompactArrayOfDoublesSketch, SketchError> {
74        if !self.inner.has_result() {
75            return Err(SketchError::EmptyIntersection);
76        }
77        let inner = self
78            .inner
79            .get_result(ordered)
80            .map_err(|e| SketchError::Cpp(e.what().to_string()))?;
81        Ok(CompactArrayOfDoublesSketch::from_shim(inner))
82    }
83
84    /// Returns `true` if [`Self::update`] has been called at least once.
85    pub fn has_result(&self) -> bool {
86        self.inner.has_result()
87    }
88
89    /// Returns the fixed number of `f64` values per entry this intersection
90    /// was created with. Every input passed to [`Self::update`] must match it.
91    pub fn get_num_values(&self) -> u8 {
92        self.num_values
93    }
94}