From 9a9ced12d6dd3f4a00e957f1352446cc60e5d997 Mon Sep 17 00:00:00 2001 From: Joel Dahne Date: Fri, 19 Apr 2024 12:07:18 +0200 Subject: [PATCH 1/6] minimum/maximum: Fix issues with Base._fast Fixes two issues with the implementation of `Base._fast` and one issue with the tests. - It no longer has ambiguities for `Base._fast(::typeof(min), x::Arb, y::AbstractFloat)`. - It now also supports `ArbRef`, the previous version would give wrong results. - Fix the test for `Base._fast` not actually using an input for which it would otherwise fail. Also added tests for all changes. --- src/minmax.jl | 17 +++++++++++------ test/minmax.jl | 22 ++++++++++++++++++++-- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/src/minmax.jl b/src/minmax.jl index b4a3dc7..1d054c7 100644 --- a/src/minmax.jl +++ b/src/minmax.jl @@ -53,12 +53,17 @@ end # doesn't solve the full problem. # The default implementation in Base is not correct for Arb -Base._fast(::typeof(min), x::Arb, y::Arb) = min(x, y) -Base._fast(::typeof(min), x::Arb, y) = min(x, y) -Base._fast(::typeof(min), x, y::Arb) = min(x, y) -Base._fast(::typeof(max), x::Arb, y::Arb) = max(x, y) -Base._fast(::typeof(max), x::Arb, y) = max(x, y) -Base._fast(::typeof(max), x, y::Arb) = max(x, y) +Base._fast(::typeof(min), x::ArbOrRef, y::ArbOrRef) = min(x, y) +Base._fast(::typeof(min), x::ArbOrRef, y) = min(x, y) +Base._fast(::typeof(min), x, y::ArbOrRef) = min(x, y) +Base._fast(::typeof(max), x::ArbOrRef, y::ArbOrRef) = max(x, y) +Base._fast(::typeof(max), x::ArbOrRef, y) = max(x, y) +Base._fast(::typeof(max), x, y::ArbOrRef) = max(x, y) +# Handle ambiguous methods +Base._fast(::typeof(min), x::ArbOrRef, y::AbstractFloat) = min(x, y) +Base._fast(::typeof(min), x::AbstractFloat, y::ArbOrRef) = min(x, y) +Base._fast(::typeof(max), x::ArbOrRef, y::AbstractFloat) = max(x, y) +Base._fast(::typeof(max), x::AbstractFloat, y::ArbOrRef) = max(x, y) # Mag, Arf and Arb don't have signed zeros Base.isbadzero(::typeof(min), x::Union{Mag,Arf,Arb}) = false diff --git a/test/minmax.jl b/test/minmax.jl index 9dd9a32..f56a40b 100644 --- a/test/minmax.jl +++ b/test/minmax.jl @@ -61,8 +61,7 @@ @test iszero(extrema(identity, -A)[2]) # Fails with default implementation due to Base._fast - #A = [Arb(0); [setball(Arb, 0, i) for i in reverse(0:257)]] - A = [setball(Arb, 0, i) for i = 0:257] + A = [Arb(0); [setball(Arb, 0, i) for i in reverse(0:257)]] @test Arblib.contains(minimum(A), -257) @test Arblib.contains(maximum(A), 257) @test Arblib.contains(extrema(A)[1], -257) @@ -71,6 +70,25 @@ @test Arblib.contains(maximum(identity, A), 257) @test Arblib.contains(extrema(identity, A)[1], -257) @test Arblib.contains(extrema(identity, A)[2], 257) + # In a previous version of Arblib, Base._fast was not correctly + # overloaded for ArbRef. + A = [ + Arblib.realref(Acb(0)) + [Arblib.realref(Acb(setball(Arb, 0, i))) for i in reverse(0:257)] + ] + @test Arblib.contains(minimum(A), -257) + @test Arblib.contains(maximum(A), 257) + @test Arblib.contains(extrema(A)[1], -257) + @test Arblib.contains(extrema(A)[2], 257) + @test Arblib.contains(minimum(identity, A), -257) + @test Arblib.contains(maximum(identity, A), 257) + @test Arblib.contains(extrema(identity, A)[1], -257) + @test Arblib.contains(extrema(identity, A)[2], 257) + # In a previous version of Arblib, Base._fast was not correctly + # handling mixture of Arb and AbstractFloat + @test minimum(AbstractFloat[Arb(0); fill(1.0, 257)]) == 0 + @test maximum(AbstractFloat[Arb(0); fill(1.0, 257)]) == 1 + @test extrema(AbstractFloat[Arb(0); fill(1.0, 257)]) == (0, 1) # Fails with default implementation due to both short circuit # and Base._fast From 5da54958aa46011862404cb8b2fbf240c8a371e8 Mon Sep 17 00:00:00 2001 From: Joel Dahne Date: Fri, 19 Apr 2024 12:10:46 +0200 Subject: [PATCH 2/6] minimum/maximum: Fix the types for which Base.isbadzero is defined It is no longer defined for `Mag`, since `Mag` is not an `AbstractFloat` it is not needed. It is now defined for `ArfRef` and `ArbRef`. --- src/minmax.jl | 6 +++--- test/minmax.jl | 12 ++++++++++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/minmax.jl b/src/minmax.jl index 1d054c7..a1351b3 100644 --- a/src/minmax.jl +++ b/src/minmax.jl @@ -65,6 +65,6 @@ Base._fast(::typeof(min), x::AbstractFloat, y::ArbOrRef) = min(x, y) Base._fast(::typeof(max), x::ArbOrRef, y::AbstractFloat) = max(x, y) Base._fast(::typeof(max), x::AbstractFloat, y::ArbOrRef) = max(x, y) -# Mag, Arf and Arb don't have signed zeros -Base.isbadzero(::typeof(min), x::Union{Mag,Arf,Arb}) = false -Base.isbadzero(::typeof(max), x::Union{Mag,Arf,Arb}) = false +# Arf and Arb don't have signed zeros +Base.isbadzero(::typeof(min), x::Union{ArfOrRef,ArbOrRef}) = false +Base.isbadzero(::typeof(max), x::Union{ArfOrRef,ArbOrRef}) = false diff --git a/test/minmax.jl b/test/minmax.jl index f56a40b..362923d 100644 --- a/test/minmax.jl +++ b/test/minmax.jl @@ -110,17 +110,29 @@ @test !Base.isbadzero(min, zero(Mag)) @test !Base.isbadzero(min, zero(Arf)) @test !Base.isbadzero(min, zero(Arb)) + @test !Base.isbadzero(min, Arblib.radref(zero(Arb))) + @test !Base.isbadzero(min, Arblib.midref(zero(Arb))) + @test !Base.isbadzero(min, Arblib.realref(zero(Acb))) @test !Base.isbadzero(max, zero(Mag)) @test !Base.isbadzero(max, zero(Arf)) @test !Base.isbadzero(max, zero(Arb)) + @test !Base.isbadzero(max, Arblib.radref(zero(Arb))) + @test !Base.isbadzero(max, Arblib.midref(zero(Arb))) + @test !Base.isbadzero(max, Arblib.realref(zero(Acb))) @test !Base.isgoodzero(min, zero(Mag)) @test !Base.isgoodzero(min, zero(Arf)) @test !Base.isgoodzero(min, zero(Arb)) + @test !Base.isgoodzero(min, Arblib.radref(zero(Arb))) + @test !Base.isgoodzero(min, Arblib.midref(zero(Arb))) + @test !Base.isgoodzero(min, Arblib.realref(zero(Acb))) @test !Base.isgoodzero(max, zero(Mag)) @test !Base.isgoodzero(max, zero(Arf)) @test !Base.isgoodzero(max, zero(Arb)) + @test !Base.isgoodzero(max, Arblib.radref(zero(Arb))) + @test !Base.isgoodzero(max, Arblib.midref(zero(Arb))) + @test !Base.isgoodzero(max, Arblib.realref(zero(Acb))) end end From 5001142d8c2d3fd11a2c8d0b7c9667898d3a1141 Mon Sep 17 00:00:00 2001 From: Joel Dahne Date: Fri, 19 Apr 2024 12:30:28 +0200 Subject: [PATCH 3/6] =?UTF-8?q?Fix=20ambiguities=20for=20Base.:^(::Irratio?= =?UTF-8?q?nal{:=E2=84=AF},=20e::Series)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/poly.jl | 2 ++ test/series.jl | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/poly.jl b/src/poly.jl index c2d54ca..079537e 100644 --- a/src/poly.jl +++ b/src/poly.jl @@ -678,6 +678,8 @@ Base.:^(p::ArbSeries, e::Integer) = pow_arb_series!(zero(p), p, convert(Arb, e), Base.:^(p::AcbSeries, e::Integer) = pow_acb_series!(zero(p), p, convert(Acb, e), length(p)) Base.:^(p::ArbSeries, e::Rational) = pow_arb_series!(zero(p), p, convert(Arb, e), length(p)) Base.:^(p::AcbSeries, e::Rational) = pow_acb_series!(zero(p), p, convert(Acb, e), length(p)) +Base.:^(::Irrational{:ℯ}, e::ArbSeries) = exp(e) +Base.:^(::Irrational{:ℯ}, e::AcbSeries) = exp(e) ## ## Series methods diff --git a/test/series.jl b/test/series.jl index c5e038c..b79906e 100644 --- a/test/series.jl +++ b/test/series.jl @@ -322,6 +322,8 @@ @test 2^TSeries([1, 0]) == TSeries([2, 0]) @test (2 + im)^TSeries([1, 0]) == AcbSeries([2 + im, 0]) + @test isequal(ℯ^p, exp(p)) + @test precision(setprecision(p, 80)^setprecision(q, 90)) == 90 @test precision(setprecision(p, 80)^T(2)) == 80 end From 45d54eff16037f61e3a5a5efe76f4dfcce519774 Mon Sep 17 00:00:00 2001 From: Joel Dahne Date: Fri, 19 Apr 2024 18:46:23 +0200 Subject: [PATCH 4/6] Fix ambiguities for ^ mixing ArbSeries and AcbSeries --- src/poly.jl | 10 ++++++++++ test/series.jl | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/poly.jl b/src/poly.jl index 079537e..ea754ae 100644 --- a/src/poly.jl +++ b/src/poly.jl @@ -652,6 +652,16 @@ function Base.:^(p::AcbSeries, q::AcbSeries) deg = _degree(p, q) return pow_series!(AcbSeries(degree = deg, prec = _precision(p, q)), p, q, deg + 1) end +function Base.:^(p::ArbSeries, q::AcbSeries) + deg = _degree(p, q) + res = AcbSeries(p, degree = deg, prec = _precision(p, q)) + return pow_series!(res, res, q, deg + 1) +end +function Base.:^(p::AcbSeries, q::ArbSeries) + deg = _degree(p, q) + res = AcbSeries(q, degree = deg, prec = _precision(p, q)) + return pow_series!(res, p, res, deg + 1) +end Base.:^(p::ArbSeries, e::Real) = pow_arb_series!(zero(p), p, convert(Arb, e), length(p)) function Base.:^(p::ArbSeries, e::Number) diff --git a/test/series.jl b/test/series.jl index b79906e..88f5a14 100644 --- a/test/series.jl +++ b/test/series.jl @@ -306,7 +306,7 @@ p = TSeries([1, 2, 3]) q = TSeries([2, 3, 0]) - @test p^q == TSeries([1, 4, 16]) + @test p^q == ArbSeries([1, 2, 3])^q == p^ArbSeries([2, 3, 0]) == TSeries([1, 4, 16]) @test p^T(2) == p^Int(2) == From 3d70838bbbd9675a3bf544141ba35b3666ccff42 Mon Sep 17 00:00:00 2001 From: Joel Dahne Date: Fri, 19 Apr 2024 12:24:36 +0200 Subject: [PATCH 5/6] Add constructors for Mag, Arf and Acb constructors from complex Similar to the methods in Base they throw an error for non-real input. For Mag, Arf and Arb they are defined for Complex and for Arb also for Acb. --- src/constructors.jl | 4 ++++ src/setters.jl | 10 ++++++++++ test/constructors.jl | 16 ++++++++++++++++ test/setters.jl | 14 ++++++++++++++ 4 files changed, 44 insertions(+) diff --git a/src/constructors.jl b/src/constructors.jl index f922eb8..d3c3fd2 100644 --- a/src/constructors.jl +++ b/src/constructors.jl @@ -6,18 +6,22 @@ Mag(x) = set!(Mag(), x) # later. Mag(x::Union{MagRef,ArfRef}) = Mag(cstruct(x)) Mag(x, y) = set!(Mag(), x, y) +# disambiguation +Mag(x::Complex) = set!(Mag(), x) # Arf Arf(x; prec::Integer = _precision(x)) = set!(Arf(; prec), x) # disambiguation Arf(x::Arf; prec::Integer = precision(x)) = set!(Arf(; prec), x) Arf(x::Rational; prec::Integer = _precision(x)) = set!(Arf(; prec), x) +Arf(x::Complex; prec::Integer = _precision(x)) = set!(Arf(; prec), x) #Arb Arb(x; prec::Integer = _precision(x)) = set!(Arb(; prec), x) # disambiguation Arb(x::Arb; prec::Integer = precision(x)) = set!(Arb(; prec), x) Arb(x::Rational; prec::Integer = _precision(x)) = set!(Arb(; prec), x) +Arb(x::Complex; prec::Integer = _precision(x)) = set!(Arb(; prec), x) function Arb(str::AbstractString; prec::Integer = DEFAULT_PRECISION[]) res = Arb(; prec) diff --git a/src/setters.jl b/src/setters.jl index f697a3d..b376fb9 100644 --- a/src/setters.jl +++ b/src/setters.jl @@ -5,6 +5,8 @@ Base.setindex!(res::Union{MagLike,ArfLike,ArbLike,AcbLike}, x) = set!(res, x) set!(res::MagLike, x::Integer) = set!(res, convert(UInt, x)) set!(res::MagLike, ::Irrational{:π}) = const_pi!(res) set!(res::MagLike, x::Integer, y::Integer) = set_ui_2exp!(res, convert(UInt, x), y) +set!(res::MagLike, x::Complex) = + isreal(x) ? set!(res, real(x)) : throw(InexactError(:Mag, Mag, x)) # Arf function set!(res::ArfLike, x::UInt128) @@ -38,6 +40,9 @@ function set!( return res end +set!(res::ArfLike, x::Complex) = + isreal(x) ? set!(res, real(x)) : throw(InexactError(:Arf, Arf, x)) + # Arb function set!(res::ArbLike, x::Union{UInt128,Int128,MagLike,BigInt,BigFloat}) set!(midref(res), x) @@ -98,6 +103,11 @@ function set!(res::ArbLike, (a, b)::Tuple{<:Real,<:Real}; prec::Integer = precis return union!(res, a, b; prec) end +set!(res::ArbLike, x::AcbOrRef) = + is_real(x) ? set!(res, realref(x)) : throw(InexactError(:Arb, Arb, x)) +set!(res::ArbLike, x::Complex) = + isreal(x) ? set!(res, real(x)) : throw(InexactError(:Arb, Arb, x)) + # Acb function set!(res::AcbLike, x::Union{Real,MagLike,ArfLike,Tuple{<:Real,<:Real}}) set!(realref(res), x) diff --git a/test/constructors.jl b/test/constructors.jl index afd6e39..37b81ec 100644 --- a/test/constructors.jl +++ b/test/constructors.jl @@ -6,6 +6,10 @@ @test Mag(UInt64(1)) == Mag(1) == one(Mag) == one(Mag()) <= Mag(1.0) @test π < Float64(Mag(π)) < 3.15 @test Mag(3, 4) == Mag(3 * 2^4) + + # Check for ambiguities + @test Mag(1 + 0im) == Mag(1) + @test_throws InexactError Mag(1 + im) end @testset "Arf" begin @@ -23,6 +27,10 @@ @test precision(zero(Arf(prec = 80))) == 80 @test precision(one(Arf(prec = 80))) == 80 + + # Check for ambiguities + @test Arf(1 + 0im) == 1 + @test_throws InexactError Arf(1 + im) end @testset "Arb" begin @@ -58,6 +66,10 @@ @test precision(Arb(MathConstants.catalan, prec = 80)) == 80 @test precision(Arb(MathConstants.φ, prec = 80)) == 80 + @test Arb(Acb(1)) == 1 + @test precision(Arb(Acb(1, prec = 80))) == 80 + @test_throws InexactError Arb(Acb(1, 1)) + # setball @test isone(Arblib.setball(Arb, Arf(1), Mag(0))) @test isone(Arblib.setball(Arb, 1, 0)) @@ -71,6 +83,10 @@ @test precision(Arblib.setball(Arb, Arf(prec = 80), 0)) == 80 @test precision(Arblib.setball(Arb, Arf(prec = 90), 0, prec = 80)) == 80 + + # Check for ambiguities + @test Arb(1 + 0im) == 1 + @test_throws InexactError Arb(1 + im) end @testset "Acb" begin diff --git a/test/setters.jl b/test/setters.jl index 30a292e..41e9464 100644 --- a/test/setters.jl +++ b/test/setters.jl @@ -8,6 +8,10 @@ # Integer times power of 2 @test Arblib.set!(T(), 3, 4) == Arblib.set!(T(), 3 * 2^4) + + # Complex + @test Arblib.set!(T(), 1 + 0im) == Mag(1) + @test_throws InexactError Arblib.set!(T(), 1 + 1im) end @testset "$name" for (name, T) in [("Arf", Arf), ("ArfRef", () -> Arblib.midref(Arb()))] @@ -35,6 +39,10 @@ @test Arblib.set!(T(), 1 // BigInt(x)) == inv(Arf(x)) @test Arblib.set!(T(), BigInt(x) // (BigInt(x) + 1)) == Arf(x) / Arf(x + 1) end + + # Complex + @test Arblib.set!(T(), 1 + 0im) == 1 + @test_throws InexactError Arblib.set!(T(), 1 + 1im) end @testset "$name" for (name, T) in @@ -146,6 +154,12 @@ @test_throws ArgumentError Arblib.set!(T(), (2, 1)) @test_throws ArgumentError Arblib.set!(T(), (2.0, 1.0)) @test_throws ArgumentError Arblib.set!(T(), (2, 1.0)) + + # Complex + @test Arblib.set!(T(), Acb(1, 0)) == 1 + @test_throws InexactError Arblib.set!(T(), Acb(1, 1)) + @test Arblib.set!(T(), 1 + 0im) == 1 + @test_throws InexactError Arblib.set!(T(), 1 + 1im) end @testset "$name" for (name, T) in [ From 1edf991ee1e1abd1fb81e7a992b09039954c660e Mon Sep 17 00:00:00 2001 From: Joel Dahne Date: Fri, 19 Apr 2024 18:48:52 +0200 Subject: [PATCH 6/6] Aqua: Add excluded checks for ambiguities with comment about why --- test/runtests.jl | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/test/runtests.jl b/test/runtests.jl index d3d4183..dedc1f1 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -9,7 +9,23 @@ DocMeta.setdocmeta!(Arblib, :DocTestSetup, :(using Arblib); recursive = true) @testset "Arblib" begin doctest(Arblib) - Aqua.test_all(Arblib; ambiguities = (; broken = true)) + # Some methods are excluded from the check for ambiguities. There + # are two reasons for these exclusions, methods in Base we don't + # care about and false positives from Aqua. + + # The methods in Base that we don't care about are construction + # from AbstractChar or Base.TwicePrecision. Both of these have + # default constructors for Number types that clash with our catch + # all constructors. They do not seem important enough to warrant + # extra code for handling them. + + # One set of false positives are for Arf(::Rational) and + # Arb(::Rational). The other set is for + and * with mix of + # ArbSeries and AcbSeries. + Aqua.test_all( + Arblib, + ambiguities = (exclude = [Mag, Arf, Arb, Acb, ArbSeries, AcbSeries, +, *],), + ) include("ArbCall/runtests.jl")