Skip to content

Commit

Permalink
Add FPTNException (#3)
Browse files Browse the repository at this point in the history
  • Loading branch information
timholy authored Oct 9, 2023
1 parent 64f17fb commit 4f9cc84
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 18 deletions.
31 changes: 25 additions & 6 deletions ThickNumbersInterfaceTests/src/ThickNumbersInterfaceTests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,17 @@ function test_reserved(@nospecialize(T=Float64))
@test_throws MethodError isnan_tn(T(1))
end

function test_required(@nospecialize(TN), @nospecialize(Ts=nothing))
function test_required(f::Function, @nospecialize(TN), @nospecialize(Ts=nothing))
lo, hi = 1/3, nextfloat(2/3)
x = lohi(TN, lo, hi)
x = f(TN, lo, hi)
@test typeof(x) <: TN
@test lo x
@test hi x
@test lo loval(x)
@test hi hival(x)
if Ts !== nothing
for T in Ts
for (x, l, h) in ((lohi(TN{T}, lo, hi), lo, hi), (lohi(TN{T}, 1, 2.0), 1.0, 2.0))
for (x, l, h) in ((f(TN{T}, lo, hi), lo, hi), (f(TN{T}, 1, 2.0), 1.0, 2.0))
@test typeof(x) <: TN{T}
@test valuetype(x) === valuetype(typeof(x)) === T
@test T(l) x
Expand All @@ -45,10 +45,11 @@ function test_required(@nospecialize(TN), @nospecialize(Ts=nothing))
end
end
end
test_required(@nospecialize(TN::Type), @nospecialize(Ts=nothing)) = test_required(lohi, TN, Ts)

function test_optional(@nospecialize(TN), @nospecialize(Ts=nothing))
function test_optional(f::Function, @nospecialize(TN::Type), @nospecialize(Ts=nothing))
m, r = 1/2, 1/6
x = midrad(TN, m, r)
x = f(TN, m, r)
@test typeof(x) <: TN
@test m x && m mid(x)
@test r <= rad(x) && r rad(x)
Expand All @@ -58,13 +59,31 @@ function test_optional(@nospecialize(TN), @nospecialize(Ts=nothing))
end
if Ts !== nothing
for T in Ts
x = midrad(TN{T}, m, r)
x = f(TN{T}, m, r)
@test typeof(x) <: TN{T}
@test valuetype(x) === valuetype(typeof(x)) === T
@test T(m) x && T(m) mid(x)
@test T(r) <= rad(x) && T(r) rad(x)
end
end
end
test_optional(@nospecialize(TN::Type), @nospecialize(Ts=nothing)) = test_optional(midrad, TN, Ts)

function test_FPTNviolations(X::ThickNumber)
@test_throws FPTNException X == X
@test_throws FPTNException isequal(X, X)
@test_throws FPTNException X X
@test_throws FPTNException X < X
@test_throws FPTNException isless(X, X)
@test_throws FPTNException X > X
@test_throws FPTNException X <= X
@test_throws FPTNException X >= X
@test_throws FPTNException X X
@test_throws FPTNException X X
@test_throws FPTNException isfinite(X)
@test_throws FPTNException isinf(X)
@test_throws FPTNException isnan(X)

end

end # module ThickNumbersInterfaceTests
1 change: 1 addition & 0 deletions ThickNumbersInterfaceTests/test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ using IntervalArith
ThickNumbersInterfaceTests.test_required(Interval, [Float32, Float64])
ThickNumbersInterfaceTests.test_optional(Interval{Float64})
ThickNumbersInterfaceTests.test_optional(Interval, [Float32, Float64])
ThickNumbersInterfaceTests.test_FPTNviolations(Interval(1.0, 2.0))
end

filter!(LOAD_PATH) do path
Expand Down
44 changes: 37 additions & 7 deletions src/ThickNumbers.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module ThickNumbers

export ThickNumber
export ThickNumber, FPTNException

# Traits
export valuetype
Expand All @@ -17,15 +17,31 @@ export lohi, midrad, loval, hival, mid, wid, rad, mag, mig
export emptyset, hull, issubset_tn, , is_strict_subset_tn, , issupset_tn, , is_strict_supset_tn,

# Operators
export isequal_tn, iseq_tn, , isapprox_tn, , isless_tn, , , ,
export isequal_tn, iseq_tn, , isapprox_tn, , isless_tn, , , ,

# Unary
export isfinite_tn, isinf_tn, isnan_tn

# Types and traits
# Types

abstract type ThickNumber{T<:Number} <: Number end

struct FPTNException
f
frep

FPTNException(@nospecialize(f), @nospecialize(frep=nothing)) = new(f, frep)
end

function Base.showerror(io::IO, err::FPTNException)
print(io, "FPTNException: ", err.f, " is deliberately not implemented for ThickNumber objects (see documentation).")
if err.frep !== nothing
print(io, "\nConsider using ", err.frep, " instead.")
end
end

# Traits

"""
valuetype(::Type{<:ThickNumber})
Expand Down Expand Up @@ -250,20 +266,24 @@ Base.promote_rule(::Type{ThickNumber{T}}, ::Type{ThickNumber{S}}) where {T<:Numb
Returns `true` if all values in `x` are finite, `false` otherwise.
"""
isfinite_tn(a::ThickNumber) = isfinite(loval(a)) & isfinite(hival(a))
Base.isfinite(::ThickNumber) = throw(FPTNException(isfinite, isfinite_tn))

"""
isinf_tn(x::ThickNumber)
Returns `true` if any value in `x` is infinite, `false` otherwise.
"""
isinf_tn(a::ThickNumber) = isinf(loval(a)) | isinf(hival(a))
Base.isinf(::ThickNumber) = throw(FPTNException(isinf, isinf_tn))

"""
isnan_tn(x::ThickNumber)
Returns `true` if any value in `x` is NaN, `false` otherwise.
"""
isnan_tn(a::ThickNumber) = isnan(loval(a)) | isnan(hival(a))
Base.isnan(::ThickNumber) = throw(FPTNException(isnan, isnan_tn))


Base.typemin(::Type{TN}) where TN<:ThickNumber{T} where T<:Number = TN(typemin(T), typemin(T))
Base.typemax(::Type{TN}) where TN<:ThickNumber{T} where T<:Number = TN(typemax(T), typemax(T))
Expand Down Expand Up @@ -296,6 +316,7 @@ function issubset_tn(a::ThickNumber, b::ThickNumber)
loval(b) loval(a) && hival(a) hival(b)
end
const = issubset_tn
Base.issubset(a::ThickNumber) = throw(FPTNException("issubset (or ⊆)", "issubset_tn (or ⫃)"))

"""
is_strict_subset_tn(a::ThickNumber, b::ThickNumber)
Expand All @@ -307,7 +328,7 @@ Returns `true` if `a` is a strict subset of `b`, `false` otherwise.
See documentation for why this is not just `⊂`.
"""
function is_strict_subset_tn(a::ThickNumber, b::ThickNumber)
a == b && return false
a b && return false
return issubset_tn(a, b)
end
const = is_strict_subset_tn
Expand All @@ -320,6 +341,7 @@ The converse of [`issubset_tn`](@ref).
"""
issupset_tn(a::ThickNumber, b::ThickNumber) = issubset_tn(b, a)
const = issupset_tn
Base.:()(a::ThickNumber) = throw(FPTNException(, ))

"""
is_strict_supset_tn(a::ThickNumber, b::ThickNumber)
Expand Down Expand Up @@ -367,19 +389,21 @@ hull(a::TN, b::TN) where TN<:ThickNumber =
Returns `true` if `a` and `b` are both empty or both `loval` and `hival` are equal in the sense of `isequal`. It is `false` otherwise.
"""
isequal_tn(a::ThickNumber, b::ThickNumber) = (isempty(a) & isempty(b)) | (isequal(loval(a), loval(b)) & isequal(hival(a), hival(b)))
Base.isequal(::ThickNumber, ::ThickNumber) = throw(FPTNException(isequal, isequal_tn))

"""
iseq_tn(a::ThickNumber, b::ThickNumber)
a ⩦ b
a ≐ b (`\\doteq`-TAB`)
Returns `true` if `a` and `b` are both empty or both `loval` and `hival` are equal in the sense of `==`. It is `false` otherwise.
"""
iseq_tn(a::ThickNumber, b::ThickNumber) = (isempty(a) & isempty(b)) | ((loval(a) == loval(b)) & (hival(a) == hival(b)))
const = iseq_tn
const = iseq_tn
Base.:(==)(::ThickNumber, ::ThickNumber) = throw(FPTNException(==, "≐ (\\doteq-TAB) or iseq_tn"))

"""
isapprox_tn(a::ThickNumber, b::ThickNumber; atol=0, rtol::Real=atol>0 ? 0 : √eps)
a ⩪ b
a ⩪ b (`\\dotsim`-TAB)
Returns `true` if `a` and `b` are both empty or both `loval` and `hival` are approximately equal (≈). It is `false` otherwise.
"""
Expand All @@ -393,41 +417,47 @@ function Base.rtoldefault(x::Union{TN1,Type{TN1}}, y::Union{TN2,Type{TN2}}, atol
end
Base.rtoldefault(::Type{TN}) where TN<:ThickNumber{T} where T = Base.rtoldefault(T)
const = isapprox_tn
Base.isapprox(::ThickNumber, ::ThickNumber; kwargs...) = throw(FPTNException("isapprox (or ≈)", "isapprox_tn (or ⩪, \\dotsim-TAB)"))

"""
isless_tn(a::ThickNumber, b::ThickNumber)
Returns `true` if `isless(hival(a), loval(b))`, `false` otherwise. See also [`≺`](@ref).
"""
isless_tn(a::ThickNumber, b::ThickNumber) = isless(hival(a), loval(b))
Base.isless(::ThickNumber, ::ThickNumber) = throw(FPTNException(isless, isless_tn))

"""
a ≺ b
Returns `true` if `hival(a) < loval(b)`, `false` otherwise. Use `\\prec`-TAB to type.
"""
(a::ThickNumber, b::ThickNumber) = hival(a) < loval(b)
Base.:(<)(::ThickNumber, ::ThickNumber) = throw(FPTNException(<, "≺ (\\prec-TAB)"))

"""
a ≻ b
Returns `true` if `loval(a) > hival(b)`, `false` otherwise. Use `\\succ`-TAB to type.
"""
(a::ThickNumber, b::ThickNumber) = hival(a) > loval(b)
Base.:(>)(::ThickNumber, ::ThickNumber) = throw(FPTNException(>, "≻ (\\succ-TAB)"))

"""
a ≼ b
Returns `true` if `hival(a) ≤ loval(b)`, `false` otherwise. Use `\\preceq`-TAB to type.
"""
(a::ThickNumber, b::ThickNumber) = hival(a) < loval(b)
Base.:(<=)(::ThickNumber, ::ThickNumber) = throw(FPTNException(<=, "⪯ (\\preceq-TAB)"))

"""
a ≽ b
Returns `true` if `loval(a) ≥ hival(b)`, `false` otherwise. Use `\\succeq`-TAB to type.
"""
(a::ThickNumber, b::ThickNumber) = hival(a) > loval(b)
Base.:(>=)(::ThickNumber, ::ThickNumber) = throw(FPTNException(>=, "⪰ (\\succeq-TAB)"))


## Unary + and -
Expand Down
31 changes: 26 additions & 5 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,41 @@ using IntervalArith
@testset "ThickNumbers generics" begin
# Test all the operations defined in ThickNumbers
@test isequal_tn(Interval(1, 2), Interval(1, 2))
@test_throws FPTNException isequal(Interval(1, 2), Interval(1, 2))
@test !isequal_tn(Interval(1, 2), Interval(1, 3))
@test Interval(1, 2) Interval(1, 2)
@test Interval(1, 2) Interval(1, 2)
@test_throws FPTNException Interval(1, 2) == Interval(1, 2)
exc = try
Interval(1, 2) == Interval(1, 3)
nothing
catch e
e
end
str = sprint(Base.showerror, exc)
@test occursin("== is deliberately not implemented", str)
@test occursin("", str)
@test occursin("doteq", str)
@test isapprox_tn(Interval(1, 2), Interval(1, nextfloat(2.0)))
@test Interval(1, 2) Interval(1, nextfloat(2.0))
@test_throws FPTNException Interval(1, 2) Interval(1, nextfloat(2.0))
@test !isapprox_tn(Interval(1, 2), Interval(1, 2.1))
@test isless_tn(Interval(1, 2), Interval(nextfloat(2.0), 3))
@test_throws FPTNException isless(Interval(1, 2), Interval(nextfloat(2.0), 3))
@test Interval(1, 2) Interval(nextfloat(2.0), 3)
@test_throws FPTNException Interval(1, 2) < Interval(nextfloat(2.0), 3)
@test Interval(nextfloat(2.0), 3) Interval(1, 2)
@test_throws FPTNException Interval(nextfloat(2.0), 3) > Interval(1, 2)
@test !isless_tn(Interval(1, 2), Interval(1, 2))
@test !isless_tn(Interval(1, 2), Interval(2, 3))
@test issubset_tn(Interval(1, 2), Interval(1, 2))
@test Interval(1, 2) Interval(1, 2)
@test_throws FPTNException issubset(Interval(1, 2), Interval(1, 2))
@test issubset_tn(Interval(1, 2), Interval(0, 3))
@test !issubset_tn(Interval(1, 2), Interval(0, 1))
@test is_strict_subset_tn(Interval(1, 2), Interval(0, 3))
@test !is_strict_subset_tn(Interval(1, 2), Interval(1, 2))
@test issupset_tn(Interval(1, 2), Interval(1, 2))
@test_throws FPTNException Interval(1, 2) Interval(1, 2)
@test issupset_tn(Interval(0, 3), Interval(1, 2))
@test !issupset_tn(Interval(0, 1), Interval(1, 2))
@test is_strict_supset_tn(Interval(0, 3), Interval(1, 2))
Expand All @@ -48,26 +66,29 @@ using IntervalArith
@test emptyset(Interval{Float32}) === Interval(Inf32, -Inf32)
@test emptyset(Interval{Float64}) === Interval(Inf, -Inf)
@test isfinite_tn(Interval(1, 2))
@test_throws FPTNException isfinite(Interval(1, 2))
@test !isfinite_tn(Interval(1, Inf))
@test !isfinite_tn(Interval(-Inf, 2))
@test !isfinite_tn(Interval(-Inf, Inf))
@test isinf_tn(Interval(1, Inf))
@test_throws FPTNException isinf(Interval(1, Inf))
@test isinf_tn(Interval(-Inf, 2))
@test isinf_tn(Interval(-Inf, Inf))
@test !isinf_tn(Interval(1, 2))
@test isnan_tn(Interval(NaN, 2))
@test_throws FPTNException isnan(Interval(NaN, 2))
@test isnan_tn(Interval(1, NaN))
@test isnan_tn(Interval(NaN, NaN))
@test !isnan_tn(Interval(1, 2))
@test !isnan_tn(Interval(1, Inf))

x = Interval(1.0, 3.0)
@test valuetype(x) === valuetype(typeof(x)) === Float64
@test typemin(x) Interval(-Inf, -Inf)
@test typemax(x) Interval(Inf, Inf)
@test typemin(x) Interval(-Inf, -Inf)
@test typemax(x) Interval(Inf, Inf)

@test x +x
@test -x Interval(-3.0, -1.0)
@test x +x
@test -x Interval(-3.0, -1.0)
y = Interval(0x00, 0xff)
@test_throws OverflowError -y
end
Expand Down

0 comments on commit 4f9cc84

Please sign in to comment.