From c3e7f2333de64ec1caebb0c178d289590fcf1dcf Mon Sep 17 00:00:00 2001 From: cubicYYY Date: Sun, 29 Sep 2024 16:43:58 +0800 Subject: [PATCH] Contains and intersects And assignment operators --- include/api.h | 2 + include/binsearch_index.h | 69 +++++++ include/froaring.h | 138 +++++++++++++- include/froaring_api/array_container.h | 9 +- include/froaring_api/bitmap_container.h | 36 +++- include/froaring_api/contains.h | 235 ++++++++++++++++++++++++ include/froaring_api/equal.h | 2 +- include/froaring_api/intersects.h | 188 +++++++++++++++++++ include/froaring_api/or.h | 2 +- tests/bitmap_container.cpp | 24 +-- tests/froaring_assignment.cpp | 43 +++++ tests/froaring_contains.cpp | 78 ++++++++ tests/froaring_intersects.cpp | 108 +++++++++++ 13 files changed, 916 insertions(+), 18 deletions(-) create mode 100644 include/froaring_api/contains.h create mode 100644 include/froaring_api/intersects.h create mode 100644 tests/froaring_assignment.cpp create mode 100644 tests/froaring_contains.cpp create mode 100644 tests/froaring_intersects.cpp diff --git a/include/api.h b/include/api.h index 5a0ba6e..3878f61 100644 --- a/include/api.h +++ b/include/api.h @@ -4,9 +4,11 @@ #include "froaring_api/and_inplace.h" #include "froaring_api/array_container.h" #include "froaring_api/bitmap_container.h" +#include "froaring_api/contains.h" #include "froaring_api/diff.h" #include "froaring_api/diff_inplace.h" #include "froaring_api/equal.h" +#include "froaring_api/intersects.h" #include "froaring_api/mix_ops.h" #include "froaring_api/or.h" #include "froaring_api/or_inplace.h" diff --git a/include/binsearch_index.h b/include/binsearch_index.h index 74ae216..b75a0b5 100644 --- a/include/binsearch_index.h +++ b/include/binsearch_index.h @@ -3,6 +3,7 @@ #include #include "api.h" +#include "froaring_api/contains.h" namespace froaring { @@ -626,6 +627,74 @@ class BinsearchIndex : public froaring_container_t { } a->size = new_container_counts; } + + static bool intersects(const BinsearchIndex* a, + const BinsearchIndex* b) { + SizeType i = 0, j = 0; + while (true) { + while (a->containers[i].index < b->containers[j].index) { + SKIP_FIRST_COMPARE: + i++; + if (i == a->size) { + return false; + } + } + while (a->containers[i].index > b->containers[j].index) { + j++; + if (j == b->size) { + return false; + } + } + if (a->containers[i].index == b->containers[j].index) { + if (froaring_intersects(a->containers[i].ptr, b->containers[j].ptr, + a->containers[i].type, b->containers[j].type)) { + return true; + } + i++; + j++; + if (i == a->size || j == b->size) { + return false; + } + } else { + goto SKIP_FIRST_COMPARE; + } + } + FROARING_UNREACHABLE + } + + static bool contains(const BinsearchIndex* a, + const BinsearchIndex* b) { + if (b->size == 0) { + return true; + } + + if (a->size < b->size) { + return false; + } + + size_t i = 0, j = 0; + + while (i < a->size && j < b->size) { + if (a->containers[i].index == b->containers[j].index) { + if (!froaring_contains(a->containers[i].ptr, b->containers[j].ptr, + a->containers[i].type, b->containers[j].type)) { + return false; + } + i++; + j++; + } else if (b->containers[j].index > a->containers[i].index) { + i++; + } else { + return false; + } + } + if (j == b->size) { + return true; + } else { + return false; + } + } + SizeType advanceAndReleaseUntil(IndexType key, SizeType pos) { while (pos < size && containers[pos].index < key) { release_container(containers[pos].ptr, containers[pos].type); diff --git a/include/froaring.h b/include/froaring.h index d0a4c31..6c57c0f 100644 --- a/include/froaring.h +++ b/include/froaring.h @@ -12,6 +12,8 @@ #include "binsearch_index.h" #include "froaring_api/array_container.h" #include "froaring_api/bitmap_container.h" +#include "froaring_api/intersects.h" +#include "froaring_api/mix_ops.h" #include "froaring_api/prelude.h" namespace froaring { @@ -37,8 +39,12 @@ class FlexibleRoaring { using ArraySized = ArrayContainer; using CTy = froaring::ContainerType; // handy local alias using ContainerHandle = froaring::ContainerHandle; + using iterator = FlexibleRoaringIterator; + using const_iterator = const iterator; + static constexpr IndexType UNKNOWN_INDEX = 0; static constexpr IndexType ANY_INDEX = 0; + friend FlexibleRoaringIterator; public: @@ -84,8 +90,8 @@ class FlexibleRoaring { FROARING_UNREACHABLE } } - FlexibleRoaring(FlexibleRoaring&& other) = default; - FlexibleRoaring& operator=(FlexibleRoaring&& other) = default; + + FlexibleRoaring(FlexibleRoaring&& other) { handle = other.handle; } ~FlexibleRoaring() { if (!handle.ptr) { @@ -98,6 +104,41 @@ class FlexibleRoaring { } } + FlexibleRoaring& operator=(FlexibleRoaring&& other) { + if (!handle.ptr) { + handle = std::move(other.handle); + other.handle.ptr = nullptr; + return *this; + } + + if (handle.type == CTy::Containers) { + delete castToContainers(handle.ptr); + } else { + release_container(handle.ptr, handle.type); + } + handle = std::move(other.handle); + other.handle.ptr = nullptr; + return *this; + } + + FlexibleRoaring& operator=(const FlexibleRoaring& other) { + if (!handle.ptr) { + if (handle.type == CTy::Containers) { + delete castToContainers(handle.ptr); + } else { + release_container(handle.ptr, handle.type); + } + } + handle.type = other.handle.type; + handle.index = other.handle.index; + if (other.handle.type == CTy::Containers) { + handle.ptr = new ContainersSized(*castToContainers(other.handle.ptr)); + } else { + handle.ptr = duplicate_container(other.handle.ptr, other.handle.type); + } + return *this; + } + void debug_print() { if (!is_inited()) { std::cout << "NULL!" << std::endl; @@ -129,6 +170,91 @@ class FlexibleRoaring { } } + const_iterator begin() const { return FlexibleRoaringIterator::begin(*this); } + + const_iterator end() const { return FlexibleRoaringIterator::end(*this); } + + bool contains(const FlexibleRoaring& other) const noexcept { + if (!other.is_inited()) { + return true; + } + if (!is_inited()) { + return false; + } + // Both containers + if (handle.type == CTy::Containers && other.handle.type == CTy::Containers) { + return ContainersSized::contains(castToContainers(handle.ptr), castToContainers(other.handle.ptr)); + } + + // One of them are containers: + if (handle.type == CTy::Containers) { // the other is a single container + // FIXME: We assume that no empty container in Containers. Is that true? + return false; + } + if (other.handle.type == CTy::Containers) { // this is a single container + const auto other_containers = castToContainers(other.handle.ptr); + const ContainerHandle& this_single = handle; + auto pos = other_containers->lower_bound(this_single.index); + if (pos == other_containers->size) { + return false; + } + if (other_containers->containers[pos].index != this_single.index) { + return false; + } + return froaring_contains(other_containers->containers[pos].ptr, this_single.ptr, + other_containers->containers[pos].type, this_single.type); + } + + // Both are single container: + if (handle.index != other.handle.index) { + return false; + } + return froaring_contains(handle.ptr, other.handle.ptr, handle.type, other.handle.type); + } + bool intersects(const FlexibleRoaring& other) const noexcept { + if (!is_inited() || !other.is_inited()) { + return false; + } + // Both containers + if (handle.type == CTy::Containers && other.handle.type == CTy::Containers) { + return ContainersSized::intersects(castToContainers(handle.ptr), castToContainers(other.handle.ptr)); + } + + // One of them are containers: + if (handle.type == CTy::Containers) { // the other is a single container + const auto this_containers = castToContainers(handle.ptr); + const ContainerHandle& other_single = other.handle; + auto pos = this_containers->lower_bound(other_single.index); + if (pos == this_containers->size) { + return false; + } + const ContainerHandle& lhs = this_containers->containers[pos]; + if (lhs.index != other_single.index) { + return false; + } + return froaring_intersects(lhs.ptr, other_single.ptr, lhs.type, other_single.type); + } + if (other.handle.type == CTy::Containers) { // this is a single container + const auto other_containers = castToContainers(other.handle.ptr); + const ContainerHandle& this_single = handle; + auto pos = other_containers->lower_bound(this_single.index); + if (pos == other_containers->size) { + return false; + } + if (other_containers->containers[pos].index != this_single.index) { + return false; + } + return froaring_intersects(other_containers->containers[pos].ptr, this_single.ptr, + other_containers->containers[pos].type, this_single.type); + } + + // Both are single container: + if (handle.index != other.handle.index) { + return false; + } + return froaring_intersects(handle.ptr, other.handle.ptr, handle.type, other.handle.type); + } + void set(WordType num) { can_fit_t index; can_fit_t data; @@ -507,6 +633,14 @@ class FlexibleRoaring { return *this; } + void intersectWithComplement(const FlexibleRoaring& other) noexcept { *this -= other; } + + /// @brief Overwrite current FlexibleRoaring with the result of lhs-rhs. + void intersectWithComplement(const FlexibleRoaring& lhs, const FlexibleRoaring& rhs) noexcept { + *this = lhs; + *this -= rhs; + } + FlexibleRoaring operator|(const FlexibleRoaring& other) const noexcept { if (!is_inited()) { return FlexibleRoaring(other); diff --git a/include/froaring_api/array_container.h b/include/froaring_api/array_container.h index 996e6ef..9312efe 100644 --- a/include/froaring_api/array_container.h +++ b/include/froaring_api/array_container.h @@ -108,7 +108,6 @@ class ArrayContainer : public froaring_container_t { this->capacity = new_cap; } -private: IndexOrNumType lower_bound(IndexOrNumType num) const { if (size < UseLinearScanThreshold) { for (SizeType i = 0; i < size; ++i) { @@ -131,6 +130,14 @@ class ArrayContainer : public froaring_container_t { return left; } + SizeType advanceUntil(IndexOrNumType key, SizeType pos) const { + // TODO: use Gallop search + while (pos < size && vals[pos] < key) { + pos++; + } + return pos; + } + public: SizeType capacity; SizeType size; diff --git a/include/froaring_api/bitmap_container.h b/include/froaring_api/bitmap_container.h index 5702e05..aff0dc6 100644 --- a/include/froaring_api/bitmap_container.h +++ b/include/froaring_api/bitmap_container.h @@ -74,6 +74,40 @@ class BitmapContainer : public froaring_container_t { std::memset(&words[start_word + 1], 0xFF, (end_word - start_word - 1) * sizeof(NumType)); } + bool any_range(NumType start, NumType end) const { + if (start >= end) { + return false; + } + const IndexType start_word = start / BitsPerWord; + const IndexType end_word = end / BitsPerWord; + + if (start_word >= WordsCount) { + return false; + } + // All "1" from `start` to MSB + const WordType first_mask = ~((1ULL << (start & IndexInsideWordMask)) - 1); + // All "1" from LSB to `end` + const WordType last_mask = + ((1ULL << ((end & IndexInsideWordMask))) - 1) ^ (1ULL << ((end & IndexInsideWordMask))); + + if (start_word == end_word) { + if (words[start_word] & (first_mask & last_mask)) return true; + } + + if (words[start_word] & first_mask) { + return true; + } + if (end_word < WordsCount && words[end_word] & last_mask) { + return true; + } + for (size_t i = start_word + 1; i < std::min((size_t)(end_word), WordsCount); ++i) { + if (words[i]) { + return true; + } + } + return false; + } + bool test(NumType index) const { return words[index / BitsPerWord] & ((WordType)1 << (index % BitsPerWord)); } bool test_and_set(NumType index) { @@ -117,7 +151,7 @@ class BitmapContainer : public froaring_container_t { /// @param start inclusive. /// @param end inclusive. /// @return If [start, end] is fully contained in the container. - bool containesRange(NumType start, NumType end) const { + bool test_range(NumType start, NumType end) const { if (start >= end) { return true; } diff --git a/include/froaring_api/contains.h b/include/froaring_api/contains.h new file mode 100644 index 0000000..74a6771 --- /dev/null +++ b/include/froaring_api/contains.h @@ -0,0 +1,235 @@ +#pragma once + +#include + +#include "array_container.h" +#include "bitmap_container.h" +#include "mix_ops.h" +#include "prelude.h" +#include "rle_container.h" +#include "utils.h" + +namespace froaring { +using CTy = froaring::ContainerType; + +template +bool froaring_contains_bb(const BitmapContainer* a, const BitmapContainer* b) { + for (size_t i = 0; i < a->WordsCount; ++i) { + if ((a->words[i] & b->words[i]) != b->words[i]) { + return false; + } + } + return true; +} + +template +bool froaring_contains_aa(const ArrayContainer* a, const ArrayContainer* b) { + if (b->size == 0) { + return true; + } + + if (a->size < b->size) { + return false; + } + + size_t i = 0, j = 0; + + while (i < a->size && j < b->size) { + if (a->vals[i] == b->vals[j]) { + i++; + j++; + } else if (b->vals[j] > a->vals[i]) { + i++; + } else { + return false; + } + } + if (j == b->size) { + return true; + } else { + return false; + } +} + +template +bool froaring_contains_rr(const RLEContainer* a, const RLEContainer* b) { + // FIXME: TODO: ... + FROARING_NOT_IMPLEMENTED +} + +template +bool froaring_contains_ar(const ArrayContainer* a, const RLEContainer* b) { + auto run_card = b->cardinality(); + if (run_card == 0) { + return true; + } + if (run_card > a->size) { + return false; + } + size_t start_pos = 0, stop_pos = 0; + for (int i = 0; i < b->run_count; ++i) { + typename ArrayContainer::SizeType start = b->runs[i].start; + typename ArrayContainer::SizeType stop = b->runs[i].end; + start_pos = a->advanceUntil(start, stop_pos); + stop_pos = a->advanceUntil(stop, stop_pos); + if (stop_pos == a->size) { + return false; + } else if (stop_pos - start_pos != stop - start || a->vals[start_pos] != start || a->vals[stop_pos] != stop) { + return false; + } + } + return true; +} + +template +bool froaring_contains_ra(const RLEContainer* a, const ArrayContainer* b) { + if (b->size == 0) { + return true; + } + if (b->size > a->cardinality()) { + return false; + } + int i_array = 0, i_run = 0; + while (i_array < b->size && i_run < a->run_count) { + typename RLEContainer::SizeType start = a->runs[i_run].start; + typename RLEContainer::SizeType stop = a->runs[i_run].end; + if (b->vals[i_array] < start) { + return false; + } else if (b->vals[i_array] > stop) { + i_run++; + } else { // the value of the array is in the run + i_array++; + } + } + if (i_array == b->size) { + return true; + } else { + return false; + } +} + +template +bool froaring_contains_br(const BitmapContainer* a, const RLEContainer* b) { + if (a->cardinality() < b->cardinality()) { + return false; + } + for (int i = 0; i < b->run_count; ++i) { + typename RLEContainer::SizeType run_start = b->runs[i].start; + typename RLEContainer::SizeType run_end = b->runs[i].end; + if (!a->test_range(run_start, run_end)) { + return false; + } + } + return true; +} + +template +bool froaring_contains_rb(const RLEContainer* a, const BitmapContainer* b) { + if (b->cardinality() > a->cardinality()) { + return false; + } + size_t i_bitset = 0, i_run = 0; + while (i_bitset < BitmapContainer::WordsCount && i_run < a->run_count) { + auto w = b->words[i_bitset]; + while (w != 0 && i_run < a->run_count) { + auto start = a->runs[i_run].start; + auto stop = a->runs[i_run].end; + auto t = w & (~w + 1); + size_t r = i_bitset * BitmapContainer::BitsPerWord + std::countr_zero(w); + if (r < start) { + return false; + } else if (r > stop) { + i_run++; + continue; + } else { + w ^= t; + } + } + if (w == 0) { + i_bitset++; + } else { + return false; + } + } + if (i_bitset < BitmapContainer::WordsCount) { + // terminated iterating on the run containers, check that rest of bitset + // is empty + for (; i_bitset < BitmapContainer::WordsCount; i_bitset++) { + if (b->words[i_bitset] != 0) { + return false; + } + } + } + return true; +} + +template +bool froaring_contains_ba(const BitmapContainer* a, const ArrayContainer* b) { + if (a->cardinality() < b->size) { + return false; + } + + for (size_t i = 0; i < b->size; ++i) { + if (!a->test(b->vals[i])) { + return false; + } + } + return true; +} + +template +bool froaring_contains_ab(const ArrayContainer* a, const BitmapContainer* b) { + // FIXME: We should assume that a bitset is always bigger, so this function should always return false. + auto bitset_card = b->cardinality(); + if (a->size < bitset_card) { + return false; + } + size_t found = 0; + for (size_t i = 0; i < a->size; i++) { + found += b->test(a->vals[i]); + } + if (found == bitset_card) { + return true; + } else { + return false; + } +} + +template +bool froaring_contains(const froaring_container_t* a, const froaring_container_t* b, CTy ta, CTy tb) { + using RLESized = RLEContainer; + using ArraySized = ArrayContainer; + using BitmapSized = BitmapContainer; + switch (CTYPE_PAIR(ta, tb)) { + case CTYPE_PAIR(CTy::Bitmap, CTy::Bitmap): { + return froaring_contains_bb(static_cast(a), static_cast(b)); + } + case CTYPE_PAIR(CTy::Array, CTy::Array): { + return froaring_contains_aa(static_cast(a), static_cast(b)); + } + case CTYPE_PAIR(CTy::RLE, CTy::RLE): { + return froaring_contains_rr(static_cast(a), static_cast(b)); + } + case CTYPE_PAIR(CTy::Bitmap, CTy::Array): { + return froaring_contains_ba(static_cast(a), static_cast(b)); + } + case CTYPE_PAIR(CTy::Array, CTy::Bitmap): { + return froaring_contains_ab(static_cast(a), static_cast(b)); + } + case CTYPE_PAIR(CTy::Bitmap, CTy::RLE): { + return froaring_contains_br(static_cast(a), static_cast(b)); + } + case CTYPE_PAIR(CTy::RLE, CTy::Bitmap): { + return froaring_contains_rb(static_cast(a), static_cast(b)); + } + case CTYPE_PAIR(CTy::Array, CTy::RLE): { + return froaring_contains_ar(static_cast(a), static_cast(b)); + } + case CTYPE_PAIR(CTy::RLE, CTy::Array): { + return froaring_contains_ra(static_cast(a), static_cast(b)); + } + default: + FROARING_UNREACHABLE + } +} +} // namespace froaring \ No newline at end of file diff --git a/include/froaring_api/equal.h b/include/froaring_api/equal.h index 056f15f..8bcef8b 100644 --- a/include/froaring_api/equal.h +++ b/include/froaring_api/equal.h @@ -54,7 +54,7 @@ template bool froaring_equal_br(const BitmapContainer* a, const RLEContainer* b) { if (a->cardinality() != b->cardinality()) return false; for (size_t i = 0; i < b->run_count; ++i) { - if (!a->containesRange(b->runs[i].start, b->runs[i].end)) { + if (!a->test_range(b->runs[i].start, b->runs[i].end)) { return false; } } diff --git a/include/froaring_api/intersects.h b/include/froaring_api/intersects.h new file mode 100644 index 0000000..6029e92 --- /dev/null +++ b/include/froaring_api/intersects.h @@ -0,0 +1,188 @@ +#pragma once + +#include +#include + +#include "array_container.h" +#include "bitmap_container.h" +#include "mix_ops.h" +#include "prelude.h" +#include "rle_container.h" + +namespace froaring { +using CTy = froaring::ContainerType; + +template +bool froaring_intersects_bb(const BitmapContainer* a, + const BitmapContainer* b) { + for (size_t i = 0; i < a->WordsCount; ++i) { + if (a->words[i] & b->words[i]) { + return true; + } + } + return false; +} + +template +bool froaring_intersects_aa(const ArrayContainer* a, const ArrayContainer* b) { + if (a->size == 0 || b->size == 0) { + return false; + } + + if (a->size > b->size) { // consider the small array first to make it faster + std::swap(a, b); + } + + size_t i = 0, j = 0; + + while (true) { + while (a->vals[i] < b->vals[j]) { + SKIP_FIRST_COMPARE: + ++i; + if (i == a->size) { + return false; + } + } + while (a->vals[i] > b->vals[j]) { + ++j; + if (j == b->size) { + return false; + } + } + if (a->vals[i] == b->vals[j]) { + return true; + } else { + goto SKIP_FIRST_COMPARE; + } + } + FROARING_UNREACHABLE +} + +template +bool froaring_intersects_rr(const RLEContainer* a, const RLEContainer* b) { + if (a->run_count == 0 || b->run_count == 0) { + return false; + } + + size_t i = 0, j = 0; + while (true) { + auto& run_a = a->runs[i]; + auto& run_b = b->runs[j]; + while (run_a.end < run_b.start) { + SKIP_FIRST_COMPARE: + ++i; + if (i == a->run_count) { + return false; + } + } + while (run_b.end < run_a.start) { + ++j; + if (j == b->run_count) { + return false; + } + } + if (run_a.end >= run_b.start && run_b.end >= run_a.start) { + return true; + } else { + goto SKIP_FIRST_COMPARE; + } + } + FROARING_UNREACHABLE +} + +template +bool froaring_intersects_ar(const ArrayContainer* a, const RLEContainer* b) { + if (a->size == 0 || b->run_count == 0) { + return false; + } + + size_t i = 0, j = 0; + while (true) { + auto& val_a = a->vals[i]; + auto& run_b = b->runs[j]; + while (val_a < run_b.start) { + SKIP_FIRST_COMPARE: + ++i; + if (i == a->size) { + return false; + } + } + while (run_b.end < val_a) { + ++j; + if (j == b->run_count) { + return false; + } + } + if (val_a >= run_b.start && run_b.end >= val_a) { + return true; + } else { + goto SKIP_FIRST_COMPARE; + } + } + FROARING_UNREACHABLE +} + +template +bool froaring_intersects_br(const BitmapContainer* a, const RLEContainer* b) { + auto* result = new BitmapContainer(*a); + auto rle_count = b->run_count; + for (size_t i = 0; i < rle_count; i++) { + if (result->any_range(b->runs[i].start, b->runs[i].end)) { + return true; + } + } + return false; +} + +template +bool froaring_intersects_ba(const BitmapContainer* a, const ArrayContainer* b) { + auto array_size = b->size; + if (array_size == 0) { + return false; + } + for (size_t i = 0; i < array_size; i++) { + if (a->test(b->vals[i])) { + return true; + } + } + return false; +} + +template +bool froaring_intersects(const froaring_container_t* a, const froaring_container_t* b, CTy ta, CTy tb) { + using RLESized = RLEContainer; + using ArraySized = ArrayContainer; + using BitmapSized = BitmapContainer; + switch (CTYPE_PAIR(ta, tb)) { + case CTYPE_PAIR(CTy::Bitmap, CTy::Bitmap): { + return froaring_intersects_bb(static_cast(a), static_cast(b)); + } + case CTYPE_PAIR(CTy::Array, CTy::Array): { + return froaring_intersects_aa(static_cast(a), static_cast(b)); + } + case CTYPE_PAIR(CTy::RLE, CTy::RLE): { + return froaring_intersects_rr(static_cast(a), static_cast(b)); + } + case CTYPE_PAIR(CTy::Bitmap, CTy::Array): { + return froaring_intersects_ba(static_cast(a), static_cast(b)); + } + case CTYPE_PAIR(CTy::Array, CTy::Bitmap): { + return froaring_intersects_ba(static_cast(b), static_cast(a)); + } + case CTYPE_PAIR(CTy::Bitmap, CTy::RLE): { + return froaring_intersects_br(static_cast(a), static_cast(b)); + } + case CTYPE_PAIR(CTy::RLE, CTy::Bitmap): { + return froaring_intersects_br(static_cast(b), static_cast(a)); + } + case CTYPE_PAIR(CTy::Array, CTy::RLE): { + return froaring_intersects_ar(static_cast(a), static_cast(b)); + } + case CTYPE_PAIR(CTy::RLE, CTy::Array): { + return froaring_intersects_ar(static_cast(b), static_cast(a)); + } + default: + FROARING_UNREACHABLE + } +} +} // namespace froaring \ No newline at end of file diff --git a/include/froaring_api/or.h b/include/froaring_api/or.h index f35335c..fb172ad 100644 --- a/include/froaring_api/or.h +++ b/include/froaring_api/or.h @@ -177,7 +177,7 @@ froaring_container_t* froaring_or_ar(const ArrayContainer* a auto* result = new RLEContainer(*b); auto array_size = a->size; for (size_t i = 0; i < array_size; i++) { - result->set(a->vals[i]); + result->set(a->vals[i]); // FIXME: Do not use set() but manually set on run! } return result; } diff --git a/tests/bitmap_container.cpp b/tests/bitmap_container.cpp index 7ce07cd..5c461e1 100644 --- a/tests/bitmap_container.cpp +++ b/tests/bitmap_container.cpp @@ -62,12 +62,12 @@ TEST_F(BitmapContainerTest, ContainsRangeTest) { container->reset(67); EXPECT_EQ(container->IndexInsideWordMask, 0x3F); // uint64_t, 64 bits = 2^6 bits, (111111)2=0x3F container->debug_print(); - EXPECT_TRUE(container->containesRange(9, 13)); - EXPECT_FALSE(container->containesRange(9, 14)); - EXPECT_FALSE(container->containesRange(8, 13)); - EXPECT_TRUE(container->containesRange(62, 66)); - EXPECT_TRUE(container->containesRange(68, 68)); - EXPECT_FALSE(container->containesRange(62, 68)); + EXPECT_TRUE(container->test_range(9, 13)); + EXPECT_FALSE(container->test_range(9, 14)); + EXPECT_FALSE(container->test_range(8, 13)); + EXPECT_TRUE(container->test_range(62, 66)); + EXPECT_TRUE(container->test_range(68, 68)); + EXPECT_FALSE(container->test_range(62, 68)); EXPECT_EQ(container->cardinality(), 12); } @@ -93,12 +93,12 @@ TEST_F(BitmapContainerTest, NonPowerOf2SizeBitmap) { EXPECT_EQ(container->IndexInsideWordMask, 0x3F); // uint64_t, 64 bits = 2^6 bits, (111111)2=0x3F container->debug_print(); EXPECT_TRUE(container->test(1023)); - EXPECT_TRUE(container->containesRange(909, 913)); - EXPECT_FALSE(container->containesRange(909, 914)); - EXPECT_FALSE(container->containesRange(908, 913)); - EXPECT_TRUE(container->containesRange(962, 966)); - EXPECT_TRUE(container->containesRange(968, 968)); - EXPECT_FALSE(container->containesRange(962, 968)); + EXPECT_TRUE(container->test_range(909, 913)); + EXPECT_FALSE(container->test_range(909, 914)); + EXPECT_FALSE(container->test_range(908, 913)); + EXPECT_TRUE(container->test_range(962, 966)); + EXPECT_TRUE(container->test_range(968, 968)); + EXPECT_FALSE(container->test_range(962, 968)); EXPECT_EQ(container->cardinality(), 12); delete container; diff --git a/tests/froaring_assignment.cpp b/tests/froaring_assignment.cpp new file mode 100644 index 0000000..9787470 --- /dev/null +++ b/tests/froaring_assignment.cpp @@ -0,0 +1,43 @@ +#include + +#include "froaring.h" + +using namespace froaring; + +class FlexibleRoaringTest : public ::testing::Test { +protected: + void SetUp() override {} + + void TearDown() override {} +}; + +TEST_F(FlexibleRoaringTest, MoveAssignmentOperator) { + FlexibleRoaring bitmap1; + bitmap1.set(1); + bitmap1.set(2); + + FlexibleRoaring bitmap2; + bitmap2 = std::move(bitmap1); + + EXPECT_TRUE(bitmap2.test(1)); + EXPECT_TRUE(bitmap2.test(2)); +} + +TEST_F(FlexibleRoaringTest, CopyAssignmentOperator) { + FlexibleRoaring bitmap1; + bitmap1.set(1); + bitmap1.set(2); + + FlexibleRoaring bitmap2; + bitmap2 = bitmap1; + + EXPECT_TRUE(bitmap1.test(1)); + EXPECT_TRUE(bitmap1.test(2)); + EXPECT_TRUE(bitmap2.test(1)); + EXPECT_TRUE(bitmap2.test(2)); +} + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file diff --git a/tests/froaring_contains.cpp b/tests/froaring_contains.cpp new file mode 100644 index 0000000..808bcf4 --- /dev/null +++ b/tests/froaring_contains.cpp @@ -0,0 +1,78 @@ +#include + +#include "froaring.h" + +using namespace froaring; + +class FlexibleRoaringContainsTest : public ::testing::Test { +protected: + void SetUp() override {} + + void TearDown() override {} +}; + +TEST_F(FlexibleRoaringContainsTest, BothUninitialized) { + FlexibleRoaring a; + FlexibleRoaring b; + EXPECT_TRUE(a.contains(b)); +} + +TEST_F(FlexibleRoaringContainsTest, OtherUninitialized) { + FlexibleRoaring a; + FlexibleRoaring b; + a.set(1); + EXPECT_TRUE(a.contains(b)); +} + +TEST_F(FlexibleRoaringContainsTest, ThisUninitialized) { + FlexibleRoaring a; + FlexibleRoaring b; + b.set(1); + EXPECT_FALSE(a.contains(b)); +} + +TEST_F(FlexibleRoaringContainsTest, BothContainers) { + FlexibleRoaring a; + FlexibleRoaring b; + a.set(1); + a.set(2); + b.set(1); + EXPECT_TRUE(a.contains(b)); + b.set(3); + EXPECT_FALSE(a.contains(b)); +} + +TEST_F(FlexibleRoaringContainsTest, SingleContainerInThis) { + FlexibleRoaring a; + FlexibleRoaring b; + a.set(1); + b.set(1); + b.set(2); + EXPECT_FALSE(a.contains(b)); +} + +TEST_F(FlexibleRoaringContainsTest, SingleContainerInOther) { + FlexibleRoaring a; + FlexibleRoaring b; + a.set(1); + a.set(2); + b.set(1); + EXPECT_TRUE(a.contains(b)); + b.set(3); + EXPECT_FALSE(a.contains(b)); +} + +TEST_F(FlexibleRoaringContainsTest, BothSingleContainers) { + FlexibleRoaring a; + FlexibleRoaring b; + a.set(1); + b.set(1); + EXPECT_TRUE(a.contains(b)); + b.set(2); + EXPECT_FALSE(a.contains(b)); +} + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file diff --git a/tests/froaring_intersects.cpp b/tests/froaring_intersects.cpp new file mode 100644 index 0000000..3eebb4e --- /dev/null +++ b/tests/froaring_intersects.cpp @@ -0,0 +1,108 @@ +#include + +#include "froaring.h" + +using namespace froaring; + +class FlexibleRoaringIntersectsTest : public ::testing::Test { +protected: + void SetUp() override {} + + void TearDown() override {} +}; + +TEST_F(FlexibleRoaringIntersectsTest, BothUninitialized) { + FlexibleRoaring a; + FlexibleRoaring b; + EXPECT_FALSE(a.intersects(b)); +} + +TEST_F(FlexibleRoaringIntersectsTest, OtherUninitialized) { + FlexibleRoaring a; + FlexibleRoaring b; + a.set(1); + EXPECT_FALSE(a.intersects(b)); +} + +TEST_F(FlexibleRoaringIntersectsTest, ThisUninitialized) { + FlexibleRoaring a; + FlexibleRoaring b; + b.set(1); + EXPECT_FALSE(a.intersects(b)); +} + +TEST_F(FlexibleRoaringIntersectsTest, BothContainersIntersect) { + FlexibleRoaring a; + FlexibleRoaring b; + a.set(1); + a.set(2); + b.set(2); + b.set(3); + EXPECT_TRUE(a.intersects(b)); +} + +TEST_F(FlexibleRoaringIntersectsTest, BothContainersNoIntersect) { + FlexibleRoaring a; + FlexibleRoaring b; + a.set(1); + a.set(2); + b.set(3); + b.set(4); + EXPECT_FALSE(a.intersects(b)); +} + +TEST_F(FlexibleRoaringIntersectsTest, SingleContainerInThisIntersect) { + FlexibleRoaring a; + FlexibleRoaring b; + a.set(1); + b.set(1); + b.set(2); + EXPECT_TRUE(a.intersects(b)); +} + +TEST_F(FlexibleRoaringIntersectsTest, SingleContainerInThisNoIntersect) { + FlexibleRoaring a; + FlexibleRoaring b; + a.set(1); + b.set(2); + EXPECT_FALSE(a.intersects(b)); +} + +TEST_F(FlexibleRoaringIntersectsTest, SingleContainerInOtherIntersect) { + FlexibleRoaring a; + FlexibleRoaring b; + a.set(1); + a.set(2); + b.set(1); + EXPECT_TRUE(a.intersects(b)); +} + +TEST_F(FlexibleRoaringIntersectsTest, SingleContainerInOtherNoIntersect) { + FlexibleRoaring a; + FlexibleRoaring b; + a.set(1); + a.set(2); + b.set(3); + EXPECT_FALSE(a.intersects(b)); +} + +TEST_F(FlexibleRoaringIntersectsTest, BothSingleContainersIntersect) { + FlexibleRoaring a; + FlexibleRoaring b; + a.set(1); + b.set(1); + EXPECT_TRUE(a.intersects(b)); +} + +TEST_F(FlexibleRoaringIntersectsTest, BothSingleContainersNoIntersect) { + FlexibleRoaring a; + FlexibleRoaring b; + a.set(1); + b.set(2); + EXPECT_FALSE(a.intersects(b)); +} + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file