'Reduces a polynomial modulo a Groebner basis.
Given a polynomial ``f`` and a set of polynomials ``G = (g_1, ..., g_n)``,
computes a set of quotients ``q = (q_1, ..., q_n)`` and the remainder ``r``
such that ``f = q_1*f_1 + ... + q_n*f_n + r``, where ``r`` vanishes or ``r``
is a completely reduced polynomial with respect to ``G``.
Examples
>>> from sympy import groebner, expand
>>> from sympy.abc import x, y
>>> f = 2*x**4 - x**2 + y**3 + y**2
>>> G = groebner([x**3 - x, y**3 - y])
>>> G.reduce(f)
([2*x, 1], x**2 + y**2 + y)
>>> Q, r = _
>>> expand(sum(q*g for q, g in zip(Q, G)) + r)
2*x**4 - x**2 + y**3 + y**2
>>> _ == f
True'

def reduce(self, expr, auto=True):

    poly = Poly._from_expr(expr, self._options)
    polys = ([poly] + list(self._basis))
    opt = self._options
    domain = opt.domain
    retract = False
    if (auto and domain.is_Ring and (not domain.is_Field)):
        opt = opt.clone(dict(domain=domain.get_field()))
        retract = True
    from sympy.polys.rings import xring
    (_ring, _) = xring(opt.gens, opt.domain, opt.order)
    for (i, poly) in enumerate(polys):
        poly = poly.set_domain(opt.domain).rep.to_dict()
        polys[i] = _ring.from_dict(poly)
    (Q, r) = polys[0].div(polys[1:])
    Q = [Poly._from_dict(dict(q), opt) for q in Q]
    r = Poly._from_dict(dict(r), opt)
    if retract:
        try:
            (_Q, _r) = ([q.to_ring() for q in Q], r.to_ring())
        except CoercionFailed:
            pass
        else:
            (Q, r) = (_Q, _r)
    if (not opt.polys):
        return ([q.as_expr() for q in Q], r.as_expr())
    else:
        return (Q, r)
