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 instant accessor for InfExtendedTime type #18

Merged
merged 3 commits into from
Jun 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "Infinity"
uuid = "a303e19e-6eb4-11e9-3b09-cd9505f79100"
authors = ["Christopher Doris <[email protected]>"]
version = "0.2.0"
version = "0.2.1"

[deps]
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
Expand Down
88 changes: 65 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,35 @@

Provides `∞ :: Infinite <: Real` representing positive infinity and `-∞` is negative infinity.

## Extended Types
### InfExtendedReal

Promotion between `Infinite` and some `T <: Real` will yield either:
* `T` itself if it can natively represent infinity (e.g. `Float64`, `Rational`); or
* `InfExtended{T} <: Real` otherwise, which represents the union of `T` and `Infinite`. (See the examples.)
* `InfExtendedReal{T} <: Real` otherwise, which represents positive/negative infinity, or a finite value of type `T`. (See the examples.)

The following `Base` functions are extended for these types:
* Arithmetic: `+`, `-`, `*`, `/`
* Arithmetic: `typemin`, `typemax`, `+`, `-`, `*`, `/`
* Comparison: `==`, `<`, `≤`, `hash`, `signbit`, `sign`, `isfinite`, `isinf`, `isapprox`
* Conversion: `promote`, `convert`, `float`, `widen`, `big`
* Random: `rand(Infinite)`

Additionally there is a submodule `Utils` exporting infinity-related functions:
* `posinf(T)`, `neginf(T)`: positive or negative infinity as a `T` if possible, or else `nothing`
* `hasposinf(T)`, `hasneginf(T)`: true if `T` contains positive or negative infinity
* `hasinf(T)`: true if `T` contains both positive and negative infinity (this is used to decide to promote to `InfExtended` or not)
* `hasinf(T)`: true if `T` contains both positive and negative infinity (this is used to decide to promote to `InfExtendedReal` or not)
* `isposinf(x)`, `isneginf(x)`: true if `x` is positive or negative infinity

### InfExtendedTime

Promotion between `Infinite` and some `T <: Dates.TimeType` will yield:
* `InfExtendedTime{T} <: Dates.TimeType`, which represents positive/negative infinity, or a finite value of type `T`. (See the examples.)

The following `Base` functions are extended for these types:
* Arithmetic: `typemin`, `typemax`, `T+Period`, `T-Period`
* Comparison: `==`. `<`, `≤`, `hash`, `isfinite`, `isinf`
* Conversion: `promote`, `convert`

## Installation

In Julia, type `]` then run
Expand All @@ -36,29 +49,58 @@ pkg> add Infinity
julia> using Infinity

julia> x = [1,2,3,∞,-1,-∞]
6-element Array{InfExtended{Int64},1}:
1
2
3
-1
-∞
6-element Array{InfExtendedReal{Int64},1}:
InfExtendedReal{Int64}(1)
InfExtendedReal{Int64}(2)
InfExtendedReal{Int64}(3)
InfExtendedReal{Int64}(∞)
InfExtendedReal{Int64}(-1)
InfExtendedReal{Int64}(-∞)

julia> sort(x)
6-element Array{InfExtended{Int64},1}:
-∞
-1
1
2
3
6-element Array{InfExtendedReal{Int64},1}:
InfExtendedReal{Int64}(-∞)
InfExtendedReal{Int64}(-1)
InfExtendedReal{Int64}(1)
InfExtendedReal{Int64}(2)
InfExtendedReal{Int64}(3)
InfExtendedReal{Int64}(∞)

julia> float(x)
6-element Array{Float64,1}:
1.0
2.0
3.0
Inf
-1.0
Inf
1.0
2.0
3.0
Inf
-1.0
-Inf

julia> using Dates

julia> x = [Date(2012, 1, 1), Date(2013, 1, 1), Date(2013, 1, 2), ∞, Date(1987, 1, 1), -∞]
6-element Array{InfExtendedTime{Date},1}:
InfExtendedTime{Date}(2012-01-01)
InfExtendedTime{Date}(2013-01-01)
InfExtendedTime{Date}(2013-01-02)
InfExtendedTime{Date}(∞)
InfExtendedTime{Date}(1987-01-01)
InfExtendedTime{Date}(-∞)

