diff --git a/meson.build b/meson.build index d5f4338b..fd2f7950 100644 --- a/meson.build +++ b/meson.build @@ -9,6 +9,17 @@ gmp_dep = dependency('gmp') mpfr_dep = dependency('mpfr') flint_dep = dependency('flint') +add_project_arguments( + '-X', 'embedsignature=True', + '-X', 'emit_code_comments=True', + language : 'cython' +) + +if get_option('coverage') + add_project_arguments('-X', 'linetrace=True', language : 'cython') + add_project_arguments('-DCYTHON_TRACE=1', language : 'c') +endif + # Add rpaths for a local build of flint found via pkgconfig # https://github.com/mesonbuild/meson/issues/13046 if get_option('add_flint_rpath') diff --git a/meson.options b/meson.options index 21079382..4c96f71d 100644 --- a/meson.options +++ b/meson.options @@ -1 +1,2 @@ +option('coverage', type : 'boolean', value : false, description : 'enable coverage build') option('add_flint_rpath', type : 'boolean', value : false) diff --git a/setup.py b/setup.py index 82ae7a98..d260834e 100644 --- a/setup.py +++ b/setup.py @@ -81,12 +81,14 @@ ("flint.flint_base.flint_context", ["src/flint/flint_base/flint_context.pyx"]), ("flint.types.fmpz", ["src/flint/types/fmpz.pyx"]), + ("flint.types.fmpz_vec", ["src/flint/types/fmpz_vec.pyx"]), ("flint.types.fmpz_poly", ["src/flint/types/fmpz_poly.pyx"]), ("flint.types.fmpz_mpoly", ["src/flint/types/fmpz_mpoly.pyx"]), ("flint.types.fmpz_mat", ["src/flint/types/fmpz_mat.pyx"]), ("flint.types.fmpz_series", ["src/flint/types/fmpz_series.pyx"]), ("flint.types.fmpq", ["src/flint/types/fmpq.pyx"]), + ("flint.types.fmpq_vec", ["src/flint/types/fmpq_vec.pyx"]), ("flint.types.fmpq_poly", ["src/flint/types/fmpq_poly.pyx"]), ("flint.types.fmpq_mat", ["src/flint/types/fmpq_mat.pyx"]), ("flint.types.fmpq_series", ["src/flint/types/fmpq_series.pyx"]), @@ -100,6 +102,9 @@ ("flint.types.fmpz_mod_poly", ["src/flint/types/fmpz_mod_poly.pyx"]), ("flint.types.fmpz_mod_mat", ["src/flint/types/fmpz_mod_mat.pyx"]), + ("flint.types.fmpq_mpoly", ["src/flint/types/fmpq_mpoly.pyx"]), + ("flint.types.fmpz_mpoly_q", ["src/flint/types/fmpz_mpoly_q.pyx"]), + ("flint.types.arf", ["src/flint/types/arf.pyx"]), ("flint.types.arb", ["src/flint/types/arb.pyx"]), ("flint.types.arb_poly", ["src/flint/types/arb_poly.pyx"]), diff --git a/src/flint/__init__.py b/src/flint/__init__.py index 9a8fb4c5..aa1e1ff6 100644 --- a/src/flint/__init__.py +++ b/src/flint/__init__.py @@ -4,22 +4,26 @@ from .types.fmpz_poly import * from .types.fmpz_mat import * from .types.fmpz_series import * +from .types.fmpz_vec import fmpz_vec from .types.fmpq import * from .types.fmpq_poly import * from .types.fmpq_mat import * from .types.fmpq_series import * +from .types.fmpq_vec import fmpq_vec from .types.nmod import * from .types.nmod_poly import * from .types.nmod_mat import * from .types.nmod_series import * -from .types.fmpz_mpoly import * +from .types.fmpz_mpoly import fmpz_mpoly_ctx, fmpz_mpoly, fmpz_mpoly_vec from .types.fmpz_mod import * from .types.fmpz_mod_poly import * from .types.fmpz_mod_mat import fmpz_mod_mat +from .types.fmpq_mpoly import fmpq_mpoly_ctx, fmpq_mpoly, fmpq_mpoly_vec + from .types.arf import * from .types.arb import * from .types.arb_poly import * @@ -36,6 +40,7 @@ from .flint_base.flint_base import ( FLINT_VERSION as __FLINT_VERSION__, FLINT_RELEASE as __FLINT_RELEASE__, + Ordering, ) __version__ = '0.6.0' diff --git a/src/flint/flint_base/flint_base.pxd b/src/flint/flint_base/flint_base.pxd index 95f87e9a..239cab54 100644 --- a/src/flint/flint_base/flint_base.pxd +++ b/src/flint/flint_base/flint_base.pxd @@ -1,3 +1,5 @@ +from flint.flintlib.mpoly cimport ordering_t + cdef class flint_elem: pass @@ -7,6 +9,10 @@ cdef class flint_scalar(flint_elem): cdef class flint_poly(flint_elem): pass +cdef class flint_mpoly_context(flint_elem): + cdef public object py_names + cdef const char ** c_names + cdef class flint_mpoly(flint_elem): pass @@ -15,3 +21,9 @@ cdef class flint_mat(flint_elem): cdef class flint_series(flint_elem): pass + +cpdef enum Ordering: + lex, deglex, degrevlex + +cdef ordering_t ordering_py_to_c(ordering: Ordering) +cdef ordering_c_to_py(ordering_t ordering) diff --git a/src/flint/flint_base/flint_base.pyx b/src/flint/flint_base/flint_base.pyx index 99aafe2f..481cac7f 100644 --- a/src/flint/flint_base/flint_base.pyx +++ b/src/flint/flint_base/flint_base.pyx @@ -2,8 +2,15 @@ from flint.flintlib.flint cimport ( FLINT_BITS as _FLINT_BITS, FLINT_VERSION as _FLINT_VERSION, __FLINT_RELEASE as _FLINT_RELEASE, + slong ) +from flint.flintlib.mpoly cimport ordering_t from flint.flint_base.flint_context cimport thectx +from flint.flint_base.flint_base cimport Ordering +from flint.utils.typecheck cimport typecheck +cimport libc.stdlib + +from typing import Optional FLINT_BITS = _FLINT_BITS @@ -114,16 +121,163 @@ cdef class flint_poly(flint_elem): v = - fac[0] roots.append((v, m)) return roots - + def complex_roots(self): raise AttributeError("Complex roots are not supported for this polynomial") +cdef class flint_mpoly_context(flint_elem): + """ + Base class for multivariate ring contexts + """ + + _ctx_cache = None + + def __init__(self, int nvars, names): + if nvars < 0: + raise ValueError("cannot have a negative amount of variables") + elif len(names) != nvars: + raise ValueError("number of variables must match number of variable names") + self.py_names = tuple(name.encode("ascii") if not isinstance(name, bytes) else name for name in names) + self.c_names = libc.stdlib.malloc(nvars * sizeof(const char *)) + for i in range(nvars): + self.c_names[i] = self.py_names[i] + + def __dealloc__(self): + libc.stdlib.free(self.c_names) + self.c_names = NULL + + def __str__(self): + return self.__repr__() + + def __repr__(self): + return f"{self.__class__.__name__}({self.nvars()}, '{repr(self.ordering())}', {self.names()})" + + def name(self, long i): + if not 0 <= i < len(self.py_names): + raise IndexError("variable name index out of range") + return self.py_names[i].decode("ascii") + + def names(self): + return tuple(name.decode("ascii") for name in self.py_names) + + def gens(self): + return tuple(self.gen(i) for i in range(self.nvars())) + + def variable_to_index(self, var: Union[int, str]): + """Convert a variable name string or possible index to its index in the context.""" + if isinstance(var, str): + try: + i = self.names().index(var) + except ValueError: + raise ValueError("variable not in context") + elif isinstance(var, int): + if not 0 <= var < self.nvars(): + raise IndexError("generator index out of range") + i = var + else: + raise TypeError("invalid variable type") + + return i + + @staticmethod + def create_variable_names(slong nvars, names: str): + """ + Create a tuple of variable names based on the comma separated `names` string. + + If `names` contains a single value, and `nvars` > 1, then the variables are numbered, e.g. + + >>> flint_mpoly_context.create_variable_names(3, "x") + ('x0', 'x1', 'x2') + + """ + nametup = tuple(name.strip() for name in names.split(',')) + if len(nametup) != nvars: + if len(nametup) == 1: + nametup = tuple(nametup[0] + str(i) for i in range(nvars)) + else: + raise ValueError("number of variables does not equal number of names") + return nametup + + @classmethod + def get_context(cls, slong nvars=1, ordering=Ordering.lex, names: Optional[str] = "x", nametup: Optional[tuple] = None): + """ + Retrieve a context via the number of variables, `nvars`, the ordering, `ordering`, and either a variable + name string, `names`, or a tuple of variable names, `nametup`. + """ + + # A type hint of `ordering: Ordering` results in the error "TypeError: an integer is required" if a Ordering + # object is not provided. This is pretty obtuse so we check it's type ourselves + if not isinstance(ordering, Ordering): + raise TypeError(f"`ordering` ('{ordering}') is not an instance of flint.Ordering") + + if nametup is not None: + key = nvars, ordering, nametup + elif nametup is None and names is not None: + key = nvars, ordering, cls.create_variable_names(nvars, names) + else: + raise ValueError("must provide either `names` or `nametup`") + + ctx = cls._ctx_cache.get(key) + if ctx is None: + ctx = cls._ctx_cache.setdefault(key, cls(*key)) + return ctx + + @classmethod + def from_context(cls, ctx: flint_mpoly_context): + return cls.get_context( + nvars=ctx.nvars(), + ordering=ctx.ordering(), + names=None, + nametup=ctx.names() + ) + + cdef class flint_mpoly(flint_elem): """ Base class for multivariate polynomials. """ + def leading_coefficient(self): + return self.coefficient(0) + + def to_dict(self): + return {self.monomial(i): self.coefficient(i) for i in range(len(self))} + + def __contains__(self, x): + """ + Returns True if `self` contains a term with exponent vector `x` and a non-zero coefficient. + + >>> from flint import fmpq_mpoly_ctx, Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) + >>> (1, 1) in p + True + >>> (5, 1) in p + False + + """ + return bool(self[x]) + + def __iter__(self): + return iter(self.monoms()) + + def __pos__(self): + return self + + def terms(self): + """ + Return the exponent vectors and coefficient of each term. + + >>> from flint import fmpq_mpoly_ctx, Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> f = ctx.from_dict({(0, 0): 1, (1, 0): 2, (0, 1): 3, (1, 1): 4}) + >>> list(f.terms()) + [((1, 1), 4), ((1, 0), 2), ((0, 1), 3), ((0, 0), 1)] + + """ + return zip(self.monoms(), self.coeffs()) + cdef class flint_series(flint_elem): """ @@ -190,3 +344,26 @@ cdef class flint_mat(flint_elem): # supports mpmath conversions tolist = table + + +cdef ordering_t ordering_py_to_c(ordering): # Cython does not like an "Ordering" type hint here + if not isinstance(ordering, Ordering): + raise TypeError(f"`ordering` ('{ordering}') is not an instance of flint.Ordering") + + if ordering == Ordering.lex: + return ordering_t.ORD_LEX + elif ordering == Ordering.deglex: + return ordering_t.ORD_DEGLEX + elif ordering == Ordering.degrevlex: + return ordering_t.ORD_DEGREVLEX + + +cdef ordering_c_to_py(ordering_t ordering): + if ordering == ordering_t.ORD_LEX: + return Ordering.lex + elif ordering == ordering_t.ORD_DEGLEX: + return Ordering.deglex + elif ordering == ordering_t.ORD_DEGREVLEX: + return Ordering.degrevlex + else: + raise ValueError("unimplemented term order %d" % ordering) diff --git a/src/flint/flint_base/flint_context.pyx b/src/flint/flint_base/flint_context.pyx index b344ddc8..fd4dc324 100644 --- a/src/flint/flint_base/flint_context.pyx +++ b/src/flint/flint_base/flint_context.pyx @@ -18,7 +18,7 @@ cdef class FlintContext: self.threads = 1 self.cap = 10 - @property + @property def prec(self): return self._prec diff --git a/src/flint/flintlib/fmpq.pxd b/src/flint/flintlib/fmpq.pxd index 12a5d400..b9934e50 100644 --- a/src/flint/flintlib/fmpq.pxd +++ b/src/flint/flintlib/fmpq.pxd @@ -1,52 +1,22 @@ from flint.flintlib.flint cimport ulong, flint_rand_t, mp_bitcnt_t, slong, flint_bitcnt_t from flint.flintlib.fmpz cimport fmpz_struct, fmpz_t +cdef extern from *: + """ + /* An ugly hack to get around the ugly hack of renaming fmpq to avoid a c/python name collision */ + typedef fmpq fmpq_struct; + """ + cdef extern from "flint/fmpq.h": ctypedef struct fmpq_struct: fmpz_struct num fmpz_struct den + ctypedef fmpq_struct fmpq_t[1] + fmpz_struct * fmpq_numref(fmpq_t x) fmpz_struct * fmpq_denref(fmpq_t x) -# from here on is parsed - void fmpq_init(fmpq_t x) - void fmpq_clear(fmpq_t x) - void fmpq_canonicalise(fmpq_t res) - void _fmpq_canonicalise(fmpz_t num, fmpz_t den) - int fmpq_is_canonical(const fmpq_t x) - int _fmpq_is_canonical(const fmpz_t num, const fmpz_t den) - void fmpq_set(fmpq_t dest, const fmpq_t src) - void fmpq_swap(fmpq_t op1, fmpq_t op2) - void fmpq_neg(fmpq_t dest, const fmpq_t src) - void fmpq_abs(fmpq_t dest, const fmpq_t src) - void fmpq_zero(fmpq_t res) - void fmpq_one(fmpq_t res) - int fmpq_is_zero(const fmpq_t res) - int fmpq_is_one(const fmpq_t res) - int fmpq_is_pm1(const fmpq_t res) - int fmpq_equal(const fmpq_t x, const fmpq_t y) - int fmpq_sgn(const fmpq_t x) - int fmpq_cmp(const fmpq_t x, const fmpq_t y) - int fmpq_cmp_fmpz(const fmpq_t x, const fmpz_t y) - int fmpq_cmp_ui(const fmpq_t x, ulong y) - int fmpq_cmp_si(const fmpq_t x, slong y) - int fmpq_equal_ui(const fmpq_t x, ulong y) - int fmpq_equal_si(const fmpq_t x, slong y) - void fmpq_height(fmpz_t height, const fmpq_t x) - flint_bitcnt_t fmpq_height_bits(const fmpq_t x) - void fmpq_set_fmpz_frac(fmpq_t res, const fmpz_t p, const fmpz_t q) - # void fmpq_get_mpz_frac(mpz_t a, mpz_t b, fmpq_t c) - void fmpq_set_si(fmpq_t res, slong p, ulong q) - void _fmpq_set_si(fmpz_t rnum, fmpz_t rden, slong p, ulong q) - void fmpq_set_ui(fmpq_t res, ulong p, ulong q) - void _fmpq_set_ui(fmpz_t rnum, fmpz_t rden, ulong p, ulong q) - # void fmpq_set_mpq(fmpq_t dest, const mpq_t src) - void fmpq_set_str(fmpq_t dest, const char * s, int base) - # void fmpq_init_set_mpz_frac_readonly(fmpq_t z, const mpz_t p, const mpz_t q) - double fmpq_get_d(const fmpq_t f) - # void fmpq_get_mpq(mpq_t dest, const fmpq_t src) - # int fmpq_get_mpfr(mpfr_t dest, const fmpq_t src, mpfr_rnd_t rnd) # from here on is parsed void fmpq_init(fmpq_t x) void fmpq_clear(fmpq_t x) diff --git a/src/flint/flintlib/fmpq_mpoly.pxd b/src/flint/flintlib/fmpq_mpoly.pxd new file mode 100644 index 00000000..ccaf77c1 --- /dev/null +++ b/src/flint/flintlib/fmpq_mpoly.pxd @@ -0,0 +1,178 @@ +from flint.flintlib.fmpq_poly cimport fmpq_poly_t, fmpq_poly_struct +from flint.flintlib.flint cimport slong, mp_limb_t, ulong, flint_bitcnt_t, fmpz_struct, flint_rand_t +from flint.flintlib.fmpz cimport fmpz_t +from flint.flintlib.fmpz_mpoly cimport fmpz_mpoly_ctx_t, fmpz_mpoly_t, fmpz_mpoly_struct +from flint.flintlib.fmpq cimport fmpq_struct, fmpq_t +from flint.flintlib.mpoly cimport ordering_t + + +# unimported types {'fmpq_mpoly_univar_t'} + +cdef extern from "flint/fmpq_mpoly.h": + ctypedef struct fmpq_mpoly_ctx_struct: + fmpz_mpoly_ctx_t zctx + + ctypedef fmpq_mpoly_ctx_struct fmpq_mpoly_ctx_t[1] + + ctypedef struct fmpq_mpoly_struct: + fmpq_t content + fmpz_mpoly_t zpoly + + ctypedef fmpq_mpoly_struct fmpq_mpoly_t[1] + + ctypedef struct fmpq_mpoly_univar_struct: + fmpq_mpoly_struct *coeffs + fmpz_struct * exps + slong alloc + slong length + + ctypedef fmpq_mpoly_univar_struct fmpq_mpoly_univar_t[1] + + void fmpq_mpoly_ctx_init(fmpq_mpoly_ctx_t ctx, slong nvars, const ordering_t ord) + slong fmpq_mpoly_ctx_nvars(const fmpq_mpoly_ctx_t ctx) + ordering_t fmpq_mpoly_ctx_ord(const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_ctx_clear(fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_init(fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_init2(fmpq_mpoly_t A, slong alloc, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_init3(fmpq_mpoly_t A, slong alloc, flint_bitcnt_t bits, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_fit_length(fmpq_mpoly_t A, slong len, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_fit_bits(fmpq_mpoly_t A, flint_bitcnt_t bits, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_realloc(fmpq_mpoly_t A, slong alloc, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_clear(fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + char * fmpq_mpoly_get_str_pretty(const fmpq_mpoly_t A, const char ** x, const fmpq_mpoly_ctx_t ctx) + # int fmpq_mpoly_fprint_pretty(FILE * file, const fmpq_mpoly_t A, const char ** x, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_print_pretty(const fmpq_mpoly_t A, const char ** x, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_set_str_pretty(fmpq_mpoly_t A, const char * str, const char ** x, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_gen(fmpq_mpoly_t A, slong var, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_is_gen(const fmpq_mpoly_t A, slong var, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_set(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_equal(const fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_swap(fmpq_mpoly_t A, fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_is_fmpq(const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_get_fmpq(fmpq_t c, const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_set_fmpq(fmpq_mpoly_t A, const fmpq_t c, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_set_fmpz(fmpq_mpoly_t A, const fmpz_t c, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_set_ui(fmpq_mpoly_t A, ulong c, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_set_si(fmpq_mpoly_t A, slong c, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_zero(fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_one(fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_equal_fmpq(const fmpq_mpoly_t A, const fmpq_t c, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_equal_fmpz(const fmpq_mpoly_t A, const fmpz_t c, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_equal_ui(const fmpq_mpoly_t A, ulong c, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_equal_si(const fmpq_mpoly_t A, slong c, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_is_zero(const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_is_one(const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_degrees_fit_si(const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_degrees_fmpz(fmpz_struct ** degs, const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_degrees_si(slong * degs, const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_degree_fmpz(fmpz_t deg, const fmpq_mpoly_t A, slong var, const fmpq_mpoly_ctx_t ctx) + slong fmpq_mpoly_degree_si(const fmpq_mpoly_t A, slong var, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_total_degree_fits_si(const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_total_degree_fmpz(fmpz_t tdeg, const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + slong fmpq_mpoly_total_degree_si(const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_used_vars(int * used, const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_get_denominator(fmpz_t d, const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_get_coeff_fmpq_monomial(fmpq_t c, const fmpq_mpoly_t A, const fmpq_mpoly_t M, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_set_coeff_fmpq_monomial(fmpq_mpoly_t A, const fmpq_t c, const fmpq_mpoly_t M, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_get_coeff_fmpq_fmpz(fmpq_t c, const fmpq_mpoly_t A, fmpz_struct * const * exp, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_get_coeff_fmpq_ui(fmpq_t c, const fmpq_mpoly_t A, const ulong * exp, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_set_coeff_fmpq_fmpz(fmpq_mpoly_t A, const fmpq_t c, fmpz_struct * const * exp, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_set_coeff_fmpq_ui(fmpq_mpoly_t A, const fmpq_t c, const ulong * exp, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_get_coeff_vars_ui(fmpq_mpoly_t C, const fmpq_mpoly_t A, const slong * vars, const ulong * exps, slong length, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_cmp(const fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) + fmpq_struct * fmpq_mpoly_content_ref(fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + fmpz_mpoly_struct * fmpq_mpoly_zpoly_ref(fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + fmpz_struct * fmpq_mpoly_zpoly_term_coeff_ref(fmpq_mpoly_t A, slong i, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_is_canonical(const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + slong fmpq_mpoly_length(const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_resize(fmpq_mpoly_t A, slong new_length, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_get_term_coeff_fmpq(fmpq_t c, const fmpq_mpoly_t A, slong i, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_set_term_coeff_fmpq(fmpq_mpoly_t A, slong i, const fmpq_t c, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_term_exp_fits_si(const fmpq_mpoly_t A, slong i, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_term_exp_fits_ui(const fmpq_mpoly_t A, slong i, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_get_term_exp_fmpz(fmpz_struct ** exps, const fmpq_mpoly_t A, slong i, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_get_term_exp_ui(ulong * exps, const fmpq_mpoly_t A, slong i, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_get_term_exp_si(slong * exps, const fmpq_mpoly_t A, slong i, const fmpq_mpoly_ctx_t ctx) + ulong fmpq_mpoly_get_term_var_exp_ui(const fmpq_mpoly_t A, slong i, slong var, const fmpq_mpoly_ctx_t ctx) + slong fmpq_mpoly_get_term_var_exp_si(const fmpq_mpoly_t A, slong i, slong var, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_set_term_exp_fmpz(fmpq_mpoly_t A, slong i, fmpz_struct * const * exps, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_set_term_exp_ui(fmpq_mpoly_t A, slong i, const ulong * exps, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_get_term(fmpq_mpoly_t M, const fmpq_mpoly_t A, slong i, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_get_term_monomial(fmpq_mpoly_t M, const fmpq_mpoly_t A, slong i, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_push_term_fmpq_fmpz(fmpq_mpoly_t A, const fmpq_t c, fmpz_struct * const * exp, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_push_term_fmpq_ffmpz(fmpq_mpoly_t A, const fmpq_t c, const fmpz_struct * exp, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_push_term_fmpz_fmpz(fmpq_mpoly_t A, const fmpz_t c, fmpz_struct * const * exp, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_push_term_fmpz_ffmpz(fmpq_mpoly_t A, const fmpz_t c, const fmpz_struct * exp, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_push_term_ui_fmpz(fmpq_mpoly_t A, ulong c, fmpz_struct * const * exp, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_push_term_ui_ffmpz(fmpq_mpoly_t A, ulong c, const fmpz_struct * exp, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_push_term_si_fmpz(fmpq_mpoly_t A, slong c, fmpz_struct * const * exp, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_push_term_si_ffmpz(fmpq_mpoly_t A, slong c, const fmpz_struct * exp, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_push_term_fmpq_ui(fmpq_mpoly_t A, const fmpq_t c, const ulong * exp, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_push_term_fmpz_ui(fmpq_mpoly_t A, const fmpz_t c, const ulong * exp, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_push_term_ui_ui(fmpq_mpoly_t A, ulong c, const ulong * exp, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_push_term_si_ui(fmpq_mpoly_t A, slong c, const ulong * exp, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_reduce(fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_sort_terms(fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_combine_like_terms(fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_reverse(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_randtest_bound(fmpq_mpoly_t A, flint_rand_t state, slong length, mp_limb_t coeff_bits, ulong exp_bound, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_randtest_bounds(fmpq_mpoly_t A, flint_rand_t state, slong length, mp_limb_t coeff_bits, ulong * exp_bounds, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_randtest_bits(fmpq_mpoly_t A, flint_rand_t state, slong length, mp_limb_t coeff_bits, mp_limb_t exp_bits, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_add_fmpq(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_t c, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_add_fmpz(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpz_t c, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_add_ui(fmpq_mpoly_t A, const fmpq_mpoly_t B, ulong c, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_add_si(fmpq_mpoly_t A, const fmpq_mpoly_t B, slong c, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_sub_fmpq(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_t c, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_sub_fmpz(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpz_t c, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_sub_ui(fmpq_mpoly_t A, const fmpq_mpoly_t B, ulong c, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_sub_si(fmpq_mpoly_t A, const fmpq_mpoly_t B, slong c, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_add(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_t C, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_sub(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_t C, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_neg(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_scalar_mul_fmpq(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_t c, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_scalar_mul_fmpz(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpz_t c, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_scalar_mul_ui(fmpq_mpoly_t A, const fmpq_mpoly_t B, ulong c, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_scalar_mul_si(fmpq_mpoly_t A, const fmpq_mpoly_t B, slong c, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_scalar_div_fmpq(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_t c, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_scalar_div_fmpz(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpz_t c, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_scalar_div_ui(fmpq_mpoly_t A, const fmpq_mpoly_t B, ulong c, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_scalar_div_si(fmpq_mpoly_t A, const fmpq_mpoly_t B, slong c, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_make_monic(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_derivative(fmpq_mpoly_t A, const fmpq_mpoly_t B, slong var, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_integral(fmpq_mpoly_t A, const fmpq_mpoly_t B, slong var, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_evaluate_all_fmpq(fmpq_t ev, const fmpq_mpoly_t A, fmpq_struct * const * vals, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_evaluate_one_fmpq(fmpq_mpoly_t A, const fmpq_mpoly_t B, slong var, const fmpq_t val, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_compose_fmpq_poly(fmpq_poly_t A, const fmpq_mpoly_t B, fmpq_poly_struct * const * C, const fmpq_mpoly_ctx_t ctxB) + int fmpq_mpoly_compose_fmpq_mpoly(fmpq_mpoly_t A, const fmpq_mpoly_t B, fmpq_mpoly_struct * const * C, const fmpq_mpoly_ctx_t ctxB, const fmpq_mpoly_ctx_t ctxAC) + void fmpq_mpoly_compose_fmpq_mpoly_gen(fmpq_mpoly_t A, const fmpq_mpoly_t B, const slong * c, const fmpq_mpoly_ctx_t ctxB, const fmpq_mpoly_ctx_t ctxAC) + void fmpq_mpoly_mul(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_t C, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_pow_fmpz(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpz_t k, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_pow_ui(fmpq_mpoly_t A, const fmpq_mpoly_t B, ulong k, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_divides(fmpq_mpoly_t Q, const fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_div(fmpq_mpoly_t Q, const fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_divrem(fmpq_mpoly_t Q, fmpq_mpoly_t R, const fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_divrem_ideal(fmpq_mpoly_struct ** Q, fmpq_mpoly_t R, const fmpq_mpoly_t A, fmpq_mpoly_struct * const * B, slong len, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_content(fmpq_t g, const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_term_content(fmpq_mpoly_t M, const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_content_vars(fmpq_mpoly_t g, const fmpq_mpoly_t A, slong * vars, slong vars_length, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_gcd(fmpq_mpoly_t G, const fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_gcd_cofactors(fmpq_mpoly_t G, fmpq_mpoly_t Abar, fmpq_mpoly_t Bbar, const fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_gcd_brown(fmpq_mpoly_t G, const fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_gcd_hensel(fmpq_mpoly_t G, const fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_gcd_subresultant(fmpq_mpoly_t G, const fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_gcd_zippel(fmpq_mpoly_t G, const fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_gcd_zippel2(fmpq_mpoly_t G, const fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_resultant(fmpq_mpoly_t R, const fmpq_mpoly_t A, const fmpq_mpoly_t B, slong var, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_discriminant(fmpq_mpoly_t D, const fmpq_mpoly_t A, slong var, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_sqrt(fmpq_mpoly_t Q, const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_is_square(const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_univar_init(fmpq_mpoly_univar_t A, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_univar_clear(fmpq_mpoly_univar_t A, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_univar_swap(fmpq_mpoly_univar_t A, fmpq_mpoly_univar_t B, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_to_univar(fmpq_mpoly_univar_t A, const fmpq_mpoly_t B, slong var, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_from_univar(fmpq_mpoly_t A, const fmpq_mpoly_univar_t B, slong var, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_univar_degree_fits_si(const fmpq_mpoly_univar_t A, const fmpq_mpoly_ctx_t ctx) + slong fmpq_mpoly_univar_length(const fmpq_mpoly_univar_t A, const fmpq_mpoly_ctx_t ctx) + slong fmpq_mpoly_univar_get_term_exp_si(fmpq_mpoly_univar_t A, slong i, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_univar_get_term_coeff(fmpq_mpoly_t c, const fmpq_mpoly_univar_t A, slong i, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_univar_swap_term_coeff(fmpq_mpoly_t c, fmpq_mpoly_univar_t A, slong i, const fmpq_mpoly_ctx_t ctx) diff --git a/src/flint/flintlib/fmpq_mpoly_factor.pxd b/src/flint/flintlib/fmpq_mpoly_factor.pxd new file mode 100644 index 00000000..3336acf6 --- /dev/null +++ b/src/flint/flintlib/fmpq_mpoly_factor.pxd @@ -0,0 +1,27 @@ +from flint.flintlib.fmpq cimport fmpq_t +from flint.flintlib.fmpz cimport fmpz_t +from flint.flintlib.fmpq_mpoly cimport fmpq_mpoly_ctx_t, fmpq_mpoly_t, fmpq_mpoly_struct +from flint.flintlib.flint cimport slong, fmpz_struct + +cdef extern from "flint/fmpq_mpoly_factor.h": + ctypedef struct fmpq_mpoly_factor_struct: + fmpq_t constant + fmpq_mpoly_struct * poly + fmpz_struct * exp + slong num + slong alloc + ctypedef fmpq_mpoly_factor_struct fmpq_mpoly_factor_t[1] + + void fmpq_mpoly_factor_init(fmpq_mpoly_factor_t f, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_factor_clear(fmpq_mpoly_factor_t f, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_factor_swap(fmpq_mpoly_factor_t f, fmpq_mpoly_factor_t g, const fmpq_mpoly_ctx_t ctx) + slong fmpq_mpoly_factor_length(const fmpq_mpoly_factor_t f, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_factor_get_constant_fmpq(fmpq_t c, const fmpq_mpoly_factor_t f, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_factor_get_base(fmpq_mpoly_t B, const fmpq_mpoly_factor_t f, slong i, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_factor_swap_base(fmpq_mpoly_t B, fmpq_mpoly_factor_t f, slong i, const fmpq_mpoly_ctx_t ctx) + slong fmpq_mpoly_factor_get_exp_si(fmpq_mpoly_factor_t f, slong i, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_factor_sort(fmpq_mpoly_factor_t f, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_factor_make_monic(fmpq_mpoly_factor_t f, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_factor_make_integral(fmpq_mpoly_factor_t f, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_factor_squarefree(fmpq_mpoly_factor_t f, const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_factor(fmpq_mpoly_factor_t f, const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) diff --git a/src/flint/flintlib/fmpq_vec.pxd b/src/flint/flintlib/fmpq_vec.pxd new file mode 100644 index 00000000..e8681822 --- /dev/null +++ b/src/flint/flintlib/fmpq_vec.pxd @@ -0,0 +1,18 @@ +from flint.flintlib.flint cimport flint_bitcnt_t, fmpz_struct, slong, flint_rand_t +from flint.flintlib.fmpq cimport fmpq_t, fmpq_struct +from flint.flintlib.fmpz cimport fmpz_t + + +# unimported types set() + +cdef extern from "flint/fmpq_vec.h": + fmpq_struct * _fmpq_vec_init(slong n) + void _fmpq_vec_clear(fmpq_struct * vec, slong n) + void _fmpq_vec_randtest(fmpq_struct * f, flint_rand_t state, slong len, flint_bitcnt_t bits) + void _fmpq_vec_randtest_uniq_sorted(fmpq_struct * vec, flint_rand_t state, slong len, flint_bitcnt_t bits) + void _fmpq_vec_sort(fmpq_struct * vec, slong len) + void _fmpq_vec_set_fmpz_vec(fmpq_struct * res, const fmpz_struct * vec, slong len) + void _fmpq_vec_get_fmpz_vec_fmpz(fmpz_struct * num, fmpz_t den, const fmpq_struct * a, slong len) + void _fmpq_vec_dot(fmpq_t res, const fmpq_struct * vec1, const fmpq_struct * vec2, slong len) + # int _fmpq_vec_fprint(FILE * file, const fmpq_struct * vec, slong len) + int _fmpq_vec_print(const fmpq_struct * vec, slong len) diff --git a/src/flint/flintlib/fmpz_mpoly.pxd b/src/flint/flintlib/fmpz_mpoly.pxd index d74f2df1..6cc7d167 100644 --- a/src/flint/flintlib/fmpz_mpoly.pxd +++ b/src/flint/flintlib/fmpz_mpoly.pxd @@ -18,9 +18,24 @@ cdef extern from "flint/fmpz_mpoly.h": ctypedef fmpz_mpoly_struct fmpz_mpoly_t[1] + ctypedef struct fmpz_mpoly_univar_struct: + fmpz_mpoly_struct * coeffs + fmpz_struct * exps + slong alloc + slong length + + ctypedef fmpz_mpoly_univar_struct fmpz_mpoly_univar_t[1] + + ctypedef struct fmpz_mpoly_vec_struct: + fmpz_mpoly_struct * p + slong alloc + slong length + + ctypedef fmpz_mpoly_vec_struct fmpz_mpoly_vec_t[1] + # from here on is parsed void fmpz_mpoly_ctx_init(fmpz_mpoly_ctx_t ctx, slong nvars, const ordering_t ord) - slong fmpz_mpoly_ctx_nvars(fmpz_mpoly_ctx_t ctx) + slong fmpz_mpoly_ctx_nvars(const fmpz_mpoly_ctx_t ctx) ordering_t fmpz_mpoly_ctx_ord(const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_ctx_clear(fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_init(fmpz_mpoly_t A, const fmpz_mpoly_ctx_t ctx) @@ -31,12 +46,13 @@ cdef extern from "flint/fmpz_mpoly.h": void fmpz_mpoly_realloc(fmpz_mpoly_t A, slong alloc, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_clear(fmpz_mpoly_t A, const fmpz_mpoly_ctx_t ctx) char * fmpz_mpoly_get_str_pretty(const fmpz_mpoly_t A, const char ** x, const fmpz_mpoly_ctx_t ctx) + # int fmpz_mpoly_fprint_pretty(FILE * file, const fmpz_mpoly_t A, const char ** x, const fmpz_mpoly_ctx_t ctx) int fmpz_mpoly_print_pretty(const fmpz_mpoly_t A, const char ** x, const fmpz_mpoly_ctx_t ctx) int fmpz_mpoly_set_str_pretty(fmpz_mpoly_t A, const char * str, const char ** x, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_gen(fmpz_mpoly_t A, slong var, const fmpz_mpoly_ctx_t ctx) int fmpz_mpoly_is_gen(const fmpz_mpoly_t A, slong var, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_set(fmpz_mpoly_t A, const fmpz_mpoly_t B, const fmpz_mpoly_ctx_t ctx) - int fmpz_mpoly_equal(fmpz_mpoly_t A, const fmpz_mpoly_t B, const fmpz_mpoly_ctx_t ctx) + int fmpz_mpoly_equal(const fmpz_mpoly_t A, const fmpz_mpoly_t B, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_swap(fmpz_mpoly_t poly1, fmpz_mpoly_t poly2, const fmpz_mpoly_ctx_t ctx) int _fmpz_mpoly_fits_small(const fmpz_struct * poly, slong len) slong fmpz_mpoly_max_bits(const fmpz_mpoly_t A) @@ -47,7 +63,7 @@ cdef extern from "flint/fmpz_mpoly.h": void fmpz_mpoly_set_si(fmpz_mpoly_t A, slong c, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_zero(fmpz_mpoly_t A, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_one(fmpz_mpoly_t A, const fmpz_mpoly_ctx_t ctx) - int fmpz_mpoly_equal_fmpz(const fmpz_mpoly_t A, fmpz_t c, const fmpz_mpoly_ctx_t ctx) + int fmpz_mpoly_equal_fmpz(const fmpz_mpoly_t A, const fmpz_t c, const fmpz_mpoly_ctx_t ctx) int fmpz_mpoly_equal_ui(const fmpz_mpoly_t A, ulong c, const fmpz_mpoly_ctx_t ctx) int fmpz_mpoly_equal_si(const fmpz_mpoly_t A, slong c, const fmpz_mpoly_ctx_t ctx) int fmpz_mpoly_is_zero(const fmpz_mpoly_t A, const fmpz_mpoly_ctx_t ctx) @@ -69,14 +85,18 @@ cdef extern from "flint/fmpz_mpoly.h": void fmpz_mpoly_get_coeff_fmpz_ui(fmpz_t c, const fmpz_mpoly_t A, const ulong * exp, const fmpz_mpoly_ctx_t ctx) ulong fmpz_mpoly_get_coeff_ui_ui(const fmpz_mpoly_t A, const ulong * exp, const fmpz_mpoly_ctx_t ctx) slong fmpz_mpoly_get_coeff_si_ui(const fmpz_mpoly_t A, const ulong * exp, const fmpz_mpoly_ctx_t ctx) - void fmpz_mpoly_set_coeff_fmpz_fmpz(fmpz_mpoly_t A, const fmpz_t c, const fmpz_struct ** exp, fmpz_mpoly_ctx_t ctx) - void fmpz_mpoly_set_coeff_ui_fmpz(fmpz_mpoly_t A, ulong c, const fmpz_struct ** exp, const fmpz_mpoly_ctx_t ctx) - void fmpz_mpoly_set_coeff_si_fmpz(fmpz_mpoly_t A, slong c, const fmpz_struct ** exp, const fmpz_mpoly_ctx_t ctx) - void fmpz_mpoly_set_coeff_fmpz_ui(fmpz_mpoly_t A, const fmpz_t c, const ulong * exp, fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_set_coeff_fmpz_fmpz(fmpz_mpoly_t A, const fmpz_t c, fmpz_struct * const * exp, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_set_coeff_ui_fmpz(fmpz_mpoly_t A, ulong c, fmpz_struct * const * exp, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_set_coeff_si_fmpz(fmpz_mpoly_t A, slong c, fmpz_struct * const * exp, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_set_coeff_fmpz_ui(fmpz_mpoly_t A, const fmpz_t c, const ulong * exp, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_set_coeff_ui_ui(fmpz_mpoly_t A, ulong c, const ulong * exp, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_set_coeff_si_ui(fmpz_mpoly_t A, slong c, const ulong * exp, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_get_coeff_vars_ui(fmpz_mpoly_t C, const fmpz_mpoly_t A, const slong * vars, const ulong * exps, slong length, const fmpz_mpoly_ctx_t ctx) int fmpz_mpoly_cmp(const fmpz_mpoly_t A, const fmpz_mpoly_t B, const fmpz_mpoly_ctx_t ctx) + int fmpz_mpoly_is_fmpz_poly(const fmpz_mpoly_t A, slong var, const fmpz_mpoly_ctx_t ctx) + int fmpz_mpoly_get_fmpz_poly(fmpz_poly_t A, const fmpz_mpoly_t B, slong var, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_set_fmpz_poly(fmpz_mpoly_t A, const fmpz_poly_t B, slong var, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_set_gen_fmpz_poly(fmpz_mpoly_t A, slong var, const fmpz_poly_t B, const fmpz_mpoly_ctx_t ctx) fmpz_struct * fmpz_mpoly_term_coeff_ref(fmpz_mpoly_t A, slong i, const fmpz_mpoly_ctx_t ctx) int fmpz_mpoly_is_canonical(const fmpz_mpoly_t A, const fmpz_mpoly_ctx_t ctx) slong fmpz_mpoly_length(const fmpz_mpoly_t A, const fmpz_mpoly_ctx_t ctx) @@ -99,8 +119,11 @@ cdef extern from "flint/fmpz_mpoly.h": void fmpz_mpoly_get_term(fmpz_mpoly_t M, const fmpz_mpoly_t A, slong i, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_get_term_monomial(fmpz_mpoly_t M, const fmpz_mpoly_t A, slong i, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_push_term_fmpz_fmpz(fmpz_mpoly_t A, const fmpz_t c, fmpz_struct * const * exp, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_push_term_fmpz_ffmpz(fmpz_mpoly_t A, const fmpz_t c, const fmpz_struct * exp, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_push_term_ui_fmpz(fmpz_mpoly_t A, ulong c, fmpz_struct * const * exp, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_push_term_ui_ffmpz(fmpz_mpoly_t A, ulong c, const fmpz_struct * exp, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_push_term_si_fmpz(fmpz_mpoly_t A, slong c, fmpz_struct * const * exp, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_push_term_si_ffmpz(fmpz_mpoly_t A, slong c, const fmpz_struct * exp, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_push_term_fmpz_ui(fmpz_mpoly_t A, const fmpz_t c, const ulong * exp, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_push_term_ui_ui(fmpz_mpoly_t A, ulong c, const ulong * exp, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_push_term_si_ui(fmpz_mpoly_t A, slong c, const ulong * exp, const fmpz_mpoly_ctx_t ctx) @@ -110,10 +133,10 @@ cdef extern from "flint/fmpz_mpoly.h": void fmpz_mpoly_randtest_bound(fmpz_mpoly_t A, flint_rand_t state, slong length, mp_limb_t coeff_bits, ulong exp_bound, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_randtest_bounds(fmpz_mpoly_t A, flint_rand_t state, slong length, mp_limb_t coeff_bits, ulong * exp_bounds, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_randtest_bits(fmpz_mpoly_t A, flint_rand_t state, slong length, mp_limb_t coeff_bits, mp_limb_t exp_bits, const fmpz_mpoly_ctx_t ctx) - void fmpz_mpoly_add_fmpz(fmpz_mpoly_t A, const fmpz_mpoly_t B, fmpz_t c, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_add_fmpz(fmpz_mpoly_t A, const fmpz_mpoly_t B, const fmpz_t c, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_add_ui(fmpz_mpoly_t A, const fmpz_mpoly_t B, ulong c, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_add_si(fmpz_mpoly_t A, const fmpz_mpoly_t B, slong c, const fmpz_mpoly_ctx_t ctx) - void fmpz_mpoly_sub_fmpz(fmpz_mpoly_t A, const fmpz_mpoly_t B, fmpz_t c, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_sub_fmpz(fmpz_mpoly_t A, const fmpz_mpoly_t B, const fmpz_t c, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_sub_ui(fmpz_mpoly_t A, const fmpz_mpoly_t B, ulong c, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_sub_si(fmpz_mpoly_t A, const fmpz_mpoly_t B, slong c, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_add(fmpz_mpoly_t A, const fmpz_mpoly_t B, const fmpz_mpoly_t C, const fmpz_mpoly_ctx_t ctx) @@ -165,34 +188,63 @@ cdef extern from "flint/fmpz_mpoly.h": int fmpz_mpoly_gcd_zippel2(fmpz_mpoly_t G, const fmpz_mpoly_t A, const fmpz_mpoly_t B, const fmpz_mpoly_ctx_t ctx) int fmpz_mpoly_resultant(fmpz_mpoly_t R, const fmpz_mpoly_t A, const fmpz_mpoly_t B, slong var, const fmpz_mpoly_ctx_t ctx) int fmpz_mpoly_discriminant(fmpz_mpoly_t D, const fmpz_mpoly_t A, slong var, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_primitive_part(fmpz_mpoly_t res, const fmpz_mpoly_t f, const fmpz_mpoly_ctx_t ctx) int fmpz_mpoly_sqrt_heap(fmpz_mpoly_t Q, const fmpz_mpoly_t A, const fmpz_mpoly_ctx_t ctx, int check) int fmpz_mpoly_sqrt(fmpz_mpoly_t q, const fmpz_mpoly_t A, const fmpz_mpoly_ctx_t ctx) int fmpz_mpoly_is_square(const fmpz_mpoly_t A, const fmpz_mpoly_ctx_t ctx) - # void fmpz_mpoly_univar_init(fmpz_mpoly_univar_t A, const fmpz_mpoly_ctx_t ctx) - # void fmpz_mpoly_univar_clear(fmpz_mpoly_univar_t A, const fmpz_mpoly_ctx_t ctx) - # void fmpz_mpoly_univar_swap(fmpz_mpoly_univar_t A, fmpz_mpoly_univar_t B, const fmpz_mpoly_ctx_t ctx) - # void fmpz_mpoly_to_univar(fmpz_mpoly_univar_t A, const fmpz_mpoly_t B, slong var, const fmpz_mpoly_ctx_t ctx) - # void fmpz_mpoly_from_univar(fmpz_mpoly_t A, const fmpz_mpoly_univar_t B, slong var, const fmpz_mpoly_ctx_t ctx) - # int fmpz_mpoly_univar_degree_fits_si(const fmpz_mpoly_univar_t A, const fmpz_mpoly_ctx_t ctx) - # slong fmpz_mpoly_univar_length(const fmpz_mpoly_univar_t A, const fmpz_mpoly_ctx_t ctx) - # slong fmpz_mpoly_univar_get_term_exp_si(fmpz_mpoly_univar_t A, slong i, const fmpz_mpoly_ctx_t ctx) - # void fmpz_mpoly_univar_get_term_coeff(fmpz_mpoly_t c, const fmpz_mpoly_univar_t A, slong i, const fmpz_mpoly_ctx_t ctx) - # void fmpz_mpoly_univar_swap_term_coeff(fmpz_mpoly_t c, fmpz_mpoly_univar_t A, slong i, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_univar_init(fmpz_mpoly_univar_t A, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_univar_clear(fmpz_mpoly_univar_t A, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_univar_swap(fmpz_mpoly_univar_t A, fmpz_mpoly_univar_t B, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_to_univar(fmpz_mpoly_univar_t A, const fmpz_mpoly_t B, slong var, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_from_univar(fmpz_mpoly_t A, const fmpz_mpoly_univar_t B, slong var, const fmpz_mpoly_ctx_t ctx) + int fmpz_mpoly_univar_degree_fits_si(const fmpz_mpoly_univar_t A, const fmpz_mpoly_ctx_t ctx) + slong fmpz_mpoly_univar_length(const fmpz_mpoly_univar_t A, const fmpz_mpoly_ctx_t ctx) + slong fmpz_mpoly_univar_get_term_exp_si(fmpz_mpoly_univar_t A, slong i, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_univar_get_term_coeff(fmpz_mpoly_t c, const fmpz_mpoly_univar_t A, slong i, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_univar_swap_term_coeff(fmpz_mpoly_t c, fmpz_mpoly_univar_t A, slong i, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_inflate(fmpz_mpoly_t A, const fmpz_mpoly_t B, const fmpz_struct * shift, const fmpz_struct * stride, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_deflate(fmpz_mpoly_t A, const fmpz_mpoly_t B, const fmpz_struct * shift, const fmpz_struct * stride, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_deflation(fmpz_struct * shift, fmpz_struct * stride, const fmpz_mpoly_t A, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_pow_fps(fmpz_mpoly_t A, const fmpz_mpoly_t B, ulong k, const fmpz_mpoly_ctx_t ctx) slong _fmpz_mpoly_divides_array(fmpz_struct ** poly1, ulong ** exp1, slong * alloc, const fmpz_struct * poly2, const ulong * exp2, slong len2, const fmpz_struct * poly3, const ulong * exp3, slong len3, slong * mults, slong num, slong bits) int fmpz_mpoly_divides_array(fmpz_mpoly_t poly1, const fmpz_mpoly_t poly2, const fmpz_mpoly_t poly3, const fmpz_mpoly_ctx_t ctx) - slong _fmpz_mpoly_divides_monagan_pearce(fmpz_struct ** poly1, ulong ** exp1, slong * alloc, const fmpz_struct * poly2, const ulong * exp2, slong len2, const fmpz_struct * poly3, const ulong * exp3, slong len3, slong bits, slong N) + slong _fmpz_mpoly_divides_monagan_pearce(fmpz_struct ** poly1, ulong ** exp1, slong * alloc, const fmpz_struct * poly2, const ulong * exp2, slong len2, const fmpz_struct * poly3, const ulong * exp3, slong len3, ulong bits, slong N, const mp_limb_t * cmpmask) int fmpz_mpoly_divides_monagan_pearce(fmpz_mpoly_t poly1, const fmpz_mpoly_t poly2, const fmpz_mpoly_t poly3, const fmpz_mpoly_ctx_t ctx) - int fmpz_mpoly_divides_heap_threaded(fmpz_mpoly_t Q, const fmpz_mpoly_t A, const fmpz_mpoly_t B, const fmpz_mpoly_ctx_t ctx, slong thread_limit) - slong _fmpz_mpoly_div_monagan_pearce(fmpz_struct ** polyq, ulong ** expq, slong * allocq, const fmpz_struct * poly2, const ulong * exp2, slong len2, const fmpz_struct * poly3, const ulong * exp3, slong len3, slong bits, slong N) + int fmpz_mpoly_divides_heap_threaded(fmpz_mpoly_t Q, const fmpz_mpoly_t A, const fmpz_mpoly_t B, const fmpz_mpoly_ctx_t ctx) + slong _fmpz_mpoly_div_monagan_pearce(fmpz_struct ** polyq, ulong ** expq, slong * allocq, const fmpz_struct * poly2, const ulong * exp2, slong len2, const fmpz_struct * poly3, const ulong * exp3, slong len3, slong bits, slong N, const mp_limb_t * cmpmask) void fmpz_mpoly_div_monagan_pearce(fmpz_mpoly_t polyq, const fmpz_mpoly_t poly2, const fmpz_mpoly_t poly3, const fmpz_mpoly_ctx_t ctx) - slong _fmpz_mpoly_divrem_monagan_pearce(slong * lenr, fmpz_struct ** polyq, ulong ** expq, slong * allocq, fmpz_struct ** polyr, ulong ** expr, slong * allocr, const fmpz_struct * poly2, const ulong * exp2, slong len2, const fmpz_struct * poly3, const ulong * exp3, slong len3, slong bits, slong N) + slong _fmpz_mpoly_divrem_monagan_pearce(slong * lenr, fmpz_struct ** polyq, ulong ** expq, slong * allocq, fmpz_struct ** polyr, ulong ** expr, slong * allocr, const fmpz_struct * poly2, const ulong * exp2, slong len2, const fmpz_struct * poly3, const ulong * exp3, slong len3, slong bits, slong N, const mp_limb_t * cmpmask) void fmpz_mpoly_divrem_monagan_pearce(fmpz_mpoly_t q, fmpz_mpoly_t r, const fmpz_mpoly_t poly2, const fmpz_mpoly_t poly3, const fmpz_mpoly_ctx_t ctx) slong _fmpz_mpoly_divrem_array(slong * lenr, fmpz_struct ** polyq, ulong ** expq, slong * allocq, fmpz_struct ** polyr, ulong ** expr, slong * allocr, const fmpz_struct * poly2, const ulong * exp2, slong len2, const fmpz_struct * poly3, const ulong * exp3, slong len3, slong * mults, slong num, slong bits) int fmpz_mpoly_divrem_array(fmpz_mpoly_t q, fmpz_mpoly_t r, const fmpz_mpoly_t poly2, const fmpz_mpoly_t poly3, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_quasidivrem_heap(fmpz_t scale, fmpz_mpoly_t q, fmpz_mpoly_t r, const fmpz_mpoly_t poly2, const fmpz_mpoly_t poly3, const fmpz_mpoly_ctx_t ctx) - slong _fmpz_mpoly_divrem_ideal_monagan_pearce(fmpz_mpoly_struct ** polyq, fmpz_struct ** polyr, ulong ** expr, slong * allocr, const fmpz_struct * poly2, const ulong * exp2, slong len2, fmpz_mpoly_struct * const * poly3, ulong * const * exp3, slong len, slong N, slong bits, const fmpz_mpoly_ctx_t ctx) + slong _fmpz_mpoly_divrem_ideal_monagan_pearce(fmpz_mpoly_struct ** polyq, fmpz_struct ** polyr, ulong ** expr, slong * allocr, const fmpz_struct * poly2, const ulong * exp2, slong len2, fmpz_mpoly_struct * const * poly3, ulong * const * exp3, slong len, slong N, slong bits, const fmpz_mpoly_ctx_t ctx, const mp_limb_t * cmpmask) void fmpz_mpoly_divrem_ideal_monagan_pearce(fmpz_mpoly_struct ** q, fmpz_mpoly_t r, const fmpz_mpoly_t poly2, fmpz_mpoly_struct * const * poly3, slong len, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_vec_init(fmpz_mpoly_vec_t vec, slong len, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_vec_clear(fmpz_mpoly_vec_t vec, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_vec_print(const fmpz_mpoly_vec_t vec, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_vec_swap(fmpz_mpoly_vec_t x, fmpz_mpoly_vec_t y, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_vec_fit_length(fmpz_mpoly_vec_t vec, slong len, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_vec_set(fmpz_mpoly_vec_t dest, const fmpz_mpoly_vec_t src, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_vec_append(fmpz_mpoly_vec_t vec, const fmpz_mpoly_t f, const fmpz_mpoly_ctx_t ctx) + slong fmpz_mpoly_vec_insert_unique(fmpz_mpoly_vec_t vec, const fmpz_mpoly_t f, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_vec_set_length(fmpz_mpoly_vec_t vec, slong len, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_vec_randtest_not_zero(fmpz_mpoly_vec_t vec, flint_rand_t state, slong len, slong poly_len, slong bits, ulong exp_bound, fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_vec_set_primitive_unique(fmpz_mpoly_vec_t res, const fmpz_mpoly_vec_t src, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_spoly(fmpz_mpoly_t res, const fmpz_mpoly_t f, const fmpz_mpoly_t g, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_reduction_primitive_part(fmpz_mpoly_t res, const fmpz_mpoly_t f, const fmpz_mpoly_vec_t vec, const fmpz_mpoly_ctx_t ctx) + int fmpz_mpoly_vec_is_groebner(const fmpz_mpoly_vec_t G, const fmpz_mpoly_vec_t F, const fmpz_mpoly_ctx_t ctx) + int fmpz_mpoly_vec_is_autoreduced(const fmpz_mpoly_vec_t F, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_vec_autoreduction(fmpz_mpoly_vec_t H, const fmpz_mpoly_vec_t F, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_vec_autoreduction_groebner(fmpz_mpoly_vec_t H, const fmpz_mpoly_vec_t G, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_buchberger_naive(fmpz_mpoly_vec_t G, const fmpz_mpoly_vec_t F, const fmpz_mpoly_ctx_t ctx) + int fmpz_mpoly_buchberger_naive_with_limits(fmpz_mpoly_vec_t G, const fmpz_mpoly_vec_t F, slong ideal_len_limit, slong poly_len_limit, slong poly_bits_limit, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_symmetric_gens(fmpz_mpoly_t res, ulong k, slong * vars, slong n, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_symmetric(fmpz_mpoly_t res, ulong k, const fmpz_mpoly_ctx_t ctx) +# undocumented functions + void _fmpz_mpoly_push_exp_ffmpz(fmpz_mpoly_t A, const fmpz_struct * exp, const fmpz_mpoly_ctx_t ctx); + void _fmpz_mpoly_push_exp_pfmpz(fmpz_mpoly_t A, fmpz_struct * const * exp, const fmpz_mpoly_ctx_t ctx); + void _fmpz_mpoly_push_exp_ui(fmpz_mpoly_t A, const ulong * exp, const fmpz_mpoly_ctx_t ctx); + + # Macros + fmpz_mpoly_struct *fmpz_mpoly_vec_entry(fmpz_mpoly_vec_t vec, slong i) diff --git a/src/flint/flintlib/fmpz_mpoly_factor.pxd b/src/flint/flintlib/fmpz_mpoly_factor.pxd new file mode 100644 index 00000000..bba20a9b --- /dev/null +++ b/src/flint/flintlib/fmpz_mpoly_factor.pxd @@ -0,0 +1,32 @@ +from flint.flintlib.fmpz_mpoly cimport fmpz_mpoly_t, fmpz_mpoly_ctx_t, fmpz_mpoly_struct +from flint.flintlib.fmpz cimport fmpz_t +from flint.flintlib.flint cimport slong, fmpz_struct +from flint.flintlib.fmpq cimport fmpq_t + + +# unimported types set() + +cdef extern from "flint/fmpz_mpoly_factor.h": + + ctypedef struct fmpz_mpoly_factor_struct: + fmpz_t constant + fmpz_t constant_den + fmpz_mpoly_struct * poly + fmpz_struct * exp + slong num + slong alloc + + ctypedef fmpz_mpoly_factor_struct fmpz_mpoly_factor_t[1]; + + void fmpz_mpoly_factor_init(fmpz_mpoly_factor_t f, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_factor_clear(fmpz_mpoly_factor_t f, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_factor_swap(fmpz_mpoly_factor_t f, fmpz_mpoly_factor_t g, const fmpz_mpoly_ctx_t ctx) + slong fmpz_mpoly_factor_length(const fmpz_mpoly_factor_t f, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_factor_get_constant_fmpz(fmpz_t c, const fmpz_mpoly_factor_t f, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_factor_get_constant_fmpq(fmpq_t c, const fmpz_mpoly_factor_t f, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_factor_get_base(fmpz_mpoly_t B, const fmpz_mpoly_factor_t f, slong i, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_factor_swap_base(fmpz_mpoly_t B, fmpz_mpoly_factor_t f, slong i, const fmpz_mpoly_ctx_t ctx) + slong fmpz_mpoly_factor_get_exp_si(fmpz_mpoly_factor_t f, slong i, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_factor_sort(fmpz_mpoly_factor_t f, const fmpz_mpoly_ctx_t ctx) + int fmpz_mpoly_factor_squarefree(fmpz_mpoly_factor_t f, const fmpz_mpoly_t A, const fmpz_mpoly_ctx_t ctx) + int fmpz_mpoly_factor(fmpz_mpoly_factor_t f, const fmpz_mpoly_t A, const fmpz_mpoly_ctx_t ctx) diff --git a/src/flint/flintlib/fmpz_mpoly_q.pxd b/src/flint/flintlib/fmpz_mpoly_q.pxd new file mode 100644 index 00000000..682e4263 --- /dev/null +++ b/src/flint/flintlib/fmpz_mpoly_q.pxd @@ -0,0 +1,53 @@ +from flint.flintlib.flint cimport mp_limb_t, flint_rand_t, slong +from flint.flintlib.fmpq cimport fmpq_t +from flint.flintlib.fmpz cimport fmpz_t +from flint.flintlib.fmpz_mpoly cimport fmpz_mpoly_ctx_t, fmpz_mpoly_t, fmpz_mpoly_struct + +cdef extern from "flint/fmpz_mpoly_q.h": + ctypedef struct fmpz_mpoly_q_struct: + fmpz_mpoly_struct num + fmpz_mpoly_struct den + ctypedef fmpz_mpoly_q_struct fmpz_mpoly_q_t[1] + + fmpz_mpoly_struct * fmpz_mpoly_q_numref(fmpz_mpoly_q_t x) + fmpz_mpoly_struct * fmpz_mpoly_q_denref(fmpz_mpoly_q_t x) + void fmpz_mpoly_q_init(fmpz_mpoly_q_t res, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_clear(fmpz_mpoly_q_t res, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_swap(fmpz_mpoly_q_t x, fmpz_mpoly_q_t y, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_set(fmpz_mpoly_q_t res, const fmpz_mpoly_q_t x, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_set_fmpq(fmpz_mpoly_q_t res, const fmpq_t x, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_set_fmpz(fmpz_mpoly_q_t res, const fmpz_t x, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_set_si(fmpz_mpoly_q_t res, slong x, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_canonicalise(fmpz_mpoly_q_t x, const fmpz_mpoly_ctx_t ctx) + int fmpz_mpoly_q_is_canonical(const fmpz_mpoly_q_t x, const fmpz_mpoly_ctx_t ctx) + int fmpz_mpoly_q_is_zero(const fmpz_mpoly_q_t x, const fmpz_mpoly_ctx_t ctx) + int fmpz_mpoly_q_is_one(const fmpz_mpoly_q_t x, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_used_vars(int * used, const fmpz_mpoly_q_t f, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_used_vars_num(int * used, const fmpz_mpoly_q_t f, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_used_vars_den(int * used, const fmpz_mpoly_q_t f, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_zero(fmpz_mpoly_q_t res, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_one(fmpz_mpoly_q_t res, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_gen(fmpz_mpoly_q_t res, slong i, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_print_pretty(const fmpz_mpoly_q_t f, const char ** x, fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_randtest(fmpz_mpoly_q_t res, flint_rand_t state, slong length, mp_limb_t coeff_bits, slong exp_bound, const fmpz_mpoly_ctx_t ctx) + int fmpz_mpoly_q_equal(const fmpz_mpoly_q_t x, const fmpz_mpoly_q_t y, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_neg(fmpz_mpoly_q_t res, const fmpz_mpoly_q_t x, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_add(fmpz_mpoly_q_t res, const fmpz_mpoly_q_t x, const fmpz_mpoly_q_t y, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_add_fmpq(fmpz_mpoly_q_t res, const fmpz_mpoly_q_t x, const fmpq_t y, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_add_fmpz(fmpz_mpoly_q_t res, const fmpz_mpoly_q_t x, const fmpz_t y, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_add_si(fmpz_mpoly_q_t res, const fmpz_mpoly_q_t x, slong y, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_sub(fmpz_mpoly_q_t res, const fmpz_mpoly_q_t x, const fmpz_mpoly_q_t y, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_sub_fmpq(fmpz_mpoly_q_t res, const fmpz_mpoly_q_t x, const fmpq_t y, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_sub_fmpz(fmpz_mpoly_q_t res, const fmpz_mpoly_q_t x, const fmpz_t y, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_sub_si(fmpz_mpoly_q_t res, const fmpz_mpoly_q_t x, slong y, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_mul(fmpz_mpoly_q_t res, const fmpz_mpoly_q_t x, const fmpz_mpoly_q_t y, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_mul_fmpq(fmpz_mpoly_q_t res, const fmpz_mpoly_q_t x, const fmpq_t y, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_mul_fmpz(fmpz_mpoly_q_t res, const fmpz_mpoly_q_t x, const fmpz_t y, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_mul_si(fmpz_mpoly_q_t res, const fmpz_mpoly_q_t x, slong y, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_div(fmpz_mpoly_q_t res, const fmpz_mpoly_q_t x, const fmpz_mpoly_q_t y, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_div_fmpq(fmpz_mpoly_q_t res, const fmpz_mpoly_q_t x, const fmpq_t y, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_div_fmpz(fmpz_mpoly_q_t res, const fmpz_mpoly_q_t x, const fmpz_t y, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_div_si(fmpz_mpoly_q_t res, const fmpz_mpoly_q_t x, slong y, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_inv(fmpz_mpoly_q_t res, const fmpz_mpoly_q_t x, const fmpz_mpoly_ctx_t ctx) + void _fmpz_mpoly_q_content(fmpz_t num, fmpz_t den, const fmpz_mpoly_t xnum, const fmpz_mpoly_t xden, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_content(fmpq_t res, const fmpz_mpoly_q_t x, const fmpz_mpoly_ctx_t ctx) diff --git a/src/flint/pyflint.pxd b/src/flint/pyflint.pxd index ec18e14c..19779743 100644 --- a/src/flint/pyflint.pxd +++ b/src/flint/pyflint.pxd @@ -14,3 +14,4 @@ cdef class Context: cdef public bint pretty cdef public long prec cdef arf_rnd_t rnd + diff --git a/src/flint/test/__main__.py b/src/flint/test/__main__.py index 3a874dee..f837f63a 100644 --- a/src/flint/test/__main__.py +++ b/src/flint/test/__main__.py @@ -57,6 +57,7 @@ def run_doctests(verbose=None): flint.types.fmpz, flint.types.fmpz_poly, flint.types.fmpz_mat, + flint.types.fmpz_mpoly, flint.types.fmpz_series, flint.types.fmpz_mod, flint.types.fmpz_mod_poly, @@ -64,6 +65,7 @@ def run_doctests(verbose=None): flint.types.fmpq, flint.types.fmpq_poly, flint.types.fmpq_mat, + flint.types.fmpq_mpoly, flint.types.fmpq_series, flint.types.nmod, flint.types.nmod_poly, diff --git a/src/flint/test/test_all.py b/src/flint/test/test_all.py index 504d0d69..f44c5e92 100644 --- a/src/flint/test/test_all.py +++ b/src/flint/test/test_all.py @@ -5,7 +5,7 @@ import doctest import platform -from flint.utils.flint_exceptions import DomainError +from flint.utils.flint_exceptions import DomainError, IncompatibleContextError import flint @@ -649,6 +649,7 @@ def set_bad(i,j): assert M6.minpoly() == flint.fmpz_poly([4,-4,1]) assert list(M6) == [2,0,0,0,2,1,0,0,2] + def test_fmpz_series(): Zp = flint.fmpz_poly Z = flint.fmpz_series @@ -1128,6 +1129,7 @@ def set_bad(i): assert M3.charpoly() == flint.fmpq_poly([-1,6,-12,8]) / 8 assert M3.minpoly() == flint.fmpq_poly([1,-4,4]) / 4 + def test_fmpq_series(): Qp = flint.fmpq_poly Q = flint.fmpq_series @@ -2631,6 +2633,414 @@ def setbad(obj, i, val): assert P([1, 2, 1]).integral() == P([0, 1, 1, S(1)/3]) +def _all_mpolys(): + return [ + (flint.fmpz_mpoly, flint.fmpz_mpoly_ctx, flint.fmpz, False), + (flint.fmpq_mpoly, flint.fmpq_mpoly_ctx, flint.fmpq, True), + ] + + +def test_mpolys(): + for P, C, S, is_field in _all_mpolys(): + + ctx = C.get_context(nvars=2) + + assert raises(lambda: C.get_context(nvars=2, ordering="bad"), TypeError) + assert raises(lambda: C.get_context(nvars=-1), ValueError) + assert raises(lambda: C(-1, flint.Ordering.lex, []), ValueError) + assert raises(lambda: ctx.constant("bad"), TypeError) + assert raises(lambda: ctx.from_dict("bad"), ValueError) + assert raises(lambda: ctx.from_dict({(0, 0): "bad"}), TypeError) + assert raises(lambda: ctx.from_dict({(0, "bad"): 1}), TypeError) + assert raises(lambda: ctx.from_dict({(0,): 1}), ValueError) + assert raises(lambda: ctx.gen(-1), IndexError) + assert raises(lambda: ctx.gen(10), IndexError) + + assert raises(lambda: P(val=C.get_context(nvars=1).constant(0), ctx=ctx), IncompatibleContextError) + assert raises(lambda: P(val={}, ctx=None), ValueError) + assert raises(lambda: P(val={"bad": 1}, ctx=None), ValueError) + assert raises(lambda: P(val="1", ctx=None), ValueError) + + assert P(val={(0, 0): 1}, ctx=ctx) == ctx.from_dict({(0, 0): 1}) + assert P(ctx=ctx).context() == ctx + assert P(1, ctx=ctx).is_one() + assert ctx.gen(1) == ctx.from_dict({(0, 1): 1}) + + def mpoly(x): + return ctx.from_dict(x) + + def quick_poly(): + return mpoly({(0, 0): 1, (0, 1): 2, (1, 0): 3, (2, 2): 4}) + + assert ctx.nvars() == 2 + assert ctx.ordering() == flint.Ordering.lex + + ctx1 = C.get_context(4) + assert [ctx1.name(i) for i in range(4)] == ['x0', 'x1', 'x2', 'x3'] + for order in list(flint.Ordering): + ctx1 = C.get_context(4, order) + assert ctx1.ordering() == order + + assert ctx.constant(1) == mpoly({(0, 0): 1}) == P(1, ctx=ctx) + + assert raises(lambda: P([None]), TypeError) + assert raises(lambda: P(object()), TypeError), f"{P(object()) = }" + assert raises(lambda: P(None), TypeError) + assert raises(lambda: P(None, None), TypeError) + assert raises(lambda: P([1,2], None), TypeError) + assert raises(lambda: P(1, None), ValueError) + + assert len(P(ctx=ctx)) == len(mpoly({(0, 0): 0})) == 0 + assert len(P(1, ctx=ctx)) == len(mpoly({(0, 0): 1})) == 1 + assert len(mpoly({(0, 0): 1, (0, 1): 1})) == 2 + assert len(mpoly({(0, 0): 1, (0, 1): 1, (1, 0): 1})) == 3 + + # degree is -1 when empty poly + assert P(ctx=ctx).degrees() == mpoly({(0, 0): 0}).degrees() == (-1, -1) + assert P(1, ctx=ctx).degrees() == mpoly({(0, 0): 1}).degrees() == (0, 0) + assert mpoly({(0, 0): 1, (0, 1): 1}).degrees() == (0, 1) + assert mpoly({(0, 0): 1, (0, 1): 1, (1, 0): 1}).degrees() == (1, 1) + assert mpoly({(0, 0): 1, (0, 1): 1, (1, 0): 1, (2, 2): 2}).degrees() == (2, 2) + + assert (P(1, ctx=ctx) == P(1, ctx=ctx)) is True + assert (P(1, ctx=ctx) != P(1, ctx=ctx)) is False + assert (P(1, ctx=ctx) == P(2, ctx=ctx)) is False + assert (P(1, ctx=ctx) != P(2, ctx=ctx)) is True + + assert (P(1, ctx=ctx) == 1) is False + assert (P(1, ctx=ctx) != 1) is True + assert (1 == P(1, ctx=ctx)) is False + assert (1 != P(1, ctx=ctx)) is True + + assert (P(1, ctx=ctx) == P(1, ctx=ctx1)) is False + assert (P(1, ctx=ctx) != P(1, ctx=ctx1)) is True + + assert (P(1, ctx=ctx) == None) is False + assert (P(1, ctx=ctx) != None) is True + assert (None == P(1, ctx=ctx)) is False + assert (None != P(1, ctx=ctx)) is True + + assert P(ctx.from_dict({(0, 1): 3})) == ctx.from_dict({(0, 1): 3}) + assert P({(0, 1): 3}, ctx=ctx) == ctx.from_dict({(0, 1): 3}) + + if P is flint.fmpq_mpoly: + ctx_z = flint.fmpz_mpoly_ctx.get_context(2) + assert quick_poly() == P(ctx_z.from_dict({(0, 0): 1, (0, 1): 2, (1, 0): 3, (2, 2): 4})) + assert P(ctx_z.from_dict({(0, 0): 1}), ctx=ctx) == P({(0, 0): 1}, ctx=ctx) + + assert raises(lambda: P(ctx=ctx) < P(ctx=ctx), TypeError) + assert raises(lambda: P(ctx=ctx) <= P(ctx=ctx), TypeError) + assert raises(lambda: P(ctx=ctx) > P(ctx=ctx), TypeError) + assert raises(lambda: P(ctx=ctx) >= P(ctx=ctx), TypeError) + assert raises(lambda: P(ctx=ctx) < None, TypeError) + assert raises(lambda: P(ctx=ctx) <= None, TypeError) + assert raises(lambda: P(ctx=ctx) > None, TypeError) + assert raises(lambda: P(ctx=ctx) >= None, TypeError) + assert raises(lambda: None < P(ctx=ctx), TypeError) + assert raises(lambda: None <= P(ctx=ctx), TypeError) + assert raises(lambda: None > P(ctx=ctx), TypeError) + assert raises(lambda: None >= P(ctx=ctx), TypeError) + + p = quick_poly() + assert p.coefficient(2) == S(2) + assert raises(lambda: p.coefficient(-1), IndexError) + assert raises(lambda: p.coefficient(10), IndexError) + + assert raises(lambda: p[-1], TypeError) + assert raises(lambda: p[4], TypeError) + + assert p[(2, 2)] == 4 + assert p[(0, 0)] == 1 + assert raises(lambda: p[(1,)], ValueError) + assert raises(lambda: p[(1, "bad")], TypeError) + assert raises(lambda: p["bad"], TypeError) + + p = quick_poly() + p[(1, 0)] = S(10) + assert p == mpoly({(0, 0): 1, (0, 1): 2, (1, 0): 10, (2, 2): 4}) + + p = quick_poly() + p[(1, 0)] = p[(1, 0)] + assert p == quick_poly() + assert (1, 0) in p + assert (100, 100) not in p + + assert raises(lambda: p.__setitem__((4,), 1), ValueError) + + assert raises(lambda: p.__setitem__((1,), 1), ValueError) + assert raises(lambda: p.__setitem__((1, "bad"), 1), TypeError) + assert raises(lambda: p.__setitem__(("bad", 1), 1), TypeError) + + assert raises(lambda: p.__setitem__((2, 1), None), TypeError) + + assert P(ctx=ctx).repr() == f"{ctx.__class__.__name__}(2, '', ('x0', 'x1')).from_dict({{}})" + assert P(1, ctx=ctx).repr() == f"{ctx.__class__.__name__}(2, '', ('x0', 'x1')).from_dict({{(0, 0): 1}})" + assert str(quick_poly()) == repr(quick_poly()) == '4*x0^2*x1^2 + 3*x0 + 2*x1 + 1' + + assert p.monomial(0) == (2, 2) + assert p.monomial(3) == (0, 0) + assert raises(lambda: p.monomial(-1), IndexError) + assert raises(lambda: p.monomial(4), IndexError) + + assert p.total_degree() == 4 + assert P(ctx=ctx).total_degree() == -1 + assert P(1, ctx=ctx).total_degree() == 0 + + p = quick_poly() + assert p(0, 0) == p(0, S(0)) == p(S(0), S(0)) == S(1) == 1 + assert p(1, 1) == S(10) == 10 + + p = quick_poly() + assert p.monoms() == [(2, 2), (1, 0), (0, 1), (0, 0)] + assert p.coeffs() == [4, 3, 2, 1] + assert list(p.terms()) == list(zip([(2, 2), (1, 0), (0, 1), (0, 0)], [4, 3, 2, 1])) + + assert p.subs({"x1": S(0), 0: S(0)}) == ctx.from_dict({(0, 0): 1}) + assert p.compose(p.subs({"x1": 0}), ctx.from_dict({(0, 1): 1})) == mpoly({ + (2, 2): 36, + (1, 2): 24, + (1, 0): 9, + (0, 2): 4, + (0, 1): 2, + (0, 0): 4 + }) + assert p.compose(ctx.from_dict({(1, 0): 1}), ctx.from_dict({(0, 1): 1})) == p + + assert raises(lambda: p(None, None), TypeError) + assert raises(lambda: p(1), ValueError) + assert raises(lambda: p(0, 1, 2), ValueError) + + assert raises(lambda: p.subs({"x0": None}), TypeError) + assert raises(lambda: p.subs({"x0": None, "x1": None}), TypeError) + assert raises(lambda: p.subs({"a": 1}), ValueError) + assert raises(lambda: p.subs({"x0": 0, "x1": 1, "x2": 2}), ValueError) + + no_gens_ctx = C.get_context(0) + no_gens_p = P("2", no_gens_ctx) + assert no_gens_p.compose(ctx=ctx1).context() is ctx1 + assert raises(lambda: no_gens_p.compose(), ValueError) + + assert raises(lambda: p.compose(p, P(ctx=ctx1)), IncompatibleContextError) + + assert bool(P(ctx=ctx)) is False + assert bool(P(1, ctx=ctx)) is True + + assert +quick_poly() \ + == quick_poly() + assert -quick_poly() \ + == mpoly({(0, 0): -1, (0, 1): -2, (1, 0): -3, (2, 2): -4}) + + assert quick_poly() \ + + mpoly({(0, 0): 5, (0, 1): 6, (1, 0): 7, (2, 2): 8}) \ + == mpoly({(0, 0): 6, (0, 1): 8, (1, 0): 10, (2, 2): 12}) + + for T in [int, S, flint.fmpz, lambda x: P(x, ctx=ctx)]: + p = quick_poly() + p += T(1) + q = quick_poly() + assert q.iadd(T(1)) is None + assert quick_poly() + T(1) \ + == p == q == mpoly({(0, 0): 2, (0, 1): 2, (1, 0): 3, (2, 2): 4}) + assert T(1) + quick_poly() \ + == mpoly({(0, 0): 2, (0, 1): 2, (1, 0): 3, (2, 2): 4}) + + assert raises(lambda: mpoly({(0, 0): 2, (0, 1): 2, (1, 0): 3, (2, 2): 4}) + None, TypeError) + assert raises(lambda: None + mpoly({(0, 0): 2, (0, 1): 2, (1, 0): 3, (2, 2): 4}), TypeError) + assert raises(lambda: quick_poly() + P(ctx=ctx1), IncompatibleContextError) + assert raises(lambda: quick_poly().iadd(P(ctx=ctx1)), IncompatibleContextError) + assert raises(lambda: quick_poly().iadd(None), NotImplementedError) + + assert quick_poly() - mpoly({(0, 0): 5, (0, 1): 6, (1, 0): 7, (2, 2): 8}) \ + == mpoly({(0, 0): -4, (0, 1): -4, (1, 0): -4, (2, 2): -4}) + + for T in [int, S, flint.fmpz, lambda x: P(x, ctx=ctx)]: + p = quick_poly() + p -= T(1) + q = quick_poly() + assert q.isub(T(1)) is None + assert quick_poly() - T(1) == p == q == mpoly({(0, 1): 2, (1, 0): 3, (2, 2): 4}) + assert T(1) - quick_poly() == mpoly({(0, 1): -2, (1, 0): -3, (2, 2): -4}) + + assert raises(lambda: quick_poly() - None, TypeError) + assert raises(lambda: None - quick_poly(), TypeError) + assert raises(lambda: quick_poly() - P(ctx=ctx1), IncompatibleContextError) + assert raises(lambda: quick_poly().isub(P(ctx=ctx1)), IncompatibleContextError) + assert raises(lambda: quick_poly().isub(None), NotImplementedError) + + assert quick_poly() * mpoly({(1, 0): 5, (0, 1): 6}) \ + == mpoly({ + (3, 2): 20, + (2, 3): 24, + (2, 0): 15, + (1, 1): 28, + (1, 0): 5, + (0, 2): 12, + (0, 1): 6 + }) + + for T in [int, S, flint.fmpz, lambda x: P(x, ctx=ctx)]: + p = quick_poly() + p *= T(2) + q = quick_poly() + assert q.imul(T(2)) is None + assert quick_poly() * T(2) == p == q == mpoly({(0, 0): 2, (0, 1): 4, (1, 0): 6, (2, 2): 8}) + assert T(2) * quick_poly() == mpoly({(0, 0): 2, (0, 1): 4, (1, 0): 6, (2, 2): 8}) + + assert raises(lambda: quick_poly() * None, TypeError) + assert raises(lambda: None * quick_poly(), TypeError) + assert raises(lambda: quick_poly() * P(ctx=ctx1), IncompatibleContextError) + assert raises(lambda: quick_poly().imul(P(ctx=ctx1)), IncompatibleContextError) + assert raises(lambda: quick_poly().imul(None), NotImplementedError) + + assert quick_poly() // mpoly({(1, 1): 1}) == mpoly({(1, 1): 4}) + assert quick_poly() % mpoly({(1, 1): 1}) \ + == mpoly({(1, 0): 3, (0, 1): 2, (0, 0): 1}) + assert divmod(quick_poly(), mpoly({(1, 1): 1})) \ + == (mpoly({(1, 1): 4}), mpoly({(1, 0): 3, (0, 1): 2, (0, 0): 1})) + + assert 1 / P(1, ctx=ctx) == P(1, ctx=ctx) + assert quick_poly() / 1 == quick_poly() + assert quick_poly() // 1 == quick_poly() + assert quick_poly() % 1 == P(ctx=ctx) + assert divmod(quick_poly(), 1) == (quick_poly(), P(ctx=ctx)) + + if is_field: + assert quick_poly() / 3 == mpoly({(0, 0): S(1, 3), (0, 1): S(2, 3), (1, 0): S(1), (2, 2): S(4, 3)}) + else: + assert raises(lambda: quick_poly() / 3, DomainError) + + assert 1 // quick_poly() == P(ctx=ctx) + assert 1 % quick_poly() == P(1, ctx=ctx) + assert divmod(1, quick_poly()) == (P(ctx=ctx), P(1, ctx=ctx)) + + assert raises(lambda: quick_poly() / None, TypeError) + assert raises(lambda: quick_poly() // None, TypeError) + assert raises(lambda: quick_poly() % None, TypeError) + assert raises(lambda: divmod(quick_poly(), None), TypeError) + + assert raises(lambda: None / quick_poly(), TypeError) + assert raises(lambda: None // quick_poly(), TypeError) + assert raises(lambda: None % quick_poly(), TypeError) + assert raises(lambda: divmod(None, quick_poly()), TypeError) + + assert raises(lambda: quick_poly() / 0, ZeroDivisionError) + assert raises(lambda: quick_poly() // 0, ZeroDivisionError) + assert raises(lambda: quick_poly() % 0, ZeroDivisionError) + assert raises(lambda: divmod(quick_poly(), 0), ZeroDivisionError) + + assert raises(lambda: 1 / P(ctx=ctx), ZeroDivisionError) + assert raises(lambda: 1 // P(ctx=ctx), ZeroDivisionError) + assert raises(lambda: 1 % P(ctx=ctx), ZeroDivisionError) + assert raises(lambda: divmod(1, P(ctx=ctx)), ZeroDivisionError) + + assert raises(lambda: quick_poly() / P(ctx=ctx), ZeroDivisionError) + assert raises(lambda: quick_poly() // P(ctx=ctx), ZeroDivisionError) + assert raises(lambda: quick_poly() % P(ctx=ctx), ZeroDivisionError) + assert raises(lambda: divmod(quick_poly(), P(ctx=ctx)), ZeroDivisionError) + + assert raises(lambda: quick_poly() / P(1, ctx=ctx1), IncompatibleContextError) + assert raises(lambda: quick_poly() // P(1, ctx=ctx1), IncompatibleContextError) + assert raises(lambda: quick_poly() % P(1, ctx=ctx1), IncompatibleContextError) + assert raises(lambda: divmod(quick_poly(), P(1, ctx=ctx1)), IncompatibleContextError) + + f = mpoly({(1, 1): 4, (0, 0): 1}) + g = mpoly({(0, 1): 2, (1, 0): 2}) + assert f * g / mpoly({(0, 1): 1, (1, 0): 1}) \ + == mpoly({(1, 1): 8, (0, 0): 2}) + + if not is_field: + assert raises(lambda: 1 / quick_poly(), DomainError) + assert raises(lambda: quick_poly() / P(2, ctx=ctx), DomainError) + + assert quick_poly() ** 0 == P(1, ctx=ctx) + assert quick_poly() ** 1 == quick_poly() + assert quick_poly() ** 2 == mpoly({ + (4, 4): 16, + (3, 2): 24, + (2, 3): 16, + (2, 2): 8, + (2, 0): 9, + (1, 1): 12, + (1, 0): 6, + (0, 2): 4, + (0, 1): 4, + (0, 0): 1, + }) + assert raises(lambda: P(ctx=ctx) ** -1, ValueError) + assert raises(lambda: P(ctx=ctx) ** None, TypeError) + + # # XXX: Not sure what this should do in general: + assert raises(lambda: pow(P(1, ctx=ctx), 2, 3), NotImplementedError) + + if is_field: + assert (f * g).gcd(f) == f / 4 + else: + assert (f * g).gcd(f) == f + assert raises(lambda: quick_poly().gcd(None), TypeError) + assert raises(lambda: quick_poly().gcd(P(ctx=ctx1)), IncompatibleContextError) + + assert (f * g).factor() == (S(2), [(mpoly({(0, 1): 1, (1, 0): 1}), 1), (f, 1)]) + + assert (f * f).sqrt() == f + if P is flint.fmpz_mpoly: + assert (f * f).sqrt(assume_perfect_square=True) == f + assert raises(lambda: quick_poly().sqrt(), ValueError) + + p = quick_poly() + assert p.derivative(0) == p.derivative("x0") == mpoly({(0, 0): 3, (1, 2): 8}) + assert p.derivative(1) == p.derivative("x1") == mpoly({(0, 0): 2, (2, 1): 8}) + + assert raises(lambda: p.derivative("x3"), ValueError) + assert raises(lambda: p.derivative(3), IndexError) + assert raises(lambda: p.derivative(None), TypeError) + + if is_field: + assert quick_poly().integral(0) == quick_poly().integral("x0") == \ + mpoly({(3, 2): S(4, 3), (2, 0): S(3, 2), (1, 1): S(2), (1, 0): S(1)}) + assert quick_poly().integral(1) == quick_poly().integral("x1") == \ + mpoly({(2, 3): S(4, 3), (1, 1): S(3), (0, 2): S(1), (0, 1): S(1)}) + else: + assert quick_poly().integral(0) == quick_poly().integral("x0") == \ + (6, mpoly({(3, 2): 8, (2, 0): 9, (1, 1): 12, (1, 0): 6})) + assert quick_poly().integral(1) == quick_poly().integral("x1") == \ + (3, mpoly({(2, 3): 4, (1, 1): 9, (0, 2): 3, (0, 1): 3})) + + assert raises(lambda: p.integral("x3"), ValueError) + assert raises(lambda: p.integral(3), IndexError) + assert raises(lambda: p.integral(None), TypeError) + + +def _all_mpoly_vecs(): + return [(flint.fmpz_mpoly_ctx, flint.fmpz_mpoly_vec), (flint.fmpq_mpoly_ctx, flint.fmpq_mpoly_vec)] + + +def test_fmpz_mpoly_vec(): + for context, mpoly_vec in _all_mpoly_vecs(): + ctx = context.get_context(nvars=2) + ctx1 = context.get_context(nvars=4) + + vec = mpoly_vec(3, ctx) + + assert vec[0] == ctx.from_dict({}) + assert vec[1] == ctx.from_dict({}) + + assert vec[2] == ctx.from_dict({}) + + assert len(vec) == 3 + assert str(vec) == f"[{', '.join(str(ctx.from_dict({})) for _ in range(3))}]" + assert repr(vec) == f"{mpoly_vec.__name__}([{', '.join(str(ctx.from_dict({})) for _ in range(3))}], ctx={str(ctx)})" + + assert raises(lambda: vec[None], TypeError) + assert raises(lambda: vec[-1], IndexError) + + vec[1] = ctx.from_dict({(1, 1): 1}) + assert vec.to_tuple() == mpoly_vec([ctx.from_dict({}), ctx.from_dict({(1, 1): 1}), ctx.from_dict({})], ctx).to_tuple() + assert raises(lambda: vec.__setitem__(None, 0), TypeError) + assert raises(lambda: vec.__setitem__(-1, 0), IndexError) + assert raises(lambda: vec.__setitem__(0, 0), TypeError) + assert raises(lambda: vec.__setitem__(0, ctx1.from_dict({})), IncompatibleContextError) + + def _all_matrices(): """Return a list of matrix types and scalar types.""" R163 = flint.fmpz_mod_ctx(163) @@ -3174,6 +3584,9 @@ def test_all_tests(): test_division_matrix, test_polys, + test_mpolys, + + test_fmpz_mpoly_vec, test_matrices_eq, test_matrices_constructor, diff --git a/src/flint/types/fmpq.pxd b/src/flint/types/fmpq.pxd index 1fe92bc8..5445fdc3 100644 --- a/src/flint/types/fmpq.pxd +++ b/src/flint/types/fmpq.pxd @@ -3,5 +3,6 @@ from flint.flintlib.fmpq cimport fmpq_t cdef int fmpq_set_any_ref(fmpq_t x, obj) cdef any_as_fmpq(obj) + cdef class fmpq(flint_scalar): cdef fmpq_t val diff --git a/src/flint/types/fmpq.pyx b/src/flint/types/fmpq.pyx index fa3cde8c..49c008b5 100644 --- a/src/flint/types/fmpq.pyx +++ b/src/flint/types/fmpq.pyx @@ -5,7 +5,7 @@ from flint.types.fmpz cimport fmpz from flint.types.fmpz cimport any_as_fmpz from flint.flintlib.flint cimport FMPZ_UNKNOWN, FMPZ_TMP, FMPZ_REF -from flint.flintlib.fmpz cimport fmpz_set, fmpz_one +from flint.flintlib.fmpz cimport fmpz_set, fmpz_one, fmpz_t from flint.flintlib.fmpz cimport fmpz_is_zero, fmpz_sgn from flint.flintlib.fmpz cimport fmpz_fdiv_q, fmpz_bits from flint.flintlib.fmpz cimport fmpz_cdiv_q @@ -14,12 +14,6 @@ from flint.flintlib.fmpz cimport fmpz_clear from flint.flintlib.fmpq cimport * from flint.flintlib.bernoulli cimport * -cdef extern from *: - """ - /* An ugly hack to get around the ugly hack of renaming fmpq to avoid a c/python name collision */ - typedef fmpq fmpq_struct; - """ - cdef int fmpq_set_any_ref(fmpq_t x, obj): cdef int status fmpq_init(x) diff --git a/src/flint/types/fmpq_mpoly.pxd b/src/flint/types/fmpq_mpoly.pxd new file mode 100644 index 00000000..166f8479 --- /dev/null +++ b/src/flint/types/fmpq_mpoly.pxd @@ -0,0 +1,39 @@ +from flint.flint_base.flint_base cimport flint_mpoly +from flint.flint_base.flint_base cimport flint_mpoly_context + +from flint.flintlib.fmpq_mpoly cimport ( + fmpq_mpoly_ctx_t, + fmpq_mpoly_t, + fmpq_mpoly_struct, + fmpq_mpoly_init, +) +from flint.flintlib.flint cimport slong + +from flint.types.fmpz_mpoly cimport fmpz_mpoly_ctx + +cdef inline init_fmpq_mpoly(fmpq_mpoly var, fmpq_mpoly_ctx ctx): + var.ctx = ctx + fmpq_mpoly_init(var.val, ctx.val) + var._init = True + +cdef inline create_fmpq_mpoly(fmpq_mpoly_ctx ctx): + cdef fmpq_mpoly var + var = fmpq_mpoly.__new__(fmpq_mpoly) + var.ctx = ctx + fmpq_mpoly_init(var.val, ctx.val) + var._init = True + return var + +cdef class fmpq_mpoly_ctx(flint_mpoly_context): + cdef fmpq_mpoly_ctx_t val + +cdef class fmpq_mpoly(flint_mpoly): + cdef fmpq_mpoly_t val + cdef fmpq_mpoly_ctx ctx + cdef bint _init + +cdef class fmpq_mpoly_vec: + cdef fmpq_mpoly_struct *val + cdef slong length + cdef fmpq_mpoly_ctx ctx + cdef fmpq_mpoly_struct **double_indirect diff --git a/src/flint/types/fmpq_mpoly.pyx b/src/flint/types/fmpq_mpoly.pyx new file mode 100644 index 00000000..234d7f63 --- /dev/null +++ b/src/flint/types/fmpq_mpoly.pyx @@ -0,0 +1,1110 @@ +from flint.flint_base.flint_base cimport ( + flint_mpoly, + flint_mpoly_context, + ordering_py_to_c, + ordering_c_to_py, +) + +from flint.utils.typecheck cimport typecheck +from flint.utils.flint_exceptions import DomainError, IncompatibleContextError + +from flint.types.fmpq cimport fmpq, any_as_fmpq +from flint.types.fmpz_vec cimport fmpz_vec +from flint.types.fmpq_vec cimport fmpq_vec + +from flint.types.fmpz cimport fmpz, any_as_fmpz +from flint.types.fmpz_mpoly cimport fmpz_mpoly + +from flint.flintlib.fmpq cimport fmpq_set, fmpq_one +from flint.flintlib.fmpq_mpoly cimport ( + fmpq_mpoly_add, + fmpq_mpoly_add_fmpq, + fmpq_mpoly_clear, + fmpq_mpoly_compose_fmpq_mpoly, + fmpq_mpoly_ctx_init, + fmpq_mpoly_degrees_fmpz, + fmpq_mpoly_derivative, + fmpq_mpoly_div, + fmpq_mpoly_divides, + fmpq_mpoly_divrem, + fmpq_mpoly_equal, + fmpq_mpoly_evaluate_all_fmpq, + fmpq_mpoly_evaluate_one_fmpq, + fmpq_mpoly_gcd, + fmpq_mpoly_gen, + fmpq_mpoly_get_coeff_fmpq_fmpz, + fmpq_mpoly_get_str_pretty, + fmpq_mpoly_get_term, + fmpq_mpoly_get_term_coeff_fmpq, + fmpq_mpoly_get_term_exp_fmpz, + fmpq_mpoly_integral, + fmpq_mpoly_is_one, + fmpq_mpoly_is_zero, + fmpq_mpoly_length, + fmpq_mpoly_mul, + fmpq_mpoly_neg, + fmpq_mpoly_pow_fmpz, + fmpq_mpoly_push_term_fmpq_ffmpz, + fmpq_mpoly_reduce, + fmpq_mpoly_scalar_div_fmpq, + fmpq_mpoly_scalar_mul_fmpq, + fmpq_mpoly_set, + fmpq_mpoly_set_coeff_fmpq_fmpz, + fmpq_mpoly_set_fmpq, + fmpq_mpoly_set_str_pretty, + fmpq_mpoly_sort_terms, + fmpq_mpoly_sqrt, + fmpq_mpoly_sub, + fmpq_mpoly_sub_fmpq, + fmpq_mpoly_total_degree_fmpz, +) +from flint.flintlib.fmpq_mpoly_factor cimport ( + fmpq_mpoly_factor, + fmpq_mpoly_factor_clear, + fmpq_mpoly_factor_init, + fmpq_mpoly_factor_squarefree, + fmpq_mpoly_factor_t, +) + +from flint.flintlib.fmpz cimport fmpz_init_set +from flint.flintlib.fmpz_mpoly cimport fmpz_mpoly_set + +from cpython.object cimport Py_EQ, Py_NE +cimport libc.stdlib + +cdef dict _fmpq_mpoly_ctx_cache = {} + + +cdef class fmpq_mpoly_ctx(flint_mpoly_context): + """ + A class for storing the polynomial context + + :param nvars: The number of variables in the ring + :param ordering: The term order for the ring + :param names: A tuple containing the names of the variables of the ring. + + Do not construct one of these directly, use `fmpz_mpoly_ctx.get_context`. + """ + + _ctx_cache = _fmpq_mpoly_ctx_cache + + def __init__(self, slong nvars, ordering, names): + fmpq_mpoly_ctx_init(self.val, nvars, ordering_py_to_c(ordering)) + super().__init__(nvars, names) + + def nvars(self): + """ + Return the number of variables in the context + + >>> from flint import Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(4, Ordering.lex, 'x') + >>> ctx.nvars() + 4 + """ + return self.val.zctx.minfo.nvars + + def ordering(self): + """ + Return the term order of the context object. + + >>> from flint import Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(4, Ordering.deglex, 'w') + >>> ctx.ordering() + + """ + return ordering_c_to_py(self.val.zctx.minfo.ord) + + def gen(self, slong i): + """ + Return the `i`th generator of the polynomial ring + + >>> from flint import Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(3, Ordering.degrevlex, 'z') + >>> ctx.gen(1) + z1 + """ + cdef fmpq_mpoly res + if not 0 <= i < self.val.zctx.minfo.nvars: + raise IndexError("generator index out of range") + res = create_fmpq_mpoly(self) + fmpq_mpoly_gen(res.val, i, res.ctx.val) + return res + + def constant(self, z): + """ + Create a constant polynomial in this context + """ + cdef fmpq_mpoly res + z = any_as_fmpq(z) + if z is NotImplemented: + raise TypeError("cannot coerce argument to fmpq") + res = create_fmpq_mpoly(self) + fmpq_mpoly_set_fmpq(res.val, (z).val, res.ctx.val) + return res + + def from_dict(self, d): + """ + Create a fmpq_mpoly from a dictionary. + + The dictionary's keys are tuples of ints (or anything that implicitly converts + to fmpz) representing exponents, and corresponding coefficient values of fmpq. + + >>> from flint import Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x,y') + >>> ctx.from_dict({(1,0):2, (1,1):3, (0,1):1}) + 3*x*y + 2*x + y + """ + cdef: + fmpz_vec exp_vec + slong nvars = self.nvars() + fmpq_mpoly res + + if not isinstance(d, dict): + raise ValueError("expected a dictionary") + + res = create_fmpq_mpoly(self) + + for k, v in d.items(): + o = any_as_fmpq(v) + if o is NotImplemented: + raise TypeError(f"cannot coerce coefficient '{v}' to fmpq") + elif len(k) != nvars: + raise ValueError(f"expected {nvars} exponents, got {len(k)}") + + exp_vec = fmpz_vec(k) + + if o: + fmpq_mpoly_push_term_fmpq_ffmpz(res.val, (o).val, exp_vec.val, self.val) + + fmpq_mpoly_sort_terms(res.val, self.val) + fmpq_mpoly_reduce(res.val, self.val) + return res + + +cdef class fmpq_mpoly(flint_mpoly): + """ + The *fmpq_mpoly* type represents sparse multivariate polynomials over + the integers. + """ + + def __cinit__(self): + self._init = False + + def __dealloc__(self): + if self._init: + fmpq_mpoly_clear(self.val, self.ctx.val) + self._init = False + + def __init__(self, val=0, ctx=None): + if typecheck(val, fmpq_mpoly): + if ctx is None or ctx == (val).ctx: + init_fmpq_mpoly(self, (val).ctx) + fmpq_mpoly_set(self.val, (val).val, self.ctx.val) + else: + raise IncompatibleContextError(f"{ctx} is not {(val).ctx}") + elif typecheck(val, fmpz_mpoly): + if ctx is None: + ctx = fmpq_mpoly_ctx.from_context((val).ctx) + elif not typecheck(ctx, fmpq_mpoly_ctx) and not typecheck(ctx, fmpz_mpoly_ctx): + raise TypeError(f"{ctx} is not a fmpq_mpoly_ctx or fmpz_mpoly_ctx") + elif ctx.nvars() != val.context().nvars(): + raise ValueError( + f"Provided context ('{ctx}') and provided fmpz_mpoly ('{val}') don't share the same number of " + "variables" + ) + init_fmpq_mpoly(self, ctx) + fmpz_mpoly_set(self.val.zpoly, (val).val, ( val).ctx.val) + fmpq_one(self.val.content) + fmpq_mpoly_reduce(self.val, self.ctx.val) + elif isinstance(val, dict): + if ctx is None: + raise ValueError("a context is required to create a fmpq_mpoly from a dict") + x = ctx.from_dict(val) + # XXX: this copy is silly, have a ctx function that assigns an fmpz_mpoly_t + init_fmpq_mpoly(self, ctx) + fmpq_mpoly_set(self.val, (x).val, self.ctx.val) + elif isinstance(val, str): + if ctx is None: + raise ValueError("cannot parse a polynomial without context") + val = val.encode("ascii") + init_fmpq_mpoly(self, ctx) + if fmpq_mpoly_set_str_pretty(self.val, val, self.ctx.c_names, self.ctx.val) == -1: + raise ValueError("unable to parse fmpq_mpoly from string") + fmpq_mpoly_sort_terms(self.val, self.ctx.val) + else: + v = any_as_fmpq(val) + if v is NotImplemented: + raise TypeError("cannot create fmpz_mpoly from type %s" % type(val)) + if ctx is None: + raise ValueError("need context to convert fmpz to fmpq_mpoly") + init_fmpq_mpoly(self, ctx) + fmpq_mpoly_set_fmpq(self.val, (v).val, self.ctx.val) + + def __bool__(self): + return not fmpq_mpoly_is_zero(self.val, self.ctx.val) + + def __richcmp__(self, other, int op): + if not (op == Py_EQ or op == Py_NE): + return NotImplemented + elif other is None: + return op == Py_NE + elif typecheck(self, fmpq_mpoly) and typecheck(other, fmpq_mpoly): + if (self).ctx is (other).ctx: + return (op == Py_NE) ^ bool( + fmpq_mpoly_equal((self).val, (other).val, (self).ctx.val) + ) + else: + return op == Py_NE + else: + return NotImplemented + + def __len__(self): + return fmpq_mpoly_length(self.val, self.ctx.val) + + def __getitem__(self, x): + """ + Return the coefficient of the term with the exponent vector `x`. + Always returns a value, missing keys will return `0`. + Negative exponents are made positive. + + >>> from flint import Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) + >>> p[1, 1] + 3 + + """ + cdef: + slong nvars = self.ctx.nvars() + + if not isinstance(x, tuple): + raise TypeError("exponent vector index is not a tuple") + elif len(x) != nvars: + raise ValueError("exponent vector provided does not match number of variables") + + res = fmpq() + exp_vec = fmpz_vec(x, double_indirect=True) + fmpq_mpoly_get_coeff_fmpq_fmpz((res).val, self.val, exp_vec.double_indirect, self.ctx.val) + return res + + def __setitem__(self, x, y): + """ + Set the coefficient of the term with the exponent vector `x` to `y`. + Will always set a value, missing keys will create a new term. + Negative exponents are made positive. + + >>> from flint import Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) + >>> p[1, 1] = 20 + >>> p + 20*x0*x1 + 2*x1 + + """ + cdef: + slong nvars = self.ctx.nvars() + + coeff = any_as_fmpq(y) + if coeff is NotImplemented: + raise TypeError("provided coefficient not coercible to fmpq") + elif not isinstance(x, tuple): + raise TypeError("exponent vector index is not a tuple") + elif len(x) != nvars: + raise ValueError("exponent vector provided does not match number of variables") + + exp_vec = fmpz_vec(x, double_indirect=True) + fmpq_mpoly_set_coeff_fmpq_fmpz(self.val, (coeff).val, exp_vec.double_indirect, self.ctx.val) + + def __neg__(self): + cdef fmpq_mpoly res + res = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_neg(res.val, (self).val, res.ctx.val) + return res + + def __add__(self, other): + cdef fmpq_mpoly res + if typecheck(other, fmpq_mpoly): + if (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + res = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_add(res.val, (self).val, (other).val, res.ctx.val) + return res + else: + other = any_as_fmpq(other) + if other is not NotImplemented: + res = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_add_fmpq(res.val, (self).val, (other).val, res.ctx.val) + return res + return NotImplemented + + def __radd__(self, other): + cdef fmpq_mpoly res + other = any_as_fmpq(other) + if other is not NotImplemented: + res = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_add_fmpq(res.val, (self).val, (other).val, res.ctx.val) + return res + return NotImplemented + + def __sub__(self, other): + cdef fmpq_mpoly res + if typecheck(other, fmpq_mpoly): + if (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + res = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_sub(res.val, (self).val, (other).val, res.ctx.val) + return res + else: + other = any_as_fmpq(other) + if other is not NotImplemented: + res = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_sub_fmpq(res.val, (self).val, (other).val, res.ctx.val) + return res + return NotImplemented + + def __rsub__(self, other): + cdef fmpq_mpoly res + other = any_as_fmpq(other) + if other is not NotImplemented: + res = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_sub_fmpq(res.val, (self).val, (other).val, res.ctx.val) + return -res + return NotImplemented + + def __mul__(self, other): + cdef fmpq_mpoly res + if typecheck(other, fmpq_mpoly): + if (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + res = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_mul(res.val, (self).val, (other).val, res.ctx.val) + return res + else: + other = any_as_fmpq(other) + if other is not NotImplemented: + res = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_scalar_mul_fmpq(res.val, (self).val, (other).val, res.ctx.val) + return res + return NotImplemented + + def __rmul__(self, other): + cdef fmpq_mpoly res + other = any_as_fmpq(other) + if other is not NotImplemented: + res = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_scalar_mul_fmpq(res.val, (self).val, (other).val, res.ctx.val) + return res + return NotImplemented + + def __pow__(self, other, modulus): + cdef fmpq_mpoly res + if modulus is not None: + raise NotImplementedError + other = any_as_fmpz(other) + if other is NotImplemented: + return other + if other < 0: + raise ValueError("cannot raise fmpq_mpoly to negative power") + res = create_fmpq_mpoly(self.ctx) + if fmpq_mpoly_pow_fmpz(res.val, (self).val, (other).val, res.ctx.val) == 0: + raise ValueError("unreasonably large polynomial") # pragma: no cover + return res + + def __divmod__(self, other): + cdef fmpq_mpoly res, res2 + if typecheck(other, fmpq_mpoly): + if not other: + raise ZeroDivisionError("fmpq_mpoly division by zero") + elif (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + res = create_fmpq_mpoly(self.ctx) + res2 = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_divrem(res.val, res2.val, (self).val, (other).val, res.ctx.val) + return (res, res2) + else: + other = any_as_fmpq(other) + if other is not NotImplemented: + other = fmpq_mpoly(other, self.ctx) + if not other: + raise ZeroDivisionError("fmpq_mpoly division by zero") + res = create_fmpq_mpoly(self.ctx) + res2 = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_divrem(res.val, res2.val, (self).val, (other).val, res.ctx.val) + return (res, res2) + return NotImplemented + + def __rdivmod__(self, other): + cdef fmpq_mpoly res, res2 + if not self: + raise ZeroDivisionError("fmpq_mpoly division by zero") + other = any_as_fmpq(other) + if other is not NotImplemented: + other = fmpq_mpoly(other, self.ctx) + res = create_fmpq_mpoly(self.ctx) + res2 = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_divrem(res.val, res2.val, (other).val, (self).val, res.ctx.val) + return (res, res2) + return NotImplemented + + def __floordiv__(self, other): + cdef fmpq_mpoly res + if typecheck(other, fmpq_mpoly): + if not other: + raise ZeroDivisionError("fmpq_mpoly division by zero") + elif (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + res = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_div(res.val, (self).val, (other).val, res.ctx.val) + return res + else: + other = any_as_fmpq(other) + if other is not NotImplemented: + if not other: + raise ZeroDivisionError("fmpq_mpoly division by zero") + other = fmpq_mpoly(other, self.ctx) + res = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_div(res.val, (self).val, (other).val, res.ctx.val) + return res + return NotImplemented + + def __rfloordiv__(self, other): + cdef fmpq_mpoly res + if not self: + raise ZeroDivisionError("fmpq_mpoly division by zero") + other = any_as_fmpq(other) + if other is not NotImplemented: + other = fmpq_mpoly(other, self.ctx) + res = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_div(res.val, (other).val, self.val, res.ctx.val) + return res + return NotImplemented + + def __truediv__(self, other): + cdef: + fmpq_mpoly res + + if typecheck(other, fmpq_mpoly): + if not other: + raise ZeroDivisionError("fmpq_mpoly division by zero") + elif self.ctx is not (other).ctx: + raise IncompatibleContextError(f"{self.ctx} is not {(other).ctx}") + + res = create_fmpq_mpoly(self.ctx) + if fmpq_mpoly_divides(res.val, self.val, (other).val, self.ctx.val): + return res + else: + raise DomainError("fmpq_mpoly division is not exact") + else: + o = any_as_fmpq(other) + if o is NotImplemented: + return NotImplemented + elif not o: + raise ZeroDivisionError("fmpq_mpoly division by zero") + res = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_scalar_div_fmpq(res.val, self.val, (o).val, self.ctx.val) + return res + + def __rtruediv__(self, other): + cdef fmpq_mpoly res + if not self: + raise ZeroDivisionError("fmpq_mpoly division by zero") + o = any_as_fmpq(other) + if o is NotImplemented: + return NotImplemented + res = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_set_fmpq(res.val, (o).val, self.ctx.val) + return res / self + + def __mod__(self, other): + return divmod(self, other)[1] + + def __rmod__(self, other): + return divmod(other, self)[1] + + def __call__(self, *args) -> fmpq: + cdef: + fmpq_vec V + fmpq vres + slong nvars = self.ctx.nvars(), nargs = len(args) + + if nargs < nvars: + raise ValueError("not enough arguments provided") + elif nargs > nvars: + raise ValueError("more arguments provided than variables") + + args_fmpq = tuple(any_as_fmpq(v) for v in args) + for arg in args_fmpq: + if arg is NotImplemented: + raise TypeError(f"cannot coerce argument ('{arg}') to fmpq") + + V = fmpq_vec(args_fmpq, double_indirect=True) + vres = fmpq.__new__(fmpq) + if fmpq_mpoly_evaluate_all_fmpq(vres.val, self.val, V.double_indirect, self.ctx.val) == 0: + raise ValueError("unreasonably large polynomial") # pragma: no cover + return vres + + def iadd(self, other): + """ + In-place addition, mutates self. + + >>> from flint import Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> f = ctx.from_dict({(1, 0): 2, (0, 1): 3, (1, 1): 4}) + >>> f + 4*x0*x1 + 2*x0 + 3*x1 + >>> f.iadd(5) + >>> f + 4*x0*x1 + 2*x0 + 3*x1 + 5 + + """ + if typecheck(other, fmpq_mpoly): + if (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + fmpq_mpoly_add((self).val, (self).val, (other).val, self.ctx.val) + return + else: + other = any_as_fmpq(other) + if other is not NotImplemented: + fmpq_mpoly_add_fmpq((self).val, (self).val, (other).val, self.ctx.val) + return + raise NotImplementedError(f"in-place addition not implemented between {type(self)} and {type(other)}") + + def isub(self, other): + """ + In-place subtraction, mutates self. + + >>> from flint import Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> f = ctx.from_dict({(1, 0): 2, (0, 1): 3, (1, 1): 4}) + >>> f + 4*x0*x1 + 2*x0 + 3*x1 + >>> f.isub(5) + >>> f + 4*x0*x1 + 2*x0 + 3*x1 - 5 + + """ + if typecheck(other, fmpq_mpoly): + if (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + fmpq_mpoly_sub((self).val, (self).val, (other).val, self.ctx.val) + return + else: + other = any_as_fmpq(other) + if other is not NotImplemented: + fmpq_mpoly_sub_fmpq((self).val, (self).val, (other).val, self.ctx.val) + return + raise NotImplementedError(f"in-place subtraction not implemented between {type(self)} and {type(other)}") + + def imul(self, other): + """ + In-place multiplication, mutates self. + + >>> from flint import Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> f = ctx.from_dict({(1, 0): 2, (0, 1): 3, (1, 1): 4}) + >>> f + 4*x0*x1 + 2*x0 + 3*x1 + >>> f.imul(2) + >>> f + 8*x0*x1 + 4*x0 + 6*x1 + + """ + if typecheck(other, fmpq_mpoly): + if (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + fmpq_mpoly_mul((self).val, (self).val, (other).val, self.ctx.val) + return + else: + other = any_as_fmpq(other) + if other is not NotImplemented: + fmpq_mpoly_scalar_mul_fmpq(self.val, (self).val, (other).val, self.ctx.val) + return + raise NotImplementedError(f"in-place multiplication not implemented between {type(self)} and {type(other)}") + + def monoms(self): + """ + Return the exponent vectors of each term as a tuple of fmpz. + + >>> from flint import Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> f = ctx.from_dict({(0, 0): 1, (1, 0): 2, (0, 1): 3, (1, 1): 4}) + >>> f.monoms() + [(1, 1), (1, 0), (0, 1), (0, 0)] + + """ + cdef: + slong i, nvars = self.ctx.nvars() + fmpz_vec vec = fmpz_vec(nvars, double_indirect=True) + + res = [] + for i in range(len(self)): + fmpq_mpoly_get_term_exp_fmpz(vec.double_indirect, self.val, i, self.ctx.val) + res.append(vec.to_tuple()) + + return res + + def coeffs(self): + """ + Return the coefficients of each term as a fmpq. + + >>> from flint import Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> f = ctx.from_dict({(0, 0): 1, (1, 0): 2, (0, 1): 3, (1, 1): 4}) + >>> f.coeffs() + [4, 2, 3, 1] + + """ + cdef: + fmpq coeff + slong i + + res = [] + for i in range(len(self)): + coeff = fmpq.__new__(fmpq) + fmpq_mpoly_get_term_coeff_fmpq(coeff.val, self.val, i, self.ctx.val) + res.append(coeff) + + return res + + # def terms(self): + # """ + # Return the terms of this polynomial as a list of fmpq_mpolys. + + # >>> from flint import Ordering + # >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') + # >>> f = ctx.from_dict({(0, 0): 1, (1, 0): 2, (0, 1): 3, (1, 1): 4}) + # >>> f.terms() + # [4*x0*x1, 2*x0, 3*x1, 1] + + # """ + # cdef: + # fmpq_mpoly term + # slong i + + # res = [] + # for i in range(len(self)): + # term = create_fmpq_mpoly(self.ctx) + # fmpq_mpoly_get_term(term.val, self.val, i, self.ctx.val) + # res.append(term) + + # return res + + def subs(self, dict_args) -> fmpq_mpoly: + """ + Partial evaluate this polynomial with select constants. Keys must be generator names or generator indices, + all values must be fmpq. + + >>> from flint import Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> f = ctx.from_dict({(0, 0): 1, (1, 0): 2, (0, 1): 3, (1, 1): 4}) + >>> f.subs({"x1": 0}) + 2*x0 + 1 + + """ + cdef: + fmpq_mpoly res + slong i + + args = tuple((self.ctx.variable_to_index(k), any_as_fmpq(v)) for k, v in dict_args.items()) + for _, v in args: + if v is NotImplemented: + raise TypeError(f"cannot coerce argument ('{v}') to fmpq") + + # Partial application with args in Q. We evaluate the polynomial one variable at a time + res = create_fmpq_mpoly(self.ctx) + + fmpq_mpoly_set(res.val, self.val, self.ctx.val) + for i, arg in args: + if fmpq_mpoly_evaluate_one_fmpq(res.val, res.val, i, (arg).val, self.ctx.val) == 0: + raise ValueError("unreasonably large polynomial") # pragma: no cover + return res + + def compose(self, *args, ctx=None) -> fmpq_mpoly: + """ + Compose this polynomial with other fmpq_mpolys. All arguments must share the same context, it may different + from this polynomials context. + + >>> from flint import Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(1, Ordering.lex, 'x') + >>> ctx1 = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'y') + >>> f = ctx.from_dict({(2,): 1}) + >>> g = ctx1.from_dict({(1, 0): 1, (0, 1): 1}) + >>> f + x^2 + >>> g + y0 + y1 + >>> f.compose(g) + y0^2 + 2*y0*y1 + y1^2 + + """ + cdef: + fmpq_mpoly res + fmpq_mpoly_ctx res_ctx + fmpq_mpoly_vec C + slong i, nvars = self.ctx.nvars(), nargs = len(args) + + if nargs < nvars: + raise ValueError("not enough arguments provided") + elif nargs > nvars: + raise ValueError("more arguments provided than variables") + elif self.ctx.nvars() == 0 and ctx is None: + raise ValueError("a context must be provided when composing a polynomial with no generators") + elif not all(typecheck(arg, fmpq_mpoly) for arg in args): + raise TypeError("all arguments must be fmpq_mpolys") + + if ctx is None: + res_ctx = ( args[0]).ctx + elif typecheck(ctx, fmpq_mpoly_ctx): + res_ctx = ctx + else: + raise TypeError(f"provided context ({ctx}) is not a fmpq_mpoly_ctx") + + if not all(( arg).ctx is res_ctx for arg in args): + raise IncompatibleContextError( + "all arguments must share the " + ("same" if ctx is not None else "provided") + " context" + ) + + C = fmpq_mpoly_vec(args, res_ctx, double_indirect=True) + res = create_fmpq_mpoly(res_ctx) + if fmpq_mpoly_compose_fmpq_mpoly(res.val, self.val, C.double_indirect, self.ctx.val, res_ctx.val) == 0: + raise ValueError("unreasonably large polynomial") # pragma: no cover + return res + + def context(self): + """ + Return the context object for this polynomials. + + >>> from flint import Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> p = ctx.from_dict({(0, 1): 2}) + >>> ctx is p.context() + True + """ + return self.ctx + + def is_one(self): + return fmpq_mpoly_is_one(self.val, self.ctx.val) + + def coefficient(self, slong i): + """ + Return the coefficient at index `i`. + + >>> from flint import Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) + >>> p.coefficient(1) + 2 + """ + cdef fmpq v + if not 0 <= i < fmpq_mpoly_length(self.val, self.ctx.val): + raise IndexError("term index out of range") + else: + v = fmpq.__new__(fmpq) + fmpq_mpoly_get_term_coeff_fmpq(v.val, self.val, i, self.ctx.val) + return v + + def monomial(self, slong i): + """ + Return the exponent vector at index `i` as a tuple. + + >>> from flint import Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) + >>> p.monomial(1) + (0, 1) + """ + cdef: + slong nvars = self.ctx.nvars() + + if not 0 <= i < fmpq_mpoly_length(self.val, self.ctx.val): + raise IndexError("term index out of range") + res = fmpz_vec(nvars, double_indirect=True) + fmpq_mpoly_get_term_exp_fmpz(res.double_indirect, self.val, i, self.ctx.val) + return res.to_tuple() + + def degrees(self): + """ + Return a dictionary of variable name to degree. + + >>> from flint import Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(4, Ordering.lex, 'x') + >>> p = ctx.from_dict({(1, 0, 0, 0): 1, (0, 2, 0, 0): 2, (0, 0, 3, 0): 3}) + >>> p.degrees() + (1, 2, 3, 0) + """ + cdef: + slong nvars = self.ctx.nvars() + + res = fmpz_vec(nvars, double_indirect=True) + fmpq_mpoly_degrees_fmpz(res.double_indirect, self.val, self.ctx.val) + return res.to_tuple() + + def total_degree(self): + """ + Return the total degree. + + >>> from flint import Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(4, Ordering.lex, 'x') + >>> p = ctx.from_dict({(1, 0, 0, 0): 1, (0, 2, 0, 0): 2, (0, 0, 3, 0): 3}) + >>> p.total_degree() + 3 + """ + cdef fmpz res = fmpz() + fmpq_mpoly_total_degree_fmpz(( res).val, self.val, self.ctx.val) + return res + + def repr(self): + return f"{self.ctx}.from_dict({self.to_dict()})" + + def str(self): + cdef bytes s = fmpq_mpoly_get_str_pretty(self.val, self.ctx.c_names, self.ctx.val) + # res = s.decode().replace("+", " + ").replace("-", " - ") + res = s.decode() + if res.startswith("- "): + res = "-" + res[3:] + return res + + def gcd(self, other): + """ + Return the gcd of self and other. + + >>> from flint import Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> f = ctx.from_dict({(1, 1): 4, (0, 0): 1}) + >>> g = ctx.from_dict({(0, 1): 2, (1, 0): 2}) + >>> (f * g).gcd(f) + x0*x1 + 1/4 + + """ + cdef fmpq_mpoly res + if not typecheck(other, fmpq_mpoly): + raise TypeError("argument must be a fmpq_mpoly") + elif (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + res = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_gcd(res.val, (self).val, (other).val, res.ctx.val) + return res + + def sqrt(self): + """ + Return the square root of self. + + >>> from flint import Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> f = ctx.from_dict({(1, 1): 4, (0, 0): 1}) + >>> (f * f).sqrt() + 4*x0*x1 + 1 + """ + cdef fmpq_mpoly res + res = create_fmpq_mpoly(self.ctx) + + if fmpq_mpoly_sqrt(res.val, self.val, self.ctx.val): + return res + else: + raise ValueError("polynomial is not a perfect square") + + def factor(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. + + >>> from flint import Ordering + >>> Zm = fmpq_mpoly + >>> ctx = fmpq_mpoly_ctx.get_context(3, Ordering.lex, 'x,y,z') + >>> p1 = Zm("2*x + 4", ctx) + >>> p2 = Zm("3*x*z + + 3*x + 3*z + 3", ctx) + >>> (p1 * p2).factor() + (6, [(z + 1, 1), (x + 2, 1), (x + 1, 1)]) + >>> (p2 * p1 * p2).factor() + (18, [(z + 1, 2), (x + 2, 1), (x + 1, 2)]) + """ + cdef: + fmpq_mpoly_factor_t fac + fmpq_mpoly u + + fmpq_mpoly_factor_init(fac, self.ctx.val) + fmpq_mpoly_factor(fac, self.val, self.ctx.val) + res = [0] * fac.num + + for i in range(fac.num): + u = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_set((u).val, &fac.poly[i], self.ctx.val) + + c = fmpz.__new__(fmpz) + fmpz_init_set((c).val, &fac.exp[i]) + + res[i] = (u, c) + + c = fmpq.__new__(fmpq) + fmpq_set((c).val, fac.constant) + fmpq_mpoly_factor_clear(fac, self.ctx.val) + return c, 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. + + >>> from flint import Ordering + >>> Zm = fmpq_mpoly + >>> ctx = fmpq_mpoly_ctx.get_context(3, Ordering.lex, 'x,y,z') + >>> p1 = Zm("2*x + 4", ctx) + >>> p2 = Zm("3*x*y + 3*x + 3*y + 3", ctx) + >>> (p1 * p2).factor_squarefree() + (6, [(y + 1, 1), (x^2 + 3*x + 2, 1)]) + >>> (p1 * p2 * p1).factor_squarefree() + (12, [(y + 1, 1), (x + 1, 1), (x + 2, 2)]) + """ + cdef: + fmpq_mpoly_factor_t fac + fmpq_mpoly u + + fmpq_mpoly_factor_init(fac, self.ctx.val) + fmpq_mpoly_factor_squarefree(fac, self.val, self.ctx.val) + res = [0] * fac.num + + for i in range(fac.num): + u = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_init(u.val, u.ctx.val) + fmpq_mpoly_set((u).val, &fac.poly[i], self.ctx.val) + + c = fmpz.__new__(fmpz) + fmpz_init_set((c).val, &fac.exp[i]) + + res[i] = (u, c) + + c = fmpq.__new__(fmpq) + fmpq_set((c).val, fac.constant) + fmpq_mpoly_factor_clear(fac, self.ctx.val) + return c, res + + def derivative(self, var): + """ + Return the derivative of this polynomial with respect to the provided variable. + The argument can either be the variable as a string, or the index of the + variable in the context. + + >>> from flint import Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> p = ctx.from_dict({(0, 3): 2, (2, 1): 3}) + >>> p + 3*x0^2*x1 + 2*x1^3 + >>> p.derivative("x0") + 6*x0*x1 + >>> p.derivative(1) + 3*x0^2 + 6*x1^2 + + """ + cdef: + fmpq_mpoly res + slong i = self.ctx.variable_to_index(var) + + res = create_fmpq_mpoly(self.ctx) + + fmpq_mpoly_derivative(res.val, self.val, i, self.ctx.val) + return res + + def integral(self, var): + """ + Return the integral of this polynomial with respect to the provided variable The argument can either be the + variable as a string, or the index of the variable in the context. + + >>> from flint import Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> p = ctx.from_dict({(0, 3): 2, (2, 1): 3}) + >>> p + 3*x0^2*x1 + 2*x1^3 + >>> p.integral("x0") + x0^3*x1 + 2*x0*x1^3 + >>> p.integral(1) + 3/2*x0^2*x1^2 + 1/2*x1^4 + + """ + cdef: + fmpq_mpoly res + slong i = self.ctx.variable_to_index(var) + + res = create_fmpq_mpoly(self.ctx) + + fmpq_mpoly_integral(res.val, self.val, i, self.ctx.val) + return res + + +cdef class fmpq_mpoly_vec: + """ + A class representing a vector of fmpq_mpolys. Not present in FLINT. + """ + + def __cinit__(self, iterable_or_len, fmpq_mpoly_ctx ctx, bint double_indirect = False): + if isinstance(iterable_or_len, int): + self.length = iterable_or_len + else: + self.length = len(iterable_or_len) + + self.ctx = ctx + + self.val = libc.stdlib.malloc(self.length * sizeof(fmpq_mpoly_struct)) + for i in range(self.length): + fmpq_mpoly_init(&self.val[i], self.ctx.val) + + if double_indirect: + self.double_indirect = libc.stdlib.malloc(self.length * sizeof(fmpq_mpoly_struct *)) + if self.double_indirect is NULL: + raise MemoryError("malloc returned a null pointer") # pragma: no cover + + for i in range(self.length): + self.double_indirect[i] = &self.val[i] + else: + self.double_indirect = NULL + + def __init__(self, iterable_or_len, _, double_indirect: bool = False): + if not isinstance(iterable_or_len, int): + for i, x in enumerate(iterable_or_len): + self[i] = x + + def __dealloc__(self): + libc.stdlib.free(self.double_indirect) + for i in range(self.length): + fmpq_mpoly_clear(&self.val[i], self.ctx.val) + libc.stdlib.free(self.val) + + def __getitem__(self, x): + if not isinstance(x, int): + raise TypeError("index is not integer") + elif not 0 <= x < self.length: + raise IndexError("index out of range") + + cdef fmpq_mpoly z = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_set(z.val, &self.val[x], self.ctx.val) + return z + + def __setitem__(self, x, y): + if not isinstance(x, int): + raise TypeError("index is not integer") + elif not 0 <= x < self.length: + raise IndexError("index out of range") + elif not typecheck(y, fmpq_mpoly): + raise TypeError("argument is not fmpq_mpoly") + elif (y).ctx is not self.ctx: + raise IncompatibleContextError(f"{(y).ctx} is not {self.ctx}") + + fmpq_mpoly_set(&self.val[x], (y).val, self.ctx.val) + + def __len__(self): + return self.length + + def __str__(self): + s = [None] * self.length + for i in range(self.length): + x = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_set(( x).val, &self.val[i], self.ctx.val) + s[i] = str(x) + return f"[{', '.join(s)}]" + + def __repr__(self): + return f"fmpq_mpoly_vec({self}, ctx={self.ctx})" + + def to_tuple(self): + return tuple(self[i] for i in range(self.length)) diff --git a/src/flint/types/fmpq_vec.pxd b/src/flint/types/fmpq_vec.pxd new file mode 100644 index 00000000..5ca2772c --- /dev/null +++ b/src/flint/types/fmpq_vec.pxd @@ -0,0 +1,7 @@ +from flint.flintlib.fmpq cimport fmpq_struct +from flint.flintlib.flint cimport slong + +cdef class fmpq_vec: + cdef fmpq_struct *val + cdef fmpq_struct **double_indirect + cdef slong length diff --git a/src/flint/types/fmpq_vec.pyx b/src/flint/types/fmpq_vec.pyx new file mode 100644 index 00000000..a4d01d79 --- /dev/null +++ b/src/flint/types/fmpq_vec.pyx @@ -0,0 +1,83 @@ +from flint.flintlib.fmpq cimport fmpq_struct, fmpq_set, fmpq_init +from flint.flintlib.fmpq_vec cimport _fmpq_vec_init, _fmpq_vec_clear + +from flint.types.fmpq cimport fmpq, any_as_fmpq + +cimport libc.stdlib + +cdef class fmpq_vec: + def __cinit__(self, iterable_or_len, bint double_indirect=False): + if isinstance(iterable_or_len, int): + self.length = iterable_or_len + else: + self.length = len(iterable_or_len) + + self.val = _fmpq_vec_init(self.length) + + if double_indirect: + self.double_indirect = libc.stdlib.malloc(self.length * sizeof(fmpq_struct *)) + if self.double_indirect is NULL: + raise MemoryError("malloc returned a null pointer") + + for i in range(self.length): + self.double_indirect[i] = &self.val[i] + else: + self.double_indirect = NULL + + def __init__(self, iterable_or_len, double_indirect: bool = False): + if not isinstance(iterable_or_len, int): + for i, x in enumerate(iterable_or_len): + self[i] = x + + def __dealloc__(self): + libc.stdlib.free(self.double_indirect) + if self.val is not NULL: + _fmpq_vec_clear(self.val, self.length) + self.val = NULL + + def __getitem__(self, x): + if not isinstance(x, int): + raise TypeError("index is not integer") + elif not 0 <= x < self.length: + raise IndexError("index out of range") + + cdef fmpq z = fmpq.__new__(fmpq) + fmpq_init(z.val) + fmpq_set(z.val, &self.val[x]) + return z + + def __setitem__(self, x, y): + if not isinstance(x, int): + raise TypeError("index is not integer") + elif not 0 <= x < self.length: + raise IndexError("index out of range") + + y = any_as_fmpq(y) + if y is NotImplemented: + raise TypeError("argument is not coercible to fmpq") + + fmpq_set(&self.val[x], (y).val) + + def __len__(self): + return self.length + + def __str__(self): + return self.str() + + def __repr__(self): + return self.repr() + + def str(self, *args): + s = [None] * self.length + for i in range(self.length): + x = fmpq.__new__(fmpq) + fmpq_init(x.val) + fmpq_set(x.val, &self.val[i]) + s[i] = x.str(*args) + return str(s) + + def repr(self, *args): + return f"fmpq_vec({self.str(*args)}, {self.length})" + + def to_tuple(self): + return tuple(self[i] for i in range(self.length)) diff --git a/src/flint/types/fmpz_mod_poly.pyx b/src/flint/types/fmpz_mod_poly.pyx index f5b5d60b..fa334c63 100644 --- a/src/flint/types/fmpz_mod_poly.pyx +++ b/src/flint/types/fmpz_mod_poly.pyx @@ -9,7 +9,7 @@ from flint.flintlib.fmpz cimport( fmpz_clear, fmpz_is_one ) -from flint.flintlib.fmpz_vec cimport _fmpz_vec_init, _fmpz_vec_clear +from flint.types.fmpz_vec cimport fmpz_vec from flint.types.fmpz cimport fmpz, any_as_fmpz from flint.types.fmpz_mod cimport fmpz_mod_ctx, fmpz_mod @@ -291,17 +291,15 @@ cdef class fmpz_mod_poly_ctx: raise ValueError("Input must be a list or tuple of points") n = len(vals) - xs = _fmpz_vec_init(n) + xs = fmpz_vec(n) for i in range(n): - check = self.mod.set_any_as_fmpz_mod(&xs[i], vals[i]) + check = self.mod.set_any_as_fmpz_mod(&xs.val[i], vals[i]) if check is NotImplemented: - _fmpz_vec_clear(xs, n) raise ValueError(f"Unable to cast {vals[i]} to an `fmpz_mod`") res = self.new_ctype_poly() - fmpz_mod_poly_minpoly(res.val, xs, n, self.mod.val) + fmpz_mod_poly_minpoly(res.val, xs.val, n, self.mod.val) - _fmpz_vec_clear(xs, n) return res cdef class fmpz_mod_poly(flint_poly): @@ -759,29 +757,25 @@ cdef class fmpz_mod_poly(flint_poly): raise ValueError("Input must be a list of points") n = len(vals) - xs = _fmpz_vec_init(n) + xs = fmpz_vec(n) for i in range(n): - check = self.ctx.mod.set_any_as_fmpz_mod(&xs[i], vals[i]) + check = self.ctx.mod.set_any_as_fmpz_mod(&xs.val[i], vals[i]) if check is NotImplemented: - _fmpz_vec_clear(xs, n) raise ValueError(f"Unable to cast {vals[i]} to an `fmpz_mod`") # Call for multipoint eval, iterative horner will be used # for small arrays (len < 32) and a fast eval for larger ones # using a subproduct tree - ys = _fmpz_vec_init(n) - fmpz_mod_poly_evaluate_fmpz_vec(ys, self.val, xs, n, self.ctx.mod.val) + ys = fmpz_vec(n) + fmpz_mod_poly_evaluate_fmpz_vec(ys.val, self.val, xs.val, n, self.ctx.mod.val) evaluations = [] for i in range(n): f = fmpz_mod.__new__(fmpz_mod) f.ctx = self.ctx.mod - fmpz_mod_set_fmpz(f.val, &ys[i], self.ctx.mod.val) + fmpz_mod_set_fmpz(f.val, &ys.val[i], self.ctx.mod.val) evaluations.append(f) - _fmpz_vec_clear(xs, n) - _fmpz_vec_clear(ys, n) - return evaluations def compose(self, input): diff --git a/src/flint/types/fmpz_mpoly.pxd b/src/flint/types/fmpz_mpoly.pxd index e4470efd..953bb82e 100644 --- a/src/flint/types/fmpz_mpoly.pxd +++ b/src/flint/types/fmpz_mpoly.pxd @@ -1,16 +1,30 @@ -from flint.flint_base.flint_base cimport flint_mpoly +from flint.flint_base.flint_base cimport flint_mpoly, flint_mpoly_context -from flint.flintlib.fmpz_mpoly cimport fmpz_mpoly_ctx_t -from flint.flintlib.fmpz_mpoly cimport fmpz_mpoly_t +from flint.flintlib.fmpz_mpoly cimport fmpz_mpoly_ctx_t, fmpz_mpoly_vec_t, fmpz_mpoly_t, fmpz_mpoly_init, fmpz_mpoly_struct from flint.flintlib.flint cimport slong -cdef class fmpz_mpoly_ctx: - cdef fmpz_mpoly_ctx_t val - cpdef slong nvars(self) +cdef inline init_fmpz_mpoly(fmpz_mpoly var, fmpz_mpoly_ctx ctx): + var.ctx = ctx + fmpz_mpoly_init(var.val, ctx.val) + var._init = True + +cdef inline fmpz_mpoly create_fmpz_mpoly(fmpz_mpoly_ctx ctx): + cdef fmpz_mpoly var + var = fmpz_mpoly.__new__(fmpz_mpoly) + var.ctx = ctx + fmpz_mpoly_init(var.val, ctx.val) + var._init = True + return var - cpdef ordering(self) +cdef class fmpz_mpoly_ctx(flint_mpoly_context): + cdef fmpz_mpoly_ctx_t val cdef class fmpz_mpoly(flint_mpoly): cdef fmpz_mpoly_t val cdef fmpz_mpoly_ctx ctx cdef bint _init + +cdef class fmpz_mpoly_vec: + cdef fmpz_mpoly_vec_t val + cdef fmpz_mpoly_ctx ctx + cdef fmpz_mpoly_struct **double_indirect diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index 1e179c9b..28c6bf76 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -1,132 +1,186 @@ -from cpython.version cimport PY_MAJOR_VERSION +from flint.flint_base.flint_base cimport ( + flint_mpoly, + flint_mpoly_context, + ordering_py_to_c, + ordering_c_to_py, +) -from flint.utils.conversion cimport str_from_chars from flint.utils.typecheck cimport typecheck -from flint.flint_base.flint_base cimport flint_mpoly -from flint.types.fmpz cimport any_as_fmpz -from flint.types.fmpz cimport fmpz +from flint.utils.flint_exceptions import DomainError, IncompatibleContextError -cimport cython +from flint.types.fmpz cimport any_as_fmpz, fmpz +from flint.types.fmpz_vec cimport fmpz_vec + +from flint.flintlib.fmpz cimport fmpz_set +from flint.flintlib.fmpz_mpoly cimport ( + fmpz_mpoly_add, + fmpz_mpoly_add_fmpz, + fmpz_mpoly_clear, + fmpz_mpoly_compose_fmpz_mpoly, + fmpz_mpoly_ctx_init, + fmpz_mpoly_degrees_fmpz, + fmpz_mpoly_derivative, + fmpz_mpoly_div, + fmpz_mpoly_divides, + fmpz_mpoly_divrem, + fmpz_mpoly_equal, + fmpz_mpoly_evaluate_all_fmpz, + fmpz_mpoly_evaluate_one_fmpz, + fmpz_mpoly_gcd, + fmpz_mpoly_gen, + fmpz_mpoly_get_coeff_fmpz_fmpz, + fmpz_mpoly_get_str_pretty, + fmpz_mpoly_get_term, + fmpz_mpoly_get_term_coeff_fmpz, + fmpz_mpoly_get_term_exp_fmpz, + fmpz_mpoly_integral, + fmpz_mpoly_is_one, + fmpz_mpoly_is_zero, + fmpz_mpoly_length, + fmpz_mpoly_mul, + fmpz_mpoly_neg, + fmpz_mpoly_pow_fmpz, + fmpz_mpoly_push_term_fmpz_ffmpz, + fmpz_mpoly_scalar_divides_fmpz, + fmpz_mpoly_scalar_mul_fmpz, + fmpz_mpoly_set, + fmpz_mpoly_set_coeff_fmpz_fmpz, + fmpz_mpoly_set_fmpz, + fmpz_mpoly_set_str_pretty, + fmpz_mpoly_sort_terms, + fmpz_mpoly_sqrt_heap, + fmpz_mpoly_sub, + fmpz_mpoly_sub_fmpz, + fmpz_mpoly_total_degree_fmpz, + fmpz_mpoly_vec_clear, + fmpz_mpoly_vec_entry, + fmpz_mpoly_vec_init, +) +from flint.flintlib.fmpz_mpoly_factor cimport ( + fmpz_mpoly_factor, + fmpz_mpoly_factor_clear, + fmpz_mpoly_factor_init, + fmpz_mpoly_factor_squarefree, + fmpz_mpoly_factor_t, +) + +from cpython.object cimport Py_EQ, Py_NE cimport libc.stdlib -from flint.flintlib.fmpz_mpoly cimport * -cdef any_as_fmpz_mpoly(x): - cdef fmpz_mpoly res +cdef dict _fmpz_mpoly_ctx_cache = {} + + +cdef class fmpz_mpoly_ctx(flint_mpoly_context): """ - if typecheck(x, fmpz_poly): - return x - elif typecheck(x, fmpz): - res = fmpz_poly.__new__(fmpz_poly) - fmpz_poly_set_fmpz(res.val, (x).val) - return res - elif PY_MAJOR_VERSION < 3 and PyInt_Check(x): - res = fmpz_poly.__new__(fmpz_poly) - fmpz_poly_set_si(res.val, PyInt_AS_LONG(x)) - return res - elif PyLong_Check(x): - res = fmpz_poly.__new__(fmpz_poly) - t = fmpz(x) - fmpz_poly_set_fmpz(res.val, (t).val) - return res + A class for storing the polynomial context + + :param nvars: The number of variables in the ring + :param ordering: The term order for the ring + :param names: A tuple containing the names of the variables of the ring. + + Do not construct one of these directly, use `fmpz_mpoly_ctx.get_context`. """ - return NotImplemented - -""" -cdef fmpz_poly_set_list(fmpz_poly_t poly, list val): - cdef long i, n - cdef fmpz_t x - n = PyList_GET_SIZE(val) - fmpz_poly_fit_length(poly, n) - fmpz_init(x) - for i from 0 <= i < n: - if typecheck(val[i], fmpz): - fmpz_poly_set_coeff_fmpz(poly, i, ((val[i])).val) - elif fmpz_set_python(x, val[i]): - fmpz_poly_set_coeff_fmpz(poly, i, x) - else: - fmpz_clear(x) - raise TypeError("unsupported coefficient in list") - fmpz_clear(x) -""" -cdef dict _fmpz_mpoly_ctx_cache = {} + _ctx_cache = _fmpz_mpoly_ctx_cache -@cython.auto_pickle(False) -cdef class fmpz_mpoly_ctx: -# cdef fmpz_mpoly_ctx_t val + def __init__(self, slong nvars, ordering, names): + fmpz_mpoly_ctx_init(self.val, nvars, ordering_py_to_c(ordering)) + super().__init__(nvars, names) - def __init__(self, slong nvars, ordering="lex"): - assert nvars >= 1 - fmpz_mpoly_ctx_init(self.val, nvars, ordering_t.ORD_LEX) + def nvars(self): + """ + Return the number of variables in the context - cpdef slong nvars(self): + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(4, Ordering.lex, 'x') + >>> ctx.nvars() + 4 + """ return self.val.minfo.nvars - cpdef ordering(self): - return "lex" - -cdef get_fmpz_mpoly_context(slong nvars=1, ordering=None): - if nvars <= 0: - nvars = 1 - if ordering is None: - ordering = "lex" - key = (nvars, ordering) - ctx = _fmpz_mpoly_ctx_cache.get(key) - if ctx is None: - ctx = fmpz_mpoly_ctx(nvars, ordering) - _fmpz_mpoly_ctx_cache[key] = ctx - return ctx - -cdef _fmpz_mpoly_set2(fmpz_mpoly_t out, fmpz_mpoly_ctx_t outctx, fmpz_mpoly_t inp, fmpz_mpoly_ctx_t inpctx): - cdef slong * C - cdef slong i - cdef slong inpvars, outvars - if outctx == inpctx: - fmpz_mpoly_set(out, inp, inpctx) - else: - inpvars = inpctx.minfo.nvars - outvars = inpctx.minfo.nvars - C = libc.stdlib.malloc(inpvars * sizeof(slong *)) - for i in range(min(outvars, inpvars)): - C[i] = i - for i in range(outvars, inpvars): - C[i] = -1 - fmpz_mpoly_compose_fmpz_mpoly_gen(out, inp, C, inpctx, outctx) - libc.stdlib.free(C) - -def coerce_fmpz_mpolys(*args): - cdef fmpz_mpoly_ctx ctx - ctx = get_fmpz_mpoly_context() - if not args: - return ctx, [] - args = list(args) - if typecheck(args[0], fmpz_mpoly): - ctx = ( args[0]).ctx - if all(typecheck(args[i], fmpz_mpoly) and ( args[i]).ctx is ctx for i in range(1, len(args))): - return ctx, args - for i in range(len(args)): - if not typecheck(args[i], fmpz_mpoly): - args[i] = fmpz_mpoly(args[i]) - nvars = max((pol).ctx.nvars() for pol in args) - ctx = get_fmpz_mpoly_context(nvars) - args2 = [fmpz_mpoly() for i in range(len(args))] - for i in range(len(args)): - ( args2[i]).ctx = ctx - _fmpz_mpoly_set2(( args2[i]).val, ctx.val, ( args[i]).val, ( args[i]).ctx.val) - return ctx, args2 - - -# todo: store cached context objects externally + def ordering(self): + """ + Return the term order of the context object. + + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(4, Ordering.deglex, 'w') + >>> ctx.ordering() + + """ + return ordering_c_to_py(self.val.minfo.ord) + + def gen(self, slong i): + """ + Return the `i`th generator of the polynomial ring + + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(3, Ordering.degrevlex, 'z') + >>> ctx.gen(1) + z1 + """ + cdef fmpz_mpoly res + if not 0 <= i < self.val.minfo.nvars: + raise IndexError("generator index out of range") + res = create_fmpz_mpoly(self) + fmpz_mpoly_gen(res.val, i, res.ctx.val) + return res + + def constant(self, z): + """ + Create a constant polynomial in this context + """ + cdef fmpz_mpoly res + z = any_as_fmpz(z) + if z is NotImplemented: + raise TypeError("cannot coerce argument to fmpz") + res = create_fmpz_mpoly(self) + fmpz_mpoly_set_fmpz(res.val, (z).val, res.ctx.val) + return res + + def from_dict(self, d): + """ + Create a fmpz_mpoly from a dictionary in this context. + + The dictionary's keys are tuples of ints (or anything that implicitly converts + to fmpz) representing exponents, and corresponding values of fmpz. + + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x,y') + >>> ctx.from_dict({(1,0):2, (1,1):3, (0,1):1}) + 3*x*y + 2*x + y + """ + cdef: + fmpz_vec exp_vec + slong i, nvars = self.nvars() + fmpz_mpoly res + + if not isinstance(d, dict): + raise ValueError("expected a dictionary") + + res = create_fmpz_mpoly(self) + + for i, (k, v) in enumerate(d.items()): + o = any_as_fmpz(v) + if o is NotImplemented: + raise TypeError(f"cannot coerce coefficient '{v}' to fmpz") + elif len(k) != nvars: + raise ValueError(f"expected {nvars} exponents, got {len(k)}") + + exp_vec = fmpz_vec(k) + + if o: + fmpz_mpoly_push_term_fmpz_ffmpz(res.val, (o).val, exp_vec.val, self.val) + + fmpz_mpoly_sort_terms(res.val, self.val) + return res + + cdef class fmpz_mpoly(flint_mpoly): """ - The *fmpz_poly* type represents sparse multivariate polynomials over + The *fmpz_mpoly* type represents sparse multivariate polynomials over the integers. """ - # cdef fmpz_mpoly_t val - # cdef fmpz_mpoly_ctx ctx - # cdef bint _init - def __cinit__(self): self._init = False @@ -135,362 +189,933 @@ cdef class fmpz_mpoly(flint_mpoly): fmpz_mpoly_clear(self.val, self.ctx.val) self._init = False - def __init__(self, val=0, slong nvars=-1, ordering=None): + def __init__(self, val=0, ctx=None): if typecheck(val, fmpz_mpoly): - if nvars == -1 and ordering is None: - self.ctx = (val).ctx - fmpz_mpoly_init(self.val, self.ctx.val) - self._init = True + if ctx is None or ctx == (val).ctx: + init_fmpz_mpoly(self, (val).ctx) fmpz_mpoly_set(self.val, (val).val, self.ctx.val) else: - self.ctx = get_fmpz_mpoly_context(nvars, ordering) - fmpz_mpoly_init(self.val, self.ctx.val) - self._init = True - _fmpz_mpoly_set2(self.val, self.ctx.val, (val).val, (val).ctx.val) + raise IncompatibleContextError(f"{ctx} is not {(val).ctx}") + elif isinstance(val, dict): + if ctx is None: + raise ValueError("a context is required to create a fmpz_mpoly from a dict") + x = ctx.from_dict(val) + # XXX: this copy is silly, have a ctx function that assigns an fmpz_mpoly_t + init_fmpz_mpoly(self, ctx) + fmpz_mpoly_set(self.val, (x).val, self.ctx.val) + elif isinstance(val, str): + if ctx is None: + raise ValueError("cannot parse a polynomial without context") + val = bytes(val, 'utf-8') + init_fmpz_mpoly(self, ctx) + if fmpz_mpoly_set_str_pretty(self.val, val, self.ctx.c_names, self.ctx.val) == -1: + raise ValueError("unable to parse fmpz_mpoly from string") + fmpz_mpoly_sort_terms(self.val, self.ctx.val) else: v = any_as_fmpz(val) if v is NotImplemented: raise TypeError("cannot create fmpz_mpoly from type %s" % type(val)) - self.ctx = get_fmpz_mpoly_context(nvars, ordering) - fmpz_mpoly_init(self.val, self.ctx.val) - self._init = True + if ctx is None: + raise ValueError("need context to convert fmpz to fmpz_mpoly") + init_fmpz_mpoly(self, ctx) fmpz_mpoly_set_fmpz(self.val, (v).val, self.ctx.val) - def __nonzero__(self): + def __bool__(self): return not fmpz_mpoly_is_zero(self.val, self.ctx.val) - def is_one(self): - return fmpz_mpoly_is_one(self.val, self.ctx.val) - def __richcmp__(self, other, int op): - if op != 2 and op != 3: + if not (op == Py_EQ or op == Py_NE): return NotImplemented - if typecheck(self, fmpz_mpoly) and typecheck(other, fmpz_mpoly): + elif other is None: + return op == Py_NE + elif typecheck(self, fmpz_mpoly) and typecheck(other, fmpz_mpoly): if (self).ctx is (other).ctx: - if op == 2: - return bool(fmpz_mpoly_equal((self).val, (other).val, (self).ctx.val)) - else: - return not bool(fmpz_mpoly_equal((self).val, (other).val, (self).ctx.val)) - if op == 2: - return not bool(self - other) + return (op == Py_NE) ^ bool( + fmpz_mpoly_equal((self).val, (other).val, (self).ctx.val) + ) + else: + return op == Py_NE else: - return bool(self - other) + return NotImplemented def __len__(self): return fmpz_mpoly_length(self.val, self.ctx.val) - def __hash__(self): - s = str(self) - i = s.index("(nvars") - s = s[:i] - return hash(s) + def __getitem__(self, x): + """ + Return the coefficient of the term with the exponent vector `x`. + Always returns a value, missing keys will return `0`. + Negative exponents are made positive. - def coefficient(self, slong i): - cdef fmpz v - if i < 0 or i >= fmpz_mpoly_length(self.val, self.ctx.val): - return fmpz(0) - else: - v = fmpz.__new__(fmpz) - fmpz_mpoly_get_term_coeff_fmpz(v.val, self.val, i, self.ctx.val) - return v + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) + >>> p[1, 1] + 3 - def leading_coefficient(self): - return self.coefficient(0) - - def exponent_tuple(self, slong i): - cdef slong j, nvars - cdef fmpz_struct ** tmp - if i < 0 or i >= fmpz_mpoly_length(self.val, self.ctx.val): - raise ValueError - nvars = self.ctx.nvars() - res = tuple(fmpz() for j in range(nvars)) - tmp = libc.stdlib.malloc(nvars * sizeof(fmpz_struct **)) - try: - for j in range(nvars): - tmp[j] = &(( (res[j])).val[0]) - fmpz_mpoly_get_term_exp_fmpz(tmp, self.val, i, self.ctx.val) - finally: - libc.stdlib.free(tmp) - return res + """ + cdef: + slong nvars = self.ctx.nvars() - @staticmethod - def gen(slong i, slong nvars=-1, ordering=None): - cdef fmpz_mpoly res - assert i >= 0 - if nvars <= 0: - nvars = i + 1 - assert i < nvars - res = fmpz_mpoly.__new__(fmpz_mpoly) - res.ctx = get_fmpz_mpoly_context(nvars, ordering) - fmpz_mpoly_init(res.val, res.ctx.val) - res._init = True - fmpz_mpoly_gen(res.val, i, res.ctx.val) + if not isinstance(x, tuple): + raise TypeError("exponent vector index is not a tuple") + elif len(x) != nvars: + raise ValueError("exponent vector provided does not match number of variables") + + res = fmpz() + exp_vec = fmpz_vec(x, double_indirect=True) + fmpz_mpoly_get_coeff_fmpz_fmpz((res).val, self.val, exp_vec.double_indirect, self.ctx.val) return res - @staticmethod - def gens(slong n, ordering=None): - # todo: (i, n)? or just (i)? - return tuple(fmpz_mpoly.gen(i, n) for i in range(n)) + def __setitem__(self, x, y): + """ + Set the coefficient of the term with the exponent vector `x` to `y`. + Will always set a value, missing keys will create a new term. + Negative exponents are made positive. - def repr(self): - cdef char * s = fmpz_mpoly_get_str_pretty(self.val, NULL, self.ctx.val) - try: - res = str_from_chars(s) - finally: - libc.stdlib.free(s) - res = res.replace("+", " + ") - res = res.replace("-", " - ") - if res.startswith(" - "): - res = "-" + res[3:] - return res + " (nvars=%s, ordering=%s)" % (self.ctx.nvars(), self.ctx.ordering()) + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) + >>> p[1, 1] = 20 + >>> p + 20*x0*x1 + 2*x1 - def str(self): - return self.repr() + """ + cdef: + slong nvars = self.ctx.nvars() + + coeff = any_as_fmpz(y) + if coeff is NotImplemented: + raise TypeError("provided coefficient not coercible to fmpz") + elif not isinstance(x, tuple): + raise TypeError("exponent vector index is not a tuple") + elif len(x) != nvars: + raise ValueError("exponent vector provided does not match number of variables") + + exp_vec = fmpz_vec(x, double_indirect=True) + fmpz_mpoly_set_coeff_fmpz_fmpz(self.val, (coeff).val, exp_vec.double_indirect, self.ctx.val) def __neg__(self): cdef fmpz_mpoly res - res = fmpz_mpoly.__new__(fmpz_mpoly) - res.ctx = (self).ctx - fmpz_mpoly_init(res.val, res.ctx.val) - res._init = True + res = create_fmpz_mpoly(self.ctx) fmpz_mpoly_neg(res.val, (self).val, res.ctx.val) return res def __add__(self, other): cdef fmpz_mpoly res - if typecheck(self, fmpz_mpoly): - if typecheck(other, fmpz_mpoly): - if (self).ctx is not (other).ctx: - ctx, (self, other) = coerce_fmpz_mpolys(self, other) - res = fmpz_mpoly.__new__(fmpz_mpoly) - res.ctx = (self).ctx - fmpz_mpoly_init(res.val, res.ctx.val) - res._init = True - fmpz_mpoly_add(res.val, (self).val, (other).val, res.ctx.val) - return res - else: - other = any_as_fmpz(other) - if other is not NotImplemented: - res = fmpz_mpoly.__new__(fmpz_mpoly) - res.ctx = (self).ctx - fmpz_mpoly_init(res.val, res.ctx.val) - res._init = True - fmpz_mpoly_add_fmpz(res.val, (self).val, (other).val, res.ctx.val) - return res + if typecheck(other, fmpz_mpoly): + if (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + res = create_fmpz_mpoly(self.ctx) + fmpz_mpoly_add(res.val, (self).val, (other).val, res.ctx.val) + return res else: - self = any_as_fmpz(self) - if self is not NotImplemented: - res = fmpz_mpoly.__new__(fmpz_mpoly) - res.ctx = (other).ctx - fmpz_mpoly_init(res.val, res.ctx.val) - res._init = True - fmpz_mpoly_add_fmpz(res.val, (other).val, (self).val, res.ctx.val) + other = any_as_fmpz(other) + if other is not NotImplemented: + res = create_fmpz_mpoly(self.ctx) + fmpz_mpoly_add_fmpz(res.val, (self).val, (other).val, self.ctx.val) return res return NotImplemented + def __radd__(self, other): + cdef fmpz_mpoly res + other = any_as_fmpz(other) + if other is not NotImplemented: + res = create_fmpz_mpoly(self.ctx) + fmpz_mpoly_add_fmpz(res.val, (self).val, (other).val, self.ctx.val) + return res + return NotImplemented + def __sub__(self, other): cdef fmpz_mpoly res - if typecheck(self, fmpz_mpoly): - if typecheck(other, fmpz_mpoly): - if (self).ctx is not (other).ctx: - ctx, (self, other) = coerce_fmpz_mpolys(self, other) - res = fmpz_mpoly.__new__(fmpz_mpoly) - res.ctx = (self).ctx - fmpz_mpoly_init(res.val, res.ctx.val) - res._init = True - fmpz_mpoly_sub(res.val, (self).val, (other).val, res.ctx.val) - return res - else: - other = any_as_fmpz(other) - if other is not NotImplemented: - res = fmpz_mpoly.__new__(fmpz_mpoly) - res.ctx = (self).ctx - fmpz_mpoly_init(res.val, res.ctx.val) - res._init = True - fmpz_mpoly_sub_fmpz(res.val, (self).val, (other).val, res.ctx.val) - return res + if typecheck(other, fmpz_mpoly): + if (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + res = create_fmpz_mpoly(self.ctx) + fmpz_mpoly_sub(res.val, (self).val, (other).val, res.ctx.val) + return res else: - self = any_as_fmpz(self) - if self is not NotImplemented: - res = fmpz_mpoly.__new__(fmpz_mpoly) - res.ctx = (other).ctx - fmpz_mpoly_init(res.val, res.ctx.val) - res._init = True - fmpz_mpoly_sub_fmpz(res.val, (other).val, (self).val, res.ctx.val) - fmpz_mpoly_neg(res.val, res.val, res.ctx.val) + other = any_as_fmpz(other) + if other is not NotImplemented: + res = create_fmpz_mpoly(self.ctx) + fmpz_mpoly_sub_fmpz(res.val, (self).val, (other).val, self.ctx.val) return res return NotImplemented + def __rsub__(self, other): + cdef fmpz_mpoly res + other = any_as_fmpz(other) + if other is not NotImplemented: + res = create_fmpz_mpoly(self.ctx) + fmpz_mpoly_sub_fmpz(res.val, (self).val, (other).val, res.ctx.val) + return -res + return NotImplemented + def __mul__(self, other): cdef fmpz_mpoly res - if typecheck(self, fmpz_mpoly): - if typecheck(other, fmpz_mpoly): - if (self).ctx is not (other).ctx: - ctx, (self, other) = coerce_fmpz_mpolys(self, other) - res = fmpz_mpoly.__new__(fmpz_mpoly) - res.ctx = (self).ctx - fmpz_mpoly_init(res.val, res.ctx.val) - res._init = True - fmpz_mpoly_mul(res.val, (self).val, (other).val, res.ctx.val) - return res - else: - other = any_as_fmpz(other) - if other is not NotImplemented: - res = fmpz_mpoly.__new__(fmpz_mpoly) - res.ctx = (self).ctx - fmpz_mpoly_init(res.val, res.ctx.val) - res._init = True - fmpz_mpoly_scalar_mul_fmpz(res.val, (self).val, (other).val, res.ctx.val) - return res + if typecheck(other, fmpz_mpoly): + if (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + res = create_fmpz_mpoly(self.ctx) + fmpz_mpoly_mul(res.val, (self).val, (other).val, res.ctx.val) + return res else: - self = any_as_fmpz(self) - if self is not NotImplemented: - res = fmpz_mpoly.__new__(fmpz_mpoly) - res.ctx = (other).ctx - fmpz_mpoly_init(res.val, res.ctx.val) - res._init = True - fmpz_mpoly_scalar_mul_fmpz(res.val, (other).val, (self).val, res.ctx.val) + other = any_as_fmpz(other) + if other is not NotImplemented: + res = create_fmpz_mpoly(self.ctx) + fmpz_mpoly_scalar_mul_fmpz(res.val, (self).val, (other).val, res.ctx.val) return res return NotImplemented + def __rmul__(self, other): + cdef fmpz_mpoly res + other = any_as_fmpz(other) + if other is not NotImplemented: + res = create_fmpz_mpoly(self.ctx) + fmpz_mpoly_scalar_mul_fmpz(res.val, (self).val, (other).val, res.ctx.val) + return res + return NotImplemented + def __pow__(self, other, modulus): cdef fmpz_mpoly res if modulus is not None: raise NotImplementedError - if typecheck(self, fmpz_mpoly): - other = any_as_fmpz(other) - if other is NotImplemented: - return other - if other < 0: - raise ValueError("cannot raise fmpz_mpoly to negative power") - res = fmpz_mpoly.__new__(fmpz_mpoly) - res.ctx = (self).ctx - fmpz_mpoly_init(res.val, res.ctx.val) - res._init = True - if fmpz_mpoly_pow_fmpz(res.val, (self).val, (other).val, res.ctx.val) == 0: - raise ValueError("unreasonably large polynomial") - return res - return NotImplemented + other = any_as_fmpz(other) + if other is NotImplemented: + return other + if other < 0: + raise ValueError("cannot raise fmpz_mpoly to negative power") + res = create_fmpz_mpoly(self.ctx) + if fmpz_mpoly_pow_fmpz(res.val, (self).val, (other).val, res.ctx.val) == 0: + raise ValueError("unreasonably large polynomial") # pragma: no cover + return res def __divmod__(self, other): cdef fmpz_mpoly res, res2 - if typecheck(self, fmpz_mpoly): - if typecheck(other, fmpz_mpoly): - if (self).ctx is not (other).ctx: - ctx, (self, other) = coerce_fmpz_mpolys(self, other) - res = fmpz_mpoly.__new__(fmpz_mpoly) - res.ctx = (self).ctx - fmpz_mpoly_init(res.val, res.ctx.val) - res._init = True - res2 = fmpz_mpoly.__new__(fmpz_mpoly) - res2.ctx = (self).ctx - fmpz_mpoly_init(res2.val, res2.ctx.val) - res2._init = True + if typecheck(other, fmpz_mpoly): + if not other: + raise ZeroDivisionError("fmpz_mpoly division by zero") + elif (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + res = create_fmpz_mpoly(self.ctx) + res2 = create_fmpz_mpoly(self.ctx) + fmpz_mpoly_divrem(res.val, res2.val, (self).val, (other).val, res.ctx.val) + return (res, res2) + else: + other = any_as_fmpz(other) + if other is not NotImplemented: + other = fmpz_mpoly(other, self.ctx) + if not other: + raise ZeroDivisionError("fmpz_mpoly division by zero") + res = create_fmpz_mpoly(self.ctx) + res2 = create_fmpz_mpoly(self.ctx) fmpz_mpoly_divrem(res.val, res2.val, (self).val, (other).val, res.ctx.val) return (res, res2) return NotImplemented + def __rdivmod__(self, other): + cdef fmpz_mpoly res, res2 + if not self: + raise ZeroDivisionError("fmpz_mpoly division by zero") + other = any_as_fmpz(other) + if other is not NotImplemented: + other = fmpz_mpoly(other, self.ctx) + res = create_fmpz_mpoly(self.ctx) + res2 = create_fmpz_mpoly(self.ctx) + fmpz_mpoly_divrem(res.val, res2.val, (other).val, (self).val, res.ctx.val) + return (res, res2) + return NotImplemented + def __floordiv__(self, other): cdef fmpz_mpoly res - if typecheck(self, fmpz_mpoly): - if typecheck(other, fmpz_mpoly): - if (self).ctx is not (other).ctx: - ctx, (self, other) = coerce_fmpz_mpolys(self, other) - res = fmpz_mpoly.__new__(fmpz_mpoly) - res.ctx = (self).ctx - fmpz_mpoly_init(res.val, res.ctx.val) - res._init = True + if typecheck(other, fmpz_mpoly): + if not other: + raise ZeroDivisionError("fmpz_mpoly division by zero") + elif (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + res = create_fmpz_mpoly(self.ctx) + fmpz_mpoly_div(res.val, (self).val, (other).val, res.ctx.val) + return res + else: + other = any_as_fmpz(other) + if other is not NotImplemented: + if not other: + raise ZeroDivisionError("fmpz_mpoly division by zero") + other = fmpz_mpoly(other, self.ctx) + res = create_fmpz_mpoly(self.ctx) fmpz_mpoly_div(res.val, (self).val, (other).val, res.ctx.val) return res return NotImplemented + def __rfloordiv__(self, other): + cdef fmpz_mpoly res + if not self: + raise ZeroDivisionError("fmpz_mpoly division by zero") + other = any_as_fmpz(other) + if other is not NotImplemented: + other = fmpz_mpoly(other, self.ctx) + res = create_fmpz_mpoly(self.ctx) + fmpz_mpoly_div(res.val, (other).val, self.val, res.ctx.val) + return res + return NotImplemented + + def __truediv__(self, other): + cdef: + fmpz_mpoly res + + if typecheck(other, fmpz_mpoly): + if not other: + raise ZeroDivisionError("fmpz_mpoly division by zero") + elif self.ctx is not (other).ctx: + raise IncompatibleContextError(f"{self.ctx} is not {(other).ctx}") + + res = create_fmpz_mpoly(self.ctx) + if fmpz_mpoly_divides(res.val, self.val, (other).val, self.ctx.val): + return res + else: + raise DomainError("fmpz_mpoly division is not exact") + else: + o = any_as_fmpz(other) + if o is NotImplemented: + return NotImplemented + elif not o: + raise ZeroDivisionError("fmpz_mpoly division by zero") + res = create_fmpz_mpoly(self.ctx) + if fmpz_mpoly_scalar_divides_fmpz(res.val, self.val, (o).val, self.ctx.val): + return res + else: + raise DomainError("fmpz_mpoly division is not exact") + + def __rtruediv__(self, other): + cdef fmpz_mpoly res + if not self: + raise ZeroDivisionError("fmpz_mpoly division by zero") + o = any_as_fmpz(other) + if o is NotImplemented: + return NotImplemented + res = create_fmpz_mpoly(self.ctx) + fmpz_mpoly_set_fmpz(res.val, (o).val, self.ctx.val) + return res / self + def __mod__(self, other): return divmod(self, other)[1] + def __rmod__(self, other): + return divmod(other, self)[1] + + def __call__(self, *args) -> fmpz: + cdef: + fmpz_vec V + fmpz vres + slong nvars = self.ctx.nvars(), nargs = len(args) + + if nargs < nvars: + raise ValueError("not enough arguments provided") + elif nargs > nvars: + raise ValueError("more arguments provided than variables") + + args_fmpz = tuple(any_as_fmpz(v) for v in args) + for arg in args_fmpz: + if arg is NotImplemented: + raise TypeError(f"cannot coerce argument ('{arg}') to fmpz") + + V = fmpz_vec(args_fmpz, double_indirect=True) + vres = fmpz.__new__(fmpz) + if fmpz_mpoly_evaluate_all_fmpz(vres.val, self.val, V.double_indirect, self.ctx.val) == 0: + raise ValueError("unreasonably large polynomial") # pragma: no cover + return vres + + def iadd(self, other): + """ + In-place addition, mutates self. + + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> f = ctx.from_dict({(1, 0): 2, (0, 1): 3, (1, 1): 4}) + >>> f + 4*x0*x1 + 2*x0 + 3*x1 + >>> f.iadd(5) + >>> f + 4*x0*x1 + 2*x0 + 3*x1 + 5 + + """ + if typecheck(other, fmpz_mpoly): + if (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + fmpz_mpoly_add((self).val, (self).val, (other).val, self.ctx.val) + return + else: + zval = any_as_fmpz(other) + if zval is not NotImplemented: + fmpz_mpoly_add_fmpz((self).val, (self).val, (zval).val, self.ctx.val) + return + raise NotImplementedError() + + def isub(self, other): + """ + In-place subtraction, mutates self. + + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> f = ctx.from_dict({(1, 0): 2, (0, 1): 3, (1, 1): 4}) + >>> f + 4*x0*x1 + 2*x0 + 3*x1 + >>> f.isub(5) + >>> f + 4*x0*x1 + 2*x0 + 3*x1 - 5 + + """ + if typecheck(other, fmpz_mpoly): + if (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + fmpz_mpoly_sub((self).val, (self).val, (other).val, self.ctx.val) + return + else: + other = any_as_fmpz(other) + if other is not NotImplemented: + fmpz_mpoly_sub_fmpz((self).val, (self).val, (other).val, self.ctx.val) + return + raise NotImplementedError() + + def imul(self, other): + """ + In-place multiplication, mutates self. + + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> f = ctx.from_dict({(1, 0): 2, (0, 1): 3, (1, 1): 4}) + >>> f + 4*x0*x1 + 2*x0 + 3*x1 + >>> f.imul(2) + >>> f + 8*x0*x1 + 4*x0 + 6*x1 + + """ + if typecheck(other, fmpz_mpoly): + if (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + fmpz_mpoly_mul((self).val, (self).val, (other).val, self.ctx.val) + return + else: + other = any_as_fmpz(other) + if other is not NotImplemented: + fmpz_mpoly_scalar_mul_fmpz(self.val, (self).val, (other).val, self.ctx.val) + return + raise NotImplementedError() + + def monoms(self): + """ + Return the exponent vectors of each term as a tuple of fmpz. + + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> f = ctx.from_dict({(0, 0): 1, (1, 0): 2, (0, 1): 3, (1, 1): 4}) + >>> f.monoms() + [(1, 1), (1, 0), (0, 1), (0, 0)] + + """ + cdef: + slong i, nvars = self.ctx.nvars() + fmpz_vec vec = fmpz_vec(nvars, double_indirect=True) + + res = [] + for i in range(len(self)): + fmpz_mpoly_get_term_exp_fmpz(vec.double_indirect, self.val, i, self.ctx.val) + res.append(vec.to_tuple()) + + return res + + def coeffs(self): + """ + Return the coefficients of each term as a fmpz + + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> f = ctx.from_dict({(0, 0): 1, (1, 0): 2, (0, 1): 3, (1, 1): 4}) + >>> f.coeffs() + [4, 2, 3, 1] + + """ + cdef: + fmpz coeff + slong i + + res = [] + for i in range(len(self)): + coeff = fmpz.__new__(fmpz) + fmpz_mpoly_get_term_coeff_fmpz(coeff.val, self.val, i, self.ctx.val) + res.append(coeff) + + return res + + # def terms(self): + # """ + # Return the terms of this polynomial as a list of fmpz_mpolys. + + # >>> from flint import Ordering + # >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') + # >>> f = ctx.from_dict({(0, 0): 1, (1, 0): 2, (0, 1): 3, (1, 1): 4}) + # >>> f.terms() + # [4*x0*x1, 2*x0, 3*x1, 1] + + # """ + # cdef: + # fmpz_mpoly term + # slong i + + # res = [] + # for i in range(len(self)): + # term = create_fmpz_mpoly(self.ctx) + # fmpz_mpoly_get_term(term.val, self.val, i, self.ctx.val) + # res.append(term) + + # return res + + def subs(self, dict_args) -> fmpz_mpoly: + """ + Partial evaluate this polynomial with select constants. Keys must be generator names or generator indices, + all values must be fmpz. + + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> f = ctx.from_dict({(0, 0): 1, (1, 0): 2, (0, 1): 3, (1, 1): 4}) + >>> f.subs({"x1": 0}) + 2*x0 + 1 + + """ + cdef: + fmpz_mpoly res + slong i + + args = tuple((self.ctx.variable_to_index(k), any_as_fmpz(v)) for k, v in dict_args.items()) + for _, v in args: + if v is NotImplemented: + raise TypeError(f"cannot coerce argument ('{v}') to fmpz") + + # Partial application with args in Z. We evaluate the polynomial one variable at a time + res = create_fmpz_mpoly(self.ctx) + + fmpz_mpoly_set(res.val, self.val, self.ctx.val) + for i, arg in args: + if fmpz_mpoly_evaluate_one_fmpz(res.val, res.val, i, (arg).val, self.ctx.val) == 0: + raise ValueError("unreasonably large polynomial") # pragma: no cover + return res + + def compose(self, *args, ctx=None) -> fmpz_mpoly: + """ + Compose this polynomial with other fmpz_mpolys. All arguments must share the same context, it may different + from this polynomials context. + + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(1, Ordering.lex, 'x') + >>> ctx1 = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'y') + >>> f = ctx.from_dict({(2,): 1}) + >>> g = ctx1.from_dict({(1, 0): 1, (0, 1): 1}) + >>> f + x^2 + >>> g + y0 + y1 + >>> f.compose(g) + y0^2 + 2*y0*y1 + y1^2 + + """ + cdef: + fmpz_mpoly res + fmpz_mpoly_ctx res_ctx + fmpz_mpoly_vec C + slong i, nvars = self.ctx.nvars(), nargs = len(args) + + if nargs < nvars: + raise ValueError("not enough arguments provided") + elif nargs > nvars: + raise ValueError("more arguments provided than variables") + elif self.ctx.nvars() == 0 and ctx is None: + raise ValueError("a context must be provided when composing a polynomial with no generators") + elif not all(typecheck(arg, fmpz_mpoly) for arg in args): + raise TypeError("all arguments must be fmpz_mpolys") + + if ctx is None: + res_ctx = ( args[0]).ctx + elif typecheck(ctx, fmpz_mpoly_ctx): + res_ctx = ctx + else: + raise TypeError(f"provided context ({ctx}) is not a fmpz_mpoly_ctx") + + if not all(( arg).ctx is res_ctx for arg in args): + raise IncompatibleContextError( + "all arguments must share the " + ("same" if ctx is not None else "provided") + " context" + ) + + C = fmpz_mpoly_vec(args, res_ctx, double_indirect=True) + res = create_fmpz_mpoly(res_ctx) + if fmpz_mpoly_compose_fmpz_mpoly(res.val, self.val, C.double_indirect, self.ctx.val, res_ctx.val) == 0: + raise ValueError("unreasonably large polynomial") # pragma: no cover + return res + + def context(self): + """ + Return the context object for this polynomials. + + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> p = ctx.from_dict({(0, 1): 2}) + >>> ctx is p.context() + True + """ + return self.ctx + + def is_one(self): + return fmpz_mpoly_is_one(self.val, self.ctx.val) + + def coefficient(self, slong i): + """ + Return the coefficient at index `i`. + + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) + >>> p.coefficient(1) + 2 + """ + cdef fmpz v + if not 0 <= i < fmpz_mpoly_length(self.val, self.ctx.val): + raise IndexError("term index out of range") + else: + v = fmpz.__new__(fmpz) + fmpz_mpoly_get_term_coeff_fmpz(v.val, self.val, i, self.ctx.val) + return v + + def monomial(self, slong i): + """ + Return the exponent vector at index `i` as a tuple. + + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) + >>> p.monomial(1) + (0, 1) + """ + cdef: + slong nvars = self.ctx.nvars() + + if not 0 <= i < fmpz_mpoly_length(self.val, self.ctx.val): + raise IndexError("term index out of range") + res = fmpz_vec(nvars, double_indirect=True) + fmpz_mpoly_get_term_exp_fmpz(res.double_indirect, self.val, i, self.ctx.val) + return res.to_tuple() + + def degrees(self): + """ + Return a dictionary of variable name to degree. + + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(4, Ordering.lex, 'x') + >>> p = ctx.from_dict({(1, 0, 0, 0): 1, (0, 2, 0, 0): 2, (0, 0, 3, 0): 3}) + >>> p.degrees() + (1, 2, 3, 0) + """ + cdef: + slong nvars = self.ctx.nvars() + + res = fmpz_vec(nvars, double_indirect=True) + fmpz_mpoly_degrees_fmpz(res.double_indirect, self.val, self.ctx.val) + return res.to_tuple() + + def total_degree(self): + """ + Return the total degree. + + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(4, Ordering.lex, 'x') + >>> p = ctx.from_dict({(1, 0, 0, 0): 1, (0, 2, 0, 0): 2, (0, 0, 3, 0): 3}) + >>> p.total_degree() + 3 + """ + cdef fmpz res = fmpz() + fmpz_mpoly_total_degree_fmpz(( res).val, self.val, self.ctx.val) + return res + + def repr(self): + return f"{self.ctx}.from_dict({self.to_dict()})" + + def str(self): + cdef bytes s = fmpz_mpoly_get_str_pretty(self.val, self.ctx.c_names, self.ctx.val) + res = s.decode().replace("+", " + ").replace("-", " - ") + if res.startswith(" - "): + res = "-" + res[3:] + return res + def gcd(self, other): + """ + Return the gcd of self and other. + + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> f = ctx.from_dict({(1, 1): 4, (0, 0): 1}) + >>> g = ctx.from_dict({(0, 1): 2, (1, 0): 2}) + >>> (f * g).gcd(f) + 4*x0*x1 + 1 + """ cdef fmpz_mpoly res - assert isinstance(other, fmpz_mpoly) - if (self).ctx is not (other).ctx: - ctx, (self, other) = coerce_fmpz_mpolys(self, other) - res = fmpz_mpoly.__new__(fmpz_mpoly) - res.ctx = (self).ctx - fmpz_mpoly_init(res.val, res.ctx.val) - res._init = True + if not typecheck(other, fmpz_mpoly): + raise TypeError("argument must be a fmpz_mpoly") + elif (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + res = create_fmpz_mpoly(self.ctx) fmpz_mpoly_gcd(res.val, (self).val, (other).val, res.ctx.val) return res - def __call__(self, *args): + def sqrt(self, assume_perfect_square: bool = False): + """ + Return the square root of self. + If self is known to be a perfect square provide `assume_perfect_square=True` for a more efficient + result. If self is not a square root the result is not guaranteed to be correct. + + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> f = ctx.from_dict({(1, 1): 4, (0, 0): 1}) + >>> (f * f).sqrt() + 4*x0*x1 + 1 + """ cdef fmpz_mpoly res - cdef fmpz_mpoly_ctx res_ctx - cdef fmpz_struct ** V - cdef fmpz vres - cdef fmpz_mpoly_struct ** C - cdef slong i, nvars, nargs - other = tuple(args) - nargs = len(args) - nvars = self.ctx.nvars() - # todo: should extend with generators instead? - if nargs < nvars: - args = args + (0,) * (nvars - nargs) - if nargs > nvars: - args = args[:nvars] - args_fmpz = [any_as_fmpz(v) for v in args] - # todo: for combination, compose - # todo: if fewer than number of variables, evaluate partially? - if NotImplemented not in args_fmpz: - V = libc.stdlib.malloc(nvars * sizeof(fmpz_struct *)) - try: - for i in range(nvars): - V[i] = &(( args_fmpz[i]).val[0]) - vres = fmpz.__new__(fmpz) - if fmpz_mpoly_evaluate_all_fmpz(vres.val, self.val, V, self.ctx.val) == 0: - raise ValueError("unreasonably large polynomial") - return vres - finally: - libc.stdlib.free(V) + res = create_fmpz_mpoly(self.ctx) + + if fmpz_mpoly_sqrt_heap(res.val, self.val, self.ctx.val, not assume_perfect_square): + return res else: - res_ctx, args = coerce_fmpz_mpolys(*args) - C = libc.stdlib.malloc(nvars * sizeof(fmpz_mpoly_struct *)) - try: - for i in range(nvars): - C[i] = &(( args[i]).val[0]) - res = fmpz_mpoly.__new__(fmpz_mpoly) - res.ctx = res_ctx - fmpz_mpoly_init(res.val, res.ctx.val) - res._init = True - if fmpz_mpoly_compose_fmpz_mpoly(res.val, self.val, C, self.ctx.val, res_ctx.val) == 0: - raise ValueError("unreasonably large polynomial") - return res - finally: - libc.stdlib.free(C) + raise ValueError("polynomial is not a perfect square") - ''' def factor(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. + >>> from flint import Ordering + >>> Zm = fmpz_mpoly + >>> ctx = fmpz_mpoly_ctx.get_context(3, Ordering.lex, 'x,y,z') + >>> p1 = Zm("2*x + 4", ctx) + >>> p2 = Zm("3*x*z + + 3*x + 3*z + 3", ctx) + >>> (p1 * p2).factor() + (6, [(z + 1, 1), (x + 2, 1), (x + 1, 1)]) + >>> (p2 * p1 * p2).factor() + (18, [(z + 1, 2), (x + 2, 1), (x + 1, 2)]) + """ + cdef: + fmpz_mpoly_factor_t fac + fmpz c + fmpz_mpoly u + + fmpz_mpoly_factor_init(fac, self.ctx.val) + fmpz_mpoly_factor(fac, self.val, self.ctx.val) + res = [0] * fac.num + + for i in range(fac.num): + u = create_fmpz_mpoly(self.ctx) + fmpz_mpoly_set((u).val, &fac.poly[i], self.ctx.val) + + c = fmpz.__new__(fmpz) + fmpz_set((c).val, &fac.exp[i]) + + res[i] = (u, c) + + c = fmpz.__new__(fmpz) + fmpz_set((c).val, fac.constant) + fmpz_mpoly_factor_clear(fac, self.ctx.val) + return c, 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. + + >>> from flint import Ordering + >>> Zm = fmpz_mpoly + >>> ctx = fmpz_mpoly_ctx.get_context(3, Ordering.lex, 'x,y,z') + >>> p1 = Zm("2*x + 4", ctx) + >>> p2 = Zm("3*x*y + 3*x + 3*y + 3", ctx) + >>> (p1 * p2).factor_squarefree() + (6, [(y + 1, 1), (x^2 + 3*x + 2, 1)]) + >>> (p1 * p2 * p1).factor_squarefree() + (12, [(y + 1, 1), (x + 1, 1), (x + 2, 2)]) """ - cdef fmpz_mpoly_factor_t fac - cdef int i - cdef fmpz c - cdef fmpz_mpoly u + cdef: + fmpz_mpoly_factor_t fac + fmpz c + fmpz_mpoly u + fmpz_mpoly_factor_init(fac, self.ctx.val) - fmpz_mpoly_factor(fac, self.val, 1, self.ctx.val) - res = [0] * fac.length - for 0 <= i < fac.length: - u = fmpz_mpoly.__new__(fmpz_mpoly) - u.ctx = self.ctx + fmpz_mpoly_factor_squarefree(fac, self.val, self.ctx.val) + res = [0] * fac.num + + for i in range(fac.num): + u = create_fmpz_mpoly(self.ctx) fmpz_mpoly_init(u.val, u.ctx.val) - u._init = True fmpz_mpoly_set((u).val, &fac.poly[i], self.ctx.val) + c = fmpz.__new__(fmpz) fmpz_set((c).val, &fac.exp[i]) + res[i] = (u, c) + c = fmpz.__new__(fmpz) - fmpz_set((c).val, fac.content) # should be & with ... + fmpz_set((c).val, fac.constant) fmpz_mpoly_factor_clear(fac, self.ctx.val) return c, res - ''' + # TODO: Rethink context conversions, particularly the proposed methods in #132 + # def coerce_to_context(self, ctx): + # cdef: + # fmpz_mpoly res + # slong *C + # slong i + + # if not typecheck(ctx, fmpz_mpoly_ctx): + # raise ValueError("provided context is not a fmpz_mpoly_ctx") + + # if self.ctx is ctx: + # return self + + # C = libc.stdlib.malloc(self.ctx.val.minfo.nvars * sizeof(slong *)) + # if C is NULL: + # raise MemoryError("malloc returned a null pointer") + # res = create_fmpz_mpoly(self.ctx) + + # vars = {x: i for i, x in enumerate(ctx.py_names)} + # for i, var in enumerate(self.ctx.py_names): + # C[i] = vars[var] + + # fmpz_mpoly_compose_fmpz_mpoly_gen(res.val, self.val, C, self.ctx.val, (ctx).val) + + # libc.stdlib.free(C) + # return res + + def derivative(self, var): + """ + Return the derivative of this polynomial with respect to the provided variable. + The argument can either be the variable as a string, or the index of the + variable in the context. + + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> p = ctx.from_dict({(0, 3): 2, (2, 1): 3}) + >>> p + 3*x0^2*x1 + 2*x1^3 + >>> p.derivative("x0") + 6*x0*x1 + >>> p.derivative(1) + 3*x0^2 + 6*x1^2 + + """ + cdef: + fmpz_mpoly res + slong i = self.ctx.variable_to_index(var) + + res = create_fmpz_mpoly(self.ctx) + + fmpz_mpoly_derivative(res.val, self.val, i, self.ctx.val) + return res + + def integral(self, var): + """ + Return the integral of this polynomial*B with respect to the provided variable where B is minimal. That is, + given p this method returns (B, P) such that P' = B*p or P/B is the formal integral of p. + + The argument can either be the variable as a string, or the index of the variable in the context. + + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> p = ctx.from_dict({(0, 3): 2, (2, 1): 3}) + >>> p + 3*x0^2*x1 + 2*x1^3 + >>> p.integral("x0") + (1, x0^3*x1 + 2*x0*x1^3) + >>> p.integral(1) + (2, 3*x0^2*x1^2 + x1^4) + + """ + cdef: + fmpz_mpoly res + fmpz scale + slong i = self.ctx.variable_to_index(var) + + res = create_fmpz_mpoly(self.ctx) + + scale = fmpz() + + fmpz_mpoly_integral(res.val, scale.val, self.val, i, self.ctx.val) + return scale, res + + +cdef class fmpz_mpoly_vec: + """ + A class representing a vector of fmpz_mpolys. + """ + + def __cinit__(self, iterable_or_len, fmpz_mpoly_ctx ctx, bint double_indirect = False): + if isinstance(iterable_or_len, int): + length = iterable_or_len + else: + length = len(iterable_or_len) + + self.ctx = ctx + fmpz_mpoly_vec_init(self.val, length, self.ctx.val) + + if double_indirect: + self.double_indirect = libc.stdlib.malloc(length * sizeof(fmpz_mpoly_struct *)) + if self.double_indirect is NULL: + raise MemoryError("malloc returned a null pointer") # pragma: no cover + + for i in range(length): + self.double_indirect[i] = fmpz_mpoly_vec_entry(self.val, i) + else: + self.double_indirect = NULL + + def __init__(self, iterable_or_len, _, double_indirect: bool = False): + if not isinstance(iterable_or_len, int): + for i, x in enumerate(iterable_or_len): + self[i] = x + + def __dealloc__(self): + libc.stdlib.free(self.double_indirect) + fmpz_mpoly_vec_clear(self.val, self.ctx.val) + + def __getitem__(self, x): + if not isinstance(x, int): + raise TypeError("index is not integer") + elif not 0 <= x < self.val.length: + raise IndexError("index out of range") + + cdef fmpz_mpoly z = create_fmpz_mpoly(self.ctx) + fmpz_mpoly_set(z.val, fmpz_mpoly_vec_entry(self.val, x), self.ctx.val) + return z + + def __setitem__(self, x, y): + if not isinstance(x, int): + raise TypeError("index is not integer") + elif not 0 <= x < self.val.length: + raise IndexError("index out of range") + elif not typecheck(y, fmpz_mpoly): + raise TypeError("argument is not fmpz_mpoly") + elif (y).ctx is not self.ctx: + raise IncompatibleContextError(f"{(y).ctx} is not {self.ctx}") + + fmpz_mpoly_set(fmpz_mpoly_vec_entry(self.val, x), (y).val, self.ctx.val) + + def __len__(self): + return self.val.length + + def __str__(self): + s = [None] * self.val.length + for i in range(self.val.length): + x = create_fmpz_mpoly(self.ctx) + fmpz_mpoly_set(x.val, fmpz_mpoly_vec_entry(self.val, i), self.ctx.val) + s[i] = str(x) + return f"[{', '.join(s)}]" + + def __repr__(self): + return f"fmpz_mpoly_vec({self}, ctx={self.ctx})" + + def to_tuple(self): + return tuple(self[i] for i in range(self.val.length)) diff --git a/src/flint/types/fmpz_poly.pxd b/src/flint/types/fmpz_poly.pxd index 61bc109d..a91ad265 100644 --- a/src/flint/types/fmpz_poly.pxd +++ b/src/flint/types/fmpz_poly.pxd @@ -1,4 +1,3 @@ - from flint.flint_base.flint_base cimport flint_poly from flint.flintlib.fmpz_poly cimport fmpz_poly_t diff --git a/src/flint/types/fmpz_vec.pxd b/src/flint/types/fmpz_vec.pxd new file mode 100644 index 00000000..6e4b1cf7 --- /dev/null +++ b/src/flint/types/fmpz_vec.pxd @@ -0,0 +1,7 @@ +from flint.flintlib.fmpz cimport fmpz_struct +from flint.flintlib.flint cimport slong + +cdef class fmpz_vec: + cdef fmpz_struct *val + cdef fmpz_struct **double_indirect + cdef slong length diff --git a/src/flint/types/fmpz_vec.pyx b/src/flint/types/fmpz_vec.pyx new file mode 100644 index 00000000..d5d7e27e --- /dev/null +++ b/src/flint/types/fmpz_vec.pyx @@ -0,0 +1,82 @@ +from flint.flintlib.fmpz cimport fmpz_struct, fmpz_set, fmpz_init_set +from flint.flintlib.flint cimport slong +from flint.flintlib.fmpz_vec cimport _fmpz_vec_init, _fmpz_vec_clear + +from flint.types.fmpz cimport fmpz, any_as_fmpz + +cimport libc.stdlib + +cdef class fmpz_vec: + def __cinit__(self, iterable_or_len, bint double_indirect=False): + if isinstance(iterable_or_len, int): + self.length = iterable_or_len + else: + self.length = len(iterable_or_len) + + self.val = _fmpz_vec_init(self.length) + + if double_indirect: + self.double_indirect = libc.stdlib.malloc(self.length * sizeof(fmpz_struct *)) + if self.double_indirect is NULL: + raise MemoryError("malloc returned a null pointer") + + for i in range(self.length): + self.double_indirect[i] = &self.val[i] + else: + self.double_indirect = NULL + + def __init__(self, iterable_or_len, double_indirect: bool = False): + if not isinstance(iterable_or_len, int): + for i, x in enumerate(iterable_or_len): + self[i] = x + + def __dealloc__(self): + libc.stdlib.free(self.double_indirect) + if self.val is not NULL: + _fmpz_vec_clear(self.val, self.length) + self.val = NULL + + def __getitem__(self, x): + if not isinstance(x, int): + raise TypeError("index is not integer") + elif not 0 <= x < self.length: + raise IndexError("index out of range") + + cdef fmpz z = fmpz.__new__(fmpz) + fmpz_init_set(z.val, &self.val[x]) + return z + + def __setitem__(self, x, y): + if not isinstance(x, int): + raise TypeError("index is not integer") + elif not 0 <= x < self.length: + raise IndexError("index out of range") + + y = any_as_fmpz(y) + if y is NotImplemented: + raise TypeError("argument is not coercible to fmpz") + + fmpz_set(&self.val[x], (y).val) + + def __len__(self): + return self.length + + def __str__(self): + return self.str() + + def __repr__(self): + return self.repr() + + def str(self, *args): + s = [None] * self.length + for i in range(self.length): + x = fmpz.__new__(fmpz) + fmpz_init_set(x.val, &self.val[i]) + s[i] = x.str(*args) + return str(s) + + def repr(self, *args): + return f"fmpz_vec({self.str(*args)}, {self.length})" + + def to_tuple(self): + return tuple(self[i] for i in range(self.length)) diff --git a/src/flint/types/meson.build b/src/flint/types/meson.build index 7a90bb41..4dbfcd35 100644 --- a/src/flint/types/meson.build +++ b/src/flint/types/meson.build @@ -9,11 +9,13 @@ exts = [ 'fmpz_poly', 'fmpz_mat', 'fmpz_series', + 'fmpz_vec', 'fmpq', 'fmpq_poly', 'fmpq_mat', 'fmpq_series', + 'fmpq_vec', 'nmod', 'nmod_poly', @@ -39,6 +41,7 @@ exts = [ 'dirichlet', 'fmpz_mpoly', + 'fmpq_mpoly', ] if have_acb_theta diff --git a/src/flint/utils/flint_exceptions.py b/src/flint/utils/flint_exceptions.py index 580b4549..9d9c271b 100644 --- a/src/flint/utils/flint_exceptions.py +++ b/src/flint/utils/flint_exceptions.py @@ -1,8 +1,18 @@ +class FlintError(Exception): + """Generic parent class for all flint relation exceptions.""" + pass - -class DomainError(Exception): +class DomainError(FlintError): """ Exception intended to be called when a method is called on a ring or field for which the domain is invalid. """ pass + + +class IncompatibleContextError(FlintError): + """ + Exception intended to be called when a method involves two or more + incompatible contexts. + """ + pass