code: def roots(
      f,
      *gens,
      auto=True,
      cubics=True,
      trig=False,
      quartics=True,
      quintics=False,
      multiple=False,
      filter=None,
      predicate=None,
      strict=False,
      **flags,
  ):
      """
      Computes symbolic roots of a univariate polynomial.
  
      Given a univariate polynomial f with symbolic coefficients (or
      a list of the polynomial's coefficients), returns a dictionary
      with its roots and their multiplicities.
  
      Only roots expressible via radicals will be returned.  To get
      a complete set of roots use RootOf class or numerical methods
      instead. By default cubic and quartic formulas are used in
      the algorithm. To disable them because of unreadable output
      set ``cubics=False`` or ``quartics=False`` respectively. If cubic
      roots are real but are expressed in terms of complex numbers
      (casus irreducibilis [1]) the ``trig`` flag can be set to True to
      have the solutions returned in terms of cosine and inverse cosine
      functions.
  
      To get roots from a specific domain set the ``filter`` flag with
      one of the following specifiers: Z, Q, R, I, C. By default all
      roots are returned (this is equivalent to setting ``filter='C'``).
  
      By default a dictionary is returned giving a compact result in
      case of multiple roots.  However to get a list containing all
      those roots set the ``multiple`` flag to True; the list will
      have identical roots appearing next to each other in the result.
      (For a given Poly, the all_roots method will give the roots in
      sorted numerical order.)
  
      If the ``strict`` flag is True, ``UnsolvableFactorError`` will be
      raised if the roots found are known to be incomplete (because
      some roots are not expressible in radicals).
  
      Examples
      ========
  
      >>> from sympy import Poly, roots, degree
      >>> from sympy.abc import x, y
  
      >>> roots(x**2 - 1, x)
      {-1: 1, 1: 1}
  
      >>> p = Poly(x**2-1, x)
      >>> roots(p)
      {-1: 1, 1: 1}
  
      >>> p = Poly(x**2-y, x, y)
  
      >>> roots(Poly(p, x))
      {-sqrt(y): 1, sqrt(y): 1}
  
      >>> roots(x**2 - y, x)
      {-sqrt(y): 1, sqrt(y): 1}
  
      >>> roots([1, 0, -1])
      {-1: 1, 1: 1}
  
      ``roots`` will only return roots expressible in radicals. If
      the given polynomial has some or all of its roots inexpressible in
      radicals, the result of ``roots`` will be incomplete or empty
      respectively.
  
      Example where result is incomplete:
  
      >>> roots((x-1)*(x**5-x+1), x)
      {1: 1}
  
      In this case, the polynomial has an unsolvable quintic factor
      whose roots cannot be expressed by radicals. The polynomial has a
      rational root (due to the factor `(x-1)`), which is returned since
      ``roots`` always finds all rational roots.
  
      Example where result is empty:
  
      >>> roots(x**7-3*x**2+1, x)
      {}
  
      Here, the polynomial has no roots expressible in radicals, so
      ``roots`` returns an empty dictionary.
  
      The result produced by ``roots`` is complete if and only if the
      sum of the multiplicity of each root is equal to the degree of
      the polynomial. If strict=True, UnsolvableFactorError will be
      raised if the result is incomplete.
  
      The result can be be checked for completeness as follows:
  
      >>> f = x**3-2*x**2+1
      >>> sum(roots(f, x).values()) == degree(f, x)
      True
      >>> f = (x-1)*(x**5-x+1)
      >>> sum(roots(f, x).values()) == degree(f, x)
      False
  
  
      References
      ==========
  
      .. [1] https://en.wikipedia.org/wiki/Cubic_equation#Trigonometric_and_hyperbolic_solutions
  
      """
      from sympy.polys.polytools import to_rational_coeffs
  
      flags = dict(flags)
  
      if isinstance(f, list):
          if gens:
              raise ValueError("redundant generators given")
  
          x = Dummy("x")
  
          poly, i = {}, len(f) - 1
  
          for coeff in f:
              poly[i], i = sympify(coeff), i - 1
  
          f = Poly(poly, x, field=True)
      else:
          try:
              F = Poly(f, *gens, **flags)
              if not isinstance(f, Poly) and not F.gen.is_Symbol:
                  raise PolynomialError("generator must be a Symbol")
              f = F
          except GeneratorsNeeded:
              if multiple:
                  return []
              else:
                  return {}
          else:
              n = f.degree()
              if f.length() == 2 and n > 2:
                  # check for foo**n in constant if dep is c*gen**m
                  con, dep = f.as_expr().as_independent(*f.gens)
                  fcon = -(-con).factor()
                  if fcon != con:
                      con = fcon
                      bases = []
                      for i in Mul.make_args(con):
                          if i.is_Pow:
                              b, e = i.as_base_exp()
                              if e.is_Integer and b.is_Add:
                                  bases.append(
                                      (b, Dummy(positive=True))
                                  )
                      if bases:
                          rv = roots(
                              Poly(
                                  (dep + con).xreplace(dict(bases)),
                                  *f.gens,
                              ),
                              *F.gens,
                              auto=auto,
                              cubics=cubics,
                              trig=trig,
                              quartics=quartics,
                              quintics=quintics,
                              multiple=multiple,
                              filter=filter,
                              predicate=predicate,
                              **flags,
                          )
                          return {
                              factor_terms(
                                  k.xreplace({v: k for k, v in bases})
                              ): v
                              for k, v in rv.items()
                          }
  
          if f.is_multivariate:
              raise PolynomialError(
                  "multivariate polynomials are not supported"
              )
  
      def _update_dict(result, zeros, currentroot, k):
          if currentroot == S.Zero:
              if S.Zero in zeros:
                  zeros[S.Zero] += k
              else:
                  zeros[S.Zero] = k
          if currentroot in result:
              result[currentroot] += k
          else:
              result[currentroot] = k
  
      def _try_decompose(f):
          """Find roots using functional decomposition."""
          factors, roots = f.decompose(), []
  
          for currentroot in _try_heuristics(factors[0]):
              roots.append(currentroot)
  
          for currentfactor in factors[1:]:
              previous, roots = list(roots), []
  
              for currentroot in previous:
                  g = currentfactor - Poly(currentroot, f.gen)
  
                  for currentroot in _try_heuristics(g):
                      roots.append(currentroot)
  
          return roots
  
      def _try_heuristics(f):
          """Find roots using formulas and some tricks."""
          if f.is_ground:
              return []
          if f.is_monomial:
              return [S.Zero] * f.degree()
  
          if f.length() == 2:
              if f.degree() == 1:
                  return list(map(cancel, roots_linear(f)))
              else:
                  return roots_binomial(f)
  
          result = []
  
          for i in [-1, 1]:
              if not f.eval(i):
                  f = f.quo(Poly(f.gen - i, f.gen))
                  result.append(i)
                  break
  
          n = f.degree()
  
          if n == 1:
              result += list(map(cancel, roots_linear(f)))
          elif n == 2:
              result += list(map(cancel, roots_quadratic(f)))
          elif f.is_cyclotomic:
              result += roots_cyclotomic(f)
          elif n == 3 and cubics:
              result += roots_cubic(f, trig=trig)
          elif n == 4 and quartics:
              result += roots_quartic(f)
          elif n == 5 and quintics:
              result += roots_quintic(f)
  
          return result
  
      # Convert the generators to symbols
      dumgens = symbols("x:%d" % len(f.gens), cls=Dummy)
      f = f.per(f.rep, dumgens)
  
      (k,), f = f.terms_gcd()
  
      if not k:
          zeros = {}
      else:
          zeros = {S.Zero: k}
  
      coeff, f = preprocess_roots(f)
  
      if auto and f.get_domain().is_Ring:
          f = f.to_field()
  
      # Use EX instead of ZZ_I or QQ_I
      if f.get_domain().is_QQ_I:
          f = f.per(f.rep.convert(EX))
  
      rescale_x = None
      translate_x = None
  
      result = {}
  
      if not f.is_ground:
          dom = f.get_domain()
          if not dom.is_Exact and dom.is_Numerical:
              for r in f.nroots():
                  _update_dict(result, zeros, r, 1)
          elif f.degree() == 1:
              _update_dict(result, zeros, roots_linear(f)[0], 1)
          elif f.length() == 2:
              roots_fun = (
                  roots_quadratic if f.degree() == 2 else roots_binomial
              )
              for r in roots_fun(f):
                  _update_dict(result, zeros, r, 1)
          else:
              _, factors = Poly(f.as_expr()).factor_list()
              if len(factors) == 1 and f.degree() == 2:
                  for r in roots_quadratic(f):
                      _update_dict(result, zeros, r, 1)
              else:
                  if len(factors) == 1 and factors[0][1] == 1:
                      if f.get_domain().is_EX:
                          res = to_rational_coeffs(f)
                          if res:
                              if res[0] is None:
                                  translate_x, f = res[2:]
                              else:
                                  rescale_x, f = res[1], res[-1]
                              result = roots(f)
                              if not result:
                                  for currentroot in _try_decompose(f):
                                      _update_dict(
                                          result, zeros, currentroot, 1
                                      )
                          else:
                              for r in _try_heuristics(f):
                                  _update_dict(result, zeros, r, 1)
                      else:
                          for currentroot in _try_decompose(f):
                              _update_dict(
                                  result, zeros, currentroot, 1
                              )
                  else:
                      for currentfactor, k in factors:
                          for r in _try_heuristics(
                              Poly(currentfactor, f.gen, field=True)
                          ):
                              _update_dict(result, zeros, r, k)
  
      if coeff is not S.One:
          (
              _result,
              result,
          ) = (
              result,
              {},
          )
  
          for currentroot, k in _result.items():
              result[coeff * currentroot] = k
  
      if filter not in [None, "C"]:
          handlers = {
              "Z": lambda r: r.is_Integer,
              "Q": lambda r: r.is_Rational,
              "R": lambda r: all(a.is_real for a in r.as_numer_denom()),
              "I": lambda r: r.is_imaginary,
          }
  
          try:
              query = handlers[filter]
          except KeyError:
              raise ValueError("Invalid filter: %s" % filter)
  
          for zero in dict(result).keys():
              if not query(zero):
                  del result[zero]
  
      if predicate is not None:
          for zero in dict(result).keys():
              if not predicate(zero):
                  del result[zero]
      if rescale_x:
          result1 = {}
          for k, v in result.items():
              result1[k * rescale_x] = v
          result = result1
      if translate_x:
          result1 = {}
          for k, v in result.items():
              result1[k + translate_x] = v
          result = result1
  
      # adding zero roots after non-trivial roots have been translated
      result.update(zeros)
  
      if strict and sum(result.values()) < f.degree():
          raise UnsolvableFactorError(
              filldedent(
                  """
              Strict mode: some factors cannot be solved in radicals, so
              a complete list of solutions cannot be returned. Call
              roots with strict=False to get solutions expressible in
              radicals (if there are any).
              """
              )
          )
  
      if not multiple:
          return result
      else:
          zeros = []
  
          for zero in ordered(result):
              zeros.extend([zero] * result[zero])
  
          return zeros
  
