"""Computational algebraic number field theory. """

from sympy import (S, Expr, I, Integer, Rational, Real, Symbol, Add, Mul,
    sympify, Q, ask, Dummy)
from sympy.polys.polytools import Poly, sqf_norm, invert, factor_list, groebner
from sympy.polys.polyutils import basic_from_dict
from sympy.polys.polyclasses import ANP, DMP
from sympy.polys.polyerrors import (IsomorphismFailed, CoercionFailed,
    NotAlgebraic)
from sympy.utilities import any, all, numbered_symbols, variations
from sympy.ntheory import sieve
from sympy.mpmath import pslq, mp

def minimal_polynomial(ex, x=None, **args):
    """Computes the minimal polynomial of an algebraic number. """
    generator = numbered_symbols('a', cls=Dummy)
    mapping, symbols, replace = {}, {}, []

    ex = sympify(ex)

    if x is not None:
        x = sympify(x)
    else:
        x = Dummy('x')

    def update_mapping(ex, exp, base=None):
        a = generator.next()
        symbols[ex] = a

        if base is not None:
            mapping[ex] = a**exp + base
        else:
            mapping[ex] = exp.as_basic(a)

        return a

    def bottom_up_scan(ex):
        if ex.is_Atom:
            if ex is S.ImaginaryUnit:
                if ex not in mapping:
                    return update_mapping(ex, 2, 1)
                else:
                    return symbols[ex]
            elif ex.is_Rational and ex.q != 0:
                return ex
        elif ex.is_Add:
            return Add(*[ bottom_up_scan(g) for g in ex.args ])
        elif ex.is_Mul:
            return Mul(*[ bottom_up_scan(g) for g in ex.args ])