code: def dmp_zz_wang(f, u, K, mod=None, seed=None):
      r"""
      Factor primitive square-free polynomials in `Z[X]`.
  
      Given a multivariate polynomial `f` in `Z[x_1,...,x_n]`, which is
      primitive and square-free in `x_1`, computes factorization of `f` into
      irreducibles over integers.
  
      The procedure is based on Wang's Enhanced Extended Zassenhaus
      algorithm. The algorithm works by viewing `f` as a univariate polynomial
      in `Z[x_2,...,x_n][x_1]`, for which an evaluation mapping is computed::
  
                        x_2 -> a_2, ..., x_n -> a_n
  
      where `a_i`, for `i = 2, \dots, n`, are carefully chosen integers.  The
      mapping is used to transform `f` into a univariate polynomial in `Z[x_1]`,
      which can be factored efficiently using Zassenhaus algorithm. The last
      step is to lift univariate factors to obtain true multivariate
      factors. For this purpose a parallel Hensel lifting procedure is used.
  
      The parameter ``seed`` is passed to _randint and can be used to seed randint
      (when an integer) or (for testing purposes) can be a sequence of numbers.
  
      References
      ==========
  
      .. [1] [Wang78]_
      .. [2] [Geddes92]_
  
      """
      from sympy.ntheory import nextprime
  
      randint = _randint(seed)
  
      ct, T = dmp_zz_factor(dmp_LC(f, K), u - 1, K)
  
      b = dmp_zz_mignotte_bound(f, u, K)
      p = K(nextprime(b))
  
      if mod is None:
          if u == 1:
              mod = 2
          else:
              mod = 1
  
      history, configs, A, r = set(), [], [K.zero] * u, None
  
      try:
          cs, s, E = dmp_zz_wang_test_points(f, T, ct, A, u, K)
  
          _, H = dup_zz_factor_sqf(s, K)
  
          r = len(H)
  
          if r == 1:
              return [f]
  
          configs = [(s, cs, E, H, A)]
      except EvaluationFailed:
          pass
  
      eez_num_configs = query("EEZ_NUMBER_OF_CONFIGS")
      eez_num_tries = query("EEZ_NUMBER_OF_TRIES")
      eez_mod_step = query("EEZ_MODULUS_STEP")
  
      while len(configs) < eez_num_configs:
          for _ in range(eez_num_tries):
              A = [K(randint(-mod, mod)) for _ in range(u)]
  
              if tuple(A) not in history:
                  history.add(tuple(A))
              else:
                  continue
  
              try:
                  cs, s, E = dmp_zz_wang_test_points(f, T, ct, A, u, K)
              except EvaluationFailed:
                  continue
  
              _, H = dup_zz_factor_sqf(s, K)
  
              rr = len(H)
  
              if r is not None:
                  if rr != r:  # pragma: no cover
                      if rr < r:
                          configs, r = [], rr
                      else:
                          continue
              else:
                  r = rr
  
              if r == 1:
                  return [f]
  
              configs.append((s, cs, E, H, A))
  
              if len(configs) == eez_num_configs:
                  break
          else:
              mod += eez_mod_step
  
      s_norm, s_arg, i = None, 0, 0
  
      for s, _, _, _, _ in configs:
          _s_norm = dup_max_norm(s, K)
  
          if s_norm is not None:
              if _s_norm < s_norm:
                  s_norm = _s_norm
                  s_arg = i
          else:
              s_norm = _s_norm
  
          i += 1
  
      _, cs, E, H, A = configs[s_arg]
      orig_f = f
  
      try:
          f, H, LC = dmp_zz_wang_lead_coeffs(f, T, cs, E, H, A, u, K)
          factors = dmp_zz_wang_hensel_lifting(f, H, LC, A, p, u, K)
      except ExtraneousFactors:  # pragma: no cover
          if query("EEZ_RESTART_IF_NEEDED"):
              return dmp_zz_wang(orig_f, u, K, mod + 1)
          else:
              raise ExtraneousFactors(
                  "we need to restart algorithm with better parameters"
              )
  
      result = []
  
      for f in factors:
          _, f = dmp_ground_primitive(f, u, K)
  
          if K.is_negative(dmp_ground_LC(f, u, K)):
              f = dmp_neg(f, u, K)
  
          result.append(f)
  
      return result
  
imports: from sympy.external.gmpy import GROUND_TYPES
  from sympy.core.random import _randint
  from sympy.polys.galoistools import gf_from_int_poly, gf_to_int_poly, gf_lshift, gf_add_mul, gf_mul, gf_div, gf_rem, gf_gcdex, gf_sqf_p, gf_factor_sqf, gf_factor
  from sympy.polys.densebasic import dup_LC, dmp_LC, dmp_ground_LC, dup_TC, dup_convert, dmp_convert, dup_degree, dmp_degree, dmp_degree_in, dmp_degree_list, dmp_from_dict, dmp_zero_p, dmp_one, dmp_nest, dmp_raise, dup_strip, dmp_ground, dup_inflate, dmp_exclude, dmp_include, dmp_inject, dmp_eject, dup_terms_gcd, dmp_terms_gcd
  from sympy.polys.densearith import dup_neg, dmp_neg, dup_add, dmp_add, dup_sub, dmp_sub, dup_mul, dmp_mul, dup_sqr, dmp_pow, dup_div, dmp_div, dup_quo, dmp_quo, dmp_expand, dmp_add_mul, dup_sub_mul, dmp_sub_mul, dup_lshift, dup_max_norm, dmp_max_norm, dup_l1_norm, dup_mul_ground, dmp_mul_ground, dup_quo_ground, dmp_quo_ground
  from sympy.polys.densetools import dup_clear_denoms, dmp_clear_denoms, dup_trunc, dmp_ground_trunc, dup_content, dup_monic, dmp_ground_monic, dup_primitive, dmp_ground_primitive, dmp_eval_tail, dmp_eval_in, dmp_diff_eval_in, dmp_compose, dup_shift, dup_mirror
  from sympy.polys.euclidtools import dmp_primitive, dup_inner_gcd, dmp_inner_gcd
  from sympy.polys.sqfreetools import dup_sqf_p, dup_sqf_norm, dmp_sqf_norm, dup_sqf_part, dmp_sqf_part
  from sympy.polys.polyutils import _sort_factors
  from sympy.polys.polyconfig import query
  from sympy.polys.polyerrors import ExtraneousFactors, DomainError, CoercionFailed, EvaluationFailed
  from sympy.utilities import subsets
  from math import ceil, log
label_desc: Use image node_id 22 for calling a global function with example usage: dmp_zz_wang(f, u, K, mod, seed) and returns: result, list, list, dmp_zz_wang