Skip to content

Commit

Permalink
Introduce closure function, extracted out of convert (#114)
Browse files Browse the repository at this point in the history
* Introduce `closure` function, separate from convert.

* Fix find_quadrants (implicit convert -> explicit closure).

* Add docstring to closure.

* Remove tests with undesired behavior.

* Remove one convert method, test another.

* closure -> atomic
  • Loading branch information
tkoolen authored and dpsanders committed Apr 10, 2018
1 parent e6375b0 commit 373b19b
Show file tree
Hide file tree
Showing 9 changed files with 97 additions and 56 deletions.
2 changes: 1 addition & 1 deletion src/decorations/intervals.jl
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ convert(::Type{DecoratedInterval{T}}, x::S) where {T<:Real, S<:Integer} =
# end
function convert(::Type{DecoratedInterval{T}}, xx::DecoratedInterval) where T<:Real
x = interval_part(xx)
x = convert(Interval{T},x)
x = atomic(Interval{T},x)
DecoratedInterval( x, decoration(xx) )
end

Expand Down
105 changes: 73 additions & 32 deletions src/intervals/conversion.jl
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,77 @@ promote_rule(::Type{BigFloat}, ::Type{Interval{T}}) where T<:Real =
Interval{promote_type(T, BigFloat)}


# convert methods:
convert(::Type{Interval{T}}, x) where {T} = atomic(Interval{T}, x)
convert(::Type{Interval{T}}, x::T) where {T} = Interval{T}(x)
convert(::Type{Interval{T}}, x::Interval{T}) where {T} = x
convert(::Type{Interval{T}}, x::Interval) where {T} = atomic(Interval{T}, x)

# Floating point intervals:
convert(::Type{Interval}, x::Real) = (T = typeof(float(x)); convert(Interval{T}, x))
convert(::Type{Interval}, x::Interval) = x

convert(::Type{Interval{T}}, x::AbstractString) where T<:AbstractFloat =
parse(Interval{T}, x)
"""
atomic(::Type{<:Interval}, x)
Construct the tightest interval of a given type that contains the value `x`.
If `x` is an `AbstractString`, the interval will be created by calling `parse`.
# Examples
Construct an `Interval{Float64}` containing a given `BigFloat`:
```julia
julia> x = big"0.1"
1.000000000000000000000000000000000000000000000000000000000000000000000000000002e-01
julia> i = IntervalArithmetic.atomic(Interval{Float64}, x)
[0.0999999, 0.100001]
julia> i isa Interval{Float64}
true
julia> i.lo <= x <= i.hi
true
julia> i.hi === nextfloat(i.lo)
true
```
Construct an `Interval{Float32}` containing a the real number 0.1 in two ways:
```julia
julia> i1 = IntervalArithmetic.atomic(Interval{Float32}, "0.1")
[0.0999999, 0.100001]
convert(::Type{Interval}, x::AbstractString) = convert(Interval{Float64}, x)
julia> i2 = IntervalArithmetic.atomic(Interval{Float32}, 1//10)
[0.0999999, 0.100001]
function convert(::Type{Interval{T}}, x::S) where {T<:AbstractFloat, S<:Real}
julia> i1 === i2
true
julia> i.lo <= 1//10 <= i.hi
true
```
"""
function atomic end

# Integer intervals:
atomic(::Type{Interval{T}}, x::T) where {T<:Integer} = Interval{T}(x)

# Floating point intervals:
atomic(::Type{Interval{T}}, x::AbstractString) where T<:AbstractFloat =
parse(Interval{T}, x)

function atomic(::Type{Interval{T}}, x::S) where {T<:AbstractFloat, S<:Real}
isinf(x) && return wideinterval(T(x))

Interval{T}( T(x, RoundDown), T(x, RoundUp) )
# the rounding up could be done as nextfloat of the rounded down one?
# use @round_up and @round_down here?
end

function convert(::Type{Interval{T}}, x::S) where {T<:AbstractFloat, S<:AbstractFloat}
function atomic(::Type{Interval{T}}, x::S) where {T<:AbstractFloat, S<:AbstractFloat}
isinf(x) && return wideinterval(x)#Interval{T}(prevfloat(T(x)), nextfloat(T(x)))
# isinf(x) && return Interval{T}(prevfloat(x), nextfloat(x))

Expand All @@ -41,50 +95,37 @@ function convert(::Type{Interval{T}}, x::S) where {T<:AbstractFloat, S<:Abstract
return Interval(parse(T, xstr, RoundDown), parse(T, xstr, RoundUp))
end

return convert(Interval{T}, xrat)
return atomic(Interval{T}, xrat)
end

convert(::Type{Interval{T}}, x::Interval{T}) where T<:AbstractFloat = x

function convert(::Type{Interval{T}}, x::Interval) where T<:AbstractFloat
function atomic(::Type{Interval{T}}, x::Interval) where T<:AbstractFloat
Interval{T}( T(x.lo, RoundDown), T(x.hi, RoundUp) )
end


# Complex numbers:
convert(::Type{Interval{T}}, x::Complex{Bool}) where T<:AbstractFloat =
atomic(::Type{Interval{T}}, x::Complex{Bool}) where T<:AbstractFloat =
(x == im) ? one(T)*im : throw(ArgumentError("Complex{Bool} not equal to im"))


# Rational intervals
function convert(::Type{Interval{Rational{Int}}}, x::Irrational)
a = float(convert(Interval{BigFloat}, x))
convert(Interval{Rational{Int}}, a)
function atomic(::Type{Interval{Rational{Int}}}, x::Irrational)
a = float(atomic(Interval{BigFloat}, x))
atomic(Interval{Rational{Int}}, a)
end

function convert(::Type{Interval{Rational{BigInt}}}, x::Irrational)
a = convert(Interval{BigFloat}, x)
convert(Interval{Rational{BigInt}}, a)
function atomic(::Type{Interval{Rational{BigInt}}}, x::Irrational)
a = atomic(Interval{BigFloat}, x)
atomic(Interval{Rational{BigInt}}, a)
end

convert(::Type{Interval{Rational{T}}}, x::S) where {T<:Integer, S<:Integer} =
atomic(::Type{Interval{Rational{T}}}, x::S) where {T<:Integer, S<:Integer} =
Interval(x*one(Rational{T}))

convert(::Type{Interval{Rational{T}}}, x::Rational{S}) where
atomic(::Type{Interval{Rational{T}}}, x::Rational{S}) where
{T<:Integer, S<:Integer} = Interval(x*one(Rational{T}))

convert(::Type{Interval{Rational{T}}}, x::S) where {T<:Integer, S<:Float64} =
atomic(::Type{Interval{Rational{T}}}, x::S) where {T<:Integer, S<:Float64} =
Interval(rationalize(T, x))

convert(::Type{Interval{Rational{T}}}, x::S) where {T<:Integer, S<:BigFloat} =
atomic(::Type{Interval{Rational{T}}}, x::S) where {T<:Integer, S<:BigFloat} =
Interval(rationalize(T, x))


# conversion to Interval without explicit type:
function convert(::Type{Interval}, x::Real)
T = typeof(float(x))

return convert(Interval{T}, x)
end

convert(::Type{Interval}, x::Interval) = x
12 changes: 6 additions & 6 deletions src/intervals/functions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

# Write explicitly like this to avoid ambiguity warnings:
for T in (:Integer, :Rational, :Float64, :BigFloat, :Interval)
@eval ^(a::Interval{Float64}, x::$T) = convert(Interval{Float64}, big53(a)^x)
@eval ^(a::Interval{Float64}, x::$T) = atomic(Interval{Float64}, big53(a)^x)
end


Expand Down Expand Up @@ -98,7 +98,7 @@ function ^(a::Interval{BigFloat}, x::BigFloat)
a = a domain
(isempty(x) || isempty(a)) && return emptyinterval(a)

xx = convert(Interval{BigFloat}, x)
xx = atomic(Interval{BigFloat}, x)

# @round() can't be used directly, because both arguments may
# Inf or -Inf, which throws an error
Expand Down Expand Up @@ -135,7 +135,7 @@ end
function ^(a::Interval{Rational{T}}, x::AbstractFloat) where T<:Integer
a = Interval(a.lo.num/a.lo.den, a.hi.num/a.hi.den)
a = a^x
convert(Interval{Rational{T}}, a)
atomic(Interval{Rational{T}}, a)
end

# Rational power
Expand All @@ -149,13 +149,13 @@ function ^(a::Interval{BigFloat}, r::Rational{S}) where S<:Integer
return emptyinterval(a)
end

isinteger(r) && return convert(Interval{T}, a^round(S,r))
isinteger(r) && return atomic(Interval{T}, a^round(S,r))
r == one(S)//2 && return sqrt(a)

a = a domain
(isempty(r) || isempty(a)) && return emptyinterval(a)

y = convert(Interval{BigFloat}, r)
y = atomic(Interval{BigFloat}, r)

a^y
end
Expand Down Expand Up @@ -245,7 +245,7 @@ for f in (:exp2, :exp10)
end
end

@eval ($f)(a::Interval{Float64}) = convert(Interval{Float64}, $f(big53(a))) # no CRlibm version
@eval ($f)(a::Interval{Float64}) = atomic(Interval{Float64}, $f(big53(a))) # no CRlibm version

@eval function ($f)(a::Interval{BigFloat})
isempty(a) && return a
Expand Down
2 changes: 1 addition & 1 deletion src/intervals/hyperbolic.jl
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,6 @@ for f in (:tanh, :asinh, :acosh, :atanh)
@eval function ($f)(a::Interval{Float64})
isempty(a) && return a

convert(Interval{Float64}, ($f)(big53(a)) )
atomic(Interval{Float64}, ($f)(big53(a)) )
end
end
7 changes: 5 additions & 2 deletions src/intervals/intervals.jl
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ Interval(x::Complex) = Interval(real(x)) + im*Interval(imag(x))

Interval{T}(x) where T = Interval(convert(T, x))

Interval{T}(x::Interval) where T = convert(Interval{T}, x)
Interval{T}(x::Interval) where T = atomic(Interval{T}, x)

"""
is_valid_interval(a::Real, b::Real)
Expand Down Expand Up @@ -122,7 +122,9 @@ include("hyperbolic.jl")

# Syntax for intervals

a..b = interval(convert(Interval, a).lo, convert(Interval, b).hi)
function ..(a::T, b::S) where {T, S}
interval(atomic(Interval{T}, a).lo, atomic(Interval{S}, b).hi)
end

# ..(a::Integer, b::Integer) = interval(a, b)
# ..(a::Integer, b::Real) = interval(a, nextfloat(float(b)))
Expand All @@ -135,3 +137,4 @@ macro I_str(ex) # I"[3,4]"
end

a ± b = (a-b)..(a+b)
±(a::Interval, b) = (a.lo - b)..(a.hi + b)
4 changes: 2 additions & 2 deletions src/intervals/macros.jl
Original file line number Diff line number Diff line change
Expand Up @@ -81,13 +81,13 @@ by calling `transform`."""
function make_interval(T, expr1, expr2)
# @show expr1, expr2

expr1 = transform(expr1, :convert, :(Interval{$T}))
expr1 = transform(expr1, :atomic, :(Interval{$T}))

if isempty(expr2) # only one argument
return :(Interval($expr1))
end

expr2 = transform(expr2[1], :convert, :(Interval{$T}))
expr2 = transform(expr2[1], :atomic, :(Interval{$T}))

:(interval(($expr1).lo, ($expr2).hi))
end
10 changes: 5 additions & 5 deletions src/intervals/precision.jl
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const parameters = IntervalParameters()
doc"`big53` creates an equivalent `BigFloat` interval to a given `Float64` interval."
function big53(a::Interval{Float64})
setprecision(Interval, 53) do # precision of Float64
convert(Interval{BigFloat}, a)
atomic(Interval{BigFloat}, a)
end
end

Expand All @@ -41,7 +41,7 @@ function setprecision(::Type{Interval}, ::Type{T}, prec::Integer) where T<:Abstr

parameters.precision_type = T
parameters.precision = prec
parameters.pi = convert(Interval{BigFloat}, pi)
parameters.pi = atomic(Interval{BigFloat}, pi)

prec
end
Expand Down Expand Up @@ -69,7 +69,7 @@ setprecision(::Type{Interval}, t::Tuple) = setprecision(Interval, t...)
precision(::Type{Interval}) = (parameters.precision_type, parameters.precision)


const float_interval_pi = convert(Interval{Float64}, pi) # does not change
const float_interval_pi = atomic(Interval{Float64}, pi) # does not change

pi_interval(::Type{BigFloat}) = parameters.pi
pi_interval(::Type{Float64}) = float_interval_pi
Expand All @@ -82,6 +82,6 @@ end



float(x::Interval{T}) where T = convert( Interval{float(T)}, x) # https://github.com/dpsanders/IntervalArithmetic.jl/issues/174
float(x::Interval{T}) where T = atomic( Interval{float(T)}, x) # https://github.com/dpsanders/IntervalArithmetic.jl/issues/174

big(x::Interval) = convert(Interval{BigFloat}, x)
big(x::Interval) = atomic(Interval{BigFloat}, x)
6 changes: 3 additions & 3 deletions src/intervals/trigonometric.jl
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ This is a rather indirect way to determine if π/2 and 3π/2 are contained
in the interval; cf. the formula for sine of an interval in
Tucker, *Validated Numerics*."""

function find_quadrants(x::AbstractFloat)
temp = x / half_pi(x)
function find_quadrants(x::T) where {T<:AbstractFloat}
temp = atomic(Interval{T}, x) / half_pi(x)
(floor(temp.lo), floor(temp.hi))
end

Expand Down Expand Up @@ -172,7 +172,7 @@ end
function atan2(y::Interval{Float64}, x::Interval{Float64})
(isempty(y) || isempty(x)) && return emptyinterval(Float64)

convert(Interval{Float64}, atan2(big53(y), big53(x)))
atomic(Interval{Float64}, atan2(big53(y), big53(x)))
end


Expand Down
5 changes: 1 addition & 4 deletions test/interval_tests/construction.jl
Original file line number Diff line number Diff line change
Expand Up @@ -64,17 +64,14 @@ using Base.Test
@test convert(Interval, eu) == @interval(eu)
@test convert(Interval, BigInt(1)) == Interval(BigInt(1))
@test convert(Interval, 1//10) == @interval(1//10)
@test convert(Interval, 0.1) == Interval(0.09999999999999999, 0.1)
@test convert(Interval, big"0.1") == big"0.1"..big"0.1"
@test convert(Interval, Interval(0.1, 0.2)) === Interval(0.1, 0.2)

@test convert(Interval{Rational{Int}}, 0.1) == Interval(1//10)
# @test convert(Interval{Rational{BigInt}}, pi) == Interval{Rational{BigInt}}(pi)

## promotion
@test promote(Interval(2//1,3//1), Interval(1, 2)) ==
(Interval(2.0,3.0), Interval(1.0,2.0))
@test promote(Interval(1.5), parse(BigFloat, "2.1")) ==
(Interval(BigFloat(1.5)), big"2.1"..big"2.1")
@test promote(Interval(1.0), pi) == (Interval(1.0), @interval(pi))

# Constructors from the macros @interval, @floatinterval @biginterval
Expand Down

0 comments on commit 373b19b

Please sign in to comment.