From c587bb7c9f084b2c1ea25141875e84d3daef3a53 Mon Sep 17 00:00:00 2001 From: rbellens Date: Mon, 11 Dec 2017 12:02:08 +0100 Subject: [PATCH 1/5] use test package iso unittest --- pubspec.yaml | 2 +- test/interval_test.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pubspec.yaml b/pubspec.yaml index c0d061e..a85902c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -4,4 +4,4 @@ author: Sean Eagan description: Provdes an Interval class, a contiguous set of values. homepage: https://github.com/seaneagan/interval dev_dependencies: - unittest: any + test: any diff --git a/test/interval_test.dart b/test/interval_test.dart index c70cd4b..db89918 100644 --- a/test/interval_test.dart +++ b/test/interval_test.dart @@ -2,7 +2,7 @@ library interval.test; import 'package:interval/interval.dart'; -import 'package:unittest/unittest.dart'; +import 'package:test/test.dart'; main() { group('Interval', () { From fa354a15952f0c59ae4717e03899e0293563f754 Mon Sep 17 00:00:00 2001 From: rbellens Date: Mon, 11 Dec 2017 12:11:00 +0100 Subject: [PATCH 2/5] add intersectAll constructor --- lib/interval.dart | 49 +++++++++++++++++++++++++++++++++++++++++ test/interval_test.dart | 44 ++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+) diff --git a/lib/interval.dart b/lib/interval.dart index 6795478..647f29f 100644 --- a/lib/interval.dart +++ b/lib/interval.dart @@ -221,6 +221,55 @@ class Interval> { upperClosed: upperClosed); } + /// The maximal interval which is enclosed in each interval in [intervals]. + /// + /// If [intervals] is empty or they do not overlap, `null` is returned. + factory Interval.intersectAll(Iterable> intervals) { + + var iterator = intervals.iterator; + if (!iterator.moveNext()) return null; + var interval = iterator.current; + var lower = interval.lower; + var upper = interval.upper; + var lowerClosed = interval.lowerClosed; + var upperClosed = interval.upperClosed; + while (iterator.moveNext()) { + interval = iterator.current; + if (lower == null) { + lower = interval.lower;; + lowerClosed = interval.lowerClosed; + } else { + if (interval.lower != null) { + var cmp = Comparable.compare(lower, interval.lower); + if (cmp<=0) { + lower = interval.lower; + lowerClosed = (cmp!=0||lowerClosed) && interval.lowerClosed; + } + } + } + if (upper == null) { + upper = interval.upper; + upperClosed = interval.upperClosed; + } else { + if (interval.upper != null) { + var cmp = Comparable.compare(upper, interval.upper); + if (cmp>=0) { + upper = interval.upper; + upperClosed = (cmp!=0||upperClosed) && interval.upperClosed; + } + } + } + } + var cmp = Comparable.compare(lower, upper); + if (cmp>0) return null; + if (cmp==0&&(!upperClosed||!lowerClosed)) return null; + return new Interval( + lower: lower, + upper: upper, + lowerClosed: lowerClosed, + upperClosed: upperClosed); + } + /// Whether `this` contains [test]. bool contains(T test) { if (lower != null) { diff --git a/test/interval_test.dart b/test/interval_test.dart index db89918..9d7ae1a 100644 --- a/test/interval_test.dart +++ b/test/interval_test.dart @@ -111,6 +111,50 @@ main() { }); + group('intersectAll', () { + + test('should return null if iterable is empty', () { + var interval = new Interval.intersectAll([]); + expect(interval, null); + }); + + test('should return null when input interval do not overlap', () { + var interval = new Interval.intersectAll([ + new Interval.atMost(0), + new Interval.atLeast(1)]); + expect(interval, null); + }); + + test('should have bounds matching extreme input interval bounds', () { + var interval = new Interval.intersectAll([ + new Interval.closed(0, 3), + new Interval.closed(-1, 1), + new Interval.closed(-8, 10), + new Interval.closed(-5, 7)]); + expect(interval.lower, 0); + expect(interval.upper, 1); + }); + + test('should have open bound when any corresponding extreme input ' + 'interval bound does', () { + var interval = new Interval.intersectAll([ + new Interval.closedOpen(0, 1), + new Interval.openClosed(0, 1)]); + expect(interval.lowerClosed, isFalse); + expect(interval.upperClosed, isFalse); + }); + + test('should have closed bound when all extreme input interval bounds ' + 'do', () { + var interval = new Interval.intersectAll([ + new Interval.closed(0, 1), + new Interval.closed(0, 1)]); + expect(interval.lowerClosed, isTrue); + expect(interval.upperClosed, isTrue); + }); + + }); + }); group('contains', () { From 8c9fe1947f350e766215e8f4f7d3ad5d2b949335 Mon Sep 17 00:00:00 2001 From: rbellens Date: Mon, 11 Dec 2017 12:12:11 +0100 Subject: [PATCH 3/5] add intersect and enclose operation --- lib/interval.dart | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/interval.dart b/lib/interval.dart index 647f29f..d35a107 100644 --- a/lib/interval.dart +++ b/lib/interval.dart @@ -270,6 +270,16 @@ class Interval> { upperClosed: upperClosed); } + /// Returns the intersection of `this` interval with [other]. + /// + /// If the intervals do not intersect, `null` is returned. + Interval intersect(Interval other) => new Interval.intersectAll([this,other]); + + /// Returns minimal interval that [encloses] both `this` and [other]. + Interval enclose(Interval other) => new Interval.encloseAll([this,other]); + + + /// Whether `this` contains [test]. bool contains(T test) { if (lower != null) { From de205686be7f1d486912efeda9274bb9de059c9e Mon Sep 17 00:00:00 2001 From: rbellens Date: Mon, 11 Dec 2017 12:14:00 +0100 Subject: [PATCH 4/5] add intersects test --- lib/interval.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/interval.dart b/lib/interval.dart index d35a107..c5fa4da 100644 --- a/lib/interval.dart +++ b/lib/interval.dart @@ -320,6 +320,9 @@ class Interval> { return true; } + /// Whether the intersection of `this` and [other] is not empty. + bool intersects(Interval other) => intersect(other)!=null; + /// Whether the union of `this` and [other] is connected (i.e. is an /// [Interval]). bool connectedTo(Interval other) { From bc235a493f67f45a3dd8cd867be25cf974c06805 Mon Sep 17 00:00:00 2001 From: rbellens Date: Mon, 11 Dec 2017 13:01:12 +0100 Subject: [PATCH 5/5] return all iso null when intervals is empty on intersectAll --- lib/interval.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/interval.dart b/lib/interval.dart index c5fa4da..e7279ba 100644 --- a/lib/interval.dart +++ b/lib/interval.dart @@ -223,11 +223,13 @@ class Interval> { /// The maximal interval which is enclosed in each interval in [intervals]. /// - /// If [intervals] is empty or they do not overlap, `null` is returned. + /// If [intervals] is empty, the returned interval contains all values. + /// + /// If [intervals] do not overlap, `null` is returned. factory Interval.intersectAll(Iterable> intervals) { var iterator = intervals.iterator; - if (!iterator.moveNext()) return null; + if (!iterator.moveNext()) return new Interval.all(); var interval = iterator.current; var lower = interval.lower; var upper = interval.upper;