Skip to content

Commit 5cde13f

Browse files
authored
Implement univariate extended gcd (#178)
* Implement univariate extended gcd * Fix pseudo division with f becoming zero * Fixes
1 parent 4a31d39 commit 5cde13f

File tree

3 files changed

+143
-16
lines changed

3 files changed

+143
-16
lines changed

src/division.jl

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,31 @@ end
5656
Base.div(f::APL, g::Union{APL, AbstractVector{<:APL}}; kwargs...) = divrem(f, g; kwargs...)[1]
5757
Base.rem(f::APL, g::Union{APL, AbstractVector{<:APL}}; kwargs...) = divrem(f, g; kwargs...)[2]
5858

59+
function pseudo_divrem(f::APL{S}, g::APL{T}, algo) where {S,T}
60+
return _pseudo_divrem(algebraic_structure(S, T), f, g, algo)
61+
end
62+
63+
function _pseudo_divrem(::Field, f::APL, g::APL, algo)
64+
q, r = divrem(f, g)
65+
return one(q), q, r
66+
end
67+
68+
function _pseudo_divrem(::UFD, f::APL, g::APL, algo)
69+
ltg = leadingterm(g)
70+
rg = removeleadingterm(g)
71+
ltf = leadingterm(f)
72+
if iszero(f) || !divides(monomial(ltg), ltf)
73+
return one(f), zero(f), zero(f)
74+
else
75+
st = constantterm(coefficient(ltg), f)
76+
new_f = st * removeleadingterm(f)
77+
qt = term(coefficient(ltf), _div(monomial(ltf), monomial(ltg)))
78+
new_g = qt * rg
79+
# Check with `::` that we don't have any type unstability on this variable.
80+
return convert(typeof(f), st), convert(typeof(f), qt), (new_f - new_g)::typeof(f)
81+
end
82+
end
83+
5984
function pseudo_rem(f::APL{S}, g::APL{T}, algo) where {S,T}
6085
return _pseudo_rem(algebraic_structure(S, T), f, g, algo)
6186
end
@@ -71,7 +96,7 @@ function _pseudo_rem(::UFD, f::APL, g::APL, algo)
7196
if !divides(monomial(ltg), ltf)
7297
return false, f
7398
end
74-
while divides(monomial(ltg), ltf)
99+
while !iszero(f) && divides(monomial(ltg), ltf)
75100
new_f = constantterm(coefficient(ltg), f) * removeleadingterm(f)
76101
new_g = term(coefficient(ltf), _div(monomial(ltf), monomial(ltg))) * rg
77102
# Check with `::` that we don't have any type unstability on this variable.

src/gcd.jl

Lines changed: 93 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,35 @@ function deflated_gcd(p1::APL{T}, p2::APL{S}, algo) where {T, S}
237237
end
238238
end
239239

240+
function Base.gcdx(p1::APL{T}, p2::APL{S}, algo::AbstractUnivariateGCDAlgorithm=GeneralizedEuclideanAlgorithm()) where {T, S}
241+
i1, i2, num_common = _extracted_variable(p1, p2)
242+
R = MA.promote_operation(gcd, typeof(p1), typeof(p2))
243+
if iszero(i1)
244+
if iszero(i2)
245+
return univariate_gcdx(p1, p2, algo)
246+
else
247+
if isapproxzero(p1)
248+
return zero(R), one(R), convert(R, p2)
249+
end
250+
error("Not implemented yet")
251+
end
252+
else
253+
if iszero(i2)
254+
if isapproxzero(p2)
255+
return one(R), zero(R), convert(R, p1)
256+
end
257+
error("Not implemented yet")
258+
else
259+
if num_common > 1
260+
@assert i1 == i2
261+
error("Not implemented yet")
262+
else
263+
return univariate_gcdx(p1, p2, algo)
264+
end
265+
end
266+
end
267+
end
268+
240269
# Returns first element in the union of two decreasing vectors
241270
function _extracted_variable(p1, p2)
242271
v1 = variables(p1)
@@ -324,6 +353,18 @@ which is a subtype of [`AbstractUnivariateGCDAlgorithm`](@ref).
324353
"""
325354
function primitive_univariate_gcd end
326355

356+
function not_divided_error(u, v)
357+
error(
358+
"Polynomial `$v` of degree `$(maxdegree(v))` and effective",
359+
" variables `$(effective_variables(v))` does not divide",
360+
" polynomial `$u` of degree `$(maxdegree(u))` and effective",
361+
" variables `$(effective_variables(u))`. Did you call",
362+
" `univariate_gcd` with polynomials with more than one",
363+
" variable in common ? If yes, call `gcd` instead, otherwise,",
364+
" please report this.",
365+
)
366+
end
367+
327368
# If `p` and `q` do not have the same time then the local variables `p` and `q`
328369
# won't be type stable.
329370
function primitive_univariate_gcd(p::APL, q::APL, algo::GeneralizedEuclideanAlgorithm)
@@ -343,15 +384,7 @@ function primitive_univariate_gcd(p::APL, q::APL, algo::GeneralizedEuclideanAlgo
343384
end
344385
divided, r = pseudo_rem(u, v, algo)
345386
if !divided
346-
error(
347-
"Polynomial `$v` of degree `$(maxdegree(v))` and effective",
348-
" variables `$(effective_variables(v))` does not divide",
349-
" polynomial `$u` of degree `$(maxdegree(u))` and effective",
350-
" variables `$(effective_variables(u))`. Did you call",
351-
" `univariate_gcd` with polynomials with more than one",
352-
" variable in common ? If yes, call `gcd` instead, otherwise,",
353-
" please report this.",
354-
)
387+
not_divided_error(u, v)
355388
end
356389
if !algo.primitive_rem
357390
r = primitive_part(r, algo)::R
@@ -360,6 +393,42 @@ function primitive_univariate_gcd(p::APL, q::APL, algo::GeneralizedEuclideanAlgo
360393
end
361394
end
362395

396+
function primitive_univariate_gcdx(u0::APL, v0::APL, algo::GeneralizedEuclideanAlgorithm)
397+
if maxdegree(u0) < maxdegree(v0)
398+
a, b, g = primitive_univariate_gcdx(v0, u0, algo)
399+
return b, a, g
400+
end
401+
R = MA.promote_operation(gcd, typeof(u0), typeof(v0))
402+
u = convert(R, u0)
403+
v = convert(R, v0)
404+
if isapproxzero(v)
405+
return one(R), zero(R), u
406+
elseif isconstant(v)
407+
# `p` and `q` are primitive so if one of them is constant, it cannot
408+
# divide the content of the other one.
409+
return zero(R), one(R), v
410+
end
411+
# p * u = q * v + r
412+
p, q, r = pseudo_divrem(u, v, algo)
413+
if iszero(q)
414+
not_divided_error(u, v)
415+
end
416+
if iszero(r)
417+
# Shortcut, does not change the output
418+
return zero(R), one(R), v
419+
end
420+
# TODO
421+
#if !algo.primitive_rem
422+
# r = primitive_part(r, algo)::R
423+
#end
424+
# a * v + b * r = g
425+
# a * v + b * (p * u - q * v) = g
426+
# b * p * u + (a - b * q) * v = g
427+
a, b, g = primitive_univariate_gcdx(v, r, algo)
428+
return p * b, (a - b * q), g
429+
end
430+
431+
363432
function primitive_univariate_gcd(p::APL, q::APL, ::SubresultantAlgorithm)
364433
error("Not implemented yet")
365434
end
@@ -403,6 +472,21 @@ function univariate_gcd(::Field, p1::APL, p2::APL, algo::AbstractUnivariateGCDAl
403472
return primitive_univariate_gcd(p1, p2, algo)
404473
end
405474

475+
function univariate_gcdx(p1::APL{S}, p2::APL{T}, algo::AbstractUnivariateGCDAlgorithm) where {S,T}
476+
return univariate_gcdx(algebraic_structure(S, T), p1, p2, algo)
477+
end
478+
function univariate_gcdx(::UFD, p1::APL, p2::APL, algo::AbstractUnivariateGCDAlgorithm)
479+
f1, g1 = primitive_part_content(p1, algo)
480+
f2, g2 = primitive_part_content(p2, algo)
481+
a, b, pp = primitive_univariate_gcdx(f1, f2, algo)
482+
gg = _gcd(g1, g2, algo)#::MA.promote_operation(gcd, typeof(g1), typeof(g2))
483+
# Multiply each coefficient by the gcd of the contents.
484+
return g2 * a, g1 * b, g1 * g2 * mapcoefficientsnz(Base.Fix1(*, gg), pp)
485+
end
486+
function univariate_gcdx(::Field, p1::APL, p2::APL, algo::AbstractUnivariateGCDAlgorithm)
487+
return primitive_univariate_gcdx(p1, p2, algo)
488+
end
489+
406490
_gcd(a::APL, b::APL, algo) = gcd(a, b, algo)
407491
_gcd(a, b::APL, algo) = gcd(a, b, algo)
408492
_gcd(a::APL, b, algo) = gcd(a, b, algo)

test/division.jl

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -96,15 +96,33 @@ function test_gcd_unit(expected, p1, p2, algo)
9696
@test g == expected || g == -expected
9797
end
9898

99+
function test_gcdx_unit(expected, p1, p2, algo)
100+
test_gcd_unit(expected, p1, p2, algo)
101+
a, b, g = @inferred gcdx(p1, p2, algo)
102+
# it does not make sense, in general, to speak of "the" greatest common
103+
# divisor of u and v; there is a set of greatest common divisors, each
104+
# one being a unit multiple of the others [Knu14, p. 424] so `expected` and
105+
# `-expected` are both accepted.
106+
@test iszero(MP.pseudo_rem(g, expected, algo)[2])
107+
@test a * p1 + b * p2 == g
108+
end
109+
function _test_gcdx_unit(expected, p1, p2, algo)
110+
test_gcdx_unit(expected, p1, p2, algo)
111+
test_gcdx_unit(expected, p2, p1, algo)
112+
end
113+
99114

100115
function univariate_gcd_test(algo=GeneralizedEuclideanAlgorithm())
101116
Mod.@polyvar x
102-
test_gcd_unit(x + 1, x^2 - 1, x^2 + 2x + 1, algo)
103-
test_gcd_unit(x + 1, x^2 + 2x + 1, x^2 - 1, algo)
104-
test_gcd_unit(x + 1, x^2 - 1, x + 1, algo)
105-
test_gcd_unit(x + 1, x + 1, x^2 - 1, algo)
106-
test_gcd_unit(x + 1, x - x, x + 1, algo)
107-
test_gcd_unit(x + 1, x + 1, x - x, algo)
117+
test_gcdx_unit(x + 1, x^2 - 1, x^2 + 2x + 1, algo)
118+
test_gcdx_unit(x + 1, x^2 + 2x + 1, x^2 - 1, algo)
119+
test_gcdx_unit(x + 1, x^2 - 1, x + 1, algo)
120+
test_gcdx_unit(x + 1, x + 1, x^2 - 1, algo)
121+
test_gcdx_unit(x + 1, x - x, x + 1, algo)
122+
test_gcdx_unit(x + 1, x + 1, x - x, algo)
123+
test_gcdx_unit(x - x + 1, x + 1, x + 2, algo)
124+
test_gcdx_unit(x - x + 1, x + 1, 2x + 1, algo)
125+
test_gcdx_unit(x - x + 1, x - 1, 2x^2 - 2x - 2, algo)
108126
@test 0 == @inferred gcd(x - x, x^2 - x^2, algo)
109127
@test 0 == @inferred gcd(x^2 - x^2, x - x, algo)
110128
end

0 commit comments

Comments
 (0)