1"""Implementation of :class:`ComplexField` class."""
2
3from __future__ import annotations
4
5import mpmath
6
7from ..core import Float, I
8from ..polys.polyerrors import CoercionFailed
9from .characteristiczero import CharacteristicZero
10from .field import Field
11from .mpelements import MPContext
12from .simpledomain import SimpleDomain
13
14
15class ComplexField(CharacteristicZero, SimpleDomain, Field):
16    """Complex numbers up to the given precision."""
17
18    rep = 'CC'
19
20    is_ComplexField = True
21
22    is_Exact = False
23    is_Numerical = True
24
25    _default_precision = 53
26
27    @property
28    def has_default_precision(self):
29        return self.precision == self._default_precision
30
31    @property
32    def precision(self):
33        return self._context.prec
34
35    @property
36    def dps(self):
37        return self._context.dps
38
39    @property
40    def tolerance(self):
41        return self._context.tolerance
42
43    def __new__(cls, prec=_default_precision, dps=None, tol=None):
44        context = MPContext(prec, dps, tol)
45
46        obj = super().__new__(cls)
47
48        try:
49            obj.dtype = _complexes_cache[(context.prec, context.tolerance)]
50        except KeyError:
51            _complexes_cache[(context.prec, context.tolerance)] = obj.dtype = context.mpc
52
53        context._parent = obj
54        obj._context = context
55        obj._hash = hash((cls.__name__, obj.dtype, context.prec, context.tolerance))
56
57        obj.zero = obj.dtype(0)
58        obj.one = obj.dtype(1)
59
60        return obj
61
62    def __getnewargs_ex__(self):
63        return (), {'prec': self.precision,
64                    'tol': mpmath.mpf(self.tolerance._mpf_)}
65
66    def __eq__(self, other):
67        return (isinstance(other, ComplexField)
68                and self.precision == other.precision
69                and self.tolerance == other.tolerance)
70
71    def __hash__(self):
72        return self._hash
73
74    def to_expr(self, element):
75        return Float(element.real, self.dps) + I*Float(element.imag, self.dps)
76
77    def from_expr(self, expr):
78        number = expr.evalf(self.dps)
79        real, imag = number.as_real_imag()
80
81        if real.is_Number and imag.is_Number:
82            return self.dtype(real, imag)
83        else:
84            raise CoercionFailed(f'expected complex number, got {expr}')
85
86    def _from_PythonIntegerRing(self, element, base):
87        return self.dtype(element)
88    _from_GMPYIntegerRing = _from_PythonIntegerRing
89    _from_RealField = _from_PythonIntegerRing
90    _from_ComplexField = _from_PythonIntegerRing
91
92    def _from_PythonRationalField(self, element, base):
93        return self.dtype(element.numerator) / element.denominator
94    _from_GMPYRationalField = _from_PythonRationalField
95
96    def _from_AlgebraicField(self, element, base):
97        return self.from_expr(base.to_expr(element))
98
99    def get_exact(self):
100        from . import QQ
101        return QQ.algebraic_field(I)
102
103    def gcd(self, a, b):
104        return self.one
105
106    def almosteq(self, a, b, tolerance=None):
107        """Check if ``a`` and ``b`` are almost equal."""
108        return self._context.almosteq(a, b, tolerance)
109
110    def is_normal(self, a):
111        return True
112
113
114_complexes_cache: dict[tuple, ComplexField] = {}
115
116
117CC = ComplexField()
118