Handle zero-width intervals correctly

Fixes #101
Fixes #165
Closes #164
This commit is contained in:
Janik Rabe 2018-08-23 10:08:10 +02:00 committed by lauft
parent 0273987ae6
commit 4325ffa136
5 changed files with 74 additions and 15 deletions

View file

@ -380,7 +380,10 @@ std::vector <Range> Database::segmentRange (const Range& range)
// Capture date after incrementing month.
Datetime segmentEnd (start_y, start_m, 1);
segments.push_back (Range (segmentStart, segmentEnd));
auto segment = Range (segmentStart, segmentEnd);
if (range.intersects (segment)) {
segments.push_back (segment);
}
}
return segments;

View file

@ -90,7 +90,7 @@ std::vector <std::string> Datafile::allLines ()
void Datafile::addInterval (const Interval& interval)
{
// Note: end date might be zero.
assert (_range.overlap (interval.range));
assert (_range.segmentContains (interval.range));
if (! _lines_loaded)
load_lines ();
@ -108,7 +108,7 @@ void Datafile::addInterval (const Interval& interval)
void Datafile::deleteInterval (const Interval& interval)
{
// Note: end date might be zero.
assert (_range.overlap (interval.range));
assert (_range.segmentContains (interval.range));
if (! _lines_loaded)
load_lines ();

View file

@ -179,6 +179,41 @@ bool Range::encloses (const Range& other) const
return false;
}
////////////////////////////////////////////////////////////////////////////////
// This is a standard enclosure check, except that the other range
// need not have an end time, even if this one does.
//
// Detect the following enclosure cases:
//
// this [--------)
// A [----)
// B [...
//
// this [...
// A [----)
// B [--------)
// C [--------)
// D [...
// E [...
//
bool Range::segmentContains (const Range& other) const
{
if (is_started ()) {
if (is_ended ()) {
if (other.is_started () && other.start >= start) {
return ! other.is_ended () || other.end <= end;
}
} else {
if (other.is_started () && other.start >= start) {
return true;
}
}
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
// Calculate the following intersection cases:
//
@ -216,7 +251,7 @@ Range Range::intersect (const Range& other) const
if (is_ended ())
{
if (other.is_ended ())
result.end = end < other.end ? end : other.end;
result.end = end < other.end ? end : other.end;
else
result.end = end;
}
@ -229,9 +264,28 @@ Range Range::intersect (const Range& other) const
return result;
}
// If there is an intersection but no overlap, we have a zero-width
// interval [p, p) and another interval [p, q), where q >= p.
if (intersects (other)) {
return Range {start, start};
}
return Range {};
}
////////////////////////////////////////////////////////////////////////////////
bool Range::intersects (const Range &other) const
{
if (overlap (other)) {
return true;
}
// A half-closed zero-width interval [p, p) may have the same
// starting point as another interval without overlapping it.
// We consider p to be an element of a range [p, p).
return (is_started () && other.is_started () && start == other.start);
}
////////////////////////////////////////////////////////////////////////////////
// If the ranges do not overlap, the result is *this.
//

View file

@ -51,7 +51,9 @@ public:
bool overlap (const Range&) const;
bool encloses (const Range&) const;
bool segmentContains (const Range&) const;
Range intersect (const Range&) const;
bool intersects (const Range&) const;
Range combine (const Range&) const;
std::vector <Range> subtract (const Range&) const;
time_t total () const;

View file

@ -343,9 +343,11 @@ std::vector <Range> subset (
const std::vector <Range>& ranges)
{
std::vector <Range> all;
for (auto& r : ranges)
if (range.overlap (r))
for (auto& r : ranges) {
if (range.intersects (r)) {
all.push_back (r);
}
}
return all;
}
@ -356,9 +358,11 @@ std::vector <Interval> subset (
const std::vector <Interval>& intervals)
{
std::vector <Interval> all;
for (auto& interval : intervals)
if (range.overlap (interval.range))
for (auto& interval : intervals) {
if (range.intersects (interval.range)) {
all.push_back (interval);
}
}
return all;
}
@ -549,12 +553,8 @@ Range outerRange (const std::vector <Interval>& intervals)
bool matchesFilter (const Interval& interval, const Interval& filter)
{
if ((filter.range.start.toEpoch () == 0 &&
filter.range.end.toEpoch () == 0)
||
((interval.range.end.toEpoch () == 0 || interval.range.end > filter.range.start) &&
(filter.range.end.toEpoch () == 0 || interval.range.start < filter.range.end)))
filter.range.end.toEpoch () == 0
) || interval.range.intersects (filter.range))
{
for (auto& tag : filter.tags ())
if (! interval.hasTag (tag))
@ -608,7 +608,7 @@ std::vector <Interval> getTracked (
if (! inclusions.empty ()) {
auto latest = inclusions.back();
if (latest.range.is_open()) {;
if (latest.range.is_open()) {
filter.range.end = 0;
}
}