From e1b37ffa7878bebb231551f2e8800a2ffb73869a Mon Sep 17 00:00:00 2001 From: Tim Ebbeke Date: Sat, 9 Aug 2025 12:17:45 +0200 Subject: [PATCH 01/11] Ignored CMakeSettings.json from visual studio. --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 7bb5cfe..d71bc63 100644 --- a/.gitignore +++ b/.gitignore @@ -17,4 +17,6 @@ tests/obj tests/*.cbp tests/*.depend tests/*build_log.html -tests/cairo-wrap \ No newline at end of file +tests/cairo-wrap + +CMakeSettings.json \ No newline at end of file From 14bc2159919f27dfeaca85d4bbc77c14f9d4bafc Mon Sep 17 00:00:00 2001 From: Tim Ebbeke Date: Sat, 9 Aug 2025 12:18:15 +0200 Subject: [PATCH 02/11] Added comment about returned iterator. --- include/interval-tree/interval_tree.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/include/interval-tree/interval_tree.hpp b/include/interval-tree/interval_tree.hpp index 8b10117..12b682d 100644 --- a/include/interval-tree/interval_tree.hpp +++ b/include/interval-tree/interval_tree.hpp @@ -1126,6 +1126,7 @@ namespace lib_interval_tree * inserted with more overlap searches? If the result is a single interval shall it insert_overlap or insert? * Be careful to not produce overlapping merge sets when doing recursive insertion, or it will recurse * endlessly. + * @return An iterator to the inserted or merged interval. */ iterator insert_overlap(interval_type const& ival, bool exclusive = false, bool recursive = true) { From 6501e3a5b5b34ece481e3742d94ab2b7f2083f22 Mon Sep 17 00:00:00 2001 From: Tim Ebbeke Date: Sat, 9 Aug 2025 12:18:25 +0200 Subject: [PATCH 03/11] Added erase range function. --- include/interval-tree/interval_tree.hpp | 39 +++++++ tests/erase_tests.hpp | 143 +++++++++++++++++++++--- 2 files changed, 164 insertions(+), 18 deletions(-) diff --git a/include/interval-tree/interval_tree.hpp b/include/interval-tree/interval_tree.hpp index 12b682d..6a42c3c 100644 --- a/include/interval-tree/interval_tree.hpp +++ b/include/interval-tree/interval_tree.hpp @@ -1205,6 +1205,45 @@ namespace lib_interval_tree return next; } + /** + * @brief Erases all elements that overlap with ival, if retainSlices is true, the left and right overhanging + * parts are reinserted. + * + * ( i1 ) + * ( i2 ) + * ( erase ) + * yields for retainSlices = true: + * (i1rest) (i2rest) + * + * Edge behavior depends on the interval type, or rather the slice implementation. + * + * @param ival The interval to erase. + * @param retainSlice If true, retains the non-overlapping parts of the intervals. + */ + template +#ifdef LIB_INTERVAL_TREE_CONCEPTS + requires detail::has_slice + void +#else + typename std::enable_if, void>::type +#endif + erase_range(interval_t const& ival, bool retainSlices = false) + { + const auto iter = insert_overlap(ival, false, true); + if (!retainSlices) + { + erase(iter); + return; + } + + const auto slices = iter->slice(ival); + erase(iter); + if (slices.left_slice) + insert(slices.left_slice.value()); + if (slices.right_slice) + insert(slices.right_slice.value()); + } + /** * Returns the size of the object. */ diff --git a/tests/erase_tests.hpp b/tests/erase_tests.hpp index e0cce41..a7641aa 100644 --- a/tests/erase_tests.hpp +++ b/tests/erase_tests.hpp @@ -16,7 +16,7 @@ struct Oracle template class OracleInterval : public lib_interval_tree::interval { -public: + public: using lib_interval_tree::interval::low_; using lib_interval_tree::interval::high_; @@ -60,26 +60,27 @@ class OracleInterval : public lib_interval_tree::interval -OracleInterval makeSafeOracleInterval(Oracle* oracle, numerical_type lhs, numerical_type rhs) +OracleInterval +makeSafeOracleInterval(Oracle* oracle, numerical_type lhs, numerical_type rhs) { - return OracleInterval {oracle, std::min(lhs, rhs), std::max(lhs, rhs)}; + return OracleInterval{oracle, std::min(lhs, rhs), std::max(lhs, rhs)}; } -class EraseTests - : public ::testing::Test +class EraseTests : public ::testing::Test { -public: + public: using interval_type = OracleInterval; -public: + public: auto makeTree() { - lib_interval_tree::interval_tree_t regularTree; + lib_interval_tree::interval_tree_t regularTree; regularTree.insert({16, 21}); regularTree.insert({8, 9}); regularTree.insert({25, 30}); @@ -93,12 +94,12 @@ class EraseTests return regularTree; } -protected: + protected: Oracle oracle; - lib_interval_tree::interval_tree > tree; + lib_interval_tree::interval_tree> tree; std::default_random_engine gen; - std::uniform_int_distribution distSmall{-500, 500}; - std::uniform_int_distribution distLarge{-50000, 50000}; + std::uniform_int_distribution distSmall{-500, 500}; + std::uniform_int_distribution distLarge{-50000, 50000}; }; TEST_F(EraseTests, EraseSingleElement) @@ -170,7 +171,7 @@ TEST_F(EraseTests, RandomEraseTest) for (int i = 0; i != deleteAmount; ++i) { - std::uniform_int_distribution dist{0, amount - i - 1}; + std::uniform_int_distribution dist{0, amount - i - 1}; auto end = dist(gen); auto iter = tree.begin(); for (int j = 0; j != end; ++j) @@ -183,8 +184,6 @@ TEST_F(EraseTests, RandomEraseTest) testTreeHeightHealth(tree); } - - TEST_F(EraseTests, MassiveDeleteEntireTreeWithEraseReturnIterator) { constexpr int amount = 1000; @@ -192,7 +191,7 @@ TEST_F(EraseTests, MassiveDeleteEntireTreeWithEraseReturnIterator) for (int i = 0; i != amount; ++i) tree.insert(makeSafeOracleInterval(&oracle, distSmall(gen), distSmall(gen))); - for(auto iter = tree.begin(); !tree.empty();) + for (auto iter = tree.begin(); !tree.empty();) { iter = tree.erase(iter); } @@ -255,7 +254,7 @@ TEST_F(EraseTests, CanEraseEntireTreeUsingReturnedIterator) TEST_F(EraseTests, FromNuiTest) { - lib_interval_tree::interval_tree_t tree; + lib_interval_tree::interval_tree_t tree; tree.insert({0, 0}); tree.insert({4, 4}); tree.insert({13, 13}); @@ -263,3 +262,111 @@ TEST_F(EraseTests, FromNuiTest) auto iter = tree.erase(tree.find({4, 4})); EXPECT_EQ(*iter, (decltype(tree)::interval_type{13, 13})) << *iter; } + +TEST_F(EraseTests, EraseRangeOnEmptyTreeDoesNothing) +{ + lib_interval_tree::interval_tree_t tree; + ASSERT_NO_FATAL_FAILURE(tree.erase_range({0, 10}, false)); + EXPECT_TRUE(tree.empty()); +} + +TEST_F(EraseTests, EraseRangeOnIntervalInsideRangeIsRemoved) +{ + lib_interval_tree::interval_tree_t tree; + tree.insert({0, 10}); + + tree.erase_range({-10, 20}, false); + EXPECT_TRUE(tree.empty()); +} + +TEST_F(EraseTests, EraseRangeOnIntervalEncompassingRangeIsRemoved) +{ + lib_interval_tree::interval_tree_t tree; + tree.insert({0, 10}); + + tree.erase_range({3, 5}, false); + EXPECT_TRUE(tree.empty()); +} + +TEST_F(EraseTests, NonOverlappingIntervalIsNotRemoved) +{ + lib_interval_tree::interval_tree_t tree; + tree.insert({0, 10}); + + tree.erase_range({20, 30}, false); + EXPECT_EQ(tree.size(), 1); + EXPECT_EQ(*tree.begin(), (decltype(tree)::interval_type{0, 10})); +} + +TEST_F(EraseTests, NonOverlappingIntervalIsNotRemoved2) +{ + lib_interval_tree::interval_tree_t tree; + tree.insert({0, 10}); + tree.insert({25, 35}); + + tree.erase_range({20, 30}, false); + EXPECT_EQ(tree.size(), 1); + EXPECT_EQ(*tree.begin(), (decltype(tree)::interval_type{0, 10})); +} + +TEST_F(EraseTests, EraseRangeOnIntervalWithLeftSlice) +{ + lib_interval_tree::interval_tree_t tree; + tree.insert({0, 10}); + + tree.erase_range({-5, 5}, true); + EXPECT_EQ(tree.size(), 1); + EXPECT_EQ(*tree.begin(), (decltype(tree)::interval_type{5, 10})); +} + +TEST_F(EraseTests, EraseRangeOnIntervalWithRightSlice) +{ + lib_interval_tree::interval_tree_t tree; + tree.insert({0, 10}); + + tree.erase_range({5, 15}, true); + EXPECT_EQ(tree.size(), 1); + EXPECT_EQ(*tree.begin(), (decltype(tree)::interval_type{0, 5})); +} + +TEST_F(EraseTests, EraseRangeMiddleCutOut) +{ + lib_interval_tree::interval_tree_t tree; + tree.insert({0, 10}); + + tree.erase_range({3, 5}, true); + EXPECT_EQ(tree.size(), 2); + EXPECT_EQ(*tree.begin(), (decltype(tree)::interval_type{0, 3})); + EXPECT_EQ(*(++tree.begin()), (decltype(tree)::interval_type{5, 10})); +} + +TEST_F(EraseTests, EraseRangeLeftSliceIsNotReinsertedIfParamIsFalse) +{ + lib_interval_tree::interval_tree_t tree; + tree.insert({0, 10}); + + tree.erase_range({-5, 5}, false); + EXPECT_TRUE(tree.empty()); +} + +TEST_F(EraseTests, EraseRangeRightSliceIsNotReinsertedIfParamIsFalse) +{ + lib_interval_tree::interval_tree_t tree; + tree.insert({0, 10}); + + tree.erase_range({5, 15}, false); + EXPECT_TRUE(tree.empty()); +} + +TEST_F(EraseTests, SlicesAreReinsertedWithMultioverlap) +{ + lib_interval_tree::interval_tree_t tree; + tree.insert({0, 10}); + tree.insert({5, 15}); + tree.insert({10, 20}); + + tree.erase_range({3, 12}, true); + EXPECT_EQ(tree.size(), 2); + EXPECT_EQ(*tree.begin(), (decltype(tree)::interval_type{0, 3})); + EXPECT_EQ(*(++tree.begin()), (decltype(tree)::interval_type{12, 20})); +} \ No newline at end of file From 7aa404e9687927bcde0dda37f6271b67b17516a7 Mon Sep 17 00:00:00 2001 From: Tim Ebbeke Date: Sat, 9 Aug 2025 12:32:34 +0200 Subject: [PATCH 04/11] Fixed punch readme images. --- README.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 9acc9d7..cb99140 100644 --- a/README.md +++ b/README.md @@ -182,7 +182,7 @@ Options are: - [After deoverlap](#after-deoverlap) - [interval\_tree deoverlap\_copy()](#interval_tree-deoverlap_copy) - [interval\_tree punch(interval\_type const\& ival)](#interval_tree-punchinterval_type-const-ival) - - [Before punching (closed\_adjacent intervals)](#before-punching-closed_adjacent-intervals) + - [Before punching (closed intervals)](#before-punching-closed-intervals) - [After punching (with \[-10, 60\])](#after-punching-with--10-60) - [interval\_tree punch()](#interval_tree-punch) - [bool empty() const noexcept](#bool-empty-const-noexcept) @@ -201,7 +201,7 @@ Options are: - [friend bool operator!=(interval const\& lhs, interval const\& other)](#friend-bool-operatorinterval-const-lhs-interval-const-other-1) - [value\_type low() const](#value_type-low-const) - [value\_type high() const](#value_type-high-const) - - [\[\[deprecated\]\] bool overlaps(value\_type l, value\_type h) const](#deprecated-bool-overlapsvalue_type-l-value_type-h-const) + - [DEPRECATED bool overlaps(value\_type l, value\_type h) const](#deprecated-bool-overlapsvalue_type-l-value_type-h-const) - [bool overlaps\_exclusive(value\_type l, value\_type h) const](#bool-overlaps_exclusivevalue_type-l-value_type-h-const) - [bool overlaps(interval const\& other) const](#bool-overlapsinterval-const-other-const) - [bool overlaps\_exclusive(interval const\& other) const](#bool-overlaps_exclusiveinterval-const-other-const) @@ -379,10 +379,10 @@ Open intervals with integral numbers will also not produce the gap (5, 6), becau `ival` can be any subrange of the tree, including encompassing the whole tree. **Returns**: A new interval_tree containing the gaps. -### Before punching (closed_adjacent intervals) -![BeforePunch](https://private-user-images.githubusercontent.com/6238896/471147224-5c631e00-dea4-4b75-a3bf-6fdd8ec1440b.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NTM1NjI1MzQsIm5iZiI6MTc1MzU2MjIzNCwicGF0aCI6Ii82MjM4ODk2LzQ3MTE0NzIyNC01YzYzMWUwMC1kZWE0LTRiNzUtYTNiZi02ZmRkOGVjMTQ0MGIucG5nP1gtQW16LUFsZ29yaXRobT1BV1M0LUhNQUMtU0hBMjU2JlgtQW16LUNyZWRlbnRpYWw9QUtJQVZDT0RZTFNBNTNQUUs0WkElMkYyMDI1MDcyNiUyRnVzLWVhc3QtMSUyRnMzJTJGYXdzNF9yZXF1ZXN0JlgtQW16LURhdGU9MjAyNTA3MjZUMjAzNzE0WiZYLUFtei1FeHBpcmVzPTMwMCZYLUFtei1TaWduYXR1cmU9ZDQ0NWIwMTcwMjZhNDA1YmUwNGI1YTIzNTBhZTQ5OTNhMWFiOTU5ZmU0N2E3NDI0NTQ0MzYwODA4N2E2MGFiZiZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QifQ.P5zLeXg0-9bd20Thj6pfq_WxriMn4GC_lDSLzzGKMbw) +### Before punching (closed intervals) +![BeforePunch](https://5cript.github.io/readme-images/interval-tree/punch_source.png) ### After punching (with [-10, 60]) -![AfterPunch](https://private-user-images.githubusercontent.com/6238896/471147227-5c226d1d-d544-4a43-89a4-b3545145107d.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NTM1NjI1MzQsIm5iZiI6MTc1MzU2MjIzNCwicGF0aCI6Ii82MjM4ODk2LzQ3MTE0NzIyNy01YzIyNmQxZC1kNTQ0LTRhNDMtODlhNC1iMzU0NTE0NTEwN2QucG5nP1gtQW16LUFsZ29yaXRobT1BV1M0LUhNQUMtU0hBMjU2JlgtQW16LUNyZWRlbnRpYWw9QUtJQVZDT0RZTFNBNTNQUUs0WkElMkYyMDI1MDcyNiUyRnVzLWVhc3QtMSUyRnMzJTJGYXdzNF9yZXF1ZXN0JlgtQW16LURhdGU9MjAyNTA3MjZUMjAzNzE0WiZYLUFtei1FeHBpcmVzPTMwMCZYLUFtei1TaWduYXR1cmU9NmE2ZDUzMjU2ZTNjZWQ0Y2QzYjQ3ZGUyYjgyNWM2NDViYTAxMTdlY2RjYmQyMzg4OWFmZDlhMWU5YjY4NjlmZCZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QifQ.Infe9i281LDOEC5GeBFuLHVE6Xjqw7KvcUo-gv3hjpk) +![AfterPunch](https://5cript.github.io/readme-images/interval-tree/punch_result.png) --- ### interval_tree punch() @@ -465,7 +465,7 @@ Which can be picked with the second template parameter of interval: - [friend bool operator!=(interval const& lhs, interval const& other)](#friend-bool-operatorinterval-const-lhs-interval-const-other-1) - [value_type low() const](#value_type-low-const) - [value_type high() const](#value_type-high-const) - - [\[\[deprecated\]\] bool overlaps(value_type l, value_type h) const](#bool-overlapsvalue_type-l-value_type-h-const) + - [DEPRECATED bool overlaps(value_type l, value_type h) const](#bool-overlapsvalue_type-l-value_type-h-const) - [bool overlaps_exclusive(value_type l, value_type h) const](#bool-overlaps_exclusivevalue_type-l-value_type-h-const) - [bool overlaps(interval const& other) const](#bool-overlapsinterval-const-other-const) - [bool overlaps_exclusive(interval const& other) const](#bool-overlaps_exclusiveinterval-const-other-const) @@ -474,6 +474,7 @@ Which can be picked with the second template parameter of interval: - [value_type operator-(interval const& other) const](#value_type-operator-interval-const-other-const) - [value_type size() const](#value_type-size-const) - [interval join(interval const& other) const](#interval-joininterval-const-other-const) + - [slice\_type slice(interval const\& other) const](#slice_type-sliceinterval-const-other-const) ### using value_type The underlying interval numerical type @@ -487,7 +488,7 @@ Comparison operator. Lower bound. ### value_type high() const Upper bound. -### \[\[deprecated\]\] bool overlaps(value_type l, value_type h) const +### DEPRECATED bool overlaps(value_type l, value_type h) const Overlap these bounds with this interval? Is deprecated because the overlapping does not work with the dynamic interval type. ### bool overlaps_exclusive(value_type l, value_type h) const @@ -507,7 +508,7 @@ Overlapping intervals have 0 distance. Returns The amount of elements in the interval when integral, or the distance between the 2 bounds when floating point. ### interval join(interval const& other) const Joins 2 intervals and whatever is inbetween. -### slice_type slice(interval const& other) const +### slice_type slice(interval const& other) const Removes other from this interval returning what is remaining. The range of other going beyond the range of this is ignored. Returns a struct with 2 members: left_slice and right_slice. From ff691d778ea1bcb269259a6780b341f7cf77bd90 Mon Sep 17 00:00:00 2001 From: Tim Ebbeke Date: Sat, 9 Aug 2025 12:46:25 +0200 Subject: [PATCH 05/11] Fixed compile issue on msvc. --- include/interval-tree/optional.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/interval-tree/optional.hpp b/include/interval-tree/optional.hpp index e50bc9c..7942af2 100644 --- a/include/interval-tree/optional.hpp +++ b/include/interval-tree/optional.hpp @@ -87,7 +87,7 @@ namespace lib_interval_tree template < \ typename U, \ typename... Args, \ - std::enable_if_t&, Args&&...>::value, int>...> \ + std::enable_if_t&, Args&&...>::value, int> = 0> \ constexpr explicit optional_base(in_place_t, std::initializer_list il, Args&&... args) \ : value_(il, std::forward(args)...) \ , engaged_{true} \ From 24e27f9dbb74195eb7d4214ea1a19c476068cd07 Mon Sep 17 00:00:00 2001 From: Tim Ebbeke Date: Sat, 9 Aug 2025 13:25:25 +0200 Subject: [PATCH 06/11] Fixed some msvc warnings. --- tests/erase_tests.hpp | 72 +++++++++++++++++++++--------------------- tests/hook_tests.hpp | 12 +++---- tests/test_utility.hpp | 19 ++++++----- 3 files changed, 53 insertions(+), 50 deletions(-) diff --git a/tests/erase_tests.hpp b/tests/erase_tests.hpp index a7641aa..234df56 100644 --- a/tests/erase_tests.hpp +++ b/tests/erase_tests.hpp @@ -96,7 +96,7 @@ class EraseTests : public ::testing::Test protected: Oracle oracle; - lib_interval_tree::interval_tree> tree; + lib_interval_tree::interval_tree> oracleTree; std::default_random_engine gen; std::uniform_int_distribution distSmall{-500, 500}; std::uniform_int_distribution distLarge{-50000, 50000}; @@ -106,14 +106,14 @@ TEST_F(EraseTests, EraseSingleElement) { auto inserted_interval = interval_type{&oracle, 0, 16}; - tree.insert(std::move(inserted_interval)); + oracleTree.insert(std::move(inserted_interval)); EXPECT_EQ(oracle.livingInstances, 1); - tree.erase(tree.begin()); + oracleTree.erase(oracleTree.begin()); EXPECT_EQ(oracle.livingInstances, 0); - EXPECT_EQ(tree.empty(), true); - EXPECT_EQ(tree.size(), 0); + EXPECT_EQ(oracleTree.empty(), true); + EXPECT_EQ(oracleTree.size(), 0); } TEST_F(EraseTests, ManualClearTest) @@ -121,13 +121,13 @@ TEST_F(EraseTests, ManualClearTest) constexpr int amount = 10'000; for (int i = 0; i != amount; ++i) - tree.insert(makeSafeOracleInterval(&oracle, distSmall(gen), distSmall(gen))); + oracleTree.insert(makeSafeOracleInterval(&oracle, distSmall(gen), distSmall(gen))); - for (auto i = std::begin(tree); i != std::end(tree);) - i = tree.erase(i); + for (auto i = std::begin(oracleTree); i != std::end(oracleTree);) + i = oracleTree.erase(i); EXPECT_EQ(oracle.livingInstances, 0); - EXPECT_EQ(tree.empty(), true); + EXPECT_EQ(oracleTree.empty(), true); } TEST_F(EraseTests, ClearTest) @@ -135,30 +135,30 @@ TEST_F(EraseTests, ClearTest) constexpr int amount = 10'000; for (int i = 0; i != amount; ++i) - tree.insert(makeSafeOracleInterval(&oracle, distSmall(gen), distSmall(gen))); + oracleTree.insert(makeSafeOracleInterval(&oracle, distSmall(gen), distSmall(gen))); - tree.clear(); + oracleTree.clear(); EXPECT_EQ(oracle.livingInstances, 0); - EXPECT_EQ(tree.empty(), true); + EXPECT_EQ(oracleTree.empty(), true); } TEST_F(EraseTests, ExpectedElementIsDeleted) { - tree.insert(makeSafeOracleInterval(&oracle, 16, 21)); - tree.insert(makeSafeOracleInterval(&oracle, 8, 9)); - tree.insert(makeSafeOracleInterval(&oracle, 25, 30)); - tree.insert(makeSafeOracleInterval(&oracle, 5, 8)); - tree.insert(makeSafeOracleInterval(&oracle, 15, 23)); - tree.insert(makeSafeOracleInterval(&oracle, 17, 19)); - tree.insert(makeSafeOracleInterval(&oracle, 26, 26)); - tree.insert(makeSafeOracleInterval(&oracle, 0, 3)); - tree.insert(makeSafeOracleInterval(&oracle, 6, 10)); - tree.insert(makeSafeOracleInterval(&oracle, 19, 20)); - - tree.erase(tree.find(makeSafeOracleInterval(&oracle, 17, 19))); - EXPECT_EQ(tree.find(makeSafeOracleInterval(&oracle, 17, 19)), tree.end()); - EXPECT_EQ(tree.size(), 9); + oracleTree.insert(makeSafeOracleInterval(&oracle, 16, 21)); + oracleTree.insert(makeSafeOracleInterval(&oracle, 8, 9)); + oracleTree.insert(makeSafeOracleInterval(&oracle, 25, 30)); + oracleTree.insert(makeSafeOracleInterval(&oracle, 5, 8)); + oracleTree.insert(makeSafeOracleInterval(&oracle, 15, 23)); + oracleTree.insert(makeSafeOracleInterval(&oracle, 17, 19)); + oracleTree.insert(makeSafeOracleInterval(&oracle, 26, 26)); + oracleTree.insert(makeSafeOracleInterval(&oracle, 0, 3)); + oracleTree.insert(makeSafeOracleInterval(&oracle, 6, 10)); + oracleTree.insert(makeSafeOracleInterval(&oracle, 19, 20)); + + oracleTree.erase(oracleTree.find(makeSafeOracleInterval(&oracle, 17, 19))); + EXPECT_EQ(oracleTree.find(makeSafeOracleInterval(&oracle, 17, 19)), oracleTree.end()); + EXPECT_EQ(oracleTree.size(), 9); } TEST_F(EraseTests, RandomEraseTest) @@ -167,21 +167,21 @@ TEST_F(EraseTests, RandomEraseTest) constexpr int deleteAmount = 50; for (int i = 0; i != amount; ++i) - tree.insert(makeSafeOracleInterval(&oracle, distSmall(gen), distSmall(gen))); + oracleTree.insert(makeSafeOracleInterval(&oracle, distSmall(gen), distSmall(gen))); for (int i = 0; i != deleteAmount; ++i) { std::uniform_int_distribution dist{0, amount - i - 1}; auto end = dist(gen); - auto iter = tree.begin(); + auto iter = oracleTree.begin(); for (int j = 0; j != end; ++j) ++iter; - tree.erase(iter); + oracleTree.erase(iter); } EXPECT_EQ(oracle.livingInstances, amount - deleteAmount); - testMaxProperty(tree); - testTreeHeightHealth(tree); + testMaxProperty(oracleTree); + testTreeHeightHealth(oracleTree); } TEST_F(EraseTests, MassiveDeleteEntireTreeWithEraseReturnIterator) @@ -189,16 +189,16 @@ TEST_F(EraseTests, MassiveDeleteEntireTreeWithEraseReturnIterator) constexpr int amount = 1000; for (int i = 0; i != amount; ++i) - tree.insert(makeSafeOracleInterval(&oracle, distSmall(gen), distSmall(gen))); + oracleTree.insert(makeSafeOracleInterval(&oracle, distSmall(gen), distSmall(gen))); - for (auto iter = tree.begin(); !tree.empty();) + for (auto iter = oracleTree.begin(); !oracleTree.empty();) { - iter = tree.erase(iter); + iter = oracleTree.erase(iter); } EXPECT_EQ(oracle.livingInstances, 0); - testMaxProperty(tree); - testTreeHeightHealth(tree); + testMaxProperty(oracleTree); + testTreeHeightHealth(oracleTree); } TEST_F(EraseTests, ReturnedIteratorPointsToNextInOrderNode) diff --git a/tests/hook_tests.hpp b/tests/hook_tests.hpp index bf9f78e..a5fd766 100644 --- a/tests/hook_tests.hpp +++ b/tests/hook_tests.hpp @@ -99,7 +99,7 @@ TEST_F(HookTests, OnBeforeInsertFixupIsCalled) { bool called = false; tree_type tree; - tree.on_before_insert_fixup = [&called](void* tree, auto node) { + tree.on_before_insert_fixup = [&called](void*, auto) { called = true; }; @@ -112,7 +112,7 @@ TEST_F(HookTests, OnAfterInsertFixupIsCalled) { bool called = false; tree_type tree; - tree.on_after_insert_fixup = [&called](void* tree, auto node) { + tree.on_after_insert_fixup = [&called](void*, auto) { called = true; }; @@ -125,7 +125,7 @@ TEST_F(HookTests, OnBeforeEraseFixupIsCalled) { bool called = false; tree_type tree; - tree.on_before_erase_fixup = [&called](void* tree, auto node, auto node_parent, bool other_is_left_child) { + tree.on_before_erase_fixup = [&called](void*, auto, auto, bool) { called = true; }; @@ -149,7 +149,7 @@ TEST_F(HookTests, OnAfterEraseFixupIsCalled) { bool called = false; tree_type tree; - tree.on_after_erase_fixup = [&called](void* tree, auto node, auto node_parent, bool other_is_left_child) { + tree.on_after_erase_fixup = [&called](void*, auto, auto, bool) { called = true; }; @@ -173,7 +173,7 @@ TEST_F(HookTests, OnBeforeRecalculateMaxIsCalled) { bool called = false; tree_type tree; - tree.on_before_recalculate_max = [&called](void* tree, auto node) { + tree.on_before_recalculate_max = [&called](void*, auto) { called = true; }; @@ -186,7 +186,7 @@ TEST_F(HookTests, OnAfterRecalculateMaxIsCalled) { bool called = false; tree_type tree; - tree.on_after_recalculate_max = [&called](void* tree, auto node) { + tree.on_after_recalculate_max = [&called](void*, auto) { called = true; }; diff --git a/tests/test_utility.hpp b/tests/test_utility.hpp index 6e6ef93..e595f36 100644 --- a/tests/test_utility.hpp +++ b/tests/test_utility.hpp @@ -1,5 +1,9 @@ #pragma once +#include + +#include + #include #include #include @@ -38,12 +42,10 @@ void testRedBlackPropertyViolation(TreeT const& tree) } } - auto leafCollector = [&](typename TreeT::const_iterator root) - { - std::list leaves{}; - std::function ::iterator)> recursiveLeafFinder; - recursiveLeafFinder = [&](typename std::list ::iterator self) - { + auto leafCollector = [&](typename TreeT::const_iterator root) { + std::list leaves{}; + std::function::iterator)> recursiveLeafFinder; + recursiveLeafFinder = [&](typename std::list::iterator self) { if (self->left() != std::end(tree)) { recursiveLeafFinder(leaves.insert(self, self->left())); @@ -104,11 +106,12 @@ void testMaxProperty(TreeT const& tree) template void testTreeHeightHealth(TreeT const& tree) { - int treeSize = tree.size(); + const auto treeSize = tree.size(); auto maxHeight{0}; for (auto i = std::begin(tree); i != std::end(tree); ++i) maxHeight = std::max(maxHeight, i.node()->height()); - EXPECT_LE(maxHeight, 2 * std::log2(treeSize + 1)); + const auto calc = 2 * std::log2(static_cast(treeSize) + 1); + EXPECT_LE(maxHeight, calc); } From 8d0dfc7a71a9ce34d18f1b3b819fbc628ddf2afc Mon Sep 17 00:00:00 2001 From: Tim Ebbeke Date: Sat, 9 Aug 2025 12:48:49 +0200 Subject: [PATCH 07/11] Added msvc build test. --- .github/workflows/build_and_test.yml | 33 ++++++++++++++++++++++++++-- tests/CMakeLists.txt | 14 +++++++++--- 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 79fb569..469f19b 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -48,11 +48,40 @@ jobs: uses: actions/checkout@v2 - name: CMake Configure - run: cmake -B ${{github.workspace}}/build -G"Ninja" -DINT_TREE_BUILD_EXAMPLES=on -DINT_TREE_ENABLE_TESTS=on -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_CXX_COMPILER=${{ matrix.compiler.cxx }} -DCMAKE_CXX_STANDARD=${{ matrix.cxx_standard }} + run: cmake -B ${{github.workspace}}/build -G"Ninja" -DINT_TREE_USE_OPTIONAL_POLYFILL=on -DINT_TREE_BUILD_EXAMPLES=on -DINT_TREE_ENABLE_TESTS=on -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_CXX_COMPILER=${{ matrix.compiler.cxx }} -DCMAKE_CXX_STANDARD=${{ matrix.cxx_standard }} - name: Build run: cmake --build ${{github.workspace}}/build - name: Test working-directory: ${{github.workspace}}/build - run: ./tests/tree-tests \ No newline at end of file + run: ./tests/tree-tests + + msvc: + runs-on: windows-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup MSVC + uses: microsoft/setup-msbuild@v1.0.2 + + - name: Install vcpkg and gtest + run: | + git clone https://github.com/microsoft/vcpkg.git + .\vcpkg\bootstrap-vcpkg.bat + .\vcpkg\vcpkg.exe install gtest + shell: pwsh + env: + VCPKG_DEFAULT_TRIPLET: x64-windows + + - name: CMake Configure + run: cmake -B ${{github.workspace}}/build -G"Visual Studio 17 2022" -A x64 -DCMAKE_TOOLCHAIN_FILE=${{github.workspace}}/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_CXX_STANDARD=20 -DINT_TREE_USE_OPTIONAL_POLYFILL=on -DINT_TREE_BUILD_EXAMPLES=on -DINT_TREE_ENABLE_TESTS=on -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} + + - name: Build + run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} + + - name: Test + working-directory: ${{github.workspace}}/build + run: .\tests\Release\tree-tests.exe + shell: cmd \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 32b946c..6b1b227 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -19,17 +19,25 @@ file(GLOB sources "*.cpp") # Add Executable add_executable(tree-tests ${sources}) -target_link_libraries(tree-tests PRIVATE gtest gmock interval-tree) +find_package(GTest REQUIRED) + +target_link_libraries(tree-tests PRIVATE interval-tree GTest::gtest GTest::gmock GTest::gmock_main) # Compiler Options set(DEBUG_OPTIONS -fexceptions -g -Wall -pedantic-errors -pedantic) target_compile_options(tree-tests PUBLIC "$<$:${DEBUG_OPTIONS}>") -set(RELEASE_OPTIONS -fexceptions -O3 -Wall -pedantic) +if (${MSVC}) + set(WARNING_FLAGS -W4) +else() + set(WARNING_FLAGS -Wall) + list(APPEND WARNING_FLAGS -Wextra) +endif() +set(RELEASE_OPTIONS -fexceptions -O3 ${WARNING_FLAGS} -pedantic) target_compile_options(tree-tests PUBLIC "$<$:${RELEASE_OPTIONS}>") if (INT_TREE_DRAW_EXAMPLES) - target_link_libraries(tree-tests PRIVATE cairo cairo-wrap) + target_link_libraries(tree-tests PRIVATE cairo-wrap cairo) endif() # If msys2, copy dynamic libraries to executable directory, visual studio does this automatically. From 7e981a09e8bf20f6c7c4c77a08a3628e16ec5ba2 Mon Sep 17 00:00:00 2001 From: Tim Ebbeke Date: Sat, 9 Aug 2025 13:56:39 +0200 Subject: [PATCH 08/11] Fixed version macro for MSVC. --- CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index b8b88ff..f8a097e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,6 +14,10 @@ endif() target_include_directories(interval-tree INTERFACE ./include) +if(${MSVC}) + target_compile_options(interval-tree INTERFACE /Zc:__cplusplus) +endif() + if(INT_TREE_DRAW_EXAMPLES) add_subdirectory(cairo-wrap) add_subdirectory(drawings) From 6d99adb87c91d1f91d1e59fd3831e859c63f6790 Mon Sep 17 00:00:00 2001 From: Tim Ebbeke Date: Sat, 9 Aug 2025 14:07:21 +0200 Subject: [PATCH 09/11] Fixed readme slice description. --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index cb99140..060949b 100644 --- a/README.md +++ b/README.md @@ -512,8 +512,10 @@ Joins 2 intervals and whatever is inbetween. Removes other from this interval returning what is remaining. The range of other going beyond the range of this is ignored. Returns a struct with 2 members: left_slice and right_slice. +``` [ this interval ] [left][other][right] +``` -When the intervals are closed, adjacent results are differenty by 1. +When the intervals are closed_adjacent, adjacent results are differenty by 1. [0, 9].slice([5, 19]) => left: [0, 4], right: nullopt \ No newline at end of file From 7261686082ebdc33f7f57db98a74ea55013acda6 Mon Sep 17 00:00:00 2001 From: Tim Ebbeke Date: Sat, 9 Aug 2025 14:19:56 +0200 Subject: [PATCH 10/11] Added erase_range overload. This overload does not require slice. --- include/interval-tree/interval_tree.hpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/include/interval-tree/interval_tree.hpp b/include/interval-tree/interval_tree.hpp index 6a42c3c..6c3fe5d 100644 --- a/include/interval-tree/interval_tree.hpp +++ b/include/interval-tree/interval_tree.hpp @@ -1227,7 +1227,7 @@ namespace lib_interval_tree #else typename std::enable_if, void>::type #endif - erase_range(interval_t const& ival, bool retainSlices = false) + erase_range(interval_t const& ival, bool retainSlices) { const auto iter = insert_overlap(ival, false, true); if (!retainSlices) @@ -1244,6 +1244,11 @@ namespace lib_interval_tree insert(slices.right_slice.value()); } + void erase_range(interval_type const& ival) + { + erase(insert_overlap(ival, false, true)); + } + /** * Returns the size of the object. */ From 22381854c63a279366b314b23766857d50cd4432 Mon Sep 17 00:00:00 2001 From: Tim Ebbeke Date: Sat, 9 Aug 2025 14:24:25 +0200 Subject: [PATCH 11/11] Added docs about erase_range. --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 060949b..56ed9a1 100644 --- a/README.md +++ b/README.md @@ -194,6 +194,8 @@ Options are: - [reverse\_iterator rend()](#reverse_iterator-rend) - [reverse\_iterator crbegin()](#reverse_iterator-crbegin) - [reverse\_iterator crend()](#reverse_iterator-crend) + - [void erase\_range(interval\_type const\& ival)](#void-erase_rangeinterval_type-const-ival) + - [void erase\_range(interval\_type const\& ival, bool retainSlices)](#void-erase_rangeinterval_type-const-ival-bool-retainslices) - [Members of Interval](#members-of-interval) - [using value\_type](#using-value_type) - [using interval\_kind](#using-interval_kind) @@ -444,6 +446,12 @@ Returns a past the end const_iterator in reverse. **Returns**: past the end const_iterator. +### void erase_range(interval_type const& ival) +Removes all intervals overlapping ival from the tree + +### void erase_range(interval_type const& ival, bool retainSlices) +Removes all intervals overlapping ival from the tree, but retains the overlap beyond the erase interval. + ## Members of Interval ___You can implement your own interval if you provide the same functions, except (slice, operator-, size, operator!=).___