range: Add deoverlap function

This patch adds the deoverlap function to range.hh, which takes in a
vector of possibly overlapping ranges and returns a vector of
non-overlapping ranges covering the same values.

Signed-off-by: Duarte Nunes <duarte@scylladb.com>
This commit is contained in:
Duarte Nunes
2016-07-07 12:00:23 +02:00
parent c910a4639c
commit 9792a77266
2 changed files with 145 additions and 0 deletions

View File

@@ -24,6 +24,7 @@
#include <experimental/optional>
#include <iostream>
#include <boost/range/algorithm/copy.hpp>
#include <boost/range/adaptor/sliced.hpp>
template<typename T>
class range_bound {
@@ -94,6 +95,12 @@ private:
<= -(!first.b->is_inclusive() && second.b->is_inclusive()));
}
template<typename Comparator>
static bool less_than(start_bound_ref first, start_bound_ref second, Comparator&& cmp) {
return second.b && (!first.b || cmp(first.b->value(), second.b->value())
< (first.b->is_inclusive() && !second.b->is_inclusive()));
}
template<typename Comparator>
static bool greater_than_or_equal(end_bound_ref first, end_bound_ref second, Comparator&& cmp) {
return !first.b || (second.b && cmp(first.b->value(), second.b->value())
@@ -353,6 +360,51 @@ public:
return (_start == other._start) && (_end == other._end) && (_singular == other._singular);
}
// Takes a vector of possibly overlapping ranges and returns a vector containing
// a set of non-overlapping ranges covering the same values.
template<typename Comparator>
static std::vector<range<T>> deoverlap(std::vector<range<T>> ranges, Comparator&& cmp) {
auto size = ranges.size();
if (size <= 1) {
return ranges;
}
for (auto it = ranges.begin(); it != ranges.end(); ++it) {
if ((*it).is_wrap_around(cmp)) {
auto&& unwrapped = it->unwrap();
it = ranges.erase(it);
it = ranges.emplace(it, std::move(unwrapped.first));
it = ranges.emplace(it, std::move(unwrapped.second));
}
}
std::sort(ranges.begin(), ranges.end(), [&](auto&& r1, auto&& r2) {
return range<T>::less_than(r1.start_bound(), r2.start_bound(), cmp);
});
std::vector<range<T>> deoverlapped_ranges;
deoverlapped_ranges.reserve(size);
auto&& current = ranges[0];
for (auto&& r : ranges | boost::adaptors::sliced(1, ranges.size())) {
bool includes_end = greater_than_or_equal(r.end_bound(), current.start_bound(), cmp)
&& greater_than_or_equal(current.end_bound(), r.end_bound(), cmp);
if (includes_end) {
continue; // last.start <= r.start <= r.end <= last.end
}
bool includes_start = greater_than_or_equal(current.end_bound(), r.start_bound(), cmp);
if (includes_start) {
current = range<T>(std::move(current.start()), std::move(r.end()));
} else {
deoverlapped_ranges.emplace_back(std::move(current));
current = std::move(r);
}
}
deoverlapped_ranges.emplace_back(std::move(current));
return deoverlapped_ranges;
}
template<typename U>
friend std::ostream& operator<<(std::ostream& out, const range<U>& r);
};

View File

