gaussiandomains.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706
  1. """Domains of Gaussian type."""
  2. from __future__ import annotations
  3. from sympy.core.numbers import I
  4. from sympy.polys.polyclasses import DMP
  5. from sympy.polys.polyerrors import CoercionFailed
  6. from sympy.polys.domains.integerring import ZZ
  7. from sympy.polys.domains.rationalfield import QQ
  8. from sympy.polys.domains.algebraicfield import AlgebraicField
  9. from sympy.polys.domains.domain import Domain
  10. from sympy.polys.domains.domainelement import DomainElement
  11. from sympy.polys.domains.field import Field
  12. from sympy.polys.domains.ring import Ring
  13. class GaussianElement(DomainElement):
  14. """Base class for elements of Gaussian type domains."""
  15. base: Domain
  16. _parent: Domain
  17. __slots__ = ('x', 'y')
  18. def __new__(cls, x, y=0):
  19. conv = cls.base.convert
  20. return cls.new(conv(x), conv(y))
  21. @classmethod
  22. def new(cls, x, y):
  23. """Create a new GaussianElement of the same domain."""
  24. obj = super().__new__(cls)
  25. obj.x = x
  26. obj.y = y
  27. return obj
  28. def parent(self):
  29. """The domain that this is an element of (ZZ_I or QQ_I)"""
  30. return self._parent
  31. def __hash__(self):
  32. return hash((self.x, self.y))
  33. def __eq__(self, other):
  34. if isinstance(other, self.__class__):
  35. return self.x == other.x and self.y == other.y
  36. else:
  37. return NotImplemented
  38. def __lt__(self, other):
  39. if not isinstance(other, GaussianElement):
  40. return NotImplemented
  41. return [self.y, self.x] < [other.y, other.x]
  42. def __pos__(self):
  43. return self
  44. def __neg__(self):
  45. return self.new(-self.x, -self.y)
  46. def __repr__(self):
  47. return "%s(%s, %s)" % (self._parent.rep, self.x, self.y)
  48. def __str__(self):
  49. return str(self._parent.to_sympy(self))
  50. @classmethod
  51. def _get_xy(cls, other):
  52. if not isinstance(other, cls):
  53. try:
  54. other = cls._parent.convert(other)
  55. except CoercionFailed:
  56. return None, None
  57. return other.x, other.y
  58. def __add__(self, other):
  59. x, y = self._get_xy(other)
  60. if x is not None:
  61. return self.new(self.x + x, self.y + y)
  62. else:
  63. return NotImplemented
  64. __radd__ = __add__
  65. def __sub__(self, other):
  66. x, y = self._get_xy(other)
  67. if x is not None:
  68. return self.new(self.x - x, self.y - y)
  69. else:
  70. return NotImplemented
  71. def __rsub__(self, other):
  72. x, y = self._get_xy(other)
  73. if x is not None:
  74. return self.new(x - self.x, y - self.y)
  75. else:
  76. return NotImplemented
  77. def __mul__(self, other):
  78. x, y = self._get_xy(other)
  79. if x is not None:
  80. return self.new(self.x*x - self.y*y, self.x*y + self.y*x)
  81. else:
  82. return NotImplemented
  83. __rmul__ = __mul__
  84. def __pow__(self, exp):
  85. if exp == 0:
  86. return self.new(1, 0)
  87. if exp < 0:
  88. self, exp = 1/self, -exp
  89. if exp == 1:
  90. return self
  91. pow2 = self
  92. prod = self if exp % 2 else self._parent.one
  93. exp //= 2
  94. while exp:
  95. pow2 *= pow2
  96. if exp % 2:
  97. prod *= pow2
  98. exp //= 2
  99. return prod
  100. def __bool__(self):
  101. return bool(self.x) or bool(self.y)
  102. def quadrant(self):
  103. """Return quadrant index 0-3.
  104. 0 is included in quadrant 0.
  105. """
  106. if self.y > 0:
  107. return 0 if self.x > 0 else 1
  108. elif self.y < 0:
  109. return 2 if self.x < 0 else 3
  110. else:
  111. return 0 if self.x >= 0 else 2
  112. def __rdivmod__(self, other):
  113. try:
  114. other = self._parent.convert(other)
  115. except CoercionFailed:
  116. return NotImplemented
  117. else:
  118. return other.__divmod__(self)
  119. def __rtruediv__(self, other):
  120. try:
  121. other = QQ_I.convert(other)
  122. except CoercionFailed:
  123. return NotImplemented
  124. else:
  125. return other.__truediv__(self)
  126. def __floordiv__(self, other):
  127. qr = self.__divmod__(other)
  128. return qr if qr is NotImplemented else qr[0]
  129. def __rfloordiv__(self, other):
  130. qr = self.__rdivmod__(other)
  131. return qr if qr is NotImplemented else qr[0]
  132. def __mod__(self, other):
  133. qr = self.__divmod__(other)
  134. return qr if qr is NotImplemented else qr[1]
  135. def __rmod__(self, other):
  136. qr = self.__rdivmod__(other)
  137. return qr if qr is NotImplemented else qr[1]
  138. class GaussianInteger(GaussianElement):
  139. """Gaussian integer: domain element for :ref:`ZZ_I`
  140. >>> from sympy import ZZ_I
  141. >>> z = ZZ_I(2, 3)
  142. >>> z
  143. (2 + 3*I)
  144. >>> type(z)
  145. <class 'sympy.polys.domains.gaussiandomains.GaussianInteger'>
  146. """
  147. base = ZZ
  148. def __truediv__(self, other):
  149. """Return a Gaussian rational."""
  150. return QQ_I.convert(self)/other
  151. def __divmod__(self, other):
  152. if not other:
  153. raise ZeroDivisionError('divmod({}, 0)'.format(self))
  154. x, y = self._get_xy(other)
  155. if x is None:
  156. return NotImplemented
  157. # multiply self and other by x - I*y
  158. # self/other == (a + I*b)/c
  159. a, b = self.x*x + self.y*y, -self.x*y + self.y*x
  160. c = x*x + y*y
  161. # find integers qx and qy such that
  162. # |a - qx*c| <= c/2 and |b - qy*c| <= c/2
  163. qx = (2*a + c) // (2*c) # -c <= 2*a - qx*2*c < c
  164. qy = (2*b + c) // (2*c)
  165. q = GaussianInteger(qx, qy)
  166. # |self/other - q| < 1 since
  167. # |a/c - qx|**2 + |b/c - qy|**2 <= 1/4 + 1/4 < 1
  168. return q, self - q*other # |r| < |other|
  169. class GaussianRational(GaussianElement):
  170. """Gaussian rational: domain element for :ref:`QQ_I`
  171. >>> from sympy import QQ_I, QQ
  172. >>> z = QQ_I(QQ(2, 3), QQ(4, 5))
  173. >>> z
  174. (2/3 + 4/5*I)
  175. >>> type(z)
  176. <class 'sympy.polys.domains.gaussiandomains.GaussianRational'>
  177. """
  178. base = QQ
  179. def __truediv__(self, other):
  180. """Return a Gaussian rational."""
  181. if not other:
  182. raise ZeroDivisionError('{} / 0'.format(self))
  183. x, y = self._get_xy(other)
  184. if x is None:
  185. return NotImplemented
  186. c = x*x + y*y
  187. return GaussianRational((self.x*x + self.y*y)/c,
  188. (-self.x*y + self.y*x)/c)
  189. def __divmod__(self, other):
  190. try:
  191. other = self._parent.convert(other)
  192. except CoercionFailed:
  193. return NotImplemented
  194. if not other:
  195. raise ZeroDivisionError('{} % 0'.format(self))
  196. else:
  197. return self/other, QQ_I.zero
  198. class GaussianDomain():
  199. """Base class for Gaussian domains."""
  200. dom: Domain
  201. is_Numerical = True
  202. is_Exact = True
  203. has_assoc_Ring = True
  204. has_assoc_Field = True
  205. def to_sympy(self, a):
  206. """Convert ``a`` to a SymPy object. """
  207. conv = self.dom.to_sympy
  208. return conv(a.x) + I*conv(a.y)
  209. def from_sympy(self, a):
  210. """Convert a SymPy object to ``self.dtype``."""
  211. r, b = a.as_coeff_Add()
  212. x = self.dom.from_sympy(r) # may raise CoercionFailed
  213. if not b:
  214. return self.new(x, 0)
  215. r, b = b.as_coeff_Mul()
  216. y = self.dom.from_sympy(r)
  217. if b is I:
  218. return self.new(x, y)
  219. else:
  220. raise CoercionFailed("{} is not Gaussian".format(a))
  221. def inject(self, *gens):
  222. """Inject generators into this domain. """
  223. return self.poly_ring(*gens)
  224. def canonical_unit(self, d):
  225. unit = self.units[-d.quadrant()] # - for inverse power
  226. return unit
  227. def is_negative(self, element):
  228. """Returns ``False`` for any ``GaussianElement``. """
  229. return False
  230. def is_positive(self, element):
  231. """Returns ``False`` for any ``GaussianElement``. """
  232. return False
  233. def is_nonnegative(self, element):
  234. """Returns ``False`` for any ``GaussianElement``. """
  235. return False
  236. def is_nonpositive(self, element):
  237. """Returns ``False`` for any ``GaussianElement``. """
  238. return False
  239. def from_ZZ_gmpy(K1, a, K0):
  240. """Convert a GMPY mpz to ``self.dtype``."""
  241. return K1(a)
  242. def from_ZZ(K1, a, K0):
  243. """Convert a ZZ_python element to ``self.dtype``."""
  244. return K1(a)
  245. def from_ZZ_python(K1, a, K0):
  246. """Convert a ZZ_python element to ``self.dtype``."""
  247. return K1(a)
  248. def from_QQ(K1, a, K0):
  249. """Convert a GMPY mpq to ``self.dtype``."""
  250. return K1(a)
  251. def from_QQ_gmpy(K1, a, K0):
  252. """Convert a GMPY mpq to ``self.dtype``."""
  253. return K1(a)
  254. def from_QQ_python(K1, a, K0):
  255. """Convert a QQ_python element to ``self.dtype``."""
  256. return K1(a)
  257. def from_AlgebraicField(K1, a, K0):
  258. """Convert an element from ZZ<I> or QQ<I> to ``self.dtype``."""
  259. if K0.ext.args[0] == I:
  260. return K1.from_sympy(K0.to_sympy(a))
  261. class GaussianIntegerRing(GaussianDomain, Ring):
  262. r"""Ring of Gaussian integers ``ZZ_I``
  263. The :ref:`ZZ_I` domain represents the `Gaussian integers`_ `\mathbb{Z}[i]`
  264. as a :py:class:`~.Domain` in the domain system (see
  265. :ref:`polys-domainsintro`).
  266. By default a :py:class:`~.Poly` created from an expression with
  267. coefficients that are combinations of integers and ``I`` (`\sqrt{-1}`)
  268. will have the domain :ref:`ZZ_I`.
  269. >>> from sympy import Poly, Symbol, I
  270. >>> x = Symbol('x')
  271. >>> p = Poly(x**2 + I)
  272. >>> p
  273. Poly(x**2 + I, x, domain='ZZ_I')
  274. >>> p.domain
  275. ZZ_I
  276. The :ref:`ZZ_I` domain can be used to factorise polynomials that are
  277. reducible over the Gaussian integers.
  278. >>> from sympy import factor
  279. >>> factor(x**2 + 1)
  280. x**2 + 1
  281. >>> factor(x**2 + 1, domain='ZZ_I')
  282. (x - I)*(x + I)
  283. The corresponding `field of fractions`_ is the domain of the Gaussian
  284. rationals :ref:`QQ_I`. Conversely :ref:`ZZ_I` is the `ring of integers`_
  285. of :ref:`QQ_I`.
  286. >>> from sympy import ZZ_I, QQ_I
  287. >>> ZZ_I.get_field()
  288. QQ_I
  289. >>> QQ_I.get_ring()
  290. ZZ_I
  291. When using the domain directly :ref:`ZZ_I` can be used as a constructor.
  292. >>> ZZ_I(3, 4)
  293. (3 + 4*I)
  294. >>> ZZ_I(5)
  295. (5 + 0*I)
  296. The domain elements of :ref:`ZZ_I` are instances of
  297. :py:class:`~.GaussianInteger` which support the rings operations
  298. ``+,-,*,**``.
  299. >>> z1 = ZZ_I(5, 1)
  300. >>> z2 = ZZ_I(2, 3)
  301. >>> z1
  302. (5 + 1*I)
  303. >>> z2
  304. (2 + 3*I)
  305. >>> z1 + z2
  306. (7 + 4*I)
  307. >>> z1 * z2
  308. (7 + 17*I)
  309. >>> z1 ** 2
  310. (24 + 10*I)
  311. Both floor (``//``) and modulo (``%``) division work with
  312. :py:class:`~.GaussianInteger` (see the :py:meth:`~.Domain.div` method).
  313. >>> z3, z4 = ZZ_I(5), ZZ_I(1, 3)
  314. >>> z3 // z4 # floor division
  315. (1 + -1*I)
  316. >>> z3 % z4 # modulo division (remainder)
  317. (1 + -2*I)
  318. >>> (z3//z4)*z4 + z3%z4 == z3
  319. True
  320. True division (``/``) in :ref:`ZZ_I` gives an element of :ref:`QQ_I`. The
  321. :py:meth:`~.Domain.exquo` method can be used to divide in :ref:`ZZ_I` when
  322. exact division is possible.
  323. >>> z1 / z2
  324. (1 + -1*I)
  325. >>> ZZ_I.exquo(z1, z2)
  326. (1 + -1*I)
  327. >>> z3 / z4
  328. (1/2 + -3/2*I)
  329. >>> ZZ_I.exquo(z3, z4)
  330. Traceback (most recent call last):
  331. ...
  332. ExactQuotientFailed: (1 + 3*I) does not divide (5 + 0*I) in ZZ_I
  333. The :py:meth:`~.Domain.gcd` method can be used to compute the `gcd`_ of any
  334. two elements.
  335. >>> ZZ_I.gcd(ZZ_I(10), ZZ_I(2))
  336. (2 + 0*I)
  337. >>> ZZ_I.gcd(ZZ_I(5), ZZ_I(2, 1))
  338. (2 + 1*I)
  339. .. _Gaussian integers: https://en.wikipedia.org/wiki/Gaussian_integer
  340. .. _gcd: https://en.wikipedia.org/wiki/Greatest_common_divisor
  341. """
  342. dom = ZZ
  343. mod = DMP([ZZ.one, ZZ.zero, ZZ.one], ZZ)
  344. dtype = GaussianInteger
  345. zero = dtype(ZZ(0), ZZ(0))
  346. one = dtype(ZZ(1), ZZ(0))
  347. imag_unit = dtype(ZZ(0), ZZ(1))
  348. units = (one, imag_unit, -one, -imag_unit) # powers of i
  349. rep = 'ZZ_I'
  350. is_GaussianRing = True
  351. is_ZZ_I = True
  352. is_PID = True
  353. def __init__(self): # override Domain.__init__
  354. """For constructing ZZ_I."""
  355. def __eq__(self, other):
  356. """Returns ``True`` if two domains are equivalent. """
  357. if isinstance(other, GaussianIntegerRing):
  358. return True
  359. else:
  360. return NotImplemented
  361. def __hash__(self):
  362. """Compute hash code of ``self``. """
  363. return hash('ZZ_I')
  364. @property
  365. def has_CharacteristicZero(self):
  366. return True
  367. def characteristic(self):
  368. return 0
  369. def get_ring(self):
  370. """Returns a ring associated with ``self``. """
  371. return self
  372. def get_field(self):
  373. """Returns a field associated with ``self``. """
  374. return QQ_I
  375. def normalize(self, d, *args):
  376. """Return first quadrant element associated with ``d``.
  377. Also multiply the other arguments by the same power of i.
  378. """
  379. unit = self.canonical_unit(d)
  380. d *= unit
  381. args = tuple(a*unit for a in args)
  382. return (d,) + args if args else d
  383. def gcd(self, a, b):
  384. """Greatest common divisor of a and b over ZZ_I."""
  385. while b:
  386. a, b = b, a % b
  387. return self.normalize(a)
  388. def gcdex(self, a, b):
  389. """Return x, y, g such that x * a + y * b = g = gcd(a, b)"""
  390. x_a = self.one
  391. x_b = self.zero
  392. y_a = self.zero
  393. y_b = self.one
  394. while b:
  395. q = a // b
  396. a, b = b, a - q * b
  397. x_a, x_b = x_b, x_a - q * x_b
  398. y_a, y_b = y_b, y_a - q * y_b
  399. a, x_a, y_a = self.normalize(a, x_a, y_a)
  400. return x_a, y_a, a
  401. def lcm(self, a, b):
  402. """Least common multiple of a and b over ZZ_I."""
  403. return (a * b) // self.gcd(a, b)
  404. def from_GaussianIntegerRing(K1, a, K0):
  405. """Convert a ZZ_I element to ZZ_I."""
  406. return a
  407. def from_GaussianRationalField(K1, a, K0):
  408. """Convert a QQ_I element to ZZ_I."""
  409. return K1.new(ZZ.convert(a.x), ZZ.convert(a.y))
  410. ZZ_I = GaussianInteger._parent = GaussianIntegerRing()
  411. class GaussianRationalField(GaussianDomain, Field):
  412. r"""Field of Gaussian rationals ``QQ_I``
  413. The :ref:`QQ_I` domain represents the `Gaussian rationals`_ `\mathbb{Q}(i)`
  414. as a :py:class:`~.Domain` in the domain system (see
  415. :ref:`polys-domainsintro`).
  416. By default a :py:class:`~.Poly` created from an expression with
  417. coefficients that are combinations of rationals and ``I`` (`\sqrt{-1}`)
  418. will have the domain :ref:`QQ_I`.
  419. >>> from sympy import Poly, Symbol, I
  420. >>> x = Symbol('x')
  421. >>> p = Poly(x**2 + I/2)
  422. >>> p
  423. Poly(x**2 + I/2, x, domain='QQ_I')
  424. >>> p.domain
  425. QQ_I
  426. The polys option ``gaussian=True`` can be used to specify that the domain
  427. should be :ref:`QQ_I` even if the coefficients do not contain ``I`` or are
  428. all integers.
  429. >>> Poly(x**2)
  430. Poly(x**2, x, domain='ZZ')
  431. >>> Poly(x**2 + I)
  432. Poly(x**2 + I, x, domain='ZZ_I')
  433. >>> Poly(x**2/2)
  434. Poly(1/2*x**2, x, domain='QQ')
  435. >>> Poly(x**2, gaussian=True)
  436. Poly(x**2, x, domain='QQ_I')
  437. >>> Poly(x**2 + I, gaussian=True)
  438. Poly(x**2 + I, x, domain='QQ_I')
  439. >>> Poly(x**2/2, gaussian=True)
  440. Poly(1/2*x**2, x, domain='QQ_I')
  441. The :ref:`QQ_I` domain can be used to factorise polynomials that are
  442. reducible over the Gaussian rationals.
  443. >>> from sympy import factor, QQ_I
  444. >>> factor(x**2/4 + 1)
  445. (x**2 + 4)/4
  446. >>> factor(x**2/4 + 1, domain='QQ_I')
  447. (x - 2*I)*(x + 2*I)/4
  448. >>> factor(x**2/4 + 1, domain=QQ_I)
  449. (x - 2*I)*(x + 2*I)/4
  450. It is also possible to specify the :ref:`QQ_I` domain explicitly with
  451. polys functions like :py:func:`~.apart`.
  452. >>> from sympy import apart
  453. >>> apart(1/(1 + x**2))
  454. 1/(x**2 + 1)
  455. >>> apart(1/(1 + x**2), domain=QQ_I)
  456. I/(2*(x + I)) - I/(2*(x - I))
  457. The corresponding `ring of integers`_ is the domain of the Gaussian
  458. integers :ref:`ZZ_I`. Conversely :ref:`QQ_I` is the `field of fractions`_
  459. of :ref:`ZZ_I`.
  460. >>> from sympy import ZZ_I, QQ_I, QQ
  461. >>> ZZ_I.get_field()
  462. QQ_I
  463. >>> QQ_I.get_ring()
  464. ZZ_I
  465. When using the domain directly :ref:`QQ_I` can be used as a constructor.
  466. >>> QQ_I(3, 4)
  467. (3 + 4*I)
  468. >>> QQ_I(5)
  469. (5 + 0*I)
  470. >>> QQ_I(QQ(2, 3), QQ(4, 5))
  471. (2/3 + 4/5*I)
  472. The domain elements of :ref:`QQ_I` are instances of
  473. :py:class:`~.GaussianRational` which support the field operations
  474. ``+,-,*,**,/``.
  475. >>> z1 = QQ_I(5, 1)
  476. >>> z2 = QQ_I(2, QQ(1, 2))
  477. >>> z1
  478. (5 + 1*I)
  479. >>> z2
  480. (2 + 1/2*I)
  481. >>> z1 + z2
  482. (7 + 3/2*I)
  483. >>> z1 * z2
  484. (19/2 + 9/2*I)
  485. >>> z2 ** 2
  486. (15/4 + 2*I)
  487. True division (``/``) in :ref:`QQ_I` gives an element of :ref:`QQ_I` and
  488. is always exact.
  489. >>> z1 / z2
  490. (42/17 + -2/17*I)
  491. >>> QQ_I.exquo(z1, z2)
  492. (42/17 + -2/17*I)
  493. >>> z1 == (z1/z2)*z2
  494. True
  495. Both floor (``//``) and modulo (``%``) division can be used with
  496. :py:class:`~.GaussianRational` (see :py:meth:`~.Domain.div`)
  497. but division is always exact so there is no remainder.
  498. >>> z1 // z2
  499. (42/17 + -2/17*I)
  500. >>> z1 % z2
  501. (0 + 0*I)
  502. >>> QQ_I.div(z1, z2)
  503. ((42/17 + -2/17*I), (0 + 0*I))
  504. >>> (z1//z2)*z2 + z1%z2 == z1
  505. True
  506. .. _Gaussian rationals: https://en.wikipedia.org/wiki/Gaussian_rational
  507. """
  508. dom = QQ
  509. mod = DMP([QQ.one, QQ.zero, QQ.one], QQ)
  510. dtype = GaussianRational
  511. zero = dtype(QQ(0), QQ(0))
  512. one = dtype(QQ(1), QQ(0))
  513. imag_unit = dtype(QQ(0), QQ(1))
  514. units = (one, imag_unit, -one, -imag_unit) # powers of i
  515. rep = 'QQ_I'
  516. is_GaussianField = True
  517. is_QQ_I = True
  518. def __init__(self): # override Domain.__init__
  519. """For constructing QQ_I."""
  520. def __eq__(self, other):
  521. """Returns ``True`` if two domains are equivalent. """
  522. if isinstance(other, GaussianRationalField):
  523. return True
  524. else:
  525. return NotImplemented
  526. def __hash__(self):
  527. """Compute hash code of ``self``. """
  528. return hash('QQ_I')
  529. @property
  530. def has_CharacteristicZero(self):
  531. return True
  532. def characteristic(self):
  533. return 0
  534. def get_ring(self):
  535. """Returns a ring associated with ``self``. """
  536. return ZZ_I
  537. def get_field(self):
  538. """Returns a field associated with ``self``. """
  539. return self
  540. def as_AlgebraicField(self):
  541. """Get equivalent domain as an ``AlgebraicField``. """
  542. return AlgebraicField(self.dom, I)
  543. def numer(self, a):
  544. """Get the numerator of ``a``."""
  545. ZZ_I = self.get_ring()
  546. return ZZ_I.convert(a * self.denom(a))
  547. def denom(self, a):
  548. """Get the denominator of ``a``."""
  549. ZZ = self.dom.get_ring()
  550. QQ = self.dom
  551. ZZ_I = self.get_ring()
  552. denom_ZZ = ZZ.lcm(QQ.denom(a.x), QQ.denom(a.y))
  553. return ZZ_I(denom_ZZ, ZZ.zero)
  554. def from_GaussianIntegerRing(K1, a, K0):
  555. """Convert a ZZ_I element to QQ_I."""
  556. return K1.new(a.x, a.y)
  557. def from_GaussianRationalField(K1, a, K0):
  558. """Convert a QQ_I element to QQ_I."""
  559. return a
  560. def from_ComplexField(K1, a, K0):
  561. """Convert a ComplexField element to QQ_I."""
  562. return K1.new(QQ.convert(a.real), QQ.convert(a.imag))
  563. QQ_I = GaussianRational._parent = GaussianRationalField()