diff --git a/CMakeLists.txt b/CMakeLists.txt index 17b5d4f..b8b88ff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,12 @@ project(interval-tree) add_library(interval-tree INTERFACE) +if(INT_TREE_USE_OPTIONAL_POLYFILL) + target_compile_definitions(interval-tree INTERFACE + -DINTERVAL_TREE_USE_OPTIONAL_POLYFILL + ) +endif() + target_include_directories(interval-tree INTERFACE ./include) if(INT_TREE_DRAW_EXAMPLES) diff --git a/README.md b/README.md index aeae98a..9acc9d7 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,11 @@ Create a build folder, navigate there, run cmake and build the tree-tests target You might have to adapt the linker line for gtest, if you built it yourself and didn't install it into your system. If you want to generate the pretty drawings, install cairo, pull the submodule and pass INT_TREE_DRAW_EXAMPLES=on to the cmake command line to generate a drawings/make_drawings executeable. +Some features of this library require the presence of an optional type. +If you are using C++17 and up, it will be std::optional. +Otherwise you can specify INTERVAL_TREE_HAVE_BOOST_OPTIONAL to use boost::optional. +And if neither, a reduced version of optional is provided in the library, not perfectly exchangeable with std::optional, but sufficient for the library to work. + ## Draw Dot Graph This draws a dot graph of the tree: ```c++ @@ -137,37 +142,75 @@ Options are: ## Members of IntervalTree - - [Members of IntervalTree](#members-of-intervaltreeinterval) - - [iterator insert(interval_type const& ival)](#iterator-insertinterval_type-const-ival) - - [iterator insert_overlap(interval_type const& ival, bool, bool)](#iterator-insert_overlapinterval_type-const-ival-bool-bool) +- [interval-tree](#interval-tree) + - [How an interval tree looks like:](#how-an-interval-tree-looks-like) + - [Example](#example) + - [Compile \& Run Testing](#compile--run-testing) + - [Draw Dot Graph](#draw-dot-graph) + - [Free Functions](#free-functions) + - [interval\ make\_safe\_interval(NumericT border1, NumericT border2)](#intervalnumerict-kind-make_safe_intervalnumerict-border1-numerict-border2) + - [draw\_dot\_graph(std::ostream\& os, interval\_tree\_t const\& tree, DrawOptions const\& options)](#draw_dot_graphstdostream-os-interval_tree_t-const-tree-drawoptions-const-options) + - [Members of IntervalTree](#members-of-intervaltree) + - [iterator insert(interval\_type const\& ival)](#iterator-insertinterval_type-const-ival) + - [Parameters](#parameters) + - [iterator insert\_overlap(interval\_type const\& ival, bool, bool)](#iterator-insert_overlapinterval_type-const-ival-bool-bool) + - [Parameters](#parameters-1) - [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) - - [(const)iterator find(interval_type const& ival, CompareFunctionT const& compare)](#constiterator-findinterval_type-const-ival-comparefunctiont-const-compare) - - [(const)iterator find_all(interval_type const& ival, OnFindFunctionT const& on_find)](#constiterator-find_allinterval_type-const-ival-onfindfunctiont-const-on_find) + - [Parameters](#parameters-2) + - [size\_type size() const](#size_type-size-const) + - [(const)iterator find(interval\_type const\& ival)](#constiterator-findinterval_type-const-ival) + - [Parameters](#parameters-3) + - [(const)iterator find(interval\_type const\& ival, CompareFunctionT const\& compare)](#constiterator-findinterval_type-const-ival-comparefunctiont-const-compare) + - [Parameters](#parameters-4) + - [(const)iterator find\_all(interval\_type const\& ival, OnFindFunctionT const\& on\_find)](#constiterator-find_allinterval_type-const-ival-onfindfunctiont-const-on_find) + - [Parameters](#parameters-5) - [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_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) + - [(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) + - [Parameters](#parameters-6) + - [(const)iterator find\_next\_in\_subtree(iterator from, interval\_type const\& ival)](#constiterator-find_next_in_subtreeiterator-from-interval_type-const-ival) + - [Parameters](#parameters-7) + - [(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) + - [Parameters](#parameters-8) + - [(const)iterator overlap\_find(interval\_type const\& ival, bool exclusive)](#constiterator-overlap_findinterval_type-const-ival-bool-exclusive) + - [Parameters](#parameters-9) + - [(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) + - [Parameters](#parameters-10) - [Example](#example-2) - - [(const)iterator overlap_find_next_in_subtree(interval_type const& ival, bool exclusive)](#constiterator-overlap_find_next_in_subtreeinterval_type-const-ival-bool-exclusive) - - [interval_tree& deoverlap()](#interval_tree-deoverlap) + - [(const)iterator overlap\_find\_next\_in\_subtree(interval\_type const\& ival, bool exclusive)](#constiterator-overlap_find_next_in_subtreeinterval_type-const-ival-bool-exclusive) + - [Parameters](#parameters-11) + - [interval\_tree\& deoverlap()](#interval_tree-deoverlap) - [After deoverlap](#after-deoverlap) - - [interval_tree& deoverlap_copy()](#interval_tree-deoverlap_copy) - - [interval_tree punch(interval_type const& ival)](#interval_tree-punchinterval_type-const-ival) - - [After punching (with [0, 50])](#after-punching-with-0-50) - - [interval_tree punch()](#interval_tree-punch) + - [interval\_tree deoverlap\_copy()](#interval_tree-deoverlap_copy) + - [interval\_tree punch(interval\_type const\& ival)](#interval_tree-punchinterval_type-const-ival) + - [Before punching (closed\_adjacent intervals)](#before-punching-closed_adjacent-intervals) + - [After punching (with \[-10, 60\])](#after-punching-with--10-60) + - [interval\_tree punch()](#interval_tree-punch) - [bool empty() const noexcept](#bool-empty-const-noexcept) - [iterator begin()](#iterator-begin) - [iterator end()](#iterator-end) - [iterator cbegin()](#iterator-cbegin) - [iterator cend()](#iterator-cend) - - [reverse_iterator rbegin()](#reverse_iterator-rbegin) - - [reverse_iterator rend()](#reverse_iterator-rend) - - [reverse_iterator crbegin()](#reverse_iterator-crbegin) - - [reverse_iterator crend()](#reverse_iterator-crend) + - [reverse\_iterator rbegin()](#reverse_iterator-rbegin) + - [reverse\_iterator rend()](#reverse_iterator-rend) + - [reverse\_iterator crbegin()](#reverse_iterator-crbegin) + - [reverse\_iterator crend()](#reverse_iterator-crend) + - [Members of Interval](#members-of-interval) + - [using value\_type](#using-value_type) + - [using interval\_kind](#using-interval_kind) + - [friend bool operator==(interval const\& lhs, interval const\& other)](#friend-bool-operatorinterval-const-lhs-interval-const-other) + - [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) + - [\[\[deprecated\]\] bool overlaps(value\_type l, value\_type h) const](#deprecated-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) + - [bool within(value\_type value) const](#bool-withinvalue_type-value-const) + - [bool within(interval const\& other) const](#bool-withininterval-const-other-const) + - [value\_type operator-(interval const\& other) const](#value_type-operator-interval-const-other-const) + - [value\_type size() const](#value_type-size-const) + - [interval join(interval const\& other) const](#interval-joininterval-const-other-const) + - [slice\_type slice(interval const\& other) const](#slice_type-sliceinterval-const-other-const) ### iterator insert(interval_type const& ival) Adds an interval into the tree. @@ -326,13 +369,20 @@ Same as deoverlap, but not inplace --- ### interval_tree punch(interval_type const& ival) -Removes all intervals from `ival` and produces a tree that contains the remaining intervals. -**The tree must be deoverlapped, or the result is undefined.** -`ival` is expected to encompass the entire interval range. +Cuts the intervals of the tree out of the given interval. Like a cookie cutter cuts out of dough. +This will return a new interval_tree containing the gaps between the intervals in the tree and the given interval. +Closed adjacent intervals are treated as exclusive on the borders. [0,5]a[6,10]a will not produce another interval between 5 and 6 as they are considered within the intervals and nothing fits inbetween. +Regular closed intervals will not behave like this, so [0,5][6,10] will produce a new interval [5,6]. +Open intervals with integral numbers will also not produce the gap (5, 6), because (5, 6) is empty for integers, not for floats. + +**IMPORTANT! The tree must be deoverlapped, or the result is undefined.** +`ival` can be any subrange of the tree, including encompassing the whole tree. **Returns**: A new interval_tree containing the gaps. -### After punching (with [0, 50]) -![AfterPunch](https://cloud.githubusercontent.com/assets/6238896/24613645/2dbf72e8-1889-11e7-813f-6d16fe0ad327.png) +### Before punching (closed_adjacent intervals) +![BeforePunch](https://private-user-images.githubusercontent.com/6238896/471147224-5c631e00-dea4-4b75-a3bf-6fdd8ec1440b.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NTM1NjI1MzQsIm5iZiI6MTc1MzU2MjIzNCwicGF0aCI6Ii82MjM4ODk2LzQ3MTE0NzIyNC01YzYzMWUwMC1kZWE0LTRiNzUtYTNiZi02ZmRkOGVjMTQ0MGIucG5nP1gtQW16LUFsZ29yaXRobT1BV1M0LUhNQUMtU0hBMjU2JlgtQW16LUNyZWRlbnRpYWw9QUtJQVZDT0RZTFNBNTNQUUs0WkElMkYyMDI1MDcyNiUyRnVzLWVhc3QtMSUyRnMzJTJGYXdzNF9yZXF1ZXN0JlgtQW16LURhdGU9MjAyNTA3MjZUMjAzNzE0WiZYLUFtei1FeHBpcmVzPTMwMCZYLUFtei1TaWduYXR1cmU9ZDQ0NWIwMTcwMjZhNDA1YmUwNGI1YTIzNTBhZTQ5OTNhMWFiOTU5ZmU0N2E3NDI0NTQ0MzYwODA4N2E2MGFiZiZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QifQ.P5zLeXg0-9bd20Thj6pfq_WxriMn4GC_lDSLzzGKMbw) +### After punching (with [-10, 60]) +![AfterPunch](https://private-user-images.githubusercontent.com/6238896/471147227-5c226d1d-d544-4a43-89a4-b3545145107d.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NTM1NjI1MzQsIm5iZiI6MTc1MzU2MjIzNCwicGF0aCI6Ii82MjM4ODk2LzQ3MTE0NzIyNy01YzIyNmQxZC1kNTQ0LTRhNDMtODlhNC1iMzU0NTE0NTEwN2QucG5nP1gtQW16LUFsZ29yaXRobT1BV1M0LUhNQUMtU0hBMjU2JlgtQW16LUNyZWRlbnRpYWw9QUtJQVZDT0RZTFNBNTNQUUs0WkElMkYyMDI1MDcyNiUyRnVzLWVhc3QtMSUyRnMzJTJGYXdzNF9yZXF1ZXN0JlgtQW16LURhdGU9MjAyNTA3MjZUMjAzNzE0WiZYLUFtei1FeHBpcmVzPTMwMCZYLUFtei1TaWduYXR1cmU9NmE2ZDUzMjU2ZTNjZWQ0Y2QzYjQ3ZGUyYjgyNWM2NDViYTAxMTdlY2RjYmQyMzg4OWFmZDlhMWU5YjY4NjlmZCZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QifQ.Infe9i281LDOEC5GeBFuLHVE6Xjqw7KvcUo-gv3hjpk) --- ### interval_tree punch() @@ -395,7 +445,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 (operator-, size, operator!=).___ +___You can implement your own interval if you provide the same functions, except (slice, operator-, size, operator!=).___ There are 6 types of intervals: - open: (a, b) @@ -457,3 +507,12 @@ Overlapping intervals have 0 distance. 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. +### slice_type slice(interval const& other) const +Removes other from this interval returning what is remaining. +The range of other going beyond the range of this is ignored. +Returns a struct with 2 members: left_slice and right_slice. +[ this interval ] +[left][other][right] + +When the intervals are closed, adjacent results are differenty by 1. +[0, 9].slice([5, 19]) => left: [0, 4], right: nullopt \ No newline at end of file diff --git a/cmake/options.cmake b/cmake/options.cmake index 5eef87f..4cb7f7e 100644 --- a/cmake/options.cmake +++ b/cmake/options.cmake @@ -1,2 +1,4 @@ option(INT_TREE_DRAW_EXAMPLES "Draws some examples in a subdirectory. run make_drawable.sh before this" OFF) -option(INT_TREE_ENABLE_TESTS "Enable tests?" OFF) \ No newline at end of file +option(INT_TREE_ENABLE_TESTS "Enable tests?" OFF) +# You generally do not want to turn this on, unless you are testing the library. +option(INT_TREE_USE_OPTIONAL_POLYFILL "Use optional polyfill?" OFF) \ No newline at end of file diff --git a/drawings/example_drawings.hpp b/drawings/example_drawings.hpp index dd80602..e0b8fcf 100644 --- a/drawings/example_drawings.hpp +++ b/drawings/example_drawings.hpp @@ -2,7 +2,9 @@ #include +#include #include +#include static void drawDocExample() { @@ -31,7 +33,7 @@ static void drawFromTests1() interval_tree_t tree; - std::vector ::interval_type> intervalCollection; + std::vector::interval_type> intervalCollection; intervalCollection.push_back({-51, 11}); intervalCollection.push_back({26, 68}); @@ -67,8 +69,147 @@ static void drawFromTests1() drawTree("drawings/from_tests_1_deoverlapped.png", tree, false, false); } +static void drawLargeOverlapFree() +{ + using namespace lib_interval_tree; + + interval_tree_t tree; + + for (int i = 0; i < 30; ++i) + { + tree.insert({i * 10, i * 10 + 5}); + } + + drawTree("drawings/large_overlap_free.png", tree, false, false); + + std::vector> intervals; + for (int i = 0; i < 30; ++i) + { + intervals.emplace_back(i * 10, i * 10 + 5); + } + + // insert shuffled into new tree + interval_tree_t tree2; + std::mt19937 rng(std::random_device{}()); + std::shuffle(intervals.begin(), intervals.end(), rng); + for (const auto& interval : intervals) + { + tree2.insert({interval.first, interval.second}); + } + drawTree("drawings/large_overlap_free_shuffled.png", tree2, false, false); +} + +static void drawOpenPunchExample() +{ + constexpr int iterations = 5; + using namespace lib_interval_tree; + + interval_tree> tree; + + // insert shuffled into new tree + std::vector> intervals; + for (int i = 0; i < iterations; ++i) + { + intervals.emplace_back(i * 10, i * 10 + 5); + } + + std::mt19937 rng(std::random_device{}()); + std::shuffle(intervals.begin(), intervals.end(), rng); + for (const auto& interval : intervals) + { + tree.insert({interval.first, interval.second}); + } + + drawTree("drawings/open_punch_source.png", tree, false, false); + const auto punched = tree.punch({-10, iterations * 10 + 10}); + drawTree("drawings/open_punched.png", punched, false, false); +} + +static void drawClosedPunchExample() +{ + constexpr int iterations = 5; + using namespace lib_interval_tree; + + interval_tree> tree; + + // insert shuffled into new tree + std::vector> intervals; + for (int i = 0; i < iterations; ++i) + { + intervals.emplace_back(i * 10, i * 10 + 5); + } + + std::mt19937 rng(std::random_device{}()); + std::shuffle(intervals.begin(), intervals.end(), rng); + for (const auto& interval : intervals) + { + tree.insert({interval.first, interval.second}); + } + + drawTree("drawings/closed_punch_source.png", tree, false, false); + const auto punched = tree.punch({-10, iterations * 10 + 10}); + drawTree("drawings/closed_punched.png", punched, false, false); +} + +static void drawAdjacentClosedPunchExample() +{ + constexpr int iterations = 5; + using namespace lib_interval_tree; + + interval_tree> tree; + + // insert shuffled into new tree + std::vector> intervals; + for (int i = 0; i < iterations; ++i) + { + intervals.emplace_back(i * 10, i * 10 + 5); + } + + std::mt19937 rng(std::random_device{}()); + std::shuffle(intervals.begin(), intervals.end(), rng); + for (const auto& interval : intervals) + { + tree.insert({interval.first, interval.second}); + } + + drawTree("drawings/closed_adjacent_punch_source.png", tree, false, false); + const auto punched = tree.punch({-10, iterations * 10 + 10}); + drawTree("drawings/closed_adjacent_punched.png", punched, false, false); +} + +static void drawFloatPunchExample() +{ + constexpr int iterations = 5; + using namespace lib_interval_tree; + + interval_tree> tree; + + // insert shuffled into new tree + std::vector> intervals; + for (int i = 0; i < iterations; ++i) + { + intervals.emplace_back(i * 10.0f, i * 10.0f + 5.0f); + } + + std::mt19937 rng(std::random_device{}()); + std::shuffle(intervals.begin(), intervals.end(), rng); + for (const auto& interval : intervals) + { + tree.insert({interval.first, interval.second}); + } + + drawTree("drawings/float_punch_source.png", tree, false, false); + const auto punched = tree.punch({-10.0f, iterations * 10.0f + 10.0f}); + drawTree("drawings/float_punched.png", punched, false, false); +} + static void drawAll() { drawDocExample(); drawFromTests1(); + drawLargeOverlapFree(); + drawOpenPunchExample(); + drawClosedPunchExample(); + drawAdjacentClosedPunchExample(); + drawFloatPunchExample(); } diff --git a/include/interval-tree/draw.hpp b/include/interval-tree/draw.hpp index 6665975..8b8c792 100644 --- a/include/interval-tree/draw.hpp +++ b/include/interval-tree/draw.hpp @@ -16,72 +16,73 @@ namespace lib_interval_tree { namespace { - template - struct NumericalPointerEquivalent - {}; - - template <> - struct NumericalPointerEquivalent - { - using type = uint32_t; - }; - - template <> - struct NumericalPointerEquivalent - { - using type = uint64_t; - }; - - template - std::string iterCaption(typename lib_interval_tree::interval_tree ::const_iterator iter) + template + struct NumericalPointerEquivalent + {}; + + template <> + struct NumericalPointerEquivalent + { + using type = uint32_t; + }; + + template <> + struct NumericalPointerEquivalent + { + using type = uint64_t; + }; + + template + std::string iterCaption(typename lib_interval_tree::interval_tree::const_iterator iter) { auto ival = *iter.node()->interval(); std::stringstream sstr; - sstr << '[' << ival.low() << ',' << ival.high() << ']'; + char leftBorder = [&ival]() { + if (ival.within(ival.low())) + return '['; + else + return '('; + }(); + char rightBorder = [&ival]() { + if (ival.within(ival.high())) + return ']'; + else + return ')'; + }(); + sstr << leftBorder << ival.low() << ',' << ival.high() << rightBorder; return sstr.str(); } std::string pointerString(void const* ptr) { std::stringstream sstr; - sstr << "0x" << std::hex << reinterpret_cast ::type> (ptr); + sstr << "0x" << std::hex << reinterpret_cast::type>(ptr); return sstr.str(); } - constexpr double margin = 5.; - constexpr double gridMargin = 5.; - constexpr double yPadding = 0.; - constexpr double xPadding = 30.; - constexpr double leftPadding = 10.; - constexpr double rightPadding = 10.; - constexpr double topPadding = 10.; - constexpr double bottomPadding = 10.; - constexpr Cairo::Pen blackPen = {3., Cairo::Colors::Black}; - constexpr Cairo::Pen iterCaptionPen = {3., Cairo::Colors::Black}; - constexpr Cairo::Pen ptrPen = {3., Cairo::Colors::Red}; - constexpr Cairo::Pen edgePen = {8., Cairo::Colors::Black}; - constexpr auto whitePen = Cairo::Colors::White; - - auto getiterBounds() + constexpr double margin = 5.; + constexpr double gridMargin = 5.; + constexpr double yPadding = 0.; + constexpr double xPadding = 30.; + constexpr double leftPadding = 10.; + constexpr double rightPadding = 10.; + constexpr double topPadding = 10.; + constexpr double bottomPadding = 10.; + constexpr Cairo::Pen blackPen = {3., Cairo::Colors::Black}; + constexpr Cairo::Pen iterCaptionPen = {3., Cairo::Colors::Black}; + constexpr Cairo::Pen ptrPen = {3., Cairo::Colors::Red}; + constexpr Cairo::Pen edgePen = {8., Cairo::Colors::Black}; + constexpr auto whitePen = Cairo::Colors::White; + + auto getiterBounds() { Cairo::Surface dummySurface(0, 0); Cairo::DrawContext dummyContext(&dummySurface); - auto const captionBoundProvider = Cairo::Text( - &dummyContext, - 0, - 0, - "[00,00]", - {"Arial", 18, CAIRO_FONT_WEIGHT_BOLD} - ); - - auto ptr = Cairo::Text( - &dummyContext, - 0, - 0, - pointerString(nullptr), - {"Arial", 10} - ); + auto const captionBoundProvider = + Cairo::Text(&dummyContext, 0, 0, "[00,00]", {"Arial", 18, CAIRO_FONT_WEIGHT_BOLD}); + + auto ptr = Cairo::Text(&dummyContext, 0, 0, pointerString(nullptr), {"Arial", 10}); auto ptrBounds = ptr.calculateBounds(ptrPen); auto bounds = captionBoundProvider.calculateBounds(iterCaptionPen); @@ -96,43 +97,42 @@ namespace lib_interval_tree return getiterBounds().getWidth() / 2. + margin * 2.; } } -//##################################################################################################################### + // ##################################################################################################################### template struct TreeGriditer { - typename lib_interval_tree::interval_tree ::const_iterator iter; - std::pair parentCoords; + typename lib_interval_tree::interval_tree::const_iterator iter; + std::pair parentCoords; }; template struct TreeGrid { // (row-major) - std::vector < // rows - std::vector < // columns - boost::optional > - > - > grid; + std::vector< // rows + std::vector< // columns + boost::optional>>> + grid; int xMax = 0; int xMin = 0; int yMax = 0; }; -//##################################################################################################################### + // ##################################################################################################################### template - void drawIterator(Cairo::DrawContext* ctx, typename lib_interval_tree::interval_tree ::const_iterator iter, double x, double y, bool drawPointers) + void drawIterator( + Cairo::DrawContext* ctx, + typename lib_interval_tree::interval_tree::const_iterator iter, + double x, + double y, + bool drawPointers + ) { - auto caption = Cairo::Text( - ctx, - 0, - 0, - iterCaption(iter), - {"Arial", 18, CAIRO_FONT_WEIGHT_BOLD} - ); + auto caption = Cairo::Text(ctx, 0, 0, iterCaption(iter), {"Arial", 18, CAIRO_FONT_WEIGHT_BOLD}); auto max = Cairo::Text( ctx, 0, 0, - //pointerString(iter), + // pointerString(iter), std::to_string(iter.max()), {"Arial", 12} ); @@ -151,7 +151,7 @@ namespace lib_interval_tree auto iterCaptionBounds = getiterBounds(); auto bounds = iterCaptionBounds; auto maxBounds = max.calculateBounds(ptrPen); - //auto ptrBounds = ptr.calculateBounds(ptrPen); + // auto ptrBounds = ptr.calculateBounds(ptrPen); iterCaptionBounds.setWidth(bounds.getWidth() - 30.); iterCaptionBounds.setHeight(bounds.getWidth() - maxBounds.getHeight() - 5.); @@ -166,29 +166,27 @@ namespace lib_interval_tree maxBounds = max.calculateBounds(ptrPen); } - Cairo::Arc circle { - ctx, - circleX, - circleY, - circleRadius - }; + Cairo::Arc circle{ctx, circleX, circleY, circleRadius}; switch (iter.node()->color()) { - case (rb_color::red): - circle.draw(blackPen, Cairo::Colors::Red); - break; - case (rb_color::black): - circle.draw(blackPen, Cairo::Colors::Black); - break; - case (rb_color::fail): - circle.draw(blackPen, Cairo::Colors::White); - break; - case (rb_color::double_black): - circle.draw(blackPen, Cairo::Colors::Gray); - break; + case (rb_color::red): + circle.draw(blackPen, Cairo::Colors::Red); + break; + case (rb_color::black): + circle.draw(blackPen, Cairo::Colors::Black); + break; + case (rb_color::fail): + circle.draw(blackPen, Cairo::Colors::White); + break; + case (rb_color::double_black): + circle.draw(blackPen, Cairo::Colors::Gray); + break; } - caption.move(circleX - actualCaptionBounds.getWidth() / 2., circleY - actualCaptionBounds.getHeight() / 2. - maxBounds.getHeight()); + caption.move( + circleX - actualCaptionBounds.getWidth() / 2., + circleY - actualCaptionBounds.getHeight() / 2. - maxBounds.getHeight() + ); if (iter.node()->color() != rb_color::black) caption.draw(iterCaptionPen); @@ -196,7 +194,8 @@ namespace lib_interval_tree caption.draw(Cairo::Colors::White); max.move(circleX - maxBounds.getWidth() / 2., circleY - maxBounds.getHeight() / 2. + 10.); - //ptr.move(circleX - ptrBounds.getWidth() / 2., circleY - ptrBounds.getHeight() / 2. + 10. + maxBounds.getHeight() + margin); + // ptr.move(circleX - ptrBounds.getWidth() / 2., circleY - ptrBounds.getHeight() / 2. + 10. + + // maxBounds.getHeight() + margin); if (iter.node()->color() != rb_color::red) max.draw(ptrPen); @@ -213,9 +212,9 @@ namespace lib_interval_tree } */ } -//--------------------------------------------------------------------------------------------------------------------- + //--------------------------------------------------------------------------------------------------------------------- template - TreeGrid createGrid(lib_interval_tree::interval_tree const& tree) + TreeGrid createGrid(lib_interval_tree::interval_tree const& tree) { auto root = tree.root(); if (root == std::end(tree)) @@ -223,7 +222,7 @@ namespace lib_interval_tree TreeGrid grid; - using tree_const_iterator = typename lib_interval_tree::interval_tree ::const_iterator; + using tree_const_iterator = typename lib_interval_tree::interval_tree::const_iterator; struct GridPoint { @@ -233,9 +232,9 @@ namespace lib_interval_tree int y; }; - std::vector gridPoints; + std::vector gridPoints; - std::function subtreeSize; + std::function subtreeSize; subtreeSize = [&](tree_const_iterator iter) { if (iter == std::end(tree)) return 0; @@ -250,9 +249,8 @@ namespace lib_interval_tree return 0; }; - std::function deduceCoordinates; - deduceCoordinates = [&](tree_const_iterator iter, int pX, int pY) - { + std::function deduceCoordinates; + deduceCoordinates = [&](tree_const_iterator iter, int pX, int pY) { int y = pY; int x = pX; if (!iter.node()->is_root()) @@ -298,7 +296,7 @@ namespace lib_interval_tree for (auto& i : gridPoints) { - std::pair parentCoords = {-1, -1}; + std::pair parentCoords = {-1, -1}; for (auto const& j : gridPoints) { if (j.iter == i.parent) @@ -313,15 +311,19 @@ namespace lib_interval_tree return grid; } -//--------------------------------------------------------------------------------------------------------------------- + //--------------------------------------------------------------------------------------------------------------------- template void drawGrid(Cairo::DrawContext* ctx, TreeGrid const& grid, bool drawPointers, bool drawEmpty) { auto iterRadius = getiterRadius(); auto cellSize = iterRadius * 2. + gridMargin; - auto iterX = [&](auto x_) {return leftPadding + iterRadius + x_ * cellSize + x_ * xPadding;}; - auto iterY = [&](auto y_) {return topPadding + iterRadius + y_ * cellSize + y_ * yPadding;}; + auto iterX = [&](auto x_) { + return leftPadding + iterRadius + x_ * cellSize + x_ * xPadding; + }; + auto iterY = [&](auto y_) { + return topPadding + iterRadius + y_ * cellSize + y_ * yPadding; + }; // Draw Lines int y = 0; @@ -332,7 +334,7 @@ namespace lib_interval_tree { if (cell && cell.get().parentCoords.first != -1) { - auto line = Cairo::Line { + auto line = Cairo::Line{ ctx, iterX(x), iterY(y), @@ -360,21 +362,15 @@ namespace lib_interval_tree } else if (drawEmpty) { - Cairo::Arc circle { - ctx, - iterX(x), - iterY(y), - iterRadius - }; + Cairo::Arc circle{ctx, iterX(x), iterY(y), iterRadius}; circle.draw(blackPen, Cairo::Colors::White); - } ++x; } ++y; } } -//--------------------------------------------------------------------------------------------------------------------- + //--------------------------------------------------------------------------------------------------------------------- template Cairo::Surface createSurface(TreeGrid const& grid) { @@ -384,13 +380,18 @@ namespace lib_interval_tree int height = grid.yMax + 1; return { - static_cast (leftPadding + (width) * cellSize + (width-1) * xPadding + rightPadding), - static_cast (topPadding + (height) * cellSize + (height-1) * yPadding + bottomPadding) + static_cast(leftPadding + (width)*cellSize + (width - 1) * xPadding + rightPadding), + static_cast(topPadding + (height)*cellSize + (height - 1) * yPadding + bottomPadding) }; } -//--------------------------------------------------------------------------------------------------------------------- + //--------------------------------------------------------------------------------------------------------------------- template - void drawTree(std::string const& fileName, lib_interval_tree::interval_tree const& tree, bool drawPointers = false, bool drawEmpty = false) + void drawTree( + std::string const& fileName, + lib_interval_tree::interval_tree const& tree, + bool drawPointers = false, + bool drawEmpty = false + ) { auto grid = createGrid(tree); auto surface = createSurface(grid); @@ -398,5 +399,5 @@ namespace lib_interval_tree drawGrid(&ctx, grid, drawPointers, drawEmpty); surface.saveToFile(fileName); } -//###################################################################################################### + // ###################################################################################################### } diff --git a/include/interval-tree/interval_tree.hpp b/include/interval-tree/interval_tree.hpp index 950f488..8b10117 100644 --- a/include/interval-tree/interval_tree.hpp +++ b/include/interval-tree/interval_tree.hpp @@ -4,6 +4,7 @@ #include "interval_types.hpp" #include "tree_hooks.hpp" #include "feature_test.hpp" +#include "optional.hpp" #include #include @@ -21,8 +22,39 @@ namespace lib_interval_tree double_black }; // ############################################################################################################ + template + struct slice_type + { + optional left_slice{}; + optional right_slice{}; + }; + // ############################################################################################################ using default_interval_value_type = int; // ############################################################################################################ + namespace detail + { + template + using void_t = void; + + template + struct has_slice_impl : std::false_type + {}; + + template + struct has_slice_impl< + interval_t, + void_t().slice(std::declval()))>> : std::true_type + {}; + +#if __cplusplus >= 202002L + template + concept has_slice = has_slice_impl::value; +#else + template + constexpr bool has_slice = has_slice_impl::value; +#endif + } + // ############################################################################################################ template struct interval_base { @@ -201,6 +233,36 @@ namespace lib_interval_tree { return interval_kind::size(low_, high_); } + + /** + * @brief Removes other from this interval returning what is remaining. + * + * The range of other going beyond the range of this is ignored. + * + * @param other + * @return slice_type + */ + slice_type slice(interval const& other) const + { + slice_type slices{}; + if (low_ < other.low_) + { + auto slice = interval{low_, std::min(interval_kind::left_slice_upper_bound(other.low_), high_)}; + // >= comparison avoids overflows in case of unsigned integers + if (slice.high_ >= slice.low_ && slice.size() > 0) + slices.left_slice = std::move(slice); + } + // low_ == other.low_ does not produce a left slice in any case. + if (high_ > other.high_) + { + auto slice = interval{std::max(interval_kind::right_slice_lower_bound(other.high_), low_), high_}; + // >= comparison avoids overflows in case of unsigned integers + if (slice.high_ >= slice.low_ && slice.size() > 0) + slices.right_slice = std::move(slice); + } + // high_ == other.high_ does not produce a right slice in any case. + return slices; + } }; template @@ -323,6 +385,47 @@ namespace lib_interval_tree return right_border_; } + /** + * @brief Removes other from this interval returning what is remaining. + * + * @param other + * @return slice_type + */ + slice_type slice(interval const& other) const + { + if (!overlaps(other)) + return {}; + + slice_type slices{}; + if (low_ < other.low_) + { + auto slice = interval{ + low_, + std::min(other.low_, high_), + left_border_, + other.left_border() == interval_border::open ? interval_border::closed : interval_border::open + }; + // >= comparison avoids overflows in case of unsigned integers + if (slice.high_ >= slice.low_ && slice.size() > 0) + slices.left_slice = std::move(slice); + } + // low_ == other.low_ does not produce a left slice in any case. + if (high_ > other.high_) + { + auto slice = interval{ + std::max(other.high_, low_), + high_, + right_border_ == interval_border::open ? interval_border::closed : interval_border::open, + other.right_border() + }; + // >= comparison avoids overflows in case of unsigned integers + if (slice.high_ >= slice.low_ && slice.size() > 0) + slices.right_slice = std::move(slice); + } + // high_ == other.high_ does not produce a right slice in any case. + return slices; + } + protected: interval_border left_border_; interval_border right_border_; @@ -1024,7 +1127,7 @@ namespace lib_interval_tree * 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 recursive = false) + iterator insert_overlap(interval_type const& ival, bool exclusive = false, bool recursive = true) { auto iter = overlap_find(ival, exclusive); if (iter == end()) @@ -1338,38 +1441,74 @@ namespace lib_interval_tree { if (empty()) return {}; - auto min = std::begin(*this)->interval()->low(); - auto max = root_->max_; - return punch({min, max}); + return punch({begin()->low(), root_->max_}); } /** - * Only works with deoverlapped trees. - * Removes all intervals from the given interval and produces a tree that contains the remaining intervals. - * This is basically the other punch overload with ival = [tree_lowest, tree_highest] + * Only works with deoverlapped trees. + * Removes all intervals from the given interval and produces a tree that contains the remaining intervals. + * This is basically the other punch overload with ival = [tree_lowest, tree_highest] + * + * @param ival The range in which to punch out the gaps as a new tree */ - interval_tree punch(interval_type const& ival) const + template +#ifdef LIB_INTERVAL_TREE_CONCEPTS + requires detail::has_slice + interval_tree +#else + typename std::enable_if, interval_tree>::type +#endif + punch(interval_type ival) const { + interval_tree result; + if (empty()) - return {}; + { + // Nothing to punch, so return the whole interval + result.insert(ival); + return result; + } - interval_tree result; - auto i = std::begin(*this); - if (ival.low() < i->interval()->low()) - result.insert({ival.low(), i->interval()->low()}); + auto* left_of_or_contains = find_leftest_interval_of_value_i(ival.low()); + + const_iterator iter{nullptr, this}; + if (left_of_or_contains == nullptr) + { + iter = cbegin(); + // not adjacent or overlapping? + if (ival.high() < iter->low()) + { + result.insert(ival); + return result; + } + } + else + { + iter = const_iterator{left_of_or_contains, this}; + } - for (auto e = end(); i != e; ++i) + slice_type ex; + bool insert_remaining = false; + for (; iter != cend(); ++iter) { - auto next = i; - ++next; - if (next != e) - result.insert({i->interval()->high(), next->interval()->low()}); + ex = ival.slice(*iter); + if (ex.left_slice) + result.insert(*ex.left_slice); + + if (ex.right_slice) + { + ival = std::move(*ex.right_slice); + insert_remaining = true; + } else + { + insert_remaining = false; break; + } } - if (i != end() && i->interval()->high() < ival.high()) - result.insert({i->interval()->high(), ival.high()}); + if (insert_remaining && ival.size() > 0) + result.insert(ival); return result; } @@ -1467,6 +1606,50 @@ namespace lib_interval_tree } private: + /** + * @brief Find the interval that is left of the given value or contains it. + * Only works in deoverlapped trees. Because a deoverlapped tree is indistinguishable + * from a regular binary search tree. The tree is then also sorted by the upper interval bound. + * Making this search possible in the first place. + * + * @param search_value + * @return node_type* + */ + node_type* find_leftest_interval_of_value_i(value_type search_value) const + { + if (empty()) + return nullptr; + + auto* node = root_; + + // low of a node is always lower than the lows of all nodes right of that node + // high of a node is always lower than the lows of all nodes right of that node + + bool go_left = false; + bool go_right = false; + + do + { + go_right = search_value > node->high(); + if (go_right) + { + go_right &= node->right_ != nullptr; + if (go_right) + node = node->right_; + continue; + } + + go_left = node->left_ != nullptr && search_value < node->low(); + if (go_left) + node = node->left_; + } while (go_left || go_right); + + if (search_value < node->low()) + return nullptr; + + return node; + } + node_type* copy_tree_impl(node_type* root, node_type* parent) { if (root) diff --git a/include/interval-tree/interval_types.hpp b/include/interval-tree/interval_types.hpp index 055060d..10bbefa 100644 --- a/include/interval-tree/interval_types.hpp +++ b/include/interval-tree/interval_types.hpp @@ -29,6 +29,18 @@ namespace lib_interval_tree { return high - low; } + + template + static inline numerical_type left_slice_upper_bound(numerical_type value) + { + return value; + } + + template + static inline numerical_type right_slice_lower_bound(numerical_type value) + { + return value; + } }; // [) struct right_open @@ -50,6 +62,18 @@ namespace lib_interval_tree { return high - low; } + + template + static inline numerical_type left_slice_upper_bound(numerical_type high) + { + return high; + } + + template + static inline numerical_type right_slice_lower_bound(numerical_type value) + { + return value; + } }; // [] struct closed @@ -67,7 +91,12 @@ namespace lib_interval_tree } template - static inline typename std::enable_if::value, numerical_type>::type +#ifdef LIB_INTERVAL_TREE_CONCEPTS + requires std::is_integral_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 + 1; @@ -84,6 +113,18 @@ namespace lib_interval_tree { return high - low; } + + template + static inline numerical_type left_slice_upper_bound(numerical_type high) + { + return high; + } + + template + static inline numerical_type right_slice_lower_bound(numerical_type value) + { + return value; + } }; // () struct open @@ -101,12 +142,36 @@ namespace lib_interval_tree } template - static inline typename std::enable_if::value, numerical_type>::type +#ifdef LIB_INTERVAL_TREE_CONCEPTS + requires(!std::is_floating_point_v && std::is_signed_v) + static inline numerical_type +#else + static inline typename std::enable_if< + !std::is_floating_point::value && std::is_signed::value, + numerical_type>::type +#endif size(numerical_type low, numerical_type high) { return high - low - 1; } + template +#ifdef LIB_INTERVAL_TREE_CONCEPTS + requires(!std::is_floating_point_v && std::is_unsigned_v) + static inline numerical_type +#else + static inline typename std::enable_if< + !std::is_floating_point::value && std::is_unsigned::value, + numerical_type>::type +#endif + size(numerical_type low, numerical_type high) + { + if (high > low) + return high - low - 1; + else + return 0; + } + template #ifdef LIB_INTERVAL_TREE_CONCEPTS requires std::is_floating_point_v @@ -118,6 +183,18 @@ namespace lib_interval_tree { return high - low; } + + template + static inline numerical_type left_slice_upper_bound(numerical_type high) + { + return high; + } + + template + static inline numerical_type right_slice_lower_bound(numerical_type high) + { + return high; + } }; /// [] and adjacent counts as overlapping struct closed_adjacent @@ -125,8 +202,11 @@ namespace lib_interval_tree template #ifdef LIB_INTERVAL_TREE_CONCEPTS requires std::is_integral_v + static inline bool +#else + static inline typename std::enable_if::value, bool>::type #endif - static inline bool within(numerical_type low, numerical_type high, numerical_type p) + within(numerical_type low, numerical_type high, numerical_type p) { return (low <= p) && (p <= high); } @@ -134,20 +214,99 @@ namespace lib_interval_tree template #ifdef LIB_INTERVAL_TREE_CONCEPTS requires std::is_integral_v + static inline bool +#else + static inline typename std::enable_if::value, bool>::type #endif - static inline bool overlaps(numerical_type l1, numerical_type h1, numerical_type l2, numerical_type h2) + overlaps(numerical_type l1, numerical_type h1, numerical_type l2, numerical_type h2) { - return (l1 <= (h2 + 1)) && ((l2 - 1) <= h1); + return (l1 <= (h2 + 1)) && (l2 <= (h1 + 1)); } template #ifdef LIB_INTERVAL_TREE_CONCEPTS requires std::is_integral_v + static inline numerical_type +#else + static inline typename std::enable_if::value, numerical_type>::type #endif - static inline numerical_type size(numerical_type low, numerical_type high) + 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; + } + + template +#ifdef LIB_INTERVAL_TREE_CONCEPTS + requires(std::is_signed_v && !std::is_floating_point_v) + static inline numerical_type +#else + static inline typename std::enable_if< + std::is_signed::value && !std::is_floating_point::value, + numerical_type>::type +#endif + left_slice_upper_bound(numerical_type value) + { + return value - 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 + left_slice_upper_bound(numerical_type value) + { + return value; + } + + template +#ifdef LIB_INTERVAL_TREE_CONCEPTS + requires std::is_unsigned_v + static inline numerical_type +#else + static inline typename std::enable_if::value, numerical_type>::type +#endif + left_slice_upper_bound(numerical_type value) + { + return value > 0 ? value - 1 : 0; + } + + 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 + right_slice_lower_bound(numerical_type value) + { + return value + 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 + right_slice_lower_bound(numerical_type value) + { + return value; + } }; enum class interval_border @@ -166,8 +325,11 @@ namespace lib_interval_tree template #ifdef LIB_INTERVAL_TREE_CONCEPTS requires std::is_integral_v + static inline bool +#else + static inline typename std::enable_if::value, bool>::type #endif - static inline bool within(interval_type const& ival, typename interval_type::value_type p) + within(interval_type const& ival, typename interval_type::value_type p) { switch (ival.left_border()) { @@ -217,8 +379,11 @@ namespace lib_interval_tree template #ifdef LIB_INTERVAL_TREE_CONCEPTS requires std::is_integral_v + static inline bool +#else + static inline typename std::enable_if::value, bool>::type #endif - static inline bool overlaps(interval_type const& ival1, interval_type const& ival2) + overlaps(interval_type const& ival1, interval_type const& ival2) { const auto lowToClosed = [](auto const& ival) { if (ival.left_border() == interval_border::open) @@ -228,7 +393,13 @@ namespace lib_interval_tree const auto highToClosed = [](auto const& ival) { if (ival.right_border() == interval_border::open) + { + INTERVAL_TREE_CONSTEXPR_IF(std::is_unsigned::value) + { + return ival.high() > 0 ? ival.high() - 1 : 0; + } return ival.high() - 1; + } return ival.high(); }; @@ -263,8 +434,13 @@ namespace lib_interval_tree template #ifdef LIB_INTERVAL_TREE_CONCEPTS requires std::is_integral_v + static inline typename interval_type::value_type +#else + static inline typename std::enable_if< + std::is_integral::value, + typename interval_type::value_type>::type #endif - static typename interval_type::value_type distance(interval_type const& ival1, interval_type const& ival2) + distance(interval_type const& ival1, interval_type const& ival2) { using value_type = typename interval_type::value_type; @@ -272,11 +448,33 @@ namespace lib_interval_tree 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 adjusted_high = [&]() { + INTERVAL_TREE_CONSTEXPR_IF(std::is_unsigned::value) + { + return ival1.right_border() == interval_border::open ? (ival1.high() > 0 ? ival1.high() - 1 : 0) + : ival1.high(); + } + else + { + return 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(); + + value_type other_adjusted_high = [&]() { + INTERVAL_TREE_CONSTEXPR_IF(std::is_unsigned::value) + { + return ival2.right_border() == interval_border::open ? (ival2.high() > 0 ? ival2.high() - 1 : 0) + : ival2.high(); + } + else + { + return ival2.right_border() == interval_border::open ? ival2.high() - 1 : ival2.high(); + } + }(); if (adjusted_high < other_adjusted_low) return other_adjusted_low - adjusted_high; @@ -303,8 +501,12 @@ namespace lib_interval_tree template #ifdef LIB_INTERVAL_TREE_CONCEPTS requires std::is_integral_v + static inline interval_type +#else + static inline + typename std::enable_if::value, interval_type>::type #endif - static interval_type join(interval_type const& ival1, interval_type const& ival2) + join(interval_type const& ival1, interval_type const& ival2) { typename interval_type::value_type low; typename interval_type::value_type high; @@ -366,7 +568,13 @@ namespace lib_interval_tree ? &ival1 : &ival2; - const auto openAdjusted = rightOpenInterval->high() - 1; + const auto openAdjusted = [&]() { + INTERVAL_TREE_CONSTEXPR_IF(std::is_unsigned::value) + { + return rightOpenInterval->high() > 0 ? rightOpenInterval->high() - 1 : 0; + } + return rightOpenInterval->high() - 1; + }(); if (openAdjusted == rightClosedInterval->high()) { @@ -391,8 +599,13 @@ namespace lib_interval_tree template #ifdef LIB_INTERVAL_TREE_CONCEPTS requires std::is_integral_v + static inline typename interval_type::value_type +#else + static inline typename std::enable_if< + std::is_integral::value, + typename interval_type::value_type>::type #endif - typename interval_type::value_type size(interval_type const& ival) + size(interval_type const& ival) { auto left = ival.left_border(); if (left == interval_border::closed_adjacent) diff --git a/include/interval-tree/optional.hpp b/include/interval-tree/optional.hpp new file mode 100644 index 0000000..e50bc9c --- /dev/null +++ b/include/interval-tree/optional.hpp @@ -0,0 +1,430 @@ +#pragma once + +#ifndef INTERVAL_TREE_USE_OPTIONAL_POLYFILL +# if __cplusplus >= 201703L +# include +namespace lib_interval_tree +{ + using nullopt_t = std::nullopt_t; + template + using optional = std::optional; + constexpr auto nullopt = std::nullopt; +} +# elif defined(INTERVAL_TREE_HAVE_BOOST_OPTIONAL) +# include +template +namespace lib_interval_tree +{ + template + using optional = boost::optional; + constexpr auto nullopt = boost::none; + using nullopt_t = decltype(boost::none); +} +# else +# define INTERVAL_TREE_USE_OPTIONAL_POLYFILL +# endif +#endif + +/** + * Note that the interval tree optional is not a complete replacement for std::optional or boost::optional. + * It is missing some features, such as comparison operators. It does not follow the standard. + */ +#ifdef INTERVAL_TREE_USE_OPTIONAL_POLYFILL +# include +# include +# include +namespace lib_interval_tree +{ + class bad_optional_access : public std::logic_error + { + public: + bad_optional_access() + : logic_error("bad optional access") + {} + virtual ~bad_optional_access() noexcept = default; + }; + + struct nullopt_t + { + nullopt_t() = delete; + enum class construct + { + token + }; + explicit constexpr nullopt_t(construct) noexcept + {} + }; + constexpr nullopt_t nullopt{nullopt_t::construct::token}; + + struct in_place_t + { + in_place_t() = delete; + enum class construct + { + token + }; + explicit constexpr in_place_t(construct) noexcept + {} + }; + constexpr in_place_t in_place{in_place_t::construct::token}; + +# define INTERVAL_TREE_OPTIONAL_INTESTINES \ +\ + public: \ + using stored_type = typename std::remove_const::type; \ +\ + public: \ + constexpr optional_base(nullopt_t) noexcept \ + : optional_base{} \ + {} \ +\ + template \ + constexpr explicit optional_base(in_place_t, Args&&... args) \ + : value_(std::forward(args)...) \ + , engaged_{true} \ + {} \ +\ + template < \ + typename U, \ + typename... Args, \ + std::enable_if_t&, Args&&...>::value, int>...> \ + constexpr explicit optional_base(in_place_t, std::initializer_list il, Args&&... args) \ + : value_(il, std::forward(args)...) \ + , engaged_{true} \ + {} \ +\ + constexpr optional_base() noexcept \ + : empty_value_{} \ + {} \ +\ + optional_base(optional_base const& other) \ + { \ + if (other.engaged_) \ + construct(other.value_); \ + } \ +\ + optional_base(optional_base&& other) noexcept(std::is_nothrow_move_constructible::value) \ + { \ + if (other.engaged_) \ + construct(std::move(other.value_)); \ + } \ +\ + optional_base& operator=(optional_base const& other) \ + { \ + if (engaged_ && other.engaged_) \ + value_ = other.value_; \ + else \ + { \ + if (other.engaged_) \ + construct(other.value_); \ + else \ + reset(); \ + } \ +\ + return *this; \ + } \ +\ + optional_base& operator=(optional_base&& other) noexcept( \ + std::is_nothrow_move_constructible::value && std::is_nothrow_move_assignable::value \ + ) \ + { \ + if (engaged_ && other.engaged_) \ + value_ = std::move(other.value_); \ + else \ + { \ + if (other.engaged_) \ + construct(std::move(other.value_)); \ + else \ + reset(); \ + } \ + return *this; \ + } \ +\ + void reset() \ + { \ + if (engaged_) \ + { \ + engaged_ = false; \ + value_.~stored_type(); \ + } \ + } \ +\ + protected: \ + template \ + void construct(Args&&... args) \ + { \ + ::new (std::addressof(value_)) stored_type(std::forward(args)...); \ + engaged_ = true; \ + } \ +\ + struct empty_byte \ + {}; \ + union \ + { \ + empty_byte empty_value_; \ + stored_type value_; \ + }; \ + bool engaged_ = false; + + template ::value> + class optional_base + { + INTERVAL_TREE_OPTIONAL_INTESTINES + + public: + ~optional_base() + { + if (engaged_) + value_.~stored_type(); + } + }; + + template + class optional_base + { + INTERVAL_TREE_OPTIONAL_INTESTINES + }; + + template + class optional : private optional_base + { + public: + using value_type = T; + + using optional_base::optional_base; + + constexpr optional() = default; + + template < + typename U = T, + std::enable_if_t< + !std::is_same, std::decay_t>::value && std::is_constructible::value && + std::is_convertible::value, + bool> = true> + constexpr optional(U&& value) + : optional_base{in_place, std::forward(value)} + {} + + template < + typename U = T, + std::enable_if_t< + !std::is_same, std::decay_t>::value && std::is_constructible::value && + !std::is_convertible::value, + bool> = false> + constexpr optional(U&& value) + : optional_base{in_place, std::forward(value)} + {} + + template < + typename U, + std::enable_if_t< + !std::is_same::value && std::is_constructible::value && + std::is_convertible::value, + bool> = true> + constexpr optional(const optional& other) + { + if (other) + emplace(*other); + } + + template < + typename U, + std::enable_if_t< + !std::is_same::value && std::is_constructible::value && + !std::is_convertible::value, + bool> = false> + explicit constexpr optional(const optional& other) + { + if (other) + emplace(*other); + } + + template < + typename U, + std::enable_if_t< + !std::is_same::value && std::is_constructible::value && + std::is_convertible::value, + bool> = true> + constexpr optional(optional&& other) + { + if (other) + emplace(std::move(*other)); + } + + template < + typename U, + std::enable_if_t< + !std::is_same::value && std::is_constructible::value && + !std::is_convertible::value, + bool> = false> + explicit constexpr optional(optional&& other) + { + if (other) + emplace(std::move(*other)); + } + + optional& operator=(nullopt_t) noexcept + { + this->reset(); + return *this; + } + + template < + typename U = T, + std::enable_if_t< + !std::is_same, std::decay_t>::value && std::is_constructible::value && + !(std::is_scalar::value && std::is_same>::value) && + std::is_assignable::value, + int> = 0> + optional& operator=(U&& value) + { + if (this->engaged_) + this->value_ = std::forward(value); + else + this->construct(std::forward(value)); + return *this; + } + + template + std::enable_if_t< + !std::is_same::value && std::is_constructible::value && + std::is_assignable::value, + optional&> + operator=(const optional& other) + { + if (other) + { + if (this->engaged_) + this->value_ = *other; + else + this->construct(*other); + } + else + { + this->reset(); + } + return *this; + } + + template + std::enable_if_t< + !std::is_same::value && std::is_constructible::value && std::is_assignable::value, + optional&> + operator=(optional&& other) + { + if (other) + { + if (this->engaged_) + this->value_ = std::move(*other); + else + this->construct(std::move(*other)); + } + else + { + this->reset(); + } + return *this; + } + + template + std::enable_if_t::value, void> emplace(Args&&... args) + { + this->reset(); + this->construct(std::forward(args)...); + } + + template + std::enable_if_t&, Args&&...>::value, void> + emplace(std::initializer_list il, Args&&... args) + { + this->reset(); + this->construct(il, std::forward(args)...); + } + + void swap(optional& other) noexcept( + std::is_nothrow_move_constructible::value && std::is_nothrow_swappable::value + ) + { + using std::swap; + + if (this->engaged_ && other.engaged_) + { + swap(this->value_, other.value_); + } + else if (this->engaged_) + { + other.construct(std::move(this->value_)); + this->reset(); + } + else if (other.engaged_) + { + this->construct(std::move(other.value_)); + other.reset(); + } + } + + constexpr const T& value() const& + { + if (this->engaged_) + return this->value_; + throw lib_interval_tree::bad_optional_access(); + } + + constexpr T& value() & + { + if (this->engaged_) + return this->value_; + throw lib_interval_tree::bad_optional_access(); + } + + constexpr T&& value() && + { + if (this->engaged_) + return std::move(this->value_); + throw lib_interval_tree::bad_optional_access(); + } + + constexpr const T&& value() const&& + { + if (this->engaged_) + return std::move(this->value_); + throw lib_interval_tree::bad_optional_access(); + } + + constexpr const T* operator->() const + { + return std::addressof(this->value_); + } + + T* operator->() + { + return std::addressof(this->value_); + } + + constexpr const T& operator*() const& + { + return this->value_; + } + + constexpr T& operator*() & + { + return this->value_; + } + + constexpr T&& operator*() && + { + return std::move(this->value_); + } + + constexpr const T&& operator*() const&& + { + return std::move(this->value_); + } + + constexpr explicit operator bool() const noexcept + { + return this->engaged_; + } + }; +} +#endif diff --git a/tests/custom_interval_tests.hpp b/tests/custom_interval_tests.hpp index 8c97518..ef6fe2b 100644 --- a/tests/custom_interval_tests.hpp +++ b/tests/custom_interval_tests.hpp @@ -51,6 +51,22 @@ struct custom_interval : public lib_interval_tree::interval diff --git a/tests/interval_tests.hpp b/tests/interval_tests.hpp index 0f4d3be..f0c94aa 100644 --- a/tests/interval_tests.hpp +++ b/tests/interval_tests.hpp @@ -899,4 +899,114 @@ TEST_F(IntervalTests, CanFindDynamicIntervalUsingComparisonFunction) EXPECT_EQ(iter->high(), 5); EXPECT_EQ(iter->left_border(), interval_border::closed); EXPECT_EQ(iter->right_border(), interval_border::closed); +} + +TEST_F(IntervalTests, ClosedAdjacentSliceLeftOverlap) +{ + // [ this ] + // [ls][param] + const auto result = i(1, 8).slice(i(5, 100)); + ASSERT_TRUE(result.left_slice); + EXPECT_EQ(result.left_slice->low(), 1); + EXPECT_EQ(result.left_slice->high(), 4); + EXPECT_FALSE(result.right_slice); +} + +TEST_F(IntervalTests, ClosedSliceLeftOverlap) +{ + // [ this ] + // [ls][param] + const auto result = i(1, 8).slice(i(5, 100)); + ASSERT_TRUE(result.left_slice); + EXPECT_EQ(result.left_slice->low(), 1); + EXPECT_EQ(result.left_slice->high(), 5); + EXPECT_FALSE(result.right_slice); +} + +TEST_F(IntervalTests, OpenSliceLeftOverlap) +{ + // [ this ] + // [ls][param] + using lib_interval_tree::open; + const auto result = i(1, 8).slice(i(5, 10)); + ASSERT_TRUE(result.left_slice); + EXPECT_EQ(result.left_slice->low(), 1); + EXPECT_EQ(result.left_slice->high(), 5); + EXPECT_FALSE(result.right_slice); +} + +TEST_F(IntervalTests, ClosedAdjacentRightRemains) +{ + // [ this ] + // [param][rs] + const auto result = i(8, 15).slice(i(8, 12)); + ASSERT_TRUE(result.right_slice); + EXPECT_EQ(result.right_slice->low(), 13); + EXPECT_EQ(result.right_slice->high(), 15); + EXPECT_FALSE(result.left_slice); +} + +TEST_F(IntervalTests, ClosedRightRemains) +{ + // [ this ] + // [param][rs] + using lib_interval_tree::closed; + const auto result = i(8, 15).slice(i(8, 12)); + ASSERT_TRUE(result.right_slice); + EXPECT_EQ(result.right_slice->low(), 12); + EXPECT_EQ(result.right_slice->high(), 15); + EXPECT_FALSE(result.left_slice); +} + +TEST_F(IntervalTests, OpenRightRemains) +{ + // [ this ] + // [param][rs] + using lib_interval_tree::open; + const auto result = i(8, 15).slice(i(8, 12)); + ASSERT_TRUE(result.right_slice); + EXPECT_EQ(result.right_slice->low(), 12); + EXPECT_EQ(result.right_slice->high(), 15); + EXPECT_FALSE(result.left_slice); +} + +TEST_F(IntervalTests, ClosedAdjacentMiddleExtrusion) +{ + // [ this ] + // [ls][param][rs] + const auto result = i(0, 10).slice(i(5, 8)); + ASSERT_TRUE(result.left_slice); + EXPECT_EQ(result.left_slice->low(), 0); + EXPECT_EQ(result.left_slice->high(), 4); + ASSERT_TRUE(result.right_slice); + EXPECT_EQ(result.right_slice->low(), 9); + EXPECT_EQ(result.right_slice->high(), 10); +} + +TEST_F(IntervalTests, OpenMiddleExtrusion) +{ + // [ this ] + // [ls][param][rs] + using lib_interval_tree::open; + const auto result = i(0, 10).slice(i(5, 8)); + ASSERT_TRUE(result.left_slice); + EXPECT_EQ(result.left_slice->low(), 0); + EXPECT_EQ(result.left_slice->high(), 5); + ASSERT_TRUE(result.right_slice); + EXPECT_EQ(result.right_slice->low(), 8); + EXPECT_EQ(result.right_slice->high(), 10); +} + +TEST_F(IntervalTests, ClosedMiddleExtrusion) +{ + // [ this ] + // [ls][param][rs] + using lib_interval_tree::closed; + const auto result = i(0, 10).slice(i(5, 8)); + ASSERT_TRUE(result.left_slice); + EXPECT_EQ(result.left_slice->low(), 0); + EXPECT_EQ(result.left_slice->high(), 5); + ASSERT_TRUE(result.right_slice); + EXPECT_EQ(result.right_slice->low(), 8); + EXPECT_EQ(result.right_slice->high(), 10); } \ No newline at end of file diff --git a/tests/punch_tests.hpp b/tests/punch_tests.hpp new file mode 100644 index 0000000..e8f492e --- /dev/null +++ b/tests/punch_tests.hpp @@ -0,0 +1,1022 @@ +#pragma once + +#include + +#include + +class PunchTests : public ::testing::Test +{ + public: + template + struct closed + { + using interval_type = lib_interval_tree::interval; + using tree_type = lib_interval_tree::interval_tree; + using iterator_type = typename tree_type::iterator; + }; + template + struct open + { + using interval_type = lib_interval_tree::interval; + using tree_type = lib_interval_tree::interval_tree; + using iterator_type = typename tree_type::iterator; + }; + template + struct left_open + { + using interval_type = lib_interval_tree::interval; + using tree_type = lib_interval_tree::interval_tree; + using iterator_type = typename tree_type::iterator; + }; + template + struct right_open + { + using interval_type = lib_interval_tree::interval; + using tree_type = lib_interval_tree::interval_tree; + using iterator_type = typename tree_type::iterator; + }; + template + struct closed_adjacent + { + using interval_type = lib_interval_tree::interval; + using tree_type = lib_interval_tree::interval_tree; + using iterator_type = typename tree_type::iterator; + }; + template + struct dynamic + { + using interval_type = lib_interval_tree::interval; + using tree_type = lib_interval_tree::interval_tree; + using iterator_type = typename tree_type::iterator; + }; + + template + auto i(NumericalT l, NumericalT h) const + { + return lib_interval_tree::interval{l, h}; + } + + template + auto + i(NumericalT l, + NumericalT h, + lib_interval_tree::interval_border l_border, + lib_interval_tree::interval_border r_border) const + { + return lib_interval_tree::interval{l, h, l_border, r_border}; + } +}; + +TEST_F(PunchTests, PunchEmptyTree) +{ + using types = closed_adjacent; + + auto tree = types::tree_type{}; + auto result = tree.punch(types::interval_type{0, 5}); + ASSERT_EQ(result.size(), 1); + EXPECT_EQ(result.begin()->low(), 0); + EXPECT_EQ(result.begin()->high(), 5); +} + +TEST_F(PunchTests, PunchOpenEmptyTree) +{ + using types = open; + + auto tree = types::tree_type{}; + auto result = tree.punch(types::interval_type{0, 5}); + ASSERT_EQ(result.size(), 1); + EXPECT_EQ(result.begin()->low(), 0); + EXPECT_EQ(result.begin()->high(), 5); +} + +TEST_F(PunchTests, PunchClosedEmptyTree) +{ + using types = closed; + + auto tree = types::tree_type{}; + auto result = tree.punch(types::interval_type{0, 5}); + ASSERT_EQ(result.size(), 1); + EXPECT_EQ(result.begin()->low(), 0); + EXPECT_EQ(result.begin()->high(), 5); +} + +TEST_F(PunchTests, PunchFullyRightOfTree) +{ + using types = closed_adjacent; + + auto tree = types::tree_type{}; + tree.insert(types::interval_type{0, 5}); + tree.insert(types::interval_type{5, 10}); + auto result = tree.punch(types::interval_type{20, 25}); + ASSERT_EQ(result.size(), 1); + EXPECT_EQ(result.begin()->low(), 20); + EXPECT_EQ(result.begin()->high(), 25); +} + +TEST_F(PunchTests, PunchFullyRightOfTreeClosed) +{ + using types = closed; + + auto tree = types::tree_type{}; + tree.insert(types::interval_type{0, 5}); + tree.insert(types::interval_type{5, 10}); + auto result = tree.punch(types::interval_type{20, 25}); + ASSERT_EQ(result.size(), 1); + EXPECT_EQ(result.begin()->low(), 20); + EXPECT_EQ(result.begin()->high(), 25); +} + +TEST_F(PunchTests, PunchFullyRightOfTreeOpen) +{ + using types = open; + + auto tree = types::tree_type{}; + tree.insert(types::interval_type{0, 5}); + tree.insert(types::interval_type{5, 10}); + auto result = tree.punch(types::interval_type{20, 25}); + ASSERT_EQ(result.size(), 1); + EXPECT_EQ(result.begin()->low(), 20); + EXPECT_EQ(result.begin()->high(), 25); +} + +TEST_F(PunchTests, PunchFullyLeftOfTree) +{ + using types = closed_adjacent; + + auto tree = types::tree_type{}; + tree.insert(types::interval_type{0, 5}); + tree.insert(types::interval_type{5, 10}); + auto result = tree.punch(types::interval_type{-10, -5}); + + ASSERT_EQ(result.size(), 1); + EXPECT_EQ(result.begin()->low(), -10); + EXPECT_EQ(result.begin()->high(), -5); +} + +TEST_F(PunchTests, PunchFullyLeftOfTreeClosed) +{ + using types = closed; + + auto tree = types::tree_type{}; + tree.insert(types::interval_type{0, 5}); + tree.insert(types::interval_type{5, 10}); + auto result = tree.punch(types::interval_type{-10, -5}); + + ASSERT_EQ(result.size(), 1); + EXPECT_EQ(result.begin()->low(), -10); + EXPECT_EQ(result.begin()->high(), -5); +} + +TEST_F(PunchTests, PunchFullyLeftOfTreeOpen) +{ + using types = open; + + auto tree = types::tree_type{}; + tree.insert(types::interval_type{0, 5}); + tree.insert(types::interval_type{5, 10}); + auto result = tree.punch(types::interval_type{-10, -5}); + + ASSERT_EQ(result.size(), 1); + EXPECT_EQ(result.begin()->low(), -10); + EXPECT_EQ(result.begin()->high(), -5); +} + +TEST_F(PunchTests, PunchAdjacentLeftOfClosedAdjacentAdjacentTree) +{ + using types = closed_adjacent; + + auto tree = types::tree_type{}; + tree.insert(types::interval_type{0, 5}); + tree.insert(types::interval_type{5, 10}); + auto result = tree.punch(types::interval_type{-10, 0}); + + ASSERT_EQ(result.size(), 1); + EXPECT_EQ(result.begin()->low(), -10); + EXPECT_EQ(result.begin()->high(), -1); +} + +TEST_F(PunchTests, PunchAjdacentLeftOfOpenTree) +{ + using types = open; + + auto tree = types::tree_type{}; + tree.insert(types::interval_type{0, 5}); + tree.insert(types::interval_type{5, 10}); + auto result = tree.punch(types::interval_type{-10, 0}); + + ASSERT_EQ(result.size(), 1); + EXPECT_EQ(result.begin()->low(), -10); + EXPECT_EQ(result.begin()->high(), 0); +} + +TEST_F(PunchTests, PunchAjdacentLeftOfClosedTree) +{ + using types = closed; + + auto tree = types::tree_type{}; + tree.insert(types::interval_type{0, 5}); + tree.insert(types::interval_type{5, 10}); + auto result = tree.punch(types::interval_type{-10, 0}); + + ASSERT_EQ(result.size(), 1); + EXPECT_EQ(result.begin()->low(), -10); + EXPECT_EQ(result.begin()->high(), 0); +} + +TEST_F(PunchTests, PunchAjdacentLeftOfLeftOpenTree) +{ + using types = left_open; + + auto tree = types::tree_type{}; + tree.insert(types::interval_type{0, 5}); + tree.insert(types::interval_type{5, 10}); + auto result = tree.punch(types::interval_type{-10, 0}); + + ASSERT_EQ(result.size(), 1); + EXPECT_EQ(result.begin()->low(), -10); + EXPECT_EQ(result.begin()->high(), 0); +} + +TEST_F(PunchTests, PunchAjdacentLeftOfRightOpenTree) +{ + using types = right_open; + + auto tree = types::tree_type{}; + tree.insert(types::interval_type{0, 5}); + tree.insert(types::interval_type{5, 10}); + auto result = tree.punch(types::interval_type{-10, 0}); + + ASSERT_EQ(result.size(), 1); + EXPECT_EQ(result.begin()->low(), -10); + EXPECT_EQ(result.begin()->high(), 0); +} + +TEST_F(PunchTests, PunchOverlapsLeftOfTreeLeftHanging) +{ + using types = closed_adjacent; + + auto tree = types::tree_type{}; + tree.insert(types::interval_type{0, 5}); + tree.insert(types::interval_type{5, 10}); + auto result = tree.punch(types::interval_type{-10, 2}); + + ASSERT_EQ(result.size(), 1); + EXPECT_EQ(result.begin()->low(), -10); + EXPECT_EQ(result.begin()->high(), -1); +} + +TEST_F(PunchTests, PunchOverlapsLeftOfTreeLeftHangingClosed) +{ + using types = closed; + + auto tree = types::tree_type{}; + tree.insert(types::interval_type{0, 5}); + tree.insert(types::interval_type{5, 10}); + auto result = tree.punch(types::interval_type{-10, 2}); + + ASSERT_EQ(result.size(), 1); + EXPECT_EQ(result.begin()->low(), -10); + EXPECT_EQ(result.begin()->high(), 0); +} + +TEST_F(PunchTests, PunchOverlapsLeftOfTreeLeftHangingOpen) +{ + using types = open; + + auto tree = types::tree_type{}; + tree.insert(types::interval_type{0, 5}); + tree.insert(types::interval_type{5, 10}); + auto result = tree.punch(types::interval_type{-10, 2}); + + ASSERT_EQ(result.size(), 1); + EXPECT_EQ(result.begin()->low(), -10); + EXPECT_EQ(result.begin()->high(), 0); +} + +TEST_F(PunchTests, PunchOverlapsLeftOfTreeFullyExact) +{ + using types = closed_adjacent; + + auto tree = types::tree_type{}; + tree.insert(types::interval_type{0, 5}); + tree.insert(types::interval_type{5, 10}); + auto result = tree.punch(types::interval_type{-10, 5}); + + ASSERT_EQ(result.size(), 1); + EXPECT_EQ(result.begin()->low(), -10); + EXPECT_EQ(result.begin()->high(), -1); +} + +TEST_F(PunchTests, PunchOverlapsLeftOfTreeFullyExactClosed) +{ + using types = closed; + + auto tree = types::tree_type{}; + tree.insert(types::interval_type{0, 5}); + tree.insert(types::interval_type{5, 10}); + auto result = tree.punch(types::interval_type{-10, 5}); + + ASSERT_EQ(result.size(), 1); + EXPECT_EQ(result.begin()->low(), -10); + EXPECT_EQ(result.begin()->high(), 0); +} + +TEST_F(PunchTests, PunchOverlapsLeftOfTreeFullyExactOpen) +{ + using types = open; + + auto tree = types::tree_type{}; + tree.insert(types::interval_type{0, 5}); + tree.insert(types::interval_type{5, 10}); + auto result = tree.punch(types::interval_type{-10, 5}); + + ASSERT_EQ(result.size(), 1); + EXPECT_EQ(result.begin()->low(), -10); + EXPECT_EQ(result.begin()->high(), 0); +} + +TEST_F(PunchTests, PunchOverlapsLeftOfTreeRightFullyOverNoGap) +{ + using types = closed_adjacent; + + auto tree = types::tree_type{}; + tree.insert(types::interval_type{0, 5}); + tree.insert(types::interval_type{5, 10}); + auto result = tree.punch(types::interval_type{-10, 7}); + + ASSERT_EQ(result.size(), 1); + auto iter = result.begin(); + + EXPECT_EQ(iter->low(), -10); + EXPECT_EQ(iter->high(), -1); +} + +TEST_F(PunchTests, PunchOverlapsLeftOfTreeRightFullyOver) +{ + using types = closed_adjacent; + + auto tree = types::tree_type{}; + tree.insert(types::interval_type{0, 5}); + tree.insert(types::interval_type{10, 15}); + auto result = tree.punch(types::interval_type{-10, 7}); + + ASSERT_EQ(result.size(), 2); + auto iter = result.begin(); + + EXPECT_EQ(iter->low(), -10); + EXPECT_EQ(iter->high(), -1); + + EXPECT_EQ((++iter)->low(), 6); + EXPECT_EQ(iter->high(), 7); +} + +TEST_F(PunchTests, PunchOverlapsLeftOfTreeFullyInsideInterval) +{ + using types = closed_adjacent; + + auto tree = types::tree_type{}; + tree.insert(types::interval_type{0, 5}); + tree.insert(types::interval_type{5, 10}); + auto result = tree.punch(types::interval_type{1, 3}); + + EXPECT_TRUE(result.empty()); +} + +TEST_F(PunchTests, PunchOverlapsMiddleOfTreeFullyInsideInterval) +{ + using types = closed_adjacent; + + auto tree = types::tree_type{}; + tree.insert(types::interval_type{0, 5}); + tree.insert(types::interval_type{5, 10}); + tree.insert(types::interval_type{10, 15}); + auto result = tree.punch(types::interval_type{6, 9}); + + EXPECT_TRUE(result.empty()); +} + +TEST_F(PunchTests, PunchOverlapsRightOfTreeFullyInsideInterval) +{ + using types = closed_adjacent; + + auto tree = types::tree_type{}; + tree.insert(types::interval_type{0, 5}); + tree.insert(types::interval_type{5, 10}); + auto result = tree.punch(types::interval_type{6, 9}); + + // Expect nothing + EXPECT_TRUE(result.empty()); +} + +TEST_F(PunchTests, ClosedAdjacentPunchOverhangsLastIntervalOnRight) +{ + using types = closed_adjacent; + + auto tree = types::tree_type{}; + tree.insert(types::interval_type{0, 5}); + tree.insert(types::interval_type{5, 10}); + auto result = tree.punch(types::interval_type{8, 15}); + + ASSERT_EQ(result.size(), 1); + EXPECT_EQ(result.begin()->low(), 11); + EXPECT_EQ(result.begin()->high(), 15); +} + +TEST_F(PunchTests, OpenPunchOverhangsLastIntervalOnRight) +{ + using types = open; + + auto tree = types::tree_type{}; + tree.insert(types::interval_type{0, 5}); + tree.insert(types::interval_type{5, 10}); + auto result = tree.punch(types::interval_type{8, 12}); + + ASSERT_EQ(result.size(), 1); + EXPECT_EQ(result.begin()->low(), 10); + EXPECT_EQ(result.begin()->high(), 12); +} + +TEST_F(PunchTests, LeftOpenPunchOverhangsLastIntervalOnRight) +{ + using types = left_open; + + auto tree = types::tree_type{}; + tree.insert(types::interval_type{0, 5}); + tree.insert(types::interval_type{5, 10}); + auto result = tree.punch(types::interval_type{8, 12}); + + ASSERT_EQ(result.size(), 1); + EXPECT_EQ(result.begin()->low(), 10); + EXPECT_EQ(result.begin()->high(), 12); +} + +TEST_F(PunchTests, RightOpenPunchOverhangsLastIntervalOnRight) +{ + using types = right_open; + + auto tree = types::tree_type{}; + tree.insert(types::interval_type{0, 5}); + tree.insert(types::interval_type{5, 10}); + auto result = tree.punch(types::interval_type{8, 12}); + + ASSERT_EQ(result.size(), 1); + EXPECT_EQ(result.begin()->low(), 10); + EXPECT_EQ(result.begin()->high(), 12); +} + +TEST_F(PunchTests, PunchSingleElementTreeEncompassing) +{ + using types = closed_adjacent; + + auto tree = types::tree_type{}; + tree.insert(types::interval_type{0, 5}); + auto result = tree.punch(types::interval_type{0, 5}); + + EXPECT_TRUE(result.empty()); +} + +TEST_F(PunchTests, PunchSingleElementTreeEncompassingWithRightOverlap) +{ + using types = closed_adjacent; + + auto tree = types::tree_type{}; + tree.insert(types::interval_type{0, 5}); + auto result = tree.punch(types::interval_type{0, 6}); + + ASSERT_EQ(result.size(), 1); + EXPECT_EQ(result.begin()->low(), 6); + EXPECT_EQ(result.begin()->high(), 6); +} + +TEST_F(PunchTests, PunchSingleElementTreeEncompassingWithLeftOverlap) +{ + using types = closed_adjacent; + + auto tree = types::tree_type{}; + tree.insert(types::interval_type{0, 5}); + auto result = tree.punch(types::interval_type{-1, 5}); + + ASSERT_EQ(result.size(), 1); + EXPECT_EQ(result.begin()->low(), -1); + EXPECT_EQ(result.begin()->high(), -1); +} + +TEST_F(PunchTests, DynamicPunchOverhangsLastIntervalOnRight) +{ + using namespace lib_interval_tree; + + using types = dynamic; + + auto tree_closed = types::tree_type{}; + tree_closed.insert(types::interval_type{0, 5, interval_border::closed, interval_border::closed}); + tree_closed.insert(types::interval_type{5, 10, interval_border::closed, interval_border::closed}); + auto result_closed = + tree_closed.punch(types::interval_type{8, 12, interval_border::closed, interval_border::closed}); + EXPECT_EQ(result_closed.size(), 1); + EXPECT_EQ(result_closed.begin()->low(), 10); + EXPECT_EQ(result_closed.begin()->left_border(), interval_border::open); + EXPECT_EQ(result_closed.begin()->high(), 12); + + auto tree_open = types::tree_type{}; + tree_open.insert(types::interval_type{0, 5, interval_border::open, interval_border::open}); + tree_open.insert(types::interval_type{5, 10, interval_border::open, interval_border::open}); + auto result_open = tree_open.punch(types::interval_type{8, 12, interval_border::open, interval_border::open}); + EXPECT_EQ(result_open.size(), 1); + EXPECT_EQ(result_open.begin()->low(), 10); + EXPECT_EQ(result_open.begin()->left_border(), interval_border::closed); + EXPECT_EQ(result_open.begin()->high(), 12); + + auto tree_left_open = types::tree_type{}; + tree_left_open.insert(types::interval_type{0, 5, interval_border::open, interval_border::closed}); + tree_left_open.insert(types::interval_type{5, 10, interval_border::open, interval_border::closed}); + auto result_left_open = + tree_left_open.punch(types::interval_type{8, 12, interval_border::open, interval_border::closed}); + EXPECT_EQ(result_left_open.size(), 1); + EXPECT_EQ(result_left_open.begin()->low(), 10); + EXPECT_EQ(result_left_open.begin()->left_border(), interval_border::open); + EXPECT_EQ(result_left_open.begin()->high(), 12); + + auto tree_right_open = types::tree_type{}; + tree_right_open.insert(types::interval_type{0, 5, interval_border::closed, interval_border::open}); + tree_right_open.insert(types::interval_type{5, 10, interval_border::closed, interval_border::open}); + auto result_right_open = + tree_right_open.punch(types::interval_type{8, 12, interval_border::closed, interval_border::open}); + EXPECT_EQ(result_right_open.size(), 1); + EXPECT_EQ(result_right_open.begin()->low(), 10); + EXPECT_EQ(result_right_open.begin()->left_border(), interval_border::closed); + EXPECT_EQ(result_right_open.begin()->high(), 12); + + auto tree_closed_adjacent = types::tree_type{}; + tree_closed_adjacent.insert( + types::interval_type{0, 5, interval_border::closed_adjacent, interval_border::closed_adjacent} + ); + tree_closed_adjacent.insert( + types::interval_type{5, 10, interval_border::closed_adjacent, interval_border::closed_adjacent} + ); + auto result_closed_adjacent = tree_closed_adjacent.punch( + types::interval_type{8, 12, interval_border::closed_adjacent, interval_border::closed_adjacent} + ); + EXPECT_EQ(result_closed_adjacent.size(), 1); + EXPECT_EQ(result_closed_adjacent.begin()->low(), 10); + EXPECT_EQ(result_closed_adjacent.begin()->left_border(), interval_border::open); + EXPECT_EQ(result_closed_adjacent.begin()->high(), 12); +} + +TEST_F(PunchTests, DynamicPunchOverhangsFirstIntervalOnLeft) +{ + using namespace lib_interval_tree; + + using types = dynamic; + + auto tree_closed = types::tree_type{}; + tree_closed.insert(types::interval_type{0, 5, interval_border::closed, interval_border::closed}); + tree_closed.insert(types::interval_type{5, 10, interval_border::closed, interval_border::closed}); + auto result_closed = + tree_closed.punch(types::interval_type{-10, 3, interval_border::closed, interval_border::closed}); + EXPECT_EQ(result_closed.size(), 1); + EXPECT_EQ(result_closed.begin()->low(), -10); + EXPECT_EQ(result_closed.begin()->right_border(), interval_border::open); + EXPECT_EQ(result_closed.begin()->high(), 0); + + auto tree_open = types::tree_type{}; + tree_open.insert(types::interval_type{0, 5, interval_border::open, interval_border::open}); + tree_open.insert(types::interval_type{5, 10, interval_border::open, interval_border::open}); + auto result_open = tree_open.punch(types::interval_type{-10, 3, interval_border::open, interval_border::open}); + EXPECT_EQ(result_open.size(), 1); + EXPECT_EQ(result_open.begin()->low(), -10); + EXPECT_EQ(result_open.begin()->right_border(), interval_border::closed); + EXPECT_EQ(result_open.begin()->high(), 0); + + auto tree_left_open = types::tree_type{}; + tree_left_open.insert(types::interval_type{0, 5, interval_border::open, interval_border::closed}); + tree_left_open.insert(types::interval_type{5, 10, interval_border::open, interval_border::closed}); + auto result_left_open = + tree_left_open.punch(types::interval_type{-10, 3, interval_border::open, interval_border::closed}); + EXPECT_EQ(result_left_open.size(), 1); + EXPECT_EQ(result_left_open.begin()->low(), -10); + EXPECT_EQ(result_left_open.begin()->right_border(), interval_border::closed); + EXPECT_EQ(result_left_open.begin()->high(), 0); + + auto tree_right_open = types::tree_type{}; + tree_right_open.insert(types::interval_type{0, 5, interval_border::closed, interval_border::open}); + tree_right_open.insert(types::interval_type{5, 10, interval_border::closed, interval_border::open}); + auto result_right_open = + tree_right_open.punch(types::interval_type{-10, 3, interval_border::closed, interval_border::open}); + EXPECT_EQ(result_right_open.size(), 1); + EXPECT_EQ(result_right_open.begin()->low(), -10); + EXPECT_EQ(result_right_open.begin()->right_border(), interval_border::open); + EXPECT_EQ(result_right_open.begin()->high(), 0); + + auto tree_closed_adjacent = types::tree_type{}; + tree_closed_adjacent.insert( + types::interval_type{0, 5, interval_border::closed_adjacent, interval_border::closed_adjacent} + ); + tree_closed_adjacent.insert( + types::interval_type{5, 10, interval_border::closed_adjacent, interval_border::closed_adjacent} + ); + auto result_closed_adjacent = tree_closed_adjacent.punch( + types::interval_type{-10, 3, interval_border::closed_adjacent, interval_border::closed_adjacent} + ); + EXPECT_EQ(result_closed_adjacent.size(), 1); + EXPECT_EQ(result_closed_adjacent.begin()->low(), -10); + EXPECT_EQ(result_closed_adjacent.begin()->right_border(), interval_border::open); + EXPECT_EQ(result_closed_adjacent.begin()->high(), 0); +} + +TEST_F(PunchTests, PunchEncompassesTree) +{ + using types = closed_adjacent; + + auto tree = types::tree_type{}; + tree.insert(types::interval_type{0, 5}); + tree.insert(types::interval_type{10, 15}); + tree.insert(types::interval_type{20, 25}); + tree.insert(types::interval_type{30, 35}); + auto result = tree.punch(types::interval_type{0, 35}); + + ASSERT_EQ(result.size(), 3); + auto iter = result.begin(); + EXPECT_EQ(iter->low(), 6); + EXPECT_EQ(iter->high(), 9); + + EXPECT_EQ((++iter)->low(), 16); + EXPECT_EQ(iter->high(), 19); + + EXPECT_EQ((++iter)->low(), 26); + EXPECT_EQ(iter->high(), 29); +} + +TEST_F(PunchTests, PunchEncompassesOpenTree) +{ + using types = open; + + auto tree = types::tree_type{}; + tree.insert(types::interval_type{0, 5}); + tree.insert(types::interval_type{10, 15}); + tree.insert(types::interval_type{20, 25}); + tree.insert(types::interval_type{30, 35}); + auto result = tree.punch(types::interval_type{0, 35}); + + ASSERT_EQ(result.size(), 3); + auto iter = result.begin(); + EXPECT_EQ(iter->low(), 5); + EXPECT_EQ(iter->high(), 10); + EXPECT_EQ((++iter)->low(), 15); + EXPECT_EQ(iter->high(), 20); + EXPECT_EQ((++iter)->low(), 25); + EXPECT_EQ(iter->high(), 30); +} + +TEST_F(PunchTests, PunchEncompassesSmallGapOpenTree) +{ + using types = open; + + auto tree = types::tree_type{}; + tree.insert(types::interval_type{0, 5}); + tree.insert(types::interval_type{6, 10}); + tree.insert(types::interval_type{11, 15}); + tree.insert(types::interval_type{16, 20}); + auto result = tree.punch(); + + EXPECT_TRUE(result.empty()); +} + +TEST_F(PunchTests, PunchEncompassesSmallGapClosedTree) +{ + using types = closed; + + auto tree = types::tree_type{}; + tree.insert(types::interval_type{0, 5}); + tree.insert(types::interval_type{6, 10}); + tree.insert(types::interval_type{11, 15}); + tree.insert(types::interval_type{16, 20}); + auto result = tree.punch(); + + ASSERT_EQ(result.size(), 3); + auto iter = result.begin(); + EXPECT_EQ(iter->low(), 5); + EXPECT_EQ(iter->high(), 6); + EXPECT_EQ((++iter)->low(), 10); + EXPECT_EQ(iter->high(), 11); + EXPECT_EQ((++iter)->low(), 15); + EXPECT_EQ(iter->high(), 16); +} + +TEST_F(PunchTests, PunchEncompassesClosedTree) +{ + using types = closed; + + auto tree = types::tree_type{}; + tree.insert(types::interval_type{0, 5}); + tree.insert(types::interval_type{10, 15}); + tree.insert(types::interval_type{20, 25}); + tree.insert(types::interval_type{30, 35}); + auto result = tree.punch(types::interval_type{0, 35}); + + ASSERT_EQ(result.size(), 3); + auto iter = result.begin(); + EXPECT_EQ(iter->low(), 5); + EXPECT_EQ(iter->high(), 10); + EXPECT_EQ((++iter)->low(), 15); + EXPECT_EQ(iter->high(), 20); + EXPECT_EQ((++iter)->low(), 25); + EXPECT_EQ(iter->high(), 30); +} + +TEST_F(PunchTests, PunchEncompassesTreeWithOverlap) +{ + using types = closed_adjacent; + + auto tree = types::tree_type{}; + tree.insert(types::interval_type{0, 5}); + tree.insert(types::interval_type{10, 15}); + tree.insert(types::interval_type{20, 25}); + tree.insert(types::interval_type{30, 35}); + auto result = tree.punch(types::interval_type{-5, 40}); + + ASSERT_EQ(result.size(), 5); + auto iter = result.begin(); + EXPECT_EQ(iter->low(), -5); + EXPECT_EQ(iter->high(), -1); + + EXPECT_EQ((++iter)->low(), 6); + EXPECT_EQ(iter->high(), 9); + + EXPECT_EQ((++iter)->low(), 16); + EXPECT_EQ(iter->high(), 19); + + EXPECT_EQ((++iter)->low(), 26); + EXPECT_EQ(iter->high(), 29); + + EXPECT_EQ((++iter)->low(), 36); + EXPECT_EQ(iter->high(), 40); +} + +TEST_F(PunchTests, PunchEncompassesOpenTreeWithOverlap) +{ + using types = open; + + auto tree = types::tree_type{}; + tree.insert(types::interval_type{0, 5}); + tree.insert(types::interval_type{10, 15}); + tree.insert(types::interval_type{20, 25}); + tree.insert(types::interval_type{30, 35}); + auto result = tree.punch(types::interval_type{-5, 40}); + + ASSERT_EQ(result.size(), 5); + auto iter = result.begin(); + EXPECT_EQ(iter->low(), -5); + EXPECT_EQ(iter->high(), 0); + + EXPECT_EQ((++iter)->low(), 5); + EXPECT_EQ(iter->high(), 10); + + EXPECT_EQ((++iter)->low(), 15); + EXPECT_EQ(iter->high(), 20); + + EXPECT_EQ((++iter)->low(), 25); + EXPECT_EQ(iter->high(), 30); + + EXPECT_EQ((++iter)->low(), 35); + EXPECT_EQ(iter->high(), 40); +} + +TEST_F(PunchTests, PunchEncompassesClosedTreeWithOverlap) +{ + using types = closed; + + auto tree = types::tree_type{}; + tree.insert(types::interval_type{0, 5}); + tree.insert(types::interval_type{10, 15}); + tree.insert(types::interval_type{20, 25}); + tree.insert(types::interval_type{30, 35}); + auto result = tree.punch(types::interval_type{-5, 40}); + + ASSERT_EQ(result.size(), 5); + auto iter = result.begin(); + EXPECT_EQ(iter->low(), -5); + EXPECT_EQ(iter->high(), 0); + + EXPECT_EQ((++iter)->low(), 5); + EXPECT_EQ(iter->high(), 10); + + EXPECT_EQ((++iter)->low(), 15); + EXPECT_EQ(iter->high(), 20); + + EXPECT_EQ((++iter)->low(), 25); + EXPECT_EQ(iter->high(), 30); + + EXPECT_EQ((++iter)->low(), 35); + EXPECT_EQ(iter->high(), 40); +} + +TEST_F(PunchTests, UnsignedPunchHasNoProblemWithZero) +{ + using types = closed_adjacent; + + auto tree = types::tree_type{}; + tree.insert(types::interval_type{0, 5}); + + auto result = tree.punch(types::interval_type{0, 7}); + ASSERT_EQ(result.size(), 1); + EXPECT_EQ(result.begin()->low(), 6); + EXPECT_EQ(result.begin()->high(), 7); + + auto tree2 = types::tree_type{}; + tree2.insert(types::interval_type{1, 5}); + + auto result2 = tree2.punch(types::interval_type{0, 7}); + + ASSERT_EQ(result2.size(), 2); + auto iter = result2.begin(); + EXPECT_EQ(iter->low(), 0); + EXPECT_EQ(iter->high(), 0); + EXPECT_EQ((++iter)->low(), 6); + EXPECT_EQ(iter->high(), 7); +} + +TEST_F(PunchTests, PunchDoesNotInsertIntervalWhenClosedAdjacentGapIsEmpty) +{ + using types = closed_adjacent; + + auto tree = types::tree_type{}; + tree.insert(types::interval_type{0, 5}); + tree.insert(types::interval_type{6, 10}); + + auto result = tree.punch(types::interval_type{0, 12}); + ASSERT_EQ(result.size(), 1); + + EXPECT_EQ(result.begin()->low(), 11); + EXPECT_EQ(result.begin()->high(), 12); +} + +TEST_F(PunchTests, PunchDoesInsertIntervalWhenClosedGapIsSmallNotEmpty) +{ + using types = closed; + + auto tree = types::tree_type{}; + tree.insert(types::interval_type{0, 5}); + tree.insert(types::interval_type{6, 10}); + + auto result = tree.punch(types::interval_type{0, 12}); + + ASSERT_EQ(result.size(), 2); + auto iter = result.begin(); + EXPECT_EQ(iter->low(), 5); + EXPECT_EQ(iter->high(), 6); + EXPECT_EQ((++iter)->low(), 10); + EXPECT_EQ(iter->high(), 12); +} + +TEST_F(PunchTests, PunchDoesInsertIntervalWhenOpenGapIsEmpty) +{ + using types = open; + + auto tree = types::tree_type{}; + tree.insert(types::interval_type{0, 5}); + tree.insert(types::interval_type{6, 10}); + + auto result = tree.punch(types::interval_type{0, 12}); + + // (5, 6) is the empty set on the whole number line. + + ASSERT_EQ(result.size(), 1); + auto iter = result.begin(); + EXPECT_EQ(iter->low(), 10); + EXPECT_EQ(iter->high(), 12); +} + +TEST_F(PunchTests, OpenFloatGap) +{ + using types = open; + + auto tree = types::tree_type{}; + tree.insert(types::interval_type{0.0f, 5.0f}); + tree.insert(types::interval_type{6.0f, 10.0f}); + + auto result = tree.punch(types::interval_type{0.0f, 12.0f}); + ASSERT_EQ(result.size(), 2); + + auto iter = result.begin(); + EXPECT_FLOAT_EQ(iter->low(), 5.0f); + EXPECT_FLOAT_EQ(iter->high(), 6.0f); + EXPECT_FLOAT_EQ((++iter)->low(), 10.0f); + EXPECT_FLOAT_EQ(iter->high(), 12.0f); +} + +TEST_F(PunchTests, ClosedFloatGap) +{ + using types = closed; + + auto tree = types::tree_type{}; + tree.insert(types::interval_type{0.0f, 5.0f}); + tree.insert(types::interval_type{6.0f, 10.0f}); + + auto result = tree.punch(types::interval_type{0.0f, 12.0f}); + ASSERT_EQ(result.size(), 2); + + auto iter = result.begin(); + EXPECT_FLOAT_EQ(iter->low(), 5.0f); + EXPECT_FLOAT_EQ(iter->high(), 6.0f); + EXPECT_FLOAT_EQ((++iter)->low(), 10.0f); + EXPECT_FLOAT_EQ(iter->high(), 12.0f); +} + +TEST_F(PunchTests, ClosedAdjacentFloatGap) +{ + using types = closed_adjacent; + + auto tree = types::tree_type{}; + tree.insert(types::interval_type{0.0f, 5.0f}); + tree.insert(types::interval_type{6.0f, 10.0f}); + + auto result = tree.punch(types::interval_type{0.0f, 12.0f}); + ASSERT_EQ(result.size(), 2); + + auto iter = result.begin(); + EXPECT_NEAR(iter->low(), 5.0f, 0.001f); + EXPECT_NEAR(iter->high(), 6.0f, 0.001f); + EXPECT_NEAR((++iter)->low(), 10.0f, 0.001f); + EXPECT_FLOAT_EQ(iter->high(), 12.0f); +} + +TEST_F(PunchTests, PunchWithoutArgsEncompassesTree) +{ + using types = open; + + auto tree = types::tree_type{}; + tree.insert(types::interval_type{0, 5}); + tree.insert(types::interval_type{10, 15}); + tree.insert(types::interval_type{20, 25}); + tree.insert(types::interval_type{30, 35}); + auto result = tree.punch(); + + ASSERT_EQ(result.size(), 3); + auto iter = result.begin(); + EXPECT_EQ(iter->low(), 5); + EXPECT_EQ(iter->high(), 10); + + EXPECT_EQ((++iter)->low(), 15); + EXPECT_EQ(iter->high(), 20); + + EXPECT_EQ((++iter)->low(), 25); + EXPECT_EQ(iter->high(), 30); +} + +TEST_F(PunchTests, PunchWithoutArgsEncompassesTreeClosed) +{ + using types = closed; + + auto tree = types::tree_type{}; + tree.insert(types::interval_type{0, 5}); + tree.insert(types::interval_type{10, 15}); + tree.insert(types::interval_type{20, 25}); + tree.insert(types::interval_type{30, 35}); + auto result = tree.punch(); + + ASSERT_EQ(result.size(), 3); + auto iter = result.begin(); + EXPECT_EQ(iter->low(), 5); + EXPECT_EQ(iter->high(), 10); + + EXPECT_EQ((++iter)->low(), 15); + EXPECT_EQ(iter->high(), 20); + + EXPECT_EQ((++iter)->low(), 25); + EXPECT_EQ(iter->high(), 30); +} + +TEST_F(PunchTests, PunchWithoutArgsEncompassesTreeClosedAdjacent) +{ + using types = closed_adjacent; + + auto tree = types::tree_type{}; + tree.insert(types::interval_type{0, 5}); + tree.insert(types::interval_type{10, 15}); + tree.insert(types::interval_type{20, 25}); + tree.insert(types::interval_type{30, 35}); + auto result = tree.punch(); + + ASSERT_EQ(result.size(), 3); + auto iter = result.begin(); + EXPECT_EQ(iter->low(), 6); + EXPECT_EQ(iter->high(), 9); + + EXPECT_EQ((++iter)->low(), 16); + EXPECT_EQ(iter->high(), 19); + + EXPECT_EQ((++iter)->low(), 26); + EXPECT_EQ(iter->high(), 29); +} + +TEST_F(PunchTests, ClosedNoGap) +{ + using types = closed; + + auto tree = types::tree_type{}; + tree.insert(types::interval_type{0, 5}); + tree.insert(types::interval_type{5, 10}); + auto result = tree.punch(); + + EXPECT_TRUE(result.empty()); +} \ No newline at end of file diff --git a/tests/tests.cpp b/tests/tests.cpp index 93a925c..b9f11d2 100644 --- a/tests/tests.cpp +++ b/tests/tests.cpp @@ -5,7 +5,6 @@ #include "typedefs.hpp" // following headers expect to be included after gtest headers and interval_tree -#include "interval_tests.hpp" #include "interval_tree_tests.hpp" #include "insert_tests.hpp" #include "erase_tests.hpp" @@ -16,6 +15,8 @@ #include "hook_tests.hpp" #include "custom_interval_tests.hpp" #include "dot_draw_tests.hpp" +#include "punch_tests.hpp" +#include "interval_tests.hpp" int main(int argc, char** argv) {