Skip to content

Added recursive insert_overlap for single interval returns. #50

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ int main()
tree.insert({19, 20});

tree.deoverlap();

for (auto const& i : tree)
{
std::cout << "[" << i.low() << ", " << i.high() << "]\n";
Expand All @@ -69,7 +69,7 @@ Creates an interval where the borders are sorted so the lower border is the firs

- [Members of IntervalTree<Interval>](#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)
Expand Down Expand Up @@ -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.

Expand Down Expand Up @@ -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)
Expand Down
18 changes: 18 additions & 0 deletions include/interval-tree/feature_test.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
26 changes: 15 additions & 11 deletions include/interval-tree/interval_tree.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

Expand Down Expand Up @@ -1484,17 +1486,17 @@ namespace lib_interval_tree
};

template <typename MergeSet>
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();
Expand All @@ -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);
}

Expand Down
62 changes: 47 additions & 15 deletions tests/insert_tests.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,16 @@
#include <random>
#include <cmath>



class InsertTests
: public ::testing::Test
class InsertTests : public ::testing::Test
{
public:
using types = IntervalTypes <int>;
public:
using types = IntervalTypes<int>;

protected:
IntervalTypes <int>::tree_type tree;
protected:
IntervalTypes<int>::tree_type tree;
std::default_random_engine gen;
std::uniform_int_distribution <int> distSmall{-500, 500};
std::uniform_int_distribution <int> distLarge{-50000, 50000};
std::uniform_int_distribution<int> distSmall{-500, 500};
std::uniform_int_distribution<int> distLarge{-50000, 50000};
};

TEST_F(InsertTests, InsertIntoEmpty1)
Expand Down Expand Up @@ -86,15 +83,50 @@ TEST_F(InsertTests, RBPropertyInsertTest)

TEST_F(InsertTests, IntervalsMayReturnMultipleIntervalsForJoin)
{
using interval_type = multi_join_interval <int>;
using interval_type = multi_join_interval<int>;
using tree_type = lib_interval_tree::interval_tree<interval_type>;

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<int>;
using tree_type = lib_interval_tree::interval_tree<interval_type>;

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<types::interval_type>;

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);
}
29 changes: 12 additions & 17 deletions tests/multi_join_interval.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,27 @@
template <typename numerical_type, typename interval_kind_ = lib_interval_tree::closed>
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_;
Expand Down Expand Up @@ -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>{
multi_join_interval{min, avg - 1},
multi_join_interval{avg + 1, max},
};
}

protected:
protected:
value_type low_;
value_type high_;
};