diff --git a/ext/IntervalArithmeticLinearAlgebraExt.jl b/ext/IntervalArithmeticLinearAlgebraExt.jl index 15f35e0d9..7dae8fb1e 100644 --- a/ext/IntervalArithmeticLinearAlgebraExt.jl +++ b/ext/IntervalArithmeticLinearAlgebraExt.jl @@ -235,6 +235,22 @@ function LinearAlgebra.mul!(C::AbstractMatrix{<:RealOrComplexI}, A::AbstractVecO return _mul!(IntervalArithmetic.default_matmul(), C, A, B, α, β) end +# disambiguate from the LinearAlgebra methods for structured matrices of Julia v1.10 +if VERSION < v"1.11" + const _StructuredMatrix = Union{LinearAlgebra.Bidiagonal,LinearAlgebra.Diagonal,LinearAlgebra.SymTridiagonal,LinearAlgebra.Tridiagonal} + + for (CT, AT, BT) ∈ ( + (:(AbstractMatrix{<:RealOrComplexI}), :AbstractMatrix, :_StructuredMatrix), + (:(AbstractMatrix{<:RealOrComplexI}), :_StructuredMatrix, :AbstractMatrix), + (:(AbstractMatrix{<:RealOrComplexI}), :_StructuredMatrix, :_StructuredMatrix), + (:(AbstractVector{<:RealOrComplexI}), :_StructuredMatrix, :AbstractVector)) + @eval function LinearAlgebra.mul!(C::$CT, A::$AT, B::$BT, α::Number, β::Number) + size(A, 2) == size(B, 1) || return throw(DimensionMismatch("The number of columns of A must match the number of rows of B.")) + return _mul!(IntervalArithmetic.default_matmul(), C, A, B, α, β) + end + end +end + # LinearAlgebra.mul!(C::AbstractVecOrMat{<:RealOrComplexI}, A::AbstractMatrix{<:RealOrComplexI}, B::AbstractVecOrMat{<:RealOrComplexI}) = diff --git a/src/IntervalArithmetic.jl b/src/IntervalArithmetic.jl index 4e6e72f9a..f84b782c3 100644 --- a/src/IntervalArithmetic.jl +++ b/src/IntervalArithmetic.jl @@ -267,9 +267,9 @@ Random.rand(rng::Random.AbstractRNG, ::Random.SamplerType{Interval{T}}) where {T sample(x::Interval) = sample(Random.default_rng(), x) -function sample(rng::Random.AbstractRNG, x::Interval{T}) where {T<:NumTypes} +function sample(rng::Random.AbstractRNG, x::Interval{T}) where {T<:AbstractFloat} lo, hi = bounds(x) - β = rand(rng, float(T)) + β = rand(rng, T) lo = ifelse(lo == typemin(T), _value_min(T), lo) hi = ifelse(hi == typemax(T), _value_max(T), hi) val = convert(T, (1 - β) * lo + β * hi) @@ -281,9 +281,6 @@ end _value_min(::Type{T}) where {T<:AbstractFloat} = floatmin(T) _value_max(::Type{T}) where {T<:AbstractFloat} = floatmax(T) -_value_min(::Type{Rational{T}}) where {T<:Integer} = convert(Rational{T}, typemin(T)) -_value_max(::Type{Rational{T}}) where {T<:Integer} = convert(Rational{T}, typemax(T)) - export sample # diff --git a/src/display.jl b/src/display.jl index 2cdb2cee9..ffdad0f97 100644 --- a/src/display.jl +++ b/src/display.jl @@ -328,8 +328,11 @@ function _round_string(x::AbstractFloat, sigdigits::Int) return str end -_round_string(x::AbstractFloat, sigdigits::Int, mode::RoundingMode) = - _round_string(round(x, mode; sigdigits = sigdigits), sigdigits) +# print as Julia Base whenever the shortest representation fits within `sigdigits` +function _round_string(x::AbstractFloat, sigdigits::Int, mode::RoundingMode) + _count_sigdigits(string(x)) ≤ sigdigits && return _round_string(x, sigdigits) + return _round_string(round(x, mode; sigdigits = sigdigits), sigdigits) +end _count_sigdigits(s::AbstractString) = length(replace(split(s, r"[eE]")[1], '-' => "", '.' => "", r"^0+" => "")) diff --git a/src/intervals/arithmetic/trigonometric.jl b/src/intervals/arithmetic/trigonometric.jl index 113aad7aa..dcdd9bf30 100644 --- a/src/intervals/arithmetic/trigonometric.jl +++ b/src/intervals/arithmetic/trigonometric.jl @@ -501,18 +501,20 @@ Base.atan(x::Complex{Interval{T}}) where {T<:NumTypes} = acot(::BareInterval) acot(::Interval) -This function is not part of the IEEE Standard 1788-2015. +This function is not part of the IEEE Standard 1788-2015. It uses the convention +`acot(0) = π/2` and the jump at 0 is decorated like the branch cut of the +two-argument `atan` described as `atan2` in the IEEE Standard 1788-2015 +(Table 9.1). """ function Base.acot(x::BareInterval{T}) where {T<:AbstractFloat} isempty_interval(x) && return x lo, hi = bounds(x) - if lo < 0 < hi || lo == hi == 0 + if lo < 0 && hi ≥ 0 HALF_PI_HI = sup(_half_pi(T)) return _unsafe_bareinterval(T, -HALF_PI_HI, HALF_PI_HI) elseif lo == 0 + hi == 0 && return _half_pi(T) return @round(T, acot(hi), +sup(_half_pi(T))) - elseif hi == 0 - return @round(T, -sup(_half_pi(T)), acot(lo)) else return @round(T, acot(hi), acot(lo)) end @@ -520,7 +522,13 @@ end Base.acot(x::BareInterval{<:Rational}) = acot(float(x)) -# automatically defined for `Interval` since it is a subtype of `Real` +function Base.acot(x::Interval) + bx = bareinterval(x) + r = acot(bx) + d = min(decoration(x), decoration(r)) + d = min(d, ifelse(in_interval(0, bx), ifelse(inf(bx) < 0, def, dac), d)) + return _unsafe_interval(r, d, isguaranteed(x)) +end """ atan(::BareInterval, ::BareInterval) @@ -588,7 +596,7 @@ function Base.atan(y::Interval, x::Interval) ifelse(in_interval(0, by), ifelse(in_interval(0, bx), trv, - ifelse(sup(bx) < 0, ifelse(inf(by) < 0, def, d), d)), + ifelse(sup(bx) < 0, ifelse(inf(by) < 0, def, dac), d)), d)) t = isguaranteed(y) & isguaranteed(x) return _unsafe_interval(r, d, t) diff --git a/src/intervals/rounding.jl b/src/intervals/rounding.jl index e84142e2a..9f8aae538 100644 --- a/src/intervals/rounding.jl +++ b/src/intervals/rounding.jl @@ -138,15 +138,19 @@ for f ∈ (:cbrt, :exp, :exp2, :exp10, :expm1, # exponential end end + # if CRlibm has no routine, dispatch falls back to the shared `:correct` methods above if f ∈ CRlibm.functions @eval _fround(::typeof($f), ::IntervalRounding{:correct}, x::Float16, r::RoundingMode) = Float16(_fround($f, Float64(x), r), r) @eval _fround(::typeof($f), ::IntervalRounding{:correct}, x::Union{Float32,Float64}, r::RoundingMode) = CRlibm.$f(x, r) end - @eval _fround(::typeof($f), ::IntervalRounding{:ulp}, x::Union{Float16,Float32,Float64}, ::RoundingMode{:Down}) = prevfloat(CoreMath.$coremath_f(x)) - @eval _fround(::typeof($f), ::IntervalRounding{:ulp}, x::Union{Float16,Float32,Float64}, ::RoundingMode{:Up}) = nextfloat(CoreMath.$coremath_f(x)) + # if CoreMath has no routine, dispatch falls back to the shared `:correct` methods above + if f ∈ CoreMath.univariate_functions + @eval _fround(::typeof($f), ::IntervalRounding{:ulp}, x::Union{Float16,Float32,Float64}, ::RoundingMode{:Down}) = prevfloat(CoreMath.$coremath_f(x)) + @eval _fround(::typeof($f), ::IntervalRounding{:ulp}, x::Union{Float16,Float32,Float64}, ::RoundingMode{:Up}) = nextfloat(CoreMath.$coremath_f(x)) + end - @eval _fround(::typeof($f), ::IntervalRounding{:none}, x::AbstractFloat, r::RoundingMode) = $f(x) + @eval _fround(::typeof($f), ::IntervalRounding{:none}, x::AbstractFloat, ::RoundingMode) = $f(x) end # 2-argument functions diff --git a/src/piecewise.jl b/src/piecewise.jl index fb301f06a..b534e542b 100644 --- a/src/piecewise.jl +++ b/src/piecewise.jl @@ -1,106 +1,78 @@ """ - Domain{LeftBound, RightBound}(lo, hi) + Domain{L,R}(lo, hi) -The domain of a function. - -`LeftBound` and `RightBound` must be symbols and are either `:closed` or -`:open` determining if the corresponding endpoint is (respectively) -included or not in the domain. - -If `hi > lo`, the domain is considered to be empty. +Domain of a real function. The type parameters `L` and `R` must be `:open` or +`:closed`, and determine whether the corresponding endpoint belongs to the +domain. A domain is empty whenever `hi < lo`, or `hi == lo` with an open +endpoint. """ -struct Domain{L, R, T, S} - lo::T - hi::S - - function Domain{L, R, T, S}(lo::T, hi::S) where {L, R, T, S} - (!(L ∈ (:open, :closed)) || !(R ∈ (:open, :closed))) && throw(ErrorException( - "Domain bound must be either :open or :closed, got $L and $R instead" - )) - return new{L, R, T, S}(lo, hi) +struct Domain{L,R,T,S} + lo :: T + hi :: S + + function Domain{L,R,T,S}(lo::T, hi::S) where {L,R,T,S} + (L ∈ (:open, :closed) && R ∈ (:open, :closed)) || + return throw(ArgumentError("Domain bound must be either :open or :closed, got $L and $R instead")) + return new{L,R,T,S}(lo, hi) end end -Domain{L, R}(lo::T, hi::S) where {T, S, L, R} = Domain{L, R, T, S}(lo, hi) -Domain(lo::Tuple, hi::Tuple) = Domain{lo[2], hi[2]}(lo[1], hi[1]) -Domain(X::Interval) = Domain{:closed, :closed}(inf(X), sup(X)) -Domain() = Domain{:open, :open}(Inf, -Inf) - -lowerbound(x::Domain{L, R}) where {L, R} = (x.lo, L) -upperbound(x::Domain{L, R}) where {L, R} = (x.hi, R) +Domain{L,R}(lo::T, hi::S) where {L,R,T,S} = Domain{L,R,T,S}(lo, hi) +Domain((lo, L)::Tuple, (hi, R)::Tuple) = Domain{L,R}(lo, hi) +Domain(x::Interval) = Domain{:closed,:closed}(inf(x), sup(x)) +Domain() = Domain{:open,:open}(Inf, -Inf) -inf(x::Domain) = x.lo -sup(x::Domain) = x.hi - -""" - rightof(val, lowerbound) +lowerbound(d::Domain{L,R}) where {L,R} = (d.lo, L) +upperbound(d::Domain{L,R}) where {L,R} = (d.hi, R) -Determine if a value is on the right of a lower bound. -""" -function rightof(x::Real, (val, bound)) - bound == :closed && return val <= x - return val < x -end +inf(d::Domain) = d.lo +sup(d::Domain) = d.hi -function rightof((val1, bound1), (val2, bound2)) - if val1 < val2 || (val1 == val2 && bound1 == bound2 == :closed) - return false - end +rightof(x::Real, (val, bound)::Tuple) = ifelse(bound === :closed, val ≤ x, val < x) - return true +# for two lower bounds: at equal values an open bound is positioned after a closed one +function rightof((val1, bound1)::Tuple, (val2, bound2)::Tuple) + val1 == val2 && return bound1 === :open && bound2 === :closed + return val1 > val2 end -""" - leftof(val, upperbound) +leftof(x::Real, (val, bound)::Tuple) = ifelse(bound === :closed, x ≤ val, x < val) -Determine if a value is on the left of an upper bound. -""" -function leftof(x::Real, (val, bound)) - bound == :closed && return x <= val - return x < val -end - -function leftof((val1, bound1), (val2, bound2)) - if val2 < val1 || (val1 == val2 && bound1 == bound2 == :closed) - return false - end - - return true +# for two upper bounds: at equal values an open bound is positioned before a closed one +function leftof((val1, bound1)::Tuple, (val2, bound2)::Tuple) + val1 == val2 && return bound1 === :open && bound2 === :closed + return val1 < val2 end function leftof(d1::Domain, d2::Domain) val1, bound1 = upperbound(d1) val2, bound2 = lowerbound(d2) - - val1 == val2 && return !(bound1 == bound2 == :closed) + val1 == val2 && return !(bound1 === bound2 === :closed) return val1 < val2 end in_domain(x::Real, domain::Domain) = rightof(x, lowerbound(domain)) && leftof(x, upperbound(domain)) -function intersect_domain(d1::Domain, d2::Domain) - left = max(lowerbound(d1), lowerbound(d2)) - right = min(upperbound(d1), upperbound(d2)) - - left > right && return Domain() - return Domain(left, right) +function isempty_domain(d::Domain{L,R}) where {L,R} + d.lo == d.hi && return !(L === R === :closed) + return d.hi < d.lo end -function isempty_domain(domain::Domain) - lo, lobound = lowerbound(domain) - hi, hibound = upperbound(domain) - - lo == hi && return !(lobound == hibound == :closed) - return lo > hi +function intersect_domain(d1::Domain, d2::Domain) + lo, L = ifelse(rightof(lowerbound(d1), lowerbound(d2)), lowerbound(d1), lowerbound(d2)) + hi, R = ifelse(leftof(upperbound(d1), upperbound(d2)), upperbound(d1), upperbound(d2)) + d = Domain{L,R}(lo, hi) + return isempty_domain(d) ? Domain() : d end """ Constant(value) -A constant function compatible with interval arithmetic. - -Return an interval containing only the value for an interval input, -and the value directly otherwise. +Constant function compatible with interval arithmetic: it wraps `value` into +an interval for an interval input, and returns `value` itself otherwise. In +contrast, `Returns(value)` from Base outputs `value` even for an interval +input, which shortcircuits the propagation of intervals and loses the +associated guarantee of correctness. ```jldoctest julia> using IntervalArithmetic @@ -116,24 +88,34 @@ julia> c(22.2) julia> c(interval(0, 1.3)) Interval{Float64}(1.2, 1.2, com, true) ``` - -Note that this is not equivalent to `Returns(value)` from base, -which always outputs `value`, even for an interval input. -This can shortcircuit the propagation of intervals in the computation -and lose the associated guarantee of correctness. """ struct Constant{T} - value::T + value :: T end -(constant::Constant)(::Any) = constant.value (constant::Constant)(::Interval) = interval(constant.value) +(constant::Constant)(::Real) = constant.value + """ - Piecewise(pairs... ; continuity = fill(-1, length(pairs) - 1)) + Piecewise(pairs::Pair...; continuity = ntuple(i -> -1, length(pairs) - 1)) + +Function defined by pieces, each pair mapping a [`Domain`](@ref) to a function. +Support both real and interval inputs. The domains must be ordered and +pairwise disjoint. For constant pieces, use [`Constant`](@ref) to preserve the +guarantee of correctness of interval inputs. -A function defined by pieces (each associating a domain to a function). -Support both intervals and standard numbers. +The `k`-th element of `continuity` gives the regularity of the function at the +junction between the `k`-th and `(k+1)`-th domains: +- `-1`: discontinuous; +- `n ≥ 0`: `n` times continuously differentiable; only relevant beyond `0` when + differentiating via ForwardDiff.jl. +It determines the decoration of an interval input spanning a junction; a +junction with a gap between the domains is always treated as discontinuous. + +An interval input not contained in the union of the domains yields the `trv` +decoration, and one disjoint from it yields the empty interval. A real input +outside every domain throws a `DomainError`. ```jldoctest julia> using IntervalArithmetic @@ -141,8 +123,8 @@ julia> using IntervalArithmetic julia> setdisplay(:full); julia> myabs = Piecewise( - Domain{:open, :closed}(-Inf, 0) => x -> -x, - Domain{:open, :open}(0, Inf) => identity + Domain{:open,:closed}(-Inf, 0) => x -> -x, + Domain{:open,:open}(0, Inf) => identity ); julia> myabs(-22.3) @@ -151,171 +133,118 @@ julia> myabs(-22.3) julia> myabs(interval(-5, 5)) Interval{Float64}(0.0, 5.0, def, true) ``` - -For constant pieces, it is recommended to use `Constant` -for full compatibility with intervals. - -The domains must be specified in increasing order and must not overlap. - -The `continuity` optional argument takes a vector of `N - 1` integers -(where `N` is the number of domains) -determining how the piecewise function behaves at the endpoints between -the subdomains. -The possibility are: -- `-1` : the function is discontinuous between the domains. -- `0` : the function is continuous but not differentiable between the domains. -- `n > 0` : the function is `n` times continuously differentiable between the - domains. This only matter when using `ForwardDiff` to compute derivative - of the function. - -This information is used to determine the decoration of intervals that -covers the endpoint of several domains. - -If an input interval goes outside the domain of definition of the piecewise -function, the output will always have the trivial (`trv`) decoration. -For standard number, it throws a `DomainError`. - -The piecewise function can have a gap between two pieces. -In this case, the `continuity` optional argument is ignored, -and interval spanning over the gap always as the `trv` decoration. """ -struct Piecewise{N, M, D<:NTuple{N, Domain}, F<:NTuple{N, Any}, S<:NTuple{M, Real}} - domains::D - fs::F - continuity::NTuple{M, Int} - singularities::S +struct Piecewise{N,M,D<:NTuple{N,Domain},F<:NTuple{N,Any},S<:NTuple{M,Real}} + domains :: D + fs :: F + continuity :: NTuple{M, Int} + singularities :: S function Piecewise( - domains::NTuple{N, Domain}, - fs::NTuple{N, Any}, - continuity::NTuple{M, Int}, - singularities::NTuple{M, Real}) where {N, M} + domains::NTuple{N,Domain}, + fs::NTuple{N,Any}, + continuity::NTuple{M,Int}, + singularities::NTuple{M,Real}) where {N,M} - N != M + 1 && throw(ArgumentError( - "a Piecewise function with N pieces must have N - 1 singularities. " * - "Given: $N pieces and $M singularities." - )) + N != M+1 && throw(ArgumentError( + "a Piecewise function with N pieces must have N - 1 singularities, got $N pieces and $M singularities.")) - return new{N, M, typeof(domains), typeof(fs), typeof(singularities)}(domains, fs, continuity, singularities) + return new{N,M,typeof(domains),typeof(fs),typeof(singularities)}(domains, fs, continuity, singularities) end end function Piecewise( - domains::NTuple{N, Domain}, - fs::NTuple{N, Any}, - continuity::NTuple{M, Int} = ntuple(i -> -1, Val(N-1))) where {N, M} + domains::NTuple{Nd,Domain}, + fs::NTuple{Nf,Any}, + continuity::NTuple{M,Integer} = ntuple(i -> -1, Val(Nd-1))) where {Nd,Nf,M} - if length(domains) != length(fs) - throw(ArgumentError("the number of domains and the number of functions don't match")) - end + Nd != Nf && return throw(ArgumentError("the number of domains and the number of functions don't match")) - if length(domains) - 1 != length(continuity) - n = length(domains) - throw(ArgumentError("$(length(sub)) junction points but $(n - 1) are expected based on the number of domains ($n)")) - end + Nd-1 != M && return throw(ArgumentError("$M junction points but $(Nd - 1) are expected based on the number of domains $Nd")) - for k ∈ 1:length(domains) - 1 - s1 = domains[k] - s2 = domains[k + 1] - - if !leftof(s1, s2) - throw(ArgumentError("domains are either not ordered or not disjoint")) - end + for k ∈ 1:Nd-1 + leftof(domains[k], domains[k+1]) || return throw(ArgumentError("domains are either not ordered or not disjoint")) end - singularities = sup.(domains[1:end-1]) - - return Piecewise(Tuple(domains), Tuple(fs), Tuple(continuity), Tuple(singularities)) + return Piecewise(domains, fs, continuity, sup.(domains[1:Nd-1])) end -function Piecewise( - pairs::Vararg{Pair, N} ; - continuity = ntuple(i -> -1, Val(N - 1))) where N - - return Piecewise(first.(pairs), last.(pairs), Tuple(continuity)) -end +Piecewise(pairs::Vararg{Pair,N}; continuity = ntuple(i -> -1, Val(N-1))) where {N} = + Piecewise(first.(pairs), last.(pairs), Tuple(continuity)) domains(piecewise::Piecewise) = piecewise.domains pieces(piecewise::Piecewise) = zip(domains(piecewise), piecewise.fs) -function discontinuities(piecewise::Piecewise, order = 0) - return [s for (s, C) ∈ zip(piecewise.singularities, piecewise.continuity) if C .< order] -end +discontinuities(piecewise::Piecewise, order::Integer = 0) = + [s for (s, C) ∈ zip(piecewise.singularities, piecewise.continuity) if C < order] -function domain_string(domain::Domain{L, R}) where {L, R} - left = (L == :closed) ? "[" : "(" - right = (R == :closed) ? "]" : ")" - - return "$left$(domain.lo), $(domain.hi)$right" -end - -function domain_string(piecewise::Piecewise) - join(domain_string.(domains(piecewise)), " ∪ ") -end - -function Base.show(io::IO, ::MIME"text/plain", piecewise::Piecewise) - n = length(pieces(piecewise)) - print(io, "Piecewise function with $n pieces:") +# +function (piecewise::Piecewise)(x::Real) for (domain, f) ∈ pieces(piecewise) - println(io) - print(io, " $(domain_string(domain)) -> $(repr(f))") - end -end - -function in_domain(domain, piecewise) - rightof(upperbound(domain), upperbound(domains(piecewise)[end])) && return false - - # This relies on the fact that domains are ordered - lo = lowerbound(domain) - - for domain ∈ domains(piecewise) - if !rightof(lo, lowerbound(domain)) - return false - end - - val, bound = upperbound(domain) - - val > upperbound(domain)[1] && break - - if bound == :closed - lo = (val, :open) - else - lo = (val, :closed) - end + in_domain(x, domain) && return f(x) end - - return true + return throw(DomainError(x, "piecewise function was called outside of its domain $(domain_string(piecewise))")) end -overlap_domain(domain, piecewise) = any(!isempty_domain, intersect_domain.(Ref(domain), domains(piecewise))) - function (piecewise::Piecewise)(X::Interval{T}) where {T} input_domain = Domain(X) - !overlap_domain(input_domain, piecewise) && return emptyinterval(T) + t = isguaranteed(X) + overlap_domain(input_domain, piecewise) || return _unsafe_interval(emptyinterval(BareInterval{T}), trv, t) if !in_domain(input_domain, piecewise) dec = trv - elseif any(x -> in_domain(x, input_domain), discontinuities(piecewise)) + elseif any(s -> in_domain(s, input_domain), discontinuities(piecewise)) dec = def else dec = com end - piece_outputs = Interval{T}[] + outputs = Interval{T}[] for (piece_domain, f) ∈ pieces(piecewise) piece_input = intersect_domain(input_domain, piece_domain) isempty_domain(piece_input) && continue - push!(piece_outputs, f(interval(inf(piece_input), sup(piece_input), decoration(X)))) + push!(outputs, f(_unsafe_interval(bareinterval(inf(piece_input), sup(piece_input)), decoration(X), t))) end - dec = min(dec, minimum(decoration.(piece_outputs))) - return IntervalArithmetic.setdecoration(reduce(hull, piece_outputs), dec) + dec = min(dec, minimum(decoration, outputs)) + return setdecoration(reduce(hull, outputs), dec) end -function (piecewise::Piecewise)(x::Real) +# + +# whether `domain` is contained in the union of the (ordered) domains of `piecewise` +function in_domain(domain, piecewise) + loval, lobound = lowerbound(domain) + hival, hibound = upperbound(domain) + for piece ∈ domains(piecewise) + supval, supbound = upperbound(piece) + (supval < loval || (supval == loval && !(lobound === supbound === :closed))) && continue + infval, infbound = lowerbound(piece) + (infval < loval || (infval == loval && (infbound === :closed || lobound === :open))) || return false + (hival < supval || (hival == supval && (supbound === :closed || hibound === :open))) && return true + loval, lobound = supval, ifelse(supbound === :closed, :open, :closed) + end + return false +end + +overlap_domain(domain, piecewise) = + any(d -> !isempty_domain(intersect_domain(domain, d)), domains(piecewise)) + +# + +function Base.show(io::IO, ::MIME"text/plain", piecewise::Piecewise) + print(io, "Piecewise function with $(length(domains(piecewise))) pieces:") for (domain, f) ∈ pieces(piecewise) - (in_domain(x, domain)) && return f(x) + println(io) + print(io, " $(domain_string(domain)) -> $(repr(f))") end - throw(DomainError(x, "piecewise function was called outside of its domain $(domain_string(piecewise))")) end + +function domain_string(d::Domain{L,R}) where {L,R} + left = ifelse(L === :closed, '[', '(') + right = ifelse(R === :closed, ']', ')') + return "$left$(d.lo), $(d.hi)$right" +end + +domain_string(piecewise::Piecewise) = join(domain_string.(domains(piecewise)), " ∪ ") diff --git a/test/ITF1788_tests/libieeep1788_elem.jl b/test/ITF1788_tests/libieeep1788_elem.jl index a98c27fbe..b214fbf9b 100644 --- a/test/ITF1788_tests/libieeep1788_elem.jl +++ b/test/ITF1788_tests/libieeep1788_elem.jl @@ -7144,8 +7144,7 @@ end @test atan(interval(bareinterval(-0.0, 1.0), com), interval(bareinterval(-0.0, -0.0), def)) === interval(bareinterval(0x1.921FB54442D18P+0, 0x1.921FB54442D19P+0), trv) - @warn "The original test `atan2 [-0.0, 1.0]_com [-2.0, -0.1]_com = [0X1.ABA397C7259DDP+0, 0X1.921FB54442D19P+1]_dac` is wrong and has been modified. The result should have the decoration `com`" - @test atan(interval(bareinterval(-0.0, 1.0), com), interval(bareinterval(-2.0, -0.1), com)) === interval(bareinterval(0x1.ABA397C7259DDP+0, 0x1.921FB54442D19P+1), com) + @test atan(interval(bareinterval(-0.0, 1.0), com), interval(bareinterval(-2.0, -0.1), com)) === interval(bareinterval(0x1.ABA397C7259DDP+0, 0x1.921FB54442D19P+1), dac) @test atan(interval(bareinterval(-0.0, 1.0), def), interval(bareinterval(-2.0, 0.0), com)) === interval(bareinterval(0x1.921FB54442D18P+0, 0x1.921FB54442D19P+1), trv) diff --git a/test/ITF1788_tests/libieeep1788_reduction.jl b/test/ITF1788_tests/libieeep1788_reduction.jl deleted file mode 100644 index 7588026df..000000000 --- a/test/ITF1788_tests/libieeep1788_reduction.jl +++ /dev/null @@ -1,45 +0,0 @@ -@testset "minimal_sum_test" begin - - @test sum([1.0, 2.0, 3.0]) === 6.0 - - @test isnan(sum([1.0, 2.0, NaN, 3.0])) - - @test isnan(sum([1.0, -Inf, 2.0, Inf, 3.0])) - -end - -@testset "minimal_sum_abs_test" begin - - @test sum(abs.([1.0, -2.0, 3.0])) === 6.0 - - @test isnan(sum(abs.([1.0, -2.0, NaN, 3.0]))) - - @test sum(abs.([1.0, -Inf, 2.0, Inf, 3.0])) === Inf - -end - -@testset "minimal_sum_sqr_test" begin - - @test sum([1.0, 2.0, 3.0].^2) === 14.0 - - @test isnan(sum([1.0, 2.0, NaN, 3.0].^2)) - - @test sum([1.0, -Inf, 2.0, Inf, 3.0].^2) === Inf - -end - -@testset "minimal_dot_test" begin - - @test sum(.*([1.0, 2.0, 3.0], [1.0, 2.0, 3.0])) === 14.0 - - @test_broken sum(.*([0x10000000000001p0, 0x1p104], [0x0fffffffffffffp0, -1.0])) === -1.0 - - @test isnan(sum(.*([1.0, 2.0, NaN, 3.0], [1.0, 2.0, 3.0, 4.0]))) - - @test isnan(sum(.*([1.0, 2.0, 3.0, 4.0], [1.0, 2.0, NaN, 3.0]))) - - @test isnan(sum(.*([1.0, 2.0, 0.0, 4.0], [1.0, 2.0, Inf, 3.0]))) - - @test isnan(sum(.*([1.0, 2.0, -Inf, 4.0], [1.0, 2.0, 0.0, 3.0]))) - -end diff --git a/test/IntervalArithmetic.jl b/test/IntervalArithmetic.jl new file mode 100644 index 000000000..f508bbfed --- /dev/null +++ b/test/IntervalArithmetic.jl @@ -0,0 +1,313 @@ +using Test +using IntervalArithmetic +using Random + +@testset "Type aliases" begin + @test RealOrComplexI{Float64} === Union{Interval{Float64},Complex{Interval{Float64}}} + @test Interval{Float64} <: RealOrComplexI{Float64} + @test Complex{Interval{Float64}} <: RealOrComplexI{Float64} + @test !(BareInterval{Float64} <: RealOrComplexI{Float64}) + + @test ComplexI{Float64} === Complex{Interval{Float64}} + @test !(Interval{Float64} <: ComplexI{Float64}) + + @test RealIntervalType{Float64} === Union{BareInterval{Float64},Interval{Float64}} + @test BareInterval{Float64} <: RealIntervalType{Float64} + @test Interval{Float64} <: RealIntervalType{Float64} + @test !(Complex{Interval{Float64}} <: RealIntervalType{Float64}) +end + +@testset "Exports" begin + expected = [Symbol("@I_str"), Symbol("@exact"), Symbol("@interval"), :BareInterval, + :ComplexI, :Constant, :Decoration, :Domain, :ExactReal, :Interval, + :IntervalArithmetic, :Overlap, :Piecewise, :RealIntervalType, :RealOrComplexI, + :bareinterval, :bisect, :bounds, :cancelminus, :cancelplus, :com, :dac, + :decoration, :def, :diam, :discontinuities, :dist, :domains, :emptyinterval, + :entireinterval, :exact, :extended_div, :fastpow, :fastpown, :has_exact_display, + :hull, :ill, :in_interval, :inf, :interiordiff, :intersect_interval, :interval, + :isatomic, :isbounded, :iscommon, :isdisjoint_interval, :isempty_interval, + :isentire_interval, :isequal_interval, :isguaranteed, :isinterior, :isnai, + :issetequal_interval, :isstrictless, :isstrictsubset, :issubset_interval, + :isthin, :isthininteger, :isthinone, :isthinzero, :isunbounded, :isweakless, + :mag, :mid, :midradius, :mig, :mince, :mince!, :nai, :numtype, :overlap, + :pieces, :pow, :pown, :precedes, :radius, :rootn, :sample, :setdisplay, + :strictprecedes, :sup, :trv, :union_interval] + @test sort(names(IntervalArithmetic)) == sort(expected) + + @test haskey(Base.Docs.meta(IntervalArithmetic), + Base.Docs.Binding(IntervalArithmetic, :IntervalArithmetic)) +end + +@testset "Type stability" begin + xs = [interval(3, 4), interval(0, 4), interval(0), interval(-4, 0), interval(-4, 4), + interval(-Inf, 4), interval(4, Inf), interval(-Inf, Inf)] + for T ∈ (Float32, Float64, BigFloat) + for x ∈ xs + xx = Interval{T}(x) + for y ∈ xs + yy = Interval{T}(y) + for op ∈ (+, -, *, /, atan) + @test @inferred(op(xx, yy)) isa Interval{T} + end + end + for op ∈ (sin, cos, exp, log, tan, abs) + @test @inferred(op(xx)) isa Interval{T} + end + for op ∈ (mid, diam) + @test @inferred(op(xx)) isa T + end + end + end +end + +@testset "Configuration defaults" begin + opts = IntervalArithmetic.configuration_options + @test opts isa IntervalArithmetic.ConfigurationOptions + @test opts.numtype === Float64 + @test opts.flavor === :set_based + @test opts.rounding === :correct + @test opts.power === :fast + @test opts.matmul === :fast + @test opts.nthreads > 0 + + @test sprint(show, MIME("text/plain"), opts) == string( + "Configuration options:\n", + " - bound type: Float64\n", + " - flavor: set_based\n", + " - interval rounding: correct\n", + " - power mode: fast\n", + " - matrix multiplication mode: fast\n", + " - number of threads for `:fast` matrix multiplication mode: ", opts.nthreads) + + @test IntervalArithmetic.default_numtype() === Float64 + @test IntervalArithmetic.default_flavor() === IntervalArithmetic.Flavor{:set_based}() + @test IntervalArithmetic.default_rounding() === IntervalArithmetic.IntervalRounding{:correct}() + @test IntervalArithmetic.default_power() === IntervalArithmetic.PowerMode{:fast}() + @test IntervalArithmetic.default_matmul() === IntervalArithmetic.MatMulMode{:fast}() + + @test IntervalArithmetic.MatMulMode isa UnionAll + @test sizeof(IntervalArithmetic.MatMulMode{:fast}()) == 0 + @test sizeof(IntervalArithmetic.MatMulMode{:slow}()) == 0 +end + +@testset "configure_ helpers" begin + try + @test IntervalArithmetic.configure_numtype(Float32) === Float32 + @test Base.invokelatest(IntervalArithmetic.default_numtype) === Float32 + finally + IntervalArithmetic.configure_numtype(Float64) + end + @test Base.invokelatest(IntervalArithmetic.default_numtype) === Float64 + + @test IntervalArithmetic.configure_flavor(:set_based) === :set_based + @test_throws ArgumentError("only the interval flavor `:set_based` is supported and implemented") IntervalArithmetic.configure_flavor(:foo) + + try + for s ∈ (:ulp, :none, :correct) + @test IntervalArithmetic.configure_rounding(s) === s + end + finally + IntervalArithmetic.configure_rounding(:correct) + end + @test_throws ArgumentError("only the rounding mode `:correct`, `:ulp` and `:none` are available") IntervalArithmetic.configure_rounding(:foo) + + try + for s ∈ (:slow, :fast) + @test IntervalArithmetic.configure_power(s) === s + end + finally + IntervalArithmetic.configure_power(:fast) + end + @test_throws ArgumentError("only the power mode `:slow` and `:fast` are available") IntervalArithmetic.configure_power(:foo) + + try + for s ∈ (:slow, :fast) + @test IntervalArithmetic.configure_matmul(s) === s + end + finally + IntervalArithmetic.configure_matmul(:fast) + end + @test_throws ArgumentError("only the matrix multiplication mode `:slow` and `:fast` are available") IntervalArithmetic.configure_matmul(:foo) +end + +@testset "Threads" begin + dt = IntervalArithmetic.default_threads() + @test dt ≥ 1 + if Sys.isapple() && Sys.ARCH === :aarch64 + @test dt == max(1, Sys.CPU_THREADS) + else + @test dt == max(1, Sys.CPU_THREADS ÷ 2) + end + + @test IntervalArithmetic._get_num_threads() isa Int + + @test_throws ArgumentError("the number of threads must be positive") IntervalArithmetic.configure_threads(0) + @test_throws ArgumentError("the number of threads must be positive") IntervalArithmetic.configure_threads(-1) + + if Int === Int64 + try + # the BLAS library may cap the effective number of threads + @test IntervalArithmetic.configure_threads(2) == IntervalArithmetic._get_num_threads() ≥ 1 + @test IntervalArithmetic._set_num_threads(3) == IntervalArithmetic._get_num_threads() ≥ 1 + finally + IntervalArithmetic.configure(nthreads = dt) + end + @test IntervalArithmetic.configuration_options.nthreads == IntervalArithmetic._get_num_threads() ≥ 1 + else + @test IntervalArithmetic._get_num_threads() == 1 + @test IntervalArithmetic._set_num_threads(5) == 1 + end +end + +@testset "configure" begin + opts = IntervalArithmetic.configuration_options + try + @test IntervalArithmetic.configure() === opts + @test opts.numtype === Float64 && opts.flavor === :set_based && + opts.rounding === :correct && opts.power === :fast && opts.matmul === :fast + + IntervalArithmetic.configure(numtype = Float32) + @test Base.invokelatest(interval, 1, 2) isa Interval{Float32} + IntervalArithmetic.configure(numtype = Float64) + @test Base.invokelatest(interval, 1, 2) isa Interval{Float64} + + IntervalArithmetic.configure(rounding = :none) + x = Base.invokelatest(() -> interval(0.1) + interval(0.2)) + @test inf(x) == sup(x) == 0.1 + 0.2 + + IntervalArithmetic.configure(rounding = :ulp) + x = Base.invokelatest(() -> interval(0.1) + interval(0.2)) + @test inf(x) == prevfloat(0.1 + 0.2) && sup(x) == nextfloat(0.1 + 0.2) + IntervalArithmetic.configure(rounding = :correct) + + @test IntervalArithmetic.configure(power = :slow).power === :slow + @test Base.invokelatest(IntervalArithmetic.default_power) === IntervalArithmetic.PowerMode{:slow}() + @test IntervalArithmetic.configure(power = :fast).power === :fast + @test Base.invokelatest(IntervalArithmetic.default_power) === IntervalArithmetic.PowerMode{:fast}() + + @test IntervalArithmetic.configure(matmul = :slow).matmul === :slow + @test Base.invokelatest(IntervalArithmetic.default_matmul) === IntervalArithmetic.MatMulMode{:slow}() + @test IntervalArithmetic.configure(matmul = :fast).matmul === :fast + @test Base.invokelatest(IntervalArithmetic.default_matmul) === IntervalArithmetic.MatMulMode{:fast}() + + if Int === Int64 + @test IntervalArithmetic.configure(nthreads = 2).nthreads == IntervalArithmetic._get_num_threads() ≥ 1 + end + + @test_throws TypeError IntervalArithmetic.configure(numtype = Int) + @test opts.numtype === Float64 + + # options are applied one at a time, so a later invalid one leaves earlier ones in place + @test_throws ArgumentError IntervalArithmetic.configure(numtype = Float32, rounding = :foo) + @test opts.numtype === Float32 + @test Base.invokelatest(IntervalArithmetic.default_numtype) === Float32 + finally + IntervalArithmetic.configure(numtype = Float64, flavor = :set_based, + rounding = :correct, power = :fast, matmul = :fast, + nthreads = IntervalArithmetic.default_threads()) + end + @test opts.numtype === Float64 && opts.flavor === :set_based && + opts.rounding === :correct && opts.power === :fast && opts.matmul === :fast + @test opts.nthreads == IntervalArithmetic._get_num_threads() ≥ 1 + @test Base.invokelatest(IntervalArithmetic.default_numtype) === Float64 +end + +@testset "rand" begin + x = rand(Interval{Float64}) + @test x isa Interval{Float64} + @test isthin(x) + @test decoration(x) === com && isguaranteed(x) + @test 0.0 ≤ inf(x) && sup(x) < 1.0 + + @test isequal_interval(rand(MersenneTwister(42), Interval{Float64}), + interval(rand(MersenneTwister(42), Float64))) + + v = rand(Interval{Float32}, 3) + @test v isa Vector{Interval{Float32}} + @test length(v) == 3 && all(isthin, v) + + y = rand(Interval{BigFloat}) + @test y isa Interval{BigFloat} && isthin(y) + + @test_throws MethodError rand(Interval{Rational{Int}}) +end + +@testset "sample" begin + x = interval(1, 2) + @test all(1:10^4) do _ + s = sample(x) + s isa Float64 && in_interval(s, x) + end + + @test sample(MersenneTwister(1), x) == sample(MersenneTwister(1), x) + @test sample(x) isa Float64 + + @test sample(interval(1, 1)) === 1.0 + + @test all(_ -> isfinite(sample(entireinterval())), 1:10^4) + + s = sample(interval(1.0, Inf)) + @test isfinite(s) && in_interval(s, interval(1.0, Inf)) + s = sample(interval(-Inf, 1.0)) + @test isfinite(s) && in_interval(s, interval(-Inf, 1.0)) + + @test isnan(sample(emptyinterval())) + @test isnan(sample(nai())) + + s = sample(interval(BigFloat, 1, 2)) + @test s isa BigFloat && 1 ≤ s ≤ 2 + + y = interval(prevfloat(1.0), 1.0) + @test all(1:10^3) do _ + s = sample(y) + s == prevfloat(1.0) || s == 1.0 + end + + s = sample(interval(1, 2) + 1) + @test s isa Float64 && 2 ≤ s ≤ 3 + + for T ∈ (Float16, Float32, Float64, BigFloat) + @test IntervalArithmetic._value_min(T) == floatmin(T) + @test IntervalArithmetic._value_max(T) == floatmax(T) + end + @test_throws MethodError sample(interval(1//2, 3//4)) +end + +@testset "Irrational bareinterval" begin + x = bareinterval(Float64, π) + @test x isa BareInterval{Float64} + @test inf(x) == 3.141592653589793 + @test sup(x) == 3.1415926535897936 == nextfloat(inf(x)) + @test @inferred(bareinterval(Float64, π)) isa BareInterval{Float64} + + for T ∈ (Float16, Float32, Float64) + y = bareinterval(T, π) + @test in_interval(π, y) + @test nextfloat(inf(y)) == sup(y) + end + @test in_interval(π, bareinterval(Rational{Int}, π)) + + for irr ∈ (MathConstants.ℯ, MathConstants.golden, MathConstants.γ, MathConstants.catalan) + for T ∈ (Float32, Float64, BigFloat) + y = bareinterval(T, irr) + @test in_interval(irr, y) + if T !== BigFloat + @test nextfloat(inf(y)) == sup(y) + end + end + end + + z = bareinterval(BigFloat, π) + @test precision(inf(z)) == precision(sup(z)) == precision(BigFloat) + @test in_interval(π, z) + @test nextfloat(inf(z)) == sup(z) + + setprecision(BigFloat, 128) do + z128 = bareinterval(BigFloat, π) + @test precision(inf(z128)) == precision(sup(z128)) == 128 + @test in_interval(π, z128) + @test nextfloat(inf(z128)) == sup(z128) + end + + @test_throws ArgumentError("only irrationals from MathConstants or IrrationalConstants.jl are supported") bareinterval(Float64, Base.Irrational{:foo}()) +end diff --git a/test/Project.toml b/test/Project.toml index 72321c6ef..e68da41c6 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -1,11 +1,16 @@ [deps] Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" Arblib = "fb37089c-8514-4489-9461-98f9c8763369" +DiffRules = "b552c78f-8df3-52c6-915a-8e097449b14b" ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240" IntervalArithmetic = "d1acc4aa-44c8-5952-acd4-ba5d80a2a253" IntervalSets = "8197267c-284f-5f27-9208-e0e47529a953" IrrationalConstants = "92d709cd-6900-40b7-9082-c6be49f344b6" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +Logging = "56ddb016-857b-54e1-b83d-db4d58db5568" Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" +Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" +SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" +TOML = "fa267f1f-6049-4f14-aa54-33bafae1ed76" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/test/aqua.jl b/test/aqua.jl index 3c585886d..18810e25f 100644 --- a/test/aqua.jl +++ b/test/aqua.jl @@ -1,26 +1,20 @@ using Test using IntervalArithmetic using Aqua +using TOML -@testset "Aqua tests (performance)" begin - # This tests that we don't accidentally run into - # https://github.com/JuliaLang/julia/issues/29393 - # Aqua.test_unbound_args(IntervalArithmetic) - ua = Aqua.detect_unbound_args_recursively(IntervalArithmetic) - @test length(ua) == 0 +@testset "Aqua" begin + # `test_ambiguities` is unreliable on Julia < 1.11 + Aqua.test_all(IntervalArithmetic; ambiguities = VERSION ≥ v"1.11") - # See: https://github.com/SciML/OrdinaryDiffEq.jl/issues/1750 - # Test that we're not introducing method ambiguities across deps - ambs = Aqua.detect_ambiguities(IntervalArithmetic; recursive = true) - pkg_match(pkgname, pkdir::Nothing) = false - pkg_match(pkgname, pkdir::AbstractString) = occursin(pkgname, pkdir) - filter!(x -> pkg_match("IntervalArithmetic", pkgdir(last(x).module)), ambs) - for method_ambiguity ∈ ambs - @show method_ambiguity - end - @test length(ambs) == 0 -end + @test isempty(Aqua.detect_unbound_args_recursively(IntervalArithmetic)) -@testset "Aqua tests (additional)" begin - Aqua.test_all(IntervalArithmetic; ambiguities = VERSION ≥ v"1.11") + # ignore ambiguities with packages loaded by other test files but foreign to the declared dependencies + project = TOML.parsefile(joinpath(pkgdir(IntervalArithmetic), "Project.toml")) + deps = union!(Set(["Base", "Core"]), keys(project["deps"]), keys(project["weakdeps"])) + known(m::Method) = String(nameof(Base.moduleroot(m.module))) ∈ deps || + startswith(String(nameof(Base.moduleroot(m.module))), "IntervalArithmetic") + ambiguities = Aqua.detect_ambiguities(IntervalArithmetic; recursive = true) + filter!(x -> all(known, x) && occursin("IntervalArithmetic", something(pkgdir(last(x).module), "")), ambiguities) + @test isempty(ambiguities) end diff --git a/test/display.jl b/test/display.jl new file mode 100644 index 000000000..4fd18797b --- /dev/null +++ b/test/display.jl @@ -0,0 +1,423 @@ +using Test +using IntervalArithmetic +using Random + +@testset "Display options" begin + d = IntervalArithmetic.display_options + @test d isa IntervalArithmetic.DisplayOptions + @test d.format === :infsup && d.decorations && d.ng_flag && d.sigdigits == 6 + @test setdisplay() === d + @test d.format === :infsup && d.decorations && d.ng_flag && d.sigdigits == 6 + try + setdisplay(:midpoint) + @test d.format === :midpoint && d.decorations && d.ng_flag && d.sigdigits == 6 + setdisplay(:infsup; sigdigits = 3) + @test d.format === :infsup && d.decorations && d.ng_flag && d.sigdigits == 3 + setdisplay(; decorations = false) + @test d.format === :infsup && !d.decorations && d.ng_flag && d.sigdigits == 3 + + @test_throws ArgumentError("`format` must be `:infsup`, `:midpoint` or `:full`") setdisplay(:foo) + @test d.format === :infsup && !d.decorations && d.ng_flag && d.sigdigits == 3 + @test_throws ArgumentError("`sigdigits` must be `≥ 1`") setdisplay(:infsup; sigdigits = 0) + @test_throws ArgumentError("`sigdigits` must be `≥ 1`") setdisplay(:infsup; sigdigits = -1) + @test d.sigdigits == 3 + + setdisplay(:infsup; sigdigits = 1) + @test d.sigdigits == 1 + + setdisplay(:infsup; decorations = true, ng_flag = true, sigdigits = 6) + @test sprint(show, MIME("text/plain"), d) == string( + "Display options:\n", + " - format: infsup\n", + " - decorations: true\n", + " - NG flag: true\n", + " - significant digits: 6") + setdisplay(:full) + @test sprint(show, MIME("text/plain"), d) == string( + "Display options:\n", + " - format: full\n", + " - decorations: true (ignored)\n", + " - NG flag: true (ignored)\n", + " - significant digits: 6 (ignored)") + finally + setdisplay(:infsup; decorations = true, ng_flag = true, sigdigits = 6) + end +end + +@testset "Printing entry points agree" begin + setdisplay(:infsup; decorations = true, ng_flag = true, sigdigits = 6) + for x ∈ (bareinterval(1, 2), interval(0.1, 0.3), interval(1, 2)/1, nai(), + complex(interval(1, 2), interval(2, 3))) + str = sprint(show, x) + @test sprint(show, MIME("text/plain"), x) == str + @test repr(x) == str + @test string(x) == str + @test sprint(print, x) == str + end + @test repr([interval(1, 2)]) == "Interval{Float64}[[1.0, 2.0]_com]" +end + +@testset "NaI" begin + illformed = @test_logs (:warn,) interval(1, -1) + try + for format ∈ (:infsup, :midpoint, :full) + setdisplay(format) + @test sprint(show, MIME("text/plain"), illformed) == "NaI" + for T ∈ (Float64, Float32, Float16, BigFloat, Rational{Int64}) + @test (@test_logs repr(nai(T))) == "NaI" + end + end + finally + setdisplay(:infsup; decorations = true, ng_flag = true, sigdigits = 6) + end +end + +setprecision(BigFloat, 256) do + @testset "BareInterval" begin + a = bareinterval(-floatmin(Float64), 1.3) + large_expo = bareinterval(0, BigFloat("1e123456789")) + try + setdisplay(:infsup; sigdigits = 6, decorations = true, ng_flag = true) + @test repr(emptyinterval(BareInterval{Float64})) == "∅" + @test repr(bareinterval(1, 2)) == "[1.0, 2.0]" + @test repr(entireinterval(BareInterval{Float64})) == "(-∞, ∞)" + @test repr(a) == "[-2.22508e-308, 1.3]" + @test repr(large_expo) == "[0.0, 1.0e+123456789]₂₅₆" + + setdisplay(; sigdigits = 20) + @test repr(a) == "[-2.2250738585072014e-308, 1.3]" + @test repr(large_expo) == "[0.0, 1.0000000000000000001e+123456789]₂₅₆" + + setdisplay(:full; sigdigits = 100) + @test repr(emptyinterval(BareInterval{Float64})) == "∅" + @test repr(bareinterval(0.1, 0.3)) == "BareInterval{Float64}(0.1, 0.3)" + @test repr(entireinterval(BareInterval{Float64})) == "BareInterval{Float64}(-Inf, Inf)" + @test repr(a) == "BareInterval{Float64}(-2.2250738585072014e-308, 1.3)" + @test repr(large_expo) == "BareInterval{BigFloat}(0.0, $(sup(large_expo)))" + + setdisplay(:midpoint; sigdigits = 6, decorations = false, ng_flag = false) + @test repr(emptyinterval(BareInterval{Float64})) == "∅" + @test repr(bareinterval(1, 2)) == "1.5 ± 0.5" + @test repr(a) == "0.65 ± 0.65" + @test repr(large_expo) == "(5.0e+123456788 ± 5.0e+123456788)₂₅₆" + + setdisplay(; decorations = true, ng_flag = true) + @test repr(bareinterval(1, 2)) == "1.5 ± 0.5" + @test repr(a) == "0.65 ± 0.65" + finally + setdisplay(:infsup; decorations = true, ng_flag = true, sigdigits = 6) + end + end + + @testset "Interval" begin + a = interval(1, 2) + a_NG = a/1 + b = interval(-floatmin(Float64), 1.3) + b32 = interval(-floatmin(Float32), parse(Float32, "1.3")) + b16 = interval(-floatmin(Float16), parse(Float16, "1.3")) + br = interval(Rational{Int64}, -11//10, 13//10) + c = interval(-1, Inf) + cr = interval(Rational{Int64}, -1//1, 1//0) + large_expo = interval(0, BigFloat("1e123456789")) + try + setdisplay(:infsup; sigdigits = 6, decorations = true, ng_flag = true) + @test repr(emptyinterval()) == "∅_trv" + @test repr(emptyinterval()/1) == "∅_trv_NG" + @test repr(a) == "[1.0, 2.0]_com" + @test repr(a_NG) == "[1.0, 2.0]_com_NG" + @test repr(b) == "[-2.22508e-308, 1.3]_com" + @test repr(b32) == "[-1.1755f-38, 1.3f0]_com" + @test repr(b16) == "[Float16(-6.104e-5), Float16(1.3)]_com" + @test repr(br) == "[-11//10, 13//10]_com" + @test repr(c) == "[-1.0, ∞)_dac" + @test repr(cr) == "[-1//1, ∞)_dac" + @test repr(large_expo) == "[0.0, 1.0e+123456789]₂₅₆_com" + @test repr(interval(0, 0)) == "[0.0, 0.0]_com" + @test repr(interval(-0.0, 0.0)) == "[0.0, 0.0]_com" + @test repr(entireinterval()) == "(-∞, ∞)_dac" + @test repr(interval(1, Inf)) == "[1.0, ∞)_dac" + @test repr(interval(-Inf, 1)) == "(-∞, 1.0]_dac" + + setdisplay(; decorations = false) + @test repr(emptyinterval()) == "∅" + @test repr(emptyinterval()/1) == "∅_NG" + @test repr(a) == "[1.0, 2.0]" + @test repr(a_NG) == "[1.0, 2.0]_NG" + @test repr(b) == "[-2.22508e-308, 1.3]" + @test repr(b32) == "[-1.1755f-38, 1.3f0]" + @test repr(b16) == "[Float16(-6.104e-5), Float16(1.3)]" + @test repr(br) == "[-11//10, 13//10]" + @test repr(c) == "[-1.0, ∞)" + @test repr(cr) == "[-1//1, ∞)" + @test repr(large_expo) == "[0.0, 1.0e+123456789]₂₅₆" + + setdisplay(; decorations = true, ng_flag = false) + @test repr(a_NG) == "[1.0, 2.0]_com" + @test repr(emptyinterval()/1) == "∅_trv" + + setdisplay(; sigdigits = 20, decorations = true, ng_flag = true) + @test repr(a) == "[1.0, 2.0]_com" + @test repr(a_NG) == "[1.0, 2.0]_com_NG" + @test repr(b) == "[-2.2250738585072014e-308, 1.3]_com" + @test repr(b32) == "[-1.1754944f-38, 1.3f0]_com" + @test repr(b16) == "[Float16(-6.104e-5), Float16(1.3)]_com" + @test repr(br) == "[-11//10, 13//10]_com" + @test repr(c) == "[-1.0, ∞)_dac" + @test repr(cr) == "[-1//1, ∞)_dac" + @test repr(large_expo) == "[0.0, 1.0000000000000000001e+123456789]₂₅₆_com" + + setdisplay(:full; sigdigits = 100, decorations = false) + @test repr(emptyinterval()) == "∅" + @test repr(emptyinterval()/1) == "∅_NG" + @test repr(a) == "Interval{Float64}(1.0, 2.0, com, true)" + @test repr(a_NG) == "Interval{Float64}(1.0, 2.0, com, false)" + @test repr(b) == "Interval{Float64}(-2.2250738585072014e-308, 1.3, com, true)" + @test repr(b32) == "Interval{Float32}(-1.1754944f-38, 1.3f0, com, true)" + @test repr(b16) == "Interval{Float16}(Float16(-6.104e-5), Float16(1.3), com, true)" + @test repr(br) == "Interval{Rational{Int64}}(-11//10, 13//10, com, true)" + @test repr(c) == "Interval{Float64}(-1.0, Inf, dac, true)" + @test repr(cr) == "Interval{Rational{Int64}}(-1//1, 1//0, dac, true)" + @test repr(large_expo) == "Interval{BigFloat}(0.0, $(sup(large_expo)), com, true)" + @test repr(interval(0.1, 0.3)) == "Interval{Float64}(0.1, 0.3, com, true)" + @test repr(entireinterval()) == "Interval{Float64}(-Inf, Inf, dac, true)" + + setdisplay(:midpoint; sigdigits = 6, decorations = true, ng_flag = true) + @test repr(emptyinterval()) == "∅_trv" + @test repr(emptyinterval()/1) == "∅_trv_NG" + @test repr(a) == "(1.5 ± 0.5)_com" + @test repr(a_NG) == "(1.5 ± 0.5)_com_NG" + @test repr(b) == "(0.65 ± 0.65)_com" + @test repr(b32) == "(0.65f0 ± 0.65f0)_com" + @test repr(b16) == "(Float16(0.65) ± Float16(0.65))_com" + @test repr(br) == "(1//10 ± 6//5)_com" + @test repr(c) == "(1.79769e+308 ± ∞)_dac" + @test repr(cr) == "(9223372036854775807//1 ± ∞)_dac" + @test repr(large_expo) == "(5.0e+123456788 ± 5.0e+123456788)₂₅₆_com" + @test repr(entireinterval()) == "(0.0 ± ∞)_dac" + + setdisplay(; decorations = false) + @test repr(emptyinterval()) == "∅" + @test repr(emptyinterval()/1) == "∅_NG" + @test repr(a) == "1.5 ± 0.5" + @test repr(a_NG) == "(1.5 ± 0.5)_NG" + @test repr(b) == "0.65 ± 0.65" + @test repr(b32) == "0.65f0 ± 0.65f0" + @test repr(b16) == "Float16(0.65) ± Float16(0.65)" + @test repr(br) == "1//10 ± 6//5" + @test repr(c) == "1.79769e+308 ± ∞" + @test repr(cr) == "9223372036854775807//1 ± ∞" + @test repr(large_expo) == "(5.0e+123456788 ± 5.0e+123456788)₂₅₆" + + setdisplay(; ng_flag = false) + @test repr(a) == "1.5 ± 0.5" + @test repr(a_NG) == "1.5 ± 0.5" + finally + setdisplay(:infsup; decorations = true, ng_flag = true, sigdigits = 6) + end + end + + @testset "Digit rounding" begin + try + setdisplay(:infsup; sigdigits = 6, decorations = true, ng_flag = true) + @test repr(interval(π)) == "[3.14159, 3.1416]_com" + setdisplay(; sigdigits = 1) + @test repr(interval(π)) == "[3.0, 4.0]_com" + setdisplay(; sigdigits = 17) + @test repr(interval(0.1, 0.3)) == "[0.1, 0.3]_com" + setdisplay(; sigdigits = 6) + @test repr(interval(1e100)) == "[1.0e+100, 1.0e+100]_com" + @test repr(interval(1e-100)) == "[1.0e-100, 1.0e-100]_com" + finally + setdisplay(:infsup; decorations = true, ng_flag = true, sigdigits = 6) + end + end + + @testset "Float32" begin + try + setdisplay(:infsup; sigdigits = 6, decorations = true, ng_flag = true) + @test repr(interval(Float32, 0.1, 0.3)) == "[0.099999f0, 0.3f0]_com" + @test repr(interval(Float32, 1e30, 2e30)) == "[1.0f+30, 2.0f+30]_com" + @test repr(entireinterval(Interval{Float32})) == "(-∞, ∞)_dac" + @test repr(emptyinterval(Interval{Float32})) == "∅_trv" + setdisplay(:full) + @test repr(interval(Float32, 1f30, 2f30)) == "Interval{Float32}(1.0f30, 2.0f30, com, true)" + @test repr(entireinterval(Interval{Float32})) == "Interval{Float32}(-Inf32, Inf32, dac, true)" + @test repr(emptyinterval(Interval{Float32})) == "∅_trv" + setdisplay(:midpoint) + # the ∞ substitution is absent from the Float32 midpoint branch, cf. src/display.jl + @test repr(entireinterval(Interval{Float32})) == "(0.0f0 ± Inf32)_dac" + @test repr(emptyinterval(Interval{Float32})) == "∅_trv" + finally + setdisplay(:infsup; decorations = true, ng_flag = true, sigdigits = 6) + end + end + + @testset "Float16" begin + try + setdisplay(:infsup; sigdigits = 6, decorations = true, ng_flag = true) + @test repr(interval(Float16, 0.1, 0.3)) == "[Float16(0.1), Float16(0.3)]_com" + @test repr(entireinterval(Interval{Float16})) == "(-∞, ∞)_dac" + setdisplay(:full) + @test repr(interval(Float16, 0.1, 0.3)) == "Interval{Float16}(Float16(0.1), Float16(0.3), com, true)" + @test repr(entireinterval(Interval{Float16})) == "Interval{Float16}(-Inf16, Inf16, dac, true)" + setdisplay(:midpoint) + @test repr(entireinterval(Interval{Float16})) == "(Float16(0.0) ± ∞)_dac" + finally + setdisplay(:infsup; decorations = true, ng_flag = true, sigdigits = 6) + end + end + + @testset "Rational" begin + try + setdisplay(:infsup; sigdigits = 6, decorations = true, ng_flag = true) + @test repr(interval(1//2, 3//4)) == "[1//2, 3//4]_com" + @test repr(entireinterval(Interval{Rational{Int64}})) == "(-∞, ∞)_dac" + setdisplay(:midpoint) + @test repr(interval(1//2, 3//4)) == "(5//8 ± 1//8)_com" + @test repr(entireinterval(Interval{Rational{Int64}})) == "(0//1 ± ∞)_dac" + setdisplay(:full) + @test repr(interval(1//2, 3//4)) == "Interval{Rational{$Int}}(1//2, 3//4, com, true)" + @test repr(entireinterval(Interval{Rational{Int64}})) == "Interval{Rational{Int64}}(-1//0, 1//0, dac, true)" + setdisplay(:infsup; sigdigits = 1) + @test repr(interval(1//2, 3//4)) == "[1//2, 3//4]_com" + setdisplay(; sigdigits = 20) + @test repr(interval(1//2, 3//4)) == "[1//2, 3//4]_com" + finally + setdisplay(:infsup; decorations = true, ng_flag = true, sigdigits = 6) + end + end + + @testset "BigFloat" begin + try + setdisplay(:infsup; sigdigits = 6, decorations = true, ng_flag = true) + @test repr(interval(BigFloat, 0.1, 0.3)) == "[0.1, 0.3]₂₅₆_com" + setdisplay(:midpoint) + @test repr(interval(BigFloat, 0.1, 0.3)) == "(0.2 ± 0.1)₂₅₆_com" + setdisplay(:full) + @test repr(interval(BigFloat, 1, 2)) == "Interval{BigFloat}(1.0, 2.0, com, true)" + @test repr(bareinterval(BigFloat, 1, 2)) == "BareInterval{BigFloat}(1.0, 2.0)" + @test repr(emptyinterval(Interval{BigFloat})) == "∅₂₅₆_trv" + @test repr(emptyinterval(BareInterval{BigFloat})) == "∅₂₅₆" + + setdisplay(:infsup) + x = IntervalArithmetic._unsafe_interval( + IntervalArithmetic._unsafe_bareinterval(BigFloat, + BigFloat(1; precision = 256), BigFloat(2; precision = 80)), com, true) + @test repr(x) == "[1.0, 2.0]₂₅₆_₈₀_com" + + setprecision(BigFloat, 53) do + @test repr(interval(BigFloat, 1, 2)) == "[1.0, 2.0]₅₃_com" + end + + setdisplay(:midpoint; decorations = false, ng_flag = false) + @test repr(interval(BigFloat, 1, 2)) == "(1.5 ± 0.5)₂₅₆" + finally + setdisplay(:infsup; decorations = true, ng_flag = true, sigdigits = 6) + end + end + + @testset "Complex" begin + a = complex(interval(0, 2), interval(1)) + b = complex(interval(0, 2), interval(-1)) + c = complex(interval(0, 1e-70), interval(-1e-70)) + try + setdisplay(:infsup; sigdigits = 6, decorations = true, ng_flag = true) + @test repr(a) == "[0.0, 2.0]_com + im*[1.0, 1.0]_com" + @test repr(b) == "[0.0, 2.0]_com - im*[1.0, 1.0]_com" + @test repr(c) == "[0.0, 1.0e-70]_com - im*[1.0e-70, 1.0e-70]_com" + @test repr(complex(interval(1, 2), interval(2, 3))) == "[1.0, 2.0]_com + im*[2.0, 3.0]_com" + @test repr(complex(interval(1, 2), interval(-3, -2))) == "[1.0, 2.0]_com - im*[2.0, 3.0]_com" + @test repr(complex(interval(1, 2), interval(-2, 0))) == "[1.0, 2.0]_com - im*[0.0, 2.0]_com" + @test repr(complex(interval(1, 2), interval(0, 0))) == "[1.0, 2.0]_com + im*[0.0, 0.0]_com" + @test repr(complex(interval(1, 2), emptyinterval())) == "[1.0, 2.0]_com + im*∅_trv" + @test repr(complex(interval(1, 2), interval(-Inf, -1))) == "[1.0, 2.0]_com - im*[1.0, ∞)_dac" + + setdisplay(; decorations = false) + @test repr(a) == "[0.0, 2.0] + im*[1.0, 1.0]" + @test repr(b) == "[0.0, 2.0] - im*[1.0, 1.0]" + @test repr(c) == "[0.0, 1.0e-70] - im*[1.0e-70, 1.0e-70]" + + setdisplay(:full; sigdigits = 100, decorations = false) + @test repr(a) == "Interval{Float64}(0.0, 2.0, com, true) + im*Interval{Float64}(1.0, 1.0, com, true)" + @test repr(b) == "Interval{Float64}(0.0, 2.0, com, true) - im*Interval{Float64}(1.0, 1.0, com, true)" + @test repr(c) == "Interval{Float64}(0.0, 1.0e-70, com, true) - im*Interval{Float64}(1.0e-70, 1.0e-70, com, true)" + @test repr(complex(interval(1, 2), interval(-3, -2))) == + "Interval{Float64}(1.0, 2.0, com, true) - im*Interval{Float64}(2.0, 3.0, com, true)" + @test repr(complex(interval(1, 2), interval(2, 3))) == + "Interval{Float64}(1.0, 2.0, com, true) + im*Interval{Float64}(2.0, 3.0, com, true)" + + setdisplay(:midpoint; sigdigits = 6, decorations = true, ng_flag = true) + @test repr(a) == "(1.0 ± 1.0)_com + im*(1.0 ± 0.0)_com" + @test repr(b) == "(1.0 ± 1.0)_com - im*(1.0 ± 0.0)_com" + @test repr(c) == "(5.0e-71 ± 5.0e-71)_com - im*(1.0e-70 ± 0.0)_com" + @test repr(complex(interval(1, 2), interval(-3, -2))) == "(1.5 ± 0.5)_com - im*(2.5 ± 0.5)_com" + + setdisplay(; decorations = false) + @test repr(a) == "(1.0 ± 1.0) + im*(1.0 ± 0.0)" + @test repr(b) == "(1.0 ± 1.0) - im*(1.0 ± 0.0)" + @test repr(c) == "(5.0e-71 ± 5.0e-71) - im*(1.0e-70 ± 0.0)" + finally + setdisplay(:infsup; decorations = true, ng_flag = true, sigdigits = 6) + end + end +end + +@testset "String helpers" begin + @test IntervalArithmetic._round_string(0.1, 6) == "0.1" + @test IntervalArithmetic._round_string(1/3, 6) == "0.333333" + @test IntervalArithmetic._round_string(1.0, 6) == "1.0" + @test IntervalArithmetic._round_string(0.0, 6) == "0.0" + @test IntervalArithmetic._round_string(123456789.0, 6) == "1.23457e+8" + @test IntervalArithmetic._round_string(1e100, 6) == "1.0e+100" + @test IntervalArithmetic._round_string(1/3, 6, RoundDown) == "0.333333" + @test IntervalArithmetic._round_string(1/3, 6, RoundUp) == "0.333334" + @test IntervalArithmetic._round_string(Inf, 6) == "Inf" + @test IntervalArithmetic._round_string(-Inf, 6) == "-Inf" + @test IntervalArithmetic._round_string(NaN, 6) == "NaN" + + @test IntervalArithmetic._count_sigdigits("0.1") == 1 + @test IntervalArithmetic._count_sigdigits("1.0e5") == 2 + @test IntervalArithmetic._count_sigdigits("1234.5") == 5 + @test IntervalArithmetic._count_sigdigits("-0.001200") == 7 + + @test IntervalArithmetic._display_midpoint_radius(bareinterval(0.1, 0.3), 6) == (0.2, 0.1) + @test IntervalArithmetic._display_midpoint_radius(bareinterval(-Inf, Inf), 6) == (0.0, Inf) + @test IntervalArithmetic._display_midpoint_radius(bareinterval(1.0, Inf), 6) == + (round(floatmax(Float64), RoundNearest; sigdigits = 6), Inf) + @test IntervalArithmetic._display_midpoint_radius(bareinterval(0.1, 0.30000001), 3) == (0.2, 0.101) + + @test IntervalArithmetic._flipl(']') == '[' + @test IntervalArithmetic._flipl(')') == '(' + @test IntervalArithmetic._flipr('[') == ']' + @test IntervalArithmetic._flipr('(') == ')' + + @test IntervalArithmetic._subscriptify(0) === '₀' + @test IntervalArithmetic._subscriptify(9) === '₉' + @test IntervalArithmetic._subscriptify(53) == "₅₃" + @test IntervalArithmetic._subscriptify(256) == "₂₅₆" + for (i, c) ∈ enumerate(('₀', '₁', '₂', '₃', '₄', '₅', '₆', '₇', '₈', '₉')) + @test IntervalArithmetic._subscript_digit(i - 1) === c + end + @test IntervalArithmetic._subscript_digit(10) === '₉' + @test IntervalArithmetic._subscript_digit(-1) === '₉' +end + +@testset "Enclosure properties" begin + rng = MersenneTwister(20260821) + for _ ∈ 1:100 + lo = 1000 * randn(rng) + hi = lo + 100 * rand(rng) + prev_lo, prev_hi = -Inf, Inf + for sigdigits ∈ 1:10 + plo = parse(Float64, IntervalArithmetic._round_string(lo, sigdigits, RoundDown)) + phi = parse(Float64, IntervalArithmetic._round_string(hi, sigdigits, RoundUp)) + @test plo ≤ lo && hi ≤ phi + @test prev_lo ≤ plo && phi ≤ prev_hi + prev_lo, prev_hi = plo, phi + end + for sigdigits ∈ 1:6 + m_d, r_d = IntervalArithmetic._display_midpoint_radius(bareinterval(lo, hi), sigdigits) + @test issubset_interval(interval(lo, hi), interval(m_d, r_d; format = :midpoint)) + end + end +end diff --git a/test/ext/IntervalArithmeticArblibExt.jl b/test/ext/IntervalArithmeticArblibExt.jl new file mode 100644 index 000000000..57370ffed --- /dev/null +++ b/test/ext/IntervalArithmeticArblibExt.jl @@ -0,0 +1,327 @@ +using Test +using IntervalArithmetic +import Arblib +using Arblib: Arf, ArfRef, Arb, ArbRef, Acb, AcbRef, setball + +# Flint < 3.3.0 returns NaN from getinterval on balls with infinite midpoint and radius +broken_getinterval = isnan(Arblib.getinterval(setball(Arb, -Inf, Inf))[2]) + +@testset "Promotion rules" begin + @test promote_type(Arf, Interval{Float64}) == Interval{Arf} + @test promote_type(Arf, Interval{BigFloat}) == Interval{Arf} + @test promote_type(Arf, Interval{Rational{Int}}) == Interval{Arf} + @test promote_type(ArfRef, Interval{Float64}) == Interval{Arf} + @test promote_type(ArfRef, Interval{BigFloat}) == Interval{Arf} + @test promote_type(ArfRef, Interval{Rational{Int}}) == Interval{Arf} + @test promote_type(Interval{Float64}, Arf) == Interval{Arf} + @test promote_type(Interval{BigFloat}, Arf) == Interval{Arf} + @test promote_type(Interval{Rational{Int}}, Arf) == Interval{Arf} + @test promote_type(Interval{Float64}, ArfRef) == Interval{Arf} + @test promote_type(Interval{BigFloat}, ArfRef) == Interval{Arf} + @test promote_type(Interval{Rational{Int}}, ArfRef) == Interval{Arf} + @test promote_type(Interval{Float32}, ArfRef) == Interval{Arf} + + @test_throws ArgumentError promote_type(Arb, Interval{Float64}) + @test_throws ArgumentError promote_type(Arb, Interval{BigFloat}) + @test_throws ArgumentError promote_type(Arb, Interval{Arf}) + @test_throws ArgumentError promote_type(Arb, Interval{Arb}) + @test_throws ArgumentError promote_type(ArbRef, Interval{Float64}) + @test_throws ArgumentError promote_type(ArbRef, Interval{BigFloat}) + @test_throws ArgumentError promote_type(ArbRef, Interval{Arf}) + @test_throws ArgumentError promote_type(ArbRef, Interval{Arb}) + @test_throws ArgumentError promote_type(Interval{Float64}, Arb) + @test_throws ArgumentError promote_type(Interval{BigFloat}, Arb) + @test_throws ArgumentError promote_type(Interval{Arf}, Arb) + @test_throws ArgumentError promote_type(Interval{Arb}, Arb) + @test_throws ArgumentError promote_type(Interval{Float64}, ArbRef) + @test_throws ArgumentError promote_type(Interval{BigFloat}, ArbRef) + @test_throws ArgumentError promote_type(Interval{Arf}, ArbRef) + @test_throws ArgumentError promote_type(Interval{Arb}, ArbRef) +end + +@testset "promote_numtype" begin + @test IntervalArithmetic.promote_numtype(Arb, Arb) == BigFloat + @test IntervalArithmetic.promote_numtype(Arb, ArbRef) == BigFloat + @test IntervalArithmetic.promote_numtype(ArbRef, Arb) == BigFloat + @test IntervalArithmetic.promote_numtype(ArbRef, ArbRef) == BigFloat + + @test IntervalArithmetic.promote_numtype(Arb, Float64) == BigFloat + @test IntervalArithmetic.promote_numtype(Float64, Arb) == BigFloat + @test IntervalArithmetic.promote_numtype(Arb, Float32) == BigFloat + @test IntervalArithmetic.promote_numtype(ArbRef, Float64) == BigFloat + @test IntervalArithmetic.promote_numtype(Arb, Rational{Int}) == BigFloat + @test IntervalArithmetic.promote_numtype(ArbRef, Rational{Int}) == BigFloat + @test IntervalArithmetic.promote_numtype(Arb, Int) == BigFloat + @test IntervalArithmetic.promote_numtype(Int, Arb) == BigFloat + + @test IntervalArithmetic.promote_numtype(Arb, Arf) == Arf + @test IntervalArithmetic.promote_numtype(ArbRef, Arf) == Arf + @test IntervalArithmetic.promote_numtype(Arb, ArfRef) == Arf + @test IntervalArithmetic.promote_numtype(ArbRef, ArfRef) == Arf +end + +@testset "Single argument constructor" begin + xs = Arb[0, 1, π, ℯ, 1//3, Arb((1, 2)), Arb((-Inf, Inf)), setball(Arb, 5, Inf)] + + for x ∈ xs + @test interval(x) isa Interval{BigFloat} + @test isguaranteed(interval(x)) + + for T ∈ [BigFloat, Arf, Float64, Float32] + y = interval(T, x) + @test y isa Interval{T} + @test !isnai(y) + @test isbounded(y) == isfinite(x) + @test Arblib.contains(Arb(y), x) + end + end + + x = interval(Arb(π; prec = 64)) + @test in_interval(π, x) + @test decoration(x) === com + @test isguaranteed(x) + xf = interval(Float64, Arb(π; prec = 64)) + @test isequal_interval(xf, interval(3.141592653589793, 3.1415926535897936)) + @test decoration(xf) === com + @test isguaranteed(xf) + + @test IntervalArithmetic._inf(Arb((1, 2))) == BigFloat(Arblib.lbound(Arb((1, 2)))) + @test IntervalArithmetic._sup(Arb((1, 2))) == BigFloat(Arblib.ubound(Arb((1, 2)))) + + @test isnai(@test_logs (:warn,) interval(Arb(-Inf))) + @test isnai(@test_logs (:warn,) interval(Arb(Inf))) + @test isnai(@test_logs (:warn,) interval(Arb(NaN))) +end + +@testset "Two argument constructor" begin + as1 = Real[0, 1, ℯ, 1//3, 0.1, BigFloat(1.1), BigInt(3)] + bs1 = Real[3, π, 7//2, BigFloat(4.1), BigInt(4)] + + for a ∈ as1 + for b ∈ bs1 + a_Arb = Arb(a) + b_Arb = Arb(b) + + y1 = interval(a, b_Arb) + y2 = interval(a_Arb, b) + y3 = interval(a_Arb, b_Arb) + + @test y1 isa Interval{BigFloat} + @test y2 isa Interval{BigFloat} + @test y3 isa Interval{BigFloat} + + for y ∈ (y1, y2, y3) + @test Arblib.overlaps(Arb(y), a_Arb) + @test Arblib.overlaps(Arb(y), b_Arb) + end + + for T ∈ [BigFloat, Float64, Float32] + y1 = interval(T, a, b_Arb) + y2 = interval(T, a_Arb, b) + y3 = interval(T, a_Arb, b_Arb) + + @test y1 isa Interval{T} + @test y2 isa Interval{T} + @test y3 isa Interval{T} + + for y ∈ (y1, y2, y3) + @test Arblib.overlaps(Arb(y), a_Arb) + @test Arblib.overlaps(Arb(y), b_Arb) + end + end + end + end + + @test isequal_interval( + interval(interval(1, 3), interval(2, 4)), + interval(setball(Arb, 2, 1), setball(Arb, 3, 1)), + ) + @test isequal_interval( + interval(interval(1, 3), interval(0, 4)), + interval(setball(Arb, 2, 1), setball(Arb, 2, 2)), + ) + @test isequal_interval( + interval(interval(1, 5), interval(2, 4)), + interval(setball(Arb, 3, 2), setball(Arb, 3, 1)), + ) + + as2 = [Arb(-Inf), Arb((-Inf, Inf)), setball(Arb, 0, Inf)] + bs2 = [Arb(Inf), Arb((-Inf, Inf)), setball(Arb, 0, Inf)] + + for a ∈ as2 + for b ∈ bs2 + broken = + broken_getinterval && ( + isequal(a, setball(Arb, Inf, Inf)) || + isequal(b, setball(Arb, -Inf, Inf)) + ) + @test isequal_interval(interval(a, b), interval(-Inf, Inf)) broken = broken + end + end + + for a ∈ as2 + broken = broken_getinterval && isequal(a, setball(Arb, Inf, Inf)) + @test isequal_interval(interval(a, Inf), interval(-Inf, Inf)) broken = broken + end + + for b ∈ bs2 + broken = broken_getinterval && isequal(b, setball(Arb, -Inf, Inf)) + @test isequal_interval(interval(-Inf, b), interval(-Inf, Inf)) broken = broken + end + + @test isnai(@test_logs (:warn,) interval(Arb(-Inf), Arb(-Inf))) + @test isnai(@test_logs (:warn,) interval(-Inf, Arb(-Inf))) + @test isnai(@test_logs (:warn,) interval(Arb(-Inf), -Inf)) + @test isnai(@test_logs (:warn,) interval(setball(Arb, -Inf, 1), Arb(-Inf))) + @test isnai(@test_logs (:warn,) interval(Arb(-Inf), setball(Arb, -Inf, 1))) + @test isnai(@test_logs (:warn,) interval(Arb(Inf), Arb(Inf))) + @test isnai(@test_logs (:warn,) interval(Inf, Arb(Inf))) + @test isnai(@test_logs (:warn,) interval(Arb(Inf), Inf)) + @test isnai(@test_logs (:warn,) interval(setball(Arb, Inf, 1), Arb(Inf))) + @test isnai(@test_logs (:warn,) interval(Arb(Inf), setball(Arb, Inf, 1))) + + @test isnai(@test_logs (:warn,) interval(Arb(NaN), 0)) + @test isnai(@test_logs (:warn,) interval(setball(Arb, NaN, Inf), 0)) + @test isnai(@test_logs (:warn,) interval(Arb(NaN), -Inf)) + @test isnai(@test_logs (:warn,) interval(Arb(NaN), Inf)) + @test isnai(@test_logs (:warn,) interval(0, Arb(NaN))) + @test isnai(@test_logs (:warn,) interval(0, setball(Arb, NaN, Inf))) + @test isnai(@test_logs (:warn,) interval(-Inf, Arb(NaN))) + @test isnai(@test_logs (:warn,) interval(Inf, Arb(NaN))) + + @test isnai(@test_logs (:warn,) interval(Arb(2), Arb(1))) + + @test isequal_interval( + interval(setball(Arb, 0, 1), format = :midpoint), + interval(-1, 1), + ) + @test isequal_interval( + interval(setball(Arb, 0, 1), setball(Arb, 4, 1), format = :midpoint), + interval(-6, 6), + ) + @test_throws DomainError interval(0, Arb((-1, 1)), format = :midpoint) +end + +@testset "Complex intervals" begin + @test numtype(Acb) === Arb + @test numtype(AcbRef) === Arb + + z = interval(Acb(1, 2)) + @test z isa Complex{Interval{BigFloat}} + @test isequal_interval(z, complex(interval(1, 1), interval(2, 2))) + @test isequal_interval(interval(Acb(1, 2), Acb(3, 4)), complex(interval(1, 3), interval(2, 4))) + + @test isequal_interval( + interval(1 + 2im, 3 + 4im), + interval(Acb(setball(Arb, 2, 1), setball(Arb, 3, 1))), + ) + @test isequal_interval( + interval(Float64, 1 + 2im, 3 + 4im), + interval(Acb(setball(Arb, 2, 1), setball(Arb, 3, 1))), + ) + + @test isequal_interval(interval(1 + 2im, 3 + 4im), interval(Acb(1, 2), Acb(3, 4))) + + @test isequal_interval(interval(1 + 2im, 3 + 4im), interval(Acb(1, 2), 3 + 4im)) + @test isequal_interval(interval(1 - 2im, 3), interval(Acb(1, -2), 3)) + @test isequal_interval(interval(1 - 2im, 3), interval(Acb(1, -2), interval(3, 3))) + + @test isequal_interval(interval(1 + 2im, 3 + 4im), interval(1 + 2im, Acb(3, 4))) + @test isequal_interval(interval(1, 3 + 2im), interval(1, Acb(3, 2))) + @test isequal_interval(interval(1, 3 + 2im), interval(interval(1), Acb(3, 2))) + + # the BareInterval ambiguity methods dispatch but real(::BareInterval) is undefined + @test_throws MethodError interval(Acb(1, 2), bareinterval(3, 4)) + @test_throws MethodError interval(bareinterval(1, 2), Acb(3, 4)) +end + +@testset "convert" begin + @test isguaranteed(convert(Interval{Float64}, Arb(1))) + @test isguaranteed(convert(Interval{Float64}, Acb(1))) + @test_throws DomainError convert(Interval{Float64}, Acb(1, 1)) + + @test isequal_interval(convert(Interval{Float64}, Arb(1)), interval(Float64, Arb(1))) + @test isequal_interval(convert(Interval{Float64}, Acb(1, 0)), interval(1)) + + @test isguaranteed(convert(Complex{Interval{Float64}}, Arb(1))) + @test isguaranteed(convert(Complex{Interval{Float64}}, Acb(1))) + @test isguaranteed(convert(Complex{Interval{Float64}}, Acb(1, 1))) + @test isequal_interval(convert(Complex{Interval{Float64}}, Arb(1)), complex(interval(1), interval(0))) + + v = [interval(0.0), interval(0.0)] + v[1] = Arb(1) + @test isguaranteed(v[1]) + @test isequal_interval(v[1], interval(1)) +end + +@testset "Arb from Interval" begin + @test isequal(Arb((0, 1)), Arb(interval(0, 1))) + @test isequal(setball(Arb, NaN, Inf), Arb(nai(Float64))) + @test Arblib.overlaps(Arb(π), Arb(interval(π))) + @test Arblib.overlaps(Arb(π), Arb(interval(BigFloat, π))) + @test isequal(Arblib.indeterminate!(Arb()), Arb(emptyinterval())) + @test isequal(Arblib.indeterminate!(Arb()), Arb(emptyinterval(BareInterval{BigFloat}))) + @test isequal(Acb(1, 2), Acb(interval(1 + 2im))) + + a = Arb(interval(BigFloat, 1, 2)) + @test Arblib.lbound(a) <= 1 + @test Arblib.ubound(a) >= 2 + + x = interval(BigFloat, 1, 2) + @test issubset_interval(x, interval(Arb(x))) + + @test isequal(Arb((0, 1)), Arblib.set!(Arb(), interval(0, 1))) + @test isequal(setball(Arb, NaN, Inf), Arblib.set!(Arb(), nai(Float64))) + @test isequal(Arb(interval(π)), Arblib.set!(Arb(), interval(π))) + @test isequal(Arb(interval(BigFloat, π)), Arblib.set!(Arb(), interval(BigFloat, π))) + @test isequal(Arblib.indeterminate!(Arb()), Arblib.set!(Arb(), emptyinterval())) + @test isequal(Acb(1, 2), Arblib.set!(Acb(), interval(1 + 2im))) + r = Arb() + @test Arblib.set!(r, interval(BigFloat, π), prec = 64) === r + @test Arblib.contains_interior( + Arblib.set!(Arb(), interval(BigFloat, π), prec = 64), + Arb(interval(BigFloat, π)), + ) + + @test Arblib._precision(interval(1, 2)) == precision(Arb) + @test Arblib._precision(interval(1, 2)) == Arblib._precision(inf(interval(1, 2)), sup(interval(1, 2))) + @test Arblib._precision(bareinterval(BigFloat(1), BigFloat(2))) == Arblib._precision(BigFloat(1), BigFloat(2)) + @test Arblib._precision(interval(BigFloat, BigFloat(1, precision = 80))) == 80 + @test Arblib._precision( + interval(BigFloat, BigFloat(1, precision = 80), BigFloat(1, precision = 64)), + ) == 80 + @test Arblib._precision( + interval(BigFloat, BigFloat(1, precision = 64), BigFloat(1, precision = 80)), + ) == 80 + + @test precision( + Arb(interval(BigFloat(1, precision = 80), BigFloat(2, precision = 64))), + ) == 80 + @test precision(Arb(interval(Arf(0, prec = 64), Arf(1, prec = 80)))) == 80 +end + +@testset "ExactReal" begin + @test Arblib.Mag(exact(5)) == Arblib.Mag(5) + @test Arf(exact(5)) == Arf(5) + @test Arb(exact(5)) == Arb(5) + @test Arblib.Mag(exact(2.0)) == Arblib.Mag(2.0) + @test Arf(exact(2.0)) == 2 + @test Arb(exact(2.0)) == 2 + + @test promote_type(Arf, ExactReal{Float64}) == Arf + @test promote_type(ArfRef, ExactReal{Float64}) == Arf + @test promote_type(ExactReal{Float64}, Arf) == Arf + @test promote_type(ExactReal{Float64}, ArfRef) == Arf + + @test promote_type(Arb, ExactReal{Float64}) == Arb + @test promote_type(ArbRef, ExactReal{Float64}) == Arb + @test promote_type(ExactReal{Float64}, Arb) == Arb + @test promote_type(ExactReal{Float64}, ArbRef) == Arb + + s = exact(2.0) + Arb(1) + @test s isa Arb + @test s == 3 + t = exact(2.0) + Arf(1) + @test t isa Arf + @test t == 3 +end diff --git a/test/ext/IntervalArithmeticDiffRulesExt.jl b/test/ext/IntervalArithmeticDiffRulesExt.jl new file mode 100644 index 000000000..d39f99738 --- /dev/null +++ b/test/ext/IntervalArithmeticDiffRulesExt.jl @@ -0,0 +1,66 @@ +using Test +using IntervalArithmetic +import DiffRules +import ForwardDiff + +@testset "Extension loading" begin + @test Base.get_extension(IntervalArithmetic, :IntervalArithmeticDiffRulesExt) !== nothing +end + +@testset "_abs_deriv values" begin + @test DiffRules._abs_deriv(interval(1, 2)) === interval(1, 1, com) + @test DiffRules._abs_deriv(interval(-2, -1)) === interval(-1, -1, com) + @test DiffRules._abs_deriv(interval(-1, 1)) === interval(-1, 1, trv) + @test DiffRules._abs_deriv(interval(0, 0)) === interval(-1, 1, trv) + @test DiffRules._abs_deriv(interval(0, 2)) === interval(0, 1, trv) + @test DiffRules._abs_deriv(interval(-2, 0)) === interval(-1, 0, trv) + @test DiffRules._abs_deriv(entireinterval()) === interval(-1, 1, trv) + @test isempty_interval(DiffRules._abs_deriv(emptyinterval())) + @test decoration(DiffRules._abs_deriv(emptyinterval())) === trv + @test DiffRules._abs_deriv(interval(1, 2)) isa Interval{Float64} +end + +@testset "_abs_deriv decoration and guarantee" begin + @test DiffRules._abs_deriv(interval(1, 2, def)) === interval(1, 1, def) + @test isnai(@test_logs (:warn,) DiffRules._abs_deriv(nai())) + @test isguaranteed(DiffRules._abs_deriv(interval(1, 2))) + x = DiffRules._abs_deriv(interval(1, 2) + 1) + @test !isguaranteed(x) + @test isequal_interval(x, interval(1, 1)) +end + +@testset "_abs_deriv bound types" begin + @test DiffRules._abs_deriv(interval(Float32, 1, 2)) === interval(Float32, 1, 1, com) + @test DiffRules._abs_deriv(interval(Float16, -2, -1)) === interval(Float16, -1, -1, com) + @test DiffRules._abs_deriv(interval(1//2, 3//4)) === interval(1//1, 1//1, com) + y = DiffRules._abs_deriv(interval(BigFloat, 1, 2)) + @test y isa Interval{BigFloat} + @test isequal_interval(y, interval(BigFloat, 1, 1)) + @test decoration(y) === com +end + +@testset "BareInterval" begin + @test_throws MethodError ForwardDiff.derivative(abs, bareinterval(-1, 1)) +end + +@testset "ForwardDiff abs" begin + @test ForwardDiff.derivative(abs, interval(-2, -1)) === interval(-1, -1, com) + @test ForwardDiff.derivative(abs, interval(1, 2)) === interval(1, 1, com) + @test ForwardDiff.derivative(abs, interval(0)) === interval(-1, 1, trv) + @test ForwardDiff.derivative(abs, interval(-1, 0)) === interval(-1, 0, trv) + @test ForwardDiff.derivative(abs, interval(0, 1)) === interval(0, 1, trv) + @test ForwardDiff.derivative(abs, interval(-2, 2)) === interval(-1, 1, trv) + + f(x) = abs(x)^interval(2) + @test ForwardDiff.derivative(f, interval(-1, 1)) === interval(-2, 2, trv) + + g(x) = abs(x)^2 + ng = interval(convert(Interval{Float64}, -2), convert(Interval{Float64}, 2), trv) + @test ForwardDiff.derivative(g, interval(-1, 1)) === ng + @test only(ForwardDiff.gradient(v -> g(v[1]), [interval(-1, 1)])) === ng + @test only(ForwardDiff.hessian(v -> g(v[1]), [interval(0)])) === ng + @test only(ForwardDiff.hessian(v -> g(v[1]), [interval(-1, 1)])) === ng + + h(x) = abs(x) * x + @test issubset_interval(interval(0, 2), ForwardDiff.derivative(h, interval(-1, 1))) +end diff --git a/test/ext/IntervalArithmeticForwardDiffExt.jl b/test/ext/IntervalArithmeticForwardDiffExt.jl new file mode 100644 index 000000000..3844c9b7f --- /dev/null +++ b/test/ext/IntervalArithmeticForwardDiffExt.jl @@ -0,0 +1,285 @@ +using Test +using IntervalArithmetic +using ForwardDiff +using ForwardDiff: Dual, Partials, Tag, value, partials, npartials + +@testset "Dual and ExactReal" begin + @test ForwardDiff.can_dual(ExactReal) + d = Dual{Nothing}(exact(2.0)) + @test value(d) === exact(2.0) + @test npartials(d) == 0 + @test Dual(exact(2.0)) === d + @test Dual{Nothing,Interval{Float64},1}(exact(2.0)) === Dual{Nothing}(interval(2.0), interval(0.0)) + @test convert(Dual{Nothing,Float64,1}, exact(2.0)) === Dual{Nothing}(2.0, 0.0) + c = convert(Dual{Nothing,Interval{Float64},2}, exact(2.0)) + @test npartials(c) == 2 + @test value(c) === interval(2.0) + @test all(isthinzero, partials(c)) +end + +@testset "Promotion rules" begin + @test promote_type(Dual{Nothing,Float64,1}, Interval{Float64}) === Dual{Nothing,Interval{Float64},1} + @test promote_type(Interval{Float64}, Dual{Nothing,Float64,1}) === Dual{Nothing,Interval{Float64},1} + @test promote_type(Dual{Nothing,Float64,1}, Interval{Float32}) === Dual{Nothing,Interval{Float64},1} + @test promote_type(Interval{Float32}, Dual{Nothing,Float64,1}) === Dual{Nothing,Interval{Float64},1} + @test promote_type(Dual{Nothing,Float64,1}, ExactReal{Float64}) === Dual{Nothing,ExactReal{Float64},1} + @test promote_type(ExactReal{Float64}, Dual{Nothing,Float64,1}) === Dual{Nothing,ExactReal{Float64},1} + @test (Dual{Nothing}(1.0, 1.0) + interval(1, 2)) isa Dual{Nothing,Interval{Float64},1} +end + +@testset "Comparisons" begin + d = Dual{Nothing}(interval(2), interval(0)) + @test interval(2) == d + @test d == interval(2) + @test !(interval(3) == d) + @test !(d == interval(3)) + @test_throws IntervalArithmetic.InconclusiveBooleanOperation interval(1, 2) == Dual{Nothing}(interval(1, 2), interval(0)) + @test interval(1, 2) < Dual{Nothing}(interval(3, 4), interval(0)) + @test !(Dual{Nothing}(interval(3, 4), interval(0)) < interval(1, 2)) +end + +@testset "Power of interval Duals" begin + d = ForwardDiff.derivative(x -> x^x, interval(2.0)) + @test isequal_interval(d, interval(6.772588722239781, 6.772588722239782)) + @test in_interval(4 * (1 + log(2)), d) + @test isguaranteed(d) + + x = Dual{Nothing}(interval(2.0), interval(1.0)) + y0 = Dual{Nothing}(interval(3.0), interval(0.0)) + z = x^y0 + @test isequal_interval(value(z), interval(8)) & isequal_interval(partials(z, 1), interval(12)) + + x0 = Dual{Nothing}(interval(0.0), interval(1.0)) + y1 = Dual{Nothing}(interval(3.0), interval(1.0)) + z = x0^y1 + @test isequal_interval(value(z), interval(0)) & isequal_interval(partials(z, 1), interval(0)) + @test !isnai(partials(z, 1)) + + z = x^y1 + @test isequal_interval(value(z), interval(8)) + @test in_interval(12 + 8 * log(2), partials(z, 1)) + + xneg = Dual{Nothing}(interval(-2.0), interval(1.0)) + z = xneg^y1 + @test isequal_interval(value(z), interval(-8)) + @test decoration(partials(z, 1)) === trv + + @test isequal_interval(ForwardDiff.derivative(x -> x^interval(3.0), interval(2.0)), interval(12.0)) + @test isequal_interval(ForwardDiff.derivative(x -> x^interval(0.0), interval(2.0)), interval(0.0)) + zt = Dual{Nothing}(interval(2.0), interval(0.0))^interval(3.0) + @test isequal_interval(partials(zt, 1), interval(0)) + + d = ForwardDiff.derivative(x -> interval(2.0)^x, interval(3.0)) + @test in_interval(8 * log(2), d) + @test isequal_interval(ForwardDiff.derivative(x -> interval(0.0)^x, interval(3.0)), interval(0.0)) +end + +@testset "Power with different tags" begin + TA = typeof(Tag(x -> x, Interval{Float64})) + TB = typeof(Tag(x -> 2x, Interval{Float64})) + @test ForwardDiff.:≺(TA, TB) & !ForwardDiff.:≺(TB, TA) + x = Dual{TA}(interval(2.0), interval(1.0)) + y = Dual{TB}(interval(3.0), interval(1.0)) + z = x^y + @test z isa Dual{TB} + @test isequal_interval(value(z), interval(8)) + @test in_interval(8 * log(2), partials(z, 1)) + z = y^x + @test z isa Dual{TB} + @test isequal_interval(value(z), interval(9)) & isequal_interval(partials(z, 1), interval(6)) + # each cross-tag reduction treats the other variable as a constant + dd = ForwardDiff.derivative(y -> ForwardDiff.derivative(t -> t^y, interval(2.0)), interval(3.0)) + @test isequal_interval(dd, interval(0)) +end + +@testset "Power and ExactReal" begin + @test isequal_interval(ForwardDiff.derivative(x -> x^exact(3), interval(2.0)), interval(12.0)) + @test in_interval(8 * log(2), ForwardDiff.derivative(x -> exact(2)^x, interval(3.0))) + @test ForwardDiff.derivative(x -> x^exact(3), 2.0) == 12.0 + @test ForwardDiff.derivative(x -> exact(2)^x, 3.0) ≈ 5.545177444479562 + @test ForwardDiff.derivative(x -> x^exact(0), 2.0) == 0.0 + @test partials(Dual{Nothing}(2.0, 0.0)^exact(3), 1) == 0.0 + @test ForwardDiff.derivative(x -> exact(0)^x, 3.0) == 0.0 +end + +@testset "Legacy power grid" begin + fxy(xy) = xy[1]^xy[2] + + for x ∈ [0.0, 1.1, 2.2] + for y ∈ [-3.3, 0.0, 4.4] + fx(xx) = xx^y + fxi(xx) = xx^interval(y) + fy(yy) = x^yy + fyi(yy) = interval(x)^yy + + dfdx = ForwardDiff.derivative(fxi, interval(x)) + dfdy = ForwardDiff.derivative(fyi, interval(y)) + grad = ForwardDiff.gradient(fxy, [interval(x), interval(y)]) + + @test isguaranteed(dfdx) + @test isguaranteed(dfdy) + @test isguaranteed(grad[1]) + @test isguaranteed(grad[2]) + + if iszero(x) && y < 0 + @test decoration(dfdx) == trv + else + @test in_interval(ForwardDiff.derivative(fx, x), dfdx) + end + + if iszero(x) && y <= 0 + @test decoration(dfdy) == trv + else + @test in_interval(ForwardDiff.derivative(fy, y), dfdy) + end + + if iszero(x) && iszero(y) + @test decoration(grad[1]) == trv + @test decoration(dfdx) == com + else + @test isequal_interval(dfdx, grad[1]) + end + @test isequal_interval(dfdy, grad[2]) + end + end +end + +@testset "sin" begin + x, w = interval(2), interval(-0.5, 0.5) + ϕ(t) = sin(x + (1+t)*w) + ϕ′(t) = cos(x + (1+t)*w) * w + ϕ′′(t) = -sin(x + (1+t)*w) * w * w + ϕ′′′(t) = -cos(x + (1+t)*w) * w * w * w + dϕ(t) = ForwardDiff.derivative(ϕ, t) + ddϕ(t) = ForwardDiff.derivative(dϕ, t) + dddϕ(t) = ForwardDiff.derivative(ddϕ, t) + + @test ϕ′(0) === dϕ(0) + @test ϕ′′(0) === ddϕ(0) + @test ϕ′′′(0) === dddϕ(0) + + y = interval(1) + ψ(t) = sin(x + (y+t)*w) + ψ′(t) = cos(x + (y+t)*w) * w + ψ′′(t) = -sin(x + (y+t)*w) * w * w + ψ′′′(t) = -cos(x + (y+t)*w) * w * w * w + dψ(t) = ForwardDiff.derivative(ψ, t) + ddψ(t) = ForwardDiff.derivative(dψ, t) + dddψ(t) = ForwardDiff.derivative(ddψ, t) + @test ψ′(0) === dψ(0) && !isguaranteed(ψ′(0)) + @test ψ′′(0) === ddψ(0) && !isguaranteed(ψ′′(0)) + @test ψ′′′(0) === dddψ(0) && !isguaranteed(ψ′′′(0)) + t₀ = interval(0) + @test ψ′(t₀) === dψ(t₀) && isguaranteed(ψ′(t₀)) + @test ψ′′(t₀) === ddψ(t₀) && isguaranteed(ψ′′(t₀)) + @test ψ′′′(t₀) === dddψ(t₀) && isguaranteed(ψ′′′(t₀)) +end + +@testset "ExactReal" begin + @exact f(x) = x^2 - 2 + @test isguaranteed(ForwardDiff.derivative(f, interval(1))) + + @exact g(x) = 2^x + 6sin(x^3) - 33 + @test isguaranteed(ForwardDiff.derivative(g, interval(1))) +end + +@testset "Constant on Dual" begin + c = Constant(1.2) + @test c(Dual{Nothing}(interval(2.0), interval(1.0))) === Dual{Nothing}(interval(1.2), interval(0.0)) + @test c(Dual{Nothing}(interval(Float32, 2.0), interval(Float32, 1.0))) isa Dual{Nothing,Interval{Float32},1} + @test isequal_interval(ForwardDiff.derivative(c, interval(2.0)), interval(0.0)) + @test npartials(c(Dual{Nothing}(interval(2.0), interval(1.0), interval(3.0)))) == 1 +end + +@testset "Piecewise on Dual" begin + myabs = Piecewise(Domain{:open,:closed}(-Inf, 0) => (x -> -x), Domain{:open,:open}(0, Inf) => identity) + @test ForwardDiff.derivative(myabs, interval(1, 2)) === interval(1, 1, com) + @test ForwardDiff.derivative(myabs, interval(-5, -1)) === interval(-1, -1, com) + @test ForwardDiff.derivative(myabs, interval(-5, 5)) === interval(-1, 1, def) + + d = myabs(Dual{Nothing}(interval(-5, 5), interval(1.0))) + @test isequal_interval(value(d), interval(0, 5)) + @test decoration(value(d)) === def + @test isequal_interval(partials(d, 1), interval(-1, 1)) + @test decoration(partials(d, 1)) === def + + g = ForwardDiff.gradient(v -> myabs(v[1]) + v[2], [interval(1.0), interval(2.0)]) + @test length(g) == 2 + @test isequal_interval(g[1], interval(1)) & isequal_interval(g[2], interval(1)) + + p = Piecewise(Domain{:closed,:closed}(0, 1) => Constant(1.0), Domain{:open,:closed}(1, 2) => identity) + d = p(Dual{Nothing}(interval(-1.0, 0.5), interval(1.0))) + @test isequal_interval(value(d), interval(1)) + @test decoration(value(d)) === trv + @test isequal_interval(partials(d, 1), interval(0)) + @test decoration(partials(d, 1)) === trv + # emptyinterval(X) .* partials(dual) is a Vector, not a Partials + @test_throws ArgumentError ForwardDiff.derivative(p, interval(3, 4)) +end + +@testset "Piecewise derivatives" begin + slide = Piecewise( + Domain{:open,:closed}(-Inf, -1) => x -> -2x - 1, + Domain{:open,:closed}(-1, 0) => x -> x^2, + Domain{:open,:open}(0, Inf) => Constant(0); + continuity = [1, 1] + ) + + @test ForwardDiff.derivative(slide, -5.5) == -2 + @test ForwardDiff.derivative(slide, -0.5) == -1 + @test ForwardDiff.derivative(slide, 1.2) == 0 + + @test isequal_interval(ForwardDiff.derivative(slide, interval(-7, -3)), interval(-2)) + @test isequal_interval(ForwardDiff.derivative(slide, interval(-0.7, -0.3)), interval(-1.4, -0.6)) + @test isequal_interval(ForwardDiff.derivative(slide, interval(0.7, 1.3)), interval(0)) + @test isequal_interval(ForwardDiff.derivative(slide, interval(-1.7, -0.3)), interval(-2, -0.6)) + @test isequal_interval(ForwardDiff.derivative(slide, interval(-0.7, 1.3)), interval(-1.4, 0)) + @test isequal_interval(ForwardDiff.derivative(slide, interval(-1.7, 1.3)), interval(-2, 0)) + + x1 = interval(-0.5, 0) + x2 = interval(-3, -2) + + grad1 = ForwardDiff.gradient(xx -> slide(-xx[1]^2), [x1, x2]) + grad2 = ForwardDiff.gradient(xx -> slide(0.7xx[2]), [x1, x2]) + + g1 = -2x1 * ForwardDiff.derivative(slide, -x1^2) + g2 = 0.7 * ForwardDiff.derivative(slide, x2) + + @test isequal_interval(grad1[1], g1) + @test isequal_interval(grad1[2], interval(0)) + @test isequal_interval(grad2[1], interval(0)) + @test isequal_interval(grad2[2], g2) + + grad = ForwardDiff.gradient(xx -> slide(-xx[1]^2 + 0.7xx[2]), [x1, x2]) + g1 = -2x1 * ForwardDiff.derivative(slide, -x1^2 + 0.7x2) + g2 = 0.7 * ForwardDiff.derivative(slide, -x1^2 + 0.7x2) + @test isequal_interval(grad[1], g1) + @test isequal_interval(grad[2], g2) +end + +@testset "Piecewise singularities" begin + f = Piecewise( + Domain{:open,:closed}(0, 1) => Constant(0), + Domain{:open,:closed}(1, 2) => x -> 0.5x, + Domain{:open,:closed}(2, 3) => Constant(1), + Domain{:open,:open}(3, 4) => x -> (x-3)^2 + 1; + continuity = [-1, 0, 1] + ) + + df = x -> ForwardDiff.derivative(f, x) + @test decoration(df(interval(0.5, 1.5))) == def + @test decoration(df(interval(1.5, 2.5))) == def + @test decoration(df(interval(2.5, 3.5))) == com +end + +@testset "_abs_deriv on Dual" begin + d = ForwardDiff.DiffRules._abs_deriv(Dual{Nothing}(interval(1, 2), interval(1.0))) + @test value(d) === interval(1, 1, com) + @test isequal_interval(partials(d, 1), interval(0)) + d0 = ForwardDiff.DiffRules._abs_deriv(Dual{Nothing}(interval(-1, 1), interval(1.0))) + @test value(d0) === interval(-1, 1, trv) + @test isequal_interval(partials(d0, 1), interval(0)) + dd = ForwardDiff.derivative(t -> ForwardDiff.derivative(abs, t), interval(1, 2)) + @test isequal_interval(dd, interval(0)) + @test decoration(dd) === com +end diff --git a/test/ext/IntervalArithmeticIntervalSetsExt.jl b/test/ext/IntervalArithmeticIntervalSetsExt.jl new file mode 100644 index 000000000..0d82035c0 --- /dev/null +++ b/test/ext/IntervalArithmeticIntervalSetsExt.jl @@ -0,0 +1,80 @@ +using Test +using IntervalArithmetic +import IntervalSets as IS + +@testset "IntervalSets to Interval" begin + i = interval(IS.Interval(1, 2)) + @test isequal_interval(i, interval(1.0, 2.0)) + @test decoration(i) === com + @test !isguaranteed(i) + i = interval(IS.Interval(0.1, 2)) + @test isequal_interval(i, interval(0.1, 2.0)) && !isguaranteed(i) + @test interval(Float64, IS.Interval(0.1, 2)) === i + + @test interval(IS.ClosedInterval(1, 2)) isa Interval{Float64} + @test interval(Float32, IS.ClosedInterval(1, 2)) isa Interval{Float32} + @test interval(BigFloat, IS.ClosedInterval(1, 2)) isa Interval{BigFloat} + @test interval(IS.ClosedInterval(1//2, 3//4)) isa Interval{Rational{Int}} + @test interval(IS.ClosedInterval(1.0f0, 2.0f0)) isa Interval{Float32} + + d = interval(IS.Interval{:closed,:closed}(1, 1)) + @test isequal_interval(d, interval(1, 1)) + @test decoration(d) === com + @test !isguaranteed(d) +end + +@testset "Open and infinite endpoints" begin + i = interval(IS.Interval{:closed,:open}(0.1, Inf)) + @test isequal_interval(i, interval(0.1, Inf)) && !isguaranteed(i) + @test decoration(i) === dac + @test interval(IS.Interval{:closed,:closed}(0.1, Inf)) === nai(Float64) + @test interval(IS.Interval{:open,:closed}(0.1, Inf)) === nai(Float64) + @test interval(IS.Interval{:open,:open}(0.1, Inf)) === nai(Float64) + @test interval(IS.Interval{:open,:open}(0.1, 1)) === nai(Float64) + @test interval(IS.Interval{:open,:closed}(0.1, 1)) === nai(Float64) + @test interval(IS.Interval{:closed,:open}(1, 2)) === nai(Float64) + @test interval(IS.OpenInterval(1, 2)) === nai(Float64) + @test_logs interval(IS.Interval{:open,:closed}(1, 2)) + + j = interval(IS.Interval{:open,:closed}(-Inf, 2)) + @test isequal_interval(j, interval(-Inf, 2.0)) + @test decoration(j) === dac + @test !isguaranteed(j) + @test interval(IS.Interval{:closed,:closed}(-Inf, 2)) === nai(Float64) + + e = interval(IS.Interval{:open,:open}(-Inf, Inf)) + @test isentire_interval(e) + @test decoration(e) === dac + @test !isguaranteed(e) + + @test isnai(@test_logs (:warn,) (:warn,) interval(IS.Interval{:closed,:closed}(2, 1))) +end + +@testset "Interval to IntervalSets" begin + @test IS.Interval(interval(1, 2)) === IS.Interval(1.0, 2.0) + @test IS.Interval(interval(0.1, 2)) === IS.Interval(0.1, 2.0) + @test IS.Interval(interval(1, 2)) === IS.Interval{:closed,:closed}(1.0, 2.0) + @test IS.Interval(interval(0.1, Inf)) === IS.Interval{:closed,:open}(0.1, Inf) + @test IS.Interval(interval(-Inf, 2)) === IS.Interval{:open,:closed}(-Inf, 2.0) + @test IS.Interval(interval(-Inf, Inf)) === IS.Interval{:open,:open}(-Inf, Inf) + @test IS.Interval(emptyinterval()) === IS.Interval{:open,:open}(Inf, -Inf) + @test IS.leftendpoint(IS.Interval(interval(0, 0))) === 0.0 + @test IS.Interval(interval(1//2, 3//4)) === IS.Interval(1//2, 3//4) + @test IS.Interval(interval(Float32, 1, 2)) === IS.Interval(1.0f0, 2.0f0) + # bounds(nai()) is (NaN, NaN), so NaI does not map to the empty IS.Interval + r = IS.Interval(nai()) + @test r isa IS.ClosedInterval{Float64} + @test isnan(IS.leftendpoint(r)) & isnan(IS.rightendpoint(r)) +end + +@testset "Round trips" begin + for x ∈ (interval(0.1, 2.0), interval(1, 1), interval(-2, 3)) + y = interval(IS.Interval(x)) + @test isequal_interval(x, y) + @test decoration(y) === decoration(x) + @test !isguaranteed(y) + end + y = interval(IS.Interval(interval(1, Inf))) + @test isequal_interval(y, interval(1, Inf)) && !isguaranteed(y) + @test isnai(@test_logs (:warn,) (:warn,) interval(IS.Interval(emptyinterval()))) +end diff --git a/test/ext/IntervalArithmeticIrrationalConstantsExt.jl b/test/ext/IntervalArithmeticIrrationalConstantsExt.jl new file mode 100644 index 000000000..439e97b56 --- /dev/null +++ b/test/ext/IntervalArithmeticIrrationalConstantsExt.jl @@ -0,0 +1,123 @@ +using Test +using IntervalArithmetic +import IrrationalConstants +import InteractiveUtils + +const IC_CONSTANTS = ( + IrrationalConstants.twoπ, IrrationalConstants.fourπ, IrrationalConstants.halfπ, + IrrationalConstants.quartπ, IrrationalConstants.invπ, IrrationalConstants.inv2π, + IrrationalConstants.inv4π, IrrationalConstants.fourinvπ, IrrationalConstants.twoinvπ, + IrrationalConstants.sqrt2, IrrationalConstants.sqrt3, IrrationalConstants.sqrtπ, + IrrationalConstants.sqrt2π, IrrationalConstants.sqrt4π, IrrationalConstants.sqrthalfπ, + IrrationalConstants.invsqrt2, IrrationalConstants.invsqrtπ, IrrationalConstants.invsqrt2π, + IrrationalConstants.logtwo, IrrationalConstants.logten, IrrationalConstants.loghalf, + IrrationalConstants.logπ, IrrationalConstants.log2π, IrrationalConstants.log4π) + +@testset "Extension loading" begin + @test Base.get_extension(IntervalArithmetic, :IntervalArithmeticIrrationalConstantsExt) !== nothing +end + +@testset "Tight enclosures" begin + for c ∈ IC_CONSTANTS + x = interval(c) + @test x isa Interval{Float64} + @test inf(x) <= BigFloat(c; precision = 256) <= sup(x) + @test sup(x) == nextfloat(inf(x)) + @test decoration(x) === com + @test isguaranteed(x) + b = bareinterval(BigFloat, c) + @test inf(x) <= inf(b) && sup(b) <= sup(x) + # the generated bareinterval in src/IntervalArithmetic.jl cannot see the extension _round methods + @test_throws MethodError bareinterval(Float64, c) + end +end + +@testset "Spot values" begin + @test isequal_interval(interval(IrrationalConstants.sqrt2), interval(1.414213562373095, 1.4142135623730951)) + @test isequal_interval(interval(IrrationalConstants.logtwo), interval(0.6931471805599453, 0.6931471805599454)) + @test isequal_interval(interval(IrrationalConstants.twoπ), interval(6.283185307179586, 6.283185307179587)) + @test isequal_interval(interval(IrrationalConstants.loghalf), interval(-0.6931471805599454, -0.6931471805599453)) + @test isequal_interval(interval(Float32, IrrationalConstants.sqrt2), interval(Float32, 1.4142135f0, 1.4142137f0)) + @test isequal_interval(interval(Rational{Int}, IrrationalConstants.sqrt2), interval(54608393//38613965, 77227930//54608393)) +end + +@testset "All exported constants and bound types" begin + for irr_name ∈ names(IrrationalConstants; all = false, imported = false) + irr = getproperty(IrrationalConstants, irr_name) + isa(irr, IrrationalConstants.IrrationalConstant) || continue + + for T ∈ (Float16, Float32, Float64, BigFloat) + @test in_interval(irr, interval(T, irr)) + if T !== BigFloat + @test nextfloat(inf(interval(T, irr))) == sup(interval(T, irr)) + end + end + + for T ∈ InteractiveUtils.subtypes(Signed) + @test in_interval(irr, interval(Rational{T}, irr)) broken = (irr == IrrationalConstants.invsqrt2π && T == Int8 && VERSION ≤ v"1.13") + end + + irr < 0 && continue + + for T ∈ InteractiveUtils.subtypes(Unsigned) + @test in_interval(irr, interval(Rational{T}, irr)) + end + end +end + +@testset "BigFloat precision" begin + x = interval(BigFloat, IrrationalConstants.sqrt2) + @test precision(inf(x)) == precision(BigFloat) + @test in_interval(IrrationalConstants.sqrt2, x) + setprecision(BigFloat, 512) do + y = interval(BigFloat, IrrationalConstants.sqrt2) + @test precision(inf(y)) == 512 + @test in_interval(IrrationalConstants.sqrt2, y) + end +end + +@testset "_round" begin + for c ∈ IC_CONSTANTS + lo = IntervalArithmetic._round(Float64, c, RoundDown) + hi = IntervalArithmetic._round(Float64, c, RoundUp) + @test lo < hi + @test lo < BigFloat(c; precision = 256) < hi + for T ∈ (Float16, Float32, Float64, BigFloat, Rational{Int}) + @test IntervalArithmetic._round(T, c, RoundDown) isa T + @test IntervalArithmetic._round(T, c, RoundUp) isa T + end + end +end + +@testset "_intervalize" begin + ext = Base.get_extension(IntervalArithmetic, :IntervalArithmeticIrrationalConstantsExt) + @test ext._intervalize(2) == :(IA.bareinterval(BigFloat, 2)) + for op ∈ (:+, :-, :*, :/, :sqrt, :log, :inv) + @test ext._intervalize(op) === op + end + @test ext._intervalize(:π) == :(IA.bareinterval(BigFloat, π)) + ex = ext._intervalize(:(2 * π)) + @test ex.head === :call + @test ex.args[1] === :* + @test ex.args[2] == :(IA.bareinterval(BigFloat, 2)) + @test ex.args[3] == :(IA.bareinterval(BigFloat, π)) + @test ext._intervalize(:(a = b)) == :(a = b) + @test ext._intervalize("str") == "str" + @test ext._intervalize(nothing) === nothing +end + +@testset "Cross-constant consistency" begin + @test issubset_interval(interval(IrrationalConstants.twoπ), interval(2) * interval(π)) + @test issubset_interval(interval(IrrationalConstants.halfπ), interval(π) / interval(2)) + @test issubset_interval(interval(IrrationalConstants.quartπ), interval(π) / interval(4)) + @test issubset_interval(interval(IrrationalConstants.invsqrt2), interval(1) / sqrt(interval(2))) + @test issubset_interval(interval(IrrationalConstants.sqrt2π), sqrt(interval(2) * interval(π))) + @test issubset_interval(interval(IrrationalConstants.loghalf), -interval(IrrationalConstants.logtwo)) + @test issubset_interval(interval(IrrationalConstants.log4π), interval(IrrationalConstants.logtwo) + interval(IrrationalConstants.log2π)) + @test issubset_interval(interval(IrrationalConstants.fourπ), interval(2) * interval(IrrationalConstants.twoπ)) + @test issubset_interval(interval(IrrationalConstants.fourinvπ), interval(2) * interval(IrrationalConstants.twoinvπ)) +end + +@testset "Unsupported irrationals" begin + @test_throws ArgumentError interval(Irrational{:notaconstant}()) +end diff --git a/test/ext/IntervalArithmeticLinearAlgebraExt.jl b/test/ext/IntervalArithmeticLinearAlgebraExt.jl new file mode 100644 index 000000000..733355940 --- /dev/null +++ b/test/ext/IntervalArithmeticLinearAlgebraExt.jl @@ -0,0 +1,342 @@ +using Test +using IntervalArithmetic +import LinearAlgebra +using LinearAlgebra: I, UniformScaling, opnorm, eigvals, eigvals!, eigen, det, mul! + +@testset "UniformScaling" begin + J = interval(I) + @test J isa UniformScaling{Interval{Float64}} + @test J.λ === interval(1, 1) + @test interval(Float64, I, I).λ === interval(1, 1) + @test interval(I, 2I).λ === interval(1, 2) + @test IntervalArithmetic._infer_numtype(2 * I) === Int + @test IntervalArithmetic._infer_numtype(2.0 * I) === Float64 + E = exact(2.0 * I) + @test E isa UniformScaling{ExactReal{Float64}} + @test E.λ === exact(2.0) + A = interval.([1.0 2.0; 3.0 4.0]) + @test all(isequal_interval.(A - interval(I), interval.([0.0 2.0; 3.0 3.0]))) + @test all(isequal_interval.(A - UniformScaling(interval(1)), interval.([0.0 2.0; 3.0 3.0]))) +end + +@testset "opnorm" begin + A = interval.([1.0 2.0; 3.0 4.0]) + @test isequal_interval(opnorm(A, 1), interval(6)) + @test isguaranteed(opnorm(A, 1)) + @test isequal_interval(opnorm(A, Inf), interval(7)) + @test isguaranteed(opnorm(A, Inf)) + o2 = opnorm(A, 2) + @test o2 isa Interval{Float64} + @test in_interval(5.464985704219043, o2) + + Ac = complex.(A, A) + @test opnorm(Ac, 1) isa Interval{Float64} + @test isguaranteed(opnorm(Ac, 1)) + @test opnorm(Ac, Inf) isa Interval{Float64} + + A32 = interval.(Float32[1 2; 3 4]) + @test opnorm(A32, 1) isa Interval{Float32} + @test isequal_interval(opnorm(A32, 1), interval(Float32, 6, 6)) + Abig = interval.(big.([1.0 2.0; 3.0 4.0])) + @test opnorm(Abig, 1) isa Interval{BigFloat} + + @test isequal_interval(opnorm(Matrix{Interval{Float64}}(undef, 0, 0), 1), interval(0)) + @test isequal_interval(opnorm(interval.(fill(-2.0, 1, 1)), 1), interval(2)) + @test isequal_interval(opnorm(interval.([1.0 -2.0]), 1), interval(2)) + @test isequal_interval(opnorm(interval.([1.0 -2.0]), Inf), interval(3)) +end + +@testset "eigvals" begin + A = interval.([1.0 2.0; 3.0 4.0]) + λ = eigvals(A) + @test λ isa Vector{Interval{Float64}} + @test in_interval((5 - sqrt(33)) / 2, λ[1]) + @test in_interval((5 + sqrt(33)) / 2, λ[2]) + B = copy(A) + eigvals!(B) + @test all(B .=== A) + @test eigvals(A; permute = false, scale = false, sortby = nothing) isa Vector{Interval{Float64}} + + R = interval.([0.0 -1.0; 1.0 0.0]) + μ = eigvals(R) + @test μ isa Vector{Complex{Interval{Float64}}} + @test isequal_interval(real(μ[1]), real(μ[2])) + @test isequal_interval(imag(μ[1]), -imag(μ[2])) + @test any(x -> in_interval(1, imag(x)), μ) + + D = [interval(1) interval(0); interval(0) interval(2)] + ν = eigvals(D) + @test ν isa Vector{Interval{Float64}} + @test in_interval(1, ν[1]) & in_interval(2, ν[2]) + + Cm = complex.(A, interval(1)) + @test eigvals(Cm) isa Vector{Complex{Interval{Float64}}} +end + +@testset "det" begin + A = interval.([1.0 2.0; 3.0 4.0]) + d = det(A) + @test d isa Interval{Float64} + @test in_interval(-2, d) + @test det(complex.(A, interval(1))) isa Complex{Interval{Float64}} + @test in_interval(0, det(interval.([1.0 2.0; 2.0 4.0]))) +end + +@testset "eigen" begin + A = interval.([1.0 2.0; 3.0 4.0]) + E = eigen(A) + @test E isa LinearAlgebra.Eigen + @test in_interval((5 - sqrt(33)) / 2, E.values[1]) + @test in_interval((5 + sqrt(33)) / 2, E.values[2]) + @test all(isguaranteed, E.vectors) + B = copy(A) + LinearAlgebra.eigen!(B) + @test all(B .=== A) + + W = A .+ interval(-1, 1) + EW = eigen(W) + @test all(isnai, EW.values) + @test all(isnai, EW.vectors) + + ANG = copy(A) + ANG[1, 1] = interval(1) + 1 + ENG = eigen(ANG) + @test all(x -> !isguaranteed(x), ENG.vectors) + + ext = Base.get_extension(IntervalArithmetic, :IntervalArithmeticLinearAlgebraExt) + λ = [complex(interval(1), interval(2))] + v = fill(complex(interval(1), interval(2)), 1, 1) + λ2, v2 = ext._fold_conjugate!(Complex{Interval{Float64}}, copy(λ), copy(v)) + @test all(λ2 .=== λ) & all(v2 .=== v) + λ3, v3 = ext._fold_conjugate!(Interval{Float64}, copy(λ), copy(v)) + @test isequal_interval(λ3[1], complex(interval(1), interval(0))) + @test isequal_interval(v3[1, 1], complex(interval(1), interval(0))) +end + +@testset "inv" begin + A = interval.([1.0 2.0; 3.0 4.0]) + iA = inv(A) + @test in_interval(-2, iA[1, 1]) & in_interval(1, iA[1, 2]) & in_interval(1.5, iA[2, 1]) & in_interval(-0.5, iA[2, 2]) + P = iA * A + @test in_interval(1, P[1, 1]) & in_interval(0, P[1, 2]) & in_interval(0, P[2, 1]) & in_interval(1, P[2, 2]) + @test all(isguaranteed, iA) + ANG = copy(A) + ANG[1, 1] = interval(1) + 1 + @test all(x -> !isguaranteed(x), inv(ANG)) + @test inv(complex.(A, interval(0))) isa Matrix{Complex{Interval{Float64}}} + @test all(isnai, inv(A .+ interval(-1, 1))) +end + +@testset "Matrix inversion" begin + IntervalArithmetic.configure(; matmul = :slow) + try + @test IntervalArithmetic.configuration_options.matmul === :slow + @test Base.invokelatest(IntervalArithmetic.default_matmul) === IntervalArithmetic.MatMulMode{:slow}() + A = [interval(2) interval(1, 2) ; interval(0) interval(1)] + @test all(isequal_interval.(Base.invokelatest(inv, A), [interval(0, 1) interval(-1.25, -0.25) ; interval(-0.5, 0.5) interval(0.5, 1.5)])) + B = [interval(2) interval(1, 2) ; interval(0) interval(0, 1)] + @test all(isnai, Base.invokelatest(inv, B)) + finally + IntervalArithmetic.configure(; matmul = :fast) + end +end + +@testset "exp and log" begin + A = interval.([1.0 2.0; 3.0 4.0]) + eA = exp(A) + @test eA isa Matrix{Interval{Float64}} + @test all(in_interval.(exp([1.0 2.0; 3.0 4.0]), eA)) + B = copy(A) + LinearAlgebra.exp!(B) + @test all(B .=== A) + @test exp(complex.(A, interval(0))) isa Matrix{Complex{Interval{Float64}}} + + L = interval.([2.0 0.0; 0.0 3.0]) + lg = log(L) + @test lg isa Matrix{Interval{Float64}} + @test in_interval(log(2), lg[1, 1]) & in_interval(log(3), lg[2, 2]) + @test all(isnai, log([interval(-0.1, 0.1) interval(0); interval(0) interval(2)])) + lgn = log(interval.([-2.0 0.0; 0.0 3.0])) + @test lgn isa Matrix{Complex{Interval{Float64}}} + @test in_interval(log(2), real(lgn[1, 1])) & in_interval(π, imag(lgn[1, 1])) + @test log(complex.(L, interval(0))) isa Matrix{Complex{Interval{Float64}}} + Pw = interval.([2.0 0.1; 0.1 3.0]) + @test all(issubset_interval.(Pw, exp(log(Pw)))) +end + +@testset "Matrix multiplication :slow" begin + IntervalArithmetic.configure(; matmul = :slow) + try + @test IntervalArithmetic.configuration_options.matmul === :slow + @test Base.invokelatest(IntervalArithmetic.default_matmul) === IntervalArithmetic.MatMulMode{:slow}() + + A = [interval(2, 4) interval(-2, 1) + interval(-1, 2) interval(2, 4)] + + b = [interval(-2, 2) + interval(-2, 2)] + + # exact for the generic algorithm; Rump's `:fast` algorithm widens the diagonal to [-2, 19.5] + @test all(isequal_interval.(Base.invokelatest(*, A, A), [interval(0, 18) interval(-16, 8) ; interval(-8, 16) interval(0, 18)])) + + @test all(isequal_interval.(Base.invokelatest(*, A, b), [interval(-12, 12), interval(-12, 12)])) + @test_throws IntervalArithmetic.InconclusiveBooleanOperation Base.invokelatest(\, A, b) + + @test all(isequal_interval.(interval.([1 2; 3 4]) * interval(-1, 1), [interval(-1, 1) interval(-2, 2) ; interval(-3, 3) interval(-4, 4)])) + + n = 100 + Aid = interval.(Matrix(1.0 * I, n, n)) + Bones = interval.(ones(n, n)) + @test all(isequal_interval.(Base.invokelatest(*, Aid, Bones), Bones)) + @test all(isequal_interval.(Base.invokelatest(*, Bones, Bones), interval.(fill(100.0, n, n)))) + + e00 = Matrix{Interval{Float64}}(undef, 0, 0) + @test size(Base.invokelatest(*, e00, e00)) == (0, 0) + @test isequal_interval(Base.invokelatest(*, interval.(fill(2.0, 1, 1)), interval.(fill(3.0, 1, 1)))[1, 1], interval(6)) + @test isequal_interval(Base.invokelatest(*, interval.(reshape([1.0, 2.0], 1, 2)), interval.(reshape([3.0, 4.0], 2, 1)))[1, 1], interval(11)) + @test size(Base.invokelatest(*, interval.(reshape([1.0, 2.0], 2, 1)), interval.(fill(3.0, 1, 1)))) == (2, 1) + finally + IntervalArithmetic.configure(; matmul = :fast) + end +end + +@testset "Matrix multiplication :fast" begin + IntervalArithmetic.configure(; matmul = :fast) + try + @test IntervalArithmetic.configuration_options.matmul === :fast + @test Base.invokelatest(IntervalArithmetic.default_matmul) === IntervalArithmetic.MatMulMode{:fast}() + + A = [interval(2, 4) interval(-2, 1) ; interval(-1, 2) interval(2, 4)] + imA = interval(im) * A + + @test all(issubset_interval.([interval(0, 18) interval(-16, 8) ; interval(-8, 16) interval(0, 18)], Base.invokelatest(*, A, A))) + @test all(issubset_interval.([interval(5, 12.5) interval(-8, 2) ; interval(-2, 8) interval(5, 12.5)], Base.invokelatest(*, A, mid.(A)))) + @test all(issubset_interval.([interval(5, 12.5) interval(-8, 2) ; interval(-2, 8) interval(5, 12.5)], Base.invokelatest(*, mid.(A), A))) + + @test all(issubset_interval.([interval(-18, 0) interval(-8, 16) ; interval(-16, 8) interval(-18, 0)], Base.invokelatest(*, imA, imA))) + @test all(issubset_interval.(interval(im)*[interval(5, 12.5) interval(-8, 2) ; interval(-2, 8) interval(5, 12.5)], Base.invokelatest(*, mid.(A), imA))) + @test all(issubset_interval.(interval(im)*[interval(5, 12.5) interval(-8, 2) ; interval(-2, 8) interval(5, 12.5)], Base.invokelatest(*, imA, mid.(A)))) + + e00 = Matrix{Interval{Float64}}(undef, 0, 0) + @test size(Base.invokelatest(*, e00, e00)) == (0, 0) + @test isequal_interval(Base.invokelatest(*, interval.(fill(2.0, 1, 1)), interval.(fill(3.0, 1, 1)))[1, 1], interval(6)) + @test isequal_interval(Base.invokelatest(*, interval.(reshape([1.0, 2.0], 1, 2)), interval.(reshape([3.0, 4.0], 2, 1)))[1, 1], interval(11)) + @test size(Base.invokelatest(*, interval.(reshape([1.0, 2.0], 2, 1)), interval.(fill(3.0, 1, 1)))) == (2, 1) + finally + IntervalArithmetic.configure(; matmul = :fast) + end +end + +@testset "mul!" begin + A = interval.([1.0 2.0; 3.0 4.0]) + B = [interval(2, 4) interval(-2, 1); interval(-1, 2) interval(2, 4)] + C = A * B + C2 = similar(C) + @test mul!(C2, A, B) === C2 + @test all(C2 .=== C) + + C3 = copy(B); mul!(C3, A, B, 0, 0) + @test all(x -> isequal_interval(x, interval(0)), C3) + C3 = copy(B); mul!(C3, A, B, interval(0), interval(1)) + @test all(C3 .=== B) + C3 = copy(B); mul!(C3, A, B, 0, 2) + @test all(isequal_interval.(C3, B .* 2)) + C3 = copy(B); mul!(C3, A, B, 1, 1) + @test all(isequal_interval.(C3, C .+ B)) + C3 = copy(B); mul!(C3, A, B, 2, 0) + @test all(isequal_interval.(C3, C .* 2)) + C3 = copy(B); mul!(C3, A, B, 2, 3) + @test all(isequal_interval.(C3, C .* 2 .+ B .* 3)) + + v = [interval(1), interval(2)] + @test all(isequal_interval.(A * v, [interval(5), interval(11)])) + cv = copy(v); mul!(cv, A, v, 2, 3) + @test all(isequal_interval.(cv, (A * v) .* 2 .+ v .* 3)) + + @test_throws DimensionMismatch mul!(similar(C), A, interval.(ones(3, 3)), 1, 0) + @test_throws DimensionMismatch mul!(similar(v), A, interval.(ones(3)), 1, 0) +end + +@testset "NG flag propagation" begin + A = interval.([1.0 2.0; 3.0 4.0]) + B = [interval(2, 4) interval(-2, 1); interval(-1, 2) interval(2, 4)] + @test all(isguaranteed, A * B) + ANG = copy(A) + ANG[1, 1] = interval(1) + 1 + @test all(x -> !isguaranteed(x), ANG * B) + @test all(x -> !isguaranteed(x), A * (interval(1) + 1)) + BNG = copy(B) + BNG[2, 2] = interval(1) + 1 + @test all(x -> !isguaranteed(x), A * BNG) + Cng = similar(A) + mul!(Cng, A, B, interval(1) + 1, interval(0)) + @test all(x -> !isguaranteed(x), Cng) + mul!(Cng, A, B, interval(1), interval(0) + 0) + @test all(x -> !isguaranteed(x), Cng) + @test all(x -> !isguaranteed(x), complex.(ANG, interval(0)) * complex.(B, B)) + + ext = Base.get_extension(IntervalArithmetic, :IntervalArithmeticLinearAlgebraExt) + r = [interval(1), interval(2)] + ext._ensure_ng_flag!(r, false) + @test all(x -> !isguaranteed(x), r) + z = [complex(interval(1), interval(2))] + ext._ensure_ng_flag!(z, false) + @test !isguaranteed(real(z[1])) & !isguaranteed(imag(z[1])) + ext._ensure_ng_flag!(z, true) + @test isguaranteed(z[1]) +end + +@testset "Fast multiplication coverage" begin + A = interval.([1.0 2.0; 3.0 4.0]) + B = [interval(2, 4) interval(-2, 1); interval(-1, 2) interval(2, 4)] + iA = complex.(A, A) + iB = complex.(B, B) + cf = [complex(1.0, 2.0) complex(0.0, 0.0); complex(0.0, 0.0) complex(3.0, 4.0)] + rf = [1.0 2.0; 3.0 4.0] + mAB = [3.0 -0.5; 0.5 3.0] + + @test all(in_interval.(rf * mAB, A * B)) + @test all(in_interval.(rf * rf, A * rf)) + @test all(in_interval.(rf * rf, rf * A)) + @test all(in_interval.(complex.(rf, rf) * complex.(mAB, mAB), iA * iB)) + @test all(in_interval.(complex.(rf, rf) * cf, iA * cf)) + @test all(in_interval.(cf * complex.(mAB, mAB), cf * iB)) + @test all(in_interval.(complex.(rf, rf) * mAB, iA * B)) + @test all(in_interval.(complex.(rf, rf) * rf, iA * rf)) + @test all(in_interval.(cf * mAB, cf * B)) + @test all(in_interval.(rf * complex.(mAB, mAB), A * iB)) + @test all(in_interval.(rf * complex.(mAB, mAB), rf * iB)) + @test all(in_interval.(mAB * cf, B * cf)) + + A32 = interval.(Float32[1 2; 3 4]) + @test A32 * A32 isa Matrix{Interval{Float32}} + @test all(in_interval.(Float32[7 10; 15 22], A32 * A32)) + @test all(isequal_interval.(view(A, 1:2, 1:2) * B, A * B)) + @test all(in_interval.(rf' * mAB, A' * B)) + @test all(in_interval.(rf * mAB, [1.0 2.0; 3.0 4.0] * B)) + @test all(in_interval.(mAB * rf, B * [1.0 2.0; 3.0 4.0])) +end + +@testset "Fast multiplication fallbacks" begin + Abig = interval.(big.([1.0 2.0; 3.0 4.0])) + Cbig = @test_logs (:info,) Abig * Abig + @test all(isequal_interval.(Cbig, interval.(big.([7.0 10.0; 15.0 22.0])))) + Arat = [interval(1//2) interval(1//3); interval(1//4) interval(1//5)] + @test_logs (:info,) Arat * Arat +end + +@testset "Fast multiplication internals" begin + ext = Base.get_extension(IntervalArithmetic, :IntervalArithmeticLinearAlgebraExt) + B = [interval(2, 4) interval(-2, 1); interval(-1, 2) interval(2, 4)] + mB, rB = ext._vec_or_mat_midradius(B) + @test all(rB .>= 0) + @test all(big.(mB) .- big.(rB) .<= inf.(B)) + @test all(big.(mB) .+ big.(rB) .>= sup.(B)) + + before = ext._getrounding() + _ = interval.([1.0 2.0; 3.0 4.0]) * B + @test ext._getrounding() == before + + @test ext._to_stride_64([1.0 2.0; 3.0 4.0]) == [1.0 2.0; 3.0 4.0] + @test ext._to_stride_64(Float32[1 2; 3 4]) isa Matrix{Float64} +end diff --git a/test/ext/IntervalArithmeticSparseArraysExt.jl b/test/ext/IntervalArithmeticSparseArraysExt.jl new file mode 100644 index 000000000..7f0ec1984 --- /dev/null +++ b/test/ext/IntervalArithmeticSparseArraysExt.jl @@ -0,0 +1,69 @@ +using Test +using IntervalArithmetic +import SparseArrays +using SparseArrays: SparseMatrixCSC, sparse, spzeros, nnz, findnz, dropzeros, dropzeros! + +@testset "Extension loading" begin + @test Base.get_extension(IntervalArithmetic, :IntervalArithmeticSparseArraysExt) !== nothing +end + +@testset "Structural zero predicates" begin + @test SparseArrays._iszero(interval(0, 0)) + @test !SparseArrays._iszero(interval(-1, 1)) + @test !SparseArrays._iszero(interval(1, 2)) + @test SparseArrays._iszero(interval(-0.0, 0.0)) + @test !SparseArrays._iszero(emptyinterval()) + @test !SparseArrays._iszero(nai()) + @test !SparseArrays._iszero(entireinterval()) + for x ∈ (interval(0, 0), interval(-1, 1), interval(1, 2)) + @test SparseArrays._isnotzero(x) == !SparseArrays._iszero(x) + end + @test SparseArrays._iszero(interval(Float32, 0, 0)) + @test SparseArrays._iszero(interval(BigFloat, 0, 0)) + @test SparseArrays._iszero(interval(0//1, 0//1)) + @test SparseArrays._iszero(interval(0, 0) + 0) + @test !isguaranteed(interval(0, 0) + 0) + @test SparseArrays._iszero(complex(interval(0, 0), interval(0, 0))) +end + +@testset "Sparse construction" begin + A = [interval(0, 0) interval(1, 2) ; interval(-1, 1) interval(0.0)] + S = sparse(A) + @test nnz(S) == 2 + @test isequal_interval(S[2, 1], interval(-1, 1)) + @test isequal_interval(S[1, 2], interval(1, 2)) + v = sparse([interval(1, 2), interval(0, 0), interval(-1, 1)]) + @test nnz(v) == 2 + @test findnz(v)[1] == [1, 3] + Z = spzeros(Interval{Float64}, 3, 3) + @test nnz(Z) == 0 + @test all(x -> x === zero(Interval{Float64}), Matrix(Z)) + @test nnz(sparse(interval.(zeros(3, 3)))) == 0 + for T ∈ (Float32, BigFloat, Rational{Int}) + B = [interval(T, 0, 0) interval(T, 1, 2) ; interval(T, -1, 1) interval(T, 0, 0)] + @test nnz(sparse(B)) == 2 + end +end + +@testset "dropzeros" begin + S = SparseMatrixCSC(2, 2, [1, 3, 3], [1, 2], [interval(0, 0), interval(-1, 1)]) + S2 = dropzeros(S) + @test nnz(S) == 2 + @test nnz(S2) == 1 + dropzeros!(S) + @test nnz(S) == 1 + @test isequal_interval(S[2, 1], interval(-1, 1)) +end + +@testset "Sparse arithmetic" begin + A = [interval(1, 2) interval(0, 0) ; interval(3, 4) interval(-1, 1)] + S = sparse(A) + v = [interval(1), interval(2)] + @test all(isequal_interval.(S * v, A * v)) + @test all(isequal_interval.(Matrix(S + S), A + A)) + @test all(isequal_interval.(Matrix(S - S), A - A)) + AA = [interval(1, 4) interval(0, 0) ; interval(-1, 12) interval(-1, 1)] + @test all(issubset_interval.(AA, Matrix(S * S))) + @test all(issubset_interval.(AA, A * A)) + @test all(isequal_interval.(S + A, A + A)) +end diff --git a/test/generate_ITF1788.jl b/test/generate_ITF1788.jl index 0dca9e659..fd5e6c4d9 100644 --- a/test/generate_ITF1788.jl +++ b/test/generate_ITF1788.jl @@ -82,11 +82,7 @@ functions = Dict( "isSingleton" => x -> "isthin($x)", "isMember" => x -> "in_interval($x)", "cancelPlus" => x -> "cancelplus($x)", - "cancelMinus" => x -> "cancelminus($x)", - "sum_nearest" => x -> "sum($x)", - "dot_nearest" => x -> "sum(.*($x))", - "sum_abs_nearest" => x -> "sum(abs.($x))", - "sum_sqr_nearest" => x -> "sum($x.^2)" + "cancelMinus" => x -> "cancelminus($x)" ) """ @@ -160,17 +156,6 @@ function parse_command(line) expr = build_expression(lhs, rhs) command = "@test $expr" - if occursin("dot_nearest {0x10000000000001p0, 0x1p104} {0x0fffffffffffffp0, -1.0} = -1.0", line) - # broken test unrelated to interval airthmetic - command = "@test_broken $expr" - elseif occursin("atan2 [-0.0, 1.0]_com [-2.0, -0.1]_com = [0X1.ABA397C7259DDP+0, 0X1.921FB54442D19P+1]_dac", line) - # erroneous test: the decoration of the result should be `com` - command = - """ - @warn "The original test `atan2 [-0.0, 1.0]_com [-2.0, -0.1]_com = [0X1.ABA397C7259DDP+0, 0X1.921FB54442D19P+1]_dac` is wrong and has been modified. The result should have the decoration `com`" - @test atan(interval(bareinterval(-0.0, 1.0), com), interval(bareinterval(-2.0, -0.1), com)) === interval(bareinterval(0x1.ABA397C7259DDP+0, 0x1.921FB54442D19P+1), com)""" - end - command = haswarning ? "@test_logs (:warn,) $command" : command return command diff --git a/test/interval_tests/IntervalArithmeticArblibExt.jl b/test/interval_tests/IntervalArithmeticArblibExt.jl deleted file mode 100644 index 770dc8bff..000000000 --- a/test/interval_tests/IntervalArithmeticArblibExt.jl +++ /dev/null @@ -1,344 +0,0 @@ -@testset "IntervalArithmeticArblibExt" begin - Arf = Arblib.Arf - ArfRef = Arblib.ArfRef - Arb = Arblib.Arb - ArbRef = Arblib.ArbRef - Acb = Arblib.Acb - setball = Arblib.setball - - # There is a bug in Flint before version 3.3.0 that gives NaN for - # getinterval on balls with infinite midpoint and radius. Check if - # we are using such a version to mark tests as broken. - broken_getinterval = isnan(Arblib.getinterval(setball(Arb, -Inf, Inf))[2]) - - @testset "Promotion rules" begin - # Arf behaves like normal promotion with Interval - @test promote_type(Arf, Interval{Float64}) == Interval{Arf} - @test promote_type(Arf, Interval{BigFloat}) == Interval{Arf} - @test promote_type(Arf, Interval{Rational{Int}}) == Interval{Arf} - @test promote_type(ArfRef, Interval{Float64}) == Interval{Arf} - @test promote_type(ArfRef, Interval{BigFloat}) == Interval{Arf} - @test promote_type(ArfRef, Interval{Rational{Int}}) == Interval{Arf} - @test promote_type(Interval{Float64}, Arf) == Interval{Arf} - @test promote_type(Interval{BigFloat}, Arf) == Interval{Arf} - @test promote_type(Interval{Rational{Int}}, Arf) == Interval{Arf} - @test promote_type(Interval{Float64}, ArfRef) == Interval{Arf} - @test promote_type(Interval{BigFloat}, ArfRef) == Interval{Arf} - @test promote_type(Interval{Rational{Int}}, ArfRef) == Interval{Arf} - - - - # Promotion between Arb and Interval is not allowed - @test_throws ArgumentError promote_type(Arb, Interval{Float64}) - @test_throws ArgumentError promote_type(Arb, Interval{BigFloat}) - @test_throws ArgumentError promote_type(Arb, Interval{Arf}) - @test_throws ArgumentError promote_type(Arb, Interval{Arb}) - @test_throws ArgumentError promote_type(ArbRef, Interval{Float64}) - @test_throws ArgumentError promote_type(ArbRef, Interval{BigFloat}) - @test_throws ArgumentError promote_type(ArbRef, Interval{Arf}) - @test_throws ArgumentError promote_type(ArbRef, Interval{Arb}) - @test_throws ArgumentError promote_type(Interval{Float64}, Arb) - @test_throws ArgumentError promote_type(Interval{BigFloat}, Arb) - @test_throws ArgumentError promote_type(Interval{Arf}, Arb) - @test_throws ArgumentError promote_type(Interval{Arb}, Arb) - @test_throws ArgumentError promote_type(Interval{Float64}, ArbRef) - @test_throws ArgumentError promote_type(Interval{BigFloat}, ArbRef) - @test_throws ArgumentError promote_type(Interval{Arf}, ArbRef) - @test_throws ArgumentError promote_type(Interval{Arb}, ArbRef) - end - - @testset "Interval from Arb" begin - @testset "promote_numtype" begin - @test IntervalArithmetic.promote_numtype(Arb, Arb) == BigFloat - @test IntervalArithmetic.promote_numtype(Arb, ArbRef) == BigFloat - @test IntervalArithmetic.promote_numtype(ArbRef, Arb) == BigFloat - @test IntervalArithmetic.promote_numtype(ArbRef, ArbRef) == BigFloat - - @test IntervalArithmetic.promote_numtype(Arb, Float64) == BigFloat - @test IntervalArithmetic.promote_numtype(ArbRef, Float64) == BigFloat - @test IntervalArithmetic.promote_numtype(Arb, Rational{Int}) == BigFloat - @test IntervalArithmetic.promote_numtype(ArbRef, Rational{Int}) == BigFloat - - @test IntervalArithmetic.promote_numtype(Arb, Arf) == Arf - @test IntervalArithmetic.promote_numtype(ArbRef, Arf) == Arf - @test IntervalArithmetic.promote_numtype(Arb, ArfRef) == Arf - @test IntervalArithmetic.promote_numtype(ArbRef, ArfRef) == Arf - end - - @testset "Single argument constructor" begin - # Valid intervals - xs = Arb[ - 0, - 1, - π, - ℯ, - 1//3, - Arb((1, 2)), - Arb((-Inf, Inf)), - setball(Arb, 5, Inf), - #setball(Arb, -Inf, Inf), - #setball(Arb, Inf, Inf), - ] - - for x in xs - @test interval(x) isa Interval{BigFloat} - - for T in [BigFloat, Arf, Float64, Float32] - y = interval(T, x) - @test y isa Interval{T} - @test !isnai(y) - @test isbounded(y) == isfinite(x) - @test Arblib.contains(Arb(y), x) - end - end - - # These are bugs in Flint - # @test isequal_interval(interval(setball(Arb, -Inf, Inf)), interval(-Inf, Inf)) broken = - # broken_getinterval - # @test isequal_interval(interval(setball(Arb, Inf, Inf)), interval(-Inf, Inf)) broken = - # broken_getinterval - - # Invalid intervals - @test isnai(interval(Arb(-Inf))) - @test isnai(interval(Arb(Inf))) - @test isnai(interval(Arb(NaN))) - end - - @testset "Two argument constructor" begin - # Valid infs and sups - # All <=3 - as1 = Real[0, 1, ℯ, 1//3, 0.1, BigFloat(1.1), BigInt(3)] - # All >=3 - bs1 = Real[3, π, 7//2, BigFloat(4.1), BigInt(4)] - - for a in as1 - for b in bs1 - a_Arb = Arb(a) - b_Arb = Arb(b) - - y1 = interval(a, b_Arb) - y2 = interval(a_Arb, b) - y3 = interval(a_Arb, b_Arb) - - @test y1 isa Interval{BigFloat} - @test y2 isa Interval{BigFloat} - @test y3 isa Interval{BigFloat} - - @test Arblib.overlaps(Arb(y1), a_Arb) - @test Arblib.overlaps(Arb(y1), b_Arb) - @test Arblib.overlaps(Arb(y2), a_Arb) - @test Arblib.overlaps(Arb(y2), b_Arb) - @test Arblib.overlaps(Arb(y3), a_Arb) - @test Arblib.overlaps(Arb(y3), b_Arb) - - # TODO: Doesn't work for Arf because it doesn't support nextfloat - for T in [BigFloat, Float64, Float32] - y1 = interval(T, a, b_Arb) - y2 = interval(T, a_Arb, b) - y3 = interval(T, a_Arb, b_Arb) - - @test y1 isa Interval{T} - @test y2 isa Interval{T} - @test y3 isa Interval{T} - - @test Arblib.overlaps(Arb(y1), a_Arb) - @test Arblib.overlaps(Arb(y1), b_Arb) - @test Arblib.overlaps(Arb(y2), a_Arb) - @test Arblib.overlaps(Arb(y2), b_Arb) - @test Arblib.overlaps(Arb(y3), a_Arb) - @test Arblib.overlaps(Arb(y3), b_Arb) - end - end - end - - # Overlapping intervals - @test isequal_interval( - interval(interval(1, 3), interval(2, 4)), - interval(setball(Arb, 2, 1), setball(Arb, 3, 1)), - ) - @test isequal_interval( - interval(interval(1, 3), interval(0, 4)), - interval(setball(Arb, 2, 1), setball(Arb, 2, 2)), - ) - @test isequal_interval( - interval(interval(1, 5), interval(2, 4)), - interval(setball(Arb, 3, 2), setball(Arb, 3, 1)), - ) - - # Infinite, but valid, intervals - as2 = [ - Arb(-Inf), - # setball(Arb, -Inf, 1), - Arb((-Inf, Inf)), - # setball(Arb, -Inf, Inf), - setball(Arb, 0, Inf), - # setball(Arb, Inf, Inf), - ] - - bs2 = [ - Arb(Inf), - # setball(Arb, Inf, 1), - Arb((-Inf, Inf)), - # setball(Arb, -Inf, Inf), - setball(Arb, 0, Inf), - # setball(Arb, Inf, Inf), - ] - - for a in as2 - for b in bs2 - # This is a bug in Flint - broken = - broken_getinterval && ( - isequal(a, setball(Arb, Inf, Inf)) || - isequal(b, setball(Arb, -Inf, Inf)) - ) - @test isequal_interval(interval(a, b), interval(-Inf, Inf)) broken = - broken - end - end - - for a in as2 - # This is a bug in Flint - broken = broken_getinterval && isequal(a, setball(Arb, Inf, Inf)) - @test isequal_interval(interval(a, Inf), interval(-Inf, Inf)) broken = - broken - end - - for b in bs2 - # This is a bug in Flint - broken = broken_getinterval && isequal(b, setball(Arb, -Inf, Inf)) - @test isequal_interval(interval(-Inf, b), interval(-Inf, Inf)) broken = - broken - end - - # Invalid intervals - - # [-Inf, -Inf] or [Inf, Inf] - @test isnai(interval(Arb(-Inf), Arb(-Inf))) - @test isnai(interval(-Inf, Arb(-Inf))) - @test isnai(interval(Arb(-Inf), -Inf)) - @test isnai(interval(setball(Arb, -Inf, 1), Arb(-Inf))) - @test isnai(interval(Arb(-Inf), setball(Arb, -Inf, 1))) - @test isnai(interval(Arb(Inf), Arb(Inf))) - @test isnai(interval(Inf, Arb(Inf))) - @test isnai(interval(Arb(Inf), Inf)) - @test isnai(interval(setball(Arb, Inf, 1), Arb(Inf))) - @test isnai(interval(Arb(Inf), setball(Arb, Inf, 1))) - - # With NaN - @test isnai(interval(Arb(NaN), 0)) - @test isnai(interval(setball(Arb, NaN, Inf), 0)) - @test isnai(interval(Arb(NaN), -Inf)) - @test isnai(interval(Arb(NaN), Inf)) - @test isnai(interval(0, Arb(NaN))) - @test isnai(interval(0, setball(Arb, NaN, Inf))) - @test isnai(interval(-Inf, Arb(NaN))) - @test isnai(interval(Inf, Arb(NaN))) - - # A few tests to check that :midpoint constructor seems ok - @test isequal_interval( - interval(setball(Arb, 0, 1), format = :midpoint), - interval(-1, 1), - ) - @test isequal_interval( - interval(setball(Arb, 0, 1), setball(Arb, 4, 1), format = :midpoint), - interval(-6, 6), - ) - @test_throws DomainError interval(0, Arb((-1, 1)), format = :midpoint) - end - - @testset "Complex intervals" begin - @test isequal_interval( - interval(1 + 2im, 3 + 4im), - interval(Acb(setball(Arb, 2, 1), setball(Arb, 3, 1))), - ) - @test isequal_interval( - interval(Float64, 1 + 2im, 3 + 4im), - interval(Acb(setball(Arb, 2, 1), setball(Arb, 3, 1))), - ) - - @test isequal_interval( - interval(1 + 2im, 3 + 4im), - interval(Acb(1, 2), Acb(3, 4)), - ) - - @test isequal_interval(interval(1 + 2im, 3 + 4im), interval(Acb(1, 2), 3 + 4im)) - @test isequal_interval(interval(1 - 2im, 3), interval(Acb(1, -2), 3)) - @test isequal_interval( - interval(1 - 2im, 3), - interval(Acb(1, -2), interval(3, 3)), - ) - - @test isequal_interval(interval(1 + 2im, 3 + 4im), interval(1 + 2im, Acb(3, 4))) - @test isequal_interval(interval(1, 3 + 2im), interval(1, Acb(3, 2))) - @test isequal_interval(interval(1, 3 + 2im), interval(interval(1), Acb(3, 2))) - end - - @testset "convert" begin - @test isguaranteed(convert(Interval{Float64}, Arb(1))) - @test isguaranteed(convert(Interval{Float64}, Acb(1))) - @test_throws DomainError convert(Interval{Float64}, Acb(1, 1)) - - @test isguaranteed(convert(Complex{Interval{Float64}}, Arb(1))) - @test isguaranteed(convert(Complex{Interval{Float64}}, Acb(1))) - @test isguaranteed(convert(Complex{Interval{Float64}}, Acb(1, 1))) - end - end - - @testset "Arb from Interval" begin - # Check constructor - @test isequal(Arb((0, 1)), Arb(interval(0, 1))) - @test isequal(setball(Arb, NaN, Inf), Arb(nai(Float64))) - @test Arblib.overlaps(Arb(π), Arb(interval(π))) - @test Arblib.overlaps(Arb(π), Arb(interval(BigFloat, π))) - @test isequal(Arblib.indeterminate!(Arb()), Arb(emptyinterval())) - @test isequal(Acb(1, 2), Acb(interval(1 + 2im))) - - # Check Arblib.set! - @test isequal(Arb((0, 1)), Arblib.set!(Arb(), interval(0, 1))) - @test isequal(setball(Arb, NaN, Inf), Arblib.set!(Arb(), nai(Float64))) - @test isequal(Arb(interval(π)), Arblib.set!(Arb(), interval(π))) - @test isequal(Arb(interval(BigFloat, π)), Arblib.set!(Arb(), interval(BigFloat, π))) - @test isequal(Arblib.indeterminate!(Arb()), Arblib.set!(Arb(), emptyinterval())) - @test isequal(Acb(1, 2), Arblib.set!(Acb(), interval(1 + 2im))) - # Check that prec argument works - @test Arblib.contains_interior( - Arblib.set!(Arb(), interval(BigFloat, π), prec = 64), - Arb(interval(BigFloat, π)), - ) - - # Check _precision - # One argument - @test Arblib._precision(interval(1, 2)) == precision(Arb) - @test Arblib._precision(interval(BigFloat, BigFloat(1, precision = 80))) == 80 - @test Arblib._precision( - interval(BigFloat, BigFloat(1, precision = 80), BigFloat(1, precision = 64)), - ) == 80 - @test Arblib._precision( - interval(BigFloat, BigFloat(1, precision = 64), BigFloat(1, precision = 80)), - ) == 80 - - # Check that precision is preserved - @test precision( - Arb(interval(BigFloat(1, precision = 80), BigFloat(2, precision = 64))), - ) == 80 - @test precision(Arb(interval(Arf(0, prec = 64), Arf(1, prec = 80)))) == 80 - end - - # Check that the ambiguity related changes actually work - @testset "ExactReal" begin - @test Arblib.Mag(exact(5)) == Arblib.Mag(5) - @test Arf(exact(5)) == Arf(5) - @test Arb(exact(5)) == Arb(5) - - @test promote_type(Arf, ExactReal{Float64}) == Arf - @test promote_type(ArfRef, ExactReal{Float64}) == Arf - @test promote_type(ExactReal{Float64}, Arf) == Arf - @test promote_type(ExactReal{Float64}, ArfRef) == Arf - - @test promote_type(Arb, ExactReal{Float64}) == Arb - @test promote_type(ArbRef, ExactReal{Float64}) == Arb - @test promote_type(ExactReal{Float64}, Arb) == Arb - @test promote_type(ExactReal{Float64}, ArbRef) == Arb - end -end diff --git a/test/interval_tests/IntervalArithmeticIrrationalConstantsExt.jl b/test/interval_tests/IntervalArithmeticIrrationalConstantsExt.jl deleted file mode 100644 index 8e9a5ad45..000000000 --- a/test/interval_tests/IntervalArithmeticIrrationalConstantsExt.jl +++ /dev/null @@ -1,32 +0,0 @@ -import IrrationalConstants - -@testset "IrrationalConstants.jl" begin - # Get all exported constants from IrrationalConstants - irr_names = names(IrrationalConstants; all=false, imported=false) - - for irr_name ∈ irr_names - irr = getproperty(IrrationalConstants, irr_name) - # Skip non-irrational types - if !isa(irr, IrrationalConstants.IrrationalConstant) - continue - end - - for T ∈ (Float16, Float32, Float64, BigFloat) - @test in_interval(irr, interval(T, irr)) - if T !== BigFloat - @test nextfloat(inf(interval(T, irr))) == sup(interval(T, irr)) - end - end - - for T ∈ InteractiveUtils.subtypes(Signed) - @test in_interval(irr, interval(Rational{T}, irr)) broken=(irr == IrrationalConstants.invsqrt2π && T == Int8 && VERSION ≤ v"1.13") - end - - # Negative irrationals obviously lack unsigned representations - irr < 0 && continue - - for T ∈ InteractiveUtils.subtypes(Unsigned) - @test in_interval(irr, interval(Rational{T}, irr)) - end - end -end diff --git a/test/interval_tests/bisect.jl b/test/interval_tests/bisect.jl deleted file mode 100644 index 3be3e20e3..000000000 --- a/test/interval_tests/bisect.jl +++ /dev/null @@ -1,20 +0,0 @@ -@testset "Interval" begin - x = emptyinterval() - @test all(isequal_interval.(bisect(x), (x, x))) - - x = I"0.1" - @test isatomic(x) - @test all(isequal_interval.(bisect(x), (x, emptyinterval()))) - - x = interval(0, 1) - @test all(isequal_interval.( bisect(x, 0.5), (interval(0, 0.5), interval(0.5, 1) ) )) - @test all(isequal_interval.( bisect(x, 0.25), (interval(0, 0.25), interval(0.25, 1) ) )) - @test all(isequal_interval.( bisect(x), (interval(0.0, 0.5), interval(0.5, 1.0)) )) - - x = interval(-Inf, Inf) - @test all(isequal_interval.( bisect(x, 0.5), (interval(-Inf, 0), interval(0, Inf)) )) - y = bisect(x, nextfloat(0.5)) - @test sup(y[1]) == inf(y[2]) > 0 - y = bisect(x, prevfloat(0.5)) - @test sup(y[1]) == inf(y[2]) < 0 -end diff --git a/test/interval_tests/complex.jl b/test/interval_tests/complex.jl deleted file mode 100644 index 8054f9207..000000000 --- a/test/interval_tests/complex.jl +++ /dev/null @@ -1,72 +0,0 @@ -@testset "Complex interval operations" begin - a = interval(1im) - b = interval(4im + 3) - c = complex(interval(-1, 4), interval(0, 2)) - - @test typeof(a) == Complex{Interval{Float64}} - - @test issubset_interval(a, c) - @test isinterior(a, c) - @test !issubset_interval(b, c) - - @test isequal_interval(a, complex(interval(0), interval(1))) - @test isequal_interval(a * a, interval(-1)) - @test isequal_interval(a + a, interval(2)*interval(im)) - @test isthin(a - a, 0) - @test isthin(a / a, 1) - - @test in_interval(3+2im, c) - @test isequal_interval(hull(a, b), complex(interval(0, 3), interval(1, 4))) - @test isequal_interval(intersect_interval(c, hull(a, b)), complex(interval(0, 3), interval(1, 2))) - @test isempty_interval(intersect_interval(a, b)) - @test isdisjoint_interval(a, b) - - @test isequal_interval(interval(-1 - im, 0), interval(-1 - im, 0 + 0im)) - @test isequal_interval(interval(0, 1 + im), interval(0 + 0im, 1 + im)) -end - -@testset "Inverse roots of unity" begin - for i ∈ 0:99 - if Int == Int32 && VERSION < v"1.10" - @test issubset_interval(cispi( -interval(i)/interval(50) ), inv(cispi( interval(i)/interval(50) ))) && - radius( inv(cispi( interval(i)/interval(50) )) ) < 100eps() - else - @test issubset_interval(cispi( -interval(i)/interval(50) ), inv(cispi( interval(i)/interval(50) ))) && - radius( inv(cispi( interval(i)/interval(50) )) ) < 10eps() - end - end -end - -# @testset "Complex functions" begin -# Z = interval(3, 1e-7; format = :midpoint) + interval(4, 1e-7; format = :midpoint)*interval(im) -# @test sin(Z) == complex(sin(real(Z)) * cosh(imag(Z)), sinh(imag(Z)) * cos(real(Z))) - -# z = exp(- interval(im) * interval(π)) -# @test in_interval(-1, real(z)) -# @test in_interval(0, imag(z)) - -# sZ = sqrt(Z) -# @test isequal_interval(sZ, complex(interval(1.99999996999999951619, 2.00000003000000070585), interval(0.99999996999999984926, 1.00000003000000048381))) -# @test isequal_interval(sqrt(-Z), complex(imag(sZ), - real(sZ))) - -# @test isequal_interval(sqrt(interval(-1, 0) + interval(0)*interval(im)), interval(0im, im)) -# @test isequal_interval(sqrt(interval(-1, 1) + interval(0)*interval(im)), interval(0, 1) + interval(0im, 1im)) -# @test isequal_interval(sqrt(interval(-9//32, Inf)*interval(im)), complex(interval(0, Inf), interval(-3//8, Inf))) -# end - -@testset "abs2 and abs" begin - x = complex(interval(0, 3), interval(0, 4)) - @test isequal_interval(abs2(x), interval(0, 25)) - @test isequal_interval(abs(x), interval(0, 5)) - - y = complex(interval(-1, 1), interval(-2, 2)) - @test inf(abs(y)) == 0 - @test inf(abs2(y)) == 0 -end - -@testset "real functions" begin - x = complex(interval(0, 3), interval(0, 4)) - @test mag(x) == 5 - @test mig(x) == 0 - @test mid(x) == 1.5 + 2im -end diff --git a/test/interval_tests/consistency.jl b/test/interval_tests/consistency.jl deleted file mode 100644 index 1a186b4b9..000000000 --- a/test/interval_tests/consistency.jl +++ /dev/null @@ -1,491 +0,0 @@ -@test size(interval(1)) == () # match the `size` behaviour of `Real` - -@testset "Consistency tests" begin - - a = interval(0.1, 1.1) - b = interval(0.9, 2.0) - c = interval(0.25, 4.0) - - @testset "Interval types and constructors" begin - @test isa( interval(1, 2), Interval ) - @test isa( interval(0.1), Interval ) - @test isa( zero(b), Interval ) - - @test isthin(zero(b), 0.0) - @test isequal_interval(zero(b), zero(typeof(b))) - @test isthin(one(a), 1.0) - @test isequal_interval(one(a), one(typeof(a))) - @test isthin(one(a), big(1.0)) - @test !isequal_interval(a, b) - @test isequal_interval(eps(typeof(a)), eps(one(typeof(a)))) - @test isequal_interval(eps(interval(1e-12, 1.0)), interval(eps(1e-12), eps(1.0))) - @test decoration(eps(interval(1e-12, 1.0))) == def - @test isequal_interval(eps(interval(1.0, 1.5)), interval(eps(1.0))) # `eps` is constant on `x` - @test decoration(eps(interval(1.0, 1.5))) == com - @test isequal_interval(eps(interval(-3.0, 1.0)), interval(eps(0.0), eps(3.0))) # `eps` is even - @test isequal_interval(eps(interval(-1.0, 2.0)), interval(eps(0.0), eps(2.0))) # `0 ∈ x` - @test isequal_interval(eps(interval(1.0, Inf)), interval(eps(1.0), Inf)) # `eps(Inf)` is `NaN` - @test isequal_interval(eps(emptyinterval()), emptyinterval()) - @test decoration(eps(emptyinterval())) == trv - @test isnai(eps(nai())) - @test isequal_interval(eps(bareinterval(-3.0, 1.0)), bareinterval(eps(0.0), eps(3.0))) - @test isequal_interval(eps(emptyinterval(BareInterval{Float64})), emptyinterval(BareInterval{Float64})) - @test isequal_interval(typemin(typeof(a)), interval(-Inf, nextfloat(-Inf))) - @test isequal_interval(typemax(typeof(a)), interval(prevfloat(Inf), Inf)) - @test isequal_interval(typemin(a), typemin(typeof(a))) - @test isequal_interval(typemax(a), typemax(typeof(a))) - @test isequal_interval(floatmin(typeof(a)), interval(floatmin(Float64))) - @test isequal_interval(floatmax(typeof(a)), interval(floatmax(Float64))) - @test isequal_interval(floatmin(a), floatmin(typeof(a))) - @test isequal_interval(floatmax(a), floatmax(typeof(a))) - - @test isequal_interval(a, interval(inf(a), sup(a))) - @test isequal_interval(emptyinterval(Rational{Int}), emptyinterval()) - - @test inf(zero(a) + one(b)) == 1 - @test sup(zero(a) + one(b)) == 1 - @test isequal_interval(interval(0,1) + emptyinterval(a), emptyinterval(a)) - @test isequal_interval(interval(0.25) - one(c)/interval(4), zero(c)) - @test isequal_interval(emptyinterval(a) - interval(0, 1), emptyinterval(a)) - @test isequal_interval(interval(0, 1) - emptyinterval(a), emptyinterval(a)) - @test isequal_interval(interval(0, 1) * emptyinterval(a), emptyinterval(a)) - @test isequal_interval(a * interval(0), zero(a)) - - @test decoration(IntervalArithmetic.setdecoration(interval(1, 2), ill)) == ill - @test decoration(IntervalArithmetic.setdecoration(emptyinterval(), com)) == trv - @test decoration(IntervalArithmetic.setdecoration(interval(1, Inf), com)) == dac - - @test !isnai(IntervalArithmetic.setdecoration(interval(NaN), com)) - @test decoration(IntervalArithmetic.setdecoration(interval(NaN), com)) == trv - end - - @testset "real interface" begin - @test isthinzero(zero(Interval{Float64})) - @test isthinzero(zero(Complex{Interval{Float64}})) - - @test isthinone(one(Interval{Float64})) - @test isthinone(one(Complex{Interval{Float64}})) - end - - @testset "inv" begin - @test isequal_interval(inv( zero(a) ), emptyinterval()) # Only for set based flavor - @test isequal_interval(inv( interval(0, 1) ), interval(1, Inf)) - @test isequal_interval(inv( interval(1, Inf) ), interval(0, 1)) - @test isequal_interval(inv(c), c) - @test isequal_interval(one(b)/b, inv(b)) - @test isequal_interval(a/emptyinterval(a), emptyinterval(a)) - @test isequal_interval(emptyinterval(a)/a, emptyinterval(a)) - @test isequal_interval(inv(interval(-4.0, 0.0)), interval(-Inf, -0.25)) - @test isequal_interval(inv(interval(0.0, 4.0)), interval(0.25, Inf)) - @test isequal_interval(inv(interval(-4.0, 4.0)), entireinterval(Float64)) - @test isequal_interval(interval(0)/interval(0), emptyinterval()) # According to the standard for :set_based flavor - @test typeof(emptyinterval()) == Interval{Float64} - end - - @testset "fma consistency" begin - @test isequal_interval(fma(emptyinterval(), a, b), emptyinterval()) - @test isequal_interval(fma(entireinterval(), zero(a), b), b) - @test isequal_interval(fma(entireinterval(), one(a), b), entireinterval()) - @test isequal_interval(fma(zero(a), entireinterval(), b), b) - @test isequal_interval(fma(one(a), entireinterval(), b), entireinterval()) - @test isequal_interval(fma(a, zero(a), c), c) - @test isequal_interval(fma(interval(Rational{Int}, 1//2, 1//2), - interval(Rational{Int}, 1//3, 1//3), - interval(Rational{Int}, 1//12, 1//12)), interval(Rational{Int}, 3//12, 3//12)) - end - - @testset "in_interval tests" begin - @test !in_interval(Inf, entireinterval()) - @test in_interval(0.1, I"0.1") - @test in_interval(0.1, I"0.1") - @test !in_interval(-Inf, entireinterval()) - @test !in_interval(Inf, entireinterval()) - - @test_throws ArgumentError in_interval(interval(3, 4), interval(3, 4)) - end - - @testset "Inclusion tests" begin - @test issubset_interval(b, c) - @test issubset_interval(b, b) - @test issubset_interval(emptyinterval(c), c) - @test !issubset_interval(c, emptyinterval(c)) - - @test isinterior(b, c) - @test !isinterior(b, b) - @test isinterior(emptyinterval(c), c) - @test !isinterior(c, emptyinterval(c)) - @test isinterior(emptyinterval(c), emptyinterval(c)) - - @test isdisjoint_interval(a, I"2.1") - @test !(isdisjoint_interval(a, b)) - @test isdisjoint_interval(emptyinterval(a), a) - @test isdisjoint_interval(emptyinterval(), emptyinterval()) - @test !isdisjoint_interval(interval(1, 2), interval(3, 4), interval(5, 6), interval(1, 2)) - end - - @testset "Comparison tests" begin - @test isweakless(emptyinterval(), emptyinterval()) - @test !isweakless(interval(1, 2), emptyinterval()) - @test isweakless(interval(-Inf,Inf), interval(-Inf,Inf)) - @test precedes(emptyinterval(), emptyinterval()) - @test precedes(interval(3, 4), emptyinterval()) - @test !(precedes(interval(0, 2),interval(-Inf,Inf))) - @test precedes(interval(1, 3),interval(3, 4)) - @test strictprecedes(interval(3, 4), emptyinterval()) - @test !(strictprecedes(interval(-3, -1),interval(-1, 0))) - @test !(iscommon(emptyinterval())) - @test !(iscommon(entireinterval())) - @test iscommon(a) - @test !(isunbounded(emptyinterval())) - @test isunbounded(entireinterval()) - @test isunbounded(interval(-Inf, 0)) - @test isunbounded(interval(0, Inf)) - @test !(isunbounded(a)) - end - - @testset "Intersection tests" begin - @test isequal_interval(emptyinterval(BareInterval{Float64}), bareinterval(Inf, -Inf)) - @test isequal_interval(intersect_interval(a, interval(-1)), emptyinterval(a)) - @test isempty_interval(intersect_interval(a, interval(-1))) - @test !isempty_interval(a) - @test !isequal_interval(emptyinterval(a), a) - @test isequal_interval(emptyinterval(), emptyinterval()) - - @test isequal_interval(intersect_interval(a, hull(a, b)), a) - @test isequal_interval(hull(a, b), interval(inf(a), sup(b))) - - # n-ary intersect_interval - @test isequal_interval(intersect_interval(interval(1.0, 2.0), - interval(-1.0, 5.0), - interval(1.8, 3.0)), interval(1.8, 2.0)) - @test isequal_interval(intersect_interval(a, emptyinterval(), b), emptyinterval()) - @test isequal_interval(intersect_interval(interval(0, 1), interval(3, 4), interval(0, 1), interval(0, 1)), emptyinterval()) - end - - @testset "hull and hull tests" begin - @test isequal_interval(hull(interval(1, 2), interval(3, 4)), interval(1, 4)) - @test isequal_interval(hull(interval(1//3, 3//4), interval(3, 4)), interval(1/3, 4)) - - @test isequal_interval(hull(interval(1, 2), interval(3, 4)), interval(1, 4)) - @test isequal_interval(hull(interval(1//3, 3//4), interval(3, 4)), interval(1/3, 4)) - end - - @testset "Special interval tests" begin - @test isequal_interval(entireinterval(Float64), interval(-Inf, Inf)) - @test isentire_interval(entireinterval(a)) - @test isentire_interval(interval(-Inf, Inf)) - @test !isentire_interval(a) - @test isinterior(interval(-Inf, Inf), interval(-Inf, Inf)) - - @test !isequal_interval(nai(a), nai(a)) - @test isnai(interval(NaN)) & isnai(convert(Interval{Float64}, NaN)) - @test isnan(inf(nai(BigFloat))) - @test isnai(nai()) - @test !isnai(a) - - @test inf(a) == bareinterval(a).lo - @test sup(a) == bareinterval(a).hi - @test inf(emptyinterval(a)) == Inf - @test sup(emptyinterval(a)) == -Inf - @test inf(entireinterval(a)) == -Inf - @test sup(entireinterval(a)) == Inf - @test isnan(sup(nai(BigFloat))) - - @test inf(2.5) == 2.5 - @test sup(2.5) == 2.5 - end - - @testset "mid" begin - @test mid(interval(Rational{Int}, 1//2)) == 1//2 - @test mid(interval(2), 0.4969816845401611) == 2 - @test mid(interval(1, 2)) == 1.5 - @test mid(interval(0.1, 0.3)) == 0.2 - @test mid(interval(-10, 5)) == -2.5 - @test mid(interval(-Inf, 1)) == nextfloat(-Inf) - @test mid(interval(1, Inf)) == prevfloat(Inf) - @test isnan(mid(emptyinterval())) - end - - @testset "mid with α" begin - @test_throws DomainError mid(interval(1, 2), 1.2) - @test_throws DomainError mid(interval(1, 2), -0.7) - @test mid(interval(0, 1), 0.75) == 0.75 - @test mid(interval(0, 1000), 0.125) == 125 - @test mid(interval(1, Inf), 0.75) > 0 - @test mid(interval(-Inf, Inf), 0.75) > 0 - @test mid(interval(-Inf, Inf), 0.25) < 0 - end - - @testset "mid with large floats" begin - @test mid(interval(0.8e308, 1.2e308)) == 1e308 - @test mid(interval(-1e308, 1e308)) == 0 - @test isfinite(mid(interval(0.8e308, 1.2e308))) - @test isfinite(mid(interval(-1e308, 1e308))) - end - - @testset "diam" begin - @test diam( interval(Rational{Int}, 1//2) ) == 0//1 - @test diam( interval(1//10) ) == 0 - @test diam( I"0.1" ) == eps(0.1) - @test isnan(diam(emptyinterval())) - @test diam(a) == 1.0000000000000002 - - @test diam(0.1) == 0 - end - - @testset "mig and mag" begin - @test mig(interval(-2, 2)) == BigFloat(0.0) - @test mig( interval(Rational{Int}, 1//2) ) == 1//2 - @test isnan(mig(emptyinterval())) - @test mag(-b) == sup(b) - @test mag( interval(Rational{Int}, 1//2) ) == 1//2 - @test isnan(mag(emptyinterval())) - end - - @testset "cancelplus tests" begin - x = interval(-2.0, 4.440892098500622e-16) - y = interval(-4.440892098500624e-16, 2.0) - @test isequal_interval(cancelminus(x, y), entireinterval(Float64)) - @test isequal_interval(cancelplus(x, y), entireinterval(Float64)) - x = interval(-big(1.0), eps(big(1.0))/4) - y = interval(-eps(big(1.0))/2, big(1.0)) - @test isequal_interval(cancelminus(x, y), entireinterval(BigFloat)) - @test isequal_interval(cancelplus(x, y), entireinterval(BigFloat)) - x = interval(-big(1.0), eps(big(1.0))/2) - y = interval(-eps(big(1.0))/2, big(1.0)) - @test issubset_interval(cancelminus(x, y), interval(-one(BigFloat), one(BigFloat))) - @test isequal_interval(cancelplus(x, y), interval(zero(BigFloat), zero(BigFloat))) - @test isequal_interval(cancelminus(emptyinterval(), emptyinterval()), emptyinterval()) - @test isequal_interval(cancelplus(emptyinterval(), emptyinterval()), emptyinterval()) - @test isequal_interval(cancelminus(emptyinterval(), interval(0.0, 5.0)), emptyinterval()) - @test isequal_interval(cancelplus(emptyinterval(), interval(0.0, 5.0)), emptyinterval()) - @test isequal_interval(cancelminus(entireinterval(), interval(0.0, 5.0)), entireinterval()) - @test isequal_interval(cancelplus(entireinterval(), interval(0.0, 5.0)), entireinterval()) - @test isequal_interval(cancelminus(interval(5.0), interval(-Inf, 0.0)), entireinterval()) - @test isequal_interval(cancelplus(interval(5.0), interval(-Inf, 0.0)), entireinterval()) - @test isequal_interval(cancelminus(interval(0.0, 5.0), emptyinterval()), entireinterval()) - @test isequal_interval(cancelplus(interval(0.0, 5.0), emptyinterval()), entireinterval()) - @test isequal_interval(cancelminus(interval(0.0), interval(0.0, 1.0)), entireinterval()) - @test isequal_interval(cancelplus(interval(0.0), interval(0.0, 1.0)), entireinterval()) - @test isequal_interval(cancelminus(interval(0.0), interval(1.0)), interval(-1.0)) - @test isequal_interval(cancelplus(interval(0.0), interval(1.0)), interval(1.0)) - @test isequal_interval(cancelminus(interval(-5.0, 0.0), interval(0.0, 5.0)), interval(-5.0)) - @test isequal_interval(cancelplus(interval(-5.0, 0.0), interval(0.0, 5.0)), interval(0.0)) - end - - @testset "mid and radius" begin - @test radius(interval(Rational{Int}, -1//10,1//10)) == diam(interval(Rational{Int}, -1//10,1//10))/2 - @test isnan(radius(emptyinterval())) - @test mid(c) == 2.125 - @test isnan(mid(emptyinterval())) - @test mid(entireinterval()) == 0.0 - @test isnan(mid(nai())) - # @test_throws InexactError nai(interval(1//2)) TODO move this test - - @test mid(2.125) == 2.125 - @test radius(2.125) == 0 - end - - @testset "abs, min, max, sign" begin - @test isequal_interval(abs(entireinterval()), interval(0.0, Inf)) - @test isequal_interval(abs(emptyinterval()), emptyinterval()) - @test isequal_interval(abs(interval(-3.0,1.0)), interval(0.0, 3.0)) - @test isequal_interval(abs(interval(-3.0,-1.0)), interval(1.0, 3.0)) - @test isequal_interval(abs2(interval(-3.0,1.0)), interval(0.0, 9.0)) - @test isequal_interval(abs2(interval(-3.0,-1.0)), interval(1.0, 9.0)) - @test isequal_interval(min(entireinterval(), interval(3.0,4.0)), interval(-Inf, 4.0)) - @test isequal_interval(min(emptyinterval(), interval(3.0,4.0)), emptyinterval()) - @test isequal_interval(min(interval(-3.0,1.0), interval(3.0,4.0)), interval(-3.0, 1.0)) - @test isequal_interval(min(interval(-3.0,-1.0), interval(3.0,4.0)), interval(-3.0, -1.0)) - @test isequal_interval(max(entireinterval(), interval(3.0,4.0)), interval(3.0, Inf)) - @test isequal_interval(max(emptyinterval(), interval(3.0,4.0)), emptyinterval()) - @test isequal_interval(max(interval(-3.0,1.0), interval(3.0,4.0)), interval(3.0, 4.0)) - @test isequal_interval(max(interval(-3.0,-1.0), interval(3.0,4.0)), interval(3.0, 4.0)) - @test isequal_interval(sign(entireinterval()), interval(-1.0, 1.0)) - @test isequal_interval(sign(emptyinterval()), emptyinterval()) - @test isequal_interval(sign(interval(-3.0,1.0)), interval(-1.0, 1.0)) - @test isequal_interval(sign(interval(-3.0,-1.0)), interval(-1.0, -1.0)) - - # Test putting functions in interval: - @test issubset_interval(log(interval(-2, 5)), interval(-Inf, log(interval(5)))) - - # - - x = interval(0, 3) + interval(0, 4)*interval(im) - @test isequal_interval(abs2(x), interval(0, 25)) - @test isequal_interval(abs(x), interval(0, 5)) - - y = interval(-1, 1) + interval(-2, 2)*interval(im) - @test inf(abs(y)) == 0 - @test inf(abs2(y)) == 0 - - @test mag(x) == 5 - @test mig(x) == 0 - @test mid(x) == 1.5 + 2im - end - - @testset "Interval power of an interval" begin - a = interval(1, 2) - b = interval(3, 4) - - @test isequal_interval(pow(a, b), interval(1, 16)) - @test isequal_interval(pow(a, interval(0.5, 1)), a) - @test isequal_interval(pow(a, interval(0.3, 0.5)), interval(1, sqrt(2))) - end - - @testset "isatomic" begin - @test isatomic(interval(1)) - @test isatomic(interval(2.3, 2.3)) - @test isatomic(emptyinterval()) - @test isnai(interval(Inf)) - - @test !isatomic(interval(1, 2)) - @test !isatomic(interval(1, nextfloat(1.0, 2))) - - end - - @testset "isthinzero" begin - @test isthinzero(interval(0)) - @test isthinzero(interval(Rational{Int}, 0//1)) - @test isthinzero(interval(big(0))) - @test isthinzero(interval(-0.0)) - @test isthinzero(interval(-0.0, 0.0)) - - @test !isthinzero(interval(1, 2)) - @test !isthinzero(interval(0.0, nextfloat(0.0))) - end - - @testset "Type stability" begin - for T ∈ (Float32, Float64, BigFloat) - - xs = [interval(3, 4), interval(0, 4), interval(0), interval(-4, 0), interval(-4, 4), interval(-Inf, 4), interval(4, Inf), interval(-Inf, Inf)] - - for x ∈ xs - for y ∈ xs - xx = Interval{T}(x) - yy = Interval{T}(y) - - for op ∈ (+, -, *, /, atan) - @inferred op(x, y) - end - end - - for op ∈ (sin, cos, exp, log, tan, abs, mid, diam) - @inferred op(x) - end - end - end - end - - @testset "`Real` functionalities" begin - x, y = interval(1), interval(2) - - @test !isnan(x) - - @test isone(x) - @test !iszero(x) - @test_throws IntervalArithmetic.InconclusiveBooleanOperation iszero(interval(0, 1)) - @test x != y - @test x == 1 - @test_throws IntervalArithmetic.InconclusiveBooleanOperation interval(1, 2) != 2 - @test_throws IntervalArithmetic.InconclusiveBooleanOperation interval(1, 2) != y - @test_throws IntervalArithmetic.InconclusiveBooleanOperation y != interval(1, 2) - @test_throws IntervalArithmetic.InconclusiveBooleanOperation interval(1, 2) == interval(1, 2) - - @test !issubnormal(interval(1, 2)) - @test issubnormal(interval(floatmin(Float64)/4, floatmin(Float64)/2)) - @test issubnormal(interval(-floatmin(Float64)/2, -floatmin(Float64)/4)) - @test !issubnormal(interval(0)) - @test !issubnormal(emptyinterval()) - @test_throws IntervalArithmetic.InconclusiveBooleanOperation issubnormal(interval(0, 1)) - @test_throws IntervalArithmetic.InconclusiveBooleanOperation issubnormal(interval(-floatmin(Float64), floatmin(Float64))) - - @test x < y - @test x < 2 - @test !(x > y) - @test !(x < x) - @test !(x < 1) - @test_throws IntervalArithmetic.InconclusiveBooleanOperation x < interval(1, 2) - - @test isfinite(x) - @test_throws IntervalArithmetic.InconclusiveBooleanOperation isfinite(interval(1, Inf)) - - @test isinteger(x) - @test !isinteger(interval(1.2, 1.9)) - @test_throws IntervalArithmetic.InconclusiveBooleanOperation isinteger(interval(1.5, 2.5)) - - # - - @test_throws ArgumentError intersect(x, x) - @test_throws ArgumentError isapprox(x, x) - @test_throws ArgumentError isdisjoint(x, x) - @test_throws ArgumentError issubset(x, x) - @test_throws ArgumentError issetequal(x, x) - @test_throws ArgumentError x ∈ x - @test_throws ArgumentError isempty(x) - @test_throws ArgumentError union(x, x) - @test_throws ArgumentError setdiff(x, x) - end - -end - -@testset "Zero interval" begin - @test isequal_interval(zero(Interval{Float64}), interval(0)) - @test isequal_interval(zero(interval(0, 1)), interval(0)) -end - -@testset "Decorations" begin - a = interval(1, 2) - b = interval(3, 4) - - @test dist(a, b) == 2.0 - - @test isnai(interval(3, 1)) - @test isnai(interval(Inf, Inf)) - @test isnai(interval(-Inf, -Inf)) - @test isnai(interval(NaN, NaN)) - @test isnai(interval(NaN, 3)) - @test isnai(interval(3, NaN)) -end - -@testset "Hashing of Intervals" begin - x = interval(Float64, 1, 2) - y = interval(BigFloat, 1, 2) - @test isequal_interval(x, y) - @test hash(x) == hash(y) - - x = I"0.1" - y = interval(BigFloat, x) - @test isequal_interval(x, y) - @test hash(x) == hash(y) - - x = interval(1, 2) - y = interval(1, 3) - @test !isequal_interval(x, y) - @test hash(x) != hash(y) -end - -@testset "Complex" begin - a = interval(1im) - b = interval(4im + 3) - c = interval(-1, 4) + interval(0, 2)*interval(im) - - @test isinterior(a, c) - @test issubset_interval(a, c) - @test isinterior(a, c) - @test !isinterior(b, c) - @test !issubset_interval(b, c) - - @test typeof(a) == Complex{Interval{Float64}} - @test isequal_interval(a, interval(0) + interval(1)*interval(im)) - @test isequal_interval(a * a, interval(-1)) - @test isequal_interval(a + a, interval(2)*interval(im)) - @test isthinzero(a - a) - @test isthinone(a / a) - - @test in_interval(3+2im, c) - @test isequal_interval(hull(a, b), interval(0, 3) + interval(1, 4)*interval(im)) - @test isequal_interval(intersect_interval(c, hull(a, b)), interval(0, 3) + interval(1, 2)*interval(im)) - @test isequal_interval(intersect_interval(a, b), emptyinterval() + emptyinterval()*interval(im)) - @test isdisjoint_interval(a, b) -end diff --git a/test/interval_tests/construction.jl b/test/interval_tests/construction.jl deleted file mode 100644 index 6b4761d16..000000000 --- a/test/interval_tests/construction.jl +++ /dev/null @@ -1,244 +0,0 @@ -@testset "Representation invariants of the bounds" begin - # `inf` and `sup` read the fields directly, so no operation may store a - # `NaN` bound; the empty interval is `(typemax(T), typemin(T))` instead - for T ∈ (Float16, Float32, Float64, BigFloat, Rational{Int}, Rational{BigInt}) - e = emptyinterval(BareInterval{T}) - @test (e.lo == typemax(T)) & (e.hi == typemin(T)) - @test isempty_interval(e) - @test (inf(e) == typemax(T)) & (sup(e) == typemin(T)) - - f = bareinterval(T, 2, 1) # ill-formed, hence empty - @test (f.lo == typemax(T)) & (f.hi == typemin(T)) - - g = nai(Interval{T}) - @test (g.bareinterval.lo == typemax(T)) & (g.bareinterval.hi == typemin(T)) - end - - # a zero bound is stored as `+0`, `inf` restoring the `-0` of the standard - x = bareinterval(0.0, 1.0) - @test (x.lo === 0.0) & (inf(x) === -0.0) & (sup(x) === 1.0) - y = bareinterval(-1.0, -0.0) - @test (y.hi === 0.0) & (sup(y) === 0.0) - - # `bounds` reports what is stored, without normalizing the infimum - @test bounds(x) === (0.0, 1.0) - @test bounds(emptyinterval(BareInterval{Float64})) === (Inf, -Inf) -end - -@testset "Difference between checked and unchecked bare intervals" begin - @test IntervalArithmetic._unsafe_bareinterval(Float64, 1, 2) === bareinterval(1, 2) - - @test inf(IntervalArithmetic._unsafe_bareinterval(Float64, 3, 2)) == 3 - @test isempty_interval(bareinterval(3, 2)) - @test isnai(interval(3, 2)) - - # `:set_based` flavor - @test sup(IntervalArithmetic._unsafe_bareinterval(Float64, Inf, Inf)) == Inf - @test isempty_interval(bareinterval(Inf, Inf)) - @test isnai(interval(Inf, Inf)) - @test isnai(interval(1//0, 1//0)) - @test isnai(interval(-1//0, -1//0)) - @test isnai(interval(1//0, -1//0)) -end - -@testset "Basics" begin - @test typeof(interval(1, 2)) == Interval{Float64} - @test typeof(big(interval(1, 2))) == Interval{BigFloat} - for T ∈ (Float16, Float32, Float64, BigFloat) - @test typeof(interval(T, 1, 2)) == Interval{T} - end - for T ∈ [InteractiveUtils.subtypes(Signed) ; InteractiveUtils.subtypes(Unsigned)] - @test typeof(interval(Rational{T}, 1, 2)) == Interval{Rational{T}} - end - @test eltype(interval(1, 2)) == Interval{Float64} - @test IntervalArithmetic.numtype(interval(1, 2)) == Float64 - @test typeof(interval(BigInt(1), 11//10)) == Interval{Rational{BigInt}} - - @test inf(interval(1, 2)) == 1 && sup(interval(1, 2)) == 2 - - @test isequal_interval( - interval(Float64, 1, 1), interval(Float64, 1), interval(1), - interval(Float64, interval(1)), interval(interval(1)), - interval(BigFloat, 1, 1), interval(BigFloat, 1), interval(big(1)), - interval(Float64, 1, 1), interval(1, 1), interval(Float64, 1), interval(1)) - - @test isequal_interval( - interval(Rational{Int}, 1//10, 1//10), interval(1//10, 1//10), interval(Rational{Int}, 1//10), interval(1//10), - interval(Rational{Int}, interval(1//10)), interval(interval(1//10)), - interval(Rational{BigInt}, 1//10, 1//10), interval(Rational{BigInt}, 1//10), interval(big(1//10)), - interval(Rational{Int}, 1//10, 1//10), interval(1//10, 1//10), interval(Rational{Int}, 1//10), interval(1//10)) - - @test_throws MethodError BareInterval(1) - @test_throws MethodError BareInterval{Float64}(1) - @test_throws MethodError BareInterval(1, 2) - @test_throws MethodError BareInterval{Float64}(1, 2) - - @test !isguaranteed(Interval(1)) - @test !isguaranteed(Interval{Float64}(1)) - @test_throws MethodError Interval(1, 2) - @test_throws MethodError Interval{Float64}(1, 2) - - @test isequal_interval( - BareInterval{Float64}(bareinterval(3, 4)), BareInterval{BigFloat}(bareinterval(3, 4)), - BareInterval{Rational{Int}}(bareinterval(3, 4)), BareInterval{Rational{BigInt}}(bareinterval(3, 4)), - bareinterval(3, 4)) - - @test isequal_interval( - Interval{Float64}(interval(3, 4)), Interval{BigFloat}(interval(3, 4)), - Interval{Rational{Int}}(interval(3, 4)), Interval{Rational{BigInt}}(interval(3, 4)), - interval(3, 4)) - - @test isempty_interval(bareinterval(2, 1)) - @test isempty_interval(bareinterval(Inf)) - @test isempty_interval(bareinterval(-Inf)) - @test isempty_interval(bareinterval(1, NaN)) - @test isempty_interval(bareinterval(NaN)) - - @test isnai(interval(2, 1)) - @test isnai(interval(Inf)) - @test isnai(interval(-Inf)) - @test isnai(interval(1, NaN)) - @test isnai(interval(NaN)) - - @test isnai(interval(1//0)) - @test isnai(interval(-1//0)) - - # check no issue with `Integer` modular arithmetic - @test bounds(interval(typemin(Int64), typemax(Int64))) == (float(typemin(Int64)), float(typemax(Int64))) - - # 1//10 < 0.1, 2//10 < 0.2 - @test !in_interval(1//10, interval(0.1, 0.2)) && in_interval(2//10, interval(0.1, 0.2)) - - @test isequal_interval(interval(1//2), interval(0.5)) - @test inf(interval(1//10)) == 1//10 && sup(interval(1//10)) == 1//10 - - x = interval(1 + 2im) - @test typeof(x) == Complex{Interval{Float64}} - @test isequal_interval(x, complex(interval(1), interval(2))) - - @test IntervalArithmetic.Symbols.:..(1, 2) === interval(1, 2; format = :infsup) -end - -@testset "Irrationals" begin - for irr ∈ (MathConstants.:π, MathConstants.:γ, MathConstants.:catalan, MathConstants.:φ, MathConstants.:ℯ) - for T ∈ (Float16, Float32, Float64, BigFloat) - @test in_interval(irr, interval(T, irr)) - if T !== BigFloat - @test nextfloat(inf(interval(T, irr))) == sup(interval(T, irr)) - end - end - for T ∈ [InteractiveUtils.subtypes(Signed) ; InteractiveUtils.subtypes(Unsigned)] - @test in_interval(irr, interval(Rational{T}, irr)) - end - end -end - -@testset "Midpoint" begin - @test isequal_interval(IntervalArithmetic.Symbols.:±(0.5, 1), - interval(0.5, 1; format = :midpoint), - interval(0.5, 1+0im; format = :midpoint), - interval(0.5, interval(1+0im); format = :midpoint), - interval(-0.5, 1.5)) - - @test isequal_interval(IntervalArithmetic.Symbols.:±(interval(0.5, 1), interval(1, 2)), - interval(interval(0.5, 1), interval(1, 2); format = :midpoint), - interval(-1.5, 3)) - - @test isequal_interval(IntervalArithmetic.Symbols.:±(0.5+im, 1), - interval(0.5+im, 1; format = :midpoint), - interval(0.5+im, 1+0im; format = :midpoint), - interval(interval(0.5+im), interval(1+0im); format = :midpoint), - complex(interval(-0.5, 1.5), interval(0, 2))) - - @test_throws DomainError interval(0.5+im, 1+im; format = :midpoint) -end - -@testset "Decorations" begin - a = interval(1, 2) - b = interval(1, 2, IntervalArithmetic.com) - c = interval(1, 2, IntervalArithmetic.dac) - d = interval(a, IntervalArithmetic.dac) - - @test decoration(a) == IntervalArithmetic.com - @test decoration(b) == IntervalArithmetic.com - @test decoration(c) == IntervalArithmetic.dac - @test decoration(d) == IntervalArithmetic.dac - - @test decoration(interval(2, 0.1)) == decoration(interval(2, 0.1, IntervalArithmetic.com)) == IntervalArithmetic.ill -end - -@testset "Conversions and promotions" begin - bx = bareinterval(Float64, π) - by = bareinterval(BigFloat, π) - big_bx, big_by = promote(bx, by) - @test promote_type(typeof(bx), typeof(by)) == typeof(big_bx) == BareInterval{BigFloat} - @test isequal_interval(big_bx, BareInterval{BigFloat}(bx)) - @test isequal_interval(big_by, by) - # cannot convert a `Real` to a `BareInterval` - @test_throws MethodError convert(BareInterval, 1) - @test_throws MethodError convert(BareInterval{Float64}, 1) - - x = interval(Float64, π) - y = interval(BigFloat, π) - big_x, big_y = promote(x, y) - @test promote_type(typeof(x), typeof(y)) == typeof(big_x) == Interval{BigFloat} - @test isequal_interval(big_x, Interval{BigFloat}(x)) - @test isequal_interval(big_y, y) - # can convert a `Real` to an `Interval` - @test isequal_interval(convert(Interval{Float64}, 1), interval(1)) & (isguaranteed(convert(Interval{Float64}, 1)) == !isguaranteed(interval(1))) - @test isequal_interval(convert(Complex{Interval{Float64}}, 1), interval(1+0im)) & (isguaranteed(convert(Complex{Interval{Float64}}, 1)) == !isguaranteed(interval(1+0im))) - @test isequal_interval(convert(Complex{Interval{Float64}}, im), interval(im)) & (isguaranteed(convert(Complex{Interval{Float64}}, im)) == !isguaranteed(interval(im))) - @test isequal_interval(convert(Interval{Float64}, 1+0im), convert(Interval{Float64}, interval(1+0im)), interval(1)) - @test_throws DomainError convert(Interval{Float64}, 1+im) - @test_throws DomainError convert(Interval{Float64}, interval(1+im)) -end - -@testset "Interval types conversion" begin - i = interval(IS.Interval(1, 2)) - @test isequal_interval(i, interval(1., 2.)) && !isguaranteed(i) - i = interval(IS.Interval(0.1, 2)) - @test isequal_interval(i, interval(0.1, 2.)) && !isguaranteed(i) - @test interval(Float64, IS.Interval(0.1, 2)) === i - - i = interval(IS.iv"[0.1, Inf)") - @test isequal_interval(i, interval(0.1, Inf)) && !isguaranteed(i) - @test interval(IS.iv"[0.1, Inf]") === nai(Float64) - @test interval(IS.iv"(0.1, Inf]") === nai(Float64) - @test interval(IS.iv"(0.1, Inf)") === nai(Float64) - @test interval(IS.iv"(0.1, 1)") === nai(Float64) - @test interval(IS.iv"(0.1, 1]") === nai(Float64) - - @test IS.Interval(interval(1, 2)) === IS.Interval(1., 2.) - @test IS.Interval(interval(0.1, 2)) === IS.Interval(0.1, 2.) - @test IS.Interval(interval(0.1, Inf)) === IS.iv"[0.1, Inf)" - @test IS.Interval(interval(-Inf, Inf)) === IS.iv"(-Inf, Inf)" -end - -@testset "Propagation of `isguaranteed`" begin - @test !isguaranteed(interval(convert(Interval{Float64}, 0), interval(convert(Interval{Float64}, 1)))) - @test !isguaranteed(interval(0, convert(Interval{Float64}, 1))) - @test !isguaranteed(interval(convert(Interval{Float64}, 0), 1)) -end - -@testset "`@interval` macro" begin - x = 1 - T = Float32 - # single expression - @test isequal_interval(@interval(sin(1)), sin(interval(1))) - # first argument as the bound type - @test isequal_interval(@interval(Float32, sin(1)), sin(interval(Float32, 1))) - @test isequal_interval(@interval(Float64, x), interval(1)) - @test isequal_interval(@interval(T, sin(x)), sin(interval(Float32, 1))) - @test isequal_interval(@interval(Float64, sin(1), exp(1)), interval(inf(sin(interval(1))), sup(exp(interval(1))))) - # first argument as the lower bound - @test isequal_interval(@interval(1, 2), interval(1, 2)) - @test isequal_interval(@interval(x, 2), interval(1, 2)) - @test isequal_interval(@interval(exp(1), exp(1)), exp(interval(1))) - @test isequal_interval(@interval(sin(1), exp(1)), interval(inf(sin(interval(1))), sup(exp(interval(1))))) - # tightness of the upper bound - @test isequal_interval(@interval(3, sin(5) + 10), interval(3, sup(sin(interval(5)) + interval(10)))) - # bounds in the wrong order return an NaI, consistently with `interval` - @test isnai(@interval(2, 1)) - @test isnai(@interval(x, sin(x))) # sin(1) < 1 - @test isnai(@interval(Float64, 2, 1)) -end diff --git a/test/interval_tests/display.jl b/test/interval_tests/display.jl deleted file mode 100644 index bb035c0f6..000000000 --- a/test/interval_tests/display.jl +++ /dev/null @@ -1,249 +0,0 @@ -setprecision(BigFloat, 256) do - @testset "Ill-formed interval" begin - @test sprint(show, MIME("text/plain"), interval(1, -1)) == "NaI" - end - - @testset "BareInterval" begin - a = bareinterval(-floatmin(Float64), 1.3) - large_expo = bareinterval(0, big"1e123456789") # use "small" exponent, cf. JuliaLang/julia#48678 - - - @testset "Standard format" begin - setdisplay(:infsup) - - @testset "6 significant digits" begin - # `decorations` keyword has no impact for `BareInterval` - setdisplay(; sigdigits = 6, decorations = true) - - @test sprint(show, MIME("text/plain"), emptyinterval(BareInterval{Float64})) == "∅" - - @test sprint(show, MIME("text/plain"), a) == "[-2.22508e-308, 1.3]" - @test sprint(show, MIME("text/plain"), large_expo) == - "[0.0, 1.0e+123456789]₂₅₆" - end - - @testset "20 significant digits" begin - # `decorations` keyword has no impact for `BareInterval` - setdisplay(; sigdigits = 20, decorations = true) - - @test sprint(show, MIME("text/plain"), a) == "[-2.2250738585072014e-308, 1.3]" - @test sprint(show, MIME("text/plain"), large_expo) == - "[0.0, 1.0000000000000000001e+123456789]₂₅₆" - end - end - - @testset "Full format" begin - # `decorations` keyword has no impact for `BareInterval` - # `sigdigits` is not taken into account for format `:full` - setdisplay(:full; sigdigits = 100, decorations = true) - - @test sprint(show, MIME("text/plain"), emptyinterval(BareInterval{Float64})) == "∅" - - @test sprint(show, MIME("text/plain"), a) == "BareInterval{Float64}(-2.2250738585072014e-308, 1.3)" - @test sprint(show, MIME("text/plain"), large_expo) == - "BareInterval{BigFloat}(0.0, $(sup(large_expo)))" - end - - @testset "Midpoint format" begin - # `decorations` keyword has no impact for `BareInterval` - setdisplay(:midpoint; sigdigits = 6, decorations = true) - - @test sprint(show, MIME("text/plain"), emptyinterval(BareInterval{Float64})) == "∅" - - @test sprint(show, MIME("text/plain"), a) == "0.65 ± 0.65" - @test sprint(show, MIME("text/plain"), large_expo) == - "(5.0e+123456788 ± 5.0e+123456788)₂₅₆" - end - end - - @testset "Interval" begin - a = interval(1, 2) - a_NG = a/1 - b = interval(-floatmin(Float64), 1.3) - b32 = interval(-floatmin(Float32), parse(Float32, "1.3")) - b16 = interval(-floatmin(Float16), parse(Float16, "1.3")) - br = interval(Rational{Int64}, -11//10, 13//10) - c = interval(-1, Inf) - cr = interval(Rational{Int64}, -1//1, 1//0) - large_expo = interval(0, big"1e123456789") # use "small" exponent, cf. JuliaLang/julia#48678 - - @testset "Standard format" begin - setdisplay(:infsup) - - @testset "6 significant digits" begin - setdisplay(; sigdigits = 6) - - @testset "Decorations" begin - setdisplay(; decorations = true) - - @test sprint(show, MIME("text/plain"), emptyinterval()) == "∅_trv" - @test sprint(show, MIME("text/plain"), emptyinterval()/1) == "∅_trv_NG" - - @test sprint(show, MIME("text/plain"), a) == "[1.0, 2.0]_com" - @test sprint(show, MIME("text/plain"), a_NG) == "[1.0, 2.0]_com_NG" - @test sprint(show, MIME("text/plain"), b) == "[-2.22508e-308, 1.3]_com" - @test sprint(show, MIME("text/plain"), b32) == "[-1.1755f-38, 1.3f0]_com" - @test sprint(show, MIME("text/plain"), b16) == "[Float16(-6.104e-5), Float16(1.3)]_com" - @test sprint(show, MIME("text/plain"), br) == "[-11//10, 13//10]_com" - @test sprint(show, MIME("text/plain"), c) == "[-1.0, ∞)_dac" - @test sprint(show, MIME("text/plain"), cr) == "[-1//1, ∞)_dac" - @test sprint(show, MIME("text/plain"), large_expo) == - "[0.0, 1.0e+123456789]₂₅₆_com" - end - - @testset "No decorations" begin - setdisplay(; decorations = false) - - @test sprint(show, MIME("text/plain"), emptyinterval()) == "∅" - @test sprint(show, MIME("text/plain"), emptyinterval()/1) == "∅_NG" - - @test sprint(show, MIME("text/plain"), a) == "[1.0, 2.0]" - @test sprint(show, MIME("text/plain"), a_NG) == "[1.0, 2.0]_NG" - @test sprint(show, MIME("text/plain"), b) == "[-2.22508e-308, 1.3]" - @test sprint(show, MIME("text/plain"), b32) == "[-1.1755f-38, 1.3f0]" - @test sprint(show, MIME("text/plain"), b16) == "[Float16(-6.104e-5), Float16(1.3)]" - @test sprint(show, MIME("text/plain"), br) == "[-11//10, 13//10]" - @test sprint(show, MIME("text/plain"), c) == "[-1.0, ∞)" - @test sprint(show, MIME("text/plain"), cr) == "[-1//1, ∞)" - @test sprint(show, MIME("text/plain"), large_expo) == - "[0.0, 1.0e+123456789]₂₅₆" - end - end - - @testset "20 significant digits" begin - setdisplay(; sigdigits = 20, decorations = true) - - @test sprint(show, MIME("text/plain"), a) == "[1.0, 2.0]_com" - @test sprint(show, MIME("text/plain"), a_NG) == "[1.0, 2.0]_com_NG" - @test sprint(show, MIME("text/plain"), b) == "[-2.2250738585072014e-308, 1.3]_com" - @test sprint(show, MIME("text/plain"), b32) == "[-1.1754944f-38, 1.3f0]_com" - @test sprint(show, MIME("text/plain"), b16) == "[Float16(-6.104e-5), Float16(1.3)]_com" - @test sprint(show, MIME("text/plain"), br) == "[-11//10, 13//10]_com" - @test sprint(show, MIME("text/plain"), c) == "[-1.0, ∞)_dac" - @test sprint(show, MIME("text/plain"), cr) == "[-1//1, ∞)_dac" - @test sprint(show, MIME("text/plain"), large_expo) == - "[0.0, 1.0000000000000000001e+123456789]₂₅₆_com" - end - end - - @testset "Full format" begin - # `sigdigits` and `decorations` keywords are not taken into account for format `:full` - setdisplay(:full; sigdigits = 100, decorations = false) - - @test sprint(show, MIME("text/plain"), emptyinterval()) == "∅" - @test sprint(show, MIME("text/plain"), emptyinterval()/1) == "∅_NG" - - @test sprint(show, MIME("text/plain"), a) == "Interval{Float64}(1.0, 2.0, com, true)" - @test sprint(show, MIME("text/plain"), a_NG) == "Interval{Float64}(1.0, 2.0, com, false)" - @test sprint(show, MIME("text/plain"), b) == "Interval{Float64}(-2.2250738585072014e-308, 1.3, com, true)" - @test sprint(show, MIME("text/plain"), b32) == "Interval{Float32}(-1.1754944f-38, 1.3f0, com, true)" - @test sprint(show, MIME("text/plain"), b16) == "Interval{Float16}(Float16(-6.104e-5), Float16(1.3), com, true)" - @test sprint(show, MIME("text/plain"), br) == "Interval{Rational{Int64}}(-11//10, 13//10, com, true)" - @test sprint(show, MIME("text/plain"), c) == "Interval{Float64}(-1.0, Inf, dac, true)" - @test sprint(show, MIME("text/plain"), cr) == "Interval{Rational{Int64}}(-1//1, 1//0, dac, true)" - @test sprint(show, MIME("text/plain"), large_expo) == - "Interval{BigFloat}(0.0, $(sup(large_expo)), com, true)" - end - - @testset "Midpoint format" begin - setdisplay(:midpoint; sigdigits = 6) - - @testset "Decorations" begin - setdisplay(; decorations = true) - - @test sprint(show, MIME("text/plain"), emptyinterval()) == "∅_trv" - @test sprint(show, MIME("text/plain"), emptyinterval()/1) == "∅_trv_NG" - - @test sprint(show, MIME("text/plain"), a) == "(1.5 ± 0.5)_com" - @test sprint(show, MIME("text/plain"), a_NG) == "(1.5 ± 0.5)_com_NG" - @test sprint(show, MIME("text/plain"), b) == "(0.65 ± 0.65)_com" - @test sprint(show, MIME("text/plain"), b32) == "(0.65f0 ± 0.65f0)_com" - @test sprint(show, MIME("text/plain"), b16) == "(Float16(0.65) ± Float16(0.65))_com" - @test sprint(show, MIME("text/plain"), br) == "(1//10 ± 6//5)_com" - @test sprint(show, MIME("text/plain"), c) == "(1.79769e+308 ± ∞)_dac" - @test sprint(show, MIME("text/plain"), cr) == "(9223372036854775807//1 ± ∞)_dac" - @test sprint(show, MIME("text/plain"), large_expo) == - "(5.0e+123456788 ± 5.0e+123456788)₂₅₆_com" - end - - @testset "No decorations" begin - setdisplay(; decorations = false) - - @test sprint(show, MIME("text/plain"), emptyinterval()) == "∅" - @test sprint(show, MIME("text/plain"), emptyinterval()/1) == "∅_NG" - - @test sprint(show, MIME("text/plain"), a) == "1.5 ± 0.5" - @test sprint(show, MIME("text/plain"), a_NG) == "(1.5 ± 0.5)_NG" - @test sprint(show, MIME("text/plain"), b) == "0.65 ± 0.65" - @test sprint(show, MIME("text/plain"), b32) == "0.65f0 ± 0.65f0" - @test sprint(show, MIME("text/plain"), b16) == "Float16(0.65) ± Float16(0.65)" - @test sprint(show, MIME("text/plain"), br) == "1//10 ± 6//5" - @test sprint(show, MIME("text/plain"), c) == "1.79769e+308 ± ∞" - @test sprint(show, MIME("text/plain"), cr) == "9223372036854775807//1 ± ∞" - @test sprint(show, MIME("text/plain"), large_expo) == - "(5.0e+123456788 ± 5.0e+123456788)₂₅₆" - end - end - end - - @testset "Complex{<:Interval}" begin - a = complex(interval(0, 2), interval(1)) - b = complex(interval(0, 2), interval(-1)) - c = complex(interval(0, 1e-70), interval(-1e-70)) - - @testset "Standard format" begin - setdisplay(:infsup) - - @testset "6 significant digits" begin - setdisplay(; sigdigits = 6) - - @testset "Decorations" begin - setdisplay(; decorations = true) - - @test sprint(show, MIME("text/plain"), a) == "[0.0, 2.0]_com + im*[1.0, 1.0]_com" - @test sprint(show, MIME("text/plain"), b) == "[0.0, 2.0]_com - im*[1.0, 1.0]_com" - @test sprint(show, MIME("text/plain"), c) == "[0.0, 1.0e-70]_com - im*[9.9999e-71, 1.0e-70]_com" - end - - @testset "No decorations" begin - setdisplay(; decorations = false) - - @test sprint(show, MIME("text/plain"), a) == "[0.0, 2.0] + im*[1.0, 1.0]" - @test sprint(show, MIME("text/plain"), b) == "[0.0, 2.0] - im*[1.0, 1.0]" - @test sprint(show, MIME("text/plain"), c) == "[0.0, 1.0e-70] - im*[9.9999e-71, 1.0e-70]" - end - end - end - - @testset "Full format" begin - # `sigdigits` and `decorations` keywords are not taken into account for format `:full` - setdisplay(:full; sigdigits = 100, decorations = false) - - @test sprint(show, MIME("text/plain"), a) == "Interval{Float64}(0.0, 2.0, com, true) + im*Interval{Float64}(1.0, 1.0, com, true)" - @test sprint(show, MIME("text/plain"), b) == "Interval{Float64}(0.0, 2.0, com, true) - im*Interval{Float64}(1.0, 1.0, com, true)" - @test sprint(show, MIME("text/plain"), c) == "Interval{Float64}(0.0, 1.0e-70, com, true) - im*Interval{Float64}(1.0e-70, 1.0e-70, com, true)" - end - - @testset "Midpoint format" begin - setdisplay(:midpoint; sigdigits = 6) - - @testset "Decorations" begin - setdisplay(; decorations = true) - - @test sprint(show, MIME("text/plain"), a) == "(1.0 ± 1.0)_com + im*(1.0 ± 0.0)_com" - @test sprint(show, MIME("text/plain"), b) == "(1.0 ± 1.0)_com - im*(1.0 ± 0.0)_com" - @test sprint(show, MIME("text/plain"), c) == "(5.0e-71 ± 5.0e-71)_com - im*(1.0e-70 ± 0.0)_com" - end - - @testset "No decorations" begin - setdisplay(; decorations = false) - - @test sprint(show, MIME("text/plain"), a) == "(1.0 ± 1.0) + im*(1.0 ± 0.0)" - @test sprint(show, MIME("text/plain"), b) == "(1.0 ± 1.0) - im*(1.0 ± 0.0)" - @test sprint(show, MIME("text/plain"), c) == "(5.0e-71 ± 5.0e-71) - im*(1.0e-70 ± 0.0)" - end - end - end - - setdisplay(:infsup; sigdigits = 6, decorations = true) # reset to default display options -end diff --git a/test/interval_tests/exact_literals.jl b/test/interval_tests/exact_literals.jl deleted file mode 100644 index 4f24a30d8..000000000 --- a/test/interval_tests/exact_literals.jl +++ /dev/null @@ -1,156 +0,0 @@ -@testset "Exact literals" begin - @test_throws MethodError convert(ExactReal{Float64}, 2) - - @test has_exact_display(0.5) - @test !has_exact_display(0.1) - - @test (@exact 2im) isa Complex{<:ExactReal} - @test (@exact 1.2 + 3.4im) isa Complex{<:ExactReal} - @test_throws ArgumentError (@exact 1.2 + 3im) - - @test exact(3).value == 3 - @test_throws MethodError ExactReal{Float64}(1//3) - @test_throws MethodError ExactReal{Float64}(1.0) - @test_throws MethodError ExactReal(1.0) - - # - - x = @exact 0.5 - - @test (2 * x) isa Float64 - @test isone(2 * x) - - @test (bareinterval(2) * x) isa BareInterval - @test isthinone(bareinterval(2) * x) - - @test (interval(2) * x) isa Interval - @test isthinone(interval(2) * x) - @test isguaranteed(interval(2) * x) - - # - - @exact function f(x) - return x^2 - 2x + 1 - end - - @test f(1.0) isa Real - @test iszero(f(1.0)) - - @test f(bareinterval(1)) isa BareInterval - @test isthinzero(f(bareinterval(1))) - - @test f(interval(1)) isa Interval - @test isthinzero(f(interval(1))) - @test isguaranteed(f(interval(1))) - - # - - @test isequal_interval(promote(bareinterval(1, 2), exact(3))[2], bareinterval(3)) - - @test isequal_interval(promote(interval(1, 2), exact(3))[2], interval(3)) - - # Exact operations (Integers and Rationals) - @test exact(1) + exact(2) === exact(3) - @test exact(1) - exact(3) === exact(-2) - @test exact(2) * exact(3) === exact(6) - @test exact(4) / exact(2) === exact(2.0) - @test exact(1) / exact(3) === 1/3 - @test exact(1) / exact(Int64(2)^Int64(60) + Int64(1)) === 1/Int64(2)^Int64(60) - @test exact(-Int64(1)) / exact(typemin(Int32)) === exact(-Int64(1)/typemin(Int32)) - @test exact(-1) / exact(typemin(Int)) === -1/typemin(Int) - @test exact(2) ^ exact(3) === exact(8) - if VERSION ≥ v"1.11" - @test_throws DomainError exact(2) ^ (-2) - else - @test_throws InexactError exact(2) ^ (-2) - end - - @test exact(1//2) + exact(1//4) === exact(3//4) - @test exact(1//2) - exact(1//4) === exact(1//4) - @test exact(1//2) * exact(1//2) === exact(1//4) - - # Checked Arithmetic Overflows - @test_throws OverflowError exact(typemax(Int)) + exact(1) - @test_throws OverflowError exact(typemin(Int)) - exact(1) - @test_throws OverflowError exact(typemax(Int)) * exact(2) - @test_throws OverflowError -exact(typemin(Int)) - - # Bool operations - let x = @exact 1.5 - @test (x * exact(true)) isa ExactReal - @test (x * exact(true)) === exact(1.5) - - @test (x * exact(false)) isa ExactReal - @test (x * exact(false)) === exact(0.0) - - @test (x / exact(true)) isa ExactReal - @test (x / exact(true)) === exact(1.5) - end - - # Loss of exactness - let val = exact(1.5) + exact(2.0) - @test val isa Float64 - @test val == 3.5 - end - - let val2 = exact(1.5) * exact(2.0) - @test val2 isa Float64 - @test val2 == 3.0 - end -end - -@testset "Exact literals with bare intervals" begin - # `+`, `-`, `*`, `/` and `\` on a matching number type bypass the promotion to - # a thin interval, so they must be more specific than the generic methods and - # must return exactly what those return. - specialized(f, S, R) = which(f, Tuple{S,R}).sig isa UnionAll - @test all(f -> specialized(f, BareInterval{Float64}, ExactReal{Float64}), (+, -, *, /)) - @test all(f -> specialized(f, ExactReal{Float64}, BareInterval{Float64}), (+, -, *, \)) - - # ... and the mixed number types must not, since the promotion is where the - # `ExactReal` gets rounded to the interval's number type. - @test !specialized(*, BareInterval{Float64}, ExactReal{Int}) - - viapromotion(f, x, y) = f(promote(x, y)...) - samebits(x::BareInterval, y::BareInterval) = (inf(x) === inf(y)) & (sup(x) === sup(y)) - - for T ∈ (Float64, Float32, Rational{Int}) - vals = T <: Rational ? - (zero(T), one(T), -one(T), T(1//2), T(-5//3)) : - (zero(T), -zero(T), one(T), -one(T), T(0.5), T(-2.5), T(0.1), T(-0.1), - floatmax(T), floatmin(T)) - ivs = (bareinterval(T, 1, 2), bareinterval(T, -2, -1), bareinterval(T, -1, 2), - bareinterval(T, 0, 1), bareinterval(T, -1, 0), bareinterval(T, 0, 0), - bareinterval(T(1//10), T(1//10)), bareinterval(T, -Inf, 2), - bareinterval(T, 3, Inf), entireinterval(BareInterval{T}), - emptyinterval(BareInterval{T})) - for v ∈ vals, x ∈ ivs - k = exact(v) - @test samebits(x + k, viapromotion(+, x, k)) - @test samebits(k + x, viapromotion(+, k, x)) - @test samebits(x - k, viapromotion(-, x, k)) - @test samebits(k - x, viapromotion(-, k, x)) - @test samebits(x * k, viapromotion(*, x, k)) - @test samebits(k * x, viapromotion(*, k, x)) - @test samebits(x / k, viapromotion(/, x, k)) - @test samebits(k \ x, viapromotion(\, k, x)) - end - end - - # A non-finite `ExactReal` has no thin interval; both routes warn and return - # the empty interval. - let x = bareinterval(1.0, 2.0) - for v ∈ (Inf, -Inf, NaN) - @test isempty_interval(@test_logs (:warn,) x + exact(v)) - @test isempty_interval(@test_logs (:warn,) x - exact(v)) - @test isempty_interval(@test_logs (:warn,) exact(v) - x) - @test isempty_interval(@test_logs (:warn,) x * exact(v)) - @test isempty_interval(@test_logs (:warn,) x / exact(v)) - end - end - - # Mixed number types still round through the promotion. - @test isequal_interval(bareinterval(1.0, 2.0) * exact(2), bareinterval(2.0, 4.0)) - @test isequal_interval(bareinterval(1.0, 2.0) + exact(1//3), - bareinterval(1.0, 2.0) + bareinterval(Float64, 1//3)) -end diff --git a/test/interval_tests/forwarddiff.jl b/test/interval_tests/forwarddiff.jl deleted file mode 100644 index 906a973e8..000000000 --- a/test/interval_tests/forwarddiff.jl +++ /dev/null @@ -1,108 +0,0 @@ -import ForwardDiff - -@testset "BareInterval" begin - @testset "abs" begin - @test_throws MethodError ForwardDiff.derivative(abs, bareinterval(-1, 1)) - end -end - -@testset "Interval" begin - @testset "abs" begin - @test ForwardDiff.derivative(abs, interval(-2, -1)) === interval(-1, -1, com) - @test ForwardDiff.derivative(abs, interval( 1, 2)) === interval( 1, 1, com) - @test ForwardDiff.derivative(abs, interval( 0 )) === interval(-1, 1, trv) - @test ForwardDiff.derivative(abs, interval(-1, 0)) === interval(-1, 0, trv) - @test ForwardDiff.derivative(abs, interval( 0, 1)) === interval( 0, 1, trv) - @test ForwardDiff.derivative(abs, interval(-2, 2)) === interval(-1, 1, trv) - - f(x) = abs(x)^interval(2) - @test ForwardDiff.derivative(f, interval(-1, 1)) === interval(-2, 2, trv) - - g(x) = abs(x)^2 - @test ForwardDiff.derivative(g, interval(-1, 1) ) === interval(convert(Interval{Float64}, -2), convert(Interval{Float64}, 2), trv) - @test all(ForwardDiff.gradient( v -> g(v[1]), [interval(-1, 1)]) .=== [interval(convert(Interval{Float64}, -2), convert(Interval{Float64}, 2), trv)]) - @test all(ForwardDiff.hessian( v -> g(v[1]), [interval( 0 )]) .=== [interval(convert(Interval{Float64}, -2), convert(Interval{Float64}, 2), trv)]) - @test all(ForwardDiff.hessian( v -> g(v[1]), [interval(-1, 1)]) .=== [interval(convert(Interval{Float64}, -2), convert(Interval{Float64}, 2), trv)]) - end - - @testset "sin" begin - x, w = interval(2), interval(-0.5, 0.5) - ϕ(t) = sin(x + (1+t)*w) - ϕ′(t) = cos(x + (1+t)*w) * w - ϕ′′(t) = -sin(x + (1+t)*w) * w * w - ϕ′′′(t) = -cos(x + (1+t)*w) * w * w * w - dϕ(t) = ForwardDiff.derivative(ϕ, t) - ddϕ(t) = ForwardDiff.derivative(dϕ, t) - dddϕ(t) = ForwardDiff.derivative(ddϕ, t) - - @test ϕ′(0) === dϕ(0) - @test ϕ′′(0) === ddϕ(0) - @test ϕ′′′(0) === dddϕ(0) - - y = interval(1) - ψ(t) = sin(x + (y+t)*w) - ψ′(t) = cos(x + (y+t)*w) * w - ψ′′(t) = -sin(x + (y+t)*w) * w * w - ψ′′′(t) = -cos(x + (y+t)*w) * w * w * w - dψ(t) = ForwardDiff.derivative(ψ, t) - ddψ(t) = ForwardDiff.derivative(dψ, t) - dddψ(t) = ForwardDiff.derivative(ddψ, t) - @test ψ′(0) === dψ(0) && !isguaranteed(ψ′(0)) - @test ψ′′(0) === ddψ(0) && !isguaranteed(ψ′′(0)) - @test ψ′′′(0) === dddψ(0) && !isguaranteed(ψ′′′(0)) - t₀ = interval(0) - @test ψ′(t₀) === dψ(t₀) && isguaranteed(ψ′(t₀)) - @test ψ′′(t₀) === ddψ(t₀) && isguaranteed(ψ′′(t₀)) - @test ψ′′′(t₀) === dddψ(t₀) && isguaranteed(ψ′′′(t₀)) - end - - @testset "Power" begin - fxy(xy) = xy[1]^xy[2] - - for x in [0.0, 1.1, 2.2] - for y in [-3.3, 0.0, 4.4] - fx(xx) = xx^y - fxi(xx) = xx^interval(y) - fy(yy) = x^yy - fyi(yy) = interval(x)^yy - - dfdx = ForwardDiff.derivative(fxi, interval(x)) - dfdy = ForwardDiff.derivative(fyi, interval(y)) - grad = ForwardDiff.gradient(fxy, [interval(x), interval(y)]) - - @test isguaranteed(dfdx) - @test isguaranteed(dfdy) - @test isguaranteed(grad[1]) - @test isguaranteed(grad[2]) - - if iszero(x) && y < 0 - @test decoration(dfdx) == trv - else - @test in_interval(ForwardDiff.derivative(fx, x), dfdx) - end - - if iszero(x) && y <= 0 - @test decoration(dfdy) == trv - else - @test in_interval(ForwardDiff.derivative(fy, y), dfdy) - end - - if iszero(x) && iszero(y) - @test decoration(grad[1]) == trv - @test decoration(dfdx) == com - else - @test isequal_interval(dfdx, grad[1]) - end - @test isequal_interval(dfdy, grad[2]) - end - end - end - - @testset "ExactReal" begin - @exact f(x) = x^2 - 2 - @test isguaranteed(ForwardDiff.derivative(f, interval(1))) - - @exact g(x) = 2^x + 6sin(x^3) - 33 - @test isguaranteed(ForwardDiff.derivative(f, interval(1))) - end -end diff --git a/test/interval_tests/hyperbolic.jl b/test/interval_tests/hyperbolic.jl deleted file mode 100644 index 2d0fe8d0c..000000000 --- a/test/interval_tests/hyperbolic.jl +++ /dev/null @@ -1,69 +0,0 @@ -@testset "sinh" begin - @test isequal_interval(sinh(emptyinterval()), emptyinterval()) - @test isequal_interval(sinh(interval(0.5)), interval(0.5210953054937473, 0.5210953054937474)) - @test isequal_interval(sinh(interval(0.5, 1.67)), interval(0.5210953054937473, 2.5619603657712102)) - @test isequal_interval(sinh(interval(-4.5, 0.1)), interval(-45.00301115199179, 0.10016675001984404)) - @test issubset_interval(sinh(interval(BigFloat, 0.5, 0.5)), sinh(interval(0.5))) - - @test issubset_interval(sinh(interval(BigFloat, 0.5, 1.67)), sinh(interval(0.5, 1.67))) - @test issubset_interval(sinh(interval(BigFloat, 1.67, 3.2)), sinh(interval(1.67, 3.2))) - @test issubset_interval(sinh(interval(BigFloat, 2.1, 5.6)), sinh(interval(2.1, 5.6))) - @test issubset_interval(sinh(interval(BigFloat, 0.5, 8.5)), sinh(interval(0.5, 8.5))) - @test issubset_interval(sinh(interval(BigFloat, -4.5, 0.1)), sinh(interval(-4.5, 0.1))) - @test issubset_interval(sinh(interval(BigFloat, 1.3, 6.3)), sinh(interval(1.3, 6.3))) -end - -@testset "cosh" begin - @test isequal_interval(cosh(emptyinterval()), emptyinterval()) - @test isequal_interval(cosh(interval(0.5)), interval(1.1276259652063807, 1.127625965206381)) - @test isequal_interval(cosh(interval(0.5, 1.67)), interval(1.1276259652063807, 2.750207431409957)) - @test isequal_interval(cosh(interval(-4.5, 0.1)), interval(1.0, 45.01412014853003)) - - @test issubset_interval(cosh(interval(BigFloat, 0.5, 0.5)), cosh(interval(0.5))) - @test issubset_interval(cosh(interval(BigFloat, 0.5, 1.67)), cosh(interval(0.5, 1.67))) - @test issubset_interval(cosh(interval(BigFloat, 1.67, 3.2)), cosh(interval(1.67, 3.2))) - @test issubset_interval(cosh(interval(BigFloat, 2.1, 5.6)), cosh(interval(2.1, 5.6))) - @test issubset_interval(cosh(interval(BigFloat, 0.5, 8.5)), cosh(interval(0.5, 8.5))) - @test issubset_interval(cosh(interval(BigFloat, -4.5, 0.1)), cosh(interval(-4.5, 0.1))) - @test issubset_interval(cosh(interval(BigFloat, 1.3, 6.3)), cosh(interval(1.3, 6.3))) -end - -@testset "tanh" begin - @test isequal_interval(tanh(emptyinterval()), emptyinterval()) - @test isequal_interval(tanh(interval(0.5)), interval(0.46211715726000974, 0.4621171572600098)) - @test isequal_interval(tanh(interval(0.5, 1.67)), interval(0.46211715726000974, 0.9315516846152083)) - @test isequal_interval(tanh(interval(-4.5, 0.1)), interval(-0.9997532108480276, 0.09966799462495583)) - - @test issubset_interval(tanh(interval(BigFloat, 0.5, 0.5)), tanh(interval(0.5))) - @test issubset_interval(tanh(interval(BigFloat, 0.5, 1.67)), tanh(interval(0.5, 1.67))) - @test issubset_interval(tanh(interval(BigFloat, 1.67, 3.2)), tanh(interval(1.67, 3.2))) - @test issubset_interval(tanh(interval(BigFloat, 2.1, 5.6)), tanh(interval(2.1, 5.6))) - @test issubset_interval(tanh(interval(BigFloat, 0.5, 8.5)), tanh(interval(0.5, 8.5))) - @test issubset_interval(tanh(interval(BigFloat, -4.5, 0.1)), tanh(interval(-4.5, 0.1))) - @test issubset_interval(tanh(interval(BigFloat, 1.3, 6.3)), tanh(interval(1.3, 6.3))) - - @test issubset_interval(tanh(interval(-4.5, 0.1)), tanh(interval(Float32, -4.5, 0.1))) - @test issubset_interval(tanh(interval(1.3, 6.3)), tanh(interval(Float32, 1.3, 6.3))) - - for a in [ interval(17, 19), interval(0.5, 1.2) ] - @test issubset_interval(tanh(a), sinh(a)/cosh(a)) - end -end - -@testset "tanh with Float32" begin - @test issubset_interval(tanh(interval(0.5)), tanh(interval(Float32, 0.5, 0.5))) - @test issubset_interval(tanh(interval(0.5, 1.67)), tanh(interval(Float32, 0.5, 1.67))) - @test issubset_interval(tanh(interval(1.67, 3.2)), tanh(interval(Float32, 1.67, 3.2))) - @test issubset_interval(tanh(interval(2.1, 5.6)), tanh(interval(Float32, 2.1, 5.6))) - @test issubset_interval(tanh(interval(0.5, 8.5)), tanh(interval(Float32, 0.5, 8.5))) -end - -@testset "inverse" begin - @test issubset_interval(asinh(interval(BigFloat, 1, 1)), asinh(interval(1))) - @test issubset_interval(asinh(interval(BigFloat, 0.9, 2)), asinh(interval(0.9, 2))) - @test issubset_interval(asinh(interval(BigFloat, 3, 4)), asinh(interval(3, 4))) - - @test issubset_interval(acosh(interval(BigFloat, 1, 1)), acosh(interval(1))) - @test issubset_interval(acosh(interval(BigFloat, -2, -0.9)), acosh(interval(-2, -0.9))) - @test issubset_interval(acosh(interval(BigFloat, 3, 4)), acosh(interval(3, 4))) -end diff --git a/test/interval_tests/linearalgebra.jl b/test/interval_tests/linearalgebra.jl deleted file mode 100644 index e1315d6c9..000000000 --- a/test/interval_tests/linearalgebra.jl +++ /dev/null @@ -1,23 +0,0 @@ -import LinearAlgebra - -@testset "Matrix inversion" begin - IntervalArithmetic.configure(; matmul = :slow) - A = [interval(2) interval(1, 2) ; interval(0) interval(1)] - @test all(isequal_interval.(inv(A), [interval(0, 1) interval(-1.25, -0.25) ; interval(-0.5, 0.5) interval(0.5, 1.5)])) - B = [interval(2) interval(1, 2) ; interval(0) interval(0, 1)] - @test all(isnai, inv(B)) -end - -@testset "Matrix multiplication" begin - IntervalArithmetic.configure(; matmul = :fast) - A = [interval(2, 4) interval(-2, 1) ; interval(-1, 2) interval(2, 4)] - imA = interval(im) * A - - @test all(issubset_interval.([interval(0, 18) interval(-16, 8) ; interval(-8, 16) interval(0, 18)], A * A)) - @test all(issubset_interval.([interval(5, 12.5) interval(-8, 2) ; interval(-2, 8) interval(5, 12.5)], A * mid.(A))) - @test all(issubset_interval.([interval(5, 12.5) interval(-8, 2) ; interval(-2, 8) interval(5, 12.5)], mid.(A) * A)) - - @test all(issubset_interval.([interval(-18, 0) interval(-8, 16) ; interval(-16, 8) interval(-18, 0)], imA * imA)) - @test all(issubset_interval.(interval(im)*[interval(5, 12.5) interval(-8, 2) ; interval(-2, 8) interval(5, 12.5)], mid.(A) * imA)) - @test all(issubset_interval.(interval(im)*[interval(5, 12.5) interval(-8, 2) ; interval(-2, 8) interval(5, 12.5)], imA * mid.(A))) -end diff --git a/test/interval_tests/loops.jl b/test/interval_tests/loops.jl deleted file mode 100644 index 210720ba4..000000000 --- a/test/interval_tests/loops.jl +++ /dev/null @@ -1,94 +0,0 @@ -@testset "Interval loop tests" begin - i = 1 - - @test inf(interval(i, i)) == 1 - @test inf(interval(i)) == 1 - - - for i in 1:10 - a = interval(i) - @test inf(a) == i - end -end - - -## Calculate pi by summing 1/i^2 to give pi^2/6: - -function calc_pi1(N) - S1 = interval(0) - - for i in 1:N - S1 += inv(interval(i)^2) - end - S1 += interval(inv(interval(N+1)), inv(interval(N))) - - return sqrt(interval(6)*S1) -end - -function calc_pi2(N) - S2 = interval(0) - - for i in 1:N - S2 += interval(1 / i^2) - end - S2 += interval(inv(interval(N+1)), inv(interval(N))) - - return sqrt(interval(6)*S2) -end - -function calc_pi3(N) - S3 = interval(0) - - for i in 1:N - S3 += interval(1 / i^2) - end - S3 += parse(Interval{Float64}, "[1/$(N+1), 1/$N]") - - return sqrt(interval(6)*S3) -end - -function calc_pi4(N) - S4 = interval(0) - II = interval(1) - - for i in N:-1:1 - S4 += II / interval(i^2) - end - S4 += II / interval(N, N+1) - - return sqrt(interval(6)*S4) -end - -function calc_pi5(N) - S5 = interval(0) - - for i in N:-1:1 - S5 += interval(1 // i^2) - end - S5 += inv(interval(N, N+1)) - - return sqrt(interval(6)*S5) -end - -@testset "Pi tests" begin - big_pi = setprecision(256) do - big(π) - end - - N = 10000 - pi1 = calc_pi1(N) - pi2 = calc_pi2(N) - pi3 = calc_pi3(N) - pi4 = calc_pi4(N) - pi5 = calc_pi5(N) - - - @test in_interval(big_pi, pi1) - @test in_interval(big_pi, pi2) - @test in_interval(big_pi, pi3) - @test in_interval(big_pi, pi4) - @test in_interval(big_pi, pi5) - - @test isequal_interval(pi1, pi2) - @test isequal_interval(pi2, pi3) -end diff --git a/test/interval_tests/multidim.jl b/test/interval_tests/multidim.jl deleted file mode 100644 index 81bfa5faf..000000000 --- a/test/interval_tests/multidim.jl +++ /dev/null @@ -1,173 +0,0 @@ -using IntervalArithmetic.Symbols - -@testset "Linear algebra" begin - IntervalArithmetic.configure(; matmul = :slow) - A = [interval(2, 4) interval(-2, 1) - interval(-1, 2) interval(2, 4)] - - b = [interval(-2, 2) - interval(-2, 2)] - - @test all(isequal_interval.(A * b, [interval(-12, 12), interval(-12, 12)])) - @test_throws IntervalArithmetic.InconclusiveBooleanOperation A \ b - - @test all(isequal_interval.(interval.([1 2; 3 4]) * interval(-1, 1), [interval(-1, 1) interval(-2, 2) ; interval(-3, 3) interval(-4, 4)])) - IntervalArithmetic.configure(; matmul = :fast) -end - - -@testset "interiordiff" begin - function sameset(A, B) - length(A) != length(B) && return false - for a ∈ A - found = false - for b ∈ B - if all(isequal_interval.(a, b)) - found = true - break - end - end - !found && return false - end - return true - end - - X = [2..4, 3..5] - Y = [3..5, 4..6] - @test sameset( - interiordiff(X, Y), - [ [3..4, 3..4], - [2..3, 3..5] ]) - - X = [2..5, 3..6] - Y = [-10..10, 4..5] - @test sameset( - interiordiff(X, Y), - [ [2..5, 3..4], - [2..5, 5..6] ]) - - X = [2..5, 3..6] - Y = [4..6, 4..5] - @test sameset( - interiordiff(X, Y), - [ [4..5, 3..4], - [4..5, 5..6], - [2..4, 3..6] ]) - - X = [2..5, 3..6] - Y = [3..4, 4..5] - @test sameset( - interiordiff(X, Y), - [ [3..4, 3..4], - [3..4, 5..6], - [2..3, 3..6], - [4..5, 3..6] ]) - - X = [2..5, 3..6] - Y = [2..4, 10..20] - @test sameset(interiordiff(X, Y), typeof(X)[X]) - - X = [2..5, 3..6] - Y = [-10..10, -10..10] - @test sameset(interiordiff(X, Y), typeof(X)[]) - - X = [1..4, 3..6, 7..10] - Y = [2..3, 4..5, 8..9] - @test sameset( - interiordiff(X, Y), - [ [2..3, 4..5, 7..8], - [2..3, 4..5, 9..10], - [2..3, 3..4, 7..10], - [2..3, 5..6, 7..10], - [1..2, 3..6, 7..10], - [3..4, 3..6, 7..10] ]) - - X = [-Inf..Inf, 1..2] - Y = [1..2, -1..1.5] - @test sameset( - interiordiff(X, Y), - [ [-Inf..1, 1..2], - [2..Inf, 1..2], - [1..2, 1.5..2] ]) -end - -@testset "Mince and hull" begin - ib2 = [-1..1, -1..1] - vb2 = mince(ib2, 4) - @test length(vb2) == 4^2 - vv = [[(-1 .. -0.5), (-1 .. -0.5)], [(-0.5 .. 0), (-1 .. -0.5)], - [(0 .. 0.5), (-1 .. -0.5)], [(0.5 .. 1), (-1 .. -0.5)], - [(-1 .. -0.5), (-0.5 .. 0)], [(-0.5 .. 0), (-0.5 .. 0)], - [(0 .. 0.5), (-0.5 .. 0)], [(0.5 .. 1), (-0.5 .. 0)], - [(-1 .. -0.5), (0 .. 0.5)], [(-0.5 .. 0), (0 .. 0.5)], - [(0 .. 0.5), (0 .. 0.5)], [(0.5 .. 1), (0 .. 0.5)], - [(-1 .. -0.5), (0.5 .. 1)], [(-0.5 .. 0), (0.5 .. 1)], - [(0 .. 0.5), (0.5 .. 1)], [(0.5 .. 1), (0.5 .. 1)]] - @test mapreduce((x, y) -> all(isequal_interval.(x, y)), &, vb2, vv) - @test all(enumerate(ib2)) do (i, Ib) - hulled = reduce(hull, [vb2[k][i] for k in eachindex(vb2)]) - isequal_interval(hulled, Ib) - end - @test mapreduce((x, y) -> all(isequal_interval.(x, y)), &, mince(ib2, (4, 4)), vb2) - @test mapreduce((x, y) -> all(isequal_interval.(x, y)), &, mince(ib2, (1,4)), - [[(-1 .. 1), (-1 .. -0.5)], [(-1 .. 1), (-0.5 .. 0)], [(-1 .. 1), (0 .. 0.5)], [(-1 .. 1), (0.5 .. 1)]]) - - vb2bis = mince(ib2, (1,4)) - @test all(enumerate(ib2)) do (i, Ib) - hulled = reduce(hull, [vb2bis[k][i] for k in eachindex(vb2bis)]) - isequal_interval(hulled, Ib) - end - - ib3 = fill(-1..1, 3) - vb3 = mince(ib3, 4) - @test length(vb3) == 4^3 - @test all(enumerate(ib3)) do (i, Ib) - hulled = reduce(hull, [vb3[k][i] for k in eachindex(vb3)]) - isequal_interval(hulled, Ib) - end - @test mapreduce((x, y) -> all(isequal_interval.(x, y)), &, mince(ib3, (4,4,4)), vb3) - @test mapreduce((x, y) -> all(isequal_interval.(x, y)), &, mince(ib3, (2,1,1)), - [[(-1 .. 0), (-1 .. 1), (-1 .. 1)], [(0 .. 1), (-1 .. 1), (-1 .. 1)]]) - vb3bis = mince(ib3, (2,1,1)) - @test all(enumerate(ib3)) do (i, Ib) - hulled = reduce(hull, [vb3bis[k][i] for k in eachindex(vb3bis)]) - isequal_interval(hulled, Ib) - end - - ib4 = fill(-1..1, 4) - vb4 = mince(ib4, 4) - @test length(vb4) == 4^4 - @test all(enumerate(ib4)) do (i, Ib) - hulled = reduce(hull, [vb4[k][i] for k in eachindex(vb4)]) - isequal_interval(hulled, Ib) - end - @test mapreduce((x, y) -> all(isequal_interval.(x, y)), &, mince(ib4, (4,4,4,4)), vb4) - @test mapreduce((x, y) -> all(isequal_interval.(x, y)), &, mince(ib4, (1,1,1,1)), (ib4,)) -end - -@testset "Bisect" begin - v = [interval(0, 1), interval(0, 2)] - w = bisect(v, 1, 0.5) - @test all(isequal_interval.( w[1], [interval(0, 0.5), interval(0, 2)] )) & - all(isequal_interval.( w[2], [interval(0.5, 1), interval(0, 2)] )) - w = bisect(v, 2, 0.5) - @test all(isequal_interval.( w[1], [interval(0, 1), interval(0, 1)] )) & - all(isequal_interval.( w[2], [interval(0, 1), interval(1, 2)] )) - w = bisect(v, 1, 0.25) - @test all(isequal_interval.( w[1], [interval(0, 0.25), interval(0, 2)] )) & - all(isequal_interval.( w[2], [interval(0.25, 1), interval(0, 2)] )) - w = bisect(v, 2, 0.25) - @test all(isequal_interval.( w[1], [interval(0, 1), interval(0, 0.5)] )) & - all(isequal_interval.( w[2], [interval(0, 1), interval(0.5, 2)] )) - w = bisect(v, 1) - @test all(isequal_interval.( w[1], [interval(0, 0.5), interval(0, 2)] )) & - all(isequal_interval.( w[2], [interval(0.5, 1), interval(0, 2)] )) - w = bisect(v, 2) - @test all(isequal_interval.( w[1], [interval(0, 1), interval(0, 1)] )) & - all(isequal_interval.( w[2], [interval(0, 1), interval(1, 2)] )) - - v = [interval(-Inf, Inf), interval(-Inf, Inf)] - w = bisect(v, 1, 0.5) - @test all(isequal_interval.( w[1], [interval(-Inf, 0), interval(-Inf, Inf)] )) & - all(isequal_interval.( w[2], [interval(0, Inf), interval(-Inf, Inf)] )) -end diff --git a/test/interval_tests/numeric.jl b/test/interval_tests/numeric.jl deleted file mode 100644 index f1712d97a..000000000 --- a/test/interval_tests/numeric.jl +++ /dev/null @@ -1,387 +0,0 @@ -@testset "Brodcasting" begin - x = interval(1, 2) - - for f ∈ (+, -, *, /) - @test isequal_interval(f.(x, x), f(x, x)) - end -end - -@testset "Numeric tests" begin - a = interval(0.1, 1.1) - b = interval(0.9, 2.0) - c = interval(0.25, 4.0) - - - ## Basic arithmetic - @test isequal_interval(+a, a) - @test isequal_interval(-a, interval(-sup(a), -inf(a))) - for f in (:+, :-, :*, :/) - @eval begin - @test isequal_interval($f(interval(Float64, pi), interval(Float32, pi)), - $f(interval(Float64, pi), Interval{Float64}(interval(Float32, pi)))) - end - end - @test isequal_interval(interval(Rational{Int}, 1//4, 1//2) + interval(Rational{Int}, 2//3), interval(Rational{Int}, 11//12, 7//6)) - @test isequal_interval(interval(Rational{Int}, 1//4, 1//2) - interval(Rational{Int}, 2//3), interval(Rational{Int}, -5//12, -1//6)) - - @test isequal_interval(interval(-30.0,-15.0) / interval(-5.0,-3.0), interval(3.0, 10.0)) - @test isequal_interval(interval(-30,-15) / interval(-5,-3), interval(3.0, 10.0)) - @test isequal_interval(a/c, interval(0.025, 4.4)) - @test isequal_interval(c/interval(4.0), interval(6.25e-02, 1e+00)) - @test isequal_interval(c/zero(c), emptyinterval(c)) - @test isequal_interval(interval(0.0, 1.0)/interval(0.0,1.0), interval(0.0, Inf)) - @test isequal_interval(interval(-1.0, 1.0)/interval(0.0,1.0), entireinterval(c)) - @test isequal_interval(interval(-1.0, 1.0)/interval(-1.0,1.0), entireinterval(c)) - - @test all(isequal_interval.(extended_div(interval(-30.0,-15.0), interval(-5.0,-3.0)), (interval(3.0, 10.0), emptyinterval(c)))) - @test all(isequal_interval.(extended_div(interval(-30,-15) , interval(-5,-3)), (interval(3.0, 10.0), emptyinterval(c)))) - @test all(isequal_interval.(extended_div(interval(1.0, 2.0), interval(0.1, 1.0)), (interval(1, 20.0), emptyinterval(c)))) - @test all(isequal_interval.(extended_div(a, c), (interval(0.025, 4.4e+00), emptyinterval(c)))) - @test all(isequal_interval.(extended_div(c, interval(4.0)), (interval(6.25e-02, 1e+00), emptyinterval(c)))) - @test all(isequal_interval.(extended_div(c, zero(c)), (emptyinterval(c), emptyinterval(c)))) - @test all(isequal_interval.(extended_div(interval(0.0, 1.0), interval(0.0,1.0)), (entireinterval(c), emptyinterval(c)))) - @test all(isequal_interval.(extended_div(interval(-1.0, 1.0), interval(0.0,1.0)), (entireinterval(c), emptyinterval(c)))) - @test all(isequal_interval.(extended_div(interval(-1.0, 1.0), interval(-1.0,1.0)), (entireinterval(c), emptyinterval(c)))) - @test all(isequal_interval.(extended_div(interval(1.0, 2.0), interval(-4.0, 4.0)), (interval(-Inf, -0.25), interval(0.25, Inf)))) - @test all(isequal_interval.(extended_div(interval(-2.0, -1.0), interval(-2.0, 4.0)), (interval(-Inf, -0.25), interval(0.5, Inf)))) - @test all(isequal_interval.(extended_div(interval(0.0, 0.0), interval(-1.0, 1.0)), (entireinterval(c), emptyinterval(c)))) - - # IEEE 1788-2015 §10.5.5 mulRevToPair: decorations on extended_div outputs. - # When the numerator and denominator are nonempty and 0 ∉ denominator, the - # first output is decorated like normal division c/b and the second is - # empty/trv. In all other cases both outputs are decorated trv. - let r = extended_div(interval(-1.0, 1.0), interval(3.0, 4.0)) # 0 ∈ x, 0 ∉ y → normal division - @test decoration(r[1]) == com - @test decoration(r[2]) == trv - end - let r = extended_div(interval(-30.0, -15.0), interval(-5.0, -3.0)) # 0 ∉ x, 0 ∉ y → normal division - @test decoration(r[1]) == com - @test decoration(r[2]) == trv - end - let r = extended_div(interval(1.0, 2.0), interval(-4.0, 4.0)) # 0 ∈ interior(y) → trv - @test decoration(r[1]) == trv - @test decoration(r[2]) == trv - end - let r = extended_div(interval(-1.0, 1.0), interval(-1.0, 1.0)) # 0 ∈ x, 0 ∈ y → trv - @test decoration(r[1]) == trv - @test decoration(r[2]) == trv - end - let r = extended_div(interval(1.0, 2.0), interval(0.0, 1.0)) # 0 on boundary of y → trv - @test decoration(r[1]) == trv - @test decoration(r[2]) == trv - end - # Decoration of input is honoured (cannot exceed it) even when 0 ∉ y. - let r = extended_div(interval(-1.0, 1.0, trv), interval(3.0, 4.0)) - @test decoration(r[1]) == trv - @test decoration(r[2]) == trv - end - - @test isequal_interval(interval(0, Inf) * interval(-1, Inf), interval(-Inf, Inf)) - - result = interval(1.1) * interval(2) + interval(3) - @test isequal_interval(muladd(interval(1.1), interval(2), interval(3)), result) - @test isequal_interval(muladd(interval(1.1), interval(Float32, 2), interval(3)), result) - -end - -@testset "Arithmetic with constants" begin - x = interval(1, 2) - - @test isequal_interval(interval(0.1) + x, interval(1.0999999999999999, 2.1)) - @test isequal_interval(interval(3.0) - x, x) - @test isequal_interval(interval(3.1) - x, interval(1.1, 2.1)) - @test isequal_interval(interval(0.1) * interval(1), interval(0.1, 0.1)) - @test isequal_interval(interval(0.0) * interval(1), interval(0.0, 0.0)) - @test isequal_interval(interval(1) / interval(10.0), interval(0.09999999999999999, 0.1)) -end - -@testset "Arithmetic with irrational" begin - @test isequal_interval(interval(1) * interval(π), interval(π)) - @test isequal_interval(interval(π) * interval(1), interval(π)) - @test isequal_interval(interval(π) + interval(0), interval(π)) - @test isequal_interval(interval(0) + interval(π), interval(π)) - @test isequal_interval(interval(π) - interval(0), interval(π)) - @test isequal_interval(interval(0) - interval(π), -interval(π)) -end - -@testset "Power tests" begin - @test isequal_interval(pown(interval(2,3) , 2), interval(4, 9)) - @test isequal_interval(pown(interval(0,3) , 2), interval(0, 9)) - @test isequal_interval(pown(interval(-3,0) , 2), interval(0, 9)) - @test isequal_interval(pown(interval(-3,-2), 2), interval(4, 9)) - @test isequal_interval(pown(interval(-3,2) , 2), interval(0, 9)) - @test isequal_interval(pown(interval(0,3) , 3), interval(0, 27)) - @test isequal_interval(pown(interval(2,3) , 3), interval(8, 27)) - @test isequal_interval(pown(interval(-3,0) , 3), interval(-27., 0.)) - @test isequal_interval(pown(interval(-3,-2), 3), interval(-27, -8)) - @test isequal_interval(pown(interval(-3,2) , 3), interval(-27., 8.)) - @test isequal_interval(pown(interval(0,3) , -2), interval(1/9, Inf)) - @test isequal_interval(pown(interval(-3,0) , -2), interval(1/9, Inf)) - @test isequal_interval(pown(interval(-3,2) , -2), interval(1/9, Inf)) - @test isequal_interval(pown(interval(2,3) , -2), interval(1/9, 1/4)) - @test isequal_interval(pown(interval(1,2) , -3), interval(1/8, 1.0)) - @test isequal_interval(pown(interval(0,3) , -3), interval(1/27, Inf)) - @test isequal_interval(pown(interval(-1,2) , -3), entireinterval()) - @test isequal_interval(pown(interval(-3,2) , 3), interval(-27, 8)) - @test isequal_interval(pow(interval(0.0), interval(1.1)), interval(0)) - @test isequal_interval(pown(interval(0.0) , 0), interval(1)) - @test isequal_interval(pow(interval(0.0), interval(1//10)), interval(0)) - @test isequal_interval(pow(interval(0.0), interval(-1//10)), emptyinterval()) - @test isequal_interval(pown(emptyinterval(), 0), emptyinterval()) - @test isequal_interval(pown(interval(2.5) , 3), interval(15.625, 15.625)) - @test isequal_interval(pown(interval(5//2), 3), interval(125//8)) - - x = interval(-3, 2) - @test isequal_interval(pown(x, 3), interval(-27, 8)) - - @test isequal_interval(pow(interval(-3, 4), interval(0.5)), interval(0, 2)) - @test isequal_interval(pow(interval(-3, 4), interval(0.5)), pow(interval(-3, 4), (1//2))) - @test isequal_interval(pown(interval(-3, 2), 2), interval(0.0, 9.0)) - @test isequal_interval(pow(interval(-3, 4), interval(0.5)), interval(0, 2)) - @test isequal_interval(pow(interval(BigFloat, -3, 4), interval(0.5)), interval(BigFloat, 0, 2)) - - @test dist(pow(interval(1, 27), interval(1/3)), interval(1, 3)) < 2*inf(eps(interval(1, 3))) - @test dist(pow(interval(1, 27), interval(1/3)), interval(1, 3)) < 2*inf(eps(interval(1, 3))) - @test issubset_interval(interval(1, 3), pow(interval(1, 27), interval(1//3))) - @test isequal_interval(pow(interval(0.1, 0.7), interval(1//3)), interval(0.46415888336127786, 0.8879040017426008)) - @test dist(pow(interval(0.1, 0.7), interval(1/3)), - interval(0.46415888336127786, 0.8879040017426008)) < 2*inf(eps(pow(interval(0.1, 0.7), interval(1/3)))) - - x = interval(BigFloat, 27) - y = pow(x, interval(1//3)) - @test diam(y) == 0 - x = interval(BigFloat, 9.595703125) - y = pow(x, interval(1//3)) - @test diam(y) == 0 - x = interval(BigFloat, 0.1) - y = pow(x, interval(1//3)) - @test (0 <= diam(y) < 1e-76) -end - -@testset "Exp and log tests" begin - @test issubset_interval(exp(interval(BigFloat, 1//2)), exp(interval(1//2))) - @test in_interval(exp(big(1//2)), exp(interval(1//2))) - @test issubset_interval(exp(interval(BigFloat, 0.1)), exp(interval(0.1))) - @test isequal_interval(exp(interval(0.1)), interval(1.1051709180756475e+00, 1.1051709180756477e+00)) - @test diam(exp(interval(0.1))) == eps(exp(0.1)) - - @test issubset_interval(log(interval(BigFloat, 1//2)), log(interval(1//2))) - @test in_interval(log(big(1//2)), log(interval(1//2))) - @test issubset_interval(log(interval(BigFloat, 0.1)), log(interval(0.1))) - @test isequal_interval(log(interval(0.1)), interval(-2.3025850929940459e+00, -2.3025850929940455e+00)) - @test diam(log(interval(0.1))) == eps(log(0.1)) - - @test issubset_interval(exp2(interval(BigFloat, 1//2)), exp2(interval(1//2))) - @test isequal_interval(exp2(interval(1024.0)), interval(1.7976931348623157e308, Inf)) - @test issubset_interval(exp10(interval(BigFloat, 1//2)), exp10(interval(1//2))) - @test isequal_interval(exp10(interval(308.5)), interval(1.7976931348623157e308, Inf)) - - @test issubset_interval(log2(interval(BigFloat, 1//2)), log2(interval(1//2))) - @test isequal_interval(log2(interval(0.25, 0.5)), interval(-2.0, -1.0)) - @test in_interval(log10(big(1//10)), log10(interval(1//10))) - - @test isequal_interval(log1p(interval(-10.0)), emptyinterval()) -end - -@testset "Comparison tests" begin - d = interval(0.1, 2) - - @test isstrictless(d, interval(3)) - @test isweakless(d, interval(2)) - @test isstrictless(interval(-1), d) - - # abs - @test isequal_interval(abs(interval(0.1, 0.2)), interval(0.1, 0.2)) - @test isequal_interval(abs(interval(-1, 2)), interval(0, 2)) - - # real - @test isequal_interval(real(interval(-1, 1)), interval(-1, 1)) -end - -@testset "Rational tests" begin - f = 1 // 3 - g = 1 // 3 - @test isequal_interval(interval(f*g), interval(1//9)) - @test isequal_interval(interval(1//9), interval(1//9, 1//9)) - @test isequal_interval(interval(f, g) - interval(1//1), interval(-2 // 3, -2 // 3)) - @test issubset_interval(interval(f*g), interval(1)/interval(9)) -end - -@testset "Floor etc. tests" begin - a = interval(0.1) - b = interval(0.1, 0.1) - @test dist(a, b) <= inf(eps(a)) - - @test isequal_interval(floor(interval(0.1, 1.1)), interval(0, 1)) - @test isequal_interval(round(interval(0.1, 1.1), RoundDown), interval(0, 1)) - @test isequal_interval(ceil(interval(0.1, 1.1)), interval(1, 2)) - @test isequal_interval(round(interval(0.1, 1.1), RoundUp), interval(1, 2)) - @test isequal_interval(sign(interval(0.1, 1.1)), interval(1.0)) - @test isequal_interval(trunc(interval(0.1, 1.1)), interval(0.0, 1.0)) - @test isequal_interval(round(interval(0.1, 1.1), RoundToZero), interval(0.0, 1.0)) - @test isequal_interval(round(interval(0.1, 1.1)), interval(0.0, 1.0)) - @test isequal_interval(round(interval(0.1, 1.5)), interval(0.0, 2.0)) - @test isequal_interval(round(interval(-1.5, 0.1)), interval(-2.0, 0.0)) - @test isequal_interval(round(interval(-2.5, 0.1)), interval(-2.0, 0.0)) - @test isequal_interval(round(interval(0.1, 1.1), RoundNearest), interval(0.0, 1.0)) - @test isequal_interval(round(interval(0.1, 1.5), RoundNearest), interval(0.0, 2.0)) - @test isequal_interval(round(interval(-1.5, 0.1), RoundNearest), interval(-2.0, 0.0)) - @test isequal_interval(round(interval(-2.5, 0.1), RoundNearest), interval(-2.0, 0.0)) - @test isequal_interval(round(interval(0.1, 1.1), RoundNearestTiesAway), interval(0.0, 1.0)) - @test isequal_interval(round(interval(0.1, 1.5), RoundNearestTiesAway), interval(0.0, 2.0)) - @test isequal_interval(round(interval(-1.5, 0.1), RoundNearestTiesAway), interval(-2.0, 0.0)) - @test isequal_interval(round(interval(-2.5, 0.1), RoundNearestTiesAway), interval(-3.0, 0.0)) -end - -@testset "Fast power" begin - - @testset "Fast integer powers" begin - x = interval(1, 2) - @test isequal_interval(fastpow(x, 2), interval(1, 4)) - @test isequal_interval(fastpow(x, 3), interval(1, 8)) - @test isempty_interval(fastpow(-x, 3)) - - @test isequal_interval(fastpow(interval(-1, 2), 2), interval(0, 4)) - @test isequal_interval(fastpow(interval(-1, 2), 3), interval(0, 8)) - @test isequal_interval(fastpow(interval(-1, 2), 4), interval(0, 16)) - - @test isempty_interval(fastpow(interval(-2, -1), interval(-1, -1))) - - @test isequal_interval(fastpow(interval(BigFloat, -1, 2), 2), interval(0, 4)) - @test isequal_interval(fastpow(interval(BigFloat, -1, 2), 3), interval(0, 8)) - @test isequal_interval(fastpow(interval(BigFloat, 1, 2), 2), interval(1, 4)) - - x = interval(π) - @test isinterior(pow(x, 100), fastpow(x, 100)) - @test isinterior(pow(x, 50), fastpow(x, 50)) - @test isinterior(pow(x, 50), fastpow(x, 50)) - - x = interval(2) - @test isequal_interval(fastpow(x, 2000), interval(floatmax(), Inf)) - end - - @testset "Fast real powers" begin - x = interval(1, 2) - @test isequal_interval(fastpow(x, 0.5), interval(1.0, 1.4142135623730951)) - @test isequal_interval(fastpow(x, 0.5), pow(x, interval(0.5))) - - y = interval(2, 3) - @test isequal_interval(fastpow(y, -0.5), interval(0.5773502691896257, 0.7071067811865476)) - - y = interval(-2, 3) - @test isequal_interval(fastpow(y, 2.1), interval(0.0, 10.045108566305146)) - @test issubset_interval(pow(y, interval(2.1)), fastpow(y, 2.1)) - end - - @testset "Fast interval powers" begin - x = interval(1, 2) - @test isequal_interval(pow(x, interval(-1.5, 2.5)), interval(0.35355339059327373, 5.656854249492381)) - - y = interval(-2, 3) - @test isequal_interval(fastpow(y, 2.1), interval(0.0, 10.045108566305146)) - @test isequal_interval(fastpow(y, interval(-2, 3)), interval(0, Inf)) - - @test isequal_interval(fastpow(y, interval(2.1)), interval(0.0, 10.045108566305146)) - end - - @testset "sqrt" begin - @test isequal_interval(sqrt(interval(2, 3)), interval(1.414213562373095, 1.7320508075688774)) - - @test isequal_interval(sqrt(big(interval(2, 3))), interval(big"1.414213562373095048801688724209698078569671875376948073176679737990732478462102", big"1.732050807568877293527446341505872366942805253810380628055806979451933016908815")) - end - - @testset "cbrt" begin - @test isequal_interval(cbrt(interval(2, 3)), interval(1.259921049894873, 1.4422495703074085)) - @test isequal_interval(cbrt(big(interval(2, 3))), interval(big"1.259921049894873164767210607278228350570251464701507980081975112155299676513956", big"1.442249570307408382321638310780109588391869253499350577546416194541687596830003")) - @test issubset_interval(cbrt(big(interval(2, 3))), cbrt(interval(2, 3))) - @test issubset_interval(Interval{Float64}(cbrt(big(interval(3, 4)))), cbrt(interval(3, 4))) - @test isequal_interval(cbrt(interval(2f0, 3f0)), interval(1.259921f0, 1.4422497f0)) - @test issubset_interval(cbrt(interval(2, 3)), cbrt(interval(2f0, 3f0))) - end - - @testset "inv" begin - @test isequal_interval(inv(interval(2, 3)), interval(0.3333333333333333, 0.5)) - @test isequal_interval(inv(big(interval(2, 3))), interval(big"3.333333333333333333333333333333333333333333333333333333333333333333333333333305e-01", big"5.0e-01")) - end - - @testset "Float32 intervals" begin - - a = interval(Float32, 1e38) - b = interval(Float32, 1e2) - @test isequal_interval(a * b, interval(Float32, floatmax(Float32), Inf)) - @test isequal_interval(pow(interval(1.0f0), interval(1.0f0)), interval(1.0f0)) # test for PR #482 - end - - - -end - -@testset "Mince for `Interval`s" begin - II = interval(-1, 1) - v = mince(II, 4) - @test all(isequal_interval.(v, [interval(-1, -0.5), interval(-0.5, 0), interval(0, 0.5), interval(0.5, 1)])) - @test isequal_interval(hull(v...), II) - v = mince(II, 8) - @test length(v) == 8 - @test isequal_interval(hull(v...), II) - - # an empty interval and an NaI have no nodes to interpolate - @test all(isempty_interval, mince(emptyinterval(BareInterval{Float64}), 3)) - @test all(isempty_interval, mince(emptyinterval(), 3)) - @test all(isnai, mince(nai(Float64), 3)) - - # an infinite diameter cannot be divided in equal parts - @test_throws "cannot split an unbounded interval" mince(entireinterval(), 2) - @test_throws "cannot split an unbounded interval" mince(interval(0, Inf), 2) - @test_throws "cannot split an unbounded interval" mince(entireinterval(BareInterval{Float64}), 2) - @test_throws "cannot split an unbounded interval" mince([interval(0, 1), entireinterval()], 2) - - # every piece is a valid interval and together they cover the input - for x ∈ (interval(-1, 1), interval(0, 1e300), interval(-1e300, 1e300), interval(3, 3)) - for n ∈ (1, 2, 3, 7) - v = mince(x, n) - @test length(v) == n - @test isequal_interval(reduce(hull, v), x) - @test all(z -> !isnan(inf(z)) & !isnan(sup(z)), v) - end - end -end - -@testset "rootn test" begin - @test isequal_interval(rootn(emptyinterval(), 3), emptyinterval()) - @test isequal_interval(rootn(emptyinterval(), 4), emptyinterval()) - @test isequal_interval(rootn(emptyinterval(), -3), emptyinterval()) - @test isequal_interval(rootn(emptyinterval(), -4), emptyinterval()) - @test isequal_interval(rootn(interval(1, 2), 0), emptyinterval()) - @test isequal_interval(rootn(interval(5, 8), 0), emptyinterval()) - @test isequal_interval(rootn(interval(1, 7), 0), emptyinterval()) - @test isequal_interval(rootn(interval(8, 27), 3), interval(2, 3)) - @test isequal_interval(rootn(interval(0, 27), 3), interval(0, 3)) - @test isequal_interval(rootn(interval(-27, 0), 3), interval(-3, 0)) - @test isequal_interval(rootn(interval(-27, 27), 3), interval(-3, 3)) - @test isequal_interval(rootn(interval(-27, -8), 3), interval(-3, -2)) - @test isequal_interval(rootn(interval(16, 81), 4), interval(2, 3)) - @test isequal_interval(rootn(interval(0, 81), 4), interval(0, 3)) - @test isequal_interval(rootn(interval(-81, 0), 4), interval(0)) - @test isequal_interval(rootn(interval(-81, 81), 4), interval(0, 3)) - @test isequal_interval(rootn(interval(-81, -16), 4), emptyinterval()) - @test isequal_interval(rootn(interval(8, 27), -3), interval(1/3, 1/2)) - @test isequal_interval(rootn(interval(0, 27), -3), interval(1/3, Inf)) - @test isequal_interval(rootn(interval(-27, 0), -3), interval(-Inf, -1/3)) - @test isequal_interval(rootn(interval(-27, 27), -3), interval(-Inf, Inf)) - @test isequal_interval(rootn(interval(-27, -8), -3), interval(-1/2, -1/3)) - @test isequal_interval(rootn(interval(16, 81), -4), interval(1/3, 1/2)) - @test isequal_interval(rootn(interval(0, 81), -4), interval(1/3, Inf)) - @test isequal_interval(rootn(interval(-81, 0), -4), emptyinterval()) - @test isequal_interval(rootn(interval(-81, 1), 1), interval(-81, 1)) - @test isequal_interval(rootn(interval(-81, 81), -4), interval(1/3, Inf)) - @test isequal_interval(rootn(interval(-81, -16), -4), emptyinterval()) - @test isequal_interval(rootn(interval(-81, -16), 1), interval(-81, -16)) - @test isequal_interval(rootn(interval(BigFloat, 16, 81), 4), interval(BigFloat, 2, 3)) - @test isequal_interval(rootn(interval(BigFloat, 0, 81), 4), interval(BigFloat, 0, 3)) - @test isequal_interval(rootn(interval(BigFloat, -81, 0), 4), interval(BigFloat, 0, 0)) - @test isequal_interval(rootn(interval(BigFloat, -81, 81), 4), interval(BigFloat, 0, 3)) - @test isequal_interval(rootn(interval(BigFloat, -27, 27), -3), interval(BigFloat, -Inf, Inf)) - @test isequal_interval(rootn(interval(BigFloat, -81, -16), -4), emptyinterval()) - @test isequal_interval(rootn(interval(BigFloat, -81, -16), 1), interval(BigFloat, -81, -16)) -end diff --git a/test/interval_tests/parsing.jl b/test/interval_tests/parsing.jl deleted file mode 100644 index 836cf1530..000000000 --- a/test/interval_tests/parsing.jl +++ /dev/null @@ -1,59 +0,0 @@ -@testset "BareInterval" begin - for T ∈ (Float16, Float32, Float64, BigFloat) - @test isequal_interval(parse(BareInterval{T}, "[1, 2]"), bareinterval(T, 1, 2)) - if T != BigFloat - @test isequal_interval(parse(BareInterval{T}, "[1e-324, 1e400]"), bareinterval(T, 0, Inf)) - else - @test isequal_interval(parse(BareInterval{BigFloat}, "[1e-324, 1e400]"), bareinterval(BigFloat("1e-324", RoundDown), BigFloat("1e400", RoundUp))) - end - @test isequal_interval(parse(BareInterval{T}, "[2,infinity]"), bareinterval(T, 2, Inf)) - @test isempty_interval(parse(BareInterval{T}, "[foobar]")) - end - - @test isequal_interval(parse(BareInterval{Rational{Int64}}, "0.1"), bareinterval(Rational{Int64}, 1//10)) - @test isequal_interval(parse(BareInterval{Rational{Int64}}, "[0.1, 0.3]"), bareinterval(Rational{Int64}, 1//10, 3//10)) -end - -@testset "Interval" begin - for T ∈ (Float16, Float32, Float64, BigFloat) - @test isequal_interval(parse(Interval{T}, "[1, 2]"), interval(T, 1, 2)) - if T != BigFloat - @test isequal_interval(parse(Interval{T}, "[1e-324, 1e400]"), interval(T, 0, Inf)) - else - @test isequal_interval(parse(Interval{BigFloat}, "[1e-324, 1e400]"), interval(BigFloat("1e-324", RoundDown), BigFloat("1e400", RoundUp))) - end - @test isequal_interval(parse(Interval{T}, "[2,infinity]"), interval(T, 2, Inf)) - @test isnai(parse(Interval{T}, "[foobar]")) - - x = parse(Interval{T}, "[1, 2]_com") - y = parse(Interval{T}, "[1, 2]") - z = interval(T, 1, 2) - @test isequal_interval(x, y, z) & (decoration(x) == decoration(y) == decoration(z)) - end - - @test isequal_interval(parse(Interval{Rational{Int64}}, "0.1"), interval(Rational{Int64}, 1//10)) - @test isequal_interval(parse(Interval{Rational{Int64}}, "[0.1, 0.3]"), interval(Rational{Int64}, 1//10, 3//10)) -end - -@testset "String macro" begin - @test typeof(I"0.1") == Interval{Float64} - - @test isequal_interval(I"[2/3, 1.1]", interval(0.6666666666666666, 1.1)) - @test isequal_interval(I"[1]", interval(1)) - @test isequal_interval(I"[-0x1.3p-1, 2/3]", interval(-0.59375, 0.6666666666666667)) - @test isequal_interval(I"123412341234123412341241234", interval(1.234123412341234e26, 1.2341234123412342e26)) - @test isequal_interval(interval(big"3"), interval(3)) - @test isequal_interval(interval(Float64, big"1e10000"), interval(Float64, big(10)^10000), interval(prevfloat(Inf), Inf)) - - @test in_interval(1//10, I"[0.1, 0.2]") && in_interval(2//10, I"[0.1, 0.2]") - @test issubset_interval(I"[0.1, 0.2]", interval(prevfloat(0.1), nextfloat(0.2))) - - @test nextfloat(inf(I"0.1")) == sup(I"0.1") - - @test isequal_interval(interval(0.5), interval(1//2), I"0.5") - - @test inf(I"1e300") == 9.999999999999999e299 && sup(I"1e300") == 1.0e300 - @test inf(I"-1e307") == -1.0000000000000001e307 && sup(I"-1e307") == -1.0e307 - # corner case for enclosure, `0.100000000000000006` rounds down to `0.1` for `Float64` - @test in_interval(big"0.100000000000000006", I"0.100000000000000006") -end diff --git a/test/interval_tests/piecewise.jl b/test/interval_tests/piecewise.jl deleted file mode 100644 index 7cd183538..000000000 --- a/test/interval_tests/piecewise.jl +++ /dev/null @@ -1,188 +0,0 @@ -using IntervalArithmetic: leftof, intersect_domain, isempty_domain - -@testset "Domain" begin - @testset "Construction" begin - @test_throws ErrorException Domain{:oopen, :closed}(0, 1) - @test_throws ErrorException Domain{:open, :close}(0, 1) - @test_throws MethodError Piecewise( - (Domain{:closed, :closed}(1, 2), Domain{:open, :open}(2, 3)), - (sin, cos), (-1,), (2, 3)) - @test_throws ArgumentError Piecewise( - (Domain{:closed, :closed}(1, 2), Domain{:open, :open}(2, 3)), - (sin, cos), (-1,-1), (2, 3)) - @test_throws MethodError Piecewise( - (Domain{:closed, :closed}(1, 2), Domain{:open, :open}(2, 3)), - (sin, cos, log), (-1,), (2)) - end - - @testset "leftof" begin - d1 = Domain{:open, :closed}(0, 1) - d2 = Domain{:open, :open}(0, 1) - d3 = Domain{:open, :closed}(1, 2) - d4 = Domain{:closed, :closed}(1, 2) - - @test !leftof(d1, d2) - @test leftof(d1, d3) - @test !leftof(d1, d4) - - @test leftof(d2, d3) - @test leftof(d2, d4) - end - - @testset "intersect_domain" begin - d1 = Domain{:closed, :open}(0, 10) - d2 = Domain{:closed, :open}(2, 15) - d3 = Domain{:open, :closed}(4, 7) - d4 = Domain{:open, :closed}(-20, 3) - - @test intersect_domain(d1, d2) == Domain{:closed, :open}(2, 10) - @test intersect_domain(d1, d3) == Domain{:open, :closed}(4, 7) - @test intersect_domain(d1, d4) == Domain{:closed, :closed}(0, 3) - @test intersect_domain(d2, d3) == Domain{:open, :closed}(4, 7) - @test intersect_domain(d2, d4) == Domain{:closed, :closed}(2, 3) - @test intersect_domain(d3, d4) == Domain() - end - - @testset "isempty_domain" begin - @test isempty_domain(Domain{:open, :open}(1, 1)) - @test !isempty_domain(Domain{:closed, :closed}(1, 1)) - @test !isempty_domain(Domain{:open, :open}(1, 2)) - @test isempty_domain(Domain()) - end -end - -@testset "Piecewise constructor" begin - d1 = Domain{:open, :closed}(0, 1) - d2 = Domain{:open, :closed}(1, 2) - d3 = Domain{:closed, :closed}(1, 2) - - @test_throws ArgumentError Piecewise(d2 => Constant(0), d1 => Constant(1)) - @test_throws ArgumentError Piecewise(d1 => Constant(0), d3 => Constant(1)) -end - -@testset "Step function" begin - step = Piecewise( - Domain{:open, :closed}(-Inf, 0) => Constant(0), - Domain{:open, :open}(0, 1000) => Constant(1) - ) - - @test step(-1) == 0 - @test step(100) == 1 - @test isequal_interval(step(interval(-3.2, -2.1)), interval(0)) - @test decoration(step(interval(-3.33))) == com - @test isequal_interval(step(interval(2.3, 3.4)), interval(1)) - @test decoration(step(interval(4.44))) == com - @test isequal_interval(step(interval(-22.2, 33.3)), interval(0, 1)) - @test decoration(step(interval(-11, 11))) == def - @test decoration(step(interval(500, 2000))) == trv -end - -@testset "abs" begin - myabs = Piecewise( - Domain{:open, :closed}(-Inf, 0) => x -> -x, - Domain{:open, :open}(0, Inf) => identity ; - continuity = [0] - ) - - @test myabs(-1) == 1 - @test myabs(100) == 100 - @test isequal_interval(myabs(interval(-3.2, -2.1)), interval(2.1, 3.2)) - @test decoration(myabs(interval(-3.33))) == com - @test isequal_interval(myabs(interval(2.3, 3.4)), interval(2.3, 3.4)) - @test decoration(myabs(interval(4.444))) == com - @test isequal_interval(myabs(interval(-22.2, 33.3)), interval(0, 33.3)) - @test decoration(myabs(interval(-11, 11))) == com -end - -@testset "Out of domain" begin - window = Piecewise( - Domain{:open, :closed}(-π, π) => x -> 1/2 * (cos(x) + 1) - ) - - @test_throws DomainError window(123) - @test isequal_interval(window(interval(0, π)), interval(0, 1)) - @test decoration(window(interval(-π, 0))) == com - @test isequal_interval(window(interval(-10, 10)), interval(0, 1)) - @test decoration(window(interval(-10, 10))) == trv - @test isempty_interval(window(interval(100, 1000))) -end - -@testset "Derivatives" begin - slide = Piecewise( - Domain{:open, :closed}(-Inf, -1) => x -> -2x - 1, - Domain{:open, :closed}(-1, 0) => x -> x^2, - Domain{:open, :open}(0, Inf) => Constant(0) ; - continuity = [1, 1] - ) - - @test ForwardDiff.derivative(slide, -5.5) == -2 - @test ForwardDiff.derivative(slide, -0.5) == -1 - @test ForwardDiff.derivative(slide, 1.2) == 0 - - @test isequal_interval( - ForwardDiff.derivative(slide, interval(-7, -3)), - interval(-2) - ) - @test isequal_interval( - ForwardDiff.derivative(slide, interval(-0.7, -0.3)), - interval(-1.4, -0.6) - ) - @test isequal_interval( - ForwardDiff.derivative(slide, interval(0.7, 1.3)), - interval(0) - ) - - @test isequal_interval( - ForwardDiff.derivative(slide, interval(-1.7, -0.3)), - interval(-2, -0.6) - ) - - @test isequal_interval( - ForwardDiff.derivative(slide, interval(-0.7, 1.3)), - interval(-1.4, 0) - ) - - @test isequal_interval( - ForwardDiff.derivative(slide, interval(-1.7, 1.3)), - interval(-2, 0) - ) - - x1 = interval(-0.5, 0) - x2 = interval(-3, -2) - - grad1 = ForwardDiff.gradient(xx -> slide(-xx[1]^2), [x1, x2]) - grad2 = ForwardDiff.gradient(xx -> slide(0.7xx[2]), [x1, x2]) - - g1 = -2x1 * ForwardDiff.derivative(slide, -x1^2) - g2 = 0.7 * ForwardDiff.derivative(slide, x2) - - @test isequal_interval(grad1[1], g1) - @test isequal_interval(grad1[2], interval(0)) - @test isequal_interval(grad2[1], interval(0)) - @test isequal_interval(grad2[2], g2) - - grad = ForwardDiff.gradient(xx -> slide(-xx[1]^2 + 0.7xx[2]), [x1, x2]) - g1 = -2x1 * ForwardDiff.derivative(slide, -x1^2 + 0.7x2) - g2 = 0.7 * ForwardDiff.derivative(slide, -x1^2 + 0.7x2) - @test isequal_interval(grad[1], g1) - @test isequal_interval(grad[2], g2) -end - -@testset "Singularities" begin - f = Piecewise( - Domain{:open, :closed}(0, 1) => Constant(0), - Domain{:open, :closed}(1, 2) => x -> 0.5x, - Domain{:open, :closed}(2, 3) => Constant(1), - Domain{:open, :open}(3, 4) => x -> (x-3)^2 + 1 ; - continuity = [-1, 0, 1] - ) - - @test decoration(f(interval(0.5, 1.5))) == def - @test decoration(f(interval(1.5, 2.5))) == com - @test decoration(f(interval(2.5, 3.5))) == com - - df = x -> ForwardDiff.derivative(f, x) - @test decoration(df(interval(0.5, 1.5))) == def - @test decoration(df(interval(1.5, 2.5))) == def - @test decoration(df(interval(2.5, 3.5))) == com -end \ No newline at end of file diff --git a/test/interval_tests/power.jl b/test/interval_tests/power.jl deleted file mode 100644 index d36f8a29a..000000000 --- a/test/interval_tests/power.jl +++ /dev/null @@ -1,155 +0,0 @@ -@testset "rational_power_test" begin - @test isequal_interval(pow(emptyinterval(), 1//3), emptyinterval()) - @test isequal_interval(pow(interval(1, 8), 1//3), interval(1, 2)) - @test issubset_interval(interval(2^(1//3), 2), pow(interval(2, 8), 1//3)) - @test issubset_interval(interval(1, 9^(1//3)), pow(interval(1, 9), 1//3)) - @test issubset_interval(interval(2^(1//3), 9^(1//3)), pow(interval(2, 9), 1//3)) - @test isequal_interval(pow(interval(-1, 8), 1//3), interval(0, 2)) - @test issubset_interval(interval(0, 2), pow(interval(-2, 8), 1//3)) - @test issubset_interval(interval(0, 9^(1//3)), pow(interval(-1, 9), 1//3)) - @test issubset_interval(interval(0, 9^(1//3)), pow(interval(-2, 9), 1//3)) - @test isequal_interval(pow(interval(1, 8), -1//3), interval(0.5, 1)) - @test issubset_interval(interval(0.5, 2^(-1//3)), pow(interval(2, 8), -1//3)) - @test issubset_interval(interval(9^(-1//3), 1), pow(interval(1, 9), -1//3)) - @test issubset_interval(interval(9^(-1//3), 2^(-1//3)), pow(interval(2, 9), -1//3)) - @test isequal_interval(pow(interval(-1, 8), -1//3), interval(0.5, Inf)) - @test issubset_interval(interval(0.5, Inf), pow(interval(-2, 8), -1//3)) - @test issubset_interval(interval(9^(-1//3), Inf), pow(interval(-1, 9), -1//3)) - @test issubset_interval(interval(9^(-1//3), Inf), pow(interval(-2, 9), -1//3)) - @test isequal_interval(pow(interval(-2, 4), 1//2), interval(0, 2)) - @test isequal_interval(pow(interval(-2, 8), 1//3), interval(0, 2)) - @test isequal_interval(pow(interval(-8, -2), 1//3), emptyinterval()) - @test isequal_interval(pow(interval(-8, -2), 1//2), emptyinterval()) - @test isequal_interval(pow(interval(-8, -2), -1//3), emptyinterval()) - @test isequal_interval(pow(interval(-8, -2), -1//2), emptyinterval()) - @test isequal_interval(pow(emptyinterval(), 2//3), emptyinterval()) - @test isequal_interval(pow(interval(1, 8), 2//3), interval(1, 4)) - @test issubset_interval(interval(2^(2//3), 4), pow(interval(2, 8), 2//3)) - @test issubset_interval(interval(1, 9^(2//3)), pow(interval(1, 9), 2//3)) - @test issubset_interval(interval(2^(2//3), 9^(2//3)), pow(interval(2, 9), 2//3)) - @test isequal_interval(pow(interval(-1, 8), 2//3), interval(0, 4)) - @test issubset_interval(interval(0, 4), pow(interval(-2, 8), 2//3)) - @test issubset_interval(interval(0, 9^(2//3)), pow(interval(-1, 9), 2//3)) - @test issubset_interval(interval(0, 9^(2//3)), pow(interval(-2, 9), 2//3)) - @test isequal_interval(pow(interval(1, 8), -2//3), interval(0.25, 1)) - @test issubset_interval(interval(0.25, 2^(-2//3)), pow(interval(2, 8), -2//3)) - @test issubset_interval(interval(9^(-2//3), 1), pow(interval(1, 9), -2//3)) - @test issubset_interval(interval(9^(-2//3), 2^(-2//3)), pow(interval(2, 9), -2//3)) - @test isequal_interval(pow(interval(-1, 8), -2//3), interval(0.25, Inf)) - @test issubset_interval(interval(0.25, Inf), pow(interval(-2, 8), -2//3)) - @test issubset_interval(interval(9^(-2//3), Inf), pow(interval(-1, 9), -2//3)) - @test issubset_interval(interval(9^(-2//3), Inf), pow(interval(-2, 9), -2//3)) - @test isequal_interval(pow(interval(-2, 4), 3//2), interval(0, 8)) - @test isequal_interval(pow(interval(-2, 8), 2//3), interval(0, 4)) - @test isequal_interval(pow(interval(-8, -2), 2//3), emptyinterval()) - @test isequal_interval(pow(interval(-8, -2), 3//2), emptyinterval()) - @test isequal_interval(pow(interval(-8, -2), -2//3), emptyinterval()) - @test isequal_interval(pow(interval(-8, -2), -3//2), emptyinterval()) - @test isequal_interval(pow(interval(-1, 1), 1000000000000000000000000000000000000000//1), interval(0, 1)) -end - -@testset "Interval{<:Rational}" begin - a = interval(Rational{Int64}, 1//2, 3//4) - b = interval(Rational{Int64}, 3//7, 9//12) - - @test issubset_interval(sqrt(a + b), interval(Int64(137482504)//142672337, Int64(46099201)//37639840)) - - @test issubset_interval(sqrt(interval(1//3)), interval(Int64(29354524)//50843527, Int64(50843527)//88063572)) -end - -@testset "Decorations" begin - a = interval(1, 2, IntervalArithmetic.com) - b = sqrt(a) - @test isequal_interval(interval(b), sqrt(interval(1, 2))) - @test decoration(b) == IntervalArithmetic.com - - a = interval(-1, 1, IntervalArithmetic.com) - b = sqrt(a) - @test isequal_interval(interval(b), sqrt(interval(0, 1))) - @test decoration(b) == IntervalArithmetic.trv - - @test isequal_interval(pown(interval(2, 3) , 2), interval(4, 9)) - @test isequal_interval(pown(interval(2, 3) , -2), interval(1/9, 1/4)) - @test isequal_interval(pown(interval(-3, 2) , 3), interval(-27, 8)) - @test isequal_interval(pown(interval(-3, -2), -3), interval(-1/8, -1/27)) - @test isequal_interval(pown(interval(0, 3) , 2), interval(0, 9)) - @test isequal_interval(pown(interval(0, 3) , -2), interval(1/9, Inf, IntervalArithmetic.trv)) - @test isequal_interval(pow(interval(2, 3) , interval(0, 1)), interval(1, 3)) - @test isequal_interval(pow(interval(2, 3) , interval(0, 1)), interval(1, 3)) - @test isequal_interval(pow(interval(0, 2) , interval(0, 1)), interval(0, 2, IntervalArithmetic.trv)) - @test isequal_interval(pow(interval(0, 2) , interval(0, 1)), interval(0, 2, IntervalArithmetic.trv)) - @test isequal_interval(pow(interval(-3, 2), interval(0, 1)), interval(0, 2, IntervalArithmetic.trv)) - @test isequal_interval(pow(interval(-3, 2), interval(0, 1)), interval(0, 2, IntervalArithmetic.trv)) - @test isequal_interval(pow(interval(-3, 2), interval(-1, 1)), interval(0, Inf, IntervalArithmetic.trv)) - @test isequal_interval(pow(interval(-3, 2), interval(-1, 1)), interval(0, Inf, IntervalArithmetic.trv)) -end - -@testset "Complex{<:Interval}" begin - a = interval(3 + 4im) - b = exp(a) - @test isequal_interval(real(b), interval(-13.12878308146216, -13.128783081462153)) - @test isequal_interval(imag(b), interval(-15.200784463067956, -15.20078446306795)) - - z = exp(-im * interval(π)) - @test in_interval(-1, real(z)) - @test in_interval(0, imag(z)) - - z = interval(0im) - @test isthinzero(z ^ 2) - @test isthinone(z ^ 0) - @test isempty_interval(z ^ (-1)) - - z = interval(0im) - @test isthinzero(z ^ interval(2)) - @test isthinone(z ^ interval(0)) - @test isempty_interval(z ^ interval(-1)) - @test isempty_interval(z ^ emptyinterval()) - - x = interval(3 + 4im) - @test in_interval(-7 + 24im, x^2) - @test issubset_interval(sqrt(x), x^0.5) - - a = -3.1 - @test issubset_interval(x, (x^a)^(1/a)) - - z = interval(3 + 4im) - sZ = sqrt(z) - @test in_interval(2 + im, sZ) - - @test issubset_interval(interval(0, 1)*interval(im), sqrt(interval(-1, 0) + interval(0)*interval(im))) - @test issubset_interval(interval(0, 1) + interval(0, 1)*interval(im), sqrt(interval(-1, 1) + interval(0)*interval(im))) - @test issubset_interval(interval(0, Inf) + interval(-3//8, Inf)*interval(im), sqrt(interval(-9//32, Inf)*interval(im))) - - x = interval(-0.5) + interval(im) * interval(-1e-14, 1e-14) - - y = interval(1) - @test issubset_interval(x^y, exp(y*log(x))) - - y = interval(1.5, 2.5) - res = x^y - ref = exp(y*log(x)) - # tighter on the real part (right half-plane) - @test inf(real(ref)) < -0.3 < -1e-13 < inf(real(res)) - - y = interval(-2, Inf) - @test isequal_interval(x^y, exp(y*log(x))) - - @test isequal_interval(fastpown(x, 1), x^interval(1)) - @test isequal_interval(fastpown(x, 2), x^interval(2)) - @test isequal_interval(fastpown(x, 5), x^interval(5)) -end - -@testset "Literal powers" begin - x = interval(-1,1) - n = 2 - @test isguaranteed(x ^ 2) - @test !isguaranteed(x ^ n) - @test !isguaranteed(x ^ 2.0) - if VERSION ≥ v"1.12-DEV" && Int != Int32 - @test isguaranteed(x ^ 2305843009213693952) - else - @test_broken isguaranteed(x ^ 2305843009213693952) - end - @test isequal_interval(x^2, interval(0,1)) - @test isequal_interval(x^3, x) -end diff --git a/test/interval_tests/rounding.jl b/test/interval_tests/rounding.jl deleted file mode 100644 index f826702f2..000000000 --- a/test/interval_tests/rounding.jl +++ /dev/null @@ -1,32 +0,0 @@ -x = interval(0.5) - -@testset "IntervalRounding{:correct}" begin - IntervalArithmetic.configure(rounding = :correct) - @test isequal_interval(sin(x), interval(0.47942553860420295, 0.479425538604203)) - - # https://github.com/JuliaIntervals/IntervalArithmetic.jl/issues/215 - tiny = interval(0, floatmin()) - huge = interval(floatmax(), Inf) - @test isequal_interval(tiny * tiny, interval(0, nextfloat(0.0))) - @test isequal_interval(huge * huge, interval(floatmax(), Inf)) - @test isequal_interval(huge / tiny, interval(floatmax(), Inf)) - @test isequal_interval(tiny / huge, interval(0, nextfloat(0.0))) -end - -@testset "No rounding" begin - IntervalArithmetic.configure(; rounding = :none) - @test isequal_interval(sin(x), interval(0.479425538604203, 0.479425538604203)) -end - -# @testset "IntervalRounding{:fast}" begin -# IntervalArithmetic.configure(rounding = :fast) -# @test isequal_interval(interval(Float64, 0.5) + interval(Float64, 0.5), interval(0.9999999999999999, 1.0000000000000002)) -# @test isequal_interval(sqrt(interval(Float64, 9)), interval(2.9999999999999996, 3.0000000000000004)) -# end - -# @testset "IntervalRounding{:slow}" begin -# IntervalArithmetic.default_rounding() = IntervalArithmetic.IntervalRounding{:slow}() -# @test isequal_interval(sin(x), interval(0.47942553860420295, 0.479425538604203)) -# end - -IntervalArithmetic.configure(rounding = :correct) diff --git a/test/interval_tests/set_operations.jl b/test/interval_tests/set_operations.jl deleted file mode 100644 index 751a3f5a7..000000000 --- a/test/interval_tests/set_operations.jl +++ /dev/null @@ -1,177 +0,0 @@ -using IntervalArithmetic: interval_diff - -# the arguments must be locals of a function for `@allocated` to be meaningful, -# and the call must be compiled before it is measured -function alloc_hull8(x) - hull(x, x, x, x, x, x, x, x) - return @allocated hull(x, x, x, x, x, x, x, x) -end - -function alloc_intersect8(x) - intersect_interval(x, x, x, x, x, x, x, x) - return @allocated intersect_interval(x, x, x, x, x, x, x, x) -end - -@testset "hull and intersect_interval" begin - # the empty interval is neutral for `hull` and absorbing for - # `intersect_interval`, in either position and for both interval types - for T ∈ (Float64, Float32, BigFloat, Rational{Int}) - x = bareinterval(T, 1, 2) - e = emptyinterval(BareInterval{T}) - - @test isequal_interval(hull(e, e), e) - @test isequal_interval(hull(x, e), x) - @test isequal_interval(hull(e, x), x) - @test isequal_interval(hull(x, bareinterval(T, 5, 6)), bareinterval(T, 1, 6)) - - @test isempty_interval(intersect_interval(e, e)) - @test isempty_interval(intersect_interval(x, e)) - @test isempty_interval(intersect_interval(e, x)) - @test isempty_interval(intersect_interval(x, bareinterval(T, 5, 6))) - @test isequal_interval(intersect_interval(x, bareinterval(T, 0, 3)), x) - - y = interval(T, 1, 2) - f = emptyinterval(Interval{T}) - - @test isempty_interval(hull(f, f)) - @test isequal_interval(hull(y, f), y) - @test isequal_interval(hull(f, y), y) - @test isempty_interval(intersect_interval(y, f)) - @test isempty_interval(intersect_interval(f, y)) - end - - # an NaI operand poisons the result, in either position and at any arity - n = nai(Float64) - x = interval(1, 2) - - @test isnai(hull(n, x)) & isnai(hull(x, n)) & isnai(hull(n, n)) - @test isnai(intersect_interval(n, x)) & isnai(intersect_interval(x, n)) - @test isnai(hull(x, x, n)) & isnai(hull(x, n, x)) & isnai(hull(n, x, x)) - @test isnai(hull(x, x, x, n)) - @test isnai(intersect_interval(x, x, n)) & isnai(intersect_interval(n, x, x)) - - # `dec = :default` yields `trv`, `dec = :auto` the minimal input decoration - y = interval(3, 4) - z = interval(5, Inf) - - @test decoration(hull(x, y)) == trv - @test decoration(hull(x, y; dec = :auto)) == com - @test decoration(hull(x, z; dec = :auto)) == dac - @test decoration(hull(x, y; dec = def)) == def - @test decoration(intersect_interval(x, y)) == trv - @test decoration(intersect_interval(x, interval(2, 3); dec = :auto)) == com - @test_throws ArgumentError hull(x, y; dec = :nonsense) - @test_throws ArgumentError intersect_interval(x, y; dec = :nonsense) - - # `dec = :auto` cannot promise `com` for an unbounded hull - @test decoration(hull(interval(-Inf, 0), interval(1, 2); dec = :auto)) == dac - - # bare intervals accept the variadic forms, without a `dec` keyword - bs = (bareinterval(1, 2), bareinterval(-3, 0), bareinterval(5, 6)) - - @test isequal_interval(hull(bs...), bareinterval(-3, 6)) - @test isequal_interval(hull(bs..., bareinterval(7, 9)), bareinterval(-3, 9)) - @test isempty_interval(intersect_interval(bs...)) - @test isequal_interval( - intersect_interval(bareinterval(0, 4), bareinterval(1, 5), bareinterval(2, 6)), - bareinterval(2, 4)) - @test_throws MethodError hull(bs...; dec = :auto) - - # the variadic forms agree with the pairwise reduction they replace - args = (interval(1, 2), interval(-3, 0), interval(5, 6), interval(-1, 8), - interval(0, 1), interval(-2, 2), interval(4, 7), interval(-5, 5)) - for dec ∈ (:default, :auto, trv, def, com) - for k ∈ 3:8 - xs = args[1:k] - @test isequal_interval(hull(xs...; dec = dec), reduce((a, b) -> hull(a, b; dec = dec), xs)) - @test decoration(hull(xs...; dec = dec)) == decoration(reduce((a, b) -> hull(a, b; dec = dec), xs)) - @test isequal_interval(intersect_interval(xs...; dec = dec), reduce((a, b) -> intersect_interval(a, b; dec = dec), xs)) - @test decoration(intersect_interval(xs...; dec = dec)) == decoration(reduce((a, b) -> intersect_interval(a, b; dec = dec), xs)) - end - end - - # The variadic methods must specialize on the number of arguments: without - # that, the reductions over the arguments allocate and dispatch dynamically - # past six arguments, which costs two orders of magnitude. - @test alloc_hull8(interval(1, 2)) == 0 - @test alloc_intersect8(interval(1, 2)) == 0 - @test alloc_hull8(bareinterval(1, 2)) == 0 - @test alloc_intersect8(bareinterval(1, 2)) == 0 - - # `isguaranteed` is the conjunction over all the arguments - ng = convert(Interval{Float64}, 1) - - @test !isguaranteed(hull(x, ng)) - @test !isguaranteed(hull(x, y, ng)) - @test !isguaranteed(hull(x, y, x, ng)) - @test isguaranteed(hull(x, y, x)) - @test !isguaranteed(intersect_interval(x, y, ng)) - - # mixed bound types promote, at any arity - @test numtype(hull(interval(Float32, 1, 2), interval(Float64, 3, 4))) === Float64 - @test numtype(hull(interval(Float32, 1, 2), interval(Float32, 0, 1), interval(Float64, 3, 4))) === Float64 - @test numtype(intersect_interval(interval(Float32, 1, 2), interval(Float32, 0, 3), interval(Float64, 1, 4))) === Float64 - - # a zero lower bound of the result still reports as `-0.0` - @test inf(hull(interval(0, 1), interval(2, 3))) === -0.0 - @test inf(intersect_interval(interval(0, 1), interval(-1, 3))) === -0.0 -end - -@testset "removed interval" begin - @test_throws ArgumentError intersect(interval(1)) - @test_throws ArgumentError intersect(interval(1), 2, [1], 4., 5) - @test_throws ArgumentError intersect(interval(1), interval(2.), interval(3.)) - @test_throws ArgumentError union(interval(1)) - @test_throws ArgumentError union(interval(1), 2, [1], 4., 5) - @test_throws ArgumentError union(interval(1), interval(2.), interval(3.)) - @test_throws ArgumentError setdiff(interval(1)) - @test_throws ArgumentError setdiff(interval(1), 2, [1], 4., 5) - @test_throws ArgumentError setdiff(interval(1), interval(2.), interval(3.)) - @test_throws ArgumentError symdiff(interval(1), interval(2.), interval(3.)) -end - -@testset "interiordiff" begin - x = interval(2, 4) - y = interval(3, 5) - - @test typeof(interiordiff(x, y)) == Vector{Interval{Float64}} - - @test all(isequal_interval.(interiordiff(x, x), [interval(2), interval(4)])) - @test all(isequal_interval.(interiordiff(x, emptyinterval(x)), [x])) - - @test all(isequal_interval.(interiordiff(x, y), [interval(2, 3)])) - @test all(isequal_interval.(interiordiff(y, x), [interval(4, 5)])) - - y = interval(2, 5) - - @test all(isequal_interval.(interiordiff(x, y), [interval(2)])) - @test all(isequal_interval.(interiordiff(y, x), [interval(2), interval(4, 5)])) - - x = interval(2, 5) - y = interval(3, 4) - @test all(isequal_interval.(interiordiff(x, y), [interval(2, 3), interval(4, 5)])) - - x = interval(1, 3) - z = interval(0, 5) - @test interiordiff(x, z) == Interval{Float64}[] - @test all(isequal_interval.(interiordiff(z, x), [interval(0, 1), interval(3, 5)])) -end - -@testset "interval_diff" begin - A, B = interval_diff(interval(1, 10), interval(2, 5)) - @test isequal_interval(A, interval(1, 2)) - @test isequal_interval(B, interval(5, 10)) - - @test isequal_interval( - only(interval_diff(interval(1, 10), interval(1, 5))), - interval(5, 10) - ) - - @test isequal_interval( - only(interval_diff(interval(1, 10), interval(7, 12))), - interval(1, 7) - ) - - @test interval_diff(interval(1, 10), interval(-1, 14)) == [] - @test interval_diff(interval(1, 10), interval(1, 10)) == [] -end diff --git a/test/interval_tests/supposition/numeric.jl b/test/interval_tests/supposition/numeric.jl deleted file mode 100644 index fb1dab298..000000000 --- a/test/interval_tests/supposition/numeric.jl +++ /dev/null @@ -1,29 +0,0 @@ -using Test -using IntervalArithmetic - -# Define properties to be checked -function degenerate_interval(a) - x = interval(a) - y = interval(a, a) - x === y -end - -@testset "Float tests" begin - # Define number generators - floatgen = Data.Floats() - - # Check properties - @check max_examples=1000 degenerate_interval(floatgen) -end - -@testset "Rational tests" begin - # Define number generators - intgen = Data.Integers(typemin(Int)+1,typemax(Int)) # Don't allow typemin(Int) to avoid overflow - rationalgen = @composed function generate_rational(num=intgen, den=intgen) - assume!(!(iszero(num) && iszero(den))) - return num // den - end - - # Check properties - @check max_examples = 1000 degenerate_interval(rationalgen) -end diff --git a/test/interval_tests/trigonometric.jl b/test/interval_tests/trigonometric.jl deleted file mode 100644 index 19235c61d..000000000 --- a/test/interval_tests/trigonometric.jl +++ /dev/null @@ -1,320 +0,0 @@ -@testset "rad2deg/deg2rad" begin - @test issubset_interval(interval(180, 360), rad2deg(interval(π, 2π))) - @test issubset_interval(interval(π, interval(2)*interval(π)), deg2rad(interval(180, 360))) -end - -@testset "sin" begin - @test isequal_interval(sin(interval(0.5)), interval(0.47942553860420295, 0.47942553860420301)) - @test isequal_interval(sin(interval(0.5, 1.67)), interval(4.7942553860420295e-01, 1.0)) - @test isequal_interval(sin(interval(1.67, 3.2)), interval(-5.8374143427580093e-02, 9.9508334981018021e-01)) - @test isequal_interval(sin(interval(2.1, 5.6)), interval(-1.0, 0.8632093666488738)) - @test isequal_interval(sin(interval(0.5, 8.5)), interval(-1.0, 1.0)) - @test isequal_interval(sin(interval(Float64, -4.5, 0.1)), interval(-1.0, 0.9775301176650971)) - @test isequal_interval(sin(interval(Float64, 1.3, 6.3)), interval(-1.0, 1.0)) - - @test issubset_interval(sin(interval(BigFloat, 0.5, 0.5)), sin(interval(0.5))) - @test issubset_interval(sin(interval(BigFloat, 0.5, 1.67)), sin(interval(0.5, 1.67))) - @test issubset_interval(sin(interval(BigFloat, 1.67, 3.2)), sin(interval(1.67, 3.2))) - @test issubset_interval(sin(interval(BigFloat, 2.1, 5.6)), sin(interval(2.1, 5.6))) - @test issubset_interval(sin(interval(BigFloat, 0.5, 8.5)), sin(interval(0.5, 8.5))) - @test issubset_interval(sin(interval(BigFloat, -4.5, 0.1)), sin(interval(-4.5, 0.1))) - @test issubset_interval(sin(interval(BigFloat, 1.3, 6.3)), sin(interval(1.3, 6.3))) - - # - - z = interval(3, 1e-7; format = :midpoint) + interval(4, 1e-7; format = :midpoint) * interval(im) - @test issubset_interval(sin(z), complex(sin(real(z)) * cosh(imag(z)), sinh(imag(z)) * cos(real(z)))) -end - -@testset "cos" begin - @test isequal_interval(cos(interval(0.5)), interval(0.87758256189037265, 0.87758256189037276)) - @test isequal_interval(cos(interval(2.1, 5.6)), interval(-1.0, 0.7755658785102496)) - @test isequal_interval(cos(interval(0.5, 8.5)), interval(-1.0, 1.0)) - @test isequal_interval(cos(interval(1.67, 3.2)), interval(-1.0, -0.09904103659872801)) - - @test issubset_interval(cos(interval(BigFloat, 0.5, 0.5)), cos(interval(0.5))) - @test issubset_interval(cos(interval(BigFloat, 0.5, 1.67)), cos(interval(0.5, 1.67))) - @test issubset_interval(cos(interval(BigFloat, 1.67, 3.2)), cos(interval(1.67, 3.2))) - @test issubset_interval(cos(interval(BigFloat, 2.1, 5.6)), cos(interval(2.1, 5.6))) - @test issubset_interval(cos(interval(BigFloat, 0.5, 8.5)), cos(interval(0.5, 8.5))) - @test issubset_interval(cos(interval(BigFloat, -4.5, 0.1)), cos(interval(-4.5, 0.1))) - @test issubset_interval(cos(interval(BigFloat, 1.3, 6.3)), cos(interval(1.3, 6.3))) - - k = [interval(0.0,0.0625), interval(0.0625,0.125), interval(0.0,0.125)] - x = (k[1] * 4 + k[2] * 4 + k[3] * 4) - @test isequal_interval(cos(2 * π * x), interval(-1, 1)) - @test isequal_interval(cospi(2x), interval(-1, 1)) -end - -@testset "sinpi" begin - @test isempty_interval(sinpi(emptyinterval())) - @test issubset_interval(interval(-1 , 0), sinpi(interval(1, 2))) - @test isequal_interval(sinpi(interval(0.5, 1.5)), interval(-1 , 1)) - @test issubset_interval(interval(1/sqrt(2) , 1), sinpi(interval(0.25, 0.75))) - @test issubset_interval(interval(-1/sqrt(2) , 1/sqrt(2)), sinpi(interval(-0.25, 0.25))) - if Int == Int32 && VERSION < v"1.10" - @test in_interval(0, sinpi(interval(1.0))) - @test in_interval(0, sinpi(interval(2.0))) - @test in_interval(1, sinpi(interval(0.5))) - @test in_interval(-1, sinpi(interval(1.5))) - else - @test isthin(sinpi(interval(1.0)), 0) - @test isthin(sinpi(interval(2.0)), 0) - @test isthin(sinpi(interval(0.5)), 1) - @test isthin(sinpi(interval(1.5)), -1) - end -end - -@testset "sind" begin - @test isempty_interval(sind(emptyinterval())) - @test issubset_interval(interval(-1 , 0), sind(interval(180, 360))) - @test isequal_interval(sind(interval(90, 270)), interval(-1 , 1)) - @test issubset_interval(interval(1/sqrt(2) , 1), sind(interval(45, 135))) - @test issubset_interval(interval(-1/sqrt(2) , 1/sqrt(2)), sind(interval(-45, 45))) - if Int == Int32 && VERSION < v"1.10" - @test in_interval(0, sind(interval(180))) - @test in_interval(0, sind(interval(360))) - @test in_interval(1, sind(interval(90))) - @test in_interval(-1, sind(interval(270))) - else - @test isthin(sind(interval(180)), 0) - @test isthin(sind(interval(360)), 0) - @test isthin(sind(interval(90)), 1) - @test isthin(sind(interval(270)), -1) - end -end - -@testset "cospi" begin - @test isempty_interval(cospi(emptyinterval())) - @test isequal_interval(cospi(interval(1, 2)), interval(-1 , 1)) - @test issubset_interval(interval(-1 , 0), cospi(interval(0.5, 1.5))) - @test issubset_interval(interval(-1/sqrt(2) , 1/sqrt(2)), cospi(interval(0.25, 0.75))) - @test isequal_interval(cospi(interval(-0.25, 0.25)), interval(1/sqrt(2) , 1)) - if Int == Int32 && VERSION < v"1.10" - @test in_interval(-1, cospi(interval(1.0))) - @test in_interval(1, cospi(interval(2.0))) - @test in_interval(0, cospi(interval(0.5))) - @test in_interval(0, cospi(interval(1.5))) - else - @test isthin(cospi(interval(1.0)), -1) - @test isthin(cospi(interval(2.0)), 1) - @test isthin(cospi(interval(0.5)), 0) - @test isthin(cospi(interval(1.5)), 0) - end -end - -@testset "cosd" begin - @test isempty_interval(cosd(emptyinterval())) - @test isequal_interval(cosd(interval(180, 360)), interval(-1 , 1)) - @test issubset_interval(interval(-1 , 0), cosd(interval(90, 270))) - @test issubset_interval(interval(-1/sqrt(2) , 1/sqrt(2)), cosd(interval(45, 135))) - @test isequal_interval(cosd(interval(-45, 45)), interval(1/sqrt(2) , 1)) - if Int == Int32 && VERSION < v"1.10" - @test in_interval(-1, cosd(interval(180))) - @test in_interval(1, cosd(interval(360))) - @test in_interval(0, cosd(interval(90))) - @test in_interval(0, cosd(interval(270))) - else - @test isthin(cosd(interval(180)), -1) - @test isthin(cosd(interval(360)), 1) - @test isthin(cosd(interval(90)), 0) - @test isthin(cosd(interval(270)), 0) - end -end - -@testset "sincospi" begin - x = sincospi(emptyinterval()) - @test isempty_interval(x[1]) & isempty_interval(x[2]) - x = sincospi(interval(1, 2)) - @test issubset_interval(interval(-1, 0), x[1]) & isequal_interval(x[2], interval(-1, 1)) - x = sincospi(interval(0.5, 1.5)) - @test isequal_interval(x[1], interval(-1, 1)) & issubset_interval(interval(-1, 0), x[2]) - x = sincospi(interval(0.25, 0.75)) - @test issubset_interval(interval(1/sqrt(2), 1), x[1]) & issubset_interval(interval(-1/sqrt(2), 1/sqrt(2)), x[2]) - x = sincospi(interval(-0.25, 0.25)) - @test issubset_interval(interval(-1/sqrt(2), 1/sqrt(2)), x[1]) & isequal_interval(x[2], interval(1/sqrt(2), 1)) -end - -@testset "sincosd" begin - x = sincosd(emptyinterval()) - @test isempty_interval(x[1]) & isempty_interval(x[2]) - x = sincosd(interval(180, 380)) - @test issubset_interval(interval(-1, 0), x[1]) & isequal_interval(x[2], interval(-1, 1)) - x = sincosd(interval(90, 270)) - @test isequal_interval(x[1], interval(-1, 1)) & issubset_interval(interval(-1, 0), x[2]) - x = sincosd(interval(45, 135)) - @test issubset_interval(interval(1/sqrt(2), 1), x[1]) & issubset_interval(interval(-1/sqrt(2), 1/sqrt(2)), x[2]) - x = sincosd(interval(-45, 45)) - @test issubset_interval(interval(-1/sqrt(2), 1/sqrt(2)), x[1]) & isequal_interval(x[2], interval(1/sqrt(2), 1)) -end - -@testset "tan" begin - @test isequal_interval(tan(interval(0.5)), interval(0.54630248984379048, 0.5463024898437906)) - @test isequal_interval(tan(interval(0.5, 1.67)), entireinterval()) - @test isequal_interval(tan(interval(1.67, 3.2)), interval(-10.047182299210307, 0.05847385445957865)) - @test isequal_interval(tan(interval(6.638314112824137, 8.38263151220128)), entireinterval()) # https://github.com/JuliaIntervals/IntervalArithmetic.jl/pull/20 - - @test issubset_interval(tan(interval(BigFloat, 0.5, 0.5)), tan(interval(0.5))) - @test isequal_interval(tan(interval(BigFloat, 0.5, 1.67)), entireinterval(BigFloat)) - @test issubset_interval(tan(interval(BigFloat, 0.5, 1.67)), tan(interval(0.5, 1.67))) - @test issubset_interval(tan(interval(BigFloat, 1.67, 3.2)), tan(interval(1.67, 3.2))) - @test issubset_interval(tan(interval(BigFloat, 2.1, 5.6)), tan(interval(2.1, 5.6))) - @test issubset_interval(tan(interval(BigFloat, 0.5, 8.5)), tan(interval(0.5, 8.5))) - @test issubset_interval(tan(interval(BigFloat, -4.5, 0.1)), tan(interval(-4.5, 0.1))) - @test issubset_interval(tan(interval(BigFloat, 1.3, 6.3)), tan(interval(1.3, 6.3))) - -end - -@testset "Inverse trig" begin - @test isequal_interval(asin(interval(1)), interval(π)/interval(2)) - @test isequal_interval(asin(interval(0.9, 2)), asin(interval(0.9, 1))) - @test isequal_interval(asin(interval(3, 4)), emptyinterval()) - - @test issubset_interval(asin(interval(BigFloat, 1, 1)), asin(interval(1))) - @test issubset_interval(asin(interval(BigFloat, 0.9, 2)), asin(interval(0.9, 2))) - @test issubset_interval(asin(interval(BigFloat, 3, 4)), asin(interval(3, 4))) - - @test isequal_interval(acos(interval(1)), interval(0., 0.)) - @test isequal_interval(acos(interval(-2, -0.9)), acos(interval(-1, -0.9))) - @test isequal_interval(acos(interval(3, 4)), emptyinterval()) - - @test issubset_interval(acos(interval(BigFloat, 1, 1)), acos(interval(1))) - @test issubset_interval(acos(interval(BigFloat, -2, -0.9)), acos(interval(-2, -0.9))) - @test issubset_interval(acos(interval(BigFloat, 3, 4)), acos(interval(3, 4))) - - @test isequal_interval(atan(interval(-1,1)), - interval(-interval(sup(interval(Float64, π)))/interval(4), interval(sup(interval(Float64, π)))/interval(4))) - @test isequal_interval(atan(interval(0)), interval(0.0, 0.0)) - @test issubset_interval(atan(interval(BigFloat, -1, 1)), atan(interval(-1, 1))) -end - -@testset "atan" begin - @test isequal_interval(atan(emptyinterval(), entireinterval()), emptyinterval()) - @test isequal_interval(atan(entireinterval(), emptyinterval()), emptyinterval()) - @test isequal_interval(atan(interval(0.0, 1.0), interval(BigFloat, 0.0, 0.0)), interval(BigFloat, π)/interval(2)) - @test isequal_interval(atan(interval(0.0, 1.0), interval(0.0)), interval(π)/interval(2)) - @test isequal_interval(atan(interval(-1.0, -0.1), interval(0.0)), -interval(π)/interval(2)) - @test isequal_interval(atan(interval(-1.0, 1.0), interval(0.0)), interval(-0.5, 0.5) * interval(π)) - @test isequal_interval(atan(interval(0.0), interval(0.1, 1.0)), interval(0.0)) - @test issubset_interval(atan(interval(BigFloat, 0.0, 0.1), interval(BigFloat, 0.1, 1.0)), - atan(interval(0.0, 0.1), interval(0.1, 1.0))) - @test isequal_interval(atan(interval(0.0, 0.1), interval(0.1, 1.0)), - interval(0.0, 0.7853981633974484)) - @test issubset_interval(atan(interval(BigFloat, -0.1, 0.0), interval(BigFloat, 0.1, 1.0)), - atan(interval(-0.1, 0.0), interval(0.1, 1.0))) - @test isequal_interval(atan(interval(-0.1, 0.0), interval(0.1, 1.0)), - interval(-0.7853981633974484, 0.0)) - @test issubset_interval(atan(interval(BigFloat, -0.1, -0.1), interval(BigFloat, 0.1, Inf)), - atan(interval(-0.1, -0.1), interval(0.1, Inf))) - @test isequal_interval(atan(interval(-0.1, 0.0), interval(0.1, Inf)), - interval(-0.7853981633974484, 0.0)) - @test issubset_interval(atan(interval(BigFloat, 0.0, 0.1), interval(BigFloat, -2.0, -0.1)), - atan(interval(0.0, 0.1), interval(-2.0, -0.1))) - @test isequal_interval(atan(interval(0.0, 0.1), interval(-2.0, -0.1)), - interval(2.356194490192345, 3.1415926535897936)) - @test issubset_interval(atan(interval(BigFloat, -0.1, 0.0), interval(BigFloat, -2.0, -0.1)), - atan(interval(-0.1, 0.0), interval(-2.0, -0.1))) - @test isequal_interval(atan(interval(-0.1, 0.0), interval(-2.0, -0.1)), - interval(-1, 1) * interval(π)) - @test issubset_interval(atan(interval(BigFloat, -0.1, 0.1), interval(BigFloat, -Inf, -0.1)), - atan(interval(-0.1, 0.1), interval(-Inf, -0.1))) - @test isequal_interval(atan(interval(-0.1, 0.1), interval(-Inf, -0.1)), - interval(-1, 1) * interval(π)) - - @test issubset_interval(atan(interval(BigFloat, 0.0, 0.0), interval(BigFloat, -2.0, 0.0)), - atan(interval(0.0, 0.0), interval(-2.0, 0.0))) - @test isequal_interval(atan(interval(-0.0, 0.0), interval(-2.0, 0.0)), - interval(3.141592653589793, 3.1415926535897936)) - @test issubset_interval(atan(interval(BigFloat, 0.0, 0.1), interval(BigFloat, -0.1, 0.0)), - atan(interval(0.0, 0.1), interval(-0.1, 0.0))) - @test isequal_interval(atan(interval(-0.0, 0.1), interval(-0.1, 0.0)), - interval(1.5707963267948966, 3.1415926535897936)) - @test issubset_interval(atan(interval(BigFloat, -0.1, -0.1), interval(BigFloat, -0.1, 0.0)), - atan(interval(-0.1, -0.1), interval(-0.1, 0.0))) - @test isequal_interval(atan(interval(-0.1, -0.1), interval(-0.1, 0.0)), - interval(-2.3561944901923453, -1.5707963267948966)) - @test issubset_interval(atan(interval(BigFloat, -0.1, 0.1), interval(BigFloat, -2.0, 0.0)), - atan(interval(-0.1, 0.1), interval(-2.0, 0.0))) - @test isequal_interval(atan(interval(-0.1, 0.1), interval(-2.0, 0.0)), - interval(-1, 1) * interval(π)) - - @test issubset_interval(atan(interval(BigFloat, 0.0, 0.0), interval(BigFloat, -2.0, 0.0)), - atan(interval(0.0, 0.0), interval(-2.0, 0.0))) - @test isequal_interval(atan(interval(-0.0, 0.0), interval(-2.0, 0.0)), - interval(3.141592653589793, 3.1415926535897936)) - @test issubset_interval(atan(interval(BigFloat, 0.0, 0.1), interval(BigFloat, -0.1, 0.0)), - atan(interval(0.0, 0.1), interval(-0.1, 0.0))) - @test isequal_interval(atan(interval(-0.0, 0.1), interval(-0.1, 0.0)), - interval(1.5707963267948966, 3.1415926535897936)) - @test issubset_interval(atan(interval(BigFloat, -0.1, -0.1), interval(BigFloat, -0.1, 0.0)), - atan(interval(-0.1, -0.1), interval(-0.1, 0.0))) - @test isequal_interval(atan(interval(-0.1, -0.1), interval(-0.1, 0.0)), - interval(-2.3561944901923453, -1.5707963267948966)) - @test issubset_interval(atan(interval(BigFloat, -0.1, 0.1), interval(BigFloat, -2.0, 0.0)), - atan(interval(-0.1, 0.1), interval(-2.0, 0.0))) - @test isequal_interval(atan(interval(-0.1, 0.1), interval(-2.0, 0.0)), - interval(-1, 1) * interval(π)) - @test issubset_interval(atan(interval(BigFloat, 0.0, 0.1), interval(BigFloat, -2.0, 0.1)), - atan(interval(0.0, 0.1), interval(-2.0, 0.1))) - @test isequal_interval(atan(interval(-0.0, 0.1), interval(-2.0, 0.1)), - interval(0.0, 3.1415926535897936)) - @test issubset_interval(atan(interval(BigFloat, -0.1, -0.1), interval(BigFloat, -0.1, 0.1)), - atan(interval(-0.1, -0.1), interval(-0.1, 0.1))) - @test isequal_interval(atan(interval(-0.1, -0.1), interval(-0.1, 0.1)), - interval(-2.3561944901923453, Float64(-big(pi)/4, RoundUp))) - @test issubset_interval(atan(interval(BigFloat, -0.1, 0.1), interval(BigFloat, -2.0, 0.1)), - atan(interval(-0.1, 0.1), interval(-2.0, 0.1))) - @test isequal_interval(interval(-1, 1) * interval(π), atan(interval(-0.1, 0.1), interval(-2.0, 0.1))) - - @test isequal_interval(atan(interval(-0.1, 0.1), interval(0.1, 0.1)), - interval(-0.7853981633974484, 0.7853981633974484)) - @test issubset_interval(atan(interval(BigFloat, -0.1, 0.1), interval(BigFloat, 0.1, 0.1)), - atan(interval(-0.1, 0.1), interval(0.1, 0.1))) - @test isequal_interval(atan(interval(0.0), interval(-0.0, 0.1)), interval(0.0)) - @test isequal_interval(atan(interval(0.0, 0.1), interval(-0.0, 0.1)), - interval(0.0, 1.5707963267948968)) - @test isequal_interval(atan(interval(-0.1, 0.0), interval(0.0, 0.1)), - interval(-1.5707963267948968, 0.0)) - @test isequal_interval(atan(interval(-0.1, 0.1), interval(-0.0, 0.1)), - interval(-1.5707963267948968, 1.5707963267948968)) - @test issubset_interval(atan(interval(BigFloat, -0.1, 0.1), interval(BigFloat, -0.0, 0.1)), - atan(interval(-0.1, 0.1), interval(0.0, 0.1))) - - @test isequal_interval(atan(interval(Float32, -0.1, 0.1), interval(Float32, 0.1, 0.1)), - interval(-0.78539824f0, 0.78539824f0)) - @test issubset_interval(atan(interval(-0.1, 0.1), interval(0.1, 0.1)), - atan(interval(Float32, -0.1, 0.1), interval(Float32, 0.1, 0.1))) - @test isequal_interval(atan(interval(Float32, 0.0, 0.0), interval(Float32, -0.0, 0.1)), - interval(Float32, 0.0, 0.0)) - @test isequal_interval(atan(interval(Float32, 0.0, 0.1), interval(Float32, -0.0, 0.1)), - interval(0.0, 1.5707964f0)) - @test isequal_interval(atan(interval(Float32, -0.1, 0.0), interval(Float32, 0.0, 0.1)), - interval(-1.5707964f0, 0.0)) - @test isequal_interval(atan(interval(Float32, -0.1, 0.1), interval(Float32, -0.0, 0.1)), - interval(-1.5707964f0, 1.5707964f0)) - @test issubset_interval(atan(interval(-0.1, 0.1), interval(-0.0, 0.1)), - atan(interval(Float32, -0.1, 0.1), interval(Float32, 0.0, 0.1))) -end - -@testset "Trig" begin - for a in ( interval(17, 19), interval(0.5, 1.2) ) - @test issubset_interval(tan(a), sin(a)/cos(a)) - end - - @test isequal_interval(sin(interval(-pi/2, 3pi/2)), interval(-1, 1)) - @test isequal_interval(cos(interval(-pi/2, 3pi/2)), interval(-1, 1)) -end - -@testset "Trig with large arguments" begin - x = pown(interval(2.), 1000) - @test diam(x) == 0.0 - - @test isequal_interval(sin(x), interval(-0.15920170308624246, -0.15920170308624243)) - @test isequal_interval(cos(x), interval(0.9872460775989135, 0.9872460775989136)) - @test isequal_interval(tan(x), interval(-0.16125837995065806, -0.16125837995065803)) - - x = interval(prevfloat(Inf), Inf) - @test isequal_interval(sin(x), interval(-1, 1)) - @test isequal_interval(cos(x), interval(-1, 1)) - @test isequal_interval(tan(x), interval(-Inf, Inf)) -end diff --git a/test/intervals/arithmetic/absmax.jl b/test/intervals/arithmetic/absmax.jl new file mode 100644 index 000000000..c83b6b546 --- /dev/null +++ b/test/intervals/arithmetic/absmax.jl @@ -0,0 +1,163 @@ +using Test +using IntervalArithmetic + +@testset "abs on BareInterval" begin + x = bareinterval(1.0, 2.0) + @test abs(x) === x + @test isequal_interval(abs(bareinterval(-3.0, -1.0)), bareinterval(1.0, 3.0)) + @test isequal_interval(abs(bareinterval(-2.0, 1.0)), bareinterval(0.0, 2.0)) + @test isequal_interval(abs(bareinterval(-1.0, 2.0)), bareinterval(0.0, 2.0)) + + z = abs(bareinterval(0.0)) + @test isthinzero(z) + @test signbit(inf(z)) + @test bounds(z)[1] === 0.0 + + e = emptyinterval(BareInterval{Float64}) + @test abs(e) === e + @test isequal_interval(abs(entireinterval(BareInterval{Float64})), bareinterval(0.0, Inf)) + @test isequal_interval(abs(bareinterval(-Inf, -1.0)), bareinterval(1.0, Inf)) + @test isequal_interval(abs(bareinterval(1.0, Inf)), bareinterval(1.0, Inf)) + + @test isthin(abs(bareinterval(0.1)), 0.1) + @test diam(abs(bareinterval(-3.7, -1.2))) == diam(bareinterval(-3.7, -1.2)) + + for T ∈ (Float16, Float32, Float64, BigFloat) + @test numtype(abs(bareinterval(T, -2, 1))) === T + end + @test numtype(abs(bareinterval(-1//2, 3//2))) === Rational{Int} + @test isequal_interval(abs(bareinterval(Rational{Int32}(-1//2), Rational{Int32}(3//2))), bareinterval(Rational{Int32}(0//1), Rational{Int32}(3//2))) +end + +@testset "abs on Interval" begin + r = abs(interval(-2.0, 1.0)) + @test isequal_interval(r, interval(0.0, 2.0)) + @test decoration(r) == com + + @test decoration(abs(interval(-Inf, 1.0))) == dac + @test decoration(abs(emptyinterval(Interval{Float64}))) == trv + @test decoration(abs(interval(1.0, 2.0, def))) == def + + @test isguaranteed(abs(interval(1.0))) + @test !isguaranteed(abs(convert(Interval{Float64}, 1))) + + r = @test_logs (:warn,) abs(nai(Float64)) + @test isnai(r) + @test decoration(r) == ill + + @test isequal_interval(abs(entireinterval()), interval(0.0, Inf)) + @test isequal_interval(abs(emptyinterval()), emptyinterval()) + @test isequal_interval(abs(interval(-3.0, 1.0)), interval(0.0, 3.0)) + @test isequal_interval(abs(interval(-3.0, -1.0)), interval(1.0, 3.0)) + @test isequal_interval(abs(interval(0.1, 0.2)), interval(0.1, 0.2)) + @test isequal_interval(abs(interval(-1, 2)), interval(0, 2)) +end + +@testset "abs on Complex" begin + @test isequal_interval(abs(complex(interval(3.0), interval(4.0))), interval(5.0)) + @test isequal_interval(abs(complex(interval(3.0), interval(4.0))), hypot(interval(3.0), interval(4.0))) + @test isthinzero(abs(complex(interval(0.0), interval(0.0)))) + @test isempty_interval(abs(complex(emptyinterval(), interval(1.0)))) + + x = complex(interval(0, 3), interval(0, 4)) + @test isequal_interval(abs(x), interval(0, 5)) + y = complex(interval(-1, 1), interval(-2, 2)) + @test inf(abs(y)) == 0 +end + +@testset "abs2" begin + @test isequal_interval(abs2(bareinterval(-2.0, 1.0)), bareinterval(0.0, 4.0)) + @test isequal_interval(abs2(bareinterval(2.0, 3.0)), bareinterval(4.0, 9.0)) + @test isempty_interval(abs2(emptyinterval(BareInterval{Float64}))) + @test isequal_interval(abs2(entireinterval(BareInterval{Float64})), bareinterval(0.0, Inf)) + + x = bareinterval(-2.0, 3.0) + @test isequal_interval(abs2(x), IntervalArithmetic._select_pown(x, 2)) + @test isequal_interval(IntervalArithmetic._select_pown(IntervalArithmetic.PowerMode{:slow}(), x, 2), pown(x, 2)) + @test isequal_interval(IntervalArithmetic._select_pown(IntervalArithmetic.PowerMode{:fast}(), x, 2), fastpown(x, 2)) + + @test decoration(abs2(interval(-Inf, 1.0))) == dac + @test decoration(abs2(interval(-2.0, 1.0))) == com + @test decoration(abs2(interval(1.0, 2.0, def))) == def + @test isguaranteed(abs2(interval(-2.0, 1.0))) + @test !isguaranteed(abs2(convert(Interval{Float64}, 2))) + + r = @test_logs (:warn,) abs2(nai(Float64)) + @test isnai(r) + + @test isequal_interval(abs2(interval(-3.0, 1.0)), interval(0.0, 9.0)) + @test isequal_interval(abs2(interval(-3.0, -1.0)), interval(1.0, 9.0)) + + @test isequal_interval(abs2(complex(interval(1, 2), interval(3, 4))), interval(10, 20)) + x = complex(interval(0, 3), interval(0, 4)) + @test isequal_interval(abs2(x), interval(0, 25)) + y = complex(interval(-1, 1), interval(-2, 2)) + @test inf(abs2(y)) == 0 +end + +@testset "min and max on BareInterval" begin + x = bareinterval(1.0, 3.0) + y = bareinterval(2.0, 4.0) + @test isequal_interval(min(x, y), bareinterval(1.0, 3.0)) + @test isequal_interval(max(x, y), bareinterval(2.0, 4.0)) + @test isequal_interval(min(x, y), min(y, x)) + @test isequal_interval(max(x, y), max(y, x)) + @test isequal_interval(min(x, x), x) + @test isequal_interval(max(x, x), x) + + @test isequal_interval(min(bareinterval(1//2, 3//2), bareinterval(1//3, 2//1)), bareinterval(1//3, 3//2)) + @test numtype(min(bareinterval(1//2, 3//2), bareinterval(1//3, 2//1))) === Rational{Int} + + e = emptyinterval(BareInterval{Float64}) + @test min(x, e) === e + @test max(e, x) === e + @test typeof(min(bareinterval(1.0, 2.0), emptyinterval(BareInterval{Float32}))) === BareInterval{Float64} + @test isempty_interval(min(bareinterval(1.0, 2.0), emptyinterval(BareInterval{Float32}))) + + @test typeof(min(bareinterval(1.0f0, 3.0f0), bareinterval(2.0, 4.0))) === BareInterval{Float64} + @test typeof(max(bareinterval(1.0f0, 3.0f0), bareinterval(2.0, 4.0))) === BareInterval{Float64} + + entire = entireinterval(BareInterval{Float64}) + @test isequal_interval(min(entire, bareinterval(1.0, 2.0)), bareinterval(-Inf, 2.0)) + @test isequal_interval(max(entire, bareinterval(1.0, 2.0)), bareinterval(1.0, Inf)) + + @test_throws MethodError min(bareinterval(1.0), 1.0) + @test_throws MethodError max(bareinterval(1.0), 1.0) +end + +@testset "min and max on Interval" begin + @test decoration(min(interval(1.0, 3.0, dac), interval(2.0, 4.0))) == dac + @test decoration(max(interval(1.0, 3.0, dac), interval(2.0, 4.0))) == dac + @test !isguaranteed(min(interval(1.0), convert(Interval{Float64}, 2))) + @test isguaranteed(min(interval(1.0), interval(2.0))) + + r = @test_logs (:warn,) min(interval(1.0), nai(Float64)) + @test isnai(r) + r = @test_logs (:warn,) max(nai(Float64), interval(1.0)) + @test isnai(r) + + @test isequal_interval(min(interval(1.0), 2.0), interval(1.0)) + @test !isguaranteed(min(interval(1.0), 2.0)) + @test isequal_interval(max(interval(1.0), 2.0), interval(2.0)) + @test !isguaranteed(max(interval(1.0), 2.0)) + + @test typeof(min(interval(Float32, 1), interval(1.0, 2.0))) === Interval{Float64} + + @test isequal_interval(min(entireinterval(), interval(3.0, 4.0)), interval(-Inf, 4.0)) + @test isequal_interval(min(emptyinterval(), interval(3.0, 4.0)), emptyinterval()) + @test isequal_interval(min(interval(-3.0, 1.0), interval(3.0, 4.0)), interval(-3.0, 1.0)) + @test isequal_interval(min(interval(-3.0, -1.0), interval(3.0, 4.0)), interval(-3.0, -1.0)) + @test isequal_interval(max(entireinterval(), interval(3.0, 4.0)), interval(3.0, Inf)) + @test isequal_interval(max(emptyinterval(), interval(3.0, 4.0)), emptyinterval()) + @test isequal_interval(max(interval(-3.0, 1.0), interval(3.0, 4.0)), interval(3.0, 4.0)) + @test isequal_interval(max(interval(-3.0, -1.0), interval(3.0, 4.0)), interval(3.0, 4.0)) +end + +@testset "docstrings" begin + meta = Base.Docs.meta(IntervalArithmetic) + for name ∈ (:abs, :abs2, :min, :max) + b = Base.Docs.Binding(Base, name) + @test haskey(meta, b) + @test any(!isempty(string(ds.text...)) for (_, ds) ∈ meta[b].docs) + end +end diff --git a/test/intervals/arithmetic/basic.jl b/test/intervals/arithmetic/basic.jl new file mode 100644 index 000000000..03d6db843 --- /dev/null +++ b/test/intervals/arithmetic/basic.jl @@ -0,0 +1,438 @@ +using Test +using IntervalArithmetic + +@testset "unary plus and minus" begin + for x ∈ (bareinterval(1.0, 2.0), emptyinterval(BareInterval{Float64}), entireinterval(BareInterval{Float64})) + @test +x === x + end + for x ∈ (interval(1.0, 2.0), emptyinterval(), entireinterval()) + @test +x === x + end + + @test isequal_interval(-bareinterval(0.0, 1.0), bareinterval(-1.0, 0.0)) + @test isempty_interval(-emptyinterval(BareInterval{Float64})) + @test isentire_interval(-entireinterval(BareInterval{Float64})) + + r = -interval(1.0, 2.0, def) + @test isequal_interval(r, interval(-2.0, -1.0)) + @test decoration(r) == def + @test isguaranteed(-interval(1.0)) + @test !isguaranteed(-convert(Interval{Float64}, 1)) + + a = interval(0.1, 1.1) + @test isequal_interval(+a, a) + @test isequal_interval(-a, interval(-sup(a), -inf(a))) +end + +@testset "addition" begin + @test bounds(bareinterval(0.1) + bareinterval(0.2)) == (0.3, 0.30000000000000004) + @test sup(bareinterval(0.1) + bareinterval(0.2)) == 0.1 + 0.2 + @test isempty_interval(emptyinterval(BareInterval{Float64}) + bareinterval(1.0)) + @test isempty_interval(bareinterval(1.0) + emptyinterval(BareInterval{Float64})) + @test isentire_interval(entireinterval(BareInterval{Float64}) + bareinterval(1.0)) + @test isequal_interval(bareinterval(1.0, Inf) + bareinterval(1.0, 2.0), bareinterval(2.0, Inf)) + @test typeof(bareinterval(1.0f0) + bareinterval(1.0)) === BareInterval{Float64} + @test isequal_interval(bareinterval(1//2) + bareinterval(1//3), bareinterval(5//6)) + @test numtype(bareinterval(1//2) + bareinterval(1//3)) === Rational{Int} + + setprecision(BigFloat, 128) do + x = bareinterval(BigFloat, 1) / bareinterval(BigFloat, 3) + y = x + x + @test precision(inf(y)) == 128 + @test inf(y) < 2//3 < sup(y) + @test sup(y) == nextfloat(inf(y)) + end + + @test decoration(interval(1.0, Inf) + interval(1.0)) == dac + @test decoration(interval(1.0, 2.0, def) + interval(1.0, 2.0)) == def + @test isguaranteed(interval(1.0) + interval(2.0)) + @test !isguaranteed(interval(1.0) + 1) + + r = @test_logs (:warn,) interval(1.0) + nai(Float64) + @test isnai(r) + r = interval(1.0) + emptyinterval() + @test isempty_interval(r) + @test decoration(r) == trv + + a = interval(0.1, 1.1) + b = interval(0.9, 2.0) + @test inf(zero(a) + one(b)) == 1 + @test sup(zero(a) + one(b)) == 1 + @test isequal_interval(interval(0, 1) + emptyinterval(a), emptyinterval(a)) + @test isequal_interval(interval(Rational{Int}, 1//4, 1//2) + interval(Rational{Int}, 2//3), interval(Rational{Int}, 11//12, 7//6)) +end + +@testset "subtraction" begin + @test isequal_interval(bareinterval(1.0, 2.0) - bareinterval(1.0, 2.0), bareinterval(-1.0, 1.0)) + @test isempty_interval(bareinterval(1.0) - emptyinterval(BareInterval{Float64})) + @test isempty_interval(emptyinterval(BareInterval{Float64}) - bareinterval(1.0)) + @test typeof(bareinterval(1.0f0) - bareinterval(1.0)) === BareInterval{Float64} + @test isequal_interval(bareinterval(1//2) - bareinterval(1//3), bareinterval(1//6)) + + @test decoration(interval(1.0, 2.0, def) - interval(1.0)) == def + @test decoration(interval(1.0, Inf) - interval(1.0)) == dac + @test !isguaranteed(interval(1.0) - convert(Interval{Float64}, 1)) + + a = interval(0.1, 1.1) + c = interval(0.25, 4.0) + @test isequal_interval(interval(0.25) - one(c) / interval(4), zero(c)) + @test isequal_interval(emptyinterval(a) - interval(0, 1), emptyinterval(a)) + @test isequal_interval(interval(0, 1) - emptyinterval(a), emptyinterval(a)) + @test isequal_interval(interval(Rational{Int}, 1//4, 1//2) - interval(Rational{Int}, 2//3), interval(Rational{Int}, -5//12, -1//6)) + @test isequal_interval(interval(1//3, 1//3) - interval(1//1), interval(-2//3, -2//3)) +end + +@testset "multiplication" begin + p = bareinterval(1.0, 2.0) + n = bareinterval(-2.0, -1.0) + m = bareinterval(-1.0, 2.0) + @test isequal_interval(p * p, bareinterval(1.0, 4.0)) + @test isequal_interval(p * n, bareinterval(-4.0, -1.0)) + @test isequal_interval(p * m, bareinterval(-2.0, 4.0)) + @test isequal_interval(n * p, bareinterval(-4.0, -1.0)) + @test isequal_interval(n * n, bareinterval(1.0, 4.0)) + @test isequal_interval(n * m, bareinterval(-4.0, 2.0)) + @test isequal_interval(m * p, bareinterval(-2.0, 4.0)) + @test isequal_interval(m * n, bareinterval(-4.0, 2.0)) + @test isequal_interval(m * m, bareinterval(-2.0, 4.0)) + + @test isequal_interval(bareinterval(0.1) * bareinterval(0.1), bareinterval(0.01, 0.010000000000000002)) + + @test isthinzero(bareinterval(0.0) * entireinterval(BareInterval{Float64})) + @test isthinzero(entireinterval(BareInterval{Float64}) * bareinterval(0.0)) + + @test isequal_interval(bareinterval(0.0, 1.0) * bareinterval(1.0, Inf), bareinterval(0.0, Inf)) + @test isequal_interval(bareinterval(-Inf, 0.0) * bareinterval(0.0, Inf), bareinterval(-Inf, 0.0)) + @test isequal_interval(bareinterval(-Inf, -1.0) * bareinterval(0.0, 1.0), bareinterval(-Inf, 0.0)) + @test isentire_interval(entireinterval(BareInterval{Float64}) * entireinterval(BareInterval{Float64})) + @test isequal_interval(bareinterval(0.0, Inf) * bareinterval(0.0, Inf), bareinterval(0.0, Inf)) + + @test isempty_interval(emptyinterval(BareInterval{Float64}) * bareinterval(0.0)) + @test isempty_interval(bareinterval(0.0) * emptyinterval(BareInterval{Float64})) + + @test IntervalArithmetic._unbounded_mul(0.0, Inf, RoundDown) == 0.0 + @test IntervalArithmetic._unbounded_mul(Inf, 0.0, RoundUp) == 0.0 + @test IntervalArithmetic._unbounded_mul(0.0, -Inf, RoundUp) === -0.0 + @test IntervalArithmetic._unbounded_mul(-2.0, Inf, RoundDown) == -Inf + + for x ∈ (p, n, m), y ∈ (p, n, m) + @test isequal_interval(IntervalArithmetic._mult(*, x, y), IntervalArithmetic._mult(IntervalArithmetic._unbounded_mul, x, y)) + end + + @test typeof(bareinterval(1.0f0) * bareinterval(1.0)) === BareInterval{Float64} + @test isequal_interval(bareinterval(1//2) * bareinterval(1//3), bareinterval(1//6)) + + @test decoration(interval(1.0, 2.0) * interval(1.0, Inf)) == dac + @test decoration(interval(1.0, 2.0, def) * interval(1.0)) == def + @test !isguaranteed(interval(1.0) * convert(Interval{Float64}, 1)) + + a = interval(0.1, 1.1) + @test isequal_interval(interval(0, 1) * emptyinterval(a), emptyinterval(a)) + @test isequal_interval(a * interval(0), zero(a)) + @test isequal_interval(interval(0, Inf) * interval(-1, Inf), entireinterval()) + + fa = interval(Float32, 1e38) + fb = interval(Float32, 1e2) + @test isequal_interval(fa * fb, interval(Float32, floatmax(Float32), Inf)) +end + +@testset "mixed numtype promotion" begin + for f ∈ (+, -, *, /) + @test isequal_interval(f(interval(Float64, π), interval(Float32, π)), f(interval(Float64, π), Interval{Float64}(interval(Float32, π)))) + end +end + +@testset "arithmetic with thin intervals" begin + x = interval(1, 2) + @test isequal_interval(interval(0.1) + x, interval(1.0999999999999999, 2.1)) + @test isequal_interval(interval(3.0) - x, x) + @test isequal_interval(interval(3.1) - x, interval(1.1, 2.1)) + @test isequal_interval(interval(0.1) * interval(1), interval(0.1, 0.1)) + @test isequal_interval(interval(0.0) * interval(1), interval(0.0, 0.0)) + @test isequal_interval(interval(1) / interval(10.0), interval(0.09999999999999999, 0.1)) + + @test isequal_interval(interval(1) * interval(π), interval(π)) + @test isequal_interval(interval(π) * interval(1), interval(π)) + @test isequal_interval(interval(π) + interval(0), interval(π)) + @test isequal_interval(interval(0) + interval(π), interval(π)) + @test isequal_interval(interval(π) - interval(0), interval(π)) + @test isequal_interval(interval(0) - interval(π), -interval(π)) +end + +@testset "inv" begin + @test isequal_interval(inv(bareinterval(3.0)), bareinterval(0.3333333333333333, 0.33333333333333337)) + @test isequal_interval(inv(bareinterval(-1.0, 0.0)), bareinterval(-Inf, -1.0)) + @test isequal_interval(inv(bareinterval(0.0, 1.0)), bareinterval(1.0, Inf)) + @test isentire_interval(inv(bareinterval(-1.0, 1.0))) + @test isempty_interval(inv(bareinterval(0.0))) + @test isempty_interval(inv(emptyinterval(BareInterval{Float64}))) + @test isentire_interval(inv(entireinterval(BareInterval{Float64}))) + @test isequal_interval(inv(bareinterval(1.0, Inf)), bareinterval(0.0, 1.0)) + + @test isequal_interval(inv(bareinterval(1//2)), bareinterval(2//1)) + @test isentire_interval(inv(bareinterval(-1//2, 1//2))) + @test numtype(inv(bareinterval(-1//2, 1//2))) === Rational{Int} + + @test decoration(inv(interval(0.0, 1.0))) == trv + @test decoration(inv(interval(-1.0, 1.0))) == trv + @test decoration(inv(interval(1.0, Inf))) == dac + @test decoration(inv(interval(1.0, 2.0))) == com + @test isguaranteed(inv(interval(1.0, 2.0))) + @test !isguaranteed(inv(convert(Interval{Float64}, 2))) + + @test isequal_interval(inv(complex(interval(1.0), interval(0.0))), complex(interval(1.0), interval(0.0))) + @test isequal_interval(inv(complex(interval(0.0), interval(1.0))), complex(interval(0.0), interval(-1.0))) + + a = interval(0.1, 1.1) + b = interval(0.9, 2.0) + c = interval(0.25, 4.0) + @test isequal_interval(inv(zero(a)), emptyinterval()) + @test isequal_interval(inv(interval(0, 1)), interval(1, Inf)) + @test isequal_interval(inv(interval(1, Inf)), interval(0, 1)) + @test isequal_interval(inv(c), c) + @test isequal_interval(one(b) / b, inv(b)) + @test isequal_interval(inv(interval(-4.0, 0.0)), interval(-Inf, -0.25)) + @test isequal_interval(inv(interval(0.0, 4.0)), interval(0.25, Inf)) + @test isequal_interval(inv(interval(-4.0, 4.0)), entireinterval(Float64)) + + @test isequal_interval(inv(interval(2, 3)), interval(0.3333333333333333, 0.5)) + @test isequal_interval(inv(big(interval(2, 3))), interval(big"3.333333333333333333333333333333333333333333333333333333333333333333333333333305e-01", big"5.0e-01")) +end + +@testset "division" begin + @test isequal_interval(bareinterval(1.0, 2.0) / bareinterval(1.0, 2.0), bareinterval(0.5, 2.0)) + @test isequal_interval(bareinterval(-2.0, -1.0) / bareinterval(1.0, 2.0), bareinterval(-2.0, -0.5)) + @test isequal_interval(bareinterval(-1.0, 2.0) / bareinterval(1.0, 2.0), bareinterval(-1.0, 2.0)) + @test isequal_interval(bareinterval(1.0, 2.0) / bareinterval(-2.0, -1.0), bareinterval(-2.0, -0.5)) + @test isequal_interval(bareinterval(-2.0, -1.0) / bareinterval(-2.0, -1.0), bareinterval(0.5, 2.0)) + @test isequal_interval(bareinterval(-1.0, 2.0) / bareinterval(-2.0, -1.0), bareinterval(-2.0, 1.0)) + + @test isempty_interval(bareinterval(1.0, 2.0) / bareinterval(0.0)) + @test isempty_interval(bareinterval(0.0) / bareinterval(0.0)) + @test isthinzero(bareinterval(0.0) / bareinterval(-1.0, 1.0)) + + @test isequal_interval(bareinterval(1.0, 2.0) / bareinterval(0.0, 1.0), bareinterval(1.0, Inf)) + @test isequal_interval(bareinterval(-2.0, -1.0) / bareinterval(0.0, 1.0), bareinterval(-Inf, -1.0)) + @test isentire_interval(bareinterval(-1.0, 1.0) / bareinterval(0.0, 1.0)) + @test isequal_interval(bareinterval(1.0, 2.0) / bareinterval(-1.0, 0.0), bareinterval(-Inf, -1.0)) + @test isequal_interval(bareinterval(-2.0, -1.0) / bareinterval(-1.0, 0.0), bareinterval(1.0, Inf)) + @test isentire_interval(bareinterval(-1.0, 1.0) / bareinterval(-1.0, 0.0)) + @test isentire_interval(bareinterval(1.0, 2.0) / bareinterval(-1.0, 1.0)) + + @test isempty_interval(emptyinterval(BareInterval{Float64}) / bareinterval(1.0)) + @test isempty_interval(bareinterval(1.0) / emptyinterval(BareInterval{Float64})) + @test typeof(bareinterval(1.0f0) / bareinterval(1.0)) === BareInterval{Float64} + @test isequal_interval(bareinterval(1//2) / bareinterval(1//3), bareinterval(3//2)) + @test isempty_interval(bareinterval(1//1) / bareinterval(0//1)) + + @test decoration(interval(1.0, 2.0) / interval(1.0, 2.0)) == com + @test decoration(interval(1.0, 2.0) / interval(0.0, 1.0)) == trv + @test decoration(interval(1.0, 2.0) / interval(-1.0, 1.0)) == trv + @test decoration(interval(1.0, 2.0, def) / interval(1.0)) == def + @test !isguaranteed(interval(1.0) / convert(Interval{Float64}, 2)) + + a = interval(0.1, 1.1) + c = interval(0.25, 4.0) + @test isequal_interval(a / emptyinterval(a), emptyinterval(a)) + @test isequal_interval(emptyinterval(a) / a, emptyinterval(a)) + @test isequal_interval(interval(0) / interval(0), emptyinterval()) + @test isequal_interval(interval(-30.0, -15.0) / interval(-5.0, -3.0), interval(3.0, 10.0)) + @test isequal_interval(interval(-30, -15) / interval(-5, -3), interval(3.0, 10.0)) + @test isequal_interval(a / c, interval(0.025, 4.4)) + @test isequal_interval(c / interval(4.0), interval(6.25e-02, 1e+00)) + @test isequal_interval(c / zero(c), emptyinterval(c)) + @test isequal_interval(interval(0.0, 1.0) / interval(0.0, 1.0), interval(0.0, Inf)) + @test isequal_interval(interval(-1.0, 1.0) / interval(0.0, 1.0), entireinterval(c)) + @test isequal_interval(interval(-1.0, 1.0) / interval(-1.0, 1.0), entireinterval(c)) + @test issubset_interval(interval(1//9), interval(1) / interval(9)) + + @test isequal_interval(bareinterval(2.0) \ bareinterval(1.0), bareinterval(0.5)) + @test isequal_interval(interval(2.0) \ interval(1.0), interval(0.5)) +end + +@testset "muladd and fma" begin + @test isequal_interval(muladd(bareinterval(2.0), bareinterval(3.0), bareinterval(1.0)), bareinterval(7.0)) + @test isequal_interval(fma(bareinterval(0.1), bareinterval(0.1), bareinterval(0.1)), bareinterval(0.11, 0.11000000000000001)) + for x ∈ (bareinterval(0.1), bareinterval(-2.0, 3.0)), y ∈ (bareinterval(0.1), bareinterval(-1.0, 1.0)), z ∈ (bareinterval(0.1),) + @test isequal_interval(fma(x, y, z), muladd(x, y, z)) + @test isequal_interval(fma(x, y, z), x * y + z) + end + @test typeof(muladd(bareinterval(1.0f0), bareinterval(2.0), bareinterval(3.0))) === BareInterval{Float64} + @test typeof(fma(bareinterval(1.0f0), bareinterval(2.0), bareinterval(3.0))) === BareInterval{Float64} + + @test decoration(muladd(interval(1.0, 2.0), interval(1.0, Inf), interval(0.0))) == dac + @test decoration(fma(interval(1.0, 2.0, def), interval(1.0), interval(0.0))) == def + @test !isguaranteed(muladd(interval(1.0), interval(2.0), convert(Interval{Float64}, 3))) + @test isempty_interval(muladd(emptyinterval(), interval(1.0), interval(2.0))) + + r = @test_logs (:warn,) fma(nai(Float64), interval(1.0), interval(2.0)) + @test isnai(r) + + a = interval(0.1, 1.1) + b = interval(0.9, 2.0) + c = interval(0.25, 4.0) + @test isequal_interval(fma(emptyinterval(), a, b), emptyinterval()) + @test isequal_interval(fma(entireinterval(), zero(a), b), b) + @test isequal_interval(fma(entireinterval(), one(a), b), entireinterval()) + @test isequal_interval(fma(zero(a), entireinterval(), b), b) + @test isequal_interval(fma(one(a), entireinterval(), b), entireinterval()) + @test isequal_interval(fma(a, zero(a), c), c) + @test isequal_interval(fma(interval(Rational{Int}, 1//2, 1//2), interval(Rational{Int}, 1//3, 1//3), interval(Rational{Int}, 1//12, 1//12)), interval(Rational{Int}, 3//12, 3//12)) + + result = interval(1.1) * interval(2) + interval(3) + @test isequal_interval(muladd(interval(1.1), interval(2), interval(3)), result) + @test isequal_interval(muladd(interval(1.1), interval(Float32, 2), interval(3)), result) +end + +@testset "sqrt" begin + @test isequal_interval(sqrt(bareinterval(2.0)), bareinterval(1.414213562373095, 1.4142135623730951)) + @test isthin(sqrt(bareinterval(4.0)), 2.0) + @test isequal_interval(sqrt(bareinterval(-1.0, 4.0)), bareinterval(0.0, 2.0)) + @test isempty_interval(sqrt(bareinterval(-4.0, -1.0))) + @test isempty_interval(sqrt(emptyinterval(BareInterval{Float64}))) + @test isequal_interval(sqrt(bareinterval(0.0, Inf)), bareinterval(0.0, Inf)) + + @test IntervalArithmetic._cut_negative_domain(bareinterval(-1.0, 4.0)) == 0.0 + @test IntervalArithmetic._cut_negative_domain(bareinterval(1.0, 4.0)) == 1.0 + @test IntervalArithmetic._cut_negative_domain(bareinterval(BigFloat, -1, 4)) == 0 + @test IntervalArithmetic._cut_negative_domain(bareinterval(BigFloat, 1, 4)) == 1 + + @test isequal_interval(sqrt(bareinterval(1//4)), bareinterval(0.5)) + @test numtype(sqrt(bareinterval(1//4))) === Float64 + + @test decoration(sqrt(interval(0.0, 4.0))) == com + @test decoration(sqrt(interval(-1.0, 4.0))) == trv + @test decoration(sqrt(interval(0.0, Inf))) == dac + r = sqrt(interval(-4.0, -1.0)) + @test isempty_interval(r) + @test decoration(r) == trv + @test isguaranteed(sqrt(interval(2.0))) + @test !isguaranteed(sqrt(convert(Interval{Float64}, 2))) + r = @test_logs (:warn,) sqrt(nai(Float64)) + @test isnai(r) + + @test isequal_interval(sqrt(bareinterval(Float32, 2)), bareinterval(1.4142135f0, 1.4142137f0)) + @test numtype(sqrt(bareinterval(Float16, 2))) === Float16 + @test in_interval(sqrt(big(2)), sqrt(bareinterval(Float16, 2))) + @test isequal_interval(sqrt(interval(2, 3)), interval(1.414213562373095, 1.7320508075688774)) + @test isequal_interval(sqrt(big(interval(2, 3))), interval(big"1.414213562373095048801688724209698078569671875376948073176679737990732478462102", big"1.732050807568877293527446341505872366942805253810380628055806979451933016908815")) + + r = sqrt(complex(interval(4.0), interval(0.0))) + @test in_interval(2, real(r)) + @test in_interval(0, imag(r)) + r = sqrt(complex(interval(-1.0), interval(0.0))) + @test in_interval(0, real(r)) + @test in_interval(1, imag(r)) + + ra = interval(Rational{Int64}, 1//2, 3//4) + rb = interval(Rational{Int64}, 3//7, 9//12) + @test issubset_interval(sqrt(ra + rb), interval(Int64(137482504)//142672337, Int64(46099201)//37639840)) + @test issubset_interval(sqrt(interval(1//3)), interval(Int64(29354524)//50843527, Int64(50843527)//88063572)) + + r = sqrt(interval(1, 2, com)) + @test isequal_interval(r, sqrt(interval(1, 2))) + @test decoration(r) == com + r = sqrt(interval(-1, 1, com)) + @test isequal_interval(r, sqrt(interval(0, 1))) + @test decoration(r) == trv +end + +@testset "no mixing of BareInterval and Number" begin + @test_throws MethodError bareinterval(1.0) + 1 + @test_throws MethodError 1 + bareinterval(1.0) + @test_throws MethodError bareinterval(1.0) + interval(1.0) + @test_throws MethodError bareinterval(1.0) * 2 + @test_throws MethodError bareinterval(1.0) / 2 +end + +@testset "complex arithmetic" begin + a = interval(1im) + @test typeof(a) == Complex{Interval{Float64}} + @test isequal_interval(a, complex(interval(0), interval(1))) + @test isequal_interval(a, interval(0) + interval(1) * interval(im)) + @test isequal_interval(a * a, interval(-1)) + @test isequal_interval(a + a, interval(2) * interval(im)) + @test isthinzero(a - a) + @test isthinone(a / a) +end + +@testset "enclosure of point operations" begin + xs = (bareinterval(0.1, 1.1), bareinterval(-2.7, -0.3), bareinterval(-1.5, 2.5)) + ys = (bareinterval(0.4, 2.3), bareinterval(-3.1, -1.2)) + for x ∈ xs, y ∈ ys + for s ∈ range(inf(x), sup(x); length = 5), t ∈ range(inf(y), sup(y); length = 5) + @test in_interval(s + t, x + y) + @test in_interval(s - t, x - y) + @test in_interval(s * t, x * y) + @test in_interval(s / t, x / y) + end + end +end + +function calc_pi1(N) + S = interval(0) + for i ∈ 1:N + S += inv(interval(i)^2) + end + S += interval(inv(interval(N+1)), inv(interval(N))) + return sqrt(interval(6) * S) +end + +function calc_pi2(N) + S = interval(0) + for i ∈ 1:N + S += interval(1 / i^2) + end + S += interval(inv(interval(N+1)), inv(interval(N))) + return sqrt(interval(6) * S) +end + +function calc_pi3(N) + S = interval(0) + for i ∈ 1:N + S += interval(1 / i^2) + end + S += parse(Interval{Float64}, "[1/$(N+1), 1/$N]") + return sqrt(interval(6) * S) +end + +function calc_pi4(N) + S = interval(0) + II = interval(1) + for i ∈ N:-1:1 + S += II / interval(i^2) + end + S += II / interval(N, N+1) + return sqrt(interval(6) * S) +end + +function calc_pi5(N) + S = interval(0) + for i ∈ N:-1:1 + S += interval(1 // i^2) + end + S += inv(interval(N, N+1)) + return sqrt(interval(6) * S) +end + +@testset "pi computations" begin + big_pi = setprecision(256) do + big(π) + end + + N = 10000 + pi1 = calc_pi1(N) + pi2 = calc_pi2(N) + pi3 = calc_pi3(N) + pi4 = calc_pi4(N) + pi5 = calc_pi5(N) + + @test in_interval(big_pi, pi1) + @test in_interval(big_pi, pi2) + @test in_interval(big_pi, pi3) + @test in_interval(big_pi, pi4) + @test in_interval(big_pi, pi5) + + @test isequal_interval(pi1, pi2) + @test isequal_interval(pi2, pi3) +end diff --git a/test/intervals/arithmetic/hyperbolic.jl b/test/intervals/arithmetic/hyperbolic.jl new file mode 100644 index 000000000..e6efbdc89 --- /dev/null +++ b/test/intervals/arithmetic/hyperbolic.jl @@ -0,0 +1,238 @@ +using Test +using IntervalArithmetic + +@testset "sinh" begin + @test isequal_interval(sinh(bareinterval(0.0, 1.0)), bareinterval(0.0, 1.1752011936438016)) + @test isempty_interval(sinh(emptyinterval(BareInterval{Float64}))) + @test isentire_interval(sinh(entireinterval(BareInterval{Float64}))) + r = sinh(bareinterval(1.0)) + @test inf(r) < sup(r) + @test in_interval(sinh(big(1)), r) + @test numtype(sinh(bareinterval(1//1))) === Float64 + + @test decoration(sinh(interval(-Inf, Inf))) == dac + @test decoration(sinh(interval(1.0, 2.0, def))) == def + @test isguaranteed(sinh(interval(1.0))) + @test !isguaranteed(sinh(convert(Interval{Float64}, 1))) + r = @test_logs (:warn,) sinh(nai(Float64)) + @test isnai(r) + + @test isequal_interval(sinh(emptyinterval()), emptyinterval()) + @test isequal_interval(sinh(interval(0.5)), interval(0.5210953054937473, 0.5210953054937474)) + @test isequal_interval(sinh(interval(0.5, 1.67)), interval(0.5210953054937473, 2.5619603657712102)) + @test isequal_interval(sinh(interval(-4.5, 0.1)), interval(-45.00301115199179, 0.10016675001984404)) + + for lo_hi ∈ ((0.5, 0.5), (0.5, 1.67), (1.67, 3.2), (2.1, 5.6), (0.5, 8.5), (-4.5, 0.1), (1.3, 6.3)) + @test issubset_interval(sinh(interval(BigFloat, lo_hi...)), sinh(interval(lo_hi...))) + end + + @test isthinzero(sinh(complex(interval(0.0), interval(0.0)))) + z = complex(interval(0.3), interval(0.4)) + @test isequal_interval(sinh(z), (exp(z) - exp(-z)) / interval(Float64, 2)) +end + +@testset "cosh" begin + @test isequal_interval(cosh(bareinterval(-1.0, 2.0)), bareinterval(1.0, 3.762195691083632)) + @test isequal_interval(cosh(bareinterval(1.0, 2.0)), bareinterval(1.5430806348152437, 3.762195691083632)) + @test isempty_interval(cosh(emptyinterval(BareInterval{Float64}))) + @test isequal_interval(cosh(entireinterval(BareInterval{Float64})), bareinterval(1.0, Inf)) + @test numtype(cosh(bareinterval(1//1))) === Float64 + + @test decoration(cosh(interval(-1.0, 2.0))) == com + @test decoration(cosh(interval(1.0, 2.0, def))) == def + @test isguaranteed(cosh(interval(1.0))) + @test !isguaranteed(cosh(convert(Interval{Float64}, 1))) + + @test isequal_interval(cosh(emptyinterval()), emptyinterval()) + @test isequal_interval(cosh(interval(0.5)), interval(1.1276259652063807, 1.127625965206381)) + @test isequal_interval(cosh(interval(0.5, 1.67)), interval(1.1276259652063807, 2.750207431409957)) + @test isequal_interval(cosh(interval(-4.5, 0.1)), interval(1.0, 45.01412014853003)) + + for lo_hi ∈ ((0.5, 0.5), (0.5, 1.67), (1.67, 3.2), (2.1, 5.6), (0.5, 8.5), (-4.5, 0.1), (1.3, 6.3)) + @test issubset_interval(cosh(interval(BigFloat, lo_hi...)), cosh(interval(lo_hi...))) + end + + z = complex(interval(0.3), interval(0.4)) + @test isequal_interval(cosh(z), (exp(z) + exp(-z)) / interval(Float64, 2)) +end + +@testset "tanh" begin + @test isequal_interval(tanh(bareinterval(0.0, 1.0)), bareinterval(0.0, 0.761594155955765)) + @test isempty_interval(tanh(emptyinterval(BareInterval{Float64}))) + @test isequal_interval(tanh(entireinterval(BareInterval{Float64})), bareinterval(-1.0, 1.0)) + @test numtype(tanh(bareinterval(1//1))) === Float64 + + @test decoration(tanh(interval(1.0, 2.0, def))) == def + @test decoration(tanh(interval(-Inf, Inf))) == dac + @test isguaranteed(tanh(interval(1.0))) + + @test isequal_interval(tanh(emptyinterval()), emptyinterval()) + @test isequal_interval(tanh(interval(0.5)), interval(0.46211715726000974, 0.4621171572600098)) + @test isequal_interval(tanh(interval(0.5, 1.67)), interval(0.46211715726000974, 0.9315516846152083)) + @test isequal_interval(tanh(interval(-4.5, 0.1)), interval(-0.9997532108480276, 0.09966799462495583)) + + for lo_hi ∈ ((0.5, 0.5), (0.5, 1.67), (1.67, 3.2), (2.1, 5.6), (0.5, 8.5), (-4.5, 0.1), (1.3, 6.3)) + @test issubset_interval(tanh(interval(BigFloat, lo_hi...)), tanh(interval(lo_hi...))) + end + for lo_hi ∈ ((0.5, 0.5), (0.5, 1.67), (1.67, 3.2), (2.1, 5.6), (0.5, 8.5), (-4.5, 0.1), (1.3, 6.3)) + @test issubset_interval(tanh(interval(lo_hi...)), tanh(interval(Float32, lo_hi...))) + end + + for a ∈ (interval(17, 19), interval(0.5, 1.2)) + @test issubset_interval(tanh(a), sinh(a) / cosh(a)) + end + + z = complex(interval(0.3), interval(0.4)) + @test isequal_interval(tanh(z), sinh(z) / cosh(z)) +end + +@testset "coth" begin + @test isempty_interval(coth(bareinterval(0.0))) + @test isentire_interval(coth(bareinterval(-1.0, 1.0))) + @test isequal_interval(coth(bareinterval(0.0, 1.0)), bareinterval(1.3130352854993312, Inf)) + @test isequal_interval(coth(bareinterval(-1.0, 0.0)), bareinterval(-Inf, -1.3130352854993312)) + @test isequal_interval(coth(bareinterval(1.0, 2.0)), bareinterval(1.037314720727548, 1.3130352854993315)) + @test isempty_interval(coth(emptyinterval(BareInterval{Float64}))) + @test numtype(coth(bareinterval(1//1))) === Float64 + + @test decoration(coth(interval(-1.0, 1.0))) == trv + @test decoration(coth(interval(0.0, 1.0))) == trv + @test decoration(coth(interval(1.0, 2.0))) == com + @test isguaranteed(coth(interval(1.0))) +end + +@testset "sech" begin + @test isequal_interval(sech(bareinterval(0.0, 1.0)), bareinterval(0.6480542736638853, 1.0)) + @test isequal_interval(sech(bareinterval(-1.0, 0.0)), bareinterval(0.6480542736638853, 1.0)) + @test isequal_interval(sech(bareinterval(-1.0, 2.0)), bareinterval(0.26580222883407967, 1.0)) + @test isempty_interval(sech(emptyinterval(BareInterval{Float64}))) + @test isequal_interval(sech(entireinterval(BareInterval{Float64})), bareinterval(0.0, 1.0)) + @test numtype(sech(bareinterval(1//1))) === Float64 + + @test decoration(sech(interval(-1.0, 1.0))) == com + @test isguaranteed(sech(interval(1.0))) +end + +@testset "csch" begin + @test isempty_interval(csch(bareinterval(0.0))) + @test isentire_interval(csch(bareinterval(-1.0, 1.0))) + @test isequal_interval(csch(bareinterval(0.0, 1.0)), bareinterval(0.8509181282393214, Inf)) + @test isequal_interval(csch(bareinterval(-1.0, 0.0)), bareinterval(-Inf, -0.8509181282393214)) + @test isequal_interval(csch(bareinterval(1.0, 2.0)), bareinterval(0.2757205647717832, 0.8509181282393216)) + @test isempty_interval(csch(emptyinterval(BareInterval{Float64}))) + @test numtype(csch(bareinterval(1//1))) === Float64 + + @test decoration(csch(interval(-1.0, 1.0))) == trv + @test decoration(csch(interval(0.0, 1.0))) == trv + @test decoration(csch(interval(1.0, 2.0))) == com +end + +@testset "asinh" begin + @test isequal_interval(asinh(bareinterval(0.0, 1.0)), bareinterval(0.0, 0.881373587019543)) + @test isempty_interval(asinh(emptyinterval(BareInterval{Float64}))) + @test isentire_interval(asinh(entireinterval(BareInterval{Float64}))) + @test numtype(asinh(bareinterval(1//1))) === Float64 + + @test decoration(asinh(interval(1.0, 2.0, def))) == def + @test isguaranteed(asinh(interval(1.0))) + r = @test_logs (:warn,) asinh(nai(Float64)) + @test isnai(r) + + @test issubset_interval(asinh(interval(BigFloat, 1, 1)), asinh(interval(1))) + @test issubset_interval(asinh(interval(BigFloat, 0.9, 2)), asinh(interval(0.9, 2))) + @test issubset_interval(asinh(interval(BigFloat, 3, 4)), asinh(interval(3, 4))) +end + +@testset "acosh" begin + @test isequal_interval(acosh(bareinterval(0.0, 2.0)), bareinterval(0.0, 1.3169578969248168)) + @test isempty_interval(acosh(bareinterval(-2.0, 0.0))) + @test isthinzero(acosh(bareinterval(1.0))) + @test isempty_interval(acosh(emptyinterval(BareInterval{Float64}))) + @test isequal_interval(acosh(bareinterval(1.0, Inf)), bareinterval(0.0, Inf)) + @test numtype(acosh(bareinterval(2//1))) === Float64 + + @test decoration(acosh(interval(1.0, 2.0))) == com + @test decoration(acosh(interval(0.0, 2.0))) == trv + @test decoration(acosh(interval(1.0, Inf))) == dac + @test isguaranteed(acosh(interval(1.0, 2.0))) + + @test issubset_interval(acosh(interval(BigFloat, 1, 1)), acosh(interval(1))) + @test issubset_interval(acosh(interval(BigFloat, -2, -0.9)), acosh(interval(-2, -0.9))) + @test issubset_interval(acosh(interval(BigFloat, 3, 4)), acosh(interval(3, 4))) +end + +@testset "atanh" begin + @test isentire_interval(atanh(bareinterval(-2.0, 2.0))) + @test isentire_interval(atanh(bareinterval(-1.0, 1.0))) + @test isequal_interval(atanh(bareinterval(0.0, 0.5)), bareinterval(0.0, 0.5493061443340549)) + @test isempty_interval(atanh(emptyinterval(BareInterval{Float64}))) + @test isempty_interval(atanh(bareinterval(-2.0, -1.5))) + @test numtype(atanh(bareinterval(1//2))) === Float64 + + @test decoration(atanh(interval(-0.5, 0.5))) == com + @test decoration(atanh(interval(-1.0, 1.0))) == trv + @test decoration(atanh(interval(0.0, 1.0))) == trv + @test isguaranteed(atanh(interval(0.5))) +end + +@testset "acoth" begin + @test isempty_interval(acoth(bareinterval(-1.0, 1.0))) + @test isentire_interval(acoth(bareinterval(-2.0, 2.0))) + @test isempty_interval(acoth(bareinterval(0.5, 2.0))) + @test isempty_interval(acoth(bareinterval(-2.0, 0.5))) + @test isequal_interval(acoth(bareinterval(1.0, 2.0)), bareinterval(0.5493061443340548, Inf)) + @test isequal_interval(acoth(bareinterval(-2.0, -1.0)), bareinterval(-Inf, -0.5493061443340548)) + @test isequal_interval(acoth(bareinterval(2.0, 3.0)), bareinterval(0.34657359027997264, 0.5493061443340549)) + @test isequal_interval(acoth(bareinterval(-3.0, -2.0)), bareinterval(-0.5493061443340549, -0.34657359027997264)) + @test isempty_interval(acoth(emptyinterval(BareInterval{Float64}))) + @test isentire_interval(acoth(entireinterval(BareInterval{Float64}))) + @test numtype(acoth(bareinterval(2//1))) === Float64 + + @test decoration(acoth(interval(2.0, 3.0))) == com + r = acoth(interval(-1.0, 1.0)) + @test isempty_interval(r) + @test decoration(r) == trv + r = acoth(interval(0.5, 2.0)) + @test isempty_interval(r) + @test decoration(r) == trv +end + +@testset "numtype stability" begin + for T ∈ (Float16, Float32, BigFloat) + for f ∈ (sinh, cosh, tanh, sech, asinh) + r = f(bareinterval(T, 1//2, 1)) + @test numtype(r) === T + @test in_interval(f(big(1) / 2), r) || in_interval(f(big(1)), r) + end + @test numtype(coth(bareinterval(T, 1, 2))) === T + @test numtype(csch(bareinterval(T, 1, 2))) === T + @test numtype(acosh(bareinterval(T, 1, 2))) === T + @test numtype(atanh(bareinterval(T, 0, 1//2))) === T + @test numtype(acoth(bareinterval(T, 2, 3))) === T + end +end + +@testset "point enclosure and inverse pairs" begin + x = bareinterval(0.1, 2.7) + for t ∈ range(inf(x), sup(x); length = 7) + @test in_interval(sinh(t), sinh(x)) + @test in_interval(cosh(t), cosh(x)) + @test in_interval(tanh(t), tanh(x)) + @test in_interval(coth(t), coth(x)) + @test in_interval(sech(t), sech(x)) + @test in_interval(csch(t), csch(x)) + @test in_interval(asinh(t), asinh(x)) + end + y = bareinterval(-0.9, 0.9) + for t ∈ range(inf(y), sup(y); length = 7) + @test in_interval(atanh(t), atanh(y)) + end + + for x ∈ (bareinterval(-0.75, 0.5), bareinterval(0.1, 0.9)) + @test issubset_interval(x, sinh(asinh(x))) + @test issubset_interval(x, tanh(atanh(x))) + end + for x ∈ (bareinterval(1.0, 2.0), bareinterval(1.5, 10.0)) + @test issubset_interval(x, cosh(acosh(x))) + end +end diff --git a/test/intervals/arithmetic/integer.jl b/test/intervals/arithmetic/integer.jl new file mode 100644 index 000000000..b8ee2707b --- /dev/null +++ b/test/intervals/arithmetic/integer.jl @@ -0,0 +1,145 @@ +using Test +using IntervalArithmetic + +@testset "sign" begin + @test isequal_interval(sign(bareinterval(-2.0, 3.0)), bareinterval(-1.0, 1.0)) + @test isequal_interval(sign(bareinterval(0.0, 3.0)), bareinterval(0.0, 1.0)) + @test isthin(sign(bareinterval(-3.0, -1.0)), -1.0) + @test isthinzero(sign(bareinterval(0.0))) + @test isempty_interval(sign(emptyinterval(BareInterval{Float64}))) + @test isequal_interval(sign(entireinterval(BareInterval{Float64})), bareinterval(-1.0, 1.0)) + + @test isequal_interval(sign(bareinterval(-1//2, 3//2)), bareinterval(-1//1, 1//1)) + @test numtype(sign(bareinterval(-1//2, 3//2))) === Rational{Int} + + @test decoration(sign(interval(1.0, 3.0))) == com + @test decoration(sign(interval(-2.0, 3.0))) == def + @test decoration(sign(interval(0.0, 3.0))) == def + @test decoration(sign(interval(1.0, 3.0, def))) == def + @test decoration(sign(emptyinterval(Interval{Float64}))) == trv + @test isguaranteed(sign(interval(1.0))) + @test !isguaranteed(sign(convert(Interval{Float64}, 1))) + r = @test_logs (:warn,) sign(nai(Float64)) + @test isnai(r) + + @test isequal_interval(sign(entireinterval()), interval(-1.0, 1.0)) + @test isequal_interval(sign(emptyinterval()), emptyinterval()) + @test isequal_interval(sign(interval(-3.0, 1.0)), interval(-1.0, 1.0)) + @test isequal_interval(sign(interval(-3.0, -1.0)), interval(-1.0, -1.0)) + @test isequal_interval(sign(interval(0.1, 1.1)), interval(1.0)) +end + +@testset "ceil, floor and trunc" begin + @test isequal_interval(ceil(bareinterval(-1.5, 2.5)), bareinterval(-1.0, 3.0)) + @test isequal_interval(floor(bareinterval(-1.5, 2.5)), bareinterval(-2.0, 2.0)) + @test isequal_interval(trunc(bareinterval(-1.5, 2.5)), bareinterval(-1.0, 2.0)) + @test isequal_interval(ceil(bareinterval(2.0, 3.0)), bareinterval(2.0, 3.0)) + + for f ∈ (ceil, floor, trunc) + @test isempty_interval(f(emptyinterval(BareInterval{Float64}))) + @test isentire_interval(f(entireinterval(BareInterval{Float64}))) + end + + @test isequal_interval(ceil(bareinterval(-1//2, 3//2)), bareinterval(0//1, 2//1)) + @test numtype(ceil(bareinterval(-1//2, 3//2))) === Rational{Int} + @test isequal_interval(floor(bareinterval(-1//2, 3//2)), bareinterval(-1//1, 1//1)) + @test isequal_interval(trunc(bareinterval(-1//2, 3//2)), bareinterval(0//1, 1//1)) + + @test decoration(ceil(interval(1.5, 2.0))) == dac + @test decoration(ceil(interval(2.0, 2.0))) == dac + @test decoration(ceil(interval(-1.5, 2.5))) == def + @test decoration(ceil(interval(1.2, 1.8))) == com + + @test decoration(floor(interval(1.2, 1.8))) == com + @test decoration(floor(interval(1.5, 2.0))) == def + @test decoration(floor(interval(-1.5, 2.5))) == def + + @test decoration(trunc(interval(-2.0, -1.5))) == def + @test decoration(trunc(interval(0.5, 0.9))) == com + @test decoration(trunc(interval(-1.5, 2.5))) == def + + for f ∈ (ceil, floor, trunc) + @test decoration(f(emptyinterval(Interval{Float64}))) == trv + @test decoration(f(entireinterval())) == def + @test isguaranteed(f(interval(1.2, 1.8))) + @test !isguaranteed(f(convert(Interval{Float64}, 1))) + r = @test_logs (:warn,) f(nai(Float64)) + @test isnai(r) + end + + @test isequal_interval(floor(interval(0.1, 1.1)), interval(0, 1)) + @test isequal_interval(ceil(interval(0.1, 1.1)), interval(1, 2)) + @test isequal_interval(trunc(interval(0.1, 1.1)), interval(0.0, 1.0)) +end + +@testset "round" begin + for x ∈ (bareinterval(-1.5, 2.5), bareinterval(0.1, 1.1), interval(-1.5, 2.5), interval(0.1, 1.1)) + @test isequal_interval(round(x), round(x, RoundNearest)) + end + for x ∈ (bareinterval(-1.5, 2.5), interval(-1.5, 2.5), interval(0.5, 0.9, def)) + @test round(x, RoundToZero) === trunc(x) + @test round(x, RoundUp) === ceil(x) + @test round(x, RoundDown) === floor(x) + end + + @test isequal_interval(round(bareinterval(-1.5, 2.5), RoundNearest), bareinterval(-2.0, 2.0)) + @test isequal_interval(round(bareinterval(-1.5, 2.5), RoundNearestTiesAway), bareinterval(-2.0, 3.0)) + @test isequal_interval(round(bareinterval(1.2, 1.8), RoundNearest), bareinterval(1.0, 2.0)) + for mode ∈ (RoundNearest, RoundNearestTiesAway, RoundToZero, RoundUp, RoundDown) + @test isempty_interval(round(emptyinterval(BareInterval{Float64}), mode)) + end + + @test isequal_interval(round(bareinterval(-1//2, 3//2)), bareinterval(0//1, 2//1)) + @test numtype(round(bareinterval(-1//2, 3//2))) === Rational{Int} + + @test decoration(round(interval(0.5, 0.7))) == def + @test decoration(round(interval(-1.5, 2.5))) == def + @test decoration(round(interval(1.2, 1.8))) == def + @test decoration(round(interval(1.2, 1.4))) == com + @test decoration(round(interval(1.2, 1.4), RoundNearestTiesAway)) == com + @test isguaranteed(round(interval(1.2, 1.8))) + @test !isguaranteed(round(convert(Interval{Float64}, 1))) + r = @test_logs (:warn,) round(nai(Float64)) + @test isnai(r) + r = @test_logs (:warn,) round(nai(Float64), RoundNearestTiesAway) + @test isnai(r) + + @test isequal_interval(round(interval(0.1, 1.1), RoundDown), interval(0, 1)) + @test isequal_interval(round(interval(0.1, 1.1), RoundUp), interval(1, 2)) + @test isequal_interval(round(interval(0.1, 1.1), RoundToZero), interval(0.0, 1.0)) + @test isequal_interval(round(interval(0.1, 1.1)), interval(0.0, 1.0)) + @test isequal_interval(round(interval(0.1, 1.5)), interval(0.0, 2.0)) + @test isequal_interval(round(interval(-1.5, 0.1)), interval(-2.0, 0.0)) + @test isequal_interval(round(interval(-2.5, 0.1)), interval(-2.0, 0.0)) + @test isequal_interval(round(interval(0.1, 1.1), RoundNearest), interval(0.0, 1.0)) + @test isequal_interval(round(interval(0.1, 1.5), RoundNearest), interval(0.0, 2.0)) + @test isequal_interval(round(interval(-1.5, 0.1), RoundNearest), interval(-2.0, 0.0)) + @test isequal_interval(round(interval(-2.5, 0.1), RoundNearest), interval(-2.0, 0.0)) + @test isequal_interval(round(interval(0.1, 1.1), RoundNearestTiesAway), interval(0.0, 1.0)) + @test isequal_interval(round(interval(0.1, 1.5), RoundNearestTiesAway), interval(0.0, 2.0)) + @test isequal_interval(round(interval(-1.5, 0.1), RoundNearestTiesAway), interval(-2.0, 0.0)) + @test isequal_interval(round(interval(-2.5, 0.1), RoundNearestTiesAway), interval(-3.0, 0.0)) +end + +@testset "numtype stability" begin + for T ∈ (Float16, Float32, BigFloat) + x = bareinterval(T, 3//2, 5//2) + for f ∈ (sign, ceil, floor, trunc, round) + @test numtype(f(x)) === T + end + @test numtype(round(x, RoundNearestTiesAway)) === T + end +end + +@testset "point enclosure" begin + for x ∈ (bareinterval(-2.6, 3.2), bareinterval(0.1, 1.1)) + for t ∈ range(inf(x), sup(x); length = 9) + @test in_interval(sign(t), sign(x)) + @test in_interval(ceil(t), ceil(x)) + @test in_interval(floor(t), floor(x)) + @test in_interval(trunc(t), trunc(x)) + @test in_interval(round(t), round(x)) + @test in_interval(round(t, RoundNearestTiesAway), round(x, RoundNearestTiesAway)) + end + end +end diff --git a/test/intervals/arithmetic/power.jl b/test/intervals/arithmetic/power.jl new file mode 100644 index 000000000..ce6aedf18 --- /dev/null +++ b/test/intervals/arithmetic/power.jl @@ -0,0 +1,613 @@ +using Test +using IntervalArithmetic + +@testset "PowerMode" begin + @test IntervalArithmetic.default_power() === IntervalArithmetic.PowerMode{:fast}() + + x = bareinterval(-2.0, 3.0) + y = bareinterval(1.0, 2.0) + @test isequal_interval(IntervalArithmetic._select_pown(IntervalArithmetic.PowerMode{:fast}(), x, 3), fastpown(x, 3)) + @test isequal_interval(IntervalArithmetic._select_pown(IntervalArithmetic.PowerMode{:slow}(), x, 3), pown(x, 3)) + @test isequal_interval(IntervalArithmetic._select_pow(IntervalArithmetic.PowerMode{:fast}(), y, bareinterval(2.0, 3.0)), fastpow(y, bareinterval(2.0, 3.0))) + @test isequal_interval(IntervalArithmetic._select_pow(IntervalArithmetic.PowerMode{:slow}(), y, bareinterval(2.0, 3.0)), pow(y, bareinterval(2.0, 3.0))) + + # invokelatest is needed since `configure` redefines `default_power` in a newer world age + try + IntervalArithmetic.configure(power = :slow) + @test Base.invokelatest(IntervalArithmetic.default_power) === IntervalArithmetic.PowerMode{:slow}() + @test isequal_interval(Base.invokelatest(^, x, bareinterval(3.0)), bareinterval(-8.0, 27.0)) + @test_throws ArgumentError IntervalArithmetic.configure(power = :bogus) + finally + IntervalArithmetic.configure(power = :fast) + end + @test Base.invokelatest(IntervalArithmetic.default_power) === IntervalArithmetic.PowerMode{:fast}() + @test isequal_interval(Base.invokelatest(^, x, bareinterval(3.0)), bareinterval(-18.0, 27.0)) +end + +@testset "^ operator" begin + @test isequal_interval(bareinterval(2, 3) ^ bareinterval(2), bareinterval(4.0, 9.0)) + @test isequal_interval(bareinterval(2.0) ^ bareinterval(0.5), bareinterval(1.414213562373095, 1.4142135623730951)) + @test_throws InexactError bareinterval(2.0) ^ bareinterval(1e300) + + r = interval(-1, 1) ^ interval(3) + @test isequal_interval(r, interval(-1.0, 1.0)) + @test decoration(r) == com + r = interval(-1, 1) ^ interval(-3) + @test isentire_interval(r) + @test decoration(r) == trv + + @test decoration(interval(2.0) ^ interval(3.0, 3.0, def)) == def + @test !isguaranteed(interval(2.0) ^ convert(Interval{Float64}, 2)) + r = interval(2.0) ^ emptyinterval() + @test isempty_interval(r) + @test decoration(r) == trv + + n = 3 + @test !isguaranteed(interval(2.0) ^ n) + @test isguaranteed(interval(2.0) ^ 3) + @test isequal_interval(interval(2.0) ^ n, interval(8.0)) + + r = interval(2.0) ^ (1//2) + @test isequal_interval(r, interval(1.414213562373095, 1.4142135623730951)) + @test !isguaranteed(r) +end + +@testset "literal powers" begin + @test isequal_interval(interval(-2.0, 3.0) ^ 2, interval(0.0, 9.0)) + @test isequal_interval(interval(-2.0, 3.0) ^ 3, interval(-18.0, 27.0)) + @test isequal_interval(interval(-2.0, 3.0) ^ 0, interval(1.0)) + r = interval(-2.0, 3.0) ^ -1 + @test isentire_interval(r) + @test decoration(r) == trv + + @test isequal_interval(complex(interval(1.0, 2.0), interval(1.0, 2.0)) ^ 2, complex(interval(-3.0, 3.0), interval(2.0, 8.0))) + @test isequal_interval(complex(interval(1.0, 2.0), interval(1.0, 2.0)) ^ 0, complex(interval(1.0), interval(0.0))) + + x = interval(-1, 1) + n2 = 2 + @test isguaranteed(x ^ 2) + @test !isguaranteed(x ^ n2) + @test !isguaranteed(x ^ 2.0) + if VERSION ≥ v"1.12-DEV" && Int != Int32 + @test isguaranteed(x ^ 2305843009213693952) + else + @test_broken isguaranteed(x ^ 2305843009213693952) + end + @test isequal_interval(x ^ 2, interval(0, 1)) + @test isequal_interval(x ^ 3, x) +end + +@testset "complex powers" begin + z = complex(interval(1.0, 2.0), interval(1.0, 2.0)) + @test isequal_interval(z ^ complex(interval(2.0), interval(0.0)), complex(interval(-3.0, 3.0), interval(2.0, 8.0))) + + w = complex(interval(2.0), interval(0.0)) ^ complex(interval(2.0, 4.0), interval(0.0)) + @test issubset_interval(interval(4.0, 16.0), real(w)) + @test isthinzero(imag(w)) + + @test isempty_interval(complex(emptyinterval(), interval(1.0)) ^ complex(interval(2.0), interval(0.0))) + + x = complex(interval(2.0), interval(1.0)) + y = complex(interval(1.0), interval(1.0)) + @test isequal_interval(x ^ y, exp(y * log(x))) + + @test isequal_interval(complex(interval(2.0), interval(0.0)) ^ interval(2.0), complex(interval(4.0), interval(0.0))) + @test isequal_interval(interval(2.0) ^ complex(interval(2.0), interval(0.0)), complex(interval(4.0), interval(0.0))) + + a = interval(3 + 4im) + b = exp(a) + @test isequal_interval(real(b), interval(-13.12878308146216, -13.128783081462153)) + @test isequal_interval(imag(b), interval(-15.200784463067956, -15.20078446306795)) + + ze = exp(-im * interval(π)) + @test in_interval(-1, real(ze)) + @test in_interval(0, imag(ze)) + + z0 = interval(0im) + @test isthinzero(z0 ^ 2) + @test isthinone(z0 ^ 0) + @test isempty_interval(z0 ^ (-1)) + @test isthinzero(z0 ^ interval(2)) + @test isthinone(z0 ^ interval(0)) + @test isempty_interval(z0 ^ interval(-1)) + @test isempty_interval(z0 ^ emptyinterval()) + + x34 = interval(3 + 4im) + @test in_interval(-7 + 24im, x34 ^ 2) + @test issubset_interval(sqrt(x34), x34 ^ 0.5) + a2 = -3.1 + @test issubset_interval(x34, (x34 ^ a2) ^ (1 / a2)) + @test in_interval(2 + im, sqrt(x34)) + + @test issubset_interval(interval(0, 1) * interval(im), sqrt(interval(-1, 0) + interval(0) * interval(im))) + @test issubset_interval(interval(0, 1) + interval(0, 1) * interval(im), sqrt(interval(-1, 1) + interval(0) * interval(im))) + @test issubset_interval(interval(0, Inf) + interval(-3//8, Inf) * interval(im), sqrt(interval(-9//32, Inf) * interval(im))) + + xc = interval(-0.5) + interval(im) * interval(-1e-14, 1e-14) + yc = interval(1) + @test issubset_interval(xc ^ yc, exp(yc * log(xc))) + yc = interval(1.5, 2.5) + res = xc ^ yc + ref = exp(yc * log(xc)) + @test inf(real(ref)) < -0.3 < -1e-13 < inf(real(res)) + yc = interval(-2, Inf) + @test isequal_interval(xc ^ yc, exp(yc * log(xc))) + @test isequal_interval(fastpown(xc, 1), xc ^ interval(1)) + @test isequal_interval(fastpown(xc, 2), xc ^ interval(2)) + @test isequal_interval(fastpown(xc, 5), xc ^ interval(5)) +end + +@testset "pow" begin + @test isequal_interval(pow(bareinterval(2, 3), bareinterval(2)), bareinterval(4.0, 9.0)) + @test isequal_interval(pow(bareinterval(-1.0, 1.0), bareinterval(0.5)), bareinterval(0.0, 1.0)) + @test isempty_interval(pow(bareinterval(2.0), emptyinterval(BareInterval{Float64}))) + @test isempty_interval(pow(bareinterval(-2.0, -1.0), bareinterval(2.0))) + @test isthinzero(pow(bareinterval(0.0), bareinterval(1.0))) + @test isempty_interval(pow(bareinterval(0.0), bareinterval(0.0))) + @test isempty_interval(pow(bareinterval(0.0), bareinterval(-1.0))) + @test isequal_interval(pow(bareinterval(0.0, 2.0), bareinterval(1.0, 2.0)), bareinterval(0.0, 4.0)) + + @test numtype(pow(bareinterval(2.0), bareinterval(1//2))) === Float64 + @test isequal_interval(pow(bareinterval(1//2, 1//1), bareinterval(2//1)), bareinterval(1//4, 1//1)) + @test numtype(pow(bareinterval(1//2, 1//1), bareinterval(2//1))) === Rational{Int} + + @test isequal_interval(pow(bareinterval(2.0), 3), bareinterval(8.0)) + @test isequal_interval(pow(bareinterval(4.0), 1//2), bareinterval(2.0)) + @test isequal_interval(pow(bareinterval(2.0), 3), pow(bareinterval(2.0), bareinterval(3))) + + r = pow(interval(-1, 1), interval(3)) + @test isequal_interval(r, interval(0.0, 1.0)) + @test decoration(r) == trv + r = pow(interval(-1, 1), interval(-3)) + @test isequal_interval(r, interval(1.0, Inf)) + @test decoration(r) == trv + @test isequal_interval(pow(interval(2, 3), interval(0, 1)), interval(1, 3)) + r = pow(interval(0, 2), interval(0, 1)) + @test isequal_interval(r, interval(0, 2)) + @test decoration(r) == trv + r = pow(interval(-3, 2), interval(0, 1)) + @test isequal_interval(r, interval(0, 2)) + @test decoration(r) == trv + r = pow(interval(-3, 2), interval(-1, 1)) + @test isequal_interval(r, interval(0, Inf)) + @test decoration(r) == trv + @test decoration(pow(interval(1.0, 2.0), interval(0.0, 1.0))) == com + @test decoration(pow(interval(0.0, 1.0), interval(1.0, 2.0))) == com + @test decoration(pow(interval(0.0, 1.0), interval(0.0, 1.0))) == trv + @test decoration(pow(interval(-1.0, 1.0), interval(1.0, 2.0))) == trv + @test !isguaranteed(pow(interval(2.0), convert(Interval{Float64}, 2))) + @test isguaranteed(pow(interval(2.0), 3)) + @test isequal_interval(pow(interval(2.0), 3), interval(8.0)) + @test isequal_interval(pow(interval(4.0), 1//2), interval(2.0)) + + a = interval(1, 2) + @test isequal_interval(pow(a, interval(3, 4)), interval(1, 16)) + @test isequal_interval(pow(a, interval(0.5, 1)), a) + @test isequal_interval(pow(a, interval(0.3, 0.5)), interval(1, sqrt(2))) + @test isequal_interval(pow(a, interval(-1.5, 2.5)), interval(0.35355339059327373, 5.656854249492381)) + + @test isequal_interval(pow(interval(0.0), interval(1.1)), interval(0)) + @test isequal_interval(pow(interval(0.0), interval(1//10)), interval(0)) + @test isequal_interval(pow(interval(0.0), interval(-1//10)), emptyinterval()) + + @test isequal_interval(pow(interval(-3, 4), interval(0.5)), interval(0, 2)) + @test isequal_interval(pow(interval(-3, 4), interval(0.5)), pow(interval(-3, 4), 1//2)) + @test isequal_interval(pow(interval(BigFloat, -3, 4), interval(0.5)), interval(BigFloat, 0, 2)) + + @test dist(pow(interval(1, 27), interval(1/3)), interval(1, 3)) < 2 * inf(eps(interval(1, 3))) + @test issubset_interval(interval(1, 3), pow(interval(1, 27), interval(1//3))) + @test isequal_interval(pow(interval(0.1, 0.7), interval(1//3)), interval(0.46415888336127786, 0.8879040017426008)) + @test dist(pow(interval(0.1, 0.7), interval(1/3)), interval(0.46415888336127786, 0.8879040017426008)) < 2 * inf(eps(pow(interval(0.1, 0.7), interval(1/3)))) + + @test diam(pow(interval(BigFloat, 27), interval(1//3))) == 0 + @test diam(pow(interval(BigFloat, 9.595703125), interval(1//3))) == 0 + @test 0 <= diam(pow(interval(BigFloat, 0.1), interval(1//3))) < 1e-76 + + @test isequal_interval(pow(interval(1.0f0), interval(1.0f0)), interval(1.0f0)) +end + +@testset "pow with rational exponents" begin + @test isequal_interval(pow(emptyinterval(), 1//3), emptyinterval()) + @test isequal_interval(pow(interval(1, 8), 1//3), interval(1, 2)) + @test issubset_interval(interval(2^(1//3), 2), pow(interval(2, 8), 1//3)) + @test issubset_interval(interval(1, 9^(1//3)), pow(interval(1, 9), 1//3)) + @test issubset_interval(interval(2^(1//3), 9^(1//3)), pow(interval(2, 9), 1//3)) + @test isequal_interval(pow(interval(-1, 8), 1//3), interval(0, 2)) + @test issubset_interval(interval(0, 2), pow(interval(-2, 8), 1//3)) + @test issubset_interval(interval(0, 9^(1//3)), pow(interval(-1, 9), 1//3)) + @test issubset_interval(interval(0, 9^(1//3)), pow(interval(-2, 9), 1//3)) + @test isequal_interval(pow(interval(1, 8), -1//3), interval(0.5, 1)) + @test issubset_interval(interval(0.5, 2^(-1//3)), pow(interval(2, 8), -1//3)) + @test issubset_interval(interval(9^(-1//3), 1), pow(interval(1, 9), -1//3)) + @test issubset_interval(interval(9^(-1//3), 2^(-1//3)), pow(interval(2, 9), -1//3)) + @test isequal_interval(pow(interval(-1, 8), -1//3), interval(0.5, Inf)) + @test issubset_interval(interval(0.5, Inf), pow(interval(-2, 8), -1//3)) + @test issubset_interval(interval(9^(-1//3), Inf), pow(interval(-1, 9), -1//3)) + @test issubset_interval(interval(9^(-1//3), Inf), pow(interval(-2, 9), -1//3)) + @test isequal_interval(pow(interval(-2, 4), 1//2), interval(0, 2)) + @test isequal_interval(pow(interval(-2, 8), 1//3), interval(0, 2)) + @test isequal_interval(pow(interval(-8, -2), 1//3), emptyinterval()) + @test isequal_interval(pow(interval(-8, -2), 1//2), emptyinterval()) + @test isequal_interval(pow(interval(-8, -2), -1//3), emptyinterval()) + @test isequal_interval(pow(interval(-8, -2), -1//2), emptyinterval()) + @test isequal_interval(pow(emptyinterval(), 2//3), emptyinterval()) + @test isequal_interval(pow(interval(1, 8), 2//3), interval(1, 4)) + @test issubset_interval(interval(2^(2//3), 4), pow(interval(2, 8), 2//3)) + @test issubset_interval(interval(1, 9^(2//3)), pow(interval(1, 9), 2//3)) + @test issubset_interval(interval(2^(2//3), 9^(2//3)), pow(interval(2, 9), 2//3)) + @test isequal_interval(pow(interval(-1, 8), 2//3), interval(0, 4)) + @test issubset_interval(interval(0, 4), pow(interval(-2, 8), 2//3)) + @test issubset_interval(interval(0, 9^(2//3)), pow(interval(-1, 9), 2//3)) + @test issubset_interval(interval(0, 9^(2//3)), pow(interval(-2, 9), 2//3)) + @test isequal_interval(pow(interval(1, 8), -2//3), interval(0.25, 1)) + @test issubset_interval(interval(0.25, 2^(-2//3)), pow(interval(2, 8), -2//3)) + @test issubset_interval(interval(9^(-2//3), 1), pow(interval(1, 9), -2//3)) + @test issubset_interval(interval(9^(-2//3), 2^(-2//3)), pow(interval(2, 9), -2//3)) + @test isequal_interval(pow(interval(-1, 8), -2//3), interval(0.25, Inf)) + @test issubset_interval(interval(0.25, Inf), pow(interval(-2, 8), -2//3)) + @test issubset_interval(interval(9^(-2//3), Inf), pow(interval(-1, 9), -2//3)) + @test issubset_interval(interval(9^(-2//3), Inf), pow(interval(-2, 9), -2//3)) + @test isequal_interval(pow(interval(-2, 4), 3//2), interval(0, 8)) + @test isequal_interval(pow(interval(-2, 8), 2//3), interval(0, 4)) + @test isequal_interval(pow(interval(-8, -2), 2//3), emptyinterval()) + @test isequal_interval(pow(interval(-8, -2), 3//2), emptyinterval()) + @test isequal_interval(pow(interval(-8, -2), -2//3), emptyinterval()) + @test isequal_interval(pow(interval(-8, -2), -3//2), emptyinterval()) + @test isequal_interval(pow(interval(-1, 1), 1000000000000000000000000000000000000000//1), interval(0, 1)) +end + +@testset "_thin_pow" begin + @test isthinzero(IntervalArithmetic._thin_pow(bareinterval(0.0), 1.0)) + @test isempty_interval(IntervalArithmetic._thin_pow(bareinterval(0.0), -1.0)) + x = bareinterval(2.0, 3.0) + @test IntervalArithmetic._thin_pow(x, 0.5) === sqrt(x) + @test isequal_interval(IntervalArithmetic._thin_pow(x, 3.0), pown(x, 3)) + @test isequal_interval(IntervalArithmetic._thin_pow(bareinterval(4.0), 3//2), bareinterval(8.0)) + @test isequal_interval(IntervalArithmetic._thin_pow(x, 4//2), pown(x, 2)) +end + +@testset "pown" begin + x = bareinterval(2.0, 3.0) + @test isequal_interval(pown(x, 0), one(BareInterval{Float64})) + @test isequal_interval(pown(entireinterval(BareInterval{Float64}), 0), one(BareInterval{Float64})) + @test pown(x, 1) === x + @test isempty_interval(pown(emptyinterval(BareInterval{Float64}), 3)) + @test isempty_interval(pown(bareinterval(0.0), -3)) + @test isempty_interval(pown(bareinterval(0.0), -2)) + + @test isequal_interval(pown(bareinterval(2, 3), 3), bareinterval(8.0, 27.0)) + @test isequal_interval(pown(bareinterval(0.0, 3.0), 3), bareinterval(0.0, 27.0)) + @test isequal_interval(pown(bareinterval(-3.0, 0.0), 3), bareinterval(-27.0, 0.0)) + @test isentire_interval(pown(entireinterval(BareInterval{Float64}), 3)) + + @test isequal_interval(pown(bareinterval(0.0, 3.0), -3), bareinterval(0.037037037037037035, Inf)) + @test isequal_interval(pown(bareinterval(-3.0, 0.0), -3), bareinterval(-Inf, -0.037037037037037035)) + @test isentire_interval(pown(bareinterval(-1.0, 1.0), -3)) + + @test isequal_interval(pown(bareinterval(-2.0, 3.0), 2), bareinterval(0.0, 9.0)) + @test isequal_interval(pown(bareinterval(2, 3), 2), bareinterval(4.0, 9.0)) + @test isequal_interval(pown(bareinterval(-3.0, -2.0), 2), bareinterval(4.0, 9.0)) + @test isequal_interval(pown(entireinterval(BareInterval{Float64}), 2), bareinterval(0.0, Inf)) + + @test isequal_interval(pown(bareinterval(-2.0, 3.0), -2), bareinterval(0.1111111111111111, Inf)) + @test isequal_interval(pown(bareinterval(2.0, 3.0), -2), bareinterval(0.1111111111111111, 0.25)) + @test isequal_interval(pown(entireinterval(BareInterval{Float64}), -2), bareinterval(0.0, Inf)) + + @test isequal_interval(pown(bareinterval(1//2), 2), bareinterval(1//4)) + @test isequal_interval(pown(bareinterval(1//2), -2), bareinterval(4//1)) + @test numtype(pown(bareinterval(1//2), 2)) === Rational{Int} + + @test decoration(pown(interval(-1, 1), 3)) == com + r = pown(interval(-1, 1), -3) + @test isentire_interval(r) + @test decoration(r) == trv + r = pown(interval(0.0), -1) + @test isempty_interval(r) + @test decoration(r) == trv + @test isguaranteed(pown(interval(2.0), 3)) + @test !isguaranteed(pown(convert(Interval{Float64}, 2), 3)) + r = @test_logs (:warn,) pown(nai(Float64), 2) + @test isnai(r) + + @test isequal_interval(pown(interval(2, 3), 2), interval(4, 9)) + @test isequal_interval(pown(interval(0, 3), 2), interval(0, 9)) + @test isequal_interval(pown(interval(-3, 0), 2), interval(0, 9)) + @test isequal_interval(pown(interval(-3, -2), 2), interval(4, 9)) + @test isequal_interval(pown(interval(-3, 2), 2), interval(0, 9)) + @test isequal_interval(pown(interval(0, 3), 3), interval(0, 27)) + @test isequal_interval(pown(interval(2, 3), 3), interval(8, 27)) + @test isequal_interval(pown(interval(-3, 0), 3), interval(-27.0, 0.0)) + @test isequal_interval(pown(interval(-3, -2), 3), interval(-27, -8)) + @test isequal_interval(pown(interval(-3, 2), 3), interval(-27.0, 8.0)) + @test isequal_interval(pown(interval(0, 3), -2), interval(1/9, Inf)) + @test isequal_interval(pown(interval(-3, 0), -2), interval(1/9, Inf)) + @test isequal_interval(pown(interval(-3, 2), -2), interval(1/9, Inf)) + @test isequal_interval(pown(interval(2, 3), -2), interval(1/9, 1/4)) + @test isequal_interval(pown(interval(1, 2), -3), interval(1/8, 1.0)) + @test isequal_interval(pown(interval(0, 3), -3), interval(1/27, Inf)) + @test isequal_interval(pown(interval(-1, 2), -3), entireinterval()) + @test isequal_interval(pown(interval(-3, -2), -3), interval(-1/8, -1/27)) + @test isequal_interval(pown(interval(0.0), 0), interval(1)) + @test isequal_interval(pown(emptyinterval(), 0), emptyinterval()) + @test isequal_interval(pown(interval(2.5), 3), interval(15.625, 15.625)) + @test isequal_interval(pown(interval(5//2), 3), interval(125//8)) + @test decoration(pown(interval(0, 3), -2)) == trv +end + +@testset "rootn" begin + x = bareinterval(2.0, 3.0) + @test isempty_interval(rootn(x, 0)) + @test rootn(x, 1) === x + @test rootn(bareinterval(4.0), 2) === sqrt(bareinterval(4.0)) + @test isequal_interval(rootn(bareinterval(8.0), -3), bareinterval(0.5)) + @test isequal_interval(rootn(bareinterval(8.0), -3), inv(rootn(bareinterval(8.0), 3))) + + @test isequal_interval(rootn(bareinterval(8.0), 3), bareinterval(2.0)) + @test isequal_interval(rootn(bareinterval(-8.0), 3), bareinterval(-2.0)) + @test isequal_interval(rootn(bareinterval(-8.0, 8.0), 2), bareinterval(0.0, 2.8284271247461903)) + @test isempty_interval(rootn(bareinterval(-8.0, -1.0), 2)) + @test isempty_interval(rootn(emptyinterval(BareInterval{Float64}), 3)) + + @test rootn(bareinterval(1//2), 3) isa BareInterval{Float64} + @test bounds(rootn(bareinterval(1//2), 3)) == (0.7937005259840997, 0.7937005259840998) + @test in_interval(0.5^(1/3), rootn(interval(1//2), 3)) + @test decoration(rootn(interval(1//2), 3)) == com + + @test decoration(rootn(interval(-8.0, 8.0), 2)) == trv + @test decoration(rootn(interval(1.0, 8.0), 3)) == com + @test isguaranteed(rootn(interval(8.0), 3)) + @test !isguaranteed(rootn(convert(Interval{Float64}, 8), 3)) + + @test isequal_interval(rootn(emptyinterval(), 3), emptyinterval()) + @test isequal_interval(rootn(emptyinterval(), 4), emptyinterval()) + @test isequal_interval(rootn(emptyinterval(), -3), emptyinterval()) + @test isequal_interval(rootn(emptyinterval(), -4), emptyinterval()) + @test isequal_interval(rootn(interval(1, 2), 0), emptyinterval()) + @test isequal_interval(rootn(interval(5, 8), 0), emptyinterval()) + @test isequal_interval(rootn(interval(1, 7), 0), emptyinterval()) + @test isequal_interval(rootn(interval(8, 27), 3), interval(2, 3)) + @test isequal_interval(rootn(interval(0, 27), 3), interval(0, 3)) + @test isequal_interval(rootn(interval(-27, 0), 3), interval(-3, 0)) + @test isequal_interval(rootn(interval(-27, 27), 3), interval(-3, 3)) + @test isequal_interval(rootn(interval(-27, -8), 3), interval(-3, -2)) + @test isequal_interval(rootn(interval(16, 81), 4), interval(2, 3)) + @test isequal_interval(rootn(interval(0, 81), 4), interval(0, 3)) + @test isequal_interval(rootn(interval(-81, 0), 4), interval(0)) + @test isequal_interval(rootn(interval(-81, 81), 4), interval(0, 3)) + @test isequal_interval(rootn(interval(-81, -16), 4), emptyinterval()) + @test isequal_interval(rootn(interval(8, 27), -3), interval(1/3, 1/2)) + @test isequal_interval(rootn(interval(0, 27), -3), interval(1/3, Inf)) + @test isequal_interval(rootn(interval(-27, 0), -3), interval(-Inf, -1/3)) + @test isequal_interval(rootn(interval(-27, 27), -3), interval(-Inf, Inf)) + @test isequal_interval(rootn(interval(-27, -8), -3), interval(-1/2, -1/3)) + @test isequal_interval(rootn(interval(16, 81), -4), interval(1/3, 1/2)) + @test isequal_interval(rootn(interval(0, 81), -4), interval(1/3, Inf)) + @test isequal_interval(rootn(interval(-81, 0), -4), emptyinterval()) + @test isequal_interval(rootn(interval(-81, 1), 1), interval(-81, 1)) + @test isequal_interval(rootn(interval(-81, 81), -4), interval(1/3, Inf)) + @test isequal_interval(rootn(interval(-81, -16), -4), emptyinterval()) + @test isequal_interval(rootn(interval(-81, -16), 1), interval(-81, -16)) + @test isequal_interval(rootn(interval(BigFloat, 16, 81), 4), interval(BigFloat, 2, 3)) + @test isequal_interval(rootn(interval(BigFloat, 0, 81), 4), interval(BigFloat, 0, 3)) + @test isequal_interval(rootn(interval(BigFloat, -81, 0), 4), interval(BigFloat, 0, 0)) + @test isequal_interval(rootn(interval(BigFloat, -81, 81), 4), interval(BigFloat, 0, 3)) + @test isequal_interval(rootn(interval(BigFloat, -27, 27), -3), interval(BigFloat, -Inf, Inf)) + @test isequal_interval(rootn(interval(BigFloat, -81, -16), -4), emptyinterval()) + @test isequal_interval(rootn(interval(BigFloat, -81, -16), 1), interval(BigFloat, -81, -16)) +end + +@testset "hypot" begin + @test isequal_interval(hypot(bareinterval(3.0), bareinterval(4.0)), bareinterval(5.0)) + r = hypot(interval(3.0), interval(4.0)) + @test isequal_interval(r, interval(5.0)) + @test decoration(r) == com + x = bareinterval(3.0) + y = bareinterval(4.0) + @test isequal_interval(hypot(x, y), sqrt(IntervalArithmetic._select_pown(x, 2) + IntervalArithmetic._select_pown(y, 2))) + @test isempty_interval(hypot(bareinterval(1.0), emptyinterval(BareInterval{Float64}))) + @test isthinzero(hypot(interval(0.0), interval(0.0))) + r = hypot(interval(0.0, Inf), interval(1.0)) + @test !isbounded(r) + @test decoration(r) == dac +end + +@testset "fastpow" begin + @test isequal_interval(fastpow(bareinterval(2.0, 3.0), bareinterval(2.0)), bareinterval(4.0, 9.0)) + @test isequal_interval(fastpow(interval(1.0, 2.0), interval(2.0, 3.0)), interval(1.0, 8.000000000000004)) + @test issubset_interval(pow(interval(1.0, 2.0), interval(2.0, 3.0)), fastpow(interval(1.0, 2.0), interval(2.0, 3.0))) + @test isequal_interval(fastpow(bareinterval(-1.0, 1.0), bareinterval(0.5)), bareinterval(0.0, 1.0)) + y = emptyinterval(BareInterval{Float64}) + @test fastpow(bareinterval(2.0), y) === y + @test isempty_interval(fastpow(bareinterval(-2.0, -1.0), bareinterval(2.0))) + @test isthinzero(fastpow(bareinterval(0.0), bareinterval(1.0))) + @test isempty_interval(fastpow(bareinterval(0.0), bareinterval(-1.0))) + @test isempty_interval(fastpow(bareinterval(0.0), bareinterval(0.0))) + + @test isthin(fastpow(bareinterval(2.0), bareinterval(-2.0)), 0.25) + @test isequal_interval(fastpow(bareinterval(2.0), bareinterval(0.5)), exp(bareinterval(0.5) * log(bareinterval(2.0)))) + @test isequal_interval(fastpow(bareinterval(2.0), 3.0), bareinterval(8.0)) + @test isequal_interval(fastpow(interval(2.0), 3.0), interval(8.0)) + + @test decoration(fastpow(interval(1.0, 2.0), interval(0.0, 1.0))) == com + @test decoration(fastpow(interval(0.0, 1.0), interval(1.0, 2.0))) == com + @test decoration(fastpow(interval(0.0, 1.0), interval(0.0, 1.0))) == trv + @test decoration(fastpow(interval(-1.0, 1.0), interval(1.0, 2.0))) == trv + @test !isguaranteed(fastpow(interval(2.0), convert(Interval{Float64}, 2))) + + x = interval(1, 2) + @test isequal_interval(fastpow(x, 2), interval(1, 4)) + @test isequal_interval(fastpow(x, 3), interval(1, 8)) + @test isempty_interval(fastpow(-x, 3)) + @test isequal_interval(fastpow(interval(-1, 2), 2), interval(0, 4)) + @test isequal_interval(fastpow(interval(-1, 2), 3), interval(0, 8)) + @test isequal_interval(fastpow(interval(-1, 2), 4), interval(0, 16)) + @test isempty_interval(fastpow(interval(-2, -1), interval(-1, -1))) + @test isequal_interval(fastpow(interval(BigFloat, -1, 2), 2), interval(0, 4)) + @test isequal_interval(fastpow(interval(BigFloat, -1, 2), 3), interval(0, 8)) + @test isequal_interval(fastpow(interval(BigFloat, 1, 2), 2), interval(1, 4)) + + xpi = interval(π) + @test isinterior(pow(xpi, 100), fastpow(xpi, 100)) + @test isinterior(pow(xpi, 50), fastpow(xpi, 50)) + @test isequal_interval(fastpow(interval(2), 2000), interval(floatmax(), Inf)) + + @test isequal_interval(fastpow(x, 0.5), interval(1.0, 1.4142135623730951)) + @test isequal_interval(fastpow(x, 0.5), pow(x, interval(0.5))) + @test isequal_interval(fastpow(interval(2, 3), -0.5), interval(0.5773502691896257, 0.7071067811865476)) + yw = interval(-2, 3) + @test isequal_interval(fastpow(yw, 2.1), interval(0.0, 10.045108566305146)) + @test issubset_interval(pow(yw, interval(2.1)), fastpow(yw, 2.1)) + @test isequal_interval(fastpow(yw, interval(-2, 3)), interval(0, Inf)) + @test isequal_interval(fastpow(yw, interval(2.1)), interval(0.0, 10.045108566305146)) +end + +@testset "fastpown" begin + @test isequal_interval(fastpown(bareinterval(-2.0, 3.0), 3), bareinterval(-18.0, 27.0)) + @test isequal_interval(pown(bareinterval(-2.0, 3.0), 3), bareinterval(-8.0, 27.0)) + @test issubset_interval(pown(bareinterval(-2.0, 3.0), 3), fastpown(bareinterval(-2.0, 3.0), 3)) + + @test isempty_interval(fastpown(bareinterval(0.0), -1)) + @test isequal_interval(fastpown(bareinterval(2.0), -2), bareinterval(0.25)) + @test isequal_interval(fastpown(bareinterval(2.0), -2), inv(fastpown(bareinterval(2.0), 2))) + @test isequal_interval(fastpown(bareinterval(-1.0, 1.0), 4), bareinterval(0.0, 1.0)) + @test inf(fastpown(bareinterval(-2.0, 1.0), 2)) ≥ 0 + @test isempty_interval(fastpown(emptyinterval(BareInterval{Float64}), 3)) + x = bareinterval(2.0, 3.0) + @test isequal_interval(fastpown(x, 0), one(x)) + @test fastpown(x, 1) === x + + @test decoration(fastpown(interval(1.0, 2.0), 2)) == com + @test decoration(fastpown(interval(-1.0, 1.0), -2)) == trv + @test isguaranteed(fastpown(interval(2.0), 3)) + + @test isequal_interval(fastpown(complex(interval(1.0, 2.0), interval(0.0)), 3), complex(interval(1.0, 8.0), interval(0.0))) + @test isequal_interval(fastpown(complex(interval(0.0), interval(1.0, 2.0)), 0), complex(interval(1.0), interval(0.0))) + @test isequal_interval(fastpown(complex(interval(0.0), interval(1.0, 2.0)), 1), complex(interval(0.0), interval(1.0, 2.0))) + @test isequal_interval(fastpown(complex(interval(0.0), interval(1.0, 2.0)), 2), complex(interval(-4.0, -1.0), interval(0.0))) + @test isequal_interval(fastpown(complex(interval(0.0), interval(1.0, 2.0)), 3), complex(interval(0.0), interval(-8.0, -1.0))) + @test isequal_interval(fastpown(complex(interval(1.0), interval(1.0)), 2), complex(interval(0.0), interval(2.0))) +end + +@testset "_positive_power_by_squaring" begin + x = bareinterval(2.0) + @test isequal_interval(IntervalArithmetic._positive_power_by_squaring(x, 10), bareinterval(1024.0)) + @test IntervalArithmetic._positive_power_by_squaring(x, 0) === one(x) + @test IntervalArithmetic._positive_power_by_squaring(x, 1) === x + @test isequal_interval(IntervalArithmetic._positive_power_by_squaring(x, 2), x * x) + for n ∈ 0:10 + @test isthin(IntervalArithmetic._positive_power_by_squaring(x, n), 2.0^n) + end +end + +@testset "cbrt, exp, exp2, exp10 and expm1" begin + @test isequal_interval(exp(bareinterval(0.0, 1.0)), bareinterval(1.0, 2.7182818284590455)) + @test isequal_interval(cbrt(bareinterval(-8.0, 8.0)), bareinterval(-2.0, 2.0)) + @test isequal_interval(exp2(bareinterval(1.0)), bareinterval(2.0)) + @test isequal_interval(exp10(bareinterval(1.0)), bareinterval(10.0)) + @test isthinzero(expm1(bareinterval(0.0))) + @test isequal_interval(exp(entireinterval(BareInterval{Float64})), bareinterval(0.0, Inf)) + @test decoration(exp(interval(-Inf, Inf))) == dac + + for f ∈ (cbrt, exp, exp2, exp10, expm1) + @test isempty_interval(f(emptyinterval(BareInterval{Float64}))) + @test numtype(f(bareinterval(1//2))) === Float64 + @test decoration(f(interval(0.0, 1.0))) == com + @test decoration(f(interval(0.0, 1.0, def))) == def + @test isguaranteed(f(interval(1.0))) + @test !isguaranteed(f(convert(Interval{Float64}, 1))) + end + + @test issubset_interval(exp(interval(BigFloat, 1//2)), exp(interval(1//2))) + @test in_interval(exp(big(1//2)), exp(interval(1//2))) + @test issubset_interval(exp(interval(BigFloat, 0.1)), exp(interval(0.1))) + @test isequal_interval(exp(interval(0.1)), interval(1.1051709180756475e+00, 1.1051709180756477e+00)) + @test diam(exp(interval(0.1))) == eps(exp(0.1)) + @test issubset_interval(exp2(interval(BigFloat, 1//2)), exp2(interval(1//2))) + @test isequal_interval(exp2(interval(1024.0)), interval(1.7976931348623157e308, Inf)) + @test issubset_interval(exp10(interval(BigFloat, 1//2)), exp10(interval(1//2))) + @test isequal_interval(exp10(interval(308.5)), interval(1.7976931348623157e308, Inf)) + + @test isequal_interval(cbrt(interval(2, 3)), interval(1.259921049894873, 1.4422495703074085)) + @test isequal_interval(cbrt(big(interval(2, 3))), interval(big"1.259921049894873164767210607278228350570251464701507980081975112155299676513956", big"1.442249570307408382321638310780109588391869253499350577546416194541687596830003")) + @test issubset_interval(cbrt(big(interval(2, 3))), cbrt(interval(2, 3))) + @test issubset_interval(Interval{Float64}(cbrt(big(interval(3, 4)))), cbrt(interval(3, 4))) + @test isequal_interval(cbrt(interval(2f0, 3f0)), interval(1.259921f0, 1.4422497f0)) + @test issubset_interval(cbrt(interval(2, 3)), cbrt(interval(2f0, 3f0))) + + z = complex(interval(0.0), interval(0.0)) + @test isequal_interval(exp(z), complex(interval(1.0), interval(0.0))) + w = complex(interval(0.3), interval(0.4)) + @test isequal_interval(exp(w), exp(real(w)) * cis(imag(w))) + @test isequal_interval(exp2(w), exp2(real(w)) * cis(imag(w) * log(interval(Float64, 2)))) + @test isequal_interval(exp10(w), exp10(real(w)) * cis(imag(w) * log(interval(Float64, 10)))) + @test isequal_interval(expm1(w), exp(w) - interval(Float64, 1)) +end + +@testset "log, log2, log10 and log1p" begin + @test isempty_interval(log(bareinterval(0.0))) + @test isempty_interval(log(bareinterval(-2.0, -1.0))) + @test isequal_interval(log(bareinterval(-1.0, 1.0)), bareinterval(-Inf, 0.0)) + @test isequal_interval(log(bareinterval(0.0, 1.0)), bareinterval(-Inf, 0.0)) + @test isequal_interval(log2(bareinterval(8.0)), bareinterval(3.0)) + @test isequal_interval(log10(bareinterval(100.0)), bareinterval(2.0)) + + for f ∈ (log, log2, log10) + @test decoration(f(interval(1.0, 2.0))) == com + @test decoration(f(interval(0.0, 1.0))) == trv + @test decoration(f(interval(-1.0, 1.0))) == trv + @test decoration(f(interval(1.0, Inf))) == dac + @test isempty_interval(f(emptyinterval(BareInterval{Float64}))) + @test numtype(f(bareinterval(1//2))) === Float64 + @test isguaranteed(f(interval(1.0))) + end + + @test isempty_interval(log1p(bareinterval(-1.0))) + @test isempty_interval(log1p(bareinterval(-2.0, -1.0))) + @test isequal_interval(log1p(bareinterval(-1.0, 1.0)), bareinterval(-Inf, 0.6931471805599454)) + @test decoration(log1p(interval(0.0, 1.0))) == com + @test decoration(log1p(interval(-1.0, 1.0))) == trv + @test numtype(log1p(bareinterval(1//2))) === Float64 + + @test issubset_interval(log(interval(BigFloat, 1//2)), log(interval(1//2))) + @test in_interval(log(big(1//2)), log(interval(1//2))) + @test issubset_interval(log(interval(BigFloat, 0.1)), log(interval(0.1))) + @test isequal_interval(log(interval(0.1)), interval(-2.3025850929940459e+00, -2.3025850929940455e+00)) + @test diam(log(interval(0.1))) == eps(log(0.1)) + @test issubset_interval(log2(interval(BigFloat, 1//2)), log2(interval(1//2))) + @test isequal_interval(log2(interval(0.25, 0.5)), interval(-2.0, -1.0)) + @test in_interval(log10(big(1//10)), log10(interval(1//10))) + @test isequal_interval(log1p(interval(-10.0)), emptyinterval()) + + @test issubset_interval(log(interval(-2, 5)), interval(-Inf, sup(log(interval(5))))) + + z = complex(interval(1.0), interval(0.0)) + @test isequal_interval(log(z), complex(interval(0.0), interval(0.0))) + w = complex(interval(2.0), interval(1.0)) + @test isequal_interval(log(w), complex(log(abs(w)), angle(w))) + @test isequal_interval(log2(w), complex(log2(abs(w)), angle(w) / log(interval(Float64, 2)))) + @test isequal_interval(log10(w), complex(log10(abs(w)), angle(w) / log(interval(Float64, 10)))) + @test isequal_interval(log1p(w), log(interval(Float64, 1) + w)) +end + +@testset "numtype stability and point enclosure" begin + for T ∈ (Float16, Float32, Float64, BigFloat) + @test isequal_interval(pown(bareinterval(T, 2), 3), bareinterval(T, 8)) + @test numtype(pown(bareinterval(T, 2), 3)) === T + for f ∈ (cbrt, exp, exp2, exp10, expm1, log, log2, log10, log1p) + r = f(bareinterval(T, 1, 2)) + @test numtype(r) === T + @test in_interval(f(big(3) / 2), r) + end + end + + x = bareinterval(0.3, 2.6) + for t ∈ range(inf(x), sup(x); length = 7) + for n ∈ (2, 3, -2) + @test in_interval(t^n, pown(x, n)) + @test in_interval(t^n, fastpown(x, n)) + end + @test in_interval(exp(t), exp(x)) + @test in_interval(log(t), log(x)) + @test in_interval(cbrt(t), cbrt(x)) + end +end diff --git a/test/intervals/arithmetic/trigonometric.jl b/test/intervals/arithmetic/trigonometric.jl new file mode 100644 index 000000000..b0f9617d5 --- /dev/null +++ b/test/intervals/arithmetic/trigonometric.jl @@ -0,0 +1,539 @@ +using Test +using IntervalArithmetic +using Logging + +@testset "helper functions" begin + @test IntervalArithmetic._quadrant(min, 0.0) == 0 + @test IntervalArithmetic._quadrant(min, 1.0) == 0 + @test IntervalArithmetic._quadrant(min, 2.0) == 1 + @test IntervalArithmetic._quadrant(min, -1.0) == 3 + @test IntervalArithmetic._quadrant(min, -2.0) == 2 + @test IntervalArithmetic._quadrant(min, 100.0) == 3 + @test IntervalArithmetic._quadrant(max, 100.0) == 3 + Logging.with_logger(Logging.NullLogger()) do + @test_throws InexactError IntervalArithmetic._quadrant(min, Inf) + end + + for x ∈ (1.5707963267948966, 1.5707963267948968, 3.141592653589793, 3.1415926535897936, -1.5707963267948966, -3.141592653589793) + @test IntervalArithmetic._quadrant(min, x) ≤ IntervalArithmetic._quadrant(max, x) + end + + for (x, q) ∈ ((0.0, 0), (0.4, 0), (0.6, 1), (1.2, 2), (1.6, 3), (-0.4, 3), (-0.6, 2), (-1.2, 1), (-1.6, 0), (2.0, 0), (-2.0, 0)) + @test IntervalArithmetic._quadrantpi(x) == q + end + + bp = IntervalArithmetic._big_pi(1.0) + @test bp isa BareInterval{BigFloat} + @test precision(inf(bp)) == 256 + @test inf(bp) < π < sup(bp) + bp = IntervalArithmetic._big_pi(BigFloat(1)) + @test precision(inf(bp)) == precision(BigFloat(1)) + 32 + + @test bounds(IntervalArithmetic._unsafe_scale(bareinterval(1.0, 2.0), 0.5)) == (0.5, 1.0) + r = IntervalArithmetic._unsafe_scale(bareinterval(3.0), 0.1) + @test inf(r) < sup(r) + @test inf(r) ≤ big(3.0) * big(0.1) ≤ sup(r) + + @test bounds(IntervalArithmetic._half_pi(Float64)) == (1.5707963267948966, 1.5707963267948968) + @test in_interval(big(π) / 2, IntervalArithmetic._half_pi(Float64)) + @test bounds(IntervalArithmetic._range_atan(Float64)) == (-3.1415926535897936, 3.1415926535897936) + @test bounds(IntervalArithmetic._half_range_atan(Float64)) == (-1.5707963267948968, 1.5707963267948968) +end + +@testset "rad2deg and deg2rad" begin + @test isequal_interval(rad2deg(bareinterval(Float64, π)), bareinterval(179.99999999999991, 180.00000000000006)) + r = rad2deg(interval(Float64, π)) + @test isequal_interval(r, interval(179.99999999999991, 180.00000000000006)) + @test decoration(r) == com + @test isequal_interval(deg2rad(bareinterval(180.0)), bareinterval(3.141592653589793, 3.1415926535897936)) + @test in_interval(big(π), deg2rad(bareinterval(180.0))) + @test decoration(deg2rad(interval(180.0))) == com + + @test isempty_interval(rad2deg(emptyinterval(BareInterval{Float64}))) + @test isempty_interval(deg2rad(emptyinterval())) + r = @test_logs (:warn,) (:warn,) rad2deg(nai(Float64)) + @test isnai(r) + @test numtype(rad2deg(bareinterval(Float32, 1))) === Float32 + @test numtype(deg2rad(bareinterval(BigFloat, 180))) === BigFloat + + @test issubset_interval(interval(180, 360), rad2deg(interval(π, 2π))) + @test issubset_interval(interval(π, interval(2) * interval(π)), deg2rad(interval(180, 360))) +end + +@testset "sin" begin + @test isthinzero(sin(bareinterval(0.0))) + @test isequal_interval(sin(bareinterval(0.0, 0.5)), bareinterval(0.0, 0.479425538604203)) + @test isequal_interval(sin(bareinterval(0.5, 1.67)), bareinterval(0.47942553860420295, 1.0)) + @test isequal_interval(sin(bareinterval(-0.5, 0.5)), bareinterval(-0.479425538604203, 0.479425538604203)) + @test isequal_interval(sin(bareinterval(2.0, 3.0)), bareinterval(0.1411200080598672, 0.9092974268256817)) + @test isequal_interval(sin(bareinterval(2.0, 4.0)), bareinterval(-0.7568024953079283, 0.9092974268256817)) + @test isequal_interval(sin(bareinterval(0.5, 6.0)), bareinterval(-1.0, 1.0)) + @test isequal_interval(sin(entireinterval(BareInterval{Float64})), bareinterval(-1.0, 1.0)) + @test isequal_interval(sin(bareinterval(-10.0, 10.0)), bareinterval(-1.0, 1.0)) + @test isempty_interval(sin(emptyinterval(BareInterval{Float64}))) + @test isequal_interval(sin(bareinterval(1e10)), bareinterval(-0.48750602508751073, -0.4875060250875107)) + @test numtype(sin(bareinterval(1//2))) === Float64 + + r = sin(interval(0.0, 1.0)) + @test isequal_interval(r, interval(0.0, 0.8414709848078966)) + @test decoration(r) == com + @test decoration(sin(interval(1.0, 2.0, def))) == def + @test isguaranteed(sin(interval(1.0))) + @test !isguaranteed(sin(convert(Interval{Float64}, 1))) + + @test isequal_interval(sin(interval(0.5)), interval(0.47942553860420295, 0.47942553860420301)) + @test isequal_interval(sin(interval(0.5, 1.67)), interval(4.7942553860420295e-01, 1.0)) + @test isequal_interval(sin(interval(1.67, 3.2)), interval(-5.8374143427580093e-02, 9.9508334981018021e-01)) + @test isequal_interval(sin(interval(2.1, 5.6)), interval(-1.0, 0.8632093666488738)) + @test isequal_interval(sin(interval(0.5, 8.5)), interval(-1.0, 1.0)) + @test isequal_interval(sin(interval(Float64, -4.5, 0.1)), interval(-1.0, 0.9775301176650971)) + @test isequal_interval(sin(interval(Float64, 1.3, 6.3)), interval(-1.0, 1.0)) + + for lo_hi ∈ ((0.5, 0.5), (0.5, 1.67), (1.67, 3.2), (2.1, 5.6), (0.5, 8.5), (-4.5, 0.1), (1.3, 6.3)) + @test issubset_interval(sin(interval(BigFloat, lo_hi...)), sin(interval(lo_hi...))) + end + + z = interval(3, 1e-7; format = :midpoint) + interval(4, 1e-7; format = :midpoint) * interval(im) + @test issubset_interval(sin(z), complex(sin(real(z)) * cosh(imag(z)), sinh(imag(z)) * cos(real(z)))) + @test isthinzero(sin(complex(interval(0.0), interval(0.0)))) + w = complex(interval(0.1), interval(0.2)) + @test isequal_interval(sin(w), complex(sin(real(w)) * cosh(imag(w)), cos(real(w)) * sinh(imag(w)))) +end + +@testset "sinpi" begin + @test isthinzero(sinpi(bareinterval(0.0))) + @test isthinzero(sinpi(bareinterval(1.0))) + @test isthin(sinpi(bareinterval(0.5)), 1.0) + @test isequal_interval(sinpi(bareinterval(-3.0, 3.0)), bareinterval(-1.0, 1.0)) + @test isempty_interval(sinpi(emptyinterval(BareInterval{Float64}))) + @test isequal_interval(sinpi(entireinterval(BareInterval{Float64})), bareinterval(-1.0, 1.0)) + @test numtype(sinpi(bareinterval(1//2))) === Float64 + + @test isempty_interval(sinpi(emptyinterval())) + @test issubset_interval(interval(-1, 0), sinpi(interval(1, 2))) + @test isequal_interval(sinpi(interval(0.5, 1.5)), interval(-1, 1)) + @test issubset_interval(interval(1/sqrt(2), 1), sinpi(interval(0.25, 0.75))) + @test issubset_interval(interval(-1/sqrt(2), 1/sqrt(2)), sinpi(interval(-0.25, 0.25))) + @test isthin(sinpi(interval(1.0)), 0) + @test isthin(sinpi(interval(2.0)), 0) + @test isthin(sinpi(interval(0.5)), 1) + @test isthin(sinpi(interval(1.5)), -1) + + z = complex(interval(0.25), interval(0.5)) + @test isequal_interval(sinpi(z), complex(sinpi(real(z)) * cosh(imag(z) * interval(Float64, π)), cospi(real(z)) * sinh(imag(z) * interval(Float64, π)))) +end + +@testset "cos" begin + @test isthin(cos(bareinterval(0.0)), 1.0) + @test isequal_interval(cos(bareinterval(0.0, 0.5)), bareinterval(0.8775825618903726, 1.0)) + @test isequal_interval(cos(entireinterval(BareInterval{Float64})), bareinterval(-1.0, 1.0)) + @test isempty_interval(cos(emptyinterval(BareInterval{Float64}))) + @test isequal_interval(cos(bareinterval(1e300)), bareinterval(-0.5753861119575491, -0.575386111957549)) + @test isequal_interval(cos(bareinterval(-2.0, 2.5)), bareinterval(-0.8011436155469338, 1.0)) + @test isequal_interval(cos(bareinterval(-1.0, 3.5)), bareinterval(-1.0, 1.0)) + @test numtype(cos(bareinterval(1//2))) === Float64 + + @test isequal_interval(cos(interval(0.5)), interval(0.87758256189037265, 0.87758256189037276)) + @test isequal_interval(cos(interval(2.1, 5.6)), interval(-1.0, 0.7755658785102496)) + @test isequal_interval(cos(interval(0.5, 8.5)), interval(-1.0, 1.0)) + @test isequal_interval(cos(interval(1.67, 3.2)), interval(-1.0, -0.09904103659872801)) + @test decoration(cos(interval(0.0, 0.5))) == com + @test isguaranteed(cos(interval(1.0))) + + for lo_hi ∈ ((0.5, 0.5), (0.5, 1.67), (1.67, 3.2), (2.1, 5.6), (0.5, 8.5), (-4.5, 0.1), (1.3, 6.3)) + @test issubset_interval(cos(interval(BigFloat, lo_hi...)), cos(interval(lo_hi...))) + end + + k = [interval(0.0, 0.0625), interval(0.0625, 0.125), interval(0.0, 0.125)] + x = k[1] * 4 + k[2] * 4 + k[3] * 4 + @test isequal_interval(cos(2 * π * x), interval(-1, 1)) + @test isequal_interval(cospi(2x), interval(-1, 1)) + + w = complex(interval(0.1), interval(0.2)) + @test isequal_interval(cos(w), complex(cos(real(w)) * cosh(imag(w)), -sin(real(w)) * sinh(imag(w)))) +end + +@testset "cospi" begin + @test isthin(cospi(bareinterval(0.0)), 1.0) + @test isthin(cospi(bareinterval(1.0)), -1.0) + @test isthinzero(cospi(bareinterval(0.5))) + @test isthinzero(cospi(bareinterval(1.5))) + @test isempty_interval(cospi(emptyinterval(BareInterval{Float64}))) + @test isequal_interval(cospi(entireinterval(BareInterval{Float64})), bareinterval(-1.0, 1.0)) + @test numtype(cospi(bareinterval(1//2))) === Float64 + + @test isempty_interval(cospi(emptyinterval())) + @test isequal_interval(cospi(interval(1, 2)), interval(-1, 1)) + @test issubset_interval(interval(-1, 0), cospi(interval(0.5, 1.5))) + @test issubset_interval(interval(-1/sqrt(2), 1/sqrt(2)), cospi(interval(0.25, 0.75))) + @test isequal_interval(cospi(interval(-0.25, 0.25)), interval(1/sqrt(2), 1)) + @test isthin(cospi(interval(1.0)), -1) + @test isthin(cospi(interval(2.0)), 1) + @test isthin(cospi(interval(0.5)), 0) + @test isthin(cospi(interval(1.5)), 0) + @test decoration(cospi(interval(0.25, 0.5))) == com + @test isguaranteed(cospi(interval(0.25))) + + z = complex(interval(0.25), interval(0.5)) + @test isequal_interval(cospi(z), complex(cospi(real(z)) * cosh(imag(z) * interval(Float64, π)), -sinpi(real(z)) * sinh(imag(z) * interval(Float64, π)))) +end + +@testset "sind, cosd, sincospi and sincosd" begin + @test isequal_interval(sind(bareinterval(90.0)), bareinterval(1.0)) + @test isequal_interval(cosd(bareinterval(0.0)), bareinterval(1.0)) + r = sind(interval(90.0)) + @test isequal_interval(r, interval(1.0)) + @test decoration(r) == com + r = cosd(interval(180.0)) + @test isequal_interval(r, interval(-1.0)) + @test decoration(r) == com + + @test isempty_interval(sind(emptyinterval())) + @test issubset_interval(interval(-1, 0), sind(interval(180, 360))) + @test isequal_interval(sind(interval(90, 270)), interval(-1, 1)) + @test issubset_interval(interval(1/sqrt(2), 1), sind(interval(45, 135))) + @test issubset_interval(interval(-1/sqrt(2), 1/sqrt(2)), sind(interval(-45, 45))) + @test isthin(sind(interval(180)), 0) + @test isthin(sind(interval(360)), 0) + @test isthin(sind(interval(90)), 1) + @test isthin(sind(interval(270)), -1) + + @test isempty_interval(cosd(emptyinterval())) + @test isequal_interval(cosd(interval(180, 360)), interval(-1, 1)) + @test issubset_interval(interval(-1, 0), cosd(interval(90, 270))) + @test issubset_interval(interval(-1/sqrt(2), 1/sqrt(2)), cosd(interval(45, 135))) + @test isequal_interval(cosd(interval(-45, 45)), interval(1/sqrt(2), 1)) + @test isthin(cosd(interval(180)), -1) + @test isthin(cosd(interval(360)), 1) + @test isthin(cosd(interval(90)), 0) + @test isthin(cosd(interval(270)), 0) + + x = sincospi(bareinterval(0.5)) + @test isequal_interval(x[1], bareinterval(1.0)) + @test isequal_interval(x[2], bareinterval(0.0)) + x = sincospi(emptyinterval()) + @test isempty_interval(x[1]) & isempty_interval(x[2]) + x = sincospi(interval(1, 2)) + @test issubset_interval(interval(-1, 0), x[1]) & isequal_interval(x[2], interval(-1, 1)) + x = sincospi(interval(0.5, 1.5)) + @test isequal_interval(x[1], interval(-1, 1)) & issubset_interval(interval(-1, 0), x[2]) + x = sincospi(interval(0.25, 0.75)) + @test issubset_interval(interval(1/sqrt(2), 1), x[1]) & issubset_interval(interval(-1/sqrt(2), 1/sqrt(2)), x[2]) + x = sincospi(interval(-0.25, 0.25)) + @test issubset_interval(interval(-1/sqrt(2), 1/sqrt(2)), x[1]) & isequal_interval(x[2], interval(1/sqrt(2), 1)) + for y ∈ (bareinterval(0.25, 0.75), interval(0.25, 0.75)) + x = sincospi(y) + @test isequal_interval(x[1], sinpi(y)) + @test isequal_interval(x[2], cospi(y)) + end + + x = sincosd(emptyinterval()) + @test isempty_interval(x[1]) & isempty_interval(x[2]) + x = sincosd(interval(180, 380)) + @test issubset_interval(interval(-1, 0), x[1]) & isequal_interval(x[2], interval(-1, 1)) + x = sincosd(interval(90, 270)) + @test isequal_interval(x[1], interval(-1, 1)) & issubset_interval(interval(-1, 0), x[2]) + x = sincosd(interval(45, 135)) + @test issubset_interval(interval(1/sqrt(2), 1), x[1]) & issubset_interval(interval(-1/sqrt(2), 1/sqrt(2)), x[2]) + x = sincosd(interval(-45, 45)) + @test issubset_interval(interval(-1/sqrt(2), 1/sqrt(2)), x[1]) & isequal_interval(x[2], interval(1/sqrt(2), 1)) +end + +@testset "tan" begin + @test isthinzero(tan(bareinterval(0.0))) + @test isequal_interval(tan(bareinterval(0.0, 1.0)), bareinterval(0.0, 1.5574077246549023)) + @test isentire_interval(tan(entireinterval(BareInterval{Float64}))) + @test isentire_interval(tan(bareinterval(1.0, 2.0))) + @test isentire_interval(tan(bareinterval(1.5, 1.6))) + @test isentire_interval(tan(bareinterval(1.57, 1.58))) + @test isequal_interval(tan(bareinterval(1e10)), bareinterval(-0.5583496378112419, -0.5583496378112418)) + @test isempty_interval(tan(emptyinterval(BareInterval{Float64}))) + @test numtype(tan(bareinterval(1//2))) === Float64 + + r = tan(interval(1.0, 2.0)) + @test isentire_interval(r) + @test decoration(r) == trv + @test decoration(tan(interval(0.0, 1.0))) == com + r = tan(interval(1.0, Inf)) + @test isentire_interval(r) + @test decoration(r) == trv + @test isguaranteed(tan(interval(1.0))) + + @test isequal_interval(tan(interval(0.5)), interval(0.54630248984379048, 0.5463024898437906)) + @test isequal_interval(tan(interval(0.5, 1.67)), entireinterval()) + @test isequal_interval(tan(interval(1.67, 3.2)), interval(-10.047182299210307, 0.05847385445957865)) + @test isequal_interval(tan(interval(6.638314112824137, 8.38263151220128)), entireinterval()) + + @test issubset_interval(tan(interval(BigFloat, 0.5, 0.5)), tan(interval(0.5))) + @test isequal_interval(tan(interval(BigFloat, 0.5, 1.67)), entireinterval(BigFloat)) + @test issubset_interval(tan(interval(BigFloat, 0.5, 1.67)), tan(interval(0.5, 1.67))) + for lo_hi ∈ ((1.67, 3.2), (2.1, 5.6), (0.5, 8.5), (-4.5, 0.1), (1.3, 6.3)) + @test issubset_interval(tan(interval(BigFloat, lo_hi...)), tan(interval(lo_hi...))) + end + + w = complex(interval(0.1), interval(0.2)) + @test isequal_interval(tan(w), sin(w) / cos(w)) +end + +@testset "cot, sec and csc" begin + @test isempty_interval(cot(bareinterval(0.0))) + @test isequal_interval(cot(bareinterval(0.5, 1.0)), bareinterval(0.6420926159343306, 1.830487721712452)) + @test isequal_interval(cot(bareinterval(-1.0, 0.0)), bareinterval(-Inf, -0.6420926159343306)) + @test isequal_interval(cot(bareinterval(2.0, 3.0)), bareinterval(-7.015252551434534, -0.4576575543602857)) + @test isentire_interval(cot(bareinterval(3.0, 3.5))) + @test isentire_interval(cot(entireinterval(BareInterval{Float64}))) + @test isempty_interval(cot(emptyinterval(BareInterval{Float64}))) + @test numtype(cot(bareinterval(1//2))) === Float64 + # cot(::Interval) falls back to 1/tan defined by Base, hence the slightly wider bound + r = cot(interval(0.5, 1.0)) + @test bounds(r) == (0.6420926159343306, 1.8304877217124522) + @test decoration(r) == com + + @test isthin(sec(bareinterval(0.0)), 1.0) + @test isequal_interval(sec(bareinterval(0.0, 1.0)), bareinterval(1.0, 1.8508157176809257)) + @test isequal_interval(sec(bareinterval(2.0, 3.0)), bareinterval(-2.4029979617223813, -1.0101086659079936)) + @test isequal_interval(sec(bareinterval(2.0, 4.0)), bareinterval(-2.4029979617223813, -1.0)) + @test inf(sec(bareinterval(-1.0, 1.0))) == 1.0 + @test isentire_interval(sec(bareinterval(1.0, 2.0))) + @test isentire_interval(sec(bareinterval(1.5, 1.6))) + @test isentire_interval(sec(entireinterval(BareInterval{Float64}))) + @test isempty_interval(sec(emptyinterval(BareInterval{Float64}))) + @test numtype(sec(bareinterval(1//2))) === Float64 + r = sec(interval(0.0, 1.0)) + @test bounds(r) == (1.0, 1.850815717680926) + @test decoration(r) == com + + @test isempty_interval(csc(bareinterval(0.0))) + @test isequal_interval(csc(bareinterval(0.5, 1.0)), bareinterval(1.188395105778121, 2.0858296429334886)) + @test isequal_interval(csc(bareinterval(-1.0, 0.0)), bareinterval(-Inf, -1.188395105778121)) + @test bounds(csc(bareinterval(-2.0, 0.0))) == (-Inf, -1.0) + @test inf(csc(bareinterval(1.0, 2.0))) == 1.0 + @test sup(csc(bareinterval(-2.0, -1.0))) == -1.0 + @test isentire_interval(csc(bareinterval(-0.1, 0.1))) + @test isentire_interval(csc(bareinterval(3.0, 3.2))) + @test isentire_interval(csc(entireinterval(BareInterval{Float64}))) + @test isempty_interval(csc(emptyinterval(BareInterval{Float64}))) + @test numtype(csc(bareinterval(1//2))) === Float64 + r = csc(interval(0.5, 1.0)) + @test issubset_interval(interval(1.188395105778121, 2.0858296429334886), r) + @test decoration(r) == com +end + +@testset "asin" begin + @test isequal_interval(asin(bareinterval(-2.0, 2.0)), bareinterval(-1.5707963267948968, 1.5707963267948968)) + @test isthinzero(asin(bareinterval(0.0))) + @test isempty_interval(asin(bareinterval(-3.0, -2.0))) + @test isempty_interval(asin(emptyinterval(BareInterval{Float64}))) + @test numtype(asin(bareinterval(1//2))) === Float64 + + @test decoration(asin(interval(-0.5, 0.5))) == com + @test decoration(asin(interval(-2.0, 2.0))) == trv + @test isguaranteed(asin(interval(0.5))) + + @test isequal_interval(asin(interval(1)), interval(π) / interval(2)) + @test isequal_interval(asin(interval(0.9, 2)), asin(interval(0.9, 1))) + @test isequal_interval(asin(interval(3, 4)), emptyinterval()) + @test issubset_interval(asin(interval(BigFloat, 1, 1)), asin(interval(1))) + @test issubset_interval(asin(interval(BigFloat, 0.9, 2)), asin(interval(0.9, 2))) + @test issubset_interval(asin(interval(BigFloat, 3, 4)), asin(interval(3, 4))) +end + +@testset "acos" begin + @test isequal_interval(acos(bareinterval(-2.0, 2.0)), bareinterval(0.0, 3.1415926535897936)) + @test isthinzero(acos(bareinterval(1.0))) + @test isempty_interval(acos(emptyinterval(BareInterval{Float64}))) + @test numtype(acos(bareinterval(1//2))) === Float64 + + @test decoration(acos(interval(-0.5, 0.5))) == com + @test decoration(acos(interval(-2.0, 2.0))) == trv + + @test isequal_interval(acos(interval(1)), interval(0.0, 0.0)) + @test isequal_interval(acos(interval(-2, -0.9)), acos(interval(-1, -0.9))) + @test isequal_interval(acos(interval(3, 4)), emptyinterval()) + @test issubset_interval(acos(interval(BigFloat, 1, 1)), acos(interval(1))) + @test issubset_interval(acos(interval(BigFloat, -2, -0.9)), acos(interval(-2, -0.9))) + @test issubset_interval(acos(interval(BigFloat, 3, 4)), acos(interval(3, 4))) +end + +@testset "atan" begin + @test isthinzero(atan(bareinterval(0.0))) + @test isequal_interval(atan(entireinterval(BareInterval{Float64})), bareinterval(-1.5707963267948968, 1.5707963267948968)) + @test isempty_interval(atan(emptyinterval(BareInterval{Float64}))) + @test numtype(atan(bareinterval(1//2))) === Float64 + + @test decoration(atan(interval(0.0, 1.0))) == com + @test decoration(atan(interval(-Inf, Inf))) == dac + + @test isequal_interval(atan(interval(-1, 1)), interval(-0.7853981633974484, 0.7853981633974484)) + @test isequal_interval(atan(interval(0)), interval(0.0, 0.0)) + @test issubset_interval(atan(interval(BigFloat, -1, 1)), atan(interval(-1, 1))) +end + +@testset "acot" begin + r = acot(bareinterval(1.0, 2.0)) + @test bounds(r) == (0.4636476090008061, 0.7853981633974484) + @test inf(r) ≤ acot(2.0) && acot(1.0) ≤ sup(r) + @test isempty_interval(acot(emptyinterval(BareInterval{Float64}))) + + # acot(0) = π/2; the jump at 0 is decorated like the branch cut of two-argument atan + @test isequal_interval(acot(bareinterval(0.0)), IntervalArithmetic._half_pi(Float64)) + @test in_interval(acot(0.0), acot(bareinterval(0.0))) + @test bounds(acot(bareinterval(-1.0, 1.0))) == (-1.5707963267948968, 1.5707963267948968) + @test bounds(acot(bareinterval(0.0, 1.0))) == (0.7853981633974483, 1.5707963267948968) + @test bounds(acot(bareinterval(-1.0, 0.0))) == (-1.5707963267948968, 1.5707963267948968) + @test bounds(acot(entireinterval(BareInterval{Float64}))) == (-1.5707963267948968, 1.5707963267948968) + + r = acot(interval(0.5, 1.0)) + @test isequal_interval(r, interval(0.7853981633974483, 1.1071487177940906)) + @test decoration(r) == com + @test decoration(acot(interval(0.0))) == dac + @test decoration(acot(interval(0.0, 1.0))) == dac + @test decoration(acot(interval(-1.0, 0.0))) == def + @test decoration(acot(interval(-1.0, 1.0))) == def + @test !isguaranteed(acot(convert(Interval{Float64}, 1))) +end + +@testset "two-argument atan" begin + @test isempty_interval(atan(bareinterval(0.0), bareinterval(0.0))) + @test isequal_interval(atan(bareinterval(1.0), bareinterval(0.0)), IntervalArithmetic._half_pi(Float64)) + @test isequal_interval(atan(bareinterval(-1.0), bareinterval(0.0)), -IntervalArithmetic._half_pi(Float64)) + @test isequal_interval(atan(bareinterval(-1.0, 1.0), bareinterval(0.0)), IntervalArithmetic._half_range_atan(Float64)) + + y = bareinterval(0.0) + @test atan(y, bareinterval(1.0, 2.0)) === y + @test isequal_interval(atan(bareinterval(1.0), bareinterval(1.0)), bareinterval(0.7853981633974483, 0.7853981633974484)) + @test isequal_interval(atan(bareinterval(0.0), bareinterval(-1.0)), bareinterval(3.141592653589793, 3.1415926535897936)) + @test isequal_interval(atan(bareinterval(-1.0, 1.0), bareinterval(-2.0, -1.0)), IntervalArithmetic._range_atan(Float64)) + @test isequal_interval(atan(bareinterval(1.0, 2.0), bareinterval(-1.0, 1.0)), bareinterval(0.7853981633974483, 2.3561944901923453)) + @test isequal_interval(atan(bareinterval(-1.0, 1.0), bareinterval(-1.0, 1.0)), IntervalArithmetic._range_atan(Float64)) + + e = emptyinterval(BareInterval{Float64}) + @test atan(e, bareinterval(1.0)) === e + @test isempty_interval(atan(bareinterval(1.0), e)) + @test isequal_interval(atan(bareinterval(1//1), bareinterval(1//1)), bareinterval(0.7853981633974483, 0.7853981633974484)) + @test isequal_interval(atan(bareinterval(Float32, 1), bareinterval(Float32, 1)), bareinterval(0.7853981f0, 0.7853982f0)) + + @test decoration(atan(interval(-1.0, 1.0), interval(-1.0, 0.0))) == trv + @test decoration(atan(interval(-1.0, 1.0), interval(-2.0, -1.0))) == def + @test decoration(atan(interval(0.0, 1.0), interval(-2.0, -1.0))) == dac + @test decoration(atan(interval(0.1, 1.0), interval(-2.0, -1.0))) == com + @test decoration(atan(interval(1.0, 2.0), interval(1.0, 2.0))) == com + @test !isguaranteed(atan(interval(1.0), convert(Interval{Float64}, 1))) + r = @test_logs (:warn,) atan(nai(Float64), interval(1.0)) + @test isnai(r) + + @test isequal_interval(atan(emptyinterval(), entireinterval()), emptyinterval()) + @test isequal_interval(atan(entireinterval(), emptyinterval()), emptyinterval()) + @test isequal_interval(atan(interval(0.0, 1.0), interval(BigFloat, 0.0, 0.0)), interval(BigFloat, π) / interval(2)) + @test isequal_interval(atan(interval(0.0, 1.0), interval(0.0)), interval(π) / interval(2)) + @test isequal_interval(atan(interval(-1.0, -0.1), interval(0.0)), -interval(π) / interval(2)) + @test isequal_interval(atan(interval(-1.0, 1.0), interval(0.0)), interval(-0.5, 0.5) * interval(π)) + @test isequal_interval(atan(interval(0.0), interval(0.1, 1.0)), interval(0.0)) + @test issubset_interval(atan(interval(BigFloat, 0.0, 0.1), interval(BigFloat, 0.1, 1.0)), atan(interval(0.0, 0.1), interval(0.1, 1.0))) + @test isequal_interval(atan(interval(0.0, 0.1), interval(0.1, 1.0)), interval(0.0, 0.7853981633974484)) + @test issubset_interval(atan(interval(BigFloat, -0.1, 0.0), interval(BigFloat, 0.1, 1.0)), atan(interval(-0.1, 0.0), interval(0.1, 1.0))) + @test isequal_interval(atan(interval(-0.1, 0.0), interval(0.1, 1.0)), interval(-0.7853981633974484, 0.0)) + @test issubset_interval(atan(interval(BigFloat, -0.1, -0.1), interval(BigFloat, 0.1, Inf)), atan(interval(-0.1, -0.1), interval(0.1, Inf))) + @test isequal_interval(atan(interval(-0.1, 0.0), interval(0.1, Inf)), interval(-0.7853981633974484, 0.0)) + @test issubset_interval(atan(interval(BigFloat, 0.0, 0.1), interval(BigFloat, -2.0, -0.1)), atan(interval(0.0, 0.1), interval(-2.0, -0.1))) + @test isequal_interval(atan(interval(0.0, 0.1), interval(-2.0, -0.1)), interval(2.356194490192345, 3.1415926535897936)) + @test issubset_interval(atan(interval(BigFloat, -0.1, 0.0), interval(BigFloat, -2.0, -0.1)), atan(interval(-0.1, 0.0), interval(-2.0, -0.1))) + @test isequal_interval(atan(interval(-0.1, 0.0), interval(-2.0, -0.1)), interval(-1, 1) * interval(π)) + @test issubset_interval(atan(interval(BigFloat, -0.1, 0.1), interval(BigFloat, -Inf, -0.1)), atan(interval(-0.1, 0.1), interval(-Inf, -0.1))) + @test isequal_interval(atan(interval(-0.1, 0.1), interval(-Inf, -0.1)), interval(-1, 1) * interval(π)) + + @test issubset_interval(atan(interval(BigFloat, 0.0, 0.0), interval(BigFloat, -2.0, 0.0)), atan(interval(0.0, 0.0), interval(-2.0, 0.0))) + @test isequal_interval(atan(interval(-0.0, 0.0), interval(-2.0, 0.0)), interval(3.141592653589793, 3.1415926535897936)) + @test issubset_interval(atan(interval(BigFloat, 0.0, 0.1), interval(BigFloat, -0.1, 0.0)), atan(interval(0.0, 0.1), interval(-0.1, 0.0))) + @test isequal_interval(atan(interval(-0.0, 0.1), interval(-0.1, 0.0)), interval(1.5707963267948966, 3.1415926535897936)) + @test issubset_interval(atan(interval(BigFloat, -0.1, -0.1), interval(BigFloat, -0.1, 0.0)), atan(interval(-0.1, -0.1), interval(-0.1, 0.0))) + @test isequal_interval(atan(interval(-0.1, -0.1), interval(-0.1, 0.0)), interval(-2.3561944901923453, -1.5707963267948966)) + @test issubset_interval(atan(interval(BigFloat, -0.1, 0.1), interval(BigFloat, -2.0, 0.0)), atan(interval(-0.1, 0.1), interval(-2.0, 0.0))) + @test isequal_interval(atan(interval(-0.1, 0.1), interval(-2.0, 0.0)), interval(-1, 1) * interval(π)) + @test issubset_interval(atan(interval(BigFloat, 0.0, 0.1), interval(BigFloat, -2.0, 0.1)), atan(interval(0.0, 0.1), interval(-2.0, 0.1))) + @test isequal_interval(atan(interval(-0.0, 0.1), interval(-2.0, 0.1)), interval(0.0, 3.1415926535897936)) + @test issubset_interval(atan(interval(BigFloat, -0.1, -0.1), interval(BigFloat, -0.1, 0.1)), atan(interval(-0.1, -0.1), interval(-0.1, 0.1))) + @test isequal_interval(atan(interval(-0.1, -0.1), interval(-0.1, 0.1)), interval(-2.3561944901923453, Float64(-big(pi)/4, RoundUp))) + @test issubset_interval(atan(interval(BigFloat, -0.1, 0.1), interval(BigFloat, -2.0, 0.1)), atan(interval(-0.1, 0.1), interval(-2.0, 0.1))) + @test isequal_interval(interval(-1, 1) * interval(π), atan(interval(-0.1, 0.1), interval(-2.0, 0.1))) + + @test isequal_interval(atan(interval(-0.1, 0.1), interval(0.1, 0.1)), interval(-0.7853981633974484, 0.7853981633974484)) + @test issubset_interval(atan(interval(BigFloat, -0.1, 0.1), interval(BigFloat, 0.1, 0.1)), atan(interval(-0.1, 0.1), interval(0.1, 0.1))) + @test isequal_interval(atan(interval(0.0), interval(-0.0, 0.1)), interval(0.0)) + @test isequal_interval(atan(interval(0.0, 0.1), interval(-0.0, 0.1)), interval(0.0, 1.5707963267948968)) + @test isequal_interval(atan(interval(-0.1, 0.0), interval(0.0, 0.1)), interval(-1.5707963267948968, 0.0)) + @test isequal_interval(atan(interval(-0.1, 0.1), interval(-0.0, 0.1)), interval(-1.5707963267948968, 1.5707963267948968)) + @test issubset_interval(atan(interval(BigFloat, -0.1, 0.1), interval(BigFloat, -0.0, 0.1)), atan(interval(-0.1, 0.1), interval(0.0, 0.1))) + + @test isequal_interval(atan(interval(Float32, -0.1, 0.1), interval(Float32, 0.1, 0.1)), interval(-0.78539824f0, 0.78539824f0)) + @test issubset_interval(atan(interval(-0.1, 0.1), interval(0.1, 0.1)), atan(interval(Float32, -0.1, 0.1), interval(Float32, 0.1, 0.1))) + @test isequal_interval(atan(interval(Float32, 0.0, 0.0), interval(Float32, -0.0, 0.1)), interval(Float32, 0.0, 0.0)) + @test isequal_interval(atan(interval(Float32, 0.0, 0.1), interval(Float32, -0.0, 0.1)), interval(0.0, 1.5707964f0)) + @test isequal_interval(atan(interval(Float32, -0.1, 0.0), interval(Float32, 0.0, 0.1)), interval(-1.5707964f0, 0.0)) + @test isequal_interval(atan(interval(Float32, -0.1, 0.1), interval(Float32, -0.0, 0.1)), interval(-1.5707964f0, 1.5707964f0)) + @test issubset_interval(atan(interval(-0.1, 0.1), interval(-0.0, 0.1)), atan(interval(Float32, -0.1, 0.1), interval(Float32, 0.0, 0.1))) +end + +@testset "trig identities" begin + for a ∈ (interval(17, 19), interval(0.5, 1.2)) + @test issubset_interval(tan(a), sin(a) / cos(a)) + end + @test isequal_interval(sin(interval(-pi/2, 3pi/2)), interval(-1, 1)) + @test isequal_interval(cos(interval(-pi/2, 3pi/2)), interval(-1, 1)) +end + +@testset "large arguments" begin + x = pown(interval(2.), 1000) + @test diam(x) == 0.0 + @test isequal_interval(sin(x), interval(-0.15920170308624246, -0.15920170308624243)) + @test isequal_interval(cos(x), interval(0.9872460775989135, 0.9872460775989136)) + @test isequal_interval(tan(x), interval(-0.16125837995065806, -0.16125837995065803)) + + x = interval(prevfloat(Inf), Inf) + @test isequal_interval(sin(x), interval(-1, 1)) + @test isequal_interval(cos(x), interval(-1, 1)) + @test isequal_interval(tan(x), interval(-Inf, Inf)) +end + +@testset "inverse roots of unity" begin + for i ∈ 0:99 + @test issubset_interval(cispi(-interval(i) / interval(50)), inv(cispi(interval(i) / interval(50)))) + @test radius(inv(cispi(interval(i) / interval(50)))) < 10eps() + end +end + +@testset "complex inverse trig" begin + z = complex(interval(0.1), interval(0.2)) + r = asin(sin(z)) + @test issubset_interval(real(z), real(r)) + @test issubset_interval(imag(z), imag(r)) + r = atan(tan(z)) + @test issubset_interval(real(z), real(r)) + @test issubset_interval(imag(z), imag(r)) + @test in_interval(asin(0.5), real(asin(complex(interval(0.5), interval(0.0))))) + @test in_interval(acos(0.5), real(acos(complex(interval(0.5), interval(0.0))))) + @test in_interval(atan(0.5), real(atan(complex(interval(0.5), interval(0.0))))) +end + +@testset "point enclosure" begin + for x ∈ (bareinterval(-1.2, 1.3), bareinterval(2.0, 7.5)) + for t ∈ range(inf(x), sup(x); length = 7) + @test in_interval(sin(t), sin(x)) + @test in_interval(cos(t), cos(x)) + @test in_interval(tan(t), tan(x)) + @test in_interval(sinpi(t), sinpi(x)) + @test in_interval(cospi(t), cospi(x)) + @test in_interval(atan(t), atan(x)) + end + end + x = bareinterval(-0.9, 0.9) + for t ∈ range(inf(x), sup(x); length = 7) + @test in_interval(asin(t), asin(x)) + @test in_interval(acos(t), acos(x)) + end + y = bareinterval(0.2, 1.7) + x = bareinterval(-2.4, -0.3) + for s ∈ range(inf(y), sup(y); length = 5), t ∈ range(inf(x), sup(x); length = 5) + @test in_interval(atan(s, t), atan(y, x)) + end +end diff --git a/test/intervals/construction.jl b/test/intervals/construction.jl new file mode 100644 index 000000000..9f89f6538 --- /dev/null +++ b/test/intervals/construction.jl @@ -0,0 +1,597 @@ +using Test +using IntervalArithmetic +using IntervalArithmetic: NumTypes, promote_numtype, _unsafe_bareinterval, _round, __round, + __float, _unsafe_interval, setdecoration, atomic, _atomic +using InteractiveUtils + +struct UnsupportedTestIrrational <: AbstractIrrational end + +@testset "NumTypes and promote_numtype" begin + @test NumTypes === Union{Rational,AbstractFloat} + + @test promote_numtype(Float64, Float32) == Float64 + @test promote_numtype(BigFloat, Float64) == BigFloat + @test promote_numtype(Rational{Int}, Float64) == Float64 + @test promote_numtype(Float16, Int8) == Float16 + @test promote_numtype(Int, Int) == Float64 + @test promote_numtype(Bool, Bool) == Float64 + @test promote_numtype(Rational{Int32}, Irrational{:π}) == Rational{Int64} + @test promote_numtype(Irrational{:π}, Rational{Int32}) == Rational{Int64} + @test promote_numtype(Rational{Int64}, Irrational{:π}) == Rational{Int64} + @test promote_numtype(Irrational{:π}, Irrational{:ℯ}) == Float64 +end + +IntervalArithmetic.configure(numtype = Float32) +try + @testset "configured default numtype" begin + @test promote_numtype(Int, Int) == Float32 + @test IntervalArithmetic.default_numtype() == Float32 + end +finally + IntervalArithmetic.configure(numtype = Float64) +end + +@testset "default numtype restored" begin + @test promote_numtype(Int, Int) == Float64 + @test IntervalArithmetic.default_numtype() == Float64 +end + +@testset "Representation invariants of the bounds" begin + @test !(BareInterval{Float64} <: Real) + @test_throws TypeError BareInterval{Int} + @test fieldnames(BareInterval) == (:lo, :hi) + @test fieldtypes(BareInterval{Float64}) == (Float64, Float64) + + for T ∈ (Float16, Float32, Float64, BigFloat, Rational{Int}, Rational{BigInt}) + e = emptyinterval(BareInterval{T}) + @test (e.lo == typemax(T)) & (e.hi == typemin(T)) + @test isempty_interval(e) + @test (inf(e) == typemax(T)) & (sup(e) == typemin(T)) + + f = @test_logs (:warn,) bareinterval(T, 2, 1) + @test (f.lo == typemax(T)) & (f.hi == typemin(T)) + + g = nai(Interval{T}) + @test (g.bareinterval.lo == typemax(T)) & (g.bareinterval.hi == typemin(T)) + end + @test bounds(emptyinterval(BareInterval{Float64})) === (Inf, -Inf) + @test bounds(emptyinterval(BareInterval{Rational{Int64}})) == (1//0, -1//0) + + x = bareinterval(0.0, 1.0) + @test (x.lo === 0.0) & (inf(x) === -0.0) & (sup(x) === 1.0) + y = bareinterval(-1.0, -0.0) + @test (y.hi === 0.0) & (sup(y) === 0.0) + z = bareinterval(-0.0, 0.0) + @test (z.lo === 0.0) & (z.hi === 0.0) + @test (inf(z) === -0.0) & (sup(z) === 0.0) + + @test bounds(x) === (0.0, 1.0) + @test bounds(bareinterval(-0.0, 1.0)) == (0.0, 1.0) + + b = bareinterval(BigFloat, -0.0, 0.0) + @test !signbit(b.lo) + @test bounds(b) == (0.0, 0.0) +end + +@testset "Difference between checked and unchecked bare intervals" begin + @test IntervalArithmetic._unsafe_bareinterval(Float64, 1, 2) === bareinterval(1, 2) + + @test inf(_unsafe_bareinterval(Float64, 3, 2)) == 3 + @test isempty_interval(@test_logs (:warn,) bareinterval(3, 2)) + @test isnai(@test_logs (:warn,) interval(3, 2)) + + x = _unsafe_bareinterval(Float64, 0.1, 0.2) + @test (x.lo === 0.1) & (x.hi === 0.2) + y = _unsafe_bareinterval(Float64, 1//10, 1//5) + @test (inf(y) === 0.09999999999999999) & (sup(y) === 0.2) + + r8 = _unsafe_bareinterval(Rational{Int8}, 1//2, 3//4) + @test (inf(r8) == 1//2) & (sup(r8) == 3//4) + r16 = _unsafe_bareinterval(Rational{Int16}, 1//2, 3//4) + @test (inf(r16) == 1//2) & (sup(r16) == 3//4) +end + +@testset "Directed rounding of the bounds" begin + @test _round(Float64, π, RoundDown) == 3.141592653589793 + @test _round(Float64, π, RoundUp) == 3.1415926535897936 == nextfloat(3.141592653589793) + @test _round(Float64, ℯ, RoundDown) == 2.718281828459045 + @test _round(Float64, ℯ, RoundUp) == 2.7182818284590455 + @test _round(Float64, MathConstants.φ, RoundDown) == 1.6180339887498947 + @test _round(Float64, MathConstants.φ, RoundUp) == 1.618033988749895 + @test _round(Float64, MathConstants.γ, RoundDown) == 0.5772156649015328 + @test _round(Float64, MathConstants.catalan, RoundDown) == 0.915965594177219 + + @test_throws ArgumentError _round(Float64, UnsupportedTestIrrational(), RoundDown) + @test_throws ArgumentError _round(Rational{Int}, UnsupportedTestIrrational(), RoundUp) + + @test _round(Rational{Int64}, π, RoundDown) == 80143857//25510582 + @test _round(Rational{Int64}, π, RoundUp) == 85563208//27235615 + + @test __round(Rational{Int}, 1//3, RoundDown) == __round(Rational{Int}, 1//3, RoundUp) == 1//3 + + @test __float(Int8) == Float16 + @test __float(UInt8) == Float16 + @test __float(Int16) == Float32 + @test __float(UInt16) == Float32 + @test __float(Int32) == Float64 + @test __float(Int64) == Float64 +end + +@testset "bareinterval constructors" begin + x = bareinterval(Rational{Int64}, 0.1) + @test (inf(x) == 1//10) & (sup(x) == 300239975158034//3002399751580339) + @test in_interval(0.1, x) + + # src/intervals/construction.jl: the `rationalize`-based `__round` loses the enclosure for small integer types + y = bareinterval(Rational{Int32}, 0.1) + @test (inf(y) == 1//10) & (sup(y) == 1//10) + @test !in_interval(0.1, y) + z = bareinterval(Rational{Int8}, 0.1) + @test (inf(z) == 1//10) & (sup(z) == 1//10) + + p16 = bareinterval(Rational{Int16}, π) + @test (inf(p16) == 31218//9937) & (sup(p16) == 355//113) + + a = bareinterval(1//1, π) + @test typeof(a) == BareInterval{Rational{Int64}} + @test (inf(a) == 1//1) & (sup(a) == 85563208//27235615) + b = bareinterval(Rational{Int32}, 1//1, π) + @test typeof(b) == BareInterval{Rational{Int32}} + @test (inf(b) == 1//1) & (sup(b) == 85563208//27235615) + c = bareinterval(1, π) + @test typeof(c) == BareInterval{Float64} + @test bounds(c) == (1.0, 3.1415926535897936) + + @test precision(sup(bareinterval(BigFloat, 1, π))) == precision(BigFloat) == 256 + @test precision(inf(bareinterval(BigFloat, 0.1))) == 256 + setprecision(BigFloat, 53) do + @test bounds(bareinterval(BigFloat, 1, π)) == (1.0, 3.1415926535897936) + end + + @test bareinterval(1) === bareinterval(1.0, 1.0) + @test typeof(bareinterval(1)) == BareInterval{Float64} + @test typeof(bareinterval(1//2)) == BareInterval{Rational{Int}} + @test typeof(bareinterval(Float32, 1)) == BareInterval{Float32} + @test bounds(bareinterval(Float32, 0.1)) === (0.099999994f0, 0.1f0) + @test bounds(bareinterval(Float16, 1, π)) === (Float16(1.0), Float16(3.143)) + + @test bareinterval((1, 2)) === bareinterval(1, 2) + @test bareinterval((1,)) === bareinterval(1) + @test typeof(bareinterval(Float32, (1, 2))) == BareInterval{Float32} + @test_throws MethodError bareinterval((1, 2, 3)) + + @test bounds(bareinterval(Float32, bareinterval(0.1))) === (0.099999994f0, 0.1f0) + @test bareinterval(bareinterval(1, 2)) === bareinterval(1, 2) + + @test isempty_interval(@test_logs (:warn, r"ill-formed bare interval") bareinterval(1, -1)) + @test isempty_interval(@test_logs (:warn,) bareinterval(NaN)) + @test isempty_interval(@test_logs (:warn,) bareinterval(1, NaN)) + @test isempty_interval(@test_logs (:warn,) bareinterval(Inf, Inf)) + @test isempty_interval(@test_logs (:warn,) bareinterval(-Inf, -Inf)) + + @test promote_type(BareInterval{Float64}, BareInterval{Float32}) == BareInterval{Float64} + @test convert(BareInterval{Float32}, bareinterval(0.1)) === bareinterval(Float32, 0.1) + @test BareInterval{Float32}(bareinterval(0.1)) === bareinterval(Float32, 0.1) + + @test_throws MethodError bareinterval(Int, 1, 2) + @test_throws MethodError bareinterval(Complex{Float64}, 1, 2) + @test_throws MethodError interval(Int, 1, 2) +end + +@testset "Decoration enum" begin + @test Decoration isa Type + @test (Int(ill), Int(trv), Int(def), Int(dac), Int(com)) == (0, 1, 2, 3, 4) + @test instances(Decoration) == (ill, trv, def, dac, com) + @test trv < def + @test min(com, trv) == trv + @test max(def, dac) == dac + @test Decoration(2) === def + + @test decoration(emptyinterval(BareInterval{Float64})) == trv + @test decoration(bareinterval(1, Inf)) == dac + @test decoration(entireinterval(BareInterval{Float64})) == dac + @test decoration(bareinterval(1, 2)) == com + + @test decoration(nai(Interval{Float64})) == ill + @test decoration(interval(1, 2)) == com + @test decoration(complex(interval(1, 2), interval(1, Inf))) == dac +end + +@testset "Interval type and setdecoration" begin + @test Interval{Float64} <: Real + @test fieldnames(Interval) == (:bareinterval, :decoration, :isguaranteed) + + x = _unsafe_interval(bareinterval(1, 2), def, false) + @test decoration(x) == def + @test !isguaranteed(x) + @test bareinterval(x) === bareinterval(1, 2) + + @test isempty_interval(@test_logs (:warn, r"interval part of NaI") bareinterval(nai(Interval{Float64}))) + @test isempty_interval(@test_logs (:warn,) bareinterval(Float32, nai(Interval{Float64}))) + @test typeof(bareinterval(Float32, interval(1, 2))) == BareInterval{Float32} + + @test decoration(setdecoration(interval(1, 2), def)) == def + @test isnai(setdecoration(interval(1, 2), ill)) + @test decoration(setdecoration(interval(1, 2), ill)) == ill + @test decoration(setdecoration(emptyinterval(Interval{Float64}), com)) == trv + @test decoration(setdecoration(interval(1, Inf), com)) == dac + @test !isguaranteed(setdecoration(convert(Interval{Float64}, 1), def)) + @test isguaranteed(setdecoration(interval(1), def)) + + y = @test_logs (:warn,) interval(NaN) + z = @test_logs (:warn,) setdecoration(y, com) + @test !isnai(z) + @test decoration(z) == trv +end + +@testset "isguaranteed" begin + @test isguaranteed(bareinterval(1)) + @test isguaranteed(interval(1)) + @test !isguaranteed(convert(Interval{Float64}, 1)) + @test !isguaranteed(interval(1) + 0) + @test !isguaranteed(1) + @test !isguaranteed(1.0) + @test !isguaranteed(π) + @test !isguaranteed(complex(interval(1), convert(Interval{Float64}, 2))) + @test isguaranteed(complex(interval(1), interval(2))) +end + +@testset "Basics" begin + @test typeof(interval(1, 2)) == Interval{Float64} + @test typeof(big(interval(1, 2))) == Interval{BigFloat} + for T ∈ (Float16, Float32, Float64, BigFloat) + @test typeof(interval(T, 1, 2)) == Interval{T} + end + for T ∈ [InteractiveUtils.subtypes(Signed) ; InteractiveUtils.subtypes(Unsigned)] + @test typeof(interval(Rational{T}, 1, 2)) == Interval{Rational{T}} + end + @test eltype(interval(1, 2)) == Interval{Float64} + @test IntervalArithmetic.numtype(interval(1, 2)) == Float64 + @test typeof(interval(BigInt(1), 11//10)) == Interval{Rational{BigInt}} + + @test isequal_interval(interval(big"3"), interval(3)) + @test isequal_interval(interval(Float64, big"1e10000"), interval(Float64, big(10)^10000), interval(prevfloat(Inf), Inf)) + + @test inf(interval(1, 2)) == 1 && sup(interval(1, 2)) == 2 + + a = interval(0.1, 1.1) + @test isequal_interval(a, interval(inf(a), sup(a))) + @test isequal_interval(emptyinterval(Rational{Int}), emptyinterval()) + + @test isequal_interval( + interval(Float64, 1, 1), interval(Float64, 1), interval(1), + interval(Float64, interval(1)), interval(interval(1)), + interval(BigFloat, 1, 1), interval(BigFloat, 1), interval(big(1)), + interval(1, 1)) + + @test isequal_interval( + interval(Rational{Int}, 1//10, 1//10), interval(1//10, 1//10), interval(Rational{Int}, 1//10), interval(1//10), + interval(Rational{Int}, interval(1//10)), interval(interval(1//10)), + interval(Rational{BigInt}, 1//10, 1//10), interval(Rational{BigInt}, 1//10), interval(big(1//10))) + + @test_throws MethodError BareInterval(1) + @test_throws MethodError BareInterval{Float64}(1) + @test_throws MethodError BareInterval(1, 2) + @test_throws MethodError BareInterval{Float64}(1, 2) + + @test !isguaranteed(Interval(1)) + @test !isguaranteed(Interval{Float64}(1)) + @test typeof(Interval(1//2)) == Interval{Rational{Int}} + @test_throws MethodError Interval(1, 2) + @test_throws MethodError Interval{Float64}(1, 2) + @test_throws MethodError interval(Float64, com) + + @test isequal_interval( + BareInterval{Float64}(bareinterval(3, 4)), BareInterval{BigFloat}(bareinterval(3, 4)), + BareInterval{Rational{Int}}(bareinterval(3, 4)), BareInterval{Rational{BigInt}}(bareinterval(3, 4)), + bareinterval(3, 4)) + + @test isequal_interval( + Interval{Float64}(interval(3, 4)), Interval{BigFloat}(interval(3, 4)), + Interval{Rational{Int}}(interval(3, 4)), Interval{Rational{BigInt}}(interval(3, 4)), + interval(3, 4)) + + @test isempty_interval(@test_logs (:warn,) bareinterval(2, 1)) + @test isempty_interval(@test_logs (:warn,) bareinterval(Inf)) + @test isempty_interval(@test_logs (:warn,) bareinterval(-Inf)) + @test isempty_interval(@test_logs (:warn,) bareinterval(1, NaN)) + @test isempty_interval(@test_logs (:warn,) bareinterval(NaN)) + + @test isnai(@test_logs (:warn, r"ill-formed interval") interval(2, 1)) + @test isnai(@test_logs (:warn,) interval(Inf)) + @test isnai(@test_logs (:warn,) interval(-Inf)) + @test isnai(@test_logs (:warn,) interval(1, NaN)) + @test isnai(@test_logs (:warn,) interval(NaN, 3)) + @test isnai(@test_logs (:warn,) interval(NaN)) + @test isnai(@test_logs (:warn,) interval(NaN, NaN)) + @test isnai(@test_logs (:warn,) interval(Inf, Inf)) + @test isnai(@test_logs (:warn,) interval(-Inf, -Inf)) + + @test isnai(@test_logs (:warn,) interval(1//0)) + @test isnai(@test_logs (:warn,) interval(-1//0)) + + @test isnai(@test_logs (:warn,) interval(1, 2, ill)) + + @test bounds(interval(typemin(Int64), typemax(Int64))) == (float(typemin(Int64)), float(typemax(Int64))) + + @test !in_interval(1//10, interval(0.1, 0.2)) && in_interval(2//10, interval(0.1, 0.2)) + + @test isequal_interval(interval(1//2), interval(0.5)) + @test inf(interval(1//10)) == 1//10 && sup(interval(1//10)) == 1//10 +end + +@testset "Rational bounds" begin + f = 1 // 3 + g = 1 // 3 + @test isequal_interval(interval(f*g), interval(1//9)) + @test isequal_interval(interval(1//9), interval(1//9, 1//9)) + @test isequal_interval(interval(f, g) - interval(1//1), interval(-2//3, -2//3)) + @test issubset_interval(interval(f*g), interval(1)/interval(9)) +end + +@testset "Irrationals" begin + for irr ∈ (MathConstants.:π, MathConstants.:γ, MathConstants.:catalan, MathConstants.:φ, MathConstants.:ℯ) + for T ∈ (Float16, Float32, Float64, BigFloat) + @test in_interval(irr, interval(T, irr)) + if T !== BigFloat + @test nextfloat(inf(interval(T, irr))) == sup(interval(T, irr)) + end + end + for T ∈ [InteractiveUtils.subtypes(Signed) ; InteractiveUtils.subtypes(Unsigned)] + @test in_interval(irr, interval(Rational{T}, irr)) + end + end +end + +@testset "Midpoint" begin + @test isequal_interval(IntervalArithmetic.Symbols.:±(0.5, 1), + interval(0.5, 1; format = :midpoint), + interval(0.5, 1+0im; format = :midpoint), + interval(0.5, interval(1+0im); format = :midpoint), + interval(-0.5, 1.5)) + + @test isequal_interval(IntervalArithmetic.Symbols.:±(interval(0.5, 1), interval(1, 2)), + interval(interval(0.5, 1), interval(1, 2); format = :midpoint), + interval(-1.5, 3)) + + @test isequal_interval(IntervalArithmetic.Symbols.:±(0.5+im, 1), + interval(0.5+im, 1; format = :midpoint), + interval(0.5+im, 1+0im; format = :midpoint), + interval(interval(0.5+im), interval(1+0im); format = :midpoint), + complex(interval(-0.5, 1.5), interval(0, 2))) + + @test_throws DomainError interval(0.5+im, 1+im; format = :midpoint) + @test_throws DomainError interval(0, -1; format = :midpoint) + @test isequal_interval(interval(1, 0; format = :midpoint), interval(1, 1)) + @test isequal_interval(interval(1, 2; format = :midpoint), interval(-1, 3)) + @test isequal_interval(interval(1; format = :midpoint), interval(1, 1)) + @test_throws ArgumentError interval(1, 2; format = :bad) + @test_throws ArgumentError interval(1; format = :bad) + + cm = interval(complex(1, 2), 1; format = :midpoint) + @test isequal_interval(real(cm), interval(0, 2)) & isequal_interval(imag(cm), interval(1, 3)) + @test isequal_interval(interval(1, complex(1, 0); format = :midpoint), interval(0, 2)) +end + +@testset "Decorations" begin + a = interval(1, 2) + b = interval(1, 2, com) + c = interval(1, 2, dac) + d = interval(a, dac) + + @test decoration(a) == com + @test decoration(b) == com + @test decoration(c) == dac + @test decoration(d) == dac + @test decoration(interval(1, Inf)) == dac + @test decoration(interval(1, 2, def)) == def + + x = @test_logs (:warn,) interval(2, 0.1) + y = @test_logs (:warn,) interval(2, 0.1, com) + @test decoration(x) == decoration(y) == ill + + z = interval(1//1, π) + @test typeof(z) == Interval{Rational{Int64}} + @test (inf(z) == 1//1) & (sup(z) == 85563208//27235615) + @test (decoration(z) == com) & isguaranteed(z) + w = interval(1, π) + @test typeof(w) == Interval{Float64} + @test bounds(w) == (1.0, 3.1415926535897936) + @test (decoration(w) == com) & isguaranteed(w) +end + +@testset "Constructor branches for intervals and mixed arguments" begin + x = interval(interval(1, 2), interval(3, 4)) + @test isequal_interval(x, interval(1, 4)) + @test (decoration(x) == com) & isguaranteed(x) + @test decoration(interval(interval(1, 2, def), interval(3, 4))) == def + + y = interval(entireinterval(), interval(3, 4)) + @test bounds(y) == (-Inf, 4.0) + @test (decoration(y) == dac) & isguaranteed(y) + + e = @test_logs interval(emptyinterval(Interval{Float64}), emptyinterval(Interval{Float64})) + @test isempty_interval(e) & (decoration(e) == trv) & isguaranteed(e) + e_ng = _unsafe_interval(emptyinterval(BareInterval{Float64}), trv, false) + @test !isguaranteed(interval(e_ng, emptyinterval(Interval{Float64}))) + + z = @test_logs (:warn, r"invalid interval") interval(emptyinterval(Interval{Float64}), convert(Interval{Float64}, 1)) + @test isnai(z) & !isguaranteed(z) + + w = @test_logs (:warn, r"invalid interval") interval(interval(3, 4), interval(1, 2)) + @test isnai(w) & isguaranteed(w) + w_ng = @test_logs (:warn,) interval(interval(3, 4, def), convert(Interval{Float64}, 1)) + @test isnai(w_ng) & !isguaranteed(w_ng) + + v = @test_logs (:warn,) interval(interval(1, 2), interval(3, 4), ill) + @test isnai(v) & isguaranteed(v) + v_ng = @test_logs (:warn,) interval(convert(Interval{Float64}, 1), interval(3, 4), ill) + @test isnai(v_ng) & !isguaranteed(v_ng) + + m = interval(convert(Interval{Float64}, 1), 4.0) + @test isequal_interval(m, interval(1, 4)) & !isguaranteed(m) & (decoration(m) == com) + m2 = interval(1.0, convert(Interval{Float64}, 1)) + @test !isguaranteed(m2) + m3 = @test_logs (:warn, r"invalid interval") interval(convert(Interval{Float64}, 2), 1.0) + @test isnai(m3) & isguaranteed(m3) + + @test !isguaranteed(interval(convert(Interval{Float64}, 0), interval(convert(Interval{Float64}, 1)))) + @test !isguaranteed(interval(0, convert(Interval{Float64}, 1))) + @test !isguaranteed(interval(convert(Interval{Float64}, 0), 1)) + + c = interval(complex(1, 2), complex(3, 4)) + @test typeof(c) == Complex{Interval{Float64}} + @test isequal_interval(real(c), interval(1, 3)) & isequal_interval(imag(c), interval(2, 4)) + c2 = interval(complex(1, 2)) + @test isequal_interval(real(c2), interval(1)) & isequal_interval(imag(c2), interval(2)) + c3 = interval(1, complex(3, 4)) + @test isequal_interval(real(c3), interval(1, 3)) & isequal_interval(imag(c3), interval(0, 4)) + + A = interval([1, 2], [3, 4]) + @test A isa Vector{Interval{Float64}} + @test isequal_interval(A[1], interval(1, 3)) & isequal_interval(A[2], interval(2, 4)) + @test interval(Float32[1], Float32[2]) isa Vector{Interval{Float32}} +end + +@testset "Complex" begin + x = interval(1 + 2im) + @test typeof(x) == Complex{Interval{Float64}} + @test isequal_interval(x, complex(interval(1), interval(2))) + + a = interval(1im) + @test typeof(a) == Complex{Interval{Float64}} + @test isequal_interval(a, interval(0) + interval(1)*interval(im)) + + @test isequal_interval(interval(-1 - im, 0), interval(-1 - im, 0 + 0im)) + @test isequal_interval(interval(0, 1 + im), interval(0 + 0im, 1 + im)) +end + +@testset "Conversions and promotions" begin + bx = bareinterval(Float64, π) + by = bareinterval(BigFloat, π) + big_bx, big_by = promote(bx, by) + @test promote_type(typeof(bx), typeof(by)) == typeof(big_bx) == BareInterval{BigFloat} + @test isequal_interval(big_bx, BareInterval{BigFloat}(bx)) + @test isequal_interval(big_by, by) + @test_throws MethodError convert(BareInterval, 1) + @test_throws MethodError convert(BareInterval{Float64}, 1) + + x = interval(Float64, π) + y = interval(BigFloat, π) + big_x, big_y = promote(x, y) + @test promote_type(typeof(x), typeof(y)) == typeof(big_x) == Interval{BigFloat} + @test isequal_interval(big_x, Interval{BigFloat}(x)) + @test isequal_interval(big_y, y) + + @test promote_type(Interval{Float64}, Interval{Float32}) == Interval{Float64} + @test promote_type(Interval{Float64}, Int) == Interval{Float64} + @test promote_type(Int, Interval{Float32}) == Interval{Float32} + @test promote_type(Bool, Interval{Float32}) == Interval{Float32} + @test promote_type(Interval{Float32}, Bool) == Interval{Float32} + @test promote_type(Interval{Float64}, BigFloat) == Interval{BigFloat} + @test promote_type(BigFloat, Interval{Float64}) == Interval{BigFloat} + @test promote_type(Interval{Float64}, Irrational{:π}) == Interval{Float64} + @test promote_type(Irrational{:π}, Interval{Float64}) == Interval{Float64} + + p = promote(interval(1), 1) + @test (p isa Tuple{Interval{Float64},Interval{Float64}}) & isguaranteed(p[1]) & !isguaranteed(p[2]) + + @test isequal_interval(convert(Interval{Float64}, 1), interval(1)) + @test !isguaranteed(convert(Interval{Float64}, 1)) && isguaranteed(interval(1)) + @test isequal_interval(convert(Complex{Interval{Float64}}, 1), interval(1+0im)) + @test !isguaranteed(convert(Complex{Interval{Float64}}, 1)) && isguaranteed(interval(1+0im)) + @test isequal_interval(convert(Complex{Interval{Float64}}, im), interval(im)) + @test !isguaranteed(convert(Complex{Interval{Float64}}, im)) && isguaranteed(interval(im)) + @test isequal_interval(convert(Interval{Float64}, 1+0im), convert(Interval{Float64}, interval(1+0im)), interval(1)) + @test !isguaranteed(convert(Interval{Float64}, 1+0im)) + @test_throws DomainError convert(Interval{Float64}, 1+im) + @test_throws DomainError convert(Interval{Float64}, interval(1+im)) + + @test isguaranteed(convert(Interval{Float64}, π)) + @test bounds(convert(Interval{Float64}, π)) == (3.141592653589793, 3.1415926535897936) + @test isguaranteed(convert(Interval{Float64}, interval(Float32, 1))) + @test isguaranteed(Interval{Float64}(interval(Float32, 1))) + @test isguaranteed(convert(Complex{Interval{Float64}}, interval(1))) + @test isguaranteed(convert(Complex{Interval{Float64}}, π)) + @test isguaranteed(convert(Interval{Float64}, complex(interval(1), interval(0)))) + @test_throws DomainError convert(Interval{Float64}, complex(interval(1), interval(2))) +end + +@testset "atomic" begin + x = atomic(Float64, 0.1) + @test bounds(x) == (0.09999999999999999, 0.1) + @test (decoration(x) == com) & isguaranteed(x) + @test in_interval(1//10, x) + + y = atomic(Float64, 0.3) + @test bounds(y) == (0.3, 0.30000000000000004) + @test in_interval(3//10, y) + + @test isequal_interval(atomic(Float64, 1), interval(1)) + @test isequal_interval(atomic(Float64, π), interval(Float64, π)) + @test isequal_interval(atomic(Float64, interval(1, 2)), interval(1, 2)) + @test isequal_interval(atomic(Float64, "0.1"), atomic(Float64, 0.1)) + @test bounds(atomic(Float32, 0.1)) === (0.099999994f0, 0.1f0) + @test bounds(atomic(Rational{Int64}, 0.1)) == (1//10, 1//10) + @test bounds(atomic(Float64, big"0.1")) == (0.09999999999999999, 0.1) + @test isequal_interval(atomic(0.1), atomic(Float64, 0.1)) +end + +@testset "`@interval` macro" begin + x = 1 + T = Float32 + @test isequal_interval(@interval(sin(1)), sin(interval(1))) + @test bounds(@interval sin(1)) == (0.8414709848078965, 0.8414709848078966) + @test isguaranteed(@interval sin(1)) + @test isequal_interval(@interval(Float32, sin(1)), sin(interval(Float32, 1))) + @test bounds(@interval Float32 sin(1)) === (0.84147096f0, 0.841471f0) + @test isequal_interval(@interval(Float64, x), interval(1)) + @test isequal_interval(@interval(T, sin(x)), sin(interval(Float32, 1))) + @test isequal_interval(@interval(Float64, sin(1), exp(1)), interval(inf(sin(interval(1))), sup(exp(interval(1))))) + @test bounds(@interval sin(1) exp(1)) == (0.8414709848078965, 2.7182818284590455) + @test bounds(@interval Float64 sin(1) exp(1)) == (0.8414709848078965, 2.7182818284590455) + @test isequal_interval(@interval(1, 2), interval(1, 2)) + @test isequal_interval(@interval(x, 2), interval(1, 2)) + @test isequal_interval(@interval(exp(1), exp(1)), exp(interval(1))) + @test isequal_interval(@interval(sin(1), exp(1)), interval(inf(sin(interval(1))), sup(exp(interval(1))))) + @test isequal_interval(@interval(3, sin(5) + 10), interval(3, sup(sin(interval(5)) + interval(10)))) + @test typeof(@interval Float32 1 2) == Interval{Float32} + + @test isequal_interval(@interval(0.1), atomic(Float64, 0.1)) + let x = 3 + @test isequal_interval(@interval(x), interval(3)) + end + @test isequal_interval(@interval(big"0.1"), atomic(Float64, big"0.1")) + A = [1.0, 2.0] + @test isequal_interval(@interval(A[1]), interval(1)) + @test isequal_interval(@interval(Base.MathConstants.pi), atomic(Float64, π)) + @test isequal_interval(@interval(2^3), interval(8)) + @test bounds(@interval sqrt(2)) == (1.414213562373095, 1.4142135623730951) + + @test isnai(@test_logs (:warn,) @interval(2, 1)) + @test isnai(@test_logs (:warn,) @interval(x, sin(x))) + @test isnai(@test_logs (:warn,) @interval(Float64, 2, 1)) + + @test _atomic(Float64, Float32) === Float32 + @test isequal_interval(_atomic(Float64, 0.1), atomic(Float64, 0.1)) +end + +@testset "Loop variables" begin + i = 1 + + @test inf(interval(i, i)) == 1 + @test inf(interval(i)) == 1 + + for i ∈ 1:10 + a = interval(i) + @test inf(a) == i + end +end + +@testset "ExactReal interoperability" begin + x = interval(exact(0.1)) + @test bounds(x) == (0.1, 0.1) + @test isguaranteed(x) + y = interval(exact(1), 2) + @test isequal_interval(y, interval(1, 2)) & isguaranteed(y) +end diff --git a/test/intervals/exact_literals.jl b/test/intervals/exact_literals.jl new file mode 100644 index 000000000..c64eb4e99 --- /dev/null +++ b/test/intervals/exact_literals.jl @@ -0,0 +1,323 @@ +using Test +using IntervalArithmetic + +@testset "ExactReal construction" begin + @test ExactReal{Float64} <: Real + @test fieldnames(ExactReal) == (:value,) + + @test exact(0.5) isa ExactReal{Float64} + @test exact(3).value == 3 + @test IntervalArithmetic._value(exact(3)) == 3 + @test exact(exact(0.5)) === exact(0.5) + + @test_throws MethodError ExactReal{Float64}(1//3) + @test_throws MethodError ExactReal{Float64}(1.0) + @test_throws MethodError ExactReal(1.0) + @test_throws MethodError convert(ExactReal{Float64}, 2) + @test ExactReal{Float64}(exact(0.5)) === exact(0.5) + + c = exact(complex(1, 2)) + @test c isa Complex{ExactReal{Int}} + @test (real(c) === exact(1)) & (imag(c) === exact(2)) + @test exact([1, 2]) isa Vector{ExactReal{Int}} + @test exact([1, 2]) == [exact(1), exact(2)] + + @test isguaranteed(exact(1)) + @test !isguaranteed(1) +end + +@testset "Display" begin + @test repr(exact(0.1)) == "ExactReal{Float64}(0.1000000000000000055511151231257827021181583404541015625)" + @test repr("text/plain", exact(0.1)) == repr(exact(0.1)) + @test repr(exact(1//2)) == "ExactReal{Rational{$Int}}(1//2)" + @test repr(exact(1)) == "ExactReal{$Int}(1)" + + @test has_exact_display(0.5) + @test !has_exact_display(0.1) + @test has_exact_display(1) + @test has_exact_display(1//3) +end + +@testset "Utilities" begin + @test [10, 20, 30][exact(2)] == 20 + + @test zero(ExactReal{Float64}) === exact(0.0) + @test one(ExactReal{Int}) === exact(1) + @test zero(exact(1.0)) === exact(0.0) + @test one(exact(2)) === exact(1) + @test zero(Complex{ExactReal{Float64}}) === complex(exact(0.0), exact(0.0)) + @test one(Complex{ExactReal{Int}}) === complex(exact(1), exact(0)) + @test zero(complex(exact(1.0), exact(2.0))) === complex(exact(0.0), exact(0.0)) + @test one(complex(exact(1), exact(2))) === complex(exact(1), exact(0)) + + @test hash(exact(1)) == hash(1) + + @test !isfinite(exact(Inf)) + @test isinf(exact(Inf)) + @test isinteger(exact(2.0)) + @test isnan(exact(NaN)) +end + +@testset "Conversion and promotion" begin + @test convert(ExactReal{Float64}, exact(0.5)) === exact(0.5) + @test convert(ExactReal{Int}, exact(Int8(1))) === exact(1) + @test convert(ExactReal{Rational{Int}}, exact(2)) === exact(2//1) + + @test_throws ArgumentError promote_type(ExactReal{Float64}, ExactReal{Float32}) + @test promote_type(ExactReal{Int8}, ExactReal{Int16}) == ExactReal{Int16} + @test promote_type(ExactReal{Int}, ExactReal{Rational{Int}}) == ExactReal{Rational{Int}} + + @test bounds(convert(BareInterval{Float64}, exact(0.1))) == (0.1, 0.1) + x = convert(Interval{Float64}, exact(0.1)) + @test (bounds(x) == (0.1, 0.1)) & (decoration(x) == com) & isguaranteed(x) + + @test BareInterval(exact(1)) === bareinterval(1) + @test BareInterval{Float32}(exact(1)) === bareinterval(Float32, 1) + @test Interval(exact(1)) isa Interval{Float64} + @test isguaranteed(Interval(exact(1))) + @test Interval{Float32}(exact(1)) isa Interval{Float32} + + @test isequal_interval(promote(bareinterval(1, 2), exact(3))[2], bareinterval(3)) + @test isequal_interval(promote(interval(1, 2), exact(3))[2], interval(3)) + + @test promote_type(BareInterval{Float64}, ExactReal{Int}) == BareInterval{Float64} + @test promote_type(ExactReal{Int}, BareInterval{Float64}) == BareInterval{Float64} + @test promote_type(Interval{Float64}, ExactReal{Int}) == Interval{Float64} + @test promote_type(ExactReal{Int}, Interval{Float32}) == Interval{Float32} + + @test promote_type(ExactReal{Float64}, Float32) == Float64 + @test promote_type(Float32, ExactReal{Float64}) == Float64 + @test promote_type(ExactReal{Int}, Bool) == Int + @test promote_type(Bool, ExactReal{Int}) == Int + @test promote_type(ExactReal{Int}, BigFloat) == BigFloat + @test promote_type(BigFloat, ExactReal{Int}) == BigFloat + @test promote_type(ExactReal{Int}, Irrational{:π}) == Float64 + @test promote_type(Irrational{:π}, ExactReal{Int}) == Float64 + + @test Float64(exact(1)) === 1.0 + @test Int(exact(2)) === 2 + @test Bool(exact(1)) === true + + c = Complex{Interval{Float64}}(exact(1)) + @test isguaranteed(c) + @test isthinzero(imag(c)) +end + +@testset "Exact arithmetic" begin + @test exact(1)//exact(2) === exact(1//2) + @test exact(1)//2 === 1//2 + @test 1//exact(2) === 1//2 + + @test -exact(0.5) === exact(-0.5) + @test -exact(1//2) === exact(-1//2) + @test -exact(1) === exact(-1) + @test (-exact(big"0.5")) isa ExactReal{BigFloat} + @test (-exact(big"0.5")).value == -0.5 + @test_throws OverflowError -exact(typemin(Int)) + @test -exact(π) === -Float64(π) + + @test exact(1) + exact(2) === exact(3) + @test exact(1) - exact(3) === exact(-2) + @test exact(2) * exact(3) === exact(6) + @test exact(2) + exact(1//2) === exact(5//2) + @test exact(2) * exact(1//2) === exact(1//1) + @test exact(1//2) + exact(1//4) === exact(3//4) + @test exact(1//2) - exact(1//4) === exact(1//4) + @test exact(1//2) * exact(1//2) === exact(1//4) + + @test_throws OverflowError exact(typemax(Int)) + exact(1) + @test_throws OverflowError exact(typemin(Int)) - exact(1) + @test_throws OverflowError exact(typemax(Int)) * exact(2) + @test_throws OverflowError exact(2) ^ exact(1000) + + @test exact(2) ^ exact(3) === exact(8) + @test exact(1//2) ^ exact(3) === exact(1//8) + @test exact(2) ^ 3 === exact(8) + let p = 3 + @test exact(2) ^ p === 8 + end + @test exact(2.0) ^ 2 === 4.0 + @test exact(2.0) ^ exact(3) === 8.0 + if VERSION ≥ v"1.11" + @test_throws DomainError exact(2) ^ (-2) + else + @test_throws InexactError exact(2) ^ (-2) + end + + let x = exact(1.5) + @test x * exact(true) === exact(1.5) + @test x * exact(false) === exact(0.0) + @test x / exact(true) === exact(1.5) + @test exact(true) * x === exact(1.5) + @test exact(true) * exact(true) === exact(true) + @test exact(true) * exact(2) === exact(2) + @test exact(true) / exact(true) === exact(1.0) + @test exact(2) / exact(true) === exact(2.0) + @test exact(1//2) / exact(true) === exact(1//2) + @test exact(true) / exact(1//2) === exact(2//1) + @test exact(true) / exact(2) === exact(0.5) + end + + @test exact(4) / exact(2) === exact(2.0) + @test exact(1) / exact(2) === exact(0.5) + @test exact(1) / exact(4) === exact(0.25) + @test exact(1) / exact(3) === 1/3 + @test exact(1) / exact(Int64(2)^Int64(60) + Int64(1)) === 1/Int64(2)^Int64(60) + @test exact(-Int64(1)) / exact(typemin(Int32)) === exact(-Int64(1)/typemin(Int32)) + @test exact(-1) / exact(typemin(Int)) === -1/typemin(Int) + @test exact(typemin(Int)) / exact(-1) === typemin(Int)/-1 + + @test exact(1//2) / exact(2) === exact(1//4) + @test exact(1//2) / exact(1//4) === exact(2//1) + @test exact(1) / exact(1//4) === exact(4//1) + + @test exact(1.5) + exact(2.0) === 3.5 + @test exact(1.5) * exact(2.0) === 3.0 + @test exact(1.5) - exact(2.0) === -0.5 + @test exact(2.0) ^ exact(3.0) === 8.0 + @test exact(0.5) + exact(0.25) === 0.75 + @test exact(0.5) * exact(0.5) === 0.25 +end + +@testset "Interaction with intervals" begin + x = @exact 0.5 + + @test (2 * x) isa Float64 + @test isone(2 * x) + + @test (bareinterval(2) * x) isa BareInterval + @test isthinone(bareinterval(2) * x) + + @test (interval(2) * x) isa Interval + @test isthinone(interval(2) * x) + @test isguaranteed(interval(2) * x) + + y = interval(1.0, 2.0) + exact(0.5) + z = interval(1.0, 2.0) + 0.5 + @test isguaranteed(y) & !isguaranteed(z) + @test isequal_interval(y, z) +end + +@testset "Exact literals with bare intervals" begin + specialized(f, S, R) = which(f, Tuple{S,R}).sig isa UnionAll + @test all(f -> specialized(f, BareInterval{Float64}, ExactReal{Float64}), (+, -, *, /)) + @test all(f -> specialized(f, ExactReal{Float64}, BareInterval{Float64}), (+, -, *, \)) + @test !specialized(*, BareInterval{Float64}, ExactReal{Int}) + + viapromotion(f, x, y) = f(promote(x, y)...) + samebits(x::BareInterval, y::BareInterval) = (inf(x) === inf(y)) & (sup(x) === sup(y)) + + for T ∈ (Float64, Float32, Rational{Int}) + vals = T <: Rational ? + (zero(T), one(T), -one(T), T(1//2), T(-5//3)) : + (zero(T), -zero(T), one(T), -one(T), T(0.5), T(-2.5), T(0.1), T(-0.1), + floatmax(T), floatmin(T)) + ivs = (bareinterval(T, 1, 2), bareinterval(T, -2, -1), bareinterval(T, -1, 2), + bareinterval(T, 0, 1), bareinterval(T, -1, 0), bareinterval(T, 0, 0), + bareinterval(T(1//10), T(1//10)), bareinterval(T, -Inf, 2), + bareinterval(T, 3, Inf), entireinterval(BareInterval{T}), + emptyinterval(BareInterval{T})) + for v ∈ vals, x ∈ ivs + k = exact(v) + @test samebits(x + k, viapromotion(+, x, k)) + @test samebits(k + x, viapromotion(+, k, x)) + @test samebits(x - k, viapromotion(-, x, k)) + @test samebits(k - x, viapromotion(-, k, x)) + @test samebits(x * k, viapromotion(*, x, k)) + @test samebits(k * x, viapromotion(*, k, x)) + @test samebits(x / k, viapromotion(/, x, k)) + @test samebits(k \ x, viapromotion(\, k, x)) + end + end + + b = bareinterval(1.0, 2.0) + e = exact(0.5) + @test bounds(b + e) == (1.5, 2.5) + @test bounds(b - e) == (0.5, 1.5) + @test bounds(e - b) == (-1.5, -0.5) + @test bounds(b * e) == (0.5, 1.0) + @test bounds(b / e) == (2.0, 4.0) + @test bounds(e \ b) == (2.0, 4.0) + @test samebits(b ^ e, viapromotion(^, b, e)) + @test bounds(bareinterval(1.0, 2.0) * exact(-2.0)) == (-4.0, -2.0) + @test bounds(bareinterval(1.0, 2.0) / exact(-2.0)) == (-1.0, -0.5) + @test bounds(bareinterval(0.1, 0.2) * exact(3.0)) == (0.3, 0.6000000000000001) + + let e = emptyinterval(BareInterval{Float64}) + @test isempty_interval(e + exact(1.0)) + @test isempty_interval(e - exact(1.0)) + @test isempty_interval(e * exact(1.0)) + @test isempty_interval(e / exact(1.0)) + end + + let x = bareinterval(1.0, 2.0) + for v ∈ (Inf, -Inf, NaN) + @test isempty_interval(@test_logs (:warn, r"ill-formed bare interval") x + exact(v)) + @test isempty_interval(@test_logs (:warn,) x - exact(v)) + @test isempty_interval(@test_logs (:warn,) exact(v) - x) + @test isempty_interval(@test_logs (:warn,) x * exact(v)) + @test isempty_interval(@test_logs (:warn,) x / exact(v)) + end + end + + @test bounds(bareinterval(-Inf, 1.0) * exact(0.0)) == (0.0, 0.0) + @test isempty_interval(bareinterval(1.0, 2.0) / exact(0.0)) + + @test bareinterval(1, 2) + exact(1) === bareinterval(2.0, 3.0) + @test (bareinterval(Float32, 1, 2) + exact(1.0f0)) isa BareInterval{Float32} + @test bareinterval(1//2, 3//4) + exact(1//4) === bareinterval(3//4, 1//1) + + @test isequal_interval(bareinterval(1.0, 2.0) * exact(2), bareinterval(2.0, 4.0)) + @test isequal_interval(bareinterval(1.0, 2.0) + exact(1//3), + bareinterval(1.0, 2.0) + bareinterval(Float64, 1//3)) +end + +@testset "@exact macro" begin + @test (@exact 1.5) === exact(1.5) + @test (@exact 1 + 2) === exact(3) + @test (@exact 1//2) === exact(1//2) + @test (@exact [1.0, 2.0]) isa Vector{ExactReal{Float64}} + + @test (@exact 2im) isa Complex{<:ExactReal} + @test (@exact 1.2 + 3.4im) isa Complex{<:ExactReal} + @test_throws ArgumentError (@exact 1.2 + 3im) + + @test (@exact im) === complex(exact(false), exact(true)) + @test (@exact 1 + im) === complex(exact(1), exact(1)) + @test (@exact im + 1) === complex(exact(1), exact(1)) + @test (@exact 1 - im) === complex(exact(1), exact(-1)) + @test (@exact 2im) === complex(exact(0), exact(2)) + @test (@exact 2 * im) === complex(exact(0), exact(2)) + @test (@exact im * 2) === complex(exact(0), exact(2)) + @test (@exact 2.5 * im) === complex(exact(0.0), exact(2.5)) + @test (@exact 1 + 2im) === complex(exact(1), exact(2)) + @test (@exact 1 - 2im) === complex(exact(1), exact(-2)) + @test (@exact 1 + im * 2) === complex(exact(1), exact(2)) + @test (@exact 2im + 1) === complex(exact(1), exact(2)) + @test (@exact im * 2 + 1) === complex(exact(1), exact(2)) + + @exact function f(x) + return x^2 - 2x + 1 + end + + @test f(1.0) isa Real + @test iszero(f(1.0)) + + @test f(bareinterval(1)) isa BareInterval + @test isthinzero(f(bareinterval(1))) + + @test f(interval(1)) isa Interval + @test isthinzero(f(interval(1))) + @test isguaranteed(f(interval(1))) + + g_plain(x) = 1.2 * x + 0.1 + @exact g_exact(x) = 1.2 * x + 0.1 + @test !isguaranteed(g_plain(interval(1, 2))) + @test isguaranteed(g_exact(interval(1, 2))) + @test isequal_interval(g_exact(interval(1, 2)), g_plain(interval(1, 2))) + @test g_exact(1.4) === 1.78 + + h = @exact (x -> 2x + 1) + @test isguaranteed(h(interval(1))) + @test isequal_interval(h(interval(1)), interval(3)) +end diff --git a/test/intervals/flavor.jl b/test/intervals/flavor.jl new file mode 100644 index 000000000..90b2a9c45 --- /dev/null +++ b/test/intervals/flavor.jl @@ -0,0 +1,108 @@ +using Test +using IntervalArithmetic +using IntervalArithmetic: Flavor, zero_times_infinity, div_by_thin_zero, contains_infinity, + is_valid_interval, default_flavor, _unsafe_bareinterval + +@testset "flavor type and configuration" begin + @test Base.issingletontype(Flavor{:set_based}) + @test default_flavor() === Flavor{:set_based}() + @test_throws ArgumentError IntervalArithmetic.configure(flavor = :cset) + @test IntervalArithmetic.configuration_options.flavor == :set_based + @test default_flavor() === Flavor{:set_based}() +end + +@testset "unimplemented flavors" begin + @test_throws MethodError zero_times_infinity(Flavor{:cset}(), Float64) + @test_throws MethodError div_by_thin_zero(Flavor{:cset}(), bareinterval(1, 2)) + @test_throws MethodError contains_infinity(Flavor{:cset}(), bareinterval(1, 2)) + @test_throws MethodError is_valid_interval(Flavor{:cset}(), 1, 2) +end + +@testset "zero_times_infinity" begin + for T ∈ (Float64, Float32, BigFloat, Rational{Int64}) + x = zero_times_infinity(Flavor{:set_based}(), T) + @test typeof(x) === T + @test iszero(x) + @test zero_times_infinity(T) == x + end +end + +@testset "div_by_thin_zero" begin + for x ∈ (bareinterval(1, 2), bareinterval(Float32, 1, 2), bareinterval(1//2, 3//4), + entireinterval(BareInterval{Float64})) + y = div_by_thin_zero(Flavor{:set_based}(), x) + @test typeof(y) === typeof(x) + @test isempty_interval(y) + @test isequal_interval(div_by_thin_zero(x), y) + end +end + +@testset "contains_infinity" begin + for x ∈ (bareinterval(1, 2), bareinterval(-Inf, Inf), bareinterval(1, Inf), + emptyinterval(BareInterval{Float64})) + @test contains_infinity(Flavor{:set_based}(), x) == false + @test contains_infinity(x) == false + end +end + +@testset "is_valid_interval" begin + @test is_valid_interval(1, 2) == true + @test is_valid_interval(2, 1) == false + @test is_valid_interval(1, 1) == true + + @test is_valid_interval(Inf, Inf) == false + @test is_valid_interval(-Inf, -Inf) == false + @test is_valid_interval(-Inf, Inf) == true + @test is_valid_interval(1, Inf) == true + + @test is_valid_interval(NaN, 1) == false + @test is_valid_interval(1, NaN) == false + @test is_valid_interval(NaN, NaN) == false + + @test is_valid_interval(0.0, -0.0) == true + @test is_valid_interval(-0.0, 0.0) == true + + @test is_valid_interval(1//1, 2//1) == true + @test is_valid_interval(2//1, 1//1) == false + @test is_valid_interval(1//2, 1//2) == true + @test is_valid_interval(1//0, 1//0) == false + @test is_valid_interval(-1//0, -1//0) == false + @test is_valid_interval(typemin(Rational{Int64}), typemax(Rational{Int64})) == true + @test is_valid_interval(typemax(Rational{Int64}), typemax(Rational{Int64})) == false + @test is_valid_interval(typemin(Rational{Int64}), typemin(Rational{Int64})) == false + + @test is_valid_interval(1//2, 0.6) == true + @test is_valid_interval(1//0, 1.0) == false + + for (a, b) ∈ ((1, 2), (2, 1), (Inf, Inf), (-Inf, Inf), (NaN, 1), (-0.0, 0.0), + (1//1, 2//1), (2//1, 1//1), (1//0, 1//0), (1//2, 0.6), (1//0, 1.0)) + @test is_valid_interval(a, b) == is_valid_interval(default_flavor(), a, b) + end +end + +@testset "set-based edge cases" begin + @test isempty_interval(bareinterval(0)/bareinterval(0)) + @test isempty_interval(bareinterval(1)/bareinterval(0)) + @test isempty_interval(bareinterval(-Inf, Inf)/bareinterval(0)) + @test isthinzero(bareinterval(0)*bareinterval(-Inf, Inf)) + + a = interval(0.1, 1.1) + @test isequal_interval(inv(zero(a)), emptyinterval()) + @test isequal_interval(interval(0)/interval(0), emptyinterval()) + + @test !in_interval(Inf, interval(1, Inf)) + @test !isbounded(interval(1, Inf)) +end + +@testset "constructor consequences" begin + @test sup(_unsafe_bareinterval(Float64, Inf, Inf)) == Inf + @test isempty_interval(@test_logs (:warn,) bareinterval(Inf, Inf)) + @test isnai(@test_logs (:warn,) interval(Inf, Inf)) + @test isnai(@test_logs (:warn,) interval(1//0, 1//0)) + @test isnai(@test_logs (:warn,) interval(-1//0, -1//0)) + @test isnai(@test_logs (:warn,) interval(1//0, -1//0)) + + x = bareinterval(-Inf, Inf) + @test isequal_interval(x, entireinterval(BareInterval{Float64})) + @test decoration(interval(-Inf, Inf)) == dac +end diff --git a/test/intervals/interval_operations/bisect.jl b/test/intervals/interval_operations/bisect.jl new file mode 100644 index 000000000..ae7e990d4 --- /dev/null +++ b/test/intervals/interval_operations/bisect.jl @@ -0,0 +1,255 @@ +using Test +using IntervalArithmetic + +@testset "bisect bare intervals" begin + x = bareinterval(0, 1) + @test all(isequal_interval.(bisect(x), (bareinterval(0, 0.5), bareinterval(0.5, 1)))) + @test all(isequal_interval.(bisect(x), bisect(x, 0.5))) + @test all(isequal_interval.(bisect(x, 0.25), (bareinterval(0, 0.25), bareinterval(0.25, 1)))) + @test sup(bisect(x)[1]) == inf(bisect(x)[2]) == mid(x, 0.5) + + y = bareinterval(1, 3) + @test all(isequal_interval.(bisect(y, 0), (bareinterval(1, 1), y))) + @test all(isequal_interval.(bisect(y, 1), (y, bareinterval(3, 3)))) + + @test_throws DomainError bisect(x, -0.1) + @test_throws DomainError bisect(x, 1.5) + @test_throws DomainError bisect(x, NaN) + + for z ∈ (bareinterval(1, 1), bareinterval(0.1, nextfloat(0.1))) + @test isatomic(z) + @test all(isequal_interval.(bisect(z), (z, emptyinterval(BareInterval{Float64})))) + end + + e = emptyinterval(BareInterval{Float64}) + @test all(isequal_interval.(bisect(e), (e, e))) + + @test all(isequal_interval.(bisect(entireinterval(BareInterval{Float64})), (bareinterval(-Inf, 0), bareinterval(0, Inf)))) + z = bisect(entireinterval(BareInterval{Float64}), nextfloat(0.5)) + @test sup(z[1]) == inf(z[2]) > 0 + z = bisect(entireinterval(BareInterval{Float64}), prevfloat(0.5)) + @test sup(z[1]) == inf(z[2]) < 0 + + z = bisect(bareinterval(-Inf, 1)) + @test sup(z[1]) == inf(z[2]) == -floatmax(Float64) + z = bisect(bareinterval(1, Inf)) + @test sup(z[1]) == inf(z[2]) == floatmax(Float64) + + @test all(isequal_interval.(bisect(bareinterval(1//1, 2//1)), (bareinterval(1//1, 3//2), bareinterval(3//2, 2//1)))) + + @test bisect(bareinterval(Float32, 0, 1)) isa Tuple{BareInterval{Float32},BareInterval{Float32}} + @test bisect(bareinterval(BigFloat, 0, 1)) isa Tuple{BareInterval{BigFloat},BareInterval{BigFloat}} + + for w ∈ (bareinterval(0, 1), bareinterval(-2, 3), bareinterval(-Inf, 1), entireinterval(BareInterval{Float64}), bareinterval(1//1, 2//1)) + @test isequal_interval(hull(bisect(w)...), w) + end +end + +@testset "bisect decorated intervals" begin + x = emptyinterval() + @test all(isequal_interval.(bisect(x), (x, x))) + + x = I"0.1" + @test isatomic(x) + @test all(isequal_interval.(bisect(x), (x, emptyinterval()))) + + x = interval(0, 1) + @test all(isequal_interval.(bisect(x, 0.5), (interval(0, 0.5), interval(0.5, 1)))) + @test all(isequal_interval.(bisect(x, 0.25), (interval(0, 0.25), interval(0.25, 1)))) + @test all(isequal_interval.(bisect(x), (interval(0, 0.5), interval(0.5, 1)))) + @test all(d -> d == com, decoration.(bisect(x))) + @test all(isguaranteed, bisect(x)) + @test_throws DomainError bisect(x, 2) + + x = interval(-Inf, Inf) + @test all(isequal_interval.(bisect(x, 0.5), (interval(-Inf, 0), interval(0, Inf)))) + @test all(d -> d == dac, decoration.(bisect(x))) + z = bisect(x, nextfloat(0.5)) + @test sup(z[1]) == inf(z[2]) > 0 + z = bisect(x, prevfloat(0.5)) + @test sup(z[1]) == inf(z[2]) < 0 + + x = IntervalArithmetic.setdecoration(interval(0, 1), trv) + @test all(d -> d == trv, decoration.(bisect(x))) + x = IntervalArithmetic.setdecoration(interval(0, 1), def) + @test all(d -> d == def, decoration.(bisect(x))) + + x = convert(Interval{Float64}, 1) + interval(0, 1) + @test all(z -> !isguaranteed(z), bisect(x)) + + z = @test_logs (:warn,) bisect(nai()) + @test isnai(z[1]) & isnai(z[2]) +end + +@testset "bisect vectors of intervals" begin + v = [interval(0, 1), interval(0, 2)] + w = bisect(v, 1, 0.5) + @test all(isequal_interval.(w[1], [interval(0, 0.5), interval(0, 2)])) & + all(isequal_interval.(w[2], [interval(0.5, 1), interval(0, 2)])) + w = bisect(v, 2, 0.5) + @test all(isequal_interval.(w[1], [interval(0, 1), interval(0, 1)])) & + all(isequal_interval.(w[2], [interval(0, 1), interval(1, 2)])) + w = bisect(v, 1, 0.25) + @test all(isequal_interval.(w[1], [interval(0, 0.25), interval(0, 2)])) & + all(isequal_interval.(w[2], [interval(0.25, 1), interval(0, 2)])) + w = bisect(v, 2, 0.25) + @test all(isequal_interval.(w[1], [interval(0, 1), interval(0, 0.5)])) & + all(isequal_interval.(w[2], [interval(0, 1), interval(0.5, 2)])) + w = bisect(v, 1) + @test all(isequal_interval.(w[1], [interval(0, 0.5), interval(0, 2)])) & + all(isequal_interval.(w[2], [interval(0.5, 1), interval(0, 2)])) + w = bisect(v, 2) + @test all(isequal_interval.(w[1], [interval(0, 1), interval(0, 1)])) & + all(isequal_interval.(w[2], [interval(0, 1), interval(1, 2)])) + + @test (w[1] !== v) & (w[2] !== v) + @test all(isequal_interval.(v, [interval(0, 1), interval(0, 2)])) + + b = [bareinterval(0, 1), bareinterval(0, 4)] + w = bisect(b, 2, 0.25) + @test all(isequal_interval.(w[1], [bareinterval(0, 1), bareinterval(0, 1)])) & + all(isequal_interval.(w[2], [bareinterval(0, 1), bareinterval(1, 4)])) + + @test_throws BoundsError bisect(v, 3) + + v = [interval(-Inf, Inf), interval(-Inf, Inf)] + w = bisect(v, 1, 0.5) + @test all(isequal_interval.(w[1], [interval(-Inf, 0), interval(-Inf, Inf)])) & + all(isequal_interval.(w[2], [interval(0, Inf), interval(-Inf, Inf)])) +end + +@testset "mince a single interval" begin + x = bareinterval(0, 1) + v = mince(x, 4) + @test v isa Vector{BareInterval{Float64}} + @test all(isequal_interval.(v, [bareinterval(0, 0.25), bareinterval(0.25, 0.5), bareinterval(0.5, 0.75), bareinterval(0.75, 1)])) + @test all(i -> sup(v[i]) == inf(v[i+1]), 1:3) + @test isequal_interval(reduce(hull, v), x) + @test isequal_interval(only(mince(x, 1)), x) + @test_throws ArgumentError mince(x, 0) + + e = emptyinterval(BareInterval{Float64}) + @test all(isempty_interval, mince(e, 3)) + @test length(mince(e, 3)) == 3 + @test isempty(mince(e, 0)) + @test all(isempty_interval, mince(emptyinterval(), 3)) + @test all(isnai, mince(nai(Float64), 3)) + v = @test_logs mince(nai(), 2) + @test all(isnai, v) + + ng = convert(Interval{Float64}, 1) + mng = mince(emptyinterval(ng), 2) + @test (length(mng) == 2) && all(z -> !isguaranteed(z), mng) + mng = mince(nai(ng), 2) + @test (length(mng) == 2) && all(z -> !isguaranteed(z), mng) + + @test_throws DomainError mince(bareinterval(-Inf, 1), 2) + @test_throws "cannot split an unbounded interval" mince(entireinterval(BareInterval{Float64}), 2) + @test_throws "cannot split an unbounded interval" mince(entireinterval(), 2) + @test_throws "cannot split an unbounded interval" mince(interval(0, Inf), 2) + @test_throws "cannot split an unbounded interval" mince(interval(1, Inf), 2) + @test_throws "cannot split an unbounded interval" mince([interval(0, 1), entireinterval()], 2) + + II = interval(-1, 1) + v = mince(II, 4) + @test v isa Vector{Interval{Float64}} + @test all(isequal_interval.(v, [interval(-1, -0.5), interval(-0.5, 0), interval(0, 0.5), interval(0.5, 1)])) + @test isequal_interval(hull(v...), II) + @test all(d -> d == com, decoration.(v)) + @test all(isguaranteed, v) + v = mince(II, 8) + @test length(v) == 8 + @test isequal_interval(hull(v...), II) + + @test all(z -> !isguaranteed(z), mince(ng + interval(0, 1), 2)) + + for x ∈ (interval(-1, 1), interval(0, 1e300), interval(-1e300, 1e300), interval(3, 3)) + for n ∈ (1, 2, 3, 7) + v = mince(x, n) + @test length(v) == n + @test isequal_interval(reduce(hull, v), x) + @test all(z -> !isnan(inf(z)) & !isnan(sup(z)), v) + end + end + + @test all(isequal_interval.(mince(bareinterval(0//1, 1//1), 4), [bareinterval(0//1, 1//4), bareinterval(1//4, 1//2), bareinterval(1//2, 3//4), bareinterval(3//4, 1//1)])) + @test mince(bareinterval(Float32, 0, 1), 3) isa Vector{BareInterval{Float32}} + @test mince(interval(BigFloat, 0, 1), 3) isa Vector{Interval{BigFloat}} +end + +@testset "mince vectors of intervals" begin + pieces = [interval(-1, -0.5), interval(-0.5, 0), interval(0, 0.5), interval(0.5, 1)] + + x = [interval(0, 1), interval(0, 1)] + v = mince(x, (2, 2)) + @test all(isequal_interval.(v[1], [interval(0, 0.5), interval(0, 0.5)])) & + all(isequal_interval.(v[2], [interval(0.5, 1), interval(0, 0.5)])) & + all(isequal_interval.(v[3], [interval(0, 0.5), interval(0.5, 1)])) & + all(isequal_interval.(v[4], [interval(0.5, 1), interval(0.5, 1)])) + @test all(box -> box isa Vector{Interval{Float64}}, v) + + ib2 = fill(interval(-1, 1), 2) + vb2 = mince(ib2, 4) + @test length(vb2) == 4^2 + vv = [[p₁, p₂] for p₂ ∈ pieces for p₁ ∈ pieces] + @test mapreduce((x, y) -> all(isequal_interval.(x, y)), &, vb2, vv) + @test all(enumerate(ib2)) do (i, xᵢ) + isequal_interval(reduce(hull, [box[i] for box ∈ vb2]), xᵢ) + end + @test mapreduce((x, y) -> all(isequal_interval.(x, y)), &, mince(ib2, (4, 4)), vb2) + vb2bis = mince(ib2, (1, 4)) + @test mapreduce((x, y) -> all(isequal_interval.(x, y)), &, vb2bis, [[interval(-1, 1), p₂] for p₂ ∈ pieces]) + @test all(enumerate(ib2)) do (i, xᵢ) + isequal_interval(reduce(hull, [box[i] for box ∈ vb2bis]), xᵢ) + end + + ib3 = fill(interval(-1, 1), 3) + vb3 = mince(ib3, 4) + @test length(vb3) == 4^3 + @test all(enumerate(ib3)) do (i, xᵢ) + isequal_interval(reduce(hull, [box[i] for box ∈ vb3]), xᵢ) + end + @test mapreduce((x, y) -> all(isequal_interval.(x, y)), &, mince(ib3, (4, 4, 4)), vb3) + vb3bis = mince(ib3, (2, 1, 1)) + @test mapreduce((x, y) -> all(isequal_interval.(x, y)), &, vb3bis, + [[interval(-1, 0), interval(-1, 1), interval(-1, 1)], [interval(0, 1), interval(-1, 1), interval(-1, 1)]]) + @test all(enumerate(ib3)) do (i, xᵢ) + isequal_interval(reduce(hull, [box[i] for box ∈ vb3bis]), xᵢ) + end + + ib4 = fill(interval(-1, 1), 4) + vb4 = mince(ib4, 4) + @test length(vb4) == 4^4 + @test all(enumerate(ib4)) do (i, xᵢ) + isequal_interval(reduce(hull, [box[i] for box ∈ vb4]), xᵢ) + end + @test mapreduce((x, y) -> all(isequal_interval.(x, y)), &, mince(ib4, (4, 4, 4, 4)), vb4) + @test mapreduce((x, y) -> all(isequal_interval.(x, y)), &, mince(ib4, (1, 1, 1, 1)), (ib4,)) + + @test_throws DimensionMismatch mince(ib2, (2, 2, 2)) + + v = mince([emptyinterval(), interval(0, 1)], (1, 2)) + @test length(v) == 2 + @test all(box -> isempty_interval(box[1]), v) +end + +@testset "mince!" begin + v = Vector{BareInterval{Float64}}(undef, 3) + @test mince!(v, bareinterval(0, 1), 3) === v + @test all(isequal_interval.(v, mince(bareinterval(0, 1), 3))) + + w = Vector{Interval{Float64}}(undef, 2) + @test mince!(w, interval(0, 1), 2) === w + @test all(isequal_interval.(w, [interval(0, 0.5), interval(0.5, 1)])) + + x = [interval(0, 1), interval(0, 1)] + u = Vector{Vector{Interval{Float64}}}(undef, 0) + @test mince!(u, x, (2, 2)) === u + @test length(u) == 4 + @test mapreduce((s, t) -> all(isequal_interval.(s, t)), &, u, mince(x, (2, 2))) + @test_throws DimensionMismatch mince!(u, x, (2, 2, 2)) + + u2 = Vector{Vector{Interval{Float64}}}(undef, 0) + @test mince!(u2, x, 2) === u2 + @test mapreduce((s, t) -> all(isequal_interval.(s, t)), &, u2, u) +end diff --git a/test/intervals/interval_operations/boolean.jl b/test/intervals/interval_operations/boolean.jl new file mode 100644 index 000000000..cb6d72a66 --- /dev/null +++ b/test/intervals/interval_operations/boolean.jl @@ -0,0 +1,441 @@ +using Test +using IntervalArithmetic + +@testset "_strictlessprime" begin + @test IntervalArithmetic._strictlessprime(1, 2) + @test !IntervalArithmetic._strictlessprime(2, 1) + @test !IntervalArithmetic._strictlessprime(1, 1) + @test IntervalArithmetic._strictlessprime(Inf, Inf) + @test IntervalArithmetic._strictlessprime(-Inf, -Inf) + @test !IntervalArithmetic._strictlessprime(Inf, -Inf) +end + +@testset "isequal_interval" begin + @test isequal_interval(bareinterval(1, 2), bareinterval(1, 2)) + @test !isequal_interval(bareinterval(1, 2), bareinterval(0, 2)) + @test !isequal_interval(bareinterval(1, 2), bareinterval(1, 3)) + @test isequal_interval(emptyinterval(BareInterval{Float64}), emptyinterval(BareInterval{Float64})) + @test !isequal_interval(emptyinterval(BareInterval{Float64}), bareinterval(1, 2)) + @test isequal_interval(entireinterval(BareInterval{Float64}), entireinterval(BareInterval{Float64})) + @test isequal_interval(bareinterval(-0.0, 1), bareinterval(0.0, 1)) + + @test !isequal_interval(nai(), interval(1, 2)) + @test !isequal_interval(interval(1, 2), nai()) + @test !isequal_interval(nai(), nai()) + @test isequal_interval(emptyinterval(), emptyinterval()) + @test !isequal_interval(emptyinterval(), interval(0.1, 1.1)) + @test !isequal_interval(interval(0.1, 1.1), interval(0.9, 2.0)) + + ng = convert(Interval{Float64}, 1) * interval(1, 2) + @test !isguaranteed(ng) + @test isequal_interval(interval(1, 2), ng) + @test isequal_interval(interval(1, 2), IntervalArithmetic.setdecoration(interval(1, 2), trv)) + + @test isequal_interval(interval(Float32, 1, 2), interval(1, 2)) + @test isequal_interval(emptyinterval(Rational{Int}), emptyinterval()) + + @test isequal_interval(complex(interval(1), interval(2)), complex(interval(1), interval(2))) + @test !isequal_interval(complex(interval(1), interval(2)), complex(interval(1), interval(3))) + @test isequal_interval(complex(interval(1, 2), interval(0)), interval(1, 2)) + @test !isequal_interval(complex(interval(1, 2), interval(0, 1)), interval(1, 2)) + @test isequal_interval(interval(1, 2), complex(interval(1, 2), interval(0))) + @test !isequal_interval(interval(1, 2), complex(interval(1, 2), interval(0, 1))) + + @test isequal_interval([interval(1), interval(2)], [interval(1), interval(2)]) + @test !isequal_interval([interval(1), interval(2)], [interval(1), interval(3)]) + @test_throws DimensionMismatch isequal_interval([interval(1)], [interval(1), interval(2)]) + + a = interval(1, 2) + b = interval(1, 3) + @test isequal_interval(a, a, a) + @test !isequal_interval(a, a, b) + @test isequal_interval(a) isa Base.Fix2 + @test isequal_interval(a)(interval(1, 2)) + @test !isequal_interval(a)(b) + + @test issetequal_interval === isequal_interval +end + +@testset "issubset_interval" begin + @test issubset_interval(bareinterval(1, 2), bareinterval(0, 3)) + @test !issubset_interval(bareinterval(0, 3), bareinterval(1, 2)) + @test issubset_interval(bareinterval(1, 2), bareinterval(1, 2)) + e = emptyinterval(BareInterval{Float64}) + @test issubset_interval(e, bareinterval(1, 2)) + @test !issubset_interval(bareinterval(1, 2), e) + @test issubset_interval(e, e) + @test issubset_interval(entireinterval(BareInterval{Float64}), entireinterval(BareInterval{Float64})) + @test issubset_interval(bareinterval(1, 2), entireinterval(BareInterval{Float64})) + + @test issubset_interval(interval(0.9, 2.0), interval(0.25, 4.0)) + @test issubset_interval(interval(0.9, 2.0), interval(0.9, 2.0)) + @test issubset_interval(emptyinterval(), interval(0.25, 4.0)) + @test !issubset_interval(interval(0.25, 4.0), emptyinterval()) + @test !issubset_interval(nai(), interval(1, 2)) + @test !issubset_interval(interval(1, 2), nai()) + + c = complex(interval(-1, 4), interval(0, 2)) + @test issubset_interval(complex(interval(0), interval(1)), c) + @test !issubset_interval(complex(interval(3), interval(4)), c) + @test issubset_interval(complex(interval(1), interval(0)), interval(0, 2)) + @test !issubset_interval(complex(interval(1), interval(0, 1)), interval(0, 2)) + @test issubset_interval(interval(1), complex(interval(0, 2), interval(-1, 1))) + @test !issubset_interval(interval(1), complex(interval(0, 2), interval(1, 2))) + + @test issubset_interval([interval(1), interval(2)], [interval(0, 2), interval(1, 3)]) + @test !issubset_interval([interval(1), interval(5)], [interval(0, 2), interval(1, 3)]) + @test_throws DimensionMismatch issubset_interval([interval(1)], [interval(1), interval(2)]) + + @test issubset_interval(interval(1, 2), interval(0, 3), interval(-1, 4)) + @test !issubset_interval(interval(1, 2), interval(0, 3), interval(2, 4)) + @test_throws MethodError issubset_interval(interval(1, 2)) +end + +@testset "isstrictsubset" begin + @test isstrictsubset(bareinterval(1, 2), bareinterval(0, 3)) + @test !isstrictsubset(bareinterval(1, 2), bareinterval(1, 2)) + @test isstrictsubset(bareinterval(1, 2), bareinterval(1, 3)) + e = emptyinterval(BareInterval{Float64}) + @test isstrictsubset(e, bareinterval(1, 2)) + @test !isstrictsubset(e, e) + + @test isstrictsubset(interval(1, 2), interval(0, 3)) + @test !isstrictsubset(interval(1, 2), interval(1, 2)) + @test !isstrictsubset(nai(), interval(1, 2)) + @test !isstrictsubset(interval(1, 2), nai()) + + x = complex(interval(1, 2), interval(1, 2)) + @test isstrictsubset(x, complex(interval(1, 2), interval(0, 3))) + @test isstrictsubset(x, complex(interval(0, 3), interval(1, 2))) + @test !isstrictsubset(x, x) + @test isstrictsubset(complex(interval(1), interval(0)), interval(0, 2)) + @test !isstrictsubset(complex(interval(1), interval(0, 1)), interval(0, 2)) + @test isstrictsubset(interval(1), complex(interval(0, 2), interval(-1, 1))) + @test !isstrictsubset(interval(1), complex(interval(0, 2), interval(1, 2))) + + @test isstrictsubset([interval(1, 2), interval(1, 2)], [interval(1, 2), interval(0, 3)]) + @test !isstrictsubset([interval(1, 2), interval(1, 2)], [interval(1, 2), interval(1, 2)]) + + @test isstrictsubset(interval(1, 2), interval(0, 3), interval(-1, 4)) + @test !isstrictsubset(interval(1, 2), interval(0, 3), interval(0, 3)) + @test isstrictsubset(interval(0, 3)) isa Base.Fix2 + @test isstrictsubset(interval(0, 3))(interval(1, 2)) +end + +@testset "isinterior" begin + @test isinterior(bareinterval(1, 2), bareinterval(0, 3)) + @test !isinterior(bareinterval(1, 2), bareinterval(1, 3)) + e = emptyinterval(BareInterval{Float64}) + @test isinterior(e, e) + @test isinterior(e, bareinterval(1, 2)) + @test !isinterior(bareinterval(1, 2), e) + @test isinterior(entireinterval(BareInterval{Float64}), entireinterval(BareInterval{Float64})) + @test isinterior(bareinterval(1, Inf), bareinterval(0, Inf)) + @test isinterior(bareinterval(-Inf, 1), bareinterval(-Inf, 2)) + + @test isinterior(interval(0.9, 2.0), interval(0.25, 4.0)) + @test !isinterior(interval(0.9, 2.0), interval(0.9, 2.0)) + @test isinterior(emptyinterval(), interval(0.25, 4.0)) + @test !isinterior(interval(0.25, 4.0), emptyinterval()) + @test isinterior(emptyinterval(), emptyinterval()) + @test isinterior(interval(-Inf, Inf), interval(-Inf, Inf)) + @test !isinterior(nai(), interval(1, 2)) + @test !isinterior(interval(1, 2), nai()) + + c = complex(interval(-1, 4), interval(0, 2)) + @test isinterior(complex(interval(0), interval(1)), c) + @test !isinterior(complex(interval(3), interval(4)), c) + @test !isinterior(complex(interval(1), interval(0)), interval(0, 2)) + @test isinterior(interval(1, 2), complex(interval(0, 3), interval(-1, 1))) + @test !isinterior(interval(1, 2), complex(interval(0, 3), interval(0, 1))) + + @test isinterior([interval(1), interval(2)], [interval(0, 2), interval(1, 3)]) + @test !isinterior([interval(1), interval(2)], [interval(0, 2), interval(1, 2)]) + @test_throws DimensionMismatch isinterior([interval(1)], [interval(1), interval(2)]) + @test isinterior(interval(1, 2), interval(0, 3), interval(-1, 4)) + @test !isinterior(interval(1, 2), interval(0, 3), interval(0, 3)) +end + +@testset "isdisjoint_interval" begin + @test isdisjoint_interval(bareinterval(1, 2), bareinterval(3, 4)) + @test !isdisjoint_interval(bareinterval(1, 2), bareinterval(2, 3)) + e = emptyinterval(BareInterval{Float64}) + @test isdisjoint_interval(e, bareinterval(1, 2)) + @test isdisjoint_interval(e, e) + @test !isdisjoint_interval(entireinterval(BareInterval{Float64}), entireinterval(BareInterval{Float64})) + @test !isdisjoint_interval(bareinterval(-Inf, 0), bareinterval(0, Inf)) + @test isdisjoint_interval(bareinterval(-Inf, -1), bareinterval(1, Inf)) + + a = interval(0.1, 1.1) + @test isdisjoint_interval(a, I"2.1") + @test !isdisjoint_interval(a, interval(0.9, 2.0)) + @test isdisjoint_interval(emptyinterval(a), a) + @test isdisjoint_interval(emptyinterval(), emptyinterval()) + @test !isdisjoint_interval(nai(), interval(1, 2)) + @test !isdisjoint_interval(interval(1, 2), nai()) + + @test isdisjoint_interval(complex(interval(0), interval(1)), complex(interval(3), interval(4))) + @test !isdisjoint_interval(complex(interval(0, 2), interval(0, 2)), complex(interval(1, 3), interval(1, 3))) + @test isdisjoint_interval(complex(interval(1, 2), interval(1, 2)), interval(1, 2)) + @test !isdisjoint_interval(complex(interval(1, 2), interval(-1, 1)), interval(1, 2)) + @test isdisjoint_interval(interval(1, 2), complex(interval(1, 2), interval(1, 2))) + @test !isdisjoint_interval(interval(1, 2), complex(interval(1, 2), interval(-1, 1))) + + @test isdisjoint_interval([interval(1, 2), interval(1, 2)], [interval(1, 2), interval(5, 6)]) + @test !isdisjoint_interval([interval(1, 2), interval(1, 2)], [interval(1, 2), interval(1, 2)]) + @test_throws DimensionMismatch isdisjoint_interval([interval(1)], [interval(1), interval(2)]) + + @test isdisjoint_interval(interval(1, 2), interval(3, 4), interval(5, 6)) + @test !isdisjoint_interval(interval(1, 2), interval(3, 4), interval(1, 6)) + @test !isdisjoint_interval(interval(1, 2), interval(3, 4), interval(5, 6), interval(1, 2)) + @test IntervalArithmetic._isdisjoint_interval(interval(1, 2)) +end + +@testset "isweakless and isstrictless" begin + @test isweakless(bareinterval(1, 2), bareinterval(1, 3)) + @test !isweakless(bareinterval(1, 2), bareinterval(0, 3)) + e = emptyinterval(BareInterval{Float64}) + @test isweakless(e, e) + + @test isweakless(emptyinterval(), emptyinterval()) + @test !isweakless(interval(1, 2), emptyinterval()) + @test isweakless(interval(-Inf, Inf), interval(-Inf, Inf)) + @test isweakless(interval(0.1, 2), interval(2)) + @test !isweakless(nai(), interval(1, 2)) + @test_throws MethodError isweakless(complex(interval(1), interval(2)), complex(interval(1), interval(2))) + @test_throws MethodError isweakless([interval(1)], [interval(1)]) + + @test !isstrictless(bareinterval(1, 2), bareinterval(1, 3)) + @test isstrictless(bareinterval(1, 2), bareinterval(2, 3)) + @test isstrictless(entireinterval(BareInterval{Float64}), entireinterval(BareInterval{Float64})) + @test isstrictless(bareinterval(-Inf, 1), bareinterval(-Inf, 2)) + + @test isstrictless(interval(0.1, 2), interval(3)) + @test isstrictless(interval(-1), interval(0.1, 2)) + @test !isstrictless(nai(), interval(1, 2)) +end + +@testset "precedes and strictprecedes" begin + @test precedes(bareinterval(1, 2), bareinterval(2, 3)) + @test !precedes(bareinterval(1, 2), bareinterval(1.5, 3)) + + @test precedes(emptyinterval(), emptyinterval()) + @test precedes(interval(3, 4), emptyinterval()) + @test precedes(emptyinterval(), interval(3, 4)) + @test !precedes(interval(0, 2), interval(-Inf, Inf)) + @test precedes(interval(1, 3), interval(3, 4)) + @test !precedes(nai(), interval(1, 2)) + + @test !strictprecedes(bareinterval(1, 2), bareinterval(2, 3)) + @test strictprecedes(bareinterval(1, 2), bareinterval(2.5, 3)) + + @test strictprecedes(interval(3, 4), emptyinterval()) + @test strictprecedes(emptyinterval(), interval(3, 4)) + @test !strictprecedes(interval(-3, -1), interval(-1, 0)) + @test !strictprecedes(nai(), interval(1, 2)) +end + +@testset "in_interval" begin + @test in_interval(1, bareinterval(0, 2)) + @test in_interval(0, bareinterval(0, 2)) + @test in_interval(2, bareinterval(0, 2)) + @test !in_interval(3, bareinterval(0, 2)) + + @test !in_interval(Inf, entireinterval()) + @test !in_interval(-Inf, entireinterval()) + @test !in_interval(Inf, entireinterval(BareInterval{Float64})) + @test !in_interval(Inf, bareinterval(1, Inf)) + @test !in_interval(-Inf, bareinterval(-Inf, 1)) + @test in_interval(0.1, I"0.1") + + @test !in_interval(0, emptyinterval(BareInterval{Float64})) + @test !in_interval(1, emptyinterval()) + + @test in_interval(1 + 0im, bareinterval(0, 2)) + @test !in_interval(1 + 1im, bareinterval(0, 2)) + @test_throws MethodError in_interval(interval(1), bareinterval(0, 2)) + @test_throws ArgumentError in_interval(bareinterval(1, 2), bareinterval(0, 2)) + @test_throws ArgumentError in_interval(interval(3, 4), interval(3, 4)) + + @test !in_interval(1, nai()) + + c = complex(interval(-1, 4), interval(0, 2)) + @test in_interval(3 + 2im, c) + @test !in_interval(3 + 3im, c) + @test in_interval(1, complex(interval(0, 2), interval(-1, 1))) + @test !in_interval(1, complex(interval(0, 2), interval(1, 2))) + + @test in_interval(bareinterval(0, 2)) isa Base.Fix2 + @test in_interval(bareinterval(0, 2))(1) + @test in_interval(interval(0, 2))(1) + + @test in_interval(1//3, bareinterval(0, 1)) + @test in_interval(1//3, bareinterval(0//1, 1//1)) + @test in_interval(big(1)/big(3), bareinterval(BigFloat, 0, 1)) +end + +@testset "isempty_interval and isentire_interval" begin + @test isempty_interval(emptyinterval(BareInterval{Float64})) + @test isempty_interval(IntervalArithmetic._unsafe_bareinterval(Float64, typemax(Float64), typemin(Float64))) + @test !isempty_interval(bareinterval(1, 2)) + @test !isempty_interval(entireinterval(BareInterval{Float64})) + @test isempty_interval(emptyinterval()) + @test !isempty_interval(interval(0.1, 1.1)) + @test !isempty_interval(nai()) + @test isempty_interval(complex(emptyinterval(), interval(1))) + @test !isempty_interval(complex(interval(1), interval(2))) + @test isempty_interval([interval(1), emptyinterval()]) + @test !isempty_interval([interval(1), interval(2)]) + + @test isentire_interval(entireinterval(BareInterval{Float64})) + @test isentire_interval(entireinterval(BareInterval{Rational{Int64}})) + @test !isentire_interval(bareinterval(1, Inf)) + @test !isentire_interval(emptyinterval(BareInterval{Float64})) + @test isentire_interval(entireinterval(interval(0.1, 1.1))) + @test isentire_interval(interval(-Inf, Inf)) + @test !isentire_interval(interval(0.1, 1.1)) + @test !isentire_interval(nai()) + @test isentire_interval(complex(entireinterval(), entireinterval())) + @test !isentire_interval(complex(entireinterval(), interval(1))) +end + +@testset "isnai" begin + @test !isnai(bareinterval(1, 2)) + @test !isnai(emptyinterval(BareInterval{Float64})) + @test isnai(nai()) + @test !isnai(emptyinterval()) + @test !isnai(interval(0.1, 1.1)) + x = @test_logs (:warn,) interval(NaN) + @test isnai(x) + x = @test_logs (:warn,) (:warn,) convert(Interval{Float64}, NaN) + @test isnai(x) + x = @test_logs (:warn,) interval(Inf) + @test isnai(x) + @test isnai(complex(nai(), nai())) + @test !isnai(complex(nai(), interval(1))) +end + +@testset "isbounded, isunbounded and iscommon" begin + @test isbounded(emptyinterval(BareInterval{Float64})) + @test !isbounded(entireinterval(BareInterval{Float64})) + @test !isbounded(bareinterval(1, Inf)) + @test isbounded(bareinterval(1, 2)) + @test !isunbounded(emptyinterval(BareInterval{Float64})) + @test isunbounded(entireinterval(BareInterval{Float64})) + + @test !isunbounded(emptyinterval()) + @test isunbounded(entireinterval()) + @test isunbounded(interval(-Inf, 0)) + @test isunbounded(interval(0, Inf)) + @test !isunbounded(interval(0.1, 1.1)) + @test !isbounded(nai()) + @test !isunbounded(nai()) + @test isbounded(complex(interval(1), interval(2))) + @test !isbounded(complex(interval(1), entireinterval())) + @test isunbounded(complex(interval(1), entireinterval())) + @test !isunbounded(complex(interval(1), interval(2))) + + @test !iscommon(emptyinterval()) + @test !iscommon(entireinterval()) + @test !iscommon(interval(1, Inf)) + @test iscommon(interval(0.1, 1.1)) + @test iscommon(IntervalArithmetic.setdecoration(interval(1, 2), trv)) + @test !iscommon(nai()) + @test !iscommon(emptyinterval(BareInterval{Float64})) + @test !iscommon(entireinterval(BareInterval{Float64})) + @test iscommon(bareinterval(1, 2)) + @test iscommon(complex(interval(1), interval(2))) + @test !iscommon(complex(interval(1), entireinterval())) +end + +@testset "isatomic" begin + @test isatomic(emptyinterval(BareInterval{Float64})) + @test isatomic(bareinterval(1, 1)) + @test isatomic(bareinterval(0.1, nextfloat(0.1))) + @test !isatomic(bareinterval(0, 1)) + @test !isatomic(entireinterval(BareInterval{Float64})) + + @test isatomic(emptyinterval(BareInterval{Rational{Int}})) + @test isatomic(bareinterval(1//2, 1//2)) + @test !isatomic(bareinterval(1//1, 2//1)) + + @test isatomic(interval(1)) + @test isatomic(interval(2.3, 2.3)) + @test isatomic(emptyinterval()) + @test !isatomic(interval(1, 2)) + @test !isatomic(interval(1, nextfloat(1.0, 2))) + @test !isatomic(nai()) + @test isatomic(complex(interval(1), interval(2))) + @test !isatomic(complex(interval(1), interval(1, 2))) +end + +@testset "isthin" begin + @test isthin(bareinterval(1, 1)) + @test !isthin(bareinterval(1, 2)) + @test !isthin(emptyinterval(BareInterval{Float64})) + @test !isthin(nai()) + @test isthin(complex(interval(1), interval(2))) + @test !isthin(complex(interval(1), interval(1, 2))) + + @test isthin(bareinterval(1, 1), 1) + @test !isthin(bareinterval(1, 1), 2) + @test !isthin(bareinterval(1, 2), 1) + @test !isthin(emptyinterval(BareInterval{Float64}), 1) + @test isthin(bareinterval(1, 1), 1 + 0im) + @test !isthin(bareinterval(1, 1), 1 + 1im) + @test_throws ArgumentError isthin(bareinterval(1, 1), interval(1)) + @test isthin(interval(1), 1) + @test !isthin(nai(), 1) + @test isthin(complex(interval(1), interval(0)), 1) + @test !isthin(complex(interval(1), interval(1)), 1) + @test isthin(complex(interval(1), interval(2)), 1 + 2im) +end + +@testset "isthinzero, isthinone and isthininteger" begin + @test isthinzero(bareinterval(0, 0)) + @test isthinzero(bareinterval(-0.0, 0.0)) + @test !isthinzero(bareinterval(0, 1)) + @test !isthinzero(emptyinterval(BareInterval{Float64})) + @test !isthinzero(nai()) + @test isthinzero(interval(0)) + @test isthinzero(interval(Rational{Int}, 0//1)) + @test isthinzero(interval(big(0))) + @test isthinzero(interval(-0.0)) + @test isthinzero(interval(-0.0, 0.0)) + @test !isthinzero(interval(1, 2)) + @test !isthinzero(interval(0.0, nextfloat(0.0))) + @test isthinzero(complex(interval(0), interval(0))) + @test !isthinzero(complex(interval(0), interval(1))) + + @test isthinone(bareinterval(1, 1)) + @test !isthinone(bareinterval(1, 2)) + @test !isthinone(nai()) + @test isthinone(complex(interval(1), interval(0))) + @test !isthinone(complex(interval(1), interval(1))) + + @test isthininteger(bareinterval(2, 2)) + @test !isthininteger(bareinterval(2.5, 2.5)) + @test !isthininteger(bareinterval(1, 2)) + @test !isthininteger(emptyinterval(BareInterval{Float64})) + @test !isthininteger(nai()) + @test isthininteger(bareinterval(2//1, 2//1)) + @test !isthininteger(bareinterval(1//2, 1//2)) + @test isthininteger(complex(interval(2), interval(0))) + @test !isthininteger(complex(interval(2), interval(1))) +end + +@testset "no warning on NaI input" begin + n = nai() + x = interval(1, 2) + for f ∈ (isequal_interval, issubset_interval, isstrictsubset, isinterior, + isdisjoint_interval, isweakless, isstrictless, precedes, strictprecedes) + @test !(@test_logs f(n, x)) + @test !(@test_logs f(x, n)) + end + for f ∈ (isempty_interval, isentire_interval, isbounded, isunbounded, + iscommon, isatomic, isthin, isthinzero, isthinone, isthininteger) + @test !(@test_logs f(n)) + end + @test !(@test_logs in_interval(1, n)) + @test @test_logs isnai(n) +end diff --git a/test/intervals/interval_operations/cancellative.jl b/test/intervals/interval_operations/cancellative.jl new file mode 100644 index 000000000..eab773e1b --- /dev/null +++ b/test/intervals/interval_operations/cancellative.jl @@ -0,0 +1,125 @@ +using Test +using IntervalArithmetic + +@testset "cancelminus bare intervals" begin + z = cancelminus(bareinterval(1, 3), bareinterval(1, 2)) + @test isequal_interval(z, bareinterval(0, 1)) + @test issubset_interval(bareinterval(1, 3), bareinterval(1, 2) + z) + + @test isequal_interval(cancelminus(bareinterval(-5, 1), bareinterval(-1, 5)), bareinterval(-4, -4)) + @test isequal_interval(cancelminus(bareinterval(1, 2), bareinterval(1, 3)), entireinterval(BareInterval{Float64})) + + for x ∈ (bareinterval(1, 3), bareinterval(-2, 5), bareinterval(0.1, 1.1)) + @test isequal_interval(cancelminus(x, x), bareinterval(0, 0)) + end + + e = emptyinterval(BareInterval{Float64}) + entire = entireinterval(BareInterval{Float64}) + @test isequal_interval(cancelminus(e, e), e) + @test isequal_interval(cancelminus(e, bareinterval(1, 2)), e) + @test isequal_interval(cancelminus(e, entire), entire) + @test isequal_interval(cancelminus(bareinterval(1, 2), e), entire) + @test isequal_interval(cancelminus(entire, bareinterval(1, 2)), entire) + @test isequal_interval(cancelminus(bareinterval(1, 2), entire), entire) + @test isequal_interval(cancelminus(bareinterval(-Inf, -1), bareinterval(1, 2)), entire) + @test isequal_interval(cancelminus(bareinterval(1, 2), bareinterval(-1, Inf)), entire) + @test isequal_interval(cancelminus(bareinterval(-5.0, -1.0), bareinterval(-5.1, -1.0)), entire) + + z = cancelminus(bareinterval(-5.1, -0.9), bareinterval(-5.0, -1.0)) + @test issubset_interval(bareinterval(-5.1, -0.9), bareinterval(-5.0, -1.0) + z) + @test bounds(z) == (-0.09999999999999964, 0.09999999999999998) +end + +@testset "cancelminus corner cases" begin + # IEEE 1788-2015 Section 9.2, page 62 + fm = floatmax(Float64) + entire = entireinterval(BareInterval{Float64}) + + @test isequal_interval(cancelminus(bareinterval(fm, fm), bareinterval(-fm, -fm)), bareinterval(fm, Inf)) + @test isequal_interval(cancelminus(bareinterval(-fm, -fm), bareinterval(fm, fm)), bareinterval(-Inf, -fm)) + + @test isequal_interval(cancelminus(bareinterval(-fm, prevfloat(fm)), bareinterval(-fm, fm)), entire) + @test isequal_interval(cancelminus(bareinterval(nextfloat(-fm), fm), bareinterval(-fm, fm)), entire) + @test isequal_interval(cancelminus(bareinterval(-fm, fm), bareinterval(-fm, fm)), bareinterval(0, 0)) + @test isequal_interval(cancelminus(bareinterval(-fm, fm), bareinterval(-fm, prevfloat(fm))), bareinterval(0.0, 0x1p971)) + + a = nextfloat(0.0) + @test isequal_interval(cancelminus(bareinterval(a, a), bareinterval(-a, -a)), bareinterval(2a, 2a)) + b = floatmin(Float64) + @test isequal_interval(cancelminus(bareinterval(b, nextfloat(b, 2)), bareinterval(b, nextfloat(b))), bareinterval(0.0, nextfloat(0.0))) +end + +@testset "cancelminus bound types" begin + @test cancelminus(bareinterval(Float32, 1, 3), bareinterval(1.0, 2.0)) isa BareInterval{Float64} + @test isequal_interval(cancelminus(bareinterval(Float32, 1, 3), bareinterval(1.0, 2.0)), bareinterval(0, 1)) + @test isequal_interval(cancelminus(bareinterval(BigFloat, 1, 3), bareinterval(BigFloat, 1, 2)), bareinterval(BigFloat, 0, 1)) + # `prevfloat` has no `Rational` method, cf. src/intervals/interval_operations/cancellative.jl + @test_throws MethodError cancelminus(bareinterval(1//1, 3//1), bareinterval(1//1, 2//1)) +end + +@testset "cancelplus" begin + @test isequal_interval(cancelplus(bareinterval(1, 3), bareinterval(-2, -1)), bareinterval(0, 1)) + z = cancelplus(bareinterval(1, 3), bareinterval(-2, -1)) + @test issubset_interval(bareinterval(1, 3), z - bareinterval(-2, -1)) + @test isequal_interval(z, cancelminus(bareinterval(1, 3), bareinterval(1, 2))) + + x = interval(-2.0, 4.440892098500622e-16) + y = interval(-4.440892098500624e-16, 2.0) + @test isequal_interval(cancelminus(x, y), entireinterval(Float64)) + @test isequal_interval(cancelplus(x, y), entireinterval(Float64)) + x = interval(-big(1.0), eps(big(1.0))/4) + y = interval(-eps(big(1.0))/2, big(1.0)) + @test isequal_interval(cancelminus(x, y), entireinterval(BigFloat)) + @test isequal_interval(cancelplus(x, y), entireinterval(BigFloat)) + x = interval(-big(1.0), eps(big(1.0))/2) + y = interval(-eps(big(1.0))/2, big(1.0)) + @test issubset_interval(cancelminus(x, y), interval(-one(BigFloat), one(BigFloat))) + @test isequal_interval(cancelplus(x, y), interval(zero(BigFloat), zero(BigFloat))) + @test isequal_interval(cancelminus(emptyinterval(), emptyinterval()), emptyinterval()) + @test isequal_interval(cancelplus(emptyinterval(), emptyinterval()), emptyinterval()) + @test isequal_interval(cancelminus(emptyinterval(), interval(0.0, 5.0)), emptyinterval()) + @test isequal_interval(cancelplus(emptyinterval(), interval(0.0, 5.0)), emptyinterval()) + @test isequal_interval(cancelminus(entireinterval(), interval(0.0, 5.0)), entireinterval()) + @test isequal_interval(cancelplus(entireinterval(), interval(0.0, 5.0)), entireinterval()) + @test isequal_interval(cancelminus(interval(5.0), interval(-Inf, 0.0)), entireinterval()) + @test isequal_interval(cancelplus(interval(5.0), interval(-Inf, 0.0)), entireinterval()) + @test isequal_interval(cancelminus(interval(0.0, 5.0), emptyinterval()), entireinterval()) + @test isequal_interval(cancelplus(interval(0.0, 5.0), emptyinterval()), entireinterval()) + @test isequal_interval(cancelminus(interval(0.0), interval(0.0, 1.0)), entireinterval()) + @test isequal_interval(cancelplus(interval(0.0), interval(0.0, 1.0)), entireinterval()) + @test isequal_interval(cancelminus(interval(0.0), interval(1.0)), interval(-1.0)) + @test isequal_interval(cancelplus(interval(0.0), interval(1.0)), interval(1.0)) + @test isequal_interval(cancelminus(interval(-5.0, 0.0), interval(0.0, 5.0)), interval(-5.0)) + @test isequal_interval(cancelplus(interval(-5.0, 0.0), interval(0.0, 5.0)), interval(0.0)) +end + +@testset "decoration and guarantee" begin + @test decoration(cancelminus(interval(1, 3), interval(1, 2))) == trv + @test decoration(cancelminus(interval(1, 3), interval(1, 2); dec = :default)) == trv + @test decoration(cancelminus(interval(1, 3), interval(1, 2); dec = :auto)) == com + @test decoration(cancelminus(interval(1, 3), IntervalArithmetic.setdecoration(interval(1, 2), def); dec = :auto)) == def + @test decoration(cancelminus(interval(1, 3), interval(1, 2); dec = com)) == com + @test decoration(cancelminus(interval(1, 3), interval(1, 2); dec = def)) == def + @test isnai(cancelminus(interval(1, 3), interval(1, 2); dec = ill)) + @test decoration(cancelminus(interval(1, 2), interval(1, 3); dec = com)) == dac + @test decoration(cancelminus(emptyinterval(), interval(1, 2); dec = com)) == trv + @test_throws ArgumentError cancelminus(interval(1, 3), interval(1, 2); dec = :bogus) + + r = @test_logs (:warn,) cancelminus(nai(), interval(1, 2)) + @test isnai(r) + r = @test_logs (:warn,) cancelminus(interval(1, 2), nai()) + @test isnai(r) + + @test isguaranteed(cancelminus(interval(1, 3), interval(1, 2))) + @test !isguaranteed(cancelminus(interval(1, 3), convert(Interval{Float64}, 1))) + @test !isguaranteed(cancelminus(convert(Interval{Float64}, 1), interval(1, 3))) + + @test decoration(cancelplus(interval(1, 3), interval(-2, -1); dec = :auto)) == com + @test decoration(cancelplus(interval(1, 3), interval(-2, -1); dec = def)) == def + @test isnai(cancelplus(interval(1, 3), interval(-2, -1); dec = ill)) + + z = complex(interval(1), interval(2)) + @test_throws MethodError cancelminus(z, z) + @test_throws MethodError cancelplus(z, z) + @test_throws MethodError cancelminus([interval(1)], [interval(1)]) +end diff --git a/test/intervals/interval_operations/constants.jl b/test/intervals/interval_operations/constants.jl new file mode 100644 index 000000000..bef2855e3 --- /dev/null +++ b/test/intervals/interval_operations/constants.jl @@ -0,0 +1,133 @@ +using Test +using IntervalArithmetic + +@testset "emptyinterval" begin + @test bounds(emptyinterval(BareInterval{Float64})) === (Inf, -Inf) + @test bounds(emptyinterval(BareInterval{Float32})) === (Inf32, -Inf32) + @test bounds(emptyinterval(BareInterval{Rational{Int64}})) == (1//0, -1//0) + for T ∈ (Float16, Float32, Float64, BigFloat, Rational{Int32}, Rational{Int64}) + @test isempty_interval(emptyinterval(BareInterval{T})) + end + @test emptyinterval(bareinterval(1, 2)) === emptyinterval(BareInterval{Float64}) + + x = emptyinterval(Interval{Float64}) + @test decoration(x) == trv + @test isguaranteed(x) + @test isempty_interval(x) + @test emptyinterval(interval(1, 2)) === x + + ng = convert(Interval{Float64}, 1) + @test !isguaranteed(emptyinterval(ng)) + @test isempty_interval(emptyinterval(ng)) + + @test emptyinterval(Float32) isa Interval{Float32} + @test emptyinterval(Rational{Int}) isa Interval{Rational{Int}} + @test emptyinterval(1.0) isa Interval{Float64} + @test emptyinterval(1//2) isa Interval{Rational{Int}} + + z = emptyinterval(Complex{Interval{Float64}}) + @test z isa Complex{Interval{Float64}} + @test isempty_interval(real(z)) & isempty_interval(imag(z)) + @test emptyinterval(Complex{Float64}) isa Complex{Interval{Float64}} + @test emptyinterval(complex(1.0, 2.0)) isa Complex{Interval{Float64}} + z = emptyinterval(complex(interval(1), ng)) + @test isguaranteed(real(z)) & !isguaranteed(imag(z)) + + @test typeof(emptyinterval()) == Interval{Float64} + @test isequal_interval(emptyinterval(Rational{Int}), emptyinterval()) + @test_throws MethodError emptyinterval(Int) +end + +@testset "entireinterval" begin + @test bounds(entireinterval(BareInterval{Float64})) === (-Inf, Inf) + @test bounds(entireinterval(BareInterval{Rational{Int64}})) == (-1//0, 1//0) + @test isentire_interval(entireinterval(BareInterval{Float64})) + @test entireinterval(bareinterval(1, 2)) === entireinterval(BareInterval{Float64}) + + x = entireinterval(Interval{Float64}) + @test decoration(x) == dac + @test isguaranteed(x) + @test isentire_interval(x) + + a = interval(0.1, 1.1) + @test isentire_interval(entireinterval(a)) + @test isequal_interval(entireinterval(Float64), interval(-Inf, Inf)) + @test isentire_interval(interval(-Inf, Inf)) + @test !isentire_interval(a) + + ng = convert(Interval{Float64}, 1) + @test !isguaranteed(entireinterval(ng)) + + @test entireinterval(Float32) isa Interval{Float32} + @test entireinterval(1.0) isa Interval{Float64} + @test entireinterval(Complex{Float64}) isa Complex{Interval{Float64}} + z = entireinterval(complex(interval(1), ng)) + @test isguaranteed(real(z)) & !isguaranteed(imag(z)) + @test isentire_interval(entireinterval(Complex{Interval{Float64}})) + + @test entireinterval() === entireinterval(Interval{Float64}) + @test bounds(entireinterval()) === (-Inf, Inf) + @test decoration(entireinterval()) == dac + + @test !in_interval(Inf, entireinterval()) + @test !in_interval(-Inf, entireinterval()) +end + +@testset "nai" begin + x = nai(Interval{Float64}) + @test decoration(x) == ill + @test isguaranteed(x) + b = @test_logs (:warn,) bareinterval(x) + @test isempty_interval(b) + + @test isnai(nai()) + @test !isempty_interval(nai()) + @test !isequal_interval(nai(), nai()) + @test isnan(inf(nai(BigFloat))) + + ng = convert(Interval{Float64}, 1) + @test !isguaranteed(nai(ng)) + @test isnai(nai(ng)) + + @test nai(Float32) isa Interval{Float32} + @test numtype(nai(Interval{Float32})) == Float32 + @test nai(1.0) isa Interval{Float64} + + z = nai(Complex{Interval{Float64}}) + @test z isa Complex{Interval{Float64}} + @test isnai(z) + @test isnai(nai(Complex{Float64})) + z = nai(complex(interval(1), ng)) + @test isnai(z) + @test isguaranteed(real(z)) & !isguaranteed(imag(z)) + + @test nai() === nai(Interval{Float64}) + + @test_throws MethodError nai(BareInterval{Float64}) + @test_throws MethodError nai(bareinterval(1, 2)) +end + +@testset "decorations and predicates of the constants" begin + @test decoration(emptyinterval()) == trv + @test decoration(entireinterval()) == dac + @test decoration(nai()) == ill + @test decoration(emptyinterval(BareInterval{Float64})) == trv + @test decoration(entireinterval(BareInterval{Float64})) == dac + + @test !iscommon(emptyinterval()) + @test !iscommon(entireinterval()) + @test !iscommon(nai()) + @test isbounded(emptyinterval()) + @test !isbounded(entireinterval()) + @test !isbounded(nai()) +end + +@testset "round-trip through the type constructors" begin + for x ∈ (bareinterval(1, 2), interval(1, 2), complex(interval(1), interval(2)), 1.0, 1//2, complex(1.0, 2.0)) + @test emptyinterval(typeof(x)) === emptyinterval(x) + @test entireinterval(typeof(x)) === entireinterval(x) + end + for x ∈ (interval(1, 2), complex(interval(1), interval(2)), 1.0, complex(1.0, 2.0)) + @test nai(typeof(x)) === nai(x) + end +end diff --git a/test/intervals/interval_operations/extended_div.jl b/test/intervals/interval_operations/extended_div.jl new file mode 100644 index 000000000..d76f1d02d --- /dev/null +++ b/test/intervals/interval_operations/extended_div.jl @@ -0,0 +1,106 @@ +using Test +using IntervalArithmetic + +@testset "extended_div bare intervals" begin + e = emptyinterval(BareInterval{Float64}) + entire = entireinterval(BareInterval{Float64}) + + r = extended_div(bareinterval(1, 2), bareinterval(3, 4)) + @test isequal_interval(r[1], bareinterval(1, 2) / bareinterval(3, 4)) + @test bounds(r[1]) == (0.25, nextfloat(2/3)) + @test in_interval(2//3, r[1]) + @test isempty_interval(r[2]) + @test isequal_interval(extended_div(bareinterval(1, 1), bareinterval(3, 3))[1], bareinterval(1, 1) / bareinterval(3, 3)) + + @test all(isequal_interval.(extended_div(bareinterval(1, 2), bareinterval(-1, 1)), (bareinterval(-Inf, -1), bareinterval(1, Inf)))) + @test all(isequal_interval.(extended_div(bareinterval(-2, -1), bareinterval(-1, 1)), (bareinterval(-Inf, -1), bareinterval(1, Inf)))) + @test all(isequal_interval.(extended_div(bareinterval(1, 2), bareinterval(0, 1)), (bareinterval(1, Inf), e))) + @test all(isequal_interval.(extended_div(bareinterval(1, 2), bareinterval(-1, 0)), (bareinterval(-Inf, -1), e))) + @test all(isequal_interval.(extended_div(bareinterval(-1, 1), bareinterval(-1, 1)), (entire, e))) + @test all(isequal_interval.(extended_div(bareinterval(0, 0), bareinterval(0, 0)), (entire, e))) + @test all(isequal_interval.(extended_div(bareinterval(1, 2), e), (e, e))) + @test all(isequal_interval.(extended_div(e, bareinterval(1, 2)), (e, e))) + @test all(isequal_interval.(extended_div(bareinterval(1, 2), bareinterval(0, 0)), (e, e))) + @test all(isequal_interval.(extended_div(entire, bareinterval(-1, 1)), (entire, e))) + + for (x, y) ∈ ((bareinterval(1, 2), bareinterval(-1, 1)), (bareinterval(-2, -1), bareinterval(-2, 4)), + (bareinterval(1, 2), bareinterval(3, 4)), (bareinterval(-1, 1), bareinterval(-1, 1))) + @test issubset_interval(x / y, hull(extended_div(x, y)...)) + end + + @test_throws MethodError extended_div(bareinterval(Float32, 1, 2), bareinterval(1.0, 2.0)) + + @test all(isequal_interval.(extended_div(bareinterval(Float32, 1, 2), bareinterval(Float32, -1, 1)), + (bareinterval(Float32, -Inf, -1), bareinterval(Float32, 1, Inf)))) + @test all(isequal_interval.(extended_div(bareinterval(BigFloat, 1, 2), bareinterval(BigFloat, -1, 1)), + (bareinterval(BigFloat, -Inf, -1), bareinterval(BigFloat, 1, Inf)))) + @test all(isequal_interval.(extended_div(bareinterval(1//1, 2//1), bareinterval(-1//1, 1//1)), + (bareinterval(-1//0, -1//1), bareinterval(1//1, 1//0)))) +end + +@testset "extended_div decorated intervals" begin + a = interval(0.1, 1.1) + c = interval(0.25, 4.0) + @test all(isequal_interval.(extended_div(interval(-30.0, -15.0), interval(-5.0, -3.0)), (interval(3.0, 10.0), emptyinterval(c)))) + @test all(isequal_interval.(extended_div(interval(-30, -15), interval(-5, -3)), (interval(3.0, 10.0), emptyinterval(c)))) + @test all(isequal_interval.(extended_div(interval(1.0, 2.0), interval(0.1, 1.0)), (interval(1, 20.0), emptyinterval(c)))) + @test all(isequal_interval.(extended_div(a, c), (interval(0.025, 4.4e+00), emptyinterval(c)))) + @test all(isequal_interval.(extended_div(c, interval(4.0)), (interval(6.25e-02, 1e+00), emptyinterval(c)))) + @test all(isequal_interval.(extended_div(c, zero(c)), (emptyinterval(c), emptyinterval(c)))) + @test all(isequal_interval.(extended_div(interval(0.0, 1.0), interval(0.0, 1.0)), (entireinterval(c), emptyinterval(c)))) + @test all(isequal_interval.(extended_div(interval(-1.0, 1.0), interval(0.0, 1.0)), (entireinterval(c), emptyinterval(c)))) + @test all(isequal_interval.(extended_div(interval(-1.0, 1.0), interval(-1.0, 1.0)), (entireinterval(c), emptyinterval(c)))) + @test all(isequal_interval.(extended_div(interval(1.0, 2.0), interval(-4.0, 4.0)), (interval(-Inf, -0.25), interval(0.25, Inf)))) + @test all(isequal_interval.(extended_div(interval(-2.0, -1.0), interval(-2.0, 4.0)), (interval(-Inf, -0.25), interval(0.5, Inf)))) + @test all(isequal_interval.(extended_div(interval(0.0, 0.0), interval(-1.0, 1.0)), (entireinterval(c), emptyinterval(c)))) +end + +@testset "extended_div decorations" begin + # IEEE 1788-2015 Section 10.5.5 + r = extended_div(interval(1, 2), interval(3, 4)) + @test decoration(r[1]) == com + @test decoration(r[2]) == trv + r = extended_div(interval(-1.0, 1.0), interval(3.0, 4.0)) + @test decoration(r[1]) == com + @test decoration(r[2]) == trv + r = extended_div(interval(-30.0, -15.0), interval(-5.0, -3.0)) + @test decoration(r[1]) == com + @test decoration(r[2]) == trv + r = extended_div(interval(1, 2), interval(0, 4)) + @test decoration(r[1]) == trv + @test isequal_interval(r[1], interval(0.25, Inf)) + r = extended_div(interval(1.0, 2.0), interval(-4.0, 4.0)) + @test decoration(r[1]) == trv + @test decoration(r[2]) == trv + r = extended_div(interval(-1.0, 1.0), interval(-1.0, 1.0)) + @test decoration(r[1]) == trv + @test decoration(r[2]) == trv + r = extended_div(interval(1.0, 2.0), interval(0.0, 1.0)) + @test decoration(r[1]) == trv + @test decoration(r[2]) == trv + r = extended_div(interval(-1.0, 1.0, trv), interval(3.0, 4.0)) + @test decoration(r[1]) == trv + @test decoration(r[2]) == trv + r = extended_div(interval(-1.0, 1.0, def), interval(3.0, 4.0)) + @test decoration(r[1]) == def + @test decoration(r[2]) == trv + + r = @test_logs (:warn,) extended_div(nai(), interval(1, 2)) + @test isnai(r[1]) & isnai(r[2]) + r = @test_logs (:warn,) extended_div(interval(1, 2), nai()) + @test isnai(r[1]) & isnai(r[2]) + + r = extended_div(interval(1, 2), convert(Interval{Float64}, 4)) + @test !isguaranteed(r[1]) & !isguaranteed(r[2]) + r = extended_div(convert(Interval{Float64}, 1), interval(3, 4)) + @test !isguaranteed(r[1]) & !isguaranteed(r[2]) + r = extended_div(interval(1, 2), interval(3, 4)) + @test isguaranteed(r[1]) & isguaranteed(r[2]) + + @test extended_div(interval(1, 2), interval(3, 4)) isa Tuple{Interval{Float64},Interval{Float64}} + @test extended_div(interval(Float32, 1, 2), interval(Float32, 3, 4)) isa Tuple{Interval{Float32},Interval{Float32}} + + z = complex(interval(1), interval(2)) + @test_throws MethodError extended_div(z, z) + @test_throws MethodError extended_div([interval(1)], [interval(1)]) +end diff --git a/test/intervals/interval_operations/numeric.jl b/test/intervals/interval_operations/numeric.jl new file mode 100644 index 000000000..5c21db5fc --- /dev/null +++ b/test/intervals/interval_operations/numeric.jl @@ -0,0 +1,257 @@ +using Test +using IntervalArithmetic + +@testset "inf and sup" begin + @test inf(bareinterval(0, 1)) === -0.0 + @test signbit(inf(bareinterval(0, 1))) + @test inf(bareinterval(1, 2)) == 1.0 + @test inf(bareinterval(-1, 0)) == -1.0 + x = inf(bareinterval(BigFloat, 0, 1)) + @test (x isa BigFloat) & iszero(x) & signbit(x) + @test inf(bareinterval(0//1, 1//1)) == 0//1 + @test !signbit(inf(bareinterval(0//1, 1//1))) + @test inf(emptyinterval(BareInterval{Float64})) == Inf + @test isnan(inf(nai())) + @test_throws ArgumentError inf(nai(Interval{Rational{Int}})) + @test inf(0) === -0.0 + @test inf(1.5) == 1.5 + @test inf(2.5) == 2.5 + + a = interval(0.1, 1.1) + @test inf(a) == inf(bareinterval(a)) + @test sup(a) == sup(bareinterval(a)) + @test inf(emptyinterval(a)) == Inf + @test sup(emptyinterval(a)) == -Inf + @test inf(entireinterval(a)) == -Inf + @test sup(entireinterval(a)) == Inf + @test isnan(sup(nai(BigFloat))) + + @test sup(bareinterval(1, 2)) == 2.0 + @test sup(bareinterval(-1, 0)) === 0.0 + @test !signbit(sup(bareinterval(-1, 0))) + @test sup(emptyinterval(BareInterval{Float64})) == -Inf + @test isnan(sup(nai())) + @test_throws ArgumentError sup(nai(Interval{Rational{Int}})) + @test sup(2) == 2.0 +end + +@testset "bounds" begin + @test bounds(bareinterval(0, 1)) === (0.0, 1.0) + @test bounds(bareinterval(0.0, 1.0)) === (0.0, 1.0) + @test bounds(emptyinterval(BareInterval{Float64})) === (Inf, -Inf) + @test all(isnan, bounds(nai())) + @test_throws ArgumentError bounds(nai(Interval{Rational{Int}})) + @test bounds(1.5) === (1.5, 1.5) + @test bounds(interval(0, 1)) === (0.0, 1.0) +end + +@testset "mid" begin + @test mid(bareinterval(1, 2)) == 1.5 + @test mid(bareinterval(1, 2), 0.25) == 1.25 + @test mid(bareinterval(1, 2)) == mid(bareinterval(1, 2), 0.5) + @test mid(interval(1, 2)) == 1.5 + @test mid(interval(0.1, 0.3)) == 0.2 + @test mid(interval(-10, 5)) == -2.5 + @test mid(interval(2), 0.4969816845401611) == 2 + @test mid(interval(0, 1), 0.75) == 0.75 + @test mid(interval(0, 1000), 0.125) == 125 + + @test_throws DomainError mid(bareinterval(1, 2), 1.2) + @test_throws DomainError mid(bareinterval(1//1, 2//1), 3//2) + @test_throws DomainError mid(interval(1, 2), 1.2) + @test_throws DomainError mid(interval(1, 2), -0.7) + @test_throws DomainError mid(interval(Rational{Int}, 1, 2), -1//2) + + @test isnan(mid(emptyinterval(BareInterval{Float64}))) + @test isnan(mid(emptyinterval())) + @test_throws ArgumentError mid(emptyinterval(BareInterval{Rational{Int}})) + @test isnan(mid(nai())) + @test_throws ArgumentError mid(nai(Interval{Rational{Int}})) + + @test mid(entireinterval(BareInterval{Float64})) == 0.0 + @test mid(entireinterval()) == 0.0 + @test mid(entireinterval(BareInterval{Float64}), 0.75) == prevfloat(typemax(Float64)) == floatmax(Float64) + @test mid(entireinterval(BareInterval{Float64}), 0.25) == nextfloat(typemin(Float64)) == -floatmax(Float64) + @test mid(interval(-Inf, Inf), 0.75) > 0 + @test mid(interval(-Inf, Inf), 0.25) < 0 + @test mid(entireinterval(BareInterval{Rational{Int64}})) == 0//1 + @test mid(entireinterval(BareInterval{Rational{Int64}}), 3//4) == Rational{Int64}(typemax(Int64)) + @test mid(entireinterval(BareInterval{Rational{Int64}}), 1//4) == Rational{Int64}(typemin(Int64)) + + for α ∈ (0.25, 0.5, 0.75) + @test mid(bareinterval(-Inf, 1), α) == -floatmax(Float64) + @test mid(bareinterval(1, Inf), α) == floatmax(Float64) + end + @test mid(interval(-Inf, 1)) == nextfloat(-Inf) + @test mid(interval(1, Inf)) == prevfloat(Inf) + @test mid(interval(1, Inf), 0.75) > 0 + + @test mid(bareinterval(1, 1)) == 1.0 + @test mid(bareinterval(-1, 1)) === 0.0 + @test mid(interval(Rational{Int}, 1//2)) == 1//2 + + for x ∈ (bareinterval(0, 1), bareinterval(-1e308, 1e308), bareinterval(0.1, 0.3), bareinterval(-Inf, 1), bareinterval(1, Inf), entireinterval(BareInterval{Float64})) + for α ∈ (0.0, 0.25, 0.5, 0.75, 1.0) + @test inf(x) ≤ mid(x, α) ≤ sup(x) + end + end + + @test mid(bareinterval(-1e308, 1e308), 0.25) == -5.0e307 + @test isfinite(mid(bareinterval(-1e308, 1e308), 0.25)) + @test mid(bareinterval(-1e308, 1e308)) == 0.0 + @test mid(interval(0.8e308, 1.2e308)) == 1e308 + @test mid(interval(-1e308, 1e308)) == 0 + @test isfinite(mid(interval(0.8e308, 1.2e308))) + @test isfinite(mid(interval(-1e308, 1e308))) + + @test mid(bareinterval(1//3, 2//3)) == 1//2 + @test mid(bareinterval(Float32, 1, 2)) isa Float32 + @test mid(bareinterval(BigFloat, 1, 2)) isa BigFloat + + @test mid(interval(0.25, 4.0)) == 2.125 + @test mid(2.125) == 2.125 + @test mid(2.0, 0.25) == 2.0 + @test mid(complex(interval(0, 3), interval(0, 4))) == 1.5 + 2im + @test mid([interval(0, 1), interval(1, 3)]) == [0.5, 2.0] +end + +@testset "diam" begin + @test diam(bareinterval(1, 2)) == 1.0 + @test diam(bareinterval(1, 1)) == 0.0 + @test diam(bareinterval(0.1, 1.1)) == 1.0000000000000002 == nextfloat(1.0) + @test diam(interval(0.1, 1.1)) == 1.0000000000000002 + + for x ∈ (bareinterval(0.1, 1.1), bareinterval(-0.3, 0.7), bareinterval(1, 2), bareinterval(1e-300, 1e300), bareinterval(0.1, nextfloat(0.1))) + @test diam(x) ≥ sup(x) - inf(x) + end + + @test diam(entireinterval(BareInterval{Float64})) == Inf + @test diam(bareinterval(-1e308, 1e308)) == Inf + @test isbounded(bareinterval(-1e308, 1e308)) + + @test isnan(diam(emptyinterval(BareInterval{Float64}))) + @test isnan(diam(emptyinterval())) + @test_throws ArgumentError diam(emptyinterval(BareInterval{Rational{Int}})) + @test isnan(diam(nai())) + @test_throws ArgumentError diam(nai(Interval{Rational{Int}})) + + @test diam(bareinterval(1//3, 1//2)) == 1//6 + @test diam(interval(Rational{Int}, 1//2)) == 0//1 + @test diam(interval(1//10)) == 0 + @test diam(I"0.1") == eps(0.1) + + @test diam(0.1) == 0 + @test diam(complex(interval(0, 1), interval(0, 3))) == 3.0 +end + +@testset "radius and midradius" begin + @test midradius(bareinterval(1, 2)) == (1.5, 0.5) + @test radius(bareinterval(1, 2)) == 0.5 + + for x ∈ (bareinterval(0.0, nextfloat(0.0, 3)), bareinterval(-1, 1e10), bareinterval(0.1, 1.1)) + m, r = midradius(x) + @test radius(x) == r + @test (m - r ≤ inf(x)) & (sup(x) ≤ m + r) + end + + @test radius(entireinterval(BareInterval{Float64})) == Inf + @test mid(entireinterval(BareInterval{Float64})) == 0.0 + + @test midradius(entireinterval(BareInterval{Rational{Int64}})) == (0//1, 1//0) + + @test_throws "cannot compute the midpoint" radius(emptyinterval(BareInterval{Rational{Int}})) + @test_throws "cannot compute the midpoint" midradius(emptyinterval(BareInterval{Rational{Int}})) + + @test isnan(radius(emptyinterval())) + @test isnan(radius(nai())) + @test all(isnan, midradius(nai())) + @test_throws "cannot compute the radius of an NaI" radius(nai(Interval{Rational{Int}})) + @test_throws "cannot compute the midpoint and radius of an NaI" midradius(nai(Interval{Rational{Int}})) + + @test radius(interval(Rational{Int}, -1//10, 1//10)) == diam(interval(Rational{Int}, -1//10, 1//10)) / 2 + @test radius(2.125) == 0 + @test radius(complex(interval(0, 1), interval(0, 3))) == 1.5 + @test midradius(complex(interval(0, 1), interval(0, 3))) == (0.5 + 1.5im, 1.5) +end + +@testset "mag and mig" begin + @test mag(bareinterval(-3, 2)) == 3.0 + @test mag(bareinterval(1, 2)) == 2.0 + @test mag(entireinterval(BareInterval{Float64})) == Inf + @test mag(-interval(0.9, 2.0)) == sup(interval(0.9, 2.0)) + @test mag(interval(Rational{Int}, 1//2)) == 1//2 + @test isnan(mag(emptyinterval(BareInterval{Float64}))) + @test isnan(mag(emptyinterval())) + @test_throws ArgumentError mag(emptyinterval(BareInterval{Rational{Int}})) + @test isnan(mag(nai())) + @test_throws ArgumentError mag(nai(Interval{Rational{Int}})) + @test mag(-3) == 3.0 + @test mag(complex(interval(0, 3), interval(0, 4))) == 5 + @test mag(complex(interval(1, 2), interval(3, 4))) == 4.47213595499958 + + @test mig(bareinterval(-3, 2)) == 0.0 + @test mig(bareinterval(2, 3)) == 2.0 + @test mig(bareinterval(-3, -2)) == 2.0 + @test mig(entireinterval(BareInterval{Float64})) == 0.0 + @test mig(interval(-2, 2)) == BigFloat(0.0) + @test mig(interval(Rational{Int}, 1//2)) == 1//2 + @test mig(bareinterval(-1//2, 1//2)) == 0//1 + @test mig(bareinterval(1//2, 3//2)) isa Rational{Int} + @test isnan(mig(emptyinterval(BareInterval{Float64}))) + @test isnan(mig(emptyinterval())) + @test_throws ArgumentError mig(emptyinterval(BareInterval{Rational{Int}})) + @test isnan(mig(nai())) + @test_throws ArgumentError mig(nai(Interval{Rational{Int}})) + @test mig(-3) == 3.0 + @test mig(complex(interval(0, 3), interval(0, 4))) == 0 + @test mig(complex(interval(1, 2), interval(3, 4))) == 3.162277660168379 +end + +@testset "dist" begin + @test dist(bareinterval(1, 2), bareinterval(1.5, 3)) == 1.0 + @test dist(bareinterval(1, 2), bareinterval(1, 2)) == 0.0 + @test dist(interval(1, 2), interval(3, 4)) == 2.0 + @test dist(interval(0.1), interval(0.1, 0.1)) <= inf(eps(interval(0.1))) + + @test isnan(dist(emptyinterval(BareInterval{Float64}), bareinterval(1, 2))) + @test isnan(dist(bareinterval(1, 2), emptyinterval(BareInterval{Float64}))) + @test_throws ArgumentError dist(emptyinterval(BareInterval{Rational{Int}}), bareinterval(1//1, 2//1)) + + @test dist(bareinterval(Float32, 1, 2), bareinterval(1.5, 3)) === 1.0 + @test dist(interval(Float32, 1, 2), interval(1.5, 3)) === 1.0 + + @test isnan(dist(nai(), interval(1, 2))) + @test_throws ArgumentError dist(nai(Interval{Rational{Int}}), interval(Rational{Int}, 1, 2)) + + @test_throws MethodError dist(1, 2) +end + +@testset "bound type preservation" begin + for T ∈ (Float16, Float32, Float64, BigFloat) + x = bareinterval(T, 1, 2) + @test inf(x) isa T + @test sup(x) isa T + @test bounds(x) isa Tuple{T,T} + @test mid(x) isa T + @test diam(x) isa T + @test radius(x) isa T + @test midradius(x) isa Tuple{T,T} + @test mag(x) isa T + @test mig(x) isa T + @test dist(x, x) isa T + end + for T ∈ (Rational{Int32}, Rational{Int64}) + x = bareinterval(T(1), T(2)) + @test inf(x) isa T + @test sup(x) isa T + @test mid(x) isa Rational + @test diam(x) isa T + @test radius(x) isa Rational + @test mag(x) isa T + @test mig(x) isa T + @test dist(x, x) isa T + end + x = bareinterval(1//1, 2//1) + @test mid(x) isa Rational{Int} + @test radius(x) isa Rational{Int} +end diff --git a/test/intervals/interval_operations/overlap.jl b/test/intervals/interval_operations/overlap.jl new file mode 100644 index 000000000..3a48bf124 --- /dev/null +++ b/test/intervals/interval_operations/overlap.jl @@ -0,0 +1,105 @@ +using Test +using IntervalArithmetic + +@testset "Overlap.State" begin + @test Overlap isa Module + @test Overlap === IntervalArithmetic.Overlap + @test Overlap.State isa DataType + # IEEE 1788-2015 Table 10.7 ordering + @test instances(Overlap.State) == + (Overlap.both_empty, Overlap.first_empty, Overlap.second_empty, + Overlap.before, Overlap.meets, Overlap.overlaps, Overlap.starts, + Overlap.contained_by, Overlap.finishes, Overlap.equals, + Overlap.finished_by, Overlap.contains, Overlap.started_by, + Overlap.overlapped_by, Overlap.met_by, Overlap.after) + @test collect(Int.(instances(Overlap.State))) == collect(1:16) + @test Int(Overlap.both_empty) == 1 + @test Int(Overlap.equals) == 10 + @test Int(Overlap.after) == 16 +end + +@testset "overlap states" begin + e = emptyinterval(BareInterval{Float64}) + @test overlap(e, e) == Overlap.both_empty + @test overlap(e, bareinterval(1, 2)) == Overlap.first_empty + @test overlap(bareinterval(1, 2), e) == Overlap.second_empty + @test overlap(bareinterval(1, 2), bareinterval(3, 4)) == Overlap.before + @test overlap(bareinterval(1, 2), bareinterval(2, 3)) == Overlap.meets + @test overlap(bareinterval(1, 3), bareinterval(2, 4)) == Overlap.overlaps + @test overlap(bareinterval(1, 2), bareinterval(1, 3)) == Overlap.starts + @test overlap(bareinterval(2, 3), bareinterval(1, 4)) == Overlap.contained_by + @test overlap(bareinterval(2, 4), bareinterval(1, 4)) == Overlap.finishes + @test overlap(bareinterval(1, 2), bareinterval(1, 2)) == Overlap.equals + @test overlap(bareinterval(1, 1), bareinterval(1, 1)) == Overlap.equals + @test overlap(bareinterval(1, 4), bareinterval(2, 4)) == Overlap.finished_by + @test overlap(bareinterval(1, 4), bareinterval(2, 3)) == Overlap.contains + @test overlap(bareinterval(1, 3), bareinterval(1, 2)) == Overlap.started_by + @test overlap(bareinterval(2, 4), bareinterval(1, 3)) == Overlap.overlapped_by + @test overlap(bareinterval(2, 3), bareinterval(1, 2)) == Overlap.met_by + @test overlap(bareinterval(3, 4), bareinterval(1, 2)) == Overlap.after + @test overlap(bareinterval(2, 2), bareinterval(1, 1)) == Overlap.after + + # `meets` requires both intervals to be non-thin + @test overlap(bareinterval(1, 1), bareinterval(1, 2)) == Overlap.starts + @test overlap(bareinterval(1, 2), bareinterval(2, 2)) == Overlap.finished_by + @test overlap(bareinterval(2, 2), bareinterval(1, 2)) == Overlap.finishes + @test overlap(bareinterval(1, 2), bareinterval(1, 1)) == Overlap.started_by + + entire = entireinterval(BareInterval{Float64}) + @test overlap(entire, entire) == Overlap.equals + @test overlap(bareinterval(-Inf, 0), bareinterval(0, Inf)) == Overlap.meets + @test overlap(bareinterval(-Inf, 0), entire) == Overlap.starts + + for T ∈ (Float32, BigFloat) + @test overlap(bareinterval(T, 1, 2), bareinterval(T, 2, 3)) == Overlap.meets + @test overlap(bareinterval(T, 1, 2), bareinterval(T, 1, 3)) == Overlap.starts + @test overlap(bareinterval(T, 3, 4), bareinterval(T, 1, 2)) == Overlap.after + end + @test overlap(bareinterval(1//1, 2//1), bareinterval(2//1, 3//1)) == Overlap.meets + @test overlap(bareinterval(1//1, 2//1), bareinterval(1//1, 3//1)) == Overlap.starts +end + +@testset "exhaustive sweep and duality" begin + dual = Dict( + Overlap.both_empty => Overlap.both_empty, + Overlap.first_empty => Overlap.second_empty, + Overlap.second_empty => Overlap.first_empty, + Overlap.before => Overlap.after, + Overlap.meets => Overlap.met_by, + Overlap.overlaps => Overlap.overlapped_by, + Overlap.starts => Overlap.started_by, + Overlap.contained_by => Overlap.contains, + Overlap.finishes => Overlap.finished_by, + Overlap.equals => Overlap.equals, + Overlap.finished_by => Overlap.finishes, + Overlap.contains => Overlap.contained_by, + Overlap.started_by => Overlap.starts, + Overlap.overlapped_by => Overlap.overlaps, + Overlap.met_by => Overlap.meets, + Overlap.after => Overlap.before) + endpoints = [-Inf, -2, -1, 0, 1, 2, Inf] + xs = [bareinterval(a, b) for a ∈ endpoints for b ∈ endpoints if a ≤ b && !(isinf(a) & (a == b))] + push!(xs, emptyinterval(BareInterval{Float64})) + for x ∈ xs, y ∈ xs + s = overlap(x, y) + @test s isa Overlap.State + @test overlap(y, x) == dual[s] + end +end + +@testset "overlap of decorated intervals" begin + @test overlap(interval(1, 2), interval(3, 4)) == Overlap.before + @test overlap(IntervalArithmetic.setdecoration(interval(1, 2), trv), interval(1, 2)) == Overlap.equals + @test overlap(emptyinterval(), emptyinterval()) == Overlap.both_empty + @test overlap(emptyinterval(), interval(1, 2)) == Overlap.first_empty + @test overlap(interval(1, 2), emptyinterval()) == Overlap.second_empty + + @test_throws ArgumentError overlap(nai(), interval(1, 2)) + @test_throws ArgumentError overlap(interval(1, 2), nai()) + @test_throws ArgumentError overlap(nai(), nai()) + @test_logs @test_throws ArgumentError overlap(nai(), interval(1, 2)) + + z = complex(interval(1), interval(2)) + @test_throws MethodError overlap(z, z) + @test_throws MethodError overlap([bareinterval(1, 2)], [bareinterval(1, 2)]) +end diff --git a/test/intervals/interval_operations/set_operations.jl b/test/intervals/interval_operations/set_operations.jl new file mode 100644 index 000000000..67e856dbc --- /dev/null +++ b/test/intervals/interval_operations/set_operations.jl @@ -0,0 +1,371 @@ +using Test +using IntervalArithmetic +using IntervalArithmetic: interval_diff, interiordiff! + +sameset(A, B) = length(A) == length(B) && + all(a -> any(b -> all(isequal_interval.(a, b)), B), A) && + all(b -> any(a -> all(isequal_interval.(a, b)), A), B) + +@testset "_set_decoration" begin + x = interval(1, 2) + @test IntervalArithmetic._set_decoration(x, :auto) === x + @test decoration(IntervalArithmetic._set_decoration(x, :default)) == trv + @test decoration(IntervalArithmetic._set_decoration(x, def)) == def + @test_throws ArgumentError IntervalArithmetic._set_decoration(x, :bogus) +end + +@testset "intersect_interval" begin + e = emptyinterval(BareInterval{Float64}) + entire = entireinterval(BareInterval{Float64}) + + @test isequal_interval(e, @test_logs (:warn,) bareinterval(Inf, -Inf)) + + @test isequal_interval(intersect_interval(bareinterval(1, 3), bareinterval(2, 4)), bareinterval(2, 3)) + @test isequal_interval(intersect_interval(bareinterval(1, 2), bareinterval(3, 4)), e) + @test isequal_interval(intersect_interval(bareinterval(1, 2), bareinterval(2, 3)), bareinterval(2, 2)) + @test isequal_interval(intersect_interval(e, entire), e) + @test isequal_interval(intersect_interval(e, e), e) + for x ∈ (bareinterval(1, 2), e, entire, bareinterval(-Inf, 0)) + @test isequal_interval(intersect_interval(entire, x), x) + end + + @test intersect_interval(bareinterval(Float32, 1, 3), bareinterval(1.5, 4.0)) isa BareInterval{Float64} + @test isequal_interval(intersect_interval(bareinterval(1//2, 3//2), bareinterval(1//1, 2//1)), bareinterval(1//1, 3//2)) + @test isequal_interval(intersect_interval(bareinterval(BigFloat, 1, 3), bareinterval(BigFloat, 2, 4)), bareinterval(BigFloat, 2, 3)) + + @test isequal_interval(intersect_interval(bareinterval(1, 5), bareinterval(2, 6), bareinterval(3, 7)), bareinterval(3, 5)) + @test isequal_interval(intersect_interval(bareinterval(1, 5), bareinterval(2, 6), bareinterval(3, 7), bareinterval(0, 4)), bareinterval(3, 4)) + + a = interval(0.1, 1.1) + @test isequal_interval(intersect_interval(a, interval(-1)), emptyinterval(a)) + @test isempty_interval(intersect_interval(a, interval(-1))) + @test isequal_interval(intersect_interval(a, hull(a, interval(0.9, 2.0))), a) + @test isequal_interval(intersect_interval(interval(1.0, 2.0), interval(-1.0, 5.0), interval(1.8, 3.0)), interval(1.8, 2.0)) + @test isequal_interval(intersect_interval(a, emptyinterval(), interval(0.9, 2.0)), emptyinterval()) + @test isequal_interval(intersect_interval(interval(0, 1), interval(3, 4), interval(0, 1), interval(0, 1)), emptyinterval()) + + r = intersect_interval(interval(1, 3), interval(2, 4)) + @test isequal_interval(r, interval(2, 3)) + @test decoration(r) == trv + @test decoration(intersect_interval(interval(1, 3), interval(2, 4); dec = :auto)) == com + @test decoration(intersect_interval(interval(1, 3), interval(2, 4); dec = com)) == com + @test decoration(intersect_interval(interval(1, 3), interval(2, 4); dec = def)) == def + @test isnai(intersect_interval(interval(1, 3), interval(2, 4); dec = ill)) + @test decoration(intersect_interval(interval(1, 2), interval(3, 4); dec = com)) == trv + @test_throws ArgumentError intersect_interval(interval(1, 3), interval(2, 4); dec = :bogus) + + @test intersect_interval(interval(Float32, 1, 3), interval(2.0, 4.0)) isa Interval{Float64} +end + +@testset "hull" begin + e = emptyinterval(BareInterval{Float64}) + entire = entireinterval(BareInterval{Float64}) + + @test isequal_interval(hull(bareinterval(1, 2), bareinterval(5, 6)), bareinterval(1, 6)) + @test isequal_interval(hull(e, bareinterval(1, 2)), bareinterval(1, 2)) + @test isequal_interval(hull(e, e), e) + @test isequal_interval(hull(entire, bareinterval(1, 2)), entire) + @test isequal_interval(hull(bareinterval(-Inf, 0), bareinterval(0, Inf)), entire) + @test hull(bareinterval(Float32, 1, 2), bareinterval(3.0, 4.0)) isa BareInterval{Float64} + + @test union_interval === hull + + @test isequal_interval(hull(bareinterval(1, 2), bareinterval(5, 6), bareinterval(-1, 0), bareinterval(9, 10)), bareinterval(-1, 10)) + + @test isequal_interval(hull(interval(1, 2), interval(3, 4)), interval(1, 4)) + @test isequal_interval(hull(interval(1//3, 3//4), interval(3, 4)), interval(1/3, 4)) + @test isequal_interval(hull(interval(0.1, 1.1), interval(0.9, 2.0)), interval(0.1, 2.0)) + + @test decoration(hull(interval(1, 2), interval(5, 6))) == trv + @test decoration(hull(interval(1, 2), interval(5, 6); dec = :auto)) == com + @test decoration(hull(interval(1, 2), interval(-Inf, 0); dec = com)) == dac +end + +@testset "hull and intersect_interval" begin + for T ∈ (Float64, Float32, BigFloat, Rational{Int}) + x = bareinterval(T, 1, 2) + e = emptyinterval(BareInterval{T}) + + @test isequal_interval(hull(e, e), e) + @test isequal_interval(hull(x, e), x) + @test isequal_interval(hull(e, x), x) + @test isequal_interval(hull(x, bareinterval(T, 5, 6)), bareinterval(T, 1, 6)) + + @test isempty_interval(intersect_interval(e, e)) + @test isempty_interval(intersect_interval(x, e)) + @test isempty_interval(intersect_interval(e, x)) + @test isempty_interval(intersect_interval(x, bareinterval(T, 5, 6))) + @test isequal_interval(intersect_interval(x, bareinterval(T, 0, 3)), x) + + y = interval(T, 1, 2) + f = emptyinterval(Interval{T}) + + @test isempty_interval(hull(f, f)) + @test isequal_interval(hull(y, f), y) + @test isequal_interval(hull(f, y), y) + @test isempty_interval(intersect_interval(y, f)) + @test isempty_interval(intersect_interval(f, y)) + end + + n = nai(Float64) + x = interval(1, 2) + + @test isnai(hull(n, x)) & isnai(hull(x, n)) & isnai(hull(n, n)) + @test isnai(intersect_interval(n, x)) & isnai(intersect_interval(x, n)) + @test isnai(hull(x, x, n)) & isnai(hull(x, n, x)) & isnai(hull(n, x, x)) + @test isnai(hull(x, x, x, n)) + @test isnai(intersect_interval(x, x, n)) & isnai(intersect_interval(n, x, x)) + @test isnai(@test_logs hull(n, x)) + @test isnai(@test_logs intersect_interval(n, x)) + @test isnai(@test_logs hull(x, x, x, n)) + + y = interval(3, 4) + z = interval(5, Inf) + + @test decoration(hull(x, y)) == trv + @test decoration(hull(x, y; dec = :auto)) == com + @test decoration(hull(x, z; dec = :auto)) == dac + @test decoration(hull(x, y; dec = def)) == def + @test decoration(intersect_interval(x, y)) == trv + @test decoration(intersect_interval(x, interval(2, 3); dec = :auto)) == com + @test_throws ArgumentError hull(x, y; dec = :nonsense) + @test_throws ArgumentError intersect_interval(x, y; dec = :nonsense) + + @test decoration(hull(interval(-Inf, 0), interval(1, 2); dec = :auto)) == dac + + bs = (bareinterval(1, 2), bareinterval(-3, 0), bareinterval(5, 6)) + + @test isequal_interval(hull(bs...), bareinterval(-3, 6)) + @test isequal_interval(hull(bs..., bareinterval(7, 9)), bareinterval(-3, 9)) + @test isempty_interval(intersect_interval(bs...)) + @test isequal_interval( + intersect_interval(bareinterval(0, 4), bareinterval(1, 5), bareinterval(2, 6)), + bareinterval(2, 4)) + @test_throws MethodError hull(bs...; dec = :auto) + + args = (interval(1, 2), interval(-3, 0), interval(5, 6), interval(-1, 8), + interval(0, 1), interval(-2, 2), interval(4, 7), interval(-5, 5)) + for dec ∈ (:default, :auto, trv, def, com) + for k ∈ 3:8 + xs = args[1:k] + @test isequal_interval(hull(xs...; dec = dec), reduce((a, b) -> hull(a, b; dec = dec), xs)) + @test decoration(hull(xs...; dec = dec)) == decoration(reduce((a, b) -> hull(a, b; dec = dec), xs)) + @test isequal_interval(intersect_interval(xs...; dec = dec), reduce((a, b) -> intersect_interval(a, b; dec = dec), xs)) + @test decoration(intersect_interval(xs...; dec = dec)) == decoration(reduce((a, b) -> intersect_interval(a, b; dec = dec), xs)) + end + end + + ng = convert(Interval{Float64}, 1) + + @test !isguaranteed(hull(x, ng)) + @test !isguaranteed(hull(x, y, ng)) + @test !isguaranteed(hull(x, y, x, ng)) + @test isguaranteed(hull(x, y, x)) + @test !isguaranteed(intersect_interval(x, y, ng)) + @test !isguaranteed(intersect_interval(x, ng)) + @test isguaranteed(intersect_interval(x, y)) + + @test numtype(hull(interval(Float32, 1, 2), interval(Float64, 3, 4))) === Float64 + @test numtype(hull(interval(Float32, 1, 2), interval(Float32, 0, 1), interval(Float64, 3, 4))) === Float64 + @test numtype(intersect_interval(interval(Float32, 1, 2), interval(Float32, 0, 3), interval(Float64, 1, 4))) === Float64 + + @test inf(hull(interval(0, 1), interval(2, 3))) === -0.0 + @test inf(intersect_interval(interval(0, 1), interval(-1, 3))) === -0.0 +end + +@testset "complex hull and intersect_interval" begin + a = complex(interval(0), interval(1)) + b = complex(interval(3), interval(4)) + c = complex(interval(-1, 4), interval(0, 2)) + + @test isequal_interval(hull(a, b), complex(interval(0, 3), interval(1, 4))) + @test isequal_interval(intersect_interval(c, hull(a, b)), complex(interval(0, 3), interval(1, 2))) + @test isempty_interval(intersect_interval(a, b)) + @test isequal_interval(intersect_interval(a, b), complex(emptyinterval(), emptyinterval())) + + @test isequal_interval(hull(interval(1, 2), complex(interval(3, 4), interval(5, 6))), complex(interval(1, 4), interval(0, 6))) + @test isequal_interval(hull(complex(interval(3, 4), interval(5, 6)), interval(1, 2)), complex(interval(1, 4), interval(0, 6))) + @test isequal_interval(intersect_interval(interval(0, 2), complex(interval(1, 3), interval(-1, 1))), complex(interval(1, 2), interval(0))) + @test isempty_interval(intersect_interval(interval(0, 2), complex(interval(1, 3), interval(1, 2)))) + @test isequal_interval(intersect_interval(complex(interval(1, 3), interval(-1, 1)), interval(0, 2)), complex(interval(1, 2), interval(0))) +end + +@testset "interiordiff of intervals" begin + e = emptyinterval(BareInterval{Float64}) + @test all(isequal_interval.(interiordiff(bareinterval(1, 4), bareinterval(2, 3)), [bareinterval(1, 2), bareinterval(3, 4)])) + @test all(isequal_interval.(interiordiff(bareinterval(1, 4), bareinterval(1, 4)), [bareinterval(1, 1), bareinterval(4, 4)])) + @test isequal_interval(only(interiordiff(bareinterval(1, 4), bareinterval(0, 3))), bareinterval(3, 4)) + @test isequal_interval(only(interiordiff(bareinterval(1, 4), bareinterval(2, 5))), bareinterval(1, 2)) + @test isempty(interiordiff(bareinterval(1, 4), bareinterval(0, 5))) + @test isequal_interval(only(interiordiff(bareinterval(1, 2), bareinterval(3, 4))), bareinterval(1, 2)) + @test isempty(interiordiff(e, bareinterval(1, 2))) + @test isequal_interval(only(interiordiff(bareinterval(1, 2), e)), bareinterval(1, 2)) + @test interiordiff(bareinterval(Float32, 1, 4), bareinterval(2.0, 3.0)) isa Vector{BareInterval{Float64}} + + for (x, y) ∈ ((bareinterval(1, 4), bareinterval(2, 3)), (bareinterval(1, 4), bareinterval(0, 3)), + (bareinterval(1, 4), bareinterval(2, 5)), (bareinterval(1, 2), bareinterval(3, 4)), + (bareinterval(1, 4), bareinterval(1, 4))) + pieces = interiordiff(x, y) + @test issubset_interval(x, reduce(hull, [pieces; [intersect_interval(x, y)]])) + end + + x = interval(2, 4) + y = interval(3, 5) + @test typeof(interiordiff(x, y)) == Vector{Interval{Float64}} + @test interiordiff(interval(Float32, 2, 4), interval(3.0, 5.0)) isa Vector{Interval{Float64}} + @test all(isequal_interval.(interiordiff(x, x), [interval(2), interval(4)])) + @test isequal_interval(only(interiordiff(x, emptyinterval(x))), x) + @test isequal_interval(only(interiordiff(x, y)), interval(2, 3)) + @test isequal_interval(only(interiordiff(y, x)), interval(4, 5)) + y = interval(2, 5) + @test isequal_interval(only(interiordiff(x, y)), interval(2)) + @test all(isequal_interval.(interiordiff(y, x), [interval(2), interval(4, 5)])) + @test all(isequal_interval.(interiordiff(interval(2, 5), interval(3, 4)), [interval(2, 3), interval(4, 5)])) + @test interiordiff(interval(1, 3), interval(0, 5)) == Interval{Float64}[] + @test all(isequal_interval.(interiordiff(interval(0, 5), interval(1, 3)), [interval(0, 1), interval(3, 5)])) + + dtrv = interiordiff(interval(1, 4), interval(2, 3)) + @test (length(dtrv) == 2) && all(d -> d == trv, decoration.(dtrv)) + dauto = interiordiff(interval(1, 4), interval(2, 3); dec = :auto) + @test (length(dauto) == 2) && all(d -> d == com, decoration.(dauto)) + ddef = interiordiff(interval(1, 4), interval(2, 3); dec = def) + @test (length(ddef) == 2) && all(d -> d == def, decoration.(ddef)) + dcom = interiordiff(interval(1, 4), interval(2, 3); dec = com) + @test (length(dcom) == 2) && all(d -> d == com, decoration.(dcom)) + @test (length(dtrv) == 2) && all(isguaranteed, dtrv) + ngdiff = interiordiff(interval(1, 4), convert(Interval{Float64}, 2)) + @test (length(ngdiff) == 2) && all(z -> !isguaranteed(z), ngdiff) + @test isempty(interiordiff(convert(Interval{Float64}, 2), interval(1, 4))) + + r = interiordiff(nai(), interval(1, 2)) + @test (length(r) == 1) && isnai(r[1]) + r = interiordiff(interval(1, 2), nai()) + @test (length(r) == 1) && isnai(r[1]) + + v = Interval{Float64}[] + @test interiordiff!(v, interval(1, 4), interval(2, 3)) === v + @test all(isequal_interval.(v, [interval(1, 2), interval(3, 4)])) + @test interiordiff!(v, interval(1, 4), interval(0, 5)) === v + @test isempty(v) + @test interiordiff!(v, interval(Float32, 1, 4), interval(2.0, 3.0)) === v + @test all(isequal_interval.(v, [interval(1, 2), interval(3, 4)])) +end + +@testset "_interiordiff" begin + h₁, h₂, inter = IntervalArithmetic._interiordiff(bareinterval(1, 2), bareinterval(3, 4), nothing) + @test isequal_interval(h₁, bareinterval(1, 2)) & isempty_interval(h₂) & isempty_interval(inter) + h₁, h₂, inter = IntervalArithmetic._interiordiff(bareinterval(2, 3), bareinterval(1, 4), nothing) + @test isempty_interval(h₁) & isempty_interval(h₂) & isequal_interval(inter, bareinterval(2, 3)) + h₁, h₂, inter = IntervalArithmetic._interiordiff(bareinterval(1, 4), bareinterval(1, 4), nothing) + @test isequal_interval(h₁, bareinterval(1, 1)) & isequal_interval(h₂, bareinterval(4, 4)) & isequal_interval(inter, bareinterval(1, 4)) + h₁, h₂, inter = IntervalArithmetic._interiordiff(bareinterval(1, 4), bareinterval(1, 3), nothing) + @test isequal_interval(h₁, bareinterval(3, 4)) & isempty_interval(h₂) & isequal_interval(inter, bareinterval(1, 3)) + h₁, h₂, inter = IntervalArithmetic._interiordiff(bareinterval(1, 4), bareinterval(2, 4), nothing) + @test isequal_interval(h₁, bareinterval(1, 2)) & isempty_interval(h₂) & isequal_interval(inter, bareinterval(2, 4)) + h₁, h₂, inter = IntervalArithmetic._interiordiff(bareinterval(1, 4), bareinterval(2, 3), nothing) + @test isequal_interval(h₁, bareinterval(1, 2)) & isequal_interval(h₂, bareinterval(3, 4)) & isequal_interval(inter, bareinterval(2, 3)) +end + +@testset "interiordiff of boxes" begin + X = [interval(2, 4), interval(3, 5)] + Y = [interval(3, 5), interval(4, 6)] + @test sameset( + interiordiff(X, Y), + [ [interval(3, 4), interval(3, 4)], + [interval(2, 3), interval(3, 5)] ]) + + X = [interval(2, 5), interval(3, 6)] + Y = [interval(-10, 10), interval(4, 5)] + @test sameset( + interiordiff(X, Y), + [ [interval(2, 5), interval(3, 4)], + [interval(2, 5), interval(5, 6)] ]) + + X = [interval(2, 5), interval(3, 6)] + Y = [interval(4, 6), interval(4, 5)] + @test sameset( + interiordiff(X, Y), + [ [interval(4, 5), interval(3, 4)], + [interval(4, 5), interval(5, 6)], + [interval(2, 4), interval(3, 6)] ]) + + X = [interval(2, 5), interval(3, 6)] + Y = [interval(3, 4), interval(4, 5)] + @test sameset( + interiordiff(X, Y), + [ [interval(3, 4), interval(3, 4)], + [interval(3, 4), interval(5, 6)], + [interval(2, 3), interval(3, 6)], + [interval(4, 5), interval(3, 6)] ]) + + X = [interval(2, 5), interval(3, 6)] + Y = [interval(2, 4), interval(10, 20)] + r = interiordiff(X, Y) + @test sameset(r, typeof(X)[X]) + @test r[1] !== X + + X = [interval(2, 5), interval(3, 6)] + Y = [interval(-10, 10), interval(-10, 10)] + @test sameset(interiordiff(X, Y), typeof(X)[]) + + X = [interval(1, 4), interval(3, 6), interval(7, 10)] + Y = [interval(2, 3), interval(4, 5), interval(8, 9)] + @test sameset( + interiordiff(X, Y), + [ [interval(2, 3), interval(4, 5), interval(7, 8)], + [interval(2, 3), interval(4, 5), interval(9, 10)], + [interval(2, 3), interval(3, 4), interval(7, 10)], + [interval(2, 3), interval(5, 6), interval(7, 10)], + [interval(1, 2), interval(3, 6), interval(7, 10)], + [interval(3, 4), interval(3, 6), interval(7, 10)] ]) + + X = [interval(-Inf, Inf), interval(1, 2)] + Y = [interval(1, 2), interval(-1, 1.5)] + @test sameset( + interiordiff(X, Y), + [ [interval(-Inf, 1), interval(1, 2)], + [interval(2, Inf), interval(1, 2)], + [interval(1, 2), interval(1.5, 2)] ]) + + r = interiordiff([bareinterval(0, 2), bareinterval(0, 2)], [bareinterval(1, 3), bareinterval(1, 3)]) + @test length(r) == 2 + @test all(isequal_interval.(r[1], [bareinterval(0, 1), bareinterval(0, 2)])) + @test all(isequal_interval.(r[2], [bareinterval(1, 2), bareinterval(0, 1)])) + @test all(box -> !any(z -> isempty_interval(z) | isnai(z), box), r) + + r = interiordiff([interval(0, 2), interval(0, 2)], [interval(1, 3), interval(1, 3)]) + @test decoration.(r[1]) == [trv, com] + @test decoration.(r[2]) == [trv, trv] + r = interiordiff([interval(0, 2), interval(0, 2)], [interval(1, 3), interval(1, 3)]; dec = :auto) + @test all(box -> all(d -> d == com, decoration.(box)), r) + + @test_throws DimensionMismatch interiordiff([interval(1, 2)], [interval(1, 2), interval(3, 4)]) + + v = Vector{Vector{Interval{Float64}}}(undef, 0) + @test interiordiff!(v, [interval(2, 5), interval(3, 6)], [interval(-10, 10), interval(-10, 10)]) === v + @test isempty(v) +end + +@testset "interval_diff" begin + A, B = interval_diff(interval(1, 10), interval(2, 5)) + @test isequal_interval(A, interval(1, 2)) + @test isequal_interval(B, interval(5, 10)) + + @test isequal_interval( + only(interval_diff(interval(1, 10), interval(1, 5))), + interval(5, 10) + ) + @test isequal_interval( + only(interval_diff(interval(1, 10), interval(7, 12))), + interval(1, 7) + ) + + @test isequal_interval(only(interval_diff(interval(1, 10), interval(20, 30))), interval(1, 10)) + @test interval_diff(interval(1, 10), interval(-1, 14)) == [] + @test interval_diff(interval(1, 10), interval(1, 10)) == [] + @test interval_diff(interval(1, 10), interval(-1, 14)) isa Vector{Interval{Float64}} + + r = interval_diff(interval(1, 4), interval(2, 3)) + @test all(isequal_interval.(r, [interval(1, 2), interval(3, 4)])) + @test all(d -> d == com, decoration.(r)) +end diff --git a/test/intervals/parsing.jl b/test/intervals/parsing.jl new file mode 100644 index 000000000..06609e0f3 --- /dev/null +++ b/test/intervals/parsing.jl @@ -0,0 +1,200 @@ +using Test +using IntervalArithmetic +using IntervalArithmetic: _parse, _parse_num + +@testset "BareInterval" begin + for T ∈ (Float16, Float32, Float64, BigFloat) + @test isequal_interval(parse(BareInterval{T}, "[1, 2]"), bareinterval(T, 1, 2)) + if T != BigFloat + @test isequal_interval(parse(BareInterval{T}, "[1e-324, 1e400]"), bareinterval(T, 0, Inf)) + else + @test isequal_interval(parse(BareInterval{BigFloat}, "[1e-324, 1e400]"), bareinterval(BigFloat("1e-324", RoundDown), BigFloat("1e400", RoundUp))) + end + @test isequal_interval(parse(BareInterval{T}, "[2,infinity]"), bareinterval(T, 2, Inf)) + @test isempty_interval(@test_logs (:warn, r"parsing error") parse(BareInterval{T}, "[foobar]")) + end + + @test isequal_interval(parse(BareInterval{Rational{Int64}}, "0.1"), bareinterval(Rational{Int64}, 1//10)) + @test isequal_interval(parse(BareInterval{Rational{Int64}}, "[0.1, 0.3]"), bareinterval(Rational{Int64}, 1//10, 3//10)) + + @test bounds(parse(BareInterval{Float64}, "[1, 2]")) == (1.0, 2.0) + @test bounds(parse(BareInterval{Float64}, "[1,]")) == (1.0, Inf) + @test bounds(parse(BareInterval{Float64}, "[,]")) == (-Inf, Inf) + @test bounds(parse(BareInterval{Float64}, "6.42?2e2")) == (640.0, 644.0) + + @test isempty_interval(@test_logs (:warn, r"failed to parse a decorated interval") parse(BareInterval{Float64}, "[1, 2]_com")) + @test isempty_interval(@test_logs (:warn, r"parsed NaI") parse(BareInterval{Float64}, "[nai]")) + for str ∈ ("[garbage]", "garbage", "", "[1, garbage]") + @test isempty_interval(@test_logs (:warn, r"parsing error") parse(BareInterval{Float64}, str)) + end +end + +@testset "Interval" begin + for T ∈ (Float16, Float32, Float64, BigFloat) + @test isequal_interval(parse(Interval{T}, "[1, 2]"), interval(T, 1, 2)) + if T != BigFloat + @test isequal_interval(parse(Interval{T}, "[1e-324, 1e400]"), interval(T, 0, Inf)) + else + @test isequal_interval(parse(Interval{BigFloat}, "[1e-324, 1e400]"), interval(BigFloat("1e-324", RoundDown), BigFloat("1e400", RoundUp))) + end + @test isequal_interval(parse(Interval{T}, "[2,infinity]"), interval(T, 2, Inf)) + @test isnai(@test_logs (:warn, r"parsing error") parse(Interval{T}, "[foobar]")) + + x = parse(Interval{T}, "[1, 2]_com") + y = parse(Interval{T}, "[1, 2]") + z = interval(T, 1, 2) + @test isequal_interval(x, y, z) & (decoration(x) == decoration(y) == decoration(z)) + end + + @test isequal_interval(parse(Interval{Rational{Int64}}, "0.1"), interval(Rational{Int64}, 1//10)) + @test isequal_interval(parse(Interval{Rational{Int64}}, "[0.1, 0.3]"), interval(Rational{Int64}, 1//10, 3//10)) + @test bounds(parse(Interval{Rational{Int64}}, "1/3")) == (1//3, 1//3) + + x = parse(Interval{Float64}, "[1, 2]") + @test bounds(x) == (1.0, 2.0) + @test (decoration(x) == com) & isguaranteed(x) + + for str ∈ ("[1.33]", "1.33") + @test bounds(parse(Interval{Float64}, str)) == (1.3299999999999998, 1.33) + end + + for str ∈ ("[empty]", "[]") + e = @test_logs parse(Interval{Float64}, str) + @test isempty_interval(e) & (decoration(e) == trv) + end + for str ∈ ("[entire]", "[,]") + e = @test_logs parse(Interval{Float64}, str) + @test (bounds(e) == (-Inf, Inf)) & (decoration(e) == dac) + end + + x = parse(Interval{Float64}, "[3,]") + @test (bounds(x) == (3.0, Inf)) & (decoration(x) == dac) + x = parse(Interval{Float64}, "[,3]") + @test (bounds(x) == (-Inf, 3.0)) & (decoration(x) == dac) + + @test isequal_interval(parse(Interval{Float64}, " [1, 2] "), parse(Interval{Float64}, "[1,2]")) + @test decoration(parse(Interval{Float64}, "[1, 2]_DEF")) == def + for (str, d) ∈ (("[1, 2]_trv", trv), ("[1, 2]_def", def), ("[1, 2]_dac", dac), ("[1, 2]_com", com)) + @test decoration(parse(Interval{Float64}, str)) == d + end + @test isnai(@test_logs (:warn, r"invalid interval") parse(Interval{Float64}, "[1, 2]_ill")) + + @test isnai(@test_logs (:warn, r"incompatible") parse(Interval{Float64}, "[1, inf]_com")) + x = parse(Interval{Float64}, "[1, 1e400]_com") + @test (bounds(x) == (1.0, Inf)) & (decoration(x) == dac) & isguaranteed(x) + @test isnai(@test_logs (:warn, r"incompatible") parse(Interval{Float64}, "[empty]_com")) + x = parse(Interval{Float64}, "[empty]_trv") + @test isempty_interval(x) & (decoration(x) == trv) + @test isnai(@test_logs (:warn, r"failed to parse the decoration") parse(Interval{Float64}, "[1,2]_foo")) + + x = @test_logs parse(Interval{Float64}, "[nai]") + @test isnai(x) + for str ∈ ("[garbage]", "garbage", "", "[1, garbage]") + @test isnai(@test_logs (:warn, r"parsing error") parse(Interval{Float64}, str)) + end + @test isnai(@test_logs (:warn, r"ill-formed interval") parse(Interval{Float64}, "[2,1]")) + + x = @test_logs (:warn, r"atomic") parse(Interval{Float64}, "[1, 1.0000000000000002]") + @test bounds(x) == (1.0, 1.0000000000000002) + x = @test_logs (:warn, r"atomic") parse(Interval{Float64}, "[1e400, 2e400]_com") + @test (bounds(x) == (1.7976931348623157e308, Inf)) & (decoration(x) == dac) + @test_logs parse(Interval{Float64}, "[1, 1]") + @test_logs parse(Interval{Float64}, "[1,2]") +end + +@testset "Uncertainty forms" begin + @test isequal_interval(parse(Interval{Float64}, "3?"), interval(2.5, 3.5)) + @test isequal_interval(parse(Interval{Float64}, "3?1"), interval(2.0, 4.0)) + @test isequal_interval(parse(Interval{Float64}, "3.0?1"), interval(2.9, 3.1)) + @test bounds(parse(Interval{Float64}, "6.42?2")) == (6.3999999999999995, 6.44) + @test isequal_interval(parse(Interval{Float64}, "6.42?2e2"), interval(640, 644)) + @test isequal_interval(parse(Interval{Float64}, "4.5?5u"), interval(4.5, 5.0)) + @test bounds(parse(Interval{Float64}, "6.42?2d")) == (6.3999999999999995, 6.420000000000001) + + x = parse(Interval{Float64}, "3??u") + @test (bounds(x) == (3.0, Inf)) & (decoration(x) == dac) + x = parse(Interval{Float64}, "3??d") + @test (bounds(x) == (-Inf, 3.0)) & (decoration(x) == dac) + @test isequal_interval(parse(Interval{Float64}, "3??"), entireinterval(Interval{Float64})) +end + +@testset "Decimal string enclosures" begin + x = parse(Interval{Float64}, "0.1") + @test bounds(x) == (0.09999999999999999, 0.1) + @test (decoration(x) == com) & isguaranteed(x) + @test in_interval(1//10, x) + + @test bounds(parse(Interval{Float64}, "1/3")) == (0.3333333333333333, 0.33333333333333337) + + @test bounds(parse(Interval{Float32}, "0.1")) === (0.099999994f0, 0.1f0) + @test in_interval(1//10, parse(Interval{BigFloat}, "0.1")) +end + +@testset "_parse_num" begin + @test _parse_num(Float64, "0.1", RoundDown) == 0.09999999999999999 + @test _parse_num(Float64, "0.1", RoundUp) == 0.1 + @test _parse_num(Float64, "0.1", RoundDown) < _parse_num(Float64, "0.1", RoundUp) + @test _parse_num(Float64, "0.1", RoundDown) < 1//10 < _parse_num(Float64, "0.1", RoundUp) + @test _parse_num(Float64, "1/3", RoundDown) == 0.3333333333333333 + + @test _parse_num(Rational{Int64}, "1/3", RoundDown) == _parse_num(Rational{Int64}, "1/3", RoundUp) == 1//3 + @test _parse_num(Rational{Int64}, "0.1", RoundDown) ≤ 1//10 ≤ _parse_num(Rational{Int64}, "0.1", RoundUp) + @test _parse_num(Rational{Int64}, "3.14159", RoundDown) ≤ 314159//100000 ≤ _parse_num(Rational{Int64}, "3.14159", RoundUp) +end + +@testset "String macro" begin + @test typeof(I"0.1") == Interval{Float64} + x = I"[3, 4]" + @test (bounds(x) == (3.0, 4.0)) & (decoration(x) == com) & isguaranteed(x) + @test bounds(I"0.1") == (0.09999999999999999, 0.1) + @test in_interval(1//10, I"0.1") + + @test isequal_interval(I"[2/3, 1.1]", interval(0.6666666666666666, 1.1)) + @test isequal_interval(I"[1]", interval(1)) + @test isequal_interval(I"[-0x1.3p-1, 2/3]", interval(-0.59375, 0.6666666666666667)) + @test isequal_interval(I"123412341234123412341241234", interval(1.234123412341234e26, 1.2341234123412342e26)) + + @test in_interval(1//10, I"[0.1, 0.2]") && in_interval(2//10, I"[0.1, 0.2]") + @test issubset_interval(I"[0.1, 0.2]", interval(prevfloat(0.1), nextfloat(0.2))) + + @test nextfloat(inf(I"0.1")) == sup(I"0.1") + + @test isequal_interval(interval(0.5), interval(1//2), I"0.5") + + @test inf(I"1e300") == 9.999999999999999e299 && sup(I"1e300") == 1.0e300 + @test inf(I"-1e307") == -1.0000000000000001e307 && sup(I"-1e307") == -1.0e307 + # corner case for enclosure, `0.100000000000000006` rounds down to `0.1` for `Float64` + @test in_interval(big"0.100000000000000006", I"0.100000000000000006") +end + +IntervalArithmetic.configure(numtype = Float32) +try + @testset "@I_str uses the default numtype" begin + @test typeof(I"0.1") == Interval{Float32} + @test typeof(I"[1, 2]") == Interval{Float32} + end +finally + IntervalArithmetic.configure(numtype = Float64) +end + +@testset "@I_str default numtype restored" begin + @test typeof(I"0.1") == Interval{Float64} +end + +@testset "_parse internals" begin + @test isequal_interval(_parse("0.1"), parse(Interval{Float64}, "0.1")) + + _, flag, isexactnai, iserror = _parse(Float64, "[1, 1e400]") + @test (flag == false) & (isexactnai == false) & (iserror == false) + _, flag, isexactnai, iserror = _parse(Float64, "[1, inf]") + @test (flag == true) & (isexactnai == false) & (iserror == false) + _, flag, isexactnai, iserror = _parse(Float64, "[nai]") + @test (flag == true) & (isexactnai == true) & (iserror == false) + for str ∈ ("[empty]", "[entire]", "3??") + _, flag, isexactnai, iserror = _parse(Float64, str) + @test (flag == true) & (isexactnai == false) & (iserror == false) + end + x, flag, isexactnai, iserror = _parse(Float64, "[garbage]") + @test (isexactnai == false) & (iserror == true) + @test isnai(x) +end diff --git a/test/intervals/real_interface.jl b/test/intervals/real_interface.jl new file mode 100644 index 000000000..e86c451c2 --- /dev/null +++ b/test/intervals/real_interface.jl @@ -0,0 +1,319 @@ +using Test +using IntervalArithmetic +using IntervalArithmetic: InconclusiveBooleanOperation, _unsafe_interval + +@testset "numtype" begin + @test numtype(interval(1, 2)) == Float64 + @test numtype(interval(Float32, 1, 2)) == Float32 + @test numtype(BareInterval{Float32}) == Float32 + @test numtype(Interval{Float32}) == Float32 + @test numtype(Complex{Interval{Float32}}) == Float32 + @test numtype(Complex{Float32}) == Float32 + @test numtype(bareinterval(1, 2)) == Float64 + @test numtype(Int) == Int + @test numtype(1.0f0) == Float32 + @test numtype(π) == Irrational{:π} +end + +@testset "float and big" begin + @test float(bareinterval(1//2, 3//4)) === bareinterval(Float64, 1//2, 3//4) + @test bounds(float(bareinterval(1//3, 1//2))) == (0.3333333333333333, 0.5) + @test typeof(float(bareinterval(1//3, 1//2))) == BareInterval{Float64} + + x = float(interval(1//2, 3//4, def)) + @test typeof(x) == Interval{Float64} + @test decoration(x) == def + @test !isguaranteed(float(convert(Interval{Float64}, 1))) + @test isguaranteed(float(interval(1))) + + @test typeof(big(bareinterval(1, 2))) == BareInterval{BigFloat} + y = big(bareinterval(1//3, 1//2)) + @test typeof(y) == BareInterval{Rational{BigInt}} + @test bounds(y) == (1//3, 1//2) + @test decoration(big(interval(1, 2, def))) == def + + @test isnai(@test_logs (:warn, r"interval part of NaI") float(nai(Interval{Float64}))) + @test isnai(@test_logs (:warn,) big(nai(Interval{Float64}))) +end + +@testset "zero, one, floatmin, floatmax" begin + a = interval(0.1, 1.1) + b = interval(0.9, 2.0) + + @test isa(zero(b), Interval) + @test isthin(zero(b), 0.0) + @test isequal_interval(zero(b), zero(typeof(b))) + @test isthin(one(a), 1.0) + @test isequal_interval(one(a), one(typeof(a))) + @test isthin(one(a), big(1.0)) + @test !isequal_interval(a, b) + + @test bounds(zero(BareInterval{Float64})) == (0.0, 0.0) + @test bounds(one(BareInterval{Float64})) == (1.0, 1.0) + @test bounds(floatmin(BareInterval{Float64})) == (floatmin(Float64), floatmin(Float64)) + @test bounds(floatmax(BareInterval{Float64})) == (floatmax(Float64), floatmax(Float64)) + @test zero(bareinterval(1, 2)) === zero(BareInterval{Float64}) + @test one(bareinterval(1, 2)) === one(BareInterval{Float64}) + @test floatmin(bareinterval(1, 2)) === floatmin(BareInterval{Float64}) + @test floatmax(bareinterval(1, 2)) === floatmax(BareInterval{Float64}) + + @test (decoration(zero(Interval{Float64})) == com) & isguaranteed(zero(Interval{Float64})) + @test (decoration(one(Interval{Float64})) == com) & isguaranteed(one(Interval{Float64})) + @test !isguaranteed(zero(convert(Interval{Float64}, 1))) + @test !isguaranteed(one(convert(Interval{Float64}, 1))) + + @test isequal_interval(floatmin(typeof(a)), interval(floatmin(Float64))) + @test isequal_interval(floatmax(typeof(a)), interval(floatmax(Float64))) + @test isequal_interval(floatmin(a), floatmin(typeof(a))) + @test isequal_interval(floatmax(a), floatmax(typeof(a))) + + @test isthinzero(zero(Interval{Float64})) + @test isthinzero(zero(Complex{Interval{Float64}})) + @test isthinone(one(Interval{Float64})) + @test isthinone(one(Complex{Interval{Float64}})) + @test isequal_interval(zero(Complex{Interval{Float64}}), complex(zero(Interval{Float64}), zero(Interval{Float64}))) + @test isequal_interval(one(Complex{Interval{Float64}}), complex(one(Interval{Float64}), zero(Interval{Float64}))) + @test isequal_interval(zero(complex(interval(1), interval(2))), zero(Complex{Interval{Float64}})) + @test isequal_interval(one(complex(interval(1), interval(2))), one(Complex{Interval{Float64}})) + + @test bounds(zero(BareInterval{Rational{Int64}})) == (0//1, 0//1) + @test bounds(one(Interval{Rational{Int64}})) == (1//1, 1//1) + @test_throws MethodError floatmin(BareInterval{Rational{Int64}}) + @test_throws MethodError floatmax(BareInterval{Rational{Int64}}) + + @test isequal_interval(zero(Interval{Float64}), interval(0)) + @test isequal_interval(zero(interval(0, 1)), interval(0)) +end + +@testset "typemin and typemax" begin + a = interval(0.1, 1.1) + + @test bounds(typemin(BareInterval{Float64})) == (-Inf, -floatmax(Float64)) + @test bounds(typemax(BareInterval{Float64})) == (floatmax(Float64), Inf) + @test isequal_interval(typemin(typeof(a)), interval(-Inf, nextfloat(-Inf))) + @test isequal_interval(typemax(typeof(a)), interval(prevfloat(Inf), Inf)) + @test isequal_interval(typemin(a), typemin(typeof(a))) + @test isequal_interval(typemax(a), typemax(typeof(a))) + @test (decoration(typemin(Interval{Float64})) == dac) & isguaranteed(typemin(Interval{Float64})) + @test (decoration(typemax(Interval{Float64})) == dac) & isguaranteed(typemax(Interval{Float64})) + @test bounds(typemin(Interval{Float32})) == (-Inf32, -floatmax(Float32)) + @test_throws MethodError typemin(BareInterval{Rational{Int64}}) + @test_throws MethodError typemax(BareInterval{Rational{Int64}}) +end + +@testset "eps" begin + a = interval(0.1, 1.1) + + @test bounds(eps(BareInterval{Float64})) == (eps(Float64), eps(Float64)) + @test decoration(eps(Interval{Float64})) == com + @test isequal_interval(eps(typeof(a)), eps(one(typeof(a)))) + + @test bounds(eps(bareinterval(1.0))) == (2.220446049250313e-16, 2.220446049250313e-16) + @test bounds(eps(bareinterval(1.0, 2.0))) == (2.220446049250313e-16, 4.440892098500626e-16) + @test bounds(eps(bareinterval(-1.0, 1.0))) == (5.0e-324, 2.220446049250313e-16) + @test bounds(eps(bareinterval(-Inf, Inf))) == (5.0e-324, Inf) + @test bounds(eps(interval(0.0))) == (5.0e-324, 5.0e-324) + @test isequal_interval(eps(emptyinterval(BareInterval{Float64})), emptyinterval(BareInterval{Float64})) + @test isequal_interval(eps(bareinterval(-3.0, 1.0)), bareinterval(eps(0.0), eps(3.0))) + + @test isequal_interval(eps(interval(1e-12, 1.0)), interval(eps(1e-12), eps(1.0))) + @test decoration(eps(interval(1e-12, 1.0))) == def + @test isequal_interval(eps(interval(1.0, 1.5)), interval(eps(1.0))) + @test decoration(eps(interval(1.0, 1.5))) == com + @test isequal_interval(eps(interval(-3.0, 1.0)), interval(eps(0.0), eps(3.0))) + @test isequal_interval(eps(interval(-1.0, 2.0)), interval(eps(0.0), eps(2.0))) + @test isequal_interval(eps(interval(1.0, Inf)), interval(eps(1.0), Inf)) + @test decoration(eps(interval(1.0, Inf))) == def + @test decoration(eps(interval(-Inf, Inf))) == def + @test decoration(eps(interval(1.0, 1.0, def))) == def + @test isequal_interval(eps(emptyinterval()), emptyinterval()) + @test decoration(eps(emptyinterval())) == trv + @test isnai(@test_logs eps(nai())) + @test !isguaranteed(eps(convert(Interval{Float64}, 1))) + + @test_throws MethodError eps(BareInterval{Rational{Int64}}) + @test_throws MethodError eps(interval(1//2)) +end + +@testset "hash" begin + @test hash(bareinterval(1, 2)) == hash(bareinterval(1, 2)) + @test hash(interval(1, 2)) == hash(interval(1, 2)) + @test hash(interval(1, 2)) == hash(interval(1, 2, def)) + @test hash(interval(1, 2)) == hash(convert(Interval{Float64}, interval(1, 2))) + @test hash(bareinterval(-0.0)) == hash(bareinterval(0.0)) + + x = interval(Float64, 1, 2) + y = interval(BigFloat, 1, 2) + @test isequal_interval(x, y) + @test hash(x) == hash(y) + + x = I"0.1" + y = interval(BigFloat, x) + @test isequal_interval(x, y) + @test hash(x) == hash(y) + + x = interval(1, 2) + y = interval(1, 3) + @test !isequal_interval(x, y) + @test hash(x) != hash(y) + + @test Dict(interval(1, 2) => 1)[interval(1, 2)] == 1 +end + +@testset "== and <" begin + x, y = interval(1), interval(2) + + @test x == x + @test x == 1 + @test x != y + @test interval(1, 2) != interval(3, 4) + @test_throws InconclusiveBooleanOperation interval(1, 2) != 2 + @test_throws InconclusiveBooleanOperation interval(1, 2) != y + @test_throws InconclusiveBooleanOperation y != interval(1, 2) + @test_throws InconclusiveBooleanOperation interval(1, 2) == interval(1, 2) + @test isequal(x, interval(1)) + + @test isone(x) + @test !iszero(x) + @test_throws InconclusiveBooleanOperation iszero(interval(0, 1)) + + @test x < y + @test x < 2 + @test !(x > y) + @test !(x < x) + @test !(x < 1) + @test interval(1, 2) < interval(3, 4) + @test !(interval(3, 4) < interval(1, 2)) + @test_throws InconclusiveBooleanOperation x < interval(1, 2) + @test_throws InconclusiveBooleanOperation interval(1, 3) < interval(2, 4) + @test x ≤ x + @test x ≥ x + @test_throws InconclusiveBooleanOperation interval(1, 3) ≤ interval(2, 4) + + @test_throws InconclusiveBooleanOperation nai(Interval{Float64}) == nai(Interval{Float64}) + @test_throws InconclusiveBooleanOperation nai(Interval{Float64}) < interval(1) + @test (emptyinterval(Interval{Float64}) == emptyinterval(Interval{Float64})) == false + @test emptyinterval(Interval{Float64}) < interval(1) + + @test isequal_interval(maximum([interval(1, 2), interval(3, 4)]), interval(3, 4)) + @test all(isequal_interval.(sort([interval(3, 4), interval(1, 2)]), [interval(1, 2), interval(3, 4)])) +end + +@testset "isfinite, isnan, isinteger, issubnormal" begin + x = interval(1) + + @test isfinite(x) + @test isfinite(interval(1, 2)) + @test !isinf(interval(1, 2)) + @test isfinite(emptyinterval(Interval{Float64})) + @test_throws InconclusiveBooleanOperation isfinite(interval(1, Inf)) + @test_throws InconclusiveBooleanOperation isfinite(nai(Interval{Float64})) + + @test isnan(nai(Interval{Float64})) + @test !isnan(interval(1)) + + @test isinteger(x) + @test !isinteger(interval(1.5)) + @test !isinteger(interval(1.2, 1.8)) + @test !isinteger(interval(1.2, 1.9)) + @test !isinteger(emptyinterval(Interval{Float64})) + @test_throws InconclusiveBooleanOperation isinteger(interval(1, 2)) + @test_throws InconclusiveBooleanOperation isinteger(interval(1.5, 2.5)) + + @test issubnormal(interval(1e-320)) + @test issubnormal(interval(-1e-320, -1e-321)) + @test issubnormal(interval(floatmin(Float64)/4, floatmin(Float64)/2)) + @test issubnormal(interval(-floatmin(Float64)/2, -floatmin(Float64)/4)) + @test !issubnormal(interval(1.0)) + @test !issubnormal(interval(1, 2)) + @test !issubnormal(interval(0.0)) + @test !issubnormal(emptyinterval(Interval{Float64})) + @test_throws InconclusiveBooleanOperation issubnormal(interval(0.0, 1.0)) + @test_throws InconclusiveBooleanOperation issubnormal(interval(-floatmin(Float64), floatmin(Float64))) +end + +@testset "InconclusiveBooleanOperation display" begin + @test InconclusiveBooleanOperation <: Exception + + e = try + interval(1, 3) == interval(2, 4) + catch err + err + end + @test e isa InconclusiveBooleanOperation + msg = sprint(showerror, e) + @test startswith(msg, "InconclusiveBooleanOperation:") + @test occursin("==", msg) & occursin("isequal_interval", msg) + + e = try + interval(1, 3) < interval(2, 4) + catch err + err + end + @test occursin("strictprecedes", sprint(showerror, e)) + + e = try + isfinite(interval(1, Inf)) + catch err + err + end + @test occursin("isbounded", sprint(showerror, e)) + + e = try + isinteger(interval(1, 2)) + catch err + err + end + @test occursin("isthininteger", sprint(showerror, e)) + + e = try + issubnormal(interval(0.0, 1.0)) + catch err + err + end + @test occursin("issubset_interval", sprint(showerror, e)) +end + +@testset "disallowed Base set functions" begin + x = interval(1) + + @test_throws ArgumentError x ∈ x + @test_throws ArgumentError isempty(x) + @test_throws ArgumentError isapprox(x, x) + @test_throws ArgumentError isdisjoint(x, x) + @test_throws ArgumentError issubset(x, x) + @test_throws ArgumentError issetequal(x, x) + + @test_throws ArgumentError intersect(x) + @test_throws ArgumentError intersect(x, x) + @test_throws ArgumentError intersect(x, 2, [1], 4.0, 5) + @test_throws ArgumentError intersect(x, interval(2.0), interval(3.0)) + + @test_throws ArgumentError union(x) + @test_throws ArgumentError union(x, x) + @test_throws ArgumentError union(x, 2, [1], 4.0, 5) + @test_throws ArgumentError union(x, interval(2.0), interval(3.0)) + @test_throws ArgumentError symdiff(x, interval(2.0)) + @test_throws ArgumentError symdiff(x, interval(2.0), interval(3.0)) + @test_throws ArgumentError union!(BitSet(), x) + @test_throws ArgumentError union!(Int[], x) + + @test_throws ArgumentError setdiff(x) + @test_throws ArgumentError setdiff(x, x) + @test_throws ArgumentError setdiff(x, 2, [1], 4.0, 5) + @test_throws ArgumentError setdiff(x, interval(2.0), interval(3.0)) + @test_throws ArgumentError setdiff!(Set(), x) +end + +@testset "Broadcasting" begin + x = interval(1, 2) + + for f ∈ (+, -, *, /) + @test isequal_interval(f.(x, x), f(x, x)) + end +end + +@testset "Real behaviour" begin + @test size(interval(1)) == () + @test isequal_interval(real(interval(-1, 1)), interval(-1, 1)) +end diff --git a/test/intervals/rounding.jl b/test/intervals/rounding.jl new file mode 100644 index 000000000..ed9af52b9 --- /dev/null +++ b/test/intervals/rounding.jl @@ -0,0 +1,257 @@ +using Test +using IntervalArithmetic +using IntervalArithmetic: IntervalRounding, default_rounding, _fround, _round_expr, + _unsafe_bareinterval, @round, rootn, CRlibm, CoreMath, RoundingEmulator + +@testset "rounding configuration" begin + @test Base.issingletontype(IntervalRounding{:correct}) + @test default_rounding() === IntervalRounding{:correct}() + @test_throws ArgumentError IntervalArithmetic.configure(rounding = :bad) + @test IntervalArithmetic.configuration_options.rounding == :correct +end + +@testset "dispatch helpers" begin + @test _fround(+, 0.1, 0.2, RoundDown) === _fround(+, default_rounding(), 0.1, 0.2, RoundDown) + @test _fround(sqrt, 2.0, RoundUp) === _fround(sqrt, default_rounding(), 2.0, RoundUp) +end + +@testset "directed Float64 arithmetic" begin + @test _fround(+, 0.1, 0.2, RoundDown) == 0.3 + @test _fround(+, 0.1, 0.2, RoundUp) == 0.30000000000000004 == 0.1 + 0.2 + @test _fround(+, 0.1, 0.2, RoundDown) < _fround(+, 0.1, 0.2, RoundUp) + @test _fround(/, 1.0, 3.0, RoundDown) == 0.3333333333333333 + @test _fround(/, 1.0, 3.0, RoundUp) == 0.33333333333333337 == nextfloat(0.3333333333333333) + @test _fround(sqrt, 2.0, RoundDown) == 1.414213562373095 + @test _fround(sqrt, 2.0, RoundUp) == 1.4142135623730951 == sqrt(2.0) + @test _fround(inv, 3.0, RoundDown) == 0.3333333333333333 + @test _fround(inv, 3.0, RoundUp) == 0.33333333333333337 +end + +@testset "exact rational arithmetic" begin + for ir ∈ (IntervalRounding{:correct}(), IntervalRounding{:ulp}(), IntervalRounding{:none}()), + r ∈ (RoundDown, RoundUp) + @test _fround(+, ir, 1//3, 1//6, r) === 1//2 + @test _fround(-, ir, 1//2, 1//3, r) === 1//6 + @test _fround(*, ir, 2//3, 3//4, r) === 1//2 + @test _fround(/, ir, 1//2, 1//4, r) === 2//1 + @test _fround(inv, ir, 3//7, r) === 7//3 + @test _fround(^, ir, 1//2, 3, r) === 1//8 + end +end + +@testset "narrow float arithmetic" begin + @test _fround(+, 0.1f0, 0.2f0, RoundDown) === 0.29999998f0 + @test _fround(+, 0.1f0, 0.2f0, RoundUp) === 0.3f0 + @test _fround(+, Float16(0.1), Float16(0.2), RoundDown) === Float16(0.2998) + @test _fround(+, Float16(0.1), Float16(0.2), RoundUp) === Float16(0.3) +end + +@testset "BigFloat via MPFR" begin + lo = _fround(+, big"0.1", big"0.2", RoundDown) + hi = _fround(+, big"0.1", big"0.2", RoundUp) + @test lo isa BigFloat + @test lo < hi + @test precision(lo) == precision(BigFloat) + x = BigFloat("0.1"; precision = 64) + y = BigFloat("0.2"; precision = 128) + @test precision(_fround(+, promote(x, y)..., RoundDown)) == 128 + @test _fround(sqrt, big"2.0", RoundDown) isa BigFloat + @test _fround(sqrt, big"2.0", RoundDown) < _fround(sqrt, big"2.0", RoundUp) + @test _fround(sqrt, big"2.0", RoundDown) ≤ sqrt(big"2.0") ≤ _fround(sqrt, big"2.0", RoundUp) +end + +@testset "correct mode uses RoundingEmulator" begin + for T ∈ (Float32, Float64) + x, y = T(0.1), T(0.3) + for (f, down, up) ∈ ((+, RoundingEmulator.add_down, RoundingEmulator.add_up), + (-, RoundingEmulator.sub_down, RoundingEmulator.sub_up), + (*, RoundingEmulator.mul_down, RoundingEmulator.mul_up), + (/, RoundingEmulator.div_down, RoundingEmulator.div_up)) + @test _fround(f, IntervalRounding{:correct}(), x, y, RoundDown) === down(x, y) + @test _fround(f, IntervalRounding{:correct}(), x, y, RoundUp) === up(x, y) + end + @test _fround(sqrt, IntervalRounding{:correct}(), T(2), RoundDown) === RoundingEmulator.sqrt_down(T(2)) + @test _fround(sqrt, IntervalRounding{:correct}(), T(2), RoundUp) === RoundingEmulator.sqrt_up(T(2)) + end +end + +@testset "ulp mode arithmetic" begin + @test _fround(+, IntervalRounding{:ulp}(), 0.1, 0.2, RoundDown) === prevfloat(0.1 + 0.2) + @test _fround(+, IntervalRounding{:ulp}(), 0.1, 0.2, RoundUp) === nextfloat(0.1 + 0.2) === 0.3000000000000001 + @test _fround(+, IntervalRounding{:ulp}(), 0.1, 0.2, RoundUp) > _fround(+, IntervalRounding{:correct}(), 0.1, 0.2, RoundUp) + for T ∈ (Float16, Float32) + @test _fround(*, IntervalRounding{:ulp}(), T(0.1), T(0.2), RoundDown) === + T(prevfloat(Float64(T(0.1)) * Float64(T(0.2))), RoundDown) + @test _fround(sqrt, IntervalRounding{:ulp}(), T(2), RoundUp) === + T(nextfloat(sqrt(Float64(T(2)))), RoundUp) + end + @test _fround(sqrt, IntervalRounding{:ulp}(), 2.0, RoundDown) === prevfloat(sqrt(2.0)) +end + +@testset "none mode" begin + @test _fround(+, IntervalRounding{:none}(), 0.1, 0.2, RoundDown) === + _fround(+, IntervalRounding{:none}(), 0.1, 0.2, RoundUp) === 0.30000000000000004 + @test _fround(sqrt, IntervalRounding{:none}(), 2.0, RoundDown) === sqrt(2.0) + @test _fround(inv, IntervalRounding{:none}(), 3.0, RoundUp) === inv(3.0) + @test _fround(sin, IntervalRounding{:none}(), 1.0, RoundDown) === sin(1.0) + @test _fround(^, IntervalRounding{:none}(), 2.0, 0.5, RoundDown) === 2.0^0.5 + @test _fround(atan, IntervalRounding{:none}(), 1.0, 2.0, RoundUp) === atan(1.0, 2.0) + @test _fround(rootn, IntervalRounding{:none}(), 8.0, 3, RoundDown) === 8.0^(1//3) +end + +@testset "inv drops the explicit rounding type" begin + # src/intervals/rounding.jl: `_fround(inv, ...)` re-dispatches through `default_rounding()` + @test _fround(inv, IntervalRounding{:ulp}(), 3.0, RoundDown) == 0.3333333333333333 + @test _fround(/, IntervalRounding{:ulp}(), 1.0, 3.0, RoundDown) == 0.33333333333333326 +end + +@testset "one-argument functions, correct mode" begin + crlibm = (exp, expm1, log, log2, log10, log1p, sin, sinpi, cos, cospi, tan, asin, acos, + atan, sinh, cosh) + for f ∈ crlibm, T ∈ (Float32, Float64) + x = T(0.7) + down = _fround(f, x, RoundDown) + up = _fround(f, x, RoundUp) + @test typeof(down) == typeof(up) == T + @test down < up + @test down ≤ f(big(x)) ≤ up + end + @test _fround(sin, 1.0, RoundDown) == 0.8414709848078965 + @test _fround(sin, 1.0, RoundUp) == 0.8414709848078966 + @test typeof(_fround(sin, IntervalRounding{:correct}(), Float16(1), RoundDown)) == Float16 + + mpfr = ((cbrt, 0.5), (exp2, 0.5), (exp10, 0.5), (cot, 0.5), (sec, 0.5), (csc, 0.5), + (acot, 1.0), (tanh, 0.5), (asinh, 0.5), (coth, 0.5), (sech, 0.5), (csch, 0.5), + (acosh, 2.0), (atanh, 0.5), (acoth, 2.0)) + for (f, x) ∈ mpfr + down = _fround(f, x, RoundDown) + up = _fround(f, x, RoundUp) + @test typeof(down) == typeof(up) == BigFloat + @test down < up + @test down ≤ f(big(x)) ≤ up + end + @test _fround(acot, 1.0, RoundDown) < acot(big(1.0)) < _fround(acot, 1.0, RoundUp) + @test _fround(acoth, 2.0, RoundDown) < acoth(big(2.0)) < _fround(acoth, 2.0, RoundUp) +end + +@testset "one-argument functions, ulp mode" begin + # CoreMath ships no library for 32-bit systems + if isdefined(CoreMath, :libcoremath) + working = ((cbrt, 0.5), (exp, 0.5), (exp2, 0.5), (exp10, 0.5), (expm1, 0.5), (log, 0.5), + (log2, 0.5), (log10, 0.5), (log1p, 0.5), (sin, 0.5), (sinpi, 0.5), (cos, 0.5), + (cospi, 0.5), (tan, 0.5), (asin, 0.5), (acos, 0.5), (atan, 0.5), (sinh, 0.5), + (tanh, 0.5), (asinh, 0.5), (cosh, 0.5), (acosh, 2.0), (atanh, 0.5)) + for (f, x) ∈ working, T ∈ (Float32, Float64) + down = _fround(f, IntervalRounding{:ulp}(), T(x), RoundDown) + up = _fround(f, IntervalRounding{:ulp}(), T(x), RoundUp) + @test typeof(down) == typeof(up) == T + @test down < up + @test down ≤ f(big(T(x))) ≤ up + end + end + # no CoreMath routine: `:ulp` falls back to `:correct`, cf. src/intervals/rounding.jl + for (f, x) ∈ ((cot, 0.5), (sec, 0.5), (csc, 0.5), (acot, 1.0), (coth, 0.5), (sech, 0.5), + (csch, 0.5), (acoth, 2.0)), + T ∈ (Float32, Float64) + down = _fround(f, IntervalRounding{:ulp}(), T(x), RoundDown) + up = _fround(f, IntervalRounding{:ulp}(), T(x), RoundUp) + @test down == _fround(f, IntervalRounding{:correct}(), T(x), RoundDown) + @test up == _fround(f, IntervalRounding{:correct}(), T(x), RoundUp) + @test down < up + @test down ≤ f(big(T(x))) ≤ up + end +end + +@testset "two-argument functions" begin + @test _fround(^, 2.0, 0.5, RoundDown) == prevfloat(sqrt(2.0)) + @test _fround(^, 2.0, 0.5, RoundUp) == 1.4142135623730951 + @test _fround(^, 2.0, 0.5, RoundDown) isa BigFloat + @test _fround(^, 2.0, 3.0, RoundDown) == 8.0 + @test _fround(^, 2.0, 3, RoundDown) == _fround(^, 2.0, 3.0, RoundDown) + @test _fround(atan, 1.0, 2.0, RoundDown) < atan(big(1.0), big(2.0)) < _fround(atan, 1.0, 2.0, RoundUp) + @test _fround(atan, 1.0, 2.0, RoundDown) isa BigFloat + # CoreMath ships no library for 32-bit systems + if isdefined(CoreMath, :libcoremath) + for T ∈ (Float32, Float64) + @test _fround(^, IntervalRounding{:ulp}(), T(2), T(0.5), RoundDown) === prevfloat(CoreMath.cr_pow(T(2), T(0.5))) + @test _fround(^, IntervalRounding{:ulp}(), T(2), T(0.5), RoundUp) === nextfloat(CoreMath.cr_pow(T(2), T(0.5))) + @test _fround(atan, IntervalRounding{:ulp}(), T(1), T(2), RoundDown) === prevfloat(CoreMath.cr_atan2(T(1), T(2))) + @test _fround(atan, IntervalRounding{:ulp}(), T(1), T(2), RoundUp) === nextfloat(CoreMath.cr_atan2(T(1), T(2))) + end + end +end + +@testset "rootn" begin + @test _fround(rootn, 8.0, 3, RoundDown) ≤ 2 ≤ _fround(rootn, 8.0, 3, RoundUp) + @test _fround(rootn, 8.0, 3, RoundUp) - _fround(rootn, 8.0, 3, RoundDown) ≤ 2eps(2.0) + @test _fround(rootn, 2.0, 3, RoundUp) isa BigFloat + @test _fround(rootn, 2.0, 3, RoundDown) ≤ cbrt(big"2.0") ≤ _fround(rootn, 2.0, 3, RoundUp) +end + +@testset "@round" begin + a = bareinterval(0.1) + b = bareinterval(0.2) + @test isequal_interval(@round(Float64, inf(a) + inf(b), sup(a) + sup(b)), + _unsafe_bareinterval(Float64, 0.3, 0.30000000000000004)) + @test isequal_interval(@round(Float64, min(inf(a) + inf(b), inf(a) - inf(b)), + max(sup(a) + sup(b), sup(a) - sup(b))), + _unsafe_bareinterval(Float64, -0.1, 0.30000000000000004)) + @test isequal_interval(@round(Float64, -inf(a), -inf(a)), _unsafe_bareinterval(Float64, -0.1, -0.1)) + @test isequal_interval(@round(Float64, typemin(Float64), typemax(Float64)), bareinterval(-Inf, Inf)) + @test isequal_interval(@round(Float64, sqrt(2.0), sqrt(2.0)), + _unsafe_bareinterval(Float64, 1.414213562373095, 1.4142135623730951)) + @test isequal_interval(@round(Rational{Int64}, 1//3 + 1//6, 1//3 + 1//6), bareinterval(1//2)) +end + +@testset "_round_expr" begin + ex = _round_expr(:(a + b), RoundDown) + @test (ex.head === :call) & (ex.args[1] === :_fround) & (ex.args[2] === :+) + @test (ex.args[3] == Expr(:escape, :a)) & (ex.args[4] == Expr(:escape, :b)) & (ex.args[5] === RoundDown) + ex = _round_expr(:(sin(a)), RoundUp) + @test (ex.args[1] === :_fround) & (ex.args[2] === :sin) & (ex.args[3] == Expr(:escape, :a)) & (ex.args[4] === RoundUp) + ex = _round_expr(:(min(a + b, c + d)), RoundDown) + @test (ex.args[1] === :min) & all(arg -> arg.args[1] === :_fround, ex.args[2:3]) + @test _round_expr(:(typemin(T)), RoundDown) == Expr(:escape, :(typemin(T))) + @test _round_expr(:(-a), RoundUp) == Expr(:escape, :(-a)) + ex = _round_expr(:(_unbounded_mul(a, b)), RoundDown) + @test (ex.args[1] === :_unbounded_mul) & (ex.args[4] === RoundDown) + @test _round_expr(1.0, RoundDown) === 1.0 + @test _round_expr(:x, RoundUp) === :x +end + +IntervalArithmetic.configure(rounding = :correct) + +@testset "end-to-end correct rounding" begin + @test default_rounding() === IntervalRounding{:correct}() + @test isequal_interval(sin(interval(0.5)), interval(0.47942553860420295, 0.479425538604203)) + tiny = interval(0, floatmin()) + huge = interval(floatmax(), Inf) + @test isequal_interval(tiny * tiny, interval(0, nextfloat(0.0))) + @test isequal_interval(huge * huge, interval(floatmax(), Inf)) + @test isequal_interval(huge / tiny, interval(floatmax(), Inf)) + @test isequal_interval(tiny / huge, interval(0, nextfloat(0.0))) + @test bounds(bareinterval(0.1) + bareinterval(0.2)) == (0.3, 0.30000000000000004) +end + +# `Base.invokelatest` advances the world age past `configure` +try + IntervalArithmetic.configure(rounding = :ulp) + Base.invokelatest() do + @testset "end-to-end ulp rounding" begin + @test default_rounding() === IntervalRounding{:ulp}() + @test bounds(bareinterval(0.1) + bareinterval(0.2)) == (0.3, 0.3000000000000001) + end + end + + IntervalArithmetic.configure(rounding = :none) + Base.invokelatest() do + @testset "end-to-end no rounding" begin + @test default_rounding() === IntervalRounding{:none}() + x = bareinterval(0.1) + bareinterval(0.2) + @test inf(x) == sup(x) == 0.30000000000000004 + @test isequal_interval(sin(interval(0.5)), interval(0.479425538604203, 0.479425538604203)) + end + end +finally + IntervalArithmetic.configure(rounding = :correct) +end diff --git a/test/itl/libieeep1788_reduction.itl b/test/itl/libieeep1788_reduction.itl deleted file mode 100644 index 9d3dd9c44..000000000 --- a/test/itl/libieeep1788_reduction.itl +++ /dev/null @@ -1,53 +0,0 @@ -/* - -Unit tests from libieeep1788 for reduction operations -(Original author: Marco Nehmeier) -converted into portable ITL format by Oliver Heimlich. - -Copyright 2013-2015 Marco Nehmeier (nehmeier@informatik.uni-wuerzburg.de) -Copyright 2015-2017 Oliver Heimlich (oheim@posteo.de) - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -*/ - -testcase minimal_sum_test { - sum_nearest {1.0, 2.0, 3.0} = 6.0; - sum_nearest {1.0, 2.0, NaN, 3.0} = NaN; - sum_nearest {1.0, -infinity, 2.0, infinity, 3.0} = NaN; -} - -testcase minimal_sum_abs_test { - sum_abs_nearest {1.0, -2.0, 3.0} = 6.0; - sum_abs_nearest {1.0, -2.0, NaN, 3.0} = NaN; - sum_abs_nearest {1.0, -infinity, 2.0, infinity, 3.0} = infinity; -} - -testcase minimal_sum_sqr_test { - sum_sqr_nearest {1.0, 2.0, 3.0} = 14.0; - sum_sqr_nearest {1.0, 2.0, NaN, 3.0} = NaN; - sum_sqr_nearest {1.0, -infinity, 2.0, infinity, 3.0} = infinity; -} - -testcase minimal_dot_test { - dot_nearest {1.0, 2.0, 3.0} {1.0, 2.0, 3.0} = 14.0; - - dot_nearest {0x10000000000001p0, 0x1p104} {0x0fffffffffffffp0, -1.0} = -1.0; - - dot_nearest {1.0, 2.0, NaN, 3.0} {1.0, 2.0, 3.0, 4.0} = NaN; - dot_nearest {1.0, 2.0, 3.0, 4.0} {1.0, 2.0, NaN, 3.0} = NaN; - - dot_nearest {1.0, 2.0, 0.0, 4.0} {1.0, 2.0, infinity, 3.0} = NaN; - dot_nearest {1.0, 2.0, -infinity, 4.0} {1.0, 2.0, 0.0, 3.0} = NaN; -} - diff --git a/test/piecewise.jl b/test/piecewise.jl new file mode 100644 index 000000000..c0c536994 --- /dev/null +++ b/test/piecewise.jl @@ -0,0 +1,340 @@ +using Test +using IntervalArithmetic +using IntervalArithmetic: lowerbound, upperbound, rightof, leftof, in_domain, + intersect_domain, isempty_domain, overlap_domain, domain_string + +@testset "Domain construction" begin + d = Domain{:closed,:open}(0, 1) + @test d isa Domain{:closed,:open,Int,Int} + @test d.lo == 0 && d.hi == 1 + + @test Domain{:open,:closed}(-Inf, 0) isa Domain{:open,:closed,Float64,Int} + + @test Domain((0, :closed), (1, :open)) == Domain{:closed,:open}(0, 1) + + @test Domain(interval(1, 2)) === Domain{:closed,:closed,Float64,Float64}(1.0, 2.0) + @test Domain(entireinterval()) === Domain{:closed,:closed,Float64,Float64}(-Inf, Inf) + + @test Domain() === Domain{:open,:open,Float64,Float64}(Inf, -Inf) + @test isempty_domain(Domain()) + + err = try Domain{:oopen,:closed}(0, 1) catch e; e end + @test err isa ArgumentError + @test err.msg == "Domain bound must be either :open or :closed, got oopen and closed instead" + @test_throws ArgumentError Domain{:open,:close}(0, 1) +end + +@testset "Domain accessors" begin + d = Domain{:closed,:open}(0, 1) + @test lowerbound(d) == (0, :closed) + @test upperbound(d) == (1, :open) + @test inf(d) === d.lo + @test sup(d) === d.hi +end + +@testset "rightof and leftof" begin + @test rightof(1.0, (1, :closed)) + @test !rightof(1.0, (1, :open)) + @test rightof(2.0, (1, :open)) + @test !rightof(0.0, (1, :closed)) + + @test leftof(1.0, (1, :closed)) + @test !leftof(1.0, (1, :open)) + @test leftof(0.0, (1, :open)) + @test !leftof(2.0, (1, :closed)) + + d1 = Domain{:open,:closed}(0, 1) + d2 = Domain{:open,:open}(0, 1) + d3 = Domain{:open,:closed}(1, 2) + d4 = Domain{:closed,:closed}(1, 2) + @test !leftof(d1, d2) + @test leftof(d1, d3) + @test !leftof(d1, d4) + @test leftof(d2, d3) + @test leftof(d2, d4) + @test leftof(d1, Domain{:open,:closed}(5, 6)) +end + +@testset "in_domain" begin + @test in_domain(0.5, Domain{:closed,:closed}(0, 1)) + @test !in_domain(0, Domain{:open,:closed}(0, 1)) + @test !in_domain(1, Domain{:closed,:open}(0, 1)) + @test in_domain(0, Domain{:closed,:open}(0, 1)) + @test in_domain(1, Domain{:open,:closed}(0, 1)) +end + +@testset "intersect_domain" begin + d1 = Domain{:closed,:open}(0, 10) + d2 = Domain{:closed,:open}(2, 15) + d3 = Domain{:open,:closed}(4, 7) + d4 = Domain{:open,:closed}(-20, 3) + + @test intersect_domain(d1, d2) == Domain{:closed,:open}(2, 10) + @test intersect_domain(d1, d3) == Domain{:open,:closed}(4, 7) + @test intersect_domain(d1, d4) == Domain{:closed,:closed}(0, 3) + @test intersect_domain(d2, d3) == Domain{:open,:closed}(4, 7) + @test intersect_domain(d2, d4) == Domain{:closed,:closed}(2, 3) + @test intersect_domain(d3, d4) == Domain() + + @test intersect_domain(Domain{:closed,:closed}(0, 2), Domain{:open,:open}(1, 3)) == + Domain{:open,:closed}(1, 2) + + @test intersect_domain(Domain{:closed,:closed}(0, 1), Domain{:closed,:open}(0, 1)) == + Domain{:closed,:open}(0, 1) + @test intersect_domain(Domain{:open,:closed}(0, 1), Domain{:closed,:closed}(0, 1)) == + Domain{:open,:closed}(0, 1) +end + +@testset "isempty_domain" begin + @test isempty_domain(Domain{:open,:open}(1, 1)) + @test isempty_domain(Domain{:open,:closed}(1, 1)) + @test !isempty_domain(Domain{:closed,:closed}(1, 1)) + @test !isempty_domain(Domain{:open,:open}(1, 2)) + @test isempty_domain(Domain{:closed,:closed}(2, 1)) + @test isempty_domain(Domain()) +end + +@testset "Constant" begin + c = Constant(1.2) + @test c isa Constant{Float64} + @test c.value == 1.2 + + @test c(22.2) === 1.2 + @test c(3) === 1.2 + @test_throws MethodError c("some string") + + @test isguaranteed(c(convert(Interval{Float64}, 1))) + + x = c(interval(0, 1.3)) + @test x isa Interval{Float64} + @test isequal_interval(x, interval(1.2)) + @test decoration(x) === com && isguaranteed(x) + + @test c(interval(Float32, 0, 1)) isa Interval{Float64} + + @test Returns(1.2)(interval(0, 1)) === 1.2 + @test Constant(1.2)(interval(0, 1)) isa Interval +end + +@testset "Piecewise construction" begin + d1 = Domain{:closed,:closed}(0, 1) + d2 = Domain{:open,:closed}(1, 2) + d3 = Domain{:closed,:closed}(1, 2) + + p = Piecewise(d1 => Constant(1.0), d2 => identity) + @test p isa Piecewise{2,1} + @test p.continuity == (-1,) + @test p.singularities == sup.(domains(p)[1:end-1]) == (1,) + + p2 = Piecewise(d1 => Constant(1.0), d2 => identity; continuity = [0]) + @test p2.continuity == (0,) + + @test_throws ArgumentError Piecewise(d2 => Constant(0), d1 => Constant(1)) + @test_throws ArgumentError Piecewise(d1 => Constant(0), d3 => Constant(1)) + + @test_throws MethodError Piecewise( + (Domain{:closed,:closed}(1, 2), Domain{:open,:open}(2, 3)), + (sin, cos), (-1,), (2, 3)) + @test_throws ArgumentError Piecewise( + (Domain{:closed,:closed}(1, 2), Domain{:open,:open}(2, 3)), + (sin, cos), (-1, -1), (2, 3)) + @test_throws MethodError Piecewise( + (Domain{:closed,:closed}(1, 2), Domain{:open,:open}(2, 3)), + (sin, cos, log), (-1,), (2)) + + p3 = Piecewise((d1, d2), (identity, identity)) + @test p3 isa Piecewise{2,1} + @test p3.continuity == (-1,) + + @test_throws ArgumentError Piecewise((d1, d2), (identity, identity), (-1, -1)) + @test_throws ArgumentError Piecewise((d1, d2), (identity,)) +end + +@testset "domains, pieces and discontinuities" begin + d1 = Domain{:closed,:closed}(0, 1) + d2 = Domain{:open,:closed}(1, 2) + p = Piecewise(d1 => Constant(1.0), d2 => identity) + p2 = Piecewise(d1 => Constant(1.0), d2 => identity; continuity = [0]) + + @test domains(p) === p.domains == (d1, d2) + ps = collect(pieces(p)) + @test length(ps) == 2 + @test ps[1] == (d1, Constant(1.0)) && ps[2] == (d2, identity) + + @test discontinuities(p) == [1] + @test discontinuities(p, 1) == [1] + @test discontinuities(p2) == Int[] + @test discontinuities(p2, 1) == [1] + @test discontinuities(p2, 100) == [1] +end + +@testset "domain_string and show" begin + @test domain_string(Domain{:open,:closed}(-Inf, 0)) == "(-Inf, 0]" + @test domain_string(Domain{:closed,:open}(0, 1)) == "[0, 1)" + + p = Piecewise(Domain{:closed,:closed}(0, 1) => Constant(1.0), Domain{:open,:closed}(1, 2) => identity) + @test domain_string(p) == "[0, 1] ∪ (1, 2]" + @test sprint(show, MIME("text/plain"), p) == + "Piecewise function with 2 pieces:\n [0, 1] -> Constant{Float64}(1.0)\n (1, 2] -> identity" +end + +@testset "overlap_domain and in_domain on Piecewise" begin + p = Piecewise(Domain{:closed,:closed}(0, 1) => Constant(1.0), Domain{:open,:closed}(1, 2) => identity) + pgap = Piecewise(Domain{:closed,:closed}(0, 1) => Constant(1.0), Domain{:closed,:closed}(2, 3) => Constant(2.0)) + + @test overlap_domain(Domain(interval(0.5, 0.6)), pgap) + @test overlap_domain(Domain(interval(2.5, 2.6)), pgap) + @test !overlap_domain(Domain(interval(1.5, 1.6)), pgap) + @test !overlap_domain(Domain(interval(4, 5)), pgap) + + @test in_domain(Domain(interval(0.2, 0.5)), p) + @test in_domain(Domain(interval(0.5, 1.5)), p) + @test in_domain(Domain(interval(1.5, 2)), p) + @test decoration(p(interval(0.2, 0.5))) === com + + @test in_domain(Domain(interval(0, 0.5)), p) + @test in_domain(Domain(interval(0.2, 0.5)), pgap) + @test in_domain(Domain(interval(2.2, 2.5)), pgap) + @test decoration(pgap(interval(2.2, 2.5))) === com + + @test !in_domain(Domain(interval(0, 5)), p) + @test !in_domain(Domain(interval(0.2, 2.5)), p) + @test !in_domain(Domain(interval(0, 3)), pgap) + @test !in_domain(Domain(interval(0.5, 2.5)), pgap) + + ppoint = Piecewise(Domain{:open,:open}(-1, 0) => identity, Domain{:open,:open}(0, 1) => identity) + @test in_domain(Domain(interval(-0.5, -0.2)), ppoint) + @test !in_domain(Domain(interval(-0.5, 0.5)), ppoint) + @test decoration(ppoint(interval(-0.5, 0.5))) === trv + @test_throws DomainError ppoint(0) +end + +@testset "Interval evaluation" begin + myabs = Piecewise( + Domain{:open,:closed}(-Inf, 0) => x -> -x, + Domain{:open,:open}(0, Inf) => identity + ) + + @test isequal_interval(myabs(interval(-5, 5)), interval(0, 5)) + @test decoration(myabs(interval(-5, 5))) === def + @test isequal_interval(myabs(interval(1, 2)), interval(1, 2)) + @test decoration(myabs(interval(1, 2))) === com + @test isequal_interval(myabs(interval(-10, -1)), interval(1, 10)) + @test decoration(myabs(interval(-10, -1))) === com + + p = Piecewise(Domain{:closed,:closed}(0, 1) => Constant(1.0), Domain{:open,:closed}(1, 2) => identity) + @test isempty_interval(p(interval(3, 4))) + @test decoration(p(interval(3, 4))) === trv + + x = p(interval(-1, 0.5)) + @test isequal_interval(x, interval(1)) && decoration(x) === trv && isguaranteed(x) + + x = p(interval(0.5, 1.5)) + @test isequal_interval(x, interval(1, 1.5)) && decoration(x) === def && isguaranteed(x) + + x = p(interval(Float32, 0.25, 0.5)) + @test x isa Interval{Float32} + @test isequal_interval(x, interval(Float32, 1, 1)) && decoration(x) === com && isguaranteed(x) + + @test isguaranteed(p(convert(Interval{Float64}, 0.5))) + @test !isguaranteed(p(convert(Interval{Float64}, 1.5))) + @test !isguaranteed(p(convert(Interval{Float64}, 5))) + + trvpiece = Piecewise( + Domain{:closed,:closed}(0, 1) => x -> hull(x, x), + Domain{:open,:closed}(1, 2) => identity; + continuity = [0] + ) + @test decoration(trvpiece(interval(0.2, 0.4))) === trv + + myabs0 = Piecewise( + Domain{:open,:closed}(-Inf, 0) => x -> -x, + Domain{:open,:open}(0, Inf) => identity; + continuity = [0] + ) + @test decoration(myabs0(interval(-11, 11))) === com + @test decoration(myabs0(interval(-11, 11, def))) === def + + pgap = Piecewise(Domain{:closed,:closed}(0, 1) => Constant(1.0), Domain{:closed,:closed}(2, 3) => Constant(2.0)) + x = pgap(interval(0.5, 2.5)) + @test isequal_interval(x, interval(1, 2)) && decoration(x) === trv +end + +@testset "Real evaluation" begin + myabs = Piecewise( + Domain{:open,:closed}(-Inf, 0) => x -> -x, + Domain{:open,:open}(0, Inf) => identity + ) + + @test myabs(-22.3) == 22.3 + @test myabs(3.0) == 3.0 + @test myabs(0.0) === -0.0 + + p = Piecewise(Domain{:closed,:closed}(0, 1) => Constant(1.0), Domain{:open,:closed}(1, 2) => identity) + err = try p(5.0) catch e; e end + @test err isa DomainError + @test err.msg == "piecewise function was called outside of its domain [0, 1] ∪ (1, 2]" +end + +@testset "Step function" begin + step = Piecewise( + Domain{:open,:closed}(-Inf, 0) => Constant(0), + Domain{:open,:open}(0, 1000) => Constant(1) + ) + + @test step(-1) == 0 + @test step(100) == 1 + @test isequal_interval(step(interval(-3.2, -2.1)), interval(0)) + @test decoration(step(interval(-3.33))) === com + @test isequal_interval(step(interval(2.3, 3.4)), interval(1)) + @test decoration(step(interval(4.44))) === com + @test isequal_interval(step(interval(-22.2, 33.3)), interval(0, 1)) + @test decoration(step(interval(-11, 11))) === def + @test decoration(step(interval(500, 2000))) === trv +end + +@testset "abs with continuity annotation" begin + myabs = Piecewise( + Domain{:open,:closed}(-Inf, 0) => x -> -x, + Domain{:open,:open}(0, Inf) => identity; + continuity = [0] + ) + + @test myabs(-1) == 1 + @test myabs(100) == 100 + @test isequal_interval(myabs(interval(-3.2, -2.1)), interval(2.1, 3.2)) + @test decoration(myabs(interval(-3.33))) === com + @test isequal_interval(myabs(interval(2.3, 3.4)), interval(2.3, 3.4)) + @test decoration(myabs(interval(4.444))) === com + @test isequal_interval(myabs(interval(-22.2, 33.3)), interval(0, 33.3)) + @test decoration(myabs(interval(-11, 11))) === com +end + +@testset "Out of domain" begin + window = Piecewise( + Domain{:open,:closed}(-π, π) => x -> 1/2 * (cos(x) + 1) + ) + + @test_throws DomainError window(123) + @test isequal_interval(window(interval(0, π)), interval(0, 1)) + # `-π` is `Float64(-π)`, a point of the input excluded by the open bound of the domain + @test decoration(window(interval(-π, 0))) === trv + @test decoration(window(interval(-3.14, 0))) === com + @test isequal_interval(window(interval(-10, 10)), interval(0, 1)) + @test decoration(window(interval(-10, 10))) === trv + @test isempty_interval(window(interval(100, 1000))) +end + +@testset "Singularities" begin + f = Piecewise( + Domain{:open,:closed}(0, 1) => Constant(0), + Domain{:open,:closed}(1, 2) => x -> 0.5x, + Domain{:open,:closed}(2, 3) => Constant(1), + Domain{:open,:open}(3, 4) => x -> (x-3)^2 + 1; + continuity = [-1, 0, 1] + ) + + @test decoration(f(interval(0.5, 1.5))) === def + @test decoration(f(interval(1.5, 2.5))) === com + @test decoration(f(interval(2.5, 3.5))) === com +end diff --git a/test/runtests.jl b/test/runtests.jl index 5363fd411..645af959e 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,45 +1,37 @@ using Test -using ForwardDiff using IntervalArithmetic -using InteractiveUtils -import Arblib -import IntervalSets as IS -include("generate_ITF1788.jl") - -# interval tests -for f ∈ filter(isfile, readdir("interval_tests"; join = true)) +# mirrors the organization of src/ and ext/ +not_repo_tests = ("runtests.jl", "aqua.jl", "generate_ITF1788.jl", "ITF1788_tests", "itl", "supposition") +repo_tests = String[] +for (root, dirs, files) ∈ walkdir(@__DIR__) + filter!(∉(not_repo_tests), dirs) + for f ∈ files + endswith(f, ".jl") && f ∉ not_repo_tests && push!(repo_tests, relpath(joinpath(root, f), @__DIR__)) + end +end +for f ∈ sort!(repo_tests) @testset "$f" begin include(f) end end -# interval tests using Supposition -# We use Pkg.add to add a specific version of Supposition using Pkg Pkg.add(url = "https://github.com/Seelengrab/Supposition.jl.git", rev = "feat/support_x86") -using Supposition, Supposition.Data - -for f ∈ filter(isfile, readdir("interval_tests/supposition"; join = true)) +for f ∈ filter(isfile, readdir("supposition"; join = true)) @testset "$f" begin include(f) end end Pkg.rm("Supposition") -# ITF1788 tests -# these tests were generated using: -# for f ∈ readdir("itl") -# if !occursin("LICENSE", f) -# generate(f) -# end -# end +# generated via `generate(f)` for each file f of itl/ (except LICENSE.md) +include("generate_ITF1788.jl") for f ∈ readdir("ITF1788_tests"; join = true) @testset "$f" begin include(f) end end -# Aqua tests include("aqua.jl") diff --git a/test/supposition/construction.jl b/test/supposition/construction.jl new file mode 100644 index 000000000..19233ed0d --- /dev/null +++ b/test/supposition/construction.jl @@ -0,0 +1,17 @@ +using Test +using IntervalArithmetic +using Supposition, Supposition.Data + +degenerate(a) = interval(a) === interval(a, a) + +@testset "Degenerate intervals" begin + floatgen = Data.Floats() + @check max_examples = 1000 degenerate(floatgen) + + intgen = Data.Integers(typemin(Int) + 1, typemax(Int)) # typemin(Int) overflows in `//` + rationalgen = @composed function _rational(num = intgen, den = intgen) + assume!(!(iszero(num) && iszero(den))) + return num // den + end + @check max_examples = 1000 degenerate(rationalgen) +end diff --git a/test/symbols.jl b/test/symbols.jl new file mode 100644 index 000000000..ab28234d9 --- /dev/null +++ b/test/symbols.jl @@ -0,0 +1,129 @@ +using Test +using IntervalArithmetic +using IntervalArithmetic.Symbols + +@testset "Exports and docstrings" begin + @test Set(names(IntervalArithmetic.Symbols)) == + Set([:Symbols, Symbol(".."), :±, :≛, :⊑, :⋤, :⪽, :⪯, :≺, :⊓, :⊔, :∅, :ℝ]) + + for s ∈ (Symbol(".."), :±, :≛, :⊑, :⋤, :⪽, :⪯, :≺, :⊓, :⊔, :∅, :ℝ) + str = string(eval(:(Base.@doc $s))) + @test !isempty(str) && !occursin("No documentation found", str) + end + + @test Symbols.:(..) isa Function + @test Symbols.:± isa Function + @test parentmodule(Symbols.:(..)) === IntervalArithmetic.Symbols + @test parentmodule(Symbols.:±) === IntervalArithmetic.Symbols + @test Symbols.:≛ === IntervalArithmetic.isequal_interval + @test parentmodule(Symbols.:⊔) === IntervalArithmetic +end + +@testset ".." begin + @test IntervalArithmetic.Symbols.:..(1, 2) === interval(1, 2; format = :infsup) + + x = 0.1..0.3 + @test x isa Interval{Float64} + @test inf(x) == 0.1 && sup(x) == 0.3 + @test decoration(x) === com && isguaranteed(x) + @test isequal_interval(x, interval(0.1, 0.3)) + + y = (1//1)..π + @test y isa Interval{Rational{Int64}} + @test inf(y) == 1//1 && sup(y) == 85563208//27235615 + @test decoration(y) === com && isguaranteed(y) + + z = 1..2 + @test z isa Interval{Float64} + @test inf(z) == 1.0 && sup(z) == 2.0 + @test decoration(z) === com && isguaranteed(z) + + w = @test_logs (:warn,) 2..1 + @test isnai(w) + + @test isguaranteed(interval(1, 2)..interval(3, 4)) + @test !isguaranteed((interval(1) + 1)..3) +end + +@testset "±" begin + x = 0 ± π + @test x isa Interval{Float64} + @test inf(x) == -3.1415926535897936 && sup(x) == 3.1415926535897936 + @test decoration(x) === com && isguaranteed(x) + + y = 0//1 ± π + @test y isa Interval{Rational{Int64}} + @test inf(y) == -85563208//27235615 && sup(y) == 85563208//27235615 + @test decoration(y) === com && isguaranteed(y) + + @test isequal_interval(1 ± 0, interval(1, 1)) + @test isthin(1 ± 0) + + @test_throws DomainError 1 ± -1 + + for (m, r) ∈ ((0.1, 0.2), (-2.5, 3.75), (1e10, 1e-10), (0.0, 0.0)) + @test isequal_interval(m ± r, interval(m, r; format = :midpoint)) + end +end + +@testset "Comparison aliases" begin + @test (≛) === isequal_interval + @test interval(1, 2) ≛ interval(1, 2) + @test !(interval(1, 2) ≛ interval(1, 3)) + + @test (⊑) === issubset_interval + @test interval(1, 2) ⊑ interval(0, 3) + + @test (⋤) === isstrictsubset + @test interval(1, 2) ⋤ interval(0, 3) + @test !(interval(1, 2) ⋤ interval(1, 2)) + + @test (⪽) === isinterior + @test interval(1, 2) ⪽ interval(0, 3) + @test !(interval(0, 2) ⪽ interval(0, 3)) + + @test (⪯) === precedes + @test interval(1, 2) ⪯ interval(2, 3) + + @test (≺) === strictprecedes + @test !(interval(1, 2) ≺ interval(2, 3)) + @test interval(1, 2) ≺ interval(3, 4) +end + +@testset "Lattice aliases" begin + @test (⊔) === hull + x = interval(1, 2) ⊔ interval(3, 4) + @test inf(x) == 1.0 && sup(x) == 4.0 + @test decoration(x) === trv && isguaranteed(x) + + @test (⊓) === intersect_interval + y = interval(1, 2) ⊓ interval(1.5, 3) + @test inf(y) == 1.5 && sup(y) == 2.0 + @test decoration(y) === trv && isguaranteed(y) +end + +@testset "∅ and ℝ" begin + @test ∅ === emptyinterval() + @test ∅ isa Interval{Float64} + @test isempty_interval(∅) + @test decoration(∅) === trv && isguaranteed(∅) + + @test isequal_interval(ℝ, entireinterval()) + @test ℝ isa Interval{Float64} + @test inf(ℝ) == -Inf && sup(ℝ) == Inf + @test decoration(ℝ) === dac && isguaranteed(ℝ) + @test isentire_interval(ℝ) + + # ∅ and ℝ are captured at load time, cf. src/symbols.jl + try + IntervalArithmetic.configure(numtype = Float32) + @test ∅ isa Interval{Float64} + @test ℝ isa Interval{Float64} + @test Base.invokelatest(emptyinterval) isa Interval{Float32} + @test Base.invokelatest(entireinterval) isa Interval{Float32} + finally + IntervalArithmetic.configure(numtype = Float64) + end + @test Base.invokelatest(emptyinterval) isa Interval{Float64} + @test IntervalArithmetic.configuration_options.numtype === Float64 +end