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

Make InfExtendedReal a bitstype #13

Merged
merged 11 commits into from
Jun 14, 2020
4 changes: 3 additions & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"

[compat]
Compat = "3.12"
julia = "1"

[extras]
Compat = "34da2185-b29b-5c13-b0c7-acf172513d20"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["Test"]
test = ["Compat", "Test"]
2 changes: 1 addition & 1 deletion src/infextendedreal/arithmetic.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Base.:-(x::T) where {T<:InfExtendedReal} = T(-x.val)

Base.:+(x::T, y::T) where {T<:InfExtendedReal} = isinf(x) ? isinf(y) ? T(x.val+y.val) : x : isinf(y) ? y : T(x.val+y.val)

Base.:-(x::T, y::T) where {T<:InfExtendedReal} = isinf(x) ? isinf(y) ? T(x.val - y.val) : x : isinf(y) ? -y : T(x.val-y.val)
Base.:-(x::T, y::T) where {T<:InfExtendedReal} = isinf(x) ? isinf(y) ? T(x.val-y.val) : x : isinf(y) ? -y : T(x.val-y.val)

Base.:*(x::T, y::T) where {T<:InfExtendedReal} =
if isinf(x)
Expand Down
23 changes: 18 additions & 5 deletions src/infextendedreal/base.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,22 @@
The type `T` extended with positive and negative infinity.
"""
struct InfExtendedReal{T<:Real} <: Real
val :: Union{T, Infinite}
InfExtendedReal{T}(x::Infinite) where {T<:Real} = new(x)
InfExtendedReal{T}(x::T) where {T<:Real} = new(x)
flag :: InfFlag
finitevalue :: T

InfExtendedReal{T}(x::T) where {T<:Real} = new(FINITE, x)
InfExtendedReal{T}(x::Infinite) where {T<:Real} = new(x==PosInf ? POSINF : NEGINF)
end

# Since InfExtendedReal is a subtype of Real, and Infinite is also a subtype of real,
# we can just use `x.val` to get either the finite value, or the infinite value. This will
# make arithmetic much simpler.
function Base.getproperty(x::InfExtendedReal, s::Symbol)
if s === :val
return isinf(x) ? (isneginf(x) ? -∞ : ∞) : x.finitevalue
else
return getfield(x, s)
end
end

InfExtendedReal{T}(x::Real) where {T<:Real} = InfExtendedReal{T}(isinf(x) ? convert(Infinite, x) : convert(T, x))
Expand All @@ -30,5 +43,5 @@ Converts `x` to a `InfExtendedReal(typeof(x))`.

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

Choose a reason for hiding this comment

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

Seems strange to be storing non-finite values in a field called finitevalue

Copy link
Owner

Choose a reason for hiding this comment

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

Normally x.finitevalue will only ever be finite, unless the user does something like InfExtendedReal{Float64}(Inf).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah I did the same thing in my dates PR as well. I didn't necessarily think it made sense to also check for finite in x.finitevalue. Guess it makes sense to remove it.


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

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

Expand Down
11 changes: 10 additions & 1 deletion src/infextendedreal/io.jl
Original file line number Diff line number Diff line change
@@ -1 +1,10 @@
Base.show(io::IO, x::InfExtendedReal) = show(io, x.val)
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
223 changes: 149 additions & 74 deletions test/infextendedreal.jl
Original file line number Diff line number Diff line change
@@ -1,77 +1,152 @@
@testset "InfExtendedRealReal" begin
@testset "base" begin
@test InfExtendedReal(Int) === InfExtendedReal{Int}
@test InfExtendedReal(Float64) === Float64
@test InfExtendedReal(Rational{Int}) === Rational{Int}
@test InfExtendedReal(10) === InfExtendedReal{Int}(10)
@test InfExtendedReal(10.0) === 10.0
@test InfExtendedReal(2//3) === 2//3
@inferred InfExtendedReal(Int)
@inferred InfExtendedReal(Float64)
@inferred InfExtendedReal(Rational{Int})
@inferred InfExtendedReal(10)
@inferred InfExtendedReal(1.2)
@inferred InfExtendedReal(2//3)
end

@testset "arithmetic" begin
@inferred -∞
@inferred 2 + ∞
@inferred ∞ + 2.3
@test_throws InfMinusInfError ∞+(-∞)
@inferred 10 - ∞
@inferred 10.0 - ∞
@test_throws InfMinusInfError ∞-∞
@inferred 2 * ∞
@inferred ∞ * 1.0
@test_throws DivideError ∞ * 0
@inferred 1 / ∞
@inferred 1.2 / ∞
@inferred -1 / -∞
@inferred ∞ / 3
@inferred 0 / ∞
@test typemin(InfExtendedReal{Int}) === InfExtendedReal{Int}(-∞)
@test typemax(InfExtendedReal{Int}) === InfExtendedReal{Int}(∞)
end

@testset "comparisons" begin
@test ∞ == ∞
@test ∞ == Inf
@test InfExtendedReal(5) == 5
@test InfExtendedReal(7) == 7.0
@test InfExtendedReal(4) != InfExtendedReal(1)
@test hash(∞) == hash(Inf)
@test hash(InfExtendedReal{Int}(∞)) == hash(Inf)
@test hash(InfExtendedReal(3)) == hash(3)
@test isinf(∞)
@test isinf(InfExtendedReal{Int}(∞))
@test !isinf(InfExtendedReal(9))
@test !isfinite(∞)
@test !isfinite(InfExtendedReal{Int}(∞))
@test isfinite(InfExtendedReal(-4))
@test ∞ ≤ ∞
@test 1 ≤ ∞
@test -∞ ≤ -∞
@test !(∞ ≤ 0)
@test !signbit(∞)
@test signbit(-∞)
@test !signbit(InfExtendedReal{Int}(∞))
@test signbit(InfExtendedReal{Int}(-∞))
@test !signbit(InfExtendedReal(20))
@test signbit(InfExtendedReal(-2))
@test sign(∞) == 1
@test sign(-∞) == -1
@test sign(InfExtendedReal{Int}(∞)) == 1
@test sign(InfExtendedReal{Int}(-∞)) == -1
@test sign(InfExtendedReal(3)) == 1
@test sign(InfExtendedReal(-99)) == -1
@test sign(InfExtendedReal(0)) == 0
@inferred sign(InfExtendedReal(2))
@inferred sign(InfExtendedReal{Int32}(∞))
end

@testset "conversions" begin
@test convert(Infinite, InfExtendedReal{Int}(∞)) === ∞
end
@testset "Base" begin
for (T, x) in ((Int, 1), (Float64, 1.0), (Rational{Int}, 2//3))
@inferred InfExtendedReal(T)
@inferred InfExtendedReal(x)
@inferred InfExtendedReal{T}(x)
@inferred InfExtendedReal{T}(∞)
@inferred InfExtendedReal{T}(-∞)
end

# Specifically test that if a value can represent `Inf` it doesn't get wrapped in
# `InfExtendedReal`
@test InfExtendedReal(Int) === InfExtendedReal{Int}
@test InfExtendedReal(Float64) === Float64
@test InfExtendedReal(Rational{Int}) === Rational{Int}
@test InfExtendedReal(10) === InfExtendedReal{Int}(10)
@test InfExtendedReal(10.0) === 10.0
@test InfExtendedReal(2//3) === 2//3

@test InfExtendedReal{Int}(Inf) == InfExtendedReal{Int}(∞)
@test InfExtendedReal{Float64}(InfExtendedReal{Int}(10)) ===
InfExtendedReal{Float64}(10.0)
@test InfExtendedReal{Int}(InfExtendedReal{Int}(1)) === InfExtendedReal{Int}(1)

a = InfExtendedReal{Int}(1)
inf = InfExtendedReal{Int}(∞)
ninf = InfExtendedReal{Int}(-∞)
@test posinf(typeof(a)) == inf
@test neginf(typeof(a)) == ninf
@test !isposinf(a)
@test !isneginf(a)
@test isposinf(inf)
@test !isneginf(inf)
@test !isposinf(ninf)
@test isneginf(ninf)
end

@testset "IO" begin
x = InfExtendedReal{Int64}(2)
i = InfExtendedReal{Int64}(∞)

@test string(x) == "InfExtendedReal{Int64}(2)"
@test sprint(show, x, context=:compact=>true) == "2"
@test sprint(show, x) == string(x)

@test string(i) == "InfExtendedReal{Int64}(∞)"
@test sprint(show, i, context=:compact=>true) == "∞"
@test sprint(show, i) == string(i)
end

@testset "Conversion" begin
@test promote_rule(Infinite, Float64) === InfExtendedReal(Float64)
@test promote_rule(InfExtendedReal{Int64}, InfExtendedReal{Float64}) === Float64
@test promote_rule(InfExtendedReal{Int32}, InfExtendedReal{Int64}) ===
InfExtendedReal{Int64}
@test promote_rule(InfExtendedReal{Int64}, Float64) === Float64
@test promote_rule(InfExtendedReal{Int64}, Infinite) === InfExtendedReal{Int64}

@test convert(Int64, InfExtendedReal{Float64}(2.0)) == 2
@test convert(Infinite, InfExtendedReal{Int}(∞)) === ∞
@test convert(InfExtendedReal{Int64}, 2.0) === InfExtendedReal{Int64}(2.0)
@test convert(InfExtendedReal{Int64}, InfExtendedReal{Float64}(2.0)) === InfExtendedReal{Int64}(2.0)
@test convert(InfExtendedReal{Int64}, ∞) == InfExtendedReal{Int64}(∞)

@test Float64(InfExtendedReal{Int64}(2)) === 2.0

@test widen(InfExtendedReal{Int32}) === InfExtendedReal{Int64}
@test big(InfExtendedReal{Int}) === InfExtendedReal{BigInt}
@test big(InfExtendedReal{Int64}(2)) == InfExtendedReal{BigInt}(2)

@test float(InfExtendedReal{Int}) === Float64
@test float(InfExtendedReal) === Float64
end

@testset "comparisons" begin
@test !isfinite(InfExtendedReal{Int}(∞))
@test isfinite(InfExtendedReal(-4))
@test !isfinite(InfExtendedReal{Float64}(Inf))

@test isinf(InfExtendedReal{Int}(∞))
@test !isinf(InfExtendedReal(9))
@test isinf(InfExtendedReal{Float64}(Inf))

@test InfExtendedReal(5) == 5
@test InfExtendedReal(7) == 7.0
@test InfExtendedReal(4) != InfExtendedReal(1)

@test hash(InfExtendedReal(3)) == hash(3)

@test InfExtendedReal(2) < InfExtendedReal{Int}(∞)
@test InfExtendedReal{Int}(-∞) < InfExtendedReal(2)
@test InfExtendedReal(2) <= InfExtendedReal(4)

@test !signbit(InfExtendedReal{Int}(∞))
@test signbit(InfExtendedReal{Int}(-∞))
@test !signbit(InfExtendedReal(20))
@test signbit(InfExtendedReal(-2))

@test sign(∞) == 1
@test sign(-∞) == -1
@test sign(InfExtendedReal{Int}(∞)) == 1
@test sign(InfExtendedReal{Int}(-∞)) == -1
@test sign(InfExtendedReal(3)) == 1
@test sign(InfExtendedReal(-99)) == -1
@test sign(InfExtendedReal(0)) == 0

@inferred sign(InfExtendedReal(2))
@inferred sign(InfExtendedReal{Int32}(∞))

@test isapprox(InfExtendedReal{Float64}(2.000000001), InfExtendedReal{Float64}(2.0000000004))
end

@testset "arithmetic" begin
@inferred -∞
@inferred InfExtendedReal 2 + ∞
@inferred ∞ + 2.3
@test_throws InfMinusInfError ∞+(-∞)
@inferred InfExtendedReal 10 - ∞
@inferred InfExtendedReal 10.0 - ∞
@test_throws InfMinusInfError ∞-∞
@inferred InfExtendedReal 2 * ∞
@inferred ∞ * 1.0
@test_throws DivideError ∞ * 0
@inferred Float64 1 / ∞
@inferred Float64 1.2 / ∞
@inferred Float64 -1 / -∞
@inferred Float64 ∞ / 3
@inferred Float64 0 / ∞

@test typemin(InfExtendedReal{Int64}) == InfExtendedReal{Int64}(-∞)
@test typemax(InfExtendedReal{Int64}) == InfExtendedReal{Int64}(∞)

@test +InfExtendedReal(2) == InfExtendedReal(2)
@test -InfExtendedReal(2) == InfExtendedReal(-2)

@test InfExtendedReal(2) + InfExtendedReal(3) == InfExtendedReal(5)
@test InfExtendedReal(3) - InfExtendedReal(2) == InfExtendedReal(1)
@test InfExtendedReal(2) * InfExtendedReal(3) == InfExtendedReal(6)
@test InfExtendedReal{Int}(∞) * InfExtendedReal{Int}(∞) == InfExtendedReal{Int}(∞)
@test_throws DivideError InfExtendedReal(0) * InfExtendedReal{Int}(∞)
@test InfExtendedReal(10) / InfExtendedReal(5) == InfExtendedReal(2)
@test_throws DivideError InfExtendedReal{Int}(∞) / InfExtendedReal{Int}(∞)
@test InfExtendedReal(10) / InfExtendedReal{Int}(∞) == InfExtendedReal(0)

@test abs(InfExtendedReal(-5)) == InfExtendedReal(5)

@test InfExtendedReal(1) // InfExtendedReal(0) == InfExtendedReal(1//0)
@test_throws DivideError InfExtendedReal{Int}(∞) // InfExtendedReal{Int}(∞)
@test InfExtendedReal(1) // 0 == InfExtendedReal(1//0)
@test 1 // InfExtendedReal(0) == InfExtendedReal(1//0)
end
end
11 changes: 7 additions & 4 deletions test/infextendedtime.jl
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,11 @@ test_time = Time(1, 1, 1, 1)
end

@testset "Conversion" begin
@test promote_type(InfExtendedTime{DateTime}, InfExtendedTime{Date}) ==
@test promote_rule(InfExtendedTime{DateTime}, InfExtendedTime{Date}) ==
InfExtendedTime{DateTime}
@test promote_type(InfExtendedTime{DateTime}, Date) == InfExtendedTime{DateTime}
@test promote_type(InfExtendedTime{Date}, Infinite) == InfExtendedTime{Date}
@test promote_type(Infinite, Date) == InfExtendedTime{Date}
@test promote_rule(InfExtendedTime{DateTime}, Date) == InfExtendedTime{DateTime}
@test promote_rule(InfExtendedTime{Date}, Infinite) == InfExtendedTime{Date}
@test promote_rule(Infinite, Date) == InfExtendedTime{Date}

@test convert(InfExtendedTime{Date}, test_datetime) ==
InfExtendedTime{Date}(test_date)
Expand Down Expand Up @@ -149,14 +149,17 @@ test_time = Time(1, 1, 1, 1)
@test ninf < ∞
@test inf <= ∞
@test ninf <= -∞
@test -∞ <= ninf
end

@testset "Arithmetic" begin
@test typemin(InfExtendedTime{Date}) == InfExtendedTime{Date}(-∞)
@test typemax(InfExtendedTime{Date}) == InfExtendedTime{Date}(∞)

@test InfExtendedTime(test_date) + Day(1) == InfExtendedTime(test_date + Day(1))
@test Day(1) + InfExtendedTime(test_date) == InfExtendedTime(test_date + Day(1))
@test InfExtendedTime(test_date) - Day(1) == InfExtendedTime(test_date - Day(1))
@test Day(1) - InfExtendedTime(test_date) == InfExtendedTime(test_date - Day(1))
@test InfExtendedTime{Date}(∞) + Day(1) == InfExtendedTime{Date}(∞)
@test InfExtendedTime{Date}(∞) - Day(1) == InfExtendedTime{Date}(∞)
@test InfExtendedTime{Date}(-∞) + Day(1) == InfExtendedTime{Date}(-∞)
Expand Down
Loading