Skip to content
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
47 changes: 38 additions & 9 deletions include/interval-tree/interval_tree.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -707,6 +707,10 @@ namespace lib_interval_tree
const_interval_tree_iterator& operator=(const_interval_tree_iterator const&) = default;
const_interval_tree_iterator& operator=(const_interval_tree_iterator&&) noexcept = default;

explicit const_interval_tree_iterator(interval_tree_iterator<node_type, reverse, tree_hooks> const& other)
: iterator_base{other.node_, other.owner_}
{}

template <typename T>
friend void increment(T& iter);
template <typename T>
Expand Down Expand Up @@ -1357,11 +1361,18 @@ namespace lib_interval_tree
}
template <typename CompareFunctionT>
const_iterator
find_next_in_subtree(iterator from, interval_type const& ival, CompareFunctionT const& compare) const
find_next_in_subtree(const_iterator from, interval_type const& ival, CompareFunctionT const& compare) const
{
if (root_ == nullptr)
return end();
return iterator{find_i_ex(from.node_, ival, compare), this};
return const_iterator{find_i_ex(from.node_, ival, compare), this};
}
// Convenience, so that a mutable iterator does not have to be converted by hand.
template <typename CompareFunctionT>
const_iterator
find_next_in_subtree(iterator from, interval_type const& ival, CompareFunctionT const& compare) const
{
return find_next_in_subtree(const_iterator{from}, ival, compare);
}

