diff --git a/src/HeckeMoreStuff.jl b/src/HeckeMoreStuff.jl index 262ab8148..c073aac1c 100644 --- a/src/HeckeMoreStuff.jl +++ b/src/HeckeMoreStuff.jl @@ -182,21 +182,6 @@ function Base.hash(f::zzModMPolyRingElem, h::UInt) return UInt(1) # TODO: enhance or throw error end -function AbstractAlgebra.map_coefficients(F::fpField, f::QQMPolyRingElem; parent=polynomial_ring(F, nvars(parent(f)), cached=false)[1]) - dF = denominator(f) - d = F(dF) - if iszero(d) - error("Denominator divisible by p!") - end - m = inv(d) - ctx = MPolyBuildCtx(parent) - for x in zip(coefficients(f), exponent_vectors(f)) - el = numerator(x[1] * dF) - push_term!(ctx, F(el) * m, x[2]) - end - return finish(ctx) -end - function tdivpow2!(B::ZZMatrix, t::Int) @ccall libflint.fmpz_mat_scalar_tdiv_q_2exp(B::Ref{ZZMatrix}, B::Ref{ZZMatrix}, t::Cint)::Nothing end diff --git a/src/flint/fmpq_mpoly.jl b/src/flint/fmpq_mpoly.jl index d3bd7d51b..8d92ee6d6 100644 --- a/src/flint/fmpq_mpoly.jl +++ b/src/flint/fmpq_mpoly.jl @@ -657,6 +657,11 @@ end # ############################################################################### +_content_ptr(c::QQMPolyRingElem) = Ptr{QQFieldElem}(pointer_from_objref(c)) +_content_ptr(c::Ptr{QQMPolyRingElem}) = Ptr{QQFieldElem}(c) +_content_ptr(c::Ref{QQMPolyRingElem}) = _content_ptr(c[]) +_zpoly_ptr(c::QQMPolyRingElemOrPtr) = Ptr{ZZMPolyRingElem}(_content_ptr(c) + sizeof(QQFieldElem)) + function zero!(a::QQMPolyRingElem) @ccall libflint.fmpq_mpoly_zero(a::Ref{QQMPolyRingElem}, a.parent::Ref{QQMPolyRing})::Nothing return a @@ -760,6 +765,20 @@ sub!(a::QQMPolyRingElem, b::RationalUnion, c::QQMPolyRingElem) = neg!(sub!(a, c, mul!(a::QQMPolyRingElem, b::QQMPolyRingElem, c::RationalUnion) = mul!(a, b, flintify(c)) mul!(a::QQMPolyRingElem, b::RationalUnion, c::QQMPolyRingElem) = mul!(a, c, b) +# special: multiply a QQMPolyRingElem by an integer and store the result as a ZZMPolyRingElem. +# obviously this only works if the integers is a multiple of the denominator of the polynomial +function mul!(a::ZZMPolyRingElem, b::QQMPolyRingElem, c::IntegerUnion) + x = QQFieldElem() + GC.@preserve a b x begin + x = mul!(x, _content_ptr(b), c) + bp = _zpoly_ptr(b) + xp = _num_ptr(x) + @ccall libflint.fmpz_mpoly_scalar_mul_fmpz(a::Ref{ZZMPolyRingElem}, bp::Ref{ZZMPolyRingElem}, xp::Ref{ZZRingElem}, parent(a)::Ref{ZZMPolyRing})::Nothing + end + return a +end + + divexact!(a::QQMPolyRingElem, b::QQMPolyRingElem, c::RationalUnion) = divexact!(a, b, flintify(c)) # Set the n-th coefficient of a to c. If zero coefficients are inserted, they @@ -1059,3 +1078,47 @@ function (R::QQMPolyRing)(a::Vector{Any}, b::Vector{Vector{T}}) where T return R(newaa, newbb) end + +############################################################################### +# +# Changing base ring, mapping coefficients +# +############################################################################### + +function map_coefficients(R::ZZRing, f::QQMPolyRingElem; + cached::Bool = true, + parent::ZZMPolyRing = AbstractAlgebra._change_mpoly_ring(R, parent(f), cached)) + @req isinteger(_content_ptr(f)) "input polynomial must have integral coefficients" + @req ngens(parent) == ngens(Nemo.parent(f)) "parents must have matching numbers of generators" + @req internal_ordering(parent) == internal_ordering(Nemo.parent(f)) "parents must have matching internal ordering" + return mul!(zero(parent), f, 1) +end + +function map_coefficients(F::fpField, f::QQMPolyRingElem; + cached::Bool = true, + parent::MPolyRing = AbstractAlgebra._change_mpoly_ring(F, parent(f), cached)) + dF = denominator(f) + d = F(dF) + if iszero(d) + error("Denominator divisible by p!") + end + m = inv(d) + ctx = MPolyBuildCtx(parent) + for x in zip(coefficients(f), exponent_vectors(f)) + el = numerator(x[1] * dF) + push_term!(ctx, F(el) * m, x[2]) + end + return finish(ctx) +end + +function change_base_ring(R::ZZRing, f::QQMPolyRingElem; + cached::Bool = true, + parent::ZZMPolyRing = AbstractAlgebra._change_mpoly_ring(R, parent(f), cached)) + return map_coefficients(R, f; cached, parent) +end + +function change_base_ring(F::fpField, f::QQMPolyRingElem; + cached::Bool = true, + parent::fpMPolyRing = AbstractAlgebra._change_mpoly_ring(F, parent(f), cached)) + return map_coefficients(R, f; cached, parent) +end diff --git a/test/flint/fmpq_mpoly-test.jl b/test/flint/fmpq_mpoly-test.jl index 37662000d..470cea320 100644 --- a/test/flint/fmpq_mpoly-test.jl +++ b/test/flint/fmpq_mpoly-test.jl @@ -813,3 +813,20 @@ end @test abc - a - b == c @test abc - ab == c end + +@testset "QQMPolyRingElem.convert_to_ZZMPolyRingElem" begin + Qxy, (x,y) = QQ[:x,:y] + f = 11*(x^2/2 + ZZ(3)^50*x*y^5/7 + y^6/12) + + Zxy, (x,y) = ZZ[:u,:v] + g = 462*x^2 + 94762534375324541717672868*x*y^5 + 77*y^6 + + @test g == @inferred mul!(zero(Zxy), f, 84) + @test g == change_base_ring(ZZ, f*84; parent = Zxy) + @test g == map_coefficients(ZZ, f*84; parent = Zxy) + + # test error handling + @test_throws ArgumentError change_base_ring(ZZ, f; parent = Zxy) + @test_throws ArgumentError map_coefficients(ZZ, f; parent = Zxy) + +end