code: def round_two(T, radicals=None):
      r"""
      Zassenhaus's "Round 2" algorithm.
  
      Explanation
      ===========
  
      Carry out Zassenhaus's "Round 2" algorithm on an irreducible polynomial
      *T* over :ref:`ZZ` or :ref:`QQ`. This computes an integral basis and the
      discriminant for the field $K = \mathbb{Q}[x]/(T(x))$.
  
      Alternatively, you may pass an :py:class:`~.AlgebraicField` instance, in
      place of the polynomial *T*, in which case the algorithm is applied to the
      minimal polynomial for the field's primitive element.
  
      Ordinarily this function need not be called directly, as one can instead
      access the :py:meth:`~.AlgebraicField.maximal_order`,
      :py:meth:`~.AlgebraicField.integral_basis`, and
      :py:meth:`~.AlgebraicField.discriminant` methods of an
      :py:class:`~.AlgebraicField`.
  
      Examples
      ========
  
      Working through an AlgebraicField:
  
      >>> from sympy import Poly, QQ
      >>> from sympy.abc import x
      >>> T = Poly(x ** 3 + x ** 2 - 2 * x + 8)
      >>> K = QQ.alg_field_from_poly(T, "theta")
      >>> print(K.maximal_order())
      Submodule[[2, 0, 0], [0, 2, 0], [0, 1, 1]]/2
      >>> print(K.discriminant())
      -503
      >>> print(K.integral_basis(fmt='sympy'))
      [1, theta, theta/2 + theta**2/2]
  
      Calling directly:
  
      >>> from sympy import Poly
      >>> from sympy.abc import x
      >>> from sympy.polys.numberfields.basis import round_two
      >>> T = Poly(x ** 3 + x ** 2 - 2 * x + 8)
      >>> print(round_two(T))
      (Submodule[[2, 0, 0], [0, 2, 0], [0, 1, 1]]/2, -503)
  
      The nilradicals mod $p$ that are sometimes computed during the Round Two
      algorithm may be useful in further calculations. Pass a dictionary under
      `radicals` to receive these:
  
      >>> T = Poly(x**3 + 3*x**2 + 5)
      >>> rad = {}
      >>> ZK, dK = round_two(T, radicals=rad)
      >>> print(rad)
      {3: Submodule[[-1, 1, 0], [-1, 0, 1]]}
  
      Parameters
      ==========
  
      T : :py:class:`~.Poly`, :py:class:`~.AlgebraicField`
          Either (1) the irreducible polynomial over :ref:`ZZ` or :ref:`QQ`
          defining the number field, or (2) an :py:class:`~.AlgebraicField`
          representing the number field itself.
  
      radicals : dict, optional
          This is a way for any $p$-radicals (if computed) to be returned by
          reference. If desired, pass an empty dictionary. If the algorithm
          reaches the point where it computes the nilradical mod $p$ of the ring
          of integers $Z_K$, then an $\mathbb{F}_p$-basis for this ideal will be
          stored in this dictionary under the key ``p``. This can be useful for
          other algorithms, such as prime decomposition.
  
      Returns
      =======
  
      Pair ``(ZK, dK)``, where:
  
          ``ZK`` is a :py:class:`~sympy.polys.numberfields.modules.Submodule`
          representing the maximal order.
  
          ``dK`` is the discriminant of the field $K = \mathbb{Q}[x]/(T(x))$.
  
      See Also
      ========
  
      .AlgebraicField.maximal_order
      .AlgebraicField.integral_basis
      .AlgebraicField.discriminant
  
      References
      ==========
  
      .. [1] Cohen, H. *A Course in Computational Algebraic Number Theory.*
  
      """
      K = None
      if isinstance(T, AlgebraicField):
          K, T = T, T.ext.minpoly_of_element()
      if (
          not T.is_univariate
          or not T.is_irreducible
          or T.domain not in [ZZ, QQ]
      ):
          raise ValueError(
              "Round 2 requires an irreducible univariate polynomial over ZZ or QQ."
          )
      T, _ = T.make_monic_over_integers_by_scaling_roots()
      n = T.degree()
      D = T.discriminant()
      D_modulus = ZZ.from_sympy(abs(D))
      # D must be 0 or 1 mod 4 (see Cohen Sec 4.4), which ensures we can write
      # it in the form D = D_0 * F**2, where D_0 is 1 or a fundamental discriminant.
      _, F = extract_fundamental_discriminant(D)
      Ztheta = PowerBasis(K or T)
      H = Ztheta.whole_submodule()
      nilrad = None
      while F:
          # Next prime:
          p, e = F.popitem()
          U_bar, m = _apply_Dedekind_criterion(T, p)
          if m == 0:
              continue
          # For a given prime p, the first enlargement of the order spanned by
          # the current basis can be done in a simple way:
          U = Ztheta.element_from_poly(Poly(U_bar, domain=ZZ))
          # TODO:
          #  Theory says only first m columns of the U//p*H term below are needed.
          #  Could be slightly more efficient to use only those. Maybe `Submodule`
          #  class should support a slice operator?
          H = H.add(U // p * H, hnf_modulus=D_modulus)
          if e <= m:
              continue
          # A second, and possibly more, enlargements for p will be needed.
          # These enlargements require a more involved procedure.
          q = p
          while q < n:
              q *= p
          H1, nilrad = _second_enlargement(H, p, q)
          while H1 != H:
              H = H1
              H1, nilrad = _second_enlargement(H, p, q)
      # Note: We do not store all nilradicals mod p, only the very last. This is
      # because, unless computed against the entire integral basis, it might not
      # be accurate. (In other words, if H was not already equal to ZK when we
      # passed it to `_second_enlargement`, then we can't trust the nilradical
      # so computed.) Example: if T(x) = x ** 3 + 15 * x ** 2 - 9 * x + 13, then
      # F is divisible by 2, 3, and 7, and the nilradical mod 2 as computed above
      # will not be accurate for the full, maximal order ZK.
      if nilrad is not None and isinstance(radicals, dict):
          radicals[p] = nilrad
      ZK = H
      # Pre-set expensive boolean properties which we already know to be true:
      ZK._starts_with_unity = True
      ZK._is_sq_maxrank_HNF = True
      dK = (D * ZK.matrix.det() ** 2) // ZK.denom ** (2 * n)
      return ZK, dK
  
file_path: sympy/sympy/polys/numberfields/basis.py
imports: from sympy.polys.polytools import Poly
  from sympy.polys.domains.algebraicfield import AlgebraicField
  from sympy.polys.domains.integerring import ZZ
  from sympy.polys.domains.rationalfield import QQ
  from sympy.utilities.decorator import public
  from .modules import ModuleEndomorphism, ModuleHomomorphism, PowerBasis
  from .utilities import extract_fundamental_discriminant
label_desc: Use image node_id 4 for calling a global function with example usage: round_two(T, radicals) and returns: ZK,  dK