From c76120c531c631525a8bcdc57737a6e4e8e09347 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Oudompheng?= Date: Sat, 6 Sep 2025 09:44:58 +0200 Subject: [PATCH 1/7] Add mul_low and pow_trunc methods to nmod_poly --- src/flint/types/nmod_poly.pyx | 54 +++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/flint/types/nmod_poly.pyx b/src/flint/types/nmod_poly.pyx index a3650323..68ded8f4 100644 --- a/src/flint/types/nmod_poly.pyx +++ b/src/flint/types/nmod_poly.pyx @@ -724,6 +724,60 @@ cdef class nmod_poly(flint_poly): ) return res + def mul_low(self, other, slong n): + r""" + Returns the lowest ``n`` coefficients of the multiplication of ``self`` with ``other`` + + Equivalent to computing `f(x) \cdot g(x) \mod x^n` + + >>> f = nmod_poly([2,3,5,7,11], 163) + >>> g = nmod_poly([1,2,4,8,16], 163) + >>> f.mul_low(g, 5) + 101*x^4 + 45*x^3 + 19*x^2 + 7*x + 2 + >>> f.mul_low(g, 3) + 19*x^2 + 7*x + 2 + >>> f.mul_low(g, 1) + 2 + """ + # Only allow multiplication with other nmod_poly + if not typecheck(other, nmod_poly): + raise TypeError("other polynomial must be of type nmod_poly") + + if (self).val.mod.n != (other).val.mod.n: + raise ValueError("cannot multiply nmod_polys with different moduli") + + cdef nmod_poly res = nmod_poly.__new__(nmod_poly) + res = nmod_poly.__new__(nmod_poly) + nmod_poly_init_preinv(res.val, self.val.mod.n, self.val.mod.ninv) + nmod_poly_mullow(res.val, self.val, (other).val, n) + return res + + def pow_trunc(self, slong e, slong n): + r""" + Returns ``self`` raised to the power ``e`` modulo `x^n`: + :math:`f^e \mod x^n`/ + + Note: For exponents larger that 2^63 (which do not fit inside a slong) use the + method :meth:`~.pow_mod` with the explicit modulus `x^n`. + + >>> f = nmod_poly([65, 44, 70, 33, 76, 104, 30], 163) + >>> x = nmod_poly([0, 1], 163) + >>> f.pow_trunc(2**20, 30) == pow(f, 2**20, x**30) + True + >>> f.pow_trunc(2**20, 5) + 132*x^4 + 113*x^3 + 36*x^2 + 48*x + 6 + >>> f.pow_trunc(5**25, 5) + 147*x^4 + 98*x^3 + 95*x^2 + 33*x + 126 + """ + if e < 0: + raise ValueError("Exponent must be non-negative") + + cdef nmod_poly res = nmod_poly.__new__(nmod_poly) + res = nmod_poly.__new__(nmod_poly) + nmod_poly_init_preinv(res.val, self.val.mod.n, self.val.mod.ninv) + nmod_poly_pow_trunc(res.val, self.val, e, n) + return res + def gcd(self, other): """ Returns the monic greatest common divisor of self and other. From 9dec4143bad8cfdf77080a93d64b7cff159bed36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Oudompheng?= Date: Sat, 6 Sep 2025 09:58:54 +0200 Subject: [PATCH 2/7] Add mul_low and pow_trunc methods to fmpz_poly and fmpq_poly --- src/flint/types/fmpq_poly.pyx | 47 +++++++++++++++++++++++++++++++++++ src/flint/types/fmpz_poly.pyx | 47 +++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+) diff --git a/src/flint/types/fmpq_poly.pyx b/src/flint/types/fmpq_poly.pyx index 026ab99c..2f3ba0f3 100644 --- a/src/flint/types/fmpq_poly.pyx +++ b/src/flint/types/fmpq_poly.pyx @@ -502,6 +502,53 @@ cdef class fmpq_poly(flint_poly): fmpq_poly_pow(res.val, self.val, exp) return res + def mul_low(self, other, slong n): + r""" + Returns the lowest ``n`` coefficients of the multiplication of ``self`` with ``other`` + + Equivalent to computing `f(x) \cdot g(x) \mod x^n` + + >>> f = fmpq_poly([2,3,5,7,11]) + >>> g = fmpq_poly([1,2,4,8,16]) + >>> f.mul_low(g, 5) + 101*x^4 + 45*x^3 + 19*x^2 + 7*x + 2 + >>> f.mul_low(g, 3) + 19*x^2 + 7*x + 2 + >>> f.mul_low(g, 1) + 2 + """ + # Only allow multiplication with other fmpq_poly + if not typecheck(other, fmpq_poly): + raise TypeError("other polynomial must be of type fmpq_poly") + + cdef fmpq_poly res + res = fmpq_poly.__new__(fmpq_poly) + fmpq_poly_mullow(res.val, self.val, (other).val, n) + return res + + def pow_trunc(self, slong e, slong n): + r""" + Returns ``self`` raised to the power ``e`` modulo `x^n`: + :math:`f^e \mod x^n`/ + + Note: For exponents larger that 2^63 (which do not fit inside a slong) use the + method :meth:`~.pow_mod` with the explicit modulus `x^n`. + + >>> f = fmpq_poly([1, 2, 3]) + >>> x = fmpq_poly([0, 1]) + >>> f.pow_trunc(2**20, 4) + 1537230871828889600*x^3 + 2199024304128*x^2 + 2097152*x + 1 + >>> f.pow_trunc(5**25, 3) + 177635683940025046765804290771484375*x^2 + 596046447753906250*x + 1 + """ + if e < 0: + raise ValueError("Exponent must be non-negative") + + cdef fmpq_poly res + res = fmpq_poly.__new__(fmpq_poly) + fmpq_poly_pow_trunc(res.val, self.val, e, n) + return res + def gcd(self, other): """ Returns the greatest common divisor of *self* and *other*. diff --git a/src/flint/types/fmpz_poly.pyx b/src/flint/types/fmpz_poly.pyx index 004cd9a8..c79ed1e2 100644 --- a/src/flint/types/fmpz_poly.pyx +++ b/src/flint/types/fmpz_poly.pyx @@ -483,6 +483,53 @@ cdef class fmpz_poly(flint_poly): fmpz_poly_pow(res.val, self.val, exp) return res + def mul_low(self, other, slong n): + r""" + Returns the lowest ``n`` coefficients of the multiplication of ``self`` with ``other`` + + Equivalent to computing `f(x) \cdot g(x) \mod x^n` + + >>> f = fmpz_poly([2,3,5,7,11]) + >>> g = fmpz_poly([1,2,4,8,16]) + >>> f.mul_low(g, 5) + 101*x^4 + 45*x^3 + 19*x^2 + 7*x + 2 + >>> f.mul_low(g, 3) + 19*x^2 + 7*x + 2 + >>> f.mul_low(g, 1) + 2 + """ + # Only allow multiplication with other fmpz_poly + if not typecheck(other, fmpz_poly): + raise TypeError("other polynomial must be of type fmpz_poly") + + cdef fmpz_poly res + res = fmpz_poly.__new__(fmpz_poly) + fmpz_poly_mullow(res.val, self.val, (other).val, n) + return res + + def pow_trunc(self, slong e, slong n): + r""" + Returns ``self`` raised to the power ``e`` modulo `x^n`: + :math:`f^e \mod x^n`/ + + Note: For exponents larger that 2^63 (which do not fit inside a slong) use the + method :meth:`~.pow_mod` with the explicit modulus `x^n`. + + >>> f = fmpz_poly([1, 2, 3]) + >>> x = fmpz_poly([0, 1]) + >>> f.pow_trunc(2**20, 4) + 1537230871828889600*x^3 + 2199024304128*x^2 + 2097152*x + 1 + >>> f.pow_trunc(5**25, 3) + 177635683940025046765804290771484375*x^2 + 596046447753906250*x + 1 + """ + if e < 0: + raise ValueError("Exponent must be non-negative") + + cdef fmpz_poly res + res = fmpz_poly.__new__(fmpz_poly) + fmpz_poly_pow_trunc(res.val, self.val, e, n) + return res + def gcd(self, other): """ Returns the greatest common divisor of self and other. From 8127bcc35aec99229c216b8155af9f398a438dc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Oudompheng?= Date: Sat, 6 Sep 2025 09:31:40 +0200 Subject: [PATCH 3/7] Generic unit tests for mul_low --- src/flint/test/test_all.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/flint/test/test_all.py b/src/flint/test/test_all.py index 005cb71d..84270322 100644 --- a/src/flint/test/test_all.py +++ b/src/flint/test/test_all.py @@ -2964,6 +2964,11 @@ def setbad(obj, i, val): assert raises(lambda: P([1, 1]) ** -1, DomainError) assert raises(lambda: P([1, 1]) ** None, TypeError) # type: ignore + # Truncated operations + assert P([1, 2, 3]).mul_low(P([4, 5, 6]), 3) == P([4, 13, 28]) + assert raises(lambda: P([1, 2, 3]).mul_low(None, 3), TypeError) # type: ignore + assert raises(lambda: P([1, 2, 3]).mul_low(P([4, 5, 6]), None), TypeError) # type: ignore + # XXX: Not sure what this should do in general: p = P([1, 1]) mod = P([1, 1]) From 25c38db47f5584e1d33e9d4ac28e0b62f486f63d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Oudompheng?= Date: Sat, 6 Sep 2025 10:05:48 +0200 Subject: [PATCH 4/7] Update release notes --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 8c1cfffd..6f6746be 100644 --- a/README.md +++ b/README.md @@ -170,6 +170,9 @@ Contributors (0.9.0): Changes (0.9.0): +- [gh-322](https://github.com/flintlib/python-flint/pull/322), + Add `mul_low` and `pow_trunc` methods to `fmpz_poly`, `fmpq_poly` and + `nmod_poly`. (RO) - [gh-318](https://github.com/flintlib/python-flint/pull/318), Add emscripten build in CI. Polynomial factors and roots are now sorted into a consistent order for `nmod_poly` and From 7dc4dbd968e231b13ab3bc960dbcd9482a80d524 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Oudompheng?= Date: Sat, 6 Sep 2025 10:15:48 +0200 Subject: [PATCH 5/7] Add type hints for added mul_low and pow_trunc methods --- src/flint/types/fmpq_poly.pyi | 2 ++ src/flint/types/fmpz_poly.pyi | 2 ++ src/flint/types/nmod_poly.pyi | 2 ++ 3 files changed, 6 insertions(+) diff --git a/src/flint/types/fmpq_poly.pyi b/src/flint/types/fmpq_poly.pyi index 968d6219..1d65fada 100644 --- a/src/flint/types/fmpq_poly.pyi +++ b/src/flint/types/fmpq_poly.pyi @@ -71,6 +71,8 @@ class fmpq_poly(flint_poly[fmpq]): def left_shift(self, n: int, /) -> fmpq_poly: ... def right_shift(self, n: int, /) -> fmpq_poly: ... def truncate(self, n: int, /) -> fmpq_poly: ... + def mul_low(self, other: fmpq_poly, n: int) -> fmpq_poly: ... + def pow_trunc(self, e: int, n: int) -> fmpq_poly: ... def gcd(self, other: ifmpq_poly, /) -> fmpq_poly: ... def discriminant(self) -> fmpq: ... diff --git a/src/flint/types/fmpz_poly.pyi b/src/flint/types/fmpz_poly.pyi index 8f745809..75612f8c 100644 --- a/src/flint/types/fmpz_poly.pyi +++ b/src/flint/types/fmpz_poly.pyi @@ -59,6 +59,8 @@ class fmpz_poly(flint_poly[fmpz]): def left_shift(self, n: int, /) -> fmpz_poly: ... def right_shift(self, n: int, /) -> fmpz_poly: ... def truncate(self, n: int, /) -> fmpz_poly: ... + def mul_low(self, other: fmpz_poly, n: int) -> fmpz_poly: ... + def pow_trunc(self, e: int, n: int) -> fmpz_poly: ... def gcd(self, other: ifmpz_poly, /) -> fmpz_poly: ... def content(self) -> fmpz: ... diff --git a/src/flint/types/nmod_poly.pyi b/src/flint/types/nmod_poly.pyi index 43721968..b6574a03 100644 --- a/src/flint/types/nmod_poly.pyi +++ b/src/flint/types/nmod_poly.pyi @@ -56,6 +56,8 @@ class nmod_poly(flint_poly[nmod]): def __rdivmod__(self, other: inmod_poly) -> tuple[nmod_poly, nmod_poly]: ... def left_shift(self, n: int) -> nmod_poly: ... def right_shift(self, n: int) -> nmod_poly: ... + def mul_low(self, other: nmod_poly, n: int) -> nmod_poly: ... + def pow_trunc(self, e: int, n: int) -> nmod_poly: ... def __pow__(self, other: int, mod: inmod_poly | None = None) -> nmod_poly: ... def pow_mod( self, e: int, modulus: inmod_poly, mod_rev_inv: inmod_poly | None = None From febe154e300c26ad4e9ef97e5642d5570badee08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Oudompheng?= Date: Sun, 7 Sep 2025 07:57:23 +0200 Subject: [PATCH 6/7] Add mul_low and pow_trunc to protocol epoly_p --- src/flint/typing.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/flint/typing.py b/src/flint/typing.py index 013ec9dd..5e6e1136 100644 --- a/src/flint/typing.py +++ b/src/flint/typing.py @@ -143,6 +143,8 @@ def gcd(self, other: Self | _Tscalar, /) -> Self: ... def factor(self) -> tuple[_Tscalar, list[tuple[Self, int]]]: ... def factor_squarefree(self) -> tuple[_Tscalar, list[tuple[Self, int]]]: ... def deflation(self) -> tuple[Self, int]: ... + def mul_low(self, other: Self, n: int) -> Self: ... + def pow_trunc(self, e: int, n: int) -> Self: ... class _series_p(elem_p, Protocol[_Tscalar]): From eebd8c247d45f7a64f020b02813220e3388efd01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Oudompheng?= Date: Sat, 6 Sep 2025 09:59:41 +0200 Subject: [PATCH 7/7] Extend pow_trunc to arbitrary size exponents --- src/flint/test/test_all.py | 9 +++++++++ src/flint/types/fmpq_poly.pyx | 25 +++++++++++++++++++------ src/flint/types/fmpz_mod_poly.pyx | 27 +++++++++++++++++++++------ src/flint/types/fmpz_poly.pyx | 25 +++++++++++++++++++------ src/flint/types/fq_default_poly.pyx | 27 +++++++++++++++++++++------ src/flint/types/nmod_poly.pyx | 27 +++++++++++++++++++++------ 6 files changed, 110 insertions(+), 30 deletions(-) diff --git a/src/flint/test/test_all.py b/src/flint/test/test_all.py index 84270322..e82c8f3a 100644 --- a/src/flint/test/test_all.py +++ b/src/flint/test/test_all.py @@ -2969,6 +2969,15 @@ def setbad(obj, i, val): assert raises(lambda: P([1, 2, 3]).mul_low(None, 3), TypeError) # type: ignore assert raises(lambda: P([1, 2, 3]).mul_low(P([4, 5, 6]), None), TypeError) # type: ignore + p = P([1, 2, 3]) + assert p.pow_trunc(1234, 3) == P([1, 2468, 3046746]) + assert raises(lambda: p.pow_trunc(None, 3), TypeError) # type: ignore + assert raises(lambda: p.pow_trunc(3, "A"), TypeError) # type: ignore + assert raises(lambda: p.pow_trunc(P([4, 5, 6]), 3), TypeError) # type: ignore + # Large exponents are allowed + assert p.pow_trunc(2**100, 2) == P([1, 2**101]) + assert p.pow_trunc(6**60, 3) == p.pow_trunc(2**60, 3).pow_trunc(3**60, 3) + # XXX: Not sure what this should do in general: p = P([1, 1]) mod = P([1, 1]) diff --git a/src/flint/types/fmpq_poly.pyx b/src/flint/types/fmpq_poly.pyx index 2f3ba0f3..ee0933d8 100644 --- a/src/flint/types/fmpq_poly.pyx +++ b/src/flint/types/fmpq_poly.pyx @@ -526,14 +526,11 @@ cdef class fmpq_poly(flint_poly): fmpq_poly_mullow(res.val, self.val, (other).val, n) return res - def pow_trunc(self, slong e, slong n): + def pow_trunc(self, e, slong n): r""" Returns ``self`` raised to the power ``e`` modulo `x^n`: :math:`f^e \mod x^n`/ - Note: For exponents larger that 2^63 (which do not fit inside a slong) use the - method :meth:`~.pow_mod` with the explicit modulus `x^n`. - >>> f = fmpq_poly([1, 2, 3]) >>> x = fmpq_poly([0, 1]) >>> f.pow_trunc(2**20, 4) @@ -544,9 +541,25 @@ cdef class fmpq_poly(flint_poly): if e < 0: raise ValueError("Exponent must be non-negative") - cdef fmpq_poly res + cdef slong e_c + cdef fmpq_poly res, tmp + + try: + e_c = e + except OverflowError: + # Exponent does not fit slong + res = fmpq_poly.__new__(fmpq_poly) + tmp = fmpq_poly.__new__(fmpq_poly) + ebytes = e.to_bytes((e.bit_length() + 15) // 16 * 2, "big") + fmpq_poly_pow_trunc(res.val, self.val, ebytes[0] * 256 + ebytes[1], n) + for i in range(2, len(ebytes), 2): + fmpq_poly_pow_trunc(res.val, res.val, 1 << 16, n) + fmpq_poly_pow_trunc(tmp.val, self.val, ebytes[i] * 256 + ebytes[i+1], n) + fmpq_poly_mullow(res.val, res.val, tmp.val, n) + return res + res = fmpq_poly.__new__(fmpq_poly) - fmpq_poly_pow_trunc(res.val, self.val, e, n) + fmpq_poly_pow_trunc(res.val, self.val, e_c, n) return res def gcd(self, other): diff --git a/src/flint/types/fmpz_mod_poly.pyx b/src/flint/types/fmpz_mod_poly.pyx index da5b64f9..ce266bee 100644 --- a/src/flint/types/fmpz_mod_poly.pyx +++ b/src/flint/types/fmpz_mod_poly.pyx @@ -1668,14 +1668,11 @@ cdef class fmpz_mod_poly(flint_poly): ) return res - def pow_trunc(self, slong e, slong n): + def pow_trunc(self, e, slong n): r""" Returns ``self`` raised to the power ``e`` modulo `x^n`: :math:`f^e \mod x^n`/ - Note: For exponents larger that 2^31 (which do not fit inside a ulong) use the - method :meth:`~.pow_mod` with the explicit modulus `x^n`. - >>> R = fmpz_mod_poly_ctx(163) >>> x = R.gen() >>> f = 30*x**6 + 104*x**5 + 76*x**4 + 33*x**3 + 70*x**2 + 44*x + 65 @@ -1683,13 +1680,31 @@ cdef class fmpz_mod_poly(flint_poly): True >>> f.pow_trunc(2**20, 5) 132*x^4 + 113*x^3 + 36*x^2 + 48*x + 6 + >>> f.pow_trunc(5**25, 5) + 147*x^4 + 98*x^3 + 95*x^2 + 33*x + 126 """ if e < 0: raise ValueError("Exponent must be non-negative") - cdef fmpz_mod_poly res + cdef fmpz_mod_poly res, tmp + cdef slong e_c + + try: + e_c = e + except OverflowError: + # Exponent does not fit slong + res = self.ctx.new_ctype_poly() + tmp = self.ctx.new_ctype_poly() + ebytes = e.to_bytes((e.bit_length() + 15) // 16 * 2, "big") + fmpz_mod_poly_pow_trunc(res.val, self.val, ebytes[0] * 256 + ebytes[1], n, res.ctx.mod.val) + for i in range(2, len(ebytes), 2): + fmpz_mod_poly_pow_trunc(res.val, res.val, 1 << 16, n, res.ctx.mod.val) + fmpz_mod_poly_pow_trunc(tmp.val, self.val, ebytes[i] * 256 + ebytes[i+1], n, res.ctx.mod.val) + fmpz_mod_poly_mullow(res.val, res.val, tmp.val, n, res.ctx.mod.val) + return res + res = self.ctx.new_ctype_poly() - fmpz_mod_poly_pow_trunc(res.val, self.val, e, n, res.ctx.mod.val) + fmpz_mod_poly_pow_trunc(res.val, self.val, e_c, n, res.ctx.mod.val) return res def inflate(self, ulong n): diff --git a/src/flint/types/fmpz_poly.pyx b/src/flint/types/fmpz_poly.pyx index c79ed1e2..504c4221 100644 --- a/src/flint/types/fmpz_poly.pyx +++ b/src/flint/types/fmpz_poly.pyx @@ -507,14 +507,11 @@ cdef class fmpz_poly(flint_poly): fmpz_poly_mullow(res.val, self.val, (other).val, n) return res - def pow_trunc(self, slong e, slong n): + def pow_trunc(self, e, slong n): r""" Returns ``self`` raised to the power ``e`` modulo `x^n`: :math:`f^e \mod x^n`/ - Note: For exponents larger that 2^63 (which do not fit inside a slong) use the - method :meth:`~.pow_mod` with the explicit modulus `x^n`. - >>> f = fmpz_poly([1, 2, 3]) >>> x = fmpz_poly([0, 1]) >>> f.pow_trunc(2**20, 4) @@ -525,9 +522,25 @@ cdef class fmpz_poly(flint_poly): if e < 0: raise ValueError("Exponent must be non-negative") - cdef fmpz_poly res + cdef slong e_c + cdef fmpz_poly res, tmp + + try: + e_c = e + except OverflowError: + # Exponent does not fit slong + res = fmpz_poly.__new__(fmpz_poly) + tmp = fmpz_poly.__new__(fmpz_poly) + ebytes = e.to_bytes((e.bit_length() + 15) // 16 * 2, "big") + fmpz_poly_pow_trunc(res.val, self.val, ebytes[0] * 256 + ebytes[1], n) + for i in range(2, len(ebytes), 2): + fmpz_poly_pow_trunc(res.val, res.val, 1 << 16, n) + fmpz_poly_pow_trunc(tmp.val, self.val, ebytes[i] * 256 + ebytes[i+1], n) + fmpz_poly_mullow(res.val, res.val, tmp.val, n) + return res + res = fmpz_poly.__new__(fmpz_poly) - fmpz_poly_pow_trunc(res.val, self.val, e, n) + fmpz_poly_pow_trunc(res.val, self.val, e_c, n) return res def gcd(self, other): diff --git a/src/flint/types/fq_default_poly.pyx b/src/flint/types/fq_default_poly.pyx index cc23f2af..924b6884 100644 --- a/src/flint/types/fq_default_poly.pyx +++ b/src/flint/types/fq_default_poly.pyx @@ -1190,14 +1190,11 @@ cdef class fq_default_poly(flint_poly): ) return res - def pow_trunc(self, slong e, slong n): + def pow_trunc(self, e, slong n): r""" Returns ``self`` raised to the power ``e`` modulo `x^n`: :math:`f^e \mod x^n`/ - Note: For exponents larger that 2^31 (which do not fit inside a ulong) use the - method :meth:`~.pow_mod` with the explicit modulus `x^n`. - >>> R = fq_default_poly_ctx(163) >>> x = R.gen() >>> f = 30*x**6 + 104*x**5 + 76*x**4 + 33*x**3 + 70*x**2 + 44*x + 65 @@ -1205,13 +1202,31 @@ cdef class fq_default_poly(flint_poly): True >>> f.pow_trunc(2**20, 5) 132*x^4 + 113*x^3 + 36*x^2 + 48*x + 6 + >>> f.pow_trunc(5**25, 5) + 147*x^4 + 98*x^3 + 95*x^2 + 33*x + 126 """ if e < 0: raise ValueError("Exponent must be non-negative") - cdef fq_default_poly res + cdef slong e_c + cdef fq_default_poly res, tmp + + try: + e_c = e + except OverflowError: + # Exponent does not fit slong + res = self.ctx.new_ctype_poly() + tmp = self.ctx.new_ctype_poly() + ebytes = e.to_bytes((e.bit_length() + 15) // 16 * 2, "big") + fq_default_poly_pow_trunc(res.val, self.val, ebytes[0] * 256 + ebytes[1], n, res.ctx.field.val) + for i in range(2, len(ebytes), 2): + fq_default_poly_pow_trunc(res.val, res.val, 1 << 16, n, res.ctx.field.val) + fq_default_poly_pow_trunc(tmp.val, self.val, ebytes[i] * 256 + ebytes[i+1], n, res.ctx.field.val) + fq_default_poly_mullow(res.val, res.val, tmp.val, n, res.ctx.field.val) + return res + res = self.ctx.new_ctype_poly() - fq_default_poly_pow_trunc(res.val, self.val, e, n, res.ctx.field.val) + fq_default_poly_pow_trunc(res.val, self.val, e_c, n, res.ctx.field.val) return res def sqrt_trunc(self, slong n): diff --git a/src/flint/types/nmod_poly.pyx b/src/flint/types/nmod_poly.pyx index 68ded8f4..722194fc 100644 --- a/src/flint/types/nmod_poly.pyx +++ b/src/flint/types/nmod_poly.pyx @@ -752,14 +752,11 @@ cdef class nmod_poly(flint_poly): nmod_poly_mullow(res.val, self.val, (other).val, n) return res - def pow_trunc(self, slong e, slong n): + def pow_trunc(self, e, slong n): r""" Returns ``self`` raised to the power ``e`` modulo `x^n`: :math:`f^e \mod x^n`/ - Note: For exponents larger that 2^63 (which do not fit inside a slong) use the - method :meth:`~.pow_mod` with the explicit modulus `x^n`. - >>> f = nmod_poly([65, 44, 70, 33, 76, 104, 30], 163) >>> x = nmod_poly([0, 1], 163) >>> f.pow_trunc(2**20, 30) == pow(f, 2**20, x**30) @@ -772,10 +769,28 @@ cdef class nmod_poly(flint_poly): if e < 0: raise ValueError("Exponent must be non-negative") - cdef nmod_poly res = nmod_poly.__new__(nmod_poly) + cdef nmod_poly res, tmp + cdef slong e_c + + try: + e_c = e + except OverflowError: + # Exponent does not fit slong + res = nmod_poly.__new__(nmod_poly) + tmp = nmod_poly.__new__(nmod_poly) + nmod_poly_init_preinv(res.val, self.val.mod.n, self.val.mod.ninv) + nmod_poly_init_preinv(tmp.val, self.val.mod.n, self.val.mod.ninv) + ebytes = e.to_bytes((e.bit_length() + 15) // 16 * 2, "big") + nmod_poly_pow_trunc(res.val, self.val, ebytes[0] * 256 + ebytes[1], n) + for i in range(2, len(ebytes), 2): + nmod_poly_pow_trunc(res.val, res.val, 1 << 16, n) + nmod_poly_pow_trunc(tmp.val, self.val, ebytes[i] * 256 + ebytes[i+1], n) + nmod_poly_mullow(res.val, res.val, tmp.val, n) + return res + res = nmod_poly.__new__(nmod_poly) nmod_poly_init_preinv(res.val, self.val.mod.n, self.val.mod.ninv) - nmod_poly_pow_trunc(res.val, self.val, e, n) + nmod_poly_pow_trunc(res.val, self.val, e_c, n) return res def gcd(self, other):