Skip to content

Commit a1a8701

Browse files
committed
WIP: punch feature
1 parent e92ec94 commit a1a8701

File tree

4 files changed

+211
-19
lines changed

4 files changed

+211
-19
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ __history/
88
*.cbp
99
*.png
1010
.vscode
11+
.clangd
1112
build
1213

1314
tests/bin

include/interval-tree/interval_tree.hpp

Lines changed: 147 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1347,35 +1347,164 @@ namespace lib_interval_tree
13471347
return punch({min, max});
13481348
}
13491349

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

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

1365-
for (auto e = end(); i != e; ++i)
1410+
if (empty())
13661411
{
1367-
auto next = i;
1368-
++next;
1369-
if (next != e)
1370-
result.insert({i->interval()->high(), next->interval()->low()});
1371-
else
1372-
break;
1412+
// Nothing to punch, so return the whole interval
1413+
result.insert(ival);
1414+
return result;
13731415
}
13741416

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

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

13811510
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)