diff --git a/.gitignore b/.gitignore index 36d5e44..f03006e 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,6 @@ # Build artifacts **/target + +# Generated files +*.sk \ No newline at end of file diff --git a/datasketches/src/lib.rs b/datasketches/src/lib.rs index 9c69ba0..47cd37e 100644 --- a/datasketches/src/lib.rs +++ b/datasketches/src/lib.rs @@ -43,12 +43,14 @@ pub mod frequencies; pub mod hll; #[cfg(feature = "tdigest")] pub mod tdigest; -#[cfg(feature = "theta")] -pub mod theta; #[cfg(any(feature = "theta", feature = "tuple"))] -pub mod thetacommon; +mod theta_family; +#[cfg(any(feature = "theta", feature = "tuple"))] +pub use self::theta_family::common as thetacommon; +#[cfg(feature = "theta")] +pub use self::theta_family::theta; #[cfg(feature = "tuple")] -pub mod tuple; +pub use self::theta_family::tuple; // common modules pub mod codec; diff --git a/datasketches/src/thetacommon/a_not_b.rs b/datasketches/src/theta_family/common/a_not_b.rs similarity index 97% rename from datasketches/src/thetacommon/a_not_b.rs rename to datasketches/src/theta_family/common/a_not_b.rs index 03470c7..782b960 100644 --- a/datasketches/src/thetacommon/a_not_b.rs +++ b/datasketches/src/theta_family/common/a_not_b.rs @@ -21,6 +21,7 @@ use crate::error::Error; use crate::hash::compute_seed_hash; use crate::thetacommon::RetainedEntry; use crate::thetacommon::ThetaFamilySketchView; +use crate::thetacommon::ThetaKeySketchView; use crate::thetacommon::constants::MAX_THETA; use crate::thetacommon::hash_table::CompactSketchParts; @@ -60,7 +61,7 @@ impl ANotBOperator { ) -> Result, Error> where A: ThetaFamilySketchView, - B: ThetaFamilySketchView, + B: ThetaKeySketchView, { // If A is empty the result is an (empty) copy of A. As with the union and intersection, an // empty input carries no keys, so its seed is not validated. @@ -104,7 +105,7 @@ impl ANotBOperator { // Both inputs are sorted ascending by hash: merge-scan without a hash set. Only // B hashes below theta can exclude an A entry (A entries are all < theta), so // unexamined B entries at or above theta are harmless. - let mut b_hashes = b.iter().map(|entry| entry.hash()).peekable(); + let mut b_hashes = b.iter_hashes().peekable(); let mut entries = vec![]; for entry in a.iter() { let hash = entry.hash(); @@ -125,8 +126,7 @@ impl ANotBOperator { entries } else { let mut b_keys: HashSet = HashSet::with_capacity(b.num_retained()); - for entry in b.iter() { - let hash = entry.hash(); + for hash in b.iter_hashes() { if hash < theta { b_keys.insert(hash); } else if b.is_ordered() { diff --git a/datasketches/src/thetacommon/binomial_bounds.rs b/datasketches/src/theta_family/common/binomial_bounds.rs similarity index 100% rename from datasketches/src/thetacommon/binomial_bounds.rs rename to datasketches/src/theta_family/common/binomial_bounds.rs diff --git a/datasketches/src/thetacommon/bounds_binomial_proportions.rs b/datasketches/src/theta_family/common/bounds_binomial_proportions.rs similarity index 100% rename from datasketches/src/thetacommon/bounds_binomial_proportions.rs rename to datasketches/src/theta_family/common/bounds_binomial_proportions.rs diff --git a/datasketches/src/thetacommon/constants.rs b/datasketches/src/theta_family/common/constants.rs similarity index 100% rename from datasketches/src/thetacommon/constants.rs rename to datasketches/src/theta_family/common/constants.rs diff --git a/datasketches/src/thetacommon/hash_table.rs b/datasketches/src/theta_family/common/hash_table.rs similarity index 100% rename from datasketches/src/thetacommon/hash_table.rs rename to datasketches/src/theta_family/common/hash_table.rs diff --git a/datasketches/src/thetacommon/intersection.rs b/datasketches/src/theta_family/common/intersection.rs similarity index 98% rename from datasketches/src/thetacommon/intersection.rs rename to datasketches/src/theta_family/common/intersection.rs index 77ae398..3707c85 100644 --- a/datasketches/src/thetacommon/intersection.rs +++ b/datasketches/src/theta_family/common/intersection.rs @@ -252,6 +252,7 @@ where mod tests { use super::*; use crate::hash::DEFAULT_UPDATE_SEED; + use crate::thetacommon::ThetaKeySketchView; #[derive(Clone, Debug, Eq, PartialEq)] struct TestEntry { @@ -280,9 +281,7 @@ mod tests { } } - impl ThetaFamilySketchView for TestSketch { - type Entry = TestEntry; - + impl ThetaKeySketchView for TestSketch { fn seed_hash(&self) -> u16 { crate::hash::compute_seed_hash(DEFAULT_UPDATE_SEED) } @@ -299,8 +298,8 @@ mod tests { false } - fn iter(&self) -> impl Iterator + '_ { - self.entries.iter().cloned() + fn iter_hashes(&self) -> impl Iterator + '_ { + self.entries.iter().map(RetainedEntry::hash) } fn num_retained(&self) -> usize { @@ -308,6 +307,14 @@ mod tests { } } + impl ThetaFamilySketchView for TestSketch { + type Entry = TestEntry; + + fn iter(&self) -> impl Iterator + '_ { + self.entries.iter().cloned() + } + } + struct SumPolicy; impl IntersectionMergePolicy for SumPolicy { diff --git a/datasketches/src/thetacommon/jaccard_similarity.rs b/datasketches/src/theta_family/common/jaccard_similarity.rs similarity index 94% rename from datasketches/src/thetacommon/jaccard_similarity.rs rename to datasketches/src/theta_family/common/jaccard_similarity.rs index f8c2522..d2a1704 100644 --- a/datasketches/src/thetacommon/jaccard_similarity.rs +++ b/datasketches/src/theta_family/common/jaccard_similarity.rs @@ -20,6 +20,7 @@ use crate::error::Error; use crate::hash::compute_seed_hash; use crate::thetacommon::RetainedEntry; use crate::thetacommon::ThetaFamilySketchView; +use crate::thetacommon::ThetaKeySketchView; use crate::thetacommon::bounds_binomial_proportions; use crate::thetacommon::constants::MAX_LG_K; use crate::thetacommon::constants::MAX_THETA; @@ -127,9 +128,7 @@ impl<'a, S> KeySketchView<'a, S> { } } -impl ThetaFamilySketchView for KeySketchView<'_, S> { - type Entry = KeyEntry; - +impl ThetaKeySketchView for KeySketchView<'_, S> { fn seed_hash(&self) -> u16 { self.sketch.seed_hash() } @@ -146,8 +145,8 @@ impl ThetaFamilySketchView for KeySketchView<'_, S> { self.sketch.is_ordered() } - fn iter(&self) -> impl Iterator + '_ { - self.sketch.iter_hashes().map(|hash| KeyEntry { hash }) + fn iter_hashes(&self) -> impl Iterator + '_ { + self.sketch.iter_hashes() } fn num_retained(&self) -> usize { @@ -155,6 +154,14 @@ impl ThetaFamilySketchView for KeySketchView<'_, S> { } } +impl ThetaFamilySketchView for KeySketchView<'_, S> { + type Entry = KeyEntry; + + fn iter(&self) -> impl Iterator + '_ { + self.sketch.iter_hashes().map(|hash| KeyEntry { hash }) + } +} + #[derive(Clone, Copy, Debug)] struct NoopMergePolicy; @@ -174,9 +181,7 @@ struct CompactKeySketchView { empty: bool, } -impl ThetaFamilySketchView for CompactKeySketchView { - type Entry = KeyEntry; - +impl ThetaKeySketchView for CompactKeySketchView { fn seed_hash(&self) -> u16 { self.seed_hash } @@ -193,8 +198,8 @@ impl ThetaFamilySketchView for CompactKeySketchView { self.ordered } - fn iter(&self) -> impl Iterator + '_ { - self.entries.iter().copied() + fn iter_hashes(&self) -> impl Iterator + '_ { + self.entries.iter().map(RetainedEntry::hash) } fn num_retained(&self) -> usize { @@ -202,6 +207,14 @@ impl ThetaFamilySketchView for CompactKeySketchView { } } +impl ThetaFamilySketchView for CompactKeySketchView { + type Entry = KeyEntry; + + fn iter(&self) -> impl Iterator + '_ { + self.entries.iter().copied() + } +} + /// Configured Jaccard operator shared by Theta and Tuple public wrappers. #[derive(Clone, Copy, Debug)] pub(crate) struct JaccardSimilarityOperator { @@ -219,8 +232,8 @@ impl JaccardSimilarityOperator { sketch_b: &B, ) -> Result where - A: ThetaFamilySketchView, - B: ThetaFamilySketchView, + A: ThetaKeySketchView, + B: ThetaKeySketchView, { if sketch_a.is_empty() && sketch_b.is_empty() { return Ok(JaccardSimilarity::exact(1.0)); diff --git a/datasketches/src/thetacommon/mod.rs b/datasketches/src/theta_family/common/mod.rs similarity index 75% rename from datasketches/src/thetacommon/mod.rs rename to datasketches/src/theta_family/common/mod.rs index 6da798d..4314158 100644 --- a/datasketches/src/thetacommon/mod.rs +++ b/datasketches/src/theta_family/common/mod.rs @@ -34,14 +34,11 @@ pub trait RetainedEntry { fn hash(&self) -> u64; } -/// Read-only input accepted by Theta-family set operations. +/// Read-only hash-key view shared by Theta-family sketches. /// -/// This trait carries complete retained entries, so Tuple union, intersection, and A-not-B -/// operations can share the Theta-family state machines while preserving per-key summaries. -pub trait ThetaFamilySketchView { - /// The retained entry representation yielded by this view. - type Entry: RetainedEntry; - +/// Key-only operations use this interface without requiring access to, or cloning, payloads such +/// as Tuple summaries. +pub trait ThetaKeySketchView { /// Return the 16-bit seed hash. fn seed_hash(&self) -> u16; @@ -54,16 +51,21 @@ pub trait ThetaFamilySketchView { /// Return whether retained entries are ordered by ascending hash. fn is_ordered(&self) -> bool; - /// Return an iterator over retained entries. - fn iter(&self) -> impl Iterator + '_; - - /// Return an iterator over retained hash keys without requiring callers to inspect payloads. - /// - /// Tuple sketches override this method so key-only operations do not clone summary values. - fn iter_hashes(&self) -> impl Iterator + '_ { - self.iter().map(|entry| entry.hash()) - } + /// Return an iterator over retained hash keys. + fn iter_hashes(&self) -> impl Iterator + '_; /// Return the number of retained entries. fn num_retained(&self) -> usize; } + +/// Read-only retained-entry view accepted by Theta-family set operations. +/// +/// This trait extends [`ThetaKeySketchView`] with complete retained entries, so operations such as +/// union and intersection can preserve and combine Tuple summaries. +pub trait ThetaFamilySketchView: ThetaKeySketchView { + /// The retained entry representation yielded by this view. + type Entry: RetainedEntry; + + /// Return an iterator over retained entries. + fn iter(&self) -> impl Iterator + '_; +} diff --git a/datasketches/src/thetacommon/union.rs b/datasketches/src/theta_family/common/union.rs similarity index 96% rename from datasketches/src/thetacommon/union.rs rename to datasketches/src/theta_family/common/union.rs index 3e89189..642de04 100644 --- a/datasketches/src/thetacommon/union.rs +++ b/datasketches/src/theta_family/common/union.rs @@ -158,6 +158,7 @@ where mod tests { use super::*; use crate::hash::DEFAULT_UPDATE_SEED; + use crate::thetacommon::ThetaKeySketchView; #[derive(Clone, Debug, Eq, PartialEq)] struct TestEntry { @@ -175,9 +176,7 @@ mod tests { entries: Vec, } - impl ThetaFamilySketchView for TestSketch { - type Entry = TestEntry; - + impl ThetaKeySketchView for TestSketch { fn seed_hash(&self) -> u16 { crate::hash::compute_seed_hash(DEFAULT_UPDATE_SEED) } @@ -194,8 +193,8 @@ mod tests { false } - fn iter(&self) -> impl Iterator + '_ { - self.entries.iter().cloned() + fn iter_hashes(&self) -> impl Iterator + '_ { + self.entries.iter().map(RetainedEntry::hash) } fn num_retained(&self) -> usize { @@ -203,6 +202,14 @@ mod tests { } } + impl ThetaFamilySketchView for TestSketch { + type Entry = TestEntry; + + fn iter(&self) -> impl Iterator + '_ { + self.entries.iter().cloned() + } + } + struct SumPolicy; impl UnionMergePolicy for SumPolicy { diff --git a/datasketches/src/theta_family/mod.rs b/datasketches/src/theta_family/mod.rs new file mode 100644 index 0000000..6f4d8cc --- /dev/null +++ b/datasketches/src/theta_family/mod.rs @@ -0,0 +1,22 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +pub mod common; +#[cfg(feature = "theta")] +pub mod theta; +#[cfg(feature = "tuple")] +pub mod tuple; diff --git a/datasketches/src/theta/a_not_b.rs b/datasketches/src/theta_family/theta/a_not_b.rs similarity index 100% rename from datasketches/src/theta/a_not_b.rs rename to datasketches/src/theta_family/theta/a_not_b.rs diff --git a/datasketches/src/theta/bit_pack.rs b/datasketches/src/theta_family/theta/bit_pack.rs similarity index 100% rename from datasketches/src/theta/bit_pack.rs rename to datasketches/src/theta_family/theta/bit_pack.rs diff --git a/datasketches/src/theta/hash_table.rs b/datasketches/src/theta_family/theta/hash_table.rs similarity index 100% rename from datasketches/src/theta/hash_table.rs rename to datasketches/src/theta_family/theta/hash_table.rs diff --git a/datasketches/src/theta/intersection.rs b/datasketches/src/theta_family/theta/intersection.rs similarity index 100% rename from datasketches/src/theta/intersection.rs rename to datasketches/src/theta_family/theta/intersection.rs diff --git a/datasketches/src/theta/jaccard_similarity.rs b/datasketches/src/theta_family/theta/jaccard_similarity.rs similarity index 100% rename from datasketches/src/theta/jaccard_similarity.rs rename to datasketches/src/theta_family/theta/jaccard_similarity.rs diff --git a/datasketches/src/theta/mod.rs b/datasketches/src/theta_family/theta/mod.rs similarity index 100% rename from datasketches/src/theta/mod.rs rename to datasketches/src/theta_family/theta/mod.rs diff --git a/datasketches/src/theta/serialization.rs b/datasketches/src/theta_family/theta/serialization.rs similarity index 100% rename from datasketches/src/theta/serialization.rs rename to datasketches/src/theta_family/theta/serialization.rs diff --git a/datasketches/src/theta/sketch.rs b/datasketches/src/theta_family/theta/sketch.rs similarity index 99% rename from datasketches/src/theta/sketch.rs rename to datasketches/src/theta_family/theta/sketch.rs index cc78b9c..3be2441 100644 --- a/datasketches/src/theta/sketch.rs +++ b/datasketches/src/theta_family/theta/sketch.rs @@ -44,6 +44,7 @@ use crate::theta::serialization::V2_PREAMBLE_EMPTY; use crate::theta::serialization::V2_PREAMBLE_ESTIMATE; use crate::theta::serialization::V2_PREAMBLE_PRECISE; use crate::thetacommon::ThetaFamilySketchView; +use crate::thetacommon::ThetaKeySketchView; use crate::thetacommon::binomial_bounds; use crate::thetacommon::constants::DEFAULT_LG_K; use crate::thetacommon::constants::FLAGS_IS_COMPACT; @@ -62,9 +63,7 @@ pub trait ThetaSketchView: ThetaFamilySketchView {} impl> ThetaSketchView for T {} -impl ThetaFamilySketchView for ThetaSketch { - type Entry = ThetaEntry; - +impl ThetaKeySketchView for ThetaSketch { fn seed_hash(&self) -> u16 { ThetaSketch::seed_hash(self) } @@ -81,8 +80,8 @@ impl ThetaFamilySketchView for ThetaSketch { false } - fn iter(&self) -> impl Iterator + '_ { - self.table.iter_entries().copied() + fn iter_hashes(&self) -> impl Iterator + '_ { + self.table.iter_entries().map(ThetaEntry::hash) } fn num_retained(&self) -> usize { @@ -90,6 +89,14 @@ impl ThetaFamilySketchView for ThetaSketch { } } +impl ThetaFamilySketchView for ThetaSketch { + type Entry = ThetaEntry; + + fn iter(&self) -> impl Iterator + '_ { + self.table.iter_entries().copied() + } +} + /// Mutable theta sketch for building from input data #[derive(Debug)] pub struct ThetaSketch { @@ -864,9 +871,7 @@ impl CompactThetaSketch { } } -impl ThetaFamilySketchView for CompactThetaSketch { - type Entry = ThetaEntry; - +impl ThetaKeySketchView for CompactThetaSketch { fn seed_hash(&self) -> u16 { CompactThetaSketch::seed_hash(self) } @@ -883,8 +888,8 @@ impl ThetaFamilySketchView for CompactThetaSketch { CompactThetaSketch::is_ordered(self) } - fn iter(&self) -> impl Iterator + '_ { - self.entries.iter().copied().map(ThetaEntry::new) + fn iter_hashes(&self) -> impl Iterator + '_ { + self.entries.iter().copied() } fn num_retained(&self) -> usize { @@ -892,6 +897,14 @@ impl ThetaFamilySketchView for CompactThetaSketch { } } +impl ThetaFamilySketchView for CompactThetaSketch { + type Entry = ThetaEntry; + + fn iter(&self) -> impl Iterator + '_ { + self.entries.iter().copied().map(ThetaEntry::new) + } +} + /// Builder for ThetaSketch #[derive(Debug)] pub struct ThetaSketchBuilder { diff --git a/datasketches/src/theta/union.rs b/datasketches/src/theta_family/theta/union.rs similarity index 100% rename from datasketches/src/theta/union.rs rename to datasketches/src/theta_family/theta/union.rs diff --git a/datasketches/src/tuple/a_not_b.rs b/datasketches/src/theta_family/tuple/a_not_b.rs similarity index 92% rename from datasketches/src/tuple/a_not_b.rs rename to datasketches/src/theta_family/tuple/a_not_b.rs index c24c559..5584b9d 100644 --- a/datasketches/src/tuple/a_not_b.rs +++ b/datasketches/src/theta_family/tuple/a_not_b.rs @@ -26,6 +26,7 @@ use crate::error::Error; use crate::hash::DEFAULT_UPDATE_SEED; use crate::thetacommon::a_not_b::ANotBOperator; use crate::tuple::sketch::CompactTupleSketch; +use crate::tuple::sketch::TupleKeySketchView; use crate::tuple::sketch::TupleSketchView; /// Set difference operator (`A and not B`) for Tuple sketches. @@ -72,8 +73,8 @@ impl TupleANotB { /// Computes `a and not b`. /// /// The result retains every key of `a` (below the combined theta) that is not present in `b`, - /// keeping the summaries from `a`. If `ordered` is true, the retained entries are sorted - /// ascending by hash. + /// 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. /// /// # Errors /// @@ -87,7 +88,7 @@ impl TupleANotB { ) -> Result, Error> where A: TupleSketchView, - B: TupleSketchView, + B: TupleKeySketchView, { let parts = self.op.compute(a, b, ordered)?; Ok(CompactTupleSketch::from_parts( diff --git a/datasketches/src/tuple/hash_table.rs b/datasketches/src/theta_family/tuple/hash_table.rs similarity index 100% rename from datasketches/src/tuple/hash_table.rs rename to datasketches/src/theta_family/tuple/hash_table.rs diff --git a/datasketches/src/tuple/intersection.rs b/datasketches/src/theta_family/tuple/intersection.rs similarity index 100% rename from datasketches/src/tuple/intersection.rs rename to datasketches/src/theta_family/tuple/intersection.rs diff --git a/datasketches/src/tuple/jaccard_similarity.rs b/datasketches/src/theta_family/tuple/jaccard_similarity.rs similarity index 86% rename from datasketches/src/tuple/jaccard_similarity.rs rename to datasketches/src/theta_family/tuple/jaccard_similarity.rs index d9faa2d..d262714 100644 --- a/datasketches/src/tuple/jaccard_similarity.rs +++ b/datasketches/src/theta_family/tuple/jaccard_similarity.rs @@ -21,13 +21,13 @@ use crate::error::Error; use crate::hash::DEFAULT_UPDATE_SEED; use crate::thetacommon::jaccard_similarity::JaccardSimilarity; use crate::thetacommon::jaccard_similarity::JaccardSimilarityOperator; -use crate::tuple::TupleSketchView; +use crate::tuple::TupleKeySketchView; /// Jaccard similarity operator for Tuple sketches. /// /// Only retained hash keys participate in the similarity calculation. Summary values are ignored, -/// so the two inputs may use different summary types. The returned [`JaccardSimilarity`] contains -/// the estimate and its 95.4% confidence interval. +/// need not implement [`Clone`], and may have different types in the two inputs. The returned +/// [`JaccardSimilarity`] contains the estimate and its 95.4% confidence interval. /// /// # Examples /// @@ -69,14 +69,10 @@ impl TupleJaccardSimilarity { /// /// Returns an error if either non-empty sketch was built with a seed different from this /// operator's configured seed. - pub fn compute( - &self, - sketch_a: &A, - sketch_b: &B, - ) -> Result + pub fn compute(&self, sketch_a: &A, sketch_b: &B) -> Result where - A: TupleSketchView, - B: TupleSketchView, + A: TupleKeySketchView, + B: TupleKeySketchView, { self.op.compute(sketch_a, sketch_b) } diff --git a/datasketches/src/tuple/mod.rs b/datasketches/src/theta_family/tuple/mod.rs similarity index 98% rename from datasketches/src/tuple/mod.rs rename to datasketches/src/theta_family/tuple/mod.rs index 1ba8b58..e77f5d4 100644 --- a/datasketches/src/tuple/mod.rs +++ b/datasketches/src/theta_family/tuple/mod.rs @@ -58,6 +58,7 @@ pub use self::policy::SummaryPolicy; pub use self::policy::SummaryUpdatePolicy; pub use self::serialization::TupleSummaryValue; pub use self::sketch::CompactTupleSketch; +pub use self::sketch::TupleKeySketchView; pub use self::sketch::TupleSketch; pub use self::sketch::TupleSketchBuilder; pub use self::sketch::TupleSketchView; diff --git a/datasketches/src/tuple/policy.rs b/datasketches/src/theta_family/tuple/policy.rs similarity index 100% rename from datasketches/src/tuple/policy.rs rename to datasketches/src/theta_family/tuple/policy.rs diff --git a/datasketches/src/tuple/serialization.rs b/datasketches/src/theta_family/tuple/serialization.rs similarity index 100% rename from datasketches/src/tuple/serialization.rs rename to datasketches/src/theta_family/tuple/serialization.rs diff --git a/datasketches/src/tuple/sketch.rs b/datasketches/src/theta_family/tuple/sketch.rs similarity index 94% rename from datasketches/src/tuple/sketch.rs rename to datasketches/src/theta_family/tuple/sketch.rs index b06c406..67080d8 100644 --- a/datasketches/src/tuple/sketch.rs +++ b/datasketches/src/theta_family/tuple/sketch.rs @@ -35,6 +35,7 @@ use crate::error::Error; use crate::hash::DEFAULT_UPDATE_SEED; use crate::hash::compute_seed_hash; use crate::thetacommon::ThetaFamilySketchView; +use crate::thetacommon::ThetaKeySketchView; use crate::thetacommon::binomial_bounds; use crate::thetacommon::constants::DEFAULT_LG_K; use crate::thetacommon::constants::FLAGS_IS_COMPACT; @@ -54,17 +55,29 @@ use crate::tuple::serialization::SKETCH_TYPE; use crate::tuple::serialization::SKETCH_TYPE_LEGACY; use crate::tuple::serialization::TupleSummaryValue; -/// Read-only view for Tuple sketches. +/// Read-only hash-key view for Tuple sketches. +/// +/// This interface does not inspect summary values and therefore does not require them to implement +/// [`Clone`]. +pub trait TupleKeySketchView: ThetaKeySketchView {} + +/// Read-only retained-entry view for Tuple sketches. /// /// This trait is the input abstraction for APIs (such as union and intersection) that accept /// either a mutable [`TupleSketch`] or an immutable [`CompactTupleSketch`]. `S` is the /// summary type retained by the sketch. /// -/// It is blanket-implemented for every [`ThetaFamilySketchView`] whose associated entry type is -/// [`TupleEntry`], so custom sketch-like inputs can be supplied by implementing that trait. -pub trait TupleSketchView: ThetaFamilySketchView> {} +/// It is blanket-implemented for every [`TupleKeySketchView`] that also implements +/// [`ThetaFamilySketchView`] with [`TupleEntry`] as its associated entry type. +pub trait TupleSketchView: + TupleKeySketchView + ThetaFamilySketchView> +{ +} -impl TupleSketchView for T where T: ThetaFamilySketchView> {} +impl TupleSketchView for T where + T: TupleKeySketchView + ThetaFamilySketchView> +{ +} /// Mutable Tuple sketch for building from input data. /// @@ -248,13 +261,10 @@ where } } -impl

