@@ -20,6 +20,11 @@ Algorithm computing the greatest common divisor of univariate polynomials using
20
20
the Euclidean algorithm generalized for polynomials with coefficients over a
21
21
a unique factorization domain, see [Knu14, Algorithm E, p. 426-427].
22
22
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
+
23
28
[Knu14] Knuth, D.E., 2014.
24
29
*Art of computer programming, volume 2: Seminumerical algorithms.*
25
30
Addison-Wesley Professional. Third edition.
@@ -35,17 +40,41 @@ struct GeneralizedEuclideanAlgorithm <: AbstractUnivariateGCDAlgorithm
35
40
end
36
41
end
37
42
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
+
38
47
"""
39
- struct SubresultantAlgorithm <: AbstractUnivariateGCDAlgorithm end
48
+ mutable struct SubresultantAlgorithm <: AbstractUnivariateGCDAlgorithm
49
+ skipped_divisions::Int
50
+ end
40
51
41
52
Algorithm computing the greatest common divisor of univariate polynomials using
42
53
the Subresultant algorithm, see [Knu14, Algorithm C, p. 428-429].
43
54
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
+
44
63
[Knu14] Knuth, D.E., 2014.
45
64
*Art of computer programming, volume 2: Seminumerical algorithms.*
46
65
Addison-Wesley Professional. Third edition.
47
66
"""
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
49
78
50
79
_coefficient_gcd (α, β) = gcd (α, β)
51
80
_coefficient_gcd (α:: AbstractFloat , β) = one (Base. promote_typeof (α, β))
57
86
function Base. lcm (
58
87
p:: _APL ,
59
88
q:: _APL ,
60
- algo:: AbstractUnivariateGCDAlgorithm = GeneralizedEuclideanAlgorithm (),
89
+ algo:: AbstractUnivariateGCDAlgorithm = SubresultantAlgorithm (),
61
90
)
62
91
return p * div (q, gcd (p, q, algo))
63
92
end
64
93
function Base. gcd (
65
94
α,
66
95
p:: _APL ,
67
- algo:: AbstractUnivariateGCDAlgorithm = GeneralizedEuclideanAlgorithm (),
68
- mα :: MA.MutableTrait = MA. IsNotMutable (),
96
+ algo:: AbstractUnivariateGCDAlgorithm = SubresultantAlgorithm (),
97
+ :: MA.MutableTrait = MA. IsNotMutable (),
69
98
mp:: MA.MutableTrait = MA. IsNotMutable (),
70
99
)
71
100
return _coefficient_gcd (α, content (p, algo, mp))
72
101
end
73
102
function Base. gcd (
74
103
p:: _APL ,
75
104
α,
76
- algo:: AbstractUnivariateGCDAlgorithm = GeneralizedEuclideanAlgorithm (),
105
+ algo:: AbstractUnivariateGCDAlgorithm = SubresultantAlgorithm (),
77
106
mp:: MA.MutableTrait = MA. IsNotMutable (),
78
107
mα:: MA.MutableTrait = MA. IsNotMutable (),
79
108
)
@@ -87,7 +116,7 @@ function MA.promote_operation(
87
116
:: typeof (gcd),
88
117
P:: Type{<:_APL} ,
89
118
Q:: Type{<:_APL} ,
90
- A:: Type = GeneralizedEuclideanAlgorithm ,
119
+ A:: Type = SubresultantAlgorithm ,
91
120
)
92
121
return MA. promote_operation (rem_or_pseudo_rem, P, Q, A)
93
122
end
@@ -140,7 +169,7 @@ Addison-Wesley Professional. Third edition.
140
169
function Base. gcd (
141
170
p1:: _APL{T} ,
142
171
p2:: _APL{S} ,
143
- algo:: AbstractUnivariateGCDAlgorithm = GeneralizedEuclideanAlgorithm (),
172
+ algo:: AbstractUnivariateGCDAlgorithm = SubresultantAlgorithm (),
144
173
m1:: MA.MutableTrait = MA. IsNotMutable (),
145
174
m2:: MA.MutableTrait = MA. IsNotMutable (),
146
175
) where {T,S}
179
208
function Base. gcd (
180
209
t1:: AbstractTermLike{T} ,
181
210
t2:: AbstractTermLike{S} ,
182
- algo :: AbstractUnivariateGCDAlgorithm = GeneralizedEuclideanAlgorithm (),
211
+ :: AbstractUnivariateGCDAlgorithm = SubresultantAlgorithm (),
183
212
m1:: MA.MutableTrait = MA. IsNotMutable (),
184
213
m2:: MA.MutableTrait = MA. IsNotMutable (),
185
214
) where {T,S}
@@ -558,6 +587,98 @@ function primitive_univariate_gcd!(
558
587
end
559
588
end
560
589
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
+
561
682
function primitive_univariate_gcdx (
562
683
u0:: _APL ,
563
684
v0:: _APL ,
@@ -597,7 +718,7 @@ function primitive_univariate_gcdx(
597
718
return p * b, (a - b * q), g
598
719
end
599
720
600
- function primitive_univariate_gcd! (p:: _APL , q:: _APL , :: SubresultantAlgorithm )
721
+ function primitive_univariate_gcdx (p:: _APL , q:: _APL , :: SubresultantAlgorithm )
601
722
return error (" Not implemented yet" )
602
723
end
603
724
0 commit comments