text: import itertools
  
  from sympy.core import S
  from sympy.core.containers import Tuple
  from sympy.core.function import _coeff_isneg
  from sympy.core.mul import Mul
  from sympy.core.numbers import Number, Rational
  from sympy.core.power import Pow
  from sympy.core.symbol import Symbol
  from sympy.core.sympify import SympifyError
  from sympy.printing.conventions import requires_partial
  from sympy.printing.precedence import PRECEDENCE, precedence, precedence_traditional
  from sympy.printing.printer import Printer, print_function
  from sympy.printing.str import sstr
  from sympy.utilities import default_sort_key
  from sympy.utilities.iterables import has_variety
  from sympy.utilities.exceptions import SymPyDeprecationWarning
  
  from sympy.printing.pretty.stringpict import prettyForm, stringPict
  from sympy.printing.pretty.pretty_symbology import hobj, vobj, xobj, \
      xsym, pretty_symbol, pretty_atom, pretty_use_unicode, greek_unicode, U, \
      pretty_try_use_unicode,  annotated
  
  # rename for usage from outside
  pprint_use_unicode = pretty_use_unicode
  pprint_try_use_unicode = pretty_try_use_unicode
  
  
  class PrettyPrinter(Printer):
      """Printer, which converts an expression into 2D ASCII-art figure."""
      printmethod = "_pretty"
  
      _default_settings = {
          "order": None,
          "full_prec": "auto",
          "use_unicode": None,
          "wrap_line": True,
          "num_columns": None,
          "use_unicode_sqrt_char": True,
          "root_notation": True,
          "mat_symbol_style": "plain",
          "imaginary_unit": "i",
          "perm_cyclic": True
      }
  
      def __init__(self, settings=None):
          Printer.__init__(self, settings)
  
          if not isinstance(self._settings['imaginary_unit'], str):
              raise TypeError("'imaginary_unit' must a string, not {}".format(self._settings['imaginary_unit']))
          elif self._settings['imaginary_unit'] not in ["i", "j"]:
              raise ValueError("'imaginary_unit' must be either 'i' or 'j', not '{}'".format(self._settings['imaginary_unit']))
  
      def emptyPrinter(self, expr):
          return prettyForm(str(expr))
  
      @property
      def _use_unicode(self):
          if self._settings['use_unicode']:
              return True
          else:
              return pretty_use_unicode()
  
      def doprint(self, expr):
          return self._print(expr).render(**self._settings)
  
      # empty op so _print(stringPict) returns the same
      def _print_stringPict(self, e):
          return e
  
      def _print_basestring(self, e):
          return prettyForm(e)
  
      def _print_atan2(self, e):
          pform = prettyForm(*self._print_seq(e.args).parens())
          pform = prettyForm(*pform.left('atan2'))
          return pform
  
      def _print_Symbol(self, e, bold_name=False):
          symb = pretty_symbol(e.name, bold_name)
          return prettyForm(symb)
      _print_RandomSymbol = _print_Symbol
      def _print_MatrixSymbol(self, e):
          return self._print_Symbol(e, self._settings['mat_symbol_style'] == "bold")
  
      def _print_Float(self, e):
          # we will use StrPrinter's Float printer, but we need to handle the
          # full_prec ourselves, according to the self._print_level
          full_prec = self._settings["full_prec"]
          if full_prec == "auto":
              full_prec = self._print_level == 1
          return prettyForm(sstr(e, full_prec=full_prec))
  
      def _print_Cross(self, e):
          vec1 = e._expr1
          vec2 = e._expr2
          pform = self._print(vec2)
          pform = prettyForm(*pform.left('('))
          pform = prettyForm(*pform.right(')'))
          pform = prettyForm(*pform.left(self._print(U('MULTIPLICATION SIGN'))))
          pform = prettyForm(*pform.left(')'))
          pform = prettyForm(*pform.left(self._print(vec1)))
          pform = prettyForm(*pform.left('('))
          return pform
  
      def _print_Curl(self, e):
          vec = e._expr
          pform = self._print(vec)
          pform = prettyForm(*pform.left('('))
          pform = prettyForm(*pform.right(')'))
          pform = prettyForm(*pform.left(self._print(U('MULTIPLICATION SIGN'))))
          pform = prettyForm(*pform.left(self._print(U('NABLA'))))
          return pform
  
      def _print_Divergence(self, e):
          vec = e._expr
          pform = self._print(vec)
          pform = prettyForm(*pform.left('('))
          pform = prettyForm(*pform.right(')'))
          pform = prettyForm(*pform.left(self._print(U('DOT OPERATOR'))))
          pform = prettyForm(*pform.left(self._print(U('NABLA'))))
          return pform
  
      def _print_Dot(self, e):
          vec1 = e._expr1
          vec2 = e._expr2
          pform = self._print(vec2)
          pform = prettyForm(*pform.left('('))
          pform = prettyForm(*pform.right(')'))
          pform = prettyForm(*pform.left(self._print(U('DOT OPERATOR'))))
          pform = prettyForm(*pform.left(')'))
          pform = prettyForm(*pform.left(self._print(vec1)))
          pform = prettyForm(*pform.left('('))
          return pform
  
      def _print_Gradient(self, e):
          func = e._expr
          pform = self._print(func)
          pform = prettyForm(*pform.left('('))
          pform = prettyForm(*pform.right(')'))
          pform = prettyForm(*pform.left(self._print(U('NABLA'))))
          return pform
  
      def _print_Laplacian(self, e):
          func = e._expr
          pform = self._print(func)
          pform = prettyForm(*pform.left('('))
          pform = prettyForm(*pform.right(')'))
          pform = prettyForm(*pform.left(self._print(U('INCREMENT'))))
          return pform
  
      def _print_Atom(self, e):
          try:
              # print atoms like Exp1 or Pi
              return prettyForm(pretty_atom(e.__class__.__name__, printer=self))
          except KeyError:
              return self.emptyPrinter(e)
  
      # Infinity inherits from Number, so we have to override _print_XXX order
      _print_Infinity = _print_Atom
      _print_NegativeInfinity = _print_Atom
      _print_EmptySet = _print_Atom
      _print_Naturals = _print_Atom
      _print_Naturals0 = _print_Atom
      _print_Integers = _print_Atom
      _print_Rationals = _print_Atom
      _print_Complexes = _print_Atom
  
      _print_EmptySequence = _print_Atom
  
      def _print_Reals(self, e):
          if self._use_unicode:
              return self._print_Atom(e)
          else:
              inf_list = ['-oo', 'oo']
              return self._print_seq(inf_list, '(', ')')
  
      def _print_subfactorial(self, e):
          x = e.args[0]
          pform = self._print(x)
          # Add parentheses if needed
          if not ((x.is_Integer and x.is_nonnegative) or x.is_Symbol):
              pform = prettyForm(*pform.parens())
          pform = prettyForm(*pform.left('!'))
          return pform
  
      def _print_factorial(self, e):
          x = e.args[0]
          pform = self._print(x)
          # Add parentheses if needed
          if not ((x.is_Integer and x.is_nonnegative) or x.is_Symbol):
              pform = prettyForm(*pform.parens())
          pform = prettyForm(*pform.right('!'))
          return pform
  
      def _print_factorial2(self, e):
          x = e.args[0]
          pform = self._print(x)
          # Add parentheses if needed
          if not ((x.is_Integer and x.is_nonnegative) or x.is_Symbol):
              pform = prettyForm(*pform.parens())
          pform = prettyForm(*pform.right('!!'))
          return pform
  
      def _print_binomial(self, e):
          n, k = e.args
  
          n_pform = self._print(n)
          k_pform = self._print(k)
  
          bar = ' '*max(n_pform.width(), k_pform.width())
  
          pform = prettyForm(*k_pform.above(bar))
          pform = prettyForm(*pform.above(n_pform))
          pform = prettyForm(*pform.parens('(', ')'))
  
          pform.baseline = (pform.baseline + 1)//2
  
          return pform
  
      def _print_Relational(self, e):
          op = prettyForm(' ' + xsym(e.rel_op) + ' ')
  
          l = self._print(e.lhs)
          r = self._print(e.rhs)
          pform = prettyForm(*stringPict.next(l, op, r))
          return pform
  
      def _print_Not(self, e):
          from sympy import Equivalent, Implies
          if self._use_unicode:
              arg = e.args[0]
              pform = self._print(arg)
              if isinstance(arg, Equivalent):
                  return self._print_Equivalent(arg, altchar="\N{LEFT RIGHT DOUBLE ARROW WITH STROKE}")
              if isinstance(arg, Implies):
                  return self._print_Implies(arg, altchar="\N{RIGHTWARDS ARROW WITH STROKE}")
  
              if arg.is_Boolean and not arg.is_Not:
                  pform = prettyForm(*pform.parens())
  
              return prettyForm(*pform.left("\N{NOT SIGN}"))
          else:
              return self._print_Function(e)
  
      def __print_Boolean(self, e, char, sort=True):
          args = e.args
          if sort:
              args = sorted(e.args, key=default_sort_key)
          arg = args[0]
          pform = self._print(arg)
  
          if arg.is_Boolean and not arg.is_Not:
              pform = prettyForm(*pform.parens())
  
          for arg in args[1:]:
              pform_arg = self._print(arg)
  
              if arg.is_Boolean and not arg.is_Not:
                  pform_arg = prettyForm(*pform_arg.parens())
  
              pform = prettyForm(*pform.right(' %s ' % char))
              pform = prettyForm(*pform.right(pform_arg))
  
          return pform
  
      def _print_And(self, e):
          if self._use_unicode:
              return self.__print_Boolean(e, "\N{LOGICAL AND}")
          else:
              return self._print_Function(e, sort=True)
  
      def _print_Or(self, e):
          if self._use_unicode:
              return self.__print_Boolean(e, "\N{LOGICAL OR}")
          else:
              return self._print_Function(e, sort=True)
  
      def _print_Xor(self, e):
          if self._use_unicode:
              return self.__print_Boolean(e, "\N{XOR}")
          else:
              return self._print_Function(e, sort=True)
  
      def _print_Nand(self, e):
          if self._use_unicode:
              return self.__print_Boolean(e, "\N{NAND}")
          else:
              return self._print_Function(e, sort=True)
  
      def _print_Nor(self, e):
          if self._use_unicode:
              return self.__print_Boolean(e, "\N{NOR}")
          else:
              return self._print_Function(e, sort=True)
  
      def _print_Implies(self, e, altchar=None):
          if self._use_unicode:
              return self.__print_Boolean(e, altchar or "\N{RIGHTWARDS ARROW}", sort=False)
          else:
              return self._print_Function(e)
  
      def _print_Equivalent(self, e, altchar=None):
          if self._use_unicode:
              return self.__print_Boolean(e, altchar or "\N{LEFT RIGHT DOUBLE ARROW}")
          else:
              return self._print_Function(e, sort=True)
  
      def _print_conjugate(self, e):
          pform = self._print(e.args[0])
          return prettyForm( *pform.above( hobj('_', pform.width())) )
  
      def _print_Abs(self, e):
          pform = self._print(e.args[0])
          pform = prettyForm(*pform.parens('|', '|'))
          return pform
      _print_Determinant = _print_Abs
  
      def _print_floor(self, e):
          if self._use_unicode:
              pform = self._print(e.args[0])
              pform = prettyForm(*pform.parens('lfloor', 'rfloor'))
              return pform
          else:
              return self._print_Function(e)
  
      def _print_ceiling(self, e):
          if self._use_unicode:
              pform = self._print(e.args[0])
              pform = prettyForm(*pform.parens('lceil', 'rceil'))
              return pform
          else:
              return self._print_Function(e)
  
      def _print_Derivative(self, deriv):
          if requires_partial(deriv.expr) and self._use_unicode:
              deriv_symbol = U('PARTIAL DIFFERENTIAL')
          else:
              deriv_symbol = r'd'
          x = None
          count_total_deriv = 0
  
          for sym, num in reversed(deriv.variable_count):
              s = self._print(sym)
              ds = prettyForm(*s.left(deriv_symbol))
              count_total_deriv += num
  
              if (not num.is_Integer) or (num > 1):
                  ds = ds**prettyForm(str(num))
  
              if x is None:
                  x = ds
              else:
                  x = prettyForm(*x.right(' '))
                  x = prettyForm(*x.right(ds))
  
          f = prettyForm(
              binding=prettyForm.FUNC, *self._print(deriv.expr).parens())
  
          pform = prettyForm(deriv_symbol)
  
          if (count_total_deriv > 1) != False:
              pform = pform**prettyForm(str(count_total_deriv))
  
          pform = prettyForm(*pform.below(stringPict.LINE, x))
          pform.baseline = pform.baseline + 1
          pform = prettyForm(*stringPict.next(pform, f))
          pform.binding = prettyForm.MUL
  
          return pform
  
      def _print_Cycle(self, dc):
          from sympy.combinatorics.permutations import Permutation, Cycle
          # for Empty Cycle
          if dc == Cycle():
              cyc = stringPict('')
              return prettyForm(*cyc.parens())
  
          dc_list = Permutation(dc.list()).cyclic_form
          # for Identity Cycle
          if dc_list == []:
              cyc = self._print(dc.size - 1)
              return prettyForm(*cyc.parens())
  
          cyc = stringPict('')
          for i in dc_list:
              l = self._print(str(tuple(i)).replace(',', ''))
              cyc = prettyForm(*cyc.right(l))
          return cyc
  
      def _print_Permutation(self, expr):
          from sympy.combinatorics.permutations import Permutation, Cycle
  
          perm_cyclic = Permutation.print_cyclic
          if perm_cyclic is not None:
              SymPyDeprecationWarning(
                  feature="Permutation.print_cyclic = {}".format(perm_cyclic),
                  useinstead="init_printing(perm_cyclic={})"
                  .format(perm_cyclic),
                  issue=15201,
                  deprecated_since_version="1.6").warn()
          else:
              perm_cyclic = self._settings.get("perm_cyclic", True)
  
          if perm_cyclic:
              return self._print_Cycle(Cycle(expr))
  
          lower = expr.array_form
          upper = list(range(len(lower)))
  
          result = stringPict('')
          first = True
          for u, l in zip(upper, lower):
              s1 = self._print(u)
              s2 = self._print(l)
              col = prettyForm(*s1.below(s2))
              if first:
                  first = False
              else:
                  col = prettyForm(*col.left(" "))
              result = prettyForm(*result.right(col))
          return prettyForm(*result.parens())
  
  
      def _print_Integral(self, integral):
          f = integral.function
  
          # Add parentheses if arg involves addition of terms and
          # create a pretty form for the argument
          prettyF = self._print(f)
          # XXX generalize parens
          if f.is_Add:
              prettyF = prettyForm(*prettyF.parens())
  
          # dx dy dz ...
          arg = prettyF
          for x in integral.limits:
              prettyArg = self._print(x[0])
              # XXX qparens (parens if needs-parens)
              if prettyArg.width() > 1:
                  prettyArg = prettyForm(*prettyArg.parens())
  
              arg = prettyForm(*arg.right(' d', prettyArg))
  
          # \int \int \int ...
          firstterm = True
          s = None
          for lim in integral.limits:
              # Create bar based on the height of the argument
              h = arg.height()
              H = h + 2
  
              # XXX hack!
              ascii_mode = not self._use_unicode
              if ascii_mode:
                  H += 2
  
              vint = vobj('int', H)
  
              # Construct the pretty form with the integral sign and the argument
              pform = prettyForm(vint)
              pform.baseline = arg.baseline + (
                  H - h)//2    # covering the whole argument
  
              if len(lim) > 1:
                  # Create pretty forms for endpoints, if definite integral.
                  # Do not print empty endpoints.
                  if len(lim) == 2:
                      prettyA = prettyForm("")
                      prettyB = self._print(lim[1])
                  if len(lim) == 3:
                      prettyA = self._print(lim[1])
                      prettyB = self._print(lim[2])
  
                  if ascii_mode:  # XXX hack
                      # Add spacing so that endpoint can more easily be
                      # identified with the correct integral sign
                      spc = max(1, 3 - prettyB.width())
                      prettyB = prettyForm(*prettyB.left(' ' * spc))
  
                      spc = max(1, 4 - prettyA.width())
                      prettyA = prettyForm(*prettyA.right(' ' * spc))
  
                  pform = prettyForm(*pform.above(prettyB))
                  pform = prettyForm(*pform.below(prettyA))
  
              if not ascii_mode:  # XXX hack
                  pform = prettyForm(*pform.right(' '))
  
              if firstterm:
                  s = pform   # first term
                  firstterm = False
              else:
                  s = prettyForm(*s.left(pform))
  
          pform = prettyForm(*arg.left(s))
          pform.binding = prettyForm.MUL
          return pform
  
      def _print_Product(self, expr):
          func = expr.term
          pretty_func = self._print(func)
  
          horizontal_chr = xobj('_', 1)
          corner_chr = xobj('_', 1)
          vertical_chr = xobj('|', 1)
  
          if self._use_unicode:
              # use unicode corners
              horizontal_chr = xobj('-', 1)
              corner_chr = '\N{BOX DRAWINGS LIGHT DOWN AND HORIZONTAL}'
  
          func_height = pretty_func.height()
  
          first = True
          max_upper = 0
          sign_height = 0
  
          for lim in expr.limits:
              pretty_lower, pretty_upper = self.__print_SumProduct_Limits(lim)
  
              width = (func_height + 2) * 5 // 3 - 2
              sign_lines = [horizontal_chr + corner_chr + (horizontal_chr * (width-2)) + corner_chr + horizontal_chr]
              for _ in range(func_height + 1):
                  sign_lines.append(' ' + vertical_chr + (' ' * (width-2)) + vertical_chr + ' ')
  
              pretty_sign = stringPict('')
              pretty_sign = prettyForm(*pretty_sign.stack(*sign_lines))
  
  
              max_upper = max(max_upper, pretty_upper.height())
  
              if first:
                  sign_height = pretty_sign.height()
  
              pretty_sign = prettyForm(*pretty_sign.above(pretty_upper))
              pretty_sign = prettyForm(*pretty_sign.below(pretty_lower))
  
              if first:
                  pretty_func.baseline = 0
                  first = False
  
              height = pretty_sign.height()
              padding = stringPict('')
              padding = prettyForm(*padding.stack(*[' ']*(height - 1)))
              pretty_sign = prettyForm(*pretty_sign.right(padding))
  
              pretty_func = prettyForm(*pretty_sign.right(pretty_func))
  
          pretty_func.baseline = max_upper + sign_height//2
          pretty_func.binding = prettyForm.MUL
          return pretty_func
  
      def __print_SumProduct_Limits(self, lim):
          def print_start(lhs, rhs):
              op = prettyForm(' ' + xsym("==") + ' ')
              l = self._print(lhs)
              r = self._print(rhs)
              pform = prettyForm(*stringPict.next(l, op, r))
              return pform
  
          prettyUpper = self._print(lim[2])
          prettyLower = print_start(lim[0], lim[1])
          return prettyLower, prettyUpper
  
      def _print_Sum(self, expr):
          ascii_mode = not self._use_unicode
  
          def asum(hrequired, lower, upper, use_ascii):
              def adjust(s, wid=None, how='<^>'):
                  if not wid or len(s) > wid:
                      return s
                  need = wid - len(s)
                  if how == '<^>' or how == "<" or how not in list('<^>'):
                      return s + ' '*need
                  half = need//2
                  lead = ' '*half
                  if how == ">":
                      return " "*need + s
                  return lead + s + ' '*(need - len(lead))
  
              h = max(hrequired, 2)
              d = h//2
              w = d + 1
              more = hrequired % 2
  
              lines = []
              if use_ascii:
                  lines.append("_"*(w) + ' ')
                  lines.append(r"\%s`" % (' '*(w - 1)))
                  for i in range(1, d):
                      lines.append('%s\\%s' % (' '*i, ' '*(w - i)))
                  if more:
                      lines.append('%s)%s' % (' '*(d), ' '*(w - d)))
                  for i in reversed(range(1, d)):
                      lines.append('%s/%s' % (' '*i, ' '*(w - i)))
                  lines.append("/" + "_"*(w - 1) + ',')
                  return d, h + more, lines, more
              else:
                  w = w + more
                  d = d + more
                  vsum = vobj('sum', 4)
                  lines.append("_"*(w))
                  for i in range(0, d):
                      lines.append('%s%s%s' % (' '*i, vsum[2], ' '*(w - i - 1)))
                  for i in reversed(range(0, d)):
                      lines.append('%s%s%s' % (' '*i, vsum[4], ' '*(w - i - 1)))
                  lines.append(vsum[8]*(w))
                  return d, h + 2*more, lines, more
  
          f = expr.function
  
          prettyF = self._print(f)
  
          if f.is_Add:  # add parens
              prettyF = prettyForm(*prettyF.parens())
  
          H = prettyF.height() + 2
  
          # \sum \sum \sum ...
          first = True
          max_upper = 0
          sign_height = 0
  
          for lim in expr.limits:
              prettyLower, prettyUpper = self.__print_SumProduct_Limits(lim)
  
              max_upper = max(max_upper, prettyUpper.height())
  
              # Create sum sign based on the height of the argument
              d, h, slines, adjustment = asum(
                  H, prettyLower.width(), prettyUpper.width(), ascii_mode)
              prettySign = stringPict('')
              prettySign = prettyForm(*prettySign.stack(*slines))
  
              if first:
                  sign_height = prettySign.height()
  
              prettySign = prettyForm(*prettySign.above(prettyUpper))
              prettySign = prettyForm(*prettySign.below(prettyLower))
  
              if first:
                  # change F baseline so it centers on the sign
                  prettyF.baseline -= d - (prettyF.height()//2 -
                                           prettyF.baseline)
                  first = False
  
              # put padding to the right
              pad = stringPict('')
              pad = prettyForm(*pad.stack(*[' ']*h))
              prettySign = prettyForm(*prettySign.right(pad))
              # put the present prettyF to the right
              prettyF = prettyForm(*prettySign.right(prettyF))
  
          # adjust baseline of ascii mode sigma with an odd height so that it is
          # exactly through the center
          ascii_adjustment = ascii_mode if not adjustment else 0
          prettyF.baseline = max_upper + sign_height//2 + ascii_adjustment
  
          prettyF.binding = prettyForm.MUL
          return prettyF
  
      def _print_Limit(self, l):
          e, z, z0, dir = l.args
  
          E = self._print(e)
          if precedence(e) <= PRECEDENCE["Mul"]:
              E = prettyForm(*E.parens('(', ')'))
          Lim = prettyForm('lim')
  
          LimArg = self._print(z)
          if self._use_unicode:
              LimArg = prettyForm(*LimArg.right('\N{BOX DRAWINGS LIGHT HORIZONTAL}\N{RIGHTWARDS ARROW}'))
          else:
              LimArg = prettyForm(*LimArg.right('->'))
          LimArg = prettyForm(*LimArg.right(self._print(z0)))
  
          if str(dir) == '+-' or z0 in (S.Infinity, S.NegativeInfinity):
              dir = ""
          else:
              if self._use_unicode:
                  dir = '\N{SUPERSCRIPT PLUS SIGN}' if str(dir) == "+" else '\N{SUPERSCRIPT MINUS}'
  
          LimArg = prettyForm(*LimArg.right(self._print(dir)))
  
          Lim = prettyForm(*Lim.below(LimArg))
          Lim = prettyForm(*Lim.right(E), binding=prettyForm.MUL)
  
          return Lim
  
      def _print_matrix_contents(self, e):
          """
          This method factors out what is essentially grid printing.
          """
          M = e   # matrix
          Ms = {}  # i,j -> pretty(M[i,j])
          for i in range(M.rows):
              for j in range(M.cols):
                  Ms[i, j] = self._print(M[i, j])
  
          # h- and v- spacers
          hsep = 2
          vsep = 1
  
          # max width for columns
          maxw = [-1] * M.cols
  
          for j in range(M.cols):
              maxw[j] = max([Ms[i, j].width() for i in range(M.rows)] or [0])
  
          # drawing result
          D = None
  
          for i in range(M.rows):
  
              D_row = None
              for j in range(M.cols):
                  s = Ms[i, j]
  
                  # reshape s to maxw
                  # XXX this should be generalized, and go to stringPict.reshape ?
                  assert s.width() <= maxw[j]
  
                  # hcenter it, +0.5 to the right                        2
                  # ( it's better to align formula starts for say 0 and r )
                  # XXX this is not good in all cases -- maybe introduce vbaseline?
                  wdelta = maxw[j] - s.width()
                  wleft = wdelta // 2
                  wright = wdelta - wleft
  
                  s = prettyForm(*s.right(' '*wright))
                  s = prettyForm(*s.left(' '*wleft))
  
                  # we don't need vcenter cells -- this is automatically done in
                  # a pretty way because when their baselines are taking into
                  # account in .right()
  
                  if D_row is None:
                      D_row = s   # first box in a row
                      continue
  
                  D_row = prettyForm(*D_row.right(' '*hsep))  # h-spacer
                  D_row = prettyForm(*D_row.right(s))
  
              if D is None:
                  D = D_row       # first row in a picture
                  continue
  
              # v-spacer
              for _ in range(vsep):
                  D = prettyForm(*D.below(' '))
  
              D = prettyForm(*D.below(D_row))
  
          if D is None:
              D = prettyForm('')  # Empty Matrix
  
          return D
  
      def _print_MatrixBase(self, e):
          D = self._print_matrix_contents(e)
          D.baseline = D.height()//2
          D = prettyForm(*D.parens('[', ']'))
          return D
  
      def _print_TensorProduct(self, expr):
          # This should somehow share the code with _print_WedgeProduct:
          circled_times = "\u2297"
          return self._print_seq(expr.args, None, None, circled_times,
              parenthesize=lambda x: precedence_traditional(x) <= PRECEDENCE["Mul"])
  
      def _print_WedgeProduct(self, expr):
          # This should somehow share the code with _print_TensorProduct:
          wedge_symbol = "\u2227"
          return self._print_seq(expr.args, None, None, wedge_symbol,
              parenthesize=lambda x: precedence_traditional(x) <= PRECEDENCE["Mul"])
  
      def _print_Trace(self, e):
          D = self._print(e.arg)
          D = prettyForm(*D.parens('(',')'))
          D.baseline = D.height()//2
          D = prettyForm(*D.left('\n'*(0) + 'tr'))
          return D
  
  
      def _print_MatrixElement(self, expr):
          from sympy.matrices import MatrixSymbol
          from sympy import Symbol
          if (isinstance(expr.parent, MatrixSymbol)
                  and expr.i.is_number and expr.j.is_number):
              return self._print(
                      Symbol(expr.parent.name + '_%d%d' % (expr.i, expr.j)))
          else:
              prettyFunc = self._print(expr.parent)
              prettyFunc = prettyForm(*prettyFunc.parens())
              prettyIndices = self._print_seq((expr.i, expr.j), delimiter=', '
                      ).parens(left='[', right=']')[0]
              pform = prettyForm(binding=prettyForm.FUNC,
                      *stringPict.next(prettyFunc, prettyIndices))
  
              # store pform parts so it can be reassembled e.g. when powered
              pform.prettyFunc = prettyFunc
              pform.prettyArgs = prettyIndices
  
              return pform
  
  
      def _print_MatrixSlice(self, m):
          # XXX works only for applied functions
          from sympy.matrices import MatrixSymbol
          prettyFunc = self._print(m.parent)
          if not isinstance(m.parent, MatrixSymbol):
              prettyFunc = prettyForm(*prettyFunc.parens())
          def ppslice(x, dim):
              x = list(x)
              if x[2] == 1:
                  del x[2]
              if x[0] == 0:
                  x[0] = ''
              if x[1] == dim:
                  x[1] = ''
              return prettyForm(*self._print_seq(x, delimiter=':'))
          prettyArgs = self._print_seq((ppslice(m.rowslice, m.parent.rows),
              ppslice(m.colslice, m.parent.cols)), delimiter=', ').parens(left='[', right=']')[0]
  
          pform = prettyForm(
              binding=prettyForm.FUNC, *stringPict.next(prettyFunc, prettyArgs))
  
          # store pform parts so it can be reassembled e.g. when powered
          pform.prettyFunc = prettyFunc
          pform.prettyArgs = prettyArgs
  
          return pform
  
      def _print_Transpose(self, expr):
          pform = self._print(expr.arg)
          from sympy.matrices import MatrixSymbol
          if not isinstance(expr.arg, MatrixSymbol):
              pform = prettyForm(*pform.parens())
          pform = pform**(prettyForm('T'))
          return pform
  
      def _print_Adjoint(self, expr):
          pform = self._print(expr.arg)
          if self._use_unicode:
              dag = prettyForm('\N{DAGGER}')
          else:
              dag = prettyForm('+')
          from sympy.matrices import MatrixSymbol
          if not isinstance(expr.arg, MatrixSymbol):
              pform = prettyForm(*pform.parens())
          pform = pform**dag
          return pform
  
      def _print_BlockMatrix(self, B):
          if B.blocks.shape == (1, 1):
              return self._print(B.blocks[0, 0])
          return self._print(B.blocks)
  
      def _print_MatAdd(self, expr):
          s = None
          for item in expr.args:
              pform = self._print(item)
              if s is None:
                  s = pform     # First element
              else:
                  coeff = item.as_coeff_mmul()[0]
                  if _coeff_isneg(S(coeff)):
                      s = prettyForm(*stringPict.next(s, ' '))
                      pform = self._print(item)
                  else:
                      s = prettyForm(*stringPict.next(s, ' + '))
                  s = prettyForm(*stringPict.next(s, pform))
  
          return s
  
      def _print_MatMul(self, expr):
          args = list(expr.args)
          from sympy import Add, MatAdd, HadamardProduct, KroneckerProduct
          for i, a in enumerate(args):
              if (isinstance(a, (Add, MatAdd, HadamardProduct, KroneckerProduct))
                      and len(expr.args) > 1):
                  args[i] = prettyForm(*self._print(a).parens())
              else:
                  args[i] = self._print(a)
  
          return prettyForm.__mul__(*args)
  
      def _print_Identity(self, expr):
          if self._use_unicode:
              return prettyForm('\N{MATHEMATICAL DOUBLE-STRUCK CAPITAL I}')
          else:
              return prettyForm('I')
  
      def _print_ZeroMatrix(self, expr):
          if self._use_unicode:
              return prettyForm('\N{MATHEMATICAL DOUBLE-STRUCK DIGIT ZERO}')
          else:
              return prettyForm('0')
  
      def _print_OneMatrix(self, expr):
          if self._use_unicode:
              return prettyForm('\N{MATHEMATICAL DOUBLE-STRUCK DIGIT ONE}')
          else:
              return prettyForm('1')
  
      def _print_DotProduct(self, expr):
          args = list(expr.args)
  
          for i, a in enumerate(args):
              args[i] = self._print(a)
          return prettyForm.__mul__(*args)
  
      def _print_MatPow(self, expr):
          pform = self._print(expr.base)
          from sympy.matrices import MatrixSymbol
          if not isinstance(expr.base, MatrixSymbol):
              pform = prettyForm(*pform.parens())
          pform = pform**(self._print(expr.exp))
          return pform
  
      def _print_HadamardProduct(self, expr):
          from sympy import MatAdd, MatMul, HadamardProduct
          if self._use_unicode:
              delim = pretty_atom('Ring')
          else:
              delim = '.*'
          return self._print_seq(expr.args, None, None, delim,
                  parenthesize=lambda x: isinstance(x, (MatAdd, MatMul, HadamardProduct)))
  
      def _print_HadamardPower(self, expr):
          # from sympy import MatAdd, MatMul
          if self._use_unicode:
              circ = pretty_atom('Ring')
          else:
              circ = self._print('.')
          pretty_base = self._print(expr.base)
          pretty_exp = self._print(expr.exp)
          if precedence(expr.exp) < PRECEDENCE["Mul"]:
              pretty_exp = prettyForm(*pretty_exp.parens())
          pretty_circ_exp = prettyForm(
              binding=prettyForm.LINE,
              *stringPict.next(circ, pretty_exp)
          )
          return pretty_base**pretty_circ_exp
  
      def _print_KroneckerProduct(self, expr):
          from sympy import MatAdd, MatMul
          if self._use_unicode:
              delim = ' \N{N-ARY CIRCLED TIMES OPERATOR} '
          else:
              delim = ' x '
          return self._print_seq(expr.args, None, None, delim,
                  parenthesize=lambda x: isinstance(x, (MatAdd, MatMul)))
  
      def _print_FunctionMatrix(self, X):
          D = self._print(X.lamda.expr)
          D = prettyForm(*D.parens('[', ']'))
          return D
  
      def _print_TransferFunction(self, expr):
          if not expr.num == 1:
              num, den = expr.num, expr.den
              res = Mul(num, Pow(den, -1, evaluate=False), evaluate=False)
              return self._print_Mul(res)
          else:
              return self._print(1)/self._print(expr.den)
  
      def _print_Series(self, expr):
          args = list(expr.args)
          for i, a in enumerate(expr.args):
              args[i] = prettyForm(*self._print(a).parens())
          return prettyForm.__mul__(*args)
  
      def _print_MIMOSeries(self, expr):
          from sympy.physics.control.lti import MIMOParallel
          args = list(expr.args)
          pretty_args = []
          for i, a in enumerate(reversed(args)):
              if (isinstance(a, MIMOParallel) and len(expr.args) > 1):
                  expression = self._print(a)
                  expression.baseline = expression.height()//2
                  pretty_args.append(prettyForm(*expression.parens()))
              else:
                  expression = self._print(a)
                  expression.baseline = expression.height()//2
                  pretty_args.append(expression)
          return prettyForm.__mul__(*pretty_args)
  
      def _print_Parallel(self, expr):
          s = None
          for item in expr.args:
              pform = self._print(item)
              if s is None:
                  s = pform     # First element
              else:
                  s = prettyForm(*stringPict.next(s))
                  s.baseline = s.height()//2
                  s = prettyForm(*stringPict.next(s, ' + '))
                  s = prettyForm(*stringPict.next(s, pform))
          return s
  
      def _print_MIMOParallel(self, expr):
          from sympy.physics.control.lti import TransferFunctionMatrix
          s = None
          for item in expr.args:
              pform = self._print(item)
              if s is None:
                  s = pform     # First element
              else:
                  s = prettyForm(*stringPict.next(s))
                  s.baseline = s.height()//2
                  s = prettyForm(*stringPict.next(s, ' + '))
                  if isinstance(item, TransferFunctionMatrix):
                      s.baseline = s.height() - 1
                  s = prettyForm(*stringPict.next(s, pform))
              # s.baseline = s.height()//2
          return s
  
      def _print_Feedback(self, expr):
          from sympy.physics.control import TransferFunction, Parallel, Series
  
          num, tf = expr.num, TransferFunction(1, 1, expr.num.var)
          num_arg_list = list(num.args) if isinstance(num, Series) else [num]
          den_arg_list = list(expr.den.args) if isinstance(expr.den, Series) else [expr.den]
  
          if isinstance(num, Series) and isinstance(expr.den, Series):
              den = Parallel(tf, Series(*num_arg_list, *den_arg_list))
          elif isinstance(num, Series) and isinstance(expr.den, TransferFunction):
              if expr.den == tf:
                  den = Parallel(tf, Series(*num_arg_list))
              else:
                  den = Parallel(tf, Series(*num_arg_list, expr.den))
          elif isinstance(num, TransferFunction) and isinstance(expr.den, Series):
              if num == tf:
                  den = Parallel(tf, Series(*den_arg_list))
              else:
                  den = Parallel(tf, Series(num, *den_arg_list))
          else:
              if num == tf:
                  den = Parallel(tf, *den_arg_list)
              elif expr.den == tf:
                  den = Parallel(tf, *num_arg_list)
              else:
                  den = Parallel(tf, Series(*num_arg_list, *den_arg_list))
  
          return self._print(num)/self._print(den)
  
      def _print_TransferFunctionMatrix(self, expr):
          mat = self._print(expr._expr_mat)
          mat.baseline = mat.height() - 1
          subscript = greek_unicode['tau'] if self._use_unicode else r'{t}'
          mat = prettyForm(*mat.right(subscript))
          return mat
  
      def _print_BasisDependent(self, expr):
          from sympy.vector import Vector
  
          if not self._use_unicode:
              raise NotImplementedError("ASCII pretty printing of BasisDependent is not implemented")
  
          if expr == expr.zero:
              return prettyForm(expr.zero._pretty_form)
          o1 = []
          vectstrs = []
          if isinstance(expr, Vector):
              items = expr.separate().items()
          else:
              items = [(0, expr)]
          for system, vect in items:
              inneritems = list(vect.components.items())
              inneritems.sort(key = lambda x: x[0].__str__())
              for k, v in inneritems:
                  #if the coef of the basis vector is 1
                  #we skip the 1
                  if v == 1:
                      o1.append("" +
                                k._pretty_form)
                  #Same for -1
                  elif v == -1:
                      o1.append("(-1) " +
                                k._pretty_form)
                  #For a general expr
                  else:
                      #We always wrap the measure numbers in
                      #parentheses
                      arg_str = self._print(
                          v).parens()[0]
  
                      o1.append(arg_str + ' ' + k._pretty_form)
                  vectstrs.append(k._pretty_form)
  
          #outstr = u("").join(o1)
          if o1[0].startswith(" + "):
              o1[0] = o1[0][3:]
          elif o1[0].startswith(" "):
              o1[0] = o1[0][1:]
          #Fixing the newlines
          lengths = []
          strs = ['']
          flag = []
          for i, partstr in enumerate(o1):
              flag.append(0)
              # XXX: What is this hack?
              if '\n' in partstr:
                  tempstr = partstr
                  tempstr = tempstr.replace(vectstrs[i], '')
                  if '\N{right parenthesis extension}' in tempstr:   # If scalar is a fraction
                      for paren in range(len(tempstr)):
                          flag[i] = 1
                          if tempstr[paren] == '\N{right parenthesis extension}':
                              tempstr = tempstr[:paren] + '\N{right parenthesis extension}'\
                                           + ' '  + vectstrs[i] + tempstr[paren + 1:]
                              break
                  elif '\N{RIGHT PARENTHESIS LOWER HOOK}' in tempstr:
                      flag[i] = 1
                      tempstr = tempstr.replace('\N{RIGHT PARENTHESIS LOWER HOOK}',
                                          '\N{RIGHT PARENTHESIS LOWER HOOK}'
                                          + ' ' + vectstrs[i])
                  else:
                      tempstr = tempstr.replace('\N{RIGHT PARENTHESIS UPPER HOOK}',
                                          '\N{RIGHT PARENTHESIS UPPER HOOK}'
                                          + ' ' + vectstrs[i])
                  o1[i] = tempstr
  
          o1 = [x.split('\n') for x in o1]
          n_newlines = max([len(x) for x in o1])  # Width of part in its pretty form
  
          if 1 in flag:                           # If there was a fractional scalar
              for i, parts in enumerate(o1):
                  if len(parts) == 1:             # If part has no newline
                      parts.insert(0, ' ' * (len(parts[0])))
                      flag[i] = 1
  
          for i, parts in enumerate(o1):
              lengths.append(len(parts[flag[i]]))
              for j in range(n_newlines):
                  if j+1 <= len(parts):
                      if j >= len(strs):
                          strs.append(' ' * (sum(lengths[:-1]) +
                                             3*(len(lengths)-1)))
                      if j == flag[i]:
                          strs[flag[i]] += parts[flag[i]] + ' + '
                      else:
                          strs[j] += parts[j] + ' '*(lengths[-1] -
                                                     len(parts[j])+
                                                     3)
                  else:
                      if j >= len(strs):
                          strs.append(' ' * (sum(lengths[:-1]) +
                                             3*(len(lengths)-1)))
                      strs[j] += ' '*(lengths[-1]+3)
  
          return prettyForm('\n'.join([s[:-3] for s in strs]))
  
      def _print_NDimArray(self, expr):
          from sympy import ImmutableMatrix
  
          if expr.rank() == 0:
              return self._print(expr[()])
  
          level_str = [[]] + [[] for i in range(expr.rank())]
          shape_ranges = [list(range(i)) for i in expr.shape]
          # leave eventual matrix elements unflattened
          mat = lambda x: ImmutableMatrix(x, evaluate=False)
          for outer_i in itertools.product(*shape_ranges):
              level_str[-1].append(expr[outer_i])
              even = True
              for back_outer_i in range(expr.rank()-1, -1, -1):
                  if len(level_str[back_outer_i+1]) < expr.shape[back_outer_i]:
                      break
                  if even:
                      level_str[back_outer_i].append(level_str[back_outer_i+1])
                  else:
                      level_str[back_outer_i].append(mat(
                          level_str[back_outer_i+1]))
                      if len(level_str[back_outer_i + 1]) == 1:
                          level_str[back_outer_i][-1] = mat(
                              [[level_str[back_outer_i][-1]]])
                  even = not even
                  level_str[back_outer_i+1] = []
  
          out_expr = level_str[0][0]
          if expr.rank() % 2 == 1:
              out_expr = mat([out_expr])
  
          return self._print(out_expr)
  
      def _printer_tensor_indices(self, name, indices, index_map={}):
          center = stringPict(name)
          top = stringPict(" "*center.width())
          bot = stringPict(" "*center.width())
  
          last_valence = None
          prev_map = None
  
          for i, index in enumerate(indices):
              indpic = self._print(index.args[0])
              if ((index in index_map) or prev_map) and last_valence == index.is_up:
                  if index.is_up:
                      top = prettyForm(*stringPict.next(top, ","))
                  else:
                      bot = prettyForm(*stringPict.next(bot, ","))
              if index in index_map:
                  indpic = prettyForm(*stringPict.next(indpic, "="))
                  indpic = prettyForm(*stringPict.next(indpic, self._print(index_map[index])))
                  prev_map = True
              else:
                  prev_map = False
              if index.is_up:
                  top = stringPict(*top.right(indpic))
                  center = stringPict(*center.right(" "*indpic.width()))
                  bot = stringPict(*bot.right(" "*indpic.width()))
              else:
                  bot = stringPict(*bot.right(indpic))
                  center = stringPict(*center.right(" "*indpic.width()))
                  top = stringPict(*top.right(" "*indpic.width()))
              last_valence = index.is_up
  
          pict = prettyForm(*center.above(top))
          pict = prettyForm(*pict.below(bot))
          return pict
  
      def _print_Tensor(self, expr):
          name = expr.args[0].name
          indices = expr.get_indices()
          return self._printer_tensor_indices(name, indices)
  
      def _print_TensorElement(self, expr):
          name = expr.expr.args[0].name
          indices = expr.expr.get_indices()
          index_map = expr.index_map
          return self._printer_tensor_indices(name, indices, index_map)
  
      def _print_TensMul(self, expr):
          sign, args = expr._get_args_for_traditional_printer()
          args = [
              prettyForm(*self._print(i).parens()) if
              precedence_traditional(i) < PRECEDENCE["Mul"] else self._print(i)
              for i in args
          ]
          pform = prettyForm.__mul__(*args)
          if sign:
              return prettyForm(*pform.left(sign))
          else:
              return pform
  
      def _print_TensAdd(self, expr):
          args = [
              prettyForm(*self._print(i).parens()) if
              precedence_traditional(i) < PRECEDENCE["Mul"] else self._print(i)
              for i in expr.args
          ]
          return prettyForm.__add__(*args)
  
      def _print_TensorIndex(self, expr):
          sym = expr.args[0]
          if not expr.is_up:
              sym = -sym
          return self._print(sym)
  
      def _print_PartialDerivative(self, deriv):
          if self._use_unicode:
              deriv_symbol = U('PARTIAL DIFFERENTIAL')
          else:
              deriv_symbol = r'd'
          x = None
  
          for variable in reversed(deriv.variables):
              s = self._print(variable)
              ds = prettyForm(*s.left(deriv_symbol))
  
              if x is None:
                  x = ds
              else:
                  x = prettyForm(*x.right(' '))
                  x = prettyForm(*x.right(ds))
  
          f = prettyForm(
              binding=prettyForm.FUNC, *self._print(deriv.expr).parens())
  
          pform = prettyForm(deriv_symbol)
  
          if len(deriv.variables) > 1:
              pform = pform**self._print(len(deriv.variables))
  
          pform = prettyForm(*pform.below(stringPict.LINE, x))
          pform.baseline = pform.baseline + 1
          pform = prettyForm(*stringPict.next(pform, f))
          pform.binding = prettyForm.MUL
  
          return pform
  
      def _print_Piecewise(self, pexpr):
  
          P = {}
          for n, ec in enumerate(pexpr.args):
              P[n, 0] = self._print(ec.expr)
              if ec.cond == True:
                  P[n, 1] = prettyForm('otherwise')
              else:
                  P[n, 1] = prettyForm(
                      *prettyForm('for ').right(self._print(ec.cond)))
          hsep = 2
          vsep = 1
          len_args = len(pexpr.args)
  
          # max widths
          maxw = [max([P[i, j].width() for i in range(len_args)])
                  for j in range(2)]
  
          # FIXME: Refactor this code and matrix into some tabular environment.
          # drawing result
          D = None
  
          for i in range(len_args):
              D_row = None
              for j in range(2):
                  p = P[i, j]
                  assert p.width() <= maxw[j]
  
                  wdelta = maxw[j] - p.width()
                  wleft = wdelta // 2
                  wright = wdelta - wleft
  
                  p = prettyForm(*p.right(' '*wright))
                  p = prettyForm(*p.left(' '*wleft))
  
                  if D_row is None:
                      D_row = p
                      continue
  
                  D_row = prettyForm(*D_row.right(' '*hsep))  # h-spacer
                  D_row = prettyForm(*D_row.right(p))
              if D is None:
                  D = D_row       # first row in a picture
                  continue
  
              # v-spacer
              for _ in range(vsep):
                  D = prettyForm(*D.below(' '))
  
              D = prettyForm(*D.below(D_row))
  
          D = prettyForm(*D.parens('{', ''))
          D.baseline = D.height()//2
          D.binding = prettyForm.OPEN
          return D
  
      def _print_ITE(self, ite):
          from sympy.functions.elementary.piecewise import Piecewise
          return self._print(ite.rewrite(Piecewise))
  
      def _hprint_vec(self, v):
          D = None
  
          for a in v:
              p = a
              if D is None:
                  D = p
              else:
                  D = prettyForm(*D.right(', '))
                  D = prettyForm(*D.right(p))
          if D is None:
              D = stringPict(' ')
  
          return D
  
      def _hprint_vseparator(self, p1, p2, left=None, right=None, delimiter='', ifascii_nougly=False):
          if ifascii_nougly and not self._use_unicode:
              return self._print_seq((p1, '|', p2), left=left, right=right,
                                     delimiter=delimiter, ifascii_nougly=True)
          tmp = self._print_seq((p1, p2,), left=left, right=right, delimiter=delimiter)
          sep = stringPict(vobj('|', tmp.height()), baseline=tmp.baseline)
          return self._print_seq((p1, sep, p2), left=left, right=right,
                                 delimiter=delimiter)
  
      def _print_hyper(self, e):
          # FIXME refactor Matrix, Piecewise, and this into a tabular environment
          ap = [self._print(a) for a in e.ap]
          bq = [self._print(b) for b in e.bq]
  
          P = self._print(e.argument)
          P.baseline = P.height()//2
  
          # Drawing result - first create the ap, bq vectors
          D = None
          for v in [ap, bq]:
              D_row = self._hprint_vec(v)
              if D is None:
                  D = D_row       # first row in a picture
              else:
                  D = prettyForm(*D.below(' '))
                  D = prettyForm(*D.below(D_row))
  
          # make sure that the argument `z' is centred vertically
          D.baseline = D.height()//2
  
          # insert horizontal separator
          P = prettyForm(*P.left(' '))
          D = prettyForm(*D.right(' '))
  
          # insert separating `|`
          D = self._hprint_vseparator(D, P)
  
          # add parens
          D = prettyForm(*D.parens('(', ')'))
  
          # create the F symbol
          above = D.height()//2 - 1
          below = D.height() - above - 1
  
          sz, t, b, add, img = annotated('F')
          F = prettyForm('\n' * (above - t) + img + '\n' * (below - b),
                         baseline=above + sz)
          add = (sz + 1)//2
  
          F = prettyForm(*F.left(self._print(len(e.ap))))
          F = prettyForm(*F.right(self._print(len(e.bq))))
          F.baseline = above + add
  
          D = prettyForm(*F.right(' ', D))
  
          return D
  
      def _print_meijerg(self, e):
          # FIXME refactor Matrix, Piecewise, and this into a tabular environment
  
          v = {}
          v[(0, 0)] = [self._print(a) for a in e.an]
          v[(0, 1)] = [self._print(a) for a in e.aother]
          v[(1, 0)] = [self._print(b) for b in e.bm]
          v[(1, 1)] = [self._print(b) for b in e.bother]
  
          P = self._print(e.argument)
          P.baseline = P.height()//2
  
          vp = {}
          for idx in v:
              vp[idx] = self._hprint_vec(v[idx])
  
          for i in range(2):
              maxw = max(vp[(0, i)].width(), vp[(1, i)].width())
              for j in range(2):
                  s = vp[(j, i)]
                  left = (maxw - s.width()) // 2
                  right = maxw - left - s.width()
                  s = prettyForm(*s.left(' ' * left))
                  s = prettyForm(*s.right(' ' * right))
                  vp[(j, i)] = s
  
          D1 = prettyForm(*vp[(0, 0)].right('  ', vp[(0, 1)]))
          D1 = prettyForm(*D1.below(' '))
          D2 = prettyForm(*vp[(1, 0)].right('  ', vp[(1, 1)]))
          D = prettyForm(*D1.below(D2))
  
          # make sure that the argument `z' is centred vertically
          D.baseline = D.height()//2
  
          # insert horizontal separator
          P = prettyForm(*P.left(' '))
          D = prettyForm(*D.right(' '))
  
          # insert separating `|`
          D = self._hprint_vseparator(D, P)
  
          # add parens
          D = prettyForm(*D.parens('(', ')'))
  
          # create the G symbol
          above = D.height()//2 - 1
          below = D.height() - above - 1
  
          sz, t, b, add, img = annotated('G')
          F = prettyForm('\n' * (above - t) + img + '\n' * (below - b),
                         baseline=above + sz)
  
          pp = self._print(len(e.ap))
          pq = self._print(len(e.bq))
          pm = self._print(len(e.bm))
          pn = self._print(len(e.an))
  
          def adjust(p1, p2):
              diff = p1.width() - p2.width()
              if diff == 0:
                  return p1, p2
              elif diff > 0:
                  return p1, prettyForm(*p2.left(' '*diff))
              else:
                  return prettyForm(*p1.left(' '*-diff)), p2
          pp, pm = adjust(pp, pm)
          pq, pn = adjust(pq, pn)
          pu = prettyForm(*pm.right(', ', pn))
          pl = prettyForm(*pp.right(', ', pq))
  
          ht = F.baseline - above - 2
          if ht > 0:
              pu = prettyForm(*pu.below('\n'*ht))
          p = prettyForm(*pu.below(pl))
  
          F.baseline = above
          F = prettyForm(*F.right(p))
  
          F.baseline = above + add
  
          D = prettyForm(*F.right(' ', D))
  
          return D
  
      def _print_ExpBase(self, e):
          # TODO should exp_polar be printed differently?
          #      what about exp_polar(0), exp_polar(1)?
          base = prettyForm(pretty_atom('Exp1', 'e'))
          return base ** self._print(e.args[0])
  
      def _print_Exp1(self, e):
          return prettyForm(pretty_atom('Exp1', 'e'))
  
      def _print_Function(self, e, sort=False, func_name=None):
          # optional argument func_name for supplying custom names
          # XXX works only for applied functions
          return self._helper_print_function(e.func, e.args, sort=sort, func_name=func_name)
  
      def _print_mathieuc(self, e):
          return self._print_Function(e, func_name='C')
  
      def _print_mathieus(self, e):
          return self._print_Function(e, func_name='S')
  
      def _print_mathieucprime(self, e):
          return self._print_Function(e, func_name="C'")
  
      def _print_mathieusprime(self, e):
          return self._print_Function(e, func_name="S'")
  
      def _helper_print_function(self, func, args, sort=False, func_name=None, delimiter=', ', elementwise=False):
          if sort:
              args = sorted(args, key=default_sort_key)
  
          if not func_name and hasattr(func, "__name__"):
              func_name = func.__name__
  
          if func_name:
              prettyFunc = self._print(Symbol(func_name))
          else:
              prettyFunc = prettyForm(*self._print(func).parens())
  
          if elementwise:
              if self._use_unicode:
                  circ = pretty_atom('Modifier Letter Low Ring')
              else:
                  circ = '.'
              circ = self._print(circ)
              prettyFunc = prettyForm(
                  binding=prettyForm.LINE,
                  *stringPict.next(prettyFunc, circ)
              )
  
          prettyArgs = prettyForm(*self._print_seq(args, delimiter=delimiter).parens())
  
          pform = prettyForm(
              binding=prettyForm.FUNC, *stringPict.next(prettyFunc, prettyArgs))
  
          # store pform parts so it can be reassembled e.g. when powered
          pform.prettyFunc = prettyFunc
          pform.prettyArgs = prettyArgs
  
          return pform
  
      def _print_ElementwiseApplyFunction(self, e):
          func = e.function
          arg = e.expr
          args = [arg]
          return self._helper_print_function(func, args, delimiter="", elementwise=True)
  
      @property
      def _special_function_classes(self):
          from sympy.functions.special.tensor_functions import KroneckerDelta
          from sympy.functions.special.gamma_functions import gamma, lowergamma
          from sympy.functions.special.zeta_functions import lerchphi
          from sympy.functions.special.beta_functions import beta
          from sympy.functions.special.delta_functions import DiracDelta
          from sympy.functions.special.error_functions import Chi
          return {KroneckerDelta: [greek_unicode['delta'], 'delta'],
                  gamma: [greek_unicode['Gamma'], 'Gamma'],
                  lerchphi: [greek_unicode['Phi'], 'lerchphi'],
                  lowergamma: [greek_unicode['gamma'], 'gamma'],
                  beta: [greek_unicode['Beta'], 'B'],
                  DiracDelta: [greek_unicode['delta'], 'delta'],
                  Chi: ['Chi', 'Chi']}
  
      def _print_FunctionClass(self, expr):
          for cls in self._special_function_classes:
              if issubclass(expr, cls) and expr.__name__ == cls.__name__:
                  if self._use_unicode:
                      return prettyForm(self._special_function_classes[cls][0])
                  else:
                      return prettyForm(self._special_function_classes[cls][1])
          func_name = expr.__name__
          return prettyForm(pretty_symbol(func_name))
  
      def _print_GeometryEntity(self, expr):
          # GeometryEntity is based on Tuple but should not print like a Tuple
          return self.emptyPrinter(expr)
  
      def _print_lerchphi(self, e):
          func_name = greek_unicode['Phi'] if self._use_unicode else 'lerchphi'
          return self._print_Function(e, func_name=func_name)
  
      def _print_dirichlet_eta(self, e):
          func_name = greek_unicode['eta'] if self._use_unicode else 'dirichlet_eta'
          return self._print_Function(e, func_name=func_name)
  
      def _print_Heaviside(self, e):
          func_name = greek_unicode['theta'] if self._use_unicode else 'Heaviside'
          if e.args[1]==1/2:
              pform = prettyForm(*self._print(e.args[0]).parens())
              pform = prettyForm(*pform.left(func_name))
              return pform
          else:
              return self._print_Function(e, func_name=func_name)
  
      def _print_fresnels(self, e):
          return self._print_Function(e, func_name="S")
  
      def _print_fresnelc(self, e):
          return self._print_Function(e, func_name="C")
  
      def _print_airyai(self, e):
          return self._print_Function(e, func_name="Ai")
  
      def _print_airybi(self, e):
          return self._print_Function(e, func_name="Bi")
  
      def _print_airyaiprime(self, e):
          return self._print_Function(e, func_name="Ai'")
  
      def _print_airybiprime(self, e):
          return self._print_Function(e, func_name="Bi'")
  
      def _print_LambertW(self, e):
          return self._print_Function(e, func_name="W")
  
      def _print_Lambda(self, e):
          expr = e.expr
          sig = e.signature
          if self._use_unicode:
              arrow = " \N{RIGHTWARDS ARROW FROM BAR} "
          else:
              arrow = " -> "
          if len(sig) == 1 and sig[0].is_symbol:
              sig = sig[0]
          var_form = self._print(sig)
  
          return prettyForm(*stringPict.next(var_form, arrow, self._print(expr)), binding=8)
  
      def _print_Order(self, expr):
          pform = self._print(expr.expr)
          if (expr.point and any(p != S.Zero for p in expr.point)) or \
             len(expr.variables) > 1:
              pform = prettyForm(*pform.right("; "))
              if len(expr.variables) > 1:
                  pform = prettyForm(*pform.right(self._print(expr.variables)))
              elif len(expr.variables):
                  pform = prettyForm(*pform.right(self._print(expr.variables[0])))
              if self._use_unicode:
                  pform = prettyForm(*pform.right(" \N{RIGHTWARDS ARROW} "))
              else:
                  pform = prettyForm(*pform.right(" -> "))
              if len(expr.point) > 1:
                  pform = prettyForm(*pform.right(self._print(expr.point)))
              else:
                  pform = prettyForm(*pform.right(self._print(expr.point[0])))
          pform = prettyForm(*pform.parens())
          pform = prettyForm(*pform.left("O"))
          return pform
  
      def _print_SingularityFunction(self, e):
          if self._use_unicode:
              shift = self._print(e.args[0]-e.args[1])
              n = self._print(e.args[2])
              base = prettyForm("<")
              base = prettyForm(*base.right(shift))
              base = prettyForm(*base.right(">"))
              pform = base**n
              return pform
          else:
              n = self._print(e.args[2])
              shift = self._print(e.args[0]-e.args[1])
              base = self._print_seq(shift, "<", ">", ' ')
              return base**n
  
      def _print_beta(self, e):
          func_name = greek_unicode['Beta'] if self._use_unicode else 'B'
          return self._print_Function(e, func_name=func_name)
  
      def _print_betainc(self, e):
          func_name = "B'"
          return self._print_Function(e, func_name=func_name)
  
      def _print_betainc_regularized(self, e):
          func_name = 'I'
          return self._print_Function(e, func_name=func_name)
  
      def _print_gamma(self, e):
          func_name = greek_unicode['Gamma'] if self._use_unicode else 'Gamma'
          return self._print_Function(e, func_name=func_name)
  
      def _print_uppergamma(self, e):
          func_name = greek_unicode['Gamma'] if self._use_unicode else 'Gamma'
          return self._print_Function(e, func_name=func_name)
  
      def _print_lowergamma(self, e):
          func_name = greek_unicode['gamma'] if self._use_unicode else 'lowergamma'
          return self._print_Function(e, func_name=func_name)
  
      def _print_DiracDelta(self, e):
          if self._use_unicode:
              if len(e.args) == 2:
                  a = prettyForm(greek_unicode['delta'])
                  b = self._print(e.args[1])
                  b = prettyForm(*b.parens())
                  c = self._print(e.args[0])
                  c = prettyForm(*c.parens())
                  pform = a**b
                  pform = prettyForm(*pform.right(' '))
                  pform = prettyForm(*pform.right(c))
                  return pform
              pform = self._print(e.args[0])
              pform = prettyForm(*pform.parens())
              pform = prettyForm(*pform.left(greek_unicode['delta']))
              return pform
          else:
              return self._print_Function(e)
  
      def _print_expint(self, e):
          from sympy import Function
          if e.args[0].is_Integer and self._use_unicode:
              return self._print_Function(Function('E_%s' % e.args[0])(e.args[1]))
          return self._print_Function(e)
  
      def _print_Chi(self, e):
          # This needs a special case since otherwise it comes out as greek
          # letter chi...
          prettyFunc = prettyForm("Chi")
          prettyArgs = prettyForm(*self._print_seq(e.args).parens())
  
          pform = prettyForm(
              binding=prettyForm.FUNC, *stringPict.next(prettyFunc, prettyArgs))
  
          # store pform parts so it can be reassembled e.g. when powered
          pform.prettyFunc = prettyFunc
          pform.prettyArgs = prettyArgs
  
          return pform
  
      def _print_elliptic_e(self, e):
          pforma0 = self._print(e.args[0])
          if len(e.args) == 1:
              pform = pforma0
          else:
              pforma1 = self._print(e.args[1])
              pform = self._hprint_vseparator(pforma0, pforma1)
          pform = prettyForm(*pform.parens())
          pform = prettyForm(*pform.left('E'))
          return pform
  
      def _print_elliptic_k(self, e):
          pform = self._print(e.args[0])
          pform = prettyForm(*pform.parens())
          pform = prettyForm(*pform.left('K'))
          return pform
  
      def _print_elliptic_f(self, e):
          pforma0 = self._print(e.args[0])
          pforma1 = self._print(e.args[1])
          pform = self._hprint_vseparator(pforma0, pforma1)
          pform = prettyForm(*pform.parens())
          pform = prettyForm(*pform.left('F'))
          return pform
  
      def _print_elliptic_pi(self, e):
          name = greek_unicode['Pi'] if self._use_unicode else 'Pi'
          pforma0 = self._print(e.args[0])
          pforma1 = self._print(e.args[1])
          if len(e.args) == 2:
              pform = self._hprint_vseparator(pforma0, pforma1)
          else:
              pforma2 = self._print(e.args[2])
              pforma = self._hprint_vseparator(pforma1, pforma2, ifascii_nougly=False)
              pforma = prettyForm(*pforma.left('; '))
              pform = prettyForm(*pforma.left(pforma0))
          pform = prettyForm(*pform.parens())
          pform = prettyForm(*pform.left(name))
          return pform
  
      def _print_GoldenRatio(self, expr):
          if self._use_unicode:
              return prettyForm(pretty_symbol('phi'))
          return self._print(Symbol("GoldenRatio"))
  
      def _print_EulerGamma(self, expr):
          if self._use_unicode:
              return prettyForm(pretty_symbol('gamma'))
          return self._print(Symbol("EulerGamma"))
  
      def _print_Mod(self, expr):
          pform = self._print(expr.args[0])
          if pform.binding > prettyForm.MUL:
              pform = prettyForm(*pform.parens())
          pform = prettyForm(*pform.right(' mod '))
          pform = prettyForm(*pform.right(self._print(expr.args[1])))
          pform.binding = prettyForm.OPEN
          return pform
  
      def _print_Add(self, expr, order=None):
          terms = self._as_ordered_terms(expr, order=order)
          pforms, indices = [], []
  
          def pretty_negative(pform, index):
              """Prepend a minus sign to a pretty form. """
              #TODO: Move this code to prettyForm
              if index == 0:
                  if pform.height() > 1:
                      pform_neg = '- '
                  else:
                      pform_neg = '-'
              else:
                  pform_neg = ' - '
  
              if (pform.binding > prettyForm.NEG
                  or pform.binding == prettyForm.ADD):
                  p = stringPict(*pform.parens())
              else:
                  p = pform
              p = stringPict.next(pform_neg, p)
              # Lower the binding to NEG, even if it was higher. Otherwise, it
              # will print as a + ( - (b)), instead of a - (b).
              return prettyForm(binding=prettyForm.NEG, *p)
  
          for i, term in enumerate(terms):
              if term.is_Mul and _coeff_isneg(term):
                  coeff, other = term.as_coeff_mul(rational=False)
                  if coeff == -1:
                      negterm = Mul(*other, evaluate=False)
                  else:
                      negterm = Mul(-coeff, *other, evaluate=False)
                  pform = self._print(negterm)
                  pforms.append(pretty_negative(pform, i))
              elif term.is_Rational and term.q > 1:
                  pforms.append(None)
                  indices.append(i)
              elif term.is_Number and term < 0:
                  pform = self._print(-term)
                  pforms.append(pretty_negative(pform, i))
              elif term.is_Relational:
                  pforms.append(prettyForm(*self._print(term).parens()))
              else:
                  pforms.append(self._print(term))
  
          if indices:
              large = True
  
              for pform in pforms:
                  if pform is not None and pform.height() > 1:
                      break
              else:
                  large = False
  
              for i in indices:
                  term, negative = terms[i], False
  
                  if term < 0:
                      term, negative = -term, True
  
                  if large:
                      pform = prettyForm(str(term.p))/prettyForm(str(term.q))
                  else:
                      pform = self._print(term)
  
                  if negative:
                      pform = pretty_negative(pform, i)
  
                  pforms[i] = pform
  
          return prettyForm.__add__(*pforms)
  
      def _print_Mul(self, product):
          from sympy.physics.units import Quantity
  
          # Check for unevaluated Mul. In this case we need to make sure the
          # identities are visible, multiple Rational factors are not combined
          # etc so we display in a straight-forward form that fully preserves all
          # args and their order.
          args = product.args
          if args[0] is S.One or any(isinstance(arg, Number) for arg in args[1:]):
              strargs = list(map(self._print, args))
              # XXX: This is a hack to work around the fact that
              # prettyForm.__mul__ absorbs a leading -1 in the args. Probably it
              # would be better to fix this in prettyForm.__mul__ instead.
              negone = strargs[0] == '-1'
              if negone:
                  strargs[0] = prettyForm('1', 0, 0)
              obj = prettyForm.__mul__(*strargs)
              if negone:
                  obj = prettyForm('-' + obj.s, obj.baseline, obj.binding)
              return obj
  
          a = []  # items in the numerator
          b = []  # items that are in the denominator (if any)
  
          if self.order not in ('old', 'none'):
              args = product.as_ordered_factors()
          else:
              args = list(product.args)
  
          # If quantities are present append them at the back
          args = sorted(args, key=lambda x: isinstance(x, Quantity) or
                       (isinstance(x, Pow) and isinstance(x.base, Quantity)))
  
          # Gather terms for numerator/denominator
          for item in args:
              if item.is_commutative and item.is_Pow and item.exp.is_Rational and item.exp.is_negative:
                  if item.exp != -1:
                      b.append(Pow(item.base, -item.exp, evaluate=False))
                  else:
                      b.append(Pow(item.base, -item.exp))
              elif item.is_Rational and item is not S.Infinity:
                  if item.p != 1:
                      a.append( Rational(item.p) )
                  if item.q != 1:
                      b.append( Rational(item.q) )
              else:
                  a.append(item)
  
          from sympy import Integral, Piecewise, Product, Sum
  
          # Convert to pretty forms. Add parens to Add instances if there
          # is more than one term in the numer/denom
          for i in range(0, len(a)):
              if (a[i].is_Add and len(a) > 1) or (i != len(a) - 1 and
                      isinstance(a[i], (Integral, Piecewise, Product, Sum))):
                  a[i] = prettyForm(*self._print(a[i]).parens())
              elif a[i].is_Relational:
                  a[i] = prettyForm(*self._print(a[i]).parens())
              else:
                  a[i] = self._print(a[i])
  
          for i in range(0, len(b)):
              if (b[i].is_Add and len(b) > 1) or (i != len(b) - 1 and
                      isinstance(b[i], (Integral, Piecewise, Product, Sum))):
                  b[i] = prettyForm(*self._print(b[i]).parens())
              else:
                  b[i] = self._print(b[i])
  
          # Construct a pretty form
          if len(b) == 0:
              return prettyForm.__mul__(*a)
          else:
              if len(a) == 0:
                  a.append( self._print(S.One) )
              return prettyForm.__mul__(*a)/prettyForm.__mul__(*b)
  
      # A helper function for _print_Pow to print x**(1/n)
      def _print_nth_root(self, base, root):
          bpretty = self._print(base)
  
          # In very simple cases, use a single-char root sign
          if (self._settings['use_unicode_sqrt_char'] and self._use_unicode
              and root == 2 and bpretty.height() == 1
              and (bpretty.width() == 1
                   or (base.is_Integer and base.is_nonnegative))):
              return prettyForm(*bpretty.left('\N{SQUARE ROOT}'))
  
          # Construct root sign, start with the \/ shape
          _zZ = xobj('/', 1)
          rootsign = xobj('\\', 1) + _zZ
          # Constructing the number to put on root
          rpretty = self._print(root)
          # roots look bad if they are not a single line
          if rpretty.height() != 1:
              return self._print(base)**self._print(1/root)
          # If power is half, no number should appear on top of root sign
          exp = '' if root == 2 else str(rpretty).ljust(2)
          if len(exp) > 2:
              rootsign = ' '*(len(exp) - 2) + rootsign
          # Stack the exponent
          rootsign = stringPict(exp + '\n' + rootsign)
          rootsign.baseline = 0
          # Diagonal: length is one less than height of base
          linelength = bpretty.height() - 1
          diagonal = stringPict('\n'.join(
              ' '*(linelength - i - 1) + _zZ + ' '*i
              for i in range(linelength)
          ))
          # Put baseline just below lowest line: next to exp
          diagonal.baseline = linelength - 1
          # Make the root symbol
          rootsign = prettyForm(*rootsign.right(diagonal))
          # Det the baseline to match contents to fix the height
          # but if the height of bpretty is one, the rootsign must be one higher
          rootsign.baseline = max(1, bpretty.baseline)
          #build result
          s = prettyForm(hobj('_', 2 + bpretty.width()))
          s = prettyForm(*bpretty.above(s))
          s = prettyForm(*s.left(rootsign))
          return s
  
      def _print_Pow(self, power):
          from sympy.simplify.simplify import fraction
          b, e = power.as_base_exp()
          if power.is_commutative:
              if e is S.NegativeOne:
                  return prettyForm("1")/self._print(b)
              n, d = fraction(e)
              if n is S.One and d.is_Atom and not e.is_Integer and (e.is_Rational or d.is_Symbol) \
                      and self._settings['root_notation']:
                  return self._print_nth_root(b, d)
              if e.is_Rational and e < 0:
                  return prettyForm("1")/self._print(Pow(b, -e, evaluate=False))
  
          if b.is_Relational:
              return prettyForm(*self._print(b).parens()).__pow__(self._print(e))
  
          return self._print(b)**self._print(e)
  
      def _print_UnevaluatedExpr(self, expr):
          return self._print(expr.args[0])
  
      def __print_numer_denom(self, p, q):
          if q == 1:
              if p < 0:
                  return prettyForm(str(p), binding=prettyForm.NEG)
              else:
                  return prettyForm(str(p))
          elif abs(p) >= 10 and abs(q) >= 10:
              # If more than one digit in numer and denom, print larger fraction
              if p < 0:
                  return prettyForm(str(p), binding=prettyForm.NEG)/prettyForm(str(q))
                  # Old printing method:
                  #pform = prettyForm(str(-p))/prettyForm(str(q))
                  #return prettyForm(binding=prettyForm.NEG, *pform.left('- '))
              else:
                  return prettyForm(str(p))/prettyForm(str(q))
          else:
              return None
  
      def _print_Rational(self, expr):
          result = self.__print_numer_denom(expr.p, expr.q)
  
          if result is not None:
              return result
          else:
              return self.emptyPrinter(expr)
  
      def _print_Fraction(self, expr):
          result = self.__print_numer_denom(expr.numerator, expr.denominator)
  
          if result is not None:
              return result
          else:
              return self.emptyPrinter(expr)
  
      def _print_ProductSet(self, p):
          if len(p.sets) >= 1 and not has_variety(p.sets):
              return self._print(p.sets[0]) ** self._print(len(p.sets))
          else:
              prod_char = "\N{MULTIPLICATION SIGN}" if self._use_unicode else 'x'
              return self._print_seq(p.sets, None, None, ' %s ' % prod_char,
                                     parenthesize=lambda set: set.is_Union or
                                     set.is_Intersection or set.is_ProductSet)
  
      def _print_FiniteSet(self, s):
          items = sorted(s.args, key=default_sort_key)
          return self._print_seq(items, '{', '}', ', ' )
  
      def _print_Range(self, s):
  
          if self._use_unicode:
              dots = "\N{HORIZONTAL ELLIPSIS}"
          else:
              dots = '...'
  
          if s.start.is_infinite and s.stop.is_infinite:
              if s.step.is_positive:
                  printset = dots, -1, 0, 1, dots
              else:
                  printset = dots, 1, 0, -1, dots
          elif s.start.is_infinite:
              printset = dots, s[-1] - s.step, s[-1]
          elif s.stop.is_infinite:
              it = iter(s)
              printset = next(it), next(it), dots
          elif len(s) > 4:
              it = iter(s)
              printset = next(it), next(it), dots, s[-1]
          else:
              printset = tuple(s)
  
          return self._print_seq(printset, '{', '}', ', ' )
  
      def _print_Interval(self, i):
          if i.start == i.end:
              return self._print_seq(i.args[:1], '{', '}')
  
          else:
              if i.left_open:
                  left = '('
              else:
                  left = '['
  
              if i.right_open:
                  right = ')'
              else:
                  right = ']'
  
              return self._print_seq(i.args[:2], left, right)
  
      def _print_AccumulationBounds(self, i):
          left = '<'
          right = '>'
  
          return self._print_seq(i.args[:2], left, right)
  
      def _print_Intersection(self, u):
  
          delimiter = ' %s ' % pretty_atom('Intersection', 'n')
  
          return self._print_seq(u.args, None, None, delimiter,
                                 parenthesize=lambda set: set.is_ProductSet or
                                 set.is_Union or set.is_Complement)
  
      def _print_Union(self, u):
  
          union_delimiter = ' %s ' % pretty_atom('Union', 'U')
  
          return self._print_seq(u.args, None, None, union_delimiter,
                                 parenthesize=lambda set: set.is_ProductSet or
                                 set.is_Intersection or set.is_Complement)
  
      def _print_SymmetricDifference(self, u):
          if not self._use_unicode:
              raise NotImplementedError("ASCII pretty printing of SymmetricDifference is not implemented")
  
          sym_delimeter = ' %s ' % pretty_atom('SymmetricDifference')
  
          return self._print_seq(u.args, None, None, sym_delimeter)
  
      def _print_Complement(self, u):
  
          delimiter = r' \ '
  
          return self._print_seq(u.args, None, None, delimiter,
               parenthesize=lambda set: set.is_ProductSet or set.is_Intersection
                                 or set.is_Union)
  
      def _print_ImageSet(self, ts):
          if self._use_unicode:
              inn = "\N{SMALL ELEMENT OF}"
          else:
              inn = 'in'
          fun = ts.lamda
          sets = ts.base_sets
          signature = fun.signature
          expr = self._print(fun.expr)
  
          # TODO: the stuff to the left of the | and the stuff to the right of
          # the | should have independent baselines, that way something like
          # ImageSet(Lambda(x, 1/x**2), S.Naturals) prints the "x in N" part
          # centered on the right instead of aligned with the fraction bar on
          # the left. The same also applies to ConditionSet and ComplexRegion
          if len(signature) == 1:
              S = self._print_seq((signature[0], inn, sets[0]),
                                  delimiter=' ')
              return self._hprint_vseparator(expr, S,
                                             left='{', right='}',
                                             ifascii_nougly=True, delimiter=' ')
          else:
              pargs = tuple(j for var, setv in zip(signature, sets) for j in
                            (var, ' ', inn, ' ', setv, ", "))
              S = self._print_seq(pargs[:-1], delimiter='')
              return self._hprint_vseparator(expr, S,
                                             left='{', right='}',
                                             ifascii_nougly=True, delimiter=' ')
  
      def _print_ConditionSet(self, ts):
          if self._use_unicode:
              inn = "\N{SMALL ELEMENT OF}"
              # using _and because and is a keyword and it is bad practice to
              # overwrite them
              _and = "\N{LOGICAL AND}"
          else:
              inn = 'in'
              _and = 'and'
  
          variables = self._print_seq(Tuple(ts.sym))
          as_expr = getattr(ts.condition, 'as_expr', None)
          if as_expr is not None:
              cond = self._print(ts.condition.as_expr())
          else:
              cond = self._print(ts.condition)
              if self._use_unicode:
                  cond = self._print(cond)
                  cond = prettyForm(*cond.parens())
  
          if ts.base_set is S.UniversalSet:
              return self._hprint_vseparator(variables, cond, left="{",
                                             right="}", ifascii_nougly=True,
                                             delimiter=' ')
  
          base = self._print(ts.base_set)
          C = self._print_seq((variables, inn, base, _and, cond),
                              delimiter=' ')
          return self._hprint_vseparator(variables, C, left="{", right="}",
                                         ifascii_nougly=True, delimiter=' ')
  
      def _print_ComplexRegion(self, ts):
          if self._use_unicode:
              inn = "\N{SMALL ELEMENT OF}"
          else:
              inn = 'in'
          variables = self._print_seq(ts.variables)
          expr = self._print(ts.expr)
          prodsets = self._print(ts.sets)
  
          C = self._print_seq((variables, inn, prodsets),
                              delimiter=' ')
          return self._hprint_vseparator(expr, C, left="{", right="}",
                                         ifascii_nougly=True, delimiter=' ')
  
      def _print_Contains(self, e):
          var, set = e.args
          if self._use_unicode:
              el = " \N{ELEMENT OF} "
              return prettyForm(*stringPict.next(self._print(var),
                                                 el, self._print(set)), binding=8)
          else:
              return prettyForm(sstr(e))
  
      def _print_FourierSeries(self, s):
          if self._use_unicode:
              dots = "\N{HORIZONTAL ELLIPSIS}"
          else:
              dots = '...'
          return self._print_Add(s.truncate()) + self._print(dots)
  
      def _print_FormalPowerSeries(self, s):
          return self._print_Add(s.infinite)
  
      def _print_SetExpr(self, se):
          pretty_set = prettyForm(*self._print(se.set).parens())
          pretty_name = self._print(Symbol("SetExpr"))
          return prettyForm(*pretty_name.right(pretty_set))
  
      def _print_SeqFormula(self, s):
          if self._use_unicode:
              dots = "\N{HORIZONTAL ELLIPSIS}"
          else:
              dots = '...'
  
          if len(s.start.free_symbols) > 0 or len(s.stop.free_symbols) > 0:
              raise NotImplementedError("Pretty printing of sequences with symbolic bound not implemented")
  
          if s.start is S.NegativeInfinity:
              stop = s.stop
              printset = (dots, s.coeff(stop - 3), s.coeff(stop - 2),
                  s.coeff(stop - 1), s.coeff(stop))
          elif s.stop is S.Infinity or s.length > 4:
              printset = s[:4]
              printset.append(dots)
              printset = tuple(printset)
          else:
              printset = tuple(s)
          return self._print_list(printset)
  
      _print_SeqPer = _print_SeqFormula
      _print_SeqAdd = _print_SeqFormula
      _print_SeqMul = _print_SeqFormula
  
      def _print_seq(self, seq, left=None, right=None, delimiter=', ',
              parenthesize=lambda x: False, ifascii_nougly=True):
          try:
              pforms = []
              for item in seq:
                  pform = self._print(item)
                  if parenthesize(item):
                      pform = prettyForm(*pform.parens())
                  if pforms:
                      pforms.append(delimiter)
                  pforms.append(pform)
  
              if not pforms:
                  s = stringPict('')
              else:
                  s = prettyForm(*stringPict.next(*pforms))
  
                  # XXX: Under the tests from #15686 the above raises:
                  # AttributeError: 'Fake' object has no attribute 'baseline'
                  # This is caught below but that is not the right way to
                  # fix it.
  
          except AttributeError:
              s = None
              for item in seq:
                  pform = self.doprint(item)
                  if parenthesize(item):
                      pform = prettyForm(*pform.parens())
                  if s is None:
                      # first element
                      s = pform
                  else :
                      s = prettyForm(*stringPict.next(s, delimiter))
                      s = prettyForm(*stringPict.next(s, pform))
  
              if s is None:
                  s = stringPict('')
  
          s = prettyForm(*s.parens(left, right, ifascii_nougly=ifascii_nougly))
          return s
  
      def join(self, delimiter, args):
          pform = None
  
          for arg in args:
              if pform is None:
                  pform = arg
              else:
                  pform = prettyForm(*pform.right(delimiter))
                  pform = prettyForm(*pform.right(arg))
  
          if pform is None:
              return prettyForm("")
          else:
              return pform
  
      def _print_list(self, l):
          return self._print_seq(l, '[', ']')
  
      def _print_tuple(self, t):
          if len(t) == 1:
              ptuple = prettyForm(*stringPict.next(self._print(t[0]), ','))
              return prettyForm(*ptuple.parens('(', ')', ifascii_nougly=True))
          else:
              return self._print_seq(t, '(', ')')
  
      def _print_Tuple(self, expr):
          return self._print_tuple(expr)
  
      def _print_dict(self, d):
          keys = sorted(d.keys(), key=default_sort_key)
          items = []
  
          for k in keys:
              K = self._print(k)
              V = self._print(d[k])
              s = prettyForm(*stringPict.next(K, ': ', V))
  
              items.append(s)
  
          return self._print_seq(items, '{', '}')
  
      def _print_Dict(self, d):
          return self._print_dict(d)
  
      def _print_set(self, s):
          if not s:
              return prettyForm('set()')
          items = sorted(s, key=default_sort_key)
          pretty = self._print_seq(items)
          pretty = prettyForm(*pretty.parens('{', '}', ifascii_nougly=True))
          return pretty
  
      def _print_frozenset(self, s):
          if not s:
              return prettyForm('frozenset()')
          items = sorted(s, key=default_sort_key)
          pretty = self._print_seq(items)
          pretty = prettyForm(*pretty.parens('{', '}', ifascii_nougly=True))
          pretty = prettyForm(*pretty.parens('(', ')', ifascii_nougly=True))
          pretty = prettyForm(*stringPict.next(type(s).__name__, pretty))
          return pretty
  
      def _print_UniversalSet(self, s):
          if self._use_unicode:
              return prettyForm("\N{MATHEMATICAL DOUBLE-STRUCK CAPITAL U}")
          else:
              return prettyForm('UniversalSet')
  
      def _print_PolyRing(self, ring):
          return prettyForm(sstr(ring))
  
      def _print_FracField(self, field):
          return prettyForm(sstr(field))
  
      def _print_FreeGroupElement(self, elm):
          return prettyForm(str(elm))
  
      def _print_PolyElement(self, poly):
          return prettyForm(sstr(poly))
  
      def _print_FracElement(self, frac):
          return prettyForm(sstr(frac))
  
      def _print_AlgebraicNumber(self, expr):
          if expr.is_aliased:
              return self._print(expr.as_poly().as_expr())
          else:
              return self._print(expr.as_expr())
  
      def _print_ComplexRootOf(self, expr):
          args = [self._print_Add(expr.expr, order='lex'), expr.index]
          pform = prettyForm(*self._print_seq(args).parens())
          pform = prettyForm(*pform.left('CRootOf'))
          return pform
  
      def _print_RootSum(self, expr):
          args = [self._print_Add(expr.expr, order='lex')]
  
          if expr.fun is not S.IdentityFunction:
              args.append(self._print(expr.fun))
  
          pform = prettyForm(*self._print_seq(args).parens())
          pform = prettyForm(*pform.left('RootSum'))
  
          return pform
  
      def _print_FiniteField(self, expr):
          if self._use_unicode:
              form = '\N{DOUBLE-STRUCK CAPITAL Z}_%d'
          else:
              form = 'GF(%d)'
  
          return prettyForm(pretty_symbol(form % expr.mod))
  
      def _print_IntegerRing(self, expr):
          if self._use_unicode:
              return prettyForm('\N{DOUBLE-STRUCK CAPITAL Z}')
          else:
              return prettyForm('ZZ')
  
      def _print_RationalField(self, expr):
          if self._use_unicode:
              return prettyForm('\N{DOUBLE-STRUCK CAPITAL Q}')
          else:
              return prettyForm('QQ')
  
      def _print_RealField(self, domain):
          if self._use_unicode:
              prefix = '\N{DOUBLE-STRUCK CAPITAL R}'
          else:
              prefix = 'RR'
  
          if domain.has_default_precision:
              return prettyForm(prefix)
          else:
              return self._print(pretty_symbol(prefix + "_" + str(domain.precision)))
  
      def _print_ComplexField(self, domain):
          if self._use_unicode:
              prefix = '\N{DOUBLE-STRUCK CAPITAL C}'
          else:
              prefix = 'CC'
  
          if domain.has_default_precision:
              return prettyForm(prefix)
          else:
              return self._print(pretty_symbol(prefix + "_" + str(domain.precision)))
  
      def _print_PolynomialRing(self, expr):
          args = list(expr.symbols)
  
          if not expr.order.is_default:
              order = prettyForm(*prettyForm("order=").right(self._print(expr.order)))
              args.append(order)
  
          pform = self._print_seq(args, '[', ']')
          pform = prettyForm(*pform.left(self._print(expr.domain)))
  
          return pform
  
      def _print_FractionField(self, expr):
          args = list(expr.symbols)
  
          if not expr.order.is_default:
              order = prettyForm(*prettyForm("order=").right(self._print(expr.order)))
              args.append(order)
  
          pform = self._print_seq(args, '(', ')')
          pform = prettyForm(*pform.left(self._print(expr.domain)))
  
          return pform
  
      def _print_PolynomialRingBase(self, expr):
          g = expr.symbols
          if str(expr.order) != str(expr.default_order):
              g = g + ("order=" + str(expr.order),)
          pform = self._print_seq(g, '[', ']')
          pform = prettyForm(*pform.left(self._print(expr.domain)))
  
          return pform
  
      def _print_GroebnerBasis(self, basis):
          exprs = [ self._print_Add(arg, order=basis.order)
                    for arg in basis.exprs ]
          exprs = prettyForm(*self.join(", ", exprs).parens(left="[", right="]"))
  
          gens = [ self._print(gen) for gen in basis.gens ]
  
          domain = prettyForm(
              *prettyForm("domain=").right(self._print(basis.domain)))
          order = prettyForm(
              *prettyForm("order=").right(self._print(basis.order)))
  
          pform = self.join(", ", [exprs] + gens + [domain, order])
  
          pform = prettyForm(*pform.parens())
          pform = prettyForm(*pform.left(basis.__class__.__name__))
  
          return pform
  
      def _print_Subs(self, e):
          pform = self._print(e.expr)
          pform = prettyForm(*pform.parens())
  
          h = pform.height() if pform.height() > 1 else 2
          rvert = stringPict(vobj('|', h), baseline=pform.baseline)
          pform = prettyForm(*pform.right(rvert))
  
          b = pform.baseline
          pform.baseline = pform.height() - 1
          pform = prettyForm(*pform.right(self._print_seq([
              self._print_seq((self._print(v[0]), xsym('=='), self._print(v[1])),
                  delimiter='') for v in zip(e.variables, e.point) ])))
  
          pform.baseline = b
          return pform
  
      def _print_number_function(self, e, name):
          # Print name_arg[0] for one argument or name_arg[0](arg[1])
          # for more than one argument
          pform = prettyForm(name)
          arg = self._print(e.args[0])
          pform_arg = prettyForm(" "*arg.width())
          pform_arg = prettyForm(*pform_arg.below(arg))
          pform = prettyForm(*pform.right(pform_arg))
          if len(e.args) == 1:
              return pform
          m, x = e.args
          # TODO: copy-pasted from _print_Function: can we do better?
          prettyFunc = pform
          prettyArgs = prettyForm(*self._print_seq([x]).parens())
          pform = prettyForm(
              binding=prettyForm.FUNC, *stringPict.next(prettyFunc, prettyArgs))
          pform.prettyFunc = prettyFunc
          pform.prettyArgs = prettyArgs
          return pform
  
      def _print_euler(self, e):
          return self._print_number_function(e, "E")
  
      def _print_catalan(self, e):
          return self._print_number_function(e, "C")
  
      def _print_bernoulli(self, e):
          return self._print_number_function(e, "B")
  
      _print_bell = _print_bernoulli
  
      def _print_lucas(self, e):
          return self._print_number_function(e, "L")
  
      def _print_fibonacci(self, e):
          return self._print_number_function(e, "F")
  
      def _print_tribonacci(self, e):
          return self._print_number_function(e, "T")
  
      def _print_stieltjes(self, e):
          if self._use_unicode:
              return self._print_number_function(e, '\N{GREEK SMALL LETTER GAMMA}')
          else:
              return self._print_number_function(e, "stieltjes")
  
      def _print_KroneckerDelta(self, e):
          pform = self._print(e.args[0])
          pform = prettyForm(*pform.right(prettyForm(',')))
          pform = prettyForm(*pform.right(self._print(e.args[1])))
          if self._use_unicode:
              a = stringPict(pretty_symbol('delta'))
          else:
              a = stringPict('d')
          b = pform
          top = stringPict(*b.left(' '*a.width()))
          bot = stringPict(*a.right(' '*b.width()))
          return prettyForm(binding=prettyForm.POW, *bot.below(top))
  
      def _print_RandomDomain(self, d):
          if hasattr(d, 'as_boolean'):
              pform = self._print('Domain: ')
              pform = prettyForm(*pform.right(self._print(d.as_boolean())))
              return pform
          elif hasattr(d, 'set'):
              pform = self._print('Domain: ')
              pform = prettyForm(*pform.right(self._print(d.symbols)))
              pform = prettyForm(*pform.right(self._print(' in ')))
              pform = prettyForm(*pform.right(self._print(d.set)))
              return pform
          elif hasattr(d, 'symbols'):
              pform = self._print('Domain on ')
              pform = prettyForm(*pform.right(self._print(d.symbols)))
              return pform
          else:
              return self._print(None)
  
      def _print_DMP(self, p):
          try:
              if p.ring is not None:
                  # TODO incorporate order
                  return self._print(p.ring.to_sympy(p))
          except SympifyError:
              pass
          return self._print(repr(p))
  
      def _print_DMF(self, p):
          return self._print_DMP(p)
  
      def _print_Object(self, object):
          return self._print(pretty_symbol(object.name))
  
      def _print_Morphism(self, morphism):
          arrow = xsym("-->")
  
          domain = self._print(morphism.domain)
          codomain = self._print(morphism.codomain)
          tail = domain.right(arrow, codomain)[0]
  
          return prettyForm(tail)
  
      def _print_NamedMorphism(self, morphism):
          pretty_name = self._print(pretty_symbol(morphism.name))
          pretty_morphism = self._print_Morphism(morphism)
          return prettyForm(pretty_name.right(":", pretty_morphism)[0])
  
      def _print_IdentityMorphism(self, morphism):
          from sympy.categories import NamedMorphism
          return self._print_NamedMorphism(
              NamedMorphism(morphism.domain, morphism.codomain, "id"))
  
      def _print_CompositeMorphism(self, morphism):
  
          circle = xsym(".")
  
          # All components of the morphism have names and it is thus
          # possible to build the name of the composite.
          component_names_list = [pretty_symbol(component.name) for
                                  component in morphism.components]
          component_names_list.reverse()
          component_names = circle.join(component_names_list) + ":"
  
          pretty_name = self._print(component_names)
          pretty_morphism = self._print_Morphism(morphism)
          return prettyForm(pretty_name.right(pretty_morphism)[0])
  
      def _print_Category(self, category):
          return self._print(pretty_symbol(category.name))
  
      def _print_Diagram(self, diagram):
          if not diagram.premises:
              # This is an empty diagram.
              return self._print(S.EmptySet)
  
          pretty_result = self._print(diagram.premises)
          if diagram.conclusions:
              results_arrow = " %s " % xsym("==>")
  
              pretty_conclusions = self._print(diagram.conclusions)[0]
              pretty_result = pretty_result.right(
                  results_arrow, pretty_conclusions)
  
          return prettyForm(pretty_result[0])
  
      def _print_DiagramGrid(self, grid):
          from sympy.matrices import Matrix
          from sympy import Symbol
          matrix = Matrix([[grid[i, j] if grid[i, j] else Symbol(" ")
                            for j in range(grid.width)]
                           for i in range(grid.height)])
          return self._print_matrix_contents(matrix)
  
      def _print_FreeModuleElement(self, m):
          # Print as row vector for convenience, for now.
          return self._print_seq(m, '[', ']')
  
      def _print_SubModule(self, M):
          return self._print_seq(M.gens, '<', '>')
  
      def _print_FreeModule(self, M):
          return self._print(M.ring)**self._print(M.rank)
  
      def _print_ModuleImplementedIdeal(self, M):
          return self._print_seq([x for [x] in M._module.gens], '<', '>')
  
      def _print_QuotientRing(self, R):
          return self._print(R.ring) / self._print(R.base_ideal)
  
      def _print_QuotientRingElement(self, R):
          return self._print(R.data) + self._print(R.ring.base_ideal)
  
      def _print_QuotientModuleElement(self, m):
          return self._print(m.data) + self._print(m.module.killed_module)
  
      def _print_QuotientModule(self, M):
          return self._print(M.base) / self._print(M.killed_module)
  
      def _print_MatrixHomomorphism(self, h):
          matrix = self._print(h._sympy_matrix())
          matrix.baseline = matrix.height() // 2
          pform = prettyForm(*matrix.right(' : ', self._print(h.domain),
              ' %s> ' % hobj('-', 2), self._print(h.codomain)))
          return pform
  
      def _print_Manifold(self, manifold):
          return self._print(manifold.name)
  
      def _print_Patch(self, patch):
          return self._print(patch.name)
  
      def _print_CoordSystem(self, coords):
          return self._print(coords.name)
  
      def _print_BaseScalarField(self, field):
          string = field._coord_sys.symbols[field._index].name
          return self._print(pretty_symbol(string))
  
      def _print_BaseVectorField(self, field):
          s = U('PARTIAL DIFFERENTIAL') + '_' + field._coord_sys.symbols[field._index].name
          return self._print(pretty_symbol(s))
  
      def _print_Differential(self, diff):
          field = diff._form_field
          if hasattr(field, '_coord_sys'):
              string = field._coord_sys.symbols[field._index].name
              return self._print('\N{DOUBLE-STRUCK ITALIC SMALL D} ' + pretty_symbol(string))
          else:
              pform = self._print(field)
              pform = prettyForm(*pform.parens())
              return prettyForm(*pform.left("\N{DOUBLE-STRUCK ITALIC SMALL D}"))
  
      def _print_Tr(self, p):
          #TODO: Handle indices
          pform = self._print(p.args[0])
          pform = prettyForm(*pform.left('%s(' % (p.__class__.__name__)))
          pform = prettyForm(*pform.right(')'))
          return pform
  
      def _print_primenu(self, e):
          pform = self._print(e.args[0])
          pform = prettyForm(*pform.parens())
          if self._use_unicode:
              pform = prettyForm(*pform.left(greek_unicode['nu']))
          else:
              pform = prettyForm(*pform.left('nu'))
          return pform
  
      def _print_primeomega(self, e):
          pform = self._print(e.args[0])
          pform = prettyForm(*pform.parens())
          if self._use_unicode:
              pform = prettyForm(*pform.left(greek_unicode['Omega']))
          else:
              pform = prettyForm(*pform.left('Omega'))
          return pform
  
      def _print_Quantity(self, e):
          if e.name.name == 'degree':
              pform = self._print("\N{DEGREE SIGN}")
              return pform
          else:
              return self.emptyPrinter(e)
  
      def _print_AssignmentBase(self, e):
  
          op = prettyForm(' ' + xsym(e.op) + ' ')
  
          l = self._print(e.lhs)
          r = self._print(e.rhs)
          pform = prettyForm(*stringPict.next(l, op, r))
          return pform
  
      def _print_Str(self, s):
          return self._print(s.name)
  
  
  @print_function(PrettyPrinter)
  def pretty(expr, **settings):
      """Returns a string containing the prettified form of expr.
  
      For information on keyword arguments see pretty_print function.
  
      """
      pp = PrettyPrinter(settings)
  
      # XXX: this is an ugly hack, but at least it works
      use_unicode = pp._settings['use_unicode']
      uflag = pretty_use_unicode(use_unicode)
  
      try:
          return pp.doprint(expr)
      finally:
          pretty_use_unicode(uflag)
  
  
  def pretty_print(expr, **kwargs):
      """Prints expr in pretty form.
  
      pprint is just a shortcut for this function.
  
      Parameters
      ==========
  
      expr : expression
          The expression to print.
  
      wrap_line : bool, optional (default=True)
          Line wrapping enabled/disabled.
  
      num_columns : int or None, optional (default=None)
          Number of columns before line breaking (default to None which reads
          the terminal width), useful when using SymPy without terminal.
  
      use_unicode : bool or None, optional (default=None)
          Use unicode characters, such as the Greek letter pi instead of
          the string pi.
  
      full_prec : bool or string, optional (default="auto")
          Use full precision.
  
      order : bool or string, optional (default=None)
          Set to 'none' for long expressions if slow; default is None.
  
      use_unicode_sqrt_char : bool, optional (default=True)
          Use compact single-character square root symbol (when unambiguous).
  
      root_notation : bool, optional (default=True)
          Set to 'False' for printing exponents of the form 1/n in fractional form.
          By default exponent is printed in root form.
  
      mat_symbol_style : string, optional (default="plain")
          Set to "bold" for printing MatrixSymbols using a bold mathematical symbol face.
          By default the standard face is used.
  
      imaginary_unit : string, optional (default="i")
          Letter to use for imaginary unit when use_unicode is True.
          Can be "i" (default) or "j".
      """
      print(pretty(expr, **kwargs))
  
  pprint = pretty_print
  
  
  def pager_print(expr, **settings):
      """Prints expr using the pager, in pretty form.
  
      This invokes a pager command using pydoc. Lines are not wrapped
      automatically. This routine is meant to be used with a pager that allows
      sideways scrolling, like ``less -S``.
  
      Parameters are the same as for ``pretty_print``. If you wish to wrap lines,
      pass ``num_columns=None`` to auto-detect the width of the terminal.
  
      """
      from pydoc import pager
      from locale import getpreferredencoding
      if 'num_columns' not in settings:
          settings['num_columns'] = 500000  # disable line wrap
      pager(pretty(expr, **settings).encode(getpreferredencoding()))
  