ThetaFamilySketchView for TupleSketch

+impl

ThetaKeySketchView for TupleSketch

where P: SummaryPolicy, - P::Summary: Clone, { - type Entry = TupleEntry; - fn seed_hash(&self) -> u16 { self.table.seed_hash() } @@ -271,10 +281,8 @@ where false } - fn iter(&self) -> impl Iterator> + '_ { - self.table - .iter() - .map(|(hash, summary)| TupleEntry::new(hash, summary.clone())) + fn iter_hashes(&self) -> impl Iterator + '_ { + self.table.iter().map(|(hash, _)| hash) } fn num_retained(&self) -> usize { @@ -282,6 +290,22 @@ where } } +impl

TupleKeySketchView for TupleSketch

where P: SummaryPolicy {} + +impl

ThetaFamilySketchView for TupleSketch

+where + P: SummaryPolicy, + P::Summary: Clone, +{ + type Entry = TupleEntry; + + fn iter(&self) -> impl Iterator> + '_ { + self.table + .iter() + .map(|(hash, summary)| TupleEntry::new(hash, summary.clone())) + } +} + /// Compact (immutable) Tuple sketch. /// /// This is the serialization-friendly form: a compact array of retained [`TupleEntry`] values @@ -568,9 +592,7 @@ impl CompactTupleSketch { } } -impl ThetaFamilySketchView for CompactTupleSketch { - type Entry = TupleEntry; - +impl ThetaKeySketchView for CompactTupleSketch { fn seed_hash(&self) -> u16 { self.seed_hash } @@ -587,8 +609,8 @@ impl ThetaFamilySketchView for CompactTupleSketch { self.ordered } - fn iter(&self) -> impl Iterator> + '_ { - self.entries.iter().cloned() + fn iter_hashes(&self) -> impl Iterator + '_ { + self.entries.iter().map(TupleEntry::hash) } fn num_retained(&self) -> usize { @@ -596,6 +618,16 @@ impl ThetaFamilySketchView for CompactTupleSketch { } } +impl TupleKeySketchView for CompactTupleSketch {} + +impl ThetaFamilySketchView for CompactTupleSketch { + type Entry = TupleEntry; + + fn iter(&self) -> impl Iterator> + '_ { + self.entries.iter().cloned() + } +} + /// Builder for [`TupleSketch`]. /// /// Every builder carries a concrete [`SummaryPolicy`]. Use diff --git a/datasketches/src/tuple/union.rs b/datasketches/src/theta_family/tuple/union.rs similarity index 100% rename from datasketches/src/tuple/union.rs rename to datasketches/src/theta_family/tuple/union.rs