Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC: Add display mode capability #115

Merged
merged 13 commits into from
May 4, 2016
10 changes: 8 additions & 2 deletions src/ValidatedNumerics.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ using FixedSizeArrays
import Base:
+, -, *, /, //, fma,
<, >, ==, !=, ⊆, ^, <=,
in, zero, one, abs, real, show, min, max,
in, zero, one, abs, real, min, max,
sqrt, exp, log, sin, cos, tan, inv,
exp2, exp10, log2, log10,
asin, acos, atan, atan2,
Expand All @@ -23,7 +23,8 @@ import Base:
floor, ceil, trunc, sign, round,
expm1, log1p,
precision,
isfinite, isnan
isfinite, isnan,
show, showall

export
Interval, AbstractInterval,
Expand All @@ -40,6 +41,9 @@ export
cancelminus, cancelplus, isunbounded,
.., @I_str, ±

export
displaymode

if VERSION >= v"0.5.0-dev+1182"
import Base: rounding, setrounding, setprecision
else
Expand Down Expand Up @@ -84,6 +88,8 @@ include("intervals/intervals.jl")
include("multidim/multidim.jl")
include("decorations/decorations.jl")

include("display.jl")

include("root_finding/root_finding.jl")


Expand Down
4 changes: 2 additions & 2 deletions src/decorations/intervals.jl
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ The nomenclature of the follows the IEEE-1788 (2015) standard
# Note that `isless`, and hence ``<` and `min`, are automatically defined for enums

"""
DecoratedInterval
DecoratedInterval

A `DecoratedInterval` is an interval, together with a *decoration*, i.e.
a flag that records the status of the interval when thought of as the result
Expand Down Expand Up @@ -84,7 +84,7 @@ function convert{T<:Real}(::Type{DecoratedInterval{T}}, xx::DecoratedInterval)
end


show(io::IO, x::DecoratedInterval) = print(io, x.interval, "_", x.decoration)
# show(io::IO, x::DecoratedInterval) = print(io, x.interval, "_", x.decoration)

macro decorated(ex...)
local x
Expand Down
136 changes: 136 additions & 0 deletions src/display.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
type DisplayParameters
format::Symbol
decorations::Bool
sigfigs::Int
end

const display_params = DisplayParameters(:standard, false, 6)

const display_options = [:standard, :full, :midpoint]

doc"""
displaymode(;kw)

`displaymode` changes how intervals are displayed using keyword arguments.
The following options are available:

- `format`: interval output format

- `:standard`: `[1, 2]`
- `:full`: `Interval(1, 2)`
- `:midpoint`: 1.5 ± 0.5
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think using Interval(0.1,0.2) yields clearer examples.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... or any other which is not an Interval with integers.


- `sigfigs`: number of significant figures to show in `standard` mode

