text: """Sparse rational function fields. """
  
  from __future__ import print_function, division
  
  from typing import Any, Dict
  
  from operator import add, mul, lt, le, gt, ge
  
  from sympy.core.compatibility import is_sequence, reduce
  from sympy.core.expr import Expr
  from sympy.core.mod import Mod
  from sympy.core.numbers import Exp1
  from sympy.core.singleton import S
  from sympy.core.symbol import Symbol
  from sympy.core.sympify import CantSympify, sympify
  from sympy.functions.elementary.exponential import ExpBase
  from sympy.polys.domains.domainelement import DomainElement
  from sympy.polys.domains.fractionfield import FractionField
  from sympy.polys.domains.polynomialring import PolynomialRing
  from sympy.polys.constructor import construct_domain
  from sympy.polys.orderings import lex
  from sympy.polys.polyerrors import CoercionFailed
  from sympy.polys.polyoptions import build_options
  from sympy.polys.polyutils import _parallel_dict_from_expr
  from sympy.polys.rings import PolyElement
  from sympy.printing.defaults import DefaultPrinting
  from sympy.utilities import public
  from sympy.utilities.magic import pollute
  
  
  @public
  def field(symbols, domain, order=lex):
      """Construct new rational function field returning (field, x1, ..., xn). """
      _field = FracField(symbols, domain, order)
      return (_field,) + _field.gens
  
  
  @public
  def xfield(symbols, domain, order=lex):
      """Construct new rational function field returning (field, (x1, ..., xn)). """
      _field = FracField(symbols, domain, order)
      return (_field, _field.gens)
  
  
  @public
  def vfield(symbols, domain, order=lex):
      """Construct new rational function field and inject generators into global namespace. """
      _field = FracField(symbols, domain, order)
      pollute([sym.name for sym in _field.symbols], _field.gens)
      return _field
  
  
  @public
  def sfield(exprs, *symbols, **options):
      """Construct a field deriving generators and domain
      from options and input expressions.
  
      Parameters
      ==========
  
      exprs : :class:`Expr` or sequence of :class:`Expr` (sympifiable)
      symbols : sequence of :class:`Symbol`/:class:`Expr`
      options : keyword arguments understood by :class:`Options`
  
      Examples
      ========
  
      >>> from sympy.core import symbols
      >>> from sympy.functions import exp, log
      >>> from sympy.polys.fields import sfield
  
      >>> x = symbols("x")
      >>> K, f = sfield((x*log(x) + 4*x**2)*exp(1/x + log(x)/3)/x**2)
      >>> K
      Rational function field in x, exp(1/x), log(x), x**(1/3) over ZZ with lex order
      >>> f
      (4*x**2*(exp(1/x)) + x*(exp(1/x))*(log(x)))/((x**(1/3))**5)
      """
      single = False
      if not is_sequence(exprs):
          exprs, single = [exprs], True
  
      exprs = list(map(sympify, exprs))
      opt = build_options(symbols, options)
      numdens = []
      for expr in exprs:
          numdens.extend(expr.as_numer_denom())
      reps, opt = _parallel_dict_from_expr(numdens, opt)
  
      if opt.domain is None:
          # NOTE: this is inefficient because construct_domain() automatically
          # performs conversion to the target domain. It shouldn't do this.
          coeffs = sum([list(rep.values()) for rep in reps], [])
          opt.domain, _ = construct_domain(coeffs, opt=opt)
  
      _field = FracField(opt.gens, opt.domain, opt.order)
      fracs = []
      for i in range(0, len(reps), 2):
          fracs.append(_field(tuple(reps[i : i + 2])))
  
      if single:
          return (_field, fracs[0])
      else:
          return (_field, fracs)
  
  
  _field_cache = {}  # type: Dict[Any, Any]
  
  
  class FracField(DefaultPrinting):
      """Multivariate distributed rational function field. """
  
      def __new__(cls, symbols, domain, order=lex):
          from sympy.polys.rings import PolyRing
  
          ring = PolyRing(symbols, domain, order)
          symbols = ring.symbols
          ngens = ring.ngens
          domain = ring.domain
          order = ring.order
  
          _hash_tuple = (cls.__name__, symbols, ngens, domain, order)
          obj = _field_cache.get(_hash_tuple)
  
          if obj is None:
              obj = object.__new__(cls)
              obj._hash_tuple = _hash_tuple
              obj._hash = hash(_hash_tuple)
              obj.ring = ring
              obj.dtype = type("FracElement", (FracElement,), {"field": obj})
              obj.symbols = symbols
              obj.ngens = ngens
              obj.domain = domain
              obj.order = order
  
              obj.zero = obj.dtype(ring.zero)
              obj.one = obj.dtype(ring.one)
  
              obj.gens = obj._gens()
  
              for symbol, generator in zip(obj.symbols, obj.gens):
                  if isinstance(symbol, Symbol):
                      name = symbol.name
  
                      if not hasattr(obj, name):
                          setattr(obj, name, generator)
  
              _field_cache[_hash_tuple] = obj
  
          return obj
  
      def _gens(self):
          """Return a list of polynomial generators. """
          return tuple([self.dtype(gen) for gen in self.ring.gens])
  
      def __getnewargs__(self):
          return (self.symbols, self.domain, self.order)
  
      def __hash__(self):
          return self._hash
  
      def __eq__(self, other):
          return isinstance(other, FracField) and (
              self.symbols,
              self.ngens,
              self.domain,
              self.order,
          ) == (other.symbols, other.ngens, other.domain, other.order)
  
      def __ne__(self, other):
          return not self == other
  
      def raw_new(self, numer, denom=None):
          return self.dtype(numer, denom)
  
      def new(self, numer, denom=None):
          if denom is None:
              denom = self.ring.one
          numer, denom = numer.cancel(denom)
          return self.raw_new(numer, denom)
  
      def domain_new(self, element):
          return self.domain.convert(element)
  
      def ground_new(self, element):
          try:
              return self.new(self.ring.ground_new(element))
          except CoercionFailed:
              domain = self.domain
  
              if not domain.is_Field and domain.has_assoc_Field:
                  ring = self.ring
                  ground_field = domain.get_field()
                  element = ground_field.convert(element)
                  numer = ring.ground_new(ground_field.numer(element))
                  denom = ring.ground_new(ground_field.denom(element))
                  return self.raw_new(numer, denom)
              else:
                  raise
  
      def field_new(self, element):
          if isinstance(element, FracElement):
              if self == element.field:
                  return element
              else:
                  raise NotImplementedError("conversion")
          elif isinstance(element, PolyElement):
              denom, numer = element.clear_denoms()
              numer = numer.set_ring(self.ring)
              denom = self.ring.ground_new(denom)
              return self.raw_new(numer, denom)
          elif isinstance(element, tuple) and len(element) == 2:
              numer, denom = list(map(self.ring.ring_new, element))
              return self.new(numer, denom)
          elif isinstance(element, str):
              raise NotImplementedError("parsing")
          elif isinstance(element, Expr):
              return self.from_expr(element)
          else:
              return self.ground_new(element)
  
      __call__ = field_new
  
      def _rebuild_expr(self, expr, mapping):
          domain = self.domain
          powers = tuple(
              (gen, gen.as_base_exp())
              for gen in mapping.keys()
              if gen.is_Pow or isinstance(gen, ExpBase)
          )
  
          def _rebuild(expr):
              generator = mapping.get(expr)
  
              if generator is not None:
                  return generator
              elif expr.is_Add:
                  return reduce(add, list(map(_rebuild, expr.args)))
              elif expr.is_Mul:
                  return reduce(mul, list(map(_rebuild, expr.args)))
              elif expr.is_Pow or isinstance(expr, (ExpBase, Exp1)):
                  b, e = expr.as_base_exp()
                  # look for bg**eg whose integer power may be b**e
                  for gen, (bg, eg) in powers:
                      if bg == b and Mod(e, eg) == 0:
                          return mapping.get(gen) ** int(e / eg)
                  if e.is_Integer and e is not S.One:
                      return _rebuild(b) ** int(e)
  
              try:
                  return domain.convert(expr)
              except CoercionFailed:
                  if not domain.is_Field and domain.has_assoc_Field:
                      return domain.get_field().convert(expr)
                  else:
                      raise
  
          return _rebuild(sympify(expr))
  
      def from_expr(self, expr):
          mapping = dict(list(zip(self.symbols, self.gens)))
  
          try:
              frac = self._rebuild_expr(expr, mapping)
          except CoercionFailed:
              raise ValueError(
                  "expected an expression convertible to a rational function in %s, got %s"
                  % (self, expr)
              )
          else:
              return self.field_new(frac)
  
      def to_domain(self):
          return FractionField(self)
  
      def to_ring(self):
          from sympy.polys.rings import PolyRing
  
          return PolyRing(self.symbols, self.domain, self.order)
  
  
  class FracElement(DomainElement, DefaultPrinting, CantSympify):
      """Element of multivariate distributed rational function field. """
  
      def __init__(self, numer, denom=None):
          if denom is None:
              denom = self.field.ring.one
          elif not denom:
              raise ZeroDivisionError("zero denominator")
  
          self.numer = numer
          self.denom = denom
  
      def raw_new(f, numer, denom):
          return f.__class__(numer, denom)
  
      def new(f, numer, denom):
          return f.raw_new(*numer.cancel(denom))
  
      def to_poly(f):
          if f.denom != 1:
              raise ValueError("f.denom should be 1")
          return f.numer
  
      def parent(self):
          return self.field.to_domain()
  
      def __getnewargs__(self):
          return (self.field, self.numer, self.denom)
  
      _hash = None
  
      def __hash__(self):
          _hash = self._hash
          if _hash is None:
              self._hash = _hash = hash((self.field, self.numer, self.denom))
          return _hash
  
      def copy(self):
          return self.raw_new(self.numer.copy(), self.denom.copy())
  
      def set_field(self, new_field):
          if self.field == new_field:
              return self
          else:
              new_ring = new_field.ring
              numer = self.numer.set_ring(new_ring)
              denom = self.denom.set_ring(new_ring)
              return new_field.new(numer, denom)
  
      def as_expr(self, *symbols):
          return self.numer.as_expr(*symbols) / self.denom.as_expr(*symbols)
  
      def __eq__(f, g):
          if isinstance(g, FracElement) and f.field == g.field:
              return f.numer == g.numer and f.denom == g.denom
          else:
              return f.numer == g and f.denom == f.field.ring.one
  
      def __ne__(f, g):
          return not f == g
  
      def __nonzero__(f):
          return bool(f.numer)
  
      __bool__ = __nonzero__
  
      def sort_key(self):
          return (self.denom.sort_key(), self.numer.sort_key())
  
      def _cmp(f1, f2, op):
          if isinstance(f2, f1.field.dtype):
              return op(f1.sort_key(), f2.sort_key())
          else:
              return NotImplemented
  
      def __lt__(f1, f2):
          return f1._cmp(f2, lt)
  
      def __le__(f1, f2):
          return f1._cmp(f2, le)
  
      def __gt__(f1, f2):
          return f1._cmp(f2, gt)
  
      def __ge__(f1, f2):
          return f1._cmp(f2, ge)
  
      def __pos__(f):
          """Negate all coefficients in ``f``. """
          return f.raw_new(f.numer, f.denom)
  
      def __neg__(f):
          """Negate all coefficients in ``f``. """
          return f.raw_new(-f.numer, f.denom)
  
      def _extract_ground(self, element):
          domain = self.field.domain
  
          try:
              element = domain.convert(element)
          except CoercionFailed:
              if not domain.is_Field and domain.has_assoc_Field:
                  ground_field = domain.get_field()
  
                  try:
                      element = ground_field.convert(element)
                  except CoercionFailed:
                      pass
                  else:
                      return -1, ground_field.numer(element), ground_field.denom(element)
  
              return 0, None, None
          else:
              return 1, element, None
  
      def __add__(f, g):
          """Add rational functions ``f`` and ``g``. """
          field = f.field
  
          if not g:
              return f
          elif not f:
              return g
          elif isinstance(g, field.dtype):
              if f.denom == g.denom:
                  return f.new(f.numer + g.numer, f.denom)
              else:
                  return f.new(f.numer * g.denom + f.denom * g.numer, f.denom * g.denom)
          elif isinstance(g, field.ring.dtype):
              return f.new(f.numer + f.denom * g, f.denom)
          else:
              if isinstance(g, FracElement):
                  if (
                      isinstance(field.domain, FractionField)
                      and field.domain.field == g.field
                  ):
                      pass
                  elif (
                      isinstance(g.field.domain, FractionField)
                      and g.field.domain.field == field
                  ):
                      return g.__radd__(f)
                  else:
                      return NotImplemented
              elif isinstance(g, PolyElement):
                  if (
                      isinstance(field.domain, PolynomialRing)
                      and field.domain.ring == g.ring
                  ):
                      pass
                  else:
                      return g.__radd__(f)
  
          return f.__radd__(g)
  
      def __radd__(f, c):
          if isinstance(c, f.field.ring.dtype):
              return f.new(f.numer + f.denom * c, f.denom)
  
          op, g_numer, g_denom = f._extract_ground(c)
  
          if op == 1:
              return f.new(f.numer + f.denom * g_numer, f.denom)
          elif not op:
              return NotImplemented
          else:
              return f.new(f.numer * g_denom + f.denom * g_numer, f.denom * g_denom)
  
      def __sub__(f, g):
          """Subtract rational functions ``f`` and ``g``. """
          field = f.field
  
          if not g:
              return f
          elif not f:
              return -g
          elif isinstance(g, field.dtype):
              if f.denom == g.denom:
                  return f.new(f.numer - g.numer, f.denom)
              else:
                  return f.new(f.numer * g.denom - f.denom * g.numer, f.denom * g.denom)
          elif isinstance(g, field.ring.dtype):
              return f.new(f.numer - f.denom * g, f.denom)
          else:
              if isinstance(g, FracElement):
                  if (
                      isinstance(field.domain, FractionField)
                      and field.domain.field == g.field
                  ):
                      pass
                  elif (
                      isinstance(g.field.domain, FractionField)
                      and g.field.domain.field == field
                  ):
                      return g.__rsub__(f)
                  else:
                      return NotImplemented
              elif isinstance(g, PolyElement):
                  if (
                      isinstance(field.domain, PolynomialRing)
                      and field.domain.ring == g.ring
                  ):
                      pass
                  else:
                      return g.__rsub__(f)
  
          op, g_numer, g_denom = f._extract_ground(g)
  
          if op == 1:
              return f.new(f.numer - f.denom * g_numer, f.denom)
          elif not op:
              return NotImplemented
          else:
              return f.new(f.numer * g_denom - f.denom * g_numer, f.denom * g_denom)
  
      def __rsub__(f, c):
          if isinstance(c, f.field.ring.dtype):
              return f.new(-f.numer + f.denom * c, f.denom)
  
          op, g_numer, g_denom = f._extract_ground(c)
  
          if op == 1:
              return f.new(-f.numer + f.denom * g_numer, f.denom)
          elif not op:
              return NotImplemented
          else:
              return f.new(-f.numer * g_denom + f.denom * g_numer, f.denom * g_denom)
  
      def __mul__(f, g):
          """Multiply rational functions ``f`` and ``g``. """
          field = f.field
  
          if not f or not g:
              return field.zero
          elif isinstance(g, field.dtype):
              return f.new(f.numer * g.numer, f.denom * g.denom)
          elif isinstance(g, field.ring.dtype):
              return f.new(f.numer * g, f.denom)
          else:
              if isinstance(g, FracElement):
                  if (
                      isinstance(field.domain, FractionField)
                      and field.domain.field == g.field
                  ):
                      pass
                  elif (
                      isinstance(g.field.domain, FractionField)
                      and g.field.domain.field == field
                  ):
                      return g.__rmul__(f)
                  else:
                      return NotImplemented
              elif isinstance(g, PolyElement):
                  if (
                      isinstance(field.domain, PolynomialRing)
                      and field.domain.ring == g.ring
                  ):
                      pass
                  else:
                      return g.__rmul__(f)
  
          return f.__rmul__(g)
  
      def __rmul__(f, c):
          if isinstance(c, f.field.ring.dtype):
              return f.new(f.numer * c, f.denom)
  
          op, g_numer, g_denom = f._extract_ground(c)
  
          if op == 1:
              return f.new(f.numer * g_numer, f.denom)
          elif not op:
              return NotImplemented
          else:
              return f.new(f.numer * g_numer, f.denom * g_denom)
  
      def __truediv__(f, g):
          """Computes quotient of fractions ``f`` and ``g``. """
          field = f.field
  
          if not g:
              raise ZeroDivisionError
          elif isinstance(g, field.dtype):
              return f.new(f.numer * g.denom, f.denom * g.numer)
          elif isinstance(g, field.ring.dtype):
              return f.new(f.numer, f.denom * g)
          else:
              if isinstance(g, FracElement):
                  if (
                      isinstance(field.domain, FractionField)
                      and field.domain.field == g.field
                  ):
                      pass
                  elif (
                      isinstance(g.field.domain, FractionField)
                      and g.field.domain.field == field
                  ):
                      return g.__rtruediv__(f)
                  else:
                      return NotImplemented
              elif isinstance(g, PolyElement):
                  if (
                      isinstance(field.domain, PolynomialRing)
                      and field.domain.ring == g.ring
                  ):
                      pass
                  else:
                      return g.__rtruediv__(f)
  
          op, g_numer, g_denom = f._extract_ground(g)
  
          if op == 1:
              return f.new(f.numer, f.denom * g_numer)
          elif not op:
              return NotImplemented
          else:
              return f.new(f.numer * g_denom, f.denom * g_numer)
  
      __div__ = __truediv__
  
      def __rtruediv__(f, c):
          if not f:
              raise ZeroDivisionError
          elif isinstance(c, f.field.ring.dtype):
              return f.new(f.denom * c, f.numer)
  
          op, g_numer, g_denom = f._extract_ground(c)
  
          if op == 1:
              return f.new(f.denom * g_numer, f.numer)
          elif not op:
              return NotImplemented
          else:
              return f.new(f.denom * g_numer, f.numer * g_denom)
  
      __rdiv__ = __rtruediv__
  
      def __pow__(f, n):
          """Raise ``f`` to a non-negative power ``n``. """
          if n >= 0:
              return f.raw_new(f.numer ** n, f.denom ** n)
          elif not f:
              raise ZeroDivisionError
          else:
              return f.raw_new(f.denom ** -n, f.numer ** -n)
  
      def diff(f, x):
          """Computes partial derivative in ``x``.
  
          Examples
          ========
  
          >>> from sympy.polys.fields import field
          >>> from sympy.polys.domains import ZZ
  
          >>> _, x, y, z = field("x,y,z", ZZ)
          >>> ((x**2 + y)/(z + 1)).diff(x)
          2*x/(z + 1)
  
          """
          x = x.to_poly()
          return f.new(
              f.numer.diff(x) * f.denom - f.numer * f.denom.diff(x), f.denom ** 2
          )
  
      def __call__(f, *values):
          if 0 < len(values) <= f.field.ngens:
              return f.evaluate(list(zip(f.field.gens, values)))
          else:
              raise ValueError(
                  "expected at least 1 and at most %s values, got %s"
                  % (f.field.ngens, len(values))
              )
  
      def evaluate(f, x, a=None):
          if isinstance(x, list) and a is None:
              x = [(X.to_poly(), a) for X, a in x]
              numer, denom = f.numer.evaluate(x), f.denom.evaluate(x)
          else:
              x = x.to_poly()
              numer, denom = f.numer.evaluate(x, a), f.denom.evaluate(x, a)
  
          field = numer.ring.to_field()
          return field.new(numer, denom)
  
      def subs(f, x, a=None):
          if isinstance(x, list) and a is None:
              x = [(X.to_poly(), a) for X, a in x]
              numer, denom = f.numer.subs(x), f.denom.subs(x)
          else:
              x = x.to_poly()
              numer, denom = f.numer.subs(x, a), f.denom.subs(x, a)
  
          return f.new(numer, denom)
  
      def compose(f, x, a=None):
          raise NotImplementedError
  