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.
Files changed (115) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -0
  3. data/ext/datasketches/theta_wrapper.cpp +1 -1
  4. data/lib/datasketches/version.rb +1 -1
  5. data/vendor/datasketches-cpp/CMakeLists.txt +5 -3
  6. data/vendor/datasketches-cpp/CODE_OF_CONDUCT.md +1 -1
  7. data/vendor/datasketches-cpp/LICENSE +14 -0
  8. data/vendor/datasketches-cpp/README.md +67 -73
  9. data/vendor/datasketches-cpp/benchmarks/CMakeLists.txt +52 -0
  10. data/vendor/datasketches-cpp/benchmarks/benchmark_count_min_sketch.cpp +153 -0
  11. data/vendor/datasketches-cpp/benchmarks/benchmark_count_min_sketch_serialization.cpp +161 -0
  12. data/vendor/datasketches-cpp/common/CMakeLists.txt +1 -0
  13. data/vendor/datasketches-cpp/common/include/binomial_bounds.hpp +2 -2
  14. data/vendor/datasketches-cpp/common/include/fdlibm_log.hpp +101 -0
  15. data/vendor/datasketches-cpp/common/include/serde.hpp +6 -0
  16. data/vendor/datasketches-cpp/common/test/CMakeLists.txt +44 -2
  17. data/vendor/datasketches-cpp/common/test/binomial_bounds_test.cpp +279 -0
  18. data/vendor/datasketches-cpp/common/test/deserialize_hardening_test.cpp +188 -0
  19. data/vendor/datasketches-cpp/count/include/count_min.hpp +17 -4
  20. data/vendor/datasketches-cpp/count/include/count_min_impl.hpp +65 -83
  21. data/vendor/datasketches-cpp/count/test/count_min_test.cpp +63 -7
  22. data/vendor/datasketches-cpp/cpc/include/compression_data.hpp +2 -0
  23. data/vendor/datasketches-cpp/cpc/include/cpc_compressor_impl.hpp +16 -7
  24. data/vendor/datasketches-cpp/cpc/include/cpc_sketch.hpp +1 -0
  25. data/vendor/datasketches-cpp/cpc/include/cpc_sketch_impl.hpp +54 -36
  26. data/vendor/datasketches-cpp/cpc/include/cpc_union_impl.hpp +27 -27
  27. data/vendor/datasketches-cpp/cpc/include/cpc_util.hpp +7 -7
  28. data/vendor/datasketches-cpp/cpc/include/icon_estimator.hpp +5 -5
  29. data/vendor/datasketches-cpp/cpc/include/u32_table_impl.hpp +16 -16
  30. data/vendor/datasketches-cpp/cpc/test/cpc_sketch_test.cpp +35 -0
  31. data/vendor/datasketches-cpp/fi/include/frequent_items_sketch.hpp +28 -3
  32. data/vendor/datasketches-cpp/fi/include/frequent_items_sketch_impl.hpp +41 -25
  33. data/vendor/datasketches-cpp/fi/include/reverse_purge_hash_map.hpp +3 -1
  34. data/vendor/datasketches-cpp/fi/include/reverse_purge_hash_map_impl.hpp +10 -5
  35. data/vendor/datasketches-cpp/fi/test/frequent_items_sketch_serialize_for_java.cpp +24 -0
  36. data/vendor/datasketches-cpp/fi/test/frequent_items_sketch_test.cpp +116 -0
  37. data/vendor/datasketches-cpp/filters/include/bloom_filter.hpp +1 -1
  38. data/vendor/datasketches-cpp/filters/include/bloom_filter_impl.hpp +32 -12
  39. data/vendor/datasketches-cpp/filters/test/bloom_filter_test.cpp +28 -1
  40. data/vendor/datasketches-cpp/hll/include/CouponHashSet-internal.hpp +1 -2
  41. data/vendor/datasketches-cpp/hll/include/CouponList-internal.hpp +19 -5
  42. data/vendor/datasketches-cpp/hll/include/CubicInterpolation-internal.hpp +4 -4
  43. data/vendor/datasketches-cpp/hll/include/HarmonicNumbers-internal.hpp +2 -1
  44. data/vendor/datasketches-cpp/hll/include/Hll4Array-internal.hpp +4 -4
  45. data/vendor/datasketches-cpp/hll/include/Hll4Array.hpp +1 -1
  46. data/vendor/datasketches-cpp/hll/include/Hll6Array-internal.hpp +3 -3
  47. data/vendor/datasketches-cpp/hll/include/Hll6Array.hpp +1 -1
  48. data/vendor/datasketches-cpp/hll/include/Hll8Array-internal.hpp +6 -3
  49. data/vendor/datasketches-cpp/hll/include/Hll8Array.hpp +1 -1
  50. data/vendor/datasketches-cpp/hll/include/HllArray-internal.hpp +35 -29
  51. data/vendor/datasketches-cpp/hll/include/HllArray.hpp +1 -1
  52. data/vendor/datasketches-cpp/hll/include/HllSketch-internal.hpp +3 -5
  53. data/vendor/datasketches-cpp/hll/include/HllSketchImpl-internal.hpp +5 -11
  54. data/vendor/datasketches-cpp/hll/include/HllSketchImpl.hpp +2 -4
  55. data/vendor/datasketches-cpp/hll/include/HllSketchImplFactory.hpp +11 -10
  56. data/vendor/datasketches-cpp/hll/include/HllUnion-internal.hpp +17 -8
  57. data/vendor/datasketches-cpp/hll/include/HllUtil.hpp +12 -3
  58. data/vendor/datasketches-cpp/hll/include/coupon_iterator-internal.hpp +2 -2
  59. data/vendor/datasketches-cpp/hll/include/coupon_iterator.hpp +3 -0
  60. data/vendor/datasketches-cpp/hll/include/hll.hpp +11 -4
  61. data/vendor/datasketches-cpp/hll/include/hll.private.hpp +19 -0
  62. data/vendor/datasketches-cpp/hll/test/CMakeLists.txt +2 -0
  63. data/vendor/datasketches-cpp/hll/test/CouponListTest.cpp +64 -0
  64. data/vendor/datasketches-cpp/hll/test/HllFullSizeTest.cpp +137 -0
  65. data/vendor/datasketches-cpp/hll/test/HllKxqRebuildTest.cpp +150 -0
  66. data/vendor/datasketches-cpp/hll/test/HllSketchTest.cpp +3 -3
  67. data/vendor/datasketches-cpp/hll/test/HllUnionTest.cpp +71 -1
  68. data/vendor/datasketches-cpp/kll/include/kll_helper_impl.hpp +10 -10
  69. data/vendor/datasketches-cpp/kll/include/kll_sketch.hpp +10 -1
  70. data/vendor/datasketches-cpp/kll/include/kll_sketch_impl.hpp +33 -24
  71. data/vendor/datasketches-cpp/kll/test/kll_sketch_deserialize_from_java_test.cpp +24 -0
  72. data/vendor/datasketches-cpp/kll/test/kll_sketch_serialize_for_java.cpp +10 -0
  73. data/vendor/datasketches-cpp/quantiles/include/quantiles_sketch.hpp +11 -2
  74. data/vendor/datasketches-cpp/quantiles/include/quantiles_sketch_impl.hpp +24 -15
  75. data/vendor/datasketches-cpp/req/include/req_sketch.hpp +9 -0
  76. data/vendor/datasketches-cpp/req/include/req_sketch_impl.hpp +24 -15
  77. data/vendor/datasketches-cpp/req/test/req_sketch_deserialize_from_java_test.cpp +46 -0
  78. data/vendor/datasketches-cpp/req/test/req_sketch_serialize_for_java.cpp +20 -0
  79. data/vendor/datasketches-cpp/req/test/req_sketch_test.cpp +70 -0
  80. data/vendor/datasketches-cpp/sampling/include/ebpps_sample_impl.hpp +17 -8
  81. data/vendor/datasketches-cpp/sampling/include/ebpps_sketch.hpp +13 -0
  82. data/vendor/datasketches-cpp/sampling/include/var_opt_sketch.hpp +10 -1
  83. data/vendor/datasketches-cpp/sampling/include/var_opt_sketch_impl.hpp +6 -7
  84. data/vendor/datasketches-cpp/sampling/include/var_opt_union.hpp +5 -1
  85. data/vendor/datasketches-cpp/sampling/include/var_opt_union_impl.hpp +2 -1
  86. data/vendor/datasketches-cpp/sampling/test/ebpps_allocation_test.cpp +1 -1
  87. data/vendor/datasketches-cpp/sampling/test/ebpps_sketch_test.cpp +2 -2
  88. data/vendor/datasketches-cpp/sampling/test/var_opt_allocation_test.cpp +1 -1
  89. data/vendor/datasketches-cpp/sampling/test/var_opt_sketch_test.cpp +10 -4
  90. data/vendor/datasketches-cpp/sampling/test/var_opt_union_test.cpp +12 -0
  91. data/vendor/datasketches-cpp/tdigest/include/tdigest.hpp +38 -2
  92. data/vendor/datasketches-cpp/tdigest/include/tdigest_impl.hpp +168 -9
  93. data/vendor/datasketches-cpp/tdigest/test/CMakeLists.txt +1 -0
  94. data/vendor/datasketches-cpp/tdigest/test/tdigest_iterator_test.cpp +274 -0
  95. data/vendor/datasketches-cpp/tdigest/test/tdigest_test.cpp +275 -0
  96. data/vendor/datasketches-cpp/theta/include/compact_theta_sketch_parser.hpp +2 -0
  97. data/vendor/datasketches-cpp/theta/include/compact_theta_sketch_parser_impl.hpp +24 -3
  98. data/vendor/datasketches-cpp/theta/include/theta_constants.hpp +4 -2
  99. data/vendor/datasketches-cpp/theta/include/theta_helpers.hpp +32 -0
  100. data/vendor/datasketches-cpp/theta/include/theta_set_difference_base_impl.hpp +4 -2
  101. data/vendor/datasketches-cpp/theta/include/theta_sketch.hpp +22 -4
  102. data/vendor/datasketches-cpp/theta/include/theta_sketch_impl.hpp +60 -38
  103. data/vendor/datasketches-cpp/theta/include/theta_union_base_impl.hpp +2 -6
  104. data/vendor/datasketches-cpp/theta/include/theta_update_sketch_base_impl.hpp +2 -2
  105. data/vendor/datasketches-cpp/theta/test/bit_packing_test.cpp +50 -0
  106. data/vendor/datasketches-cpp/theta/test/theta_a_not_b_test.cpp +22 -0
  107. data/vendor/datasketches-cpp/theta/test/theta_sketch_test.cpp +315 -0
  108. data/vendor/datasketches-cpp/tools/rat-check.sh +68 -0
  109. data/vendor/datasketches-cpp/tuple/include/array_tuple_sketch.hpp +35 -4
  110. data/vendor/datasketches-cpp/tuple/include/array_tuple_sketch_impl.hpp +2 -2
  111. data/vendor/datasketches-cpp/tuple/include/tuple_sketch.hpp +41 -0
  112. data/vendor/datasketches-cpp/tuple/include/tuple_sketch_impl.hpp +5 -4
  113. data/vendor/datasketches-cpp/tuple/test/tuple_sketch_test.cpp +59 -0
  114. data/vendor/datasketches-cpp/version.cfg.in +1 -1
  115. metadata +12 -2