@@ -505,3 +505,96 @@ BOOST_AUTO_TEST_CASE(test_range_interval_map) {
BOOST_REQUIRE(search_item("8") == true);
BOOST_REQUIRE(search_item("9") == false);
}
BOOST_AUTO_TEST_CASE(range_deoverlap_tests) {
using ranges = std::vector<range<unsigned>>;
{
ranges rs = { range<unsigned>::make(1, 4), range<unsigned>::make(2, 5) };
auto deoverlapped = range<unsigned>::deoverlap(std::move(rs), unsigned_comparator());
BOOST_REQUIRE_EQUAL(range<unsigned>::make(1, 5), deoverlapped[0]);
BOOST_REQUIRE_EQUAL(1, deoverlapped.size());
}
{
ranges rs = { range<unsigned>::make(1, 4), range<unsigned>::make(4, 5) };
auto deoverlapped = range<unsigned>::deoverlap(std::move(rs), unsigned_comparator());
BOOST_REQUIRE_EQUAL(range<unsigned>::make(1, 5), deoverlapped[0]);
BOOST_REQUIRE_EQUAL(1, deoverlapped.size());
}
{
ranges rs = { range<unsigned>::make(2, 4), range<unsigned>::make(1, 3) };
auto deoverlapped = range<unsigned>::deoverlap(std::move(rs), unsigned_comparator());
BOOST_REQUIRE_EQUAL(range<unsigned>::make(1, 4), deoverlapped[0]);
BOOST_REQUIRE_EQUAL(1, deoverlapped.size());
}
{
ranges rs = { range<unsigned>::make(1, 4), range<unsigned>::make(0, 5), range<unsigned>::make(7, 12), range<unsigned>::make(8, 10) };
auto deoverlapped = range<unsigned>::deoverlap(std::move(rs), unsigned_comparator());
BOOST_REQUIRE_EQUAL(range<unsigned>::make(0, 5), deoverlapped[0]);
BOOST_REQUIRE_EQUAL(range<unsigned>::make(7, 12), deoverlapped[1]);
BOOST_REQUIRE_EQUAL(2, deoverlapped.size());
}
{
ranges rs = { range<unsigned>::make(1, 4), range<unsigned>({2}, { }) };
auto deoverlapped = range<unsigned>::deoverlap(std::move(rs), unsigned_comparator());
BOOST_REQUIRE_EQUAL(range<unsigned>({1}, { }), deoverlapped[0]);
BOOST_REQUIRE_EQUAL(1, deoverlapped.size());
}
{
ranges rs = { range<unsigned>({ }, {4}), range<unsigned>::make(3, 5) };
auto deoverlapped = range<unsigned>::deoverlap(std::move(rs), unsigned_comparator());
BOOST_REQUIRE_EQUAL(range<unsigned>({ }, {5}), deoverlapped[0]);
BOOST_REQUIRE_EQUAL(1, deoverlapped.size());
}
{
ranges rs = { range<unsigned>({14}, { }), range<unsigned>({2}, { }) };
auto deoverlapped = range<unsigned>::deoverlap(std::move(rs), unsigned_comparator());
BOOST_REQUIRE_EQUAL(range<unsigned>({2}, { }), deoverlapped[0]);
BOOST_REQUIRE_EQUAL(1, deoverlapped.size());
}
{
ranges rs = { range<unsigned>({2}, { }), range<unsigned>({12}, { }) };
auto deoverlapped = range<unsigned>::deoverlap(std::move(rs), unsigned_comparator());
BOOST_REQUIRE_EQUAL(range<unsigned>({2}, { }), deoverlapped[0]);
BOOST_REQUIRE_EQUAL(1, deoverlapped.size());
}
{
ranges rs = { range<unsigned>::make(14, 4), range<unsigned>::make(2, 4) };
auto deoverlapped = range<unsigned>::deoverlap(std::move(rs), unsigned_comparator());
BOOST_REQUIRE_EQUAL(range<unsigned>({ }, {4}), deoverlapped[0]);
BOOST_REQUIRE_EQUAL(range<unsigned>({14}, { }), deoverlapped[1]);
BOOST_REQUIRE_EQUAL(2, deoverlapped.size());
}
{
ranges rs = { range<unsigned>({3}, {{4, false}}), range<unsigned>({{4, false}}, {5}) };
auto deoverlapped = range<unsigned>::deoverlap(std::move(rs), unsigned_comparator());
BOOST_REQUIRE_EQUAL(range<unsigned>({3}, {{4, false}}), deoverlapped[0]);
BOOST_REQUIRE_EQUAL(range<unsigned>({{4, false}}, {5}), deoverlapped[1]);
BOOST_REQUIRE_EQUAL(2, deoverlapped.size());
}
{
ranges rs = { range<unsigned>({3}, {{4, false}}), range<unsigned>::make(4, 5) };
auto deoverlapped = range<unsigned>::deoverlap(std::move(rs), unsigned_comparator());
BOOST_REQUIRE_EQUAL(range<unsigned>({3}, {{4, false}}), deoverlapped[0]);
BOOST_REQUIRE_EQUAL(range<unsigned>::make(4, 5), deoverlapped[1]);
BOOST_REQUIRE_EQUAL(2, deoverlapped.size());
}
{
ranges rs = { range<unsigned>::make(3, 4), range<unsigned>({{4, false}}, {5}) };
auto deoverlapped = range<unsigned>::deoverlap(std::move(rs), unsigned_comparator());
BOOST_REQUIRE_EQUAL(range<unsigned>::make(3, 4), deoverlapped[0]);
BOOST_REQUIRE_EQUAL(range<unsigned>({{4, false}}, {5}), deoverlapped[1]);
BOOST_REQUIRE_EQUAL(2, deoverlapped.size());
}
}