11export GeneralizedEuclideanAlgorithm
22
3+ _copy (p, :: MA.IsMutable ) = p
4+ _copy (p, :: MA.IsNotMutable ) = copy (p)
5+
36"""
47 abstract type AbstractUnivariateGCDAlgorithm end
58
@@ -51,8 +54,12 @@ _coefficient_gcd(α, β::AbstractFloat) = one(Base.promote_typeof(α, β))
5154_coefficient_gcd (α:: AbstractFloat , β:: AbstractFloat ) = one (Base. promote_typeof (α, β))
5255
5356Base. lcm (p:: APL , q:: APL , algo:: AbstractUnivariateGCDAlgorithm = GeneralizedEuclideanAlgorithm ()) = p * div (q, gcd (p, q, algo))
54- Base. gcd (α, p:: APL , algo:: AbstractUnivariateGCDAlgorithm = GeneralizedEuclideanAlgorithm ()) = _coefficient_gcd (α, content (p, algo))
55- Base. gcd (p:: APL , α, algo:: AbstractUnivariateGCDAlgorithm = GeneralizedEuclideanAlgorithm ()) = _coefficient_gcd (content (p, algo), α)
57+ function Base. gcd (α, p:: APL , algo:: AbstractUnivariateGCDAlgorithm = GeneralizedEuclideanAlgorithm (), mα:: MA.MutableTrait = MA. IsNotMutable (), mp:: MA.MutableTrait = MA. IsNotMutable ())
58+ return _coefficient_gcd (α, content (p, algo, mp))
59+ end
60+ function Base. gcd (p:: APL , α, algo:: AbstractUnivariateGCDAlgorithm = GeneralizedEuclideanAlgorithm (), mp:: MA.MutableTrait = MA. IsNotMutable (), mα:: MA.MutableTrait = MA. IsNotMutable ())
61+ return _coefficient_gcd (content (p, algo, mp), α)
62+ end
5663
5764# function MA.promote_operation(::typeof(gcd), P::Type{<:Number}, Q::Type{<:Number})
5865# return typeof(gcd(one(P), one(Q)))
@@ -106,14 +113,14 @@ This is the [`GeneralizedEuclideanAlgorithm`](@ref).
106113*Art of computer programming, volume 2: Seminumerical algorithms.*
107114Addison-Wesley Professional. Third edition.
108115"""
109- function Base. gcd (p1:: APL{T} , p2:: APL{S} , algo:: AbstractUnivariateGCDAlgorithm = GeneralizedEuclideanAlgorithm ()) where {T, S}
116+ function Base. gcd (p1:: APL{T} , p2:: APL{S} , algo:: AbstractUnivariateGCDAlgorithm = GeneralizedEuclideanAlgorithm (), m1 :: MA.MutableTrait = MA . IsNotMutable (), m2 :: MA.MutableTrait = MA . IsNotMutable () ) where {T, S}
110117 # If one of these is zero, `shift` should be infinite
111118 # for this method to work so we exclude these cases.
112119 if isapproxzero (p1)
113- return convert (MA. promote_operation (gcd, typeof (p1), typeof (p2)), p2 )
120+ return convert (MA. promote_operation (gcd, typeof (p1), typeof (p2)), _copy (p2, m2) )
114121 end
115122 if isapproxzero (p2)
116- return convert (MA. promote_operation (gcd, typeof (p1), typeof (p2)), p1 )
123+ return convert (MA. promote_operation (gcd, typeof (p1), typeof (p2)), _copy (p1, m1) )
117124 end
118125 shift1, defl1 = deflation (p1)
119126 shift2, defl2 = deflation (p2)
@@ -125,11 +132,11 @@ function Base.gcd(p1::APL{T}, p2::APL{S}, algo::AbstractUnivariateGCDAlgorithm=G
125132 # Then, we subsitute `y[i] = x[i]^defl[i]`.
126133 q1 = deflate (p1, shift1, defl)
127134 q2 = deflate (p2, shift2, defl)
128- g = deflated_gcd (q1, q2, algo)
135+ g = deflated_gcd (q1, q2, algo, m1, m2 )
129136 return inflate (g, shift, defl):: MA.promote_operation (gcd, typeof (p1), typeof (p2))
130137end
131138
132- function Base. gcd (t1:: AbstractTermLike{T} , t2:: AbstractTermLike{S} , algo:: AbstractUnivariateGCDAlgorithm = GeneralizedEuclideanAlgorithm ()) where {T, S}
139+ function Base. gcd (t1:: AbstractTermLike{T} , t2:: AbstractTermLike{S} , algo:: AbstractUnivariateGCDAlgorithm = GeneralizedEuclideanAlgorithm (), m1 :: MA.MutableTrait = MA . IsNotMutable (), m2 :: MA.MutableTrait = MA . IsNotMutable () ) where {T, S}
133140 return term (_coefficient_gcd (coefficient (t1), coefficient (t2)), gcd (monomial (t1), monomial (t2)))
134141end
135142
@@ -211,36 +218,36 @@ function MA.operate(op::Union{typeof(deflate), typeof(inflate)}, p::AbstractPoly
211218 end )
212219end
213220
214- function deflated_gcd (p1:: APL{T} , p2:: APL{S} , algo) where {T, S}
221+ function deflated_gcd (p1:: APL{T} , p2:: APL{S} , algo, m1 :: MA.MutableTrait , m2 :: MA.MutableTrait ) where {T, S}
215222 i1, i2, num_common = _extracted_variable (p1, p2)
216223 if iszero (i1)
217224 if iszero (i2)
218- return univariate_gcd (p1, p2, algo)
225+ return univariate_gcd (p1, p2, algo, m1, m2 )
219226 else
220227 if isapproxzero (p1)
221- return convert (MA. promote_operation (gcd, typeof (p1), typeof (p2)), p2 )
228+ return convert (MA. promote_operation (gcd, typeof (p1), typeof (p2)), _copy (p2, m2) )
222229 end
223230 v2 = variables (p2)[i2]
224- q2 = isolate_variable (p2, v2)
225- g = content (q2, algo)
226- return gcd (p1, g, algo)
231+ q2 = isolate_variable (p2, v2, m2 )
232+ g = content (q2, algo, MA . IsMutable () )
233+ return gcd (p1, g, algo, m1, MA . IsMutable () )
227234 end
228235 else
229236 if iszero (i2)
230237 if isapproxzero (p2)
231- return convert (MA. promote_operation (gcd, typeof (p1), typeof (p2)), p1 )
238+ return convert (MA. promote_operation (gcd, typeof (p1), typeof (p2)), _copy (p1, m1) )
232239 end
233240 v1 = variables (p1)[i1]
234- q1 = isolate_variable (p1, v1)
235- g = content (q1, algo)
236- return gcd (g, p2, algo)
241+ q1 = isolate_variable (p1, v1, m1 )
242+ g = content (q1, algo, MA . IsMutable () )
243+ return gcd (g, p2, algo, MA . IsMutable (), m2 )
237244 else
238245 if num_common > 1
239246 v1 = variables (p1)[i1]
240247 @assert v1 == variables (p2)[i2]
241- return multivariate_gcd (p1, p2, v1, algo)
248+ return multivariate_gcd (p1, p2, v1, algo, m1, m2 )
242249 else
243- return univariate_gcd (p1, p2, algo)
250+ return univariate_gcd (p1, p2, algo, m1, m2 )
244251 end
245252 end
246253 end
@@ -332,35 +339,53 @@ function _extracted_variable(p1, p2)
332339 return best_var1, best_var2, num_common
333340end
334341
335- function multivariate_gcd (p1:: APL , p2:: APL , var, algo)
336- q = univariate_gcd (isolate_variable (p1, var), isolate_variable (p2, var), algo)
342+ function multivariate_gcd (p1:: APL , p2:: APL , var, algo, m1 :: MA.MutableTrait , m2 :: MA.MutableTrait )
343+ q = univariate_gcd (isolate_variable (p1, var, m1 ), isolate_variable (p2, var, m2 ), algo, MA . IsMutable (), MA . IsMutable () )
337344 return sum (coefficient (t) * monomial (t) for t in terms (q)):: MA.promote_operation (gcd, typeof (p1), typeof (p2))
338345end
339346
340- function isolate_variable (poly:: APL , var:: AbstractVariable )
341- T = typeof (substitute (Subs (), zeroterm (poly), (var,) => (1 ,)))
342- dict = Dict {Int,Vector{T}} ()
343- for t in terms (poly)
344- d = degree (t, var)
345- dict_ts = get (dict, d, nothing )
346- ts = dict_ts === nothing ? T[] : dict_ts
347- push! (ts, subs (t, (var,) => (1 ,)))
348- if dict_ts === nothing
349- dict[d] = ts
347+ _vector (t:: AbstractVector ) = collect (t)
348+ _vector (t:: Vector ) = t
349+
350+ """
351+ isolate_variable(poly::APL, var::AbstractVariable, mutability::MA.MutableTrait)
352+
353+ Returns a polynomial with variable `var`. The other variables of `poly` are moved as coefficients.
354+
355+ The output can be mutated without affecting `poly` if `mutability` is
356+ `MA.IsNotMutable`.
357+ """
358+ function isolate_variable (poly:: APL , var:: AbstractVariable , mutability:: MA.MutableTrait )
359+ old_terms = sort! (_vector (terms (_copy (poly, mutability))), by = Base. Fix2 (degree, var), rev= true )
360+ T = termtype (var, typeof (substitute (Subs (), zero (poly), (var,) => (1 ,))))
361+ new_terms = T[]
362+ i = firstindex (old_terms)
363+ while i <= lastindex (old_terms)
364+ j = i + 1
365+ d = degree (old_terms[i], var)
366+ while j <= lastindex (old_terms)
367+ if degree (old_terms[j], var) != d
368+ break
369+ end
370+ j += 1
350371 end
372+ coef = _polynomial ([subs (old_terms[k], (var,) => (1 ,)) for k in i: (j - 1 )], SortedUniqState (), mutability)
373+ push! (new_terms, term (coef, var^ d))
374+ i = j
351375 end
352- return polynomial ([
353- term (polynomial (ts), var^ d) for (d, ts) in dict
354- ])
376+ return polynomial! (new_terms, SortedUniqState ())
355377end
356378
379+ _polynomial (ts, state, :: MA.IsNotMutable ) = polynomial (ts, state)
380+ _polynomial (ts, state, :: MA.IsMutable ) = polynomial! (ts, state)
381+
357382"""
358- primitive_univariate_gcd(p::APL, q::APL, algo::AbstractUnivariateGCDAlgorithm)
383+ primitive_univariate_gcd! (p::APL, q::APL, algo::AbstractUnivariateGCDAlgorithm)
359384
360385Returns the `gcd` of primitive polynomials `p` and `q` using algorithm `algo`
361386which is a subtype of [`AbstractUnivariateGCDAlgorithm`](@ref).
362387"""
363- function primitive_univariate_gcd end
388+ function primitive_univariate_gcd! end
364389
365390function not_divided_error (u, v)
366391 error (
376401
377402# If `p` and `q` do not have the same time then the local variables `p` and `q`
378403# won't be type stable.
379- function primitive_univariate_gcd (p:: APL , q:: APL , algo:: GeneralizedEuclideanAlgorithm )
404+ function primitive_univariate_gcd! (p:: APL , q:: APL , algo:: GeneralizedEuclideanAlgorithm )
380405 if maxdegree (p) < maxdegree (q)
381- return primitive_univariate_gcd (q, p, algo)
406+ return primitive_univariate_gcd! (q, p, algo)
382407 end
383408 R = MA. promote_operation (gcd, typeof (p), typeof (q))
384409 u = convert (R, p)
@@ -396,7 +421,7 @@ function primitive_univariate_gcd(p::APL, q::APL, algo::GeneralizedEuclideanAlgo
396421 not_divided_error (u, v)
397422 end
398423 if ! algo. primitive_rem
399- r = primitive_part (r, algo):: R
424+ r = primitive_part (r, algo, MA . IsMutable () ):: R
400425 end
401426 u, v = v, r:: R
402427 end
@@ -438,7 +463,7 @@ function primitive_univariate_gcdx(u0::APL, v0::APL, algo::GeneralizedEuclideanA
438463end
439464
440465
441- function primitive_univariate_gcd (p:: APL , q:: APL , :: SubresultantAlgorithm )
466+ function primitive_univariate_gcd! (p:: APL , q:: APL , :: SubresultantAlgorithm )
442467 error (" Not implemented yet" )
443468end
444469
@@ -457,118 +482,121 @@ If the coefficients are not `AbstractFloat`, this
457482 [`primitive_part`](@ref) using [`primitive_part_content`](@ref); see
458483 [Knu14, Algorithm E: E1, p. 426] or [Knu14, Algorithm C: C1, p. 428].
4594842. Computes the [`gcd`](@ref) of the contents and primitive parts, using
460- [`primitive_univariate_gcd`](@ref) for primitive parts.
485+ [`primitive_univariate_gcd! `](@ref) for primitive parts.
4614863. Return the product of these two `gcd`; see
462487 [Knu14, Algorithm E: E4, p. 427] or [Knu14, Algorithm C: C4, p. 429].
463488
464489[Knu14] Knuth, D.E., 2014.
465490*Art of computer programming, volume 2: Seminumerical algorithms.*
466491Addison-Wesley Professional. Third edition.
467492"""
468- function univariate_gcd (p1:: APL{S} , p2:: APL{T} , algo:: AbstractUnivariateGCDAlgorithm ) where {S,T}
469- return univariate_gcd (_field_absorb (algebraic_structure (S), algebraic_structure (T)), p1, p2, algo)
470- end
471- function univariate_gcd (:: UFD , p1:: APL , p2:: APL , algo:: AbstractUnivariateGCDAlgorithm )
472- f1, g1 = primitive_part_content (p1, algo)
473- f2, g2 = primitive_part_content (p2, algo)
474- pp = primitive_univariate_gcd (f1, f2, algo)
475- gg = _gcd (g1, g2, algo)# ::MA.promote_operation(gcd, typeof(g1), typeof(g2))
493+ function univariate_gcd (p1:: APL{S} , p2:: APL{T} , algo:: AbstractUnivariateGCDAlgorithm , m1 :: MA.MutableTrait , m2 :: MA.MutableTrait ) where {S,T}
494+ return univariate_gcd (_field_absorb (algebraic_structure (S), algebraic_structure (T)), p1, p2, algo, m1, m2 )
495+ end
496+ function univariate_gcd (:: UFD , p1:: APL , p2:: APL , algo:: AbstractUnivariateGCDAlgorithm , m1 :: MA.MutableTrait , m2 :: MA.MutableTrait )
497+ f1, g1 = primitive_part_content (p1, algo, m1 )
498+ f2, g2 = primitive_part_content (p2, algo, m2 )
499+ pp = primitive_univariate_gcd! (f1, f2, algo)
500+ gg = _gcd (g1, g2, algo, MA . IsMutable (), MA . IsMutable () )# ::MA.promote_operation(gcd, typeof(g1), typeof(g2))
476501 # Multiply each coefficient by the gcd of the contents.
477- return mapcoefficientsnz (Base. Fix1 (* , gg), pp)
502+ return mapcoefficients! (Base. Fix1 (* , gg), pp, nonzero = true )
478503end
479504
480- function univariate_gcd (:: Field , p1:: APL , p2:: APL , algo:: AbstractUnivariateGCDAlgorithm )
481- return primitive_univariate_gcd (p1, p2, algo)
505+ function univariate_gcd (:: Field , p1:: APL , p2:: APL , algo:: AbstractUnivariateGCDAlgorithm , m1 :: MA.MutableTrait , m2 :: MA.MutableTrait )
506+ return primitive_univariate_gcd! (p1, p2, algo)
482507end
483508
484509function univariate_gcdx (p1:: APL{S} , p2:: APL{T} , algo:: AbstractUnivariateGCDAlgorithm ) where {S,T}
485510 return univariate_gcdx (_field_absorb (algebraic_structure (S), algebraic_structure (T)), p1, p2, algo)
486511end
487512function univariate_gcdx (:: UFD , p1:: APL , p2:: APL , algo:: AbstractUnivariateGCDAlgorithm )
488- f1, g1 = primitive_part_content (p1, algo)
489- f2, g2 = primitive_part_content (p2, algo)
513+ f1, g1 = primitive_part_content (p1, algo, MA . IsNotMutable () )
514+ f2, g2 = primitive_part_content (p2, algo, MA . IsNotMutable () )
490515 a, b, pp = primitive_univariate_gcdx (f1, f2, algo)
491- gg = _gcd (g1, g2, algo)# ::MA.promote_operation(gcd, typeof(g1), typeof(g2))
516+ gg = _gcd (g1, g2, algo, MA . IsMutable (), MA . IsMutable () )# ::MA.promote_operation(gcd, typeof(g1), typeof(g2))
492517 # Multiply each coefficient by the gcd of the contents.
493- return g2 * a, g1 * b, g1 * g2 * mapcoefficientsnz (Base. Fix1 (* , gg), pp)
518+ return g2 * a, g1 * b, g1 * g2 * mapcoefficients (Base. Fix1 (* , gg), pp, nonzero = true )
494519end
495520function univariate_gcdx (:: Field , p1:: APL , p2:: APL , algo:: AbstractUnivariateGCDAlgorithm )
496521 return primitive_univariate_gcdx (p1, p2, algo)
497522end
498523
499- _gcd (a:: APL , b:: APL , algo) = gcd (a, b, algo)
500- _gcd (a, b:: APL , algo) = gcd (a, b, algo)
501- _gcd (a:: APL , b, algo) = gcd (a, b, algo)
502- _gcd (a, b, algo) = gcd (a, b)
524+ _gcd (a:: APL , b:: APL , algo, ma, mb ) = gcd (a, b, algo, ma, mb )
525+ _gcd (a, b:: APL , algo, ma, mb ) = gcd (a, b, algo, ma, mb )
526+ _gcd (a:: APL , b, algo, ma, mb ) = gcd (a, b, algo, ma, mb )
527+ _gcd (a, b, algo, ma, mb ) = gcd (a, b)
503528
504- _simplifier (a:: APL , b:: APL , algo) = gcd (a, b, algo)
505- _simplifier (a, b, algo) = _gcd (a, b, algo)
529+ _simplifier (a:: APL , b:: APL , algo, ma, mb ) = gcd (a, b, algo, ma, mb )
530+ _simplifier (a, b, algo, ma, mb ) = _gcd (a, b, algo, ma, mb )
506531# Before Julia v1.4, it is not defined.
507532# After Julia v1.4, it is defined as `gcd of num / lcm of den`.
508533# We prefer `gcd of den`, otherwise,
509534# `1/a0 + 1/a1 x + 1/a2 x^2 + ... + 1/an x^n`
510535# will be transformed into
511536# `a1*a2*...*an + a0*a2*...*an x + ...`
512537# which makes the size of the `BigInt`s grow significantly which slows things down.
513- _simplifier (a:: Rational , b:: Rational , algo) = gcd (a. num, b. num) // gcd (a. den, b. den)
538+ _simplifier (a:: Rational , b:: Rational , algo, ma, mb ) = gcd (a. num, b. num) // gcd (a. den, b. den)
514539
515540# Largely inspired from from `YingboMa/SIMDPolynomials.jl`.
516- function termwise_content (p:: APL )
541+ function termwise_content (p:: APL , algo, mutability :: MA.MutableTrait )
517542 ts = terms (p)
518- length (ts) == 1 && return first (ts)
519- g = gcd (ts[1 ], ts[2 ])
543+ length (ts) == 1 && return _copy ( first (ts), mutability )
544+ g = gcd (ts[1 ], ts[2 ], algo, mutability, mutability )
520545 isone (g) || for i in 3 : length (ts)
521- g = gcd (g, ts[i])
546+ g = gcd (g, ts[i], algo, MA . IsMutable (), mutability )
522547 isone (g) && break
523548 end
524549 return g
525550end
526551
527552"""
528- content(poly::AbstractPolynomialLike{T}, algo::AbstractUnivariateGCDAlgorithm) where {T}
553+ content(poly::AbstractPolynomialLike{T}, algo::AbstractUnivariateGCDAlgorithm, mutability::MA.MutableTrait ) where {T}
529554
530555Return the *content* of the polynomial `poly` over a unique factorization
531556domain `S` as defined in [Knu14, (3) p. 423].
532557That is, return the `gcd` of the coefficients of `poly`.
533558See also [`primitive_part_content`](@ref).
534559
560+ The output can be mutated without affecting `poly` if `mutability` is
561+ `MA.IsNotMutable`.
562+
535563[Knu14] Knuth, D.E., 2014.
536564*Art of computer programming, volume 2: Seminumerical algorithms.*
537565Addison-Wesley Professional. Third edition.
538566"""
539- function content (poly:: APL{T} , algo:: AbstractUnivariateGCDAlgorithm ) where {T}
567+ function content (poly:: APL{T} , algo:: AbstractUnivariateGCDAlgorithm , mutability :: MA.MutableTrait ) where {T}
540568 P = MA. promote_operation (gcd, T, T)
541- # This is tricky to infer a `content` calls `gcd` which calls `content`, etc...
542- # To help Julia break the loop, we annotate the result here.
543569 coefs = coefficients (poly)
544- length (coefs) == 0 && return zero (T )
545- length (coefs) == 1 && return first (coefs)
570+ isempty (coefs) && return zero (P )
571+ length (coefs) == 1 && return convert (P, _copy ( first (coefs), mutability) )
546572 # Largely inspired from from `YingboMa/SIMDPolynomials.jl`.
547573 if T <: APL
548574 for i in eachindex (coefs)
549575 if nterms (coefs[i]) == 1
550- g = gcd (termwise_content (coefs[1 ]), termwise_content (coefs[2 ]))
576+ g = _gcd (termwise_content (coefs[1 ], algo, mutability ), termwise_content (coefs[2 ], algo, mutability), algo, MA . IsMutable (), MA . IsMutable ( ))
551577 isone (g) || for i in 3 : length (coefs)
552- g = gcd (g, termwise_content (coefs[i]))
578+ g = _gcd (g, termwise_content (coefs[i], algo, mutability), algo, MA . IsMutable (), MA . IsMutable ( ))
553579 isone (g) && break
554580 end
555581 return convert (P, g)
556582 end
557583 end
558584 end
559- g = gcd (coefs[1 ], coefs[2 ]):: P
585+ # This is tricky to infer a `content` calls `gcd` which calls `content`, etc...
586+ # To help Julia break the loop, we annotate the result here.
587+ g = _gcd (coefs[1 ], coefs[2 ], algo, mutability, mutability):: P
560588 isone (g) || for i in 3 : length (coefs)
561- g = _simplifier (g, coefs[i], algo):: P
589+ g = _simplifier (g, coefs[i], algo, MA . IsMutable (), mutability ):: P
562590 isone (g) && break
563591 end
564592 return g:: P
565593end
566- function content (poly:: APL{T} , algo :: AbstractUnivariateGCDAlgorithm ) where {T<: AbstractFloat }
594+ function content (poly:: APL{T} , :: AbstractUnivariateGCDAlgorithm , :: MA.MutableTrait ) where {T<: AbstractFloat }
567595 return one (T)
568596end
569597
570598"""
571- primitive_part_content (poly::AbstractPolynomialLike{T}, algo::AbstractUnivariateGCDAlgorithm) where {T}
599+ primitive_part (poly::AbstractPolynomialLike{T}, algo::AbstractUnivariateGCDAlgorithm) where {T}
572600
573601Return the *primitive part* of the polynomial `poly` over a unique
574602factorization domain `S` as defined in [Knu14, (3) p. 423].
@@ -580,8 +608,8 @@ instead.
580608*Art of computer programming, volume 2: Seminumerical algorithms.*
581609Addison-Wesley Professional. Third edition.
582610"""
583- primitive_part (p:: APL , algo:: AbstractUnivariateGCDAlgorithm ) = primitive_part_content (p, algo)[1 ]
584- primitive_part (p:: APL{<:AbstractFloat} , algo :: AbstractUnivariateGCDAlgorithm ) = p
611+ primitive_part (p:: APL , algo:: AbstractUnivariateGCDAlgorithm , mutability :: MA.MutableTrait ) = primitive_part_content (p, algo, mutability )[1 ]
612+ primitive_part (p:: APL{<:AbstractFloat} , :: AbstractUnivariateGCDAlgorithm , :: MA.MutableTrait ) = p
585613
586614"""
587615 primitive_part_content(poly::AbstractPolynomialLike{T}, algo::AbstractUnivariateGCDAlgorithm) where {T}
@@ -596,7 +624,7 @@ computing the content first and this function avoid computing the content twice.
596624*Art of computer programming, volume 2: Seminumerical algorithms.*
597625Addison-Wesley Professional. Third edition.
598626"""
599- function primitive_part_content (p, algo:: AbstractUnivariateGCDAlgorithm )
600- g = content (p, algo)
601- return mapcoefficientsnz (Base. Fix2 (_div, g), p), g
627+ function primitive_part_content (p, algo:: AbstractUnivariateGCDAlgorithm , mutability :: MA.MutableTrait )
628+ g = content (p, algo, MA . IsNotMutable () )
629+ return mapcoefficients (Base. Fix2 (_div, g), p, mutability; nonzero = true ), g
602630end
0 commit comments