Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
fea7def
Begin adding fmpz_mod_poly
GiacomoPope Sep 20, 2023
6b1611a
Modify methods for contexts
GiacomoPope Sep 20, 2023
6797150
Add conversion and comparison functions
GiacomoPope Sep 21, 2023
e6b28d4
Start work on factor; bug
GiacomoPope Sep 21, 2023
4e64490
Monic, Leading Coefficent and Factor
GiacomoPope Sep 21, 2023
36724e3
More progress, but badly unittested currently
GiacomoPope Sep 21, 2023
0a84867
Add additional bool to check if modulus is prime on init
GiacomoPope Sep 22, 2023
6122937
Continue adding functions and docstrings
GiacomoPope Sep 22, 2023
4827066
Address review, add random elements and further methods and docstrings
GiacomoPope Sep 22, 2023
00b87f5
Add more functions to fmpz_mod_poly, also add is_unit to fmpz_mod
GiacomoPope Sep 22, 2023
a3fec08
Add a few more functions, coverage currently at 22%
GiacomoPope Sep 26, 2023
586307a
include new types to docstring tests
GiacomoPope Sep 26, 2023
b2e2894
Ensure 100% coverage of fmpz_mod, fix docstring test bugs, fmpz_mod_p…
GiacomoPope Sep 26, 2023
b060b23
Add base class docstrings
GiacomoPope Sep 27, 2023
c30ad7a
Improve docstrings and get coverage to 100%
GiacomoPope Sep 27, 2023
a2a7c1f
Include fmpz_mod_poly into documentation:
GiacomoPope Sep 27, 2023
857ee26
Address first round of comments from the review
GiacomoPope Sep 27, 2023
9cabaa2
Add a custom, DomainError, Exception
GiacomoPope Sep 27, 2023
024a09c
Improve based off feedback and add additional functions
GiacomoPope Sep 27, 2023
9a01c5d
Begin adding Berlekamp-Massey algorithm
GiacomoPope Sep 27, 2023
87a77b7
Add minpoly, remove BM, problems with multipoint eval
GiacomoPope Sep 28, 2023
85f82a4
Fix stupid copy paste bug
GiacomoPope Sep 28, 2023
428bece
Add support for vec types
GiacomoPope Sep 29, 2023
bc85cd3
Clean up set_any function and improve memory management
GiacomoPope Sep 29, 2023
cb60fe7
Add tests
GiacomoPope Sep 29, 2023
e57a071
Merge branch 'master' into add_fmpz_mod_poly
GiacomoPope Sep 29, 2023
69b2e50
Address bug from merging in test. Rename shift and (in/de)flation fun…
GiacomoPope Sep 29, 2023
a793997
Include new type into generic test, but with some TODO
GiacomoPope Sep 29, 2023
8fd39d1
Address TODO in tests and modify how _div_ works
GiacomoPope Sep 29, 2023
3614a42
Hacky fix for memory?
GiacomoPope Sep 29, 2023
9dfd197
Simplify init function
GiacomoPope Sep 30, 2023
1945859
better handling of dealloc
GiacomoPope Sep 30, 2023
57b8346
Add new poly method to context
GiacomoPope Sep 30, 2023
6a3b447
Return to 100% coverage
GiacomoPope Sep 30, 2023
52dee64
Forgot to push test commit
GiacomoPope Sep 30, 2023
b2543b4
Fix docstring types and initalise int type
GiacomoPope Oct 1, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/flint/types/fmpz_mod.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ cdef class fmpz_mod_ctx:
Return the modulus from the context as an fmpz
type

>>> mod_ctx = fmpz_mod_poly_ctx(2**127 - 1)
>>> mod_ctx = fmpz_mod_ctx(2**127 - 1)
>>> mod_ctx.modulus()
170141183460469231731687303715884105727

Expand Down Expand Up @@ -130,7 +130,7 @@ cdef class fmpz_mod_ctx:
return False

def __hash__(self):
return hash(repr(self))
return hash(self.modulus())

def __str__(self):
return f"Context for fmpz_mod with modulus: {self.modulus()}"
Expand Down
245 changes: 241 additions & 4 deletions src/flint/types/fmpz_mod_poly.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,26 @@ cdef class fmpz_mod_poly_ctx:
"""
return self.mod.modulus()

def zero(self):
"""
TODO
"""
cdef fmpz_mod_poly res
res = fmpz_mod_poly.__new__(fmpz_mod_poly)
res.ctx = self
fmpz_mod_poly_zero(res.val, self.mod.val)
return res

def one(self):
"""
TODO
"""
cdef fmpz_mod_poly res
res = fmpz_mod_poly.__new__(fmpz_mod_poly)
res.ctx = self
fmpz_mod_poly_one(res.val, self.mod.val)
return res

def gen(self):
"""
Return the generator of the polynomial `x`
Expand Down Expand Up @@ -127,7 +147,7 @@ cdef class fmpz_mod_poly_ctx:
cdef any_as_fmpz_mod_poly(self, obj):
# Convert fmpz_mod_poly
if typecheck(obj, fmpz_mod_poly):
if self.mod != (<fmpz_mod_poly>obj).ctx:
if self != (<fmpz_mod_poly>obj).ctx:
raise ValueError("moduli must match")
return obj

