diff --git a/lib/python3.12/site-packages/sympy/codegen/__init__.py b/lib/python3.12/site-packages/sympy/codegen/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..62b195633bae28371bdf9e79317050d7fa7125ae --- /dev/null +++ b/lib/python3.12/site-packages/sympy/codegen/__init__.py @@ -0,0 +1,24 @@ +""" The ``sympy.codegen`` module contains classes and functions for building +abstract syntax trees of algorithms. These trees may then be printed by the +code-printers in ``sympy.printing``. + +There are several submodules available: +- ``sympy.codegen.ast``: AST nodes useful across multiple languages. +- ``sympy.codegen.cnodes``: AST nodes useful for the C family of languages. +- ``sympy.codegen.fnodes``: AST nodes useful for Fortran. +- ``sympy.codegen.cfunctions``: functions specific to C (C99 math functions) +- ``sympy.codegen.ffunctions``: functions specific to Fortran (e.g. ``kind``). + + + +""" +from .ast import ( + Assignment, aug_assign, CodeBlock, For, Attribute, Variable, Declaration, + While, Scope, Print, FunctionPrototype, FunctionDefinition, FunctionCall +) + +__all__ = [ + 'Assignment', 'aug_assign', 'CodeBlock', 'For', 'Attribute', 'Variable', + 'Declaration', 'While', 'Scope', 'Print', 'FunctionPrototype', + 'FunctionDefinition', 'FunctionCall', +] diff --git a/lib/python3.12/site-packages/sympy/codegen/abstract_nodes.py b/lib/python3.12/site-packages/sympy/codegen/abstract_nodes.py new file mode 100644 index 0000000000000000000000000000000000000000..ae0a8b3e996a7112edf2568c00138e11c3f3327d --- /dev/null +++ b/lib/python3.12/site-packages/sympy/codegen/abstract_nodes.py @@ -0,0 +1,18 @@ +"""This module provides containers for python objects that are valid +printing targets but are not a subclass of SymPy's Printable. +""" + + +from sympy.core.containers import Tuple + + +class List(Tuple): + """Represents a (frozen) (Python) list (for code printing purposes).""" + def __eq__(self, other): + if isinstance(other, list): + return self == List(*other) + else: + return self.args == other + + def __hash__(self): + return super().__hash__() diff --git a/lib/python3.12/site-packages/sympy/codegen/algorithms.py b/lib/python3.12/site-packages/sympy/codegen/algorithms.py new file mode 100644 index 0000000000000000000000000000000000000000..f4890eb8c25e565095600e2713c0de270aa0cf97 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/codegen/algorithms.py @@ -0,0 +1,180 @@ +from sympy.core.containers import Tuple +from sympy.core.numbers import oo +from sympy.core.relational import (Gt, Lt) +from sympy.core.symbol import (Dummy, Symbol) +from sympy.functions.elementary.complexes import Abs +from sympy.functions.elementary.miscellaneous import Min, Max +from sympy.logic.boolalg import And +from sympy.codegen.ast import ( + Assignment, AddAugmentedAssignment, break_, CodeBlock, Declaration, FunctionDefinition, + Print, Return, Scope, While, Variable, Pointer, real +) +from sympy.codegen.cfunctions import isnan + +""" This module collects functions for constructing ASTs representing algorithms. """ + +def newtons_method(expr, wrt, atol=1e-12, delta=None, *, rtol=4e-16, debug=False, + itermax=None, counter=None, delta_fn=lambda e, x: -e/e.diff(x), + cse=False, handle_nan=None, + bounds=None): + """ Generates an AST for Newton-Raphson method (a root-finding algorithm). + + Explanation + =========== + + Returns an abstract syntax tree (AST) based on ``sympy.codegen.ast`` for Netwon's + method of root-finding. + + Parameters + ========== + + expr : expression + wrt : Symbol + With respect to, i.e. what is the variable. + atol : number or expression + Absolute tolerance (stopping criterion) + rtol : number or expression + Relative tolerance (stopping criterion) + delta : Symbol + Will be a ``Dummy`` if ``None``. + debug : bool + Whether to print convergence information during iterations + itermax : number or expr + Maximum number of iterations. + counter : Symbol + Will be a ``Dummy`` if ``None``. + delta_fn: Callable[[Expr, Symbol], Expr] + computes the step, default is newtons method. For e.g. Halley's method + use delta_fn=lambda e, x: -2*e*e.diff(x)/(2*e.diff(x)**2 - e*e.diff(x, 2)) + cse: bool + Perform common sub-expression elimination on delta expression + handle_nan: Token + How to handle occurrence of not-a-number (NaN). + bounds: Optional[tuple[Expr, Expr]] + Perform optimization within bounds + + Examples + ======== + + >>> from sympy import symbols, cos + >>> from sympy.codegen.ast import Assignment + >>> from sympy.codegen.algorithms import newtons_method + >>> x, dx, atol = symbols('x dx atol') + >>> expr = cos(x) - x**3 + >>> algo = newtons_method(expr, x, atol=atol, delta=dx) + >>> algo.has(Assignment(dx, -expr/expr.diff(x))) + True + + References + ========== + + .. [1] https://en.wikipedia.org/wiki/Newton%27s_method + + """ + + if delta is None: + delta = Dummy() + Wrapper = Scope + name_d = 'delta' + else: + Wrapper = lambda x: x + name_d = delta.name + + delta_expr = delta_fn(expr, wrt) + if cse: + from sympy.simplify.cse_main import cse + cses, (red,) = cse([delta_expr.factor()]) + whl_bdy = [Assignment(dum, sub_e) for dum, sub_e in cses] + whl_bdy += [Assignment(delta, red)] + else: + whl_bdy = [Assignment(delta, delta_expr)] + if handle_nan is not None: + whl_bdy += [While(isnan(delta), CodeBlock(handle_nan, break_))] + whl_bdy += [AddAugmentedAssignment(wrt, delta)] + if bounds is not None: + whl_bdy += [Assignment(wrt, Min(Max(wrt, bounds[0]), bounds[1]))] + if debug: + prnt = Print([wrt, delta], r"{}=%12.5g {}=%12.5g\n".format(wrt.name, name_d)) + whl_bdy += [prnt] + req = Gt(Abs(delta), atol + rtol*Abs(wrt)) + declars = [Declaration(Variable(delta, type=real, value=oo))] + if itermax is not None: + counter = counter or Dummy(integer=True) + v_counter = Variable.deduced(counter, 0) + declars.append(Declaration(v_counter)) + whl_bdy.append(AddAugmentedAssignment(counter, 1)) + req = And(req, Lt(counter, itermax)) + whl = While(req, CodeBlock(*whl_bdy)) + blck = declars + if debug: + blck.append(Print([wrt], r"{}=%12.5g\n".format(wrt.name))) + blck += [whl] + return Wrapper(CodeBlock(*blck)) + + +def _symbol_of(arg): + if isinstance(arg, Declaration): + arg = arg.variable.symbol + elif isinstance(arg, Variable): + arg = arg.symbol + return arg + + +def newtons_method_function(expr, wrt, params=None, func_name="newton", attrs=Tuple(), *, delta=None, **kwargs): + """ Generates an AST for a function implementing the Newton-Raphson method. + + Parameters + ========== + + expr : expression + wrt : Symbol + With respect to, i.e. what is the variable + params : iterable of symbols + Symbols appearing in expr that are taken as constants during the iterations + (these will be accepted as parameters to the generated function). + func_name : str + Name of the generated function. + attrs : Tuple + Attribute instances passed as ``attrs`` to ``FunctionDefinition``. + \\*\\*kwargs : + Keyword arguments passed to :func:`sympy.codegen.algorithms.newtons_method`. + + Examples + ======== + + >>> from sympy import symbols, cos + >>> from sympy.codegen.algorithms import newtons_method_function + >>> from sympy.codegen.pyutils import render_as_module + >>> x = symbols('x') + >>> expr = cos(x) - x**3 + >>> func = newtons_method_function(expr, x) + >>> py_mod = render_as_module(func) # source code as string + >>> namespace = {} + >>> exec(py_mod, namespace, namespace) + >>> res = eval('newton(0.5)', namespace) + >>> abs(res - 0.865474033102) < 1e-12 + True + + See Also + ======== + + sympy.codegen.algorithms.newtons_method + + """ + if params is None: + params = (wrt,) + pointer_subs = {p.symbol: Symbol('(*%s)' % p.symbol.name) + for p in params if isinstance(p, Pointer)} + if delta is None: + delta = Symbol('d_' + wrt.name) + if expr.has(delta): + delta = None # will use Dummy + algo = newtons_method(expr, wrt, delta=delta, **kwargs).xreplace(pointer_subs) + if isinstance(algo, Scope): + algo = algo.body + not_in_params = expr.free_symbols.difference({_symbol_of(p) for p in params}) + if not_in_params: + raise ValueError("Missing symbols in params: %s" % ', '.join(map(str, not_in_params))) + declars = tuple(Variable(p, real) for p in params) + body = CodeBlock(algo, Return(wrt)) + return FunctionDefinition(real, func_name, declars, body, attrs=attrs) diff --git a/lib/python3.12/site-packages/sympy/codegen/approximations.py b/lib/python3.12/site-packages/sympy/codegen/approximations.py new file mode 100644 index 0000000000000000000000000000000000000000..4f15266ab18f93b18b50269356c94484dd2f8ac4 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/codegen/approximations.py @@ -0,0 +1,187 @@ +import math +from sympy.sets.sets import Interval +from sympy.calculus.singularities import is_increasing, is_decreasing +from sympy.codegen.rewriting import Optimization +from sympy.core.function import UndefinedFunction + +""" +This module collects classes useful for approimate rewriting of expressions. +This can be beneficial when generating numeric code for which performance is +of greater importance than precision (e.g. for preconditioners used in iterative +methods). +""" + +class SumApprox(Optimization): + """ + Approximates sum by neglecting small terms. + + Explanation + =========== + + If terms are expressions which can be determined to be monotonic, then + bounds for those expressions are added. + + Parameters + ========== + + bounds : dict + Mapping expressions to length 2 tuple of bounds (low, high). + reltol : number + Threshold for when to ignore a term. Taken relative to the largest + lower bound among bounds. + + Examples + ======== + + >>> from sympy import exp + >>> from sympy.abc import x, y, z + >>> from sympy.codegen.rewriting import optimize + >>> from sympy.codegen.approximations import SumApprox + >>> bounds = {x: (-1, 1), y: (1000, 2000), z: (-10, 3)} + >>> sum_approx3 = SumApprox(bounds, reltol=1e-3) + >>> sum_approx2 = SumApprox(bounds, reltol=1e-2) + >>> sum_approx1 = SumApprox(bounds, reltol=1e-1) + >>> expr = 3*(x + y + exp(z)) + >>> optimize(expr, [sum_approx3]) + 3*(x + y + exp(z)) + >>> optimize(expr, [sum_approx2]) + 3*y + 3*exp(z) + >>> optimize(expr, [sum_approx1]) + 3*y + + """ + + def __init__(self, bounds, reltol, **kwargs): + super().__init__(**kwargs) + self.bounds = bounds + self.reltol = reltol + + def __call__(self, expr): + return expr.factor().replace(self.query, lambda arg: self.value(arg)) + + def query(self, expr): + return expr.is_Add + + def value(self, add): + for term in add.args: + if term.is_number or term in self.bounds or len(term.free_symbols) != 1: + continue + fs, = term.free_symbols + if fs not in self.bounds: + continue + intrvl = Interval(*self.bounds[fs]) + if is_increasing(term, intrvl, fs): + self.bounds[term] = ( + term.subs({fs: self.bounds[fs][0]}), + term.subs({fs: self.bounds[fs][1]}) + ) + elif is_decreasing(term, intrvl, fs): + self.bounds[term] = ( + term.subs({fs: self.bounds[fs][1]}), + term.subs({fs: self.bounds[fs][0]}) + ) + else: + return add + + if all(term.is_number or term in self.bounds for term in add.args): + bounds = [(term, term) if term.is_number else self.bounds[term] for term in add.args] + largest_abs_guarantee = 0 + for lo, hi in bounds: + if lo <= 0 <= hi: + continue + largest_abs_guarantee = max(largest_abs_guarantee, + min(abs(lo), abs(hi))) + new_terms = [] + for term, (lo, hi) in zip(add.args, bounds): + if max(abs(lo), abs(hi)) >= largest_abs_guarantee*self.reltol: + new_terms.append(term) + return add.func(*new_terms) + else: + return add + + +class SeriesApprox(Optimization): + """ Approximates functions by expanding them as a series. + + Parameters + ========== + + bounds : dict + Mapping expressions to length 2 tuple of bounds (low, high). + reltol : number + Threshold for when to ignore a term. Taken relative to the largest + lower bound among bounds. + max_order : int + Largest order to include in series expansion + n_point_checks : int (even) + The validity of an expansion (with respect to reltol) is checked at + discrete points (linearly spaced over the bounds of the variable). The + number of points used in this numerical check is given by this number. + + Examples + ======== + + >>> from sympy import sin, pi + >>> from sympy.abc import x, y + >>> from sympy.codegen.rewriting import optimize + >>> from sympy.codegen.approximations import SeriesApprox + >>> bounds = {x: (-.1, .1), y: (pi-1, pi+1)} + >>> series_approx2 = SeriesApprox(bounds, reltol=1e-2) + >>> series_approx3 = SeriesApprox(bounds, reltol=1e-3) + >>> series_approx8 = SeriesApprox(bounds, reltol=1e-8) + >>> expr = sin(x)*sin(y) + >>> optimize(expr, [series_approx2]) + x*(-y + (y - pi)**3/6 + pi) + >>> optimize(expr, [series_approx3]) + (-x**3/6 + x)*sin(y) + >>> optimize(expr, [series_approx8]) + sin(x)*sin(y) + + """ + def __init__(self, bounds, reltol, max_order=4, n_point_checks=4, **kwargs): + super().__init__(**kwargs) + self.bounds = bounds + self.reltol = reltol + self.max_order = max_order + if n_point_checks % 2 == 1: + raise ValueError("Checking the solution at expansion point is not helpful") + self.n_point_checks = n_point_checks + self._prec = math.ceil(-math.log10(self.reltol)) + + def __call__(self, expr): + return expr.factor().replace(self.query, lambda arg: self.value(arg)) + + def query(self, expr): + return (expr.is_Function and not isinstance(expr, UndefinedFunction) + and len(expr.args) == 1) + + def value(self, fexpr): + free_symbols = fexpr.free_symbols + if len(free_symbols) != 1: + return fexpr + symb, = free_symbols + if symb not in self.bounds: + return fexpr + lo, hi = self.bounds[symb] + x0 = (lo + hi)/2 + cheapest = None + for n in range(self.max_order+1, 0, -1): + fseri = fexpr.series(symb, x0=x0, n=n).removeO() + n_ok = True + for idx in range(self.n_point_checks): + x = lo + idx*(hi - lo)/(self.n_point_checks - 1) + val = fseri.xreplace({symb: x}) + ref = fexpr.xreplace({symb: x}) + if abs((1 - val/ref).evalf(self._prec)) > self.reltol: + n_ok = False + break + + if n_ok: + cheapest = fseri + else: + break + + if cheapest is None: + return fexpr + else: + return cheapest diff --git a/lib/python3.12/site-packages/sympy/codegen/ast.py b/lib/python3.12/site-packages/sympy/codegen/ast.py new file mode 100644 index 0000000000000000000000000000000000000000..0522a25eb301108c4eb2a50b6ec99c74d41869c6 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/codegen/ast.py @@ -0,0 +1,1906 @@ +""" +Types used to represent a full function/module as an Abstract Syntax Tree. + +Most types are small, and are merely used as tokens in the AST. A tree diagram +has been included below to illustrate the relationships between the AST types. + + +AST Type Tree +------------- +:: + + *Basic* + | + | + CodegenAST + | + |--->AssignmentBase + | |--->Assignment + | |--->AugmentedAssignment + | |--->AddAugmentedAssignment + | |--->SubAugmentedAssignment + | |--->MulAugmentedAssignment + | |--->DivAugmentedAssignment + | |--->ModAugmentedAssignment + | + |--->CodeBlock + | + | + |--->Token + |--->Attribute + |--->For + |--->String + | |--->QuotedString + | |--->Comment + |--->Type + | |--->IntBaseType + | | |--->_SizedIntType + | | |--->SignedIntType + | | |--->UnsignedIntType + | |--->FloatBaseType + | |--->FloatType + | |--->ComplexBaseType + | |--->ComplexType + |--->Node + | |--->Variable + | | |---> Pointer + | |--->FunctionPrototype + | |--->FunctionDefinition + |--->Element + |--->Declaration + |--->While + |--->Scope + |--->Stream + |--->Print + |--->FunctionCall + |--->BreakToken + |--->ContinueToken + |--->NoneToken + |--->Return + + +Predefined types +---------------- + +A number of ``Type`` instances are provided in the ``sympy.codegen.ast`` module +for convenience. Perhaps the two most common ones for code-generation (of numeric +codes) are ``float32`` and ``float64`` (known as single and double precision respectively). +There are also precision generic versions of Types (for which the codeprinters selects the +underlying data type at time of printing): ``real``, ``integer``, ``complex_``, ``bool_``. + +The other ``Type`` instances defined are: + +- ``intc``: Integer type used by C's "int". +- ``intp``: Integer type used by C's "unsigned". +- ``int8``, ``int16``, ``int32``, ``int64``: n-bit integers. +- ``uint8``, ``uint16``, ``uint32``, ``uint64``: n-bit unsigned integers. +- ``float80``: known as "extended precision" on modern x86/amd64 hardware. +- ``complex64``: Complex number represented by two ``float32`` numbers +- ``complex128``: Complex number represented by two ``float64`` numbers + +Using the nodes +--------------- + +It is possible to construct simple algorithms using the AST nodes. Let's construct a loop applying +Newton's method:: + + >>> from sympy import symbols, cos + >>> from sympy.codegen.ast import While, Assignment, aug_assign, Print, QuotedString + >>> t, dx, x = symbols('tol delta val') + >>> expr = cos(x) - x**3 + >>> whl = While(abs(dx) > t, [ + ... Assignment(dx, -expr/expr.diff(x)), + ... aug_assign(x, '+', dx), + ... Print([x]) + ... ]) + >>> from sympy import pycode + >>> py_str = pycode(whl) + >>> print(py_str) + while (abs(delta) > tol): + delta = (val**3 - math.cos(val))/(-3*val**2 - math.sin(val)) + val += delta + print(val) + >>> import math + >>> tol, val, delta = 1e-5, 0.5, float('inf') + >>> exec(py_str) + 1.1121416371 + 0.909672693737 + 0.867263818209 + 0.865477135298 + 0.865474033111 + >>> print('%3.1g' % (math.cos(val) - val**3)) + -3e-11 + +If we want to generate Fortran code for the same while loop we simple call ``fcode``:: + + >>> from sympy import fcode + >>> print(fcode(whl, standard=2003, source_format='free')) + do while (abs(delta) > tol) + delta = (val**3 - cos(val))/(-3*val**2 - sin(val)) + val = val + delta + print *, val + end do + +There is a function constructing a loop (or a complete function) like this in +:mod:`sympy.codegen.algorithms`. + +""" + +from __future__ import annotations +from typing import Any + +from collections import defaultdict + +from sympy.core.relational import (Ge, Gt, Le, Lt) +from sympy.core import Symbol, Tuple, Dummy +from sympy.core.basic import Basic +from sympy.core.expr import Expr, Atom +from sympy.core.numbers import Float, Integer, oo +from sympy.core.sympify import _sympify, sympify, SympifyError +from sympy.utilities.iterables import (iterable, topological_sort, + numbered_symbols, filter_symbols) + + +def _mk_Tuple(args): + """ + Create a SymPy Tuple object from an iterable, converting Python strings to + AST strings. + + Parameters + ========== + + args: iterable + Arguments to :class:`sympy.Tuple`. + + Returns + ======= + + sympy.Tuple + """ + args = [String(arg) if isinstance(arg, str) else arg for arg in args] + return Tuple(*args) + + +class CodegenAST(Basic): + __slots__ = () + + +class Token(CodegenAST): + """ Base class for the AST types. + + Explanation + =========== + + Defining fields are set in ``_fields``. Attributes (defined in _fields) + are only allowed to contain instances of Basic (unless atomic, see + ``String``). The arguments to ``__new__()`` correspond to the attributes in + the order defined in ``_fields`. The ``defaults`` class attribute is a + dictionary mapping attribute names to their default values. + + Subclasses should not need to override the ``__new__()`` method. They may + define a class or static method named ``_construct_`` for each + attribute to process the value passed to ``__new__()``. Attributes listed + in the class attribute ``not_in_args`` are not passed to :class:`~.Basic`. + """ + + __slots__: tuple[str, ...] = () + _fields = __slots__ + defaults: dict[str, Any] = {} + not_in_args: list[str] = [] + indented_args = ['body'] + + @property + def is_Atom(self): + return len(self._fields) == 0 + + @classmethod + def _get_constructor(cls, attr): + """ Get the constructor function for an attribute by name. """ + return getattr(cls, '_construct_%s' % attr, lambda x: x) + + @classmethod + def _construct(cls, attr, arg): + """ Construct an attribute value from argument passed to ``__new__()``. """ + # arg may be ``NoneToken()``, so comparison is done using == instead of ``is`` operator + if arg == None: + return cls.defaults.get(attr, none) + else: + if isinstance(arg, Dummy): # SymPy's replace uses Dummy instances + return arg + else: + return cls._get_constructor(attr)(arg) + + def __new__(cls, *args, **kwargs): + # Pass through existing instances when given as sole argument + if len(args) == 1 and not kwargs and isinstance(args[0], cls): + return args[0] + + if len(args) > len(cls._fields): + raise ValueError("Too many arguments (%d), expected at most %d" % (len(args), len(cls._fields))) + + attrvals = [] + + # Process positional arguments + for attrname, argval in zip(cls._fields, args): + if attrname in kwargs: + raise TypeError('Got multiple values for attribute %r' % attrname) + + attrvals.append(cls._construct(attrname, argval)) + + # Process keyword arguments + for attrname in cls._fields[len(args):]: + if attrname in kwargs: + argval = kwargs.pop(attrname) + + elif attrname in cls.defaults: + argval = cls.defaults[attrname] + + else: + raise TypeError('No value for %r given and attribute has no default' % attrname) + + attrvals.append(cls._construct(attrname, argval)) + + if kwargs: + raise ValueError("Unknown keyword arguments: %s" % ' '.join(kwargs)) + + # Parent constructor + basic_args = [ + val for attr, val in zip(cls._fields, attrvals) + if attr not in cls.not_in_args + ] + obj = CodegenAST.__new__(cls, *basic_args) + + # Set attributes + for attr, arg in zip(cls._fields, attrvals): + setattr(obj, attr, arg) + + return obj + + def __eq__(self, other): + if not isinstance(other, self.__class__): + return False + for attr in self._fields: + if getattr(self, attr) != getattr(other, attr): + return False + return True + + def _hashable_content(self): + return tuple([getattr(self, attr) for attr in self._fields]) + + def __hash__(self): + return super().__hash__() + + def _joiner(self, k, indent_level): + return (',\n' + ' '*indent_level) if k in self.indented_args else ', ' + + def _indented(self, printer, k, v, *args, **kwargs): + il = printer._context['indent_level'] + def _print(arg): + if isinstance(arg, Token): + return printer._print(arg, *args, joiner=self._joiner(k, il), **kwargs) + else: + return printer._print(arg, *args, **kwargs) + + if isinstance(v, Tuple): + joined = self._joiner(k, il).join([_print(arg) for arg in v.args]) + if k in self.indented_args: + return '(\n' + ' '*il + joined + ',\n' + ' '*(il - 4) + ')' + else: + return ('({0},)' if len(v.args) == 1 else '({0})').format(joined) + else: + return _print(v) + + def _sympyrepr(self, printer, *args, joiner=', ', **kwargs): + from sympy.printing.printer import printer_context + exclude = kwargs.get('exclude', ()) + values = [getattr(self, k) for k in self._fields] + indent_level = printer._context.get('indent_level', 0) + + arg_reprs = [] + + for i, (attr, value) in enumerate(zip(self._fields, values)): + if attr in exclude: + continue + + # Skip attributes which have the default value + if attr in self.defaults and value == self.defaults[attr]: + continue + + ilvl = indent_level + 4 if attr in self.indented_args else 0 + with printer_context(printer, indent_level=ilvl): + indented = self._indented(printer, attr, value, *args, **kwargs) + arg_reprs.append(('{1}' if i == 0 else '{0}={1}').format(attr, indented.lstrip())) + + return "{}({})".format(self.__class__.__name__, joiner.join(arg_reprs)) + + _sympystr = _sympyrepr + + def __repr__(self): # sympy.core.Basic.__repr__ uses sstr + from sympy.printing import srepr + return srepr(self) + + def kwargs(self, exclude=(), apply=None): + """ Get instance's attributes as dict of keyword arguments. + + Parameters + ========== + + exclude : collection of str + Collection of keywords to exclude. + + apply : callable, optional + Function to apply to all values. + """ + kwargs = {k: getattr(self, k) for k in self._fields if k not in exclude} + if apply is not None: + return {k: apply(v) for k, v in kwargs.items()} + else: + return kwargs + +class BreakToken(Token): + """ Represents 'break' in C/Python ('exit' in Fortran). + + Use the premade instance ``break_`` or instantiate manually. + + Examples + ======== + + >>> from sympy import ccode, fcode + >>> from sympy.codegen.ast import break_ + >>> ccode(break_) + 'break' + >>> fcode(break_, source_format='free') + 'exit' + """ + +break_ = BreakToken() + + +class ContinueToken(Token): + """ Represents 'continue' in C/Python ('cycle' in Fortran) + + Use the premade instance ``continue_`` or instantiate manually. + + Examples + ======== + + >>> from sympy import ccode, fcode + >>> from sympy.codegen.ast import continue_ + >>> ccode(continue_) + 'continue' + >>> fcode(continue_, source_format='free') + 'cycle' + """ + +continue_ = ContinueToken() + +class NoneToken(Token): + """ The AST equivalence of Python's NoneType + + The corresponding instance of Python's ``None`` is ``none``. + + Examples + ======== + + >>> from sympy.codegen.ast import none, Variable + >>> from sympy import pycode + >>> print(pycode(Variable('x').as_Declaration(value=none))) + x = None + + """ + def __eq__(self, other): + return other is None or isinstance(other, NoneToken) + + def _hashable_content(self): + return () + + def __hash__(self): + return super().__hash__() + + +none = NoneToken() + + +class AssignmentBase(CodegenAST): + """ Abstract base class for Assignment and AugmentedAssignment. + + Attributes: + =========== + + op : str + Symbol for assignment operator, e.g. "=", "+=", etc. + """ + + def __new__(cls, lhs, rhs): + lhs = _sympify(lhs) + rhs = _sympify(rhs) + + cls._check_args(lhs, rhs) + + return super().__new__(cls, lhs, rhs) + + @property + def lhs(self): + return self.args[0] + + @property + def rhs(self): + return self.args[1] + + @classmethod + def _check_args(cls, lhs, rhs): + """ Check arguments to __new__ and raise exception if any problems found. + + Derived classes may wish to override this. + """ + from sympy.matrices.expressions.matexpr import ( + MatrixElement, MatrixSymbol) + from sympy.tensor.indexed import Indexed + from sympy.tensor.array.expressions import ArrayElement + + # Tuple of things that can be on the lhs of an assignment + assignable = (Symbol, MatrixSymbol, MatrixElement, Indexed, Element, Variable, + ArrayElement) + if not isinstance(lhs, assignable): + raise TypeError("Cannot assign to lhs of type %s." % type(lhs)) + + # Indexed types implement shape, but don't define it until later. This + # causes issues in assignment validation. For now, matrices are defined + # as anything with a shape that is not an Indexed + lhs_is_mat = hasattr(lhs, 'shape') and not isinstance(lhs, Indexed) + rhs_is_mat = hasattr(rhs, 'shape') and not isinstance(rhs, Indexed) + + # If lhs and rhs have same structure, then this assignment is ok + if lhs_is_mat: + if not rhs_is_mat: + raise ValueError("Cannot assign a scalar to a matrix.") + elif lhs.shape != rhs.shape: + raise ValueError("Dimensions of lhs and rhs do not align.") + elif rhs_is_mat and not lhs_is_mat: + raise ValueError("Cannot assign a matrix to a scalar.") + + +class Assignment(AssignmentBase): + """ + Represents variable assignment for code generation. + + Parameters + ========== + + lhs : Expr + SymPy object representing the lhs of the expression. These should be + singular objects, such as one would use in writing code. Notable types + include Symbol, MatrixSymbol, MatrixElement, and Indexed. Types that + subclass these types are also supported. + + rhs : Expr + SymPy object representing the rhs of the expression. This can be any + type, provided its shape corresponds to that of the lhs. For example, + a Matrix type can be assigned to MatrixSymbol, but not to Symbol, as + the dimensions will not align. + + Examples + ======== + + >>> from sympy import symbols, MatrixSymbol, Matrix + >>> from sympy.codegen.ast import Assignment + >>> x, y, z = symbols('x, y, z') + >>> Assignment(x, y) + Assignment(x, y) + >>> Assignment(x, 0) + Assignment(x, 0) + >>> A = MatrixSymbol('A', 1, 3) + >>> mat = Matrix([x, y, z]).T + >>> Assignment(A, mat) + Assignment(A, Matrix([[x, y, z]])) + >>> Assignment(A[0, 1], x) + Assignment(A[0, 1], x) + """ + + op = ':=' + + +class AugmentedAssignment(AssignmentBase): + """ + Base class for augmented assignments. + + Attributes: + =========== + + binop : str + Symbol for binary operation being applied in the assignment, such as "+", + "*", etc. + """ + binop = None # type: str + + @property + def op(self): + return self.binop + '=' + + +class AddAugmentedAssignment(AugmentedAssignment): + binop = '+' + + +class SubAugmentedAssignment(AugmentedAssignment): + binop = '-' + + +class MulAugmentedAssignment(AugmentedAssignment): + binop = '*' + + +class DivAugmentedAssignment(AugmentedAssignment): + binop = '/' + + +class ModAugmentedAssignment(AugmentedAssignment): + binop = '%' + + +# Mapping from binary op strings to AugmentedAssignment subclasses +augassign_classes = { + cls.binop: cls for cls in [ + AddAugmentedAssignment, SubAugmentedAssignment, MulAugmentedAssignment, + DivAugmentedAssignment, ModAugmentedAssignment + ] +} + + +def aug_assign(lhs, op, rhs): + """ + Create 'lhs op= rhs'. + + Explanation + =========== + + Represents augmented variable assignment for code generation. This is a + convenience function. You can also use the AugmentedAssignment classes + directly, like AddAugmentedAssignment(x, y). + + Parameters + ========== + + lhs : Expr + SymPy object representing the lhs of the expression. These should be + singular objects, such as one would use in writing code. Notable types + include Symbol, MatrixSymbol, MatrixElement, and Indexed. Types that + subclass these types are also supported. + + op : str + Operator (+, -, /, \\*, %). + + rhs : Expr + SymPy object representing the rhs of the expression. This can be any + type, provided its shape corresponds to that of the lhs. For example, + a Matrix type can be assigned to MatrixSymbol, but not to Symbol, as + the dimensions will not align. + + Examples + ======== + + >>> from sympy import symbols + >>> from sympy.codegen.ast import aug_assign + >>> x, y = symbols('x, y') + >>> aug_assign(x, '+', y) + AddAugmentedAssignment(x, y) + """ + if op not in augassign_classes: + raise ValueError("Unrecognized operator %s" % op) + return augassign_classes[op](lhs, rhs) + + +class CodeBlock(CodegenAST): + """ + Represents a block of code. + + Explanation + =========== + + For now only assignments are supported. This restriction will be lifted in + the future. + + Useful attributes on this object are: + + ``left_hand_sides``: + Tuple of left-hand sides of assignments, in order. + ``left_hand_sides``: + Tuple of right-hand sides of assignments, in order. + ``free_symbols``: Free symbols of the expressions in the right-hand sides + which do not appear in the left-hand side of an assignment. + + Useful methods on this object are: + + ``topological_sort``: + Class method. Return a CodeBlock with assignments + sorted so that variables are assigned before they + are used. + ``cse``: + Return a new CodeBlock with common subexpressions eliminated and + pulled out as assignments. + + Examples + ======== + + >>> from sympy import symbols, ccode + >>> from sympy.codegen.ast import CodeBlock, Assignment + >>> x, y = symbols('x y') + >>> c = CodeBlock(Assignment(x, 1), Assignment(y, x + 1)) + >>> print(ccode(c)) + x = 1; + y = x + 1; + + """ + def __new__(cls, *args): + left_hand_sides = [] + right_hand_sides = [] + for i in args: + if isinstance(i, Assignment): + lhs, rhs = i.args + left_hand_sides.append(lhs) + right_hand_sides.append(rhs) + + obj = CodegenAST.__new__(cls, *args) + + obj.left_hand_sides = Tuple(*left_hand_sides) + obj.right_hand_sides = Tuple(*right_hand_sides) + return obj + + def __iter__(self): + return iter(self.args) + + def _sympyrepr(self, printer, *args, **kwargs): + il = printer._context.get('indent_level', 0) + joiner = ',\n' + ' '*il + joined = joiner.join(map(printer._print, self.args)) + return ('{}(\n'.format(' '*(il-4) + self.__class__.__name__,) + + ' '*il + joined + '\n' + ' '*(il - 4) + ')') + + _sympystr = _sympyrepr + + @property + def free_symbols(self): + return super().free_symbols - set(self.left_hand_sides) + + @classmethod + def topological_sort(cls, assignments): + """ + Return a CodeBlock with topologically sorted assignments so that + variables are assigned before they are used. + + Examples + ======== + + The existing order of assignments is preserved as much as possible. + + This function assumes that variables are assigned to only once. + + This is a class constructor so that the default constructor for + CodeBlock can error when variables are used before they are assigned. + + >>> from sympy import symbols + >>> from sympy.codegen.ast import CodeBlock, Assignment + >>> x, y, z = symbols('x y z') + + >>> assignments = [ + ... Assignment(x, y + z), + ... Assignment(y, z + 1), + ... Assignment(z, 2), + ... ] + >>> CodeBlock.topological_sort(assignments) + CodeBlock( + Assignment(z, 2), + Assignment(y, z + 1), + Assignment(x, y + z) + ) + + """ + + if not all(isinstance(i, Assignment) for i in assignments): + # Will support more things later + raise NotImplementedError("CodeBlock.topological_sort only supports Assignments") + + if any(isinstance(i, AugmentedAssignment) for i in assignments): + raise NotImplementedError("CodeBlock.topological_sort does not yet work with AugmentedAssignments") + + # Create a graph where the nodes are assignments and there is a directed edge + # between nodes that use a variable and nodes that assign that + # variable, like + + # [(x := 1, y := x + 1), (x := 1, z := y + z), (y := x + 1, z := y + z)] + + # If we then topologically sort these nodes, they will be in + # assignment order, like + + # x := 1 + # y := x + 1 + # z := y + z + + # A = The nodes + # + # enumerate keeps nodes in the same order they are already in if + # possible. It will also allow us to handle duplicate assignments to + # the same variable when those are implemented. + A = list(enumerate(assignments)) + + # var_map = {variable: [nodes for which this variable is assigned to]} + # like {x: [(1, x := y + z), (4, x := 2 * w)], ...} + var_map = defaultdict(list) + for node in A: + i, a = node + var_map[a.lhs].append(node) + + # E = Edges in the graph + E = [] + for dst_node in A: + i, a = dst_node + for s in a.rhs.free_symbols: + for src_node in var_map[s]: + E.append((src_node, dst_node)) + + ordered_assignments = topological_sort([A, E]) + + # De-enumerate the result + return cls(*[a for i, a in ordered_assignments]) + + def cse(self, symbols=None, optimizations=None, postprocess=None, + order='canonical'): + """ + Return a new code block with common subexpressions eliminated. + + Explanation + =========== + + See the docstring of :func:`sympy.simplify.cse_main.cse` for more + information. + + Examples + ======== + + >>> from sympy import symbols, sin + >>> from sympy.codegen.ast import CodeBlock, Assignment + >>> x, y, z = symbols('x y z') + + >>> c = CodeBlock( + ... Assignment(x, 1), + ... Assignment(y, sin(x) + 1), + ... Assignment(z, sin(x) - 1), + ... ) + ... + >>> c.cse() + CodeBlock( + Assignment(x, 1), + Assignment(x0, sin(x)), + Assignment(y, x0 + 1), + Assignment(z, x0 - 1) + ) + + """ + from sympy.simplify.cse_main import cse + + # Check that the CodeBlock only contains assignments to unique variables + if not all(isinstance(i, Assignment) for i in self.args): + # Will support more things later + raise NotImplementedError("CodeBlock.cse only supports Assignments") + + if any(isinstance(i, AugmentedAssignment) for i in self.args): + raise NotImplementedError("CodeBlock.cse does not yet work with AugmentedAssignments") + + for i, lhs in enumerate(self.left_hand_sides): + if lhs in self.left_hand_sides[:i]: + raise NotImplementedError("Duplicate assignments to the same " + "variable are not yet supported (%s)" % lhs) + + # Ensure new symbols for subexpressions do not conflict with existing + existing_symbols = self.atoms(Symbol) + if symbols is None: + symbols = numbered_symbols() + symbols = filter_symbols(symbols, existing_symbols) + + replacements, reduced_exprs = cse(list(self.right_hand_sides), + symbols=symbols, optimizations=optimizations, postprocess=postprocess, + order=order) + + new_block = [Assignment(var, expr) for var, expr in + zip(self.left_hand_sides, reduced_exprs)] + new_assignments = [Assignment(var, expr) for var, expr in replacements] + return self.topological_sort(new_assignments + new_block) + + +class For(Token): + """Represents a 'for-loop' in the code. + + Expressions are of the form: + "for target in iter: + body..." + + Parameters + ========== + + target : symbol + iter : iterable + body : CodeBlock or iterable +! When passed an iterable it is used to instantiate a CodeBlock. + + Examples + ======== + + >>> from sympy import symbols, Range + >>> from sympy.codegen.ast import aug_assign, For + >>> x, i, j, k = symbols('x i j k') + >>> for_i = For(i, Range(10), [aug_assign(x, '+', i*j*k)]) + >>> for_i # doctest: -NORMALIZE_WHITESPACE + For(i, iterable=Range(0, 10, 1), body=CodeBlock( + AddAugmentedAssignment(x, i*j*k) + )) + >>> for_ji = For(j, Range(7), [for_i]) + >>> for_ji # doctest: -NORMALIZE_WHITESPACE + For(j, iterable=Range(0, 7, 1), body=CodeBlock( + For(i, iterable=Range(0, 10, 1), body=CodeBlock( + AddAugmentedAssignment(x, i*j*k) + )) + )) + >>> for_kji =For(k, Range(5), [for_ji]) + >>> for_kji # doctest: -NORMALIZE_WHITESPACE + For(k, iterable=Range(0, 5, 1), body=CodeBlock( + For(j, iterable=Range(0, 7, 1), body=CodeBlock( + For(i, iterable=Range(0, 10, 1), body=CodeBlock( + AddAugmentedAssignment(x, i*j*k) + )) + )) + )) + """ + __slots__ = _fields = ('target', 'iterable', 'body') + _construct_target = staticmethod(_sympify) + + @classmethod + def _construct_body(cls, itr): + if isinstance(itr, CodeBlock): + return itr + else: + return CodeBlock(*itr) + + @classmethod + def _construct_iterable(cls, itr): + if not iterable(itr): + raise TypeError("iterable must be an iterable") + if isinstance(itr, list): # _sympify errors on lists because they are mutable + itr = tuple(itr) + return _sympify(itr) + + +class String(Atom, Token): + """ SymPy object representing a string. + + Atomic object which is not an expression (as opposed to Symbol). + + Parameters + ========== + + text : str + + Examples + ======== + + >>> from sympy.codegen.ast import String + >>> f = String('foo') + >>> f + foo + >>> str(f) + 'foo' + >>> f.text + 'foo' + >>> print(repr(f)) + String('foo') + + """ + __slots__ = _fields = ('text',) + not_in_args = ['text'] + is_Atom = True + + @classmethod + def _construct_text(cls, text): + if not isinstance(text, str): + raise TypeError("Argument text is not a string type.") + return text + + def _sympystr(self, printer, *args, **kwargs): + return self.text + + def kwargs(self, exclude = (), apply = None): + return {} + + #to be removed when Atom is given a suitable func + @property + def func(self): + return lambda: self + + def _latex(self, printer): + from sympy.printing.latex import latex_escape + return r'\texttt{{"{}"}}'.format(latex_escape(self.text)) + +class QuotedString(String): + """ Represents a string which should be printed with quotes. """ + +class Comment(String): + """ Represents a comment. """ + +class Node(Token): + """ Subclass of Token, carrying the attribute 'attrs' (Tuple) + + Examples + ======== + + >>> from sympy.codegen.ast import Node, value_const, pointer_const + >>> n1 = Node([value_const]) + >>> n1.attr_params('value_const') # get the parameters of attribute (by name) + () + >>> from sympy.codegen.fnodes import dimension + >>> n2 = Node([value_const, dimension(5, 3)]) + >>> n2.attr_params(value_const) # get the parameters of attribute (by Attribute instance) + () + >>> n2.attr_params('dimension') # get the parameters of attribute (by name) + (5, 3) + >>> n2.attr_params(pointer_const) is None + True + + """ + + __slots__: tuple[str, ...] = ('attrs',) + _fields = __slots__ + + defaults: dict[str, Any] = {'attrs': Tuple()} + + _construct_attrs = staticmethod(_mk_Tuple) + + def attr_params(self, looking_for): + """ Returns the parameters of the Attribute with name ``looking_for`` in self.attrs """ + for attr in self.attrs: + if str(attr.name) == str(looking_for): + return attr.parameters + + +class Type(Token): + """ Represents a type. + + Explanation + =========== + + The naming is a super-set of NumPy naming. Type has a classmethod + ``from_expr`` which offer type deduction. It also has a method + ``cast_check`` which casts the argument to its type, possibly raising an + exception if rounding error is not within tolerances, or if the value is not + representable by the underlying data type (e.g. unsigned integers). + + Parameters + ========== + + name : str + Name of the type, e.g. ``object``, ``int16``, ``float16`` (where the latter two + would use the ``Type`` sub-classes ``IntType`` and ``FloatType`` respectively). + If a ``Type`` instance is given, the said instance is returned. + + Examples + ======== + + >>> from sympy.codegen.ast import Type + >>> t = Type.from_expr(42) + >>> t + integer + >>> print(repr(t)) + IntBaseType(String('integer')) + >>> from sympy.codegen.ast import uint8 + >>> uint8.cast_check(-1) # doctest: +ELLIPSIS + Traceback (most recent call last): + ... + ValueError: Minimum value for data type bigger than new value. + >>> from sympy.codegen.ast import float32 + >>> v6 = 0.123456 + >>> float32.cast_check(v6) + 0.123456 + >>> v10 = 12345.67894 + >>> float32.cast_check(v10) # doctest: +ELLIPSIS + Traceback (most recent call last): + ... + ValueError: Casting gives a significantly different value. + >>> boost_mp50 = Type('boost::multiprecision::cpp_dec_float_50') + >>> from sympy import cxxcode + >>> from sympy.codegen.ast import Declaration, Variable + >>> cxxcode(Declaration(Variable('x', type=boost_mp50))) + 'boost::multiprecision::cpp_dec_float_50 x' + + References + ========== + + .. [1] https://numpy.org/doc/stable/user/basics.types.html + + """ + __slots__: tuple[str, ...] = ('name',) + _fields = __slots__ + + _construct_name = String + + def _sympystr(self, printer, *args, **kwargs): + return str(self.name) + + @classmethod + def from_expr(cls, expr): + """ Deduces type from an expression or a ``Symbol``. + + Parameters + ========== + + expr : number or SymPy object + The type will be deduced from type or properties. + + Examples + ======== + + >>> from sympy.codegen.ast import Type, integer, complex_ + >>> Type.from_expr(2) == integer + True + >>> from sympy import Symbol + >>> Type.from_expr(Symbol('z', complex=True)) == complex_ + True + >>> Type.from_expr(sum) # doctest: +ELLIPSIS + Traceback (most recent call last): + ... + ValueError: Could not deduce type from expr. + + Raises + ====== + + ValueError when type deduction fails. + + """ + if isinstance(expr, (float, Float)): + return real + if isinstance(expr, (int, Integer)) or getattr(expr, 'is_integer', False): + return integer + if getattr(expr, 'is_real', False): + return real + if isinstance(expr, complex) or getattr(expr, 'is_complex', False): + return complex_ + if isinstance(expr, bool) or getattr(expr, 'is_Relational', False): + return bool_ + else: + raise ValueError("Could not deduce type from expr.") + + def _check(self, value): + pass + + def cast_check(self, value, rtol=None, atol=0, precision_targets=None): + """ Casts a value to the data type of the instance. + + Parameters + ========== + + value : number + rtol : floating point number + Relative tolerance. (will be deduced if not given). + atol : floating point number + Absolute tolerance (in addition to ``rtol``). + type_aliases : dict + Maps substitutions for Type, e.g. {integer: int64, real: float32} + + Examples + ======== + + >>> from sympy.codegen.ast import integer, float32, int8 + >>> integer.cast_check(3.0) == 3 + True + >>> float32.cast_check(1e-40) # doctest: +ELLIPSIS + Traceback (most recent call last): + ... + ValueError: Minimum value for data type bigger than new value. + >>> int8.cast_check(256) # doctest: +ELLIPSIS + Traceback (most recent call last): + ... + ValueError: Maximum value for data type smaller than new value. + >>> v10 = 12345.67894 + >>> float32.cast_check(v10) # doctest: +ELLIPSIS + Traceback (most recent call last): + ... + ValueError: Casting gives a significantly different value. + >>> from sympy.codegen.ast import float64 + >>> float64.cast_check(v10) + 12345.67894 + >>> from sympy import Float + >>> v18 = Float('0.123456789012345646') + >>> float64.cast_check(v18) + Traceback (most recent call last): + ... + ValueError: Casting gives a significantly different value. + >>> from sympy.codegen.ast import float80 + >>> float80.cast_check(v18) + 0.123456789012345649 + + """ + val = sympify(value) + + ten = Integer(10) + exp10 = getattr(self, 'decimal_dig', None) + + if rtol is None: + rtol = 1e-15 if exp10 is None else 2.0*ten**(-exp10) + + def tol(num): + return atol + rtol*abs(num) + + new_val = self.cast_nocheck(value) + self._check(new_val) + + delta = new_val - val + if abs(delta) > tol(val): # rounding, e.g. int(3.5) != 3.5 + raise ValueError("Casting gives a significantly different value.") + + return new_val + + def _latex(self, printer): + from sympy.printing.latex import latex_escape + type_name = latex_escape(self.__class__.__name__) + name = latex_escape(self.name.text) + return r"\text{{{}}}\left(\texttt{{{}}}\right)".format(type_name, name) + + +class IntBaseType(Type): + """ Integer base type, contains no size information. """ + __slots__ = () + cast_nocheck = lambda self, i: Integer(int(i)) + + +class _SizedIntType(IntBaseType): + __slots__ = ('nbits',) + _fields = Type._fields + __slots__ + + _construct_nbits = Integer + + def _check(self, value): + if value < self.min: + raise ValueError("Value is too small: %d < %d" % (value, self.min)) + if value > self.max: + raise ValueError("Value is too big: %d > %d" % (value, self.max)) + + +class SignedIntType(_SizedIntType): + """ Represents a signed integer type. """ + __slots__ = () + @property + def min(self): + return -2**(self.nbits-1) + + @property + def max(self): + return 2**(self.nbits-1) - 1 + + +class UnsignedIntType(_SizedIntType): + """ Represents an unsigned integer type. """ + __slots__ = () + @property + def min(self): + return 0 + + @property + def max(self): + return 2**self.nbits - 1 + +two = Integer(2) + +class FloatBaseType(Type): + """ Represents a floating point number type. """ + __slots__ = () + cast_nocheck = Float + +class FloatType(FloatBaseType): + """ Represents a floating point type with fixed bit width. + + Base 2 & one sign bit is assumed. + + Parameters + ========== + + name : str + Name of the type. + nbits : integer + Number of bits used (storage). + nmant : integer + Number of bits used to represent the mantissa. + nexp : integer + Number of bits used to represent the mantissa. + + Examples + ======== + + >>> from sympy import S + >>> from sympy.codegen.ast import FloatType + >>> half_precision = FloatType('f16', nbits=16, nmant=10, nexp=5) + >>> half_precision.max + 65504 + >>> half_precision.tiny == S(2)**-14 + True + >>> half_precision.eps == S(2)**-10 + True + >>> half_precision.dig == 3 + True + >>> half_precision.decimal_dig == 5 + True + >>> half_precision.cast_check(1.0) + 1.0 + >>> half_precision.cast_check(1e5) # doctest: +ELLIPSIS + Traceback (most recent call last): + ... + ValueError: Maximum value for data type smaller than new value. + """ + + __slots__ = ('nbits', 'nmant', 'nexp',) + _fields = Type._fields + __slots__ + + _construct_nbits = _construct_nmant = _construct_nexp = Integer + + + @property + def max_exponent(self): + """ The largest positive number n, such that 2**(n - 1) is a representable finite value. """ + # cf. C++'s ``std::numeric_limits::max_exponent`` + return two**(self.nexp - 1) + + @property + def min_exponent(self): + """ The lowest negative number n, such that 2**(n - 1) is a valid normalized number. """ + # cf. C++'s ``std::numeric_limits::min_exponent`` + return 3 - self.max_exponent + + @property + def max(self): + """ Maximum value representable. """ + return (1 - two**-(self.nmant+1))*two**self.max_exponent + + @property + def tiny(self): + """ The minimum positive normalized value. """ + # See C macros: FLT_MIN, DBL_MIN, LDBL_MIN + # or C++'s ``std::numeric_limits::min`` + # or numpy.finfo(dtype).tiny + return two**(self.min_exponent - 1) + + + @property + def eps(self): + """ Difference between 1.0 and the next representable value. """ + return two**(-self.nmant) + + @property + def dig(self): + """ Number of decimal digits that are guaranteed to be preserved in text. + + When converting text -> float -> text, you are guaranteed that at least ``dig`` + number of digits are preserved with respect to rounding or overflow. + """ + from sympy.functions import floor, log + return floor(self.nmant * log(2)/log(10)) + + @property + def decimal_dig(self): + """ Number of digits needed to store & load without loss. + + Explanation + =========== + + Number of decimal digits needed to guarantee that two consecutive conversions + (float -> text -> float) to be idempotent. This is useful when one do not want + to loose precision due to rounding errors when storing a floating point value + as text. + """ + from sympy.functions import ceiling, log + return ceiling((self.nmant + 1) * log(2)/log(10) + 1) + + def cast_nocheck(self, value): + """ Casts without checking if out of bounds or subnormal. """ + if value == oo: # float(oo) or oo + return float(oo) + elif value == -oo: # float(-oo) or -oo + return float(-oo) + return Float(str(sympify(value).evalf(self.decimal_dig)), self.decimal_dig) + + def _check(self, value): + if value < -self.max: + raise ValueError("Value is too small: %d < %d" % (value, -self.max)) + if value > self.max: + raise ValueError("Value is too big: %d > %d" % (value, self.max)) + if abs(value) < self.tiny: + raise ValueError("Smallest (absolute) value for data type bigger than new value.") + +class ComplexBaseType(FloatBaseType): + + __slots__ = () + + def cast_nocheck(self, value): + """ Casts without checking if out of bounds or subnormal. """ + from sympy.functions import re, im + return ( + super().cast_nocheck(re(value)) + + super().cast_nocheck(im(value))*1j + ) + + def _check(self, value): + from sympy.functions import re, im + super()._check(re(value)) + super()._check(im(value)) + + +class ComplexType(ComplexBaseType, FloatType): + """ Represents a complex floating point number. """ + __slots__ = () + + +# NumPy types: +intc = IntBaseType('intc') +intp = IntBaseType('intp') +int8 = SignedIntType('int8', 8) +int16 = SignedIntType('int16', 16) +int32 = SignedIntType('int32', 32) +int64 = SignedIntType('int64', 64) +uint8 = UnsignedIntType('uint8', 8) +uint16 = UnsignedIntType('uint16', 16) +uint32 = UnsignedIntType('uint32', 32) +uint64 = UnsignedIntType('uint64', 64) +float16 = FloatType('float16', 16, nexp=5, nmant=10) # IEEE 754 binary16, Half precision +float32 = FloatType('float32', 32, nexp=8, nmant=23) # IEEE 754 binary32, Single precision +float64 = FloatType('float64', 64, nexp=11, nmant=52) # IEEE 754 binary64, Double precision +float80 = FloatType('float80', 80, nexp=15, nmant=63) # x86 extended precision (1 integer part bit), "long double" +float128 = FloatType('float128', 128, nexp=15, nmant=112) # IEEE 754 binary128, Quadruple precision +float256 = FloatType('float256', 256, nexp=19, nmant=236) # IEEE 754 binary256, Octuple precision + +complex64 = ComplexType('complex64', nbits=64, **float32.kwargs(exclude=('name', 'nbits'))) +complex128 = ComplexType('complex128', nbits=128, **float64.kwargs(exclude=('name', 'nbits'))) + +# Generic types (precision may be chosen by code printers): +untyped = Type('untyped') +real = FloatBaseType('real') +integer = IntBaseType('integer') +complex_ = ComplexBaseType('complex') +bool_ = Type('bool') + + +class Attribute(Token): + """ Attribute (possibly parametrized) + + For use with :class:`sympy.codegen.ast.Node` (which takes instances of + ``Attribute`` as ``attrs``). + + Parameters + ========== + + name : str + parameters : Tuple + + Examples + ======== + + >>> from sympy.codegen.ast import Attribute + >>> volatile = Attribute('volatile') + >>> volatile + volatile + >>> print(repr(volatile)) + Attribute(String('volatile')) + >>> a = Attribute('foo', [1, 2, 3]) + >>> a + foo(1, 2, 3) + >>> a.parameters == (1, 2, 3) + True + """ + __slots__ = _fields = ('name', 'parameters') + defaults = {'parameters': Tuple()} + + _construct_name = String + _construct_parameters = staticmethod(_mk_Tuple) + + def _sympystr(self, printer, *args, **kwargs): + result = str(self.name) + if self.parameters: + result += '(%s)' % ', '.join((printer._print( + arg, *args, **kwargs) for arg in self.parameters)) + return result + +value_const = Attribute('value_const') +pointer_const = Attribute('pointer_const') + + +class Variable(Node): + """ Represents a variable. + + Parameters + ========== + + symbol : Symbol + type : Type (optional) + Type of the variable. + attrs : iterable of Attribute instances + Will be stored as a Tuple. + + Examples + ======== + + >>> from sympy import Symbol + >>> from sympy.codegen.ast import Variable, float32, integer + >>> x = Symbol('x') + >>> v = Variable(x, type=float32) + >>> v.attrs + () + >>> v == Variable('x') + False + >>> v == Variable('x', type=float32) + True + >>> v + Variable(x, type=float32) + + One may also construct a ``Variable`` instance with the type deduced from + assumptions about the symbol using the ``deduced`` classmethod: + + >>> i = Symbol('i', integer=True) + >>> v = Variable.deduced(i) + >>> v.type == integer + True + >>> v == Variable('i') + False + >>> from sympy.codegen.ast import value_const + >>> value_const in v.attrs + False + >>> w = Variable('w', attrs=[value_const]) + >>> w + Variable(w, attrs=(value_const,)) + >>> value_const in w.attrs + True + >>> w.as_Declaration(value=42) + Declaration(Variable(w, value=42, attrs=(value_const,))) + + """ + + __slots__ = ('symbol', 'type', 'value') + _fields = __slots__ + Node._fields + + defaults = Node.defaults.copy() + defaults.update({'type': untyped, 'value': none}) + + _construct_symbol = staticmethod(sympify) + _construct_value = staticmethod(sympify) + + @classmethod + def deduced(cls, symbol, value=None, attrs=Tuple(), cast_check=True): + """ Alt. constructor with type deduction from ``Type.from_expr``. + + Deduces type primarily from ``symbol``, secondarily from ``value``. + + Parameters + ========== + + symbol : Symbol + value : expr + (optional) value of the variable. + attrs : iterable of Attribute instances + cast_check : bool + Whether to apply ``Type.cast_check`` on ``value``. + + Examples + ======== + + >>> from sympy import Symbol + >>> from sympy.codegen.ast import Variable, complex_ + >>> n = Symbol('n', integer=True) + >>> str(Variable.deduced(n).type) + 'integer' + >>> x = Symbol('x', real=True) + >>> v = Variable.deduced(x) + >>> v.type + real + >>> z = Symbol('z', complex=True) + >>> Variable.deduced(z).type == complex_ + True + + """ + if isinstance(symbol, Variable): + return symbol + + try: + type_ = Type.from_expr(symbol) + except ValueError: + type_ = Type.from_expr(value) + + if value is not None and cast_check: + value = type_.cast_check(value) + return cls(symbol, type=type_, value=value, attrs=attrs) + + def as_Declaration(self, **kwargs): + """ Convenience method for creating a Declaration instance. + + Explanation + =========== + + If the variable of the Declaration need to wrap a modified + variable keyword arguments may be passed (overriding e.g. + the ``value`` of the Variable instance). + + Examples + ======== + + >>> from sympy.codegen.ast import Variable, NoneToken + >>> x = Variable('x') + >>> decl1 = x.as_Declaration() + >>> # value is special NoneToken() which must be tested with == operator + >>> decl1.variable.value is None # won't work + False + >>> decl1.variable.value == None # not PEP-8 compliant + True + >>> decl1.variable.value == NoneToken() # OK + True + >>> decl2 = x.as_Declaration(value=42.0) + >>> decl2.variable.value == 42.0 + True + + """ + kw = self.kwargs() + kw.update(kwargs) + return Declaration(self.func(**kw)) + + def _relation(self, rhs, op): + try: + rhs = _sympify(rhs) + except SympifyError: + raise TypeError("Invalid comparison %s < %s" % (self, rhs)) + return op(self, rhs, evaluate=False) + + __lt__ = lambda self, other: self._relation(other, Lt) + __le__ = lambda self, other: self._relation(other, Le) + __ge__ = lambda self, other: self._relation(other, Ge) + __gt__ = lambda self, other: self._relation(other, Gt) + +class Pointer(Variable): + """ Represents a pointer. See ``Variable``. + + Examples + ======== + + Can create instances of ``Element``: + + >>> from sympy import Symbol + >>> from sympy.codegen.ast import Pointer + >>> i = Symbol('i', integer=True) + >>> p = Pointer('x') + >>> p[i+1] + Element(x, indices=(i + 1,)) + + """ + __slots__ = () + + def __getitem__(self, key): + try: + return Element(self.symbol, key) + except TypeError: + return Element(self.symbol, (key,)) + + +class Element(Token): + """ Element in (a possibly N-dimensional) array. + + Examples + ======== + + >>> from sympy.codegen.ast import Element + >>> elem = Element('x', 'ijk') + >>> elem.symbol.name == 'x' + True + >>> elem.indices + (i, j, k) + >>> from sympy import ccode + >>> ccode(elem) + 'x[i][j][k]' + >>> ccode(Element('x', 'ijk', strides='lmn', offset='o')) + 'x[i*l + j*m + k*n + o]' + + """ + __slots__ = _fields = ('symbol', 'indices', 'strides', 'offset') + defaults = {'strides': none, 'offset': none} + _construct_symbol = staticmethod(sympify) + _construct_indices = staticmethod(lambda arg: Tuple(*arg)) + _construct_strides = staticmethod(lambda arg: Tuple(*arg)) + _construct_offset = staticmethod(sympify) + + +class Declaration(Token): + """ Represents a variable declaration + + Parameters + ========== + + variable : Variable + + Examples + ======== + + >>> from sympy.codegen.ast import Declaration, NoneToken, untyped + >>> z = Declaration('z') + >>> z.variable.type == untyped + True + >>> # value is special NoneToken() which must be tested with == operator + >>> z.variable.value is None # won't work + False + >>> z.variable.value == None # not PEP-8 compliant + True + >>> z.variable.value == NoneToken() # OK + True + """ + __slots__ = _fields = ('variable',) + _construct_variable = Variable + + +class While(Token): + """ Represents a 'for-loop' in the code. + + Expressions are of the form: + "while condition: + body..." + + Parameters + ========== + + condition : expression convertible to Boolean + body : CodeBlock or iterable + When passed an iterable it is used to instantiate a CodeBlock. + + Examples + ======== + + >>> from sympy import symbols, Gt, Abs + >>> from sympy.codegen import aug_assign, Assignment, While + >>> x, dx = symbols('x dx') + >>> expr = 1 - x**2 + >>> whl = While(Gt(Abs(dx), 1e-9), [ + ... Assignment(dx, -expr/expr.diff(x)), + ... aug_assign(x, '+', dx) + ... ]) + + """ + __slots__ = _fields = ('condition', 'body') + _construct_condition = staticmethod(lambda cond: _sympify(cond)) + + @classmethod + def _construct_body(cls, itr): + if isinstance(itr, CodeBlock): + return itr + else: + return CodeBlock(*itr) + + +class Scope(Token): + """ Represents a scope in the code. + + Parameters + ========== + + body : CodeBlock or iterable + When passed an iterable it is used to instantiate a CodeBlock. + + """ + __slots__ = _fields = ('body',) + + @classmethod + def _construct_body(cls, itr): + if isinstance(itr, CodeBlock): + return itr + else: + return CodeBlock(*itr) + + +class Stream(Token): + """ Represents a stream. + + There are two predefined Stream instances ``stdout`` & ``stderr``. + + Parameters + ========== + + name : str + + Examples + ======== + + >>> from sympy import pycode, Symbol + >>> from sympy.codegen.ast import Print, stderr, QuotedString + >>> print(pycode(Print(['x'], file=stderr))) + print(x, file=sys.stderr) + >>> x = Symbol('x') + >>> print(pycode(Print([QuotedString('x')], file=stderr))) # print literally "x" + print("x", file=sys.stderr) + + """ + __slots__ = _fields = ('name',) + _construct_name = String + +stdout = Stream('stdout') +stderr = Stream('stderr') + + +class Print(Token): + r""" Represents print command in the code. + + Parameters + ========== + + formatstring : str + *args : Basic instances (or convertible to such through sympify) + + Examples + ======== + + >>> from sympy.codegen.ast import Print + >>> from sympy import pycode + >>> print(pycode(Print('x y'.split(), "coordinate: %12.5g %12.5g\\n"))) + print("coordinate: %12.5g %12.5g\n" % (x, y), end="") + + """ + + __slots__ = _fields = ('print_args', 'format_string', 'file') + defaults = {'format_string': none, 'file': none} + + _construct_print_args = staticmethod(_mk_Tuple) + _construct_format_string = QuotedString + _construct_file = Stream + + +class FunctionPrototype(Node): + """ Represents a function prototype + + Allows the user to generate forward declaration in e.g. C/C++. + + Parameters + ========== + + return_type : Type + name : str + parameters: iterable of Variable instances + attrs : iterable of Attribute instances + + Examples + ======== + + >>> from sympy import ccode, symbols + >>> from sympy.codegen.ast import real, FunctionPrototype + >>> x, y = symbols('x y', real=True) + >>> fp = FunctionPrototype(real, 'foo', [x, y]) + >>> ccode(fp) + 'double foo(double x, double y)' + + """ + + __slots__ = ('return_type', 'name', 'parameters') + _fields: tuple[str, ...] = __slots__ + Node._fields + + _construct_return_type = Type + _construct_name = String + + @staticmethod + def _construct_parameters(args): + def _var(arg): + if isinstance(arg, Declaration): + return arg.variable + elif isinstance(arg, Variable): + return arg + else: + return Variable.deduced(arg) + return Tuple(*map(_var, args)) + + @classmethod + def from_FunctionDefinition(cls, func_def): + if not isinstance(func_def, FunctionDefinition): + raise TypeError("func_def is not an instance of FunctionDefinition") + return cls(**func_def.kwargs(exclude=('body',))) + + +class FunctionDefinition(FunctionPrototype): + """ Represents a function definition in the code. + + Parameters + ========== + + return_type : Type + name : str + parameters: iterable of Variable instances + body : CodeBlock or iterable + attrs : iterable of Attribute instances + + Examples + ======== + + >>> from sympy import ccode, symbols + >>> from sympy.codegen.ast import real, FunctionPrototype + >>> x, y = symbols('x y', real=True) + >>> fp = FunctionPrototype(real, 'foo', [x, y]) + >>> ccode(fp) + 'double foo(double x, double y)' + >>> from sympy.codegen.ast import FunctionDefinition, Return + >>> body = [Return(x*y)] + >>> fd = FunctionDefinition.from_FunctionPrototype(fp, body) + >>> print(ccode(fd)) + double foo(double x, double y){ + return x*y; + } + """ + + __slots__ = ('body', ) + _fields = FunctionPrototype._fields[:-1] + __slots__ + Node._fields + + @classmethod + def _construct_body(cls, itr): + if isinstance(itr, CodeBlock): + return itr + else: + return CodeBlock(*itr) + + @classmethod + def from_FunctionPrototype(cls, func_proto, body): + if not isinstance(func_proto, FunctionPrototype): + raise TypeError("func_proto is not an instance of FunctionPrototype") + return cls(body=body, **func_proto.kwargs()) + + +class Return(Token): + """ Represents a return command in the code. + + Parameters + ========== + + return : Basic + + Examples + ======== + + >>> from sympy.codegen.ast import Return + >>> from sympy.printing.pycode import pycode + >>> from sympy import Symbol + >>> x = Symbol('x') + >>> print(pycode(Return(x))) + return x + + """ + __slots__ = _fields = ('return',) + _construct_return=staticmethod(_sympify) + + +class FunctionCall(Token, Expr): + """ Represents a call to a function in the code. + + Parameters + ========== + + name : str + function_args : Tuple + + Examples + ======== + + >>> from sympy.codegen.ast import FunctionCall + >>> from sympy import pycode + >>> fcall = FunctionCall('foo', 'bar baz'.split()) + >>> print(pycode(fcall)) + foo(bar, baz) + + """ + __slots__ = _fields = ('name', 'function_args') + + _construct_name = String + _construct_function_args = staticmethod(lambda args: Tuple(*args)) + + +class Raise(Token): + """ Prints as 'raise ...' in Python, 'throw ...' in C++""" + __slots__ = _fields = ('exception',) + + +class RuntimeError_(Token): + """ Represents 'std::runtime_error' in C++ and 'RuntimeError' in Python. + + Note that the latter is uncommon, and you might want to use e.g. ValueError. + """ + __slots__ = _fields = ('message',) + _construct_message = String diff --git a/lib/python3.12/site-packages/sympy/codegen/cfunctions.py b/lib/python3.12/site-packages/sympy/codegen/cfunctions.py new file mode 100644 index 0000000000000000000000000000000000000000..2eb3176ea8cd0967de9aa6d93b54f0c1c1354755 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/codegen/cfunctions.py @@ -0,0 +1,536 @@ +""" +This module contains SymPy functions mathcin corresponding to special math functions in the +C standard library (since C99, also available in C++11). + +The functions defined in this module allows the user to express functions such as ``expm1`` +as a SymPy function for symbolic manipulation. + +""" +from sympy.core.function import ArgumentIndexError, Function +from sympy.core.numbers import Rational +from sympy.core.power import Pow +from sympy.core.singleton import S +from sympy.functions.elementary.exponential import exp, log +from sympy.functions.elementary.miscellaneous import sqrt + + +def _expm1(x): + return exp(x) - S.One + + +class expm1(Function): + """ + Represents the exponential function minus one. + + Explanation + =========== + + The benefit of using ``expm1(x)`` over ``exp(x) - 1`` + is that the latter is prone to cancellation under finite precision + arithmetic when x is close to zero. + + Examples + ======== + + >>> from sympy.abc import x + >>> from sympy.codegen.cfunctions import expm1 + >>> '%.0e' % expm1(1e-99).evalf() + '1e-99' + >>> from math import exp + >>> exp(1e-99) - 1 + 0.0 + >>> expm1(x).diff(x) + exp(x) + + See Also + ======== + + log1p + """ + nargs = 1 + + def fdiff(self, argindex=1): + """ + Returns the first derivative of this function. + """ + if argindex == 1: + return exp(*self.args) + else: + raise ArgumentIndexError(self, argindex) + + def _eval_expand_func(self, **hints): + return _expm1(*self.args) + + def _eval_rewrite_as_exp(self, arg, **kwargs): + return exp(arg) - S.One + + _eval_rewrite_as_tractable = _eval_rewrite_as_exp + + @classmethod + def eval(cls, arg): + exp_arg = exp.eval(arg) + if exp_arg is not None: + return exp_arg - S.One + + def _eval_is_real(self): + return self.args[0].is_real + + def _eval_is_finite(self): + return self.args[0].is_finite + + +def _log1p(x): + return log(x + S.One) + + +class log1p(Function): + """ + Represents the natural logarithm of a number plus one. + + Explanation + =========== + + The benefit of using ``log1p(x)`` over ``log(x + 1)`` + is that the latter is prone to cancellation under finite precision + arithmetic when x is close to zero. + + Examples + ======== + + >>> from sympy.abc import x + >>> from sympy.codegen.cfunctions import log1p + >>> from sympy import expand_log + >>> '%.0e' % expand_log(log1p(1e-99)).evalf() + '1e-99' + >>> from math import log + >>> log(1 + 1e-99) + 0.0 + >>> log1p(x).diff(x) + 1/(x + 1) + + See Also + ======== + + expm1 + """ + nargs = 1 + + + def fdiff(self, argindex=1): + """ + Returns the first derivative of this function. + """ + if argindex == 1: + return S.One/(self.args[0] + S.One) + else: + raise ArgumentIndexError(self, argindex) + + + def _eval_expand_func(self, **hints): + return _log1p(*self.args) + + def _eval_rewrite_as_log(self, arg, **kwargs): + return _log1p(arg) + + _eval_rewrite_as_tractable = _eval_rewrite_as_log + + @classmethod + def eval(cls, arg): + if arg.is_Rational: + return log(arg + S.One) + elif not arg.is_Float: # not safe to add 1 to Float + return log.eval(arg + S.One) + elif arg.is_number: + return log(Rational(arg) + S.One) + + def _eval_is_real(self): + return (self.args[0] + S.One).is_nonnegative + + def _eval_is_finite(self): + if (self.args[0] + S.One).is_zero: + return False + return self.args[0].is_finite + + def _eval_is_positive(self): + return self.args[0].is_positive + + def _eval_is_zero(self): + return self.args[0].is_zero + + def _eval_is_nonnegative(self): + return self.args[0].is_nonnegative + +_Two = S(2) + +def _exp2(x): + return Pow(_Two, x) + +class exp2(Function): + """ + Represents the exponential function with base two. + + Explanation + =========== + + The benefit of using ``exp2(x)`` over ``2**x`` + is that the latter is not as efficient under finite precision + arithmetic. + + Examples + ======== + + >>> from sympy.abc import x + >>> from sympy.codegen.cfunctions import exp2 + >>> exp2(2).evalf() == 4.0 + True + >>> exp2(x).diff(x) + log(2)*exp2(x) + + See Also + ======== + + log2 + """ + nargs = 1 + + + def fdiff(self, argindex=1): + """ + Returns the first derivative of this function. + """ + if argindex == 1: + return self*log(_Two) + else: + raise ArgumentIndexError(self, argindex) + + def _eval_rewrite_as_Pow(self, arg, **kwargs): + return _exp2(arg) + + _eval_rewrite_as_tractable = _eval_rewrite_as_Pow + + def _eval_expand_func(self, **hints): + return _exp2(*self.args) + + @classmethod + def eval(cls, arg): + if arg.is_number: + return _exp2(arg) + + +def _log2(x): + return log(x)/log(_Two) + + +class log2(Function): + """ + Represents the logarithm function with base two. + + Explanation + =========== + + The benefit of using ``log2(x)`` over ``log(x)/log(2)`` + is that the latter is not as efficient under finite precision + arithmetic. + + Examples + ======== + + >>> from sympy.abc import x + >>> from sympy.codegen.cfunctions import log2 + >>> log2(4).evalf() == 2.0 + True + >>> log2(x).diff(x) + 1/(x*log(2)) + + See Also + ======== + + exp2 + log10 + """ + nargs = 1 + + def fdiff(self, argindex=1): + """ + Returns the first derivative of this function. + """ + if argindex == 1: + return S.One/(log(_Two)*self.args[0]) + else: + raise ArgumentIndexError(self, argindex) + + + @classmethod + def eval(cls, arg): + if arg.is_number: + result = log.eval(arg, base=_Two) + if result.is_Atom: + return result + elif arg.is_Pow and arg.base == _Two: + return arg.exp + + def _eval_evalf(self, *args, **kwargs): + return self.rewrite(log).evalf(*args, **kwargs) + + def _eval_expand_func(self, **hints): + return _log2(*self.args) + + def _eval_rewrite_as_log(self, arg, **kwargs): + return _log2(arg) + + _eval_rewrite_as_tractable = _eval_rewrite_as_log + + +def _fma(x, y, z): + return x*y + z + + +class fma(Function): + """ + Represents "fused multiply add". + + Explanation + =========== + + The benefit of using ``fma(x, y, z)`` over ``x*y + z`` + is that, under finite precision arithmetic, the former is + supported by special instructions on some CPUs. + + Examples + ======== + + >>> from sympy.abc import x, y, z + >>> from sympy.codegen.cfunctions import fma + >>> fma(x, y, z).diff(x) + y + + """ + nargs = 3 + + def fdiff(self, argindex=1): + """ + Returns the first derivative of this function. + """ + if argindex in (1, 2): + return self.args[2 - argindex] + elif argindex == 3: + return S.One + else: + raise ArgumentIndexError(self, argindex) + + + def _eval_expand_func(self, **hints): + return _fma(*self.args) + + def _eval_rewrite_as_tractable(self, arg, limitvar=None, **kwargs): + return _fma(arg) + + +_Ten = S(10) + + +def _log10(x): + return log(x)/log(_Ten) + + +class log10(Function): + """ + Represents the logarithm function with base ten. + + Examples + ======== + + >>> from sympy.abc import x + >>> from sympy.codegen.cfunctions import log10 + >>> log10(100).evalf() == 2.0 + True + >>> log10(x).diff(x) + 1/(x*log(10)) + + See Also + ======== + + log2 + """ + nargs = 1 + + def fdiff(self, argindex=1): + """ + Returns the first derivative of this function. + """ + if argindex == 1: + return S.One/(log(_Ten)*self.args[0]) + else: + raise ArgumentIndexError(self, argindex) + + + @classmethod + def eval(cls, arg): + if arg.is_number: + result = log.eval(arg, base=_Ten) + if result.is_Atom: + return result + elif arg.is_Pow and arg.base == _Ten: + return arg.exp + + def _eval_expand_func(self, **hints): + return _log10(*self.args) + + def _eval_rewrite_as_log(self, arg, **kwargs): + return _log10(arg) + + _eval_rewrite_as_tractable = _eval_rewrite_as_log + + +def _Sqrt(x): + return Pow(x, S.Half) + + +class Sqrt(Function): # 'sqrt' already defined in sympy.functions.elementary.miscellaneous + """ + Represents the square root function. + + Explanation + =========== + + The reason why one would use ``Sqrt(x)`` over ``sqrt(x)`` + is that the latter is internally represented as ``Pow(x, S.Half)`` which + may not be what one wants when doing code-generation. + + Examples + ======== + + >>> from sympy.abc import x + >>> from sympy.codegen.cfunctions import Sqrt + >>> Sqrt(x) + Sqrt(x) + >>> Sqrt(x).diff(x) + 1/(2*sqrt(x)) + + See Also + ======== + + Cbrt + """ + nargs = 1 + + def fdiff(self, argindex=1): + """ + Returns the first derivative of this function. + """ + if argindex == 1: + return Pow(self.args[0], Rational(-1, 2))/_Two + else: + raise ArgumentIndexError(self, argindex) + + def _eval_expand_func(self, **hints): + return _Sqrt(*self.args) + + def _eval_rewrite_as_Pow(self, arg, **kwargs): + return _Sqrt(arg) + + _eval_rewrite_as_tractable = _eval_rewrite_as_Pow + + +def _Cbrt(x): + return Pow(x, Rational(1, 3)) + + +class Cbrt(Function): # 'cbrt' already defined in sympy.functions.elementary.miscellaneous + """ + Represents the cube root function. + + Explanation + =========== + + The reason why one would use ``Cbrt(x)`` over ``cbrt(x)`` + is that the latter is internally represented as ``Pow(x, Rational(1, 3))`` which + may not be what one wants when doing code-generation. + + Examples + ======== + + >>> from sympy.abc import x + >>> from sympy.codegen.cfunctions import Cbrt + >>> Cbrt(x) + Cbrt(x) + >>> Cbrt(x).diff(x) + 1/(3*x**(2/3)) + + See Also + ======== + + Sqrt + """ + nargs = 1 + + def fdiff(self, argindex=1): + """ + Returns the first derivative of this function. + """ + if argindex == 1: + return Pow(self.args[0], Rational(-_Two/3))/3 + else: + raise ArgumentIndexError(self, argindex) + + + def _eval_expand_func(self, **hints): + return _Cbrt(*self.args) + + def _eval_rewrite_as_Pow(self, arg, **kwargs): + return _Cbrt(arg) + + _eval_rewrite_as_tractable = _eval_rewrite_as_Pow + + +def _hypot(x, y): + return sqrt(Pow(x, 2) + Pow(y, 2)) + + +class hypot(Function): + """ + Represents the hypotenuse function. + + Explanation + =========== + + The hypotenuse function is provided by e.g. the math library + in the C99 standard, hence one may want to represent the function + symbolically when doing code-generation. + + Examples + ======== + + >>> from sympy.abc import x, y + >>> from sympy.codegen.cfunctions import hypot + >>> hypot(3, 4).evalf() == 5.0 + True + >>> hypot(x, y) + hypot(x, y) + >>> hypot(x, y).diff(x) + x/hypot(x, y) + + """ + nargs = 2 + + def fdiff(self, argindex=1): + """ + Returns the first derivative of this function. + """ + if argindex in (1, 2): + return 2*self.args[argindex-1]/(_Two*self.func(*self.args)) + else: + raise ArgumentIndexError(self, argindex) + + + def _eval_expand_func(self, **hints): + return _hypot(*self.args) + + def _eval_rewrite_as_Pow(self, arg, **kwargs): + return _hypot(arg) + + _eval_rewrite_as_tractable = _eval_rewrite_as_Pow + + +class isnan(Function): + nargs = 1 diff --git a/lib/python3.12/site-packages/sympy/codegen/cnodes.py b/lib/python3.12/site-packages/sympy/codegen/cnodes.py new file mode 100644 index 0000000000000000000000000000000000000000..dd2a324ee49cabcd42e99ca5d8e5379cc9262c4e --- /dev/null +++ b/lib/python3.12/site-packages/sympy/codegen/cnodes.py @@ -0,0 +1,156 @@ +""" +AST nodes specific to the C family of languages +""" + +from sympy.codegen.ast import ( + Attribute, Declaration, Node, String, Token, Type, none, + FunctionCall, CodeBlock + ) +from sympy.core.basic import Basic +from sympy.core.containers import Tuple +from sympy.core.sympify import sympify + +void = Type('void') + +restrict = Attribute('restrict') # guarantees no pointer aliasing +volatile = Attribute('volatile') +static = Attribute('static') + + +def alignof(arg): + """ Generate of FunctionCall instance for calling 'alignof' """ + return FunctionCall('alignof', [String(arg) if isinstance(arg, str) else arg]) + + +def sizeof(arg): + """ Generate of FunctionCall instance for calling 'sizeof' + + Examples + ======== + + >>> from sympy.codegen.ast import real + >>> from sympy.codegen.cnodes import sizeof + >>> from sympy import ccode + >>> ccode(sizeof(real)) + 'sizeof(double)' + """ + return FunctionCall('sizeof', [String(arg) if isinstance(arg, str) else arg]) + + +class CommaOperator(Basic): + """ Represents the comma operator in C """ + def __new__(cls, *args): + return Basic.__new__(cls, *[sympify(arg) for arg in args]) + + +class Label(Node): + """ Label for use with e.g. goto statement. + + Examples + ======== + + >>> from sympy import ccode, Symbol + >>> from sympy.codegen.cnodes import Label, PreIncrement + >>> print(ccode(Label('foo'))) + foo: + >>> print(ccode(Label('bar', [PreIncrement(Symbol('a'))]))) + bar: + ++(a); + + """ + __slots__ = _fields = ('name', 'body') + defaults = {'body': none} + _construct_name = String + + @classmethod + def _construct_body(cls, itr): + if isinstance(itr, CodeBlock): + return itr + else: + return CodeBlock(*itr) + + +class goto(Token): + """ Represents goto in C """ + __slots__ = _fields = ('label',) + _construct_label = Label + + +class PreDecrement(Basic): + """ Represents the pre-decrement operator + + Examples + ======== + + >>> from sympy.abc import x + >>> from sympy.codegen.cnodes import PreDecrement + >>> from sympy import ccode + >>> ccode(PreDecrement(x)) + '--(x)' + + """ + nargs = 1 + + +class PostDecrement(Basic): + """ Represents the post-decrement operator + + Examples + ======== + + >>> from sympy.abc import x + >>> from sympy.codegen.cnodes import PostDecrement + >>> from sympy import ccode + >>> ccode(PostDecrement(x)) + '(x)--' + + """ + nargs = 1 + + +class PreIncrement(Basic): + """ Represents the pre-increment operator + + Examples + ======== + + >>> from sympy.abc import x + >>> from sympy.codegen.cnodes import PreIncrement + >>> from sympy import ccode + >>> ccode(PreIncrement(x)) + '++(x)' + + """ + nargs = 1 + + +class PostIncrement(Basic): + """ Represents the post-increment operator + + Examples + ======== + + >>> from sympy.abc import x + >>> from sympy.codegen.cnodes import PostIncrement + >>> from sympy import ccode + >>> ccode(PostIncrement(x)) + '(x)++' + + """ + nargs = 1 + + +class struct(Node): + """ Represents a struct in C """ + __slots__ = _fields = ('name', 'declarations') + defaults = {'name': none} + _construct_name = String + + @classmethod + def _construct_declarations(cls, args): + return Tuple(*[Declaration(arg) for arg in args]) + + +class union(struct): + """ Represents a union in C """ + __slots__ = () diff --git a/lib/python3.12/site-packages/sympy/codegen/cutils.py b/lib/python3.12/site-packages/sympy/codegen/cutils.py new file mode 100644 index 0000000000000000000000000000000000000000..2182ac1f3455da490a0bb57f8d6731fe8a29a232 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/codegen/cutils.py @@ -0,0 +1,8 @@ +from sympy.printing.c import C99CodePrinter + +def render_as_source_file(content, Printer=C99CodePrinter, settings=None): + """ Renders a C source file (with required #include statements) """ + printer = Printer(settings or {}) + code_str = printer.doprint(content) + includes = '\n'.join(['#include <%s>' % h for h in printer.headers]) + return includes + '\n\n' + code_str diff --git a/lib/python3.12/site-packages/sympy/codegen/cxxnodes.py b/lib/python3.12/site-packages/sympy/codegen/cxxnodes.py new file mode 100644 index 0000000000000000000000000000000000000000..7f7aafd01ab2de99ad0f668275889863fc73f5aa --- /dev/null +++ b/lib/python3.12/site-packages/sympy/codegen/cxxnodes.py @@ -0,0 +1,14 @@ +""" +AST nodes specific to C++. +""" + +from sympy.codegen.ast import Attribute, String, Token, Type, none + +class using(Token): + """ Represents a 'using' statement in C++ """ + __slots__ = _fields = ('type', 'alias') + defaults = {'alias': none} + _construct_type = Type + _construct_alias = String + +constexpr = Attribute('constexpr') diff --git a/lib/python3.12/site-packages/sympy/codegen/fnodes.py b/lib/python3.12/site-packages/sympy/codegen/fnodes.py new file mode 100644 index 0000000000000000000000000000000000000000..6d38a60e5f5c1e53d70641ec395053bfe9dd95ea --- /dev/null +++ b/lib/python3.12/site-packages/sympy/codegen/fnodes.py @@ -0,0 +1,657 @@ +""" +AST nodes specific to Fortran. + +The functions defined in this module allows the user to express functions such as ``dsign`` +as a SymPy function for symbolic manipulation. +""" + +from sympy.codegen.ast import ( + Attribute, CodeBlock, FunctionCall, Node, none, String, + Token, _mk_Tuple, Variable +) +from sympy.core.basic import Basic +from sympy.core.containers import Tuple +from sympy.core.expr import Expr +from sympy.core.function import Function +from sympy.core.numbers import Float, Integer +from sympy.core.symbol import Str +from sympy.core.sympify import sympify +from sympy.logic import true, false +from sympy.utilities.iterables import iterable + + + +pure = Attribute('pure') +elemental = Attribute('elemental') # (all elemental procedures are also pure) + +intent_in = Attribute('intent_in') +intent_out = Attribute('intent_out') +intent_inout = Attribute('intent_inout') + +allocatable = Attribute('allocatable') + +class Program(Token): + """ Represents a 'program' block in Fortran. + + Examples + ======== + + >>> from sympy.codegen.ast import Print + >>> from sympy.codegen.fnodes import Program + >>> prog = Program('myprogram', [Print([42])]) + >>> from sympy import fcode + >>> print(fcode(prog, source_format='free')) + program myprogram + print *, 42 + end program + + """ + __slots__ = _fields = ('name', 'body') + _construct_name = String + _construct_body = staticmethod(lambda body: CodeBlock(*body)) + + +class use_rename(Token): + """ Represents a renaming in a use statement in Fortran. + + Examples + ======== + + >>> from sympy.codegen.fnodes import use_rename, use + >>> from sympy import fcode + >>> ren = use_rename("thingy", "convolution2d") + >>> print(fcode(ren, source_format='free')) + thingy => convolution2d + >>> full = use('signallib', only=['snr', ren]) + >>> print(fcode(full, source_format='free')) + use signallib, only: snr, thingy => convolution2d + + """ + __slots__ = _fields = ('local', 'original') + _construct_local = String + _construct_original = String + +def _name(arg): + if hasattr(arg, 'name'): + return arg.name + else: + return String(arg) + +class use(Token): + """ Represents a use statement in Fortran. + + Examples + ======== + + >>> from sympy.codegen.fnodes import use + >>> from sympy import fcode + >>> fcode(use('signallib'), source_format='free') + 'use signallib' + >>> fcode(use('signallib', [('metric', 'snr')]), source_format='free') + 'use signallib, metric => snr' + >>> fcode(use('signallib', only=['snr', 'convolution2d']), source_format='free') + 'use signallib, only: snr, convolution2d' + + """ + __slots__ = _fields = ('namespace', 'rename', 'only') + defaults = {'rename': none, 'only': none} + _construct_namespace = staticmethod(_name) + _construct_rename = staticmethod(lambda args: Tuple(*[arg if isinstance(arg, use_rename) else use_rename(*arg) for arg in args])) + _construct_only = staticmethod(lambda args: Tuple(*[arg if isinstance(arg, use_rename) else _name(arg) for arg in args])) + + +class Module(Token): + """ Represents a module in Fortran. + + Examples + ======== + + >>> from sympy.codegen.fnodes import Module + >>> from sympy import fcode + >>> print(fcode(Module('signallib', ['implicit none'], []), source_format='free')) + module signallib + implicit none + + contains + + + end module + + """ + __slots__ = _fields = ('name', 'declarations', 'definitions') + defaults = {'declarations': Tuple()} + _construct_name = String + + @classmethod + def _construct_declarations(cls, args): + args = [Str(arg) if isinstance(arg, str) else arg for arg in args] + return CodeBlock(*args) + + _construct_definitions = staticmethod(lambda arg: CodeBlock(*arg)) + + +class Subroutine(Node): + """ Represents a subroutine in Fortran. + + Examples + ======== + + >>> from sympy import fcode, symbols + >>> from sympy.codegen.ast import Print + >>> from sympy.codegen.fnodes import Subroutine + >>> x, y = symbols('x y', real=True) + >>> sub = Subroutine('mysub', [x, y], [Print([x**2 + y**2, x*y])]) + >>> print(fcode(sub, source_format='free', standard=2003)) + subroutine mysub(x, y) + real*8 :: x + real*8 :: y + print *, x**2 + y**2, x*y + end subroutine + + """ + __slots__ = ('name', 'parameters', 'body') + _fields = __slots__ + Node._fields + _construct_name = String + _construct_parameters = staticmethod(lambda params: Tuple(*map(Variable.deduced, params))) + + @classmethod + def _construct_body(cls, itr): + if isinstance(itr, CodeBlock): + return itr + else: + return CodeBlock(*itr) + +class SubroutineCall(Token): + """ Represents a call to a subroutine in Fortran. + + Examples + ======== + + >>> from sympy.codegen.fnodes import SubroutineCall + >>> from sympy import fcode + >>> fcode(SubroutineCall('mysub', 'x y'.split())) + ' call mysub(x, y)' + + """ + __slots__ = _fields = ('name', 'subroutine_args') + _construct_name = staticmethod(_name) + _construct_subroutine_args = staticmethod(_mk_Tuple) + + +class Do(Token): + """ Represents a Do loop in in Fortran. + + Examples + ======== + + >>> from sympy import fcode, symbols + >>> from sympy.codegen.ast import aug_assign, Print + >>> from sympy.codegen.fnodes import Do + >>> i, n = symbols('i n', integer=True) + >>> r = symbols('r', real=True) + >>> body = [aug_assign(r, '+', 1/i), Print([i, r])] + >>> do1 = Do(body, i, 1, n) + >>> print(fcode(do1, source_format='free')) + do i = 1, n + r = r + 1d0/i + print *, i, r + end do + >>> do2 = Do(body, i, 1, n, 2) + >>> print(fcode(do2, source_format='free')) + do i = 1, n, 2 + r = r + 1d0/i + print *, i, r + end do + + """ + + __slots__ = _fields = ('body', 'counter', 'first', 'last', 'step', 'concurrent') + defaults = {'step': Integer(1), 'concurrent': false} + _construct_body = staticmethod(lambda body: CodeBlock(*body)) + _construct_counter = staticmethod(sympify) + _construct_first = staticmethod(sympify) + _construct_last = staticmethod(sympify) + _construct_step = staticmethod(sympify) + _construct_concurrent = staticmethod(lambda arg: true if arg else false) + + +class ArrayConstructor(Token): + """ Represents an array constructor. + + Examples + ======== + + >>> from sympy import fcode + >>> from sympy.codegen.fnodes import ArrayConstructor + >>> ac = ArrayConstructor([1, 2, 3]) + >>> fcode(ac, standard=95, source_format='free') + '(/1, 2, 3/)' + >>> fcode(ac, standard=2003, source_format='free') + '[1, 2, 3]' + + """ + __slots__ = _fields = ('elements',) + _construct_elements = staticmethod(_mk_Tuple) + + +class ImpliedDoLoop(Token): + """ Represents an implied do loop in Fortran. + + Examples + ======== + + >>> from sympy import Symbol, fcode + >>> from sympy.codegen.fnodes import ImpliedDoLoop, ArrayConstructor + >>> i = Symbol('i', integer=True) + >>> idl = ImpliedDoLoop(i**3, i, -3, 3, 2) # -27, -1, 1, 27 + >>> ac = ArrayConstructor([-28, idl, 28]) # -28, -27, -1, 1, 27, 28 + >>> fcode(ac, standard=2003, source_format='free') + '[-28, (i**3, i = -3, 3, 2), 28]' + + """ + __slots__ = _fields = ('expr', 'counter', 'first', 'last', 'step') + defaults = {'step': Integer(1)} + _construct_expr = staticmethod(sympify) + _construct_counter = staticmethod(sympify) + _construct_first = staticmethod(sympify) + _construct_last = staticmethod(sympify) + _construct_step = staticmethod(sympify) + + +class Extent(Basic): + """ Represents a dimension extent. + + Examples + ======== + + >>> from sympy.codegen.fnodes import Extent + >>> e = Extent(-3, 3) # -3, -2, -1, 0, 1, 2, 3 + >>> from sympy import fcode + >>> fcode(e, source_format='free') + '-3:3' + >>> from sympy.codegen.ast import Variable, real + >>> from sympy.codegen.fnodes import dimension, intent_out + >>> dim = dimension(e, e) + >>> arr = Variable('x', real, attrs=[dim, intent_out]) + >>> fcode(arr.as_Declaration(), source_format='free', standard=2003) + 'real*8, dimension(-3:3, -3:3), intent(out) :: x' + + """ + def __new__(cls, *args): + if len(args) == 2: + low, high = args + return Basic.__new__(cls, sympify(low), sympify(high)) + elif len(args) == 0 or (len(args) == 1 and args[0] in (':', None)): + return Basic.__new__(cls) # assumed shape + else: + raise ValueError("Expected 0 or 2 args (or one argument == None or ':')") + + def _sympystr(self, printer): + if len(self.args) == 0: + return ':' + return ":".join(str(arg) for arg in self.args) + +assumed_extent = Extent() # or Extent(':'), Extent(None) + + +def dimension(*args): + """ Creates a 'dimension' Attribute with (up to 7) extents. + + Examples + ======== + + >>> from sympy import fcode + >>> from sympy.codegen.fnodes import dimension, intent_in + >>> dim = dimension('2', ':') # 2 rows, runtime determined number of columns + >>> from sympy.codegen.ast import Variable, integer + >>> arr = Variable('a', integer, attrs=[dim, intent_in]) + >>> fcode(arr.as_Declaration(), source_format='free', standard=2003) + 'integer*4, dimension(2, :), intent(in) :: a' + + """ + if len(args) > 7: + raise ValueError("Fortran only supports up to 7 dimensional arrays") + parameters = [] + for arg in args: + if isinstance(arg, Extent): + parameters.append(arg) + elif isinstance(arg, str): + if arg == ':': + parameters.append(Extent()) + else: + parameters.append(String(arg)) + elif iterable(arg): + parameters.append(Extent(*arg)) + else: + parameters.append(sympify(arg)) + if len(args) == 0: + raise ValueError("Need at least one dimension") + return Attribute('dimension', parameters) + + +assumed_size = dimension('*') + +def array(symbol, dim, intent=None, *, attrs=(), value=None, type=None): + """ Convenience function for creating a Variable instance for a Fortran array. + + Parameters + ========== + + symbol : symbol + dim : Attribute or iterable + If dim is an ``Attribute`` it need to have the name 'dimension'. If it is + not an ``Attribute``, then it is passed to :func:`dimension` as ``*dim`` + intent : str + One of: 'in', 'out', 'inout' or None + \\*\\*kwargs: + Keyword arguments for ``Variable`` ('type' & 'value') + + Examples + ======== + + >>> from sympy import fcode + >>> from sympy.codegen.ast import integer, real + >>> from sympy.codegen.fnodes import array + >>> arr = array('a', '*', 'in', type=integer) + >>> print(fcode(arr.as_Declaration(), source_format='free', standard=2003)) + integer*4, dimension(*), intent(in) :: a + >>> x = array('x', [3, ':', ':'], intent='out', type=real) + >>> print(fcode(x.as_Declaration(value=1), source_format='free', standard=2003)) + real*8, dimension(3, :, :), intent(out) :: x = 1 + + """ + if isinstance(dim, Attribute): + if str(dim.name) != 'dimension': + raise ValueError("Got an unexpected Attribute argument as dim: %s" % str(dim)) + else: + dim = dimension(*dim) + + attrs = list(attrs) + [dim] + if intent is not None: + if intent not in (intent_in, intent_out, intent_inout): + intent = {'in': intent_in, 'out': intent_out, 'inout': intent_inout}[intent] + attrs.append(intent) + if type is None: + return Variable.deduced(symbol, value=value, attrs=attrs) + else: + return Variable(symbol, type, value=value, attrs=attrs) + +def _printable(arg): + return String(arg) if isinstance(arg, str) else sympify(arg) + + +def allocated(array): + """ Creates an AST node for a function call to Fortran's "allocated(...)" + + Examples + ======== + + >>> from sympy import fcode + >>> from sympy.codegen.fnodes import allocated + >>> alloc = allocated('x') + >>> fcode(alloc, source_format='free') + 'allocated(x)' + + """ + return FunctionCall('allocated', [_printable(array)]) + + +def lbound(array, dim=None, kind=None): + """ Creates an AST node for a function call to Fortran's "lbound(...)" + + Parameters + ========== + + array : Symbol or String + dim : expr + kind : expr + + Examples + ======== + + >>> from sympy import fcode + >>> from sympy.codegen.fnodes import lbound + >>> lb = lbound('arr', dim=2) + >>> fcode(lb, source_format='free') + 'lbound(arr, 2)' + + """ + return FunctionCall( + 'lbound', + [_printable(array)] + + ([_printable(dim)] if dim else []) + + ([_printable(kind)] if kind else []) + ) + + +def ubound(array, dim=None, kind=None): + return FunctionCall( + 'ubound', + [_printable(array)] + + ([_printable(dim)] if dim else []) + + ([_printable(kind)] if kind else []) + ) + + +def shape(source, kind=None): + """ Creates an AST node for a function call to Fortran's "shape(...)" + + Parameters + ========== + + source : Symbol or String + kind : expr + + Examples + ======== + + >>> from sympy import fcode + >>> from sympy.codegen.fnodes import shape + >>> shp = shape('x') + >>> fcode(shp, source_format='free') + 'shape(x)' + + """ + return FunctionCall( + 'shape', + [_printable(source)] + + ([_printable(kind)] if kind else []) + ) + + +def size(array, dim=None, kind=None): + """ Creates an AST node for a function call to Fortran's "size(...)" + + Examples + ======== + + >>> from sympy import fcode, Symbol + >>> from sympy.codegen.ast import FunctionDefinition, real, Return + >>> from sympy.codegen.fnodes import array, sum_, size + >>> a = Symbol('a', real=True) + >>> body = [Return((sum_(a**2)/size(a))**.5)] + >>> arr = array(a, dim=[':'], intent='in') + >>> fd = FunctionDefinition(real, 'rms', [arr], body) + >>> print(fcode(fd, source_format='free', standard=2003)) + real*8 function rms(a) + real*8, dimension(:), intent(in) :: a + rms = sqrt(sum(a**2)*1d0/size(a)) + end function + + """ + return FunctionCall( + 'size', + [_printable(array)] + + ([_printable(dim)] if dim else []) + + ([_printable(kind)] if kind else []) + ) + + +def reshape(source, shape, pad=None, order=None): + """ Creates an AST node for a function call to Fortran's "reshape(...)" + + Parameters + ========== + + source : Symbol or String + shape : ArrayExpr + + """ + return FunctionCall( + 'reshape', + [_printable(source), _printable(shape)] + + ([_printable(pad)] if pad else []) + + ([_printable(order)] if pad else []) + ) + + +def bind_C(name=None): + """ Creates an Attribute ``bind_C`` with a name. + + Parameters + ========== + + name : str + + Examples + ======== + + >>> from sympy import fcode, Symbol + >>> from sympy.codegen.ast import FunctionDefinition, real, Return + >>> from sympy.codegen.fnodes import array, sum_, bind_C + >>> a = Symbol('a', real=True) + >>> s = Symbol('s', integer=True) + >>> arr = array(a, dim=[s], intent='in') + >>> body = [Return((sum_(a**2)/s)**.5)] + >>> fd = FunctionDefinition(real, 'rms', [arr, s], body, attrs=[bind_C('rms')]) + >>> print(fcode(fd, source_format='free', standard=2003)) + real*8 function rms(a, s) bind(C, name="rms") + real*8, dimension(s), intent(in) :: a + integer*4 :: s + rms = sqrt(sum(a**2)/s) + end function + + """ + return Attribute('bind_C', [String(name)] if name else []) + +class GoTo(Token): + """ Represents a goto statement in Fortran + + Examples + ======== + + >>> from sympy.codegen.fnodes import GoTo + >>> go = GoTo([10, 20, 30], 'i') + >>> from sympy import fcode + >>> fcode(go, source_format='free') + 'go to (10, 20, 30), i' + + """ + __slots__ = _fields = ('labels', 'expr') + defaults = {'expr': none} + _construct_labels = staticmethod(_mk_Tuple) + _construct_expr = staticmethod(sympify) + + +class FortranReturn(Token): + """ AST node explicitly mapped to a fortran "return". + + Explanation + =========== + + Because a return statement in fortran is different from C, and + in order to aid reuse of our codegen ASTs the ordinary + ``.codegen.ast.Return`` is interpreted as assignment to + the result variable of the function. If one for some reason needs + to generate a fortran RETURN statement, this node should be used. + + Examples + ======== + + >>> from sympy.codegen.fnodes import FortranReturn + >>> from sympy import fcode + >>> fcode(FortranReturn('x')) + ' return x' + + """ + __slots__ = _fields = ('return_value',) + defaults = {'return_value': none} + _construct_return_value = staticmethod(sympify) + + +class FFunction(Function): + _required_standard = 77 + + def _fcode(self, printer): + name = self.__class__.__name__ + if printer._settings['standard'] < self._required_standard: + raise NotImplementedError("%s requires Fortran %d or newer" % + (name, self._required_standard)) + return '{}({})'.format(name, ', '.join(map(printer._print, self.args))) + + +class F95Function(FFunction): + _required_standard = 95 + + +class isign(FFunction): + """ Fortran sign intrinsic for integer arguments. """ + nargs = 2 + + +class dsign(FFunction): + """ Fortran sign intrinsic for double precision arguments. """ + nargs = 2 + + +class cmplx(FFunction): + """ Fortran complex conversion function. """ + nargs = 2 # may be extended to (2, 3) at a later point + + +class kind(FFunction): + """ Fortran kind function. """ + nargs = 1 + + +class merge(F95Function): + """ Fortran merge function """ + nargs = 3 + + +class _literal(Float): + _token = None # type: str + _decimals = None # type: int + + def _fcode(self, printer, *args, **kwargs): + mantissa, sgnd_ex = ('%.{}e'.format(self._decimals) % self).split('e') + mantissa = mantissa.strip('0').rstrip('.') + ex_sgn, ex_num = sgnd_ex[0], sgnd_ex[1:].lstrip('0') + ex_sgn = '' if ex_sgn == '+' else ex_sgn + return (mantissa or '0') + self._token + ex_sgn + (ex_num or '0') + + +class literal_sp(_literal): + """ Fortran single precision real literal """ + _token = 'e' + _decimals = 9 + + +class literal_dp(_literal): + """ Fortran double precision real literal """ + _token = 'd' + _decimals = 17 + + +class sum_(Token, Expr): + __slots__ = _fields = ('array', 'dim', 'mask') + defaults = {'dim': none, 'mask': none} + _construct_array = staticmethod(sympify) + _construct_dim = staticmethod(sympify) + + +class product_(Token, Expr): + __slots__ = _fields = ('array', 'dim', 'mask') + defaults = {'dim': none, 'mask': none} + _construct_array = staticmethod(sympify) + _construct_dim = staticmethod(sympify) diff --git a/lib/python3.12/site-packages/sympy/codegen/futils.py b/lib/python3.12/site-packages/sympy/codegen/futils.py new file mode 100644 index 0000000000000000000000000000000000000000..4a1f5751fbd4d6b44d99c69a74ad89a8496f8648 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/codegen/futils.py @@ -0,0 +1,40 @@ +from itertools import chain +from sympy.codegen.fnodes import Module +from sympy.core.symbol import Dummy +from sympy.printing.fortran import FCodePrinter + +""" This module collects utilities for rendering Fortran code. """ + + +def render_as_module(definitions, name, declarations=(), printer_settings=None): + """ Creates a ``Module`` instance and renders it as a string. + + This generates Fortran source code for a module with the correct ``use`` statements. + + Parameters + ========== + + definitions : iterable + Passed to :class:`sympy.codegen.fnodes.Module`. + name : str + Passed to :class:`sympy.codegen.fnodes.Module`. + declarations : iterable + Passed to :class:`sympy.codegen.fnodes.Module`. It will be extended with + use statements, 'implicit none' and public list generated from ``definitions``. + printer_settings : dict + Passed to ``FCodePrinter`` (default: ``{'standard': 2003, 'source_format': 'free'}``). + + """ + printer_settings = printer_settings or {'standard': 2003, 'source_format': 'free'} + printer = FCodePrinter(printer_settings) + dummy = Dummy() + if isinstance(definitions, Module): + raise ValueError("This function expects to construct a module on its own.") + mod = Module(name, chain(declarations, [dummy]), definitions) + fstr = printer.doprint(mod) + module_use_str = ' %s\n' % ' \n'.join(['use %s, only: %s' % (k, ', '.join(v)) for + k, v in printer.module_uses.items()]) + module_use_str += ' implicit none\n' + module_use_str += ' private\n' + module_use_str += ' public %s\n' % ', '.join([str(node.name) for node in definitions if getattr(node, 'name', None)]) + return fstr.replace(printer.doprint(dummy), module_use_str) diff --git a/lib/python3.12/site-packages/sympy/codegen/matrix_nodes.py b/lib/python3.12/site-packages/sympy/codegen/matrix_nodes.py new file mode 100644 index 0000000000000000000000000000000000000000..cf0a13a81c963e2c9e2b885dabd0ff3e2d2b3eb9 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/codegen/matrix_nodes.py @@ -0,0 +1,71 @@ +""" +Additional AST nodes for operations on matrices. The nodes in this module +are meant to represent optimization of matrix expressions within codegen's +target languages that cannot be represented by SymPy expressions. + +As an example, we can use :meth:`sympy.codegen.rewriting.optimize` and the +``matin_opt`` optimization provided in :mod:`sympy.codegen.rewriting` to +transform matrix multiplication under certain assumptions: + + >>> from sympy import symbols, MatrixSymbol + >>> n = symbols('n', integer=True) + >>> A = MatrixSymbol('A', n, n) + >>> x = MatrixSymbol('x', n, 1) + >>> expr = A**(-1) * x + >>> from sympy import assuming, Q + >>> from sympy.codegen.rewriting import matinv_opt, optimize + >>> with assuming(Q.fullrank(A)): + ... optimize(expr, [matinv_opt]) + MatrixSolve(A, vector=x) +""" + +from .ast import Token +from sympy.matrices import MatrixExpr +from sympy.core.sympify import sympify + + +class MatrixSolve(Token, MatrixExpr): + """Represents an operation to solve a linear matrix equation. + + Parameters + ========== + + matrix : MatrixSymbol + + Matrix representing the coefficients of variables in the linear + equation. This matrix must be square and full-rank (i.e. all columns must + be linearly independent) for the solving operation to be valid. + + vector : MatrixSymbol + + One-column matrix representing the solutions to the equations + represented in ``matrix``. + + Examples + ======== + + >>> from sympy import symbols, MatrixSymbol + >>> from sympy.codegen.matrix_nodes import MatrixSolve + >>> n = symbols('n', integer=True) + >>> A = MatrixSymbol('A', n, n) + >>> x = MatrixSymbol('x', n, 1) + >>> from sympy.printing.numpy import NumPyPrinter + >>> NumPyPrinter().doprint(MatrixSolve(A, x)) + 'numpy.linalg.solve(A, x)' + >>> from sympy import octave_code + >>> octave_code(MatrixSolve(A, x)) + 'A \\\\ x' + + """ + __slots__ = _fields = ('matrix', 'vector') + + _construct_matrix = staticmethod(sympify) + _construct_vector = staticmethod(sympify) + + @property + def shape(self): + return self.vector.shape + + def _eval_derivative(self, x): + A, b = self.matrix, self.vector + return MatrixSolve(A, b.diff(x) - A.diff(x) * MatrixSolve(A, b)) diff --git a/lib/python3.12/site-packages/sympy/codegen/numpy_nodes.py b/lib/python3.12/site-packages/sympy/codegen/numpy_nodes.py new file mode 100644 index 0000000000000000000000000000000000000000..2132718e63855b5756ee183b4fd19d42a7b764f5 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/codegen/numpy_nodes.py @@ -0,0 +1,110 @@ +from sympy.core.function import Add, ArgumentIndexError, Function +from sympy.core.power import Pow +from sympy.core.singleton import S +from sympy.core.sorting import default_sort_key +from sympy.functions.elementary.exponential import exp, log + + +def _logaddexp(x1, x2, *, evaluate=True): + return log(Add(exp(x1, evaluate=evaluate), exp(x2, evaluate=evaluate), evaluate=evaluate)) + + +_two = S.One*2 +_ln2 = log(_two) + + +def _lb(x, *, evaluate=True): + return log(x, evaluate=evaluate)/_ln2 + + +def _exp2(x, *, evaluate=True): + return Pow(_two, x, evaluate=evaluate) + + +def _logaddexp2(x1, x2, *, evaluate=True): + return _lb(Add(_exp2(x1, evaluate=evaluate), + _exp2(x2, evaluate=evaluate), evaluate=evaluate)) + + +class logaddexp(Function): + """ Logarithm of the sum of exponentiations of the inputs. + + Helper class for use with e.g. numpy.logaddexp + + See Also + ======== + + https://numpy.org/doc/stable/reference/generated/numpy.logaddexp.html + """ + nargs = 2 + + def __new__(cls, *args): + return Function.__new__(cls, *sorted(args, key=default_sort_key)) + + def fdiff(self, argindex=1): + """ + Returns the first derivative of this function. + """ + if argindex == 1: + wrt, other = self.args + elif argindex == 2: + other, wrt = self.args + else: + raise ArgumentIndexError(self, argindex) + return S.One/(S.One + exp(other-wrt)) + + def _eval_rewrite_as_log(self, x1, x2, **kwargs): + return _logaddexp(x1, x2) + + def _eval_evalf(self, *args, **kwargs): + return self.rewrite(log).evalf(*args, **kwargs) + + def _eval_simplify(self, *args, **kwargs): + a, b = (x.simplify(**kwargs) for x in self.args) + candidate = _logaddexp(a, b) + if candidate != _logaddexp(a, b, evaluate=False): + return candidate + else: + return logaddexp(a, b) + + +class logaddexp2(Function): + """ Logarithm of the sum of exponentiations of the inputs in base-2. + + Helper class for use with e.g. numpy.logaddexp2 + + See Also + ======== + + https://numpy.org/doc/stable/reference/generated/numpy.logaddexp2.html + """ + nargs = 2 + + def __new__(cls, *args): + return Function.__new__(cls, *sorted(args, key=default_sort_key)) + + def fdiff(self, argindex=1): + """ + Returns the first derivative of this function. + """ + if argindex == 1: + wrt, other = self.args + elif argindex == 2: + other, wrt = self.args + else: + raise ArgumentIndexError(self, argindex) + return S.One/(S.One + _exp2(other-wrt)) + + def _eval_rewrite_as_log(self, x1, x2, **kwargs): + return _logaddexp2(x1, x2) + + def _eval_evalf(self, *args, **kwargs): + return self.rewrite(log).evalf(*args, **kwargs) + + def _eval_simplify(self, *args, **kwargs): + a, b = (x.simplify(**kwargs).factor() for x in self.args) + candidate = _logaddexp2(a, b) + if candidate != _logaddexp2(a, b, evaluate=False): + return candidate + else: + return logaddexp2(a, b) diff --git a/lib/python3.12/site-packages/sympy/codegen/pynodes.py b/lib/python3.12/site-packages/sympy/codegen/pynodes.py new file mode 100644 index 0000000000000000000000000000000000000000..f0a08b4a79d32f63d345947d6be310b44504dbf5 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/codegen/pynodes.py @@ -0,0 +1,11 @@ +from .abstract_nodes import List as AbstractList +from .ast import Token + + +class List(AbstractList): + pass + + +class NumExprEvaluate(Token): + """represents a call to :class:`numexpr`s :func:`evaluate`""" + __slots__ = _fields = ('expr',) diff --git a/lib/python3.12/site-packages/sympy/codegen/pyutils.py b/lib/python3.12/site-packages/sympy/codegen/pyutils.py new file mode 100644 index 0000000000000000000000000000000000000000..e14eabe92ce50105a4055b71a49767aae04610b9 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/codegen/pyutils.py @@ -0,0 +1,24 @@ +from sympy.printing.pycode import PythonCodePrinter + +""" This module collects utilities for rendering Python code. """ + + +def render_as_module(content, standard='python3'): + """Renders Python code as a module (with the required imports). + + Parameters + ========== + + standard : + See the parameter ``standard`` in + :meth:`sympy.printing.pycode.pycode` + """ + + printer = PythonCodePrinter({'standard':standard}) + pystr = printer.doprint(content) + if printer._settings['fully_qualified_modules']: + module_imports_str = '\n'.join('import %s' % k for k in printer.module_imports) + else: + module_imports_str = '\n'.join(['from %s import %s' % (k, ', '.join(v)) for + k, v in printer.module_imports.items()]) + return module_imports_str + '\n\n' + pystr diff --git a/lib/python3.12/site-packages/sympy/codegen/rewriting.py b/lib/python3.12/site-packages/sympy/codegen/rewriting.py new file mode 100644 index 0000000000000000000000000000000000000000..274b7770b46ded6711468ab2a01db3a53d6fde87 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/codegen/rewriting.py @@ -0,0 +1,357 @@ +""" +Classes and functions useful for rewriting expressions for optimized code +generation. Some languages (or standards thereof), e.g. C99, offer specialized +math functions for better performance and/or precision. + +Using the ``optimize`` function in this module, together with a collection of +rules (represented as instances of ``Optimization``), one can rewrite the +expressions for this purpose:: + + >>> from sympy import Symbol, exp, log + >>> from sympy.codegen.rewriting import optimize, optims_c99 + >>> x = Symbol('x') + >>> optimize(3*exp(2*x) - 3, optims_c99) + 3*expm1(2*x) + >>> optimize(exp(2*x) - 1 - exp(-33), optims_c99) + expm1(2*x) - exp(-33) + >>> optimize(log(3*x + 3), optims_c99) + log1p(x) + log(3) + >>> optimize(log(2*x + 3), optims_c99) + log(2*x + 3) + +The ``optims_c99`` imported above is tuple containing the following instances +(which may be imported from ``sympy.codegen.rewriting``): + +- ``expm1_opt`` +- ``log1p_opt`` +- ``exp2_opt`` +- ``log2_opt`` +- ``log2const_opt`` + + +""" +from sympy.core.function import expand_log +from sympy.core.singleton import S +from sympy.core.symbol import Wild +from sympy.functions.elementary.complexes import sign +from sympy.functions.elementary.exponential import (exp, log) +from sympy.functions.elementary.miscellaneous import (Max, Min) +from sympy.functions.elementary.trigonometric import (cos, sin, sinc) +from sympy.assumptions import Q, ask +from sympy.codegen.cfunctions import log1p, log2, exp2, expm1 +from sympy.codegen.matrix_nodes import MatrixSolve +from sympy.core.expr import UnevaluatedExpr +from sympy.core.power import Pow +from sympy.codegen.numpy_nodes import logaddexp, logaddexp2 +from sympy.codegen.scipy_nodes import cosm1, powm1 +from sympy.core.mul import Mul +from sympy.matrices.expressions.matexpr import MatrixSymbol +from sympy.utilities.iterables import sift + + +class Optimization: + """ Abstract base class for rewriting optimization. + + Subclasses should implement ``__call__`` taking an expression + as argument. + + Parameters + ========== + cost_function : callable returning number + priority : number + + """ + def __init__(self, cost_function=None, priority=1): + self.cost_function = cost_function + self.priority=priority + + def cheapest(self, *args): + return min(args, key=self.cost_function) + + +class ReplaceOptim(Optimization): + """ Rewriting optimization calling replace on expressions. + + Explanation + =========== + + The instance can be used as a function on expressions for which + it will apply the ``replace`` method (see + :meth:`sympy.core.basic.Basic.replace`). + + Parameters + ========== + + query : + First argument passed to replace. + value : + Second argument passed to replace. + + Examples + ======== + + >>> from sympy import Symbol + >>> from sympy.codegen.rewriting import ReplaceOptim + >>> from sympy.codegen.cfunctions import exp2 + >>> x = Symbol('x') + >>> exp2_opt = ReplaceOptim(lambda p: p.is_Pow and p.base == 2, + ... lambda p: exp2(p.exp)) + >>> exp2_opt(2**x) + exp2(x) + + """ + + def __init__(self, query, value, **kwargs): + super().__init__(**kwargs) + self.query = query + self.value = value + + def __call__(self, expr): + return expr.replace(self.query, self.value) + + +def optimize(expr, optimizations): + """ Apply optimizations to an expression. + + Parameters + ========== + + expr : expression + optimizations : iterable of ``Optimization`` instances + The optimizations will be sorted with respect to ``priority`` (highest first). + + Examples + ======== + + >>> from sympy import log, Symbol + >>> from sympy.codegen.rewriting import optims_c99, optimize + >>> x = Symbol('x') + >>> optimize(log(x+3)/log(2) + log(x**2 + 1), optims_c99) + log1p(x**2) + log2(x + 3) + + """ + + for optim in sorted(optimizations, key=lambda opt: opt.priority, reverse=True): + new_expr = optim(expr) + if optim.cost_function is None: + expr = new_expr + else: + expr = optim.cheapest(expr, new_expr) + return expr + + +exp2_opt = ReplaceOptim( + lambda p: p.is_Pow and p.base == 2, + lambda p: exp2(p.exp) +) + + +_d = Wild('d', properties=[lambda x: x.is_Dummy]) +_u = Wild('u', properties=[lambda x: not x.is_number and not x.is_Add]) +_v = Wild('v') +_w = Wild('w') +_n = Wild('n', properties=[lambda x: x.is_number]) + +sinc_opt1 = ReplaceOptim( + sin(_w)/_w, sinc(_w) +) +sinc_opt2 = ReplaceOptim( + sin(_n*_w)/_w, _n*sinc(_n*_w) +) +sinc_opts = (sinc_opt1, sinc_opt2) + +log2_opt = ReplaceOptim(_v*log(_w)/log(2), _v*log2(_w), cost_function=lambda expr: expr.count( + lambda e: ( # division & eval of transcendentals are expensive floating point operations... + e.is_Pow and e.exp.is_negative # division + or (isinstance(e, (log, log2)) and not e.args[0].is_number)) # transcendental + ) +) + +log2const_opt = ReplaceOptim(log(2)*log2(_w), log(_w)) + +logsumexp_2terms_opt = ReplaceOptim( + lambda l: (isinstance(l, log) + and l.args[0].is_Add + and len(l.args[0].args) == 2 + and all(isinstance(t, exp) for t in l.args[0].args)), + lambda l: ( + Max(*[e.args[0] for e in l.args[0].args]) + + log1p(exp(Min(*[e.args[0] for e in l.args[0].args]))) + ) +) + + +class FuncMinusOneOptim(ReplaceOptim): + """Specialization of ReplaceOptim for functions evaluating "f(x) - 1". + + Explanation + =========== + + Numerical functions which go toward one as x go toward zero is often best + implemented by a dedicated function in order to avoid catastrophic + cancellation. One such example is ``expm1(x)`` in the C standard library + which evaluates ``exp(x) - 1``. Such functions preserves many more + significant digits when its argument is much smaller than one, compared + to subtracting one afterwards. + + Parameters + ========== + + func : + The function which is subtracted by one. + func_m_1 : + The specialized function evaluating ``func(x) - 1``. + opportunistic : bool + When ``True``, apply the transformation as long as the magnitude of the + remaining number terms decreases. When ``False``, only apply the + transformation if it completely eliminates the number term. + + Examples + ======== + + >>> from sympy import symbols, exp + >>> from sympy.codegen.rewriting import FuncMinusOneOptim + >>> from sympy.codegen.cfunctions import expm1 + >>> x, y = symbols('x y') + >>> expm1_opt = FuncMinusOneOptim(exp, expm1) + >>> expm1_opt(exp(x) + 2*exp(5*y) - 3) + expm1(x) + 2*expm1(5*y) + + + """ + + def __init__(self, func, func_m_1, opportunistic=True): + weight = 10 # <-- this is an arbitrary number (heuristic) + super().__init__(lambda e: e.is_Add, self.replace_in_Add, + cost_function=lambda expr: expr.count_ops() - weight*expr.count(func_m_1)) + self.func = func + self.func_m_1 = func_m_1 + self.opportunistic = opportunistic + + def _group_Add_terms(self, add): + numbers, non_num = sift(add.args, lambda arg: arg.is_number, binary=True) + numsum = sum(numbers) + terms_with_func, other = sift(non_num, lambda arg: arg.has(self.func), binary=True) + return numsum, terms_with_func, other + + def replace_in_Add(self, e): + """ passed as second argument to Basic.replace(...) """ + numsum, terms_with_func, other_non_num_terms = self._group_Add_terms(e) + if numsum == 0: + return e + substituted, untouched = [], [] + for with_func in terms_with_func: + if with_func.is_Mul: + func, coeff = sift(with_func.args, lambda arg: arg.func == self.func, binary=True) + if len(func) == 1 and len(coeff) == 1: + func, coeff = func[0], coeff[0] + else: + coeff = None + elif with_func.func == self.func: + func, coeff = with_func, S.One + else: + coeff = None + + if coeff is not None and coeff.is_number and sign(coeff) == -sign(numsum): + if self.opportunistic: + do_substitute = abs(coeff+numsum) < abs(numsum) + else: + do_substitute = coeff+numsum == 0 + + if do_substitute: # advantageous substitution + numsum += coeff + substituted.append(coeff*self.func_m_1(*func.args)) + continue + untouched.append(with_func) + + return e.func(numsum, *substituted, *untouched, *other_non_num_terms) + + def __call__(self, expr): + alt1 = super().__call__(expr) + alt2 = super().__call__(expr.factor()) + return self.cheapest(alt1, alt2) + + +expm1_opt = FuncMinusOneOptim(exp, expm1) +cosm1_opt = FuncMinusOneOptim(cos, cosm1) +powm1_opt = FuncMinusOneOptim(Pow, powm1) + +log1p_opt = ReplaceOptim( + lambda e: isinstance(e, log), + lambda l: expand_log(l.replace( + log, lambda arg: log(arg.factor()) + )).replace(log(_u+1), log1p(_u)) +) + +def create_expand_pow_optimization(limit, *, base_req=lambda b: b.is_symbol): + """ Creates an instance of :class:`ReplaceOptim` for expanding ``Pow``. + + Explanation + =========== + + The requirements for expansions are that the base needs to be a symbol + and the exponent needs to be an Integer (and be less than or equal to + ``limit``). + + Parameters + ========== + + limit : int + The highest power which is expanded into multiplication. + base_req : function returning bool + Requirement on base for expansion to happen, default is to return + the ``is_symbol`` attribute of the base. + + Examples + ======== + + >>> from sympy import Symbol, sin + >>> from sympy.codegen.rewriting import create_expand_pow_optimization + >>> x = Symbol('x') + >>> expand_opt = create_expand_pow_optimization(3) + >>> expand_opt(x**5 + x**3) + x**5 + x*x*x + >>> expand_opt(x**5 + x**3 + sin(x)**3) + x**5 + sin(x)**3 + x*x*x + >>> opt2 = create_expand_pow_optimization(3, base_req=lambda b: not b.is_Function) + >>> opt2((x+1)**2 + sin(x)**2) + sin(x)**2 + (x + 1)*(x + 1) + + """ + return ReplaceOptim( + lambda e: e.is_Pow and base_req(e.base) and e.exp.is_Integer and abs(e.exp) <= limit, + lambda p: ( + UnevaluatedExpr(Mul(*([p.base]*+p.exp), evaluate=False)) if p.exp > 0 else + 1/UnevaluatedExpr(Mul(*([p.base]*-p.exp), evaluate=False)) + )) + +# Optimization procedures for turning A**(-1) * x into MatrixSolve(A, x) +def _matinv_predicate(expr): + # TODO: We should be able to support more than 2 elements + if expr.is_MatMul and len(expr.args) == 2: + left, right = expr.args + if left.is_Inverse and right.shape[1] == 1: + inv_arg = left.arg + if isinstance(inv_arg, MatrixSymbol): + return bool(ask(Q.fullrank(left.arg))) + + return False + +def _matinv_transform(expr): + left, right = expr.args + inv_arg = left.arg + return MatrixSolve(inv_arg, right) + + +matinv_opt = ReplaceOptim(_matinv_predicate, _matinv_transform) + + +logaddexp_opt = ReplaceOptim(log(exp(_v)+exp(_w)), logaddexp(_v, _w)) +logaddexp2_opt = ReplaceOptim(log(Pow(2, _v)+Pow(2, _w)), logaddexp2(_v, _w)*log(2)) + +# Collections of optimizations: +optims_c99 = (expm1_opt, log1p_opt, exp2_opt, log2_opt, log2const_opt) + +optims_numpy = optims_c99 + (logaddexp_opt, logaddexp2_opt,) + sinc_opts + +optims_scipy = (cosm1_opt, powm1_opt) diff --git a/lib/python3.12/site-packages/sympy/codegen/scipy_nodes.py b/lib/python3.12/site-packages/sympy/codegen/scipy_nodes.py new file mode 100644 index 0000000000000000000000000000000000000000..059a853fc8cac6e0b9a1a3c7395dd3a15384dcba --- /dev/null +++ b/lib/python3.12/site-packages/sympy/codegen/scipy_nodes.py @@ -0,0 +1,79 @@ +from sympy.core.function import Add, ArgumentIndexError, Function +from sympy.core.power import Pow +from sympy.core.singleton import S +from sympy.functions.elementary.exponential import log +from sympy.functions.elementary.trigonometric import cos, sin + + +def _cosm1(x, *, evaluate=True): + return Add(cos(x, evaluate=evaluate), -S.One, evaluate=evaluate) + + +class cosm1(Function): + """ Minus one plus cosine of x, i.e. cos(x) - 1. For use when x is close to zero. + + Helper class for use with e.g. scipy.special.cosm1 + See: https://docs.scipy.org/doc/scipy/reference/generated/scipy.special.cosm1.html + """ + nargs = 1 + + def fdiff(self, argindex=1): + """ + Returns the first derivative of this function. + """ + if argindex == 1: + return -sin(*self.args) + else: + raise ArgumentIndexError(self, argindex) + + def _eval_rewrite_as_cos(self, x, **kwargs): + return _cosm1(x) + + def _eval_evalf(self, *args, **kwargs): + return self.rewrite(cos).evalf(*args, **kwargs) + + def _eval_simplify(self, **kwargs): + x, = self.args + candidate = _cosm1(x.simplify(**kwargs)) + if candidate != _cosm1(x, evaluate=False): + return candidate + else: + return cosm1(x) + + +def _powm1(x, y, *, evaluate=True): + return Add(Pow(x, y, evaluate=evaluate), -S.One, evaluate=evaluate) + + +class powm1(Function): + """ Minus one plus x to the power of y, i.e. x**y - 1. For use when x is close to one or y is close to zero. + + Helper class for use with e.g. scipy.special.powm1 + See: https://docs.scipy.org/doc/scipy/reference/generated/scipy.special.powm1.html + """ + nargs = 2 + + def fdiff(self, argindex=1): + """ + Returns the first derivative of this function. + """ + if argindex == 1: + return Pow(self.args[0], self.args[1])*self.args[1]/self.args[0] + elif argindex == 2: + return log(self.args[0])*Pow(*self.args) + else: + raise ArgumentIndexError(self, argindex) + + def _eval_rewrite_as_Pow(self, x, y, **kwargs): + return _powm1(x, y) + + def _eval_evalf(self, *args, **kwargs): + return self.rewrite(Pow).evalf(*args, **kwargs) + + def _eval_simplify(self, **kwargs): + x, y = self.args + candidate = _powm1(x.simplify(**kwargs), y.simplify(**kwargs)) + if candidate != _powm1(x, y, evaluate=False): + return candidate + else: + return powm1(x, y) diff --git a/lib/python3.12/site-packages/sympy/codegen/tests/__init__.py b/lib/python3.12/site-packages/sympy/codegen/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/lib/python3.12/site-packages/sympy/codegen/tests/__pycache__/__init__.cpython-312.pyc b/lib/python3.12/site-packages/sympy/codegen/tests/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3a13e7afb1c44c2fcfd13871182193fea5343eca Binary files /dev/null and b/lib/python3.12/site-packages/sympy/codegen/tests/__pycache__/__init__.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/codegen/tests/__pycache__/test_abstract_nodes.cpython-312.pyc b/lib/python3.12/site-packages/sympy/codegen/tests/__pycache__/test_abstract_nodes.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b7c04ae568c6ef321728911c19d590c1d1e92ac4 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/codegen/tests/__pycache__/test_abstract_nodes.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/codegen/tests/__pycache__/test_applications.cpython-312.pyc b/lib/python3.12/site-packages/sympy/codegen/tests/__pycache__/test_applications.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..859005b988ca7458b7622a4cd56fb0cc48f6ce64 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/codegen/tests/__pycache__/test_applications.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/codegen/tests/__pycache__/test_approximations.cpython-312.pyc b/lib/python3.12/site-packages/sympy/codegen/tests/__pycache__/test_approximations.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0b2099f2b2a8b7899ecc0dc9d387d3a217348930 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/codegen/tests/__pycache__/test_approximations.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/codegen/tests/__pycache__/test_ast.cpython-312.pyc b/lib/python3.12/site-packages/sympy/codegen/tests/__pycache__/test_ast.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ad8c503fde0f9e323186639d58c914ac4cc07054 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/codegen/tests/__pycache__/test_ast.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/codegen/tests/__pycache__/test_cfunctions.cpython-312.pyc b/lib/python3.12/site-packages/sympy/codegen/tests/__pycache__/test_cfunctions.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5abfbd4cf92e1e95d9a58a3b636adbe232fdeb3b Binary files /dev/null and b/lib/python3.12/site-packages/sympy/codegen/tests/__pycache__/test_cfunctions.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/codegen/tests/__pycache__/test_cnodes.cpython-312.pyc b/lib/python3.12/site-packages/sympy/codegen/tests/__pycache__/test_cnodes.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f3eef1e2f61234df3e8e909795fecbd48339fdc2 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/codegen/tests/__pycache__/test_cnodes.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/codegen/tests/__pycache__/test_cxxnodes.cpython-312.pyc b/lib/python3.12/site-packages/sympy/codegen/tests/__pycache__/test_cxxnodes.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4dec879ded9d59515404c6302d908534b38d7857 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/codegen/tests/__pycache__/test_cxxnodes.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/codegen/tests/__pycache__/test_fnodes.cpython-312.pyc b/lib/python3.12/site-packages/sympy/codegen/tests/__pycache__/test_fnodes.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4fdeda84f9d67031fd1bda278885d47b959fbe39 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/codegen/tests/__pycache__/test_fnodes.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/codegen/tests/__pycache__/test_matrix_nodes.cpython-312.pyc b/lib/python3.12/site-packages/sympy/codegen/tests/__pycache__/test_matrix_nodes.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..091273cb0ec8710a36f5d7690ae2ae3447168f85 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/codegen/tests/__pycache__/test_matrix_nodes.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/codegen/tests/__pycache__/test_numpy_nodes.cpython-312.pyc b/lib/python3.12/site-packages/sympy/codegen/tests/__pycache__/test_numpy_nodes.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4a6a0809241c85a675b5f058307ed35e7b71e206 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/codegen/tests/__pycache__/test_numpy_nodes.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/codegen/tests/__pycache__/test_pynodes.cpython-312.pyc b/lib/python3.12/site-packages/sympy/codegen/tests/__pycache__/test_pynodes.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a76f6130d177e90d193788451a38c872c1f4fc33 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/codegen/tests/__pycache__/test_pynodes.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/codegen/tests/__pycache__/test_pyutils.cpython-312.pyc b/lib/python3.12/site-packages/sympy/codegen/tests/__pycache__/test_pyutils.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b436f29fea86faaf78229b0810decd5f53e7b6e2 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/codegen/tests/__pycache__/test_pyutils.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/codegen/tests/test_abstract_nodes.py b/lib/python3.12/site-packages/sympy/codegen/tests/test_abstract_nodes.py new file mode 100644 index 0000000000000000000000000000000000000000..89e1f73ff8cb24a4a865aa51304ec66e9901e3cb --- /dev/null +++ b/lib/python3.12/site-packages/sympy/codegen/tests/test_abstract_nodes.py @@ -0,0 +1,14 @@ +from sympy.core.symbol import symbols +from sympy.codegen.abstract_nodes import List + + +def test_List(): + l = List(2, 3, 4) + assert l == List(2, 3, 4) + assert str(l) == "[2, 3, 4]" + x, y, z = symbols('x y z') + l = List(x**2,y**3,z**4) + # contrary to python's built-in list, we can call e.g. "replace" on List. + m = l.replace(lambda arg: arg.is_Pow and arg.exp>2, lambda p: p.base-p.exp) + assert m == [x**2, y-3, z-4] + hash(m) diff --git a/lib/python3.12/site-packages/sympy/codegen/tests/test_algorithms.py b/lib/python3.12/site-packages/sympy/codegen/tests/test_algorithms.py new file mode 100644 index 0000000000000000000000000000000000000000..09446258d461d71e299408555399c7f09fbd8419 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/codegen/tests/test_algorithms.py @@ -0,0 +1,179 @@ +import tempfile +from sympy import log, Min, Max, sqrt +from sympy.core.numbers import Float +from sympy.core.symbol import Symbol, symbols +from sympy.functions.elementary.trigonometric import cos +from sympy.codegen.ast import Assignment, Raise, RuntimeError_, QuotedString +from sympy.codegen.algorithms import newtons_method, newtons_method_function +from sympy.codegen.cfunctions import expm1 +from sympy.codegen.fnodes import bind_C +from sympy.codegen.futils import render_as_module as f_module +from sympy.codegen.pyutils import render_as_module as py_module +from sympy.external import import_module +from sympy.printing.codeprinter import ccode +from sympy.utilities._compilation import compile_link_import_strings, has_c, has_fortran +from sympy.utilities._compilation.util import may_xfail +from sympy.testing.pytest import skip, raises + +cython = import_module('cython') +wurlitzer = import_module('wurlitzer') + +def test_newtons_method(): + x, dx, atol = symbols('x dx atol') + expr = cos(x) - x**3 + algo = newtons_method(expr, x, atol, dx) + assert algo.has(Assignment(dx, -expr/expr.diff(x))) + + +@may_xfail +def test_newtons_method_function__ccode(): + x = Symbol('x', real=True) + expr = cos(x) - x**3 + func = newtons_method_function(expr, x) + + if not cython: + skip("cython not installed.") + if not has_c(): + skip("No C compiler found.") + + compile_kw = {"std": 'c99'} + with tempfile.TemporaryDirectory() as folder: + mod, info = compile_link_import_strings([ + ('newton.c', ('#include \n' + '#include \n') + ccode(func)), + ('_newton.pyx', ("#cython: language_level={}\n".format("3") + + "cdef extern double newton(double)\n" + "def py_newton(x):\n" + " return newton(x)\n")) + ], build_dir=folder, compile_kwargs=compile_kw) + assert abs(mod.py_newton(0.5) - 0.865474033102) < 1e-12 + + +@may_xfail +def test_newtons_method_function__fcode(): + x = Symbol('x', real=True) + expr = cos(x) - x**3 + func = newtons_method_function(expr, x, attrs=[bind_C(name='newton')]) + + if not cython: + skip("cython not installed.") + if not has_fortran(): + skip("No Fortran compiler found.") + + f_mod = f_module([func], 'mod_newton') + with tempfile.TemporaryDirectory() as folder: + mod, info = compile_link_import_strings([ + ('newton.f90', f_mod), + ('_newton.pyx', ("#cython: language_level={}\n".format("3") + + "cdef extern double newton(double*)\n" + "def py_newton(double x):\n" + " return newton(&x)\n")) + ], build_dir=folder) + assert abs(mod.py_newton(0.5) - 0.865474033102) < 1e-12 + + +def test_newtons_method_function__pycode(): + x = Symbol('x', real=True) + expr = cos(x) - x**3 + func = newtons_method_function(expr, x) + py_mod = py_module(func) + namespace = {} + exec(py_mod, namespace, namespace) + res = eval('newton(0.5)', namespace) + assert abs(res - 0.865474033102) < 1e-12 + + +@may_xfail +def test_newtons_method_function__ccode_parameters(): + args = x, A, k, p = symbols('x A k p') + expr = A*cos(k*x) - p*x**3 + raises(ValueError, lambda: newtons_method_function(expr, x)) + use_wurlitzer = wurlitzer + + func = newtons_method_function(expr, x, args, debug=use_wurlitzer) + + if not has_c(): + skip("No C compiler found.") + if not cython: + skip("cython not installed.") + + compile_kw = {"std": 'c99'} + with tempfile.TemporaryDirectory() as folder: + mod, info = compile_link_import_strings([ + ('newton_par.c', ('#include \n' + '#include \n') + ccode(func)), + ('_newton_par.pyx', ("#cython: language_level={}\n".format("3") + + "cdef extern double newton(double, double, double, double)\n" + "def py_newton(x, A=1, k=1, p=1):\n" + " return newton(x, A, k, p)\n")) + ], compile_kwargs=compile_kw, build_dir=folder) + + if use_wurlitzer: + with wurlitzer.pipes() as (out, err): + result = mod.py_newton(0.5) + else: + result = mod.py_newton(0.5) + + assert abs(result - 0.865474033102) < 1e-12 + + if not use_wurlitzer: + skip("C-level output only tested when package 'wurlitzer' is available.") + + out, err = out.read(), err.read() + assert err == '' + assert out == """\ +x= 0.5 +x= 1.1121 d_x= 0.61214 +x= 0.90967 d_x= -0.20247 +x= 0.86726 d_x= -0.042409 +x= 0.86548 d_x= -0.0017867 +x= 0.86547 d_x= -3.1022e-06 +x= 0.86547 d_x= -9.3421e-12 +x= 0.86547 d_x= 3.6902e-17 +""" # try to run tests with LC_ALL=C if this assertion fails + + +def test_newtons_method_function__rtol_cse_nan(): + a, b, c, N_geo, N_tot = symbols('a b c N_geo N_tot', real=True, nonnegative=True) + i = Symbol('i', integer=True, nonnegative=True) + N_ari = N_tot - N_geo - 1 + delta_ari = (c-b)/N_ari + ln_delta_geo = log(b) + log(-expm1((log(a)-log(b))/N_geo)) + eqb_log = ln_delta_geo - log(delta_ari) + + def _clamp(low, expr, high): + return Min(Max(low, expr), high) + + meth_kw = { + 'clamped_newton': {'delta_fn': lambda e, x: _clamp( + (sqrt(a*x)-x)*0.99, + -e/e.diff(x), + (sqrt(c*x)-x)*0.99 + )}, + 'halley': {'delta_fn': lambda e, x: (-2*(e*e.diff(x))/(2*e.diff(x)**2 - e*e.diff(x, 2)))}, + 'halley_alt': {'delta_fn': lambda e, x: (-e/e.diff(x)/(1-e/e.diff(x)*e.diff(x,2)/2/e.diff(x)))}, + } + args = eqb_log, b + for use_cse in [False, True]: + kwargs = { + 'params': (b, a, c, N_geo, N_tot), 'itermax': 60, 'debug': True, 'cse': use_cse, + 'counter': i, 'atol': 1e-100, 'rtol': 2e-16, 'bounds': (a,c), + 'handle_nan': Raise(RuntimeError_(QuotedString("encountered NaN."))) + } + func = {k: newtons_method_function(*args, func_name=f"{k}_b", **dict(kwargs, **kw)) for k, kw in meth_kw.items()} + py_mod = {k: py_module(v) for k, v in func.items()} + namespace = {} + root_find_b = {} + for k, v in py_mod.items(): + ns = namespace[k] = {} + exec(v, ns, ns) + root_find_b[k] = ns[f'{k}_b'] + ref = Float('13.2261515064168768938151923226496') + reftol = {'clamped_newton': 2e-16, 'halley': 2e-16, 'halley_alt': 3e-16} + guess = 4.0 + for meth, func in root_find_b.items(): + result = func(guess, 1e-2, 1e2, 50, 100) + req = ref*reftol[meth] + if use_cse: + req *= 2 + assert abs(result - ref) < req diff --git a/lib/python3.12/site-packages/sympy/codegen/tests/test_applications.py b/lib/python3.12/site-packages/sympy/codegen/tests/test_applications.py new file mode 100644 index 0000000000000000000000000000000000000000..26d5d0f699b947db13b658d793f808d632f67a1a --- /dev/null +++ b/lib/python3.12/site-packages/sympy/codegen/tests/test_applications.py @@ -0,0 +1,57 @@ +# This file contains tests that exercise multiple AST nodes + +import tempfile + +from sympy.external import import_module +from sympy.printing.codeprinter import ccode +from sympy.utilities._compilation import compile_link_import_strings, has_c +from sympy.utilities._compilation.util import may_xfail +from sympy.testing.pytest import skip +from sympy.codegen.ast import ( + FunctionDefinition, FunctionPrototype, Variable, Pointer, real, Assignment, + integer, CodeBlock, While +) +from sympy.codegen.cnodes import void, PreIncrement +from sympy.codegen.cutils import render_as_source_file + +cython = import_module('cython') +np = import_module('numpy') + +def _mk_func1(): + declars = n, inp, out = Variable('n', integer), Pointer('inp', real), Pointer('out', real) + i = Variable('i', integer) + whl = While(i2, lambda p: p.base-p.exp) + assert m == [x**2, y-3, z-4] diff --git a/lib/python3.12/site-packages/sympy/codegen/tests/test_pyutils.py b/lib/python3.12/site-packages/sympy/codegen/tests/test_pyutils.py new file mode 100644 index 0000000000000000000000000000000000000000..0a2f0ff358f333635c8d44195a5c39d63ac8f16f --- /dev/null +++ b/lib/python3.12/site-packages/sympy/codegen/tests/test_pyutils.py @@ -0,0 +1,7 @@ +from sympy.codegen.ast import Print +from sympy.codegen.pyutils import render_as_module + +def test_standard(): + ast = Print('x y'.split(), r"coordinate: %12.5g %12.5g\n") + assert render_as_module(ast, standard='python3') == \ + '\n\nprint("coordinate: %12.5g %12.5g\\n" % (x, y), end="")' diff --git a/lib/python3.12/site-packages/sympy/codegen/tests/test_rewriting.py b/lib/python3.12/site-packages/sympy/codegen/tests/test_rewriting.py new file mode 100644 index 0000000000000000000000000000000000000000..51e0c9ecc940f60186cc04d4bf15650281d31cd8 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/codegen/tests/test_rewriting.py @@ -0,0 +1,479 @@ +import tempfile +from sympy.core.numbers import pi, Rational +from sympy.core.power import Pow +from sympy.core.singleton import S +from sympy.core.symbol import Symbol +from sympy.functions.elementary.complexes import Abs +from sympy.functions.elementary.exponential import (exp, log) +from sympy.functions.elementary.trigonometric import (cos, sin, sinc) +from sympy.matrices.expressions.matexpr import MatrixSymbol +from sympy.assumptions import assuming, Q +from sympy.external import import_module +from sympy.printing.codeprinter import ccode +from sympy.codegen.matrix_nodes import MatrixSolve +from sympy.codegen.cfunctions import log2, exp2, expm1, log1p +from sympy.codegen.numpy_nodes import logaddexp, logaddexp2 +from sympy.codegen.scipy_nodes import cosm1, powm1 +from sympy.codegen.rewriting import ( + optimize, cosm1_opt, log2_opt, exp2_opt, expm1_opt, log1p_opt, powm1_opt, optims_c99, + create_expand_pow_optimization, matinv_opt, logaddexp_opt, logaddexp2_opt, + optims_numpy, optims_scipy, sinc_opts, FuncMinusOneOptim +) +from sympy.testing.pytest import XFAIL, skip +from sympy.utilities import lambdify +from sympy.utilities._compilation import compile_link_import_strings, has_c +from sympy.utilities._compilation.util import may_xfail + +cython = import_module('cython') +numpy = import_module('numpy') +scipy = import_module('scipy') + + +def test_log2_opt(): + x = Symbol('x') + expr1 = 7*log(3*x + 5)/(log(2)) + opt1 = optimize(expr1, [log2_opt]) + assert opt1 == 7*log2(3*x + 5) + assert opt1.rewrite(log) == expr1 + + expr2 = 3*log(5*x + 7)/(13*log(2)) + opt2 = optimize(expr2, [log2_opt]) + assert opt2 == 3*log2(5*x + 7)/13 + assert opt2.rewrite(log) == expr2 + + expr3 = log(x)/log(2) + opt3 = optimize(expr3, [log2_opt]) + assert opt3 == log2(x) + assert opt3.rewrite(log) == expr3 + + expr4 = log(x)/log(2) + log(x+1) + opt4 = optimize(expr4, [log2_opt]) + assert opt4 == log2(x) + log(2)*log2(x+1) + assert opt4.rewrite(log) == expr4 + + expr5 = log(17) + opt5 = optimize(expr5, [log2_opt]) + assert opt5 == expr5 + + expr6 = log(x + 3)/log(2) + opt6 = optimize(expr6, [log2_opt]) + assert str(opt6) == 'log2(x + 3)' + assert opt6.rewrite(log) == expr6 + + +def test_exp2_opt(): + x = Symbol('x') + expr1 = 1 + 2**x + opt1 = optimize(expr1, [exp2_opt]) + assert opt1 == 1 + exp2(x) + assert opt1.rewrite(Pow) == expr1 + + expr2 = 1 + 3**x + assert expr2 == optimize(expr2, [exp2_opt]) + + +def test_expm1_opt(): + x = Symbol('x') + + expr1 = exp(x) - 1 + opt1 = optimize(expr1, [expm1_opt]) + assert expm1(x) - opt1 == 0 + assert opt1.rewrite(exp) == expr1 + + expr2 = 3*exp(x) - 3 + opt2 = optimize(expr2, [expm1_opt]) + assert 3*expm1(x) == opt2 + assert opt2.rewrite(exp) == expr2 + + expr3 = 3*exp(x) - 5 + opt3 = optimize(expr3, [expm1_opt]) + assert 3*expm1(x) - 2 == opt3 + assert opt3.rewrite(exp) == expr3 + expm1_opt_non_opportunistic = FuncMinusOneOptim(exp, expm1, opportunistic=False) + assert expr3 == optimize(expr3, [expm1_opt_non_opportunistic]) + assert opt1 == optimize(expr1, [expm1_opt_non_opportunistic]) + assert opt2 == optimize(expr2, [expm1_opt_non_opportunistic]) + + expr4 = 3*exp(x) + log(x) - 3 + opt4 = optimize(expr4, [expm1_opt]) + assert 3*expm1(x) + log(x) == opt4 + assert opt4.rewrite(exp) == expr4 + + expr5 = 3*exp(2*x) - 3 + opt5 = optimize(expr5, [expm1_opt]) + assert 3*expm1(2*x) == opt5 + assert opt5.rewrite(exp) == expr5 + + expr6 = (2*exp(x) + 1)/(exp(x) + 1) + 1 + opt6 = optimize(expr6, [expm1_opt]) + assert opt6.count_ops() <= expr6.count_ops() + + def ev(e): + return e.subs(x, 3).evalf() + assert abs(ev(expr6) - ev(opt6)) < 1e-15 + + y = Symbol('y') + expr7 = (2*exp(x) - 1)/(1 - exp(y)) - 1/(1-exp(y)) + opt7 = optimize(expr7, [expm1_opt]) + assert -2*expm1(x)/expm1(y) == opt7 + assert (opt7.rewrite(exp) - expr7).factor() == 0 + + expr8 = (1+exp(x))**2 - 4 + opt8 = optimize(expr8, [expm1_opt]) + tgt8a = (exp(x) + 3)*expm1(x) + tgt8b = 2*expm1(x) + expm1(2*x) + # Both tgt8a & tgt8b seem to give full precision (~16 digits for double) + # for x=1e-7 (compare with expr8 which only achieves ~8 significant digits). + # If we can show that either tgt8a or tgt8b is preferable, we can + # change this test to ensure the preferable version is returned. + assert (tgt8a - tgt8b).rewrite(exp).factor() == 0 + assert opt8 in (tgt8a, tgt8b) + assert (opt8.rewrite(exp) - expr8).factor() == 0 + + expr9 = sin(expr8) + opt9 = optimize(expr9, [expm1_opt]) + tgt9a = sin(tgt8a) + tgt9b = sin(tgt8b) + assert opt9 in (tgt9a, tgt9b) + assert (opt9.rewrite(exp) - expr9.rewrite(exp)).factor().is_zero + + +def test_expm1_two_exp_terms(): + x, y = map(Symbol, 'x y'.split()) + expr1 = exp(x) + exp(y) - 2 + opt1 = optimize(expr1, [expm1_opt]) + assert opt1 == expm1(x) + expm1(y) + + +def test_cosm1_opt(): + x = Symbol('x') + + expr1 = cos(x) - 1 + opt1 = optimize(expr1, [cosm1_opt]) + assert cosm1(x) - opt1 == 0 + assert opt1.rewrite(cos) == expr1 + + expr2 = 3*cos(x) - 3 + opt2 = optimize(expr2, [cosm1_opt]) + assert 3*cosm1(x) == opt2 + assert opt2.rewrite(cos) == expr2 + + expr3 = 3*cos(x) - 5 + opt3 = optimize(expr3, [cosm1_opt]) + assert 3*cosm1(x) - 2 == opt3 + assert opt3.rewrite(cos) == expr3 + cosm1_opt_non_opportunistic = FuncMinusOneOptim(cos, cosm1, opportunistic=False) + assert expr3 == optimize(expr3, [cosm1_opt_non_opportunistic]) + assert opt1 == optimize(expr1, [cosm1_opt_non_opportunistic]) + assert opt2 == optimize(expr2, [cosm1_opt_non_opportunistic]) + + expr4 = 3*cos(x) + log(x) - 3 + opt4 = optimize(expr4, [cosm1_opt]) + assert 3*cosm1(x) + log(x) == opt4 + assert opt4.rewrite(cos) == expr4 + + expr5 = 3*cos(2*x) - 3 + opt5 = optimize(expr5, [cosm1_opt]) + assert 3*cosm1(2*x) == opt5 + assert opt5.rewrite(cos) == expr5 + + expr6 = 2 - 2*cos(x) + opt6 = optimize(expr6, [cosm1_opt]) + assert -2*cosm1(x) == opt6 + assert opt6.rewrite(cos) == expr6 + + +def test_cosm1_two_cos_terms(): + x, y = map(Symbol, 'x y'.split()) + expr1 = cos(x) + cos(y) - 2 + opt1 = optimize(expr1, [cosm1_opt]) + assert opt1 == cosm1(x) + cosm1(y) + + +def test_expm1_cosm1_mixed(): + x = Symbol('x') + expr1 = exp(x) + cos(x) - 2 + opt1 = optimize(expr1, [expm1_opt, cosm1_opt]) + assert opt1 == cosm1(x) + expm1(x) + + +def _check_num_lambdify(expr, opt, val_subs, approx_ref, lambdify_kw=None, poorness=1e10): + """ poorness=1e10 signifies that `expr` loses precision of at least ten decimal digits. """ + num_ref = expr.subs(val_subs).evalf() + eps = numpy.finfo(numpy.float64).eps + assert abs(num_ref - approx_ref) < approx_ref*eps + f1 = lambdify(list(val_subs.keys()), opt, **(lambdify_kw or {})) + args_float = tuple(map(float, val_subs.values())) + num_err1 = abs(f1(*args_float) - approx_ref) + assert num_err1 < abs(num_ref*eps) + f2 = lambdify(list(val_subs.keys()), expr, **(lambdify_kw or {})) + num_err2 = abs(f2(*args_float) - approx_ref) + assert num_err2 > abs(num_ref*eps*poorness) # this only ensures that the *test* works as intended + + +def test_cosm1_apart(): + x = Symbol('x') + + expr1 = 1/cos(x) - 1 + opt1 = optimize(expr1, [cosm1_opt]) + assert opt1 == -cosm1(x)/cos(x) + if scipy: + _check_num_lambdify(expr1, opt1, {x: S(10)**-30}, 5e-61, lambdify_kw={"modules": 'scipy'}) + + expr2 = 2/cos(x) - 2 + opt2 = optimize(expr2, optims_scipy) + assert opt2 == -2*cosm1(x)/cos(x) + if scipy: + _check_num_lambdify(expr2, opt2, {x: S(10)**-30}, 1e-60, lambdify_kw={"modules": 'scipy'}) + + expr3 = pi/cos(3*x) - pi + opt3 = optimize(expr3, [cosm1_opt]) + assert opt3 == -pi*cosm1(3*x)/cos(3*x) + if scipy: + _check_num_lambdify(expr3, opt3, {x: S(10)**-30/3}, float(5e-61*pi), lambdify_kw={"modules": 'scipy'}) + + +def test_powm1(): + args = x, y = map(Symbol, "xy") + + expr1 = x**y - 1 + opt1 = optimize(expr1, [powm1_opt]) + assert opt1 == powm1(x, y) + for arg in args: + assert expr1.diff(arg) == opt1.diff(arg) + if scipy and tuple(map(int, scipy.version.version.split('.')[:3])) >= (1, 10, 0): + subs1_a = {x: Rational(*(1.0+1e-13).as_integer_ratio()), y: pi} + ref1_f64_a = 3.139081648208105e-13 + _check_num_lambdify(expr1, opt1, subs1_a, ref1_f64_a, lambdify_kw={"modules": 'scipy'}, poorness=10**11) + + subs1_b = {x: pi, y: Rational(*(1e-10).as_integer_ratio())} + ref1_f64_b = 1.1447298859149205e-10 + _check_num_lambdify(expr1, opt1, subs1_b, ref1_f64_b, lambdify_kw={"modules": 'scipy'}, poorness=10**9) + + +def test_log1p_opt(): + x = Symbol('x') + expr1 = log(x + 1) + opt1 = optimize(expr1, [log1p_opt]) + assert log1p(x) - opt1 == 0 + assert opt1.rewrite(log) == expr1 + + expr2 = log(3*x + 3) + opt2 = optimize(expr2, [log1p_opt]) + assert log1p(x) + log(3) == opt2 + assert (opt2.rewrite(log) - expr2).simplify() == 0 + + expr3 = log(2*x + 1) + opt3 = optimize(expr3, [log1p_opt]) + assert log1p(2*x) - opt3 == 0 + assert opt3.rewrite(log) == expr3 + + expr4 = log(x+3) + opt4 = optimize(expr4, [log1p_opt]) + assert str(opt4) == 'log(x + 3)' + + +def test_optims_c99(): + x = Symbol('x') + + expr1 = 2**x + log(x)/log(2) + log(x + 1) + exp(x) - 1 + opt1 = optimize(expr1, optims_c99).simplify() + assert opt1 == exp2(x) + log2(x) + log1p(x) + expm1(x) + assert opt1.rewrite(exp).rewrite(log).rewrite(Pow) == expr1 + + expr2 = log(x)/log(2) + log(x + 1) + opt2 = optimize(expr2, optims_c99) + assert opt2 == log2(x) + log1p(x) + assert opt2.rewrite(log) == expr2 + + expr3 = log(x)/log(2) + log(17*x + 17) + opt3 = optimize(expr3, optims_c99) + delta3 = opt3 - (log2(x) + log(17) + log1p(x)) + assert delta3 == 0 + assert (opt3.rewrite(log) - expr3).simplify() == 0 + + expr4 = 2**x + 3*log(5*x + 7)/(13*log(2)) + 11*exp(x) - 11 + log(17*x + 17) + opt4 = optimize(expr4, optims_c99).simplify() + delta4 = opt4 - (exp2(x) + 3*log2(5*x + 7)/13 + 11*expm1(x) + log(17) + log1p(x)) + assert delta4 == 0 + assert (opt4.rewrite(exp).rewrite(log).rewrite(Pow) - expr4).simplify() == 0 + + expr5 = 3*exp(2*x) - 3 + opt5 = optimize(expr5, optims_c99) + delta5 = opt5 - 3*expm1(2*x) + assert delta5 == 0 + assert opt5.rewrite(exp) == expr5 + + expr6 = exp(2*x) - 3 + opt6 = optimize(expr6, optims_c99) + assert opt6 in (expm1(2*x) - 2, expr6) # expm1(2*x) - 2 is not better or worse + + expr7 = log(3*x + 3) + opt7 = optimize(expr7, optims_c99) + delta7 = opt7 - (log(3) + log1p(x)) + assert delta7 == 0 + assert (opt7.rewrite(log) - expr7).simplify() == 0 + + expr8 = log(2*x + 3) + opt8 = optimize(expr8, optims_c99) + assert opt8 == expr8 + + +def test_create_expand_pow_optimization(): + cc = lambda x: ccode( + optimize(x, [create_expand_pow_optimization(4)])) + x = Symbol('x') + assert cc(x**4) == 'x*x*x*x' + assert cc(x**4 + x**2) == 'x*x + x*x*x*x' + assert cc(x**5 + x**4) == 'pow(x, 5) + x*x*x*x' + assert cc(sin(x)**4) == 'pow(sin(x), 4)' + # gh issue 15335 + assert cc(x**(-4)) == '1.0/(x*x*x*x)' + assert cc(x**(-5)) == 'pow(x, -5)' + assert cc(-x**4) == '-(x*x*x*x)' + assert cc(x**4 - x**2) == '-(x*x) + x*x*x*x' + i = Symbol('i', integer=True) + assert cc(x**i - x**2) == 'pow(x, i) - (x*x)' + y = Symbol('y', real=True) + assert cc(Abs(exp(y**4))) == "exp(y*y*y*y)" + + # gh issue 20753 + cc2 = lambda x: ccode(optimize(x, [create_expand_pow_optimization( + 4, base_req=lambda b: b.is_Function)])) + assert cc2(x**3 + sin(x)**3) == "pow(x, 3) + sin(x)*sin(x)*sin(x)" + + +def test_matsolve(): + n = Symbol('n', integer=True) + A = MatrixSymbol('A', n, n) + x = MatrixSymbol('x', n, 1) + + with assuming(Q.fullrank(A)): + assert optimize(A**(-1) * x, [matinv_opt]) == MatrixSolve(A, x) + assert optimize(A**(-1) * x + x, [matinv_opt]) == MatrixSolve(A, x) + x + + +def test_logaddexp_opt(): + x, y = map(Symbol, 'x y'.split()) + expr1 = log(exp(x) + exp(y)) + opt1 = optimize(expr1, [logaddexp_opt]) + assert logaddexp(x, y) - opt1 == 0 + assert logaddexp(y, x) - opt1 == 0 + assert opt1.rewrite(log) == expr1 + + +def test_logaddexp2_opt(): + x, y = map(Symbol, 'x y'.split()) + expr1 = log(2**x + 2**y)/log(2) + opt1 = optimize(expr1, [logaddexp2_opt]) + assert logaddexp2(x, y) - opt1 == 0 + assert logaddexp2(y, x) - opt1 == 0 + assert opt1.rewrite(log) == expr1 + + +def test_sinc_opts(): + def check(d): + for k, v in d.items(): + assert optimize(k, sinc_opts) == v + + x = Symbol('x') + check({ + sin(x)/x : sinc(x), + sin(2*x)/(2*x) : sinc(2*x), + sin(3*x)/x : 3*sinc(3*x), + x*sin(x) : x*sin(x) + }) + + y = Symbol('y') + check({ + sin(x*y)/(x*y) : sinc(x*y), + y*sin(x/y)/x : sinc(x/y), + sin(sin(x))/sin(x) : sinc(sin(x)), + sin(3*sin(x))/sin(x) : 3*sinc(3*sin(x)), + sin(x)/y : sin(x)/y + }) + + +def test_optims_numpy(): + def check(d): + for k, v in d.items(): + assert optimize(k, optims_numpy) == v + + x = Symbol('x') + check({ + sin(2*x)/(2*x) + exp(2*x) - 1: sinc(2*x) + expm1(2*x), + log(x+3)/log(2) + log(x**2 + 1): log1p(x**2) + log2(x+3) + }) + + +@XFAIL # room for improvement, ideally this test case should pass. +def test_optims_numpy_TODO(): + def check(d): + for k, v in d.items(): + assert optimize(k, optims_numpy) == v + + x, y = map(Symbol, 'x y'.split()) + check({ + log(x*y)*sin(x*y)*log(x*y+1)/(log(2)*x*y): log2(x*y)*sinc(x*y)*log1p(x*y), + exp(x*sin(y)/y) - 1: expm1(x*sinc(y)) + }) + + +@may_xfail +def test_compiled_ccode_with_rewriting(): + if not cython: + skip("cython not installed.") + if not has_c(): + skip("No C compiler found.") + + x = Symbol('x') + about_two = 2**(58/S(117))*3**(97/S(117))*5**(4/S(39))*7**(92/S(117))/S(30)*pi + # about_two: 1.999999999999581826 + unchanged = 2*exp(x) - about_two + xval = S(10)**-11 + ref = unchanged.subs(x, xval).n(19) # 2.0418173913673213e-11 + + rewritten = optimize(2*exp(x) - about_two, [expm1_opt]) + + # Unfortunately, we need to call ``.n()`` on our expressions before we hand them + # to ``ccode``, and we need to request a large number of significant digits. + # In this test, results converged for double precision when the following number + # of significant digits were chosen: + NUMBER_OF_DIGITS = 25 # TODO: this should ideally be automatically handled. + + func_c = ''' +#include + +double func_unchanged(double x) { + return %(unchanged)s; +} +double func_rewritten(double x) { + return %(rewritten)s; +} +''' % {"unchanged": ccode(unchanged.n(NUMBER_OF_DIGITS)), + "rewritten": ccode(rewritten.n(NUMBER_OF_DIGITS))} + + func_pyx = ''' +#cython: language_level=3 +cdef extern double func_unchanged(double) +cdef extern double func_rewritten(double) +def py_unchanged(x): + return func_unchanged(x) +def py_rewritten(x): + return func_rewritten(x) +''' + with tempfile.TemporaryDirectory() as folder: + mod, info = compile_link_import_strings( + [('func.c', func_c), ('_func.pyx', func_pyx)], + build_dir=folder, compile_kwargs={"std": 'c99'} + ) + err_rewritten = abs(mod.py_rewritten(1e-11) - ref) + err_unchanged = abs(mod.py_unchanged(1e-11) - ref) + assert 1e-27 < err_rewritten < 1e-25 # highly accurate. + assert 1e-19 < err_unchanged < 1e-16 # quite poor. + + # Tolerances used above were determined as follows: + # >>> no_opt = unchanged.subs(x, xval.evalf()).evalf() + # >>> with_opt = rewritten.n(25).subs(x, 1e-11).evalf() + # >>> with_opt - ref, no_opt - ref + # (1.1536301877952077e-26, 1.6547074214222335e-18) diff --git a/lib/python3.12/site-packages/sympy/codegen/tests/test_scipy_nodes.py b/lib/python3.12/site-packages/sympy/codegen/tests/test_scipy_nodes.py new file mode 100644 index 0000000000000000000000000000000000000000..c0d1461037eec81ade0c99b18fbbf5a4517ce0b7 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/codegen/tests/test_scipy_nodes.py @@ -0,0 +1,44 @@ +from itertools import product +from sympy.core.power import Pow +from sympy.core.symbol import symbols +from sympy.functions.elementary.exponential import exp, log +from sympy.functions.elementary.trigonometric import cos +from sympy.core.numbers import pi +from sympy.codegen.scipy_nodes import cosm1, powm1 + +x, y, z = symbols('x y z') + + +def test_cosm1(): + cm1_xy = cosm1(x*y) + ref_xy = cos(x*y) - 1 + for wrt, deriv_order in product([x, y, z], range(3)): + assert ( + cm1_xy.diff(wrt, deriv_order) - + ref_xy.diff(wrt, deriv_order) + ).rewrite(cos).simplify() == 0 + + expr_minus2 = cosm1(pi) + assert expr_minus2.rewrite(cos) == -2 + assert cosm1(3.14).simplify() == cosm1(3.14) # cannot simplify with 3.14 + assert cosm1(pi/2).simplify() == -1 + assert (1/cos(x) - 1 + cosm1(x)/cos(x)).simplify() == 0 + + +def test_powm1(): + cases = { + powm1(x, y): x**y - 1, + powm1(x*y, z): (x*y)**z - 1, + powm1(x, y*z): x**(y*z)-1, + powm1(x*y*z, x*y*z): (x*y*z)**(x*y*z)-1 + } + for pm1_e, ref_e in cases.items(): + for wrt, deriv_order in product([x, y, z], range(3)): + der = pm1_e.diff(wrt, deriv_order) + ref = ref_e.diff(wrt, deriv_order) + delta = (der - ref).rewrite(Pow) + assert delta.simplify() == 0 + + eulers_constant_m1 = powm1(x, 1/log(x)) + assert eulers_constant_m1.rewrite(Pow) == exp(1) - 1 + assert eulers_constant_m1.simplify() == exp(1) - 1 diff --git a/lib/python3.12/site-packages/sympy/integrals/__pycache__/__init__.cpython-312.pyc b/lib/python3.12/site-packages/sympy/integrals/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b67e79a40a9f0ef5db6ad8c22976473bbacd2d18 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/integrals/__pycache__/__init__.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/integrals/__pycache__/deltafunctions.cpython-312.pyc b/lib/python3.12/site-packages/sympy/integrals/__pycache__/deltafunctions.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2f6bc1bebc25f374c8dd759ca9901f418d7d1f37 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/integrals/__pycache__/deltafunctions.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/integrals/__pycache__/heurisch.cpython-312.pyc b/lib/python3.12/site-packages/sympy/integrals/__pycache__/heurisch.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4ce60409247aa81a94507f4f3f89dc4b37e9697e Binary files /dev/null and b/lib/python3.12/site-packages/sympy/integrals/__pycache__/heurisch.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/integrals/__pycache__/intpoly.cpython-312.pyc b/lib/python3.12/site-packages/sympy/integrals/__pycache__/intpoly.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cf9542a08a1092b5c9a3d99432c997ba10adf0eb Binary files /dev/null and b/lib/python3.12/site-packages/sympy/integrals/__pycache__/intpoly.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/integrals/__pycache__/prde.cpython-312.pyc b/lib/python3.12/site-packages/sympy/integrals/__pycache__/prde.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8d261d462a416f3b5e68e55c5dc9c85614066c5b Binary files /dev/null and b/lib/python3.12/site-packages/sympy/integrals/__pycache__/prde.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/integrals/__pycache__/quadrature.cpython-312.pyc b/lib/python3.12/site-packages/sympy/integrals/__pycache__/quadrature.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6de474c904787e74d47afaf495ad27e370bc7902 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/integrals/__pycache__/quadrature.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/integrals/__pycache__/transforms.cpython-312.pyc b/lib/python3.12/site-packages/sympy/integrals/__pycache__/transforms.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9d6687c65ad9e840721cff5a47655ca54736b139 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/integrals/__pycache__/transforms.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/integrals/__pycache__/trigonometry.cpython-312.pyc b/lib/python3.12/site-packages/sympy/integrals/__pycache__/trigonometry.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4064b580fb8df56180c2f526a311424122bd5caa Binary files /dev/null and b/lib/python3.12/site-packages/sympy/integrals/__pycache__/trigonometry.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/polys/compatibility.py b/lib/python3.12/site-packages/sympy/polys/compatibility.py new file mode 100644 index 0000000000000000000000000000000000000000..ee71d9efedae9cf15f36968a9e04ff90719cda48 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/polys/compatibility.py @@ -0,0 +1,1142 @@ +"""Compatibility interface between dense and sparse polys. """ + + +from sympy.polys.densearith import dup_add_term +from sympy.polys.densearith import dmp_add_term +from sympy.polys.densearith import dup_sub_term +from sympy.polys.densearith import dmp_sub_term +from sympy.polys.densearith import dup_mul_term +from sympy.polys.densearith import dmp_mul_term +from sympy.polys.densearith import dup_add_ground +from sympy.polys.densearith import dmp_add_ground +from sympy.polys.densearith import dup_sub_ground +from sympy.polys.densearith import dmp_sub_ground +from sympy.polys.densearith import dup_mul_ground +from sympy.polys.densearith import dmp_mul_ground +from sympy.polys.densearith import dup_quo_ground +from sympy.polys.densearith import dmp_quo_ground +from sympy.polys.densearith import dup_exquo_ground +from sympy.polys.densearith import dmp_exquo_ground +from sympy.polys.densearith import dup_lshift +from sympy.polys.densearith import dup_rshift +from sympy.polys.densearith import dup_abs +from sympy.polys.densearith import dmp_abs +from sympy.polys.densearith import dup_neg +from sympy.polys.densearith import dmp_neg +from sympy.polys.densearith import dup_add +from sympy.polys.densearith import dmp_add +from sympy.polys.densearith import dup_sub +from sympy.polys.densearith import dmp_sub +from sympy.polys.densearith import dup_add_mul +from sympy.polys.densearith import dmp_add_mul +from sympy.polys.densearith import dup_sub_mul +from sympy.polys.densearith import dmp_sub_mul +from sympy.polys.densearith import dup_mul +from sympy.polys.densearith import dmp_mul +from sympy.polys.densearith import dup_sqr +from sympy.polys.densearith import dmp_sqr +from sympy.polys.densearith import dup_pow +from sympy.polys.densearith import dmp_pow +from sympy.polys.densearith import dup_pdiv +from sympy.polys.densearith import dup_prem +from sympy.polys.densearith import dup_pquo +from sympy.polys.densearith import dup_pexquo +from sympy.polys.densearith import dmp_pdiv +from sympy.polys.densearith import dmp_prem +from sympy.polys.densearith import dmp_pquo +from sympy.polys.densearith import dmp_pexquo +from sympy.polys.densearith import dup_rr_div +from sympy.polys.densearith import dmp_rr_div +from sympy.polys.densearith import dup_ff_div +from sympy.polys.densearith import dmp_ff_div +from sympy.polys.densearith import dup_div +from sympy.polys.densearith import dup_rem +from sympy.polys.densearith import dup_quo +from sympy.polys.densearith import dup_exquo +from sympy.polys.densearith import dmp_div +from sympy.polys.densearith import dmp_rem +from sympy.polys.densearith import dmp_quo +from sympy.polys.densearith import dmp_exquo +from sympy.polys.densearith import dup_max_norm +from sympy.polys.densearith import dmp_max_norm +from sympy.polys.densearith import dup_l1_norm +from sympy.polys.densearith import dmp_l1_norm +from sympy.polys.densearith import dup_l2_norm_squared +from sympy.polys.densearith import dmp_l2_norm_squared +from sympy.polys.densearith import dup_expand +from sympy.polys.densearith import dmp_expand +from sympy.polys.densebasic import dup_LC +from sympy.polys.densebasic import dmp_LC +from sympy.polys.densebasic import dup_TC +from sympy.polys.densebasic import dmp_TC +from sympy.polys.densebasic import dmp_ground_LC +from sympy.polys.densebasic import dmp_ground_TC +from sympy.polys.densebasic import dup_degree +from sympy.polys.densebasic import dmp_degree +from sympy.polys.densebasic import dmp_degree_in +from sympy.polys.densebasic import dmp_to_dict +from sympy.polys.densetools import dup_integrate +from sympy.polys.densetools import dmp_integrate +from sympy.polys.densetools import dmp_integrate_in +from sympy.polys.densetools import dup_diff +from sympy.polys.densetools import dmp_diff +from sympy.polys.densetools import dmp_diff_in +from sympy.polys.densetools import dup_eval +from sympy.polys.densetools import dmp_eval +from sympy.polys.densetools import dmp_eval_in +from sympy.polys.densetools import dmp_eval_tail +from sympy.polys.densetools import dmp_diff_eval_in +from sympy.polys.densetools import dup_trunc +from sympy.polys.densetools import dmp_trunc +from sympy.polys.densetools import dmp_ground_trunc +from sympy.polys.densetools import dup_monic +from sympy.polys.densetools import dmp_ground_monic +from sympy.polys.densetools import dup_content +from sympy.polys.densetools import dmp_ground_content +from sympy.polys.densetools import dup_primitive +from sympy.polys.densetools import dmp_ground_primitive +from sympy.polys.densetools import dup_extract +from sympy.polys.densetools import dmp_ground_extract +from sympy.polys.densetools import dup_real_imag +from sympy.polys.densetools import dup_mirror +from sympy.polys.densetools import dup_scale +from sympy.polys.densetools import dup_shift +from sympy.polys.densetools import dmp_shift +from sympy.polys.densetools import dup_transform +from sympy.polys.densetools import dup_compose +from sympy.polys.densetools import dmp_compose +from sympy.polys.densetools import dup_decompose +from sympy.polys.densetools import dmp_lift +from sympy.polys.densetools import dup_sign_variations +from sympy.polys.densetools import dup_clear_denoms +from sympy.polys.densetools import dmp_clear_denoms +from sympy.polys.densetools import dup_revert +from sympy.polys.euclidtools import dup_half_gcdex +from sympy.polys.euclidtools import dmp_half_gcdex +from sympy.polys.euclidtools import dup_gcdex +from sympy.polys.euclidtools import dmp_gcdex +from sympy.polys.euclidtools import dup_invert +from sympy.polys.euclidtools import dmp_invert +from sympy.polys.euclidtools import dup_euclidean_prs +from sympy.polys.euclidtools import dmp_euclidean_prs +from sympy.polys.euclidtools import dup_primitive_prs +from sympy.polys.euclidtools import dmp_primitive_prs +from sympy.polys.euclidtools import dup_inner_subresultants +from sympy.polys.euclidtools import dup_subresultants +from sympy.polys.euclidtools import dup_prs_resultant +from sympy.polys.euclidtools import dup_resultant +from sympy.polys.euclidtools import dmp_inner_subresultants +from sympy.polys.euclidtools import dmp_subresultants +from sympy.polys.euclidtools import dmp_prs_resultant +from sympy.polys.euclidtools import dmp_zz_modular_resultant +from sympy.polys.euclidtools import dmp_zz_collins_resultant +from sympy.polys.euclidtools import dmp_qq_collins_resultant +from sympy.polys.euclidtools import dmp_resultant +from sympy.polys.euclidtools import dup_discriminant +from sympy.polys.euclidtools import dmp_discriminant +from sympy.polys.euclidtools import dup_rr_prs_gcd +from sympy.polys.euclidtools import dup_ff_prs_gcd +from sympy.polys.euclidtools import dmp_rr_prs_gcd +from sympy.polys.euclidtools import dmp_ff_prs_gcd +from sympy.polys.euclidtools import dup_zz_heu_gcd +from sympy.polys.euclidtools import dmp_zz_heu_gcd +from sympy.polys.euclidtools import dup_qq_heu_gcd +from sympy.polys.euclidtools import dmp_qq_heu_gcd +from sympy.polys.euclidtools import dup_inner_gcd +from sympy.polys.euclidtools import dmp_inner_gcd +from sympy.polys.euclidtools import dup_gcd +from sympy.polys.euclidtools import dmp_gcd +from sympy.polys.euclidtools import dup_rr_lcm +from sympy.polys.euclidtools import dup_ff_lcm +from sympy.polys.euclidtools import dup_lcm +from sympy.polys.euclidtools import dmp_rr_lcm +from sympy.polys.euclidtools import dmp_ff_lcm +from sympy.polys.euclidtools import dmp_lcm +from sympy.polys.euclidtools import dmp_content +from sympy.polys.euclidtools import dmp_primitive +from sympy.polys.euclidtools import dup_cancel +from sympy.polys.euclidtools import dmp_cancel +from sympy.polys.factortools import dup_trial_division +from sympy.polys.factortools import dmp_trial_division +from sympy.polys.factortools import dup_zz_mignotte_bound +from sympy.polys.factortools import dmp_zz_mignotte_bound +from sympy.polys.factortools import dup_zz_hensel_step +from sympy.polys.factortools import dup_zz_hensel_lift +from sympy.polys.factortools import dup_zz_zassenhaus +from sympy.polys.factortools import dup_zz_irreducible_p +from sympy.polys.factortools import dup_cyclotomic_p +from sympy.polys.factortools import dup_zz_cyclotomic_poly +from sympy.polys.factortools import dup_zz_cyclotomic_factor +from sympy.polys.factortools import dup_zz_factor_sqf +from sympy.polys.factortools import dup_zz_factor +from sympy.polys.factortools import dmp_zz_wang_non_divisors +from sympy.polys.factortools import dmp_zz_wang_lead_coeffs +from sympy.polys.factortools import dup_zz_diophantine +from sympy.polys.factortools import dmp_zz_diophantine +from sympy.polys.factortools import dmp_zz_wang_hensel_lifting +from sympy.polys.factortools import dmp_zz_wang +from sympy.polys.factortools import dmp_zz_factor +from sympy.polys.factortools import dup_qq_i_factor +from sympy.polys.factortools import dup_zz_i_factor +from sympy.polys.factortools import dmp_qq_i_factor +from sympy.polys.factortools import dmp_zz_i_factor +from sympy.polys.factortools import dup_ext_factor +from sympy.polys.factortools import dmp_ext_factor +from sympy.polys.factortools import dup_gf_factor +from sympy.polys.factortools import dmp_gf_factor +from sympy.polys.factortools import dup_factor_list +from sympy.polys.factortools import dup_factor_list_include +from sympy.polys.factortools import dmp_factor_list +from sympy.polys.factortools import dmp_factor_list_include +from sympy.polys.factortools import dup_irreducible_p +from sympy.polys.factortools import dmp_irreducible_p +from sympy.polys.rootisolation import dup_sturm +from sympy.polys.rootisolation import dup_root_upper_bound +from sympy.polys.rootisolation import dup_root_lower_bound +from sympy.polys.rootisolation import dup_step_refine_real_root +from sympy.polys.rootisolation import dup_inner_refine_real_root +from sympy.polys.rootisolation import dup_outer_refine_real_root +from sympy.polys.rootisolation import dup_refine_real_root +from sympy.polys.rootisolation import dup_inner_isolate_real_roots +from sympy.polys.rootisolation import dup_inner_isolate_positive_roots +from sympy.polys.rootisolation import dup_inner_isolate_negative_roots +from sympy.polys.rootisolation import dup_isolate_real_roots_sqf +from sympy.polys.rootisolation import dup_isolate_real_roots +from sympy.polys.rootisolation import dup_isolate_real_roots_list +from sympy.polys.rootisolation import dup_count_real_roots +from sympy.polys.rootisolation import dup_count_complex_roots +from sympy.polys.rootisolation import dup_isolate_complex_roots_sqf +from sympy.polys.rootisolation import dup_isolate_all_roots_sqf +from sympy.polys.rootisolation import dup_isolate_all_roots + +from sympy.polys.sqfreetools import ( + dup_sqf_p, dmp_sqf_p, dmp_norm, dup_sqf_norm, dmp_sqf_norm, + dup_gf_sqf_part, dmp_gf_sqf_part, dup_sqf_part, dmp_sqf_part, + dup_gf_sqf_list, dmp_gf_sqf_list, dup_sqf_list, dup_sqf_list_include, + dmp_sqf_list, dmp_sqf_list_include, dup_gff_list, dmp_gff_list) + +from sympy.polys.galoistools import ( + gf_degree, gf_LC, gf_TC, gf_strip, gf_from_dict, + gf_to_dict, gf_from_int_poly, gf_to_int_poly, gf_neg, gf_add_ground, gf_sub_ground, + gf_mul_ground, gf_quo_ground, gf_add, gf_sub, gf_mul, gf_sqr, gf_add_mul, gf_sub_mul, + gf_expand, gf_div, gf_rem, gf_quo, gf_exquo, gf_lshift, gf_rshift, gf_pow, gf_pow_mod, + gf_gcd, gf_lcm, gf_cofactors, gf_gcdex, gf_monic, gf_diff, gf_eval, gf_multi_eval, + gf_compose, gf_compose_mod, gf_trace_map, gf_random, gf_irreducible, gf_irred_p_ben_or, + gf_irred_p_rabin, gf_irreducible_p, gf_sqf_p, gf_sqf_part, gf_Qmatrix, + gf_berlekamp, gf_ddf_zassenhaus, gf_edf_zassenhaus, gf_ddf_shoup, gf_edf_shoup, + gf_zassenhaus, gf_shoup, gf_factor_sqf, gf_factor) + +from sympy.utilities import public + +@public +class IPolys: + symbols = None + ngens = None + domain = None + order = None + gens = None + + def drop(self, gen): + pass + + def clone(self, symbols=None, domain=None, order=None): + pass + + def to_ground(self): + pass + + def ground_new(self, element): + pass + + def domain_new(self, element): + pass + + def from_dict(self, d): + pass + + def wrap(self, element): + from sympy.polys.rings import PolyElement + if isinstance(element, PolyElement): + if element.ring == self: + return element + else: + raise NotImplementedError("domain conversions") + else: + return self.ground_new(element) + + def to_dense(self, element): + return self.wrap(element).to_dense() + + def from_dense(self, element): + return self.from_dict(dmp_to_dict(element, self.ngens-1, self.domain)) + + def dup_add_term(self, f, c, i): + return self.from_dense(dup_add_term(self.to_dense(f), c, i, self.domain)) + def dmp_add_term(self, f, c, i): + return self.from_dense(dmp_add_term(self.to_dense(f), self.wrap(c).drop(0).to_dense(), i, self.ngens-1, self.domain)) + def dup_sub_term(self, f, c, i): + return self.from_dense(dup_sub_term(self.to_dense(f), c, i, self.domain)) + def dmp_sub_term(self, f, c, i): + return self.from_dense(dmp_sub_term(self.to_dense(f), self.wrap(c).drop(0).to_dense(), i, self.ngens-1, self.domain)) + def dup_mul_term(self, f, c, i): + return self.from_dense(dup_mul_term(self.to_dense(f), c, i, self.domain)) + def dmp_mul_term(self, f, c, i): + return self.from_dense(dmp_mul_term(self.to_dense(f), self.wrap(c).drop(0).to_dense(), i, self.ngens-1, self.domain)) + + def dup_add_ground(self, f, c): + return self.from_dense(dup_add_ground(self.to_dense(f), c, self.domain)) + def dmp_add_ground(self, f, c): + return self.from_dense(dmp_add_ground(self.to_dense(f), c, self.ngens-1, self.domain)) + def dup_sub_ground(self, f, c): + return self.from_dense(dup_sub_ground(self.to_dense(f), c, self.domain)) + def dmp_sub_ground(self, f, c): + return self.from_dense(dmp_sub_ground(self.to_dense(f), c, self.ngens-1, self.domain)) + def dup_mul_ground(self, f, c): + return self.from_dense(dup_mul_ground(self.to_dense(f), c, self.domain)) + def dmp_mul_ground(self, f, c): + return self.from_dense(dmp_mul_ground(self.to_dense(f), c, self.ngens-1, self.domain)) + def dup_quo_ground(self, f, c): + return self.from_dense(dup_quo_ground(self.to_dense(f), c, self.domain)) + def dmp_quo_ground(self, f, c): + return self.from_dense(dmp_quo_ground(self.to_dense(f), c, self.ngens-1, self.domain)) + def dup_exquo_ground(self, f, c): + return self.from_dense(dup_exquo_ground(self.to_dense(f), c, self.domain)) + def dmp_exquo_ground(self, f, c): + return self.from_dense(dmp_exquo_ground(self.to_dense(f), c, self.ngens-1, self.domain)) + + def dup_lshift(self, f, n): + return self.from_dense(dup_lshift(self.to_dense(f), n, self.domain)) + def dup_rshift(self, f, n): + return self.from_dense(dup_rshift(self.to_dense(f), n, self.domain)) + + def dup_abs(self, f): + return self.from_dense(dup_abs(self.to_dense(f), self.domain)) + def dmp_abs(self, f): + return self.from_dense(dmp_abs(self.to_dense(f), self.ngens-1, self.domain)) + + def dup_neg(self, f): + return self.from_dense(dup_neg(self.to_dense(f), self.domain)) + def dmp_neg(self, f): + return self.from_dense(dmp_neg(self.to_dense(f), self.ngens-1, self.domain)) + + def dup_add(self, f, g): + return self.from_dense(dup_add(self.to_dense(f), self.to_dense(g), self.domain)) + def dmp_add(self, f, g): + return self.from_dense(dmp_add(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain)) + + def dup_sub(self, f, g): + return self.from_dense(dup_sub(self.to_dense(f), self.to_dense(g), self.domain)) + def dmp_sub(self, f, g): + return self.from_dense(dmp_sub(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain)) + + def dup_add_mul(self, f, g, h): + return self.from_dense(dup_add_mul(self.to_dense(f), self.to_dense(g), self.to_dense(h), self.domain)) + def dmp_add_mul(self, f, g, h): + return self.from_dense(dmp_add_mul(self.to_dense(f), self.to_dense(g), self.to_dense(h), self.ngens-1, self.domain)) + def dup_sub_mul(self, f, g, h): + return self.from_dense(dup_sub_mul(self.to_dense(f), self.to_dense(g), self.to_dense(h), self.domain)) + def dmp_sub_mul(self, f, g, h): + return self.from_dense(dmp_sub_mul(self.to_dense(f), self.to_dense(g), self.to_dense(h), self.ngens-1, self.domain)) + + def dup_mul(self, f, g): + return self.from_dense(dup_mul(self.to_dense(f), self.to_dense(g), self.domain)) + def dmp_mul(self, f, g): + return self.from_dense(dmp_mul(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain)) + + def dup_sqr(self, f): + return self.from_dense(dup_sqr(self.to_dense(f), self.domain)) + def dmp_sqr(self, f): + return self.from_dense(dmp_sqr(self.to_dense(f), self.ngens-1, self.domain)) + def dup_pow(self, f, n): + return self.from_dense(dup_pow(self.to_dense(f), n, self.domain)) + def dmp_pow(self, f, n): + return self.from_dense(dmp_pow(self.to_dense(f), n, self.ngens-1, self.domain)) + + def dup_pdiv(self, f, g): + q, r = dup_pdiv(self.to_dense(f), self.to_dense(g), self.domain) + return (self.from_dense(q), self.from_dense(r)) + def dup_prem(self, f, g): + return self.from_dense(dup_prem(self.to_dense(f), self.to_dense(g), self.domain)) + def dup_pquo(self, f, g): + return self.from_dense(dup_pquo(self.to_dense(f), self.to_dense(g), self.domain)) + def dup_pexquo(self, f, g): + return self.from_dense(dup_pexquo(self.to_dense(f), self.to_dense(g), self.domain)) + + def dmp_pdiv(self, f, g): + q, r = dmp_pdiv(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain) + return (self.from_dense(q), self.from_dense(r)) + def dmp_prem(self, f, g): + return self.from_dense(dmp_prem(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain)) + def dmp_pquo(self, f, g): + return self.from_dense(dmp_pquo(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain)) + def dmp_pexquo(self, f, g): + return self.from_dense(dmp_pexquo(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain)) + + def dup_rr_div(self, f, g): + q, r = dup_rr_div(self.to_dense(f), self.to_dense(g), self.domain) + return (self.from_dense(q), self.from_dense(r)) + def dmp_rr_div(self, f, g): + q, r = dmp_rr_div(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain) + return (self.from_dense(q), self.from_dense(r)) + def dup_ff_div(self, f, g): + q, r = dup_ff_div(self.to_dense(f), self.to_dense(g), self.domain) + return (self.from_dense(q), self.from_dense(r)) + def dmp_ff_div(self, f, g): + q, r = dmp_ff_div(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain) + return (self.from_dense(q), self.from_dense(r)) + + def dup_div(self, f, g): + q, r = dup_div(self.to_dense(f), self.to_dense(g), self.domain) + return (self.from_dense(q), self.from_dense(r)) + def dup_rem(self, f, g): + return self.from_dense(dup_rem(self.to_dense(f), self.to_dense(g), self.domain)) + def dup_quo(self, f, g): + return self.from_dense(dup_quo(self.to_dense(f), self.to_dense(g), self.domain)) + def dup_exquo(self, f, g): + return self.from_dense(dup_exquo(self.to_dense(f), self.to_dense(g), self.domain)) + + def dmp_div(self, f, g): + q, r = dmp_div(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain) + return (self.from_dense(q), self.from_dense(r)) + def dmp_rem(self, f, g): + return self.from_dense(dmp_rem(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain)) + def dmp_quo(self, f, g): + return self.from_dense(dmp_quo(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain)) + def dmp_exquo(self, f, g): + return self.from_dense(dmp_exquo(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain)) + + def dup_max_norm(self, f): + return dup_max_norm(self.to_dense(f), self.domain) + def dmp_max_norm(self, f): + return dmp_max_norm(self.to_dense(f), self.ngens-1, self.domain) + + def dup_l1_norm(self, f): + return dup_l1_norm(self.to_dense(f), self.domain) + def dmp_l1_norm(self, f): + return dmp_l1_norm(self.to_dense(f), self.ngens-1, self.domain) + + def dup_l2_norm_squared(self, f): + return dup_l2_norm_squared(self.to_dense(f), self.domain) + def dmp_l2_norm_squared(self, f): + return dmp_l2_norm_squared(self.to_dense(f), self.ngens-1, self.domain) + + def dup_expand(self, polys): + return self.from_dense(dup_expand(list(map(self.to_dense, polys)), self.domain)) + def dmp_expand(self, polys): + return self.from_dense(dmp_expand(list(map(self.to_dense, polys)), self.ngens-1, self.domain)) + + def dup_LC(self, f): + return dup_LC(self.to_dense(f), self.domain) + def dmp_LC(self, f): + LC = dmp_LC(self.to_dense(f), self.domain) + if isinstance(LC, list): + return self[1:].from_dense(LC) + else: + return LC + def dup_TC(self, f): + return dup_TC(self.to_dense(f), self.domain) + def dmp_TC(self, f): + TC = dmp_TC(self.to_dense(f), self.domain) + if isinstance(TC, list): + return self[1:].from_dense(TC) + else: + return TC + + def dmp_ground_LC(self, f): + return dmp_ground_LC(self.to_dense(f), self.ngens-1, self.domain) + def dmp_ground_TC(self, f): + return dmp_ground_TC(self.to_dense(f), self.ngens-1, self.domain) + + def dup_degree(self, f): + return dup_degree(self.to_dense(f)) + def dmp_degree(self, f): + return dmp_degree(self.to_dense(f), self.ngens-1) + def dmp_degree_in(self, f, j): + return dmp_degree_in(self.to_dense(f), j, self.ngens-1) + def dup_integrate(self, f, m): + return self.from_dense(dup_integrate(self.to_dense(f), m, self.domain)) + def dmp_integrate(self, f, m): + return self.from_dense(dmp_integrate(self.to_dense(f), m, self.ngens-1, self.domain)) + + def dup_diff(self, f, m): + return self.from_dense(dup_diff(self.to_dense(f), m, self.domain)) + def dmp_diff(self, f, m): + return self.from_dense(dmp_diff(self.to_dense(f), m, self.ngens-1, self.domain)) + + def dmp_diff_in(self, f, m, j): + return self.from_dense(dmp_diff_in(self.to_dense(f), m, j, self.ngens-1, self.domain)) + def dmp_integrate_in(self, f, m, j): + return self.from_dense(dmp_integrate_in(self.to_dense(f), m, j, self.ngens-1, self.domain)) + + def dup_eval(self, f, a): + return dup_eval(self.to_dense(f), a, self.domain) + def dmp_eval(self, f, a): + result = dmp_eval(self.to_dense(f), a, self.ngens-1, self.domain) + return self[1:].from_dense(result) + + def dmp_eval_in(self, f, a, j): + result = dmp_eval_in(self.to_dense(f), a, j, self.ngens-1, self.domain) + return self.drop(j).from_dense(result) + def dmp_diff_eval_in(self, f, m, a, j): + result = dmp_diff_eval_in(self.to_dense(f), m, a, j, self.ngens-1, self.domain) + return self.drop(j).from_dense(result) + + def dmp_eval_tail(self, f, A): + result = dmp_eval_tail(self.to_dense(f), A, self.ngens-1, self.domain) + if isinstance(result, list): + return self[:-len(A)].from_dense(result) + else: + return result + + def dup_trunc(self, f, p): + return self.from_dense(dup_trunc(self.to_dense(f), p, self.domain)) + def dmp_trunc(self, f, g): + return self.from_dense(dmp_trunc(self.to_dense(f), self[1:].to_dense(g), self.ngens-1, self.domain)) + def dmp_ground_trunc(self, f, p): + return self.from_dense(dmp_ground_trunc(self.to_dense(f), p, self.ngens-1, self.domain)) + + def dup_monic(self, f): + return self.from_dense(dup_monic(self.to_dense(f), self.domain)) + def dmp_ground_monic(self, f): + return self.from_dense(dmp_ground_monic(self.to_dense(f), self.ngens-1, self.domain)) + + def dup_extract(self, f, g): + c, F, G = dup_extract(self.to_dense(f), self.to_dense(g), self.domain) + return (c, self.from_dense(F), self.from_dense(G)) + def dmp_ground_extract(self, f, g): + c, F, G = dmp_ground_extract(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain) + return (c, self.from_dense(F), self.from_dense(G)) + + def dup_real_imag(self, f): + p, q = dup_real_imag(self.wrap(f).drop(1).to_dense(), self.domain) + return (self.from_dense(p), self.from_dense(q)) + + def dup_mirror(self, f): + return self.from_dense(dup_mirror(self.to_dense(f), self.domain)) + def dup_scale(self, f, a): + return self.from_dense(dup_scale(self.to_dense(f), a, self.domain)) + def dup_shift(self, f, a): + return self.from_dense(dup_shift(self.to_dense(f), a, self.domain)) + def dmp_shift(self, f, a): + return self.from_dense(dmp_shift(self.to_dense(f), a, self.ngens-1, self.domain)) + def dup_transform(self, f, p, q): + return self.from_dense(dup_transform(self.to_dense(f), self.to_dense(p), self.to_dense(q), self.domain)) + + def dup_compose(self, f, g): + return self.from_dense(dup_compose(self.to_dense(f), self.to_dense(g), self.domain)) + def dmp_compose(self, f, g): + return self.from_dense(dmp_compose(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain)) + + def dup_decompose(self, f): + components = dup_decompose(self.to_dense(f), self.domain) + return list(map(self.from_dense, components)) + + def dmp_lift(self, f): + result = dmp_lift(self.to_dense(f), self.ngens-1, self.domain) + return self.to_ground().from_dense(result) + + def dup_sign_variations(self, f): + return dup_sign_variations(self.to_dense(f), self.domain) + + def dup_clear_denoms(self, f, convert=False): + c, F = dup_clear_denoms(self.to_dense(f), self.domain, convert=convert) + if convert: + ring = self.clone(domain=self.domain.get_ring()) + else: + ring = self + return (c, ring.from_dense(F)) + def dmp_clear_denoms(self, f, convert=False): + c, F = dmp_clear_denoms(self.to_dense(f), self.ngens-1, self.domain, convert=convert) + if convert: + ring = self.clone(domain=self.domain.get_ring()) + else: + ring = self + return (c, ring.from_dense(F)) + + def dup_revert(self, f, n): + return self.from_dense(dup_revert(self.to_dense(f), n, self.domain)) + + def dup_half_gcdex(self, f, g): + s, h = dup_half_gcdex(self.to_dense(f), self.to_dense(g), self.domain) + return (self.from_dense(s), self.from_dense(h)) + def dmp_half_gcdex(self, f, g): + s, h = dmp_half_gcdex(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain) + return (self.from_dense(s), self.from_dense(h)) + def dup_gcdex(self, f, g): + s, t, h = dup_gcdex(self.to_dense(f), self.to_dense(g), self.domain) + return (self.from_dense(s), self.from_dense(t), self.from_dense(h)) + def dmp_gcdex(self, f, g): + s, t, h = dmp_gcdex(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain) + return (self.from_dense(s), self.from_dense(t), self.from_dense(h)) + + def dup_invert(self, f, g): + return self.from_dense(dup_invert(self.to_dense(f), self.to_dense(g), self.domain)) + def dmp_invert(self, f, g): + return self.from_dense(dmp_invert(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain)) + + def dup_euclidean_prs(self, f, g): + prs = dup_euclidean_prs(self.to_dense(f), self.to_dense(g), self.domain) + return list(map(self.from_dense, prs)) + def dmp_euclidean_prs(self, f, g): + prs = dmp_euclidean_prs(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain) + return list(map(self.from_dense, prs)) + def dup_primitive_prs(self, f, g): + prs = dup_primitive_prs(self.to_dense(f), self.to_dense(g), self.domain) + return list(map(self.from_dense, prs)) + def dmp_primitive_prs(self, f, g): + prs = dmp_primitive_prs(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain) + return list(map(self.from_dense, prs)) + + def dup_inner_subresultants(self, f, g): + prs, sres = dup_inner_subresultants(self.to_dense(f), self.to_dense(g), self.domain) + return (list(map(self.from_dense, prs)), sres) + def dmp_inner_subresultants(self, f, g): + prs, sres = dmp_inner_subresultants(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain) + return (list(map(self.from_dense, prs)), sres) + + def dup_subresultants(self, f, g): + prs = dup_subresultants(self.to_dense(f), self.to_dense(g), self.domain) + return list(map(self.from_dense, prs)) + def dmp_subresultants(self, f, g): + prs = dmp_subresultants(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain) + return list(map(self.from_dense, prs)) + + def dup_prs_resultant(self, f, g): + res, prs = dup_prs_resultant(self.to_dense(f), self.to_dense(g), self.domain) + return (res, list(map(self.from_dense, prs))) + def dmp_prs_resultant(self, f, g): + res, prs = dmp_prs_resultant(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain) + return (self[1:].from_dense(res), list(map(self.from_dense, prs))) + + def dmp_zz_modular_resultant(self, f, g, p): + res = dmp_zz_modular_resultant(self.to_dense(f), self.to_dense(g), self.domain_new(p), self.ngens-1, self.domain) + return self[1:].from_dense(res) + def dmp_zz_collins_resultant(self, f, g): + res = dmp_zz_collins_resultant(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain) + return self[1:].from_dense(res) + def dmp_qq_collins_resultant(self, f, g): + res = dmp_qq_collins_resultant(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain) + return self[1:].from_dense(res) + + def dup_resultant(self, f, g): #, includePRS=False): + return dup_resultant(self.to_dense(f), self.to_dense(g), self.domain) #, includePRS=includePRS) + def dmp_resultant(self, f, g): #, includePRS=False): + res = dmp_resultant(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain) #, includePRS=includePRS) + if isinstance(res, list): + return self[1:].from_dense(res) + else: + return res + + def dup_discriminant(self, f): + return dup_discriminant(self.to_dense(f), self.domain) + def dmp_discriminant(self, f): + disc = dmp_discriminant(self.to_dense(f), self.ngens-1, self.domain) + if isinstance(disc, list): + return self[1:].from_dense(disc) + else: + return disc + + def dup_rr_prs_gcd(self, f, g): + H, F, G = dup_rr_prs_gcd(self.to_dense(f), self.to_dense(g), self.domain) + return (self.from_dense(H), self.from_dense(F), self.from_dense(G)) + def dup_ff_prs_gcd(self, f, g): + H, F, G = dup_ff_prs_gcd(self.to_dense(f), self.to_dense(g), self.domain) + return (self.from_dense(H), self.from_dense(F), self.from_dense(G)) + def dmp_rr_prs_gcd(self, f, g): + H, F, G = dmp_rr_prs_gcd(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain) + return (self.from_dense(H), self.from_dense(F), self.from_dense(G)) + def dmp_ff_prs_gcd(self, f, g): + H, F, G = dmp_ff_prs_gcd(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain) + return (self.from_dense(H), self.from_dense(F), self.from_dense(G)) + def dup_zz_heu_gcd(self, f, g): + H, F, G = dup_zz_heu_gcd(self.to_dense(f), self.to_dense(g), self.domain) + return (self.from_dense(H), self.from_dense(F), self.from_dense(G)) + def dmp_zz_heu_gcd(self, f, g): + H, F, G = dmp_zz_heu_gcd(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain) + return (self.from_dense(H), self.from_dense(F), self.from_dense(G)) + def dup_qq_heu_gcd(self, f, g): + H, F, G = dup_qq_heu_gcd(self.to_dense(f), self.to_dense(g), self.domain) + return (self.from_dense(H), self.from_dense(F), self.from_dense(G)) + def dmp_qq_heu_gcd(self, f, g): + H, F, G = dmp_qq_heu_gcd(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain) + return (self.from_dense(H), self.from_dense(F), self.from_dense(G)) + def dup_inner_gcd(self, f, g): + H, F, G = dup_inner_gcd(self.to_dense(f), self.to_dense(g), self.domain) + return (self.from_dense(H), self.from_dense(F), self.from_dense(G)) + def dmp_inner_gcd(self, f, g): + H, F, G = dmp_inner_gcd(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain) + return (self.from_dense(H), self.from_dense(F), self.from_dense(G)) + def dup_gcd(self, f, g): + H = dup_gcd(self.to_dense(f), self.to_dense(g), self.domain) + return self.from_dense(H) + def dmp_gcd(self, f, g): + H = dmp_gcd(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain) + return self.from_dense(H) + def dup_rr_lcm(self, f, g): + H = dup_rr_lcm(self.to_dense(f), self.to_dense(g), self.domain) + return self.from_dense(H) + def dup_ff_lcm(self, f, g): + H = dup_ff_lcm(self.to_dense(f), self.to_dense(g), self.domain) + return self.from_dense(H) + def dup_lcm(self, f, g): + H = dup_lcm(self.to_dense(f), self.to_dense(g), self.domain) + return self.from_dense(H) + def dmp_rr_lcm(self, f, g): + H = dmp_rr_lcm(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain) + return self.from_dense(H) + def dmp_ff_lcm(self, f, g): + H = dmp_ff_lcm(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain) + return self.from_dense(H) + def dmp_lcm(self, f, g): + H = dmp_lcm(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain) + return self.from_dense(H) + + def dup_content(self, f): + cont = dup_content(self.to_dense(f), self.domain) + return cont + def dup_primitive(self, f): + cont, prim = dup_primitive(self.to_dense(f), self.domain) + return cont, self.from_dense(prim) + + def dmp_content(self, f): + cont = dmp_content(self.to_dense(f), self.ngens-1, self.domain) + if isinstance(cont, list): + return self[1:].from_dense(cont) + else: + return cont + def dmp_primitive(self, f): + cont, prim = dmp_primitive(self.to_dense(f), self.ngens-1, self.domain) + if isinstance(cont, list): + return (self[1:].from_dense(cont), self.from_dense(prim)) + else: + return (cont, self.from_dense(prim)) + + def dmp_ground_content(self, f): + cont = dmp_ground_content(self.to_dense(f), self.ngens-1, self.domain) + return cont + def dmp_ground_primitive(self, f): + cont, prim = dmp_ground_primitive(self.to_dense(f), self.ngens-1, self.domain) + return (cont, self.from_dense(prim)) + + def dup_cancel(self, f, g, include=True): + result = dup_cancel(self.to_dense(f), self.to_dense(g), self.domain, include=include) + if not include: + cf, cg, F, G = result + return (cf, cg, self.from_dense(F), self.from_dense(G)) + else: + F, G = result + return (self.from_dense(F), self.from_dense(G)) + def dmp_cancel(self, f, g, include=True): + result = dmp_cancel(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain, include=include) + if not include: + cf, cg, F, G = result + return (cf, cg, self.from_dense(F), self.from_dense(G)) + else: + F, G = result + return (self.from_dense(F), self.from_dense(G)) + + def dup_trial_division(self, f, factors): + factors = dup_trial_division(self.to_dense(f), list(map(self.to_dense, factors)), self.domain) + return [ (self.from_dense(g), k) for g, k in factors ] + def dmp_trial_division(self, f, factors): + factors = dmp_trial_division(self.to_dense(f), list(map(self.to_dense, factors)), self.ngens-1, self.domain) + return [ (self.from_dense(g), k) for g, k in factors ] + + def dup_zz_mignotte_bound(self, f): + return dup_zz_mignotte_bound(self.to_dense(f), self.domain) + def dmp_zz_mignotte_bound(self, f): + return dmp_zz_mignotte_bound(self.to_dense(f), self.ngens-1, self.domain) + + def dup_zz_hensel_step(self, m, f, g, h, s, t): + D = self.to_dense + G, H, S, T = dup_zz_hensel_step(m, D(f), D(g), D(h), D(s), D(t), self.domain) + return (self.from_dense(G), self.from_dense(H), self.from_dense(S), self.from_dense(T)) + def dup_zz_hensel_lift(self, p, f, f_list, l): + D = self.to_dense + polys = dup_zz_hensel_lift(p, D(f), list(map(D, f_list)), l, self.domain) + return list(map(self.from_dense, polys)) + + def dup_zz_zassenhaus(self, f): + factors = dup_zz_zassenhaus(self.to_dense(f), self.domain) + return [ (self.from_dense(g), k) for g, k in factors ] + + def dup_zz_irreducible_p(self, f): + return dup_zz_irreducible_p(self.to_dense(f), self.domain) + def dup_cyclotomic_p(self, f, irreducible=False): + return dup_cyclotomic_p(self.to_dense(f), self.domain, irreducible=irreducible) + def dup_zz_cyclotomic_poly(self, n): + F = dup_zz_cyclotomic_poly(n, self.domain) + return self.from_dense(F) + def dup_zz_cyclotomic_factor(self, f): + result = dup_zz_cyclotomic_factor(self.to_dense(f), self.domain) + if result is None: + return result + else: + return list(map(self.from_dense, result)) + + # E: List[ZZ], cs: ZZ, ct: ZZ + def dmp_zz_wang_non_divisors(self, E, cs, ct): + return dmp_zz_wang_non_divisors(E, cs, ct, self.domain) + + # f: Poly, T: List[(Poly, int)], ct: ZZ, A: List[ZZ] + #def dmp_zz_wang_test_points(f, T, ct, A): + # dmp_zz_wang_test_points(self.to_dense(f), T, ct, A, self.ngens-1, self.domain) + + # f: Poly, T: List[(Poly, int)], cs: ZZ, E: List[ZZ], H: List[Poly], A: List[ZZ] + def dmp_zz_wang_lead_coeffs(self, f, T, cs, E, H, A): + mv = self[1:] + T = [ (mv.to_dense(t), k) for t, k in T ] + uv = self[:1] + H = list(map(uv.to_dense, H)) + f, HH, CC = dmp_zz_wang_lead_coeffs(self.to_dense(f), T, cs, E, H, A, self.ngens-1, self.domain) + return self.from_dense(f), list(map(uv.from_dense, HH)), list(map(mv.from_dense, CC)) + + # f: List[Poly], m: int, p: ZZ + def dup_zz_diophantine(self, F, m, p): + result = dup_zz_diophantine(list(map(self.to_dense, F)), m, p, self.domain) + return list(map(self.from_dense, result)) + + # f: List[Poly], c: List[Poly], A: List[ZZ], d: int, p: ZZ + def dmp_zz_diophantine(self, F, c, A, d, p): + result = dmp_zz_diophantine(list(map(self.to_dense, F)), self.to_dense(c), A, d, p, self.ngens-1, self.domain) + return list(map(self.from_dense, result)) + + # f: Poly, H: List[Poly], LC: List[Poly], A: List[ZZ], p: ZZ + def dmp_zz_wang_hensel_lifting(self, f, H, LC, A, p): + uv = self[:1] + mv = self[1:] + H = list(map(uv.to_dense, H)) + LC = list(map(mv.to_dense, LC)) + result = dmp_zz_wang_hensel_lifting(self.to_dense(f), H, LC, A, p, self.ngens-1, self.domain) + return list(map(self.from_dense, result)) + + def dmp_zz_wang(self, f, mod=None, seed=None): + factors = dmp_zz_wang(self.to_dense(f), self.ngens-1, self.domain, mod=mod, seed=seed) + return [ self.from_dense(g) for g in factors ] + + def dup_zz_factor_sqf(self, f): + coeff, factors = dup_zz_factor_sqf(self.to_dense(f), self.domain) + return (coeff, [ self.from_dense(g) for g in factors ]) + + def dup_zz_factor(self, f): + coeff, factors = dup_zz_factor(self.to_dense(f), self.domain) + return (coeff, [ (self.from_dense(g), k) for g, k in factors ]) + def dmp_zz_factor(self, f): + coeff, factors = dmp_zz_factor(self.to_dense(f), self.ngens-1, self.domain) + return (coeff, [ (self.from_dense(g), k) for g, k in factors ]) + + def dup_qq_i_factor(self, f): + coeff, factors = dup_qq_i_factor(self.to_dense(f), self.domain) + return (coeff, [ (self.from_dense(g), k) for g, k in factors ]) + def dmp_qq_i_factor(self, f): + coeff, factors = dmp_qq_i_factor(self.to_dense(f), self.ngens-1, self.domain) + return (coeff, [ (self.from_dense(g), k) for g, k in factors ]) + + def dup_zz_i_factor(self, f): + coeff, factors = dup_zz_i_factor(self.to_dense(f), self.domain) + return (coeff, [ (self.from_dense(g), k) for g, k in factors ]) + def dmp_zz_i_factor(self, f): + coeff, factors = dmp_zz_i_factor(self.to_dense(f), self.ngens-1, self.domain) + return (coeff, [ (self.from_dense(g), k) for g, k in factors ]) + + def dup_ext_factor(self, f): + coeff, factors = dup_ext_factor(self.to_dense(f), self.domain) + return (coeff, [ (self.from_dense(g), k) for g, k in factors ]) + def dmp_ext_factor(self, f): + coeff, factors = dmp_ext_factor(self.to_dense(f), self.ngens-1, self.domain) + return (coeff, [ (self.from_dense(g), k) for g, k in factors ]) + + def dup_gf_factor(self, f): + coeff, factors = dup_gf_factor(self.to_dense(f), self.domain) + return (coeff, [ (self.from_dense(g), k) for g, k in factors ]) + def dmp_gf_factor(self, f): + coeff, factors = dmp_gf_factor(self.to_dense(f), self.ngens-1, self.domain) + return (coeff, [ (self.from_dense(g), k) for g, k in factors ]) + + def dup_factor_list(self, f): + coeff, factors = dup_factor_list(self.to_dense(f), self.domain) + return (coeff, [ (self.from_dense(g), k) for g, k in factors ]) + def dup_factor_list_include(self, f): + factors = dup_factor_list_include(self.to_dense(f), self.domain) + return [ (self.from_dense(g), k) for g, k in factors ] + + def dmp_factor_list(self, f): + coeff, factors = dmp_factor_list(self.to_dense(f), self.ngens-1, self.domain) + return (coeff, [ (self.from_dense(g), k) for g, k in factors ]) + def dmp_factor_list_include(self, f): + factors = dmp_factor_list_include(self.to_dense(f), self.ngens-1, self.domain) + return [ (self.from_dense(g), k) for g, k in factors ] + + def dup_irreducible_p(self, f): + return dup_irreducible_p(self.to_dense(f), self.domain) + def dmp_irreducible_p(self, f): + return dmp_irreducible_p(self.to_dense(f), self.ngens-1, self.domain) + + def dup_sturm(self, f): + seq = dup_sturm(self.to_dense(f), self.domain) + return list(map(self.from_dense, seq)) + + def dup_sqf_p(self, f): + return dup_sqf_p(self.to_dense(f), self.domain) + def dmp_sqf_p(self, f): + return dmp_sqf_p(self.to_dense(f), self.ngens-1, self.domain) + + def dmp_norm(self, f): + n = dmp_norm(self.to_dense(f), self.ngens-1, self.domain) + return self.to_ground().from_dense(n) + + def dup_sqf_norm(self, f): + s, F, R = dup_sqf_norm(self.to_dense(f), self.domain) + return (s, self.from_dense(F), self.to_ground().from_dense(R)) + def dmp_sqf_norm(self, f): + s, F, R = dmp_sqf_norm(self.to_dense(f), self.ngens-1, self.domain) + return (s, self.from_dense(F), self.to_ground().from_dense(R)) + + def dup_gf_sqf_part(self, f): + return self.from_dense(dup_gf_sqf_part(self.to_dense(f), self.domain)) + def dmp_gf_sqf_part(self, f): + return self.from_dense(dmp_gf_sqf_part(self.to_dense(f), self.domain)) + def dup_sqf_part(self, f): + return self.from_dense(dup_sqf_part(self.to_dense(f), self.domain)) + def dmp_sqf_part(self, f): + return self.from_dense(dmp_sqf_part(self.to_dense(f), self.ngens-1, self.domain)) + + def dup_gf_sqf_list(self, f, all=False): + coeff, factors = dup_gf_sqf_list(self.to_dense(f), self.domain, all=all) + return (coeff, [ (self.from_dense(g), k) for g, k in factors ]) + def dmp_gf_sqf_list(self, f, all=False): + coeff, factors = dmp_gf_sqf_list(self.to_dense(f), self.ngens-1, self.domain, all=all) + return (coeff, [ (self.from_dense(g), k) for g, k in factors ]) + + def dup_sqf_list(self, f, all=False): + coeff, factors = dup_sqf_list(self.to_dense(f), self.domain, all=all) + return (coeff, [ (self.from_dense(g), k) for g, k in factors ]) + def dup_sqf_list_include(self, f, all=False): + factors = dup_sqf_list_include(self.to_dense(f), self.domain, all=all) + return [ (self.from_dense(g), k) for g, k in factors ] + def dmp_sqf_list(self, f, all=False): + coeff, factors = dmp_sqf_list(self.to_dense(f), self.ngens-1, self.domain, all=all) + return (coeff, [ (self.from_dense(g), k) for g, k in factors ]) + def dmp_sqf_list_include(self, f, all=False): + factors = dmp_sqf_list_include(self.to_dense(f), self.ngens-1, self.domain, all=all) + return [ (self.from_dense(g), k) for g, k in factors ] + + def dup_gff_list(self, f): + factors = dup_gff_list(self.to_dense(f), self.domain) + return [ (self.from_dense(g), k) for g, k in factors ] + def dmp_gff_list(self, f): + factors = dmp_gff_list(self.to_dense(f), self.ngens-1, self.domain) + return [ (self.from_dense(g), k) for g, k in factors ] + + def dup_root_upper_bound(self, f): + return dup_root_upper_bound(self.to_dense(f), self.domain) + def dup_root_lower_bound(self, f): + return dup_root_lower_bound(self.to_dense(f), self.domain) + + def dup_step_refine_real_root(self, f, M, fast=False): + return dup_step_refine_real_root(self.to_dense(f), M, self.domain, fast=fast) + def dup_inner_refine_real_root(self, f, M, eps=None, steps=None, disjoint=None, fast=False, mobius=False): + return dup_inner_refine_real_root(self.to_dense(f), M, self.domain, eps=eps, steps=steps, disjoint=disjoint, fast=fast, mobius=mobius) + def dup_outer_refine_real_root(self, f, s, t, eps=None, steps=None, disjoint=None, fast=False): + return dup_outer_refine_real_root(self.to_dense(f), s, t, self.domain, eps=eps, steps=steps, disjoint=disjoint, fast=fast) + def dup_refine_real_root(self, f, s, t, eps=None, steps=None, disjoint=None, fast=False): + return dup_refine_real_root(self.to_dense(f), s, t, self.domain, eps=eps, steps=steps, disjoint=disjoint, fast=fast) + def dup_inner_isolate_real_roots(self, f, eps=None, fast=False): + return dup_inner_isolate_real_roots(self.to_dense(f), self.domain, eps=eps, fast=fast) + def dup_inner_isolate_positive_roots(self, f, eps=None, inf=None, sup=None, fast=False, mobius=False): + return dup_inner_isolate_positive_roots(self.to_dense(f), self.domain, eps=eps, inf=inf, sup=sup, fast=fast, mobius=mobius) + def dup_inner_isolate_negative_roots(self, f, inf=None, sup=None, eps=None, fast=False, mobius=False): + return dup_inner_isolate_negative_roots(self.to_dense(f), self.domain, inf=inf, sup=sup, eps=eps, fast=fast, mobius=mobius) + def dup_isolate_real_roots_sqf(self, f, eps=None, inf=None, sup=None, fast=False, blackbox=False): + return dup_isolate_real_roots_sqf(self.to_dense(f), self.domain, eps=eps, inf=inf, sup=sup, fast=fast, blackbox=blackbox) + def dup_isolate_real_roots(self, f, eps=None, inf=None, sup=None, basis=False, fast=False): + return dup_isolate_real_roots(self.to_dense(f), self.domain, eps=eps, inf=inf, sup=sup, basis=basis, fast=fast) + def dup_isolate_real_roots_list(self, polys, eps=None, inf=None, sup=None, strict=False, basis=False, fast=False): + return dup_isolate_real_roots_list(list(map(self.to_dense, polys)), self.domain, eps=eps, inf=inf, sup=sup, strict=strict, basis=basis, fast=fast) + def dup_count_real_roots(self, f, inf=None, sup=None): + return dup_count_real_roots(self.to_dense(f), self.domain, inf=inf, sup=sup) + def dup_count_complex_roots(self, f, inf=None, sup=None, exclude=None): + return dup_count_complex_roots(self.to_dense(f), self.domain, inf=inf, sup=sup, exclude=exclude) + def dup_isolate_complex_roots_sqf(self, f, eps=None, inf=None, sup=None, blackbox=False): + return dup_isolate_complex_roots_sqf(self.to_dense(f), self.domain, eps=eps, inf=inf, sup=sup, blackbox=blackbox) + def dup_isolate_all_roots_sqf(self, f, eps=None, inf=None, sup=None, fast=False, blackbox=False): + return dup_isolate_all_roots_sqf(self.to_dense(f), self.domain, eps=eps, inf=inf, sup=sup, fast=fast, blackbox=blackbox) + def dup_isolate_all_roots(self, f, eps=None, inf=None, sup=None, fast=False): + return dup_isolate_all_roots(self.to_dense(f), self.domain, eps=eps, inf=inf, sup=sup, fast=fast) + + def fateman_poly_F_1(self): + from sympy.polys.specialpolys import dmp_fateman_poly_F_1 + return tuple(map(self.from_dense, dmp_fateman_poly_F_1(self.ngens-1, self.domain))) + def fateman_poly_F_2(self): + from sympy.polys.specialpolys import dmp_fateman_poly_F_2 + return tuple(map(self.from_dense, dmp_fateman_poly_F_2(self.ngens-1, self.domain))) + def fateman_poly_F_3(self): + from sympy.polys.specialpolys import dmp_fateman_poly_F_3 + return tuple(map(self.from_dense, dmp_fateman_poly_F_3(self.ngens-1, self.domain))) + + def to_gf_dense(self, element): + return gf_strip([ self.domain.dom.convert(c, self.domain) for c in self.wrap(element).to_dense() ]) + + def from_gf_dense(self, element): + return self.from_dict(dmp_to_dict(element, self.ngens-1, self.domain.dom)) + + def gf_degree(self, f): + return gf_degree(self.to_gf_dense(f)) + + def gf_LC(self, f): + return gf_LC(self.to_gf_dense(f), self.domain.dom) + def gf_TC(self, f): + return gf_TC(self.to_gf_dense(f), self.domain.dom) + + def gf_strip(self, f): + return self.from_gf_dense(gf_strip(self.to_gf_dense(f))) + def gf_trunc(self, f): + return self.from_gf_dense(gf_strip(self.to_gf_dense(f), self.domain.mod)) + def gf_normal(self, f): + return self.from_gf_dense(gf_strip(self.to_gf_dense(f), self.domain.mod, self.domain.dom)) + + def gf_from_dict(self, f): + return self.from_gf_dense(gf_from_dict(f, self.domain.mod, self.domain.dom)) + def gf_to_dict(self, f, symmetric=True): + return gf_to_dict(self.to_gf_dense(f), self.domain.mod, symmetric=symmetric) + + def gf_from_int_poly(self, f): + return self.from_gf_dense(gf_from_int_poly(f, self.domain.mod)) + def gf_to_int_poly(self, f, symmetric=True): + return gf_to_int_poly(self.to_gf_dense(f), self.domain.mod, symmetric=symmetric) + + def gf_neg(self, f): + return self.from_gf_dense(gf_neg(self.to_gf_dense(f), self.domain.mod, self.domain.dom)) + + def gf_add_ground(self, f, a): + return self.from_gf_dense(gf_add_ground(self.to_gf_dense(f), a, self.domain.mod, self.domain.dom)) + def gf_sub_ground(self, f, a): + return self.from_gf_dense(gf_sub_ground(self.to_gf_dense(f), a, self.domain.mod, self.domain.dom)) + def gf_mul_ground(self, f, a): + return self.from_gf_dense(gf_mul_ground(self.to_gf_dense(f), a, self.domain.mod, self.domain.dom)) + def gf_quo_ground(self, f, a): + return self.from_gf_dense(gf_quo_ground(self.to_gf_dense(f), a, self.domain.mod, self.domain.dom)) + + def gf_add(self, f, g): + return self.from_gf_dense(gf_add(self.to_gf_dense(f), self.to_gf_dense(g), self.domain.mod, self.domain.dom)) + def gf_sub(self, f, g): + return self.from_gf_dense(gf_sub(self.to_gf_dense(f), self.to_gf_dense(g), self.domain.mod, self.domain.dom)) + def gf_mul(self, f, g): + return self.from_gf_dense(gf_mul(self.to_gf_dense(f), self.to_gf_dense(g), self.domain.mod, self.domain.dom)) + def gf_sqr(self, f): + return self.from_gf_dense(gf_sqr(self.to_gf_dense(f), self.domain.mod, self.domain.dom)) + + def gf_add_mul(self, f, g, h): + return self.from_gf_dense(gf_add_mul(self.to_gf_dense(f), self.to_gf_dense(g), self.to_gf_dense(h), self.domain.mod, self.domain.dom)) + def gf_sub_mul(self, f, g, h): + return self.from_gf_dense(gf_sub_mul(self.to_gf_dense(f), self.to_gf_dense(g), self.to_gf_dense(h), self.domain.mod, self.domain.dom)) + + def gf_expand(self, F): + return self.from_gf_dense(gf_expand(list(map(self.to_gf_dense, F)), self.domain.mod, self.domain.dom)) + + def gf_div(self, f, g): + q, r = gf_div(self.to_gf_dense(f), self.to_gf_dense(g), self.domain.mod, self.domain.dom) + return self.from_gf_dense(q), self.from_gf_dense(r) + def gf_rem(self, f, g): + return self.from_gf_dense(gf_rem(self.to_gf_dense(f), self.to_gf_dense(g), self.domain.mod, self.domain.dom)) + def gf_quo(self, f, g): + return self.from_gf_dense(gf_quo(self.to_gf_dense(f), self.to_gf_dense(g), self.domain.mod, self.domain.dom)) + def gf_exquo(self, f, g): + return self.from_gf_dense(gf_exquo(self.to_gf_dense(f), self.to_gf_dense(g), self.domain.mod, self.domain.dom)) + + def gf_lshift(self, f, n): + return self.from_gf_dense(gf_lshift(self.to_gf_dense(f), n, self.domain.dom)) + def gf_rshift(self, f, n): + return self.from_gf_dense(gf_rshift(self.to_gf_dense(f), n, self.domain.dom)) + + def gf_pow(self, f, n): + return self.from_gf_dense(gf_pow(self.to_gf_dense(f), n, self.domain.mod, self.domain.dom)) + def gf_pow_mod(self, f, n, g): + return self.from_gf_dense(gf_pow_mod(self.to_gf_dense(f), n, self.to_gf_dense(g), self.domain.mod, self.domain.dom)) + + def gf_cofactors(self, f, g): + h, cff, cfg = gf_cofactors(self.to_gf_dense(f), self.to_gf_dense(g), self.domain.mod, self.domain.dom) + return self.from_gf_dense(h), self.from_gf_dense(cff), self.from_gf_dense(cfg) + def gf_gcd(self, f, g): + return self.from_gf_dense(gf_gcd(self.to_gf_dense(f), self.to_gf_dense(g), self.domain.mod, self.domain.dom)) + def gf_lcm(self, f, g): + return self.from_gf_dense(gf_lcm(self.to_gf_dense(f), self.to_gf_dense(g), self.domain.mod, self.domain.dom)) + def gf_gcdex(self, f, g): + return self.from_gf_dense(gf_gcdex(self.to_gf_dense(f), self.to_gf_dense(g), self.domain.mod, self.domain.dom)) + + def gf_monic(self, f): + return self.from_gf_dense(gf_monic(self.to_gf_dense(f), self.domain.mod, self.domain.dom)) + def gf_diff(self, f): + return self.from_gf_dense(gf_diff(self.to_gf_dense(f), self.domain.mod, self.domain.dom)) + + def gf_eval(self, f, a): + return gf_eval(self.to_gf_dense(f), a, self.domain.mod, self.domain.dom) + def gf_multi_eval(self, f, A): + return gf_multi_eval(self.to_gf_dense(f), A, self.domain.mod, self.domain.dom) + + def gf_compose(self, f, g): + return self.from_gf_dense(gf_compose(self.to_gf_dense(f), self.to_gf_dense(g), self.domain.mod, self.domain.dom)) + def gf_compose_mod(self, g, h, f): + return self.from_gf_dense(gf_compose_mod(self.to_gf_dense(g), self.to_gf_dense(h), self.to_gf_dense(f), self.domain.mod, self.domain.dom)) + + def gf_trace_map(self, a, b, c, n, f): + a = self.to_gf_dense(a) + b = self.to_gf_dense(b) + c = self.to_gf_dense(c) + f = self.to_gf_dense(f) + U, V = gf_trace_map(a, b, c, n, f, self.domain.mod, self.domain.dom) + return self.from_gf_dense(U), self.from_gf_dense(V) + + def gf_random(self, n): + return self.from_gf_dense(gf_random(n, self.domain.mod, self.domain.dom)) + def gf_irreducible(self, n): + return self.from_gf_dense(gf_irreducible(n, self.domain.mod, self.domain.dom)) + + def gf_irred_p_ben_or(self, f): + return gf_irred_p_ben_or(self.to_gf_dense(f), self.domain.mod, self.domain.dom) + def gf_irred_p_rabin(self, f): + return gf_irred_p_rabin(self.to_gf_dense(f), self.domain.mod, self.domain.dom) + def gf_irreducible_p(self, f): + return gf_irreducible_p(self.to_gf_dense(f), self.domain.mod, self.domain.dom) + def gf_sqf_p(self, f): + return gf_sqf_p(self.to_gf_dense(f), self.domain.mod, self.domain.dom) + + def gf_sqf_part(self, f): + return self.from_gf_dense(gf_sqf_part(self.to_gf_dense(f), self.domain.mod, self.domain.dom)) + def gf_sqf_list(self, f, all=False): + coeff, factors = gf_sqf_part(self.to_gf_dense(f), self.domain.mod, self.domain.dom) + return coeff, [ (self.from_gf_dense(g), k) for g, k in factors ] + + def gf_Qmatrix(self, f): + return gf_Qmatrix(self.to_gf_dense(f), self.domain.mod, self.domain.dom) + def gf_berlekamp(self, f): + factors = gf_berlekamp(self.to_gf_dense(f), self.domain.mod, self.domain.dom) + return [ self.from_gf_dense(g) for g in factors ] + + def gf_ddf_zassenhaus(self, f): + factors = gf_ddf_zassenhaus(self.to_gf_dense(f), self.domain.mod, self.domain.dom) + return [ (self.from_gf_dense(g), k) for g, k in factors ] + def gf_edf_zassenhaus(self, f, n): + factors = gf_edf_zassenhaus(self.to_gf_dense(f), self.domain.mod, self.domain.dom) + return [ self.from_gf_dense(g) for g in factors ] + + def gf_ddf_shoup(self, f): + factors = gf_ddf_shoup(self.to_gf_dense(f), self.domain.mod, self.domain.dom) + return [ (self.from_gf_dense(g), k) for g, k in factors ] + def gf_edf_shoup(self, f, n): + factors = gf_edf_shoup(self.to_gf_dense(f), self.domain.mod, self.domain.dom) + return [ self.from_gf_dense(g) for g in factors ] + + def gf_zassenhaus(self, f): + factors = gf_zassenhaus(self.to_gf_dense(f), self.domain.mod, self.domain.dom) + return [ self.from_gf_dense(g) for g in factors ] + def gf_shoup(self, f): + factors = gf_shoup(self.to_gf_dense(f), self.domain.mod, self.domain.dom) + return [ self.from_gf_dense(g) for g in factors ] + + def gf_factor_sqf(self, f, method=None): + coeff, factors = gf_factor_sqf(self.to_gf_dense(f), self.domain.mod, self.domain.dom, method=method) + return coeff, [ self.from_gf_dense(g) for g in factors ] + def gf_factor(self, f): + coeff, factors = gf_factor(self.to_gf_dense(f), self.domain.mod, self.domain.dom) + return coeff, [ (self.from_gf_dense(g), k) for g, k in factors ] diff --git a/lib/python3.12/site-packages/sympy/polys/euclidtools.py b/lib/python3.12/site-packages/sympy/polys/euclidtools.py new file mode 100644 index 0000000000000000000000000000000000000000..768a44a94930f05e701e9f27a8b0f570a3312314 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/polys/euclidtools.py @@ -0,0 +1,1912 @@ +"""Euclidean algorithms, GCDs, LCMs and polynomial remainder sequences. """ + + +from sympy.polys.densearith import ( + dup_sub_mul, + dup_neg, dmp_neg, + dmp_add, + dmp_sub, + dup_mul, dmp_mul, + dmp_pow, + dup_div, dmp_div, + dup_rem, + dup_quo, dmp_quo, + dup_prem, dmp_prem, + dup_mul_ground, dmp_mul_ground, + dmp_mul_term, + dup_quo_ground, dmp_quo_ground, + dup_max_norm, dmp_max_norm) +from sympy.polys.densebasic import ( + dup_strip, dmp_raise, + dmp_zero, dmp_one, dmp_ground, + dmp_one_p, dmp_zero_p, + dmp_zeros, + dup_degree, dmp_degree, dmp_degree_in, + dup_LC, dmp_LC, dmp_ground_LC, + dmp_multi_deflate, dmp_inflate, + dup_convert, dmp_convert, + dmp_apply_pairs) +from sympy.polys.densetools import ( + dup_clear_denoms, dmp_clear_denoms, + dup_diff, dmp_diff, + dup_eval, dmp_eval, dmp_eval_in, + dup_trunc, dmp_ground_trunc, + dup_monic, dmp_ground_monic, + dup_primitive, dmp_ground_primitive, + dup_extract, dmp_ground_extract) +from sympy.polys.galoistools import ( + gf_int, gf_crt) +from sympy.polys.polyconfig import query +from sympy.polys.polyerrors import ( + MultivariatePolynomialError, + HeuristicGCDFailed, + HomomorphismFailed, + NotInvertible, + DomainError) + + + + +def dup_half_gcdex(f, g, K): + """ + Half extended Euclidean algorithm in `F[x]`. + + Returns ``(s, h)`` such that ``h = gcd(f, g)`` and ``s*f = h (mod g)``. + + Examples + ======== + + >>> from sympy.polys import ring, QQ + >>> R, x = ring("x", QQ) + + >>> f = x**4 - 2*x**3 - 6*x**2 + 12*x + 15 + >>> g = x**3 + x**2 - 4*x - 4 + + >>> R.dup_half_gcdex(f, g) + (-1/5*x + 3/5, x + 1) + + """ + if not K.is_Field: + raise DomainError("Cannot compute half extended GCD over %s" % K) + + a, b = [K.one], [] + + while g: + q, r = dup_div(f, g, K) + f, g = g, r + a, b = b, dup_sub_mul(a, q, b, K) + + a = dup_quo_ground(a, dup_LC(f, K), K) + f = dup_monic(f, K) + + return a, f + + +def dmp_half_gcdex(f, g, u, K): + """ + Half extended Euclidean algorithm in `F[X]`. + + Examples + ======== + + >>> from sympy.polys import ring, ZZ + >>> R, x,y = ring("x,y", ZZ) + + """ + if not u: + return dup_half_gcdex(f, g, K) + else: + raise MultivariatePolynomialError(f, g) + + +def dup_gcdex(f, g, K): + """ + Extended Euclidean algorithm in `F[x]`. + + Returns ``(s, t, h)`` such that ``h = gcd(f, g)`` and ``s*f + t*g = h``. + + Examples + ======== + + >>> from sympy.polys import ring, QQ + >>> R, x = ring("x", QQ) + + >>> f = x**4 - 2*x**3 - 6*x**2 + 12*x + 15 + >>> g = x**3 + x**2 - 4*x - 4 + + >>> R.dup_gcdex(f, g) + (-1/5*x + 3/5, 1/5*x**2 - 6/5*x + 2, x + 1) + + """ + s, h = dup_half_gcdex(f, g, K) + + F = dup_sub_mul(h, s, f, K) + t = dup_quo(F, g, K) + + return s, t, h + + +def dmp_gcdex(f, g, u, K): + """ + Extended Euclidean algorithm in `F[X]`. + + Examples + ======== + + >>> from sympy.polys import ring, ZZ + >>> R, x,y = ring("x,y", ZZ) + + """ + if not u: + return dup_gcdex(f, g, K) + else: + raise MultivariatePolynomialError(f, g) + + +def dup_invert(f, g, K): + """ + Compute multiplicative inverse of `f` modulo `g` in `F[x]`. + + Examples + ======== + + >>> from sympy.polys import ring, QQ + >>> R, x = ring("x", QQ) + + >>> f = x**2 - 1 + >>> g = 2*x - 1 + >>> h = x - 1 + + >>> R.dup_invert(f, g) + -4/3 + + >>> R.dup_invert(f, h) + Traceback (most recent call last): + ... + NotInvertible: zero divisor + + """ + s, h = dup_half_gcdex(f, g, K) + + if h == [K.one]: + return dup_rem(s, g, K) + else: + raise NotInvertible("zero divisor") + + +def dmp_invert(f, g, u, K): + """ + Compute multiplicative inverse of `f` modulo `g` in `F[X]`. + + Examples + ======== + + >>> from sympy.polys import ring, QQ + >>> R, x = ring("x", QQ) + + """ + if not u: + return dup_invert(f, g, K) + else: + raise MultivariatePolynomialError(f, g) + + +def dup_euclidean_prs(f, g, K): + """ + Euclidean polynomial remainder sequence (PRS) in `K[x]`. + + Examples + ======== + + >>> from sympy.polys import ring, QQ + >>> R, x = ring("x", QQ) + + >>> f = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 + >>> g = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 + + >>> prs = R.dup_euclidean_prs(f, g) + + >>> prs[0] + x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 + >>> prs[1] + 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 + >>> prs[2] + -5/9*x**4 + 1/9*x**2 - 1/3 + >>> prs[3] + -117/25*x**2 - 9*x + 441/25 + >>> prs[4] + 233150/19773*x - 102500/6591 + >>> prs[5] + -1288744821/543589225 + + """ + prs = [f, g] + h = dup_rem(f, g, K) + + while h: + prs.append(h) + f, g = g, h + h = dup_rem(f, g, K) + + return prs + + +def dmp_euclidean_prs(f, g, u, K): + """ + Euclidean polynomial remainder sequence (PRS) in `K[X]`. + + Examples + ======== + + >>> from sympy.polys import ring, ZZ + >>> R, x,y = ring("x,y", ZZ) + + """ + if not u: + return dup_euclidean_prs(f, g, K) + else: + raise MultivariatePolynomialError(f, g) + + +def dup_primitive_prs(f, g, K): + """ + Primitive polynomial remainder sequence (PRS) in `K[x]`. + + Examples + ======== + + >>> from sympy.polys import ring, ZZ + >>> R, x = ring("x", ZZ) + + >>> f = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 + >>> g = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 + + >>> prs = R.dup_primitive_prs(f, g) + + >>> prs[0] + x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 + >>> prs[1] + 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 + >>> prs[2] + -5*x**4 + x**2 - 3 + >>> prs[3] + 13*x**2 + 25*x - 49 + >>> prs[4] + 4663*x - 6150 + >>> prs[5] + 1 + + """ + prs = [f, g] + _, h = dup_primitive(dup_prem(f, g, K), K) + + while h: + prs.append(h) + f, g = g, h + _, h = dup_primitive(dup_prem(f, g, K), K) + + return prs + + +def dmp_primitive_prs(f, g, u, K): + """ + Primitive polynomial remainder sequence (PRS) in `K[X]`. + + Examples + ======== + + >>> from sympy.polys import ring, ZZ + >>> R, x,y = ring("x,y", ZZ) + + """ + if not u: + return dup_primitive_prs(f, g, K) + else: + raise MultivariatePolynomialError(f, g) + + +def dup_inner_subresultants(f, g, K): + """ + Subresultant PRS algorithm in `K[x]`. + + Computes the subresultant polynomial remainder sequence (PRS) + and the non-zero scalar subresultants of `f` and `g`. + By [1] Thm. 3, these are the constants '-c' (- to optimize + computation of sign). + The first subdeterminant is set to 1 by convention to match + the polynomial and the scalar subdeterminants. + If 'deg(f) < deg(g)', the subresultants of '(g,f)' are computed. + + Examples + ======== + + >>> from sympy.polys import ring, ZZ + >>> R, x = ring("x", ZZ) + + >>> R.dup_inner_subresultants(x**2 + 1, x**2 - 1) + ([x**2 + 1, x**2 - 1, -2], [1, 1, 4]) + + References + ========== + + .. [1] W.S. Brown, The Subresultant PRS Algorithm. + ACM Transaction of Mathematical Software 4 (1978) 237-249 + + """ + n = dup_degree(f) + m = dup_degree(g) + + if n < m: + f, g = g, f + n, m = m, n + + if not f: + return [], [] + + if not g: + return [f], [K.one] + + R = [f, g] + d = n - m + + b = (-K.one)**(d + 1) + + h = dup_prem(f, g, K) + h = dup_mul_ground(h, b, K) + + lc = dup_LC(g, K) + c = lc**d + + # Conventional first scalar subdeterminant is 1 + S = [K.one, c] + c = -c + + while h: + k = dup_degree(h) + R.append(h) + + f, g, m, d = g, h, k, m - k + + b = -lc * c**d + + h = dup_prem(f, g, K) + h = dup_quo_ground(h, b, K) + + lc = dup_LC(g, K) + + if d > 1: # abnormal case + q = c**(d - 1) + c = K.quo((-lc)**d, q) + else: + c = -lc + + S.append(-c) + + return R, S + + +def dup_subresultants(f, g, K): + """ + Computes subresultant PRS of two polynomials in `K[x]`. + + Examples + ======== + + >>> from sympy.polys import ring, ZZ + >>> R, x = ring("x", ZZ) + + >>> R.dup_subresultants(x**2 + 1, x**2 - 1) + [x**2 + 1, x**2 - 1, -2] + + """ + return dup_inner_subresultants(f, g, K)[0] + + +def dup_prs_resultant(f, g, K): + """ + Resultant algorithm in `K[x]` using subresultant PRS. + + Examples + ======== + + >>> from sympy.polys import ring, ZZ + >>> R, x = ring("x", ZZ) + + >>> R.dup_prs_resultant(x**2 + 1, x**2 - 1) + (4, [x**2 + 1, x**2 - 1, -2]) + + """ + if not f or not g: + return (K.zero, []) + + R, S = dup_inner_subresultants(f, g, K) + + if dup_degree(R[-1]) > 0: + return (K.zero, R) + + return S[-1], R + + +def dup_resultant(f, g, K, includePRS=False): + """ + Computes resultant of two polynomials in `K[x]`. + + Examples + ======== + + >>> from sympy.polys import ring, ZZ + >>> R, x = ring("x", ZZ) + + >>> R.dup_resultant(x**2 + 1, x**2 - 1) + 4 + + """ + if includePRS: + return dup_prs_resultant(f, g, K) + return dup_prs_resultant(f, g, K)[0] + + +def dmp_inner_subresultants(f, g, u, K): + """ + Subresultant PRS algorithm in `K[X]`. + + Examples + ======== + + >>> from sympy.polys import ring, ZZ + >>> R, x,y = ring("x,y", ZZ) + + >>> f = 3*x**2*y - y**3 - 4 + >>> g = x**2 + x*y**3 - 9 + + >>> a = 3*x*y**4 + y**3 - 27*y + 4 + >>> b = -3*y**10 - 12*y**7 + y**6 - 54*y**4 + 8*y**3 + 729*y**2 - 216*y + 16 + + >>> prs = [f, g, a, b] + >>> sres = [[1], [1], [3, 0, 0, 0, 0], [-3, 0, 0, -12, 1, 0, -54, 8, 729, -216, 16]] + + >>> R.dmp_inner_subresultants(f, g) == (prs, sres) + True + + """ + if not u: + return dup_inner_subresultants(f, g, K) + + n = dmp_degree(f, u) + m = dmp_degree(g, u) + + if n < m: + f, g = g, f + n, m = m, n + + if dmp_zero_p(f, u): + return [], [] + + v = u - 1 + if dmp_zero_p(g, u): + return [f], [dmp_ground(K.one, v)] + + R = [f, g] + d = n - m + + b = dmp_pow(dmp_ground(-K.one, v), d + 1, v, K) + + h = dmp_prem(f, g, u, K) + h = dmp_mul_term(h, b, 0, u, K) + + lc = dmp_LC(g, K) + c = dmp_pow(lc, d, v, K) + + S = [dmp_ground(K.one, v), c] + c = dmp_neg(c, v, K) + + while not dmp_zero_p(h, u): + k = dmp_degree(h, u) + R.append(h) + + f, g, m, d = g, h, k, m - k + + b = dmp_mul(dmp_neg(lc, v, K), + dmp_pow(c, d, v, K), v, K) + + h = dmp_prem(f, g, u, K) + h = [ dmp_quo(ch, b, v, K) for ch in h ] + + lc = dmp_LC(g, K) + + if d > 1: + p = dmp_pow(dmp_neg(lc, v, K), d, v, K) + q = dmp_pow(c, d - 1, v, K) + c = dmp_quo(p, q, v, K) + else: + c = dmp_neg(lc, v, K) + + S.append(dmp_neg(c, v, K)) + + return R, S + + +def dmp_subresultants(f, g, u, K): + """ + Computes subresultant PRS of two polynomials in `K[X]`. + + Examples + ======== + + >>> from sympy.polys import ring, ZZ + >>> R, x,y = ring("x,y", ZZ) + + >>> f = 3*x**2*y - y**3 - 4 + >>> g = x**2 + x*y**3 - 9 + + >>> a = 3*x*y**4 + y**3 - 27*y + 4 + >>> b = -3*y**10 - 12*y**7 + y**6 - 54*y**4 + 8*y**3 + 729*y**2 - 216*y + 16 + + >>> R.dmp_subresultants(f, g) == [f, g, a, b] + True + + """ + return dmp_inner_subresultants(f, g, u, K)[0] + + +def dmp_prs_resultant(f, g, u, K): + """ + Resultant algorithm in `K[X]` using subresultant PRS. + + Examples + ======== + + >>> from sympy.polys import ring, ZZ + >>> R, x,y = ring("x,y", ZZ) + + >>> f = 3*x**2*y - y**3 - 4 + >>> g = x**2 + x*y**3 - 9 + + >>> a = 3*x*y**4 + y**3 - 27*y + 4 + >>> b = -3*y**10 - 12*y**7 + y**6 - 54*y**4 + 8*y**3 + 729*y**2 - 216*y + 16 + + >>> res, prs = R.dmp_prs_resultant(f, g) + + >>> res == b # resultant has n-1 variables + False + >>> res == b.drop(x) + True + >>> prs == [f, g, a, b] + True + + """ + if not u: + return dup_prs_resultant(f, g, K) + + if dmp_zero_p(f, u) or dmp_zero_p(g, u): + return (dmp_zero(u - 1), []) + + R, S = dmp_inner_subresultants(f, g, u, K) + + if dmp_degree(R[-1], u) > 0: + return (dmp_zero(u - 1), R) + + return S[-1], R + + +def dmp_zz_modular_resultant(f, g, p, u, K): + """ + Compute resultant of `f` and `g` modulo a prime `p`. + + Examples + ======== + + >>> from sympy.polys import ring, ZZ + >>> R, x,y = ring("x,y", ZZ) + + >>> f = x + y + 2 + >>> g = 2*x*y + x + 3 + + >>> R.dmp_zz_modular_resultant(f, g, 5) + -2*y**2 + 1 + + """ + if not u: + return gf_int(dup_prs_resultant(f, g, K)[0] % p, p) + + v = u - 1 + + n = dmp_degree(f, u) + m = dmp_degree(g, u) + + N = dmp_degree_in(f, 1, u) + M = dmp_degree_in(g, 1, u) + + B = n*M + m*N + + D, a = [K.one], -K.one + r = dmp_zero(v) + + while dup_degree(D) <= B: + while True: + a += K.one + + if a == p: + raise HomomorphismFailed('no luck') + + F = dmp_eval_in(f, gf_int(a, p), 1, u, K) + + if dmp_degree(F, v) == n: + G = dmp_eval_in(g, gf_int(a, p), 1, u, K) + + if dmp_degree(G, v) == m: + break + + R = dmp_zz_modular_resultant(F, G, p, v, K) + e = dmp_eval(r, a, v, K) + + if not v: + R = dup_strip([R]) + e = dup_strip([e]) + else: + R = [R] + e = [e] + + d = K.invert(dup_eval(D, a, K), p) + d = dup_mul_ground(D, d, K) + d = dmp_raise(d, v, 0, K) + + c = dmp_mul(d, dmp_sub(R, e, v, K), v, K) + r = dmp_add(r, c, v, K) + + r = dmp_ground_trunc(r, p, v, K) + + D = dup_mul(D, [K.one, -a], K) + D = dup_trunc(D, p, K) + + return r + + +def _collins_crt(r, R, P, p, K): + """Wrapper of CRT for Collins's resultant algorithm. """ + return gf_int(gf_crt([r, R], [P, p], K), P*p) + + +def dmp_zz_collins_resultant(f, g, u, K): + """ + Collins's modular resultant algorithm in `Z[X]`. + + Examples + ======== + + >>> from sympy.polys import ring, ZZ + >>> R, x,y = ring("x,y", ZZ) + + >>> f = x + y + 2 + >>> g = 2*x*y + x + 3 + + >>> R.dmp_zz_collins_resultant(f, g) + -2*y**2 - 5*y + 1 + + """ + + n = dmp_degree(f, u) + m = dmp_degree(g, u) + + if n < 0 or m < 0: + return dmp_zero(u - 1) + + A = dmp_max_norm(f, u, K) + B = dmp_max_norm(g, u, K) + + a = dmp_ground_LC(f, u, K) + b = dmp_ground_LC(g, u, K) + + v = u - 1 + + B = K(2)*K.factorial(K(n + m))*A**m*B**n + r, p, P = dmp_zero(v), K.one, K.one + + from sympy.ntheory import nextprime + + while P <= B: + p = K(nextprime(p)) + + while not (a % p) or not (b % p): + p = K(nextprime(p)) + + F = dmp_ground_trunc(f, p, u, K) + G = dmp_ground_trunc(g, p, u, K) + + try: + R = dmp_zz_modular_resultant(F, G, p, u, K) + except HomomorphismFailed: + continue + + if K.is_one(P): + r = R + else: + r = dmp_apply_pairs(r, R, _collins_crt, (P, p, K), v, K) + + P *= p + + return r + + +def dmp_qq_collins_resultant(f, g, u, K0): + """ + Collins's modular resultant algorithm in `Q[X]`. + + Examples + ======== + + >>> from sympy.polys import ring, QQ + >>> R, x,y = ring("x,y", QQ) + + >>> f = QQ(1,2)*x + y + QQ(2,3) + >>> g = 2*x*y + x + 3 + + >>> R.dmp_qq_collins_resultant(f, g) + -2*y**2 - 7/3*y + 5/6 + + """ + n = dmp_degree(f, u) + m = dmp_degree(g, u) + + if n < 0 or m < 0: + return dmp_zero(u - 1) + + K1 = K0.get_ring() + + cf, f = dmp_clear_denoms(f, u, K0, K1) + cg, g = dmp_clear_denoms(g, u, K0, K1) + + f = dmp_convert(f, u, K0, K1) + g = dmp_convert(g, u, K0, K1) + + r = dmp_zz_collins_resultant(f, g, u, K1) + r = dmp_convert(r, u - 1, K1, K0) + + c = K0.convert(cf**m * cg**n, K1) + + return dmp_quo_ground(r, c, u - 1, K0) + + +def dmp_resultant(f, g, u, K, includePRS=False): + """ + Computes resultant of two polynomials in `K[X]`. + + Examples + ======== + + >>> from sympy.polys import ring, ZZ + >>> R, x,y = ring("x,y", ZZ) + + >>> f = 3*x**2*y - y**3 - 4 + >>> g = x**2 + x*y**3 - 9 + + >>> R.dmp_resultant(f, g) + -3*y**10 - 12*y**7 + y**6 - 54*y**4 + 8*y**3 + 729*y**2 - 216*y + 16 + + """ + if not u: + return dup_resultant(f, g, K, includePRS=includePRS) + + if includePRS: + return dmp_prs_resultant(f, g, u, K) + + if K.is_Field: + if K.is_QQ and query('USE_COLLINS_RESULTANT'): + return dmp_qq_collins_resultant(f, g, u, K) + else: + if K.is_ZZ and query('USE_COLLINS_RESULTANT'): + return dmp_zz_collins_resultant(f, g, u, K) + + return dmp_prs_resultant(f, g, u, K)[0] + + +def dup_discriminant(f, K): + """ + Computes discriminant of a polynomial in `K[x]`. + + Examples + ======== + + >>> from sympy.polys import ring, ZZ + >>> R, x = ring("x", ZZ) + + >>> R.dup_discriminant(x**2 + 2*x + 3) + -8 + + """ + d = dup_degree(f) + + if d <= 0: + return K.zero + else: + s = (-1)**((d*(d - 1)) // 2) + c = dup_LC(f, K) + + r = dup_resultant(f, dup_diff(f, 1, K), K) + + return K.quo(r, c*K(s)) + + +def dmp_discriminant(f, u, K): + """ + Computes discriminant of a polynomial in `K[X]`. + + Examples + ======== + + >>> from sympy.polys import ring, ZZ + >>> R, x,y,z,t = ring("x,y,z,t", ZZ) + + >>> R.dmp_discriminant(x**2*y + x*z + t) + -4*y*t + z**2 + + """ + if not u: + return dup_discriminant(f, K) + + d, v = dmp_degree(f, u), u - 1 + + if d <= 0: + return dmp_zero(v) + else: + s = (-1)**((d*(d - 1)) // 2) + c = dmp_LC(f, K) + + r = dmp_resultant(f, dmp_diff(f, 1, u, K), u, K) + c = dmp_mul_ground(c, K(s), v, K) + + return dmp_quo(r, c, v, K) + + +def _dup_rr_trivial_gcd(f, g, K): + """Handle trivial cases in GCD algorithm over a ring. """ + if not (f or g): + return [], [], [] + elif not f: + if K.is_nonnegative(dup_LC(g, K)): + return g, [], [K.one] + else: + return dup_neg(g, K), [], [-K.one] + elif not g: + if K.is_nonnegative(dup_LC(f, K)): + return f, [K.one], [] + else: + return dup_neg(f, K), [-K.one], [] + + return None + + +def _dup_ff_trivial_gcd(f, g, K): + """Handle trivial cases in GCD algorithm over a field. """ + if not (f or g): + return [], [], [] + elif not f: + return dup_monic(g, K), [], [dup_LC(g, K)] + elif not g: + return dup_monic(f, K), [dup_LC(f, K)], [] + else: + return None + + +def _dmp_rr_trivial_gcd(f, g, u, K): + """Handle trivial cases in GCD algorithm over a ring. """ + zero_f = dmp_zero_p(f, u) + zero_g = dmp_zero_p(g, u) + if_contain_one = dmp_one_p(f, u, K) or dmp_one_p(g, u, K) + + if zero_f and zero_g: + return tuple(dmp_zeros(3, u, K)) + elif zero_f: + if K.is_nonnegative(dmp_ground_LC(g, u, K)): + return g, dmp_zero(u), dmp_one(u, K) + else: + return dmp_neg(g, u, K), dmp_zero(u), dmp_ground(-K.one, u) + elif zero_g: + if K.is_nonnegative(dmp_ground_LC(f, u, K)): + return f, dmp_one(u, K), dmp_zero(u) + else: + return dmp_neg(f, u, K), dmp_ground(-K.one, u), dmp_zero(u) + elif if_contain_one: + return dmp_one(u, K), f, g + elif query('USE_SIMPLIFY_GCD'): + return _dmp_simplify_gcd(f, g, u, K) + else: + return None + + +def _dmp_ff_trivial_gcd(f, g, u, K): + """Handle trivial cases in GCD algorithm over a field. """ + zero_f = dmp_zero_p(f, u) + zero_g = dmp_zero_p(g, u) + + if zero_f and zero_g: + return tuple(dmp_zeros(3, u, K)) + elif zero_f: + return (dmp_ground_monic(g, u, K), + dmp_zero(u), + dmp_ground(dmp_ground_LC(g, u, K), u)) + elif zero_g: + return (dmp_ground_monic(f, u, K), + dmp_ground(dmp_ground_LC(f, u, K), u), + dmp_zero(u)) + elif query('USE_SIMPLIFY_GCD'): + return _dmp_simplify_gcd(f, g, u, K) + else: + return None + + +def _dmp_simplify_gcd(f, g, u, K): + """Try to eliminate `x_0` from GCD computation in `K[X]`. """ + df = dmp_degree(f, u) + dg = dmp_degree(g, u) + + if df > 0 and dg > 0: + return None + + if not (df or dg): + F = dmp_LC(f, K) + G = dmp_LC(g, K) + else: + if not df: + F = dmp_LC(f, K) + G = dmp_content(g, u, K) + else: + F = dmp_content(f, u, K) + G = dmp_LC(g, K) + + v = u - 1 + h = dmp_gcd(F, G, v, K) + + cff = [ dmp_quo(cf, h, v, K) for cf in f ] + cfg = [ dmp_quo(cg, h, v, K) for cg in g ] + + return [h], cff, cfg + + +def dup_rr_prs_gcd(f, g, K): + """ + Computes polynomial GCD using subresultants over a ring. + + Returns ``(h, cff, cfg)`` such that ``a = gcd(f, g)``, ``cff = quo(f, h)``, + and ``cfg = quo(g, h)``. + + Examples + ======== + + >>> from sympy.polys import ring, ZZ + >>> R, x = ring("x", ZZ) + + >>> R.dup_rr_prs_gcd(x**2 - 1, x**2 - 3*x + 2) + (x - 1, x + 1, x - 2) + + """ + result = _dup_rr_trivial_gcd(f, g, K) + + if result is not None: + return result + + fc, F = dup_primitive(f, K) + gc, G = dup_primitive(g, K) + + c = K.gcd(fc, gc) + + h = dup_subresultants(F, G, K)[-1] + _, h = dup_primitive(h, K) + + c *= K.canonical_unit(dup_LC(h, K)) + + h = dup_mul_ground(h, c, K) + + cff = dup_quo(f, h, K) + cfg = dup_quo(g, h, K) + + return h, cff, cfg + + +def dup_ff_prs_gcd(f, g, K): + """ + Computes polynomial GCD using subresultants over a field. + + Returns ``(h, cff, cfg)`` such that ``a = gcd(f, g)``, ``cff = quo(f, h)``, + and ``cfg = quo(g, h)``. + + Examples + ======== + + >>> from sympy.polys import ring, QQ + >>> R, x = ring("x", QQ) + + >>> R.dup_ff_prs_gcd(x**2 - 1, x**2 - 3*x + 2) + (x - 1, x + 1, x - 2) + + """ + result = _dup_ff_trivial_gcd(f, g, K) + + if result is not None: + return result + + h = dup_subresultants(f, g, K)[-1] + h = dup_monic(h, K) + + cff = dup_quo(f, h, K) + cfg = dup_quo(g, h, K) + + return h, cff, cfg + + +def dmp_rr_prs_gcd(f, g, u, K): + """ + Computes polynomial GCD using subresultants over a ring. + + Returns ``(h, cff, cfg)`` such that ``a = gcd(f, g)``, ``cff = quo(f, h)``, + and ``cfg = quo(g, h)``. + + Examples + ======== + + >>> from sympy.polys import ring, ZZ + >>> R, x,y, = ring("x,y", ZZ) + + >>> f = x**2 + 2*x*y + y**2 + >>> g = x**2 + x*y + + >>> R.dmp_rr_prs_gcd(f, g) + (x + y, x + y, x) + + """ + if not u: + return dup_rr_prs_gcd(f, g, K) + + result = _dmp_rr_trivial_gcd(f, g, u, K) + + if result is not None: + return result + + fc, F = dmp_primitive(f, u, K) + gc, G = dmp_primitive(g, u, K) + + h = dmp_subresultants(F, G, u, K)[-1] + c, _, _ = dmp_rr_prs_gcd(fc, gc, u - 1, K) + + _, h = dmp_primitive(h, u, K) + h = dmp_mul_term(h, c, 0, u, K) + + unit = K.canonical_unit(dmp_ground_LC(h, u, K)) + + if unit != K.one: + h = dmp_mul_ground(h, unit, u, K) + + cff = dmp_quo(f, h, u, K) + cfg = dmp_quo(g, h, u, K) + + return h, cff, cfg + + +def dmp_ff_prs_gcd(f, g, u, K): + """ + Computes polynomial GCD using subresultants over a field. + + Returns ``(h, cff, cfg)`` such that ``a = gcd(f, g)``, ``cff = quo(f, h)``, + and ``cfg = quo(g, h)``. + + Examples + ======== + + >>> from sympy.polys import ring, QQ + >>> R, x,y, = ring("x,y", QQ) + + >>> f = QQ(1,2)*x**2 + x*y + QQ(1,2)*y**2 + >>> g = x**2 + x*y + + >>> R.dmp_ff_prs_gcd(f, g) + (x + y, 1/2*x + 1/2*y, x) + + """ + if not u: + return dup_ff_prs_gcd(f, g, K) + + result = _dmp_ff_trivial_gcd(f, g, u, K) + + if result is not None: + return result + + fc, F = dmp_primitive(f, u, K) + gc, G = dmp_primitive(g, u, K) + + h = dmp_subresultants(F, G, u, K)[-1] + c, _, _ = dmp_ff_prs_gcd(fc, gc, u - 1, K) + + _, h = dmp_primitive(h, u, K) + h = dmp_mul_term(h, c, 0, u, K) + h = dmp_ground_monic(h, u, K) + + cff = dmp_quo(f, h, u, K) + cfg = dmp_quo(g, h, u, K) + + return h, cff, cfg + +HEU_GCD_MAX = 6 + + +def _dup_zz_gcd_interpolate(h, x, K): + """Interpolate polynomial GCD from integer GCD. """ + f = [] + + while h: + g = h % x + + if g > x // 2: + g -= x + + f.insert(0, g) + h = (h - g) // x + + return f + + +def dup_zz_heu_gcd(f, g, K): + """ + Heuristic polynomial GCD in `Z[x]`. + + Given univariate polynomials `f` and `g` in `Z[x]`, returns + their GCD and cofactors, i.e. polynomials ``h``, ``cff`` and ``cfg`` + such that:: + + h = gcd(f, g), cff = quo(f, h) and cfg = quo(g, h) + + The algorithm is purely heuristic which means it may fail to compute + the GCD. This will be signaled by raising an exception. In this case + you will need to switch to another GCD method. + + The algorithm computes the polynomial GCD by evaluating polynomials + f and g at certain points and computing (fast) integer GCD of those + evaluations. The polynomial GCD is recovered from the integer image + by interpolation. The final step is to verify if the result is the + correct GCD. This gives cofactors as a side effect. + + Examples + ======== + + >>> from sympy.polys import ring, ZZ + >>> R, x = ring("x", ZZ) + + >>> R.dup_zz_heu_gcd(x**2 - 1, x**2 - 3*x + 2) + (x - 1, x + 1, x - 2) + + References + ========== + + .. [1] [Liao95]_ + + """ + result = _dup_rr_trivial_gcd(f, g, K) + + if result is not None: + return result + + df = dup_degree(f) + dg = dup_degree(g) + + gcd, f, g = dup_extract(f, g, K) + + if df == 0 or dg == 0: + return [gcd], f, g + + f_norm = dup_max_norm(f, K) + g_norm = dup_max_norm(g, K) + + B = K(2*min(f_norm, g_norm) + 29) + + x = max(min(B, 99*K.sqrt(B)), + 2*min(f_norm // abs(dup_LC(f, K)), + g_norm // abs(dup_LC(g, K))) + 4) + + for i in range(0, HEU_GCD_MAX): + ff = dup_eval(f, x, K) + gg = dup_eval(g, x, K) + + if ff and gg: + h = K.gcd(ff, gg) + + cff = ff // h + cfg = gg // h + + h = _dup_zz_gcd_interpolate(h, x, K) + h = dup_primitive(h, K)[1] + + cff_, r = dup_div(f, h, K) + + if not r: + cfg_, r = dup_div(g, h, K) + + if not r: + h = dup_mul_ground(h, gcd, K) + return h, cff_, cfg_ + + cff = _dup_zz_gcd_interpolate(cff, x, K) + + h, r = dup_div(f, cff, K) + + if not r: + cfg_, r = dup_div(g, h, K) + + if not r: + h = dup_mul_ground(h, gcd, K) + return h, cff, cfg_ + + cfg = _dup_zz_gcd_interpolate(cfg, x, K) + + h, r = dup_div(g, cfg, K) + + if not r: + cff_, r = dup_div(f, h, K) + + if not r: + h = dup_mul_ground(h, gcd, K) + return h, cff_, cfg + + x = 73794*x * K.sqrt(K.sqrt(x)) // 27011 + + raise HeuristicGCDFailed('no luck') + + +def _dmp_zz_gcd_interpolate(h, x, v, K): + """Interpolate polynomial GCD from integer GCD. """ + f = [] + + while not dmp_zero_p(h, v): + g = dmp_ground_trunc(h, x, v, K) + f.insert(0, g) + + h = dmp_sub(h, g, v, K) + h = dmp_quo_ground(h, x, v, K) + + if K.is_negative(dmp_ground_LC(f, v + 1, K)): + return dmp_neg(f, v + 1, K) + else: + return f + + +def dmp_zz_heu_gcd(f, g, u, K): + """ + Heuristic polynomial GCD in `Z[X]`. + + Given univariate polynomials `f` and `g` in `Z[X]`, returns + their GCD and cofactors, i.e. polynomials ``h``, ``cff`` and ``cfg`` + such that:: + + h = gcd(f, g), cff = quo(f, h) and cfg = quo(g, h) + + The algorithm is purely heuristic which means it may fail to compute + the GCD. This will be signaled by raising an exception. In this case + you will need to switch to another GCD method. + + The algorithm computes the polynomial GCD by evaluating polynomials + f and g at certain points and computing (fast) integer GCD of those + evaluations. The polynomial GCD is recovered from the integer image + by interpolation. The evaluation process reduces f and g variable by + variable into a large integer. The final step is to verify if the + interpolated polynomial is the correct GCD. This gives cofactors of + the input polynomials as a side effect. + + Examples + ======== + + >>> from sympy.polys import ring, ZZ + >>> R, x,y, = ring("x,y", ZZ) + + >>> f = x**2 + 2*x*y + y**2 + >>> g = x**2 + x*y + + >>> R.dmp_zz_heu_gcd(f, g) + (x + y, x + y, x) + + References + ========== + + .. [1] [Liao95]_ + + """ + if not u: + return dup_zz_heu_gcd(f, g, K) + + result = _dmp_rr_trivial_gcd(f, g, u, K) + + if result is not None: + return result + + gcd, f, g = dmp_ground_extract(f, g, u, K) + + f_norm = dmp_max_norm(f, u, K) + g_norm = dmp_max_norm(g, u, K) + + B = K(2*min(f_norm, g_norm) + 29) + + x = max(min(B, 99*K.sqrt(B)), + 2*min(f_norm // abs(dmp_ground_LC(f, u, K)), + g_norm // abs(dmp_ground_LC(g, u, K))) + 4) + + for i in range(0, HEU_GCD_MAX): + ff = dmp_eval(f, x, u, K) + gg = dmp_eval(g, x, u, K) + + v = u - 1 + + if not (dmp_zero_p(ff, v) or dmp_zero_p(gg, v)): + h, cff, cfg = dmp_zz_heu_gcd(ff, gg, v, K) + + h = _dmp_zz_gcd_interpolate(h, x, v, K) + h = dmp_ground_primitive(h, u, K)[1] + + cff_, r = dmp_div(f, h, u, K) + + if dmp_zero_p(r, u): + cfg_, r = dmp_div(g, h, u, K) + + if dmp_zero_p(r, u): + h = dmp_mul_ground(h, gcd, u, K) + return h, cff_, cfg_ + + cff = _dmp_zz_gcd_interpolate(cff, x, v, K) + + h, r = dmp_div(f, cff, u, K) + + if dmp_zero_p(r, u): + cfg_, r = dmp_div(g, h, u, K) + + if dmp_zero_p(r, u): + h = dmp_mul_ground(h, gcd, u, K) + return h, cff, cfg_ + + cfg = _dmp_zz_gcd_interpolate(cfg, x, v, K) + + h, r = dmp_div(g, cfg, u, K) + + if dmp_zero_p(r, u): + cff_, r = dmp_div(f, h, u, K) + + if dmp_zero_p(r, u): + h = dmp_mul_ground(h, gcd, u, K) + return h, cff_, cfg + + x = 73794*x * K.sqrt(K.sqrt(x)) // 27011 + + raise HeuristicGCDFailed('no luck') + + +def dup_qq_heu_gcd(f, g, K0): + """ + Heuristic polynomial GCD in `Q[x]`. + + Returns ``(h, cff, cfg)`` such that ``a = gcd(f, g)``, + ``cff = quo(f, h)``, and ``cfg = quo(g, h)``. + + Examples + ======== + + >>> from sympy.polys import ring, QQ + >>> R, x = ring("x", QQ) + + >>> f = QQ(1,2)*x**2 + QQ(7,4)*x + QQ(3,2) + >>> g = QQ(1,2)*x**2 + x + + >>> R.dup_qq_heu_gcd(f, g) + (x + 2, 1/2*x + 3/4, 1/2*x) + + """ + result = _dup_ff_trivial_gcd(f, g, K0) + + if result is not None: + return result + + K1 = K0.get_ring() + + cf, f = dup_clear_denoms(f, K0, K1) + cg, g = dup_clear_denoms(g, K0, K1) + + f = dup_convert(f, K0, K1) + g = dup_convert(g, K0, K1) + + h, cff, cfg = dup_zz_heu_gcd(f, g, K1) + + h = dup_convert(h, K1, K0) + + c = dup_LC(h, K0) + h = dup_monic(h, K0) + + cff = dup_convert(cff, K1, K0) + cfg = dup_convert(cfg, K1, K0) + + cff = dup_mul_ground(cff, K0.quo(c, cf), K0) + cfg = dup_mul_ground(cfg, K0.quo(c, cg), K0) + + return h, cff, cfg + + +def dmp_qq_heu_gcd(f, g, u, K0): + """ + Heuristic polynomial GCD in `Q[X]`. + + Returns ``(h, cff, cfg)`` such that ``a = gcd(f, g)``, + ``cff = quo(f, h)``, and ``cfg = quo(g, h)``. + + Examples + ======== + + >>> from sympy.polys import ring, QQ + >>> R, x,y, = ring("x,y", QQ) + + >>> f = QQ(1,4)*x**2 + x*y + y**2 + >>> g = QQ(1,2)*x**2 + x*y + + >>> R.dmp_qq_heu_gcd(f, g) + (x + 2*y, 1/4*x + 1/2*y, 1/2*x) + + """ + result = _dmp_ff_trivial_gcd(f, g, u, K0) + + if result is not None: + return result + + K1 = K0.get_ring() + + cf, f = dmp_clear_denoms(f, u, K0, K1) + cg, g = dmp_clear_denoms(g, u, K0, K1) + + f = dmp_convert(f, u, K0, K1) + g = dmp_convert(g, u, K0, K1) + + h, cff, cfg = dmp_zz_heu_gcd(f, g, u, K1) + + h = dmp_convert(h, u, K1, K0) + + c = dmp_ground_LC(h, u, K0) + h = dmp_ground_monic(h, u, K0) + + cff = dmp_convert(cff, u, K1, K0) + cfg = dmp_convert(cfg, u, K1, K0) + + cff = dmp_mul_ground(cff, K0.quo(c, cf), u, K0) + cfg = dmp_mul_ground(cfg, K0.quo(c, cg), u, K0) + + return h, cff, cfg + + +def dup_inner_gcd(f, g, K): + """ + Computes polynomial GCD and cofactors of `f` and `g` in `K[x]`. + + Returns ``(h, cff, cfg)`` such that ``a = gcd(f, g)``, + ``cff = quo(f, h)``, and ``cfg = quo(g, h)``. + + Examples + ======== + + >>> from sympy.polys import ring, ZZ + >>> R, x = ring("x", ZZ) + + >>> R.dup_inner_gcd(x**2 - 1, x**2 - 3*x + 2) + (x - 1, x + 1, x - 2) + + """ + # XXX: This used to check for K.is_Exact but leads to awkward results when + # the domain is something like RR[z] e.g.: + # + # >>> g, p, q = Poly(1, x).cancel(Poly(51.05*x*y - 1.0, x)) + # >>> g + # 1.0 + # >>> p + # Poly(17592186044421.0, x, domain='RR[y]') + # >>> q + # Poly(898081097567692.0*y*x - 17592186044421.0, x, domain='RR[y]')) + # + # Maybe it would be better to flatten into multivariate polynomials first. + if K.is_RR or K.is_CC: + try: + exact = K.get_exact() + except DomainError: + return [K.one], f, g + + f = dup_convert(f, K, exact) + g = dup_convert(g, K, exact) + + h, cff, cfg = dup_inner_gcd(f, g, exact) + + h = dup_convert(h, exact, K) + cff = dup_convert(cff, exact, K) + cfg = dup_convert(cfg, exact, K) + + return h, cff, cfg + elif K.is_Field: + if K.is_QQ and query('USE_HEU_GCD'): + try: + return dup_qq_heu_gcd(f, g, K) + except HeuristicGCDFailed: + pass + + return dup_ff_prs_gcd(f, g, K) + else: + if K.is_ZZ and query('USE_HEU_GCD'): + try: + return dup_zz_heu_gcd(f, g, K) + except HeuristicGCDFailed: + pass + + return dup_rr_prs_gcd(f, g, K) + + +def _dmp_inner_gcd(f, g, u, K): + """Helper function for `dmp_inner_gcd()`. """ + if not K.is_Exact: + try: + exact = K.get_exact() + except DomainError: + return dmp_one(u, K), f, g + + f = dmp_convert(f, u, K, exact) + g = dmp_convert(g, u, K, exact) + + h, cff, cfg = _dmp_inner_gcd(f, g, u, exact) + + h = dmp_convert(h, u, exact, K) + cff = dmp_convert(cff, u, exact, K) + cfg = dmp_convert(cfg, u, exact, K) + + return h, cff, cfg + elif K.is_Field: + if K.is_QQ and query('USE_HEU_GCD'): + try: + return dmp_qq_heu_gcd(f, g, u, K) + except HeuristicGCDFailed: + pass + + return dmp_ff_prs_gcd(f, g, u, K) + else: + if K.is_ZZ and query('USE_HEU_GCD'): + try: + return dmp_zz_heu_gcd(f, g, u, K) + except HeuristicGCDFailed: + pass + + return dmp_rr_prs_gcd(f, g, u, K) + + +def dmp_inner_gcd(f, g, u, K): + """ + Computes polynomial GCD and cofactors of `f` and `g` in `K[X]`. + + Returns ``(h, cff, cfg)`` such that ``a = gcd(f, g)``, + ``cff = quo(f, h)``, and ``cfg = quo(g, h)``. + + Examples + ======== + + >>> from sympy.polys import ring, ZZ + >>> R, x,y, = ring("x,y", ZZ) + + >>> f = x**2 + 2*x*y + y**2 + >>> g = x**2 + x*y + + >>> R.dmp_inner_gcd(f, g) + (x + y, x + y, x) + + """ + if not u: + return dup_inner_gcd(f, g, K) + + J, (f, g) = dmp_multi_deflate((f, g), u, K) + h, cff, cfg = _dmp_inner_gcd(f, g, u, K) + + return (dmp_inflate(h, J, u, K), + dmp_inflate(cff, J, u, K), + dmp_inflate(cfg, J, u, K)) + + +def dup_gcd(f, g, K): + """ + Computes polynomial GCD of `f` and `g` in `K[x]`. + + Examples + ======== + + >>> from sympy.polys import ring, ZZ + >>> R, x = ring("x", ZZ) + + >>> R.dup_gcd(x**2 - 1, x**2 - 3*x + 2) + x - 1 + + """ + return dup_inner_gcd(f, g, K)[0] + + +def dmp_gcd(f, g, u, K): + """ + Computes polynomial GCD of `f` and `g` in `K[X]`. + + Examples + ======== + + >>> from sympy.polys import ring, ZZ + >>> R, x,y, = ring("x,y", ZZ) + + >>> f = x**2 + 2*x*y + y**2 + >>> g = x**2 + x*y + + >>> R.dmp_gcd(f, g) + x + y + + """ + return dmp_inner_gcd(f, g, u, K)[0] + + +def dup_rr_lcm(f, g, K): + """ + Computes polynomial LCM over a ring in `K[x]`. + + Examples + ======== + + >>> from sympy.polys import ring, ZZ + >>> R, x = ring("x", ZZ) + + >>> R.dup_rr_lcm(x**2 - 1, x**2 - 3*x + 2) + x**3 - 2*x**2 - x + 2 + + """ + if not f or not g: + return dmp_zero(0) + + fc, f = dup_primitive(f, K) + gc, g = dup_primitive(g, K) + + c = K.lcm(fc, gc) + + h = dup_quo(dup_mul(f, g, K), + dup_gcd(f, g, K), K) + + u = K.canonical_unit(dup_LC(h, K)) + + return dup_mul_ground(h, c*u, K) + + +def dup_ff_lcm(f, g, K): + """ + Computes polynomial LCM over a field in `K[x]`. + + Examples + ======== + + >>> from sympy.polys import ring, QQ + >>> R, x = ring("x", QQ) + + >>> f = QQ(1,2)*x**2 + QQ(7,4)*x + QQ(3,2) + >>> g = QQ(1,2)*x**2 + x + + >>> R.dup_ff_lcm(f, g) + x**3 + 7/2*x**2 + 3*x + + """ + h = dup_quo(dup_mul(f, g, K), + dup_gcd(f, g, K), K) + + return dup_monic(h, K) + + +def dup_lcm(f, g, K): + """ + Computes polynomial LCM of `f` and `g` in `K[x]`. + + Examples + ======== + + >>> from sympy.polys import ring, ZZ + >>> R, x = ring("x", ZZ) + + >>> R.dup_lcm(x**2 - 1, x**2 - 3*x + 2) + x**3 - 2*x**2 - x + 2 + + """ + if K.is_Field: + return dup_ff_lcm(f, g, K) + else: + return dup_rr_lcm(f, g, K) + + +def dmp_rr_lcm(f, g, u, K): + """ + Computes polynomial LCM over a ring in `K[X]`. + + Examples + ======== + + >>> from sympy.polys import ring, ZZ + >>> R, x,y, = ring("x,y", ZZ) + + >>> f = x**2 + 2*x*y + y**2 + >>> g = x**2 + x*y + + >>> R.dmp_rr_lcm(f, g) + x**3 + 2*x**2*y + x*y**2 + + """ + fc, f = dmp_ground_primitive(f, u, K) + gc, g = dmp_ground_primitive(g, u, K) + + c = K.lcm(fc, gc) + + h = dmp_quo(dmp_mul(f, g, u, K), + dmp_gcd(f, g, u, K), u, K) + + return dmp_mul_ground(h, c, u, K) + + +def dmp_ff_lcm(f, g, u, K): + """ + Computes polynomial LCM over a field in `K[X]`. + + Examples + ======== + + >>> from sympy.polys import ring, QQ + >>> R, x,y, = ring("x,y", QQ) + + >>> f = QQ(1,4)*x**2 + x*y + y**2 + >>> g = QQ(1,2)*x**2 + x*y + + >>> R.dmp_ff_lcm(f, g) + x**3 + 4*x**2*y + 4*x*y**2 + + """ + h = dmp_quo(dmp_mul(f, g, u, K), + dmp_gcd(f, g, u, K), u, K) + + return dmp_ground_monic(h, u, K) + + +def dmp_lcm(f, g, u, K): + """ + Computes polynomial LCM of `f` and `g` in `K[X]`. + + Examples + ======== + + >>> from sympy.polys import ring, ZZ + >>> R, x,y, = ring("x,y", ZZ) + + >>> f = x**2 + 2*x*y + y**2 + >>> g = x**2 + x*y + + >>> R.dmp_lcm(f, g) + x**3 + 2*x**2*y + x*y**2 + + """ + if not u: + return dup_lcm(f, g, K) + + if K.is_Field: + return dmp_ff_lcm(f, g, u, K) + else: + return dmp_rr_lcm(f, g, u, K) + + +def dmp_content(f, u, K): + """ + Returns GCD of multivariate coefficients. + + Examples + ======== + + >>> from sympy.polys import ring, ZZ + >>> R, x,y, = ring("x,y", ZZ) + + >>> R.dmp_content(2*x*y + 6*x + 4*y + 12) + 2*y + 6 + + """ + cont, v = dmp_LC(f, K), u - 1 + + if dmp_zero_p(f, u): + return cont + + for c in f[1:]: + cont = dmp_gcd(cont, c, v, K) + + if dmp_one_p(cont, v, K): + break + + if K.is_negative(dmp_ground_LC(cont, v, K)): + return dmp_neg(cont, v, K) + else: + return cont + + +def dmp_primitive(f, u, K): + """ + Returns multivariate content and a primitive polynomial. + + Examples + ======== + + >>> from sympy.polys import ring, ZZ + >>> R, x,y, = ring("x,y", ZZ) + + >>> R.dmp_primitive(2*x*y + 6*x + 4*y + 12) + (2*y + 6, x + 2) + + """ + cont, v = dmp_content(f, u, K), u - 1 + + if dmp_zero_p(f, u) or dmp_one_p(cont, v, K): + return cont, f + else: + return cont, [ dmp_quo(c, cont, v, K) for c in f ] + + +def dup_cancel(f, g, K, include=True): + """ + Cancel common factors in a rational function `f/g`. + + Examples + ======== + + >>> from sympy.polys import ring, ZZ + >>> R, x = ring("x", ZZ) + + >>> R.dup_cancel(2*x**2 - 2, x**2 - 2*x + 1) + (2*x + 2, x - 1) + + """ + return dmp_cancel(f, g, 0, K, include=include) + + +def dmp_cancel(f, g, u, K, include=True): + """ + Cancel common factors in a rational function `f/g`. + + Examples + ======== + + >>> from sympy.polys import ring, ZZ + >>> R, x,y = ring("x,y", ZZ) + + >>> R.dmp_cancel(2*x**2 - 2, x**2 - 2*x + 1) + (2*x + 2, x - 1) + + """ + K0 = None + + if K.is_Field and K.has_assoc_Ring: + K0, K = K, K.get_ring() + + cq, f = dmp_clear_denoms(f, u, K0, K, convert=True) + cp, g = dmp_clear_denoms(g, u, K0, K, convert=True) + else: + cp, cq = K.one, K.one + + _, p, q = dmp_inner_gcd(f, g, u, K) + + if K0 is not None: + _, cp, cq = K.cofactors(cp, cq) + + p = dmp_convert(p, u, K, K0) + q = dmp_convert(q, u, K, K0) + + K = K0 + + p_neg = K.is_negative(dmp_ground_LC(p, u, K)) + q_neg = K.is_negative(dmp_ground_LC(q, u, K)) + + if p_neg and q_neg: + p, q = dmp_neg(p, u, K), dmp_neg(q, u, K) + elif p_neg: + cp, p = -cp, dmp_neg(p, u, K) + elif q_neg: + cp, q = -cp, dmp_neg(q, u, K) + + if not include: + return cp, cq, p, q + + p = dmp_mul_ground(p, cp, u, K) + q = dmp_mul_ground(q, cq, u, K) + + return p, q diff --git a/lib/python3.12/site-packages/sympy/polys/galoistools.py b/lib/python3.12/site-packages/sympy/polys/galoistools.py new file mode 100644 index 0000000000000000000000000000000000000000..b09f85057eced59b8054c6007f2b291a35a2fafb --- /dev/null +++ b/lib/python3.12/site-packages/sympy/polys/galoistools.py @@ -0,0 +1,2532 @@ +"""Dense univariate polynomials with coefficients in Galois fields. """ + +from math import ceil as _ceil, sqrt as _sqrt, prod + +from sympy.core.random import uniform, _randint +from sympy.external.gmpy import SYMPY_INTS, MPZ, invert +from sympy.polys.polyconfig import query +from sympy.polys.polyerrors import ExactQuotientFailed +from sympy.polys.polyutils import _sort_factors + + +def gf_crt(U, M, K=None): + """ + Chinese Remainder Theorem. + + Given a set of integer residues ``u_0,...,u_n`` and a set of + co-prime integer moduli ``m_0,...,m_n``, returns an integer + ``u``, such that ``u = u_i mod m_i`` for ``i = ``0,...,n``. + + Examples + ======== + + Consider a set of residues ``U = [49, 76, 65]`` + and a set of moduli ``M = [99, 97, 95]``. Then we have:: + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_crt + + >>> gf_crt([49, 76, 65], [99, 97, 95], ZZ) + 639985 + + This is the correct result because:: + + >>> [639985 % m for m in [99, 97, 95]] + [49, 76, 65] + + Note: this is a low-level routine with no error checking. + + See Also + ======== + + sympy.ntheory.modular.crt : a higher level crt routine + sympy.ntheory.modular.solve_congruence + + """ + p = prod(M, start=K.one) + v = K.zero + + for u, m in zip(U, M): + e = p // m + s, _, _ = K.gcdex(e, m) + v += e*(u*s % m) + + return v % p + + +def gf_crt1(M, K): + """ + First part of the Chinese Remainder Theorem. + + Examples + ======== + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_crt, gf_crt1, gf_crt2 + >>> U = [49, 76, 65] + >>> M = [99, 97, 95] + + The following two codes have the same result. + + >>> gf_crt(U, M, ZZ) + 639985 + + >>> p, E, S = gf_crt1(M, ZZ) + >>> gf_crt2(U, M, p, E, S, ZZ) + 639985 + + However, it is faster when we want to fix ``M`` and + compute for multiple U, i.e. the following cases: + + >>> p, E, S = gf_crt1(M, ZZ) + >>> Us = [[49, 76, 65], [23, 42, 67]] + >>> for U in Us: + ... print(gf_crt2(U, M, p, E, S, ZZ)) + 639985 + 236237 + + See Also + ======== + + sympy.ntheory.modular.crt1 : a higher level crt routine + sympy.polys.galoistools.gf_crt + sympy.polys.galoistools.gf_crt2 + + """ + E, S = [], [] + p = prod(M, start=K.one) + + for m in M: + E.append(p // m) + S.append(K.gcdex(E[-1], m)[0] % m) + + return p, E, S + + +def gf_crt2(U, M, p, E, S, K): + """ + Second part of the Chinese Remainder Theorem. + + See ``gf_crt1`` for usage. + + Examples + ======== + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_crt2 + + >>> U = [49, 76, 65] + >>> M = [99, 97, 95] + >>> p = 912285 + >>> E = [9215, 9405, 9603] + >>> S = [62, 24, 12] + + >>> gf_crt2(U, M, p, E, S, ZZ) + 639985 + + See Also + ======== + + sympy.ntheory.modular.crt2 : a higher level crt routine + sympy.polys.galoistools.gf_crt + sympy.polys.galoistools.gf_crt1 + + """ + v = K.zero + + for u, m, e, s in zip(U, M, E, S): + v += e*(u*s % m) + + return v % p + + +def gf_int(a, p): + """ + Coerce ``a mod p`` to an integer in the range ``[-p/2, p/2]``. + + Examples + ======== + + >>> from sympy.polys.galoistools import gf_int + + >>> gf_int(2, 7) + 2 + >>> gf_int(5, 7) + -2 + + """ + if a <= p // 2: + return a + else: + return a - p + + +def gf_degree(f): + """ + Return the leading degree of ``f``. + + Examples + ======== + + >>> from sympy.polys.galoistools import gf_degree + + >>> gf_degree([1, 1, 2, 0]) + 3 + >>> gf_degree([]) + -1 + + """ + return len(f) - 1 + + +def gf_LC(f, K): + """ + Return the leading coefficient of ``f``. + + Examples + ======== + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_LC + + >>> gf_LC([3, 0, 1], ZZ) + 3 + + """ + if not f: + return K.zero + else: + return f[0] + + +def gf_TC(f, K): + """ + Return the trailing coefficient of ``f``. + + Examples + ======== + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_TC + + >>> gf_TC([3, 0, 1], ZZ) + 1 + + """ + if not f: + return K.zero + else: + return f[-1] + + +def gf_strip(f): + """ + Remove leading zeros from ``f``. + + + Examples + ======== + + >>> from sympy.polys.galoistools import gf_strip + + >>> gf_strip([0, 0, 0, 3, 0, 1]) + [3, 0, 1] + + """ + if not f or f[0]: + return f + + k = 0 + + for coeff in f: + if coeff: + break + else: + k += 1 + + return f[k:] + + +def gf_trunc(f, p): + """ + Reduce all coefficients modulo ``p``. + + Examples + ======== + + >>> from sympy.polys.galoistools import gf_trunc + + >>> gf_trunc([7, -2, 3], 5) + [2, 3, 3] + + """ + return gf_strip([ a % p for a in f ]) + + +def gf_normal(f, p, K): + """ + Normalize all coefficients in ``K``. + + Examples + ======== + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_normal + + >>> gf_normal([5, 10, 21, -3], 5, ZZ) + [1, 2] + + """ + return gf_trunc(list(map(K, f)), p) + + +def gf_from_dict(f, p, K): + """ + Create a ``GF(p)[x]`` polynomial from a dict. + + Examples + ======== + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_from_dict + + >>> gf_from_dict({10: ZZ(4), 4: ZZ(33), 0: ZZ(-1)}, 5, ZZ) + [4, 0, 0, 0, 0, 0, 3, 0, 0, 0, 4] + + """ + n, h = max(f.keys()), [] + + if isinstance(n, SYMPY_INTS): + for k in range(n, -1, -1): + h.append(f.get(k, K.zero) % p) + else: + (n,) = n + + for k in range(n, -1, -1): + h.append(f.get((k,), K.zero) % p) + + return gf_trunc(h, p) + + +def gf_to_dict(f, p, symmetric=True): + """ + Convert a ``GF(p)[x]`` polynomial to a dict. + + Examples + ======== + + >>> from sympy.polys.galoistools import gf_to_dict + + >>> gf_to_dict([4, 0, 0, 0, 0, 0, 3, 0, 0, 0, 4], 5) + {0: -1, 4: -2, 10: -1} + >>> gf_to_dict([4, 0, 0, 0, 0, 0, 3, 0, 0, 0, 4], 5, symmetric=False) + {0: 4, 4: 3, 10: 4} + + """ + n, result = gf_degree(f), {} + + for k in range(0, n + 1): + if symmetric: + a = gf_int(f[n - k], p) + else: + a = f[n - k] + + if a: + result[k] = a + + return result + + +def gf_from_int_poly(f, p): + """ + Create a ``GF(p)[x]`` polynomial from ``Z[x]``. + + Examples + ======== + + >>> from sympy.polys.galoistools import gf_from_int_poly + + >>> gf_from_int_poly([7, -2, 3], 5) + [2, 3, 3] + + """ + return gf_trunc(f, p) + + +def gf_to_int_poly(f, p, symmetric=True): + """ + Convert a ``GF(p)[x]`` polynomial to ``Z[x]``. + + + Examples + ======== + + >>> from sympy.polys.galoistools import gf_to_int_poly + + >>> gf_to_int_poly([2, 3, 3], 5) + [2, -2, -2] + >>> gf_to_int_poly([2, 3, 3], 5, symmetric=False) + [2, 3, 3] + + """ + if symmetric: + return [ gf_int(c, p) for c in f ] + else: + return f + + +def gf_neg(f, p, K): + """ + Negate a polynomial in ``GF(p)[x]``. + + Examples + ======== + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_neg + + >>> gf_neg([3, 2, 1, 0], 5, ZZ) + [2, 3, 4, 0] + + """ + return [ -coeff % p for coeff in f ] + + +def gf_add_ground(f, a, p, K): + """ + Compute ``f + a`` where ``f`` in ``GF(p)[x]`` and ``a`` in ``GF(p)``. + + Examples + ======== + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_add_ground + + >>> gf_add_ground([3, 2, 4], 2, 5, ZZ) + [3, 2, 1] + + """ + if not f: + a = a % p + else: + a = (f[-1] + a) % p + + if len(f) > 1: + return f[:-1] + [a] + + if not a: + return [] + else: + return [a] + + +def gf_sub_ground(f, a, p, K): + """ + Compute ``f - a`` where ``f`` in ``GF(p)[x]`` and ``a`` in ``GF(p)``. + + Examples + ======== + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_sub_ground + + >>> gf_sub_ground([3, 2, 4], 2, 5, ZZ) + [3, 2, 2] + + """ + if not f: + a = -a % p + else: + a = (f[-1] - a) % p + + if len(f) > 1: + return f[:-1] + [a] + + if not a: + return [] + else: + return [a] + + +def gf_mul_ground(f, a, p, K): + """ + Compute ``f * a`` where ``f`` in ``GF(p)[x]`` and ``a`` in ``GF(p)``. + + Examples + ======== + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_mul_ground + + >>> gf_mul_ground([3, 2, 4], 2, 5, ZZ) + [1, 4, 3] + + """ + if not a: + return [] + else: + return [ (a*b) % p for b in f ] + + +def gf_quo_ground(f, a, p, K): + """ + Compute ``f/a`` where ``f`` in ``GF(p)[x]`` and ``a`` in ``GF(p)``. + + Examples + ======== + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_quo_ground + + >>> gf_quo_ground(ZZ.map([3, 2, 4]), ZZ(2), 5, ZZ) + [4, 1, 2] + + """ + return gf_mul_ground(f, K.invert(a, p), p, K) + + +def gf_add(f, g, p, K): + """ + Add polynomials in ``GF(p)[x]``. + + Examples + ======== + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_add + + >>> gf_add([3, 2, 4], [2, 2, 2], 5, ZZ) + [4, 1] + + """ + if not f: + return g + if not g: + return f + + df = gf_degree(f) + dg = gf_degree(g) + + if df == dg: + return gf_strip([ (a + b) % p for a, b in zip(f, g) ]) + else: + k = abs(df - dg) + + if df > dg: + h, f = f[:k], f[k:] + else: + h, g = g[:k], g[k:] + + return h + [ (a + b) % p for a, b in zip(f, g) ] + + +def gf_sub(f, g, p, K): + """ + Subtract polynomials in ``GF(p)[x]``. + + Examples + ======== + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_sub + + >>> gf_sub([3, 2, 4], [2, 2, 2], 5, ZZ) + [1, 0, 2] + + """ + if not g: + return f + if not f: + return gf_neg(g, p, K) + + df = gf_degree(f) + dg = gf_degree(g) + + if df == dg: + return gf_strip([ (a - b) % p for a, b in zip(f, g) ]) + else: + k = abs(df - dg) + + if df > dg: + h, f = f[:k], f[k:] + else: + h, g = gf_neg(g[:k], p, K), g[k:] + + return h + [ (a - b) % p for a, b in zip(f, g) ] + + +def gf_mul(f, g, p, K): + """ + Multiply polynomials in ``GF(p)[x]``. + + Examples + ======== + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_mul + + >>> gf_mul([3, 2, 4], [2, 2, 2], 5, ZZ) + [1, 0, 3, 2, 3] + + """ + df = gf_degree(f) + dg = gf_degree(g) + + dh = df + dg + h = [0]*(dh + 1) + + for i in range(0, dh + 1): + coeff = K.zero + + for j in range(max(0, i - dg), min(i, df) + 1): + coeff += f[j]*g[i - j] + + h[i] = coeff % p + + return gf_strip(h) + + +def gf_sqr(f, p, K): + """ + Square polynomials in ``GF(p)[x]``. + + Examples + ======== + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_sqr + + >>> gf_sqr([3, 2, 4], 5, ZZ) + [4, 2, 3, 1, 1] + + """ + df = gf_degree(f) + + dh = 2*df + h = [0]*(dh + 1) + + for i in range(0, dh + 1): + coeff = K.zero + + jmin = max(0, i - df) + jmax = min(i, df) + + n = jmax - jmin + 1 + + jmax = jmin + n // 2 - 1 + + for j in range(jmin, jmax + 1): + coeff += f[j]*f[i - j] + + coeff += coeff + + if n & 1: + elem = f[jmax + 1] + coeff += elem**2 + + h[i] = coeff % p + + return gf_strip(h) + + +def gf_add_mul(f, g, h, p, K): + """ + Returns ``f + g*h`` where ``f``, ``g``, ``h`` in ``GF(p)[x]``. + + Examples + ======== + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_add_mul + >>> gf_add_mul([3, 2, 4], [2, 2, 2], [1, 4], 5, ZZ) + [2, 3, 2, 2] + """ + return gf_add(f, gf_mul(g, h, p, K), p, K) + + +def gf_sub_mul(f, g, h, p, K): + """ + Compute ``f - g*h`` where ``f``, ``g``, ``h`` in ``GF(p)[x]``. + + Examples + ======== + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_sub_mul + + >>> gf_sub_mul([3, 2, 4], [2, 2, 2], [1, 4], 5, ZZ) + [3, 3, 2, 1] + + """ + return gf_sub(f, gf_mul(g, h, p, K), p, K) + + +def gf_expand(F, p, K): + """ + Expand results of :func:`~.factor` in ``GF(p)[x]``. + + Examples + ======== + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_expand + + >>> gf_expand([([3, 2, 4], 1), ([2, 2], 2), ([3, 1], 3)], 5, ZZ) + [4, 3, 0, 3, 0, 1, 4, 1] + + """ + if isinstance(F, tuple): + lc, F = F + else: + lc = K.one + + g = [lc] + + for f, k in F: + f = gf_pow(f, k, p, K) + g = gf_mul(g, f, p, K) + + return g + + +def gf_div(f, g, p, K): + """ + Division with remainder in ``GF(p)[x]``. + + Given univariate polynomials ``f`` and ``g`` with coefficients in a + finite field with ``p`` elements, returns polynomials ``q`` and ``r`` + (quotient and remainder) such that ``f = q*g + r``. + + Consider polynomials ``x**3 + x + 1`` and ``x**2 + x`` in GF(2):: + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_div, gf_add_mul + + >>> gf_div(ZZ.map([1, 0, 1, 1]), ZZ.map([1, 1, 0]), 2, ZZ) + ([1, 1], [1]) + + As result we obtained quotient ``x + 1`` and remainder ``1``, thus:: + + >>> gf_add_mul(ZZ.map([1]), ZZ.map([1, 1]), ZZ.map([1, 1, 0]), 2, ZZ) + [1, 0, 1, 1] + + References + ========== + + .. [1] [Monagan93]_ + .. [2] [Gathen99]_ + + """ + df = gf_degree(f) + dg = gf_degree(g) + + if not g: + raise ZeroDivisionError("polynomial division") + elif df < dg: + return [], f + + inv = K.invert(g[0], p) + + h, dq, dr = list(f), df - dg, dg - 1 + + for i in range(0, df + 1): + coeff = h[i] + + for j in range(max(0, dg - i), min(df - i, dr) + 1): + coeff -= h[i + j - dg] * g[dg - j] + + if i <= dq: + coeff *= inv + + h[i] = coeff % p + + return h[:dq + 1], gf_strip(h[dq + 1:]) + + +def gf_rem(f, g, p, K): + """ + Compute polynomial remainder in ``GF(p)[x]``. + + Examples + ======== + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_rem + + >>> gf_rem(ZZ.map([1, 0, 1, 1]), ZZ.map([1, 1, 0]), 2, ZZ) + [1] + + """ + return gf_div(f, g, p, K)[1] + + +def gf_quo(f, g, p, K): + """ + Compute exact quotient in ``GF(p)[x]``. + + Examples + ======== + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_quo + + >>> gf_quo(ZZ.map([1, 0, 1, 1]), ZZ.map([1, 1, 0]), 2, ZZ) + [1, 1] + >>> gf_quo(ZZ.map([1, 0, 3, 2, 3]), ZZ.map([2, 2, 2]), 5, ZZ) + [3, 2, 4] + + """ + df = gf_degree(f) + dg = gf_degree(g) + + if not g: + raise ZeroDivisionError("polynomial division") + elif df < dg: + return [] + + inv = K.invert(g[0], p) + + h, dq, dr = f[:], df - dg, dg - 1 + + for i in range(0, dq + 1): + coeff = h[i] + + for j in range(max(0, dg - i), min(df - i, dr) + 1): + coeff -= h[i + j - dg] * g[dg - j] + + h[i] = (coeff * inv) % p + + return h[:dq + 1] + + +def gf_exquo(f, g, p, K): + """ + Compute polynomial quotient in ``GF(p)[x]``. + + Examples + ======== + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_exquo + + >>> gf_exquo(ZZ.map([1, 0, 3, 2, 3]), ZZ.map([2, 2, 2]), 5, ZZ) + [3, 2, 4] + + >>> gf_exquo(ZZ.map([1, 0, 1, 1]), ZZ.map([1, 1, 0]), 2, ZZ) + Traceback (most recent call last): + ... + ExactQuotientFailed: [1, 1, 0] does not divide [1, 0, 1, 1] + + """ + q, r = gf_div(f, g, p, K) + + if not r: + return q + else: + raise ExactQuotientFailed(f, g) + + +def gf_lshift(f, n, K): + """ + Efficiently multiply ``f`` by ``x**n``. + + Examples + ======== + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_lshift + + >>> gf_lshift([3, 2, 4], 4, ZZ) + [3, 2, 4, 0, 0, 0, 0] + + """ + if not f: + return f + else: + return f + [K.zero]*n + + +def gf_rshift(f, n, K): + """ + Efficiently divide ``f`` by ``x**n``. + + Examples + ======== + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_rshift + + >>> gf_rshift([1, 2, 3, 4, 0], 3, ZZ) + ([1, 2], [3, 4, 0]) + + """ + if not n: + return f, [] + else: + return f[:-n], f[-n:] + + +def gf_pow(f, n, p, K): + """ + Compute ``f**n`` in ``GF(p)[x]`` using repeated squaring. + + Examples + ======== + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_pow + + >>> gf_pow([3, 2, 4], 3, 5, ZZ) + [2, 4, 4, 2, 2, 1, 4] + + """ + if not n: + return [K.one] + elif n == 1: + return f + elif n == 2: + return gf_sqr(f, p, K) + + h = [K.one] + + while True: + if n & 1: + h = gf_mul(h, f, p, K) + n -= 1 + + n >>= 1 + + if not n: + break + + f = gf_sqr(f, p, K) + + return h + +def gf_frobenius_monomial_base(g, p, K): + """ + return the list of ``x**(i*p) mod g in Z_p`` for ``i = 0, .., n - 1`` + where ``n = gf_degree(g)`` + + Examples + ======== + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_frobenius_monomial_base + >>> g = ZZ.map([1, 0, 2, 1]) + >>> gf_frobenius_monomial_base(g, 5, ZZ) + [[1], [4, 4, 2], [1, 2]] + + """ + n = gf_degree(g) + if n == 0: + return [] + b = [0]*n + b[0] = [1] + if p < n: + for i in range(1, n): + mon = gf_lshift(b[i - 1], p, K) + b[i] = gf_rem(mon, g, p, K) + elif n > 1: + b[1] = gf_pow_mod([K.one, K.zero], p, g, p, K) + for i in range(2, n): + b[i] = gf_mul(b[i - 1], b[1], p, K) + b[i] = gf_rem(b[i], g, p, K) + + return b + +def gf_frobenius_map(f, g, b, p, K): + """ + compute gf_pow_mod(f, p, g, p, K) using the Frobenius map + + Parameters + ========== + + f, g : polynomials in ``GF(p)[x]`` + b : frobenius monomial base + p : prime number + K : domain + + Examples + ======== + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_frobenius_monomial_base, gf_frobenius_map + >>> f = ZZ.map([2, 1, 0, 1]) + >>> g = ZZ.map([1, 0, 2, 1]) + >>> p = 5 + >>> b = gf_frobenius_monomial_base(g, p, ZZ) + >>> r = gf_frobenius_map(f, g, b, p, ZZ) + >>> gf_frobenius_map(f, g, b, p, ZZ) + [4, 0, 3] + """ + m = gf_degree(g) + if gf_degree(f) >= m: + f = gf_rem(f, g, p, K) + if not f: + return [] + n = gf_degree(f) + sf = [f[-1]] + for i in range(1, n + 1): + v = gf_mul_ground(b[i], f[n - i], p, K) + sf = gf_add(sf, v, p, K) + return sf + +def _gf_pow_pnm1d2(f, n, g, b, p, K): + """ + utility function for ``gf_edf_zassenhaus`` + Compute ``f**((p**n - 1) // 2)`` in ``GF(p)[x]/(g)`` + ``f**((p**n - 1) // 2) = (f*f**p*...*f**(p**n - 1))**((p - 1) // 2)`` + """ + f = gf_rem(f, g, p, K) + h = f + r = f + for i in range(1, n): + h = gf_frobenius_map(h, g, b, p, K) + r = gf_mul(r, h, p, K) + r = gf_rem(r, g, p, K) + + res = gf_pow_mod(r, (p - 1)//2, g, p, K) + return res + +def gf_pow_mod(f, n, g, p, K): + """ + Compute ``f**n`` in ``GF(p)[x]/(g)`` using repeated squaring. + + Given polynomials ``f`` and ``g`` in ``GF(p)[x]`` and a non-negative + integer ``n``, efficiently computes ``f**n (mod g)`` i.e. the remainder + of ``f**n`` from division by ``g``, using the repeated squaring algorithm. + + Examples + ======== + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_pow_mod + + >>> gf_pow_mod(ZZ.map([3, 2, 4]), 3, ZZ.map([1, 1]), 5, ZZ) + [] + + References + ========== + + .. [1] [Gathen99]_ + + """ + if not n: + return [K.one] + elif n == 1: + return gf_rem(f, g, p, K) + elif n == 2: + return gf_rem(gf_sqr(f, p, K), g, p, K) + + h = [K.one] + + while True: + if n & 1: + h = gf_mul(h, f, p, K) + h = gf_rem(h, g, p, K) + n -= 1 + + n >>= 1 + + if not n: + break + + f = gf_sqr(f, p, K) + f = gf_rem(f, g, p, K) + + return h + + +def gf_gcd(f, g, p, K): + """ + Euclidean Algorithm in ``GF(p)[x]``. + + Examples + ======== + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_gcd + + >>> gf_gcd(ZZ.map([3, 2, 4]), ZZ.map([2, 2, 3]), 5, ZZ) + [1, 3] + + """ + while g: + f, g = g, gf_rem(f, g, p, K) + + return gf_monic(f, p, K)[1] + + +def gf_lcm(f, g, p, K): + """ + Compute polynomial LCM in ``GF(p)[x]``. + + Examples + ======== + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_lcm + + >>> gf_lcm(ZZ.map([3, 2, 4]), ZZ.map([2, 2, 3]), 5, ZZ) + [1, 2, 0, 4] + + """ + if not f or not g: + return [] + + h = gf_quo(gf_mul(f, g, p, K), + gf_gcd(f, g, p, K), p, K) + + return gf_monic(h, p, K)[1] + + +def gf_cofactors(f, g, p, K): + """ + Compute polynomial GCD and cofactors in ``GF(p)[x]``. + + Examples + ======== + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_cofactors + + >>> gf_cofactors(ZZ.map([3, 2, 4]), ZZ.map([2, 2, 3]), 5, ZZ) + ([1, 3], [3, 3], [2, 1]) + + """ + if not f and not g: + return ([], [], []) + + h = gf_gcd(f, g, p, K) + + return (h, gf_quo(f, h, p, K), + gf_quo(g, h, p, K)) + + +def gf_gcdex(f, g, p, K): + """ + Extended Euclidean Algorithm in ``GF(p)[x]``. + + Given polynomials ``f`` and ``g`` in ``GF(p)[x]``, computes polynomials + ``s``, ``t`` and ``h``, such that ``h = gcd(f, g)`` and ``s*f + t*g = h``. + The typical application of EEA is solving polynomial diophantine equations. + + Consider polynomials ``f = (x + 7) (x + 1)``, ``g = (x + 7) (x**2 + 1)`` + in ``GF(11)[x]``. Application of Extended Euclidean Algorithm gives:: + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_gcdex, gf_mul, gf_add + + >>> s, t, g = gf_gcdex(ZZ.map([1, 8, 7]), ZZ.map([1, 7, 1, 7]), 11, ZZ) + >>> s, t, g + ([5, 6], [6], [1, 7]) + + As result we obtained polynomials ``s = 5*x + 6`` and ``t = 6``, and + additionally ``gcd(f, g) = x + 7``. This is correct because:: + + >>> S = gf_mul(s, ZZ.map([1, 8, 7]), 11, ZZ) + >>> T = gf_mul(t, ZZ.map([1, 7, 1, 7]), 11, ZZ) + + >>> gf_add(S, T, 11, ZZ) == [1, 7] + True + + References + ========== + + .. [1] [Gathen99]_ + + """ + if not (f or g): + return [K.one], [], [] + + p0, r0 = gf_monic(f, p, K) + p1, r1 = gf_monic(g, p, K) + + if not f: + return [], [K.invert(p1, p)], r1 + if not g: + return [K.invert(p0, p)], [], r0 + + s0, s1 = [K.invert(p0, p)], [] + t0, t1 = [], [K.invert(p1, p)] + + while True: + Q, R = gf_div(r0, r1, p, K) + + if not R: + break + + (lc, r1), r0 = gf_monic(R, p, K), r1 + + inv = K.invert(lc, p) + + s = gf_sub_mul(s0, s1, Q, p, K) + t = gf_sub_mul(t0, t1, Q, p, K) + + s1, s0 = gf_mul_ground(s, inv, p, K), s1 + t1, t0 = gf_mul_ground(t, inv, p, K), t1 + + return s1, t1, r1 + + +def gf_monic(f, p, K): + """ + Compute LC and a monic polynomial in ``GF(p)[x]``. + + Examples + ======== + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_monic + + >>> gf_monic(ZZ.map([3, 2, 4]), 5, ZZ) + (3, [1, 4, 3]) + + """ + if not f: + return K.zero, [] + else: + lc = f[0] + + if K.is_one(lc): + return lc, list(f) + else: + return lc, gf_quo_ground(f, lc, p, K) + + +def gf_diff(f, p, K): + """ + Differentiate polynomial in ``GF(p)[x]``. + + Examples + ======== + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_diff + + >>> gf_diff([3, 2, 4], 5, ZZ) + [1, 2] + + """ + df = gf_degree(f) + + h, n = [K.zero]*df, df + + for coeff in f[:-1]: + coeff *= K(n) + coeff %= p + + if coeff: + h[df - n] = coeff + + n -= 1 + + return gf_strip(h) + + +def gf_eval(f, a, p, K): + """ + Evaluate ``f(a)`` in ``GF(p)`` using Horner scheme. + + Examples + ======== + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_eval + + >>> gf_eval([3, 2, 4], 2, 5, ZZ) + 0 + + """ + result = K.zero + + for c in f: + result *= a + result += c + result %= p + + return result + + +def gf_multi_eval(f, A, p, K): + """ + Evaluate ``f(a)`` for ``a`` in ``[a_1, ..., a_n]``. + + Examples + ======== + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_multi_eval + + >>> gf_multi_eval([3, 2, 4], [0, 1, 2, 3, 4], 5, ZZ) + [4, 4, 0, 2, 0] + + """ + return [ gf_eval(f, a, p, K) for a in A ] + + +def gf_compose(f, g, p, K): + """ + Compute polynomial composition ``f(g)`` in ``GF(p)[x]``. + + Examples + ======== + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_compose + + >>> gf_compose([3, 2, 4], [2, 2, 2], 5, ZZ) + [2, 4, 0, 3, 0] + + """ + if len(g) <= 1: + return gf_strip([gf_eval(f, gf_LC(g, K), p, K)]) + + if not f: + return [] + + h = [f[0]] + + for c in f[1:]: + h = gf_mul(h, g, p, K) + h = gf_add_ground(h, c, p, K) + + return h + + +def gf_compose_mod(g, h, f, p, K): + """ + Compute polynomial composition ``g(h)`` in ``GF(p)[x]/(f)``. + + Examples + ======== + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_compose_mod + + >>> gf_compose_mod(ZZ.map([3, 2, 4]), ZZ.map([2, 2, 2]), ZZ.map([4, 3]), 5, ZZ) + [4] + + """ + if not g: + return [] + + comp = [g[0]] + + for a in g[1:]: + comp = gf_mul(comp, h, p, K) + comp = gf_add_ground(comp, a, p, K) + comp = gf_rem(comp, f, p, K) + + return comp + + +def gf_trace_map(a, b, c, n, f, p, K): + """ + Compute polynomial trace map in ``GF(p)[x]/(f)``. + + Given a polynomial ``f`` in ``GF(p)[x]``, polynomials ``a``, ``b``, + ``c`` in the quotient ring ``GF(p)[x]/(f)`` such that ``b = c**t + (mod f)`` for some positive power ``t`` of ``p``, and a positive + integer ``n``, returns a mapping:: + + a -> a**t**n, a + a**t + a**t**2 + ... + a**t**n (mod f) + + In factorization context, ``b = x**p mod f`` and ``c = x mod f``. + This way we can efficiently compute trace polynomials in equal + degree factorization routine, much faster than with other methods, + like iterated Frobenius algorithm, for large degrees. + + Examples + ======== + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_trace_map + + >>> gf_trace_map([1, 2], [4, 4], [1, 1], 4, [3, 2, 4], 5, ZZ) + ([1, 3], [1, 3]) + + References + ========== + + .. [1] [Gathen92]_ + + """ + u = gf_compose_mod(a, b, f, p, K) + v = b + + if n & 1: + U = gf_add(a, u, p, K) + V = b + else: + U = a + V = c + + n >>= 1 + + while n: + u = gf_add(u, gf_compose_mod(u, v, f, p, K), p, K) + v = gf_compose_mod(v, v, f, p, K) + + if n & 1: + U = gf_add(U, gf_compose_mod(u, V, f, p, K), p, K) + V = gf_compose_mod(v, V, f, p, K) + + n >>= 1 + + return gf_compose_mod(a, V, f, p, K), U + +def _gf_trace_map(f, n, g, b, p, K): + """ + utility for ``gf_edf_shoup`` + """ + f = gf_rem(f, g, p, K) + h = f + r = f + for i in range(1, n): + h = gf_frobenius_map(h, g, b, p, K) + r = gf_add(r, h, p, K) + r = gf_rem(r, g, p, K) + return r + + +def gf_random(n, p, K): + """ + Generate a random polynomial in ``GF(p)[x]`` of degree ``n``. + + Examples + ======== + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_random + >>> gf_random(10, 5, ZZ) #doctest: +SKIP + [1, 2, 3, 2, 1, 1, 1, 2, 0, 4, 2] + + """ + pi = int(p) + return [K.one] + [ K(int(uniform(0, pi))) for i in range(0, n) ] + + +def gf_irreducible(n, p, K): + """ + Generate random irreducible polynomial of degree ``n`` in ``GF(p)[x]``. + + Examples + ======== + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_irreducible + >>> gf_irreducible(10, 5, ZZ) #doctest: +SKIP + [1, 4, 2, 2, 3, 2, 4, 1, 4, 0, 4] + + """ + while True: + f = gf_random(n, p, K) + if gf_irreducible_p(f, p, K): + return f + + +def gf_irred_p_ben_or(f, p, K): + """ + Ben-Or's polynomial irreducibility test over finite fields. + + Examples + ======== + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_irred_p_ben_or + + >>> gf_irred_p_ben_or(ZZ.map([1, 4, 2, 2, 3, 2, 4, 1, 4, 0, 4]), 5, ZZ) + True + >>> gf_irred_p_ben_or(ZZ.map([3, 2, 4]), 5, ZZ) + False + + """ + n = gf_degree(f) + + if n <= 1: + return True + + _, f = gf_monic(f, p, K) + if n < 5: + H = h = gf_pow_mod([K.one, K.zero], p, f, p, K) + + for i in range(0, n//2): + g = gf_sub(h, [K.one, K.zero], p, K) + + if gf_gcd(f, g, p, K) == [K.one]: + h = gf_compose_mod(h, H, f, p, K) + else: + return False + else: + b = gf_frobenius_monomial_base(f, p, K) + H = h = gf_frobenius_map([K.one, K.zero], f, b, p, K) + for i in range(0, n//2): + g = gf_sub(h, [K.one, K.zero], p, K) + if gf_gcd(f, g, p, K) == [K.one]: + h = gf_frobenius_map(h, f, b, p, K) + else: + return False + + return True + + +def gf_irred_p_rabin(f, p, K): + """ + Rabin's polynomial irreducibility test over finite fields. + + Examples + ======== + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_irred_p_rabin + + >>> gf_irred_p_rabin(ZZ.map([1, 4, 2, 2, 3, 2, 4, 1, 4, 0, 4]), 5, ZZ) + True + >>> gf_irred_p_rabin(ZZ.map([3, 2, 4]), 5, ZZ) + False + + """ + n = gf_degree(f) + + if n <= 1: + return True + + _, f = gf_monic(f, p, K) + + x = [K.one, K.zero] + + from sympy.ntheory import factorint + + indices = { n//d for d in factorint(n) } + + b = gf_frobenius_monomial_base(f, p, K) + h = b[1] + + for i in range(1, n): + if i in indices: + g = gf_sub(h, x, p, K) + + if gf_gcd(f, g, p, K) != [K.one]: + return False + + h = gf_frobenius_map(h, f, b, p, K) + + return h == x + +_irred_methods = { + 'ben-or': gf_irred_p_ben_or, + 'rabin': gf_irred_p_rabin, +} + + +def gf_irreducible_p(f, p, K): + """ + Test irreducibility of a polynomial ``f`` in ``GF(p)[x]``. + + Examples + ======== + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_irreducible_p + + >>> gf_irreducible_p(ZZ.map([1, 4, 2, 2, 3, 2, 4, 1, 4, 0, 4]), 5, ZZ) + True + >>> gf_irreducible_p(ZZ.map([3, 2, 4]), 5, ZZ) + False + + """ + method = query('GF_IRRED_METHOD') + + if method is not None: + irred = _irred_methods[method](f, p, K) + else: + irred = gf_irred_p_rabin(f, p, K) + + return irred + + +def gf_sqf_p(f, p, K): + """ + Return ``True`` if ``f`` is square-free in ``GF(p)[x]``. + + Examples + ======== + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_sqf_p + + >>> gf_sqf_p(ZZ.map([3, 2, 4]), 5, ZZ) + True + >>> gf_sqf_p(ZZ.map([2, 4, 4, 2, 2, 1, 4]), 5, ZZ) + False + + """ + _, f = gf_monic(f, p, K) + + if not f: + return True + else: + return gf_gcd(f, gf_diff(f, p, K), p, K) == [K.one] + + +def gf_sqf_part(f, p, K): + """ + Return square-free part of a ``GF(p)[x]`` polynomial. + + Examples + ======== + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_sqf_part + + >>> gf_sqf_part(ZZ.map([1, 1, 3, 0, 1, 0, 2, 2, 1]), 5, ZZ) + [1, 4, 3] + + """ + _, sqf = gf_sqf_list(f, p, K) + + g = [K.one] + + for f, _ in sqf: + g = gf_mul(g, f, p, K) + + return g + + +def gf_sqf_list(f, p, K, all=False): + """ + Return the square-free decomposition of a ``GF(p)[x]`` polynomial. + + Given a polynomial ``f`` in ``GF(p)[x]``, returns the leading coefficient + of ``f`` and a square-free decomposition ``f_1**e_1 f_2**e_2 ... f_k**e_k`` + such that all ``f_i`` are monic polynomials and ``(f_i, f_j)`` for ``i != j`` + are co-prime and ``e_1 ... e_k`` are given in increasing order. All trivial + terms (i.e. ``f_i = 1``) are not included in the output. + + Consider polynomial ``f = x**11 + 1`` over ``GF(11)[x]``:: + + >>> from sympy.polys.domains import ZZ + + >>> from sympy.polys.galoistools import ( + ... gf_from_dict, gf_diff, gf_sqf_list, gf_pow, + ... ) + ... # doctest: +NORMALIZE_WHITESPACE + + >>> f = gf_from_dict({11: ZZ(1), 0: ZZ(1)}, 11, ZZ) + + Note that ``f'(x) = 0``:: + + >>> gf_diff(f, 11, ZZ) + [] + + This phenomenon does not happen in characteristic zero. However we can + still compute square-free decomposition of ``f`` using ``gf_sqf()``:: + + >>> gf_sqf_list(f, 11, ZZ) + (1, [([1, 1], 11)]) + + We obtained factorization ``f = (x + 1)**11``. This is correct because:: + + >>> gf_pow([1, 1], 11, 11, ZZ) == f + True + + References + ========== + + .. [1] [Geddes92]_ + + """ + n, sqf, factors, r = 1, False, [], int(p) + + lc, f = gf_monic(f, p, K) + + if gf_degree(f) < 1: + return lc, [] + + while True: + F = gf_diff(f, p, K) + + if F != []: + g = gf_gcd(f, F, p, K) + h = gf_quo(f, g, p, K) + + i = 1 + + while h != [K.one]: + G = gf_gcd(g, h, p, K) + H = gf_quo(h, G, p, K) + + if gf_degree(H) > 0: + factors.append((H, i*n)) + + g, h, i = gf_quo(g, G, p, K), G, i + 1 + + if g == [K.one]: + sqf = True + else: + f = g + + if not sqf: + d = gf_degree(f) // r + + for i in range(0, d + 1): + f[i] = f[i*r] + + f, n = f[:d + 1], n*r + else: + break + + if all: + raise ValueError("'all=True' is not supported yet") + + return lc, factors + + +def gf_Qmatrix(f, p, K): + """ + Calculate Berlekamp's ``Q`` matrix. + + Examples + ======== + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_Qmatrix + + >>> gf_Qmatrix([3, 2, 4], 5, ZZ) + [[1, 0], + [3, 4]] + + >>> gf_Qmatrix([1, 0, 0, 0, 1], 5, ZZ) + [[1, 0, 0, 0], + [0, 4, 0, 0], + [0, 0, 1, 0], + [0, 0, 0, 4]] + + """ + n, r = gf_degree(f), int(p) + + q = [K.one] + [K.zero]*(n - 1) + Q = [list(q)] + [[]]*(n - 1) + + for i in range(1, (n - 1)*r + 1): + qq, c = [(-q[-1]*f[-1]) % p], q[-1] + + for j in range(1, n): + qq.append((q[j - 1] - c*f[-j - 1]) % p) + + if not (i % r): + Q[i//r] = list(qq) + + q = qq + + return Q + + +def gf_Qbasis(Q, p, K): + """ + Compute a basis of the kernel of ``Q``. + + Examples + ======== + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_Qmatrix, gf_Qbasis + + >>> gf_Qbasis(gf_Qmatrix([1, 0, 0, 0, 1], 5, ZZ), 5, ZZ) + [[1, 0, 0, 0], [0, 0, 1, 0]] + + >>> gf_Qbasis(gf_Qmatrix([3, 2, 4], 5, ZZ), 5, ZZ) + [[1, 0]] + + """ + Q, n = [ list(q) for q in Q ], len(Q) + + for k in range(0, n): + Q[k][k] = (Q[k][k] - K.one) % p + + for k in range(0, n): + for i in range(k, n): + if Q[k][i]: + break + else: + continue + + inv = K.invert(Q[k][i], p) + + for j in range(0, n): + Q[j][i] = (Q[j][i]*inv) % p + + for j in range(0, n): + t = Q[j][k] + Q[j][k] = Q[j][i] + Q[j][i] = t + + for i in range(0, n): + if i != k: + q = Q[k][i] + + for j in range(0, n): + Q[j][i] = (Q[j][i] - Q[j][k]*q) % p + + for i in range(0, n): + for j in range(0, n): + if i == j: + Q[i][j] = (K.one - Q[i][j]) % p + else: + Q[i][j] = (-Q[i][j]) % p + + basis = [] + + for q in Q: + if any(q): + basis.append(q) + + return basis + + +def gf_berlekamp(f, p, K): + """ + Factor a square-free ``f`` in ``GF(p)[x]`` for small ``p``. + + Examples + ======== + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_berlekamp + + >>> gf_berlekamp([1, 0, 0, 0, 1], 5, ZZ) + [[1, 0, 2], [1, 0, 3]] + + """ + Q = gf_Qmatrix(f, p, K) + V = gf_Qbasis(Q, p, K) + + for i, v in enumerate(V): + V[i] = gf_strip(list(reversed(v))) + + factors = [f] + + for k in range(1, len(V)): + for f in list(factors): + s = K.zero + + while s < p: + g = gf_sub_ground(V[k], s, p, K) + h = gf_gcd(f, g, p, K) + + if h != [K.one] and h != f: + factors.remove(f) + + f = gf_quo(f, h, p, K) + factors.extend([f, h]) + + if len(factors) == len(V): + return _sort_factors(factors, multiple=False) + + s += K.one + + return _sort_factors(factors, multiple=False) + + +def gf_ddf_zassenhaus(f, p, K): + """ + Cantor-Zassenhaus: Deterministic Distinct Degree Factorization + + Given a monic square-free polynomial ``f`` in ``GF(p)[x]``, computes + partial distinct degree factorization ``f_1 ... f_d`` of ``f`` where + ``deg(f_i) != deg(f_j)`` for ``i != j``. The result is returned as a + list of pairs ``(f_i, e_i)`` where ``deg(f_i) > 0`` and ``e_i > 0`` + is an argument to the equal degree factorization routine. + + Consider the polynomial ``x**15 - 1`` in ``GF(11)[x]``:: + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_from_dict + + >>> f = gf_from_dict({15: ZZ(1), 0: ZZ(-1)}, 11, ZZ) + + Distinct degree factorization gives:: + + >>> from sympy.polys.galoistools import gf_ddf_zassenhaus + + >>> gf_ddf_zassenhaus(f, 11, ZZ) + [([1, 0, 0, 0, 0, 10], 1), ([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2)] + + which means ``x**15 - 1 = (x**5 - 1) (x**10 + x**5 + 1)``. To obtain + factorization into irreducibles, use equal degree factorization + procedure (EDF) with each of the factors. + + References + ========== + + .. [1] [Gathen99]_ + .. [2] [Geddes92]_ + + """ + i, g, factors = 1, [K.one, K.zero], [] + + b = gf_frobenius_monomial_base(f, p, K) + while 2*i <= gf_degree(f): + g = gf_frobenius_map(g, f, b, p, K) + h = gf_gcd(f, gf_sub(g, [K.one, K.zero], p, K), p, K) + + if h != [K.one]: + factors.append((h, i)) + + f = gf_quo(f, h, p, K) + g = gf_rem(g, f, p, K) + b = gf_frobenius_monomial_base(f, p, K) + + i += 1 + + if f != [K.one]: + return factors + [(f, gf_degree(f))] + else: + return factors + + +def gf_edf_zassenhaus(f, n, p, K): + """ + Cantor-Zassenhaus: Probabilistic Equal Degree Factorization + + Given a monic square-free polynomial ``f`` in ``GF(p)[x]`` and + an integer ``n``, such that ``n`` divides ``deg(f)``, returns all + irreducible factors ``f_1,...,f_d`` of ``f``, each of degree ``n``. + EDF procedure gives complete factorization over Galois fields. + + Consider the square-free polynomial ``f = x**3 + x**2 + x + 1`` in + ``GF(5)[x]``. Let's compute its irreducible factors of degree one:: + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_edf_zassenhaus + + >>> gf_edf_zassenhaus([1,1,1,1], 1, 5, ZZ) + [[1, 1], [1, 2], [1, 3]] + + Notes + ===== + + The case p == 2 is handled by Cohen's Algorithm 3.4.8. The case p odd is + as in Geddes Algorithm 8.9 (or Cohen's Algorithm 3.4.6). + + References + ========== + + .. [1] [Gathen99]_ + .. [2] [Geddes92]_ Algorithm 8.9 + .. [3] [Cohen93]_ Algorithm 3.4.8 + + """ + factors = [f] + + if gf_degree(f) <= n: + return factors + + N = gf_degree(f) // n + if p != 2: + b = gf_frobenius_monomial_base(f, p, K) + + t = [K.one, K.zero] + while len(factors) < N: + if p == 2: + h = r = t + + for i in range(n - 1): + r = gf_pow_mod(r, 2, f, p, K) + h = gf_add(h, r, p, K) + + g = gf_gcd(f, h, p, K) + t += [K.zero, K.zero] + else: + r = gf_random(2 * n - 1, p, K) + h = _gf_pow_pnm1d2(r, n, f, b, p, K) + g = gf_gcd(f, gf_sub_ground(h, K.one, p, K), p, K) + + if g != [K.one] and g != f: + factors = gf_edf_zassenhaus(g, n, p, K) \ + + gf_edf_zassenhaus(gf_quo(f, g, p, K), n, p, K) + + return _sort_factors(factors, multiple=False) + + +def gf_ddf_shoup(f, p, K): + """ + Kaltofen-Shoup: Deterministic Distinct Degree Factorization + + Given a monic square-free polynomial ``f`` in ``GF(p)[x]``, computes + partial distinct degree factorization ``f_1,...,f_d`` of ``f`` where + ``deg(f_i) != deg(f_j)`` for ``i != j``. The result is returned as a + list of pairs ``(f_i, e_i)`` where ``deg(f_i) > 0`` and ``e_i > 0`` + is an argument to the equal degree factorization routine. + + This algorithm is an improved version of Zassenhaus algorithm for + large ``deg(f)`` and modulus ``p`` (especially for ``deg(f) ~ lg(p)``). + + Examples + ======== + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_ddf_shoup, gf_from_dict + + >>> f = gf_from_dict({6: ZZ(1), 5: ZZ(-1), 4: ZZ(1), 3: ZZ(1), 1: ZZ(-1)}, 3, ZZ) + + >>> gf_ddf_shoup(f, 3, ZZ) + [([1, 1, 0], 1), ([1, 1, 0, 1, 2], 2)] + + References + ========== + + .. [1] [Kaltofen98]_ + .. [2] [Shoup95]_ + .. [3] [Gathen92]_ + + """ + n = gf_degree(f) + k = int(_ceil(_sqrt(n//2))) + b = gf_frobenius_monomial_base(f, p, K) + h = gf_frobenius_map([K.one, K.zero], f, b, p, K) + # U[i] = x**(p**i) + U = [[K.one, K.zero], h] + [K.zero]*(k - 1) + + for i in range(2, k + 1): + U[i] = gf_frobenius_map(U[i-1], f, b, p, K) + + h, U = U[k], U[:k] + # V[i] = x**(p**(k*(i+1))) + V = [h] + [K.zero]*(k - 1) + + for i in range(1, k): + V[i] = gf_compose_mod(V[i - 1], h, f, p, K) + + factors = [] + + for i, v in enumerate(V): + h, j = [K.one], k - 1 + + for u in U: + g = gf_sub(v, u, p, K) + h = gf_mul(h, g, p, K) + h = gf_rem(h, f, p, K) + + g = gf_gcd(f, h, p, K) + f = gf_quo(f, g, p, K) + + for u in reversed(U): + h = gf_sub(v, u, p, K) + F = gf_gcd(g, h, p, K) + + if F != [K.one]: + factors.append((F, k*(i + 1) - j)) + + g, j = gf_quo(g, F, p, K), j - 1 + + if f != [K.one]: + factors.append((f, gf_degree(f))) + + return factors + +def gf_edf_shoup(f, n, p, K): + """ + Gathen-Shoup: Probabilistic Equal Degree Factorization + + Given a monic square-free polynomial ``f`` in ``GF(p)[x]`` and integer + ``n`` such that ``n`` divides ``deg(f)``, returns all irreducible factors + ``f_1,...,f_d`` of ``f``, each of degree ``n``. This is a complete + factorization over Galois fields. + + This algorithm is an improved version of Zassenhaus algorithm for + large ``deg(f)`` and modulus ``p`` (especially for ``deg(f) ~ lg(p)``). + + Examples + ======== + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_edf_shoup + + >>> gf_edf_shoup(ZZ.map([1, 2837, 2277]), 1, 2917, ZZ) + [[1, 852], [1, 1985]] + + References + ========== + + .. [1] [Shoup91]_ + .. [2] [Gathen92]_ + + """ + N, q = gf_degree(f), int(p) + + if not N: + return [] + if N <= n: + return [f] + + factors, x = [f], [K.one, K.zero] + + r = gf_random(N - 1, p, K) + + if p == 2: + h = gf_pow_mod(x, q, f, p, K) + H = gf_trace_map(r, h, x, n - 1, f, p, K)[1] + h1 = gf_gcd(f, H, p, K) + h2 = gf_quo(f, h1, p, K) + + factors = gf_edf_shoup(h1, n, p, K) \ + + gf_edf_shoup(h2, n, p, K) + else: + b = gf_frobenius_monomial_base(f, p, K) + H = _gf_trace_map(r, n, f, b, p, K) + h = gf_pow_mod(H, (q - 1)//2, f, p, K) + + h1 = gf_gcd(f, h, p, K) + h2 = gf_gcd(f, gf_sub_ground(h, K.one, p, K), p, K) + h3 = gf_quo(f, gf_mul(h1, h2, p, K), p, K) + + factors = gf_edf_shoup(h1, n, p, K) \ + + gf_edf_shoup(h2, n, p, K) \ + + gf_edf_shoup(h3, n, p, K) + + return _sort_factors(factors, multiple=False) + + +def gf_zassenhaus(f, p, K): + """ + Factor a square-free ``f`` in ``GF(p)[x]`` for medium ``p``. + + Examples + ======== + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_zassenhaus + + >>> gf_zassenhaus(ZZ.map([1, 4, 3]), 5, ZZ) + [[1, 1], [1, 3]] + + """ + factors = [] + + for factor, n in gf_ddf_zassenhaus(f, p, K): + factors += gf_edf_zassenhaus(factor, n, p, K) + + return _sort_factors(factors, multiple=False) + + +def gf_shoup(f, p, K): + """ + Factor a square-free ``f`` in ``GF(p)[x]`` for large ``p``. + + Examples + ======== + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_shoup + + >>> gf_shoup(ZZ.map([1, 4, 3]), 5, ZZ) + [[1, 1], [1, 3]] + + """ + factors = [] + + for factor, n in gf_ddf_shoup(f, p, K): + factors += gf_edf_shoup(factor, n, p, K) + + return _sort_factors(factors, multiple=False) + +_factor_methods = { + 'berlekamp': gf_berlekamp, # ``p`` : small + 'zassenhaus': gf_zassenhaus, # ``p`` : medium + 'shoup': gf_shoup, # ``p`` : large +} + + +def gf_factor_sqf(f, p, K, method=None): + """ + Factor a square-free polynomial ``f`` in ``GF(p)[x]``. + + Examples + ======== + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_factor_sqf + + >>> gf_factor_sqf(ZZ.map([3, 2, 4]), 5, ZZ) + (3, [[1, 1], [1, 3]]) + + """ + lc, f = gf_monic(f, p, K) + + if gf_degree(f) < 1: + return lc, [] + + method = method or query('GF_FACTOR_METHOD') + + if method is not None: + factors = _factor_methods[method](f, p, K) + else: + factors = gf_zassenhaus(f, p, K) + + return lc, factors + + +def gf_factor(f, p, K): + """ + Factor (non square-free) polynomials in ``GF(p)[x]``. + + Given a possibly non square-free polynomial ``f`` in ``GF(p)[x]``, + returns its complete factorization into irreducibles:: + + f_1(x)**e_1 f_2(x)**e_2 ... f_d(x)**e_d + + where each ``f_i`` is a monic polynomial and ``gcd(f_i, f_j) == 1``, + for ``i != j``. The result is given as a tuple consisting of the + leading coefficient of ``f`` and a list of factors of ``f`` with + their multiplicities. + + The algorithm proceeds by first computing square-free decomposition + of ``f`` and then iteratively factoring each of square-free factors. + + Consider a non square-free polynomial ``f = (7*x + 1) (x + 2)**2`` in + ``GF(11)[x]``. We obtain its factorization into irreducibles as follows:: + + >>> from sympy.polys.domains import ZZ + >>> from sympy.polys.galoistools import gf_factor + + >>> gf_factor(ZZ.map([5, 2, 7, 2]), 11, ZZ) + (5, [([1, 2], 1), ([1, 8], 2)]) + + We arrived with factorization ``f = 5 (x + 2) (x + 8)**2``. We did not + recover the exact form of the input polynomial because we requested to + get monic factors of ``f`` and its leading coefficient separately. + + Square-free factors of ``f`` can be factored into irreducibles over + ``GF(p)`` using three very different methods: + + Berlekamp + efficient for very small values of ``p`` (usually ``p < 25``) + Cantor-Zassenhaus + efficient on average input and with "typical" ``p`` + Shoup-Kaltofen-Gathen + efficient with very large inputs and modulus + + If you want to use a specific factorization method, instead of the default + one, set ``GF_FACTOR_METHOD`` with one of ``berlekamp``, ``zassenhaus`` or + ``shoup`` values. + + References + ========== + + .. [1] [Gathen99]_ + + """ + lc, f = gf_monic(f, p, K) + + if gf_degree(f) < 1: + return lc, [] + + factors = [] + + for g, n in gf_sqf_list(f, p, K)[1]: + for h in gf_factor_sqf(g, p, K)[1]: + factors.append((h, n)) + + return lc, _sort_factors(factors) + + +def gf_value(f, a): + """ + Value of polynomial 'f' at 'a' in field R. + + Examples + ======== + + >>> from sympy.polys.galoistools import gf_value + + >>> gf_value([1, 7, 2, 4], 11) + 2204 + + """ + result = 0 + for c in f: + result *= a + result += c + return result + + +def linear_congruence(a, b, m): + """ + Returns the values of x satisfying a*x congruent b mod(m) + + Here m is positive integer and a, b are natural numbers. + This function returns only those values of x which are distinct mod(m). + + Examples + ======== + + >>> from sympy.polys.galoistools import linear_congruence + + >>> linear_congruence(3, 12, 15) + [4, 9, 14] + + There are 3 solutions distinct mod(15) since gcd(a, m) = gcd(3, 15) = 3. + + References + ========== + + .. [1] https://en.wikipedia.org/wiki/Linear_congruence_theorem + + """ + from sympy.polys.polytools import gcdex + if a % m == 0: + if b % m == 0: + return list(range(m)) + else: + return [] + r, _, g = gcdex(a, m) + if b % g != 0: + return [] + return [(r * b // g + t * m // g) % m for t in range(g)] + + +def _raise_mod_power(x, s, p, f): + """ + Used in gf_csolve to generate solutions of f(x) cong 0 mod(p**(s + 1)) + from the solutions of f(x) cong 0 mod(p**s). + + Examples + ======== + + >>> from sympy.polys.galoistools import _raise_mod_power + >>> from sympy.polys.galoistools import csolve_prime + + These is the solutions of f(x) = x**2 + x + 7 cong 0 mod(3) + + >>> f = [1, 1, 7] + >>> csolve_prime(f, 3) + [1] + >>> [ i for i in range(3) if not (i**2 + i + 7) % 3] + [1] + + The solutions of f(x) cong 0 mod(9) are constructed from the + values returned from _raise_mod_power: + + >>> x, s, p = 1, 1, 3 + >>> V = _raise_mod_power(x, s, p, f) + >>> [x + v * p**s for v in V] + [1, 4, 7] + + And these are confirmed with the following: + + >>> [ i for i in range(3**2) if not (i**2 + i + 7) % 3**2] + [1, 4, 7] + + """ + from sympy.polys.domains import ZZ + f_f = gf_diff(f, p, ZZ) + alpha = gf_value(f_f, x) + beta = - gf_value(f, x) // p**s + return linear_congruence(alpha, beta, p) + + +def _csolve_prime_las_vegas(f, p, seed=None): + r""" Solutions of `f(x) \equiv 0 \pmod{p}`, `f(0) \not\equiv 0 \pmod{p}`. + + Explanation + =========== + + This algorithm is classified as the Las Vegas method. + That is, it always returns the correct answer and solves the problem + fast in many cases, but if it is unlucky, it does not answer forever. + + Suppose the polynomial f is not a zero polynomial. Assume further + that it is of degree at most p-1 and `f(0)\not\equiv 0 \pmod{p}`. + These assumptions are not an essential part of the algorithm, + only that it is more convenient for the function calling this + function to resolve them. + + Note that `x^{p-1} - 1 \equiv \prod_{a=1}^{p-1}(x - a) \pmod{p}`. + Thus, the greatest common divisor with f is `\prod_{s \in S}(x - s)`, + with S being the set of solutions to f. Furthermore, + when a is randomly determined, `(x+a)^{(p-1)/2}-1` is + a polynomial with (p-1)/2 randomly chosen solutions. + The greatest common divisor of f may be a nontrivial factor of f. + + When p is large and the degree of f is small, + it is faster than naive solution methods. + + Parameters + ========== + + f : polynomial + p : prime number + + Returns + ======= + + list[int] + a list of solutions, sorted in ascending order + by integers in the range [1, p). The same value + does not exist in the list even if there is + a multiple solution. If no solution exists, returns []. + + Examples + ======== + + >>> from sympy.polys.galoistools import _csolve_prime_las_vegas + >>> _csolve_prime_las_vegas([1, 4, 3], 7) # x^2 + 4x + 3 = 0 (mod 7) + [4, 6] + >>> _csolve_prime_las_vegas([5, 7, 1, 9], 11) # 5x^3 + 7x^2 + x + 9 = 0 (mod 11) + [1, 5, 8] + + References + ========== + + .. [1] R. Crandall and C. Pomerance "Prime Numbers", 2nd Ed., Algorithm 2.3.10 + + """ + from sympy.polys.domains import ZZ + from sympy.ntheory import sqrt_mod + randint = _randint(seed) + root = set() + g = gf_pow_mod([1, 0], p - 1, f, p, ZZ) + g = gf_sub_ground(g, 1, p, ZZ) + # We want to calculate gcd(x**(p-1) - 1, f(x)) + factors = [gf_gcd(f, g, p, ZZ)] + while factors: + f = factors.pop() + # If the degree is small, solve directly + if len(f) <= 1: + continue + if len(f) == 2: + root.add(-invert(f[0], p) * f[1] % p) + continue + if len(f) == 3: + inv = invert(f[0], p) + b = f[1] * inv % p + b = (b + p * (b % 2)) // 2 + root.update((r - b) % p for r in + sqrt_mod(b**2 - f[2] * inv, p, all_roots=True)) + continue + while True: + # Determine `a` randomly and + # compute gcd((x+a)**((p-1)//2)-1, f(x)) + a = randint(0, p - 1) + g = gf_pow_mod([1, a], (p - 1) // 2, f, p, ZZ) + g = gf_sub_ground(g, 1, p, ZZ) + g = gf_gcd(f, g, p, ZZ) + if 1 < len(g) < len(f): + factors.append(g) + factors.append(gf_div(f, g, p, ZZ)[0]) + break + return sorted(root) + + +def csolve_prime(f, p, e=1): + r""" Solutions of `f(x) \equiv 0 \pmod{p^e}`. + + Parameters + ========== + + f : polynomial + p : prime number + e : positive integer + + Returns + ======= + + list[int] + a list of solutions, sorted in ascending order + by integers in the range [1, p**e). The same value + does not exist in the list even if there is + a multiple solution. If no solution exists, returns []. + + Examples + ======== + + >>> from sympy.polys.galoistools import csolve_prime + >>> csolve_prime([1, 1, 7], 3, 1) + [1] + >>> csolve_prime([1, 1, 7], 3, 2) + [1, 4, 7] + + Solutions [7, 4, 1] (mod 3**2) are generated by ``_raise_mod_power()`` + from solution [1] (mod 3). + """ + from sympy.polys.domains import ZZ + g = [MPZ(int(c)) for c in f] + # Convert to polynomial of degree at most p-1 + for i in range(len(g) - p): + g[i + p - 1] += g[i] + g[i] = 0 + g = gf_trunc(g, p) + # Checks whether g(x) is divisible by x + k = 0 + while k < len(g) and g[len(g) - k - 1] == 0: + k += 1 + if k: + g = g[:-k] + root_zero = [0] + else: + root_zero = [] + if g == []: + X1 = list(range(p)) + elif len(g)**2 < p: + # The conditions under which `_csolve_prime_las_vegas` is faster than + # a naive solution are worth considering. + X1 = root_zero + _csolve_prime_las_vegas(g, p) + else: + X1 = root_zero + [i for i in range(p) if gf_eval(g, i, p, ZZ) == 0] + if e == 1: + return X1 + X = [] + S = list(zip(X1, [1]*len(X1))) + while S: + x, s = S.pop() + if s == e: + X.append(x) + else: + s1 = s + 1 + ps = p**s + S.extend([(x + v*ps, s1) for v in _raise_mod_power(x, s, p, f)]) + return sorted(X) + + +def gf_csolve(f, n): + """ + To solve f(x) congruent 0 mod(n). + + n is divided into canonical factors and f(x) cong 0 mod(p**e) will be + solved for each factor. Applying the Chinese Remainder Theorem to the + results returns the final answers. + + Examples + ======== + + Solve [1, 1, 7] congruent 0 mod(189): + + >>> from sympy.polys.galoistools import gf_csolve + >>> gf_csolve([1, 1, 7], 189) + [13, 49, 76, 112, 139, 175] + + See Also + ======== + + sympy.ntheory.residue_ntheory.polynomial_congruence : a higher level solving routine + + References + ========== + + .. [1] 'An introduction to the Theory of Numbers' 5th Edition by Ivan Niven, + Zuckerman and Montgomery. + + """ + from sympy.polys.domains import ZZ + from sympy.ntheory import factorint + P = factorint(n) + X = [csolve_prime(f, p, e) for p, e in P.items()] + pools = list(map(tuple, X)) + perms = [[]] + for pool in pools: + perms = [x + [y] for x in perms for y in pool] + dist_factors = [pow(p, e) for p, e in P.items()] + return sorted([gf_crt(per, dist_factors, ZZ) for per in perms]) diff --git a/lib/python3.12/site-packages/sympy/polys/orderings.py b/lib/python3.12/site-packages/sympy/polys/orderings.py new file mode 100644 index 0000000000000000000000000000000000000000..b6ed575d5103440e1e8ebda4c53c4149d3badf11 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/polys/orderings.py @@ -0,0 +1,286 @@ +"""Definitions of monomial orderings. """ + +from __future__ import annotations + +__all__ = ["lex", "grlex", "grevlex", "ilex", "igrlex", "igrevlex"] + +from sympy.core import Symbol +from sympy.utilities.iterables import iterable + +class MonomialOrder: + """Base class for monomial orderings. """ + + alias: str | None = None + is_global: bool | None = None + is_default = False + + def __repr__(self): + return self.__class__.__name__ + "()" + + def __str__(self): + return self.alias + + def __call__(self, monomial): + raise NotImplementedError + + def __eq__(self, other): + return self.__class__ == other.__class__ + + def __hash__(self): + return hash(self.__class__) + + def __ne__(self, other): + return not (self == other) + +class LexOrder(MonomialOrder): + """Lexicographic order of monomials. """ + + alias = 'lex' + is_global = True + is_default = True + + def __call__(self, monomial): + return monomial + +class GradedLexOrder(MonomialOrder): + """Graded lexicographic order of monomials. """ + + alias = 'grlex' + is_global = True + + def __call__(self, monomial): + return (sum(monomial), monomial) + +class ReversedGradedLexOrder(MonomialOrder): + """Reversed graded lexicographic order of monomials. """ + + alias = 'grevlex' + is_global = True + + def __call__(self, monomial): + return (sum(monomial), tuple(reversed([-m for m in monomial]))) + +class ProductOrder(MonomialOrder): + """ + A product order built from other monomial orders. + + Given (not necessarily total) orders O1, O2, ..., On, their product order + P is defined as M1 > M2 iff there exists i such that O1(M1) = O2(M2), + ..., Oi(M1) = Oi(M2), O{i+1}(M1) > O{i+1}(M2). + + Product orders are typically built from monomial orders on different sets + of variables. + + ProductOrder is constructed by passing a list of pairs + [(O1, L1), (O2, L2), ...] where Oi are MonomialOrders and Li are callables. + Upon comparison, the Li are passed the total monomial, and should filter + out the part of the monomial to pass to Oi. + + Examples + ======== + + We can use a lexicographic order on x_1, x_2 and also on + y_1, y_2, y_3, and their product on {x_i, y_i} as follows: + + >>> from sympy.polys.orderings import lex, grlex, ProductOrder + >>> P = ProductOrder( + ... (lex, lambda m: m[:2]), # lex order on x_1 and x_2 of monomial + ... (grlex, lambda m: m[2:]) # grlex on y_1, y_2, y_3 + ... ) + >>> P((2, 1, 1, 0, 0)) > P((1, 10, 0, 2, 0)) + True + + Here the exponent `2` of `x_1` in the first monomial + (`x_1^2 x_2 y_1`) is bigger than the exponent `1` of `x_1` in the + second monomial (`x_1 x_2^10 y_2^2`), so the first monomial is greater + in the product ordering. + + >>> P((2, 1, 1, 0, 0)) < P((2, 1, 0, 2, 0)) + True + + Here the exponents of `x_1` and `x_2` agree, so the grlex order on + `y_1, y_2, y_3` is used to decide the ordering. In this case the monomial + `y_2^2` is ordered larger than `y_1`, since for the grlex order the degree + of the monomial is most important. + """ + + def __init__(self, *args): + self.args = args + + def __call__(self, monomial): + return tuple(O(lamda(monomial)) for (O, lamda) in self.args) + + def __repr__(self): + contents = [repr(x[0]) for x in self.args] + return self.__class__.__name__ + '(' + ", ".join(contents) + ')' + + def __str__(self): + contents = [str(x[0]) for x in self.args] + return self.__class__.__name__ + '(' + ", ".join(contents) + ')' + + def __eq__(self, other): + if not isinstance(other, ProductOrder): + return False + return self.args == other.args + + def __hash__(self): + return hash((self.__class__, self.args)) + + @property + def is_global(self): + if all(o.is_global is True for o, _ in self.args): + return True + if all(o.is_global is False for o, _ in self.args): + return False + return None + +class InverseOrder(MonomialOrder): + """ + The "inverse" of another monomial order. + + If O is any monomial order, we can construct another monomial order iO + such that `A >_{iO} B` if and only if `B >_O A`. This is useful for + constructing local orders. + + Note that many algorithms only work with *global* orders. + + For example, in the inverse lexicographic order on a single variable `x`, + high powers of `x` count as small: + + >>> from sympy.polys.orderings import lex, InverseOrder + >>> ilex = InverseOrder(lex) + >>> ilex((5,)) < ilex((0,)) + True + """ + + def __init__(self, O): + self.O = O + + def __str__(self): + return "i" + str(self.O) + + def __call__(self, monomial): + def inv(l): + if iterable(l): + return tuple(inv(x) for x in l) + return -l + return inv(self.O(monomial)) + + @property + def is_global(self): + if self.O.is_global is True: + return False + if self.O.is_global is False: + return True + return None + + def __eq__(self, other): + return isinstance(other, InverseOrder) and other.O == self.O + + def __hash__(self): + return hash((self.__class__, self.O)) + +lex = LexOrder() +grlex = GradedLexOrder() +grevlex = ReversedGradedLexOrder() +ilex = InverseOrder(lex) +igrlex = InverseOrder(grlex) +igrevlex = InverseOrder(grevlex) + +_monomial_key = { + 'lex': lex, + 'grlex': grlex, + 'grevlex': grevlex, + 'ilex': ilex, + 'igrlex': igrlex, + 'igrevlex': igrevlex +} + +def monomial_key(order=None, gens=None): + """ + Return a function defining admissible order on monomials. + + The result of a call to :func:`monomial_key` is a function which should + be used as a key to :func:`sorted` built-in function, to provide order + in a set of monomials of the same length. + + Currently supported monomial orderings are: + + 1. lex - lexicographic order (default) + 2. grlex - graded lexicographic order + 3. grevlex - reversed graded lexicographic order + 4. ilex, igrlex, igrevlex - the corresponding inverse orders + + If the ``order`` input argument is not a string but has ``__call__`` + attribute, then it will pass through with an assumption that the + callable object defines an admissible order on monomials. + + If the ``gens`` input argument contains a list of generators, the + resulting key function can be used to sort SymPy ``Expr`` objects. + + """ + if order is None: + order = lex + + if isinstance(order, Symbol): + order = str(order) + + if isinstance(order, str): + try: + order = _monomial_key[order] + except KeyError: + raise ValueError("supported monomial orderings are 'lex', 'grlex' and 'grevlex', got %r" % order) + if hasattr(order, '__call__'): + if gens is not None: + def _order(expr): + return order(expr.as_poly(*gens).degree_list()) + return _order + return order + else: + raise ValueError("monomial ordering specification must be a string or a callable, got %s" % order) + +class _ItemGetter: + """Helper class to return a subsequence of values.""" + + def __init__(self, seq): + self.seq = tuple(seq) + + def __call__(self, m): + return tuple(m[idx] for idx in self.seq) + + def __eq__(self, other): + if not isinstance(other, _ItemGetter): + return False + return self.seq == other.seq + +def build_product_order(arg, gens): + """ + Build a monomial order on ``gens``. + + ``arg`` should be a tuple of iterables. The first element of each iterable + should be a string or monomial order (will be passed to monomial_key), + the others should be subsets of the generators. This function will build + the corresponding product order. + + For example, build a product of two grlex orders: + + >>> from sympy.polys.orderings import build_product_order + >>> from sympy.abc import x, y, z, t + + >>> O = build_product_order((("grlex", x, y), ("grlex", z, t)), [x, y, z, t]) + >>> O((1, 2, 3, 4)) + ((3, (1, 2)), (7, (3, 4))) + + """ + gens2idx = {} + for i, g in enumerate(gens): + gens2idx[g] = i + order = [] + for expr in arg: + name = expr[0] + var = expr[1:] + + def makelambda(var): + return _ItemGetter(gens2idx[g] for g in var) + order.append((monomial_key(name), makelambda(var))) + return ProductOrder(*order) diff --git a/lib/python3.12/site-packages/sympy/polys/polyerrors.py b/lib/python3.12/site-packages/sympy/polys/polyerrors.py new file mode 100644 index 0000000000000000000000000000000000000000..79385ffaf6746386f8f108c3e02992dcaf4a4f55 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/polys/polyerrors.py @@ -0,0 +1,183 @@ +"""Definitions of common exceptions for `polys` module. """ + + +from sympy.utilities import public + +@public +class BasePolynomialError(Exception): + """Base class for polynomial related exceptions. """ + + def new(self, *args): + raise NotImplementedError("abstract base class") + +@public +class ExactQuotientFailed(BasePolynomialError): + + def __init__(self, f, g, dom=None): + self.f, self.g, self.dom = f, g, dom + + def __str__(self): # pragma: no cover + from sympy.printing.str import sstr + + if self.dom is None: + return "%s does not divide %s" % (sstr(self.g), sstr(self.f)) + else: + return "%s does not divide %s in %s" % (sstr(self.g), sstr(self.f), sstr(self.dom)) + + def new(self, f, g): + return self.__class__(f, g, self.dom) + +@public +class PolynomialDivisionFailed(BasePolynomialError): + + def __init__(self, f, g, domain): + self.f = f + self.g = g + self.domain = domain + + def __str__(self): + if self.domain.is_EX: + msg = "You may want to use a different simplification algorithm. Note " \ + "that in general it's not possible to guarantee to detect zero " \ + "in this domain." + elif not self.domain.is_Exact: + msg = "Your working precision or tolerance of computations may be set " \ + "improperly. Adjust those parameters of the coefficient domain " \ + "and try again." + else: + msg = "Zero detection is guaranteed in this coefficient domain. This " \ + "may indicate a bug in SymPy or the domain is user defined and " \ + "doesn't implement zero detection properly." + + return "couldn't reduce degree in a polynomial division algorithm when " \ + "dividing %s by %s. This can happen when it's not possible to " \ + "detect zero in the coefficient domain. The domain of computation " \ + "is %s. %s" % (self.f, self.g, self.domain, msg) + +@public +class OperationNotSupported(BasePolynomialError): + + def __init__(self, poly, func): + self.poly = poly + self.func = func + + def __str__(self): # pragma: no cover + return "`%s` operation not supported by %s representation" % (self.func, self.poly.rep.__class__.__name__) + +@public +class HeuristicGCDFailed(BasePolynomialError): + pass + +class ModularGCDFailed(BasePolynomialError): + pass + +@public +class HomomorphismFailed(BasePolynomialError): + pass + +@public +class IsomorphismFailed(BasePolynomialError): + pass + +@public +class ExtraneousFactors(BasePolynomialError): + pass + +@public +class EvaluationFailed(BasePolynomialError): + pass + +@public +class RefinementFailed(BasePolynomialError): + pass + +@public +class CoercionFailed(BasePolynomialError): + pass + +@public +class NotInvertible(BasePolynomialError): + pass + +@public +class NotReversible(BasePolynomialError): + pass + +@public +class NotAlgebraic(BasePolynomialError): + pass + +@public +class DomainError(BasePolynomialError): + pass + +@public +class PolynomialError(BasePolynomialError): + pass + +@public +class UnificationFailed(BasePolynomialError): + pass + +@public +class UnsolvableFactorError(BasePolynomialError): + """Raised if ``roots`` is called with strict=True and a polynomial + having a factor whose solutions are not expressible in radicals + is encountered.""" + +@public +class GeneratorsError(BasePolynomialError): + pass + +@public +class GeneratorsNeeded(GeneratorsError): + pass + +@public +class ComputationFailed(BasePolynomialError): + + def __init__(self, func, nargs, exc): + self.func = func + self.nargs = nargs + self.exc = exc + + def __str__(self): + return "%s(%s) failed without generators" % (self.func, ', '.join(map(str, self.exc.exprs[:self.nargs]))) + +@public +class UnivariatePolynomialError(PolynomialError): + pass + +@public +class MultivariatePolynomialError(PolynomialError): + pass + +@public +class PolificationFailed(PolynomialError): + + def __init__(self, opt, origs, exprs, seq=False): + if not seq: + self.orig = origs + self.expr = exprs + self.origs = [origs] + self.exprs = [exprs] + else: + self.origs = origs + self.exprs = exprs + + self.opt = opt + self.seq = seq + + def __str__(self): # pragma: no cover + if not self.seq: + return "Cannot construct a polynomial from %s" % str(self.orig) + else: + return "Cannot construct polynomials from %s" % ', '.join(map(str, self.origs)) + +@public +class OptionError(BasePolynomialError): + pass + +@public +class FlagError(OptionError): + pass diff --git a/lib/python3.12/site-packages/sympy/polys/polyoptions.py b/lib/python3.12/site-packages/sympy/polys/polyoptions.py new file mode 100644 index 0000000000000000000000000000000000000000..739437ff50ba3586479b692bd1123b602dedd8b7 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/polys/polyoptions.py @@ -0,0 +1,786 @@ +"""Options manager for :class:`~.Poly` and public API functions. """ + +from __future__ import annotations + +__all__ = ["Options"] + +from sympy.core import Basic, sympify +from sympy.polys.polyerrors import GeneratorsError, OptionError, FlagError +from sympy.utilities import numbered_symbols, topological_sort, public +from sympy.utilities.iterables import has_dups, is_sequence + +import sympy.polys + +import re + +class Option: + """Base class for all kinds of options. """ + + option: str | None = None + + is_Flag = False + + requires: list[str] = [] + excludes: list[str] = [] + + after: list[str] = [] + before: list[str] = [] + + @classmethod + def default(cls): + return None + + @classmethod + def preprocess(cls, option): + return None + + @classmethod + def postprocess(cls, options): + pass + + +class Flag(Option): + """Base class for all kinds of flags. """ + + is_Flag = True + + +class BooleanOption(Option): + """An option that must have a boolean value or equivalent assigned. """ + + @classmethod + def preprocess(cls, value): + if value in [True, False]: + return bool(value) + else: + raise OptionError("'%s' must have a boolean value assigned, got %s" % (cls.option, value)) + + +class OptionType(type): + """Base type for all options that does registers options. """ + + def __init__(cls, *args, **kwargs): + @property + def getter(self): + try: + return self[cls.option] + except KeyError: + return cls.default() + + setattr(Options, cls.option, getter) + Options.__options__[cls.option] = cls + + +@public +class Options(dict): + """ + Options manager for polynomial manipulation module. + + Examples + ======== + + >>> from sympy.polys.polyoptions import Options + >>> from sympy.polys.polyoptions import build_options + + >>> from sympy.abc import x, y, z + + >>> Options((x, y, z), {'domain': 'ZZ'}) + {'auto': False, 'domain': ZZ, 'gens': (x, y, z)} + + >>> build_options((x, y, z), {'domain': 'ZZ'}) + {'auto': False, 'domain': ZZ, 'gens': (x, y, z)} + + **Options** + + * Expand --- boolean option + * Gens --- option + * Wrt --- option + * Sort --- option + * Order --- option + * Field --- boolean option + * Greedy --- boolean option + * Domain --- option + * Split --- boolean option + * Gaussian --- boolean option + * Extension --- option + * Modulus --- option + * Symmetric --- boolean option + * Strict --- boolean option + + **Flags** + + * Auto --- boolean flag + * Frac --- boolean flag + * Formal --- boolean flag + * Polys --- boolean flag + * Include --- boolean flag + * All --- boolean flag + * Gen --- flag + * Series --- boolean flag + + """ + + __order__ = None + __options__: dict[str, type[Option]] = {} + + def __init__(self, gens, args, flags=None, strict=False): + dict.__init__(self) + + if gens and args.get('gens', ()): + raise OptionError( + "both '*gens' and keyword argument 'gens' supplied") + elif gens: + args = dict(args) + args['gens'] = gens + + defaults = args.pop('defaults', {}) + + def preprocess_options(args): + for option, value in args.items(): + try: + cls = self.__options__[option] + except KeyError: + raise OptionError("'%s' is not a valid option" % option) + + if issubclass(cls, Flag): + if flags is None or option not in flags: + if strict: + raise OptionError("'%s' flag is not allowed in this context" % option) + + if value is not None: + self[option] = cls.preprocess(value) + + preprocess_options(args) + + for key in dict(defaults): + if key in self: + del defaults[key] + else: + for option in self.keys(): + cls = self.__options__[option] + + if key in cls.excludes: + del defaults[key] + break + + preprocess_options(defaults) + + for option in self.keys(): + cls = self.__options__[option] + + for require_option in cls.requires: + if self.get(require_option) is None: + raise OptionError("'%s' option is only allowed together with '%s'" % (option, require_option)) + + for exclude_option in cls.excludes: + if self.get(exclude_option) is not None: + raise OptionError("'%s' option is not allowed together with '%s'" % (option, exclude_option)) + + for option in self.__order__: + self.__options__[option].postprocess(self) + + @classmethod + def _init_dependencies_order(cls): + """Resolve the order of options' processing. """ + if cls.__order__ is None: + vertices, edges = [], set() + + for name, option in cls.__options__.items(): + vertices.append(name) + + edges.update((_name, name) for _name in option.after) + + edges.update((name, _name) for _name in option.before) + + try: + cls.__order__ = topological_sort((vertices, list(edges))) + except ValueError: + raise RuntimeError( + "cycle detected in sympy.polys options framework") + + def clone(self, updates={}): + """Clone ``self`` and update specified options. """ + obj = dict.__new__(self.__class__) + + for option, value in self.items(): + obj[option] = value + + for option, value in updates.items(): + obj[option] = value + + return obj + + def __setattr__(self, attr, value): + if attr in self.__options__: + self[attr] = value + else: + super().__setattr__(attr, value) + + @property + def args(self): + args = {} + + for option, value in self.items(): + if value is not None and option != 'gens': + cls = self.__options__[option] + + if not issubclass(cls, Flag): + args[option] = value + + return args + + @property + def options(self): + options = {} + + for option, cls in self.__options__.items(): + if not issubclass(cls, Flag): + options[option] = getattr(self, option) + + return options + + @property + def flags(self): + flags = {} + + for option, cls in self.__options__.items(): + if issubclass(cls, Flag): + flags[option] = getattr(self, option) + + return flags + + +class Expand(BooleanOption, metaclass=OptionType): + """``expand`` option to polynomial manipulation functions. """ + + option = 'expand' + + requires: list[str] = [] + excludes: list[str] = [] + + @classmethod + def default(cls): + return True + + +class Gens(Option, metaclass=OptionType): + """``gens`` option to polynomial manipulation functions. """ + + option = 'gens' + + requires: list[str] = [] + excludes: list[str] = [] + + @classmethod + def default(cls): + return () + + @classmethod + def preprocess(cls, gens): + if isinstance(gens, Basic): + gens = (gens,) + elif len(gens) == 1 and is_sequence(gens[0]): + gens = gens[0] + + if gens == (None,): + gens = () + elif has_dups(gens): + raise GeneratorsError("duplicated generators: %s" % str(gens)) + elif any(gen.is_commutative is False for gen in gens): + raise GeneratorsError("non-commutative generators: %s" % str(gens)) + + return tuple(gens) + + +class Wrt(Option, metaclass=OptionType): + """``wrt`` option to polynomial manipulation functions. """ + + option = 'wrt' + + requires: list[str] = [] + excludes: list[str] = [] + + _re_split = re.compile(r"\s*,\s*|\s+") + + @classmethod + def preprocess(cls, wrt): + if isinstance(wrt, Basic): + return [str(wrt)] + elif isinstance(wrt, str): + wrt = wrt.strip() + if wrt.endswith(','): + raise OptionError('Bad input: missing parameter.') + if not wrt: + return [] + return list(cls._re_split.split(wrt)) + elif hasattr(wrt, '__getitem__'): + return list(map(str, wrt)) + else: + raise OptionError("invalid argument for 'wrt' option") + + +class Sort(Option, metaclass=OptionType): + """``sort`` option to polynomial manipulation functions. """ + + option = 'sort' + + requires: list[str] = [] + excludes: list[str] = [] + + @classmethod + def default(cls): + return [] + + @classmethod + def preprocess(cls, sort): + if isinstance(sort, str): + return [ gen.strip() for gen in sort.split('>') ] + elif hasattr(sort, '__getitem__'): + return list(map(str, sort)) + else: + raise OptionError("invalid argument for 'sort' option") + + +class Order(Option, metaclass=OptionType): + """``order`` option to polynomial manipulation functions. """ + + option = 'order' + + requires: list[str] = [] + excludes: list[str] = [] + + @classmethod + def default(cls): + return sympy.polys.orderings.lex + + @classmethod + def preprocess(cls, order): + return sympy.polys.orderings.monomial_key(order) + + +class Field(BooleanOption, metaclass=OptionType): + """``field`` option to polynomial manipulation functions. """ + + option = 'field' + + requires: list[str] = [] + excludes = ['domain', 'split', 'gaussian'] + + +class Greedy(BooleanOption, metaclass=OptionType): + """``greedy`` option to polynomial manipulation functions. """ + + option = 'greedy' + + requires: list[str] = [] + excludes = ['domain', 'split', 'gaussian', 'extension', 'modulus', 'symmetric'] + + +class Composite(BooleanOption, metaclass=OptionType): + """``composite`` option to polynomial manipulation functions. """ + + option = 'composite' + + @classmethod + def default(cls): + return None + + requires: list[str] = [] + excludes = ['domain', 'split', 'gaussian', 'extension', 'modulus', 'symmetric'] + + +class Domain(Option, metaclass=OptionType): + """``domain`` option to polynomial manipulation functions. """ + + option = 'domain' + + requires: list[str] = [] + excludes = ['field', 'greedy', 'split', 'gaussian', 'extension'] + + after = ['gens'] + + _re_realfield = re.compile(r"^(R|RR)(_(\d+))?$") + _re_complexfield = re.compile(r"^(C|CC)(_(\d+))?$") + _re_finitefield = re.compile(r"^(FF|GF)\((\d+)\)$") + _re_polynomial = re.compile(r"^(Z|ZZ|Q|QQ|ZZ_I|QQ_I|R|RR|C|CC)\[(.+)\]$") + _re_fraction = re.compile(r"^(Z|ZZ|Q|QQ)\((.+)\)$") + _re_algebraic = re.compile(r"^(Q|QQ)\<(.+)\>$") + + @classmethod + def preprocess(cls, domain): + if isinstance(domain, sympy.polys.domains.Domain): + return domain + elif hasattr(domain, 'to_domain'): + return domain.to_domain() + elif isinstance(domain, str): + if domain in ['Z', 'ZZ']: + return sympy.polys.domains.ZZ + + if domain in ['Q', 'QQ']: + return sympy.polys.domains.QQ + + if domain == 'ZZ_I': + return sympy.polys.domains.ZZ_I + + if domain == 'QQ_I': + return sympy.polys.domains.QQ_I + + if domain == 'EX': + return sympy.polys.domains.EX + + r = cls._re_realfield.match(domain) + + if r is not None: + _, _, prec = r.groups() + + if prec is None: + return sympy.polys.domains.RR + else: + return sympy.polys.domains.RealField(int(prec)) + + r = cls._re_complexfield.match(domain) + + if r is not None: + _, _, prec = r.groups() + + if prec is None: + return sympy.polys.domains.CC + else: + return sympy.polys.domains.ComplexField(int(prec)) + + r = cls._re_finitefield.match(domain) + + if r is not None: + return sympy.polys.domains.FF(int(r.groups()[1])) + + r = cls._re_polynomial.match(domain) + + if r is not None: + ground, gens = r.groups() + + gens = list(map(sympify, gens.split(','))) + + if ground in ['Z', 'ZZ']: + return sympy.polys.domains.ZZ.poly_ring(*gens) + elif ground in ['Q', 'QQ']: + return sympy.polys.domains.QQ.poly_ring(*gens) + elif ground in ['R', 'RR']: + return sympy.polys.domains.RR.poly_ring(*gens) + elif ground == 'ZZ_I': + return sympy.polys.domains.ZZ_I.poly_ring(*gens) + elif ground == 'QQ_I': + return sympy.polys.domains.QQ_I.poly_ring(*gens) + else: + return sympy.polys.domains.CC.poly_ring(*gens) + + r = cls._re_fraction.match(domain) + + if r is not None: + ground, gens = r.groups() + + gens = list(map(sympify, gens.split(','))) + + if ground in ['Z', 'ZZ']: + return sympy.polys.domains.ZZ.frac_field(*gens) + else: + return sympy.polys.domains.QQ.frac_field(*gens) + + r = cls._re_algebraic.match(domain) + + if r is not None: + gens = list(map(sympify, r.groups()[1].split(','))) + return sympy.polys.domains.QQ.algebraic_field(*gens) + + raise OptionError('expected a valid domain specification, got %s' % domain) + + @classmethod + def postprocess(cls, options): + if 'gens' in options and 'domain' in options and options['domain'].is_Composite and \ + (set(options['domain'].symbols) & set(options['gens'])): + raise GeneratorsError( + "ground domain and generators interfere together") + elif ('gens' not in options or not options['gens']) and \ + 'domain' in options and options['domain'] == sympy.polys.domains.EX: + raise GeneratorsError("you have to provide generators because EX domain was requested") + + +class Split(BooleanOption, metaclass=OptionType): + """``split`` option to polynomial manipulation functions. """ + + option = 'split' + + requires: list[str] = [] + excludes = ['field', 'greedy', 'domain', 'gaussian', 'extension', + 'modulus', 'symmetric'] + + @classmethod + def postprocess(cls, options): + if 'split' in options: + raise NotImplementedError("'split' option is not implemented yet") + + +class Gaussian(BooleanOption, metaclass=OptionType): + """``gaussian`` option to polynomial manipulation functions. """ + + option = 'gaussian' + + requires: list[str] = [] + excludes = ['field', 'greedy', 'domain', 'split', 'extension', + 'modulus', 'symmetric'] + + @classmethod + def postprocess(cls, options): + if 'gaussian' in options and options['gaussian'] is True: + options['domain'] = sympy.polys.domains.QQ_I + Extension.postprocess(options) + + +class Extension(Option, metaclass=OptionType): + """``extension`` option to polynomial manipulation functions. """ + + option = 'extension' + + requires: list[str] = [] + excludes = ['greedy', 'domain', 'split', 'gaussian', 'modulus', + 'symmetric'] + + @classmethod + def preprocess(cls, extension): + if extension == 1: + return bool(extension) + elif extension == 0: + raise OptionError("'False' is an invalid argument for 'extension'") + else: + if not hasattr(extension, '__iter__'): + extension = {extension} + else: + if not extension: + extension = None + else: + extension = set(extension) + + return extension + + @classmethod + def postprocess(cls, options): + if 'extension' in options and options['extension'] is not True: + options['domain'] = sympy.polys.domains.QQ.algebraic_field( + *options['extension']) + + +class Modulus(Option, metaclass=OptionType): + """``modulus`` option to polynomial manipulation functions. """ + + option = 'modulus' + + requires: list[str] = [] + excludes = ['greedy', 'split', 'domain', 'gaussian', 'extension'] + + @classmethod + def preprocess(cls, modulus): + modulus = sympify(modulus) + + if modulus.is_Integer and modulus > 0: + return int(modulus) + else: + raise OptionError( + "'modulus' must a positive integer, got %s" % modulus) + + @classmethod + def postprocess(cls, options): + if 'modulus' in options: + modulus = options['modulus'] + symmetric = options.get('symmetric', True) + options['domain'] = sympy.polys.domains.FF(modulus, symmetric) + + +class Symmetric(BooleanOption, metaclass=OptionType): + """``symmetric`` option to polynomial manipulation functions. """ + + option = 'symmetric' + + requires = ['modulus'] + excludes = ['greedy', 'domain', 'split', 'gaussian', 'extension'] + + +class Strict(BooleanOption, metaclass=OptionType): + """``strict`` option to polynomial manipulation functions. """ + + option = 'strict' + + @classmethod + def default(cls): + return True + + +class Auto(BooleanOption, Flag, metaclass=OptionType): + """``auto`` flag to polynomial manipulation functions. """ + + option = 'auto' + + after = ['field', 'domain', 'extension', 'gaussian'] + + @classmethod + def default(cls): + return True + + @classmethod + def postprocess(cls, options): + if ('domain' in options or 'field' in options) and 'auto' not in options: + options['auto'] = False + + +class Frac(BooleanOption, Flag, metaclass=OptionType): + """``auto`` option to polynomial manipulation functions. """ + + option = 'frac' + + @classmethod + def default(cls): + return False + + +class Formal(BooleanOption, Flag, metaclass=OptionType): + """``formal`` flag to polynomial manipulation functions. """ + + option = 'formal' + + @classmethod + def default(cls): + return False + + +class Polys(BooleanOption, Flag, metaclass=OptionType): + """``polys`` flag to polynomial manipulation functions. """ + + option = 'polys' + + +class Include(BooleanOption, Flag, metaclass=OptionType): + """``include`` flag to polynomial manipulation functions. """ + + option = 'include' + + @classmethod + def default(cls): + return False + + +class All(BooleanOption, Flag, metaclass=OptionType): + """``all`` flag to polynomial manipulation functions. """ + + option = 'all' + + @classmethod + def default(cls): + return False + + +class Gen(Flag, metaclass=OptionType): + """``gen`` flag to polynomial manipulation functions. """ + + option = 'gen' + + @classmethod + def default(cls): + return 0 + + @classmethod + def preprocess(cls, gen): + if isinstance(gen, (Basic, int)): + return gen + else: + raise OptionError("invalid argument for 'gen' option") + + +class Series(BooleanOption, Flag, metaclass=OptionType): + """``series`` flag to polynomial manipulation functions. """ + + option = 'series' + + @classmethod + def default(cls): + return False + + +class Symbols(Flag, metaclass=OptionType): + """``symbols`` flag to polynomial manipulation functions. """ + + option = 'symbols' + + @classmethod + def default(cls): + return numbered_symbols('s', start=1) + + @classmethod + def preprocess(cls, symbols): + if hasattr(symbols, '__iter__'): + return iter(symbols) + else: + raise OptionError("expected an iterator or iterable container, got %s" % symbols) + + +class Method(Flag, metaclass=OptionType): + """``method`` flag to polynomial manipulation functions. """ + + option = 'method' + + @classmethod + def preprocess(cls, method): + if isinstance(method, str): + return method.lower() + else: + raise OptionError("expected a string, got %s" % method) + + +def build_options(gens, args=None): + """Construct options from keyword arguments or ... options. """ + if args is None: + gens, args = (), gens + + if len(args) != 1 or 'opt' not in args or gens: + return Options(gens, args) + else: + return args['opt'] + + +def allowed_flags(args, flags): + """ + Allow specified flags to be used in the given context. + + Examples + ======== + + >>> from sympy.polys.polyoptions import allowed_flags + >>> from sympy.polys.domains import ZZ + + >>> allowed_flags({'domain': ZZ}, []) + + >>> allowed_flags({'domain': ZZ, 'frac': True}, []) + Traceback (most recent call last): + ... + FlagError: 'frac' flag is not allowed in this context + + >>> allowed_flags({'domain': ZZ, 'frac': True}, ['frac']) + + """ + flags = set(flags) + + for arg in args.keys(): + try: + if Options.__options__[arg].is_Flag and arg not in flags: + raise FlagError( + "'%s' flag is not allowed in this context" % arg) + except KeyError: + raise OptionError("'%s' is not a valid option" % arg) + + +def set_defaults(options, **defaults): + """Update options with default values. """ + if 'defaults' not in options: + options = dict(options) + options['defaults'] = defaults + + return options + +Options._init_dependencies_order() diff --git a/lib/python3.12/site-packages/sympy/polys/tests/__init__.py b/lib/python3.12/site-packages/sympy/polys/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_appellseqs.cpython-312.pyc b/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_appellseqs.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f19f41a9f258e72f6099d4d1900b6f8f55ab5509 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_appellseqs.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_constructor.cpython-312.pyc b/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_constructor.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b487f87ce3537b854966d5bff87ef8c63bfb0ecc Binary files /dev/null and b/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_constructor.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_densearith.cpython-312.pyc b/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_densearith.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b660f8b2b37c0ce20421fdc42f19fcd50a21c594 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_densearith.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_densetools.cpython-312.pyc b/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_densetools.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e0dad12bbb3e754a93e13f70365731088561edb3 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_densetools.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_dispersion.cpython-312.pyc b/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_dispersion.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1dc84d8ee9b1b87d088438b12b0ce275a0901697 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_dispersion.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_euclidtools.cpython-312.pyc b/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_euclidtools.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cf3d5a908308ddd9ec21f138239443d3c7dc26b9 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_euclidtools.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_factortools.cpython-312.pyc b/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_factortools.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5c6844da4c0af15e5588719d70843667cb9e0843 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_factortools.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_fields.cpython-312.pyc b/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_fields.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..aac44fad0ede6f765393cdb4634f959b123da30c Binary files /dev/null and b/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_fields.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_groebnertools.cpython-312.pyc b/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_groebnertools.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..eec571ccd182be45047cd96824124a3d82096dad Binary files /dev/null and b/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_groebnertools.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_heuristicgcd.cpython-312.pyc b/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_heuristicgcd.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e2a2d4f7c387f5bb10ce1309a1eb39771b6614fc Binary files /dev/null and b/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_heuristicgcd.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_hypothesis.cpython-312.pyc b/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_hypothesis.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1f9c3318d3103e55ba76889e1e79d64e237ead10 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_hypothesis.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_multivariate_resultants.cpython-312.pyc b/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_multivariate_resultants.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..09c070879613f38bd12dab1a47e5e4d47dc2e4fa Binary files /dev/null and b/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_multivariate_resultants.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_orthopolys.cpython-312.pyc b/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_orthopolys.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1f31e65955f03f7f6357b9cdc1d586c83aa669ae Binary files /dev/null and b/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_orthopolys.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_partfrac.cpython-312.pyc b/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_partfrac.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..48f850f63fbeefa52f85fc621c40609f6b857517 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_partfrac.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_polyclasses.cpython-312.pyc b/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_polyclasses.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dcbb8d0e78fde0dd6ccf4807b843460e4ac47667 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_polyclasses.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_polyfuncs.cpython-312.pyc b/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_polyfuncs.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2bb96f3647ef2b45f4c8a35a5299ded36ad2e27f Binary files /dev/null and b/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_polyfuncs.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_polymatrix.cpython-312.pyc b/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_polymatrix.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..afdde036817c8ff1b94c15c32caba23f212d27c0 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_polymatrix.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_polyoptions.cpython-312.pyc b/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_polyoptions.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..74a355b7544d342c73e1e152f03d2a6b297411ce Binary files /dev/null and b/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_polyoptions.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_polyroots.cpython-312.pyc b/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_polyroots.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ef8887fbb7adabed9ae8564a3c7ac25c3160ea6d Binary files /dev/null and b/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_polyroots.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_polyutils.cpython-312.pyc b/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_polyutils.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..69990c3b5fd8f699babad92fdd9420277518828d Binary files /dev/null and b/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_polyutils.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_ring_series.cpython-312.pyc b/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_ring_series.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ffa659191b4b993a05e3a6f633562281e6b28b68 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_ring_series.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_rootisolation.cpython-312.pyc b/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_rootisolation.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..065099006b52ac3e55fdb93bd42b00c278b0321a Binary files /dev/null and b/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_rootisolation.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_rootoftools.cpython-312.pyc b/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_rootoftools.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..00526e885a233856a9790f802a506a68ba5e3b04 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_rootoftools.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_solvers.cpython-312.pyc b/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_solvers.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0336fbd8af9bf10b42539765acafce918265b883 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_solvers.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_specialpolys.cpython-312.pyc b/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_specialpolys.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2114ef0a2a623bda57cb8e890665fe7a16a96361 Binary files /dev/null and b/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_specialpolys.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_subresultants_qq_zz.cpython-312.pyc b/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_subresultants_qq_zz.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..650a9eb6f45c9111c2ea91a0f506aaf69654511a Binary files /dev/null and b/lib/python3.12/site-packages/sympy/polys/tests/__pycache__/test_subresultants_qq_zz.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/sympy/polys/tests/test_appellseqs.py b/lib/python3.12/site-packages/sympy/polys/tests/test_appellseqs.py new file mode 100644 index 0000000000000000000000000000000000000000..f4718a2da272ac6f36a968572dc246ebc699e5c4 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/polys/tests/test_appellseqs.py @@ -0,0 +1,91 @@ +"""Tests for efficient functions for generating Appell sequences.""" +from sympy.core.numbers import Rational as Q +from sympy.polys.polytools import Poly +from sympy.testing.pytest import raises +from sympy.polys.appellseqs import (bernoulli_poly, bernoulli_c_poly, + euler_poly, genocchi_poly, andre_poly) +from sympy.abc import x + +def test_bernoulli_poly(): + raises(ValueError, lambda: bernoulli_poly(-1, x)) + assert bernoulli_poly(1, x, polys=True) == Poly(x - Q(1,2)) + + assert bernoulli_poly(0, x) == 1 + assert bernoulli_poly(1, x) == x - Q(1,2) + assert bernoulli_poly(2, x) == x**2 - x + Q(1,6) + assert bernoulli_poly(3, x) == x**3 - Q(3,2)*x**2 + Q(1,2)*x + assert bernoulli_poly(4, x) == x**4 - 2*x**3 + x**2 - Q(1,30) + assert bernoulli_poly(5, x) == x**5 - Q(5,2)*x**4 + Q(5,3)*x**3 - Q(1,6)*x + assert bernoulli_poly(6, x) == x**6 - 3*x**5 + Q(5,2)*x**4 - Q(1,2)*x**2 + Q(1,42) + + assert bernoulli_poly(1).dummy_eq(x - Q(1,2)) + assert bernoulli_poly(1, polys=True) == Poly(x - Q(1,2)) + +def test_bernoulli_c_poly(): + raises(ValueError, lambda: bernoulli_c_poly(-1, x)) + assert bernoulli_c_poly(1, x, polys=True) == Poly(x, domain='QQ') + + assert bernoulli_c_poly(0, x) == 1 + assert bernoulli_c_poly(1, x) == x + assert bernoulli_c_poly(2, x) == x**2 - Q(1,3) + assert bernoulli_c_poly(3, x) == x**3 - x + assert bernoulli_c_poly(4, x) == x**4 - 2*x**2 + Q(7,15) + assert bernoulli_c_poly(5, x) == x**5 - Q(10,3)*x**3 + Q(7,3)*x + assert bernoulli_c_poly(6, x) == x**6 - 5*x**4 + 7*x**2 - Q(31,21) + + assert bernoulli_c_poly(1).dummy_eq(x) + assert bernoulli_c_poly(1, polys=True) == Poly(x, domain='QQ') + + assert 2**8 * bernoulli_poly(8, (x+1)/2).expand() == bernoulli_c_poly(8, x) + assert 2**9 * bernoulli_poly(9, (x+1)/2).expand() == bernoulli_c_poly(9, x) + +def test_genocchi_poly(): + raises(ValueError, lambda: genocchi_poly(-1, x)) + assert genocchi_poly(2, x, polys=True) == Poly(-2*x + 1) + + assert genocchi_poly(0, x) == 0 + assert genocchi_poly(1, x) == -1 + assert genocchi_poly(2, x) == 1 - 2*x + assert genocchi_poly(3, x) == 3*x - 3*x**2 + assert genocchi_poly(4, x) == -1 + 6*x**2 - 4*x**3 + assert genocchi_poly(5, x) == -5*x + 10*x**3 - 5*x**4 + assert genocchi_poly(6, x) == 3 - 15*x**2 + 15*x**4 - 6*x**5 + + assert genocchi_poly(2).dummy_eq(-2*x + 1) + assert genocchi_poly(2, polys=True) == Poly(-2*x + 1) + + assert 2 * (bernoulli_poly(8, x) - bernoulli_c_poly(8, x)) == genocchi_poly(8, x) + assert 2 * (bernoulli_poly(9, x) - bernoulli_c_poly(9, x)) == genocchi_poly(9, x) + +def test_euler_poly(): + raises(ValueError, lambda: euler_poly(-1, x)) + assert euler_poly(1, x, polys=True) == Poly(x - Q(1,2)) + + assert euler_poly(0, x) == 1 + assert euler_poly(1, x) == x - Q(1,2) + assert euler_poly(2, x) == x**2 - x + assert euler_poly(3, x) == x**3 - Q(3,2)*x**2 + Q(1,4) + assert euler_poly(4, x) == x**4 - 2*x**3 + x + assert euler_poly(5, x) == x**5 - Q(5,2)*x**4 + Q(5,2)*x**2 - Q(1,2) + assert euler_poly(6, x) == x**6 - 3*x**5 + 5*x**3 - 3*x + + assert euler_poly(1).dummy_eq(x - Q(1,2)) + assert euler_poly(1, polys=True) == Poly(x - Q(1,2)) + + assert genocchi_poly(9, x) == euler_poly(8, x) * -9 + assert genocchi_poly(10, x) == euler_poly(9, x) * -10 + +def test_andre_poly(): + raises(ValueError, lambda: andre_poly(-1, x)) + assert andre_poly(1, x, polys=True) == Poly(x) + + assert andre_poly(0, x) == 1 + assert andre_poly(1, x) == x + assert andre_poly(2, x) == x**2 - 1 + assert andre_poly(3, x) == x**3 - 3*x + assert andre_poly(4, x) == x**4 - 6*x**2 + 5 + assert andre_poly(5, x) == x**5 - 10*x**3 + 25*x + assert andre_poly(6, x) == x**6 - 15*x**4 + 75*x**2 - 61 + + assert andre_poly(1).dummy_eq(x) + assert andre_poly(1, polys=True) == Poly(x) diff --git a/lib/python3.12/site-packages/sympy/polys/tests/test_constructor.py b/lib/python3.12/site-packages/sympy/polys/tests/test_constructor.py new file mode 100644 index 0000000000000000000000000000000000000000..14dacb9bb1c12e983a83590fd9af8c8d8f3ff036 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/polys/tests/test_constructor.py @@ -0,0 +1,208 @@ +"""Tests for tools for constructing domains for expressions. """ + +from sympy.polys.constructor import construct_domain +from sympy.polys.domains import ZZ, QQ, ZZ_I, QQ_I, RR, CC, EX +from sympy.polys.domains.realfield import RealField +from sympy.polys.domains.complexfield import ComplexField + +from sympy.core import (Catalan, GoldenRatio) +from sympy.core.numbers import (E, Float, I, Rational, pi) +from sympy.core.singleton import S +from sympy.functions.elementary.exponential import exp +from sympy.functions.elementary.miscellaneous import sqrt +from sympy.functions.elementary.trigonometric import sin +from sympy.abc import x, y + + +def test_construct_domain(): + + assert construct_domain([1, 2, 3]) == (ZZ, [ZZ(1), ZZ(2), ZZ(3)]) + assert construct_domain([1, 2, 3], field=True) == (QQ, [QQ(1), QQ(2), QQ(3)]) + + assert construct_domain([S.One, S(2), S(3)]) == (ZZ, [ZZ(1), ZZ(2), ZZ(3)]) + assert construct_domain([S.One, S(2), S(3)], field=True) == (QQ, [QQ(1), QQ(2), QQ(3)]) + + assert construct_domain([S.Half, S(2)]) == (QQ, [QQ(1, 2), QQ(2)]) + result = construct_domain([3.14, 1, S.Half]) + assert isinstance(result[0], RealField) + assert result[1] == [RR(3.14), RR(1.0), RR(0.5)] + + result = construct_domain([3.14, I, S.Half]) + assert isinstance(result[0], ComplexField) + assert result[1] == [CC(3.14), CC(1.0j), CC(0.5)] + + assert construct_domain([1.0+I]) == (CC, [CC(1.0, 1.0)]) + assert construct_domain([2.0+3.0*I]) == (CC, [CC(2.0, 3.0)]) + + assert construct_domain([1, I]) == (ZZ_I, [ZZ_I(1, 0), ZZ_I(0, 1)]) + assert construct_domain([1, I/2]) == (QQ_I, [QQ_I(1, 0), QQ_I(0, S.Half)]) + + assert construct_domain([3.14, sqrt(2)], extension=None) == (EX, [EX(3.14), EX(sqrt(2))]) + assert construct_domain([3.14, sqrt(2)], extension=True) == (EX, [EX(3.14), EX(sqrt(2))]) + + assert construct_domain([1, sqrt(2)], extension=None) == (EX, [EX(1), EX(sqrt(2))]) + + assert construct_domain([x, sqrt(x)]) == (EX, [EX(x), EX(sqrt(x))]) + assert construct_domain([x, sqrt(x), sqrt(y)]) == (EX, [EX(x), EX(sqrt(x)), EX(sqrt(y))]) + + alg = QQ.algebraic_field(sqrt(2)) + + assert construct_domain([7, S.Half, sqrt(2)], extension=True) == \ + (alg, [alg.convert(7), alg.convert(S.Half), alg.convert(sqrt(2))]) + + alg = QQ.algebraic_field(sqrt(2) + sqrt(3)) + + assert construct_domain([7, sqrt(2), sqrt(3)], extension=True) == \ + (alg, [alg.convert(7), alg.convert(sqrt(2)), alg.convert(sqrt(3))]) + + dom = ZZ[x] + + assert construct_domain([2*x, 3]) == \ + (dom, [dom.convert(2*x), dom.convert(3)]) + + dom = ZZ[x, y] + + assert construct_domain([2*x, 3*y]) == \ + (dom, [dom.convert(2*x), dom.convert(3*y)]) + + dom = QQ[x] + + assert construct_domain([x/2, 3]) == \ + (dom, [dom.convert(x/2), dom.convert(3)]) + + dom = QQ[x, y] + + assert construct_domain([x/2, 3*y]) == \ + (dom, [dom.convert(x/2), dom.convert(3*y)]) + + dom = ZZ_I[x] + + assert construct_domain([2*x, I]) == \ + (dom, [dom.convert(2*x), dom.convert(I)]) + + dom = ZZ_I[x, y] + + assert construct_domain([2*x, I*y]) == \ + (dom, [dom.convert(2*x), dom.convert(I*y)]) + + dom = QQ_I[x] + + assert construct_domain([x/2, I]) == \ + (dom, [dom.convert(x/2), dom.convert(I)]) + + dom = QQ_I[x, y] + + assert construct_domain([x/2, I*y]) == \ + (dom, [dom.convert(x/2), dom.convert(I*y)]) + + dom = RR[x] + + assert construct_domain([x/2, 3.5]) == \ + (dom, [dom.convert(x/2), dom.convert(3.5)]) + + dom = RR[x, y] + + assert construct_domain([x/2, 3.5*y]) == \ + (dom, [dom.convert(x/2), dom.convert(3.5*y)]) + + dom = CC[x] + + assert construct_domain([I*x/2, 3.5]) == \ + (dom, [dom.convert(I*x/2), dom.convert(3.5)]) + + dom = CC[x, y] + + assert construct_domain([I*x/2, 3.5*y]) == \ + (dom, [dom.convert(I*x/2), dom.convert(3.5*y)]) + + dom = CC[x] + + assert construct_domain([x/2, I*3.5]) == \ + (dom, [dom.convert(x/2), dom.convert(I*3.5)]) + + dom = CC[x, y] + + assert construct_domain([x/2, I*3.5*y]) == \ + (dom, [dom.convert(x/2), dom.convert(I*3.5*y)]) + + dom = ZZ.frac_field(x) + + assert construct_domain([2/x, 3]) == \ + (dom, [dom.convert(2/x), dom.convert(3)]) + + dom = ZZ.frac_field(x, y) + + assert construct_domain([2/x, 3*y]) == \ + (dom, [dom.convert(2/x), dom.convert(3*y)]) + + dom = RR.frac_field(x) + + assert construct_domain([2/x, 3.5]) == \ + (dom, [dom.convert(2/x), dom.convert(3.5)]) + + dom = RR.frac_field(x, y) + + assert construct_domain([2/x, 3.5*y]) == \ + (dom, [dom.convert(2/x), dom.convert(3.5*y)]) + + dom = RealField(prec=336)[x] + + assert construct_domain([pi.evalf(100)*x]) == \ + (dom, [dom.convert(pi.evalf(100)*x)]) + + assert construct_domain(2) == (ZZ, ZZ(2)) + assert construct_domain(S(2)/3) == (QQ, QQ(2, 3)) + assert construct_domain(Rational(2, 3)) == (QQ, QQ(2, 3)) + + assert construct_domain({}) == (ZZ, {}) + + +def test_complex_exponential(): + w = exp(-I*2*pi/3, evaluate=False) + alg = QQ.algebraic_field(w) + assert construct_domain([w**2, w, 1], extension=True) == ( + alg, + [alg.convert(w**2), + alg.convert(w), + alg.convert(1)] + ) + + +def test_composite_option(): + assert construct_domain({(1,): sin(y)}, composite=False) == \ + (EX, {(1,): EX(sin(y))}) + + assert construct_domain({(1,): y}, composite=False) == \ + (EX, {(1,): EX(y)}) + + assert construct_domain({(1, 1): 1}, composite=False) == \ + (ZZ, {(1, 1): 1}) + + assert construct_domain({(1, 0): y}, composite=False) == \ + (EX, {(1, 0): EX(y)}) + + +def test_precision(): + f1 = Float("1.01") + f2 = Float("1.0000000000000000000001") + for u in [1, 1e-2, 1e-6, 1e-13, 1e-14, 1e-16, 1e-20, 1e-100, 1e-300, + f1, f2]: + result = construct_domain([u]) + v = float(result[1][0]) + assert abs(u - v) / u < 1e-14 # Test relative accuracy + + result = construct_domain([f1]) + y = result[1][0] + assert y-1 > 1e-50 + + result = construct_domain([f2]) + y = result[1][0] + assert y-1 > 1e-50 + + +def test_issue_11538(): + for n in [E, pi, Catalan]: + assert construct_domain(n)[0] == ZZ[n] + assert construct_domain(x + n)[0] == ZZ[x, n] + assert construct_domain(GoldenRatio)[0] == EX + assert construct_domain(x + GoldenRatio)[0] == EX diff --git a/lib/python3.12/site-packages/sympy/polys/tests/test_densearith.py b/lib/python3.12/site-packages/sympy/polys/tests/test_densearith.py new file mode 100644 index 0000000000000000000000000000000000000000..ea626f1feac246198d46986d0c193804e9c78891 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/polys/tests/test_densearith.py @@ -0,0 +1,997 @@ +"""Tests for dense recursive polynomials' arithmetics. """ + +from sympy.external.gmpy import GROUND_TYPES + +from sympy.polys.densebasic import ( + dup_normal, dmp_normal, +) + +from sympy.polys.densearith import ( + dup_add_term, dmp_add_term, + dup_sub_term, dmp_sub_term, + dup_mul_term, dmp_mul_term, + dup_add_ground, dmp_add_ground, + dup_sub_ground, dmp_sub_ground, + dup_mul_ground, dmp_mul_ground, + dup_quo_ground, dmp_quo_ground, + dup_exquo_ground, dmp_exquo_ground, + dup_lshift, dup_rshift, + dup_abs, dmp_abs, + dup_neg, dmp_neg, + dup_add, dmp_add, + dup_sub, dmp_sub, + dup_mul, dmp_mul, + dup_sqr, dmp_sqr, + dup_pow, dmp_pow, + dup_add_mul, dmp_add_mul, + dup_sub_mul, dmp_sub_mul, + dup_pdiv, dup_prem, dup_pquo, dup_pexquo, + dmp_pdiv, dmp_prem, dmp_pquo, dmp_pexquo, + dup_rr_div, dmp_rr_div, + dup_ff_div, dmp_ff_div, + dup_div, dup_rem, dup_quo, dup_exquo, + dmp_div, dmp_rem, dmp_quo, dmp_exquo, + dup_max_norm, dmp_max_norm, + dup_l1_norm, dmp_l1_norm, + dup_l2_norm_squared, dmp_l2_norm_squared, + dup_expand, dmp_expand, +) + +from sympy.polys.polyerrors import ( + ExactQuotientFailed, +) + +from sympy.polys.specialpolys import f_polys +from sympy.polys.domains import FF, ZZ, QQ + +from sympy.testing.pytest import raises + +f_0, f_1, f_2, f_3, f_4, f_5, f_6 = [ f.to_dense() for f in f_polys() ] +F_0 = dmp_mul_ground(dmp_normal(f_0, 2, QQ), QQ(1, 7), 2, QQ) + +def test_dup_add_term(): + f = dup_normal([], ZZ) + + assert dup_add_term(f, ZZ(0), 0, ZZ) == dup_normal([], ZZ) + + assert dup_add_term(f, ZZ(1), 0, ZZ) == dup_normal([1], ZZ) + assert dup_add_term(f, ZZ(1), 1, ZZ) == dup_normal([1, 0], ZZ) + assert dup_add_term(f, ZZ(1), 2, ZZ) == dup_normal([1, 0, 0], ZZ) + + f = dup_normal([1, 1, 1], ZZ) + + assert dup_add_term(f, ZZ(1), 0, ZZ) == dup_normal([1, 1, 2], ZZ) + assert dup_add_term(f, ZZ(1), 1, ZZ) == dup_normal([1, 2, 1], ZZ) + assert dup_add_term(f, ZZ(1), 2, ZZ) == dup_normal([2, 1, 1], ZZ) + + assert dup_add_term(f, ZZ(1), 3, ZZ) == dup_normal([1, 1, 1, 1], ZZ) + assert dup_add_term(f, ZZ(1), 4, ZZ) == dup_normal([1, 0, 1, 1, 1], ZZ) + assert dup_add_term(f, ZZ(1), 5, ZZ) == dup_normal([1, 0, 0, 1, 1, 1], ZZ) + assert dup_add_term( + f, ZZ(1), 6, ZZ) == dup_normal([1, 0, 0, 0, 1, 1, 1], ZZ) + + assert dup_add_term(f, ZZ(-1), 2, ZZ) == dup_normal([1, 1], ZZ) + + +def test_dmp_add_term(): + assert dmp_add_term([ZZ(1), ZZ(1), ZZ(1)], ZZ(1), 2, 0, ZZ) == \ + dup_add_term([ZZ(1), ZZ(1), ZZ(1)], ZZ(1), 2, ZZ) + assert dmp_add_term(f_0, [[]], 3, 2, ZZ) == f_0 + assert dmp_add_term(F_0, [[]], 3, 2, QQ) == F_0 + + +def test_dup_sub_term(): + f = dup_normal([], ZZ) + + assert dup_sub_term(f, ZZ(0), 0, ZZ) == dup_normal([], ZZ) + + assert dup_sub_term(f, ZZ(1), 0, ZZ) == dup_normal([-1], ZZ) + assert dup_sub_term(f, ZZ(1), 1, ZZ) == dup_normal([-1, 0], ZZ) + assert dup_sub_term(f, ZZ(1), 2, ZZ) == dup_normal([-1, 0, 0], ZZ) + + f = dup_normal([1, 1, 1], ZZ) + + assert dup_sub_term(f, ZZ(2), 0, ZZ) == dup_normal([ 1, 1, -1], ZZ) + assert dup_sub_term(f, ZZ(2), 1, ZZ) == dup_normal([ 1, -1, 1], ZZ) + assert dup_sub_term(f, ZZ(2), 2, ZZ) == dup_normal([-1, 1, 1], ZZ) + + assert dup_sub_term(f, ZZ(1), 3, ZZ) == dup_normal([-1, 1, 1, 1], ZZ) + assert dup_sub_term(f, ZZ(1), 4, ZZ) == dup_normal([-1, 0, 1, 1, 1], ZZ) + assert dup_sub_term(f, ZZ(1), 5, ZZ) == dup_normal([-1, 0, 0, 1, 1, 1], ZZ) + assert dup_sub_term( + f, ZZ(1), 6, ZZ) == dup_normal([-1, 0, 0, 0, 1, 1, 1], ZZ) + + assert dup_sub_term(f, ZZ(1), 2, ZZ) == dup_normal([1, 1], ZZ) + + +def test_dmp_sub_term(): + assert dmp_sub_term([ZZ(1), ZZ(1), ZZ(1)], ZZ(1), 2, 0, ZZ) == \ + dup_sub_term([ZZ(1), ZZ(1), ZZ(1)], ZZ(1), 2, ZZ) + assert dmp_sub_term(f_0, [[]], 3, 2, ZZ) == f_0 + assert dmp_sub_term(F_0, [[]], 3, 2, QQ) == F_0 + + +def test_dup_mul_term(): + f = dup_normal([], ZZ) + + assert dup_mul_term(f, ZZ(2), 3, ZZ) == dup_normal([], ZZ) + + f = dup_normal([1, 1], ZZ) + + assert dup_mul_term(f, ZZ(0), 3, ZZ) == dup_normal([], ZZ) + + f = dup_normal([1, 2, 3], ZZ) + + assert dup_mul_term(f, ZZ(2), 0, ZZ) == dup_normal([2, 4, 6], ZZ) + assert dup_mul_term(f, ZZ(2), 1, ZZ) == dup_normal([2, 4, 6, 0], ZZ) + assert dup_mul_term(f, ZZ(2), 2, ZZ) == dup_normal([2, 4, 6, 0, 0], ZZ) + assert dup_mul_term(f, ZZ(2), 3, ZZ) == dup_normal([2, 4, 6, 0, 0, 0], ZZ) + + +def test_dmp_mul_term(): + assert dmp_mul_term([ZZ(1), ZZ(2), ZZ(3)], ZZ(2), 1, 0, ZZ) == \ + dup_mul_term([ZZ(1), ZZ(2), ZZ(3)], ZZ(2), 1, ZZ) + + assert dmp_mul_term([[]], [ZZ(2)], 3, 1, ZZ) == [[]] + assert dmp_mul_term([[ZZ(1)]], [], 3, 1, ZZ) == [[]] + + assert dmp_mul_term([[ZZ(1), ZZ(2)], [ZZ(3)]], [ZZ(2)], 2, 1, ZZ) == \ + [[ZZ(2), ZZ(4)], [ZZ(6)], [], []] + + assert dmp_mul_term([[]], [QQ(2, 3)], 3, 1, QQ) == [[]] + assert dmp_mul_term([[QQ(1, 2)]], [], 3, 1, QQ) == [[]] + + assert dmp_mul_term([[QQ(1, 5), QQ(2, 5)], [QQ(3, 5)]], [QQ(2, 3)], 2, 1, QQ) == \ + [[QQ(2, 15), QQ(4, 15)], [QQ(6, 15)], [], []] + + +def test_dup_add_ground(): + f = ZZ.map([1, 2, 3, 4]) + g = ZZ.map([1, 2, 3, 8]) + + assert dup_add_ground(f, ZZ(4), ZZ) == g + + +def test_dmp_add_ground(): + f = ZZ.map([[1], [2], [3], [4]]) + g = ZZ.map([[1], [2], [3], [8]]) + + assert dmp_add_ground(f, ZZ(4), 1, ZZ) == g + + +def test_dup_sub_ground(): + f = ZZ.map([1, 2, 3, 4]) + g = ZZ.map([1, 2, 3, 0]) + + assert dup_sub_ground(f, ZZ(4), ZZ) == g + + +def test_dmp_sub_ground(): + f = ZZ.map([[1], [2], [3], [4]]) + g = ZZ.map([[1], [2], [3], []]) + + assert dmp_sub_ground(f, ZZ(4), 1, ZZ) == g + + +def test_dup_mul_ground(): + f = dup_normal([], ZZ) + + assert dup_mul_ground(f, ZZ(2), ZZ) == dup_normal([], ZZ) + + f = dup_normal([1, 2, 3], ZZ) + + assert dup_mul_ground(f, ZZ(0), ZZ) == dup_normal([], ZZ) + assert dup_mul_ground(f, ZZ(2), ZZ) == dup_normal([2, 4, 6], ZZ) + + +def test_dmp_mul_ground(): + assert dmp_mul_ground(f_0, ZZ(2), 2, ZZ) == [ + [[ZZ(2), ZZ(4), ZZ(6)], [ZZ(4)]], + [[ZZ(6)]], + [[ZZ(8), ZZ(10), ZZ(12)], [ZZ(2), ZZ(4), ZZ(2)], [ZZ(2)]] + ] + + assert dmp_mul_ground(F_0, QQ(1, 2), 2, QQ) == [ + [[QQ(1, 14), QQ(2, 14), QQ(3, 14)], [QQ(2, 14)]], + [[QQ(3, 14)]], + [[QQ(4, 14), QQ(5, 14), QQ(6, 14)], [QQ(1, 14), QQ(2, 14), + QQ(1, 14)], [QQ(1, 14)]] + ] + + +def test_dup_quo_ground(): + raises(ZeroDivisionError, lambda: dup_quo_ground(dup_normal([1, 2, + 3], ZZ), ZZ(0), ZZ)) + + f = dup_normal([], ZZ) + + assert dup_quo_ground(f, ZZ(3), ZZ) == dup_normal([], ZZ) + + f = dup_normal([6, 2, 8], ZZ) + + assert dup_quo_ground(f, ZZ(1), ZZ) == f + assert dup_quo_ground(f, ZZ(2), ZZ) == dup_normal([3, 1, 4], ZZ) + + assert dup_quo_ground(f, ZZ(3), ZZ) == dup_normal([2, 0, 2], ZZ) + + f = dup_normal([6, 2, 8], QQ) + + assert dup_quo_ground(f, QQ(1), QQ) == f + assert dup_quo_ground(f, QQ(2), QQ) == [QQ(3), QQ(1), QQ(4)] + assert dup_quo_ground(f, QQ(7), QQ) == [QQ(6, 7), QQ(2, 7), QQ(8, 7)] + + +def test_dup_exquo_ground(): + raises(ZeroDivisionError, lambda: dup_exquo_ground(dup_normal([1, + 2, 3], ZZ), ZZ(0), ZZ)) + raises(ExactQuotientFailed, lambda: dup_exquo_ground(dup_normal([1, + 2, 3], ZZ), ZZ(3), ZZ)) + + f = dup_normal([], ZZ) + + assert dup_exquo_ground(f, ZZ(3), ZZ) == dup_normal([], ZZ) + + f = dup_normal([6, 2, 8], ZZ) + + assert dup_exquo_ground(f, ZZ(1), ZZ) == f + assert dup_exquo_ground(f, ZZ(2), ZZ) == dup_normal([3, 1, 4], ZZ) + + f = dup_normal([6, 2, 8], QQ) + + assert dup_exquo_ground(f, QQ(1), QQ) == f + assert dup_exquo_ground(f, QQ(2), QQ) == [QQ(3), QQ(1), QQ(4)] + assert dup_exquo_ground(f, QQ(7), QQ) == [QQ(6, 7), QQ(2, 7), QQ(8, 7)] + + +def test_dmp_quo_ground(): + f = dmp_normal([[6], [2], [8]], 1, ZZ) + + assert dmp_quo_ground(f, ZZ(1), 1, ZZ) == f + assert dmp_quo_ground( + f, ZZ(2), 1, ZZ) == dmp_normal([[3], [1], [4]], 1, ZZ) + + assert dmp_normal(dmp_quo_ground( + f, ZZ(3), 1, ZZ), 1, ZZ) == dmp_normal([[2], [], [2]], 1, ZZ) + + +def test_dmp_exquo_ground(): + f = dmp_normal([[6], [2], [8]], 1, ZZ) + + assert dmp_exquo_ground(f, ZZ(1), 1, ZZ) == f + assert dmp_exquo_ground( + f, ZZ(2), 1, ZZ) == dmp_normal([[3], [1], [4]], 1, ZZ) + + +def test_dup_lshift(): + assert dup_lshift([], 3, ZZ) == [] + assert dup_lshift([1], 3, ZZ) == [1, 0, 0, 0] + + +def test_dup_rshift(): + assert dup_rshift([], 3, ZZ) == [] + assert dup_rshift([1, 0, 0, 0], 3, ZZ) == [1] + + +def test_dup_abs(): + assert dup_abs([], ZZ) == [] + assert dup_abs([ZZ( 1)], ZZ) == [ZZ(1)] + assert dup_abs([ZZ(-7)], ZZ) == [ZZ(7)] + assert dup_abs([ZZ(-1), ZZ(2), ZZ(3)], ZZ) == [ZZ(1), ZZ(2), ZZ(3)] + + assert dup_abs([], QQ) == [] + assert dup_abs([QQ( 1, 2)], QQ) == [QQ(1, 2)] + assert dup_abs([QQ(-7, 3)], QQ) == [QQ(7, 3)] + assert dup_abs( + [QQ(-1, 7), QQ(2, 7), QQ(3, 7)], QQ) == [QQ(1, 7), QQ(2, 7), QQ(3, 7)] + + +def test_dmp_abs(): + assert dmp_abs([ZZ(-1)], 0, ZZ) == [ZZ(1)] + assert dmp_abs([QQ(-1, 2)], 0, QQ) == [QQ(1, 2)] + + assert dmp_abs([[[]]], 2, ZZ) == [[[]]] + assert dmp_abs([[[ZZ(1)]]], 2, ZZ) == [[[ZZ(1)]]] + assert dmp_abs([[[ZZ(-7)]]], 2, ZZ) == [[[ZZ(7)]]] + + assert dmp_abs([[[]]], 2, QQ) == [[[]]] + assert dmp_abs([[[QQ(1, 2)]]], 2, QQ) == [[[QQ(1, 2)]]] + assert dmp_abs([[[QQ(-7, 9)]]], 2, QQ) == [[[QQ(7, 9)]]] + + +def test_dup_neg(): + assert dup_neg([], ZZ) == [] + assert dup_neg([ZZ(1)], ZZ) == [ZZ(-1)] + assert dup_neg([ZZ(-7)], ZZ) == [ZZ(7)] + assert dup_neg([ZZ(-1), ZZ(2), ZZ(3)], ZZ) == [ZZ(1), ZZ(-2), ZZ(-3)] + + assert dup_neg([], QQ) == [] + assert dup_neg([QQ(1, 2)], QQ) == [QQ(-1, 2)] + assert dup_neg([QQ(-7, 9)], QQ) == [QQ(7, 9)] + assert dup_neg([QQ( + -1, 7), QQ(2, 7), QQ(3, 7)], QQ) == [QQ(1, 7), QQ(-2, 7), QQ(-3, 7)] + + +def test_dmp_neg(): + assert dmp_neg([ZZ(-1)], 0, ZZ) == [ZZ(1)] + assert dmp_neg([QQ(-1, 2)], 0, QQ) == [QQ(1, 2)] + + assert dmp_neg([[[]]], 2, ZZ) == [[[]]] + assert dmp_neg([[[ZZ(1)]]], 2, ZZ) == [[[ZZ(-1)]]] + assert dmp_neg([[[ZZ(-7)]]], 2, ZZ) == [[[ZZ(7)]]] + + assert dmp_neg([[[]]], 2, QQ) == [[[]]] + assert dmp_neg([[[QQ(1, 9)]]], 2, QQ) == [[[QQ(-1, 9)]]] + assert dmp_neg([[[QQ(-7, 9)]]], 2, QQ) == [[[QQ(7, 9)]]] + + +def test_dup_add(): + assert dup_add([], [], ZZ) == [] + assert dup_add([ZZ(1)], [], ZZ) == [ZZ(1)] + assert dup_add([], [ZZ(1)], ZZ) == [ZZ(1)] + assert dup_add([ZZ(1)], [ZZ(1)], ZZ) == [ZZ(2)] + assert dup_add([ZZ(1)], [ZZ(2)], ZZ) == [ZZ(3)] + + assert dup_add([ZZ(1), ZZ(2)], [ZZ(1)], ZZ) == [ZZ(1), ZZ(3)] + assert dup_add([ZZ(1)], [ZZ(1), ZZ(2)], ZZ) == [ZZ(1), ZZ(3)] + + assert dup_add([ZZ(1), ZZ( + 2), ZZ(3)], [ZZ(8), ZZ(9), ZZ(10)], ZZ) == [ZZ(9), ZZ(11), ZZ(13)] + + assert dup_add([], [], QQ) == [] + assert dup_add([QQ(1, 2)], [], QQ) == [QQ(1, 2)] + assert dup_add([], [QQ(1, 2)], QQ) == [QQ(1, 2)] + assert dup_add([QQ(1, 4)], [QQ(1, 4)], QQ) == [QQ(1, 2)] + assert dup_add([QQ(1, 4)], [QQ(1, 2)], QQ) == [QQ(3, 4)] + + assert dup_add([QQ(1, 2), QQ(2, 3)], [QQ(1)], QQ) == [QQ(1, 2), QQ(5, 3)] + assert dup_add([QQ(1)], [QQ(1, 2), QQ(2, 3)], QQ) == [QQ(1, 2), QQ(5, 3)] + + assert dup_add([QQ(1, 7), QQ(2, 7), QQ(3, 7)], [QQ( + 8, 7), QQ(9, 7), QQ(10, 7)], QQ) == [QQ(9, 7), QQ(11, 7), QQ(13, 7)] + + +def test_dmp_add(): + assert dmp_add([ZZ(1), ZZ(2)], [ZZ(1)], 0, ZZ) == \ + dup_add([ZZ(1), ZZ(2)], [ZZ(1)], ZZ) + assert dmp_add([QQ(1, 2), QQ(2, 3)], [QQ(1)], 0, QQ) == \ + dup_add([QQ(1, 2), QQ(2, 3)], [QQ(1)], QQ) + + assert dmp_add([[[]]], [[[]]], 2, ZZ) == [[[]]] + assert dmp_add([[[ZZ(1)]]], [[[]]], 2, ZZ) == [[[ZZ(1)]]] + assert dmp_add([[[]]], [[[ZZ(1)]]], 2, ZZ) == [[[ZZ(1)]]] + assert dmp_add([[[ZZ(2)]]], [[[ZZ(1)]]], 2, ZZ) == [[[ZZ(3)]]] + assert dmp_add([[[ZZ(1)]]], [[[ZZ(2)]]], 2, ZZ) == [[[ZZ(3)]]] + + assert dmp_add([[[]]], [[[]]], 2, QQ) == [[[]]] + assert dmp_add([[[QQ(1, 2)]]], [[[]]], 2, QQ) == [[[QQ(1, 2)]]] + assert dmp_add([[[]]], [[[QQ(1, 2)]]], 2, QQ) == [[[QQ(1, 2)]]] + assert dmp_add([[[QQ(2, 7)]]], [[[QQ(1, 7)]]], 2, QQ) == [[[QQ(3, 7)]]] + assert dmp_add([[[QQ(1, 7)]]], [[[QQ(2, 7)]]], 2, QQ) == [[[QQ(3, 7)]]] + + +def test_dup_sub(): + assert dup_sub([], [], ZZ) == [] + assert dup_sub([ZZ(1)], [], ZZ) == [ZZ(1)] + assert dup_sub([], [ZZ(1)], ZZ) == [ZZ(-1)] + assert dup_sub([ZZ(1)], [ZZ(1)], ZZ) == [] + assert dup_sub([ZZ(1)], [ZZ(2)], ZZ) == [ZZ(-1)] + + assert dup_sub([ZZ(1), ZZ(2)], [ZZ(1)], ZZ) == [ZZ(1), ZZ(1)] + assert dup_sub([ZZ(1)], [ZZ(1), ZZ(2)], ZZ) == [ZZ(-1), ZZ(-1)] + + assert dup_sub([ZZ(3), ZZ( + 2), ZZ(1)], [ZZ(8), ZZ(9), ZZ(10)], ZZ) == [ZZ(-5), ZZ(-7), ZZ(-9)] + + assert dup_sub([], [], QQ) == [] + assert dup_sub([QQ(1, 2)], [], QQ) == [QQ(1, 2)] + assert dup_sub([], [QQ(1, 2)], QQ) == [QQ(-1, 2)] + assert dup_sub([QQ(1, 3)], [QQ(1, 3)], QQ) == [] + assert dup_sub([QQ(1, 3)], [QQ(2, 3)], QQ) == [QQ(-1, 3)] + + assert dup_sub([QQ(1, 7), QQ(2, 7)], [QQ(1)], QQ) == [QQ(1, 7), QQ(-5, 7)] + assert dup_sub([QQ(1)], [QQ(1, 7), QQ(2, 7)], QQ) == [QQ(-1, 7), QQ(5, 7)] + + assert dup_sub([QQ(3, 7), QQ(2, 7), QQ(1, 7)], [QQ( + 8, 7), QQ(9, 7), QQ(10, 7)], QQ) == [QQ(-5, 7), QQ(-7, 7), QQ(-9, 7)] + + +def test_dmp_sub(): + assert dmp_sub([ZZ(1), ZZ(2)], [ZZ(1)], 0, ZZ) == \ + dup_sub([ZZ(1), ZZ(2)], [ZZ(1)], ZZ) + assert dmp_sub([QQ(1, 2), QQ(2, 3)], [QQ(1)], 0, QQ) == \ + dup_sub([QQ(1, 2), QQ(2, 3)], [QQ(1)], QQ) + + assert dmp_sub([[[]]], [[[]]], 2, ZZ) == [[[]]] + assert dmp_sub([[[ZZ(1)]]], [[[]]], 2, ZZ) == [[[ZZ(1)]]] + assert dmp_sub([[[]]], [[[ZZ(1)]]], 2, ZZ) == [[[ZZ(-1)]]] + assert dmp_sub([[[ZZ(2)]]], [[[ZZ(1)]]], 2, ZZ) == [[[ZZ(1)]]] + assert dmp_sub([[[ZZ(1)]]], [[[ZZ(2)]]], 2, ZZ) == [[[ZZ(-1)]]] + + assert dmp_sub([[[]]], [[[]]], 2, QQ) == [[[]]] + assert dmp_sub([[[QQ(1, 2)]]], [[[]]], 2, QQ) == [[[QQ(1, 2)]]] + assert dmp_sub([[[]]], [[[QQ(1, 2)]]], 2, QQ) == [[[QQ(-1, 2)]]] + assert dmp_sub([[[QQ(2, 7)]]], [[[QQ(1, 7)]]], 2, QQ) == [[[QQ(1, 7)]]] + assert dmp_sub([[[QQ(1, 7)]]], [[[QQ(2, 7)]]], 2, QQ) == [[[QQ(-1, 7)]]] + + +def test_dup_add_mul(): + assert dup_add_mul([ZZ(1), ZZ(2), ZZ(3)], [ZZ(3), ZZ(2), ZZ(1)], + [ZZ(1), ZZ(2)], ZZ) == [ZZ(3), ZZ(9), ZZ(7), ZZ(5)] + assert dmp_add_mul([[ZZ(1), ZZ(2)], [ZZ(3)]], [[ZZ(3)], [ZZ(2), ZZ(1)]], + [[ZZ(1)], [ZZ(2)]], 1, ZZ) == [[ZZ(3)], [ZZ(3), ZZ(9)], [ZZ(4), ZZ(5)]] + + +def test_dup_sub_mul(): + assert dup_sub_mul([ZZ(1), ZZ(2), ZZ(3)], [ZZ(3), ZZ(2), ZZ(1)], + [ZZ(1), ZZ(2)], ZZ) == [ZZ(-3), ZZ(-7), ZZ(-3), ZZ(1)] + assert dmp_sub_mul([[ZZ(1), ZZ(2)], [ZZ(3)]], [[ZZ(3)], [ZZ(2), ZZ(1)]], + [[ZZ(1)], [ZZ(2)]], 1, ZZ) == [[ZZ(-3)], [ZZ(-1), ZZ(-5)], [ZZ(-4), ZZ(1)]] + + +def test_dup_mul(): + assert dup_mul([], [], ZZ) == [] + assert dup_mul([], [ZZ(1)], ZZ) == [] + assert dup_mul([ZZ(1)], [], ZZ) == [] + assert dup_mul([ZZ(1)], [ZZ(1)], ZZ) == [ZZ(1)] + assert dup_mul([ZZ(5)], [ZZ(7)], ZZ) == [ZZ(35)] + + assert dup_mul([], [], QQ) == [] + assert dup_mul([], [QQ(1, 2)], QQ) == [] + assert dup_mul([QQ(1, 2)], [], QQ) == [] + assert dup_mul([QQ(1, 2)], [QQ(4, 7)], QQ) == [QQ(2, 7)] + assert dup_mul([QQ(5, 7)], [QQ(3, 7)], QQ) == [QQ(15, 49)] + + f = dup_normal([3, 0, 0, 6, 1, 2], ZZ) + g = dup_normal([4, 0, 1, 0], ZZ) + h = dup_normal([12, 0, 3, 24, 4, 14, 1, 2, 0], ZZ) + + assert dup_mul(f, g, ZZ) == h + assert dup_mul(g, f, ZZ) == h + + f = dup_normal([2, 0, 0, 1, 7], ZZ) + h = dup_normal([4, 0, 0, 4, 28, 0, 1, 14, 49], ZZ) + + assert dup_mul(f, f, ZZ) == h + + K = FF(6) + + assert dup_mul([K(2), K(1)], [K(3), K(4)], K) == [K(5), K(4)] + + p1 = dup_normal([79, -1, 78, -94, -10, 11, 32, -19, 78, 2, -89, 30, 73, 42, + 85, 77, 83, -30, -34, -2, 95, -81, 37, -49, -46, -58, -16, 37, 35, -11, + -57, -15, -31, 67, -20, 27, 76, 2, 70, 67, -65, 65, -26, -93, -44, -12, + -92, 57, -90, -57, -11, -67, -98, -69, 97, -41, 89, 33, 89, -50, 81, + -31, 60, -27, 43, 29, -77, 44, 21, -91, 32, -57, 33, 3, 53, -51, -38, + -99, -84, 23, -50, 66, -100, 1, -75, -25, 27, -60, 98, -51, -87, 6, 8, + 78, -28, -95, -88, 12, -35, 26, -9, 16, -92, 55, -7, -86, 68, -39, -46, + 84, 94, 45, 60, 92, 68, -75, -74, -19, 8, 75, 78, 91, 57, 34, 14, -3, + -49, 65, 78, -18, 6, -29, -80, -98, 17, 13, 58, 21, 20, 9, 37, 7, -30, + -53, -20, 34, 67, -42, 89, -22, 73, 43, -6, 5, 51, -8, -15, -52, -22, + -58, -72, -3, 43, -92, 82, 83, -2, -13, -23, -60, 16, -94, -8, -28, + -95, -72, 63, -90, 76, 6, -43, -100, -59, 76, 3, 3, 46, -85, 75, 62, + -71, -76, 88, 97, -72, -1, 30, -64, 72, -48, 14, -78, 58, 63, -91, 24, + -87, -27, -80, -100, -44, 98, 70, 100, -29, -38, 11, 77, 100, 52, 86, + 65, -5, -42, -81, -38, -42, 43, -2, -70, -63, -52], ZZ) + p2 = dup_normal([65, -19, -47, 1, 90, 81, -15, -34, 25, -75, 9, -83, 50, -5, + -44, 31, 1, 70, -7, 78, 74, 80, 85, 65, 21, 41, 66, 19, -40, 63, -21, + -27, 32, 69, 83, 34, -35, 14, 81, 57, -75, 32, -67, -89, -100, -61, 46, + 84, -78, -29, -50, -94, -24, -32, -68, -16, 100, -7, -72, -89, 35, 82, + 58, 81, -92, 62, 5, -47, -39, -58, -72, -13, 84, 44, 55, -25, 48, -54, + -31, -56, -11, -50, -84, 10, 67, 17, 13, -14, 61, 76, -64, -44, -40, + -96, 11, -11, -94, 2, 6, 27, -6, 68, -54, 66, -74, -14, -1, -24, -73, + 96, 89, -11, -89, 56, -53, 72, -43, 96, 25, 63, -31, 29, 68, 83, 91, + -93, -19, -38, -40, 40, -12, -19, -79, 44, 100, -66, -29, -77, 62, 39, + -8, 11, -97, 14, 87, 64, 21, -18, 13, 15, -59, -75, -99, -88, 57, 54, + 56, -67, 6, -63, -59, -14, 28, 87, -20, -39, 84, -91, -2, 49, -75, 11, + -24, -95, 36, 66, 5, 25, -72, -40, 86, 90, 37, -33, 57, -35, 29, -18, + 4, -79, 64, -17, -27, 21, 29, -5, -44, -87, -24, 52, 78, 11, -23, -53, + 36, 42, 21, -68, 94, -91, -51, -21, 51, -76, 72, 31, 24, -48, -80, -9, + 37, -47, -6, -8, -63, -91, 79, -79, -100, 38, -20, 38, 100, 83, -90, + 87, 63, -36, 82, -19, 18, -98, -38, 26, 98, -70, 79, 92, 12, 12, 70, + 74, 36, 48, -13, 31, 31, -47, -71, -12, -64, 36, -42, 32, -86, 60, 83, + 70, 55, 0, 1, 29, -35, 8, -82, 8, -73, -46, -50, 43, 48, -5, -86, -72, + 44, -90, 19, 19, 5, -20, 97, -13, -66, -5, 5, -69, 64, -30, 41, 51, 36, + 13, -99, -61, 94, -12, 74, 98, 68, 24, 46, -97, -87, -6, -27, 82, 62, + -11, -77, 86, 66, -47, -49, -50, 13, 18, 89, -89, 46, -80, 13, 98, -35, + -36, -25, 12, 20, 26, -52, 79, 27, 79, 100, 8, 62, -58, -28, 37], ZZ) + res = dup_normal([5135, -1566, 1376, -7466, 4579, 11710, 8001, -7183, + -3737, -7439, 345, -10084, 24522, -1201, 1070, -10245, 9582, 9264, + 1903, 23312, 18953, 10037, -15268, -5450, 6442, -6243, -3777, 5110, + 10936, -16649, -6022, 16255, 31300, 24818, 31922, 32760, 7854, 27080, + 15766, 29596, 7139, 31945, -19810, 465, -38026, -3971, 9641, 465, + -19375, 5524, -30112, -11960, -12813, 13535, 30670, 5925, -43725, + -14089, 11503, -22782, 6371, 43881, 37465, -33529, -33590, -39798, + -37854, -18466, -7908, -35825, -26020, -36923, -11332, -5699, 25166, + -3147, 19885, 12962, -20659, -1642, 27723, -56331, -24580, -11010, + -20206, 20087, -23772, -16038, 38580, 20901, -50731, 32037, -4299, + 26508, 18038, -28357, 31846, -7405, -20172, -15894, 2096, 25110, + -45786, 45918, -55333, -31928, -49428, -29824, -58796, -24609, -15408, + 69, -35415, -18439, 10123, -20360, -65949, 33356, -20333, 26476, + -32073, 33621, 930, 28803, -42791, 44716, 38164, 12302, -1739, 11421, + 73385, -7613, 14297, 38155, -414, 77587, 24338, -21415, 29367, 42639, + 13901, -288, 51027, -11827, 91260, 43407, 88521, -15186, 70572, -12049, + 5090, -12208, -56374, 15520, -623, -7742, 50825, 11199, -14894, 40892, + 59591, -31356, -28696, -57842, -87751, -33744, -28436, -28945, -40287, + 37957, -35638, 33401, -61534, 14870, 40292, 70366, -10803, 102290, + -71719, -85251, 7902, -22409, 75009, 99927, 35298, -1175, -762, -34744, + -10587, -47574, -62629, -19581, -43659, -54369, -32250, -39545, 15225, + -24454, 11241, -67308, -30148, 39929, 37639, 14383, -73475, -77636, + -81048, -35992, 41601, -90143, 76937, -8112, 56588, 9124, -40094, + -32340, 13253, 10898, -51639, 36390, 12086, -1885, 100714, -28561, + -23784, -18735, 18916, 16286, 10742, -87360, -13697, 10689, -19477, + -29770, 5060, 20189, -8297, 112407, 47071, 47743, 45519, -4109, 17468, + -68831, 78325, -6481, -21641, -19459, 30919, 96115, 8607, 53341, 32105, + -16211, 23538, 57259, -76272, -40583, 62093, 38511, -34255, -40665, + -40604, -37606, -15274, 33156, -13885, 103636, 118678, -14101, -92682, + -100791, 2634, 63791, 98266, 19286, -34590, -21067, -71130, 25380, + -40839, -27614, -26060, 52358, -15537, 27138, -6749, 36269, -33306, + 13207, -91084, -5540, -57116, 69548, 44169, -57742, -41234, -103327, + -62904, -8566, 41149, -12866, 71188, 23980, 1838, 58230, 73950, 5594, + 43113, -8159, -15925, 6911, 85598, -75016, -16214, -62726, -39016, + 8618, -63882, -4299, 23182, 49959, 49342, -3238, -24913, -37138, 78361, + 32451, 6337, -11438, -36241, -37737, 8169, -3077, -24829, 57953, 53016, + -31511, -91168, 12599, -41849, 41576, 55275, -62539, 47814, -62319, + 12300, -32076, -55137, -84881, -27546, 4312, -3433, -54382, 113288, + -30157, 74469, 18219, 79880, -2124, 98911, 17655, -33499, -32861, + 47242, -37393, 99765, 14831, -44483, 10800, -31617, -52710, 37406, + 22105, 29704, -20050, 13778, 43683, 36628, 8494, 60964, -22644, 31550, + -17693, 33805, -124879, -12302, 19343, 20400, -30937, -21574, -34037, + -33380, 56539, -24993, -75513, -1527, 53563, 65407, -101, 53577, 37991, + 18717, -23795, -8090, -47987, -94717, 41967, 5170, -14815, -94311, + 17896, -17734, -57718, -774, -38410, 24830, 29682, 76480, 58802, + -46416, -20348, -61353, -68225, -68306, 23822, -31598, 42972, 36327, + 28968, -65638, -21638, 24354, -8356, 26777, 52982, -11783, -44051, + -26467, -44721, -28435, -53265, -25574, -2669, 44155, 22946, -18454, + -30718, -11252, 58420, 8711, 67447, 4425, 41749, 67543, 43162, 11793, + -41907, 20477, -13080, 6559, -6104, -13244, 42853, 42935, 29793, 36730, + -28087, 28657, 17946, 7503, 7204, 21491, -27450, -24241, -98156, + -18082, -42613, -24928, 10775, -14842, -44127, 55910, 14777, 31151, -2194, + 39206, -2100, -4211, 11827, -8918, -19471, 72567, 36447, -65590, -34861, + -17147, -45303, 9025, -7333, -35473, 11101, 11638, 3441, 6626, -41800, + 9416, 13679, 33508, 40502, -60542, 16358, 8392, -43242, -35864, -34127, + -48721, 35878, 30598, 28630, 20279, -19983, -14638, -24455, -1851, -11344, + 45150, 42051, 26034, -28889, -32382, -3527, -14532, 22564, -22346, 477, + 11706, 28338, -25972, -9185, -22867, -12522, 32120, -4424, 11339, -33913, + -7184, 5101, -23552, -17115, -31401, -6104, 21906, 25708, 8406, 6317, + -7525, 5014, 20750, 20179, 22724, 11692, 13297, 2493, -253, -16841, -17339, + -6753, -4808, 2976, -10881, -10228, -13816, -12686, 1385, 2316, 2190, -875, + -1924], ZZ) + + assert dup_mul(p1, p2, ZZ) == res + + p1 = dup_normal([83, -61, -86, -24, 12, 43, -88, -9, 42, 55, -66, 74, 95, + -25, -12, 68, -99, 4, 45, 6, -15, -19, 78, 65, -55, 47, -13, 17, 86, + 81, -58, -27, 50, -40, -24, 39, -41, -92, 75, 90, -1, 40, -15, -27, + -35, 68, 70, -64, -40, 78, -88, -58, -39, 69, 46, 12, 28, -94, -37, + -50, -80, -96, -61, 25, 1, 71, 4, 12, 48, 4, 34, -47, -75, 5, 48, 82, + 88, 23, 98, 35, 17, -10, 48, -61, -95, 47, 65, -19, -66, -57, -6, -51, + -42, -89, 66, -13, 18, 37, 90, -23, 72, 96, -53, 0, 40, -73, -52, -68, + 32, -25, -53, 79, -52, 18, 44, 73, -81, 31, -90, 70, 3, 36, 48, 76, + -24, -44, 23, 98, -4, 73, 69, 88, -70, 14, -68, 94, -78, -15, -64, -97, + -70, -35, 65, 88, 49, -53, -7, 12, -45, -7, 59, -94, 99, -2, 67, -60, + -71, 29, -62, -77, 1, 51, 17, 80, -20, -47, -19, 24, -9, 39, -23, 21, + -84, 10, 84, 56, -17, -21, -66, 85, 70, 46, -51, -22, -95, 78, -60, + -96, -97, -45, 72, 35, 30, -61, -92, -93, -60, -61, 4, -4, -81, -73, + 46, 53, -11, 26, 94, 45, 14, -78, 55, 84, -68, 98, 60, 23, 100, -63, + 68, 96, -16, 3, 56, 21, -58, 62, -67, 66, 85, 41, -79, -22, 97, -67, + 82, 82, -96, -20, -7, 48, -67, 48, -9, -39, 78], ZZ) + p2 = dup_normal([52, 88, 76, 66, 9, -64, 46, -20, -28, 69, 60, 96, -36, + -92, -30, -11, -35, 35, 55, 63, -92, -7, 25, -58, 74, 55, -6, 4, 47, + -92, -65, 67, -45, 74, -76, 59, -6, 69, 39, 24, -71, -7, 39, -45, 60, + -68, 98, 97, -79, 17, 4, 94, -64, 68, -100, -96, -2, 3, 22, 96, 54, + -77, -86, 67, 6, 57, 37, 40, 89, -78, 64, -94, -45, -92, 57, 87, -26, + 36, 19, 97, 25, 77, -87, 24, 43, -5, 35, 57, 83, 71, 35, 63, 61, 96, + -22, 8, -1, 96, 43, 45, 94, -93, 36, 71, -41, -99, 85, -48, 59, 52, + -17, 5, 87, -16, -68, -54, 76, -18, 100, 91, -42, -70, -66, -88, -12, + 1, 95, -82, 52, 43, -29, 3, 12, 72, -99, -43, -32, -93, -51, 16, -20, + -12, -11, 5, 33, -38, 93, -5, -74, 25, 74, -58, 93, 59, -63, -86, 63, + -20, -4, -74, -73, -95, 29, -28, 93, -91, -2, -38, -62, 77, -58, -85, + -28, 95, 38, 19, -69, 86, 94, 25, -2, -4, 47, 34, -59, 35, -48, 29, + -63, -53, 34, 29, 66, 73, 6, 92, -84, 89, 15, 81, 93, 97, 51, -72, -78, + 25, 60, 90, -45, 39, 67, -84, -62, 57, 26, -32, -56, -14, -83, 76, 5, + -2, 99, -100, 28, 46, 94, -7, 53, -25, 16, -23, -36, 89, -78, -63, 31, + 1, 84, -99, -52, 76, 48, 90, -76, 44, -19, 54, -36, -9, -73, -100, -69, + 31, 42, 25, -39, 76, -26, -8, -14, 51, 3, 37, 45, 2, -54, 13, -34, -92, + 17, -25, -65, 53, -63, 30, 4, -70, -67, 90, 52, 51, 18, -3, 31, -45, + -9, 59, 63, -87, 22, -32, 29, -38, 21, 36, -82, 27, -11], ZZ) + res = dup_normal([4316, 4132, -3532, -7974, -11303, -10069, 5484, -3330, + -5874, 7734, 4673, 11327, -9884, -8031, 17343, 21035, -10570, -9285, + 15893, 3780, -14083, 8819, 17592, 10159, 7174, -11587, 8598, -16479, + 3602, 25596, 9781, 12163, 150, 18749, -21782, -12307, 27578, -2757, + -12573, 12565, 6345, -18956, 19503, -15617, 1443, -16778, 36851, 23588, + -28474, 5749, 40695, -7521, -53669, -2497, -18530, 6770, 57038, 3926, + -6927, -15399, 1848, -64649, -27728, 3644, 49608, 15187, -8902, -9480, + -7398, -40425, 4824, 23767, -7594, -6905, 33089, 18786, 12192, 24670, + 31114, 35334, -4501, -14676, 7107, -59018, -21352, 20777, 19661, 20653, + 33754, -885, -43758, 6269, 51897, -28719, -97488, -9527, 13746, 11644, + 17644, -21720, 23782, -10481, 47867, 20752, 33810, -1875, 39918, -7710, + -40840, 19808, -47075, 23066, 46616, 25201, 9287, 35436, -1602, 9645, + -11978, 13273, 15544, 33465, 20063, 44539, 11687, 27314, -6538, -37467, + 14031, 32970, -27086, 41323, 29551, 65910, -39027, -37800, -22232, + 8212, 46316, -28981, -55282, 50417, -44929, -44062, 73879, 37573, + -2596, -10877, -21893, -133218, -33707, -25753, -9531, 17530, 61126, + 2748, -56235, 43874, -10872, -90459, -30387, 115267, -7264, -44452, + 122626, 14839, -599, 10337, 57166, -67467, -54957, 63669, 1202, 18488, + 52594, 7205, -97822, 612, 78069, -5403, -63562, 47236, 36873, -154827, + -26188, 82427, -39521, 5628, 7416, 5276, -53095, 47050, 26121, -42207, + 79021, -13035, 2499, -66943, 29040, -72355, -23480, 23416, -12885, + -44225, -42688, -4224, 19858, 55299, 15735, 11465, 101876, -39169, + 51786, 14723, 43280, -68697, 16410, 92295, 56767, 7183, 111850, 4550, + 115451, -38443, -19642, -35058, 10230, 93829, 8925, 63047, 3146, 29250, + 8530, 5255, -98117, -115517, -76817, -8724, 41044, 1312, -35974, 79333, + -28567, 7547, -10580, -24559, -16238, 10794, -3867, 24848, 57770, + -51536, -35040, 71033, 29853, 62029, -7125, -125585, -32169, -47907, + 156811, -65176, -58006, -15757, -57861, 11963, 30225, -41901, -41681, + 31310, 27982, 18613, 61760, 60746, -59096, 33499, 30097, -17997, 24032, + 56442, -83042, 23747, -20931, -21978, -158752, -9883, -73598, -7987, + -7333, -125403, -116329, 30585, 53281, 51018, -29193, 88575, 8264, + -40147, -16289, 113088, 12810, -6508, 101552, -13037, 34440, -41840, + 101643, 24263, 80532, 61748, 65574, 6423, -20672, 6591, -10834, -71716, + 86919, -92626, 39161, 28490, 81319, 46676, 106720, 43530, 26998, 57456, + -8862, 60989, 13982, 3119, -2224, 14743, 55415, -49093, -29303, 28999, + 1789, 55953, -84043, -7780, -65013, 57129, -47251, 61484, 61994, + -78361, -82778, 22487, -26894, 9756, -74637, -15519, -4360, 30115, + 42433, 35475, 15286, 69768, 21509, -20214, 78675, -21163, 13596, 11443, + -10698, -53621, -53867, -24155, 64500, -42784, -33077, -16500, 873, + -52788, 14546, -38011, 36974, -39849, -34029, -94311, 83068, -50437, + -26169, -46746, 59185, 42259, -101379, -12943, 30089, -59086, 36271, + 22723, -30253, -52472, -70826, -23289, 3331, -31687, 14183, -857, + -28627, 35246, -51284, 5636, -6933, 66539, 36654, 50927, 24783, 3457, + 33276, 45281, 45650, -4938, -9968, -22590, 47995, 69229, 5214, -58365, + -17907, -14651, 18668, 18009, 12649, -11851, -13387, 20339, 52472, + -1087, -21458, -68647, 52295, 15849, 40608, 15323, 25164, -29368, + 10352, -7055, 7159, 21695, -5373, -54849, 101103, -24963, -10511, + 33227, 7659, 41042, -69588, 26718, -20515, 6441, 38135, -63, 24088, + -35364, -12785, -18709, 47843, 48533, -48575, 17251, -19394, 32878, + -9010, -9050, 504, -12407, 28076, -3429, 25324, -4210, -26119, 752, + -29203, 28251, -11324, -32140, -3366, -25135, 18702, -31588, -7047, + -24267, 49987, -14975, -33169, 37744, -7720, -9035, 16964, -2807, -421, + 14114, -17097, -13662, 40628, -12139, -9427, 5369, 17551, -13232, -16211, + 9804, -7422, 2677, 28635, -8280, -4906, 2908, -22558, 5604, 12459, 8756, + -3980, -4745, -18525, 7913, 5970, -16457, 20230, -6247, -13812, 2505, + 11899, 1409, -15094, 22540, -18863, 137, 11123, -4516, 2290, -8594, 12150, + -10380, 3005, 5235, -7350, 2535, -858], ZZ) + + assert dup_mul(p1, p2, ZZ) == res + + +def test_dmp_mul(): + assert dmp_mul([ZZ(5)], [ZZ(7)], 0, ZZ) == \ + dup_mul([ZZ(5)], [ZZ(7)], ZZ) + assert dmp_mul([QQ(5, 7)], [QQ(3, 7)], 0, QQ) == \ + dup_mul([QQ(5, 7)], [QQ(3, 7)], QQ) + + assert dmp_mul([[[]]], [[[]]], 2, ZZ) == [[[]]] + assert dmp_mul([[[ZZ(1)]]], [[[]]], 2, ZZ) == [[[]]] + assert dmp_mul([[[]]], [[[ZZ(1)]]], 2, ZZ) == [[[]]] + assert dmp_mul([[[ZZ(2)]]], [[[ZZ(1)]]], 2, ZZ) == [[[ZZ(2)]]] + assert dmp_mul([[[ZZ(1)]]], [[[ZZ(2)]]], 2, ZZ) == [[[ZZ(2)]]] + + assert dmp_mul([[[]]], [[[]]], 2, QQ) == [[[]]] + assert dmp_mul([[[QQ(1, 2)]]], [[[]]], 2, QQ) == [[[]]] + assert dmp_mul([[[]]], [[[QQ(1, 2)]]], 2, QQ) == [[[]]] + assert dmp_mul([[[QQ(2, 7)]]], [[[QQ(1, 3)]]], 2, QQ) == [[[QQ(2, 21)]]] + assert dmp_mul([[[QQ(1, 7)]]], [[[QQ(2, 3)]]], 2, QQ) == [[[QQ(2, 21)]]] + + K = FF(6) + + assert dmp_mul( + [[K(2)], [K(1)]], [[K(3)], [K(4)]], 1, K) == [[K(5)], [K(4)]] + + +def test_dup_sqr(): + assert dup_sqr([], ZZ) == [] + assert dup_sqr([ZZ(2)], ZZ) == [ZZ(4)] + assert dup_sqr([ZZ(1), ZZ(2)], ZZ) == [ZZ(1), ZZ(4), ZZ(4)] + + assert dup_sqr([], QQ) == [] + assert dup_sqr([QQ(2, 3)], QQ) == [QQ(4, 9)] + assert dup_sqr([QQ(1, 3), QQ(2, 3)], QQ) == [QQ(1, 9), QQ(4, 9), QQ(4, 9)] + + f = dup_normal([2, 0, 0, 1, 7], ZZ) + + assert dup_sqr(f, ZZ) == dup_normal([4, 0, 0, 4, 28, 0, 1, 14, 49], ZZ) + + K = FF(9) + + assert dup_sqr([K(3), K(4)], K) == [K(6), K(7)] + + +def test_dmp_sqr(): + assert dmp_sqr([ZZ(1), ZZ(2)], 0, ZZ) == \ + dup_sqr([ZZ(1), ZZ(2)], ZZ) + + assert dmp_sqr([[[]]], 2, ZZ) == [[[]]] + assert dmp_sqr([[[ZZ(2)]]], 2, ZZ) == [[[ZZ(4)]]] + + assert dmp_sqr([[[]]], 2, QQ) == [[[]]] + assert dmp_sqr([[[QQ(2, 3)]]], 2, QQ) == [[[QQ(4, 9)]]] + + K = FF(9) + + assert dmp_sqr([[K(3)], [K(4)]], 1, K) == [[K(6)], [K(7)]] + + +def test_dup_pow(): + assert dup_pow([], 0, ZZ) == [ZZ(1)] + assert dup_pow([], 0, QQ) == [QQ(1)] + + assert dup_pow([], 1, ZZ) == [] + assert dup_pow([], 7, ZZ) == [] + + assert dup_pow([ZZ(1)], 0, ZZ) == [ZZ(1)] + assert dup_pow([ZZ(1)], 1, ZZ) == [ZZ(1)] + assert dup_pow([ZZ(1)], 7, ZZ) == [ZZ(1)] + + assert dup_pow([ZZ(3)], 0, ZZ) == [ZZ(1)] + assert dup_pow([ZZ(3)], 1, ZZ) == [ZZ(3)] + assert dup_pow([ZZ(3)], 7, ZZ) == [ZZ(2187)] + + assert dup_pow([QQ(1, 1)], 0, QQ) == [QQ(1, 1)] + assert dup_pow([QQ(1, 1)], 1, QQ) == [QQ(1, 1)] + assert dup_pow([QQ(1, 1)], 7, QQ) == [QQ(1, 1)] + + assert dup_pow([QQ(3, 7)], 0, QQ) == [QQ(1, 1)] + assert dup_pow([QQ(3, 7)], 1, QQ) == [QQ(3, 7)] + assert dup_pow([QQ(3, 7)], 7, QQ) == [QQ(2187, 823543)] + + f = dup_normal([2, 0, 0, 1, 7], ZZ) + + assert dup_pow(f, 0, ZZ) == dup_normal([1], ZZ) + assert dup_pow(f, 1, ZZ) == dup_normal([2, 0, 0, 1, 7], ZZ) + assert dup_pow(f, 2, ZZ) == dup_normal([4, 0, 0, 4, 28, 0, 1, 14, 49], ZZ) + assert dup_pow(f, 3, ZZ) == dup_normal( + [8, 0, 0, 12, 84, 0, 6, 84, 294, 1, 21, 147, 343], ZZ) + + +def test_dmp_pow(): + assert dmp_pow([[]], 0, 1, ZZ) == [[ZZ(1)]] + assert dmp_pow([[]], 0, 1, QQ) == [[QQ(1)]] + + assert dmp_pow([[]], 1, 1, ZZ) == [[]] + assert dmp_pow([[]], 7, 1, ZZ) == [[]] + + assert dmp_pow([[ZZ(1)]], 0, 1, ZZ) == [[ZZ(1)]] + assert dmp_pow([[ZZ(1)]], 1, 1, ZZ) == [[ZZ(1)]] + assert dmp_pow([[ZZ(1)]], 7, 1, ZZ) == [[ZZ(1)]] + + assert dmp_pow([[QQ(3, 7)]], 0, 1, QQ) == [[QQ(1, 1)]] + assert dmp_pow([[QQ(3, 7)]], 1, 1, QQ) == [[QQ(3, 7)]] + assert dmp_pow([[QQ(3, 7)]], 7, 1, QQ) == [[QQ(2187, 823543)]] + + f = dup_normal([2, 0, 0, 1, 7], ZZ) + + assert dmp_pow(f, 2, 0, ZZ) == dup_pow(f, 2, ZZ) + + +def test_dup_pdiv(): + f = dup_normal([3, 1, 1, 5], ZZ) + g = dup_normal([5, -3, 1], ZZ) + + q = dup_normal([15, 14], ZZ) + r = dup_normal([52, 111], ZZ) + + assert dup_pdiv(f, g, ZZ) == (q, r) + assert dup_pquo(f, g, ZZ) == q + assert dup_prem(f, g, ZZ) == r + + raises(ExactQuotientFailed, lambda: dup_pexquo(f, g, ZZ)) + + f = dup_normal([3, 1, 1, 5], QQ) + g = dup_normal([5, -3, 1], QQ) + + q = dup_normal([15, 14], QQ) + r = dup_normal([52, 111], QQ) + + assert dup_pdiv(f, g, QQ) == (q, r) + assert dup_pquo(f, g, QQ) == q + assert dup_prem(f, g, QQ) == r + + raises(ExactQuotientFailed, lambda: dup_pexquo(f, g, QQ)) + + +def test_dmp_pdiv(): + f = dmp_normal([[1], [], [1, 0, 0]], 1, ZZ) + g = dmp_normal([[1], [-1, 0]], 1, ZZ) + + q = dmp_normal([[1], [1, 0]], 1, ZZ) + r = dmp_normal([[2, 0, 0]], 1, ZZ) + + assert dmp_pdiv(f, g, 1, ZZ) == (q, r) + assert dmp_pquo(f, g, 1, ZZ) == q + assert dmp_prem(f, g, 1, ZZ) == r + + raises(ExactQuotientFailed, lambda: dmp_pexquo(f, g, 1, ZZ)) + + f = dmp_normal([[1], [], [1, 0, 0]], 1, ZZ) + g = dmp_normal([[2], [-2, 0]], 1, ZZ) + + q = dmp_normal([[2], [2, 0]], 1, ZZ) + r = dmp_normal([[8, 0, 0]], 1, ZZ) + + assert dmp_pdiv(f, g, 1, ZZ) == (q, r) + assert dmp_pquo(f, g, 1, ZZ) == q + assert dmp_prem(f, g, 1, ZZ) == r + + raises(ExactQuotientFailed, lambda: dmp_pexquo(f, g, 1, ZZ)) + + +def test_dup_rr_div(): + raises(ZeroDivisionError, lambda: dup_rr_div([1, 2, 3], [], ZZ)) + + f = dup_normal([3, 1, 1, 5], ZZ) + g = dup_normal([5, -3, 1], ZZ) + + q, r = [], f + + assert dup_rr_div(f, g, ZZ) == (q, r) + + +def test_dmp_rr_div(): + raises(ZeroDivisionError, lambda: dmp_rr_div([[1, 2], [3]], [[]], 1, ZZ)) + + f = dmp_normal([[1], [], [1, 0, 0]], 1, ZZ) + g = dmp_normal([[1], [-1, 0]], 1, ZZ) + + q = dmp_normal([[1], [1, 0]], 1, ZZ) + r = dmp_normal([[2, 0, 0]], 1, ZZ) + + assert dmp_rr_div(f, g, 1, ZZ) == (q, r) + + f = dmp_normal([[1], [], [1, 0, 0]], 1, ZZ) + g = dmp_normal([[-1], [1, 0]], 1, ZZ) + + q = dmp_normal([[-1], [-1, 0]], 1, ZZ) + r = dmp_normal([[2, 0, 0]], 1, ZZ) + + assert dmp_rr_div(f, g, 1, ZZ) == (q, r) + + f = dmp_normal([[1], [], [1, 0, 0]], 1, ZZ) + g = dmp_normal([[2], [-2, 0]], 1, ZZ) + + q, r = [[]], f + + assert dmp_rr_div(f, g, 1, ZZ) == (q, r) + + +def test_dup_ff_div(): + raises(ZeroDivisionError, lambda: dup_ff_div([1, 2, 3], [], QQ)) + + f = dup_normal([3, 1, 1, 5], QQ) + g = dup_normal([5, -3, 1], QQ) + + q = [QQ(3, 5), QQ(14, 25)] + r = [QQ(52, 25), QQ(111, 25)] + + assert dup_ff_div(f, g, QQ) == (q, r) + +def test_dup_ff_div_gmpy2(): + if GROUND_TYPES != 'gmpy2': + return + + from gmpy2 import mpq + from sympy.polys.domains import GMPYRationalField + K = GMPYRationalField() + + f = [mpq(1,3), mpq(3,2)] + g = [mpq(2,1)] + assert dmp_ff_div(f, g, 0, K) == ([mpq(1,6), mpq(3,4)], []) + + f = [mpq(1,2), mpq(1,3), mpq(1,4), mpq(1,5)] + g = [mpq(-1,1), mpq(1,1), mpq(-1,1)] + assert dmp_ff_div(f, g, 0, K) == ([mpq(-1,2), mpq(-5,6)], [mpq(7,12), mpq(-19,30)]) + +def test_dmp_ff_div(): + raises(ZeroDivisionError, lambda: dmp_ff_div([[1, 2], [3]], [[]], 1, QQ)) + + f = dmp_normal([[1], [], [1, 0, 0]], 1, QQ) + g = dmp_normal([[1], [-1, 0]], 1, QQ) + + q = [[QQ(1, 1)], [QQ(1, 1), QQ(0, 1)]] + r = [[QQ(2, 1), QQ(0, 1), QQ(0, 1)]] + + assert dmp_ff_div(f, g, 1, QQ) == (q, r) + + f = dmp_normal([[1], [], [1, 0, 0]], 1, QQ) + g = dmp_normal([[-1], [1, 0]], 1, QQ) + + q = [[QQ(-1, 1)], [QQ(-1, 1), QQ(0, 1)]] + r = [[QQ(2, 1), QQ(0, 1), QQ(0, 1)]] + + assert dmp_ff_div(f, g, 1, QQ) == (q, r) + + f = dmp_normal([[1], [], [1, 0, 0]], 1, QQ) + g = dmp_normal([[2], [-2, 0]], 1, QQ) + + q = [[QQ(1, 2)], [QQ(1, 2), QQ(0, 1)]] + r = [[QQ(2, 1), QQ(0, 1), QQ(0, 1)]] + + assert dmp_ff_div(f, g, 1, QQ) == (q, r) + + +def test_dup_div(): + f, g, q, r = [5, 4, 3, 2, 1], [1, 2, 3], [5, -6, 0], [20, 1] + + assert dup_div(f, g, ZZ) == (q, r) + assert dup_quo(f, g, ZZ) == q + assert dup_rem(f, g, ZZ) == r + + raises(ExactQuotientFailed, lambda: dup_exquo(f, g, ZZ)) + + f, g, q, r = [5, 4, 3, 2, 1, 0], [1, 2, 0, 0, 9], [5, -6], [15, 2, -44, 54] + + assert dup_div(f, g, ZZ) == (q, r) + assert dup_quo(f, g, ZZ) == q + assert dup_rem(f, g, ZZ) == r + + raises(ExactQuotientFailed, lambda: dup_exquo(f, g, ZZ)) + + +def test_dmp_div(): + f, g, q, r = [5, 4, 3, 2, 1], [1, 2, 3], [5, -6, 0], [20, 1] + + assert dmp_div(f, g, 0, ZZ) == (q, r) + assert dmp_quo(f, g, 0, ZZ) == q + assert dmp_rem(f, g, 0, ZZ) == r + + raises(ExactQuotientFailed, lambda: dmp_exquo(f, g, 0, ZZ)) + + f, g, q, r = [[[1]]], [[[2]], [1]], [[[]]], [[[1]]] + + assert dmp_div(f, g, 2, ZZ) == (q, r) + assert dmp_quo(f, g, 2, ZZ) == q + assert dmp_rem(f, g, 2, ZZ) == r + + raises(ExactQuotientFailed, lambda: dmp_exquo(f, g, 2, ZZ)) + + +def test_dup_max_norm(): + assert dup_max_norm([], ZZ) == 0 + assert dup_max_norm([1], ZZ) == 1 + + assert dup_max_norm([1, 4, 2, 3], ZZ) == 4 + + +def test_dmp_max_norm(): + assert dmp_max_norm([[[]]], 2, ZZ) == 0 + assert dmp_max_norm([[[1]]], 2, ZZ) == 1 + + assert dmp_max_norm(f_0, 2, ZZ) == 6 + + +def test_dup_l1_norm(): + assert dup_l1_norm([], ZZ) == 0 + assert dup_l1_norm([1], ZZ) == 1 + assert dup_l1_norm([1, 4, 2, 3], ZZ) == 10 + + +def test_dmp_l1_norm(): + assert dmp_l1_norm([[[]]], 2, ZZ) == 0 + assert dmp_l1_norm([[[1]]], 2, ZZ) == 1 + + assert dmp_l1_norm(f_0, 2, ZZ) == 31 + + +def test_dup_l2_norm_squared(): + assert dup_l2_norm_squared([], ZZ) == 0 + assert dup_l2_norm_squared([1], ZZ) == 1 + assert dup_l2_norm_squared([1, 4, 2, 3], ZZ) == 30 + + +def test_dmp_l2_norm_squared(): + assert dmp_l2_norm_squared([[[]]], 2, ZZ) == 0 + assert dmp_l2_norm_squared([[[1]]], 2, ZZ) == 1 + assert dmp_l2_norm_squared(f_0, 2, ZZ) == 111 + + +def test_dup_expand(): + assert dup_expand((), ZZ) == [1] + assert dup_expand(([1, 2, 3], [1, 2], [7, 5, 4, 3]), ZZ) == \ + dup_mul([1, 2, 3], dup_mul([1, 2], [7, 5, 4, 3], ZZ), ZZ) + + +def test_dmp_expand(): + assert dmp_expand((), 1, ZZ) == [[1]] + assert dmp_expand(([[1], [2], [3]], [[1], [2]], [[7], [5], [4], [3]]), 1, ZZ) == \ + dmp_mul([[1], [2], [3]], dmp_mul([[1], [2]], [[7], [5], [ + 4], [3]], 1, ZZ), 1, ZZ) diff --git a/lib/python3.12/site-packages/sympy/polys/tests/test_densebasic.py b/lib/python3.12/site-packages/sympy/polys/tests/test_densebasic.py new file mode 100644 index 0000000000000000000000000000000000000000..43386d86d0e6ec7b20d3962d8063aa6402165f9a --- /dev/null +++ b/lib/python3.12/site-packages/sympy/polys/tests/test_densebasic.py @@ -0,0 +1,730 @@ +"""Tests for dense recursive polynomials' basic tools. """ + +from sympy.polys.densebasic import ( + ninf, + dup_LC, dmp_LC, + dup_TC, dmp_TC, + dmp_ground_LC, dmp_ground_TC, + dmp_true_LT, + dup_degree, dmp_degree, + dmp_degree_in, dmp_degree_list, + dup_strip, dmp_strip, + dmp_validate, + dup_reverse, + dup_copy, dmp_copy, + dup_normal, dmp_normal, + dup_convert, dmp_convert, + dup_from_sympy, dmp_from_sympy, + dup_nth, dmp_nth, dmp_ground_nth, + dmp_zero_p, dmp_zero, + dmp_one_p, dmp_one, + dmp_ground_p, dmp_ground, + dmp_negative_p, dmp_positive_p, + dmp_zeros, dmp_grounds, + dup_from_dict, dup_from_raw_dict, + dup_to_dict, dup_to_raw_dict, + dmp_from_dict, dmp_to_dict, + dmp_swap, dmp_permute, + dmp_nest, dmp_raise, + dup_deflate, dmp_deflate, + dup_multi_deflate, dmp_multi_deflate, + dup_inflate, dmp_inflate, + dmp_exclude, dmp_include, + dmp_inject, dmp_eject, + dup_terms_gcd, dmp_terms_gcd, + dmp_list_terms, dmp_apply_pairs, + dup_slice, + dup_random, +) + +from sympy.polys.specialpolys import f_polys +from sympy.polys.domains import ZZ, QQ +from sympy.polys.rings import ring + +from sympy.core.singleton import S +from sympy.testing.pytest import raises + +from sympy.core.numbers import oo + +f_0, f_1, f_2, f_3, f_4, f_5, f_6 = [ f.to_dense() for f in f_polys() ] + +def test_dup_LC(): + assert dup_LC([], ZZ) == 0 + assert dup_LC([2, 3, 4, 5], ZZ) == 2 + + +def test_dup_TC(): + assert dup_TC([], ZZ) == 0 + assert dup_TC([2, 3, 4, 5], ZZ) == 5 + + +def test_dmp_LC(): + assert dmp_LC([[]], ZZ) == [] + assert dmp_LC([[2, 3, 4], [5]], ZZ) == [2, 3, 4] + assert dmp_LC([[[]]], ZZ) == [[]] + assert dmp_LC([[[2], [3, 4]], [[5]]], ZZ) == [[2], [3, 4]] + + +def test_dmp_TC(): + assert dmp_TC([[]], ZZ) == [] + assert dmp_TC([[2, 3, 4], [5]], ZZ) == [5] + assert dmp_TC([[[]]], ZZ) == [[]] + assert dmp_TC([[[2], [3, 4]], [[5]]], ZZ) == [[5]] + + +def test_dmp_ground_LC(): + assert dmp_ground_LC([[]], 1, ZZ) == 0 + assert dmp_ground_LC([[2, 3, 4], [5]], 1, ZZ) == 2 + assert dmp_ground_LC([[[]]], 2, ZZ) == 0 + assert dmp_ground_LC([[[2], [3, 4]], [[5]]], 2, ZZ) == 2 + + +def test_dmp_ground_TC(): + assert dmp_ground_TC([[]], 1, ZZ) == 0 + assert dmp_ground_TC([[2, 3, 4], [5]], 1, ZZ) == 5 + assert dmp_ground_TC([[[]]], 2, ZZ) == 0 + assert dmp_ground_TC([[[2], [3, 4]], [[5]]], 2, ZZ) == 5 + + +def test_dmp_true_LT(): + assert dmp_true_LT([[]], 1, ZZ) == ((0, 0), 0) + assert dmp_true_LT([[7]], 1, ZZ) == ((0, 0), 7) + + assert dmp_true_LT([[1, 0]], 1, ZZ) == ((0, 1), 1) + assert dmp_true_LT([[1], []], 1, ZZ) == ((1, 0), 1) + assert dmp_true_LT([[1, 0], []], 1, ZZ) == ((1, 1), 1) + + +def test_dup_degree(): + assert ninf == float('-inf') + assert dup_degree([]) is ninf + assert dup_degree([1]) == 0 + assert dup_degree([1, 0]) == 1 + assert dup_degree([1, 0, 0, 0, 1]) == 4 + + +def test_dmp_degree(): + assert dmp_degree([[]], 1) is ninf + assert dmp_degree([[[]]], 2) is ninf + + assert dmp_degree([[1]], 1) == 0 + assert dmp_degree([[2], [1]], 1) == 1 + + +def test_dmp_degree_in(): + assert dmp_degree_in([[[]]], 0, 2) is ninf + assert dmp_degree_in([[[]]], 1, 2) is ninf + assert dmp_degree_in([[[]]], 2, 2) is ninf + + assert dmp_degree_in([[[1]]], 0, 2) == 0 + assert dmp_degree_in([[[1]]], 1, 2) == 0 + assert dmp_degree_in([[[1]]], 2, 2) == 0 + + assert dmp_degree_in(f_4, 0, 2) == 9 + assert dmp_degree_in(f_4, 1, 2) == 12 + assert dmp_degree_in(f_4, 2, 2) == 8 + + assert dmp_degree_in(f_6, 0, 2) == 4 + assert dmp_degree_in(f_6, 1, 2) == 4 + assert dmp_degree_in(f_6, 2, 2) == 6 + assert dmp_degree_in(f_6, 3, 3) == 3 + + raises(IndexError, lambda: dmp_degree_in([[1]], -5, 1)) + + +def test_dmp_degree_list(): + assert dmp_degree_list([[[[ ]]]], 3) == (-oo, -oo, -oo, -oo) + assert dmp_degree_list([[[[1]]]], 3) == ( 0, 0, 0, 0) + + assert dmp_degree_list(f_0, 2) == (2, 2, 2) + assert dmp_degree_list(f_1, 2) == (3, 3, 3) + assert dmp_degree_list(f_2, 2) == (5, 3, 3) + assert dmp_degree_list(f_3, 2) == (5, 4, 7) + assert dmp_degree_list(f_4, 2) == (9, 12, 8) + assert dmp_degree_list(f_5, 2) == (3, 3, 3) + assert dmp_degree_list(f_6, 3) == (4, 4, 6, 3) + + +def test_dup_strip(): + assert dup_strip([]) == [] + assert dup_strip([0]) == [] + assert dup_strip([0, 0, 0]) == [] + + assert dup_strip([1]) == [1] + assert dup_strip([0, 1]) == [1] + assert dup_strip([0, 0, 0, 1]) == [1] + + assert dup_strip([1, 2, 0]) == [1, 2, 0] + assert dup_strip([0, 1, 2, 0]) == [1, 2, 0] + assert dup_strip([0, 0, 0, 1, 2, 0]) == [1, 2, 0] + + +def test_dmp_strip(): + assert dmp_strip([0, 1, 0], 0) == [1, 0] + + assert dmp_strip([[]], 1) == [[]] + assert dmp_strip([[], []], 1) == [[]] + assert dmp_strip([[], [], []], 1) == [[]] + + assert dmp_strip([[[]]], 2) == [[[]]] + assert dmp_strip([[[]], [[]]], 2) == [[[]]] + assert dmp_strip([[[]], [[]], [[]]], 2) == [[[]]] + + assert dmp_strip([[[1]]], 2) == [[[1]]] + assert dmp_strip([[[]], [[1]]], 2) == [[[1]]] + assert dmp_strip([[[]], [[1]], [[]]], 2) == [[[1]], [[]]] + + +def test_dmp_validate(): + assert dmp_validate([]) == ([], 0) + assert dmp_validate([0, 0, 0, 1, 0]) == ([1, 0], 0) + + assert dmp_validate([[[]]]) == ([[[]]], 2) + assert dmp_validate([[0], [], [0], [1], [0]]) == ([[1], []], 1) + + raises(ValueError, lambda: dmp_validate([[0], 0, [0], [1], [0]])) + + +def test_dup_reverse(): + assert dup_reverse([1, 2, 0, 3]) == [3, 0, 2, 1] + assert dup_reverse([1, 2, 3, 0]) == [3, 2, 1] + + +def test_dup_copy(): + f = [ZZ(1), ZZ(0), ZZ(2)] + g = dup_copy(f) + + g[0], g[2] = ZZ(7), ZZ(0) + + assert f != g + + +def test_dmp_copy(): + f = [[ZZ(1)], [ZZ(2), ZZ(0)]] + g = dmp_copy(f, 1) + + g[0][0], g[1][1] = ZZ(7), ZZ(1) + + assert f != g + + +def test_dup_normal(): + assert dup_normal([0, 0, 2, 1, 0, 11, 0], ZZ) == \ + [ZZ(2), ZZ(1), ZZ(0), ZZ(11), ZZ(0)] + + +def test_dmp_normal(): + assert dmp_normal([[0], [], [0, 2, 1], [0], [11], []], 1, ZZ) == \ + [[ZZ(2), ZZ(1)], [], [ZZ(11)], []] + + +def test_dup_convert(): + K0, K1 = ZZ['x'], ZZ + + f = [K0(1), K0(2), K0(0), K0(3)] + + assert dup_convert(f, K0, K1) == \ + [ZZ(1), ZZ(2), ZZ(0), ZZ(3)] + + +def test_dmp_convert(): + K0, K1 = ZZ['x'], ZZ + + f = [[K0(1)], [K0(2)], [], [K0(3)]] + + assert dmp_convert(f, 1, K0, K1) == \ + [[ZZ(1)], [ZZ(2)], [], [ZZ(3)]] + + +def test_dup_from_sympy(): + assert dup_from_sympy([S.One, S(2)], ZZ) == \ + [ZZ(1), ZZ(2)] + assert dup_from_sympy([S.Half, S(3)], QQ) == \ + [QQ(1, 2), QQ(3, 1)] + + +def test_dmp_from_sympy(): + assert dmp_from_sympy([[S.One, S(2)], [S.Zero]], 1, ZZ) == \ + [[ZZ(1), ZZ(2)], []] + assert dmp_from_sympy([[S.Half, S(2)]], 1, QQ) == \ + [[QQ(1, 2), QQ(2, 1)]] + + +def test_dup_nth(): + assert dup_nth([1, 2, 3], 0, ZZ) == 3 + assert dup_nth([1, 2, 3], 1, ZZ) == 2 + assert dup_nth([1, 2, 3], 2, ZZ) == 1 + + assert dup_nth([1, 2, 3], 9, ZZ) == 0 + + raises(IndexError, lambda: dup_nth([3, 4, 5], -1, ZZ)) + + +def test_dmp_nth(): + assert dmp_nth([[1], [2], [3]], 0, 1, ZZ) == [3] + assert dmp_nth([[1], [2], [3]], 1, 1, ZZ) == [2] + assert dmp_nth([[1], [2], [3]], 2, 1, ZZ) == [1] + + assert dmp_nth([[1], [2], [3]], 9, 1, ZZ) == [] + + raises(IndexError, lambda: dmp_nth([[3], [4], [5]], -1, 1, ZZ)) + + +def test_dmp_ground_nth(): + assert dmp_ground_nth([[]], (0, 0), 1, ZZ) == 0 + assert dmp_ground_nth([[1], [2], [3]], (0, 0), 1, ZZ) == 3 + assert dmp_ground_nth([[1], [2], [3]], (1, 0), 1, ZZ) == 2 + assert dmp_ground_nth([[1], [2], [3]], (2, 0), 1, ZZ) == 1 + + assert dmp_ground_nth([[1], [2], [3]], (2, 1), 1, ZZ) == 0 + assert dmp_ground_nth([[1], [2], [3]], (3, 0), 1, ZZ) == 0 + + raises(IndexError, lambda: dmp_ground_nth([[3], [4], [5]], (2, -1), 1, ZZ)) + + +def test_dmp_zero_p(): + assert dmp_zero_p([], 0) is True + assert dmp_zero_p([[]], 1) is True + + assert dmp_zero_p([[[]]], 2) is True + assert dmp_zero_p([[[1]]], 2) is False + + +def test_dmp_zero(): + assert dmp_zero(0) == [] + assert dmp_zero(2) == [[[]]] + + +def test_dmp_one_p(): + assert dmp_one_p([1], 0, ZZ) is True + assert dmp_one_p([[1]], 1, ZZ) is True + assert dmp_one_p([[[1]]], 2, ZZ) is True + assert dmp_one_p([[[12]]], 2, ZZ) is False + + +def test_dmp_one(): + assert dmp_one(0, ZZ) == [ZZ(1)] + assert dmp_one(2, ZZ) == [[[ZZ(1)]]] + + +def test_dmp_ground_p(): + assert dmp_ground_p([], 0, 0) is True + assert dmp_ground_p([[]], 0, 1) is True + assert dmp_ground_p([[]], 1, 1) is False + + assert dmp_ground_p([[ZZ(1)]], 1, 1) is True + assert dmp_ground_p([[[ZZ(2)]]], 2, 2) is True + + assert dmp_ground_p([[[ZZ(2)]]], 3, 2) is False + assert dmp_ground_p([[[ZZ(3)], []]], 3, 2) is False + + assert dmp_ground_p([], None, 0) is True + assert dmp_ground_p([[]], None, 1) is True + + assert dmp_ground_p([ZZ(1)], None, 0) is True + assert dmp_ground_p([[[ZZ(1)]]], None, 2) is True + + assert dmp_ground_p([[[ZZ(3)], []]], None, 2) is False + + +def test_dmp_ground(): + assert dmp_ground(ZZ(0), 2) == [[[]]] + + assert dmp_ground(ZZ(7), -1) == ZZ(7) + assert dmp_ground(ZZ(7), 0) == [ZZ(7)] + assert dmp_ground(ZZ(7), 2) == [[[ZZ(7)]]] + + +def test_dmp_zeros(): + assert dmp_zeros(4, 0, ZZ) == [[], [], [], []] + + assert dmp_zeros(0, 2, ZZ) == [] + assert dmp_zeros(1, 2, ZZ) == [[[[]]]] + assert dmp_zeros(2, 2, ZZ) == [[[[]]], [[[]]]] + assert dmp_zeros(3, 2, ZZ) == [[[[]]], [[[]]], [[[]]]] + + assert dmp_zeros(3, -1, ZZ) == [0, 0, 0] + + +def test_dmp_grounds(): + assert dmp_grounds(ZZ(7), 0, 2) == [] + + assert dmp_grounds(ZZ(7), 1, 2) == [[[[7]]]] + assert dmp_grounds(ZZ(7), 2, 2) == [[[[7]]], [[[7]]]] + assert dmp_grounds(ZZ(7), 3, 2) == [[[[7]]], [[[7]]], [[[7]]]] + + assert dmp_grounds(ZZ(7), 3, -1) == [7, 7, 7] + + +def test_dmp_negative_p(): + assert dmp_negative_p([[[]]], 2, ZZ) is False + assert dmp_negative_p([[[1], [2]]], 2, ZZ) is False + assert dmp_negative_p([[[-1], [2]]], 2, ZZ) is True + + +def test_dmp_positive_p(): + assert dmp_positive_p([[[]]], 2, ZZ) is False + assert dmp_positive_p([[[1], [2]]], 2, ZZ) is True + assert dmp_positive_p([[[-1], [2]]], 2, ZZ) is False + + +def test_dup_from_to_dict(): + assert dup_from_raw_dict({}, ZZ) == [] + assert dup_from_dict({}, ZZ) == [] + + assert dup_to_raw_dict([]) == {} + assert dup_to_dict([]) == {} + + assert dup_to_raw_dict([], ZZ, zero=True) == {0: ZZ(0)} + assert dup_to_dict([], ZZ, zero=True) == {(0,): ZZ(0)} + + f = [3, 0, 0, 2, 0, 0, 0, 0, 8] + g = {8: 3, 5: 2, 0: 8} + h = {(8,): 3, (5,): 2, (0,): 8} + + assert dup_from_raw_dict(g, ZZ) == f + assert dup_from_dict(h, ZZ) == f + + assert dup_to_raw_dict(f) == g + assert dup_to_dict(f) == h + + R, x,y = ring("x,y", ZZ) + K = R.to_domain() + + f = [R(3), R(0), R(2), R(0), R(0), R(8)] + g = {5: R(3), 3: R(2), 0: R(8)} + h = {(5,): R(3), (3,): R(2), (0,): R(8)} + + assert dup_from_raw_dict(g, K) == f + assert dup_from_dict(h, K) == f + + assert dup_to_raw_dict(f) == g + assert dup_to_dict(f) == h + + +def test_dmp_from_to_dict(): + assert dmp_from_dict({}, 1, ZZ) == [[]] + assert dmp_to_dict([[]], 1) == {} + + assert dmp_to_dict([], 0, ZZ, zero=True) == {(0,): ZZ(0)} + assert dmp_to_dict([[]], 1, ZZ, zero=True) == {(0, 0): ZZ(0)} + + f = [[3], [], [], [2], [], [], [], [], [8]] + g = {(8, 0): 3, (5, 0): 2, (0, 0): 8} + + assert dmp_from_dict(g, 1, ZZ) == f + assert dmp_to_dict(f, 1) == g + + +def test_dmp_swap(): + f = dmp_normal([[1, 0, 0], [], [1, 0], [], [1]], 1, ZZ) + g = dmp_normal([[1, 0, 0, 0, 0], [1, 0, 0], [1]], 1, ZZ) + + assert dmp_swap(f, 1, 1, 1, ZZ) == f + + assert dmp_swap(f, 0, 1, 1, ZZ) == g + assert dmp_swap(g, 0, 1, 1, ZZ) == f + + raises(IndexError, lambda: dmp_swap(f, -1, -7, 1, ZZ)) + + +def test_dmp_permute(): + f = dmp_normal([[1, 0, 0], [], [1, 0], [], [1]], 1, ZZ) + g = dmp_normal([[1, 0, 0, 0, 0], [1, 0, 0], [1]], 1, ZZ) + + assert dmp_permute(f, [0, 1], 1, ZZ) == f + assert dmp_permute(g, [0, 1], 1, ZZ) == g + + assert dmp_permute(f, [1, 0], 1, ZZ) == g + assert dmp_permute(g, [1, 0], 1, ZZ) == f + + +def test_dmp_nest(): + assert dmp_nest(ZZ(1), 2, ZZ) == [[[1]]] + + assert dmp_nest([[1]], 0, ZZ) == [[1]] + assert dmp_nest([[1]], 1, ZZ) == [[[1]]] + assert dmp_nest([[1]], 2, ZZ) == [[[[1]]]] + + +def test_dmp_raise(): + assert dmp_raise([], 2, 0, ZZ) == [[[]]] + assert dmp_raise([[1]], 0, 1, ZZ) == [[1]] + + assert dmp_raise([[1, 2, 3], [], [2, 3]], 2, 1, ZZ) == \ + [[[[1]], [[2]], [[3]]], [[[]]], [[[2]], [[3]]]] + + +def test_dup_deflate(): + assert dup_deflate([], ZZ) == (1, []) + assert dup_deflate([2], ZZ) == (1, [2]) + assert dup_deflate([1, 2, 3], ZZ) == (1, [1, 2, 3]) + assert dup_deflate([1, 0, 2, 0, 3], ZZ) == (2, [1, 2, 3]) + + assert dup_deflate(dup_from_raw_dict({7: 1, 1: 1}, ZZ), ZZ) == \ + (1, [1, 0, 0, 0, 0, 0, 1, 0]) + assert dup_deflate(dup_from_raw_dict({7: 1, 0: 1}, ZZ), ZZ) == \ + (7, [1, 1]) + assert dup_deflate(dup_from_raw_dict({7: 1, 3: 1}, ZZ), ZZ) == \ + (1, [1, 0, 0, 0, 1, 0, 0, 0]) + + assert dup_deflate(dup_from_raw_dict({7: 1, 4: 1}, ZZ), ZZ) == \ + (1, [1, 0, 0, 1, 0, 0, 0, 0]) + assert dup_deflate(dup_from_raw_dict({8: 1, 4: 1}, ZZ), ZZ) == \ + (4, [1, 1, 0]) + + assert dup_deflate(dup_from_raw_dict({8: 1}, ZZ), ZZ) == \ + (8, [1, 0]) + assert dup_deflate(dup_from_raw_dict({7: 1}, ZZ), ZZ) == \ + (7, [1, 0]) + assert dup_deflate(dup_from_raw_dict({1: 1}, ZZ), ZZ) == \ + (1, [1, 0]) + + +def test_dmp_deflate(): + assert dmp_deflate([[]], 1, ZZ) == ((1, 1), [[]]) + assert dmp_deflate([[2]], 1, ZZ) == ((1, 1), [[2]]) + + f = [[1, 0, 0], [], [1, 0], [], [1]] + + assert dmp_deflate(f, 1, ZZ) == ((2, 1), [[1, 0, 0], [1, 0], [1]]) + + +def test_dup_multi_deflate(): + assert dup_multi_deflate(([2],), ZZ) == (1, ([2],)) + assert dup_multi_deflate(([], []), ZZ) == (1, ([], [])) + + assert dup_multi_deflate(([1, 2, 3],), ZZ) == (1, ([1, 2, 3],)) + assert dup_multi_deflate(([1, 0, 2, 0, 3],), ZZ) == (2, ([1, 2, 3],)) + + assert dup_multi_deflate(([1, 0, 2, 0, 3], [2, 0, 0]), ZZ) == \ + (2, ([1, 2, 3], [2, 0])) + assert dup_multi_deflate(([1, 0, 2, 0, 3], [2, 1, 0]), ZZ) == \ + (1, ([1, 0, 2, 0, 3], [2, 1, 0])) + + +def test_dmp_multi_deflate(): + assert dmp_multi_deflate(([[]],), 1, ZZ) == \ + ((1, 1), ([[]],)) + assert dmp_multi_deflate(([[]], [[]]), 1, ZZ) == \ + ((1, 1), ([[]], [[]])) + + assert dmp_multi_deflate(([[1]], [[]]), 1, ZZ) == \ + ((1, 1), ([[1]], [[]])) + assert dmp_multi_deflate(([[1]], [[2]]), 1, ZZ) == \ + ((1, 1), ([[1]], [[2]])) + assert dmp_multi_deflate(([[1]], [[2, 0]]), 1, ZZ) == \ + ((1, 1), ([[1]], [[2, 0]])) + + assert dmp_multi_deflate(([[2, 0]], [[2, 0]]), 1, ZZ) == \ + ((1, 1), ([[2, 0]], [[2, 0]])) + + assert dmp_multi_deflate( + ([[2]], [[2, 0, 0]]), 1, ZZ) == ((1, 2), ([[2]], [[2, 0]])) + assert dmp_multi_deflate( + ([[2, 0, 0]], [[2, 0, 0]]), 1, ZZ) == ((1, 2), ([[2, 0]], [[2, 0]])) + + assert dmp_multi_deflate(([2, 0, 0], [1, 0, 4, 0, 1]), 0, ZZ) == \ + ((2,), ([2, 0], [1, 4, 1])) + + f = [[1, 0, 0], [], [1, 0], [], [1]] + g = [[1, 0, 1, 0], [], [1]] + + assert dmp_multi_deflate((f,), 1, ZZ) == \ + ((2, 1), ([[1, 0, 0], [1, 0], [1]],)) + + assert dmp_multi_deflate((f, g), 1, ZZ) == \ + ((2, 1), ([[1, 0, 0], [1, 0], [1]], + [[1, 0, 1, 0], [1]])) + + +def test_dup_inflate(): + assert dup_inflate([], 17, ZZ) == [] + + assert dup_inflate([1, 2, 3], 1, ZZ) == [1, 2, 3] + assert dup_inflate([1, 2, 3], 2, ZZ) == [1, 0, 2, 0, 3] + assert dup_inflate([1, 2, 3], 3, ZZ) == [1, 0, 0, 2, 0, 0, 3] + assert dup_inflate([1, 2, 3], 4, ZZ) == [1, 0, 0, 0, 2, 0, 0, 0, 3] + + raises(IndexError, lambda: dup_inflate([1, 2, 3], 0, ZZ)) + + +def test_dmp_inflate(): + assert dmp_inflate([1], (3,), 0, ZZ) == [1] + + assert dmp_inflate([[]], (3, 7), 1, ZZ) == [[]] + assert dmp_inflate([[2]], (1, 2), 1, ZZ) == [[2]] + + assert dmp_inflate([[2, 0]], (1, 1), 1, ZZ) == [[2, 0]] + assert dmp_inflate([[2, 0]], (1, 2), 1, ZZ) == [[2, 0, 0]] + assert dmp_inflate([[2, 0]], (1, 3), 1, ZZ) == [[2, 0, 0, 0]] + + assert dmp_inflate([[1, 0, 0], [1], [1, 0]], (2, 1), 1, ZZ) == \ + [[1, 0, 0], [], [1], [], [1, 0]] + + raises(IndexError, lambda: dmp_inflate([[]], (-3, 7), 1, ZZ)) + + +def test_dmp_exclude(): + assert dmp_exclude([[[]]], 2, ZZ) == ([], [[[]]], 2) + assert dmp_exclude([[[7]]], 2, ZZ) == ([], [[[7]]], 2) + + assert dmp_exclude([1, 2, 3], 0, ZZ) == ([], [1, 2, 3], 0) + assert dmp_exclude([[1], [2, 3]], 1, ZZ) == ([], [[1], [2, 3]], 1) + + assert dmp_exclude([[1, 2, 3]], 1, ZZ) == ([0], [1, 2, 3], 0) + assert dmp_exclude([[1], [2], [3]], 1, ZZ) == ([1], [1, 2, 3], 0) + + assert dmp_exclude([[[1, 2, 3]]], 2, ZZ) == ([0, 1], [1, 2, 3], 0) + assert dmp_exclude([[[1]], [[2]], [[3]]], 2, ZZ) == ([1, 2], [1, 2, 3], 0) + + +def test_dmp_include(): + assert dmp_include([1, 2, 3], [], 0, ZZ) == [1, 2, 3] + + assert dmp_include([1, 2, 3], [0], 0, ZZ) == [[1, 2, 3]] + assert dmp_include([1, 2, 3], [1], 0, ZZ) == [[1], [2], [3]] + + assert dmp_include([1, 2, 3], [0, 1], 0, ZZ) == [[[1, 2, 3]]] + assert dmp_include([1, 2, 3], [1, 2], 0, ZZ) == [[[1]], [[2]], [[3]]] + + +def test_dmp_inject(): + R, x,y = ring("x,y", ZZ) + K = R.to_domain() + + assert dmp_inject([], 0, K) == ([[[]]], 2) + assert dmp_inject([[]], 1, K) == ([[[[]]]], 3) + + assert dmp_inject([R(1)], 0, K) == ([[[1]]], 2) + assert dmp_inject([[R(1)]], 1, K) == ([[[[1]]]], 3) + + assert dmp_inject([R(1), 2*x + 3*y + 4], 0, K) == ([[[1]], [[2], [3, 4]]], 2) + + f = [3*x**2 + 7*x*y + 5*y**2, 2*x, R(0), x*y**2 + 11] + g = [[[3], [7, 0], [5, 0, 0]], [[2], []], [[]], [[1, 0, 0], [11]]] + + assert dmp_inject(f, 0, K) == (g, 2) + + +def test_dmp_eject(): + R, x,y = ring("x,y", ZZ) + K = R.to_domain() + + assert dmp_eject([[[]]], 2, K) == [] + assert dmp_eject([[[[]]]], 3, K) == [[]] + + assert dmp_eject([[[1]]], 2, K) == [R(1)] + assert dmp_eject([[[[1]]]], 3, K) == [[R(1)]] + + assert dmp_eject([[[1]], [[2], [3, 4]]], 2, K) == [R(1), 2*x + 3*y + 4] + + f = [3*x**2 + 7*x*y + 5*y**2, 2*x, R(0), x*y**2 + 11] + g = [[[3], [7, 0], [5, 0, 0]], [[2], []], [[]], [[1, 0, 0], [11]]] + + assert dmp_eject(g, 2, K) == f + + +def test_dup_terms_gcd(): + assert dup_terms_gcd([], ZZ) == (0, []) + assert dup_terms_gcd([1, 0, 1], ZZ) == (0, [1, 0, 1]) + assert dup_terms_gcd([1, 0, 1, 0], ZZ) == (1, [1, 0, 1]) + + +def test_dmp_terms_gcd(): + assert dmp_terms_gcd([[]], 1, ZZ) == ((0, 0), [[]]) + + assert dmp_terms_gcd([1, 0, 1, 0], 0, ZZ) == ((1,), [1, 0, 1]) + assert dmp_terms_gcd([[1], [], [1], []], 1, ZZ) == ((1, 0), [[1], [], [1]]) + + assert dmp_terms_gcd( + [[1, 0], [], [1]], 1, ZZ) == ((0, 0), [[1, 0], [], [1]]) + assert dmp_terms_gcd( + [[1, 0], [1, 0, 0], [], []], 1, ZZ) == ((2, 1), [[1], [1, 0]]) + + +def test_dmp_list_terms(): + assert dmp_list_terms([[[]]], 2, ZZ) == [((0, 0, 0), 0)] + assert dmp_list_terms([[[1]]], 2, ZZ) == [((0, 0, 0), 1)] + + assert dmp_list_terms([1, 2, 4, 3, 5], 0, ZZ) == \ + [((4,), 1), ((3,), 2), ((2,), 4), ((1,), 3), ((0,), 5)] + + assert dmp_list_terms([[1], [2, 4], [3, 5, 0]], 1, ZZ) == \ + [((2, 0), 1), ((1, 1), 2), ((1, 0), 4), ((0, 2), 3), ((0, 1), 5)] + + f = [[2, 0, 0, 0], [1, 0, 0], []] + + assert dmp_list_terms(f, 1, ZZ, order='lex') == [((2, 3), 2), ((1, 2), 1)] + assert dmp_list_terms( + f, 1, ZZ, order='grlex') == [((2, 3), 2), ((1, 2), 1)] + + f = [[2, 0, 0, 0], [1, 0, 0, 0, 0, 0], []] + + assert dmp_list_terms(f, 1, ZZ, order='lex') == [((2, 3), 2), ((1, 5), 1)] + assert dmp_list_terms( + f, 1, ZZ, order='grlex') == [((1, 5), 1), ((2, 3), 2)] + + +def test_dmp_apply_pairs(): + h = lambda a, b: a*b + + assert dmp_apply_pairs([1, 2, 3], [4, 5, 6], h, [], 0, ZZ) == [4, 10, 18] + + assert dmp_apply_pairs([2, 3], [4, 5, 6], h, [], 0, ZZ) == [10, 18] + assert dmp_apply_pairs([1, 2, 3], [5, 6], h, [], 0, ZZ) == [10, 18] + + assert dmp_apply_pairs( + [[1, 2], [3]], [[4, 5], [6]], h, [], 1, ZZ) == [[4, 10], [18]] + + assert dmp_apply_pairs( + [[1, 2], [3]], [[4], [5, 6]], h, [], 1, ZZ) == [[8], [18]] + assert dmp_apply_pairs( + [[1], [2, 3]], [[4, 5], [6]], h, [], 1, ZZ) == [[5], [18]] + + +def test_dup_slice(): + f = [1, 2, 3, 4] + + assert dup_slice(f, 0, 0, ZZ) == [] + assert dup_slice(f, 0, 1, ZZ) == [4] + assert dup_slice(f, 0, 2, ZZ) == [3, 4] + assert dup_slice(f, 0, 3, ZZ) == [2, 3, 4] + assert dup_slice(f, 0, 4, ZZ) == [1, 2, 3, 4] + + assert dup_slice(f, 0, 4, ZZ) == f + assert dup_slice(f, 0, 9, ZZ) == f + + assert dup_slice(f, 1, 0, ZZ) == [] + assert dup_slice(f, 1, 1, ZZ) == [] + assert dup_slice(f, 1, 2, ZZ) == [3, 0] + assert dup_slice(f, 1, 3, ZZ) == [2, 3, 0] + assert dup_slice(f, 1, 4, ZZ) == [1, 2, 3, 0] + + assert dup_slice([1, 2], 0, 3, ZZ) == [1, 2] + + g = [1, 0, 0, 2] + + assert dup_slice(g, 0, 3, ZZ) == [2] + + +def test_dup_random(): + f = dup_random(0, -10, 10, ZZ) + + assert dup_degree(f) == 0 + assert all(-10 <= c <= 10 for c in f) + + f = dup_random(1, -20, 20, ZZ) + + assert dup_degree(f) == 1 + assert all(-20 <= c <= 20 for c in f) + + f = dup_random(2, -30, 30, ZZ) + + assert dup_degree(f) == 2 + assert all(-30 <= c <= 30 for c in f) + + f = dup_random(3, -40, 40, ZZ) + + assert dup_degree(f) == 3 + assert all(-40 <= c <= 40 for c in f) diff --git a/lib/python3.12/site-packages/sympy/polys/tests/test_densetools.py b/lib/python3.12/site-packages/sympy/polys/tests/test_densetools.py new file mode 100644 index 0000000000000000000000000000000000000000..43dae691f52d200b8f934eab8bfa146f0fe8ef49 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/polys/tests/test_densetools.py @@ -0,0 +1,715 @@ +"""Tests for dense recursive polynomials' tools. """ + +from sympy.polys.densebasic import ( + dup_normal, dmp_normal, + dup_from_raw_dict, + dmp_convert, dmp_swap, +) + +from sympy.polys.densearith import dmp_mul_ground + +from sympy.polys.densetools import ( + dup_clear_denoms, dmp_clear_denoms, + dup_integrate, dmp_integrate, dmp_integrate_in, + dup_diff, dmp_diff, dmp_diff_in, + dup_eval, dmp_eval, dmp_eval_in, + dmp_eval_tail, dmp_diff_eval_in, + dup_trunc, dmp_trunc, dmp_ground_trunc, + dup_monic, dmp_ground_monic, + dup_content, dmp_ground_content, + dup_primitive, dmp_ground_primitive, + dup_extract, dmp_ground_extract, + dup_real_imag, + dup_mirror, dup_scale, dup_shift, dmp_shift, + dup_transform, + dup_compose, dmp_compose, + dup_decompose, + dmp_lift, + dup_sign_variations, + dup_revert, dmp_revert, +) +from sympy.polys.polyclasses import ANP + +from sympy.polys.polyerrors import ( + MultivariatePolynomialError, + ExactQuotientFailed, + NotReversible, + DomainError, +) + +from sympy.polys.specialpolys import f_polys + +from sympy.polys.domains import FF, ZZ, QQ, ZZ_I, QQ_I, EX, RR +from sympy.polys.rings import ring + +from sympy.core.numbers import I +from sympy.core.singleton import S +from sympy.functions.elementary.trigonometric import sin + +from sympy.abc import x +from sympy.testing.pytest import raises + +f_0, f_1, f_2, f_3, f_4, f_5, f_6 = [ f.to_dense() for f in f_polys() ] + +def test_dup_integrate(): + assert dup_integrate([], 1, QQ) == [] + assert dup_integrate([], 2, QQ) == [] + + assert dup_integrate([QQ(1)], 1, QQ) == [QQ(1), QQ(0)] + assert dup_integrate([QQ(1)], 2, QQ) == [QQ(1, 2), QQ(0), QQ(0)] + + assert dup_integrate([QQ(1), QQ(2), QQ(3)], 0, QQ) == \ + [QQ(1), QQ(2), QQ(3)] + assert dup_integrate([QQ(1), QQ(2), QQ(3)], 1, QQ) == \ + [QQ(1, 3), QQ(1), QQ(3), QQ(0)] + assert dup_integrate([QQ(1), QQ(2), QQ(3)], 2, QQ) == \ + [QQ(1, 12), QQ(1, 3), QQ(3, 2), QQ(0), QQ(0)] + assert dup_integrate([QQ(1), QQ(2), QQ(3)], 3, QQ) == \ + [QQ(1, 60), QQ(1, 12), QQ(1, 2), QQ(0), QQ(0), QQ(0)] + + assert dup_integrate(dup_from_raw_dict({29: QQ(17)}, QQ), 3, QQ) == \ + dup_from_raw_dict({32: QQ(17, 29760)}, QQ) + + assert dup_integrate(dup_from_raw_dict({29: QQ(17), 5: QQ(1, 2)}, QQ), 3, QQ) == \ + dup_from_raw_dict({32: QQ(17, 29760), 8: QQ(1, 672)}, QQ) + + +def test_dmp_integrate(): + assert dmp_integrate([QQ(1)], 2, 0, QQ) == [QQ(1, 2), QQ(0), QQ(0)] + + assert dmp_integrate([[[]]], 1, 2, QQ) == [[[]]] + assert dmp_integrate([[[]]], 2, 2, QQ) == [[[]]] + + assert dmp_integrate([[[QQ(1)]]], 1, 2, QQ) == [[[QQ(1)]], [[]]] + assert dmp_integrate([[[QQ(1)]]], 2, 2, QQ) == [[[QQ(1, 2)]], [[]], [[]]] + + assert dmp_integrate([[QQ(1)], [QQ(2)], [QQ(3)]], 0, 1, QQ) == \ + [[QQ(1)], [QQ(2)], [QQ(3)]] + assert dmp_integrate([[QQ(1)], [QQ(2)], [QQ(3)]], 1, 1, QQ) == \ + [[QQ(1, 3)], [QQ(1)], [QQ(3)], []] + assert dmp_integrate([[QQ(1)], [QQ(2)], [QQ(3)]], 2, 1, QQ) == \ + [[QQ(1, 12)], [QQ(1, 3)], [QQ(3, 2)], [], []] + assert dmp_integrate([[QQ(1)], [QQ(2)], [QQ(3)]], 3, 1, QQ) == \ + [[QQ(1, 60)], [QQ(1, 12)], [QQ(1, 2)], [], [], []] + + +def test_dmp_integrate_in(): + f = dmp_convert(f_6, 3, ZZ, QQ) + + assert dmp_integrate_in(f, 2, 1, 3, QQ) == \ + dmp_swap( + dmp_integrate(dmp_swap(f, 0, 1, 3, QQ), 2, 3, QQ), 0, 1, 3, QQ) + assert dmp_integrate_in(f, 3, 1, 3, QQ) == \ + dmp_swap( + dmp_integrate(dmp_swap(f, 0, 1, 3, QQ), 3, 3, QQ), 0, 1, 3, QQ) + assert dmp_integrate_in(f, 2, 2, 3, QQ) == \ + dmp_swap( + dmp_integrate(dmp_swap(f, 0, 2, 3, QQ), 2, 3, QQ), 0, 2, 3, QQ) + assert dmp_integrate_in(f, 3, 2, 3, QQ) == \ + dmp_swap( + dmp_integrate(dmp_swap(f, 0, 2, 3, QQ), 3, 3, QQ), 0, 2, 3, QQ) + + raises(IndexError, lambda: dmp_integrate_in(f, 1, -1, 3, QQ)) + raises(IndexError, lambda: dmp_integrate_in(f, 1, 4, 3, QQ)) + + +def test_dup_diff(): + assert dup_diff([], 1, ZZ) == [] + assert dup_diff([7], 1, ZZ) == [] + assert dup_diff([2, 7], 1, ZZ) == [2] + assert dup_diff([1, 2, 1], 1, ZZ) == [2, 2] + assert dup_diff([1, 2, 3, 4], 1, ZZ) == [3, 4, 3] + assert dup_diff([1, -1, 0, 0, 2], 1, ZZ) == [4, -3, 0, 0] + + f = dup_normal([17, 34, 56, -345, 23, 76, 0, 0, 12, 3, 7], ZZ) + + assert dup_diff(f, 0, ZZ) == f + assert dup_diff(f, 1, ZZ) == [170, 306, 448, -2415, 138, 380, 0, 0, 24, 3] + assert dup_diff(f, 2, ZZ) == dup_diff(dup_diff(f, 1, ZZ), 1, ZZ) + assert dup_diff( + f, 3, ZZ) == dup_diff(dup_diff(dup_diff(f, 1, ZZ), 1, ZZ), 1, ZZ) + + K = FF(3) + f = dup_normal([17, 34, 56, -345, 23, 76, 0, 0, 12, 3, 7], K) + + assert dup_diff(f, 1, K) == dup_normal([2, 0, 1, 0, 0, 2, 0, 0, 0, 0], K) + assert dup_diff(f, 2, K) == dup_normal([1, 0, 0, 2, 0, 0, 0], K) + assert dup_diff(f, 3, K) == dup_normal([], K) + + assert dup_diff(f, 0, K) == f + assert dup_diff(f, 2, K) == dup_diff(dup_diff(f, 1, K), 1, K) + assert dup_diff( + f, 3, K) == dup_diff(dup_diff(dup_diff(f, 1, K), 1, K), 1, K) + + +def test_dmp_diff(): + assert dmp_diff([], 1, 0, ZZ) == [] + assert dmp_diff([[]], 1, 1, ZZ) == [[]] + assert dmp_diff([[[]]], 1, 2, ZZ) == [[[]]] + + assert dmp_diff([[[1], [2]]], 1, 2, ZZ) == [[[]]] + + assert dmp_diff([[[1]], [[]]], 1, 2, ZZ) == [[[1]]] + assert dmp_diff([[[3]], [[1]], [[]]], 1, 2, ZZ) == [[[6]], [[1]]] + + assert dmp_diff([1, -1, 0, 0, 2], 1, 0, ZZ) == \ + dup_diff([1, -1, 0, 0, 2], 1, ZZ) + + assert dmp_diff(f_6, 0, 3, ZZ) == f_6 + assert dmp_diff(f_6, 1, 3, ZZ) == [[[[8460]], [[]]], + [[[135, 0, 0], [], [], [-135, 0, 0]]], + [[[]]], + [[[-423]], [[-47]], [[]], [[141], [], [94, 0], []], [[]]]] + assert dmp_diff( + f_6, 2, 3, ZZ) == dmp_diff(dmp_diff(f_6, 1, 3, ZZ), 1, 3, ZZ) + assert dmp_diff(f_6, 3, 3, ZZ) == dmp_diff( + dmp_diff(dmp_diff(f_6, 1, 3, ZZ), 1, 3, ZZ), 1, 3, ZZ) + + K = FF(23) + F_6 = dmp_normal(f_6, 3, K) + + assert dmp_diff(F_6, 0, 3, K) == F_6 + assert dmp_diff(F_6, 1, 3, K) == dmp_diff(F_6, 1, 3, K) + assert dmp_diff(F_6, 2, 3, K) == dmp_diff(dmp_diff(F_6, 1, 3, K), 1, 3, K) + assert dmp_diff(F_6, 3, 3, K) == dmp_diff( + dmp_diff(dmp_diff(F_6, 1, 3, K), 1, 3, K), 1, 3, K) + + +def test_dmp_diff_in(): + assert dmp_diff_in(f_6, 2, 1, 3, ZZ) == \ + dmp_swap(dmp_diff(dmp_swap(f_6, 0, 1, 3, ZZ), 2, 3, ZZ), 0, 1, 3, ZZ) + assert dmp_diff_in(f_6, 3, 1, 3, ZZ) == \ + dmp_swap(dmp_diff(dmp_swap(f_6, 0, 1, 3, ZZ), 3, 3, ZZ), 0, 1, 3, ZZ) + assert dmp_diff_in(f_6, 2, 2, 3, ZZ) == \ + dmp_swap(dmp_diff(dmp_swap(f_6, 0, 2, 3, ZZ), 2, 3, ZZ), 0, 2, 3, ZZ) + assert dmp_diff_in(f_6, 3, 2, 3, ZZ) == \ + dmp_swap(dmp_diff(dmp_swap(f_6, 0, 2, 3, ZZ), 3, 3, ZZ), 0, 2, 3, ZZ) + + raises(IndexError, lambda: dmp_diff_in(f_6, 1, -1, 3, ZZ)) + raises(IndexError, lambda: dmp_diff_in(f_6, 1, 4, 3, ZZ)) + +def test_dup_eval(): + assert dup_eval([], 7, ZZ) == 0 + assert dup_eval([1, 2], 0, ZZ) == 2 + assert dup_eval([1, 2, 3], 7, ZZ) == 66 + + +def test_dmp_eval(): + assert dmp_eval([], 3, 0, ZZ) == 0 + + assert dmp_eval([[]], 3, 1, ZZ) == [] + assert dmp_eval([[[]]], 3, 2, ZZ) == [[]] + + assert dmp_eval([[1, 2]], 0, 1, ZZ) == [1, 2] + + assert dmp_eval([[[1]]], 3, 2, ZZ) == [[1]] + assert dmp_eval([[[1, 2]]], 3, 2, ZZ) == [[1, 2]] + + assert dmp_eval([[3, 2], [1, 2]], 3, 1, ZZ) == [10, 8] + assert dmp_eval([[[3, 2]], [[1, 2]]], 3, 2, ZZ) == [[10, 8]] + + +def test_dmp_eval_in(): + assert dmp_eval_in( + f_6, -2, 1, 3, ZZ) == dmp_eval(dmp_swap(f_6, 0, 1, 3, ZZ), -2, 3, ZZ) + assert dmp_eval_in( + f_6, 7, 1, 3, ZZ) == dmp_eval(dmp_swap(f_6, 0, 1, 3, ZZ), 7, 3, ZZ) + assert dmp_eval_in(f_6, -2, 2, 3, ZZ) == dmp_swap( + dmp_eval(dmp_swap(f_6, 0, 2, 3, ZZ), -2, 3, ZZ), 0, 1, 2, ZZ) + assert dmp_eval_in(f_6, 7, 2, 3, ZZ) == dmp_swap( + dmp_eval(dmp_swap(f_6, 0, 2, 3, ZZ), 7, 3, ZZ), 0, 1, 2, ZZ) + + f = [[[int(45)]], [[]], [[]], [[int(-9)], [-1], [], [int(3), int(0), int(10), int(0)]]] + + assert dmp_eval_in(f, -2, 2, 2, ZZ) == \ + [[45], [], [], [-9, -1, 0, -44]] + + raises(IndexError, lambda: dmp_eval_in(f_6, ZZ(1), -1, 3, ZZ)) + raises(IndexError, lambda: dmp_eval_in(f_6, ZZ(1), 4, 3, ZZ)) + + +def test_dmp_eval_tail(): + assert dmp_eval_tail([[]], [1], 1, ZZ) == [] + assert dmp_eval_tail([[[]]], [1], 2, ZZ) == [[]] + assert dmp_eval_tail([[[]]], [1, 2], 2, ZZ) == [] + + assert dmp_eval_tail(f_0, [], 2, ZZ) == f_0 + + assert dmp_eval_tail(f_0, [1, -17, 8], 2, ZZ) == 84496 + assert dmp_eval_tail(f_0, [-17, 8], 2, ZZ) == [-1409, 3, 85902] + assert dmp_eval_tail(f_0, [8], 2, ZZ) == [[83, 2], [3], [302, 81, 1]] + + assert dmp_eval_tail(f_1, [-17, 8], 2, ZZ) == [-136, 15699, 9166, -27144] + + assert dmp_eval_tail( + f_2, [-12, 3], 2, ZZ) == [-1377, 0, -702, -1224, 0, -624] + assert dmp_eval_tail( + f_3, [-12, 3], 2, ZZ) == [144, 82, -5181, -28872, -14868, -540] + + assert dmp_eval_tail( + f_4, [25, -1], 2, ZZ) == [152587890625, 9765625, -59605407714843750, + -3839159765625, -1562475, 9536712644531250, 610349546750, -4, 24414375000, 1562520] + assert dmp_eval_tail(f_5, [25, -1], 2, ZZ) == [-1, -78, -2028, -17576] + + assert dmp_eval_tail(f_6, [0, 2, 4], 3, ZZ) == [5040, 0, 0, 4480] + + +def test_dmp_diff_eval_in(): + assert dmp_diff_eval_in(f_6, 2, 7, 1, 3, ZZ) == \ + dmp_eval(dmp_diff(dmp_swap(f_6, 0, 1, 3, ZZ), 2, 3, ZZ), 7, 3, ZZ) + + assert dmp_diff_eval_in(f_6, 2, 7, 0, 3, ZZ) == \ + dmp_eval(dmp_diff(f_6, 2, 3, ZZ), 7, 3, ZZ) + + raises(IndexError, lambda: dmp_diff_eval_in(f_6, 1, ZZ(1), 4, 3, ZZ)) + + +def test_dup_revert(): + f = [-QQ(1, 720), QQ(0), QQ(1, 24), QQ(0), -QQ(1, 2), QQ(0), QQ(1)] + g = [QQ(61, 720), QQ(0), QQ(5, 24), QQ(0), QQ(1, 2), QQ(0), QQ(1)] + + assert dup_revert(f, 8, QQ) == g + + raises(NotReversible, lambda: dup_revert([QQ(1), QQ(0)], 3, QQ)) + + +def test_dmp_revert(): + f = [-QQ(1, 720), QQ(0), QQ(1, 24), QQ(0), -QQ(1, 2), QQ(0), QQ(1)] + g = [QQ(61, 720), QQ(0), QQ(5, 24), QQ(0), QQ(1, 2), QQ(0), QQ(1)] + + assert dmp_revert(f, 8, 0, QQ) == g + + raises(MultivariatePolynomialError, lambda: dmp_revert([[1]], 2, 1, QQ)) + + +def test_dup_trunc(): + assert dup_trunc([1, 2, 3, 4, 5, 6], ZZ(3), ZZ) == [1, -1, 0, 1, -1, 0] + assert dup_trunc([6, 5, 4, 3, 2, 1], ZZ(3), ZZ) == [-1, 1, 0, -1, 1] + + R = ZZ_I + assert dup_trunc([R(3), R(4), R(5)], R(3), R) == [R(1), R(-1)] + + K = FF(5) + assert dup_trunc([K(3), K(4), K(5)], K(3), K) == [K(1), K(0)] + + +def test_dmp_trunc(): + assert dmp_trunc([[]], [1, 2], 2, ZZ) == [[]] + assert dmp_trunc([[1, 2], [1, 4, 1], [1]], [1, 2], 1, ZZ) == [[-3], [1]] + + +def test_dmp_ground_trunc(): + assert dmp_ground_trunc(f_0, ZZ(3), 2, ZZ) == \ + dmp_normal( + [[[1, -1, 0], [-1]], [[]], [[1, -1, 0], [1, -1, 1], [1]]], 2, ZZ) + + +def test_dup_monic(): + assert dup_monic([3, 6, 9], ZZ) == [1, 2, 3] + + raises(ExactQuotientFailed, lambda: dup_monic([3, 4, 5], ZZ)) + + assert dup_monic([], QQ) == [] + assert dup_monic([QQ(1)], QQ) == [QQ(1)] + assert dup_monic([QQ(7), QQ(1), QQ(21)], QQ) == [QQ(1), QQ(1, 7), QQ(3)] + + +def test_dmp_ground_monic(): + assert dmp_ground_monic([3, 6, 9], 0, ZZ) == [1, 2, 3] + + assert dmp_ground_monic([[3], [6], [9]], 1, ZZ) == [[1], [2], [3]] + + raises( + ExactQuotientFailed, lambda: dmp_ground_monic([[3], [4], [5]], 1, ZZ)) + + assert dmp_ground_monic([[]], 1, QQ) == [[]] + assert dmp_ground_monic([[QQ(1)]], 1, QQ) == [[QQ(1)]] + assert dmp_ground_monic( + [[QQ(7)], [QQ(1)], [QQ(21)]], 1, QQ) == [[QQ(1)], [QQ(1, 7)], [QQ(3)]] + + +def test_dup_content(): + assert dup_content([], ZZ) == ZZ(0) + assert dup_content([1], ZZ) == ZZ(1) + assert dup_content([-1], ZZ) == ZZ(1) + assert dup_content([1, 1], ZZ) == ZZ(1) + assert dup_content([2, 2], ZZ) == ZZ(2) + assert dup_content([1, 2, 1], ZZ) == ZZ(1) + assert dup_content([2, 4, 2], ZZ) == ZZ(2) + + assert dup_content([QQ(2, 3), QQ(4, 9)], QQ) == QQ(2, 9) + assert dup_content([QQ(2, 3), QQ(4, 5)], QQ) == QQ(2, 15) + + +def test_dmp_ground_content(): + assert dmp_ground_content([[]], 1, ZZ) == ZZ(0) + assert dmp_ground_content([[]], 1, QQ) == QQ(0) + assert dmp_ground_content([[1]], 1, ZZ) == ZZ(1) + assert dmp_ground_content([[-1]], 1, ZZ) == ZZ(1) + assert dmp_ground_content([[1], [1]], 1, ZZ) == ZZ(1) + assert dmp_ground_content([[2], [2]], 1, ZZ) == ZZ(2) + assert dmp_ground_content([[1], [2], [1]], 1, ZZ) == ZZ(1) + assert dmp_ground_content([[2], [4], [2]], 1, ZZ) == ZZ(2) + + assert dmp_ground_content([[QQ(2, 3)], [QQ(4, 9)]], 1, QQ) == QQ(2, 9) + assert dmp_ground_content([[QQ(2, 3)], [QQ(4, 5)]], 1, QQ) == QQ(2, 15) + + assert dmp_ground_content(f_0, 2, ZZ) == ZZ(1) + assert dmp_ground_content( + dmp_mul_ground(f_0, ZZ(2), 2, ZZ), 2, ZZ) == ZZ(2) + + assert dmp_ground_content(f_1, 2, ZZ) == ZZ(1) + assert dmp_ground_content( + dmp_mul_ground(f_1, ZZ(3), 2, ZZ), 2, ZZ) == ZZ(3) + + assert dmp_ground_content(f_2, 2, ZZ) == ZZ(1) + assert dmp_ground_content( + dmp_mul_ground(f_2, ZZ(4), 2, ZZ), 2, ZZ) == ZZ(4) + + assert dmp_ground_content(f_3, 2, ZZ) == ZZ(1) + assert dmp_ground_content( + dmp_mul_ground(f_3, ZZ(5), 2, ZZ), 2, ZZ) == ZZ(5) + + assert dmp_ground_content(f_4, 2, ZZ) == ZZ(1) + assert dmp_ground_content( + dmp_mul_ground(f_4, ZZ(6), 2, ZZ), 2, ZZ) == ZZ(6) + + assert dmp_ground_content(f_5, 2, ZZ) == ZZ(1) + assert dmp_ground_content( + dmp_mul_ground(f_5, ZZ(7), 2, ZZ), 2, ZZ) == ZZ(7) + + assert dmp_ground_content(f_6, 3, ZZ) == ZZ(1) + assert dmp_ground_content( + dmp_mul_ground(f_6, ZZ(8), 3, ZZ), 3, ZZ) == ZZ(8) + + +def test_dup_primitive(): + assert dup_primitive([], ZZ) == (ZZ(0), []) + assert dup_primitive([ZZ(1)], ZZ) == (ZZ(1), [ZZ(1)]) + assert dup_primitive([ZZ(1), ZZ(1)], ZZ) == (ZZ(1), [ZZ(1), ZZ(1)]) + assert dup_primitive([ZZ(2), ZZ(2)], ZZ) == (ZZ(2), [ZZ(1), ZZ(1)]) + assert dup_primitive( + [ZZ(1), ZZ(2), ZZ(1)], ZZ) == (ZZ(1), [ZZ(1), ZZ(2), ZZ(1)]) + assert dup_primitive( + [ZZ(2), ZZ(4), ZZ(2)], ZZ) == (ZZ(2), [ZZ(1), ZZ(2), ZZ(1)]) + + assert dup_primitive([], QQ) == (QQ(0), []) + assert dup_primitive([QQ(1)], QQ) == (QQ(1), [QQ(1)]) + assert dup_primitive([QQ(1), QQ(1)], QQ) == (QQ(1), [QQ(1), QQ(1)]) + assert dup_primitive([QQ(2), QQ(2)], QQ) == (QQ(2), [QQ(1), QQ(1)]) + assert dup_primitive( + [QQ(1), QQ(2), QQ(1)], QQ) == (QQ(1), [QQ(1), QQ(2), QQ(1)]) + assert dup_primitive( + [QQ(2), QQ(4), QQ(2)], QQ) == (QQ(2), [QQ(1), QQ(2), QQ(1)]) + + assert dup_primitive( + [QQ(2, 3), QQ(4, 9)], QQ) == (QQ(2, 9), [QQ(3), QQ(2)]) + assert dup_primitive( + [QQ(2, 3), QQ(4, 5)], QQ) == (QQ(2, 15), [QQ(5), QQ(6)]) + + +def test_dmp_ground_primitive(): + assert dmp_ground_primitive([ZZ(1)], 0, ZZ) == (ZZ(1), [ZZ(1)]) + + assert dmp_ground_primitive([[]], 1, ZZ) == (ZZ(0), [[]]) + + assert dmp_ground_primitive(f_0, 2, ZZ) == (ZZ(1), f_0) + assert dmp_ground_primitive( + dmp_mul_ground(f_0, ZZ(2), 2, ZZ), 2, ZZ) == (ZZ(2), f_0) + + assert dmp_ground_primitive(f_1, 2, ZZ) == (ZZ(1), f_1) + assert dmp_ground_primitive( + dmp_mul_ground(f_1, ZZ(3), 2, ZZ), 2, ZZ) == (ZZ(3), f_1) + + assert dmp_ground_primitive(f_2, 2, ZZ) == (ZZ(1), f_2) + assert dmp_ground_primitive( + dmp_mul_ground(f_2, ZZ(4), 2, ZZ), 2, ZZ) == (ZZ(4), f_2) + + assert dmp_ground_primitive(f_3, 2, ZZ) == (ZZ(1), f_3) + assert dmp_ground_primitive( + dmp_mul_ground(f_3, ZZ(5), 2, ZZ), 2, ZZ) == (ZZ(5), f_3) + + assert dmp_ground_primitive(f_4, 2, ZZ) == (ZZ(1), f_4) + assert dmp_ground_primitive( + dmp_mul_ground(f_4, ZZ(6), 2, ZZ), 2, ZZ) == (ZZ(6), f_4) + + assert dmp_ground_primitive(f_5, 2, ZZ) == (ZZ(1), f_5) + assert dmp_ground_primitive( + dmp_mul_ground(f_5, ZZ(7), 2, ZZ), 2, ZZ) == (ZZ(7), f_5) + + assert dmp_ground_primitive(f_6, 3, ZZ) == (ZZ(1), f_6) + assert dmp_ground_primitive( + dmp_mul_ground(f_6, ZZ(8), 3, ZZ), 3, ZZ) == (ZZ(8), f_6) + + assert dmp_ground_primitive([[ZZ(2)]], 1, ZZ) == (ZZ(2), [[ZZ(1)]]) + assert dmp_ground_primitive([[QQ(2)]], 1, QQ) == (QQ(2), [[QQ(1)]]) + + assert dmp_ground_primitive( + [[QQ(2, 3)], [QQ(4, 9)]], 1, QQ) == (QQ(2, 9), [[QQ(3)], [QQ(2)]]) + assert dmp_ground_primitive( + [[QQ(2, 3)], [QQ(4, 5)]], 1, QQ) == (QQ(2, 15), [[QQ(5)], [QQ(6)]]) + + +def test_dup_extract(): + f = dup_normal([2930944, 0, 2198208, 0, 549552, 0, 45796], ZZ) + g = dup_normal([17585664, 0, 8792832, 0, 1099104, 0], ZZ) + + F = dup_normal([64, 0, 48, 0, 12, 0, 1], ZZ) + G = dup_normal([384, 0, 192, 0, 24, 0], ZZ) + + assert dup_extract(f, g, ZZ) == (45796, F, G) + + +def test_dmp_ground_extract(): + f = dmp_normal( + [[2930944], [], [2198208], [], [549552], [], [45796]], 1, ZZ) + g = dmp_normal([[17585664], [], [8792832], [], [1099104], []], 1, ZZ) + + F = dmp_normal([[64], [], [48], [], [12], [], [1]], 1, ZZ) + G = dmp_normal([[384], [], [192], [], [24], []], 1, ZZ) + + assert dmp_ground_extract(f, g, 1, ZZ) == (45796, F, G) + + +def test_dup_real_imag(): + assert dup_real_imag([], ZZ) == ([[]], [[]]) + assert dup_real_imag([1], ZZ) == ([[1]], [[]]) + + assert dup_real_imag([1, 1], ZZ) == ([[1], [1]], [[1, 0]]) + assert dup_real_imag([1, 2], ZZ) == ([[1], [2]], [[1, 0]]) + + assert dup_real_imag( + [1, 2, 3], ZZ) == ([[1], [2], [-1, 0, 3]], [[2, 0], [2, 0]]) + + assert dup_real_imag([ZZ(1), ZZ(0), ZZ(1), ZZ(3)], ZZ) == ( + [[ZZ(1)], [], [ZZ(-3), ZZ(0), ZZ(1)], [ZZ(3)]], + [[ZZ(3), ZZ(0)], [], [ZZ(-1), ZZ(0), ZZ(1), ZZ(0)]] + ) + + raises(DomainError, lambda: dup_real_imag([EX(1), EX(2)], EX)) + + + +def test_dup_mirror(): + assert dup_mirror([], ZZ) == [] + assert dup_mirror([1], ZZ) == [1] + + assert dup_mirror([1, 2, 3, 4, 5], ZZ) == [1, -2, 3, -4, 5] + assert dup_mirror([1, 2, 3, 4, 5, 6], ZZ) == [-1, 2, -3, 4, -5, 6] + + +def test_dup_scale(): + assert dup_scale([], -1, ZZ) == [] + assert dup_scale([1], -1, ZZ) == [1] + + assert dup_scale([1, 2, 3, 4, 5], -1, ZZ) == [1, -2, 3, -4, 5] + assert dup_scale([1, 2, 3, 4, 5], -7, ZZ) == [2401, -686, 147, -28, 5] + + +def test_dup_shift(): + assert dup_shift([], 1, ZZ) == [] + assert dup_shift([1], 1, ZZ) == [1] + + assert dup_shift([1, 2, 3, 4, 5], 1, ZZ) == [1, 6, 15, 20, 15] + assert dup_shift([1, 2, 3, 4, 5], 7, ZZ) == [1, 30, 339, 1712, 3267] + + +def test_dmp_shift(): + assert dmp_shift([ZZ(1), ZZ(2)], [ZZ(1)], 0, ZZ) == [ZZ(1), ZZ(3)] + + assert dmp_shift([[]], [ZZ(1), ZZ(2)], 1, ZZ) == [[]] + + xy = [[ZZ(1), ZZ(0)], []] # x*y + x1y2 = [[ZZ(1), ZZ(2)], [ZZ(1), ZZ(2)]] # (x+1)*(y+2) + assert dmp_shift(xy, [ZZ(1), ZZ(2)], 1, ZZ) == x1y2 + + +def test_dup_transform(): + assert dup_transform([], [], [1, 1], ZZ) == [] + assert dup_transform([], [1], [1, 1], ZZ) == [] + assert dup_transform([], [1, 2], [1, 1], ZZ) == [] + + assert dup_transform([6, -5, 4, -3, 17], [1, -3, 4], [2, -3], ZZ) == \ + [6, -82, 541, -2205, 6277, -12723, 17191, -13603, 4773] + + +def test_dup_compose(): + assert dup_compose([], [], ZZ) == [] + assert dup_compose([], [1], ZZ) == [] + assert dup_compose([], [1, 2], ZZ) == [] + + assert dup_compose([1], [], ZZ) == [1] + + assert dup_compose([1, 2, 0], [], ZZ) == [] + assert dup_compose([1, 2, 1], [], ZZ) == [1] + + assert dup_compose([1, 2, 1], [1], ZZ) == [4] + assert dup_compose([1, 2, 1], [7], ZZ) == [64] + + assert dup_compose([1, 2, 1], [1, -1], ZZ) == [1, 0, 0] + assert dup_compose([1, 2, 1], [1, 1], ZZ) == [1, 4, 4] + assert dup_compose([1, 2, 1], [1, 2, 1], ZZ) == [1, 4, 8, 8, 4] + + +def test_dmp_compose(): + assert dmp_compose([1, 2, 1], [1, 2, 1], 0, ZZ) == [1, 4, 8, 8, 4] + + assert dmp_compose([[[]]], [[[]]], 2, ZZ) == [[[]]] + assert dmp_compose([[[]]], [[[1]]], 2, ZZ) == [[[]]] + assert dmp_compose([[[]]], [[[1]], [[2]]], 2, ZZ) == [[[]]] + + assert dmp_compose([[[1]]], [], 2, ZZ) == [[[1]]] + + assert dmp_compose([[1], [2], [ ]], [[]], 1, ZZ) == [[]] + assert dmp_compose([[1], [2], [1]], [[]], 1, ZZ) == [[1]] + + assert dmp_compose([[1], [2], [1]], [[1]], 1, ZZ) == [[4]] + assert dmp_compose([[1], [2], [1]], [[7]], 1, ZZ) == [[64]] + + assert dmp_compose([[1], [2], [1]], [[1], [-1]], 1, ZZ) == [[1], [ ], [ ]] + assert dmp_compose([[1], [2], [1]], [[1], [ 1]], 1, ZZ) == [[1], [4], [4]] + + assert dmp_compose( + [[1], [2], [1]], [[1], [2], [1]], 1, ZZ) == [[1], [4], [8], [8], [4]] + + +def test_dup_decompose(): + assert dup_decompose([1], ZZ) == [[1]] + + assert dup_decompose([1, 0], ZZ) == [[1, 0]] + assert dup_decompose([1, 0, 0, 0], ZZ) == [[1, 0, 0, 0]] + + assert dup_decompose([1, 0, 0, 0, 0], ZZ) == [[1, 0, 0], [1, 0, 0]] + assert dup_decompose( + [1, 0, 0, 0, 0, 0, 0], ZZ) == [[1, 0, 0, 0], [1, 0, 0]] + + assert dup_decompose([7, 0, 0, 0, 1], ZZ) == [[7, 0, 1], [1, 0, 0]] + assert dup_decompose([4, 0, 3, 0, 2], ZZ) == [[4, 3, 2], [1, 0, 0]] + + f = [1, 0, 20, 0, 150, 0, 500, 0, 625, -2, 0, -10, 9] + + assert dup_decompose(f, ZZ) == [[1, 0, 0, -2, 9], [1, 0, 5, 0]] + + f = [2, 0, 40, 0, 300, 0, 1000, 0, 1250, -4, 0, -20, 18] + + assert dup_decompose(f, ZZ) == [[2, 0, 0, -4, 18], [1, 0, 5, 0]] + + f = [1, 0, 20, -8, 150, -120, 524, -600, 865, -1034, 600, -170, 29] + + assert dup_decompose(f, ZZ) == [[1, -8, 24, -34, 29], [1, 0, 5, 0]] + + R, t = ring("t", ZZ) + f = [6*t**2 - 42, + 48*t**2 + 96, + 144*t**2 + 648*t + 288, + 624*t**2 + 864*t + 384, + 108*t**3 + 312*t**2 + 432*t + 192] + + assert dup_decompose(f, R.to_domain()) == [f] + + +def test_dmp_lift(): + q = [QQ(1, 1), QQ(0, 1), QQ(1, 1)] + + f_a = [ANP([QQ(1, 1)], q, QQ), ANP([], q, QQ), ANP([], q, QQ), + ANP([QQ(1, 1), QQ(0, 1)], q, QQ), ANP([QQ(17, 1), QQ(0, 1)], q, QQ)] + + f_lift = [QQ(1), QQ(0), QQ(0), QQ(0), QQ(0), QQ(0), QQ(2), QQ(0), QQ(578), + QQ(0), QQ(0), QQ(0), QQ(1), QQ(0), QQ(-578), QQ(0), QQ(83521)] + + assert dmp_lift(f_a, 0, QQ.algebraic_field(I)) == f_lift + + f_g = [QQ_I(1), QQ_I(0), QQ_I(0), QQ_I(0, 1), QQ_I(0, 17)] + + assert dmp_lift(f_g, 0, QQ_I) == f_lift + + raises(DomainError, lambda: dmp_lift([EX(1), EX(2)], 0, EX)) + + +def test_dup_sign_variations(): + assert dup_sign_variations([], ZZ) == 0 + assert dup_sign_variations([1, 0], ZZ) == 0 + assert dup_sign_variations([1, 0, 2], ZZ) == 0 + assert dup_sign_variations([1, 0, 3, 0], ZZ) == 0 + assert dup_sign_variations([1, 0, 4, 0, 5], ZZ) == 0 + + assert dup_sign_variations([-1, 0, 2], ZZ) == 1 + assert dup_sign_variations([-1, 0, 3, 0], ZZ) == 1 + assert dup_sign_variations([-1, 0, 4, 0, 5], ZZ) == 1 + + assert dup_sign_variations([-1, -4, -5], ZZ) == 0 + assert dup_sign_variations([ 1, -4, -5], ZZ) == 1 + assert dup_sign_variations([ 1, 4, -5], ZZ) == 1 + assert dup_sign_variations([ 1, -4, 5], ZZ) == 2 + assert dup_sign_variations([-1, 4, -5], ZZ) == 2 + assert dup_sign_variations([-1, 4, 5], ZZ) == 1 + assert dup_sign_variations([-1, -4, 5], ZZ) == 1 + assert dup_sign_variations([ 1, 4, 5], ZZ) == 0 + + assert dup_sign_variations([-1, 0, -4, 0, -5], ZZ) == 0 + assert dup_sign_variations([ 1, 0, -4, 0, -5], ZZ) == 1 + assert dup_sign_variations([ 1, 0, 4, 0, -5], ZZ) == 1 + assert dup_sign_variations([ 1, 0, -4, 0, 5], ZZ) == 2 + assert dup_sign_variations([-1, 0, 4, 0, -5], ZZ) == 2 + assert dup_sign_variations([-1, 0, 4, 0, 5], ZZ) == 1 + assert dup_sign_variations([-1, 0, -4, 0, 5], ZZ) == 1 + assert dup_sign_variations([ 1, 0, 4, 0, 5], ZZ) == 0 + + +def test_dup_clear_denoms(): + assert dup_clear_denoms([], QQ, ZZ) == (ZZ(1), []) + + assert dup_clear_denoms([QQ(1)], QQ, ZZ) == (ZZ(1), [QQ(1)]) + assert dup_clear_denoms([QQ(7)], QQ, ZZ) == (ZZ(1), [QQ(7)]) + + assert dup_clear_denoms([QQ(7, 3)], QQ) == (ZZ(3), [QQ(7)]) + assert dup_clear_denoms([QQ(7, 3)], QQ, ZZ) == (ZZ(3), [QQ(7)]) + + assert dup_clear_denoms( + [QQ(3), QQ(1), QQ(0)], QQ, ZZ) == (ZZ(1), [QQ(3), QQ(1), QQ(0)]) + assert dup_clear_denoms( + [QQ(1), QQ(1, 2), QQ(0)], QQ, ZZ) == (ZZ(2), [QQ(2), QQ(1), QQ(0)]) + + assert dup_clear_denoms([QQ(3), QQ( + 1), QQ(0)], QQ, ZZ, convert=True) == (ZZ(1), [ZZ(3), ZZ(1), ZZ(0)]) + assert dup_clear_denoms([QQ(1), QQ( + 1, 2), QQ(0)], QQ, ZZ, convert=True) == (ZZ(2), [ZZ(2), ZZ(1), ZZ(0)]) + + assert dup_clear_denoms( + [EX(S(3)/2), EX(S(9)/4)], EX) == (EX(4), [EX(6), EX(9)]) + + assert dup_clear_denoms([EX(7)], EX) == (EX(1), [EX(7)]) + assert dup_clear_denoms([EX(sin(x)/x), EX(0)], EX) == (EX(x), [EX(sin(x)), EX(0)]) + + F = RR.frac_field(x) + result = dup_clear_denoms([F(8.48717/(8.0089*x + 2.83)), F(0.0)], F) + assert str(result) == "(x + 0.353356890459364, [1.05971731448763, 0.0])" + +def test_dmp_clear_denoms(): + assert dmp_clear_denoms([[]], 1, QQ, ZZ) == (ZZ(1), [[]]) + + assert dmp_clear_denoms([[QQ(1)]], 1, QQ, ZZ) == (ZZ(1), [[QQ(1)]]) + assert dmp_clear_denoms([[QQ(7)]], 1, QQ, ZZ) == (ZZ(1), [[QQ(7)]]) + + assert dmp_clear_denoms([[QQ(7, 3)]], 1, QQ) == (ZZ(3), [[QQ(7)]]) + assert dmp_clear_denoms([[QQ(7, 3)]], 1, QQ, ZZ) == (ZZ(3), [[QQ(7)]]) + + assert dmp_clear_denoms( + [[QQ(3)], [QQ(1)], []], 1, QQ, ZZ) == (ZZ(1), [[QQ(3)], [QQ(1)], []]) + assert dmp_clear_denoms([[QQ( + 1)], [QQ(1, 2)], []], 1, QQ, ZZ) == (ZZ(2), [[QQ(2)], [QQ(1)], []]) + + assert dmp_clear_denoms([QQ(3), QQ( + 1), QQ(0)], 0, QQ, ZZ, convert=True) == (ZZ(1), [ZZ(3), ZZ(1), ZZ(0)]) + assert dmp_clear_denoms([QQ(1), QQ(1, 2), QQ( + 0)], 0, QQ, ZZ, convert=True) == (ZZ(2), [ZZ(2), ZZ(1), ZZ(0)]) + + assert dmp_clear_denoms([[QQ(3)], [QQ( + 1)], []], 1, QQ, ZZ, convert=True) == (ZZ(1), [[QQ(3)], [QQ(1)], []]) + assert dmp_clear_denoms([[QQ(1)], [QQ(1, 2)], []], 1, QQ, ZZ, + convert=True) == (ZZ(2), [[QQ(2)], [QQ(1)], []]) + + assert dmp_clear_denoms( + [[EX(S(3)/2)], [EX(S(9)/4)]], 1, EX) == (EX(4), [[EX(6)], [EX(9)]]) + assert dmp_clear_denoms([[EX(7)]], 1, EX) == (EX(1), [[EX(7)]]) + assert dmp_clear_denoms([[EX(sin(x)/x), EX(0)]], 1, EX) == (EX(x), [[EX(sin(x)), EX(0)]]) diff --git a/lib/python3.12/site-packages/sympy/polys/tests/test_dispersion.py b/lib/python3.12/site-packages/sympy/polys/tests/test_dispersion.py new file mode 100644 index 0000000000000000000000000000000000000000..5fc4c078bd4b0e1d89add93979787ec7b40899b1 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/polys/tests/test_dispersion.py @@ -0,0 +1,95 @@ +from sympy.core import Symbol, S, oo +from sympy.functions.elementary.miscellaneous import sqrt +from sympy.polys import poly +from sympy.polys.dispersion import dispersion, dispersionset + + +def test_dispersion(): + x = Symbol("x") + a = Symbol("a") + + fp = poly(S.Zero, x) + assert sorted(dispersionset(fp)) == [0] + + fp = poly(S(2), x) + assert sorted(dispersionset(fp)) == [0] + + fp = poly(x + 1, x) + assert sorted(dispersionset(fp)) == [0] + assert dispersion(fp) == 0 + + fp = poly((x + 1)*(x + 2), x) + assert sorted(dispersionset(fp)) == [0, 1] + assert dispersion(fp) == 1 + + fp = poly(x*(x + 3), x) + assert sorted(dispersionset(fp)) == [0, 3] + assert dispersion(fp) == 3 + + fp = poly((x - 3)*(x + 3), x) + assert sorted(dispersionset(fp)) == [0, 6] + assert dispersion(fp) == 6 + + fp = poly(x**4 - 3*x**2 + 1, x) + gp = fp.shift(-3) + assert sorted(dispersionset(fp, gp)) == [2, 3, 4] + assert dispersion(fp, gp) == 4 + assert sorted(dispersionset(gp, fp)) == [] + assert dispersion(gp, fp) is -oo + + fp = poly(x*(3*x**2+a)*(x-2536)*(x**3+a), x) + gp = fp.as_expr().subs(x, x-345).as_poly(x) + assert sorted(dispersionset(fp, gp)) == [345, 2881] + assert sorted(dispersionset(gp, fp)) == [2191] + + gp = poly((x-2)**2*(x-3)**3*(x-5)**3, x) + assert sorted(dispersionset(gp)) == [0, 1, 2, 3] + assert sorted(dispersionset(gp, (gp+4)**2)) == [1, 2] + + fp = poly(x*(x+2)*(x-1), x) + assert sorted(dispersionset(fp)) == [0, 1, 2, 3] + + fp = poly(x**2 + sqrt(5)*x - 1, x, domain='QQ') + gp = poly(x**2 + (2 + sqrt(5))*x + sqrt(5), x, domain='QQ') + assert sorted(dispersionset(fp, gp)) == [2] + assert sorted(dispersionset(gp, fp)) == [1, 4] + + # There are some difficulties if we compute over Z[a] + # and alpha happenes to lie in Z[a] instead of simply Z. + # Hence we can not decide if alpha is indeed integral + # in general. + + fp = poly(4*x**4 + (4*a + 8)*x**3 + (a**2 + 6*a + 4)*x**2 + (a**2 + 2*a)*x, x) + assert sorted(dispersionset(fp)) == [0, 1] + + # For any specific value of a, the dispersion is 3*a + # but the algorithm can not find this in general. + # This is the point where the resultant based Ansatz + # is superior to the current one. + fp = poly(a**2*x**3 + (a**3 + a**2 + a + 1)*x, x) + gp = fp.as_expr().subs(x, x - 3*a).as_poly(x) + assert sorted(dispersionset(fp, gp)) == [] + + fpa = fp.as_expr().subs(a, 2).as_poly(x) + gpa = gp.as_expr().subs(a, 2).as_poly(x) + assert sorted(dispersionset(fpa, gpa)) == [6] + + # Work with Expr instead of Poly + f = (x + 1)*(x + 2) + assert sorted(dispersionset(f)) == [0, 1] + assert dispersion(f) == 1 + + f = x**4 - 3*x**2 + 1 + g = x**4 - 12*x**3 + 51*x**2 - 90*x + 55 + assert sorted(dispersionset(f, g)) == [2, 3, 4] + assert dispersion(f, g) == 4 + + # Work with Expr and specify a generator + f = (x + 1)*(x + 2) + assert sorted(dispersionset(f, None, x)) == [0, 1] + assert dispersion(f, None, x) == 1 + + f = x**4 - 3*x**2 + 1 + g = x**4 - 12*x**3 + 51*x**2 - 90*x + 55 + assert sorted(dispersionset(f, g, x)) == [2, 3, 4] + assert dispersion(f, g, x) == 4 diff --git a/lib/python3.12/site-packages/sympy/polys/tests/test_distributedmodules.py b/lib/python3.12/site-packages/sympy/polys/tests/test_distributedmodules.py new file mode 100644 index 0000000000000000000000000000000000000000..c95672f99f878f3def660aadec901afbde9adf8b --- /dev/null +++ b/lib/python3.12/site-packages/sympy/polys/tests/test_distributedmodules.py @@ -0,0 +1,208 @@ +"""Tests for sparse distributed modules. """ + +from sympy.polys.distributedmodules import ( + sdm_monomial_mul, sdm_monomial_deg, sdm_monomial_divides, + sdm_add, sdm_LM, sdm_LT, sdm_mul_term, sdm_zero, sdm_deg, + sdm_LC, sdm_from_dict, + sdm_spoly, sdm_ecart, sdm_nf_mora, sdm_groebner, + sdm_from_vector, sdm_to_vector, sdm_monomial_lcm +) + +from sympy.polys.orderings import lex, grlex, InverseOrder +from sympy.polys.domains import QQ + +from sympy.abc import x, y, z + + +def test_sdm_monomial_mul(): + assert sdm_monomial_mul((1, 1, 0), (1, 3)) == (1, 2, 3) + + +def test_sdm_monomial_deg(): + assert sdm_monomial_deg((5, 2, 1)) == 3 + + +def test_sdm_monomial_lcm(): + assert sdm_monomial_lcm((1, 2, 3), (1, 5, 0)) == (1, 5, 3) + + +def test_sdm_monomial_divides(): + assert sdm_monomial_divides((1, 0, 0), (1, 0, 0)) is True + assert sdm_monomial_divides((1, 0, 0), (1, 2, 1)) is True + assert sdm_monomial_divides((5, 1, 1), (5, 2, 1)) is True + + assert sdm_monomial_divides((1, 0, 0), (2, 0, 0)) is False + assert sdm_monomial_divides((1, 1, 0), (1, 0, 0)) is False + assert sdm_monomial_divides((5, 1, 2), (5, 0, 1)) is False + + +def test_sdm_LC(): + assert sdm_LC([((1, 2, 3), QQ(5))], QQ) == QQ(5) + + +def test_sdm_from_dict(): + dic = {(1, 2, 1, 1): QQ(1), (1, 1, 2, 1): QQ(1), (1, 0, 2, 1): QQ(1), + (1, 0, 0, 3): QQ(1), (1, 1, 1, 0): QQ(1)} + assert sdm_from_dict(dic, grlex) == \ + [((1, 2, 1, 1), QQ(1)), ((1, 1, 2, 1), QQ(1)), + ((1, 0, 2, 1), QQ(1)), ((1, 0, 0, 3), QQ(1)), ((1, 1, 1, 0), QQ(1))] + +# TODO test to_dict? + + +def test_sdm_add(): + assert sdm_add([((1, 1, 1), QQ(1))], [((2, 0, 0), QQ(1))], lex, QQ) == \ + [((2, 0, 0), QQ(1)), ((1, 1, 1), QQ(1))] + assert sdm_add([((1, 1, 1), QQ(1))], [((1, 1, 1), QQ(-1))], lex, QQ) == [] + assert sdm_add([((1, 0, 0), QQ(1))], [((1, 0, 0), QQ(2))], lex, QQ) == \ + [((1, 0, 0), QQ(3))] + assert sdm_add([((1, 0, 1), QQ(1))], [((1, 1, 0), QQ(1))], lex, QQ) == \ + [((1, 1, 0), QQ(1)), ((1, 0, 1), QQ(1))] + + +def test_sdm_LM(): + dic = {(1, 2, 3): QQ(1), (4, 0, 0): QQ(1), (4, 0, 1): QQ(1)} + assert sdm_LM(sdm_from_dict(dic, lex)) == (4, 0, 1) + + +def test_sdm_LT(): + dic = {(1, 2, 3): QQ(1), (4, 0, 0): QQ(2), (4, 0, 1): QQ(3)} + assert sdm_LT(sdm_from_dict(dic, lex)) == ((4, 0, 1), QQ(3)) + + +def test_sdm_mul_term(): + assert sdm_mul_term([((1, 0, 0), QQ(1))], ((0, 0), QQ(0)), lex, QQ) == [] + assert sdm_mul_term([], ((1, 0), QQ(1)), lex, QQ) == [] + assert sdm_mul_term([((1, 0, 0), QQ(1))], ((1, 0), QQ(1)), lex, QQ) == \ + [((1, 1, 0), QQ(1))] + f = [((2, 0, 1), QQ(4)), ((1, 1, 0), QQ(3))] + assert sdm_mul_term(f, ((1, 1), QQ(2)), lex, QQ) == \ + [((2, 1, 2), QQ(8)), ((1, 2, 1), QQ(6))] + + +def test_sdm_zero(): + assert sdm_zero() == [] + + +def test_sdm_deg(): + assert sdm_deg([((1, 2, 3), 1), ((10, 0, 1), 1), ((2, 3, 4), 4)]) == 7 + + +def test_sdm_spoly(): + f = [((2, 1, 1), QQ(1)), ((1, 0, 1), QQ(1))] + g = [((2, 3, 0), QQ(1))] + h = [((1, 2, 3), QQ(1))] + assert sdm_spoly(f, h, lex, QQ) == [] + assert sdm_spoly(f, g, lex, QQ) == [((1, 2, 1), QQ(1))] + + +def test_sdm_ecart(): + assert sdm_ecart([((1, 2, 3), 1), ((1, 0, 1), 1)]) == 0 + assert sdm_ecart([((2, 2, 1), 1), ((1, 5, 1), 1)]) == 3 + + +def test_sdm_nf_mora(): + f = sdm_from_dict({(1, 2, 1, 1): QQ(1), (1, 1, 2, 1): QQ(1), + (1, 0, 2, 1): QQ(1), (1, 0, 0, 3): QQ(1), (1, 1, 1, 0): QQ(1)}, + grlex) + f1 = sdm_from_dict({(1, 1, 1, 0): QQ(1), (1, 0, 2, 0): QQ(1), + (1, 0, 0, 0): QQ(-1)}, grlex) + f2 = sdm_from_dict({(1, 1, 1, 0): QQ(1)}, grlex) + (id0, id1, id2) = [sdm_from_dict({(i, 0, 0, 0): QQ(1)}, grlex) + for i in range(3)] + + assert sdm_nf_mora(f, [f1, f2], grlex, QQ, phantom=(id0, [id1, id2])) == \ + ([((1, 0, 2, 1), QQ(1)), ((1, 0, 0, 3), QQ(1)), ((1, 1, 1, 0), QQ(1)), + ((1, 1, 0, 1), QQ(1))], + [((1, 1, 0, 1), QQ(-1)), ((0, 0, 0, 0), QQ(1))]) + assert sdm_nf_mora(f, [f2, f1], grlex, QQ, phantom=(id0, [id2, id1])) == \ + ([((1, 0, 2, 1), QQ(1)), ((1, 0, 0, 3), QQ(1)), ((1, 1, 1, 0), QQ(1))], + [((2, 1, 0, 1), QQ(-1)), ((2, 0, 1, 1), QQ(-1)), ((0, 0, 0, 0), QQ(1))]) + + f = sdm_from_vector([x*z, y**2 + y*z - z, y], lex, QQ, gens=[x, y, z]) + f1 = sdm_from_vector([x, y, 1], lex, QQ, gens=[x, y, z]) + f2 = sdm_from_vector([x*y, z, z**2], lex, QQ, gens=[x, y, z]) + assert sdm_nf_mora(f, [f1, f2], lex, QQ) == \ + sdm_nf_mora(f, [f2, f1], lex, QQ) == \ + [((1, 0, 1, 1), QQ(1)), ((1, 0, 0, 1), QQ(-1)), ((0, 1, 1, 0), QQ(-1)), + ((0, 1, 0, 1), QQ(1))] + + +def test_conversion(): + f = [x**2 + y**2, 2*z] + g = [((1, 0, 0, 1), QQ(2)), ((0, 2, 0, 0), QQ(1)), ((0, 0, 2, 0), QQ(1))] + assert sdm_to_vector(g, [x, y, z], QQ) == f + assert sdm_from_vector(f, lex, QQ) == g + assert sdm_from_vector( + [x, 1], lex, QQ) == [((1, 0), QQ(1)), ((0, 1), QQ(1))] + assert sdm_to_vector([((1, 1, 0, 0), 1)], [x, y, z], QQ, n=3) == [0, x, 0] + assert sdm_from_vector([0, 0], lex, QQ, gens=[x, y]) == sdm_zero() + + +def test_nontrivial(): + gens = [x, y, z] + + def contains(I, f): + S = [sdm_from_vector([g], lex, QQ, gens=gens) for g in I] + G = sdm_groebner(S, sdm_nf_mora, lex, QQ) + return sdm_nf_mora(sdm_from_vector([f], lex, QQ, gens=gens), + G, lex, QQ) == sdm_zero() + + assert contains([x, y], x) + assert contains([x, y], x + y) + assert not contains([x, y], 1) + assert not contains([x, y], z) + assert contains([x**2 + y, x**2 + x], x - y) + assert not contains([x + y + z, x*y + x*z + y*z, x*y*z], x**2) + assert contains([x + y + z, x*y + x*z + y*z, x*y*z], x**3) + assert contains([x + y + z, x*y + x*z + y*z, x*y*z], x**4) + assert not contains([x + y + z, x*y + x*z + y*z, x*y*z], x*y**2) + assert contains([x + y + z, x*y + x*z + y*z, x*y*z], x**4 + y**3 + 2*z*y*x) + assert contains([x + y + z, x*y + x*z + y*z, x*y*z], x*y*z) + assert contains([x, 1 + x + y, 5 - 7*y], 1) + assert contains( + [x**3 + y**3, y**3 + z**3, z**3 + x**3, x**2*y + x**2*z + y**2*z], + x**3) + assert not contains( + [x**3 + y**3, y**3 + z**3, z**3 + x**3, x**2*y + x**2*z + y**2*z], + x**2 + y**2) + + # compare local order + assert not contains([x*(1 + x + y), y*(1 + z)], x) + assert not contains([x*(1 + x + y), y*(1 + z)], x + y) + + +def test_local(): + igrlex = InverseOrder(grlex) + gens = [x, y, z] + + def contains(I, f): + S = [sdm_from_vector([g], igrlex, QQ, gens=gens) for g in I] + G = sdm_groebner(S, sdm_nf_mora, igrlex, QQ) + return sdm_nf_mora(sdm_from_vector([f], lex, QQ, gens=gens), + G, lex, QQ) == sdm_zero() + assert contains([x, y], x) + assert contains([x, y], x + y) + assert not contains([x, y], 1) + assert not contains([x, y], z) + assert contains([x**2 + y, x**2 + x], x - y) + assert not contains([x + y + z, x*y + x*z + y*z, x*y*z], x**2) + assert contains([x*(1 + x + y), y*(1 + z)], x) + assert contains([x*(1 + x + y), y*(1 + z)], x + y) + + +def test_uncovered_line(): + gens = [x, y] + f1 = sdm_zero() + f2 = sdm_from_vector([x, 0], lex, QQ, gens=gens) + f3 = sdm_from_vector([0, y], lex, QQ, gens=gens) + + assert sdm_spoly(f1, f2, lex, QQ) == sdm_zero() + assert sdm_spoly(f3, f2, lex, QQ) == sdm_zero() + + +def test_chain_criterion(): + gens = [x] + f1 = sdm_from_vector([1, x], grlex, QQ, gens=gens) + f2 = sdm_from_vector([0, x - 2], grlex, QQ, gens=gens) + assert len(sdm_groebner([f1, f2], sdm_nf_mora, grlex, QQ)) == 2 diff --git a/lib/python3.12/site-packages/sympy/polys/tests/test_euclidtools.py b/lib/python3.12/site-packages/sympy/polys/tests/test_euclidtools.py new file mode 100644 index 0000000000000000000000000000000000000000..3061be73f987163951a5836ff50125d29abc60c7 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/polys/tests/test_euclidtools.py @@ -0,0 +1,712 @@ +"""Tests for Euclidean algorithms, GCDs, LCMs and polynomial remainder sequences. """ + +from sympy.polys.rings import ring +from sympy.polys.domains import ZZ, QQ, RR + +from sympy.polys.specialpolys import ( + f_polys, + dmp_fateman_poly_F_1, + dmp_fateman_poly_F_2, + dmp_fateman_poly_F_3) + +f_0, f_1, f_2, f_3, f_4, f_5, f_6 = f_polys() + +def test_dup_gcdex(): + R, x = ring("x", QQ) + + f = x**4 - 2*x**3 - 6*x**2 + 12*x + 15 + g = x**3 + x**2 - 4*x - 4 + + s = -QQ(1,5)*x + QQ(3,5) + t = QQ(1,5)*x**2 - QQ(6,5)*x + 2 + h = x + 1 + + assert R.dup_half_gcdex(f, g) == (s, h) + assert R.dup_gcdex(f, g) == (s, t, h) + + f = x**4 + 4*x**3 - x + 1 + g = x**3 - x + 1 + + s, t, h = R.dup_gcdex(f, g) + S, T, H = R.dup_gcdex(g, f) + + assert R.dup_add(R.dup_mul(s, f), + R.dup_mul(t, g)) == h + assert R.dup_add(R.dup_mul(S, g), + R.dup_mul(T, f)) == H + + f = 2*x + g = x**2 - 16 + + s = QQ(1,32)*x + t = -QQ(1,16) + h = 1 + + assert R.dup_half_gcdex(f, g) == (s, h) + assert R.dup_gcdex(f, g) == (s, t, h) + + +def test_dup_invert(): + R, x = ring("x", QQ) + assert R.dup_invert(2*x, x**2 - 16) == QQ(1,32)*x + + +def test_dup_euclidean_prs(): + R, x = ring("x", QQ) + + f = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 + g = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 + + assert R.dup_euclidean_prs(f, g) == [ + f, + g, + -QQ(5,9)*x**4 + QQ(1,9)*x**2 - QQ(1,3), + -QQ(117,25)*x**2 - 9*x + QQ(441,25), + QQ(233150,19773)*x - QQ(102500,6591), + -QQ(1288744821,543589225)] + + +def test_dup_primitive_prs(): + R, x = ring("x", ZZ) + + f = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 + g = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 + + assert R.dup_primitive_prs(f, g) == [ + f, + g, + -5*x**4 + x**2 - 3, + 13*x**2 + 25*x - 49, + 4663*x - 6150, + 1] + + +def test_dup_subresultants(): + R, x = ring("x", ZZ) + + assert R.dup_resultant(0, 0) == 0 + + assert R.dup_resultant(1, 0) == 0 + assert R.dup_resultant(0, 1) == 0 + + f = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 + g = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 + + a = 15*x**4 - 3*x**2 + 9 + b = 65*x**2 + 125*x - 245 + c = 9326*x - 12300 + d = 260708 + + assert R.dup_subresultants(f, g) == [f, g, a, b, c, d] + assert R.dup_resultant(f, g) == R.dup_LC(d) + + f = x**2 - 2*x + 1 + g = x**2 - 1 + + a = 2*x - 2 + + assert R.dup_subresultants(f, g) == [f, g, a] + assert R.dup_resultant(f, g) == 0 + + f = x**2 + 1 + g = x**2 - 1 + + a = -2 + + assert R.dup_subresultants(f, g) == [f, g, a] + assert R.dup_resultant(f, g) == 4 + + f = x**2 - 1 + g = x**3 - x**2 + 2 + + assert R.dup_resultant(f, g) == 0 + + f = 3*x**3 - x + g = 5*x**2 + 1 + + assert R.dup_resultant(f, g) == 64 + + f = x**2 - 2*x + 7 + g = x**3 - x + 5 + + assert R.dup_resultant(f, g) == 265 + + f = x**3 - 6*x**2 + 11*x - 6 + g = x**3 - 15*x**2 + 74*x - 120 + + assert R.dup_resultant(f, g) == -8640 + + f = x**3 - 6*x**2 + 11*x - 6 + g = x**3 - 10*x**2 + 29*x - 20 + + assert R.dup_resultant(f, g) == 0 + + f = x**3 - 1 + g = x**3 + 2*x**2 + 2*x - 1 + + assert R.dup_resultant(f, g) == 16 + + f = x**8 - 2 + g = x - 1 + + assert R.dup_resultant(f, g) == -1 + + +def test_dmp_subresultants(): + R, x, y = ring("x,y", ZZ) + + assert R.dmp_resultant(0, 0) == 0 + assert R.dmp_prs_resultant(0, 0)[0] == 0 + assert R.dmp_zz_collins_resultant(0, 0) == 0 + assert R.dmp_qq_collins_resultant(0, 0) == 0 + + assert R.dmp_resultant(1, 0) == 0 + assert R.dmp_resultant(1, 0) == 0 + assert R.dmp_resultant(1, 0) == 0 + + assert R.dmp_resultant(0, 1) == 0 + assert R.dmp_prs_resultant(0, 1)[0] == 0 + assert R.dmp_zz_collins_resultant(0, 1) == 0 + assert R.dmp_qq_collins_resultant(0, 1) == 0 + + f = 3*x**2*y - y**3 - 4 + g = x**2 + x*y**3 - 9 + + a = 3*x*y**4 + y**3 - 27*y + 4 + b = -3*y**10 - 12*y**7 + y**6 - 54*y**4 + 8*y**3 + 729*y**2 - 216*y + 16 + + r = R.dmp_LC(b) + + assert R.dmp_subresultants(f, g) == [f, g, a, b] + + assert R.dmp_resultant(f, g) == r + assert R.dmp_prs_resultant(f, g)[0] == r + assert R.dmp_zz_collins_resultant(f, g) == r + assert R.dmp_qq_collins_resultant(f, g) == r + + f = -x**3 + 5 + g = 3*x**2*y + x**2 + + a = 45*y**2 + 30*y + 5 + b = 675*y**3 + 675*y**2 + 225*y + 25 + + r = R.dmp_LC(b) + + assert R.dmp_subresultants(f, g) == [f, g, a] + assert R.dmp_resultant(f, g) == r + assert R.dmp_prs_resultant(f, g)[0] == r + assert R.dmp_zz_collins_resultant(f, g) == r + assert R.dmp_qq_collins_resultant(f, g) == r + + R, x, y, z, u, v = ring("x,y,z,u,v", ZZ) + + f = 6*x**2 - 3*x*y - 2*x*z + y*z + g = x**2 - x*u - x*v + u*v + + r = y**2*z**2 - 3*y**2*z*u - 3*y**2*z*v + 9*y**2*u*v - 2*y*z**2*u \ + - 2*y*z**2*v + 6*y*z*u**2 + 12*y*z*u*v + 6*y*z*v**2 - 18*y*u**2*v \ + - 18*y*u*v**2 + 4*z**2*u*v - 12*z*u**2*v - 12*z*u*v**2 + 36*u**2*v**2 + + assert R.dmp_zz_collins_resultant(f, g) == r.drop(x) + + R, x, y, z, u, v = ring("x,y,z,u,v", QQ) + + f = x**2 - QQ(1,2)*x*y - QQ(1,3)*x*z + QQ(1,6)*y*z + g = x**2 - x*u - x*v + u*v + + r = QQ(1,36)*y**2*z**2 - QQ(1,12)*y**2*z*u - QQ(1,12)*y**2*z*v + QQ(1,4)*y**2*u*v \ + - QQ(1,18)*y*z**2*u - QQ(1,18)*y*z**2*v + QQ(1,6)*y*z*u**2 + QQ(1,3)*y*z*u*v \ + + QQ(1,6)*y*z*v**2 - QQ(1,2)*y*u**2*v - QQ(1,2)*y*u*v**2 + QQ(1,9)*z**2*u*v \ + - QQ(1,3)*z*u**2*v - QQ(1,3)*z*u*v**2 + u**2*v**2 + + assert R.dmp_qq_collins_resultant(f, g) == r.drop(x) + + Rt, t = ring("t", ZZ) + Rx, x = ring("x", Rt) + + f = x**6 - 5*x**4 + 5*x**2 + 4 + g = -6*t*x**5 + x**4 + 20*t*x**3 - 3*x**2 - 10*t*x + 6 + + assert Rx.dup_resultant(f, g) == 2930944*t**6 + 2198208*t**4 + 549552*t**2 + 45796 + + +def test_dup_discriminant(): + R, x = ring("x", ZZ) + + assert R.dup_discriminant(0) == 0 + assert R.dup_discriminant(x) == 1 + + assert R.dup_discriminant(x**3 + 3*x**2 + 9*x - 13) == -11664 + assert R.dup_discriminant(5*x**5 + x**3 + 2) == 31252160 + assert R.dup_discriminant(x**4 + 2*x**3 + 6*x**2 - 22*x + 13) == 0 + assert R.dup_discriminant(12*x**7 + 15*x**4 + 30*x**3 + x**2 + 1) == -220289699947514112 + + +def test_dmp_discriminant(): + R, x = ring("x", ZZ) + + assert R.dmp_discriminant(0) == 0 + + R, x, y = ring("x,y", ZZ) + + assert R.dmp_discriminant(0) == 0 + assert R.dmp_discriminant(y) == 0 + + assert R.dmp_discriminant(x**3 + 3*x**2 + 9*x - 13) == -11664 + assert R.dmp_discriminant(5*x**5 + x**3 + 2) == 31252160 + assert R.dmp_discriminant(x**4 + 2*x**3 + 6*x**2 - 22*x + 13) == 0 + assert R.dmp_discriminant(12*x**7 + 15*x**4 + 30*x**3 + x**2 + 1) == -220289699947514112 + + assert R.dmp_discriminant(x**2*y + 2*y) == (-8*y**2).drop(x) + assert R.dmp_discriminant(x*y**2 + 2*x) == 1 + + R, x, y, z = ring("x,y,z", ZZ) + assert R.dmp_discriminant(x*y + z) == 1 + + R, x, y, z, u = ring("x,y,z,u", ZZ) + assert R.dmp_discriminant(x**2*y + x*z + u) == (-4*y*u + z**2).drop(x) + + R, x, y, z, u, v = ring("x,y,z,u,v", ZZ) + assert R.dmp_discriminant(x**3*y + x**2*z + x*u + v) == \ + (-27*y**2*v**2 + 18*y*z*u*v - 4*y*u**3 - 4*z**3*v + z**2*u**2).drop(x) + + +def test_dup_gcd(): + R, x = ring("x", ZZ) + + f, g = 0, 0 + assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (0, 0, 0) + + f, g = 2, 0 + assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2, 1, 0) + + f, g = -2, 0 + assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2, -1, 0) + + f, g = 0, -2 + assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2, 0, -1) + + f, g = 0, 2*x + 4 + assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2*x + 4, 0, 1) + + f, g = 2*x + 4, 0 + assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2*x + 4, 1, 0) + + f, g = 2, 2 + assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2, 1, 1) + + f, g = -2, 2 + assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2, -1, 1) + + f, g = 2, -2 + assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2, 1, -1) + + f, g = -2, -2 + assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2, -1, -1) + + f, g = x**2 + 2*x + 1, 1 + assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (1, x**2 + 2*x + 1, 1) + + f, g = x**2 + 2*x + 1, 2 + assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (1, x**2 + 2*x + 1, 2) + + f, g = 2*x**2 + 4*x + 2, 2 + assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2, x**2 + 2*x + 1, 1) + + f, g = 2, 2*x**2 + 4*x + 2 + assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2, 1, x**2 + 2*x + 1) + + f, g = 2*x**2 + 4*x + 2, x + 1 + assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (x + 1, 2*x + 2, 1) + + f, g = x + 1, 2*x**2 + 4*x + 2 + assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (x + 1, 1, 2*x + 2) + + f, g = x - 31, x + assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (1, f, g) + + f = x**4 + 8*x**3 + 21*x**2 + 22*x + 8 + g = x**3 + 6*x**2 + 11*x + 6 + + h = x**2 + 3*x + 2 + + cff = x**2 + 5*x + 4 + cfg = x + 3 + + assert R.dup_zz_heu_gcd(f, g) == (h, cff, cfg) + assert R.dup_rr_prs_gcd(f, g) == (h, cff, cfg) + + f = x**4 - 4 + g = x**4 + 4*x**2 + 4 + + h = x**2 + 2 + + cff = x**2 - 2 + cfg = x**2 + 2 + + assert R.dup_zz_heu_gcd(f, g) == (h, cff, cfg) + assert R.dup_rr_prs_gcd(f, g) == (h, cff, cfg) + + f = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 + g = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 + + h = 1 + + cff = f + cfg = g + + assert R.dup_zz_heu_gcd(f, g) == (h, cff, cfg) + assert R.dup_rr_prs_gcd(f, g) == (h, cff, cfg) + + R, x = ring("x", QQ) + + f = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 + g = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 + + h = 1 + + cff = f + cfg = g + + assert R.dup_qq_heu_gcd(f, g) == (h, cff, cfg) + assert R.dup_ff_prs_gcd(f, g) == (h, cff, cfg) + + R, x = ring("x", ZZ) + + f = - 352518131239247345597970242177235495263669787845475025293906825864749649589178600387510272*x**49 \ + + 46818041807522713962450042363465092040687472354933295397472942006618953623327997952*x**42 \ + + 378182690892293941192071663536490788434899030680411695933646320291525827756032*x**35 \ + + 112806468807371824947796775491032386836656074179286744191026149539708928*x**28 \ + - 12278371209708240950316872681744825481125965781519138077173235712*x**21 \ + + 289127344604779611146960547954288113529690984687482920704*x**14 \ + + 19007977035740498977629742919480623972236450681*x**7 \ + + 311973482284542371301330321821976049 + + g = 365431878023781158602430064717380211405897160759702125019136*x**21 \ + + 197599133478719444145775798221171663643171734081650688*x**14 \ + - 9504116979659010018253915765478924103928886144*x**7 \ + - 311973482284542371301330321821976049 + + assert R.dup_zz_heu_gcd(f, R.dup_diff(f, 1))[0] == g + assert R.dup_rr_prs_gcd(f, R.dup_diff(f, 1))[0] == g + + R, x = ring("x", QQ) + + f = QQ(1,2)*x**2 + x + QQ(1,2) + g = QQ(1,2)*x + QQ(1,2) + + h = x + 1 + + assert R.dup_qq_heu_gcd(f, g) == (h, g, QQ(1,2)) + assert R.dup_ff_prs_gcd(f, g) == (h, g, QQ(1,2)) + + R, x = ring("x", ZZ) + + f = 1317378933230047068160*x + 2945748836994210856960 + g = 120352542776360960*x + 269116466014453760 + + h = 120352542776360960*x + 269116466014453760 + cff = 10946 + cfg = 1 + + assert R.dup_zz_heu_gcd(f, g) == (h, cff, cfg) + + +def test_dmp_gcd(): + R, x, y = ring("x,y", ZZ) + + f, g = 0, 0 + assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (0, 0, 0) + + f, g = 2, 0 + assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2, 1, 0) + + f, g = -2, 0 + assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2, -1, 0) + + f, g = 0, -2 + assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2, 0, -1) + + f, g = 0, 2*x + 4 + assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2*x + 4, 0, 1) + + f, g = 2*x + 4, 0 + assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2*x + 4, 1, 0) + + f, g = 2, 2 + assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2, 1, 1) + + f, g = -2, 2 + assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2, -1, 1) + + f, g = 2, -2 + assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2, 1, -1) + + f, g = -2, -2 + assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2, -1, -1) + + f, g = x**2 + 2*x + 1, 1 + assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (1, x**2 + 2*x + 1, 1) + + f, g = x**2 + 2*x + 1, 2 + assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (1, x**2 + 2*x + 1, 2) + + f, g = 2*x**2 + 4*x + 2, 2 + assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2, x**2 + 2*x + 1, 1) + + f, g = 2, 2*x**2 + 4*x + 2 + assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2, 1, x**2 + 2*x + 1) + + f, g = 2*x**2 + 4*x + 2, x + 1 + assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (x + 1, 2*x + 2, 1) + + f, g = x + 1, 2*x**2 + 4*x + 2 + assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (x + 1, 1, 2*x + 2) + + R, x, y, z, u = ring("x,y,z,u", ZZ) + + f, g = u**2 + 2*u + 1, 2*u + 2 + assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (u + 1, u + 1, 2) + + f, g = z**2*u**2 + 2*z**2*u + z**2 + z*u + z, u**2 + 2*u + 1 + h, cff, cfg = u + 1, z**2*u + z**2 + z, u + 1 + + assert R.dmp_zz_heu_gcd(f, g) == (h, cff, cfg) + assert R.dmp_rr_prs_gcd(f, g) == (h, cff, cfg) + + assert R.dmp_zz_heu_gcd(g, f) == (h, cfg, cff) + assert R.dmp_rr_prs_gcd(g, f) == (h, cfg, cff) + + R, x, y, z = ring("x,y,z", ZZ) + + f, g, h = map(R.from_dense, dmp_fateman_poly_F_1(2, ZZ)) + H, cff, cfg = R.dmp_zz_heu_gcd(f, g) + + assert H == h and R.dmp_mul(H, cff) == f \ + and R.dmp_mul(H, cfg) == g + + H, cff, cfg = R.dmp_rr_prs_gcd(f, g) + + assert H == h and R.dmp_mul(H, cff) == f \ + and R.dmp_mul(H, cfg) == g + + R, x, y, z, u, v = ring("x,y,z,u,v", ZZ) + + f, g, h = map(R.from_dense, dmp_fateman_poly_F_1(4, ZZ)) + H, cff, cfg = R.dmp_zz_heu_gcd(f, g) + + assert H == h and R.dmp_mul(H, cff) == f \ + and R.dmp_mul(H, cfg) == g + + R, x, y, z, u, v, a, b = ring("x,y,z,u,v,a,b", ZZ) + + f, g, h = map(R.from_dense, dmp_fateman_poly_F_1(6, ZZ)) + H, cff, cfg = R.dmp_zz_heu_gcd(f, g) + + assert H == h and R.dmp_mul(H, cff) == f \ + and R.dmp_mul(H, cfg) == g + + R, x, y, z, u, v, a, b, c, d = ring("x,y,z,u,v,a,b,c,d", ZZ) + + f, g, h = map(R.from_dense, dmp_fateman_poly_F_1(8, ZZ)) + H, cff, cfg = R.dmp_zz_heu_gcd(f, g) + + assert H == h and R.dmp_mul(H, cff) == f \ + and R.dmp_mul(H, cfg) == g + + R, x, y, z = ring("x,y,z", ZZ) + + f, g, h = map(R.from_dense, dmp_fateman_poly_F_2(2, ZZ)) + H, cff, cfg = R.dmp_zz_heu_gcd(f, g) + + assert H == h and R.dmp_mul(H, cff) == f \ + and R.dmp_mul(H, cfg) == g + + H, cff, cfg = R.dmp_rr_prs_gcd(f, g) + + assert H == h and R.dmp_mul(H, cff) == f \ + and R.dmp_mul(H, cfg) == g + + f, g, h = map(R.from_dense, dmp_fateman_poly_F_3(2, ZZ)) + H, cff, cfg = R.dmp_zz_heu_gcd(f, g) + + assert H == h and R.dmp_mul(H, cff) == f \ + and R.dmp_mul(H, cfg) == g + + H, cff, cfg = R.dmp_rr_prs_gcd(f, g) + + assert H == h and R.dmp_mul(H, cff) == f \ + and R.dmp_mul(H, cfg) == g + + R, x, y, z, u, v = ring("x,y,z,u,v", ZZ) + + f, g, h = map(R.from_dense, dmp_fateman_poly_F_3(4, ZZ)) + H, cff, cfg = R.dmp_inner_gcd(f, g) + + assert H == h and R.dmp_mul(H, cff) == f \ + and R.dmp_mul(H, cfg) == g + + R, x, y = ring("x,y", QQ) + + f = QQ(1,2)*x**2 + x + QQ(1,2) + g = QQ(1,2)*x + QQ(1,2) + + h = x + 1 + + assert R.dmp_qq_heu_gcd(f, g) == (h, g, QQ(1,2)) + assert R.dmp_ff_prs_gcd(f, g) == (h, g, QQ(1,2)) + + R, x, y = ring("x,y", RR) + + f = 2.1*x*y**2 - 2.2*x*y + 2.1*x + g = 1.0*x**3 + + assert R.dmp_ff_prs_gcd(f, g) == \ + (1.0*x, 2.1*y**2 - 2.2*y + 2.1, 1.0*x**2) + + +def test_dup_lcm(): + R, x = ring("x", ZZ) + + assert R.dup_lcm(2, 6) == 6 + + assert R.dup_lcm(2*x**3, 6*x) == 6*x**3 + assert R.dup_lcm(2*x**3, 3*x) == 6*x**3 + + assert R.dup_lcm(x**2 + x, x) == x**2 + x + assert R.dup_lcm(x**2 + x, 2*x) == 2*x**2 + 2*x + assert R.dup_lcm(x**2 + 2*x, x) == x**2 + 2*x + assert R.dup_lcm(2*x**2 + x, x) == 2*x**2 + x + assert R.dup_lcm(2*x**2 + x, 2*x) == 4*x**2 + 2*x + + +def test_dmp_lcm(): + R, x, y = ring("x,y", ZZ) + + assert R.dmp_lcm(2, 6) == 6 + assert R.dmp_lcm(x, y) == x*y + + assert R.dmp_lcm(2*x**3, 6*x*y**2) == 6*x**3*y**2 + assert R.dmp_lcm(2*x**3, 3*x*y**2) == 6*x**3*y**2 + + assert R.dmp_lcm(x**2*y, x*y**2) == x**2*y**2 + + f = 2*x*y**5 - 3*x*y**4 - 2*x*y**3 + 3*x*y**2 + g = y**5 - 2*y**3 + y + h = 2*x*y**7 - 3*x*y**6 - 4*x*y**5 + 6*x*y**4 + 2*x*y**3 - 3*x*y**2 + + assert R.dmp_lcm(f, g) == h + + f = x**3 - 3*x**2*y - 9*x*y**2 - 5*y**3 + g = x**4 + 6*x**3*y + 12*x**2*y**2 + 10*x*y**3 + 3*y**4 + h = x**5 + x**4*y - 18*x**3*y**2 - 50*x**2*y**3 - 47*x*y**4 - 15*y**5 + + assert R.dmp_lcm(f, g) == h + + +def test_dmp_content(): + R, x,y = ring("x,y", ZZ) + + assert R.dmp_content(-2) == 2 + + f, g, F = 3*y**2 + 2*y + 1, 1, 0 + + for i in range(0, 5): + g *= f + F += x**i*g + + assert R.dmp_content(F) == f.drop(x) + + R, x,y,z = ring("x,y,z", ZZ) + + assert R.dmp_content(f_4) == 1 + assert R.dmp_content(f_5) == 1 + + R, x,y,z,t = ring("x,y,z,t", ZZ) + assert R.dmp_content(f_6) == 1 + + +def test_dmp_primitive(): + R, x,y = ring("x,y", ZZ) + + assert R.dmp_primitive(0) == (0, 0) + assert R.dmp_primitive(1) == (1, 1) + + f, g, F = 3*y**2 + 2*y + 1, 1, 0 + + for i in range(0, 5): + g *= f + F += x**i*g + + assert R.dmp_primitive(F) == (f.drop(x), F / f) + + R, x,y,z = ring("x,y,z", ZZ) + + cont, f = R.dmp_primitive(f_4) + assert cont == 1 and f == f_4 + cont, f = R.dmp_primitive(f_5) + assert cont == 1 and f == f_5 + + R, x,y,z,t = ring("x,y,z,t", ZZ) + + cont, f = R.dmp_primitive(f_6) + assert cont == 1 and f == f_6 + + +def test_dup_cancel(): + R, x = ring("x", ZZ) + + f = 2*x**2 - 2 + g = x**2 - 2*x + 1 + + p = 2*x + 2 + q = x - 1 + + assert R.dup_cancel(f, g) == (p, q) + assert R.dup_cancel(f, g, include=False) == (1, 1, p, q) + + f = -x - 2 + g = 3*x - 4 + + F = x + 2 + G = -3*x + 4 + + assert R.dup_cancel(f, g) == (f, g) + assert R.dup_cancel(F, G) == (f, g) + + assert R.dup_cancel(0, 0) == (0, 0) + assert R.dup_cancel(0, 0, include=False) == (1, 1, 0, 0) + + assert R.dup_cancel(x, 0) == (1, 0) + assert R.dup_cancel(x, 0, include=False) == (1, 1, 1, 0) + + assert R.dup_cancel(0, x) == (0, 1) + assert R.dup_cancel(0, x, include=False) == (1, 1, 0, 1) + + f = 0 + g = x + one = 1 + + assert R.dup_cancel(f, g, include=True) == (f, one) + + +def test_dmp_cancel(): + R, x, y = ring("x,y", ZZ) + + f = 2*x**2 - 2 + g = x**2 - 2*x + 1 + + p = 2*x + 2 + q = x - 1 + + assert R.dmp_cancel(f, g) == (p, q) + assert R.dmp_cancel(f, g, include=False) == (1, 1, p, q) + + assert R.dmp_cancel(0, 0) == (0, 0) + assert R.dmp_cancel(0, 0, include=False) == (1, 1, 0, 0) + + assert R.dmp_cancel(y, 0) == (1, 0) + assert R.dmp_cancel(y, 0, include=False) == (1, 1, 1, 0) + + assert R.dmp_cancel(0, y) == (0, 1) + assert R.dmp_cancel(0, y, include=False) == (1, 1, 0, 1) diff --git a/lib/python3.12/site-packages/sympy/polys/tests/test_factortools.py b/lib/python3.12/site-packages/sympy/polys/tests/test_factortools.py new file mode 100644 index 0000000000000000000000000000000000000000..7f99097c71e9cde761a800b01b149ec5c9896266 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/polys/tests/test_factortools.py @@ -0,0 +1,784 @@ +"""Tools for polynomial factorization routines in characteristic zero. """ + +from sympy.polys.rings import ring, xring +from sympy.polys.domains import FF, ZZ, QQ, ZZ_I, QQ_I, RR, EX + +from sympy.polys import polyconfig as config +from sympy.polys.polyerrors import DomainError +from sympy.polys.polyclasses import ANP +from sympy.polys.specialpolys import f_polys, w_polys + +from sympy.core.numbers import I +from sympy.functions.elementary.miscellaneous import sqrt +from sympy.functions.elementary.trigonometric import sin +from sympy.ntheory.generate import nextprime +from sympy.testing.pytest import raises, XFAIL + + +f_0, f_1, f_2, f_3, f_4, f_5, f_6 = f_polys() +w_1, w_2 = w_polys() + +def test_dup_trial_division(): + R, x = ring("x", ZZ) + assert R.dup_trial_division(x**5 + 8*x**4 + 25*x**3 + 38*x**2 + 28*x + 8, (x + 1, x + 2)) == [(x + 1, 2), (x + 2, 3)] + + +def test_dmp_trial_division(): + R, x, y = ring("x,y", ZZ) + assert R.dmp_trial_division(x**5 + 8*x**4 + 25*x**3 + 38*x**2 + 28*x + 8, (x + 1, x + 2)) == [(x + 1, 2), (x + 2, 3)] + + +def test_dup_zz_mignotte_bound(): + R, x = ring("x", ZZ) + assert R.dup_zz_mignotte_bound(2*x**2 + 3*x + 4) == 6 + assert R.dup_zz_mignotte_bound(x**3 + 14*x**2 + 56*x + 64) == 152 + + +def test_dmp_zz_mignotte_bound(): + R, x, y = ring("x,y", ZZ) + assert R.dmp_zz_mignotte_bound(2*x**2 + 3*x + 4) == 32 + + +def test_dup_zz_hensel_step(): + R, x = ring("x", ZZ) + + f = x**4 - 1 + g = x**3 + 2*x**2 - x - 2 + h = x - 2 + s = -2 + t = 2*x**2 - 2*x - 1 + + G, H, S, T = R.dup_zz_hensel_step(5, f, g, h, s, t) + + assert G == x**3 + 7*x**2 - x - 7 + assert H == x - 7 + assert S == 8 + assert T == -8*x**2 - 12*x - 1 + + +def test_dup_zz_hensel_lift(): + R, x = ring("x", ZZ) + + f = x**4 - 1 + F = [x - 1, x - 2, x + 2, x + 1] + + assert R.dup_zz_hensel_lift(ZZ(5), f, F, 4) == \ + [x - 1, x - 182, x + 182, x + 1] + + +def test_dup_zz_irreducible_p(): + R, x = ring("x", ZZ) + + assert R.dup_zz_irreducible_p(3*x**4 + 2*x**3 + 6*x**2 + 8*x + 7) is None + assert R.dup_zz_irreducible_p(3*x**4 + 2*x**3 + 6*x**2 + 8*x + 4) is None + + assert R.dup_zz_irreducible_p(3*x**4 + 2*x**3 + 6*x**2 + 8*x + 10) is True + assert R.dup_zz_irreducible_p(3*x**4 + 2*x**3 + 6*x**2 + 8*x + 14) is True + + +def test_dup_cyclotomic_p(): + R, x = ring("x", ZZ) + + assert R.dup_cyclotomic_p(x - 1) is True + assert R.dup_cyclotomic_p(x + 1) is True + assert R.dup_cyclotomic_p(x**2 + x + 1) is True + assert R.dup_cyclotomic_p(x**2 + 1) is True + assert R.dup_cyclotomic_p(x**4 + x**3 + x**2 + x + 1) is True + assert R.dup_cyclotomic_p(x**2 - x + 1) is True + assert R.dup_cyclotomic_p(x**6 + x**5 + x**4 + x**3 + x**2 + x + 1) is True + assert R.dup_cyclotomic_p(x**4 + 1) is True + assert R.dup_cyclotomic_p(x**6 + x**3 + 1) is True + + assert R.dup_cyclotomic_p(0) is False + assert R.dup_cyclotomic_p(1) is False + assert R.dup_cyclotomic_p(x) is False + assert R.dup_cyclotomic_p(x + 2) is False + assert R.dup_cyclotomic_p(3*x + 1) is False + assert R.dup_cyclotomic_p(x**2 - 1) is False + + f = x**16 + x**14 - x**10 + x**8 - x**6 + x**2 + 1 + assert R.dup_cyclotomic_p(f) is False + + g = x**16 + x**14 - x**10 - x**8 - x**6 + x**2 + 1 + assert R.dup_cyclotomic_p(g) is True + + R, x = ring("x", QQ) + assert R.dup_cyclotomic_p(x**2 + x + 1) is True + assert R.dup_cyclotomic_p(QQ(1,2)*x**2 + x + 1) is False + + R, x = ring("x", ZZ["y"]) + assert R.dup_cyclotomic_p(x**2 + x + 1) is False + + +def test_dup_zz_cyclotomic_poly(): + R, x = ring("x", ZZ) + + assert R.dup_zz_cyclotomic_poly(1) == x - 1 + assert R.dup_zz_cyclotomic_poly(2) == x + 1 + assert R.dup_zz_cyclotomic_poly(3) == x**2 + x + 1 + assert R.dup_zz_cyclotomic_poly(4) == x**2 + 1 + assert R.dup_zz_cyclotomic_poly(5) == x**4 + x**3 + x**2 + x + 1 + assert R.dup_zz_cyclotomic_poly(6) == x**2 - x + 1 + assert R.dup_zz_cyclotomic_poly(7) == x**6 + x**5 + x**4 + x**3 + x**2 + x + 1 + assert R.dup_zz_cyclotomic_poly(8) == x**4 + 1 + assert R.dup_zz_cyclotomic_poly(9) == x**6 + x**3 + 1 + + +def test_dup_zz_cyclotomic_factor(): + R, x = ring("x", ZZ) + + assert R.dup_zz_cyclotomic_factor(0) is None + assert R.dup_zz_cyclotomic_factor(1) is None + + assert R.dup_zz_cyclotomic_factor(2*x**10 - 1) is None + assert R.dup_zz_cyclotomic_factor(x**10 - 3) is None + assert R.dup_zz_cyclotomic_factor(x**10 + x**5 - 1) is None + + assert R.dup_zz_cyclotomic_factor(x + 1) == [x + 1] + assert R.dup_zz_cyclotomic_factor(x - 1) == [x - 1] + + assert R.dup_zz_cyclotomic_factor(x**2 + 1) == [x**2 + 1] + assert R.dup_zz_cyclotomic_factor(x**2 - 1) == [x - 1, x + 1] + + assert R.dup_zz_cyclotomic_factor(x**27 + 1) == \ + [x + 1, x**2 - x + 1, x**6 - x**3 + 1, x**18 - x**9 + 1] + assert R.dup_zz_cyclotomic_factor(x**27 - 1) == \ + [x - 1, x**2 + x + 1, x**6 + x**3 + 1, x**18 + x**9 + 1] + + +def test_dup_zz_factor(): + R, x = ring("x", ZZ) + + assert R.dup_zz_factor(0) == (0, []) + assert R.dup_zz_factor(7) == (7, []) + assert R.dup_zz_factor(-7) == (-7, []) + + assert R.dup_zz_factor_sqf(0) == (0, []) + assert R.dup_zz_factor_sqf(7) == (7, []) + assert R.dup_zz_factor_sqf(-7) == (-7, []) + + assert R.dup_zz_factor(2*x + 4) == (2, [(x + 2, 1)]) + assert R.dup_zz_factor_sqf(2*x + 4) == (2, [x + 2]) + + f = x**4 + x + 1 + + for i in range(0, 20): + assert R.dup_zz_factor(f) == (1, [(f, 1)]) + + assert R.dup_zz_factor(x**2 + 2*x + 2) == \ + (1, [(x**2 + 2*x + 2, 1)]) + + assert R.dup_zz_factor(18*x**2 + 12*x + 2) == \ + (2, [(3*x + 1, 2)]) + + assert R.dup_zz_factor(-9*x**2 + 1) == \ + (-1, [(3*x - 1, 1), + (3*x + 1, 1)]) + + assert R.dup_zz_factor_sqf(-9*x**2 + 1) == \ + (-1, [3*x - 1, + 3*x + 1]) + + # The order of the factors will be different when the ground types are + # flint. At the higher level dup_factor_list will sort the factors. + c, factors = R.dup_zz_factor(x**3 - 6*x**2 + 11*x - 6) + assert c == 1 + assert set(factors) == {(x - 3, 1), (x - 2, 1), (x - 1, 1)} + + assert R.dup_zz_factor_sqf(x**3 - 6*x**2 + 11*x - 6) == \ + (1, [x - 3, + x - 2, + x - 1]) + + assert R.dup_zz_factor(3*x**3 + 10*x**2 + 13*x + 10) == \ + (1, [(x + 2, 1), + (3*x**2 + 4*x + 5, 1)]) + + assert R.dup_zz_factor_sqf(3*x**3 + 10*x**2 + 13*x + 10) == \ + (1, [x + 2, + 3*x**2 + 4*x + 5]) + + c, factors = R.dup_zz_factor(-x**6 + x**2) + assert c == -1 + assert set(factors) == {(x, 2), (x - 1, 1), (x + 1, 1), (x**2 + 1, 1)} + + f = 1080*x**8 + 5184*x**7 + 2099*x**6 + 744*x**5 + 2736*x**4 - 648*x**3 + 129*x**2 - 324 + + assert R.dup_zz_factor(f) == \ + (1, [(5*x**4 + 24*x**3 + 9*x**2 + 12, 1), + (216*x**4 + 31*x**2 - 27, 1)]) + + f = -29802322387695312500000000000000000000*x**25 \ + + 2980232238769531250000000000000000*x**20 \ + + 1743435859680175781250000000000*x**15 \ + + 114142894744873046875000000*x**10 \ + - 210106372833251953125*x**5 \ + + 95367431640625 + + c, factors = R.dup_zz_factor(f) + assert c == -95367431640625 + assert set(factors) == { + (5*x - 1, 1), + (100*x**2 + 10*x - 1, 2), + (625*x**4 + 125*x**3 + 25*x**2 + 5*x + 1, 1), + (10000*x**4 - 3000*x**3 + 400*x**2 - 20*x + 1, 2), + (10000*x**4 + 2000*x**3 + 400*x**2 + 30*x + 1, 2), + } + + f = x**10 - 1 + + config.setup('USE_CYCLOTOMIC_FACTOR', True) + c0, F_0 = R.dup_zz_factor(f) + + config.setup('USE_CYCLOTOMIC_FACTOR', False) + c1, F_1 = R.dup_zz_factor(f) + + assert c0 == c1 == 1 + assert set(F_0) == set(F_1) == { + (x - 1, 1), + (x + 1, 1), + (x**4 - x**3 + x**2 - x + 1, 1), + (x**4 + x**3 + x**2 + x + 1, 1), + } + + config.setup('USE_CYCLOTOMIC_FACTOR') + + f = x**10 + 1 + + config.setup('USE_CYCLOTOMIC_FACTOR', True) + F_0 = R.dup_zz_factor(f) + + config.setup('USE_CYCLOTOMIC_FACTOR', False) + F_1 = R.dup_zz_factor(f) + + assert F_0 == F_1 == \ + (1, [(x**2 + 1, 1), + (x**8 - x**6 + x**4 - x**2 + 1, 1)]) + + config.setup('USE_CYCLOTOMIC_FACTOR') + +def test_dmp_zz_wang(): + R, x,y,z = ring("x,y,z", ZZ) + UV, _x = ring("x", ZZ) + + p = ZZ(nextprime(R.dmp_zz_mignotte_bound(w_1))) + assert p == 6291469 + + t_1, k_1, e_1 = y, 1, ZZ(-14) + t_2, k_2, e_2 = z, 2, ZZ(3) + t_3, k_3, e_3 = y + z, 2, ZZ(-11) + t_4, k_4, e_4 = y - z, 1, ZZ(-17) + + T = [t_1, t_2, t_3, t_4] + K = [k_1, k_2, k_3, k_4] + E = [e_1, e_2, e_3, e_4] + + T = zip([ t.drop(x) for t in T ], K) + + A = [ZZ(-14), ZZ(3)] + + S = R.dmp_eval_tail(w_1, A) + cs, s = UV.dup_primitive(S) + + assert cs == 1 and s == S == \ + 1036728*_x**6 + 915552*_x**5 + 55748*_x**4 + 105621*_x**3 - 17304*_x**2 - 26841*_x - 644 + + assert R.dmp_zz_wang_non_divisors(E, cs, ZZ(4)) == [7, 3, 11, 17] + assert UV.dup_sqf_p(s) and UV.dup_degree(s) == R.dmp_degree(w_1) + + _, H = UV.dup_zz_factor_sqf(s) + + h_1 = 44*_x**2 + 42*_x + 1 + h_2 = 126*_x**2 - 9*_x + 28 + h_3 = 187*_x**2 - 23 + + assert H == [h_1, h_2, h_3] + + LC = [ lc.drop(x) for lc in [-4*y - 4*z, -y*z**2, y**2 - z**2] ] + + assert R.dmp_zz_wang_lead_coeffs(w_1, T, cs, E, H, A) == (w_1, H, LC) + + factors = R.dmp_zz_wang_hensel_lifting(w_1, H, LC, A, p) + assert R.dmp_expand(factors) == w_1 + + +@XFAIL +def test_dmp_zz_wang_fail(): + R, x,y,z = ring("x,y,z", ZZ) + UV, _x = ring("x", ZZ) + + p = ZZ(nextprime(R.dmp_zz_mignotte_bound(w_1))) + assert p == 6291469 + + H_1 = [44*x**2 + 42*x + 1, 126*x**2 - 9*x + 28, 187*x**2 - 23] + H_2 = [-4*x**2*y - 12*x**2 - 3*x*y + 1, -9*x**2*y - 9*x - 2*y, x**2*y**2 - 9*x**2 + y - 9] + H_3 = [-4*x**2*y - 12*x**2 - 3*x*y + 1, -9*x**2*y - 9*x - 2*y, x**2*y**2 - 9*x**2 + y - 9] + + c_1 = -70686*x**5 - 5863*x**4 - 17826*x**3 + 2009*x**2 + 5031*x + 74 + c_2 = 9*x**5*y**4 + 12*x**5*y**3 - 45*x**5*y**2 - 108*x**5*y - 324*x**5 + 18*x**4*y**3 - 216*x**4*y**2 - 810*x**4*y + 2*x**3*y**4 + 9*x**3*y**3 - 252*x**3*y**2 - 288*x**3*y - 945*x**3 - 30*x**2*y**2 - 414*x**2*y + 2*x*y**3 - 54*x*y**2 - 3*x*y + 81*x + 12*y + c_3 = -36*x**4*y**2 - 108*x**4*y - 27*x**3*y**2 - 36*x**3*y - 108*x**3 - 8*x**2*y**2 - 42*x**2*y - 6*x*y**2 + 9*x + 2*y + + assert R.dmp_zz_diophantine(H_1, c_1, [], 5, p) == [-3*x, -2, 1] + assert R.dmp_zz_diophantine(H_2, c_2, [ZZ(-14)], 5, p) == [-x*y, -3*x, -6] + assert R.dmp_zz_diophantine(H_3, c_3, [ZZ(-14)], 5, p) == [0, 0, -1] + + +def test_issue_6355(): + # This tests a bug in the Wang algorithm that occurred only with a very + # specific set of random numbers. + random_sequence = [-1, -1, 0, 0, 0, 0, -1, -1, 0, -1, 3, -1, 3, 3, 3, 3, -1, 3] + + R, x, y, z = ring("x,y,z", ZZ) + f = 2*x**2 + y*z - y - z**2 + z + + assert R.dmp_zz_wang(f, seed=random_sequence) == [f] + + +def test_dmp_zz_factor(): + R, x = ring("x", ZZ) + assert R.dmp_zz_factor(0) == (0, []) + assert R.dmp_zz_factor(7) == (7, []) + assert R.dmp_zz_factor(-7) == (-7, []) + + assert R.dmp_zz_factor(x**2 - 9) == (1, [(x - 3, 1), (x + 3, 1)]) + + R, x, y = ring("x,y", ZZ) + assert R.dmp_zz_factor(0) == (0, []) + assert R.dmp_zz_factor(7) == (7, []) + assert R.dmp_zz_factor(-7) == (-7, []) + + assert R.dmp_zz_factor(x) == (1, [(x, 1)]) + assert R.dmp_zz_factor(4*x) == (4, [(x, 1)]) + assert R.dmp_zz_factor(4*x + 2) == (2, [(2*x + 1, 1)]) + assert R.dmp_zz_factor(x*y + 1) == (1, [(x*y + 1, 1)]) + assert R.dmp_zz_factor(y**2 + 1) == (1, [(y**2 + 1, 1)]) + assert R.dmp_zz_factor(y**2 - 1) == (1, [(y - 1, 1), (y + 1, 1)]) + + assert R.dmp_zz_factor(x**2*y**2 + 6*x**2*y + 9*x**2 - 1) == (1, [(x*y + 3*x - 1, 1), (x*y + 3*x + 1, 1)]) + assert R.dmp_zz_factor(x**2*y**2 - 9) == (1, [(x*y - 3, 1), (x*y + 3, 1)]) + + R, x, y, z = ring("x,y,z", ZZ) + assert R.dmp_zz_factor(x**2*y**2*z**2 - 9) == \ + (1, [(x*y*z - 3, 1), + (x*y*z + 3, 1)]) + + R, x, y, z, u = ring("x,y,z,u", ZZ) + assert R.dmp_zz_factor(x**2*y**2*z**2*u**2 - 9) == \ + (1, [(x*y*z*u - 3, 1), + (x*y*z*u + 3, 1)]) + + R, x, y, z = ring("x,y,z", ZZ) + assert R.dmp_zz_factor(f_1) == \ + (1, [(x + y*z + 20, 1), + (x*y + z + 10, 1), + (x*z + y + 30, 1)]) + + assert R.dmp_zz_factor(f_2) == \ + (1, [(x**2*y**2 + x**2*z**2 + y + 90, 1), + (x**3*y + x**3*z + z - 11, 1)]) + + assert R.dmp_zz_factor(f_3) == \ + (1, [(x**2*y**2 + x*z**4 + x + z, 1), + (x**3 + x*y*z + y**2 + y*z**3, 1)]) + + assert R.dmp_zz_factor(f_4) == \ + (-1, [(x*y**3 + z**2, 1), + (x**2*z + y**4*z**2 + 5, 1), + (x**3*y - z**2 - 3, 1), + (x**3*y**4 + z**2, 1)]) + + assert R.dmp_zz_factor(f_5) == \ + (-1, [(x + y - z, 3)]) + + R, x, y, z, t = ring("x,y,z,t", ZZ) + assert R.dmp_zz_factor(f_6) == \ + (1, [(47*x*y + z**3*t**2 - t**2, 1), + (45*x**3 - 9*y**3 - y**2 + 3*z**3 + 2*z*t, 1)]) + + R, x, y, z = ring("x,y,z", ZZ) + assert R.dmp_zz_factor(w_1) == \ + (1, [(x**2*y**2 - x**2*z**2 + y - z**2, 1), + (x**2*y*z**2 + 3*x*z + 2*y, 1), + (4*x**2*y + 4*x**2*z + x*y*z - 1, 1)]) + + R, x, y = ring("x,y", ZZ) + f = -12*x**16*y + 240*x**12*y**3 - 768*x**10*y**4 + 1080*x**8*y**5 - 768*x**6*y**6 + 240*x**4*y**7 - 12*y**9 + + assert R.dmp_zz_factor(f) == \ + (-12, [(y, 1), + (x**2 - y, 6), + (x**4 + 6*x**2*y + y**2, 1)]) + + +def test_dup_qq_i_factor(): + R, x = ring("x", QQ_I) + i = QQ_I(0, 1) + + assert R.dup_qq_i_factor(x**2 - 2) == (QQ_I(1, 0), [(x**2 - 2, 1)]) + + assert R.dup_qq_i_factor(x**2 - 1) == (QQ_I(1, 0), [(x - 1, 1), (x + 1, 1)]) + + assert R.dup_qq_i_factor(x**2 + 1) == (QQ_I(1, 0), [(x - i, 1), (x + i, 1)]) + + assert R.dup_qq_i_factor(x**2/4 + 1) == \ + (QQ_I(QQ(1, 4), 0), [(x - 2*i, 1), (x + 2*i, 1)]) + + assert R.dup_qq_i_factor(x**2 + 4) == \ + (QQ_I(1, 0), [(x - 2*i, 1), (x + 2*i, 1)]) + + assert R.dup_qq_i_factor(x**2 + 2*x + 1) == \ + (QQ_I(1, 0), [(x + 1, 2)]) + + assert R.dup_qq_i_factor(x**2 + 2*i*x - 1) == \ + (QQ_I(1, 0), [(x + i, 2)]) + + f = 8192*x**2 + x*(22656 + 175232*i) - 921416 + 242313*i + + assert R.dup_qq_i_factor(f) == \ + (QQ_I(8192, 0), [(x + QQ_I(QQ(177, 128), QQ(1369, 128)), 2)]) + + +def test_dmp_qq_i_factor(): + R, x, y = ring("x, y", QQ_I) + i = QQ_I(0, 1) + + assert R.dmp_qq_i_factor(x**2 + 2*y**2) == \ + (QQ_I(1, 0), [(x**2 + 2*y**2, 1)]) + + assert R.dmp_qq_i_factor(x**2 + y**2) == \ + (QQ_I(1, 0), [(x - i*y, 1), (x + i*y, 1)]) + + assert R.dmp_qq_i_factor(x**2 + y**2/4) == \ + (QQ_I(1, 0), [(x - i*y/2, 1), (x + i*y/2, 1)]) + + assert R.dmp_qq_i_factor(4*x**2 + y**2) == \ + (QQ_I(4, 0), [(x - i*y/2, 1), (x + i*y/2, 1)]) + + +def test_dup_zz_i_factor(): + R, x = ring("x", ZZ_I) + i = ZZ_I(0, 1) + + assert R.dup_zz_i_factor(x**2 - 2) == (ZZ_I(1, 0), [(x**2 - 2, 1)]) + + assert R.dup_zz_i_factor(x**2 - 1) == (ZZ_I(1, 0), [(x - 1, 1), (x + 1, 1)]) + + assert R.dup_zz_i_factor(x**2 + 1) == (ZZ_I(1, 0), [(x - i, 1), (x + i, 1)]) + + assert R.dup_zz_i_factor(x**2 + 4) == \ + (ZZ_I(1, 0), [(x - 2*i, 1), (x + 2*i, 1)]) + + assert R.dup_zz_i_factor(x**2 + 2*x + 1) == \ + (ZZ_I(1, 0), [(x + 1, 2)]) + + assert R.dup_zz_i_factor(x**2 + 2*i*x - 1) == \ + (ZZ_I(1, 0), [(x + i, 2)]) + + f = 8192*x**2 + x*(22656 + 175232*i) - 921416 + 242313*i + + assert R.dup_zz_i_factor(f) == \ + (ZZ_I(0, 1), [((64 - 64*i)*x + (773 + 596*i), 2)]) + + +def test_dmp_zz_i_factor(): + R, x, y = ring("x, y", ZZ_I) + i = ZZ_I(0, 1) + + assert R.dmp_zz_i_factor(x**2 + 2*y**2) == \ + (ZZ_I(1, 0), [(x**2 + 2*y**2, 1)]) + + assert R.dmp_zz_i_factor(x**2 + y**2) == \ + (ZZ_I(1, 0), [(x - i*y, 1), (x + i*y, 1)]) + + assert R.dmp_zz_i_factor(4*x**2 + y**2) == \ + (ZZ_I(1, 0), [(2*x - i*y, 1), (2*x + i*y, 1)]) + + +def test_dup_ext_factor(): + R, x = ring("x", QQ.algebraic_field(I)) + def anp(element): + return ANP(element, [QQ(1), QQ(0), QQ(1)], QQ) + + assert R.dup_ext_factor(0) == (anp([]), []) + + f = anp([QQ(1)])*x + anp([QQ(1)]) + + assert R.dup_ext_factor(f) == (anp([QQ(1)]), [(f, 1)]) + + g = anp([QQ(2)])*x + anp([QQ(2)]) + + assert R.dup_ext_factor(g) == (anp([QQ(2)]), [(f, 1)]) + + f = anp([QQ(7)])*x**4 + anp([QQ(1, 1)]) + g = anp([QQ(1)])*x**4 + anp([QQ(1, 7)]) + + assert R.dup_ext_factor(f) == (anp([QQ(7)]), [(g, 1)]) + + f = anp([QQ(1)])*x**4 + anp([QQ(1)]) + + assert R.dup_ext_factor(f) == \ + (anp([QQ(1, 1)]), [(anp([QQ(1)])*x**2 + anp([QQ(-1), QQ(0)]), 1), + (anp([QQ(1)])*x**2 + anp([QQ( 1), QQ(0)]), 1)]) + + f = anp([QQ(4, 1)])*x**2 + anp([QQ(9, 1)]) + + assert R.dup_ext_factor(f) == \ + (anp([QQ(4, 1)]), [(anp([QQ(1, 1)])*x + anp([-QQ(3, 2), QQ(0, 1)]), 1), + (anp([QQ(1, 1)])*x + anp([ QQ(3, 2), QQ(0, 1)]), 1)]) + + f = anp([QQ(4, 1)])*x**4 + anp([QQ(8, 1)])*x**3 + anp([QQ(77, 1)])*x**2 + anp([QQ(18, 1)])*x + anp([QQ(153, 1)]) + + assert R.dup_ext_factor(f) == \ + (anp([QQ(4, 1)]), [(anp([QQ(1, 1)])*x + anp([-QQ(4, 1), QQ(1, 1)]), 1), + (anp([QQ(1, 1)])*x + anp([-QQ(3, 2), QQ(0, 1)]), 1), + (anp([QQ(1, 1)])*x + anp([ QQ(3, 2), QQ(0, 1)]), 1), + (anp([QQ(1, 1)])*x + anp([ QQ(4, 1), QQ(1, 1)]), 1)]) + + R, x = ring("x", QQ.algebraic_field(sqrt(2))) + def anp(element): + return ANP(element, [QQ(1), QQ(0), QQ(-2)], QQ) + + f = anp([QQ(1)])*x**4 + anp([QQ(1, 1)]) + + assert R.dup_ext_factor(f) == \ + (anp([QQ(1)]), [(anp([QQ(1)])*x**2 + anp([QQ(-1), QQ(0)])*x + anp([QQ(1)]), 1), + (anp([QQ(1)])*x**2 + anp([QQ( 1), QQ(0)])*x + anp([QQ(1)]), 1)]) + + f = anp([QQ(1, 1)])*x**2 + anp([QQ(2), QQ(0)])*x + anp([QQ(2, 1)]) + + assert R.dup_ext_factor(f) == \ + (anp([QQ(1, 1)]), [(anp([1])*x + anp([1, 0]), 2)]) + + assert R.dup_ext_factor(f**3) == \ + (anp([QQ(1, 1)]), [(anp([1])*x + anp([1, 0]), 6)]) + + f *= anp([QQ(2, 1)]) + + assert R.dup_ext_factor(f) == \ + (anp([QQ(2, 1)]), [(anp([1])*x + anp([1, 0]), 2)]) + + assert R.dup_ext_factor(f**3) == \ + (anp([QQ(8, 1)]), [(anp([1])*x + anp([1, 0]), 6)]) + + +def test_dmp_ext_factor(): + K = QQ.algebraic_field(sqrt(2)) + R, x,y = ring("x,y", K) + sqrt2 = K.unit + + def anp(x): + return ANP(x, [QQ(1), QQ(0), QQ(-2)], QQ) + + assert R.dmp_ext_factor(0) == (anp([]), []) + + f = anp([QQ(1)])*x + anp([QQ(1)]) + + assert R.dmp_ext_factor(f) == (anp([QQ(1)]), [(f, 1)]) + + g = anp([QQ(2)])*x + anp([QQ(2)]) + + assert R.dmp_ext_factor(g) == (anp([QQ(2)]), [(f, 1)]) + + f = anp([QQ(1)])*x**2 + anp([QQ(-2)])*y**2 + + assert R.dmp_ext_factor(f) == \ + (anp([QQ(1)]), [(anp([QQ(1)])*x + anp([QQ(-1), QQ(0)])*y, 1), + (anp([QQ(1)])*x + anp([QQ( 1), QQ(0)])*y, 1)]) + + f = anp([QQ(2)])*x**2 + anp([QQ(-4)])*y**2 + + assert R.dmp_ext_factor(f) == \ + (anp([QQ(2)]), [(anp([QQ(1)])*x + anp([QQ(-1), QQ(0)])*y, 1), + (anp([QQ(1)])*x + anp([QQ( 1), QQ(0)])*y, 1)]) + + f1 = y + 1 + f2 = y + sqrt2 + f3 = x**2 + x + 2 + 3*sqrt2 + f = f1**2 * f2**2 * f3**2 + assert R.dmp_ext_factor(f) == (K.one, [(f1, 2), (f2, 2), (f3, 2)]) + + +def test_dup_factor_list(): + R, x = ring("x", ZZ) + assert R.dup_factor_list(0) == (0, []) + assert R.dup_factor_list(7) == (7, []) + + R, x = ring("x", QQ) + assert R.dup_factor_list(0) == (0, []) + assert R.dup_factor_list(QQ(1, 7)) == (QQ(1, 7), []) + + R, x = ring("x", ZZ['t']) + assert R.dup_factor_list(0) == (0, []) + assert R.dup_factor_list(7) == (7, []) + + R, x = ring("x", QQ['t']) + assert R.dup_factor_list(0) == (0, []) + assert R.dup_factor_list(QQ(1, 7)) == (QQ(1, 7), []) + + R, x = ring("x", ZZ) + assert R.dup_factor_list_include(0) == [(0, 1)] + assert R.dup_factor_list_include(7) == [(7, 1)] + + assert R.dup_factor_list(x**2 + 2*x + 1) == (1, [(x + 1, 2)]) + assert R.dup_factor_list_include(x**2 + 2*x + 1) == [(x + 1, 2)] + # issue 8037 + assert R.dup_factor_list(6*x**2 - 5*x - 6) == (1, [(2*x - 3, 1), (3*x + 2, 1)]) + + R, x = ring("x", QQ) + assert R.dup_factor_list(QQ(1,2)*x**2 + x + QQ(1,2)) == (QQ(1, 2), [(x + 1, 2)]) + + R, x = ring("x", FF(2)) + assert R.dup_factor_list(x**2 + 1) == (1, [(x + 1, 2)]) + + R, x = ring("x", RR) + assert R.dup_factor_list(1.0*x**2 + 2.0*x + 1.0) == (1.0, [(1.0*x + 1.0, 2)]) + assert R.dup_factor_list(2.0*x**2 + 4.0*x + 2.0) == (2.0, [(1.0*x + 1.0, 2)]) + + f = 6.7225336055071*x**2 - 10.6463972754741*x - 0.33469524022264 + coeff, factors = R.dup_factor_list(f) + assert coeff == RR(10.6463972754741) + assert len(factors) == 1 + assert factors[0][0].max_norm() == RR(1.0) + assert factors[0][1] == 1 + + Rt, t = ring("t", ZZ) + R, x = ring("x", Rt) + + f = 4*t*x**2 + 4*t**2*x + + assert R.dup_factor_list(f) == \ + (4*t, [(x, 1), + (x + t, 1)]) + + Rt, t = ring("t", QQ) + R, x = ring("x", Rt) + + f = QQ(1, 2)*t*x**2 + QQ(1, 2)*t**2*x + + assert R.dup_factor_list(f) == \ + (QQ(1, 2)*t, [(x, 1), + (x + t, 1)]) + + R, x = ring("x", QQ.algebraic_field(I)) + def anp(element): + return ANP(element, [QQ(1), QQ(0), QQ(1)], QQ) + + f = anp([QQ(1, 1)])*x**4 + anp([QQ(2, 1)])*x**2 + + assert R.dup_factor_list(f) == \ + (anp([QQ(1, 1)]), [(anp([QQ(1, 1)])*x, 2), + (anp([QQ(1, 1)])*x**2 + anp([])*x + anp([QQ(2, 1)]), 1)]) + + R, x = ring("x", EX) + raises(DomainError, lambda: R.dup_factor_list(EX(sin(1)))) + + +def test_dmp_factor_list(): + R, x, y = ring("x,y", ZZ) + assert R.dmp_factor_list(0) == (ZZ(0), []) + assert R.dmp_factor_list(7) == (7, []) + + R, x, y = ring("x,y", QQ) + assert R.dmp_factor_list(0) == (QQ(0), []) + assert R.dmp_factor_list(QQ(1, 7)) == (QQ(1, 7), []) + + Rt, t = ring("t", ZZ) + R, x, y = ring("x,y", Rt) + assert R.dmp_factor_list(0) == (0, []) + assert R.dmp_factor_list(7) == (ZZ(7), []) + + Rt, t = ring("t", QQ) + R, x, y = ring("x,y", Rt) + assert R.dmp_factor_list(0) == (0, []) + assert R.dmp_factor_list(QQ(1, 7)) == (QQ(1, 7), []) + + R, x, y = ring("x,y", ZZ) + assert R.dmp_factor_list_include(0) == [(0, 1)] + assert R.dmp_factor_list_include(7) == [(7, 1)] + + R, X = xring("x:200", ZZ) + + f, g = X[0]**2 + 2*X[0] + 1, X[0] + 1 + assert R.dmp_factor_list(f) == (1, [(g, 2)]) + + f, g = X[-1]**2 + 2*X[-1] + 1, X[-1] + 1 + assert R.dmp_factor_list(f) == (1, [(g, 2)]) + + R, x = ring("x", ZZ) + assert R.dmp_factor_list(x**2 + 2*x + 1) == (1, [(x + 1, 2)]) + R, x = ring("x", QQ) + assert R.dmp_factor_list(QQ(1,2)*x**2 + x + QQ(1,2)) == (QQ(1,2), [(x + 1, 2)]) + + R, x, y = ring("x,y", ZZ) + assert R.dmp_factor_list(x**2 + 2*x + 1) == (1, [(x + 1, 2)]) + R, x, y = ring("x,y", QQ) + assert R.dmp_factor_list(QQ(1,2)*x**2 + x + QQ(1,2)) == (QQ(1,2), [(x + 1, 2)]) + + R, x, y = ring("x,y", ZZ) + f = 4*x**2*y + 4*x*y**2 + + assert R.dmp_factor_list(f) == \ + (4, [(y, 1), + (x, 1), + (x + y, 1)]) + + assert R.dmp_factor_list_include(f) == \ + [(4*y, 1), + (x, 1), + (x + y, 1)] + + R, x, y = ring("x,y", QQ) + f = QQ(1,2)*x**2*y + QQ(1,2)*x*y**2 + + assert R.dmp_factor_list(f) == \ + (QQ(1,2), [(y, 1), + (x, 1), + (x + y, 1)]) + + R, x, y = ring("x,y", RR) + f = 2.0*x**2 - 8.0*y**2 + + assert R.dmp_factor_list(f) == \ + (RR(8.0), [(0.5*x - y, 1), + (0.5*x + y, 1)]) + + f = 6.7225336055071*x**2*y**2 - 10.6463972754741*x*y - 0.33469524022264 + coeff, factors = R.dmp_factor_list(f) + assert coeff == RR(10.6463972754741) + assert len(factors) == 1 + assert factors[0][0].max_norm() == RR(1.0) + assert factors[0][1] == 1 + + Rt, t = ring("t", ZZ) + R, x, y = ring("x,y", Rt) + f = 4*t*x**2 + 4*t**2*x + + assert R.dmp_factor_list(f) == \ + (4*t, [(x, 1), + (x + t, 1)]) + + Rt, t = ring("t", QQ) + R, x, y = ring("x,y", Rt) + f = QQ(1, 2)*t*x**2 + QQ(1, 2)*t**2*x + + assert R.dmp_factor_list(f) == \ + (QQ(1, 2)*t, [(x, 1), + (x + t, 1)]) + + R, x, y = ring("x,y", FF(2)) + raises(NotImplementedError, lambda: R.dmp_factor_list(x**2 + y**2)) + + R, x, y = ring("x,y", EX) + raises(DomainError, lambda: R.dmp_factor_list(EX(sin(1)))) + + +def test_dup_irreducible_p(): + R, x = ring("x", ZZ) + assert R.dup_irreducible_p(x**2 + x + 1) is True + assert R.dup_irreducible_p(x**2 + 2*x + 1) is False + + +def test_dmp_irreducible_p(): + R, x, y = ring("x,y", ZZ) + assert R.dmp_irreducible_p(x**2 + x + 1) is True + assert R.dmp_irreducible_p(x**2 + 2*x + 1) is False diff --git a/lib/python3.12/site-packages/sympy/polys/tests/test_fields.py b/lib/python3.12/site-packages/sympy/polys/tests/test_fields.py new file mode 100644 index 0000000000000000000000000000000000000000..da9f3910159929cb0b7bb44dd08d879bdc3b61d6 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/polys/tests/test_fields.py @@ -0,0 +1,362 @@ +"""Test sparse rational functions. """ + +from sympy.polys.fields import field, sfield, FracField, FracElement +from sympy.polys.rings import ring +from sympy.polys.domains import ZZ, QQ +from sympy.polys.orderings import lex + +from sympy.testing.pytest import raises, XFAIL +from sympy.core import symbols, E +from sympy.core.numbers import Rational +from sympy.functions.elementary.exponential import (exp, log) +from sympy.functions.elementary.miscellaneous import sqrt + +def test_FracField___init__(): + F1 = FracField("x,y", ZZ, lex) + F2 = FracField("x,y", ZZ, lex) + F3 = FracField("x,y,z", ZZ, lex) + + assert F1.x == F1.gens[0] + assert F1.y == F1.gens[1] + assert F1.x == F2.x + assert F1.y == F2.y + assert F1.x != F3.x + assert F1.y != F3.y + +def test_FracField___hash__(): + F, x, y, z = field("x,y,z", QQ) + assert hash(F) + +def test_FracField___eq__(): + assert field("x,y,z", QQ)[0] == field("x,y,z", QQ)[0] + assert field("x,y,z", QQ)[0] is field("x,y,z", QQ)[0] + + assert field("x,y,z", QQ)[0] != field("x,y,z", ZZ)[0] + assert field("x,y,z", QQ)[0] is not field("x,y,z", ZZ)[0] + + assert field("x,y,z", ZZ)[0] != field("x,y,z", QQ)[0] + assert field("x,y,z", ZZ)[0] is not field("x,y,z", QQ)[0] + + assert field("x,y,z", QQ)[0] != field("x,y", QQ)[0] + assert field("x,y,z", QQ)[0] is not field("x,y", QQ)[0] + + assert field("x,y", QQ)[0] != field("x,y,z", QQ)[0] + assert field("x,y", QQ)[0] is not field("x,y,z", QQ)[0] + +def test_sfield(): + x = symbols("x") + + F = FracField((E, exp(exp(x)), exp(x)), ZZ, lex) + e, exex, ex = F.gens + assert sfield(exp(x)*exp(exp(x) + 1 + log(exp(x) + 3)/2)**2/(exp(x) + 3)) \ + == (F, e**2*exex**2*ex) + + F = FracField((x, exp(1/x), log(x), x**QQ(1, 3)), ZZ, lex) + _, ex, lg, x3 = F.gens + assert sfield(((x-3)*log(x)+4*x**2)*exp(1/x+log(x)/3)/x**2) == \ + (F, (4*F.x**2*ex + F.x*ex*lg - 3*ex*lg)/x3**5) + + F = FracField((x, log(x), sqrt(x + log(x))), ZZ, lex) + _, lg, srt = F.gens + assert sfield((x + 1) / (x * (x + log(x))**QQ(3, 2)) - 1/(x * log(x)**2)) \ + == (F, (F.x*lg**2 - F.x*srt + lg**2 - lg*srt)/ + (F.x**2*lg**2*srt + F.x*lg**3*srt)) + +def test_FracElement___hash__(): + F, x, y, z = field("x,y,z", QQ) + assert hash(x*y/z) + +def test_FracElement_copy(): + F, x, y, z = field("x,y,z", ZZ) + + f = x*y/3*z + g = f.copy() + + assert f == g + g.numer[(1, 1, 1)] = 7 + assert f != g + +def test_FracElement_as_expr(): + F, x, y, z = field("x,y,z", ZZ) + f = (3*x**2*y - x*y*z)/(7*z**3 + 1) + + X, Y, Z = F.symbols + g = (3*X**2*Y - X*Y*Z)/(7*Z**3 + 1) + + assert f != g + assert f.as_expr() == g + + X, Y, Z = symbols("x,y,z") + g = (3*X**2*Y - X*Y*Z)/(7*Z**3 + 1) + + assert f != g + assert f.as_expr(X, Y, Z) == g + + raises(ValueError, lambda: f.as_expr(X)) + +def test_FracElement_from_expr(): + x, y, z = symbols("x,y,z") + F, X, Y, Z = field((x, y, z), ZZ) + + f = F.from_expr(1) + assert f == 1 and isinstance(f, F.dtype) + + f = F.from_expr(Rational(3, 7)) + assert f == F(3)/7 and isinstance(f, F.dtype) + + f = F.from_expr(x) + assert f == X and isinstance(f, F.dtype) + + f = F.from_expr(Rational(3,7)*x) + assert f == X*Rational(3, 7) and isinstance(f, F.dtype) + + f = F.from_expr(1/x) + assert f == 1/X and isinstance(f, F.dtype) + + f = F.from_expr(x*y*z) + assert f == X*Y*Z and isinstance(f, F.dtype) + + f = F.from_expr(x*y/z) + assert f == X*Y/Z and isinstance(f, F.dtype) + + f = F.from_expr(x*y*z + x*y + x) + assert f == X*Y*Z + X*Y + X and isinstance(f, F.dtype) + + f = F.from_expr((x*y*z + x*y + x)/(x*y + 7)) + assert f == (X*Y*Z + X*Y + X)/(X*Y + 7) and isinstance(f, F.dtype) + + f = F.from_expr(x**3*y*z + x**2*y**7 + 1) + assert f == X**3*Y*Z + X**2*Y**7 + 1 and isinstance(f, F.dtype) + + raises(ValueError, lambda: F.from_expr(2**x)) + raises(ValueError, lambda: F.from_expr(7*x + sqrt(2))) + + assert isinstance(ZZ[2**x].get_field().convert(2**(-x)), + FracElement) + assert isinstance(ZZ[x**2].get_field().convert(x**(-6)), + FracElement) + assert isinstance(ZZ[exp(Rational(1, 3))].get_field().convert(E), + FracElement) + + +def test_FracField_nested(): + a, b, x = symbols('a b x') + F1 = ZZ.frac_field(a, b) + F2 = F1.frac_field(x) + frac = F2(a + b) + assert frac.numer == F1.poly_ring(x)(a + b) + assert frac.numer.coeffs() == [F1(a + b)] + assert frac.denom == F1.poly_ring(x)(1) + + F3 = ZZ.poly_ring(a, b) + F4 = F3.frac_field(x) + frac = F4(a + b) + assert frac.numer == F3.poly_ring(x)(a + b) + assert frac.numer.coeffs() == [F3(a + b)] + assert frac.denom == F3.poly_ring(x)(1) + + frac = F2(F3(a + b)) + assert frac.numer == F1.poly_ring(x)(a + b) + assert frac.numer.coeffs() == [F1(a + b)] + assert frac.denom == F1.poly_ring(x)(1) + + frac = F4(F1(a + b)) + assert frac.numer == F3.poly_ring(x)(a + b) + assert frac.numer.coeffs() == [F3(a + b)] + assert frac.denom == F3.poly_ring(x)(1) + + +def test_FracElement__lt_le_gt_ge__(): + F, x, y = field("x,y", ZZ) + + assert F(1) < 1/x < 1/x**2 < 1/x**3 + assert F(1) <= 1/x <= 1/x**2 <= 1/x**3 + + assert -7/x < 1/x < 3/x < y/x < 1/x**2 + assert -7/x <= 1/x <= 3/x <= y/x <= 1/x**2 + + assert 1/x**3 > 1/x**2 > 1/x > F(1) + assert 1/x**3 >= 1/x**2 >= 1/x >= F(1) + + assert 1/x**2 > y/x > 3/x > 1/x > -7/x + assert 1/x**2 >= y/x >= 3/x >= 1/x >= -7/x + +def test_FracElement___neg__(): + F, x,y = field("x,y", QQ) + + f = (7*x - 9)/y + g = (-7*x + 9)/y + + assert -f == g + assert -g == f + +def test_FracElement___add__(): + F, x,y = field("x,y", QQ) + + f, g = 1/x, 1/y + assert f + g == g + f == (x + y)/(x*y) + + assert x + F.ring.gens[0] == F.ring.gens[0] + x == 2*x + + F, x,y = field("x,y", ZZ) + assert x + 3 == 3 + x + assert x + QQ(3,7) == QQ(3,7) + x == (7*x + 3)/7 + + Fuv, u,v = field("u,v", ZZ) + Fxyzt, x,y,z,t = field("x,y,z,t", Fuv) + + f = (u*v + x)/(y + u*v) + assert dict(f.numer) == {(1, 0, 0, 0): 1, (0, 0, 0, 0): u*v} + assert dict(f.denom) == {(0, 1, 0, 0): 1, (0, 0, 0, 0): u*v} + + Ruv, u,v = ring("u,v", ZZ) + Fxyzt, x,y,z,t = field("x,y,z,t", Ruv) + + f = (u*v + x)/(y + u*v) + assert dict(f.numer) == {(1, 0, 0, 0): 1, (0, 0, 0, 0): u*v} + assert dict(f.denom) == {(0, 1, 0, 0): 1, (0, 0, 0, 0): u*v} + +def test_FracElement___sub__(): + F, x,y = field("x,y", QQ) + + f, g = 1/x, 1/y + assert f - g == (-x + y)/(x*y) + + assert x - F.ring.gens[0] == F.ring.gens[0] - x == 0 + + F, x,y = field("x,y", ZZ) + assert x - 3 == -(3 - x) + assert x - QQ(3,7) == -(QQ(3,7) - x) == (7*x - 3)/7 + + Fuv, u,v = field("u,v", ZZ) + Fxyzt, x,y,z,t = field("x,y,z,t", Fuv) + + f = (u*v - x)/(y - u*v) + assert dict(f.numer) == {(1, 0, 0, 0):-1, (0, 0, 0, 0): u*v} + assert dict(f.denom) == {(0, 1, 0, 0): 1, (0, 0, 0, 0):-u*v} + + Ruv, u,v = ring("u,v", ZZ) + Fxyzt, x,y,z,t = field("x,y,z,t", Ruv) + + f = (u*v - x)/(y - u*v) + assert dict(f.numer) == {(1, 0, 0, 0):-1, (0, 0, 0, 0): u*v} + assert dict(f.denom) == {(0, 1, 0, 0): 1, (0, 0, 0, 0):-u*v} + +def test_FracElement___mul__(): + F, x,y = field("x,y", QQ) + + f, g = 1/x, 1/y + assert f*g == g*f == 1/(x*y) + + assert x*F.ring.gens[0] == F.ring.gens[0]*x == x**2 + + F, x,y = field("x,y", ZZ) + assert x*3 == 3*x + assert x*QQ(3,7) == QQ(3,7)*x == x*Rational(3, 7) + + Fuv, u,v = field("u,v", ZZ) + Fxyzt, x,y,z,t = field("x,y,z,t", Fuv) + + f = ((u + 1)*x*y + 1)/((v - 1)*z - t*u*v - 1) + assert dict(f.numer) == {(1, 1, 0, 0): u + 1, (0, 0, 0, 0): 1} + assert dict(f.denom) == {(0, 0, 1, 0): v - 1, (0, 0, 0, 1): -u*v, (0, 0, 0, 0): -1} + + Ruv, u,v = ring("u,v", ZZ) + Fxyzt, x,y,z,t = field("x,y,z,t", Ruv) + + f = ((u + 1)*x*y + 1)/((v - 1)*z - t*u*v - 1) + assert dict(f.numer) == {(1, 1, 0, 0): u + 1, (0, 0, 0, 0): 1} + assert dict(f.denom) == {(0, 0, 1, 0): v - 1, (0, 0, 0, 1): -u*v, (0, 0, 0, 0): -1} + +def test_FracElement___truediv__(): + F, x,y = field("x,y", QQ) + + f, g = 1/x, 1/y + assert f/g == y/x + + assert x/F.ring.gens[0] == F.ring.gens[0]/x == 1 + + F, x,y = field("x,y", ZZ) + assert x*3 == 3*x + assert x/QQ(3,7) == (QQ(3,7)/x)**-1 == x*Rational(7, 3) + + raises(ZeroDivisionError, lambda: x/0) + raises(ZeroDivisionError, lambda: 1/(x - x)) + raises(ZeroDivisionError, lambda: x/(x - x)) + + Fuv, u,v = field("u,v", ZZ) + Fxyzt, x,y,z,t = field("x,y,z,t", Fuv) + + f = (u*v)/(x*y) + assert dict(f.numer) == {(0, 0, 0, 0): u*v} + assert dict(f.denom) == {(1, 1, 0, 0): 1} + + g = (x*y)/(u*v) + assert dict(g.numer) == {(1, 1, 0, 0): 1} + assert dict(g.denom) == {(0, 0, 0, 0): u*v} + + Ruv, u,v = ring("u,v", ZZ) + Fxyzt, x,y,z,t = field("x,y,z,t", Ruv) + + f = (u*v)/(x*y) + assert dict(f.numer) == {(0, 0, 0, 0): u*v} + assert dict(f.denom) == {(1, 1, 0, 0): 1} + + g = (x*y)/(u*v) + assert dict(g.numer) == {(1, 1, 0, 0): 1} + assert dict(g.denom) == {(0, 0, 0, 0): u*v} + +def test_FracElement___pow__(): + F, x,y = field("x,y", QQ) + + f, g = 1/x, 1/y + + assert f**3 == 1/x**3 + assert g**3 == 1/y**3 + + assert (f*g)**3 == 1/(x**3*y**3) + assert (f*g)**-3 == (x*y)**3 + + raises(ZeroDivisionError, lambda: (x - x)**-3) + +def test_FracElement_diff(): + F, x,y,z = field("x,y,z", ZZ) + + assert ((x**2 + y)/(z + 1)).diff(x) == 2*x/(z + 1) + +@XFAIL +def test_FracElement___call__(): + F, x,y,z = field("x,y,z", ZZ) + f = (x**2 + 3*y)/z + + r = f(1, 1, 1) + assert r == 4 and not isinstance(r, FracElement) + raises(ZeroDivisionError, lambda: f(1, 1, 0)) + +def test_FracElement_evaluate(): + F, x,y,z = field("x,y,z", ZZ) + Fyz = field("y,z", ZZ)[0] + f = (x**2 + 3*y)/z + + assert f.evaluate(x, 0) == 3*Fyz.y/Fyz.z + raises(ZeroDivisionError, lambda: f.evaluate(z, 0)) + +def test_FracElement_subs(): + F, x,y,z = field("x,y,z", ZZ) + f = (x**2 + 3*y)/z + + assert f.subs(x, 0) == 3*y/z + raises(ZeroDivisionError, lambda: f.subs(z, 0)) + +def test_FracElement_compose(): + pass + +def test_FracField_index(): + a = symbols("a") + F, x, y, z = field('x y z', QQ) + assert F.index(x) == 0 + assert F.index(y) == 1 + + raises(ValueError, lambda: F.index(1)) + raises(ValueError, lambda: F.index(a)) + pass diff --git a/lib/python3.12/site-packages/sympy/polys/tests/test_galoistools.py b/lib/python3.12/site-packages/sympy/polys/tests/test_galoistools.py new file mode 100644 index 0000000000000000000000000000000000000000..e512bdd865c300bb138cb40b4ff78f393b323c22 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/polys/tests/test_galoistools.py @@ -0,0 +1,875 @@ +from sympy.polys.galoistools import ( + gf_crt, gf_crt1, gf_crt2, gf_int, + gf_degree, gf_strip, gf_trunc, gf_normal, + gf_from_dict, gf_to_dict, + gf_from_int_poly, gf_to_int_poly, + gf_neg, gf_add_ground, gf_sub_ground, gf_mul_ground, + gf_add, gf_sub, gf_add_mul, gf_sub_mul, gf_mul, gf_sqr, + gf_div, gf_rem, gf_quo, gf_exquo, + gf_lshift, gf_rshift, gf_expand, + gf_pow, gf_pow_mod, + gf_gcdex, gf_gcd, gf_lcm, gf_cofactors, + gf_LC, gf_TC, gf_monic, + gf_eval, gf_multi_eval, + gf_compose, gf_compose_mod, + gf_trace_map, + gf_diff, + gf_irreducible, gf_irreducible_p, + gf_irred_p_ben_or, gf_irred_p_rabin, + gf_sqf_list, gf_sqf_part, gf_sqf_p, + gf_Qmatrix, gf_Qbasis, + gf_ddf_zassenhaus, gf_ddf_shoup, + gf_edf_zassenhaus, gf_edf_shoup, + gf_berlekamp, + gf_factor_sqf, gf_factor, + gf_value, linear_congruence, _csolve_prime_las_vegas, + csolve_prime, gf_csolve, gf_frobenius_map, gf_frobenius_monomial_base +) + +from sympy.polys.polyerrors import ( + ExactQuotientFailed, +) + +from sympy.polys import polyconfig as config + +from sympy.polys.domains import ZZ +from sympy.core.numbers import pi +from sympy.ntheory.generate import nextprime +from sympy.testing.pytest import raises + + +def test_gf_crt(): + U = [49, 76, 65] + M = [99, 97, 95] + + p = 912285 + u = 639985 + + assert gf_crt(U, M, ZZ) == u + + E = [9215, 9405, 9603] + S = [62, 24, 12] + + assert gf_crt1(M, ZZ) == (p, E, S) + assert gf_crt2(U, M, p, E, S, ZZ) == u + + +def test_gf_int(): + assert gf_int(0, 5) == 0 + assert gf_int(1, 5) == 1 + assert gf_int(2, 5) == 2 + assert gf_int(3, 5) == -2 + assert gf_int(4, 5) == -1 + assert gf_int(5, 5) == 0 + + +def test_gf_degree(): + assert gf_degree([]) == -1 + assert gf_degree([1]) == 0 + assert gf_degree([1, 0]) == 1 + assert gf_degree([1, 0, 0, 0, 1]) == 4 + + +def test_gf_strip(): + assert gf_strip([]) == [] + assert gf_strip([0]) == [] + assert gf_strip([0, 0, 0]) == [] + + assert gf_strip([1]) == [1] + assert gf_strip([0, 1]) == [1] + assert gf_strip([0, 0, 0, 1]) == [1] + + assert gf_strip([1, 2, 0]) == [1, 2, 0] + assert gf_strip([0, 1, 2, 0]) == [1, 2, 0] + assert gf_strip([0, 0, 0, 1, 2, 0]) == [1, 2, 0] + + +def test_gf_trunc(): + assert gf_trunc([], 11) == [] + assert gf_trunc([1], 11) == [1] + assert gf_trunc([22], 11) == [] + assert gf_trunc([12], 11) == [1] + + assert gf_trunc([11, 22, 17, 1, 0], 11) == [6, 1, 0] + assert gf_trunc([12, 23, 17, 1, 0], 11) == [1, 1, 6, 1, 0] + + +def test_gf_normal(): + assert gf_normal([11, 22, 17, 1, 0], 11, ZZ) == [6, 1, 0] + + +def test_gf_from_to_dict(): + f = {11: 12, 6: 2, 0: 25} + F = {11: 1, 6: 2, 0: 3} + g = [1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 3] + + assert gf_from_dict(f, 11, ZZ) == g + assert gf_to_dict(g, 11) == F + + f = {11: -5, 4: 0, 3: 1, 0: 12} + F = {11: -5, 3: 1, 0: 1} + g = [6, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1] + + assert gf_from_dict(f, 11, ZZ) == g + assert gf_to_dict(g, 11) == F + + assert gf_to_dict([10], 11, symmetric=True) == {0: -1} + assert gf_to_dict([10], 11, symmetric=False) == {0: 10} + + +def test_gf_from_to_int_poly(): + assert gf_from_int_poly([1, 0, 7, 2, 20], 5) == [1, 0, 2, 2, 0] + assert gf_to_int_poly([1, 0, 4, 2, 3], 5) == [1, 0, -1, 2, -2] + + assert gf_to_int_poly([10], 11, symmetric=True) == [-1] + assert gf_to_int_poly([10], 11, symmetric=False) == [10] + + +def test_gf_LC(): + assert gf_LC([], ZZ) == 0 + assert gf_LC([1], ZZ) == 1 + assert gf_LC([1, 2], ZZ) == 1 + + +def test_gf_TC(): + assert gf_TC([], ZZ) == 0 + assert gf_TC([1], ZZ) == 1 + assert gf_TC([1, 2], ZZ) == 2 + + +def test_gf_monic(): + assert gf_monic(ZZ.map([]), 11, ZZ) == (0, []) + + assert gf_monic(ZZ.map([1]), 11, ZZ) == (1, [1]) + assert gf_monic(ZZ.map([2]), 11, ZZ) == (2, [1]) + + assert gf_monic(ZZ.map([1, 2, 3, 4]), 11, ZZ) == (1, [1, 2, 3, 4]) + assert gf_monic(ZZ.map([2, 3, 4, 5]), 11, ZZ) == (2, [1, 7, 2, 8]) + + +def test_gf_arith(): + assert gf_neg([], 11, ZZ) == [] + assert gf_neg([1], 11, ZZ) == [10] + assert gf_neg([1, 2, 3], 11, ZZ) == [10, 9, 8] + + assert gf_add_ground([], 0, 11, ZZ) == [] + assert gf_sub_ground([], 0, 11, ZZ) == [] + + assert gf_add_ground([], 3, 11, ZZ) == [3] + assert gf_sub_ground([], 3, 11, ZZ) == [8] + + assert gf_add_ground([1], 3, 11, ZZ) == [4] + assert gf_sub_ground([1], 3, 11, ZZ) == [9] + + assert gf_add_ground([8], 3, 11, ZZ) == [] + assert gf_sub_ground([3], 3, 11, ZZ) == [] + + assert gf_add_ground([1, 2, 3], 3, 11, ZZ) == [1, 2, 6] + assert gf_sub_ground([1, 2, 3], 3, 11, ZZ) == [1, 2, 0] + + assert gf_mul_ground([], 0, 11, ZZ) == [] + assert gf_mul_ground([], 1, 11, ZZ) == [] + + assert gf_mul_ground([1], 0, 11, ZZ) == [] + assert gf_mul_ground([1], 1, 11, ZZ) == [1] + + assert gf_mul_ground([1, 2, 3], 0, 11, ZZ) == [] + assert gf_mul_ground([1, 2, 3], 1, 11, ZZ) == [1, 2, 3] + assert gf_mul_ground([1, 2, 3], 7, 11, ZZ) == [7, 3, 10] + + assert gf_add([], [], 11, ZZ) == [] + assert gf_add([1], [], 11, ZZ) == [1] + assert gf_add([], [1], 11, ZZ) == [1] + assert gf_add([1], [1], 11, ZZ) == [2] + assert gf_add([1], [2], 11, ZZ) == [3] + + assert gf_add([1, 2], [1], 11, ZZ) == [1, 3] + assert gf_add([1], [1, 2], 11, ZZ) == [1, 3] + + assert gf_add([1, 2, 3], [8, 9, 10], 11, ZZ) == [9, 0, 2] + + assert gf_sub([], [], 11, ZZ) == [] + assert gf_sub([1], [], 11, ZZ) == [1] + assert gf_sub([], [1], 11, ZZ) == [10] + assert gf_sub([1], [1], 11, ZZ) == [] + assert gf_sub([1], [2], 11, ZZ) == [10] + + assert gf_sub([1, 2], [1], 11, ZZ) == [1, 1] + assert gf_sub([1], [1, 2], 11, ZZ) == [10, 10] + + assert gf_sub([3, 2, 1], [8, 9, 10], 11, ZZ) == [6, 4, 2] + + assert gf_add_mul( + [1, 5, 6], [7, 3], [8, 0, 6, 1], 11, ZZ) == [1, 2, 10, 8, 9] + assert gf_sub_mul( + [1, 5, 6], [7, 3], [8, 0, 6, 1], 11, ZZ) == [10, 9, 3, 2, 3] + + assert gf_mul([], [], 11, ZZ) == [] + assert gf_mul([], [1], 11, ZZ) == [] + assert gf_mul([1], [], 11, ZZ) == [] + assert gf_mul([1], [1], 11, ZZ) == [1] + assert gf_mul([5], [7], 11, ZZ) == [2] + + assert gf_mul([3, 0, 0, 6, 1, 2], [4, 0, 1, 0], 11, ZZ) == [1, 0, + 3, 2, 4, 3, 1, 2, 0] + assert gf_mul([4, 0, 1, 0], [3, 0, 0, 6, 1, 2], 11, ZZ) == [1, 0, + 3, 2, 4, 3, 1, 2, 0] + + assert gf_mul([2, 0, 0, 1, 7], [2, 0, 0, 1, 7], 11, ZZ) == [4, 0, + 0, 4, 6, 0, 1, 3, 5] + + assert gf_sqr([], 11, ZZ) == [] + assert gf_sqr([2], 11, ZZ) == [4] + assert gf_sqr([1, 2], 11, ZZ) == [1, 4, 4] + + assert gf_sqr([2, 0, 0, 1, 7], 11, ZZ) == [4, 0, 0, 4, 6, 0, 1, 3, 5] + + +def test_gf_division(): + raises(ZeroDivisionError, lambda: gf_div([1, 2, 3], [], 11, ZZ)) + raises(ZeroDivisionError, lambda: gf_rem([1, 2, 3], [], 11, ZZ)) + raises(ZeroDivisionError, lambda: gf_quo([1, 2, 3], [], 11, ZZ)) + raises(ZeroDivisionError, lambda: gf_quo([1, 2, 3], [], 11, ZZ)) + + assert gf_div([1], [1, 2, 3], 7, ZZ) == ([], [1]) + assert gf_rem([1], [1, 2, 3], 7, ZZ) == [1] + assert gf_quo([1], [1, 2, 3], 7, ZZ) == [] + + f = ZZ.map([5, 4, 3, 2, 1, 0]) + g = ZZ.map([1, 2, 3]) + q = [5, 1, 0, 6] + r = [3, 3] + + assert gf_div(f, g, 7, ZZ) == (q, r) + assert gf_rem(f, g, 7, ZZ) == r + assert gf_quo(f, g, 7, ZZ) == q + + raises(ExactQuotientFailed, lambda: gf_exquo(f, g, 7, ZZ)) + + f = ZZ.map([5, 4, 3, 2, 1, 0]) + g = ZZ.map([1, 2, 3, 0]) + q = [5, 1, 0] + r = [6, 1, 0] + + assert gf_div(f, g, 7, ZZ) == (q, r) + assert gf_rem(f, g, 7, ZZ) == r + assert gf_quo(f, g, 7, ZZ) == q + + raises(ExactQuotientFailed, lambda: gf_exquo(f, g, 7, ZZ)) + + assert gf_quo(ZZ.map([1, 2, 1]), ZZ.map([1, 1]), 11, ZZ) == [1, 1] + + +def test_gf_shift(): + f = [1, 2, 3, 4, 5] + + assert gf_lshift([], 5, ZZ) == [] + assert gf_rshift([], 5, ZZ) == ([], []) + + assert gf_lshift(f, 1, ZZ) == [1, 2, 3, 4, 5, 0] + assert gf_lshift(f, 2, ZZ) == [1, 2, 3, 4, 5, 0, 0] + + assert gf_rshift(f, 0, ZZ) == (f, []) + assert gf_rshift(f, 1, ZZ) == ([1, 2, 3, 4], [5]) + assert gf_rshift(f, 3, ZZ) == ([1, 2], [3, 4, 5]) + assert gf_rshift(f, 5, ZZ) == ([], f) + + +def test_gf_expand(): + F = [([1, 1], 2), ([1, 2], 3)] + + assert gf_expand(F, 11, ZZ) == [1, 8, 3, 5, 6, 8] + assert gf_expand((4, F), 11, ZZ) == [4, 10, 1, 9, 2, 10] + + +def test_gf_powering(): + assert gf_pow([1, 0, 0, 1, 8], 0, 11, ZZ) == [1] + assert gf_pow([1, 0, 0, 1, 8], 1, 11, ZZ) == [1, 0, 0, 1, 8] + assert gf_pow([1, 0, 0, 1, 8], 2, 11, ZZ) == [1, 0, 0, 2, 5, 0, 1, 5, 9] + + assert gf_pow([1, 0, 0, 1, 8], 5, 11, ZZ) == \ + [1, 0, 0, 5, 7, 0, 10, 6, 2, 10, 9, 6, 10, 6, 6, 0, 5, 2, 5, 9, 10] + + assert gf_pow([1, 0, 0, 1, 8], 8, 11, ZZ) == \ + [1, 0, 0, 8, 9, 0, 6, 8, 10, 1, 2, 5, 10, 7, 7, 9, 1, 2, 0, 0, 6, 2, + 5, 2, 5, 7, 7, 9, 10, 10, 7, 5, 5] + + assert gf_pow([1, 0, 0, 1, 8], 45, 11, ZZ) == \ + [ 1, 0, 0, 1, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 4, 10, 0, 0, 0, 0, 0, 0, + 10, 0, 0, 10, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 6, 0, 0, 6, 4, 0, 0, 0, 0, 0, 0, 8, 0, 0, 8, 9, 0, 0, 0, 0, 0, 0, + 10, 0, 0, 10, 3, 0, 0, 0, 0, 0, 0, 4, 0, 0, 4, 10, 0, 0, 0, 0, 0, 0, + 8, 0, 0, 8, 9, 0, 0, 0, 0, 0, 0, 9, 0, 0, 9, 6, 0, 0, 0, 0, 0, 0, + 3, 0, 0, 3, 2, 0, 0, 0, 0, 0, 0, 10, 0, 0, 10, 3, 0, 0, 0, 0, 0, 0, + 10, 0, 0, 10, 3, 0, 0, 0, 0, 0, 0, 2, 0, 0, 2, 5, 0, 0, 0, 0, 0, 0, + 4, 0, 0, 4, 10] + + assert gf_pow_mod(ZZ.map([1, 0, 0, 1, 8]), 0, ZZ.map([2, 0, 7]), 11, ZZ) == [1] + assert gf_pow_mod(ZZ.map([1, 0, 0, 1, 8]), 1, ZZ.map([2, 0, 7]), 11, ZZ) == [1, 1] + assert gf_pow_mod(ZZ.map([1, 0, 0, 1, 8]), 2, ZZ.map([2, 0, 7]), 11, ZZ) == [2, 3] + assert gf_pow_mod(ZZ.map([1, 0, 0, 1, 8]), 5, ZZ.map([2, 0, 7]), 11, ZZ) == [7, 8] + assert gf_pow_mod(ZZ.map([1, 0, 0, 1, 8]), 8, ZZ.map([2, 0, 7]), 11, ZZ) == [1, 5] + assert gf_pow_mod(ZZ.map([1, 0, 0, 1, 8]), 45, ZZ.map([2, 0, 7]), 11, ZZ) == [5, 4] + + +def test_gf_gcdex(): + assert gf_gcdex(ZZ.map([]), ZZ.map([]), 11, ZZ) == ([1], [], []) + assert gf_gcdex(ZZ.map([2]), ZZ.map([]), 11, ZZ) == ([6], [], [1]) + assert gf_gcdex(ZZ.map([]), ZZ.map([2]), 11, ZZ) == ([], [6], [1]) + assert gf_gcdex(ZZ.map([2]), ZZ.map([2]), 11, ZZ) == ([], [6], [1]) + + assert gf_gcdex(ZZ.map([]), ZZ.map([3, 0]), 11, ZZ) == ([], [4], [1, 0]) + assert gf_gcdex(ZZ.map([3, 0]), ZZ.map([]), 11, ZZ) == ([4], [], [1, 0]) + + assert gf_gcdex(ZZ.map([3, 0]), ZZ.map([3, 0]), 11, ZZ) == ([], [4], [1, 0]) + + assert gf_gcdex(ZZ.map([1, 8, 7]), ZZ.map([1, 7, 1, 7]), 11, ZZ) == ([5, 6], [6], [1, 7]) + + +def test_gf_gcd(): + assert gf_gcd(ZZ.map([]), ZZ.map([]), 11, ZZ) == [] + assert gf_gcd(ZZ.map([2]), ZZ.map([]), 11, ZZ) == [1] + assert gf_gcd(ZZ.map([]), ZZ.map([2]), 11, ZZ) == [1] + assert gf_gcd(ZZ.map([2]), ZZ.map([2]), 11, ZZ) == [1] + + assert gf_gcd(ZZ.map([]), ZZ.map([1, 0]), 11, ZZ) == [1, 0] + assert gf_gcd(ZZ.map([1, 0]), ZZ.map([]), 11, ZZ) == [1, 0] + + assert gf_gcd(ZZ.map([3, 0]), ZZ.map([3, 0]), 11, ZZ) == [1, 0] + assert gf_gcd(ZZ.map([1, 8, 7]), ZZ.map([1, 7, 1, 7]), 11, ZZ) == [1, 7] + + +def test_gf_lcm(): + assert gf_lcm(ZZ.map([]), ZZ.map([]), 11, ZZ) == [] + assert gf_lcm(ZZ.map([2]), ZZ.map([]), 11, ZZ) == [] + assert gf_lcm(ZZ.map([]), ZZ.map([2]), 11, ZZ) == [] + assert gf_lcm(ZZ.map([2]), ZZ.map([2]), 11, ZZ) == [1] + + assert gf_lcm(ZZ.map([]), ZZ.map([1, 0]), 11, ZZ) == [] + assert gf_lcm(ZZ.map([1, 0]), ZZ.map([]), 11, ZZ) == [] + + assert gf_lcm(ZZ.map([3, 0]), ZZ.map([3, 0]), 11, ZZ) == [1, 0] + assert gf_lcm(ZZ.map([1, 8, 7]), ZZ.map([1, 7, 1, 7]), 11, ZZ) == [1, 8, 8, 8, 7] + + +def test_gf_cofactors(): + assert gf_cofactors(ZZ.map([]), ZZ.map([]), 11, ZZ) == ([], [], []) + assert gf_cofactors(ZZ.map([2]), ZZ.map([]), 11, ZZ) == ([1], [2], []) + assert gf_cofactors(ZZ.map([]), ZZ.map([2]), 11, ZZ) == ([1], [], [2]) + assert gf_cofactors(ZZ.map([2]), ZZ.map([2]), 11, ZZ) == ([1], [2], [2]) + + assert gf_cofactors(ZZ.map([]), ZZ.map([1, 0]), 11, ZZ) == ([1, 0], [], [1]) + assert gf_cofactors(ZZ.map([1, 0]), ZZ.map([]), 11, ZZ) == ([1, 0], [1], []) + + assert gf_cofactors(ZZ.map([3, 0]), ZZ.map([3, 0]), 11, ZZ) == ( + [1, 0], [3], [3]) + assert gf_cofactors(ZZ.map([1, 8, 7]), ZZ.map([1, 7, 1, 7]), 11, ZZ) == ( + ([1, 7], [1, 1], [1, 0, 1])) + + +def test_gf_diff(): + assert gf_diff([], 11, ZZ) == [] + assert gf_diff([7], 11, ZZ) == [] + + assert gf_diff([7, 3], 11, ZZ) == [7] + assert gf_diff([7, 3, 1], 11, ZZ) == [3, 3] + + assert gf_diff([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 11, ZZ) == [] + + +def test_gf_eval(): + assert gf_eval([], 4, 11, ZZ) == 0 + assert gf_eval([], 27, 11, ZZ) == 0 + assert gf_eval([7], 4, 11, ZZ) == 7 + assert gf_eval([7], 27, 11, ZZ) == 7 + + assert gf_eval([1, 0, 3, 2, 4, 3, 1, 2, 0], 0, 11, ZZ) == 0 + assert gf_eval([1, 0, 3, 2, 4, 3, 1, 2, 0], 4, 11, ZZ) == 9 + assert gf_eval([1, 0, 3, 2, 4, 3, 1, 2, 0], 27, 11, ZZ) == 5 + + assert gf_eval([4, 0, 0, 4, 6, 0, 1, 3, 5], 0, 11, ZZ) == 5 + assert gf_eval([4, 0, 0, 4, 6, 0, 1, 3, 5], 4, 11, ZZ) == 3 + assert gf_eval([4, 0, 0, 4, 6, 0, 1, 3, 5], 27, 11, ZZ) == 9 + + assert gf_multi_eval([3, 2, 1], [0, 1, 2, 3], 11, ZZ) == [1, 6, 6, 1] + + +def test_gf_compose(): + assert gf_compose([], [1, 0], 11, ZZ) == [] + assert gf_compose_mod([], [1, 0], [1, 0], 11, ZZ) == [] + + assert gf_compose([1], [], 11, ZZ) == [1] + assert gf_compose([1, 0], [], 11, ZZ) == [] + assert gf_compose([1, 0], [1, 0], 11, ZZ) == [1, 0] + + f = ZZ.map([1, 1, 4, 9, 1]) + g = ZZ.map([1, 1, 1]) + h = ZZ.map([1, 0, 0, 2]) + + assert gf_compose(g, h, 11, ZZ) == [1, 0, 0, 5, 0, 0, 7] + assert gf_compose_mod(g, h, f, 11, ZZ) == [3, 9, 6, 10] + + +def test_gf_trace_map(): + f = ZZ.map([1, 1, 4, 9, 1]) + a = [1, 1, 1] + c = ZZ.map([1, 0]) + b = gf_pow_mod(c, 11, f, 11, ZZ) + + assert gf_trace_map(a, b, c, 0, f, 11, ZZ) == \ + ([1, 1, 1], [1, 1, 1]) + assert gf_trace_map(a, b, c, 1, f, 11, ZZ) == \ + ([5, 2, 10, 3], [5, 3, 0, 4]) + assert gf_trace_map(a, b, c, 2, f, 11, ZZ) == \ + ([5, 9, 5, 3], [10, 1, 5, 7]) + assert gf_trace_map(a, b, c, 3, f, 11, ZZ) == \ + ([1, 10, 6, 0], [7]) + assert gf_trace_map(a, b, c, 4, f, 11, ZZ) == \ + ([1, 1, 1], [1, 1, 8]) + assert gf_trace_map(a, b, c, 5, f, 11, ZZ) == \ + ([5, 2, 10, 3], [5, 3, 0, 0]) + assert gf_trace_map(a, b, c, 11, f, 11, ZZ) == \ + ([1, 10, 6, 0], [10]) + + +def test_gf_irreducible(): + assert gf_irreducible_p(gf_irreducible(1, 11, ZZ), 11, ZZ) is True + assert gf_irreducible_p(gf_irreducible(2, 11, ZZ), 11, ZZ) is True + assert gf_irreducible_p(gf_irreducible(3, 11, ZZ), 11, ZZ) is True + assert gf_irreducible_p(gf_irreducible(4, 11, ZZ), 11, ZZ) is True + assert gf_irreducible_p(gf_irreducible(5, 11, ZZ), 11, ZZ) is True + assert gf_irreducible_p(gf_irreducible(6, 11, ZZ), 11, ZZ) is True + assert gf_irreducible_p(gf_irreducible(7, 11, ZZ), 11, ZZ) is True + + +def test_gf_irreducible_p(): + assert gf_irred_p_ben_or(ZZ.map([7]), 11, ZZ) is True + assert gf_irred_p_ben_or(ZZ.map([7, 3]), 11, ZZ) is True + assert gf_irred_p_ben_or(ZZ.map([7, 3, 1]), 11, ZZ) is False + + assert gf_irred_p_rabin(ZZ.map([7]), 11, ZZ) is True + assert gf_irred_p_rabin(ZZ.map([7, 3]), 11, ZZ) is True + assert gf_irred_p_rabin(ZZ.map([7, 3, 1]), 11, ZZ) is False + + config.setup('GF_IRRED_METHOD', 'ben-or') + + assert gf_irreducible_p(ZZ.map([7]), 11, ZZ) is True + assert gf_irreducible_p(ZZ.map([7, 3]), 11, ZZ) is True + assert gf_irreducible_p(ZZ.map([7, 3, 1]), 11, ZZ) is False + + config.setup('GF_IRRED_METHOD', 'rabin') + + assert gf_irreducible_p(ZZ.map([7]), 11, ZZ) is True + assert gf_irreducible_p(ZZ.map([7, 3]), 11, ZZ) is True + assert gf_irreducible_p(ZZ.map([7, 3, 1]), 11, ZZ) is False + + config.setup('GF_IRRED_METHOD', 'other') + raises(KeyError, lambda: gf_irreducible_p([7], 11, ZZ)) + config.setup('GF_IRRED_METHOD') + + f = ZZ.map([1, 9, 9, 13, 16, 15, 6, 7, 7, 7, 10]) + g = ZZ.map([1, 7, 16, 7, 15, 13, 13, 11, 16, 10, 9]) + + h = gf_mul(f, g, 17, ZZ) + + assert gf_irred_p_ben_or(f, 17, ZZ) is True + assert gf_irred_p_ben_or(g, 17, ZZ) is True + + assert gf_irred_p_ben_or(h, 17, ZZ) is False + + assert gf_irred_p_rabin(f, 17, ZZ) is True + assert gf_irred_p_rabin(g, 17, ZZ) is True + + assert gf_irred_p_rabin(h, 17, ZZ) is False + + +def test_gf_squarefree(): + assert gf_sqf_list([], 11, ZZ) == (0, []) + assert gf_sqf_list([1], 11, ZZ) == (1, []) + assert gf_sqf_list([1, 1], 11, ZZ) == (1, [([1, 1], 1)]) + + assert gf_sqf_p([], 11, ZZ) is True + assert gf_sqf_p([1], 11, ZZ) is True + assert gf_sqf_p([1, 1], 11, ZZ) is True + + f = gf_from_dict({11: 1, 0: 1}, 11, ZZ) + + assert gf_sqf_p(f, 11, ZZ) is False + + assert gf_sqf_list(f, 11, ZZ) == \ + (1, [([1, 1], 11)]) + + f = [1, 5, 8, 4] + + assert gf_sqf_p(f, 11, ZZ) is False + + assert gf_sqf_list(f, 11, ZZ) == \ + (1, [([1, 1], 1), + ([1, 2], 2)]) + + assert gf_sqf_part(f, 11, ZZ) == [1, 3, 2] + + f = [1, 0, 0, 2, 0, 0, 2, 0, 0, 1, 0] + + assert gf_sqf_list(f, 3, ZZ) == \ + (1, [([1, 0], 1), + ([1, 1], 3), + ([1, 2], 6)]) + +def test_gf_frobenius_map(): + f = ZZ.map([2, 0, 1, 0, 2, 2, 0, 2, 2, 2]) + g = ZZ.map([1,1,0,2,0,1,0,2,0,1]) + p = 3 + b = gf_frobenius_monomial_base(g, p, ZZ) + h = gf_frobenius_map(f, g, b, p, ZZ) + h1 = gf_pow_mod(f, p, g, p, ZZ) + assert h == h1 + + +def test_gf_berlekamp(): + f = gf_from_int_poly([1, -3, 1, -3, -1, -3, 1], 11) + + Q = [[1, 0, 0, 0, 0, 0], + [3, 5, 8, 8, 6, 5], + [3, 6, 6, 1, 10, 0], + [9, 4, 10, 3, 7, 9], + [7, 8, 10, 0, 0, 8], + [8, 10, 7, 8, 10, 8]] + + V = [[1, 0, 0, 0, 0, 0], + [0, 1, 1, 1, 1, 0], + [0, 0, 7, 9, 0, 1]] + + assert gf_Qmatrix(f, 11, ZZ) == Q + assert gf_Qbasis(Q, 11, ZZ) == V + + assert gf_berlekamp(f, 11, ZZ) == \ + [[1, 1], [1, 5, 3], [1, 2, 3, 4]] + + f = ZZ.map([1, 0, 1, 0, 10, 10, 8, 2, 8]) + + Q = ZZ.map([[1, 0, 0, 0, 0, 0, 0, 0], + [2, 1, 7, 11, 10, 12, 5, 11], + [3, 6, 4, 3, 0, 4, 7, 2], + [4, 3, 6, 5, 1, 6, 2, 3], + [2, 11, 8, 8, 3, 1, 3, 11], + [6, 11, 8, 6, 2, 7, 10, 9], + [5, 11, 7, 10, 0, 11, 7, 12], + [3, 3, 12, 5, 0, 11, 9, 12]]) + + V = [[1, 0, 0, 0, 0, 0, 0, 0], + [0, 5, 5, 0, 9, 5, 1, 0], + [0, 9, 11, 9, 10, 12, 0, 1]] + + assert gf_Qmatrix(f, 13, ZZ) == Q + assert gf_Qbasis(Q, 13, ZZ) == V + + assert gf_berlekamp(f, 13, ZZ) == \ + [[1, 3], [1, 8, 4, 12], [1, 2, 3, 4, 6]] + + +def test_gf_ddf(): + f = gf_from_dict({15: ZZ(1), 0: ZZ(-1)}, 11, ZZ) + g = [([1, 0, 0, 0, 0, 10], 1), + ([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2)] + + assert gf_ddf_zassenhaus(f, 11, ZZ) == g + assert gf_ddf_shoup(f, 11, ZZ) == g + + f = gf_from_dict({63: ZZ(1), 0: ZZ(1)}, 2, ZZ) + g = [([1, 1], 1), + ([1, 1, 1], 2), + ([1, 1, 1, 1, 1, 1, 1], 3), + ([1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1], 6)] + + assert gf_ddf_zassenhaus(f, 2, ZZ) == g + assert gf_ddf_shoup(f, 2, ZZ) == g + + f = gf_from_dict({6: ZZ(1), 5: ZZ(-1), 4: ZZ(1), 3: ZZ(1), 1: ZZ(-1)}, 3, ZZ) + g = [([1, 1, 0], 1), + ([1, 1, 0, 1, 2], 2)] + + assert gf_ddf_zassenhaus(f, 3, ZZ) == g + assert gf_ddf_shoup(f, 3, ZZ) == g + + f = ZZ.map([1, 2, 5, 26, 677, 436, 791, 325, 456, 24, 577]) + g = [([1, 701], 1), + ([1, 110, 559, 532, 694, 151, 110, 70, 735, 122], 9)] + + assert gf_ddf_zassenhaus(f, 809, ZZ) == g + assert gf_ddf_shoup(f, 809, ZZ) == g + + p = ZZ(nextprime(int((2**15 * pi).evalf()))) + f = gf_from_dict({15: 1, 1: 1, 0: 1}, p, ZZ) + g = [([1, 22730, 68144], 2), + ([1, 64876, 83977, 10787, 12561, 68608, 52650, 88001, 84356], 4), + ([1, 15347, 95022, 84569, 94508, 92335], 5)] + + assert gf_ddf_zassenhaus(f, p, ZZ) == g + assert gf_ddf_shoup(f, p, ZZ) == g + + +def test_gf_edf(): + f = ZZ.map([1, 1, 0, 1, 2]) + g = ZZ.map([[1, 0, 1], [1, 1, 2]]) + + assert gf_edf_zassenhaus(f, 2, 3, ZZ) == g + assert gf_edf_shoup(f, 2, 3, ZZ) == g + + +def test_issue_23174(): + f = ZZ.map([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]) + g = ZZ.map([[1, 0, 0, 1, 1, 1, 0, 0, 1], [1, 1, 1, 0, 1, 0, 1, 1, 1]]) + + assert gf_edf_zassenhaus(f, 8, 2, ZZ) == g + + +def test_gf_factor(): + assert gf_factor([], 11, ZZ) == (0, []) + assert gf_factor([1], 11, ZZ) == (1, []) + assert gf_factor([1, 1], 11, ZZ) == (1, [([1, 1], 1)]) + + assert gf_factor_sqf([], 11, ZZ) == (0, []) + assert gf_factor_sqf([1], 11, ZZ) == (1, []) + assert gf_factor_sqf([1, 1], 11, ZZ) == (1, [[1, 1]]) + + config.setup('GF_FACTOR_METHOD', 'berlekamp') + + assert gf_factor_sqf([], 11, ZZ) == (0, []) + assert gf_factor_sqf([1], 11, ZZ) == (1, []) + assert gf_factor_sqf([1, 1], 11, ZZ) == (1, [[1, 1]]) + + config.setup('GF_FACTOR_METHOD', 'zassenhaus') + + assert gf_factor_sqf([], 11, ZZ) == (0, []) + assert gf_factor_sqf([1], 11, ZZ) == (1, []) + assert gf_factor_sqf([1, 1], 11, ZZ) == (1, [[1, 1]]) + + config.setup('GF_FACTOR_METHOD', 'shoup') + + assert gf_factor_sqf(ZZ.map([]), 11, ZZ) == (0, []) + assert gf_factor_sqf(ZZ.map([1]), 11, ZZ) == (1, []) + assert gf_factor_sqf(ZZ.map([1, 1]), 11, ZZ) == (1, [[1, 1]]) + + f, p = ZZ.map([1, 0, 0, 1, 0]), 2 + + g = (1, [([1, 0], 1), + ([1, 1], 1), + ([1, 1, 1], 1)]) + + config.setup('GF_FACTOR_METHOD', 'berlekamp') + assert gf_factor(f, p, ZZ) == g + + config.setup('GF_FACTOR_METHOD', 'zassenhaus') + assert gf_factor(f, p, ZZ) == g + + config.setup('GF_FACTOR_METHOD', 'shoup') + assert gf_factor(f, p, ZZ) == g + + g = (1, [[1, 0], + [1, 1], + [1, 1, 1]]) + + config.setup('GF_FACTOR_METHOD', 'berlekamp') + assert gf_factor_sqf(f, p, ZZ) == g + + config.setup('GF_FACTOR_METHOD', 'zassenhaus') + assert gf_factor_sqf(f, p, ZZ) == g + + config.setup('GF_FACTOR_METHOD', 'shoup') + assert gf_factor_sqf(f, p, ZZ) == g + + f, p = gf_from_int_poly([1, -3, 1, -3, -1, -3, 1], 11), 11 + + g = (1, [([1, 1], 1), + ([1, 5, 3], 1), + ([1, 2, 3, 4], 1)]) + + config.setup('GF_FACTOR_METHOD', 'berlekamp') + assert gf_factor(f, p, ZZ) == g + + config.setup('GF_FACTOR_METHOD', 'zassenhaus') + assert gf_factor(f, p, ZZ) == g + + config.setup('GF_FACTOR_METHOD', 'shoup') + assert gf_factor(f, p, ZZ) == g + + f, p = [1, 5, 8, 4], 11 + + g = (1, [([1, 1], 1), ([1, 2], 2)]) + + config.setup('GF_FACTOR_METHOD', 'berlekamp') + assert gf_factor(f, p, ZZ) == g + + config.setup('GF_FACTOR_METHOD', 'zassenhaus') + assert gf_factor(f, p, ZZ) == g + + config.setup('GF_FACTOR_METHOD', 'shoup') + assert gf_factor(f, p, ZZ) == g + + f, p = [1, 1, 10, 1, 0, 10, 10, 10, 0, 0], 11 + + g = (1, [([1, 0], 2), ([1, 9, 5], 1), ([1, 3, 0, 8, 5, 2], 1)]) + + config.setup('GF_FACTOR_METHOD', 'berlekamp') + assert gf_factor(f, p, ZZ) == g + + config.setup('GF_FACTOR_METHOD', 'zassenhaus') + assert gf_factor(f, p, ZZ) == g + + config.setup('GF_FACTOR_METHOD', 'shoup') + assert gf_factor(f, p, ZZ) == g + + f, p = gf_from_dict({32: 1, 0: 1}, 11, ZZ), 11 + + g = (1, [([1, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 10], 1), + ([1, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 10], 1)]) + + config.setup('GF_FACTOR_METHOD', 'berlekamp') + assert gf_factor(f, p, ZZ) == g + + config.setup('GF_FACTOR_METHOD', 'zassenhaus') + assert gf_factor(f, p, ZZ) == g + + config.setup('GF_FACTOR_METHOD', 'shoup') + assert gf_factor(f, p, ZZ) == g + + f, p = gf_from_dict({32: ZZ(8), 0: ZZ(5)}, 11, ZZ), 11 + + g = (8, [([1, 3], 1), + ([1, 8], 1), + ([1, 0, 9], 1), + ([1, 2, 2], 1), + ([1, 9, 2], 1), + ([1, 0, 5, 0, 7], 1), + ([1, 0, 6, 0, 7], 1), + ([1, 0, 0, 0, 1, 0, 0, 0, 6], 1), + ([1, 0, 0, 0, 10, 0, 0, 0, 6], 1)]) + + config.setup('GF_FACTOR_METHOD', 'berlekamp') + assert gf_factor(f, p, ZZ) == g + + config.setup('GF_FACTOR_METHOD', 'zassenhaus') + assert gf_factor(f, p, ZZ) == g + + config.setup('GF_FACTOR_METHOD', 'shoup') + assert gf_factor(f, p, ZZ) == g + + f, p = gf_from_dict({63: ZZ(8), 0: ZZ(5)}, 11, ZZ), 11 + + g = (8, [([1, 7], 1), + ([1, 4, 5], 1), + ([1, 6, 8, 2], 1), + ([1, 9, 9, 2], 1), + ([1, 0, 0, 9, 0, 0, 4], 1), + ([1, 2, 0, 8, 4, 6, 4], 1), + ([1, 2, 3, 8, 0, 6, 4], 1), + ([1, 2, 6, 0, 8, 4, 4], 1), + ([1, 3, 3, 1, 6, 8, 4], 1), + ([1, 5, 6, 0, 8, 6, 4], 1), + ([1, 6, 2, 7, 9, 8, 4], 1), + ([1, 10, 4, 7, 10, 7, 4], 1), + ([1, 10, 10, 1, 4, 9, 4], 1)]) + + config.setup('GF_FACTOR_METHOD', 'berlekamp') + assert gf_factor(f, p, ZZ) == g + + config.setup('GF_FACTOR_METHOD', 'zassenhaus') + assert gf_factor(f, p, ZZ) == g + + config.setup('GF_FACTOR_METHOD', 'shoup') + assert gf_factor(f, p, ZZ) == g + + # Gathen polynomials: x**n + x + 1 (mod p > 2**n * pi) + + p = ZZ(nextprime(int((2**15 * pi).evalf()))) + f = gf_from_dict({15: 1, 1: 1, 0: 1}, p, ZZ) + + assert gf_sqf_p(f, p, ZZ) is True + + g = (1, [([1, 22730, 68144], 1), + ([1, 81553, 77449, 86810, 4724], 1), + ([1, 86276, 56779, 14859, 31575], 1), + ([1, 15347, 95022, 84569, 94508, 92335], 1)]) + + config.setup('GF_FACTOR_METHOD', 'zassenhaus') + assert gf_factor(f, p, ZZ) == g + + config.setup('GF_FACTOR_METHOD', 'shoup') + assert gf_factor(f, p, ZZ) == g + + g = (1, [[1, 22730, 68144], + [1, 81553, 77449, 86810, 4724], + [1, 86276, 56779, 14859, 31575], + [1, 15347, 95022, 84569, 94508, 92335]]) + + config.setup('GF_FACTOR_METHOD', 'zassenhaus') + assert gf_factor_sqf(f, p, ZZ) == g + + config.setup('GF_FACTOR_METHOD', 'shoup') + assert gf_factor_sqf(f, p, ZZ) == g + + # Shoup polynomials: f = a_0 x**n + a_1 x**(n-1) + ... + a_n + # (mod p > 2**(n-2) * pi), where a_n = a_{n-1}**2 + 1, a_0 = 1 + + p = ZZ(nextprime(int((2**4 * pi).evalf()))) + f = ZZ.map([1, 2, 5, 26, 41, 39, 38]) + + assert gf_sqf_p(f, p, ZZ) is True + + g = (1, [([1, 44, 26], 1), + ([1, 11, 25, 18, 30], 1)]) + + config.setup('GF_FACTOR_METHOD', 'zassenhaus') + assert gf_factor(f, p, ZZ) == g + + config.setup('GF_FACTOR_METHOD', 'shoup') + assert gf_factor(f, p, ZZ) == g + + g = (1, [[1, 44, 26], + [1, 11, 25, 18, 30]]) + + config.setup('GF_FACTOR_METHOD', 'zassenhaus') + assert gf_factor_sqf(f, p, ZZ) == g + + config.setup('GF_FACTOR_METHOD', 'shoup') + assert gf_factor_sqf(f, p, ZZ) == g + + config.setup('GF_FACTOR_METHOD', 'other') + raises(KeyError, lambda: gf_factor([1, 1], 11, ZZ)) + config.setup('GF_FACTOR_METHOD') + + +def test_gf_csolve(): + assert gf_value([1, 7, 2, 4], 11) == 2204 + + assert linear_congruence(4, 3, 5) == [2] + assert linear_congruence(0, 3, 5) == [] + assert linear_congruence(6, 1, 4) == [] + assert linear_congruence(0, 5, 5) == [0, 1, 2, 3, 4] + assert linear_congruence(3, 12, 15) == [4, 9, 14] + assert linear_congruence(6, 0, 18) == [0, 3, 6, 9, 12, 15] + # _csolve_prime_las_vegas + assert _csolve_prime_las_vegas([2, 3, 1], 5) == [2, 4] + assert _csolve_prime_las_vegas([2, 0, 1], 5) == [] + from sympy.ntheory import primerange + for p in primerange(2, 100): + # f = x**(p-1) - 1 + f = gf_sub_ground(gf_pow([1, 0], p - 1, p, ZZ), 1, p, ZZ) + assert _csolve_prime_las_vegas(f, p) == list(range(1, p)) + # with power = 1 + assert csolve_prime([1, 3, 2, 17], 7) == [3] + assert csolve_prime([1, 3, 1, 5], 5) == [0, 1] + assert csolve_prime([3, 6, 9, 3], 3) == [0, 1, 2] + # with power > 1 + assert csolve_prime( + [1, 1, 223], 3, 4) == [4, 13, 22, 31, 40, 49, 58, 67, 76] + assert csolve_prime([3, 5, 2, 25], 5, 3) == [16, 50, 99] + assert csolve_prime([3, 2, 2, 49], 7, 3) == [147, 190, 234] + + assert gf_csolve([1, 1, 7], 189) == [13, 49, 76, 112, 139, 175] + assert gf_csolve([1, 3, 4, 1, 30], 60) == [10, 30] + assert gf_csolve([1, 1, 7], 15) == [] diff --git a/lib/python3.12/site-packages/sympy/polys/tests/test_groebnertools.py b/lib/python3.12/site-packages/sympy/polys/tests/test_groebnertools.py new file mode 100644 index 0000000000000000000000000000000000000000..b7d0fc112047ac26f67d096db02eb8a1c91cab89 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/polys/tests/test_groebnertools.py @@ -0,0 +1,533 @@ +"""Tests for Groebner bases. """ + +from sympy.polys.groebnertools import ( + groebner, sig, sig_key, + lbp, lbp_key, critical_pair, + cp_key, is_rewritable_or_comparable, + Sign, Polyn, Num, s_poly, f5_reduce, + groebner_lcm, groebner_gcd, is_groebner, + is_reduced +) + +from sympy.polys.fglmtools import _representing_matrices +from sympy.polys.orderings import lex, grlex + +from sympy.polys.rings import ring, xring +from sympy.polys.domains import ZZ, QQ + +from sympy.testing.pytest import slow +from sympy.polys import polyconfig as config + +def _do_test_groebner(): + R, x,y = ring("x,y", QQ, lex) + f = x**2 + 2*x*y**2 + g = x*y + 2*y**3 - 1 + + assert groebner([f, g], R) == [x, y**3 - QQ(1,2)] + + R, y,x = ring("y,x", QQ, lex) + f = 2*x**2*y + y**2 + g = 2*x**3 + x*y - 1 + + assert groebner([f, g], R) == [y, x**3 - QQ(1,2)] + + R, x,y,z = ring("x,y,z", QQ, lex) + f = x - z**2 + g = y - z**3 + + assert groebner([f, g], R) == [f, g] + + R, x,y = ring("x,y", QQ, grlex) + f = x**3 - 2*x*y + g = x**2*y + x - 2*y**2 + + assert groebner([f, g], R) == [x**2, x*y, -QQ(1,2)*x + y**2] + + R, x,y,z = ring("x,y,z", QQ, lex) + f = -x**2 + y + g = -x**3 + z + + assert groebner([f, g], R) == [x**2 - y, x*y - z, x*z - y**2, y**3 - z**2] + + R, x,y,z = ring("x,y,z", QQ, grlex) + f = -x**2 + y + g = -x**3 + z + + assert groebner([f, g], R) == [y**3 - z**2, x**2 - y, x*y - z, x*z - y**2] + + R, x,y,z = ring("x,y,z", QQ, lex) + f = -x**2 + z + g = -x**3 + y + + assert groebner([f, g], R) == [x**2 - z, x*y - z**2, x*z - y, y**2 - z**3] + + R, x,y,z = ring("x,y,z", QQ, grlex) + f = -x**2 + z + g = -x**3 + y + + assert groebner([f, g], R) == [-y**2 + z**3, x**2 - z, x*y - z**2, x*z - y] + + R, x,y,z = ring("x,y,z", QQ, lex) + f = x - y**2 + g = -y**3 + z + + assert groebner([f, g], R) == [x - y**2, y**3 - z] + + R, x,y,z = ring("x,y,z", QQ, grlex) + f = x - y**2 + g = -y**3 + z + + assert groebner([f, g], R) == [x**2 - y*z, x*y - z, -x + y**2] + + R, x,y,z = ring("x,y,z", QQ, lex) + f = x - z**2 + g = y - z**3 + + assert groebner([f, g], R) == [x - z**2, y - z**3] + + R, x,y,z = ring("x,y,z", QQ, grlex) + f = x - z**2 + g = y - z**3 + + assert groebner([f, g], R) == [x**2 - y*z, x*z - y, -x + z**2] + + R, x,y,z = ring("x,y,z", QQ, lex) + f = -y**2 + z + g = x - y**3 + + assert groebner([f, g], R) == [x - y*z, y**2 - z] + + R, x,y,z = ring("x,y,z", QQ, grlex) + f = -y**2 + z + g = x - y**3 + + assert groebner([f, g], R) == [-x**2 + z**3, x*y - z**2, y**2 - z, -x + y*z] + + R, x,y,z = ring("x,y,z", QQ, lex) + f = y - z**2 + g = x - z**3 + + assert groebner([f, g], R) == [x - z**3, y - z**2] + + R, x,y,z = ring("x,y,z", QQ, grlex) + f = y - z**2 + g = x - z**3 + + assert groebner([f, g], R) == [-x**2 + y**3, x*z - y**2, -x + y*z, -y + z**2] + + R, x,y,z = ring("x,y,z", QQ, lex) + f = 4*x**2*y**2 + 4*x*y + 1 + g = x**2 + y**2 - 1 + + assert groebner([f, g], R) == [ + x - 4*y**7 + 8*y**5 - 7*y**3 + 3*y, + y**8 - 2*y**6 + QQ(3,2)*y**4 - QQ(1,2)*y**2 + QQ(1,16), + ] + +def test_groebner_buchberger(): + with config.using(groebner='buchberger'): + _do_test_groebner() + +def test_groebner_f5b(): + with config.using(groebner='f5b'): + _do_test_groebner() + +def _do_test_benchmark_minpoly(): + R, x,y,z = ring("x,y,z", QQ, lex) + + F = [x**3 + x + 1, y**2 + y + 1, (x + y) * z - (x**2 + y)] + G = [x + QQ(155,2067)*z**5 - QQ(355,689)*z**4 + QQ(6062,2067)*z**3 - QQ(3687,689)*z**2 + QQ(6878,2067)*z - QQ(25,53), + y + QQ(4,53)*z**5 - QQ(91,159)*z**4 + QQ(523,159)*z**3 - QQ(387,53)*z**2 + QQ(1043,159)*z - QQ(308,159), + z**6 - 7*z**5 + 41*z**4 - 82*z**3 + 89*z**2 - 46*z + 13] + + assert groebner(F, R) == G + +def test_benchmark_minpoly_buchberger(): + with config.using(groebner='buchberger'): + _do_test_benchmark_minpoly() + +def test_benchmark_minpoly_f5b(): + with config.using(groebner='f5b'): + _do_test_benchmark_minpoly() + + +def test_benchmark_coloring(): + V = range(1, 12 + 1) + E = [(1, 2), (2, 3), (1, 4), (1, 6), (1, 12), (2, 5), (2, 7), (3, 8), (3, 10), + (4, 11), (4, 9), (5, 6), (6, 7), (7, 8), (8, 9), (9, 10), (10, 11), + (11, 12), (5, 12), (5, 9), (6, 10), (7, 11), (8, 12), (3, 4)] + + R, V = xring([ "x%d" % v for v in V ], QQ, lex) + E = [(V[i - 1], V[j - 1]) for i, j in E] + + x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12 = V + + I3 = [x**3 - 1 for x in V] + Ig = [x**2 + x*y + y**2 for x, y in E] + + I = I3 + Ig + + assert groebner(I[:-1], R) == [ + x1 + x11 + x12, + x2 - x11, + x3 - x12, + x4 - x12, + x5 + x11 + x12, + x6 - x11, + x7 - x12, + x8 + x11 + x12, + x9 - x11, + x10 + x11 + x12, + x11**2 + x11*x12 + x12**2, + x12**3 - 1, + ] + + assert groebner(I, R) == [1] + + +def _do_test_benchmark_katsura_3(): + R, x0,x1,x2 = ring("x:3", ZZ, lex) + I = [x0 + 2*x1 + 2*x2 - 1, + x0**2 + 2*x1**2 + 2*x2**2 - x0, + 2*x0*x1 + 2*x1*x2 - x1] + + assert groebner(I, R) == [ + -7 + 7*x0 + 8*x2 + 158*x2**2 - 420*x2**3, + 7*x1 + 3*x2 - 79*x2**2 + 210*x2**3, + x2 + x2**2 - 40*x2**3 + 84*x2**4, + ] + + R, x0,x1,x2 = ring("x:3", ZZ, grlex) + I = [ i.set_ring(R) for i in I ] + + assert groebner(I, R) == [ + 7*x1 + 3*x2 - 79*x2**2 + 210*x2**3, + -x1 + x2 - 3*x2**2 + 5*x1**2, + -x1 - 4*x2 + 10*x1*x2 + 12*x2**2, + -1 + x0 + 2*x1 + 2*x2, + ] + +def test_benchmark_katsura3_buchberger(): + with config.using(groebner='buchberger'): + _do_test_benchmark_katsura_3() + +def test_benchmark_katsura3_f5b(): + with config.using(groebner='f5b'): + _do_test_benchmark_katsura_3() + +def _do_test_benchmark_katsura_4(): + R, x0,x1,x2,x3 = ring("x:4", ZZ, lex) + I = [x0 + 2*x1 + 2*x2 + 2*x3 - 1, + x0**2 + 2*x1**2 + 2*x2**2 + 2*x3**2 - x0, + 2*x0*x1 + 2*x1*x2 + 2*x2*x3 - x1, + x1**2 + 2*x0*x2 + 2*x1*x3 - x2] + + assert groebner(I, R) == [ + 5913075*x0 - 159690237696*x3**7 + 31246269696*x3**6 + 27439610544*x3**5 - 6475723368*x3**4 - 838935856*x3**3 + 275119624*x3**2 + 4884038*x3 - 5913075, + 1971025*x1 - 97197721632*x3**7 + 73975630752*x3**6 - 12121915032*x3**5 - 2760941496*x3**4 + 814792828*x3**3 - 1678512*x3**2 - 9158924*x3, + 5913075*x2 + 371438283744*x3**7 - 237550027104*x3**6 + 22645939824*x3**5 + 11520686172*x3**4 - 2024910556*x3**3 - 132524276*x3**2 + 30947828*x3, + 128304*x3**8 - 93312*x3**7 + 15552*x3**6 + 3144*x3**5 - + 1120*x3**4 + 36*x3**3 + 15*x3**2 - x3, + ] + + R, x0,x1,x2,x3 = ring("x:4", ZZ, grlex) + I = [ i.set_ring(R) for i in I ] + + assert groebner(I, R) == [ + 393*x1 - 4662*x2**2 + 4462*x2*x3 - 59*x2 + 224532*x3**4 - 91224*x3**3 - 678*x3**2 + 2046*x3, + -x1 + 196*x2**3 - 21*x2**2 + 60*x2*x3 - 18*x2 - 168*x3**3 + 83*x3**2 - 9*x3, + -6*x1 + 1134*x2**2*x3 - 189*x2**2 - 466*x2*x3 + 32*x2 - 630*x3**3 + 57*x3**2 + 51*x3, + 33*x1 + 63*x2**2 + 2268*x2*x3**2 - 188*x2*x3 + 34*x2 + 2520*x3**3 - 849*x3**2 + 3*x3, + 7*x1**2 - x1 - 7*x2**2 - 24*x2*x3 + 3*x2 - 15*x3**2 + 5*x3, + 14*x1*x2 - x1 + 14*x2**2 + 18*x2*x3 - 4*x2 + 6*x3**2 - 2*x3, + 14*x1*x3 - x1 + 7*x2**2 + 32*x2*x3 - 4*x2 + 27*x3**2 - 9*x3, + x0 + 2*x1 + 2*x2 + 2*x3 - 1, + ] + +def test_benchmark_kastura_4_buchberger(): + with config.using(groebner='buchberger'): + _do_test_benchmark_katsura_4() + +def test_benchmark_kastura_4_f5b(): + with config.using(groebner='f5b'): + _do_test_benchmark_katsura_4() + +def _do_test_benchmark_czichowski(): + R, x,t = ring("x,t", ZZ, lex) + I = [9*x**8 + 36*x**7 - 32*x**6 - 252*x**5 - 78*x**4 + 468*x**3 + 288*x**2 - 108*x + 9, + (-72 - 72*t)*x**7 + (-256 - 252*t)*x**6 + (192 + 192*t)*x**5 + (1280 + 1260*t)*x**4 + (312 + 312*t)*x**3 + (-404*t)*x**2 + (-576 - 576*t)*x + 96 + 108*t] + + assert groebner(I, R) == [ + 3725588592068034903797967297424801242396746870413359539263038139343329273586196480000*x - + 160420835591776763325581422211936558925462474417709511019228211783493866564923546661604487873*t**7 - + 1406108495478033395547109582678806497509499966197028487131115097902188374051595011248311352864*t**6 - + 5241326875850889518164640374668786338033653548841427557880599579174438246266263602956254030352*t**5 - + 10758917262823299139373269714910672770004760114329943852726887632013485035262879510837043892416*t**4 - + 13119383576444715672578819534846747735372132018341964647712009275306635391456880068261130581248*t**3 - + 9491412317016197146080450036267011389660653495578680036574753839055748080962214787557853941760*t**2 - + 3767520915562795326943800040277726397326609797172964377014046018280260848046603967211258368000*t - + 632314652371226552085897259159210286886724229880266931574701654721512325555116066073245696000, + 610733380717522355121*t**8 + + 6243748742141230639968*t**7 + + 27761407182086143225024*t**6 + + 70066148869420956398592*t**5 + + 109701225644313784229376*t**4 + + 109009005495588442152960*t**3 + + 67072101084384786432000*t**2 + + 23339979742629593088000*t + + 3513592776846090240000, + ] + + R, x,t = ring("x,t", ZZ, grlex) + I = [ i.set_ring(R) for i in I ] + + assert groebner(I, R) == [ + 16996618586000601590732959134095643086442*t**3*x - + 32936701459297092865176560282688198064839*t**3 + + 78592411049800639484139414821529525782364*t**2*x - + 120753953358671750165454009478961405619916*t**2 + + 120988399875140799712152158915653654637280*t*x - + 144576390266626470824138354942076045758736*t + + 60017634054270480831259316163620768960*x**2 + + 61976058033571109604821862786675242894400*x - + 56266268491293858791834120380427754600960, + 576689018321912327136790519059646508441672750656050290242749*t**4 + + 2326673103677477425562248201573604572527893938459296513327336*t**3 + + 110743790416688497407826310048520299245819959064297990236000*t**2*x + + 3308669114229100853338245486174247752683277925010505284338016*t**2 + + 323150205645687941261103426627818874426097912639158572428800*t*x + + 1914335199925152083917206349978534224695445819017286960055680*t + + 861662882561803377986838989464278045397192862768588480000*x**2 + + 235296483281783440197069672204341465480107019878814196672000*x + + 361850798943225141738895123621685122544503614946436727532800, + -117584925286448670474763406733005510014188341867*t**3 + + 68566565876066068463853874568722190223721653044*t**2*x - + 435970731348366266878180788833437896139920683940*t**2 + + 196297602447033751918195568051376792491869233408*t*x - + 525011527660010557871349062870980202067479780112*t + + 517905853447200553360289634770487684447317120*x**3 + + 569119014870778921949288951688799397569321920*x**2 + + 138877356748142786670127389526667463202210102080*x - + 205109210539096046121625447192779783475018619520, + -3725142681462373002731339445216700112264527*t**3 + + 583711207282060457652784180668273817487940*t**2*x - + 12381382393074485225164741437227437062814908*t**2 + + 151081054097783125250959636747516827435040*t*x**2 + + 1814103857455163948531448580501928933873280*t*x - + 13353115629395094645843682074271212731433648*t + + 236415091385250007660606958022544983766080*x**2 + + 1390443278862804663728298060085399578417600*x - + 4716885828494075789338754454248931750698880, + ] + +# NOTE: This is very slow (> 2 minutes on 3.4 GHz) without GMPY +@slow +def test_benchmark_czichowski_buchberger(): + with config.using(groebner='buchberger'): + _do_test_benchmark_czichowski() + +def test_benchmark_czichowski_f5b(): + with config.using(groebner='f5b'): + _do_test_benchmark_czichowski() + +def _do_test_benchmark_cyclic_4(): + R, a,b,c,d = ring("a,b,c,d", ZZ, lex) + + I = [a + b + c + d, + a*b + a*d + b*c + b*d, + a*b*c + a*b*d + a*c*d + b*c*d, + a*b*c*d - 1] + + assert groebner(I, R) == [ + 4*a + 3*d**9 - 4*d**5 - 3*d, + 4*b + 4*c - 3*d**9 + 4*d**5 + 7*d, + 4*c**2 + 3*d**10 - 4*d**6 - 3*d**2, + 4*c*d**4 + 4*c - d**9 + 4*d**5 + 5*d, d**12 - d**8 - d**4 + 1 + ] + + R, a,b,c,d = ring("a,b,c,d", ZZ, grlex) + I = [ i.set_ring(R) for i in I ] + + assert groebner(I, R) == [ + 3*b*c - c**2 + d**6 - 3*d**2, + -b + 3*c**2*d**3 - c - d**5 - 4*d, + -b + 3*c*d**4 + 2*c + 2*d**5 + 2*d, + c**4 + 2*c**2*d**2 - d**4 - 2, + c**3*d + c*d**3 + d**4 + 1, + b*c**2 - c**3 - c**2*d - 2*c*d**2 - d**3, + b**2 - c**2, b*d + c**2 + c*d + d**2, + a + b + c + d + ] + +def test_benchmark_cyclic_4_buchberger(): + with config.using(groebner='buchberger'): + _do_test_benchmark_cyclic_4() + +def test_benchmark_cyclic_4_f5b(): + with config.using(groebner='f5b'): + _do_test_benchmark_cyclic_4() + +def test_sig_key(): + s1 = sig((0,) * 3, 2) + s2 = sig((1,) * 3, 4) + s3 = sig((2,) * 3, 2) + + assert sig_key(s1, lex) > sig_key(s2, lex) + assert sig_key(s2, lex) < sig_key(s3, lex) + + +def test_lbp_key(): + R, x,y,z,t = ring("x,y,z,t", ZZ, lex) + + p1 = lbp(sig((0,) * 4, 3), R.zero, 12) + p2 = lbp(sig((0,) * 4, 4), R.zero, 13) + p3 = lbp(sig((0,) * 4, 4), R.zero, 12) + + assert lbp_key(p1) > lbp_key(p2) + assert lbp_key(p2) < lbp_key(p3) + + +def test_critical_pair(): + # from cyclic4 with grlex + R, x,y,z,t = ring("x,y,z,t", QQ, grlex) + + p1 = (((0, 0, 0, 0), 4), y*z*t**2 + z**2*t**2 - t**4 - 1, 4) + q1 = (((0, 0, 0, 0), 2), -y**2 - y*t - z*t - t**2, 2) + + p2 = (((0, 0, 0, 2), 3), z**3*t**2 + z**2*t**3 - z - t, 5) + q2 = (((0, 0, 2, 2), 2), y*z + z*t**5 + z*t + t**6, 13) + + assert critical_pair(p1, q1, R) == ( + ((0, 0, 1, 2), 2), ((0, 0, 1, 2), QQ(-1, 1)), (((0, 0, 0, 0), 2), -y**2 - y*t - z*t - t**2, 2), + ((0, 1, 0, 0), 4), ((0, 1, 0, 0), QQ(1, 1)), (((0, 0, 0, 0), 4), y*z*t**2 + z**2*t**2 - t**4 - 1, 4) + ) + assert critical_pair(p2, q2, R) == ( + ((0, 0, 4, 2), 2), ((0, 0, 2, 0), QQ(1, 1)), (((0, 0, 2, 2), 2), y*z + z*t**5 + z*t + t**6, 13), + ((0, 0, 0, 5), 3), ((0, 0, 0, 3), QQ(1, 1)), (((0, 0, 0, 2), 3), z**3*t**2 + z**2*t**3 - z - t, 5) + ) + +def test_cp_key(): + # from cyclic4 with grlex + R, x,y,z,t = ring("x,y,z,t", QQ, grlex) + + p1 = (((0, 0, 0, 0), 4), y*z*t**2 + z**2*t**2 - t**4 - 1, 4) + q1 = (((0, 0, 0, 0), 2), -y**2 - y*t - z*t - t**2, 2) + + p2 = (((0, 0, 0, 2), 3), z**3*t**2 + z**2*t**3 - z - t, 5) + q2 = (((0, 0, 2, 2), 2), y*z + z*t**5 + z*t + t**6, 13) + + cp1 = critical_pair(p1, q1, R) + cp2 = critical_pair(p2, q2, R) + + assert cp_key(cp1, R) < cp_key(cp2, R) + + cp1 = critical_pair(p1, p2, R) + cp2 = critical_pair(q1, q2, R) + + assert cp_key(cp1, R) < cp_key(cp2, R) + + +def test_is_rewritable_or_comparable(): + # from katsura4 with grlex + R, x,y,z,t = ring("x,y,z,t", QQ, grlex) + + p = lbp(sig((0, 0, 2, 1), 2), R.zero, 2) + B = [lbp(sig((0, 0, 0, 1), 2), QQ(2,45)*y**2 + QQ(1,5)*y*z + QQ(5,63)*y*t + z**2*t + QQ(4,45)*z**2 + QQ(76,35)*z*t**2 - QQ(32,105)*z*t + QQ(13,7)*t**3 - QQ(13,21)*t**2, 6)] + + # rewritable: + assert is_rewritable_or_comparable(Sign(p), Num(p), B) is True + + p = lbp(sig((0, 1, 1, 0), 2), R.zero, 7) + B = [lbp(sig((0, 0, 0, 0), 3), QQ(10,3)*y*z + QQ(4,3)*y*t - QQ(1,3)*y + 4*z**2 + QQ(22,3)*z*t - QQ(4,3)*z + 4*t**2 - QQ(4,3)*t, 3)] + + # comparable: + assert is_rewritable_or_comparable(Sign(p), Num(p), B) is True + + +def test_f5_reduce(): + # katsura3 with lex + R, x,y,z = ring("x,y,z", QQ, lex) + + F = [(((0, 0, 0), 1), x + 2*y + 2*z - 1, 1), + (((0, 0, 0), 2), 6*y**2 + 8*y*z - 2*y + 6*z**2 - 2*z, 2), + (((0, 0, 0), 3), QQ(10,3)*y*z - QQ(1,3)*y + 4*z**2 - QQ(4,3)*z, 3), + (((0, 0, 1), 2), y + 30*z**3 - QQ(79,7)*z**2 + QQ(3,7)*z, 4), + (((0, 0, 2), 2), z**4 - QQ(10,21)*z**3 + QQ(1,84)*z**2 + QQ(1,84)*z, 5)] + + cp = critical_pair(F[0], F[1], R) + s = s_poly(cp) + + assert f5_reduce(s, F) == (((0, 2, 0), 1), R.zero, 1) + + s = lbp(sig(Sign(s)[0], 100), Polyn(s), Num(s)) + assert f5_reduce(s, F) == s + + +def test_representing_matrices(): + R, x,y = ring("x,y", QQ, grlex) + + basis = [(0, 0), (0, 1), (1, 0), (1, 1)] + F = [x**2 - x - 3*y + 1, -2*x + y**2 + y - 1] + + assert _representing_matrices(basis, F, R) == [ + [[QQ(0, 1), QQ(0, 1),-QQ(1, 1), QQ(3, 1)], + [QQ(0, 1), QQ(0, 1), QQ(3, 1),-QQ(4, 1)], + [QQ(1, 1), QQ(0, 1), QQ(1, 1), QQ(6, 1)], + [QQ(0, 1), QQ(1, 1), QQ(0, 1), QQ(1, 1)]], + [[QQ(0, 1), QQ(1, 1), QQ(0, 1),-QQ(2, 1)], + [QQ(1, 1),-QQ(1, 1), QQ(0, 1), QQ(6, 1)], + [QQ(0, 1), QQ(2, 1), QQ(0, 1), QQ(3, 1)], + [QQ(0, 1), QQ(0, 1), QQ(1, 1),-QQ(1, 1)]]] + +def test_groebner_lcm(): + R, x,y,z = ring("x,y,z", ZZ) + + assert groebner_lcm(x**2 - y**2, x - y) == x**2 - y**2 + assert groebner_lcm(2*x**2 - 2*y**2, 2*x - 2*y) == 2*x**2 - 2*y**2 + + R, x,y,z = ring("x,y,z", QQ) + + assert groebner_lcm(x**2 - y**2, x - y) == x**2 - y**2 + assert groebner_lcm(2*x**2 - 2*y**2, 2*x - 2*y) == 2*x**2 - 2*y**2 + + R, x,y = ring("x,y", ZZ) + + assert groebner_lcm(x**2*y, x*y**2) == x**2*y**2 + + f = 2*x*y**5 - 3*x*y**4 - 2*x*y**3 + 3*x*y**2 + g = y**5 - 2*y**3 + y + h = 2*x*y**7 - 3*x*y**6 - 4*x*y**5 + 6*x*y**4 + 2*x*y**3 - 3*x*y**2 + + assert groebner_lcm(f, g) == h + + f = x**3 - 3*x**2*y - 9*x*y**2 - 5*y**3 + g = x**4 + 6*x**3*y + 12*x**2*y**2 + 10*x*y**3 + 3*y**4 + h = x**5 + x**4*y - 18*x**3*y**2 - 50*x**2*y**3 - 47*x*y**4 - 15*y**5 + + assert groebner_lcm(f, g) == h + +def test_groebner_gcd(): + R, x,y,z = ring("x,y,z", ZZ) + + assert groebner_gcd(x**2 - y**2, x - y) == x - y + assert groebner_gcd(2*x**2 - 2*y**2, 2*x - 2*y) == 2*x - 2*y + + R, x,y,z = ring("x,y,z", QQ) + + assert groebner_gcd(x**2 - y**2, x - y) == x - y + assert groebner_gcd(2*x**2 - 2*y**2, 2*x - 2*y) == x - y + +def test_is_groebner(): + R, x,y = ring("x,y", QQ, grlex) + valid_groebner = [x**2, x*y, -QQ(1,2)*x + y**2] + invalid_groebner = [x**3, x*y, -QQ(1,2)*x + y**2] + assert is_groebner(valid_groebner, R) is True + assert is_groebner(invalid_groebner, R) is False + +def test_is_reduced(): + R, x, y = ring("x,y", QQ, lex) + f = x**2 + 2*x*y**2 + g = x*y + 2*y**3 - 1 + assert is_reduced([f, g], R) == False + G = groebner([f, g], R) + assert is_reduced(G, R) == True diff --git a/lib/python3.12/site-packages/sympy/polys/tests/test_heuristicgcd.py b/lib/python3.12/site-packages/sympy/polys/tests/test_heuristicgcd.py new file mode 100644 index 0000000000000000000000000000000000000000..7ff6bd6ea4effbd49c5e942ea8925cfcca4ba162 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/polys/tests/test_heuristicgcd.py @@ -0,0 +1,152 @@ +from sympy.polys.rings import ring +from sympy.polys.domains import ZZ +from sympy.polys.heuristicgcd import heugcd + + +def test_heugcd_univariate_integers(): + R, x = ring("x", ZZ) + + f = x**4 + 8*x**3 + 21*x**2 + 22*x + 8 + g = x**3 + 6*x**2 + 11*x + 6 + + h = x**2 + 3*x + 2 + + cff = x**2 + 5*x + 4 + cfg = x + 3 + + assert heugcd(f, g) == (h, cff, cfg) + + f = x**4 - 4 + g = x**4 + 4*x**2 + 4 + + h = x**2 + 2 + + cff = x**2 - 2 + cfg = x**2 + 2 + + assert heugcd(f, g) == (h, cff, cfg) + + f = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 + g = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 + + h = 1 + + cff = f + cfg = g + + assert heugcd(f, g) == (h, cff, cfg) + + f = - 352518131239247345597970242177235495263669787845475025293906825864749649589178600387510272*x**49 \ + + 46818041807522713962450042363465092040687472354933295397472942006618953623327997952*x**42 \ + + 378182690892293941192071663536490788434899030680411695933646320291525827756032*x**35 \ + + 112806468807371824947796775491032386836656074179286744191026149539708928*x**28 \ + - 12278371209708240950316872681744825481125965781519138077173235712*x**21 \ + + 289127344604779611146960547954288113529690984687482920704*x**14 \ + + 19007977035740498977629742919480623972236450681*x**7 \ + + 311973482284542371301330321821976049 + + g = 365431878023781158602430064717380211405897160759702125019136*x**21 \ + + 197599133478719444145775798221171663643171734081650688*x**14 \ + - 9504116979659010018253915765478924103928886144*x**7 \ + - 311973482284542371301330321821976049 + + # TODO: assert heugcd(f, f.diff(x))[0] == g + + f = 1317378933230047068160*x + 2945748836994210856960 + g = 120352542776360960*x + 269116466014453760 + + h = 120352542776360960*x + 269116466014453760 + cff = 10946 + cfg = 1 + + assert heugcd(f, g) == (h, cff, cfg) + +def test_heugcd_multivariate_integers(): + R, x, y = ring("x,y", ZZ) + + f, g = 2*x**2 + 4*x + 2, x + 1 + assert heugcd(f, g) == (x + 1, 2*x + 2, 1) + + f, g = x + 1, 2*x**2 + 4*x + 2 + assert heugcd(f, g) == (x + 1, 1, 2*x + 2) + + R, x, y, z, u = ring("x,y,z,u", ZZ) + + f, g = u**2 + 2*u + 1, 2*u + 2 + assert heugcd(f, g) == (u + 1, u + 1, 2) + + f, g = z**2*u**2 + 2*z**2*u + z**2 + z*u + z, u**2 + 2*u + 1 + h, cff, cfg = u + 1, z**2*u + z**2 + z, u + 1 + + assert heugcd(f, g) == (h, cff, cfg) + assert heugcd(g, f) == (h, cfg, cff) + + R, x, y, z = ring("x,y,z", ZZ) + + f, g, h = R.fateman_poly_F_1() + H, cff, cfg = heugcd(f, g) + + assert H == h and H*cff == f and H*cfg == g + + R, x, y, z, u, v = ring("x,y,z,u,v", ZZ) + + f, g, h = R.fateman_poly_F_1() + H, cff, cfg = heugcd(f, g) + + assert H == h and H*cff == f and H*cfg == g + + R, x, y, z, u, v, a, b = ring("x,y,z,u,v,a,b", ZZ) + + f, g, h = R.fateman_poly_F_1() + H, cff, cfg = heugcd(f, g) + + assert H == h and H*cff == f and H*cfg == g + + R, x, y, z, u, v, a, b, c, d = ring("x,y,z,u,v,a,b,c,d", ZZ) + + f, g, h = R.fateman_poly_F_1() + H, cff, cfg = heugcd(f, g) + + assert H == h and H*cff == f and H*cfg == g + + R, x, y, z = ring("x,y,z", ZZ) + + f, g, h = R.fateman_poly_F_2() + H, cff, cfg = heugcd(f, g) + + assert H == h and H*cff == f and H*cfg == g + + f, g, h = R.fateman_poly_F_3() + H, cff, cfg = heugcd(f, g) + + assert H == h and H*cff == f and H*cfg == g + + R, x, y, z, t = ring("x,y,z,t", ZZ) + + f, g, h = R.fateman_poly_F_3() + H, cff, cfg = heugcd(f, g) + + assert H == h and H*cff == f and H*cfg == g + + +def test_issue_10996(): + R, x, y, z = ring("x,y,z", ZZ) + + f = 12*x**6*y**7*z**3 - 3*x**4*y**9*z**3 + 12*x**3*y**5*z**4 + g = -48*x**7*y**8*z**3 + 12*x**5*y**10*z**3 - 48*x**5*y**7*z**2 + \ + 36*x**4*y**7*z - 48*x**4*y**6*z**4 + 12*x**3*y**9*z**2 - 48*x**3*y**4 \ + - 9*x**2*y**9*z - 48*x**2*y**5*z**3 + 12*x*y**6 + 36*x*y**5*z**2 - 48*y**2*z + + H, cff, cfg = heugcd(f, g) + + assert H == 12*x**3*y**4 - 3*x*y**6 + 12*y**2*z + assert H*cff == f and H*cfg == g + + +def test_issue_25793(): + R, x = ring("x", ZZ) + f = x - 4851 # failure starts for values more than 4850 + g = f*(2*x + 1) + H, cff, cfg = R.dup_zz_heu_gcd(f, g) + assert H == f + # needs a test for dmp, too, that fails in master before this change diff --git a/lib/python3.12/site-packages/sympy/polys/tests/test_hypothesis.py b/lib/python3.12/site-packages/sympy/polys/tests/test_hypothesis.py new file mode 100644 index 0000000000000000000000000000000000000000..78c2369179c3f0ea4d34b8a7868417506177e3c5 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/polys/tests/test_hypothesis.py @@ -0,0 +1,36 @@ +from hypothesis import given +from hypothesis import strategies as st +from sympy.abc import x +from sympy.polys.polytools import Poly + + +def polys(*, nonzero=False, domain="ZZ"): + # This is a simple strategy, but sufficient the tests below + elems = {"ZZ": st.integers(), "QQ": st.fractions()} + coeff_st = st.lists(elems[domain]) + if nonzero: + coeff_st = coeff_st.filter(any) + return st.builds(Poly, coeff_st, st.just(x), domain=st.just(domain)) + + +@given(f=polys(), g=polys(), r=polys()) +def test_gcd_hypothesis(f, g, r): + gcd_1 = f.gcd(g) + gcd_2 = g.gcd(f) + assert gcd_1 == gcd_2 + + # multiply by r + gcd_3 = g.gcd(f + r * g) + assert gcd_1 == gcd_3 + + +@given(f_z=polys(), g_z=polys(nonzero=True)) +def test_poly_hypothesis_integers(f_z, g_z): + remainder_z = f_z.rem(g_z) + assert g_z.degree() >= remainder_z.degree() or remainder_z.degree() == 0 + + +@given(f_q=polys(domain="QQ"), g_q=polys(nonzero=True, domain="QQ")) +def test_poly_hypothesis_rationals(f_q, g_q): + remainder_q = f_q.rem(g_q) + assert g_q.degree() >= remainder_q.degree() or remainder_q.degree() == 0 diff --git a/lib/python3.12/site-packages/sympy/polys/tests/test_injections.py b/lib/python3.12/site-packages/sympy/polys/tests/test_injections.py new file mode 100644 index 0000000000000000000000000000000000000000..63a5537c94f00e52a3899c97f0d78bfadab78a67 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/polys/tests/test_injections.py @@ -0,0 +1,39 @@ +"""Tests for functions that inject symbols into the global namespace. """ + +from sympy.polys.rings import vring +from sympy.polys.fields import vfield +from sympy.polys.domains import QQ + +def test_vring(): + ns = {'vring':vring, 'QQ':QQ} + exec('R = vring("r", QQ)', ns) + exec('assert r == R.gens[0]', ns) + + exec('R = vring("rb rbb rcc rzz _rx", QQ)', ns) + exec('assert rb == R.gens[0]', ns) + exec('assert rbb == R.gens[1]', ns) + exec('assert rcc == R.gens[2]', ns) + exec('assert rzz == R.gens[3]', ns) + exec('assert _rx == R.gens[4]', ns) + + exec('R = vring(["rd", "re", "rfg"], QQ)', ns) + exec('assert rd == R.gens[0]', ns) + exec('assert re == R.gens[1]', ns) + exec('assert rfg == R.gens[2]', ns) + +def test_vfield(): + ns = {'vfield':vfield, 'QQ':QQ} + exec('F = vfield("f", QQ)', ns) + exec('assert f == F.gens[0]', ns) + + exec('F = vfield("fb fbb fcc fzz _fx", QQ)', ns) + exec('assert fb == F.gens[0]', ns) + exec('assert fbb == F.gens[1]', ns) + exec('assert fcc == F.gens[2]', ns) + exec('assert fzz == F.gens[3]', ns) + exec('assert _fx == F.gens[4]', ns) + + exec('F = vfield(["fd", "fe", "ffg"], QQ)', ns) + exec('assert fd == F.gens[0]', ns) + exec('assert fe == F.gens[1]', ns) + exec('assert ffg == F.gens[2]', ns) diff --git a/lib/python3.12/site-packages/sympy/polys/tests/test_modulargcd.py b/lib/python3.12/site-packages/sympy/polys/tests/test_modulargcd.py new file mode 100644 index 0000000000000000000000000000000000000000..235fb8df0a5c582a82626326aedc0d6727b1c21a --- /dev/null +++ b/lib/python3.12/site-packages/sympy/polys/tests/test_modulargcd.py @@ -0,0 +1,325 @@ +from sympy.polys.rings import ring +from sympy.polys.domains import ZZ, QQ, AlgebraicField +from sympy.polys.modulargcd import ( + modgcd_univariate, + modgcd_bivariate, + _chinese_remainder_reconstruction_multivariate, + modgcd_multivariate, + _to_ZZ_poly, + _to_ANP_poly, + func_field_modgcd, + _func_field_modgcd_m) +from sympy.functions.elementary.miscellaneous import sqrt + + +def test_modgcd_univariate_integers(): + R, x = ring("x", ZZ) + + f, g = R.zero, R.zero + assert modgcd_univariate(f, g) == (0, 0, 0) + + f, g = R.zero, x + assert modgcd_univariate(f, g) == (x, 0, 1) + assert modgcd_univariate(g, f) == (x, 1, 0) + + f, g = R.zero, -x + assert modgcd_univariate(f, g) == (x, 0, -1) + assert modgcd_univariate(g, f) == (x, -1, 0) + + f, g = 2*x, R(2) + assert modgcd_univariate(f, g) == (2, x, 1) + + f, g = 2*x + 2, 6*x**2 - 6 + assert modgcd_univariate(f, g) == (2*x + 2, 1, 3*x - 3) + + f = x**4 + 8*x**3 + 21*x**2 + 22*x + 8 + g = x**3 + 6*x**2 + 11*x + 6 + + h = x**2 + 3*x + 2 + + cff = x**2 + 5*x + 4 + cfg = x + 3 + + assert modgcd_univariate(f, g) == (h, cff, cfg) + + f = x**4 - 4 + g = x**4 + 4*x**2 + 4 + + h = x**2 + 2 + + cff = x**2 - 2 + cfg = x**2 + 2 + + assert modgcd_univariate(f, g) == (h, cff, cfg) + + f = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 + g = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 + + h = 1 + + cff = f + cfg = g + + assert modgcd_univariate(f, g) == (h, cff, cfg) + + f = - 352518131239247345597970242177235495263669787845475025293906825864749649589178600387510272*x**49 \ + + 46818041807522713962450042363465092040687472354933295397472942006618953623327997952*x**42 \ + + 378182690892293941192071663536490788434899030680411695933646320291525827756032*x**35 \ + + 112806468807371824947796775491032386836656074179286744191026149539708928*x**28 \ + - 12278371209708240950316872681744825481125965781519138077173235712*x**21 \ + + 289127344604779611146960547954288113529690984687482920704*x**14 \ + + 19007977035740498977629742919480623972236450681*x**7 \ + + 311973482284542371301330321821976049 + + g = 365431878023781158602430064717380211405897160759702125019136*x**21 \ + + 197599133478719444145775798221171663643171734081650688*x**14 \ + - 9504116979659010018253915765478924103928886144*x**7 \ + - 311973482284542371301330321821976049 + + assert modgcd_univariate(f, f.diff(x))[0] == g + + f = 1317378933230047068160*x + 2945748836994210856960 + g = 120352542776360960*x + 269116466014453760 + + h = 120352542776360960*x + 269116466014453760 + cff = 10946 + cfg = 1 + + assert modgcd_univariate(f, g) == (h, cff, cfg) + + +def test_modgcd_bivariate_integers(): + R, x, y = ring("x,y", ZZ) + + f, g = R.zero, R.zero + assert modgcd_bivariate(f, g) == (0, 0, 0) + + f, g = 2*x, R(2) + assert modgcd_bivariate(f, g) == (2, x, 1) + + f, g = x + 2*y, x + y + assert modgcd_bivariate(f, g) == (1, f, g) + + f, g = x**2 + 2*x*y + y**2, x**3 + y**3 + assert modgcd_bivariate(f, g) == (x + y, x + y, x**2 - x*y + y**2) + + f, g = x*y**2 + 2*x*y + x, x*y**3 + x + assert modgcd_bivariate(f, g) == (x*y + x, y + 1, y**2 - y + 1) + + f, g = x**2*y**2 + x**2*y + 1, x*y**2 + x*y + 1 + assert modgcd_bivariate(f, g) == (1, f, g) + + f = 2*x*y**2 + 4*x*y + 2*x + y**2 + 2*y + 1 + g = 2*x*y**3 + 2*x + y**3 + 1 + assert modgcd_bivariate(f, g) == (2*x*y + 2*x + y + 1, y + 1, y**2 - y + 1) + + f, g = 2*x**2 + 4*x + 2, x + 1 + assert modgcd_bivariate(f, g) == (x + 1, 2*x + 2, 1) + + f, g = x + 1, 2*x**2 + 4*x + 2 + assert modgcd_bivariate(f, g) == (x + 1, 1, 2*x + 2) + + f = 2*x**2 + 4*x*y - 2*x - 4*y + g = x**2 + x - 2 + assert modgcd_bivariate(f, g) == (x - 1, 2*x + 4*y, x + 2) + + f = 2*x**2 + 2*x*y - 3*x - 3*y + g = 4*x*y - 2*x + 4*y**2 - 2*y + assert modgcd_bivariate(f, g) == (x + y, 2*x - 3, 4*y - 2) + + +def test_chinese_remainder(): + R, x, y = ring("x, y", ZZ) + p, q = 3, 5 + + hp = x**3*y - x**2 - 1 + hq = -x**3*y - 2*x*y**2 + 2 + + hpq = _chinese_remainder_reconstruction_multivariate(hp, hq, p, q) + + assert hpq.trunc_ground(p) == hp + assert hpq.trunc_ground(q) == hq + + T, z = ring("z", R) + p, q = 3, 7 + + hp = (x*y + 1)*z**2 + x + hq = (x**2 - 3*y)*z + 2 + + hpq = _chinese_remainder_reconstruction_multivariate(hp, hq, p, q) + + assert hpq.trunc_ground(p) == hp + assert hpq.trunc_ground(q) == hq + + +def test_modgcd_multivariate_integers(): + R, x, y = ring("x,y", ZZ) + + f, g = R.zero, R.zero + assert modgcd_multivariate(f, g) == (0, 0, 0) + + f, g = 2*x**2 + 4*x + 2, x + 1 + assert modgcd_multivariate(f, g) == (x + 1, 2*x + 2, 1) + + f, g = x + 1, 2*x**2 + 4*x + 2 + assert modgcd_multivariate(f, g) == (x + 1, 1, 2*x + 2) + + f = 2*x**2 + 2*x*y - 3*x - 3*y + g = 4*x*y - 2*x + 4*y**2 - 2*y + assert modgcd_multivariate(f, g) == (x + y, 2*x - 3, 4*y - 2) + + f, g = x*y**2 + 2*x*y + x, x*y**3 + x + assert modgcd_multivariate(f, g) == (x*y + x, y + 1, y**2 - y + 1) + + f, g = x**2*y**2 + x**2*y + 1, x*y**2 + x*y + 1 + assert modgcd_multivariate(f, g) == (1, f, g) + + f = x**4 + 8*x**3 + 21*x**2 + 22*x + 8 + g = x**3 + 6*x**2 + 11*x + 6 + + h = x**2 + 3*x + 2 + + cff = x**2 + 5*x + 4 + cfg = x + 3 + + assert modgcd_multivariate(f, g) == (h, cff, cfg) + + R, x, y, z, u = ring("x,y,z,u", ZZ) + + f, g = x + y + z, -x - y - z - u + assert modgcd_multivariate(f, g) == (1, f, g) + + f, g = u**2 + 2*u + 1, 2*u + 2 + assert modgcd_multivariate(f, g) == (u + 1, u + 1, 2) + + f, g = z**2*u**2 + 2*z**2*u + z**2 + z*u + z, u**2 + 2*u + 1 + h, cff, cfg = u + 1, z**2*u + z**2 + z, u + 1 + + assert modgcd_multivariate(f, g) == (h, cff, cfg) + assert modgcd_multivariate(g, f) == (h, cfg, cff) + + R, x, y, z = ring("x,y,z", ZZ) + + f, g = x - y*z, x - y*z + assert modgcd_multivariate(f, g) == (x - y*z, 1, 1) + + f, g, h = R.fateman_poly_F_1() + H, cff, cfg = modgcd_multivariate(f, g) + + assert H == h and H*cff == f and H*cfg == g + + R, x, y, z, u, v = ring("x,y,z,u,v", ZZ) + + f, g, h = R.fateman_poly_F_1() + H, cff, cfg = modgcd_multivariate(f, g) + + assert H == h and H*cff == f and H*cfg == g + + R, x, y, z, u, v, a, b = ring("x,y,z,u,v,a,b", ZZ) + + f, g, h = R.fateman_poly_F_1() + H, cff, cfg = modgcd_multivariate(f, g) + + assert H == h and H*cff == f and H*cfg == g + + R, x, y, z, u, v, a, b, c, d = ring("x,y,z,u,v,a,b,c,d", ZZ) + + f, g, h = R.fateman_poly_F_1() + H, cff, cfg = modgcd_multivariate(f, g) + + assert H == h and H*cff == f and H*cfg == g + + R, x, y, z = ring("x,y,z", ZZ) + + f, g, h = R.fateman_poly_F_2() + H, cff, cfg = modgcd_multivariate(f, g) + + assert H == h and H*cff == f and H*cfg == g + + f, g, h = R.fateman_poly_F_3() + H, cff, cfg = modgcd_multivariate(f, g) + + assert H == h and H*cff == f and H*cfg == g + + R, x, y, z, t = ring("x,y,z,t", ZZ) + + f, g, h = R.fateman_poly_F_3() + H, cff, cfg = modgcd_multivariate(f, g) + + assert H == h and H*cff == f and H*cfg == g + + +def test_to_ZZ_ANP_poly(): + A = AlgebraicField(QQ, sqrt(2)) + R, x = ring("x", A) + f = x*(sqrt(2) + 1) + + T, x_, z_ = ring("x_, z_", ZZ) + f_ = x_*z_ + x_ + + assert _to_ZZ_poly(f, T) == f_ + assert _to_ANP_poly(f_, R) == f + + R, x, t, s = ring("x, t, s", A) + f = x*t**2 + x*s + sqrt(2) + + D, t_, s_ = ring("t_, s_", ZZ) + T, x_, z_ = ring("x_, z_", D) + f_ = (t_**2 + s_)*x_ + z_ + + assert _to_ZZ_poly(f, T) == f_ + assert _to_ANP_poly(f_, R) == f + + +def test_modgcd_algebraic_field(): + A = AlgebraicField(QQ, sqrt(2)) + R, x = ring("x", A) + one = A.one + + f, g = 2*x, R(2) + assert func_field_modgcd(f, g) == (one, f, g) + + f, g = 2*x, R(sqrt(2)) + assert func_field_modgcd(f, g) == (one, f, g) + + f, g = 2*x + 2, 6*x**2 - 6 + assert func_field_modgcd(f, g) == (x + 1, R(2), 6*x - 6) + + R, x, y = ring("x, y", A) + + f, g = x + sqrt(2)*y, x + y + assert func_field_modgcd(f, g) == (one, f, g) + + f, g = x*y + sqrt(2)*y**2, R(sqrt(2))*y + assert func_field_modgcd(f, g) == (y, x + sqrt(2)*y, R(sqrt(2))) + + f, g = x**2 + 2*sqrt(2)*x*y + 2*y**2, x + sqrt(2)*y + assert func_field_modgcd(f, g) == (g, g, one) + + A = AlgebraicField(QQ, sqrt(2), sqrt(3)) + R, x, y, z = ring("x, y, z", A) + + h = x**2*y**7 + sqrt(6)/21*z + f, g = h*(27*y**3 + 1), h*(y + x) + assert func_field_modgcd(f, g) == (h, 27*y**3+1, y+x) + + h = x**13*y**3 + 1/2*x**10 + 1/sqrt(2) + f, g = h*(x + 1), h*sqrt(2)/sqrt(3) + assert func_field_modgcd(f, g) == (h, x + 1, R(sqrt(2)/sqrt(3))) + + A = AlgebraicField(QQ, sqrt(2)**(-1)*sqrt(3)) + R, x = ring("x", A) + + f, g = x + 1, x - 1 + assert func_field_modgcd(f, g) == (A.one, f, g) + + +# when func_field_modgcd suppors function fields, this test can be changed +def test_modgcd_func_field(): + D, t = ring("t", ZZ) + R, x, z = ring("x, z", D) + + minpoly = (z**2*t**2 + z**2*t - 1).drop(0) + f, g = x + 1, x - 1 + + assert _func_field_modgcd_m(f, g, minpoly) == R.one diff --git a/lib/python3.12/site-packages/sympy/polys/tests/test_monomials.py b/lib/python3.12/site-packages/sympy/polys/tests/test_monomials.py new file mode 100644 index 0000000000000000000000000000000000000000..c5ed28ba0e8e3f8e9f85c543a4fffcaef855fff8 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/polys/tests/test_monomials.py @@ -0,0 +1,269 @@ +"""Tests for tools and arithmetics for monomials of distributed polynomials. """ + +from sympy.polys.monomials import ( + itermonomials, monomial_count, + monomial_mul, monomial_div, + monomial_gcd, monomial_lcm, + monomial_max, monomial_min, + monomial_divides, monomial_pow, + Monomial, +) + +from sympy.polys.polyerrors import ExactQuotientFailed + +from sympy.abc import a, b, c, x, y, z +from sympy.core import S, symbols +from sympy.testing.pytest import raises + +def test_monomials(): + + # total_degree tests + assert set(itermonomials([], 0)) == {S.One} + assert set(itermonomials([], 1)) == {S.One} + assert set(itermonomials([], 2)) == {S.One} + + assert set(itermonomials([], 0, 0)) == {S.One} + assert set(itermonomials([], 1, 0)) == {S.One} + assert set(itermonomials([], 2, 0)) == {S.One} + + raises(StopIteration, lambda: next(itermonomials([], 0, 1))) + raises(StopIteration, lambda: next(itermonomials([], 0, 2))) + raises(StopIteration, lambda: next(itermonomials([], 0, 3))) + + assert set(itermonomials([], 0, 1)) == set() + assert set(itermonomials([], 0, 2)) == set() + assert set(itermonomials([], 0, 3)) == set() + + raises(ValueError, lambda: set(itermonomials([], -1))) + raises(ValueError, lambda: set(itermonomials([x], -1))) + raises(ValueError, lambda: set(itermonomials([x, y], -1))) + + assert set(itermonomials([x], 0)) == {S.One} + assert set(itermonomials([x], 1)) == {S.One, x} + assert set(itermonomials([x], 2)) == {S.One, x, x**2} + assert set(itermonomials([x], 3)) == {S.One, x, x**2, x**3} + + assert set(itermonomials([x, y], 0)) == {S.One} + assert set(itermonomials([x, y], 1)) == {S.One, x, y} + assert set(itermonomials([x, y], 2)) == {S.One, x, y, x**2, y**2, x*y} + assert set(itermonomials([x, y], 3)) == \ + {S.One, x, y, x**2, x**3, y**2, y**3, x*y, x*y**2, y*x**2} + + i, j, k = symbols('i j k', commutative=False) + assert set(itermonomials([i, j, k], 0)) == {S.One} + assert set(itermonomials([i, j, k], 1)) == {S.One, i, j, k} + assert set(itermonomials([i, j, k], 2)) == \ + {S.One, i, j, k, i**2, j**2, k**2, i*j, i*k, j*i, j*k, k*i, k*j} + + assert set(itermonomials([i, j, k], 3)) == \ + {S.One, i, j, k, i**2, j**2, k**2, i*j, i*k, j*i, j*k, k*i, k*j, + i**3, j**3, k**3, + i**2 * j, i**2 * k, j * i**2, k * i**2, + j**2 * i, j**2 * k, i * j**2, k * j**2, + k**2 * i, k**2 * j, i * k**2, j * k**2, + i*j*i, i*k*i, j*i*j, j*k*j, k*i*k, k*j*k, + i*j*k, i*k*j, j*i*k, j*k*i, k*i*j, k*j*i, + } + + assert set(itermonomials([x, i, j], 0)) == {S.One} + assert set(itermonomials([x, i, j], 1)) == {S.One, x, i, j} + assert set(itermonomials([x, i, j], 2)) == {S.One, x, i, j, x*i, x*j, i*j, j*i, x**2, i**2, j**2} + assert set(itermonomials([x, i, j], 3)) == \ + {S.One, x, i, j, x*i, x*j, i*j, j*i, x**2, i**2, j**2, + x**3, i**3, j**3, + x**2 * i, x**2 * j, + x * i**2, j * i**2, i**2 * j, i*j*i, + x * j**2, i * j**2, j**2 * i, j*i*j, + x * i * j, x * j * i + } + + # degree_list tests + assert set(itermonomials([], [])) == {S.One} + + raises(ValueError, lambda: set(itermonomials([], [0]))) + raises(ValueError, lambda: set(itermonomials([], [1]))) + raises(ValueError, lambda: set(itermonomials([], [2]))) + + raises(ValueError, lambda: set(itermonomials([x], [1], []))) + raises(ValueError, lambda: set(itermonomials([x], [1, 2], []))) + raises(ValueError, lambda: set(itermonomials([x], [1, 2, 3], []))) + + raises(ValueError, lambda: set(itermonomials([x], [], [1]))) + raises(ValueError, lambda: set(itermonomials([x], [], [1, 2]))) + raises(ValueError, lambda: set(itermonomials([x], [], [1, 2, 3]))) + + raises(ValueError, lambda: set(itermonomials([x, y], [1, 2], [1, 2, 3]))) + raises(ValueError, lambda: set(itermonomials([x, y, z], [1, 2, 3], [0, 1]))) + + raises(ValueError, lambda: set(itermonomials([x], [1], [-1]))) + raises(ValueError, lambda: set(itermonomials([x, y], [1, 2], [1, -1]))) + + raises(ValueError, lambda: set(itermonomials([], [], 1))) + raises(ValueError, lambda: set(itermonomials([], [], 2))) + raises(ValueError, lambda: set(itermonomials([], [], 3))) + + raises(ValueError, lambda: set(itermonomials([x, y], [0, 1], [1, 2]))) + raises(ValueError, lambda: set(itermonomials([x, y, z], [0, 0, 3], [0, 1, 2]))) + + assert set(itermonomials([x], [0])) == {S.One} + assert set(itermonomials([x], [1])) == {S.One, x} + assert set(itermonomials([x], [2])) == {S.One, x, x**2} + assert set(itermonomials([x], [3])) == {S.One, x, x**2, x**3} + + assert set(itermonomials([x], [3], [1])) == {x, x**3, x**2} + assert set(itermonomials([x], [3], [2])) == {x**3, x**2} + + assert set(itermonomials([x, y], 3, 3)) == {x**3, x**2*y, x*y**2, y**3} + assert set(itermonomials([x, y], 3, 2)) == {x**2, x*y, y**2, x**3, x**2*y, x*y**2, y**3} + + assert set(itermonomials([x, y], [0, 0])) == {S.One} + assert set(itermonomials([x, y], [0, 1])) == {S.One, y} + assert set(itermonomials([x, y], [0, 2])) == {S.One, y, y**2} + assert set(itermonomials([x, y], [0, 2], [0, 1])) == {y, y**2} + assert set(itermonomials([x, y], [0, 2], [0, 2])) == {y**2} + + assert set(itermonomials([x, y], [1, 0])) == {S.One, x} + assert set(itermonomials([x, y], [1, 1])) == {S.One, x, y, x*y} + assert set(itermonomials([x, y], [1, 2])) == {S.One, x, y, x*y, y**2, x*y**2} + assert set(itermonomials([x, y], [1, 2], [1, 1])) == {x*y, x*y**2} + assert set(itermonomials([x, y], [1, 2], [1, 2])) == {x*y**2} + + assert set(itermonomials([x, y], [2, 0])) == {S.One, x, x**2} + assert set(itermonomials([x, y], [2, 1])) == {S.One, x, y, x*y, x**2, x**2*y} + assert set(itermonomials([x, y], [2, 2])) == \ + {S.One, y**2, x*y**2, x, x*y, x**2, x**2*y**2, y, x**2*y} + + i, j, k = symbols('i j k', commutative=False) + assert set(itermonomials([i, j, k], 2, 2)) == \ + {k*i, i**2, i*j, j*k, j*i, k**2, j**2, k*j, i*k} + assert set(itermonomials([i, j, k], 3, 2)) == \ + {j*k**2, i*k**2, k*i*j, k*i**2, k**2, j*k*j, k*j**2, i*k*i, i*j, + j**2*k, i**2*j, j*i*k, j**3, i**3, k*j*i, j*k*i, j*i, + k**2*j, j*i**2, k*j, k*j*k, i*j*i, j*i*j, i*j**2, j**2, + k*i*k, i**2, j*k, i*k, i*k*j, k**3, i**2*k, j**2*i, k**2*i, + i*j*k, k*i + } + assert set(itermonomials([i, j, k], [0, 0, 0])) == {S.One} + assert set(itermonomials([i, j, k], [0, 0, 1])) == {1, k} + assert set(itermonomials([i, j, k], [0, 1, 0])) == {1, j} + assert set(itermonomials([i, j, k], [1, 0, 0])) == {i, 1} + assert set(itermonomials([i, j, k], [0, 0, 2])) == {k**2, 1, k} + assert set(itermonomials([i, j, k], [0, 2, 0])) == {1, j, j**2} + assert set(itermonomials([i, j, k], [2, 0, 0])) == {i, 1, i**2} + assert set(itermonomials([i, j, k], [1, 1, 1])) == {1, k, j, j*k, i*k, i, i*j, i*j*k} + assert set(itermonomials([i, j, k], [2, 2, 2])) == \ + {1, k, i**2*k**2, j*k, j**2, i, i*k, j*k**2, i*j**2*k**2, + i**2*j, i**2*j**2, k**2, j**2*k, i*j**2*k, + j**2*k**2, i*j, i**2*k, i**2*j**2*k, j, i**2*j*k, + i*j**2, i*k**2, i*j*k, i**2*j**2*k**2, i*j*k**2, i**2, i**2*j*k**2 + } + + assert set(itermonomials([x, j, k], [0, 0, 0])) == {S.One} + assert set(itermonomials([x, j, k], [0, 0, 1])) == {1, k} + assert set(itermonomials([x, j, k], [0, 1, 0])) == {1, j} + assert set(itermonomials([x, j, k], [1, 0, 0])) == {x, 1} + assert set(itermonomials([x, j, k], [0, 0, 2])) == {k**2, 1, k} + assert set(itermonomials([x, j, k], [0, 2, 0])) == {1, j, j**2} + assert set(itermonomials([x, j, k], [2, 0, 0])) == {x, 1, x**2} + assert set(itermonomials([x, j, k], [1, 1, 1])) == {1, k, j, j*k, x*k, x, x*j, x*j*k} + assert set(itermonomials([x, j, k], [2, 2, 2])) == \ + {1, k, x**2*k**2, j*k, j**2, x, x*k, j*k**2, x*j**2*k**2, + x**2*j, x**2*j**2, k**2, j**2*k, x*j**2*k, + j**2*k**2, x*j, x**2*k, x**2*j**2*k, j, x**2*j*k, + x*j**2, x*k**2, x*j*k, x**2*j**2*k**2, x*j*k**2, x**2, x**2*j*k**2 + } + +def test_monomial_count(): + assert monomial_count(2, 2) == 6 + assert monomial_count(2, 3) == 10 + +def test_monomial_mul(): + assert monomial_mul((3, 4, 1), (1, 2, 0)) == (4, 6, 1) + +def test_monomial_div(): + assert monomial_div((3, 4, 1), (1, 2, 0)) == (2, 2, 1) + +def test_monomial_gcd(): + assert monomial_gcd((3, 4, 1), (1, 2, 0)) == (1, 2, 0) + +def test_monomial_lcm(): + assert monomial_lcm((3, 4, 1), (1, 2, 0)) == (3, 4, 1) + +def test_monomial_max(): + assert monomial_max((3, 4, 5), (0, 5, 1), (6, 3, 9)) == (6, 5, 9) + +def test_monomial_pow(): + assert monomial_pow((1, 2, 3), 3) == (3, 6, 9) + +def test_monomial_min(): + assert monomial_min((3, 4, 5), (0, 5, 1), (6, 3, 9)) == (0, 3, 1) + +def test_monomial_divides(): + assert monomial_divides((1, 2, 3), (4, 5, 6)) is True + assert monomial_divides((1, 2, 3), (0, 5, 6)) is False + +def test_Monomial(): + m = Monomial((3, 4, 1), (x, y, z)) + n = Monomial((1, 2, 0), (x, y, z)) + + assert m.as_expr() == x**3*y**4*z + assert n.as_expr() == x**1*y**2 + + assert m.as_expr(a, b, c) == a**3*b**4*c + assert n.as_expr(a, b, c) == a**1*b**2 + + assert m.exponents == (3, 4, 1) + assert m.gens == (x, y, z) + + assert n.exponents == (1, 2, 0) + assert n.gens == (x, y, z) + + assert m == (3, 4, 1) + assert n != (3, 4, 1) + assert m != (1, 2, 0) + assert n == (1, 2, 0) + assert (m == 1) is False + + assert m[0] == m[-3] == 3 + assert m[1] == m[-2] == 4 + assert m[2] == m[-1] == 1 + + assert n[0] == n[-3] == 1 + assert n[1] == n[-2] == 2 + assert n[2] == n[-1] == 0 + + assert m[:2] == (3, 4) + assert n[:2] == (1, 2) + + assert m*n == Monomial((4, 6, 1)) + assert m/n == Monomial((2, 2, 1)) + + assert m*(1, 2, 0) == Monomial((4, 6, 1)) + assert m/(1, 2, 0) == Monomial((2, 2, 1)) + + assert m.gcd(n) == Monomial((1, 2, 0)) + assert m.lcm(n) == Monomial((3, 4, 1)) + + assert m.gcd((1, 2, 0)) == Monomial((1, 2, 0)) + assert m.lcm((1, 2, 0)) == Monomial((3, 4, 1)) + + assert m**0 == Monomial((0, 0, 0)) + assert m**1 == m + assert m**2 == Monomial((6, 8, 2)) + assert m**3 == Monomial((9, 12, 3)) + _a = Monomial((0, 0, 0)) + for n in range(10): + assert _a == m**n + _a *= m + + raises(ExactQuotientFailed, lambda: m/Monomial((5, 2, 0))) + + mm = Monomial((1, 2, 3)) + raises(ValueError, lambda: mm.as_expr()) + assert str(mm) == 'Monomial((1, 2, 3))' + assert str(m) == 'x**3*y**4*z**1' + raises(NotImplementedError, lambda: m*1) + raises(NotImplementedError, lambda: m/1) + raises(ValueError, lambda: m**-1) + raises(TypeError, lambda: m.gcd(3)) + raises(TypeError, lambda: m.lcm(3)) diff --git a/lib/python3.12/site-packages/sympy/polys/tests/test_multivariate_resultants.py b/lib/python3.12/site-packages/sympy/polys/tests/test_multivariate_resultants.py new file mode 100644 index 0000000000000000000000000000000000000000..0799feb41fc875cf038723916a3efd62ff31b1b4 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/polys/tests/test_multivariate_resultants.py @@ -0,0 +1,294 @@ +"""Tests for Dixon's and Macaulay's classes. """ + +from sympy.matrices.dense import Matrix +from sympy.polys.polytools import factor +from sympy.core import symbols +from sympy.tensor.indexed import IndexedBase + +from sympy.polys.multivariate_resultants import (DixonResultant, + MacaulayResultant) + +c, d = symbols("a, b") +x, y = symbols("x, y") + +p = c * x + y +q = x + d * y + +dixon = DixonResultant(polynomials=[p, q], variables=[x, y]) +macaulay = MacaulayResultant(polynomials=[p, q], variables=[x, y]) + +def test_dixon_resultant_init(): + """Test init method of DixonResultant.""" + a = IndexedBase("alpha") + + assert dixon.polynomials == [p, q] + assert dixon.variables == [x, y] + assert dixon.n == 2 + assert dixon.m == 2 + assert dixon.dummy_variables == [a[0], a[1]] + +def test_get_dixon_polynomial_numerical(): + """Test Dixon's polynomial for a numerical example.""" + a = IndexedBase("alpha") + + p = x + y + q = x ** 2 + y **3 + h = x ** 2 + y + + dixon = DixonResultant([p, q, h], [x, y]) + polynomial = -x * y ** 2 * a[0] - x * y ** 2 * a[1] - x * y * a[0] \ + * a[1] - x * y * a[1] ** 2 - x * a[0] * a[1] ** 2 + x * a[0] - \ + y ** 2 * a[0] * a[1] + y ** 2 * a[1] - y * a[0] * a[1] ** 2 + y * \ + a[1] ** 2 + + assert dixon.get_dixon_polynomial().as_expr().expand() == polynomial + +def test_get_max_degrees(): + """Tests max degrees function.""" + + p = x + y + q = x ** 2 + y **3 + h = x ** 2 + y + + dixon = DixonResultant(polynomials=[p, q, h], variables=[x, y]) + dixon_polynomial = dixon.get_dixon_polynomial() + + assert dixon.get_max_degrees(dixon_polynomial) == [1, 2] + +def test_get_dixon_matrix(): + """Test Dixon's resultant for a numerical example.""" + + x, y = symbols('x, y') + + p = x + y + q = x ** 2 + y ** 3 + h = x ** 2 + y + + dixon = DixonResultant([p, q, h], [x, y]) + polynomial = dixon.get_dixon_polynomial() + + assert dixon.get_dixon_matrix(polynomial).det() == 0 + +def test_get_dixon_matrix_example_two(): + """Test Dixon's matrix for example from [Palancz08]_.""" + x, y, z = symbols('x, y, z') + + f = x ** 2 + y ** 2 - 1 + z * 0 + g = x ** 2 + z ** 2 - 1 + y * 0 + h = y ** 2 + z ** 2 - 1 + + example_two = DixonResultant([f, g, h], [y, z]) + poly = example_two.get_dixon_polynomial() + matrix = example_two.get_dixon_matrix(poly) + + expr = 1 - 8 * x ** 2 + 24 * x ** 4 - 32 * x ** 6 + 16 * x ** 8 + assert (matrix.det() - expr).expand() == 0 + +def test_KSY_precondition(): + """Tests precondition for KSY Resultant.""" + A, B, C = symbols('A, B, C') + + m1 = Matrix([[1, 2, 3], + [4, 5, 12], + [6, 7, 18]]) + + m2 = Matrix([[0, C**2], + [-2 * C, -C ** 2]]) + + m3 = Matrix([[1, 0], + [0, 1]]) + + m4 = Matrix([[A**2, 0, 1], + [A, 1, 1 / A]]) + + m5 = Matrix([[5, 1], + [2, B], + [0, 1], + [0, 0]]) + + assert dixon.KSY_precondition(m1) == False + assert dixon.KSY_precondition(m2) == True + assert dixon.KSY_precondition(m3) == True + assert dixon.KSY_precondition(m4) == False + assert dixon.KSY_precondition(m5) == True + +def test_delete_zero_rows_and_columns(): + """Tests method for deleting rows and columns containing only zeros.""" + A, B, C = symbols('A, B, C') + + m1 = Matrix([[0, 0], + [0, 0], + [1, 2]]) + + m2 = Matrix([[0, 1, 2], + [0, 3, 4], + [0, 5, 6]]) + + m3 = Matrix([[0, 0, 0, 0], + [0, 1, 2, 0], + [0, 3, 4, 0], + [0, 0, 0, 0]]) + + m4 = Matrix([[1, 0, 2], + [0, 0, 0], + [3, 0, 4]]) + + m5 = Matrix([[0, 0, 0, 1], + [0, 0, 0, 2], + [0, 0, 0, 3], + [0, 0, 0, 4]]) + + m6 = Matrix([[0, 0, A], + [B, 0, 0], + [0, 0, C]]) + + assert dixon.delete_zero_rows_and_columns(m1) == Matrix([[1, 2]]) + + assert dixon.delete_zero_rows_and_columns(m2) == Matrix([[1, 2], + [3, 4], + [5, 6]]) + + assert dixon.delete_zero_rows_and_columns(m3) == Matrix([[1, 2], + [3, 4]]) + + assert dixon.delete_zero_rows_and_columns(m4) == Matrix([[1, 2], + [3, 4]]) + + assert dixon.delete_zero_rows_and_columns(m5) == Matrix([[1], + [2], + [3], + [4]]) + + assert dixon.delete_zero_rows_and_columns(m6) == Matrix([[0, A], + [B, 0], + [0, C]]) + +def test_product_leading_entries(): + """Tests product of leading entries method.""" + A, B = symbols('A, B') + + m1 = Matrix([[1, 2, 3], + [0, 4, 5], + [0, 0, 6]]) + + m2 = Matrix([[0, 0, 1], + [2, 0, 3]]) + + m3 = Matrix([[0, 0, 0], + [1, 2, 3], + [0, 0, 0]]) + + m4 = Matrix([[0, 0, A], + [1, 2, 3], + [B, 0, 0]]) + + assert dixon.product_leading_entries(m1) == 24 + assert dixon.product_leading_entries(m2) == 2 + assert dixon.product_leading_entries(m3) == 1 + assert dixon.product_leading_entries(m4) == A * B + +def test_get_KSY_Dixon_resultant_example_one(): + """Tests the KSY Dixon resultant for example one""" + x, y, z = symbols('x, y, z') + + p = x * y * z + q = x**2 - z**2 + h = x + y + z + dixon = DixonResultant([p, q, h], [x, y]) + dixon_poly = dixon.get_dixon_polynomial() + dixon_matrix = dixon.get_dixon_matrix(dixon_poly) + D = dixon.get_KSY_Dixon_resultant(dixon_matrix) + + assert D == -z**3 + +def test_get_KSY_Dixon_resultant_example_two(): + """Tests the KSY Dixon resultant for example two""" + x, y, A = symbols('x, y, A') + + p = x * y + x * A + x - A**2 - A + y**2 + y + q = x**2 + x * A - x + x * y + y * A - y + h = x**2 + x * y + 2 * x - x * A - y * A - 2 * A + + dixon = DixonResultant([p, q, h], [x, y]) + dixon_poly = dixon.get_dixon_polynomial() + dixon_matrix = dixon.get_dixon_matrix(dixon_poly) + D = factor(dixon.get_KSY_Dixon_resultant(dixon_matrix)) + + assert D == -8*A*(A - 1)*(A + 2)*(2*A - 1)**2 + +def test_macaulay_resultant_init(): + """Test init method of MacaulayResultant.""" + + assert macaulay.polynomials == [p, q] + assert macaulay.variables == [x, y] + assert macaulay.n == 2 + assert macaulay.degrees == [1, 1] + assert macaulay.degree_m == 1 + assert macaulay.monomials_size == 2 + +def test_get_degree_m(): + assert macaulay._get_degree_m() == 1 + +def test_get_size(): + assert macaulay.get_size() == 2 + +def test_macaulay_example_one(): + """Tests the Macaulay for example from [Bruce97]_""" + + x, y, z = symbols('x, y, z') + a_1_1, a_1_2, a_1_3 = symbols('a_1_1, a_1_2, a_1_3') + a_2_2, a_2_3, a_3_3 = symbols('a_2_2, a_2_3, a_3_3') + b_1_1, b_1_2, b_1_3 = symbols('b_1_1, b_1_2, b_1_3') + b_2_2, b_2_3, b_3_3 = symbols('b_2_2, b_2_3, b_3_3') + c_1, c_2, c_3 = symbols('c_1, c_2, c_3') + + f_1 = a_1_1 * x ** 2 + a_1_2 * x * y + a_1_3 * x * z + \ + a_2_2 * y ** 2 + a_2_3 * y * z + a_3_3 * z ** 2 + f_2 = b_1_1 * x ** 2 + b_1_2 * x * y + b_1_3 * x * z + \ + b_2_2 * y ** 2 + b_2_3 * y * z + b_3_3 * z ** 2 + f_3 = c_1 * x + c_2 * y + c_3 * z + + mac = MacaulayResultant([f_1, f_2, f_3], [x, y, z]) + + assert mac.degrees == [2, 2, 1] + assert mac.degree_m == 3 + + assert mac.monomial_set == [x ** 3, x ** 2 * y, x ** 2 * z, + x * y ** 2, + x * y * z, x * z ** 2, y ** 3, + y ** 2 *z, y * z ** 2, z ** 3] + assert mac.monomials_size == 10 + assert mac.get_row_coefficients() == [[x, y, z], [x, y, z], + [x * y, x * z, y * z, z ** 2]] + + matrix = mac.get_matrix() + assert matrix.shape == (mac.monomials_size, mac.monomials_size) + assert mac.get_submatrix(matrix) == Matrix([[a_1_1, a_2_2], + [b_1_1, b_2_2]]) + +def test_macaulay_example_two(): + """Tests the Macaulay formulation for example from [Stiller96]_.""" + + x, y, z = symbols('x, y, z') + a_0, a_1, a_2 = symbols('a_0, a_1, a_2') + b_0, b_1, b_2 = symbols('b_0, b_1, b_2') + c_0, c_1, c_2, c_3, c_4 = symbols('c_0, c_1, c_2, c_3, c_4') + + f = a_0 * y - a_1 * x + a_2 * z + g = b_1 * x ** 2 + b_0 * y ** 2 - b_2 * z ** 2 + h = c_0 * y - c_1 * x ** 3 + c_2 * x ** 2 * z - c_3 * x * z ** 2 + \ + c_4 * z ** 3 + + mac = MacaulayResultant([f, g, h], [x, y, z]) + + assert mac.degrees == [1, 2, 3] + assert mac.degree_m == 4 + assert mac.monomials_size == 15 + assert len(mac.get_row_coefficients()) == mac.n + + matrix = mac.get_matrix() + assert matrix.shape == (mac.monomials_size, mac.monomials_size) + assert mac.get_submatrix(matrix) == Matrix([[-a_1, a_0, a_2, 0], + [0, -a_1, 0, 0], + [0, 0, -a_1, 0], + [0, 0, 0, -a_1]]) diff --git a/lib/python3.12/site-packages/sympy/polys/tests/test_orderings.py b/lib/python3.12/site-packages/sympy/polys/tests/test_orderings.py new file mode 100644 index 0000000000000000000000000000000000000000..d61d4887754c9d9f49905c2e131d253a45cf2ffd --- /dev/null +++ b/lib/python3.12/site-packages/sympy/polys/tests/test_orderings.py @@ -0,0 +1,124 @@ +"""Tests of monomial orderings. """ + +from sympy.polys.orderings import ( + monomial_key, lex, grlex, grevlex, ilex, igrlex, + LexOrder, InverseOrder, ProductOrder, build_product_order, +) + +from sympy.abc import x, y, z, t +from sympy.core import S +from sympy.testing.pytest import raises + +def test_lex_order(): + assert lex((1, 2, 3)) == (1, 2, 3) + assert str(lex) == 'lex' + + assert lex((1, 2, 3)) == lex((1, 2, 3)) + + assert lex((2, 2, 3)) > lex((1, 2, 3)) + assert lex((1, 3, 3)) > lex((1, 2, 3)) + assert lex((1, 2, 4)) > lex((1, 2, 3)) + + assert lex((0, 2, 3)) < lex((1, 2, 3)) + assert lex((1, 1, 3)) < lex((1, 2, 3)) + assert lex((1, 2, 2)) < lex((1, 2, 3)) + + assert lex.is_global is True + assert lex == LexOrder() + assert lex != grlex + +def test_grlex_order(): + assert grlex((1, 2, 3)) == (6, (1, 2, 3)) + assert str(grlex) == 'grlex' + + assert grlex((1, 2, 3)) == grlex((1, 2, 3)) + + assert grlex((2, 2, 3)) > grlex((1, 2, 3)) + assert grlex((1, 3, 3)) > grlex((1, 2, 3)) + assert grlex((1, 2, 4)) > grlex((1, 2, 3)) + + assert grlex((0, 2, 3)) < grlex((1, 2, 3)) + assert grlex((1, 1, 3)) < grlex((1, 2, 3)) + assert grlex((1, 2, 2)) < grlex((1, 2, 3)) + + assert grlex((2, 2, 3)) > grlex((1, 2, 4)) + assert grlex((1, 3, 3)) > grlex((1, 2, 4)) + + assert grlex((0, 2, 3)) < grlex((1, 2, 2)) + assert grlex((1, 1, 3)) < grlex((1, 2, 2)) + + assert grlex((0, 1, 1)) > grlex((0, 0, 2)) + assert grlex((0, 3, 1)) < grlex((2, 2, 1)) + + assert grlex.is_global is True + +def test_grevlex_order(): + assert grevlex((1, 2, 3)) == (6, (-3, -2, -1)) + assert str(grevlex) == 'grevlex' + + assert grevlex((1, 2, 3)) == grevlex((1, 2, 3)) + + assert grevlex((2, 2, 3)) > grevlex((1, 2, 3)) + assert grevlex((1, 3, 3)) > grevlex((1, 2, 3)) + assert grevlex((1, 2, 4)) > grevlex((1, 2, 3)) + + assert grevlex((0, 2, 3)) < grevlex((1, 2, 3)) + assert grevlex((1, 1, 3)) < grevlex((1, 2, 3)) + assert grevlex((1, 2, 2)) < grevlex((1, 2, 3)) + + assert grevlex((2, 2, 3)) > grevlex((1, 2, 4)) + assert grevlex((1, 3, 3)) > grevlex((1, 2, 4)) + + assert grevlex((0, 2, 3)) < grevlex((1, 2, 2)) + assert grevlex((1, 1, 3)) < grevlex((1, 2, 2)) + + assert grevlex((0, 1, 1)) > grevlex((0, 0, 2)) + assert grevlex((0, 3, 1)) < grevlex((2, 2, 1)) + + assert grevlex.is_global is True + +def test_InverseOrder(): + ilex = InverseOrder(lex) + igrlex = InverseOrder(grlex) + + assert ilex((1, 2, 3)) > ilex((2, 0, 3)) + assert igrlex((1, 2, 3)) < igrlex((0, 2, 3)) + assert str(ilex) == "ilex" + assert str(igrlex) == "igrlex" + assert ilex.is_global is False + assert igrlex.is_global is False + assert ilex != igrlex + assert ilex == InverseOrder(LexOrder()) + +def test_ProductOrder(): + P = ProductOrder((grlex, lambda m: m[:2]), (grlex, lambda m: m[2:])) + assert P((1, 3, 3, 4, 5)) > P((2, 1, 5, 5, 5)) + assert str(P) == "ProductOrder(grlex, grlex)" + assert P.is_global is True + assert ProductOrder((grlex, None), (ilex, None)).is_global is None + assert ProductOrder((igrlex, None), (ilex, None)).is_global is False + +def test_monomial_key(): + assert monomial_key() == lex + + assert monomial_key('lex') == lex + assert monomial_key('grlex') == grlex + assert monomial_key('grevlex') == grevlex + + raises(ValueError, lambda: monomial_key('foo')) + raises(ValueError, lambda: monomial_key(1)) + + M = [x, x**2*z**2, x*y, x**2, S.One, y**2, x**3, y, z, x*y**2*z, x**2*y**2] + assert sorted(M, key=monomial_key('lex', [z, y, x])) == \ + [S.One, x, x**2, x**3, y, x*y, y**2, x**2*y**2, z, x*y**2*z, x**2*z**2] + assert sorted(M, key=monomial_key('grlex', [z, y, x])) == \ + [S.One, x, y, z, x**2, x*y, y**2, x**3, x**2*y**2, x*y**2*z, x**2*z**2] + assert sorted(M, key=monomial_key('grevlex', [z, y, x])) == \ + [S.One, x, y, z, x**2, x*y, y**2, x**3, x**2*y**2, x**2*z**2, x*y**2*z] + +def test_build_product_order(): + assert build_product_order((("grlex", x, y), ("grlex", z, t)), [x, y, z, t])((4, 5, 6, 7)) == \ + ((9, (4, 5)), (13, (6, 7))) + + assert build_product_order((("grlex", x, y), ("grlex", z, t)), [x, y, z, t]) == \ + build_product_order((("grlex", x, y), ("grlex", z, t)), [x, y, z, t]) diff --git a/lib/python3.12/site-packages/sympy/polys/tests/test_orthopolys.py b/lib/python3.12/site-packages/sympy/polys/tests/test_orthopolys.py new file mode 100644 index 0000000000000000000000000000000000000000..e81fbe75aa6285d229ba817026f44b23b76abd6a --- /dev/null +++ b/lib/python3.12/site-packages/sympy/polys/tests/test_orthopolys.py @@ -0,0 +1,175 @@ +"""Tests for efficient functions for generating orthogonal polynomials. """ + +from sympy.core.numbers import Rational as Q +from sympy.core.singleton import S +from sympy.core.symbol import symbols +from sympy.polys.polytools import Poly +from sympy.testing.pytest import raises + +from sympy.polys.orthopolys import ( + jacobi_poly, + gegenbauer_poly, + chebyshevt_poly, + chebyshevu_poly, + hermite_poly, + hermite_prob_poly, + legendre_poly, + laguerre_poly, + spherical_bessel_fn, +) + +from sympy.abc import x, a, b + + +def test_jacobi_poly(): + raises(ValueError, lambda: jacobi_poly(-1, a, b, x)) + + assert jacobi_poly(1, a, b, x, polys=True) == Poly( + (a/2 + b/2 + 1)*x + a/2 - b/2, x, domain='ZZ(a,b)') + + assert jacobi_poly(0, a, b, x) == 1 + assert jacobi_poly(1, a, b, x) == a/2 - b/2 + x*(a/2 + b/2 + 1) + assert jacobi_poly(2, a, b, x) == (a**2/8 - a*b/4 - a/8 + b**2/8 - b/8 + + x**2*(a**2/8 + a*b/4 + a*Q(7, 8) + b**2/8 + + b*Q(7, 8) + Q(3, 2)) + x*(a**2/4 + + a*Q(3, 4) - b**2/4 - b*Q(3, 4)) - S.Half) + + assert jacobi_poly(1, a, b, polys=True) == Poly( + (a/2 + b/2 + 1)*x + a/2 - b/2, x, domain='ZZ(a,b)') + + +def test_gegenbauer_poly(): + raises(ValueError, lambda: gegenbauer_poly(-1, a, x)) + + assert gegenbauer_poly( + 1, a, x, polys=True) == Poly(2*a*x, x, domain='ZZ(a)') + + assert gegenbauer_poly(0, a, x) == 1 + assert gegenbauer_poly(1, a, x) == 2*a*x + assert gegenbauer_poly(2, a, x) == -a + x**2*(2*a**2 + 2*a) + assert gegenbauer_poly( + 3, a, x) == x**3*(4*a**3/3 + 4*a**2 + a*Q(8, 3)) + x*(-2*a**2 - 2*a) + + assert gegenbauer_poly(1, S.Half).dummy_eq(x) + assert gegenbauer_poly(1, a, polys=True) == Poly(2*a*x, x, domain='ZZ(a)') + + +def test_chebyshevt_poly(): + raises(ValueError, lambda: chebyshevt_poly(-1, x)) + + assert chebyshevt_poly(1, x, polys=True) == Poly(x) + + assert chebyshevt_poly(0, x) == 1 + assert chebyshevt_poly(1, x) == x + assert chebyshevt_poly(2, x) == 2*x**2 - 1 + assert chebyshevt_poly(3, x) == 4*x**3 - 3*x + assert chebyshevt_poly(4, x) == 8*x**4 - 8*x**2 + 1 + assert chebyshevt_poly(5, x) == 16*x**5 - 20*x**3 + 5*x + assert chebyshevt_poly(6, x) == 32*x**6 - 48*x**4 + 18*x**2 - 1 + assert chebyshevt_poly(75, x) == (2*chebyshevt_poly(37, x)*chebyshevt_poly(38, x) - x).expand() + assert chebyshevt_poly(100, x) == (2*chebyshevt_poly(50, x)**2 - 1).expand() + + assert chebyshevt_poly(1).dummy_eq(x) + assert chebyshevt_poly(1, polys=True) == Poly(x) + + +def test_chebyshevu_poly(): + raises(ValueError, lambda: chebyshevu_poly(-1, x)) + + assert chebyshevu_poly(1, x, polys=True) == Poly(2*x) + + assert chebyshevu_poly(0, x) == 1 + assert chebyshevu_poly(1, x) == 2*x + assert chebyshevu_poly(2, x) == 4*x**2 - 1 + assert chebyshevu_poly(3, x) == 8*x**3 - 4*x + assert chebyshevu_poly(4, x) == 16*x**4 - 12*x**2 + 1 + assert chebyshevu_poly(5, x) == 32*x**5 - 32*x**3 + 6*x + assert chebyshevu_poly(6, x) == 64*x**6 - 80*x**4 + 24*x**2 - 1 + + assert chebyshevu_poly(1).dummy_eq(2*x) + assert chebyshevu_poly(1, polys=True) == Poly(2*x) + + +def test_hermite_poly(): + raises(ValueError, lambda: hermite_poly(-1, x)) + + assert hermite_poly(1, x, polys=True) == Poly(2*x) + + assert hermite_poly(0, x) == 1 + assert hermite_poly(1, x) == 2*x + assert hermite_poly(2, x) == 4*x**2 - 2 + assert hermite_poly(3, x) == 8*x**3 - 12*x + assert hermite_poly(4, x) == 16*x**4 - 48*x**2 + 12 + assert hermite_poly(5, x) == 32*x**5 - 160*x**3 + 120*x + assert hermite_poly(6, x) == 64*x**6 - 480*x**4 + 720*x**2 - 120 + + assert hermite_poly(1).dummy_eq(2*x) + assert hermite_poly(1, polys=True) == Poly(2*x) + + +def test_hermite_prob_poly(): + raises(ValueError, lambda: hermite_prob_poly(-1, x)) + + assert hermite_prob_poly(1, x, polys=True) == Poly(x) + + assert hermite_prob_poly(0, x) == 1 + assert hermite_prob_poly(1, x) == x + assert hermite_prob_poly(2, x) == x**2 - 1 + assert hermite_prob_poly(3, x) == x**3 - 3*x + assert hermite_prob_poly(4, x) == x**4 - 6*x**2 + 3 + assert hermite_prob_poly(5, x) == x**5 - 10*x**3 + 15*x + assert hermite_prob_poly(6, x) == x**6 - 15*x**4 + 45*x**2 - 15 + + assert hermite_prob_poly(1).dummy_eq(x) + assert hermite_prob_poly(1, polys=True) == Poly(x) + + +def test_legendre_poly(): + raises(ValueError, lambda: legendre_poly(-1, x)) + + assert legendre_poly(1, x, polys=True) == Poly(x, domain='QQ') + + assert legendre_poly(0, x) == 1 + assert legendre_poly(1, x) == x + assert legendre_poly(2, x) == Q(3, 2)*x**2 - Q(1, 2) + assert legendre_poly(3, x) == Q(5, 2)*x**3 - Q(3, 2)*x + assert legendre_poly(4, x) == Q(35, 8)*x**4 - Q(30, 8)*x**2 + Q(3, 8) + assert legendre_poly(5, x) == Q(63, 8)*x**5 - Q(70, 8)*x**3 + Q(15, 8)*x + assert legendre_poly(6, x) == Q( + 231, 16)*x**6 - Q(315, 16)*x**4 + Q(105, 16)*x**2 - Q(5, 16) + + assert legendre_poly(1).dummy_eq(x) + assert legendre_poly(1, polys=True) == Poly(x) + + +def test_laguerre_poly(): + raises(ValueError, lambda: laguerre_poly(-1, x)) + + assert laguerre_poly(1, x, polys=True) == Poly(-x + 1, domain='QQ') + + assert laguerre_poly(0, x) == 1 + assert laguerre_poly(1, x) == -x + 1 + assert laguerre_poly(2, x) == Q(1, 2)*x**2 - Q(4, 2)*x + 1 + assert laguerre_poly(3, x) == -Q(1, 6)*x**3 + Q(9, 6)*x**2 - Q(18, 6)*x + 1 + assert laguerre_poly(4, x) == Q( + 1, 24)*x**4 - Q(16, 24)*x**3 + Q(72, 24)*x**2 - Q(96, 24)*x + 1 + assert laguerre_poly(5, x) == -Q(1, 120)*x**5 + Q(25, 120)*x**4 - Q( + 200, 120)*x**3 + Q(600, 120)*x**2 - Q(600, 120)*x + 1 + assert laguerre_poly(6, x) == Q(1, 720)*x**6 - Q(36, 720)*x**5 + Q(450, 720)*x**4 - Q(2400, 720)*x**3 + Q(5400, 720)*x**2 - Q(4320, 720)*x + 1 + + assert laguerre_poly(0, x, a) == 1 + assert laguerre_poly(1, x, a) == -x + a + 1 + assert laguerre_poly(2, x, a) == x**2/2 + (-a - 2)*x + a**2/2 + a*Q(3, 2) + 1 + assert laguerre_poly(3, x, a) == -x**3/6 + (a/2 + Q( + 3)/2)*x**2 + (-a**2/2 - a*Q(5, 2) - 3)*x + a**3/6 + a**2 + a*Q(11, 6) + 1 + + assert laguerre_poly(1).dummy_eq(-x + 1) + assert laguerre_poly(1, polys=True) == Poly(-x + 1) + + +def test_spherical_bessel_fn(): + x, z = symbols("x z") + assert spherical_bessel_fn(1, z) == 1/z**2 + assert spherical_bessel_fn(2, z) == -1/z + 3/z**3 + assert spherical_bessel_fn(3, z) == -6/z**2 + 15/z**4 + assert spherical_bessel_fn(4, z) == 1/z - 45/z**3 + 105/z**5 diff --git a/lib/python3.12/site-packages/sympy/polys/tests/test_partfrac.py b/lib/python3.12/site-packages/sympy/polys/tests/test_partfrac.py new file mode 100644 index 0000000000000000000000000000000000000000..83c5d48383d20e67dbb53c081093ad35e654c9a0 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/polys/tests/test_partfrac.py @@ -0,0 +1,249 @@ +"""Tests for algorithms for partial fraction decomposition of rational +functions. """ + +from sympy.polys.partfrac import ( + apart_undetermined_coeffs, + apart, + apart_list, assemble_partfrac_list +) + +from sympy.core.expr import Expr +from sympy.core.function import Lambda +from sympy.core.numbers import (E, I, Rational, pi, all_close) +from sympy.core.relational import Eq +from sympy.core.singleton import S +from sympy.core.symbol import (Dummy, Symbol) +from sympy.functions.elementary.miscellaneous import sqrt +from sympy.matrices.dense import Matrix +from sympy.polys.polytools import (Poly, factor) +from sympy.polys.rationaltools import together +from sympy.polys.rootoftools import RootSum +from sympy.testing.pytest import raises, XFAIL +from sympy.abc import x, y, a, b, c + + +def test_apart(): + assert apart(1) == 1 + assert apart(1, x) == 1 + + f, g = (x**2 + 1)/(x + 1), 2/(x + 1) + x - 1 + + assert apart(f, full=False) == g + assert apart(f, full=True) == g + + f, g = 1/(x + 2)/(x + 1), 1/(1 + x) - 1/(2 + x) + + assert apart(f, full=False) == g + assert apart(f, full=True) == g + + f, g = 1/(x + 1)/(x + 5), -1/(5 + x)/4 + 1/(1 + x)/4 + + assert apart(f, full=False) == g + assert apart(f, full=True) == g + + assert apart((E*x + 2)/(x - pi)*(x - 1), x) == \ + 2 - E + E*pi + E*x + (E*pi + 2)*(pi - 1)/(x - pi) + + assert apart(Eq((x**2 + 1)/(x + 1), x), x) == Eq(x - 1 + 2/(x + 1), x) + + assert apart(x/2, y) == x/2 + + f, g = (x+y)/(2*x - y), Rational(3, 2)*y/(2*x - y) + S.Half + + assert apart(f, x, full=False) == g + assert apart(f, x, full=True) == g + + f, g = (x+y)/(2*x - y), 3*x/(2*x - y) - 1 + + assert apart(f, y, full=False) == g + assert apart(f, y, full=True) == g + + raises(NotImplementedError, lambda: apart(1/(x + 1)/(y + 2))) + + +def test_apart_matrix(): + M = Matrix(2, 2, lambda i, j: 1/(x + i + 1)/(x + j)) + + assert apart(M) == Matrix([ + [1/x - 1/(x + 1), (x + 1)**(-2)], + [1/(2*x) - (S.Half)/(x + 2), 1/(x + 1) - 1/(x + 2)], + ]) + + +def test_apart_symbolic(): + f = a*x**4 + (2*b + 2*a*c)*x**3 + (4*b*c - a**2 + a*c**2)*x**2 + \ + (-2*a*b + 2*b*c**2)*x - b**2 + g = a**2*x**4 + (2*a*b + 2*c*a**2)*x**3 + (4*a*b*c + b**2 + + a**2*c**2)*x**2 + (2*c*b**2 + 2*a*b*c**2)*x + b**2*c**2 + + assert apart(f/g, x) == 1/a - 1/(x + c)**2 - b**2/(a*(a*x + b)**2) + + assert apart(1/((x + a)*(x + b)*(x + c)), x) == \ + 1/((a - c)*(b - c)*(c + x)) - 1/((a - b)*(b - c)*(b + x)) + \ + 1/((a - b)*(a - c)*(a + x)) + + +def _make_extension_example(): + # https://github.com/sympy/sympy/issues/18531 + from sympy.core import Mul + def mul2(expr): + # 2-arg mul hack... + return Mul(2, expr, evaluate=False) + + f = ((x**2 + 1)**3/((x - 1)**2*(x + 1)**2*(-x**2 + 2*x + 1)*(x**2 + 2*x - 1))) + g = (1/mul2(x - sqrt(2) + 1) + - 1/mul2(x - sqrt(2) - 1) + + 1/mul2(x + 1 + sqrt(2)) + - 1/mul2(x - 1 + sqrt(2)) + + 1/mul2((x + 1)**2) + + 1/mul2((x - 1)**2)) + return f, g + + +def test_apart_extension(): + f = 2/(x**2 + 1) + g = I/(x + I) - I/(x - I) + + assert apart(f, extension=I) == g + assert apart(f, gaussian=True) == g + + f = x/((x - 2)*(x + I)) + + assert factor(together(apart(f)).expand()) == f + + f, g = _make_extension_example() + + # XXX: Only works with dotprodsimp. See test_apart_extension_xfail below + from sympy.matrices import dotprodsimp + with dotprodsimp(True): + assert apart(f, x, extension={sqrt(2)}) == g + + +def test_apart_extension_xfail(): + f, g = _make_extension_example() + assert apart(f, x, extension={sqrt(2)}) == g + + +def test_apart_full(): + f = 1/(x**2 + 1) + + assert apart(f, full=False) == f + assert apart(f, full=True).dummy_eq( + -RootSum(x**2 + 1, Lambda(a, a/(x - a)), auto=False)/2) + + f = 1/(x**3 + x + 1) + + assert apart(f, full=False) == f + assert apart(f, full=True).dummy_eq( + RootSum(x**3 + x + 1, + Lambda(a, (a**2*Rational(6, 31) - a*Rational(9, 31) + Rational(4, 31))/(x - a)), auto=False)) + + f = 1/(x**5 + 1) + + assert apart(f, full=False) == \ + (Rational(-1, 5))*((x**3 - 2*x**2 + 3*x - 4)/(x**4 - x**3 + x**2 - + x + 1)) + (Rational(1, 5))/(x + 1) + assert apart(f, full=True).dummy_eq( + -RootSum(x**4 - x**3 + x**2 - x + 1, + Lambda(a, a/(x - a)), auto=False)/5 + (Rational(1, 5))/(x + 1)) + + +def test_apart_full_floats(): + # https://github.com/sympy/sympy/issues/26648 + f = ( + 6.43369157032015e-9*x**3 + 1.35203404799555e-5*x**2 + + 0.00357538393743079*x + 0.085 + )/( + 4.74334912634438e-11*x**4 + 4.09576274286244e-6*x**3 + + 0.00334241812250921*x**2 + 0.15406018058983*x + 1.0 + ) + + expected = ( + 133.599202650992/(x + 85524.0054884464) + + 1.07757928431867/(x + 774.88576677949) + + 0.395006955518971/(x + 40.7977016133126) + + 0.564264854137341/(x + 7.79746609204661) + ) + + f_apart = apart(f, full=True).evalf() + + # There is a significant floating point error in this operation. + assert all_close(f_apart, expected, rtol=1e-3, atol=1e-5) + + +def test_apart_undetermined_coeffs(): + p = Poly(2*x - 3) + q = Poly(x**9 - x**8 - x**6 + x**5 - 2*x**2 + 3*x - 1) + r = (-x**7 - x**6 - x**5 + 4)/(x**8 - x**5 - 2*x + 1) + 1/(x - 1) + + assert apart_undetermined_coeffs(p, q) == r + + p = Poly(1, x, domain='ZZ[a,b]') + q = Poly((x + a)*(x + b), x, domain='ZZ[a,b]') + r = 1/((a - b)*(b + x)) - 1/((a - b)*(a + x)) + + assert apart_undetermined_coeffs(p, q) == r + + +def test_apart_list(): + from sympy.utilities.iterables import numbered_symbols + def dummy_eq(i, j): + if type(i) in (list, tuple): + return all(dummy_eq(i, j) for i, j in zip(i, j)) + return i == j or i.dummy_eq(j) + + w0, w1, w2 = Symbol("w0"), Symbol("w1"), Symbol("w2") + _a = Dummy("a") + + f = (-2*x - 2*x**2) / (3*x**2 - 6*x) + got = apart_list(f, x, dummies=numbered_symbols("w")) + ans = (-1, Poly(Rational(2, 3), x, domain='QQ'), + [(Poly(w0 - 2, w0, domain='ZZ'), Lambda(_a, 2), Lambda(_a, -_a + x), 1)]) + assert dummy_eq(got, ans) + + got = apart_list(2/(x**2-2), x, dummies=numbered_symbols("w")) + ans = (1, Poly(0, x, domain='ZZ'), [(Poly(w0**2 - 2, w0, domain='ZZ'), + Lambda(_a, _a/2), + Lambda(_a, -_a + x), 1)]) + assert dummy_eq(got, ans) + + f = 36 / (x**5 - 2*x**4 - 2*x**3 + 4*x**2 + x - 2) + got = apart_list(f, x, dummies=numbered_symbols("w")) + ans = (1, Poly(0, x, domain='ZZ'), + [(Poly(w0 - 2, w0, domain='ZZ'), Lambda(_a, 4), Lambda(_a, -_a + x), 1), + (Poly(w1**2 - 1, w1, domain='ZZ'), Lambda(_a, -3*_a - 6), Lambda(_a, -_a + x), 2), + (Poly(w2 + 1, w2, domain='ZZ'), Lambda(_a, -4), Lambda(_a, -_a + x), 1)]) + assert dummy_eq(got, ans) + + +def test_assemble_partfrac_list(): + f = 36 / (x**5 - 2*x**4 - 2*x**3 + 4*x**2 + x - 2) + pfd = apart_list(f) + assert assemble_partfrac_list(pfd) == -4/(x + 1) - 3/(x + 1)**2 - 9/(x - 1)**2 + 4/(x - 2) + + a = Dummy("a") + pfd = (1, Poly(0, x, domain='ZZ'), [([sqrt(2),-sqrt(2)], Lambda(a, a/2), Lambda(a, -a + x), 1)]) + assert assemble_partfrac_list(pfd) == -1/(sqrt(2)*(x + sqrt(2))) + 1/(sqrt(2)*(x - sqrt(2))) + + +@XFAIL +def test_noncommutative_pseudomultivariate(): + # apart doesn't go inside noncommutative expressions + class foo(Expr): + is_commutative=False + e = x/(x + x*y) + c = 1/(1 + y) + assert apart(e + foo(e)) == c + foo(c) + assert apart(e*foo(e)) == c*foo(c) + +def test_noncommutative(): + class foo(Expr): + is_commutative=False + e = x/(x + x*y) + c = 1/(1 + y) + assert apart(e + foo()) == c + foo() + +def test_issue_5798(): + assert apart( + 2*x/(x**2 + 1) - (x - 1)/(2*(x**2 + 1)) + 1/(2*(x + 1)) - 2/x) == \ + (3*x + 1)/(x**2 + 1)/2 + 1/(x + 1)/2 - 2/x diff --git a/lib/python3.12/site-packages/sympy/polys/tests/test_polyclasses.py b/lib/python3.12/site-packages/sympy/polys/tests/test_polyclasses.py new file mode 100644 index 0000000000000000000000000000000000000000..da7a924528702bfb2e6527bd68a566be41583221 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/polys/tests/test_polyclasses.py @@ -0,0 +1,588 @@ +"""Tests for OO layer of several polynomial representations. """ + +from sympy.functions.elementary.miscellaneous import sqrt +from sympy.polys.domains import ZZ, QQ +from sympy.polys.polyclasses import DMP, DMF, ANP +from sympy.polys.polyerrors import (CoercionFailed, ExactQuotientFailed, + NotInvertible) +from sympy.polys.specialpolys import f_polys +from sympy.testing.pytest import raises, warns_deprecated_sympy + +f_0, f_1, f_2, f_3, f_4, f_5, f_6 = [ f.to_dense() for f in f_polys() ] + +def test_DMP___init__(): + f = DMP([[ZZ(0)], [], [ZZ(0), ZZ(1), ZZ(2)], [ZZ(3)]], ZZ) + + assert f._rep == [[1, 2], [3]] + assert f.dom == ZZ + assert f.lev == 1 + + f = DMP([[ZZ(1), ZZ(2)], [ZZ(3)]], ZZ, 1) + + assert f._rep == [[1, 2], [3]] + assert f.dom == ZZ + assert f.lev == 1 + + f = DMP.from_dict({(1, 1): ZZ(1), (0, 0): ZZ(2)}, 1, ZZ) + + assert f._rep == [[1, 0], [2]] + assert f.dom == ZZ + assert f.lev == 1 + + +def test_DMP_rep_deprecation(): + f = DMP([1, 2, 3], ZZ) + + with warns_deprecated_sympy(): + assert f.rep == [1, 2, 3] + + +def test_DMP___eq__(): + assert DMP([[ZZ(1), ZZ(2)], [ZZ(3)]], ZZ) == \ + DMP([[ZZ(1), ZZ(2)], [ZZ(3)]], ZZ) + + assert DMP([[ZZ(1), ZZ(2)], [ZZ(3)]], ZZ) == \ + DMP([[QQ(1), QQ(2)], [QQ(3)]], QQ) + assert DMP([[QQ(1), QQ(2)], [QQ(3)]], QQ) == \ + DMP([[ZZ(1), ZZ(2)], [ZZ(3)]], ZZ) + + assert DMP([[[ZZ(1)]]], ZZ) != DMP([[ZZ(1)]], ZZ) + assert DMP([[ZZ(1)]], ZZ) != DMP([[[ZZ(1)]]], ZZ) + + +def test_DMP___bool__(): + assert bool(DMP([[]], ZZ)) is False + assert bool(DMP([[ZZ(1)]], ZZ)) is True + + +def test_DMP_to_dict(): + f = DMP([[ZZ(3)], [], [ZZ(2)], [], [ZZ(8)]], ZZ) + + assert f.to_dict() == \ + {(4, 0): 3, (2, 0): 2, (0, 0): 8} + assert f.to_sympy_dict() == \ + {(4, 0): ZZ.to_sympy(3), (2, 0): ZZ.to_sympy(2), (0, 0): + ZZ.to_sympy(8)} + + +def test_DMP_properties(): + assert DMP([[]], ZZ).is_zero is True + assert DMP([[ZZ(1)]], ZZ).is_zero is False + + assert DMP([[ZZ(1)]], ZZ).is_one is True + assert DMP([[ZZ(2)]], ZZ).is_one is False + + assert DMP([[ZZ(1)]], ZZ).is_ground is True + assert DMP([[ZZ(1)], [ZZ(2)], [ZZ(1)]], ZZ).is_ground is False + + assert DMP([[ZZ(1)], [ZZ(2), ZZ(0)], [ZZ(1), ZZ(0)]], ZZ).is_sqf is True + assert DMP([[ZZ(1)], [ZZ(2), ZZ(0)], [ZZ(1), ZZ(0), ZZ(0)]], ZZ).is_sqf is False + + assert DMP([[ZZ(1), ZZ(2)], [ZZ(3)]], ZZ).is_monic is True + assert DMP([[ZZ(2), ZZ(2)], [ZZ(3)]], ZZ).is_monic is False + + assert DMP([[ZZ(1), ZZ(2)], [ZZ(3)]], ZZ).is_primitive is True + assert DMP([[ZZ(2), ZZ(4)], [ZZ(6)]], ZZ).is_primitive is False + + +def test_DMP_arithmetics(): + f = DMP([[ZZ(2)], [ZZ(2), ZZ(0)]], ZZ) + + assert f.mul_ground(2) == DMP([[ZZ(4)], [ZZ(4), ZZ(0)]], ZZ) + assert f.quo_ground(2) == DMP([[ZZ(1)], [ZZ(1), ZZ(0)]], ZZ) + + raises(ExactQuotientFailed, lambda: f.exquo_ground(3)) + + f = DMP([[ZZ(-5)]], ZZ) + g = DMP([[ZZ(5)]], ZZ) + + assert f.abs() == g + assert abs(f) == g + + assert g.neg() == f + assert -g == f + + h = DMP([[]], ZZ) + + assert f.add(g) == h + assert f + g == h + assert g + f == h + assert f + 5 == h + assert 5 + f == h + + h = DMP([[ZZ(-10)]], ZZ) + + assert f.sub(g) == h + assert f - g == h + assert g - f == -h + assert f - 5 == h + assert 5 - f == -h + + h = DMP([[ZZ(-25)]], ZZ) + + assert f.mul(g) == h + assert f * g == h + assert g * f == h + assert f * 5 == h + assert 5 * f == h + + h = DMP([[ZZ(25)]], ZZ) + + assert f.sqr() == h + assert f.pow(2) == h + assert f**2 == h + + raises(TypeError, lambda: f.pow('x')) + + f = DMP([[ZZ(1)], [], [ZZ(1), ZZ(0), ZZ(0)]], ZZ) + g = DMP([[ZZ(2)], [ZZ(-2), ZZ(0)]], ZZ) + + q = DMP([[ZZ(2)], [ZZ(2), ZZ(0)]], ZZ) + r = DMP([[ZZ(8), ZZ(0), ZZ(0)]], ZZ) + + assert f.pdiv(g) == (q, r) + assert f.pquo(g) == q + assert f.prem(g) == r + + raises(ExactQuotientFailed, lambda: f.pexquo(g)) + + f = DMP([[ZZ(1)], [], [ZZ(1), ZZ(0), ZZ(0)]], ZZ) + g = DMP([[ZZ(1)], [ZZ(-1), ZZ(0)]], ZZ) + + q = DMP([[ZZ(1)], [ZZ(1), ZZ(0)]], ZZ) + r = DMP([[ZZ(2), ZZ(0), ZZ(0)]], ZZ) + + assert f.div(g) == (q, r) + assert f.quo(g) == q + assert f.rem(g) == r + + assert divmod(f, g) == (q, r) + assert f // g == q + assert f % g == r + + raises(ExactQuotientFailed, lambda: f.exquo(g)) + + f = DMP([ZZ(1), ZZ(0), ZZ(-1)], ZZ) + g = DMP([ZZ(2), ZZ(-2)], ZZ) + + q = DMP([], ZZ) + r = f + + pq = DMP([ZZ(2), ZZ(2)], ZZ) + pr = DMP([], ZZ) + + assert f.div(g) == (q, r) + assert f.quo(g) == q + assert f.rem(g) == r + + assert divmod(f, g) == (q, r) + assert f // g == q + assert f % g == r + + raises(ExactQuotientFailed, lambda: f.exquo(g)) + + assert f.pdiv(g) == (pq, pr) + assert f.pquo(g) == pq + assert f.prem(g) == pr + assert f.pexquo(g) == pq + + +def test_DMP_functionality(): + f = DMP([[ZZ(1)], [ZZ(2), ZZ(0)], [ZZ(1), ZZ(0), ZZ(0)]], ZZ) + g = DMP([[ZZ(1)], [ZZ(1), ZZ(0)]], ZZ) + h = DMP([[ZZ(1)]], ZZ) + + assert f.degree() == 2 + assert f.degree_list() == (2, 2) + assert f.total_degree() == 2 + + assert f.LC() == ZZ(1) + assert f.TC() == ZZ(0) + assert f.nth(1, 1) == ZZ(2) + + raises(TypeError, lambda: f.nth(0, 'x')) + + assert f.max_norm() == 2 + assert f.l1_norm() == 4 + + u = DMP([[ZZ(2)], [ZZ(2), ZZ(0)]], ZZ) + + assert f.diff(m=1, j=0) == u + assert f.diff(m=1, j=1) == u + + raises(TypeError, lambda: f.diff(m='x', j=0)) + + u = DMP([ZZ(1), ZZ(2), ZZ(1)], ZZ) + v = DMP([ZZ(1), ZZ(2), ZZ(1)], ZZ) + + assert f.eval(a=1, j=0) == u + assert f.eval(a=1, j=1) == v + + assert f.eval(1).eval(1) == ZZ(4) + + assert f.cofactors(g) == (g, g, h) + assert f.gcd(g) == g + assert f.lcm(g) == f + + u = DMP([[QQ(45), QQ(30), QQ(5)]], QQ) + v = DMP([[QQ(1), QQ(2, 3), QQ(1, 9)]], QQ) + + assert u.monic() == v + + assert (4*f).content() == ZZ(4) + assert (4*f).primitive() == (ZZ(4), f) + + f = DMP([QQ(1,3), QQ(1)], QQ) + g = DMP([QQ(1,7), QQ(1)], QQ) + + assert f.cancel(g) == f.cancel(g, include=True) == ( + DMP([QQ(7), QQ(21)], QQ), + DMP([QQ(3), QQ(21)], QQ) + ) + assert f.cancel(g, include=False) == ( + QQ(7), + QQ(3), + DMP([QQ(1), QQ(3)], QQ), + DMP([QQ(1), QQ(7)], QQ) + ) + + f = DMP([[ZZ(1)], [ZZ(2)], [ZZ(3)], [ZZ(4)], [ZZ(5)], [ZZ(6)]], ZZ) + + assert f.trunc(3) == DMP([[ZZ(1)], [ZZ(-1)], [], [ZZ(1)], [ZZ(-1)], []], ZZ) + + f = DMP(f_4, ZZ) + + assert f.sqf_part() == -f + assert f.sqf_list() == (ZZ(-1), [(-f, 1)]) + + f = DMP([[ZZ(-1)], [], [], [ZZ(5)]], ZZ) + g = DMP([[ZZ(3), ZZ(1)], [], []], ZZ) + h = DMP([[ZZ(45), ZZ(30), ZZ(5)]], ZZ) + + r = DMP([ZZ(675), ZZ(675), ZZ(225), ZZ(25)], ZZ) + + assert f.subresultants(g) == [f, g, h] + assert f.resultant(g) == r + + f = DMP([ZZ(1), ZZ(3), ZZ(9), ZZ(-13)], ZZ) + + assert f.discriminant() == -11664 + + f = DMP([QQ(2), QQ(0)], QQ) + g = DMP([QQ(1), QQ(0), QQ(-16)], QQ) + + s = DMP([QQ(1, 32), QQ(0)], QQ) + t = DMP([QQ(-1, 16)], QQ) + h = DMP([QQ(1)], QQ) + + assert f.half_gcdex(g) == (s, h) + assert f.gcdex(g) == (s, t, h) + + assert f.invert(g) == s + + f = DMP([[QQ(1)], [QQ(2)], [QQ(3)]], QQ) + + raises(ValueError, lambda: f.half_gcdex(f)) + raises(ValueError, lambda: f.gcdex(f)) + + raises(ValueError, lambda: f.invert(f)) + + f = DMP(ZZ.map([1, 0, 20, 0, 150, 0, 500, 0, 625, -2, 0, -10, 9]), ZZ) + g = DMP([ZZ(1), ZZ(0), ZZ(0), ZZ(-2), ZZ(9)], ZZ) + h = DMP([ZZ(1), ZZ(0), ZZ(5), ZZ(0)], ZZ) + + assert g.compose(h) == f + assert f.decompose() == [g, h] + + f = DMP([[QQ(1)], [QQ(2)], [QQ(3)]], QQ) + + raises(ValueError, lambda: f.decompose()) + raises(ValueError, lambda: f.sturm()) + + +def test_DMP_exclude(): + f = [[[[[[[[[[[[[[[[[[[[[[[[[[ZZ(1)]], [[]]]]]]]]]]]]]]]]]]]]]]]]]] + J = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 24, 25] + + assert DMP(f, ZZ).exclude() == (J, DMP([ZZ(1), ZZ(0)], ZZ)) + assert DMP([[ZZ(1)], [ZZ(1), ZZ(0)]], ZZ).exclude() ==\ + ([], DMP([[ZZ(1)], [ZZ(1), ZZ(0)]], ZZ)) + + +def test_DMF__init__(): + f = DMF(([[0], [], [0, 1, 2], [3]], [[1, 2, 3]]), ZZ) + + assert f.num == [[1, 2], [3]] + assert f.den == [[1, 2, 3]] + assert f.lev == 1 + assert f.dom == ZZ + + f = DMF(([[1, 2], [3]], [[1, 2, 3]]), ZZ, 1) + + assert f.num == [[1, 2], [3]] + assert f.den == [[1, 2, 3]] + assert f.lev == 1 + assert f.dom == ZZ + + f = DMF(([[-1], [-2]], [[3], [-4]]), ZZ) + + assert f.num == [[-1], [-2]] + assert f.den == [[3], [-4]] + assert f.lev == 1 + assert f.dom == ZZ + + f = DMF(([[1], [2]], [[-3], [4]]), ZZ) + + assert f.num == [[-1], [-2]] + assert f.den == [[3], [-4]] + assert f.lev == 1 + assert f.dom == ZZ + + f = DMF(([[1], [2]], [[-3], [4]]), ZZ) + + assert f.num == [[-1], [-2]] + assert f.den == [[3], [-4]] + assert f.lev == 1 + assert f.dom == ZZ + + f = DMF(([[]], [[-3], [4]]), ZZ) + + assert f.num == [[]] + assert f.den == [[1]] + assert f.lev == 1 + assert f.dom == ZZ + + f = DMF(17, ZZ, 1) + + assert f.num == [[17]] + assert f.den == [[1]] + assert f.lev == 1 + assert f.dom == ZZ + + f = DMF(([[1], [2]]), ZZ) + + assert f.num == [[1], [2]] + assert f.den == [[1]] + assert f.lev == 1 + assert f.dom == ZZ + + f = DMF([[0], [], [0, 1, 2], [3]], ZZ) + + assert f.num == [[1, 2], [3]] + assert f.den == [[1]] + assert f.lev == 1 + assert f.dom == ZZ + + f = DMF({(1, 1): 1, (0, 0): 2}, ZZ, 1) + + assert f.num == [[1, 0], [2]] + assert f.den == [[1]] + assert f.lev == 1 + assert f.dom == ZZ + + f = DMF(([[QQ(1)], [QQ(2)]], [[-QQ(3)], [QQ(4)]]), QQ) + + assert f.num == [[-QQ(1)], [-QQ(2)]] + assert f.den == [[QQ(3)], [-QQ(4)]] + assert f.lev == 1 + assert f.dom == QQ + + f = DMF(([[QQ(1, 5)], [QQ(2, 5)]], [[-QQ(3, 7)], [QQ(4, 7)]]), QQ) + + assert f.num == [[-QQ(7)], [-QQ(14)]] + assert f.den == [[QQ(15)], [-QQ(20)]] + assert f.lev == 1 + assert f.dom == QQ + + raises(ValueError, lambda: DMF(([1], [[1]]), ZZ)) + raises(ZeroDivisionError, lambda: DMF(([1], []), ZZ)) + + +def test_DMF__bool__(): + assert bool(DMF([[]], ZZ)) is False + assert bool(DMF([[1]], ZZ)) is True + + +def test_DMF_properties(): + assert DMF([[]], ZZ).is_zero is True + assert DMF([[]], ZZ).is_one is False + + assert DMF([[1]], ZZ).is_zero is False + assert DMF([[1]], ZZ).is_one is True + + assert DMF(([[1]], [[2]]), ZZ).is_one is False + + +def test_DMF_arithmetics(): + f = DMF([[7], [-9]], ZZ) + g = DMF([[-7], [9]], ZZ) + + assert f.neg() == -f == g + + f = DMF(([[1]], [[1], []]), ZZ) + g = DMF(([[1]], [[1, 0]]), ZZ) + + h = DMF(([[1], [1, 0]], [[1, 0], []]), ZZ) + + assert f.add(g) == f + g == h + assert g.add(f) == g + f == h + + h = DMF(([[-1], [1, 0]], [[1, 0], []]), ZZ) + + assert f.sub(g) == f - g == h + + h = DMF(([[1]], [[1, 0], []]), ZZ) + + assert f.mul(g) == f*g == h + assert g.mul(f) == g*f == h + + h = DMF(([[1, 0]], [[1], []]), ZZ) + + assert f.quo(g) == f/g == h + + h = DMF(([[1]], [[1], [], [], []]), ZZ) + + assert f.pow(3) == f**3 == h + + h = DMF(([[1]], [[1, 0, 0, 0]]), ZZ) + + assert g.pow(3) == g**3 == h + + h = DMF(([[1, 0]], [[1]]), ZZ) + + assert g.pow(-1) == g**-1 == h + + +def test_ANP___init__(): + rep = [QQ(1), QQ(1)] + mod = [QQ(1), QQ(0), QQ(1)] + + f = ANP(rep, mod, QQ) + + assert f.to_list() == [QQ(1), QQ(1)] + assert f.mod_to_list() == [QQ(1), QQ(0), QQ(1)] + assert f.dom == QQ + + rep = {1: QQ(1), 0: QQ(1)} + mod = {2: QQ(1), 0: QQ(1)} + + f = ANP(rep, mod, QQ) + + assert f.to_list() == [QQ(1), QQ(1)] + assert f.mod_to_list() == [QQ(1), QQ(0), QQ(1)] + assert f.dom == QQ + + f = ANP(1, mod, QQ) + + assert f.to_list() == [QQ(1)] + assert f.mod_to_list() == [QQ(1), QQ(0), QQ(1)] + assert f.dom == QQ + + f = ANP([1, 0.5], mod, QQ) + + assert all(QQ.of_type(a) for a in f.to_list()) + + raises(CoercionFailed, lambda: ANP([sqrt(2)], mod, QQ)) + + +def test_ANP___eq__(): + a = ANP([QQ(1), QQ(1)], [QQ(1), QQ(0), QQ(1)], QQ) + b = ANP([QQ(1), QQ(1)], [QQ(1), QQ(0), QQ(2)], QQ) + + assert (a == a) is True + assert (a != a) is False + + assert (a == b) is False + assert (a != b) is True + + b = ANP([QQ(1), QQ(2)], [QQ(1), QQ(0), QQ(1)], QQ) + + assert (a == b) is False + assert (a != b) is True + + +def test_ANP___bool__(): + assert bool(ANP([], [QQ(1), QQ(0), QQ(1)], QQ)) is False + assert bool(ANP([QQ(1)], [QQ(1), QQ(0), QQ(1)], QQ)) is True + + +def test_ANP_properties(): + mod = [QQ(1), QQ(0), QQ(1)] + + assert ANP([QQ(0)], mod, QQ).is_zero is True + assert ANP([QQ(1)], mod, QQ).is_zero is False + + assert ANP([QQ(1)], mod, QQ).is_one is True + assert ANP([QQ(2)], mod, QQ).is_one is False + + +def test_ANP_arithmetics(): + mod = [QQ(1), QQ(0), QQ(0), QQ(-2)] + + a = ANP([QQ(2), QQ(-1), QQ(1)], mod, QQ) + b = ANP([QQ(1), QQ(2)], mod, QQ) + + c = ANP([QQ(-2), QQ(1), QQ(-1)], mod, QQ) + + assert a.neg() == -a == c + + c = ANP([QQ(2), QQ(0), QQ(3)], mod, QQ) + + assert a.add(b) == a + b == c + assert b.add(a) == b + a == c + + c = ANP([QQ(2), QQ(-2), QQ(-1)], mod, QQ) + + assert a.sub(b) == a - b == c + + c = ANP([QQ(-2), QQ(2), QQ(1)], mod, QQ) + + assert b.sub(a) == b - a == c + + c = ANP([QQ(3), QQ(-1), QQ(6)], mod, QQ) + + assert a.mul(b) == a*b == c + assert b.mul(a) == b*a == c + + c = ANP([QQ(-1, 43), QQ(9, 43), QQ(5, 43)], mod, QQ) + + assert a.pow(0) == a**(0) == ANP(1, mod, QQ) + assert a.pow(1) == a**(1) == a + + assert a.pow(-1) == a**(-1) == c + + assert a.quo(a) == a.mul(a.pow(-1)) == a*a**(-1) == ANP(1, mod, QQ) + + c = ANP([], [1, 0, 0, -2], QQ) + r1 = a.rem(b) + + (q, r2) = a.div(b) + + assert r1 == r2 == c == a % b + + raises(NotInvertible, lambda: a.div(c)) + raises(NotInvertible, lambda: a.rem(c)) + + # Comparison with "hard-coded" value fails despite looking identical + # from sympy import Rational + # c = ANP([Rational(11, 10), Rational(-1, 5), Rational(-3, 5)], [1, 0, 0, -2], QQ) + + assert q == a/b # == c + +def test_ANP_unify(): + mod_z = [ZZ(1), ZZ(0), ZZ(-2)] + mod_q = [QQ(1), QQ(0), QQ(-2)] + + a = ANP([QQ(1)], mod_q, QQ) + b = ANP([ZZ(1)], mod_z, ZZ) + + assert a.unify(b)[0] == QQ + assert b.unify(a)[0] == QQ + assert a.unify(a)[0] == QQ + assert b.unify(b)[0] == ZZ + + assert a.unify_ANP(b)[-1] == QQ + assert b.unify_ANP(a)[-1] == QQ + assert a.unify_ANP(a)[-1] == QQ + assert b.unify_ANP(b)[-1] == ZZ diff --git a/lib/python3.12/site-packages/sympy/polys/tests/test_polyfuncs.py b/lib/python3.12/site-packages/sympy/polys/tests/test_polyfuncs.py new file mode 100644 index 0000000000000000000000000000000000000000..496f63bf14e4dd9f68cf653004eb35a3ed7615ca --- /dev/null +++ b/lib/python3.12/site-packages/sympy/polys/tests/test_polyfuncs.py @@ -0,0 +1,126 @@ +"""Tests for high-level polynomials manipulation functions. """ + +from sympy.polys.polyfuncs import ( + symmetrize, horner, interpolate, rational_interpolate, viete, +) + +from sympy.polys.polyerrors import ( + MultivariatePolynomialError, +) + +from sympy.core.singleton import S +from sympy.core.symbol import symbols +from sympy.testing.pytest import raises + +from sympy.abc import a, b, c, d, e, x, y, z + + +def test_symmetrize(): + assert symmetrize(0, x, y, z) == (0, 0) + assert symmetrize(1, x, y, z) == (1, 0) + + s1 = x + y + z + s2 = x*y + x*z + y*z + + assert symmetrize(1) == (1, 0) + assert symmetrize(1, formal=True) == (1, 0, []) + + assert symmetrize(x) == (x, 0) + assert symmetrize(x + 1) == (x + 1, 0) + + assert symmetrize(x, x, y) == (x + y, -y) + assert symmetrize(x + 1, x, y) == (x + y + 1, -y) + + assert symmetrize(x, x, y, z) == (s1, -y - z) + assert symmetrize(x + 1, x, y, z) == (s1 + 1, -y - z) + + assert symmetrize(x**2, x, y, z) == (s1**2 - 2*s2, -y**2 - z**2) + + assert symmetrize(x**2 + y**2) == (-2*x*y + (x + y)**2, 0) + assert symmetrize(x**2 - y**2) == (-2*x*y + (x + y)**2, -2*y**2) + + assert symmetrize(x**3 + y**2 + a*x**2 + b*y**3, x, y) == \ + (-3*x*y*(x + y) - 2*a*x*y + a*(x + y)**2 + (x + y)**3, + y**2*(1 - a) + y**3*(b - 1)) + + U = [u0, u1, u2] = symbols('u:3') + + assert symmetrize(x + 1, x, y, z, formal=True, symbols=U) == \ + (u0 + 1, -y - z, [(u0, x + y + z), (u1, x*y + x*z + y*z), (u2, x*y*z)]) + + assert symmetrize([1, 2, 3]) == [(1, 0), (2, 0), (3, 0)] + assert symmetrize([1, 2, 3], formal=True) == ([(1, 0), (2, 0), (3, 0)], []) + + assert symmetrize([x + y, x - y]) == [(x + y, 0), (x + y, -2*y)] + + +def test_horner(): + assert horner(0) == 0 + assert horner(1) == 1 + assert horner(x) == x + + assert horner(x + 1) == x + 1 + assert horner(x**2 + 1) == x**2 + 1 + assert horner(x**2 + x) == (x + 1)*x + assert horner(x**2 + x + 1) == (x + 1)*x + 1 + + assert horner( + 9*x**4 + 8*x**3 + 7*x**2 + 6*x + 5) == (((9*x + 8)*x + 7)*x + 6)*x + 5 + assert horner( + a*x**4 + b*x**3 + c*x**2 + d*x + e) == (((a*x + b)*x + c)*x + d)*x + e + + assert horner(4*x**2*y**2 + 2*x**2*y + 2*x*y**2 + x*y, wrt=x) == (( + 4*y + 2)*x*y + (2*y + 1)*y)*x + assert horner(4*x**2*y**2 + 2*x**2*y + 2*x*y**2 + x*y, wrt=y) == (( + 4*x + 2)*y*x + (2*x + 1)*x)*y + + +def test_interpolate(): + assert interpolate([1, 4, 9, 16], x) == x**2 + assert interpolate([1, 4, 9, 25], x) == S(3)*x**3/2 - S(8)*x**2 + S(33)*x/2 - 9 + assert interpolate([(1, 1), (2, 4), (3, 9)], x) == x**2 + assert interpolate([(1, 2), (2, 5), (3, 10)], x) == 1 + x**2 + assert interpolate({1: 2, 2: 5, 3: 10}, x) == 1 + x**2 + assert interpolate({5: 2, 7: 5, 8: 10, 9: 13}, x) == \ + -S(13)*x**3/24 + S(12)*x**2 - S(2003)*x/24 + 187 + assert interpolate([(1, 3), (0, 6), (2, 5), (5, 7), (-2, 4)], x) == \ + S(-61)*x**4/280 + S(247)*x**3/210 + S(139)*x**2/280 - S(1871)*x/420 + 6 + assert interpolate((9, 4, 9), 3) == 9 + assert interpolate((1, 9, 16), 1) is S.One + assert interpolate(((x, 1), (2, 3)), x) is S.One + assert interpolate({x: 1, 2: 3}, x) is S.One + assert interpolate(((2, x), (1, 3)), x) == x**2 - 4*x + 6 + + +def test_rational_interpolate(): + x, y = symbols('x,y') + xdata = [1, 2, 3, 4, 5, 6] + ydata1 = [120, 150, 200, 255, 312, 370] + ydata2 = [-210, -35, 105, 231, 350, 465] + assert rational_interpolate(list(zip(xdata, ydata1)), 2) == ( + (60*x**2 + 60)/x ) + assert rational_interpolate(list(zip(xdata, ydata1)), 3) == ( + (60*x**2 + 60)/x ) + assert rational_interpolate(list(zip(xdata, ydata2)), 2, X=y) == ( + (105*y**2 - 525)/(y + 1) ) + xdata = list(range(1,11)) + ydata = [-1923885361858460, -5212158811973685, -9838050145867125, + -15662936261217245, -22469424125057910, -30073793365223685, + -38332297297028735, -47132954289530109, -56387719094026320, + -66026548943876885] + assert rational_interpolate(list(zip(xdata, ydata)), 5) == ( + (-12986226192544605*x**4 + + 8657484128363070*x**3 - 30301194449270745*x**2 + 4328742064181535*x + - 4328742064181535)/(x**3 + 9*x**2 - 3*x + 11)) + + +def test_viete(): + r1, r2 = symbols('r1, r2') + + assert viete( + a*x**2 + b*x + c, [r1, r2], x) == [(r1 + r2, -b/a), (r1*r2, c/a)] + + raises(ValueError, lambda: viete(1, [], x)) + raises(ValueError, lambda: viete(x**2 + 1, [r1])) + + raises(MultivariatePolynomialError, lambda: viete(x + y, [r1])) diff --git a/lib/python3.12/site-packages/sympy/polys/tests/test_polymatrix.py b/lib/python3.12/site-packages/sympy/polys/tests/test_polymatrix.py new file mode 100644 index 0000000000000000000000000000000000000000..287f23d537392510acda094e764a8c3dbbd1ef73 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/polys/tests/test_polymatrix.py @@ -0,0 +1,185 @@ +from sympy.testing.pytest import raises + +from sympy.polys.polymatrix import PolyMatrix +from sympy.polys import Poly + +from sympy.core.singleton import S +from sympy.matrices.dense import Matrix +from sympy.polys.domains.integerring import ZZ +from sympy.polys.domains.rationalfield import QQ + +from sympy.abc import x, y + + +def _test_polymatrix(): + pm1 = PolyMatrix([[Poly(x**2, x), Poly(-x, x)], [Poly(x**3, x), Poly(-1 + x, x)]]) + v1 = PolyMatrix([[1, 0], [-1, 0]], ring='ZZ[x]') + m1 = PolyMatrix([[1, 0], [-1, 0]], ring='ZZ[x]') + A = PolyMatrix([[Poly(x**2 + x, x), Poly(0, x)], \ + [Poly(x**3 - x + 1, x), Poly(0, x)]]) + B = PolyMatrix([[Poly(x**2, x), Poly(-x, x)], [Poly(-x**2, x), Poly(x, x)]]) + assert A.ring == ZZ[x] + assert isinstance(pm1*v1, PolyMatrix) + assert pm1*v1 == A + assert pm1*m1 == A + assert v1*pm1 == B + + pm2 = PolyMatrix([[Poly(x**2, x, domain='QQ'), Poly(0, x, domain='QQ'), Poly(-x**2, x, domain='QQ'), \ + Poly(x**3, x, domain='QQ'), Poly(0, x, domain='QQ'), Poly(-x**3, x, domain='QQ')]]) + assert pm2.ring == QQ[x] + v2 = PolyMatrix([1, 0, 0, 0, 0, 0], ring='ZZ[x]') + m2 = PolyMatrix([1, 0, 0, 0, 0, 0], ring='ZZ[x]') + C = PolyMatrix([[Poly(x**2, x, domain='QQ')]]) + assert pm2*v2 == C + assert pm2*m2 == C + + pm3 = PolyMatrix([[Poly(x**2, x), S.One]], ring='ZZ[x]') + v3 = S.Half*pm3 + assert v3 == PolyMatrix([[Poly(S.Half*x**2, x, domain='QQ'), S.Half]], ring='QQ[x]') + assert pm3*S.Half == v3 + assert v3.ring == QQ[x] + + pm4 = PolyMatrix([[Poly(x**2, x, domain='ZZ'), Poly(-x**2, x, domain='ZZ')]]) + v4 = PolyMatrix([1, -1], ring='ZZ[x]') + assert pm4*v4 == PolyMatrix([[Poly(2*x**2, x, domain='ZZ')]]) + + assert len(PolyMatrix(ring=ZZ[x])) == 0 + assert PolyMatrix([1, 0, 0, 1], x)/(-1) == PolyMatrix([-1, 0, 0, -1], x) + + +def test_polymatrix_constructor(): + M1 = PolyMatrix([[x, y]], ring=QQ[x,y]) + assert M1.ring == QQ[x,y] + assert M1.domain == QQ + assert M1.gens == (x, y) + assert M1.shape == (1, 2) + assert M1.rows == 1 + assert M1.cols == 2 + assert len(M1) == 2 + assert list(M1) == [Poly(x, (x, y), domain=QQ), Poly(y, (x, y), domain=QQ)] + + M2 = PolyMatrix([[x, y]], ring=QQ[x][y]) + assert M2.ring == QQ[x][y] + assert M2.domain == QQ[x] + assert M2.gens == (y,) + assert M2.shape == (1, 2) + assert M2.rows == 1 + assert M2.cols == 2 + assert len(M2) == 2 + assert list(M2) == [Poly(x, (y,), domain=QQ[x]), Poly(y, (y,), domain=QQ[x])] + + assert PolyMatrix([[x, y]], y) == PolyMatrix([[x, y]], ring=ZZ.frac_field(x)[y]) + assert PolyMatrix([[x, y]], ring='ZZ[x,y]') == PolyMatrix([[x, y]], ring=ZZ[x,y]) + + assert PolyMatrix([[x, y]], (x, y)) == PolyMatrix([[x, y]], ring=QQ[x,y]) + assert PolyMatrix([[x, y]], x, y) == PolyMatrix([[x, y]], ring=QQ[x,y]) + assert PolyMatrix([x, y]) == PolyMatrix([[x], [y]], ring=QQ[x,y]) + assert PolyMatrix(1, 2, [x, y]) == PolyMatrix([[x, y]], ring=QQ[x,y]) + assert PolyMatrix(1, 2, lambda i,j: [x,y][j]) == PolyMatrix([[x, y]], ring=QQ[x,y]) + assert PolyMatrix(0, 2, [], x, y).shape == (0, 2) + assert PolyMatrix(2, 0, [], x, y).shape == (2, 0) + assert PolyMatrix([[], []], x, y).shape == (2, 0) + assert PolyMatrix(ring=QQ[x,y]) == PolyMatrix(0, 0, [], ring=QQ[x,y]) == PolyMatrix([], ring=QQ[x,y]) + raises(TypeError, lambda: PolyMatrix()) + raises(TypeError, lambda: PolyMatrix(1)) + + assert PolyMatrix([Poly(x), Poly(y)]) == PolyMatrix([[x], [y]], ring=ZZ[x,y]) + + # XXX: Maybe a bug in parallel_poly_from_expr (x lost from gens and domain): + assert PolyMatrix([Poly(y, x), 1]) == PolyMatrix([[y], [1]], ring=QQ[y]) + + +def test_polymatrix_eq(): + assert (PolyMatrix([x]) == PolyMatrix([x])) is True + assert (PolyMatrix([y]) == PolyMatrix([x])) is False + assert (PolyMatrix([x]) != PolyMatrix([x])) is False + assert (PolyMatrix([y]) != PolyMatrix([x])) is True + + assert PolyMatrix([[x, y]]) != PolyMatrix([x, y]) == PolyMatrix([[x], [y]]) + + assert PolyMatrix([x], ring=QQ[x]) != PolyMatrix([x], ring=ZZ[x]) + + assert PolyMatrix([x]) != Matrix([x]) + assert PolyMatrix([x]).to_Matrix() == Matrix([x]) + + assert PolyMatrix([1], x) == PolyMatrix([1], x) + assert PolyMatrix([1], x) != PolyMatrix([1], y) + + +def test_polymatrix_from_Matrix(): + assert PolyMatrix.from_Matrix(Matrix([1, 2]), x) == PolyMatrix([1, 2], x, ring=QQ[x]) + assert PolyMatrix.from_Matrix(Matrix([1]), ring=QQ[x]) == PolyMatrix([1], x) + pmx = PolyMatrix([1, 2], x) + pmy = PolyMatrix([1, 2], y) + assert pmx != pmy + assert pmx.set_gens(y) == pmy + + +def test_polymatrix_repr(): + assert repr(PolyMatrix([[1, 2]], x)) == 'PolyMatrix([[1, 2]], ring=QQ[x])' + assert repr(PolyMatrix(0, 2, [], x)) == 'PolyMatrix(0, 2, [], ring=QQ[x])' + + +def test_polymatrix_getitem(): + M = PolyMatrix([[1, 2], [3, 4]], x) + assert M[:, :] == M + assert M[0, :] == PolyMatrix([[1, 2]], x) + assert M[:, 0] == PolyMatrix([1, 3], x) + assert M[0, 0] == Poly(1, x, domain=QQ) + assert M[0] == Poly(1, x, domain=QQ) + assert M[:2] == [Poly(1, x, domain=QQ), Poly(2, x, domain=QQ)] + + +def test_polymatrix_arithmetic(): + M = PolyMatrix([[1, 2], [3, 4]], x) + assert M + M == PolyMatrix([[2, 4], [6, 8]], x) + assert M - M == PolyMatrix([[0, 0], [0, 0]], x) + assert -M == PolyMatrix([[-1, -2], [-3, -4]], x) + raises(TypeError, lambda: M + 1) + raises(TypeError, lambda: M - 1) + raises(TypeError, lambda: 1 + M) + raises(TypeError, lambda: 1 - M) + + assert M * M == PolyMatrix([[7, 10], [15, 22]], x) + assert 2 * M == PolyMatrix([[2, 4], [6, 8]], x) + assert M * 2 == PolyMatrix([[2, 4], [6, 8]], x) + assert S(2) * M == PolyMatrix([[2, 4], [6, 8]], x) + assert M * S(2) == PolyMatrix([[2, 4], [6, 8]], x) + raises(TypeError, lambda: [] * M) + raises(TypeError, lambda: M * []) + M2 = PolyMatrix([[1, 2]], ring=ZZ[x]) + assert S.Half * M2 == PolyMatrix([[S.Half, 1]], ring=QQ[x]) + assert M2 * S.Half == PolyMatrix([[S.Half, 1]], ring=QQ[x]) + + assert M / 2 == PolyMatrix([[S(1)/2, 1], [S(3)/2, 2]], x) + assert M / Poly(2, x) == PolyMatrix([[S(1)/2, 1], [S(3)/2, 2]], x) + raises(TypeError, lambda: M / []) + + +def test_polymatrix_manipulations(): + M1 = PolyMatrix([[1, 2], [3, 4]], x) + assert M1.transpose() == PolyMatrix([[1, 3], [2, 4]], x) + M2 = PolyMatrix([[5, 6], [7, 8]], x) + assert M1.row_join(M2) == PolyMatrix([[1, 2, 5, 6], [3, 4, 7, 8]], x) + assert M1.col_join(M2) == PolyMatrix([[1, 2], [3, 4], [5, 6], [7, 8]], x) + assert M1.applyfunc(lambda e: 2*e) == PolyMatrix([[2, 4], [6, 8]], x) + + +def test_polymatrix_ones_zeros(): + assert PolyMatrix.zeros(1, 2, x) == PolyMatrix([[0, 0]], x) + assert PolyMatrix.eye(2, x) == PolyMatrix([[1, 0], [0, 1]], x) + + +def test_polymatrix_rref(): + M = PolyMatrix([[1, 2], [3, 4]], x) + assert M.rref() == (PolyMatrix.eye(2, x), (0, 1)) + raises(ValueError, lambda: PolyMatrix([1, 2], ring=ZZ[x]).rref()) + raises(ValueError, lambda: PolyMatrix([1, x], ring=QQ[x]).rref()) + + +def test_polymatrix_nullspace(): + M = PolyMatrix([[1, 2], [3, 6]], x) + assert M.nullspace() == [PolyMatrix([-2, 1], x)] + raises(ValueError, lambda: PolyMatrix([1, 2], ring=ZZ[x]).nullspace()) + raises(ValueError, lambda: PolyMatrix([1, x], ring=QQ[x]).nullspace()) + assert M.rank() == 1 diff --git a/lib/python3.12/site-packages/sympy/polys/tests/test_polyoptions.py b/lib/python3.12/site-packages/sympy/polys/tests/test_polyoptions.py new file mode 100644 index 0000000000000000000000000000000000000000..fa2e6054bad43aef5470949180ea5c2ffdc11f30 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/polys/tests/test_polyoptions.py @@ -0,0 +1,485 @@ +"""Tests for options manager for :class:`Poly` and public API functions. """ + +from sympy.polys.polyoptions import ( + Options, Expand, Gens, Wrt, Sort, Order, Field, Greedy, Domain, + Split, Gaussian, Extension, Modulus, Symmetric, Strict, Auto, + Frac, Formal, Polys, Include, All, Gen, Symbols, Method) + +from sympy.polys.orderings import lex +from sympy.polys.domains import FF, GF, ZZ, QQ, QQ_I, RR, CC, EX + +from sympy.polys.polyerrors import OptionError, GeneratorsError + +from sympy.core.numbers import (I, Integer) +from sympy.core.symbol import Symbol +from sympy.functions.elementary.miscellaneous import sqrt +from sympy.testing.pytest import raises +from sympy.abc import x, y, z + + +def test_Options_clone(): + opt = Options((x, y, z), {'domain': 'ZZ'}) + + assert opt.gens == (x, y, z) + assert opt.domain == ZZ + assert ('order' in opt) is False + + new_opt = opt.clone({'gens': (x, y), 'order': 'lex'}) + + assert opt.gens == (x, y, z) + assert opt.domain == ZZ + assert ('order' in opt) is False + + assert new_opt.gens == (x, y) + assert new_opt.domain == ZZ + assert ('order' in new_opt) is True + + +def test_Expand_preprocess(): + assert Expand.preprocess(False) is False + assert Expand.preprocess(True) is True + + assert Expand.preprocess(0) is False + assert Expand.preprocess(1) is True + + raises(OptionError, lambda: Expand.preprocess(x)) + + +def test_Expand_postprocess(): + opt = {'expand': True} + Expand.postprocess(opt) + + assert opt == {'expand': True} + + +def test_Gens_preprocess(): + assert Gens.preprocess((None,)) == () + assert Gens.preprocess((x, y, z)) == (x, y, z) + assert Gens.preprocess(((x, y, z),)) == (x, y, z) + + a = Symbol('a', commutative=False) + + raises(GeneratorsError, lambda: Gens.preprocess((x, x, y))) + raises(GeneratorsError, lambda: Gens.preprocess((x, y, a))) + + +def test_Gens_postprocess(): + opt = {'gens': (x, y)} + Gens.postprocess(opt) + + assert opt == {'gens': (x, y)} + + +def test_Wrt_preprocess(): + assert Wrt.preprocess(x) == ['x'] + assert Wrt.preprocess('') == [] + assert Wrt.preprocess(' ') == [] + assert Wrt.preprocess('x,y') == ['x', 'y'] + assert Wrt.preprocess('x y') == ['x', 'y'] + assert Wrt.preprocess('x, y') == ['x', 'y'] + assert Wrt.preprocess('x , y') == ['x', 'y'] + assert Wrt.preprocess(' x, y') == ['x', 'y'] + assert Wrt.preprocess(' x, y') == ['x', 'y'] + assert Wrt.preprocess([x, y]) == ['x', 'y'] + + raises(OptionError, lambda: Wrt.preprocess(',')) + raises(OptionError, lambda: Wrt.preprocess(0)) + + +def test_Wrt_postprocess(): + opt = {'wrt': ['x']} + Wrt.postprocess(opt) + + assert opt == {'wrt': ['x']} + + +def test_Sort_preprocess(): + assert Sort.preprocess([x, y, z]) == ['x', 'y', 'z'] + assert Sort.preprocess((x, y, z)) == ['x', 'y', 'z'] + + assert Sort.preprocess('x > y > z') == ['x', 'y', 'z'] + assert Sort.preprocess('x>y>z') == ['x', 'y', 'z'] + + raises(OptionError, lambda: Sort.preprocess(0)) + raises(OptionError, lambda: Sort.preprocess({x, y, z})) + + +def test_Sort_postprocess(): + opt = {'sort': 'x > y'} + Sort.postprocess(opt) + + assert opt == {'sort': 'x > y'} + + +def test_Order_preprocess(): + assert Order.preprocess('lex') == lex + + +def test_Order_postprocess(): + opt = {'order': True} + Order.postprocess(opt) + + assert opt == {'order': True} + + +def test_Field_preprocess(): + assert Field.preprocess(False) is False + assert Field.preprocess(True) is True + + assert Field.preprocess(0) is False + assert Field.preprocess(1) is True + + raises(OptionError, lambda: Field.preprocess(x)) + + +def test_Field_postprocess(): + opt = {'field': True} + Field.postprocess(opt) + + assert opt == {'field': True} + + +def test_Greedy_preprocess(): + assert Greedy.preprocess(False) is False + assert Greedy.preprocess(True) is True + + assert Greedy.preprocess(0) is False + assert Greedy.preprocess(1) is True + + raises(OptionError, lambda: Greedy.preprocess(x)) + + +def test_Greedy_postprocess(): + opt = {'greedy': True} + Greedy.postprocess(opt) + + assert opt == {'greedy': True} + + +def test_Domain_preprocess(): + assert Domain.preprocess(ZZ) == ZZ + assert Domain.preprocess(QQ) == QQ + assert Domain.preprocess(EX) == EX + assert Domain.preprocess(FF(2)) == FF(2) + assert Domain.preprocess(ZZ[x, y]) == ZZ[x, y] + + assert Domain.preprocess('Z') == ZZ + assert Domain.preprocess('Q') == QQ + + assert Domain.preprocess('ZZ') == ZZ + assert Domain.preprocess('QQ') == QQ + + assert Domain.preprocess('EX') == EX + + assert Domain.preprocess('FF(23)') == FF(23) + assert Domain.preprocess('GF(23)') == GF(23) + + raises(OptionError, lambda: Domain.preprocess('Z[]')) + + assert Domain.preprocess('Z[x]') == ZZ[x] + assert Domain.preprocess('Q[x]') == QQ[x] + assert Domain.preprocess('R[x]') == RR[x] + assert Domain.preprocess('C[x]') == CC[x] + + assert Domain.preprocess('ZZ[x]') == ZZ[x] + assert Domain.preprocess('QQ[x]') == QQ[x] + assert Domain.preprocess('RR[x]') == RR[x] + assert Domain.preprocess('CC[x]') == CC[x] + + assert Domain.preprocess('Z[x,y]') == ZZ[x, y] + assert Domain.preprocess('Q[x,y]') == QQ[x, y] + assert Domain.preprocess('R[x,y]') == RR[x, y] + assert Domain.preprocess('C[x,y]') == CC[x, y] + + assert Domain.preprocess('ZZ[x,y]') == ZZ[x, y] + assert Domain.preprocess('QQ[x,y]') == QQ[x, y] + assert Domain.preprocess('RR[x,y]') == RR[x, y] + assert Domain.preprocess('CC[x,y]') == CC[x, y] + + raises(OptionError, lambda: Domain.preprocess('Z()')) + + assert Domain.preprocess('Z(x)') == ZZ.frac_field(x) + assert Domain.preprocess('Q(x)') == QQ.frac_field(x) + + assert Domain.preprocess('ZZ(x)') == ZZ.frac_field(x) + assert Domain.preprocess('QQ(x)') == QQ.frac_field(x) + + assert Domain.preprocess('Z(x,y)') == ZZ.frac_field(x, y) + assert Domain.preprocess('Q(x,y)') == QQ.frac_field(x, y) + + assert Domain.preprocess('ZZ(x,y)') == ZZ.frac_field(x, y) + assert Domain.preprocess('QQ(x,y)') == QQ.frac_field(x, y) + + assert Domain.preprocess('Q') == QQ.algebraic_field(I) + assert Domain.preprocess('QQ') == QQ.algebraic_field(I) + + assert Domain.preprocess('Q') == QQ.algebraic_field(sqrt(2), I) + assert Domain.preprocess( + 'QQ') == QQ.algebraic_field(sqrt(2), I) + + raises(OptionError, lambda: Domain.preprocess('abc')) + + +def test_Domain_postprocess(): + raises(GeneratorsError, lambda: Domain.postprocess({'gens': (x, y), + 'domain': ZZ[y, z]})) + + raises(GeneratorsError, lambda: Domain.postprocess({'gens': (), + 'domain': EX})) + raises(GeneratorsError, lambda: Domain.postprocess({'domain': EX})) + + +def test_Split_preprocess(): + assert Split.preprocess(False) is False + assert Split.preprocess(True) is True + + assert Split.preprocess(0) is False + assert Split.preprocess(1) is True + + raises(OptionError, lambda: Split.preprocess(x)) + + +def test_Split_postprocess(): + raises(NotImplementedError, lambda: Split.postprocess({'split': True})) + + +def test_Gaussian_preprocess(): + assert Gaussian.preprocess(False) is False + assert Gaussian.preprocess(True) is True + + assert Gaussian.preprocess(0) is False + assert Gaussian.preprocess(1) is True + + raises(OptionError, lambda: Gaussian.preprocess(x)) + + +def test_Gaussian_postprocess(): + opt = {'gaussian': True} + Gaussian.postprocess(opt) + + assert opt == { + 'gaussian': True, + 'domain': QQ_I, + } + + +def test_Extension_preprocess(): + assert Extension.preprocess(True) is True + assert Extension.preprocess(1) is True + + assert Extension.preprocess([]) is None + + assert Extension.preprocess(sqrt(2)) == {sqrt(2)} + assert Extension.preprocess([sqrt(2)]) == {sqrt(2)} + + assert Extension.preprocess([sqrt(2), I]) == {sqrt(2), I} + + raises(OptionError, lambda: Extension.preprocess(False)) + raises(OptionError, lambda: Extension.preprocess(0)) + + +def test_Extension_postprocess(): + opt = {'extension': {sqrt(2)}} + Extension.postprocess(opt) + + assert opt == { + 'extension': {sqrt(2)}, + 'domain': QQ.algebraic_field(sqrt(2)), + } + + opt = {'extension': True} + Extension.postprocess(opt) + + assert opt == {'extension': True} + + +def test_Modulus_preprocess(): + assert Modulus.preprocess(23) == 23 + assert Modulus.preprocess(Integer(23)) == 23 + + raises(OptionError, lambda: Modulus.preprocess(0)) + raises(OptionError, lambda: Modulus.preprocess(x)) + + +def test_Modulus_postprocess(): + opt = {'modulus': 5} + Modulus.postprocess(opt) + + assert opt == { + 'modulus': 5, + 'domain': FF(5), + } + + opt = {'modulus': 5, 'symmetric': False} + Modulus.postprocess(opt) + + assert opt == { + 'modulus': 5, + 'domain': FF(5, False), + 'symmetric': False, + } + + +def test_Symmetric_preprocess(): + assert Symmetric.preprocess(False) is False + assert Symmetric.preprocess(True) is True + + assert Symmetric.preprocess(0) is False + assert Symmetric.preprocess(1) is True + + raises(OptionError, lambda: Symmetric.preprocess(x)) + + +def test_Symmetric_postprocess(): + opt = {'symmetric': True} + Symmetric.postprocess(opt) + + assert opt == {'symmetric': True} + + +def test_Strict_preprocess(): + assert Strict.preprocess(False) is False + assert Strict.preprocess(True) is True + + assert Strict.preprocess(0) is False + assert Strict.preprocess(1) is True + + raises(OptionError, lambda: Strict.preprocess(x)) + + +def test_Strict_postprocess(): + opt = {'strict': True} + Strict.postprocess(opt) + + assert opt == {'strict': True} + + +def test_Auto_preprocess(): + assert Auto.preprocess(False) is False + assert Auto.preprocess(True) is True + + assert Auto.preprocess(0) is False + assert Auto.preprocess(1) is True + + raises(OptionError, lambda: Auto.preprocess(x)) + + +def test_Auto_postprocess(): + opt = {'auto': True} + Auto.postprocess(opt) + + assert opt == {'auto': True} + + +def test_Frac_preprocess(): + assert Frac.preprocess(False) is False + assert Frac.preprocess(True) is True + + assert Frac.preprocess(0) is False + assert Frac.preprocess(1) is True + + raises(OptionError, lambda: Frac.preprocess(x)) + + +def test_Frac_postprocess(): + opt = {'frac': True} + Frac.postprocess(opt) + + assert opt == {'frac': True} + + +def test_Formal_preprocess(): + assert Formal.preprocess(False) is False + assert Formal.preprocess(True) is True + + assert Formal.preprocess(0) is False + assert Formal.preprocess(1) is True + + raises(OptionError, lambda: Formal.preprocess(x)) + + +def test_Formal_postprocess(): + opt = {'formal': True} + Formal.postprocess(opt) + + assert opt == {'formal': True} + + +def test_Polys_preprocess(): + assert Polys.preprocess(False) is False + assert Polys.preprocess(True) is True + + assert Polys.preprocess(0) is False + assert Polys.preprocess(1) is True + + raises(OptionError, lambda: Polys.preprocess(x)) + + +def test_Polys_postprocess(): + opt = {'polys': True} + Polys.postprocess(opt) + + assert opt == {'polys': True} + + +def test_Include_preprocess(): + assert Include.preprocess(False) is False + assert Include.preprocess(True) is True + + assert Include.preprocess(0) is False + assert Include.preprocess(1) is True + + raises(OptionError, lambda: Include.preprocess(x)) + + +def test_Include_postprocess(): + opt = {'include': True} + Include.postprocess(opt) + + assert opt == {'include': True} + + +def test_All_preprocess(): + assert All.preprocess(False) is False + assert All.preprocess(True) is True + + assert All.preprocess(0) is False + assert All.preprocess(1) is True + + raises(OptionError, lambda: All.preprocess(x)) + + +def test_All_postprocess(): + opt = {'all': True} + All.postprocess(opt) + + assert opt == {'all': True} + + +def test_Gen_postprocess(): + opt = {'gen': x} + Gen.postprocess(opt) + + assert opt == {'gen': x} + + +def test_Symbols_preprocess(): + raises(OptionError, lambda: Symbols.preprocess(x)) + + +def test_Symbols_postprocess(): + opt = {'symbols': [x, y, z]} + Symbols.postprocess(opt) + + assert opt == {'symbols': [x, y, z]} + + +def test_Method_preprocess(): + raises(OptionError, lambda: Method.preprocess(10)) + + +def test_Method_postprocess(): + opt = {'method': 'f5b'} + Method.postprocess(opt) + + assert opt == {'method': 'f5b'} diff --git a/lib/python3.12/site-packages/sympy/polys/tests/test_polyroots.py b/lib/python3.12/site-packages/sympy/polys/tests/test_polyroots.py new file mode 100644 index 0000000000000000000000000000000000000000..7f96b1930f6789ce3150ae2c920ba7d9faa68791 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/polys/tests/test_polyroots.py @@ -0,0 +1,758 @@ +"""Tests for algorithms for computing symbolic roots of polynomials. """ + +from sympy.core.numbers import (I, Rational, pi) +from sympy.core.singleton import S +from sympy.core.symbol import (Symbol, Wild, symbols) +from sympy.functions.elementary.complexes import (conjugate, im, re) +from sympy.functions.elementary.exponential import exp +from sympy.functions.elementary.miscellaneous import (root, sqrt) +from sympy.functions.elementary.piecewise import Piecewise +from sympy.functions.elementary.trigonometric import (acos, cos, sin) +from sympy.polys.domains.integerring import ZZ +from sympy.sets.sets import Interval +from sympy.simplify.powsimp import powsimp + +from sympy.polys import Poly, cyclotomic_poly, intervals, nroots, rootof + +from sympy.polys.polyroots import (root_factors, roots_linear, + roots_quadratic, roots_cubic, roots_quartic, roots_quintic, + roots_cyclotomic, roots_binomial, preprocess_roots, roots) + +from sympy.polys.orthopolys import legendre_poly +from sympy.polys.polyerrors import PolynomialError, \ + UnsolvableFactorError +from sympy.polys.polyutils import _nsort + +from sympy.testing.pytest import raises, slow +from sympy.core.random import verify_numerically +import mpmath +from itertools import product + + + +a, b, c, d, e, q, t, x, y, z = symbols('a,b,c,d,e,q,t,x,y,z') + + +def _check(roots): + # this is the desired invariant for roots returned + # by all_roots. It is trivially true for linear + # polynomials. + nreal = sum(1 if i.is_real else 0 for i in roots) + assert sorted(roots[:nreal]) == list(roots[:nreal]) + for ix in range(nreal, len(roots), 2): + if not ( + roots[ix + 1] == roots[ix] or + roots[ix + 1] == conjugate(roots[ix])): + return False + return True + + +def test_roots_linear(): + assert roots_linear(Poly(2*x + 1, x)) == [Rational(-1, 2)] + + +def test_roots_quadratic(): + assert roots_quadratic(Poly(2*x**2, x)) == [0, 0] + assert roots_quadratic(Poly(2*x**2 + 3*x, x)) == [Rational(-3, 2), 0] + assert roots_quadratic(Poly(2*x**2 + 3, x)) == [-I*sqrt(6)/2, I*sqrt(6)/2] + assert roots_quadratic(Poly(2*x**2 + 4*x + 3, x)) == [-1 - I*sqrt(2)/2, -1 + I*sqrt(2)/2] + _check(Poly(2*x**2 + 4*x + 3, x).all_roots()) + + f = x**2 + (2*a*e + 2*c*e)/(a - c)*x + (d - b + a*e**2 - c*e**2)/(a - c) + assert roots_quadratic(Poly(f, x)) == \ + [-e*(a + c)/(a - c) - sqrt(a*b + c*d - a*d - b*c + 4*a*c*e**2)/(a - c), + -e*(a + c)/(a - c) + sqrt(a*b + c*d - a*d - b*c + 4*a*c*e**2)/(a - c)] + + # check for simplification + f = Poly(y*x**2 - 2*x - 2*y, x) + assert roots_quadratic(f) == \ + [-sqrt(2*y**2 + 1)/y + 1/y, sqrt(2*y**2 + 1)/y + 1/y] + f = Poly(x**2 + (-y**2 - 2)*x + y**2 + 1, x) + assert roots_quadratic(f) == \ + [1,y**2 + 1] + + f = Poly(sqrt(2)*x**2 - 1, x) + r = roots_quadratic(f) + assert r == _nsort(r) + + # issue 8255 + f = Poly(-24*x**2 - 180*x + 264) + assert [w.n(2) for w in f.all_roots(radicals=True)] == \ + [w.n(2) for w in f.all_roots(radicals=False)] + for _a, _b, _c in product((-2, 2), (-2, 2), (0, -1)): + f = Poly(_a*x**2 + _b*x + _c) + roots = roots_quadratic(f) + assert roots == _nsort(roots) + + +def test_issue_7724(): + eq = Poly(x**4*I + x**2 + I, x) + assert roots(eq) == { + sqrt(I/2 + sqrt(5)*I/2): 1, + sqrt(-sqrt(5)*I/2 + I/2): 1, + -sqrt(I/2 + sqrt(5)*I/2): 1, + -sqrt(-sqrt(5)*I/2 + I/2): 1} + + +def test_issue_8438(): + p = Poly([1, y, -2, -3], x).as_expr() + roots = roots_cubic(Poly(p, x), x) + z = Rational(-3, 2) - I*7/2 # this will fail in code given in commit msg + post = [r.subs(y, z) for r in roots] + assert set(post) == \ + set(roots_cubic(Poly(p.subs(y, z), x))) + # /!\ if p is not made an expression, this is *very* slow + assert all(p.subs({y: z, x: i}).n(2, chop=True) == 0 for i in post) + + +def test_issue_8285(): + roots = (Poly(4*x**8 - 1, x)*Poly(x**2 + 1)).all_roots() + assert _check(roots) + f = Poly(x**4 + 5*x**2 + 6, x) + ro = [rootof(f, i) for i in range(4)] + roots = Poly(x**4 + 5*x**2 + 6, x).all_roots() + assert roots == ro + assert _check(roots) + # more than 2 complex roots from which to identify the + # imaginary ones + roots = Poly(2*x**8 - 1).all_roots() + assert _check(roots) + assert len(Poly(2*x**10 - 1).all_roots()) == 10 # doesn't fail + + +def test_issue_8289(): + roots = (Poly(x**2 + 2)*Poly(x**4 + 2)).all_roots() + assert _check(roots) + roots = Poly(x**6 + 3*x**3 + 2, x).all_roots() + assert _check(roots) + roots = Poly(x**6 - x + 1).all_roots() + assert _check(roots) + # all imaginary roots with multiplicity of 2 + roots = Poly(x**4 + 4*x**2 + 4, x).all_roots() + assert _check(roots) + + +def test_issue_14291(): + assert Poly(((x - 1)**2 + 1)*((x - 1)**2 + 2)*(x - 1) + ).all_roots() == [1, 1 - I, 1 + I, 1 - sqrt(2)*I, 1 + sqrt(2)*I] + p = x**4 + 10*x**2 + 1 + ans = [rootof(p, i) for i in range(4)] + assert Poly(p).all_roots() == ans + _check(ans) + + +def test_issue_13340(): + eq = Poly(y**3 + exp(x)*y + x, y, domain='EX') + roots_d = roots(eq) + assert len(roots_d) == 3 + + +def test_issue_14522(): + eq = Poly(x**4 + x**3*(16 + 32*I) + x**2*(-285 + 386*I) + x*(-2824 - 448*I) - 2058 - 6053*I, x) + roots_eq = roots(eq) + assert all(eq(r) == 0 for r in roots_eq) + + +def test_issue_15076(): + sol = roots_quartic(Poly(t**4 - 6*t**2 + t/x - 3, t)) + assert sol[0].has(x) + + +def test_issue_16589(): + eq = Poly(x**4 - 8*sqrt(2)*x**3 + 4*x**3 - 64*sqrt(2)*x**2 + 1024*x, x) + roots_eq = roots(eq) + assert 0 in roots_eq + + +def test_roots_cubic(): + assert roots_cubic(Poly(2*x**3, x)) == [0, 0, 0] + assert roots_cubic(Poly(x**3 - 3*x**2 + 3*x - 1, x)) == [1, 1, 1] + + # valid for arbitrary y (issue 21263) + r = root(y, 3) + assert roots_cubic(Poly(x**3 - y, x)) == [r, + r*(-S.Half + sqrt(3)*I/2), + r*(-S.Half - sqrt(3)*I/2)] + # simpler form when y is negative + assert roots_cubic(Poly(x**3 - -1, x)) == \ + [-1, S.Half - I*sqrt(3)/2, S.Half + I*sqrt(3)/2] + assert roots_cubic(Poly(2*x**3 - 3*x**2 - 3*x - 1, x))[0] == \ + S.Half + 3**Rational(1, 3)/2 + 3**Rational(2, 3)/2 + eq = -x**3 + 2*x**2 + 3*x - 2 + assert roots(eq, trig=True, multiple=True) == \ + roots_cubic(Poly(eq, x), trig=True) == [ + Rational(2, 3) + 2*sqrt(13)*cos(acos(8*sqrt(13)/169)/3)/3, + -2*sqrt(13)*sin(-acos(8*sqrt(13)/169)/3 + pi/6)/3 + Rational(2, 3), + -2*sqrt(13)*cos(-acos(8*sqrt(13)/169)/3 + pi/3)/3 + Rational(2, 3), + ] + + +def test_roots_quartic(): + assert roots_quartic(Poly(x**4, x)) == [0, 0, 0, 0] + assert roots_quartic(Poly(x**4 + x**3, x)) in [ + [-1, 0, 0, 0], + [0, -1, 0, 0], + [0, 0, -1, 0], + [0, 0, 0, -1] + ] + assert roots_quartic(Poly(x**4 - x**3, x)) in [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [0, 0, 0, 1] + ] + + lhs = roots_quartic(Poly(x**4 + x, x)) + rhs = [S.Half + I*sqrt(3)/2, S.Half - I*sqrt(3)/2, S.Zero, -S.One] + + assert sorted(lhs, key=hash) == sorted(rhs, key=hash) + + # test of all branches of roots quartic + for i, (a, b, c, d) in enumerate([(1, 2, 3, 0), + (3, -7, -9, 9), + (1, 2, 3, 4), + (1, 2, 3, 4), + (-7, -3, 3, -6), + (-3, 5, -6, -4), + (6, -5, -10, -3)]): + if i == 2: + c = -a*(a**2/S(8) - b/S(2)) + elif i == 3: + d = a*(a*(a**2*Rational(3, 256) - b/S(16)) + c/S(4)) + eq = x**4 + a*x**3 + b*x**2 + c*x + d + ans = roots_quartic(Poly(eq, x)) + assert all(eq.subs(x, ai).n(chop=True) == 0 for ai in ans) + + # not all symbolic quartics are unresolvable + eq = Poly(q*x + q/4 + x**4 + x**3 + 2*x**2 - Rational(1, 3), x) + sol = roots_quartic(eq) + assert all(verify_numerically(eq.subs(x, i), 0) for i in sol) + z = symbols('z', negative=True) + eq = x**4 + 2*x**3 + 3*x**2 + x*(z + 11) + 5 + zans = roots_quartic(Poly(eq, x)) + assert all(verify_numerically(eq.subs(((x, i), (z, -1))), 0) for i in zans) + # but some are (see also issue 4989) + # it's ok if the solution is not Piecewise, but the tests below should pass + eq = Poly(y*x**4 + x**3 - x + z, x) + ans = roots_quartic(eq) + assert all(type(i) == Piecewise for i in ans) + reps = ( + {"y": Rational(-1, 3), "z": Rational(-1, 4)}, # 4 real + {"y": Rational(-1, 3), "z": Rational(-1, 2)}, # 2 real + {"y": Rational(-1, 3), "z": -2}) # 0 real + for rep in reps: + sol = roots_quartic(Poly(eq.subs(rep), x)) + assert all(verify_numerically(w.subs(rep) - s, 0) for w, s in zip(ans, sol)) + + +def test_issue_21287(): + assert not any(isinstance(i, Piecewise) for i in roots_quartic( + Poly(x**4 - x**2*(3 + 5*I) + 2*x*(-1 + I) - 1 + 3*I, x))) + + +def test_roots_quintic(): + eqs = (x**5 - 2, + (x/2 + 1)**5 - 5*(x/2 + 1) + 12, + x**5 - 110*x**3 - 55*x**2 + 2310*x + 979) + for eq in eqs: + roots = roots_quintic(Poly(eq)) + assert len(roots) == 5 + assert all(eq.subs(x, r.n(10)).n(chop = 1e-5) == 0 for r in roots) + + +def test_roots_cyclotomic(): + assert roots_cyclotomic(cyclotomic_poly(1, x, polys=True)) == [1] + assert roots_cyclotomic(cyclotomic_poly(2, x, polys=True)) == [-1] + assert roots_cyclotomic(cyclotomic_poly( + 3, x, polys=True)) == [Rational(-1, 2) - I*sqrt(3)/2, Rational(-1, 2) + I*sqrt(3)/2] + assert roots_cyclotomic(cyclotomic_poly(4, x, polys=True)) == [-I, I] + assert roots_cyclotomic(cyclotomic_poly( + 6, x, polys=True)) == [S.Half - I*sqrt(3)/2, S.Half + I*sqrt(3)/2] + + assert roots_cyclotomic(cyclotomic_poly(7, x, polys=True)) == [ + -cos(pi/7) - I*sin(pi/7), + -cos(pi/7) + I*sin(pi/7), + -cos(pi*Rational(3, 7)) - I*sin(pi*Rational(3, 7)), + -cos(pi*Rational(3, 7)) + I*sin(pi*Rational(3, 7)), + cos(pi*Rational(2, 7)) - I*sin(pi*Rational(2, 7)), + cos(pi*Rational(2, 7)) + I*sin(pi*Rational(2, 7)), + ] + + assert roots_cyclotomic(cyclotomic_poly(8, x, polys=True)) == [ + -sqrt(2)/2 - I*sqrt(2)/2, + -sqrt(2)/2 + I*sqrt(2)/2, + sqrt(2)/2 - I*sqrt(2)/2, + sqrt(2)/2 + I*sqrt(2)/2, + ] + + assert roots_cyclotomic(cyclotomic_poly(12, x, polys=True)) == [ + -sqrt(3)/2 - I/2, + -sqrt(3)/2 + I/2, + sqrt(3)/2 - I/2, + sqrt(3)/2 + I/2, + ] + + assert roots_cyclotomic( + cyclotomic_poly(1, x, polys=True), factor=True) == [1] + assert roots_cyclotomic( + cyclotomic_poly(2, x, polys=True), factor=True) == [-1] + + assert roots_cyclotomic(cyclotomic_poly(3, x, polys=True), factor=True) == \ + [-root(-1, 3), -1 + root(-1, 3)] + assert roots_cyclotomic(cyclotomic_poly(4, x, polys=True), factor=True) == \ + [-I, I] + assert roots_cyclotomic(cyclotomic_poly(5, x, polys=True), factor=True) == \ + [-root(-1, 5), -root(-1, 5)**3, root(-1, 5)**2, -1 - root(-1, 5)**2 + root(-1, 5) + root(-1, 5)**3] + + assert roots_cyclotomic(cyclotomic_poly(6, x, polys=True), factor=True) == \ + [1 - root(-1, 3), root(-1, 3)] + + +def test_roots_binomial(): + assert roots_binomial(Poly(5*x, x)) == [0] + assert roots_binomial(Poly(5*x**4, x)) == [0, 0, 0, 0] + assert roots_binomial(Poly(5*x + 2, x)) == [Rational(-2, 5)] + + A = 10**Rational(3, 4)/10 + + assert roots_binomial(Poly(5*x**4 + 2, x)) == \ + [-A - A*I, -A + A*I, A - A*I, A + A*I] + _check(roots_binomial(Poly(x**8 - 2))) + + a1 = Symbol('a1', nonnegative=True) + b1 = Symbol('b1', nonnegative=True) + + r0 = roots_quadratic(Poly(a1*x**2 + b1, x)) + r1 = roots_binomial(Poly(a1*x**2 + b1, x)) + + assert powsimp(r0[0]) == powsimp(r1[0]) + assert powsimp(r0[1]) == powsimp(r1[1]) + for a, b, s, n in product((1, 2), (1, 2), (-1, 1), (2, 3, 4, 5)): + if a == b and a != 1: # a == b == 1 is sufficient + continue + p = Poly(a*x**n + s*b) + ans = roots_binomial(p) + assert ans == _nsort(ans) + + # issue 8813 + assert roots(Poly(2*x**3 - 16*y**3, x)) == { + 2*y*(Rational(-1, 2) - sqrt(3)*I/2): 1, + 2*y: 1, + 2*y*(Rational(-1, 2) + sqrt(3)*I/2): 1} + + +def test_roots_preprocessing(): + f = a*y*x**2 + y - b + + coeff, poly = preprocess_roots(Poly(f, x)) + + assert coeff == 1 + assert poly == Poly(a*y*x**2 + y - b, x) + + f = c**3*x**3 + c**2*x**2 + c*x + a + + coeff, poly = preprocess_roots(Poly(f, x)) + + assert coeff == 1/c + assert poly == Poly(x**3 + x**2 + x + a, x) + + f = c**3*x**3 + c**2*x**2 + a + + coeff, poly = preprocess_roots(Poly(f, x)) + + assert coeff == 1/c + assert poly == Poly(x**3 + x**2 + a, x) + + f = c**3*x**3 + c*x + a + + coeff, poly = preprocess_roots(Poly(f, x)) + + assert coeff == 1/c + assert poly == Poly(x**3 + x + a, x) + + f = c**3*x**3 + a + + coeff, poly = preprocess_roots(Poly(f, x)) + + assert coeff == 1/c + assert poly == Poly(x**3 + a, x) + + E, F, J, L = symbols("E,F,J,L") + + f = -21601054687500000000*E**8*J**8/L**16 + \ + 508232812500000000*F*x*E**7*J**7/L**14 - \ + 4269543750000000*E**6*F**2*J**6*x**2/L**12 + \ + 16194716250000*E**5*F**3*J**5*x**3/L**10 - \ + 27633173750*E**4*F**4*J**4*x**4/L**8 + \ + 14840215*E**3*F**5*J**3*x**5/L**6 + \ + 54794*E**2*F**6*J**2*x**6/(5*L**4) - \ + 1153*E*J*F**7*x**7/(80*L**2) + \ + 633*F**8*x**8/160000 + + coeff, poly = preprocess_roots(Poly(f, x)) + + assert coeff == 20*E*J/(F*L**2) + assert poly == 633*x**8 - 115300*x**7 + 4383520*x**6 + 296804300*x**5 - 27633173750*x**4 + \ + 809735812500*x**3 - 10673859375000*x**2 + 63529101562500*x - 135006591796875 + + f = Poly(-y**2 + x**2*exp(x), y, domain=ZZ[x, exp(x)]) + g = Poly(-y**2 + exp(x), y, domain=ZZ[exp(x)]) + + assert preprocess_roots(f) == (x, g) + + +def test_roots0(): + assert roots(1, x) == {} + assert roots(x, x) == {S.Zero: 1} + assert roots(x**9, x) == {S.Zero: 9} + assert roots(((x - 2)*(x + 3)*(x - 4)).expand(), x) == {-S(3): 1, S(2): 1, S(4): 1} + + assert roots(2*x + 1, x) == {Rational(-1, 2): 1} + assert roots((2*x + 1)**2, x) == {Rational(-1, 2): 2} + assert roots((2*x + 1)**5, x) == {Rational(-1, 2): 5} + assert roots((2*x + 1)**10, x) == {Rational(-1, 2): 10} + + assert roots(x**4 - 1, x) == {I: 1, S.One: 1, -S.One: 1, -I: 1} + assert roots((x**4 - 1)**2, x) == {I: 2, S.One: 2, -S.One: 2, -I: 2} + + assert roots(((2*x - 3)**2).expand(), x) == {Rational( 3, 2): 2} + assert roots(((2*x + 3)**2).expand(), x) == {Rational(-3, 2): 2} + + assert roots(((2*x - 3)**3).expand(), x) == {Rational( 3, 2): 3} + assert roots(((2*x + 3)**3).expand(), x) == {Rational(-3, 2): 3} + + assert roots(((2*x - 3)**5).expand(), x) == {Rational( 3, 2): 5} + assert roots(((2*x + 3)**5).expand(), x) == {Rational(-3, 2): 5} + + assert roots(((a*x - b)**5).expand(), x) == { b/a: 5} + assert roots(((a*x + b)**5).expand(), x) == {-b/a: 5} + + assert roots(x**2 + (-a - 1)*x + a, x) == {a: 1, S.One: 1} + + assert roots(x**4 - 2*x**2 + 1, x) == {S.One: 2, S.NegativeOne: 2} + + assert roots(x**6 - 4*x**4 + 4*x**3 - x**2, x) == \ + {S.One: 2, -1 - sqrt(2): 1, S.Zero: 2, -1 + sqrt(2): 1} + + assert roots(x**8 - 1, x) == { + sqrt(2)/2 + I*sqrt(2)/2: 1, + sqrt(2)/2 - I*sqrt(2)/2: 1, + -sqrt(2)/2 + I*sqrt(2)/2: 1, + -sqrt(2)/2 - I*sqrt(2)/2: 1, + S.One: 1, -S.One: 1, I: 1, -I: 1 + } + + f = -2016*x**2 - 5616*x**3 - 2056*x**4 + 3324*x**5 + 2176*x**6 - \ + 224*x**7 - 384*x**8 - 64*x**9 + + assert roots(f) == {S.Zero: 2, -S(2): 2, S(2): 1, Rational(-7, 2): 1, + Rational(-3, 2): 1, Rational(-1, 2): 1, Rational(3, 2): 1} + + assert roots((a + b + c)*x - (a + b + c + d), x) == {(a + b + c + d)/(a + b + c): 1} + + assert roots(x**3 + x**2 - x + 1, x, cubics=False) == {} + assert roots(((x - 2)*( + x + 3)*(x - 4)).expand(), x, cubics=False) == {-S(3): 1, S(2): 1, S(4): 1} + assert roots(((x - 2)*(x + 3)*(x - 4)*(x - 5)).expand(), x, cubics=False) == \ + {-S(3): 1, S(2): 1, S(4): 1, S(5): 1} + assert roots(x**3 + 2*x**2 + 4*x + 8, x) == {-S(2): 1, -2*I: 1, 2*I: 1} + assert roots(x**3 + 2*x**2 + 4*x + 8, x, cubics=True) == \ + {-2*I: 1, 2*I: 1, -S(2): 1} + assert roots((x**2 - x)*(x**3 + 2*x**2 + 4*x + 8), x ) == \ + {S.One: 1, S.Zero: 1, -S(2): 1, -2*I: 1, 2*I: 1} + + r1_2, r1_3 = S.Half, Rational(1, 3) + + x0 = (3*sqrt(33) + 19)**r1_3 + x1 = 4/x0/3 + x2 = x0/3 + x3 = sqrt(3)*I/2 + x4 = x3 - r1_2 + x5 = -x3 - r1_2 + assert roots(x**3 + x**2 - x + 1, x, cubics=True) == { + -x1 - x2 - r1_3: 1, + -x1/x4 - x2*x4 - r1_3: 1, + -x1/x5 - x2*x5 - r1_3: 1, + } + + f = (x**2 + 2*x + 3).subs(x, 2*x**2 + 3*x).subs(x, 5*x - 4) + + r13_20, r1_20 = [ Rational(*r) + for r in ((13, 20), (1, 20)) ] + + s2 = sqrt(2) + assert roots(f, x) == { + r13_20 + r1_20*sqrt(1 - 8*I*s2): 1, + r13_20 - r1_20*sqrt(1 - 8*I*s2): 1, + r13_20 + r1_20*sqrt(1 + 8*I*s2): 1, + r13_20 - r1_20*sqrt(1 + 8*I*s2): 1, + } + + f = x**4 + x**3 + x**2 + x + 1 + + r1_4, r1_8, r5_8 = [ Rational(*r) for r in ((1, 4), (1, 8), (5, 8)) ] + + assert roots(f, x) == { + -r1_4 + r1_4*5**r1_2 + I*(r5_8 + r1_8*5**r1_2)**r1_2: 1, + -r1_4 + r1_4*5**r1_2 - I*(r5_8 + r1_8*5**r1_2)**r1_2: 1, + -r1_4 - r1_4*5**r1_2 + I*(r5_8 - r1_8*5**r1_2)**r1_2: 1, + -r1_4 - r1_4*5**r1_2 - I*(r5_8 - r1_8*5**r1_2)**r1_2: 1, + } + + f = z**3 + (-2 - y)*z**2 + (1 + 2*y - 2*x**2)*z - y + 2*x**2 + + assert roots(f, z) == { + S.One: 1, + S.Half + S.Half*y + S.Half*sqrt(1 - 2*y + y**2 + 8*x**2): 1, + S.Half + S.Half*y - S.Half*sqrt(1 - 2*y + y**2 + 8*x**2): 1, + } + + assert roots(a*b*c*x**3 + 2*x**2 + 4*x + 8, x, cubics=False) == {} + assert roots(a*b*c*x**3 + 2*x**2 + 4*x + 8, x, cubics=True) != {} + + assert roots(x**4 - 1, x, filter='Z') == {S.One: 1, -S.One: 1} + assert roots(x**4 - 1, x, filter='I') == {I: 1, -I: 1} + + assert roots((x - 1)*(x + 1), x) == {S.One: 1, -S.One: 1} + assert roots( + (x - 1)*(x + 1), x, predicate=lambda r: r.is_positive) == {S.One: 1} + + assert roots(x**4 - 1, x, filter='Z', multiple=True) == [-S.One, S.One] + assert roots(x**4 - 1, x, filter='I', multiple=True) == [I, -I] + + ar, br = symbols('a, b', real=True) + p = x**2*(ar-br)**2 + 2*x*(br-ar) + 1 + assert roots(p, x, filter='R') == {1/(ar - br): 2} + + assert roots(x**3, x, multiple=True) == [S.Zero, S.Zero, S.Zero] + assert roots(1234, x, multiple=True) == [] + + f = x**6 - x**5 + x**4 - x**3 + x**2 - x + 1 + + assert roots(f) == { + -I*sin(pi/7) + cos(pi/7): 1, + -I*sin(pi*Rational(2, 7)) - cos(pi*Rational(2, 7)): 1, + -I*sin(pi*Rational(3, 7)) + cos(pi*Rational(3, 7)): 1, + I*sin(pi/7) + cos(pi/7): 1, + I*sin(pi*Rational(2, 7)) - cos(pi*Rational(2, 7)): 1, + I*sin(pi*Rational(3, 7)) + cos(pi*Rational(3, 7)): 1, + } + + g = ((x**2 + 1)*f**2).expand() + + assert roots(g) == { + -I*sin(pi/7) + cos(pi/7): 2, + -I*sin(pi*Rational(2, 7)) - cos(pi*Rational(2, 7)): 2, + -I*sin(pi*Rational(3, 7)) + cos(pi*Rational(3, 7)): 2, + I*sin(pi/7) + cos(pi/7): 2, + I*sin(pi*Rational(2, 7)) - cos(pi*Rational(2, 7)): 2, + I*sin(pi*Rational(3, 7)) + cos(pi*Rational(3, 7)): 2, + -I: 1, I: 1, + } + + r = roots(x**3 + 40*x + 64) + real_root = [rx for rx in r if rx.is_real][0] + cr = 108 + 6*sqrt(1074) + assert real_root == -2*root(cr, 3)/3 + 20/root(cr, 3) + + eq = Poly((7 + 5*sqrt(2))*x**3 + (-6 - 4*sqrt(2))*x**2 + (-sqrt(2) - 1)*x + 2, x, domain='EX') + assert roots(eq) == {-1 + sqrt(2): 1, -2 + 2*sqrt(2): 1, -sqrt(2) + 1: 1} + + eq = Poly(41*x**5 + 29*sqrt(2)*x**5 - 153*x**4 - 108*sqrt(2)*x**4 + + 175*x**3 + 125*sqrt(2)*x**3 - 45*x**2 - 30*sqrt(2)*x**2 - 26*sqrt(2)*x - + 26*x + 24, x, domain='EX') + assert roots(eq) == {-sqrt(2) + 1: 1, -2 + 2*sqrt(2): 1, -1 + sqrt(2): 1, + -4 + 4*sqrt(2): 1, -3 + 3*sqrt(2): 1} + + eq = Poly(x**3 - 2*x**2 + 6*sqrt(2)*x**2 - 8*sqrt(2)*x + 23*x - 14 + + 14*sqrt(2), x, domain='EX') + assert roots(eq) == {-2*sqrt(2) + 2: 1, -2*sqrt(2) + 1: 1, -2*sqrt(2) - 1: 1} + + assert roots(Poly((x + sqrt(2))**3 - 7, x, domain='EX')) == \ + {-sqrt(2) + root(7, 3)*(-S.Half - sqrt(3)*I/2): 1, + -sqrt(2) + root(7, 3)*(-S.Half + sqrt(3)*I/2): 1, + -sqrt(2) + root(7, 3): 1} + +def test_roots_slow(): + """Just test that calculating these roots does not hang. """ + a, b, c, d, x = symbols("a,b,c,d,x") + + f1 = x**2*c + (a/b) + x*c*d - a + f2 = x**2*(a + b*(c - d)*a) + x*a*b*c/(b*d - d) + (a*d - c/d) + + assert list(roots(f1, x).values()) == [1, 1] + assert list(roots(f2, x).values()) == [1, 1] + + (zz, yy, xx, zy, zx, yx, k) = symbols("zz,yy,xx,zy,zx,yx,k") + + e1 = (zz - k)*(yy - k)*(xx - k) + zy*yx*zx + zx - zy - yx + e2 = (zz - k)*yx*yx + zx*(yy - k)*zx + zy*zy*(xx - k) + + assert list(roots(e1 - e2, k).values()) == [1, 1, 1] + + f = x**3 + 2*x**2 + 8 + R = list(roots(f).keys()) + + assert not any(i for i in [f.subs(x, ri).n(chop=True) for ri in R]) + + +def test_roots_inexact(): + R1 = roots(x**2 + x + 1, x, multiple=True) + R2 = roots(x**2 + x + 1.0, x, multiple=True) + + for r1, r2 in zip(R1, R2): + assert abs(r1 - r2) < 1e-12 + + f = x**4 + 3.0*sqrt(2.0)*x**3 - (78.0 + 24.0*sqrt(3.0))*x**2 \ + + 144.0*(2*sqrt(3.0) + 9.0) + + R1 = roots(f, multiple=True) + R2 = (-12.7530479110482, -3.85012393732929, + 4.89897948556636, 7.46155167569183) + + for r1, r2 in zip(R1, R2): + assert abs(r1 - r2) < 1e-10 + + +def test_roots_preprocessed(): + E, F, J, L = symbols("E,F,J,L") + + f = -21601054687500000000*E**8*J**8/L**16 + \ + 508232812500000000*F*x*E**7*J**7/L**14 - \ + 4269543750000000*E**6*F**2*J**6*x**2/L**12 + \ + 16194716250000*E**5*F**3*J**5*x**3/L**10 - \ + 27633173750*E**4*F**4*J**4*x**4/L**8 + \ + 14840215*E**3*F**5*J**3*x**5/L**6 + \ + 54794*E**2*F**6*J**2*x**6/(5*L**4) - \ + 1153*E*J*F**7*x**7/(80*L**2) + \ + 633*F**8*x**8/160000 + + assert roots(f, x) == {} + + R1 = roots(f.evalf(), x, multiple=True) + R2 = [-1304.88375606366, 97.1168816800648, 186.946430171876, 245.526792947065, + 503.441004174773, 791.549343830097, 1273.16678129348, 1850.10650616851] + + w = Wild('w') + p = w*E*J/(F*L**2) + + assert len(R1) == len(R2) + + for r1, r2 in zip(R1, R2): + match = r1.match(p) + assert match is not None and abs(match[w] - r2) < 1e-10 + + +def test_roots_strict(): + assert roots(x**2 - 2*x + 1, strict=False) == {1: 2} + assert roots(x**2 - 2*x + 1, strict=True) == {1: 2} + + assert roots(x**6 - 2*x**5 - x**2 + 3*x - 2, strict=False) == {2: 1} + raises(UnsolvableFactorError, lambda: roots(x**6 - 2*x**5 - x**2 + 3*x - 2, strict=True)) + + +def test_roots_mixed(): + f = -1936 - 5056*x - 7592*x**2 + 2704*x**3 - 49*x**4 + + _re, _im = intervals(f, all=True) + _nroots = nroots(f) + _sroots = roots(f, multiple=True) + + _re = [ Interval(a, b) for (a, b), _ in _re ] + _im = [ Interval(re(a), re(b))*Interval(im(a), im(b)) for (a, b), + _ in _im ] + + _intervals = _re + _im + _sroots = [ r.evalf() for r in _sroots ] + + _nroots = sorted(_nroots, key=lambda x: x.sort_key()) + _sroots = sorted(_sroots, key=lambda x: x.sort_key()) + + for _roots in (_nroots, _sroots): + for i, r in zip(_intervals, _roots): + if r.is_real: + assert r in i + else: + assert (re(r), im(r)) in i + + +def test_root_factors(): + assert root_factors(Poly(1, x)) == [Poly(1, x)] + assert root_factors(Poly(x, x)) == [Poly(x, x)] + + assert root_factors(x**2 - 1, x) == [x + 1, x - 1] + assert root_factors(x**2 - y, x) == [x - sqrt(y), x + sqrt(y)] + + assert root_factors((x**4 - 1)**2) == \ + [x + 1, x + 1, x - 1, x - 1, x - I, x - I, x + I, x + I] + + assert root_factors(Poly(x**4 - 1, x), filter='Z') == \ + [Poly(x + 1, x), Poly(x - 1, x), Poly(x**2 + 1, x)] + assert root_factors(8*x**2 + 12*x**4 + 6*x**6 + x**8, x, filter='Q') == \ + [x, x, x**6 + 6*x**4 + 12*x**2 + 8] + + +@slow +def test_nroots1(): + n = 64 + p = legendre_poly(n, x, polys=True) + + raises(mpmath.mp.NoConvergence, lambda: p.nroots(n=3, maxsteps=5)) + + roots = p.nroots(n=3) + # The order of roots matters. They are ordered from smallest to the + # largest. + assert [str(r) for r in roots] == \ + ['-0.999', '-0.996', '-0.991', '-0.983', '-0.973', '-0.961', + '-0.946', '-0.930', '-0.911', '-0.889', '-0.866', '-0.841', + '-0.813', '-0.784', '-0.753', '-0.720', '-0.685', '-0.649', + '-0.611', '-0.572', '-0.531', '-0.489', '-0.446', '-0.402', + '-0.357', '-0.311', '-0.265', '-0.217', '-0.170', '-0.121', + '-0.0730', '-0.0243', '0.0243', '0.0730', '0.121', '0.170', + '0.217', '0.265', '0.311', '0.357', '0.402', '0.446', '0.489', + '0.531', '0.572', '0.611', '0.649', '0.685', '0.720', '0.753', + '0.784', '0.813', '0.841', '0.866', '0.889', '0.911', '0.930', + '0.946', '0.961', '0.973', '0.983', '0.991', '0.996', '0.999'] + +def test_nroots2(): + p = Poly(x**5 + 3*x + 1, x) + + roots = p.nroots(n=3) + # The order of roots matters. The roots are ordered by their real + # components (if they agree, then by their imaginary components), + # with real roots appearing first. + assert [str(r) for r in roots] == \ + ['-0.332', '-0.839 - 0.944*I', '-0.839 + 0.944*I', + '1.01 - 0.937*I', '1.01 + 0.937*I'] + + roots = p.nroots(n=5) + assert [str(r) for r in roots] == \ + ['-0.33199', '-0.83907 - 0.94385*I', '-0.83907 + 0.94385*I', + '1.0051 - 0.93726*I', '1.0051 + 0.93726*I'] + + +def test_roots_composite(): + assert len(roots(Poly(y**3 + y**2*sqrt(x) + y + x, y, composite=True))) == 3 + + +def test_issue_19113(): + eq = cos(x)**3 - cos(x) + 1 + raises(PolynomialError, lambda: roots(eq)) + + +def test_issue_17454(): + assert roots([1, -3*(-4 - 4*I)**2/8 + 12*I, 0], multiple=True) == [0, 0] + + +def test_issue_20913(): + assert Poly(x + 9671406556917067856609794, x).real_roots() == [-9671406556917067856609794] + assert Poly(x**3 + 4, x).real_roots() == [-2**(S(2)/3)] + + +def test_issue_22768(): + e = Rational(1, 3) + r = (-1/a)**e*(a + 1)**(5*e) + assert roots(Poly(a*x**3 + (a + 1)**5, x)) == { + r: 1, + -r*(1 + sqrt(3)*I)/2: 1, + r*(-1 + sqrt(3)*I)/2: 1} diff --git a/lib/python3.12/site-packages/sympy/polys/tests/test_polytools.py b/lib/python3.12/site-packages/sympy/polys/tests/test_polytools.py new file mode 100644 index 0000000000000000000000000000000000000000..b64ec83f2da59227cbae52c42e4e5473488cce13 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/polys/tests/test_polytools.py @@ -0,0 +1,3771 @@ +"""Tests for user-friendly public interface to polynomial functions. """ + +import pickle + +from sympy.polys.polytools import ( + Poly, PurePoly, poly, + parallel_poly_from_expr, + degree, degree_list, + total_degree, + LC, LM, LT, + pdiv, prem, pquo, pexquo, + div, rem, quo, exquo, + half_gcdex, gcdex, invert, + subresultants, + resultant, discriminant, + terms_gcd, cofactors, + gcd, gcd_list, + lcm, lcm_list, + trunc, + monic, content, primitive, + compose, decompose, + sturm, + gff_list, gff, + sqf_norm, sqf_part, sqf_list, sqf, + factor_list, factor, + intervals, refine_root, count_roots, + all_roots, real_roots, nroots, ground_roots, + nth_power_roots_poly, + cancel, reduced, groebner, + GroebnerBasis, is_zero_dimensional, + _torational_factor_list, + to_rational_coeffs) + +from sympy.polys.polyerrors import ( + MultivariatePolynomialError, + ExactQuotientFailed, + PolificationFailed, + ComputationFailed, + UnificationFailed, + RefinementFailed, + GeneratorsNeeded, + GeneratorsError, + PolynomialError, + CoercionFailed, + DomainError, + OptionError, + FlagError) + +from sympy.polys.polyclasses import DMP + +from sympy.polys.fields import field +from sympy.polys.domains import FF, ZZ, QQ, ZZ_I, QQ_I, RR, EX +from sympy.polys.domains.realfield import RealField +from sympy.polys.domains.complexfield import ComplexField +from sympy.polys.orderings import lex, grlex, grevlex + +from sympy.combinatorics.galois import S4TransitiveSubgroups +from sympy.core.add import Add +from sympy.core.basic import _aresame +from sympy.core.containers import Tuple +from sympy.core.expr import Expr +from sympy.core.function import (Derivative, diff, expand) +from sympy.core.mul import _keep_coeff, Mul +from sympy.core.numbers import (Float, I, Integer, Rational, oo, pi) +from sympy.core.power import Pow +from sympy.core.relational import Eq +from sympy.core.singleton import S +from sympy.core.symbol import Symbol +from sympy.functions.elementary.complexes import (im, re) +from sympy.functions.elementary.exponential import exp +from sympy.functions.elementary.hyperbolic import tanh +from sympy.functions.elementary.miscellaneous import sqrt +from sympy.functions.elementary.piecewise import Piecewise +from sympy.functions.elementary.trigonometric import sin +from sympy.matrices.dense import Matrix +from sympy.matrices.expressions.matexpr import MatrixSymbol +from sympy.polys.rootoftools import rootof +from sympy.simplify.simplify import signsimp +from sympy.utilities.iterables import iterable +from sympy.utilities.exceptions import SymPyDeprecationWarning + +from sympy.testing.pytest import raises, warns_deprecated_sympy, warns + +from sympy.abc import a, b, c, d, p, q, t, w, x, y, z + + +def _epsilon_eq(a, b): + for u, v in zip(a, b): + if abs(u - v) > 1e-10: + return False + return True + + +def _strict_eq(a, b): + if type(a) == type(b): + if iterable(a): + if len(a) == len(b): + return all(_strict_eq(c, d) for c, d in zip(a, b)) + else: + return False + else: + return isinstance(a, Poly) and a.eq(b, strict=True) + else: + return False + + +def test_Poly_mixed_operations(): + p = Poly(x, x) + with warns_deprecated_sympy(): + p * exp(x) + with warns_deprecated_sympy(): + p + exp(x) + with warns_deprecated_sympy(): + p - exp(x) + + +def test_Poly_from_dict(): + K = FF(3) + + assert Poly.from_dict( + {0: 1, 1: 2}, gens=x, domain=K).rep == DMP([K(2), K(1)], K) + assert Poly.from_dict( + {0: 1, 1: 5}, gens=x, domain=K).rep == DMP([K(2), K(1)], K) + + assert Poly.from_dict( + {(0,): 1, (1,): 2}, gens=x, domain=K).rep == DMP([K(2), K(1)], K) + assert Poly.from_dict( + {(0,): 1, (1,): 5}, gens=x, domain=K).rep == DMP([K(2), K(1)], K) + + assert Poly.from_dict({(0, 0): 1, (1, 1): 2}, gens=( + x, y), domain=K).rep == DMP([[K(2), K(0)], [K(1)]], K) + + assert Poly.from_dict({0: 1, 1: 2}, gens=x).rep == DMP([ZZ(2), ZZ(1)], ZZ) + assert Poly.from_dict( + {0: 1, 1: 2}, gens=x, field=True).rep == DMP([QQ(2), QQ(1)], QQ) + + assert Poly.from_dict( + {0: 1, 1: 2}, gens=x, domain=ZZ).rep == DMP([ZZ(2), ZZ(1)], ZZ) + assert Poly.from_dict( + {0: 1, 1: 2}, gens=x, domain=QQ).rep == DMP([QQ(2), QQ(1)], QQ) + + assert Poly.from_dict( + {(0,): 1, (1,): 2}, gens=x).rep == DMP([ZZ(2), ZZ(1)], ZZ) + assert Poly.from_dict( + {(0,): 1, (1,): 2}, gens=x, field=True).rep == DMP([QQ(2), QQ(1)], QQ) + + assert Poly.from_dict( + {(0,): 1, (1,): 2}, gens=x, domain=ZZ).rep == DMP([ZZ(2), ZZ(1)], ZZ) + assert Poly.from_dict( + {(0,): 1, (1,): 2}, gens=x, domain=QQ).rep == DMP([QQ(2), QQ(1)], QQ) + + assert Poly.from_dict({(1,): sin(y)}, gens=x, composite=False) == \ + Poly(sin(y)*x, x, domain='EX') + assert Poly.from_dict({(1,): y}, gens=x, composite=False) == \ + Poly(y*x, x, domain='EX') + assert Poly.from_dict({(1, 1): 1}, gens=(x, y), composite=False) == \ + Poly(x*y, x, y, domain='ZZ') + assert Poly.from_dict({(1, 0): y}, gens=(x, z), composite=False) == \ + Poly(y*x, x, z, domain='EX') + + +def test_Poly_from_list(): + K = FF(3) + + assert Poly.from_list([2, 1], gens=x, domain=K).rep == DMP([K(2), K(1)], K) + assert Poly.from_list([5, 1], gens=x, domain=K).rep == DMP([K(2), K(1)], K) + + assert Poly.from_list([2, 1], gens=x).rep == DMP([ZZ(2), ZZ(1)], ZZ) + assert Poly.from_list([2, 1], gens=x, field=True).rep == DMP([QQ(2), QQ(1)], QQ) + + assert Poly.from_list([2, 1], gens=x, domain=ZZ).rep == DMP([ZZ(2), ZZ(1)], ZZ) + assert Poly.from_list([2, 1], gens=x, domain=QQ).rep == DMP([QQ(2), QQ(1)], QQ) + + assert Poly.from_list([0, 1.0], gens=x).rep == DMP([RR(1.0)], RR) + assert Poly.from_list([1.0, 0], gens=x).rep == DMP([RR(1.0), RR(0.0)], RR) + + raises(MultivariatePolynomialError, lambda: Poly.from_list([[]], gens=(x, y))) + + +def test_Poly_from_poly(): + f = Poly(x + 7, x, domain=ZZ) + g = Poly(x + 2, x, modulus=3) + h = Poly(x + y, x, y, domain=ZZ) + + K = FF(3) + + assert Poly.from_poly(f) == f + assert Poly.from_poly(f, domain=K).rep == DMP([K(1), K(1)], K) + assert Poly.from_poly(f, domain=ZZ).rep == DMP([ZZ(1), ZZ(7)], ZZ) + assert Poly.from_poly(f, domain=QQ).rep == DMP([QQ(1), QQ(7)], QQ) + + assert Poly.from_poly(f, gens=x) == f + assert Poly.from_poly(f, gens=x, domain=K).rep == DMP([K(1), K(1)], K) + assert Poly.from_poly(f, gens=x, domain=ZZ).rep == DMP([ZZ(1), ZZ(7)], ZZ) + assert Poly.from_poly(f, gens=x, domain=QQ).rep == DMP([QQ(1), QQ(7)], QQ) + + assert Poly.from_poly(f, gens=y) == Poly(x + 7, y, domain='ZZ[x]') + raises(CoercionFailed, lambda: Poly.from_poly(f, gens=y, domain=K)) + raises(CoercionFailed, lambda: Poly.from_poly(f, gens=y, domain=ZZ)) + raises(CoercionFailed, lambda: Poly.from_poly(f, gens=y, domain=QQ)) + + assert Poly.from_poly(f, gens=(x, y)) == Poly(x + 7, x, y, domain='ZZ') + assert Poly.from_poly( + f, gens=(x, y), domain=ZZ) == Poly(x + 7, x, y, domain='ZZ') + assert Poly.from_poly( + f, gens=(x, y), domain=QQ) == Poly(x + 7, x, y, domain='QQ') + assert Poly.from_poly( + f, gens=(x, y), modulus=3) == Poly(x + 7, x, y, domain='FF(3)') + + K = FF(2) + + assert Poly.from_poly(g) == g + assert Poly.from_poly(g, domain=ZZ).rep == DMP([ZZ(1), ZZ(-1)], ZZ) + raises(CoercionFailed, lambda: Poly.from_poly(g, domain=QQ)) + assert Poly.from_poly(g, domain=K).rep == DMP([K(1), K(0)], K) + + assert Poly.from_poly(g, gens=x) == g + assert Poly.from_poly(g, gens=x, domain=ZZ).rep == DMP([ZZ(1), ZZ(-1)], ZZ) + raises(CoercionFailed, lambda: Poly.from_poly(g, gens=x, domain=QQ)) + assert Poly.from_poly(g, gens=x, domain=K).rep == DMP([K(1), K(0)], K) + + K = FF(3) + + assert Poly.from_poly(h) == h + assert Poly.from_poly( + h, domain=ZZ).rep == DMP([[ZZ(1)], [ZZ(1), ZZ(0)]], ZZ) + assert Poly.from_poly( + h, domain=QQ).rep == DMP([[QQ(1)], [QQ(1), QQ(0)]], QQ) + assert Poly.from_poly(h, domain=K).rep == DMP([[K(1)], [K(1), K(0)]], K) + + assert Poly.from_poly(h, gens=x) == Poly(x + y, x, domain=ZZ[y]) + raises(CoercionFailed, lambda: Poly.from_poly(h, gens=x, domain=ZZ)) + assert Poly.from_poly( + h, gens=x, domain=ZZ[y]) == Poly(x + y, x, domain=ZZ[y]) + raises(CoercionFailed, lambda: Poly.from_poly(h, gens=x, domain=QQ)) + assert Poly.from_poly( + h, gens=x, domain=QQ[y]) == Poly(x + y, x, domain=QQ[y]) + raises(CoercionFailed, lambda: Poly.from_poly(h, gens=x, modulus=3)) + + assert Poly.from_poly(h, gens=y) == Poly(x + y, y, domain=ZZ[x]) + raises(CoercionFailed, lambda: Poly.from_poly(h, gens=y, domain=ZZ)) + assert Poly.from_poly( + h, gens=y, domain=ZZ[x]) == Poly(x + y, y, domain=ZZ[x]) + raises(CoercionFailed, lambda: Poly.from_poly(h, gens=y, domain=QQ)) + assert Poly.from_poly( + h, gens=y, domain=QQ[x]) == Poly(x + y, y, domain=QQ[x]) + raises(CoercionFailed, lambda: Poly.from_poly(h, gens=y, modulus=3)) + + assert Poly.from_poly(h, gens=(x, y)) == h + assert Poly.from_poly( + h, gens=(x, y), domain=ZZ).rep == DMP([[ZZ(1)], [ZZ(1), ZZ(0)]], ZZ) + assert Poly.from_poly( + h, gens=(x, y), domain=QQ).rep == DMP([[QQ(1)], [QQ(1), QQ(0)]], QQ) + assert Poly.from_poly( + h, gens=(x, y), domain=K).rep == DMP([[K(1)], [K(1), K(0)]], K) + + assert Poly.from_poly( + h, gens=(y, x)).rep == DMP([[ZZ(1)], [ZZ(1), ZZ(0)]], ZZ) + assert Poly.from_poly( + h, gens=(y, x), domain=ZZ).rep == DMP([[ZZ(1)], [ZZ(1), ZZ(0)]], ZZ) + assert Poly.from_poly( + h, gens=(y, x), domain=QQ).rep == DMP([[QQ(1)], [QQ(1), QQ(0)]], QQ) + assert Poly.from_poly( + h, gens=(y, x), domain=K).rep == DMP([[K(1)], [K(1), K(0)]], K) + + assert Poly.from_poly( + h, gens=(x, y), field=True).rep == DMP([[QQ(1)], [QQ(1), QQ(0)]], QQ) + assert Poly.from_poly( + h, gens=(x, y), field=True).rep == DMP([[QQ(1)], [QQ(1), QQ(0)]], QQ) + + +def test_Poly_from_expr(): + raises(GeneratorsNeeded, lambda: Poly.from_expr(S.Zero)) + raises(GeneratorsNeeded, lambda: Poly.from_expr(S(7))) + + F3 = FF(3) + + assert Poly.from_expr(x + 5, domain=F3).rep == DMP([F3(1), F3(2)], F3) + assert Poly.from_expr(y + 5, domain=F3).rep == DMP([F3(1), F3(2)], F3) + + assert Poly.from_expr(x + 5, x, domain=F3).rep == DMP([F3(1), F3(2)], F3) + assert Poly.from_expr(y + 5, y, domain=F3).rep == DMP([F3(1), F3(2)], F3) + + assert Poly.from_expr(x + y, domain=F3).rep == DMP([[F3(1)], [F3(1), F3(0)]], F3) + assert Poly.from_expr(x + y, x, y, domain=F3).rep == DMP([[F3(1)], [F3(1), F3(0)]], F3) + + assert Poly.from_expr(x + 5).rep == DMP([ZZ(1), ZZ(5)], ZZ) + assert Poly.from_expr(y + 5).rep == DMP([ZZ(1), ZZ(5)], ZZ) + + assert Poly.from_expr(x + 5, x).rep == DMP([ZZ(1), ZZ(5)], ZZ) + assert Poly.from_expr(y + 5, y).rep == DMP([ZZ(1), ZZ(5)], ZZ) + + assert Poly.from_expr(x + 5, domain=ZZ).rep == DMP([ZZ(1), ZZ(5)], ZZ) + assert Poly.from_expr(y + 5, domain=ZZ).rep == DMP([ZZ(1), ZZ(5)], ZZ) + + assert Poly.from_expr(x + 5, x, domain=ZZ).rep == DMP([ZZ(1), ZZ(5)], ZZ) + assert Poly.from_expr(y + 5, y, domain=ZZ).rep == DMP([ZZ(1), ZZ(5)], ZZ) + + assert Poly.from_expr(x + 5, x, y, domain=ZZ).rep == DMP([[ZZ(1)], [ZZ(5)]], ZZ) + assert Poly.from_expr(y + 5, x, y, domain=ZZ).rep == DMP([[ZZ(1), ZZ(5)]], ZZ) + + +def test_poly_from_domain_element(): + dom = ZZ[x] + assert Poly(dom(x+1), y, domain=dom).rep == DMP([dom(x+1)], dom) + dom = dom.get_field() + assert Poly(dom(x+1), y, domain=dom).rep == DMP([dom(x+1)], dom) + + dom = QQ[x] + assert Poly(dom(x+1), y, domain=dom).rep == DMP([dom(x+1)], dom) + dom = dom.get_field() + assert Poly(dom(x+1), y, domain=dom).rep == DMP([dom(x+1)], dom) + + dom = ZZ.old_poly_ring(x) + assert Poly(dom([ZZ(1), ZZ(1)]), y, domain=dom).rep == DMP([dom([ZZ(1), ZZ(1)])], dom) + dom = dom.get_field() + assert Poly(dom([ZZ(1), ZZ(1)]), y, domain=dom).rep == DMP([dom([ZZ(1), ZZ(1)])], dom) + + dom = QQ.old_poly_ring(x) + assert Poly(dom([QQ(1), QQ(1)]), y, domain=dom).rep == DMP([dom([QQ(1), QQ(1)])], dom) + dom = dom.get_field() + assert Poly(dom([QQ(1), QQ(1)]), y, domain=dom).rep == DMP([dom([QQ(1), QQ(1)])], dom) + + dom = QQ.algebraic_field(I) + assert Poly(dom([1, 1]), x, domain=dom).rep == DMP([dom([1, 1])], dom) + + +def test_Poly__new__(): + raises(GeneratorsError, lambda: Poly(x + 1, x, x)) + + raises(GeneratorsError, lambda: Poly(x + y, x, y, domain=ZZ[x])) + raises(GeneratorsError, lambda: Poly(x + y, x, y, domain=ZZ[y])) + + raises(OptionError, lambda: Poly(x, x, symmetric=True)) + raises(OptionError, lambda: Poly(x + 2, x, modulus=3, domain=QQ)) + + raises(OptionError, lambda: Poly(x + 2, x, domain=ZZ, gaussian=True)) + raises(OptionError, lambda: Poly(x + 2, x, modulus=3, gaussian=True)) + + raises(OptionError, lambda: Poly(x + 2, x, domain=ZZ, extension=[sqrt(3)])) + raises(OptionError, lambda: Poly(x + 2, x, modulus=3, extension=[sqrt(3)])) + + raises(OptionError, lambda: Poly(x + 2, x, domain=ZZ, extension=True)) + raises(OptionError, lambda: Poly(x + 2, x, modulus=3, extension=True)) + + raises(OptionError, lambda: Poly(x + 2, x, domain=ZZ, greedy=True)) + raises(OptionError, lambda: Poly(x + 2, x, domain=QQ, field=True)) + + raises(OptionError, lambda: Poly(x + 2, x, domain=ZZ, greedy=False)) + raises(OptionError, lambda: Poly(x + 2, x, domain=QQ, field=False)) + + raises(NotImplementedError, lambda: Poly(x + 1, x, modulus=3, order='grlex')) + raises(NotImplementedError, lambda: Poly(x + 1, x, order='grlex')) + + raises(GeneratorsNeeded, lambda: Poly({1: 2, 0: 1})) + raises(GeneratorsNeeded, lambda: Poly([2, 1])) + raises(GeneratorsNeeded, lambda: Poly((2, 1))) + + raises(GeneratorsNeeded, lambda: Poly(1)) + + assert Poly('x-x') == Poly(0, x) + + f = a*x**2 + b*x + c + + assert Poly({2: a, 1: b, 0: c}, x) == f + assert Poly(iter([a, b, c]), x) == f + assert Poly([a, b, c], x) == f + assert Poly((a, b, c), x) == f + + f = Poly({}, x, y, z) + + assert f.gens == (x, y, z) and f.as_expr() == 0 + + assert Poly(Poly(a*x + b*y, x, y), x) == Poly(a*x + b*y, x) + + assert Poly(3*x**2 + 2*x + 1, domain='ZZ').all_coeffs() == [3, 2, 1] + assert Poly(3*x**2 + 2*x + 1, domain='QQ').all_coeffs() == [3, 2, 1] + assert Poly(3*x**2 + 2*x + 1, domain='RR').all_coeffs() == [3.0, 2.0, 1.0] + + raises(CoercionFailed, lambda: Poly(3*x**2/5 + x*Rational(2, 5) + 1, domain='ZZ')) + assert Poly( + 3*x**2/5 + x*Rational(2, 5) + 1, domain='QQ').all_coeffs() == [Rational(3, 5), Rational(2, 5), 1] + assert _epsilon_eq( + Poly(3*x**2/5 + x*Rational(2, 5) + 1, domain='RR').all_coeffs(), [0.6, 0.4, 1.0]) + + assert Poly(3.0*x**2 + 2.0*x + 1, domain='ZZ').all_coeffs() == [3, 2, 1] + assert Poly(3.0*x**2 + 2.0*x + 1, domain='QQ').all_coeffs() == [3, 2, 1] + assert Poly( + 3.0*x**2 + 2.0*x + 1, domain='RR').all_coeffs() == [3.0, 2.0, 1.0] + + raises(CoercionFailed, lambda: Poly(3.1*x**2 + 2.1*x + 1, domain='ZZ')) + assert Poly(3.1*x**2 + 2.1*x + 1, domain='QQ').all_coeffs() == [Rational(31, 10), Rational(21, 10), 1] + assert Poly(3.1*x**2 + 2.1*x + 1, domain='RR').all_coeffs() == [3.1, 2.1, 1.0] + + assert Poly({(2, 1): 1, (1, 2): 2, (1, 1): 3}, x, y) == \ + Poly(x**2*y + 2*x*y**2 + 3*x*y, x, y) + + assert Poly(x**2 + 1, extension=I).get_domain() == QQ.algebraic_field(I) + + f = 3*x**5 - x**4 + x**3 - x** 2 + 65538 + + assert Poly(f, x, modulus=65537, symmetric=True) == \ + Poly(3*x**5 - x**4 + x**3 - x** 2 + 1, x, modulus=65537, + symmetric=True) + assert Poly(f, x, modulus=65537, symmetric=False) == \ + Poly(3*x**5 + 65536*x**4 + x**3 + 65536*x** 2 + 1, x, + modulus=65537, symmetric=False) + + assert isinstance(Poly(x**2 + x + 1.0).get_domain(), RealField) + assert isinstance(Poly(x**2 + x + I + 1.0).get_domain(), ComplexField) + + +def test_Poly__args(): + assert Poly(x**2 + 1).args == (x**2 + 1, x) + + +def test_Poly__gens(): + assert Poly((x - p)*(x - q), x).gens == (x,) + assert Poly((x - p)*(x - q), p).gens == (p,) + assert Poly((x - p)*(x - q), q).gens == (q,) + + assert Poly((x - p)*(x - q), x, p).gens == (x, p) + assert Poly((x - p)*(x - q), x, q).gens == (x, q) + + assert Poly((x - p)*(x - q), x, p, q).gens == (x, p, q) + assert Poly((x - p)*(x - q), p, x, q).gens == (p, x, q) + assert Poly((x - p)*(x - q), p, q, x).gens == (p, q, x) + + assert Poly((x - p)*(x - q)).gens == (x, p, q) + + assert Poly((x - p)*(x - q), sort='x > p > q').gens == (x, p, q) + assert Poly((x - p)*(x - q), sort='p > x > q').gens == (p, x, q) + assert Poly((x - p)*(x - q), sort='p > q > x').gens == (p, q, x) + + assert Poly((x - p)*(x - q), x, p, q, sort='p > q > x').gens == (x, p, q) + + assert Poly((x - p)*(x - q), wrt='x').gens == (x, p, q) + assert Poly((x - p)*(x - q), wrt='p').gens == (p, x, q) + assert Poly((x - p)*(x - q), wrt='q').gens == (q, x, p) + + assert Poly((x - p)*(x - q), wrt=x).gens == (x, p, q) + assert Poly((x - p)*(x - q), wrt=p).gens == (p, x, q) + assert Poly((x - p)*(x - q), wrt=q).gens == (q, x, p) + + assert Poly((x - p)*(x - q), x, p, q, wrt='p').gens == (x, p, q) + + assert Poly((x - p)*(x - q), wrt='p', sort='q > x').gens == (p, q, x) + assert Poly((x - p)*(x - q), wrt='q', sort='p > x').gens == (q, p, x) + + +def test_Poly_zero(): + assert Poly(x).zero == Poly(0, x, domain=ZZ) + assert Poly(x/2).zero == Poly(0, x, domain=QQ) + + +def test_Poly_one(): + assert Poly(x).one == Poly(1, x, domain=ZZ) + assert Poly(x/2).one == Poly(1, x, domain=QQ) + + +def test_Poly__unify(): + raises(UnificationFailed, lambda: Poly(x)._unify(y)) + + F3 = FF(3) + + assert Poly(x, x, modulus=3)._unify(Poly(y, y, modulus=3))[2:] == ( + DMP([[F3(1)], []], F3), DMP([[F3(1), F3(0)]], F3)) + raises(UnificationFailed, lambda: Poly(x, x, modulus=3)._unify(Poly(y, y, modulus=5))) + + raises(UnificationFailed, lambda: Poly(y, x, y)._unify(Poly(x, x, modulus=3))) + raises(UnificationFailed, lambda: Poly(x, x, modulus=3)._unify(Poly(y, x, y))) + + assert Poly(x + 1, x)._unify(Poly(x + 2, x))[2:] ==\ + (DMP([ZZ(1), ZZ(1)], ZZ), DMP([ZZ(1), ZZ(2)], ZZ)) + assert Poly(x + 1, x, domain='QQ')._unify(Poly(x + 2, x))[2:] ==\ + (DMP([QQ(1), QQ(1)], QQ), DMP([QQ(1), QQ(2)], QQ)) + assert Poly(x + 1, x)._unify(Poly(x + 2, x, domain='QQ'))[2:] ==\ + (DMP([QQ(1), QQ(1)], QQ), DMP([QQ(1), QQ(2)], QQ)) + + assert Poly(x + 1, x)._unify(Poly(x + 2, x, y))[2:] ==\ + (DMP([[ZZ(1)], [ZZ(1)]], ZZ), DMP([[ZZ(1)], [ZZ(2)]], ZZ)) + assert Poly(x + 1, x, domain='QQ')._unify(Poly(x + 2, x, y))[2:] ==\ + (DMP([[QQ(1)], [QQ(1)]], QQ), DMP([[QQ(1)], [QQ(2)]], QQ)) + assert Poly(x + 1, x)._unify(Poly(x + 2, x, y, domain='QQ'))[2:] ==\ + (DMP([[QQ(1)], [QQ(1)]], QQ), DMP([[QQ(1)], [QQ(2)]], QQ)) + + assert Poly(x + 1, x, y)._unify(Poly(x + 2, x))[2:] ==\ + (DMP([[ZZ(1)], [ZZ(1)]], ZZ), DMP([[ZZ(1)], [ZZ(2)]], ZZ)) + assert Poly(x + 1, x, y, domain='QQ')._unify(Poly(x + 2, x))[2:] ==\ + (DMP([[QQ(1)], [QQ(1)]], QQ), DMP([[QQ(1)], [QQ(2)]], QQ)) + assert Poly(x + 1, x, y)._unify(Poly(x + 2, x, domain='QQ'))[2:] ==\ + (DMP([[QQ(1)], [QQ(1)]], QQ), DMP([[QQ(1)], [QQ(2)]], QQ)) + + assert Poly(x + 1, x, y)._unify(Poly(x + 2, x, y))[2:] ==\ + (DMP([[ZZ(1)], [ZZ(1)]], ZZ), DMP([[ZZ(1)], [ZZ(2)]], ZZ)) + assert Poly(x + 1, x, y, domain='QQ')._unify(Poly(x + 2, x, y))[2:] ==\ + (DMP([[QQ(1)], [QQ(1)]], QQ), DMP([[QQ(1)], [QQ(2)]], QQ)) + assert Poly(x + 1, x, y)._unify(Poly(x + 2, x, y, domain='QQ'))[2:] ==\ + (DMP([[QQ(1)], [QQ(1)]], QQ), DMP([[QQ(1)], [QQ(2)]], QQ)) + + assert Poly(x + 1, x)._unify(Poly(x + 2, y, x))[2:] ==\ + (DMP([[ZZ(1), ZZ(1)]], ZZ), DMP([[ZZ(1), ZZ(2)]], ZZ)) + assert Poly(x + 1, x, domain='QQ')._unify(Poly(x + 2, y, x))[2:] ==\ + (DMP([[QQ(1), QQ(1)]], QQ), DMP([[QQ(1), QQ(2)]], QQ)) + assert Poly(x + 1, x)._unify(Poly(x + 2, y, x, domain='QQ'))[2:] ==\ + (DMP([[QQ(1), QQ(1)]], QQ), DMP([[QQ(1), QQ(2)]], QQ)) + + assert Poly(x + 1, y, x)._unify(Poly(x + 2, x))[2:] ==\ + (DMP([[ZZ(1), ZZ(1)]], ZZ), DMP([[ZZ(1), ZZ(2)]], ZZ)) + assert Poly(x + 1, y, x, domain='QQ')._unify(Poly(x + 2, x))[2:] ==\ + (DMP([[QQ(1), QQ(1)]], QQ), DMP([[QQ(1), QQ(2)]], QQ)) + assert Poly(x + 1, y, x)._unify(Poly(x + 2, x, domain='QQ'))[2:] ==\ + (DMP([[QQ(1), QQ(1)]], QQ), DMP([[QQ(1), QQ(2)]], QQ)) + + assert Poly(x + 1, x, y)._unify(Poly(x + 2, y, x))[2:] ==\ + (DMP([[ZZ(1)], [ZZ(1)]], ZZ), DMP([[ZZ(1)], [ZZ(2)]], ZZ)) + assert Poly(x + 1, x, y, domain='QQ')._unify(Poly(x + 2, y, x))[2:] ==\ + (DMP([[QQ(1)], [QQ(1)]], QQ), DMP([[QQ(1)], [QQ(2)]], QQ)) + assert Poly(x + 1, x, y)._unify(Poly(x + 2, y, x, domain='QQ'))[2:] ==\ + (DMP([[QQ(1)], [QQ(1)]], QQ), DMP([[QQ(1)], [QQ(2)]], QQ)) + + assert Poly(x + 1, y, x)._unify(Poly(x + 2, x, y))[2:] ==\ + (DMP([[ZZ(1), ZZ(1)]], ZZ), DMP([[ZZ(1), ZZ(2)]], ZZ)) + assert Poly(x + 1, y, x, domain='QQ')._unify(Poly(x + 2, x, y))[2:] ==\ + (DMP([[QQ(1), QQ(1)]], QQ), DMP([[QQ(1), QQ(2)]], QQ)) + assert Poly(x + 1, y, x)._unify(Poly(x + 2, x, y, domain='QQ'))[2:] ==\ + (DMP([[QQ(1), QQ(1)]], QQ), DMP([[QQ(1), QQ(2)]], QQ)) + + assert Poly(x**2 + I, x, domain=ZZ_I).unify(Poly(x**2 + sqrt(2), x, extension=True)) == \ + (Poly(x**2 + I, x, domain='QQ'), Poly(x**2 + sqrt(2), x, domain='QQ')) + + F, A, B = field("a,b", ZZ) + + assert Poly(a*x, x, domain='ZZ[a]')._unify(Poly(a*b*x, x, domain='ZZ(a,b)'))[2:] == \ + (DMP([A, F(0)], F.to_domain()), DMP([A*B, F(0)], F.to_domain())) + + assert Poly(a*x, x, domain='ZZ(a)')._unify(Poly(a*b*x, x, domain='ZZ(a,b)'))[2:] == \ + (DMP([A, F(0)], F.to_domain()), DMP([A*B, F(0)], F.to_domain())) + + raises(CoercionFailed, lambda: Poly(Poly(x**2 + x**2*z, y, field=True), domain='ZZ(x)')) + + f = Poly(t**2 + t/3 + x, t, domain='QQ(x)') + g = Poly(t**2 + t/3 + x, t, domain='QQ[x]') + + assert f._unify(g)[2:] == (f.rep, f.rep) + + +def test_Poly_free_symbols(): + assert Poly(x**2 + 1).free_symbols == {x} + assert Poly(x**2 + y*z).free_symbols == {x, y, z} + assert Poly(x**2 + y*z, x).free_symbols == {x, y, z} + assert Poly(x**2 + sin(y*z)).free_symbols == {x, y, z} + assert Poly(x**2 + sin(y*z), x).free_symbols == {x, y, z} + assert Poly(x**2 + sin(y*z), x, domain=EX).free_symbols == {x, y, z} + assert Poly(1 + x + x**2, x, y, z).free_symbols == {x} + assert Poly(x + sin(y), z).free_symbols == {x, y} + + +def test_PurePoly_free_symbols(): + assert PurePoly(x**2 + 1).free_symbols == set() + assert PurePoly(x**2 + y*z).free_symbols == set() + assert PurePoly(x**2 + y*z, x).free_symbols == {y, z} + assert PurePoly(x**2 + sin(y*z)).free_symbols == set() + assert PurePoly(x**2 + sin(y*z), x).free_symbols == {y, z} + assert PurePoly(x**2 + sin(y*z), x, domain=EX).free_symbols == {y, z} + + +def test_Poly__eq__(): + assert (Poly(x, x) == Poly(x, x)) is True + assert (Poly(x, x, domain=QQ) == Poly(x, x)) is False + assert (Poly(x, x) == Poly(x, x, domain=QQ)) is False + + assert (Poly(x, x, domain=ZZ[a]) == Poly(x, x)) is False + assert (Poly(x, x) == Poly(x, x, domain=ZZ[a])) is False + + assert (Poly(x*y, x, y) == Poly(x, x)) is False + + assert (Poly(x, x, y) == Poly(x, x)) is False + assert (Poly(x, x) == Poly(x, x, y)) is False + + assert (Poly(x**2 + 1, x) == Poly(y**2 + 1, y)) is False + assert (Poly(y**2 + 1, y) == Poly(x**2 + 1, x)) is False + + f = Poly(x, x, domain=ZZ) + g = Poly(x, x, domain=QQ) + + assert f.eq(g) is False + assert f.ne(g) is True + + assert f.eq(g, strict=True) is False + assert f.ne(g, strict=True) is True + + t0 = Symbol('t0') + + f = Poly((t0/2 + x**2)*t**2 - x**2*t, t, domain='QQ[x,t0]') + g = Poly((t0/2 + x**2)*t**2 - x**2*t, t, domain='ZZ(x,t0)') + + assert (f == g) is False + +def test_PurePoly__eq__(): + assert (PurePoly(x, x) == PurePoly(x, x)) is True + assert (PurePoly(x, x, domain=QQ) == PurePoly(x, x)) is True + assert (PurePoly(x, x) == PurePoly(x, x, domain=QQ)) is True + + assert (PurePoly(x, x, domain=ZZ[a]) == PurePoly(x, x)) is True + assert (PurePoly(x, x) == PurePoly(x, x, domain=ZZ[a])) is True + + assert (PurePoly(x*y, x, y) == PurePoly(x, x)) is False + + assert (PurePoly(x, x, y) == PurePoly(x, x)) is False + assert (PurePoly(x, x) == PurePoly(x, x, y)) is False + + assert (PurePoly(x**2 + 1, x) == PurePoly(y**2 + 1, y)) is True + assert (PurePoly(y**2 + 1, y) == PurePoly(x**2 + 1, x)) is True + + f = PurePoly(x, x, domain=ZZ) + g = PurePoly(x, x, domain=QQ) + + assert f.eq(g) is True + assert f.ne(g) is False + + assert f.eq(g, strict=True) is False + assert f.ne(g, strict=True) is True + + f = PurePoly(x, x, domain=ZZ) + g = PurePoly(y, y, domain=QQ) + + assert f.eq(g) is True + assert f.ne(g) is False + + assert f.eq(g, strict=True) is False + assert f.ne(g, strict=True) is True + + +def test_PurePoly_Poly(): + assert isinstance(PurePoly(Poly(x**2 + 1)), PurePoly) is True + assert isinstance(Poly(PurePoly(x**2 + 1)), Poly) is True + + +def test_Poly_get_domain(): + assert Poly(2*x).get_domain() == ZZ + + assert Poly(2*x, domain='ZZ').get_domain() == ZZ + assert Poly(2*x, domain='QQ').get_domain() == QQ + + assert Poly(x/2).get_domain() == QQ + + raises(CoercionFailed, lambda: Poly(x/2, domain='ZZ')) + assert Poly(x/2, domain='QQ').get_domain() == QQ + + assert isinstance(Poly(0.2*x).get_domain(), RealField) + + +def test_Poly_set_domain(): + assert Poly(2*x + 1).set_domain(ZZ) == Poly(2*x + 1) + assert Poly(2*x + 1).set_domain('ZZ') == Poly(2*x + 1) + + assert Poly(2*x + 1).set_domain(QQ) == Poly(2*x + 1, domain='QQ') + assert Poly(2*x + 1).set_domain('QQ') == Poly(2*x + 1, domain='QQ') + + assert Poly(Rational(2, 10)*x + Rational(1, 10)).set_domain('RR') == Poly(0.2*x + 0.1) + assert Poly(0.2*x + 0.1).set_domain('QQ') == Poly(Rational(2, 10)*x + Rational(1, 10)) + + raises(CoercionFailed, lambda: Poly(x/2 + 1).set_domain(ZZ)) + raises(CoercionFailed, lambda: Poly(x + 1, modulus=2).set_domain(QQ)) + + raises(GeneratorsError, lambda: Poly(x*y, x, y).set_domain(ZZ[y])) + + +def test_Poly_get_modulus(): + assert Poly(x**2 + 1, modulus=2).get_modulus() == 2 + raises(PolynomialError, lambda: Poly(x**2 + 1).get_modulus()) + + +def test_Poly_set_modulus(): + assert Poly( + x**2 + 1, modulus=2).set_modulus(7) == Poly(x**2 + 1, modulus=7) + assert Poly( + x**2 + 5, modulus=7).set_modulus(2) == Poly(x**2 + 1, modulus=2) + + assert Poly(x**2 + 1).set_modulus(2) == Poly(x**2 + 1, modulus=2) + + raises(CoercionFailed, lambda: Poly(x/2 + 1).set_modulus(2)) + + +def test_Poly_add_ground(): + assert Poly(x + 1).add_ground(2) == Poly(x + 3) + + +def test_Poly_sub_ground(): + assert Poly(x + 1).sub_ground(2) == Poly(x - 1) + + +def test_Poly_mul_ground(): + assert Poly(x + 1).mul_ground(2) == Poly(2*x + 2) + + +def test_Poly_quo_ground(): + assert Poly(2*x + 4).quo_ground(2) == Poly(x + 2) + assert Poly(2*x + 3).quo_ground(2) == Poly(x + 1) + + +def test_Poly_exquo_ground(): + assert Poly(2*x + 4).exquo_ground(2) == Poly(x + 2) + raises(ExactQuotientFailed, lambda: Poly(2*x + 3).exquo_ground(2)) + + +def test_Poly_abs(): + assert Poly(-x + 1, x).abs() == abs(Poly(-x + 1, x)) == Poly(x + 1, x) + + +def test_Poly_neg(): + assert Poly(-x + 1, x).neg() == -Poly(-x + 1, x) == Poly(x - 1, x) + + +def test_Poly_add(): + assert Poly(0, x).add(Poly(0, x)) == Poly(0, x) + assert Poly(0, x) + Poly(0, x) == Poly(0, x) + + assert Poly(1, x).add(Poly(0, x)) == Poly(1, x) + assert Poly(1, x, y) + Poly(0, x) == Poly(1, x, y) + assert Poly(0, x).add(Poly(1, x, y)) == Poly(1, x, y) + assert Poly(0, x, y) + Poly(1, x, y) == Poly(1, x, y) + + assert Poly(1, x) + x == Poly(x + 1, x) + with warns_deprecated_sympy(): + Poly(1, x) + sin(x) + + assert Poly(x, x) + 1 == Poly(x + 1, x) + assert 1 + Poly(x, x) == Poly(x + 1, x) + + +def test_Poly_sub(): + assert Poly(0, x).sub(Poly(0, x)) == Poly(0, x) + assert Poly(0, x) - Poly(0, x) == Poly(0, x) + + assert Poly(1, x).sub(Poly(0, x)) == Poly(1, x) + assert Poly(1, x, y) - Poly(0, x) == Poly(1, x, y) + assert Poly(0, x).sub(Poly(1, x, y)) == Poly(-1, x, y) + assert Poly(0, x, y) - Poly(1, x, y) == Poly(-1, x, y) + + assert Poly(1, x) - x == Poly(1 - x, x) + with warns_deprecated_sympy(): + Poly(1, x) - sin(x) + + assert Poly(x, x) - 1 == Poly(x - 1, x) + assert 1 - Poly(x, x) == Poly(1 - x, x) + + +def test_Poly_mul(): + assert Poly(0, x).mul(Poly(0, x)) == Poly(0, x) + assert Poly(0, x) * Poly(0, x) == Poly(0, x) + + assert Poly(2, x).mul(Poly(4, x)) == Poly(8, x) + assert Poly(2, x, y) * Poly(4, x) == Poly(8, x, y) + assert Poly(4, x).mul(Poly(2, x, y)) == Poly(8, x, y) + assert Poly(4, x, y) * Poly(2, x, y) == Poly(8, x, y) + + assert Poly(1, x) * x == Poly(x, x) + with warns_deprecated_sympy(): + Poly(1, x) * sin(x) + + assert Poly(x, x) * 2 == Poly(2*x, x) + assert 2 * Poly(x, x) == Poly(2*x, x) + +def test_issue_13079(): + assert Poly(x)*x == Poly(x**2, x, domain='ZZ') + assert x*Poly(x) == Poly(x**2, x, domain='ZZ') + assert -2*Poly(x) == Poly(-2*x, x, domain='ZZ') + assert S(-2)*Poly(x) == Poly(-2*x, x, domain='ZZ') + assert Poly(x)*S(-2) == Poly(-2*x, x, domain='ZZ') + +def test_Poly_sqr(): + assert Poly(x*y, x, y).sqr() == Poly(x**2*y**2, x, y) + + +def test_Poly_pow(): + assert Poly(x, x).pow(10) == Poly(x**10, x) + assert Poly(x, x).pow(Integer(10)) == Poly(x**10, x) + + assert Poly(2*y, x, y).pow(4) == Poly(16*y**4, x, y) + assert Poly(2*y, x, y).pow(Integer(4)) == Poly(16*y**4, x, y) + + assert Poly(7*x*y, x, y)**3 == Poly(343*x**3*y**3, x, y) + + raises(TypeError, lambda: Poly(x*y + 1, x, y)**(-1)) + raises(TypeError, lambda: Poly(x*y + 1, x, y)**x) + + +def test_Poly_divmod(): + f, g = Poly(x**2), Poly(x) + q, r = g, Poly(0, x) + + assert divmod(f, g) == (q, r) + assert f // g == q + assert f % g == r + + assert divmod(f, x) == (q, r) + assert f // x == q + assert f % x == r + + q, r = Poly(0, x), Poly(2, x) + + assert divmod(2, g) == (q, r) + assert 2 // g == q + assert 2 % g == r + + assert Poly(x)/Poly(x) == 1 + assert Poly(x**2)/Poly(x) == x + assert Poly(x)/Poly(x**2) == 1/x + + +def test_Poly_eq_ne(): + assert (Poly(x + y, x, y) == Poly(x + y, x, y)) is True + assert (Poly(x + y, x) == Poly(x + y, x, y)) is False + assert (Poly(x + y, x, y) == Poly(x + y, x)) is False + assert (Poly(x + y, x) == Poly(x + y, x)) is True + assert (Poly(x + y, y) == Poly(x + y, y)) is True + + assert (Poly(x + y, x, y) == x + y) is True + assert (Poly(x + y, x) == x + y) is True + assert (Poly(x + y, x, y) == x + y) is True + assert (Poly(x + y, x) == x + y) is True + assert (Poly(x + y, y) == x + y) is True + + assert (Poly(x + y, x, y) != Poly(x + y, x, y)) is False + assert (Poly(x + y, x) != Poly(x + y, x, y)) is True + assert (Poly(x + y, x, y) != Poly(x + y, x)) is True + assert (Poly(x + y, x) != Poly(x + y, x)) is False + assert (Poly(x + y, y) != Poly(x + y, y)) is False + + assert (Poly(x + y, x, y) != x + y) is False + assert (Poly(x + y, x) != x + y) is False + assert (Poly(x + y, x, y) != x + y) is False + assert (Poly(x + y, x) != x + y) is False + assert (Poly(x + y, y) != x + y) is False + + assert (Poly(x, x) == sin(x)) is False + assert (Poly(x, x) != sin(x)) is True + + +def test_Poly_nonzero(): + assert not bool(Poly(0, x)) is True + assert not bool(Poly(1, x)) is False + + +def test_Poly_properties(): + assert Poly(0, x).is_zero is True + assert Poly(1, x).is_zero is False + + assert Poly(1, x).is_one is True + assert Poly(2, x).is_one is False + + assert Poly(x - 1, x).is_sqf is True + assert Poly((x - 1)**2, x).is_sqf is False + + assert Poly(x - 1, x).is_monic is True + assert Poly(2*x - 1, x).is_monic is False + + assert Poly(3*x + 2, x).is_primitive is True + assert Poly(4*x + 2, x).is_primitive is False + + assert Poly(1, x).is_ground is True + assert Poly(x, x).is_ground is False + + assert Poly(x + y + z + 1).is_linear is True + assert Poly(x*y*z + 1).is_linear is False + + assert Poly(x*y + z + 1).is_quadratic is True + assert Poly(x*y*z + 1).is_quadratic is False + + assert Poly(x*y).is_monomial is True + assert Poly(x*y + 1).is_monomial is False + + assert Poly(x**2 + x*y).is_homogeneous is True + assert Poly(x**3 + x*y).is_homogeneous is False + + assert Poly(x).is_univariate is True + assert Poly(x*y).is_univariate is False + + assert Poly(x*y).is_multivariate is True + assert Poly(x).is_multivariate is False + + assert Poly( + x**16 + x**14 - x**10 + x**8 - x**6 + x**2 + 1).is_cyclotomic is False + assert Poly( + x**16 + x**14 - x**10 - x**8 - x**6 + x**2 + 1).is_cyclotomic is True + + +def test_Poly_is_irreducible(): + assert Poly(x**2 + x + 1).is_irreducible is True + assert Poly(x**2 + 2*x + 1).is_irreducible is False + + assert Poly(7*x + 3, modulus=11).is_irreducible is True + assert Poly(7*x**2 + 3*x + 1, modulus=11).is_irreducible is False + + +def test_Poly_subs(): + assert Poly(x + 1).subs(x, 0) == 1 + + assert Poly(x + 1).subs(x, x) == Poly(x + 1) + assert Poly(x + 1).subs(x, y) == Poly(y + 1) + + assert Poly(x*y, x).subs(y, x) == x**2 + assert Poly(x*y, x).subs(x, y) == y**2 + + +def test_Poly_replace(): + assert Poly(x + 1).replace(x) == Poly(x + 1) + assert Poly(x + 1).replace(y) == Poly(y + 1) + + raises(PolynomialError, lambda: Poly(x + y).replace(z)) + + assert Poly(x + 1).replace(x, x) == Poly(x + 1) + assert Poly(x + 1).replace(x, y) == Poly(y + 1) + + assert Poly(x + y).replace(x, x) == Poly(x + y) + assert Poly(x + y).replace(x, z) == Poly(z + y, z, y) + + assert Poly(x + y).replace(y, y) == Poly(x + y) + assert Poly(x + y).replace(y, z) == Poly(x + z, x, z) + assert Poly(x + y).replace(z, t) == Poly(x + y) + + raises(PolynomialError, lambda: Poly(x + y).replace(x, y)) + + assert Poly(x + y, x).replace(x, z) == Poly(z + y, z) + assert Poly(x + y, y).replace(y, z) == Poly(x + z, z) + + raises(PolynomialError, lambda: Poly(x + y, x).replace(x, y)) + raises(PolynomialError, lambda: Poly(x + y, y).replace(y, x)) + + +def test_Poly_reorder(): + raises(PolynomialError, lambda: Poly(x + y).reorder(x, z)) + + assert Poly(x + y, x, y).reorder(x, y) == Poly(x + y, x, y) + assert Poly(x + y, x, y).reorder(y, x) == Poly(x + y, y, x) + + assert Poly(x + y, y, x).reorder(x, y) == Poly(x + y, x, y) + assert Poly(x + y, y, x).reorder(y, x) == Poly(x + y, y, x) + + assert Poly(x + y, x, y).reorder(wrt=x) == Poly(x + y, x, y) + assert Poly(x + y, x, y).reorder(wrt=y) == Poly(x + y, y, x) + + +def test_Poly_ltrim(): + f = Poly(y**2 + y*z**2, x, y, z).ltrim(y) + assert f.as_expr() == y**2 + y*z**2 and f.gens == (y, z) + assert Poly(x*y - x, z, x, y).ltrim(1) == Poly(x*y - x, x, y) + + raises(PolynomialError, lambda: Poly(x*y**2 + y**2, x, y).ltrim(y)) + raises(PolynomialError, lambda: Poly(x*y - x, x, y).ltrim(-1)) + +def test_Poly_has_only_gens(): + assert Poly(x*y + 1, x, y, z).has_only_gens(x, y) is True + assert Poly(x*y + z, x, y, z).has_only_gens(x, y) is False + + raises(GeneratorsError, lambda: Poly(x*y**2 + y**2, x, y).has_only_gens(t)) + + +def test_Poly_to_ring(): + assert Poly(2*x + 1, domain='ZZ').to_ring() == Poly(2*x + 1, domain='ZZ') + assert Poly(2*x + 1, domain='QQ').to_ring() == Poly(2*x + 1, domain='ZZ') + + raises(CoercionFailed, lambda: Poly(x/2 + 1).to_ring()) + raises(DomainError, lambda: Poly(2*x + 1, modulus=3).to_ring()) + + +def test_Poly_to_field(): + assert Poly(2*x + 1, domain='ZZ').to_field() == Poly(2*x + 1, domain='QQ') + assert Poly(2*x + 1, domain='QQ').to_field() == Poly(2*x + 1, domain='QQ') + + assert Poly(x/2 + 1, domain='QQ').to_field() == Poly(x/2 + 1, domain='QQ') + assert Poly(2*x + 1, modulus=3).to_field() == Poly(2*x + 1, modulus=3) + + assert Poly(2.0*x + 1.0).to_field() == Poly(2.0*x + 1.0) + + +def test_Poly_to_exact(): + assert Poly(2*x).to_exact() == Poly(2*x) + assert Poly(x/2).to_exact() == Poly(x/2) + + assert Poly(0.1*x).to_exact() == Poly(x/10) + + +def test_Poly_retract(): + f = Poly(x**2 + 1, x, domain=QQ[y]) + + assert f.retract() == Poly(x**2 + 1, x, domain='ZZ') + assert f.retract(field=True) == Poly(x**2 + 1, x, domain='QQ') + + assert Poly(0, x, y).retract() == Poly(0, x, y) + + +def test_Poly_slice(): + f = Poly(x**3 + 2*x**2 + 3*x + 4) + + assert f.slice(0, 0) == Poly(0, x) + assert f.slice(0, 1) == Poly(4, x) + assert f.slice(0, 2) == Poly(3*x + 4, x) + assert f.slice(0, 3) == Poly(2*x**2 + 3*x + 4, x) + assert f.slice(0, 4) == Poly(x**3 + 2*x**2 + 3*x + 4, x) + + assert f.slice(x, 0, 0) == Poly(0, x) + assert f.slice(x, 0, 1) == Poly(4, x) + assert f.slice(x, 0, 2) == Poly(3*x + 4, x) + assert f.slice(x, 0, 3) == Poly(2*x**2 + 3*x + 4, x) + assert f.slice(x, 0, 4) == Poly(x**3 + 2*x**2 + 3*x + 4, x) + + g = Poly(x**3 + 1) + + assert g.slice(0, 3) == Poly(1, x) + + +def test_Poly_coeffs(): + assert Poly(0, x).coeffs() == [0] + assert Poly(1, x).coeffs() == [1] + + assert Poly(2*x + 1, x).coeffs() == [2, 1] + + assert Poly(7*x**2 + 2*x + 1, x).coeffs() == [7, 2, 1] + assert Poly(7*x**4 + 2*x + 1, x).coeffs() == [7, 2, 1] + + assert Poly(x*y**7 + 2*x**2*y**3).coeffs('lex') == [2, 1] + assert Poly(x*y**7 + 2*x**2*y**3).coeffs('grlex') == [1, 2] + + +def test_Poly_monoms(): + assert Poly(0, x).monoms() == [(0,)] + assert Poly(1, x).monoms() == [(0,)] + + assert Poly(2*x + 1, x).monoms() == [(1,), (0,)] + + assert Poly(7*x**2 + 2*x + 1, x).monoms() == [(2,), (1,), (0,)] + assert Poly(7*x**4 + 2*x + 1, x).monoms() == [(4,), (1,), (0,)] + + assert Poly(x*y**7 + 2*x**2*y**3).monoms('lex') == [(2, 3), (1, 7)] + assert Poly(x*y**7 + 2*x**2*y**3).monoms('grlex') == [(1, 7), (2, 3)] + + +def test_Poly_terms(): + assert Poly(0, x).terms() == [((0,), 0)] + assert Poly(1, x).terms() == [((0,), 1)] + + assert Poly(2*x + 1, x).terms() == [((1,), 2), ((0,), 1)] + + assert Poly(7*x**2 + 2*x + 1, x).terms() == [((2,), 7), ((1,), 2), ((0,), 1)] + assert Poly(7*x**4 + 2*x + 1, x).terms() == [((4,), 7), ((1,), 2), ((0,), 1)] + + assert Poly( + x*y**7 + 2*x**2*y**3).terms('lex') == [((2, 3), 2), ((1, 7), 1)] + assert Poly( + x*y**7 + 2*x**2*y**3).terms('grlex') == [((1, 7), 1), ((2, 3), 2)] + + +def test_Poly_all_coeffs(): + assert Poly(0, x).all_coeffs() == [0] + assert Poly(1, x).all_coeffs() == [1] + + assert Poly(2*x + 1, x).all_coeffs() == [2, 1] + + assert Poly(7*x**2 + 2*x + 1, x).all_coeffs() == [7, 2, 1] + assert Poly(7*x**4 + 2*x + 1, x).all_coeffs() == [7, 0, 0, 2, 1] + + +def test_Poly_all_monoms(): + assert Poly(0, x).all_monoms() == [(0,)] + assert Poly(1, x).all_monoms() == [(0,)] + + assert Poly(2*x + 1, x).all_monoms() == [(1,), (0,)] + + assert Poly(7*x**2 + 2*x + 1, x).all_monoms() == [(2,), (1,), (0,)] + assert Poly(7*x**4 + 2*x + 1, x).all_monoms() == [(4,), (3,), (2,), (1,), (0,)] + + +def test_Poly_all_terms(): + assert Poly(0, x).all_terms() == [((0,), 0)] + assert Poly(1, x).all_terms() == [((0,), 1)] + + assert Poly(2*x + 1, x).all_terms() == [((1,), 2), ((0,), 1)] + + assert Poly(7*x**2 + 2*x + 1, x).all_terms() == \ + [((2,), 7), ((1,), 2), ((0,), 1)] + assert Poly(7*x**4 + 2*x + 1, x).all_terms() == \ + [((4,), 7), ((3,), 0), ((2,), 0), ((1,), 2), ((0,), 1)] + + +def test_Poly_termwise(): + f = Poly(x**2 + 20*x + 400) + g = Poly(x**2 + 2*x + 4) + + def func(monom, coeff): + (k,) = monom + return coeff//10**(2 - k) + + assert f.termwise(func) == g + + def func(monom, coeff): + (k,) = monom + return (k,), coeff//10**(2 - k) + + assert f.termwise(func) == g + + +def test_Poly_length(): + assert Poly(0, x).length() == 0 + assert Poly(1, x).length() == 1 + assert Poly(x, x).length() == 1 + + assert Poly(x + 1, x).length() == 2 + assert Poly(x**2 + 1, x).length() == 2 + assert Poly(x**2 + x + 1, x).length() == 3 + + +def test_Poly_as_dict(): + assert Poly(0, x).as_dict() == {} + assert Poly(0, x, y, z).as_dict() == {} + + assert Poly(1, x).as_dict() == {(0,): 1} + assert Poly(1, x, y, z).as_dict() == {(0, 0, 0): 1} + + assert Poly(x**2 + 3, x).as_dict() == {(2,): 1, (0,): 3} + assert Poly(x**2 + 3, x, y, z).as_dict() == {(2, 0, 0): 1, (0, 0, 0): 3} + + assert Poly(3*x**2*y*z**3 + 4*x*y + 5*x*z).as_dict() == {(2, 1, 3): 3, + (1, 1, 0): 4, (1, 0, 1): 5} + + +def test_Poly_as_expr(): + assert Poly(0, x).as_expr() == 0 + assert Poly(0, x, y, z).as_expr() == 0 + + assert Poly(1, x).as_expr() == 1 + assert Poly(1, x, y, z).as_expr() == 1 + + assert Poly(x**2 + 3, x).as_expr() == x**2 + 3 + assert Poly(x**2 + 3, x, y, z).as_expr() == x**2 + 3 + + assert Poly( + 3*x**2*y*z**3 + 4*x*y + 5*x*z).as_expr() == 3*x**2*y*z**3 + 4*x*y + 5*x*z + + f = Poly(x**2 + 2*x*y**2 - y, x, y) + + assert f.as_expr() == -y + x**2 + 2*x*y**2 + + assert f.as_expr({x: 5}) == 25 - y + 10*y**2 + assert f.as_expr({y: 6}) == -6 + 72*x + x**2 + + assert f.as_expr({x: 5, y: 6}) == 379 + assert f.as_expr(5, 6) == 379 + + raises(GeneratorsError, lambda: f.as_expr({z: 7})) + + +def test_Poly_lift(): + assert Poly(x**4 - I*x + 17*I, x, gaussian=True).lift() == \ + Poly(x**16 + 2*x**10 + 578*x**8 + x**4 - 578*x**2 + 83521, + x, domain='QQ') + + +def test_Poly_deflate(): + assert Poly(0, x).deflate() == ((1,), Poly(0, x)) + assert Poly(1, x).deflate() == ((1,), Poly(1, x)) + assert Poly(x, x).deflate() == ((1,), Poly(x, x)) + + assert Poly(x**2, x).deflate() == ((2,), Poly(x, x)) + assert Poly(x**17, x).deflate() == ((17,), Poly(x, x)) + + assert Poly( + x**2*y*z**11 + x**4*z**11).deflate() == ((2, 1, 11), Poly(x*y*z + x**2*z)) + + +def test_Poly_inject(): + f = Poly(x**2*y + x*y**3 + x*y + 1, x) + + assert f.inject() == Poly(x**2*y + x*y**3 + x*y + 1, x, y) + assert f.inject(front=True) == Poly(y**3*x + y*x**2 + y*x + 1, y, x) + + +def test_Poly_eject(): + f = Poly(x**2*y + x*y**3 + x*y + 1, x, y) + + assert f.eject(x) == Poly(x*y**3 + (x**2 + x)*y + 1, y, domain='ZZ[x]') + assert f.eject(y) == Poly(y*x**2 + (y**3 + y)*x + 1, x, domain='ZZ[y]') + + ex = x + y + z + t + w + g = Poly(ex, x, y, z, t, w) + + assert g.eject(x) == Poly(ex, y, z, t, w, domain='ZZ[x]') + assert g.eject(x, y) == Poly(ex, z, t, w, domain='ZZ[x, y]') + assert g.eject(x, y, z) == Poly(ex, t, w, domain='ZZ[x, y, z]') + assert g.eject(w) == Poly(ex, x, y, z, t, domain='ZZ[w]') + assert g.eject(t, w) == Poly(ex, x, y, z, domain='ZZ[t, w]') + assert g.eject(z, t, w) == Poly(ex, x, y, domain='ZZ[z, t, w]') + + raises(DomainError, lambda: Poly(x*y, x, y, domain=ZZ[z]).eject(y)) + raises(NotImplementedError, lambda: Poly(x*y, x, y, z).eject(y)) + + +def test_Poly_exclude(): + assert Poly(x, x, y).exclude() == Poly(x, x) + assert Poly(x*y, x, y).exclude() == Poly(x*y, x, y) + assert Poly(1, x, y).exclude() == Poly(1, x, y) + + +def test_Poly__gen_to_level(): + assert Poly(1, x, y)._gen_to_level(-2) == 0 + assert Poly(1, x, y)._gen_to_level(-1) == 1 + assert Poly(1, x, y)._gen_to_level( 0) == 0 + assert Poly(1, x, y)._gen_to_level( 1) == 1 + + raises(PolynomialError, lambda: Poly(1, x, y)._gen_to_level(-3)) + raises(PolynomialError, lambda: Poly(1, x, y)._gen_to_level( 2)) + + assert Poly(1, x, y)._gen_to_level(x) == 0 + assert Poly(1, x, y)._gen_to_level(y) == 1 + + assert Poly(1, x, y)._gen_to_level('x') == 0 + assert Poly(1, x, y)._gen_to_level('y') == 1 + + raises(PolynomialError, lambda: Poly(1, x, y)._gen_to_level(z)) + raises(PolynomialError, lambda: Poly(1, x, y)._gen_to_level('z')) + + +def test_Poly_degree(): + assert Poly(0, x).degree() is -oo + assert Poly(1, x).degree() == 0 + assert Poly(x, x).degree() == 1 + + assert Poly(0, x).degree(gen=0) is -oo + assert Poly(1, x).degree(gen=0) == 0 + assert Poly(x, x).degree(gen=0) == 1 + + assert Poly(0, x).degree(gen=x) is -oo + assert Poly(1, x).degree(gen=x) == 0 + assert Poly(x, x).degree(gen=x) == 1 + + assert Poly(0, x).degree(gen='x') is -oo + assert Poly(1, x).degree(gen='x') == 0 + assert Poly(x, x).degree(gen='x') == 1 + + raises(PolynomialError, lambda: Poly(1, x).degree(gen=1)) + raises(PolynomialError, lambda: Poly(1, x).degree(gen=y)) + raises(PolynomialError, lambda: Poly(1, x).degree(gen='y')) + + assert Poly(1, x, y).degree() == 0 + assert Poly(2*y, x, y).degree() == 0 + assert Poly(x*y, x, y).degree() == 1 + + assert Poly(1, x, y).degree(gen=x) == 0 + assert Poly(2*y, x, y).degree(gen=x) == 0 + assert Poly(x*y, x, y).degree(gen=x) == 1 + + assert Poly(1, x, y).degree(gen=y) == 0 + assert Poly(2*y, x, y).degree(gen=y) == 1 + assert Poly(x*y, x, y).degree(gen=y) == 1 + + assert degree(0, x) is -oo + assert degree(1, x) == 0 + assert degree(x, x) == 1 + + assert degree(x*y**2, x) == 1 + assert degree(x*y**2, y) == 2 + assert degree(x*y**2, z) == 0 + + assert degree(pi) == 1 + + raises(TypeError, lambda: degree(y**2 + x**3)) + raises(TypeError, lambda: degree(y**2 + x**3, 1)) + raises(PolynomialError, lambda: degree(x, 1.1)) + raises(PolynomialError, lambda: degree(x**2/(x**3 + 1), x)) + + assert degree(Poly(0,x),z) is -oo + assert degree(Poly(1,x),z) == 0 + assert degree(Poly(x**2+y**3,y)) == 3 + assert degree(Poly(y**2 + x**3, y, x), 1) == 3 + assert degree(Poly(y**2 + x**3, x), z) == 0 + assert degree(Poly(y**2 + x**3 + z**4, x), z) == 4 + +def test_Poly_degree_list(): + assert Poly(0, x).degree_list() == (-oo,) + assert Poly(0, x, y).degree_list() == (-oo, -oo) + assert Poly(0, x, y, z).degree_list() == (-oo, -oo, -oo) + + assert Poly(1, x).degree_list() == (0,) + assert Poly(1, x, y).degree_list() == (0, 0) + assert Poly(1, x, y, z).degree_list() == (0, 0, 0) + + assert Poly(x**2*y + x**3*z**2 + 1).degree_list() == (3, 1, 2) + + assert degree_list(1, x) == (0,) + assert degree_list(x, x) == (1,) + + assert degree_list(x*y**2) == (1, 2) + + raises(ComputationFailed, lambda: degree_list(1)) + + +def test_Poly_total_degree(): + assert Poly(x**2*y + x**3*z**2 + 1).total_degree() == 5 + assert Poly(x**2 + z**3).total_degree() == 3 + assert Poly(x*y*z + z**4).total_degree() == 4 + assert Poly(x**3 + x + 1).total_degree() == 3 + + assert total_degree(x*y + z**3) == 3 + assert total_degree(x*y + z**3, x, y) == 2 + assert total_degree(1) == 0 + assert total_degree(Poly(y**2 + x**3 + z**4)) == 4 + assert total_degree(Poly(y**2 + x**3 + z**4, x)) == 3 + assert total_degree(Poly(y**2 + x**3 + z**4, x), z) == 4 + assert total_degree(Poly(x**9 + x*z*y + x**3*z**2 + z**7,x), z) == 7 + +def test_Poly_homogenize(): + assert Poly(x**2+y).homogenize(z) == Poly(x**2+y*z) + assert Poly(x+y).homogenize(z) == Poly(x+y, x, y, z) + assert Poly(x+y**2).homogenize(y) == Poly(x*y+y**2) + + +def test_Poly_homogeneous_order(): + assert Poly(0, x, y).homogeneous_order() is -oo + assert Poly(1, x, y).homogeneous_order() == 0 + assert Poly(x, x, y).homogeneous_order() == 1 + assert Poly(x*y, x, y).homogeneous_order() == 2 + + assert Poly(x + 1, x, y).homogeneous_order() is None + assert Poly(x*y + x, x, y).homogeneous_order() is None + + assert Poly(x**5 + 2*x**3*y**2 + 9*x*y**4).homogeneous_order() == 5 + assert Poly(x**5 + 2*x**3*y**3 + 9*x*y**4).homogeneous_order() is None + + +def test_Poly_LC(): + assert Poly(0, x).LC() == 0 + assert Poly(1, x).LC() == 1 + assert Poly(2*x**2 + x, x).LC() == 2 + + assert Poly(x*y**7 + 2*x**2*y**3).LC('lex') == 2 + assert Poly(x*y**7 + 2*x**2*y**3).LC('grlex') == 1 + + assert LC(x*y**7 + 2*x**2*y**3, order='lex') == 2 + assert LC(x*y**7 + 2*x**2*y**3, order='grlex') == 1 + + +def test_Poly_TC(): + assert Poly(0, x).TC() == 0 + assert Poly(1, x).TC() == 1 + assert Poly(2*x**2 + x, x).TC() == 0 + + +def test_Poly_EC(): + assert Poly(0, x).EC() == 0 + assert Poly(1, x).EC() == 1 + assert Poly(2*x**2 + x, x).EC() == 1 + + assert Poly(x*y**7 + 2*x**2*y**3).EC('lex') == 1 + assert Poly(x*y**7 + 2*x**2*y**3).EC('grlex') == 2 + + +def test_Poly_coeff(): + assert Poly(0, x).coeff_monomial(1) == 0 + assert Poly(0, x).coeff_monomial(x) == 0 + + assert Poly(1, x).coeff_monomial(1) == 1 + assert Poly(1, x).coeff_monomial(x) == 0 + + assert Poly(x**8, x).coeff_monomial(1) == 0 + assert Poly(x**8, x).coeff_monomial(x**7) == 0 + assert Poly(x**8, x).coeff_monomial(x**8) == 1 + assert Poly(x**8, x).coeff_monomial(x**9) == 0 + + assert Poly(3*x*y**2 + 1, x, y).coeff_monomial(1) == 1 + assert Poly(3*x*y**2 + 1, x, y).coeff_monomial(x*y**2) == 3 + + p = Poly(24*x*y*exp(8) + 23*x, x, y) + + assert p.coeff_monomial(x) == 23 + assert p.coeff_monomial(y) == 0 + assert p.coeff_monomial(x*y) == 24*exp(8) + + assert p.as_expr().coeff(x) == 24*y*exp(8) + 23 + raises(NotImplementedError, lambda: p.coeff(x)) + + raises(ValueError, lambda: Poly(x + 1).coeff_monomial(0)) + raises(ValueError, lambda: Poly(x + 1).coeff_monomial(3*x)) + raises(ValueError, lambda: Poly(x + 1).coeff_monomial(3*x*y)) + + +def test_Poly_nth(): + assert Poly(0, x).nth(0) == 0 + assert Poly(0, x).nth(1) == 0 + + assert Poly(1, x).nth(0) == 1 + assert Poly(1, x).nth(1) == 0 + + assert Poly(x**8, x).nth(0) == 0 + assert Poly(x**8, x).nth(7) == 0 + assert Poly(x**8, x).nth(8) == 1 + assert Poly(x**8, x).nth(9) == 0 + + assert Poly(3*x*y**2 + 1, x, y).nth(0, 0) == 1 + assert Poly(3*x*y**2 + 1, x, y).nth(1, 2) == 3 + + raises(ValueError, lambda: Poly(x*y + 1, x, y).nth(1)) + + +def test_Poly_LM(): + assert Poly(0, x).LM() == (0,) + assert Poly(1, x).LM() == (0,) + assert Poly(2*x**2 + x, x).LM() == (2,) + + assert Poly(x*y**7 + 2*x**2*y**3).LM('lex') == (2, 3) + assert Poly(x*y**7 + 2*x**2*y**3).LM('grlex') == (1, 7) + + assert LM(x*y**7 + 2*x**2*y**3, order='lex') == x**2*y**3 + assert LM(x*y**7 + 2*x**2*y**3, order='grlex') == x*y**7 + + +def test_Poly_LM_custom_order(): + f = Poly(x**2*y**3*z + x**2*y*z**3 + x*y*z + 1) + rev_lex = lambda monom: tuple(reversed(monom)) + + assert f.LM(order='lex') == (2, 3, 1) + assert f.LM(order=rev_lex) == (2, 1, 3) + + +def test_Poly_EM(): + assert Poly(0, x).EM() == (0,) + assert Poly(1, x).EM() == (0,) + assert Poly(2*x**2 + x, x).EM() == (1,) + + assert Poly(x*y**7 + 2*x**2*y**3).EM('lex') == (1, 7) + assert Poly(x*y**7 + 2*x**2*y**3).EM('grlex') == (2, 3) + + +def test_Poly_LT(): + assert Poly(0, x).LT() == ((0,), 0) + assert Poly(1, x).LT() == ((0,), 1) + assert Poly(2*x**2 + x, x).LT() == ((2,), 2) + + assert Poly(x*y**7 + 2*x**2*y**3).LT('lex') == ((2, 3), 2) + assert Poly(x*y**7 + 2*x**2*y**3).LT('grlex') == ((1, 7), 1) + + assert LT(x*y**7 + 2*x**2*y**3, order='lex') == 2*x**2*y**3 + assert LT(x*y**7 + 2*x**2*y**3, order='grlex') == x*y**7 + + +def test_Poly_ET(): + assert Poly(0, x).ET() == ((0,), 0) + assert Poly(1, x).ET() == ((0,), 1) + assert Poly(2*x**2 + x, x).ET() == ((1,), 1) + + assert Poly(x*y**7 + 2*x**2*y**3).ET('lex') == ((1, 7), 1) + assert Poly(x*y**7 + 2*x**2*y**3).ET('grlex') == ((2, 3), 2) + + +def test_Poly_max_norm(): + assert Poly(-1, x).max_norm() == 1 + assert Poly( 0, x).max_norm() == 0 + assert Poly( 1, x).max_norm() == 1 + + +def test_Poly_l1_norm(): + assert Poly(-1, x).l1_norm() == 1 + assert Poly( 0, x).l1_norm() == 0 + assert Poly( 1, x).l1_norm() == 1 + + +def test_Poly_clear_denoms(): + coeff, poly = Poly(x + 2, x).clear_denoms() + assert coeff == 1 and poly == Poly( + x + 2, x, domain='ZZ') and poly.get_domain() == ZZ + + coeff, poly = Poly(x/2 + 1, x).clear_denoms() + assert coeff == 2 and poly == Poly( + x + 2, x, domain='QQ') and poly.get_domain() == QQ + + coeff, poly = Poly(x/2 + 1, x).clear_denoms(convert=True) + assert coeff == 2 and poly == Poly( + x + 2, x, domain='ZZ') and poly.get_domain() == ZZ + + coeff, poly = Poly(x/y + 1, x).clear_denoms(convert=True) + assert coeff == y and poly == Poly( + x + y, x, domain='ZZ[y]') and poly.get_domain() == ZZ[y] + + coeff, poly = Poly(x/3 + sqrt(2), x, domain='EX').clear_denoms() + assert coeff == 3 and poly == Poly( + x + 3*sqrt(2), x, domain='EX') and poly.get_domain() == EX + + coeff, poly = Poly( + x/3 + sqrt(2), x, domain='EX').clear_denoms(convert=True) + assert coeff == 3 and poly == Poly( + x + 3*sqrt(2), x, domain='EX') and poly.get_domain() == EX + + +def test_Poly_rat_clear_denoms(): + f = Poly(x**2/y + 1, x) + g = Poly(x**3 + y, x) + + assert f.rat_clear_denoms(g) == \ + (Poly(x**2 + y, x), Poly(y*x**3 + y**2, x)) + + f = f.set_domain(EX) + g = g.set_domain(EX) + + assert f.rat_clear_denoms(g) == (f, g) + + +def test_issue_20427(): + f = Poly(-117968192370600*18**(S(1)/3)/(217603955769048*(24201 + + 253*sqrt(9165))**(S(1)/3) + 2273005839412*sqrt(9165)*(24201 + + 253*sqrt(9165))**(S(1)/3)) - 15720318185*2**(S(2)/3)*3**(S(1)/3)*(24201 + + 253*sqrt(9165))**(S(2)/3)/(217603955769048*(24201 + 253*sqrt(9165))** + (S(1)/3) + 2273005839412*sqrt(9165)*(24201 + 253*sqrt(9165))**(S(1)/3)) + + 15720318185*12**(S(1)/3)*(24201 + 253*sqrt(9165))**(S(2)/3)/( + 217603955769048*(24201 + 253*sqrt(9165))**(S(1)/3) + 2273005839412* + sqrt(9165)*(24201 + 253*sqrt(9165))**(S(1)/3)) + 117968192370600*2**( + S(1)/3)*3**(S(2)/3)/(217603955769048*(24201 + 253*sqrt(9165))**(S(1)/3) + + 2273005839412*sqrt(9165)*(24201 + 253*sqrt(9165))**(S(1)/3)), x) + assert f == Poly(0, x, domain='EX') + + +def test_Poly_integrate(): + assert Poly(x + 1).integrate() == Poly(x**2/2 + x) + assert Poly(x + 1).integrate(x) == Poly(x**2/2 + x) + assert Poly(x + 1).integrate((x, 1)) == Poly(x**2/2 + x) + + assert Poly(x*y + 1).integrate(x) == Poly(x**2*y/2 + x) + assert Poly(x*y + 1).integrate(y) == Poly(x*y**2/2 + y) + + assert Poly(x*y + 1).integrate(x, x) == Poly(x**3*y/6 + x**2/2) + assert Poly(x*y + 1).integrate(y, y) == Poly(x*y**3/6 + y**2/2) + + assert Poly(x*y + 1).integrate((x, 2)) == Poly(x**3*y/6 + x**2/2) + assert Poly(x*y + 1).integrate((y, 2)) == Poly(x*y**3/6 + y**2/2) + + assert Poly(x*y + 1).integrate(x, y) == Poly(x**2*y**2/4 + x*y) + assert Poly(x*y + 1).integrate(y, x) == Poly(x**2*y**2/4 + x*y) + + +def test_Poly_diff(): + assert Poly(x**2 + x).diff() == Poly(2*x + 1) + assert Poly(x**2 + x).diff(x) == Poly(2*x + 1) + assert Poly(x**2 + x).diff((x, 1)) == Poly(2*x + 1) + + assert Poly(x**2*y**2 + x*y).diff(x) == Poly(2*x*y**2 + y) + assert Poly(x**2*y**2 + x*y).diff(y) == Poly(2*x**2*y + x) + + assert Poly(x**2*y**2 + x*y).diff(x, x) == Poly(2*y**2, x, y) + assert Poly(x**2*y**2 + x*y).diff(y, y) == Poly(2*x**2, x, y) + + assert Poly(x**2*y**2 + x*y).diff((x, 2)) == Poly(2*y**2, x, y) + assert Poly(x**2*y**2 + x*y).diff((y, 2)) == Poly(2*x**2, x, y) + + assert Poly(x**2*y**2 + x*y).diff(x, y) == Poly(4*x*y + 1) + assert Poly(x**2*y**2 + x*y).diff(y, x) == Poly(4*x*y + 1) + + +def test_issue_9585(): + assert diff(Poly(x**2 + x)) == Poly(2*x + 1) + assert diff(Poly(x**2 + x), x, evaluate=False) == \ + Derivative(Poly(x**2 + x), x) + assert Derivative(Poly(x**2 + x), x).doit() == Poly(2*x + 1) + + +def test_Poly_eval(): + assert Poly(0, x).eval(7) == 0 + assert Poly(1, x).eval(7) == 1 + assert Poly(x, x).eval(7) == 7 + + assert Poly(0, x).eval(0, 7) == 0 + assert Poly(1, x).eval(0, 7) == 1 + assert Poly(x, x).eval(0, 7) == 7 + + assert Poly(0, x).eval(x, 7) == 0 + assert Poly(1, x).eval(x, 7) == 1 + assert Poly(x, x).eval(x, 7) == 7 + + assert Poly(0, x).eval('x', 7) == 0 + assert Poly(1, x).eval('x', 7) == 1 + assert Poly(x, x).eval('x', 7) == 7 + + raises(PolynomialError, lambda: Poly(1, x).eval(1, 7)) + raises(PolynomialError, lambda: Poly(1, x).eval(y, 7)) + raises(PolynomialError, lambda: Poly(1, x).eval('y', 7)) + + assert Poly(123, x, y).eval(7) == Poly(123, y) + assert Poly(2*y, x, y).eval(7) == Poly(2*y, y) + assert Poly(x*y, x, y).eval(7) == Poly(7*y, y) + + assert Poly(123, x, y).eval(x, 7) == Poly(123, y) + assert Poly(2*y, x, y).eval(x, 7) == Poly(2*y, y) + assert Poly(x*y, x, y).eval(x, 7) == Poly(7*y, y) + + assert Poly(123, x, y).eval(y, 7) == Poly(123, x) + assert Poly(2*y, x, y).eval(y, 7) == Poly(14, x) + assert Poly(x*y, x, y).eval(y, 7) == Poly(7*x, x) + + assert Poly(x*y + y, x, y).eval({x: 7}) == Poly(8*y, y) + assert Poly(x*y + y, x, y).eval({y: 7}) == Poly(7*x + 7, x) + + assert Poly(x*y + y, x, y).eval({x: 6, y: 7}) == 49 + assert Poly(x*y + y, x, y).eval({x: 7, y: 6}) == 48 + + assert Poly(x*y + y, x, y).eval((6, 7)) == 49 + assert Poly(x*y + y, x, y).eval([6, 7]) == 49 + + assert Poly(x + 1, domain='ZZ').eval(S.Half) == Rational(3, 2) + assert Poly(x + 1, domain='ZZ').eval(sqrt(2)) == sqrt(2) + 1 + + raises(ValueError, lambda: Poly(x*y + y, x, y).eval((6, 7, 8))) + raises(DomainError, lambda: Poly(x + 1, domain='ZZ').eval(S.Half, auto=False)) + + # issue 6344 + alpha = Symbol('alpha') + result = (2*alpha*z - 2*alpha + z**2 + 3)/(z**2 - 2*z + 1) + + f = Poly(x**2 + (alpha - 1)*x - alpha + 1, x, domain='ZZ[alpha]') + assert f.eval((z + 1)/(z - 1)) == result + + g = Poly(x**2 + (alpha - 1)*x - alpha + 1, x, y, domain='ZZ[alpha]') + assert g.eval((z + 1)/(z - 1)) == Poly(result, y, domain='ZZ(alpha,z)') + +def test_Poly___call__(): + f = Poly(2*x*y + 3*x + y + 2*z) + + assert f(2) == Poly(5*y + 2*z + 6) + assert f(2, 5) == Poly(2*z + 31) + assert f(2, 5, 7) == 45 + + +def test_parallel_poly_from_expr(): + assert parallel_poly_from_expr( + [x - 1, x**2 - 1], x)[0] == [Poly(x - 1, x), Poly(x**2 - 1, x)] + assert parallel_poly_from_expr( + [Poly(x - 1, x), x**2 - 1], x)[0] == [Poly(x - 1, x), Poly(x**2 - 1, x)] + assert parallel_poly_from_expr( + [x - 1, Poly(x**2 - 1, x)], x)[0] == [Poly(x - 1, x), Poly(x**2 - 1, x)] + assert parallel_poly_from_expr([Poly( + x - 1, x), Poly(x**2 - 1, x)], x)[0] == [Poly(x - 1, x), Poly(x**2 - 1, x)] + + assert parallel_poly_from_expr( + [x - 1, x**2 - 1], x, y)[0] == [Poly(x - 1, x, y), Poly(x**2 - 1, x, y)] + assert parallel_poly_from_expr([Poly( + x - 1, x), x**2 - 1], x, y)[0] == [Poly(x - 1, x, y), Poly(x**2 - 1, x, y)] + assert parallel_poly_from_expr([x - 1, Poly( + x**2 - 1, x)], x, y)[0] == [Poly(x - 1, x, y), Poly(x**2 - 1, x, y)] + assert parallel_poly_from_expr([Poly(x - 1, x), Poly( + x**2 - 1, x)], x, y)[0] == [Poly(x - 1, x, y), Poly(x**2 - 1, x, y)] + + assert parallel_poly_from_expr( + [x - 1, x**2 - 1])[0] == [Poly(x - 1, x), Poly(x**2 - 1, x)] + assert parallel_poly_from_expr( + [Poly(x - 1, x), x**2 - 1])[0] == [Poly(x - 1, x), Poly(x**2 - 1, x)] + assert parallel_poly_from_expr( + [x - 1, Poly(x**2 - 1, x)])[0] == [Poly(x - 1, x), Poly(x**2 - 1, x)] + assert parallel_poly_from_expr( + [Poly(x - 1, x), Poly(x**2 - 1, x)])[0] == [Poly(x - 1, x), Poly(x**2 - 1, x)] + + assert parallel_poly_from_expr( + [1, x**2 - 1])[0] == [Poly(1, x), Poly(x**2 - 1, x)] + assert parallel_poly_from_expr( + [1, x**2 - 1])[0] == [Poly(1, x), Poly(x**2 - 1, x)] + assert parallel_poly_from_expr( + [1, Poly(x**2 - 1, x)])[0] == [Poly(1, x), Poly(x**2 - 1, x)] + assert parallel_poly_from_expr( + [1, Poly(x**2 - 1, x)])[0] == [Poly(1, x), Poly(x**2 - 1, x)] + + assert parallel_poly_from_expr( + [x**2 - 1, 1])[0] == [Poly(x**2 - 1, x), Poly(1, x)] + assert parallel_poly_from_expr( + [x**2 - 1, 1])[0] == [Poly(x**2 - 1, x), Poly(1, x)] + assert parallel_poly_from_expr( + [Poly(x**2 - 1, x), 1])[0] == [Poly(x**2 - 1, x), Poly(1, x)] + assert parallel_poly_from_expr( + [Poly(x**2 - 1, x), 1])[0] == [Poly(x**2 - 1, x), Poly(1, x)] + + assert parallel_poly_from_expr([Poly(x, x, y), Poly(y, x, y)], x, y, order='lex')[0] == \ + [Poly(x, x, y, domain='ZZ'), Poly(y, x, y, domain='ZZ')] + + raises(PolificationFailed, lambda: parallel_poly_from_expr([0, 1])) + + +def test_pdiv(): + f, g = x**2 - y**2, x - y + q, r = x + y, 0 + + F, G, Q, R = [ Poly(h, x, y) for h in (f, g, q, r) ] + + assert F.pdiv(G) == (Q, R) + assert F.prem(G) == R + assert F.pquo(G) == Q + assert F.pexquo(G) == Q + + assert pdiv(f, g) == (q, r) + assert prem(f, g) == r + assert pquo(f, g) == q + assert pexquo(f, g) == q + + assert pdiv(f, g, x, y) == (q, r) + assert prem(f, g, x, y) == r + assert pquo(f, g, x, y) == q + assert pexquo(f, g, x, y) == q + + assert pdiv(f, g, (x, y)) == (q, r) + assert prem(f, g, (x, y)) == r + assert pquo(f, g, (x, y)) == q + assert pexquo(f, g, (x, y)) == q + + assert pdiv(F, G) == (Q, R) + assert prem(F, G) == R + assert pquo(F, G) == Q + assert pexquo(F, G) == Q + + assert pdiv(f, g, polys=True) == (Q, R) + assert prem(f, g, polys=True) == R + assert pquo(f, g, polys=True) == Q + assert pexquo(f, g, polys=True) == Q + + assert pdiv(F, G, polys=False) == (q, r) + assert prem(F, G, polys=False) == r + assert pquo(F, G, polys=False) == q + assert pexquo(F, G, polys=False) == q + + raises(ComputationFailed, lambda: pdiv(4, 2)) + raises(ComputationFailed, lambda: prem(4, 2)) + raises(ComputationFailed, lambda: pquo(4, 2)) + raises(ComputationFailed, lambda: pexquo(4, 2)) + + +def test_div(): + f, g = x**2 - y**2, x - y + q, r = x + y, 0 + + F, G, Q, R = [ Poly(h, x, y) for h in (f, g, q, r) ] + + assert F.div(G) == (Q, R) + assert F.rem(G) == R + assert F.quo(G) == Q + assert F.exquo(G) == Q + + assert div(f, g) == (q, r) + assert rem(f, g) == r + assert quo(f, g) == q + assert exquo(f, g) == q + + assert div(f, g, x, y) == (q, r) + assert rem(f, g, x, y) == r + assert quo(f, g, x, y) == q + assert exquo(f, g, x, y) == q + + assert div(f, g, (x, y)) == (q, r) + assert rem(f, g, (x, y)) == r + assert quo(f, g, (x, y)) == q + assert exquo(f, g, (x, y)) == q + + assert div(F, G) == (Q, R) + assert rem(F, G) == R + assert quo(F, G) == Q + assert exquo(F, G) == Q + + assert div(f, g, polys=True) == (Q, R) + assert rem(f, g, polys=True) == R + assert quo(f, g, polys=True) == Q + assert exquo(f, g, polys=True) == Q + + assert div(F, G, polys=False) == (q, r) + assert rem(F, G, polys=False) == r + assert quo(F, G, polys=False) == q + assert exquo(F, G, polys=False) == q + + raises(ComputationFailed, lambda: div(4, 2)) + raises(ComputationFailed, lambda: rem(4, 2)) + raises(ComputationFailed, lambda: quo(4, 2)) + raises(ComputationFailed, lambda: exquo(4, 2)) + + f, g = x**2 + 1, 2*x - 4 + + qz, rz = 0, x**2 + 1 + qq, rq = x/2 + 1, 5 + + assert div(f, g) == (qq, rq) + assert div(f, g, auto=True) == (qq, rq) + assert div(f, g, auto=False) == (qz, rz) + assert div(f, g, domain=ZZ) == (qz, rz) + assert div(f, g, domain=QQ) == (qq, rq) + assert div(f, g, domain=ZZ, auto=True) == (qq, rq) + assert div(f, g, domain=ZZ, auto=False) == (qz, rz) + assert div(f, g, domain=QQ, auto=True) == (qq, rq) + assert div(f, g, domain=QQ, auto=False) == (qq, rq) + + assert rem(f, g) == rq + assert rem(f, g, auto=True) == rq + assert rem(f, g, auto=False) == rz + assert rem(f, g, domain=ZZ) == rz + assert rem(f, g, domain=QQ) == rq + assert rem(f, g, domain=ZZ, auto=True) == rq + assert rem(f, g, domain=ZZ, auto=False) == rz + assert rem(f, g, domain=QQ, auto=True) == rq + assert rem(f, g, domain=QQ, auto=False) == rq + + assert quo(f, g) == qq + assert quo(f, g, auto=True) == qq + assert quo(f, g, auto=False) == qz + assert quo(f, g, domain=ZZ) == qz + assert quo(f, g, domain=QQ) == qq + assert quo(f, g, domain=ZZ, auto=True) == qq + assert quo(f, g, domain=ZZ, auto=False) == qz + assert quo(f, g, domain=QQ, auto=True) == qq + assert quo(f, g, domain=QQ, auto=False) == qq + + f, g, q = x**2, 2*x, x/2 + + assert exquo(f, g) == q + assert exquo(f, g, auto=True) == q + raises(ExactQuotientFailed, lambda: exquo(f, g, auto=False)) + raises(ExactQuotientFailed, lambda: exquo(f, g, domain=ZZ)) + assert exquo(f, g, domain=QQ) == q + assert exquo(f, g, domain=ZZ, auto=True) == q + raises(ExactQuotientFailed, lambda: exquo(f, g, domain=ZZ, auto=False)) + assert exquo(f, g, domain=QQ, auto=True) == q + assert exquo(f, g, domain=QQ, auto=False) == q + + f, g = Poly(x**2), Poly(x) + + q, r = f.div(g) + assert q.get_domain().is_ZZ and r.get_domain().is_ZZ + r = f.rem(g) + assert r.get_domain().is_ZZ + q = f.quo(g) + assert q.get_domain().is_ZZ + q = f.exquo(g) + assert q.get_domain().is_ZZ + + f, g = Poly(x+y, x), Poly(2*x+y, x) + q, r = f.div(g) + assert q.get_domain().is_Frac and r.get_domain().is_Frac + + # https://github.com/sympy/sympy/issues/19579 + p = Poly(2+3*I, x, domain=ZZ_I) + q = Poly(1-I, x, domain=ZZ_I) + assert p.div(q, auto=False) == \ + (Poly(0, x, domain='ZZ_I'), Poly(2 + 3*I, x, domain='ZZ_I')) + assert p.div(q, auto=True) == \ + (Poly(-S(1)/2 + 5*I/2, x, domain='QQ_I'), Poly(0, x, domain='QQ_I')) + + f = 5*x**2 + 10*x + 3 + g = 2*x + 2 + assert div(f, g, domain=ZZ) == (0, f) + + +def test_issue_7864(): + q, r = div(a, .408248290463863*a) + assert abs(q - 2.44948974278318) < 1e-14 + assert r == 0 + + +def test_gcdex(): + f, g = 2*x, x**2 - 16 + s, t, h = x/32, Rational(-1, 16), 1 + + F, G, S, T, H = [ Poly(u, x, domain='QQ') for u in (f, g, s, t, h) ] + + assert F.half_gcdex(G) == (S, H) + assert F.gcdex(G) == (S, T, H) + assert F.invert(G) == S + + assert half_gcdex(f, g) == (s, h) + assert gcdex(f, g) == (s, t, h) + assert invert(f, g) == s + + assert half_gcdex(f, g, x) == (s, h) + assert gcdex(f, g, x) == (s, t, h) + assert invert(f, g, x) == s + + assert half_gcdex(f, g, (x,)) == (s, h) + assert gcdex(f, g, (x,)) == (s, t, h) + assert invert(f, g, (x,)) == s + + assert half_gcdex(F, G) == (S, H) + assert gcdex(F, G) == (S, T, H) + assert invert(F, G) == S + + assert half_gcdex(f, g, polys=True) == (S, H) + assert gcdex(f, g, polys=True) == (S, T, H) + assert invert(f, g, polys=True) == S + + assert half_gcdex(F, G, polys=False) == (s, h) + assert gcdex(F, G, polys=False) == (s, t, h) + assert invert(F, G, polys=False) == s + + assert half_gcdex(100, 2004) == (-20, 4) + assert gcdex(100, 2004) == (-20, 1, 4) + assert invert(3, 7) == 5 + + raises(DomainError, lambda: half_gcdex(x + 1, 2*x + 1, auto=False)) + raises(DomainError, lambda: gcdex(x + 1, 2*x + 1, auto=False)) + raises(DomainError, lambda: invert(x + 1, 2*x + 1, auto=False)) + + +def test_revert(): + f = Poly(1 - x**2/2 + x**4/24 - x**6/720) + g = Poly(61*x**6/720 + 5*x**4/24 + x**2/2 + 1) + + assert f.revert(8) == g + + +def test_subresultants(): + f, g, h = x**2 - 2*x + 1, x**2 - 1, 2*x - 2 + F, G, H = Poly(f), Poly(g), Poly(h) + + assert F.subresultants(G) == [F, G, H] + assert subresultants(f, g) == [f, g, h] + assert subresultants(f, g, x) == [f, g, h] + assert subresultants(f, g, (x,)) == [f, g, h] + assert subresultants(F, G) == [F, G, H] + assert subresultants(f, g, polys=True) == [F, G, H] + assert subresultants(F, G, polys=False) == [f, g, h] + + raises(ComputationFailed, lambda: subresultants(4, 2)) + + +def test_resultant(): + f, g, h = x**2 - 2*x + 1, x**2 - 1, 0 + F, G = Poly(f), Poly(g) + + assert F.resultant(G) == h + assert resultant(f, g) == h + assert resultant(f, g, x) == h + assert resultant(f, g, (x,)) == h + assert resultant(F, G) == h + assert resultant(f, g, polys=True) == h + assert resultant(F, G, polys=False) == h + assert resultant(f, g, includePRS=True) == (h, [f, g, 2*x - 2]) + + f, g, h = x - a, x - b, a - b + F, G, H = Poly(f), Poly(g), Poly(h) + + assert F.resultant(G) == H + assert resultant(f, g) == h + assert resultant(f, g, x) == h + assert resultant(f, g, (x,)) == h + assert resultant(F, G) == H + assert resultant(f, g, polys=True) == H + assert resultant(F, G, polys=False) == h + + raises(ComputationFailed, lambda: resultant(4, 2)) + + +def test_discriminant(): + f, g = x**3 + 3*x**2 + 9*x - 13, -11664 + F = Poly(f) + + assert F.discriminant() == g + assert discriminant(f) == g + assert discriminant(f, x) == g + assert discriminant(f, (x,)) == g + assert discriminant(F) == g + assert discriminant(f, polys=True) == g + assert discriminant(F, polys=False) == g + + f, g = a*x**2 + b*x + c, b**2 - 4*a*c + F, G = Poly(f), Poly(g) + + assert F.discriminant() == G + assert discriminant(f) == g + assert discriminant(f, x, a, b, c) == g + assert discriminant(f, (x, a, b, c)) == g + assert discriminant(F) == G + assert discriminant(f, polys=True) == G + assert discriminant(F, polys=False) == g + + raises(ComputationFailed, lambda: discriminant(4)) + + +def test_dispersion(): + # We test only the API here. For more mathematical + # tests see the dedicated test file. + fp = poly((x + 1)*(x + 2), x) + assert sorted(fp.dispersionset()) == [0, 1] + assert fp.dispersion() == 1 + + fp = poly(x**4 - 3*x**2 + 1, x) + gp = fp.shift(-3) + assert sorted(fp.dispersionset(gp)) == [2, 3, 4] + assert fp.dispersion(gp) == 4 + + +def test_gcd_list(): + F = [x**3 - 1, x**2 - 1, x**2 - 3*x + 2] + + assert gcd_list(F) == x - 1 + assert gcd_list(F, polys=True) == Poly(x - 1) + + assert gcd_list([]) == 0 + assert gcd_list([1, 2]) == 1 + assert gcd_list([4, 6, 8]) == 2 + + assert gcd_list([x*(y + 42) - x*y - x*42]) == 0 + + gcd = gcd_list([], x) + assert gcd.is_Number and gcd is S.Zero + + gcd = gcd_list([], x, polys=True) + assert gcd.is_Poly and gcd.is_zero + + a = sqrt(2) + assert gcd_list([a, -a]) == gcd_list([-a, a]) == a + + raises(ComputationFailed, lambda: gcd_list([], polys=True)) + + +def test_lcm_list(): + F = [x**3 - 1, x**2 - 1, x**2 - 3*x + 2] + + assert lcm_list(F) == x**5 - x**4 - 2*x**3 - x**2 + x + 2 + assert lcm_list(F, polys=True) == Poly(x**5 - x**4 - 2*x**3 - x**2 + x + 2) + + assert lcm_list([]) == 1 + assert lcm_list([1, 2]) == 2 + assert lcm_list([4, 6, 8]) == 24 + + assert lcm_list([x*(y + 42) - x*y - x*42]) == 0 + + lcm = lcm_list([], x) + assert lcm.is_Number and lcm is S.One + + lcm = lcm_list([], x, polys=True) + assert lcm.is_Poly and lcm.is_one + + raises(ComputationFailed, lambda: lcm_list([], polys=True)) + + +def test_gcd(): + f, g = x**3 - 1, x**2 - 1 + s, t = x**2 + x + 1, x + 1 + h, r = x - 1, x**4 + x**3 - x - 1 + + F, G, S, T, H, R = [ Poly(u) for u in (f, g, s, t, h, r) ] + + assert F.cofactors(G) == (H, S, T) + assert F.gcd(G) == H + assert F.lcm(G) == R + + assert cofactors(f, g) == (h, s, t) + assert gcd(f, g) == h + assert lcm(f, g) == r + + assert cofactors(f, g, x) == (h, s, t) + assert gcd(f, g, x) == h + assert lcm(f, g, x) == r + + assert cofactors(f, g, (x,)) == (h, s, t) + assert gcd(f, g, (x,)) == h + assert lcm(f, g, (x,)) == r + + assert cofactors(F, G) == (H, S, T) + assert gcd(F, G) == H + assert lcm(F, G) == R + + assert cofactors(f, g, polys=True) == (H, S, T) + assert gcd(f, g, polys=True) == H + assert lcm(f, g, polys=True) == R + + assert cofactors(F, G, polys=False) == (h, s, t) + assert gcd(F, G, polys=False) == h + assert lcm(F, G, polys=False) == r + + f, g = 1.0*x**2 - 1.0, 1.0*x - 1.0 + h, s, t = g, 1.0*x + 1.0, 1.0 + + assert cofactors(f, g) == (h, s, t) + assert gcd(f, g) == h + assert lcm(f, g) == f + + f, g = 1.0*x**2 - 1.0, 1.0*x - 1.0 + h, s, t = g, 1.0*x + 1.0, 1.0 + + assert cofactors(f, g) == (h, s, t) + assert gcd(f, g) == h + assert lcm(f, g) == f + + assert cofactors(8, 6) == (2, 4, 3) + assert gcd(8, 6) == 2 + assert lcm(8, 6) == 24 + + f, g = x**2 - 3*x - 4, x**3 - 4*x**2 + x - 4 + l = x**4 - 3*x**3 - 3*x**2 - 3*x - 4 + h, s, t = x - 4, x + 1, x**2 + 1 + + assert cofactors(f, g, modulus=11) == (h, s, t) + assert gcd(f, g, modulus=11) == h + assert lcm(f, g, modulus=11) == l + + f, g = x**2 + 8*x + 7, x**3 + 7*x**2 + x + 7 + l = x**4 + 8*x**3 + 8*x**2 + 8*x + 7 + h, s, t = x + 7, x + 1, x**2 + 1 + + assert cofactors(f, g, modulus=11, symmetric=False) == (h, s, t) + assert gcd(f, g, modulus=11, symmetric=False) == h + assert lcm(f, g, modulus=11, symmetric=False) == l + + a, b = sqrt(2), -sqrt(2) + assert gcd(a, b) == gcd(b, a) == sqrt(2) + + a, b = sqrt(-2), -sqrt(-2) + assert gcd(a, b) == gcd(b, a) == sqrt(2) + + assert gcd(Poly(x - 2, x), Poly(I*x, x)) == Poly(1, x, domain=ZZ_I) + + raises(TypeError, lambda: gcd(x)) + raises(TypeError, lambda: lcm(x)) + + f = Poly(-1, x) + g = Poly(1, x) + assert lcm(f, g) == Poly(1, x) + + f = Poly(0, x) + g = Poly([1, 1], x) + for i in (f, g): + assert lcm(i, 0) == 0 + assert lcm(0, i) == 0 + assert lcm(i, f) == 0 + assert lcm(f, i) == 0 + + f = 4*x**2 + x + 2 + pfz = Poly(f, domain=ZZ) + pfq = Poly(f, domain=QQ) + + assert pfz.gcd(pfz) == pfz + assert pfz.lcm(pfz) == pfz + assert pfq.gcd(pfq) == pfq.monic() + assert pfq.lcm(pfq) == pfq.monic() + assert gcd(f, f) == f + assert lcm(f, f) == f + assert gcd(f, f, domain=QQ) == monic(f) + assert lcm(f, f, domain=QQ) == monic(f) + + +def test_gcd_numbers_vs_polys(): + assert isinstance(gcd(3, 9), Integer) + assert isinstance(gcd(3*x, 9), Integer) + + assert gcd(3, 9) == 3 + assert gcd(3*x, 9) == 3 + + assert isinstance(gcd(Rational(3, 2), Rational(9, 4)), Rational) + assert isinstance(gcd(Rational(3, 2)*x, Rational(9, 4)), Rational) + + assert gcd(Rational(3, 2), Rational(9, 4)) == Rational(3, 4) + assert gcd(Rational(3, 2)*x, Rational(9, 4)) == 1 + + assert isinstance(gcd(3.0, 9.0), Float) + assert isinstance(gcd(3.0*x, 9.0), Float) + + assert gcd(3.0, 9.0) == 1.0 + assert gcd(3.0*x, 9.0) == 1.0 + + # partial fix of 20597 + assert gcd(Mul(2, 3, evaluate=False), 2) == 2 + + +def test_terms_gcd(): + assert terms_gcd(1) == 1 + assert terms_gcd(1, x) == 1 + + assert terms_gcd(x - 1) == x - 1 + assert terms_gcd(-x - 1) == -x - 1 + + assert terms_gcd(2*x + 3) == 2*x + 3 + assert terms_gcd(6*x + 4) == Mul(2, 3*x + 2, evaluate=False) + + assert terms_gcd(x**3*y + x*y**3) == x*y*(x**2 + y**2) + assert terms_gcd(2*x**3*y + 2*x*y**3) == 2*x*y*(x**2 + y**2) + assert terms_gcd(x**3*y/2 + x*y**3/2) == x*y/2*(x**2 + y**2) + + assert terms_gcd(x**3*y + 2*x*y**3) == x*y*(x**2 + 2*y**2) + assert terms_gcd(2*x**3*y + 4*x*y**3) == 2*x*y*(x**2 + 2*y**2) + assert terms_gcd(2*x**3*y/3 + 4*x*y**3/5) == x*y*Rational(2, 15)*(5*x**2 + 6*y**2) + + assert terms_gcd(2.0*x**3*y + 4.1*x*y**3) == x*y*(2.0*x**2 + 4.1*y**2) + assert _aresame(terms_gcd(2.0*x + 3), 2.0*x + 3) + + assert terms_gcd((3 + 3*x)*(x + x*y), expand=False) == \ + (3*x + 3)*(x*y + x) + assert terms_gcd((3 + 3*x)*(x + x*sin(3 + 3*y)), expand=False, deep=True) == \ + 3*x*(x + 1)*(sin(Mul(3, y + 1, evaluate=False)) + 1) + assert terms_gcd(sin(x + x*y), deep=True) == \ + sin(x*(y + 1)) + + eq = Eq(2*x, 2*y + 2*z*y) + assert terms_gcd(eq) == Eq(2*x, 2*y*(z + 1)) + assert terms_gcd(eq, deep=True) == Eq(2*x, 2*y*(z + 1)) + + raises(TypeError, lambda: terms_gcd(x < 2)) + + +def test_trunc(): + f, g = x**5 + 2*x**4 + 3*x**3 + 4*x**2 + 5*x + 6, x**5 - x**4 + x**2 - x + F, G = Poly(f), Poly(g) + + assert F.trunc(3) == G + assert trunc(f, 3) == g + assert trunc(f, 3, x) == g + assert trunc(f, 3, (x,)) == g + assert trunc(F, 3) == G + assert trunc(f, 3, polys=True) == G + assert trunc(F, 3, polys=False) == g + + f, g = 6*x**5 + 5*x**4 + 4*x**3 + 3*x**2 + 2*x + 1, -x**4 + x**3 - x + 1 + F, G = Poly(f), Poly(g) + + assert F.trunc(3) == G + assert trunc(f, 3) == g + assert trunc(f, 3, x) == g + assert trunc(f, 3, (x,)) == g + assert trunc(F, 3) == G + assert trunc(f, 3, polys=True) == G + assert trunc(F, 3, polys=False) == g + + f = Poly(x**2 + 2*x + 3, modulus=5) + + assert f.trunc(2) == Poly(x**2 + 1, modulus=5) + + +def test_monic(): + f, g = 2*x - 1, x - S.Half + F, G = Poly(f, domain='QQ'), Poly(g) + + assert F.monic() == G + assert monic(f) == g + assert monic(f, x) == g + assert monic(f, (x,)) == g + assert monic(F) == G + assert monic(f, polys=True) == G + assert monic(F, polys=False) == g + + raises(ComputationFailed, lambda: monic(4)) + + assert monic(2*x**2 + 6*x + 4, auto=False) == x**2 + 3*x + 2 + raises(ExactQuotientFailed, lambda: monic(2*x + 6*x + 1, auto=False)) + + assert monic(2.0*x**2 + 6.0*x + 4.0) == 1.0*x**2 + 3.0*x + 2.0 + assert monic(2*x**2 + 3*x + 4, modulus=5) == x**2 - x + 2 + + +def test_content(): + f, F = 4*x + 2, Poly(4*x + 2) + + assert F.content() == 2 + assert content(f) == 2 + + raises(ComputationFailed, lambda: content(4)) + + f = Poly(2*x, modulus=3) + + assert f.content() == 1 + + +def test_primitive(): + f, g = 4*x + 2, 2*x + 1 + F, G = Poly(f), Poly(g) + + assert F.primitive() == (2, G) + assert primitive(f) == (2, g) + assert primitive(f, x) == (2, g) + assert primitive(f, (x,)) == (2, g) + assert primitive(F) == (2, G) + assert primitive(f, polys=True) == (2, G) + assert primitive(F, polys=False) == (2, g) + + raises(ComputationFailed, lambda: primitive(4)) + + f = Poly(2*x, modulus=3) + g = Poly(2.0*x, domain=RR) + + assert f.primitive() == (1, f) + assert g.primitive() == (1.0, g) + + assert primitive(S('-3*x/4 + y + 11/8')) == \ + S('(1/8, -6*x + 8*y + 11)') + + +def test_compose(): + f = x**12 + 20*x**10 + 150*x**8 + 500*x**6 + 625*x**4 - 2*x**3 - 10*x + 9 + g = x**4 - 2*x + 9 + h = x**3 + 5*x + + F, G, H = map(Poly, (f, g, h)) + + assert G.compose(H) == F + assert compose(g, h) == f + assert compose(g, h, x) == f + assert compose(g, h, (x,)) == f + assert compose(G, H) == F + assert compose(g, h, polys=True) == F + assert compose(G, H, polys=False) == f + + assert F.decompose() == [G, H] + assert decompose(f) == [g, h] + assert decompose(f, x) == [g, h] + assert decompose(f, (x,)) == [g, h] + assert decompose(F) == [G, H] + assert decompose(f, polys=True) == [G, H] + assert decompose(F, polys=False) == [g, h] + + raises(ComputationFailed, lambda: compose(4, 2)) + raises(ComputationFailed, lambda: decompose(4)) + + assert compose(x**2 - y**2, x - y, x, y) == x**2 - 2*x*y + assert compose(x**2 - y**2, x - y, y, x) == -y**2 + 2*x*y + + +def test_shift(): + assert Poly(x**2 - 2*x + 1, x).shift(2) == Poly(x**2 + 2*x + 1, x) + + +def test_shift_list(): + assert Poly(x*y, [x,y]).shift_list([1,2]) == Poly((x+1)*(y+2), [x,y]) + + +def test_transform(): + # Also test that 3-way unification is done correctly + assert Poly(x**2 - 2*x + 1, x).transform(Poly(x + 1), Poly(x - 1)) == \ + Poly(4, x) == \ + cancel((x - 1)**2*(x**2 - 2*x + 1).subs(x, (x + 1)/(x - 1))) + + assert Poly(x**2 - x/2 + 1, x).transform(Poly(x + 1), Poly(x - 1)) == \ + Poly(3*x**2/2 + Rational(5, 2), x) == \ + cancel((x - 1)**2*(x**2 - x/2 + 1).subs(x, (x + 1)/(x - 1))) + + assert Poly(x**2 - 2*x + 1, x).transform(Poly(x + S.Half), Poly(x - 1)) == \ + Poly(Rational(9, 4), x) == \ + cancel((x - 1)**2*(x**2 - 2*x + 1).subs(x, (x + S.Half)/(x - 1))) + + assert Poly(x**2 - 2*x + 1, x).transform(Poly(x + 1), Poly(x - S.Half)) == \ + Poly(Rational(9, 4), x) == \ + cancel((x - S.Half)**2*(x**2 - 2*x + 1).subs(x, (x + 1)/(x - S.Half))) + + # Unify ZZ, QQ, and RR + assert Poly(x**2 - 2*x + 1, x).transform(Poly(x + 1.0), Poly(x - S.Half)) == \ + Poly(Rational(9, 4), x, domain='RR') == \ + cancel((x - S.Half)**2*(x**2 - 2*x + 1).subs(x, (x + 1.0)/(x - S.Half))) + + raises(ValueError, lambda: Poly(x*y).transform(Poly(x + 1), Poly(x - 1))) + raises(ValueError, lambda: Poly(x).transform(Poly(y + 1), Poly(x - 1))) + raises(ValueError, lambda: Poly(x).transform(Poly(x + 1), Poly(y - 1))) + raises(ValueError, lambda: Poly(x).transform(Poly(x*y + 1), Poly(x - 1))) + raises(ValueError, lambda: Poly(x).transform(Poly(x + 1), Poly(x*y - 1))) + + +def test_sturm(): + f, F = x, Poly(x, domain='QQ') + g, G = 1, Poly(1, x, domain='QQ') + + assert F.sturm() == [F, G] + assert sturm(f) == [f, g] + assert sturm(f, x) == [f, g] + assert sturm(f, (x,)) == [f, g] + assert sturm(F) == [F, G] + assert sturm(f, polys=True) == [F, G] + assert sturm(F, polys=False) == [f, g] + + raises(ComputationFailed, lambda: sturm(4)) + raises(DomainError, lambda: sturm(f, auto=False)) + + f = Poly(S(1024)/(15625*pi**8)*x**5 + - S(4096)/(625*pi**8)*x**4 + + S(32)/(15625*pi**4)*x**3 + - S(128)/(625*pi**4)*x**2 + + Rational(1, 62500)*x + - Rational(1, 625), x, domain='ZZ(pi)') + + assert sturm(f) == \ + [Poly(x**3 - 100*x**2 + pi**4/64*x - 25*pi**4/16, x, domain='ZZ(pi)'), + Poly(3*x**2 - 200*x + pi**4/64, x, domain='ZZ(pi)'), + Poly((Rational(20000, 9) - pi**4/96)*x + 25*pi**4/18, x, domain='ZZ(pi)'), + Poly((-3686400000000*pi**4 - 11520000*pi**8 - 9*pi**12)/(26214400000000 - 245760000*pi**4 + 576*pi**8), x, domain='ZZ(pi)')] + + +def test_gff(): + f = x**5 + 2*x**4 - x**3 - 2*x**2 + + assert Poly(f).gff_list() == [(Poly(x), 1), (Poly(x + 2), 4)] + assert gff_list(f) == [(x, 1), (x + 2, 4)] + + raises(NotImplementedError, lambda: gff(f)) + + f = x*(x - 1)**3*(x - 2)**2*(x - 4)**2*(x - 5) + + assert Poly(f).gff_list() == [( + Poly(x**2 - 5*x + 4), 1), (Poly(x**2 - 5*x + 4), 2), (Poly(x), 3)] + assert gff_list(f) == [(x**2 - 5*x + 4, 1), (x**2 - 5*x + 4, 2), (x, 3)] + + raises(NotImplementedError, lambda: gff(f)) + + +def test_norm(): + a, b = sqrt(2), sqrt(3) + f = Poly(a*x + b*y, x, y, extension=(a, b)) + assert f.norm() == Poly(4*x**4 - 12*x**2*y**2 + 9*y**4, x, y, domain='QQ') + + +def test_sqf_norm(): + assert sqf_norm(x**2 - 2, extension=sqrt(3)) == \ + ([1], x**2 - 2*sqrt(3)*x + 1, x**4 - 10*x**2 + 1) + assert sqf_norm(x**2 - 3, extension=sqrt(2)) == \ + ([1], x**2 - 2*sqrt(2)*x - 1, x**4 - 10*x**2 + 1) + + assert Poly(x**2 - 2, extension=sqrt(3)).sqf_norm() == \ + ([1], Poly(x**2 - 2*sqrt(3)*x + 1, x, extension=sqrt(3)), + Poly(x**4 - 10*x**2 + 1, x, domain='QQ')) + + assert Poly(x**2 - 3, extension=sqrt(2)).sqf_norm() == \ + ([1], Poly(x**2 - 2*sqrt(2)*x - 1, x, extension=sqrt(2)), + Poly(x**4 - 10*x**2 + 1, x, domain='QQ')) + + +def test_sqf(): + f = x**5 - x**3 - x**2 + 1 + g = x**3 + 2*x**2 + 2*x + 1 + h = x - 1 + + p = x**4 + x**3 - x - 1 + + F, G, H, P = map(Poly, (f, g, h, p)) + + assert F.sqf_part() == P + assert sqf_part(f) == p + assert sqf_part(f, x) == p + assert sqf_part(f, (x,)) == p + assert sqf_part(F) == P + assert sqf_part(f, polys=True) == P + assert sqf_part(F, polys=False) == p + + assert F.sqf_list() == (1, [(G, 1), (H, 2)]) + assert sqf_list(f) == (1, [(g, 1), (h, 2)]) + assert sqf_list(f, x) == (1, [(g, 1), (h, 2)]) + assert sqf_list(f, (x,)) == (1, [(g, 1), (h, 2)]) + assert sqf_list(F) == (1, [(G, 1), (H, 2)]) + assert sqf_list(f, polys=True) == (1, [(G, 1), (H, 2)]) + assert sqf_list(F, polys=False) == (1, [(g, 1), (h, 2)]) + + assert F.sqf_list_include() == [(G, 1), (H, 2)] + + raises(ComputationFailed, lambda: sqf_part(4)) + + assert sqf(1) == 1 + assert sqf_list(1) == (1, []) + + assert sqf((2*x**2 + 2)**7) == 128*(x**2 + 1)**7 + + assert sqf(f) == g*h**2 + assert sqf(f, x) == g*h**2 + assert sqf(f, (x,)) == g*h**2 + + d = x**2 + y**2 + + assert sqf(f/d) == (g*h**2)/d + assert sqf(f/d, x) == (g*h**2)/d + assert sqf(f/d, (x,)) == (g*h**2)/d + + assert sqf(x - 1) == x - 1 + assert sqf(-x - 1) == -x - 1 + + assert sqf(x - 1) == x - 1 + assert sqf(6*x - 10) == Mul(2, 3*x - 5, evaluate=False) + + assert sqf((6*x - 10)/(3*x - 6)) == Rational(2, 3)*((3*x - 5)/(x - 2)) + assert sqf(Poly(x**2 - 2*x + 1)) == (x - 1)**2 + + f = 3 + x - x*(1 + x) + x**2 + + assert sqf(f) == 3 + + f = (x**2 + 2*x + 1)**20000000000 + + assert sqf(f) == (x + 1)**40000000000 + assert sqf_list(f) == (1, [(x + 1, 40000000000)]) + + # https://github.com/sympy/sympy/issues/26497 + assert sqf(expand(((y - 2)**2 * (y + 2) * (x + 1)))) == \ + (y - 2)**2 * expand((y + 2) * (x + 1)) + assert sqf(expand(((y - 2)**2 * (y + 2) * (z + 1)))) == \ + (y - 2)**2 * expand((y + 2) * (z + 1)) + assert sqf(expand(((y - I)**2 * (y + I) * (x + 1)))) == \ + (y - I)**2 * expand((y + I) * (x + 1)) + assert sqf(expand(((y - I)**2 * (y + I) * (z + 1)))) == \ + (y - I)**2 * expand((y + I) * (z + 1)) + + # Check that factors are combined and sorted. + p = (x - 2)**2*(x - 1)*(x + y)**2*(y - 2)**2*(y - 1) + assert Poly(p).sqf_list() == (1, [ + (Poly(x*y - x - y + 1), 1), + (Poly(x**2*y - 2*x**2 + x*y**2 - 4*x*y + 4*x - 2*y**2 + 4*y), 2) + ]) + + +def test_factor(): + f = x**5 - x**3 - x**2 + 1 + + u = x + 1 + v = x - 1 + w = x**2 + x + 1 + + F, U, V, W = map(Poly, (f, u, v, w)) + + assert F.factor_list() == (1, [(U, 1), (V, 2), (W, 1)]) + assert factor_list(f) == (1, [(u, 1), (v, 2), (w, 1)]) + assert factor_list(f, x) == (1, [(u, 1), (v, 2), (w, 1)]) + assert factor_list(f, (x,)) == (1, [(u, 1), (v, 2), (w, 1)]) + assert factor_list(F) == (1, [(U, 1), (V, 2), (W, 1)]) + assert factor_list(f, polys=True) == (1, [(U, 1), (V, 2), (W, 1)]) + assert factor_list(F, polys=False) == (1, [(u, 1), (v, 2), (w, 1)]) + + assert F.factor_list_include() == [(U, 1), (V, 2), (W, 1)] + + assert factor_list(1) == (1, []) + assert factor_list(6) == (6, []) + assert factor_list(sqrt(3), x) == (sqrt(3), []) + assert factor_list((-1)**x, x) == (1, [(-1, x)]) + assert factor_list((2*x)**y, x) == (1, [(2, y), (x, y)]) + assert factor_list(sqrt(x*y), x) == (1, [(x*y, S.Half)]) + + assert factor(6) == 6 and factor(6).is_Integer + + assert factor_list(3*x) == (3, [(x, 1)]) + assert factor_list(3*x**2) == (3, [(x, 2)]) + + assert factor(3*x) == 3*x + assert factor(3*x**2) == 3*x**2 + + assert factor((2*x**2 + 2)**7) == 128*(x**2 + 1)**7 + + assert factor(f) == u*v**2*w + assert factor(f, x) == u*v**2*w + assert factor(f, (x,)) == u*v**2*w + + g, p, q, r = x**2 - y**2, x - y, x + y, x**2 + 1 + + assert factor(f/g) == (u*v**2*w)/(p*q) + assert factor(f/g, x) == (u*v**2*w)/(p*q) + assert factor(f/g, (x,)) == (u*v**2*w)/(p*q) + + p = Symbol('p', positive=True) + i = Symbol('i', integer=True) + r = Symbol('r', real=True) + + assert factor(sqrt(x*y)).is_Pow is True + + assert factor(sqrt(3*x**2 - 3)) == sqrt(3)*sqrt((x - 1)*(x + 1)) + assert factor(sqrt(3*x**2 + 3)) == sqrt(3)*sqrt(x**2 + 1) + + assert factor((y*x**2 - y)**i) == y**i*(x - 1)**i*(x + 1)**i + assert factor((y*x**2 + y)**i) == y**i*(x**2 + 1)**i + + assert factor((y*x**2 - y)**t) == (y*(x - 1)*(x + 1))**t + assert factor((y*x**2 + y)**t) == (y*(x**2 + 1))**t + + f = sqrt(expand((r**2 + 1)*(p + 1)*(p - 1)*(p - 2)**3)) + g = sqrt((p - 2)**3*(p - 1))*sqrt(p + 1)*sqrt(r**2 + 1) + + assert factor(f) == g + assert factor(g) == g + + g = (x - 1)**5*(r**2 + 1) + f = sqrt(expand(g)) + + assert factor(f) == sqrt(g) + + f = Poly(sin(1)*x + 1, x, domain=EX) + + assert f.factor_list() == (1, [(f, 1)]) + + f = x**4 + 1 + + assert factor(f) == f + assert factor(f, extension=I) == (x**2 - I)*(x**2 + I) + assert factor(f, gaussian=True) == (x**2 - I)*(x**2 + I) + assert factor( + f, extension=sqrt(2)) == (x**2 + sqrt(2)*x + 1)*(x**2 - sqrt(2)*x + 1) + + assert factor(x**2 + 4*I*x - 4) == (x + 2*I)**2 + + f = x**2 + 2*I*x - 4 + + assert factor(f) == f + + f = 8192*x**2 + x*(22656 + 175232*I) - 921416 + 242313*I + f_zzi = I*(x*(64 - 64*I) + 773 + 596*I)**2 + f_qqi = 8192*(x + S(177)/128 + 1369*I/128)**2 + + assert factor(f) == f_zzi + assert factor(f, domain=ZZ_I) == f_zzi + assert factor(f, domain=QQ_I) == f_qqi + + f = x**2 + 2*sqrt(2)*x + 2 + + assert factor(f, extension=sqrt(2)) == (x + sqrt(2))**2 + assert factor(f**3, extension=sqrt(2)) == (x + sqrt(2))**6 + + assert factor(x**2 - 2*y**2, extension=sqrt(2)) == \ + (x + sqrt(2)*y)*(x - sqrt(2)*y) + assert factor(2*x**2 - 4*y**2, extension=sqrt(2)) == \ + 2*((x + sqrt(2)*y)*(x - sqrt(2)*y)) + + assert factor(x - 1) == x - 1 + assert factor(-x - 1) == -x - 1 + + assert factor(x - 1) == x - 1 + + assert factor(6*x - 10) == Mul(2, 3*x - 5, evaluate=False) + + assert factor(x**11 + x + 1, modulus=65537, symmetric=True) == \ + (x**2 + x + 1)*(x**9 - x**8 + x**6 - x**5 + x**3 - x** 2 + 1) + assert factor(x**11 + x + 1, modulus=65537, symmetric=False) == \ + (x**2 + x + 1)*(x**9 + 65536*x**8 + x**6 + 65536*x**5 + + x**3 + 65536*x** 2 + 1) + + f = x/pi + x*sin(x)/pi + g = y/(pi**2 + 2*pi + 1) + y*sin(x)/(pi**2 + 2*pi + 1) + + assert factor(f) == x*(sin(x) + 1)/pi + assert factor(g) == y*(sin(x) + 1)/(pi + 1)**2 + + assert factor(Eq( + x**2 + 2*x + 1, x**3 + 1)) == Eq((x + 1)**2, (x + 1)*(x**2 - x + 1)) + + f = (x**2 - 1)/(x**2 + 4*x + 4) + + assert factor(f) == (x + 1)*(x - 1)/(x + 2)**2 + assert factor(f, x) == (x + 1)*(x - 1)/(x + 2)**2 + + f = 3 + x - x*(1 + x) + x**2 + + assert factor(f) == 3 + assert factor(f, x) == 3 + + assert factor(1/(x**2 + 2*x + 1/x) - 1) == -((1 - x + 2*x**2 + + x**3)/(1 + 2*x**2 + x**3)) + + assert factor(f, expand=False) == f + raises(PolynomialError, lambda: factor(f, x, expand=False)) + + raises(FlagError, lambda: factor(x**2 - 1, polys=True)) + + assert factor([x, Eq(x**2 - y**2, Tuple(x**2 - z**2, 1/x + 1/y))]) == \ + [x, Eq((x - y)*(x + y), Tuple((x - z)*(x + z), (x + y)/x/y))] + + assert not isinstance( + Poly(x**3 + x + 1).factor_list()[1][0][0], PurePoly) is True + assert isinstance( + PurePoly(x**3 + x + 1).factor_list()[1][0][0], PurePoly) is True + + assert factor(sqrt(-x)) == sqrt(-x) + + # issue 5917 + e = (-2*x*(-x + 1)*(x - 1)*(-x*(-x + 1)*(x - 1) - x*(x - 1)**2)*(x**2*(x - + 1) - x*(x - 1) - x) - (-2*x**2*(x - 1)**2 - x*(-x + 1)*(-x*(-x + 1) + + x*(x - 1)))*(x**2*(x - 1)**4 - x*(-x*(-x + 1)*(x - 1) - x*(x - 1)**2))) + assert factor(e) == 0 + + # deep option + assert factor(sin(x**2 + x) + x, deep=True) == sin(x*(x + 1)) + x + assert factor(sin(x**2 + x)*x, deep=True) == sin(x*(x + 1))*x + + assert factor(sqrt(x**2)) == sqrt(x**2) + + # issue 13149 + assert factor(expand((0.5*x+1)*(0.5*y+1))) == Mul(1.0, 0.5*x + 1.0, + 0.5*y + 1.0, evaluate = False) + assert factor(expand((0.5*x+0.5)**2)) == 0.25*(1.0*x + 1.0)**2 + + eq = x**2*y**2 + 11*x**2*y + 30*x**2 + 7*x*y**2 + 77*x*y + 210*x + 12*y**2 + 132*y + 360 + assert factor(eq, x) == (x + 3)*(x + 4)*(y**2 + 11*y + 30) + assert factor(eq, x, deep=True) == (x + 3)*(x + 4)*(y**2 + 11*y + 30) + assert factor(eq, y, deep=True) == (y + 5)*(y + 6)*(x**2 + 7*x + 12) + + # fraction option + f = 5*x + 3*exp(2 - 7*x) + assert factor(f, deep=True) == factor(f, deep=True, fraction=True) + assert factor(f, deep=True, fraction=False) == 5*x + 3*exp(2)*exp(-7*x) + + assert factor_list(x**3 - x*y**2, t, w, x) == ( + 1, [(x, 1), (x - y, 1), (x + y, 1)]) + + # https://github.com/sympy/sympy/issues/24952 + s2, s2p, s2n = sqrt(2), 1 + sqrt(2), 1 - sqrt(2) + pip, pin = 1 + pi, 1 - pi + assert factor_list(s2p*s2n) == (-1, [(-s2n, 1), (s2p, 1)]) + assert factor_list(pip*pin) == (-1, [(-pin, 1), (pip, 1)]) + # Not sure about this one. Maybe coeff should be 1 or -1? + assert factor_list(s2*s2n) == (-s2, [(-s2n, 1)]) + assert factor_list(pi*pin) == (-1, [(-pin, 1), (pi, 1)]) + assert factor_list(s2p*s2n, x) == (s2p*s2n, []) + assert factor_list(pip*pin, x) == (pip*pin, []) + assert factor_list(s2*s2n, x) == (s2*s2n, []) + assert factor_list(pi*pin, x) == (pi*pin, []) + assert factor_list((x - sqrt(2)*pi)*(x + sqrt(2)*pi), x) == ( + 1, [(x - sqrt(2)*pi, 1), (x + sqrt(2)*pi, 1)]) + + # https://github.com/sympy/sympy/issues/26497 + p = ((y - I)**2 * (y + I) * (x + 1)) + assert factor(expand(p)) == p + + p = ((x - I)**2 * (x + I) * (y + 1)) + assert factor(expand(p)) == p + + p = (y + 1)**2*(y + sqrt(2))**2*(x**2 + x + 2 + 3*sqrt(2))**2 + assert factor(expand(p), extension=True) == p + + e = ( + -x**2*y**4/(y**2 + 1) + 2*I*x**2*y**3/(y**2 + 1) + 2*I*x**2*y/(y**2 + 1) + + x**2/(y**2 + 1) - 2*x*y**4/(y**2 + 1) + 4*I*x*y**3/(y**2 + 1) + + 4*I*x*y/(y**2 + 1) + 2*x/(y**2 + 1) - y**4 - y**4/(y**2 + 1) + 2*I*y**3 + + 2*I*y**3/(y**2 + 1) + 2*I*y + 2*I*y/(y**2 + 1) + 1 + 1/(y**2 + 1) + ) + assert factor(e) == -(y - I)**3*(y + I)*(x**2 + 2*x + y**2 + 2)/(y**2 + 1) + + +def test_factor_large(): + f = (x**2 + 4*x + 4)**10000000*(x**2 + 1)*(x**2 + 2*x + 1)**1234567 + g = ((x**2 + 2*x + 1)**3000*y**2 + (x**2 + 2*x + 1)**3000*2*y + ( + x**2 + 2*x + 1)**3000) + + assert factor(f) == (x + 2)**20000000*(x**2 + 1)*(x + 1)**2469134 + assert factor(g) == (x + 1)**6000*(y + 1)**2 + + assert factor_list( + f) == (1, [(x + 1, 2469134), (x + 2, 20000000), (x**2 + 1, 1)]) + assert factor_list(g) == (1, [(y + 1, 2), (x + 1, 6000)]) + + f = (x**2 - y**2)**200000*(x**7 + 1) + g = (x**2 + y**2)**200000*(x**7 + 1) + + assert factor(f) == \ + (x + 1)*(x - y)**200000*(x + y)**200000*(x**6 - x**5 + + x**4 - x**3 + x**2 - x + 1) + assert factor(g, gaussian=True) == \ + (x + 1)*(x - I*y)**200000*(x + I*y)**200000*(x**6 - x**5 + + x**4 - x**3 + x**2 - x + 1) + + assert factor_list(f) == \ + (1, [(x + 1, 1), (x - y, 200000), (x + y, 200000), (x**6 - + x**5 + x**4 - x**3 + x**2 - x + 1, 1)]) + assert factor_list(g, gaussian=True) == \ + (1, [(x + 1, 1), (x - I*y, 200000), (x + I*y, 200000), ( + x**6 - x**5 + x**4 - x**3 + x**2 - x + 1, 1)]) + + +def test_factor_noeval(): + assert factor(6*x - 10) == Mul(2, 3*x - 5, evaluate=False) + assert factor((6*x - 10)/(3*x - 6)) == Mul(Rational(2, 3), 3*x - 5, 1/(x - 2)) + + +def test_intervals(): + assert intervals(0) == [] + assert intervals(1) == [] + + assert intervals(x, sqf=True) == [(0, 0)] + assert intervals(x) == [((0, 0), 1)] + + assert intervals(x**128) == [((0, 0), 128)] + assert intervals([x**2, x**4]) == [((0, 0), {0: 2, 1: 4})] + + f = Poly((x*Rational(2, 5) - Rational(17, 3))*(4*x + Rational(1, 257))) + + assert f.intervals(sqf=True) == [(-1, 0), (14, 15)] + assert f.intervals() == [((-1, 0), 1), ((14, 15), 1)] + + assert f.intervals(fast=True, sqf=True) == [(-1, 0), (14, 15)] + assert f.intervals(fast=True) == [((-1, 0), 1), ((14, 15), 1)] + + assert f.intervals(eps=Rational(1, 10)) == f.intervals(eps=0.1) == \ + [((Rational(-1, 258), 0), 1), ((Rational(85, 6), Rational(85, 6)), 1)] + assert f.intervals(eps=Rational(1, 100)) == f.intervals(eps=0.01) == \ + [((Rational(-1, 258), 0), 1), ((Rational(85, 6), Rational(85, 6)), 1)] + assert f.intervals(eps=Rational(1, 1000)) == f.intervals(eps=0.001) == \ + [((Rational(-1, 1002), 0), 1), ((Rational(85, 6), Rational(85, 6)), 1)] + assert f.intervals(eps=Rational(1, 10000)) == f.intervals(eps=0.0001) == \ + [((Rational(-1, 1028), Rational(-1, 1028)), 1), ((Rational(85, 6), Rational(85, 6)), 1)] + + f = (x*Rational(2, 5) - Rational(17, 3))*(4*x + Rational(1, 257)) + + assert intervals(f, sqf=True) == [(-1, 0), (14, 15)] + assert intervals(f) == [((-1, 0), 1), ((14, 15), 1)] + + assert intervals(f, eps=Rational(1, 10)) == intervals(f, eps=0.1) == \ + [((Rational(-1, 258), 0), 1), ((Rational(85, 6), Rational(85, 6)), 1)] + assert intervals(f, eps=Rational(1, 100)) == intervals(f, eps=0.01) == \ + [((Rational(-1, 258), 0), 1), ((Rational(85, 6), Rational(85, 6)), 1)] + assert intervals(f, eps=Rational(1, 1000)) == intervals(f, eps=0.001) == \ + [((Rational(-1, 1002), 0), 1), ((Rational(85, 6), Rational(85, 6)), 1)] + assert intervals(f, eps=Rational(1, 10000)) == intervals(f, eps=0.0001) == \ + [((Rational(-1, 1028), Rational(-1, 1028)), 1), ((Rational(85, 6), Rational(85, 6)), 1)] + + f = Poly((x**2 - 2)*(x**2 - 3)**7*(x + 1)*(7*x + 3)**3) + + assert f.intervals() == \ + [((-2, Rational(-3, 2)), 7), ((Rational(-3, 2), -1), 1), + ((-1, -1), 1), ((-1, 0), 3), + ((1, Rational(3, 2)), 1), ((Rational(3, 2), 2), 7)] + + assert intervals([x**5 - 200, x**5 - 201]) == \ + [((Rational(75, 26), Rational(101, 35)), {0: 1}), ((Rational(309, 107), Rational(26, 9)), {1: 1})] + + assert intervals([x**5 - 200, x**5 - 201], fast=True) == \ + [((Rational(75, 26), Rational(101, 35)), {0: 1}), ((Rational(309, 107), Rational(26, 9)), {1: 1})] + + assert intervals([x**2 - 200, x**2 - 201]) == \ + [((Rational(-71, 5), Rational(-85, 6)), {1: 1}), ((Rational(-85, 6), -14), {0: 1}), + ((14, Rational(85, 6)), {0: 1}), ((Rational(85, 6), Rational(71, 5)), {1: 1})] + + assert intervals([x + 1, x + 2, x - 1, x + 1, 1, x - 1, x - 1, (x - 2)**2]) == \ + [((-2, -2), {1: 1}), ((-1, -1), {0: 1, 3: 1}), ((1, 1), {2: + 1, 5: 1, 6: 1}), ((2, 2), {7: 2})] + + f, g, h = x**2 - 2, x**4 - 4*x**2 + 4, x - 1 + + assert intervals(f, inf=Rational(7, 4), sqf=True) == [] + assert intervals(f, inf=Rational(7, 5), sqf=True) == [(Rational(7, 5), Rational(3, 2))] + assert intervals(f, sup=Rational(7, 4), sqf=True) == [(-2, -1), (1, Rational(3, 2))] + assert intervals(f, sup=Rational(7, 5), sqf=True) == [(-2, -1)] + + assert intervals(g, inf=Rational(7, 4)) == [] + assert intervals(g, inf=Rational(7, 5)) == [((Rational(7, 5), Rational(3, 2)), 2)] + assert intervals(g, sup=Rational(7, 4)) == [((-2, -1), 2), ((1, Rational(3, 2)), 2)] + assert intervals(g, sup=Rational(7, 5)) == [((-2, -1), 2)] + + assert intervals([g, h], inf=Rational(7, 4)) == [] + assert intervals([g, h], inf=Rational(7, 5)) == [((Rational(7, 5), Rational(3, 2)), {0: 2})] + assert intervals([g, h], sup=S( + 7)/4) == [((-2, -1), {0: 2}), ((1, 1), {1: 1}), ((1, Rational(3, 2)), {0: 2})] + assert intervals( + [g, h], sup=Rational(7, 5)) == [((-2, -1), {0: 2}), ((1, 1), {1: 1})] + + assert intervals([x + 2, x**2 - 2]) == \ + [((-2, -2), {0: 1}), ((-2, -1), {1: 1}), ((1, 2), {1: 1})] + assert intervals([x + 2, x**2 - 2], strict=True) == \ + [((-2, -2), {0: 1}), ((Rational(-3, 2), -1), {1: 1}), ((1, 2), {1: 1})] + + f = 7*z**4 - 19*z**3 + 20*z**2 + 17*z + 20 + + assert intervals(f) == [] + + real_part, complex_part = intervals(f, all=True, sqf=True) + + assert real_part == [] + assert all(re(a) < re(r) < re(b) and im( + a) < im(r) < im(b) for (a, b), r in zip(complex_part, nroots(f))) + + assert complex_part == [(Rational(-40, 7) - I*40/7, 0), + (Rational(-40, 7), I*40/7), + (I*Rational(-40, 7), Rational(40, 7)), + (0, Rational(40, 7) + I*40/7)] + + real_part, complex_part = intervals(f, all=True, sqf=True, eps=Rational(1, 10)) + + assert real_part == [] + assert all(re(a) < re(r) < re(b) and im( + a) < im(r) < im(b) for (a, b), r in zip(complex_part, nroots(f))) + + raises(ValueError, lambda: intervals(x**2 - 2, eps=10**-100000)) + raises(ValueError, lambda: Poly(x**2 - 2).intervals(eps=10**-100000)) + raises( + ValueError, lambda: intervals([x**2 - 2, x**2 - 3], eps=10**-100000)) + + +def test_refine_root(): + f = Poly(x**2 - 2) + + assert f.refine_root(1, 2, steps=0) == (1, 2) + assert f.refine_root(-2, -1, steps=0) == (-2, -1) + + assert f.refine_root(1, 2, steps=None) == (1, Rational(3, 2)) + assert f.refine_root(-2, -1, steps=None) == (Rational(-3, 2), -1) + + assert f.refine_root(1, 2, steps=1) == (1, Rational(3, 2)) + assert f.refine_root(-2, -1, steps=1) == (Rational(-3, 2), -1) + + assert f.refine_root(1, 2, steps=1, fast=True) == (1, Rational(3, 2)) + assert f.refine_root(-2, -1, steps=1, fast=True) == (Rational(-3, 2), -1) + + assert f.refine_root(1, 2, eps=Rational(1, 100)) == (Rational(24, 17), Rational(17, 12)) + assert f.refine_root(1, 2, eps=1e-2) == (Rational(24, 17), Rational(17, 12)) + + raises(PolynomialError, lambda: (f**2).refine_root(1, 2, check_sqf=True)) + + raises(RefinementFailed, lambda: (f**2).refine_root(1, 2)) + raises(RefinementFailed, lambda: (f**2).refine_root(2, 3)) + + f = x**2 - 2 + + assert refine_root(f, 1, 2, steps=1) == (1, Rational(3, 2)) + assert refine_root(f, -2, -1, steps=1) == (Rational(-3, 2), -1) + + assert refine_root(f, 1, 2, steps=1, fast=True) == (1, Rational(3, 2)) + assert refine_root(f, -2, -1, steps=1, fast=True) == (Rational(-3, 2), -1) + + assert refine_root(f, 1, 2, eps=Rational(1, 100)) == (Rational(24, 17), Rational(17, 12)) + assert refine_root(f, 1, 2, eps=1e-2) == (Rational(24, 17), Rational(17, 12)) + + raises(PolynomialError, lambda: refine_root(1, 7, 8, eps=Rational(1, 100))) + + raises(ValueError, lambda: Poly(f).refine_root(1, 2, eps=10**-100000)) + raises(ValueError, lambda: refine_root(f, 1, 2, eps=10**-100000)) + + +def test_count_roots(): + assert count_roots(x**2 - 2) == 2 + + assert count_roots(x**2 - 2, inf=-oo) == 2 + assert count_roots(x**2 - 2, sup=+oo) == 2 + assert count_roots(x**2 - 2, inf=-oo, sup=+oo) == 2 + + assert count_roots(x**2 - 2, inf=-2) == 2 + assert count_roots(x**2 - 2, inf=-1) == 1 + + assert count_roots(x**2 - 2, sup=1) == 1 + assert count_roots(x**2 - 2, sup=2) == 2 + + assert count_roots(x**2 - 2, inf=-1, sup=1) == 0 + assert count_roots(x**2 - 2, inf=-2, sup=2) == 2 + + assert count_roots(x**2 - 2, inf=-1, sup=1) == 0 + assert count_roots(x**2 - 2, inf=-2, sup=2) == 2 + + assert count_roots(x**2 + 2) == 0 + assert count_roots(x**2 + 2, inf=-2*I) == 2 + assert count_roots(x**2 + 2, sup=+2*I) == 2 + assert count_roots(x**2 + 2, inf=-2*I, sup=+2*I) == 2 + + assert count_roots(x**2 + 2, inf=0) == 0 + assert count_roots(x**2 + 2, sup=0) == 0 + + assert count_roots(x**2 + 2, inf=-I) == 1 + assert count_roots(x**2 + 2, sup=+I) == 1 + + assert count_roots(x**2 + 2, inf=+I/2, sup=+I) == 0 + assert count_roots(x**2 + 2, inf=-I, sup=-I/2) == 0 + + raises(PolynomialError, lambda: count_roots(1)) + + +def test_Poly_root(): + f = Poly(2*x**3 - 7*x**2 + 4*x + 4) + + assert f.root(0) == Rational(-1, 2) + assert f.root(1) == 2 + assert f.root(2) == 2 + raises(IndexError, lambda: f.root(3)) + + assert Poly(x**5 + x + 1).root(0) == rootof(x**3 - x**2 + 1, 0) + + +def test_real_roots(): + + assert real_roots(x) == [0] + assert real_roots(x, multiple=False) == [(0, 1)] + + assert real_roots(x**3) == [0, 0, 0] + assert real_roots(x**3, multiple=False) == [(0, 3)] + + assert real_roots(x*(x**3 + x + 3)) == [rootof(x**3 + x + 3, 0), 0] + assert real_roots(x*(x**3 + x + 3), multiple=False) == [(rootof( + x**3 + x + 3, 0), 1), (0, 1)] + + assert real_roots( + x**3*(x**3 + x + 3)) == [rootof(x**3 + x + 3, 0), 0, 0, 0] + assert real_roots(x**3*(x**3 + x + 3), multiple=False) == [(rootof( + x**3 + x + 3, 0), 1), (0, 3)] + + assert real_roots(x**2 - 2, radicals=False) == [ + rootof(x**2 - 2, 0, radicals=False), + rootof(x**2 - 2, 1, radicals=False), + ] + + f = 2*x**3 - 7*x**2 + 4*x + 4 + g = x**3 + x + 1 + + assert Poly(f).real_roots() == [Rational(-1, 2), 2, 2] + assert Poly(g).real_roots() == [rootof(g, 0)] + + +def test_all_roots(): + + f = 2*x**3 - 7*x**2 + 4*x + 4 + froots = [Rational(-1, 2), 2, 2] + assert all_roots(f) == Poly(f).all_roots() == froots + + g = x**3 + x + 1 + groots = [rootof(g, 0), rootof(g, 1), rootof(g, 2)] + assert all_roots(g) == Poly(g).all_roots() == groots + + assert all_roots(x**2 - 2) == [-sqrt(2), sqrt(2)] + assert all_roots(x**2 - 2, multiple=False) == [(-sqrt(2), 1), (sqrt(2), 1)] + assert all_roots(x**2 - 2, radicals=False) == [ + rootof(x**2 - 2, 0, radicals=False), + rootof(x**2 - 2, 1, radicals=False), + ] + + p = x**5 - x - 1 + assert all_roots(p) == [ + rootof(p, 0), rootof(p, 1), rootof(p, 2), rootof(p, 3), rootof(p, 4) + ] + + +def test_nroots(): + assert Poly(0, x).nroots() == [] + assert Poly(1, x).nroots() == [] + + assert Poly(x**2 - 1, x).nroots() == [-1.0, 1.0] + assert Poly(x**2 + 1, x).nroots() == [-1.0*I, 1.0*I] + + roots = Poly(x**2 - 1, x).nroots() + assert roots == [-1.0, 1.0] + + roots = Poly(x**2 + 1, x).nroots() + assert roots == [-1.0*I, 1.0*I] + + roots = Poly(x**2/3 - Rational(1, 3), x).nroots() + assert roots == [-1.0, 1.0] + + roots = Poly(x**2/3 + Rational(1, 3), x).nroots() + assert roots == [-1.0*I, 1.0*I] + + assert Poly(x**2 + 2*I, x).nroots() == [-1.0 + 1.0*I, 1.0 - 1.0*I] + assert Poly( + x**2 + 2*I, x, extension=I).nroots() == [-1.0 + 1.0*I, 1.0 - 1.0*I] + + assert Poly(0.2*x + 0.1).nroots() == [-0.5] + + roots = nroots(x**5 + x + 1, n=5) + eps = Float("1e-5") + + assert re(roots[0]).epsilon_eq(-0.75487, eps) is S.true + assert im(roots[0]) == 0.0 + assert re(roots[1]) == Float(-0.5, 5) + assert im(roots[1]).epsilon_eq(-0.86602, eps) is S.true + assert re(roots[2]) == Float(-0.5, 5) + assert im(roots[2]).epsilon_eq(+0.86602, eps) is S.true + assert re(roots[3]).epsilon_eq(+0.87743, eps) is S.true + assert im(roots[3]).epsilon_eq(-0.74486, eps) is S.true + assert re(roots[4]).epsilon_eq(+0.87743, eps) is S.true + assert im(roots[4]).epsilon_eq(+0.74486, eps) is S.true + + eps = Float("1e-6") + + assert re(roots[0]).epsilon_eq(-0.75487, eps) is S.false + assert im(roots[0]) == 0.0 + assert re(roots[1]) == Float(-0.5, 5) + assert im(roots[1]).epsilon_eq(-0.86602, eps) is S.false + assert re(roots[2]) == Float(-0.5, 5) + assert im(roots[2]).epsilon_eq(+0.86602, eps) is S.false + assert re(roots[3]).epsilon_eq(+0.87743, eps) is S.false + assert im(roots[3]).epsilon_eq(-0.74486, eps) is S.false + assert re(roots[4]).epsilon_eq(+0.87743, eps) is S.false + assert im(roots[4]).epsilon_eq(+0.74486, eps) is S.false + + raises(DomainError, lambda: Poly(x + y, x).nroots()) + raises(MultivariatePolynomialError, lambda: Poly(x + y).nroots()) + + assert nroots(x**2 - 1) == [-1.0, 1.0] + + roots = nroots(x**2 - 1) + assert roots == [-1.0, 1.0] + + assert nroots(x + I) == [-1.0*I] + assert nroots(x + 2*I) == [-2.0*I] + + raises(PolynomialError, lambda: nroots(0)) + + # issue 8296 + f = Poly(x**4 - 1) + assert f.nroots(2) == [w.n(2) for w in f.all_roots()] + + assert str(Poly(x**16 + 32*x**14 + 508*x**12 + 5440*x**10 + + 39510*x**8 + 204320*x**6 + 755548*x**4 + 1434496*x**2 + + 877969).nroots(2)) == ('[-1.7 - 1.9*I, -1.7 + 1.9*I, -1.7 ' + '- 2.5*I, -1.7 + 2.5*I, -1.0*I, 1.0*I, -1.7*I, 1.7*I, -2.8*I, ' + '2.8*I, -3.4*I, 3.4*I, 1.7 - 1.9*I, 1.7 + 1.9*I, 1.7 - 2.5*I, ' + '1.7 + 2.5*I]') + assert str(Poly(1e-15*x**2 -1).nroots()) == ('[-31622776.6016838, 31622776.6016838]') + + +def test_ground_roots(): + f = x**6 - 4*x**4 + 4*x**3 - x**2 + + assert Poly(f).ground_roots() == {S.One: 2, S.Zero: 2} + assert ground_roots(f) == {S.One: 2, S.Zero: 2} + + +def test_nth_power_roots_poly(): + f = x**4 - x**2 + 1 + + f_2 = (x**2 - x + 1)**2 + f_3 = (x**2 + 1)**2 + f_4 = (x**2 + x + 1)**2 + f_12 = (x - 1)**4 + + assert nth_power_roots_poly(f, 1) == f + + raises(ValueError, lambda: nth_power_roots_poly(f, 0)) + raises(ValueError, lambda: nth_power_roots_poly(f, x)) + + assert factor(nth_power_roots_poly(f, 2)) == f_2 + assert factor(nth_power_roots_poly(f, 3)) == f_3 + assert factor(nth_power_roots_poly(f, 4)) == f_4 + assert factor(nth_power_roots_poly(f, 12)) == f_12 + + raises(MultivariatePolynomialError, lambda: nth_power_roots_poly( + x + y, 2, x, y)) + + +def test_same_root(): + f = Poly(x**4 + x**3 + x**2 + x + 1) + eq = f.same_root + r0 = exp(2 * I * pi / 5) + assert [i for i, r in enumerate(f.all_roots()) if eq(r, r0)] == [3] + + raises(PolynomialError, + lambda: Poly(x + 1, domain=QQ).same_root(0, 0)) + raises(DomainError, + lambda: Poly(x**2 + 1, domain=FF(7)).same_root(0, 0)) + raises(DomainError, + lambda: Poly(x ** 2 + 1, domain=ZZ_I).same_root(0, 0)) + raises(DomainError, + lambda: Poly(y * x**2 + 1, domain=ZZ[y]).same_root(0, 0)) + raises(MultivariatePolynomialError, + lambda: Poly(x * y + 1, domain=ZZ).same_root(0, 0)) + + +def test_torational_factor_list(): + p = expand(((x**2-1)*(x-2)).subs({x:x*(1 + sqrt(2))})) + assert _torational_factor_list(p, x) == (-2, [ + (-x*(1 + sqrt(2))/2 + 1, 1), + (-x*(1 + sqrt(2)) - 1, 1), + (-x*(1 + sqrt(2)) + 1, 1)]) + + + p = expand(((x**2-1)*(x-2)).subs({x:x*(1 + 2**Rational(1, 4))})) + assert _torational_factor_list(p, x) is None + + +def test_cancel(): + assert cancel(0) == 0 + assert cancel(7) == 7 + assert cancel(x) == x + + assert cancel(oo) is oo + + assert cancel((2, 3)) == (1, 2, 3) + + assert cancel((1, 0), x) == (1, 1, 0) + assert cancel((0, 1), x) == (1, 0, 1) + + f, g, p, q = 4*x**2 - 4, 2*x - 2, 2*x + 2, 1 + F, G, P, Q = [ Poly(u, x) for u in (f, g, p, q) ] + + assert F.cancel(G) == (1, P, Q) + assert cancel((f, g)) == (1, p, q) + assert cancel((f, g), x) == (1, p, q) + assert cancel((f, g), (x,)) == (1, p, q) + assert cancel((F, G)) == (1, P, Q) + assert cancel((f, g), polys=True) == (1, P, Q) + assert cancel((F, G), polys=False) == (1, p, q) + + f = (x**2 - 2)/(x + sqrt(2)) + + assert cancel(f) == f + assert cancel(f, greedy=False) == x - sqrt(2) + + f = (x**2 - 2)/(x - sqrt(2)) + + assert cancel(f) == f + assert cancel(f, greedy=False) == x + sqrt(2) + + assert cancel((x**2/4 - 1, x/2 - 1)) == (1, x + 2, 2) + # assert cancel((x**2/4 - 1, x/2 - 1)) == (S.Half, x + 2, 1) + + assert cancel((x**2 - y)/(x - y)) == 1/(x - y)*(x**2 - y) + + assert cancel((x**2 - y**2)/(x - y), x) == x + y + assert cancel((x**2 - y**2)/(x - y), y) == x + y + assert cancel((x**2 - y**2)/(x - y)) == x + y + + assert cancel((x**3 - 1)/(x**2 - 1)) == (x**2 + x + 1)/(x + 1) + assert cancel((x**3/2 - S.Half)/(x**2 - 1)) == (x**2 + x + 1)/(2*x + 2) + + assert cancel((exp(2*x) + 2*exp(x) + 1)/(exp(x) + 1)) == exp(x) + 1 + + f = Poly(x**2 - a**2, x) + g = Poly(x - a, x) + + F = Poly(x + a, x, domain='ZZ[a]') + G = Poly(1, x, domain='ZZ[a]') + + assert cancel((f, g)) == (1, F, G) + + f = x**3 + (sqrt(2) - 2)*x**2 - (2*sqrt(2) + 3)*x - 3*sqrt(2) + g = x**2 - 2 + + assert cancel((f, g), extension=True) == (1, x**2 - 2*x - 3, x - sqrt(2)) + + f = Poly(-2*x + 3, x) + g = Poly(-x**9 + x**8 + x**6 - x**5 + 2*x**2 - 3*x + 1, x) + + assert cancel((f, g)) == (1, -f, -g) + + f = Poly(x/3 + 1, x) + g = Poly(x/7 + 1, x) + + assert f.cancel(g) == (S(7)/3, + Poly(x + 3, x, domain=QQ), + Poly(x + 7, x, domain=QQ)) + assert f.cancel(g, include=True) == ( + Poly(7*x + 21, x, domain=QQ), + Poly(3*x + 21, x, domain=QQ)) + + pairs = [ + (1 + x, 1 + x, 1, 1, 1), + (1 + x, 1 - x, -1, -1-x, -1+x), + (1 - x, 1 + x, -1, 1-x, 1+x), + (1 - x, 1 - x, 1, 1, 1), + ] + for f, g, coeff, p, q in pairs: + assert cancel((f, g)) == (1, p, q) + pf = Poly(f, x) + pg = Poly(g, x) + pp = Poly(p, x) + pq = Poly(q, x) + assert pf.cancel(pg) == (coeff, coeff*pp, pq) + assert pf.rep.cancel(pg.rep) == (pp.rep, pq.rep) + assert pf.rep.cancel(pg.rep, include=True) == (pp.rep, pq.rep) + + f = Poly(y, y, domain='ZZ(x)') + g = Poly(1, y, domain='ZZ[x]') + + assert f.cancel( + g) == (1, Poly(y, y, domain='ZZ(x)'), Poly(1, y, domain='ZZ(x)')) + assert f.cancel(g, include=True) == ( + Poly(y, y, domain='ZZ(x)'), Poly(1, y, domain='ZZ(x)')) + + f = Poly(5*x*y + x, y, domain='ZZ(x)') + g = Poly(2*x**2*y, y, domain='ZZ(x)') + + assert f.cancel(g, include=True) == ( + Poly(5*y + 1, y, domain='ZZ(x)'), Poly(2*x*y, y, domain='ZZ(x)')) + + f = -(-2*x - 4*y + 0.005*(z - y)**2)/((z - y)*(-z + y + 2)) + assert cancel(f).is_Mul == True + + P = tanh(x - 3.0) + Q = tanh(x + 3.0) + f = ((-2*P**2 + 2)*(-P**2 + 1)*Q**2/2 + (-2*P**2 + 2)*(-2*Q**2 + 2)*P*Q - (-2*P**2 + 2)*P**2*Q**2 + (-2*Q**2 + 2)*(-Q**2 + 1)*P**2/2 - (-2*Q**2 + 2)*P**2*Q**2)/(2*sqrt(P**2*Q**2 + 0.0001)) \ + + (-(-2*P**2 + 2)*P*Q**2/2 - (-2*Q**2 + 2)*P**2*Q/2)*((-2*P**2 + 2)*P*Q**2/2 + (-2*Q**2 + 2)*P**2*Q/2)/(2*(P**2*Q**2 + 0.0001)**Rational(3, 2)) + assert cancel(f).is_Mul == True + + # issue 7022 + A = Symbol('A', commutative=False) + p1 = Piecewise((A*(x**2 - 1)/(x + 1), x > 1), ((x + 2)/(x**2 + 2*x), True)) + p2 = Piecewise((A*(x - 1), x > 1), (1/x, True)) + assert cancel(p1) == p2 + assert cancel(2*p1) == 2*p2 + assert cancel(1 + p1) == 1 + p2 + assert cancel((x**2 - 1)/(x + 1)*p1) == (x - 1)*p2 + assert cancel((x**2 - 1)/(x + 1) + p1) == (x - 1) + p2 + p3 = Piecewise(((x**2 - 1)/(x + 1), x > 1), ((x + 2)/(x**2 + 2*x), True)) + p4 = Piecewise(((x - 1), x > 1), (1/x, True)) + assert cancel(p3) == p4 + assert cancel(2*p3) == 2*p4 + assert cancel(1 + p3) == 1 + p4 + assert cancel((x**2 - 1)/(x + 1)*p3) == (x - 1)*p4 + assert cancel((x**2 - 1)/(x + 1) + p3) == (x - 1) + p4 + + # issue 4077 + q = S('''(2*1*(x - 1/x)/(x*(2*x - (-x + 1/x)/(x**2*(x - 1/x)**2) - 1/(x**2*(x - + 1/x)) - 2/x)) - 2*1*((x - 1/x)/((x*(x - 1/x)**2)) - 1/(x*(x - + 1/x)))*((-x + 1/x)*((x - 1/x)/((x*(x - 1/x)**2)) - 1/(x*(x - + 1/x)))/(2*x - (-x + 1/x)/(x**2*(x - 1/x)**2) - 1/(x**2*(x - 1/x)) - + 2/x) + 1)*((x - 1/x)/((x - 1/x)**2) - ((x - 1/x)/((x*(x - 1/x)**2)) - + 1/(x*(x - 1/x)))**2/(2*x - (-x + 1/x)/(x**2*(x - 1/x)**2) - 1/(x**2*(x + - 1/x)) - 2/x) - 1/(x - 1/x))*(2*x - (-x + 1/x)/(x**2*(x - 1/x)**2) - + 1/(x**2*(x - 1/x)) - 2/x)/x - 1/x)*(((-x + 1/x)/((x*(x - 1/x)**2)) + + 1/(x*(x - 1/x)))*((-(x - 1/x)/(x*(x - 1/x)) - 1/x)*((x - 1/x)/((x*(x - + 1/x)**2)) - 1/(x*(x - 1/x)))/(2*x - (-x + 1/x)/(x**2*(x - 1/x)**2) - + 1/(x**2*(x - 1/x)) - 2/x) - 1 + (x - 1/x)/(x - 1/x))/((x*((x - + 1/x)/((x - 1/x)**2) - ((x - 1/x)/((x*(x - 1/x)**2)) - 1/(x*(x - + 1/x)))**2/(2*x - (-x + 1/x)/(x**2*(x - 1/x)**2) - 1/(x**2*(x - 1/x)) - + 2/x) - 1/(x - 1/x))*(2*x - (-x + 1/x)/(x**2*(x - 1/x)**2) - 1/(x**2*(x + - 1/x)) - 2/x))) + ((x - 1/x)/((x*(x - 1/x))) + 1/x)/((x*(2*x - (-x + + 1/x)/(x**2*(x - 1/x)**2) - 1/(x**2*(x - 1/x)) - 2/x))) + 1/x)/(2*x + + 2*((x - 1/x)/((x*(x - 1/x)**2)) - 1/(x*(x - 1/x)))*((-(x - 1/x)/(x*(x + - 1/x)) - 1/x)*((x - 1/x)/((x*(x - 1/x)**2)) - 1/(x*(x - 1/x)))/(2*x - + (-x + 1/x)/(x**2*(x - 1/x)**2) - 1/(x**2*(x - 1/x)) - 2/x) - 1 + (x - + 1/x)/(x - 1/x))/((x*((x - 1/x)/((x - 1/x)**2) - ((x - 1/x)/((x*(x - + 1/x)**2)) - 1/(x*(x - 1/x)))**2/(2*x - (-x + 1/x)/(x**2*(x - 1/x)**2) + - 1/(x**2*(x - 1/x)) - 2/x) - 1/(x - 1/x))*(2*x - (-x + 1/x)/(x**2*(x + - 1/x)**2) - 1/(x**2*(x - 1/x)) - 2/x))) - 2*((x - 1/x)/((x*(x - + 1/x))) + 1/x)/(x*(2*x - (-x + 1/x)/(x**2*(x - 1/x)**2) - 1/(x**2*(x - + 1/x)) - 2/x)) - 2/x) - ((x - 1/x)/((x*(x - 1/x)**2)) - 1/(x*(x - + 1/x)))*((-x + 1/x)*((x - 1/x)/((x*(x - 1/x)**2)) - 1/(x*(x - + 1/x)))/(2*x - (-x + 1/x)/(x**2*(x - 1/x)**2) - 1/(x**2*(x - 1/x)) - + 2/x) + 1)/(x*((x - 1/x)/((x - 1/x)**2) - ((x - 1/x)/((x*(x - 1/x)**2)) + - 1/(x*(x - 1/x)))**2/(2*x - (-x + 1/x)/(x**2*(x - 1/x)**2) - + 1/(x**2*(x - 1/x)) - 2/x) - 1/(x - 1/x))*(2*x - (-x + 1/x)/(x**2*(x - + 1/x)**2) - 1/(x**2*(x - 1/x)) - 2/x)) + (x - 1/x)/((x*(2*x - (-x + + 1/x)/(x**2*(x - 1/x)**2) - 1/(x**2*(x - 1/x)) - 2/x))) - 1/x''', + evaluate=False) + assert cancel(q, _signsimp=False) is S.NaN + assert q.subs(x, 2) is S.NaN + assert signsimp(q) is S.NaN + + # issue 9363 + M = MatrixSymbol('M', 5, 5) + assert cancel(M[0,0] + 7) == M[0,0] + 7 + expr = sin(M[1, 4] + M[2, 1] * 5 * M[4, 0]) - 5 * M[1, 2] / z + assert cancel(expr) == (z*sin(M[1, 4] + M[2, 1] * 5 * M[4, 0]) - 5 * M[1, 2]) / z + + assert cancel((x**2 + 1)/(x - I)) == x + I + + +def test_make_monic_over_integers_by_scaling_roots(): + f = Poly(x**2 + 3*x + 4, x, domain='ZZ') + g, c = f.make_monic_over_integers_by_scaling_roots() + assert g == f + assert c == ZZ.one + + f = Poly(x**2 + 3*x + 4, x, domain='QQ') + g, c = f.make_monic_over_integers_by_scaling_roots() + assert g == f.to_ring() + assert c == ZZ.one + + f = Poly(x**2/2 + S(1)/4 * x + S(1)/8, x, domain='QQ') + g, c = f.make_monic_over_integers_by_scaling_roots() + assert g == Poly(x**2 + 2*x + 4, x, domain='ZZ') + assert c == 4 + + f = Poly(x**3/2 + S(1)/4 * x + S(1)/8, x, domain='QQ') + g, c = f.make_monic_over_integers_by_scaling_roots() + assert g == Poly(x**3 + 8*x + 16, x, domain='ZZ') + assert c == 4 + + f = Poly(x*y, x, y) + raises(ValueError, lambda: f.make_monic_over_integers_by_scaling_roots()) + + f = Poly(x, domain='RR') + raises(ValueError, lambda: f.make_monic_over_integers_by_scaling_roots()) + + +def test_galois_group(): + f = Poly(x ** 4 - 2) + G, alt = f.galois_group(by_name=True) + assert G == S4TransitiveSubgroups.D4 + assert alt is False + + +def test_reduced(): + f = 2*x**4 + y**2 - x**2 + y**3 + G = [x**3 - x, y**3 - y] + + Q = [2*x, 1] + r = x**2 + y**2 + y + + assert reduced(f, G) == (Q, r) + assert reduced(f, G, x, y) == (Q, r) + + H = groebner(G) + + assert H.reduce(f) == (Q, r) + + Q = [Poly(2*x, x, y), Poly(1, x, y)] + r = Poly(x**2 + y**2 + y, x, y) + + assert _strict_eq(reduced(f, G, polys=True), (Q, r)) + assert _strict_eq(reduced(f, G, x, y, polys=True), (Q, r)) + + H = groebner(G, polys=True) + + assert _strict_eq(H.reduce(f), (Q, r)) + + f = 2*x**3 + y**3 + 3*y + G = groebner([x**2 + y**2 - 1, x*y - 2]) + + Q = [x**2 - x*y**3/2 + x*y/2 + y**6/4 - y**4/2 + y**2/4, -y**5/4 + y**3/2 + y*Rational(3, 4)] + r = 0 + + assert reduced(f, G) == (Q, r) + assert G.reduce(f) == (Q, r) + + assert reduced(f, G, auto=False)[1] != 0 + assert G.reduce(f, auto=False)[1] != 0 + + assert G.contains(f) is True + assert G.contains(f + 1) is False + + assert reduced(1, [1], x) == ([1], 0) + raises(ComputationFailed, lambda: reduced(1, [1])) + + +def test_groebner(): + assert groebner([], x, y, z) == [] + + assert groebner([x**2 + 1, y**4*x + x**3], x, y, order='lex') == [1 + x**2, -1 + y**4] + assert groebner([x**2 + 1, y**4*x + x**3, x*y*z**3], x, y, z, order='grevlex') == [-1 + y**4, z**3, 1 + x**2] + + assert groebner([x**2 + 1, y**4*x + x**3], x, y, order='lex', polys=True) == \ + [Poly(1 + x**2, x, y), Poly(-1 + y**4, x, y)] + assert groebner([x**2 + 1, y**4*x + x**3, x*y*z**3], x, y, z, order='grevlex', polys=True) == \ + [Poly(-1 + y**4, x, y, z), Poly(z**3, x, y, z), Poly(1 + x**2, x, y, z)] + + assert groebner([x**3 - 1, x**2 - 1]) == [x - 1] + assert groebner([Eq(x**3, 1), Eq(x**2, 1)]) == [x - 1] + + F = [3*x**2 + y*z - 5*x - 1, 2*x + 3*x*y + y**2, x - 3*y + x*z - 2*z**2] + f = z**9 - x**2*y**3 - 3*x*y**2*z + 11*y*z**2 + x**2*z**2 - 5 + + G = groebner(F, x, y, z, modulus=7, symmetric=False) + + assert G == [1 + x + y + 3*z + 2*z**2 + 2*z**3 + 6*z**4 + z**5, + 1 + 3*y + y**2 + 6*z**2 + 3*z**3 + 3*z**4 + 3*z**5 + 4*z**6, + 1 + 4*y + 4*z + y*z + 4*z**3 + z**4 + z**6, + 6 + 6*z + z**2 + 4*z**3 + 3*z**4 + 6*z**5 + 3*z**6 + z**7] + + Q, r = reduced(f, G, x, y, z, modulus=7, symmetric=False, polys=True) + + assert sum([ q*g for q, g in zip(Q, G.polys)], r) == Poly(f, modulus=7) + + F = [x*y - 2*y, 2*y**2 - x**2] + + assert groebner(F, x, y, order='grevlex') == \ + [y**3 - 2*y, x**2 - 2*y**2, x*y - 2*y] + assert groebner(F, y, x, order='grevlex') == \ + [x**3 - 2*x**2, -x**2 + 2*y**2, x*y - 2*y] + assert groebner(F, order='grevlex', field=True) == \ + [y**3 - 2*y, x**2 - 2*y**2, x*y - 2*y] + + assert groebner([1], x) == [1] + + assert groebner([x**2 + 2.0*y], x, y) == [1.0*x**2 + 2.0*y] + raises(ComputationFailed, lambda: groebner([1])) + + assert groebner([x**2 - 1, x**3 + 1], method='buchberger') == [x + 1] + assert groebner([x**2 - 1, x**3 + 1], method='f5b') == [x + 1] + + raises(ValueError, lambda: groebner([x, y], method='unknown')) + + +def test_fglm(): + F = [a + b + c + d, a*b + a*d + b*c + b*d, a*b*c + a*b*d + a*c*d + b*c*d, a*b*c*d - 1] + G = groebner(F, a, b, c, d, order=grlex) + + B = [ + 4*a + 3*d**9 - 4*d**5 - 3*d, + 4*b + 4*c - 3*d**9 + 4*d**5 + 7*d, + 4*c**2 + 3*d**10 - 4*d**6 - 3*d**2, + 4*c*d**4 + 4*c - d**9 + 4*d**5 + 5*d, + d**12 - d**8 - d**4 + 1, + ] + + assert groebner(F, a, b, c, d, order=lex) == B + assert G.fglm(lex) == B + + F = [9*x**8 + 36*x**7 - 32*x**6 - 252*x**5 - 78*x**4 + 468*x**3 + 288*x**2 - 108*x + 9, + -72*t*x**7 - 252*t*x**6 + 192*t*x**5 + 1260*t*x**4 + 312*t*x**3 - 404*t*x**2 - 576*t*x + \ + 108*t - 72*x**7 - 256*x**6 + 192*x**5 + 1280*x**4 + 312*x**3 - 576*x + 96] + G = groebner(F, t, x, order=grlex) + + B = [ + 203577793572507451707*t + 627982239411707112*x**7 - 666924143779443762*x**6 - \ + 10874593056632447619*x**5 + 5119998792707079562*x**4 + 72917161949456066376*x**3 + \ + 20362663855832380362*x**2 - 142079311455258371571*x + 183756699868981873194, + 9*x**8 + 36*x**7 - 32*x**6 - 252*x**5 - 78*x**4 + 468*x**3 + 288*x**2 - 108*x + 9, + ] + + assert groebner(F, t, x, order=lex) == B + assert G.fglm(lex) == B + + F = [x**2 - x - 3*y + 1, -2*x + y**2 + y - 1] + G = groebner(F, x, y, order=lex) + + B = [ + x**2 - x - 3*y + 1, + y**2 - 2*x + y - 1, + ] + + assert groebner(F, x, y, order=grlex) == B + assert G.fglm(grlex) == B + + +def test_is_zero_dimensional(): + assert is_zero_dimensional([x, y], x, y) is True + assert is_zero_dimensional([x**3 + y**2], x, y) is False + + assert is_zero_dimensional([x, y, z], x, y, z) is True + assert is_zero_dimensional([x, y, z], x, y, z, t) is False + + F = [x*y - z, y*z - x, x*y - y] + assert is_zero_dimensional(F, x, y, z) is True + + F = [x**2 - 2*x*z + 5, x*y**2 + y*z**3, 3*y**2 - 8*z**2] + assert is_zero_dimensional(F, x, y, z) is True + + +def test_GroebnerBasis(): + F = [x*y - 2*y, 2*y**2 - x**2] + + G = groebner(F, x, y, order='grevlex') + H = [y**3 - 2*y, x**2 - 2*y**2, x*y - 2*y] + P = [ Poly(h, x, y) for h in H ] + + assert groebner(F + [0], x, y, order='grevlex') == G + assert isinstance(G, GroebnerBasis) is True + + assert len(G) == 3 + + assert G[0] == H[0] and not G[0].is_Poly + assert G[1] == H[1] and not G[1].is_Poly + assert G[2] == H[2] and not G[2].is_Poly + + assert G[1:] == H[1:] and not any(g.is_Poly for g in G[1:]) + assert G[:2] == H[:2] and not any(g.is_Poly for g in G[1:]) + + assert G.exprs == H + assert G.polys == P + assert G.gens == (x, y) + assert G.domain == ZZ + assert G.order == grevlex + + assert G == H + assert G == tuple(H) + assert G == P + assert G == tuple(P) + + assert G != [] + + G = groebner(F, x, y, order='grevlex', polys=True) + + assert G[0] == P[0] and G[0].is_Poly + assert G[1] == P[1] and G[1].is_Poly + assert G[2] == P[2] and G[2].is_Poly + + assert G[1:] == P[1:] and all(g.is_Poly for g in G[1:]) + assert G[:2] == P[:2] and all(g.is_Poly for g in G[1:]) + + +def test_poly(): + assert poly(x) == Poly(x, x) + assert poly(y) == Poly(y, y) + + assert poly(x + y) == Poly(x + y, x, y) + assert poly(x + sin(x)) == Poly(x + sin(x), x, sin(x)) + + assert poly(x + y, wrt=y) == Poly(x + y, y, x) + assert poly(x + sin(x), wrt=sin(x)) == Poly(x + sin(x), sin(x), x) + + assert poly(x*y + 2*x*z**2 + 17) == Poly(x*y + 2*x*z**2 + 17, x, y, z) + + assert poly(2*(y + z)**2 - 1) == Poly(2*y**2 + 4*y*z + 2*z**2 - 1, y, z) + assert poly( + x*(y + z)**2 - 1) == Poly(x*y**2 + 2*x*y*z + x*z**2 - 1, x, y, z) + assert poly(2*x*( + y + z)**2 - 1) == Poly(2*x*y**2 + 4*x*y*z + 2*x*z**2 - 1, x, y, z) + + assert poly(2*( + y + z)**2 - x - 1) == Poly(2*y**2 + 4*y*z + 2*z**2 - x - 1, x, y, z) + assert poly(x*( + y + z)**2 - x - 1) == Poly(x*y**2 + 2*x*y*z + x*z**2 - x - 1, x, y, z) + assert poly(2*x*(y + z)**2 - x - 1) == Poly(2*x*y**2 + 4*x*y*z + 2* + x*z**2 - x - 1, x, y, z) + + assert poly(x*y + (x + y)**2 + (x + z)**2) == \ + Poly(2*x*z + 3*x*y + y**2 + z**2 + 2*x**2, x, y, z) + assert poly(x*y*(x + y)*(x + z)**2) == \ + Poly(x**3*y**2 + x*y**2*z**2 + y*x**2*z**2 + 2*z*x**2* + y**2 + 2*y*z*x**3 + y*x**4, x, y, z) + + assert poly(Poly(x + y + z, y, x, z)) == Poly(x + y + z, y, x, z) + + assert poly((x + y)**2, x) == Poly(x**2 + 2*x*y + y**2, x, domain=ZZ[y]) + assert poly((x + y)**2, y) == Poly(x**2 + 2*x*y + y**2, y, domain=ZZ[x]) + + assert poly(1, x) == Poly(1, x) + raises(GeneratorsNeeded, lambda: poly(1)) + + # issue 6184 + assert poly(x + y, x, y) == Poly(x + y, x, y) + assert poly(x + y, y, x) == Poly(x + y, y, x) + + # https://github.com/sympy/sympy/issues/19755 + expr1 = x + (2*x + 3)**2/5 + S(6)/5 + assert poly(expr1).as_expr() == expr1.expand() + expr2 = y*(y+1) + S(1)/3 + assert poly(expr2).as_expr() == expr2.expand() + + +def test_keep_coeff(): + u = Mul(2, x + 1, evaluate=False) + assert _keep_coeff(S.One, x) == x + assert _keep_coeff(S.NegativeOne, x) == -x + assert _keep_coeff(S(1.0), x) == 1.0*x + assert _keep_coeff(S(-1.0), x) == -1.0*x + assert _keep_coeff(S.One, 2*x) == 2*x + assert _keep_coeff(S(2), x/2) == x + assert _keep_coeff(S(2), sin(x)) == 2*sin(x) + assert _keep_coeff(S(2), x + 1) == u + assert _keep_coeff(x, 1/x) == 1 + assert _keep_coeff(x + 1, S(2)) == u + assert _keep_coeff(S.Half, S.One) == S.Half + p = Pow(2, 3, evaluate=False) + assert _keep_coeff(S(-1), p) == Mul(-1, p, evaluate=False) + a = Add(2, p, evaluate=False) + assert _keep_coeff(S.Half, a, clear=True + ) == Mul(S.Half, a, evaluate=False) + assert _keep_coeff(S.Half, a, clear=False + ) == Add(1, Mul(S.Half, p, evaluate=False), evaluate=False) + + +def test_poly_matching_consistency(): + # Test for this issue: + # https://github.com/sympy/sympy/issues/5514 + assert I * Poly(x, x) == Poly(I*x, x) + assert Poly(x, x) * I == Poly(I*x, x) + + +def test_issue_5786(): + assert expand(factor(expand( + (x - I*y)*(z - I*t)), extension=[I])) == -I*t*x - t*y + x*z - I*y*z + + +def test_noncommutative(): + class foo(Expr): + is_commutative=False + e = x/(x + x*y) + c = 1/( 1 + y) + assert cancel(foo(e)) == foo(c) + assert cancel(e + foo(e)) == c + foo(c) + assert cancel(e*foo(c)) == c*foo(c) + + +def test_to_rational_coeffs(): + assert to_rational_coeffs( + Poly(x**3 + y*x**2 + sqrt(y), x, domain='EX')) is None + # issue 21268 + assert to_rational_coeffs( + Poly(y**3 + sqrt(2)*y**2*sin(x) + 1, y)) is None + + assert to_rational_coeffs(Poly(x, y)) is None + assert to_rational_coeffs(Poly(sqrt(2)*y)) is None + + +def test_factor_terms(): + # issue 7067 + assert factor_list(x*(x + y)) == (1, [(x, 1), (x + y, 1)]) + assert sqf_list(x*(x + y)) == (1, [(x**2 + x*y, 1)]) + + +def test_as_list(): + # issue 14496 + assert Poly(x**3 + 2, x, domain='ZZ').as_list() == [1, 0, 0, 2] + assert Poly(x**2 + y + 1, x, y, domain='ZZ').as_list() == [[1], [], [1, 1]] + assert Poly(x**2 + y + 1, x, y, z, domain='ZZ').as_list() == \ + [[[1]], [[]], [[1], [1]]] + + +def test_issue_11198(): + assert factor_list(sqrt(2)*x) == (sqrt(2), [(x, 1)]) + assert factor_list(sqrt(2)*sin(x), sin(x)) == (sqrt(2), [(sin(x), 1)]) + + +def test_Poly_precision(): + # Make sure Poly doesn't lose precision + p = Poly(pi.evalf(100)*x) + assert p.as_expr() == pi.evalf(100)*x + + +def test_issue_12400(): + # Correction of check for negative exponents + assert poly(1/(1+sqrt(2)), x) == \ + Poly(1/(1+sqrt(2)), x, domain='EX') + +def test_issue_14364(): + assert gcd(S(6)*(1 + sqrt(3))/5, S(3)*(1 + sqrt(3))/10) == Rational(3, 10) * (1 + sqrt(3)) + assert gcd(sqrt(5)*Rational(4, 7), sqrt(5)*Rational(2, 3)) == sqrt(5)*Rational(2, 21) + + assert lcm(Rational(2, 3)*sqrt(3), Rational(5, 6)*sqrt(3)) == S(10)*sqrt(3)/3 + assert lcm(3*sqrt(3), 4/sqrt(3)) == 12*sqrt(3) + assert lcm(S(5)*(1 + 2**Rational(1, 3))/6, S(3)*(1 + 2**Rational(1, 3))/8) == Rational(15, 2) * (1 + 2**Rational(1, 3)) + + assert gcd(Rational(2, 3)*sqrt(3), Rational(5, 6)/sqrt(3)) == sqrt(3)/18 + assert gcd(S(4)*sqrt(13)/7, S(3)*sqrt(13)/14) == sqrt(13)/14 + + # gcd_list and lcm_list + assert gcd([S(2)*sqrt(47)/7, S(6)*sqrt(47)/5, S(8)*sqrt(47)/5]) == sqrt(47)*Rational(2, 35) + assert gcd([S(6)*(1 + sqrt(7))/5, S(2)*(1 + sqrt(7))/7, S(4)*(1 + sqrt(7))/13]) == (1 + sqrt(7))*Rational(2, 455) + assert lcm((Rational(7, 2)/sqrt(15), Rational(5, 6)/sqrt(15), Rational(5, 8)/sqrt(15))) == Rational(35, 2)/sqrt(15) + assert lcm([S(5)*(2 + 2**Rational(5, 7))/6, S(7)*(2 + 2**Rational(5, 7))/2, S(13)*(2 + 2**Rational(5, 7))/4]) == Rational(455, 2) * (2 + 2**Rational(5, 7)) + + +def test_issue_15669(): + x = Symbol("x", positive=True) + expr = (16*x**3/(-x**2 + sqrt(8*x**2 + (x**2 - 2)**2) + 2)**2 - + 2*2**Rational(4, 5)*x*(-x**2 + sqrt(8*x**2 + (x**2 - 2)**2) + 2)**Rational(3, 5) + 10*x) + assert factor(expr, deep=True) == x*(x**2 + 2) + + +def test_issue_17988(): + x = Symbol('x') + p = poly(x - 1) + with warns_deprecated_sympy(): + M = Matrix([[poly(x + 1), poly(x + 1)]]) + with warns(SymPyDeprecationWarning, test_stacklevel=False): + assert p * M == M * p == Matrix([[poly(x**2 - 1), poly(x**2 - 1)]]) + + +def test_issue_18205(): + assert cancel((2 + I)*(3 - I)) == 7 + I + assert cancel((2 + I)*(2 - I)) == 5 + + +def test_issue_8695(): + p = (x**2 + 1) * (x - 1)**2 * (x - 2)**3 * (x - 3)**3 + result = (1, [(x**2 + 1, 1), (x - 1, 2), (x**2 - 5*x + 6, 3)]) + assert sqf_list(p) == result + + +def test_issue_19113(): + eq = sin(x)**3 - sin(x) + 1 + raises(PolynomialError, lambda: refine_root(eq, 1, 2, 1e-2)) + raises(PolynomialError, lambda: count_roots(eq, -1, 1)) + raises(PolynomialError, lambda: real_roots(eq)) + raises(PolynomialError, lambda: nroots(eq)) + raises(PolynomialError, lambda: ground_roots(eq)) + raises(PolynomialError, lambda: nth_power_roots_poly(eq, 2)) + + +def test_issue_19360(): + f = 2*x**2 - 2*sqrt(2)*x*y + y**2 + assert factor(f, extension=sqrt(2)) == 2*(x - (sqrt(2)*y/2))**2 + + f = -I*t*x - t*y + x*z - I*y*z + assert factor(f, extension=I) == (x - I*y)*(-I*t + z) + + +def test_poly_copy_equals_original(): + poly = Poly(x + y, x, y, z) + copy = poly.copy() + assert poly == copy, ( + "Copied polynomial not equal to original.") + assert poly.gens == copy.gens, ( + "Copied polynomial has different generators than original.") + + +def test_deserialized_poly_equals_original(): + poly = Poly(x + y, x, y, z) + deserialized = pickle.loads(pickle.dumps(poly)) + assert poly == deserialized, ( + "Deserialized polynomial not equal to original.") + assert poly.gens == deserialized.gens, ( + "Deserialized polynomial has different generators than original.") + + +def test_issue_20389(): + result = degree(x * (x + 1) - x ** 2 - x, x) + assert result == -oo + + +def test_issue_20985(): + from sympy.core.symbol import symbols + w, R = symbols('w R') + poly = Poly(1.0 + I*w/R, w, 1/R) + assert poly.degree() == S(1) diff --git a/lib/python3.12/site-packages/sympy/polys/tests/test_polyutils.py b/lib/python3.12/site-packages/sympy/polys/tests/test_polyutils.py new file mode 100644 index 0000000000000000000000000000000000000000..f39561a1c5035fed52add5e49476d0eea91bdae0 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/polys/tests/test_polyutils.py @@ -0,0 +1,300 @@ +"""Tests for useful utilities for higher level polynomial classes. """ + +from sympy.core.mul import Mul +from sympy.core.numbers import (Integer, pi) +from sympy.core.relational import Eq +from sympy.core.singleton import S +from sympy.core.symbol import (Symbol, symbols) +from sympy.functions.elementary.exponential import exp +from sympy.functions.elementary.miscellaneous import sqrt +from sympy.functions.elementary.trigonometric import (cos, sin) +from sympy.integrals.integrals import Integral +from sympy.testing.pytest import raises + +from sympy.polys.polyutils import ( + _nsort, + _sort_gens, + _unify_gens, + _analyze_gens, + _sort_factors, + parallel_dict_from_expr, + dict_from_expr, +) + +from sympy.polys.polyerrors import PolynomialError + +from sympy.polys.domains import ZZ + +x, y, z, p, q, r, s, t, u, v, w = symbols('x,y,z,p,q,r,s,t,u,v,w') +A, B = symbols('A,B', commutative=False) + + +def test__nsort(): + # issue 6137 + r = S('''[3/2 + sqrt(-14/3 - 2*(-415/216 + 13*I/12)**(1/3) - 4/sqrt(-7/3 + + 61/(18*(-415/216 + 13*I/12)**(1/3)) + 2*(-415/216 + 13*I/12)**(1/3)) - + 61/(18*(-415/216 + 13*I/12)**(1/3)))/2 - sqrt(-7/3 + 61/(18*(-415/216 + + 13*I/12)**(1/3)) + 2*(-415/216 + 13*I/12)**(1/3))/2, 3/2 - sqrt(-7/3 + + 61/(18*(-415/216 + 13*I/12)**(1/3)) + 2*(-415/216 + + 13*I/12)**(1/3))/2 - sqrt(-14/3 - 2*(-415/216 + 13*I/12)**(1/3) - + 4/sqrt(-7/3 + 61/(18*(-415/216 + 13*I/12)**(1/3)) + 2*(-415/216 + + 13*I/12)**(1/3)) - 61/(18*(-415/216 + 13*I/12)**(1/3)))/2, 3/2 + + sqrt(-14/3 - 2*(-415/216 + 13*I/12)**(1/3) + 4/sqrt(-7/3 + + 61/(18*(-415/216 + 13*I/12)**(1/3)) + 2*(-415/216 + 13*I/12)**(1/3)) - + 61/(18*(-415/216 + 13*I/12)**(1/3)))/2 + sqrt(-7/3 + 61/(18*(-415/216 + + 13*I/12)**(1/3)) + 2*(-415/216 + 13*I/12)**(1/3))/2, 3/2 + sqrt(-7/3 + + 61/(18*(-415/216 + 13*I/12)**(1/3)) + 2*(-415/216 + + 13*I/12)**(1/3))/2 - sqrt(-14/3 - 2*(-415/216 + 13*I/12)**(1/3) + + 4/sqrt(-7/3 + 61/(18*(-415/216 + 13*I/12)**(1/3)) + 2*(-415/216 + + 13*I/12)**(1/3)) - 61/(18*(-415/216 + 13*I/12)**(1/3)))/2]''') + ans = [r[1], r[0], r[-1], r[-2]] + assert _nsort(r) == ans + assert len(_nsort(r, separated=True)[0]) == 0 + b, c, a = exp(-1000), exp(-999), exp(-1001) + assert _nsort((b, c, a)) == [a, b, c] + # issue 12560 + a = cos(1)**2 + sin(1)**2 - 1 + assert _nsort([a]) == [a] + + +def test__sort_gens(): + assert _sort_gens([]) == () + + assert _sort_gens([x]) == (x,) + assert _sort_gens([p]) == (p,) + assert _sort_gens([q]) == (q,) + + assert _sort_gens([x, p]) == (x, p) + assert _sort_gens([p, x]) == (x, p) + assert _sort_gens([q, p]) == (p, q) + + assert _sort_gens([q, p, x]) == (x, p, q) + + assert _sort_gens([x, p, q], wrt=x) == (x, p, q) + assert _sort_gens([x, p, q], wrt=p) == (p, x, q) + assert _sort_gens([x, p, q], wrt=q) == (q, x, p) + + assert _sort_gens([x, p, q], wrt='x') == (x, p, q) + assert _sort_gens([x, p, q], wrt='p') == (p, x, q) + assert _sort_gens([x, p, q], wrt='q') == (q, x, p) + + assert _sort_gens([x, p, q], wrt='x,q') == (x, q, p) + assert _sort_gens([x, p, q], wrt='q,x') == (q, x, p) + assert _sort_gens([x, p, q], wrt='p,q') == (p, q, x) + assert _sort_gens([x, p, q], wrt='q,p') == (q, p, x) + + assert _sort_gens([x, p, q], wrt='x, q') == (x, q, p) + assert _sort_gens([x, p, q], wrt='q, x') == (q, x, p) + assert _sort_gens([x, p, q], wrt='p, q') == (p, q, x) + assert _sort_gens([x, p, q], wrt='q, p') == (q, p, x) + + assert _sort_gens([x, p, q], wrt=[x, 'q']) == (x, q, p) + assert _sort_gens([x, p, q], wrt=[q, 'x']) == (q, x, p) + assert _sort_gens([x, p, q], wrt=[p, 'q']) == (p, q, x) + assert _sort_gens([x, p, q], wrt=[q, 'p']) == (q, p, x) + + assert _sort_gens([x, p, q], wrt=['x', 'q']) == (x, q, p) + assert _sort_gens([x, p, q], wrt=['q', 'x']) == (q, x, p) + assert _sort_gens([x, p, q], wrt=['p', 'q']) == (p, q, x) + assert _sort_gens([x, p, q], wrt=['q', 'p']) == (q, p, x) + + assert _sort_gens([x, p, q], sort='x > p > q') == (x, p, q) + assert _sort_gens([x, p, q], sort='p > x > q') == (p, x, q) + assert _sort_gens([x, p, q], sort='p > q > x') == (p, q, x) + + assert _sort_gens([x, p, q], wrt='x', sort='q > p') == (x, q, p) + assert _sort_gens([x, p, q], wrt='p', sort='q > x') == (p, q, x) + assert _sort_gens([x, p, q], wrt='q', sort='p > x') == (q, p, x) + + # https://github.com/sympy/sympy/issues/19353 + n1 = Symbol('\n1') + assert _sort_gens([n1]) == (n1,) + assert _sort_gens([x, n1]) == (x, n1) + + X = symbols('x0,x1,x2,x10,x11,x12,x20,x21,x22') + + assert _sort_gens(X) == X + + +def test__unify_gens(): + assert _unify_gens([], []) == () + + assert _unify_gens([x], [x]) == (x,) + assert _unify_gens([y], [y]) == (y,) + + assert _unify_gens([x, y], [x]) == (x, y) + assert _unify_gens([x], [x, y]) == (x, y) + + assert _unify_gens([x, y], [x, y]) == (x, y) + assert _unify_gens([y, x], [y, x]) == (y, x) + + assert _unify_gens([x], [y]) == (x, y) + assert _unify_gens([y], [x]) == (y, x) + + assert _unify_gens([x], [y, x]) == (y, x) + assert _unify_gens([y, x], [x]) == (y, x) + + assert _unify_gens([x, y, z], [x, y, z]) == (x, y, z) + assert _unify_gens([z, y, x], [x, y, z]) == (z, y, x) + assert _unify_gens([x, y, z], [z, y, x]) == (x, y, z) + assert _unify_gens([z, y, x], [z, y, x]) == (z, y, x) + + assert _unify_gens([x, y, z], [t, x, p, q, z]) == (t, x, y, p, q, z) + + +def test__analyze_gens(): + assert _analyze_gens((x, y, z)) == (x, y, z) + assert _analyze_gens([x, y, z]) == (x, y, z) + + assert _analyze_gens(([x, y, z],)) == (x, y, z) + assert _analyze_gens(((x, y, z),)) == (x, y, z) + + +def test__sort_factors(): + assert _sort_factors([], multiple=True) == [] + assert _sort_factors([], multiple=False) == [] + + F = [[1, 2, 3], [1, 2], [1]] + G = [[1], [1, 2], [1, 2, 3]] + + assert _sort_factors(F, multiple=False) == G + + F = [[1, 2], [1, 2, 3], [1, 2], [1]] + G = [[1], [1, 2], [1, 2], [1, 2, 3]] + + assert _sort_factors(F, multiple=False) == G + + F = [[2, 2], [1, 2, 3], [1, 2], [1]] + G = [[1], [1, 2], [2, 2], [1, 2, 3]] + + assert _sort_factors(F, multiple=False) == G + + F = [([1, 2, 3], 1), ([1, 2], 1), ([1], 1)] + G = [([1], 1), ([1, 2], 1), ([1, 2, 3], 1)] + + assert _sort_factors(F, multiple=True) == G + + F = [([1, 2], 1), ([1, 2, 3], 1), ([1, 2], 1), ([1], 1)] + G = [([1], 1), ([1, 2], 1), ([1, 2], 1), ([1, 2, 3], 1)] + + assert _sort_factors(F, multiple=True) == G + + F = [([2, 2], 1), ([1, 2, 3], 1), ([1, 2], 1), ([1], 1)] + G = [([1], 1), ([1, 2], 1), ([2, 2], 1), ([1, 2, 3], 1)] + + assert _sort_factors(F, multiple=True) == G + + F = [([2, 2], 1), ([1, 2, 3], 1), ([1, 2], 2), ([1], 1)] + G = [([1], 1), ([2, 2], 1), ([1, 2], 2), ([1, 2, 3], 1)] + + assert _sort_factors(F, multiple=True) == G + + +def test__dict_from_expr_if_gens(): + assert dict_from_expr( + Integer(17), gens=(x,)) == ({(0,): Integer(17)}, (x,)) + assert dict_from_expr( + Integer(17), gens=(x, y)) == ({(0, 0): Integer(17)}, (x, y)) + assert dict_from_expr( + Integer(17), gens=(x, y, z)) == ({(0, 0, 0): Integer(17)}, (x, y, z)) + + assert dict_from_expr( + Integer(-17), gens=(x,)) == ({(0,): Integer(-17)}, (x,)) + assert dict_from_expr( + Integer(-17), gens=(x, y)) == ({(0, 0): Integer(-17)}, (x, y)) + assert dict_from_expr(Integer( + -17), gens=(x, y, z)) == ({(0, 0, 0): Integer(-17)}, (x, y, z)) + + assert dict_from_expr( + Integer(17)*x, gens=(x,)) == ({(1,): Integer(17)}, (x,)) + assert dict_from_expr( + Integer(17)*x, gens=(x, y)) == ({(1, 0): Integer(17)}, (x, y)) + assert dict_from_expr(Integer( + 17)*x, gens=(x, y, z)) == ({(1, 0, 0): Integer(17)}, (x, y, z)) + + assert dict_from_expr( + Integer(17)*x**7, gens=(x,)) == ({(7,): Integer(17)}, (x,)) + assert dict_from_expr( + Integer(17)*x**7*y, gens=(x, y)) == ({(7, 1): Integer(17)}, (x, y)) + assert dict_from_expr(Integer(17)*x**7*y*z**12, gens=( + x, y, z)) == ({(7, 1, 12): Integer(17)}, (x, y, z)) + + assert dict_from_expr(x + 2*y + 3*z, gens=(x,)) == \ + ({(1,): Integer(1), (0,): 2*y + 3*z}, (x,)) + assert dict_from_expr(x + 2*y + 3*z, gens=(x, y)) == \ + ({(1, 0): Integer(1), (0, 1): Integer(2), (0, 0): 3*z}, (x, y)) + assert dict_from_expr(x + 2*y + 3*z, gens=(x, y, z)) == \ + ({(1, 0, 0): Integer( + 1), (0, 1, 0): Integer(2), (0, 0, 1): Integer(3)}, (x, y, z)) + + assert dict_from_expr(x*y + 2*x*z + 3*y*z, gens=(x,)) == \ + ({(1,): y + 2*z, (0,): 3*y*z}, (x,)) + assert dict_from_expr(x*y + 2*x*z + 3*y*z, gens=(x, y)) == \ + ({(1, 1): Integer(1), (1, 0): 2*z, (0, 1): 3*z}, (x, y)) + assert dict_from_expr(x*y + 2*x*z + 3*y*z, gens=(x, y, z)) == \ + ({(1, 1, 0): Integer( + 1), (1, 0, 1): Integer(2), (0, 1, 1): Integer(3)}, (x, y, z)) + + assert dict_from_expr(2**y*x, gens=(x,)) == ({(1,): 2**y}, (x,)) + assert dict_from_expr(Integral(x, (x, 1, 2)) + x) == ( + {(0, 1): 1, (1, 0): 1}, (x, Integral(x, (x, 1, 2)))) + raises(PolynomialError, lambda: dict_from_expr(2**y*x, gens=(x, y))) + + +def test__dict_from_expr_no_gens(): + assert dict_from_expr(Integer(17)) == ({(): Integer(17)}, ()) + + assert dict_from_expr(x) == ({(1,): Integer(1)}, (x,)) + assert dict_from_expr(y) == ({(1,): Integer(1)}, (y,)) + + assert dict_from_expr(x*y) == ({(1, 1): Integer(1)}, (x, y)) + assert dict_from_expr( + x + y) == ({(1, 0): Integer(1), (0, 1): Integer(1)}, (x, y)) + + assert dict_from_expr(sqrt(2)) == ({(1,): Integer(1)}, (sqrt(2),)) + assert dict_from_expr(sqrt(2), greedy=False) == ({(): sqrt(2)}, ()) + + assert dict_from_expr(x*y, domain=ZZ[x]) == ({(1,): x}, (y,)) + assert dict_from_expr(x*y, domain=ZZ[y]) == ({(1,): y}, (x,)) + + assert dict_from_expr(3*sqrt( + 2)*pi*x*y, extension=None) == ({(1, 1, 1, 1): 3}, (x, y, pi, sqrt(2))) + assert dict_from_expr(3*sqrt( + 2)*pi*x*y, extension=True) == ({(1, 1, 1): 3*sqrt(2)}, (x, y, pi)) + + assert dict_from_expr(3*sqrt( + 2)*pi*x*y, extension=True) == ({(1, 1, 1): 3*sqrt(2)}, (x, y, pi)) + + f = cos(x)*sin(x) + cos(x)*sin(y) + cos(y)*sin(x) + cos(y)*sin(y) + + assert dict_from_expr(f) == ({(0, 1, 0, 1): 1, (0, 1, 1, 0): 1, + (1, 0, 0, 1): 1, (1, 0, 1, 0): 1}, (cos(x), cos(y), sin(x), sin(y))) + + +def test__parallel_dict_from_expr_if_gens(): + assert parallel_dict_from_expr([x + 2*y + 3*z, Integer(7)], gens=(x,)) == \ + ([{(1,): Integer(1), (0,): 2*y + 3*z}, {(0,): Integer(7)}], (x,)) + + +def test__parallel_dict_from_expr_no_gens(): + assert parallel_dict_from_expr([x*y, Integer(3)]) == \ + ([{(1, 1): Integer(1)}, {(0, 0): Integer(3)}], (x, y)) + assert parallel_dict_from_expr([x*y, 2*z, Integer(3)]) == \ + ([{(1, 1, 0): Integer( + 1)}, {(0, 0, 1): Integer(2)}, {(0, 0, 0): Integer(3)}], (x, y, z)) + assert parallel_dict_from_expr((Mul(x, x**2, evaluate=False),)) == \ + ([{(3,): 1}], (x,)) + + +def test_parallel_dict_from_expr(): + assert parallel_dict_from_expr([Eq(x, 1), Eq( + x**2, 2)]) == ([{(0,): -Integer(1), (1,): Integer(1)}, + {(0,): -Integer(2), (2,): Integer(1)}], (x,)) + raises(PolynomialError, lambda: parallel_dict_from_expr([A*B - B*A])) + + +def test_dict_from_expr(): + assert dict_from_expr(Eq(x, 1)) == \ + ({(0,): -Integer(1), (1,): Integer(1)}, (x,)) + raises(PolynomialError, lambda: dict_from_expr(A*B - B*A)) + raises(PolynomialError, lambda: dict_from_expr(S.true)) diff --git a/lib/python3.12/site-packages/sympy/polys/tests/test_pythonrational.py b/lib/python3.12/site-packages/sympy/polys/tests/test_pythonrational.py new file mode 100644 index 0000000000000000000000000000000000000000..547a5679626fd3a6165b151364bb506a574bb1db --- /dev/null +++ b/lib/python3.12/site-packages/sympy/polys/tests/test_pythonrational.py @@ -0,0 +1,139 @@ +"""Tests for PythonRational type. """ + +from sympy.polys.domains import PythonRational as QQ +from sympy.testing.pytest import raises + +def test_PythonRational__init__(): + assert QQ(0).numerator == 0 + assert QQ(0).denominator == 1 + assert QQ(0, 1).numerator == 0 + assert QQ(0, 1).denominator == 1 + assert QQ(0, -1).numerator == 0 + assert QQ(0, -1).denominator == 1 + + assert QQ(1).numerator == 1 + assert QQ(1).denominator == 1 + assert QQ(1, 1).numerator == 1 + assert QQ(1, 1).denominator == 1 + assert QQ(-1, -1).numerator == 1 + assert QQ(-1, -1).denominator == 1 + + assert QQ(-1).numerator == -1 + assert QQ(-1).denominator == 1 + assert QQ(-1, 1).numerator == -1 + assert QQ(-1, 1).denominator == 1 + assert QQ( 1, -1).numerator == -1 + assert QQ( 1, -1).denominator == 1 + + assert QQ(1, 2).numerator == 1 + assert QQ(1, 2).denominator == 2 + assert QQ(3, 4).numerator == 3 + assert QQ(3, 4).denominator == 4 + + assert QQ(2, 2).numerator == 1 + assert QQ(2, 2).denominator == 1 + assert QQ(2, 4).numerator == 1 + assert QQ(2, 4).denominator == 2 + +def test_PythonRational__hash__(): + assert hash(QQ(0)) == hash(0) + assert hash(QQ(1)) == hash(1) + assert hash(QQ(117)) == hash(117) + +def test_PythonRational__int__(): + assert int(QQ(-1, 4)) == 0 + assert int(QQ( 1, 4)) == 0 + assert int(QQ(-5, 4)) == -1 + assert int(QQ( 5, 4)) == 1 + +def test_PythonRational__float__(): + assert float(QQ(-1, 2)) == -0.5 + assert float(QQ( 1, 2)) == 0.5 + +def test_PythonRational__abs__(): + assert abs(QQ(-1, 2)) == QQ(1, 2) + assert abs(QQ( 1, 2)) == QQ(1, 2) + +def test_PythonRational__pos__(): + assert +QQ(-1, 2) == QQ(-1, 2) + assert +QQ( 1, 2) == QQ( 1, 2) + +def test_PythonRational__neg__(): + assert -QQ(-1, 2) == QQ( 1, 2) + assert -QQ( 1, 2) == QQ(-1, 2) + +def test_PythonRational__add__(): + assert QQ(-1, 2) + QQ( 1, 2) == QQ(0) + assert QQ( 1, 2) + QQ(-1, 2) == QQ(0) + + assert QQ(1, 2) + QQ(1, 2) == QQ(1) + assert QQ(1, 2) + QQ(3, 2) == QQ(2) + assert QQ(3, 2) + QQ(1, 2) == QQ(2) + assert QQ(3, 2) + QQ(3, 2) == QQ(3) + + assert 1 + QQ(1, 2) == QQ(3, 2) + assert QQ(1, 2) + 1 == QQ(3, 2) + +def test_PythonRational__sub__(): + assert QQ(-1, 2) - QQ( 1, 2) == QQ(-1) + assert QQ( 1, 2) - QQ(-1, 2) == QQ( 1) + + assert QQ(1, 2) - QQ(1, 2) == QQ( 0) + assert QQ(1, 2) - QQ(3, 2) == QQ(-1) + assert QQ(3, 2) - QQ(1, 2) == QQ( 1) + assert QQ(3, 2) - QQ(3, 2) == QQ( 0) + + assert 1 - QQ(1, 2) == QQ( 1, 2) + assert QQ(1, 2) - 1 == QQ(-1, 2) + +def test_PythonRational__mul__(): + assert QQ(-1, 2) * QQ( 1, 2) == QQ(-1, 4) + assert QQ( 1, 2) * QQ(-1, 2) == QQ(-1, 4) + + assert QQ(1, 2) * QQ(1, 2) == QQ(1, 4) + assert QQ(1, 2) * QQ(3, 2) == QQ(3, 4) + assert QQ(3, 2) * QQ(1, 2) == QQ(3, 4) + assert QQ(3, 2) * QQ(3, 2) == QQ(9, 4) + + assert 2 * QQ(1, 2) == QQ(1) + assert QQ(1, 2) * 2 == QQ(1) + +def test_PythonRational__truediv__(): + assert QQ(-1, 2) / QQ( 1, 2) == QQ(-1) + assert QQ( 1, 2) / QQ(-1, 2) == QQ(-1) + + assert QQ(1, 2) / QQ(1, 2) == QQ(1) + assert QQ(1, 2) / QQ(3, 2) == QQ(1, 3) + assert QQ(3, 2) / QQ(1, 2) == QQ(3) + assert QQ(3, 2) / QQ(3, 2) == QQ(1) + + assert 2 / QQ(1, 2) == QQ(4) + assert QQ(1, 2) / 2 == QQ(1, 4) + + raises(ZeroDivisionError, lambda: QQ(1, 2) / QQ(0)) + raises(ZeroDivisionError, lambda: QQ(1, 2) / 0) + +def test_PythonRational__pow__(): + assert QQ(1)**10 == QQ(1) + assert QQ(2)**10 == QQ(1024) + + assert QQ(1)**(-10) == QQ(1) + assert QQ(2)**(-10) == QQ(1, 1024) + +def test_PythonRational__eq__(): + assert (QQ(1, 2) == QQ(1, 2)) is True + assert (QQ(1, 2) != QQ(1, 2)) is False + + assert (QQ(1, 2) == QQ(1, 3)) is False + assert (QQ(1, 2) != QQ(1, 3)) is True + +def test_PythonRational__lt_le_gt_ge__(): + assert (QQ(1, 2) < QQ(1, 4)) is False + assert (QQ(1, 2) <= QQ(1, 4)) is False + assert (QQ(1, 2) > QQ(1, 4)) is True + assert (QQ(1, 2) >= QQ(1, 4)) is True + + assert (QQ(1, 4) < QQ(1, 2)) is True + assert (QQ(1, 4) <= QQ(1, 2)) is True + assert (QQ(1, 4) > QQ(1, 2)) is False + assert (QQ(1, 4) >= QQ(1, 2)) is False diff --git a/lib/python3.12/site-packages/sympy/polys/tests/test_rationaltools.py b/lib/python3.12/site-packages/sympy/polys/tests/test_rationaltools.py new file mode 100644 index 0000000000000000000000000000000000000000..3ee0192a3fbc8997347df081663015afd91dd8ad --- /dev/null +++ b/lib/python3.12/site-packages/sympy/polys/tests/test_rationaltools.py @@ -0,0 +1,63 @@ +"""Tests for tools for manipulation of rational expressions. """ + +from sympy.polys.rationaltools import together + +from sympy.core.mul import Mul +from sympy.core.numbers import Rational +from sympy.core.relational import Eq +from sympy.core.singleton import S +from sympy.core.symbol import symbols +from sympy.functions.elementary.exponential import exp +from sympy.functions.elementary.trigonometric import sin +from sympy.integrals.integrals import Integral +from sympy.abc import x, y, z + +A, B = symbols('A,B', commutative=False) + + +def test_together(): + assert together(0) == 0 + assert together(1) == 1 + + assert together(x*y*z) == x*y*z + assert together(x + y) == x + y + + assert together(1/x) == 1/x + + assert together(1/x + 1) == (x + 1)/x + assert together(1/x + 3) == (3*x + 1)/x + assert together(1/x + x) == (x**2 + 1)/x + + assert together(1/x + S.Half) == (x + 2)/(2*x) + assert together(S.Half + x/2) == Mul(S.Half, x + 1, evaluate=False) + + assert together(1/x + 2/y) == (2*x + y)/(y*x) + assert together(1/(1 + 1/x)) == x/(1 + x) + assert together(x/(1 + 1/x)) == x**2/(1 + x) + + assert together(1/x + 1/y + 1/z) == (x*y + x*z + y*z)/(x*y*z) + assert together(1/(1 + x + 1/y + 1/z)) == y*z/(y + z + y*z + x*y*z) + + assert together(1/(x*y) + 1/(x*y)**2) == y**(-2)*x**(-2)*(1 + x*y) + assert together(1/(x*y) + 1/(x*y)**4) == y**(-4)*x**(-4)*(1 + x**3*y**3) + assert together(1/(x**7*y) + 1/(x*y)**4) == y**(-4)*x**(-7)*(x**3 + y**3) + + assert together(5/(2 + 6/(3 + 7/(4 + 8/(5 + 9/x))))) == \ + Rational(5, 2)*((171 + 119*x)/(279 + 203*x)) + + assert together(1 + 1/(x + 1)**2) == (1 + (x + 1)**2)/(x + 1)**2 + assert together(1 + 1/(x*(1 + x))) == (1 + x*(1 + x))/(x*(1 + x)) + assert together( + 1/(x*(x + 1)) + 1/(x*(x + 2))) == (3 + 2*x)/(x*(1 + x)*(2 + x)) + assert together(1 + 1/(2*x + 2)**2) == (4*(x + 1)**2 + 1)/(4*(x + 1)**2) + + assert together(sin(1/x + 1/y)) == sin(1/x + 1/y) + assert together(sin(1/x + 1/y), deep=True) == sin((x + y)/(x*y)) + + assert together(1/exp(x) + 1/(x*exp(x))) == (1 + x)/(x*exp(x)) + assert together(1/exp(2*x) + 1/(x*exp(3*x))) == (1 + exp(x)*x)/(x*exp(3*x)) + + assert together(Integral(1/x + 1/y, x)) == Integral((x + y)/(x*y), x) + assert together(Eq(1/x + 1/y, 1 + 1/z)) == Eq((x + y)/(x*y), (z + 1)/z) + + assert together((A*B)**-1 + (B*A)**-1) == (A*B)**-1 + (B*A)**-1 diff --git a/lib/python3.12/site-packages/sympy/polys/tests/test_ring_series.py b/lib/python3.12/site-packages/sympy/polys/tests/test_ring_series.py new file mode 100644 index 0000000000000000000000000000000000000000..0f70c05d3888376c685948bee82123b7d25ea182 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/polys/tests/test_ring_series.py @@ -0,0 +1,635 @@ +from sympy.polys.domains import QQ, EX, RR +from sympy.polys.rings import ring +from sympy.polys.ring_series import (_invert_monoms, rs_integrate, + rs_trunc, rs_mul, rs_square, rs_pow, _has_constant_term, rs_hadamard_exp, + rs_series_from_list, rs_exp, rs_log, rs_newton, rs_series_inversion, + rs_compose_add, rs_asin, rs_atan, rs_atanh, rs_tan, rs_cot, rs_sin, rs_cos, + rs_cos_sin, rs_sinh, rs_cosh, rs_tanh, _tan1, rs_fun, rs_nth_root, + rs_LambertW, rs_series_reversion, rs_is_puiseux, rs_series) +from sympy.testing.pytest import raises, slow +from sympy.core.symbol import symbols +from sympy.functions import (sin, cos, exp, tan, cot, atan, atanh, + tanh, log, sqrt) +from sympy.core.numbers import Rational +from sympy.core import expand, S + +def is_close(a, b): + tol = 10**(-10) + assert abs(a - b) < tol + +def test_ring_series1(): + R, x = ring('x', QQ) + p = x**4 + 2*x**3 + 3*x + 4 + assert _invert_monoms(p) == 4*x**4 + 3*x**3 + 2*x + 1 + assert rs_hadamard_exp(p) == x**4/24 + x**3/3 + 3*x + 4 + R, x = ring('x', QQ) + p = x**4 + 2*x**3 + 3*x + 4 + assert rs_integrate(p, x) == x**5/5 + x**4/2 + 3*x**2/2 + 4*x + R, x, y = ring('x, y', QQ) + p = x**2*y**2 + x + 1 + assert rs_integrate(p, x) == x**3*y**2/3 + x**2/2 + x + assert rs_integrate(p, y) == x**2*y**3/3 + x*y + y + +def test_trunc(): + R, x, y, t = ring('x, y, t', QQ) + p = (y + t*x)**4 + p1 = rs_trunc(p, x, 3) + assert p1 == y**4 + 4*y**3*t*x + 6*y**2*t**2*x**2 + +def test_mul_trunc(): + R, x, y, t = ring('x, y, t', QQ) + p = 1 + t*x + t*y + for i in range(2): + p = rs_mul(p, p, t, 3) + + assert p == 6*x**2*t**2 + 12*x*y*t**2 + 6*y**2*t**2 + 4*x*t + 4*y*t + 1 + p = 1 + t*x + t*y + t**2*x*y + p1 = rs_mul(p, p, t, 2) + assert p1 == 1 + 2*t*x + 2*t*y + R1, z = ring('z', QQ) + raises(ValueError, lambda: rs_mul(p, z, x, 2)) + + p1 = 2 + 2*x + 3*x**2 + p2 = 3 + x**2 + assert rs_mul(p1, p2, x, 4) == 2*x**3 + 11*x**2 + 6*x + 6 + +def test_square_trunc(): + R, x, y, t = ring('x, y, t', QQ) + p = (1 + t*x + t*y)*2 + p1 = rs_mul(p, p, x, 3) + p2 = rs_square(p, x, 3) + assert p1 == p2 + p = 1 + x + x**2 + x**3 + assert rs_square(p, x, 4) == 4*x**3 + 3*x**2 + 2*x + 1 + +def test_pow_trunc(): + R, x, y, z = ring('x, y, z', QQ) + p0 = y + x*z + p = p0**16 + for xx in (x, y, z): + p1 = rs_trunc(p, xx, 8) + p2 = rs_pow(p0, 16, xx, 8) + assert p1 == p2 + + p = 1 + x + p1 = rs_pow(p, 3, x, 2) + assert p1 == 1 + 3*x + assert rs_pow(p, 0, x, 2) == 1 + assert rs_pow(p, -2, x, 2) == 1 - 2*x + p = x + y + assert rs_pow(p, 3, y, 3) == x**3 + 3*x**2*y + 3*x*y**2 + assert rs_pow(1 + x, Rational(2, 3), x, 4) == 4*x**3/81 - x**2/9 + x*Rational(2, 3) + 1 + +def test_has_constant_term(): + R, x, y, z = ring('x, y, z', QQ) + p = y + x*z + assert _has_constant_term(p, x) + p = x + x**4 + assert not _has_constant_term(p, x) + p = 1 + x + x**4 + assert _has_constant_term(p, x) + p = x + y + x*z + +def test_inversion(): + R, x = ring('x', QQ) + p = 2 + x + 2*x**2 + n = 5 + p1 = rs_series_inversion(p, x, n) + assert rs_trunc(p*p1, x, n) == 1 + R, x, y = ring('x, y', QQ) + p = 2 + x + 2*x**2 + y*x + x**2*y + p1 = rs_series_inversion(p, x, n) + assert rs_trunc(p*p1, x, n) == 1 + + R, x, y = ring('x, y', QQ) + p = 1 + x + y + raises(NotImplementedError, lambda: rs_series_inversion(p, x, 4)) + p = R.zero + raises(ZeroDivisionError, lambda: rs_series_inversion(p, x, 3)) + + +def test_series_reversion(): + R, x, y = ring('x, y', QQ) + + p = rs_tan(x, x, 10) + assert rs_series_reversion(p, x, 8, y) == rs_atan(y, y, 8) + + p = rs_sin(x, x, 10) + assert rs_series_reversion(p, x, 8, y) == 5*y**7/112 + 3*y**5/40 + \ + y**3/6 + y + +def test_series_from_list(): + R, x = ring('x', QQ) + p = 1 + 2*x + x**2 + 3*x**3 + c = [1, 2, 0, 4, 4] + r = rs_series_from_list(p, c, x, 5) + pc = R.from_list(list(reversed(c))) + r1 = rs_trunc(pc.compose(x, p), x, 5) + assert r == r1 + R, x, y = ring('x, y', QQ) + c = [1, 3, 5, 7] + p1 = rs_series_from_list(x + y, c, x, 3, concur=0) + p2 = rs_trunc((1 + 3*(x+y) + 5*(x+y)**2 + 7*(x+y)**3), x, 3) + assert p1 == p2 + + R, x = ring('x', QQ) + h = 25 + p = rs_exp(x, x, h) - 1 + p1 = rs_series_from_list(p, c, x, h) + p2 = 0 + for i, cx in enumerate(c): + p2 += cx*rs_pow(p, i, x, h) + assert p1 == p2 + +def test_log(): + R, x = ring('x', QQ) + p = 1 + x + p1 = rs_log(p, x, 4)/x**2 + assert p1 == Rational(1, 3)*x - S.Half + x**(-1) + p = 1 + x +2*x**2/3 + p1 = rs_log(p, x, 9) + assert p1 == -17*x**8/648 + 13*x**7/189 - 11*x**6/162 - x**5/45 + \ + 7*x**4/36 - x**3/3 + x**2/6 + x + p2 = rs_series_inversion(p, x, 9) + p3 = rs_log(p2, x, 9) + assert p3 == -p1 + + R, x, y = ring('x, y', QQ) + p = 1 + x + 2*y*x**2 + p1 = rs_log(p, x, 6) + assert p1 == (4*x**5*y**2 - 2*x**5*y - 2*x**4*y**2 + x**5/5 + 2*x**4*y - + x**4/4 - 2*x**3*y + x**3/3 + 2*x**2*y - x**2/2 + x) + + # Constant term in series + a = symbols('a') + R, x, y = ring('x, y', EX) + assert rs_log(x + a, x, 5) == -EX(1/(4*a**4))*x**4 + EX(1/(3*a**3))*x**3 \ + - EX(1/(2*a**2))*x**2 + EX(1/a)*x + EX(log(a)) + assert rs_log(x + x**2*y + a, x, 4) == -EX(a**(-2))*x**3*y + \ + EX(1/(3*a**3))*x**3 + EX(1/a)*x**2*y - EX(1/(2*a**2))*x**2 + \ + EX(1/a)*x + EX(log(a)) + + p = x + x**2 + 3 + assert rs_log(p, x, 10).compose(x, 5) == EX(log(3) + Rational(19281291595, 9920232)) + +def test_exp(): + R, x = ring('x', QQ) + p = x + x**4 + for h in [10, 30]: + q = rs_series_inversion(1 + p, x, h) - 1 + p1 = rs_exp(q, x, h) + q1 = rs_log(p1, x, h) + assert q1 == q + p1 = rs_exp(p, x, 30) + assert p1.coeff(x**29) == QQ(74274246775059676726972369, 353670479749588078181744640000) + prec = 21 + p = rs_log(1 + x, x, prec) + p1 = rs_exp(p, x, prec) + assert p1 == x + 1 + + # Constant term in series + a = symbols('a') + R, x, y = ring('x, y', QQ[exp(a), a]) + assert rs_exp(x + a, x, 5) == exp(a)*x**4/24 + exp(a)*x**3/6 + \ + exp(a)*x**2/2 + exp(a)*x + exp(a) + assert rs_exp(x + x**2*y + a, x, 5) == exp(a)*x**4*y**2/2 + \ + exp(a)*x**4*y/2 + exp(a)*x**4/24 + exp(a)*x**3*y + \ + exp(a)*x**3/6 + exp(a)*x**2*y + exp(a)*x**2/2 + exp(a)*x + exp(a) + + R, x, y = ring('x, y', EX) + assert rs_exp(x + a, x, 5) == EX(exp(a)/24)*x**4 + EX(exp(a)/6)*x**3 + \ + EX(exp(a)/2)*x**2 + EX(exp(a))*x + EX(exp(a)) + assert rs_exp(x + x**2*y + a, x, 5) == EX(exp(a)/2)*x**4*y**2 + \ + EX(exp(a)/2)*x**4*y + EX(exp(a)/24)*x**4 + EX(exp(a))*x**3*y + \ + EX(exp(a)/6)*x**3 + EX(exp(a))*x**2*y + EX(exp(a)/2)*x**2 + \ + EX(exp(a))*x + EX(exp(a)) + +def test_newton(): + R, x = ring('x', QQ) + p = x**2 - 2 + r = rs_newton(p, x, 4) + assert r == 8*x**4 + 4*x**2 + 2 + +def test_compose_add(): + R, x = ring('x', QQ) + p1 = x**3 - 1 + p2 = x**2 - 2 + assert rs_compose_add(p1, p2) == x**6 - 6*x**4 - 2*x**3 + 12*x**2 - 12*x - 7 + +def test_fun(): + R, x, y = ring('x, y', QQ) + p = x*y + x**2*y**3 + x**5*y + assert rs_fun(p, rs_tan, x, 10) == rs_tan(p, x, 10) + assert rs_fun(p, _tan1, x, 10) == _tan1(p, x, 10) + +def test_nth_root(): + R, x, y = ring('x, y', QQ) + assert rs_nth_root(1 + x**2*y, 4, x, 10) == -77*x**8*y**4/2048 + \ + 7*x**6*y**3/128 - 3*x**4*y**2/32 + x**2*y/4 + 1 + assert rs_nth_root(1 + x*y + x**2*y**3, 3, x, 5) == -x**4*y**6/9 + \ + 5*x**4*y**5/27 - 10*x**4*y**4/243 - 2*x**3*y**4/9 + 5*x**3*y**3/81 + \ + x**2*y**3/3 - x**2*y**2/9 + x*y/3 + 1 + assert rs_nth_root(8*x, 3, x, 3) == 2*x**QQ(1, 3) + assert rs_nth_root(8*x + x**2 + x**3, 3, x, 3) == x**QQ(4,3)/12 + 2*x**QQ(1,3) + r = rs_nth_root(8*x + x**2*y + x**3, 3, x, 4) + assert r == -x**QQ(7,3)*y**2/288 + x**QQ(7,3)/12 + x**QQ(4,3)*y/12 + 2*x**QQ(1,3) + + # Constant term in series + a = symbols('a') + R, x, y = ring('x, y', EX) + assert rs_nth_root(x + a, 3, x, 4) == EX(5/(81*a**QQ(8, 3)))*x**3 - \ + EX(1/(9*a**QQ(5, 3)))*x**2 + EX(1/(3*a**QQ(2, 3)))*x + EX(a**QQ(1, 3)) + assert rs_nth_root(x**QQ(2, 3) + x**2*y + 5, 2, x, 3) == -EX(sqrt(5)/100)*\ + x**QQ(8, 3)*y - EX(sqrt(5)/16000)*x**QQ(8, 3) + EX(sqrt(5)/10)*x**2*y + \ + EX(sqrt(5)/2000)*x**2 - EX(sqrt(5)/200)*x**QQ(4, 3) + \ + EX(sqrt(5)/10)*x**QQ(2, 3) + EX(sqrt(5)) + +def test_atan(): + R, x, y = ring('x, y', QQ) + assert rs_atan(x, x, 9) == -x**7/7 + x**5/5 - x**3/3 + x + assert rs_atan(x*y + x**2*y**3, x, 9) == 2*x**8*y**11 - x**8*y**9 + \ + 2*x**7*y**9 - x**7*y**7/7 - x**6*y**9/3 + x**6*y**7 - x**5*y**7 + \ + x**5*y**5/5 - x**4*y**5 - x**3*y**3/3 + x**2*y**3 + x*y + + # Constant term in series + a = symbols('a') + R, x, y = ring('x, y', EX) + assert rs_atan(x + a, x, 5) == -EX((a**3 - a)/(a**8 + 4*a**6 + 6*a**4 + \ + 4*a**2 + 1))*x**4 + EX((3*a**2 - 1)/(3*a**6 + 9*a**4 + \ + 9*a**2 + 3))*x**3 - EX(a/(a**4 + 2*a**2 + 1))*x**2 + \ + EX(1/(a**2 + 1))*x + EX(atan(a)) + assert rs_atan(x + x**2*y + a, x, 4) == -EX(2*a/(a**4 + 2*a**2 + 1)) \ + *x**3*y + EX((3*a**2 - 1)/(3*a**6 + 9*a**4 + 9*a**2 + 3))*x**3 + \ + EX(1/(a**2 + 1))*x**2*y - EX(a/(a**4 + 2*a**2 + 1))*x**2 + EX(1/(a**2 \ + + 1))*x + EX(atan(a)) + +def test_asin(): + R, x, y = ring('x, y', QQ) + assert rs_asin(x + x*y, x, 5) == x**3*y**3/6 + x**3*y**2/2 + x**3*y/2 + \ + x**3/6 + x*y + x + assert rs_asin(x*y + x**2*y**3, x, 6) == x**5*y**7/2 + 3*x**5*y**5/40 + \ + x**4*y**5/2 + x**3*y**3/6 + x**2*y**3 + x*y + +def test_tan(): + R, x, y = ring('x, y', QQ) + assert rs_tan(x, x, 9)/x**5 == \ + Rational(17, 315)*x**2 + Rational(2, 15) + Rational(1, 3)*x**(-2) + x**(-4) + assert rs_tan(x*y + x**2*y**3, x, 9) == 4*x**8*y**11/3 + 17*x**8*y**9/45 + \ + 4*x**7*y**9/3 + 17*x**7*y**7/315 + x**6*y**9/3 + 2*x**6*y**7/3 + \ + x**5*y**7 + 2*x**5*y**5/15 + x**4*y**5 + x**3*y**3/3 + x**2*y**3 + x*y + + # Constant term in series + a = symbols('a') + R, x, y = ring('x, y', QQ[tan(a), a]) + assert rs_tan(x + a, x, 5) == (tan(a)**5 + 5*tan(a)**3/3 + + 2*tan(a)/3)*x**4 + (tan(a)**4 + 4*tan(a)**2/3 + Rational(1, 3))*x**3 + \ + (tan(a)**3 + tan(a))*x**2 + (tan(a)**2 + 1)*x + tan(a) + assert rs_tan(x + x**2*y + a, x, 4) == (2*tan(a)**3 + 2*tan(a))*x**3*y + \ + (tan(a)**4 + Rational(4, 3)*tan(a)**2 + Rational(1, 3))*x**3 + (tan(a)**2 + 1)*x**2*y + \ + (tan(a)**3 + tan(a))*x**2 + (tan(a)**2 + 1)*x + tan(a) + + R, x, y = ring('x, y', EX) + assert rs_tan(x + a, x, 5) == EX(tan(a)**5 + 5*tan(a)**3/3 + + 2*tan(a)/3)*x**4 + EX(tan(a)**4 + 4*tan(a)**2/3 + EX(1)/3)*x**3 + \ + EX(tan(a)**3 + tan(a))*x**2 + EX(tan(a)**2 + 1)*x + EX(tan(a)) + assert rs_tan(x + x**2*y + a, x, 4) == EX(2*tan(a)**3 + + 2*tan(a))*x**3*y + EX(tan(a)**4 + 4*tan(a)**2/3 + EX(1)/3)*x**3 + \ + EX(tan(a)**2 + 1)*x**2*y + EX(tan(a)**3 + tan(a))*x**2 + \ + EX(tan(a)**2 + 1)*x + EX(tan(a)) + + p = x + x**2 + 5 + assert rs_atan(p, x, 10).compose(x, 10) == EX(atan(5) + S(67701870330562640) / \ + 668083460499) + +def test_cot(): + R, x, y = ring('x, y', QQ) + assert rs_cot(x**6 + x**7, x, 8) == x**(-6) - x**(-5) + x**(-4) - \ + x**(-3) + x**(-2) - x**(-1) + 1 - x + x**2 - x**3 + x**4 - x**5 + \ + 2*x**6/3 - 4*x**7/3 + assert rs_cot(x + x**2*y, x, 5) == -x**4*y**5 - x**4*y/15 + x**3*y**4 - \ + x**3/45 - x**2*y**3 - x**2*y/3 + x*y**2 - x/3 - y + x**(-1) + +def test_sin(): + R, x, y = ring('x, y', QQ) + assert rs_sin(x, x, 9)/x**5 == \ + Rational(-1, 5040)*x**2 + Rational(1, 120) - Rational(1, 6)*x**(-2) + x**(-4) + assert rs_sin(x*y + x**2*y**3, x, 9) == x**8*y**11/12 - \ + x**8*y**9/720 + x**7*y**9/12 - x**7*y**7/5040 - x**6*y**9/6 + \ + x**6*y**7/24 - x**5*y**7/2 + x**5*y**5/120 - x**4*y**5/2 - \ + x**3*y**3/6 + x**2*y**3 + x*y + + # Constant term in series + a = symbols('a') + R, x, y = ring('x, y', QQ[sin(a), cos(a), a]) + assert rs_sin(x + a, x, 5) == sin(a)*x**4/24 - cos(a)*x**3/6 - \ + sin(a)*x**2/2 + cos(a)*x + sin(a) + assert rs_sin(x + x**2*y + a, x, 5) == -sin(a)*x**4*y**2/2 - \ + cos(a)*x**4*y/2 + sin(a)*x**4/24 - sin(a)*x**3*y - cos(a)*x**3/6 + \ + cos(a)*x**2*y - sin(a)*x**2/2 + cos(a)*x + sin(a) + + R, x, y = ring('x, y', EX) + assert rs_sin(x + a, x, 5) == EX(sin(a)/24)*x**4 - EX(cos(a)/6)*x**3 - \ + EX(sin(a)/2)*x**2 + EX(cos(a))*x + EX(sin(a)) + assert rs_sin(x + x**2*y + a, x, 5) == -EX(sin(a)/2)*x**4*y**2 - \ + EX(cos(a)/2)*x**4*y + EX(sin(a)/24)*x**4 - EX(sin(a))*x**3*y - \ + EX(cos(a)/6)*x**3 + EX(cos(a))*x**2*y - EX(sin(a)/2)*x**2 + \ + EX(cos(a))*x + EX(sin(a)) + +def test_cos(): + R, x, y = ring('x, y', QQ) + assert rs_cos(x, x, 9)/x**5 == \ + Rational(1, 40320)*x**3 - Rational(1, 720)*x + Rational(1, 24)*x**(-1) - S.Half*x**(-3) + x**(-5) + assert rs_cos(x*y + x**2*y**3, x, 9) == x**8*y**12/24 - \ + x**8*y**10/48 + x**8*y**8/40320 + x**7*y**10/6 - \ + x**7*y**8/120 + x**6*y**8/4 - x**6*y**6/720 + x**5*y**6/6 - \ + x**4*y**6/2 + x**4*y**4/24 - x**3*y**4 - x**2*y**2/2 + 1 + + # Constant term in series + a = symbols('a') + R, x, y = ring('x, y', QQ[sin(a), cos(a), a]) + assert rs_cos(x + a, x, 5) == cos(a)*x**4/24 + sin(a)*x**3/6 - \ + cos(a)*x**2/2 - sin(a)*x + cos(a) + assert rs_cos(x + x**2*y + a, x, 5) == -cos(a)*x**4*y**2/2 + \ + sin(a)*x**4*y/2 + cos(a)*x**4/24 - cos(a)*x**3*y + sin(a)*x**3/6 - \ + sin(a)*x**2*y - cos(a)*x**2/2 - sin(a)*x + cos(a) + + R, x, y = ring('x, y', EX) + assert rs_cos(x + a, x, 5) == EX(cos(a)/24)*x**4 + EX(sin(a)/6)*x**3 - \ + EX(cos(a)/2)*x**2 - EX(sin(a))*x + EX(cos(a)) + assert rs_cos(x + x**2*y + a, x, 5) == -EX(cos(a)/2)*x**4*y**2 + \ + EX(sin(a)/2)*x**4*y + EX(cos(a)/24)*x**4 - EX(cos(a))*x**3*y + \ + EX(sin(a)/6)*x**3 - EX(sin(a))*x**2*y - EX(cos(a)/2)*x**2 - \ + EX(sin(a))*x + EX(cos(a)) + +def test_cos_sin(): + R, x, y = ring('x, y', QQ) + cos, sin = rs_cos_sin(x, x, 9) + assert cos == rs_cos(x, x, 9) + assert sin == rs_sin(x, x, 9) + cos, sin = rs_cos_sin(x + x*y, x, 5) + assert cos == rs_cos(x + x*y, x, 5) + assert sin == rs_sin(x + x*y, x, 5) + +def test_atanh(): + R, x, y = ring('x, y', QQ) + assert rs_atanh(x, x, 9)/x**5 == Rational(1, 7)*x**2 + Rational(1, 5) + Rational(1, 3)*x**(-2) + x**(-4) + assert rs_atanh(x*y + x**2*y**3, x, 9) == 2*x**8*y**11 + x**8*y**9 + \ + 2*x**7*y**9 + x**7*y**7/7 + x**6*y**9/3 + x**6*y**7 + x**5*y**7 + \ + x**5*y**5/5 + x**4*y**5 + x**3*y**3/3 + x**2*y**3 + x*y + + # Constant term in series + a = symbols('a') + R, x, y = ring('x, y', EX) + assert rs_atanh(x + a, x, 5) == EX((a**3 + a)/(a**8 - 4*a**6 + 6*a**4 - \ + 4*a**2 + 1))*x**4 - EX((3*a**2 + 1)/(3*a**6 - 9*a**4 + \ + 9*a**2 - 3))*x**3 + EX(a/(a**4 - 2*a**2 + 1))*x**2 - EX(1/(a**2 - \ + 1))*x + EX(atanh(a)) + assert rs_atanh(x + x**2*y + a, x, 4) == EX(2*a/(a**4 - 2*a**2 + \ + 1))*x**3*y - EX((3*a**2 + 1)/(3*a**6 - 9*a**4 + 9*a**2 - 3))*x**3 - \ + EX(1/(a**2 - 1))*x**2*y + EX(a/(a**4 - 2*a**2 + 1))*x**2 - \ + EX(1/(a**2 - 1))*x + EX(atanh(a)) + + p = x + x**2 + 5 + assert rs_atanh(p, x, 10).compose(x, 10) == EX(Rational(-733442653682135, 5079158784) \ + + atanh(5)) + +def test_sinh(): + R, x, y = ring('x, y', QQ) + assert rs_sinh(x, x, 9)/x**5 == Rational(1, 5040)*x**2 + Rational(1, 120) + Rational(1, 6)*x**(-2) + x**(-4) + assert rs_sinh(x*y + x**2*y**3, x, 9) == x**8*y**11/12 + \ + x**8*y**9/720 + x**7*y**9/12 + x**7*y**7/5040 + x**6*y**9/6 + \ + x**6*y**7/24 + x**5*y**7/2 + x**5*y**5/120 + x**4*y**5/2 + \ + x**3*y**3/6 + x**2*y**3 + x*y + +def test_cosh(): + R, x, y = ring('x, y', QQ) + assert rs_cosh(x, x, 9)/x**5 == Rational(1, 40320)*x**3 + Rational(1, 720)*x + Rational(1, 24)*x**(-1) + \ + S.Half*x**(-3) + x**(-5) + assert rs_cosh(x*y + x**2*y**3, x, 9) == x**8*y**12/24 + \ + x**8*y**10/48 + x**8*y**8/40320 + x**7*y**10/6 + \ + x**7*y**8/120 + x**6*y**8/4 + x**6*y**6/720 + x**5*y**6/6 + \ + x**4*y**6/2 + x**4*y**4/24 + x**3*y**4 + x**2*y**2/2 + 1 + +def test_tanh(): + R, x, y = ring('x, y', QQ) + assert rs_tanh(x, x, 9)/x**5 == Rational(-17, 315)*x**2 + Rational(2, 15) - Rational(1, 3)*x**(-2) + x**(-4) + assert rs_tanh(x*y + x**2*y**3, x, 9) == 4*x**8*y**11/3 - \ + 17*x**8*y**9/45 + 4*x**7*y**9/3 - 17*x**7*y**7/315 - x**6*y**9/3 + \ + 2*x**6*y**7/3 - x**5*y**7 + 2*x**5*y**5/15 - x**4*y**5 - \ + x**3*y**3/3 + x**2*y**3 + x*y + + # Constant term in series + a = symbols('a') + R, x, y = ring('x, y', EX) + assert rs_tanh(x + a, x, 5) == EX(tanh(a)**5 - 5*tanh(a)**3/3 + + 2*tanh(a)/3)*x**4 + EX(-tanh(a)**4 + 4*tanh(a)**2/3 - QQ(1, 3))*x**3 + \ + EX(tanh(a)**3 - tanh(a))*x**2 + EX(-tanh(a)**2 + 1)*x + EX(tanh(a)) + + p = rs_tanh(x + x**2*y + a, x, 4) + assert (p.compose(x, 10)).compose(y, 5) == EX(-1000*tanh(a)**4 + \ + 10100*tanh(a)**3 + 2470*tanh(a)**2/3 - 10099*tanh(a) + QQ(530, 3)) + +def test_RR(): + rs_funcs = [rs_sin, rs_cos, rs_tan, rs_cot, rs_atan, rs_tanh] + sympy_funcs = [sin, cos, tan, cot, atan, tanh] + R, x, y = ring('x, y', RR) + a = symbols('a') + for rs_func, sympy_func in zip(rs_funcs, sympy_funcs): + p = rs_func(2 + x, x, 5).compose(x, 5) + q = sympy_func(2 + a).series(a, 0, 5).removeO() + is_close(p.as_expr(), q.subs(a, 5).n()) + + p = rs_nth_root(2 + x, 5, x, 5).compose(x, 5) + q = ((2 + a)**QQ(1, 5)).series(a, 0, 5).removeO() + is_close(p.as_expr(), q.subs(a, 5).n()) + +def test_is_regular(): + R, x, y = ring('x, y', QQ) + p = 1 + 2*x + x**2 + 3*x**3 + assert not rs_is_puiseux(p, x) + + p = x + x**QQ(1,5)*y + assert rs_is_puiseux(p, x) + assert not rs_is_puiseux(p, y) + + p = x + x**2*y**QQ(1,5)*y + assert not rs_is_puiseux(p, x) + +def test_puiseux(): + R, x, y = ring('x, y', QQ) + p = x**QQ(2,5) + x**QQ(2,3) + x + + r = rs_series_inversion(p, x, 1) + r1 = -x**QQ(14,15) + x**QQ(4,5) - 3*x**QQ(11,15) + x**QQ(2,3) + \ + 2*x**QQ(7,15) - x**QQ(2,5) - x**QQ(1,5) + x**QQ(2,15) - x**QQ(-2,15) \ + + x**QQ(-2,5) + assert r == r1 + + r = rs_nth_root(1 + p, 3, x, 1) + assert r == -x**QQ(4,5)/9 + x**QQ(2,3)/3 + x**QQ(2,5)/3 + 1 + + r = rs_log(1 + p, x, 1) + assert r == -x**QQ(4,5)/2 + x**QQ(2,3) + x**QQ(2,5) + + r = rs_LambertW(p, x, 1) + assert r == -x**QQ(4,5) + x**QQ(2,3) + x**QQ(2,5) + + p1 = x + x**QQ(1,5)*y + r = rs_exp(p1, x, 1) + assert r == x**QQ(4,5)*y**4/24 + x**QQ(3,5)*y**3/6 + x**QQ(2,5)*y**2/2 + \ + x**QQ(1,5)*y + 1 + + r = rs_atan(p, x, 2) + assert r == -x**QQ(9,5) - x**QQ(26,15) - x**QQ(22,15) - x**QQ(6,5)/3 + \ + x + x**QQ(2,3) + x**QQ(2,5) + + r = rs_atan(p1, x, 2) + assert r == x**QQ(9,5)*y**9/9 + x**QQ(9,5)*y**4 - x**QQ(7,5)*y**7/7 - \ + x**QQ(7,5)*y**2 + x*y**5/5 + x - x**QQ(3,5)*y**3/3 + x**QQ(1,5)*y + + r = rs_asin(p, x, 2) + assert r == x**QQ(9,5)/2 + x**QQ(26,15)/2 + x**QQ(22,15)/2 + \ + x**QQ(6,5)/6 + x + x**QQ(2,3) + x**QQ(2,5) + + r = rs_cot(p, x, 1) + assert r == -x**QQ(14,15) + x**QQ(4,5) - 3*x**QQ(11,15) + \ + 2*x**QQ(2,3)/3 + 2*x**QQ(7,15) - 4*x**QQ(2,5)/3 - x**QQ(1,5) + \ + x**QQ(2,15) - x**QQ(-2,15) + x**QQ(-2,5) + + r = rs_cos_sin(p, x, 2) + assert r[0] == x**QQ(28,15)/6 - x**QQ(5,3) + x**QQ(8,5)/24 - x**QQ(7,5) - \ + x**QQ(4,3)/2 - x**QQ(16,15) - x**QQ(4,5)/2 + 1 + assert r[1] == -x**QQ(9,5)/2 - x**QQ(26,15)/2 - x**QQ(22,15)/2 - \ + x**QQ(6,5)/6 + x + x**QQ(2,3) + x**QQ(2,5) + + r = rs_atanh(p, x, 2) + assert r == x**QQ(9,5) + x**QQ(26,15) + x**QQ(22,15) + x**QQ(6,5)/3 + x + \ + x**QQ(2,3) + x**QQ(2,5) + + r = rs_sinh(p, x, 2) + assert r == x**QQ(9,5)/2 + x**QQ(26,15)/2 + x**QQ(22,15)/2 + \ + x**QQ(6,5)/6 + x + x**QQ(2,3) + x**QQ(2,5) + + r = rs_cosh(p, x, 2) + assert r == x**QQ(28,15)/6 + x**QQ(5,3) + x**QQ(8,5)/24 + x**QQ(7,5) + \ + x**QQ(4,3)/2 + x**QQ(16,15) + x**QQ(4,5)/2 + 1 + + r = rs_tanh(p, x, 2) + assert r == -x**QQ(9,5) - x**QQ(26,15) - x**QQ(22,15) - x**QQ(6,5)/3 + \ + x + x**QQ(2,3) + x**QQ(2,5) + +def test_puiseux_algebraic(): # https://github.com/sympy/sympy/issues/24395 + + K = QQ.algebraic_field(sqrt(2)) + sqrt2 = K.from_sympy(sqrt(2)) + x, y = symbols('x, y') + R, xr, yr = ring([x, y], K) + p = (1+sqrt2)*xr**QQ(1,2) + (1-sqrt2)*yr**QQ(2,3) + + assert dict(p) == {(QQ(1,2),QQ(0)):1+sqrt2, (QQ(0),QQ(2,3)):1-sqrt2} + assert p.as_expr() == (1 + sqrt(2))*x**(S(1)/2) + (1 - sqrt(2))*y**(S(2)/3) + + +def test1(): + R, x = ring('x', QQ) + r = rs_sin(x, x, 15)*x**(-5) + assert r == x**8/6227020800 - x**6/39916800 + x**4/362880 - x**2/5040 + \ + QQ(1,120) - x**-2/6 + x**-4 + + p = rs_sin(x, x, 10) + r = rs_nth_root(p, 2, x, 10) + assert r == -67*x**QQ(17,2)/29030400 - x**QQ(13,2)/24192 + \ + x**QQ(9,2)/1440 - x**QQ(5,2)/12 + x**QQ(1,2) + + p = rs_sin(x, x, 10) + r = rs_nth_root(p, 7, x, 10) + r = rs_pow(r, 5, x, 10) + assert r == -97*x**QQ(61,7)/124467840 - x**QQ(47,7)/16464 + \ + 11*x**QQ(33,7)/3528 - 5*x**QQ(19,7)/42 + x**QQ(5,7) + + r = rs_exp(x**QQ(1,2), x, 10) + assert r == x**QQ(19,2)/121645100408832000 + x**9/6402373705728000 + \ + x**QQ(17,2)/355687428096000 + x**8/20922789888000 + \ + x**QQ(15,2)/1307674368000 + x**7/87178291200 + \ + x**QQ(13,2)/6227020800 + x**6/479001600 + x**QQ(11,2)/39916800 + \ + x**5/3628800 + x**QQ(9,2)/362880 + x**4/40320 + x**QQ(7,2)/5040 + \ + x**3/720 + x**QQ(5,2)/120 + x**2/24 + x**QQ(3,2)/6 + x/2 + \ + x**QQ(1,2) + 1 + +def test_puiseux2(): + R, y = ring('y', QQ) + S, x = ring('x', R) + + p = x + x**QQ(1,5)*y + r = rs_atan(p, x, 3) + assert r == (y**13/13 + y**8 + 2*y**3)*x**QQ(13,5) - (y**11/11 + y**6 + + y)*x**QQ(11,5) + (y**9/9 + y**4)*x**QQ(9,5) - (y**7/7 + + y**2)*x**QQ(7,5) + (y**5/5 + 1)*x - y**3*x**QQ(3,5)/3 + y*x**QQ(1,5) + + +@slow +def test_rs_series(): + x, a, b, c = symbols('x, a, b, c') + + assert rs_series(a, a, 5).as_expr() == a + assert rs_series(sin(a), a, 5).as_expr() == (sin(a).series(a, 0, + 5)).removeO() + assert rs_series(sin(a) + cos(a), a, 5).as_expr() == ((sin(a) + + cos(a)).series(a, 0, 5)).removeO() + assert rs_series(sin(a)*cos(a), a, 5).as_expr() == ((sin(a)* + cos(a)).series(a, 0, 5)).removeO() + + p = (sin(a) - a)*(cos(a**2) + a**4/2) + assert expand(rs_series(p, a, 10).as_expr()) == expand(p.series(a, 0, + 10).removeO()) + + p = sin(a**2/2 + a/3) + cos(a/5)*sin(a/2)**3 + assert expand(rs_series(p, a, 5).as_expr()) == expand(p.series(a, 0, + 5).removeO()) + + p = sin(x**2 + a)*(cos(x**3 - 1) - a - a**2) + assert expand(rs_series(p, a, 5).as_expr()) == expand(p.series(a, 0, + 5).removeO()) + + p = sin(a**2 - a/3 + 2)**5*exp(a**3 - a/2) + assert expand(rs_series(p, a, 10).as_expr()) == expand(p.series(a, 0, + 10).removeO()) + + p = sin(a + b + c) + assert expand(rs_series(p, a, 5).as_expr()) == expand(p.series(a, 0, + 5).removeO()) + + p = tan(sin(a**2 + 4) + b + c) + assert expand(rs_series(p, a, 6).as_expr()) == expand(p.series(a, 0, + 6).removeO()) + + p = a**QQ(2,5) + a**QQ(2,3) + a + + r = rs_series(tan(p), a, 2) + assert r.as_expr() == a**QQ(9,5) + a**QQ(26,15) + a**QQ(22,15) + a**QQ(6,5)/3 + \ + a + a**QQ(2,3) + a**QQ(2,5) + + r = rs_series(exp(p), a, 1) + assert r.as_expr() == a**QQ(4,5)/2 + a**QQ(2,3) + a**QQ(2,5) + 1 + + r = rs_series(sin(p), a, 2) + assert r.as_expr() == -a**QQ(9,5)/2 - a**QQ(26,15)/2 - a**QQ(22,15)/2 - \ + a**QQ(6,5)/6 + a + a**QQ(2,3) + a**QQ(2,5) + + r = rs_series(cos(p), a, 2) + assert r.as_expr() == a**QQ(28,15)/6 - a**QQ(5,3) + a**QQ(8,5)/24 - a**QQ(7,5) - \ + a**QQ(4,3)/2 - a**QQ(16,15) - a**QQ(4,5)/2 + 1 + + assert rs_series(sin(a)/7, a, 5).as_expr() == (sin(a)/7).series(a, 0, + 5).removeO() + + assert rs_series(log(1 + x), x, 5).as_expr() == -x**4/4 + x**3/3 - \ + x**2/2 + x + assert rs_series(log(1 + 4*x), x, 5).as_expr() == -64*x**4 + 64*x**3/3 - \ + 8*x**2 + 4*x + assert rs_series(log(1 + x + x**2), x, 10).as_expr() == -2*x**9/9 + \ + x**8/8 + x**7/7 - x**6/3 + x**5/5 + x**4/4 - 2*x**3/3 + \ + x**2/2 + x + assert rs_series(log(1 + x*a**2), x, 7).as_expr() == -x**6*a**12/6 + \ + x**5*a**10/5 - x**4*a**8/4 + x**3*a**6/3 - \ + x**2*a**4/2 + x*a**2 diff --git a/lib/python3.12/site-packages/sympy/polys/tests/test_rings.py b/lib/python3.12/site-packages/sympy/polys/tests/test_rings.py new file mode 100644 index 0000000000000000000000000000000000000000..3a48d45a6f1576355a53a4415e4fe36b8ce80254 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/polys/tests/test_rings.py @@ -0,0 +1,1578 @@ +"""Test sparse polynomials. """ + +from functools import reduce +from operator import add, mul + +from sympy.polys.rings import ring, xring, sring, PolyRing, PolyElement +from sympy.polys.fields import field, FracField +from sympy.polys.densebasic import ninf +from sympy.polys.domains import ZZ, QQ, RR, FF, EX +from sympy.polys.orderings import lex, grlex +from sympy.polys.polyerrors import GeneratorsError, \ + ExactQuotientFailed, MultivariatePolynomialError, CoercionFailed + +from sympy.testing.pytest import raises +from sympy.core import Symbol, symbols +from sympy.core.singleton import S +from sympy.core.numbers import pi +from sympy.functions.elementary.exponential import exp +from sympy.functions.elementary.miscellaneous import sqrt + +def test_PolyRing___init__(): + x, y, z, t = map(Symbol, "xyzt") + + assert len(PolyRing("x,y,z", ZZ, lex).gens) == 3 + assert len(PolyRing(x, ZZ, lex).gens) == 1 + assert len(PolyRing(("x", "y", "z"), ZZ, lex).gens) == 3 + assert len(PolyRing((x, y, z), ZZ, lex).gens) == 3 + assert len(PolyRing("", ZZ, lex).gens) == 0 + assert len(PolyRing([], ZZ, lex).gens) == 0 + + raises(GeneratorsError, lambda: PolyRing(0, ZZ, lex)) + + assert PolyRing("x", ZZ[t], lex).domain == ZZ[t] + assert PolyRing("x", 'ZZ[t]', lex).domain == ZZ[t] + assert PolyRing("x", PolyRing("t", ZZ, lex), lex).domain == ZZ[t] + + raises(GeneratorsError, lambda: PolyRing("x", PolyRing("x", ZZ, lex), lex)) + + _lex = Symbol("lex") + assert PolyRing("x", ZZ, lex).order == lex + assert PolyRing("x", ZZ, _lex).order == lex + assert PolyRing("x", ZZ, 'lex').order == lex + + R1 = PolyRing("x,y", ZZ, lex) + R2 = PolyRing("x,y", ZZ, lex) + R3 = PolyRing("x,y,z", ZZ, lex) + + assert R1.x == R1.gens[0] + assert R1.y == R1.gens[1] + assert R1.x == R2.x + assert R1.y == R2.y + assert R1.x != R3.x + assert R1.y != R3.y + +def test_PolyRing___hash__(): + R, x, y, z = ring("x,y,z", QQ) + assert hash(R) + +def test_PolyRing___eq__(): + assert ring("x,y,z", QQ)[0] == ring("x,y,z", QQ)[0] + assert ring("x,y,z", QQ)[0] is ring("x,y,z", QQ)[0] + + assert ring("x,y,z", QQ)[0] != ring("x,y,z", ZZ)[0] + assert ring("x,y,z", QQ)[0] is not ring("x,y,z", ZZ)[0] + + assert ring("x,y,z", ZZ)[0] != ring("x,y,z", QQ)[0] + assert ring("x,y,z", ZZ)[0] is not ring("x,y,z", QQ)[0] + + assert ring("x,y,z", QQ)[0] != ring("x,y", QQ)[0] + assert ring("x,y,z", QQ)[0] is not ring("x,y", QQ)[0] + + assert ring("x,y", QQ)[0] != ring("x,y,z", QQ)[0] + assert ring("x,y", QQ)[0] is not ring("x,y,z", QQ)[0] + +def test_PolyRing_ring_new(): + R, x, y, z = ring("x,y,z", QQ) + + assert R.ring_new(7) == R(7) + assert R.ring_new(7*x*y*z) == 7*x*y*z + + f = x**2 + 2*x*y + 3*x + 4*z**2 + 5*z + 6 + + assert R.ring_new([[[1]], [[2], [3]], [[4, 5, 6]]]) == f + assert R.ring_new({(2, 0, 0): 1, (1, 1, 0): 2, (1, 0, 0): 3, (0, 0, 2): 4, (0, 0, 1): 5, (0, 0, 0): 6}) == f + assert R.ring_new([((2, 0, 0), 1), ((1, 1, 0), 2), ((1, 0, 0), 3), ((0, 0, 2), 4), ((0, 0, 1), 5), ((0, 0, 0), 6)]) == f + + R, = ring("", QQ) + assert R.ring_new([((), 7)]) == R(7) + +def test_PolyRing_drop(): + R, x,y,z = ring("x,y,z", ZZ) + + assert R.drop(x) == PolyRing("y,z", ZZ, lex) + assert R.drop(y) == PolyRing("x,z", ZZ, lex) + assert R.drop(z) == PolyRing("x,y", ZZ, lex) + + assert R.drop(0) == PolyRing("y,z", ZZ, lex) + assert R.drop(0).drop(0) == PolyRing("z", ZZ, lex) + assert R.drop(0).drop(0).drop(0) == ZZ + + assert R.drop(1) == PolyRing("x,z", ZZ, lex) + + assert R.drop(2) == PolyRing("x,y", ZZ, lex) + assert R.drop(2).drop(1) == PolyRing("x", ZZ, lex) + assert R.drop(2).drop(1).drop(0) == ZZ + + raises(ValueError, lambda: R.drop(3)) + raises(ValueError, lambda: R.drop(x).drop(y)) + +def test_PolyRing___getitem__(): + R, x,y,z = ring("x,y,z", ZZ) + + assert R[0:] == PolyRing("x,y,z", ZZ, lex) + assert R[1:] == PolyRing("y,z", ZZ, lex) + assert R[2:] == PolyRing("z", ZZ, lex) + assert R[3:] == ZZ + +def test_PolyRing_is_(): + R = PolyRing("x", QQ, lex) + + assert R.is_univariate is True + assert R.is_multivariate is False + + R = PolyRing("x,y,z", QQ, lex) + + assert R.is_univariate is False + assert R.is_multivariate is True + + R = PolyRing("", QQ, lex) + + assert R.is_univariate is False + assert R.is_multivariate is False + +def test_PolyRing_add(): + R, x = ring("x", ZZ) + F = [ x**2 + 2*i + 3 for i in range(4) ] + + assert R.add(F) == reduce(add, F) == 4*x**2 + 24 + + R, = ring("", ZZ) + + assert R.add([2, 5, 7]) == 14 + +def test_PolyRing_mul(): + R, x = ring("x", ZZ) + F = [ x**2 + 2*i + 3 for i in range(4) ] + + assert R.mul(F) == reduce(mul, F) == x**8 + 24*x**6 + 206*x**4 + 744*x**2 + 945 + + R, = ring("", ZZ) + + assert R.mul([2, 3, 5]) == 30 + +def test_PolyRing_symmetric_poly(): + R, x, y, z, t = ring("x,y,z,t", ZZ) + + raises(ValueError, lambda: R.symmetric_poly(-1)) + raises(ValueError, lambda: R.symmetric_poly(5)) + + assert R.symmetric_poly(0) == R.one + assert R.symmetric_poly(1) == x + y + z + t + assert R.symmetric_poly(2) == x*y + x*z + x*t + y*z + y*t + z*t + assert R.symmetric_poly(3) == x*y*z + x*y*t + x*z*t + y*z*t + assert R.symmetric_poly(4) == x*y*z*t + +def test_sring(): + x, y, z, t = symbols("x,y,z,t") + + R = PolyRing("x,y,z", ZZ, lex) + assert sring(x + 2*y + 3*z) == (R, R.x + 2*R.y + 3*R.z) + + R = PolyRing("x,y,z", QQ, lex) + assert sring(x + 2*y + z/3) == (R, R.x + 2*R.y + R.z/3) + assert sring([x, 2*y, z/3]) == (R, [R.x, 2*R.y, R.z/3]) + + Rt = PolyRing("t", ZZ, lex) + R = PolyRing("x,y,z", Rt, lex) + assert sring(x + 2*t*y + 3*t**2*z, x, y, z) == (R, R.x + 2*Rt.t*R.y + 3*Rt.t**2*R.z) + + Rt = PolyRing("t", QQ, lex) + R = PolyRing("x,y,z", Rt, lex) + assert sring(x + t*y/2 + t**2*z/3, x, y, z) == (R, R.x + Rt.t*R.y/2 + Rt.t**2*R.z/3) + + Rt = FracField("t", ZZ, lex) + R = PolyRing("x,y,z", Rt, lex) + assert sring(x + 2*y/t + t**2*z/3, x, y, z) == (R, R.x + 2*R.y/Rt.t + Rt.t**2*R.z/3) + + r = sqrt(2) - sqrt(3) + R, a = sring(r, extension=True) + assert R.domain == QQ.algebraic_field(sqrt(2) + sqrt(3)) + assert R.gens == () + assert a == R.domain.from_sympy(r) + +def test_PolyElement___hash__(): + R, x, y, z = ring("x,y,z", QQ) + assert hash(x*y*z) + +def test_PolyElement___eq__(): + R, x, y = ring("x,y", ZZ, lex) + + assert ((x*y + 5*x*y) == 6) == False + assert ((x*y + 5*x*y) == 6*x*y) == True + assert (6 == (x*y + 5*x*y)) == False + assert (6*x*y == (x*y + 5*x*y)) == True + + assert ((x*y - x*y) == 0) == True + assert (0 == (x*y - x*y)) == True + + assert ((x*y - x*y) == 1) == False + assert (1 == (x*y - x*y)) == False + + assert ((x*y - x*y) == 1) == False + assert (1 == (x*y - x*y)) == False + + assert ((x*y + 5*x*y) != 6) == True + assert ((x*y + 5*x*y) != 6*x*y) == False + assert (6 != (x*y + 5*x*y)) == True + assert (6*x*y != (x*y + 5*x*y)) == False + + assert ((x*y - x*y) != 0) == False + assert (0 != (x*y - x*y)) == False + + assert ((x*y - x*y) != 1) == True + assert (1 != (x*y - x*y)) == True + + assert R.one == QQ(1, 1) == R.one + assert R.one == 1 == R.one + + Rt, t = ring("t", ZZ) + R, x, y = ring("x,y", Rt) + + assert (t**3*x/x == t**3) == True + assert (t**3*x/x == t**4) == False + +def test_PolyElement__lt_le_gt_ge__(): + R, x, y = ring("x,y", ZZ) + + assert R(1) < x < x**2 < x**3 + assert R(1) <= x <= x**2 <= x**3 + + assert x**3 > x**2 > x > R(1) + assert x**3 >= x**2 >= x >= R(1) + +def test_PolyElement__str__(): + x, y = symbols('x, y') + + for dom in [ZZ, QQ, ZZ[x], ZZ[x,y], ZZ[x][y]]: + R, t = ring('t', dom) + assert str(2*t**2 + 1) == '2*t**2 + 1' + + for dom in [EX, EX[x]]: + R, t = ring('t', dom) + assert str(2*t**2 + 1) == 'EX(2)*t**2 + EX(1)' + +def test_PolyElement_copy(): + R, x, y, z = ring("x,y,z", ZZ) + + f = x*y + 3*z + g = f.copy() + + assert f == g + g[(1, 1, 1)] = 7 + assert f != g + +def test_PolyElement_as_expr(): + R, x, y, z = ring("x,y,z", ZZ) + f = 3*x**2*y - x*y*z + 7*z**3 + 1 + + X, Y, Z = R.symbols + g = 3*X**2*Y - X*Y*Z + 7*Z**3 + 1 + + assert f != g + assert f.as_expr() == g + + U, V, W = symbols("u,v,w") + g = 3*U**2*V - U*V*W + 7*W**3 + 1 + + assert f != g + assert f.as_expr(U, V, W) == g + + raises(ValueError, lambda: f.as_expr(X)) + + R, = ring("", ZZ) + assert R(3).as_expr() == 3 + +def test_PolyElement_from_expr(): + x, y, z = symbols("x,y,z") + R, X, Y, Z = ring((x, y, z), ZZ) + + f = R.from_expr(1) + assert f == 1 and isinstance(f, R.dtype) + + f = R.from_expr(x) + assert f == X and isinstance(f, R.dtype) + + f = R.from_expr(x*y*z) + assert f == X*Y*Z and isinstance(f, R.dtype) + + f = R.from_expr(x*y*z + x*y + x) + assert f == X*Y*Z + X*Y + X and isinstance(f, R.dtype) + + f = R.from_expr(x**3*y*z + x**2*y**7 + 1) + assert f == X**3*Y*Z + X**2*Y**7 + 1 and isinstance(f, R.dtype) + + r, F = sring([exp(2)]) + f = r.from_expr(exp(2)) + assert f == F[0] and isinstance(f, r.dtype) + + raises(ValueError, lambda: R.from_expr(1/x)) + raises(ValueError, lambda: R.from_expr(2**x)) + raises(ValueError, lambda: R.from_expr(7*x + sqrt(2))) + + R, = ring("", ZZ) + f = R.from_expr(1) + assert f == 1 and isinstance(f, R.dtype) + +def test_PolyElement_degree(): + R, x,y,z = ring("x,y,z", ZZ) + + assert ninf == float('-inf') + + assert R(0).degree() is ninf + assert R(1).degree() == 0 + assert (x + 1).degree() == 1 + assert (2*y**3 + z).degree() == 0 + assert (x*y**3 + z).degree() == 1 + assert (x**5*y**3 + z).degree() == 5 + + assert R(0).degree(x) is ninf + assert R(1).degree(x) == 0 + assert (x + 1).degree(x) == 1 + assert (2*y**3 + z).degree(x) == 0 + assert (x*y**3 + z).degree(x) == 1 + assert (7*x**5*y**3 + z).degree(x) == 5 + + assert R(0).degree(y) is ninf + assert R(1).degree(y) == 0 + assert (x + 1).degree(y) == 0 + assert (2*y**3 + z).degree(y) == 3 + assert (x*y**3 + z).degree(y) == 3 + assert (7*x**5*y**3 + z).degree(y) == 3 + + assert R(0).degree(z) is ninf + assert R(1).degree(z) == 0 + assert (x + 1).degree(z) == 0 + assert (2*y**3 + z).degree(z) == 1 + assert (x*y**3 + z).degree(z) == 1 + assert (7*x**5*y**3 + z).degree(z) == 1 + + R, = ring("", ZZ) + assert R(0).degree() is ninf + assert R(1).degree() == 0 + +def test_PolyElement_tail_degree(): + R, x,y,z = ring("x,y,z", ZZ) + + assert R(0).tail_degree() is ninf + assert R(1).tail_degree() == 0 + assert (x + 1).tail_degree() == 0 + assert (2*y**3 + x**3*z).tail_degree() == 0 + assert (x*y**3 + x**3*z).tail_degree() == 1 + assert (x**5*y**3 + x**3*z).tail_degree() == 3 + + assert R(0).tail_degree(x) is ninf + assert R(1).tail_degree(x) == 0 + assert (x + 1).tail_degree(x) == 0 + assert (2*y**3 + x**3*z).tail_degree(x) == 0 + assert (x*y**3 + x**3*z).tail_degree(x) == 1 + assert (7*x**5*y**3 + x**3*z).tail_degree(x) == 3 + + assert R(0).tail_degree(y) is ninf + assert R(1).tail_degree(y) == 0 + assert (x + 1).tail_degree(y) == 0 + assert (2*y**3 + x**3*z).tail_degree(y) == 0 + assert (x*y**3 + x**3*z).tail_degree(y) == 0 + assert (7*x**5*y**3 + x**3*z).tail_degree(y) == 0 + + assert R(0).tail_degree(z) is ninf + assert R(1).tail_degree(z) == 0 + assert (x + 1).tail_degree(z) == 0 + assert (2*y**3 + x**3*z).tail_degree(z) == 0 + assert (x*y**3 + x**3*z).tail_degree(z) == 0 + assert (7*x**5*y**3 + x**3*z).tail_degree(z) == 0 + + R, = ring("", ZZ) + assert R(0).tail_degree() is ninf + assert R(1).tail_degree() == 0 + +def test_PolyElement_degrees(): + R, x,y,z = ring("x,y,z", ZZ) + + assert R(0).degrees() == (ninf, ninf, ninf) + assert R(1).degrees() == (0, 0, 0) + assert (x**2*y + x**3*z**2).degrees() == (3, 1, 2) + +def test_PolyElement_tail_degrees(): + R, x,y,z = ring("x,y,z", ZZ) + + assert R(0).tail_degrees() == (ninf, ninf, ninf) + assert R(1).tail_degrees() == (0, 0, 0) + assert (x**2*y + x**3*z**2).tail_degrees() == (2, 0, 0) + +def test_PolyElement_coeff(): + R, x, y, z = ring("x,y,z", ZZ, lex) + f = 3*x**2*y - x*y*z + 7*z**3 + 23 + + assert f.coeff(1) == 23 + raises(ValueError, lambda: f.coeff(3)) + + assert f.coeff(x) == 0 + assert f.coeff(y) == 0 + assert f.coeff(z) == 0 + + assert f.coeff(x**2*y) == 3 + assert f.coeff(x*y*z) == -1 + assert f.coeff(z**3) == 7 + + raises(ValueError, lambda: f.coeff(3*x**2*y)) + raises(ValueError, lambda: f.coeff(-x*y*z)) + raises(ValueError, lambda: f.coeff(7*z**3)) + + R, = ring("", ZZ) + assert R(3).coeff(1) == 3 + +def test_PolyElement_LC(): + R, x, y = ring("x,y", QQ, lex) + assert R(0).LC == QQ(0) + assert (QQ(1,2)*x).LC == QQ(1, 2) + assert (QQ(1,4)*x*y + QQ(1,2)*x).LC == QQ(1, 4) + +def test_PolyElement_LM(): + R, x, y = ring("x,y", QQ, lex) + assert R(0).LM == (0, 0) + assert (QQ(1,2)*x).LM == (1, 0) + assert (QQ(1,4)*x*y + QQ(1,2)*x).LM == (1, 1) + +def test_PolyElement_LT(): + R, x, y = ring("x,y", QQ, lex) + assert R(0).LT == ((0, 0), QQ(0)) + assert (QQ(1,2)*x).LT == ((1, 0), QQ(1, 2)) + assert (QQ(1,4)*x*y + QQ(1,2)*x).LT == ((1, 1), QQ(1, 4)) + + R, = ring("", ZZ) + assert R(0).LT == ((), 0) + assert R(1).LT == ((), 1) + +def test_PolyElement_leading_monom(): + R, x, y = ring("x,y", QQ, lex) + assert R(0).leading_monom() == 0 + assert (QQ(1,2)*x).leading_monom() == x + assert (QQ(1,4)*x*y + QQ(1,2)*x).leading_monom() == x*y + +def test_PolyElement_leading_term(): + R, x, y = ring("x,y", QQ, lex) + assert R(0).leading_term() == 0 + assert (QQ(1,2)*x).leading_term() == QQ(1,2)*x + assert (QQ(1,4)*x*y + QQ(1,2)*x).leading_term() == QQ(1,4)*x*y + +def test_PolyElement_terms(): + R, x,y,z = ring("x,y,z", QQ) + terms = (x**2/3 + y**3/4 + z**4/5).terms() + assert terms == [((2,0,0), QQ(1,3)), ((0,3,0), QQ(1,4)), ((0,0,4), QQ(1,5))] + + R, x,y = ring("x,y", ZZ, lex) + f = x*y**7 + 2*x**2*y**3 + + assert f.terms() == f.terms(lex) == f.terms('lex') == [((2, 3), 2), ((1, 7), 1)] + assert f.terms(grlex) == f.terms('grlex') == [((1, 7), 1), ((2, 3), 2)] + + R, x,y = ring("x,y", ZZ, grlex) + f = x*y**7 + 2*x**2*y**3 + + assert f.terms() == f.terms(grlex) == f.terms('grlex') == [((1, 7), 1), ((2, 3), 2)] + assert f.terms(lex) == f.terms('lex') == [((2, 3), 2), ((1, 7), 1)] + + R, = ring("", ZZ) + assert R(3).terms() == [((), 3)] + +def test_PolyElement_monoms(): + R, x,y,z = ring("x,y,z", QQ) + monoms = (x**2/3 + y**3/4 + z**4/5).monoms() + assert monoms == [(2,0,0), (0,3,0), (0,0,4)] + + R, x,y = ring("x,y", ZZ, lex) + f = x*y**7 + 2*x**2*y**3 + + assert f.monoms() == f.monoms(lex) == f.monoms('lex') == [(2, 3), (1, 7)] + assert f.monoms(grlex) == f.monoms('grlex') == [(1, 7), (2, 3)] + + R, x,y = ring("x,y", ZZ, grlex) + f = x*y**7 + 2*x**2*y**3 + + assert f.monoms() == f.monoms(grlex) == f.monoms('grlex') == [(1, 7), (2, 3)] + assert f.monoms(lex) == f.monoms('lex') == [(2, 3), (1, 7)] + +def test_PolyElement_coeffs(): + R, x,y,z = ring("x,y,z", QQ) + coeffs = (x**2/3 + y**3/4 + z**4/5).coeffs() + assert coeffs == [QQ(1,3), QQ(1,4), QQ(1,5)] + + R, x,y = ring("x,y", ZZ, lex) + f = x*y**7 + 2*x**2*y**3 + + assert f.coeffs() == f.coeffs(lex) == f.coeffs('lex') == [2, 1] + assert f.coeffs(grlex) == f.coeffs('grlex') == [1, 2] + + R, x,y = ring("x,y", ZZ, grlex) + f = x*y**7 + 2*x**2*y**3 + + assert f.coeffs() == f.coeffs(grlex) == f.coeffs('grlex') == [1, 2] + assert f.coeffs(lex) == f.coeffs('lex') == [2, 1] + +def test_PolyElement___add__(): + Rt, t = ring("t", ZZ) + Ruv, u,v = ring("u,v", ZZ) + Rxyz, x,y,z = ring("x,y,z", Ruv) + + assert dict(x + 3*y) == {(1, 0, 0): 1, (0, 1, 0): 3} + + assert dict(u + x) == dict(x + u) == {(1, 0, 0): 1, (0, 0, 0): u} + assert dict(u + x*y) == dict(x*y + u) == {(1, 1, 0): 1, (0, 0, 0): u} + assert dict(u + x*y + z) == dict(x*y + z + u) == {(1, 1, 0): 1, (0, 0, 1): 1, (0, 0, 0): u} + + assert dict(u*x + x) == dict(x + u*x) == {(1, 0, 0): u + 1} + assert dict(u*x + x*y) == dict(x*y + u*x) == {(1, 1, 0): 1, (1, 0, 0): u} + assert dict(u*x + x*y + z) == dict(x*y + z + u*x) == {(1, 1, 0): 1, (0, 0, 1): 1, (1, 0, 0): u} + + raises(TypeError, lambda: t + x) + raises(TypeError, lambda: x + t) + raises(TypeError, lambda: t + u) + raises(TypeError, lambda: u + t) + + Fuv, u,v = field("u,v", ZZ) + Rxyz, x,y,z = ring("x,y,z", Fuv) + + assert dict(u + x) == dict(x + u) == {(1, 0, 0): 1, (0, 0, 0): u} + + Rxyz, x,y,z = ring("x,y,z", EX) + + assert dict(EX(pi) + x*y*z) == dict(x*y*z + EX(pi)) == {(1, 1, 1): EX(1), (0, 0, 0): EX(pi)} + +def test_PolyElement___sub__(): + Rt, t = ring("t", ZZ) + Ruv, u,v = ring("u,v", ZZ) + Rxyz, x,y,z = ring("x,y,z", Ruv) + + assert dict(x - 3*y) == {(1, 0, 0): 1, (0, 1, 0): -3} + + assert dict(-u + x) == dict(x - u) == {(1, 0, 0): 1, (0, 0, 0): -u} + assert dict(-u + x*y) == dict(x*y - u) == {(1, 1, 0): 1, (0, 0, 0): -u} + assert dict(-u + x*y + z) == dict(x*y + z - u) == {(1, 1, 0): 1, (0, 0, 1): 1, (0, 0, 0): -u} + + assert dict(-u*x + x) == dict(x - u*x) == {(1, 0, 0): -u + 1} + assert dict(-u*x + x*y) == dict(x*y - u*x) == {(1, 1, 0): 1, (1, 0, 0): -u} + assert dict(-u*x + x*y + z) == dict(x*y + z - u*x) == {(1, 1, 0): 1, (0, 0, 1): 1, (1, 0, 0): -u} + + raises(TypeError, lambda: t - x) + raises(TypeError, lambda: x - t) + raises(TypeError, lambda: t - u) + raises(TypeError, lambda: u - t) + + Fuv, u,v = field("u,v", ZZ) + Rxyz, x,y,z = ring("x,y,z", Fuv) + + assert dict(-u + x) == dict(x - u) == {(1, 0, 0): 1, (0, 0, 0): -u} + + Rxyz, x,y,z = ring("x,y,z", EX) + + assert dict(-EX(pi) + x*y*z) == dict(x*y*z - EX(pi)) == {(1, 1, 1): EX(1), (0, 0, 0): -EX(pi)} + +def test_PolyElement___mul__(): + Rt, t = ring("t", ZZ) + Ruv, u,v = ring("u,v", ZZ) + Rxyz, x,y,z = ring("x,y,z", Ruv) + + assert dict(u*x) == dict(x*u) == {(1, 0, 0): u} + + assert dict(2*u*x + z) == dict(x*2*u + z) == {(1, 0, 0): 2*u, (0, 0, 1): 1} + assert dict(u*2*x + z) == dict(2*x*u + z) == {(1, 0, 0): 2*u, (0, 0, 1): 1} + assert dict(2*u*x + z) == dict(x*2*u + z) == {(1, 0, 0): 2*u, (0, 0, 1): 1} + assert dict(u*x*2 + z) == dict(x*u*2 + z) == {(1, 0, 0): 2*u, (0, 0, 1): 1} + + assert dict(2*u*x*y + z) == dict(x*y*2*u + z) == {(1, 1, 0): 2*u, (0, 0, 1): 1} + assert dict(u*2*x*y + z) == dict(2*x*y*u + z) == {(1, 1, 0): 2*u, (0, 0, 1): 1} + assert dict(2*u*x*y + z) == dict(x*y*2*u + z) == {(1, 1, 0): 2*u, (0, 0, 1): 1} + assert dict(u*x*y*2 + z) == dict(x*y*u*2 + z) == {(1, 1, 0): 2*u, (0, 0, 1): 1} + + assert dict(2*u*y*x + z) == dict(y*x*2*u + z) == {(1, 1, 0): 2*u, (0, 0, 1): 1} + assert dict(u*2*y*x + z) == dict(2*y*x*u + z) == {(1, 1, 0): 2*u, (0, 0, 1): 1} + assert dict(2*u*y*x + z) == dict(y*x*2*u + z) == {(1, 1, 0): 2*u, (0, 0, 1): 1} + assert dict(u*y*x*2 + z) == dict(y*x*u*2 + z) == {(1, 1, 0): 2*u, (0, 0, 1): 1} + + assert dict(3*u*(x + y) + z) == dict((x + y)*3*u + z) == {(1, 0, 0): 3*u, (0, 1, 0): 3*u, (0, 0, 1): 1} + + raises(TypeError, lambda: t*x + z) + raises(TypeError, lambda: x*t + z) + raises(TypeError, lambda: t*u + z) + raises(TypeError, lambda: u*t + z) + + Fuv, u,v = field("u,v", ZZ) + Rxyz, x,y,z = ring("x,y,z", Fuv) + + assert dict(u*x) == dict(x*u) == {(1, 0, 0): u} + + Rxyz, x,y,z = ring("x,y,z", EX) + + assert dict(EX(pi)*x*y*z) == dict(x*y*z*EX(pi)) == {(1, 1, 1): EX(pi)} + +def test_PolyElement___truediv__(): + R, x,y,z = ring("x,y,z", ZZ) + + assert (2*x**2 - 4)/2 == x**2 - 2 + assert (2*x**2 - 3)/2 == x**2 + + assert (x**2 - 1).quo(x) == x + assert (x**2 - x).quo(x) == x - 1 + + assert (x**2 - 1)/x == x - x**(-1) + assert (x**2 - x)/x == x - 1 + assert (x**2 - 1)/(2*x) == x/2 - x**(-1)/2 + + assert (x**2 - 1).quo(2*x) == 0 + assert (x**2 - x)/(x - 1) == (x**2 - x).quo(x - 1) == x + + + R, x,y,z = ring("x,y,z", ZZ) + assert len((x**2/3 + y**3/4 + z**4/5).terms()) == 0 + + R, x,y,z = ring("x,y,z", QQ) + assert len((x**2/3 + y**3/4 + z**4/5).terms()) == 3 + + Rt, t = ring("t", ZZ) + Ruv, u,v = ring("u,v", ZZ) + Rxyz, x,y,z = ring("x,y,z", Ruv) + + assert dict((u**2*x + u)/u) == {(1, 0, 0): u, (0, 0, 0): 1} + raises(TypeError, lambda: u/(u**2*x + u)) + + raises(TypeError, lambda: t/x) + raises(TypeError, lambda: x/t) + raises(TypeError, lambda: t/u) + raises(TypeError, lambda: u/t) + + R, x = ring("x", ZZ) + f, g = x**2 + 2*x + 3, R(0) + + raises(ZeroDivisionError, lambda: f.div(g)) + raises(ZeroDivisionError, lambda: divmod(f, g)) + raises(ZeroDivisionError, lambda: f.rem(g)) + raises(ZeroDivisionError, lambda: f % g) + raises(ZeroDivisionError, lambda: f.quo(g)) + raises(ZeroDivisionError, lambda: f / g) + raises(ZeroDivisionError, lambda: f.exquo(g)) + + R, x, y = ring("x,y", ZZ) + f, g = x*y + 2*x + 3, R(0) + + raises(ZeroDivisionError, lambda: f.div(g)) + raises(ZeroDivisionError, lambda: divmod(f, g)) + raises(ZeroDivisionError, lambda: f.rem(g)) + raises(ZeroDivisionError, lambda: f % g) + raises(ZeroDivisionError, lambda: f.quo(g)) + raises(ZeroDivisionError, lambda: f / g) + raises(ZeroDivisionError, lambda: f.exquo(g)) + + R, x = ring("x", ZZ) + + f, g = x**2 + 1, 2*x - 4 + q, r = R(0), x**2 + 1 + + assert f.div(g) == divmod(f, g) == (q, r) + assert f.rem(g) == f % g == r + assert f.quo(g) == f / g == q + raises(ExactQuotientFailed, lambda: f.exquo(g)) + + f, g = 3*x**3 + x**2 + x + 5, 5*x**2 - 3*x + 1 + q, r = R(0), f + + assert f.div(g) == divmod(f, g) == (q, r) + assert f.rem(g) == f % g == r + assert f.quo(g) == f / g == q + raises(ExactQuotientFailed, lambda: f.exquo(g)) + + f, g = 5*x**4 + 4*x**3 + 3*x**2 + 2*x + 1, x**2 + 2*x + 3 + q, r = 5*x**2 - 6*x, 20*x + 1 + + assert f.div(g) == divmod(f, g) == (q, r) + assert f.rem(g) == f % g == r + assert f.quo(g) == f / g == q + raises(ExactQuotientFailed, lambda: f.exquo(g)) + + f, g = 5*x**5 + 4*x**4 + 3*x**3 + 2*x**2 + x, x**4 + 2*x**3 + 9 + q, r = 5*x - 6, 15*x**3 + 2*x**2 - 44*x + 54 + + assert f.div(g) == divmod(f, g) == (q, r) + assert f.rem(g) == f % g == r + assert f.quo(g) == f / g == q + raises(ExactQuotientFailed, lambda: f.exquo(g)) + + R, x = ring("x", QQ) + + f, g = x**2 + 1, 2*x - 4 + q, r = x/2 + 1, R(5) + + assert f.div(g) == divmod(f, g) == (q, r) + assert f.rem(g) == f % g == r + assert f.quo(g) == f / g == q + raises(ExactQuotientFailed, lambda: f.exquo(g)) + + f, g = 3*x**3 + x**2 + x + 5, 5*x**2 - 3*x + 1 + q, r = QQ(3, 5)*x + QQ(14, 25), QQ(52, 25)*x + QQ(111, 25) + + assert f.div(g) == divmod(f, g) == (q, r) + assert f.rem(g) == f % g == r + assert f.quo(g) == f / g == q + raises(ExactQuotientFailed, lambda: f.exquo(g)) + + R, x,y = ring("x,y", ZZ) + + f, g = x**2 - y**2, x - y + q, r = x + y, R(0) + + assert f.div(g) == divmod(f, g) == (q, r) + assert f.rem(g) == f % g == r + assert f.quo(g) == f / g == q + assert f.exquo(g) == q + + f, g = x**2 + y**2, x - y + q, r = x + y, 2*y**2 + + assert f.div(g) == divmod(f, g) == (q, r) + assert f.rem(g) == f % g == r + assert f.quo(g) == f / g == q + raises(ExactQuotientFailed, lambda: f.exquo(g)) + + f, g = x**2 + y**2, -x + y + q, r = -x - y, 2*y**2 + + assert f.div(g) == divmod(f, g) == (q, r) + assert f.rem(g) == f % g == r + assert f.quo(g) == f / g == q + raises(ExactQuotientFailed, lambda: f.exquo(g)) + + f, g = x**2 + y**2, 2*x - 2*y + q, r = R(0), f + + assert f.div(g) == divmod(f, g) == (q, r) + assert f.rem(g) == f % g == r + assert f.quo(g) == f / g == q + raises(ExactQuotientFailed, lambda: f.exquo(g)) + + R, x,y = ring("x,y", QQ) + + f, g = x**2 - y**2, x - y + q, r = x + y, R(0) + + assert f.div(g) == divmod(f, g) == (q, r) + assert f.rem(g) == f % g == r + assert f.quo(g) == f / g == q + assert f.exquo(g) == q + + f, g = x**2 + y**2, x - y + q, r = x + y, 2*y**2 + + assert f.div(g) == divmod(f, g) == (q, r) + assert f.rem(g) == f % g == r + assert f.quo(g) == f / g == q + raises(ExactQuotientFailed, lambda: f.exquo(g)) + + f, g = x**2 + y**2, -x + y + q, r = -x - y, 2*y**2 + + assert f.div(g) == divmod(f, g) == (q, r) + assert f.rem(g) == f % g == r + assert f.quo(g) == f / g == q + raises(ExactQuotientFailed, lambda: f.exquo(g)) + + f, g = x**2 + y**2, 2*x - 2*y + q, r = x/2 + y/2, 2*y**2 + + assert f.div(g) == divmod(f, g) == (q, r) + assert f.rem(g) == f % g == r + assert f.quo(g) == f / g == q + raises(ExactQuotientFailed, lambda: f.exquo(g)) + +def test_PolyElement___pow__(): + R, x = ring("x", ZZ, grlex) + f = 2*x + 3 + + assert f**0 == 1 + assert f**1 == f + raises(ValueError, lambda: f**(-1)) + + assert x**(-1) == x**(-1) + + assert f**2 == f._pow_generic(2) == f._pow_multinomial(2) == 4*x**2 + 12*x + 9 + assert f**3 == f._pow_generic(3) == f._pow_multinomial(3) == 8*x**3 + 36*x**2 + 54*x + 27 + assert f**4 == f._pow_generic(4) == f._pow_multinomial(4) == 16*x**4 + 96*x**3 + 216*x**2 + 216*x + 81 + assert f**5 == f._pow_generic(5) == f._pow_multinomial(5) == 32*x**5 + 240*x**4 + 720*x**3 + 1080*x**2 + 810*x + 243 + + R, x,y,z = ring("x,y,z", ZZ, grlex) + f = x**3*y - 2*x*y**2 - 3*z + 1 + g = x**6*y**2 - 4*x**4*y**3 - 6*x**3*y*z + 2*x**3*y + 4*x**2*y**4 + 12*x*y**2*z - 4*x*y**2 + 9*z**2 - 6*z + 1 + + assert f**2 == f._pow_generic(2) == f._pow_multinomial(2) == g + + R, t = ring("t", ZZ) + f = -11200*t**4 - 2604*t**2 + 49 + g = 15735193600000000*t**16 + 14633730048000000*t**14 + 4828147466240000*t**12 \ + + 598976863027200*t**10 + 3130812416256*t**8 - 2620523775744*t**6 \ + + 92413760096*t**4 - 1225431984*t**2 + 5764801 + + assert f**4 == f._pow_generic(4) == f._pow_multinomial(4) == g + +def test_PolyElement_div(): + R, x = ring("x", ZZ, grlex) + + f = x**3 - 12*x**2 - 42 + g = x - 3 + + q = x**2 - 9*x - 27 + r = -123 + + assert f.div([g]) == ([q], r) + + R, x = ring("x", ZZ, grlex) + f = x**2 + 2*x + 2 + assert f.div([R(1)]) == ([f], 0) + + R, x = ring("x", QQ, grlex) + f = x**2 + 2*x + 2 + assert f.div([R(2)]) == ([QQ(1,2)*x**2 + x + 1], 0) + + R, x,y = ring("x,y", ZZ, grlex) + f = 4*x**2*y - 2*x*y + 4*x - 2*y + 8 + + assert f.div([R(2)]) == ([2*x**2*y - x*y + 2*x - y + 4], 0) + assert f.div([2*y]) == ([2*x**2 - x - 1], 4*x + 8) + + f = x - 1 + g = y - 1 + + assert f.div([g]) == ([0], f) + + f = x*y**2 + 1 + G = [x*y + 1, y + 1] + + Q = [y, -1] + r = 2 + + assert f.div(G) == (Q, r) + + f = x**2*y + x*y**2 + y**2 + G = [x*y - 1, y**2 - 1] + + Q = [x + y, 1] + r = x + y + 1 + + assert f.div(G) == (Q, r) + + G = [y**2 - 1, x*y - 1] + + Q = [x + 1, x] + r = 2*x + 1 + + assert f.div(G) == (Q, r) + + R, = ring("", ZZ) + assert R(3).div(R(2)) == (0, 3) + + R, = ring("", QQ) + assert R(3).div(R(2)) == (QQ(3, 2), 0) + +def test_PolyElement_rem(): + R, x = ring("x", ZZ, grlex) + + f = x**3 - 12*x**2 - 42 + g = x - 3 + r = -123 + + assert f.rem([g]) == f.div([g])[1] == r + + R, x,y = ring("x,y", ZZ, grlex) + + f = 4*x**2*y - 2*x*y + 4*x - 2*y + 8 + + assert f.rem([R(2)]) == f.div([R(2)])[1] == 0 + assert f.rem([2*y]) == f.div([2*y])[1] == 4*x + 8 + + f = x - 1 + g = y - 1 + + assert f.rem([g]) == f.div([g])[1] == f + + f = x*y**2 + 1 + G = [x*y + 1, y + 1] + r = 2 + + assert f.rem(G) == f.div(G)[1] == r + + f = x**2*y + x*y**2 + y**2 + G = [x*y - 1, y**2 - 1] + r = x + y + 1 + + assert f.rem(G) == f.div(G)[1] == r + + G = [y**2 - 1, x*y - 1] + r = 2*x + 1 + + assert f.rem(G) == f.div(G)[1] == r + +def test_PolyElement_deflate(): + R, x = ring("x", ZZ) + + assert (2*x**2).deflate(x**4 + 4*x**2 + 1) == ((2,), [2*x, x**2 + 4*x + 1]) + + R, x,y = ring("x,y", ZZ) + + assert R(0).deflate(R(0)) == ((1, 1), [0, 0]) + assert R(1).deflate(R(0)) == ((1, 1), [1, 0]) + assert R(1).deflate(R(2)) == ((1, 1), [1, 2]) + assert R(1).deflate(2*y) == ((1, 1), [1, 2*y]) + assert (2*y).deflate(2*y) == ((1, 1), [2*y, 2*y]) + assert R(2).deflate(2*y**2) == ((1, 2), [2, 2*y]) + assert (2*y**2).deflate(2*y**2) == ((1, 2), [2*y, 2*y]) + + f = x**4*y**2 + x**2*y + 1 + g = x**2*y**3 + x**2*y + 1 + + assert f.deflate(g) == ((2, 1), [x**2*y**2 + x*y + 1, x*y**3 + x*y + 1]) + +def test_PolyElement_clear_denoms(): + R, x,y = ring("x,y", QQ) + + assert R(1).clear_denoms() == (ZZ(1), 1) + assert R(7).clear_denoms() == (ZZ(1), 7) + + assert R(QQ(7,3)).clear_denoms() == (3, 7) + assert R(QQ(7,3)).clear_denoms() == (3, 7) + + assert (3*x**2 + x).clear_denoms() == (1, 3*x**2 + x) + assert (x**2 + QQ(1,2)*x).clear_denoms() == (2, 2*x**2 + x) + + rQQ, x,t = ring("x,t", QQ, lex) + rZZ, X,T = ring("x,t", ZZ, lex) + + F = [x - QQ(17824537287975195925064602467992950991718052713078834557692023531499318507213727406844943097,413954288007559433755329699713866804710749652268151059918115348815925474842910720000)*t**7 + - QQ(4882321164854282623427463828745855894130208215961904469205260756604820743234704900167747753,12936071500236232304854053116058337647210926633379720622441104650497671088840960000)*t**6 + - QQ(36398103304520066098365558157422127347455927422509913596393052633155821154626830576085097433,25872143000472464609708106232116675294421853266759441244882209300995342177681920000)*t**5 + - QQ(168108082231614049052707339295479262031324376786405372698857619250210703675982492356828810819,58212321751063045371843239022262519412449169850208742800984970927239519899784320000)*t**4 + - QQ(5694176899498574510667890423110567593477487855183144378347226247962949388653159751849449037,1617008937529529038106756639507292205901365829172465077805138081312208886105120000)*t**3 + - QQ(154482622347268833757819824809033388503591365487934245386958884099214649755244381307907779,60637835157357338929003373981523457721301218593967440417692678049207833228942000)*t**2 + - QQ(2452813096069528207645703151222478123259511586701148682951852876484544822947007791153163,2425513406294293557160134959260938308852048743758697616707707121968313329157680)*t + - QQ(34305265428126440542854669008203683099323146152358231964773310260498715579162112959703,202126117191191129763344579938411525737670728646558134725642260164026110763140), + t**8 + QQ(693749860237914515552,67859264524169150569)*t**7 + + QQ(27761407182086143225024,610733380717522355121)*t**6 + + QQ(7785127652157884044288,67859264524169150569)*t**5 + + QQ(36567075214771261409792,203577793572507451707)*t**4 + + QQ(36336335165196147384320,203577793572507451707)*t**3 + + QQ(7452455676042754048000,67859264524169150569)*t**2 + + QQ(2593331082514399232000,67859264524169150569)*t + + QQ(390399197427343360000,67859264524169150569)] + + G = [3725588592068034903797967297424801242396746870413359539263038139343329273586196480000*X - + 160420835591776763325581422211936558925462474417709511019228211783493866564923546661604487873*T**7 - + 1406108495478033395547109582678806497509499966197028487131115097902188374051595011248311352864*T**6 - + 5241326875850889518164640374668786338033653548841427557880599579174438246266263602956254030352*T**5 - + 10758917262823299139373269714910672770004760114329943852726887632013485035262879510837043892416*T**4 - + 13119383576444715672578819534846747735372132018341964647712009275306635391456880068261130581248*T**3 - + 9491412317016197146080450036267011389660653495578680036574753839055748080962214787557853941760*T**2 - + 3767520915562795326943800040277726397326609797172964377014046018280260848046603967211258368000*T - + 632314652371226552085897259159210286886724229880266931574701654721512325555116066073245696000, + 610733380717522355121*T**8 + + 6243748742141230639968*T**7 + + 27761407182086143225024*T**6 + + 70066148869420956398592*T**5 + + 109701225644313784229376*T**4 + + 109009005495588442152960*T**3 + + 67072101084384786432000*T**2 + + 23339979742629593088000*T + + 3513592776846090240000] + + assert [ f.clear_denoms()[1].set_ring(rZZ) for f in F ] == G + +def test_PolyElement_cofactors(): + R, x, y = ring("x,y", ZZ) + + f, g = R(0), R(0) + assert f.cofactors(g) == (0, 0, 0) + + f, g = R(2), R(0) + assert f.cofactors(g) == (2, 1, 0) + + f, g = R(-2), R(0) + assert f.cofactors(g) == (2, -1, 0) + + f, g = R(0), R(-2) + assert f.cofactors(g) == (2, 0, -1) + + f, g = R(0), 2*x + 4 + assert f.cofactors(g) == (2*x + 4, 0, 1) + + f, g = 2*x + 4, R(0) + assert f.cofactors(g) == (2*x + 4, 1, 0) + + f, g = R(2), R(2) + assert f.cofactors(g) == (2, 1, 1) + + f, g = R(-2), R(2) + assert f.cofactors(g) == (2, -1, 1) + + f, g = R(2), R(-2) + assert f.cofactors(g) == (2, 1, -1) + + f, g = R(-2), R(-2) + assert f.cofactors(g) == (2, -1, -1) + + f, g = x**2 + 2*x + 1, R(1) + assert f.cofactors(g) == (1, x**2 + 2*x + 1, 1) + + f, g = x**2 + 2*x + 1, R(2) + assert f.cofactors(g) == (1, x**2 + 2*x + 1, 2) + + f, g = 2*x**2 + 4*x + 2, R(2) + assert f.cofactors(g) == (2, x**2 + 2*x + 1, 1) + + f, g = R(2), 2*x**2 + 4*x + 2 + assert f.cofactors(g) == (2, 1, x**2 + 2*x + 1) + + f, g = 2*x**2 + 4*x + 2, x + 1 + assert f.cofactors(g) == (x + 1, 2*x + 2, 1) + + f, g = x + 1, 2*x**2 + 4*x + 2 + assert f.cofactors(g) == (x + 1, 1, 2*x + 2) + + R, x, y, z, t = ring("x,y,z,t", ZZ) + + f, g = t**2 + 2*t + 1, 2*t + 2 + assert f.cofactors(g) == (t + 1, t + 1, 2) + + f, g = z**2*t**2 + 2*z**2*t + z**2 + z*t + z, t**2 + 2*t + 1 + h, cff, cfg = t + 1, z**2*t + z**2 + z, t + 1 + + assert f.cofactors(g) == (h, cff, cfg) + assert g.cofactors(f) == (h, cfg, cff) + + R, x, y = ring("x,y", QQ) + + f = QQ(1,2)*x**2 + x + QQ(1,2) + g = QQ(1,2)*x + QQ(1,2) + + h = x + 1 + + assert f.cofactors(g) == (h, g, QQ(1,2)) + assert g.cofactors(f) == (h, QQ(1,2), g) + + R, x, y = ring("x,y", RR) + + f = 2.1*x*y**2 - 2.1*x*y + 2.1*x + g = 2.1*x**3 + h = 1.0*x + + assert f.cofactors(g) == (h, f/h, g/h) + assert g.cofactors(f) == (h, g/h, f/h) + +def test_PolyElement_gcd(): + R, x, y = ring("x,y", QQ) + + f = QQ(1,2)*x**2 + x + QQ(1,2) + g = QQ(1,2)*x + QQ(1,2) + + assert f.gcd(g) == x + 1 + +def test_PolyElement_cancel(): + R, x, y = ring("x,y", ZZ) + + f = 2*x**3 + 4*x**2 + 2*x + g = 3*x**2 + 3*x + F = 2*x + 2 + G = 3 + + assert f.cancel(g) == (F, G) + + assert (-f).cancel(g) == (-F, G) + assert f.cancel(-g) == (-F, G) + + R, x, y = ring("x,y", QQ) + + f = QQ(1,2)*x**3 + x**2 + QQ(1,2)*x + g = QQ(1,3)*x**2 + QQ(1,3)*x + F = 3*x + 3 + G = 2 + + assert f.cancel(g) == (F, G) + + assert (-f).cancel(g) == (-F, G) + assert f.cancel(-g) == (-F, G) + + Fx, x = field("x", ZZ) + Rt, t = ring("t", Fx) + + f = (-x**2 - 4)/4*t + g = t**2 + (x**2 + 2)/2 + + assert f.cancel(g) == ((-x**2 - 4)*t, 4*t**2 + 2*x**2 + 4) + +def test_PolyElement_max_norm(): + R, x, y = ring("x,y", ZZ) + + assert R(0).max_norm() == 0 + assert R(1).max_norm() == 1 + + assert (x**3 + 4*x**2 + 2*x + 3).max_norm() == 4 + +def test_PolyElement_l1_norm(): + R, x, y = ring("x,y", ZZ) + + assert R(0).l1_norm() == 0 + assert R(1).l1_norm() == 1 + + assert (x**3 + 4*x**2 + 2*x + 3).l1_norm() == 10 + +def test_PolyElement_diff(): + R, X = xring("x:11", QQ) + + f = QQ(288,5)*X[0]**8*X[1]**6*X[4]**3*X[10]**2 + 8*X[0]**2*X[2]**3*X[4]**3 +2*X[0]**2 - 2*X[1]**2 + + assert f.diff(X[0]) == QQ(2304,5)*X[0]**7*X[1]**6*X[4]**3*X[10]**2 + 16*X[0]*X[2]**3*X[4]**3 + 4*X[0] + assert f.diff(X[4]) == QQ(864,5)*X[0]**8*X[1]**6*X[4]**2*X[10]**2 + 24*X[0]**2*X[2]**3*X[4]**2 + assert f.diff(X[10]) == QQ(576,5)*X[0]**8*X[1]**6*X[4]**3*X[10] + +def test_PolyElement___call__(): + R, x = ring("x", ZZ) + f = 3*x + 1 + + assert f(0) == 1 + assert f(1) == 4 + + raises(ValueError, lambda: f()) + raises(ValueError, lambda: f(0, 1)) + + raises(CoercionFailed, lambda: f(QQ(1,7))) + + R, x,y = ring("x,y", ZZ) + f = 3*x + y**2 + 1 + + assert f(0, 0) == 1 + assert f(1, 7) == 53 + + Ry = R.drop(x) + + assert f(0) == Ry.y**2 + 1 + assert f(1) == Ry.y**2 + 4 + + raises(ValueError, lambda: f()) + raises(ValueError, lambda: f(0, 1, 2)) + + raises(CoercionFailed, lambda: f(1, QQ(1,7))) + raises(CoercionFailed, lambda: f(QQ(1,7), 1)) + raises(CoercionFailed, lambda: f(QQ(1,7), QQ(1,7))) + +def test_PolyElement_evaluate(): + R, x = ring("x", ZZ) + f = x**3 + 4*x**2 + 2*x + 3 + + r = f.evaluate(x, 0) + assert r == 3 and not isinstance(r, PolyElement) + + raises(CoercionFailed, lambda: f.evaluate(x, QQ(1,7))) + + R, x, y, z = ring("x,y,z", ZZ) + f = (x*y)**3 + 4*(x*y)**2 + 2*x*y + 3 + + r = f.evaluate(x, 0) + assert r == 3 and isinstance(r, R.drop(x).dtype) + r = f.evaluate([(x, 0), (y, 0)]) + assert r == 3 and isinstance(r, R.drop(x, y).dtype) + r = f.evaluate(y, 0) + assert r == 3 and isinstance(r, R.drop(y).dtype) + r = f.evaluate([(y, 0), (x, 0)]) + assert r == 3 and isinstance(r, R.drop(y, x).dtype) + + r = f.evaluate([(x, 0), (y, 0), (z, 0)]) + assert r == 3 and not isinstance(r, PolyElement) + + raises(CoercionFailed, lambda: f.evaluate([(x, 1), (y, QQ(1,7))])) + raises(CoercionFailed, lambda: f.evaluate([(x, QQ(1,7)), (y, 1)])) + raises(CoercionFailed, lambda: f.evaluate([(x, QQ(1,7)), (y, QQ(1,7))])) + +def test_PolyElement_subs(): + R, x = ring("x", ZZ) + f = x**3 + 4*x**2 + 2*x + 3 + + r = f.subs(x, 0) + assert r == 3 and isinstance(r, R.dtype) + + raises(CoercionFailed, lambda: f.subs(x, QQ(1,7))) + + R, x, y, z = ring("x,y,z", ZZ) + f = x**3 + 4*x**2 + 2*x + 3 + + r = f.subs(x, 0) + assert r == 3 and isinstance(r, R.dtype) + r = f.subs([(x, 0), (y, 0)]) + assert r == 3 and isinstance(r, R.dtype) + + raises(CoercionFailed, lambda: f.subs([(x, 1), (y, QQ(1,7))])) + raises(CoercionFailed, lambda: f.subs([(x, QQ(1,7)), (y, 1)])) + raises(CoercionFailed, lambda: f.subs([(x, QQ(1,7)), (y, QQ(1,7))])) + +def test_PolyElement_symmetrize(): + R, x, y = ring("x,y", ZZ) + + # Homogeneous, symmetric + f = x**2 + y**2 + sym, rem, m = f.symmetrize() + assert rem == 0 + assert sym.compose(m) + rem == f + + # Homogeneous, asymmetric + f = x**2 - y**2 + sym, rem, m = f.symmetrize() + assert rem != 0 + assert sym.compose(m) + rem == f + + # Inhomogeneous, symmetric + f = x*y + 7 + sym, rem, m = f.symmetrize() + assert rem == 0 + assert sym.compose(m) + rem == f + + # Inhomogeneous, asymmetric + f = y + 7 + sym, rem, m = f.symmetrize() + assert rem != 0 + assert sym.compose(m) + rem == f + + # Constant + f = R.from_expr(3) + sym, rem, m = f.symmetrize() + assert rem == 0 + assert sym.compose(m) + rem == f + + # Constant constructed from sring + R, f = sring(3) + sym, rem, m = f.symmetrize() + assert rem == 0 + assert sym.compose(m) + rem == f + +def test_PolyElement_compose(): + R, x = ring("x", ZZ) + f = x**3 + 4*x**2 + 2*x + 3 + + r = f.compose(x, 0) + assert r == 3 and isinstance(r, R.dtype) + + assert f.compose(x, x) == f + assert f.compose(x, x**2) == x**6 + 4*x**4 + 2*x**2 + 3 + + raises(CoercionFailed, lambda: f.compose(x, QQ(1,7))) + + R, x, y, z = ring("x,y,z", ZZ) + f = x**3 + 4*x**2 + 2*x + 3 + + r = f.compose(x, 0) + assert r == 3 and isinstance(r, R.dtype) + r = f.compose([(x, 0), (y, 0)]) + assert r == 3 and isinstance(r, R.dtype) + + r = (x**3 + 4*x**2 + 2*x*y*z + 3).compose(x, y*z**2 - 1) + q = (y*z**2 - 1)**3 + 4*(y*z**2 - 1)**2 + 2*(y*z**2 - 1)*y*z + 3 + assert r == q and isinstance(r, R.dtype) + +def test_PolyElement_is_(): + R, x,y,z = ring("x,y,z", QQ) + + assert (x - x).is_generator == False + assert (x - x).is_ground == True + assert (x - x).is_monomial == True + assert (x - x).is_term == True + + assert (x - x + 1).is_generator == False + assert (x - x + 1).is_ground == True + assert (x - x + 1).is_monomial == True + assert (x - x + 1).is_term == True + + assert x.is_generator == True + assert x.is_ground == False + assert x.is_monomial == True + assert x.is_term == True + + assert (x*y).is_generator == False + assert (x*y).is_ground == False + assert (x*y).is_monomial == True + assert (x*y).is_term == True + + assert (3*x).is_generator == False + assert (3*x).is_ground == False + assert (3*x).is_monomial == False + assert (3*x).is_term == True + + assert (3*x + 1).is_generator == False + assert (3*x + 1).is_ground == False + assert (3*x + 1).is_monomial == False + assert (3*x + 1).is_term == False + + assert R(0).is_zero is True + assert R(1).is_zero is False + + assert R(0).is_one is False + assert R(1).is_one is True + + assert (x - 1).is_monic is True + assert (2*x - 1).is_monic is False + + assert (3*x + 2).is_primitive is True + assert (4*x + 2).is_primitive is False + + assert (x + y + z + 1).is_linear is True + assert (x*y*z + 1).is_linear is False + + assert (x*y + z + 1).is_quadratic is True + assert (x*y*z + 1).is_quadratic is False + + assert (x - 1).is_squarefree is True + assert ((x - 1)**2).is_squarefree is False + + assert (x**2 + x + 1).is_irreducible is True + assert (x**2 + 2*x + 1).is_irreducible is False + + _, t = ring("t", FF(11)) + + assert (7*t + 3).is_irreducible is True + assert (7*t**2 + 3*t + 1).is_irreducible is False + + _, u = ring("u", ZZ) + f = u**16 + u**14 - u**10 - u**8 - u**6 + u**2 + + assert f.is_cyclotomic is False + assert (f + 1).is_cyclotomic is True + + raises(MultivariatePolynomialError, lambda: x.is_cyclotomic) + + R, = ring("", ZZ) + assert R(4).is_squarefree is True + assert R(6).is_irreducible is True + +def test_PolyElement_drop(): + R, x,y,z = ring("x,y,z", ZZ) + + assert R(1).drop(0).ring == PolyRing("y,z", ZZ, lex) + assert R(1).drop(0).drop(0).ring == PolyRing("z", ZZ, lex) + assert isinstance(R(1).drop(0).drop(0).drop(0), R.dtype) is False + + raises(ValueError, lambda: z.drop(0).drop(0).drop(0)) + raises(ValueError, lambda: x.drop(0)) + +def test_PolyElement_coeff_wrt(): + R, x, y, z = ring("x, y, z", ZZ) + + p = 4*x**3 + 5*y**2 + 6*y**2*z + 7 + assert p.coeff_wrt(1, 2) == 6*z + 5 # using generator index + assert p.coeff_wrt(x, 3) == 4 # using generator + + p = 2*x**4 + 3*x*y**2*z + 10*y**2 + 10*x*z**2 + assert p.coeff_wrt(x, 1) == 3*y**2*z + 10*z**2 + assert p.coeff_wrt(y, 2) == 3*x*z + 10 + + p = 4*x**2 + 2*x*y + 5 + assert p.coeff_wrt(z, 1) == R(0) + assert p.coeff_wrt(y, 2) == R(0) + +def test_PolyElement_prem(): + R, x, y = ring("x, y", ZZ) + + f, g = x**2 + x*y, 2*x + 2 + assert f.prem(g) == -4*y + 4 # first generator is chosen by default if it is not given + + f, g = x**2 + 1, 2*x - 4 + assert f.prem(g) == f.prem(g, x) == 20 + assert f.prem(g, 1) == R(0) + + f, g = x*y + 2*x + 1, x + y + assert f.prem(g) == -y**2 - 2*y + 1 + assert f.prem(g, 1) == f.prem(g, y) == -x**2 + 2*x + 1 + + raises(ZeroDivisionError, lambda: f.prem(R(0))) + +def test_PolyElement_pdiv(): + R, x, y = ring("x,y", ZZ) + + f, g = x**4 + 5*x**3 + 7*x**2, 2*x**2 + 3 + assert f.pdiv(g) == f.pdiv(g, x) == (4*x**2 + 20*x + 22, -60*x - 66) + + f, g = x**2 - y**2, x - y + assert f.pdiv(g) == f.pdiv(g, 0) == (x + y, 0) + + f, g = x*y + 2*x + 1, x + y + assert f.pdiv(g) == (y + 2, -y**2 - 2*y + 1) + assert f.pdiv(g, y) == f.pdiv(g, 1) == (x + 1, -x**2 + 2*x + 1) + + assert R(0).pdiv(g) == (0, 0) + raises(ZeroDivisionError, lambda: f.prem(R(0))) + +def test_PolyElement_pquo(): + R, x, y = ring("x, y", ZZ) + + f, g = x**4 - 4*x**2*y + 4*y**2, x**2 - 2*y + assert f.pquo(g) == f.pquo(g, x) == x**2 - 2*y + assert f.pquo(g, y) == 4*x**2 - 8*y + 4 + + f, g = x**4 - y**4, x**2 - y**2 + assert f.pquo(g) == f.pquo(g, 0) == x**2 + y**2 + +def test_PolyElement_pexquo(): + R, x, y = ring("x, y", ZZ) + + f, g = x**2 - y**2, x - y + assert f.pexquo(g) == f.pexquo(g, x) == x + y + assert f.pexquo(g, y) == f.pexquo(g, 1) == x + y + 1 + + f, g = x**2 + 3*x + 6, x + 2 + raises(ExactQuotientFailed, lambda: f.pexquo(g)) + +def test_PolyElement_gcdex(): + _, x = ring("x", QQ) + + f, g = 2*x, x**2 - 16 + s, t, h = x/32, -QQ(1, 16), 1 + + assert f.half_gcdex(g) == (s, h) + assert f.gcdex(g) == (s, t, h) + +def test_PolyElement_subresultants(): + R, x, y = ring("x, y", ZZ) + + f, g = x**2*y + x*y, x + y # degree(f, x) > degree(g, x) + h = y**3 - y**2 + assert f.subresultants(g) == [f, g, h] # first generator is chosen default + + # generator index or generator is given + assert f.subresultants(g, 0) == f.subresultants(g, x) == [f, g, h] + + assert f.subresultants(g, y) == [x**2*y + x*y, x + y, x**3 + x**2] + + f, g = 2*x - y, x**2 + 2*y + x # degree(f, x) < degree(g, x) + assert f.subresultants(g) == [x**2 + x + 2*y, 2*x - y, y**2 + 10*y] + + f, g = R(0), y**3 - y**2 # f = 0 + assert f.subresultants(g) == [y**3 - y**2, 1] + + f, g = x**2*y + x*y, R(0) # g = 0 + assert f.subresultants(g) == [x**2*y + x*y, 1] + + f, g = R(0), R(0) # f = 0 and g = 0 + assert f.subresultants(g) == [0, 0] + + f, g = x**2 + x, x**2 + x # f and g are same polynomial + assert f.subresultants(g) == [x**2 + x, x**2 + x] + +def test_PolyElement_resultant(): + _, x = ring("x", ZZ) + f, g, h = x**2 - 2*x + 1, x**2 - 1, 0 + + assert f.resultant(g) == h + +def test_PolyElement_discriminant(): + _, x = ring("x", ZZ) + f, g = x**3 + 3*x**2 + 9*x - 13, -11664 + + assert f.discriminant() == g + + F, a, b, c = ring("a,b,c", ZZ) + _, x = ring("x", F) + + f, g = a*x**2 + b*x + c, b**2 - 4*a*c + + assert f.discriminant() == g + +def test_PolyElement_decompose(): + _, x = ring("x", ZZ) + + f = x**12 + 20*x**10 + 150*x**8 + 500*x**6 + 625*x**4 - 2*x**3 - 10*x + 9 + g = x**4 - 2*x + 9 + h = x**3 + 5*x + + assert g.compose(x, h) == f + assert f.decompose() == [g, h] + +def test_PolyElement_shift(): + _, x = ring("x", ZZ) + assert (x**2 - 2*x + 1).shift(2) == x**2 + 2*x + 1 + assert (x**2 - 2*x + 1).shift_list([2]) == x**2 + 2*x + 1 + + R, x, y = ring("x, y", ZZ) + assert (x*y).shift_list([1, 2]) == (x+1)*(y+2) + + raises(MultivariatePolynomialError, lambda: (x*y).shift(1)) + +def test_PolyElement_sturm(): + F, t = field("t", ZZ) + _, x = ring("x", F) + + f = 1024/(15625*t**8)*x**5 - 4096/(625*t**8)*x**4 + 32/(15625*t**4)*x**3 - 128/(625*t**4)*x**2 + F(1)/62500*x - F(1)/625 + + assert f.sturm() == [ + x**3 - 100*x**2 + t**4/64*x - 25*t**4/16, + 3*x**2 - 200*x + t**4/64, + (-t**4/96 + F(20000)/9)*x + 25*t**4/18, + (-9*t**12 - 11520000*t**8 - 3686400000000*t**4)/(576*t**8 - 245760000*t**4 + 26214400000000), + ] + +def test_PolyElement_gff_list(): + _, x = ring("x", ZZ) + + f = x**5 + 2*x**4 - x**3 - 2*x**2 + assert f.gff_list() == [(x, 1), (x + 2, 4)] + + f = x*(x - 1)**3*(x - 2)**2*(x - 4)**2*(x - 5) + assert f.gff_list() == [(x**2 - 5*x + 4, 1), (x**2 - 5*x + 4, 2), (x, 3)] + +def test_PolyElement_norm(): + k = QQ + K = QQ.algebraic_field(sqrt(2)) + sqrt2 = K.unit + _, X, Y = ring("x,y", k) + _, x, y = ring("x,y", K) + + assert (x*y + sqrt2).norm() == X**2*Y**2 - 2 + +def test_PolyElement_sqf_norm(): + R, x = ring("x", QQ.algebraic_field(sqrt(3))) + X = R.to_ground().x + + assert (x**2 - 2).sqf_norm() == ([1], x**2 - 2*sqrt(3)*x + 1, X**4 - 10*X**2 + 1) + + R, x = ring("x", QQ.algebraic_field(sqrt(2))) + X = R.to_ground().x + + assert (x**2 - 3).sqf_norm() == ([1], x**2 - 2*sqrt(2)*x - 1, X**4 - 10*X**2 + 1) + +def test_PolyElement_sqf_list(): + _, x = ring("x", ZZ) + + f = x**5 - x**3 - x**2 + 1 + g = x**3 + 2*x**2 + 2*x + 1 + h = x - 1 + p = x**4 + x**3 - x - 1 + + assert f.sqf_part() == p + assert f.sqf_list() == (1, [(g, 1), (h, 2)]) + +def test_issue_18894(): + items = [S(3)/16 + sqrt(3*sqrt(3) + 10)/8, S(1)/8 + 3*sqrt(3)/16, S(1)/8 + 3*sqrt(3)/16, -S(3)/16 + sqrt(3*sqrt(3) + 10)/8] + R, a = sring(items, extension=True) + assert R.domain == QQ.algebraic_field(sqrt(3)+sqrt(3*sqrt(3)+10)) + assert R.gens == () + result = [] + for item in items: + result.append(R.domain.from_sympy(item)) + assert a == result + +def test_PolyElement_factor_list(): + _, x = ring("x", ZZ) + + f = x**5 - x**3 - x**2 + 1 + + u = x + 1 + v = x - 1 + w = x**2 + x + 1 + + assert f.factor_list() == (1, [(u, 1), (v, 2), (w, 1)]) + + +def test_issue_21410(): + R, x = ring('x', FF(2)) + p = x**6 + x**5 + x**4 + x**3 + 1 + assert p._pow_multinomial(4) == x**24 + x**20 + x**16 + x**12 + 1 diff --git a/lib/python3.12/site-packages/sympy/polys/tests/test_rootisolation.py b/lib/python3.12/site-packages/sympy/polys/tests/test_rootisolation.py new file mode 100644 index 0000000000000000000000000000000000000000..9661c1d6b63bfb941157c7e904ba4e048afbc538 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/polys/tests/test_rootisolation.py @@ -0,0 +1,823 @@ +"""Tests for real and complex root isolation and refinement algorithms. """ + +from sympy.polys.rings import ring +from sympy.polys.domains import ZZ, QQ, ZZ_I, EX +from sympy.polys.polyerrors import DomainError, RefinementFailed, PolynomialError +from sympy.polys.rootisolation import ( + dup_cauchy_upper_bound, dup_cauchy_lower_bound, + dup_mignotte_sep_bound_squared, +) +from sympy.testing.pytest import raises + +def test_dup_sturm(): + R, x = ring("x", QQ) + + assert R.dup_sturm(5) == [1] + assert R.dup_sturm(x) == [x, 1] + + f = x**3 - 2*x**2 + 3*x - 5 + assert R.dup_sturm(f) == [f, 3*x**2 - 4*x + 3, -QQ(10,9)*x + QQ(13,3), -QQ(3303,100)] + + +def test_dup_cauchy_upper_bound(): + raises(PolynomialError, lambda: dup_cauchy_upper_bound([], QQ)) + raises(PolynomialError, lambda: dup_cauchy_upper_bound([QQ(1)], QQ)) + raises(DomainError, lambda: dup_cauchy_upper_bound([ZZ_I(1), ZZ_I(1)], ZZ_I)) + + assert dup_cauchy_upper_bound([QQ(1), QQ(0), QQ(0)], QQ) == QQ.zero + assert dup_cauchy_upper_bound([QQ(1), QQ(0), QQ(-2)], QQ) == QQ(3) + + +def test_dup_cauchy_lower_bound(): + raises(PolynomialError, lambda: dup_cauchy_lower_bound([], QQ)) + raises(PolynomialError, lambda: dup_cauchy_lower_bound([QQ(1)], QQ)) + raises(PolynomialError, lambda: dup_cauchy_lower_bound([QQ(1), QQ(0), QQ(0)], QQ)) + raises(DomainError, lambda: dup_cauchy_lower_bound([ZZ_I(1), ZZ_I(1)], ZZ_I)) + + assert dup_cauchy_lower_bound([QQ(1), QQ(0), QQ(-2)], QQ) == QQ(2, 3) + + +def test_dup_mignotte_sep_bound_squared(): + raises(PolynomialError, lambda: dup_mignotte_sep_bound_squared([], QQ)) + raises(PolynomialError, lambda: dup_mignotte_sep_bound_squared([QQ(1)], QQ)) + + assert dup_mignotte_sep_bound_squared([QQ(1), QQ(0), QQ(-2)], QQ) == QQ(3, 5) + + +def test_dup_refine_real_root(): + R, x = ring("x", ZZ) + f = x**2 - 2 + + assert R.dup_refine_real_root(f, QQ(1), QQ(1), steps=1) == (QQ(1), QQ(1)) + assert R.dup_refine_real_root(f, QQ(1), QQ(1), steps=9) == (QQ(1), QQ(1)) + + raises(ValueError, lambda: R.dup_refine_real_root(f, QQ(-2), QQ(2))) + + s, t = QQ(1, 1), QQ(2, 1) + + assert R.dup_refine_real_root(f, s, t, steps=0) == (QQ(1, 1), QQ(2, 1)) + assert R.dup_refine_real_root(f, s, t, steps=1) == (QQ(1, 1), QQ(3, 2)) + assert R.dup_refine_real_root(f, s, t, steps=2) == (QQ(4, 3), QQ(3, 2)) + assert R.dup_refine_real_root(f, s, t, steps=3) == (QQ(7, 5), QQ(3, 2)) + assert R.dup_refine_real_root(f, s, t, steps=4) == (QQ(7, 5), QQ(10, 7)) + + s, t = QQ(1, 1), QQ(3, 2) + + assert R.dup_refine_real_root(f, s, t, steps=0) == (QQ(1, 1), QQ(3, 2)) + assert R.dup_refine_real_root(f, s, t, steps=1) == (QQ(4, 3), QQ(3, 2)) + assert R.dup_refine_real_root(f, s, t, steps=2) == (QQ(7, 5), QQ(3, 2)) + assert R.dup_refine_real_root(f, s, t, steps=3) == (QQ(7, 5), QQ(10, 7)) + assert R.dup_refine_real_root(f, s, t, steps=4) == (QQ(7, 5), QQ(17, 12)) + + s, t = QQ(1, 1), QQ(5, 3) + + assert R.dup_refine_real_root(f, s, t, steps=0) == (QQ(1, 1), QQ(5, 3)) + assert R.dup_refine_real_root(f, s, t, steps=1) == (QQ(1, 1), QQ(3, 2)) + assert R.dup_refine_real_root(f, s, t, steps=2) == (QQ(7, 5), QQ(3, 2)) + assert R.dup_refine_real_root(f, s, t, steps=3) == (QQ(7, 5), QQ(13, 9)) + assert R.dup_refine_real_root(f, s, t, steps=4) == (QQ(7, 5), QQ(27, 19)) + + s, t = QQ(-1, 1), QQ(-2, 1) + + assert R.dup_refine_real_root(f, s, t, steps=0) == (-QQ(2, 1), -QQ(1, 1)) + assert R.dup_refine_real_root(f, s, t, steps=1) == (-QQ(3, 2), -QQ(1, 1)) + assert R.dup_refine_real_root(f, s, t, steps=2) == (-QQ(3, 2), -QQ(4, 3)) + assert R.dup_refine_real_root(f, s, t, steps=3) == (-QQ(3, 2), -QQ(7, 5)) + assert R.dup_refine_real_root(f, s, t, steps=4) == (-QQ(10, 7), -QQ(7, 5)) + + raises(RefinementFailed, lambda: R.dup_refine_real_root(f, QQ(0), QQ(1))) + + s, t, u, v, w = QQ(1), QQ(2), QQ(24, 17), QQ(17, 12), QQ(7, 5) + + assert R.dup_refine_real_root(f, s, t, eps=QQ(1, 100)) == (u, v) + assert R.dup_refine_real_root(f, s, t, steps=6) == (u, v) + + assert R.dup_refine_real_root(f, s, t, eps=QQ(1, 100), steps=5) == (w, v) + assert R.dup_refine_real_root(f, s, t, eps=QQ(1, 100), steps=6) == (u, v) + assert R.dup_refine_real_root(f, s, t, eps=QQ(1, 100), steps=7) == (u, v) + + s, t, u, v = QQ(-2), QQ(-1), QQ(-3, 2), QQ(-4, 3) + + assert R.dup_refine_real_root(f, s, t, disjoint=QQ(-5)) == (s, t) + assert R.dup_refine_real_root(f, s, t, disjoint=-v) == (s, t) + assert R.dup_refine_real_root(f, s, t, disjoint=v) == (u, v) + + s, t, u, v = QQ(1), QQ(2), QQ(4, 3), QQ(3, 2) + + assert R.dup_refine_real_root(f, s, t, disjoint=QQ(5)) == (s, t) + assert R.dup_refine_real_root(f, s, t, disjoint=-u) == (s, t) + assert R.dup_refine_real_root(f, s, t, disjoint=u) == (u, v) + + +def test_dup_isolate_real_roots_sqf(): + R, x = ring("x", ZZ) + + assert R.dup_isolate_real_roots_sqf(0) == [] + assert R.dup_isolate_real_roots_sqf(5) == [] + + assert R.dup_isolate_real_roots_sqf(x**2 + x) == [(-1, -1), (0, 0)] + assert R.dup_isolate_real_roots_sqf(x**2 - x) == [( 0, 0), (1, 1)] + + assert R.dup_isolate_real_roots_sqf(x**4 + x + 1) == [] + + I = [(-2, -1), (1, 2)] + + assert R.dup_isolate_real_roots_sqf(x**2 - 2) == I + assert R.dup_isolate_real_roots_sqf(-x**2 + 2) == I + + assert R.dup_isolate_real_roots_sqf(x - 1) == \ + [(1, 1)] + assert R.dup_isolate_real_roots_sqf(x**2 - 3*x + 2) == \ + [(1, 1), (2, 2)] + assert R.dup_isolate_real_roots_sqf(x**3 - 6*x**2 + 11*x - 6) == \ + [(1, 1), (2, 2), (3, 3)] + assert R.dup_isolate_real_roots_sqf(x**4 - 10*x**3 + 35*x**2 - 50*x + 24) == \ + [(1, 1), (2, 2), (3, 3), (4, 4)] + assert R.dup_isolate_real_roots_sqf(x**5 - 15*x**4 + 85*x**3 - 225*x**2 + 274*x - 120) == \ + [(1, 1), (2, 2), (3, 3), (4, 4), (5, 5)] + + assert R.dup_isolate_real_roots_sqf(x - 10) == \ + [(10, 10)] + assert R.dup_isolate_real_roots_sqf(x**2 - 30*x + 200) == \ + [(10, 10), (20, 20)] + assert R.dup_isolate_real_roots_sqf(x**3 - 60*x**2 + 1100*x - 6000) == \ + [(10, 10), (20, 20), (30, 30)] + assert R.dup_isolate_real_roots_sqf(x**4 - 100*x**3 + 3500*x**2 - 50000*x + 240000) == \ + [(10, 10), (20, 20), (30, 30), (40, 40)] + assert R.dup_isolate_real_roots_sqf(x**5 - 150*x**4 + 8500*x**3 - 225000*x**2 + 2740000*x - 12000000) == \ + [(10, 10), (20, 20), (30, 30), (40, 40), (50, 50)] + + assert R.dup_isolate_real_roots_sqf(x + 1) == \ + [(-1, -1)] + assert R.dup_isolate_real_roots_sqf(x**2 + 3*x + 2) == \ + [(-2, -2), (-1, -1)] + assert R.dup_isolate_real_roots_sqf(x**3 + 6*x**2 + 11*x + 6) == \ + [(-3, -3), (-2, -2), (-1, -1)] + assert R.dup_isolate_real_roots_sqf(x**4 + 10*x**3 + 35*x**2 + 50*x + 24) == \ + [(-4, -4), (-3, -3), (-2, -2), (-1, -1)] + assert R.dup_isolate_real_roots_sqf(x**5 + 15*x**4 + 85*x**3 + 225*x**2 + 274*x + 120) == \ + [(-5, -5), (-4, -4), (-3, -3), (-2, -2), (-1, -1)] + + assert R.dup_isolate_real_roots_sqf(x + 10) == \ + [(-10, -10)] + assert R.dup_isolate_real_roots_sqf(x**2 + 30*x + 200) == \ + [(-20, -20), (-10, -10)] + assert R.dup_isolate_real_roots_sqf(x**3 + 60*x**2 + 1100*x + 6000) == \ + [(-30, -30), (-20, -20), (-10, -10)] + assert R.dup_isolate_real_roots_sqf(x**4 + 100*x**3 + 3500*x**2 + 50000*x + 240000) == \ + [(-40, -40), (-30, -30), (-20, -20), (-10, -10)] + assert R.dup_isolate_real_roots_sqf(x**5 + 150*x**4 + 8500*x**3 + 225000*x**2 + 2740000*x + 12000000) == \ + [(-50, -50), (-40, -40), (-30, -30), (-20, -20), (-10, -10)] + + assert R.dup_isolate_real_roots_sqf(x**2 - 5) == [(-3, -2), (2, 3)] + assert R.dup_isolate_real_roots_sqf(x**3 - 5) == [(1, 2)] + assert R.dup_isolate_real_roots_sqf(x**4 - 5) == [(-2, -1), (1, 2)] + assert R.dup_isolate_real_roots_sqf(x**5 - 5) == [(1, 2)] + assert R.dup_isolate_real_roots_sqf(x**6 - 5) == [(-2, -1), (1, 2)] + assert R.dup_isolate_real_roots_sqf(x**7 - 5) == [(1, 2)] + assert R.dup_isolate_real_roots_sqf(x**8 - 5) == [(-2, -1), (1, 2)] + assert R.dup_isolate_real_roots_sqf(x**9 - 5) == [(1, 2)] + + assert R.dup_isolate_real_roots_sqf(x**2 - 1) == \ + [(-1, -1), (1, 1)] + assert R.dup_isolate_real_roots_sqf(x**3 + 2*x**2 - x - 2) == \ + [(-2, -2), (-1, -1), (1, 1)] + assert R.dup_isolate_real_roots_sqf(x**4 - 5*x**2 + 4) == \ + [(-2, -2), (-1, -1), (1, 1), (2, 2)] + assert R.dup_isolate_real_roots_sqf(x**5 + 3*x**4 - 5*x**3 - 15*x**2 + 4*x + 12) == \ + [(-3, -3), (-2, -2), (-1, -1), (1, 1), (2, 2)] + assert R.dup_isolate_real_roots_sqf(x**6 - 14*x**4 + 49*x**2 - 36) == \ + [(-3, -3), (-2, -2), (-1, -1), (1, 1), (2, 2), (3, 3)] + assert R.dup_isolate_real_roots_sqf(2*x**7 + x**6 - 28*x**5 - 14*x**4 + 98*x**3 + 49*x**2 - 72*x - 36) == \ + [(-3, -3), (-2, -2), (-1, -1), (-1, 0), (1, 1), (2, 2), (3, 3)] + assert R.dup_isolate_real_roots_sqf(4*x**8 - 57*x**6 + 210*x**4 - 193*x**2 + 36) == \ + [(-3, -3), (-2, -2), (-1, -1), (-1, 0), (0, 1), (1, 1), (2, 2), (3, 3)] + + f = 9*x**2 - 2 + + assert R.dup_isolate_real_roots_sqf(f) == \ + [(-1, 0), (0, 1)] + + assert R.dup_isolate_real_roots_sqf(f, eps=QQ(1, 10)) == \ + [(QQ(-1, 2), QQ(-3, 7)), (QQ(3, 7), QQ(1, 2))] + assert R.dup_isolate_real_roots_sqf(f, eps=QQ(1, 100)) == \ + [(QQ(-9, 19), QQ(-8, 17)), (QQ(8, 17), QQ(9, 19))] + assert R.dup_isolate_real_roots_sqf(f, eps=QQ(1, 1000)) == \ + [(QQ(-33, 70), QQ(-8, 17)), (QQ(8, 17), QQ(33, 70))] + assert R.dup_isolate_real_roots_sqf(f, eps=QQ(1, 10000)) == \ + [(QQ(-33, 70), QQ(-107, 227)), (QQ(107, 227), QQ(33, 70))] + assert R.dup_isolate_real_roots_sqf(f, eps=QQ(1, 100000)) == \ + [(QQ(-305, 647), QQ(-272, 577)), (QQ(272, 577), QQ(305, 647))] + assert R.dup_isolate_real_roots_sqf(f, eps=QQ(1, 1000000)) == \ + [(QQ(-1121, 2378), QQ(-272, 577)), (QQ(272, 577), QQ(1121, 2378))] + + f = 200100012*x**5 - 700390052*x**4 + 700490079*x**3 - 200240054*x**2 + 40017*x - 2 + + assert R.dup_isolate_real_roots_sqf(f) == \ + [(QQ(0), QQ(1, 10002)), (QQ(1, 10002), QQ(1, 10002)), + (QQ(1, 2), QQ(1, 2)), (QQ(1), QQ(1)), (QQ(2), QQ(2))] + + assert R.dup_isolate_real_roots_sqf(f, eps=QQ(1, 100000)) == \ + [(QQ(1, 10003), QQ(1, 10003)), (QQ(1, 10002), QQ(1, 10002)), + (QQ(1, 2), QQ(1, 2)), (QQ(1), QQ(1)), (QQ(2), QQ(2))] + + a, b, c, d = 10000090000001, 2000100003, 10000300007, 10000005000008 + + f = 20001600074001600021*x**4 \ + + 1700135866278935491773999857*x**3 \ + - 2000179008931031182161141026995283662899200197*x**2 \ + - 800027600594323913802305066986600025*x \ + + 100000950000540000725000008 + + assert R.dup_isolate_real_roots_sqf(f) == \ + [(-a, -a), (-1, 0), (0, 1), (d, d)] + + assert R.dup_isolate_real_roots_sqf(f, eps=QQ(1, 100000000000)) == \ + [(-QQ(a), -QQ(a)), (-QQ(1, b), -QQ(1, b)), (QQ(1, c), QQ(1, c)), (QQ(d), QQ(d))] + + (u, v), B, C, (s, t) = R.dup_isolate_real_roots_sqf(f, fast=True) + + assert u < -a < v and B == (-QQ(1), QQ(0)) and C == (QQ(0), QQ(1)) and s < d < t + + assert R.dup_isolate_real_roots_sqf(f, fast=True, eps=QQ(1, 100000000000000000000000000000)) == \ + [(-QQ(a), -QQ(a)), (-QQ(1, b), -QQ(1, b)), (QQ(1, c), QQ(1, c)), (QQ(d), QQ(d))] + + f = -10*x**4 + 8*x**3 + 80*x**2 - 32*x - 160 + + assert R.dup_isolate_real_roots_sqf(f) == \ + [(-2, -2), (-2, -1), (2, 2), (2, 3)] + + assert R.dup_isolate_real_roots_sqf(f, eps=QQ(1, 100)) == \ + [(-QQ(2), -QQ(2)), (-QQ(23, 14), -QQ(18, 11)), (QQ(2), QQ(2)), (QQ(39, 16), QQ(22, 9))] + + f = x - 1 + + assert R.dup_isolate_real_roots_sqf(f, inf=2) == [] + assert R.dup_isolate_real_roots_sqf(f, sup=0) == [] + + assert R.dup_isolate_real_roots_sqf(f) == [(1, 1)] + assert R.dup_isolate_real_roots_sqf(f, inf=1) == [(1, 1)] + assert R.dup_isolate_real_roots_sqf(f, sup=1) == [(1, 1)] + assert R.dup_isolate_real_roots_sqf(f, inf=1, sup=1) == [(1, 1)] + + f = x**2 - 2 + + assert R.dup_isolate_real_roots_sqf(f, inf=QQ(7, 4)) == [] + assert R.dup_isolate_real_roots_sqf(f, inf=QQ(7, 5)) == [(QQ(7, 5), QQ(3, 2))] + assert R.dup_isolate_real_roots_sqf(f, sup=QQ(7, 5)) == [(-2, -1)] + assert R.dup_isolate_real_roots_sqf(f, sup=QQ(7, 4)) == [(-2, -1), (1, QQ(3, 2))] + assert R.dup_isolate_real_roots_sqf(f, sup=-QQ(7, 4)) == [] + assert R.dup_isolate_real_roots_sqf(f, sup=-QQ(7, 5)) == [(-QQ(3, 2), -QQ(7, 5))] + assert R.dup_isolate_real_roots_sqf(f, inf=-QQ(7, 5)) == [(1, 2)] + assert R.dup_isolate_real_roots_sqf(f, inf=-QQ(7, 4)) == [(-QQ(3, 2), -1), (1, 2)] + + I = [(-2, -1), (1, 2)] + + assert R.dup_isolate_real_roots_sqf(f, inf=-2) == I + assert R.dup_isolate_real_roots_sqf(f, sup=+2) == I + + assert R.dup_isolate_real_roots_sqf(f, inf=-2, sup=2) == I + + R, x = ring("x", QQ) + f = QQ(8, 5)*x**2 - QQ(87374, 3855)*x - QQ(17, 771) + + assert R.dup_isolate_real_roots_sqf(f) == [(-1, 0), (14, 15)] + + R, x = ring("x", EX) + raises(DomainError, lambda: R.dup_isolate_real_roots_sqf(x + 3)) + +def test_dup_isolate_real_roots(): + R, x = ring("x", ZZ) + + assert R.dup_isolate_real_roots(0) == [] + assert R.dup_isolate_real_roots(3) == [] + + assert R.dup_isolate_real_roots(5*x) == [((0, 0), 1)] + assert R.dup_isolate_real_roots(7*x**4) == [((0, 0), 4)] + + assert R.dup_isolate_real_roots(x**2 + x) == [((-1, -1), 1), ((0, 0), 1)] + assert R.dup_isolate_real_roots(x**2 - x) == [((0, 0), 1), ((1, 1), 1)] + + assert R.dup_isolate_real_roots(x**4 + x + 1) == [] + + I = [((-2, -1), 1), ((1, 2), 1)] + + assert R.dup_isolate_real_roots(x**2 - 2) == I + assert R.dup_isolate_real_roots(-x**2 + 2) == I + + f = 16*x**14 - 96*x**13 + 24*x**12 + 936*x**11 - 1599*x**10 - 2880*x**9 + 9196*x**8 \ + + 552*x**7 - 21831*x**6 + 13968*x**5 + 21690*x**4 - 26784*x**3 - 2916*x**2 + 15552*x - 5832 + g = R.dup_sqf_part(f) + + assert R.dup_isolate_real_roots(f) == \ + [((-QQ(2), -QQ(3, 2)), 2), ((-QQ(3, 2), -QQ(1, 1)), 3), ((QQ(1), QQ(3, 2)), 3), + ((QQ(3, 2), QQ(3, 2)), 4), ((QQ(5, 3), QQ(2)), 2)] + + assert R.dup_isolate_real_roots_sqf(g) == \ + [(-QQ(2), -QQ(3, 2)), (-QQ(3, 2), -QQ(1, 1)), (QQ(1), QQ(3, 2)), + (QQ(3, 2), QQ(3, 2)), (QQ(3, 2), QQ(2))] + assert R.dup_isolate_real_roots(g) == \ + [((-QQ(2), -QQ(3, 2)), 1), ((-QQ(3, 2), -QQ(1, 1)), 1), ((QQ(1), QQ(3, 2)), 1), + ((QQ(3, 2), QQ(3, 2)), 1), ((QQ(3, 2), QQ(2)), 1)] + + f = x - 1 + + assert R.dup_isolate_real_roots(f, inf=2) == [] + assert R.dup_isolate_real_roots(f, sup=0) == [] + + assert R.dup_isolate_real_roots(f) == [((1, 1), 1)] + assert R.dup_isolate_real_roots(f, inf=1) == [((1, 1), 1)] + assert R.dup_isolate_real_roots(f, sup=1) == [((1, 1), 1)] + assert R.dup_isolate_real_roots(f, inf=1, sup=1) == [((1, 1), 1)] + + f = x**4 - 4*x**2 + 4 + + assert R.dup_isolate_real_roots(f, inf=QQ(7, 4)) == [] + assert R.dup_isolate_real_roots(f, inf=QQ(7, 5)) == [((QQ(7, 5), QQ(3, 2)), 2)] + assert R.dup_isolate_real_roots(f, sup=QQ(7, 5)) == [((-2, -1), 2)] + assert R.dup_isolate_real_roots(f, sup=QQ(7, 4)) == [((-2, -1), 2), ((1, QQ(3, 2)), 2)] + assert R.dup_isolate_real_roots(f, sup=-QQ(7, 4)) == [] + assert R.dup_isolate_real_roots(f, sup=-QQ(7, 5)) == [((-QQ(3, 2), -QQ(7, 5)), 2)] + assert R.dup_isolate_real_roots(f, inf=-QQ(7, 5)) == [((1, 2), 2)] + assert R.dup_isolate_real_roots(f, inf=-QQ(7, 4)) == [((-QQ(3, 2), -1), 2), ((1, 2), 2)] + + I = [((-2, -1), 2), ((1, 2), 2)] + + assert R.dup_isolate_real_roots(f, inf=-2) == I + assert R.dup_isolate_real_roots(f, sup=+2) == I + + assert R.dup_isolate_real_roots(f, inf=-2, sup=2) == I + + f = x**11 - 3*x**10 - x**9 + 11*x**8 - 8*x**7 - 8*x**6 + 12*x**5 - 4*x**4 + + assert R.dup_isolate_real_roots(f, basis=False) == \ + [((-2, -1), 2), ((0, 0), 4), ((1, 1), 3), ((1, 2), 2)] + assert R.dup_isolate_real_roots(f, basis=True) == \ + [((-2, -1), 2, [1, 0, -2]), ((0, 0), 4, [1, 0]), ((1, 1), 3, [1, -1]), ((1, 2), 2, [1, 0, -2])] + + f = (x**45 - 45*x**44 + 990*x**43 - 1) + g = (x**46 - 15180*x**43 + 9366819*x**40 - 53524680*x**39 + 260932815*x**38 - 1101716330*x**37 + 4076350421*x**36 - 13340783196*x**35 + 38910617655*x**34 - 101766230790*x**33 + 239877544005*x**32 - 511738760544*x**31 + 991493848554*x**30 - 1749695026860*x**29 + 2818953098830*x**28 - 4154246671960*x**27 + 5608233007146*x**26 - 6943526580276*x**25 + 7890371113950*x**24 - 8233430727600*x**23 + 7890371113950*x**22 - 6943526580276*x**21 + 5608233007146*x**20 - 4154246671960*x**19 + 2818953098830*x**18 - 1749695026860*x**17 + 991493848554*x**16 - 511738760544*x**15 + 239877544005*x**14 - 101766230790*x**13 + 38910617655*x**12 - 13340783196*x**11 + 4076350421*x**10 - 1101716330*x**9 + 260932815*x**8 - 53524680*x**7 + 9366819*x**6 - 1370754*x**5 + 163185*x**4 - 15180*x**3 + 1035*x**2 - 47*x + 1) + + assert R.dup_isolate_real_roots(f*g) == \ + [((0, QQ(1, 2)), 1), ((QQ(2, 3), QQ(3, 4)), 1), ((QQ(3, 4), 1), 1), ((6, 7), 1), ((24, 25), 1)] + + R, x = ring("x", EX) + raises(DomainError, lambda: R.dup_isolate_real_roots(x + 3)) + + +def test_dup_isolate_real_roots_list(): + R, x = ring("x", ZZ) + + assert R.dup_isolate_real_roots_list([x**2 + x, x]) == \ + [((-1, -1), {0: 1}), ((0, 0), {0: 1, 1: 1})] + assert R.dup_isolate_real_roots_list([x**2 - x, x]) == \ + [((0, 0), {0: 1, 1: 1}), ((1, 1), {0: 1})] + + assert R.dup_isolate_real_roots_list([x + 1, x + 2, x - 1, x + 1, x - 1, x - 1]) == \ + [((-QQ(2), -QQ(2)), {1: 1}), ((-QQ(1), -QQ(1)), {0: 1, 3: 1}), ((QQ(1), QQ(1)), {2: 1, 4: 1, 5: 1})] + + assert R.dup_isolate_real_roots_list([x + 1, x + 2, x - 1, x + 1, x - 1, x + 2]) == \ + [((-QQ(2), -QQ(2)), {1: 1, 5: 1}), ((-QQ(1), -QQ(1)), {0: 1, 3: 1}), ((QQ(1), QQ(1)), {2: 1, 4: 1})] + + f, g = x**4 - 4*x**2 + 4, x - 1 + + assert R.dup_isolate_real_roots_list([f, g], inf=QQ(7, 4)) == [] + assert R.dup_isolate_real_roots_list([f, g], inf=QQ(7, 5)) == \ + [((QQ(7, 5), QQ(3, 2)), {0: 2})] + assert R.dup_isolate_real_roots_list([f, g], sup=QQ(7, 5)) == \ + [((-2, -1), {0: 2}), ((1, 1), {1: 1})] + assert R.dup_isolate_real_roots_list([f, g], sup=QQ(7, 4)) == \ + [((-2, -1), {0: 2}), ((1, 1), {1: 1}), ((1, QQ(3, 2)), {0: 2})] + assert R.dup_isolate_real_roots_list([f, g], sup=-QQ(7, 4)) == [] + assert R.dup_isolate_real_roots_list([f, g], sup=-QQ(7, 5)) == \ + [((-QQ(3, 2), -QQ(7, 5)), {0: 2})] + assert R.dup_isolate_real_roots_list([f, g], inf=-QQ(7, 5)) == \ + [((1, 1), {1: 1}), ((1, 2), {0: 2})] + assert R.dup_isolate_real_roots_list([f, g], inf=-QQ(7, 4)) == \ + [((-QQ(3, 2), -1), {0: 2}), ((1, 1), {1: 1}), ((1, 2), {0: 2})] + + f, g = 2*x**2 - 1, x**2 - 2 + + assert R.dup_isolate_real_roots_list([f, g]) == \ + [((-QQ(2), -QQ(1)), {1: 1}), ((-QQ(1), QQ(0)), {0: 1}), + ((QQ(0), QQ(1)), {0: 1}), ((QQ(1), QQ(2)), {1: 1})] + assert R.dup_isolate_real_roots_list([f, g], strict=True) == \ + [((-QQ(3, 2), -QQ(4, 3)), {1: 1}), ((-QQ(1), -QQ(2, 3)), {0: 1}), + ((QQ(2, 3), QQ(1)), {0: 1}), ((QQ(4, 3), QQ(3, 2)), {1: 1})] + + f, g = x**2 - 2, x**3 - x**2 - 2*x + 2 + + assert R.dup_isolate_real_roots_list([f, g]) == \ + [((-QQ(2), -QQ(1)), {1: 1, 0: 1}), ((QQ(1), QQ(1)), {1: 1}), ((QQ(1), QQ(2)), {1: 1, 0: 1})] + + f, g = x**3 - 2*x, x**5 - x**4 - 2*x**3 + 2*x**2 + + assert R.dup_isolate_real_roots_list([f, g]) == \ + [((-QQ(2), -QQ(1)), {1: 1, 0: 1}), ((QQ(0), QQ(0)), {0: 1, 1: 2}), + ((QQ(1), QQ(1)), {1: 1}), ((QQ(1), QQ(2)), {1: 1, 0: 1})] + + f, g = x**9 - 3*x**8 - x**7 + 11*x**6 - 8*x**5 - 8*x**4 + 12*x**3 - 4*x**2, x**5 - 2*x**4 + 3*x**3 - 4*x**2 + 2*x + + assert R.dup_isolate_real_roots_list([f, g], basis=False) == \ + [((-2, -1), {0: 2}), ((0, 0), {0: 2, 1: 1}), ((1, 1), {0: 3, 1: 2}), ((1, 2), {0: 2})] + assert R.dup_isolate_real_roots_list([f, g], basis=True) == \ + [((-2, -1), {0: 2}, [1, 0, -2]), ((0, 0), {0: 2, 1: 1}, [1, 0]), + ((1, 1), {0: 3, 1: 2}, [1, -1]), ((1, 2), {0: 2}, [1, 0, -2])] + + R, x = ring("x", EX) + raises(DomainError, lambda: R.dup_isolate_real_roots_list([x + 3])) + + +def test_dup_isolate_real_roots_list_QQ(): + R, x = ring("x", ZZ) + + f = x**5 - 200 + g = x**5 - 201 + + assert R.dup_isolate_real_roots_list([f, g]) == \ + [((QQ(75, 26), QQ(101, 35)), {0: 1}), ((QQ(309, 107), QQ(26, 9)), {1: 1})] + + R, x = ring("x", QQ) + + f = -QQ(1, 200)*x**5 + 1 + g = -QQ(1, 201)*x**5 + 1 + + assert R.dup_isolate_real_roots_list([f, g]) == \ + [((QQ(75, 26), QQ(101, 35)), {0: 1}), ((QQ(309, 107), QQ(26, 9)), {1: 1})] + + +def test_dup_count_real_roots(): + R, x = ring("x", ZZ) + + assert R.dup_count_real_roots(0) == 0 + assert R.dup_count_real_roots(7) == 0 + + f = x - 1 + assert R.dup_count_real_roots(f) == 1 + assert R.dup_count_real_roots(f, inf=1) == 1 + assert R.dup_count_real_roots(f, sup=0) == 0 + assert R.dup_count_real_roots(f, sup=1) == 1 + assert R.dup_count_real_roots(f, inf=0, sup=1) == 1 + assert R.dup_count_real_roots(f, inf=0, sup=2) == 1 + assert R.dup_count_real_roots(f, inf=1, sup=2) == 1 + + f = x**2 - 2 + assert R.dup_count_real_roots(f) == 2 + assert R.dup_count_real_roots(f, sup=0) == 1 + assert R.dup_count_real_roots(f, inf=-1, sup=1) == 0 + + +# parameters for test_dup_count_complex_roots_n(): n = 1..8 +a, b = (-QQ(1), -QQ(1)), (QQ(1), QQ(1)) +c, d = ( QQ(0), QQ(0)), (QQ(1), QQ(1)) + +def test_dup_count_complex_roots_1(): + R, x = ring("x", ZZ) + + # z-1 + f = x - 1 + assert R.dup_count_complex_roots(f, a, b) == 1 + assert R.dup_count_complex_roots(f, c, d) == 1 + + # z+1 + f = x + 1 + assert R.dup_count_complex_roots(f, a, b) == 1 + assert R.dup_count_complex_roots(f, c, d) == 0 + + +def test_dup_count_complex_roots_2(): + R, x = ring("x", ZZ) + + # (z-1)*(z) + f = x**2 - x + assert R.dup_count_complex_roots(f, a, b) == 2 + assert R.dup_count_complex_roots(f, c, d) == 2 + + # (z-1)*(-z) + f = -x**2 + x + assert R.dup_count_complex_roots(f, a, b) == 2 + assert R.dup_count_complex_roots(f, c, d) == 2 + + # (z+1)*(z) + f = x**2 + x + assert R.dup_count_complex_roots(f, a, b) == 2 + assert R.dup_count_complex_roots(f, c, d) == 1 + + # (z+1)*(-z) + f = -x**2 - x + assert R.dup_count_complex_roots(f, a, b) == 2 + assert R.dup_count_complex_roots(f, c, d) == 1 + + +def test_dup_count_complex_roots_3(): + R, x = ring("x", ZZ) + + # (z-1)*(z+1) + f = x**2 - 1 + assert R.dup_count_complex_roots(f, a, b) == 2 + assert R.dup_count_complex_roots(f, c, d) == 1 + + # (z-1)*(z+1)*(z) + f = x**3 - x + assert R.dup_count_complex_roots(f, a, b) == 3 + assert R.dup_count_complex_roots(f, c, d) == 2 + + # (z-1)*(z+1)*(-z) + f = -x**3 + x + assert R.dup_count_complex_roots(f, a, b) == 3 + assert R.dup_count_complex_roots(f, c, d) == 2 + + +def test_dup_count_complex_roots_4(): + R, x = ring("x", ZZ) + + # (z-I)*(z+I) + f = x**2 + 1 + assert R.dup_count_complex_roots(f, a, b) == 2 + assert R.dup_count_complex_roots(f, c, d) == 1 + + # (z-I)*(z+I)*(z) + f = x**3 + x + assert R.dup_count_complex_roots(f, a, b) == 3 + assert R.dup_count_complex_roots(f, c, d) == 2 + + # (z-I)*(z+I)*(-z) + f = -x**3 - x + assert R.dup_count_complex_roots(f, a, b) == 3 + assert R.dup_count_complex_roots(f, c, d) == 2 + + # (z-I)*(z+I)*(z-1) + f = x**3 - x**2 + x - 1 + assert R.dup_count_complex_roots(f, a, b) == 3 + assert R.dup_count_complex_roots(f, c, d) == 2 + + # (z-I)*(z+I)*(z-1)*(z) + f = x**4 - x**3 + x**2 - x + assert R.dup_count_complex_roots(f, a, b) == 4 + assert R.dup_count_complex_roots(f, c, d) == 3 + + # (z-I)*(z+I)*(z-1)*(-z) + f = -x**4 + x**3 - x**2 + x + assert R.dup_count_complex_roots(f, a, b) == 4 + assert R.dup_count_complex_roots(f, c, d) == 3 + + # (z-I)*(z+I)*(z-1)*(z+1) + f = x**4 - 1 + assert R.dup_count_complex_roots(f, a, b) == 4 + assert R.dup_count_complex_roots(f, c, d) == 2 + + # (z-I)*(z+I)*(z-1)*(z+1)*(z) + f = x**5 - x + assert R.dup_count_complex_roots(f, a, b) == 5 + assert R.dup_count_complex_roots(f, c, d) == 3 + + # (z-I)*(z+I)*(z-1)*(z+1)*(-z) + f = -x**5 + x + assert R.dup_count_complex_roots(f, a, b) == 5 + assert R.dup_count_complex_roots(f, c, d) == 3 + + +def test_dup_count_complex_roots_5(): + R, x = ring("x", ZZ) + + # (z-I+1)*(z+I+1) + f = x**2 + 2*x + 2 + assert R.dup_count_complex_roots(f, a, b) == 2 + assert R.dup_count_complex_roots(f, c, d) == 0 + + # (z-I+1)*(z+I+1)*(z-1) + f = x**3 + x**2 - 2 + assert R.dup_count_complex_roots(f, a, b) == 3 + assert R.dup_count_complex_roots(f, c, d) == 1 + + # (z-I+1)*(z+I+1)*(z-1)*z + f = x**4 + x**3 - 2*x + assert R.dup_count_complex_roots(f, a, b) == 4 + assert R.dup_count_complex_roots(f, c, d) == 2 + + # (z-I+1)*(z+I+1)*(z+1) + f = x**3 + 3*x**2 + 4*x + 2 + assert R.dup_count_complex_roots(f, a, b) == 3 + assert R.dup_count_complex_roots(f, c, d) == 0 + + # (z-I+1)*(z+I+1)*(z+1)*z + f = x**4 + 3*x**3 + 4*x**2 + 2*x + assert R.dup_count_complex_roots(f, a, b) == 4 + assert R.dup_count_complex_roots(f, c, d) == 1 + + # (z-I+1)*(z+I+1)*(z-1)*(z+1) + f = x**4 + 2*x**3 + x**2 - 2*x - 2 + assert R.dup_count_complex_roots(f, a, b) == 4 + assert R.dup_count_complex_roots(f, c, d) == 1 + + # (z-I+1)*(z+I+1)*(z-1)*(z+1)*z + f = x**5 + 2*x**4 + x**3 - 2*x**2 - 2*x + assert R.dup_count_complex_roots(f, a, b) == 5 + assert R.dup_count_complex_roots(f, c, d) == 2 + + +def test_dup_count_complex_roots_6(): + R, x = ring("x", ZZ) + + # (z-I-1)*(z+I-1) + f = x**2 - 2*x + 2 + assert R.dup_count_complex_roots(f, a, b) == 2 + assert R.dup_count_complex_roots(f, c, d) == 1 + + # (z-I-1)*(z+I-1)*(z-1) + f = x**3 - 3*x**2 + 4*x - 2 + assert R.dup_count_complex_roots(f, a, b) == 3 + assert R.dup_count_complex_roots(f, c, d) == 2 + + # (z-I-1)*(z+I-1)*(z-1)*z + f = x**4 - 3*x**3 + 4*x**2 - 2*x + assert R.dup_count_complex_roots(f, a, b) == 4 + assert R.dup_count_complex_roots(f, c, d) == 3 + + # (z-I-1)*(z+I-1)*(z+1) + f = x**3 - x**2 + 2 + assert R.dup_count_complex_roots(f, a, b) == 3 + assert R.dup_count_complex_roots(f, c, d) == 1 + + # (z-I-1)*(z+I-1)*(z+1)*z + f = x**4 - x**3 + 2*x + assert R.dup_count_complex_roots(f, a, b) == 4 + assert R.dup_count_complex_roots(f, c, d) == 2 + + # (z-I-1)*(z+I-1)*(z-1)*(z+1) + f = x**4 - 2*x**3 + x**2 + 2*x - 2 + assert R.dup_count_complex_roots(f, a, b) == 4 + assert R.dup_count_complex_roots(f, c, d) == 2 + + # (z-I-1)*(z+I-1)*(z-1)*(z+1)*z + f = x**5 - 2*x**4 + x**3 + 2*x**2 - 2*x + assert R.dup_count_complex_roots(f, a, b) == 5 + assert R.dup_count_complex_roots(f, c, d) == 3 + + +def test_dup_count_complex_roots_7(): + R, x = ring("x", ZZ) + + # (z-I-1)*(z+I-1)*(z-I+1)*(z+I+1) + f = x**4 + 4 + assert R.dup_count_complex_roots(f, a, b) == 4 + assert R.dup_count_complex_roots(f, c, d) == 1 + + # (z-I-1)*(z+I-1)*(z-I+1)*(z+I+1)*(z-2) + f = x**5 - 2*x**4 + 4*x - 8 + assert R.dup_count_complex_roots(f, a, b) == 4 + assert R.dup_count_complex_roots(f, c, d) == 1 + + # (z-I-1)*(z+I-1)*(z-I+1)*(z+I+1)*(z**2-2) + f = x**6 - 2*x**4 + 4*x**2 - 8 + assert R.dup_count_complex_roots(f, a, b) == 4 + assert R.dup_count_complex_roots(f, c, d) == 1 + + # (z-I-1)*(z+I-1)*(z-I+1)*(z+I+1)*(z-1) + f = x**5 - x**4 + 4*x - 4 + assert R.dup_count_complex_roots(f, a, b) == 5 + assert R.dup_count_complex_roots(f, c, d) == 2 + + # (z-I-1)*(z+I-1)*(z-I+1)*(z+I+1)*(z-1)*z + f = x**6 - x**5 + 4*x**2 - 4*x + assert R.dup_count_complex_roots(f, a, b) == 6 + assert R.dup_count_complex_roots(f, c, d) == 3 + + # (z-I-1)*(z+I-1)*(z-I+1)*(z+I+1)*(z+1) + f = x**5 + x**4 + 4*x + 4 + assert R.dup_count_complex_roots(f, a, b) == 5 + assert R.dup_count_complex_roots(f, c, d) == 1 + + # (z-I-1)*(z+I-1)*(z-I+1)*(z+I+1)*(z+1)*z + f = x**6 + x**5 + 4*x**2 + 4*x + assert R.dup_count_complex_roots(f, a, b) == 6 + assert R.dup_count_complex_roots(f, c, d) == 2 + + # (z-I-1)*(z+I-1)*(z-I+1)*(z+I+1)*(z-1)*(z+1) + f = x**6 - x**4 + 4*x**2 - 4 + assert R.dup_count_complex_roots(f, a, b) == 6 + assert R.dup_count_complex_roots(f, c, d) == 2 + + # (z-I-1)*(z+I-1)*(z-I+1)*(z+I+1)*(z-1)*(z+1)*z + f = x**7 - x**5 + 4*x**3 - 4*x + assert R.dup_count_complex_roots(f, a, b) == 7 + assert R.dup_count_complex_roots(f, c, d) == 3 + + # (z-I-1)*(z+I-1)*(z-I+1)*(z+I+1)*(z-1)*(z+1)*(z-I)*(z+I) + f = x**8 + 3*x**4 - 4 + assert R.dup_count_complex_roots(f, a, b) == 8 + assert R.dup_count_complex_roots(f, c, d) == 3 + + +def test_dup_count_complex_roots_8(): + R, x = ring("x", ZZ) + + # (z-I-1)*(z+I-1)*(z-I+1)*(z+I+1)*(z-1)*(z+1)*(z-I)*(z+I)*z + f = x**9 + 3*x**5 - 4*x + assert R.dup_count_complex_roots(f, a, b) == 9 + assert R.dup_count_complex_roots(f, c, d) == 4 + + # (z-I-1)*(z+I-1)*(z-I+1)*(z+I+1)*(z-1)*(z+1)*(z-I)*(z+I)*(z**2-2)*z + f = x**11 - 2*x**9 + 3*x**7 - 6*x**5 - 4*x**3 + 8*x + assert R.dup_count_complex_roots(f, a, b) == 9 + assert R.dup_count_complex_roots(f, c, d) == 4 + + +def test_dup_count_complex_roots_implicit(): + R, x = ring("x", ZZ) + + # z*(z-1)*(z+1)*(z-I)*(z+I) + f = x**5 - x + + assert R.dup_count_complex_roots(f) == 5 + + assert R.dup_count_complex_roots(f, sup=(0, 0)) == 3 + assert R.dup_count_complex_roots(f, inf=(0, 0)) == 3 + + +def test_dup_count_complex_roots_exclude(): + R, x = ring("x", ZZ) + + # z*(z-1)*(z+1)*(z-I)*(z+I) + f = x**5 - x + + a, b = (-QQ(1), QQ(0)), (QQ(1), QQ(1)) + + assert R.dup_count_complex_roots(f, a, b) == 4 + + assert R.dup_count_complex_roots(f, a, b, exclude=['S']) == 3 + assert R.dup_count_complex_roots(f, a, b, exclude=['N']) == 3 + + assert R.dup_count_complex_roots(f, a, b, exclude=['S', 'N']) == 2 + + assert R.dup_count_complex_roots(f, a, b, exclude=['E']) == 4 + assert R.dup_count_complex_roots(f, a, b, exclude=['W']) == 4 + + assert R.dup_count_complex_roots(f, a, b, exclude=['E', 'W']) == 4 + + assert R.dup_count_complex_roots(f, a, b, exclude=['N', 'S', 'E', 'W']) == 2 + + assert R.dup_count_complex_roots(f, a, b, exclude=['SW']) == 3 + assert R.dup_count_complex_roots(f, a, b, exclude=['SE']) == 3 + + assert R.dup_count_complex_roots(f, a, b, exclude=['SW', 'SE']) == 2 + assert R.dup_count_complex_roots(f, a, b, exclude=['SW', 'SE', 'S']) == 1 + assert R.dup_count_complex_roots(f, a, b, exclude=['SW', 'SE', 'S', 'N']) == 0 + + a, b = (QQ(0), QQ(0)), (QQ(1), QQ(1)) + + assert R.dup_count_complex_roots(f, a, b, exclude=True) == 1 + + +def test_dup_isolate_complex_roots_sqf(): + R, x = ring("x", ZZ) + f = x**2 - 2*x + 3 + + assert R.dup_isolate_complex_roots_sqf(f) == \ + [((0, -6), (6, 0)), ((0, 0), (6, 6))] + assert [ r.as_tuple() for r in R.dup_isolate_complex_roots_sqf(f, blackbox=True) ] == \ + [((0, -6), (6, 0)), ((0, 0), (6, 6))] + + assert R.dup_isolate_complex_roots_sqf(f, eps=QQ(1, 10)) == \ + [((QQ(15, 16), -QQ(3, 2)), (QQ(33, 32), -QQ(45, 32))), + ((QQ(15, 16), QQ(45, 32)), (QQ(33, 32), QQ(3, 2)))] + assert R.dup_isolate_complex_roots_sqf(f, eps=QQ(1, 100)) == \ + [((QQ(255, 256), -QQ(363, 256)), (QQ(513, 512), -QQ(723, 512))), + ((QQ(255, 256), QQ(723, 512)), (QQ(513, 512), QQ(363, 256)))] + + f = 7*x**4 - 19*x**3 + 20*x**2 + 17*x + 20 + + assert R.dup_isolate_complex_roots_sqf(f) == \ + [((-QQ(40, 7), -QQ(40, 7)), (0, 0)), ((-QQ(40, 7), 0), (0, QQ(40, 7))), + ((0, -QQ(40, 7)), (QQ(40, 7), 0)), ((0, 0), (QQ(40, 7), QQ(40, 7)))] + + +def test_dup_isolate_all_roots_sqf(): + R, x = ring("x", ZZ) + f = 4*x**4 - x**3 + 2*x**2 + 5*x + + assert R.dup_isolate_all_roots_sqf(f) == \ + ([(-1, 0), (0, 0)], + [((0, -QQ(5, 2)), (QQ(5, 2), 0)), ((0, 0), (QQ(5, 2), QQ(5, 2)))]) + + assert R.dup_isolate_all_roots_sqf(f, eps=QQ(1, 10)) == \ + ([(QQ(-7, 8), QQ(-6, 7)), (0, 0)], + [((QQ(35, 64), -QQ(35, 32)), (QQ(5, 8), -QQ(65, 64))), ((QQ(35, 64), QQ(65, 64)), (QQ(5, 8), QQ(35, 32)))]) + + +def test_dup_isolate_all_roots(): + R, x = ring("x", ZZ) + f = 4*x**4 - x**3 + 2*x**2 + 5*x + + assert R.dup_isolate_all_roots(f) == \ + ([((-1, 0), 1), ((0, 0), 1)], + [(((0, -QQ(5, 2)), (QQ(5, 2), 0)), 1), + (((0, 0), (QQ(5, 2), QQ(5, 2))), 1)]) + + assert R.dup_isolate_all_roots(f, eps=QQ(1, 10)) == \ + ([((QQ(-7, 8), QQ(-6, 7)), 1), ((0, 0), 1)], + [(((QQ(35, 64), -QQ(35, 32)), (QQ(5, 8), -QQ(65, 64))), 1), + (((QQ(35, 64), QQ(65, 64)), (QQ(5, 8), QQ(35, 32))), 1)]) + + f = x**5 + x**4 - 2*x**3 - 2*x**2 + x + 1 + raises(NotImplementedError, lambda: R.dup_isolate_all_roots(f)) diff --git a/lib/python3.12/site-packages/sympy/polys/tests/test_rootoftools.py b/lib/python3.12/site-packages/sympy/polys/tests/test_rootoftools.py new file mode 100644 index 0000000000000000000000000000000000000000..e7a1ae0cb2b034fbeb013560669189a03eacf0f3 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/polys/tests/test_rootoftools.py @@ -0,0 +1,653 @@ +"""Tests for the implementation of RootOf class and related tools. """ + +from sympy.polys.polytools import Poly +import sympy.polys.rootoftools as rootoftools +from sympy.polys.rootoftools import (rootof, RootOf, CRootOf, RootSum, + _pure_key_dict as D) + +from sympy.polys.polyerrors import ( + MultivariatePolynomialError, + GeneratorsNeeded, + PolynomialError, +) + +from sympy.core.function import (Function, Lambda) +from sympy.core.numbers import (Float, I, Rational) +from sympy.core.relational import Eq +from sympy.core.singleton import S +from sympy.functions.elementary.exponential import (exp, log) +from sympy.functions.elementary.miscellaneous import sqrt +from sympy.functions.elementary.trigonometric import tan +from sympy.integrals.integrals import Integral +from sympy.polys.orthopolys import legendre_poly +from sympy.solvers.solvers import solve + + +from sympy.testing.pytest import raises, slow +from sympy.core.expr import unchanged + +from sympy.abc import a, b, x, y, z, r + + +def test_CRootOf___new__(): + assert rootof(x, 0) == 0 + assert rootof(x, -1) == 0 + + assert rootof(x, S.Zero) == 0 + + assert rootof(x - 1, 0) == 1 + assert rootof(x - 1, -1) == 1 + + assert rootof(x + 1, 0) == -1 + assert rootof(x + 1, -1) == -1 + + assert rootof(x**2 + 2*x + 3, 0) == -1 - I*sqrt(2) + assert rootof(x**2 + 2*x + 3, 1) == -1 + I*sqrt(2) + assert rootof(x**2 + 2*x + 3, -1) == -1 + I*sqrt(2) + assert rootof(x**2 + 2*x + 3, -2) == -1 - I*sqrt(2) + + r = rootof(x**2 + 2*x + 3, 0, radicals=False) + assert isinstance(r, RootOf) is True + + r = rootof(x**2 + 2*x + 3, 1, radicals=False) + assert isinstance(r, RootOf) is True + + r = rootof(x**2 + 2*x + 3, -1, radicals=False) + assert isinstance(r, RootOf) is True + + r = rootof(x**2 + 2*x + 3, -2, radicals=False) + assert isinstance(r, RootOf) is True + + assert rootof((x - 1)*(x + 1), 0, radicals=False) == -1 + assert rootof((x - 1)*(x + 1), 1, radicals=False) == 1 + assert rootof((x - 1)*(x + 1), -1, radicals=False) == 1 + assert rootof((x - 1)*(x + 1), -2, radicals=False) == -1 + + assert rootof((x - 1)*(x + 1), 0, radicals=True) == -1 + assert rootof((x - 1)*(x + 1), 1, radicals=True) == 1 + assert rootof((x - 1)*(x + 1), -1, radicals=True) == 1 + assert rootof((x - 1)*(x + 1), -2, radicals=True) == -1 + + assert rootof((x - 1)*(x**3 + x + 3), 0) == rootof(x**3 + x + 3, 0) + assert rootof((x - 1)*(x**3 + x + 3), 1) == 1 + assert rootof((x - 1)*(x**3 + x + 3), 2) == rootof(x**3 + x + 3, 1) + assert rootof((x - 1)*(x**3 + x + 3), 3) == rootof(x**3 + x + 3, 2) + assert rootof((x - 1)*(x**3 + x + 3), -1) == rootof(x**3 + x + 3, 2) + assert rootof((x - 1)*(x**3 + x + 3), -2) == rootof(x**3 + x + 3, 1) + assert rootof((x - 1)*(x**3 + x + 3), -3) == 1 + assert rootof((x - 1)*(x**3 + x + 3), -4) == rootof(x**3 + x + 3, 0) + + assert rootof(x**4 + 3*x**3, 0) == -3 + assert rootof(x**4 + 3*x**3, 1) == 0 + assert rootof(x**4 + 3*x**3, 2) == 0 + assert rootof(x**4 + 3*x**3, 3) == 0 + + raises(GeneratorsNeeded, lambda: rootof(0, 0)) + raises(GeneratorsNeeded, lambda: rootof(1, 0)) + + raises(PolynomialError, lambda: rootof(Poly(0, x), 0)) + raises(PolynomialError, lambda: rootof(Poly(1, x), 0)) + raises(PolynomialError, lambda: rootof(x - y, 0)) + # issue 8617 + raises(PolynomialError, lambda: rootof(exp(x), 0)) + + raises(NotImplementedError, lambda: rootof(x**3 - x + sqrt(2), 0)) + raises(NotImplementedError, lambda: rootof(x**3 - x + I, 0)) + + raises(IndexError, lambda: rootof(x**2 - 1, -4)) + raises(IndexError, lambda: rootof(x**2 - 1, -3)) + raises(IndexError, lambda: rootof(x**2 - 1, 2)) + raises(IndexError, lambda: rootof(x**2 - 1, 3)) + raises(ValueError, lambda: rootof(x**2 - 1, x)) + + assert rootof(Poly(x - y, x), 0) == y + + assert rootof(Poly(x**2 - y, x), 0) == -sqrt(y) + assert rootof(Poly(x**2 - y, x), 1) == sqrt(y) + + assert rootof(Poly(x**3 - y, x), 0) == y**Rational(1, 3) + + assert rootof(y*x**3 + y*x + 2*y, x, 0) == -1 + raises(NotImplementedError, lambda: rootof(x**3 + x + 2*y, x, 0)) + + assert rootof(x**3 + x + 1, 0).is_commutative is True + + +def test_CRootOf_attributes(): + r = rootof(x**3 + x + 3, 0) + assert r.is_number + assert r.free_symbols == set() + # if the following assertion fails then multivariate polynomials + # are apparently supported and the RootOf.free_symbols routine + # should be changed to return whatever symbols would not be + # the PurePoly dummy symbol + raises(NotImplementedError, lambda: rootof(Poly(x**3 + y*x + 1, x), 0)) + + +def test_CRootOf___eq__(): + assert (rootof(x**3 + x + 3, 0) == rootof(x**3 + x + 3, 0)) is True + assert (rootof(x**3 + x + 3, 0) == rootof(x**3 + x + 3, 1)) is False + assert (rootof(x**3 + x + 3, 1) == rootof(x**3 + x + 3, 1)) is True + assert (rootof(x**3 + x + 3, 1) == rootof(x**3 + x + 3, 2)) is False + assert (rootof(x**3 + x + 3, 2) == rootof(x**3 + x + 3, 2)) is True + + assert (rootof(x**3 + x + 3, 0) == rootof(y**3 + y + 3, 0)) is True + assert (rootof(x**3 + x + 3, 0) == rootof(y**3 + y + 3, 1)) is False + assert (rootof(x**3 + x + 3, 1) == rootof(y**3 + y + 3, 1)) is True + assert (rootof(x**3 + x + 3, 1) == rootof(y**3 + y + 3, 2)) is False + assert (rootof(x**3 + x + 3, 2) == rootof(y**3 + y + 3, 2)) is True + + +def test_CRootOf___eval_Eq__(): + f = Function('f') + eq = x**3 + x + 3 + r = rootof(eq, 2) + r1 = rootof(eq, 1) + assert Eq(r, r1) is S.false + assert Eq(r, r) is S.true + assert unchanged(Eq, r, x) + assert Eq(r, 0) is S.false + assert Eq(r, S.Infinity) is S.false + assert Eq(r, I) is S.false + assert unchanged(Eq, r, f(0)) + sol = solve(eq) + for s in sol: + if s.is_real: + assert Eq(r, s) is S.false + r = rootof(eq, 0) + for s in sol: + if s.is_real: + assert Eq(r, s) is S.true + eq = x**3 + x + 1 + sol = solve(eq) + assert [Eq(rootof(eq, i), j) for i in range(3) for j in sol + ].count(True) == 3 + assert Eq(rootof(eq, 0), 1 + S.ImaginaryUnit) == False + + +def test_CRootOf_is_real(): + assert rootof(x**3 + x + 3, 0).is_real is True + assert rootof(x**3 + x + 3, 1).is_real is False + assert rootof(x**3 + x + 3, 2).is_real is False + + +def test_CRootOf_is_complex(): + assert rootof(x**3 + x + 3, 0).is_complex is True + + +def test_CRootOf_subs(): + assert rootof(x**3 + x + 1, 0).subs(x, y) == rootof(y**3 + y + 1, 0) + + +def test_CRootOf_diff(): + assert rootof(x**3 + x + 1, 0).diff(x) == 0 + assert rootof(x**3 + x + 1, 0).diff(y) == 0 + + +@slow +def test_CRootOf_evalf(): + real = rootof(x**3 + x + 3, 0).evalf(n=20) + + assert real.epsilon_eq(Float("-1.2134116627622296341")) + + re, im = rootof(x**3 + x + 3, 1).evalf(n=20).as_real_imag() + + assert re.epsilon_eq( Float("0.60670583138111481707")) + assert im.epsilon_eq(-Float("1.45061224918844152650")) + + re, im = rootof(x**3 + x + 3, 2).evalf(n=20).as_real_imag() + + assert re.epsilon_eq(Float("0.60670583138111481707")) + assert im.epsilon_eq(Float("1.45061224918844152650")) + + p = legendre_poly(4, x, polys=True) + roots = [str(r.n(17)) for r in p.real_roots()] + # magnitudes are given by + # sqrt(3/S(7) - 2*sqrt(6/S(5))/7) + # and + # sqrt(3/S(7) + 2*sqrt(6/S(5))/7) + assert roots == [ + "-0.86113631159405258", + "-0.33998104358485626", + "0.33998104358485626", + "0.86113631159405258", + ] + + re = rootof(x**5 - 5*x + 12, 0).evalf(n=20) + assert re.epsilon_eq(Float("-1.84208596619025438271")) + + re, im = rootof(x**5 - 5*x + 12, 1).evalf(n=20).as_real_imag() + assert re.epsilon_eq(Float("-0.351854240827371999559")) + assert im.epsilon_eq(Float("-1.709561043370328882010")) + + re, im = rootof(x**5 - 5*x + 12, 2).evalf(n=20).as_real_imag() + assert re.epsilon_eq(Float("-0.351854240827371999559")) + assert im.epsilon_eq(Float("+1.709561043370328882010")) + + re, im = rootof(x**5 - 5*x + 12, 3).evalf(n=20).as_real_imag() + assert re.epsilon_eq(Float("+1.272897223922499190910")) + assert im.epsilon_eq(Float("-0.719798681483861386681")) + + re, im = rootof(x**5 - 5*x + 12, 4).evalf(n=20).as_real_imag() + assert re.epsilon_eq(Float("+1.272897223922499190910")) + assert im.epsilon_eq(Float("+0.719798681483861386681")) + + # issue 6393 + assert str(rootof(x**5 + 2*x**4 + x**3 - 68719476736, 0).n(3)) == '147.' + eq = (531441*x**11 + 3857868*x**10 + 13730229*x**9 + 32597882*x**8 + + 55077472*x**7 + 60452000*x**6 + 32172064*x**5 - 4383808*x**4 - + 11942912*x**3 - 1506304*x**2 + 1453312*x + 512) + a, b = rootof(eq, 1).n(2).as_real_imag() + c, d = rootof(eq, 2).n(2).as_real_imag() + assert a == c + assert b < d + assert b == -d + # issue 6451 + r = rootof(legendre_poly(64, x), 7) + assert r.n(2) == r.n(100).n(2) + # issue 9019 + r0 = rootof(x**2 + 1, 0, radicals=False) + r1 = rootof(x**2 + 1, 1, radicals=False) + assert r0.n(4) == Float(-1.0, 4) * I + assert r1.n(4) == Float(1.0, 4) * I + + # make sure verification is used in case a max/min traps the "root" + assert str(rootof(4*x**5 + 16*x**3 + 12*x**2 + 7, 0).n(3)) == '-0.976' + + # watch out for UnboundLocalError + c = CRootOf(90720*x**6 - 4032*x**4 + 84*x**2 - 1, 0) + assert c._eval_evalf(2) # doesn't fail + + # watch out for imaginary parts that don't want to evaluate + assert str(RootOf(x**16 + 32*x**14 + 508*x**12 + 5440*x**10 + + 39510*x**8 + 204320*x**6 + 755548*x**4 + 1434496*x**2 + + 877969, 10).n(2)) == '-3.4*I' + assert abs(RootOf(x**4 + 10*x**2 + 1, 0).n(2)) < 0.4 + + # check reset and args + r = [RootOf(x**3 + x + 3, i) for i in range(3)] + r[0]._reset() + for ri in r: + i = ri._get_interval() + ri.n(2) + assert i != ri._get_interval() + ri._reset() + assert i == ri._get_interval() + assert i == i.func(*i.args) + + +def test_issue_24978(): + # Irreducible poly with negative leading coeff is normalized + # (factor of -1 is extracted), before being stored as CRootOf.poly. + f = -x**2 + 2 + r = CRootOf(f, 0) + assert r.poly.as_expr() == x**2 - 2 + # An action that prompts calculation of an interval puts r.poly in + # the cache. + r.n() + assert r.poly in rootoftools._reals_cache + + +def test_CRootOf_evalf_caching_bug(): + r = rootof(x**5 - 5*x + 12, 1) + r.n() + a = r._get_interval() + r = rootof(x**5 - 5*x + 12, 1) + r.n() + b = r._get_interval() + assert a == b + + +def test_CRootOf_real_roots(): + assert Poly(x**5 + x + 1).real_roots() == [rootof(x**3 - x**2 + 1, 0)] + assert Poly(x**5 + x + 1).real_roots(radicals=False) == [rootof( + x**3 - x**2 + 1, 0)] + + # https://github.com/sympy/sympy/issues/20902 + p = Poly(-3*x**4 - 10*x**3 - 12*x**2 - 6*x - 1, x, domain='ZZ') + assert CRootOf.real_roots(p) == [S(-1), S(-1), S(-1), S(-1)/3] + + +def test_CRootOf_all_roots(): + assert Poly(x**5 + x + 1).all_roots() == [ + rootof(x**3 - x**2 + 1, 0), + Rational(-1, 2) - sqrt(3)*I/2, + Rational(-1, 2) + sqrt(3)*I/2, + rootof(x**3 - x**2 + 1, 1), + rootof(x**3 - x**2 + 1, 2), + ] + + assert Poly(x**5 + x + 1).all_roots(radicals=False) == [ + rootof(x**3 - x**2 + 1, 0), + rootof(x**2 + x + 1, 0, radicals=False), + rootof(x**2 + x + 1, 1, radicals=False), + rootof(x**3 - x**2 + 1, 1), + rootof(x**3 - x**2 + 1, 2), + ] + + +def test_CRootOf_eval_rational(): + p = legendre_poly(4, x, polys=True) + roots = [r.eval_rational(n=18) for r in p.real_roots()] + for root in roots: + assert isinstance(root, Rational) + roots = [str(root.n(17)) for root in roots] + assert roots == [ + "-0.86113631159405258", + "-0.33998104358485626", + "0.33998104358485626", + "0.86113631159405258", + ] + + +def test_CRootOf_lazy(): + # irreducible poly with both real and complex roots: + f = Poly(x**3 + 2*x + 2) + + # real root: + CRootOf.clear_cache() + r = CRootOf(f, 0) + # Not yet in cache, after construction: + assert r.poly not in rootoftools._reals_cache + assert r.poly not in rootoftools._complexes_cache + r.evalf() + # In cache after evaluation: + assert r.poly in rootoftools._reals_cache + assert r.poly not in rootoftools._complexes_cache + + # complex root: + CRootOf.clear_cache() + r = CRootOf(f, 1) + # Not yet in cache, after construction: + assert r.poly not in rootoftools._reals_cache + assert r.poly not in rootoftools._complexes_cache + r.evalf() + # In cache after evaluation: + assert r.poly in rootoftools._reals_cache + assert r.poly in rootoftools._complexes_cache + + # composite poly with both real and complex roots: + f = Poly((x**2 - 2)*(x**2 + 1)) + + # real root: + CRootOf.clear_cache() + r = CRootOf(f, 0) + # In cache immediately after construction: + assert r.poly in rootoftools._reals_cache + assert r.poly not in rootoftools._complexes_cache + + # complex root: + CRootOf.clear_cache() + r = CRootOf(f, 2) + # In cache immediately after construction: + assert r.poly in rootoftools._reals_cache + assert r.poly in rootoftools._complexes_cache + + +def test_RootSum___new__(): + f = x**3 + x + 3 + + g = Lambda(r, log(r*x)) + s = RootSum(f, g) + + assert isinstance(s, RootSum) is True + + assert RootSum(f**2, g) == 2*RootSum(f, g) + assert RootSum((x - 7)*f**3, g) == log(7*x) + 3*RootSum(f, g) + + # issue 5571 + assert hash(RootSum((x - 7)*f**3, g)) == hash(log(7*x) + 3*RootSum(f, g)) + + raises(MultivariatePolynomialError, lambda: RootSum(x**3 + x + y)) + raises(ValueError, lambda: RootSum(x**2 + 3, lambda x: x)) + + assert RootSum(f, exp) == RootSum(f, Lambda(x, exp(x))) + assert RootSum(f, log) == RootSum(f, Lambda(x, log(x))) + + assert isinstance(RootSum(f, auto=False), RootSum) is True + + assert RootSum(f) == 0 + assert RootSum(f, Lambda(x, x)) == 0 + assert RootSum(f, Lambda(x, x**2)) == -2 + + assert RootSum(f, Lambda(x, 1)) == 3 + assert RootSum(f, Lambda(x, 2)) == 6 + + assert RootSum(f, auto=False).is_commutative is True + + assert RootSum(f, Lambda(x, 1/(x + x**2))) == Rational(11, 3) + assert RootSum(f, Lambda(x, y/(x + x**2))) == Rational(11, 3)*y + + assert RootSum(x**2 - 1, Lambda(x, 3*x**2), x) == 6 + assert RootSum(x**2 - y, Lambda(x, 3*x**2), x) == 6*y + + assert RootSum(x**2 - 1, Lambda(x, z*x**2), x) == 2*z + assert RootSum(x**2 - y, Lambda(x, z*x**2), x) == 2*z*y + + assert RootSum( + x**2 - 1, Lambda(x, exp(x)), quadratic=True) == exp(-1) + exp(1) + + assert RootSum(x**3 + a*x + a**3, tan, x) == \ + RootSum(x**3 + x + 1, Lambda(x, tan(a*x))) + assert RootSum(a**3*x**3 + a*x + 1, tan, x) == \ + RootSum(x**3 + x + 1, Lambda(x, tan(x/a))) + + +def test_RootSum_free_symbols(): + assert RootSum(x**3 + x + 3, Lambda(r, exp(r))).free_symbols == set() + assert RootSum(x**3 + x + 3, Lambda(r, exp(a*r))).free_symbols == {a} + assert RootSum( + x**3 + x + y, Lambda(r, exp(a*r)), x).free_symbols == {a, y} + + +def test_RootSum___eq__(): + f = Lambda(x, exp(x)) + + assert (RootSum(x**3 + x + 1, f) == RootSum(x**3 + x + 1, f)) is True + assert (RootSum(x**3 + x + 1, f) == RootSum(y**3 + y + 1, f)) is True + + assert (RootSum(x**3 + x + 1, f) == RootSum(x**3 + x + 2, f)) is False + assert (RootSum(x**3 + x + 1, f) == RootSum(y**3 + y + 2, f)) is False + + +def test_RootSum_doit(): + rs = RootSum(x**2 + 1, exp) + + assert isinstance(rs, RootSum) is True + assert rs.doit() == exp(-I) + exp(I) + + rs = RootSum(x**2 + a, exp, x) + + assert isinstance(rs, RootSum) is True + assert rs.doit() == exp(-sqrt(-a)) + exp(sqrt(-a)) + + +def test_RootSum_evalf(): + rs = RootSum(x**2 + 1, exp) + + assert rs.evalf(n=20, chop=True).epsilon_eq(Float("1.0806046117362794348")) + assert rs.evalf(n=15, chop=True).epsilon_eq(Float("1.08060461173628")) + + rs = RootSum(x**2 + a, exp, x) + + assert rs.evalf() == rs + + +def test_RootSum_diff(): + f = x**3 + x + 3 + + g = Lambda(r, exp(r*x)) + h = Lambda(r, r*exp(r*x)) + + assert RootSum(f, g).diff(x) == RootSum(f, h) + + +def test_RootSum_subs(): + f = x**3 + x + 3 + g = Lambda(r, exp(r*x)) + + F = y**3 + y + 3 + G = Lambda(r, exp(r*y)) + + assert RootSum(f, g).subs(y, 1) == RootSum(f, g) + assert RootSum(f, g).subs(x, y) == RootSum(F, G) + + +def test_RootSum_rational(): + assert RootSum( + z**5 - z + 1, Lambda(z, z/(x - z))) == (4*x - 5)/(x**5 - x + 1) + + f = 161*z**3 + 115*z**2 + 19*z + 1 + g = Lambda(z, z*log( + -3381*z**4/4 - 3381*z**3/4 - 625*z**2/2 - z*Rational(125, 2) - 5 + exp(x))) + + assert RootSum(f, g).diff(x) == -( + (5*exp(2*x) - 6*exp(x) + 4)*exp(x)/(exp(3*x) - exp(2*x) + 1))/7 + + +def test_RootSum_independent(): + f = (x**3 - a)**2*(x**4 - b)**3 + + g = Lambda(x, 5*tan(x) + 7) + h = Lambda(x, tan(x)) + + r0 = RootSum(x**3 - a, h, x) + r1 = RootSum(x**4 - b, h, x) + + assert RootSum(f, g, x).as_ordered_terms() == [10*r0, 15*r1, 126] + + +def test_issue_7876(): + l1 = Poly(x**6 - x + 1, x).all_roots() + l2 = [rootof(x**6 - x + 1, i) for i in range(6)] + assert frozenset(l1) == frozenset(l2) + + +def test_issue_8316(): + f = Poly(7*x**8 - 9) + assert len(f.all_roots()) == 8 + f = Poly(7*x**8 - 10) + assert len(f.all_roots()) == 8 + + +def test__imag_count(): + from sympy.polys.rootoftools import _imag_count_of_factor + def imag_count(p): + return sum(_imag_count_of_factor(f)*m for f, m in + p.factor_list()[1]) + assert imag_count(Poly(x**6 + 10*x**2 + 1)) == 2 + assert imag_count(Poly(x**2)) == 0 + assert imag_count(Poly([1]*3 + [-1], x)) == 0 + assert imag_count(Poly(x**3 + 1)) == 0 + assert imag_count(Poly(x**2 + 1)) == 2 + assert imag_count(Poly(x**2 - 1)) == 0 + assert imag_count(Poly(x**4 - 1)) == 2 + assert imag_count(Poly(x**4 + 1)) == 0 + assert imag_count(Poly([1, 2, 3], x)) == 0 + assert imag_count(Poly(x**3 + x + 1)) == 0 + assert imag_count(Poly(x**4 + x + 1)) == 0 + def q(r1, r2, p): + return Poly(((x - r1)*(x - r2)).subs(x, x**p), x) + assert imag_count(q(-1, -2, 2)) == 4 + assert imag_count(q(-1, 2, 2)) == 2 + assert imag_count(q(1, 2, 2)) == 0 + assert imag_count(q(1, 2, 4)) == 4 + assert imag_count(q(-1, 2, 4)) == 2 + assert imag_count(q(-1, -2, 4)) == 0 + + +def test_RootOf_is_imaginary(): + r = RootOf(x**4 + 4*x**2 + 1, 1) + i = r._get_interval() + assert r.is_imaginary and i.ax*i.bx <= 0 + + +def test_is_disjoint(): + eq = x**3 + 5*x + 1 + ir = rootof(eq, 0)._get_interval() + ii = rootof(eq, 1)._get_interval() + assert ir.is_disjoint(ii) + assert ii.is_disjoint(ir) + + +def test_pure_key_dict(): + p = D() + assert (x in p) is False + assert (1 in p) is False + p[x] = 1 + assert x in p + assert y in p + assert p[y] == 1 + raises(KeyError, lambda: p[1]) + def dont(k): + p[k] = 2 + raises(ValueError, lambda: dont(1)) + + +@slow +def test_eval_approx_relative(): + CRootOf.clear_cache() + t = [CRootOf(x**3 + 10*x + 1, i) for i in range(3)] + assert [i.eval_rational(1e-1) for i in t] == [ + Rational(-21, 220), Rational(15, 256) - I*805/256, + Rational(15, 256) + I*805/256] + t[0]._reset() + assert [i.eval_rational(1e-1, 1e-4) for i in t] == [ + Rational(-21, 220), Rational(3275, 65536) - I*414645/131072, + Rational(3275, 65536) + I*414645/131072] + assert S(t[0]._get_interval().dx) < 1e-1 + assert S(t[1]._get_interval().dx) < 1e-1 + assert S(t[1]._get_interval().dy) < 1e-4 + assert S(t[2]._get_interval().dx) < 1e-1 + assert S(t[2]._get_interval().dy) < 1e-4 + t[0]._reset() + assert [i.eval_rational(1e-4, 1e-4) for i in t] == [ + Rational(-2001, 20020), Rational(6545, 131072) - I*414645/131072, + Rational(6545, 131072) + I*414645/131072] + assert S(t[0]._get_interval().dx) < 1e-4 + assert S(t[1]._get_interval().dx) < 1e-4 + assert S(t[1]._get_interval().dy) < 1e-4 + assert S(t[2]._get_interval().dx) < 1e-4 + assert S(t[2]._get_interval().dy) < 1e-4 + # in the following, the actual relative precision is + # less than tested, but it should never be greater + t[0]._reset() + assert [i.eval_rational(n=2) for i in t] == [ + Rational(-202201, 2024022), Rational(104755, 2097152) - I*6634255/2097152, + Rational(104755, 2097152) + I*6634255/2097152] + assert abs(S(t[0]._get_interval().dx)/t[0]) < 1e-2 + assert abs(S(t[1]._get_interval().dx)/t[1]).n() < 1e-2 + assert abs(S(t[1]._get_interval().dy)/t[1]).n() < 1e-2 + assert abs(S(t[2]._get_interval().dx)/t[2]).n() < 1e-2 + assert abs(S(t[2]._get_interval().dy)/t[2]).n() < 1e-2 + t[0]._reset() + assert [i.eval_rational(n=3) for i in t] == [ + Rational(-202201, 2024022), Rational(1676045, 33554432) - I*106148135/33554432, + Rational(1676045, 33554432) + I*106148135/33554432] + assert abs(S(t[0]._get_interval().dx)/t[0]) < 1e-3 + assert abs(S(t[1]._get_interval().dx)/t[1]).n() < 1e-3 + assert abs(S(t[1]._get_interval().dy)/t[1]).n() < 1e-3 + assert abs(S(t[2]._get_interval().dx)/t[2]).n() < 1e-3 + assert abs(S(t[2]._get_interval().dy)/t[2]).n() < 1e-3 + + t[0]._reset() + a = [i.eval_approx(2) for i in t] + assert [str(i) for i in a] == [ + '-0.10', '0.05 - 3.2*I', '0.05 + 3.2*I'] + assert all(abs(((a[i] - t[i])/t[i]).n()) < 1e-2 for i in range(len(a))) + + +def test_issue_15920(): + r = rootof(x**5 - x + 1, 0) + p = Integral(x, (x, 1, y)) + assert unchanged(Eq, r, p) + + +def test_issue_19113(): + eq = y**3 - y + 1 + # generator is a canonical x in RootOf + assert str(Poly(eq).real_roots()) == '[CRootOf(x**3 - x + 1, 0)]' + assert str(Poly(eq.subs(y, tan(y))).real_roots() + ) == '[CRootOf(x**3 - x + 1, 0)]' + assert str(Poly(eq.subs(y, tan(x))).real_roots() + ) == '[CRootOf(x**3 - x + 1, 0)]' diff --git a/lib/python3.12/site-packages/sympy/polys/tests/test_solvers.py b/lib/python3.12/site-packages/sympy/polys/tests/test_solvers.py new file mode 100644 index 0000000000000000000000000000000000000000..9b7c2b3c9f74f9626e2d1aa973fccb3011e4d808 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/polys/tests/test_solvers.py @@ -0,0 +1,112 @@ +"""Tests for low-level linear systems solver. """ + +from sympy.matrices import Matrix +from sympy.polys.domains import ZZ, QQ +from sympy.polys.fields import field +from sympy.polys.rings import ring +from sympy.polys.solvers import solve_lin_sys, eqs_to_matrix + + +def test_solve_lin_sys_2x2_one(): + domain, x1,x2 = ring("x1,x2", QQ) + eqs = [x1 + x2 - 5, + 2*x1 - x2] + sol = {x1: QQ(5, 3), x2: QQ(10, 3)} + _sol = solve_lin_sys(eqs, domain) + assert _sol == sol and all(isinstance(s, domain.dtype) for s in _sol) + +def test_solve_lin_sys_2x4_none(): + domain, x1,x2 = ring("x1,x2", QQ) + eqs = [x1 - 1, + x1 - x2, + x1 - 2*x2, + x2 - 1] + assert solve_lin_sys(eqs, domain) is None + + +def test_solve_lin_sys_3x4_one(): + domain, x1,x2,x3 = ring("x1,x2,x3", QQ) + eqs = [x1 + 2*x2 + 3*x3, + 2*x1 - x2 + x3, + 3*x1 + x2 + x3, + 5*x2 + 2*x3] + sol = {x1: 0, x2: 0, x3: 0} + assert solve_lin_sys(eqs, domain) == sol + +def test_solve_lin_sys_3x3_inf(): + domain, x1,x2,x3 = ring("x1,x2,x3", QQ) + eqs = [x1 - x2 + 2*x3 - 1, + 2*x1 + x2 + x3 - 8, + x1 + x2 - 5] + sol = {x1: -x3 + 3, x2: x3 + 2} + assert solve_lin_sys(eqs, domain) == sol + +def test_solve_lin_sys_3x4_none(): + domain, x1,x2,x3,x4 = ring("x1,x2,x3,x4", QQ) + eqs = [2*x1 + x2 + 7*x3 - 7*x4 - 2, + -3*x1 + 4*x2 - 5*x3 - 6*x4 - 3, + x1 + x2 + 4*x3 - 5*x4 - 2] + assert solve_lin_sys(eqs, domain) is None + + +def test_solve_lin_sys_4x7_inf(): + domain, x1,x2,x3,x4,x5,x6,x7 = ring("x1,x2,x3,x4,x5,x6,x7", QQ) + eqs = [x1 + 4*x2 - x4 + 7*x6 - 9*x7 - 3, + 2*x1 + 8*x2 - x3 + 3*x4 + 9*x5 - 13*x6 + 7*x7 - 9, + 2*x3 - 3*x4 - 4*x5 + 12*x6 - 8*x7 - 1, + -x1 - 4*x2 + 2*x3 + 4*x4 + 8*x5 - 31*x6 + 37*x7 - 4] + sol = {x1: 4 - 4*x2 - 2*x5 - x6 + 3*x7, + x3: 2 - x5 + 3*x6 - 5*x7, + x4: 1 - 2*x5 + 6*x6 - 6*x7} + assert solve_lin_sys(eqs, domain) == sol + +def test_solve_lin_sys_5x5_inf(): + domain, x1,x2,x3,x4,x5 = ring("x1,x2,x3,x4,x5", QQ) + eqs = [x1 - x2 - 2*x3 + x4 + 11*x5 - 13, + x1 - x2 + x3 + x4 + 5*x5 - 16, + 2*x1 - 2*x2 + x4 + 10*x5 - 21, + 2*x1 - 2*x2 - x3 + 3*x4 + 20*x5 - 38, + 2*x1 - 2*x2 + x3 + x4 + 8*x5 - 22] + sol = {x1: 6 + x2 - 3*x5, + x3: 1 + 2*x5, + x4: 9 - 4*x5} + assert solve_lin_sys(eqs, domain) == sol + +def test_solve_lin_sys_6x6_1(): + ground, d,r,e,g,i,j,l,o,m,p,q = field("d,r,e,g,i,j,l,o,m,p,q", ZZ) + domain, c,f,h,k,n,b = ring("c,f,h,k,n,b", ground) + + eqs = [b + q/d - c/d, c*(1/d + 1/e + 1/g) - f/g - q/d, f*(1/g + 1/i + 1/j) - c/g - h/i, h*(1/i + 1/l + 1/m) - f/i - k/m, k*(1/m + 1/o + 1/p) - h/m - n/p, n/p - k/p] + sol = { + b: (e*i*l*q + e*i*m*q + e*i*o*q + e*j*l*q + e*j*m*q + e*j*o*q + e*l*m*q + e*l*o*q + g*i*l*q + g*i*m*q + g*i*o*q + g*j*l*q + g*j*m*q + g*j*o*q + g*l*m*q + g*l*o*q + i*j*l*q + i*j*m*q + i*j*o*q + j*l*m*q + j*l*o*q)/(-d*e*i*l - d*e*i*m - d*e*i*o - d*e*j*l - d*e*j*m - d*e*j*o - d*e*l*m - d*e*l*o - d*g*i*l - d*g*i*m - d*g*i*o - d*g*j*l - d*g*j*m - d*g*j*o - d*g*l*m - d*g*l*o - d*i*j*l - d*i*j*m - d*i*j*o - d*j*l*m - d*j*l*o - e*g*i*l - e*g*i*m - e*g*i*o - e*g*j*l - e*g*j*m - e*g*j*o - e*g*l*m - e*g*l*o - e*i*j*l - e*i*j*m - e*i*j*o - e*j*l*m - e*j*l*o), + c: (-e*g*i*l*q - e*g*i*m*q - e*g*i*o*q - e*g*j*l*q - e*g*j*m*q - e*g*j*o*q - e*g*l*m*q - e*g*l*o*q - e*i*j*l*q - e*i*j*m*q - e*i*j*o*q - e*j*l*m*q - e*j*l*o*q)/(-d*e*i*l - d*e*i*m - d*e*i*o - d*e*j*l - d*e*j*m - d*e*j*o - d*e*l*m - d*e*l*o - d*g*i*l - d*g*i*m - d*g*i*o - d*g*j*l - d*g*j*m - d*g*j*o - d*g*l*m - d*g*l*o - d*i*j*l - d*i*j*m - d*i*j*o - d*j*l*m - d*j*l*o - e*g*i*l - e*g*i*m - e*g*i*o - e*g*j*l - e*g*j*m - e*g*j*o - e*g*l*m - e*g*l*o - e*i*j*l - e*i*j*m - e*i*j*o - e*j*l*m - e*j*l*o), + f: (-e*i*j*l*q - e*i*j*m*q - e*i*j*o*q - e*j*l*m*q - e*j*l*o*q)/(-d*e*i*l - d*e*i*m - d*e*i*o - d*e*j*l - d*e*j*m - d*e*j*o - d*e*l*m - d*e*l*o - d*g*i*l - d*g*i*m - d*g*i*o - d*g*j*l - d*g*j*m - d*g*j*o - d*g*l*m - d*g*l*o - d*i*j*l - d*i*j*m - d*i*j*o - d*j*l*m - d*j*l*o - e*g*i*l - e*g*i*m - e*g*i*o - e*g*j*l - e*g*j*m - e*g*j*o - e*g*l*m - e*g*l*o - e*i*j*l - e*i*j*m - e*i*j*o - e*j*l*m - e*j*l*o), + h: (-e*j*l*m*q - e*j*l*o*q)/(-d*e*i*l - d*e*i*m - d*e*i*o - d*e*j*l - d*e*j*m - d*e*j*o - d*e*l*m - d*e*l*o - d*g*i*l - d*g*i*m - d*g*i*o - d*g*j*l - d*g*j*m - d*g*j*o - d*g*l*m - d*g*l*o - d*i*j*l - d*i*j*m - d*i*j*o - d*j*l*m - d*j*l*o - e*g*i*l - e*g*i*m - e*g*i*o - e*g*j*l - e*g*j*m - e*g*j*o - e*g*l*m - e*g*l*o - e*i*j*l - e*i*j*m - e*i*j*o - e*j*l*m - e*j*l*o), + k: e*j*l*o*q/(d*e*i*l + d*e*i*m + d*e*i*o + d*e*j*l + d*e*j*m + d*e*j*o + d*e*l*m + d*e*l*o + d*g*i*l + d*g*i*m + d*g*i*o + d*g*j*l + d*g*j*m + d*g*j*o + d*g*l*m + d*g*l*o + d*i*j*l + d*i*j*m + d*i*j*o + d*j*l*m + d*j*l*o + e*g*i*l + e*g*i*m + e*g*i*o + e*g*j*l + e*g*j*m + e*g*j*o + e*g*l*m + e*g*l*o + e*i*j*l + e*i*j*m + e*i*j*o + e*j*l*m + e*j*l*o), + n: e*j*l*o*q/(d*e*i*l + d*e*i*m + d*e*i*o + d*e*j*l + d*e*j*m + d*e*j*o + d*e*l*m + d*e*l*o + d*g*i*l + d*g*i*m + d*g*i*o + d*g*j*l + d*g*j*m + d*g*j*o + d*g*l*m + d*g*l*o + d*i*j*l + d*i*j*m + d*i*j*o + d*j*l*m + d*j*l*o + e*g*i*l + e*g*i*m + e*g*i*o + e*g*j*l + e*g*j*m + e*g*j*o + e*g*l*m + e*g*l*o + e*i*j*l + e*i*j*m + e*i*j*o + e*j*l*m + e*j*l*o), + } + + assert solve_lin_sys(eqs, domain) == sol + +def test_solve_lin_sys_6x6_2(): + ground, d,r,e,g,i,j,l,o,m,p,q = field("d,r,e,g,i,j,l,o,m,p,q", ZZ) + domain, c,f,h,k,n,b = ring("c,f,h,k,n,b", ground) + + eqs = [b + r/d - c/d, c*(1/d + 1/e + 1/g) - f/g - r/d, f*(1/g + 1/i + 1/j) - c/g - h/i, h*(1/i + 1/l + 1/m) - f/i - k/m, k*(1/m + 1/o + 1/p) - h/m - n/p, n*(1/p + 1/q) - k/p] + sol = { + b: -((l*q*e*o + l*q*g*o + i*m*q*e + i*l*q*e + i*l*p*e + i*j*o*q + j*e*o*q + g*j*o*q + i*e*o*q + g*i*o*q + e*l*o*p + e*l*m*p + e*l*m*o + e*i*o*p + e*i*m*p + e*i*m*o + e*i*l*o + j*e*o*p + j*e*m*q + j*e*m*p + j*e*m*o + j*l*m*q + j*l*m*p + j*l*m*o + i*j*m*p + i*j*m*o + i*j*l*q + i*j*l*o + i*j*m*q + j*l*o*p + j*e*l*o + g*j*o*p + g*j*m*q + g*j*m*p + i*j*l*p + i*j*o*p + j*e*l*q + j*e*l*p + j*l*o*q + g*j*m*o + g*j*l*q + g*j*l*p + g*j*l*o + g*l*o*p + g*l*m*p + g*l*m*o + g*i*m*o + g*i*o*p + g*i*m*q + g*i*m*p + g*i*l*q + g*i*l*p + g*i*l*o + l*m*q*e + l*m*q*g)*r)/(l*q*d*e*o + l*q*d*g*o + l*q*e*g*o + i*j*d*o*q + i*j*e*o*q + j*d*e*o*q + g*j*d*o*q + g*j*e*o*q + g*i*e*o*q + i*d*e*o*q + g*i*d*o*q + g*i*d*o*p + g*i*d*m*q + g*i*d*m*p + g*i*d*m*o + g*i*d*l*q + g*i*d*l*p + g*i*d*l*o + g*e*l*m*p + g*e*l*o*p + g*j*e*l*q + g*e*l*m*o + g*j*e*m*p + g*j*e*m*o + d*e*l*m*p + d*e*l*m*o + i*d*e*m*p + g*j*e*l*p + g*j*e*l*o + d*e*l*o*p + i*j*d*l*o + i*j*e*o*p + i*j*e*m*q + i*j*d*m*q + i*j*d*m*p + i*j*d*m*o + i*j*d*l*q + i*j*d*l*p + i*j*e*m*p + i*j*e*m*o + i*j*e*l*q + i*j*e*l*p + i*j*e*l*o + i*d*e*m*q + i*d*e*m*o + i*d*e*l*q + i*d*e*l*p + j*d*l*o*p + j*d*e*l*o + g*j*d*o*p + g*j*d*m*q + g*j*d*m*p + g*j*d*m*o + g*j*d*l*q + g*j*d*l*p + g*j*d*l*o + g*j*e*o*p + g*j*e*m*q + g*d*l*o*p + g*d*l*m*p + g*d*l*m*o + j*d*e*m*p + i*d*e*o*p + j*e*o*q*l + j*e*o*p*l + j*e*m*q*l + j*d*e*o*p + j*d*e*m*q + i*j*d*o*p + g*i*e*o*p + j*d*e*m*o + j*d*e*l*q + j*d*e*l*p + j*e*m*p*l + j*e*m*o*l + g*i*e*m*q + g*i*e*m*p + g*i*e*m*o + g*i*e*l*q + g*i*e*l*p + g*i*e*l*o + j*d*l*o*q + j*d*l*m*q + j*d*l*m*p + j*d*l*m*o + i*d*e*l*o + l*m*q*d*e + l*m*q*d*g + l*m*q*e*g), + c: (r*e*(l*q*g*o + i*j*o*q + g*j*o*q + g*i*o*q + j*l*m*q + j*l*m*p + j*l*m*o + i*j*m*p + i*j*m*o + i*j*l*q + i*j*l*o + i*j*m*q + j*l*o*p + g*j*o*p + g*j*m*q + g*j*m*p + i*j*l*p + i*j*o*p + j*l*o*q + g*j*m*o + g*j*l*q + g*j*l*p + g*j*l*o + g*l*o*p + g*l*m*p + g*l*m*o + g*i*m*o + g*i*o*p + g*i*m*q + g*i*m*p + g*i*l*q + g*i*l*p + g*i*l*o + l*m*q*g))/(l*q*d*e*o + l*q*d*g*o + l*q*e*g*o + i*j*d*o*q + i*j*e*o*q + j*d*e*o*q + g*j*d*o*q + g*j*e*o*q + g*i*e*o*q + i*d*e*o*q + g*i*d*o*q + g*i*d*o*p + g*i*d*m*q + g*i*d*m*p + g*i*d*m*o + g*i*d*l*q + g*i*d*l*p + g*i*d*l*o + g*e*l*m*p + g*e*l*o*p + g*j*e*l*q + g*e*l*m*o + g*j*e*m*p + g*j*e*m*o + d*e*l*m*p + d*e*l*m*o + i*d*e*m*p + g*j*e*l*p + g*j*e*l*o + d*e*l*o*p + i*j*d*l*o + i*j*e*o*p + i*j*e*m*q + i*j*d*m*q + i*j*d*m*p + i*j*d*m*o + i*j*d*l*q + i*j*d*l*p + i*j*e*m*p + i*j*e*m*o + i*j*e*l*q + i*j*e*l*p + i*j*e*l*o + i*d*e*m*q + i*d*e*m*o + i*d*e*l*q + i*d*e*l*p + j*d*l*o*p + j*d*e*l*o + g*j*d*o*p + g*j*d*m*q + g*j*d*m*p + g*j*d*m*o + g*j*d*l*q + g*j*d*l*p + g*j*d*l*o + g*j*e*o*p + g*j*e*m*q + g*d*l*o*p + g*d*l*m*p + g*d*l*m*o + j*d*e*m*p + i*d*e*o*p + j*e*o*q*l + j*e*o*p*l + j*e*m*q*l + j*d*e*o*p + j*d*e*m*q + i*j*d*o*p + g*i*e*o*p + j*d*e*m*o + j*d*e*l*q + j*d*e*l*p + j*e*m*p*l + j*e*m*o*l + g*i*e*m*q + g*i*e*m*p + g*i*e*m*o + g*i*e*l*q + g*i*e*l*p + g*i*e*l*o + j*d*l*o*q + j*d*l*m*q + j*d*l*m*p + j*d*l*m*o + i*d*e*l*o + l*m*q*d*e + l*m*q*d*g + l*m*q*e*g), + f: (r*e*j*(l*q*o + l*o*p + l*m*q + l*m*p + l*m*o + i*o*q + i*o*p + i*m*q + i*m*p + i*m*o + i*l*q + i*l*p + i*l*o))/(l*q*d*e*o + l*q*d*g*o + l*q*e*g*o + i*j*d*o*q + i*j*e*o*q + j*d*e*o*q + g*j*d*o*q + g*j*e*o*q + g*i*e*o*q + i*d*e*o*q + g*i*d*o*q + g*i*d*o*p + g*i*d*m*q + g*i*d*m*p + g*i*d*m*o + g*i*d*l*q + g*i*d*l*p + g*i*d*l*o + g*e*l*m*p + g*e*l*o*p + g*j*e*l*q + g*e*l*m*o + g*j*e*m*p + g*j*e*m*o + d*e*l*m*p + d*e*l*m*o + i*d*e*m*p + g*j*e*l*p + g*j*e*l*o + d*e*l*o*p + i*j*d*l*o + i*j*e*o*p + i*j*e*m*q + i*j*d*m*q + i*j*d*m*p + i*j*d*m*o + i*j*d*l*q + i*j*d*l*p + i*j*e*m*p + i*j*e*m*o + i*j*e*l*q + i*j*e*l*p + i*j*e*l*o + i*d*e*m*q + i*d*e*m*o + i*d*e*l*q + i*d*e*l*p + j*d*l*o*p + j*d*e*l*o + g*j*d*o*p + g*j*d*m*q + g*j*d*m*p + g*j*d*m*o + g*j*d*l*q + g*j*d*l*p + g*j*d*l*o + g*j*e*o*p + g*j*e*m*q + g*d*l*o*p + g*d*l*m*p + g*d*l*m*o + j*d*e*m*p + i*d*e*o*p + j*e*o*q*l + j*e*o*p*l + j*e*m*q*l + j*d*e*o*p + j*d*e*m*q + i*j*d*o*p + g*i*e*o*p + j*d*e*m*o + j*d*e*l*q + j*d*e*l*p + j*e*m*p*l + j*e*m*o*l + g*i*e*m*q + g*i*e*m*p + g*i*e*m*o + g*i*e*l*q + g*i*e*l*p + g*i*e*l*o + j*d*l*o*q + j*d*l*m*q + j*d*l*m*p + j*d*l*m*o + i*d*e*l*o + l*m*q*d*e + l*m*q*d*g + l*m*q*e*g), + h: (j*e*r*l*(o*q + o*p + m*q + m*p + m*o))/(l*q*d*e*o + l*q*d*g*o + l*q*e*g*o + i*j*d*o*q + i*j*e*o*q + j*d*e*o*q + g*j*d*o*q + g*j*e*o*q + g*i*e*o*q + i*d*e*o*q + g*i*d*o*q + g*i*d*o*p + g*i*d*m*q + g*i*d*m*p + g*i*d*m*o + g*i*d*l*q + g*i*d*l*p + g*i*d*l*o + g*e*l*m*p + g*e*l*o*p + g*j*e*l*q + g*e*l*m*o + g*j*e*m*p + g*j*e*m*o + d*e*l*m*p + d*e*l*m*o + i*d*e*m*p + g*j*e*l*p + g*j*e*l*o + d*e*l*o*p + i*j*d*l*o + i*j*e*o*p + i*j*e*m*q + i*j*d*m*q + i*j*d*m*p + i*j*d*m*o + i*j*d*l*q + i*j*d*l*p + i*j*e*m*p + i*j*e*m*o + i*j*e*l*q + i*j*e*l*p + i*j*e*l*o + i*d*e*m*q + i*d*e*m*o + i*d*e*l*q + i*d*e*l*p + j*d*l*o*p + j*d*e*l*o + g*j*d*o*p + g*j*d*m*q + g*j*d*m*p + g*j*d*m*o + g*j*d*l*q + g*j*d*l*p + g*j*d*l*o + g*j*e*o*p + g*j*e*m*q + g*d*l*o*p + g*d*l*m*p + g*d*l*m*o + j*d*e*m*p + i*d*e*o*p + j*e*o*q*l + j*e*o*p*l + j*e*m*q*l + j*d*e*o*p + j*d*e*m*q + i*j*d*o*p + g*i*e*o*p + j*d*e*m*o + j*d*e*l*q + j*d*e*l*p + j*e*m*p*l + j*e*m*o*l + g*i*e*m*q + g*i*e*m*p + g*i*e*m*o + g*i*e*l*q + g*i*e*l*p + g*i*e*l*o + j*d*l*o*q + j*d*l*m*q + j*d*l*m*p + j*d*l*m*o + i*d*e*l*o + l*m*q*d*e + l*m*q*d*g + l*m*q*e*g), + k: (j*e*r*o*l*(q + p))/(l*q*d*e*o + l*q*d*g*o + l*q*e*g*o + i*j*d*o*q + i*j*e*o*q + j*d*e*o*q + g*j*d*o*q + g*j*e*o*q + g*i*e*o*q + i*d*e*o*q + g*i*d*o*q + g*i*d*o*p + g*i*d*m*q + g*i*d*m*p + g*i*d*m*o + g*i*d*l*q + g*i*d*l*p + g*i*d*l*o + g*e*l*m*p + g*e*l*o*p + g*j*e*l*q + g*e*l*m*o + g*j*e*m*p + g*j*e*m*o + d*e*l*m*p + d*e*l*m*o + i*d*e*m*p + g*j*e*l*p + g*j*e*l*o + d*e*l*o*p + i*j*d*l*o + i*j*e*o*p + i*j*e*m*q + i*j*d*m*q + i*j*d*m*p + i*j*d*m*o + i*j*d*l*q + i*j*d*l*p + i*j*e*m*p + i*j*e*m*o + i*j*e*l*q + i*j*e*l*p + i*j*e*l*o + i*d*e*m*q + i*d*e*m*o + i*d*e*l*q + i*d*e*l*p + j*d*l*o*p + j*d*e*l*o + g*j*d*o*p + g*j*d*m*q + g*j*d*m*p + g*j*d*m*o + g*j*d*l*q + g*j*d*l*p + g*j*d*l*o + g*j*e*o*p + g*j*e*m*q + g*d*l*o*p + g*d*l*m*p + g*d*l*m*o + j*d*e*m*p + i*d*e*o*p + j*e*o*q*l + j*e*o*p*l + j*e*m*q*l + j*d*e*o*p + j*d*e*m*q + i*j*d*o*p + g*i*e*o*p + j*d*e*m*o + j*d*e*l*q + j*d*e*l*p + j*e*m*p*l + j*e*m*o*l + g*i*e*m*q + g*i*e*m*p + g*i*e*m*o + g*i*e*l*q + g*i*e*l*p + g*i*e*l*o + j*d*l*o*q + j*d*l*m*q + j*d*l*m*p + j*d*l*m*o + i*d*e*l*o + l*m*q*d*e + l*m*q*d*g + l*m*q*e*g), + n: (j*e*r*o*q*l)/(l*q*d*e*o + l*q*d*g*o + l*q*e*g*o + i*j*d*o*q + i*j*e*o*q + j*d*e*o*q + g*j*d*o*q + g*j*e*o*q + g*i*e*o*q + i*d*e*o*q + g*i*d*o*q + g*i*d*o*p + g*i*d*m*q + g*i*d*m*p + g*i*d*m*o + g*i*d*l*q + g*i*d*l*p + g*i*d*l*o + g*e*l*m*p + g*e*l*o*p + g*j*e*l*q + g*e*l*m*o + g*j*e*m*p + g*j*e*m*o + d*e*l*m*p + d*e*l*m*o + i*d*e*m*p + g*j*e*l*p + g*j*e*l*o + d*e*l*o*p + i*j*d*l*o + i*j*e*o*p + i*j*e*m*q + i*j*d*m*q + i*j*d*m*p + i*j*d*m*o + i*j*d*l*q + i*j*d*l*p + i*j*e*m*p + i*j*e*m*o + i*j*e*l*q + i*j*e*l*p + i*j*e*l*o + i*d*e*m*q + i*d*e*m*o + i*d*e*l*q + i*d*e*l*p + j*d*l*o*p + j*d*e*l*o + g*j*d*o*p + g*j*d*m*q + g*j*d*m*p + g*j*d*m*o + g*j*d*l*q + g*j*d*l*p + g*j*d*l*o + g*j*e*o*p + g*j*e*m*q + g*d*l*o*p + g*d*l*m*p + g*d*l*m*o + j*d*e*m*p + i*d*e*o*p + j*e*o*q*l + j*e*o*p*l + j*e*m*q*l + j*d*e*o*p + j*d*e*m*q + i*j*d*o*p + g*i*e*o*p + j*d*e*m*o + j*d*e*l*q + j*d*e*l*p + j*e*m*p*l + j*e*m*o*l + g*i*e*m*q + g*i*e*m*p + g*i*e*m*o + g*i*e*l*q + g*i*e*l*p + g*i*e*l*o + j*d*l*o*q + j*d*l*m*q + j*d*l*m*p + j*d*l*m*o + i*d*e*l*o + l*m*q*d*e + l*m*q*d*g + l*m*q*e*g), + } + + assert solve_lin_sys(eqs, domain) == sol + +def test_eqs_to_matrix(): + domain, x1,x2 = ring("x1,x2", QQ) + eqs_coeff = [{x1: QQ(1), x2: QQ(1)}, {x1: QQ(2), x2: QQ(-1)}] + eqs_rhs = [QQ(-5), QQ(0)] + M = eqs_to_matrix(eqs_coeff, eqs_rhs, [x1, x2], QQ) + assert M.to_Matrix() == Matrix([[1, 1, 5], [2, -1, 0]]) diff --git a/lib/python3.12/site-packages/sympy/polys/tests/test_specialpolys.py b/lib/python3.12/site-packages/sympy/polys/tests/test_specialpolys.py new file mode 100644 index 0000000000000000000000000000000000000000..39f551c9e70b5c2bae748ea681b9c8a8cb349fe1 --- /dev/null +++ b/lib/python3.12/site-packages/sympy/polys/tests/test_specialpolys.py @@ -0,0 +1,152 @@ +"""Tests for functions for generating interesting polynomials. """ + +from sympy.core.add import Add +from sympy.core.symbol import symbols +from sympy.functions.elementary.miscellaneous import sqrt +from sympy.ntheory.generate import prime +from sympy.polys.domains.integerring import ZZ +from sympy.polys.polytools import Poly +from sympy.utilities.iterables import permute_signs +from sympy.testing.pytest import raises + +from sympy.polys.specialpolys import ( + swinnerton_dyer_poly, + cyclotomic_poly, + symmetric_poly, + random_poly, + interpolating_poly, + fateman_poly_F_1, + dmp_fateman_poly_F_1, + fateman_poly_F_2, + dmp_fateman_poly_F_2, + fateman_poly_F_3, + dmp_fateman_poly_F_3, +) + +from sympy.abc import x, y, z + + +def test_swinnerton_dyer_poly(): + raises(ValueError, lambda: swinnerton_dyer_poly(0, x)) + + assert swinnerton_dyer_poly(1, x, polys=True) == Poly(x**2 - 2) + + assert swinnerton_dyer_poly(1, x) == x**2 - 2 + assert swinnerton_dyer_poly(2, x) == x**4 - 10*x**2 + 1 + assert swinnerton_dyer_poly( + 3, x) == x**8 - 40*x**6 + 352*x**4 - 960*x**2 + 576 + # we only need to check that the polys arg works but + # we may as well test that the roots are correct + p = [sqrt(prime(i)) for i in range(1, 5)] + assert str([i.n(3) for i in + swinnerton_dyer_poly(4, polys=True).all_roots()] + ) == str(sorted([Add(*i).n(3) for i in permute_signs(p)])) + + +def test_cyclotomic_poly(): + raises(ValueError, lambda: cyclotomic_poly(0, x)) + + assert cyclotomic_poly(1, x, polys=True) == Poly(x - 1) + + assert cyclotomic_poly(1, x) == x - 1 + assert cyclotomic_poly(2, x) == x + 1 + assert cyclotomic_poly(3, x) == x**2 + x + 1 + assert cyclotomic_poly(4, x) == x**2 + 1 + assert cyclotomic_poly(5, x) == x**4 + x**3 + x**2 + x + 1 + assert cyclotomic_poly(6, x) == x**2 - x + 1 + + +def test_symmetric_poly(): + raises(ValueError, lambda: symmetric_poly(-1, x, y, z)) + raises(ValueError, lambda: symmetric_poly(5, x, y, z)) + + assert symmetric_poly(1, x, y, z, polys=True) == Poly(x + y + z) + assert symmetric_poly(1, (x, y, z), polys=True) == Poly(x + y + z) + + assert symmetric_poly(0, x, y, z) == 1 + assert symmetric_poly(1, x, y, z) == x + y + z + assert symmetric_poly(2, x, y, z) == x*y + x*z + y*z + assert symmetric_poly(3, x, y, z) == x*y*z + + +def test_random_poly(): + poly = random_poly(x, 10, -100, 100, polys=False) + + assert Poly(poly).degree() == 10 + assert all(-100 <= coeff <= 100 for coeff in Poly(poly).coeffs()) is True + + poly = random_poly(x, 10, -100, 100, polys=True) + + assert poly.degree() == 10 + assert all(-100 <= coeff <= 100 for coeff in poly.coeffs()) is True + + +def test_interpolating_poly(): + x0, x1, x2, x3, y0, y1, y2, y3 = symbols('x:4, y:4') + + assert interpolating_poly(0, x) == 0 + assert interpolating_poly(1, x) == y0 + + assert interpolating_poly(2, x) == \ + y0*(x - x1)/(x0 - x1) + y1*(x - x0)/(x1 - x0) + + assert interpolating_poly(3, x) == \ + y0*(x - x1)*(x - x2)/((x0 - x1)*(x0 - x2)) + \ + y1*(x - x0)*(x - x2)/((x1 - x0)*(x1 - x2)) + \ + y2*(x - x0)*(x - x1)/((x2 - x0)*(x2 - x1)) + + assert interpolating_poly(4, x) == \ + y0*(x - x1)*(x - x2)*(x - x3)/((x0 - x1)*(x0 - x2)*(x0 - x3)) + \ + y1*(x - x0)*(x - x2)*(x - x3)/((x1 - x0)*(x1 - x2)*(x1 - x3)) + \ + y2*(x - x0)*(x - x1)*(x - x3)/((x2 - x0)*(x2 - x1)*(x2 - x3)) + \ + y3*(x - x0)*(x - x1)*(x - x2)/((x3 - x0)*(x3 - x1)*(x3 - x2)) + + raises(ValueError, lambda: + interpolating_poly(2, x, (x, 2), (1, 3))) + raises(ValueError, lambda: + interpolating_poly(2, x, (x + y, 2), (1, 3))) + raises(ValueError, lambda: + interpolating_poly(2, x + y, (x, 2), (1, 3))) + raises(ValueError, lambda: + interpolating_poly(2, 3, (4, 5), (6, 7))) + raises(ValueError, lambda: + interpolating_poly(2, 3, (4, 5), (6, 7, 8))) + assert interpolating_poly(0, x, (1, 2), (3, 4)) == 0 + assert interpolating_poly(1, x, (1, 2), (3, 4)) == 3 + assert interpolating_poly(2, x, (1, 2), (3, 4)) == x + 2 + + +def test_fateman_poly_F_1(): + f, g, h = fateman_poly_F_1(1) + F, G, H = dmp_fateman_poly_F_1(1, ZZ) + + assert [ t.rep.to_list() for t in [f, g, h] ] == [F, G, H] + + f, g, h = fateman_poly_F_1(3) + F, G, H = dmp_fateman_poly_F_1(3, ZZ) + + assert [ t.rep.to_list() for t in [f, g, h] ] == [F, G, H] + + +def test_fateman_poly_F_2(): + f, g, h = fateman_poly_F_2(1) + F, G, H = dmp_fateman_poly_F_2(1, ZZ) + + assert [ t.rep.to_list() for t in [f, g, h] ] == [F, G, H] + + f, g, h = fateman_poly_F_2(3) + F, G, H = dmp_fateman_poly_F_2(3, ZZ) + + assert [ t.rep.to_list() for t in [f, g, h] ] == [F, G, H] + + +def test_fateman_poly_F_3(): + f, g, h = fateman_poly_F_3(1) + F, G, H = dmp_fateman_poly_F_3(1, ZZ) + + assert [ t.rep.to_list() for t in [f, g, h] ] == [F, G, H] + + f, g, h = fateman_poly_F_3(3) + F, G, H = dmp_fateman_poly_F_3(3, ZZ) + + assert [ t.rep.to_list() for t in [f, g, h] ] == [F, G, H] diff --git a/lib/python3.12/site-packages/sympy/polys/tests/test_sqfreetools.py b/lib/python3.12/site-packages/sympy/polys/tests/test_sqfreetools.py new file mode 100644 index 0000000000000000000000000000000000000000..b772a05a50e2eacd5a7c80352b1eadd52c69c3fa --- /dev/null +++ b/lib/python3.12/site-packages/sympy/polys/tests/test_sqfreetools.py @@ -0,0 +1,160 @@ +"""Tests for square-free decomposition algorithms and related tools. """ + +from sympy.polys.rings import ring +from sympy.polys.domains import FF, ZZ, QQ +from sympy.polys.specialpolys import f_polys + +from sympy.testing.pytest import raises +from sympy.external.gmpy import MPQ + +f_0, f_1, f_2, f_3, f_4, f_5, f_6 = f_polys() + +def test_dup_sqf(): + R, x = ring("x", ZZ) + + assert R.dup_sqf_part(0) == 0 + assert R.dup_sqf_p(0) is True + + assert R.dup_sqf_part(7) == 1 + assert R.dup_sqf_p(7) is True + + assert R.dup_sqf_part(2*x + 2) == x + 1 + assert R.dup_sqf_p(2*x + 2) is True + + assert R.dup_sqf_part(x**3 + x + 1) == x**3 + x + 1 + assert R.dup_sqf_p(x**3 + x + 1) is True + + assert R.dup_sqf_part(-x**3 + x + 1) == x**3 - x - 1 + assert R.dup_sqf_p(-x**3 + x + 1) is True + + assert R.dup_sqf_part(2*x**3 + 3*x**2) == 2*x**2 + 3*x + assert R.dup_sqf_p(2*x**3 + 3*x**2) is False + + assert R.dup_sqf_part(-2*x**3 + 3*x**2) == 2*x**2 - 3*x + assert R.dup_sqf_p(-2*x**3 + 3*x**2) is False + + assert R.dup_sqf_list(0) == (0, []) + assert R.dup_sqf_list(1) == (1, []) + + assert R.dup_sqf_list(x) == (1, [(x, 1)]) + assert R.dup_sqf_list(2*x**2) == (2, [(x, 2)]) + assert R.dup_sqf_list(3*x**3) == (3, [(x, 3)]) + + assert R.dup_sqf_list(-x**5 + x**4 + x - 1) == \ + (-1, [(x**3 + x**2 + x + 1, 1), (x - 1, 2)]) + assert R.dup_sqf_list(x**8 + 6*x**6 + 12*x**4 + 8*x**2) == \ + ( 1, [(x, 2), (x**2 + 2, 3)]) + + assert R.dup_sqf_list(2*x**2 + 4*x + 2) == (2, [(x + 1, 2)]) + + R, x = ring("x", QQ) + assert R.dup_sqf_list(2*x**2 + 4*x + 2) == (2, [(x + 1, 2)]) + + R, x = ring("x", FF(2)) + assert R.dup_sqf_list(x**2 + 1) == (1, [(x + 1, 2)]) + + R, x = ring("x", FF(3)) + assert R.dup_sqf_list(x**10 + 2*x**7 + 2*x**4 + x) == \ + (1, [(x, 1), + (x + 1, 3), + (x + 2, 6)]) + + R1, x = ring("x", ZZ) + R2, y = ring("y", FF(3)) + + f = x**3 + 1 + g = y**3 + 1 + + assert R1.dup_sqf_part(f) == f + assert R2.dup_sqf_part(g) == y + 1 + + assert R1.dup_sqf_p(f) is True + assert R2.dup_sqf_p(g) is False + + R, x, y = ring("x,y", ZZ) + + A = x**4 - 3*x**2 + 6 + D = x**6 - 5*x**4 + 5*x**2 + 4 + + f, g = D, R.dmp_sub(A, R.dmp_mul(R.dmp_diff(D, 1), y)) + res = R.dmp_resultant(f, g) + h = (4*y**2 + 1).drop(x) + + assert R.drop(x).dup_sqf_list(res) == (45796, [(h, 3)]) + + Rt, t = ring("t", ZZ) + R, x = ring("x", Rt) + assert R.dup_sqf_list_include(t**3*x**2) == [(t**3, 1), (x, 2)] + + +def test_dmp_sqf(): + R, x, y = ring("x,y", ZZ) + assert R.dmp_sqf_part(0) == 0 + assert R.dmp_sqf_p(0) is True + + assert R.dmp_sqf_part(7) == 1 + assert R.dmp_sqf_p(7) is True + + assert R.dmp_sqf_list(3) == (3, []) + assert R.dmp_sqf_list_include(3) == [(3, 1)] + + R, x, y, z = ring("x,y,z", ZZ) + assert R.dmp_sqf_p(f_0) is True + assert R.dmp_sqf_p(f_0**2) is False + assert R.dmp_sqf_p(f_1) is True + assert R.dmp_sqf_p(f_1**2) is False + assert R.dmp_sqf_p(f_2) is True + assert R.dmp_sqf_p(f_2**2) is False + assert R.dmp_sqf_p(f_3) is True + assert R.dmp_sqf_p(f_3**2) is False + assert R.dmp_sqf_p(f_5) is False + assert R.dmp_sqf_p(f_5**2) is False + + assert R.dmp_sqf_p(f_4) is True + assert R.dmp_sqf_part(f_4) == -f_4 + + assert R.dmp_sqf_part(f_5) == x + y - z + + R, x, y, z, t = ring("x,y,z,t", ZZ) + assert R.dmp_sqf_p(f_6) is True + assert R.dmp_sqf_part(f_6) == f_6 + + R, x = ring("x", ZZ) + f = -x**5 + x**4 + x - 1 + + assert R.dmp_sqf_list(f) == (-1, [(x**3 + x**2 + x + 1, 1), (x - 1, 2)]) + assert R.dmp_sqf_list_include(f) == [(-x**3 - x**2 - x - 1, 1), (x - 1, 2)] + + R, x, y = ring("x,y", ZZ) + f = -x**5 + x**4 + x - 1 + + assert R.dmp_sqf_list(f) == (-1, [(x**3 + x**2 + x + 1, 1), (x - 1, 2)]) + assert R.dmp_sqf_list_include(f) == [(-x**3 - x**2 - x - 1, 1), (x - 1, 2)] + + f = -x**2 + 2*x - 1 + assert R.dmp_sqf_list_include(f) == [(-1, 1), (x - 1, 2)] + + f = (y**2 + 1)**2*(x**2 + 2*x + 2) + assert R.dmp_sqf_p(f) is False + assert R.dmp_sqf_list(f) == (1, [(x**2 + 2*x + 2, 1), (y**2 + 1, 2)]) + + R, x, y = ring("x,y", FF(2)) + raises(NotImplementedError, lambda: R.dmp_sqf_list(y**2 + 1)) + + +def test_dup_gff_list(): + R, x = ring("x", ZZ) + + f = x**5 + 2*x**4 - x**3 - 2*x**2 + assert R.dup_gff_list(f) == [(x, 1), (x + 2, 4)] + + g = x**9 - 20*x**8 + 166*x**7 - 744*x**6 + 1965*x**5 - 3132*x**4 + 2948*x**3 - 1504*x**2 + 320*x + assert R.dup_gff_list(g) == [(x**2 - 5*x + 4, 1), (x**2 - 5*x + 4, 2), (x, 3)] + + raises(ValueError, lambda: R.dup_gff_list(0)) + +def test_issue_26178(): + R, x, y, z = ring(['x', 'y', 'z'], QQ) + assert (x**2 - 2*y**2 + 1).sqf_list() == (MPQ(1,1), [(x**2 - 2*y**2 + 1, 1)]) + assert (x**2 - 2*z**2 + 1).sqf_list() == (MPQ(1,1), [(x**2 - 2*z**2 + 1, 1)]) + assert (y**2 - 2*z**2 + 1).sqf_list() == (MPQ(1,1), [(y**2 - 2*z**2 + 1, 1)]) diff --git a/lib/python3.12/site-packages/sympy/polys/tests/test_subresultants_qq_zz.py b/lib/python3.12/site-packages/sympy/polys/tests/test_subresultants_qq_zz.py new file mode 100644 index 0000000000000000000000000000000000000000..354663d36615f6578e8b484612d8a6571731376d --- /dev/null +++ b/lib/python3.12/site-packages/sympy/polys/tests/test_subresultants_qq_zz.py @@ -0,0 +1,347 @@ +from sympy.core.symbol import var +from sympy.polys.polytools import (pquo, prem, sturm, subresultants) +from sympy.matrices import Matrix +from sympy.polys.subresultants_qq_zz import (sylvester, res, res_q, res_z, bezout, + subresultants_sylv, modified_subresultants_sylv, + subresultants_bezout, modified_subresultants_bezout, + backward_eye, + sturm_pg, sturm_q, sturm_amv, euclid_pg, euclid_q, + euclid_amv, modified_subresultants_pg, subresultants_pg, + subresultants_amv_q, quo_z, rem_z, subresultants_amv, + modified_subresultants_amv, subresultants_rem, + subresultants_vv, subresultants_vv_2) + + +def test_sylvester(): + x = var('x') + + assert sylvester(x**3 -7, 0, x) == sylvester(x**3 -7, 0, x, 1) == Matrix([[0]]) + assert sylvester(0, x**3 -7, x) == sylvester(0, x**3 -7, x, 1) == Matrix([[0]]) + assert sylvester(x**3 -7, 0, x, 2) == Matrix([[0]]) + assert sylvester(0, x**3 -7, x, 2) == Matrix([[0]]) + + assert sylvester(x**3 -7, 7, x).det() == sylvester(x**3 -7, 7, x, 1).det() == 343 + assert sylvester(7, x**3 -7, x).det() == sylvester(7, x**3 -7, x, 1).det() == 343 + assert sylvester(x**3 -7, 7, x, 2).det() == -343 + assert sylvester(7, x**3 -7, x, 2).det() == 343 + + assert sylvester(3, 7, x).det() == sylvester(3, 7, x, 1).det() == sylvester(3, 7, x, 2).det() == 1 + + assert sylvester(3, 0, x).det() == sylvester(3, 0, x, 1).det() == sylvester(3, 0, x, 2).det() == 1 + + assert sylvester(x - 3, x - 8, x) == sylvester(x - 3, x - 8, x, 1) == sylvester(x - 3, x - 8, x, 2) == Matrix([[1, -3], [1, -8]]) + + assert sylvester(x**3 - 7*x + 7, 3*x**2 - 7, x) == sylvester(x**3 - 7*x + 7, 3*x**2 - 7, x, 1) == Matrix([[1, 0, -7, 7, 0], [0, 1, 0, -7, 7], [3, 0, -7, 0, 0], [0, 3, 0, -7, 0], [0, 0, 3, 0, -7]]) + + assert sylvester(x**3 - 7*x + 7, 3*x**2 - 7, x, 2) == Matrix([ +[1, 0, -7, 7, 0, 0], [0, 3, 0, -7, 0, 0], [0, 1, 0, -7, 7, 0], [0, 0, 3, 0, -7, 0], [0, 0, 1, 0, -7, 7], [0, 0, 0, 3, 0, -7]]) + +def test_subresultants_sylv(): + x = var('x') + + p = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 + q = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 + assert subresultants_sylv(p, q, x) == subresultants(p, q, x) + assert subresultants_sylv(p, q, x)[-1] == res(p, q, x) + assert subresultants_sylv(p, q, x) != euclid_amv(p, q, x) + amv_factors = [1, 1, -1, 1, -1, 1] + assert subresultants_sylv(p, q, x) == [i*j for i, j in zip(amv_factors, modified_subresultants_amv(p, q, x))] + + p = x**3 - 7*x + 7 + q = 3*x**2 - 7 + assert subresultants_sylv(p, q, x) == euclid_amv(p, q, x) + +def test_modified_subresultants_sylv(): + x = var('x') + + p = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 + q = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 + amv_factors = [1, 1, -1, 1, -1, 1] + assert modified_subresultants_sylv(p, q, x) == [i*j for i, j in zip(amv_factors, subresultants_amv(p, q, x))] + assert modified_subresultants_sylv(p, q, x)[-1] != res_q(p + x**8, q, x) + assert modified_subresultants_sylv(p, q, x) != sturm_amv(p, q, x) + + p = x**3 - 7*x + 7 + q = 3*x**2 - 7 + assert modified_subresultants_sylv(p, q, x) == sturm_amv(p, q, x) + assert modified_subresultants_sylv(-p, q, x) != sturm_amv(-p, q, x) + +def test_res(): + x = var('x') + + assert res(3, 5, x) == 1 + +def test_res_q(): + x = var('x') + + assert res_q(3, 5, x) == 1 + +def test_res_z(): + x = var('x') + + assert res_z(3, 5, x) == 1 + assert res(3, 5, x) == res_q(3, 5, x) == res_z(3, 5, x) + +def test_bezout(): + x = var('x') + + p = -2*x**5+7*x**3+9*x**2-3*x+1 + q = -10*x**4+21*x**2+18*x-3 + assert bezout(p, q, x, 'bz').det() == sylvester(p, q, x, 2).det() + assert bezout(p, q, x, 'bz').det() != sylvester(p, q, x, 1).det() + assert bezout(p, q, x, 'prs') == backward_eye(5) * bezout(p, q, x, 'bz') * backward_eye(5) + +def test_subresultants_bezout(): + x = var('x') + + p = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 + q = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 + assert subresultants_bezout(p, q, x) == subresultants(p, q, x) + assert subresultants_bezout(p, q, x)[-1] == sylvester(p, q, x).det() + assert subresultants_bezout(p, q, x) != euclid_amv(p, q, x) + amv_factors = [1, 1, -1, 1, -1, 1] + assert subresultants_bezout(p, q, x) == [i*j for i, j in zip(amv_factors, modified_subresultants_amv(p, q, x))] + + p = x**3 - 7*x + 7 + q = 3*x**2 - 7 + assert subresultants_bezout(p, q, x) == euclid_amv(p, q, x) + +def test_modified_subresultants_bezout(): + x = var('x') + + p = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 + q = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 + amv_factors = [1, 1, -1, 1, -1, 1] + assert modified_subresultants_bezout(p, q, x) == [i*j for i, j in zip(amv_factors, subresultants_amv(p, q, x))] + assert modified_subresultants_bezout(p, q, x)[-1] != sylvester(p + x**8, q, x).det() + assert modified_subresultants_bezout(p, q, x) != sturm_amv(p, q, x) + + p = x**3 - 7*x + 7 + q = 3*x**2 - 7 + assert modified_subresultants_bezout(p, q, x) == sturm_amv(p, q, x) + assert modified_subresultants_bezout(-p, q, x) != sturm_amv(-p, q, x) + +def test_sturm_pg(): + x = var('x') + + p = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 + q = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 + assert sturm_pg(p, q, x)[-1] != sylvester(p, q, x, 2).det() + sam_factors = [1, 1, -1, -1, 1, 1] + assert sturm_pg(p, q, x) == [i*j for i,j in zip(sam_factors, euclid_pg(p, q, x))] + + p = -9*x**5 - 5*x**3 - 9 + q = -45*x**4 - 15*x**2 + assert sturm_pg(p, q, x, 1)[-1] == sylvester(p, q, x, 1).det() + assert sturm_pg(p, q, x)[-1] != sylvester(p, q, x, 2).det() + assert sturm_pg(-p, q, x)[-1] == sylvester(-p, q, x, 2).det() + assert sturm_pg(-p, q, x) == modified_subresultants_pg(-p, q, x) + +def test_sturm_q(): + x = var('x') + + p = x**3 - 7*x + 7 + q = 3*x**2 - 7 + assert sturm_q(p, q, x) == sturm(p) + assert sturm_q(-p, -q, x) != sturm(-p) + + +def test_sturm_amv(): + x = var('x') + + p = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 + q = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 + assert sturm_amv(p, q, x)[-1] != sylvester(p, q, x, 2).det() + sam_factors = [1, 1, -1, -1, 1, 1] + assert sturm_amv(p, q, x) == [i*j for i,j in zip(sam_factors, euclid_amv(p, q, x))] + + p = -9*x**5 - 5*x**3 - 9 + q = -45*x**4 - 15*x**2 + assert sturm_amv(p, q, x, 1)[-1] == sylvester(p, q, x, 1).det() + assert sturm_amv(p, q, x)[-1] != sylvester(p, q, x, 2).det() + assert sturm_amv(-p, q, x)[-1] == sylvester(-p, q, x, 2).det() + assert sturm_pg(-p, q, x) == modified_subresultants_pg(-p, q, x) + + +def test_euclid_pg(): + x = var('x') + + p = x**6+x**5-x**4-x**3+x**2-x+1 + q = 6*x**5+5*x**4-4*x**3-3*x**2+2*x-1 + assert euclid_pg(p, q, x)[-1] == sylvester(p, q, x).det() + assert euclid_pg(p, q, x) == subresultants_pg(p, q, x) + + p = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 + q = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 + assert euclid_pg(p, q, x)[-1] != sylvester(p, q, x, 2).det() + sam_factors = [1, 1, -1, -1, 1, 1] + assert euclid_pg(p, q, x) == [i*j for i,j in zip(sam_factors, sturm_pg(p, q, x))] + + +def test_euclid_q(): + x = var('x') + + p = x**3 - 7*x + 7 + q = 3*x**2 - 7 + assert euclid_q(p, q, x)[-1] == -sturm(p)[-1] + + +def test_euclid_amv(): + x = var('x') + + p = x**3 - 7*x + 7 + q = 3*x**2 - 7 + assert euclid_amv(p, q, x)[-1] == sylvester(p, q, x).det() + assert euclid_amv(p, q, x) == subresultants_amv(p, q, x) + + p = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 + q = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 + assert euclid_amv(p, q, x)[-1] != sylvester(p, q, x, 2).det() + sam_factors = [1, 1, -1, -1, 1, 1] + assert euclid_amv(p, q, x) == [i*j for i,j in zip(sam_factors, sturm_amv(p, q, x))] + + +def test_modified_subresultants_pg(): + x = var('x') + + p = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 + q = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 + amv_factors = [1, 1, -1, 1, -1, 1] + assert modified_subresultants_pg(p, q, x) == [i*j for i, j in zip(amv_factors, subresultants_pg(p, q, x))] + assert modified_subresultants_pg(p, q, x)[-1] != sylvester(p + x**8, q, x).det() + assert modified_subresultants_pg(p, q, x) != sturm_pg(p, q, x) + + p = x**3 - 7*x + 7 + q = 3*x**2 - 7 + assert modified_subresultants_pg(p, q, x) == sturm_pg(p, q, x) + assert modified_subresultants_pg(-p, q, x) != sturm_pg(-p, q, x) + + +def test_subresultants_pg(): + x = var('x') + + p = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 + q = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 + assert subresultants_pg(p, q, x) == subresultants(p, q, x) + assert subresultants_pg(p, q, x)[-1] == sylvester(p, q, x).det() + assert subresultants_pg(p, q, x) != euclid_pg(p, q, x) + amv_factors = [1, 1, -1, 1, -1, 1] + assert subresultants_pg(p, q, x) == [i*j for i, j in zip(amv_factors, modified_subresultants_amv(p, q, x))] + + p = x**3 - 7*x + 7 + q = 3*x**2 - 7 + assert subresultants_pg(p, q, x) == euclid_pg(p, q, x) + + +def test_subresultants_amv_q(): + x = var('x') + + p = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 + q = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 + assert subresultants_amv_q(p, q, x) == subresultants(p, q, x) + assert subresultants_amv_q(p, q, x)[-1] == sylvester(p, q, x).det() + assert subresultants_amv_q(p, q, x) != euclid_amv(p, q, x) + amv_factors = [1, 1, -1, 1, -1, 1] + assert subresultants_amv_q(p, q, x) == [i*j for i, j in zip(amv_factors, modified_subresultants_amv(p, q, x))] + + p = x**3 - 7*x + 7 + q = 3*x**2 - 7 + assert subresultants_amv(p, q, x) == euclid_amv(p, q, x) + + +def test_rem_z(): + x = var('x') + + p = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 + q = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 + assert rem_z(p, -q, x) != prem(p, -q, x) + +def test_quo_z(): + x = var('x') + + p = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 + q = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 + assert quo_z(p, -q, x) != pquo(p, -q, x) + + y = var('y') + q = 3*x**6 + 5*y**4 - 4*x**2 - 9*x + 21 + assert quo_z(p, -q, x) == pquo(p, -q, x) + +def test_subresultants_amv(): + x = var('x') + + p = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 + q = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 + assert subresultants_amv(p, q, x) == subresultants(p, q, x) + assert subresultants_amv(p, q, x)[-1] == sylvester(p, q, x).det() + assert subresultants_amv(p, q, x) != euclid_amv(p, q, x) + amv_factors = [1, 1, -1, 1, -1, 1] + assert subresultants_amv(p, q, x) == [i*j for i, j in zip(amv_factors, modified_subresultants_amv(p, q, x))] + + p = x**3 - 7*x + 7 + q = 3*x**2 - 7 + assert subresultants_amv(p, q, x) == euclid_amv(p, q, x) + + +def test_modified_subresultants_amv(): + x = var('x') + + p = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 + q = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 + amv_factors = [1, 1, -1, 1, -1, 1] + assert modified_subresultants_amv(p, q, x) == [i*j for i, j in zip(amv_factors, subresultants_amv(p, q, x))] + assert modified_subresultants_amv(p, q, x)[-1] != sylvester(p + x**8, q, x).det() + assert modified_subresultants_amv(p, q, x) != sturm_amv(p, q, x) + + p = x**3 - 7*x + 7 + q = 3*x**2 - 7 + assert modified_subresultants_amv(p, q, x) == sturm_amv(p, q, x) + assert modified_subresultants_amv(-p, q, x) != sturm_amv(-p, q, x) + + +def test_subresultants_rem(): + x = var('x') + + p = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 + q = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 + assert subresultants_rem(p, q, x) == subresultants(p, q, x) + assert subresultants_rem(p, q, x)[-1] == sylvester(p, q, x).det() + assert subresultants_rem(p, q, x) != euclid_amv(p, q, x) + amv_factors = [1, 1, -1, 1, -1, 1] + assert subresultants_rem(p, q, x) == [i*j for i, j in zip(amv_factors, modified_subresultants_amv(p, q, x))] + + p = x**3 - 7*x + 7 + q = 3*x**2 - 7 + assert subresultants_rem(p, q, x) == euclid_amv(p, q, x) + + +def test_subresultants_vv(): + x = var('x') + + p = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 + q = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 + assert subresultants_vv(p, q, x) == subresultants(p, q, x) + assert subresultants_vv(p, q, x)[-1] == sylvester(p, q, x).det() + assert subresultants_vv(p, q, x) != euclid_amv(p, q, x) + amv_factors = [1, 1, -1, 1, -1, 1] + assert subresultants_vv(p, q, x) == [i*j for i, j in zip(amv_factors, modified_subresultants_amv(p, q, x))] + + p = x**3 - 7*x + 7 + q = 3*x**2 - 7 + assert subresultants_vv(p, q, x) == euclid_amv(p, q, x) + + +def test_subresultants_vv_2(): + x = var('x') + + p = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 + q = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 + assert subresultants_vv_2(p, q, x) == subresultants(p, q, x) + assert subresultants_vv_2(p, q, x)[-1] == sylvester(p, q, x).det() + assert subresultants_vv_2(p, q, x) != euclid_amv(p, q, x) + amv_factors = [1, 1, -1, 1, -1, 1] + assert subresultants_vv_2(p, q, x) == [i*j for i, j in zip(amv_factors, modified_subresultants_amv(p, q, x))] + + p = x**3 - 7*x + 7 + q = 3*x**2 - 7 + assert subresultants_vv_2(p, q, x) == euclid_amv(p, q, x)