From fea1b3403a5682874b4d42c5c8170f67be71d582 Mon Sep 17 00:00:00 2001 From: Petr Vana Date: Tue, 24 May 2022 20:10:05 +0200 Subject: [PATCH 01/11] Introduce simplified version of mod --- src/IntervalArithmetic.jl | 1 + src/intervals/functions.jl | 10 ++++++++++ test/interval_tests/numeric.jl | 24 ++++++++++++++++++++++++ 3 files changed, 35 insertions(+) diff --git a/src/IntervalArithmetic.jl b/src/IntervalArithmetic.jl index 0d8fc079b..19676a1ba 100644 --- a/src/IntervalArithmetic.jl +++ b/src/IntervalArithmetic.jl @@ -24,6 +24,7 @@ import Base: in, zero, one, eps, typemin, typemax, abs, abs2, real, min, max, sqrt, exp, log, sin, cos, tan, cot, inv, cbrt, csc, hypot, sec, exp2, exp10, log2, log10, + mod, asin, acos, atan, sinh, cosh, tanh, coth, csch, sech, asinh, acosh, atanh, sinpi, cospi, union, intersect, isempty, diff --git a/src/intervals/functions.jl b/src/intervals/functions.jl index 110bb41ab..731d7d2f5 100644 --- a/src/intervals/functions.jl +++ b/src/intervals/functions.jl @@ -373,3 +373,13 @@ function nthroot(a::Interval{T}, n::Integer) where T b = nthroot(bigequiv(a), n) return convert(Interval{T}, b) end + +""" +Calculate `x mod y` where `x` is an interval and `y` is a positive divisor. +""" +function mod(x::Interval, y::Real) + @assert y > 0 "modulo is currently implemented only for a positive divisor." + division = x / y + fl = floor(division) + fl.lo < fl.hi ? 0..y : y * (division - fl) +end diff --git a/test/interval_tests/numeric.jl b/test/interval_tests/numeric.jl index 3f447a5d6..8503d7ef4 100644 --- a/test/interval_tests/numeric.jl +++ b/test/interval_tests/numeric.jl @@ -434,3 +434,27 @@ end @test nthroot(Interval{BigFloat}(-81, -16), -4) == ∅ @test nthroot(Interval{BigFloat}(-81, -16), 1) == Interval{BigFloat}(-81, -16) end + +# approximation used in this testing (not to rely on ≈ for intervals) +≊(x::Interval, y::Interval) = x.lo ≈ y.lo && x.hi ≈ y.hi + +@testset "`mod`" begin + r = 0.0625 + x = r..(1+r) + @test mod(x, 1) == mod(x, 1.0) == 0..1 + @test mod(x, 2) == mod(x, 2.0) ≊ x + @test mod(x, 2.5) ≊ x + @test mod(x, 0.5) == 0..0.5 + + x = (-1+r) .. -r + @test mod(x, 1) == mod(x, 1.0) ≊ 1+x + @test mod(x, 2) == mod(x, 2.0) ≊ 2+x + @test mod(x, 2.5) ≊ 2.5+x + @test mod(x, 0.5) == 0..0.5 + + x = -r .. 1-r + @test mod(x, 1) == mod(x, 1.0) == 0..1 + @test mod(x, 2) == mod(x, 2.0) == 0..2 + @test mod(x, 2.5) == 0..2.5 + @test mod(x, 0.5) == 0..0.5 +end From 153d749887a1cfbe5c480c91630116da25bd8020 Mon Sep 17 00:00:00 2001 From: Petr Vana Date: Tue, 24 May 2022 20:27:51 +0200 Subject: [PATCH 02/11] Improve testing --- test/interval_tests/numeric.jl | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/test/interval_tests/numeric.jl b/test/interval_tests/numeric.jl index 8503d7ef4..f055754d0 100644 --- a/test/interval_tests/numeric.jl +++ b/test/interval_tests/numeric.jl @@ -435,21 +435,22 @@ end @test nthroot(Interval{BigFloat}(-81, -16), 1) == Interval{BigFloat}(-81, -16) end -# approximation used in this testing (not to rely on ≈ for intervals) -≊(x::Interval, y::Interval) = x.lo ≈ y.lo && x.hi ≈ y.hi +# approximation used for testing (not to rely on ≈ for intervals) +# ⪆(x, y) = (x ≈ y) && (y ⊆ x) +⪆(x::Interval, y::Interval) = x.lo ≈ y.lo && x.hi ≈ y.hi && y ⊆ x @testset "`mod`" begin r = 0.0625 x = r..(1+r) @test mod(x, 1) == mod(x, 1.0) == 0..1 - @test mod(x, 2) == mod(x, 2.0) ≊ x - @test mod(x, 2.5) ≊ x + @test mod(x, 2) == mod(x, 2.0) ⪆ x + @test mod(x, 2.5) ⪆ x @test mod(x, 0.5) == 0..0.5 x = (-1+r) .. -r - @test mod(x, 1) == mod(x, 1.0) ≊ 1+x - @test mod(x, 2) == mod(x, 2.0) ≊ 2+x - @test mod(x, 2.5) ≊ 2.5+x + @test mod(x, 1) == mod(x, 1.0) ⪆ 1+x + @test mod(x, 2) == mod(x, 2.0) ⪆ 2+x + @test mod(x, 2.5) ⪆ 2.5+x @test mod(x, 0.5) == 0..0.5 x = -r .. 1-r From d23df89472ae2721abdfbcaf46687efdd4c3890e Mon Sep 17 00:00:00 2001 From: Petr Vana Date: Tue, 24 May 2022 21:13:53 +0200 Subject: [PATCH 03/11] Update src/intervals/functions.jl Co-authored-by: lucaferranti <49938764+lucaferranti@users.noreply.github.com> --- src/intervals/functions.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/intervals/functions.jl b/src/intervals/functions.jl index 731d7d2f5..be3de79c2 100644 --- a/src/intervals/functions.jl +++ b/src/intervals/functions.jl @@ -381,5 +381,5 @@ function mod(x::Interval, y::Real) @assert y > 0 "modulo is currently implemented only for a positive divisor." division = x / y fl = floor(division) - fl.lo < fl.hi ? 0..y : y * (division - fl) + fl.lo < fl.hi ? Interval(zero(y), y) : y * (division - fl) end From b56f4bec4b77fba85fee6039943542a7a4caa3d7 Mon Sep 17 00:00:00 2001 From: Petr Vana Date: Wed, 25 May 2022 13:44:24 +0200 Subject: [PATCH 04/11] =?UTF-8?q?Use=20=E2=8A=87=20operator?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/interval_tests/numeric.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/interval_tests/numeric.jl b/test/interval_tests/numeric.jl index f055754d0..d23b679e1 100644 --- a/test/interval_tests/numeric.jl +++ b/test/interval_tests/numeric.jl @@ -436,8 +436,8 @@ end end # approximation used for testing (not to rely on ≈ for intervals) -# ⪆(x, y) = (x ≈ y) && (y ⊆ x) -⪆(x::Interval, y::Interval) = x.lo ≈ y.lo && x.hi ≈ y.hi && y ⊆ x +# ⪆(x, y) = (x ≈ y) && (x ⊇ y) +⪆(x::Interval, y::Interval) = x.lo ≈ y.lo && x.hi ≈ y.hi && x ⊇ y @testset "`mod`" begin r = 0.0625 From ea00b231585d2307e4b10ba846f83943f0fe68d8 Mon Sep 17 00:00:00 2001 From: Petr Vana Date: Wed, 25 May 2022 19:59:30 +0200 Subject: [PATCH 05/11] Imrpove test coverage + use zero() --- src/intervals/functions.jl | 2 +- test/interval_tests/numeric.jl | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/intervals/functions.jl b/src/intervals/functions.jl index be3de79c2..cc9d6f2b1 100644 --- a/src/intervals/functions.jl +++ b/src/intervals/functions.jl @@ -378,7 +378,7 @@ end Calculate `x mod y` where `x` is an interval and `y` is a positive divisor. """ function mod(x::Interval, y::Real) - @assert y > 0 "modulo is currently implemented only for a positive divisor." + @assert y > zero(y) "modulo is currently implemented only for a positive divisor." division = x / y fl = floor(division) fl.lo < fl.hi ? Interval(zero(y), y) : y * (division - fl) diff --git a/test/interval_tests/numeric.jl b/test/interval_tests/numeric.jl index d23b679e1..3d88a91a0 100644 --- a/test/interval_tests/numeric.jl +++ b/test/interval_tests/numeric.jl @@ -458,4 +458,6 @@ end @test mod(x, 2) == mod(x, 2.0) == 0..2 @test mod(x, 2.5) == 0..2.5 @test mod(x, 0.5) == 0..0.5 + + @test_throws AssertionError mod(x, -1) end From ff4791034526c3104889ee7105b7bcfb3809cd93 Mon Sep 17 00:00:00 2001 From: Petr Vana Date: Thu, 26 May 2022 11:11:35 +0200 Subject: [PATCH 06/11] Add todo for mod with between two intervals --- test/interval_tests/numeric.jl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/interval_tests/numeric.jl b/test/interval_tests/numeric.jl index 3d88a91a0..d44a44122 100644 --- a/test/interval_tests/numeric.jl +++ b/test/interval_tests/numeric.jl @@ -460,4 +460,7 @@ end @test mod(x, 0.5) == 0..0.5 @test_throws AssertionError mod(x, -1) + + # TODO - implement mod for two intervals + @test_throws TypeError mod(1..2, 1.4..1.5) end From 45abc460ab885ba1648c3b83fb74a96053c169f9 Mon Sep 17 00:00:00 2001 From: Petr Vana Date: Thu, 26 May 2022 11:30:14 +0200 Subject: [PATCH 07/11] Implementation for strictly negative divisors for mod --- src/intervals/functions.jl | 9 +++++++-- test/interval_tests/numeric.jl | 14 ++++++++++++-- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/intervals/functions.jl b/src/intervals/functions.jl index cc9d6f2b1..5fb2f03ef 100644 --- a/src/intervals/functions.jl +++ b/src/intervals/functions.jl @@ -378,8 +378,13 @@ end Calculate `x mod y` where `x` is an interval and `y` is a positive divisor. """ function mod(x::Interval, y::Real) - @assert y > zero(y) "modulo is currently implemented only for a positive divisor." + @assert y != zero(y) """mod(x::Interval, y::Real) +is currently implemented only for a strictly positive or negative divisor y.""" division = x / y fl = floor(division) - fl.lo < fl.hi ? Interval(zero(y), y) : y * (division - fl) + if !isthin(fl) + return y > zero(y) ? Interval(zero(y), y) : Interval(y, zero(y)) + else + return y * (division - fl) + end end diff --git a/test/interval_tests/numeric.jl b/test/interval_tests/numeric.jl index d44a44122..d933f9c7c 100644 --- a/test/interval_tests/numeric.jl +++ b/test/interval_tests/numeric.jl @@ -446,20 +446,30 @@ end @test mod(x, 2) == mod(x, 2.0) ⪆ x @test mod(x, 2.5) ⪆ x @test mod(x, 0.5) == 0..0.5 + @test mod(x, -1) == mod(x, -1.0) == -1..0 + @test mod(x, -2) == mod(x, -2.0) ⪆ -2+x + @test mod(x, -2.5) ⪆ -2.5+x + @test mod(x, -0.5) == -0.5..0 x = (-1+r) .. -r @test mod(x, 1) == mod(x, 1.0) ⪆ 1+x @test mod(x, 2) == mod(x, 2.0) ⪆ 2+x @test mod(x, 2.5) ⪆ 2.5+x @test mod(x, 0.5) == 0..0.5 + @test mod(x, -1) == mod(x, -1.0) ⪆ x + @test mod(x, -2) == mod(x, -2.0) ⪆ x + @test mod(x, -2.5) ⪆ x + @test mod(x, -0.5) == -0.5..0 x = -r .. 1-r @test mod(x, 1) == mod(x, 1.0) == 0..1 @test mod(x, 2) == mod(x, 2.0) == 0..2 @test mod(x, 2.5) == 0..2.5 @test mod(x, 0.5) == 0..0.5 - - @test_throws AssertionError mod(x, -1) + @test mod(x, -1) == mod(x, -1.0) == -1..0 + @test mod(x, -2) == mod(x, -2.0) == -2..0 + @test mod(x, -2.5) == -2.5..0 + @test mod(x, -0.5) == -0.5..0 # TODO - implement mod for two intervals @test_throws TypeError mod(1..2, 1.4..1.5) From d2603d6c4524ab884aeaabaf2265f8d123b13d28 Mon Sep 17 00:00:00 2001 From: Petr Vana Date: Thu, 26 May 2022 11:34:04 +0200 Subject: [PATCH 08/11] Update docs --- src/intervals/functions.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/intervals/functions.jl b/src/intervals/functions.jl index 5fb2f03ef..11b82571e 100644 --- a/src/intervals/functions.jl +++ b/src/intervals/functions.jl @@ -375,7 +375,7 @@ function nthroot(a::Interval{T}, n::Integer) where T end """ -Calculate `x mod y` where `x` is an interval and `y` is a positive divisor. +Calculate `x::Interval mod y::Real`, limited by `y != 0`. """ function mod(x::Interval, y::Real) @assert y != zero(y) """mod(x::Interval, y::Real) From 439723f068feb228e703fb03b20270586c2d83b9 Mon Sep 17 00:00:00 2001 From: Petr Vana Date: Fri, 27 May 2022 11:01:27 +0200 Subject: [PATCH 09/11] Disable divisor for mod to be an interval --- src/intervals/functions.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/intervals/functions.jl b/src/intervals/functions.jl index cc9d6f2b1..5ea1cf59d 100644 --- a/src/intervals/functions.jl +++ b/src/intervals/functions.jl @@ -383,3 +383,5 @@ function mod(x::Interval, y::Real) fl = floor(division) fl.lo < fl.hi ? Interval(zero(y), y) : y * (division - fl) end + +mod(x:T, y::Interval) where T = throw(ArgumentError("mod not defined for interval as divisor `y`")) From f9f4733a1134e4ad3a946ddf8023e0d7b5d7fa80 Mon Sep 17 00:00:00 2001 From: Petr Vana Date: Fri, 27 May 2022 11:10:05 +0200 Subject: [PATCH 10/11] Throw ArgumentError for Interval divisor for mod --- src/intervals/functions.jl | 5 +++-- test/interval_tests/numeric.jl | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/intervals/functions.jl b/src/intervals/functions.jl index 588b163d7..fdb3f3f16 100644 --- a/src/intervals/functions.jl +++ b/src/intervals/functions.jl @@ -375,7 +375,7 @@ function nthroot(a::Interval{T}, n::Integer) where T end """ -Calculate `x::Interval mod y::Real`, limited by `y != 0`. +Calculate `x::Interval mod y::Real` where y != zero(y) and y is not inteval`. """ function mod(x::Interval, y::Real) @assert y != zero(y) """mod(x::Interval, y::Real) @@ -389,4 +389,5 @@ is currently implemented only for a strictly positive or negative divisor y.""" end end -mod(x:T, y::Interval) where T = throw(ArgumentError("mod not defined for interval as divisor `y`")) +mod(x::Interval, y::Interval) where T = throw(ArgumentError("mod not defined for interval as divisor `y`")) +mod(x::Real, y::Interval) where T = throw(ArgumentError("mod not defined for interval as divisor `y`")) diff --git a/test/interval_tests/numeric.jl b/test/interval_tests/numeric.jl index d933f9c7c..11f1d0877 100644 --- a/test/interval_tests/numeric.jl +++ b/test/interval_tests/numeric.jl @@ -472,5 +472,6 @@ end @test mod(x, -0.5) == -0.5..0 # TODO - implement mod for two intervals - @test_throws TypeError mod(1..2, 1.4..1.5) + @test_throws ArgumentError mod(1..2, 1.4..1.5) + @test_throws ArgumentError mod(1.0, 1.4..1.5) end From 7bef23ed8c85ee8c392f8f80e91092e64fc72d63 Mon Sep 17 00:00:00 2001 From: Petr Vana Date: Fri, 27 May 2022 11:11:53 +0200 Subject: [PATCH 11/11] Cleanup --- src/intervals/functions.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/intervals/functions.jl b/src/intervals/functions.jl index fdb3f3f16..aa3ee1c42 100644 --- a/src/intervals/functions.jl +++ b/src/intervals/functions.jl @@ -389,5 +389,5 @@ is currently implemented only for a strictly positive or negative divisor y.""" end end -mod(x::Interval, y::Interval) where T = throw(ArgumentError("mod not defined for interval as divisor `y`")) -mod(x::Real, y::Interval) where T = throw(ArgumentError("mod not defined for interval as divisor `y`")) +mod(x::Interval, y::Interval) = throw(ArgumentError("mod not defined for interval as divisor `y`")) +mod(x::Real, y::Interval) = throw(ArgumentError("mod not defined for interval as divisor `y`"))