class_docstr: A container for all the information relating to a differential extension.
  
  Explanation
  ===========
  
  The attributes of this object are (see also the docstring of __init__):
  
  - f: The original (Expr) integrand.
  - x: The variable of integration.
  - T: List of variables in the extension.
  - D: List of derivations in the extension; corresponds to the elements of T.
  - fa: Poly of the numerator of the integrand.
  - fd: Poly of the denominator of the integrand.
  - Tfuncs: Lambda() representations of each element of T (except for x).
    For back-substitution after integration.
  - backsubs: A (possibly empty) list of further substitutions to be made on
    the final integral to make it look more like the integrand.
  - exts:
  - extargs:
  - cases: List of string representations of the cases of T.
  - t: The top level extension variable, as defined by the current level
    (see level below).
  - d: The top level extension derivation, as defined by the current
    derivation (see level below).
  - case: The string representation of the case of self.d.
  (Note that self.T and self.D will always contain the complete extension,
  regardless of the level.  Therefore, you should ALWAYS use DE.t and DE.d
  instead of DE.T[-1] and DE.D[-1].  If you want to have a list of the
  derivations or variables only up to the current level, use
  DE.D[:len(DE.D) + DE.level + 1] and DE.T[:len(DE.T) + DE.level + 1].  Note
  that, in particular, the derivation() function does this.)
  
  The following are also attributes, but will probably not be useful other
  than in internal use:
  - newf: Expr form of fa/fd.
  - level: The number (between -1 and -len(self.T)) such that
    self.T[self.level] == self.t and self.D[self.level] == self.d.
    Use the methods self.increment_level() and self.decrement_level() to change
    the current level.
class_docstr_tok: ["A","container","for","all","the","information","relating","to","a","differential","extension",".","Explanation","===========","The","attributes","of","this","object","are","(","see","also","the","docstring","of","__init__",")",":","-","f",":","The","original","(","Expr",")","integrand",".","-","x",":","The","variable","of","integration",".","-","T",":","List","of","variables","in","the","extension",".","-","D",":","List","of","derivations","in","the","extension",";","corresponds","to","the","elements","of","T.","-","fa",":","Poly","of","the","numerator","of","the","integrand",".","-","fd",":","Poly","of","the","denominator","of","the","integrand",".","-","Tfuncs",":","Lambda","(",")","representations","of","each","element","of","T","(","except","for","x",")",".","For","back-substitution","after","integration",".","-","backsubs",":","A","(","possibly","empty",")","list","of","further","substitutions","to","be","made","on","the","final","integral","to","make","it","look","more","like","the","integrand",".","-","exts",":","-","extargs",":","-","cases",":","List","of","string","representations","of","the","cases","of","T.","-","t",":","The","top","level","extension","variable",",","as","defined","by","the","current","level","(","see","level","below",")",".","-","d",":","The","top","level","extension","derivation",",","as","defined","by","the","current","derivation","(","see","level","below",")",".","-","case",":","The","string","representation","of","the","case","of","self.d",".","(","Note","that","self.T","and","self.D","will","always","contain","the","complete","extension",",","regardless","of","the","level",".","Therefore",",","you","should","ALWAYS","use","DE.t","and","DE.d","instead","of","DE.T","[","-1","]","and","DE.D","[","-1","]",".","If","you","want","to","have","a","list","of","the","derivations","or","variables","only","up","to","the","current","level",",","use","DE.D","[",":","len","(","DE.D",")","+","DE.level","+","1","]","and","DE.T","[",":","len","(","DE.T",")","+","DE.level","+","1","]",".","Note","that",",","in","particular",",","the","derivation","(",")","function","does","this",".",")","The","following","are","also","attributes",",","but","will","probably","not","be","useful","other","than","in","internal","use",":","-","newf",":","Expr","form","of","fa\/fd",".","-","level",":","The","number","(","between","-1","and","-len","(","self.T",")",")","such","that","self.T","[","self.level","]","==","self.t","and","self.D","[","self.level","]","==","self.d",".","Use","the","methods","self.increment_level","(",")","and","self.decrement_level","(",")","to","change","the","current","level","."]
docstr: Try to build an exponential extension.
  
  Returns
  =======
  
  Returns True if there was a new extension, False if there was no new
  extension but it was able to rewrite the given exponentials in terms
  of the existing extension, and None if the entire extension building
  process should be restarted.  If the process fails because there is no
  way around an algebraic extension (e.g., exp(log(x)/2)), it will raise
  NotImplementedError.