@@ -0,0 +1,101 @@
1
+ // fdlibm __ieee754_log, used by Java's StrictMath.log (and by Math.log on most JVMs).
2
+ // Derived from FDLIBM 5.3, Copyright (C) 1993 by Sun Microsystems, Inc.
3
+ // "Permission to use, copy, modify, and distribute this software is freely granted,
4
+ // provided that this notice is preserved."
5
+ #ifndef FDLIBM_LOG_HPP
6
+ #define FDLIBM_LOG_HPP
7
+ #include <cstdint>
8
+ #include <cstring>
9
+ #include <limits>
10
+
11
+ namespace fdlibm {
12
+
13
+ inline int32_t hi_word(double x){ uint64_t u; std::memcpy(&u,&x,8); return (int32_t)(uint32_t)(u>>32); }
14
+ inline uint32_t lo_word(double x){ uint64_t u; std::memcpy(&u,&x,8); return (uint32_t)u; }
15
+ inline void set_hi_word(double& x, uint32_t hi){ uint64_t u; std::memcpy(&u,&x,8);
16
+ u = (u & 0x00000000ffffffffULL) | ((uint64_t)hi<<32); std::memcpy(&x,&u,8); }
17
+
18
+ // Forces a value to be rounded to a double before it is used again. fdlibm needs strict
19
+ // IEEE-754 evaluation: a fused multiply-add anywhere in the polynomial below changes the
20
+ // result. Pragmas are overridden by an explicit -ffp-contract=fast, so use a volatile
21
+ // round-trip, which the standard requires the compiler to honour.
22
+ inline double rnd(double v) { volatile double t = v; return t; }
23
+
24
+ inline double log(double x) {
25
+ // fdlibm depends on strict IEEE-754 evaluation: a fused multiply-add would change the result
26
+ // of the polynomial evaluation below, so contraction must be off for this function.
27
+ #if defined(__clang__)
28
+ #pragma clang fp contract(off)
29
+ #endif
30
+ static const double
31
+ ln2_hi = 6.93147180369123816490e-01, /* 3fe62e42 fee00000 */
32
+ ln2_lo = 1.90821492927058770002e-10, /* 3dea39ef 35793c76 */
33
+ two54 = 1.80143985094819840000e+16, /* 43500000 00000000 */
34
+ Lg1 = 6.666666666666735130e-01, /* 3FE55555 55555593 */
35
+ Lg2 = 3.999999999940941908e-01, /* 3FD99999 9997FA04 */
36
+ Lg3 = 2.857142874366239149e-01, /* 3FD24924 94229359 */
37
+ Lg4 = 2.222219843214978396e-01, /* 3FCC71C5 1D8E78AF */
38
+ Lg5 = 1.818357216161805012e-01, /* 3FC74664 96CB03DE */
39
+ Lg6 = 1.531383769920937332e-01, /* 3FC39A09 D078C69F */
40
+ Lg7 = 1.479819860511658591e-01, /* 3FC2F112 DF3E5244 */
41
+ zero = 0.0;
42
+
43
+ double hfsq,f,s,z,R,w,t1,t2,dk;
44
+ int32_t k,hx,i,j;
45
+ uint32_t lx;
46
+
47
+ hx = hi_word(x);
48
+ lx = lo_word(x);
49
+
50
+ k = 0;
51
+ if (hx < 0x00100000) { /* x < 2**-1022 */
52
+ // fdlibm writes these as -two54/zero and (x-x)/zero, which also raise the divide-by-zero
53
+ // and invalid flags. MSVC rejects a compile-time division by a zero constant (C2124), so
54
+ // return the same values directly. The estimators never call log() with these inputs.
55
+ if (((hx & 0x7fffffff) | lx) == 0) { /* log(+-0) = -inf */
56
+ return -std::numeric_limits<double>::infinity();
57
+ }
58
+ if (hx < 0) { /* log(-#) = NaN */
59
+ return std::numeric_limits<double>::quiet_NaN();
60
+ }
61
+ k -= 54; x *= two54; /* subnormal: scale up */
62
+ hx = hi_word(x);
63
+ }
64
+ if (hx >= 0x7ff00000) { return x+x; }
65
+ k += (hx>>20) - 1023;
66
+ hx &= 0x000fffff;
67
+ i = (hx + 0x95f64) & 0x100000;
68
+ set_hi_word(x, (uint32_t)(hx | (i ^ 0x3ff00000))); /* normalize x or x/2 */
69
+ k += (i>>20);
70
+ f = x - 1.0;
71
+ if ((0x000fffff & (2+hx)) < 3) { /* |f| < 2**-20 */
72
+ if (f == zero) {
73
+ if (k == 0) { return zero; }
74
+ dk = (double)k; return rnd(dk*ln2_hi) + rnd(dk*ln2_lo);
75
+ }
76
+ R = rnd(rnd(f*f)*rnd(0.5 - rnd(0.33333333333333333*f)));
77
+ if (k == 0) { return f-R; }
78
+ dk = (double)k; return rnd(dk*ln2_hi) - (rnd(R - rnd(dk*ln2_lo)) - f);
79
+ }
80
+ s = f/(2.0+f);
81
+ dk = (double)k;
82
+ z = s*s;
83
+ i = hx - 0x6147a;
84
+ w = z*z;
85
+ j = 0x6b851 - hx;
86
+ t1 = rnd(w*rnd(Lg2 + rnd(w*rnd(Lg4 + rnd(w*Lg6)))));
87
+ t2 = rnd(z*rnd(Lg1 + rnd(w*rnd(Lg3 + rnd(w*rnd(Lg5 + rnd(w*Lg7)))))));
88
+ i |= j;
89
+ R = t2 + t1;
90
+ if (i > 0) {
91
+ hfsq = rnd(0.5*f)*f;
92
+ if (k == 0) { return f - rnd(hfsq - rnd(s*(hfsq+R))); }
93
+ return rnd(dk*ln2_hi) - (rnd(hfsq - rnd(rnd(s*(hfsq+R)) + rnd(dk*ln2_lo))) - f);
94
+ } else {
95
+ if (k == 0) { return f - rnd(s*(f-R)); }
96
+ return rnd(dk*ln2_hi) - (rnd(rnd(s*(f-R)) - rnd(dk*ln2_lo)) - f);
97
+ }
98
+ }
99
+
100
+ } // namespace fdlibm
101
+ #endif
@@ -20,6 +20,7 @@
20
20
  #ifndef DATASKETCHES_SERDE_HPP_
