2525
2626use std:: ops:: AddAssign ;
2727
28+ use crate :: thetacommon:: union:: RawThetaUnionPolicy ;
29+ use crate :: tuple:: hash_table:: TupleEntry ;
30+
31+ /// Adapts a [`SummaryCombinePolicy`] to the raw union's entry-merge policy.
32+ #[ derive( Debug ) ]
33+ pub ( crate ) struct CombinePolicyAdapter < P > ( pub ( crate ) P ) ;
34+
35+ impl < S , P > RawThetaUnionPolicy < TupleEntry < S > > for CombinePolicyAdapter < P >
36+ where
37+ P : SummaryCombinePolicy < S > ,
38+ {
39+ fn merge ( & self , existing : & mut TupleEntry < S > , incoming : TupleEntry < S > ) {
40+ self . 0 . combine ( existing. summary_mut ( ) , incoming. summary ( ) ) ;
41+ }
42+ }
43+
2844/// Defines how a summary is created and how update values are folded into it.
2945///
3046/// This is used by the update tuple sketch. `S` is the stored summary type and `U` is the type of
@@ -40,11 +56,20 @@ pub trait SummaryUpdatePolicy<S, U> {
4056 fn update ( & self , summary : & mut S , value : U ) ;
4157}
4258
59+ /// Defines how two summaries that share the same key are combined.
60+ ///
61+ /// This is used by both union and intersection. Each operator is given its own policy instance,
62+ /// because the two operations may combine summaries differently for the same summary type.
63+ pub trait SummaryCombinePolicy < S > {
64+ /// Combines `other` into `summary` in place.
65+ fn combine ( & self , summary : & mut S , other : & S ) ;
66+ }
67+
4368/// Default update policy for summaries that are default-constructible and additive.
4469///
45- /// This is the convenience policy used when no custom policy is supplied, equivalent to C++
46- /// `default_tuple_update_policy` (which folds updates with ` summary += update`) . It is available
47- /// for any summary type `S` and update type `U` where `S: Default + AddAssign<U>`.
70+ /// This is the convenience policy used when no custom policy is supplied; it folds updates with
71+ /// `summary += update`. It is available for any summary type `S` and update type `U` where
72+ /// `S: Default + AddAssign<U>`.
4873#[ derive( Debug , Default , Clone , Copy ) ]
4974pub struct DefaultUpdatePolicy ;
5075
6186 }
6287}
6388
89+ /// Default combine policy for additive summaries, used by the union when no custom policy is given.
90+ ///
91+ /// It combines two summaries with `summary += other` and is available for any summary type `S`
92+ /// where `S: AddAssign<&S>`.
93+ ///
94+ /// There is intentionally no default combine policy for the intersection: how to combine summaries
95+ /// of the keys present in both inputs is application-specific, so the intersection always requires
96+ /// an explicit policy.
97+ #[ derive( Debug , Default , Clone , Copy ) ]
98+ pub struct DefaultUnionPolicy ;
99+
100+ impl < S > SummaryCombinePolicy < S > for DefaultUnionPolicy
101+ where
102+ for < ' a > S : AddAssign < & ' a S > ,
103+ {
104+ fn combine ( & self , summary : & mut S , other : & S ) {
105+ * summary += other;
106+ }
107+ }
108+
64109#[ cfg( test) ]
65110mod tests {
66111 use super :: * ;
@@ -89,6 +134,12 @@ mod tests {
89134 }
90135 }
91136
137+ impl SummaryCombinePolicy < u64 > for MaxPolicy {
138+ fn combine ( & self , summary : & mut u64 , other : & u64 ) {
139+ self . update ( summary, * other) ;
140+ }
141+ }
142+
92143 #[ test]
93144 fn custom_update_policy_keeps_max ( ) {
94145 let policy = MaxPolicy ;
@@ -98,4 +149,22 @@ mod tests {
98149 policy. update ( & mut summary, 2 ) ;
99150 assert_eq ! ( summary, 7 ) ;
100151 }
152+
153+ #[ test]
154+ fn custom_combine_policy_keeps_max ( ) {
155+ let policy = MaxPolicy ;
156+ let mut summary = 5u64 ;
157+ policy. combine ( & mut summary, & 10 ) ;
158+ policy. combine ( & mut summary, & 7 ) ;
159+ assert_eq ! ( summary, 10 ) ;
160+ }
161+
162+ #[ test]
163+ fn default_union_policy_combines_additively ( ) {
164+ let policy = DefaultUnionPolicy ;
165+ let mut summary = 5u64 ;
166+ policy. combine ( & mut summary, & 10 ) ;
167+ policy. combine ( & mut summary, & 7 ) ;
168+ assert_eq ! ( summary, 22 ) ;
169+ }
101170}
0 commit comments