diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index 537896ff..990072bf 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -18,7 +18,7 @@ jobs: - uses: actions/checkout@v2 - name: Install JuliaFormatter and format run: | - julia -e 'using Pkg; Pkg.add(PackageSpec(name="JuliaFormatter", version="0.22.10"))' + julia -e 'using Pkg; Pkg.add(PackageSpec(name="JuliaFormatter", version="1.0.9"))' julia -e 'using JuliaFormatter; format(["./src", "./test"], verbose=true)' - name: Format check run: | diff --git a/Project.toml b/Project.toml index 48298903..e52407e0 100644 --- a/Project.toml +++ b/Project.toml @@ -1,13 +1,14 @@ name = "Arblib" uuid = "fb37089c-8514-4489-9461-98f9c8763369" authors = ["Marek Kaluba ", "Sascha Timme ", "Joel Dahne "] -version = "0.8.0" +version = "0.8.1" [deps] Arb_jll = "d9960996-1013-53c9-9ba4-74a4155039c3" FLINT_jll = "e134572f-a0d5-539d-bddf-3cad8db41a82" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" +Serialization = "9e88b42a-f829-5b0c-bbe9-9e923198166b" SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/src/Arblib.jl b/src/Arblib.jl index 9fce22ea..98161905 100644 --- a/src/Arblib.jl +++ b/src/Arblib.jl @@ -2,6 +2,7 @@ module Arblib using Arb_jll import LinearAlgebra +import Serialization import SpecialFunctions # So that the parsed contains method extends the base function @@ -52,8 +53,11 @@ function flint_set_num_threads(a::Integer) end include("arb_types.jl") +include("fmpz.jl") include("rounding.jl") include("types.jl") +include("hash.jl") +include("serialize.jl") include("ArbCall/ArbCall.jl") import .ArbCall: @arbcall_str, @arbfpwrapcall_str diff --git a/src/fmpz.jl b/src/fmpz.jl new file mode 100644 index 00000000..14036f11 --- /dev/null +++ b/src/fmpz.jl @@ -0,0 +1,25 @@ +""" + fmpz_struct() + +Low level wrapper of `fmpz_t`. Not part of the Arblib interface but +only used internally by a few methods for conversion from `fmpz_t` to +`BigInt`. +""" +mutable struct fmpz_struct + d::Int + + function fmpz_struct() + z = new() + ccall(@libflint(fmpz_init), Nothing, (Ref{fmpz_struct},), z) + finalizer(fmpz_clear!, z) + return z + end +end + +fmpz_clear!(x::fmpz_struct) = ccall(@libflint(fmpz_clear), Nothing, (Ref{fmpz_struct},), x) + +function Base.BigInt(x::fmpz_struct) + res = BigInt() + ccall(@libflint(fmpz_get_mpz), Nothing, (Ref{BigInt}, Ref{fmpz_struct}), res, x) + return res +end diff --git a/src/hash.jl b/src/hash.jl new file mode 100644 index 00000000..3efa5ffb --- /dev/null +++ b/src/hash.jl @@ -0,0 +1,98 @@ +#= +The Base implementation of hash(::Real) is based on Base.decompose +to guarantee that all numbers that compare equal are given the same +hash. We implement Base.decompose for Mag and Arf. + +It should return a, not necessarily canonical, decomposition of +rational values as `num*2^pow/den`. For Mag and Arf we always have den += 1 and we hence only need to find num and pow, corresponding to the +mantissa end exponent. For Arf this is straight forward using +arf_get_fmpz_2exp. For Mag the mantissa is stored directly in the +struct as a UInt and the exponent as a fmpz. +=# +function Base.decompose(x::Union{mag_struct,Ptr{mag_struct}})::Tuple{UInt,BigInt,Int} + isinf(x) && return 1, 0, 0 + + if x isa Ptr{mag_struct} + x = unsafe_load(x) + end + + pow = BigInt() + ccall(@libflint(fmpz_get_mpz), Nothing, (Ref{BigInt}, Ref{UInt}), pow, x.exponent) + # There is an implicit factor 2^30 for the exponent, coming from + # the number of bits of the mantissa + pow -= 30 + + return x.mantissa, pow, 1 +end + +function Base.decompose(x::Union{arf_struct,Ptr{arf_struct}})::Tuple{BigInt,BigInt,Int} + isnan(x) && return 0, 0, 0 + isinf(x) && return ifelse(x < 0, -1, 1), 0, 0 + + num = fmpz_struct() + pow = fmpz_struct() + ccall( + @libarb(arf_get_fmpz_2exp), + Cvoid, + (Ref{fmpz_struct}, Ref{fmpz_struct}, Ref{arf_struct}), + num, + pow, + x, + ) + + return BigInt(num), BigInt(pow), 1 +end + +Base.decompose(x::Union{MagOrRef,ArfOrRef}) = Base.decompose(cstruct(x)) + +# Hashes of structs are computed using the method for the wrapping +# type +Base.hash(x::mag_struct, h::UInt) = hash(Mag(x), h) +Base.hash(x::arf_struct, h::UInt) = hash(Arf(x), h) +Base.hash(x::arb_struct, h::UInt) = hash(Arb(x), h) +Base.hash(x::acb_struct, h::UInt) = hash(Acb(x), h) + +# Hashes of Mag and Arf are computed using the Base implementation +# which used Base.decompose defined above. + +function Base.hash(x::ArbLike, h::UInt) + # If the radius is zero we compute the hash using only the + # midpoint, so that we get identical hashes as for the + # corresponding Arf + if !isexact(x) + h = hash(Arblib.radref(x), h) + end + return hash(Arblib.midref(x), h) +end + +function Base.hash(z::AcbLike, h::UInt) + # Same as for Complex{T} + hash(realref(z), h ⊻ hash(imagref(z), Base.h_imag) ⊻ Base.hash_0_imag) +end + +# Compare with Base.h_imag +if UInt === UInt64 + const h_poly = 0xfd6de1a6c0e66975 +else + const h_poly = 0xa0617887 +end +# arb_poly_struct and acb_poly_struct use default hash implementation, +# this is okay since they don't implement an isequal method. +function Base.hash(p::Union{ArbPoly,AcbPoly}, h::UInt) + h = hash(h_poly, h) + for i = 0:degree(p) + h = hash(ref(p, i), h) + end + return h +end + +function Base.hash(p::Union{ArbSeries,AcbSeries}, h::UInt) + # Conversion of Number to series gives a degree 0 series, we want + # the hashes to match in this case + degree(p) == 0 && return hash(ref(p, 0), h) + + hash(p.poly, hash(degree(p), h)) +end + +# Vectors and Matrices have an implementation in Base that works well diff --git a/src/matrix.jl b/src/matrix.jl index 0308b7ff..1e69ad74 100644 --- a/src/matrix.jl +++ b/src/matrix.jl @@ -106,7 +106,7 @@ end # General constructor for T in [:ArbMatrix, :ArbRefMatrix, :AcbMatrix, :AcbRefMatrix] - @eval function $T(A::AbstractMatrix; prec::Integer = _precision(first(A))) + @eval function $T(A::AbstractMatrix; prec::Integer = _precision(A)) B = $T(size(A)...; prec = prec) # ensure to handle all kind of indices ax1, ax2 = axes(A) @@ -116,7 +116,7 @@ for T in [:ArbMatrix, :ArbRefMatrix, :AcbMatrix, :AcbRefMatrix] return B end - @eval function $T(v::AbstractVector; prec::Integer = _precision(first(v))) + @eval function $T(v::AbstractVector; prec::Integer = _precision(v)) A = $T(length(v), 1; prec = prec) for (i, vᵢ) in enumerate(v) A[i, 1] = vᵢ diff --git a/src/poly.jl b/src/poly.jl index e7a24f11..db510f88 100644 --- a/src/poly.jl +++ b/src/poly.jl @@ -129,7 +129,7 @@ for (TPoly, TSeries) in [(:ArbPoly, :ArbSeries), (:AcbPoly, :AcbSeries)] @eval function $TPoly( coeffs::Union{Tuple,AbstractVector}; - prec::Integer = _precision(first(coeffs)), + prec::Integer = _precision(coeffs), ) p = fit_length!($TPoly(; prec), length(coeffs)) @inbounds for (i, c) in enumerate(coeffs) @@ -142,7 +142,7 @@ for (TPoly, TSeries) in [(:ArbPoly, :ArbSeries), (:AcbPoly, :AcbSeries)] # with two elements. This would for example be used when # constructing a polynomial with a constant plus x, e.g # ArbPoly((x, 1)) - @eval function $TPoly(coeffs::Tuple{Any,Any}; prec::Integer = _precision(first(coeffs))) + @eval function $TPoly(coeffs::Tuple{Any,Any}; prec::Integer = _precision(coeffs)) p = fit_length!($TPoly(; prec), length(coeffs)) @inbounds p[0] = coeffs[1] @inbounds p[1] = coeffs[2] @@ -175,8 +175,8 @@ for (TSeries, TPoly) in [(:ArbSeries, :ArbPoly), (:AcbSeries, :AcbPoly)] @eval function $TSeries( coeffs::Union{Tuple,AbstractVector}; - degree::Integer = length(coeffs) - 1, - prec::Integer = _precision(first(coeffs)), + degree::Integer = max(length(coeffs) - 1, 0), + prec::Integer = _precision(coeffs), ) p = $TSeries(; degree, prec) @inbounds for (i, c) in enumerate(coeffs) @@ -193,7 +193,7 @@ for (TSeries, TPoly) in [(:ArbSeries, :ArbPoly), (:AcbSeries, :AcbPoly)] @eval function $TSeries( coeffs::Tuple{Any,Any}; degree::Integer = length(coeffs) - 1, - prec::Integer = _precision(first(coeffs)), + prec::Integer = _precision(coeffs), ) p = $TSeries(; degree, prec) @inbounds p[0] = coeffs[1] diff --git a/src/precision.jl b/src/precision.jl index a9451f98..b55abf67 100644 --- a/src/precision.jl +++ b/src/precision.jl @@ -22,6 +22,8 @@ Base.precision(x::Union{ArbSeries,AcbSeries}) = precision(x.poly) @inline _precision(x::Union{ArbTypes,BigFloat}) = precision(x) @inline _precision(z::Complex) = max(_precision(real(z)), _precision(imag(z))) +@inline _precision(v::Union{Tuple,AbstractVector}) = + isempty(v) ? DEFAULT_PRECISION[] : _precision(first(v)) @inline _precision( a::Union{ArbTypes,BigFloat,Complex{<:Union{ArbTypes,BigFloat}}}, b::Union{ArbTypes,BigFloat,Complex{<:Union{ArbTypes,BigFloat}}}, diff --git a/src/serialize.jl b/src/serialize.jl new file mode 100644 index 00000000..51302e33 --- /dev/null +++ b/src/serialize.jl @@ -0,0 +1,125 @@ +# Compare with BigInt in julia/stdlib/v1.7/Serialization/src/Serialization.jl +function Serialization.serialize( + s::Serialization.AbstractSerializer, + x::Union{mag_struct,arf_struct,arb_struct}, +) + Serialization.serialize_type(s, typeof(x)) + Serialization.serialize(s, dump_string(x)) +end + +function Serialization.serialize(s::Serialization.AbstractSerializer, x::acb_struct) + Serialization.serialize_type(s, typeof(x)) + str = dump_string(Arblib.realref(x)) * " " * dump_string(Arblib.imagref(x)) + Serialization.serialize(s, str) +end + +function Serialization.serialize( + s::Serialization.AbstractSerializer, + v::Union{arb_vec_struct,acb_vec_struct}, +) + Serialization.serialize_type(s, typeof(v)) + Serialization.serialize(s, size(v)[1]) + for i = 1:size(v)[1] + Serialization.serialize(s, unsafe_load(v[i])) + end +end + +function Serialization.serialize( + s::Serialization.AbstractSerializer, + v::Union{arb_mat_struct,acb_mat_struct}, +) + Serialization.serialize_type(s, typeof(v)) + Serialization.serialize(s, size(v)) + for i = 1:size(v)[1] + for j = 1:size(v)[2] + Serialization.serialize(s, unsafe_load(v[i, j])) + end + end +end + +function Serialization.serialize( + s::Serialization.AbstractSerializer, + p::Union{arb_poly_struct,acb_poly_struct}, +) + Serialization.serialize_type(s, typeof(p)) + Serialization.serialize(s, length(p)) + T = p isa arb_poly_struct ? arb_struct : acb_struct + for i = 0:length(p)-1 + Serialization.serialize(s, unsafe_load(p.coeffs + i * sizeof(T))) + end +end + + +Serialization.deserialize( + s::Serialization.AbstractSerializer, + T::Type{<:Union{mag_struct,arf_struct,arb_struct}}, +) = Arblib.load_string!(T(), Serialization.deserialize(s)) + +function Serialization.deserialize(s::Serialization.AbstractSerializer, T::Type{acb_struct}) + str = Serialization.deserialize(s) + # Three spaces in the real part, so we are looking for + spaces = findall(" ", str) + @assert length(spaces) == 7 + + real_str = str[1:spaces[4].start-1] + imag_str = str[spaces[4].stop+1:end] + + res = acb_struct() + Arblib.load_string!(Arblib.realref(res), real_str) + Arblib.load_string!(Arblib.imagref(res), imag_str) + return res +end + +function Serialization.deserialize( + s::Serialization.AbstractSerializer, + T::Type{<:Union{arb_vec_struct,acb_vec_struct}}, +) + n = Serialization.deserialize(s) + res = T(n) + for i = 1:n + res[i] = Serialization.deserialize(s) + end + return res +end + +function Serialization.deserialize( + s::Serialization.AbstractSerializer, + T::Type{<:Union{arb_mat_struct,acb_mat_struct}}, +) + r, c = Serialization.deserialize(s) + res = T(r, c) + for i = 1:r + for j = 1:c + res[i, j] = Serialization.deserialize(s) + end + end + return res +end + +function Serialization.deserialize( + s::Serialization.AbstractSerializer, + T::Type{<:Union{arb_poly_struct,acb_poly_struct}}, +) + n = Serialization.deserialize(s) + res = T() + for i = 0:n-1 + set_coeff!(res, i, Serialization.deserialize(s)) + end + return res +end + +# For series we want to make sure that we have allocated the right +# number of coefficients +function Serialization.deserialize( + s::Serialization.AbstractSerializer, + T::Type{<:Union{ArbSeries,AcbSeries}}, +) + # This uses the base implementation of serialize so if that + # changes this might need to be updated + poly = Serialization.deserialize(s) + degree = Serialization.deserialize(s) + # The series constructor assures us that the number of allocated + # coefficients is determined from the degree + res = T(poly; degree) # TODO: Here an inplace constructor could be used + return res +end diff --git a/src/show.jl b/src/show.jl index d3d3533b..97bbc979 100644 --- a/src/show.jl +++ b/src/show.jl @@ -106,21 +106,17 @@ for ArbT in (Mag, MagRef, Arf, ArfRef, Arb, ArbRef, Acb, AcbRef) end end -for ArbT in (Mag, MagRef, Arf, ArfRef, Arb, ArbRef) - @eval begin - function load_string!(x::$ArbT, str::AbstractString) - res = load!(x, str) - iszero(res) || throw(ArgumentError("could not load $str as " * $(string(ArbT)))) - return x - 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)))")) + return x +end - function dump_string(x::$ArbT) - char_ptr = dump(x) - str = unsafe_string(char_ptr) - ccall(@libflint(flint_free), Cvoid, (Cstring,), char_ptr) - return str - end - end +function dump_string(x::Union{MagLike,ArfLike,ArbLike}) + char_ptr = dump(x) + str = unsafe_string(char_ptr) + ccall(@libflint(flint_free), Cvoid, (Cstring,), char_ptr) + return str end for T in [ diff --git a/src/vector.jl b/src/vector.jl index dece1e0d..67ec9818 100644 --- a/src/vector.jl +++ b/src/vector.jl @@ -94,7 +94,7 @@ const Vectors = Union{ArbVector,ArbRefVector,AcbVector,AcbRefVector} # General constructor for T in [:ArbVector, :ArbRefVector, :AcbVector, :AcbRefVector] - @eval function $T(v::AbstractVector; prec::Integer = _precision(first(v))) + @eval function $T(v::AbstractVector; prec::Integer = _precision(v)) V = $T(length(v); prec = prec) @inbounds for (i, vᵢ) in enumerate(v) V[i] = vᵢ diff --git a/test/hash.jl b/test/hash.jl new file mode 100644 index 00000000..c8491059 --- /dev/null +++ b/test/hash.jl @@ -0,0 +1,94 @@ +@testset "Hash" begin + @testset "decompose" begin + + end + + @testset "Mag, Arf, Arb, Acb" begin + Ts1 = (Mag, Arf, Arb, Acb) + + @test all(isequal(hash(0)), hash.(zero.(Ts1))) + @test all(isequal(hash(1)), hash.(one.(Ts1))) + @test all(isequal(hash(2)), hash.(convert.(Ts1, 2))) + @test all(isequal(hash(Mag(0.1))), hash.(convert.(Ts1, Mag(0.1)))) + @test all( + isequal(hash(Mag(typemax(UInt), typemax(Int)))), + hash.(convert.(Ts1, Mag(typemax(UInt), typemax(Int)))), + ) + @test all(isequal(hash(Inf)), hash.(convert.(Ts1, Inf))) + + Ts2 = (Arf, Arb, Acb) + @test all(isequal(hash(-1)), hash.(convert.(Ts2, -1))) + @test all(isequal(hash(-Inf)), hash.(convert.(Ts2, -Inf))) + @test all(isequal(hash(NaN)), hash.(convert.(Ts2, NaN))) + + @test hash(0 + im) == hash(Acb(0, 1)) + @test hash(1 + im) == hash(Acb(1, 1)) + @test hash(0 + Inf * im) == hash(Acb(0, Inf)) + @test hash(0 + NaN * im) == hash(Acb(0, NaN)) + + @test hash(Arb(π)) == hash(Acb(π)) != hash(midpoint(Arb(π))) + @test hash(Acb(π)) != hash(Acb(0, π)) + end + + @testset "mag_struct, arf_struct, arb_struct, acb_struct" begin + # Internally uses the hashing for Mag, Arf, Arb and Acb so we + # only test that it calls that correctly + @test hash(Arblib.mag_struct()) == hash(zero(Mag)) + @test hash(Arblib.arf_struct()) == hash(zero(Arf)) + @test hash(Arblib.arb_struct()) == hash(zero(Arb)) + @test hash(Arblib.acb_struct()) == hash(zero(Acb)) + end + + @testset "Poly, Series" begin + # Poly + for T in (ArbPoly, AcbPoly) + @test hash(T()) == hash(T()) == hash(T(prec = 80)) + @test hash(T(1)) == hash(T(1)) == hash(T(1, prec = 80)) + @test hash(T([1, 2, 3])) == hash(T([1, 2, 3])) == hash(T([1, 2, 3], prec = 80)) + + @test hash(T([1, 2, 3])) != hash(T([1, 2, 4])) + @test hash(T(0)) != hash(T(1)) + end + + # Series + for T in (ArbSeries, AcbSeries) + @test hash(T()) == hash(T()) == hash(T(prec = 80)) + @test hash(T(1)) == hash(T(1)) == hash(T(1, prec = 80)) + @test hash(T([1, 2, 3])) == hash(T([1, 2, 3])) == hash(T([1, 2, 3], prec = 80)) + + @test hash(T([1, 2, 3])) != hash(T([1, 2, 4])) + @test hash(T(0)) != hash(T(1)) + + # Degree 0 series should have same hash as the only coefficient + @test hash(T()) == hash(0) + @test hash(T(1)) == hash(1) + @test hash(T(π)) == hash(T(π)) + end + end + + @testset "Vector, Matrix" begin + # Vector + for T in (ArbVector, AcbVector) + @test hash(T([], prec = 256)) == + hash(T([], prec = 256)) == + hash(T([], prec = 80)) # FIXME + @test hash(T([1])) == hash(T([1])) == hash(T([1], prec = 80)) + @test hash(T([1, 2, 3])) == hash(T([1, 2, 3])) == hash(T([1, 2, 3], prec = 80)) + + @test hash(T([1, 2, 3])) != hash(T([1, 2, 4])) + @test hash(T([0])) != hash(T([1])) + end + + # Matrix + for T in (ArbMatrix, AcbMatrix) + @test hash(T([], prec = 256)) == + hash(T([], prec = 256)) == + hash(T([], prec = 80)) # FIXME + @test hash(T([1])) == hash(T([1])) == hash(T([1], prec = 80)) + @test hash(T([1, 2, 3])) == hash(T([1, 2, 3])) == hash(T([1, 2, 3], prec = 80)) + + @test hash(T([1, 2, 3])) != hash(T([1, 2, 4])) + @test hash(T([0])) != hash(T([1])) + end + end +end diff --git a/test/matrix.jl b/test/matrix.jl index e2f71a9c..63761417 100644 --- a/test/matrix.jl +++ b/test/matrix.jl @@ -5,6 +5,9 @@ (AcbRefMatrix, Acb, AcbRef), ] @testset "Basic" begin + @test isempty(TMat([])) + @test isempty(TMat([])) + M = TMat(4, 4, prec = 128) @test size(M) == (4, 4) @test precision(M) == 128 diff --git a/test/poly.jl b/test/poly.jl index 3a2a1984..b6ca8edd 100644 --- a/test/poly.jl +++ b/test/poly.jl @@ -57,6 +57,7 @@ @testset "Constructors" begin @test TPoly() == + TPoly([]) == TPoly(T(0)) == TPoly((0,)) == TPoly(T[0]) == diff --git a/test/runtests.jl b/test/runtests.jl index c9583621..9ddae11e 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,12 +1,14 @@ ENV["NEMO_THREADED"] = 1 -using Arblib, Test, LinearAlgebra, Random, SpecialFunctions +using Arblib, Test, LinearAlgebra, Random, Serialization, SpecialFunctions @testset "Arblib" begin include("ArbCall/runtests.jl") include("arb_types.jl") include("types.jl") + include("hash.jl") + include("serialize.jl") include("precision.jl") include("manual_overrides.jl") include("setters.jl") diff --git a/test/serialize.jl b/test/serialize.jl new file mode 100644 index 00000000..48a2276b --- /dev/null +++ b/test/serialize.jl @@ -0,0 +1,360 @@ +@testset "Serialize" begin + # Tests are organized similar to in Serialization package + + function create_serialization_stream(f::Function) + s = IOBuffer() + f(s) + close(s) + end + + isequal_and_prec_equal(x, y) = isequal(x, y) && precision(x) == precision(y) + + # Mag + create_serialization_stream() do s + for x in (Mag(), Mag(1), Mag(π), Mag(typemax(UInt), typemax(Int))) + Serialization.serialize(s, x) + Arblib.zero!(x) # To catch aliasing issues + end + seek(s, 0) + @test isequal(Serialization.deserialize(s), Mag()) + @test isequal(Serialization.deserialize(s), Mag(1)) + @test isequal(Serialization.deserialize(s), Mag(π)) + @test isequal(Serialization.deserialize(s), Mag(typemax(UInt), typemax(Int))) + end + + # Arf + create_serialization_stream() do s + for x in ( + Arf(), + Arf(1), + Arf(-Inf), + Arf(Inf), + Arf(NaN), + Arf(1 // 3, prec = 64), + Arf(1 // 3, prec = 256), + Arf(1 // 3, prec = 512), + ) + Serialization.serialize(s, x) + Arblib.zero!(x) # To catch aliasing issues + + end + seek(s, 0) + @test isequal_and_prec_equal(Serialization.deserialize(s), Arf()) + @test isequal_and_prec_equal(Serialization.deserialize(s), Arf(1)) + @test isequal_and_prec_equal(Serialization.deserialize(s), Arf(-Inf)) + @test isequal_and_prec_equal(Serialization.deserialize(s), Arf(Inf)) + @test isequal_and_prec_equal(Serialization.deserialize(s), Arf(NaN)) + @test isequal_and_prec_equal(Serialization.deserialize(s), Arf(1 // 3, prec = 64)) + @test isequal_and_prec_equal(Serialization.deserialize(s), Arf(1 // 3, prec = 256)) + @test isequal_and_prec_equal(Serialization.deserialize(s), Arf(1 // 3, prec = 512)) + end + + # Arb + create_serialization_stream() do s + for x in ( + Arb(), + Arb(1), + Arb(-Inf), + Arb(Inf), + Arb(NaN), + Arb(π, prec = 64), + Arb(π, prec = 256), + Arb(π, prec = 512), + ) + + Serialization.serialize(s, x) + Arblib.zero!(x) # To catch aliasing issues + end + seek(s, 0) + @test isequal_and_prec_equal(Serialization.deserialize(s), Arb()) + @test isequal_and_prec_equal(Serialization.deserialize(s), Arb(1)) + @test isequal_and_prec_equal(Serialization.deserialize(s), Arb(-Inf)) + @test isequal_and_prec_equal(Serialization.deserialize(s), Arb(Inf)) + @test isequal_and_prec_equal(Serialization.deserialize(s), Arb(NaN)) + @test isequal_and_prec_equal(Serialization.deserialize(s), Arb(π, prec = 64)) + @test isequal_and_prec_equal(Serialization.deserialize(s), Arb(π, prec = 256)) + @test isequal_and_prec_equal(Serialization.deserialize(s), Arb(π, prec = 512)) + end + + # Acb + create_serialization_stream() do s + for x in ( + Acb(), + Acb(1), + Acb(0, 1), + Acb(1, 2), + Acb(-Inf, Inf), + Acb(Inf, -Inf), + Acb(NaN), + Acb(0, NaN), + Acb(π, ℯ, prec = 64), + Acb(π, ℯ, prec = 256), + Acb(π, ℯ, prec = 512), + ) + Serialization.serialize(s, x) + Arblib.zero!(x) # To catch aliasing issues + end + seek(s, 0) + @test isequal_and_prec_equal(Serialization.deserialize(s), Acb()) + @test isequal_and_prec_equal(Serialization.deserialize(s), Acb(1)) + @test isequal_and_prec_equal(Serialization.deserialize(s), Acb(0, 1)) + @test isequal_and_prec_equal(Serialization.deserialize(s), Acb(1, 2)) + @test isequal_and_prec_equal(Serialization.deserialize(s), Acb(-Inf, Inf)) + @test isequal_and_prec_equal(Serialization.deserialize(s), Acb(Inf, -Inf)) + @test isequal_and_prec_equal(Serialization.deserialize(s), Acb(NaN)) + @test isequal_and_prec_equal(Serialization.deserialize(s), Acb(0, NaN)) + @test isequal_and_prec_equal(Serialization.deserialize(s), Acb(π, ℯ, prec = 64)) + @test isequal_and_prec_equal(Serialization.deserialize(s), Acb(π, ℯ, prec = 256)) + @test isequal_and_prec_equal(Serialization.deserialize(s), Acb(π, ℯ, prec = 512)) + end + + # ArbVector + create_serialization_stream() do s + for v in ( + ArbVector([]), + ArbVector(Arb[1]), + ArbVector(Arb[1, 2, 3, 4]), + ArbVector(Arb[1//3, π, ℯ, NaN]), + ) + Serialization.serialize(s, v) + Arblib.zero!(v) # To catch aliasing issues + end + seek(s, 0) + @test isequal_and_prec_equal(Serialization.deserialize(s), ArbVector([])) + @test isequal_and_prec_equal(Serialization.deserialize(s), ArbVector(Arb[1])) + @test isequal_and_prec_equal( + Serialization.deserialize(s), + ArbVector(Arb[1, 2, 3, 4]), + ) + @test isequal_and_prec_equal( + Serialization.deserialize(s), + ArbVector(Arb[1//3, π, ℯ, NaN]), + ) + end + + # AcbVector + create_serialization_stream() do s + for v in ( + AcbVector([]), + AcbVector(Acb[1]), + AcbVector(Acb[1, 2, 3, 4]), + AcbVector(Acb[1//3, π, ℯ, NaN]), + AcbVector(im * Acb[1//3, π, ℯ, NaN]), + AcbVector((1 + 2im) * Acb[1//3, π, ℯ, NaN]), + ) + Serialization.serialize(s, v) + Arblib.zero!(v) # To catch aliasing issues + end + seek(s, 0) + @test isequal_and_prec_equal(Serialization.deserialize(s), AcbVector([])) + @test isequal_and_prec_equal(Serialization.deserialize(s), AcbVector(Acb[1])) + @test isequal_and_prec_equal( + Serialization.deserialize(s), + AcbVector(Acb[1, 2, 3, 4]), + ) + @test isequal_and_prec_equal( + Serialization.deserialize(s), + AcbVector(Acb[1//3, π, ℯ, NaN]), + ) + @test isequal_and_prec_equal( + Serialization.deserialize(s), + AcbVector(im * Acb[1//3, π, ℯ, NaN]), + ) + @test isequal_and_prec_equal( + Serialization.deserialize(s), + AcbVector((1 + 2im) * Acb[1//3, π, ℯ, NaN]), + ) + end + + # ArbMatrix + create_serialization_stream() do s + for v in ( + ArbMatrix([]), + ArbMatrix([]), + ArbMatrix(Arb[1;;]), + ArbMatrix(Arb[1 2; 3 4]), + ArbMatrix(Arb[1 2 3; 4 5 6]), + ArbMatrix(Arb[1//3 π; ℯ NaN]), + ) + Serialization.serialize(s, v) + Arblib.zero!(v) # To catch aliasing issues + end + seek(s, 0) + @test isequal_and_prec_equal(Serialization.deserialize(s), ArbMatrix([])) + @test isequal_and_prec_equal(Serialization.deserialize(s), ArbMatrix([])) + @test isequal_and_prec_equal(Serialization.deserialize(s), ArbMatrix(Arb[1;;])) + @test isequal_and_prec_equal(Serialization.deserialize(s), ArbMatrix(Arb[1 2; 3 4])) + @test isequal_and_prec_equal( + Serialization.deserialize(s), + ArbMatrix(Arb[1 2 3; 4 5 6]), + ) + @test isequal_and_prec_equal( + Serialization.deserialize(s), + ArbMatrix(Arb[1//3 π; ℯ NaN]), + ) + end + + # AcbMatrix + create_serialization_stream() do s + for v in ( + AcbMatrix([]), + AcbMatrix([]), + AcbMatrix(Acb[1;;]), + AcbMatrix(Acb[1 2; 3 4]), + AcbMatrix(Acb[1 2 3; 4 5 6]), + AcbMatrix(Acb[1//3 π; ℯ NaN]), + AcbMatrix(im * Acb[1//3 π; ℯ NaN]), + AcbMatrix((1 + 2im) * Acb[1//3 π; ℯ NaN]), + ) + Serialization.serialize(s, v) + Arblib.zero!(v) # To catch aliasing issues + end + seek(s, 0) + @test isequal_and_prec_equal(Serialization.deserialize(s), AcbMatrix([])) + @test isequal_and_prec_equal(Serialization.deserialize(s), AcbMatrix([])) + @test isequal_and_prec_equal(Serialization.deserialize(s), AcbMatrix(Acb[1;;])) + @test isequal_and_prec_equal(Serialization.deserialize(s), AcbMatrix(Acb[1 2; 3 4])) + @test isequal_and_prec_equal( + Serialization.deserialize(s), + AcbMatrix(Acb[1 2 3; 4 5 6]), + ) + @test isequal_and_prec_equal( + Serialization.deserialize(s), + AcbMatrix(Acb[1//3 π; ℯ NaN]), + ) + @test isequal_and_prec_equal( + Serialization.deserialize(s), + AcbMatrix(im * Acb[1//3 π; ℯ NaN]), + ) + @test isequal_and_prec_equal( + Serialization.deserialize(s), + AcbMatrix((1 + 2im) * Acb[1//3 π; ℯ NaN]), + ) + end + + # ArbPoly + create_serialization_stream() do s + for v in ( + ArbPoly([]), + ArbPoly(Arb[1]), + ArbPoly(Arb[1, 0, 0]), + ArbPoly(Arb[1, 2, 3, 4]), + ArbPoly(Arb[1//3, π, ℯ, NaN]), + ) + Serialization.serialize(s, v) + Arblib.zero!(v) # To catch aliasing issues + end + seek(s, 0) + @test isequal_and_prec_equal(Serialization.deserialize(s), ArbPoly([])) + @test isequal_and_prec_equal(Serialization.deserialize(s), ArbPoly(Arb[1])) + @test isequal_and_prec_equal(Serialization.deserialize(s), ArbPoly(Arb[1, 0, 0])) + @test isequal_and_prec_equal(Serialization.deserialize(s), ArbPoly(Arb[1, 2, 3, 4])) + @test isequal_and_prec_equal( + Serialization.deserialize(s), + ArbPoly(Arb[1//3, π, ℯ, NaN]), + ) + end + + # AcbPoly + create_serialization_stream() do s + for v in ( + AcbPoly([]), + AcbPoly(Acb[1]), + AcbPoly(Acb[1, 0, 0]), + AcbPoly(Acb[1, 2, 3, 4]), + AcbPoly(Acb[1//3, π, ℯ, NaN]), + AcbPoly(im * Acb[1//3, π, ℯ, NaN]), + AcbPoly((1 + 2im) * Acb[1//3, π, ℯ, NaN]), + ) + Serialization.serialize(s, v) + Arblib.zero!(v) # To catch aliasing issues + end + seek(s, 0) + @test isequal_and_prec_equal(Serialization.deserialize(s), AcbPoly([])) + @test isequal_and_prec_equal(Serialization.deserialize(s), AcbPoly(Acb[1])) + @test isequal_and_prec_equal(Serialization.deserialize(s), AcbPoly(Acb[1, 0, 0])) + @test isequal_and_prec_equal(Serialization.deserialize(s), AcbPoly(Acb[1, 2, 3, 4])) + @test isequal_and_prec_equal( + Serialization.deserialize(s), + AcbPoly(Acb[1//3, π, ℯ, NaN]), + ) + @test isequal_and_prec_equal( + Serialization.deserialize(s), + AcbPoly(im * Acb[1//3, π, ℯ, NaN]), + ) + @test isequal_and_prec_equal( + Serialization.deserialize(s), + AcbPoly((1 + 2im) * Acb[1//3, π, ℯ, NaN]), + ) + end + + # ArbSeries + create_serialization_stream() do s + for v in ( + ArbSeries([]), + ArbSeries(Arb[1]), + ArbSeries(Arb[1, 0, 0]), + ArbSeries(Arb[1, 2, 3, 4]), + ArbSeries(Arb[1//3, π, ℯ, NaN]), + ArbSeries(Arb[1, 0, 0]), + ) + Serialization.serialize(s, v) + Arblib.zero!(v) # To catch aliasing issues + end + seek(s, 0) + @test isequal_and_prec_equal(Serialization.deserialize(s), ArbSeries([])) + @test isequal_and_prec_equal(Serialization.deserialize(s), ArbSeries(Arb[1])) + @test isequal_and_prec_equal(Serialization.deserialize(s), ArbSeries(Arb[1, 0, 0])) + @test isequal_and_prec_equal( + Serialization.deserialize(s), + ArbSeries(Arb[1, 2, 3, 4]), + ) + @test isequal_and_prec_equal( + Serialization.deserialize(s), + ArbSeries(Arb[1//3, π, ℯ, NaN]), + ) + # Check that it allocates space according to the degree + # This fails with the default implementation of deserialize + @test Arblib.cstruct(Serialization.deserialize(s)).alloc == 3 + end + + # AcbSeries + create_serialization_stream() do s + for v in ( + AcbSeries([]), + AcbSeries(Acb[1]), + AcbSeries(Acb[1, 0, 0]), + AcbSeries(Acb[1, 2, 3, 4]), + AcbSeries(Acb[1//3, π, ℯ, NaN]), + AcbSeries(im * Acb[1//3, π, ℯ, NaN]), + AcbSeries((1 + 2im) * Acb[1//3, π, ℯ, NaN]), + AcbSeries(Acb[1, 0, 0]), + ) + Serialization.serialize(s, v) + Arblib.zero!(v) # To catch aliasing issues + end + seek(s, 0) + @test isequal_and_prec_equal(Serialization.deserialize(s), AcbSeries([])) + @test isequal_and_prec_equal(Serialization.deserialize(s), AcbSeries(Acb[1])) + @test isequal_and_prec_equal(Serialization.deserialize(s), AcbSeries(Acb[1, 0, 0])) + @test isequal_and_prec_equal( + Serialization.deserialize(s), + AcbSeries(Acb[1, 2, 3, 4]), + ) + @test isequal_and_prec_equal( + Serialization.deserialize(s), + AcbSeries(Acb[1//3, π, ℯ, NaN]), + ) + @test isequal_and_prec_equal( + Serialization.deserialize(s), + AcbSeries(im * Acb[1//3, π, ℯ, NaN]), + ) + @test isequal_and_prec_equal( + Serialization.deserialize(s), + AcbSeries((1 + 2im) * Acb[1//3, π, ℯ, NaN]), + ) + # Check that it allocates space according to the degree + # This fails with the default implementation of deserialize + @test Arblib.cstruct(Serialization.deserialize(s)).alloc == 3 + end +end diff --git a/test/series.jl b/test/series.jl index 318c018e..44911546 100644 --- a/test/series.jl +++ b/test/series.jl @@ -58,6 +58,7 @@ @testset "Constructors" begin @test TSeries() == + TSeries([]) == TSeries(T(0)) == TSeries((0,)) == TSeries(T[0]) == diff --git a/test/show.jl b/test/show.jl index 9276135c..13c6e6cd 100644 --- a/test/show.jl +++ b/test/show.jl @@ -8,11 +8,9 @@ end @testset "dump" begin - x = Mag(1.1) - for x in (Mag(1.1), Arf(1.1), Arb(1.1)) - y = zero(x) + for x in (Mag(π), Arf(1 // 3), Arb(π)) str = Arblib.dump_string(x) - Arblib.load_string!(y, str) + y = Arblib.load_string!(zero(x), str) @test isequal(x, y) end end diff --git a/test/vector.jl b/test/vector.jl index 9dd6bff6..f7be7bb6 100644 --- a/test/vector.jl +++ b/test/vector.jl @@ -4,6 +4,9 @@ (ArbRefVector, Arb, ArbRef), (AcbRefVector, Acb, AcbRef), ] + @test isempty(TVec([])) + @test isempty(TVec([], prec = 80)) + V = TVec(4, prec = 128) @test size(V) == (4,) @test precision(V) == 128