"""Implementation of :class:`AlgebraicField` class. """

from sympy.core import sympify

from sympy.polys.domains.field import Field
from sympy.polys.domains.simpledomain import SimpleDomain
from sympy.polys.domains.characteristiczero import CharacteristicZero

from sympy.polys.polyclasses import ANP
from sympy.polys.polyerrors import CoercionFailed, DomainError, NotAlgebraic, IsomorphismFailed

class AlgebraicField(Field, CharacteristicZero, SimpleDomain):
    """A class for representing algebraic number fields. """

    dtype        = ANP

    is_Numerical = True
    is_Algebraic = True

    has_assoc_Ring  = False
    has_assoc_Field = True

    def __init__(self, dom, *ext):
        if not dom.is_QQ:
            raise DomainError("ground domain must be a rational field")

        from sympy.polys.numberfields import to_number_field

        self.ext  = to_number_field(ext)
        self.mod  = self.ext.minpoly.rep
        self.dom  = dom

        self.gens = (self.ext,)
        self.unit = self([dom(1), dom(0)])

        self.zero = self.dtype.zero(self.mod.rep, dom)
        self.one  = self.dtype.one(self.mod.rep, dom)

    def __str__(self):
        return str(self.dom) + '<' + str(self.ext) + '>'

    def __hash__(self):
        return hash((self.__class__.__name__, self.dtype, self.dom, self.ext))

    def __call__(self, a):
        """Construct an element of `self` domain from `a`. """
        return ANP(a, self.mod.rep, self.dom)

    def __eq__(self, other):
        """Returns `True` if two domains are equivalent. """
        if self.dtype == other.dtype:
            return self.ext == other.ext
        else:
            return False

    def __ne__(self, other):
        """Returns `False` if two domains are equivalent. """
        if self.dtype == other.dtype:
            return self.ext != other.ext
        else:
            return True

    def algebraic_field(self, *extension):
        """Returns an algebraic field, i.e. `QQ(alpha, ...)`. """
        return AlgebraicField(self.dom, *((self.ext,) + extension))

    def to_sympy(self, a):
        """Convert `a` to a SymPy object. """
        from sympy.polys.numberfields import AlgebraicNumber
        return AlgebraicNumber(self.ext, a).as_expr()

    def from_sympy(self, a):
        """Convert SymPy's expression to `dtype`. """
        try:
            return self([self.dom.from_sympy(a)])
        except CoercionFailed:
            pass

        from sympy.polys.numberfields import to_number_field

        try:
            return self(to_number_field(a, self.ext).native_coeffs())
        except (NotAlgebraic, IsomorphismFailed):
            raise CoercionFailed("%s is not a valid algebraic number in %s" % (a, self))

    def from_ZZ_python(K1, a, K0):
        """Convert a Python `int` object to `dtype`. """
        return K1(K1.dom.convert(a, K0))

    def from_QQ_python(K1, a, K0):
        """Convert a Python `Fraction` object to `dtype`. """
        return K1(K1.dom.convert(a, K0))

    def from_ZZ_sympy(K1, a, K0):
        """Convert a SymPy `Integer` object to `dtype`. """
        return K1(K1.dom.convert(a, K0))

    def from_QQ_sympy(K1, a, K0):
        """Convert a SymPy `Rational` object to `dtype`. """
        return K1(K1.dom.convert(a, K0))

    def from_ZZ_gmpy(K1, a, K0):
        """Convert a GMPY `mpz` object to `dtype`. """
        return K1(K1.dom.convert(a, K0))

    def from_QQ_gmpy(K1, a, K0):
        """Convert a GMPY `mpq` object to `dtype`. """
        return K1(K1.dom.convert(a, K0))

    def from_RR_sympy(K1, a, K0):
        """Convert a SymPy `Float` object to `dtype`. """
        return K1(K1.dom.convert(a, K0))

    def from_RR_mpmath(K1, a, K0):
        """Convert a mpmath `mpf` object to `dtype`. """
        return K1(K1.dom.convert(a, K0))

    def get_ring(self):
        """Returns a ring associated with `self`. """
        raise DomainError('there is no ring associated with %s' % self)

    def is_positive(self, a):
        """Returns True if `a` is positive. """
        return self.dom.is_positive(a.LC())

    def is_negative(self, a):
        """Returns True if `a` is negative. """
        return self.dom.is_negative(a.LC())

    def is_nonpositive(self, a):
        """Returns True if `a` is non-positive. """
        return self.dom.is_nonpositive(a.LC())

    def is_nonnegative(self, a):
        """Returns True if `a` is non-negative. """
        return self.dom.is_nonnegative(a.LC())

    def numer(self, a):
        """Returns numerator of `a`. """
        return a

    def denom(self, a):
        """Returns denominator of `a`. """
        return self.one