/**
Expand All @@ -1377,12 +1388,17 @@ namespace lib_interval_tree
return lhs == rhs;
});
}
const_iterator find_next_in_subtree(iterator from, interval_type const& ival) const
const_iterator find_next_in_subtree(const_iterator from, interval_type const& ival) const
{
return find_next_in_subtree(from, ival, [](auto const& lhs, auto const& rhs) {
return lhs == rhs;
});
}
// Convenience, so that a mutable iterator does not have to be converted by hand.
const_iterator find_next_in_subtree(iterator from, interval_type const& ival) const
{
return find_next_in_subtree(const_iterator{from}, ival);
}

/**
* Finds the first interval that overlaps with ival.
Expand Down Expand Up @@ -1447,14 +1463,26 @@ namespace lib_interval_tree
{
if (root_ == nullptr)
return end();
return iterator{overlap_find_i_ex(from.node_, ival, exclusive), this};
if (exclusive)
return iterator{overlap_find_i_ex<true>(from.node_, ival), this};
else
return iterator{overlap_find_i_ex<false>(from.node_, ival), this};
}
const_iterator
overlap_find_next_in_subtree(const_iterator from, interval_type const& ival, bool exclusive = false) const
{
if (root_ == nullptr)
return end();
return const_iterator{overlap_find_i_ex(from.node_, ival, exclusive), this};
if (exclusive)
return const_iterator{overlap_find_i_ex<true>(from.node_, ival), this};
else
return const_iterator{overlap_find_i_ex<false>(from.node_, ival), this};
}
// Convenience, so that a mutable iterator does not have to be converted by hand.
const_iterator
overlap_find_next_in_subtree(iterator from, interval_type const& ival, bool exclusive = false) const
{
return overlap_find_next_in_subtree(const_iterator{from}, ival, exclusive);
}

/**
Expand Down Expand Up @@ -1793,9 +1821,10 @@ namespace lib_interval_tree
return find_i_ex(ptr, ival, compare);
}

// excludes ptr
// excludes ptr, which is therefore never dereferenced for anything but its child pointers.
template <typename ComparatorFunctionT>
node_type* find_i_ex(node_type* ptr, interval_type const& ival, ComparatorFunctionT const& compare) const
node_type*
find_i_ex(node_type const* ptr, interval_type const& ival, ComparatorFunctionT const& compare) const
{
if (ptr->left_ && ival.high() <= ptr->left_->max())
{
Expand Down Expand Up @@ -1877,9 +1906,9 @@ namespace lib_interval_tree
return true;
}

// excludes ptr
// excludes ptr, which is therefore never dereferenced for anything but its child pointers.
template <bool Exclusive>
node_type* overlap_find_i_ex(node_type* ptr, interval_type const& ival) const
node_type* overlap_find_i_ex(node_type const* ptr, interval_type const& ival) const
{
if (ptr->left_ && ptr->left_->max() >= ival.low())
{
Expand Down
65 changes: 65 additions & 0 deletions tests/find_tests.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,71 @@ TEST_F(FindTests, CanFindAllOnConstTree)
EXPECT_EQ(intervals[0], targetInterval);
}

TEST_F(FindTests, WillReturnEndForNextInSubtreeIfTreeIsEmpty)
{
EXPECT_EQ(tree.find_next_in_subtree(tree.root(), {2, 7}), std::end(tree));
}

TEST_F(FindTests, WillFindNextExactMatchInSubtree)
{
const auto targetInterval = decltype(tree)::interval_type{0, 5};
tree.insert(targetInterval);
tree.insert({10, 15});
tree.insert(targetInterval);
tree.insert({30, 35});

// The node started from is excluded, so this must find the duplicate and not the root itself.
const auto found = tree.find_next_in_subtree(tree.root(), targetInterval);
ASSERT_NE(found, std::end(tree));
EXPECT_EQ(*found, targetInterval);
EXPECT_NE(found, tree.root());

EXPECT_EQ(tree.find_next_in_subtree(tree.root(), {99, 100}), std::end(tree));
}

TEST_F(FindTests, WillFindNextExactMatchInSubtreeOnConstTree)
{
const auto targetInterval = decltype(tree)::interval_type{0, 5};
tree.insert(targetInterval);
tree.insert({10, 15});
tree.insert(targetInterval);
tree.insert({30, 35});

[&targetInterval](auto const& tree) {
const auto found = tree.find_next_in_subtree(tree.root(), targetInterval);
static_assert(
std::is_same<decltype(found), typename std::decay_t<decltype(tree)>::const_iterator const>::value,
"the const overload must yield a const_iterator"
);
ASSERT_NE(found, std::end(tree));
EXPECT_EQ(*found, targetInterval);
}(tree);
}

TEST_F(FindTests, CanFindNextExactMatchInSubtreeOnConstTreeFromMutableIterator)
{
const auto targetInterval = decltype(tree)::interval_type{0, 5};
tree.insert(targetInterval);
tree.insert({10, 15});
tree.insert(targetInterval);
tree.insert({30, 35});

// A mutable iterator is accepted by the const overloads without converting it by hand.
const auto from = tree.root();
const auto compare = [](auto const& lhs, auto const& rhs) {
return lhs == rhs;
};
[&from, &targetInterval, &compare](auto const& tree) {
const auto found = tree.find_next_in_subtree(from, targetInterval);
ASSERT_NE(found, std::end(tree));
EXPECT_EQ(*found, targetInterval);

const auto foundWithCompare = tree.find_next_in_subtree(from, targetInterval, compare);
ASSERT_NE(foundWithCompare, std::end(tree));
EXPECT_EQ(*foundWithCompare, targetInterval);
}(tree);
}

TEST_F(FindTests, FuzzyFindAllInTree)
{
std::mt19937 gen{0};
Expand Down
80 changes: 80 additions & 0 deletions tests/overlap_find_tests.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,86 @@ TEST_F(OverlapFindTests, CanOverlapFindAllOnConstTree)
EXPECT_EQ(intervals[0], targetInterval);
}

TEST_F(OverlapFindTests, WillReturnEndForNextInSubtreeIfTreeIsEmpty)
{
EXPECT_EQ(tree.overlap_find_next_in_subtree(tree.root(), {2, 7}), std::end(tree));
}

TEST_F(OverlapFindTests, WillFindNextOverlapInSubtree)
{
tree.insert({0, 5});
tree.insert({10, 15});
tree.insert({20, 25});
tree.insert({30, 35});

const auto found = tree.overlap_find_next_in_subtree(tree.root(), {21, 22});
ASSERT_NE(found, std::end(tree));
EXPECT_EQ(*found, (decltype(tree)::interval_type{20, 25}));
}

TEST_F(OverlapFindTests, WillNotFindNextOverlapInSubtreeWithTheStartNodeItself)
{
tree.insert({0, 5});
tree.insert({10, 15});
tree.insert({20, 25});
tree.insert({30, 35});

// The node started from is excluded and all intervals are disjoint, so nothing else can overlap it.
const auto rootInterval = *tree.root();
EXPECT_EQ(tree.overlap_find_next_in_subtree(tree.root(), rootInterval), std::end(tree));
}

TEST_F(OverlapFindTests, WillObeyExclusiveWhenFindingNextOverlapInSubtree)
{
tree.insert({0, 5});
tree.insert({10, 15});
tree.insert({20, 25});
tree.insert({30, 35});

// {25, 27} touches {20, 25} on the border only.
const auto inclusive = tree.overlap_find_next_in_subtree(tree.root(), {25, 27}, false);
ASSERT_NE(inclusive, std::end(tree));
EXPECT_EQ(*inclusive, (decltype(tree)::interval_type{20, 25}));

EXPECT_EQ(tree.overlap_find_next_in_subtree(tree.root(), {25, 27}, true), std::end(tree));
}

TEST_F(OverlapFindTests, WillFindNextOverlapInSubtreeOnConstTree)
{
tree.insert({0, 5});
tree.insert({10, 15});
tree.insert({20, 25});
tree.insert({30, 35});

const auto expected = decltype(tree)::interval_type{20, 25};
[&expected](auto const& tree) {
const auto found = tree.overlap_find_next_in_subtree(tree.root(), {21, 22});
ASSERT_NE(found, std::end(tree));
EXPECT_EQ(*found, expected);
}(tree);
}

TEST_F(OverlapFindTests, CanConvertIteratorToConstIteratorForNextOverlapInSubtree)
{
tree.insert({0, 5});
tree.insert({10, 15});
tree.insert({20, 25});
tree.insert({30, 35});

// The conversion itself is explicit, but the const overloads also accept a mutable iterator directly.
const auto converted = decltype(tree)::const_iterator{tree.root()};
const auto mutableIterator = tree.root();
const auto expected = decltype(tree)::interval_type{20, 25};
[&converted, &mutableIterator, &expected](auto const& tree) {
const auto found = tree.overlap_find_next_in_subtree(converted, {21, 22});
ASSERT_NE(found, std::end(tree));
EXPECT_EQ(*found, expected);

const auto fromMutable = tree.overlap_find_next_in_subtree(mutableIterator, {21, 22});
EXPECT_EQ(fromMutable, found);
}(tree);
}

TEST_F(OverlapFindTests, FuzzyOverlapFindAllInTree)
{
std::mt19937 gen{0};
Expand Down
Loading