Skip to content

Commit 0524d8c

Browse files
committed
WIP: punch feature
1 parent 12bf0d5 commit 0524d8c

File tree

3 files changed

+210
-19
lines changed

3 files changed

+210
-19
lines changed

include/interval-tree/interval_tree.hpp

Lines changed: 147 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1343,35 +1343,164 @@ namespace lib_interval_tree
13431343
return punch({min, max});
13441344
}
13451345

1346+
// TODO: private
13461347
/**
1347-
* Only works with deoverlapped trees.
1348-
* Removes all intervals from the given interval and produces a tree that contains the remaining intervals.
1349-
* This is basically the other punch overload with ival = [tree_lowest, tree_highest]
1348+
* @brief Finds the first interval that is right of the given value and does not contain it.
1349+
*
1350+
* @param low
1351+
* @return node_type*
13501352
*/
1351-
interval_tree punch(interval_type const& ival) const
1353+
node_type* find_first_not_right_of_i(value_type search_value) const
13521354
{
13531355
if (empty())
1354-
return {};
1356+
return nullptr;
1357+
1358+
// There can be no interval strictly right of the value, if the value
1359+
// is larger than the max.
1360+
if (search_value > root_->max_)
1361+
return nullptr;
1362+
1363+
const auto is_interval_strictly_right_of_value = [search_value](node_type* node) {
1364+
return node->low() > search_value ||
1365+
(node->low() == search_value && !node->interval()->within(search_value));
1366+
};
1367+
1368+
auto* node = root_;
1369+
1370+
// If the interval is not strictly right of the value, we can only go down right
1371+
// And dont have to check left.
1372+
while (!is_interval_strictly_right_of_value(node) && node->right_)
1373+
node = node->right_;
1374+
1375+
bool go_left = false;
1376+
bool go_right = false;
1377+
do
1378+
{
1379+
go_left = node->left_ && is_interval_strictly_right_of_value(node->left_);
1380+
go_right = node->right_ && is_interval_strictly_right_of_value(node->right_);
1381+
1382+
if (go_left)
1383+
node = node->left_;
1384+
else if (go_right)
1385+
node = node->right_;
1386+
} while (go_left || go_right);
1387+
1388+
if (is_interval_strictly_right_of_value(node))
1389+
return node;
1390+
1391+
// We only end up when node == root_, otherwise we never went down the tree to begin with.
1392+
return nullptr;
1393+
}
13551394

1395+
/**
1396+
* Only works with deoverlapped trees.
1397+
* Removes all intervals from the given interval and produces a tree that contains the remaining intervals.
1398+
* This is basically the other punch overload with ival = [tree_lowest, tree_highest]
1399+
*
1400+
* @param ival The range in which to punch out the gaps as a new tree
1401+
*/
1402+
interval_tree punch(interval_type const& ival) const
1403+
{
13561404
interval_tree result;
1357-
auto i = std::begin(*this);
1358-
if (ival.low() < i->interval()->low())
1359-
result.insert({ival.low(), i->interval()->low()});
13601405

1361-
for (auto e = end(); i != e; ++i)
1406+
if (empty())
13621407
{
1363-
auto next = i;
1364-
++next;
1365-
if (next != e)
1366-
result.insert({i->interval()->high(), next->interval()->low()});
1367-
else
1368-
break;
1408+
// Nothing to punch, so return the whole interval
1409+
result.insert(ival);
1410+
return result;
13691411
}
13701412

1371-
if (i != end() && i->interval()->high() < ival.high())
1372-
result.insert({i->interval()->high(), ival.high()});
1413+
// These two helper functions help to offset the adjacent interval edge depending on the interval type.
13731414

1374-
return result;
1415+
const auto low_with_offset_1 = [](interval_type const& interval) {
1416+
return interval.low() + (interval.within(interval.low()) ? 1 : 0);
1417+
};
1418+
const auto low_with_offset_minus_1 = [](interval_type const& interval) {
1419+
INTERVAL_TREE_CONSTEXPR_IF(std::is_unsigned<value_type>::value)
1420+
{
1421+
return static_cast<value_type>(
1422+
static_cast<std::make_signed<value_type>>(interval.low()) -
1423+
(interval.within(interval.low()) ? 1 : 0)
1424+
);
1425+
}
1426+
return interval.low() - (interval.within(interval.low()) ? 1 : 0);
1427+
};
1428+
const auto high_with_offset_1 = [](interval_type const& interval) {
1429+
return interval.high() + (interval.within(interval.high()) ? 1 : 0);
1430+
};
1431+
const auto high_with_offset_minus_1 = [](interval_type const& interval) {
1432+
INTERVAL_TREE_CONSTEXPR_IF(std::is_unsigned<value_type>::value)
1433+
{
1434+
return static_cast<value_type>(
1435+
static_cast<std::make_signed<value_type>>(interval.high()) -
1436+
(interval.within(interval.high()) ? 1 : 0)
1437+
);
1438+
}
1439+
return interval.high() - (interval.within(interval.high()) ? 1 : 0);
1440+
};
1441+
const auto is_empty_interval = [](interval_type const& interval) {
1442+
return !interval.within(interval.low()) && !interval.within(interval.high());
1443+
};
1444+
const auto insert_if_not_empty = [&](value_type left, value_type right) {
1445+
if (left <= right)
1446+
{
1447+
const auto interval = interval_type{left, right};
1448+
if (!is_empty_interval(interval))
1449+
result.insert(interval);
1450+
}
1451+
};
1452+
1453+
auto* first_not_right = find_first_not_right_of_i(ival.low);
1454+
if (first_not_right == nullptr)
1455+
{
1456+
// There is no interval not fully right of the interval. So ival is either fully right of the rest of
1457+
// the tree or the last interval overlaps ival.
1458+
1459+
auto last = crbegin();
1460+
if (!ival.overlaps(*crbegin()))
1461+
{
1462+
// ival is fully right of the tree, so just return a tree with this interval:
1463+
result.insert(ival);
1464+
return result;
1465+
}
1466+
1467+
if (std::max(ival.high(), last->high()) == last.high())
1468+
{
1469+
// The slice is not going beyond the last interval:
1470+
return {};
1471+
}
1472+
1473+
// Slice off the part overlapping over the end of last:
1474+
// TODO:
1475+
// if ()
1476+
}
1477+
else
1478+
{
1479+
// There is an interval left of or inside ival.
1480+
1481+
const auto low = [&]() {
1482+
if (first_not_right->interval()->overlap(ival))
1483+
{
1484+
const auto joined = ival.join(*first_not_right->interval()).high();
1485+
return joined.high() + (joined.within(joined.high()) ? 1 : 0);
1486+
}
1487+
else
1488+
{
1489+
return ival.low();
1490+
}
1491+
}();
1492+
1493+
auto next = increment({first_not_right});
1494+
1495+
if (next == end())
1496+
{
1497+
value_type high = next->low() - (next->interval()->within(next->low() - 1));
1498+
}
1499+
else
1500+
{
1501+
// TODO:
1502+
}
1503+
}
13751504
}
13761505

