/*
 * 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
