datasketches-cpp
Loading...
Searching...
No Matches
theta_sketch_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 THETA_SKETCH_IMPL_HPP_
21#define THETA_SKETCH_IMPL_HPP_
22
23#include <sstream>
24#include <vector>
25#include <stdexcept>
26#include <algorithm>
27
28#include "binomial_bounds.hpp"
29#include "theta_helpers.hpp"
30#include "count_zeros.hpp"
31#include "bit_packing.hpp"
32#include "memory_operations.hpp"
33
34namespace datasketches {
35
36template<typename A>
40
41template<typename A>
43 return static_cast<double>(get_theta64()) /
44 static_cast<double>(theta_constants::MAX_THETA);
45}
46
47template<typename A>
51
52template<typename A>
53double base_theta_sketch_alloc<A>::get_lower_bound(uint8_t num_std_devs) const {
54 if (!is_estimation_mode()) return get_num_retained();
55 return binomial_bounds::get_lower_bound(get_num_retained(), get_theta(), num_std_devs);
56}
57
58template<typename A>
59double base_theta_sketch_alloc<A>::get_upper_bound(uint8_t num_std_devs) const {
60 if (!is_estimation_mode()) return get_num_retained();
61 return binomial_bounds::get_upper_bound(get_num_retained(), get_theta(), num_std_devs);
62}
64template<typename A>
65string<A> base_theta_sketch_alloc<A>::to_string(bool print_details) const {
66 // Using a temporary stream for implementation here does not comply with AllocatorAwareContainer requirements.
67 // The stream does not support passing an allocator instance, and alternatives are complicated.
68 std::ostringstream os;
69 os << "### Theta sketch summary:" << std::endl;
70 os << " num retained entries : " << this->get_num_retained() << std::endl;
71 os << " seed hash : " << this->get_seed_hash() << std::endl;
72 os << " empty? : " << (this->is_empty() ? "true" : "false") << std::endl;
73 os << " ordered? : " << (this->is_ordered() ? "true" : "false") << std::endl;
74 os << " estimation mode? : " << (this->is_estimation_mode() ? "true" : "false") << std::endl;
75 os << " theta (fraction) : " << this->get_theta() << std::endl;
76 os << " theta (raw 64-bit) : " << this->get_theta64() << std::endl;
77 os << " estimate : " << this->get_estimate() << std::endl;
78 os << " lower bound 95% conf : " << this->get_lower_bound(2) << std::endl;
79 os << " upper bound 95% conf : " << this->get_upper_bound(2) << std::endl;
80 print_specifics(os);
81 os << "### End sketch summary" << std::endl;
82 if (print_details) {
83 print_items(os);
84 }
85 return string<A>(os.str().c_str(), this->get_allocator());
87
88template<typename A>
89void theta_sketch_alloc<A>::print_items(std::ostringstream& os) const {
90 os << "### Retained entries" << std::endl;
91 for (const auto& hash: *this) {
92 os << hash << std::endl;
93 }
94 os << "### End retained entries" << std::endl;
95}
96
97
98// update sketch
99
100template<typename A>
101update_theta_sketch_alloc<A>::update_theta_sketch_alloc(uint8_t lg_cur_size, uint8_t lg_nom_size, resize_factor rf,
102 float p, uint64_t theta, uint64_t seed, const A& allocator):
103table_(lg_cur_size, lg_nom_size, rf, p, theta, seed, allocator)
104{}
105
106template<typename A>
108 return table_.allocator_;
109}
110
111template<typename A>
113 return table_.is_empty_;
114}
115
116template<typename A>
118 return table_.num_entries_ > 1 ? false : true;
119}
120
121template<typename A>
123 return is_empty() ? theta_constants::MAX_THETA : table_.theta_;
124}
125
126template<typename A>
128 return table_.num_entries_;
129}
130
131template<typename A>
133 return compute_seed_hash(table_.seed_);
134}
135
136template<typename A>
138 return table_.lg_nom_size_;
139}
140
141template<typename A>
142auto update_theta_sketch_alloc<A>::get_rf() const -> resize_factor {
143 return table_.rf_;
144}
145
146template<typename A>
148 update(&value, sizeof(value));
149}
150
151template<typename A>
153 update(&value, sizeof(value));
154}
155
156template<typename A>
158 update(static_cast<int32_t>(value));
159}
160
161template<typename A>
163 update(static_cast<int64_t>(value));
164}
165
166template<typename A>
168 update(static_cast<int16_t>(value));
169}
170
171template<typename A>
173 update(static_cast<int64_t>(value));
174}
175
176template<typename A>
178 update(static_cast<int8_t>(value));
179}
180
181template<typename A>
183 update(static_cast<int64_t>(value));
184}
185
186template<typename A>
188 update(canonical_double(value));
189}
190
191template<typename A>
193 update(static_cast<double>(value));
194}
195
196template<typename A>
197void update_theta_sketch_alloc<A>::update(const std::string& value) {
198 if (value.empty()) return;
199 update(value.c_str(), value.length());
200}
201
202template<typename A>
203void update_theta_sketch_alloc<A>::update(const void* data, size_t length) {
204 const uint64_t hash = table_.hash_and_screen(data, length);
205 if (hash == 0) return;
206 auto result = table_.find(hash);
207 if (!result.second) {
208 table_.insert(result.first, hash);
209 }
210}
211
212template<typename A>
214 table_.trim();
215}
216
217template<typename A>
219 table_.reset();
220}
221
222template<typename A>
224 return iterator(table_.entries_, 1 << table_.lg_cur_size_, 0);
225}
226
227template<typename A>
229 return iterator(nullptr, 0, 1 << table_.lg_cur_size_);
230}
231
232template<typename A>
233auto update_theta_sketch_alloc<A>::begin() const -> const_iterator {
234 return const_iterator(table_.entries_, 1 << table_.lg_cur_size_, 0);
235}
236
237template<typename A>
238auto update_theta_sketch_alloc<A>::end() const -> const_iterator {
239 return const_iterator(nullptr, 0, 1 << table_.lg_cur_size_);
240}
241
242template<typename A>
244 if (!trim) return compact_theta_sketch_alloc<A>(*this, ordered);
245
246 std::vector<uint64_t, A> entries(table_.allocator_);
247 if (this->is_empty()) {
248 return compact_theta_sketch_alloc<A>(true, true, this->get_seed_hash(), this->get_theta64(), std::move(entries));
249 }
250 // copy first: this method is const, and quick select would permute the source table
251 entries.reserve(this->get_num_retained());
252 std::copy(this->begin(), this->end(), std::back_inserter(entries));
253 uint64_t theta = this->get_theta64();
254 const uint32_t nominal_size = 1 << table_.lg_nom_size_;
255 theta = trim_to_nominal<trivial_extract_key>(entries, nominal_size, theta);
256 if (ordered) std::sort(entries.begin(), entries.end());
257 return compact_theta_sketch_alloc<A>(false, ordered, this->get_seed_hash(), theta, std::move(entries));
258}
259
260template<typename A>
261void update_theta_sketch_alloc<A>::print_specifics(std::ostringstream& os) const {
262 os << " lg nominal size : " << static_cast<int>(table_.lg_nom_size_) << std::endl;
263 os << " lg current size : " << static_cast<int>(table_.lg_cur_size_) << std::endl;
264 os << " resize factor : " << (1 << table_.rf_) << std::endl;
265}
266
267// builder
268
269template<typename A>
271
272template<typename A>
274 return update_theta_sketch_alloc(this->starting_lg_size(), this->lg_k_, this->rf_, this->p_, this->starting_theta(), this->seed_, this->allocator_);
275}
276
277// compact sketch
278
279template<typename A>
280template<typename Other>
282is_empty_(other.is_empty()),
283is_ordered_(other.is_ordered() || ordered),
284seed_hash_(other.get_seed_hash()),
285theta_(other.get_theta64()),
286entries_(other.get_allocator())
287{
288 if (!other.is_empty()) {
289 entries_.reserve(other.get_num_retained());
290 std::copy(other.begin(), other.end(), std::back_inserter(entries_));
291 if (ordered && !other.is_ordered()) std::sort(entries_.begin(), entries_.end());
292 }
293}
294
295template<typename A>
296compact_theta_sketch_alloc<A>::compact_theta_sketch_alloc(bool is_empty, bool is_ordered, uint16_t seed_hash, uint64_t theta,
297 std::vector<uint64_t, A>&& entries):
298is_empty_(is_empty),
299is_ordered_(is_ordered || (entries.size() <= 1ULL)),
300seed_hash_(seed_hash),
301theta_(theta),
302entries_(std::move(entries))
303{}
304
305template<typename A>
307 return entries_.get_allocator();
308}
309
310template<typename A>
312 return is_empty_;
313}
314
315template<typename A>
317 return is_ordered_;
318}
319
320template<typename A>
322 return theta_;
323}
324
325template<typename A>
327 return static_cast<uint32_t>(entries_.size());
328}
329
330template<typename A>
332 return is_empty_ ? 0 : seed_hash_;
333}
334
335template<typename A>
337 return iterator(entries_.data(), static_cast<uint32_t>(entries_.size()), 0);
338}
339
340template<typename A>
342 return iterator(nullptr, 0, static_cast<uint32_t>(entries_.size()));
343}
344
345template<typename A>
346auto compact_theta_sketch_alloc<A>::begin() const -> const_iterator {
347 return const_iterator(entries_.data(), static_cast<uint32_t>(entries_.size()), 0);
348}
349
350template<typename A>
351auto compact_theta_sketch_alloc<A>::end() const -> const_iterator {
352 return const_iterator(nullptr, 0, static_cast<uint32_t>(entries_.size()));
353}
354
355template<typename A>
356void compact_theta_sketch_alloc<A>::print_specifics(std::ostringstream&) const {}
357
358template<typename A>
359uint8_t compact_theta_sketch_alloc<A>::get_preamble_longs(bool compressed) const {
360 if (compressed) {
361 return this->is_estimation_mode() ? 2 : 1;
362 }
363 return this->is_estimation_mode() ? 3 : this->is_empty() || entries_.size() == 1 ? 1 : 2;
364}
365
366template<typename A>
368 return sizeof(uint64_t) * (3 + update_theta_sketch_alloc<A>::theta_table::get_capacity(lg_k + 1, lg_k));
369}
370
371template<typename A>
373 if (compressed && is_suitable_for_compression()) {
374 return get_compressed_serialized_size_bytes(compute_entry_bits(), get_num_entries_bytes());
375 }
376 return sizeof(uint64_t) * get_preamble_longs(false) + sizeof(uint64_t) * entries_.size();
377}
378
379// store num_entries as whole bytes since whole-byte blocks will follow (most probably)
380template<typename A>
381uint8_t compact_theta_sketch_alloc<A>::get_num_entries_bytes() const {
382 return whole_bytes_to_hold_bits<uint8_t>(32 - count_leading_zeros_in_u32(static_cast<uint32_t>(entries_.size())));
383}
384
385template<typename A>
386size_t compact_theta_sketch_alloc<A>::get_compressed_serialized_size_bytes(uint8_t entry_bits, uint8_t num_entries_bytes) const {
387 const size_t compressed_bits = entry_bits * entries_.size();
388 return sizeof(uint64_t) * get_preamble_longs(true) + num_entries_bytes + whole_bytes_to_hold_bits(compressed_bits);
389}
390
391template<typename A>
392void compact_theta_sketch_alloc<A>::serialize(std::ostream& os) const {
393 const uint8_t preamble_longs = get_preamble_longs(false);
394 write(os, preamble_longs);
395 write(os, UNCOMPRESSED_SERIAL_VERSION);
396 write(os, SKETCH_TYPE);
397 write<uint16_t>(os, 0); // unused
398 const uint8_t flags_byte(
399 (1 << flags::IS_COMPACT) |
400 (1 << flags::IS_READ_ONLY) |
401 (this->is_empty() ? 1 << flags::IS_EMPTY : 0) |
402 (this->is_ordered() ? 1 << flags::IS_ORDERED : 0) |
403 (is_single_item() ? 1 << flags::IS_SINGLE_ITEM : 0)
404 );
405 write(os, flags_byte);
406 write(os, get_seed_hash());
407 if (preamble_longs > 1) {
408 write(os, static_cast<uint32_t>(entries_.size()));
409 write<uint32_t>(os, 0); // unused
410 }
411 if (this->is_estimation_mode()) write(os, this->theta_);
412 if (entries_.size() > 0) write(os, entries_.data(), entries_.size() * sizeof(uint64_t));
413}
414
415template<typename A>
416auto compact_theta_sketch_alloc<A>::serialize(unsigned header_size_bytes) const -> vector_bytes {
417 const size_t size = get_serialized_size_bytes() + header_size_bytes;
418 vector_bytes bytes(size, 0, entries_.get_allocator());
419 uint8_t* ptr = bytes.data() + header_size_bytes;
420 const uint8_t preamble_longs = get_preamble_longs(false);
421 *ptr++ = preamble_longs;
422 *ptr++ = UNCOMPRESSED_SERIAL_VERSION;
423 *ptr++ = SKETCH_TYPE;
424 ptr += sizeof(uint16_t); // unused
425 const uint8_t flags_byte(
426 (1 << flags::IS_COMPACT) |
427 (1 << flags::IS_READ_ONLY) |
428 (this->is_empty() ? 1 << flags::IS_EMPTY : 0) |
429 (this->is_ordered() ? 1 << flags::IS_ORDERED : 0) |
430 (is_single_item() ? 1 << flags::IS_SINGLE_ITEM : 0)
431 );
432 *ptr++ = flags_byte;
433 ptr += copy_to_mem(get_seed_hash(), ptr);
434 if (preamble_longs > 1) {
435 ptr += copy_to_mem(static_cast<uint32_t>(entries_.size()), ptr);
436 ptr += sizeof(uint32_t); // unused
437 }
438 if (this->is_estimation_mode()) ptr += copy_to_mem(theta_, ptr);
439 if (entries_.size() > 0) ptr += copy_to_mem(entries_.data(), ptr, entries_.size() * sizeof(uint64_t));
440 return bytes;
441}
442
443template<typename A>
444bool compact_theta_sketch_alloc<A>::is_single_item() const {
445 // one entry in exact mode: the same condition Java uses to set its SingleItem flag
446 return entries_.size() == 1 && !this->is_estimation_mode();
447}
448
449template<typename A>
450bool compact_theta_sketch_alloc<A>::is_suitable_for_compression() const {
451 if (!this->is_ordered() || entries_.size() == 0 ||
452 (entries_.size() == 1 && !this->is_estimation_mode())) return false;
453 return true;
454}
455
456template<typename A>
458 if (is_suitable_for_compression()) return serialize_version_4(os);
459 return serialize(os);
460}
461
462template<typename A>
463auto compact_theta_sketch_alloc<A>::serialize_compressed(unsigned header_size_bytes) const -> vector_bytes {
464 if (is_suitable_for_compression()) return serialize_version_4(header_size_bytes);
465 return serialize(header_size_bytes);
466}
467
468template<typename A>
469uint8_t compact_theta_sketch_alloc<A>::compute_entry_bits() const {
470 // compression is based on leading zeros in deltas between ordered hash values
471 // assumes ordered sketch
472 uint64_t previous = 0;
473 uint64_t ored = 0;
474 for (const uint64_t entry: entries_) {
475 const uint64_t delta = entry - previous;
476 ored |= delta;
477 previous = entry;
478 }
479 return 64 - count_leading_zeros_in_u64(ored);
480}
481
482template<typename A>
483void compact_theta_sketch_alloc<A>::serialize_version_4(std::ostream& os) const {
484 const uint8_t preamble_longs = get_preamble_longs(true);
485 const uint8_t entry_bits = compute_entry_bits();
486 const uint8_t num_entries_bytes = get_num_entries_bytes();
487
488 write(os, preamble_longs);
489 write(os, COMPRESSED_SERIAL_VERSION);
490 write(os, SKETCH_TYPE);
491 write(os, entry_bits);
492 write(os, num_entries_bytes);
493 const uint8_t flags_byte(
494 (1 << flags::IS_COMPACT) |
495 (1 << flags::IS_READ_ONLY) |
496 (1 << flags::IS_ORDERED)
497 );
498 write(os, flags_byte);
499 write(os, get_seed_hash());
500 if (this->is_estimation_mode()) write(os, this->theta_);
501 uint32_t num_entries = static_cast<uint32_t>(entries_.size());
502 for (unsigned i = 0; i < num_entries_bytes; ++i) {
503 write<uint8_t>(os, num_entries & 0xff);
504 num_entries >>= 8;
505 }
506
507 uint64_t previous = 0;
508 uint64_t deltas[8];
509 vector_bytes buffer(entry_bits, 0, entries_.get_allocator()); // block of 8 entries takes entry_bits bytes
510
511 // pack blocks of 8 deltas
512 unsigned i;
513 for (i = 0; i + 7 < entries_.size(); i += 8) {
514 for (unsigned j = 0; j < 8; ++j) {
515 deltas[j] = entries_[i + j] - previous;
516 previous = entries_[i + j];
517 }
518 pack_bits_block8(deltas, buffer.data(), entry_bits);
519 write(os, buffer.data(), buffer.size());
520 }
521
522 // pack extra deltas if fewer than 8 of them left
523 if (i < entries_.size()) {
524 uint8_t offset = 0;
525 uint8_t* ptr = buffer.data();
526 for (; i < entries_.size(); ++i) {
527 const uint64_t delta = entries_[i] - previous;
528 previous = entries_[i];
529 offset = pack_bits(delta, entry_bits, ptr, offset);
530 }
531 if (offset > 0) ++ptr;
532 write(os, buffer.data(), ptr - buffer.data());
533 }
534}
535
536template<typename A>
537auto compact_theta_sketch_alloc<A>::serialize_version_4(unsigned header_size_bytes) const -> vector_bytes {
538 const uint8_t entry_bits = compute_entry_bits();
539 const uint8_t num_entries_bytes = get_num_entries_bytes();
540 const size_t size = get_compressed_serialized_size_bytes(entry_bits, num_entries_bytes) + header_size_bytes;
541 vector_bytes bytes(size, 0, entries_.get_allocator());
542 uint8_t* ptr = bytes.data() + header_size_bytes;
543
544 *ptr++ = get_preamble_longs(true);
545 *ptr++ = COMPRESSED_SERIAL_VERSION;
546 *ptr++ = SKETCH_TYPE;
547 *ptr++ = entry_bits;
548 *ptr++ = num_entries_bytes;
549 const uint8_t flags_byte(
550 (1 << flags::IS_COMPACT) |
551 (1 << flags::IS_READ_ONLY) |
552 (1 << flags::IS_ORDERED)
553 );
554 *ptr++ = flags_byte;
555 ptr += copy_to_mem(get_seed_hash(), ptr);
556 if (this->is_estimation_mode()) {
557 ptr += copy_to_mem(theta_, ptr);
558 }
559 uint32_t num_entries = static_cast<uint32_t>(entries_.size());
560 for (unsigned i = 0; i < num_entries_bytes; ++i) {
561 *ptr++ = num_entries & 0xff;
562 num_entries >>= 8;
563 }
564
565 uint64_t previous = 0;
566 uint64_t deltas[8];
567
568 // pack blocks of 8 deltas
569 unsigned i;
570 for (i = 0; i + 7 < entries_.size(); i += 8) {
571 for (unsigned j = 0; j < 8; ++j) {
572 deltas[j] = entries_[i + j] - previous;
573 previous = entries_[i + j];
574 }
575 pack_bits_block8(deltas, ptr, entry_bits);
576 ptr += entry_bits;
577 }
578
579 // pack extra deltas if fewer than 8 of them left
580 uint8_t offset = 0;
581 for (; i < entries_.size(); ++i) {
582 const uint64_t delta = entries_[i] - previous;
583 previous = entries_[i];
584 offset = pack_bits(delta, entry_bits, ptr, offset);
585 }
586 return bytes;
587}
588
589template<typename A>
590compact_theta_sketch_alloc<A> compact_theta_sketch_alloc<A>::deserialize(std::istream& is, uint64_t seed, const A& allocator) {
591 const auto preamble_longs = read<uint8_t>(is);
592 const auto serial_version = read<uint8_t>(is);
593 const auto type = read<uint8_t>(is);
594 checker<true>::check_sketch_type(type, SKETCH_TYPE);
595 switch (serial_version) {
596 case 4:
597 return deserialize_v4(preamble_longs, is, seed, allocator);
598 case 3:
599 return deserialize_v3(preamble_longs, is, seed, allocator);
600 case 1:
601 return deserialize_v1(preamble_longs, is, seed, allocator);
602 case 2:
603 return deserialize_v2(preamble_longs, is, seed, allocator);
604 default:
605 throw std::invalid_argument("unexpected sketch serialization version " + std::to_string(serial_version));
606 }
607}
608
609template<typename A>
610compact_theta_sketch_alloc<A> compact_theta_sketch_alloc<A>::deserialize_v1(
611 uint8_t, std::istream& is, uint64_t seed, const A& allocator)
612{
613 const auto seed_hash = compute_seed_hash(seed);
614 read<uint8_t>(is); // unused
615 read<uint32_t>(is); // unused
616 const auto num_entries = read<uint32_t>(is);
617 read<uint32_t>(is); //unused
618 const auto theta = read<uint64_t>(is);
619 std::vector<uint64_t, A> entries(num_entries, 0, allocator);
620 bool is_empty = (num_entries == 0) && (theta == theta_constants::MAX_THETA);
621 if (!is_empty) read(is, entries.data(), sizeof(uint64_t) * entries.size());
622 if (!is.good()) throw std::runtime_error("error reading from std::istream");
623 return compact_theta_sketch_alloc(is_empty, true, seed_hash, theta, std::move(entries));
624}
625
626template<typename A>
627compact_theta_sketch_alloc<A> compact_theta_sketch_alloc<A>::deserialize_v2(
628 uint8_t preamble_longs, std::istream& is, uint64_t seed, const A& allocator)
629{
630 read<uint8_t>(is); // unused
631 read<uint16_t>(is); // unused
632 const uint16_t seed_hash = read<uint16_t>(is);
633 checker<true>::check_seed_hash(seed_hash, compute_seed_hash(seed));
634 if (preamble_longs == 1) {
635 if (!is.good()) throw std::runtime_error("error reading from std::istream");
636 std::vector<uint64_t, A> entries(0, 0, allocator);
637 return compact_theta_sketch_alloc(true, true, seed_hash, theta_constants::MAX_THETA, std::move(entries));
638 } else if (preamble_longs == 2) {
639 const uint32_t num_entries = read<uint32_t>(is);
640 read<uint32_t>(is); // unused
641 std::vector<uint64_t, A> entries(num_entries, 0, allocator);
642 if (num_entries == 0) {
643 return compact_theta_sketch_alloc(true, true, seed_hash, theta_constants::MAX_THETA, std::move(entries));
644 }
645 read(is, entries.data(), entries.size() * sizeof(uint64_t));
646 if (!is.good()) throw std::runtime_error("error reading from std::istream");
647 return compact_theta_sketch_alloc(false, true, seed_hash, theta_constants::MAX_THETA, std::move(entries));
648 } else if (preamble_longs == 3) {
649 const uint32_t num_entries = read<uint32_t>(is);
650 read<uint32_t>(is); // unused
651 const auto theta = read<uint64_t>(is);
652 bool is_empty = (num_entries == 0) && (theta == theta_constants::MAX_THETA);
653 std::vector<uint64_t, A> entries(num_entries, 0, allocator);
654 if (is_empty) {
655 if (!is.good()) throw std::runtime_error("error reading from std::istream");
656 return compact_theta_sketch_alloc(true, true, seed_hash, theta, std::move(entries));
657 } else {
658 read(is, entries.data(), sizeof(uint64_t) * entries.size());
659 if (!is.good()) throw std::runtime_error("error reading from std::istream");
660 return compact_theta_sketch_alloc(false, true, seed_hash, theta, std::move(entries));
661 }
662 } else {
663 throw std::invalid_argument(std::to_string(preamble_longs) + " longs of premable, but expected 1, 2, or 3");
664 }
665}
666
667template<typename A>
668compact_theta_sketch_alloc<A> compact_theta_sketch_alloc<A>::deserialize_v3(
669 uint8_t preamble_longs, std::istream& is, uint64_t seed, const A& allocator)
670{
671 read<uint16_t>(is); // unused
672 const auto flags_byte = read<uint8_t>(is);
673 const auto seed_hash = read<uint16_t>(is);
674 const bool is_empty = flags_byte & (1 << flags::IS_EMPTY);
675 if (!is_empty) checker<true>::check_seed_hash(seed_hash, compute_seed_hash(seed));
676 uint64_t theta = theta_constants::MAX_THETA;
677 uint32_t num_entries = 0;
678 if (!is_empty) {
679 if (preamble_longs == 1) {
680 num_entries = 1;
681 } else {
682 num_entries = read<uint32_t>(is);
683 read<uint32_t>(is); // unused
684 if (preamble_longs > 2) theta = read<uint64_t>(is);
685 }
686 }
687 std::vector<uint64_t, A> entries(num_entries, 0, allocator);
688 if (!is_empty) read(is, entries.data(), sizeof(uint64_t) * entries.size());
689 const bool is_ordered = flags_byte & (1 << flags::IS_ORDERED);
690 if (!is.good()) throw std::runtime_error("error reading from std::istream");
691 return compact_theta_sketch_alloc(is_empty, is_ordered, seed_hash, theta, std::move(entries));
692}
693
694template<typename A>
695compact_theta_sketch_alloc<A> compact_theta_sketch_alloc<A>::deserialize_v4(
696 uint8_t preamble_longs, std::istream& is, uint64_t seed, const A& allocator)
697{
698 const auto entry_bits = read<uint8_t>(is);
699 compact_theta_sketch_parser<true>::check_v4_entry_bits(entry_bits);
700 const auto num_entries_bytes = read<uint8_t>(is);
701 compact_theta_sketch_parser<true>::check_v4_num_entries_bytes(num_entries_bytes);
702 const auto flags_byte = read<uint8_t>(is);
703 const auto seed_hash = read<uint16_t>(is);
704 const bool is_empty = flags_byte & (1 << flags::IS_EMPTY);
705 if (!is_empty) checker<true>::check_seed_hash(seed_hash, compute_seed_hash(seed));
706 uint64_t theta = theta_constants::MAX_THETA;
707 if (preamble_longs > 1) theta = read<uint64_t>(is);
708 uint32_t num_entries = 0;
709 for (unsigned i = 0; i < num_entries_bytes; ++i) {
710 num_entries |= read<uint8_t>(is) << (i << 3);
711 }
712 vector_bytes buffer(entry_bits, 0, allocator); // block of 8 entries takes entry_bits bytes
713 std::vector<uint64_t, A> entries(num_entries, 0, allocator);
714
715 // unpack blocks of 8 deltas
716 unsigned i;
717 for (i = 0; i + 7 < num_entries; i += 8) {
718 read(is, buffer.data(), buffer.size());
719 unpack_bits_block8(&entries[i], buffer.data(), entry_bits);
720 }
721 // unpack extra deltas if fewer than 8 of them left
722 if (i < num_entries) read(is, buffer.data(), whole_bytes_to_hold_bits((num_entries - i) * entry_bits));
723 if (!is.good()) throw std::runtime_error("error reading from std::istream");
724 const uint8_t* ptr = buffer.data();
725 uint8_t offset = 0;
726 for (; i < num_entries; ++i) {
727 offset = unpack_bits(entries[i], entry_bits, ptr, offset);
728 }
729 // undo deltas
730 uint64_t previous = 0;
731 for (i = 0; i < num_entries; ++i) {
732 entries[i] += previous;
733 previous = entries[i];
734 }
735 const bool is_ordered = flags_byte & (1 << flags::IS_ORDERED);
736 return compact_theta_sketch_alloc(is_empty, is_ordered, seed_hash, theta, std::move(entries));
737}
738
739template<typename A>
740compact_theta_sketch_alloc<A> compact_theta_sketch_alloc<A>::deserialize(const void* bytes, size_t size, uint64_t seed, const A& allocator) {
741 auto data = compact_theta_sketch_parser<true>::parse(bytes, size, seed, false);
742 if (data.entry_bits == 64) { // versions 1 to 3
743 const uint64_t* entries = reinterpret_cast<const uint64_t*>(data.entries_start_ptr);
744 return compact_theta_sketch_alloc(data.is_empty, data.is_ordered, data.seed_hash, data.theta,
745 std::vector<uint64_t, A>(entries, entries + data.num_entries, allocator));
746 } else { // version 4
747 std::vector<uint64_t, A> entries(data.num_entries, 0, allocator);
748 const uint8_t* ptr = reinterpret_cast<const uint8_t*>(data.entries_start_ptr);
749 // unpack blocks of 8 deltas
750 unsigned i;
751 for (i = 0; i + 7 < data.num_entries; i += 8) {
752 unpack_bits_block8(&entries[i], ptr, data.entry_bits);
753 ptr += data.entry_bits;
754 }
755 // unpack extra deltas if fewer than 8 of them left
756 uint8_t offset = 0;
757 for (; i < data.num_entries; ++i) {
758 offset = unpack_bits(entries[i], data.entry_bits, ptr, offset);
759 }
760 // undo deltas
761 uint64_t previous = 0;
762 for (i = 0; i < data.num_entries; ++i) {
763 entries[i] += previous;
764 previous = entries[i];
765 }
766 return compact_theta_sketch_alloc(data.is_empty, data.is_ordered, data.seed_hash, data.theta, std::move(entries));
767 }
768}
769
770// wrapped compact sketch
771
772template<typename A>
773wrapped_compact_theta_sketch_alloc<A>::wrapped_compact_theta_sketch_alloc(const data_type& data):
774data_(data)
775{}
776
777template<typename A>
778const wrapped_compact_theta_sketch_alloc<A> wrapped_compact_theta_sketch_alloc<A>::wrap(const void* bytes, size_t size, uint64_t seed, bool dump_on_error) {
779 return wrapped_compact_theta_sketch_alloc(compact_theta_sketch_parser<true>::parse(bytes, size, seed, dump_on_error));
780}
781
782template<typename A>
786
787template<typename A>
789 return data_.is_empty;
790}
791
792template<typename A>
794 return data_.is_ordered;
795}
796
797template<typename A>
799 return data_.theta;
800}
801
802template<typename A>
804 return data_.num_entries;
805}
806
807template<typename A>
809 return is_empty() ? 0 : data_.seed_hash;
810}
811
812template<typename A>
813auto wrapped_compact_theta_sketch_alloc<A>::begin() const -> const_iterator {
814 return const_iterator(data_.entries_start_ptr, data_.entry_bits, data_.num_entries, 0);
815}
816
817template<typename A>
818auto wrapped_compact_theta_sketch_alloc<A>::end() const -> const_iterator {
819 return const_iterator(data_.entries_start_ptr, data_.entry_bits, data_.num_entries, data_.num_entries);
820}
821
822template<typename A>
823void wrapped_compact_theta_sketch_alloc<A>::print_specifics(std::ostringstream&) const {}
824
825template<typename A>
826void wrapped_compact_theta_sketch_alloc<A>::print_items(std::ostringstream& os) const {
827 os << "### Retained entries" << std::endl;
828 for (const auto hash: *this) {
829 os << hash << std::endl;
830 }
831 os << "### End retained entries" << std::endl;
832}
833
834// assumes index == 0 or index == num_entries
835template<typename Allocator>
836wrapped_compact_theta_sketch_alloc<Allocator>::const_iterator::const_iterator(
837 const void* ptr, uint8_t entry_bits, uint32_t num_entries, uint32_t index):
838ptr_(ptr),
839entry_bits_(entry_bits),
840num_entries_(num_entries),
841index_(index),
842previous_(0),
843is_block_mode_(num_entries_ >= 8),
844offset_(0)
845{
846 if (entry_bits == 64) { // no compression
847 ptr_ = reinterpret_cast<const uint64_t*>(ptr) + index;
848 } else if (index < num_entries) {
849 if (is_block_mode_) {
850 unpack8();
851 } else {
852 unpack1();
853 }
854 }
855}
856
857template<typename Allocator>
858auto wrapped_compact_theta_sketch_alloc<Allocator>::const_iterator::operator++() -> const_iterator& {
859 if (entry_bits_ == 64) { // no compression
860 ptr_ = reinterpret_cast<const uint64_t*>(ptr_) + 1;
861 return *this;
862 }
863 if (++index_ < num_entries_) {
864 if (is_block_mode_) {
865 if ((index_ & 7) == 0) {
866 if (num_entries_ - index_ >= 8) {
867 unpack8();
868 } else {
869 is_block_mode_ = false;
870 unpack1();
871 }
872 }
873 } else {
874 unpack1();
875 }
876 }
877 return *this;
878}
879
880template<typename Allocator>
881void wrapped_compact_theta_sketch_alloc<Allocator>::const_iterator::unpack1() {
882 const uint32_t i = index_ & 7;
883 offset_ = unpack_bits(buffer_[i], entry_bits_, reinterpret_cast<const uint8_t*&>(ptr_), offset_);
884 buffer_[i] += previous_;
885 previous_ = buffer_[i];
886}
887
888template<typename Allocator>
889void wrapped_compact_theta_sketch_alloc<Allocator>::const_iterator::unpack8() {
890 unpack_bits_block8(buffer_, reinterpret_cast<const uint8_t*>(ptr_), entry_bits_);
891 ptr_ = reinterpret_cast<const uint8_t*>(ptr_) + entry_bits_;
892 for (int i = 0; i < 8; ++i) {
893 buffer_[i] += previous_;
894 previous_ = buffer_[i];
895 }
896}
897
898template<typename Allocator>
899auto wrapped_compact_theta_sketch_alloc<Allocator>::const_iterator::operator++(int) -> const_iterator {
900 const_iterator tmp(*this);
901 operator++();
902 return tmp;
903}
904
905template<typename Allocator>
906bool wrapped_compact_theta_sketch_alloc<Allocator>::const_iterator::operator!=(const const_iterator& other) const {
907 if (entry_bits_ == 64) return ptr_ != other.ptr_;
908 return index_ != other.index_;
909}
910
911template<typename Allocator>
912bool wrapped_compact_theta_sketch_alloc<Allocator>::const_iterator::operator==(const const_iterator& other) const {
913 if (entry_bits_ == 64) return ptr_ == other.ptr_;
914 return index_ == other.index_;
915}
916
917template<typename Allocator>
918auto wrapped_compact_theta_sketch_alloc<Allocator>::const_iterator::operator*() const -> reference {
919 if (entry_bits_ == 64) return *reinterpret_cast<const uint64_t*>(ptr_);
920 return buffer_[index_ & 7];
921}
922
923template<typename Allocator>
924auto wrapped_compact_theta_sketch_alloc<Allocator>::const_iterator::operator->() const -> pointer {
925 if (entry_bits_ == 64) return reinterpret_cast<const uint64_t*>(ptr_);
926 return buffer_ + (index_ & 7);
927}
928
929} /* namespace datasketches */
930
931#endif
double get_estimate() const
Definition theta_sketch_impl.hpp:48
double get_lower_bound(uint8_t num_std_devs) const
Returns the approximate lower error bound given a number of standard deviations.
Definition theta_sketch_impl.hpp:53
virtual bool is_empty() const =0
virtual string< Allocator > to_string(bool print_items=false) const
Provides a human-readable summary of this sketch as a string.
Definition theta_sketch_impl.hpp:65
virtual uint32_t get_num_retained() const =0
double get_upper_bound(uint8_t num_std_devs) const
Returns the approximate upper error bound given a number of standard deviations.
Definition theta_sketch_impl.hpp:59
double get_theta() const
Definition theta_sketch_impl.hpp:42
virtual uint16_t get_seed_hash() const =0
bool is_estimation_mode() const
Definition theta_sketch_impl.hpp:37
virtual uint64_t get_theta64() const =0
Compact Theta sketch.
Definition theta_sketch.hpp:373
compact_theta_sketch_alloc(const Other &other, bool ordered)
Copy constructor.
Definition theta_sketch_impl.hpp:281
void serialize(std::ostream &os) const
This method serializes the sketch into a given stream in a binary form.
Definition theta_sketch_impl.hpp:392
virtual uint64_t get_theta64() const
Definition theta_sketch_impl.hpp:321
virtual uint32_t get_num_retained() const
Definition theta_sketch_impl.hpp:326
virtual bool is_empty() const
Definition theta_sketch_impl.hpp:311
virtual bool is_ordered() const
Definition theta_sketch_impl.hpp:316
virtual uint16_t get_seed_hash() const
Definition theta_sketch_impl.hpp:331
virtual iterator end()
Iterator pointing past the valid range.
Definition theta_sketch_impl.hpp:341
static size_t get_max_serialized_size_bytes(uint8_t lg_k)
Computes maximum serialized size in bytes.
Definition theta_sketch_impl.hpp:367
static compact_theta_sketch_alloc deserialize(std::istream &is, uint64_t seed=DEFAULT_SEED, const Allocator &allocator=Allocator())
This method deserializes a sketch from a given stream.
virtual Allocator get_allocator() const
Definition theta_sketch_impl.hpp:306
size_t get_serialized_size_bytes(bool compressed=false) const
Computes size in bytes required to serialize the current state of the sketch.
Definition theta_sketch_impl.hpp:372
void serialize_compressed(std::ostream &os) const
This method serializes the sketch into a given stream in a compressed binary form.
Definition theta_sketch_impl.hpp:457
virtual iterator begin()
Iterator over hash values in this sketch.
Definition theta_sketch_impl.hpp:336
theta_base_builder(const Allocator &allocator)
Definition theta_update_sketch_base_impl.hpp:304
builder(const Allocator &allocator=Allocator())
Constructor.
Definition theta_sketch_impl.hpp:270
update_theta_sketch_alloc build() const
Definition theta_sketch_impl.hpp:273
void trim()
Remove retained entries in excess of the nominal size k (if any).
Definition theta_sketch_impl.hpp:213
virtual uint64_t get_theta64() const
Definition theta_sketch_impl.hpp:122
virtual uint32_t get_num_retained() const
Definition theta_sketch_impl.hpp:127
resize_factor get_rf() const
Definition theta_sketch_impl.hpp:142
void update(const std::string &value)
Update this sketch with a given string.
Definition theta_sketch_impl.hpp:197
virtual bool is_empty() const
Definition theta_sketch_impl.hpp:112
virtual bool is_ordered() const
Definition theta_sketch_impl.hpp:117
virtual uint16_t get_seed_hash() const
Definition theta_sketch_impl.hpp:132
compact_theta_sketch_alloc< Allocator > compact(bool ordered=true, bool trim=false) const
Converts this sketch to a compact sketch (ordered or unordered, trimmed or not trimmed).
Definition theta_sketch_impl.hpp:243
virtual iterator end()
Iterator pointing past the valid range.
Definition theta_sketch_impl.hpp:228
uint8_t get_lg_k() const
Definition theta_sketch_impl.hpp:137
virtual Allocator get_allocator() const
Definition theta_sketch_impl.hpp:107
void reset()
Reset the sketch to the initial empty state.
Definition theta_sketch_impl.hpp:218
virtual iterator begin()
Iterator over hash values in this sketch.
Definition theta_sketch_impl.hpp:223
update_theta_sketch_alloc(const update_theta_sketch_alloc &other)=default
Copy constructor.
uint64_t get_theta64() const
Definition theta_sketch_impl.hpp:798
uint32_t get_num_retained() const
Definition theta_sketch_impl.hpp:803
bool is_empty() const
Definition theta_sketch_impl.hpp:788
bool is_ordered() const
Definition theta_sketch_impl.hpp:793
uint16_t get_seed_hash() const
Definition theta_sketch_impl.hpp:808
static const wrapped_compact_theta_sketch_alloc wrap(const void *bytes, size_t size, uint64_t seed=DEFAULT_SEED, bool dump_on_error=false)
This method wraps a serialized compact sketch as an array of bytes.
Definition theta_sketch_impl.hpp:778
Allocator get_allocator() const
Definition theta_sketch_impl.hpp:783
const_iterator begin() const
Const iterator over hash values in this sketch.
Definition theta_sketch_impl.hpp:813
const_iterator end() const
Const iterator pointing past the valid range.
Definition theta_sketch_impl.hpp:818
const uint64_t MAX_THETA
max theta - signed max for compatibility with Java
Definition theta_constants.hpp:36
DataSketches namespace.
Definition binomial_bounds.hpp:38