diff --git a/README.md b/README.md index ea62691..5bb5c4c 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ int main() tree.insert({19, 20}); tree.deoverlap(); - + for (auto const& i : tree) { std::cout << "[" << i.low() << ", " << i.high() << "]\n"; @@ -69,7 +69,7 @@ Creates an interval where the borders are sorted so the lower border is the firs - [Members of IntervalTree](#members-of-intervaltreeinterval) - [iterator insert(interval_type const& ival)](#iterator-insertinterval_type-const-ival) - - [iterator insert_overlap(interval_type const& ival)](#iterator-insert_overlapinterval_type-const-ival) + - [iterator insert_overlap(interval_type const& ival, bool, bool)](#iterator-insert_overlapinterval_type-const-ival-bool-bool) - [iterator erase(iterator iter)](#iterator-eraseiterator-iter) - [size_type size() const](#size_type-size-const) - [(const)iterator find(interval_type const& ival)](#constiterator-findinterval_type-const-ival) @@ -100,20 +100,20 @@ Creates an interval where the borders are sorted so the lower border is the firs - [reverse_iterator crend()](#reverse_iterator-crend) ### iterator insert(interval_type const& ival) -Adds an interval into the tree. +Adds an interval into the tree. #### Parameters * `ival` An interval **Returns**: An iterator to the inserted element. --- -### iterator insert_overlap(interval_type const& ival) +### iterator insert_overlap(interval_type const& ival, bool, bool) Inserts an interval into the tree if no other interval overlaps it. Otherwise merge the interval with the one being overlapped. #### Parameters * `ival` An interval * `exclusive` Exclude borders from overlap check. Defaults to false. -* `mergeSetOverlapping` If the result of interval::join is a collection of intervals, shall each be inserted with more overlap searches? Defaults to false +* `recursive` If the result of interval::join is a collection of intervals, shall each be inserted with more overlap searches? If the result is a single interval, shall it be inserted via insert_overlap or insert? Defaults to false. recursive=true picks insert_overlap. Also be careful to not produce overlapping merge sets when doing recursive insertion, or it will recurse endlessly. **Returns**: An iterator to the inserted element. @@ -325,7 +325,7 @@ Returns a past the end const_iterator in reverse. **Returns**: past the end const_iterator. ## Members of Interval -___You can implement your own interval if you provide the same functions, except (within, operator-, size, operator!=).___ +___You can implement your own interval if you provide the same functions, except (operator-, size, operator!=).___ There are 6 types of intervals: - open: (a, b) diff --git a/include/interval-tree/feature_test.hpp b/include/interval-tree/feature_test.hpp index 4ca3ed5..ba70b30 100644 --- a/include/interval-tree/feature_test.hpp +++ b/include/interval-tree/feature_test.hpp @@ -22,4 +22,22 @@ # else # define LIB_INTERVAL_TREE_FALLTHROUGH __attribute__((fallthrough)) # endif +#endif + +// if constexpr is supported, use it, otherwise use plain if and pray the compiler optimizes it. +#if __cplusplus >= 201703L +# define INTERVAL_TREE_CONSTEXPR_IF if constexpr +#else +# define INTERVAL_TREE_CONSTEXPR_IF if +#endif + +// enum { value = value_ } or static constexpr +#if __cplusplus >= 201703L +# define INTERVAL_TREE_META_VALUE(type, name, the_value) static constexpr type name = the_value +#else +# define INTERVAL_TREE_META_VALUE(type, name, the_value) \ + enum : type \ + { \ + name = the_value \ + } #endif \ No newline at end of file diff --git a/include/interval-tree/interval_tree.hpp b/include/interval-tree/interval_tree.hpp index 2b283c5..cf6d4b9 100644 --- a/include/interval-tree/interval_tree.hpp +++ b/include/interval-tree/interval_tree.hpp @@ -1022,20 +1022,22 @@ namespace lib_interval_tree * Otherwise merge the interval with the being overlapped. * * @param ival The interval - * @param exclusive Exclude borders. - * @param mergeSetOverlapping If the result of interval::join is a collection of intervals, shall each be - * inserted with more overlap searches? + * @param exclusive Exclude borders regardeless of interval type. + * @param recursive If the result of interval::join is a collection of intervals, shall each be + * 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. */ - iterator insert_overlap(interval_type const& ival, bool exclusive = false, bool mergeSetOverlapping = false) + iterator insert_overlap(interval_type const& ival, bool exclusive = false, bool recursive = false) { auto iter = overlap_find(ival, exclusive); if (iter == end()) return insert(ival); else { - auto mergeSet = iter.interval().join(ival); + auto merge_set = iter.interval().join(ival); erase(iter); - return insert_merge_set(mergeSet, mergeSetOverlapping); + return insert_merge_set(std::move(merge_set), exclusive, recursive); } } @@ -1484,17 +1486,17 @@ namespace lib_interval_tree }; template - iterator insert_merge_set(MergeSet const& merge_set, bool mergeSetOverlapping) + iterator insert_merge_set(MergeSet const& merge_set, bool exclusive, bool recursive) { - if (mergeSetOverlapping) + if (recursive) { for (auto iter = merge_set.begin(), end = merge_set.end(); iter != end;) { auto next = iter; if (++next == end) - return insert_overlap(*iter); + return insert_overlap(*iter, exclusive, recursive); else - insert_overlap(*iter); + insert_overlap(*iter, exclusive, recursive); iter = std::move(next); } return end(); @@ -1513,8 +1515,10 @@ namespace lib_interval_tree return end(); } } - iterator insert_merge_set(interval_type const& interval, bool) + iterator insert_merge_set(interval_type const& interval, bool exclusive, bool recursive) { + if (recursive) + return insert_overlap(interval, exclusive, recursive); return insert(interval); } diff --git a/tests/insert_tests.hpp b/tests/insert_tests.hpp index 65b05cf..f73d987 100644 --- a/tests/insert_tests.hpp +++ b/tests/insert_tests.hpp @@ -7,19 +7,16 @@ #include #include - - -class InsertTests - : public ::testing::Test +class InsertTests : public ::testing::Test { -public: - using types = IntervalTypes ; + public: + using types = IntervalTypes; -protected: - IntervalTypes ::tree_type tree; + protected: + IntervalTypes::tree_type 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(InsertTests, InsertIntoEmpty1) @@ -86,15 +83,50 @@ TEST_F(InsertTests, RBPropertyInsertTest) TEST_F(InsertTests, IntervalsMayReturnMultipleIntervalsForJoin) { - using interval_type = multi_join_interval ; + using interval_type = multi_join_interval; using tree_type = lib_interval_tree::interval_tree; auto multiJoinTree = tree_type{}; - multiJoinTree.insert({0, 1}); - multiJoinTree.insert_overlap({0, 2}); + multiJoinTree.insert({0, 2}); + multiJoinTree.insert_overlap({0, 4}); EXPECT_EQ(multiJoinTree.size(), 2); - EXPECT_EQ(*multiJoinTree.begin(), (interval_type{0, 1})) << multiJoinTree.begin()->low() << multiJoinTree.begin()->high(); - EXPECT_EQ(*++multiJoinTree.begin(), (interval_type{1, 2})); + EXPECT_EQ(*multiJoinTree.begin(), (interval_type{0, 1})) + << multiJoinTree.begin()->low() << multiJoinTree.begin()->high(); + EXPECT_EQ(*++multiJoinTree.begin(), (interval_type{3, 4})); +} + +TEST_F(InsertTests, IntervalsMayReturnMultipleIntervalsForJoinAndJoinRecursively) +{ + using interval_type = multi_join_interval; + using tree_type = lib_interval_tree::interval_tree; + + auto multiJoinTree = tree_type{}; + + multiJoinTree.insert({0, 10}); + multiJoinTree.insert({5, 10}); + multiJoinTree.insert_overlap({0, 20}, false, true); + + EXPECT_EQ(multiJoinTree.size(), 3); + + auto iter = multiJoinTree.begin(); + + EXPECT_EQ(*iter, (interval_type{0, 4})) << multiJoinTree.begin()->low() << multiJoinTree.begin()->high(); + EXPECT_EQ(*++iter, (interval_type{6, 10})) << multiJoinTree.begin()->low() << multiJoinTree.begin()->high(); + EXPECT_EQ(*++iter, (interval_type{11, 20})) << multiJoinTree.begin()->low() << multiJoinTree.begin()->high(); +} + +TEST_F(InsertTests, CanInsertOverlapRecursively) +{ + using tree_type = lib_interval_tree::interval_tree; + + auto tree = tree_type{}; + tree.insert({0, 9}); + tree.insert({20, 29}); + tree.insert_overlap({8, 21}, false, true); + + EXPECT_EQ(tree.size(), 1); + EXPECT_EQ(tree.begin()->low(), 0); + EXPECT_EQ(tree.begin()->high(), 29); } diff --git a/tests/multi_join_interval.hpp b/tests/multi_join_interval.hpp index f778e63..92adab4 100644 --- a/tests/multi_join_interval.hpp +++ b/tests/multi_join_interval.hpp @@ -7,32 +7,27 @@ template struct multi_join_interval { -public: + public: using value_type = numerical_type; using interval_kind = interval_kind_; -#ifndef INTERVAL_TREE_SAFE_INTERVALS #if __cplusplus >= 201703L constexpr #endif - multi_join_interval(value_type low, value_type high) + multi_join_interval(value_type low, value_type high) : low_{low} , high_{high} { if (low > high) throw std::invalid_argument("Low border is not lower or equal to high border."); } -#else -#if __cplusplus >= 201703L - constexpr -#endif - multi_join_interval(value_type low, value_type high) - : low_{std::min(low, high)} - , high_{std::max(low, high)} - { - } -#endif + virtual ~multi_join_interval() = default; + multi_join_interval(multi_join_interval const&) = default; + multi_join_interval(multi_join_interval&&) noexcept = default; + multi_join_interval& operator=(multi_join_interval const&) = default; + multi_join_interval& operator=(multi_join_interval&&) noexcept = default; + friend bool operator==(multi_join_interval const& lhs, multi_join_interval const& other) { return lhs.low_ == other.low_ && lhs.high_ == other.high_; @@ -91,13 +86,13 @@ struct multi_join_interval const auto min = std::min(low_, other.low_); const auto max = std::max(high_, other.high_); const auto avg = (min + max) / 2; - return { - {min, avg}, - {avg, max}, + return std::vector{ + multi_join_interval{min, avg - 1}, + multi_join_interval{avg + 1, max}, }; } -protected: + protected: value_type low_; value_type high_; }; \ No newline at end of file