Skip to content

Commit

Permalink
Merge pull request #16 from fchorney/fc/common
Browse files Browse the repository at this point in the history
Consolidate common functions. Add to README
  • Loading branch information
cjdoris authored Jun 18, 2020
2 parents 9250c38 + 4ddf44c commit 5b42bae
Show file tree
Hide file tree
Showing 15 changed files with 115 additions and 90 deletions.
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}(-∞)
```
5 changes: 2 additions & 3 deletions src/Infinity.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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.

5 changes: 1 addition & 4 deletions src/infextendedtime/arithmetic.jl
Original file line number Diff line number Diff line change
@@ -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
Expand Down
3 changes: 0 additions & 3 deletions src/infextendedtime/base.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
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.

6 changes: 3 additions & 3 deletions test/infextendedreal.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down Expand Up @@ -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 /
Expand Down

0 comments on commit 5b42bae

Please sign in to comment.