Skip to content

Commit

Permalink
Merge pull request #13 from fchorney/fc/bitstype
Browse files Browse the repository at this point in the history
Make InfExtendedReal a bitstype
  • Loading branch information
cjdoris committed Jun 14, 2020
2 parents f0f7037 + 5e14363 commit 9250c38
Show file tree
Hide file tree
Showing 10 changed files with 334 additions and 107 deletions.
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)

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

2 comments on commit 9250c38

@cjdoris
Copy link
Owner Author

Choose a reason for hiding this comment

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

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

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

Registration pull request created: JuliaRegistries/General/16364

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v0.2.0 -m "<description of version>" 9250c3814794d99403bfbe195c435a7518bcb6d3
git push origin v0.2.0

Please sign in to comment.