From 6ee44355090e59b2f5d5adeb8bf2a333be3dc6d3 Mon Sep 17 00:00:00 2001 From: Johannes Schmitt Date: Sun, 25 Aug 2024 09:47:05 +0200 Subject: [PATCH] Make `set_precision` reduce the element (#1773) * Test edge cases * Make `set_precision!` truncate the polynomial for generic types --- src/RelSeries.jl | 5 +++++ src/generic/AbsSeries.jl | 20 ++++++++++++++++++++ src/generic/RelSeries.jl | 28 ++++++++++++++++++++++++++++ test/generic/AbsSeries-test.jl | 19 ++++++++++++++++++- test/generic/RelSeries-test.jl | 19 ++++++++++++++++++- 5 files changed, 89 insertions(+), 2 deletions(-) diff --git a/src/RelSeries.jl b/src/RelSeries.jl index 1c7004e86..ec835b614 100644 --- a/src/RelSeries.jl +++ b/src/RelSeries.jl @@ -95,6 +95,11 @@ function set_length!(a::SeriesElem, len::Int) return a end +# TODO: set_precision! for the generic types RelSeries and AbsSeries +# truncates the underlying polynomial since #1773. In a breaking release, +# this should possibly also happen for the abstract types. Alternatively, +# this set_precision! should be renamed (and only be kept as a purely internal +# setter function). function set_precision!(a::SeriesElem, prec::Int) a.prec = prec return a diff --git a/src/generic/AbsSeries.jl b/src/generic/AbsSeries.jl index d93788c48..d81020e58 100644 --- a/src/generic/AbsSeries.jl +++ b/src/generic/AbsSeries.jl @@ -72,6 +72,13 @@ function characteristic(a::AbsPowerSeriesRing{T}) where T <: RingElement return characteristic(base_ring(a)) end +function set_precision!(a::AbsSeries, prec::Int) + prec < 0 && throw(DomainError(prec, "Precision must be non-negative")) + a = truncate!(a, prec) + a.prec = prec + return a +end + ############################################################################### # # Binary operations @@ -164,6 +171,19 @@ end # ############################################################################### +function truncate!(a::AbsSeries{T}, n::Int) where T <: RingElement + n < 0 && throw(DomainError(n, "n must be >= 0")) + if precision(a) <= n + return a + end + a.length = min(n, length(a)) + while length(a) != 0 && is_zero(coeff(a, length(a) - 1)) + a.length -= 1 + end + a.prec = n + return a +end + function zero!(c::AbsSeries{T}) where T <: RingElement c.length = 0 c.prec = parent(c).prec_max diff --git a/src/generic/RelSeries.jl b/src/generic/RelSeries.jl index 54faf04d9..40bfda5fe 100644 --- a/src/generic/RelSeries.jl +++ b/src/generic/RelSeries.jl @@ -62,6 +62,16 @@ function characteristic(a::RelPowerSeriesRing{T}) where T <: RingElement return characteristic(base_ring(a)) end +function set_precision!(a::RelSeries, prec::Int) + prec < 0 && throw(DomainError(prec, "Precision must be non-negative")) + a = truncate!(a, prec) + a.prec = prec + if is_zero(a) + a.val = prec + end + return a +end + ############################################################################### # # Binary operators @@ -152,6 +162,24 @@ end # ############################################################################### +function truncate!(a::RelSeries{T}, n::Int) where T <: RingElement + n < 0 && throw(DomainError(n, "n must be >= 0")) + if precision(a) <= n + return a + end + if n <= valuation(a) + a = zero!(a) + a.val = n + else + a.length = min(n - valuation(a), pol_length(a)) + while is_zero(polcoeff(a, pol_length(a) - 1)) + a.length -= 1 + end + end + a.prec = n + return a +end + function zero!(a::RelSeries) a.length = 0 a.prec = parent(a).prec_max diff --git a/test/generic/AbsSeries-test.jl b/test/generic/AbsSeries-test.jl index 2741976e7..5e254111a 100644 --- a/test/generic/AbsSeries-test.jl +++ b/test/generic/AbsSeries-test.jl @@ -575,6 +575,10 @@ end # Exact ring R, x = power_series_ring(ZZ, 10, "x", model=:capped_absolute) + # Special case: length 1 + f = R(2) + @test isequal(f^3, R(8)) + for iter = 1:100 f = rand(R, 0:12, -10:10) r2 = R(1) @@ -1297,4 +1301,17 @@ end @test is_unit(det(U)) @test is_unit(det(V)) end - end +end + +@testset "Generic.AbsSeries.set_precision" begin + R, x = power_series_ring(QQ, 20, "x", model = :capped_absolute) + a = 1 + x + x^3 + O(x^5) + b = x^2 + O(x^3) + + @test isequal(set_precision(a, 7), 1 + x + x^3 + O(x^7)) + @test isequal(set_precision(a, 5), 1 + x + x^3 + O(x^5)) + @test isequal(set_precision(a, 3), 1 + x + O(x^3)) + @test isequal(set_precision(a, 0), O(x^0)) + @test isequal(set_precision(b, 1), O(x)) + @test is_zero(set_precision(b, 1)) +end diff --git a/test/generic/RelSeries-test.jl b/test/generic/RelSeries-test.jl index c2301ab61..f1aa9e506 100644 --- a/test/generic/RelSeries-test.jl +++ b/test/generic/RelSeries-test.jl @@ -894,6 +894,10 @@ end @testset "Generic.RelSeries.square_root" begin # Exact ring R, x = power_series_ring(ZZ, 10, "x") + + # Special case: precision 0 + @test is_zero(sqrt(O(x^0))) + for iter = 1:300 f = rand(R, 0:10, -10:10) g = f^2 @@ -1333,4 +1337,17 @@ end @test is_unit(det(U)) @test is_unit(det(V)) end - end +end + +@testset "Generic.RelSeries.set_precision" begin + R, x = power_series_ring(QQ, 20, "x", model = :capped_relative) + a = 1 + x + x^3 + O(x^5) + b = x^2 + O(x^3) + + @test isequal(set_precision(a, 7), 1 + x + x^3 + O(x^7)) + @test isequal(set_precision(a, 5), 1 + x + x^3 + O(x^5)) + @test isequal(set_precision(a, 3), 1 + x + O(x^3)) + @test isequal(set_precision(a, 0), O(x^0)) + @test isequal(set_precision(b, 1), O(x)) + @test is_zero(set_precision(b, 1)) +end