Skip to content
This repository was archived by the owner on Jan 30, 2023. It is now read-only.

Commit 87499b2

Browse files
committed
Fix inverse_mod for univariate polynomials over ZZ mod n for composite n.
This fixes trac 15788 and a failure noted in trac 22237.
1 parent 03f3642 commit 87499b2

File tree

2 files changed

+126
-1
lines changed

2 files changed

+126
-1
lines changed

src/sage/rings/polynomial/polynomial_element.pyx

Lines changed: 115 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ from sage.structure.element cimport (parent, have_same_parent,
8989
from sage.rings.rational_field import QQ, is_RationalField
9090
from sage.rings.integer_ring import ZZ, is_IntegerRing
9191
from sage.rings.integer cimport Integer, smallInteger
92+
from sage.rings.finite_rings.integer_mod_ring import is_IntegerModRing
9293
from sage.libs.gmp.mpz cimport *
9394
from sage.rings.fraction_field import is_FractionField
9495
from sage.rings.padics.generic_nodes import is_pAdicRing, is_pAdicField
@@ -1373,9 +1374,27 @@ cdef class Polynomial(CommutativeAlgebraElement):
13731374
system for the coefficients of s and t for inexact rings (as the
13741375
Euclidean algorithm may not converge in that case).
13751376
1377+
In the case of polynomial rings over Zmod(k), factors k and uses
1378+
the Chinese Remainder Theorem to combine inverses mod prime power
1379+
divisors. The first such computation may be slow while k is factored.
1380+
13761381
AUTHORS:
13771382
13781383
- Robert Bradshaw (2007-05-31)
1384+
1385+
TESTS:
1386+
1387+
Check that :trac: `15788` is fixed:
1388+
1389+
sage: K.<t> = PolynomialRing(IntegerModRing(42), 't', implementation='NTL')
1390+
sage: (t^2+1).inverse_mod(t^2)
1391+
1
1392+
1393+
And an error noted in :trac: `22237`:
1394+
1395+
sage: _.<x> = Zmod(4)[]
1396+
sage: (2*x + 1).inverse_mod(x^2 + x + 1)
1397+
2*x + 1
13791398
"""
13801399
from sage.rings.ideal import is_Ideal
13811400
if is_Ideal(m):
@@ -1392,7 +1411,11 @@ cdef class Polynomial(CommutativeAlgebraElement):
13921411
if u.is_unit():
13931412
return a.parent()(~u)
13941413
if a.parent().is_exact():
1395-
# use xgcd
1414+
R = a.parent().base_ring()
1415+
if is_IntegerModRing(R) and not R.is_field():
1416+
# xgcd is not well defined, use an alternative algorithm, broken out below
1417+
return _inverse_mod_in_integer_mod_polynomial_ring(a,m)
1418+
# might be other failing cases, but we try xgcd
13961419
g, s, _ = a.xgcd(m)
13971420
if g == 1:
13981421
return s
@@ -10301,3 +10324,94 @@ cdef class PolynomialBaseringInjection(Morphism):
1030110324
<type 'sage.rings.polynomial.polynomial_element.ConstantPolynomialSection'>
1030210325
"""
1030310326
return ConstantPolynomialSection(self._codomain, self.domain())
10327+
10328+
# In the rings Zmod(n)['x'], the above implementation of
10329+
# inverse_mod will fail, as 'xgcd' is troublesome (not
10330+
# all ideals are principal). An alternative implementation
10331+
# avoids this problem. It is defined here so that it can be
10332+
# used by both polynomial_zmod_flint, polynomial_dense_modn_ntl_zz,
10333+
# and polynomial_dense_modn_ntl_ZZ. 'Polynomial' is the closest common
10334+
# superclass.
10335+
10336+
# For some discussion on xgcd in rings that are not PIDs, such as we have here,
10337+
# see trac #17674 and https://groups.google.com/forum/#!topic/sage-devel/JV8fCPUqTzo.
10338+
10339+
cdef _inverse_mod_prime_power(a, m, p, k):
10340+
'''return i so that a*i % m = 1 mod p^k, if such an i exists.
10341+
Otherwise raise an exception.
10342+
10343+
INPUT:
10344+
10345+
`a` - a polynomial over ZZ
10346+
`m` - a polynomial over ZZ
10347+
`p` - a prime number
10348+
`k` - a positive integer
10349+
10350+
OUTPUT:
10351+
10352+
A polynomial `i` over ZZ, with coefficients reduced mod p^k,
10353+
such that a*i = 1 mod (m, p^k), when such a result exists.
10354+
10355+
TBD: citation for the algorithm. Hensel lifting is used
10356+
after solving for the inverse mod p using the extended gcd.'''
10357+
from sage.rings.finite_rings.integer_mod_ring import IntegerModRing
10358+
zp = IntegerModRing(p)
10359+
(g,s,t) = a.change_ring(zp).xgcd(m.change_ring(zp))
10360+
s = s.change_ring(ZZ)
10361+
t = t.change_ring(ZZ)
10362+
if g != 1:
10363+
raise Exception("Impossible inverse")
10364+
pj = p
10365+
j = 1
10366+
while j < k:
10367+
r = s*a + t*m - 1
10368+
# invariant: s*a + t*m = 1 + r and p^j | r and j is a power of 2
10369+
pj = pj**2
10370+
f = 1 - r
10371+
s = (f*s) % pj
10372+
t = (f*t) % pj
10373+
j = 2*j
10374+
return s
10375+
10376+
cdef _inverse_mod_in_integer_mod_polynomial_ring(a, modulus):
10377+
"""The inverse of the polynomial ``a`` modulo ``modulus``.
10378+
10379+
Raises Exception('Impossible inverse') if no such inverse exists.
10380+
10381+
For polynomials of an IntegerModRing, this has to factor the modulus,
10382+
(once, and then the factorization is cached), so can be slow when
10383+
the modulus is large.
10384+
10385+
EXAMPLES::
10386+
10387+
sage: _.<x> = Zmod(8)[]
10388+
sage: (1+x).inverse_mod(x^4 + x^2 - 3) # indirect doctest
10389+
x^3 + 7*x^2 + 2*x + 6
10390+
sage: (x+1).inverse_mod(x^2 + 2*x + 1) # indirect doctest
10391+
Traceback (most recent call last):
10392+
...
10393+
Exception: Impossible inverse
10394+
sage: y = PolynomialRing(Zmod(360),'y',implementation='NTL').gen() # NTL_zz
10395+
sage: a = 79*y^2 - 8*y + 4
10396+
sage: m = y^7 + 1
10397+
sage: i = a.inverse_mod(m) # indirect doctest
10398+
sage: i
10399+
352*y^6 + 305*y^5 + 168*y^4 + 316*y^3 + 224*y^2 + 312*y + 280
10400+
sage: (a * i) % m == 1
10401+
True
10402+
sage: _.<z> = Zmod(3^4 * 17^9 * 97^3 * 1009 * 2003^5)[] # big enough to be NTL_ZZ
10403+
sage: a = z^5 + 1
10404+
sage: m = z^18 - 100*z
10405+
sage: i = a.inverse_mod(m) # indirect doctest
10406+
sage: (a * i) % m == 1
10407+
True
10408+
"""
10409+
from sage.arith.misc import crt
10410+
R = a.parent()
10411+
az = a.change_ring(ZZ)
10412+
mz = modulus.change_ring(ZZ)
10413+
factors = R.factored_modulus()
10414+
inverses = [_inverse_mod_prime_power(az,mz, p, k) for p,k in factors]
10415+
if len(inverses) == 1:
10416+
return R(inverses[0]) % modulus # CRT does not do this modular reduction step.
10417+
return R(crt(inverses, [p**k for p,k in factors])) % modulus

src/sage/rings/polynomial/polynomial_ring.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2638,6 +2638,17 @@ def modulus(self):
26382638
"""
26392639
return self.base_ring().characteristic()
26402640

2641+
@cached_method
2642+
def factored_modulus(self):
2643+
"""
2644+
EXAMPLES::
2645+
2646+
sage: R.<x> = Zmod(150)[]
2647+
sage: R.factored_modulus()
2648+
2 * 3 * 5^2
2649+
"""
2650+
return self.modulus().factor()
2651+
26412652
def _repr_(self):
26422653
"""
26432654
TESTS::

0 commit comments

Comments
 (0)