diff --git a/include/roaring/roaring64.h b/include/roaring/roaring64.h index acf16d65b..edf1f1b77 100644 --- a/include/roaring/roaring64.h +++ b/include/roaring/roaring64.h @@ -272,6 +272,12 @@ uint64_t roaring64_bitmap_get_cardinality(const roaring64_bitmap_t *r); uint64_t roaring64_bitmap_range_cardinality(const roaring64_bitmap_t *r, uint64_t min, uint64_t max); +/** + * Returns the number of elements in the range [min, max] + */ +uint64_t roaring64_bitmap_range_closed_cardinality(const roaring64_bitmap_t *r, + uint64_t min, uint64_t max); + /** * Returns true if the bitmap is empty (cardinality is zero). */ diff --git a/src/roaring64.c b/src/roaring64.c index 3828abfa0..2dc68131a 100644 --- a/src/roaring64.c +++ b/src/roaring64.c @@ -724,7 +724,17 @@ uint64_t roaring64_bitmap_range_cardinality(const roaring64_bitmap_t *r, if (min >= max) { return 0; } - max--; // A closed range is easier to work with. + // Covert to a closed range + // No underflow here: passing the above condition implies min < max, so + // there is a number less than max + return roaring64_bitmap_range_closed_cardinality(r, min, max - 1); +} + +uint64_t roaring64_bitmap_range_closed_cardinality(const roaring64_bitmap_t *r, + uint64_t min, uint64_t max) { + if (min > max) { + return 0; + } uint64_t cardinality = 0; uint8_t min_high48[ART_KEY_BYTES]; diff --git a/tests/roaring64_unit.cpp b/tests/roaring64_unit.cpp index 7233b293e..4c481d3f0 100644 --- a/tests/roaring64_unit.cpp +++ b/tests/roaring64_unit.cpp @@ -727,9 +727,29 @@ DEFINE_TEST(test_range_cardinality) { // range is exclusive, so UINT64_MAX is not included assert_int_equal( roaring64_bitmap_range_cardinality(r, start, UINT64_MAX), 4); + // With an inclusive range, UINT64_MAX _is_ included + assert_int_equal( + roaring64_bitmap_range_closed_cardinality(r, start, UINT64_MAX), 5); roaring64_bitmap_free(r); } + { + // Empty ranges always have zero cardinality + roaring64_bitmap_t* r = + roaring64_bitmap_from(0, 1, 2, 3, 4, 5, UINT64_MAX); + assert_int_equal(roaring64_bitmap_range_cardinality(r, 1, 1), 0); + assert_int_equal(roaring64_bitmap_range_cardinality(r, 1, 0), 0); + assert_int_equal(roaring64_bitmap_range_cardinality(r, UINT64_MAX, 0), + 0); + assert_int_equal( + roaring64_bitmap_range_cardinality(r, UINT64_MAX, UINT64_MAX), 0); + assert_int_equal(roaring64_bitmap_range_closed_cardinality(r, 1, 0), 0); + assert_int_equal( + roaring64_bitmap_range_closed_cardinality(r, UINT64_MAX, 0), 0); + assert_int_equal(roaring64_bitmap_range_closed_cardinality( + r, UINT64_MAX, UINT64_MAX - 1), + 0); + } } DEFINE_TEST(test_is_empty) {