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}