From 617d1e1398a3bae6f757b96cff3b4abbfb2e102c Mon Sep 17 00:00:00 2001 From: Fernando Chorney Date: Tue, 26 May 2020 09:30:00 -0500 Subject: [PATCH 01/12] Try using Infinity --- Project.toml | 4 +++- src/Intervals.jl | 1 + test/interval.jl | 17 +++++++++++++++-- test/runtests.jl | 13 ++++++------- 4 files changed, 25 insertions(+), 10 deletions(-) diff --git a/Project.toml b/Project.toml index 199673af..70b00afc 100644 --- a/Project.toml +++ b/Project.toml @@ -2,15 +2,17 @@ name = "Intervals" uuid = "d8418881-c3e1-53bb-8760-2df7ec849ed5" license = "MIT" authors = ["Invenia Technical Computing"] -version = "1.1.0" +version = "1.2.0" [deps] Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" +Infinity = "a303e19e-6eb4-11e9-3b09-cd9505f79100" Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" TimeZones = "f269a46b-ccf7-5d73-abea-4c690281aa53" [compat] +Infinity = "0.1" RecipesBase = "0.8, 1" TimeZones = "0.7, 0.8, 0.9, 0.10, 0.11, 1" julia = "1" diff --git a/src/Intervals.jl b/src/Intervals.jl index 32d863ee..171f5566 100644 --- a/src/Intervals.jl +++ b/src/Intervals.jl @@ -1,6 +1,7 @@ module Intervals using Dates +using Infinity using Printf using RecipesBase using TimeZones diff --git a/test/interval.jl b/test/interval.jl index b8fd9bc8..a785374b 100644 --- a/test/interval.jl +++ b/test/interval.jl @@ -1,10 +1,23 @@ @testset "Interval" begin test_values = [ (-10, 1000, 1), - (0.0, 1, 0.01), # Use different types to test promotion + # Use different types to test promotion + (0.0, 1, 0.01), ('a', 'z', 1), (Date(2013, 2, 13), Date(2013, 3, 13), Day(1)), - (DateTime(2016, 8, 11, 0, 30), DateTime(2016, 8, 11, 1), Millisecond(1)) + (DateTime(2016, 8, 11, 0, 30), DateTime(2016, 8, 11, 1), Millisecond(1)), + # Test unbounded endpoints + (-∞, ∞, 1), + (-∞, 10, 1), + (10, ∞, 1), + (-∞, 1, 0.01), + (0.0, ∞, 0.01), + (-∞, 'z', 1), + ('a', ∞, 1), + (-∞, Date(2013, 3, 13), Day(1)), + (Date(2013, 2, 13), ∞, Day(1)), + (-∞, DateTime(2016, 8, 11, 1), Millisecond(1)), + (DateTime(2016, 8, 11, 0, 30), ∞, Millisecond(1)), ] @testset "constructor" begin diff --git a/test/runtests.jl b/test/runtests.jl index 3c46dfd4..9f2f0cb9 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,17 +1,16 @@ using Dates using Plots +using Infinity using Intervals using TimeZones using Test using VisualRegressionTests - - @testset "Intervals" begin - include("inclusivity.jl") - include("endpoint.jl") + #include("inclusivity.jl") + #include("endpoint.jl") include("interval.jl") - include("anchoredinterval.jl") - include("comparisons.jl") - include("plotting.jl") + #include("anchoredinterval.jl") + #include("comparisons.jl") + #include("plotting.jl") end From 4e3ebe54dca79c09cc42cbebaafd868de59e3156 Mon Sep 17 00:00:00 2001 From: Fernando Chorney Date: Wed, 27 May 2020 15:24:35 -0500 Subject: [PATCH 02/12] Add support for Infinity.jl --- src/Intervals.jl | 1 + src/interval.jl | 34 +++++++- test/comparisons.jl | 60 ++++++++------ test/interval.jl | 198 ++++++++++++++++++++++++++++++++++++++++---- test/runtests.jl | 11 +-- 5 files changed, 253 insertions(+), 51 deletions(-) diff --git a/src/Intervals.jl b/src/Intervals.jl index 171f5566..6dc9c779 100644 --- a/src/Intervals.jl +++ b/src/Intervals.jl @@ -2,6 +2,7 @@ module Intervals using Dates using Infinity +using Infinity: isposinf, isneginf using Printf using RecipesBase using TimeZones diff --git a/src/interval.jl b/src/interval.jl index 4eb595e6..5cba8b78 100644 --- a/src/interval.jl +++ b/src/interval.jl @@ -71,7 +71,27 @@ struct Interval{T} <: AbstractInterval{T} end end -Interval{T}(f, l, inc::Inclusivity) where T = Interval{T}(convert(T, f), convert(T, l), inc) +isbounded(a) = !isposinf(a) && !isneginf(a) +isunbounded(a) = !isbounded(a) + +function Interval{T}(f, l, inc::Inclusivity) where T + if (isbounded(f) && isbounded(l)) || (isunbounded(f) && isunbounded(l)) + return Interval{T}(convert(T, f), convert(T, l), inc) + else + # If either endpoint is unbounded, we want to convert the bounded variable, and then + # try and promote them both to a compatable type. + # If T is a subset of the Infinite type, then don't try to convert at all, as trying + # to convert any type to Infinite will result in an error + if !(T <: Infinite) + if isbounded(f) + f = convert(T, f) + else + l = convert(T, l) + end + end + return Interval(f, l, inc) + end +end Interval{T}(f, l, x::Bool, y::Bool) where T = Interval{T}(f, l, Inclusivity(x, y)) Interval{T}(f, l) where T = Interval{T}(f, l, true, true) @@ -161,7 +181,8 @@ end ##### ARITHMETIC ##### -Base.:+(a::T, b) where {T <: Interval} = T(first(a) + b, last(a) + b, inclusivity(a)) +Base.:+(a::Interval, b) = Interval(first(a) + b, last(a) + b, inclusivity(a)) + Base.:+(a, b::Interval) = b + a Base.:-(a::Interval, b) = a + -b @@ -338,7 +359,14 @@ function Base.merge(a::AbstractInterval, b::AbstractInterval) left = min(LeftEndpoint(a), LeftEndpoint(b)) right = max(RightEndpoint(a), RightEndpoint(b)) - return Interval(left, right) + + # This promotion fixes the situation where one endpoint has a type of + # `InfExtended{T}` yet the ∞ value is of type `Infinite`. + # This will cause an error when trying to `promote(left, right)` + return Interval( + promote(left.endpoint, right.endpoint)..., + left.included, right.included + ) end ##### TIME ZONES ##### diff --git a/test/comparisons.jl b/test/comparisons.jl index 1abe1789..bb77db41 100644 --- a/test/comparisons.jl +++ b/test/comparisons.jl @@ -18,31 +18,41 @@ const INTERVAL_TYPES = [Interval, AnchoredInterval{Ending}, AnchoredInterval{Beg # [12] # [45] @testset "non-overlapping" begin - earlier = convert(A, Interval(1, 2, true, true)) - later = convert(B, Interval(4, 5, true, true)) - - @test earlier != later - @test !isequal(earlier, later) - @test hash(earlier) != hash(later) - - @test isless(earlier, later) - @test !isless(later, earlier) - - @test earlier < later - @test !(later < earlier) - - @test earlier ≪ later - @test !(later ≪ earlier) - - @test !issubset(earlier, later) - @test !issubset(later, earlier) - - @test isempty(intersect(earlier, later)) - @test_throws ArgumentError merge(earlier, later) - @test union([earlier, later]) == [earlier, later] - @test !overlaps(earlier, later) - @test !contiguous(earlier, later) - @test superset([earlier, later]) == Interval(1, 5, true, true) + tests = [ + ((1, 2, true, true), (4, 5, true, true)), + ((-∞, 2, true, true), (4, ∞, true, true)), + ] + for (a, b) in tests + @show a + @show b + @show A + @show B + earlier = convert(A, Interval(a[1], a[2], a[3], a[4])) + later = convert(B, Interval(b[1], b[2], b[3], b[4])) + + @test earlier != later + @test !isequal(earlier, later) + @test hash(earlier) != hash(later) + + @test isless(earlier, later) + @test !isless(later, earlier) + + @test earlier < later + @test !(later < earlier) + + @test earlier ≪ later + @test !(later ≪ earlier) + + @test !issubset(earlier, later) + @test !issubset(later, earlier) + + @test isempty(intersect(earlier, later)) + @test_throws ArgumentError merge(earlier, later) + @test union([earlier, later]) == [earlier, later] + @test !overlaps(earlier, later) + @test !contiguous(earlier, later) + @test superset([earlier, later]) == Interval(a[1], b[2], a[3], b[4]) + end end # Compare two intervals which "touch" but both intervals do not include that point: diff --git a/test/interval.jl b/test/interval.jl index a785374b..a57b2d94 100644 --- a/test/interval.jl +++ b/test/interval.jl @@ -6,19 +6,20 @@ ('a', 'z', 1), (Date(2013, 2, 13), Date(2013, 3, 13), Day(1)), (DateTime(2016, 8, 11, 0, 30), DateTime(2016, 8, 11, 1), Millisecond(1)), + ] + inf_test_values = [ # Test unbounded endpoints (-∞, ∞, 1), (-∞, 10, 1), (10, ∞, 1), - (-∞, 1, 0.01), + (-∞, 1.0, 0.01), (0.0, ∞, 0.01), - (-∞, 'z', 1), - ('a', ∞, 1), - (-∞, Date(2013, 3, 13), Day(1)), - (Date(2013, 2, 13), ∞, Day(1)), - (-∞, DateTime(2016, 8, 11, 1), Millisecond(1)), - (DateTime(2016, 8, 11, 0, 30), ∞, Millisecond(1)), + #(-∞, Date(2013, 3, 13), Day(1)), + #(Date(2013, 2, 13), ∞, Day(1)), + #(-∞, DateTime(2016, 8, 11, 1), Millisecond(1)), + #(DateTime(2016, 8, 11, 0, 30), ∞, Millisecond(1)), ] + all_test_values = vcat(test_values, inf_test_values) @testset "constructor" begin for T in [Int32, Int64, Float64] @@ -33,7 +34,7 @@ ZonedDateTime(0, tz"UTC"), ZonedDateTime(0, tz"UTC"), Inclusivity(false, false) ) - for (a, b, _) in test_values + for (a, b, _) in all_test_values @test a..b == Interval(a, b) @test Interval(a, b) == Interval{typeof(a)}(a, b, Inclusivity(true, true)) @test Interval(a, b, true, false) == @@ -87,7 +88,7 @@ end @testset "accessors" begin - for (a, b, _) in test_values + for (a, b, _) in all_test_values for i in 0:3 inc = Inclusivity(i) interval = Interval(a, b, inc) @@ -142,6 +143,18 @@ @test sprint(show, interval, context=:compact=>true) == string(interval) @test sprint(show, interval) == "Interval{String}(\"a\", \"b\", Inclusivity(true, true))" + + interval = Interval(-∞, ∞, Inclusivity(true, true)) + @test string(interval) == "[-∞ .. ∞]" + @test sprint(show, interval, context=:compact=>true) == string(interval) + @test sprint(show, interval) == + "Interval{Infinite}(-∞, ∞, Inclusivity(true, true))" + + interval = Interval(0, ∞, Inclusivity(true, true)) + @test string(interval) == "[0 .. ∞]" + @test sprint(show, interval, context=:compact=>true) == string(interval) + @test sprint(show, interval) == + "Interval{InfExtended{Int64}}(0, ∞, Inclusivity(true, true))" end @testset "equality" begin @@ -193,6 +206,36 @@ end end + ################### + # Unbounded tests # + ################### + # Disjoint + @test !(Interval(-∞, ∞) ≪ Interval(-∞, ∞)) + @test !(Interval(-∞, ∞) ≪ Interval(-100, 100)) + @test !(Interval(-∞, ∞) ≫ Interval(-∞, ∞)) + @test !(Interval(-∞, ∞) ≫ Interval(-100, 100)) + @test Interval(-100, 100) ≪ Interval(200, ∞) + @test !(Interval(-100, 100) ≪ Interval(50, ∞)) + @test Interval(-100, 100) ≫ Interval(-∞, -200) + @test !(Interval(-100, 100) ≫ Interval(-∞, -50)) + + # Less/Greater than + @test !isless(Interval(-∞, ∞), Interval(-∞, ∞)) + @test !(Interval(-∞, ∞) < Interval(-∞, ∞)) + @test !(Interval(100, 200) < Interval(-∞, ∞)) + @test Interval(100, 200) < Interval(150, ∞) + @test !(Interval(-∞, ∞) > Interval(-∞, ∞)) + @test Interval(100, 200) > Interval(-∞, ∞) + @test Interval(100, 200) > Interval(-∞, 150) + @test Interval(150, 200) > Interval(100, ∞) + + # Equality + @test Interval(-∞, ∞) == Interval(-∞, ∞) + @test Interval(-∞, ∞) != Interval(-∞, ∞, true, false) + @test Interval(-∞, ∞) != Interval(-∞, ∞, false, true) + @test Interval(-∞, ∞) != Interval(-∞, ∞, false, false) + ################### + @test Interval(2010, 2011) ≪ Interval(2012, 2013) @test !(Interval(2012, 2013) ≪ Interval(2010, 2011)) @@ -311,13 +354,14 @@ i1 = 1 .. 10 i2 = Interval(1, 10, false, false) i3 = 2 .. 11 + i4 = -∞ .. ∞ - @test sort([i1, i2, i3]) == [i1, i2, i3] - @test sort([i1, i2, i3]; rev=true) == [i3, i2, i1] + @test sort([i1, i2, i3, i4]) == [i4, i1, i2, i3] + @test sort([i1, i2, i3, i4]; rev=true) == [i3, i2, i1, i4] end @testset "arithmetic" begin - for (a, b, unit) in test_values + for (a, b, unit) in all_test_values for i in 0:3 inc = Inclusivity(i) interval = Interval(a, b, inc) @@ -413,6 +457,77 @@ @test_throws ArgumentError (in(Interval(a, b), Interval(a, b))) end + + for (a, b, unit) in inf_test_values + if isunbounded(a) && isunbounded(b) + # Both values are unbounded + interval = Interval(a, b) + @test in(a, interval) + @test in(b, interval) + @test in(unit, interval) + @test in(-unit, interval) + + interval = Interval(a, b, true, false) + @test in(a, interval) + @test !in(b, interval) + @test in(unit, interval) + @test in(-unit, interval) + + interval = Interval(a, b, false, true) + @test !in(a, interval) + @test in(b, interval) + @test in(unit, interval) + @test in(-unit, interval) + + interval = Interval(a, b, false, false) + @test !in(a, interval) + @test !in(b, interval) + @test in(unit, interval) + @test in(-unit, interval) + elseif isbounded(a) + # a is a bounded value, and b is unbounded + interval = Interval(a, b) + @test in(a, interval) + @test in(a + unit, interval) + @test !in(a - unit, interval) + + interval = Interval(a, b, true, false) + @test in(a, interval) + @test in(a + unit, interval) + @test !in(a - unit, interval) + + interval = Interval(a, b, false, true) + @test !in(a, interval) + @test in(a + unit, interval) + @test !in(a - unit, interval) + + interval = Interval(a, b, false, false) + @test !in(a, interval) + @test in(a + unit, interval) + @test !in(a - unit, interval) + else + # a is an unbounded value, and b is bounded + interval = Interval(a, b) + @test in(b, interval) + @test !in(b + unit, interval) + @test in(b - unit, interval) + + interval = Interval(a, b, true, false) + @test !in(b, interval) + @test !in(b + unit, interval) + @test in(b - unit, interval) + + interval = Interval(a, b, false, true) + @test in(b, interval) + @test !in(b + unit, interval) + @test in(b - unit, interval) + + interval = Interval(a, b, false, false) + @test !in(b, interval) + @test !in(b + unit, interval) + @test in(b - unit, interval) + end + end end @testset "issubset" begin @@ -426,12 +541,24 @@ @test 1..9 ⊉ 0..10 @test 0..10 ⊈ 1..9 @test 0..10 ⊇ 1..9 + @test 1..11 ⊈ 0..10 @test 1..11 ⊉ 0..10 @test -1..9 ⊈ 0..10 @test -1..9 ⊉ 0..10 @test 20..30 ⊈ 0..10 @test 20..30 ⊉ 0..10 + + @test -∞..∞ ⊆ -∞..∞ + @test -∞..∞ ⊇ -∞..∞ + @test Interval(-∞, ∞, false, false) ⊆ -∞..∞ + @test Interval(-∞, ∞, false, false) ⊉ -∞..∞ + @test -∞..∞ ⊈ Interval(-∞, ∞, false, false) + @test -∞..∞ ⊇ Interval(-∞, ∞, false, false) + @test -100..100 ⊆ -∞..∞ + @test -100..100 ⊉ -∞..∞ + @test -∞..∞ ⊇ -100..100 + @test -∞..∞ ⊈ -100..100 end @testset "intersect" begin @@ -455,6 +582,11 @@ b = Interval(-2, 10, Inclusivity(false, true)) @test intersect(a, b) == Interval(-2, 5, Inclusivity(false, false)) @test intersect(b, a) == intersect(a, b) + + a = Interval(-∞, 100, Inclusivity(true, true)) + b = Interval(50, ∞, Inclusivity(true, true)) + @test intersect(a, b) == Interval(50, 100, Inclusivity(true, true)) + @test intersect(b, a) == intersect(a, b) end @testset "adjacent" begin @@ -477,12 +609,28 @@ b = Interval(0, 10, Inclusivity(false, true)) @test isempty(intersect(a, b)) @test isempty(intersect(b, a)) + + a = Interval(-∞, 0, Inclusivity(true, true)) + b = Interval(0, ∞, Inclusivity(true, true)) + @test intersect(a, b) == Interval(0, 0, Inclusivity(true, true)) + @test intersect(b, a) == intersect(a, b) + + a = Interval(-∞, 0, Inclusivity(true, false)) + b = Interval(0, ∞, Inclusivity(true, true)) + @test isempty(intersect(a, b)) + @test isempty(intersect(b, a)) end @testset "identical" begin for inclusivity in Inclusivity.(0:3) x = Interval(1, 10, inclusivity) @test intersect(x, x) == x + + x = Interval(-∞, 10, inclusivity) + @test intersect(x, x) == x + + x = Interval(1, ∞, inclusivity) + @test intersect(x, x) == x end x = Interval(0, 0, Inclusivity(false, false)) @@ -507,6 +655,11 @@ b = Interval(1, 100, inclusivity) @test isempty(intersect(a, b)) @test isempty(intersect(b, a)) + + a = Interval(-∞, -1, inclusivity) + b = Interval(1, ∞, inclusivity) + @test isempty(intersect(a, b)) + @test isempty(intersect(b, a)) end end end @@ -542,18 +695,24 @@ a = Interval(-100, -1, Inclusivity(true, false)) b = Interval(-2, 10, Inclusivity(false, false)) @test merge(a, b) == Interval(-100, 10, Inclusivity(true, false)) + + a = Interval(-∞, -1) + b = Interval(-2, ∞) + @test merge(a, b) == Interval(-∞, ∞) end @testset "union" begin intervals = [ + Interval(-∞, -50, Inclusivity(false, false)), Interval(-100, -1, Inclusivity(false, false)), Interval(-10, -1, Inclusivity(false, false)), Interval(10, 15, Inclusivity(false, false)), - Interval(13, 20, Inclusivity(false, false)) + Interval(13, 20, Inclusivity(false, false)), + Interval(18, ∞, Inclusivity(false, false)) ] expected = [ - Interval(-100, -1, Inclusivity(false, false)), - Interval(10, 20, Inclusivity(false, false)) + Interval(-∞, -1, Inclusivity(false, false)), + Interval(10, ∞, Inclusivity(false, false)) ] @test union(intervals) == expected @@ -562,16 +721,19 @@ Interval(-100, -1, Inclusivity(false, false)), Interval(10, 15, Inclusivity(false, false)), Interval(-10, -1, Inclusivity(false, false)), - Interval(13, 20, Inclusivity(false, false)) + Interval(18, ∞, Inclusivity(false, false)), + Interval(13, 20, Inclusivity(false, false)), + Interval(-∞, -50, Inclusivity(false, false)) ] @test union(intervals) == expected @test intervals == [ Interval(-100, -1, Inclusivity(false, false)), Interval(10, 15, Inclusivity(false, false)), Interval(-10, -1, Inclusivity(false, false)), - Interval(13, 20, Inclusivity(false, false)) + Interval(18, ∞, Inclusivity(false, false)), + Interval(13, 20, Inclusivity(false, false)), + Interval(-∞, -50, Inclusivity(false, false)) ] - @test union!(intervals) == expected @test intervals == expected diff --git a/test/runtests.jl b/test/runtests.jl index 9f2f0cb9..34d8c538 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -2,15 +2,16 @@ using Dates using Plots using Infinity using Intervals +using Intervals: isbounded, isunbounded using TimeZones using Test using VisualRegressionTests @testset "Intervals" begin - #include("inclusivity.jl") - #include("endpoint.jl") + include("inclusivity.jl") + include("endpoint.jl") include("interval.jl") - #include("anchoredinterval.jl") - #include("comparisons.jl") - #include("plotting.jl") + include("anchoredinterval.jl") + include("comparisons.jl") + include("plotting.jl") end From 6b819617024975540b29037de1aae64f07bac52c Mon Sep 17 00:00:00 2001 From: Fernando Chorney Date: Thu, 28 May 2020 15:30:42 -0500 Subject: [PATCH 03/12] Revert the comparisons.jl test changes --- test/comparisons.jl | 60 +++++++++++++++++++-------------------------- 1 file changed, 25 insertions(+), 35 deletions(-) diff --git a/test/comparisons.jl b/test/comparisons.jl index bb77db41..1abe1789 100644 --- a/test/comparisons.jl +++ b/test/comparisons.jl @@ -18,41 +18,31 @@ const INTERVAL_TYPES = [Interval, AnchoredInterval{Ending}, AnchoredInterval{Beg # [12] # [45] @testset "non-overlapping" begin - tests = [ - ((1, 2, true, true), (4, 5, true, true)), - ((-∞, 2, true, true), (4, ∞, true, true)), - ] - for (a, b) in tests - @show a - @show b - @show A - @show B - earlier = convert(A, Interval(a[1], a[2], a[3], a[4])) - later = convert(B, Interval(b[1], b[2], b[3], b[4])) - - @test earlier != later - @test !isequal(earlier, later) - @test hash(earlier) != hash(later) - - @test isless(earlier, later) - @test !isless(later, earlier) - - @test earlier < later - @test !(later < earlier) - - @test earlier ≪ later - @test !(later ≪ earlier) - - @test !issubset(earlier, later) - @test !issubset(later, earlier) - - @test isempty(intersect(earlier, later)) - @test_throws ArgumentError merge(earlier, later) - @test union([earlier, later]) == [earlier, later] - @test !overlaps(earlier, later) - @test !contiguous(earlier, later) - @test superset([earlier, later]) == Interval(a[1], b[2], a[3], b[4]) - end + earlier = convert(A, Interval(1, 2, true, true)) + later = convert(B, Interval(4, 5, true, true)) + + @test earlier != later + @test !isequal(earlier, later) + @test hash(earlier) != hash(later) + + @test isless(earlier, later) + @test !isless(later, earlier) + + @test earlier < later + @test !(later < earlier) + + @test earlier ≪ later + @test !(later ≪ earlier) + + @test !issubset(earlier, later) + @test !issubset(later, earlier) + + @test isempty(intersect(earlier, later)) + @test_throws ArgumentError merge(earlier, later) + @test union([earlier, later]) == [earlier, later] + @test !overlaps(earlier, later) + @test !contiguous(earlier, later) + @test superset([earlier, later]) == Interval(1, 5, true, true) end # Compare two intervals which "touch" but both intervals do not include that point: From 52c528a8aafe7711e77444666d3705cea6c33f95 Mon Sep 17 00:00:00 2001 From: Fernando Chorney Date: Thu, 28 May 2020 15:32:57 -0500 Subject: [PATCH 04/12] Revert constructor change. Fix up tests --- src/Intervals.jl | 3 +-- src/interval.jl | 30 ++++++++++++------------------ test/interval.jl | 19 +++++++++++++++++-- 3 files changed, 30 insertions(+), 22 deletions(-) diff --git a/src/Intervals.jl b/src/Intervals.jl index 6dc9c779..f0176b3b 100644 --- a/src/Intervals.jl +++ b/src/Intervals.jl @@ -1,8 +1,7 @@ module Intervals using Dates -using Infinity -using Infinity: isposinf, isneginf +using Infinity: Infinite, InfExtended, isposinf, isneginf using Printf using RecipesBase using TimeZones diff --git a/src/interval.jl b/src/interval.jl index 5cba8b78..a34d29c9 100644 --- a/src/interval.jl +++ b/src/interval.jl @@ -71,27 +71,21 @@ struct Interval{T} <: AbstractInterval{T} end end -isbounded(a) = !isposinf(a) && !isneginf(a) -isunbounded(a) = !isbounded(a) - -function Interval{T}(f, l, inc::Inclusivity) where T - if (isbounded(f) && isbounded(l)) || (isunbounded(f) && isunbounded(l)) - return Interval{T}(convert(T, f), convert(T, l), inc) +""" +Check if a value is ∞ or not. Specifically check if the type is `Infinite`, +or `InfExtended`, and then check if the value is pos or neg infinity. +""" +function isbounded(a) + T = typeof(a) + if T <: Infinite || T <: InfExtended + return !isposinf(a) && !isneginf(a) else - # If either endpoint is unbounded, we want to convert the bounded variable, and then - # try and promote them both to a compatable type. - # If T is a subset of the Infinite type, then don't try to convert at all, as trying - # to convert any type to Infinite will result in an error - if !(T <: Infinite) - if isbounded(f) - f = convert(T, f) - else - l = convert(T, l) - end - end - return Interval(f, l, inc) + return true end end +isunbounded(a) = !isbounded(a) + +Interval{T}(f, l, inc::Inclusivity) where T = Interval{T}(convert(T, f), convert(T, l), inc) Interval{T}(f, l, x::Bool, y::Bool) where T = Interval{T}(f, l, Inclusivity(x, y)) Interval{T}(f, l) where T = Interval{T}(f, l, true, true) diff --git a/test/interval.jl b/test/interval.jl index a57b2d94..f2d83455 100644 --- a/test/interval.jl +++ b/test/interval.jl @@ -20,7 +20,7 @@ #(DateTime(2016, 8, 11, 0, 30), ∞, Millisecond(1)), ] all_test_values = vcat(test_values, inf_test_values) - +∞ @testset "constructor" begin for T in [Int32, Int64, Float64] @test Interval{T}() == Interval{T}(zero(T), zero(T), Inclusivity(false, false)) @@ -34,7 +34,7 @@ ZonedDateTime(0, tz"UTC"), ZonedDateTime(0, tz"UTC"), Inclusivity(false, false) ) - for (a, b, _) in all_test_values + for (a, b, _) in test_values @test a..b == Interval(a, b) @test Interval(a, b) == Interval{typeof(a)}(a, b, Inclusivity(true, true)) @test Interval(a, b, true, false) == @@ -47,6 +47,21 @@ Interval{typeof(a)}(a, b, Inclusivity(false, true)) end + for (a, b, _) in inf_test_values + @test a..b == Interval(a, b) + T = isbounded(a) ? typeof(a) : typeof(b) + + @test Interval(a, b) == Interval{InfExtended{T}}(a, b, Inclusivity(true, true)) + @test Interval(a, b, true, false) == + Interval{InfExtended{T}}(a, b, Inclusivity(true, false)) + @test Interval{InfExtended{T}}(a, b, true, false) == + Interval{InfExtended{T}}(a, b, Inclusivity(true, false)) + @test Interval(a, b, Inclusivity(true, false)) == + Interval{InfExtended{T}}(a, b, Inclusivity(true, false)) + @test Interval(b, a, Inclusivity(true, false)) == + Interval{InfExtended{T}}(a, b, Inclusivity(false, true)) + end + # The three-argument Interval constructor can generate a StackOverflow if we aren't # careful @test_throws MethodError Interval(1, 2, 3) From f50c26642b23caf7fc43b3d8f4101a6383cd517e Mon Sep 17 00:00:00 2001 From: Fernando Chorney Date: Thu, 28 May 2020 15:46:53 -0500 Subject: [PATCH 05/12] Clean up constructor tests --- test/interval.jl | 30 ++++++++---------------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/test/interval.jl b/test/interval.jl index f2d83455..b04b08da 100644 --- a/test/interval.jl +++ b/test/interval.jl @@ -34,32 +34,18 @@ ZonedDateTime(0, tz"UTC"), ZonedDateTime(0, tz"UTC"), Inclusivity(false, false) ) - for (a, b, _) in test_values - @test a..b == Interval(a, b) - @test Interval(a, b) == Interval{typeof(a)}(a, b, Inclusivity(true, true)) - @test Interval(a, b, true, false) == - Interval{typeof(a)}(a, b, Inclusivity(true, false)) - @test Interval{typeof(a)}(a, b, true, false) == - Interval{typeof(a)}(a, b, Inclusivity(true, false)) - @test Interval(a, b, Inclusivity(true, false)) == - Interval{typeof(a)}(a, b, Inclusivity(true, false)) - @test Interval(b, a, Inclusivity(true, false)) == - Interval{typeof(a)}(a, b, Inclusivity(false, true)) - end - - for (a, b, _) in inf_test_values + for (a, b, _) in all_test_values + T = promote_type(typeof(a), typeof(b)) @test a..b == Interval(a, b) - T = isbounded(a) ? typeof(a) : typeof(b) - - @test Interval(a, b) == Interval{InfExtended{T}}(a, b, Inclusivity(true, true)) + @test Interval(a, b) == Interval{T}(a, b, Inclusivity(true, true)) @test Interval(a, b, true, false) == - Interval{InfExtended{T}}(a, b, Inclusivity(true, false)) - @test Interval{InfExtended{T}}(a, b, true, false) == - Interval{InfExtended{T}}(a, b, Inclusivity(true, false)) + Interval{T}(a, b, Inclusivity(true, false)) + @test Interval{T}(a, b, true, false) == + Interval{T}(a, b, Inclusivity(true, false)) @test Interval(a, b, Inclusivity(true, false)) == - Interval{InfExtended{T}}(a, b, Inclusivity(true, false)) + Interval{T}(a, b, Inclusivity(true, false)) @test Interval(b, a, Inclusivity(true, false)) == - Interval{InfExtended{T}}(a, b, Inclusivity(false, true)) + Interval{T}(a, b, Inclusivity(false, true)) end # The three-argument Interval constructor can generate a StackOverflow if we aren't From 3564a856f590874779dad0ed91d88e7344f9a4c4 Mon Sep 17 00:00:00 2001 From: Fernando Chorney Date: Thu, 28 May 2020 15:56:56 -0500 Subject: [PATCH 06/12] Fix Interval constructor for two endpoints --- src/interval.jl | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/interval.jl b/src/interval.jl index a34d29c9..3159affa 100644 --- a/src/interval.jl +++ b/src/interval.jl @@ -103,6 +103,14 @@ function Interval{T}(left::LeftEndpoint{T}, right::RightEndpoint{T}) where T Interval{T}(left.endpoint, right.endpoint, left.included, right.included) end +""" +When the endpoints are different types, try to find a common type to combined them into +""" +function Interval(left::LeftEndpoint{S}, right::RightEndpoint{D}) where {S, D} + T = promote_type(S, D) + return Interval{T}(left.endpoint, right.endpoint, left.included, right.included) +end + Interval(left::LeftEndpoint{T}, right::RightEndpoint{T}) where T = Interval{T}(left, right) # Empty Intervals @@ -353,14 +361,7 @@ function Base.merge(a::AbstractInterval, b::AbstractInterval) left = min(LeftEndpoint(a), LeftEndpoint(b)) right = max(RightEndpoint(a), RightEndpoint(b)) - - # This promotion fixes the situation where one endpoint has a type of - # `InfExtended{T}` yet the ∞ value is of type `Infinite`. - # This will cause an error when trying to `promote(left, right)` - return Interval( - promote(left.endpoint, right.endpoint)..., - left.included, right.included - ) + return Interval(left, right) end ##### TIME ZONES ##### From 2e1a0398cffcfca89187e65a5dd2ef3c4ae6a834 Mon Sep 17 00:00:00 2001 From: Curtis Vogt Date: Tue, 2 Jun 2020 13:06:06 -0500 Subject: [PATCH 07/12] Use isfinite for bounded check --- Project.toml | 4 ++-- src/Intervals.jl | 1 - src/interval.jl | 13 +------------ 3 files changed, 3 insertions(+), 15 deletions(-) diff --git a/Project.toml b/Project.toml index 70b00afc..2229f110 100644 --- a/Project.toml +++ b/Project.toml @@ -6,7 +6,6 @@ version = "1.2.0" [deps] Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" -Infinity = "a303e19e-6eb4-11e9-3b09-cd9505f79100" Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" TimeZones = "f269a46b-ccf7-5d73-abea-4c690281aa53" @@ -19,9 +18,10 @@ julia = "1" [extras] ImageMagick = "6218d12a-5da1-5696-b52f-db25d2ecc6d1" +Infinity = "a303e19e-6eb4-11e9-3b09-cd9505f79100" Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" VisualRegressionTests = "34922c18-7c2a-561c-bac1-01e79b2c4c92" [targets] -test = ["ImageMagick", "Plots", "Test", "VisualRegressionTests"] +test = ["ImageMagick", "Infinity", "Plots", "Test", "VisualRegressionTests"] diff --git a/src/Intervals.jl b/src/Intervals.jl index f0176b3b..32d863ee 100644 --- a/src/Intervals.jl +++ b/src/Intervals.jl @@ -1,7 +1,6 @@ module Intervals using Dates -using Infinity: Infinite, InfExtended, isposinf, isneginf using Printf using RecipesBase using TimeZones diff --git a/src/interval.jl b/src/interval.jl index 3159affa..58176590 100644 --- a/src/interval.jl +++ b/src/interval.jl @@ -71,18 +71,7 @@ struct Interval{T} <: AbstractInterval{T} end end -""" -Check if a value is ∞ or not. Specifically check if the type is `Infinite`, -or `InfExtended`, and then check if the value is pos or neg infinity. -""" -function isbounded(a) - T = typeof(a) - if T <: Infinite || T <: InfExtended - return !isposinf(a) && !isneginf(a) - else - return true - end -end +isbounded(x) = isfinite(x) isunbounded(a) = !isbounded(a) Interval{T}(f, l, inc::Inclusivity) where T = Interval{T}(convert(T, f), convert(T, l), inc) From 614967fd5ec6fb3f9e695cb05e142ff5c12c4dcb Mon Sep 17 00:00:00 2001 From: Curtis Vogt Date: Mon, 1 Jun 2020 14:08:10 -0500 Subject: [PATCH 08/12] Improve generality of comparison tests --- test/comparisons.jl | 158 ++++++++++++++++++++++++++++---------------- 1 file changed, 102 insertions(+), 56 deletions(-) diff --git a/test/comparisons.jl b/test/comparisons.jl index 1abe1789..d0d78e96 100644 --- a/test/comparisons.jl +++ b/test/comparisons.jl @@ -21,6 +21,9 @@ const INTERVAL_TYPES = [Interval, AnchoredInterval{Ending}, AnchoredInterval{Beg earlier = convert(A, Interval(1, 2, true, true)) later = convert(B, Interval(4, 5, true, true)) + expected_superset = Interval(1, 5, true, true) + expected_overlap = Interval{Int}() + @test earlier != later @test !isequal(earlier, later) @test hash(earlier) != hash(later) @@ -37,12 +40,12 @@ const INTERVAL_TYPES = [Interval, AnchoredInterval{Ending}, AnchoredInterval{Beg @test !issubset(earlier, later) @test !issubset(later, earlier) - @test isempty(intersect(earlier, later)) + @test intersect(earlier, later) == expected_overlap @test_throws ArgumentError merge(earlier, later) @test union([earlier, later]) == [earlier, later] @test !overlaps(earlier, later) @test !contiguous(earlier, later) - @test superset([earlier, later]) == Interval(1, 5, true, true) + @test superset([earlier, later]) == expected_superset end # Compare two intervals which "touch" but both intervals do not include that point: @@ -54,6 +57,9 @@ const INTERVAL_TYPES = [Interval, AnchoredInterval{Ending}, AnchoredInterval{Beg earlier = convert(A, Interval(1, 3, false, false)) later = convert(B, Interval(3, 5, false, false)) + expected_superset = Interval(1, 5, false, false) + expected_overlap = Interval{Int}() + @test earlier != later @test !isequal(earlier, later) @test hash(earlier) != hash(later) @@ -70,12 +76,12 @@ const INTERVAL_TYPES = [Interval, AnchoredInterval{Ending}, AnchoredInterval{Beg @test !issubset(earlier, later) @test !issubset(later, earlier) - @test isempty(intersect(earlier, later)) + @test intersect(earlier, later) == expected_overlap @test_throws ArgumentError merge(earlier, later) @test union([earlier, later]) == [earlier, later] @test !overlaps(earlier, later) @test !contiguous(earlier, later) - @test superset([earlier, later]) == Interval(1, 5, false, false) + @test superset([earlier, later]) == expected_superset end # Compare two intervals which "touch" and the later interval includes that point: @@ -87,6 +93,9 @@ const INTERVAL_TYPES = [Interval, AnchoredInterval{Ending}, AnchoredInterval{Beg earlier = convert(A, Interval(1, 3, false, false)) later = convert(B, Interval(3, 5, true, true)) + expected_superset = Interval(1, 5, false, true) + expected_overlap = Interval{Int}() + @test earlier != later @test !isequal(earlier, later) @test hash(earlier) != hash(later) @@ -103,12 +112,12 @@ const INTERVAL_TYPES = [Interval, AnchoredInterval{Ending}, AnchoredInterval{Beg @test !issubset(earlier, later) @test !issubset(later, earlier) - @test isempty(intersect(earlier, later)) - @test merge(earlier, later) == Interval(1, 5, false, true) - @test union([earlier, later]) == [Interval(1, 5, false, true)] + @test intersect(earlier, later) == expected_overlap + @test merge(earlier, later) == expected_superset + @test union([earlier, later]) == [expected_superset] @test !overlaps(earlier, later) @test contiguous(earlier, later) - @test superset([earlier, later]) == Interval(1, 5, false, true) + @test superset([earlier, later]) == expected_superset end # Compare two intervals which "touch" and the earlier interval includes that point: @@ -120,6 +129,9 @@ const INTERVAL_TYPES = [Interval, AnchoredInterval{Ending}, AnchoredInterval{Beg earlier = convert(A, Interval(1, 3, true, true)) later = convert(B, Interval(3, 5, false, false)) + expected_superset = Interval(1, 5, true, false) + expected_overlap = Interval{Int}() + @test earlier != later @test !isequal(earlier, later) @test hash(earlier) != hash(later) @@ -136,12 +148,12 @@ const INTERVAL_TYPES = [Interval, AnchoredInterval{Ending}, AnchoredInterval{Beg @test !issubset(earlier, later) @test !issubset(later, earlier) - @test isempty(intersect(earlier, later)) - @test merge(earlier, later) == Interval(1, 5, true, false) - @test union([earlier, later]) == [Interval(1, 5, true, false)] + @test intersect(earlier, later) == expected_overlap + @test merge(earlier, later) == expected_superset + @test union([earlier, later]) == [expected_superset] @test !overlaps(earlier, later) @test contiguous(earlier, later) - @test superset([earlier, later]) == Interval(1, 5, true, false) + @test superset([earlier, later]) == expected_superset end # Compare two intervals which "touch" and both intervals include that point: @@ -153,6 +165,9 @@ const INTERVAL_TYPES = [Interval, AnchoredInterval{Ending}, AnchoredInterval{Beg earlier = convert(A, Interval(1, 3, true, true)) later = convert(B, Interval(3, 5, true, true)) + expected_superset = Interval(1, 5, true, true) + expected_overlap = Interval(3, 3, true, true) + @test earlier != later @test !isequal(earlier, later) @test hash(earlier) != hash(later) @@ -169,12 +184,12 @@ const INTERVAL_TYPES = [Interval, AnchoredInterval{Ending}, AnchoredInterval{Beg @test !issubset(earlier, later) @test !issubset(later, earlier) - @test intersect(earlier, later) == Interval(3, 3, true, true) - @test merge(earlier, later) == Interval(1, 5, true, true) - @test union([earlier, later]) == [Interval(1, 5, true, true)] + @test intersect(earlier, later) == expected_overlap + @test merge(earlier, later) == expected_superset + @test union([earlier, later]) == [expected_superset] @test overlaps(earlier, later) @test !contiguous(earlier, later) - @test superset([earlier, later]) == Interval(1, 5, true, true) + @test superset([earlier, later]) == expected_superset end # Compare two intervals which overlap @@ -186,6 +201,9 @@ const INTERVAL_TYPES = [Interval, AnchoredInterval{Ending}, AnchoredInterval{Beg earlier = convert(A, Interval(1, 4, true, true)) later = convert(B, Interval(2, 5, true, true)) + expected_superset = Interval(1, 5, true, true) + expected_overlap = Interval(2, 4, true, true) + @test earlier != later @test !isequal(earlier, later) @test hash(earlier) != hash(later) @@ -202,18 +220,21 @@ const INTERVAL_TYPES = [Interval, AnchoredInterval{Ending}, AnchoredInterval{Beg @test !issubset(earlier, later) @test !issubset(later, earlier) - @test intersect(earlier, later) == Interval(2, 4, true, true) - @test merge(earlier, later) == Interval(1, 5, true, true) - @test union([earlier, later]) == [Interval(1, 5, true, true)] + @test intersect(earlier, later) == expected_overlap + @test merge(earlier, later) == expected_superset + @test union([earlier, later]) == [expected_superset] @test overlaps(earlier, later) @test !contiguous(earlier, later) - @test superset([earlier, later]) == Interval(1, 5, true, true) + @test superset([earlier, later]) == expected_superset end @testset "equal ()/()" begin a = convert(A, Interval(1, 5, false, false)) b = convert(B, Interval(1, 5, false, false)) + expected_superset = Interval(1, 5, false, false) + expected_overlap = Interval(1, 5, false, false) + @test a == b @test isequal(a, b) @test hash(a) == hash(b) @@ -230,18 +251,21 @@ const INTERVAL_TYPES = [Interval, AnchoredInterval{Ending}, AnchoredInterval{Beg @test issubset(a, b) @test issubset(b, a) - @test intersect(a, b) == Interval(1, 5, false, false) - @test merge(a, b) == Interval(1, 5, false, false) - @test union([a, b]) == [Interval(1, 5, false, false)] + @test intersect(a, b) == expected_overlap + @test merge(a, b) == expected_superset + @test union([a, b]) == [expected_superset] @test overlaps(a, b) @test !contiguous(a, b) - @test superset([a, b]) == Interval(1, 5, false, false) + @test superset([a, b]) == expected_superset end @testset "equal [)/()" begin a = convert(A, Interval(1, 5, true, false)) b = convert(B, Interval(1, 5, false, false)) + expected_superset = Interval(1, 5, true, false) + expected_overlap = Interval(1, 5, false, false) + @test a != b @test !isequal(a, b) @test hash(a) != hash(b) @@ -258,18 +282,21 @@ const INTERVAL_TYPES = [Interval, AnchoredInterval{Ending}, AnchoredInterval{Beg @test !issubset(a, b) @test issubset(b, a) - @test intersect(a, b) == Interval(1, 5, false, false) - @test merge(a, b) == Interval(1, 5, true, false) - @test union([a, b]) == [Interval(1, 5, true, false)] + @test intersect(a, b) == expected_overlap + @test merge(a, b) == expected_superset + @test union([a, b]) == [expected_superset] @test overlaps(a, b) @test !contiguous(a, b) - @test superset([a, b]) == Interval(1, 5, true, false) + @test superset([a, b]) == expected_superset end @testset "equal (]/()" begin a = convert(A, Interval(1, 5, false, true)) b = convert(B, Interval(1, 5, false, false)) + expected_superset = Interval(1, 5, false, true) + expected_overlap = Interval(1, 5, false, false) + @test a != b @test !isequal(a, b) @test hash(a) != hash(b) @@ -286,18 +313,21 @@ const INTERVAL_TYPES = [Interval, AnchoredInterval{Ending}, AnchoredInterval{Beg @test !issubset(a, b) @test issubset(b, a) - @test intersect(a, b) == Interval(1, 5, false, false) - @test merge(a, b) == Interval(1, 5, false, true) - @test union([a, b]) == [Interval(1, 5, false, true)] + @test intersect(a, b) == expected_overlap + @test merge(a, b) == expected_superset + @test union([a, b]) == [expected_superset] @test overlaps(a, b) @test !contiguous(a, b) - @test superset([a, b]) == Interval(1, 5, false, true) + @test superset([a, b]) == expected_superset end @testset "equal []/()" begin a = convert(A, Interval(1, 5, true, true)) b = convert(B, Interval(1, 5, false, false)) + expected_superset = Interval(1, 5, true, true) + expected_overlap = Interval(1, 5, false, false) + @test a != b @test !isequal(a, b) @test hash(a) != hash(b) @@ -314,18 +344,21 @@ const INTERVAL_TYPES = [Interval, AnchoredInterval{Ending}, AnchoredInterval{Beg @test !issubset(a, b) @test issubset(b, a) - @test intersect(a, b) == Interval(1, 5, false, false) - @test merge(a, b) == Interval(1, 5, true, true) - @test union([a, b]) == [Interval(1, 5, true, true)] + @test intersect(a, b) == expected_overlap + @test merge(a, b) == expected_superset + @test union([a, b]) == [expected_superset] @test overlaps(a, b) @test !contiguous(a, b) - @test superset([a, b]) == Interval(1, 5, true, true) + @test superset([a, b]) == expected_superset end @testset "equal [)/[]" begin a = convert(A, Interval(1, 5, true, false)) b = convert(B, Interval(1, 5, true, true)) + expected_superset = Interval(1, 5, true, true) + expected_overlap = Interval(1, 5, true, false) + @test a != b @test !isequal(a, b) @test hash(a) != hash(b) @@ -342,18 +375,21 @@ const INTERVAL_TYPES = [Interval, AnchoredInterval{Ending}, AnchoredInterval{Beg @test issubset(a, b) @test !issubset(b, a) - @test intersect(a, b) == Interval(1, 5, true, false) - @test merge(a, b) == Interval(1, 5, true, true) - @test union([a, b]) == [Interval(1, 5, true, true)] + @test intersect(a, b) == expected_overlap + @test merge(a, b) == expected_superset + @test union([a, b]) == [expected_superset] @test overlaps(a, b) @test !contiguous(a, b) - @test superset([a, b]) == Interval(1, 5, true, true) + @test superset([a, b]) == expected_superset end @testset "equal (]/[]" begin a = convert(A, Interval(1, 5, false, true)) b = convert(B, Interval(1, 5, true, true)) + expected_superset = Interval(1, 5, true, true) + expected_overlap = Interval(1, 5, false, true) + @test a != b @test !isequal(a, b) @test hash(a) != hash(b) @@ -370,18 +406,21 @@ const INTERVAL_TYPES = [Interval, AnchoredInterval{Ending}, AnchoredInterval{Beg @test issubset(a, b) @test !issubset(b, a) - @test intersect(a, b) == Interval(1, 5, false, true) - @test merge(a, b) == Interval(1, 5, true, true) - @test union([a, b]) == [Interval(1, 5, true, true)] + @test intersect(a, b) == expected_overlap + @test merge(a, b) == expected_superset + @test union([a, b]) == [expected_superset] @test overlaps(a, b) @test !contiguous(a, b) - @test superset([a, b]) == Interval(1, 5, true, true) + @test superset([a, b]) == expected_superset end @testset "equal []/[]" begin a = convert(A, Interval(1, 5, true, true)) b = convert(B, Interval(1, 5, true, true)) + expected_superset = Interval(1, 5, true, true) + expected_overlap = Interval(1, 5, true, true) + @test a == b @test isequal(a, b) @test hash(a) == hash(b) @@ -398,12 +437,12 @@ const INTERVAL_TYPES = [Interval, AnchoredInterval{Ending}, AnchoredInterval{Beg @test issubset(a, b) @test issubset(b, a) - @test intersect(a, b) == Interval(1, 5, true, true) - @test merge(a, b) == Interval(1, 5, true, true) - @test union([a, b]) == [Interval(1, 5, true, true)] + @test intersect(a, b) == expected_overlap + @test merge(a, b) == expected_superset + @test union([a, b]) == [expected_superset] @test overlaps(a, b) @test !contiguous(a, b) - @test superset([a, b]) == Interval(1, 5, true, true) + @test superset([a, b]) == expected_superset end @testset "equal -0.0/0.0" begin @@ -412,6 +451,9 @@ const INTERVAL_TYPES = [Interval, AnchoredInterval{Ending}, AnchoredInterval{Beg a = convert(A, Interval(0.0, -0.0)) b = convert(B, Interval(-0.0, 0.0)) + expected_superset = Interval(0.0, 0.0) + expected_overlap = Interval(0.0, 0.0) + @test a == b @test !isequal(a, b) @test hash(a) != hash(b) @@ -429,14 +471,15 @@ const INTERVAL_TYPES = [Interval, AnchoredInterval{Ending}, AnchoredInterval{Beg @test issubset(a, b) @test issubset(b, a) - @test intersect(a, b) == Interval(0.0, 0.0) - @test merge(a, b) == Interval(0.0, 0.0) - @test union([a, b]) == [Interval(0.0, 0.0)] + @test intersect(a, b) == expected_overlap + @test merge(a, b) == expected_superset + @test union([a, b]) == [expected_superset] @test overlaps(a, b) @test !contiguous(a, b) - @test superset([a, b]) == Interval(0.0, 0.0) + @test superset([a, b]) == expected_superset end end + # Compare two intervals where the first interval is contained by the second # Visualization: # @@ -446,6 +489,9 @@ const INTERVAL_TYPES = [Interval, AnchoredInterval{Ending}, AnchoredInterval{Beg smaller = convert(A, Interval(2, 4, true, true)) larger = convert(B, Interval(1, 5, true, true)) + expected_superset = Interval(larger) + expected_overlap = Interval(smaller) + @test smaller != larger @test !isequal(smaller, larger) @test hash(smaller) != hash(larger) @@ -462,11 +508,11 @@ const INTERVAL_TYPES = [Interval, AnchoredInterval{Ending}, AnchoredInterval{Beg @test issubset(smaller, larger) @test !issubset(larger, smaller) - @test intersect(smaller, larger) == Interval(smaller) - @test merge(smaller, larger) == Interval(larger) - @test union([smaller, larger]) == [Interval(larger)] + @test intersect(smaller, larger) == expected_overlap + @test merge(smaller, larger) == expected_superset + @test union([smaller, larger]) == [expected_superset] @test overlaps(smaller, larger) @test !contiguous(smaller, larger) - @test superset([smaller, larger]) == Interval(1, 5, true, true) + @test superset([smaller, larger]) == expected_superset end end From 945757d0f1d6c252eb71e3db2b274a066eb8b42f Mon Sep 17 00:00:00 2001 From: Curtis Vogt Date: Mon, 1 Jun 2020 14:20:20 -0500 Subject: [PATCH 09/12] Experiment with unbounded intervals in comparisons --- src/interval.jl | 8 ++++++++ test/comparisons.jl | 37 +++++++++++++++++++++++++++++++------ 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/src/interval.jl b/src/interval.jl index 58176590..31804148 100644 --- a/src/interval.jl +++ b/src/interval.jl @@ -289,6 +289,14 @@ function Base.intersect(a::AbstractInterval{T}, b::AbstractInterval{T}) where T return Interval{T}(left, right) end +function Base.intersect(a::AbstractInterval{S}, b::AbstractInterval{T}) where {S, T} + !overlaps(a, b) && return Interval{promote_type(S, T)}() + left = max(LeftEndpoint(a), LeftEndpoint(b)) + right = min(RightEndpoint(a), RightEndpoint(b)) + + return Interval(left, right) +end + # There is power in a union. """ union(intervals::AbstractVector{<:AbstractInterval}) diff --git a/test/comparisons.jl b/test/comparisons.jl index d0d78e96..33f34711 100644 --- a/test/comparisons.jl +++ b/test/comparisons.jl @@ -17,12 +17,37 @@ const INTERVAL_TYPES = [Interval, AnchoredInterval{Ending}, AnchoredInterval{Beg # # [12] # [45] - @testset "non-overlapping" begin - earlier = convert(A, Interval(1, 2, true, true)) - later = convert(B, Interval(4, 5, true, true)) - - expected_superset = Interval(1, 5, true, true) - expected_overlap = Interval{Int}() + @testset "non-overlapping" for x in ( + ( + a=Interval(1, 2, true, true), + b=Interval(4, 5, true, true), + superset=Interval(1, 5, true, true), + overlap=Interval{Int}(), + ), + ( + a=Interval(-∞, 2, true, true), + b=Interval(4, ∞, true, true), + superset=Interval(-∞, ∞, true, true), + overlap=Interval{Int}(), + ), + ( + a=Interval(-∞, 2, true, true), + b=Interval(4, 5, true, true), + superset=Interval(-∞, 5, true, true), + overlap=Interval{Int}(), + ), + ( + a=Interval(1, 2, true, true), + b=Interval(4, ∞, true, true), + superset=Interval(1, ∞, true, true), + overlap=Interval{Int}(), + ), + ) + earlier = convert(A, x.a) + later = convert(B, x.b) + + expected_superset = x.superset + expected_overlap = x.overlap @test earlier != later @test !isequal(earlier, later) From dfb91a7dfdf77d0fbf2468cba8adbe9374fd60b7 Mon Sep 17 00:00:00 2001 From: Curtis Vogt Date: Mon, 1 Jun 2020 15:35:04 -0500 Subject: [PATCH 10/12] Work around InfExtended{T} not being bits Prevents it from being used in type parameters --- Project.toml | 4 ++-- src/Intervals.jl | 1 + src/anchoredinterval.jl | 9 ++++++--- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Project.toml b/Project.toml index 2229f110..70b00afc 100644 --- a/Project.toml +++ b/Project.toml @@ -6,6 +6,7 @@ version = "1.2.0" [deps] Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" +Infinity = "a303e19e-6eb4-11e9-3b09-cd9505f79100" Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" TimeZones = "f269a46b-ccf7-5d73-abea-4c690281aa53" @@ -18,10 +19,9 @@ julia = "1" [extras] ImageMagick = "6218d12a-5da1-5696-b52f-db25d2ecc6d1" -Infinity = "a303e19e-6eb4-11e9-3b09-cd9505f79100" Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" VisualRegressionTests = "34922c18-7c2a-561c-bac1-01e79b2c4c92" [targets] -test = ["ImageMagick", "Infinity", "Plots", "Test", "VisualRegressionTests"] +test = ["ImageMagick", "Plots", "Test", "VisualRegressionTests"] diff --git a/src/Intervals.jl b/src/Intervals.jl index 32d863ee..6e672675 100644 --- a/src/Intervals.jl +++ b/src/Intervals.jl @@ -1,6 +1,7 @@ module Intervals using Dates +using Infinity: InfExtended using Printf using RecipesBase using TimeZones diff --git a/src/anchoredinterval.jl b/src/anchoredinterval.jl index b2381b6e..84720036 100644 --- a/src/anchoredinterval.jl +++ b/src/anchoredinterval.jl @@ -138,6 +138,9 @@ span(interval::AnchoredInterval{P}) where P = abs(P) ##### CONVERSION ##### +infbits(x::InfExtended) = x.val +infbits(x) = x + function Base.convert(::Type{Interval}, interval::AnchoredInterval{P, T}) where {P, T} return Interval{T}(first(interval), last(interval), inclusivity(interval)) end @@ -164,11 +167,11 @@ end =# function Base.convert(::Type{AnchoredInterval{Ending}}, interval::Interval{T}) where {T} - AnchoredInterval{-span(interval), T}(last(interval), inclusivity(interval)) + AnchoredInterval{-infbits(span(interval)), T}(last(interval), inclusivity(interval)) end function Base.convert(::Type{AnchoredInterval{Beginning}}, interval::Interval{T}) where {T} - AnchoredInterval{span(interval), T}(first(interval), inclusivity(interval)) + AnchoredInterval{infbits(span(interval)), T}(first(interval), inclusivity(interval)) end Base.convert(::Type{T}, interval::AnchoredInterval{P, T}) where {P, T} = anchor(interval) @@ -264,7 +267,7 @@ function Base.intersect(a::AnchoredInterval{P, T}, b::AnchoredInterval{Q, T}) wh new_P = sp end - return AnchoredInterval{new_P, T}(anchor, inclusivity(interval)) + return AnchoredInterval{infbits(new_P), T}(anchor, inclusivity(interval)) end ##### UTILITIES ##### From f8d323147c3a497fd812bac5eab6bf5ad5686cd9 Mon Sep 17 00:00:00 2001 From: Curtis Vogt Date: Mon, 1 Jun 2020 15:51:49 -0500 Subject: [PATCH 11/12] Detect negative via sign instead of zero Allows for use of types where zero cannot be represented. Note that using `signbit(x)` appears preferable to `sign(x) < 0` but unfortunately less types support `signbit` (e.g. Date) --- src/anchoredinterval.jl | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/anchoredinterval.jl b/src/anchoredinterval.jl index 84720036..789d5ce8 100644 --- a/src/anchoredinterval.jl +++ b/src/anchoredinterval.jl @@ -72,7 +72,8 @@ end # When an interval is anchored to the lesser endpoint, default to Inclusivity(false, true) # When an interval is anchored to the greater endpoint, default to Inclusivity(true, false) function AnchoredInterval{P, T}(i::T) where {P, T} - return AnchoredInterval{P, T}(i::T, Inclusivity(P ≥ zero(P), P ≤ zero(P))) + s = sign(P) + return AnchoredInterval{P, T}(i::T, Inclusivity(s ≥ 0, s ≤ 0)) end function AnchoredInterval{P, T}(i, inc...) where {P, T} @@ -126,11 +127,11 @@ end # that is no longer comparable (e.g., `NaN`). function Base.first(interval::AnchoredInterval{P}) where P - P < zero(P) ? (interval.anchor + P) : (interval.anchor) + sign(P) < 0 ? (interval.anchor + P) : (interval.anchor) end function Base.last(interval::AnchoredInterval{P}) where P - P < zero(P) ? (interval.anchor) : (interval.anchor + P) + sign(P) < 0 ? (interval.anchor) : (interval.anchor + P) end anchor(interval::AnchoredInterval) = interval.anchor @@ -155,13 +156,13 @@ end #= function Base.convert(::Type{AnchoredInterval{P, T}}, interval::Interval{T}) where {P, T} @assert abs(P) == span(interval) - anchor = P < zero(P) ? last(interval) : first(interval) + anchor = sign(P) < 0 ? last(interval) : first(interval) AnchoredInterval{P, T}(last(interval), inclusivity(interval)) end function Base.convert(::Type{AnchoredInterval{P}}, interval::Interval{T}) where {P, T} @assert abs(P) == span(interval) - anchor = P < zero(P) ? last(interval) : first(interval) + anchor = sign(P) < 0 ? last(interval) : first(interval) AnchoredInterval{P, T}(anchor, inclusivity(interval)) end =# @@ -252,14 +253,14 @@ end ##### SET OPERATIONS ##### function Base.isempty(interval::AnchoredInterval{P, T}) where {P, T} - return P == zero(P) && !isclosed(interval) + return sign(P) == 0 && !isclosed(interval) end function Base.intersect(a::AnchoredInterval{P, T}, b::AnchoredInterval{Q, T}) where {P,Q,T} interval = invoke(intersect, Tuple{AbstractInterval{T}, AbstractInterval{T}}, a, b) sp = isa(P, Period) ? canonicalize(typeof(P), span(interval)) : span(interval) - if P ≤ zero(P) + if sign(P) ≤ 0 anchor = last(interval) new_P = -sp else From aba26a28c50e5c3a5e8b7af410a70de6bc754f39 Mon Sep 17 00:00:00 2001 From: Curtis Vogt Date: Tue, 2 Jun 2020 11:40:26 -0500 Subject: [PATCH 12/12] Update comparisons test with unbounded intervals --- test/comparisons.jl | 957 ++++++++++++++++++++++++++------------------ 1 file changed, 568 insertions(+), 389 deletions(-) diff --git a/test/comparisons.jl b/test/comparisons.jl index 33f34711..74d5a676 100644 --- a/test/comparisons.jl +++ b/test/comparisons.jl @@ -1,4 +1,5 @@ using Intervals: Ending, Beginning, overlaps, contiguous, RightEndpoint, LeftEndpoint +using Base.Iterators: product function unique_paired_permutation(v::Vector{T}) where T results = Tuple{T, T}[] @@ -13,461 +14,624 @@ const INTERVAL_TYPES = [Interval, AnchoredInterval{Ending}, AnchoredInterval{Beg @testset "comparisons: $A vs. $B" for (A, B) in unique_paired_permutation(INTERVAL_TYPES) # Compare two intervals which are non-overlapping: - # Visualization: + # Visualization of the finite case: # # [12] # [45] - @testset "non-overlapping" for x in ( - ( - a=Interval(1, 2, true, true), - b=Interval(4, 5, true, true), - superset=Interval(1, 5, true, true), - overlap=Interval{Int}(), - ), - ( - a=Interval(-∞, 2, true, true), - b=Interval(4, ∞, true, true), - superset=Interval(-∞, ∞, true, true), - overlap=Interval{Int}(), - ), - ( - a=Interval(-∞, 2, true, true), - b=Interval(4, 5, true, true), - superset=Interval(-∞, 5, true, true), - overlap=Interval{Int}(), - ), - ( - a=Interval(1, 2, true, true), - b=Interval(4, ∞, true, true), - superset=Interval(1, ∞, true, true), - overlap=Interval{Int}(), - ), - ) - earlier = convert(A, x.a) - later = convert(B, x.b) - - expected_superset = x.superset - expected_overlap = x.overlap - - @test earlier != later - @test !isequal(earlier, later) - @test hash(earlier) != hash(later) - - @test isless(earlier, later) - @test !isless(later, earlier) - - @test earlier < later - @test !(later < earlier) - - @test earlier ≪ later - @test !(later ≪ earlier) - - @test !issubset(earlier, later) - @test !issubset(later, earlier) - - @test intersect(earlier, later) == expected_overlap - @test_throws ArgumentError merge(earlier, later) - @test union([earlier, later]) == [earlier, later] - @test !overlaps(earlier, later) - @test !contiguous(earlier, later) - @test superset([earlier, later]) == expected_superset + @testset "non-overlapping" begin + test_intervals = product( + [ + Interval(1, 2, true, true), + Interval(-∞, 2, true, true), + ], + [ + Interval(4, 5, true, true), + Interval(4, ∞, true, true), + ], + ) + + @testset "$a vs. $b" for (a, b) in test_intervals + A == AnchoredInterval{Beginning} && !isfinite(first(a)) && continue + B == AnchoredInterval{Ending} && !isfinite(last(b)) && continue + + earlier = convert(A, a) + later = convert(B, b) + expected_superset = Interval(LeftEndpoint(a), RightEndpoint(b)) + expected_overlap = Interval{promote_type(eltype(a), eltype(b))}() + + @test earlier != later + @test !isequal(earlier, later) + @test hash(earlier) != hash(later) + + @test isless(earlier, later) + @test !isless(later, earlier) + + @test earlier < later + @test !(later < earlier) + + @test earlier ≪ later + @test !(later ≪ earlier) + + @test !issubset(earlier, later) + @test !issubset(later, earlier) + + @test intersect(earlier, later) == expected_overlap + @test_throws ArgumentError merge(earlier, later) + @test union([earlier, later]) == [earlier, later] + @test !overlaps(earlier, later) + @test !contiguous(earlier, later) + @test superset([earlier, later]) == expected_superset + end end # Compare two intervals which "touch" but both intervals do not include that point: - # Visualization: + # Visualization of the finite case: # # (123) # (345) @testset "touching open/open" begin - earlier = convert(A, Interval(1, 3, false, false)) - later = convert(B, Interval(3, 5, false, false)) - - expected_superset = Interval(1, 5, false, false) - expected_overlap = Interval{Int}() - - @test earlier != later - @test !isequal(earlier, later) - @test hash(earlier) != hash(later) - - @test isless(earlier, later) - @test !isless(later, earlier) - - @test earlier < later - @test !(later < earlier) - - @test earlier ≪ later - @test !(later ≪ earlier) - - @test !issubset(earlier, later) - @test !issubset(later, earlier) - - @test intersect(earlier, later) == expected_overlap - @test_throws ArgumentError merge(earlier, later) - @test union([earlier, later]) == [earlier, later] - @test !overlaps(earlier, later) - @test !contiguous(earlier, later) - @test superset([earlier, later]) == expected_superset + test_intervals = product( + [ + Interval(1, 3, false, false), + Interval(-∞, 3, false, false), + ], + [ + Interval(3, 5, false, false), + Interval(3, ∞, false, false), + ], + ) + + @testset "$a vs. $b" for (a, b) in test_intervals + A == AnchoredInterval{Beginning} && !isfinite(first(a)) && continue + B == AnchoredInterval{Ending} && !isfinite(last(b)) && continue + + earlier = convert(A, a) + later = convert(B, b) + expected_superset = Interval(LeftEndpoint(a), RightEndpoint(b)) + expected_overlap = Interval{promote_type(eltype(a), eltype(b))}() + + @test earlier != later + @test !isequal(earlier, later) + @test hash(earlier) != hash(later) + + @test isless(earlier, later) + @test !isless(later, earlier) + + @test earlier < later + @test !(later < earlier) + + @test earlier ≪ later + @test !(later ≪ earlier) + + @test !issubset(earlier, later) + @test !issubset(later, earlier) + + @test intersect(earlier, later) == expected_overlap + @test_throws ArgumentError merge(earlier, later) + @test union([earlier, later]) == [earlier, later] + @test !overlaps(earlier, later) + @test !contiguous(earlier, later) + @test superset([earlier, later]) == expected_superset + end end # Compare two intervals which "touch" and the later interval includes that point: - # Visualization: + # Visualization of the finite case: # # (123) # [345] @testset "touching open/closed" begin - earlier = convert(A, Interval(1, 3, false, false)) - later = convert(B, Interval(3, 5, true, true)) - - expected_superset = Interval(1, 5, false, true) - expected_overlap = Interval{Int}() - - @test earlier != later - @test !isequal(earlier, later) - @test hash(earlier) != hash(later) - - @test isless(earlier, later) - @test !isless(later, earlier) - - @test earlier < later - @test !(later < earlier) - - @test earlier ≪ later - @test !(later ≪ earlier) - - @test !issubset(earlier, later) - @test !issubset(later, earlier) - - @test intersect(earlier, later) == expected_overlap - @test merge(earlier, later) == expected_superset - @test union([earlier, later]) == [expected_superset] - @test !overlaps(earlier, later) - @test contiguous(earlier, later) - @test superset([earlier, later]) == expected_superset + test_intervals = product( + [ + Interval(1, 3, false, false), + Interval(-∞, 3, false, false), + ], + [ + Interval(3, 5, true, true), + Interval(3, ∞, true, true), + ], + ) + + @testset "$a vs. $b" for (a, b) in test_intervals + A == AnchoredInterval{Beginning} && !isfinite(first(a)) && continue + B == AnchoredInterval{Ending} && !isfinite(last(b)) && continue + + earlier = convert(A, a) + later = convert(B, b) + expected_superset = Interval(LeftEndpoint(a), RightEndpoint(b)) + expected_overlap = Interval{promote_type(eltype(a), eltype(b))}() + + @test earlier != later + @test !isequal(earlier, later) + @test hash(earlier) != hash(later) + + @test isless(earlier, later) + @test !isless(later, earlier) + + @test earlier < later + @test !(later < earlier) + + @test earlier ≪ later + @test !(later ≪ earlier) + + @test !issubset(earlier, later) + @test !issubset(later, earlier) + + @test intersect(earlier, later) == expected_overlap + @test merge(earlier, later) == expected_superset + @test union([earlier, later]) == [expected_superset] + @test !overlaps(earlier, later) + @test contiguous(earlier, later) + @test superset([earlier, later]) == expected_superset + end end # Compare two intervals which "touch" and the earlier interval includes that point: - # Visualization: + # Visualization of the finite case: # # [123] # (345) @testset "touching closed/open" begin - earlier = convert(A, Interval(1, 3, true, true)) - later = convert(B, Interval(3, 5, false, false)) - - expected_superset = Interval(1, 5, true, false) - expected_overlap = Interval{Int}() - - @test earlier != later - @test !isequal(earlier, later) - @test hash(earlier) != hash(later) - - @test isless(earlier, later) - @test !isless(later, earlier) - - @test earlier < later - @test !(later < earlier) - - @test earlier ≪ later - @test !(later ≪ earlier) - - @test !issubset(earlier, later) - @test !issubset(later, earlier) - - @test intersect(earlier, later) == expected_overlap - @test merge(earlier, later) == expected_superset - @test union([earlier, later]) == [expected_superset] - @test !overlaps(earlier, later) - @test contiguous(earlier, later) - @test superset([earlier, later]) == expected_superset + test_intervals = product( + [ + Interval(1, 3, true, true), + Interval(-∞, 3, true, true), + ], + [ + Interval(3, 5, false, false), + Interval(3, ∞, false, false), + ], + ) + + @testset "$a vs $b" for (a, b) in test_intervals + A == AnchoredInterval{Beginning} && !isfinite(first(a)) && continue + B == AnchoredInterval{Ending} && !isfinite(last(b)) && continue + + earlier = convert(A, a) + later = convert(B, b) + expected_superset = Interval(LeftEndpoint(a), RightEndpoint(b)) + expected_overlap = Interval{promote_type(eltype(a), eltype(b))}() + + @test earlier != later + @test !isequal(earlier, later) + @test hash(earlier) != hash(later) + + @test isless(earlier, later) + @test !isless(later, earlier) + + @test earlier < later + @test !(later < earlier) + + @test earlier ≪ later + @test !(later ≪ earlier) + + @test !issubset(earlier, later) + @test !issubset(later, earlier) + + @test intersect(earlier, later) == expected_overlap + @test merge(earlier, later) == expected_superset + @test union([earlier, later]) == [expected_superset] + @test !overlaps(earlier, later) + @test contiguous(earlier, later) + @test superset([earlier, later]) == expected_superset + end end # Compare two intervals which "touch" and both intervals include that point: - # Visualization: + # Visualization of the finite case: # # [123] # [345] @testset "touching closed/closed" begin - earlier = convert(A, Interval(1, 3, true, true)) - later = convert(B, Interval(3, 5, true, true)) - - expected_superset = Interval(1, 5, true, true) - expected_overlap = Interval(3, 3, true, true) - - @test earlier != later - @test !isequal(earlier, later) - @test hash(earlier) != hash(later) - - @test isless(earlier, later) - @test !isless(later, earlier) - - @test earlier < later - @test !(later < earlier) - - @test !(earlier ≪ later) - @test !(later ≪ earlier) - - @test !issubset(earlier, later) - @test !issubset(later, earlier) - - @test intersect(earlier, later) == expected_overlap - @test merge(earlier, later) == expected_superset - @test union([earlier, later]) == [expected_superset] - @test overlaps(earlier, later) - @test !contiguous(earlier, later) - @test superset([earlier, later]) == expected_superset + test_intervals = product( + [ + Interval(1, 3, true, true), + Interval(-∞, 3, true, true), + ], + [ + Interval(3, 5, true, true), + Interval(3, ∞, true, true), + ], + ) + + @testset "$a vs $b" for (a, b) in test_intervals + A == AnchoredInterval{Beginning} && !isfinite(first(a)) && continue + B == AnchoredInterval{Ending} && !isfinite(last(b)) && continue + + earlier = convert(A, a) + later = convert(B, b) + expected_superset = Interval(LeftEndpoint(a), RightEndpoint(b)) + expected_overlap = Interval(last(a), first(b), true, true) + + @test earlier != later + @test !isequal(earlier, later) + @test hash(earlier) != hash(later) + + @test isless(earlier, later) + @test !isless(later, earlier) + + @test earlier < later + @test !(later < earlier) + + @test !(earlier ≪ later) + @test !(later ≪ earlier) + + @test !issubset(earlier, later) + @test !issubset(later, earlier) + + @test intersect(earlier, later) == expected_overlap + @test merge(earlier, later) == expected_superset + @test union([earlier, later]) == [expected_superset] + @test overlaps(earlier, later) + @test !contiguous(earlier, later) + @test superset([earlier, later]) == expected_superset + end end # Compare two intervals which overlap - # Visualization: + # Visualization of the finite case: # # [1234] # [2345] @testset "overlapping" begin - earlier = convert(A, Interval(1, 4, true, true)) - later = convert(B, Interval(2, 5, true, true)) - - expected_superset = Interval(1, 5, true, true) - expected_overlap = Interval(2, 4, true, true) - - @test earlier != later - @test !isequal(earlier, later) - @test hash(earlier) != hash(later) - - @test isless(earlier, later) - @test !isless(later, earlier) - - @test earlier < later - @test !(later < earlier) - - @test !(earlier ≪ later) - @test !(later ≪ earlier) - - @test !issubset(earlier, later) - @test !issubset(later, earlier) - - @test intersect(earlier, later) == expected_overlap - @test merge(earlier, later) == expected_superset - @test union([earlier, later]) == [expected_superset] - @test overlaps(earlier, later) - @test !contiguous(earlier, later) - @test superset([earlier, later]) == expected_superset + test_intervals = product( + [ + Interval(1, 4, true, true), + Interval(-∞, 4, true, true), + ], + [ + Interval(2, 5, true, true), + Interval(2, ∞, true, true), + ], + ) + + @testset "$a vs. $b" for (a, b) in test_intervals + A == AnchoredInterval{Beginning} && !isfinite(first(a)) && continue + B == AnchoredInterval{Ending} && !isfinite(last(b)) && continue + + earlier = convert(A, a) + later = convert(B, b) + expected_superset = Interval(LeftEndpoint(a), RightEndpoint(b)) + expected_overlap = Interval(LeftEndpoint(b), RightEndpoint(a)) + + @test earlier != later + @test !isequal(earlier, later) + @test hash(earlier) != hash(later) + + @test isless(earlier, later) + @test !isless(later, earlier) + + @test earlier < later + @test !(later < earlier) + + @test !(earlier ≪ later) + @test !(later ≪ earlier) + + @test !issubset(earlier, later) + @test !issubset(later, earlier) + + @test intersect(earlier, later) == expected_overlap + @test merge(earlier, later) == expected_superset + @test union([earlier, later]) == [expected_superset] + @test overlaps(earlier, later) + @test !contiguous(earlier, later) + @test superset([earlier, later]) == expected_superset + end end @testset "equal ()/()" begin - a = convert(A, Interval(1, 5, false, false)) - b = convert(B, Interval(1, 5, false, false)) - - expected_superset = Interval(1, 5, false, false) - expected_overlap = Interval(1, 5, false, false) + test_intervals = ( + [ + Interval(l, u, false, false), + Interval(l, u, false, false), + ] + for (l, u) in product((1, -∞), (5, ∞)) + ) + + @testset "$a vs. $b" for (a, b) in test_intervals + A == AnchoredInterval{Beginning} && !isfinite(first(a)) && continue + A == AnchoredInterval{Ending} && !isfinite(last(a)) && continue + B == AnchoredInterval{Beginning} && !isfinite(first(b)) && continue + B == AnchoredInterval{Ending} && !isfinite(last(b)) && continue + + a = convert(A, a) + b = convert(B, b) + expected_superset = Interval(LeftEndpoint(a), RightEndpoint(a)) + expected_overlap = Interval(LeftEndpoint(b), RightEndpoint(b)) - @test a == b - @test isequal(a, b) - @test hash(a) == hash(b) + @test a == b + @test isequal(a, b) + @test hash(a) == hash(b) - @test !isless(a, b) - @test !isless(a, b) + @test !isless(a, b) + @test !isless(a, b) - @test !(a < b) - @test !(b < a) + @test !(a < b) + @test !(b < a) - @test !(a ≪ b) - @test !(b ≪ a) + @test !(a ≪ b) + @test !(b ≪ a) - @test issubset(a, b) - @test issubset(b, a) + @test issubset(a, b) + @test issubset(b, a) - @test intersect(a, b) == expected_overlap - @test merge(a, b) == expected_superset - @test union([a, b]) == [expected_superset] - @test overlaps(a, b) - @test !contiguous(a, b) - @test superset([a, b]) == expected_superset + @test intersect(a, b) == expected_overlap + @test merge(a, b) == expected_superset + @test union([a, b]) == [expected_superset] + @test overlaps(a, b) + @test !contiguous(a, b) + @test superset([a, b]) == expected_superset + end end @testset "equal [)/()" begin - a = convert(A, Interval(1, 5, true, false)) - b = convert(B, Interval(1, 5, false, false)) - - expected_superset = Interval(1, 5, true, false) - expected_overlap = Interval(1, 5, false, false) - - @test a != b - @test !isequal(a, b) - @test hash(a) != hash(b) + test_intervals = ( + [ + Interval(l, u, true, false), + Interval(l, u, false, false), + ] + for (l, u) in product((1, -∞), (5, ∞)) + ) + + @testset "$a vs. $b" for (a, b) in test_intervals + A == AnchoredInterval{Beginning} && !isfinite(first(a)) && continue + A == AnchoredInterval{Ending} && !isfinite(last(a)) && continue + B == AnchoredInterval{Beginning} && !isfinite(first(b)) && continue + B == AnchoredInterval{Ending} && !isfinite(last(b)) && continue + + a = convert(A, a) + b = convert(B, b) + expected_superset = Interval(LeftEndpoint(a), RightEndpoint(a)) + expected_overlap = Interval(LeftEndpoint(b), RightEndpoint(b)) + + @test a != b + @test !isequal(a, b) + @test hash(a) != hash(b) - @test isless(a, b) - @test !isless(b, a) + @test isless(a, b) + @test !isless(b, a) - @test a < b - @test !(b < a) + @test a < b + @test !(b < a) - @test !(a ≪ b) - @test !(b ≪ a) + @test !(a ≪ b) + @test !(b ≪ a) - @test !issubset(a, b) - @test issubset(b, a) + @test !issubset(a, b) + @test issubset(b, a) - @test intersect(a, b) == expected_overlap - @test merge(a, b) == expected_superset - @test union([a, b]) == [expected_superset] - @test overlaps(a, b) - @test !contiguous(a, b) - @test superset([a, b]) == expected_superset + @test intersect(a, b) == expected_overlap + @test merge(a, b) == expected_superset + @test union([a, b]) == [expected_superset] + @test overlaps(a, b) + @test !contiguous(a, b) + @test superset([a, b]) == expected_superset + end end @testset "equal (]/()" begin - a = convert(A, Interval(1, 5, false, true)) - b = convert(B, Interval(1, 5, false, false)) - - expected_superset = Interval(1, 5, false, true) - expected_overlap = Interval(1, 5, false, false) - - @test a != b - @test !isequal(a, b) - @test hash(a) != hash(b) + test_intervals = ( + [ + Interval(l, u, false, true), + Interval(l, u, false, false), + ] + for (l, u) in product((1, -∞), (5, ∞)) + ) + + @testset "$a vs. $b" for (a, b) in test_intervals + A == AnchoredInterval{Beginning} && !isfinite(first(a)) && continue + A == AnchoredInterval{Ending} && !isfinite(last(a)) && continue + B == AnchoredInterval{Beginning} && !isfinite(first(b)) && continue + B == AnchoredInterval{Ending} && !isfinite(last(b)) && continue + + a = convert(A, a) + b = convert(B, b) + expected_superset = Interval(LeftEndpoint(a), RightEndpoint(a)) + expected_overlap = Interval(LeftEndpoint(b), RightEndpoint(b)) + + @test a != b + @test !isequal(a, b) + @test hash(a) != hash(b) - @test !isless(a, b) - @test !isless(b, a) + @test !isless(a, b) + @test !isless(b, a) - @test !(a < b) - @test !(b < a) + @test !(a < b) + @test !(b < a) - @test !(a ≪ b) - @test !(b ≪ a) + @test !(a ≪ b) + @test !(b ≪ a) - @test !issubset(a, b) - @test issubset(b, a) + @test !issubset(a, b) + @test issubset(b, a) - @test intersect(a, b) == expected_overlap - @test merge(a, b) == expected_superset - @test union([a, b]) == [expected_superset] - @test overlaps(a, b) - @test !contiguous(a, b) - @test superset([a, b]) == expected_superset + @test intersect(a, b) == expected_overlap + @test merge(a, b) == expected_superset + @test union([a, b]) == [expected_superset] + @test overlaps(a, b) + @test !contiguous(a, b) + @test superset([a, b]) == expected_superset + end end @testset "equal []/()" begin - a = convert(A, Interval(1, 5, true, true)) - b = convert(B, Interval(1, 5, false, false)) - - expected_superset = Interval(1, 5, true, true) - expected_overlap = Interval(1, 5, false, false) - - @test a != b - @test !isequal(a, b) - @test hash(a) != hash(b) + test_intervals = ( + [ + Interval(l, u, true, true), + Interval(l, u, false, false), + ] + for (l, u) in product((1, -∞), (5, ∞)) + ) + + @testset "$a vs. $b" for (a, b) in test_intervals + A == AnchoredInterval{Beginning} && !isfinite(first(a)) && continue + A == AnchoredInterval{Ending} && !isfinite(last(a)) && continue + B == AnchoredInterval{Beginning} && !isfinite(first(b)) && continue + B == AnchoredInterval{Ending} && !isfinite(last(b)) && continue + + a = convert(A, a) + b = convert(B, b) + expected_superset = Interval(LeftEndpoint(a), RightEndpoint(a)) + expected_overlap = Interval(LeftEndpoint(b), RightEndpoint(b)) + + @test a != b + @test !isequal(a, b) + @test hash(a) != hash(b) - @test isless(a, b) - @test !isless(b, a) + @test isless(a, b) + @test !isless(b, a) - @test a < b - @test !(b < a) + @test a < b + @test !(b < a) - @test !(a ≪ b) - @test !(b ≪ a) + @test !(a ≪ b) + @test !(b ≪ a) - @test !issubset(a, b) - @test issubset(b, a) + @test !issubset(a, b) + @test issubset(b, a) - @test intersect(a, b) == expected_overlap - @test merge(a, b) == expected_superset - @test union([a, b]) == [expected_superset] - @test overlaps(a, b) - @test !contiguous(a, b) - @test superset([a, b]) == expected_superset + @test intersect(a, b) == expected_overlap + @test merge(a, b) == expected_superset + @test union([a, b]) == [expected_superset] + @test overlaps(a, b) + @test !contiguous(a, b) + @test superset([a, b]) == expected_superset + end end @testset "equal [)/[]" begin - a = convert(A, Interval(1, 5, true, false)) - b = convert(B, Interval(1, 5, true, true)) - - expected_superset = Interval(1, 5, true, true) - expected_overlap = Interval(1, 5, true, false) - - @test a != b - @test !isequal(a, b) - @test hash(a) != hash(b) + test_intervals = ( + [ + Interval(l, u, true, false), + Interval(l, u, true, true), + ] + for (l, u) in product((1, -∞), (5, ∞)) + ) + + @testset "$a vs. $b" for (a, b) in test_intervals + A == AnchoredInterval{Beginning} && !isfinite(first(a)) && continue + A == AnchoredInterval{Ending} && !isfinite(last(a)) && continue + B == AnchoredInterval{Beginning} && !isfinite(first(b)) && continue + B == AnchoredInterval{Ending} && !isfinite(last(b)) && continue + + a = convert(A, a) + b = convert(B, b) + expected_superset = Interval(LeftEndpoint(b), RightEndpoint(b)) + expected_overlap = Interval(LeftEndpoint(a), RightEndpoint(a)) + + @test a != b + @test !isequal(a, b) + @test hash(a) != hash(b) - @test !isless(a, b) - @test !isless(b, a) + @test !isless(a, b) + @test !isless(b, a) - @test !(a < b) - @test !(b < a) + @test !(a < b) + @test !(b < a) - @test !(a ≪ b) - @test !(b ≪ a) + @test !(a ≪ b) + @test !(b ≪ a) - @test issubset(a, b) - @test !issubset(b, a) + @test issubset(a, b) + @test !issubset(b, a) - @test intersect(a, b) == expected_overlap - @test merge(a, b) == expected_superset - @test union([a, b]) == [expected_superset] - @test overlaps(a, b) - @test !contiguous(a, b) - @test superset([a, b]) == expected_superset + @test intersect(a, b) == expected_overlap + @test merge(a, b) == expected_superset + @test union([a, b]) == [expected_superset] + @test overlaps(a, b) + @test !contiguous(a, b) + @test superset([a, b]) == expected_superset + end end @testset "equal (]/[]" begin - a = convert(A, Interval(1, 5, false, true)) - b = convert(B, Interval(1, 5, true, true)) - - expected_superset = Interval(1, 5, true, true) - expected_overlap = Interval(1, 5, false, true) - - @test a != b - @test !isequal(a, b) - @test hash(a) != hash(b) + test_intervals = ( + [ + Interval(l, u, false, true), + Interval(l, u, true, true), + ] + for (l, u) in product((1, -∞), (5, ∞)) + ) + + @testset "$a vs. $b" for (a, b) in test_intervals + A == AnchoredInterval{Beginning} && !isfinite(first(a)) && continue + A == AnchoredInterval{Ending} && !isfinite(last(a)) && continue + B == AnchoredInterval{Beginning} && !isfinite(first(b)) && continue + B == AnchoredInterval{Ending} && !isfinite(last(b)) && continue + + a = convert(A, a) + b = convert(B, b) + expected_superset = Interval(LeftEndpoint(b), RightEndpoint(b)) + expected_overlap = Interval(LeftEndpoint(a), RightEndpoint(a)) + + @test a != b + @test !isequal(a, b) + @test hash(a) != hash(b) - @test !isless(a, b) - @test isless(b, a) + @test !isless(a, b) + @test isless(b, a) - @test !(a < b) - @test b < a + @test !(a < b) + @test b < a - @test !(a ≪ b) - @test !(b ≪ a) + @test !(a ≪ b) + @test !(b ≪ a) - @test issubset(a, b) - @test !issubset(b, a) + @test issubset(a, b) + @test !issubset(b, a) - @test intersect(a, b) == expected_overlap - @test merge(a, b) == expected_superset - @test union([a, b]) == [expected_superset] - @test overlaps(a, b) - @test !contiguous(a, b) - @test superset([a, b]) == expected_superset + @test intersect(a, b) == expected_overlap + @test merge(a, b) == expected_superset + @test union([a, b]) == [expected_superset] + @test overlaps(a, b) + @test !contiguous(a, b) + @test superset([a, b]) == expected_superset + end end @testset "equal []/[]" begin - a = convert(A, Interval(1, 5, true, true)) - b = convert(B, Interval(1, 5, true, true)) + test_intervals = ( + [ + Interval(l, u, true, true), + Interval(l, u, true, true), + ] + for (l, u) in product((1, -∞), (5, ∞)) + ) + + @testset "$a vs. $b" for (a, b) in test_intervals + A == AnchoredInterval{Beginning} && !isfinite(first(a)) && continue + A == AnchoredInterval{Ending} && !isfinite(last(a)) && continue + B == AnchoredInterval{Beginning} && !isfinite(first(b)) && continue + B == AnchoredInterval{Ending} && !isfinite(last(b)) && continue + + a = convert(A, a) + b = convert(B, b) + expected_superset = Interval(LeftEndpoint(b), RightEndpoint(b)) + expected_overlap = Interval(LeftEndpoint(a), RightEndpoint(a)) - expected_superset = Interval(1, 5, true, true) - expected_overlap = Interval(1, 5, true, true) - - @test a == b - @test isequal(a, b) - @test hash(a) == hash(b) + @test a == b + @test isequal(a, b) + @test hash(a) == hash(b) - @test !isless(a, b) - @test !isless(b, a) + @test !isless(a, b) + @test !isless(b, a) - @test !(a < b) - @test !(b < a) + @test !(a < b) + @test !(b < a) - @test !(a ≪ b) - @test !(b ≪ a) + @test !(a ≪ b) + @test !(b ≪ a) - @test issubset(a, b) - @test issubset(b, a) + @test issubset(a, b) + @test issubset(b, a) - @test intersect(a, b) == expected_overlap - @test merge(a, b) == expected_superset - @test union([a, b]) == [expected_superset] - @test overlaps(a, b) - @test !contiguous(a, b) - @test superset([a, b]) == expected_superset + @test intersect(a, b) == expected_overlap + @test merge(a, b) == expected_superset + @test union([a, b]) == [expected_superset] + @test overlaps(a, b) + @test !contiguous(a, b) + @test superset([a, b]) == expected_superset + end end @testset "equal -0.0/0.0" begin @@ -475,7 +639,6 @@ const INTERVAL_TYPES = [Interval, AnchoredInterval{Ending}, AnchoredInterval{Beg if Set((A, B)) != Set((AnchoredInterval{Ending}, AnchoredInterval{Beginning})) a = convert(A, Interval(0.0, -0.0)) b = convert(B, Interval(-0.0, 0.0)) - expected_superset = Interval(0.0, 0.0) expected_overlap = Interval(0.0, 0.0) @@ -506,38 +669,54 @@ const INTERVAL_TYPES = [Interval, AnchoredInterval{Ending}, AnchoredInterval{Beg end # Compare two intervals where the first interval is contained by the second - # Visualization: + # Visualization of the finite case: # # [234] # [12345] @testset "containing" begin - smaller = convert(A, Interval(2, 4, true, true)) - larger = convert(B, Interval(1, 5, true, true)) - - expected_superset = Interval(larger) - expected_overlap = Interval(smaller) - - @test smaller != larger - @test !isequal(smaller, larger) - @test hash(smaller) != hash(larger) - - @test !isless(smaller, larger) - @test isless(larger, smaller) - - @test !(smaller < larger) - @test larger < smaller - - @test !(smaller ≪ larger) - @test !(larger ≪ smaller) - - @test issubset(smaller, larger) - @test !issubset(larger, smaller) - - @test intersect(smaller, larger) == expected_overlap - @test merge(smaller, larger) == expected_superset - @test union([smaller, larger]) == [expected_superset] - @test overlaps(smaller, larger) - @test !contiguous(smaller, larger) - @test superset([smaller, larger]) == expected_superset + test_intervals = product( + [ + Interval(2, 4, true, true), + ], + [ + Interval(1, 5, true, true), + Interval(1, ∞, true, true), + Interval(-∞, 5, true, true), + Interval(-∞, ∞, true, true), + ], + ) + + @testset "$a vs $b" for (a, b) in test_intervals + B == AnchoredInterval{Beginning} && !isfinite(first(b)) && continue + B == AnchoredInterval{Ending} && !isfinite(last(b)) && continue + + smaller = convert(A, a) + larger = convert(B, b) + expected_superset = Interval(larger) + expected_overlap = Interval(smaller) + + @test smaller != larger + @test !isequal(smaller, larger) + @test hash(smaller) != hash(larger) + + @test !isless(smaller, larger) + @test isless(larger, smaller) + + @test !(smaller < larger) + @test larger < smaller + + @test !(smaller ≪ larger) + @test !(larger ≪ smaller) + + @test issubset(smaller, larger) + @test !issubset(larger, smaller) + + @test intersect(smaller, larger) == expected_overlap + @test merge(smaller, larger) == expected_superset + @test union([smaller, larger]) == [expected_superset] + @test overlaps(smaller, larger) + @test !contiguous(smaller, larger) + @test superset([smaller, larger]) == expected_superset + end end end