datasketches 0.5.2 → 0.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/ext/datasketches/theta_wrapper.cpp +1 -1
- data/lib/datasketches/version.rb +1 -1
- data/vendor/datasketches-cpp/CMakeLists.txt +5 -3
- data/vendor/datasketches-cpp/CODE_OF_CONDUCT.md +1 -1
- data/vendor/datasketches-cpp/LICENSE +14 -0
- data/vendor/datasketches-cpp/README.md +67 -73
- data/vendor/datasketches-cpp/benchmarks/CMakeLists.txt +52 -0
- data/vendor/datasketches-cpp/benchmarks/benchmark_count_min_sketch.cpp +153 -0
- data/vendor/datasketches-cpp/benchmarks/benchmark_count_min_sketch_serialization.cpp +161 -0
- data/vendor/datasketches-cpp/common/CMakeLists.txt +1 -0
- data/vendor/datasketches-cpp/common/include/binomial_bounds.hpp +2 -2
- data/vendor/datasketches-cpp/common/include/fdlibm_log.hpp +101 -0
- data/vendor/datasketches-cpp/common/include/serde.hpp +6 -0
- data/vendor/datasketches-cpp/common/test/CMakeLists.txt +44 -2
- data/vendor/datasketches-cpp/common/test/binomial_bounds_test.cpp +279 -0
- data/vendor/datasketches-cpp/common/test/deserialize_hardening_test.cpp +188 -0
- data/vendor/datasketches-cpp/count/include/count_min.hpp +17 -4
- data/vendor/datasketches-cpp/count/include/count_min_impl.hpp +65 -83
- data/vendor/datasketches-cpp/count/test/count_min_test.cpp +63 -7
- data/vendor/datasketches-cpp/cpc/include/compression_data.hpp +2 -0
- data/vendor/datasketches-cpp/cpc/include/cpc_compressor_impl.hpp +16 -7
- data/vendor/datasketches-cpp/cpc/include/cpc_sketch.hpp +1 -0
- data/vendor/datasketches-cpp/cpc/include/cpc_sketch_impl.hpp +54 -36
- data/vendor/datasketches-cpp/cpc/include/cpc_union_impl.hpp +27 -27
- data/vendor/datasketches-cpp/cpc/include/cpc_util.hpp +7 -7
- data/vendor/datasketches-cpp/cpc/include/icon_estimator.hpp +5 -5
- data/vendor/datasketches-cpp/cpc/include/u32_table_impl.hpp +16 -16
- data/vendor/datasketches-cpp/cpc/test/cpc_sketch_test.cpp +35 -0
- data/vendor/datasketches-cpp/fi/include/frequent_items_sketch.hpp +28 -3
- data/vendor/datasketches-cpp/fi/include/frequent_items_sketch_impl.hpp +41 -25
- data/vendor/datasketches-cpp/fi/include/reverse_purge_hash_map.hpp +3 -1
- data/vendor/datasketches-cpp/fi/include/reverse_purge_hash_map_impl.hpp +10 -5
- data/vendor/datasketches-cpp/fi/test/frequent_items_sketch_serialize_for_java.cpp +24 -0
- data/vendor/datasketches-cpp/fi/test/frequent_items_sketch_test.cpp +116 -0
- data/vendor/datasketches-cpp/filters/include/bloom_filter.hpp +1 -1
- data/vendor/datasketches-cpp/filters/include/bloom_filter_impl.hpp +32 -12
- data/vendor/datasketches-cpp/filters/test/bloom_filter_test.cpp +28 -1
- data/vendor/datasketches-cpp/hll/include/CouponHashSet-internal.hpp +1 -2
- data/vendor/datasketches-cpp/hll/include/CouponList-internal.hpp +19 -5
- data/vendor/datasketches-cpp/hll/include/CubicInterpolation-internal.hpp +4 -4
- data/vendor/datasketches-cpp/hll/include/HarmonicNumbers-internal.hpp +2 -1
- data/vendor/datasketches-cpp/hll/include/Hll4Array-internal.hpp +4 -4
- data/vendor/datasketches-cpp/hll/include/Hll4Array.hpp +1 -1
- data/vendor/datasketches-cpp/hll/include/Hll6Array-internal.hpp +3 -3
- data/vendor/datasketches-cpp/hll/include/Hll6Array.hpp +1 -1
- data/vendor/datasketches-cpp/hll/include/Hll8Array-internal.hpp +6 -3
- data/vendor/datasketches-cpp/hll/include/Hll8Array.hpp +1 -1
- data/vendor/datasketches-cpp/hll/include/HllArray-internal.hpp +35 -29
- data/vendor/datasketches-cpp/hll/include/HllArray.hpp +1 -1
- data/vendor/datasketches-cpp/hll/include/HllSketch-internal.hpp +3 -5
- data/vendor/datasketches-cpp/hll/include/HllSketchImpl-internal.hpp +5 -11
- data/vendor/datasketches-cpp/hll/include/HllSketchImpl.hpp +2 -4
- data/vendor/datasketches-cpp/hll/include/HllSketchImplFactory.hpp +11 -10
- data/vendor/datasketches-cpp/hll/include/HllUnion-internal.hpp +17 -8
- data/vendor/datasketches-cpp/hll/include/HllUtil.hpp +12 -3
- data/vendor/datasketches-cpp/hll/include/coupon_iterator-internal.hpp +2 -2
- data/vendor/datasketches-cpp/hll/include/coupon_iterator.hpp +3 -0
- data/vendor/datasketches-cpp/hll/include/hll.hpp +11 -4
- data/vendor/datasketches-cpp/hll/include/hll.private.hpp +19 -0
- data/vendor/datasketches-cpp/hll/test/CMakeLists.txt +2 -0
- data/vendor/datasketches-cpp/hll/test/CouponListTest.cpp +64 -0
- data/vendor/datasketches-cpp/hll/test/HllFullSizeTest.cpp +137 -0
- data/vendor/datasketches-cpp/hll/test/HllKxqRebuildTest.cpp +150 -0
- data/vendor/datasketches-cpp/hll/test/HllSketchTest.cpp +3 -3
- data/vendor/datasketches-cpp/hll/test/HllUnionTest.cpp +71 -1
- data/vendor/datasketches-cpp/kll/include/kll_helper_impl.hpp +10 -10
- data/vendor/datasketches-cpp/kll/include/kll_sketch.hpp +10 -1
- data/vendor/datasketches-cpp/kll/include/kll_sketch_impl.hpp +33 -24
- data/vendor/datasketches-cpp/kll/test/kll_sketch_deserialize_from_java_test.cpp +24 -0
- data/vendor/datasketches-cpp/kll/test/kll_sketch_serialize_for_java.cpp +10 -0
- data/vendor/datasketches-cpp/quantiles/include/quantiles_sketch.hpp +11 -2
- data/vendor/datasketches-cpp/quantiles/include/quantiles_sketch_impl.hpp +24 -15
- data/vendor/datasketches-cpp/req/include/req_sketch.hpp +9 -0
- data/vendor/datasketches-cpp/req/include/req_sketch_impl.hpp +24 -15
- data/vendor/datasketches-cpp/req/test/req_sketch_deserialize_from_java_test.cpp +46 -0
- data/vendor/datasketches-cpp/req/test/req_sketch_serialize_for_java.cpp +20 -0
- data/vendor/datasketches-cpp/req/test/req_sketch_test.cpp +70 -0
- data/vendor/datasketches-cpp/sampling/include/ebpps_sample_impl.hpp +17 -8
- data/vendor/datasketches-cpp/sampling/include/ebpps_sketch.hpp +13 -0
- data/vendor/datasketches-cpp/sampling/include/var_opt_sketch.hpp +10 -1
- data/vendor/datasketches-cpp/sampling/include/var_opt_sketch_impl.hpp +6 -7
- data/vendor/datasketches-cpp/sampling/include/var_opt_union.hpp +5 -1
- data/vendor/datasketches-cpp/sampling/include/var_opt_union_impl.hpp +2 -1
- data/vendor/datasketches-cpp/sampling/test/ebpps_allocation_test.cpp +1 -1
- data/vendor/datasketches-cpp/sampling/test/ebpps_sketch_test.cpp +2 -2
- data/vendor/datasketches-cpp/sampling/test/var_opt_allocation_test.cpp +1 -1
- data/vendor/datasketches-cpp/sampling/test/var_opt_sketch_test.cpp +10 -4
- data/vendor/datasketches-cpp/sampling/test/var_opt_union_test.cpp +12 -0
- data/vendor/datasketches-cpp/tdigest/include/tdigest.hpp +38 -2
- data/vendor/datasketches-cpp/tdigest/include/tdigest_impl.hpp +168 -9
- data/vendor/datasketches-cpp/tdigest/test/CMakeLists.txt +1 -0
- data/vendor/datasketches-cpp/tdigest/test/tdigest_iterator_test.cpp +274 -0
- data/vendor/datasketches-cpp/tdigest/test/tdigest_test.cpp +275 -0
- data/vendor/datasketches-cpp/theta/include/compact_theta_sketch_parser.hpp +2 -0
- data/vendor/datasketches-cpp/theta/include/compact_theta_sketch_parser_impl.hpp +24 -3
- data/vendor/datasketches-cpp/theta/include/theta_constants.hpp +4 -2
- data/vendor/datasketches-cpp/theta/include/theta_helpers.hpp +32 -0
- data/vendor/datasketches-cpp/theta/include/theta_set_difference_base_impl.hpp +4 -2
- data/vendor/datasketches-cpp/theta/include/theta_sketch.hpp +22 -4
- data/vendor/datasketches-cpp/theta/include/theta_sketch_impl.hpp +60 -38
- data/vendor/datasketches-cpp/theta/include/theta_union_base_impl.hpp +2 -6
- data/vendor/datasketches-cpp/theta/include/theta_update_sketch_base_impl.hpp +2 -2
- data/vendor/datasketches-cpp/theta/test/bit_packing_test.cpp +50 -0
- data/vendor/datasketches-cpp/theta/test/theta_a_not_b_test.cpp +22 -0
- data/vendor/datasketches-cpp/theta/test/theta_sketch_test.cpp +315 -0
- data/vendor/datasketches-cpp/tools/rat-check.sh +68 -0
- data/vendor/datasketches-cpp/tuple/include/array_tuple_sketch.hpp +35 -4
- data/vendor/datasketches-cpp/tuple/include/array_tuple_sketch_impl.hpp +2 -2
- data/vendor/datasketches-cpp/tuple/include/tuple_sketch.hpp +41 -0
- data/vendor/datasketches-cpp/tuple/include/tuple_sketch_impl.hpp +5 -4
- data/vendor/datasketches-cpp/tuple/test/tuple_sketch_test.cpp +59 -0
- data/vendor/datasketches-cpp/version.cfg.in +1 -1
- metadata +12 -2
|
@@ -43,7 +43,16 @@ static const uint8_t FAMILY_ID = 7;
|
|
|
43
43
|
static const uint8_t EMPTY_FLAG_MASK = 4;
|
|
44
44
|
static const uint8_t COMPACT_FLAG_MASK = 8;
|
|
45
45
|
static const uint8_t OUT_OF_ORDER_FLAG_MASK = 16;
|
|
46
|
-
|
|
46
|
+
// Bit 32 is RESERVED and must not be written or interpreted.
|
|
47
|
+
// It was formerly FULL_SIZE_FLAG_MASK here, while datasketches-java uses the same bit as
|
|
48
|
+
// REBUILD_CURMIN_NUM_KXQ_MASK for its union gadget. The two meanings collided across
|
|
49
|
+
// implementations, so this side no longer writes or reads it. Do not reuse: bits 64 and 128
|
|
50
|
+
// are free.
|
|
51
|
+
static const uint8_t RESERVED_FLAG_MASK_32 = 32;
|
|
52
|
+
// Deprecated alias for the bit above, retained for source compatibility. Do not use.
|
|
53
|
+
// Added 2026-09-07 (#521), first shipping in the release after 5.2.0. No [[deprecated]]:
|
|
54
|
+
// this library targets C++11, so the alias warns no one.
|
|
55
|
+
static const uint8_t FULL_SIZE_FLAG_MASK = RESERVED_FLAG_MASK_32;
|
|
47
56
|
|
|
48
57
|
static const uint32_t PREAMBLE_INTS_BYTE = 0;
|
|
49
58
|
static const uint32_t SER_VER_BYTE = 1;
|
|
@@ -82,8 +91,8 @@ static const uint32_t EMPTY = 0;
|
|
|
82
91
|
static const uint8_t MIN_LOG_K = 4;
|
|
83
92
|
static const uint8_t MAX_LOG_K = 21;
|
|
84
93
|
|
|
85
|
-
static const double HLL_HIP_RSE_FACTOR = 0.
|
|
86
|
-
static const double HLL_NON_HIP_RSE_FACTOR = 1.
|
|
94
|
+
static const double HLL_HIP_RSE_FACTOR = 0.8325546111576977; // sqrt(ln(2))
|
|
95
|
+
static const double HLL_NON_HIP_RSE_FACTOR = 1.0389617614136892; // sqrt((3 * ln(2)) - 1)
|
|
87
96
|
static const double COUPON_RSE_FACTOR = 0.409; // at transition point not the asymptote
|
|
88
97
|
static const double COUPON_RSE = COUPON_RSE_FACTOR / (1 << 13);
|
|
89
98
|
|
|
@@ -28,7 +28,7 @@ template<typename A>
|
|
|
28
28
|
coupon_iterator<A>::coupon_iterator(const uint32_t* array, size_t array_size, size_t index, bool all):
|
|
29
29
|
array_(array), array_size_(array_size), index_(index), all_(all) {
|
|
30
30
|
while (index_ < array_size_) {
|
|
31
|
-
if (all_ || array_[index_] != hll_constants::EMPTY) break;
|
|
31
|
+
if (all_ || array_[index_] != hll_constants::EMPTY) { break; }
|
|
32
32
|
++index_;
|
|
33
33
|
}
|
|
34
34
|
}
|
|
@@ -36,7 +36,7 @@ array_(array), array_size_(array_size), index_(index), all_(all) {
|
|
|
36
36
|
template<typename A>
|
|
37
37
|
coupon_iterator<A>& coupon_iterator<A>::operator++() {
|
|
38
38
|
while (++index_ < array_size_) {
|
|
39
|
-
if (all_ || array_[index_] != hll_constants::EMPTY) break;
|
|
39
|
+
if (all_ || array_[index_] != hll_constants::EMPTY) { break; }
|
|
40
40
|
}
|
|
41
41
|
return *this;
|
|
42
42
|
}
|
|
@@ -120,7 +120,10 @@ class hll_sketch_alloc final {
|
|
|
120
120
|
* @param tgt_type The HLL mode to use, if/when the sketch reaches that state
|
|
121
121
|
* @param start_full_size Indicates whether to start in HLL mode,
|
|
122
122
|
* keeping memory use constant (if HLL_6 or HLL_8) at the cost of
|
|
123
|
-
* starting out using much more memory
|
|
123
|
+
* starting out using much more memory. This is a property of this
|
|
124
|
+
* constructor call only: it is not retained by the sketch and is not
|
|
125
|
+
* serialized, so reset() returns to coupon collection mode unless
|
|
126
|
+
* reset(true) is used.
|
|
124
127
|
* @param allocator instance of an Allocator
|
|
125
128
|
*/
|
|
126
129
|
explicit hll_sketch_alloc(uint8_t lg_config_k, target_hll_type tgt_type = HLL_4, bool start_full_size = false, const A& allocator = A());
|
|
@@ -160,7 +163,7 @@ class hll_sketch_alloc final {
|
|
|
160
163
|
static hll_sketch_alloc deserialize(const void* bytes, size_t len, const A& allocator = A());
|
|
161
164
|
|
|
162
165
|
//! Class destructor
|
|
163
|
-
|
|
166
|
+
~hll_sketch_alloc();
|
|
164
167
|
|
|
165
168
|
/**
|
|
166
169
|
* Copy assignment operator
|
|
@@ -177,10 +180,14 @@ class hll_sketch_alloc final {
|
|
|
177
180
|
hll_sketch_alloc& operator=(hll_sketch_alloc<A>&& other);
|
|
178
181
|
|
|
179
182
|
/**
|
|
180
|
-
* Resets the sketch to an empty state
|
|
183
|
+
* Resets the sketch to an empty state.
|
|
181
184
|
* Does not re-use existing internal objects.
|
|
185
|
+
* @param full_size if true, reset to an empty full-size HLL array, as
|
|
186
|
+
* the start_full_size constructor argument does; otherwise reset to
|
|
187
|
+
* coupon collection mode. Full size is not remembered across a reset
|
|
188
|
+
* or a serialization round trip, so it must be requested each time.
|
|
182
189
|
*/
|
|
183
|
-
void reset();
|
|
190
|
+
void reset(bool full_size = false);
|
|
184
191
|
|
|
185
192
|
// This is a convenience alias for users
|
|
186
193
|
// The type returned by the following serialize method
|
|
@@ -1,3 +1,22 @@
|
|
|
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
|
+
|
|
1
20
|
#ifndef _HLL_PRIVATE_HPP_
|
|
2
21
|
#define _HLL_PRIVATE_HPP_
|
|
3
22
|
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
#include <string>
|
|
25
25
|
#include <exception>
|
|
26
26
|
#include <stdexcept>
|
|
27
|
+
#include <vector>
|
|
27
28
|
|
|
28
29
|
#include "hll.hpp"
|
|
29
30
|
#include "CouponList.hpp"
|
|
@@ -180,4 +181,67 @@ TEST_CASE("coupon list: check corrupt stream data", "[coupon_list]") {
|
|
|
180
181
|
REQUIRE_THROWS_AS(hll_sketch::deserialize(ss), std::invalid_argument);
|
|
181
182
|
}
|
|
182
183
|
|
|
184
|
+
TEST_CASE("coupon list: rejects malformed coupon count in bytes", "[coupon_list]") {
|
|
185
|
+
// Reject declared LIST counts at or beyond the fixed LIST capacity.
|
|
186
|
+
uint8_t lgK = 8;
|
|
187
|
+
hll_sketch sk1(lgK);
|
|
188
|
+
sk1.update(1);
|
|
189
|
+
sk1.update(2);
|
|
190
|
+
const auto validBytes = sk1.serialize_compact();
|
|
191
|
+
|
|
192
|
+
const uint8_t listCapacity = static_cast<uint8_t>(1u << hll_constants::LG_INIT_LIST_SIZE);
|
|
193
|
+
const uint8_t malformedCount = listCapacity;
|
|
194
|
+
auto sketchBytes = validBytes;
|
|
195
|
+
// Keep the input long enough to validate the declared LIST count.
|
|
196
|
+
const size_t requiredLen = hll_constants::LIST_INT_ARR_START
|
|
197
|
+
+ malformedCount * sizeof(uint32_t);
|
|
198
|
+
if (sketchBytes.size() < requiredLen) {
|
|
199
|
+
sketchBytes.resize(requiredLen, 0);
|
|
200
|
+
}
|
|
201
|
+
sketchBytes[hll_constants::LIST_COUNT_BYTE] = malformedCount;
|
|
202
|
+
|
|
203
|
+
REQUIRE_THROWS_AS(
|
|
204
|
+
hll_sketch::deserialize(sketchBytes.data(), sketchBytes.size()),
|
|
205
|
+
std::invalid_argument);
|
|
206
|
+
REQUIRE_THROWS_AS(
|
|
207
|
+
CouponList<std::allocator<uint8_t>>::newList(
|
|
208
|
+
sketchBytes.data(), sketchBytes.size(), std::allocator<uint8_t>()),
|
|
209
|
+
std::invalid_argument);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
TEST_CASE("coupon list: rejects malformed coupon count in stream", "[coupon_list]") {
|
|
213
|
+
uint8_t lgK = 8;
|
|
214
|
+
hll_sketch sk1(lgK);
|
|
215
|
+
sk1.update(1);
|
|
216
|
+
sk1.update(2);
|
|
217
|
+
|
|
218
|
+
const uint8_t listCapacity = static_cast<uint8_t>(1u << hll_constants::LG_INIT_LIST_SIZE);
|
|
219
|
+
const uint8_t malformedCount = listCapacity;
|
|
220
|
+
std::stringstream ss(std::ios::in | std::ios::out | std::ios::binary);
|
|
221
|
+
sk1.serialize_compact(ss);
|
|
222
|
+
|
|
223
|
+
const size_t requiredLen = hll_constants::LIST_INT_ARR_START
|
|
224
|
+
+ malformedCount * sizeof(uint32_t);
|
|
225
|
+
// Keep the stream long enough to validate the declared LIST count.
|
|
226
|
+
ss.seekp(0, std::ios::end);
|
|
227
|
+
const auto endPos = static_cast<size_t>(ss.tellp());
|
|
228
|
+
if (endPos < requiredLen) {
|
|
229
|
+
std::vector<char> pad(requiredLen - endPos, 0);
|
|
230
|
+
ss.write(pad.data(), pad.size());
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
ss.seekp(hll_constants::LIST_COUNT_BYTE);
|
|
234
|
+
ss.put(static_cast<char>(malformedCount));
|
|
235
|
+
|
|
236
|
+
ss.clear();
|
|
237
|
+
ss.seekg(0);
|
|
238
|
+
REQUIRE_THROWS_AS(hll_sketch::deserialize(ss), std::invalid_argument);
|
|
239
|
+
|
|
240
|
+
ss.clear();
|
|
241
|
+
ss.seekg(0);
|
|
242
|
+
REQUIRE_THROWS_AS(
|
|
243
|
+
CouponList<std::allocator<uint8_t>>::newList(ss, std::allocator<uint8_t>()),
|
|
244
|
+
std::invalid_argument);
|
|
245
|
+
}
|
|
246
|
+
|
|
183
247
|
} /* namespace datasketches */
|
|
@@ -0,0 +1,137 @@
|
|
|
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
|
+
#include <catch2/catch.hpp>
|
|
21
|
+
|
|
22
|
+
#include "hll.hpp"
|
|
23
|
+
|
|
24
|
+
namespace datasketches {
|
|
25
|
+
|
|
26
|
+
// preamble byte 0 is preInts, which identifies the current mode
|
|
27
|
+
static const uint8_t LIST_PREINTS = 2;
|
|
28
|
+
static const uint8_t HLL_PREINTS = 10;
|
|
29
|
+
// preamble byte 5 is the flags byte; bit 32 is reserved and must never be written
|
|
30
|
+
static const size_t FLAGS_BYTE = 5;
|
|
31
|
+
static const uint8_t RESERVED_BIT_32 = 32;
|
|
32
|
+
|
|
33
|
+
static uint8_t preints_of(const hll_sketch& sk) {
|
|
34
|
+
return sk.serialize_compact()[0];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
static hll_sketch make(uint8_t lg_k, target_hll_type type, bool full_size, uint64_t n) {
|
|
38
|
+
hll_sketch sk(lg_k, type, full_size);
|
|
39
|
+
for (uint64_t i = 0; i < n; ++i) sk.update(i);
|
|
40
|
+
return sk;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
TEST_CASE("hll full size: reserved bit 32 is never written", "[hll_full_size]") {
|
|
44
|
+
for (auto type: {HLL_4, HLL_6, HLL_8}) {
|
|
45
|
+
for (bool full_size: {false, true}) {
|
|
46
|
+
for (uint64_t n: {uint64_t(0), uint64_t(5), uint64_t(5000)}) {
|
|
47
|
+
const hll_sketch sk = make(8, type, full_size, n);
|
|
48
|
+
REQUIRE((sk.serialize_compact()[FLAGS_BYTE] & RESERVED_BIT_32) == 0);
|
|
49
|
+
REQUIRE((sk.serialize_updatable()[FLAGS_BYTE] & RESERVED_BIT_32) == 0);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
TEST_CASE("hll full size: start_full_size starts in HLL mode", "[hll_full_size]") {
|
|
56
|
+
REQUIRE(preints_of(make(8, HLL_8, true, 0)) == HLL_PREINTS);
|
|
57
|
+
REQUIRE(preints_of(make(8, HLL_8, false, 0)) == LIST_PREINTS);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
TEST_CASE("hll full size: reset takes the mode as an argument", "[hll_full_size]") {
|
|
61
|
+
for (auto type: {HLL_4, HLL_6, HLL_8}) {
|
|
62
|
+
// full size is not remembered: a plain reset() returns to coupon collection mode
|
|
63
|
+
hll_sketch sk = make(8, type, true, 5000);
|
|
64
|
+
sk.reset();
|
|
65
|
+
REQUIRE(sk.is_empty());
|
|
66
|
+
REQUIRE(sk.get_estimate() == 0.0);
|
|
67
|
+
REQUIRE(preints_of(sk) == LIST_PREINTS);
|
|
68
|
+
|
|
69
|
+
// ...and must be asked for explicitly
|
|
70
|
+
hll_sketch sk2 = make(8, type, false, 5000);
|
|
71
|
+
sk2.reset(true);
|
|
72
|
+
REQUIRE(sk2.is_empty());
|
|
73
|
+
REQUIRE(sk2.get_estimate() == 0.0);
|
|
74
|
+
REQUIRE(preints_of(sk2) == HLL_PREINTS);
|
|
75
|
+
REQUIRE(sk2.get_lg_config_k() == 8);
|
|
76
|
+
REQUIRE(sk2.get_target_type() == type);
|
|
77
|
+
|
|
78
|
+
// reset(false) is the same as reset()
|
|
79
|
+
hll_sketch sk3 = make(8, type, true, 5000);
|
|
80
|
+
sk3.reset(false);
|
|
81
|
+
REQUIRE(preints_of(sk3) == LIST_PREINTS);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
TEST_CASE("hll full size: a reset sketch is usable again", "[hll_full_size]") {
|
|
86
|
+
hll_sketch sk = make(8, HLL_8, false, 100);
|
|
87
|
+
sk.reset(true);
|
|
88
|
+
for (uint64_t i = 0; i < 1000; ++i) sk.update(i);
|
|
89
|
+
REQUIRE_FALSE(sk.is_empty());
|
|
90
|
+
REQUIRE(sk.get_estimate() == Approx(1000).epsilon(0.2));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
TEST_CASE("hll full size: an image with the reserved bit set is read as if it were clear",
|
|
94
|
+
"[hll_full_size]") {
|
|
95
|
+
// an image produced by another implementation may have bit 32 set: datasketches-java uses it
|
|
96
|
+
// as its union rebuild flag. It must not change how this implementation reads the sketch.
|
|
97
|
+
for (auto type: {HLL_4, HLL_6, HLL_8}) {
|
|
98
|
+
const hll_sketch sk = make(8, type, false, 5000);
|
|
99
|
+
auto bytes = sk.serialize_updatable();
|
|
100
|
+
auto tampered = bytes;
|
|
101
|
+
tampered[FLAGS_BYTE] |= RESERVED_BIT_32;
|
|
102
|
+
|
|
103
|
+
const hll_sketch clean = hll_sketch::deserialize(bytes.data(), bytes.size());
|
|
104
|
+
hll_sketch tainted = hll_sketch::deserialize(tampered.data(), tampered.size());
|
|
105
|
+
|
|
106
|
+
REQUIRE(tainted.get_estimate() == clean.get_estimate());
|
|
107
|
+
REQUIRE(tainted.serialize_updatable() == bytes); // the bit is not propagated back out
|
|
108
|
+
tainted.reset();
|
|
109
|
+
REQUIRE(preints_of(tainted) == LIST_PREINTS); // and does not alter reset() behaviour
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
TEST_CASE("hll full size: a union never inherits full size from an input sketch",
|
|
114
|
+
"[hll_full_size]") {
|
|
115
|
+
// the gadget is an internal detail; unioning a full-size sketch must not change how the
|
|
116
|
+
// union resets, nor put the reserved bit into the union's result
|
|
117
|
+
const hll_sketch full = make(8, HLL_8, true, 5000);
|
|
118
|
+
|
|
119
|
+
hll_union u(8);
|
|
120
|
+
u.update(full);
|
|
121
|
+
const hll_sketch result = u.get_result(HLL_8);
|
|
122
|
+
REQUIRE((result.serialize_compact()[FLAGS_BYTE] & RESERVED_BIT_32) == 0);
|
|
123
|
+
REQUIRE((result.serialize_updatable()[FLAGS_BYTE] & RESERVED_BIT_32) == 0);
|
|
124
|
+
|
|
125
|
+
u.reset();
|
|
126
|
+
REQUIRE(u.is_empty());
|
|
127
|
+
REQUIRE(preints_of(u.get_result(HLL_8)) == LIST_PREINTS);
|
|
128
|
+
|
|
129
|
+
// same through the rvalue overload, which moves the sketch into the gadget
|
|
130
|
+
hll_union u2(8);
|
|
131
|
+
u2.update(make(8, HLL_8, true, 5000));
|
|
132
|
+
u2.reset();
|
|
133
|
+
REQUIRE(u2.is_empty());
|
|
134
|
+
REQUIRE(preints_of(u2.get_result(HLL_8)) == LIST_PREINTS);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
} /* namespace datasketches */
|
|
@@ -0,0 +1,150 @@
|
|
|
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
|
+
#include <catch2/catch.hpp>
|
|
21
|
+
#include <cmath>
|
|
22
|
+
#include <cstring>
|
|
23
|
+
#include <limits>
|
|
24
|
+
|
|
25
|
+
#include "hll.hpp"
|
|
26
|
+
#include "fdlibm_log.hpp"
|
|
27
|
+
|
|
28
|
+
namespace datasketches {
|
|
29
|
+
|
|
30
|
+
// offsets into the HLL updatable image
|
|
31
|
+
static const size_t CUR_MIN_BYTE = 6;
|
|
32
|
+
static const size_t NUM_AT_CUR_MIN_INT = 32;
|
|
33
|
+
static const size_t HLL_BYTE_ARR_START = 40;
|
|
34
|
+
|
|
35
|
+
static hll_sketch make(uint8_t lg_k, target_hll_type type, uint64_t lo, uint64_t hi) {
|
|
36
|
+
hll_sketch sk(lg_k, type);
|
|
37
|
+
for (uint64_t i = lo; i < hi; ++i) sk.update(i);
|
|
38
|
+
return sk;
|
|
39
|
+
}
|
|
40
|
+
static uint32_t num_at_cur_min_of(const hll_sketch::vector_bytes& img) {
|
|
41
|
+
uint32_t v; std::memcpy(&v, img.data() + NUM_AT_CUR_MIN_INT, sizeof(v)); return v;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
TEST_CASE("hll kxq rebuild: union result is merge-order independent", "[hll_kxq]") {
|
|
45
|
+
// C stays in SET mode, so this exercises the coupon-update path into a gadget with a
|
|
46
|
+
// deferred rebuild pending, which is where the stored curMin/numAtCurMin used to drift
|
|
47
|
+
const hll_sketch a = make(12, HLL_4, 20000, 30364);
|
|
48
|
+
const hll_sketch b = make(10, HLL_8, 5000, 14699);
|
|
49
|
+
const hll_sketch c = make(17, HLL_4, 70000, 72598);
|
|
50
|
+
const hll_sketch* in[3] = {&a, &b, &c};
|
|
51
|
+
|
|
52
|
+
auto run = [&](int i, int j, int k) {
|
|
53
|
+
hll_union u(7);
|
|
54
|
+
u.update(*in[i]); u.update(*in[j]); u.update(*in[k]);
|
|
55
|
+
return u.get_result(HLL_8).serialize_updatable();
|
|
56
|
+
};
|
|
57
|
+
const auto ref = run(0, 1, 2);
|
|
58
|
+
REQUIRE(run(0, 2, 1) == ref);
|
|
59
|
+
REQUIRE(run(1, 0, 2) == ref);
|
|
60
|
+
REQUIRE(run(1, 2, 0) == ref);
|
|
61
|
+
REQUIRE(run(2, 0, 1) == ref);
|
|
62
|
+
REQUIRE(run(2, 1, 0) == ref);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
TEST_CASE("hll kxq rebuild: reading an estimate does not change a later result",
|
|
66
|
+
"[hll_kxq]") {
|
|
67
|
+
// the rebuild is lazy; when it fires must not be observable in the serialized image
|
|
68
|
+
const hll_sketch p = make(13, HLL_8, 0, 50000);
|
|
69
|
+
const hll_sketch q = make(13, HLL_8, 50000, 100000);
|
|
70
|
+
|
|
71
|
+
for (uint8_t lg_max_k: {uint8_t(7), uint8_t(8), uint8_t(9)}) {
|
|
72
|
+
hll_union peeked(lg_max_k);
|
|
73
|
+
peeked.update(p); peeked.update(q);
|
|
74
|
+
(void) peeked.get_estimate(); // forces the rebuild here
|
|
75
|
+
for (uint64_t v = 9000000; v < 9400000; ++v) peeked.update(v);
|
|
76
|
+
|
|
77
|
+
hll_union plain(lg_max_k);
|
|
78
|
+
plain.update(p); plain.update(q);
|
|
79
|
+
for (uint64_t v = 9000000; v < 9400000; ++v) plain.update(v);
|
|
80
|
+
|
|
81
|
+
REQUIRE(peeked.get_result(HLL_8).serialize_updatable()
|
|
82
|
+
== plain.get_result(HLL_8).serialize_updatable());
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
TEST_CASE("hll kxq rebuild: stored curMin and numAtCurMin agree with the registers",
|
|
87
|
+
"[hll_kxq]") {
|
|
88
|
+
// HLL_8 convention: curMin is always 0 and numAtCurMin counts the zero registers
|
|
89
|
+
const hll_sketch a = make(15, HLL_8, 0, 100000);
|
|
90
|
+
const hll_sketch b = make(8, HLL_8, 100000, 200000);
|
|
91
|
+
hll_union u(8);
|
|
92
|
+
u.update(a); u.update(b);
|
|
93
|
+
const auto img = u.get_result(HLL_8).serialize_updatable();
|
|
94
|
+
|
|
95
|
+
uint32_t zeros = 0;
|
|
96
|
+
for (size_t i = HLL_BYTE_ARR_START; i < img.size(); ++i) if (img[i] == 0) ++zeros;
|
|
97
|
+
|
|
98
|
+
REQUIRE(img[CUR_MIN_BYTE] == 0);
|
|
99
|
+
REQUIRE(num_at_cur_min_of(img) == zeros);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
TEST_CASE("hll kxq rebuild: relative error constants are full precision", "[hll_kxq]") {
|
|
103
|
+
// lg_k > 12 uses the closed form rather than the interpolation table, so a constant
|
|
104
|
+
// truncated to seven digits is directly observable in the bounds
|
|
105
|
+
const double hip = std::sqrt(std::log(2.0)); // sqrt(ln 2)
|
|
106
|
+
const double non_hip = std::sqrt((3.0 * std::log(2.0)) - 1.0); // sqrt(3 ln 2 - 1)
|
|
107
|
+
|
|
108
|
+
for (uint8_t lg_k: {uint8_t(13), uint8_t(16), uint8_t(21)}) {
|
|
109
|
+
const double k = static_cast<double>(1 << lg_k);
|
|
110
|
+
for (uint8_t sd = 1; sd <= 3; ++sd) {
|
|
111
|
+
const double got_hip = hll_union::get_rel_err(false, false, lg_k, sd);
|
|
112
|
+
const double got_non = hll_union::get_rel_err(false, true, lg_k, sd);
|
|
113
|
+
REQUIRE(got_hip == Approx(sd * hip / std::sqrt(k)).epsilon(1e-15));
|
|
114
|
+
REQUIRE(got_non == Approx(sd * non_hip / std::sqrt(k)).epsilon(1e-15));
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
TEST_CASE("hll kxq rebuild: log matches the fdlibm reference", "[hll_kxq]") {
|
|
120
|
+
// the linear counting estimator subtracts two nearby harmonic numbers, which amplifies a
|
|
121
|
+
// 1 ULP difference in log() by more than an order of magnitude. Pin log() to fdlibm, the
|
|
122
|
+
// function datasketches-java's StrictMath.log is specified to be. These expected values
|
|
123
|
+
// were taken from Java; the platform libm differs from every one of them.
|
|
124
|
+
struct { int x; uint64_t bits; } expected[] = {
|
|
125
|
+
{ 48, 0x400ef8383c50bb74ULL},
|
|
126
|
+
{ 74, 0x4011375cd6fcab1cULL},
|
|
127
|
+
{ 185, 0x4014e1a4f518c72cULL},
|
|
128
|
+
{ 196, 0x40151cca16d7bba8ULL},
|
|
129
|
+
{ 299, 0x4016cd411481a020ULL},
|
|
130
|
+
{ 308, 0x4016eb9f470ac0b8ULL},
|
|
131
|
+
{ 334, 0x40173e9bbe951e9cULL},
|
|
132
|
+
{ 343, 0x401759d602a5c3c2ULL},
|
|
133
|
+
{ 1261, 0x401c8f031e7e1220ULL},
|
|
134
|
+
};
|
|
135
|
+
for (const auto& e: expected) {
|
|
136
|
+
const double got = fdlibm::log(static_cast<double>(e.x));
|
|
137
|
+
uint64_t bits; std::memcpy(&bits, &got, sizeof(bits));
|
|
138
|
+
REQUIRE(bits == e.bits);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// edge cases: fdlibm produces these by dividing by a zero constant, which MSVC rejects at
|
|
142
|
+
// compile time, so they are returned directly. Pin the values.
|
|
143
|
+
REQUIRE(fdlibm::log(0.0) == -std::numeric_limits<double>::infinity());
|
|
144
|
+
REQUIRE(fdlibm::log(-0.0) == -std::numeric_limits<double>::infinity());
|
|
145
|
+
REQUIRE(std::isnan(fdlibm::log(-1.0)));
|
|
146
|
+
REQUIRE(fdlibm::log(1.0) == 0.0);
|
|
147
|
+
REQUIRE(fdlibm::log(5e-320) == Approx(-735.2178).epsilon(1e-6)); // subnormal scaling path
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
} /* namespace datasketches */
|
|
@@ -298,7 +298,7 @@ TEST_CASE("hll sketch: deserialize list mode buffer overrun", "[hll_sketch]") {
|
|
|
298
298
|
REQUIRE_THROWS_AS(hll_sketch_test_alloc::deserialize(bytes.data(), 7, 0), std::out_of_range);
|
|
299
299
|
REQUIRE_THROWS_AS(hll_sketch_test_alloc::deserialize(bytes.data(), bytes.size() - 1, 0), std::out_of_range);
|
|
300
300
|
|
|
301
|
-
//
|
|
301
|
+
// check for leaks on stream exceptions
|
|
302
302
|
{
|
|
303
303
|
std::stringstream ss;
|
|
304
304
|
ss.exceptions(std::ios::failbit | std::ios::badbit);
|
|
@@ -325,7 +325,7 @@ TEST_CASE("hll sketch: deserialize set mode buffer overrun", "[hll_sketch]") {
|
|
|
325
325
|
REQUIRE_THROWS_AS(hll_sketch_test_alloc::deserialize(bytes.data(), 7, 0), std::out_of_range);
|
|
326
326
|
REQUIRE_THROWS_AS(hll_sketch_test_alloc::deserialize(bytes.data(), bytes.size() - 1, 0), std::out_of_range);
|
|
327
327
|
|
|
328
|
-
//
|
|
328
|
+
// check for leaks on stream exceptions
|
|
329
329
|
{
|
|
330
330
|
std::stringstream ss;
|
|
331
331
|
ss.exceptions(std::ios::failbit | std::ios::badbit);
|
|
@@ -355,7 +355,7 @@ TEST_CASE("hll sketch: deserialize HLL mode buffer overrun", "[hll_sketch]") {
|
|
|
355
355
|
REQUIRE_THROWS_AS(hll_sketch_test_alloc::deserialize(bytes.data(), 16420, 0), std::out_of_range); // before aux table
|
|
356
356
|
REQUIRE_THROWS_AS(hll_sketch_test_alloc::deserialize(bytes.data(), bytes.size() - 1, 0), std::out_of_range);
|
|
357
357
|
|
|
358
|
-
//
|
|
358
|
+
// check for leaks on stream exceptions
|
|
359
359
|
{
|
|
360
360
|
std::stringstream ss;
|
|
361
361
|
ss.exceptions(std::ios::failbit | std::ios::badbit);
|
|
@@ -58,7 +58,7 @@ static void basicUnion(uint64_t n1, uint64_t n2,
|
|
|
58
58
|
|
|
59
59
|
hll_sketch result = u.get_result(resultType);
|
|
60
60
|
|
|
61
|
-
// ensure we check a direct union estimate, without first
|
|
61
|
+
// ensure we check a direct union estimate, without first calling get_result()
|
|
62
62
|
u.reset();
|
|
63
63
|
u.update(std::move(h1));
|
|
64
64
|
u.update(h2);
|
|
@@ -314,4 +314,74 @@ TEST_CASE("hll union: check hll to hll", "[hll_union]") {
|
|
|
314
314
|
union_two_sketches_with_overlap(1000000, 11, HLL_4);
|
|
315
315
|
}
|
|
316
316
|
|
|
317
|
+
static hll_sketch make_hll_sketch(uint8_t lg_config_k, uint64_t start, uint64_t end) {
|
|
318
|
+
hll_sketch sketch(lg_config_k);
|
|
319
|
+
for (uint64_t i = start; i < end; ++i) sketch.update(i);
|
|
320
|
+
return sketch;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
static hll_sketch::vector_bytes serialize_flat_union(
|
|
324
|
+
const hll_sketch& first, const hll_sketch& second, const hll_sketch& third) {
|
|
325
|
+
hll_union u(8);
|
|
326
|
+
u.update(first);
|
|
327
|
+
u.update(second);
|
|
328
|
+
u.update(third);
|
|
329
|
+
return u.get_result(HLL_8).serialize_updatable();
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
static hll_sketch::vector_bytes serialize_nested_union(
|
|
333
|
+
const hll_sketch& first, const hll_sketch& second, const hll_sketch& third) {
|
|
334
|
+
hll_union prefix(8);
|
|
335
|
+
prefix.update(first);
|
|
336
|
+
prefix.update(second);
|
|
337
|
+
|
|
338
|
+
hll_union u(8);
|
|
339
|
+
u.update(prefix.get_result(HLL_8));
|
|
340
|
+
u.update(third);
|
|
341
|
+
return u.get_result(HLL_8).serialize_updatable();
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
TEST_CASE("hll union: downsampling merge is not empty", "[hll_union]") {
|
|
345
|
+
const hll_sketch sketch = make_hll_sketch(15, 0, 100000);
|
|
346
|
+
hll_union u(8);
|
|
347
|
+
u.update(sketch);
|
|
348
|
+
REQUIRE_FALSE(u.is_empty());
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
TEST_CASE("hll union: mixed lgConfigK estimate is merge-order independent", "[hll_union]") {
|
|
352
|
+
const uint64_t n = 100000;
|
|
353
|
+
const hll_sketch a = make_hll_sketch(15, 0, n);
|
|
354
|
+
const hll_sketch b = make_hll_sketch(8, n, 2 * n);
|
|
355
|
+
const double truth = 2.0 * n;
|
|
356
|
+
|
|
357
|
+
hll_union larger_first(8);
|
|
358
|
+
larger_first.update(a);
|
|
359
|
+
larger_first.update(b);
|
|
360
|
+
REQUIRE(larger_first.get_estimate() == Approx(truth).epsilon(0.1));
|
|
361
|
+
|
|
362
|
+
hll_union smaller_first(8);
|
|
363
|
+
smaller_first.update(b);
|
|
364
|
+
smaller_first.update(a);
|
|
365
|
+
REQUIRE(smaller_first.get_estimate() == Approx(truth).epsilon(0.1));
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
TEST_CASE("hll union: scalar update after downsampling merge", "[hll_union]") {
|
|
369
|
+
const uint64_t n = 100000;
|
|
370
|
+
const hll_sketch sketch = make_hll_sketch(15, 0, n);
|
|
371
|
+
hll_union u(8);
|
|
372
|
+
u.update(sketch);
|
|
373
|
+
for (uint64_t i = n; i < 2 * n; ++i) u.update(i);
|
|
374
|
+
REQUIRE(u.get_estimate() == Approx(2.0 * n).epsilon(0.1));
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
TEST_CASE("hll union: serialization is grouping independent", "[hll_union]") {
|
|
378
|
+
const uint64_t n = 100000;
|
|
379
|
+
const hll_sketch a = make_hll_sketch(15, 0, n);
|
|
380
|
+
const hll_sketch b = make_hll_sketch(8, n, 2 * n);
|
|
381
|
+
const hll_sketch c = make_hll_sketch(11, n / 2, n + n / 2);
|
|
382
|
+
REQUIRE(serialize_nested_union(b, c, a) == serialize_flat_union(b, c, a));
|
|
383
|
+
REQUIRE(serialize_nested_union(a, c, b) == serialize_flat_union(a, c, b));
|
|
384
|
+
REQUIRE(serialize_nested_union(a, b, c) == serialize_flat_union(a, b, c));
|
|
385
|
+
}
|
|
386
|
+
|
|
317
387
|
} /* namespace datasketches */
|
|
@@ -36,17 +36,17 @@ bool kll_helper::is_odd(uint32_t value) {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
uint8_t kll_helper::floor_of_log2_of_fraction(uint64_t numer, uint64_t denom) {
|
|
39
|
-
if (denom > numer) return 0;
|
|
39
|
+
if (denom > numer) { return 0; }
|
|
40
40
|
uint8_t count = 0;
|
|
41
41
|
while (true) {
|
|
42
42
|
denom <<= 1;
|
|
43
|
-
if (denom > numer) return count;
|
|
43
|
+
if (denom > numer) { return count; }
|
|
44
44
|
count++;
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
uint8_t kll_helper::ub_on_num_levels(uint64_t n) {
|
|
49
|
-
if (n == 0) return 1;
|
|
49
|
+
if (n == 0) { return 1; }
|
|
50
50
|
return 1 + floor_of_log2_of_fraction(n, 1);
|
|
51
51
|
}
|
|
52
52
|
|
|
@@ -65,8 +65,8 @@ uint16_t kll_helper::level_capacity(uint16_t k, uint8_t numLevels, uint8_t heigh
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
uint16_t kll_helper::int_cap_aux(uint16_t k, uint8_t depth) {
|
|
68
|
-
if (depth > 60) throw std::invalid_argument("depth > 60");
|
|
69
|
-
if (depth <= 30) return int_cap_aux_aux(k, depth);
|
|
68
|
+
if (depth > 60) { throw std::invalid_argument("depth > 60"); }
|
|
69
|
+
if (depth <= 30) { return int_cap_aux_aux(k, depth); }
|
|
70
70
|
const uint8_t half = depth / 2;
|
|
71
71
|
const uint8_t rest = depth - half;
|
|
72
72
|
const uint16_t tmp = int_cap_aux_aux(k, half);
|
|
@@ -74,11 +74,11 @@ uint16_t kll_helper::int_cap_aux(uint16_t k, uint8_t depth) {
|
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
uint16_t kll_helper::int_cap_aux_aux(uint16_t k, uint8_t depth) {
|
|
77
|
-
if (depth > 30) throw std::invalid_argument("depth > 30");
|
|
77
|
+
if (depth > 30) { throw std::invalid_argument("depth > 30"); }
|
|
78
78
|
const uint64_t twok = k << 1; // for rounding, we pre-multiply by 2
|
|
79
79
|
const uint64_t tmp = (uint64_t) (((uint64_t) twok << depth) / powers_of_three[depth]);
|
|
80
80
|
const uint64_t result = (tmp + 1) >> 1; // then here we add 1 and divide by 2
|
|
81
|
-
if (result > k) throw std::logic_error("result > k");
|
|
81
|
+
if (result > k) { throw std::logic_error("result > k"); }
|
|
82
82
|
return static_cast<uint16_t>(result);
|
|
83
83
|
}
|
|
84
84
|
|
|
@@ -94,7 +94,7 @@ uint64_t kll_helper::sum_the_sample_weights(uint8_t num_levels, const uint32_t*
|
|
|
94
94
|
|
|
95
95
|
template <typename T>
|
|
96
96
|
void kll_helper::randomly_halve_down(T* buf, uint32_t start, uint32_t length) {
|
|
97
|
-
if (!is_even(length)) throw std::invalid_argument("length must be even");
|
|
97
|
+
if (!is_even(length)) { throw std::invalid_argument("length must be even"); }
|
|
98
98
|
const uint32_t half_length = length / 2;
|
|
99
99
|
#ifdef KLL_VALIDATION
|
|
100
100
|
const uint32_t offset = deterministic_offset();
|
|
@@ -110,7 +110,7 @@ void kll_helper::randomly_halve_down(T* buf, uint32_t start, uint32_t length) {
|
|
|
110
110
|
|
|
111
111
|
template <typename T>
|
|
112
112
|
void kll_helper::randomly_halve_up(T* buf, uint32_t start, uint32_t length) {
|
|
113
|
-
if (!is_even(length)) throw std::invalid_argument("length must be even");
|
|
113
|
+
if (!is_even(length)) { throw std::invalid_argument("length must be even"); }
|
|
114
114
|
const uint32_t half_length = length / 2;
|
|
115
115
|
#ifdef KLL_VALIDATION
|
|
116
116
|
const uint32_t offset = deterministic_offset();
|
|
@@ -206,7 +206,7 @@ template <typename T, typename C>
|
|
|
206
206
|
kll_helper::compress_result kll_helper::general_compress(uint16_t k, uint8_t m, uint8_t num_levels_in, T* items,
|
|
207
207
|
uint32_t* in_levels, uint32_t* out_levels, bool is_level_zero_sorted)
|
|
208
208
|
{
|
|
209
|
-
if (num_levels_in == 0) throw std::invalid_argument("num_levels_in == 0"); // things are too weird if zero levels are allowed
|
|
209
|
+
if (num_levels_in == 0) { throw std::invalid_argument("num_levels_in == 0"); } // things are too weird if zero levels are allowed
|
|
210
210
|
const uint32_t starting_item_count = in_levels[num_levels_in] - in_levels[0];
|
|
211
211
|
uint8_t current_num_levels = num_levels_in;
|
|
212
212
|
uint32_t current_item_count = starting_item_count; // decreases with each compaction
|