1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
/*
* 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.
*/
#ifndef TUPLE_SKETCH_HPP_
#define TUPLE_SKETCH_HPP_
#include <string>
#include "serde.hpp"
#include "theta_update_sketch_base.hpp"
namespace datasketches {
// forward declarations
template<typename S, typename A> class tuple_sketch;
template<typename S, typename U, typename P, typename A> class update_tuple_sketch;
template<typename S, typename A> class compact_tuple_sketch;
template<typename A> class theta_sketch_alloc;
template<typename K, typename V>
struct pair_extract_key {
K& operator()(std::pair<K, V>& entry) const {
return entry.first;
}
const K& operator()(const std::pair<K, V>& entry) const {
return entry.first;
}
};
/**
* Base class for Tuple sketch.
* This is an extension of Theta sketch that allows keeping arbitrary Summary associated with each retained key.
*/
template<
typename Summary,
typename Allocator = std::allocator<Summary>
>
class tuple_sketch {
public:
using Entry = std::pair<uint64_t, Summary>;
using ExtractKey = pair_extract_key<uint64_t, Summary>;
using iterator = theta_iterator<Entry, ExtractKey>;
using const_iterator = theta_const_iterator<Entry, ExtractKey>;
virtual ~tuple_sketch() = default;
/**
* @return allocator
*/
virtual Allocator get_allocator() const = 0;
/**
* @return true if this sketch represents an empty set (not the same as no retained entries!)
*/
virtual bool is_empty() const = 0;
/**
* @return estimate of the distinct count of the input stream
*/
double get_estimate() const;
/**
* Returns the approximate lower error bound given a number of standard deviations over an arbitrary number of
* items stored in the sketch.
* This parameter is similar to the number of standard deviations of the normal distribution
* and corresponds to approximately 67%, 95% and 99% confidence intervals.
* @param num_std_devs number of Standard Deviations (1, 2 or 3)
* @param num_subset_entries number of items from {0, 1, ..., get_num_retained()} over which to estimate the bound
* @return the lower bound
*/
double get_lower_bound(uint8_t num_std_devs, uint32_t num_subset_entries) const ;
/**
* Returns the approximate lower error bound given a number of standard deviations.
* This parameter is similar to the number of standard deviations of the normal distribution
* and corresponds to approximately 67%, 95% and 99% confidence intervals.
* @param num_std_devs number of Standard Deviations (1, 2 or 3)
* @return the lower bound
*/
double get_lower_bound(uint8_t num_std_devs) const;
/**
* Returns the approximate upper error bound given a number of standard deviations over an arbitrary number of
* items stored in the sketch.
* This parameter is similar to the number of standard deviations of the normal distribution
* and corresponds to approximately 67%, 95% and 99% confidence intervals.
* @param num_std_devs number of Standard Deviations (1, 2 or 3)
* @param num_subset_entries number of items from {0, 1, ..., get_num_retained()} over which to estimate the bound
* @return the lower bound
*/
double get_upper_bound(uint8_t num_std_devs, uint32_t num_subset_entries) const ;
/**
* Returns the approximate upper error bound given a number of standard deviations.
* This parameter is similar to the number of standard deviations of the normal distribution
* and corresponds to approximately 67%, 95% and 99% confidence intervals.
* @param num_std_devs number of Standard Deviations (1, 2 or 3)
* @return the upper bound
*/
double get_upper_bound(uint8_t num_std_devs) const;
/**
* @return true if the sketch is in estimation mode (as opposed to exact mode)
*/
bool is_estimation_mode() const;
/**
* @return theta as a fraction from 0 to 1 (effective sampling rate)
*/
double get_theta() const;
/**
* @return theta as a positive integer between 0 and LLONG_MAX
*/
virtual uint64_t get_theta64() const = 0;
/**
* @return the number of retained entries in the sketch
*/
virtual uint32_t get_num_retained() const = 0;
/**
* @return hash of the seed that was used to hash the input
*/
virtual uint16_t get_seed_hash() const = 0;
/**
* @return true if retained entries are ordered
*/
virtual bool is_ordered() const = 0;
/**
* Provides a human-readable summary of this sketch as a string
* @param print_items if true include the list of items retained by the sketch
* @return sketch summary as a string
*/
string<Allocator> to_string(bool print_items = false) const;
/**
* Iterator over entries in this sketch.
* @return begin iterator
*/
virtual iterator begin() = 0;
/**
* Iterator pointing past the valid range.
* Not to be incremented or dereferenced.
* @return end iterator
*/
virtual iterator end() = 0;
/**
* Const iterator over entries in this sketch.
* @return begin const iterator
*/
virtual const_iterator begin() const = 0;
/**
* Const iterator pointing past the valid range.
* Not to be incremented or dereferenced.
* @return end const iterator
*/
virtual const_iterator end() const = 0;
protected:
virtual void print_specifics(std::ostringstream& os) const = 0;
static uint16_t get_seed_hash(uint64_t seed);
static void check_sketch_type(uint8_t actual, uint8_t expected);
static void check_serial_version(uint8_t actual, uint8_t expected);
static void check_seed_hash(uint16_t actual, uint16_t expected);
};
// update sketch
// for types with defined default constructor and + operation
template<typename Summary, typename Update>
struct default_tuple_update_policy {
Summary create() const {
return Summary();
}
void update(Summary& summary, const Update& update) const {
summary += update;
}
};
/**
* Update Tuple sketch.
* The purpose of this class is to build a Tuple sketch from input data via the update() methods.
* There is no constructor. Use builder instead.
*/
template<
typename Summary,
typename Update = Summary,
typename Policy = default_tuple_update_policy<Summary, Update>,
typename Allocator = std::allocator<Summary>
>
class update_tuple_sketch: public tuple_sketch<Summary, Allocator> {
public:
using Base = tuple_sketch<Summary, Allocator>;
using Entry = typename Base::Entry;
using ExtractKey = typename Base::ExtractKey;
using iterator = typename Base::iterator;
using const_iterator = typename Base::const_iterator;
using AllocEntry = typename std::allocator_traits<Allocator>::template rebind_alloc<Entry>;
using tuple_map = theta_update_sketch_base<Entry, ExtractKey, AllocEntry>;
using resize_factor = typename tuple_map::resize_factor;
// No constructor here. Use builder instead.
class builder;
update_tuple_sketch(const update_tuple_sketch&) = default;
update_tuple_sketch(update_tuple_sketch&&) noexcept = default;
virtual ~update_tuple_sketch() = default;
update_tuple_sketch& operator=(const update_tuple_sketch&) = default;
update_tuple_sketch& operator=(update_tuple_sketch&&) = default;
virtual Allocator get_allocator() const;
virtual bool is_empty() const;
virtual bool is_ordered() const;
virtual uint64_t get_theta64() const;
virtual uint32_t get_num_retained() const;
virtual uint16_t get_seed_hash() const;
/**
* @return configured nominal number of entries in the sketch
*/
uint8_t get_lg_k() const;
/**
* @return configured resize factor of the sketch
*/
resize_factor get_rf() const;
/**
* Update this sketch with a given string.
* @param key string to update the sketch with
* @param value to update the sketch with
*/
template<typename FwdUpdate>
inline void update(const std::string& key, FwdUpdate&& value);
/**
* Update this sketch with a given unsigned 64-bit integer.
* @param key uint64_t to update the sketch with
* @param value to update the sketch with
*/
template<typename FwdUpdate>
inline void update(uint64_t key, FwdUpdate&& value);
/**
* Update this sketch with a given signed 64-bit integer.
* @param key int64_t to update the sketch with
* @param value to update the sketch with
*/
template<typename FwdUpdate>
inline void update(int64_t key, FwdUpdate&& value);
/**
* Update this sketch with a given unsigned 32-bit integer.
* For compatibility with Java implementation.
* @param key uint32_t to update the sketch with
* @param value to update the sketch with
*/
template<typename FwdUpdate>
inline void update(uint32_t key, FwdUpdate&& value);
/**
* Update this sketch with a given signed 32-bit integer.
* For compatibility with Java implementation.
* @param key int32_t to update the sketch with
* @param value to update the sketch with
*/
template<typename FwdUpdate>
inline void update(int32_t key, FwdUpdate&& value);
/**
* Update this sketch with a given unsigned 16-bit integer.
* For compatibility with Java implementation.
* @param key uint16_t to update the sketch with
* @param value to update the sketch with
*/
template<typename FwdUpdate>
inline void update(uint16_t key, FwdUpdate&& value);
/**
* Update this sketch with a given signed 16-bit integer.
* For compatibility with Java implementation.
* @param key int16_t to update the sketch with
* @param value to update the sketch with
*/
template<typename FwdUpdate>
inline void update(int16_t key, FwdUpdate&& value);
/**
* Update this sketch with a given unsigned 8-bit integer.
* For compatibility with Java implementation.
* @param key uint8_t to update the sketch with
* @param value to update the sketch with
*/
template<typename FwdUpdate>
inline void update(uint8_t key, FwdUpdate&& value);
/**
* Update this sketch with a given signed 8-bit integer.
* For compatibility with Java implementation.
* @param key int8_t to update the sketch with
* @param value to update the sketch with
*/
template<typename FwdUpdate>
inline void update(int8_t key, FwdUpdate&& value);
/**
* Update this sketch with a given double-precision floating point value.
* For compatibility with Java implementation.
* @param key double to update the sketch with
* @param value to update the sketch with
*/
template<typename FwdUpdate>
inline void update(double key, FwdUpdate&& value);
/**
* Update this sketch with a given floating point value.
* For compatibility with Java implementation.
* @param key float to update the sketch with
* @param value to update the sketch with
*/
template<typename FwdUpdate>
inline void update(float key, FwdUpdate&& value);
/**
* Update this sketch with given data of any type.
* This is a "universal" update that covers all cases above,
* but may produce different hashes.
* Be very careful to hash input values consistently using the same approach
* both over time and on different platforms
* and while passing sketches between C++ environment and Java environment.
* Otherwise two sketches that should represent overlapping sets will be disjoint
* For instance, for signed 32-bit values call update(int32_t) method above,
* which does widening conversion to int64_t, if compatibility with Java is expected
* @param key pointer to the data
* @param length of the data in bytes
* @param value to update the sketch with
*/
template<typename FwdUpdate>
void update(const void* key, size_t length, FwdUpdate&& value);
/**
* Remove retained entries in excess of the nominal size k (if any)
*/
void trim();
/**
* Reset the sketch to the initial empty state
*/
void reset();
/**
* Converts this sketch to a compact sketch (ordered or unordered).
* @param ordered optional flag to specify if an ordered sketch should be produced
* @return compact sketch
*/
compact_tuple_sketch<Summary, Allocator> compact(bool ordered = true) const;
/**
* Produces a Compact Tuple sketch from this sketch
* by applying a given predicate to each entry.
* @param predicate should return true for the entries to keep
* @return compact sketch with the entries retained according to the predicate
*/
template<typename Predicate>
compact_tuple_sketch<Summary, Allocator> filter(const Predicate& predicate) const;
virtual iterator begin();
virtual iterator end();
virtual const_iterator begin() const;
virtual const_iterator end() const;
protected:
Policy policy_;
tuple_map map_;
// for builder
update_tuple_sketch(uint8_t lg_cur_size, uint8_t lg_nom_size, resize_factor rf, float p, uint64_t theta, uint64_t seed, const Policy& policy, const Allocator& allocator);
virtual void print_specifics(std::ostringstream& os) const;
};
/**
* Compact Tuple sketch.
* This is an immutable form of the Tuple sketch, the form that can be serialized and deserialized.
*/
template<
typename Summary,
typename Allocator = std::allocator<Summary>
>
class compact_tuple_sketch: public tuple_sketch<Summary, Allocator> {
public:
using Base = tuple_sketch<Summary, Allocator>;
using Entry = typename Base::Entry;
using ExtractKey = typename Base::ExtractKey;
using iterator = typename Base::iterator;
using const_iterator = typename Base::const_iterator;
using AllocEntry = typename std::allocator_traits<Allocator>::template rebind_alloc<Entry>;
using AllocU64 = typename std::allocator_traits<Allocator>::template rebind_alloc<uint64_t>;
using AllocBytes = typename std::allocator_traits<Allocator>::template rebind_alloc<uint8_t>;
using vector_bytes = std::vector<uint8_t, AllocBytes>;
using comparator = compare_by_key<ExtractKey>;
static const uint8_t SERIAL_VERSION_LEGACY = 1;
static const uint8_t SERIAL_VERSION = 3;
static const uint8_t SKETCH_FAMILY = 9;
static const uint8_t SKETCH_TYPE = 1;
static const uint8_t SKETCH_TYPE_LEGACY = 5;
enum flags { IS_BIG_ENDIAN, IS_READ_ONLY, IS_EMPTY, IS_COMPACT, IS_ORDERED };
// Instances of this type can be obtained:
// - by compacting an update_tuple_sketch
// - as a result of a set operation
// - by deserializing a previously serialized compact sketch
/**
* Copy constructor.
* Constructs a compact sketch from another sketch (either update or compact)
* @param other sketch to be copied
* @param ordered if true make the resulting sketch ordered
*/
compact_tuple_sketch(const Base& other, bool ordered);
/**
* Copy constructor.
* @param other sketch to be copied
*/
compact_tuple_sketch(const compact_tuple_sketch& other) = default;
/**
* Move constructor.
* @param other sketch to be moved
*/
compact_tuple_sketch(compact_tuple_sketch&&) noexcept;
virtual ~compact_tuple_sketch() = default;
/**
* Copy assignment
* @param other sketch to be copied
* @return reference to this sketch
*/
compact_tuple_sketch& operator=(const compact_tuple_sketch& other) = default;
/**
* Move assignment
* @param other sketch to be moved
* @return reference to this sketch
*/
compact_tuple_sketch& operator=(compact_tuple_sketch&& other) = default;
/**
* Constructor from Theta sketch
* @param other Theta sketch to be constructed from
* @param summary Summary instance to be associated with each entry
* @param ordered if true make the resulting sketch ordered
*/
compact_tuple_sketch(const theta_sketch_alloc<AllocU64>& other, const Summary& summary, bool ordered = true);
virtual Allocator get_allocator() const;
virtual bool is_empty() const;
virtual bool is_ordered() const;
virtual uint64_t get_theta64() const;
virtual uint32_t get_num_retained() const;
virtual uint16_t get_seed_hash() const;
/**
* Produces a Compact Tuple sketch from this sketch
* by applying a given predicate to each entry.
* @param predicate should return true for the entries to keep
* @return compact sketch with the entries retained according to the predicate
*/
template<typename Predicate>
compact_tuple_sketch filter(const Predicate& predicate) const;
/**
* Produces a Compact Tuple sketch from a given sketch (Update or Compact)
* by applying a given predicate to each entry.
* @param sketch input sketch
* @param predicate should return true for the entries to keep
* @return compact sketch with the entries retained according to the predicate
*/
template<typename Sketch, typename Predicate>
static compact_tuple_sketch filter(const Sketch& sketch, const Predicate& predicate);
/**
* This method serializes the sketch into a given stream in a binary form
* @param os output stream
* @param sd instance of a SerDe
*/
template<typename SerDe = serde<Summary>>
void serialize(std::ostream& os, const SerDe& sd = SerDe()) const;
/**
* This method serializes the sketch as a vector of bytes.
* An optional header can be reserved in front of the sketch.
* It is a blank space of a given size.
* This header is used in Datasketches PostgreSQL extension.
* @param header_size_bytes space to reserve in front of the sketch
* @param sd instance of a SerDe
* @return serialized sketch as a vector of bytes
*/
template<typename SerDe = serde<Summary>>
vector_bytes serialize(unsigned header_size_bytes = 0, const SerDe& sd = SerDe()) const;
virtual iterator begin();
virtual iterator end();
virtual const_iterator begin() const;
virtual const_iterator end() const;
/**
* This method deserializes a sketch from a given stream.
* @param is input stream
* @param seed the seed for the hash function that was used to create the sketch
* @param sd instance of a SerDe
* @param allocator instance of an Allocator
* @return an instance of a sketch
*/
template<typename SerDe = serde<Summary>>
static compact_tuple_sketch deserialize(std::istream& is, uint64_t seed = DEFAULT_SEED,
const SerDe& sd = SerDe(), const Allocator& allocator = Allocator());
/**
* This method deserializes a sketch from a given array of bytes.
* @param bytes pointer to the array of bytes
* @param size the size of the array
* @param seed the seed for the hash function that was used to create the sketch
* @param sd instance of a SerDe
* @param allocator instance of an Allocator
* @return an instance of the sketch
*/
template<typename SerDe = serde<Summary>>
static compact_tuple_sketch deserialize(const void* bytes, size_t size, uint64_t seed = DEFAULT_SEED,
const SerDe& sd = SerDe(), const Allocator& allocator = Allocator());
protected:
bool is_empty_;
bool is_ordered_;
uint16_t seed_hash_;
uint64_t theta_;
std::vector<Entry, AllocEntry> entries_;
/**
* Computes size needed to serialize summaries in the sketch.
* This version is for fixed-size arithmetic types (integral and floating point).
* @return size in bytes needed to serialize summaries in this sketch
*/
template<typename SerDe, typename SS = Summary, typename std::enable_if<std::is_arithmetic<SS>::value, int>::type = 0>
size_t get_serialized_size_summaries_bytes(const SerDe& sd) const;
/**
* Computes size needed to serialize summaries in the sketch.
* This version is for all other types and can be expensive since every item needs to be looked at.
* @return size in bytes needed to serialize summaries in this sketch
*/
template<typename SerDe, typename SS = Summary, typename std::enable_if<!std::is_arithmetic<SS>::value, int>::type = 0>
size_t get_serialized_size_summaries_bytes(const SerDe& sd) const;
// for deserialize
class deleter_of_summaries {
public:
deleter_of_summaries(uint32_t num, bool destroy, const Allocator& allocator):
allocator_(allocator), num_(num), destroy_(destroy) {}
void set_destroy(bool destroy) { destroy_ = destroy; }
void operator() (Summary* ptr) {
if (ptr != nullptr) {
if (destroy_) {
for (uint32_t i = 0; i < num_; ++i) ptr[i].~Summary();
}
allocator_.deallocate(ptr, num_);
}
}
private:
Allocator allocator_;
uint32_t num_;
bool destroy_;
};
virtual void print_specifics(std::ostringstream& os) const;
template<typename E, typename EK, typename P, typename S, typename CS, typename A> friend class theta_union_base;
template<typename E, typename EK, typename P, typename S, typename CS, typename A> friend class theta_intersection_base;
template<typename E, typename EK, typename CS, typename A> friend class theta_set_difference_base;
compact_tuple_sketch(bool is_empty, bool is_ordered, uint16_t seed_hash, uint64_t theta, std::vector<Entry, AllocEntry>&& entries);
};
/// Tuple base builder
template<typename Derived, typename Policy, typename Allocator>
class tuple_base_builder: public theta_base_builder<Derived, Allocator> {
public:
tuple_base_builder(const Policy& policy, const Allocator& allocator);
protected:
Policy policy_;
};
/// Update Tuple sketch builder
template<typename S, typename U, typename P, typename A>
class update_tuple_sketch<S, U, P, A>::builder: public tuple_base_builder<builder, P, A> {
public:
/**
* Constructor
* Creates and instance of the builder with default parameters.
* @param policy user-defined way of creating and updating Summary
* @param allocator instance of an Allocator to pass to created sketches
*/
builder(const P& policy = P(), const A& allocator = A());
/**
* This is to create an instance of the sketch with predefined parameters.
* @return an instance of the sketch
*/
update_tuple_sketch<S, U, P, A> build() const;
};
} /* namespace datasketches */
#include "tuple_sketch_impl.hpp"
#endif