Skip to content

Commit c8713a9

Browse files
Merge pull request #310 from remyoudompheng/nmod-poly-meths
Add shift and truncate methods to nmod_poly
2 parents 81784a9 + d98a8e5 commit c8713a9

File tree

12 files changed

+472
-1
lines changed

12 files changed

+472
-1
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,9 +166,14 @@ Contributors
166166

167167
- Oscar Benjamin (OB)
168168
- Robert Dougherty-Bliss (RDB)
169+
- Rémy Oudompheng (RO)
169170

170171
Changes
171172

173+
- [gh-310](https://github.com/flintlib/python-flint/pull/310),
174+
Add `truncate`, `left_shift` and `right_shift` methods to
175+
`fmpz_poly`, `fmpq_poly`, `nmod_poly`, `acb_poly`, `arb_poly`
176+
to match other univariate polynomial types. (RO)
172177
- [gh-300](https://github.com/flintlib/python-flint/pull/300), Fix `arb.repr`
173178
which now returns a Python representation that round trips. (OB)
174179
- [gh-289](https://github.com/flintlib/python-flint/pull/289),

src/flint/test/test_all.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2952,6 +2952,12 @@ def setbad(obj, i, val):
29522952
assert raises(lambda: 1 / P([1, 1]), DomainError)
29532953
assert raises(lambda: P([1, 2, 1]) / P([1, 2]), DomainError)
29542954

2955+
# Shifts and truncation.
2956+
assert P([1, 2, 3]).left_shift(3) == P([0, 0, 0, 1, 2, 3])
2957+
assert P([1, 2, 3]).right_shift(1) == P([2, 3])
2958+
assert P([1, 2, 3]).truncate(4) == P([1, 2, 3])
2959+
assert P([1, 2, 3]).truncate(2) == P([1, 2])
2960+
29552961
assert P([1, 1]) ** 0 == P([1])
29562962
assert P([1, 1]) ** 1 == P([1, 1])
29572963
assert P([1, 1]) ** 2 == P([1, 2, 1])

src/flint/types/acb_poly.pyx

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,94 @@ cdef class acb_poly(flint_poly):
314314
return s
315315
return divmod(t, s)
316316

317+
def truncate(self, slong n):
318+
r"""
319+
Notionally truncate the polynomial to have length ``n``. If
320+
``n`` is larger than the length of the input, then a copy of ``self`` is
321+
returned. If ``n`` is not positive, then the zero polynomial
322+
is returned.
323+
324+
Effectively returns this polynomial :math:`\mod x^n`.
325+
326+
>>> f = acb_poly([1,2,3])
327+
>>> f.truncate(3)
328+
3.00000000000000*x^2 + 2.00000000000000*x + 1.00000000000000
329+
>>> f.truncate(2)
330+
2.00000000000000*x + 1.00000000000000
331+
>>> f.truncate(1)
332+
1.00000000000000
333+
>>> f.truncate(0)
334+
0
335+
>>> f.truncate(-1)
336+
0
337+
338+
"""
339+
cdef acb_poly res
340+
res = acb_poly.__new__(acb_poly)
341+
342+
length = acb_poly_length(self.val)
343+
if n <= 0: # return zero
344+
return res
345+
elif n > length: # do nothing
346+
acb_poly_set(res.val, self.val)
347+
else:
348+
acb_poly_set_trunc(res.val, self.val, n)
349+
350+
return res
351+
352+
def left_shift(self, slong n):
353+
"""
354+
Returns ``self`` shifted left by ``n`` coefficients by inserting
355+
zero coefficients. This is equivalent to multiplying the polynomial
356+
by x^n
357+
358+
>>> f = acb_poly([1,2,3])
359+
>>> f.left_shift(0)
360+
3.00000000000000*x^2 + 2.00000000000000*x + 1.00000000000000
361+
>>> f.left_shift(1)
362+
3.00000000000000*x^3 + 2.00000000000000*x^2 + 1.00000000000000*x
363+
>>> f.left_shift(4)
364+
3.00000000000000*x^6 + 2.00000000000000*x^5 + 1.00000000000000*x^4
365+
366+
"""
367+
cdef acb_poly res
368+
res = acb_poly.__new__(acb_poly)
369+
370+
if n < 0:
371+
raise ValueError("Value must be shifted by a non-negative integer")
372+
if n > 0:
373+
acb_poly_shift_left(res.val, self.val, n)
374+
else: # do nothing, just copy self
375+
acb_poly_set(res.val, self.val)
376+
377+
return res
378+
379+
def right_shift(self, slong n):
380+
"""
381+
Returns ``self`` shifted right by ``n`` coefficients.
382+
This is equivalent to the floor division of the polynomial
383+
by x^n
384+
385+
>>> f = acb_poly([1,2,3])
386+
>>> f.right_shift(0)
387+
3.00000000000000*x^2 + 2.00000000000000*x + 1.00000000000000
388+
>>> f.right_shift(1)
389+
3.00000000000000*x + 2.00000000000000
390+
>>> f.right_shift(4)
391+
0
392+
"""
393+
cdef acb_poly res
394+
res = acb_poly.__new__(acb_poly)
395+
396+
if n < 0:
397+
raise ValueError("Value must be shifted by a non-negative integer")
398+
if n > 0:
399+
acb_poly_shift_right(res.val, self.val, n)
400+
else: # do nothing, just copy self
401+
acb_poly_set(res.val, self.val)
402+
403+
return res
404+
317405
def __pow__(acb_poly s, ulong exp, mod):
318406
if mod is not None:
319407
raise NotImplementedError("acb_poly modular exponentiation")

src/flint/types/arb_poly.pyx

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,94 @@ cdef class arb_poly(flint_poly):
312312
return s
313313
return divmod(t, s)
314314

315+
def truncate(self, slong n):
316+
r"""
317+
Notionally truncate the polynomial to have length ``n``. If
318+
``n`` is larger than the length of the input, then a copy of ``self`` is
319+
returned. If ``n`` is not positive, then the zero polynomial
320+
is returned.
321+
322+
Effectively returns this polynomial :math:`\mod x^n`.
323+
324+
>>> f = arb_poly([1,2,3])
325+
>>> f.truncate(3)
326+
3.00000000000000*x^2 + 2.00000000000000*x + 1.00000000000000
327+
>>> f.truncate(2)
328+
2.00000000000000*x + 1.00000000000000
329+
>>> f.truncate(1)
330+
1.00000000000000
331+
>>> f.truncate(0)
332+
0
333+
>>> f.truncate(-1)
334+
0
335+
336+
"""
337+
cdef arb_poly res
338+
res = arb_poly.__new__(arb_poly)
339+
340+
length = arb_poly_length(self.val)
341+
if n <= 0: # return zero
342+
return res
343+
elif n > length: # do nothing
344+
arb_poly_set(res.val, self.val)
345+
else:
346+
arb_poly_set_trunc(res.val, self.val, n)
347+
348+
return res
349+
350+
def left_shift(self, slong n):
351+
"""
352+
Returns ``self`` shifted left by ``n`` coefficients by inserting
353+
zero coefficients. This is equivalent to multiplying the polynomial
354+
by x^n
355+
356+
>>> f = arb_poly([1,2,3])
357+
>>> f.left_shift(0)
358+
3.00000000000000*x^2 + 2.00000000000000*x + 1.00000000000000
359+
>>> f.left_shift(1)
360+
3.00000000000000*x^3 + 2.00000000000000*x^2 + 1.00000000000000*x
361+
>>> f.left_shift(4)
362+
3.00000000000000*x^6 + 2.00000000000000*x^5 + 1.00000000000000*x^4
363+
364+
"""
365+
cdef arb_poly res
366+
res = arb_poly.__new__(arb_poly)
367+
368+
if n < 0:
369+
raise ValueError("Value must be shifted by a non-negative integer")
370+
if n > 0:
371+
arb_poly_shift_left(res.val, self.val, n)
372+
else: # do nothing, just copy self
373+
arb_poly_set(res.val, self.val)
374+
375+
return res
376+
377+
def right_shift(self, slong n):
378+
"""
379+
Returns ``self`` shifted right by ``n`` coefficients.
380+
This is equivalent to the floor division of the polynomial
381+
by x^n
382+
383+
>>> f = arb_poly([1,2,3])
384+
>>> f.right_shift(0)
385+
3.00000000000000*x^2 + 2.00000000000000*x + 1.00000000000000
386+
>>> f.right_shift(1)
387+
3.00000000000000*x + 2.00000000000000
388+
>>> f.right_shift(4)
389+
0
390+
"""
391+
cdef arb_poly res
392+
res = arb_poly.__new__(arb_poly)
393+
394+
if n < 0:
395+
raise ValueError("Value must be shifted by a non-negative integer")
396+
if n > 0:
397+
arb_poly_shift_right(res.val, self.val, n)
398+
else: # do nothing, just copy self
399+
arb_poly_set(res.val, self.val)
400+
401+
return res
402+
315403
def __pow__(arb_poly s, ulong exp, mod):
316404
if mod is not None:
317405
raise NotImplementedError("arb_poly modular exponentiation")

src/flint/types/fmpq_poly.pyi

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ class fmpq_poly(flint_poly[fmpq]):
6868
def __rdivmod__(self, other: ifmpq_poly, /) -> tuple[fmpq_poly, fmpq_poly]: ...
6969
def __pow__(self, exp: int | ifmpz, /) -> fmpq_poly: ...
7070

71+
def left_shift(self, n: int, /) -> fmpq_poly: ...
72+
def right_shift(self, n: int, /) -> fmpq_poly: ...
73+
def truncate(self, n: int, /) -> fmpq_poly: ...
74+
7175
def gcd(self, other: ifmpq_poly, /) -> fmpq_poly: ...
7276
def resultant(self, other: ifmpq_poly, /) -> fmpq: ...
7377
def xgcd(self, other: ifmpq_poly, /) -> tuple[fmpq_poly, fmpq_poly, fmpq_poly]: ...

src/flint/types/fmpq_poly.pyx

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,41 @@ cdef class fmpq_poly(flint_poly):
209209
"""
210210
return <bint>fmpq_poly_is_gen(self.val)
211211

212+
def truncate(self, slong n):
213+
r"""
214+
Notionally truncate the polynomial to have length ``n``. If
215+
``n`` is larger than the length of the input, then a copy of ``self`` is
216+
returned. If ``n`` is not positive, then the zero polynomial
217+
is returned.
218+
219+
Effectively returns this polynomial :math:`\mod x^n`.
220+
221+
>>> f = fmpq_poly([1,2,3])
222+
>>> f.truncate(3) == f
223+
True
224+
>>> f.truncate(2)
225+
2*x + 1
226+
>>> f.truncate(1)
227+
1
228+
>>> f.truncate(0)
229+
0
230+
>>> f.truncate(-1)
231+
0
232+
233+
"""
234+
cdef fmpq_poly res
235+
res = fmpq_poly.__new__(fmpq_poly)
236+
237+
length = fmpq_poly_length(self.val)
238+
if n <= 0: # return zero
239+
return res
240+
elif n > length: # do nothing
241+
fmpq_poly_set(res.val, self.val)
242+
else:
243+
fmpq_poly_set_trunc(res.val, self.val, n)
244+
245+
return res
246+
212247
def leading_coefficient(self):
213248
"""
214249
Returns the leading coefficient of the polynomial.
@@ -402,6 +437,59 @@ cdef class fmpq_poly(flint_poly):
402437
return t
403438
return t._divmod_(s)
404439

440+
def left_shift(self, slong n):
441+
"""
442+
Returns ``self`` shifted left by ``n`` coefficients by inserting
443+
zero coefficients. This is equivalent to multiplying the polynomial
444+
by x^n
445+
446+
>>> f = fmpq_poly([1,2,3])
447+
>>> f.left_shift(0)
448+
3*x^2 + 2*x + 1
449+
>>> f.left_shift(1)
450+
3*x^3 + 2*x^2 + x
451+
>>> f.left_shift(4)
452+
3*x^6 + 2*x^5 + x^4
453+
454+
"""
455+
cdef fmpq_poly res
456+
res = fmpq_poly.__new__(fmpq_poly)
457+
458+
if n < 0:
459+
raise ValueError("Value must be shifted by a non-negative integer")
460+
if n > 0:
461+
fmpq_poly_shift_left(res.val, self.val, n)
462+
else: # do nothing, just copy self
463+
fmpq_poly_set(res.val, self.val)
464+
465+
return res
466+
467+
def right_shift(self, slong n):
468+
"""
469+
Returns ``self`` shifted right by ``n`` coefficients.
470+
This is equivalent to the floor division of the polynomial
471+
by x^n
472+
473+
>>> f = fmpq_poly([1,2,3])
474+
>>> f.right_shift(0)
475+
3*x^2 + 2*x + 1
476+
>>> f.right_shift(1)
477+
3*x + 2
478+
>>> f.right_shift(4)
479+
0
480+
"""
481+
cdef fmpq_poly res
482+
res = fmpq_poly.__new__(fmpq_poly)
483+
484+
if n < 0:
485+
raise ValueError("Value must be shifted by a non-negative integer")
486+
if n > 0:
487+
fmpq_poly_shift_right(res.val, self.val, n)
488+
else: # do nothing, just copy self
489+
fmpq_poly_set(res.val, self.val)
490+
491+
return res
492+
405493
def __pow__(fmpq_poly self, exp, mod):
406494
cdef fmpq_poly res
407495
if mod is not None:

src/flint/types/fmpz_mod_poly.pyx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -990,7 +990,7 @@ cdef class fmpz_mod_poly(flint_poly):
990990
def truncate(self, slong n):
991991
r"""
992992
Notionally truncate the polynomial to have length ``n``. If
993-
``n`` is larger than the length of the input, then ``self`` is
993+
``n`` is larger than the length of the input, then a copy of ``self`` is
994994
returned. If ``n`` is not positive, then the zero polynomial
995995
is returned.
996996

src/flint/types/fmpz_poly.pyi

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@ class fmpz_poly(flint_poly[fmpz]):
5555
def __divmod__(self, other: ifmpz_poly, /) -> tuple[fmpz_poly, fmpz_poly]: ...
5656
def __rdivmod__(self, other: ifmpz, /) -> tuple[fmpz_poly, fmpz_poly]: ...
5757
def __pow__(self, other: int, /) -> fmpz_poly: ...
58+
59+
def left_shift(self, n: int, /) -> fmpz_poly: ...
60+
def right_shift(self, n: int, /) -> fmpz_poly: ...
61+
def truncate(self, n: int, /) -> fmpz_poly: ...
62+
5863
def gcd(self, other: ifmpz_poly, /) -> fmpz_poly: ...
5964
def content(self) -> fmpz: ...
6065
def resultant(self, other: ifmpz_poly, /) -> fmpz: ...

0 commit comments

Comments
 (0)