docstr_tok: ["Try","to","build","an","exponential","extension",".","Returns","=======","Returns","True","if","there","was","a","new","extension",",","False","if","there","was","no","new","extension","but","it","was","able","to","rewrite","the","given","exponentials","in","terms","of","the","existing","extension",",","and","None","if","the","entire","extension","building","process","should","be","restarted",".","If","the","process","fails","because","there","is","no","way","around","an","algebraic","extension","(","e.g.",",","exp","(","log","(","x",")","\/2",")",")",",","it","will","raise","NotImplementedError","."]
code: def _exp_part(self, exps):
      """
      Try to build an exponential extension.
  
      Returns
      =======
  
      Returns True if there was a new extension, False if there was no new
      extension but it was able to rewrite the given exponentials in terms
      of the existing extension, and None if the entire extension building
      process should be restarted.  If the process fails because there is no
      way around an algebraic extension (e.g., exp(log(x)/2)), it will raise
      NotImplementedError.
      """
      from .prde import is_log_deriv_k_t_radical
  
      new_extension = False
      restart = False
      expargs = [i.exp for i in exps]
      ip = integer_powers(expargs)
      for arg, others in ip:
          # Minimize potential problems with algebraic substitution
          others.sort(key=lambda i: i[1])
  
          arga, argd = frac_in(arg, self.t)
          A = is_log_deriv_k_t_radical(arga, argd, self)
  
          if A is not None:
              ans, u, n, const = A
              # if n is 1 or -1, it's algebraic, but we can handle it
              if n == -1:
                  # This probably will never happen, because
                  # Rational.as_numer_denom() returns the negative term in
                  # the numerator.  But in case that changes, reduce it to
                  # n == 1.
                  n = 1
                  u **= -1
                  const *= -1
                  ans = [(i, -j) for i, j in ans]
  
              if n == 1:
                  # Example: exp(x + x**2) over QQ(x, exp(x), exp(x**2))
                  self.newf = self.newf.xreplace(
                      {
                          exp(arg): exp(const)
                          * Mul(*[u**power for u, power in ans])
                      }
                  )
                  self.newf = self.newf.xreplace(
                      {
                          exp(p * exparg): exp(const * p)
                          * Mul(*[u**power for u, power in ans])
                          for exparg, p in others
                      }
                  )
                  # TODO: Add something to backsubs to put exp(const*p)
                  # back together.
  
                  continue
  
              else:
                  # Bad news: we have an algebraic radical.  But maybe we
                  # could still avoid it by choosing a different extension.
                  # For example, integer_powers() won't handle exp(x/2 + 1)
                  # over QQ(x, exp(x)), but if we pull out the exp(1), it
                  # will.  Or maybe we have exp(x + x**2/2), over
                  # QQ(x, exp(x), exp(x**2)), which is exp(x)*sqrt(exp(x**2)),
                  # but if we use QQ(x, exp(x), exp(x**2/2)), then they will
                  # all work.
                  #
                  # So here is what we do: If there is a non-zero const, pull
                  # it out and retry.  Also, if len(ans) > 1, then rewrite
                  # exp(arg) as the product of exponentials from ans, and
                  # retry that.  If const == 0 and len(ans) == 1, then we
                  # assume that it would have been handled by either
                  # integer_powers() or n == 1 above if it could be handled,
                  # so we give up at that point.  For example, you can never
                  # handle exp(log(x)/2) because it equals sqrt(x).
  
                  if const or len(ans) > 1:
                      rad = Mul(
                          *[term ** (power / n) for term, power in ans]
                      )
                      self.newf = self.newf.xreplace(
                          {
                              exp(p * exparg): exp(const * p) * rad
                              for exparg, p in others
                          }
                      )
                      self.newf = self.newf.xreplace(
                          dict(
                              list(
                                  zip(
                                      reversed(self.T),
                                      reversed(
                                          [
                                              f(self.x)
                                              for f in self.Tfuncs
                                          ]
                                      ),
                                  )
                              )
                          )
                      )
                      restart = True
                      break
                  else:
                      # TODO: give algebraic dependence in error string
                      raise NotImplementedError(
                          "Cannot integrate over "
                          "algebraic extensions."
                      )
  
          else:
              arga, argd = frac_in(arg, self.t)
              darga = argd * derivation(
                  Poly(arga, self.t), self
              ) - arga * derivation(Poly(argd, self.t), self)
              dargd = argd**2
              darga, dargd = darga.cancel(dargd, include=True)
              darg = darga.as_expr() / dargd.as_expr()
              self.t = next(self.ts)
              self.T.append(self.t)
              self.extargs.append(arg)
              self.exts.append("exp")
              self.D.append(
                  darg.as_poly(self.t, expand=False)
                  * Poly(self.t, self.t, expand=False)
              )
              if self.dummy:
                  i = Dummy("i")
              else:
                  i = Symbol("i")
              self.Tfuncs += [Lambda(i, exp(arg.subs(self.x, i)))]
              self.newf = self.newf.xreplace(
                  {exp(exparg): self.t**p for exparg, p in others}
              )
              new_extension = True
  
      if restart:
          return None
      return new_extension
  
imports: from types import GeneratorType
  from functools import reduce
  from sympy.core.function import Lambda
  from sympy.core.mul import Mul
  from sympy.core.intfunc import ilcm
  from sympy.core.numbers import I, oo
  from sympy.core.power import Pow
  from sympy.core.relational import Ne
  from sympy.core.singleton import S
  from sympy.core.sorting import ordered, default_sort_key
  from sympy.core.symbol import Dummy, Symbol
  from sympy.functions.elementary.exponential import log, exp
  from sympy.functions.elementary.hyperbolic import cosh, coth, sinh, tanh
  from sympy.functions.elementary.piecewise import Piecewise
  from sympy.functions.elementary.trigonometric import atan, sin, cos, tan, acot, cot, asin, acos
  from .integrals import integrate, Integral
  from .heurisch import _symbols
  from sympy.polys.polyerrors import DomainError, PolynomialError
  from sympy.polys.polytools import real_roots, cancel, Poly, gcd, reduced
  from sympy.polys.rootoftools import RootSum
  from sympy.utilities.iterables import numbered_symbols
label_desc: Use image node_id 6 for calling the DifferentialExtension obj's underlying member method code with example usage: obj._exp_part(exps) and returns: new_extension, None