@@ -443,6 +443,9 @@ TEST_CASE("kll sketch", "[kll_sketch]") {
443443 REQUIRE (kll_helper::floor_of_log2_of_fraction (6 , 2 ) == 1 );
444444 REQUIRE (kll_helper::floor_of_log2_of_fraction (7 , 2 ) == 1 );
445445 REQUIRE (kll_helper::floor_of_log2_of_fraction (8 , 2 ) == 2 );
446+ REQUIRE (kll_helper::floor_of_log2_of_fraction (1ULL << 63 , 1 ) == 63 );
447+ REQUIRE (kll_helper::floor_of_log2_of_fraction (std::numeric_limits<uint64_t >::max (), 1 ) == 63 );
448+ REQUIRE (kll_helper::floor_of_log2_of_fraction (std::numeric_limits<uint64_t >::max (), 3 ) == 62 );
446449 }
447450
448451 SECTION (" out of order split points, float" ) {
@@ -831,6 +834,123 @@ TEST_CASE("kll sketch", "[kll_sketch]") {
831834 REQUIRE (sb.get_n () == 3 );
832835 }
833836
837+ SECTION (" weighted update: zero weight" ) {
838+ kll_float_sketch sketch (200 , std::less<float >(), 0 );
839+ REQUIRE_THROWS_AS (sketch.update (1 .0f , 0 ), std::invalid_argument);
840+ REQUIRE (sketch.is_empty ());
841+ }
842+
843+ SECTION (" weighted update: NaN" ) {
844+ kll_float_sketch sketch (200 , std::less<float >(), 0 );
845+ sketch.update (std::numeric_limits<float >::quiet_NaN (), 1000 );
846+ REQUIRE (sketch.is_empty ());
847+ }
848+
849+ SECTION (" weighted update: same as repeated updates in exact mode" ) {
850+ kll_float_sketch weighted (200 , std::less<float >(), 0 );
851+ kll_float_sketch repeated (200 , std::less<float >(), 0 );
852+ for (int i = 1 ; i <= 15 ; ++i) {
853+ weighted.update (static_cast <float >(i), i);
854+ for (int j = 0 ; j < i; ++j) repeated.update (static_cast <float >(i));
855+ }
856+ REQUIRE (weighted.get_n () == repeated.get_n ());
857+ REQUIRE (weighted.get_num_retained () == repeated.get_num_retained ());
858+ REQUIRE_FALSE (weighted.is_estimation_mode ());
859+ for (int i = 0 ; i <= 16 ; ++i) {
860+ REQUIRE (weighted.get_rank (static_cast <float >(i)) == repeated.get_rank (static_cast <float >(i)));
861+ REQUIRE (weighted.get_rank (static_cast <float >(i), false ) == repeated.get_rank (static_cast <float >(i), false ));
862+ }
863+ }
864+
865+ SECTION (" weighted update: one item with large weight" ) {
866+ kll_float_sketch sketch (200 , std::less<float >(), 0 );
867+ const uint64_t weight = (1ULL << 40 ) + 12345 ;
868+ sketch.update (7 .0f , weight);
869+ REQUIRE (sketch.get_n () == weight);
870+ REQUIRE (sketch.get_num_retained () == 7 ); // number of bits set in the weight
871+ REQUIRE (sketch.get_min_item () == 7 .0f );
872+ REQUIRE (sketch.get_max_item () == 7 .0f );
873+ REQUIRE (sketch.get_rank (7 .0f ) == 1 );
874+ REQUIRE (sketch.get_rank (7 .0f , false ) == 0 );
875+ REQUIRE (sketch.get_quantile (0.5 ) == 7 .0f );
876+
877+ uint64_t total_weight = 0 ;
878+ for (auto pair: sketch) total_weight += pair.second ;
879+ REQUIRE (total_weight == weight);
880+ }
881+
882+ SECTION (" weighted update: maximum weight" ) {
883+ kll_float_sketch sketch (200 , std::less<float >(), 0 );
884+ sketch.update (1 .0f );
885+ sketch.update (2 .0f , std::numeric_limits<uint64_t >::max () - 1 );
886+ REQUIRE (sketch.get_n () == std::numeric_limits<uint64_t >::max ());
887+ REQUIRE (sketch.get_min_item () == 1 .0f );
888+ REQUIRE (sketch.get_max_item () == 2 .0f );
889+ REQUIRE (sketch.get_quantile (0.5 ) == 2 .0f );
890+
891+ uint64_t total_weight = 0 ;
892+ for (auto pair: sketch) total_weight += pair.second ;
893+ REQUIRE (total_weight == sketch.get_n ());
894+
895+ auto bytes = sketch.serialize ();
896+ auto sketch2 = kll_float_sketch::deserialize (bytes.data (), bytes.size (), serde<float >(), std::less<float >(), 0 );
897+ REQUIRE (sketch2.get_n () == sketch.get_n ());
898+ REQUIRE (sketch2.get_num_retained () == sketch.get_num_retained ());
899+ }
900+
901+ SECTION (" weighted update: large weights into a full sketch" ) {
902+ kll_float_sketch sketch (200 , std::less<float >(), 0 );
903+ for (int i = 0 ; i < 1000 ; ++i) sketch.update (static_cast <float >(i));
904+ sketch.update (-1 .0f , 1000 );
905+ sketch.update (2000 .0f , 3000 );
906+ REQUIRE (sketch.get_n () == 5000 );
907+ REQUIRE (sketch.get_min_item () == -1 .0f );
908+ REQUIRE (sketch.get_max_item () == 2000 .0f );
909+ REQUIRE (sketch.get_rank (-1 .0f ) == Approx (0.2 ).margin (RANK_EPS_FOR_K_200 ));
910+ REQUIRE (sketch.get_rank (2000 .0f , false ) == Approx (0.4 ).margin (RANK_EPS_FOR_K_200 ));
911+ }
912+
913+ SECTION (" weighted update: estimation mode" ) {
914+ kll_float_sketch sketch (200 , std::less<float >(), 0 );
915+ const int n = 10000 ;
916+ std::vector<uint64_t > weights (n);
917+ uint64_t total = 0 ;
918+ for (int i = 0 ; i < n; ++i) {
919+ weights[i] = static_cast <uint64_t >(i % 13 ) * 1000 + 1 ;
920+ total += weights[i];
921+ sketch.update (static_cast <float >(i), weights[i]);
922+ }
923+ REQUIRE (sketch.get_n () == total);
924+ REQUIRE (sketch.is_estimation_mode ());
925+ REQUIRE (sketch.get_min_item () == 0 );
926+ REQUIRE (sketch.get_max_item () == n - 1 );
927+
928+ uint64_t weight_below = 0 ;
929+ for (int i = 0 ; i < n; ++i) {
930+ const double true_rank = static_cast <double >(weight_below) / total;
931+ REQUIRE (sketch.get_rank (static_cast <float >(i), false ) == Approx (true_rank).margin (RANK_EPS_FOR_K_200 ));
932+ weight_below += weights[i];
933+ }
934+
935+ auto bytes = sketch.serialize ();
936+ auto sketch2 = kll_float_sketch::deserialize (bytes.data (), bytes.size (), serde<float >(), std::less<float >(), 0 );
937+ REQUIRE (sketch2.get_n () == sketch.get_n ());
938+ REQUIRE (sketch2.get_num_retained () == sketch.get_num_retained ());
939+ REQUIRE (sketch2.get_quantile (0.5 ) == sketch.get_quantile (0.5 ));
940+ }
941+
942+ SECTION (" weighted update: strings" ) {
943+ kll_string_sketch sketch (200 , std::less<std::string>(), 0 );
944+ sketch.update (std::string (" b" ), 3 );
945+ const std::string a (" a" );
946+ sketch.update (a, 1000 );
947+ sketch.update (std::string (" c" ), 1ULL << 20 );
948+ REQUIRE (sketch.get_n () == 3 + 1000 + (1ULL << 20 ));
949+ REQUIRE (sketch.get_min_item () == " a" );
950+ REQUIRE (sketch.get_max_item () == " c" );
951+ REQUIRE (sketch.get_rank (" a" ) == Approx (1000.0 / sketch.get_n ()).margin (RANK_EPS_FOR_K_200 ));
952+ }
953+
834954 // cleanup
835955 REQUIRE (test_allocator_total_bytes == 0 );
836956}
0 commit comments