@@ -20,6 +20,11 @@ Algorithm computing the greatest common divisor of univariate polynomials using
2020the Euclidean algorithm generalized for polynomials with coefficients over a
2121a unique factorization domain, see [Knu14, Algorithm E, p. 426-427].
2222
23+ If `primitive_rem` is `true`, the intermediate remainders produced in the
24+ polynomial division are made primitive. If `primitive_part` is set to `false`,
25+ only the resuting remainder is made primitive (the intermediate remainders
26+ of the generalized Euclidean algorithm still need to be made primitive).
27+
2328[Knu14] Knuth, D.E., 2014.
2429*Art of computer programming, volume 2: Seminumerical algorithms.*
2530Addison-Wesley Professional. Third edition.
@@ -35,17 +40,41 @@ struct GeneralizedEuclideanAlgorithm <: AbstractUnivariateGCDAlgorithm
3540 end
3641end
3742
43+ _primitive_rem (algo:: GeneralizedEuclideanAlgorithm ) = algo. primitive_rem
44+ _skip_last (algo:: GeneralizedEuclideanAlgorithm ) = algo. skip_last
45+ function _set_skipped_divisions! (:: GeneralizedEuclideanAlgorithm , :: Int ) end
46+
3847"""
39- struct SubresultantAlgorithm <: AbstractUnivariateGCDAlgorithm end
48+ mutable struct SubresultantAlgorithm <: AbstractUnivariateGCDAlgorithm
49+ skipped_divisions::Int
50+ end
4051
4152Algorithm computing the greatest common divisor of univariate polynomials using
4253the Subresultant algorithm, see [Knu14, Algorithm C, p. 428-429].
4354
55+ The division by `g*h^δ` in the algorithm only works if the iteration of
56+ [Knu14, Algorithm R, p. 426] is carried out even when the divided polynomial
57+ has a zero term. For computational savings, we don't do that so we store
58+ in `skipped_division` the number of skipped divisions so that the division
59+ by `g*h^δ` can be adapted accordingly.
60+
61+ In [Knu14, Algorithm C, p. 426], it is stated that there should be ``
62+
4463[Knu14] Knuth, D.E., 2014.
4564*Art of computer programming, volume 2: Seminumerical algorithms.*
4665Addison-Wesley Professional. Third edition.
4766"""
48- struct SubresultantAlgorithm <: AbstractUnivariateGCDAlgorithm end
67+ mutable struct SubresultantAlgorithm <: AbstractUnivariateGCDAlgorithm
68+ skipped_divisions:: Int
69+ SubresultantAlgorithm () = new (0 )
70+ end
71+
72+ _primitive_rem (:: SubresultantAlgorithm ) = false
73+ _skip_last (:: SubresultantAlgorithm ) = false
74+ function _set_skipped_divisions! (algo:: SubresultantAlgorithm , n:: Int )
75+ algo. skipped_divisions = n
76+ return
77+ end
4978
5079_coefficient_gcd (α, β) = gcd (α, β)
5180_coefficient_gcd (α:: AbstractFloat , β) = one (Base. promote_typeof (α, β))
5786function Base. lcm (
5887 p:: _APL ,
5988 q:: _APL ,
60- algo:: AbstractUnivariateGCDAlgorithm = GeneralizedEuclideanAlgorithm (),
89+ algo:: AbstractUnivariateGCDAlgorithm = SubresultantAlgorithm (),
6190)
6291 return p * div (q, gcd (p, q, algo))
6392end
6493function Base. gcd (
6594 α,
6695 p:: _APL ,
67- algo:: AbstractUnivariateGCDAlgorithm = GeneralizedEuclideanAlgorithm (),
68- mα :: MA.MutableTrait = MA. IsNotMutable (),
96+ algo:: AbstractUnivariateGCDAlgorithm = SubresultantAlgorithm (),
97+ :: MA.MutableTrait = MA. IsNotMutable (),
6998 mp:: MA.MutableTrait = MA. IsNotMutable (),
7099)
71100 return _coefficient_gcd (α, content (p, algo, mp))
72101end
73102function Base. gcd (
74103 p:: _APL ,
75104 α,
76- algo:: AbstractUnivariateGCDAlgorithm = GeneralizedEuclideanAlgorithm (),
105+ algo:: AbstractUnivariateGCDAlgorithm = SubresultantAlgorithm (),
77106 mp:: MA.MutableTrait = MA. IsNotMutable (),
78107 mα:: MA.MutableTrait = MA. IsNotMutable (),
79108)
@@ -87,7 +116,7 @@ function MA.promote_operation(
87116 :: typeof (gcd),
88117 P:: Type{<:_APL} ,
89118 Q:: Type{<:_APL} ,
90- A:: Type = GeneralizedEuclideanAlgorithm ,
119+ A:: Type = SubresultantAlgorithm ,
91120)
92121 return MA. promote_operation (rem_or_pseudo_rem, P, Q, A)
93122end
@@ -140,7 +169,7 @@ Addison-Wesley Professional. Third edition.
140169function Base. gcd (
141170 p1:: _APL{T} ,
142171 p2:: _APL{S} ,
143- algo:: AbstractUnivariateGCDAlgorithm = GeneralizedEuclideanAlgorithm (),
172+ algo:: AbstractUnivariateGCDAlgorithm = SubresultantAlgorithm (),
144173 m1:: MA.MutableTrait = MA. IsNotMutable (),
145174 m2:: MA.MutableTrait = MA. IsNotMutable (),
146175) where {T,S}
179208function Base. gcd (
180209 t1:: AbstractTermLike{T} ,
181210 t2:: AbstractTermLike{S} ,
182- algo :: AbstractUnivariateGCDAlgorithm = GeneralizedEuclideanAlgorithm (),
211+ :: AbstractUnivariateGCDAlgorithm = SubresultantAlgorithm (),
183212 m1:: MA.MutableTrait = MA. IsNotMutable (),
184213 m2:: MA.MutableTrait = MA. IsNotMutable (),
185214) where {T,S}
@@ -558,6 +587,98 @@ function primitive_univariate_gcd!(
558587 end
559588end
560589
590+ function _pow_no_copy (a, b)
591+ if isone (b)
592+ # `a^1` is `copy(a)` but that copy is not needed here
593+ return a
594+ else
595+ return a^ b
596+ end
597+ end
598+
599+ function primitive_univariate_gcd! (
600+ p:: _APL ,
601+ q:: _APL ,
602+ algo:: SubresultantAlgorithm ,
603+ )
604+ if maxdegree (p) < maxdegree (q)
605+ return primitive_univariate_gcd! (q, p, algo)
606+ end
607+ R = MA. promote_operation (gcd, typeof (p), typeof (q))
608+ u = convert (R, p)
609+ v = convert (R, q)
610+ if isapproxzero (v)
611+ return primitive_part (u, algo, MA. IsMutable ()):: R
612+ elseif isconstant (v)
613+ # `p` and `q` are primitive so if one of them is constant, it cannot
614+ # divide the content of the other one.
615+ return MA. operate! (one, u)
616+ end
617+ g = h = nothing # `nothing` means `1`
618+ while true
619+ δ = maxdegree (u) - maxdegree (v)
620+ d_before = degree (leading_monomial (u))
621+ r = MA. operate! (rem_or_pseudo_rem, u, v, algo)
622+ if isapproxzero (r)
623+ return primitive_part (v, algo, MA. IsMutable ()):: R
624+ elseif isconstant (r)
625+ return MA. operate! (one, v)
626+ end
627+
628+ d_after = degree (leading_monomial (r))
629+ if d_after == d_before
630+ not_divided_error (u, v)
631+ end
632+ if ! isnothing (g)
633+ if isnothing (h) # equivalent to `iszero(δ)`
634+ ghδ = g
635+ else
636+ ghδ = g * _pow_no_copy (h, δ)
637+ end
638+ if ! iszero (algo. skipped_divisions)
639+ @assert algo. skipped_divisions > 0
640+ if isnothing (h)
641+ # It is an alias to `g`
642+ ghδ = MA. copy_if_mutable (ghδ)
643+ end
644+ # TODO not sure this works, sometimes it `ghδ` is not a multiple of `leading_coefficient(v)`
645+ # we just know it divides the content of `r`, it is not guaranteed to be equal to the content of `r`
646+ # We could maybe do better than multiply `r` here though but let's start with this approach as a baseline
647+ # ghδ = div_multiple(ghδ, _pow_no_copy(leading_coefficient(v), algo.skipped_divisions), MA.IsMutable())
648+ r = MA. operate! (
649+ right_constant_mult,
650+ r,
651+ _pow_no_copy (
652+ leading_coefficient (v),
653+ algo. skipped_divisions,
654+ ),
655+ )
656+ end
657+ r = right_constant_div_multiple (r, ghδ, MA. IsMutable ()):: R
658+ end
659+ u, v = v, r:: R
660+ g = leading_coefficient (u)
661+ # Computes `h = h^(1 - δ) * g^δ` (step C3) of [Knu14, Algorithm C p. 429]
662+ # If `δ` is zero then `h^(1 - δ) * g^δ = h` so there is nothing to do
663+ if δ == 1
664+ h = g
665+ elseif δ > 1
666+ if isnothing (h) || δ == 2
667+ # `h^1` is `copy(h)` but that copy is not needed here
668+ hδ = h
669+ else
670+ hδ = h^ (δ - 1 )
671+ end
672+ if isnothing (h)
673+ h = g
674+ else
675+ # We assume that `g^δ` is mutable since `δ > 1`
676+ h = div_multiple (g^ δ, hδ, MA. IsMutable ())
677+ end
678+ end
679+ end
680+ end
681+
561682function primitive_univariate_gcdx (
562683 u0:: _APL ,
563684 v0:: _APL ,
@@ -597,7 +718,7 @@ function primitive_univariate_gcdx(
597718 return p * b, (a - b * q), g
598719end
599720
600- function primitive_univariate_gcd! (p:: _APL , q:: _APL , :: SubresultantAlgorithm )
721+ function primitive_univariate_gcdx (p:: _APL , q:: _APL , :: SubresultantAlgorithm )
601722 return error (" Not implemented yet" )
602723end
603724
0 commit comments