julia> sort(x)
6-element Array{InfExtendedTime{Date},1}:
InfExtendedTime{Date}(-∞)
InfExtendedTime{Date}(1987-01-01)
InfExtendedTime{Date}(2012-01-01)
InfExtendedTime{Date}(2013-01-01)
InfExtendedTime{Date}(2013-01-02)
InfExtendedTime{Date}(∞)

julia> Day(1) + x
6-element Array{InfExtendedTime{Date},1}:
InfExtendedTime{Date}(2012-01-02)
InfExtendedTime{Date}(2013-01-02)
InfExtendedTime{Date}(2013-01-03)
InfExtendedTime{Date}(∞)
InfExtendedTime{Date}(1987-01-02)
InfExtendedTime{Date}(-∞)
```
7 changes: 3 additions & 4 deletions src/Infinity.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module Infinity

using Dates: Period, TimeType
using Dates: Period, TimeType, UTInstant

export Infinite, PosInf, NegInf, ∞, InfMinusInfError, InfExtendedReal, InfExtendedTime

Expand All @@ -18,15 +18,14 @@ include("infinite/rand.jl")
# InfExtendedReal
include("infextendedreal/base.jl")
include("infextendedreal/arithmetic.jl")
include("infextendedreal/io.jl")
include("infextendedreal/comparison.jl")
include("infextendedreal/conversion.jl")

# InfExtendedTime
include("infextendedtime/base.jl")
include("infextendedtime/io.jl")
include("infextendedtime/arithmetic.jl")
include("infextendedtime/comparison.jl")
include("infextendedtime/conversion.jl")

# Extended Common Functions
include("common.jl")
end
43 changes: 43 additions & 0 deletions src/common.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Extract out common functions/etc for the Extended Types

for (Name, T) in ((InfExtendedReal, Real), (InfExtendedTime, TimeType))
@eval begin
# base.jl
$Name{T}(x::$Name{T}) where {T<:$T} = x

Utils.posinf(::Type{T}) where {T<:$Name} = T(PosInf)
Utils.neginf(::Type{T}) where {T<:$Name} = T(NegInf)

# arithmetic.jl
Base.typemin(::Type{T}) where {T<:$Name} = neginf(T)
Base.typemax(::Type{T}) where {T<:$Name} = posinf(T)

# io.jl
function Base.show(io::IO, x::T) where {T<:$Name}
value = isposinf(x) ? ∞ : isneginf(x) ? -∞ : x.finitevalue
if get(io, :compact, false)
print(io, value)
else
print(io, "$T(")
show(io, value)
print(io, ")")
end
end

# comparison.jl
Base.isfinite(x::$Name) = x.flag == FINITE && isfinite(x.finitevalue)
Base.isinf(x::$Name) = isposinf(x) || isneginf(x)

Base.:≤(x::$Name, y::$Name) = !(y < x)

# conversion.jl
Base.promote_rule(::Type{Infinite}, ::Type{T}) where {T<:$T} = T <: $Name ? T : $Name{T}
Base.promote_rule(::Type{$Name{T}}, ::Type{$Name{S}}) where {T<:$T, S<:$T} = $Name(promote_type(T, S))
Base.promote_rule(::Type{$Name{T}}, ::Type{S}) where {T<:$T, S<:$T} = $Name(promote_type(T, S))
Base.promote_rule(::Type{$Name{T}}, ::Type{Infinite}) where {T<:$T} = $Name{T}

Base.convert(::Type{T}, x::S) where {T<:$Name, S<:$T} = T(x)
Base.convert(::Type{T}, x::$Name) where {T<:$Name} = T(x)
Base.convert(::Type{T}, x::Infinite) where {T<:$Name} = T(x)
end
end
5 changes: 0 additions & 5 deletions src/infextendedreal/arithmetic.jl
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
# todo: ^, fma, muladd, div, fld, cld, rem, mod, mod1, fld1

Base.typemin(::Type{T}) where {T<:InfExtendedReal} = T(NegInf)

Base.typemax(::Type{T}) where {T<:InfExtendedReal} = T(PosInf)

Base.:+(x::T) where {T<:InfExtendedReal} = T(+x.val)

Base.:-(x::T) where {T<:InfExtendedReal} = T(-x.val)
Expand Down
3 changes: 0 additions & 3 deletions src/infextendedreal/base.jl
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ end

InfExtendedReal{T}(x::Real) where {T<:Real} = InfExtendedReal{T}(isinf(x) ? convert(Infinite, x) : convert(T, x))
InfExtendedReal{T}(x::InfExtendedReal) where {T<:Real} = InfExtendedReal{T}(x.val)
InfExtendedReal{T}(x::InfExtendedReal{T}) where {T<:Real} = x

"""
InfExtendedReal(T)
Expand All @@ -41,7 +40,5 @@ Converts `x` to a `InfExtendedReal(typeof(x))`.
@generated InfExtendedReal(x::T) where {T<:Real} = hasinf(T) ? :x : :($(InfExtendedReal(T))(x))


Utils.posinf(::Type{T}) where {T<:InfExtendedReal} = T(PosInf)
Utils.neginf(::Type{T}) where {T<:InfExtendedReal} = T(NegInf)
Utils.isposinf(x::InfExtendedReal) = x.flag == POSINF || isposinf(x.finitevalue)
Utils.isneginf(x::InfExtendedReal) = x.flag == NEGINF || isneginf(x.finitevalue)
6 changes: 0 additions & 6 deletions src/infextendedreal/comparison.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
Base.isfinite(x::InfExtendedReal) = x.flag == FINITE && isfinite(x.finitevalue)

Base.isinf(x::InfExtendedReal) = isposinf(x) || isneginf(x)

Base.:(==)(x::InfExtendedReal, y::InfExtendedReal) = isinf(x)==isinf(y) && x.val==y.val

Base.hash(x::InfExtendedReal, h::UInt) = hash(x.val, h)
Expand All @@ -15,8 +11,6 @@ Base.:<(x::InfExtendedReal, y::InfExtendedReal) =
x.val < y.val
end

Base.:≤(x::InfExtendedReal, y::InfExtendedReal) = !(y < x)

Base.signbit(x::InfExtendedReal) = signbit(x.val)

Base.sign(x::InfExtendedReal{T}) where {T<:Real} = convert(T, sign(x.val))
Expand Down
8 changes: 0 additions & 8 deletions src/infextendedreal/conversion.jl
Original file line number Diff line number Diff line change
@@ -1,13 +1,5 @@
Base.promote_rule(::Type{Infinite}, ::Type{T}) where {T<:Real} = InfExtendedReal(T)
Base.promote_rule(::Type{InfExtendedReal{T}}, ::Type{InfExtendedReal{S}}) where {T<:Real, S<:Real} = InfExtendedReal(promote_type(T, S))
Base.promote_rule(::Type{InfExtendedReal{T}}, ::Type{S}) where {T<:Real, S<:Real} = InfExtendedReal(promote_type(T, S))
Base.promote_rule(::Type{InfExtendedReal{T}}, ::Type{Infinite}) where {T<:Real} = InfExtendedReal{T}

@generated Base.convert(::Type{T}, x::InfExtendedReal{S}) where {T<:Real,S<:Real} = :(convert($(typeof(convert(T,zero(S)))), x.val))
Base.convert(::Type{Infinite}, x::InfExtendedReal{T}) where {T<:Real} = isinf(x) ? Infinite(signbit(x)) : throw(InexactError(:convert,Infinite,x))
Base.convert(::Type{T}, x::S) where {T<:InfExtendedReal, S<:Real} = T(x)
Base.convert(::Type{T}, x::InfExtendedReal) where {T<:InfExtendedReal} = T(x)
Base.convert(::Type{T}, x::Infinite) where {T<:InfExtendedReal} = T(x)

(::Type{T})(x::InfExtendedReal) where {T<:AbstractFloat} = convert(T, x)

Expand Down
10 changes: 0 additions & 10 deletions src/infextendedreal/io.jl

This file was deleted.

15 changes: 6 additions & 9 deletions src/infextendedtime/arithmetic.jl
Original file line number Diff line number Diff line change
@@ -1,27 +1,24 @@
Base.typemin(::Type{T}) where {T<:InfExtendedTime} = neginf(T)
Base.typemax(::Type{T}) where {T<:InfExtendedTime} = posinf(T)

function Base.:+(x::T, y::S) where {T<:InfExtendedTime, S<:Period}
isinf(x) ? x : T(x.finitevalue + y)
end
Base.:+(x::S, y::T) where {T<:InfExtendedTime, S<:Period} = y + x
Base.:-(x::T, y::S) where {T<:InfExtendedTime, S<:Period} = x + -y
Base.:-(x::S, y::T) where {T<:InfExtendedTime, S<:Period} = isposinf(y) ? Utils.neginf(T) : y + -x
Base.:-(x::S, y::T) where {T<:InfExtendedTime, S<:Period} = isposinf(y) ? neginf(T) : y + -x

for TType in (TimeType, Period)
for TType in (TimeType, Period, UTInstant)
@eval begin
Base.:+(x::Infinite, y::T) where {T<:$TType} = x
Base.:+(x::T, y::Infinite) where {T<:$TType} = y
Base.:-(x::Infinite, y::T) where {T<:$TType} = x
Base.:-(x::T, y::Infinite) where {T<:$TType} = y
Base.:-(x::T, y::Infinite) where {T<:$TType} = Infinite(!y.signbit)
end
end

function Base.:+(x::T, y::Infinite) where {T<:InfExtendedTime}
if isfinite(x)
return T(y)
else
val = x.flag == POSINF ? ∞ : -∞
val = isposinf(x) ? ∞ : -∞
return T(val + y)
end
end
Expand All @@ -30,15 +27,15 @@ function Base.:-(x::T, y::Infinite) where {T<:InfExtendedTime}
if isfinite(x)
return T(-y)
else
val = x.flag == POSINF ? ∞ : -∞
val = isposinf(x) ? ∞ : -∞
return T(val - y)
end
end
function Base.:-(x::Infinite, y::T) where {T<:InfExtendedTime}
if isfinite(y)
return T(x)
else
val = y.flag == POSINF ? ∞ : -∞
val = isposinf(y) ? ∞ : -∞
return T(x - val)
end
end
15 changes: 12 additions & 3 deletions src/infextendedtime/base.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,20 @@ struct InfExtendedTime{T<:TimeType} <: TimeType
InfExtendedTime{T}(x::Infinite) where {T<:TimeType} = new{T}(x==PosInf ? POSINF : NEGINF)
end

function Base.getproperty(x::InfExtendedTime, s::Symbol)
if s === :instant
if isinf(x)
return isposinf(x) ? ∞ : -∞
else
return x.finitevalue.instant
end
else
return getfield(x, s)
end
end

InfExtendedTime{T}(x::TimeType) where {T<:TimeType} = InfExtendedTime{T}(convert(T, x))
InfExtendedTime{T}(x::InfExtendedTime) where {T<:TimeType} = InfExtendedTime{T}(convert(T, x.finitevalue))
InfExtendedTime{T}(x::InfExtendedTime{T}) where {T<:TimeType} = x

"""
InfExtendedTime(T)
Expand All @@ -29,7 +40,5 @@ Converts `x` to `InfExtendedTime(typeof(x))`.
"""
InfExtendedTime(x::T) where {T<:TimeType} = InfExtendedTime{T}(x)

Utils.posinf(::Type{T}) where {T<:InfExtendedTime} = T(PosInf)
Utils.neginf(::Type{T}) where {T<:InfExtendedTime} = T(NegInf)
Utils.isposinf(x::InfExtendedTime) = x.flag == POSINF
Utils.isneginf(x::InfExtendedTime) = x.flag == NEGINF
3 changes: 0 additions & 3 deletions src/infextendedtime/comparison.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
Base.isfinite(x::InfExtendedTime) = x.flag == FINITE && isfinite(x.finitevalue)
Base.isinf(x::InfExtendedTime) = isposinf(x) || isneginf(x)
Base.:(==)(x::InfExtendedTime, y::InfExtendedTime) = (isfinite(x) && isfinite(y)) ? x.finitevalue == y.finitevalue : x.flag == y.flag
Base.:(==)(x::Infinite, y::T) where {T<:InfExtendedTime} = T(x) == y
Base.:(==)(x::T, y::Infinite) where {T<:InfExtendedTime} = x == T(y)
Expand All @@ -18,6 +16,5 @@ end
Base.isless(x::Infinite, y::T) where {T<:InfExtendedTime} = isless(T(x), y)
Base.isless(x::T, y::Infinite) where {T<:InfExtendedTime} = isless(x, T(y))

Base.:≤(x::InfExtendedTime, y::InfExtendedTime) = !(y < x)
Base.:≤(x::Infinite, y::T) where {T<:InfExtendedTime} = T(x) ≤ y
Base.:≤(x::T, y::Infinite) where {T<:InfExtendedTime} = x ≤ T(y)
8 changes: 0 additions & 8 deletions src/infextendedtime/conversion.jl

This file was deleted.

10 changes: 0 additions & 10 deletions src/infextendedtime/io.jl

This file was deleted.

Loading