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
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ Creates an interval where the borders are sorted so the lower border is the firs
- [Example](#example-1)
- [(const)iterator find_all(interval_type const& ival, OnFindFunctionT const& on_find, CompareFunctionT const& compare)](#constiterator-find_allinterval_type-const-ival-onfindfunctiont-const-on_find-comparefunctiont-const-compare)
- [(const)iterator find_next_in_subtree(iterator from, interval_type const& ival)](#constiterator-find_next_in_subtreeiterator-from-interval_type-const-ival)
- [(const)iterator find_next(iterator from, interval_type const& ival, CompareFunctionT const& compare)](#constiterator-find_nextiterator-from-interval_type-const-ival-comparefunctiont-const-compare)
- [(const)iterator find_next_in_subtree(iterator from, interval_type const& ival, CompareFunctionT const& compare)](#constiterator-find_next_in_subtreeiterator-from-interval_type-const-ival-comparefunctiont-const-compare)
- [(const)iterator overlap_find(interval_type const& ival, bool exclusive)](#constiterator-overlap_findinterval_type-const-ival-bool-exclusive)
- [(const)iterator overlap_find_all(interval_type const& ival, OnFindFunctionT const& on_find, bool exclusive)](#constiterator-overlap_find_allinterval_type-const-ival-onfindfunctiont-const-on_find-bool-exclusive)
- [Example](#example-2)
Expand Down Expand Up @@ -127,11 +127,11 @@ Finds the first interval in the interval tree that has an exact match.

---
### (const)iterator find(interval_type const& ival, CompareFunctionT const& compare)
Finds the first interval in the interval tree that has the following statement evaluate to true: compare(ival, interval_in_tree);
Finds the first interval in the interval tree that has the following statement evaluate to true: compare(interval_in_tree, ival);
Allows for propper float comparisons.
#### Parameters
* `ival` The interval to find.
* `compare` The compare function to compare intervals with.
* `compare` The compare function to compare intervals with. Function is called like so: compare(interval_in_tree, ival).

**Returns**: An iterator to the found element, or std::end(tree).

Expand Down Expand Up @@ -160,7 +160,7 @@ tree.find_all({3, 7}, [](auto iter) /* iter will be const_iterator if tree is co
Find all intervals in the tree that the compare function returns true for.
#### Parameters
* `ival` The interval to find.
* `compare` The compare function to compare intervals with.
* `compare` The compare function to compare intervals with. Function is called like so: compare(interval_in_tree, ival).
* `on_find` A function of type bool(iterator) that is called when an interval was found.
Return true to continue, false to preemptively abort search.

Expand All @@ -177,13 +177,13 @@ You cannot find all matches this way, use find_all for that.
**Returns**: An iterator to the found element, or std::end(tree).

---
### (const)iterator find_next(iterator from, interval_type const& ival, CompareFunctionT const& compare)
### (const)iterator find_next_in_subtree(iterator from, interval_type const& ival, CompareFunctionT const& compare)
Finds the next exact match EXCLUDING from in the subtree originating from "from".
You cannot find all matches this way, use find_all for that.
#### Parameters
* `from` The iterator to start from (including this iterator!)
* `ival` The interval to find.
* `compare` The compare function to compare intervals with.
* `compare` The compare function to compare intervals with. Function is called like so: compare(interval_in_tree, ival).

**Returns**: An iterator to the found element, or std::end(tree).

Expand Down
73 changes: 48 additions & 25 deletions interval_tree.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,19 @@ namespace lib_interval_tree
* Constructs an interval. low MUST be smaller than high.
*/
#ifndef INTERVAL_TREE_SAFE_INTERVALS
#if __cplusplus >= 201703L
constexpr
#endif
interval(value_type low, value_type high)
: low_{low}
, high_{high}
{
assert(low <= high);
}
#else
#if __cplusplus >= 201703L
constexpr
#endif
interval(value_type low, value_type high)
: low_{std::min(low, high)}
, high_{std::max(low, high)}
Expand Down Expand Up @@ -173,6 +179,9 @@ namespace lib_interval_tree
* Creates a safe interval that puts the lower bound left automatically.
*/
template <typename numerical_type, typename interval_kind_ = closed>
#if __cplusplus >= 201703L
constexpr
#endif
interval <numerical_type, interval_kind_> make_safe_interval(numerical_type lhs, numerical_type rhs)
{
return interval <numerical_type, interval_kind_>{std::min(lhs, rhs), std::max(lhs, rhs)};
Expand Down Expand Up @@ -621,6 +630,7 @@ namespace lib_interval_tree
using iterator = interval_tree_iterator <node_type>;
using const_iterator = const_interval_tree_iterator <node_type>;
using size_type = long long;
using this_type = interval_tree<interval_type>;

public:
friend const_interval_tree_iterator <node_type>;
Expand Down Expand Up @@ -854,14 +864,14 @@ namespace lib_interval_tree
{
if (root_ == nullptr)
return;
find_all_i<iterator>(root_, ival, on_find, compare);
find_all_i<this_type, iterator>(this, root_, ival, on_find, compare);
}
template <typename FunctionT, typename CompareFunctionT>
void find_all(interval_type const& ival, FunctionT const& on_find, CompareFunctionT const& compare) const
{
if (root_ == nullptr)
return;
find_all_i<const_iterator>(root_, ival, on_find, compare);
find_all_i<this_type, const_iterator>(this, root_, ival, on_find, compare);
}

template <typename FunctionT>
Expand Down Expand Up @@ -950,19 +960,19 @@ namespace lib_interval_tree
if (root_ == nullptr)
return;
if (exclusive)
overlap_find_all_i<true, iterator>(root_, ival, on_find);
overlap_find_all_i<this_type, true, iterator>(this, root_, ival, on_find);
else
overlap_find_all_i<false, iterator>(root_, ival, on_find);
overlap_find_all_i<this_type, false, iterator>(this, root_, ival, on_find);
}
template <typename FunctionT>
void overlap_find_all(interval_type const& ival, FunctionT const& on_find, bool exclusive = false) const
{
if (root_ == nullptr)
return;
if (exclusive)
overlap_find_all_i<true, const_iterator>(root_, ival, on_find);
overlap_find_all_i<this_type, true, const_iterator>(this, root_, ival, on_find);
else
overlap_find_all_i<false, const_iterator>(root_, ival, on_find);
overlap_find_all_i<this_type, false, const_iterator>(this, root_, ival, on_find);
}

/**
Expand Down Expand Up @@ -1114,36 +1124,43 @@ namespace lib_interval_tree
return nullptr;
};

template <typename IteratorT, typename FunctionT, typename ComparatorFunctionT>
bool find_all_i(node_type* ptr, interval_type const& ival, FunctionT const& on_find, ComparatorFunctionT const& compare)
template <typename ThisType, typename IteratorT, typename FunctionT, typename ComparatorFunctionT>
static bool find_all_i
(
typename std::conditional<std::is_same<IteratorT, iterator>::value, ThisType, ThisType const>::type* self,
node_type* ptr,
interval_type const& ival,
FunctionT const& on_find,
ComparatorFunctionT const& compare
)
{
if (compare(ptr->interval(), ival))
{
if (!on_find(IteratorT{ptr, this}))
if (!on_find(IteratorT{ptr, self}))
return false;
}
if (ptr->left_ && ival.high() <= ptr->left_->max())
{
// no right? can only continue left
if (!ptr->right_ || ival.low() > ptr->right_->max())
return find_all_i<IteratorT>(ptr->left_, ival, on_find, compare);
return find_all_i<ThisType, IteratorT>(self, ptr->left_, ival, on_find, compare);

if (!find_all_i<IteratorT>(ptr->left_, ival, on_find, compare))
if (!find_all_i<ThisType, IteratorT>(self, ptr->left_, ival, on_find, compare))
return false;
}
if (ptr->right_ && ival.high() <= ptr->right_->max())
{
if (!ptr->left_ || ival.low() > ptr->left_->max())
return find_all_i<IteratorT>(ptr->right_, ival, on_find, compare);
return find_all_i<ThisType, IteratorT>(self, ptr->right_, ival, on_find, compare);

if (!find_all_i<IteratorT>(ptr->right_, ival, on_find, compare))
if (!find_all_i<ThisType, IteratorT>(self, ptr->right_, ival, on_find, compare))
return false;
}
return true;
}

template <typename ComparatorFunctionT>
node_type* find_i(node_type* ptr, interval_type const& ival, ComparatorFunctionT const& compare)
node_type* find_i(node_type* ptr, interval_type const& ival, ComparatorFunctionT const& compare) const
{
if (compare(ptr->interval(), ival))
return ptr;
Expand All @@ -1153,7 +1170,7 @@ namespace lib_interval_tree

// excludes ptr
template <typename ComparatorFunctionT>
node_type* find_i_ex(node_type* ptr, interval_type const& ival, ComparatorFunctionT const& compare)
node_type* find_i_ex(node_type* ptr, interval_type const& ival, ComparatorFunctionT const& compare) const
{
if (ptr->left_ && ival.high() <= ptr->left_->max())
{
Expand All @@ -1178,7 +1195,7 @@ namespace lib_interval_tree
}

template <bool Exclusive>
node_type* overlap_find_i(node_type* ptr, interval_type const& ival)
node_type* overlap_find_i(node_type* ptr, interval_type const& ival) const
{
#if __cplusplus >= 201703L
if constexpr (Exclusive)
Expand All @@ -1198,8 +1215,14 @@ namespace lib_interval_tree
return overlap_find_i_ex<Exclusive>(ptr, ival);
}

template <bool Exclusive, typename IteratorT, typename FunctionT>
bool overlap_find_all_i(node_type* ptr, interval_type const& ival, FunctionT const& on_find)
template <typename ThisType, bool Exclusive, typename IteratorT, typename FunctionT>
static bool overlap_find_all_i
(
typename std::conditional<std::is_same<IteratorT, iterator>::value, ThisType, ThisType const>::type* self,
node_type* ptr,
interval_type const& ival,
FunctionT const& on_find
)
{
#if __cplusplus >= 201703L
if constexpr (Exclusive)
Expand All @@ -1209,7 +1232,7 @@ namespace lib_interval_tree
{
if (ptr->interval().overlaps_exclusive(ival))
{
if (!on_find(IteratorT{ptr, this}))
if (!on_find(IteratorT{ptr, self}))
{
return false;
}
Expand All @@ -1219,7 +1242,7 @@ namespace lib_interval_tree
{
if (ptr->interval().overlaps(ival))
{
if (!on_find(IteratorT{ptr, this}))
if (!on_find(IteratorT{ptr, self}))
{
return false;
}
Expand All @@ -1230,25 +1253,25 @@ namespace lib_interval_tree
// no right? can only continue left
// or interval low is bigger than max of right branch.
if (!ptr->right_ || ival.low() > ptr->right_->max())
return overlap_find_all_i<Exclusive, IteratorT>(ptr->left_, ival, on_find);
return overlap_find_all_i<ThisType, Exclusive, IteratorT>(self, ptr->left_, ival, on_find);

if (!overlap_find_all_i<Exclusive, IteratorT>(ptr->left_, ival, on_find))
if (!overlap_find_all_i<ThisType, Exclusive, IteratorT>(self, ptr->left_, ival, on_find))
return false;
}
if (ptr->right_ && ptr->right_->max() >= ival.low())
{
if (!ptr->left_ || ival.low() > ptr->right_->max())
return overlap_find_all_i<Exclusive, IteratorT>(ptr->right_, ival, on_find);
return overlap_find_all_i<ThisType, Exclusive, IteratorT>(self, ptr->right_, ival, on_find);

if (!overlap_find_all_i<Exclusive, IteratorT>(ptr->right_, ival, on_find))
if (!overlap_find_all_i<ThisType, Exclusive, IteratorT>(self, ptr->right_, ival, on_find))
return false;
}
return true;
}

// excludes ptr
template <bool Exclusive>
node_type* overlap_find_i_ex(node_type* ptr, interval_type const& ival)
node_type* overlap_find_i_ex(node_type* ptr, interval_type const& ival) const
{
if (ptr->left_ && ptr->left_->max() >= ival.low())
{
Expand Down
2 changes: 1 addition & 1 deletion tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ file(GLOB sources "*.cpp")
# Add Executable
add_executable(tree-tests ${sources})

target_link_libraries(tree-tests gtest)
target_link_libraries(tree-tests gtest gmock)

# Options
if(DRAW_EXAMPLES)
Expand Down
29 changes: 29 additions & 0 deletions tests/find_tests.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,15 @@ TEST_F(FindTests, WillFindRoot)
EXPECT_EQ(tree.find({0, 1}), std::begin(tree));
}

TEST_F(FindTests, WillFindRootOnConstTree)
{
tree.insert({0, 1});
[](auto const& tree)
{
EXPECT_EQ(tree.find({0, 1}), std::begin(tree));
}(tree);
}

TEST_F(FindTests, WillFindInBiggerTree)
{
tree.insert({16, 21});
Expand Down Expand Up @@ -137,3 +146,23 @@ TEST_F(FindTests, CanFindAllElementsBackInStrictlyAscendingOverlappingIntervals)
ASSERT_NE(tree.find(ival), std::end(tree));
}
}

TEST_F(FindTests, CanFindAllOnConstTree)
{
const auto targetInterval = lib_interval_tree::make_safe_interval(16, 21);
tree.insert(targetInterval);
tree.insert({8, 9});
tree.insert({25, 30});
std::vector <decltype(tree)::interval_type> intervals;
auto findWithConstTree = [&intervals, &targetInterval](auto const& tree)
{
tree.find_all(targetInterval, [&intervals](auto const& iter) {
intervals.emplace_back(*iter);
return true;
});
};
findWithConstTree(tree);

ASSERT_EQ(intervals.size(), 1);
EXPECT_EQ(intervals[0], targetInterval);
}
6 changes: 2 additions & 4 deletions tests/interval_tests.hpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
#pragma once

#include <boost/preprocessor/comma.hpp>

#include <limits>

class IntervalTests
Expand Down Expand Up @@ -30,13 +28,13 @@ class DistanceTests
{
public:
using types = IntervalTypes <int>;
};
};

TEST_F(IntervalTests, FailBadBorders)
{
auto f = []()
{
[[maybe_unused]] auto ival = types::interval_type{1 BOOST_PP_COMMA() 0};
[[maybe_unused]] auto ival = types::interval_type{1, 0};
};

EXPECT_DEATH(f(), "low <= high");
Expand Down
30 changes: 29 additions & 1 deletion tests/overlap_find_tests.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ TEST_F(OverlapFindTests, WillFindOverlapWithRoot)
EXPECT_EQ(tree.overlap_find({2, 7}), std::begin(tree));
}

TEST_F(OverlapFindTests, WillFindOverlapWithRootOnConstTree)
{
tree.insert({2, 4});
[](auto const& tree) {
EXPECT_EQ(tree.overlap_find({2, 7}), std::begin(tree));
}(tree);
}

TEST_F(OverlapFindTests, WillFindOverlapWithRootIfMatchingExactly)
{
tree.insert({2, 7});
Expand Down Expand Up @@ -149,4 +157,24 @@ TEST_F(OverlapFindTests, WillFindSingleOverlapInBiggerTree)
EXPECT_NE(iter, std::end(tree));
EXPECT_EQ(iter->low(), 1000);
EXPECT_EQ(iter->high(), 2000);
}
}

TEST_F(FindTests, CanOverlapFindAllOnConstTree)
{
const auto targetInterval = lib_interval_tree::make_safe_interval(16, 21);
tree.insert(targetInterval);
tree.insert({8, 9});
tree.insert({25, 30});
std::vector <decltype(tree)::interval_type> intervals;
auto findWithConstTree = [&intervals, &targetInterval](auto const& tree)
{
tree.overlap_find_all(targetInterval, [&intervals](auto const& iter) {
intervals.emplace_back(*iter);
return true;
});
};
findWithConstTree(tree);

ASSERT_EQ(intervals.size(), 1);
EXPECT_EQ(intervals[0], targetInterval);
}