Skip to content

Commit f3d42c4

Browse files
committed
WIP: blabla
1 parent a92a5c5 commit f3d42c4

File tree

5 files changed

+814
-176
lines changed

5 files changed

+814
-176
lines changed

drawings/example_drawings.hpp

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
#include <interval-tree/draw.hpp>
44

5+
#include <random>
56
#include <string>
7+
#include <iostream>
68

79
static void drawDocExample()
810
{
@@ -31,7 +33,7 @@ static void drawFromTests1()
3133

3234
interval_tree_t<int> tree;
3335

34-
std::vector <interval_tree_t<int>::interval_type> intervalCollection;
36+
std::vector<interval_tree_t<int>::interval_type> intervalCollection;
3537

3638
intervalCollection.push_back({-51, 11});
3739
intervalCollection.push_back({26, 68});
@@ -67,8 +69,39 @@ static void drawFromTests1()
6769
drawTree("drawings/from_tests_1_deoverlapped.png", tree, false, false);
6870
}
6971

72+
static void drawLargeOverlapFree()
73+
{
74+
using namespace lib_interval_tree;
75+
76+
interval_tree_t<int> tree;
77+
78+
for (int i = 0; i < 30; ++i)
79+
{
80+
tree.insert({i * 10, i * 10 + 5});
81+
}
82+
83+
drawTree("drawings/large_overlap_free.png", tree, false, false);
84+
85+
std::vector<std::pair<int, int>> intervals;
86+
for (int i = 0; i < 30; ++i)
87+
{
88+
intervals.emplace_back(i * 10, i * 10 + 5);
89+
}
90+
91+
// insert shuffled into new tree
92+
interval_tree_t<int> tree2;
93+
std::mt19937 rng(std::random_device{}());
94+
std::shuffle(intervals.begin(), intervals.end(), rng);
95+
for (const auto& interval : intervals)
96+
{
97+
tree2.insert({interval.first, interval.second});
98+
}
99+
drawTree("drawings/large_overlap_free_shuffled.png", tree2, false, false);
100+
}
101+
70102
static void drawAll()
71103
{
72104
drawDocExample();
73105
drawFromTests1();
106+
drawLargeOverlapFree();
74107
}

include/interval-tree/interval_tree.hpp

Lines changed: 100 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <stdexcept>
1111
#include <iterator>
1212
#include <type_traits>
13+
#include <cassert>
1314

