diff --git a/src/intervals/interval_operations/boolean.jl b/src/intervals/interval_operations/boolean.jl index a4e3392bd..d11363aac 100644 --- a/src/intervals/interval_operations/boolean.jl +++ b/src/intervals/interval_operations/boolean.jl @@ -4,8 +4,13 @@ # Some other (non required) related functions are also present, as well as some # of the "Recommended operations" (Section 10.6.3) -# used internally, equivalent to `<` but with `(Inf < Inf) == true` -_strictlessprime(x::Real, y::Real) = (x < y) | ((isinf(x) | isinf(y)) & (x == y)) +# + +_strictlessprime(x::Real, y::Real) = _strictlessprime(default_flavor(), x, y) +_strictlessprime(::Flavor{:set_based}, x::Real, y::Real) = + (x < y) | ((isinf(x) | isinf(y)) & (x == y)) # equivalent to `<` but with `(Inf < Inf) == true` + +# """ isequal_interval(x, y) @@ -28,6 +33,8 @@ isequal_interval(x::Real, y::Complex) = isequal_interval(x, real(y)) & isthinzer isequal_interval(x) = Base.Fix2(isequal_interval, x) +isequal_interval(x::Number, y::Number) = isequal_interval(interval(x), interval(y)) + """ issubset_interval(x, y) @@ -47,6 +54,8 @@ issubset_interval(x::Complex, y::Complex) = issubset_interval(real(x), real(y)) issubset_interval(x::Complex, y::Real) = issubset_interval(real(x), y) & isthinzero(imag(x)) issubset_interval(x::Real, y::Complex) = issubset_interval(x, real(y)) & in_interval(0, imag(y)) +issubset_interval(x::Number, y::Number) = issubset_interval(interval(x), interval(y)) + """ isinterior(x, y) @@ -78,6 +87,8 @@ isinterior(x::Complex, y::Complex) = isinterior(x::Complex, y::Real) = isinterior(real(x), y) & isthinzero(imag(x)) isinterior(x::Real, y::Complex) = isinterior(x, real(y)) & in_interval(0, imag(y)) +isinterior(x::Number, y::Number) = isinterior(interval(x), interval(y)) + """ isstrictsubset(x, y) @@ -100,6 +111,8 @@ isstrictsubset(x::Complex, y::Complex) = isstrictsubset(x::Complex, y::Real) = isstrictsubset(real(x), y) & isthinzero(imag(x)) isstrictsubset(x::Real, y::Complex) = isstrictsubset(x, real(y)) & in_interval(0, imag(y)) +isstrictsubset(x::Number, y::Number) = isstrictsubset(interval(x), interval(y)) + """ isweakless(x, y) @@ -115,6 +128,8 @@ function isweakless(x::Interval, y::Interval) return isweakless(bareinterval(x), bareinterval(y)) end +isweakless(x::Number, y::Number) = isweakless(interval(x), interval(y)) + """ isstrictless(x, y) @@ -123,7 +138,7 @@ Test whether `inf(x) < inf(y)` and `sup(x) < sup(y)`, where `<` is replaced by Implement the `strictLess` function of the IEEE Standard 1788-2015 (Table 10.3). """ -isstrictless(x::BareInterval, y::BareInterval) = # this may be flavor dependent? Should _strictlessprime be < for cset flavor? +isstrictless(x::BareInterval, y::BareInterval) = _strictlessprime(inf(x), inf(y)) & _strictlessprime(sup(x), sup(y)) function isstrictless(x::Interval, y::Interval) @@ -131,6 +146,8 @@ function isstrictless(x::Interval, y::Interval) return isstrictless(bareinterval(x), bareinterval(y)) end +isstrictless(x::Number, y::Number) = isstrictless(interval(x), interval(y)) + """ precedes(x, y) @@ -145,6 +162,8 @@ function precedes(x::Interval, y::Interval) return precedes(bareinterval(x), bareinterval(y)) end +precedes(x::Number, y::Number) = precedes(interval(x), interval(y)) + """ strictprecedes(x, y) @@ -159,6 +178,8 @@ function strictprecedes(x::Interval, y::Interval) return strictprecedes(bareinterval(x), bareinterval(y)) end +strictprecedes(x::Number, y::Number) = strictprecedes(interval(x), interval(y)) + """ isdisjoint_interval(x, y) @@ -177,6 +198,8 @@ end isdisjoint_interval(x::Complex, y::Complex) = isdisjoint_interval(real(x), real(y)) | isdisjoint_interval(imag(x), imag(y)) +isdisjoint_interval(x::Number, y::Number) = isdisjoint_interval(interval(x), interval(y)) + """ in_interval(x, y) @@ -207,6 +230,8 @@ in_interval(x::Number, y::Complex) = in_interval(x, real(y)) & in_interval(0, im in_interval(x) = Base.Fix2(in_interval, x) +in_interval(x::Number, y::Number) = in_interval(interval(x), interval(y)) + """ isempty_interval(x) @@ -223,6 +248,8 @@ end isempty_interval(x::Complex) = isempty_interval(real(x)) & isempty_interval(imag(x)) +isempty_interval(x::Number) = isempty_interval(interval(x)) + """ isentire_interval(x) @@ -239,6 +266,8 @@ end isentire_interval(x::Complex) = isentire_interval(real(x)) & isentire_interval(imag(x)) +isentire_interval(::Number) = false + """ isnai(x) @@ -250,6 +279,8 @@ isnai(x::Interval) = decoration(x) == ill isnai(x::Complex) = isnai(real(x)) & isnai(imag(x)) +isnai(x::Number) = isnai(interval(x)) + """ isbounded(x) @@ -264,6 +295,8 @@ end isbounded(x::Complex) = isbounded(real(x)) & isbounded(imag(x)) +isbounded(x::Number) = isbounded(interval(x)) + """ isunbounded(x) @@ -278,6 +311,8 @@ end isunbounded(x::Complex) = isunbounded(real(x)) | isunbounded(imag(x)) +isunbounded(x::Number) = isunbounded(interval(x)) + """ iscommon(x) @@ -295,6 +330,8 @@ end iscommon(x::Complex) = iscommon(real(x)) & iscommon(imag(x)) +iscommon(x::Number) = iscommon(interval(x)) + """ isatomic(x) @@ -312,6 +349,8 @@ end isatomic(x::Complex) = isatomic(real(x)) & isatomic(imag(x)) +isatomic(x::Number) = isatomic(interval(x)) + """ isthin(x) @@ -328,6 +367,8 @@ end isthin(x::Complex) = isthin(real(x)) & isthin(imag(x)) +isthin(x::Number) = isthin(interval(x)) + """ isthin(x, y) @@ -347,6 +388,8 @@ isthin(x::Number, y::Complex) = isthin(real(x), real(y)) & iszero(imag(y)) isthin(x::BareInterval, y::Interval) = throw(MethodError(isthin, (x, y))) +isthin(x::Number, y::Number) = isthin(interval(x), y) + """ isthinzero(x) @@ -361,6 +404,8 @@ end isthinzero(x::Complex) = isthinzero(real(x)) & isthinzero(imag(x)) +isthinzero(x::Number) = isthinzero(interval(x)) + """ isthinone(x) @@ -375,6 +420,8 @@ end isthinone(x::Complex) = isthinone(real(x)) & isthinzero(imag(x)) +isthinone(x::Number) = isthinone(interval(x)) + """ isthininteger(x) @@ -388,3 +435,5 @@ function isthininteger(x::Interval) end isthininteger(x::Complex) = isthininteger(real(x)) & isthinzero(imag(x)) + +isthininteger(x::Number) = isthininteger(interval(x)) diff --git a/src/intervals/real_interface.jl b/src/intervals/real_interface.jl index bb0abfe8f..2de852064 100644 --- a/src/intervals/real_interface.jl +++ b/src/intervals/real_interface.jl @@ -80,32 +80,15 @@ Base.hash(x::Interval, h::UInt) = hash(sup(x), hash(inf(x), hash(Interval, h))) for T ∈ (:BareInterval, :Interval) @eval begin - Base.:(==)(::$T, ::$T) = # also returned when calling `≤`, `≥`, `isequal` - throw(ArgumentError("`==` is purposely not supported for intervals. See instead `isequal_interval`")) + Base.isdisjoint(::$T, ::$T) = throw(ArgumentError("`isdisjoint` is purposely not supported for intervals. See instead `isdisjoint_interval`")) - Base.:<(::$T, ::$T) = # also returned when calling `isless`, `>` - throw(ArgumentError("`<` is purposely not supported for intervals. See instead `isstrictless`, `strictprecedes`")) + Base.issubset(::$T, ::$T) = throw(ArgumentError("`issubset` is purposely not supported for intervals. See instead `issubset_interval`")) - Base.isdisjoint(::$T, ::$T) = - throw(ArgumentError("`isdisjoint` is purposely not supported for intervals. See instead `isdisjoint_interval`")) + Base.issetequal(::$T, ::$T) = throw(ArgumentError("`issetequal` is purposely not supported for intervals. See instead `isequal_interval`")) - Base.issubset(::$T, ::$T) = - throw(ArgumentError("`issubset` is purposely not supported for intervals. See instead `issubset_interval`")) + Base.in(::$T, ::$T) = throw(ArgumentError("`in` is purposely not supported for intervals. See instead `in_interval`")) - Base.issetequal(::$T, ::$T) = - throw(ArgumentError("`issetequal` is purposely not supported for intervals. See instead `isequal_interval`")) - - Base.in(::$T, ::$T) = - throw(ArgumentError("`in` is purposely not supported for intervals. See instead `in_interval`")) - - Base.isempty(::$T) = - throw(ArgumentError("`isempty` is purposely not supported for intervals. See instead `isempty_interval`")) - - Base.isfinite(::$T) = # also returned when calling `isinf` - throw(ArgumentError("`isfinite` is purposely not supported for intervals. See instead `isbounded`")) - - Base.isnan(::$T) = - throw(ArgumentError("`isnan` is purposely not supported for intervals. See instead `isnai`")) + Base.isempty(::$T) = throw(ArgumentError("`isempty` is purposely not supported for intervals. See instead `isempty_interval`")) Base.intersect(::$T) = throw(ArgumentError("`intersect` is purposely not supported for intervals. See instead `intersect_interval`")) @@ -136,72 +119,144 @@ end ==(::Interval, ::Number) ==(::Number, ::Interval) -Test whether an interval is the singleton of a given number. In other words, the -result is true if and only if the interval contains only that number. This -function errors whenever the input interval is not a singleton. +Equivalent to `isthin`, but throws an error whenever the input interval is not +thin. !!! note Comparison between intervals is purposely disallowed. Indeed, equality between non-singleton intervals has distinct properties, notably ``x = y`` does not imply ``x - y = 0``. See instead [`isequal_interval`](@ref). + +See also: [`isthin`](@ref). """ -function Base.:(==)(x::BareInterval, y::Number) - isthin(x) || return throw(ArgumentError("`==` is only supported between thin intervals and numbers")) - return inf(x) == y +function Base.:(==)(x::Union{BareInterval,Interval}, y::Union{BareInterval,Interval}) # also returned when calling `≤`, `≥`, `isequal` + isthin(y) || return throw(ArgumentError("`==` is only supported for thin intervals. See instead `isequal_interval`")) + # `y` is not empty, nor an NaI + return x == sup(y) end -Base.:(==)(x::Number, y::BareInterval) = y == x -function Base.:(==)(x::Interval, y::Number) - isnai(x) && return false - return bareinterval(x) == y +function Base.:(==)(x::Union{BareInterval,Interval}, y::Number) + isthin(x) || return throw(ArgumentError("`==` is only supported between thin intervals and numbers. See instead `isequal_interval`")) + # `x` is not empty, nor an NaI + return sup(x) == y end -Base.:(==)(x::Number, y::Interval) = y == x +Base.:(==)(x::Number, y::Union{BareInterval,Interval}) = y == x +function Base.:(==)(x::Union{BareInterval,Interval}, y::Complex) + isthin(x) || return throw(ArgumentError("`==` is only supported between thin intervals and numbers. See instead `isequal_interval`")) + # `x` is not empty, nor an NaI + return sup(x) == y +end +Base.:(==)(x::Complex, y::Union{BareInterval,Interval}) = y == x + Base.:(==)(x::BareInterval, y::Interval) = throw(MethodError(==, (x, y))) Base.:(==)(x::Interval, y::BareInterval) = throw(MethodError(==, (x, y))) +Base.:(==)(x::BareInterval, y::Complex{<:Interval}) = throw(MethodError(==, (x, y))) +Base.:(==)(x::Complex{<:Interval}, y::BareInterval) = throw(MethodError(==, (x, y))) + +""" + <(::BareInterval, ::Real) + <(::Real, ::BareInterval) + <(::Interval, ::Real) + <(::Real, ::Interval) + +Equivalent to `strictprecedes`, but throws an error whenever the input interval +is not thin. + +!!! note + Comparison between intervals is purposely disallowed. Indeed, equality + between non-singleton intervals has distinct properties, notably ``x < y`` + does not imply ``x - y < 0``. See instead [`isequal_interval`](@ref). + +See also: [`strictprecedes`](@ref). +""" +function Base.:<(x::Union{BareInterval,Interval}, y::Union{BareInterval,Interval}) # also returned when calling `isless`, `>` + isthin(y) || return throw(ArgumentError("`<` is only supported for thin intervals. See instead `isstrictless`, `strictprecedes`")) + # `y` is not empty, nor an NaI + return x < sup(y) +end +function Base.:<(x::Union{BareInterval,Interval}, y::Real) + isthin(x) || return throw(ArgumentError("`<` is only supported between thin intervals and numbers. See instead `isequal_interval`")) + # `x` is not empty, nor an NaI + return sup(x) < y +end +function Base.:<(x::Real, y::Union{BareInterval,Interval}) + isthin(y) || return throw(ArgumentError("`<` is only supported between thin intervals and numbers. See instead `isequal_interval`")) + # `y` is not empty, nor an NaI + return x < sup(y) +end + +Base.:<(x::BareInterval, y::Interval) = throw(MethodError(<, (x, y))) +Base.:<(x::Interval, y::BareInterval) = throw(MethodError(<, (x, y))) + +""" + isnan(::BareInterval) + isnan(::Interval) + +Return `false`, but throws an error whenever the input interval is not thin. + +See also: [`isbounded`](@ref). +""" +function Base.isnan(x::Union{BareInterval,Interval}) + isthin(x) || return throw(ArgumentError("`isnan` is only supported for thin intervals. See instead `isnai`")) + # `x` is not empty, nor an NaI + return false +end + +""" + isfinite(::BareInterval) + isfinite(::Interval) + +Equivalent to `isbounded`, but throws an error whenever the input interval is +not thin. + +See also: [`isbounded`](@ref). +""" +function Base.isfinite(x::Union{BareInterval,Interval}) # also returned when calling `isinf` + isthin(x) || return throw(ArgumentError("`isfinite` is only supported for thin intervals. See instead `isbounded`")) + # `x` is not empty, nor an NaI + return isfinite(sup(x)) +end """ iszero(::BareInterval) iszero(::Interval) -Test whether an interval is the singleton of zero. This function errors whenever -the input interval is not a singleton. +Equivalent to `isthinzero`, but throws an error whenever the input interval is +not thin. + +See also: [`isthinzero`](@ref). """ -function Base.iszero(x::BareInterval) - isthin(x) || return throw(ArgumentError("`iszero` is only supported for thin intervals")) - return iszero(inf(x)) -end -function Base.iszero(x::Interval) - isnai(x) && return false - return iszero(bareinterval(x)) +function Base.iszero(x::Union{BareInterval,Interval}) + isthin(x) || return throw(ArgumentError("`iszero` is only supported for thin intervals. See instead `isthinzero`")) + # `x` is not empty, nor an NaI + return iszero(sup(x)) end """ isone(::BareInterval) isone(::Interval) -Test whether an interval is the singleton of one. This function errors whenever -the input interval is not a singleton. +Equivalent to `isthinone`, but throws an error whenever the input interval is +not thin. + +See also: [`isthinone`](@ref). """ -function Base.isone(x::BareInterval) - isthin(x) || return throw(ArgumentError("`isone` is only supported for thin intervals")) - return isone(inf(x)) -end -function Base.isone(x::Interval) - isnai(x) && return false - return isone(bareinterval(x)) +function Base.isone(x::Union{BareInterval,Interval}) + isthin(x) || return throw(ArgumentError("`isone` is only supported for thin intervals. See instead `isthinone`")) + # `x` is not empty, nor an NaI + return isone(sup(x)) end """ isinteger(::BareInterval) isinteger(::Interval) -Test whether an interval is the singleton of an integer. This function errors -whenever the input interval is not a singleton. +Equivalent to `isthininteger`, but throws an error whenever the input interval is +not thin. + +See also: [`isthininteger`](@ref). """ -function Base.isinteger(x::BareInterval) - isthin(x) || return throw(ArgumentError("`isinteger` is only supported for thin intervals")) - return isinteger(inf(x)) -end -function Base.isinteger(x::Interval) - isnai(x) && return false - return isinteger(bareinterval(x)) +function Base.isinteger(x::Union{BareInterval,Interval}) + isthin(x) || return throw(ArgumentError("`isinteger` is only supported for thin intervals. See instead `isthininteger`")) + # `x` is not empty, nor an NaI + return isinteger(sup(x)) end diff --git a/test/interval_tests/consistency.jl b/test/interval_tests/consistency.jl index 7da03b89b..8fa75df43 100644 --- a/test/interval_tests/consistency.jl +++ b/test/interval_tests/consistency.jl @@ -340,19 +340,32 @@ @testset "Disallowed `Real` functionalities" begin x, y = interval(1), interval(2) - @test_throws ArgumentError x == y - @test_throws ArgumentError x < y - @test_throws ArgumentError isdisjoint(x, y) - @test_throws ArgumentError issubset(x, y) - @test_throws ArgumentError issetequal(x, y) - @test_throws ArgumentError x ∈ y - @test_throws ArgumentError isempty(x) - @test_throws ArgumentError isfinite(x) - @test_throws ArgumentError isnan(x) + + @test x != y + @test x < y + @test isfinite(x) + @test !isnan(x) @test isinteger(x) @test x == 1 @test isone(x) @test !iszero(x) + + @test_throws ArgumentError !isdisjoint(x, y) + @test_throws ArgumentError !issubset(x, y) + @test_throws ArgumentError !issetequal(x, y) + @test_throws ArgumentError !(x ∈ y) + @test_throws ArgumentError !isempty(x) + + x, y = interval(2, 3), interval(1) + + @test_throws ArgumentError x != y + @test_throws ArgumentError x < y + @test_throws ArgumentError isfinite(x) + @test_throws ArgumentError !isnan(x) + @test_throws ArgumentError isinteger(x) + @test_throws ArgumentError x == 1 + @test_throws ArgumentError isone(x) + @test_throws ArgumentError !iszero(x) end end