|
"""Domains of Gaussian type.""" |
|
|
|
from __future__ import annotations |
|
from sympy.core.numbers import I |
|
from sympy.polys.polyclasses import DMP |
|
from sympy.polys.polyerrors import CoercionFailed |
|
from sympy.polys.domains.integerring import ZZ |
|
from sympy.polys.domains.rationalfield import QQ |
|
from sympy.polys.domains.algebraicfield import AlgebraicField |
|
from sympy.polys.domains.domain import Domain |
|
from sympy.polys.domains.domainelement import DomainElement |
|
from sympy.polys.domains.field import Field |
|
from sympy.polys.domains.ring import Ring |
|
|
|
|
|
class GaussianElement(DomainElement): |
|
"""Base class for elements of Gaussian type domains.""" |
|
base: Domain |
|
_parent: Domain |
|
|
|
__slots__ = ('x', 'y') |
|
|
|
def __new__(cls, x, y=0): |
|
conv = cls.base.convert |
|
return cls.new(conv(x), conv(y)) |
|
|
|
@classmethod |
|
def new(cls, x, y): |
|
"""Create a new GaussianElement of the same domain.""" |
|
obj = super().__new__(cls) |
|
obj.x = x |
|
obj.y = y |
|
return obj |
|
|
|
def parent(self): |
|
"""The domain that this is an element of (ZZ_I or QQ_I)""" |
|
return self._parent |
|
|
|
def __hash__(self): |
|
return hash((self.x, self.y)) |
|
|
|
def __eq__(self, other): |
|
if isinstance(other, self.__class__): |
|
return self.x == other.x and self.y == other.y |
|
else: |
|
return NotImplemented |
|
|
|
def __lt__(self, other): |
|
if not isinstance(other, GaussianElement): |
|
return NotImplemented |
|
return [self.y, self.x] < [other.y, other.x] |
|
|
|
def __pos__(self): |
|
return self |
|
|
|
def __neg__(self): |
|
return self.new(-self.x, -self.y) |
|
|
|
def __repr__(self): |
|
return "%s(%s, %s)" % (self._parent.rep, self.x, self.y) |
|
|
|
def __str__(self): |
|
return str(self._parent.to_sympy(self)) |
|
|
|
@classmethod |
|
def _get_xy(cls, other): |
|
if not isinstance(other, cls): |
|
try: |
|
other = cls._parent.convert(other) |
|
except CoercionFailed: |
|
return None, None |
|
return other.x, other.y |
|
|
|
def __add__(self, other): |
|
x, y = self._get_xy(other) |
|
if x is not None: |
|
return self.new(self.x + x, self.y + y) |
|
else: |
|
return NotImplemented |
|
|
|
__radd__ = __add__ |
|
|
|
def __sub__(self, other): |
|
x, y = self._get_xy(other) |
|
if x is not None: |
|
return self.new(self.x - x, self.y - y) |
|
else: |
|
return NotImplemented |
|
|
|
def __rsub__(self, other): |
|
x, y = self._get_xy(other) |
|
if x is not None: |
|
return self.new(x - self.x, y - self.y) |
|
else: |
|
return NotImplemented |
|
|
|
def __mul__(self, other): |
|
x, y = self._get_xy(other) |
|
if x is not None: |
|
return self.new(self.x*x - self.y*y, self.x*y + self.y*x) |
|
else: |
|
return NotImplemented |
|
|
|
__rmul__ = __mul__ |
|
|
|
def __pow__(self, exp): |
|
if exp == 0: |
|
return self.new(1, 0) |
|
if exp < 0: |
|
self, exp = 1/self, -exp |
|
if exp == 1: |
|
return self |
|
pow2 = self |
|
prod = self if exp % 2 else self._parent.one |
|
exp //= 2 |
|
while exp: |
|
pow2 *= pow2 |
|
if exp % 2: |
|
prod *= pow2 |
|
exp //= 2 |
|
return prod |
|
|
|
def __bool__(self): |
|
return bool(self.x) or bool(self.y) |
|
|
|
def quadrant(self): |
|
"""Return quadrant index 0-3. |
|
|
|
0 is included in quadrant 0. |
|
""" |
|
if self.y > 0: |
|
return 0 if self.x > 0 else 1 |
|
elif self.y < 0: |
|
return 2 if self.x < 0 else 3 |
|
else: |
|
return 0 if self.x >= 0 else 2 |
|
|
|
def __rdivmod__(self, other): |
|
try: |
|
other = self._parent.convert(other) |
|
except CoercionFailed: |
|
return NotImplemented |
|
else: |
|
return other.__divmod__(self) |
|
|
|
def __rtruediv__(self, other): |
|
try: |
|
other = QQ_I.convert(other) |
|
except CoercionFailed: |
|
return NotImplemented |
|
else: |
|
return other.__truediv__(self) |
|
|
|
def __floordiv__(self, other): |
|
qr = self.__divmod__(other) |
|
return qr if qr is NotImplemented else qr[0] |
|
|
|
def __rfloordiv__(self, other): |
|
qr = self.__rdivmod__(other) |
|
return qr if qr is NotImplemented else qr[0] |
|
|
|
def __mod__(self, other): |
|
qr = self.__divmod__(other) |
|
return qr if qr is NotImplemented else qr[1] |
|
|
|
def __rmod__(self, other): |
|
qr = self.__rdivmod__(other) |
|
return qr if qr is NotImplemented else qr[1] |
|
|
|
|
|
class GaussianInteger(GaussianElement): |
|
"""Gaussian integer: domain element for :ref:`ZZ_I` |
|
|
|
>>> from sympy import ZZ_I |
|
>>> z = ZZ_I(2, 3) |
|
>>> z |
|
(2 + 3*I) |
|
>>> type(z) |
|
<class 'sympy.polys.domains.gaussiandomains.GaussianInteger'> |
|
""" |
|
base = ZZ |
|
|
|
def __truediv__(self, other): |
|
"""Return a Gaussian rational.""" |
|
return QQ_I.convert(self)/other |
|
|
|
def __divmod__(self, other): |
|
if not other: |
|
raise ZeroDivisionError('divmod({}, 0)'.format(self)) |
|
x, y = self._get_xy(other) |
|
if x is None: |
|
return NotImplemented |
|
|
|
|
|
|
|
a, b = self.x*x + self.y*y, -self.x*y + self.y*x |
|
c = x*x + y*y |
|
|
|
|
|
|
|
qx = (2*a + c) // (2*c) |
|
qy = (2*b + c) // (2*c) |
|
|
|
q = GaussianInteger(qx, qy) |
|
|
|
|
|
|
|
return q, self - q*other |
|
|
|
|
|
class GaussianRational(GaussianElement): |
|
"""Gaussian rational: domain element for :ref:`QQ_I` |
|
|
|
>>> from sympy import QQ_I, QQ |
|
>>> z = QQ_I(QQ(2, 3), QQ(4, 5)) |
|
>>> z |
|
(2/3 + 4/5*I) |
|
>>> type(z) |
|
<class 'sympy.polys.domains.gaussiandomains.GaussianRational'> |
|
""" |
|
base = QQ |
|
|
|
def __truediv__(self, other): |
|
"""Return a Gaussian rational.""" |
|
if not other: |
|
raise ZeroDivisionError('{} / 0'.format(self)) |
|
x, y = self._get_xy(other) |
|
if x is None: |
|
return NotImplemented |
|
c = x*x + y*y |
|
|
|
return GaussianRational((self.x*x + self.y*y)/c, |
|
(-self.x*y + self.y*x)/c) |
|
|
|
def __divmod__(self, other): |
|
try: |
|
other = self._parent.convert(other) |
|
except CoercionFailed: |
|
return NotImplemented |
|
if not other: |
|
raise ZeroDivisionError('{} % 0'.format(self)) |
|
else: |
|
return self/other, QQ_I.zero |
|
|
|
|
|
class GaussianDomain(): |
|
"""Base class for Gaussian domains.""" |
|
dom: Domain |
|
|
|
is_Numerical = True |
|
is_Exact = True |
|
|
|
has_assoc_Ring = True |
|
has_assoc_Field = True |
|
|
|
def to_sympy(self, a): |
|
"""Convert ``a`` to a SymPy object. """ |
|
conv = self.dom.to_sympy |
|
return conv(a.x) + I*conv(a.y) |
|
|
|
def from_sympy(self, a): |
|
"""Convert a SymPy object to ``self.dtype``.""" |
|
r, b = a.as_coeff_Add() |
|
x = self.dom.from_sympy(r) |
|
if not b: |
|
return self.new(x, 0) |
|
r, b = b.as_coeff_Mul() |
|
y = self.dom.from_sympy(r) |
|
if b is I: |
|
return self.new(x, y) |
|
else: |
|
raise CoercionFailed("{} is not Gaussian".format(a)) |
|
|
|
def inject(self, *gens): |
|
"""Inject generators into this domain. """ |
|
return self.poly_ring(*gens) |
|
|
|
def canonical_unit(self, d): |
|
unit = self.units[-d.quadrant()] |
|
return unit |
|
|
|
def is_negative(self, element): |
|
"""Returns ``False`` for any ``GaussianElement``. """ |
|
return False |
|
|
|
def is_positive(self, element): |
|
"""Returns ``False`` for any ``GaussianElement``. """ |
|
return False |
|
|
|
def is_nonnegative(self, element): |
|
"""Returns ``False`` for any ``GaussianElement``. """ |
|
return False |
|
|
|
def is_nonpositive(self, element): |
|
"""Returns ``False`` for any ``GaussianElement``. """ |
|
return False |
|
|
|
def from_ZZ_gmpy(K1, a, K0): |
|
"""Convert a GMPY mpz to ``self.dtype``.""" |
|
return K1(a) |
|
|
|
def from_ZZ(K1, a, K0): |
|
"""Convert a ZZ_python element to ``self.dtype``.""" |
|
return K1(a) |
|
|
|
def from_ZZ_python(K1, a, K0): |
|
"""Convert a ZZ_python element to ``self.dtype``.""" |
|
return K1(a) |
|
|
|
def from_QQ(K1, a, K0): |
|
"""Convert a GMPY mpq to ``self.dtype``.""" |
|
return K1(a) |
|
|
|
def from_QQ_gmpy(K1, a, K0): |
|
"""Convert a GMPY mpq to ``self.dtype``.""" |
|
return K1(a) |
|
|
|
def from_QQ_python(K1, a, K0): |
|
"""Convert a QQ_python element to ``self.dtype``.""" |
|
return K1(a) |
|
|
|
def from_AlgebraicField(K1, a, K0): |
|
"""Convert an element from ZZ<I> or QQ<I> to ``self.dtype``.""" |
|
if K0.ext.args[0] == I: |
|
return K1.from_sympy(K0.to_sympy(a)) |
|
|
|
|
|
class GaussianIntegerRing(GaussianDomain, Ring): |
|
r"""Ring of Gaussian integers ``ZZ_I`` |
|
|
|
The :ref:`ZZ_I` domain represents the `Gaussian integers`_ `\mathbb{Z}[i]` |
|
as a :py:class:`~.Domain` in the domain system (see |
|
:ref:`polys-domainsintro`). |
|
|
|
By default a :py:class:`~.Poly` created from an expression with |
|
coefficients that are combinations of integers and ``I`` (`\sqrt{-1}`) |
|
will have the domain :ref:`ZZ_I`. |
|
|
|
>>> from sympy import Poly, Symbol, I |
|
>>> x = Symbol('x') |
|
>>> p = Poly(x**2 + I) |
|
>>> p |
|
Poly(x**2 + I, x, domain='ZZ_I') |
|
>>> p.domain |
|
ZZ_I |
|
|
|
The :ref:`ZZ_I` domain can be used to factorise polynomials that are |
|
reducible over the Gaussian integers. |
|
|
|
>>> from sympy import factor |
|
>>> factor(x**2 + 1) |
|
x**2 + 1 |
|
>>> factor(x**2 + 1, domain='ZZ_I') |
|
(x - I)*(x + I) |
|
|
|
The corresponding `field of fractions`_ is the domain of the Gaussian |
|
rationals :ref:`QQ_I`. Conversely :ref:`ZZ_I` is the `ring of integers`_ |
|
of :ref:`QQ_I`. |
|
|
|
>>> from sympy import ZZ_I, QQ_I |
|
>>> ZZ_I.get_field() |
|
QQ_I |
|
>>> QQ_I.get_ring() |
|
ZZ_I |
|
|
|
When using the domain directly :ref:`ZZ_I` can be used as a constructor. |
|
|
|
>>> ZZ_I(3, 4) |
|
(3 + 4*I) |
|
>>> ZZ_I(5) |
|
(5 + 0*I) |
|
|
|
The domain elements of :ref:`ZZ_I` are instances of |
|
:py:class:`~.GaussianInteger` which support the rings operations |
|
``+,-,*,**``. |
|
|
|
>>> z1 = ZZ_I(5, 1) |
|
>>> z2 = ZZ_I(2, 3) |
|
>>> z1 |
|
(5 + 1*I) |
|
>>> z2 |
|
(2 + 3*I) |
|
>>> z1 + z2 |
|
(7 + 4*I) |
|
>>> z1 * z2 |
|
(7 + 17*I) |
|
>>> z1 ** 2 |
|
(24 + 10*I) |
|
|
|
Both floor (``//``) and modulo (``%``) division work with |
|
:py:class:`~.GaussianInteger` (see the :py:meth:`~.Domain.div` method). |
|
|
|
>>> z3, z4 = ZZ_I(5), ZZ_I(1, 3) |
|
>>> z3 // z4 # floor division |
|
(1 + -1*I) |
|
>>> z3 % z4 # modulo division (remainder) |
|
(1 + -2*I) |
|
>>> (z3//z4)*z4 + z3%z4 == z3 |
|
True |
|
|
|
True division (``/``) in :ref:`ZZ_I` gives an element of :ref:`QQ_I`. The |
|
:py:meth:`~.Domain.exquo` method can be used to divide in :ref:`ZZ_I` when |
|
exact division is possible. |
|
|
|
>>> z1 / z2 |
|
(1 + -1*I) |
|
>>> ZZ_I.exquo(z1, z2) |
|
(1 + -1*I) |
|
>>> z3 / z4 |
|
(1/2 + -3/2*I) |
|
>>> ZZ_I.exquo(z3, z4) |
|
Traceback (most recent call last): |
|
... |
|
ExactQuotientFailed: (1 + 3*I) does not divide (5 + 0*I) in ZZ_I |
|
|
|
The :py:meth:`~.Domain.gcd` method can be used to compute the `gcd`_ of any |
|
two elements. |
|
|
|
>>> ZZ_I.gcd(ZZ_I(10), ZZ_I(2)) |
|
(2 + 0*I) |
|
>>> ZZ_I.gcd(ZZ_I(5), ZZ_I(2, 1)) |
|
(2 + 1*I) |
|
|
|
.. _Gaussian integers: https://en.wikipedia.org/wiki/Gaussian_integer |
|
.. _gcd: https://en.wikipedia.org/wiki/Greatest_common_divisor |
|
|
|
""" |
|
dom = ZZ |
|
mod = DMP([ZZ.one, ZZ.zero, ZZ.one], ZZ) |
|
dtype = GaussianInteger |
|
zero = dtype(ZZ(0), ZZ(0)) |
|
one = dtype(ZZ(1), ZZ(0)) |
|
imag_unit = dtype(ZZ(0), ZZ(1)) |
|
units = (one, imag_unit, -one, -imag_unit) |
|
|
|
rep = 'ZZ_I' |
|
|
|
is_GaussianRing = True |
|
is_ZZ_I = True |
|
is_PID = True |
|
|
|
def __init__(self): |
|
"""For constructing ZZ_I.""" |
|
|
|
def __eq__(self, other): |
|
"""Returns ``True`` if two domains are equivalent. """ |
|
if isinstance(other, GaussianIntegerRing): |
|
return True |
|
else: |
|
return NotImplemented |
|
|
|
def __hash__(self): |
|
"""Compute hash code of ``self``. """ |
|
return hash('ZZ_I') |
|
|
|
@property |
|
def has_CharacteristicZero(self): |
|
return True |
|
|
|
def characteristic(self): |
|
return 0 |
|
|
|
def get_ring(self): |
|
"""Returns a ring associated with ``self``. """ |
|
return self |
|
|
|
def get_field(self): |
|
"""Returns a field associated with ``self``. """ |
|
return QQ_I |
|
|
|
def normalize(self, d, *args): |
|
"""Return first quadrant element associated with ``d``. |
|
|
|
Also multiply the other arguments by the same power of i. |
|
""" |
|
unit = self.canonical_unit(d) |
|
d *= unit |
|
args = tuple(a*unit for a in args) |
|
return (d,) + args if args else d |
|
|
|
def gcd(self, a, b): |
|
"""Greatest common divisor of a and b over ZZ_I.""" |
|
while b: |
|
a, b = b, a % b |
|
return self.normalize(a) |
|
|
|
def gcdex(self, a, b): |
|
"""Return x, y, g such that x * a + y * b = g = gcd(a, b)""" |
|
x_a = self.one |
|
x_b = self.zero |
|
y_a = self.zero |
|
y_b = self.one |
|
while b: |
|
q = a // b |
|
a, b = b, a - q * b |
|
x_a, x_b = x_b, x_a - q * x_b |
|
y_a, y_b = y_b, y_a - q * y_b |
|
|
|
a, x_a, y_a = self.normalize(a, x_a, y_a) |
|
return x_a, y_a, a |
|
|
|
def lcm(self, a, b): |
|
"""Least common multiple of a and b over ZZ_I.""" |
|
return (a * b) // self.gcd(a, b) |
|
|
|
def from_GaussianIntegerRing(K1, a, K0): |
|
"""Convert a ZZ_I element to ZZ_I.""" |
|
return a |
|
|
|
def from_GaussianRationalField(K1, a, K0): |
|
"""Convert a QQ_I element to ZZ_I.""" |
|
return K1.new(ZZ.convert(a.x), ZZ.convert(a.y)) |
|
|
|
ZZ_I = GaussianInteger._parent = GaussianIntegerRing() |
|
|
|
|
|
class GaussianRationalField(GaussianDomain, Field): |
|
r"""Field of Gaussian rationals ``QQ_I`` |
|
|
|
The :ref:`QQ_I` domain represents the `Gaussian rationals`_ `\mathbb{Q}(i)` |
|
as a :py:class:`~.Domain` in the domain system (see |
|
:ref:`polys-domainsintro`). |
|
|
|
By default a :py:class:`~.Poly` created from an expression with |
|
coefficients that are combinations of rationals and ``I`` (`\sqrt{-1}`) |
|
will have the domain :ref:`QQ_I`. |
|
|
|
>>> from sympy import Poly, Symbol, I |
|
>>> x = Symbol('x') |
|
>>> p = Poly(x**2 + I/2) |
|
>>> p |
|
Poly(x**2 + I/2, x, domain='QQ_I') |
|
>>> p.domain |
|
QQ_I |
|
|
|
The polys option ``gaussian=True`` can be used to specify that the domain |
|
should be :ref:`QQ_I` even if the coefficients do not contain ``I`` or are |
|
all integers. |
|
|
|
>>> Poly(x**2) |
|
Poly(x**2, x, domain='ZZ') |
|
>>> Poly(x**2 + I) |
|
Poly(x**2 + I, x, domain='ZZ_I') |
|
>>> Poly(x**2/2) |
|
Poly(1/2*x**2, x, domain='QQ') |
|
>>> Poly(x**2, gaussian=True) |
|
Poly(x**2, x, domain='QQ_I') |
|
>>> Poly(x**2 + I, gaussian=True) |
|
Poly(x**2 + I, x, domain='QQ_I') |
|
>>> Poly(x**2/2, gaussian=True) |
|
Poly(1/2*x**2, x, domain='QQ_I') |
|
|
|
The :ref:`QQ_I` domain can be used to factorise polynomials that are |
|
reducible over the Gaussian rationals. |
|
|
|
>>> from sympy import factor, QQ_I |
|
>>> factor(x**2/4 + 1) |
|
(x**2 + 4)/4 |
|
>>> factor(x**2/4 + 1, domain='QQ_I') |
|
(x - 2*I)*(x + 2*I)/4 |
|
>>> factor(x**2/4 + 1, domain=QQ_I) |
|
(x - 2*I)*(x + 2*I)/4 |
|
|
|
It is also possible to specify the :ref:`QQ_I` domain explicitly with |
|
polys functions like :py:func:`~.apart`. |
|
|
|
>>> from sympy import apart |
|
>>> apart(1/(1 + x**2)) |
|
1/(x**2 + 1) |
|
>>> apart(1/(1 + x**2), domain=QQ_I) |
|
I/(2*(x + I)) - I/(2*(x - I)) |
|
|
|
The corresponding `ring of integers`_ is the domain of the Gaussian |
|
integers :ref:`ZZ_I`. Conversely :ref:`QQ_I` is the `field of fractions`_ |
|
of :ref:`ZZ_I`. |
|
|
|
>>> from sympy import ZZ_I, QQ_I, QQ |
|
>>> ZZ_I.get_field() |
|
QQ_I |
|
>>> QQ_I.get_ring() |
|
ZZ_I |
|
|
|
When using the domain directly :ref:`QQ_I` can be used as a constructor. |
|
|
|
>>> QQ_I(3, 4) |
|
(3 + 4*I) |
|
>>> QQ_I(5) |
|
(5 + 0*I) |
|
>>> QQ_I(QQ(2, 3), QQ(4, 5)) |
|
(2/3 + 4/5*I) |
|
|
|
The domain elements of :ref:`QQ_I` are instances of |
|
:py:class:`~.GaussianRational` which support the field operations |
|
``+,-,*,**,/``. |
|
|
|
>>> z1 = QQ_I(5, 1) |
|
>>> z2 = QQ_I(2, QQ(1, 2)) |
|
>>> z1 |
|
(5 + 1*I) |
|
>>> z2 |
|
(2 + 1/2*I) |
|
>>> z1 + z2 |
|
(7 + 3/2*I) |
|
>>> z1 * z2 |
|
(19/2 + 9/2*I) |
|
>>> z2 ** 2 |
|
(15/4 + 2*I) |
|
|
|
True division (``/``) in :ref:`QQ_I` gives an element of :ref:`QQ_I` and |
|
is always exact. |
|
|
|
>>> z1 / z2 |
|
(42/17 + -2/17*I) |
|
>>> QQ_I.exquo(z1, z2) |
|
(42/17 + -2/17*I) |
|
>>> z1 == (z1/z2)*z2 |
|
True |
|
|
|
Both floor (``//``) and modulo (``%``) division can be used with |
|
:py:class:`~.GaussianRational` (see :py:meth:`~.Domain.div`) |
|
but division is always exact so there is no remainder. |
|
|
|
>>> z1 // z2 |
|
(42/17 + -2/17*I) |
|
>>> z1 % z2 |
|
(0 + 0*I) |
|
>>> QQ_I.div(z1, z2) |
|
((42/17 + -2/17*I), (0 + 0*I)) |
|
>>> (z1//z2)*z2 + z1%z2 == z1 |
|
True |
|
|
|
.. _Gaussian rationals: https://en.wikipedia.org/wiki/Gaussian_rational |
|
""" |
|
dom = QQ |
|
mod = DMP([QQ.one, QQ.zero, QQ.one], QQ) |
|
dtype = GaussianRational |
|
zero = dtype(QQ(0), QQ(0)) |
|
one = dtype(QQ(1), QQ(0)) |
|
imag_unit = dtype(QQ(0), QQ(1)) |
|
units = (one, imag_unit, -one, -imag_unit) |
|
|
|
rep = 'QQ_I' |
|
|
|
is_GaussianField = True |
|
is_QQ_I = True |
|
|
|
def __init__(self): |
|
"""For constructing QQ_I.""" |
|
|
|
def __eq__(self, other): |
|
"""Returns ``True`` if two domains are equivalent. """ |
|
if isinstance(other, GaussianRationalField): |
|
return True |
|
else: |
|
return NotImplemented |
|
|
|
def __hash__(self): |
|
"""Compute hash code of ``self``. """ |
|
return hash('QQ_I') |
|
|
|
@property |
|
def has_CharacteristicZero(self): |
|
return True |
|
|
|
def characteristic(self): |
|
return 0 |
|
|
|
def get_ring(self): |
|
"""Returns a ring associated with ``self``. """ |
|
return ZZ_I |
|
|
|
def get_field(self): |
|
"""Returns a field associated with ``self``. """ |
|
return self |
|
|
|
def as_AlgebraicField(self): |
|
"""Get equivalent domain as an ``AlgebraicField``. """ |
|
return AlgebraicField(self.dom, I) |
|
|
|
def numer(self, a): |
|
"""Get the numerator of ``a``.""" |
|
ZZ_I = self.get_ring() |
|
return ZZ_I.convert(a * self.denom(a)) |
|
|
|
def denom(self, a): |
|
"""Get the denominator of ``a``.""" |
|
ZZ = self.dom.get_ring() |
|
QQ = self.dom |
|
ZZ_I = self.get_ring() |
|
denom_ZZ = ZZ.lcm(QQ.denom(a.x), QQ.denom(a.y)) |
|
return ZZ_I(denom_ZZ, ZZ.zero) |
|
|
|
def from_GaussianIntegerRing(K1, a, K0): |
|
"""Convert a ZZ_I element to QQ_I.""" |
|
return K1.new(a.x, a.y) |
|
|
|
def from_GaussianRationalField(K1, a, K0): |
|
"""Convert a QQ_I element to QQ_I.""" |
|
return a |
|
|
|
def from_ComplexField(K1, a, K0): |
|
"""Convert a ComplexField element to QQ_I.""" |
|
return K1.new(QQ.convert(a.real), QQ.convert(a.imag)) |
|
|
|
|
|
QQ_I = GaussianRational._parent = GaussianRationalField() |
|
|