From 3d97a1469288d2a81f7ae3bbc3dd7393355e7761 Mon Sep 17 00:00:00 2001 From: OlivierHnt <38465572+OlivierHnt@users.noreply.github.com> Date: Wed, 21 Aug 2024 17:07:04 +0200 Subject: [PATCH 1/4] Improve display --- src/display.jl | 226 ++++++++++++++++++--------------- test/interval_tests/display.jl | 113 +++++++++-------- 2 files changed, 185 insertions(+), 154 deletions(-) diff --git a/src/display.jl b/src/display.jl index 119e128c2..5ad572b3e 100644 --- a/src/display.jl +++ b/src/display.jl @@ -111,181 +111,201 @@ Base.show(io::IO, ::MIME"text/plain", a::Union{BareInterval,Interval,Complex{<:I function _str_repr(a::BareInterval{T}, format::Symbol) where {T<:NumTypes} # `format` is either `:infsup`, `:midpoint` or `:full` str_interval = _str_basic_repr(a, format) - format === :full && return string("BareInterval{", T, "}(", str_interval, ')') - return str_interval -end - -function _str_repr(a::BareInterval{BigFloat}, format::Symbol) - # `format` is either `:infsup`, `:midpoint` or `:full` - str_interval = _str_basic_repr(a, format) - format === :full && return string("BareInterval{BigFloat}(", str_interval, ')') - if format === :midpoint && str_interval != "∅" - str_interval = string('(', str_interval, ')') - end - return string(str_interval, _str_precision(a)) + ((format === :full) & (str_interval != "∅")) && return string("BareInterval{", T, "}(", str_interval, ')') + return _str_precision(str_interval, a, format) end function _str_repr(a::Interval{T}, format::Symbol) where {T<:NumTypes} # `format` is either `:infsup`, `:midpoint` or `:full` str_interval = _str_basic_repr(a.bareinterval, format) # use `a.bareinterval` to not print a warning if `a` is an NaI - if isguaranteed(a) || !display_options.ng_flag - format === :full && return string("Interval{", T, "}(", str_interval, ", ", decoration(a), ')') - display_options.decorations || return str_interval - if format === :midpoint && str_interval != "∅" - str_interval = string('(', str_interval, ')') - end - return string(str_interval, '_', decoration(a)) + if format === :full && str_interval != "∅" + str_interval = string("Interval{", T, "}(", str_interval, ", ", decoration(a), ')') else - format === :full && return string("Interval{", T, "}(", str_interval, ", ", decoration(a), ", NG)") - if format === :midpoint && str_interval != "∅" + str_interval = _str_precision(str_interval, a, format) + if format === :midpoint && str_interval != "∅" && T !== BigFloat && (display_options.decorations | (display_options.ng_flag & !isguaranteed(a))) str_interval = string('(', str_interval, ')') end - display_options.decorations || return string(str_interval, "_NG") - return string(str_interval, '_', decoration(a), "_NG") + str_interval = ifelse(display_options.decorations, string(str_interval, '_', decoration(a)), str_interval) end + return ifelse(display_options.ng_flag & !isguaranteed(a), string(str_interval, "_NG"), str_interval) end -function _str_repr(a::Interval{BigFloat}, format::Symbol) +function _str_repr(x::Complex{Interval{T}}, format::Symbol) where {T<:NumTypes} # `format` is either `:infsup`, `:midpoint` or `:full` - str_interval = _str_basic_repr(a.bareinterval, format) # use `a.bareinterval` to not print a warning if `a` is an NaI - if isguaranteed(a) || !display_options.ng_flag - format === :full && return string("Interval{BigFloat}(", str_interval, ", ", decoration(a), ')') - if format === :midpoint && str_interval != "∅" - str_interval = string('(', str_interval, ')') + str_imag = _str_repr(imag(x), format) + if format === :full + if occursin("}(-", str_imag) && occursin(", -", str_imag) + c1, c2 = split(str_imag, ", "; limit = 2) + l = findfirst('-', c1) + ll = ifelse(occursin(',', c2), findfirst(',', c2), findfirst(')', c2)) + return string(_str_repr(real(x), format), " - im*", view(c1, 1:l-1), view(c2, 2:ll-1), ", ", view(c1, l+1:lastindex(c1)), view(c2, ll:lastindex(c2))) + else + return string(_str_repr(real(x), format), " + im*", str_imag) end - display_options.decorations || return string(str_interval, _str_precision(a)) - return string(str_interval, _str_precision(a), '_', decoration(a)) - else - format === :full && return string("Interval{", BigFloat, "}(", str_interval, ", ", decoration(a), ", NG)") - if format === :midpoint && str_interval != "∅" - str_interval = string('(', str_interval, ')') + elseif format === :midpoint + str_real = _str_repr(real(x), format) + if !startswith(str_real, '(') + str_real = string('(', str_real, ')') end - display_options.decorations || return string(str_interval, _str_precision(a), "_NG") - return string(str_interval, _str_precision(a), '_', decoration(a), "_NG") - end -end - -function _str_repr(x::Complex{<:Interval}, format::Symbol) - # `format` is either `:infsup`, `:midpoint` or `:full` - if format === :full - return string(_str_repr(real(x), format), " + ", _str_repr(imag(x), format), "im") - elseif format === :infsup - ((!isguaranteed(x) & display_options.ng_flag) | display_options.decorations) && return string(_str_repr(real(x), format), " + (", _str_repr(imag(x), format), ")im") - return string(_str_repr(real(x), format), " + ", _str_repr(imag(x), format), "im") + if !startswith(str_imag, '(') + str_imag = string('(', str_imag, ')') + end + startswith(str_imag, "(-") && return string(str_real, " - im*(", view(str_imag, 3:lastindex(str_imag))) + return string(str_real, " + im*", str_imag) else - ((!isguaranteed(x) & display_options.ng_flag) | display_options.decorations) && return string(_str_repr(real(x), format), " + (", _str_repr(imag(x), format), ")im") - return string('(', _str_repr(real(x), format), ") + (", _str_repr(imag(x), format), ")im") + if str_imag[2] == '-' && occursin(", -", str_imag) + c1, c2 = split(str_imag, ", ") + l = findfirst(t -> (t == ']') | (t == ')'), c2) + return string(_str_repr(real(x), format), " - im*", _flipl(c2[l]), view(c2, 2:l-1), ", ", view(c1, 3:lastindex(c1)), _flipr(c1[1]), view(c2, l+1:lastindex(c2))) + else + return string(_str_repr(real(x), format), " + im*", str_imag) + end end end -function _str_repr(x::Complex{Interval{BigFloat}}, format::Symbol) - # `format` is either `:infsup`, `:midpoint` or `:full` - ((!isguaranteed(x) & display_options.ng_flag) | display_options.decorations) && return string(_str_repr(real(x), format), " + (", _str_repr(imag(x), format), ")im") - return string(_str_repr(real(x), format), " + ", _str_repr(imag(x), format), "im") -end +_flipl(char) = ifelse(char == ']', '[', '(') +_flipr(char) = ifelse(char == '[', ']', ')') # -function _str_precision(x) +_str_precision(str_interval, x, format) = str_interval + +function _str_precision(str_interval, x::Union{BareInterval{BigFloat},Interval{BigFloat}}, format) plo = precision(inf(x)) phi = precision(sup(x)) pstr = _subscriptify(plo) - plo == phi && return pstr - return string(pstr, "_", _subscriptify(phi)) + if format === :midpoint && str_interval != "∅" + str_interval = string('(', str_interval, ')') + end + str_interval = string(str_interval, pstr) + return ifelse(plo == phi, str_interval, string(str_interval, '_', _subscriptify(phi))) end # function _str_basic_repr(a::BareInterval{<:AbstractFloat}, format::Symbol) # `format` is either `:infsup`, `:midpoint` or `:full` - # do not use `inf(a)` to avoid `-0.0` isempty_interval(a) && return "∅" + lo, hi = bounds(a) # do not use `inf(a)` to avoid `-0.0` sigdigits = display_options.sigdigits if format === :full - return string(a.lo, ", ", sup(a)) + str_lo = string(lo) + # str_lo = ifelse(lo ≥ 0, string('+', str_lo), str_lo) + str_hi = string(hi) + # str_hi = ifelse(hi ≥ 0, string('+', str_hi), str_hi) + return string(str_lo, ", ", str_hi) elseif format === :midpoint - m = _round_string(mid(a), sigdigits, RoundNearest) - r = _round_string(radius(a), sigdigits, RoundUp) - output = string(m, " ± ", r) + m = mid(a) + str_m = _round_string(m, sigdigits, RoundNearest) + # str_m = ifelse(m ≥ 0, string('+', str_m), str_m) + output = string(str_m, " ± ", _round_string(radius(a), sigdigits, RoundUp)) return replace(output, "Inf" => '∞') else - lo = _round_string(a.lo, sigdigits, RoundDown) - hi = _round_string(sup(a), sigdigits, RoundUp) - output = string('[', lo, ", ", hi, ']') + str_lo = _round_string(lo, sigdigits, RoundDown) + # str_lo = ifelse(lo ≥ 0, string('+', str_lo), str_lo) + str_hi = _round_string(hi, sigdigits, RoundUp) + # str_hi = ifelse(hi ≥ 0, string('+', str_hi), str_hi) + output = string('[', str_lo, ", ", str_hi, ']') return replace(output, "Inf]" => "∞)", "[-Inf" => "(-∞") end end function _str_basic_repr(a::BareInterval{Float32}, format::Symbol) # `format` is either `:infsup`, `:midpoint` or `:full` - # do not use `inf(a)` to avoid `-0.0` isempty_interval(a) && return "∅" + lo, hi = bounds(a) # do not use `inf(a)` to avoid `-0.0` sigdigits = display_options.sigdigits if format === :full - lo = replace(string(a.lo, "f0"), "NaNf0" => "NaN32", "Inff0" => "Inf32") - if contains(lo, 'e') - lo = replace(lo, 'e' => 'f', "f0" => "") + str_lo = string(lo) + str_lo = replace(string(str_lo, "f0"), "NaNf0" => "NaN32", "Inff0" => "Inf32") + if contains(str_lo, 'e') + str_lo = replace(str_lo, 'e' => 'f', "f0" => "") end - hi = replace(string(sup(a), "f0"), "NaNf0" => "NaN32", "Inff0" => "Inf32") - if contains(hi, 'e') - hi = replace(hi, 'e' => 'f', "f0" => "") + # str_lo = ifelse(lo ≥ 0, string('+', str_lo), str_lo) + str_hi = string(hi) + str_hi = replace(string(str_hi, "f0"), "NaNf0" => "NaN32", "Inff0" => "Inf32") + if contains(str_hi, 'e') + str_hi = replace(str_hi, 'e' => 'f', "f0" => "") end - return string(lo, ", ", hi) + # str_hi = ifelse(hi ≥ 0, string('+', str_hi), str_hi) + return string(str_lo, ", ", str_hi) elseif format === :midpoint - m = _round_string(mid(a), sigdigits, RoundNearest) - m = replace(string(m, "f0"), "NaNf0" => "NaN32", "Inff0" => "Inf32") - if contains(m, 'e') - m = replace(m, 'e' => 'f', "f0" => "") + m = mid(a) + str_m = _round_string(m, sigdigits, RoundNearest) + str_m = replace(string(str_m, "f0"), "NaNf0" => "NaN32", "Inff0" => "Inf32") + if contains(str_m, 'e') + str_m = replace(str_m, 'e' => 'f', "f0" => "") end - r = _round_string(radius(a), sigdigits, RoundUp) - r = replace(string(r, "f0"), "NaNf0" => "NaN32", "Inff0" => "Inf32") - if contains(r, 'e') - r = replace(r, 'e' => 'f', "f0" => "") + # str_m = ifelse(m ≥ 0, string('+', str_m), str_m) + str_r = _round_string(radius(a), sigdigits, RoundUp) + str_r = replace(string(str_r, "f0"), "NaNf0" => "NaN32", "Inff0" => "Inf32") + if contains(str_r, 'e') + str_r = replace(str_r, 'e' => 'f', "f0" => "") end - return string(m, " ± ", r) + return string(str_m, " ± ", str_r) else - lo = _round_string(a.lo, sigdigits, RoundDown) - lo = replace(string('[', lo, "f0"), "NaNf0" => "NaN32", "[-Inff0" => "(-∞") - if contains(lo, 'e') - lo = replace(lo, 'e' => 'f', "f0" => "") + str_lo = _round_string(lo, sigdigits, RoundDown) + str_lo = replace(string('[', str_lo, "f0"), "NaNf0" => "NaN32", "[-Inff0" => "(-∞") + if contains(str_lo, 'e') + str_lo = replace(str_lo, 'e' => 'f', "f0" => "") end - hi = _round_string(sup(a), sigdigits, RoundUp) - hi = replace(string(hi, "f0]"), "NaNf0" => "NaN32", "Inff0]" => "∞)") - if contains(hi, 'e') - hi = replace(hi, 'e' => 'f', "f0" => "") + # str_lo = ifelse(lo ≥ 0, string('+', str_lo), str_lo) + str_hi = _round_string(hi, sigdigits, RoundUp) + str_hi = replace(string(str_hi, "f0]"), "NaNf0" => "NaN32", "Inff0]" => "∞)") + if contains(str_hi, 'e') + str_hi = replace(str_hi, 'e' => 'f', "f0" => "") end - return string(lo, ", ", hi) + # str_hi = ifelse(hi ≥ 0, string('+', str_hi), str_hi) + return string(str_lo, ", ", str_hi) end end function _str_basic_repr(a::BareInterval{Float16}, format::Symbol) # `format` is either `:infsup`, `:midpoint` or `:full` - # do not use `inf(a)` to avoid `-0.0` isempty_interval(a) && return "∅" + lo, hi = bounds(a) # do not use `inf(a)` to avoid `-0.0` sigdigits = display_options.sigdigits if format === :full - output = string("Float16(", a.lo, "), Float16(", sup(a), ')') + str_lo = string(lo) + # str_lo = ifelse(lo ≥ 0, string('+', str_lo), str_lo) + str_hi = string(hi) + # str_hi = ifelse(hi ≥ 0, string('+', str_hi), str_hi) + output = string("Float16(", str_lo, "), Float16(", str_hi, ')') return replace(output, "Float16(NaN)" => "NaN16", "Float16(-Inf)" => "-Inf16", "Float16(Inf)" => "Inf16") elseif format === :midpoint - m = _round_string(mid(a), sigdigits, RoundNearest) - r = _round_string(radius(a), sigdigits, RoundUp) - output = string("Float16(", m, ") ± Float16(", r, ')') + m = mid(a) + str_m = _round_string(mid(a), sigdigits, RoundNearest) + # str_m = ifelse(m ≥ 0, string('+', str_m), str_m) + output = string("Float16(", str_m, ") ± Float16(", _round_string(radius(a), sigdigits, RoundUp), ')') return replace(output, "Float16(NaN)" => "NaN16", "Float16(Inf)" => '∞') else - lo = _round_string(a.lo, sigdigits, RoundDown) - hi = _round_string(sup(a), sigdigits, RoundUp) - output = string("[Float16(", lo, "), Float16(", hi, ")]") + str_lo = _round_string(lo, sigdigits, RoundDown) + # str_lo = ifelse(lo ≥ 0, string('+', str_lo), str_lo) + str_hi = _round_string(sup(a), sigdigits, RoundUp) + # str_hi = ifelse(hi ≥ 0, string('+', str_hi), str_hi) + output = string("[Float16(", str_lo, "), Float16(", str_hi, ")]") return replace(output, "Float16(NaN)" => "NaN16", "[Float16(-Inf)" => "(-∞", "Float16(Inf)]" => "∞)") end end function _str_basic_repr(a::BareInterval{<:Rational}, format::Symbol) # `format` is either `:infsup`, `:midpoint` or `:full` - # do not use `inf(a)` to avoid `-0.0` isempty_interval(a) && return "∅" - format === :full && return string(a.lo, ", ", sup(a)) - format === :midpoint && return string(mid(a), " ± ", radius(a)) - return string('[', a.lo, ", ", sup(a), ']') + if format === :midpoint + m = mid(a) + str_m = string(m) + # str_m = ifelse(m ≥ 0, string('+', str_m), str_m) + return string(str_m, " ± ", radius(a)) + else + lo, hi = bounds(a) # do not use `inf(a)` to avoid `-0.0` + str_lo = string(lo) + # str_lo = ifelse(lo ≥ 0, string('+', str_lo), str_lo) + str_hi = string(hi) + # str_hi = ifelse(hi ≥ 0, string('+', str_hi), str_hi) + output = string(str_lo, ", ", str_hi) + output = ifelse(format === :full, output, string('[', output, ']')) + return output + end end # round to the prescribed significant digits diff --git a/test/interval_tests/display.jl b/test/interval_tests/display.jl index 0bdf876aa..e1dd97bc2 100644 --- a/test/interval_tests/display.jl +++ b/test/interval_tests/display.jl @@ -32,7 +32,7 @@ setprecision(BigFloat, 256) do # `sigdigits` is not taken into account for format `:full` setdisplay(:full; sigdigits = 100, decorations = true) - @test sprint(show, MIME("text/plain"), emptyinterval(BareInterval{Float64})) == "BareInterval{Float64}(∅)" + @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) == @@ -73,13 +73,13 @@ setprecision(BigFloat, 256) do @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.30001]_com" - @test sprint(show, MIME("text/plain"), b32) == "[-1.1755f-38, 1.30001f0]_com" - @test sprint(show, MIME("text/plain"), b16) == "[Float16(-6.104e-5), Float16(1.29981)]_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"), 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.30001]_com" + @test sprint(show, MIME("text/plain"), b32) == "[-1.1755f-38, 1.30001f0]_com" + @test sprint(show, MIME("text/plain"), b16) == "[Float16(-6.104e-5), Float16(1.29981)]_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"), large_expo) == "[0.0, 1.00001e+123456789]₂₅₆_com" end @@ -90,13 +90,13 @@ setprecision(BigFloat, 256) do @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.30001]" - @test sprint(show, MIME("text/plain"), b32) == "[-1.1755f-38, 1.30001f0]" - @test sprint(show, MIME("text/plain"), b16) == "[Float16(-6.104e-5), Float16(1.29981)]" - @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"), 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.30001]" + @test sprint(show, MIME("text/plain"), b32) == "[-1.1755f-38, 1.30001f0]" + @test sprint(show, MIME("text/plain"), b16) == "[Float16(-6.104e-5), Float16(1.29981)]" + @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"), large_expo) == "[0.0, 1.00001e+123456789]₂₅₆" end @@ -105,13 +105,13 @@ setprecision(BigFloat, 256) do @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.3000000000000000445]_com" - @test sprint(show, MIME("text/plain"), b32) == "[-1.1754944f-38, 1.2999999523162841797f0]_com" - @test sprint(show, MIME("text/plain"), b16) == "[Float16(-6.104e-5), Float16(1.2998046875000000001)]_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"), 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.3000000000000000445]_com" + @test sprint(show, MIME("text/plain"), b32) == "[-1.1754944f-38, 1.2999999523162841797f0]_com" + @test sprint(show, MIME("text/plain"), b16) == "[Float16(-6.104e-5), Float16(1.2998046875000000001)]_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"), large_expo) == "[0.0, 1.0000000000000000001e+123456789]₂₅₆_com" end @@ -121,16 +121,16 @@ setprecision(BigFloat, 256) do # `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()) == "Interval{Float64}(∅, trv)" - @test sprint(show, MIME("text/plain"), emptyinterval()/1) == "Interval{Float64}(∅, trv, NG)" + @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)" - @test sprint(show, MIME("text/plain"), a_NG) == "Interval{Float64}(1.0, 2.0, com, NG)" - @test sprint(show, MIME("text/plain"), b) == "Interval{Float64}(-2.2250738585072014e-308, 1.3, com)" - @test sprint(show, MIME("text/plain"), b32) == "Interval{Float32}(-1.1754944f-38, 1.3f0, com)" - @test sprint(show, MIME("text/plain"), b16) == "Interval{Float16}(Float16(-6.104e-5), Float16(1.3), com)" - @test sprint(show, MIME("text/plain"), br) == "Interval{Rational{Int64}}(-11//10, 13//10, com)" - @test sprint(show, MIME("text/plain"), c) == "Interval{Float64}(-1.0, Inf, dac)" + @test sprint(show, MIME("text/plain"), a) == "Interval{Float64}(1.0, 2.0, com)" + @test sprint(show, MIME("text/plain"), a_NG) == "Interval{Float64}(1.0, 2.0, com)_NG" + @test sprint(show, MIME("text/plain"), b) == "Interval{Float64}(-2.2250738585072014e-308, 1.3, com)" + @test sprint(show, MIME("text/plain"), b32) == "Interval{Float32}(-1.1754944f-38, 1.3f0, com)" + @test sprint(show, MIME("text/plain"), b16) == "Interval{Float16}(Float16(-6.104e-5), Float16(1.3), com)" + @test sprint(show, MIME("text/plain"), br) == "Interval{Rational{Int64}}(-11//10, 13//10, com)" + @test sprint(show, MIME("text/plain"), c) == "Interval{Float64}(-1.0, Inf, dac)" @test sprint(show, MIME("text/plain"), large_expo) == "Interval{BigFloat}(0.0, 1.000000000000000000000000000000000000000000000000000000000000000000000000000004e+123456789, com)" end @@ -144,13 +144,13 @@ setprecision(BigFloat, 256) do @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.650001)_com" - @test sprint(show, MIME("text/plain"), b32) == "(0.65f0 ± 0.650001f0)_com" - @test sprint(show, MIME("text/plain"), b16) == "(Float16(0.649902) ± Float16(0.649903))_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"), 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.650001)_com" + @test sprint(show, MIME("text/plain"), b32) == "(0.65f0 ± 0.650001f0)_com" + @test sprint(show, MIME("text/plain"), b16) == "(Float16(0.649902) ± Float16(0.649903))_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"), large_expo) == "(5.0e+123456788 ± 5.00001e+123456788)₂₅₆_com" end @@ -161,13 +161,13 @@ setprecision(BigFloat, 256) do @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.650001" - @test sprint(show, MIME("text/plain"), b32) == "0.65f0 ± 0.650001f0" - @test sprint(show, MIME("text/plain"), b16) == "Float16(0.649902) ± Float16(0.649903)" - @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"), 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.650001" + @test sprint(show, MIME("text/plain"), b32) == "0.65f0 ± 0.650001f0" + @test sprint(show, MIME("text/plain"), b16) == "Float16(0.649902) ± Float16(0.649903)" + @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"), large_expo) == "(5.0e+123456788 ± 5.00001e+123456788)₂₅₆" end @@ -176,7 +176,8 @@ setprecision(BigFloat, 256) do @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) @@ -187,13 +188,17 @@ setprecision(BigFloat, 256) do @testset "Decorations" begin setdisplay(; decorations = true) - @test sprint(show, MIME("text/plain"), a) == "[0.0, 2.0]_com + ([1.0, 1.0]_com)im" + @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.00001e-70]_com - im*[0.999999e-70, 1.00001e-70]_com" end @testset "No decorations" begin setdisplay(; decorations = false) - @test sprint(show, MIME("text/plain"), a) == "[0.0, 2.0] + [1.0, 1.0]im" + @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.00001e-70] - im*[0.999999e-70, 1.00001e-70]" end end end @@ -202,7 +207,9 @@ setprecision(BigFloat, 256) do # `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) + Interval{Float64}(1.0, 1.0, com)im" + @test sprint(show, MIME("text/plain"), a) == "Interval{Float64}(0.0, 2.0, com) + im*Interval{Float64}(1.0, 1.0, com)" + @test sprint(show, MIME("text/plain"), b) == "Interval{Float64}(0.0, 2.0, com) - im*Interval{Float64}(1.0, 1.0, com)" + # @test sprint(show, MIME("text/plain"), c) == "Interval{Float64}(0.0, 2.0, com) + im*Interval{Float64}(1.0, 1.0, com)" end @testset "Midpoint format" begin @@ -211,13 +218,17 @@ setprecision(BigFloat, 256) do @testset "Decorations" begin setdisplay(; decorations = true) - @test sprint(show, MIME("text/plain"), a) == "(1.0 ± 1.0)_com + ((1.0 ± 0.0)_com)im" + @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.00001e-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) + (1.0 ± 0.0)im" + @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.00001e-71) - im*(1.0e-70 ± 0.0)" end end end From a92738d596a96f04360a36056e4412760045aec3 Mon Sep 17 00:00:00 2001 From: OlivierHnt <38465572+OlivierHnt@users.noreply.github.com> Date: Wed, 21 Aug 2024 17:19:29 +0200 Subject: [PATCH 2/4] Fix docstring --- src/intervals/exact_literals.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/intervals/exact_literals.jl b/src/intervals/exact_literals.jl index cdb2fef9c..08c5dea01 100644 --- a/src/intervals/exact_literals.jl +++ b/src/intervals/exact_literals.jl @@ -29,14 +29,14 @@ producing the "NG" flag. julia> setdisplay(:full); julia> 0.5 * interval(1) -Interval{Float64}(0.5, 0.5, com, NG) +Interval{Float64}(0.5, 0.5, com)_NG julia> ExactReal(0.5) * interval(1) Interval{Float64}(0.5, 0.5, com) julia> [1, interval(2)] 2-element Vector{Interval{Float64}}: - Interval{Float64}(1.0, 1.0, com, NG) + Interval{Float64}(1.0, 1.0, com)_NG Interval{Float64}(2.0, 2.0, com) julia> [ExactReal(1), interval(2)] @@ -199,7 +199,7 @@ julia> f(x) = 1.2*x + 0.1 f (generic function with 1 method) julia> f(interval(1, 2)) -Interval{Float64}(1.2999999999999998, 2.5, com, NG) +Interval{Float64}(1.2999999999999998, 2.5, com)_NG julia> @exact g(x) = 1.2*x + 0.1 g (generic function with 1 method) From 1bc70c6183fd15664815c4ea9356d2b0f758822d Mon Sep 17 00:00:00 2001 From: OlivierHnt <38465572+OlivierHnt@users.noreply.github.com> Date: Wed, 21 Aug 2024 17:33:01 +0200 Subject: [PATCH 3/4] Fix issue if the supremum is zero --- src/display.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/display.jl b/src/display.jl index 5ad572b3e..b036b4441 100644 --- a/src/display.jl +++ b/src/display.jl @@ -134,11 +134,11 @@ function _str_repr(x::Complex{Interval{T}}, format::Symbol) where {T<:NumTypes} # `format` is either `:infsup`, `:midpoint` or `:full` str_imag = _str_repr(imag(x), format) if format === :full - if occursin("}(-", str_imag) && occursin(", -", str_imag) + if !isthinzero(imag(x)) && sup(imag(x)) ≤ 0 && !isempty_interval(imag(x)) c1, c2 = split(str_imag, ", "; limit = 2) l = findfirst('-', c1) ll = ifelse(occursin(',', c2), findfirst(',', c2), findfirst(')', c2)) - return string(_str_repr(real(x), format), " - im*", view(c1, 1:l-1), view(c2, 2:ll-1), ", ", view(c1, l+1:lastindex(c1)), view(c2, ll:lastindex(c2))) + return string(_str_repr(real(x), format), " - im*", view(c1, 1:l-1), view(c2, 1+!iszero(sup(imag(x))):ll-1), ", ", view(c1, l+1:lastindex(c1)), view(c2, ll:lastindex(c2))) else return string(_str_repr(real(x), format), " + im*", str_imag) end @@ -153,10 +153,10 @@ function _str_repr(x::Complex{Interval{T}}, format::Symbol) where {T<:NumTypes} startswith(str_imag, "(-") && return string(str_real, " - im*(", view(str_imag, 3:lastindex(str_imag))) return string(str_real, " + im*", str_imag) else - if str_imag[2] == '-' && occursin(", -", str_imag) + if !isthinzero(imag(x)) && sup(imag(x)) ≤ 0 && !isempty_interval(imag(x)) c1, c2 = split(str_imag, ", ") l = findfirst(t -> (t == ']') | (t == ')'), c2) - return string(_str_repr(real(x), format), " - im*", _flipl(c2[l]), view(c2, 2:l-1), ", ", view(c1, 3:lastindex(c1)), _flipr(c1[1]), view(c2, l+1:lastindex(c2))) + return string(_str_repr(real(x), format), " - im*", _flipl(c2[l]), view(c2, 1+!iszero(sup(imag(x))):l-1), ", ", view(c1, 3:lastindex(c1)), _flipr(c1[1]), view(c2, l+1:lastindex(c2))) else return string(_str_repr(real(x), format), " + im*", str_imag) end From ffad210b9d5cfbd42b75af739f80c8c1d9f392a7 Mon Sep 17 00:00:00 2001 From: Alexander Plavin Date: Tue, 3 Sep 2024 06:38:59 +0200 Subject: [PATCH 4/4] add IntervalSets conversions (#677) * add IntervalSets conversions * upd Project * fixes * upd * more careful conversion --- Project.toml | 3 +++ ext/IntervalArithmeticsIntervalSetsExt.jl | 23 +++++++++++++++++++++++ test/Project.toml | 1 + test/interval_tests/construction.jl | 21 +++++++++++++++++++++ test/runtests.jl | 1 + 5 files changed, 49 insertions(+) create mode 100644 ext/IntervalArithmeticsIntervalSetsExt.jl diff --git a/Project.toml b/Project.toml index 057ee785a..570a25c05 100644 --- a/Project.toml +++ b/Project.toml @@ -11,17 +11,20 @@ RoundingEmulator = "5eaf0fd0-dfba-4ccb-bf02-d820a40db705" [weakdeps] DiffRules = "b552c78f-8df3-52c6-915a-8e097449b14b" ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" +IntervalSets = "8197267c-284f-5f27-9208-e0e47529a953" RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" [extensions] IntervalArithmeticDiffRulesExt = "DiffRules" IntervalArithmeticForwardDiffExt = "ForwardDiff" +IntervalArithmeticsIntervalSetsExt = "IntervalSets" IntervalArithmeticRecipesBaseExt = "RecipesBase" [compat] CRlibm_jll = "1" DiffRules = "1" ForwardDiff = "0.10" +IntervalSets = "0.7" MacroTools = "0.5" RecipesBase = "1" RoundingEmulator = "0.2" diff --git a/ext/IntervalArithmeticsIntervalSetsExt.jl b/ext/IntervalArithmeticsIntervalSetsExt.jl new file mode 100644 index 000000000..d5b89713e --- /dev/null +++ b/ext/IntervalArithmeticsIntervalSetsExt.jl @@ -0,0 +1,23 @@ +module IntervalArithmeticsIntervalSetsExt + +import IntervalSets as IS +import IntervalArithmetic as IA + +IA.interval(i::IS.Interval{L,R,T}) where {L,R,T} = IA.interval(IA.promote_numtype(T, T), i) +function IA.interval(::Type{T}, i::IS.Interval{L,R}) where {T<:IA.NumTypes,L,R} + # infinite endpoints are always open in IA, finite always closed: + isinf(IS.leftendpoint(i)) != IS.isleftopen(i) && return IA.nai(T) + isinf(IS.rightendpoint(i)) != IS.isrightopen(i) && return IA.nai(T) + x = IA.interval(T, IS.endpoints(i)...) + return IA._unsafe_interval(IA.bareinterval(x), IA.decoration(x), false) +end + +function IS.Interval(i::IA.Interval) + lo, hi = IA.bounds(i) + # infinite endpoints are always open in IA, finite always closed: + L = ifelse(isinf(lo), :open, :closed) + R = ifelse(isinf(hi), :open, :closed) + return IS.Interval{L,R}(lo, hi) +end + +end diff --git a/test/Project.toml b/test/Project.toml index a7e449099..70dd3a83f 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -1,4 +1,5 @@ [deps] ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240" +IntervalSets = "8197267c-284f-5f27-9208-e0e47529a953" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/test/interval_tests/construction.jl b/test/interval_tests/construction.jl index 4115a9e01..81e882302 100644 --- a/test/interval_tests/construction.jl +++ b/test/interval_tests/construction.jl @@ -157,6 +157,27 @@ end @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))) diff --git a/test/runtests.jl b/test/runtests.jl index 1c5cf2d50..cebbfb63a 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -3,6 +3,7 @@ using Test using ForwardDiff using IntervalArithmetic using InteractiveUtils +import IntervalSets as IS include("generate_ITF1788.jl")