/*
Ousía
Copyright (C) 2014 Benjamin Paaßen, Andreas Stöckel
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#ifndef _OUSIA_RANGE_SET_HPP_
#define _OUSIA_RANGE_SET_HPP_
#include
#include
namespace ousia {
/**
* The Range structure represents an interval of numerical values of type T.
*/
template
struct Range {
/**
* Start is the start value of the range.
*/
T start;
/**
* End is the end value of the range (inclusively).
*/
T end;
/**
* Default constructor of the range class. The range is initialized as
* invalid, with start being set to the maximum possible value of the
* numerical type T, and end being set to the minimum possible value.
*/
Range()
: start(std::numeric_limits::max()),
end(std::numeric_limits::min())
{
// Do nothing here
}
/**
* Copies the given start and end value. The given values are not checked
* for validity. Use the "isValid"
*
* @param start is the minimum value the range still covers.
* @param end is the maximum value the range still covers.
*/
Range(const T &start, const T &end) : start(start), end(end)
{
// Do nothing here
}
/**
* Creates a range that covers exactly one element, namely the value given
* as parameter n.
*/
Range(const T &n) : start(n), end(n)
{
// Do nothing here
}
/**
* Returns true if this range is valid, e.g. its start value is smaller or
* equal to its end value.
*
* @return true if start is smaller or equal to end, false otherwise.
*/
bool isValid() const { return start <= end; }
/**
* Checks whether the given value lies inside the range.
*
* @param v is the value that is being checked.
* @return true if the value lies within the range, false otherwise.
*/
bool inRange(T v) const { return (v >= start) && (v <= end); }
/**
* Checks whether the given range overlaps with another range. Not that
* this check is only meaningful if both ranges are valid.
*
* @param r is the range that should be checked for overlapping with this
* range.
*/
bool overlaps(const Range &r) const
{
return (((r.start >= start) || (r.end >= start)) &&
((r.start <= end) || (r.end <= end)));
}
/**
* Returns true if the two given ranges are neighbours (their limits only
* differ in the smallest representable difference between them).
*/
bool neighbours(const Range &r) const
{
constexpr T eps = std::numeric_limits::is_integer
? 1
: std::numeric_limits::epsilon();
return ((r.start > end) && ((r.start - eps) <= end)) ||
((r.end < start) && ((r.end + eps) >= start));
}
/**
* Checks whether the given range completely covers this range.
*/
bool coveredBy(const Range &r) const
{
return (r.start <= start) && (r.end >= end);
}
/**
* Checks whether this range completely covers the given range.
*/
bool covers(const Range &r) const { return r.coveredBy(*this); }
/**
* Calculates the union of the two ranges -- note that this operation is
* only valid if the ranges overlapp. Use the RangeSet class if you cannot
* guarantee that.
*/
Range merge(const Range &r) const
{
return Range(std::min(start, r.start), std::max(end, r.end));
}
/**
* Returns a range that represents the spans the complete set defined by the
* given type T.
*/
static Range typeRange()
{
return Range(std::numeric_limits::min(),
std::numeric_limits::max());
}
/**
* Returns a range that represents the spans the complete set defined by the
* given type T up to a given value.
*
* @param till is the value up to which the range should be defined (till is
* included in the set).
*/
static Range typeRangeUntil(const T &till)
{
return Range(std::numeric_limits::min(), till);
}
/**
* Returns a range that represents the spans the complete set defined by the
* given type T up to a given value.
*
* @param from is the value from which the range should be defined (from is
* included in the set).
*/
static Range typeRangeFrom(const T &from)
{
return Range(from, std::numeric_limits::max());
}
};
/**
* RangeComp is a comperator used to order to sort the ranges within the
* ranges list. Sorts by the start element.
*/
template
struct RangeComp {
bool operator()(const Range &lhs, const Range &rhs) const
{
return lhs.start < rhs.start;
}
};
/**
* RangeSet represents a set of ranges of the given numerical type and is thus
* capable of representing any possible subset of the given numerical type T.
*/
template
class RangeSet {
protected:
/**
* Set of ranges used internally.
*/
std::set, RangeComp> ranges;
/**
* Returns an iterator to the first element in the ranges list that overlaps
* with the given range.
*
* @param r is the range for which the first overlapping element should be
* found.
* @return an iterator pointing to the first overlapping element or to the
* end of the list if no such element was found.
*/
typename std::set, RangeComp>::iterator firstOverlapping(
const Range &r, const bool allowNeighbours)
{
// Find the element with the next larger start value compared to the
// start value given in r.
auto it = ranges.upper_bound(r);
// Go back one element
if (it != ranges.begin()) {
it--;
}
// Iterate until an overlapping element is found
while ((it != ranges.end()) &&
!(it->overlaps(r) || (allowNeighbours && it->neighbours(r)))) {
it++;
}
return it;
}
public:
/**
* Calculates the union of this range set and the given range.
*
* @param range is the range that should be merged into this range set.
*/
void merge(Range r)
{
// Calculate a new range that covers both the new range and all old
// ranges in the set -- delete all old elements on the way
auto it = firstOverlapping(r, true);
while ((it != ranges.end()) && (it->overlaps(r) || it->neighbours(r))) {
r = r.merge(*it);
it = ranges.erase(it);
}
// Insert the new range
ranges.insert(r);
}
/**
* Calculates the union of this range set and the given range set.
*
* @param ranges is another range set for which the union with this set
* should be calculated.
*/
void merge(const RangeSet &s)
{
for (Range &r : s.ranges) {
merge(r);
}
}
/**
* Checks whether this range set S contains the given range R:
* S u R = R
* (The intersection between R and S equals the given range)
*
* @param r is the range for which the containment should be checked.
* @return true if the above condition is met, false otherwise.
*/
bool contains(const Range &r)
{
auto it = firstOverlapping(r, false);
if (it != ranges.end()) {
return (*it).covers(r);
}
return false;
}
/**
* Checks whether this range set S1 contains the given range set S2:
*
* @param s is the range for which the containment should be checked.
* @return true if the above condition is met, false otherwise.
*/
bool contains(const RangeSet &s)
{
bool res = true;
for (Range &r : s.ranges) {
res = res && contains(r);
}
return res;
}
/**
* Empties the set.
*/
void clear() { ranges.clear(); }
/**
* Returns the current list of ranges as a const reference.
*/
const std::set, RangeComp> &getRanges() { return this->ranges; }
};
}
#endif /* _OUSIA_RANGE_SET_HPP_ */