datasketches-cpp
Loading...
Searching...
No Matches
kll_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 KLL_SKETCH_IMPL_HPP_
21#define KLL_SKETCH_IMPL_HPP_
22
23#include <iostream>
24#include <iomanip>
25#include <sstream>
26#include <stdexcept>
27#include <type_traits>
28
29#include "conditional_forward.hpp"
30#include "count_zeros.hpp"
31#include "memory_operations.hpp"
32#include "kll_helper.hpp"
33
34namespace datasketches {
35
36template<typename T, typename C, typename A>
37kll_sketch<T, C, A>::kll_sketch(uint16_t k, const C& comparator, const A& allocator):
38comparator_(comparator),
39allocator_(allocator),
40k_(k),
41m_(kll_constants::DEFAULT_M),
42min_k_(k),
43num_levels_(1),
44is_level_zero_sorted_(false),
45n_(0),
46levels_(2, 0, allocator),
47items_(nullptr),
48items_size_(k_),
49min_item_(),
50max_item_(),
51sorted_view_(nullptr)
52{
53 if (k < kll_constants::MIN_K || k > kll_constants::MAX_K) {
54 throw std::invalid_argument("K must be >= " + std::to_string(kll_constants::MIN_K) + " and <= "
55 + std::to_string(kll_constants::MAX_K) + ": " + std::to_string(k));
56 }
57 levels_[0] = levels_[1] = k;
58 items_ = allocator_.allocate(items_size_);
59}
60
61template<typename T, typename C, typename A>
62kll_sketch<T, C, A>::kll_sketch(const kll_sketch& other):
63comparator_(other.comparator_),
64allocator_(other.allocator_),
65k_(other.k_),
66m_(other.m_),
67min_k_(other.min_k_),
68num_levels_(other.num_levels_),
69is_level_zero_sorted_(other.is_level_zero_sorted_),
70n_(other.n_),
71levels_(other.levels_),
72items_(nullptr),
73items_size_(other.items_size_),
74min_item_(other.min_item_),
75max_item_(other.max_item_),
76sorted_view_(nullptr)
77{
78 items_ = allocator_.allocate(items_size_);
79 for (auto i = levels_[0]; i < levels_[num_levels_]; ++i) new (&items_[i]) T(other.items_[i]);
80}
81
82template<typename T, typename C, typename A>
83kll_sketch<T, C, A>::kll_sketch(kll_sketch&& other) noexcept:
84comparator_(std::move(other.comparator_)),
85allocator_(std::move(other.allocator_)),
86k_(other.k_),
87m_(other.m_),
88min_k_(other.min_k_),
89num_levels_(other.num_levels_),
90is_level_zero_sorted_(other.is_level_zero_sorted_),
91n_(other.n_),
92levels_(std::move(other.levels_)),
93items_(other.items_),
94items_size_(other.items_size_),
95min_item_(std::move(other.min_item_)),
96max_item_(std::move(other.max_item_)),
97sorted_view_(nullptr)
98{
99 other.items_ = nullptr;
100}
101
102template<typename T, typename C, typename A>
103kll_sketch<T, C, A>& kll_sketch<T, C, A>::operator=(const kll_sketch& other) {
104 kll_sketch copy(other);
105 std::swap(comparator_, copy.comparator_);
106 std::swap(allocator_, copy.allocator_);
107 std::swap(k_, copy.k_);
108 std::swap(m_, copy.m_);
109 std::swap(min_k_, copy.min_k_);
110 std::swap(num_levels_, copy.num_levels_);
111 std::swap(is_level_zero_sorted_, copy.is_level_zero_sorted_);
112 std::swap(n_, copy.n_);
113 std::swap(levels_, copy.levels_);
114 std::swap(items_, copy.items_);
115 std::swap(items_size_, copy.items_size_);
116 std::swap(min_item_, copy.min_item_);
117 std::swap(max_item_, copy.max_item_);
118 reset_sorted_view();
119 return *this;
120}
121
122template<typename T, typename C, typename A>
123kll_sketch<T, C, A>& kll_sketch<T, C, A>::operator=(kll_sketch&& other) {
124 std::swap(comparator_, other.comparator_);
125 std::swap(allocator_, other.allocator_);
126 std::swap(k_, other.k_);
127 std::swap(m_, other.m_);
128 std::swap(min_k_, other.min_k_);
129 std::swap(num_levels_, other.num_levels_);
130 std::swap(is_level_zero_sorted_, other.is_level_zero_sorted_);
131 std::swap(n_, other.n_);
132 std::swap(levels_, other.levels_);
133 std::swap(items_, other.items_);
134 std::swap(items_size_, other.items_size_);
135 std::swap(min_item_, other.min_item_);
136 std::swap(max_item_, other.max_item_);
137 reset_sorted_view();
138 return *this;
139}
140
141template<typename T, typename C, typename A>
142kll_sketch<T, C, A>::~kll_sketch() {
143 if (items_ != nullptr) {
144 const uint32_t begin = levels_[0];
145 const uint32_t end = levels_[num_levels_];
146 for (uint32_t i = begin; i < end; i++) items_[i].~T();
147 allocator_.deallocate(items_, items_size_);
148 }
149 reset_sorted_view();
150}
151
152template<typename T, typename C, typename A>
153template<typename TT, typename CC, typename AA>
154kll_sketch<T, C, A>::kll_sketch(const kll_sketch<TT, CC, AA>& other, const C& comparator, const A& allocator):
155comparator_(comparator),
156allocator_(allocator),
157k_(other.k_),
158m_(other.m_),
159min_k_(other.min_k_),
160num_levels_(other.num_levels_),
161is_level_zero_sorted_(other.is_level_zero_sorted_),
162n_(other.n_),
163levels_(other.levels_, allocator_),
164items_(nullptr),
165items_size_(other.items_size_),
166min_item_(other.min_item_),
167max_item_(other.max_item_),
168sorted_view_(nullptr)
169{
170 static_assert(
171 std::is_constructible<T, TT>::value,
172 "Type converting constructor requires new type to be constructible from existing type"
173 );
174 items_ = allocator_.allocate(items_size_);
175 for (auto i = levels_[0]; i < levels_[num_levels_]; ++i) new (&items_[i]) T(other.items_[i]);
176 check_sorting();
177}
178
179template<typename T, typename C, typename A>
180template<typename FwdT>
182 if (!check_update_item(item)) { return; }
183 update_min_max(static_cast<const T&>(item)); // min and max are always copies
184 const uint32_t index = internal_update();
185 new (&items_[index]) T(std::forward<FwdT>(item));
186 reset_sorted_view();
187}
188
189template<typename T, typename C, typename A>
190void kll_sketch<T, C, A>::update_min_max(const T& item) {
191 if (is_empty()) {
192 min_item_.emplace(item);
193 max_item_.emplace(item);
194 } else {
195 if (comparator_(item, *min_item_)) *min_item_ = item;
196 if (comparator_(*max_item_, item)) *max_item_ = item;
197 }
198}
199
200template<typename T, typename C, typename A>
201uint32_t kll_sketch<T, C, A>::internal_update() {
202 if (levels_[0] == 0) { compress_while_updating(); }
203 n_++;
204 is_level_zero_sorted_ = false;
205 return --levels_[0];
206}
207
208template<typename T, typename C, typename A>
209template<typename FwdSk>
210void kll_sketch<T, C, A>::merge(FwdSk&& other) {
211 if (other.is_empty()) { return; }
212 if (m_ != other.m_) {
213 throw std::invalid_argument("incompatible M: " + std::to_string(m_) + " and " + std::to_string(other.m_));
214 }
215 if (is_empty()) {
216 min_item_.emplace(conditional_forward<FwdSk>(*other.min_item_));
217 max_item_.emplace(conditional_forward<FwdSk>(*other.max_item_));
218 } else {
219 if (comparator_(*other.min_item_, *min_item_)) *min_item_ = conditional_forward<FwdSk>(*other.min_item_);
220 if (comparator_(*max_item_, *other.max_item_)) *max_item_ = conditional_forward<FwdSk>(*other.max_item_);
221 }
222 const uint64_t final_n = n_ + other.n_;
223 for (uint32_t i = other.levels_[0]; i < other.levels_[1]; i++) {
224 const uint32_t index = internal_update();
225 new (&items_[index]) T(conditional_forward<FwdSk>(other.items_[i]));
226 }
227 if (other.num_levels_ >= 2) { merge_higher_levels(other, final_n); }
228 n_ = final_n;
229 if (other.is_estimation_mode()) { min_k_ = std::min(min_k_, other.min_k_); }
230 assert_correct_total_weight();
231 reset_sorted_view();
232}
233
234template<typename T, typename C, typename A>
236 return n_ == 0;
237}
238
239template<typename T, typename C, typename A>
241 return k_;
242}
243
244template<typename T, typename C, typename A>
246 return n_;
247}
248
249template<typename T, typename C, typename A>
251 return levels_[num_levels_] - levels_[0];
252}
253
254template<typename T, typename C, typename A>
256 return num_levels_ > 1;
257}
258
259template<typename T, typename C, typename A>
261 if (is_empty()) { throw std::runtime_error("operation is undefined for an empty sketch"); }
262 return *min_item_;
263}
264
265template<typename T, typename C, typename A>
267 if (is_empty()) { throw std::runtime_error("operation is undefined for an empty sketch"); }
268 return *max_item_;
269}
270
271template<typename T, typename C, typename A>
273 return comparator_;
274}
275
276template<typename T, typename C, typename A>
278 return allocator_;
279}
280
281template<typename T, typename C, typename A>
282double kll_sketch<T, C, A>::get_rank(const T& item, bool inclusive) const {
283 if (is_empty()) { throw std::runtime_error("operation is undefined for an empty sketch"); }
284 setup_sorted_view();
285 return sorted_view_->get_rank(item, inclusive);
286}
287
288template<typename T, typename C, typename A>
289auto kll_sketch<T, C, A>::get_PMF(const T* split_points, uint32_t size, bool inclusive) const -> vector_double {
290 if (is_empty()) { throw std::runtime_error("operation is undefined for an empty sketch"); }
291 setup_sorted_view();
292 return sorted_view_->get_PMF(split_points, size, inclusive);
293}
294
295template<typename T, typename C, typename A>
296auto kll_sketch<T, C, A>::get_CDF(const T* split_points, uint32_t size, bool inclusive) const -> vector_double {
297 if (is_empty()) { throw std::runtime_error("operation is undefined for an empty sketch"); }
298 setup_sorted_view();
299 return sorted_view_->get_CDF(split_points, size, inclusive);
300}
301
302template<typename T, typename C, typename A>
303auto kll_sketch<T, C, A>::get_quantile(double rank, bool inclusive) const -> quantile_return_type {
304 if (is_empty()) { throw std::runtime_error("operation is undefined for an empty sketch"); }
305 if ((rank < 0.0) || (rank > 1.0)) {
306 throw std::invalid_argument("normalized rank cannot be less than zero or greater than 1.0");
307 }
308 // may have a side effect of sorting level zero if needed
309 setup_sorted_view();
310 return sorted_view_->get_quantile(rank, inclusive);
311}
312
313template<typename T, typename C, typename A>
315 return get_normalized_rank_error(min_k_, pmf);
316}
317
318// implementation for fixed-size arithmetic types (integral and floating point)
319template<typename T, typename C, typename A>
320template<typename TT, typename SerDe, typename std::enable_if<std::is_arithmetic<TT>::value, int>::type>
322 if (is_empty()) { return EMPTY_SIZE_BYTES; }
323 if (num_levels_ == 1 && get_num_retained() == 1) {
324 return DATA_START_SINGLE_ITEM + sizeof(TT);
325 }
326 // the last integer in the levels_ array is not serialized because it can be derived
327 return DATA_START + num_levels_ * sizeof(uint32_t) + (get_num_retained() + 2) * sizeof(TT);
328}
329
330// implementation for all other types
331template<typename T, typename C, typename A>
332template<typename TT, typename SerDe, typename std::enable_if<!std::is_arithmetic<TT>::value, int>::type>
333size_t kll_sketch<T, C, A>::get_serialized_size_bytes(const SerDe& sd) const {
334 if (is_empty()) { return EMPTY_SIZE_BYTES; }
335 if (num_levels_ == 1 && get_num_retained() == 1) {
336 return DATA_START_SINGLE_ITEM + sd.size_of_item(items_[levels_[0]]);
337 }
338 // the last integer in the levels_ array is not serialized because it can be derived
339 size_t size = DATA_START + num_levels_ * sizeof(uint32_t);
340 size += sd.size_of_item(*min_item_);
341 size += sd.size_of_item(*max_item_);
342 for (auto it: *this) size += sd.size_of_item(it.first);
343 return size;
344}
345
346// implementation for fixed-size arithmetic types (integral and floating point)
347template<typename T, typename C, typename A>
348template<typename TT, typename std::enable_if<std::is_arithmetic<TT>::value, int>::type>
350 const uint8_t num_levels = kll_helper::ub_on_num_levels(n);
351 const uint32_t max_num_retained = kll_helper::compute_total_capacity(k, kll_constants::DEFAULT_M, num_levels);
352 // the last integer in the levels_ array is not serialized because it can be derived
353 return DATA_START + num_levels * sizeof(uint32_t) + (max_num_retained + 2) * sizeof(TT);
354}
355
356// implementation for all other types
357template<typename T, typename C, typename A>
358template<typename TT, typename std::enable_if<!std::is_arithmetic<TT>::value, int>::type>
359size_t kll_sketch<T, C, A>::get_max_serialized_size_bytes(uint16_t k, uint64_t n, size_t max_item_size_bytes) {
360 const uint8_t num_levels = kll_helper::ub_on_num_levels(n);
361 const uint32_t max_num_retained = kll_helper::compute_total_capacity(k, kll_constants::DEFAULT_M, num_levels);
362 // the last integer in the levels_ array is not serialized because it can be derived
363 return DATA_START + num_levels * sizeof(uint32_t) + (max_num_retained + 2) * max_item_size_bytes;
364}
365
366template<typename T, typename C, typename A>
367template<typename SerDe>
368void kll_sketch<T, C, A>::serialize(std::ostream& os, const SerDe& sd) const {
369 const bool is_single_item = n_ == 1;
370 const uint8_t preamble_ints(is_empty() || is_single_item ? PREAMBLE_INTS_SHORT : PREAMBLE_INTS_FULL);
371 write(os, preamble_ints);
372 const uint8_t serial_version(is_single_item ? SERIAL_VERSION_2 : SERIAL_VERSION_1);
373 write(os, serial_version);
374 const uint8_t family(FAMILY);
375 write(os, family);
376 const uint8_t flags_byte(
377 (is_empty() ? 1 << flags::IS_EMPTY : 0)
378 | (is_level_zero_sorted_ ? 1 << flags::IS_LEVEL_ZERO_SORTED : 0)
379 | (is_single_item ? 1 << flags::IS_SINGLE_ITEM : 0)
380 );
381 write(os, flags_byte);
382 write(os, k_);
383 write(os, m_);
384 const uint8_t unused = 0;
385 write(os, unused);
386 if (is_empty()) return;
387 if (!is_single_item) {
388 write(os, n_);
389 write(os, min_k_);
390 write(os, num_levels_);
391 write(os, unused);
392 write(os, levels_.data(), sizeof(levels_[0]) * num_levels_);
393 sd.serialize(os, &*min_item_, 1);
394 sd.serialize(os, &*max_item_, 1);
395 }
396 sd.serialize(os, &items_[levels_[0]], get_num_retained());
397}
398
399template<typename T, typename C, typename A>
400template<typename SerDe>
401auto kll_sketch<T, C, A>::serialize(unsigned header_size_bytes, const SerDe& sd) const -> vector_bytes {
402 const bool is_single_item = n_ == 1;
403 const size_t size = header_size_bytes + get_serialized_size_bytes(sd);
404 vector_bytes bytes(size, 0, allocator_);
405 uint8_t* ptr = bytes.data() + header_size_bytes;
406 const uint8_t* end_ptr = ptr + size;
407 const uint8_t preamble_ints(is_empty() || is_single_item ? PREAMBLE_INTS_SHORT : PREAMBLE_INTS_FULL);
408 ptr += copy_to_mem(preamble_ints, ptr);
409 const uint8_t serial_version(is_single_item ? SERIAL_VERSION_2 : SERIAL_VERSION_1);
410 ptr += copy_to_mem(serial_version, ptr);
411 const uint8_t family(FAMILY);
412 ptr += copy_to_mem(family, ptr);
413 const uint8_t flags_byte(
414 (is_empty() ? 1 << flags::IS_EMPTY : 0)
415 | (is_level_zero_sorted_ ? 1 << flags::IS_LEVEL_ZERO_SORTED : 0)
416 | (is_single_item ? 1 << flags::IS_SINGLE_ITEM : 0)
417 );
418 ptr += copy_to_mem(flags_byte, ptr);
419 ptr += copy_to_mem(k_, ptr);
420 ptr += copy_to_mem(m_, ptr);
421 ptr += sizeof(uint8_t); // unused
422 if (!is_empty()) {
423 if (!is_single_item) {
424 ptr += copy_to_mem(n_, ptr);
425 ptr += copy_to_mem(min_k_, ptr);
426 ptr += copy_to_mem(num_levels_, ptr);
427 ptr += sizeof(uint8_t); // unused
428 ptr += copy_to_mem(levels_.data(), ptr, sizeof(levels_[0]) * num_levels_);
429 ptr += sd.serialize(ptr, end_ptr - ptr, &*min_item_, 1);
430 ptr += sd.serialize(ptr, end_ptr - ptr, &*max_item_, 1);
431 }
432 const size_t bytes_remaining = end_ptr - ptr;
433 ptr += sd.serialize(ptr, bytes_remaining, &items_[levels_[0]], get_num_retained());
434 }
435 const size_t delta = ptr - bytes.data();
436 if (delta != size) throw std::logic_error("serialized size mismatch: " + std::to_string(delta)
437 + " != " + std::to_string(size));
438 return bytes;
439}
440
441template<typename T, typename C, typename A>
442template<typename SerDe>
443kll_sketch<T, C, A> kll_sketch<T, C, A>::deserialize(std::istream& is, const SerDe& sd,
444 const C& comparator, const A& allocator) {
445 const auto preamble_ints = read<uint8_t>(is);
446 const auto serial_version = read<uint8_t>(is);
447 const auto family_id = read<uint8_t>(is);
448 const auto flags_byte = read<uint8_t>(is);
449 const auto k = read<uint16_t>(is);
450 const auto m = read<uint8_t>(is);
451 read<uint8_t>(is); // skip unused byte
452
453 check_k(k);
454 check_m(m);
455 check_preamble_ints(preamble_ints, flags_byte);
456 check_serial_version(serial_version);
457 check_family_id(family_id);
458
459 if (!is.good()) throw std::runtime_error("error reading from std::istream");
460 const bool is_empty(flags_byte & (1 << flags::IS_EMPTY));
461 if (is_empty) return kll_sketch(k, comparator, allocator);
462
463 uint64_t n;
464 uint16_t min_k;
465 uint8_t num_levels;
466 const bool is_single_item(flags_byte & (1 << flags::IS_SINGLE_ITEM)); // used in serial version 2
467 if (is_single_item) {
468 n = 1;
469 min_k = k;
470 num_levels = 1;
471 } else {
472 n = read<uint64_t>(is);
473 min_k = read<uint16_t>(is);
474 num_levels = read<uint8_t>(is);
475 read<uint8_t>(is); // skip unused byte
476 check_num_levels(num_levels);
477 }
478 vector_u32 levels(num_levels + 1, 0, allocator);
479 const uint32_t capacity(kll_helper::compute_total_capacity(k, m, num_levels));
480 if (is_single_item) {
481 levels[0] = capacity - 1;
482 } else {
483 // the last integer in levels_ is not serialized because it can be derived
484 read(is, levels.data(), sizeof(levels[0]) * num_levels);
485 if (!is.good()) throw std::runtime_error("error reading from std::istream");
486 }
487 levels[num_levels] = capacity;
488 check_levels(levels);
489 optional<T> min_item;
490 optional<T> max_item;
491 if (!is_single_item) {
492 // Space to deserialize min and max.
493 // serde::deserialize expects allocated but not initialized storage.
494 typename std::aligned_storage<sizeof(T), alignof(T)>::type tmp_storage;
495 T* tmp = reinterpret_cast<T*>(&tmp_storage);
496
497 sd.deserialize(is, tmp, 1);
498 // serde call did not throw, repackage and cleanup
499 min_item.emplace(std::move(*tmp));
500 tmp->~T();
501 sd.deserialize(is, tmp, 1);
502 // serde call did not throw, repackage and cleanup
503 max_item.emplace(std::move(*tmp));
504 tmp->~T();
505 }
506 A alloc(allocator);
507 auto items_buffer_deleter = [capacity, &alloc](T* ptr) { alloc.deallocate(ptr, capacity); };
508 std::unique_ptr<T, decltype(items_buffer_deleter)> items_buffer(alloc.allocate(capacity), items_buffer_deleter);
509 const auto num_items = levels[num_levels] - levels[0];
510 sd.deserialize(is, &items_buffer.get()[levels[0]], num_items);
511 // serde call did not throw, repackage with destrtuctors
512 std::unique_ptr<T, items_deleter> items(items_buffer.release(), items_deleter(levels[0], capacity, allocator));
513 const bool is_level_zero_sorted = (flags_byte & (1 << flags::IS_LEVEL_ZERO_SORTED)) > 0;
514 if (is_single_item) {
515 min_item.emplace(items.get()[levels[0]]);
516 max_item.emplace(items.get()[levels[0]]);
517 }
518 if (!is.good())
519 throw std::runtime_error("error reading from std::istream");
520 return kll_sketch(k, min_k, n, num_levels, std::move(levels), std::move(items), capacity,
521 std::move(min_item), std::move(max_item), is_level_zero_sorted, comparator);
522}
523
524template<typename T, typename C, typename A>
525template<typename SerDe>
526kll_sketch<T, C, A> kll_sketch<T, C, A>::deserialize(const void* bytes, size_t size, const SerDe& sd,
527 const C& comparator, const A& allocator) {
528 ensure_minimum_memory(size, 8);
529 const char* ptr = static_cast<const char*>(bytes);
530 uint8_t preamble_ints;
531 ptr += copy_from_mem(ptr, preamble_ints);
532 uint8_t serial_version;
533 ptr += copy_from_mem(ptr, serial_version);
534 uint8_t family_id;
535 ptr += copy_from_mem(ptr, family_id);
536 uint8_t flags_byte;
537 ptr += copy_from_mem(ptr, flags_byte);
538 uint16_t k;
539 ptr += copy_from_mem(ptr, k);
540 uint8_t m;
541 ptr += copy_from_mem(ptr, m);
542 ptr += sizeof(uint8_t); // skip unused byte
543
544 check_k(k);
545 check_m(m);
546 check_preamble_ints(preamble_ints, flags_byte);
547 check_serial_version(serial_version);
548 check_family_id(family_id);
549 ensure_minimum_memory(size, preamble_ints * sizeof(uint32_t));
550
551 const bool is_empty(flags_byte & (1 << flags::IS_EMPTY));
552 if (is_empty) return kll_sketch(k, comparator, allocator);
553
554 uint64_t n;
555 uint16_t min_k;
556 uint8_t num_levels;
557 const bool is_single_item(flags_byte & (1 << flags::IS_SINGLE_ITEM)); // used in serial version 2
558 const char* end_ptr = static_cast<const char*>(bytes) + size;
559 if (is_single_item) {
560 n = 1;
561 min_k = k;
562 num_levels = 1;
563 } else {
564 ptr += copy_from_mem(ptr, n);
565 ptr += copy_from_mem(ptr, min_k);
566 ptr += copy_from_mem(ptr, num_levels);
567 ptr += sizeof(uint8_t); // skip unused byte
568 check_num_levels(num_levels);
569 }
570 vector_u32 levels(num_levels + 1, 0, allocator);
571 const uint32_t capacity(kll_helper::compute_total_capacity(k, m, num_levels));
572 if (is_single_item) {
573 levels[0] = capacity - 1;
574 } else {
575 // the last integer in levels_ is not serialized because it can be derived
576 ensure_minimum_memory(size, (ptr - static_cast<const char*>(bytes)) + sizeof(levels[0]) * num_levels);
577 ptr += copy_from_mem(ptr, levels.data(), sizeof(levels[0]) * num_levels);
578 }
579 levels[num_levels] = capacity;
580 check_levels(levels);
581 optional<T> min_item;
582 optional<T> max_item;
583 if (!is_single_item) {
584 // Space to deserialize min and max.
585 // serde::deserialize expects allocated but not initialized storage.
586 typename std::aligned_storage<sizeof(T), alignof(T)>::type tmp_storage;
587 T* tmp = reinterpret_cast<T*>(&tmp_storage);
588
589 ptr += sd.deserialize(ptr, end_ptr - ptr, tmp, 1);
590 // serde call did not throw, repackage and cleanup
591 min_item.emplace(std::move(*tmp));
592 tmp->~T();
593 ptr += sd.deserialize(ptr, end_ptr - ptr, tmp, 1);
594 // serde call did not throw, repackage and cleanup
595 max_item.emplace(std::move(*tmp));
596 tmp->~T();
597 }
598 A alloc(allocator);
599 auto items_buffer_deleter = [capacity, &alloc](T* ptr) { alloc.deallocate(ptr, capacity); };
600 std::unique_ptr<T, decltype(items_buffer_deleter)> items_buffer(alloc.allocate(capacity), items_buffer_deleter);
601 const auto num_items = levels[num_levels] - levels[0];
602 ptr += sd.deserialize(ptr, end_ptr - ptr, &items_buffer.get()[levels[0]], num_items);
603 // serde call did not throw, repackage with destrtuctors
604 std::unique_ptr<T, items_deleter> items(items_buffer.release(), items_deleter(levels[0], capacity, allocator));
605 const size_t delta = ptr - static_cast<const char*>(bytes);
606 if (delta != size) throw std::logic_error("deserialized size mismatch: " + std::to_string(delta) + " != " + std::to_string(size));
607 const bool is_level_zero_sorted = (flags_byte & (1 << flags::IS_LEVEL_ZERO_SORTED)) > 0;
608 if (is_single_item) {
609 min_item.emplace(items.get()[levels[0]]);
610 max_item.emplace(items.get()[levels[0]]);
611 }
612 return kll_sketch(k, min_k, n, num_levels, std::move(levels), std::move(items), capacity,
613 std::move(min_item), std::move(max_item), is_level_zero_sorted, comparator);
614}
615
616/*
617 * Gets the normalized rank error given k and pmf.
618 * k - the configuration parameter
619 * pmf - if true, returns the "double-sided" normalized rank error for the get_PMF() function.
620 * Otherwise, it is the "single-sided" normalized rank error for all the other queries.
621 * Constants were derived as the best fit to 99 percentile empirically measured max error in thousands of trials
622 */
623template<typename T, typename C, typename A>
624double kll_sketch<T, C, A>::get_normalized_rank_error(uint16_t k, bool pmf) {
625 return pmf
626 ? 2.446 / pow(k, 0.9433)
627 : 2.296 / pow(k, 0.9723);
628}
629
630// for deserialization
631template<typename T, typename C, typename A>
632kll_sketch<T, C, A>::kll_sketch(uint16_t k, uint16_t min_k, uint64_t n, uint8_t num_levels, vector_u32&& levels,
633 std::unique_ptr<T, items_deleter> items, uint32_t items_size, optional<T>&& min_item,
634 optional<T>&& max_item, bool is_level_zero_sorted, const C& comparator):
635comparator_(comparator),
636allocator_(levels.get_allocator()),
637k_(k),
638m_(kll_constants::DEFAULT_M),
639min_k_(min_k),
640num_levels_(num_levels),
641is_level_zero_sorted_(is_level_zero_sorted),
642n_(n),
643levels_(std::move(levels)),
644items_(items.release()),
645items_size_(items_size),
646min_item_(std::move(min_item)),
647max_item_(std::move(max_item)),
648sorted_view_(nullptr)
649{}
650
651// The following code is only valid in the special case of exactly reaching capacity while updating.
652// It cannot be used while merging, while reducing k, or anything else.
653template<typename T, typename C, typename A>
654void kll_sketch<T, C, A>::compress_while_updating(void) {
655 const uint8_t level = find_level_to_compact();
656
657 // It is important to add the new top level right here. Be aware that this operation
658 // grows the buffer and shifts the data and also the boundaries of the data and grows the
659 // levels array and increments num_levels_
660 if (level == (num_levels_ - 1)) {
661 add_empty_top_level_to_completely_full_sketch();
662 }
663
664 const uint32_t raw_beg = levels_[level];
665 const uint32_t raw_lim = levels_[level + 1];
666 // +2 is OK because we already added a new top level if necessary
667 const uint32_t pop_above = levels_[level + 2] - raw_lim;
668 const uint32_t raw_pop = raw_lim - raw_beg;
669 const bool odd_pop = kll_helper::is_odd(raw_pop);
670 const uint32_t adj_beg = odd_pop ? raw_beg + 1 : raw_beg;
671 const uint32_t adj_pop = odd_pop ? raw_pop - 1 : raw_pop;
672 const uint32_t half_adj_pop = adj_pop / 2;
673 const uint32_t destroy_beg = levels_[0];
674
675 // level zero might not be sorted, so we must sort it if we wish to compact it
676 // sort_level_zero() is not used here because of the adjustment for odd number of items
677 if ((level == 0) && !is_level_zero_sorted_) {
678 std::sort(items_ + adj_beg, items_ + adj_beg + adj_pop, comparator_);
679 }
680 if (pop_above == 0) {
681 kll_helper::randomly_halve_up(items_, adj_beg, adj_pop);
682 } else {
683 kll_helper::randomly_halve_down(items_, adj_beg, adj_pop);
684 kll_helper::merge_sorted_arrays<T, C>(items_, adj_beg, half_adj_pop, raw_lim, pop_above, adj_beg + half_adj_pop);
685 }
686 levels_[level + 1] -= half_adj_pop; // adjust boundaries of the level above
687 if (odd_pop) {
688 levels_[level] = levels_[level + 1] - 1; // the current level now contains one item
689 if (levels_[level] != raw_beg) items_[levels_[level]] = std::move(items_[raw_beg]); // namely this leftover guy
690 } else {
691 levels_[level] = levels_[level + 1]; // the current level is now empty
692 }
693
694 // verify that we freed up half_adj_pop array slots just below the current level
695 if (levels_[level] != (raw_beg + half_adj_pop)) throw std::logic_error("compaction error");
696
697 // finally, we need to shift up the data in the levels below
698 // so that the freed-up space can be used by level zero
699 if (level > 0) {
700 const uint32_t amount = raw_beg - levels_[0];
701 std::move_backward(items_ + levels_[0], items_ + levels_[0] + amount, items_ + levels_[0] + half_adj_pop + amount);
702 for (uint8_t lvl = 0; lvl < level; lvl++) levels_[lvl] += half_adj_pop;
703 }
704 for (uint32_t i = 0; i < half_adj_pop; i++) items_[i + destroy_beg].~T();
705}
706
707template<typename T, typename C, typename A>
708uint8_t kll_sketch<T, C, A>::find_level_to_compact() const {
709 uint8_t level = 0;
710 while (true) {
711 if (level >= num_levels_) throw std::logic_error("capacity calculation error");
712 const uint32_t pop = levels_[level + 1] - levels_[level];
713 const uint32_t cap = kll_helper::level_capacity(k_, num_levels_, level, m_);
714 if (pop >= cap) {
715 return level;
716 }
717 level++;
718 }
719}
720
721template<typename T, typename C, typename A>
722void kll_sketch<T, C, A>::add_empty_top_level_to_completely_full_sketch() {
723 const uint32_t cur_total_cap = levels_[num_levels_];
724
725 // make sure that we are following a certain growth scheme
726 if (levels_[0] != 0) throw std::logic_error("full sketch expected");
727 if (items_size_ != cur_total_cap) throw std::logic_error("current capacity mismatch");
728
729 // note that merging MIGHT over-grow levels_, in which case we might not have to grow it here
730 const uint8_t new_levels_size = num_levels_ + 2;
731 if (levels_.size() < new_levels_size) {
732 levels_.resize(new_levels_size);
733 }
734
735 const uint32_t delta_cap = kll_helper::level_capacity(k_, num_levels_ + 1, 0, m_);
736 const uint32_t new_total_cap = cur_total_cap + delta_cap;
737
738 // move (and shift) the current data into the new buffer
739 T* new_buf = allocator_.allocate(new_total_cap);
740 kll_helper::move_construct<T>(items_, 0, cur_total_cap, new_buf, delta_cap, true);
741 allocator_.deallocate(items_, items_size_);
742 items_ = new_buf;
743 items_size_ = new_total_cap;
744
745 // this loop includes the old "extra" index at the top
746 for (uint8_t i = 0; i <= num_levels_; i++) {
747 levels_[i] += delta_cap;
748 }
749
750 if (levels_[num_levels_] != new_total_cap) throw std::logic_error("new capacity mismatch");
751
752 num_levels_++;
753 levels_[num_levels_] = new_total_cap; // initialize the new "extra" index at the top
754}
755
756template<typename T, typename C, typename A>
757void kll_sketch<T, C, A>::sort_level_zero() {
758 if (!is_level_zero_sorted_) {
759 std::sort(items_ + levels_[0], items_ + levels_[1], comparator_);
760 is_level_zero_sorted_ = true;
761 }
762}
763
764template<typename T, typename C, typename A>
765void kll_sketch<T, C, A>::check_sorting() const {
766 // not checking level 0
767 for (uint8_t level = 1; level < num_levels_; ++level) {
768 const auto from = items_ + levels_[level];
769 const auto to = items_ + levels_[level + 1];
770 if (!std::is_sorted(from, to, comparator_)) {
771 throw std::logic_error("levels must be sorted");
772 }
773 }
774}
775
776template<typename T, typename C, typename A>
778 const_cast<kll_sketch*>(this)->sort_level_zero(); // allow this side effect
779 quantiles_sorted_view<T, C, A> view(get_num_retained(), comparator_, allocator_);
780 for (uint8_t level = 0; level < num_levels_; ++level) {
781 const auto from = items_ + levels_[level];
782 const auto to = items_ + levels_[level + 1]; // exclusive
783 view.add(from, to, 1ULL << level);
784 }
785 view.convert_to_cummulative();
786 return view;
787}
788
789template<typename T, typename C, typename A>
790template<typename O>
791void kll_sketch<T, C, A>::merge_higher_levels(O&& other, uint64_t final_n) {
792 const uint32_t tmp_num_items = get_num_retained() + other.get_num_retained_above_level_zero();
793 A alloc(allocator_);
794 auto tmp_items_deleter = [tmp_num_items, &alloc](T* ptr) { alloc.deallocate(ptr, tmp_num_items); }; // no destructor needed
795 const std::unique_ptr<T, decltype(tmp_items_deleter)> workbuf(allocator_.allocate(tmp_num_items), tmp_items_deleter);
796 const uint8_t ub = kll_helper::ub_on_num_levels(final_n);
797 const size_t work_levels_size = ub + 2; // ub+1 does not work
798 vector_u32 worklevels(work_levels_size, 0, allocator_);
799 vector_u32 outlevels(work_levels_size, 0, allocator_);
800
801 const uint8_t provisional_num_levels = std::max(num_levels_, other.num_levels_);
802
803 populate_work_arrays(std::forward<O>(other), workbuf.get(), worklevels.data(), provisional_num_levels);
804
805 const kll_helper::compress_result result = kll_helper::general_compress<T, C>(k_, m_, provisional_num_levels, workbuf.get(),
806 worklevels.data(), outlevels.data(), is_level_zero_sorted_);
807
808 // ub can sometimes be much bigger
809 if (result.final_num_levels > ub) throw std::logic_error("merge error");
810
811 // now we need to transfer the results back into "this" sketch
812 if (result.final_capacity != items_size_) {
813 allocator_.deallocate(items_, items_size_);
814 items_size_ = result.final_capacity;
815 items_ = allocator_.allocate(items_size_);
816 }
817 const uint32_t free_space_at_bottom = result.final_capacity - result.final_num_items;
818 kll_helper::move_construct<T>(workbuf.get(), outlevels[0], outlevels[0] + result.final_num_items, items_, free_space_at_bottom, true);
819
820 const size_t new_levels_size = result.final_num_levels + 1;
821 if (levels_.size() < new_levels_size) {
822 levels_.resize(new_levels_size);
823 }
824 const uint32_t offset = free_space_at_bottom - outlevels[0];
825 for (uint8_t lvl = 0; lvl < levels_.size(); lvl++) { // includes the "extra" index
826 levels_[lvl] = outlevels[lvl] + offset;
827 }
828 num_levels_ = result.final_num_levels;
829}
830
831// this leaves items_ uninitialized (all objects moved out and destroyed)
832template<typename T, typename C, typename A>
833template<typename FwdSk>
834void kll_sketch<T, C, A>::populate_work_arrays(FwdSk&& other, T* workbuf, uint32_t* worklevels, uint8_t provisional_num_levels) {
835 worklevels[0] = 0;
836
837 // the level zero data from "other" was already inserted into "this"
838 kll_helper::move_construct<T>(items_, levels_[0], levels_[1], workbuf, 0, true);
839 worklevels[1] = safe_level_size(0);
840
841 for (uint8_t lvl = 1; lvl < provisional_num_levels; lvl++) {
842 const uint32_t self_pop = safe_level_size(lvl);
843 const uint32_t other_pop = other.safe_level_size(lvl);
844 worklevels[lvl + 1] = worklevels[lvl] + self_pop + other_pop;
845
846 if ((self_pop > 0) && (other_pop == 0)) {
847 kll_helper::move_construct<T>(items_, levels_[lvl], levels_[lvl] + self_pop, workbuf, worklevels[lvl], true);
848 } else if ((self_pop == 0) && (other_pop > 0)) {
849 for (auto i = other.levels_[lvl], j = worklevels[lvl]; i < other.levels_[lvl] + other_pop; ++i, ++j) {
850 new (&workbuf[j]) T(conditional_forward<FwdSk>(other.items_[i]));
851 }
852 } else if ((self_pop > 0) && (other_pop > 0)) {
853 kll_helper::merge_sorted_arrays<T, C>(items_, levels_[lvl], self_pop, other.items_, other.levels_[lvl], other_pop, workbuf, worklevels[lvl]);
854 }
855 }
856}
857
858template<typename T, typename C, typename A>
859void kll_sketch<T, C, A>::assert_correct_total_weight() const {
860 const uint64_t total(kll_helper::sum_the_sample_weights(num_levels_, levels_.data()));
861 if (total != n_) {
862 throw std::logic_error("Total weight does not match N");
863 }
864}
865
866template<typename T, typename C, typename A>
867uint32_t kll_sketch<T, C, A>::safe_level_size(uint8_t level) const {
868 if (level >= num_levels_) return 0;
869 return levels_[level + 1] - levels_[level];
870}
871
872template<typename T, typename C, typename A>
873uint32_t kll_sketch<T, C, A>::get_num_retained_above_level_zero() const {
874 if (num_levels_ == 1) return 0;
875 return levels_[num_levels_] - levels_[1];
876}
877
878template<typename T, typename C, typename A>
879void kll_sketch<T, C, A>::check_k(uint16_t k) {
880 if (k < kll_constants::MIN_K) {
881 throw std::invalid_argument("Possible corruption: K must be >= " + std::to_string(kll_constants::MIN_K)
882 + ": " + std::to_string(k));
883 }
884}
885
886template<typename T, typename C, typename A>
887void kll_sketch<T, C, A>::check_m(uint8_t m) {
888 if (m != kll_constants::DEFAULT_M) {
889 throw std::invalid_argument("Possible corruption: M must be " + std::to_string(kll_constants::DEFAULT_M)
890 + ": " + std::to_string(m));
891 }
892}
893
894template<typename T, typename C, typename A>
895void kll_sketch<T, C, A>::check_preamble_ints(uint8_t preamble_ints, uint8_t flags_byte) {
896 const bool is_empty(flags_byte & (1 << flags::IS_EMPTY));
897 const bool is_single_item(flags_byte & (1 << flags::IS_SINGLE_ITEM));
898 if (is_empty || is_single_item) {
899 if (preamble_ints != PREAMBLE_INTS_SHORT) {
900 throw std::invalid_argument("Possible corruption: preamble ints must be "
901 + std::to_string(PREAMBLE_INTS_SHORT) + " for an empty or single item sketch: " + std::to_string(preamble_ints));
902 }
903 } else {
904 if (preamble_ints != PREAMBLE_INTS_FULL) {
905 throw std::invalid_argument("Possible corruption: preamble ints must be "
906 + std::to_string(PREAMBLE_INTS_FULL) + " for a sketch with more than one item: " + std::to_string(preamble_ints));
907 }
908 }
909}
910
911template<typename T, typename C, typename A>
912void kll_sketch<T, C, A>::check_serial_version(uint8_t serial_version) {
913 if (serial_version != SERIAL_VERSION_1 && serial_version != SERIAL_VERSION_2) {
914 throw std::invalid_argument("Possible corruption: serial version mismatch: expected "
915 + std::to_string(SERIAL_VERSION_1) + " or " + std::to_string(SERIAL_VERSION_2)
916 + ", got " + std::to_string(serial_version));
917 }
918}
919
920template<typename T, typename C, typename A>
921void kll_sketch<T, C, A>::check_family_id(uint8_t family_id) {
922 if (family_id != FAMILY) {
923 throw std::invalid_argument("Possible corruption: family mismatch: expected "
924 + std::to_string(FAMILY) + ", got " + std::to_string(family_id));
925 }
926}
927
928template<typename T, typename C, typename A>
929void kll_sketch<T, C, A>::check_num_levels(uint8_t num_levels) {
930 if (num_levels == 0) {
931 throw std::invalid_argument("Possible corruption: number of levels must be > 0");
932 }
933}
934
935// The last entry is the capacity. Each level must start at or after the previous one,
936// so every level lies within the items buffer.
937template<typename T, typename C, typename A>
938void kll_sketch<T, C, A>::check_levels(const vector_u32& levels) {
939 for (size_t i = 1; i < levels.size(); ++i) {
940 if (levels[i - 1] > levels[i]) {
941 throw std::invalid_argument("Possible corruption: levels[" + std::to_string(i - 1) + "] = "
942 + std::to_string(levels[i - 1]) + " > levels[" + std::to_string(i) + "] = " + std::to_string(levels[i]));
943 }
944 }
945}
946
947template <typename T, typename C, typename A>
948string<A> kll_sketch<T, C, A>::to_string(bool print_levels, bool print_items) const {
949 // Using a temporary stream for implementation here does not comply with AllocatorAwareContainer requirements.
950 // The stream does not support passing an allocator instance, and alternatives are complicated.
951 std::ostringstream os;
952 os << "### KLL sketch summary:" << std::endl;
953 os << " K : " << k_ << std::endl;
954 os << " min K : " << min_k_ << std::endl;
955 os << " M : " << (unsigned int) m_ << std::endl;
956 os << " N : " << n_ << std::endl;
957 os << " Epsilon : " << std::setprecision(3) << get_normalized_rank_error(false) * 100 << "%" << std::endl;
958 os << " Epsilon PMF : " << get_normalized_rank_error(true) * 100 << "%" << std::endl;
959 os << " Empty : " << (is_empty() ? "true" : "false") << std::endl;
960 os << " Estimation mode: " << (is_estimation_mode() ? "true" : "false") << std::endl;
961 os << " Levels : " << (unsigned int) num_levels_ << std::endl;
962 os << " Sorted : " << (is_level_zero_sorted_ ? "true" : "false") << std::endl;
963 os << " Capacity items : " << items_size_ << std::endl;
964 os << " Retained items : " << get_num_retained() << std::endl;
965 if (!is_empty()) {
966 os << " Min item : " << *min_item_ << std::endl;
967 os << " Max item : " << *max_item_ << std::endl;
968 }
969 os << "### End sketch summary" << std::endl;
970
971 if (print_levels) {
972 os << "### KLL sketch levels:" << std::endl;
973 os << " index: nominal capacity, actual size" << std::endl;
974 for (uint8_t i = 0; i < num_levels_; i++) {
975 os << " " << (unsigned int) i << ": " << kll_helper::level_capacity(k_, num_levels_, i, m_) << ", " << safe_level_size(i) << std::endl;
976 }
977 os << "### End sketch levels" << std::endl;
978 }
979
980 if (print_items) {
981 os << "### KLL sketch data:" << std::endl;
982 uint8_t level = 0;
983 while (level < num_levels_) {
984 const uint32_t from_index = levels_[level];
985 const uint32_t to_index = levels_[level + 1]; // exclusive
986 if (from_index < to_index) {
987 os << " level " << (unsigned int) level << ":" << std::endl;
988 }
989 for (uint32_t i = from_index; i < to_index; i++) {
990 os << " " << items_[i] << std::endl;
991 }
992 level++;
993 }
994 os << "### End sketch data" << std::endl;
995 }
996 return string<A>(os.str().c_str(), allocator_);
997}
998
999template <typename T, typename C, typename A>
1000typename kll_sketch<T, C, A>::const_iterator kll_sketch<T, C, A>::begin() const {
1001 return kll_sketch<T, C, A>::const_iterator(items_, levels_.data(), num_levels_);
1002}
1003
1004template <typename T, typename C, typename A>
1005typename kll_sketch<T, C, A>::const_iterator kll_sketch<T, C, A>::end() const {
1006 return kll_sketch<T, C, A>::const_iterator(nullptr, levels_.data(), num_levels_);
1007}
1008
1009template<typename T, typename C, typename A>
1010class kll_sketch<T, C, A>::items_deleter {
1011 public:
1012 items_deleter(uint32_t start, uint32_t num, const A& allocator):
1013 allocator_(allocator), start_(start), num_(num) {}
1014 void operator() (T* ptr) {
1015 if (ptr != nullptr) {
1016 for (uint32_t i = start_; i < num_; ++i) ptr[i].~T();
1017 allocator_.deallocate(ptr, num_);
1018 }
1019 }
1020 private:
1021 A allocator_;
1022 uint32_t start_;
1023 uint32_t num_;
1024};
1025
1026template<typename T, typename C, typename A>
1027void kll_sketch<T, C, A>::setup_sorted_view() const {
1028 if (sorted_view_ == nullptr) {
1029 using AllocSortedView = typename std::allocator_traits<A>::template rebind_alloc<quantiles_sorted_view<T, C, A>>;
1030 sorted_view_ = new (AllocSortedView(allocator_).allocate(1)) quantiles_sorted_view<T, C, A>(get_sorted_view());
1031 }
1032}
1033
1034template<typename T, typename C, typename A>
1035void kll_sketch<T, C, A>::reset_sorted_view() {
1036 if (sorted_view_ != nullptr) {
1037 sorted_view_->~quantiles_sorted_view();
1038 using AllocSortedView = typename std::allocator_traits<A>::template rebind_alloc<quantiles_sorted_view<T, C, A>>;
1039 AllocSortedView(allocator_).deallocate(sorted_view_, 1);
1040 sorted_view_ = nullptr;
1041 }
1042}
1043
1044// kll_sketch::const_iterator implementation
1045
1046template<typename T, typename C, typename A>
1047kll_sketch<T, C, A>::const_iterator::const_iterator(const T* items, const uint32_t* levels, const uint8_t num_levels):
1048items(items), levels(levels), num_levels(num_levels), index(items == nullptr ? levels[num_levels] : levels[0]), level(items == nullptr ? num_levels : 0), weight(1)
1049{}
1050
1051template<typename T, typename C, typename A>
1052typename kll_sketch<T, C, A>::const_iterator& kll_sketch<T, C, A>::const_iterator::operator++() {
1053 ++index;
1054 if (index == levels[level + 1]) { // go to the next non-empty level
1055 do {
1056 ++level;
1057 weight *= 2;
1058 } while (level < num_levels && levels[level] == levels[level + 1]);
1059 }
1060 return *this;
1061}
1062
1063template<typename T, typename C, typename A>
1064typename kll_sketch<T, C, A>::const_iterator& kll_sketch<T, C, A>::const_iterator::operator++(int) {
1065 const_iterator tmp(*this);
1066 operator++();
1067 return tmp;
1068}
1069
1070template<typename T, typename C, typename A>
1071bool kll_sketch<T, C, A>::const_iterator::operator==(const const_iterator& other) const {
1072 return index == other.index;
1073}
1074
1075template<typename T, typename C, typename A>
1076bool kll_sketch<T, C, A>::const_iterator::operator!=(const const_iterator& other) const {
1077 return !operator==(other);
1078}
1079
1080template<typename T, typename C, typename A>
1081auto kll_sketch<T, C, A>::const_iterator::operator*() const -> reference {
1082 return value_type(items[index], weight);
1083}
1084
1085template<typename T, typename C, typename A>
1086auto kll_sketch<T, C, A>::const_iterator::operator->() const -> pointer {
1087 return **this;
1088}
1089
1090} /* namespace datasketches */
1091
1092#endif
Implementation of a very compact quantiles sketch with lazy compaction scheme and nearly optimal accu...
Definition kll_sketch.hpp:171
C get_comparator() const
Returns an instance of the comparator for this sketch.
Definition kll_sketch_impl.hpp:272
quantiles_sorted_view< T, C, A > get_sorted_view() const
Gets the sorted view of this sketch.
Definition kll_sketch_impl.hpp:777
kll_sketch & operator=(const kll_sketch &other)
Copy assignment.
Definition kll_sketch_impl.hpp:103
vector_double get_CDF(const T *split_points, uint32_t size, bool inclusive=true) const
Returns an approximation to the Cumulative Distribution Function (CDF), which is the cumulative analo...
Definition kll_sketch_impl.hpp:296
const_iterator begin() const
Iterator pointing to the first item in the sketch.
Definition kll_sketch_impl.hpp:1000
uint32_t get_num_retained() const
Returns the number of retained items (samples) in the sketch.
Definition kll_sketch_impl.hpp:250
static kll_sketch deserialize(std::istream &is, const SerDe &sd=SerDe(), const C &comparator=C(), const A &allocator=A())
This method deserializes a sketch from a given stream.
vector_double get_PMF(const T *split_points, uint32_t size, bool inclusive=true) const
Returns an approximation to the Probability Mass Function (PMF) of the input stream given a set of sp...
Definition kll_sketch_impl.hpp:289
void merge(FwdSk &&other)
Merges another sketch into this one.
Definition kll_sketch_impl.hpp:210
T get_max_item() const
Returns the max item of the stream.
Definition kll_sketch_impl.hpp:266
const_iterator end() const
Iterator pointing to the past-the-end item in the sketch.
Definition kll_sketch_impl.hpp:1005
void update(FwdT &&item)
Updates this sketch with the given data item.
Definition kll_sketch_impl.hpp:181
void serialize(std::ostream &os, const SerDe &sd=SerDe()) const
This method serializes the sketch into a given stream in a binary form.
Definition kll_sketch_impl.hpp:368
string< A > to_string(bool print_levels=false, bool print_items=false) const
Prints a summary of the sketch.
Definition kll_sketch_impl.hpp:948
uint16_t get_k() const
Returns configured parameter k.
Definition kll_sketch_impl.hpp:240
bool is_empty() const
Returns true if this sketch is empty.
Definition kll_sketch_impl.hpp:235
double get_rank(const T &item, bool inclusive=true) const
Returns an approximation to the normalized rank of the given item from 0 to 1, inclusive.
Definition kll_sketch_impl.hpp:282
A get_allocator() const
Returns an instance of the allocator for this sketch.
Definition kll_sketch_impl.hpp:277
T get_min_item() const
Returns the min item of the stream.
Definition kll_sketch_impl.hpp:260
typename quantiles_sorted_view< T, C, A >::quantile_return_type quantile_return_type
Quantile return type.
Definition kll_sketch.hpp:183
quantile_return_type get_quantile(double rank, bool inclusive=true) const
Returns an item from the sketch that is the best approximation to an item from the original stream wi...
Definition kll_sketch_impl.hpp:303
size_t get_serialized_size_bytes(const SerDe &sd=SerDe()) const
Computes size needed to serialize the current state of the sketch.
Definition kll_sketch_impl.hpp:321
static size_t get_max_serialized_size_bytes(uint16_t k, uint64_t n)
Returns upper bound on the serialized size of a sketch given a parameter k and stream length.
Definition kll_sketch_impl.hpp:349
double get_normalized_rank_error(bool pmf) const
Gets the approximate rank error of this sketch normalized as a fraction between zero and one.
Definition kll_sketch_impl.hpp:314
bool is_estimation_mode() const
Returns true if this sketch is in estimation mode.
Definition kll_sketch_impl.hpp:255
uint64_t get_n() const
Returns the length of the input stream.
Definition kll_sketch_impl.hpp:245
Sorted view for quantiles sketches (REQ, KLL and Quantiles).
Definition quantiles_sorted_view.hpp:38
KLL sketch constants.
Definition kll_sketch.hpp:34
const uint16_t MAX_K
max value of parameter K
Definition kll_sketch.hpp:41
const uint16_t MIN_K
min value of parameter K
Definition kll_sketch.hpp:39
DataSketches namespace.
Definition binomial_bounds.hpp:38