diff --git a/src/intervals/interval_operations/boolean.jl b/src/intervals/interval_operations/boolean.jl index ac46e177b..a4e3392bd 100644 --- a/src/intervals/interval_operations/boolean.jl +++ b/src/intervals/interval_operations/boolean.jl @@ -64,7 +64,12 @@ function isinterior(x::Interval, y::Interval) return isinterior(bareinterval(x), bareinterval(y)) end -isinterior(x::AbstractVector, y::AbstractVector) = all(t -> isinterior(t[1], t[2]), zip(x, y)) +function isinterior(x::AbstractVector, y::AbstractVector) + n = length(x) + m = length(y) + n == m || return throw(DimensionMismatch("dimensions must match: x has length $n, y has length $m")) + return all(t -> isinterior(t[1], t[2]), zip(x, y)) +end isinterior(x, y, z, w...) = isinterior(x, y) & isinterior(y, z, w...) isinterior(x::Complex, y::Complex) = @@ -86,7 +91,7 @@ isstrictsubset(x::BareInterval, y::BareInterval) = isinterior(x, y) isstrictsubset(x::Interval, y::Interval) = isinterior(x, y) -isstrictsubset(x::AbstractVector, y::AbstractVector) = any(t -> isinterior(t[1], t[2]), zip(x, y)) +isstrictsubset(x::AbstractVector, y::AbstractVector) = isinterior(x, y) & any(t -> isinterior(t[1], t[2]), zip(x, y)) isstrictsubset(x, y, z, w...) = isstrictsubset(x, y) & isstrictsubset(y, z, w...) isstrictsubset(x::Complex, y::Complex) = @@ -179,12 +184,13 @@ Test whether `x` is an element of `y`. Implement the `isMember` function of the IEEE Standard 1788-2015 (Section 10.6.3). """ -function in_interval(x::Real, y::BareInterval) +function in_interval(x::Number, y::BareInterval) isinf(x) && return contains_infinity(y) return inf(y) ≤ x ≤ sup(y) end +in_interval(x::Complex, y::BareInterval) = in_interval(real(x), y) & iszero(imag(x)) -function in_interval(x::Real, y::Interval) +function in_interval(x::Number, y::Interval) isnai(y) && return false return in_interval(x, bareinterval(y)) end @@ -196,8 +202,8 @@ in_interval(::Interval, ::Interval) = throw(ArgumentError("`in_interval` is purposely not supported for two interval arguments. See instead `issubset_interval`")) in_interval(x::Complex, y::Complex) = in_interval(real(x), real(y)) & in_interval(imag(x), imag(y)) -in_interval(x::Complex, y::Real) = in_interval(real(x), y) & isthinzero(imag(x)) -in_interval(x::Real, y::Complex) = in_interval(x, real(y)) & in_interval(0, imag(y)) +in_interval(x::Complex, y::Number) = in_interval(real(x), y) & iszero(imag(x)) +in_interval(x::Number, y::Complex) = in_interval(x, real(y)) & in_interval(0, imag(y)) in_interval(x) = Base.Fix2(in_interval, x) @@ -328,13 +334,18 @@ isthin(x::Complex) = isthin(real(x)) & isthin(imag(x)) Test whether `x` contains only `y`. """ isthin(x::BareInterval, y::Number) = inf(x) == sup(x) == y +isthin(x::BareInterval, y::Complex) = isthin(x, real(y)) & iszero(imag(y)) function isthin(x::Interval, y::Number) isnai(x) && return false return isthin(bareinterval(x), y) end -isthin(x::Complex, y::Number) = isthin(real(x), y) & isthin(imag(x), y) +isthin(x::Complex, y::Complex) = isthin(real(x), real(y)) & isthin(imag(x), imag(y)) +isthin(x::Complex, y::Number) = isthin(real(x), real(y)) & isthinzero(imag(x)) +isthin(x::Number, y::Complex) = isthin(real(x), real(y)) & iszero(imag(y)) + +isthin(x::BareInterval, y::Interval) = throw(MethodError(isthin, (x, y))) """ isthinzero(x) @@ -376,4 +387,4 @@ function isthininteger(x::Interval) return isthininteger(bareinterval(x)) end -isthininteger(x::Complex) = isthininteger(real(x)) & isthininteger(imag(x)) +isthininteger(x::Complex) = isthininteger(real(x)) & isthinzero(imag(x)) diff --git a/src/intervals/real_interface.jl b/src/intervals/real_interface.jl index 676ce3c98..64152fcb5 100644 --- a/src/intervals/real_interface.jl +++ b/src/intervals/real_interface.jl @@ -107,9 +107,6 @@ for T ∈ (:BareInterval, :Interval) Base.isnan(::$T) = throw(ArgumentError("`isnan` is purposely not supported for intervals. See instead `isnai`")) - Base.isinteger(::$T) = - throw(ArgumentError("`isinteger` is purposely not supported for intervals. See instead `isthininteger`")) - Base.intersect(::$T) = throw(ArgumentError("`intersect` is purposely not supported for intervals. See instead `intersect_interval`")) @@ -130,3 +127,47 @@ for T ∈ (:BareInterval, :Interval) throw(ArgumentError("`setdiff!` is purposely not supported for intervals. See instead `interiordiff`")) end end + +# allow pointwise equality + +""" + ==(::BareInterval, ::Number) + ==(::Number, ::BareInterval) + ==(::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. + +!!! 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). +""" +Base.:(==)(x::BareInterval, y::Number) = inf(x) == sup(x) == y +Base.:(==)(x::Number, y::BareInterval) = y == x +function Base.:(==)(x::Interval, y::Number) + isnai(x) && return false + return bareinterval(x) == y +end +Base.:(==)(x::Number, y::Interval) = y == x +Base.:(==)(x::BareInterval, y::Interval) = throw(MethodError(==, (x, y))) +Base.:(==)(x::Interval, y::BareInterval) = throw(MethodError(==, (x, y))) + +Base.iszero(x::BareInterval) = iszero(inf(x)) & iszero(sup(x)) +function Base.iszero(x::Interval) + isnai(x) && return false + return iszero(bareinterval(x)) +end + +Base.isone(x::BareInterval) = isone(inf(x)) & isone(sup(x)) +function Base.isone(x::Interval) + isnai(x) && return false + return isone(bareinterval(x)) +end + +Base.isinteger(x::BareInterval) = (inf(x) == sup(x)) & isinteger(inf(x)) +function Base.isinteger(x::Interval) + isnai(x) && return false + return isinteger(bareinterval(x)) +end diff --git a/test/interval_tests/complex.jl b/test/interval_tests/complex.jl index 3b2ff5ac7..87ddc206f 100644 --- a/test/interval_tests/complex.jl +++ b/test/interval_tests/complex.jl @@ -13,7 +13,7 @@ @test isequal_interval(a * a, interval(-1)) @test isequal_interval(a + a, interval(2)*interval(im)) @test isthin(a - a, 0) - @test_broken isthin(a / a, 1) + @test isthin(a / a, 1) @test in_interval(3+2im, c) @test isequal_interval(hull(a, b), complex(interval(0, 3), interval(1, 4))) diff --git a/test/interval_tests/consistency.jl b/test/interval_tests/consistency.jl index 308817287..c07ba722f 100644 --- a/test/interval_tests/consistency.jl +++ b/test/interval_tests/consistency.jl @@ -339,7 +339,10 @@ @test_throws ArgumentError isempty(x) @test_throws ArgumentError isfinite(x) @test_throws ArgumentError isnan(x) - @test_throws ArgumentError isinteger(x) + @test isinteger(x) + @test x == 1 + @test isone(x) + @test !iszero(x) end end diff --git a/test/interval_tests/forwarddiff.jl b/test/interval_tests/forwarddiff.jl index 21ec86277..7d48f3d0b 100644 --- a/test/interval_tests/forwarddiff.jl +++ b/test/interval_tests/forwarddiff.jl @@ -46,7 +46,7 @@ end 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) === dψ(0) && !isguaranteed(ψ′(0)) @test_broken ψ′′(0) === ddψ(0) && !isguaranteed(ψ′′(0)) # rely on `Interval{T}(::Real)` being defined @test_broken ψ′′′(0) === dddψ(0) && !isguaranteed(ψ′′′(0)) # rely on `Interval{T}(::Real)` being defined t₀ = interval(0) @@ -62,6 +62,6 @@ end # g(x) = 2^x # not guaranteed - @test_broken f′(0) === df(0) + @test f′(0) === df(0) end end