Skip to content

Commit 6aab185

Browse files
authored
Exploit mutability in gcd (#217)
* Exploit mutability in gcd * Fix for DP
1 parent fed1a36 commit 6aab185

File tree

4 files changed

+128
-88
lines changed

4 files changed

+128
-88
lines changed

src/division.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ function _pseudo_rem(::UFD, f::APL, g::APL, algo)
126126
# Check with `::` that we don't have any type unstability on this variable.
127127
f = (new_f - new_g)::typeof(f)
128128
if algo.primitive_rem
129-
f = primitive_part(f, algo)::typeof(f)
129+
f = primitive_part(f, algo, MA.IsMutable())::typeof(f)
130130
end
131131
if algo.skip_last && maxdegree(f) == maxdegree(g)
132132
break

src/gcd.jl

Lines changed: 112 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
export 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

5356
Base.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.*
107114
Addison-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))
130137
end
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)))
134141
end
135142

@@ -211,36 +218,36 @@ function MA.operate(op::Union{typeof(deflate), typeof(inflate)}, p::AbstractPoly
211218
end)
212219
end
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
333340
end
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))
338345
end
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())
355377
end
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
360385
Returns the `gcd` of primitive polynomials `p` and `q` using algorithm `algo`
361386
which is a subtype of [`AbstractUnivariateGCDAlgorithm`](@ref).
362387
"""
363-
function primitive_univariate_gcd end
388+
function primitive_univariate_gcd! end
364389

365390
function not_divided_error(u, v)
366391
error(
@@ -376,9 +401,9 @@ end
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
438463
end
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")
443468
end
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].
459484
2. 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.
461486
3. 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.*
466491
Addison-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)
478503
end
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)
482507
end
483508

484509
function 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)
486511
end
487512
function 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)
494519
end
495520
function univariate_gcdx(::Field, p1::APL, p2::APL, algo::AbstractUnivariateGCDAlgorithm)
496521
return primitive_univariate_gcdx(p1, p2, algo)
497522
end
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
525550
end
526551

527552
"""
528-
content(poly::AbstractPolynomialLike{T}, algo::AbstractUnivariateGCDAlgorithm) where {T}
553+
content(poly::AbstractPolynomialLike{T}, algo::AbstractUnivariateGCDAlgorithm, mutability::MA.MutableTrait) where {T}
529554
530555
Return the *content* of the polynomial `poly` over a unique factorization
531556
domain `S` as defined in [Knu14, (3) p. 423].
532557
That is, return the `gcd` of the coefficients of `poly`.
533558
See 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.*
537565
Addison-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
565593
end
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)
568596
end
569597

570598
"""
571-
primitive_part_content(poly::AbstractPolynomialLike{T}, algo::AbstractUnivariateGCDAlgorithm) where {T}
599+
primitive_part(poly::AbstractPolynomialLike{T}, algo::AbstractUnivariateGCDAlgorithm) where {T}
572600
573601
Return the *primitive part* of the polynomial `poly` over a unique
574602
factorization domain `S` as defined in [Knu14, (3) p. 423].
@@ -580,8 +608,8 @@ instead.
580608
*Art of computer programming, volume 2: Seminumerical algorithms.*
581609
Addison-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.*
597625
Addison-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
602630
end

0 commit comments

Comments
 (0)