13771506
iterator begin()

tests/custom_interval_tests.hpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,22 @@ struct custom_interval : public lib_interval_tree::interval<numerical_type, inte
5151
return on_join(other);
5252
return {std::min(low_, other.low_), std::max(high_, other.high_)};
5353
}
54+
55+
/**
56+
* Returns whether the given value is in this.
57+
*/
58+
bool within(value_type value) const
59+
{
60+
return interval_kind::within(low_, high_, value);
61+
}
62+
63+
/**
64+
* Returns whether the given interval is in this.
65+
*/
66+
bool within(custom_interval const& other) const
67+
{
68+
return within(other.low_) && within(other.high_);
69+
}
5470
};
5571

5672
struct minimal_custom_interval : public lib_interval_tree::interval<int, lib_interval_tree::closed>

tests/interval_tree_tests.hpp

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,4 +108,50 @@ TEST_F(IntervalTreeTests, CanMoveBiggerTree)
108108
{
109109
EXPECT_EQ(i->low(), i->high() - 5);
110110
}
111-
}
111+
}
112+
113+
TEST_F(IntervalTreeTests, PunchFullyRightOfTree)
114+
{
115+
// TODO:
116+
}
117+
118+
TEST_F(IntervalTreeTests, PunchOverlapsRightOfTree)
119+
{
120+
// TODO:
121+
}
122+
123+
TEST_F(IntervalTreeTests, PunchFullyLeftOfTree)
124+
{
125+
// TODO:
126+
}
127+
128+
TEST_F(IntervalTreeTests, PunchOverlapsLeftOfTree)
129+
{
130+
// TODO:
131+
}
132+
133+
TEST_F(IntervalTreeTests, PunchEncompassesTree)
134+
{
135+
// TODO:
136+
}
137+
138+
TEST_F(IntervalTreeTests, PunchWithOpenInterval)
139+
{}
140+
141+
TEST_F(IntervalTreeTests, PunchWithClosedInterval)
142+
{}
143+
144+
TEST_F(IntervalTreeTests, PunchWithLeftOpenInterval)
145+
{}
146+
147+
TEST_F(IntervalTreeTests, PunchWithRightOpenInterval)
148+
{}
149+
150+
TEST_F(IntervalTreeTests, PunchWithClosedAdjacentInterval)
151+
{}
152+
153+
TEST_F(IntervalTreeTests, OverlapFreeSubtreePunch)
154+
{}
155+
156+
TEST_F(IntervalTreeTests, OverlappingSubtreePunch)
157+
{}

0 commit comments

Comments
 (0)