Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Infinity.jl #95

Closed
wants to merge 12 commits into from
4 changes: 3 additions & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
1 change: 1 addition & 0 deletions src/Intervals.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
module Intervals

using Dates
using Infinity: InfExtended
using Printf
using RecipesBase
using TimeZones
Expand Down
24 changes: 14 additions & 10 deletions src/anchoredinterval.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down Expand Up @@ -126,18 +127,21 @@ 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
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
Expand All @@ -152,23 +156,23 @@ 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
=#

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)
Expand Down Expand Up @@ -249,22 +253,22 @@ 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 Pzero(P)
if sign(P)0
anchor = last(interval)
new_P = -sp
else
anchor = first(interval)
new_P = sp
end

return AnchoredInterval{new_P, T}(anchor, inclusivity(interval))
return AnchoredInterval{infbits(new_P), T}(anchor, inclusivity(interval))
end

##### UTILITIES #####
Expand Down
22 changes: 21 additions & 1 deletion src/interval.jl
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ struct Interval{T} <: AbstractInterval{T}
end
end

isbounded(x) = isfinite(x)
isunbounded(a) = !isbounded(a)
omus marked this conversation as resolved.
Show resolved Hide resolved

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)
Expand All @@ -89,6 +92,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
Expand Down Expand Up @@ -161,7 +172,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))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The previous version always kept the Interval unit type the same. This could cause the Interval type to change.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This becomes the same issue as most of the other issues that cropped up by adding Infinity. If a is -∞ .. ∞ and b is of type Int then ∞ - b where b is 1 becomes an InfExtended{Int64} type which will error out since T would have been Interval{Infinite}(...). So we end up trying to run convert(Infinite, InfExtended{Int64}) which is undefined. That being said, maybe that shouldn't be undefined. Converting an InfExtended{Int64}) to Infinite could potentially work if we check the value to make sure it's not an actual extended value

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That conversion makes sense if the InfExtended{Int64} is infinite

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed by adding the conversion: cjdoris/Infinity.jl#7



Base.:+(a, b::Interval) = b + a
Base.:-(a::Interval, b) = a + -b
Expand Down Expand Up @@ -277,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})
Expand Down
Loading