1515// specific language governing permissions and limitations
1616// under the License.
1717
18- use crate :: common:: ResizeFactor ;
1918use crate :: error:: Error ;
2019use crate :: hash:: DEFAULT_UPDATE_SEED ;
2120use crate :: theta:: CompactThetaSketch ;
2221use crate :: theta:: ThetaSketchView ;
23- use crate :: theta:: hash_table:: ThetaHashTable ;
24- use crate :: thetacommon:: constants :: HASH_TABLE_REBUILD_THRESHOLD ;
25- use crate :: thetacommon:: constants :: MAX_THETA ;
22+ use crate :: theta:: hash_table:: ThetaEntry ;
23+ use crate :: thetacommon:: intersection :: RawThetaIntersection ;
24+ use crate :: thetacommon:: intersection :: RawThetaIntersectionPolicy ;
2625
2726/// Stateful intersection operator for Theta sketches.
2827///
2928/// Before the first [`update`](Self::update), the result is undefined; use
3029/// [`has_result`](Self::has_result) to check.
3130#[ derive( Debug ) ]
3231pub struct ThetaIntersection {
33- is_valid : bool ,
34- table : ThetaHashTable ,
32+ raw : RawThetaIntersection < ThetaEntry , NoopIntersectionPolicy > ,
33+ }
34+
35+ #[ derive( Debug ) ]
36+ struct NoopIntersectionPolicy ;
37+
38+ impl RawThetaIntersectionPolicy < ThetaEntry > for NoopIntersectionPolicy {
39+ fn merge ( & self , _existing : & mut ThetaEntry , _incoming : ThetaEntry ) { }
3540}
3641
3742impl ThetaIntersection {
3843 /// Creates a new intersection operator for the given `seed`.
3944 pub fn new ( seed : u64 ) -> Self {
4045 Self {
41- is_valid : false ,
42- table : ThetaHashTable :: from_raw_parts (
43- 0 ,
44- 0 ,
45- ResizeFactor :: X1 ,
46- 1.0 ,
47- MAX_THETA ,
48- seed,
49- false ,
50- ) ,
46+ raw : RawThetaIntersection :: new ( seed, NoopIntersectionPolicy ) ,
5147 }
5248 }
5349
@@ -62,144 +58,12 @@ impl ThetaIntersection {
6258 /// and every update can reduce the current set to leave the overlapping
6359 /// subset only.
6460 pub fn update < S : ThetaSketchView > ( & mut self , sketch : & S ) -> Result < ( ) , Error > {
65- let new_default_table = |table : & ThetaHashTable | {
66- ThetaHashTable :: from_raw_parts (
67- 0 ,
68- 0 ,
69- ResizeFactor :: X1 ,
70- 1.0 ,
71- table. theta ( ) ,
72- table. hash_seed ( ) ,
73- table. is_empty ( ) ,
74- )
75- } ;
76-
77- if self . table . is_empty ( ) {
78- return Ok ( ( ) ) ;
79- }
80-
81- if !sketch. is_empty ( ) && sketch. seed_hash ( ) != self . table . seed_hash ( ) {
82- return Err ( Error :: invalid_argument ( format ! (
83- "incompatible seed hash: expected {}, got {}" ,
84- self . table. seed_hash( ) ,
85- sketch. seed_hash( )
86- ) ) ) ;
87- }
88-
89- if sketch. is_empty ( ) {
90- self . table . set_empty ( true ) ;
91- }
92-
93- self . table . set_theta ( if self . table . is_empty ( ) {
94- MAX_THETA
95- } else {
96- self . table . theta ( ) . min ( sketch. theta ( ) )
97- } ) ;
98-
99- if self . is_valid && self . table . num_retained ( ) == 0 {
100- return Ok ( ( ) ) ;
101- }
102-
103- if sketch. num_retained ( ) == 0 {
104- self . is_valid = true ;
105- self . table = new_default_table ( & self . table ) ;
106- return Ok ( ( ) ) ;
107- }
108-
109- // first update, copy or move incoming sketch
110- if !self . is_valid {
111- self . is_valid = true ;
112- let lg_size = ThetaHashTable :: lg_size_from_count_for_rebuild (
113- sketch. num_retained ( ) ,
114- HASH_TABLE_REBUILD_THRESHOLD ,
115- ) ;
116- self . table = ThetaHashTable :: from_raw_parts (
117- lg_size,
118- lg_size - 1 ,
119- ResizeFactor :: X1 ,
120- 1.0 ,
121- self . table . theta ( ) ,
122- self . table . hash_seed ( ) ,
123- self . table . is_empty ( ) ,
124- ) ;
125- for entry in sketch. iter ( ) {
126- let hash = entry. hash ( ) ;
127- if !self . table . try_insert_hash ( hash) {
128- return Err ( Error :: invalid_argument (
129- "Insert entries from sketch fail, possibly corrupted input sketch" ,
130- ) ) ;
131- }
132- }
133- // Safety check.
134- if self . table . num_retained ( ) != sketch. num_retained ( ) {
135- return Err ( Error :: invalid_argument (
136- "num entries mismatch, possibly corrupted input sketch" ,
137- ) ) ;
138- }
139- } else {
140- let max_matches = self . table . num_retained ( ) . min ( sketch. num_retained ( ) ) ;
141- let mut matched_entries = Vec :: with_capacity ( max_matches) ;
142- let mut count = 0 ;
143- for entry in sketch. iter ( ) {
144- let hash = entry. hash ( ) ;
145- if hash < self . table . theta ( ) {
146- if self . table . contains_hash ( hash) {
147- if matched_entries. len ( ) == max_matches {
148- return Err ( Error :: invalid_argument (
149- "max matches exceeded, possibly corrupted input sketch" ,
150- ) ) ;
151- }
152- matched_entries. push ( hash) ;
153- }
154- } else if sketch. is_ordered ( ) {
155- break ; // early stop for ordered sketches
156- }
157- count += 1 ;
158- }
159- // Safety check.
160- if count > sketch. num_retained ( ) {
161- return Err ( Error :: invalid_argument (
162- "more keys than expected, possibly corrupted input sketch" ,
163- ) ) ;
164- } else if !sketch. is_ordered ( ) && count < sketch. num_retained ( ) {
165- return Err ( Error :: invalid_argument (
166- "fewer keys than expected, possibly corrupted input sketch" ,
167- ) ) ;
168- }
169- if matched_entries. is_empty ( ) {
170- self . table = new_default_table ( & self . table ) ;
171- if self . table . theta ( ) == MAX_THETA {
172- self . table . set_empty ( true ) ;
173- }
174- } else {
175- let lg_size = ThetaHashTable :: lg_size_from_count_for_rebuild (
176- matched_entries. len ( ) ,
177- HASH_TABLE_REBUILD_THRESHOLD ,
178- ) ;
179- self . table = ThetaHashTable :: from_raw_parts (
180- lg_size,
181- lg_size - 1 ,
182- ResizeFactor :: X1 ,
183- 1.0 ,
184- self . table . theta ( ) ,
185- self . table . hash_seed ( ) ,
186- self . table . is_empty ( ) ,
187- ) ;
188- for hash in matched_entries {
189- if !self . table . try_insert_hash ( hash) {
190- return Err ( Error :: invalid_argument (
191- "duplicate key, possibly corrupted input sketch" ,
192- ) ) ;
193- }
194- }
195- }
196- }
197- Ok ( ( ) )
61+ self . raw . update ( sketch)
19862 }
19963
20064 /// Returns whether this operator has received at least one update.
20165 pub fn has_result ( & self ) -> bool {
202- self . is_valid
66+ self . raw . has_result ( )
20367 }
20468
20569 /// Returns the intersection result as a compact theta sketch.
@@ -209,19 +73,20 @@ impl ThetaIntersection {
20973 /// Panics if called before the first [`update`](Self::update).
21074 pub fn to_sketch ( & self , ordered : bool ) -> CompactThetaSketch {
21175 assert ! (
212- self . is_valid ,
76+ self . raw . has_result ( ) ,
21377 "ThetaIntersection::to_sketch() called before first update()"
21478 ) ;
215- let mut hashes: Vec < u64 > = self . table . iter ( ) . collect ( ) ;
216- if ordered {
217- hashes. sort_unstable ( ) ;
218- }
79+ let parts = self . raw . result ( ordered) ;
21980 CompactThetaSketch :: from_parts (
220- hashes,
221- self . table . theta ( ) ,
222- self . table . seed_hash ( ) ,
223- ordered,
224- self . table . is_empty ( ) ,
81+ parts
82+ . entries
83+ . into_iter ( )
84+ . map ( |entry| entry. hash ( ) )
85+ . collect ( ) ,
86+ parts. theta ,
87+ parts. seed_hash ,
88+ parts. ordered ,
89+ parts. empty ,
22590 )
22691 }
22792}
0 commit comments