def minpoly_groebner(ex, x, domain):
    "\n    Computes the minimal polynomial of an algebraic number\n    using Gröbner bases\n\n    Examples\n    ========\n\n    >>> minimal_polynomial(sqrt(2) + 1, method='groebner')(x)\n    x**2 - 2*x - 1\n\n    References\n    ==========\n\n    * :cite:`Adams1994intro`\n\n    "
    generator = numbered_symbols('a', cls=Dummy)
    (mapping, symbols) = ({}, {})

    def update_mapping(ex, exp, base=None):
        if (ex in mapping):
            return symbols[ex]
        a = next(generator)
        symbols[ex] = a
        if (base is not None):
            mapping[ex] = ((a ** exp) + base)
        else:
            mapping[ex] = exp.as_expr(a)
        return a

    def bottom_up_scan(ex):
        if ex.is_Atom:
            if (ex is I):
                return update_mapping(ex, 2, 1)
            elif (ex is GoldenRatio):
                return bottom_up_scan(ex.expand(func=True))
            elif ex.is_Rational:
                return ex
            elif ex.is_Symbol:
                return ex
        elif (ex.is_Add or ex.is_Mul):
            return ex.func(*[bottom_up_scan(g) for g in ex.args])
        elif ex.is_Pow:
            if ex.exp.is_Rational:
                (base, exp) = (ex.base, ex.exp)
                if exp.is_nonnegative:
                    if exp.is_noninteger:
                        (base, exp) = ((base ** exp.numerator), Rational(1, exp.denominator))
                    base = bottom_up_scan(base)
                else:
                    bmp = PurePoly(minpoly_groebner((1 / base), x, domain=domain), x)
                    (base, exp) = (update_mapping((1 / base), bmp), (- exp))
                return update_mapping(ex, exp.denominator, (- (base ** exp.numerator)))
        elif (isinstance(ex, RootOf) and ex.poly.domain.is_Numerical):
            if ex.poly.domain.is_IntegerRing:
                return update_mapping(ex, ex.poly)
            else:
                return update_mapping(ex, ex.poly.sqf_norm()[(- 1)])
        elif isinstance(ex, conjugate):
            return update_mapping(ex, minimal_polynomial(ex.args[0], domain=domain, method='groebner'))
        elif isinstance(ex, Abs):
            return bottom_up_scan(sqrt((ex.args[0] * ex.args[0].conjugate())))
        elif isinstance(ex, re):
            return bottom_up_scan(((ex.args[0] + ex.args[0].conjugate()) / 2))
        elif isinstance(ex, im):
            return bottom_up_scan((((ex.args[0] - ex.args[0].conjugate()) / 2) / I))
        raise NotAlgebraic(f"{ex} doesn't seem to be an algebraic number")
    if (ex.is_Pow and ex.exp.is_negative):
        (n, d) = (Integer(1), bottom_up_scan((1 / ex)))
    else:
        (n, d) = (bottom_up_scan(ex), Integer(1))
    F = ([((d * x) - n)] + list(mapping.values()))
    G = groebner(F, *(list(symbols.values()) + [x]), order='lex', domain=domain)
    return G[(- 1)]
Computes the minimal polynomial of an algebraic number
using Gröbner bases

Examples
========

>>> minimal_polynomial(sqrt(2) + 1, method='groebner')(x)
x**2 - 2*x - 1

References
==========

* :cite:`Adams1994intro`
def minpoly_groebner(ex, x, domain):
    "\n    Computes the minimal polynomial of an algebraic number\n    using Gröbner bases\n\n    Examples\n    ========\n\n    >>> minimal_polynomial(sqrt(2) + 1, method='groebner')(x)\n    x**2 - 2*x - 1\n\n    References\n    ==========\n\n    * :cite:`Adams1994intro`\n\n    "
    generator = numbered_symbols('a', cls=Dummy)
    (mapping, symbols) = ({}, {})

    def update_mapping(ex, exp, base=None):
        if (ex in mapping):
            return symbols[ex]
        a = next(generator)
        symbols[ex] = a
        if (base is not None):
            mapping[ex] = ((a ** exp) + base)
        else:
            mapping[ex] = exp.as_expr(a)
        return a

    def bottom_up_scan(ex):
        if ex.is_Atom:
            if (ex is I):
                return update_mapping(ex, 2, 1)
            elif (ex is GoldenRatio):
                return bottom_up_scan(ex.expand(func=True))
            elif ex.is_Rational:
                return ex
            elif ex.is_Symbol:
                return ex
        elif (ex.is_Add or ex.is_Mul):
            return ex.func(*[bottom_up_scan(g) for g in ex.args])
        elif ex.is_Pow:
            if ex.exp.is_Rational:
                (base, exp) = (ex.base, ex.exp)
                if exp.is_nonnegative:
                    if exp.is_noninteger:
                        (base, exp) = ((base ** exp.numerator), Rational(1, exp.denominator))
                    base = bottom_up_scan(base)
                else:
                    bmp = PurePoly(minpoly_groebner((1 / base), x, domain=domain), x)
                    (base, exp) = (update_mapping((1 / base), bmp), (- exp))
                return update_mapping(ex, exp.denominator, (- (base ** exp.numerator)))
        elif (isinstance(ex, RootOf) and ex.poly.domain.is_Numerical):
            if ex.poly.domain.is_IntegerRing:
                return update_mapping(ex, ex.poly)
            else:
                return update_mapping(ex, ex.poly.sqf_norm()[(- 1)])
        elif isinstance(ex, conjugate):
            return update_mapping(ex, minimal_polynomial(ex.args[0], domain=domain, method='groebner'))
        elif isinstance(ex, Abs):
            return bottom_up_scan(sqrt((ex.args[0] * ex.args[0].conjugate())))
        elif isinstance(ex, re):
            return bottom_up_scan(((ex.args[0] + ex.args[0].conjugate()) / 2))
        elif isinstance(ex, im):
            return bottom_up_scan((((ex.args[0] - ex.args[0].conjugate()) / 2) / I))
        raise NotAlgebraic(f"{ex} doesn't seem to be an algebraic number")
    if (ex.is_Pow and ex.exp.is_negative):
        (n, d) = (Integer(1), bottom_up_scan((1 / ex)))
    else:
        (n, d) = (bottom_up_scan(ex), Integer(1))
    F = ([((d * x) - n)] + list(mapping.values()))
    G = groebner(F, *(list(symbols.values()) + [x]), order='lex', domain=domain)
    return G[(- 1)]