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

Improve show and string #183

Merged
merged 9 commits into from
Mar 19, 2024
16 changes: 8 additions & 8 deletions src/poly.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand Down
160 changes: 119 additions & 41 deletions src/show.jl
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -16,35 +38,115 @@ 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,
)
cstr = ccall(@libflint(arf_get_str), Ptr{UInt8}, (Ref{arf_struct}, Int), Arf(x), digits)
str = unsafe_string(cstr)
ccall(@libflint(flint_free), Nothing, (Ptr{UInt8},), cstr)

return remove_trailing_zeros ? _remove_trailing_zeros(str) : str
end
Copy link
Owner

Choose a reason for hiding this comment

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

we could just replace this with

Suggested change
function Base.string(
x::MagOrRef;
digits::Integer = digits_prec(30),
remove_trailing_zeros::Bool = true,
)
cstr = ccall(@libflint(arf_get_str), Ptr{UInt8}, (Ref{arf_struct}, Int), Arf(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::MagOrRef;
digits::Integer = digits_prec(30),
remove_trailing_zeros::Bool = true,
)
return string(Arf(x); digits, remove_trailing_zeros)
end

I actually didn't know that one can pass kwargs without explicitly writing them! when was it introduced?!


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

Base.show(io::IO, x::ArfOrRef) = print(io, BigFloat(x))
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,
)
str = string(
realref(z);
digits,
more,
no_radius,
condense,
unicode,
remove_trailing_zeros,
)

if !iszero(imagref(z))
str *=
" + " *
string(
imagref(z);
digits,
more,
no_radius,
condense,
unicode,
remove_trailing_zeros,
) *
"im"
end
Copy link
Owner

Choose a reason for hiding this comment

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

Suggested change
str = string(
realref(z);
digits,
more,
no_radius,
condense,
unicode,
remove_trailing_zeros,
)
if !iszero(imagref(z))
str *=
" + " *
string(
imagref(z);
digits,
more,
no_radius,
condense,
unicode,
remove_trailing_zeros,
) *
"im"
end
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

seems a bit more readable for me


return str
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{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::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

Expand Down Expand Up @@ -82,30 +184,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)))"))
Expand Down
2 changes: 1 addition & 1 deletion test/examples.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
104 changes: 90 additions & 14 deletions test/show.jl
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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)"
Expand Down
Loading