"""Implementation of :class:`Domain` class. """ from typing import Any, Optional, Type from sympy.core import Basic, sympify from sympy.core.sorting import default_sort_key, ordered from sympy.external.gmpy import HAS_GMPY from sympy.polys.domains.domainelement import DomainElement from sympy.polys.orderings import lex from sympy.polys.polyerrors import UnificationFailed, CoercionFailed, DomainError from sympy.polys.polyutils import _unify_gens, _not_a_coeff from sympy.utilities import public from sympy.utilities.iterables import is_sequence @public class Domain: """Superclass for all domains in the polys domains system. See :ref:`polys-domainsintro` for an introductory explanation of the domains system. The :py:class:`~.Domain` class is an abstract base class for all of the concrete domain types. There are many different :py:class:`~.Domain` subclasses each of which has an associated ``dtype`` which is a class representing the elements of the domain. The coefficients of a :py:class:`~.Poly` are elements of a domain which must be a subclass of :py:class:`~.Domain`. Examples ======== The most common example domains are the integers :ref:`ZZ` and the rationals :ref:`QQ`. >>> from sympy import Poly, symbols, Domain >>> x, y = symbols('x, y') >>> p = Poly(x**2 + y) >>> p Poly(x**2 + y, x, y, domain='ZZ') >>> p.domain ZZ >>> isinstance(p.domain, Domain) True >>> Poly(x**2 + y/2) Poly(x**2 + 1/2*y, x, y, domain='QQ') The domains can be used directly in which case the domain object e.g. (:ref:`ZZ` or :ref:`QQ`) can be used as a constructor for elements of ``dtype``. >>> from sympy import ZZ, QQ >>> ZZ(2) 2 >>> ZZ.dtype # doctest: +SKIP >>> type(ZZ(2)) # doctest: +SKIP >>> QQ(1, 2) 1/2 >>> type(QQ(1, 2)) # doctest: +SKIP The corresponding domain elements can be used with the arithmetic operations ``+,-,*,**`` and depending on the domain some combination of ``/,//,%`` might be usable. For example in :ref:`ZZ` both ``//`` (floor division) and ``%`` (modulo division) can be used but ``/`` (true division) cannot. Since :ref:`QQ` is a :py:class:`~.Field` its elements can be used with ``/`` but ``//`` and ``%`` should not be used. Some domains have a :py:meth:`~.Domain.gcd` method. >>> ZZ(2) + ZZ(3) 5 >>> ZZ(5) // ZZ(2) 2 >>> ZZ(5) % ZZ(2) 1 >>> QQ(1, 2) / QQ(2, 3) 3/4 >>> ZZ.gcd(ZZ(4), ZZ(2)) 2 >>> QQ.gcd(QQ(2,7), QQ(5,3)) 1/21 >>> ZZ.is_Field False >>> QQ.is_Field True There are also many other domains including: 1. :ref:`GF(p)` for finite fields of prime order. 2. :ref:`RR` for real (floating point) numbers. 3. :ref:`CC` for complex (floating point) numbers. 4. :ref:`QQ(a)` for algebraic number fields. 5. :ref:`K[x]` for polynomial rings. 6. :ref:`K(x)` for rational function fields. 7. :ref:`EX` for arbitrary expressions. Each domain is represented by a domain object and also an implementation class (``dtype``) for the elements of the domain. For example the :ref:`K[x]` domains are represented by a domain object which is an instance of :py:class:`~.PolynomialRing` and the elements are always instances of :py:class:`~.PolyElement`. The implementation class represents particular types of mathematical expressions in a way that is more efficient than a normal SymPy expression which is of type :py:class:`~.Expr`. The domain methods :py:meth:`~.Domain.from_sympy` and :py:meth:`~.Domain.to_sympy` are used to convert from :py:class:`~.Expr` to a domain element and vice versa. >>> from sympy import Symbol, ZZ, Expr >>> x = Symbol('x') >>> K = ZZ[x] # polynomial ring domain >>> K ZZ[x] >>> type(K) # class of the domain >>> K.dtype # class of the elements >>> p_expr = x**2 + 1 # Expr >>> p_expr x**2 + 1 >>> type(p_expr) >>> isinstance(p_expr, Expr) True >>> p_domain = K.from_sympy(p_expr) >>> p_domain # domain element x**2 + 1 >>> type(p_domain) >>> K.to_sympy(p_domain) == p_expr True The :py:meth:`~.Domain.convert_from` method is used to convert domain elements from one domain to another. >>> from sympy import ZZ, QQ >>> ez = ZZ(2) >>> eq = QQ.convert_from(ez, ZZ) >>> type(ez) # doctest: +SKIP >>> type(eq) # doctest: +SKIP Elements from different domains should not be mixed in arithmetic or other operations: they should be converted to a common domain first. The domain method :py:meth:`~.Domain.unify` is used to find a domain that can represent all the elements of two given domains. >>> from sympy import ZZ, QQ, symbols >>> x, y = symbols('x, y') >>> ZZ.unify(QQ) QQ >>> ZZ[x].unify(QQ) QQ[x] >>> ZZ[x].unify(QQ[y]) QQ[x,y] If a domain is a :py:class:`~.Ring` then is might have an associated :py:class:`~.Field` and vice versa. The :py:meth:`~.Domain.get_field` and :py:meth:`~.Domain.get_ring` methods will find or create the associated domain. >>> from sympy import ZZ, QQ, Symbol >>> x = Symbol('x') >>> ZZ.has_assoc_Field True >>> ZZ.get_field() QQ >>> QQ.has_assoc_Ring True >>> QQ.get_ring() ZZ >>> K = QQ[x] >>> K QQ[x] >>> K.get_field() QQ(x) See also ======== DomainElement: abstract base class for domain elements construct_domain: construct a minimal domain for some expressions """ dtype = None # type: Optional[Type] """The type (class) of the elements of this :py:class:`~.Domain`: >>> from sympy import ZZ, QQ, Symbol >>> ZZ.dtype >>> z = ZZ(2) >>> z 2 >>> type(z) >>> type(z) == ZZ.dtype True Every domain has an associated **dtype** ("datatype") which is the class of the associated domain elements. See also ======== of_type """ zero = None # type: Optional[Any] """The zero element of the :py:class:`~.Domain`: >>> from sympy import QQ >>> QQ.zero 0 >>> QQ.of_type(QQ.zero) True See also ======== of_type one """ one = None # type: Optional[Any] """The one element of the :py:class:`~.Domain`: >>> from sympy import QQ >>> QQ.one 1 >>> QQ.of_type(QQ.one) True See also ======== of_type zero """ is_Ring = False """Boolean flag indicating if the domain is a :py:class:`~.Ring`. >>> from sympy import ZZ >>> ZZ.is_Ring True Basically every :py:class:`~.Domain` represents a ring so this flag is not that useful. See also ======== is_PID is_Field get_ring has_assoc_Ring """ is_Field = False """Boolean flag indicating if the domain is a :py:class:`~.Field`. >>> from sympy import ZZ, QQ >>> ZZ.is_Field False >>> QQ.is_Field True See also ======== is_PID is_Ring get_field has_assoc_Field """ has_assoc_Ring = False """Boolean flag indicating if the domain has an associated :py:class:`~.Ring`. >>> from sympy import QQ >>> QQ.has_assoc_Ring True >>> QQ.get_ring() ZZ See also ======== is_Field get_ring """ has_assoc_Field = False """Boolean flag indicating if the domain has an associated :py:class:`~.Field`. >>> from sympy import ZZ >>> ZZ.has_assoc_Field True >>> ZZ.get_field() QQ See also ======== is_Field get_field """ is_FiniteField = is_FF = False is_IntegerRing = is_ZZ = False is_RationalField = is_QQ = False is_GaussianRing = is_ZZ_I = False is_GaussianField = is_QQ_I = False is_RealField = is_RR = False is_ComplexField = is_CC = False is_AlgebraicField = is_Algebraic = False is_PolynomialRing = is_Poly = False is_FractionField = is_Frac = False is_SymbolicDomain = is_EX = False is_SymbolicRawDomain = is_EXRAW = False is_FiniteExtension = False is_Exact = True is_Numerical = False is_Simple = False is_Composite = False is_PID = False """Boolean flag indicating if the domain is a `principal ideal domain`_. >>> from sympy import ZZ >>> ZZ.has_assoc_Field True >>> ZZ.get_field() QQ .. _principal ideal domain: https://en.wikipedia.org/wiki/Principal_ideal_domain See also ======== is_Field get_field """ has_CharacteristicZero = False rep = None # type: Optional[str] alias = None # type: Optional[str] def __init__(self): raise NotImplementedError def __str__(self): return self.rep def __repr__(self): return str(self) def __hash__(self): return hash((self.__class__.__name__, self.dtype)) def new(self, *args): return self.dtype(*args) @property def tp(self): """Alias for :py:attr:`~.Domain.dtype`""" return self.dtype def __call__(self, *args): """Construct an element of ``self`` domain from ``args``. """ return self.new(*args) def normal(self, *args): return self.dtype(*args) def convert_from(self, element, base): """Convert ``element`` to ``self.dtype`` given the base domain. """ if base.alias is not None: method = "from_" + base.alias else: method = "from_" + base.__class__.__name__ _convert = getattr(self, method) if _convert is not None: result = _convert(element, base) if result is not None: return result raise CoercionFailed("Cannot convert %s of type %s from %s to %s" % (element, type(element), base, self)) def convert(self, element, base=None): """Convert ``element`` to ``self.dtype``. """ if base is not None: if _not_a_coeff(element): raise CoercionFailed('%s is not in any domain' % element) return self.convert_from(element, base) if self.of_type(element): return element if _not_a_coeff(element): raise CoercionFailed('%s is not in any domain' % element) from sympy.polys.domains import ZZ, QQ, RealField, ComplexField if ZZ.of_type(element): return self.convert_from(element, ZZ) if isinstance(element, int): return self.convert_from(ZZ(element), ZZ) if HAS_GMPY: integers = ZZ if isinstance(element, integers.tp): return self.convert_from(element, integers) rationals = QQ if isinstance(element, rationals.tp): return self.convert_from(element, rationals) if isinstance(element, float): parent = RealField(tol=False) return self.convert_from(parent(element), parent) if isinstance(element, complex): parent = ComplexField(tol=False) return self.convert_from(parent(element), parent) if isinstance(element, DomainElement): return self.convert_from(element, element.parent()) # TODO: implement this in from_ methods if self.is_Numerical and getattr(element, 'is_ground', False): return self.convert(element.LC()) if isinstance(element, Basic): try: return self.from_sympy(element) except (TypeError, ValueError): pass else: # TODO: remove this branch if not is_sequence(element): try: element = sympify(element, strict=True) if isinstance(element, Basic): return self.from_sympy(element) except (TypeError, ValueError): pass raise CoercionFailed("Cannot convert %s of type %s to %s" % (element, type(element), self)) def of_type(self, element): """Check if ``a`` is of type ``dtype``. """ return isinstance(element, self.tp) # XXX: this isn't correct, e.g. PolyElement def __contains__(self, a): """Check if ``a`` belongs to this domain. """ try: if _not_a_coeff(a): raise CoercionFailed self.convert(a) # this might raise, too except CoercionFailed: return False return True def to_sympy(self, a): """Convert domain element *a* to a SymPy expression (Expr). Explanation =========== Convert a :py:class:`~.Domain` element *a* to :py:class:`~.Expr`. Most public SymPy functions work with objects of type :py:class:`~.Expr`. The elements of a :py:class:`~.Domain` have a different internal representation. It is not possible to mix domain elements with :py:class:`~.Expr` so each domain has :py:meth:`~.Domain.to_sympy` and :py:meth:`~.Domain.from_sympy` methods to convert its domain elements to and from :py:class:`~.Expr`. Parameters ========== a: domain element An element of this :py:class:`~.Domain`. Returns ======= expr: Expr A normal SymPy expression of type :py:class:`~.Expr`. Examples ======== Construct an element of the :ref:`QQ` domain and then convert it to :py:class:`~.Expr`. >>> from sympy import QQ, Expr >>> q_domain = QQ(2) >>> q_domain 2 >>> q_expr = QQ.to_sympy(q_domain) >>> q_expr 2 Although the printed forms look similar these objects are not of the same type. >>> isinstance(q_domain, Expr) False >>> isinstance(q_expr, Expr) True Construct an element of :ref:`K[x]` and convert to :py:class:`~.Expr`. >>> from sympy import Symbol >>> x = Symbol('x') >>> K = QQ[x] >>> x_domain = K.gens[0] # generator x as a domain element >>> p_domain = x_domain**2/3 + 1 >>> p_domain 1/3*x**2 + 1 >>> p_expr = K.to_sympy(p_domain) >>> p_expr x**2/3 + 1 The :py:meth:`~.Domain.from_sympy` method is used for the opposite conversion from a normal SymPy expression to a domain element. >>> p_domain == p_expr False >>> K.from_sympy(p_expr) == p_domain True >>> K.to_sympy(p_domain) == p_expr True >>> K.from_sympy(K.to_sympy(p_domain)) == p_domain True >>> K.to_sympy(K.from_sympy(p_expr)) == p_expr True The :py:meth:`~.Domain.from_sympy` method makes it easier to construct domain elements interactively. >>> from sympy import Symbol >>> x = Symbol('x') >>> K = QQ[x] >>> K.from_sympy(x**2/3 + 1) 1/3*x**2 + 1 See also ======== from_sympy convert_from """ raise NotImplementedError def from_sympy(self, a): """Convert a SymPy expression to an element of this domain. Explanation =========== See :py:meth:`~.Domain.to_sympy` for explanation and examples. Parameters ========== expr: Expr A normal SymPy expression of type :py:class:`~.Expr`. Returns ======= a: domain element An element of this :py:class:`~.Domain`. See also ======== to_sympy convert_from """ raise NotImplementedError def sum(self, args): return sum(args) def from_FF(K1, a, K0): """Convert ``ModularInteger(int)`` to ``dtype``. """ return None def from_FF_python(K1, a, K0): """Convert ``ModularInteger(int)`` to ``dtype``. """ return None def from_ZZ_python(K1, a, K0): """Convert a Python ``int`` object to ``dtype``. """ return None def from_QQ_python(K1, a, K0): """Convert a Python ``Fraction`` object to ``dtype``. """ return None def from_FF_gmpy(K1, a, K0): """Convert ``ModularInteger(mpz)`` to ``dtype``. """ return None def from_ZZ_gmpy(K1, a, K0): """Convert a GMPY ``mpz`` object to ``dtype``. """ return None def from_QQ_gmpy(K1, a, K0): """Convert a GMPY ``mpq`` object to ``dtype``. """ return None def from_RealField(K1, a, K0): """Convert a real element object to ``dtype``. """ return None def from_ComplexField(K1, a, K0): """Convert a complex element to ``dtype``. """ return None def from_AlgebraicField(K1, a, K0): """Convert an algebraic number to ``dtype``. """ return None def from_PolynomialRing(K1, a, K0): """Convert a polynomial to ``dtype``. """ if a.is_ground: return K1.convert(a.LC, K0.dom) def from_FractionField(K1, a, K0): """Convert a rational function to ``dtype``. """ return None def from_MonogenicFiniteExtension(K1, a, K0): """Convert an ``ExtensionElement`` to ``dtype``. """ return K1.convert_from(a.rep, K0.ring) def from_ExpressionDomain(K1, a, K0): """Convert a ``EX`` object to ``dtype``. """ return K1.from_sympy(a.ex) def from_ExpressionRawDomain(K1, a, K0): """Convert a ``EX`` object to ``dtype``. """ return K1.from_sympy(a) def from_GlobalPolynomialRing(K1, a, K0): """Convert a polynomial to ``dtype``. """ if a.degree() <= 0: return K1.convert(a.LC(), K0.dom) def from_GeneralizedPolynomialRing(K1, a, K0): return K1.from_FractionField(a, K0) def unify_with_symbols(K0, K1, symbols): if (K0.is_Composite and (set(K0.symbols) & set(symbols))) or (K1.is_Composite and (set(K1.symbols) & set(symbols))): raise UnificationFailed("Cannot unify %s with %s, given %s generators" % (K0, K1, tuple(symbols))) return K0.unify(K1) def unify(K0, K1, symbols=None): """ Construct a minimal domain that contains elements of ``K0`` and ``K1``. Known domains (from smallest to largest): - ``GF(p)`` - ``ZZ`` - ``QQ`` - ``RR(prec, tol)`` - ``CC(prec, tol)`` - ``ALG(a, b, c)`` - ``K[x, y, z]`` - ``K(x, y, z)`` - ``EX`` """ if symbols is not None: return K0.unify_with_symbols(K1, symbols) if K0 == K1: return K0 if K0.is_EXRAW: return K0 if K1.is_EXRAW: return K1 if K0.is_EX: return K0 if K1.is_EX: return K1 if K0.is_FiniteExtension or K1.is_FiniteExtension: if K1.is_FiniteExtension: K0, K1 = K1, K0 if K1.is_FiniteExtension: # Unifying two extensions. # Try to ensure that K0.unify(K1) == K1.unify(K0) if list(ordered([K0.modulus, K1.modulus]))[1] == K0.modulus: K0, K1 = K1, K0 return K1.set_domain(K0) else: # Drop the generator from other and unify with the base domain K1 = K1.drop(K0.symbol) K1 = K0.domain.unify(K1) return K0.set_domain(K1) if K0.is_Composite or K1.is_Composite: K0_ground = K0.dom if K0.is_Composite else K0 K1_ground = K1.dom if K1.is_Composite else K1 K0_symbols = K0.symbols if K0.is_Composite else () K1_symbols = K1.symbols if K1.is_Composite else () domain = K0_ground.unify(K1_ground) symbols = _unify_gens(K0_symbols, K1_symbols) order = K0.order if K0.is_Composite else K1.order if ((K0.is_FractionField and K1.is_PolynomialRing or K1.is_FractionField and K0.is_PolynomialRing) and (not K0_ground.is_Field or not K1_ground.is_Field) and domain.is_Field and domain.has_assoc_Ring): domain = domain.get_ring() if K0.is_Composite and (not K1.is_Composite or K0.is_FractionField or K1.is_PolynomialRing): cls = K0.__class__ else: cls = K1.__class__ from sympy.polys.domains.old_polynomialring import GlobalPolynomialRing if cls == GlobalPolynomialRing: return cls(domain, symbols) return cls(domain, symbols, order) def mkinexact(cls, K0, K1): prec = max(K0.precision, K1.precision) tol = max(K0.tolerance, K1.tolerance) return cls(prec=prec, tol=tol) if K1.is_ComplexField: K0, K1 = K1, K0 if K0.is_ComplexField: if K1.is_ComplexField or K1.is_RealField: return mkinexact(K0.__class__, K0, K1) else: return K0 if K1.is_RealField: K0, K1 = K1, K0 if K0.is_RealField: if K1.is_RealField: return mkinexact(K0.__class__, K0, K1) elif K1.is_GaussianRing or K1.is_GaussianField: from sympy.polys.domains.complexfield import ComplexField return ComplexField(prec=K0.precision, tol=K0.tolerance) else: return K0 if K1.is_AlgebraicField: K0, K1 = K1, K0 if K0.is_AlgebraicField: if K1.is_GaussianRing: K1 = K1.get_field() if K1.is_GaussianField: K1 = K1.as_AlgebraicField() if K1.is_AlgebraicField: return K0.__class__(K0.dom.unify(K1.dom), *_unify_gens(K0.orig_ext, K1.orig_ext)) else: return K0 if K0.is_GaussianField: return K0 if K1.is_GaussianField: return K1 if K0.is_GaussianRing: if K1.is_RationalField: K0 = K0.get_field() return K0 if K1.is_GaussianRing: if K0.is_RationalField: K1 = K1.get_field() return K1 if K0.is_RationalField: return K0 if K1.is_RationalField: return K1 if K0.is_IntegerRing: return K0 if K1.is_IntegerRing: return K1 if K0.is_FiniteField and K1.is_FiniteField: return K0.__class__(max(K0.mod, K1.mod, key=default_sort_key)) from sympy.polys.domains import EX return EX def __eq__(self, other): """Returns ``True`` if two domains are equivalent. """ return isinstance(other, Domain) and self.dtype == other.dtype def __ne__(self, other): """Returns ``False`` if two domains are equivalent. """ return not self == other def map(self, seq): """Rersively apply ``self`` to all elements of ``seq``. """ result = [] for elt in seq: if isinstance(elt, list): result.append(self.map(elt)) else: result.append(self(elt)) return result def get_ring(self): """Returns a ring associated with ``self``. """ raise DomainError('there is no ring associated with %s' % self) def get_field(self): """Returns a field associated with ``self``. """ raise DomainError('there is no field associated with %s' % self) def get_exact(self): """Returns an exact domain associated with ``self``. """ return self def __getitem__(self, symbols): """The mathematical way to make a polynomial ring. """ if hasattr(symbols, '__iter__'): return self.poly_ring(*symbols) else: return self.poly_ring(symbols) def poly_ring(self, *symbols, order=lex): """Returns a polynomial ring, i.e. `K[X]`. """ from sympy.polys.domains.polynomialring import PolynomialRing return PolynomialRing(self, symbols, order) def frac_field(self, *symbols, order=lex): """Returns a fraction field, i.e. `K(X)`. """ from sympy.polys.domains.fractionfield import FractionField return FractionField(self, symbols, order) def old_poly_ring(self, *symbols, **kwargs): """Returns a polynomial ring, i.e. `K[X]`. """ from sympy.polys.domains.old_polynomialring import PolynomialRing return PolynomialRing(self, *symbols, **kwargs) def old_frac_field(self, *symbols, **kwargs): """Returns a fraction field, i.e. `K(X)`. """ from sympy.polys.domains.old_fractionfield import FractionField return FractionField(self, *symbols, **kwargs) def algebraic_field(self, *extension): r"""Returns an algebraic field, i.e. `K(\alpha, \ldots)`. """ raise DomainError("Cannot create algebraic field over %s" % self) def inject(self, *symbols): """Inject generators into this domain. """ raise NotImplementedError def drop(self, *symbols): """Drop generators from this domain. """ if self.is_Simple: return self raise NotImplementedError # pragma: no cover def is_zero(self, a): """Returns True if ``a`` is zero. """ return not a def is_one(self, a): """Returns True if ``a`` is one. """ return a == self.one def is_positive(self, a): """Returns True if ``a`` is positive. """ return a > 0 def is_negative(self, a): """Returns True if ``a`` is negative. """ return a < 0 def is_nonpositive(self, a): """Returns True if ``a`` is non-positive. """ return a <= 0 def is_nonnegative(self, a): """Returns True if ``a`` is non-negative. """ return a >= 0 def canonical_unit(self, a): if self.is_negative(a): return -self.one else: return self.one def abs(self, a): """Absolute value of ``a``, implies ``__abs__``. """ return abs(a) def neg(self, a): """Returns ``a`` negated, implies ``__neg__``. """ return -a def pos(self, a): """Returns ``a`` positive, implies ``__pos__``. """ return +a def add(self, a, b): """Sum of ``a`` and ``b``, implies ``__add__``. """ return a + b def sub(self, a, b): """Difference of ``a`` and ``b``, implies ``__sub__``. """ return a - b def mul(self, a, b): """Product of ``a`` and ``b``, implies ``__mul__``. """ return a * b def pow(self, a, b): """Raise ``a`` to power ``b``, implies ``__pow__``. """ return a ** b def exquo(self, a, b): """Exact quotient of *a* and *b*. Analogue of ``a / b``. Explanation =========== This is essentially the same as ``a / b`` except that an error will be raised if the division is inexact (if there is any remainder) and the result will always be a domain element. When working in a :py:class:`~.Domain` that is not a :py:class:`~.Field` (e.g. :ref:`ZZ` or :ref:`K[x]`) ``exquo`` should be used instead of ``/``. The key invariant is that if ``q = K.exquo(a, b)`` (and ``exquo`` does not raise an exception) then ``a == b*q``. Examples ======== We can use ``K.exquo`` instead of ``/`` for exact division. >>> from sympy import ZZ >>> ZZ.exquo(ZZ(4), ZZ(2)) 2 >>> ZZ.exquo(ZZ(5), ZZ(2)) Traceback (most recent call last): ... ExactQuotientFailed: 2 does not divide 5 in ZZ Over a :py:class:`~.Field` such as :ref:`QQ`, division (with nonzero divisor) is always exact so in that case ``/`` can be used instead of :py:meth:`~.Domain.exquo`. >>> from sympy import QQ >>> QQ.exquo(QQ(5), QQ(2)) 5/2 >>> QQ(5) / QQ(2) 5/2 Parameters ========== a: domain element The dividend b: domain element The divisor Returns ======= q: domain element The exact quotient Raises ====== ExactQuotientFailed: if exact division is not possible. ZeroDivisionError: when the divisor is zero. See also ======== quo: Analogue of ``a // b`` rem: Analogue of ``a % b`` div: Analogue of ``divmod(a, b)`` Notes ===== Since the default :py:attr:`~.Domain.dtype` for :ref:`ZZ` is ``int`` (or ``mpz``) division as ``a / b`` should not be used as it would give a ``float``. >>> ZZ(4) / ZZ(2) 2.0 >>> ZZ(5) / ZZ(2) 2.5 Using ``/`` with :ref:`ZZ` will lead to incorrect results so :py:meth:`~.Domain.exquo` should be used instead. """ raise NotImplementedError def quo(self, a, b): """Quotient of *a* and *b*. Analogue of ``a // b``. ``K.quo(a, b)`` is equivalent to ``K.div(a, b)[0]``. See :py:meth:`~.Domain.div` for more explanation. See also ======== rem: Analogue of ``a % b`` div: Analogue of ``divmod(a, b)`` exquo: Analogue of ``a / b`` """ raise NotImplementedError def rem(self, a, b): """Modulo division of *a* and *b*. Analogue of ``a % b``. ``K.rem(a, b)`` is equivalent to ``K.div(a, b)[1]``. See :py:meth:`~.Domain.div` for more explanation. See also ======== quo: Analogue of ``a // b`` div: Analogue of ``divmod(a, b)`` exquo: Analogue of ``a / b`` """ raise NotImplementedError def div(self, a, b): """Quotient and remainder for *a* and *b*. Analogue of ``divmod(a, b)`` Explanation =========== This is essentially the same as ``divmod(a, b)`` except that is more consistent when working over some :py:class:`~.Field` domains such as :ref:`QQ`. When working over an arbitrary :py:class:`~.Domain` the :py:meth:`~.Domain.div` method should be used instead of ``divmod``. The key invariant is that if ``q, r = K.div(a, b)`` then ``a == b*q + r``. The result of ``K.div(a, b)`` is the same as the tuple ``(K.quo(a, b), K.rem(a, b))`` except that if both quotient and remainder are needed then it is more efficient to use :py:meth:`~.Domain.div`. Examples ======== We can use ``K.div`` instead of ``divmod`` for floor division and remainder. >>> from sympy import ZZ, QQ >>> ZZ.div(ZZ(5), ZZ(2)) (2, 1) If ``K`` is a :py:class:`~.Field` then the division is always exact with a remainder of :py:attr:`~.Domain.zero`. >>> QQ.div(QQ(5), QQ(2)) (5/2, 0) Parameters ========== a: domain element The dividend b: domain element The divisor Returns ======= (q, r): tuple of domain elements The quotient and remainder Raises ====== ZeroDivisionError: when the divisor is zero. See also ======== quo: Analogue of ``a // b`` rem: Analogue of ``a % b`` exquo: Analogue of ``a / b`` Notes ===== If ``gmpy`` is installed then the ``gmpy.mpq`` type will be used as the :py:attr:`~.Domain.dtype` for :ref:`QQ`. The ``gmpy.mpq`` type defines ``divmod`` in a way that is undesirable so :py:meth:`~.Domain.div` should be used instead of ``divmod``. >>> a = QQ(1) >>> b = QQ(3, 2) >>> a # doctest: +SKIP mpq(1,1) >>> b # doctest: +SKIP mpq(3,2) >>> divmod(a, b) # doctest: +SKIP (mpz(0), mpq(1,1)) >>> QQ.div(a, b) # doctest: +SKIP (mpq(2,3), mpq(0,1)) Using ``//`` or ``%`` with :ref:`QQ` will lead to incorrect results so :py:meth:`~.Domain.div` should be used instead. """ raise NotImplementedError def invert(self, a, b): """Returns inversion of ``a mod b``, implies something. """ raise NotImplementedError def revert(self, a): """Returns ``a**(-1)`` if possible. """ raise NotImplementedError def numer(self, a): """Returns numerator of ``a``. """ raise NotImplementedError def denom(self, a): """Returns denominator of ``a``. """ raise NotImplementedError def half_gcdex(self, a, b): """Half extended GCD of ``a`` and ``b``. """ s, t, h = self.gcdex(a, b) return s, h def gcdex(self, a, b): """Extended GCD of ``a`` and ``b``. """ raise NotImplementedError def cofactors(self, a, b): """Returns GCD and cofactors of ``a`` and ``b``. """ gcd = self.gcd(a, b) cfa = self.quo(a, gcd) cfb = self.quo(b, gcd) return gcd, cfa, cfb def gcd(self, a, b): """Returns GCD of ``a`` and ``b``. """ raise NotImplementedError def lcm(self, a, b): """Returns LCM of ``a`` and ``b``. """ raise NotImplementedError def log(self, a, b): """Returns b-base logarithm of ``a``. """ raise NotImplementedError def sqrt(self, a): """Returns square root of ``a``. """ raise NotImplementedError def evalf(self, a, prec=None, **options): """Returns numerical approximation of ``a``. """ return self.to_sympy(a).evalf(prec, **options) n = evalf def real(self, a): return a def imag(self, a): return self.zero def almosteq(self, a, b, tolerance=None): """Check if ``a`` and ``b`` are almost equal. """ return a == b def characteristic(self): """Return the characteristic of this domain. """ raise NotImplementedError('characteristic()') __all__ = ['Domain']