Expand All @@ -153,7 +173,7 @@ cdef class fmpz_mod_poly_ctx:
return False

def __hash__(self):
return hash(repr(self))
return hash(self.modulus())

def __str__(self):
return f"Context for fmpz_mod_poly with modulus: {self.modulus()}"
Expand Down Expand Up @@ -204,19 +224,164 @@ cdef class fmpz_mod_poly(flint_poly):
def __len__(self):
return fmpz_mod_poly_length(self.val, self.ctx.mod.val)

def __hash__(self):
return hash(map(int, self.coeffs()))

# ---------------- #
# Arithmetic #
# ---------------- #

def __pos__(self):
return self

def __neg__(self):
cdef fmpz_mod_poly res
res = fmpz_mod_poly.__new__(fmpz_mod_poly)
res.ctx = self.ctx
fmpz_mod_poly_neg(res.val, self.val, self.ctx.mod.val)
return res

def __add__(self, other):
cdef fmpz_mod_poly res
other = self.ctx.any_as_fmpz_mod_poly(other)
if other is NotImplemented:
return other

res = fmpz_mod_poly.__new__(fmpz_mod_poly)
fmpz_mod_poly_add(
res.val, self.val, (<fmpz_mod_poly>other).val, self.ctx.mod.val
)
res.ctx = self.ctx
return res

def __radd__(self, other):
return self.__add__(other)

@staticmethod
def _sub_(left, right):
cdef fmpz_mod_poly res
res = fmpz_mod_poly.__new__(fmpz_mod_poly)

# Case when left and right are already fmpz_mod_poly
if typecheck(left, fmpz_mod_poly) and typecheck(right, fmpz_mod_poly):
if not (<fmpz_mod_poly>left).ctx == (<fmpz_mod_poly>right).ctx:
raise ValueError("moduli must match")

# Case when right is not fmpz_mod_poly, try to convert to fmpz
elif typecheck(left, fmpz_mod_poly):
right = (<fmpz_mod_poly>left).ctx.any_as_fmpz_mod_poly(right)
if right is NotImplemented:
return NotImplemented

# Case when left is not fmpz_mod_poly, try to convert to fmpz
else:
left = (<fmpz_mod_poly>right).ctx.any_as_fmpz_mod_poly(left)
if left is NotImplemented:
return NotImplemented

res.ctx = (<fmpz_mod_poly>left).ctx
fmpz_mod_poly_sub(
res.val, (<fmpz_mod_poly>left).val, (<fmpz_mod_poly>right).val, res.ctx.mod.val
)
return res

def __sub__(s, t):
return fmpz_mod_poly._sub_(s, t)

def __rsub__(s, t):
return fmpz_mod_poly._sub_(t, s)

def __mul__(self, other):
# TODO:
# Allow scalar multiplication for efficiency, rather
# than casting `other` to a polynomial?
cdef fmpz_mod_poly res
other = self.ctx.any_as_fmpz_mod_poly(other)
if other is NotImplemented:
return other

res = fmpz_mod_poly.__new__(fmpz_mod_poly)
fmpz_mod_poly_mul(
res.val, self.val, (<fmpz_mod_poly>other).val, self.ctx.mod.val
)
res.ctx = self.ctx
return res

def __rmul__(self, other):
return self.__mul__(other)

@staticmethod
def _div_(left, right):
# TODO:
# Allow scalar division for efficiency, rather
# than casting `other` to a polynomial?

cdef bint check
cdef fmpz_mod_poly res
res = fmpz_mod_poly.__new__(fmpz_mod_poly)

# Case when left and right are already fmpz_mod_poly
if typecheck(left, fmpz_mod_poly) and typecheck(right, fmpz_mod_poly):
if not (<fmpz_mod_poly>left).ctx == (<fmpz_mod_poly>right).ctx:
raise ValueError("moduli must match")

# Case when right is not fmpz_mod_poly, try to convert to fmpz
elif typecheck(left, fmpz_mod_poly):
right = (<fmpz_mod_poly>left).ctx.any_as_fmpz_mod_poly(right)
if right is NotImplemented:
return NotImplemented