1415
namespace lib_interval_tree
1516
{
@@ -218,21 +219,19 @@ namespace lib_interval_tree
218219
*/
219220
slice_type<interval> extrude(interval const& other) const
220221
{
221-
if (!overlaps(other))
222-
return {};
223-
224222
slice_type<interval> slices{};
225223
if (low_ < other.low_)
226224
{
227-
auto slice = interval{low_, interval_kind::left_slice_upper_bound(other.low_)};
225+
auto slice = interval{low_, std::min(interval_kind::left_slice_upper_bound(other.low_), high_)};
228226
// >= comparison avoids overflows in case of unsigned integers
229227
if (slice.high_ >= slice.low_ && slice.size() > 0)
230228
slices.left_slice = std::move(slice);
231229
}
232230
// low_ == other.low_ does not produce a left slice in any case.
233-
if (other.high_ > high_)
231+
if (high_ > other.high_)
234232
{
235-
auto slice = interval{interval_kind::right_slice_lower_bound(high_), other.high_};
233+
// FIXME: think: is the max violating edge conditions?
234+
auto slice = interval{std::max(interval_kind::right_slice_lower_bound(other.high_), low_), high_};
236235
// >= comparison avoids overflows in case of unsigned integers
237236
if (slice.high_ >= slice.low_ && slice.size() > 0)
238237
slices.right_slice = std::move(slice);
@@ -376,15 +375,25 @@ namespace lib_interval_tree
376375
slice_type<interval> slices{};
377376
if (low_ < other.low_)
378377
{
379-
auto slice = interval{low_, interval_kind::left_slice_upper_bound(other.low_, other.left_border())};
378+
auto slice = interval{
379+
low_,
380+
std::min(other.low_, high_),
381+
left_border_,
382+
other.left_border() == interval_border::open ? interval_border::closed : interval_border::open
383+
};
380384
// >= comparison avoids overflows in case of unsigned integers
381385
if (slice.high_ >= slice.low_ && slice.size() > 0)
382386
slices.left_slice = std::move(slice);
383387
}
384388
// low_ == other.low_ does not produce a left slice in any case.
385-
if (other.high_ > high_)
389+
if (high_ > other.high_)
386390
{
387-
auto slice = interval{interval_kind::right_slice_lower_bound(high_, right_border_), other.high_};
391+
auto slice = interval{
392+
std::max(other.high_, low_),
393+
high_,
394+
right_border_ == interval_border::open ? interval_border::closed : interval_border::open,
395+
other.right_border()
396+
};
388397
// >= comparison avoids overflows in case of unsigned integers
389398
if (slice.high_ >= slice.low_ && slice.size() > 0)
390399
slices.right_slice = std::move(slice);
@@ -1415,12 +1424,13 @@ namespace lib_interval_tree
14151424

14161425
// TODO: private
14171426
/**
1418-
* @brief Finds the first interval that is right of the given value and does not contain it.
1427+
* @brief Finds the interval that is right of the given value and does not contain it.
1428+
* Only works with deoverlapped trees.
14191429
*
14201430
* @param low
14211431
* @return node_type*
14221432
*/
1423-
node_type* find_first_not_right_of_i(value_type search_value) const
1433+
node_type* find_directly_right_of_i(value_type search_value) const
14241434
{
14251435
if (empty())
14261436
return nullptr;
@@ -1458,18 +1468,62 @@ namespace lib_interval_tree
14581468
if (is_interval_strictly_right_of_value(node))
14591469
return node;
14601470

1461-
// We only end up when node == root_, otherwise we never went down the tree to begin with.
1471+
// We only end up here when node == root_, otherwise we never went down the tree to begin with.
14621472
return nullptr;
14631473
}
14641474

1475+
/**
1476+
* @brief Find the interval that is left of the given value or contains it.
1477+
* Only works in deoverlapped trees. Because a deoverlapped tree is indistinguishable
1478+
* from a regular binary search tree. The tree is then also sorted by the upper interval bound.
1479+
* Making this search possible in the first place.
1480+
*
1481+
* @param search_value
1482+
* @return node_type*
1483+
*/
1484+
node_type* find_leftest_interval_of_value_i(value_type search_value) const
1485+
{
1486+
if (empty())
1487+
return nullptr;
1488+
1489+
auto* node = root_;
1490+
1491+
// low of a node is always lower than the lows of all nodes right of that node
1492+
// high of a node is always lower than the lows of all nodes right of that node
1493+
1494+
bool go_left = false;
1495+
bool go_right = false;
1496+
1497+
do
1498+
{
1499+
go_right = search_value > node->high();
1500+
if (go_right)
1501+
{
1502+
go_right &= node->right_ != nullptr;
1503+
if (go_right)
1504+
node = node->right_;
1505+
continue;
1506+
}
1507+
1508+
go_left = node->left_ != nullptr && search_value < node->low();
1509+
if (go_left)
1510+
node = node->left_;
1511+
} while (go_left || go_right);
1512+
1513+
if (search_value < node->low())
1514+
return nullptr;
1515+
1516+
return node;
1517+
}
1518+
14651519
/**
14661520
* Only works with deoverlapped trees.
14671521
* Removes all intervals from the given interval and produces a tree that contains the remaining intervals.
14681522
* This is basically the other punch overload with ival = [tree_lowest, tree_highest]
14691523
*
14701524
* @param ival The range in which to punch out the gaps as a new tree
14711525
*/
1472-
interval_tree punch(interval_type const& ival) const
1526+
interval_tree punch(interval_type ival) const
14731527
{
14741528
interval_tree result;
14751529

@@ -1480,57 +1534,48 @@ namespace lib_interval_tree
14801534
return result;
14811535
}
14821536

1483-
auto* first_not_right = find_first_not_right_of_i(ival.low());
1484-
if (first_not_right == nullptr)
1537+
auto* left_of_or_contains = find_leftest_interval_of_value_i(ival.low());
1538+
1539+
const_iterator iter{nullptr, this};
1540+
if (left_of_or_contains == nullptr)
14851541
{
1486-
// [LAST] [ival]
1487-
// or
1488-
// [LAST]
1489-
// [ival]
1490-
// or
1491-
// [ LAST ]
1492-
// [ival]
1493-
1494-
auto last = crbegin();
1495-
if (!ival.overlaps(*last))
1542+
iter = cbegin();
1543+
// not adjacent or overlapping?
1544+
if (ival.high() < iter->low())
14961545
{
1497-
// [LAST] [ival]
1498-
// ival is fully right of the tree, so just return a tree with this interval:
14991546
result.insert(ival);
15001547
return result;
15011548
}
1502-
1503-
const auto slices = last->extrude(ival);
1504-
if (slices.right_slice)
1505-
result.insert(std::move(slices.right_slice).value());
15061549
}
15071550
else
15081551
{
1509-
// // There is an interval left of or inside ival.
1510-
1511-
// const auto low = [&]() {
1512-
// if (first_not_right->interval()->overlap(ival))
1513-
// {
1514-
// const auto joined = ival.join(*first_not_right->interval()).high();
1515-
// return joined.high() + (joined.within(joined.high()) ? 1 : 0);
1516-
// }
1517-
// else
1518-
// {
1519-
// return ival.low();
1520-
// }
1521-
// }();
1522-
1523-
// auto next = increment({first_not_right});
1524-
1525-
// if (next == end())
1526-
// {
1527-
// value_type high = next->low() - (next->interval()->within(next->low() - 1));
1528-
// }
1529-
// else
1530-
// {
1531-
// // TODO:
1532-
// }
1552+
iter = const_iterator{left_of_or_contains, this};
15331553
}
1554+
1555+
slice_type<interval_type> ex;
1556+
bool insert_remaining = false;
1557+
for (; iter != cend(); ++iter)
1558+
{
1559+
ex = ival.extrude(*iter);
1560+
if (ex.left_slice)
1561+
result.insert(*ex.left_slice);
1562+
1563+
if (ex.right_slice)
1564+
{
1565+
ival = std::move(*ex.right_slice);
1566+
// TODO: Can I avoid assigning this every loop? -> maybe by extracting the first iteration?
1567+
insert_remaining = true;
1568+
}
1569+
else
1570+
{
1571+
insert_remaining = false;
1572+
break;
1573+
}
1574+
}
1575+
1576+
if (insert_remaining && ival.size() > 0)
1577+
result.insert(ival);
1578+
15341579
return result;
15351580
}
15361581

0 commit comments

Comments
 (0)