- `decorations` (boolean): show decorations or not
"""
function displaymode(;decorations=nothing, format=nothing, sigfigs=-1)
if format != nothing

if format in display_options
display_params.format = format
else
throw(ArgumentError("Allowed format option is one of $display_options."))
end

end

if decorations != nothing
display_params.decorations = decorations
end

if sigfigs >= 0
display_params.sigfigs = sigfigs
end
end


## Output

# round to given number of signficant digits
# basic structure taken from base/mpfr.jl
function round_string(x::BigFloat, digits::Int, r::RoundingMode)

lng = digits + Int32(8)
buf = Array(UInt8, lng + 1)

lng = ccall((:mpfr_snprintf,:libmpfr), Int32,
(Ptr{UInt8}, Culong, Ptr{UInt8}, Int32, Ptr{BigFloat}...),
buf, lng + 1, "%.$(digits)R*g", Base.MPFR.to_mpfr(r), &x)

return bytestring(pointer(buf))
end

round_string(x::Real, digits::Int, r::RoundingMode) = round_string(big(x), digits, r)


function representation(a::Interval, format=nothing)
if isempty(a)
return "∅"
end

if format == nothing
format = display_params.format # default
end

sigfigs = display_params.sigfigs

local output

if format == :standard

aa = round_string(a.lo, sigfigs, RoundDown)
bb = round_string(a.hi, sigfigs, RoundUp)

output = "[$aa, $bb]"
output = replace(output, "inf", "∞")
output = replace(output, "Inf", "∞")

elseif format == :full
output = "Interval($(a.lo), $(a.hi))"

elseif format == :midpoint
m = round_string(mid(a), sigfigs, RoundNearest)
r = round_string(radius(a), sigfigs, RoundUp)
output = "$m ± $r"
end

output
end

function representation(a::Interval{BigFloat})
if display_params.format == :standard
string( invoke(representation, (Interval,), a),
subscriptify(precision(a.lo)) )

elseif display_params.format == :full
invoke(representation, (Interval,), a)
end
end

function representation(a::DecoratedInterval)

if display_params.format==:full
return "DecoratedInterval($(interval_part(a)), $(decoration(a)))"
end

interval = representation(interval_part(a))

if display_params.decorations
string(interval, "_", decoration(a))
else
interval
end

end

show(io::IO, a::Interval) = print(io, representation(a))
show(io::IO, a::DecoratedInterval) = print(io, representation(a))

showall(io::IO, a::Interval) = print(io, representation(a, :full))

function subscriptify(n::Int)
subscript_digits = [c for c in "₀₁₂₃₄₅₆₇₈₉"]
dig = reverse(digits(n))
join([subscript_digits[i+1] for i in dig])
end
28 changes: 1 addition & 27 deletions src/intervals/intervals.jl
Original file line number Diff line number Diff line change
Expand Up @@ -58,30 +58,4 @@ macro I_str(ex) # I"[3,4]"
@interval(ex)
end

a ± b = (a-b)..(a+b)


## Output

function basic_show(io::IO, a::Interval)
if isempty(a)
output = "∅"
else
output = "[$(a.lo), $(a.hi)]"
output = replace(output, "inf", "∞")
output = replace(output, "Inf", "∞")

output
end

print(io, output)
end

show(io::IO, a::Interval) = basic_show(io, a)
show(io::IO, a::Interval{BigFloat}) = ( basic_show(io, a); print(io, subscriptify(precision(a.lo))) )

function subscriptify(n::Int)
subscript_digits = [c for c in "₀₁₂₃₄₅₆₇₈₉"]
dig = reverse(digits(n))
join([subscript_digits[i+1] for i in dig])
end
a ± b = (a-b)..(a+b)
87 changes: 87 additions & 0 deletions test/display_tests/display.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@

using ValidatedNumerics
using FactCheck

setprecision(Interval, Float64)

facts("displaymode tests") do

context("Interval") do

a = 1..2
b = -1.1..1.3
c = Interval(pi)
d = @interval(π)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to add some tests with Interval{BigFloat} and DecoratedInterval{BigFloat}...


context("6 sig figs") do
displaymode(format=:standard, sigfigs=6)

@fact string(a) --> "[1, 2]"
@fact string(b) --> "[-1.10001, 1.30001]"
@fact string(c) --> "[3.14159, 3.1416]"
@fact string(d) --> "[3.14159, 3.1416]"
end

context("10 sig figs") do
displaymode(sigfigs=10)

@fact string(a) --> "[1, 2]"
@fact string(b) --> "[-1.100000001, 1.300000001]"
@fact string(c) --> "[3.141592653, 3.141592654]"
@fact string(d) --> "[3.141592653, 3.141592654]"
end

context("20 sig figs") do
displaymode(sigfigs=20)

@fact string(a) --> "[1, 2]"
@fact string(b) --> "[-1.1000000000000000889, 1.3000000000000000445]"
@fact string(c) --> "[3.1415926535897931159, 3.141592653589793116]"
@fact string(d) --> "[3.1415926535897931159, 3.1415926535897935601]"
end

context("Full") do
displaymode(format=:full)

@fact string(a) --> "Interval(1.0, 2.0)"
@fact string(b) --> "Interval(-1.1, 1.3)"
@fact string(c) --> "Interval(3.141592653589793, 3.141592653589793)"
@fact string(d) --> "Interval(3.141592653589793, 3.1415926535897936)"
end

context("Midpoint") do
displaymode(format=:midpoint, sigfigs=6)

@fact string(a) --> "1.5 ± 0.5"
@fact string(b) --> "0.1 ± 1.20001"
@fact string(c) --> "3.14159 ± 0"
@fact string(d) --> "3.14159 ± 4.4409e-16"
end


end

context("DecoratedInterval") do
a = @decorated(1, 2)

displaymode(format=:standard, decorations=false)

@fact string(a) --> "[1, 2]"

displaymode(format=:standard, decorations=true)

@fact string(a) --> "[1, 2]_com"

end

context("IntervalBox") do
displaymode(sigfigs=6)

X = IntervalBox(1..2, 3..4)
@fact string(X) --> "[1, 2] × [3, 4]"

X = IntervalBox(1.1..1.2, 2.1..2.2)
@fact string(X) --> "[1.09999, 1.20001] × [2.09999, 2.20001]"

end
end
5 changes: 0 additions & 5 deletions test/multidim_tests/multidim.jl
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,4 @@ facts("@intervalbox tests") do
@fact isa(g(X), IntervalBox) --> true


setprecision(Interval, Float64)

X = IntervalBox(1..1, 2..2)
@fact string(X) --> "[1.0, 1.0] × [2.0, 2.0]"

end
4 changes: 3 additions & 1 deletion test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@ include("interval_tests/intervals.jl")
include("multidim_tests/multidim.jl")
include("decoration_tests/decoration_tests.jl")

# Display tests:
include("display_tests/display.jl")

# Root-finding tests:

include("root_finding_tests/root_finding.jl")

# Multidimensional boxes tests:

# ITF1788 tests

Expand Down