# Case when left is not fmpz_mod_poly, try to convert to fmpz
else:
left = (<fmpz_mod_poly>right).ctx.any_as_fmpz_mod_poly(left)
if left is NotImplemented:
return NotImplemented

res.ctx = (<fmpz_mod_poly>left).ctx
check = fmpz_mod_poly_divides(
res.val, (<fmpz_mod_poly>left).val, (<fmpz_mod_poly>right).val, res.ctx.mod.val
)
if check == 0:
raise ValueError(
f"{right} does not divide {left}"
)

return res

def __truediv__(s, t):
return fmpz_mod_poly._div_(s, t)

def __rtruediv__(s, t):
return fmpz_mod_poly._div_(t, s)

def __floordiv__(self, other):
return NotImplemented

cpdef long length(self):
return fmpz_mod_poly_length(self.val, self.ctx.mod.val)

cpdef long degree(self):
return fmpz_mod_poly_degree(self.val, self.ctx.mod.val)

def is_zero(self):
"""
Return `True` if the polynomial is the zero polynomial
and `False` otherwise
"""
return 0 != fmpz_mod_poly_is_zero(self.val, self.ctx.mod.val)

def is_one(self):
"""
Return `True` if the polynomial is the zero polynomial
and `False` otherwise
"""
return 0 != fmpz_mod_poly_is_one(self.val, self.ctx.mod.val)

def is_gen(self):
"""
Return `True` if the polynomial is the zero polynomial
and `False` otherwise
"""
return 0 != fmpz_mod_poly_is_gen(self.val, self.ctx.mod.val)

def __richcmp__(self, other, int op):
Expand Down Expand Up @@ -248,6 +413,27 @@ cdef class fmpz_mod_poly(flint_poly):
"""
return self[self.degree()]

def reverse(self, degree=None):
"""
TODO
"""
cdef fmpz_mod_poly res
cdef slong d

res = fmpz_mod_poly.__new__(fmpz_mod_poly)
res.ctx = self.ctx

if degree is not None:
d = degree
if d != degree or d < 0:
raise ValueError(f"degree argument must be a non-negative integer, got {degree}")
else:
d = fmpz_mod_poly_degree(self.val, self.ctx.mod.val)

length = d + 1
fmpz_mod_poly_reverse(res.val, self.val, length, self.ctx.mod.val)
return res

def monic(self, check=True):
"""
Return this polynomial divided by its leading coefficient.
Expand Down Expand Up @@ -279,11 +465,61 @@ cdef class fmpz_mod_poly(flint_poly):
return res

def is_irreducible(self):
pass
"""
Return whether this polynomial is irreducible.
"""
return 1 == fmpz_mod_poly_is_irreducible(self.val, self.ctx.mod.val)

def is_squarefree(self):
pass
"""
Return whether this polynomial is squarefree.
"""
return 1 == fmpz_mod_poly_is_squarefree(self.val, self.ctx.mod.val)

def radical(self):
"""
Return the radical of self, the product of the irreducible
factors of the polynomial. This is also referred to as the
square-free part of the polynomial.
"""
res = self.ctx.one()
# TODO: this should be the squarefree leading coeff I guess...
res *= self.leading_coefficient()
_, factors = self.factor()
for p, _ in factors:
res *= p
return res

def factor_squarefree(self):
"""
Factors self into irreducible factors, returning a tuple
(c, factors) where c is the content of the coefficients and
factors is a list of (poly, exp) pairs.

TODO: docstrings
TODO: add a check that at least the leading term is
invertible to stop segfaults?
TODO: Other option is to only support prime moduli?
"""
cdef fmpz_mod_poly_factor_t fac
cdef int i
fmpz_mod_poly_factor_init(fac, self.ctx.mod.val)
fmpz_mod_poly_factor_squarefree(fac, self.val, self.ctx.mod.val)

res = [0] * fac.num

cdef fmpz_mod_poly u
for i in range(fac.num):
u = fmpz_mod_poly.__new__(fmpz_mod_poly)
u.ctx = self.ctx
fmpz_mod_poly_set(u.val, &fac.poly[i], self.ctx.mod.val)
exp = fac.exp[i]
res[i] = (u, exp)
return self.leading_coefficient(), res

# TODO: we could make a factorisation class which we could then
# implement the factor methods such as pow and concat. I think
# sage does something like this with `Factorisation` classes.
def factor(self, algorithm=None):
"""
Factors self into irreducible factors, returning a tuple
Expand All @@ -293,6 +529,7 @@ cdef class fmpz_mod_poly(flint_poly):
TODO: docstrings
TODO: add a check that at least the leading term is
invertible to stop segfaults?
TODO: Other option is to only support prime moduli?
"""
cdef fmpz_mod_poly_factor_t fac
cdef int i
Expand Down