@@ -89,6 +89,7 @@ from sage.structure.element cimport (parent, have_same_parent,
8989from sage.rings.rational_field import QQ, is_RationalField
9090from sage.rings.integer_ring import ZZ, is_IntegerRing
9191from sage.rings.integer cimport Integer, smallInteger
92+ from sage.rings.finite_rings.integer_mod_ring import is_IntegerModRing
9293from sage.libs.gmp.mpz cimport *
9394from sage.rings.fraction_field import is_FractionField
9495from 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
0 commit comments