code: def tschirnhausen_transformation(
      T, max_coeff=10, max_tries=30, history=None, fixed_order=True
  ):
      r"""
      Given a univariate, monic, irreducible polynomial over the integers, find
      another such polynomial defining the same number field.
  
      Explanation
      ===========
  
      See Alg 6.3.4 of [1].
  
      Parameters
      ==========
  
      T : Poly
          The given polynomial
      max_coeff : int
          When choosing a transformation as part of the process,
          keep the coeffs between plus and minus this.
      max_tries : int
          Consider at most this many transformations.
      history : set, None, optional (default=None)
          Pass a set of ``Poly.rep``'s in order to prevent any of these
          polynomials from being returned as the polynomial ``U`` i.e. the
          transformation of the given polynomial *T*. The given poly *T* will
          automatically be added to this set, before we try to find a new one.
      fixed_order : bool, default True
          If ``True``, work through candidate transformations A(x) in a fixed
          order, from small coeffs to large, resulting in deterministic behavior.
          If ``False``, the A(x) are chosen randomly, while still working our way
          up from small coefficients to larger ones.
  
      Returns
      =======
  
      Pair ``(A, U)``
  
          ``A`` and ``U`` are ``Poly``, ``A`` is the
          transformation, and ``U`` is the transformed polynomial that defines
          the same number field as *T*. The polynomial ``A`` maps the roots of
          *T* to the roots of ``U``.
  
      Raises
      ======
  
      MaxTriesException
          if could not find a polynomial before exceeding *max_tries*.
  
      """
      X = Dummy("X")
      n = T.degree()
      if history is None:
          history = set()
      history.add(T.rep)
  
      if fixed_order:
          coeff_generators = {}
          deg_coeff_sum = 3
          current_degree = 2
  
      def get_coeff_generator(degree):
          gen = coeff_generators.get(degree, coeff_search(degree, 1))
          coeff_generators[degree] = gen
          return gen
  
      for i in range(max_tries):
          # We never use linear A(x), since applying a fixed linear transformation
          # to all roots will only multiply the discriminant of T by a square
          # integer. This will change nothing important. In particular, if disc(T)
          # was zero before, it will still be zero now, and typically we apply
          # the transformation in hopes of replacing T by a squarefree poly.
  
          if fixed_order:
              # If d is degree and c max coeff, we move through the dc-space
              # along lines of constant sum. First d + c = 3 with (d, c) = (2, 1).
              # Then d + c = 4 with (d, c) = (3, 1), (2, 2). Then d + c = 5 with
              # (d, c) = (4, 1), (3, 2), (2, 3), and so forth. For a given (d, c)
              # we go though all sets of coeffs where max = c, before moving on.
              gen = get_coeff_generator(current_degree)
              coeffs = next(gen)
              m = max(abs(c) for c in coeffs)
              if current_degree + m > deg_coeff_sum:
                  if current_degree == 2:
                      deg_coeff_sum += 1
                      current_degree = deg_coeff_sum - 1
                  else:
                      current_degree -= 1
                  gen = get_coeff_generator(current_degree)
                  coeffs = next(gen)
              a = [ZZ(1)] + [ZZ(c) for c in coeffs]
  
          else:
              # We use a progressive coeff bound, up to the max specified, since it
              # is preferable to succeed with smaller coeffs.
              # Give each coeff bound five tries, before incrementing.
              C = min(i // 5 + 1, max_coeff)
              d = random.randint(2, n - 1)
              a = dup_random(d, -C, C, ZZ)
  
          A = Poly(a, T.gen)
          U = Poly(T.resultant(X - A), X)
          if U.rep not in history and dup_sqf_p(U.rep.to_list(), ZZ):
              return A, U
      raise MaxTriesException
  
imports: from collections import defaultdict
  import random
  from sympy.core.symbol import Dummy, symbols
  from sympy.ntheory.primetest import is_square
  from sympy.polys.domains import ZZ
  from sympy.polys.densebasic import dup_random
  from sympy.polys.densetools import dup_eval
  from sympy.polys.euclidtools import dup_discriminant
  from sympy.polys.factortools import dup_factor_list, dup_irreducible_p
  from sympy.polys.numberfields.galois_resolvents import GaloisGroupException, get_resolvent_by_lookup, define_resolvents, Resolvent
  from sympy.polys.numberfields.utilities import coeff_search
  from sympy.polys.polytools import Poly, poly_from_expr, PolificationFailed, ComputationFailed
  from sympy.polys.sqfreetools import dup_sqf_p
  from sympy.utilities import public
label_desc: Use image node_id 1 for calling a global function with example usage: tschirnhausen_transformation(T, max_coeff, max_tries, history, fixed_order) and returns: gen, A,  U