From da633fc8be0b044bc1498e4fa6e38f4616d6a27a Mon Sep 17 00:00:00 2001 From: lani_karrot Date: Fri, 6 Nov 2026 17:10:43 +0900 Subject: [PATCH 1/4] fix: stop REQ section growth at the minimum section size --- req/include/req_compactor_impl.hpp | 4 +++- req/test/req_sketch_test.cpp | 38 ++++++++++++++++++++++-------- 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/req/include/req_compactor_impl.hpp b/req/include/req_compactor_impl.hpp index 72aafc94..e37080c3 100755 --- a/req/include/req_compactor_impl.hpp +++ b/req/include/req_compactor_impl.hpp @@ -301,7 +301,9 @@ template bool req_compactor::ensure_enough_sections() { const float ssr = section_size_raw_ / sqrtf(2); const uint32_t ne = nearest_even(ssr); - if (state_ >= static_cast(1ULL << (num_sections_ - 1)) && ne >= req_constants::MIN_K) { + if (state_ >= 1ULL << (num_sections_ - 1) + && section_size_ > req_constants::MIN_K + && ne >= req_constants::MIN_K) { section_size_raw_ = ssr; section_size_ = ne; num_sections_ <<= 1; diff --git a/req/test/req_sketch_test.cpp b/req/test/req_sketch_test.cpp index c8cf5cbe..af9efc85 100755 --- a/req/test/req_sketch_test.cpp +++ b/req/test/req_sketch_test.cpp @@ -581,15 +581,33 @@ TEST_CASE("get_rank equivalence") { } } -//TEST_CASE("for manual comparison with Java") { -// req_sketch sketch(12, false); -// for (size_t i = 0; i < 100000; ++i) sketch.update(i); -// sketch.merge(sketch); -// std::ofstream os; -// os.exceptions(std::ios::failbit | std::ios::badbit); -// os.open("req_float_lra_12_100000_merged.sk", std::ios::binary); -// sketch.get_quantile(0.5); // force sorting level 0 -// sketch.serialize(os); -//} +using req_compactor_float = req_compactor, std::allocator>; + +static req_compactor_float make_compactor_with_state(uint64_t state) { + std::stringstream s(std::ios::in | std::ios::out | std::ios::binary); + const float section_size_raw = req_constants::MIN_K; // not used by merge + const uint8_t lg_weight = 0; + const uint8_t num_sections = req_constants::INIT_NUM_SECTIONS; + const uint16_t padding = 0; + const uint32_t num_items = 0; + s.write(reinterpret_cast(&state), sizeof(state)); + s.write(reinterpret_cast(§ion_size_raw), sizeof(section_size_raw)); + s.write(reinterpret_cast(&lg_weight), sizeof(lg_weight)); + s.write(reinterpret_cast(&num_sections), sizeof(num_sections)); + s.write(reinterpret_cast(&padding), sizeof(padding)); + s.write(reinterpret_cast(&num_items), sizeof(num_items)); + return req_compactor_float::deserialize(s, serde(), std::less(), std::allocator(), true, true); +} + +TEST_CASE("req compactor: section growth stops at minimum section size", "[req_compactor]") { + const uint16_t ks[] = {6, 12, 14}; + const uint32_t expected_nom_capacities[] = {48, 192, 192}; + for (size_t i = 0; i < 3; ++i) { + req_compactor_float c(true, 0, ks[i], std::less(), std::allocator()); + for (uint8_t lg_state: {2, 5, 11, 23}) c.merge(make_compactor_with_state(1ULL << lg_state)); + INFO("k=" << ks[i]); + CHECK(c.get_nom_capacity() == expected_nom_capacities[i]); + } +} } /* namespace datasketches */ From 9be4f5ffcecb7e49dcd34308b6aaae24de5644f5 Mon Sep 17 00:00:00 2001 From: lani_karrot Date: Fri, 6 Nov 2026 17:12:15 +0900 Subject: [PATCH 2/4] fix: cast --- req/include/req_compactor_impl.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/req/include/req_compactor_impl.hpp b/req/include/req_compactor_impl.hpp index e37080c3..eb76d98e 100755 --- a/req/include/req_compactor_impl.hpp +++ b/req/include/req_compactor_impl.hpp @@ -301,7 +301,7 @@ template bool req_compactor::ensure_enough_sections() { const float ssr = section_size_raw_ / sqrtf(2); const uint32_t ne = nearest_even(ssr); - if (state_ >= 1ULL << (num_sections_ - 1) + if (state_ >= static_cast(1ULL << (num_sections_ - 1)) && section_size_ > req_constants::MIN_K && ne >= req_constants::MIN_K) { section_size_raw_ = ssr; From bd03985345ef0f7b1a6c03fd69ed2763f4d1b690 Mon Sep 17 00:00:00 2001 From: lani_karrot Date: Sat, 7 Nov 2026 15:30:57 +0900 Subject: [PATCH 3/4] test: revert dead test code --- req/test/req_sketch_test.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/req/test/req_sketch_test.cpp b/req/test/req_sketch_test.cpp index af9efc85..fc2657a8 100755 --- a/req/test/req_sketch_test.cpp +++ b/req/test/req_sketch_test.cpp @@ -581,6 +581,17 @@ TEST_CASE("get_rank equivalence") { } } +//TEST_CASE("for manual comparison with Java") { +// req_sketch sketch(12, false); +// for (size_t i = 0; i < 100000; ++i) sketch.update(i); +// sketch.merge(sketch); +// std::ofstream os; +// os.exceptions(std::ios::failbit | std::ios::badbit); +// os.open("req_float_lra_12_100000_merged.sk", std::ios::binary); +// sketch.get_quantile(0.5); // force sorting level 0 +// sketch.serialize(os); +//} + using req_compactor_float = req_compactor, std::allocator>; static req_compactor_float make_compactor_with_state(uint64_t state) { From 6d4156956ac1278f561f921146a4284a36984194 Mon Sep 17 00:00:00 2001 From: lani_karrot Date: Sat, 7 Nov 2026 15:39:49 +0900 Subject: [PATCH 4/4] test: pass k and level to req_compactor::deserialize --- req/test/req_sketch_test.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/req/test/req_sketch_test.cpp b/req/test/req_sketch_test.cpp index 0575c340..3db56247 100755 --- a/req/test/req_sketch_test.cpp +++ b/req/test/req_sketch_test.cpp @@ -735,9 +735,9 @@ TEST_CASE("req sketch: deserialize rejects inconsistent compactors", "[req_sketc using req_compactor_float = req_compactor, std::allocator>; -static req_compactor_float make_compactor_with_state(uint64_t state) { +static req_compactor_float make_compactor_with_state(uint16_t k, uint64_t state) { std::stringstream s(std::ios::in | std::ios::out | std::ios::binary); - const float section_size_raw = req_constants::MIN_K; // not used by merge + const float section_size_raw = k; // not used by merge const uint8_t lg_weight = 0; const uint8_t num_sections = req_constants::INIT_NUM_SECTIONS; const uint16_t padding = 0; @@ -748,7 +748,8 @@ static req_compactor_float make_compactor_with_state(uint64_t state) { s.write(reinterpret_cast(&num_sections), sizeof(num_sections)); s.write(reinterpret_cast(&padding), sizeof(padding)); s.write(reinterpret_cast(&num_items), sizeof(num_items)); - return req_compactor_float::deserialize(s, serde(), std::less(), std::allocator(), true, true); + return req_compactor_float::deserialize(s, serde(), std::less(), std::allocator(), true, true, + k, 0); } TEST_CASE("req compactor: section growth stops at minimum section size", "[req_compactor]") { @@ -756,7 +757,7 @@ TEST_CASE("req compactor: section growth stops at minimum section size", "[req_c const uint32_t expected_nom_capacities[] = {48, 192, 192}; for (size_t i = 0; i < 3; ++i) { req_compactor_float c(true, 0, ks[i], std::less(), std::allocator()); - for (uint8_t lg_state: {2, 5, 11, 23}) c.merge(make_compactor_with_state(1ULL << lg_state)); + for (uint8_t lg_state: {2, 5, 11, 23}) c.merge(make_compactor_with_state(ks[i], 1ULL << lg_state)); INFO("k=" << ks[i]); CHECK(c.get_nom_capacity() == expected_nom_capacities[i]); }