diff --git a/Project.toml b/Project.toml index d2581a9..5707dd0 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Arblib" uuid = "fb37089c-8514-4489-9461-98f9c8763369" authors = ["Marek Kaluba ", "Sascha Timme ", "Joel Dahne "] -version = "1.1.0" +version = "1.2.0" [deps] FLINT_jll = "e134572f-a0d5-539d-bddf-3cad8db41a82" diff --git a/src/poly.jl b/src/poly.jl index e3a4f75..d8dce65 100644 --- a/src/poly.jl +++ b/src/poly.jl @@ -117,7 +117,7 @@ Here is an example were the leading coefficient is mutated so that the degree is lowered. ```jldoctest julia> p = ArbPoly([1, 2], prec = 64) # Polynomial of degree 1 -1.000000000000000000 + 2.000000000000000000⋅x +1.0 + 2.0⋅x julia> Arblib.zero!(Arblib.ref(p, 1)) # Set leading coefficient to 0 0 @@ -129,33 +129,33 @@ julia> length(p) # And the length as 2 2 julia> p # Printing gives weird results -1.000000000000000000 + +1.0 + julia> Arblib.normalise!(p) # Normalising the polynomial updates the degree -1.000000000000000000 +1.0 julia> Arblib.degree(p) # This is now correct 0 julia> p # And so is printing -1.000000000000000000 +1.0 ``` Here is an example when a coefficient above the degree is mutated. ```jldoctest julia> q = ArbSeries([1, 2, 0], prec = 64) # Series of degree 3 with leading coefficient zero -1.000000000000000000 + 2.000000000000000000⋅x + 𝒪(x^3) +1.0 + 2.0⋅x + 𝒪(x^3) julia> Arblib.one!(Arblib.ref(q, 2)) # Set the leading coefficient to 1 -1.000000000000000000 +1.0 julia> q # The leading coefficient is not picked up -1.000000000000000000 + 2.000000000000000000⋅x + 𝒪(x^3) +1.0 + 2.0⋅x + 𝒪(x^3) julia> Arblib.degree(q.poly) # The degree of the underlying polynomial is still 1 1 julia> Arblib.set_length!(q, 3) # After explicitly setting the length to 3 it is ok -1.000000000000000000 + 2.000000000000000000⋅x + 1.000000000000000000⋅x^2 + 𝒪(x^3) +1.0 + 2.0⋅x + 1.0⋅x^2 + 𝒪(x^3) ``` """ Base.@propagate_inbounds function ref(p::Union{ArbPoly,ArbSeries}, i::Integer) diff --git a/src/show.jl b/src/show.jl index 99cd80f..f2e977b 100644 --- a/src/show.jl +++ b/src/show.jl @@ -1,5 +1,27 @@ digits_prec(prec::Integer) = floor(Int, prec * (log(2) / log(10))) +function _remove_trailing_zeros(str::AbstractString) + if occursin('.', str) + if occursin('e', str) + # Numbers on the form xxx.yyy0ezzz + mantissa, exponent = split(str, 'e', limit = 2) + mantissa = rstrip(mantissa, '0') + if endswith(mantissa, '.') + mantissa *= '0' + end + str = mantissa * 'e' * exponent + else + # Numbers on the form xxx.yyy0 + str = rstrip(str, '0') + if endswith(str, '.') + str *= '0' + end + end + end + + return str +end + function _string(x::MagOrRef) Libc.flush_cstdio() Base.flush(stdout) @@ -16,35 +38,100 @@ function _string(x::MagOrRef) return read(out_rd, String) end -function Base.show(io::IO, x::MagOrRef) - if isdefined(Main, :IJulia) && Main.IJulia.inited - print(io, Float64(x)) - else - print(io, _string(x)) - end +function Base.string( + x::MagOrRef; + digits::Integer = digits_prec(30), + remove_trailing_zeros::Bool = true, +) + return string(Arf(x); digits, remove_trailing_zeros) end -Base.show(io::IO, x::ArfOrRef) = print(io, BigFloat(x)) +function Base.string( + x::ArfOrRef; + digits::Integer = digits_prec(precision(x)), + remove_trailing_zeros::Bool = true, +) + cstr = ccall(@libflint(arf_get_str), Ptr{UInt8}, (Ref{arf_struct}, Int), x, digits) + str = unsafe_string(cstr) + ccall(@libflint(flint_free), Nothing, (Ptr{UInt8},), cstr) + + return remove_trailing_zeros ? _remove_trailing_zeros(str) : str +end + +function Base.string( + x::ArbOrRef; + digits::Integer = digits_prec(precision(x)), + more::Bool = false, + no_radius::Bool = false, + condense::Integer = 0, + unicode::Bool = false, + remove_trailing_zeros::Bool = !no_radius, +) + flag = convert(UInt, more + 2no_radius + 16condense) -function Base.show(io::IO, x::ArbOrRef) cstr = ccall( @libflint(arb_get_str), Ptr{UInt8}, (Ref{arb_struct}, Int, UInt), x, - digits_prec(precision(x)), - UInt(0), + digits, + flag, ) - print(io, unsafe_string(cstr)) + str = unsafe_string(cstr) ccall(@libflint(flint_free), Nothing, (Ptr{UInt8},), cstr) + + if unicode + # Multiple patterns in same call requires Julia 1.7 + str = replace(replace(str, "+/-" => "±"), "..." => "…") + end + + if remove_trailing_zeros && !startswith(str, '[') + str = _remove_trailing_zeros(str) + end + + return str +end + +function Base.string( + z::AcbOrRef; + digits::Integer = digits_prec(precision(z)), + more::Bool = false, + no_radius::Bool = false, + condense::Integer = 0, + unicode::Bool = false, + remove_trailing_zeros::Bool = true, +) + kwargs = ( + :digits => digits, + :more => more, + :no_radius => no_radius, + :condense => condense, + :unicode => unicode, + :remove_trailing_zeros => remove_trailing_zeros, + ) + + str = string(realref(z); kwargs...) + if !iszero(imagref(z)) + str *= " + " * string(imagref(z); kwargs...) * "im" + end + + return str +end + +function Base.show(io::IO, x::Union{MagOrRef,ArfOrRef}) + if Base.get(io, :compact, false) + digits = min(6, digits_prec(precision(x))) + print(io, string(x; digits)) + else + print(io, string(x)) + end end -function Base.show(io::IO, x::AcbOrRef) - show(io, realref(x)) - if !iszero(imagref(x)) - print(io, " + ") - show(io, imagref(x)) - print(io, "im") +function Base.show(io::IO, x::Union{ArbOrRef,AcbOrRef}) + if Base.get(io, :compact, false) && rel_accuracy_bits(x) > 48 + print(io, string(x, condense = 2, unicode = true)) + else + print(io, string(x)) end end @@ -82,30 +169,6 @@ function Base.show(io::IO, poly::T) where {T<:Union{ArbPoly,ArbSeries,AcbPoly,Ac end end -for ArbT in (Mag, MagRef, Arf, ArfRef, Arb, ArbRef, Acb, AcbRef) - arbf = Symbol(cprefix(ArbT), :_, :print) - @eval begin - function string_nice( - x::$ArbT, - digits::Integer = digits_prec(precision(x)), - flags::UInt = UInt(0), - ) - Libc.flush_cstdio() - Base.flush(stdout) - original_stdout = stdout - out_rd, out_wr = redirect_stdout() - try - printn(x, digits, flags) - Libc.flush_cstdio() - finally - redirect_stdout(original_stdout) - close(out_wr) - end - return read(out_rd, String) - end - end -end - function load_string!(x::Union{MagLike,ArfLike,ArbLike}, str::AbstractString) res = load!(x, str) iszero(res) || throw(ArgumentError("could not load $str as $(string(typeof(x)))")) diff --git a/test/examples.jl b/test/examples.jl index 5ced99a..c23c7e2 100644 --- a/test/examples.jl +++ b/test/examples.jl @@ -36,7 +36,7 @@ y = zero(x) y = sin_naive!(y, x) str *= sprint(print, "Using $(lpad(prec, 5)) bits, sin(x) = ") - str *= sprint(println, Arblib.string_nice(y, 10)) + str *= sprint(println, string(y, digits = 10)) y < zero(y) && break prec *= 2 end diff --git a/test/show.jl b/test/show.jl index d8d79db..6a8f960 100644 --- a/test/show.jl +++ b/test/show.jl @@ -1,10 +1,80 @@ @testset "show" begin - Mag = Arblib.Mag @testset "string" begin - @test Arblib._string(Mag()) isa String - @test !isempty(Arblib._string(Mag(2.3))) - @test Arblib.string_nice(Arb()) isa String - @test Arblib.string_nice(Acb()) isa String + @test Arblib._string(Mag()) == "(0)" + @test Arblib._string(Mag(1)) == "(536870912 * 2^-29)" + + @test string(Mag()) == "0" + @test string(Mag(1)) == "1.0" + @test string(Mag(1), digits = 3, remove_trailing_zeros = false) == "1.00" + @test string(Mag(1), digits = 12, remove_trailing_zeros = false) == "1.00000000000" + @test string(Mag(10^15)) == "1.0e+15" + @test string(Mag(10^15), remove_trailing_zeros = false) == "1.00000000e+15" + @test string(Mag(π) * 10^10) == "3.14159267e+10" + + @test string(Arf()) == "0" + @test string(Arf(1)) == "1.0" + @test string(Arf(1), digits = 3, remove_trailing_zeros = false) == "1.00" + @test string(Arf(1), digits = 12, remove_trailing_zeros = false) == "1.00000000000" + @test string(Arf(1) / 3) == + "0.33333333333333333333333333333333333333333333333333333333333333333333333333333" + @test string(Arf(1) / 3, digits = 12) == "0.333333333333" + @test string(Arf(10)^100) == "1.0e+100" + @test string(Arf(10)^100, remove_trailing_zeros = false) == + "1.0000000000000000000000000000000000000000000000000000000000000000000000000000e+100" + @test string(Arf(1 // 3) * Arf(10)^81) == + "3.3333333333333333333333333333333333333333333333333333333333333333333333333334e+80" + + @test string(Arb()) == "0" + @test string(Arb(1)) == "1.0" + @test string(Arb(1, prec = 64), remove_trailing_zeros = false) == + "1.000000000000000000" + @test string(Arb(1), digits = 2, remove_trailing_zeros = false) == "1.0" + @test string(Arb(1), digits = 12, remove_trailing_zeros = false) == "1.00000000000" + @test string(Arb(10)^100) == "1.0e+100" + @test string(Arb(10)^100, remove_trailing_zeros = false) == + "1.0000000000000000000000000000000000000000000000000000000000000000000000000000e+100" + @test string(Arb(π)) == + "[3.1415926535897932384626433832795028841971693993751058209749445923078164062862 +/- 1.93e-77]" + @test string(Arb(π, prec = 64)) == "[3.141592653589793239 +/- 5.96e-19]" + @test string(Arb(π), digits = 2) == "[3.1 +/- 0.0416]" + @test string(Arb(π), digits = 12) == "[3.14159265359 +/- 2.07e-13]" + @test string(Arb(Arb(π, prec = 64), prec = 256)) == + "[3.141592653589793239 +/- 5.96e-19]" + @test string(Arb(Arb(π, prec = 64), prec = 256), more = true) == + "[3.1415926535897932385128089594061862044327426701784133911132812500000000000000 +/- 1.09e-19]" + @test string(Arb(π), no_radius = true) == + "3.1415926535897932384626433832795028841971693993751058209749445923078164062862" + @test string(Arb(π), condense = 5) == "[3.14159{...66 digits...}62862 +/- 1.93e-77]" + @test string(Arb(π), unicode = true) == + "[3.1415926535897932384626433832795028841971693993751058209749445923078164062862 ± 1.93e-77]" + @test string(Arb(π), condense = 5, unicode = true) == + "[3.14159{…66 digits…}62862 ± 1.93e-77]" + + @test string(Acb()) == "0" + @test string(Acb(1)) == "1.0" + @test string(Acb(0, 1)) == "0 + 1.0im" + @test string(Acb(1, 1)) == "1.0 + 1.0im" + @test string(Acb(1, 1), remove_trailing_zeros = false) == + "1.0000000000000000000000000000000000000000000000000000000000000000000000000000 + 1.0000000000000000000000000000000000000000000000000000000000000000000000000000im" + @test string(Acb(π, ℯ)) == + "[3.1415926535897932384626433832795028841971693993751058209749445923078164062862 +/- 1.93e-77] + [2.7182818284590452353602874713526624977572470936999595749669676277240766303535 +/- 5.46e-77]im" + @test string(Acb(π, ℯ, prec = 64)) == + "[3.141592653589793239 +/- 5.96e-19] + [2.718281828459045235 +/- 4.29e-19]im" + @test string(Acb(π, ℯ), digits = 2) == "[3.1 +/- 0.0416] + [2.7 +/- 0.0183]im" + @test string(Acb(π, ℯ), digits = 12) == + "[3.14159265359 +/- 2.07e-13] + [2.71828182846 +/- 9.55e-13]im" + @test string(Acb(Acb(π, ℯ, prec = 64), prec = 256)) == + "[3.141592653589793239 +/- 5.96e-19] + [2.718281828459045235 +/- 4.29e-19]im" + @test string(Acb(Acb(π, ℯ, prec = 64), prec = 256), more = true) == + "[3.1415926535897932385128089594061862044327426701784133911132812500000000000000 +/- 1.09e-19] + [2.7182818284590452352113276734968394521274603903293609619140625000000000000000 +/- 2.17e-19]im" + @test string(Acb(π, ℯ), no_radius = true) == + "3.1415926535897932384626433832795028841971693993751058209749445923078164062862 + 2.7182818284590452353602874713526624977572470936999595749669676277240766303535im" + @test string(Acb(π, ℯ), condense = 5) == + "[3.14159{...66 digits...}62862 +/- 1.93e-77] + [2.71828{...66 digits...}03535 +/- 5.46e-77]im" + @test string(Acb(π, ℯ), unicode = true) == + "[3.1415926535897932384626433832795028841971693993751058209749445923078164062862 ± 1.93e-77] + [2.7182818284590452353602874713526624977572470936999595749669676277240766303535 ± 5.46e-77]im" + @test string(Acb(π, ℯ), condense = 5, unicode = true) == + "[3.14159{…66 digits…}62862 ± 1.93e-77] + [2.71828{…66 digits…}03535 ± 5.46e-77]im" end @testset "dump" begin @@ -18,18 +88,24 @@ end @testset "show" begin + @test repr(Mag(1), context = IOContext(stdout, :compact => true)) == "1.0" + @test repr(Arf(1), context = IOContext(stdout, :compact => true)) == "1.0" + @test repr(Arb(π), context = IOContext(stdout, :compact => true)) == + "[3.14{…72 digits…}62 ± 1.93e-77]" + @test repr(Acb(π, ℯ), context = IOContext(stdout, :compact => true)) == + "[3.14{…72 digits…}62 ± 1.93e-77] + [2.71{…72 digits…}35 ± 5.46e-77]im" + prec = 32 - P = ArbPoly(Arb[1, 2, 0, π], prec = prec) - @test "$P" == "1.00000000 + 2.00000000⋅x + [3.14159265 +/- 3.59e-9]⋅x^3" - P = AcbPoly([Acb[1, 2, 0, π]; Acb(1, 1)], prec = prec) - @test "$P" == - "1.00000000 + 2.00000000⋅x + [3.14159265 +/- 3.59e-9]⋅x^3 + (1.00000000 + 1.00000000im)⋅x^4" - P = ArbSeries(Arb[1, 2, 0, π], degree = 4, prec = prec) - @test "$P" == "1.00000000 + 2.00000000⋅x + [3.14159265 +/- 3.59e-9]⋅x^3 + 𝒪(x^5)" - P = AcbSeries([Acb[1, 2, 0, π]; Acb(1, 1)], degree = 5, prec = prec) + P = ArbPoly(Arb[1, 2, 0, π]; prec) + @test "$P" == "1.0 + 2.0⋅x + [3.14159265 +/- 3.59e-9]⋅x^3" + P = AcbPoly([Acb[1, 2, 0, π]; Acb(1, 1)]; prec) + @test "$P" == "1.0 + 2.0⋅x + [3.14159265 +/- 3.59e-9]⋅x^3 + (1.0 + 1.0im)⋅x^4" + P = ArbSeries(Arb[1, 2, 0, π], degree = 4; prec) + @test "$P" == "1.0 + 2.0⋅x + [3.14159265 +/- 3.59e-9]⋅x^3 + 𝒪(x^5)" + P = AcbSeries([Acb[1, 2, 0, π]; Acb(1, 1)], degree = 5; prec) @test "$P" == - "1.00000000 + 2.00000000⋅x + [3.14159265 +/- 3.59e-9]⋅x^3 + (1.00000000 + 1.00000000im)⋅x^4 + 𝒪(x^6)" + "1.0 + 2.0⋅x + [3.14159265 +/- 3.59e-9]⋅x^3 + (1.0 + 1.0im)⋅x^4 + 𝒪(x^6)" @test "$(ArbPoly())" == "$(AcbPoly())" == "0" @test "$(ArbSeries())" == "$(AcbSeries())" == "𝒪(x)"