datasketches 0.5.1 → 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 (120) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +9 -0
  3. data/ext/datasketches/cpc_wrapper.cpp +8 -9
  4. data/ext/datasketches/fi_wrapper.cpp +4 -5
  5. data/ext/datasketches/hll_wrapper.cpp +9 -10
  6. data/ext/datasketches/kll_wrapper.cpp +13 -12
  7. data/ext/datasketches/theta_wrapper.cpp +11 -13
  8. data/ext/datasketches/vo_wrapper.cpp +1 -2
  9. data/lib/datasketches/version.rb +1 -1
  10. data/vendor/datasketches-cpp/CMakeLists.txt +5 -3
  11. data/vendor/datasketches-cpp/CODE_OF_CONDUCT.md +1 -1
  12. data/vendor/datasketches-cpp/LICENSE +14 -0
  13. data/vendor/datasketches-cpp/README.md +67 -73
  14. data/vendor/datasketches-cpp/benchmarks/CMakeLists.txt +52 -0
  15. data/vendor/datasketches-cpp/benchmarks/benchmark_count_min_sketch.cpp +153 -0
  16. data/vendor/datasketches-cpp/benchmarks/benchmark_count_min_sketch_serialization.cpp +161 -0
  17. data/vendor/datasketches-cpp/common/CMakeLists.txt +1 -0
  18. data/vendor/datasketches-cpp/common/include/binomial_bounds.hpp +2 -2
  19. data/vendor/datasketches-cpp/common/include/fdlibm_log.hpp +101 -0
  20. data/vendor/datasketches-cpp/common/include/serde.hpp +6 -0
  21. data/vendor/datasketches-cpp/common/test/CMakeLists.txt +44 -2
  22. data/vendor/datasketches-cpp/common/test/binomial_bounds_test.cpp +279 -0
  23. data/vendor/datasketches-cpp/common/test/deserialize_hardening_test.cpp +188 -0
  24. data/vendor/datasketches-cpp/count/include/count_min.hpp +17 -4
  25. data/vendor/datasketches-cpp/count/include/count_min_impl.hpp +65 -83
  26. data/vendor/datasketches-cpp/count/test/count_min_test.cpp +63 -7
  27. data/vendor/datasketches-cpp/cpc/include/compression_data.hpp +2 -0
  28. data/vendor/datasketches-cpp/cpc/include/cpc_compressor_impl.hpp +16 -7
  29. data/vendor/datasketches-cpp/cpc/include/cpc_sketch.hpp +1 -0
  30. data/vendor/datasketches-cpp/cpc/include/cpc_sketch_impl.hpp +54 -36
  31. data/vendor/datasketches-cpp/cpc/include/cpc_union_impl.hpp +27 -27
  32. data/vendor/datasketches-cpp/cpc/include/cpc_util.hpp +7 -7
  33. data/vendor/datasketches-cpp/cpc/include/icon_estimator.hpp +5 -5
  34. data/vendor/datasketches-cpp/cpc/include/u32_table_impl.hpp +16 -16
  35. data/vendor/datasketches-cpp/cpc/test/cpc_sketch_test.cpp +35 -0
  36. data/vendor/datasketches-cpp/fi/include/frequent_items_sketch.hpp +28 -3
  37. data/vendor/datasketches-cpp/fi/include/frequent_items_sketch_impl.hpp +41 -25
  38. data/vendor/datasketches-cpp/fi/include/reverse_purge_hash_map.hpp +3 -1
  39. data/vendor/datasketches-cpp/fi/include/reverse_purge_hash_map_impl.hpp +10 -5
  40. data/vendor/datasketches-cpp/fi/test/frequent_items_sketch_serialize_for_java.cpp +24 -0
  41. data/vendor/datasketches-cpp/fi/test/frequent_items_sketch_test.cpp +116 -0
  42. data/vendor/datasketches-cpp/filters/include/bloom_filter.hpp +1 -1
  43. data/vendor/datasketches-cpp/filters/include/bloom_filter_impl.hpp +32 -12
  44. data/vendor/datasketches-cpp/filters/test/bloom_filter_test.cpp +28 -1
  45. data/vendor/datasketches-cpp/hll/include/CouponHashSet-internal.hpp +1 -2
  46. data/vendor/datasketches-cpp/hll/include/CouponList-internal.hpp +19 -5
  47. data/vendor/datasketches-cpp/hll/include/CubicInterpolation-internal.hpp +4 -4
  48. data/vendor/datasketches-cpp/hll/include/HarmonicNumbers-internal.hpp +2 -1
  49. data/vendor/datasketches-cpp/hll/include/Hll4Array-internal.hpp +4 -4
  50. data/vendor/datasketches-cpp/hll/include/Hll4Array.hpp +1 -1
  51. data/vendor/datasketches-cpp/hll/include/Hll6Array-internal.hpp +3 -3
  52. data/vendor/datasketches-cpp/hll/include/Hll6Array.hpp +1 -1
  53. data/vendor/datasketches-cpp/hll/include/Hll8Array-internal.hpp +6 -3
  54. data/vendor/datasketches-cpp/hll/include/Hll8Array.hpp +1 -1
  55. data/vendor/datasketches-cpp/hll/include/HllArray-internal.hpp +35 -29
  56. data/vendor/datasketches-cpp/hll/include/HllArray.hpp +1 -1
  57. data/vendor/datasketches-cpp/hll/include/HllSketch-internal.hpp +3 -5
  58. data/vendor/datasketches-cpp/hll/include/HllSketchImpl-internal.hpp +5 -11
  59. data/vendor/datasketches-cpp/hll/include/HllSketchImpl.hpp +2 -4
  60. data/vendor/datasketches-cpp/hll/include/HllSketchImplFactory.hpp +11 -10
  61. data/vendor/datasketches-cpp/hll/include/HllUnion-internal.hpp +17 -8
  62. data/vendor/datasketches-cpp/hll/include/HllUtil.hpp +12 -3
  63. data/vendor/datasketches-cpp/hll/include/coupon_iterator-internal.hpp +2 -2
  64. data/vendor/datasketches-cpp/hll/include/coupon_iterator.hpp +3 -0
  65. data/vendor/datasketches-cpp/hll/include/hll.hpp +11 -4
  66. data/vendor/datasketches-cpp/hll/include/hll.private.hpp +19 -0
  67. data/vendor/datasketches-cpp/hll/test/CMakeLists.txt +2 -0
  68. data/vendor/datasketches-cpp/hll/test/CouponListTest.cpp +64 -0
  69. data/vendor/datasketches-cpp/hll/test/HllFullSizeTest.cpp +137 -0
  70. data/vendor/datasketches-cpp/hll/test/HllKxqRebuildTest.cpp +150 -0
  71. data/vendor/datasketches-cpp/hll/test/HllSketchTest.cpp +3 -3
  72. data/vendor/datasketches-cpp/hll/test/HllUnionTest.cpp +71 -1
  73. data/vendor/datasketches-cpp/kll/include/kll_helper_impl.hpp +10 -10
  74. data/vendor/datasketches-cpp/kll/include/kll_sketch.hpp +10 -1
  75. data/vendor/datasketches-cpp/kll/include/kll_sketch_impl.hpp +33 -24
  76. data/vendor/datasketches-cpp/kll/test/kll_sketch_deserialize_from_java_test.cpp +24 -0
  77. data/vendor/datasketches-cpp/kll/test/kll_sketch_serialize_for_java.cpp +10 -0
  78. data/vendor/datasketches-cpp/quantiles/include/quantiles_sketch.hpp +11 -2
  79. data/vendor/datasketches-cpp/quantiles/include/quantiles_sketch_impl.hpp +24 -15
  80. data/vendor/datasketches-cpp/req/include/req_sketch.hpp +9 -0
  81. data/vendor/datasketches-cpp/req/include/req_sketch_impl.hpp +24 -15
  82. data/vendor/datasketches-cpp/req/test/req_sketch_deserialize_from_java_test.cpp +46 -0
  83. data/vendor/datasketches-cpp/req/test/req_sketch_serialize_for_java.cpp +20 -0
  84. data/vendor/datasketches-cpp/req/test/req_sketch_test.cpp +70 -0
  85. data/vendor/datasketches-cpp/sampling/include/ebpps_sample_impl.hpp +17 -8
  86. data/vendor/datasketches-cpp/sampling/include/ebpps_sketch.hpp +13 -0
  87. data/vendor/datasketches-cpp/sampling/include/var_opt_sketch.hpp +10 -1
  88. data/vendor/datasketches-cpp/sampling/include/var_opt_sketch_impl.hpp +6 -7
  89. data/vendor/datasketches-cpp/sampling/include/var_opt_union.hpp +5 -1
  90. data/vendor/datasketches-cpp/sampling/include/var_opt_union_impl.hpp +2 -1
  91. data/vendor/datasketches-cpp/sampling/test/ebpps_allocation_test.cpp +1 -1
  92. data/vendor/datasketches-cpp/sampling/test/ebpps_sketch_test.cpp +2 -2
  93. data/vendor/datasketches-cpp/sampling/test/var_opt_allocation_test.cpp +1 -1
  94. data/vendor/datasketches-cpp/sampling/test/var_opt_sketch_test.cpp +10 -4
  95. data/vendor/datasketches-cpp/sampling/test/var_opt_union_test.cpp +12 -0
  96. data/vendor/datasketches-cpp/tdigest/include/tdigest.hpp +38 -2
  97. data/vendor/datasketches-cpp/tdigest/include/tdigest_impl.hpp +168 -9
  98. data/vendor/datasketches-cpp/tdigest/test/CMakeLists.txt +1 -0
  99. data/vendor/datasketches-cpp/tdigest/test/tdigest_iterator_test.cpp +274 -0
  100. data/vendor/datasketches-cpp/tdigest/test/tdigest_test.cpp +275 -0
  101. data/vendor/datasketches-cpp/theta/include/compact_theta_sketch_parser.hpp +2 -0
  102. data/vendor/datasketches-cpp/theta/include/compact_theta_sketch_parser_impl.hpp +24 -3
  103. data/vendor/datasketches-cpp/theta/include/theta_constants.hpp +4 -2
  104. data/vendor/datasketches-cpp/theta/include/theta_helpers.hpp +32 -0
  105. data/vendor/datasketches-cpp/theta/include/theta_set_difference_base_impl.hpp +4 -2
  106. data/vendor/datasketches-cpp/theta/include/theta_sketch.hpp +22 -4
  107. data/vendor/datasketches-cpp/theta/include/theta_sketch_impl.hpp +60 -38
  108. data/vendor/datasketches-cpp/theta/include/theta_union_base_impl.hpp +2 -6
  109. data/vendor/datasketches-cpp/theta/include/theta_update_sketch_base_impl.hpp +2 -2
  110. data/vendor/datasketches-cpp/theta/test/bit_packing_test.cpp +50 -0
  111. data/vendor/datasketches-cpp/theta/test/theta_a_not_b_test.cpp +22 -0
  112. data/vendor/datasketches-cpp/theta/test/theta_sketch_test.cpp +315 -0
  113. data/vendor/datasketches-cpp/tools/rat-check.sh +68 -0
  114. data/vendor/datasketches-cpp/tuple/include/array_tuple_sketch.hpp +35 -4
  115. data/vendor/datasketches-cpp/tuple/include/array_tuple_sketch_impl.hpp +2 -2
  116. data/vendor/datasketches-cpp/tuple/include/tuple_sketch.hpp +41 -0
  117. data/vendor/datasketches-cpp/tuple/include/tuple_sketch_impl.hpp +5 -4
  118. data/vendor/datasketches-cpp/tuple/test/tuple_sketch_test.cpp +59 -0
  119. data/vendor/datasketches-cpp/version.cfg.in +1 -1
  120. metadata +12 -2
