datasketches-cpp
Loading...
Searching...
No Matches
var_opt_union_impl.hpp
1/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20#ifndef _VAR_OPT_UNION_IMPL_HPP_
21#define _VAR_OPT_UNION_IMPL_HPP_
22
23#include "var_opt_union.hpp"
24
25#include <cmath>
26#include <sstream>
27#include <stdexcept>
28
29namespace datasketches {
30
31template<typename T, typename A>
32var_opt_union<T, A>::var_opt_union(uint32_t max_k, const A& allocator) :
33 n_(0),
34 outer_tau_numer_(0.0),
35 outer_tau_denom_(0),
36 max_k_(max_k),
37 allocator_(allocator),
38 gadget_(max_k, var_opt_sketch<T, A>::DEFAULT_RESIZE_FACTOR, true, allocator)
39{}
40
41template<typename T, typename A>
42var_opt_union<T, A>::var_opt_union(const var_opt_union& other) :
43 n_(other.n_),
44 outer_tau_numer_(other.outer_tau_numer_),
45 outer_tau_denom_(other.outer_tau_denom_),
46 max_k_(other.max_k_),
47 allocator_(other.allocator_),
48 gadget_(other.gadget_)
49{}
50
51template<typename T, typename A>
52var_opt_union<T, A>::var_opt_union(var_opt_union&& other) noexcept :
53 n_(other.n_),
54 outer_tau_numer_(other.outer_tau_numer_),
55 outer_tau_denom_(other.outer_tau_denom_),
56 max_k_(other.max_k_),
57 allocator_(other.allocator_),
58 gadget_(std::move(other.gadget_))
59{}
60
61template<typename T, typename A>
62var_opt_union<T, A>::var_opt_union(uint64_t n, double outer_tau_numer, uint64_t outer_tau_denom,
63 uint32_t max_k, var_opt_sketch<T, A>&& gadget, const A& allocator) :
64 n_(n),
65 outer_tau_numer_(outer_tau_numer),
66 outer_tau_denom_(outer_tau_denom),
67 max_k_(max_k),
68 allocator_(allocator),
69 gadget_(gadget)
70{}
71
72template<typename T, typename A>
73var_opt_union<T, A>::~var_opt_union() {}
74
75template<typename T, typename A>
76var_opt_union<T, A>& var_opt_union<T, A>::operator=(const var_opt_union& other) {
77 var_opt_union union_copy(other);
78 std::swap(n_, union_copy.n_);
79 std::swap(outer_tau_numer_, union_copy.outer_tau_numer_);
80 std::swap(outer_tau_denom_, union_copy.outer_tau_denom_);
81 std::swap(max_k_, union_copy.max_k_);
82 std::swap(allocator_, other.allocator_);
83 std::swap(gadget_, union_copy.gadget_);
84 return *this;
85}
86
87template<typename T, typename A>
88var_opt_union<T, A>& var_opt_union<T, A>::operator=(var_opt_union&& other) {
89 std::swap(n_, other.n_);
90 std::swap(outer_tau_numer_, other.outer_tau_numer_);
91 std::swap(outer_tau_denom_, other.outer_tau_denom_);
92 std::swap(max_k_, other.max_k_);
93 std::swap(allocator_, other.allocator_);
94 std::swap(gadget_, other.gadget_);
95 return *this;
96}
97
98/*
99 * An empty union requires 8 bytes.
100 *
101 * <pre>
102 * Long || Start Byte Adr:
103 * Adr:
104 * || 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
105 * 0 || Preamble_Longs | SerVer | FamID | Flags |---------Max Res. Size (K)---------|
106 * </pre>
107 *
108 * A non-empty sketch requires 24 bytes of preamble for an under-full sample; once there are
109 * at least k items the sketch uses 32 bytes of preamble.
110 *
111 * The count of items seen is limited to 48 bits (~256 trillion) even though there are adjacent
112 * unused preamble bits. The acceptance probability for an item is a double in the range [0,1),
113 * limiting us to 53 bits of randomness due to details of the IEEE floating point format. To
114 * ensure meaningful probabilities as the items seen count approaches capacity, we intentionally
115 * use slightly fewer bits.
116 *
117 * Following the header are weights for the heavy items, then marks in the event this is a gadget.
118 * The serialized items come last.
119 *
120 * <pre>
121 * Long || Start Byte Adr:
122 * Adr:
123 * || 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
124 * 0 || Preamble_Longs | SerVer | FamID | Flags |---------Max Res. Size (K)---------|
125 *
126 * || 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
127 * 1 ||---------------------------Items Seen Count (N)--------------------------------|
128 *
129 * || 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
130 * 2 ||------------------------Outer Tau Numerator (double)---------------------------|
131 *
132 * || 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
133 * 3 ||----------------------Outer Tau Denominator (uint64_t)-------------------------|
134 * </pre>
135 */
136
137template<typename T, typename A>
138template<typename SerDe>
139var_opt_union<T, A> var_opt_union<T, A>::deserialize(std::istream& is, const SerDe& sd, const A& allocator) {
140 const auto preamble_longs = read<uint8_t>(is);
141 const auto serial_version = read<uint8_t>(is);
142 const auto family_id = read<uint8_t>(is);
143 const auto flags = read<uint8_t>(is);
144 const auto max_k = read<uint32_t>(is);
145
146 check_preamble_longs(preamble_longs, flags);
147 check_family_and_serialization_version(family_id, serial_version);
148
149 if (max_k == 0 || max_k > var_opt_constants::MAX_K) {
150 throw std::invalid_argument("k must be at least 1 and less than 2^31 - 1");
151 }
152
153 bool is_empty = flags & EMPTY_FLAG_MASK;
154
155 if (is_empty) {
156 if (!is.good())
157 throw std::runtime_error("error reading from std::istream");
158 else
159 return var_opt_union(max_k);
160 }
161
162 const auto items_seen = read<uint64_t>(is);
163 const auto outer_tau_numer = read<double>(is);
164 const auto outer_tau_denom = read<uint64_t>(is);
165
166 var_opt_sketch<T, A> gadget = var_opt_sketch<T, A>::deserialize(is, sd, allocator);
167
168 if (!is.good())
169 throw std::runtime_error("error reading from std::istream");
170
171 return var_opt_union(items_seen, outer_tau_numer, outer_tau_denom, max_k, std::move(gadget), allocator);
172}
173
174template<typename T, typename A>
175template<typename SerDe>
176var_opt_union<T, A> var_opt_union<T, A>::deserialize(const void* bytes, size_t size, const SerDe& sd, const A& allocator) {
177 ensure_minimum_memory(size, 8);
178 const char* ptr = static_cast<const char*>(bytes);
179 uint8_t preamble_longs;
180 ptr += copy_from_mem(ptr, preamble_longs);
181 uint8_t serial_version;
182 ptr += copy_from_mem(ptr, serial_version);
183 uint8_t family_id;
184 ptr += copy_from_mem(ptr, family_id);
185 uint8_t flags;
186 ptr += copy_from_mem(ptr, flags);
187 uint32_t max_k;
188 ptr += copy_from_mem(ptr, max_k);
189
190 check_preamble_longs(preamble_longs, flags);
191 check_family_and_serialization_version(family_id, serial_version);
192
193 if (max_k == 0 || max_k > var_opt_constants::MAX_K) {
194 throw std::invalid_argument("k must be at least 1 and less than 2^31 - 1");
195 }
196
197 bool is_empty = flags & EMPTY_FLAG_MASK;
198
199 if (is_empty) {
200 return var_opt_union(max_k);
201 }
202
203 ensure_minimum_memory(size, PREAMBLE_LONGS_NON_EMPTY << 3);
204 uint64_t items_seen;
205 ptr += copy_from_mem(ptr, items_seen);
206 double outer_tau_numer;
207 ptr += copy_from_mem(ptr, outer_tau_numer);
208 uint64_t outer_tau_denom;
209 ptr += copy_from_mem(ptr, outer_tau_denom);
210
211 const size_t gadget_size = size - (PREAMBLE_LONGS_NON_EMPTY << 3);
212 var_opt_sketch<T, A> gadget = var_opt_sketch<T, A>::deserialize(ptr, gadget_size, sd, allocator);
213
214 return var_opt_union(items_seen, outer_tau_numer, outer_tau_denom, max_k, std::move(gadget), allocator);
215}
216
217template<typename T, typename A>
218template<typename SerDe>
220 if (n_ == 0) {
221 return PREAMBLE_LONGS_EMPTY << 3;
222 } else {
223 return (PREAMBLE_LONGS_NON_EMPTY << 3) + gadget_.get_serialized_size_bytes(sd);
224 }
225}
226
227template<typename T, typename A>
228template<typename SerDe>
229void var_opt_union<T, A>::serialize(std::ostream& os, const SerDe& sd) const {
230 bool empty = (n_ == 0);
231
232 const uint8_t serialization_version(SER_VER);
233 const uint8_t family_id(FAMILY_ID);
234
235 uint8_t preamble_longs;
236 uint8_t flags;
237 if (empty) {
238 preamble_longs = PREAMBLE_LONGS_EMPTY;
239 flags = EMPTY_FLAG_MASK;
240 } else {
241 preamble_longs = PREAMBLE_LONGS_NON_EMPTY;
242 flags = 0;
243 }
244
245 write(os, preamble_longs);
246 write(os, serialization_version);
247 write(os, family_id);
248 write(os, flags);
249 write(os, max_k_);
250
251 if (!empty) {
252 write(os, n_);
253 write(os, outer_tau_numer_);
254 write(os, outer_tau_denom_);
255 gadget_.serialize(os, sd);
256 }
257}
258
259template<typename T, typename A>
260template<typename SerDe>
261std::vector<uint8_t, AllocU8<A>> var_opt_union<T, A>::serialize(unsigned header_size_bytes, const SerDe& sd) const {
262 const size_t size = header_size_bytes + get_serialized_size_bytes(sd);
263 std::vector<uint8_t, AllocU8<A>> bytes(size, 0, gadget_.allocator_);
264 uint8_t* ptr = bytes.data() + header_size_bytes;
265
266 const bool empty = n_ == 0;
267
268 const uint8_t serialization_version(SER_VER);
269 const uint8_t family_id(FAMILY_ID);
270
271 uint8_t preamble_longs;
272 uint8_t flags;
273
274 if (empty) {
275 preamble_longs = PREAMBLE_LONGS_EMPTY;
276 flags = EMPTY_FLAG_MASK;
277 } else {
278 preamble_longs = PREAMBLE_LONGS_NON_EMPTY;
279 flags = 0;
280 }
281
282 // first prelong
283 ptr += copy_to_mem(preamble_longs, ptr);
284 ptr += copy_to_mem(serialization_version, ptr);
285 ptr += copy_to_mem(family_id, ptr);
286 ptr += copy_to_mem(flags, ptr);
287 ptr += copy_to_mem(max_k_, ptr);
288
289 if (!empty) {
290 ptr += copy_to_mem(n_, ptr);
291 ptr += copy_to_mem(outer_tau_numer_, ptr);
292 ptr += copy_to_mem(outer_tau_denom_, ptr);
293
294 auto gadget_bytes = gadget_.serialize(0, sd);
295 ptr += copy_to_mem(gadget_bytes.data(), ptr, gadget_bytes.size() * sizeof(uint8_t));
296 }
297
298 return bytes;
299}
300
301template<typename T, typename A>
303 n_ = 0;
304 outer_tau_numer_ = 0.0;
305 outer_tau_denom_ = 0;
306 gadget_.reset();
307}
308
309template<typename T, typename A>
311 // Using a temporary stream for implementation here does not comply with AllocatorAwareContainer requirements.
312 // The stream does not support passing an allocator instance, and alternatives are complicated.
313 std::ostringstream os;
314 os << "### VarOpt Union SUMMARY:" << std::endl;
315 os << " n : " << n_ << std::endl;
316 os << " Max k : " << max_k_ << std::endl;
317 os << " Gadget Summary:" << std::endl;
318 os << gadget_.to_string();
319 os << "### END VarOpt Union SUMMARY" << std::endl;
320 return string<A>(os.str().c_str(), gadget_.allocator_);
321}
322
323template<typename T, typename A>
325 merge_items(sk);
326 resolve_tau(sk);
327}
328
329template<typename T, typename A>
331 merge_items(std::move(sk));
332 resolve_tau(sk); // don't need items, so ok even if they've been moved out
333}
334
335template<typename T, typename A>
336double var_opt_union<T, A>::get_outer_tau() const {
337 if (outer_tau_denom_ == 0) {
338 return 0.0;
339 } else {
340 return outer_tau_numer_ / outer_tau_denom_;
341 }
342}
343
344template<typename T, typename A>
345void var_opt_union<T, A>::merge_items(const var_opt_sketch<T, A>& sketch) {
346 if (sketch.n_ == 0) {
347 return;
348 }
349
350 n_ += sketch.n_;
351
352 // H region const_iterator
353 typename var_opt_sketch<T, A>::const_iterator h_itr(sketch, false, false);
354 typename var_opt_sketch<T, A>::const_iterator h_end(sketch, true, false);
355 while (h_itr != h_end) {
356 std::pair<const T&, const double> sample = *h_itr;
357 gadget_.update(sample.first, sample.second, false);
358 ++h_itr;
359 }
360
361 // Weight-correcting R region iterator (const_iterator doesn't do the correction)
362 typename var_opt_sketch<T, A>::iterator r_itr(sketch, false, true);
363 typename var_opt_sketch<T, A>::iterator r_end(sketch, true, true);
364 while (r_itr != r_end) {
365 std::pair<const T&, const double> sample = *r_itr;
366 gadget_.update(sample.first, sample.second, true);
367 ++r_itr;
368 }
369}
370
371template<typename T, typename A>
372void var_opt_union<T, A>::merge_items(var_opt_sketch<T, A>&& sketch) {
373 if (sketch.n_ == 0) {
374 return;
375 }
376
377 n_ += sketch.n_;
378
379 // H region iterator
380 typename var_opt_sketch<T, A>::iterator h_itr(sketch, false, false);
381 typename var_opt_sketch<T, A>::iterator h_end(sketch, true, false);
382 while (h_itr != h_end) {
383 std::pair<T&, double> sample = *h_itr;
384 gadget_.update(std::move(sample.first), sample.second, false);
385 ++h_itr;
386 }
387
388 // Weight-correcting R region iterator
389 typename var_opt_sketch<T, A>::iterator r_itr(sketch, false, true);
390 typename var_opt_sketch<T, A>::iterator r_end(sketch, true, true);
391 while (r_itr != r_end) {
392 std::pair<T&, double> sample = *r_itr;
393 gadget_.update(std::move(sample.first), sample.second, true);
394 ++r_itr;
395 }
396}
397
398template<typename T, typename A>
399void var_opt_union<T, A>::resolve_tau(const var_opt_sketch<T, A>& sketch) {
400 if (sketch.r_ > 0) {
401 const double sketch_tau = sketch.get_tau();
402 const double outer_tau = get_outer_tau();
403
404 if (outer_tau_denom_ == 0) {
405 // detect first estimation mode sketch and grab its tau
406 outer_tau_numer_ = sketch.total_wt_r_;
407 outer_tau_denom_ = sketch.r_;
408 } else if (sketch_tau > outer_tau) {
409 // switch to a bigger value of outer_tau
410 outer_tau_numer_ = sketch.total_wt_r_;
411 outer_tau_denom_ = sketch.r_;
412 } else if (sketch_tau == outer_tau) {
413 // Ok if previous equality test isn't quite perfect. Mistakes in either direction should
414 // be fairly benign.
415 // Without conceptually changing outer_tau, update number and denominator. In particular,
416 // add the total weight of the incoming reservoir to the running total.
417 outer_tau_numer_ += sketch.total_wt_r_;
418 outer_tau_denom_ += sketch.r_;
419 }
420
421 // do nothing if sketch's tau is smaller than outer_tau
422 }
423}
424
425template<typename T, typename A>
427 // If no marked items in H, gadget is already valid mathematically. We can return what is
428 // basically just a copy of the gadget.
429 if (gadget_.num_marks_in_h_ == 0) {
430 return simple_gadget_coercer();
431 } else {
432 // Copy of gadget. This may produce needless copying in the
433 // pseudo-exact case below, but should simplify the code without
434 // needing to make the gadget a pointer
435 var_opt_sketch<T, A> gcopy(gadget_, false, n_);
436
437 // At this point, we know that marked items are present in H. So:
438 // 1. Result will necessarily be in estimation mode
439 // 2. Marked items currently in H need to be absorbed into reservoir (R)
440 const bool is_pseudo_exact = detect_and_handle_subcase_of_pseudo_exact(gcopy);
441 if (!is_pseudo_exact) {
442 // continue with main logic
443 migrate_marked_items_by_decreasing_k(gcopy);
444 }
445 // sub-case was already detected and handled, so return the result
446 return gcopy;
447 }
448}
449
456template<typename T, typename A>
457var_opt_sketch<T, A> var_opt_union<T, A>::simple_gadget_coercer() const {
458 if (gadget_.num_marks_in_h_ != 0) throw std::logic_error("simple gadget coercer only applies if no marks");
459 return var_opt_sketch<T, A>(gadget_, true, n_);
460}
461
462// this is a condition checked in detect_and_handle_subcase_of_pseudo_exact()
463template<typename T, typename A>
464bool var_opt_union<T, A>::there_exist_unmarked_h_items_lighter_than_target(double threshold) const {
465 for (uint32_t i = 0; i < gadget_.h_; ++i) {
466 if ((gadget_.weights_[i] < threshold) && !gadget_.marks_[i]) {
467 return true;
468 }
469 }
470 return false;
471}
472
473template<typename T, typename A>
474bool var_opt_union<T, A>::detect_and_handle_subcase_of_pseudo_exact(var_opt_sketch<T, A>& sk) const {
475 // gadget is seemingly exact
476 const bool condition1 = gadget_.r_ == 0;
477
478 // but there are marked items in H, so only _pseudo_ exact
479 const bool condition2 = gadget_.num_marks_in_h_ > 0;
480
481 // if gadget is pseudo-exact and the number of marks equals outer_tau_denom, then we can deduce
482 // from the bookkeeping logic of resolve_tau() that all estimation mode input sketches must
483 // have had the same tau, so we can throw all of the marked items into a common reservoir.
484 const bool condition3 = gadget_.num_marks_in_h_ == outer_tau_denom_;
485
486 if (!(condition1 && condition2 && condition3)) {
487 return false;
488 } else {
489
490 // explicitly enforce rule that items in H should not be lighter than the sketch's tau
491 const bool anti_condition4 = there_exist_unmarked_h_items_lighter_than_target(gadget_.get_tau());
492 if (anti_condition4) {
493 return false;
494 } else {
495 // conditions 1 through 4 hold
496 mark_moving_gadget_coercer(sk);
497 return true;
498 }
499 }
500}
501
510template<typename T, typename A>
511void var_opt_union<T, A>::mark_moving_gadget_coercer(var_opt_sketch<T, A>& sk) const {
512 const uint32_t result_k = gadget_.h_ + gadget_.r_;
513
514 uint32_t result_h = 0;
515 uint32_t result_r = 0;
516 size_t next_r_pos = result_k; // = (result_k+1)-1, to fill R region from back to front
517
518 double* wts = AllocDouble(allocator_).allocate(result_k + 1);
519 T* data = A(allocator_).allocate(result_k + 1);
520
521 // insert R region items, ignoring weights
522 // Currently (May 2017) this next block is unreachable; this coercer is used only in the
523 // pseudo-exact case in which case there are no items natively in R, only marked items in H
524 // that will be moved into R as part of the coercion process.
525 // Addedndum (Jan 2020): Cleanup at end of method assumes R count is 0
526 const size_t final_idx = gadget_.get_num_samples();
527 for (size_t idx = gadget_.h_ + 1; idx <= final_idx; ++idx) {
528 new (&data[next_r_pos]) T(gadget_.data_[idx]);
529 wts[next_r_pos] = gadget_.weights_[idx];
530 ++result_r;
531 --next_r_pos;
532 }
533
534 double transferred_weight = 0;
535
536 // insert H region items
537 for (size_t idx = 0; idx < gadget_.h_; ++idx) {
538 if (gadget_.marks_[idx]) {
539 new (&data[next_r_pos]) T(gadget_.data_[idx]);
540 wts[next_r_pos] = -1.0;
541 transferred_weight += gadget_.weights_[idx];
542 ++result_r;
543 --next_r_pos;
544 } else {
545 new (&data[result_h]) T(gadget_.data_[idx]);
546 wts[result_h] = gadget_.weights_[idx];
547 ++result_h;
548 }
549 }
550
551 if (result_h + result_r != result_k) throw std::logic_error("H + R counts must equal k");
552 if (std::abs(transferred_weight - outer_tau_numer_) > 1e-10) {
553 throw std::logic_error("unexpected mismatch in transferred weight");
554 }
555
556 const double result_r_weight = gadget_.total_wt_r_ + transferred_weight;
557 const uint64_t result_n = n_;
558
559 // explicitly set weight value for the gap
560 wts[result_h] = -1.0;
561
562 // clean up arrays in input sketch, replace with new values
563 AllocBool(allocator_).deallocate(sk.marks_, sk.curr_items_alloc_);
564 AllocDouble(allocator_).deallocate(sk.weights_, sk.curr_items_alloc_);
565 for (size_t i = 0; i < result_k; ++i) { sk.data_[i].~T(); } // assumes everything in H region, no gap
566 A(allocator_).deallocate(sk.data_, sk.curr_items_alloc_);
567
568 sk.data_ = data;
569 sk.weights_ = wts;
570 sk.marks_ = nullptr;
571 sk.num_marks_in_h_ = 0;
572 sk.curr_items_alloc_ = result_k + 1;
573 sk.k_ = result_k;
574 sk.n_ = result_n;
575 sk.h_ = result_h;
576 sk.r_ = result_r;
577 sk.total_wt_r_ = result_r_weight;
578}
579
580// this is basically a continuation of get_result(), but modifying the input gadget copy
581template<typename T, typename A>
582void var_opt_union<T, A>::migrate_marked_items_by_decreasing_k(var_opt_sketch<T, A>& gcopy) const {
583 const uint32_t r_count = gcopy.r_;
584 const uint32_t h_count = gcopy.h_;
585 const uint32_t k = gcopy.k_;
586
587 // should be ensured by caller
588 if (gcopy.num_marks_in_h_ == 0) throw std::logic_error("unexpectedly found no marked items to migrate");
589 // either full (of samples), in pseudo-exact mode, or both
590 if ((r_count != 0) && ((h_count + r_count) != k)) throw std::logic_error("invalid gadget state");
591
592 // if non-full and pseudo-exact, change k so that gcopy is full
593 if ((r_count == 0) && (h_count < k)) {
594 gcopy.k_ = h_count; // may leave extra space allocated but that's ok
595 }
596
597 // Now k equals the number of samples, so reducing k will increase tau.
598 // Also, we know that there are at least 2 samples because 0 or 1 would have been handled
599 // by the earlier logic in get_result()
600 gcopy.decrease_k_by_1();
601
602 // gcopy is now in estimation mode, just like the final result must be (due to marked items)
603 if (gcopy.get_tau() == 0.0) throw std::logic_error("gadget must be in sampling mode");
604
605 // keep reducing k until all marked items have been absorbed into the reservoir
606 while (gcopy.num_marks_in_h_ > 0) {
607 // gcopy.k_ >= 2 because h_ and r_ are both at least 1, but checked in next method anyway
608 gcopy.decrease_k_by_1();
609 }
610
611 gcopy.strip_marks();
612}
613
614template<typename T, typename A>
615void var_opt_union<T, A>::check_preamble_longs(uint8_t preamble_longs, uint8_t flags) {
616 bool is_empty(flags & EMPTY_FLAG_MASK);
617
618 if (is_empty) {
619 if (preamble_longs != PREAMBLE_LONGS_EMPTY) {
620 throw std::invalid_argument("Possible corruption: Preamble longs must be "
621 + std::to_string(PREAMBLE_LONGS_EMPTY) + " for an empty sketch. Found: "
622 + std::to_string(preamble_longs));
623 }
624 } else {
625 if (preamble_longs != PREAMBLE_LONGS_NON_EMPTY) {
626 throw std::invalid_argument("Possible corruption: Preamble longs must be "
627 + std::to_string(PREAMBLE_LONGS_NON_EMPTY)
628 + " for a non-empty sketch. Found: " + std::to_string(preamble_longs));
629 }
630 }
631}
632
633template<typename T, typename A>
634void var_opt_union<T, A>::check_family_and_serialization_version(uint8_t family_id, uint8_t ser_ver) {
635 if (family_id == FAMILY_ID) {
636 if (ser_ver != SER_VER) {
637 throw std::invalid_argument("Possible corruption: VarOpt Union serialization version must be "
638 + std::to_string(SER_VER) + ". Found: " + std::to_string(ser_ver));
639 }
640 return;
641 }
642 // TODO: extend to handle reservoir sampling
643
644 throw std::invalid_argument("Possible corruption: VarOpt Union family id must be "
645 + std::to_string(FAMILY_ID) + ". Found: " + std::to_string(family_id));
646}
647
648} // namespace datasketches
649
650#endif // _VAR_OPT_UNION_IMPL_HPP_
This sketch samples data from a stream of items.
Definition var_opt_sketch.hpp:72
vector_bytes serialize(unsigned header_size_bytes=0, const SerDe &sd=SerDe()) const
NOTE: This method may be deprecated in a future version.
var_opt_sketch< T, A > get_result() const
Gets the varopt sketch resulting from the union of any input sketches.
Definition var_opt_union_impl.hpp:426
void update(const var_opt_sketch< T, A > &sk)
Updates this union with the given sketch This method takes an lvalue.
Definition var_opt_union_impl.hpp:324
size_t get_serialized_size_bytes(const SerDe &sd=SerDe()) const
Computes size needed to serialize the current state of the union.
Definition var_opt_union_impl.hpp:219
void reset()
Resets the union to its default, empty state.
Definition var_opt_union_impl.hpp:302
string< A > to_string() const
Prints a summary of the union as a string.
Definition var_opt_union_impl.hpp:310
const resize_factor DEFAULT_RESIZE_FACTOR
default resize factor
Definition theta_constants.hpp:33
DataSketches namespace.
Definition binomial_bounds.hpp:38