21
21
  #define DATASKETCHES_SERDE_HPP_
22
22
 
23
+ #include <cstdint>
23
24
  #include <cstring>
24
25
  #include <iostream>
25
26
  #include <memory>
@@ -132,6 +133,11 @@ struct serde<T, typename std::enable_if<std::is_arithmetic<T>::value>::type> {
132
133
  /// ItemsSketch<String> with ArrayOfStringsSerDe in Java.
133
134
  /// The length of each string is stored as a 32-bit integer (historically),
134
135
  /// which may be too wasteful. Treat this as an example.
136
+ ///
137
+ /// This implementation treats std::string as an arbitrary byte container.
138
+ /// It does not check whether string contents are valid UTF-8.
139
+ ///
140
+ /// Use a UTF-8-validating SerDe when cross-language portability is required.
135
141
  template<>
136
142
  struct serde<std::string> {
137
143
  /// @copydoc serde::serialize
@@ -69,15 +69,21 @@ target_sources(common_test
69
69
  PRIVATE
70
70
  quantiles_sorted_view_test.cpp
71
71
  optional_test.cpp
72
+ binomial_bounds_test.cpp
72
73
  )
73
74
 
74
75
  # now the integration test part
75
76
  add_executable(integration_test)
76
77
 
77
- target_link_libraries(integration_test count cpc density fi hll kll req sampling theta tuple common_test_lib)
78
+ target_link_libraries(integration_test count cpc density fi hll kll req sampling theta tuple quantiles common_test_lib)
78
79
 
80
+ # Use CMAKE_CXX_STANDARD if defined, otherwise C++11
81
+ set(_integration_cxx_standard 11)
82
+ if(DEFINED CMAKE_CXX_STANDARD)
83
+ set(_integration_cxx_standard ${CMAKE_CXX_STANDARD})
84
+ endif()
79
85
  set_target_properties(integration_test PROPERTIES
80
- CXX_STANDARD 11
86
+ CXX_STANDARD ${_integration_cxx_standard}
81
87
  CXX_STANDARD_REQUIRED YES
82
88
  )
83
89
 
@@ -90,3 +96,39 @@ target_sources(integration_test
90
96
  PRIVATE
91
97
  integration_test.cpp
92
98
  )
99
+
100
+ # Separate hardening test executable (header-only, no pre-compiled libs)
101
+ # This ensures the sketch code is compiled with C++17 + hardening
102
+ # Always build this target - it will use CMAKE_CXX_STANDARD if set (and >= 17), otherwise C++17
103
+
104
+ add_executable(hardening_test)
105
+ target_link_libraries(hardening_test common common_test_lib)
106
+
107
+ # Include directories for header-only sketch implementations
108
+ target_include_directories(hardening_test PRIVATE
109
+ ${CMAKE_SOURCE_DIR}/quantiles/include
110
+ ${CMAKE_SOURCE_DIR}/kll/include
111
+ ${CMAKE_SOURCE_DIR}/req/include
112
+ ${CMAKE_SOURCE_DIR}/common/include
113
+ )
114
+
115
+ # Use C++17 minimum for hardening tests
116
+ set(_hardening_cxx_standard 17)
117
+ if(DEFINED CMAKE_CXX_STANDARD AND CMAKE_CXX_STANDARD GREATER_EQUAL 17)
118
+ set(_hardening_cxx_standard ${CMAKE_CXX_STANDARD})
119
+ endif()
120
+ set_target_properties(hardening_test PROPERTIES
121
+ CXX_STANDARD ${_hardening_cxx_standard}
122
+ CXX_STANDARD_REQUIRED YES
123
+ )
124
+ message(STATUS "hardening_test will use C++${_hardening_cxx_standard}")
125
+
126
+ add_test(
127
+ NAME hardening_test
128
+ COMMAND hardening_test "[deserialize_hardening]"
129
+ )
130
+
131
+ target_sources(hardening_test
132
+ PRIVATE
133
+ deserialize_hardening_test.cpp
134
+ )
@@ -0,0 +1,279 @@
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 "binomial_bounds.hpp"
23
+
24
+ namespace datasketches {
25
+
26
+ TEST_CASE("binomial_bounds: get_lower_bound", "[common]") {
27
+
28
+ SECTION("num_samples == 0") {
29
+ double result = binomial_bounds::get_lower_bound(0, 0.5, 1);
30
+ REQUIRE(result == 0.0);
31
+ }
32
+
33
+ SECTION("theta == 1.0") {
34
+ double result = binomial_bounds::get_lower_bound(100, 1.0, 1);
35
+ REQUIRE(result == 100.0);
36
+ }
37
+
38
+ SECTION("num_samples == 1") {
39
+ double result = binomial_bounds::get_lower_bound(1, 0.5, 1);
40
+ REQUIRE(result >= 0.0);
41
+ }
42
+
43
+ SECTION("num_samples == 1, stddev=2") {
44
+ double result = binomial_bounds::get_lower_bound(1, 0.5, 2);
45
+ REQUIRE(result >= 0.0);
46
+ }
47
+
48
+ SECTION("num_samples == 1, stddev=3") {
49
+ double result = binomial_bounds::get_lower_bound(1, 0.5, 3);
50
+ REQUIRE(result >= 0.0);
51
+ }
52
+
53
+ SECTION("num_samples > 120") {
54
+ double result = binomial_bounds::get_lower_bound(121, 0.5, 1);
55
+ REQUIRE(result >= 0.0);
56
+ }
57
+
58
+ SECTION("num_samples > 120, stddev=2") {
59
+ double result = binomial_bounds::get_lower_bound(200, 0.5, 2);
60
+ REQUIRE(result >= 0.0);
61
+ }
62
+
63
+ SECTION("num_samples > 120, stddev=3") {
64
+ double result = binomial_bounds::get_lower_bound(500, 0.5, 3);
65
+ REQUIRE(result >= 0.0);
66
+ }
67
+
68
+ SECTION("2 <= num_samples <= 120 AND theta > (1-1e-5)") {
69
+ double result = binomial_bounds::get_lower_bound(50, 1.0 - 1e-6, 1);
70
+ REQUIRE(std::abs(result - 50.0) < 50.0 * 0.01);
71
+ }
72
+
73
+ SECTION("2 <= num_samples <= 120 AND theta > (1-1e-5), stddev=2") {
74
+ double result = binomial_bounds::get_lower_bound(50, 1.0 - 1e-6, 2);
75
+ REQUIRE(std::abs(result - 50.0) < 50.0 * 0.01);
76
+ }
77
+
78
+ SECTION("2 <= num_samples <= 120 AND theta > (1-1e-5), stddev=3") {
79
+ double result = binomial_bounds::get_lower_bound(50, 1.0 - 1e-6, 3);
80
+ REQUIRE(std::abs(result - 50.0) < 50.0 * 0.01);
81
+ }
82
+
83
+ SECTION("2 <= num_samples <= 120 AND theta < num_samples/360") {
84
+ double result = binomial_bounds::get_lower_bound(100, 0.001, 1);
85
+ REQUIRE(result >= 0.0);
86
+ }
87
+
88
+ SECTION("2 <= num_samples <= 120 AND theta < num_samples/360, stddev=2") {
89
+ double result = binomial_bounds::get_lower_bound(100, 0.001, 2);
90
+ REQUIRE(result >= 0.0);
91
+ }
92
+
93
+ SECTION("2 <= num_samples <= 120 AND theta < num_samples/360, stddev=3") {
94
+ double result = binomial_bounds::get_lower_bound(100, 0.001, 3);
95
+ REQUIRE(result >= 0.0);
96
+ }
97
+
98
+ SECTION("2 <= num_samples <= 120 AND middle range theta (exact calculation)") {
99
+ double result = binomial_bounds::get_lower_bound(10, 0.5, 1);
100
+ REQUIRE(result >= 0.0);
101
+ }
102
+
103
+ SECTION("2 <= num_samples <= 120 AND middle range theta, stddev=2") {
104
+ double result = binomial_bounds::get_lower_bound(10, 0.5, 2);
105
+ REQUIRE(result >= 0.0);
106
+ }
107
+
108
+ SECTION("2 <= num_samples <= 120 AND middle range theta, stddev=3") {
109
+ double result = binomial_bounds::get_lower_bound(10, 0.5, 3);
110
+ REQUIRE(result >= 0.0);
111
+ }
112
+
113
+ SECTION("theta=0") {
114
+ REQUIRE_THROWS_AS(binomial_bounds::get_lower_bound(10, 0.0, 1), std::invalid_argument);
115
+ }
116
+
117
+ SECTION("theta very close to 0") {
118
+ double result = binomial_bounds::get_lower_bound(10, 1e-10, 1);
119
+ REQUIRE(result >= 0.0);
120
+ }
121
+
122
+ SECTION("num_samples=2 boundary") {
123
+ double result = binomial_bounds::get_lower_bound(2, 0.5, 1);
124
+ REQUIRE(result >= 0.0);
125
+ }
126
+
127
+ SECTION("num_samples=120 boundary") {
128
+ double result = binomial_bounds::get_lower_bound(120, 0.5, 1);
129
+ REQUIRE(result >= 0.0);
130
+ }
131
+
132
+ SECTION("estimate clamping case") {
133
+ double result = binomial_bounds::get_lower_bound(10, 0.9, 1);
134
+ double estimate = 10.0 / 0.9;
135
+ REQUIRE(result <= estimate);
136
+ }
137
+
138
+ SECTION("invalid theta < 0") {
139
+ REQUIRE_THROWS_AS(binomial_bounds::get_lower_bound(100, -0.1, 1), std::invalid_argument);
140
+ }
141
+
142
+ SECTION("invalid theta > 1") {
143
+ REQUIRE_THROWS_AS(binomial_bounds::get_lower_bound(100, 1.1, 1), std::invalid_argument);
144
+ }
145
+
146
+ SECTION("invalid stddev = 0") {
147
+ REQUIRE_THROWS_AS(binomial_bounds::get_lower_bound(100, 0.5, 0), std::invalid_argument);
148
+ }
149
+
150
+ SECTION("invalid stddev = 4") {
151
+ REQUIRE_THROWS_AS(binomial_bounds::get_lower_bound(100, 0.5, 4), std::invalid_argument);
152
+ }
153
+ }
154
+
155
+ TEST_CASE("binomial_bounds: get_upper_bound", "[common]") {
156
+
157
+ SECTION("theta == 1.0") {
158
+ double result = binomial_bounds::get_upper_bound(100, 1.0, 1);
159
+ REQUIRE(result == 100.0);
160
+ }
161
+
162
+ SECTION("num_samples == 0") {
163
+ double result = binomial_bounds::get_upper_bound(0, 0.5, 1);
164
+ REQUIRE(result > 0.0);
165
+ }
166
+
167
+ SECTION("num_samples == 0, stddev=2") {
168
+ double result = binomial_bounds::get_upper_bound(0, 0.5, 2);
169
+ REQUIRE(result > 0.0);
170
+ }
171
+
172
+ SECTION("num_samples == 0, stddev=3") {
173
+ double result = binomial_bounds::get_upper_bound(0, 0.5, 3);
174
+ REQUIRE(result > 0.0);
175
+ }
176
+
177
+ SECTION("num_samples > 120") {
178
+ double result = binomial_bounds::get_upper_bound(121, 0.5, 1);
179
+ REQUIRE(result >= 0.0);
180
+ }
181
+
182
+ SECTION("num_samples > 120, stddev=2") {
183
+ double result = binomial_bounds::get_upper_bound(200, 0.5, 2);
184
+ REQUIRE(result >= 0.0);
185
+ }
186
+
187
+ SECTION("num_samples > 120, stddev=3") {
188
+ double result = binomial_bounds::get_upper_bound(500, 0.5, 3);
189
+ REQUIRE(result >= 0.0);
190
+ }
191
+
192
+ SECTION("1 <= num_samples <= 120 AND theta > (1-1e-5)") {
193
+ double result = binomial_bounds::get_upper_bound(50, 1.0 - 1e-6, 1);
194
+ REQUIRE(result == 51.0);
195
+ }
196
+
197
+ SECTION("1 <= num_samples <= 120 AND theta > (1-1e-5), stddev=2") {
198
+ double result = binomial_bounds::get_upper_bound(50, 1.0 - 1e-6, 2);
199
+ REQUIRE(result == 51.0);
200
+ }
201
+
202
+ SECTION("1 <= num_samples <= 120 AND theta > (1-1e-5), stddev=3") {
203
+ double result = binomial_bounds::get_upper_bound(50, 1.0 - 1e-6, 3);
204
+ REQUIRE(result == 51.0);
205
+ }
206
+
207
+ SECTION("1 <= num_samples <= 120 AND theta < num_samples/360") {
208
+ double result = binomial_bounds::get_upper_bound(100, 0.001, 1);
209
+ REQUIRE(result >= 0.0);
210
+ }
211
+
212
+ SECTION("1 <= num_samples <= 120 AND theta < num_samples/360, stddev=2") {
213
+ double result = binomial_bounds::get_upper_bound(100, 0.001, 2);
214
+ REQUIRE(result >= 0.0);
215
+ }
216
+
217
+ SECTION("1 <= num_samples <= 120 AND theta < num_samples/360, stddev=3") {
218
+ double result = binomial_bounds::get_upper_bound(100, 0.001, 3);
219
+ REQUIRE(result >= 0.0);
220
+ }
221
+
222
+ SECTION("1 <= num_samples <= 120 AND middle range theta (exact calculation)") {
223
+ double result = binomial_bounds::get_upper_bound(10, 0.5, 1);
224
+ REQUIRE(result >= 0.0);
225
+ }
226
+
227
+ SECTION("1 <= num_samples <= 120 AND middle range theta, stddev=2") {
228
+ double result = binomial_bounds::get_upper_bound(10, 0.5, 2);
229
+ REQUIRE(result >= 0.0);
230
+ }
231
+
232
+ SECTION("1 <= num_samples <= 120 AND middle range theta, stddev=3") {
233
+ double result = binomial_bounds::get_upper_bound(10, 0.5, 3);
234
+ REQUIRE(result >= 0.0);
235
+ }
236
+
237
+ SECTION("theta=0") {
238
+ REQUIRE_THROWS_AS(binomial_bounds::get_upper_bound(10, 0.0, 1), std::invalid_argument);
239
+ }
240
+
241
+ SECTION("theta very close to 0") {
242
+ double result = binomial_bounds::get_upper_bound(10, 1e-10, 1);
243
+ REQUIRE(result >= 0.0);
244
+ }
245
+
246
+ SECTION("num_samples=1 boundary") {
247
+ double result = binomial_bounds::get_upper_bound(1, 0.5, 1);
248
+ REQUIRE(result >= 0.0);
249
+ }
250
+
251
+ SECTION("num_samples=120 boundary") {
252
+ double result = binomial_bounds::get_upper_bound(120, 0.5, 1);
253
+ REQUIRE(result >= 0.0);
254
+ }
255
+
256
+ SECTION("estimate clamping case") {
257
+ double result = binomial_bounds::get_upper_bound(10, 0.9, 1);
258
+ double estimate = 10.0 / 0.9;
259
+ REQUIRE(result >= estimate);
260
+ }
261
+
262
+ SECTION("invalid theta < 0") {
263
+ REQUIRE_THROWS_AS(binomial_bounds::get_upper_bound(100, -0.1, 1), std::invalid_argument);
264
+ }
265
+
266
+ SECTION("invalid theta > 1") {
267
+ REQUIRE_THROWS_AS(binomial_bounds::get_upper_bound(100, 1.1, 1), std::invalid_argument);
268
+ }
269
+
270
+ SECTION("invalid stddev = 0") {
271
+ REQUIRE_THROWS_AS(binomial_bounds::get_upper_bound(100, 0.5, 0), std::invalid_argument);
272
+ }
273
+
274
+ SECTION("invalid stddev = 4") {
275
+ REQUIRE_THROWS_AS(binomial_bounds::get_upper_bound(100, 0.5, 4), std::invalid_argument);
276
+ }
277
+ }
278
+
279
+ } /* namespace datasketches */
@@ -0,0 +1,188 @@
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 <sstream>
22
+ #include <vector>
23
+
24
+ // Include all affected sketch types
25
+ #include <quantiles_sketch.hpp>
26
+ #include <kll_sketch.hpp>
27
+ #include <req_sketch.hpp>
28
+
29
+ namespace datasketches {
30
+
31
+ /**
32
+ * Test for fix of issue #477:
33
+ * BUG: SIGABRT in deserialize(): dereferencing empty std::optional (libc++ verbose_abort)
34
+ *
35
+ * These tests exercise the actual deserialization code path that contained the bug.
36
+ * With buggy code (&*tmp on empty optional) and hardening enabled, these will SIGABRT.
37
+ * With fixed code (aligned_storage), these pass normally.
38
+ *
39
+ * IMPORTANT: These tests actually call deserialize() on multi-item sketches, which
40
+ * exercises the buggy code path where min/max are deserialized.
41
+ */
42
+
43
+ TEST_CASE("quantiles_sketch: deserialize multi-item sketch", "[deserialize_hardening]") {
44
+ // Create sketch with multiple items (so min/max are stored in serialization)
45
+ quantiles_sketch<double> sketch(128);
46
+ for (int i = 0; i < 1000; i++) {
47
+ sketch.update(static_cast<double>(i));
48
+ }
49
+
50
+ // Serialize
51
+ auto bytes = sketch.serialize();
52
+
53
+ // Deserialize - WITH BUGGY CODE AND HARDENING, THIS WILL SIGABRT HERE
54
+ // The bug is: sd.deserialize(is, &*tmp, 1) where tmp is empty optional
55
+ auto sketch2 = quantiles_sketch<double>::deserialize(bytes.data(), bytes.size());
56
+
57
+ // Verify deserialization worked correctly
58
+ REQUIRE(sketch2.get_n() == sketch.get_n());
59
+ REQUIRE(sketch2.get_min_item() == sketch.get_min_item());
60
+ REQUIRE(sketch2.get_max_item() == sketch.get_max_item());
61
+ REQUIRE(sketch2.get_quantile(0.5) == sketch.get_quantile(0.5));
62
+ }
63
+
64
+ TEST_CASE("quantiles_sketch: deserialize from stream", "[deserialize_hardening]") {
65
+ quantiles_sketch<float> sketch(256);
66
+ for (int i = 0; i < 2000; i++) {
67
+ sketch.update(static_cast<float>(i) * 0.5f);
68
+ }
69
+
70
+ // Serialize to stream
71
+ std::stringstream ss;
72
+ sketch.serialize(ss);
73
+
74
+ // Deserialize from stream - exercises the buggy code path
75
+ auto sketch2 = quantiles_sketch<float>::deserialize(ss);
76
+
77
+ REQUIRE(sketch2.get_n() == sketch.get_n());
78
+ REQUIRE(sketch2.get_min_item() == sketch.get_min_item());
79
+ REQUIRE(sketch2.get_max_item() == sketch.get_max_item());
80
+ }
81
+
82
+ TEST_CASE("kll_sketch: deserialize multi-item sketch", "[deserialize_hardening]") {
83
+ kll_sketch<float> sketch(200);
84
+ for (int i = 0; i < 1500; i++) {
85
+ sketch.update(static_cast<float>(i));
86
+ }
87
+
88
+ auto bytes = sketch.serialize();
89
+
90
+ // Deserialize - exercises buggy &*tmp code path
91
+ auto sketch2 = kll_sketch<float>::deserialize(bytes.data(), bytes.size());
92
+
93
+ REQUIRE(sketch2.get_n() == sketch.get_n());
94
+ REQUIRE(sketch2.get_min_item() == sketch.get_min_item());
95
+ REQUIRE(sketch2.get_max_item() == sketch.get_max_item());
96
+ }
97
+
98
+ TEST_CASE("kll_sketch: deserialize from stream", "[deserialize_hardening]") {
99
+ kll_sketch<int> sketch(400);
100
+ for (int i = 0; i < 3000; i++) {
101
+ sketch.update(i);
102
+ }
103
+
104
+ std::stringstream ss;
105
+ sketch.serialize(ss);
106
+
107
+ // Deserialize from stream
108
+ auto sketch2 = kll_sketch<int>::deserialize(ss);
109
+
110
+ REQUIRE(sketch2.get_n() == sketch.get_n());
111
+ REQUIRE(sketch2.get_min_item() == sketch.get_min_item());
112
+ REQUIRE(sketch2.get_max_item() == sketch.get_max_item());
113
+ }
114
+
115
+ TEST_CASE("req_sketch: deserialize multi-level sketch", "[deserialize_hardening]") {
116
+ // REQ sketch only has the bug when num_levels > 1
117
+ // We need to add enough items to trigger multiple levels
118
+ req_sketch<float> sketch(12);
119
+ for (int i = 0; i < 10000; i++) {
120
+ sketch.update(static_cast<float>(i));
121
+ }
122
+
123
+ auto bytes = sketch.serialize();
124
+
125
+ // Deserialize - exercises buggy code path when num_levels > 1
126
+ auto sketch2 = req_sketch<float>::deserialize(bytes.data(), bytes.size());
127
+
128
+ REQUIRE(sketch2.get_n() == sketch.get_n());
129
+ REQUIRE(sketch2.get_min_item() == sketch.get_min_item());
130
+ REQUIRE(sketch2.get_max_item() == sketch.get_max_item());
131
+ }
132
+
133
+ TEST_CASE("req_sketch: deserialize from stream", "[deserialize_hardening]") {
134
+ req_sketch<double> sketch(20);
135
+ for (int i = 0; i < 15000; i++) {
136
+ sketch.update(static_cast<double>(i) * 0.1);
137
+ }
138
+
139
+ std::stringstream ss;
140
+ sketch.serialize(ss);
141
+
142
+ // Deserialize from stream
143
+ auto sketch2 = req_sketch<double>::deserialize(ss);
144
+
145
+ REQUIRE(sketch2.get_n() == sketch.get_n());
146
+ REQUIRE(sketch2.get_min_item() == sketch.get_min_item());
147
+ REQUIRE(sketch2.get_max_item() == sketch.get_max_item());
148
+ }
149
+
150
+ TEST_CASE("multiple sketch types: stress test", "[deserialize_hardening]") {
151
+ SECTION("quantiles with various sizes") {
152
+ for (int k : {64, 128, 256}) {
153
+ quantiles_sketch<int> sketch(k);
154
+ for (int i = 0; i < 5000; i++) {
155
+ sketch.update(i);
156
+ }
157
+ auto bytes = sketch.serialize();
158
+ auto sketch2 = quantiles_sketch<int>::deserialize(bytes.data(), bytes.size());
159
+ REQUIRE(sketch2.get_n() == sketch.get_n());
160
+ }
161
+ }
162
+
163
+ SECTION("kll with various sizes") {
164
+ for (int k : {100, 200, 400}) {
165
+ kll_sketch<double> sketch(k);
166
+ for (int i = 0; i < 4000; i++) {
167
+ sketch.update(static_cast<double>(i) / 10.0);
168
+ }
169
+ auto bytes = sketch.serialize();
170
+ auto sketch2 = kll_sketch<double>::deserialize(bytes.data(), bytes.size());
171
+ REQUIRE(sketch2.get_n() == sketch.get_n());
172
+ }
173
+ }
174
+
175
+ SECTION("req with various sizes") {
176
+ for (int k : {12, 20}) {
177
+ req_sketch<float> sketch(k);
178
+ for (int i = 0; i < 8000; i++) {
179
+ sketch.update(static_cast<float>(i));
180
+ }
181
+ auto bytes = sketch.serialize();
182
+ auto sketch2 = req_sketch<float>::deserialize(bytes.data(), bytes.size());
183
+ REQUIRE(sketch2.get_n() == sketch.get_n());
184
+ }
185
+ }
186
+ }
187
+
188
+ } // namespace datasketches