@@ -23,6 +23,7 @@
23
23
  #include <sstream>
24
24
  #include <vector>
25
25
  #include <stdexcept>
26
+ #include <algorithm>
26
27
 
27
28
  #include "binomial_bounds.hpp"
28
29
  #include "theta_helpers.hpp"
@@ -239,8 +240,21 @@ auto update_theta_sketch_alloc<A>::end() const -> const_iterator {
239
240
  }
240
241
 
241
242
  template<typename A>
242
- compact_theta_sketch_alloc<A> update_theta_sketch_alloc<A>::compact(bool ordered) const {
243
- return compact_theta_sketch_alloc<A>(*this, ordered);
243
+ compact_theta_sketch_alloc<A> update_theta_sketch_alloc<A>::compact(bool ordered, bool trim) const {
244
+ if (!trim) return compact_theta_sketch_alloc<A>(*this, ordered);
245
+
246
+ std::vector<uint64_t, A> entries(table_.allocator_);
247
+ if (this->is_empty()) {
248
+ return compact_theta_sketch_alloc<A>(true, true, this->get_seed_hash(), this->get_theta64(), std::move(entries));
249
+ }
250
+ // copy first: this method is const, and quick select would permute the source table
251
+ entries.reserve(this->get_num_retained());
252
+ std::copy(this->begin(), this->end(), std::back_inserter(entries));
253
+ uint64_t theta = this->get_theta64();
254
+ const uint32_t nominal_size = 1 << table_.lg_nom_size_;
255
+ theta = trim_to_nominal<trivial_extract_key>(entries, nominal_size, theta);
256
+ if (ordered) std::sort(entries.begin(), entries.end());
257
+ return compact_theta_sketch_alloc<A>(false, ordered, this->get_seed_hash(), theta, std::move(entries));
244
258
  }
245
259
 
246
260
  template<typename A>
@@ -315,7 +329,7 @@ uint32_t compact_theta_sketch_alloc<A>::get_num_retained() const {
315
329
 
316
330
  template<typename A>
317
331
  uint16_t compact_theta_sketch_alloc<A>::get_seed_hash() const {
318
- return seed_hash_;
332
+ return is_empty_ ? 0 : seed_hash_;
319
333
  }
320
334
 
321
335
  template<typename A>
@@ -376,7 +390,7 @@ size_t compact_theta_sketch_alloc<A>::get_compressed_serialized_size_bytes(uint8
376
390
 
377
391
  template<typename A>
378
392
  void compact_theta_sketch_alloc<A>::serialize(std::ostream& os) const {
379
- const uint8_t preamble_longs = this->is_estimation_mode() ? 3 : this->is_empty() || entries_.size() == 1 ? 1 : 2;
393
+ const uint8_t preamble_longs = get_preamble_longs(false);
380
394
  write(os, preamble_longs);
381
395
  write(os, UNCOMPRESSED_SERIAL_VERSION);
382
396
  write(os, SKETCH_TYPE);
@@ -385,7 +399,8 @@ void compact_theta_sketch_alloc<A>::serialize(std::ostream& os) const {
385
399
  (1 << flags::IS_COMPACT) |
386
400
  (1 << flags::IS_READ_ONLY) |
387
401
  (this->is_empty() ? 1 << flags::IS_EMPTY : 0) |
388
- (this->is_ordered() ? 1 << flags::IS_ORDERED : 0)
402
+ (this->is_ordered() ? 1 << flags::IS_ORDERED : 0) |
403
+ (is_single_item() ? 1 << flags::IS_SINGLE_ITEM : 0)
389
404
  );
390
405
  write(os, flags_byte);
391
406
  write(os, get_seed_hash());
@@ -411,7 +426,8 @@ auto compact_theta_sketch_alloc<A>::serialize(unsigned header_size_bytes) const
411
426
  (1 << flags::IS_COMPACT) |
412
427
  (1 << flags::IS_READ_ONLY) |
413
428
  (this->is_empty() ? 1 << flags::IS_EMPTY : 0) |
414
- (this->is_ordered() ? 1 << flags::IS_ORDERED : 0)
429
+ (this->is_ordered() ? 1 << flags::IS_ORDERED : 0) |
430
+ (is_single_item() ? 1 << flags::IS_SINGLE_ITEM : 0)
415
431
  );
416
432
  *ptr++ = flags_byte;
417
433
  ptr += copy_to_mem(get_seed_hash(), ptr);
@@ -424,6 +440,12 @@ auto compact_theta_sketch_alloc<A>::serialize(unsigned header_size_bytes) const
424
440
  return bytes;
425
441
  }
426
442
 
443
+ template<typename A>
444
+ bool compact_theta_sketch_alloc<A>::is_single_item() const {
445
+ // one entry in exact mode: the same condition Java uses to set its SingleItem flag
446
+ return entries_.size() == 1 && !this->is_estimation_mode();
447
+ }
448
+
427
449
  template<typename A>
428
450
  bool compact_theta_sketch_alloc<A>::is_suitable_for_compression() const {
429
451
  if (!this->is_ordered() || entries_.size() == 0 ||
@@ -459,7 +481,7 @@ uint8_t compact_theta_sketch_alloc<A>::compute_entry_bits() const {
459
481
 
460
482
  template<typename A>
461
483
  void compact_theta_sketch_alloc<A>::serialize_version_4(std::ostream& os) const {
462
- const uint8_t preamble_longs = this->is_estimation_mode() ? 2 : 1;
484
+ const uint8_t preamble_longs = get_preamble_longs(true);
463
485
  const uint8_t entry_bits = compute_entry_bits();
464
486
  const uint8_t num_entries_bytes = get_num_entries_bytes();
465
487
 
@@ -674,7 +696,9 @@ compact_theta_sketch_alloc<A> compact_theta_sketch_alloc<A>::deserialize_v4(
674
696
  uint8_t preamble_longs, std::istream& is, uint64_t seed, const A& allocator)
675
697
  {
676
698
  const auto entry_bits = read<uint8_t>(is);
699
+ compact_theta_sketch_parser<true>::check_v4_entry_bits(entry_bits);
677
700
  const auto num_entries_bytes = read<uint8_t>(is);
701
+ compact_theta_sketch_parser<true>::check_v4_num_entries_bytes(num_entries_bytes);
678
702
  const auto flags_byte = read<uint8_t>(is);
679
703
  const auto seed_hash = read<uint16_t>(is);
680
704
  const bool is_empty = flags_byte & (1 << flags::IS_EMPTY);
@@ -782,7 +806,7 @@ uint32_t wrapped_compact_theta_sketch_alloc<A>::get_num_retained() const {
782
806
 
783
807
  template<typename A>
784
808
  uint16_t wrapped_compact_theta_sketch_alloc<A>::get_seed_hash() const {
785
- return data_.seed_hash;
809
+ return is_empty() ? 0 : data_.seed_hash;
786
810
  }
787
811
 
788
812
  template<typename A>
@@ -817,23 +841,15 @@ num_entries_(num_entries),
817
841
  index_(index),
818
842
  previous_(0),
819
843
  is_block_mode_(num_entries_ >= 8),
820
- buf_i_(0),
821
844
  offset_(0)
822
845
  {
823
846
  if (entry_bits == 64) { // no compression
824
847
  ptr_ = reinterpret_cast<const uint64_t*>(ptr) + index;
825
848
  } else if (index < num_entries) {
826
849
  if (is_block_mode_) {
827
- unpack_bits_block8(buffer_, reinterpret_cast<const uint8_t*>(ptr_), entry_bits_);
828
- ptr_ = reinterpret_cast<const uint8_t*>(ptr_) + entry_bits_;
829
- for (int i = 0; i < 8; ++i) {
830
- buffer_[i] += previous_;
831
- previous_ = buffer_[i];
832
- }
850
+ unpack8();
833
851
  } else {
834
- offset_ = unpack_bits(buffer_[0], entry_bits_, reinterpret_cast<const uint8_t*&>(ptr_), offset_);
835
- buffer_[0] += previous_;
836
- previous_ = buffer_[0];
852
+ unpack1();
837
853
  }
838
854
  }
839
855
  }
@@ -844,35 +860,41 @@ auto wrapped_compact_theta_sketch_alloc<Allocator>::const_iterator::operator++()
844
860
  ptr_ = reinterpret_cast<const uint64_t*>(ptr_) + 1;
845
861
  return *this;
846
862
  }
847
- ++index_;
848
- if (index_ < num_entries_) {
863
+ if (++index_ < num_entries_) {
849
864
  if (is_block_mode_) {
850
- ++buf_i_;
851
- if (buf_i_ == 8) {
852
- buf_i_ = 0;
853
- if (index_ + 8 < num_entries_) {
854
- unpack_bits_block8(buffer_, reinterpret_cast<const uint8_t*>(ptr_), entry_bits_);
855
- ptr_ = reinterpret_cast<const uint8_t*>(ptr_) + entry_bits_;
856
- for (int i = 0; i < 8; ++i) {
857
- buffer_[i] += previous_;
858
- previous_ = buffer_[i];
859
- }
865
+ if ((index_ & 7) == 0) {
866
+ if (num_entries_ - index_ >= 8) {
867
+ unpack8();
860
868
  } else {
861
869
  is_block_mode_ = false;
862
- offset_ = unpack_bits(buffer_[0], entry_bits_, reinterpret_cast<const uint8_t*&>(ptr_), offset_);
863
- buffer_[0] += previous_;
864
- previous_ = buffer_[0];
870
+ unpack1();
865
871
  }
866
872
  }
867
873
  } else {
868
- offset_ = unpack_bits(buffer_[0], entry_bits_, reinterpret_cast<const uint8_t*&>(ptr_), offset_);
869
- buffer_[0] += previous_;
870
- previous_ = buffer_[0];
874
+ unpack1();
871
875
  }
872
876
  }
873
877
  return *this;
874
878
  }
875
879
 
880
+ template<typename Allocator>
881
+ void wrapped_compact_theta_sketch_alloc<Allocator>::const_iterator::unpack1() {
882
+ const uint32_t i = index_ & 7;
883
+ offset_ = unpack_bits(buffer_[i], entry_bits_, reinterpret_cast<const uint8_t*&>(ptr_), offset_);
884
+ buffer_[i] += previous_;
885
+ previous_ = buffer_[i];
886
+ }
887
+
888
+ template<typename Allocator>
889
+ void wrapped_compact_theta_sketch_alloc<Allocator>::const_iterator::unpack8() {
890
+ unpack_bits_block8(buffer_, reinterpret_cast<const uint8_t*>(ptr_), entry_bits_);
891
+ ptr_ = reinterpret_cast<const uint8_t*>(ptr_) + entry_bits_;
892
+ for (int i = 0; i < 8; ++i) {
893
+ buffer_[i] += previous_;
894
+ previous_ = buffer_[i];
895
+ }
896
+ }
897
+
876
898
  template<typename Allocator>
877
899
  auto wrapped_compact_theta_sketch_alloc<Allocator>::const_iterator::operator++(int) -> const_iterator {
878
900
  const_iterator tmp(*this);
@@ -895,13 +917,13 @@ bool wrapped_compact_theta_sketch_alloc<Allocator>::const_iterator::operator==(c
895
917
  template<typename Allocator>
896
918
  auto wrapped_compact_theta_sketch_alloc<Allocator>::const_iterator::operator*() const -> reference {
897
919
  if (entry_bits_ == 64) return *reinterpret_cast<const uint64_t*>(ptr_);
898
- return buffer_[buf_i_];
920
+ return buffer_[index_ & 7];
899
921
  }
900
922
 
901
923
  template<typename Allocator>
902
924
  auto wrapped_compact_theta_sketch_alloc<Allocator>::const_iterator::operator->() const -> pointer {
903
925
  if (entry_bits_ == 64) return reinterpret_cast<const uint64_t*>(ptr_);
904
- return buffer_ + buf_i_;
926
+ return buffer_ + (index_ & 7);
905
927
  }
906
928
 
907
929
  } /* namespace datasketches */
@@ -24,6 +24,7 @@
24
24
  #include <stdexcept>
25
25
 
26
26
  #include "conditional_forward.hpp"
27
+ #include "theta_helpers.hpp"
27
28
 
28
29
  namespace datasketches {
29
30
 
@@ -70,12 +71,7 @@ CS theta_union_base<EN, EK, P, S, CS, A>::get_result(bool ordered) const {
70
71
  } else {
71
72
  std::copy_if(table_.begin(), table_.end(), std::back_inserter(entries), key_not_zero_less_than<uint64_t, EN, EK>(theta));
72
73
  }
73
- if (entries.size() > nominal_num) {
74
- std::nth_element(entries.begin(), entries.begin() + nominal_num, entries.end(), comparator());
75
- theta = EK()(entries[nominal_num]);
76
- entries.erase(entries.begin() + nominal_num, entries.end());
77
- entries.shrink_to_fit();
78
- }
74
+ theta = trim_to_nominal<EK>(entries, nominal_num, theta);
79
75
  if (ordered) std::sort(entries.begin(), entries.end(), comparator());
80
76
  return CS(table_.is_empty_, ordered, compute_seed_hash(table_.seed_), theta, std::move(entries));
81
77
  }
@@ -265,7 +265,7 @@ void theta_update_sketch_base<EN, EK, A>::reset() {
265
265
  }
266
266
  }
267
267
  const uint8_t starting_lg_size = theta_build_helper<true>::starting_sub_multiple(
268
- lg_nom_size_ + 1, theta_constants::MIN_LG_K, static_cast<uint8_t>(rf_));
268
+ lg_nom_size_ + 1, theta_constants::MIN_LG_ARR, static_cast<uint8_t>(rf_));
269
269
  if (starting_lg_size != lg_cur_size_) {
270
270
  allocator_.deallocate(entries_, cur_size);
271
271
  lg_cur_size_ = starting_lg_size;
@@ -346,7 +346,7 @@ uint64_t theta_base_builder<Derived, Allocator>::starting_theta() const {
346
346
 
347
347
  template<typename Derived, typename Allocator>
348
348
  uint8_t theta_base_builder<Derived, Allocator>::starting_lg_size() const {
349
- return theta_build_helper<true>::starting_sub_multiple(lg_k_ + 1, theta_constants::MIN_LG_K, static_cast<uint8_t>(rf_));
349
+ return theta_build_helper<true>::starting_sub_multiple(lg_k_ + 1, theta_constants::MIN_LG_ARR, static_cast<uint8_t>(rf_));
350
350
  }
351
351
 
352
352
  // iterator
@@ -80,4 +80,54 @@ TEST_CASE("pack unpack blocks") {
80
80
  }
81
81
  }
82
82
 
83
+ TEST_CASE("pack bits unpack blocks") {
84
+ uint64_t value = 0; // arbitrary starting value
85
+ for (int m = 0; m < 10000; ++m) {
86
+ for (uint8_t bits = 1; bits <= 63; ++bits) {
87
+ const uint64_t mask = (1ULL << bits) - 1;
88
+ std::vector<uint64_t> input(8, 0);
89
+ for (int i = 0; i < 8; ++i) {
90
+ input[i] = value & mask;
91
+ value += IGOLDEN64;
92
+ }
93
+ std::vector<uint8_t> bytes(bits, 0);
94
+ uint8_t offset = 0;
95
+ uint8_t* ptr = bytes.data();
96
+ for (int i = 0; i < 8; ++i) {
97
+ offset = pack_bits(input[i], bits, ptr, offset);
98
+ }
99
+ std::vector<uint64_t> output(8, 0);
100
+ unpack_bits_block8(output.data(), bytes.data(), bits);
101
+ for (int i = 0; i < 8; ++i) {
102
+ REQUIRE(input[i] == output[i]);
103
+ }
104
+ }
105
+ }
106
+ }
107
+
108
+ TEST_CASE("pack blocks unpack bits") {
109
+ uint64_t value = 111; // arbitrary starting value
110
+ for (int m = 0; m < 10000; ++m) {
111
+ for (uint8_t bits = 1; bits <= 63; ++bits) {
112
+ const uint64_t mask = (1ULL << bits) - 1;
113
+ std::vector<uint64_t> input(8, 0);
114
+ for (int i = 0; i < 8; ++i) {
115
+ input[i] = value & mask;
116
+ value += IGOLDEN64;
117
+ }
118
+ std::vector<uint8_t> bytes(bits, 0);
119
+ pack_bits_block8(input.data(), bytes.data(), bits);
120
+ std::vector<uint64_t> output(8, 0);
121
+ uint8_t offset = 0;
122
+ const uint8_t* cptr = bytes.data();
123
+ for (int i = 0; i < 8; ++i) {
124
+ offset = unpack_bits(output[i], bits, cptr, offset);
125
+ }
126
+ for (int i = 0; i < 8; ++i) {
127
+ REQUIRE(input[i] == output[i]);
128
+ }
129
+ }
130
+ }
131
+ }
132
+
83
133
  } /* namespace datasketches */
@@ -241,6 +241,28 @@ TEST_CASE("theta a-not-b: seed mismatch", "[theta_a_not_b]") {
241
241
  REQUIRE_THROWS_AS(a_not_b.compute(sketch, sketch), std::invalid_argument);
242
242
  }
243
243
 
244
+ TEST_CASE("theta a-not-b: empty B with different seed must be ignored", "[theta_a_not_b]") {
245
+ // An empty sketch carries no hashes, so its seed hash is meaningless. Deserialization,
246
+ // theta_union::update() and theta_intersection::update() all skip the seed hash check
247
+ // for empty inputs; a-not-b must do the same. This matters across languages because
248
+ // datasketches-java serializes every empty compact sketch with a seed hash of 0.
249
+
250
+ // A: non-empty, but with zero retained entries (all hashes exceeded theta)
251
+ update_theta_sketch a = update_theta_sketch::builder().set_p(1e-6f).build();
252
+ a.update(1);
253
+ REQUIRE_FALSE(a.is_empty());
254
+ REQUIRE(a.get_num_retained() == 0);
255
+
256
+ // B: empty, built with a different seed
257
+ compact_theta_sketch b = update_theta_sketch::builder().set_seed(123).build().compact();
258
+ REQUIRE(b.is_empty());
259
+
260
+ theta_a_not_b a_not_b; // default seed
261
+ compact_theta_sketch result = a_not_b.compute(a.compact(), b);
262
+ REQUIRE_FALSE(result.is_empty());
263
+ REQUIRE(result.get_num_retained() == 0);
264
+ }
265
+
244
266
  TEST_CASE("theta a-not-b: issue #152", "[theta_a_not_b]") {
245
267
  update_theta_sketch a = update_theta_sketch::builder().build();
246
268
  int value = 0;