From f2760e281a874e8edf107f57aacfdd4cbfd12e95 Mon Sep 17 00:00:00 2001 From: Tim Ebbeke Date: Fri, 2 Aug 2024 19:13:14 +0200 Subject: [PATCH 1/4] Made interval types meaningful. --- include/interval-tree/feature_test.hpp | 26 +- include/interval-tree/interval_tree.hpp | 209 +++++- include/interval-tree/interval_types.hpp | 352 +++++++++- tests/interval_tests.hpp | 785 +++++++++++++++++++---- 4 files changed, 1224 insertions(+), 148 deletions(-) diff --git a/include/interval-tree/feature_test.hpp b/include/interval-tree/feature_test.hpp index 6b2fd4d..e0a73fd 100644 --- a/include/interval-tree/feature_test.hpp +++ b/include/interval-tree/feature_test.hpp @@ -1,9 +1,25 @@ #pragma once -#if defined(__cpp_concepts) && __cpp_concepts >= 202002L -# define LIB_INTERVAL_TREE_CONCEPTS +// define LIB_INTERVAL_TREE_CONCEPTS to override the concepts feature test +#ifndef LIB_INTERVAL_TREE_CONCEPTS +# if defined(__cpp_concepts) && __cpp_concepts >= 202002L +# define LIB_INTERVAL_TREE_CONCEPTS +# endif #endif -namespace lib_interval_tree -{ -} \ No newline at end of file +// define LIB_INTERVAL_TREE_DEPRECATED if __attribute__((deprecated)) is not supported +#ifndef LIB_INTERVAL_TREE_DEPRECATED +# if __has_cpp_attribute(depreacted) +# define LIB_INTERVAL_TREE_DEPRECATED [[deprecated]] +# else +# define LIB_INTERVAL_TREE_DEPRECATED __attribute__((deprecated)) +# endif +#endif + +#ifndef LIB_INTERVAL_TREE_FALLTHROUGH +# if __has_cpp_attribute(fallthrough) +# define LIB_INTERVAL_TREE_FALLTHROUGH [[fallthrough]] +# else +# define LIB_INTERVAL_TREE_FALLTHROUGH __attribute__((fallthrough)) +# endif +#endif \ No newline at end of file diff --git a/include/interval-tree/interval_tree.hpp b/include/interval-tree/interval_tree.hpp index 88990c7..a1fc0ab 100644 --- a/include/interval-tree/interval_tree.hpp +++ b/include/interval-tree/interval_tree.hpp @@ -3,6 +3,7 @@ #include "interval_tree_fwd.hpp" #include "interval_types.hpp" #include "tree_hooks.hpp" +#include "feature_test.hpp" #include #include @@ -26,8 +27,8 @@ namespace lib_interval_tree // ############################################################################################################ using default_interval_value_type = int; // ############################################################################################################ - template - struct interval + template + struct interval_base { public: using value_type = numerical_type; @@ -40,7 +41,7 @@ namespace lib_interval_tree # if __cplusplus >= 201703L constexpr # endif - interval(value_type low, value_type high) + interval_base(value_type low, value_type high) : low_{low} , high_{high} { @@ -51,52 +52,86 @@ namespace lib_interval_tree # if __cplusplus >= 201703L constexpr # endif - interval(value_type low, value_type high) + interval_base(value_type low, value_type high) : low_{std::min(low, high)} , high_{std::max(low, high)} {} #endif - virtual ~interval() = default; + virtual ~interval_base() = default; /** - * Returns if both intervals equal. + * Returns the lower bound of the interval */ - friend bool operator==(interval const& lhs, interval const& other) + value_type low() const { - return lhs.low_ == other.low_ && lhs.high_ == other.high_; + return low_; } /** - * Returns if both intervals are different. + * Returns the upper bound of the interval */ - friend bool operator!=(interval const& lhs, interval const& other) + value_type high() const { - return lhs.low_ != other.low_ || lhs.high_ != other.high_; + return high_; } + protected: + value_type low_; + value_type high_; + }; + // ############################################################################################################ + template + struct interval : public interval_base + { + public: + using value_type = typename interval_base::value_type; + using interval_kind = typename interval_base::interval_kind; + + using interval_base::low; + using interval_base::high; + + using interval_base::low_; + using interval_base::high_; + /** - * Returns the lower bound of the interval + * Constructs an interval. low MUST be smaller than high. */ - value_type low() const +#if __cplusplus >= 201703L + constexpr +#endif + interval(value_type low, value_type high) + : interval_base{low, high} + {} + + /** + * Returns true if both intervals equal. + */ + friend bool operator==( + interval const& lhs, + interval const& rhs + ) { - return low_; + return lhs.low_ == rhs.low_ && lhs.high_ == rhs.high_; } /** - * Returns the upper bound of the interval + * Returns true if both intervals are different. */ - value_type high() const + friend bool operator!=( + interval const& lhs, + interval const& rhs + ) { - return high_; + return lhs.low_ != rhs.low_ || lhs.high_ != rhs.high_; } /** * Returns whether the intervals overlap. * For when both intervals are closed. */ - bool overlaps(value_type l, value_type h) const + LIB_INTERVAL_TREE_DEPRECATED bool overlaps(value_type l, value_type h) const { - return low_ <= h && l <= high_; + return interval_kind::overlaps(low_, high_, l, h); } /** @@ -113,7 +148,7 @@ namespace lib_interval_tree */ bool overlaps(interval const& other) const { - return overlaps(other.low_, other.high_); + return interval_kind::overlaps(low_, high_, other.low_, other.high_); } /** @@ -137,7 +172,7 @@ namespace lib_interval_tree */ bool within(interval const& other) const { - return low_ <= other.low_ && high_ >= other.high_; + return within(other.low_) && within(other.high_); } /** @@ -148,18 +183,113 @@ namespace lib_interval_tree { if (overlaps(other)) return 0; - if (high_ < other.low_) + if (high_ <= other.low_) return other.low_ - high_; else return low_ - other.high_; } + /** + * Creates a new interval from this and other, that contains both intervals and whatever + * is between. + */ + interval join(interval const& other) const + { + return {std::min(low_, other.low_), std::max(high_, other.high_)}; + } + /** * Returns the size of the interval. */ value_type size() const { - return high_ - low_; + return interval_kind::size(low_, high_); + } + }; + + template + struct interval : public interval_base + { + public: + using value_type = typename interval_base::value_type; + using interval_kind = dynamic; + + /** + * Constructs an interval. low MUST be smaller than high. + */ +#if __cplusplus >= 201703L + constexpr +#endif + interval(value_type low, value_type high, interval_border leftBorder, interval_border rightBorder) + : interval_base{low, high} + , left_border_{leftBorder} + , right_border_{rightBorder} + {} + + /** + * Returns true if both intervals equal. + */ + friend bool + operator==(interval const& lhs, interval const& other) + { + return lhs.low_ == other.low_ && lhs.high_ == other.high_ && lhs.left_border_ == other.left_border_ && + lhs.right_border_ == other.right_border_; + } + + /** + * Returns true if both intervals are different. + */ + friend bool + operator!=(interval const& lhs, interval const& other) + { + return lhs.low_ != other.low_ || lhs.high_ != other.high_ || lhs.left_border_ != other.left_border_ || + lhs.right_border_ != other.right_border_; + } + + using interval_base::low; + using interval_base::high; + using interval_base::low_; + using interval_base::high_; + + /** + * Returns whether the intervals overlap + */ + bool overlaps(interval const& other) const + { + return interval_kind::overlaps(*this, other); + } + + /** + * Returns whether the intervals overlap exclusively, independent of border. + */ + bool overlaps_exclusive(interval const& other) const + { + return low_ < other.high_ && other.low_ < high_; + } + + /** + * Returns whether the given value is in this. + */ + bool within(value_type value) const + { + return interval_kind::within(*this, value); + } + + /** + * Returns whether the given interval is in this. + */ + bool within(interval const& other) const + { + return within(other.low_) && within(other.high_); + } + + /** + * Calculates the distance between the two intervals. + * Overlapping intervals have 0 distance. + */ + value_type operator-(interval const& other) const + { + interval_kind::distance(*this, other); } /** @@ -168,13 +298,40 @@ namespace lib_interval_tree */ interval join(interval const& other) const { - return {std::min(low_, other.low_), std::max(high_, other.high_)}; + return interval_kind::join(*this, other); + } + + /** + * Returns the size of the interval. + */ + value_type size() const + { + return interval_kind::size(*this); + } + + /** + * @brief Returns the left border type of the interval. + * + */ + interval_border left_border() const + { + return left_border_; + } + + /** + * @brief Returns the right border type of the interval. + * + */ + interval_border right_border() const + { + return right_border_; } protected: - value_type low_; - value_type high_; + interval_border left_border_; + interval_border right_border_; }; + // ############################################################################################################ /** * Creates a safe interval that puts the lower bound left automatically. diff --git a/include/interval-tree/interval_types.hpp b/include/interval-tree/interval_types.hpp index 0cfa5fe..2178267 100644 --- a/include/interval-tree/interval_types.hpp +++ b/include/interval-tree/interval_types.hpp @@ -1,41 +1,377 @@ #pragma once +#include "feature_test.hpp" + +#ifdef LIB_INTERVAL_TREE_CONCEPTS +# include +#endif + +#include +#include + namespace lib_interval_tree { // (] struct left_open { template - static inline bool within(numerical_type b, numerical_type e, numerical_type p) + static inline bool within(numerical_type low, numerical_type high, numerical_type p) + { + return (low < p) && (p <= high); + } + + template + static inline bool overlaps(numerical_type l1, numerical_type h1, numerical_type l2, numerical_type h2) + { + return (l1 < h2) && (l2 <= h1); + } + + template + static inline numerical_type size(numerical_type low, numerical_type high) { - return (b < p) && (p <= e); + return high - low; } }; // [) struct right_open { template - static inline bool within(numerical_type b, numerical_type e, numerical_type p) + static inline bool within(numerical_type low, numerical_type high, numerical_type p) { - return (b <= p) && (p < e); + return (low <= p) && (p < high); + } + + template + static inline bool overlaps(numerical_type l1, numerical_type h1, numerical_type l2, numerical_type h2) + { + return (l1 <= h2) && (l2 < h1); + } + + template + static inline numerical_type size(numerical_type low, numerical_type high) + { + return high - low; } }; // [] struct closed { template - static inline bool within(numerical_type b, numerical_type e, numerical_type p) + static inline bool within(numerical_type low, numerical_type high, numerical_type p) { - return (b <= p) && (p <= e); + return (low <= p) && (p <= high); + } + + template + static inline bool overlaps(numerical_type l1, numerical_type h1, numerical_type l2, numerical_type h2) + { + return (l1 <= h2) && (l2 <= h1); + } + + template + static inline numerical_type size(numerical_type low, numerical_type high) + { + return high - low + 1; } }; // () struct open { template - static inline bool within(numerical_type b, numerical_type e, numerical_type p) + static inline bool within(numerical_type low, numerical_type high, numerical_type p) { - return (b < p) && (p < e); + return (low < p) && (p < high); + } + + template + static inline bool overlaps(numerical_type l1, numerical_type h1, numerical_type l2, numerical_type h2) + { + return (l1 < h2) && (l2 < h1); + } + + template + static inline numerical_type size(numerical_type low, numerical_type high) + { + return high - low - 1; + } + }; + /// [] and adjacent counts as overlapping + struct closed_adjacent + { + template + static inline bool within(numerical_type low, numerical_type high, numerical_type p) + { + return (low <= p) && (p <= high); + } + + template + static inline bool overlaps(numerical_type l1, numerical_type h1, numerical_type l2, numerical_type h2) + { + return (l1 <= (h2 + 1)) && ((l2 - 1) <= h1); + } + + template + static inline numerical_type size(numerical_type low, numerical_type high) + { + return high - low + 1; + } + }; + + enum class interval_border + { + closed, + open, + closed_adjacent + }; + class dynamic + { + public: + template +#ifdef LIB_INTERVAL_TREE_CONCEPTS + requires std::is_integral_v +#endif + static inline bool within(interval_type const& ival, typename interval_type::value_type p) + { + switch (ival.left_border()) + { + case interval_border::open: + { + if (ival.low() >= p) + { + return false; + } + break; + } + case interval_border::closed_adjacent: + LIB_INTERVAL_TREE_FALLTHROUGH; + case interval_border::closed: + { + if (ival.low() > p) + { + return false; + } + break; + } + } + switch (ival.right_border()) + { + case interval_border::open: + { + if (p >= ival.high()) + { + return false; + } + break; + } + case interval_border::closed_adjacent: + LIB_INTERVAL_TREE_FALLTHROUGH; + case interval_border::closed: + { + if (p > ival.high()) + { + return false; + } + break; + } + } + return true; + } + + template +#ifdef LIB_INTERVAL_TREE_CONCEPTS + requires std::is_integral_v +#endif + static inline bool overlaps(interval_type const& ival1, interval_type const& ival2) + { + const auto lowToClosed = [](auto const& ival) { + if (ival.left_border() == interval_border::open) + return ival.low() + 1; + return ival.low(); + }; + + const auto highToClosed = [](auto const& ival) { + if (ival.right_border() == interval_border::open) + return ival.high() - 1; + return ival.high(); + }; + + const interval_type closedEquiv1{ + lowToClosed(ival1), highToClosed(ival1), interval_border::closed, interval_border::closed + }; + const interval_type closedEquiv2{ + lowToClosed(ival2), highToClosed(ival2), interval_border::closed, interval_border::closed + }; + + auto closedOverlap = + closed::overlaps(closedEquiv1.low(), closedEquiv1.high(), closedEquiv2.low(), closedEquiv2.high()); + if (!closedOverlap) + { + if (closedEquiv1.high() + 1 == closedEquiv2.low() && + (ival1.right_border() == interval_border::closed_adjacent || + ival2.left_border() == interval_border::closed_adjacent)) + { + return true; + } + if (closedEquiv2.high() + 1 == closedEquiv1.low() && + (ival2.right_border() == interval_border::closed_adjacent || + ival1.left_border() == interval_border::closed_adjacent)) + { + return true; + } + return false; + } + return true; + } + + template +#ifdef LIB_INTERVAL_TREE_CONCEPTS + requires std::is_integral_v +#endif + static typename interval_type::value_type distance(interval_type const& ival1, interval_type const& ival2) + { + using value_type = typename interval_type::value_type; + + if (overlaps(ival1, ival2)) + return 0; + + value_type adjusted_low = ival1.left_border() == interval_border::open ? ival1.low() + 1 : ival1.low(); + value_type adjusted_high = ival1.right_border() == interval_border::open ? ival1.high() - 1 : ival1.high(); + value_type other_adjusted_low = + ival2.left_border() == interval_border::open ? ival2.low() + 1 : ival2.low(); + value_type other_adjusted_high = + ival2.right_border() == interval_border::open ? ival2.high() - 1 : ival2.high(); + + if (adjusted_high < other_adjusted_low) + return other_adjusted_low - adjusted_high; + return adjusted_low - other_adjusted_high; + } + + static interval_border border_promote(interval_border border1, interval_border border2) + { + if (border1 == interval_border::closed_adjacent || border2 == interval_border::closed_adjacent) + return interval_border::closed_adjacent; + if (border1 == interval_border::closed || border2 == interval_border::closed) + return interval_border::closed; + return interval_border::open; + } + + /** + * @brief Assumes that ival1 and ival2 overlap and both intervals are dynamic + * + * @tparam interval_type + * @param ival1 + * @param ival2 + * @return interval_type + */ + template +#ifdef LIB_INTERVAL_TREE_CONCEPTS + requires std::is_integral_v +#endif + static interval_type join(interval_type const& ival1, interval_type const& ival2) + { + typename interval_type::value_type low; + typename interval_type::value_type high; + interval_border left; + interval_border right; + + const bool ival1LeftAnyClosed = ival1.left_border() == interval_border::closed_adjacent || + ival1.left_border() == interval_border::closed; + const bool ival2LeftAnyClosed = ival2.left_border() == interval_border::closed_adjacent || + ival2.left_border() == interval_border::closed; + const bool ival1RightAnyClosed = ival1.right_border() == interval_border::closed_adjacent || + ival1.right_border() == interval_border::closed; + const bool ival2RightAnyClosed = ival2.right_border() == interval_border::closed_adjacent || + ival2.right_border() == interval_border::closed; + + // same border means trivial min/max: + if (ival1.left_border() == ival2.left_border() || (ival1LeftAnyClosed && ival2LeftAnyClosed)) + { + left = border_promote(ival1.left_border(), ival2.left_border()); + low = std::min(ival1.low(), ival2.low()); + } + else + { + auto* leftOpenInterval = ival1.left_border() == interval_border::open ? &ival1 : &ival2; + auto* leftClosedInterval = (ival1.left_border() == interval_border::closed || + ival1.left_border() == interval_border::closed_adjacent) + ? &ival1 + : &ival2; + + const auto openAdjusted = leftOpenInterval->low() + 1; + + if (openAdjusted == leftClosedInterval->low()) + { + left = leftClosedInterval->left_border(); + low = leftClosedInterval->low(); + } + else if (leftOpenInterval->low() < leftClosedInterval->low()) + { + left = leftOpenInterval->left_border(); + low = leftOpenInterval->low(); + } + else + { + left = leftClosedInterval->left_border(); + low = leftClosedInterval->low(); + } + } + + if (ival1.right_border() == ival2.right_border() || (ival1RightAnyClosed && ival2RightAnyClosed)) + { + right = border_promote(ival1.right_border(), ival2.right_border()); + high = std::max(ival1.high(), ival2.high()); + } + else + { + auto* rightOpenInterval = ival1.right_border() == interval_border::open ? &ival1 : &ival2; + auto* rightClosedInterval = (ival1.right_border() == interval_border::closed || + ival1.right_border() == interval_border::closed_adjacent) + ? &ival1 + : &ival2; + + const auto openAdjusted = rightOpenInterval->high() - 1; + + if (openAdjusted == rightClosedInterval->high()) + { + right = rightClosedInterval->right_border(); + high = rightClosedInterval->high(); + } + else if (rightOpenInterval->high() > rightClosedInterval->high()) + { + right = rightOpenInterval->right_border(); + high = rightOpenInterval->high(); + } + else + { + right = rightClosedInterval->right_border(); + high = rightClosedInterval->high(); + } + } + + return interval_type{low, high, left, right}; + } + + template +#ifdef LIB_INTERVAL_TREE_CONCEPTS + requires std::is_integral_v +#endif + typename interval_type::value_type size(interval_type const& ival) + { + auto left = ival.left_border(); + if (left == interval_border::closed_adjacent) + left = interval_border::closed; + auto right = ival.right_border(); + if (right == interval_border::closed_adjacent) + right = interval_border::closed; + + if (left == right) + { + return left == interval_border::open ? open::size(ival.low(), ival.high()) + : closed::size(ival.low(), ival.high()); + } + if (left == interval_border::open) + return left_open::size(ival.low(), ival.high()); + return right_open::size(ival.low(), ival.high()); } }; } diff --git a/tests/interval_tests.hpp b/tests/interval_tests.hpp index 2f8d1cc..827d13e 100644 --- a/tests/interval_tests.hpp +++ b/tests/interval_tests.hpp @@ -2,42 +2,40 @@ #include -class IntervalTests - : public ::testing::Test -{ -public: - using types = IntervalTypes ; -}; +using namespace lib_interval_tree; -class OverlapTests - : public ::testing::Test +class IntervalTests : public ::testing::Test { -public: - using types = IntervalTypes ; -}; + public: + template + auto i(NumericalT l, NumericalT h) const + { + return lib_interval_tree::interval{l, h}; + } -class ContainmentTests - : public ::testing::Test -{ -public: - using types = IntervalTypes ; + template + auto i(NumericalT l, NumericalT h, interval_border l_border, interval_border r_border) const + { + return lib_interval_tree::interval{l, h, l_border, r_border}; + } }; -class DistanceTests - : public ::testing::Test -{ -public: - using types = IntervalTypes ; -}; +class OverlapTests : public IntervalTests +{}; + +class ContainmentTests : public IntervalTests +{}; + +class DistanceTests : public IntervalTests +{}; TEST_F(IntervalTests, FailBadBorders) { - auto f = []() - { + auto f = [this]() { #if __cplusplus >= 201703L - [[maybe_unused]] + [[maybe_unused]] #endif - auto ival = types::interval_type{1, 0}; + auto ival = i(1, 0); }; ASSERT_THROW(f(), std::invalid_argument); @@ -45,169 +43,364 @@ TEST_F(IntervalTests, FailBadBorders) TEST_F(IntervalTests, ShallCreateInterval) { - auto ival = types::interval_type{1, 24}; + auto ival = i(1, 24); EXPECT_EQ(ival.low(), 1); EXPECT_EQ(ival.high(), 24); } TEST_F(IntervalTests, ShallCreateInterval2) { - auto ival = types::interval_type{-23, 24}; + auto ival = i(-23, 24); EXPECT_EQ(ival.low(), -23); EXPECT_EQ(ival.high(), 24); } TEST_F(IntervalTests, ShallCreateInterval3) { - auto ival = types::interval_type{-21, -12}; + auto ival = i(-21, -12); EXPECT_EQ(ival.low(), -21); EXPECT_EQ(ival.high(), -12); } TEST_F(IntervalTests, ShallCreateInterval4) { - auto ival = types::interval_type{1, 24}; + auto ival = i(1, 24); EXPECT_EQ(ival.low(), 1); EXPECT_EQ(ival.high(), 24); } TEST_F(IntervalTests, ShallCreateInterval5) { - auto ival = types::interval_type{1, 1}; + auto ival = i(1, 1); EXPECT_EQ(ival.low(), 1); EXPECT_EQ(ival.high(), 1); } TEST_F(IntervalTests, TestLimits) { - auto ival = types::interval_type{std::numeric_limits::min(), std::numeric_limits::max()}; - EXPECT_EQ(ival.low(), std::numeric_limits::min()); - EXPECT_EQ(ival.high(), std::numeric_limits::max()); + auto ival = i(std::numeric_limits::min(), std::numeric_limits::max()); + EXPECT_EQ(ival.low(), std::numeric_limits::min()); + EXPECT_EQ(ival.high(), std::numeric_limits::max()); } TEST_F(IntervalTests, TestIntervalSize) { - auto ival = types::interval_type{0, 5}; - EXPECT_EQ(ival.size(), 5); + using lib_interval_tree::open; - auto ival2 = types::interval_type{-21, 5}; - EXPECT_EQ(ival2.size(), 26); + EXPECT_EQ(i(0, 5).size(), 6); + EXPECT_EQ(i(-12, 0).size(), 13); + EXPECT_EQ(i(-21, 5).size(), 27); + EXPECT_EQ(i(-20, -5).size(), 16); + EXPECT_EQ(i(100, 125).size(), 26); - auto ival3 = types::interval_type{-20, -5}; - EXPECT_EQ(ival3.size(), 15); + EXPECT_EQ(i(0, 5).size(), 5); + EXPECT_EQ(i(-12, 0).size(), 12); + EXPECT_EQ(i(-21, 5).size(), 26); + EXPECT_EQ(i(-20, -5).size(), 15); + EXPECT_EQ(i(100, 125).size(), 25); - auto ival4 = types::interval_type{100, 125}; - EXPECT_EQ(ival4.size(), 25); + EXPECT_EQ(i(0, 5).size(), 5); + EXPECT_EQ(i(-12, 0).size(), 12); + EXPECT_EQ(i(-21, 5).size(), 26); + EXPECT_EQ(i(-20, -5).size(), 15); + EXPECT_EQ(i(100, 125).size(), 25); + + EXPECT_EQ(i(0, 5).size(), 4); + EXPECT_EQ(i(-12, 0).size(), 11); + EXPECT_EQ(i(-21, 5).size(), 25); + EXPECT_EQ(i(-20, -5).size(), 14); + EXPECT_EQ(i(100, 125).size(), 24); + + EXPECT_EQ(i(0, 5).size(), 6); + EXPECT_EQ(i(-12, 0).size(), 13); + EXPECT_EQ(i(-21, 5).size(), 27); + EXPECT_EQ(i(-20, -5).size(), 16); + EXPECT_EQ(i(100, 125).size(), 26); } TEST_F(OverlapTests, ShallOverlapItself) { - auto base = types::interval_type{0, 5}; - EXPECT_EQ(base.overlaps(base), true); -} + using lib_interval_tree::open; -TEST_F(OverlapTests, ShallOverlapItself2) -{ - auto base = types::interval_type{0, 5}; - EXPECT_EQ(base.overlaps({0, 5}), true); -} + auto base = i(0, 5); + EXPECT_TRUE(base.overlaps(base)); -TEST_F(OverlapTests, ShallOverlapRight) -{ - auto base = types::interval_type{0, 5}; - EXPECT_EQ(base.overlaps({3, 16}), true); -} + auto base2 = i(-5, 0); + EXPECT_TRUE(base2.overlaps(base2)); -TEST_F(OverlapTests, ShallOverlapLeft) -{ - auto base = types::interval_type{0, 5}; - EXPECT_EQ(base.overlaps({-8, 1}), true); -} + auto base3 = i(-5, 0); + EXPECT_TRUE(base3.overlaps(base3)); -TEST_F(OverlapTests, ShallEncompassCompletely) -{ - auto base = types::interval_type{0, 5}; - EXPECT_EQ(base.overlaps({-99, 16}), true); -} + auto base4 = i(-5, 0); + EXPECT_TRUE(base4.overlaps(base4)); -TEST_F(OverlapTests, ShallBeContainedIn) -{ - auto base = types::interval_type{0, 5}; - EXPECT_EQ(base.overlaps({3, 4}), true); + auto base5 = i(-5, 0); + EXPECT_TRUE(base5.overlaps(base5)); } -TEST_F(OverlapTests, ExpectDisjunct) +TEST_F(OverlapTests, ShallOverlapItself2) { - auto base = types::interval_type{0, 5}; - EXPECT_EQ(base.overlaps({7, 19}), false); + using lib_interval_tree::open; + + auto base = i(0, 5); + EXPECT_TRUE(base.overlaps({0, 5})); + + auto base2 = i(-5, 0); + EXPECT_TRUE(base2.overlaps({-5, 0})); + + auto base3 = i(-5, 0); + EXPECT_TRUE(base3.overlaps({-5, 0})); + + auto base4 = i(-5, 0); + EXPECT_TRUE(base4.overlaps({-5, 0})); + + auto base5 = i(-5, 0); + EXPECT_TRUE(base4.overlaps({-5, 0})); +} + +TEST_F(OverlapTests, RightOverlapTests) +{ + using lib_interval_tree::open; + + // large overlap + EXPECT_TRUE(i(0, 5).overlaps({3, 16})); + EXPECT_TRUE(i(0, 5).overlaps({3, 16})); + EXPECT_TRUE(i(0, 5).overlaps({3, 16})); + EXPECT_TRUE(i(0, 5).overlaps({3, 16})); + EXPECT_TRUE(i(0, 5).overlaps({3, 16})); + + // large overlap, negative values: + EXPECT_TRUE(i(-5, 5).overlaps({-3, 16})); + EXPECT_TRUE(i(-5, 5).overlaps({-3, 16})); + EXPECT_TRUE(i(-5, 5).overlaps({-3, 16})); + EXPECT_TRUE(i(-5, 5).overlaps({-3, 16})); + EXPECT_TRUE(i(-5, 5).overlaps({-3, 16})); + + // large overlap, completely negative: + EXPECT_TRUE(i(-20, -10).overlaps({-13, -5})); + EXPECT_TRUE(i(-20, -10).overlaps({-13, -5})); + EXPECT_TRUE(i(-20, -10).overlaps({-13, -5})); + EXPECT_TRUE(i(-20, -10).overlaps({-13, -5})); + EXPECT_TRUE(i(-20, -10).overlaps({-13, -5})); + + // edge overlap + EXPECT_TRUE(i(0, 5).overlaps({5, 16})); + EXPECT_FALSE(i(0, 5).overlaps({5, 16})); + EXPECT_TRUE(i(0, 5).overlaps({5, 16})); + EXPECT_FALSE(i(0, 5).overlaps({5, 16})); + + // edge overlap, negative values: + EXPECT_TRUE(i(-5, 5).overlaps({5, 16})); + EXPECT_FALSE(i(-5, 5).overlaps({5, 16})); + EXPECT_TRUE(i(-5, 5).overlaps({5, 16})); + EXPECT_FALSE(i(-5, 5).overlaps({5, 16})); + + // edge overlap, completely negative: + EXPECT_TRUE(i(-20, -10).overlaps({-10, -5})); + EXPECT_FALSE(i(-20, -10).overlaps({-10, -5})); + EXPECT_TRUE(i(-20, -10).overlaps({-10, -5})); + EXPECT_FALSE(i(-20, -10).overlaps({-10, -5})); + + // adjacent does overlap case: + EXPECT_TRUE(i(0, 5).overlaps({5, 16})); + EXPECT_TRUE(i(0, 5).overlaps({6, 16})); + EXPECT_FALSE(i(0, 5).overlaps({7, 16})); + + // adjacent does overlap case, negative values: + EXPECT_TRUE(i(-5, 5).overlaps({5, 16})); + EXPECT_TRUE(i(-5, 5).overlaps({6, 16})); + EXPECT_FALSE(i(-5, 5).overlaps({7, 16})); + + // adjacent does overlap case, completely negative: + EXPECT_TRUE(i(-20, -10).overlaps({-10, -5})); + EXPECT_TRUE(i(-20, -10).overlaps({-9, -5})); + EXPECT_FALSE(i(-20, -10).overlaps({-8, -5})); + + // no overlap + EXPECT_FALSE(i(0, 5).overlaps({6, 16})); + EXPECT_FALSE(i(0, 5).overlaps({6, 16})); + EXPECT_FALSE(i(0, 5).overlaps({6, 16})); + EXPECT_FALSE(i(0, 5).overlaps({6, 16})); + + // no overlap negative values: + EXPECT_FALSE(i(-5, 5).overlaps({6, 16})); + EXPECT_FALSE(i(-5, 5).overlaps({6, 16})); + EXPECT_FALSE(i(-5, 5).overlaps({6, 16})); + EXPECT_FALSE(i(-5, 5).overlaps({6, 16})); + + // no overlap completely negative: + EXPECT_FALSE(i(-20, -10).overlaps({-8, -5})); + EXPECT_FALSE(i(-20, -10).overlaps({-8, -5})); + EXPECT_FALSE(i(-20, -10).overlaps({-8, -5})); + EXPECT_FALSE(i(-20, -10).overlaps({-8, -5})); +} + +TEST_F(OverlapTests, LeftOverlapTests) +{ + using lib_interval_tree::open; + + // large overlap: + EXPECT_TRUE(i(0, 5).overlaps({-3, 16})); + EXPECT_TRUE(i(0, 5).overlaps({-3, 16})); + EXPECT_TRUE(i(0, 5).overlaps({-3, 16})); + EXPECT_TRUE(i(0, 5).overlaps({-3, 16})); + EXPECT_TRUE(i(0, 5).overlaps({-3, 16})); + + // large overlap, negative values: + EXPECT_TRUE(i(-5, 5).overlaps({-13, 16})); + EXPECT_TRUE(i(-5, 5).overlaps({-13, 16})); + EXPECT_TRUE(i(-5, 5).overlaps({-13, 16})); + EXPECT_TRUE(i(-5, 5).overlaps({-13, 16})); + EXPECT_TRUE(i(-5, 5).overlaps({-13, 16})); + + // large overlap, completely negative: + EXPECT_TRUE(i(-20, -10).overlaps({-23, -5})); + EXPECT_TRUE(i(-20, -10).overlaps({-23, -5})); + EXPECT_TRUE(i(-20, -10).overlaps({-23, -5})); + EXPECT_TRUE(i(-20, -10).overlaps({-23, -5})); + EXPECT_TRUE(i(-20, -10).overlaps({-23, -5})); + + // edge overlap: + EXPECT_TRUE(i(0, 5).overlaps({-3, 0})); + EXPECT_FALSE(i(0, 5).overlaps({-3, 0})); + EXPECT_FALSE(i(0, 5).overlaps({-3, 0})); + EXPECT_TRUE(i(0, 5).overlaps({-3, 0})); + + // edge overlap, negative values: + EXPECT_TRUE(i(-5, 5).overlaps({-13, -5})); + EXPECT_FALSE(i(-5, 5).overlaps({-13, -5})); + EXPECT_FALSE(i(-5, 5).overlaps({-13, -5})); + EXPECT_TRUE(i(-5, 5).overlaps({-13, -5})); + + // edge overlap, completely negative: + EXPECT_TRUE(i(-10, -5).overlaps({-23, -10})); + EXPECT_FALSE(i(-10, -5).overlaps({-23, -10})); + EXPECT_FALSE(i(-10, -5).overlaps({-23, -10})); + EXPECT_TRUE(i(-10, -5).overlaps({-23, -10})); + + // adjacent does overlap case: + EXPECT_TRUE(i(0, 5).overlaps({-3, 0})); + EXPECT_TRUE(i(0, 5).overlaps({-4, -1})); + EXPECT_FALSE(i(0, 5).overlaps({-5, -2})); + + // no overlap + EXPECT_FALSE(i(0, 5).overlaps({-6, -1})); + EXPECT_FALSE(i(0, 5).overlaps({-6, -1})); + EXPECT_FALSE(i(0, 5).overlaps({-6, -1})); + EXPECT_FALSE(i(0, 5).overlaps({-6, -1})); + EXPECT_FALSE(i(0, 5).overlaps({-6, -2})); } -TEST_F(OverlapTests, ShallBarelyOverlapLeft) +TEST_F(OverlapTests, ShallEncompassCompletely) { - auto base = types::interval_type{0, 5}; - EXPECT_EQ(base.overlaps({-3, 0}), true); + using lib_interval_tree::open; + EXPECT_TRUE(i(0, 5).overlaps({-99, 16})); + EXPECT_TRUE(i(0, 5).overlaps({-99, 16})); + EXPECT_TRUE(i(0, 5).overlaps({-99, 16})); + EXPECT_TRUE(i(0, 5).overlaps({-99, 16})); + EXPECT_TRUE(i(0, 5).overlaps({-99, 16})); } -TEST_F(OverlapTests, ShallBarelyOverlapRight) +TEST_F(OverlapTests, ShallBeContainedIn) { - auto base = types::interval_type{0, 5}; - EXPECT_EQ(base.overlaps({5, 10}), true); + using lib_interval_tree::open; + EXPECT_TRUE(i(0, 5).overlaps({3, 4})); + EXPECT_TRUE(i(0, 5).overlaps({3, 4})); + EXPECT_TRUE(i(0, 5).overlaps({3, 4})); + EXPECT_TRUE(i(0, 5).overlaps({3, 4})); + EXPECT_TRUE(i(0, 5).overlaps({3, 4})); } TEST_F(OverlapTests, ShallNotOverlapExclusiveLeft) { - auto base = types::interval_type{0, 5}; - EXPECT_EQ(base.overlaps_exclusive({-7, 0}), false); + auto base = i(0, 5); + EXPECT_FALSE(base.overlaps_exclusive({-7, 0})); } TEST_F(OverlapTests, ShallNotOverlapExclusiveRight) { - auto base = types::interval_type{0, 5}; - EXPECT_EQ(base.overlaps_exclusive({5, 10}), false); + auto base = i(0, 5); + EXPECT_FALSE(base.overlaps_exclusive({5, 10})); } TEST_F(OverlapTests, ShallOverlapExclusiveRight) { - auto base = types::interval_type{0, 5}; - EXPECT_EQ(base.overlaps_exclusive({4, 10}), true); + auto base = i(0, 5); + EXPECT_TRUE(base.overlaps_exclusive({4, 10})); } TEST_F(OverlapTests, ShallOverlapExclusiveLeft) { - auto base = types::interval_type{0, 5}; - EXPECT_EQ(base.overlaps_exclusive({-4, 2}), true); + auto base = i(0, 5); + EXPECT_TRUE(base.overlaps_exclusive({-4, 2})); } TEST_F(OverlapTests, ShallOverlapExclusiveEncompass) { - auto base = types::interval_type{0, 5}; - EXPECT_EQ(base.overlaps_exclusive({-6, 10}), true); + auto base = i(0, 5); + EXPECT_TRUE(base.overlaps_exclusive({-6, 10})); } TEST_F(OverlapTests, ShallOverlapExclusiveContained) { - auto base = types::interval_type{0, 5}; - EXPECT_EQ(base.overlaps_exclusive({1, 4}), true); + auto base = i(0, 5); + EXPECT_TRUE(base.overlaps_exclusive({1, 4})); } TEST_F(OverlapTests, ExpectDisjunctExclusive) { - auto base = types::interval_type{0, 5}; - EXPECT_EQ(base.overlaps_exclusive({99, 101}), false); + auto base = i(0, 5); + EXPECT_FALSE(base.overlaps_exclusive({99, 101})); } TEST_F(ContainmentTests, ShallSingleBeWithin) { - auto base = types::interval_type{-86, 35}; - EXPECT_EQ(base.within(3), true); - EXPECT_EQ(base.within(-3), true); - EXPECT_EQ(base.within(-86), true); - EXPECT_EQ(base.within(35), true); + using lib_interval_tree::open; + auto cl = i(-86, 35); + EXPECT_TRUE(cl.within(3)); + EXPECT_TRUE(cl.within(-3)); + EXPECT_TRUE(cl.within(-86)); + EXPECT_TRUE(cl.within(35)); + EXPECT_FALSE(cl.within(36)); + EXPECT_FALSE(cl.within(-87)); + + auto op = i(-86, 35); + EXPECT_TRUE(op.within(3)); + EXPECT_TRUE(op.within(-3)); + EXPECT_FALSE(op.within(-86)); + EXPECT_FALSE(op.within(35)); + EXPECT_FALSE(op.within(36)); + EXPECT_FALSE(op.within(-87)); + + auto lo = i(-86, 35); + EXPECT_TRUE(lo.within(3)); + EXPECT_TRUE(lo.within(-3)); + EXPECT_FALSE(lo.within(-86)); + EXPECT_TRUE(lo.within(35)); + EXPECT_FALSE(lo.within(36)); + EXPECT_FALSE(lo.within(-87)); + + auto ro = i(-86, 35); + EXPECT_TRUE(ro.within(3)); + EXPECT_TRUE(ro.within(-3)); + EXPECT_TRUE(ro.within(-86)); + EXPECT_FALSE(ro.within(35)); + EXPECT_FALSE(ro.within(36)); + EXPECT_FALSE(ro.within(-87)); + + auto ca = i(-86, 35); + EXPECT_TRUE(ca.within(3)); + EXPECT_TRUE(ca.within(-3)); + EXPECT_TRUE(ca.within(-86)); + EXPECT_TRUE(ca.within(35)); + EXPECT_FALSE(ca.within(36)); + EXPECT_FALSE(ca.within(-87)); } TEST_F(ContainmentTests, ExpectIntervalWithinOther) { - auto base = types::interval_type{-100, 100}; + using lib_interval_tree::open; + auto base = i(-100, 100); EXPECT_EQ(base.within({-23, 10}), true); EXPECT_EQ(base.within({-100, 100}), true); EXPECT_EQ(base.within({12, 30}), true); @@ -215,11 +408,35 @@ TEST_F(ContainmentTests, ExpectIntervalWithinOther) EXPECT_EQ(base.within({-100, -100}), true); EXPECT_EQ(base.within({100, 100}), true); EXPECT_EQ(base.within({0, 0}), true); + + auto base2 = i(-100, 100); + EXPECT_TRUE(base2.within({-23, 10})); + EXPECT_FALSE(base2.within({-100, 100})); + EXPECT_FALSE(base2.within({-100, 50})); + EXPECT_FALSE(base2.within({-50, 100})); + + auto base3 = i(-100, 100); + EXPECT_TRUE(base3.within({-23, 10})); + EXPECT_FALSE(base3.within({-100, 100})); + EXPECT_FALSE(base3.within({-100, 50})); + EXPECT_TRUE(base3.within({-50, 100})); + + auto base4 = i(-100, 100); + EXPECT_TRUE(base4.within({-23, 10})); + EXPECT_FALSE(base4.within({-100, 100})); + EXPECT_TRUE(base4.within({-100, 50})); + EXPECT_FALSE(base4.within({-50, 100})); + + auto base5 = i(-100, 100); + EXPECT_TRUE(base5.within({-23, 10})); + EXPECT_TRUE(base5.within({-100, 100})); + EXPECT_TRUE(base5.within({-100, 50})); + EXPECT_TRUE(base5.within({-50, 100})); } TEST_F(ContainmentTests, ExpectIntervalNotWithinOther) { - auto base = types::interval_type{-100, 100}; + auto base = i(-100, 100); EXPECT_EQ(base.within({-101, -100}), false); EXPECT_EQ(base.within({-100, 101}), false); EXPECT_EQ(base.within({-200, 0}), false); @@ -230,35 +447,385 @@ TEST_F(ContainmentTests, ExpectIntervalNotWithinOther) TEST_F(DistanceTests, DistanceIsZeroOnOverlap) { - auto base = types::interval_type{-35, 96}; - auto other = types::interval_type{-20, 600}; + auto base = i(-35, 96); + auto other = i(-20, 600); EXPECT_EQ(base - other, 0); } TEST_F(DistanceTests, DistanceLeftSide) { - auto base = types::interval_type{5, 10}; - auto other = types::interval_type{0, 1}; + using lib_interval_tree::open; + auto base = i(5, 10); + auto other = i(0, 1); EXPECT_EQ(base - other, 4); + + auto base2 = i(5, 10); + auto other2 = i(0, 1); + EXPECT_EQ(base2 - other2, 4); + + auto base3 = i(5, 10); + auto other3 = i(0, 1); + EXPECT_EQ(base3 - other3, 4); + + auto base4 = i(5, 10); + auto other4 = i(0, 1); + EXPECT_EQ(base4 - other4, 4); + + auto base5 = i(5, 10); + auto other5 = i(0, 1); + + EXPECT_EQ(base5 - other5, 4); } TEST_F(DistanceTests, DistanceRightSide) { - auto base = types::interval_type{5, 10}; - auto other = types::interval_type{15, 18}; + using lib_interval_tree::open; + auto base = i(5, 10); + auto other = i(15, 18); EXPECT_EQ(base - other, 5); + + auto base2 = i(5, 10); + auto other2 = i(15, 18); + EXPECT_EQ(base2 - other2, 5); + + auto base3 = i(5, 10); + auto other3 = i(15, 18); + EXPECT_EQ(base3 - other3, 5); + + auto base4 = i(5, 10); + auto other4 = i(15, 18); + EXPECT_EQ(base4 - other4, 5); + + auto base5 = i(5, 10); + auto other5 = i(15, 18); + EXPECT_EQ(base5 - other5, 5); } TEST_F(DistanceTests, DistanceAdjacent) { - auto base = types::interval_type{5, 10}; - auto other = types::interval_type{10, 18}; + using lib_interval_tree::open; + auto base = i(5, 10); + auto other = i(10, 18); EXPECT_EQ(base - other, 0); + + auto base2 = i(5, 10); + auto other2 = i(10, 18); + EXPECT_EQ(base2 - other2, 0); + + auto base3 = i(5, 10); + auto other3 = i(10, 18); + EXPECT_EQ(base3 - other3, 0); + + auto base4 = i(5, 10); + auto other4 = i(10, 18); + EXPECT_EQ(base4 - other4, 0); + + auto base5 = i(5, 10); + auto other5 = i(10, 18); + EXPECT_EQ(base5 - other5, 0); } TEST_F(DistanceTests, DistanceAdjacent2) { - auto base = types::interval_type{5, 10}; - auto other = types::interval_type{0, 5}; + using lib_interval_tree::open; + auto base = i(5, 10); + auto other = i(0, 5); EXPECT_EQ(base - other, 0); + + auto base2 = i(5, 10); + auto other2 = i(0, 5); + EXPECT_EQ(base2 - other2, 0); + + auto base3 = i(5, 10); + auto other3 = i(0, 5); + EXPECT_EQ(base3 - other3, 0); + + auto base4 = i(5, 10); + auto other4 = i(0, 5); + EXPECT_EQ(base4 - other4, 0); + + auto base5 = i(5, 10); + auto other5 = i(0, 5); + EXPECT_EQ(base5 - other5, 0); } + +TEST_F(OverlapTests, DynamicOverlapContainedCompletely) +{ + auto containment = i(-100, 100, interval_border::closed, interval_border::closed); + + for (interval_border l : {interval_border::closed, interval_border::open, interval_border::closed_adjacent}) + { + for (interval_border r : {interval_border::closed, interval_border::open, interval_border::closed_adjacent}) + { + EXPECT_TRUE(containment.overlaps(i(-100, 100, l, r))); + EXPECT_TRUE(containment.overlaps(i(-10, 10, l, r))); + EXPECT_TRUE(containment.overlaps(i(-200, 200, l, r))); + } + } +} + +TEST_F(OverlapTests, DynamicBorderTests) +{ + constexpr auto c = interval_border::closed; + constexpr auto o = interval_border::open; + constexpr auto ca = interval_border::closed_adjacent; + + // right side: + EXPECT_TRUE(i(0, 5, c, c).overlaps(i(5, 10, c, c))); + EXPECT_TRUE(i(0, 5, c, c).overlaps(i(5, 10, c, o))); + EXPECT_FALSE(i(0, 5, c, c).overlaps(i(5, 10, o, c))); + EXPECT_FALSE(i(0, 5, c, c).overlaps(i(5, 10, o, o))); + EXPECT_TRUE(i(0, 5, c, c).overlaps(i(5, 10, c, ca))); + EXPECT_TRUE(i(0, 5, c, c).overlaps(i(5, 10, ca, c))); + EXPECT_TRUE(i(0, 5, c, c).overlaps(i(5, 10, ca, ca))); + EXPECT_TRUE(i(0, 5, c, c).overlaps(i(6, 10, ca, c))); + EXPECT_FALSE(i(0, 5, c, c).overlaps(i(6, 10, c, c))); + EXPECT_FALSE(i(0, 5, c, c).overlaps(i(6, 10, c, o))); + EXPECT_FALSE(i(0, 5, c, c).overlaps(i(6, 10, o, c))); + EXPECT_FALSE(i(0, 5, c, c).overlaps(i(6, 10, o, o))); + + // left side: + EXPECT_TRUE(i(5, 10, c, c).overlaps(i(0, 5, c, c))); + EXPECT_FALSE(i(5, 10, c, c).overlaps(i(0, 5, c, o))); + EXPECT_TRUE(i(5, 10, c, c).overlaps(i(0, 5, o, c))); + EXPECT_FALSE(i(5, 10, c, c).overlaps(i(0, 5, o, o))); + EXPECT_TRUE(i(5, 10, c, c).overlaps(i(0, 5, c, ca))); + EXPECT_TRUE(i(5, 10, c, c).overlaps(i(0, 5, ca, c))); + EXPECT_TRUE(i(5, 10, c, c).overlaps(i(0, 5, ca, ca))); + EXPECT_TRUE(i(5, 10, c, c).overlaps(i(0, 4, c, ca))); + EXPECT_FALSE(i(5, 10, c, c).overlaps(i(0, 4, c, c))); + EXPECT_FALSE(i(5, 10, c, c).overlaps(i(0, 4, c, o))); + EXPECT_FALSE(i(5, 10, c, c).overlaps(i(0, 4, o, c))); + EXPECT_FALSE(i(5, 10, c, c).overlaps(i(0, 4, o, o))); + + // right side, open: + EXPECT_FALSE(i(0, 5, o, o).overlaps(i(5, 10, c, c))); + EXPECT_FALSE(i(0, 5, o, o).overlaps(i(5, 10, c, o))); + EXPECT_FALSE(i(0, 5, o, o).overlaps(i(5, 10, o, c))); + EXPECT_FALSE(i(0, 5, o, o).overlaps(i(5, 10, o, o))); + EXPECT_FALSE(i(0, 5, o, o).overlaps(i(5, 10, c, ca))); + EXPECT_TRUE(i(0, 5, o, o).overlaps(i(5, 10, ca, c))); + EXPECT_TRUE(i(0, 5, o, o).overlaps(i(5, 10, ca, ca))); + EXPECT_FALSE(i(0, 5, o, o).overlaps(i(6, 10, ca, c))); + EXPECT_FALSE(i(0, 5, o, o).overlaps(i(6, 10, c, c))); + EXPECT_FALSE(i(0, 5, o, o).overlaps(i(6, 10, c, o))); + EXPECT_FALSE(i(0, 5, o, o).overlaps(i(6, 10, o, c))); + EXPECT_FALSE(i(0, 5, o, o).overlaps(i(6, 10, o, o))); + EXPECT_TRUE(i(0, 6, o, o).overlaps(i(5, 10, c, c))); + EXPECT_TRUE(i(0, 6, o, o).overlaps(i(5, 10, c, o))); + EXPECT_FALSE(i(0, 6, o, o).overlaps(i(5, 10, o, c))); + EXPECT_FALSE(i(0, 6, o, o).overlaps(i(5, 10, o, o))); + EXPECT_TRUE(i(0, 6, o, o).overlaps(i(5, 10, c, ca))); + EXPECT_TRUE(i(0, 6, o, o).overlaps(i(5, 10, ca, c))); + EXPECT_TRUE(i(0, 6, o, o).overlaps(i(5, 10, ca, ca))); + + // left side, open: + EXPECT_FALSE(i(5, 10, o, o).overlaps(i(0, 5, c, c))); + EXPECT_FALSE(i(5, 10, o, o).overlaps(i(0, 5, c, o))); + EXPECT_FALSE(i(5, 10, o, o).overlaps(i(0, 5, o, c))); + EXPECT_FALSE(i(5, 10, o, o).overlaps(i(0, 5, o, o))); + EXPECT_TRUE(i(5, 10, o, o).overlaps(i(0, 5, c, ca))); + EXPECT_FALSE(i(5, 10, o, o).overlaps(i(0, 5, ca, c))); + EXPECT_TRUE(i(5, 10, o, o).overlaps(i(0, 5, ca, ca))); + EXPECT_FALSE(i(5, 10, o, o).overlaps(i(0, 4, c, ca))); + EXPECT_FALSE(i(5, 10, o, o).overlaps(i(0, 4, c, c))); + EXPECT_FALSE(i(5, 10, o, o).overlaps(i(0, 4, c, o))); + EXPECT_FALSE(i(5, 10, o, o).overlaps(i(0, 4, o, c))); + EXPECT_FALSE(i(5, 10, o, o).overlaps(i(0, 4, o, o))); + EXPECT_TRUE(i(5, 10, o, o).overlaps(i(0, 6, c, c))); + EXPECT_FALSE(i(5, 10, o, o).overlaps(i(0, 6, c, o))); + EXPECT_TRUE(i(5, 10, o, o).overlaps(i(0, 6, o, c))); + EXPECT_FALSE(i(5, 10, o, o).overlaps(i(0, 6, o, o))); + + // one side open or closed follows from other tests +} + +TEST_F(IntervalTests, DynamicWithinTest) +{ + auto base = i(-100, 100, interval_border::closed, interval_border::closed); + EXPECT_TRUE(base.within(0)); + EXPECT_TRUE(base.within(-100)); + EXPECT_TRUE(base.within(100)); + EXPECT_FALSE(base.within(-101)); + EXPECT_FALSE(base.within(101)); + + auto base2 = i(-100, 100, interval_border::open, interval_border::open); + EXPECT_TRUE(base2.within(0)); + EXPECT_FALSE(base2.within(-100)); + EXPECT_FALSE(base2.within(100)); + EXPECT_FALSE(base2.within(-101)); + EXPECT_FALSE(base2.within(101)); + + auto base3 = i(-100, 100, interval_border::open, interval_border::closed); + EXPECT_TRUE(base3.within(0)); + EXPECT_FALSE(base3.within(-100)); + EXPECT_TRUE(base3.within(100)); + EXPECT_FALSE(base3.within(-101)); + EXPECT_FALSE(base3.within(101)); + + auto base4 = i(-100, 100, interval_border::closed, interval_border::open); + EXPECT_TRUE(base4.within(0)); + EXPECT_TRUE(base4.within(-100)); + EXPECT_FALSE(base4.within(100)); + EXPECT_FALSE(base4.within(-101)); + EXPECT_FALSE(base4.within(101)); + + auto base5 = i(-100, 100, interval_border::closed, interval_border::closed_adjacent); + EXPECT_TRUE(base5.within(0)); + EXPECT_TRUE(base5.within(-100)); + EXPECT_TRUE(base5.within(100)); + EXPECT_FALSE(base5.within(-101)); + EXPECT_FALSE(base5.within(101)); + + auto base6 = i(-100, 100, interval_border::closed_adjacent, interval_border::closed); + EXPECT_TRUE(base6.within(0)); + EXPECT_TRUE(base6.within(-100)); + EXPECT_TRUE(base6.within(100)); + EXPECT_FALSE(base6.within(-101)); + EXPECT_FALSE(base6.within(101)); + + auto base7 = i(-100, 100, interval_border::closed_adjacent, interval_border::closed_adjacent); + EXPECT_TRUE(base7.within(0)); + EXPECT_TRUE(base7.within(-100)); + EXPECT_TRUE(base7.within(100)); + EXPECT_FALSE(base7.within(-101)); + EXPECT_FALSE(base7.within(101)); +} + +TEST_F(IntervalTests, DynamicJoinTestBorderPromotion) +{ + EXPECT_EQ( + i(-100, 100, interval_border::closed, interval_border::closed) + .join(i(-100, 100, interval_border::closed, interval_border::closed)), + i(-100, 100, interval_border::closed, interval_border::closed) + ); + + EXPECT_EQ( + i(-100, 100, interval_border::closed, interval_border::closed) + .join(i(-100, 100, interval_border::open, interval_border::open)), + i(-100, 100, interval_border::closed, interval_border::closed) + ); + + EXPECT_EQ( + i(-100, 100, interval_border::closed, interval_border::closed) + .join(i(-100, 100, interval_border::open, interval_border::closed)), + i(-100, 100, interval_border::closed, interval_border::closed) + ); + + EXPECT_EQ( + i(-100, 100, interval_border::closed, interval_border::closed) + .join(i(-100, 100, interval_border::closed, interval_border::open)), + i(-100, 100, interval_border::closed, interval_border::closed) + ); + + EXPECT_EQ( + i(-100, 100, interval_border::closed, interval_border::closed) + .join(i(-100, 100, interval_border::closed, interval_border::closed_adjacent)), + i(-100, 100, interval_border::closed, interval_border::closed_adjacent) + ); + + EXPECT_EQ( + i(-100, 100, interval_border::closed, interval_border::closed) + .join(i(-100, 100, interval_border::closed_adjacent, interval_border::closed)), + i(-100, 100, interval_border::closed_adjacent, interval_border::closed) + ); + + EXPECT_EQ( + i(-100, 100, interval_border::closed, interval_border::closed) + .join(i(-100, 100, interval_border::closed_adjacent, interval_border::closed_adjacent)), + i(-100, 100, interval_border::closed_adjacent, interval_border::closed_adjacent) + ); + + EXPECT_EQ( + i(-100, 100, interval_border::closed, interval_border::closed) + .join(i(-100, 100, interval_border::closed_adjacent, interval_border::open)), + i(-100, 100, interval_border::closed_adjacent, interval_border::closed) + ); + + EXPECT_EQ( + i(-100, 100, interval_border::closed, interval_border::closed) + .join(i(-100, 100, interval_border::open, interval_border::closed_adjacent)), + i(-100, 100, interval_border::closed, interval_border::closed_adjacent) + ); +} + +TEST_F(IntervalTests, DynamicJoinTest) +{ + EXPECT_EQ( + i(-50, 100, interval_border::closed, interval_border::open) + .join(i(-100, 50, interval_border::open, interval_border::open)), + i(-100, 100, interval_border::open, interval_border::open) + ); + + EXPECT_EQ( + i(-50, 100, interval_border::open, interval_border::open) + .join(i(-100, 50, interval_border::closed, interval_border::open)), + i(-100, 100, interval_border::closed, interval_border::open) + ); + + EXPECT_EQ( + i(-50, 100, interval_border::open, interval_border::open) + .join(i(-100, 50, interval_border::open, interval_border::open)), + i(-100, 100, interval_border::open, interval_border::open) + ); + + EXPECT_EQ( + i(-50, 100, interval_border::open, interval_border::open) + .join(i(-100, 50, interval_border::closed, interval_border::closed)), + i(-100, 100, interval_border::closed, interval_border::open) + ); + + EXPECT_EQ( + i(-50, 100, interval_border::open, interval_border::open) + .join(i(-100, 50, interval_border::closed, interval_border::closed_adjacent)), + i(-100, 100, interval_border::closed, interval_border::open) + ); + + EXPECT_EQ( + i(-50, 100, interval_border::open, interval_border::open) + .join(i(-100, 50, interval_border::closed_adjacent, interval_border::closed)), + i(-100, 100, interval_border::closed_adjacent, interval_border::open) + ); + + EXPECT_EQ( + i(-50, 100, interval_border::closed_adjacent, interval_border::open) + .join(i(-100, 50, interval_border::open, interval_border::closed_adjacent)), + i(-100, 100, interval_border::open, interval_border::open) + ); + + EXPECT_EQ( + i(0, 5, interval_border::open, interval_border::open) + .join(i(10, 15, interval_border::closed_adjacent, interval_border::closed)), + i(0, 15, interval_border::open, interval_border::closed) + ); + + EXPECT_EQ( + i(0, 5, interval_border::open, interval_border::closed) + .join(i(0, 1, interval_border::closed, interval_border::open)), + i(0, 5, interval_border::closed, interval_border::closed) + ); + + // left open adjusted test + EXPECT_EQ( + i(0, 5, interval_border::open, interval_border::closed) + .join(i(1, 2, interval_border::closed, interval_border::open)), + i(1, 5, interval_border::closed, interval_border::closed) + ); + EXPECT_EQ( + i(0, 5, interval_border::open, interval_border::closed) + .join(i(1, 2, interval_border::closed_adjacent, interval_border::open)), + i(1, 5, interval_border::closed_adjacent, interval_border::closed) + ); + + // right open adjust test + EXPECT_EQ( + i(0, 5, interval_border::closed, interval_border::open) + .join(i(3, 4, interval_border::closed, interval_border::closed)), + i(0, 4, interval_border::closed, interval_border::closed) + ); + EXPECT_EQ( + i(0, 5, interval_border::closed, interval_border::open) + .join(i(3, 4, interval_border::closed, interval_border::closed_adjacent)), + i(0, 4, interval_border::closed, interval_border::closed_adjacent) + ); +} \ No newline at end of file From 9188575e95ff22db48840ff64b1abe498a3c2027 Mon Sep 17 00:00:00 2001 From: Tim Ebbeke Date: Fri, 2 Aug 2024 19:30:38 +0200 Subject: [PATCH 2/4] Updated README. --- README.md | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 87a025e..6d4b00d 100644 --- a/README.md +++ b/README.md @@ -315,7 +315,18 @@ 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 all the same functions.___ +___You can implement your own interval if you provide the same functions, except (within, operator-, size, operator!=).___ + +There are 6 types of intervals: +- open: (a, b) +- left_open: (a, b] +- right_open: [a, b) +- closed: [a, b] +- closed_adjacent: [a, b] (counts adjacent intervals as overlapping) +- dynamic: Can be any of the above, depending on the input. + +Which can be picked with the second template parameter of interval: +`lib_interval_tree::interval` - [Members of Interval](#members-of-interval) - [using value_type](#using-value_type) @@ -324,7 +335,7 @@ ___You can implement your own interval if you provide all the same functions.___ - [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) - - [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) @@ -346,8 +357,9 @@ Comparison operator. Lower bound. ### value_type high() const Upper bound. -### 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 (closed)? +Is deprecated because the overlapping only works with closed intervals. ### bool overlaps_exclusive(value_type l, value_type h) const Overlap these bounds with this interval excluding borders? ### bool overlaps(interval const& other) const @@ -355,13 +367,13 @@ Like overlaps with lower and upper bound. ### bool overlaps_exclusive(interval const& other) const Like overlaps with lower and upper bound. ### bool within(value_type value) const -Is the value within the interval (closed)? +Is the value within the interval? ### bool within(interval const& other) const Is the interval within the interval? ### value_type operator-(interval const& other) const Calculates the distance between the two intervals. Overlapping intervals have 0 distance. ### value_type size() const -Returns high - low. +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. From 7783d85712526b06a27b56e367fd9a0bc41e4e6c Mon Sep 17 00:00:00 2001 From: Tim Ebbeke Date: Fri, 2 Aug 2024 19:39:53 +0200 Subject: [PATCH 3/4] Fixed interval kinds for floating point. --- README.md | 2 +- include/interval-tree/interval_types.hpp | 49 +++++++++++++++++++++--- 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 6d4b00d..4bb9efb 100644 --- a/README.md +++ b/README.md @@ -323,7 +323,7 @@ There are 6 types of intervals: - right_open: [a, b) - closed: [a, b] - closed_adjacent: [a, b] (counts adjacent intervals as overlapping) -- dynamic: Can be any of the above, depending on the input. +- dynamic: Can be any of the above, depending on the input. Not supported for floating point. Which can be picked with the second template parameter of interval: `lib_interval_tree::interval` diff --git a/include/interval-tree/interval_types.hpp b/include/interval-tree/interval_types.hpp index 2178267..055060d 100644 --- a/include/interval-tree/interval_types.hpp +++ b/include/interval-tree/interval_types.hpp @@ -2,12 +2,10 @@ #include "feature_test.hpp" -#ifdef LIB_INTERVAL_TREE_CONCEPTS -# include -#endif - #include #include +#include +#include namespace lib_interval_tree { @@ -69,10 +67,23 @@ namespace lib_interval_tree } template - static inline numerical_type size(numerical_type low, numerical_type high) + static inline typename std::enable_if::value, numerical_type>::type + size(numerical_type low, numerical_type high) { return high - low + 1; } + + template +#ifdef LIB_INTERVAL_TREE_CONCEPTS + requires std::is_floating_point_v + static inline numerical_type +#else + static inline typename std::enable_if::value, numerical_type>::type +#endif + size(numerical_type low, numerical_type high) + { + return high - low; + } }; // () struct open @@ -90,27 +101,49 @@ namespace lib_interval_tree } template - static inline numerical_type size(numerical_type low, numerical_type high) + static inline typename std::enable_if::value, numerical_type>::type + size(numerical_type low, numerical_type high) { return high - low - 1; } + + template +#ifdef LIB_INTERVAL_TREE_CONCEPTS + requires std::is_floating_point_v + static inline numerical_type +#else + static inline typename std::enable_if::value, numerical_type>::type +#endif + size(numerical_type low, numerical_type high) + { + return high - low; + } }; /// [] and adjacent counts as overlapping struct closed_adjacent { template +#ifdef LIB_INTERVAL_TREE_CONCEPTS + requires std::is_integral_v +#endif static inline bool within(numerical_type low, numerical_type high, numerical_type p) { return (low <= p) && (p <= high); } template +#ifdef LIB_INTERVAL_TREE_CONCEPTS + requires std::is_integral_v +#endif static inline bool overlaps(numerical_type l1, numerical_type h1, numerical_type l2, numerical_type h2) { return (l1 <= (h2 + 1)) && ((l2 - 1) <= h1); } template +#ifdef LIB_INTERVAL_TREE_CONCEPTS + requires std::is_integral_v +#endif static inline numerical_type size(numerical_type low, numerical_type high) { return high - low + 1; @@ -123,6 +156,10 @@ namespace lib_interval_tree open, closed_adjacent }; + + /** + * @brief Do not use for floating point types + */ class dynamic { public: From 9f9bad2bb6255051f1d582183826348f949370f3 Mon Sep 17 00:00:00 2001 From: Tim Ebbeke Date: Fri, 2 Aug 2024 19:48:49 +0200 Subject: [PATCH 4/4] Added readme example to CI build. --- .github/workflows/build_and_test.yml | 2 +- CMakeLists.txt | 3 +++ README.md | 15 +++++++++-- example/CMakeLists.txt | 1 + example/from_readme/CMakeLists.txt | 7 +++++ example/from_readme/main.cpp | 39 ++++++++++++++++++++++++++++ 6 files changed, 64 insertions(+), 3 deletions(-) create mode 100644 example/CMakeLists.txt create mode 100644 example/from_readme/CMakeLists.txt create mode 100644 example/from_readme/main.cpp diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 4b631bc..79fb569 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -48,7 +48,7 @@ jobs: uses: actions/checkout@v2 - name: CMake Configure - run: cmake -B ${{github.workspace}}/build -G"Ninja" -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_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 diff --git a/CMakeLists.txt b/CMakeLists.txt index 100cae8..17b5d4f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,4 +14,7 @@ if(INT_TREE_DRAW_EXAMPLES) endif() if (INT_TREE_ENABLE_TESTS) add_subdirectory(tests) +endif() +if (INT_TREE_BUILD_EXAMPLES) + add_subdirectory(example) endif() \ No newline at end of file diff --git a/README.md b/README.md index 4bb9efb..a29a9ac 100644 --- a/README.md +++ b/README.md @@ -22,8 +22,13 @@ int main() { using namespace lib_interval_tree; - // interval_tree >; - interval_tree_t tree; + // interval_tree>; // closed by default + // interval_tree>; + // interval_tree>; + // interval_tree>; + // interval_tree>; + // interval_tree>; // counts adjacent intervals as overlapping + interval_tree_t tree; tree.insert(make_safe_interval(21, 16)); // make_safe_interval swaps low and high if not in right order. tree.insert({8, 9}); @@ -42,6 +47,12 @@ int main() { std::cout << "[" << i.low() << ", " << i.high() << "]\n"; } + + using lib_interval_tree::open; + // dynamic has some logic overhead. + interval_tree> dynamicIntervals; + dynamicIntervals.insert({0, 1, interval_border::closed, interval_border::open}); + dynamicIntervals.insert({7, 5, interval_border::open, interval_border::closed_adjacent}); } ``` diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt new file mode 100644 index 0000000..8af7d72 --- /dev/null +++ b/example/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(from_readme) \ No newline at end of file diff --git a/example/from_readme/CMakeLists.txt b/example/from_readme/CMakeLists.txt new file mode 100644 index 0000000..2a612b9 --- /dev/null +++ b/example/from_readme/CMakeLists.txt @@ -0,0 +1,7 @@ +add_executable(from_readme main.cpp) +target_link_libraries(from_readme interval-tree) + +set_target_properties(from_readme + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/interval_tree/examples" +) \ No newline at end of file diff --git a/example/from_readme/main.cpp b/example/from_readme/main.cpp new file mode 100644 index 0000000..17d5659 --- /dev/null +++ b/example/from_readme/main.cpp @@ -0,0 +1,39 @@ +// #include // to draw tree. this is not header only anymore. +#include + +int main() +{ + using namespace lib_interval_tree; + + // interval_tree>; // closed by default + // interval_tree>; + // interval_tree>; + // interval_tree>; + // interval_tree>; + // interval_tree>; // counts adjacent intervals as overlapping + interval_tree_t tree; + + tree.insert(make_safe_interval(21, 16)); // make_safe_interval swaps low and high if not in right order. + tree.insert({8, 9}); + tree.insert({25, 30}); + tree.insert({5, 8}); + tree.insert({15, 23}); + tree.insert({17, 19}); + tree.insert({26, 26}); + tree.insert({0, 3}); + tree.insert({6, 10}); + tree.insert({19, 20}); + + tree.deoverlap(); + + for (auto const& i : tree) + { + std::cout << "[" << i.low() << ", " << i.high() << "]\n"; + } + + using lib_interval_tree::open; + // dynamic has some logic overhead. + interval_tree> dynamicIntervals; + dynamicIntervals.insert({0, 1, interval_border::closed, interval_border::open}); + dynamicIntervals.insert({7, 5, interval_border::open, interval_border::closed_adjacent}); +} \ No newline at end of file