diff --git a/Project.toml b/Project.toml index e1b5d36..f5202e8 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Infinity" uuid = "a303e19e-6eb4-11e9-3b09-cd9505f79100" authors = ["Christopher Doris "] -version = "0.2.0" +version = "0.2.1" [deps] Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" diff --git a/README.md b/README.md index 080de20..892656a 100644 --- a/README.md +++ b/README.md @@ -6,12 +6,15 @@ 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)` @@ -19,9 +22,19 @@ The following `Base` functions are extended for these types: 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 @@ -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}(-∞) ``` diff --git a/src/Infinity.jl b/src/Infinity.jl index c6b8bf8..189352c 100644 --- a/src/Infinity.jl +++ b/src/Infinity.jl @@ -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 diff --git a/src/common.jl b/src/common.jl new file mode 100644 index 0000000..5e47077 --- /dev/null +++ b/src/common.jl @@ -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 diff --git a/src/infextendedreal/arithmetic.jl b/src/infextendedreal/arithmetic.jl index f48d047..a08f420 100644 --- a/src/infextendedreal/arithmetic.jl +++ b/src/infextendedreal/arithmetic.jl @@ -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) diff --git a/src/infextendedreal/base.jl b/src/infextendedreal/base.jl index bfaa9c5..654cf6f 100644 --- a/src/infextendedreal/base.jl +++ b/src/infextendedreal/base.jl @@ -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) @@ -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) diff --git a/src/infextendedreal/comparison.jl b/src/infextendedreal/comparison.jl index 363b671..ab5b72d 100644 --- a/src/infextendedreal/comparison.jl +++ b/src/infextendedreal/comparison.jl @@ -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) @@ -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)) diff --git a/src/infextendedreal/conversion.jl b/src/infextendedreal/conversion.jl index 1fe946d..08879b3 100644 --- a/src/infextendedreal/conversion.jl +++ b/src/infextendedreal/conversion.jl @@ -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) diff --git a/src/infextendedreal/io.jl b/src/infextendedreal/io.jl deleted file mode 100644 index 99dc6c5..0000000 --- a/src/infextendedreal/io.jl +++ /dev/null @@ -1,10 +0,0 @@ -function Base.show(io::IO, x::T) where {T<:InfExtendedReal} - value = x.val - if get(io, :compact, false) - print(io, value) - else - print(io, "$T(") - show(io, value) - print(io, ")") - end -end diff --git a/src/infextendedtime/arithmetic.jl b/src/infextendedtime/arithmetic.jl index ae1557a..8f916b0 100644 --- a/src/infextendedtime/arithmetic.jl +++ b/src/infextendedtime/arithmetic.jl @@ -1,12 +1,9 @@ -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) @eval begin diff --git a/src/infextendedtime/base.jl b/src/infextendedtime/base.jl index 91e2a43..716ef72 100644 --- a/src/infextendedtime/base.jl +++ b/src/infextendedtime/base.jl @@ -13,7 +13,6 @@ 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) @@ -29,7 +28,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 diff --git a/src/infextendedtime/comparison.jl b/src/infextendedtime/comparison.jl index 2d965c5..d4e1159 100644 --- a/src/infextendedtime/comparison.jl +++ b/src/infextendedtime/comparison.jl @@ -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) @@ -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) diff --git a/src/infextendedtime/conversion.jl b/src/infextendedtime/conversion.jl deleted file mode 100644 index b07ea16..0000000 --- a/src/infextendedtime/conversion.jl +++ /dev/null @@ -1,8 +0,0 @@ -Base.promote_rule(::Type{Infinite}, ::Type{S}) where {S<:TimeType} = S <: InfExtendedTime ? S : InfExtendedTime{S} -Base.promote_rule(::Type{InfExtendedTime{T}}, ::Type{InfExtendedTime{S}}) where {T<:TimeType, S<:TimeType} = InfExtendedTime(promote_type(T, S)) -Base.promote_rule(::Type{InfExtendedTime{T}}, ::Type{S}) where {T<:TimeType, S<:TimeType} = InfExtendedTime(promote_type(T, S)) -Base.promote_rule(::Type{InfExtendedTime{T}}, ::Type{Infinite}) where {T<:TimeType} = InfExtendedTime{T} - -Base.convert(::Type{T}, x::S) where {T<:InfExtendedTime, S<:TimeType} = T(x) -Base.convert(::Type{T}, x::InfExtendedTime) where {T<:InfExtendedTime} = T(x) -Base.convert(::Type{T}, x::Infinite) where {T<:InfExtendedTime} = T(x) diff --git a/src/infextendedtime/io.jl b/src/infextendedtime/io.jl deleted file mode 100644 index 633af32..0000000 --- a/src/infextendedtime/io.jl +++ /dev/null @@ -1,10 +0,0 @@ -function Base.show(io::IO, x::T) where {T<:InfExtendedTime} - 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 diff --git a/test/infextendedreal.jl b/test/infextendedreal.jl index e145806..10c34f9 100644 --- a/test/infextendedreal.jl +++ b/test/infextendedreal.jl @@ -49,7 +49,7 @@ end @testset "Conversion" begin - @test promote_rule(Infinite, Float64) === InfExtendedReal(Float64) + @test promote_rule(Infinite, Float64) === InfExtendedReal{Float64} @test promote_rule(InfExtendedReal{Int64}, InfExtendedReal{Float64}) === Float64 @test promote_rule(InfExtendedReal{Int32}, InfExtendedReal{Int64}) === InfExtendedReal{Int64} @@ -113,13 +113,13 @@ @testset "arithmetic" begin @inferred -∞ @inferred InfExtendedReal 2 + ∞ - @inferred ∞ + 2.3 + @inferred InfExtendedReal ∞ + 2.3 @test_throws InfMinusInfError ∞+(-∞) @inferred InfExtendedReal 10 - ∞ @inferred InfExtendedReal 10.0 - ∞ @test_throws InfMinusInfError ∞-∞ @inferred InfExtendedReal 2 * ∞ - @inferred ∞ * 1.0 + @inferred InfExtendedReal ∞ * 1.0 @test_throws DivideError ∞ * 0 @inferred Float64 1 / ∞ @inferred Float64 1.2 / ∞