blob: 6a51d29ac16eadba9f12c0e43f56034868917ca1 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "paimon/utils/roaring_bitmap64.h"
#include <climits>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <memory>
#include <utility>
#include "gtest/gtest.h"
#include "paimon/io/byte_array_input_stream.h"
#include "paimon/result.h"
#include "paimon/testing/utils/testharness.h"
#include "paimon/utils/range.h"
namespace paimon::test {
TEST(RoaringBitmap64Test, TestSimple) {
RoaringBitmap64 roaring;
ASSERT_TRUE(roaring.IsEmpty());
roaring.Add(4147483647l);
roaring.Add(614748364721l);
ASSERT_TRUE(roaring.Contains(4147483647l));
ASSERT_TRUE(roaring.Contains(614748364721l));
ASSERT_FALSE(roaring.CheckedAdd(614748364721l));
ASSERT_TRUE(roaring.CheckedAdd(8147483647210l));
ASSERT_TRUE(roaring.Contains(614748364721l));
ASSERT_FALSE(roaring.IsEmpty());
ASSERT_EQ("{4147483647,614748364721,8147483647210}", roaring.ToString());
ASSERT_EQ(3, roaring.Cardinality());
roaring.AddRange(614748364720, 614748364725);
ASSERT_EQ(
"{4147483647,614748364720,614748364721,614748364722,614748364723,614748364724,"
"8147483647210}",
roaring.ToString());
ASSERT_EQ(7, roaring.Cardinality());
roaring.RemoveRange(614748364721l, 614748364723l);
ASSERT_EQ("{4147483647,614748364720,614748364723,614748364724,8147483647210}",
roaring.ToString());
ASSERT_EQ(5, roaring.Cardinality());
}
TEST(RoaringBitmap64Test, TestCompatibleWithJava) {
auto pool = GetDefaultPool();
{
RoaringBitmap64 roaring;
for (int64_t i = 0; i < 100; i++) {
roaring.Add(i + 100000000000l);
}
for (int64_t i = 0; i < 100; i++) {
ASSERT_TRUE(roaring.Contains(i + 100000000000l));
}
auto bytes = roaring.Serialize(pool.get());
std::vector<uint8_t> result(bytes->data(), bytes->data() + bytes->size());
std::vector<uint8_t> expected = {1, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 59, 48,
0, 0, 1, 118, 72, 99, 0, 1, 0, 0, 232, 99, 0};
ASSERT_EQ(result, expected);
RoaringBitmap64 de_roaring;
ASSERT_OK(de_roaring.Deserialize(bytes->data(), bytes->size()));
for (int64_t i = 0; i < 100; i++) {
ASSERT_TRUE(de_roaring.Contains(i + 100000000000l));
}
}
{
RoaringBitmap64 roaring;
for (int64_t i = RoaringBitmap64::MAX_VALUE - 1; i > RoaringBitmap64::MAX_VALUE - 101;
i--) {
roaring.Add(i);
}
for (int64_t i = RoaringBitmap64::MAX_VALUE - 1; i > RoaringBitmap64::MAX_VALUE - 101;
i--) {
ASSERT_TRUE(roaring.Contains(i));
}
auto bytes = roaring.Serialize(pool.get());
std::vector<uint8_t> result(bytes->data(), bytes->data() + bytes->size());
std::vector<uint8_t> expected = {1, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 127, 59, 48,
0, 0, 1, 255, 255, 99, 0, 1, 0, 155, 255, 99, 0};
ASSERT_EQ(result, expected);
RoaringBitmap64 de_roaring;
ASSERT_OK(de_roaring.Deserialize(bytes->data(), bytes->size()));
for (int64_t i = RoaringBitmap64::MAX_VALUE - 1; i > RoaringBitmap64::MAX_VALUE - 101;
i--) {
ASSERT_TRUE(de_roaring.Contains(i));
}
}
{
RoaringBitmap64 roaring;
for (int64_t i = 5000; i < 10000; i += 17) {
roaring.Add(i + 300000000000l);
}
for (int64_t i = 5000; i < 10000; i += 17) {
ASSERT_TRUE(roaring.Contains(i + 300000000000l));
}
auto bytes = roaring.Serialize(pool.get());
std::vector<uint8_t> result(bytes->data(), bytes->data() + bytes->size());
std::vector<uint8_t> expected = {
1, 0, 0, 0, 0, 0, 0, 0, 69, 0, 0, 0, 58, 48, 0, 0, 1,
0, 0, 0, 100, 217, 38, 1, 16, 0, 0, 0, 136, 203, 153, 203, 170, 203,
187, 203, 204, 203, 221, 203, 238, 203, 255, 203, 16, 204, 33, 204, 50, 204, 67,
204, 84, 204, 101, 204, 118, 204, 135, 204, 152, 204, 169, 204, 186, 204, 203, 204,
220, 204, 237, 204, 254, 204, 15, 205, 32, 205, 49, 205, 66, 205, 83, 205, 100,
205, 117, 205, 134, 205, 151, 205, 168, 205, 185, 205, 202, 205, 219, 205, 236, 205,
253, 205, 14, 206, 31, 206, 48, 206, 65, 206, 82, 206, 99, 206, 116, 206, 133,
206, 150, 206, 167, 206, 184, 206, 201, 206, 218, 206, 235, 206, 252, 206, 13, 207,
30, 207, 47, 207, 64, 207, 81, 207, 98, 207, 115, 207, 132, 207, 149, 207, 166,
207, 183, 207, 200, 207, 217, 207, 234, 207, 251, 207, 12, 208, 29, 208, 46, 208,
63, 208, 80, 208, 97, 208, 114, 208, 131, 208, 148, 208, 165, 208, 182, 208, 199,
208, 216, 208, 233, 208, 250, 208, 11, 209, 28, 209, 45, 209, 62, 209, 79, 209,
96, 209, 113, 209, 130, 209, 147, 209, 164, 209, 181, 209, 198, 209, 215, 209, 232,
209, 249, 209, 10, 210, 27, 210, 44, 210, 61, 210, 78, 210, 95, 210, 112, 210,
129, 210, 146, 210, 163, 210, 180, 210, 197, 210, 214, 210, 231, 210, 248, 210, 9,
211, 26, 211, 43, 211, 60, 211, 77, 211, 94, 211, 111, 211, 128, 211, 145, 211,
162, 211, 179, 211, 196, 211, 213, 211, 230, 211, 247, 211, 8, 212, 25, 212, 42,
212, 59, 212, 76, 212, 93, 212, 110, 212, 127, 212, 144, 212, 161, 212, 178, 212,
195, 212, 212, 212, 229, 212, 246, 212, 7, 213, 24, 213, 41, 213, 58, 213, 75,
213, 92, 213, 109, 213, 126, 213, 143, 213, 160, 213, 177, 213, 194, 213, 211, 213,
228, 213, 245, 213, 6, 214, 23, 214, 40, 214, 57, 214, 74, 214, 91, 214, 108,
214, 125, 214, 142, 214, 159, 214, 176, 214, 193, 214, 210, 214, 227, 214, 244, 214,
5, 215, 22, 215, 39, 215, 56, 215, 73, 215, 90, 215, 107, 215, 124, 215, 141,
215, 158, 215, 175, 215, 192, 215, 209, 215, 226, 215, 243, 215, 4, 216, 21, 216,
38, 216, 55, 216, 72, 216, 89, 216, 106, 216, 123, 216, 140, 216, 157, 216, 174,
216, 191, 216, 208, 216, 225, 216, 242, 216, 3, 217, 20, 217, 37, 217, 54, 217,
71, 217, 88, 217, 105, 217, 122, 217, 139, 217, 156, 217, 173, 217, 190, 217, 207,
217, 224, 217, 241, 217, 2, 218, 19, 218, 36, 218, 53, 218, 70, 218, 87, 218,
104, 218, 121, 218, 138, 218, 155, 218, 172, 218, 189, 218, 206, 218, 223, 218, 240,
218, 1, 219, 18, 219, 35, 219, 52, 219, 69, 219, 86, 219, 103, 219, 120, 219,
137, 219, 154, 219, 171, 219, 188, 219, 205, 219, 222, 219, 239, 219, 0, 220, 17,
220, 34, 220, 51, 220, 68, 220, 85, 220, 102, 220, 119, 220, 136, 220, 153, 220,
170, 220, 187, 220, 204, 220, 221, 220, 238, 220, 255, 220, 16, 221, 33, 221, 50,
221, 67, 221, 84, 221, 101, 221, 118, 221, 135, 221, 152, 221, 169, 221, 186, 221,
203, 221, 220, 221, 237, 221, 254, 221, 15, 222, 32, 222, 49, 222, 66, 222, 83,
222, 100, 222, 117, 222, 134, 222, 151, 222, 168, 222, 185, 222, 202, 222, 219, 222,
236, 222, 253, 222, 14, 223};
ASSERT_EQ(result, expected);
RoaringBitmap64 de_roaring;
ASSERT_OK(de_roaring.Deserialize(bytes->data(), bytes->size()));
for (int64_t i = 5000; i < 10000; i += 17) {
ASSERT_TRUE(de_roaring.Contains(i + 300000000000l));
}
}
// empty
{
RoaringBitmap64 roaring;
auto bytes = roaring.Serialize(pool.get());
std::vector<uint8_t> result(bytes->data(), bytes->data() + bytes->size());
std::vector<uint8_t> expected = {0, 0, 0, 0, 0, 0, 0, 0};
ASSERT_EQ(result, expected);
RoaringBitmap64 de_roaring;
ASSERT_OK(de_roaring.Deserialize(bytes->data(), bytes->size()));
ASSERT_TRUE(de_roaring.IsEmpty());
ASSERT_FALSE(de_roaring.Contains(58));
}
}
TEST(RoaringBitmap64Test, TestDeserializeFailed) {
RoaringBitmap64 roaring;
std::vector<char> invalid_bytes = {0, 100};
ASSERT_NOK_WITH_MSG(roaring.Deserialize(invalid_bytes.data(), invalid_bytes.size()),
"catch exception in Deserialize() of RoaringBitmap64");
}
TEST(RoaringBitmap64Test, TestAndOr) {
RoaringBitmap64 roaring1 = RoaringBitmap64::From({100000000000l, 100000000000000l});
RoaringBitmap64 roaring2 =
RoaringBitmap64::From({200000000000l, 100000000000000l, 200000000000000l});
auto and_roaring = RoaringBitmap64::And(roaring1, roaring2);
ASSERT_EQ(and_roaring, RoaringBitmap64::From({100000000000000l}));
auto or_roaring = RoaringBitmap64::Or(roaring1, roaring2);
ASSERT_EQ(or_roaring, RoaringBitmap64::From(
{100000000000l, 200000000000l, 100000000000000l, 200000000000000l}));
}
TEST(RoaringBitmap64Test, TestAssignAndMove) {
RoaringBitmap64 roaring1 = RoaringBitmap64::From({100000000000l, 200000000000000l});
RoaringBitmap64 roaring2 = roaring1;
ASSERT_EQ(roaring1, roaring2);
ASSERT_FALSE(roaring1.IsEmpty());
ASSERT_FALSE(roaring2.IsEmpty());
RoaringBitmap64 roaring3 = std::move(roaring1);
ASSERT_EQ(roaring3, roaring2);
ASSERT_FALSE(roaring1.roaring_bitmap_); // NOLINT(bugprone-use-after-move)
roaring3.Add(20);
ASSERT_FALSE(roaring3 == roaring2);
roaring3 = roaring2;
ASSERT_EQ(roaring3, roaring2);
roaring2 = std::move(roaring3);
ASSERT_FALSE(roaring3.roaring_bitmap_); // NOLINT(bugprone-use-after-move)
ASSERT_EQ("{100000000000,200000000000000}", roaring2.ToString());
roaring3 = roaring2;
ASSERT_EQ("{100000000000,200000000000000}", roaring3.ToString());
roaring3 = std::move(roaring2);
ASSERT_EQ("{100000000000,200000000000000}", roaring3.ToString());
ASSERT_FALSE(roaring2.roaring_bitmap_); // NOLINT(bugprone-use-after-move)
}
TEST(RoaringBitmap64Test, TestFastUnion) {
RoaringBitmap64 roaring1 = RoaringBitmap64::From({100000000000l, 200000000000000l});
RoaringBitmap64 roaring2 = RoaringBitmap64::From(
{1l, 100000000000l, 150000000000l, 200000000000000l, 300000000000000l});
RoaringBitmap64 roaring3 = RoaringBitmap64::From({2l, 200000000000000l, 800l});
RoaringBitmap64 res = RoaringBitmap64::FastUnion({&roaring1, &roaring2, &roaring3});
ASSERT_EQ(res, RoaringBitmap64::From({1l, 2l, 100000000000l, 150000000000l, 200000000000000l,
300000000000000l, 800l}));
std::vector<RoaringBitmap64> roarings = {roaring1, roaring2, roaring3};
RoaringBitmap64 res2 = RoaringBitmap64::FastUnion(roarings);
ASSERT_EQ(res2, RoaringBitmap64::From({1l, 2l, 100000000000l, 150000000000l, 200000000000000l,
300000000000000l, 800l}));
}
TEST(RoaringBitmap64Test, TestFlip) {
RoaringBitmap64 roaring =
RoaringBitmap64::From({1000000000001l, 1000000000002l, 1000000000004l});
roaring.Flip(1000000000000l, 1000000000005l);
ASSERT_EQ(roaring, RoaringBitmap64::From({1000000000000l, 1000000000003l}));
}
TEST(RoaringBitmap64Test, TestAndNot) {
RoaringBitmap64 roaring1 = RoaringBitmap64::From(
{10000000000010l, 10000000000020l, 100000000000100l, 100000000000200l});
RoaringBitmap64 roaring2 = RoaringBitmap64::From(
{1000000000005l, 10000000000020l, 10000000000080l, 100000000000200l, 100000000000250l});
auto andnot_roaring = RoaringBitmap64::AndNot(roaring1, roaring2);
ASSERT_EQ(andnot_roaring, RoaringBitmap64::From({10000000000010l, 100000000000100l}));
}
TEST(RoaringBitmap64Test, TestIterator) {
RoaringBitmap64 roaring = RoaringBitmap64::From({100000000000l, 200000000000l});
auto iter = roaring.Begin();
ASSERT_EQ(*iter, 100000000000l);
auto iter2 = ++iter;
ASSERT_EQ(*iter, 200000000000l);
ASSERT_EQ(*iter2, 200000000000l);
++iter;
ASSERT_EQ(iter, roaring.End());
iter = roaring.EqualOrLarger(5);
ASSERT_EQ(*iter, 100000000000l);
iter = roaring.EqualOrLarger(100000000000l);
ASSERT_EQ(*iter, 100000000000l);
iter = roaring.EqualOrLarger(150000000000l);
ASSERT_EQ(*iter, 200000000000l);
iter = roaring.EqualOrLarger(200000000000l);
ASSERT_EQ(*iter, 200000000000l);
iter = roaring.EqualOrLarger(200000000001l);
ASSERT_EQ(iter, roaring.End());
}
TEST(RoaringBitmap64Test, TestIteratorAssignAndMove) {
RoaringBitmap64 roaring =
RoaringBitmap64::From({10000000000010l, 100000000000100l, 100000000000200l});
auto iter1 = roaring.EqualOrLarger(10000000000010l);
auto iter2 = iter1;
ASSERT_EQ(iter1, iter2);
ASSERT_EQ(10000000000010l, *iter1);
ASSERT_EQ(10000000000010l, *iter2);
auto iter3 = std::move(iter1);
ASSERT_EQ(iter2, iter3);
ASSERT_FALSE(iter1.iterator_); // NOLINT(bugprone-use-after-move)
++iter3;
ASSERT_NE(iter3, iter2);
ASSERT_EQ(100000000000100l, *iter3);
iter3 = iter2;
ASSERT_EQ(iter3, iter2);
iter2 = std::move(iter3);
ASSERT_FALSE(iter3.iterator_); // NOLINT(bugprone-use-after-move)
ASSERT_EQ(10000000000010l, *iter2);
iter3 = iter2;
ASSERT_EQ(iter2, iter3);
ASSERT_EQ(10000000000010l, *iter2);
ASSERT_EQ(10000000000010l, *iter3);
iter3 = std::move(iter2);
ASSERT_EQ(10000000000010l, *iter3);
ASSERT_FALSE(iter2.iterator_); // NOLINT(bugprone-use-after-move)
}
TEST(RoaringBitmap64Test, TestDeserializeFromInputStream) {
RoaringBitmap64 roaring1 =
RoaringBitmap64::From({10000000000010l, 10000000000020l, 100000000000100l});
RoaringBitmap64 roaring2 = RoaringBitmap64::From(
{10000000000010l, 10000000000050l, 100000000000150l, 100000000000200l});
RoaringBitmap64 roaring3 =
RoaringBitmap64::From({1000000000002l, 1000000000004l, 1000000000006l, 1000000000001000l});
size_t len1 = roaring1.GetSizeInBytes();
size_t len2 = roaring2.GetSizeInBytes();
size_t len3 = roaring3.GetSizeInBytes();
auto pool = GetDefaultPool();
auto bytes1 = roaring1.Serialize(pool.get());
ASSERT_EQ(bytes1->size(), len1);
auto bytes2 = roaring2.Serialize(pool.get());
ASSERT_EQ(bytes2->size(), len2);
auto bytes3 = roaring3.Serialize(pool.get());
ASSERT_EQ(bytes3->size(), len3);
auto concat_bytes = std::make_shared<Bytes>(len1 + len2 + len3, pool.get());
memcpy(concat_bytes->data(), bytes1->data(), len1);
memcpy(concat_bytes->data() + len1, bytes2->data(), len2);
memcpy(concat_bytes->data() + len1 + len2, bytes3->data(), len3);
ByteArrayInputStream byte_array_input_stream(concat_bytes->data(), concat_bytes->size());
RoaringBitmap64 de_roaring1;
ASSERT_OK(de_roaring1.Deserialize(&byte_array_input_stream));
ASSERT_EQ(de_roaring1, roaring1);
ASSERT_EQ(byte_array_input_stream.GetPos().value(), len1);
RoaringBitmap64 de_roaring2;
ASSERT_OK(de_roaring2.Deserialize(&byte_array_input_stream));
ASSERT_EQ(de_roaring2, roaring2);
ASSERT_EQ(byte_array_input_stream.GetPos().value(), len1 + len2);
RoaringBitmap64 de_roaring3;
ASSERT_OK(de_roaring3.Deserialize(&byte_array_input_stream));
ASSERT_EQ(de_roaring3, roaring3);
ASSERT_EQ(byte_array_input_stream.GetPos().value(), len1 + len2 + len3);
}
TEST(RoaringBitmap64Test, TestHighCardinality) {
std::srand(time(nullptr));
auto pool = GetDefaultPool();
RoaringBitmap64 roaring;
for (int64_t i = 0; i < 10000; i++) {
roaring.Add(static_cast<int64_t>(std::numeric_limits<int32_t>::max()) + std::rand());
}
auto bytes = roaring.Serialize(pool.get());
RoaringBitmap64 de_roaring;
ASSERT_OK(de_roaring.Deserialize(bytes->data(), bytes->size()));
ASSERT_EQ(roaring, de_roaring);
}
TEST(RoaringBitmap64Test, TestInplaceAndOr) {
RoaringBitmap64 roaring = RoaringBitmap64::From({0l, 1l, 100000000000100l});
RoaringBitmap64 roaring1 = RoaringBitmap64::From({1l, 2l, 1000000000005l, 100000000000200l});
roaring |= roaring1;
ASSERT_EQ(roaring.ToString(), "{0,1,2,1000000000005,100000000000100,100000000000200}");
RoaringBitmap64 roaring2 =
RoaringBitmap64::From({1l, 2l, 3l, 1000000000005l, 100000000000100l, 100000000000500l});
roaring &= roaring2;
ASSERT_EQ(roaring.ToString(), "{1,2,1000000000005,100000000000100}");
RoaringBitmap64 roaring3 = RoaringBitmap64::From({2l, 100000000000100l});
roaring -= roaring3;
ASSERT_EQ(roaring.ToString(), "{1,1000000000005}");
}
TEST(RoaringBitmap64Test, TestContainsAny) {
RoaringBitmap64 roaring =
RoaringBitmap64::From({10000000000010l, 10000000000011l, 10000000000100l});
ASSERT_TRUE(roaring.ContainsAny(10000000000010l, 10000000000011l));
ASSERT_TRUE(roaring.ContainsAny(10000000000010l, 10000000000020l));
ASSERT_TRUE(roaring.ContainsAny(10000000000010l, 10000000000101l));
ASSERT_TRUE(roaring.ContainsAny(10000000000020l, 10000000000200l));
ASSERT_TRUE(roaring.ContainsAny(10000000000005l, 10000000000011l));
ASSERT_FALSE(roaring.ContainsAny(10000000000005l, 10000000000010l));
ASSERT_FALSE(roaring.ContainsAny(10000000000020l, 10000000000100l));
ASSERT_FALSE(roaring.ContainsAny(10000000000020l, 10000000000030l));
ASSERT_FALSE(roaring.ContainsAny(10000000000500l, 10000000000520l));
}
TEST(RoaringBitmap64Test, TestFromRoaringBitmap32) {
{
RoaringBitmap32 roaring32 = RoaringBitmap32::From({10, 20, 21});
RoaringBitmap64 roaring64(roaring32);
ASSERT_EQ(roaring64.ToString(), "{10,20,21}");
}
{
RoaringBitmap32 roaring32 = RoaringBitmap32::From({10, 20, 21});
RoaringBitmap64 roaring64;
roaring64 = roaring32;
ASSERT_EQ(roaring64.ToString(), "{10,20,21}");
}
}
TEST(RoaringBitmap64Test, TestIteratorEqualOrLarger) {
RoaringBitmap64 roaring = RoaringBitmap64::From({1l, 3l, 5l, 100l});
auto iter = roaring.Begin();
ASSERT_EQ(*iter, 1l);
iter.EqualOrLarger(5l);
ASSERT_EQ(*iter, 5l);
iter.EqualOrLarger(10l);
ASSERT_EQ(*iter, 100l);
iter.EqualOrLarger(200l);
ASSERT_EQ(iter, roaring.End());
}
// Helper function to convert a RoaringBitmap64 to a list of contiguous ranges.
static std::vector<Range> ToRangeList(const RoaringBitmap64& bitmap) {
std::vector<Range> ranges;
if (bitmap.IsEmpty()) {
return ranges;
}
int64_t current_start = -1;
int64_t current_end = -1;
for (auto it = bitmap.Begin(); it != bitmap.End(); ++it) {
int64_t value = *it;
if (current_start == -1) {
current_start = value;
current_end = value;
} else if (value == current_end + 1) {
current_end = value;
} else {
ranges.emplace_back(current_start, current_end);
current_start = value;
current_end = value;
}
}
if (current_start != -1) {
ranges.emplace_back(current_start, current_end);
}
return ranges;
}
TEST(RoaringBitmap64Test, TestAddRangeBasic) {
RoaringBitmap64 bitmap;
bitmap.AddRange(5, 11); // half-open interval [5, 11) == closed [5, 10]
ASSERT_EQ(bitmap.Cardinality(), 6);
ASSERT_FALSE(bitmap.Contains(4));
ASSERT_TRUE(bitmap.Contains(5));
ASSERT_TRUE(bitmap.Contains(7));
ASSERT_TRUE(bitmap.Contains(10));
ASSERT_FALSE(bitmap.Contains(11));
}
TEST(RoaringBitmap64Test, TestAddRangeSingleElement) {
RoaringBitmap64 bitmap;
bitmap.AddRange(100, 101); // half-open interval [100, 101) == single element 100
ASSERT_EQ(bitmap.Cardinality(), 1);
ASSERT_FALSE(bitmap.Contains(99));
ASSERT_TRUE(bitmap.Contains(100));
ASSERT_FALSE(bitmap.Contains(101));
}
TEST(RoaringBitmap64Test, TestAddRangeMultipleNonOverlapping) {
RoaringBitmap64 bitmap;
bitmap.AddRange(0, 6); // [0, 5]
bitmap.AddRange(10, 16); // [10, 15]
bitmap.AddRange(20, 26); // [20, 25]
ASSERT_EQ(bitmap.Cardinality(), 18);
ASSERT_FALSE(bitmap.Contains(6));
ASSERT_FALSE(bitmap.Contains(9));
ASSERT_FALSE(bitmap.Contains(16));
ASSERT_FALSE(bitmap.Contains(19));
ASSERT_TRUE(bitmap.Contains(0));
ASSERT_TRUE(bitmap.Contains(5));
ASSERT_TRUE(bitmap.Contains(10));
ASSERT_TRUE(bitmap.Contains(15));
ASSERT_TRUE(bitmap.Contains(20));
ASSERT_TRUE(bitmap.Contains(25));
std::vector<Range> ranges = ToRangeList(bitmap);
ASSERT_EQ(ranges.size(), 3);
ASSERT_EQ(ranges[0], Range(0, 5));
ASSERT_EQ(ranges[1], Range(10, 15));
ASSERT_EQ(ranges[2], Range(20, 25));
}
TEST(RoaringBitmap64Test, TestAddRangeLargeValues) {
RoaringBitmap64 bitmap;
int64_t start = static_cast<int64_t>(INT_MAX) + 100L;
int64_t end = static_cast<int64_t>(INT_MAX) + 200L;
bitmap.AddRange(start, end + 1); // half-open interval [start, end+1) == closed [start, end]
ASSERT_EQ(bitmap.Cardinality(), 101);
ASSERT_FALSE(bitmap.Contains(start - 1));
ASSERT_TRUE(bitmap.Contains(start));
ASSERT_TRUE(bitmap.Contains(start + 50));
ASSERT_TRUE(bitmap.Contains(end));
ASSERT_FALSE(bitmap.Contains(end + 1));
std::vector<int64_t> values;
for (auto it = bitmap.Begin(); it != bitmap.End(); ++it) {
values.push_back(*it);
}
ASSERT_EQ(values.size(), 101);
ASSERT_EQ(values[0], start);
ASSERT_EQ(values[100], end);
}
TEST(RoaringBitmap64Test, TestAddMany) {
// empty input
{
RoaringBitmap64 bitmap;
bitmap.AddMany(0, nullptr);
ASSERT_TRUE(bitmap.IsEmpty());
}
// single element
{
RoaringBitmap64 bitmap;
int64_t value = 999999999999ll;
bitmap.AddMany(1, &value);
ASSERT_EQ(bitmap.Cardinality(), 1);
ASSERT_TRUE(bitmap.Contains(999999999999ll));
}
// single bucket (all values share the same high-32 bits)
{
RoaringBitmap64 bitmap;
std::vector<int64_t> values = {100000000001ll, 100000000005ll, 100000000003ll,
100000000002ll};
bitmap.AddMany(values.size(), values.data());
ASSERT_EQ(bitmap.Cardinality(), 4);
ASSERT_TRUE(bitmap.Contains(100000000001ll));
ASSERT_TRUE(bitmap.Contains(100000000002ll));
ASSERT_TRUE(bitmap.Contains(100000000003ll));
ASSERT_TRUE(bitmap.Contains(100000000005ll));
ASSERT_FALSE(bitmap.Contains(100000000004ll));
}
// multiple buckets (values span different high-32 bit buckets)
{
RoaringBitmap64 bitmap;
std::vector<int64_t> values = {1ll, 100000000000ll, 200000000000ll, 2ll, 100000000001ll};
bitmap.AddMany(values.size(), values.data());
ASSERT_EQ(bitmap.Cardinality(), 5);
ASSERT_TRUE(bitmap.Contains(1ll));
ASSERT_TRUE(bitmap.Contains(2ll));
ASSERT_TRUE(bitmap.Contains(100000000000ll));
ASSERT_TRUE(bitmap.Contains(100000000001ll));
ASSERT_TRUE(bitmap.Contains(200000000000ll));
}
// duplicates
{
RoaringBitmap64 bitmap;
std::vector<int64_t> values = {42ll, 42ll, 42ll, 100000000000ll, 100000000000ll};
bitmap.AddMany(values.size(), values.data());
ASSERT_EQ(bitmap.Cardinality(), 2);
ASSERT_TRUE(bitmap.Contains(42ll));
ASSERT_TRUE(bitmap.Contains(100000000000ll));
}
// unsorted input (reverse order)
{
std::vector<int64_t> values;
for (int64_t i = 99; i >= 0; --i) {
values.push_back(i + 100000000000ll);
}
RoaringBitmap64 bitmap;
bitmap.AddMany(values.size(), values.data());
ASSERT_EQ(bitmap.Cardinality(), 100);
for (int64_t i = 0; i < 100; ++i) {
ASSERT_TRUE(bitmap.Contains(i + 100000000000ll));
}
}
// incremental insert (multiple AddMany calls accumulate)
{
RoaringBitmap64 bitmap;
std::vector<int64_t> batch1 = {10ll, 20ll, 30ll};
std::vector<int64_t> batch2 = {20ll, 40ll, 100000000000ll};
bitmap.AddMany(batch1.size(), batch1.data());
ASSERT_EQ(bitmap.Cardinality(), 3);
bitmap.AddMany(batch2.size(), batch2.data());
ASSERT_EQ(bitmap.Cardinality(), 5);
ASSERT_TRUE(bitmap.Contains(10ll));
ASSERT_TRUE(bitmap.Contains(20ll));
ASSERT_TRUE(bitmap.Contains(30ll));
ASSERT_TRUE(bitmap.Contains(40ll));
ASSERT_TRUE(bitmap.Contains(100000000000ll));
}
// large values near MAX_VALUE
{
std::vector<int64_t> values;
for (int64_t i = 0; i < 50; ++i) {
values.push_back(RoaringBitmap64::MAX_VALUE - 1 - i);
}
RoaringBitmap64 bitmap;
bitmap.AddMany(values.size(), values.data());
ASSERT_EQ(bitmap.Cardinality(), 50);
for (auto v : values) {
ASSERT_TRUE(bitmap.Contains(v));
}
}
// consistency with Add: AddMany must produce the same bitmap as repeated Add
{
std::vector<int64_t> values;
for (int64_t i = 0; i < 100; ++i) {
values.push_back(i + 100000000000ll);
}
for (int64_t i = 0; i < 50; ++i) {
values.push_back(i + 200000000000ll);
}
RoaringBitmap64 bitmap_add;
for (auto v : values) {
bitmap_add.Add(v);
}
RoaringBitmap64 bitmap_add_many;
bitmap_add_many.AddMany(values.size(), values.data());
ASSERT_EQ(bitmap_add, bitmap_add_many);
}
}
} // namespace paimon::test