imports: import math
  from functools import reduce
  from sympy.core import S, I, pi
  from sympy.core.exprtools import factor_terms
  from sympy.core.function import _mexpand
  from sympy.core.logic import fuzzy_not
  from sympy.core.mul import expand_2arg, Mul
  from sympy.core.intfunc import igcd
  from sympy.core.numbers import Rational, comp
  from sympy.core.power import Pow
  from sympy.core.relational import Eq
  from sympy.core.sorting import ordered
  from sympy.core.symbol import Dummy, Symbol, symbols
  from sympy.core.sympify import sympify
  from sympy.functions import exp, im, cos, acos, Piecewise
  from sympy.functions.elementary.miscellaneous import root, sqrt
  from sympy.ntheory import divisors, isprime, nextprime
  from sympy.polys.domains import EX
  from sympy.polys.polyerrors import PolynomialError, GeneratorsNeeded, DomainError, UnsolvableFactorError
  from sympy.polys.polyquinticconst import PolyQuintic
  from sympy.polys.polytools import Poly, cancel, factor, gcd_list, discriminant
  from sympy.polys.rationaltools import together
  from sympy.polys.specialpolys import cyclotomic_poly
  from sympy.utilities import public
  from sympy.utilities.misc import filldedent
label_desc: Use image node_id 13 for calling a global function with example usage: roots(f) and returns: roots, result, result, zeros, list, unknown, list, roots_binomial, list, dict, unknown