| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252 | 
							- from collections import defaultdict
 
- from functools import reduce
 
- from sympy.core import (sympify, Basic, S, Expr, factor_terms,
 
-                         Mul, Add, bottom_up)
 
- from sympy.core.cache import cacheit
 
- from sympy.core.function import (count_ops, _mexpand, FunctionClass, expand,
 
-                                  expand_mul, _coeff_isneg, Derivative)
 
- from sympy.core.numbers import I, Integer, igcd
 
- from sympy.core.sorting import _nodes
 
- from sympy.core.symbol import Dummy, symbols, Wild
 
- from sympy.external.gmpy import SYMPY_INTS
 
- from sympy.functions import sin, cos, exp, cosh, tanh, sinh, tan, cot, coth
 
- from sympy.functions import atan2
 
- from sympy.functions.elementary.hyperbolic import HyperbolicFunction
 
- from sympy.functions.elementary.trigonometric import TrigonometricFunction
 
- from sympy.polys import Poly, factor, cancel, parallel_poly_from_expr
 
- from sympy.polys.domains import ZZ
 
- from sympy.polys.polyerrors import PolificationFailed
 
- from sympy.polys.polytools import groebner
 
- from sympy.simplify.cse_main import cse
 
- from sympy.strategies.core import identity
 
- from sympy.strategies.tree import greedy
 
- from sympy.utilities.iterables import iterable
 
- from sympy.utilities.misc import debug
 
- def trigsimp_groebner(expr, hints=[], quick=False, order="grlex",
 
-                       polynomial=False):
 
-     """
 
-     Simplify trigonometric expressions using a groebner basis algorithm.
 
-     Explanation
 
-     ===========
 
-     This routine takes a fraction involving trigonometric or hyperbolic
 
-     expressions, and tries to simplify it. The primary metric is the
 
-     total degree. Some attempts are made to choose the simplest possible
 
-     expression of the minimal degree, but this is non-rigorous, and also
 
-     very slow (see the ``quick=True`` option).
 
-     If ``polynomial`` is set to True, instead of simplifying numerator and
 
-     denominator together, this function just brings numerator and denominator
 
-     into a canonical form. This is much faster, but has potentially worse
 
-     results. However, if the input is a polynomial, then the result is
 
-     guaranteed to be an equivalent polynomial of minimal degree.
 
-     The most important option is hints. Its entries can be any of the
 
-     following:
 
-     - a natural number
 
-     - a function
 
-     - an iterable of the form (func, var1, var2, ...)
 
-     - anything else, interpreted as a generator
 
-     A number is used to indicate that the search space should be increased.
 
-     A function is used to indicate that said function is likely to occur in a
 
-     simplified expression.
 
-     An iterable is used indicate that func(var1 + var2 + ...) is likely to
 
-     occur in a simplified .
 
-     An additional generator also indicates that it is likely to occur.
 
-     (See examples below).
 
-     This routine carries out various computationally intensive algorithms.
 
-     The option ``quick=True`` can be used to suppress one particularly slow
 
-     step (at the expense of potentially more complicated results, but never at
 
-     the expense of increased total degree).
 
-     Examples
 
-     ========
 
-     >>> from sympy.abc import x, y
 
-     >>> from sympy import sin, tan, cos, sinh, cosh, tanh
 
-     >>> from sympy.simplify.trigsimp import trigsimp_groebner
 
-     Suppose you want to simplify ``sin(x)*cos(x)``. Naively, nothing happens:
 
-     >>> ex = sin(x)*cos(x)
 
-     >>> trigsimp_groebner(ex)
 
-     sin(x)*cos(x)
 
-     This is because ``trigsimp_groebner`` only looks for a simplification
 
-     involving just ``sin(x)`` and ``cos(x)``. You can tell it to also try
 
-     ``2*x`` by passing ``hints=[2]``:
 
-     >>> trigsimp_groebner(ex, hints=[2])
 
-     sin(2*x)/2
 
-     >>> trigsimp_groebner(sin(x)**2 - cos(x)**2, hints=[2])
 
-     -cos(2*x)
 
-     Increasing the search space this way can quickly become expensive. A much
 
-     faster way is to give a specific expression that is likely to occur:
 
-     >>> trigsimp_groebner(ex, hints=[sin(2*x)])
 
-     sin(2*x)/2
 
-     Hyperbolic expressions are similarly supported:
 
-     >>> trigsimp_groebner(sinh(2*x)/sinh(x))
 
-     2*cosh(x)
 
-     Note how no hints had to be passed, since the expression already involved
 
-     ``2*x``.
 
-     The tangent function is also supported. You can either pass ``tan`` in the
 
-     hints, to indicate that tan should be tried whenever cosine or sine are,
 
-     or you can pass a specific generator:
 
-     >>> trigsimp_groebner(sin(x)/cos(x), hints=[tan])
 
-     tan(x)
 
-     >>> trigsimp_groebner(sinh(x)/cosh(x), hints=[tanh(x)])
 
-     tanh(x)
 
-     Finally, you can use the iterable form to suggest that angle sum formulae
 
-     should be tried:
 
-     >>> ex = (tan(x) + tan(y))/(1 - tan(x)*tan(y))
 
-     >>> trigsimp_groebner(ex, hints=[(tan, x, y)])
 
-     tan(x + y)
 
-     """
 
-     # TODO
 
-     #  - preprocess by replacing everything by funcs we can handle
 
-     # - optionally use cot instead of tan
 
-     # - more intelligent hinting.
 
-     #     For example, if the ideal is small, and we have sin(x), sin(y),
 
-     #     add sin(x + y) automatically... ?
 
-     # - algebraic numbers ...
 
-     # - expressions of lowest degree are not distinguished properly
 
-     #   e.g. 1 - sin(x)**2
 
-     # - we could try to order the generators intelligently, so as to influence
 
-     #   which monomials appear in the quotient basis
 
-     # THEORY
 
-     # ------
 
-     # Ratsimpmodprime above can be used to "simplify" a rational function
 
-     # modulo a prime ideal. "Simplify" mainly means finding an equivalent
 
-     # expression of lower total degree.
 
-     #
 
-     # We intend to use this to simplify trigonometric functions. To do that,
 
-     # we need to decide (a) which ring to use, and (b) modulo which ideal to
 
-     # simplify. In practice, (a) means settling on a list of "generators"
 
-     # a, b, c, ..., such that the fraction we want to simplify is a rational
 
-     # function in a, b, c, ..., with coefficients in ZZ (integers).
 
-     # (2) means that we have to decide what relations to impose on the
 
-     # generators. There are two practical problems:
 
-     #   (1) The ideal has to be *prime* (a technical term).
 
-     #   (2) The relations have to be polynomials in the generators.
 
-     #
 
-     # We typically have two kinds of generators:
 
-     # - trigonometric expressions, like sin(x), cos(5*x), etc
 
-     # - "everything else", like gamma(x), pi, etc.
 
-     #
 
-     # Since this function is trigsimp, we will concentrate on what to do with
 
-     # trigonometric expressions. We can also simplify hyperbolic expressions,
 
-     # but the extensions should be clear.
 
-     #
 
-     # One crucial point is that all *other* generators really should behave
 
-     # like indeterminates. In particular if (say) "I" is one of them, then
 
-     # in fact I**2 + 1 = 0 and we may and will compute non-sensical
 
-     # expressions. However, we can work with a dummy and add the relation
 
-     # I**2 + 1 = 0 to our ideal, then substitute back in the end.
 
-     #
 
-     # Now regarding trigonometric generators. We split them into groups,
 
-     # according to the argument of the trigonometric functions. We want to
 
-     # organise this in such a way that most trigonometric identities apply in
 
-     # the same group. For example, given sin(x), cos(2*x) and cos(y), we would
 
-     # group as [sin(x), cos(2*x)] and [cos(y)].
 
-     #
 
-     # Our prime ideal will be built in three steps:
 
-     # (1) For each group, compute a "geometrically prime" ideal of relations.
 
-     #     Geometrically prime means that it generates a prime ideal in
 
-     #     CC[gens], not just ZZ[gens].
 
-     # (2) Take the union of all the generators of the ideals for all groups.
 
-     #     By the geometric primality condition, this is still prime.
 
-     # (3) Add further inter-group relations which preserve primality.
 
-     #
 
-     # Step (1) works as follows. We will isolate common factors in the
 
-     # argument, so that all our generators are of the form sin(n*x), cos(n*x)
 
-     # or tan(n*x), with n an integer. Suppose first there are no tan terms.
 
-     # The ideal [sin(x)**2 + cos(x)**2 - 1] is geometrically prime, since
 
-     # X**2 + Y**2 - 1 is irreducible over CC.
 
-     # Now, if we have a generator sin(n*x), than we can, using trig identities,
 
-     # express sin(n*x) as a polynomial in sin(x) and cos(x). We can add this
 
-     # relation to the ideal, preserving geometric primality, since the quotient
 
-     # ring is unchanged.
 
-     # Thus we have treated all sin and cos terms.
 
-     # For tan(n*x), we add a relation tan(n*x)*cos(n*x) - sin(n*x) = 0.
 
-     # (This requires of course that we already have relations for cos(n*x) and
 
-     # sin(n*x).) It is not obvious, but it seems that this preserves geometric
 
-     # primality.
 
-     # XXX A real proof would be nice. HELP!
 
-     #     Sketch that <S**2 + C**2 - 1, C*T - S> is a prime ideal of
 
-     #     CC[S, C, T]:
 
-     #     - it suffices to show that the projective closure in CP**3 is
 
-     #       irreducible
 
-     #     - using the half-angle substitutions, we can express sin(x), tan(x),
 
-     #       cos(x) as rational functions in tan(x/2)
 
-     #     - from this, we get a rational map from CP**1 to our curve
 
-     #     - this is a morphism, hence the curve is prime
 
-     #
 
-     # Step (2) is trivial.
 
-     #
 
-     # Step (3) works by adding selected relations of the form
 
-     # sin(x + y) - sin(x)*cos(y) - sin(y)*cos(x), etc. Geometric primality is
 
-     # preserved by the same argument as before.
 
-     def parse_hints(hints):
 
-         """Split hints into (n, funcs, iterables, gens)."""
 
-         n = 1
 
-         funcs, iterables, gens = [], [], []
 
-         for e in hints:
 
-             if isinstance(e, (SYMPY_INTS, Integer)):
 
-                 n = e
 
-             elif isinstance(e, FunctionClass):
 
-                 funcs.append(e)
 
-             elif iterable(e):
 
-                 iterables.append((e[0], e[1:]))
 
-                 # XXX sin(x+2y)?
 
-                 # Note: we go through polys so e.g.
 
-                 # sin(-x) -> -sin(x) -> sin(x)
 
-                 gens.extend(parallel_poly_from_expr(
 
-                     [e[0](x) for x in e[1:]] + [e[0](Add(*e[1:]))])[1].gens)
 
-             else:
 
-                 gens.append(e)
 
-         return n, funcs, iterables, gens
 
-     def build_ideal(x, terms):
 
-         """
 
-         Build generators for our ideal. ``Terms`` is an iterable with elements of
 
-         the form (fn, coeff), indicating that we have a generator fn(coeff*x).
 
-         If any of the terms is trigonometric, sin(x) and cos(x) are guaranteed
 
-         to appear in terms. Similarly for hyperbolic functions. For tan(n*x),
 
-         sin(n*x) and cos(n*x) are guaranteed.
 
-         """
 
-         I = []
 
-         y = Dummy('y')
 
-         for fn, coeff in terms:
 
-             for c, s, t, rel in (
 
-                     [cos, sin, tan, cos(x)**2 + sin(x)**2 - 1],
 
-                     [cosh, sinh, tanh, cosh(x)**2 - sinh(x)**2 - 1]):
 
-                 if coeff == 1 and fn in [c, s]:
 
-                     I.append(rel)
 
-                 elif fn == t:
 
-                     I.append(t(coeff*x)*c(coeff*x) - s(coeff*x))
 
-                 elif fn in [c, s]:
 
-                     cn = fn(coeff*y).expand(trig=True).subs(y, x)
 
-                     I.append(fn(coeff*x) - cn)
 
-         return list(set(I))
 
-     def analyse_gens(gens, hints):
 
-         """
 
-         Analyse the generators ``gens``, using the hints ``hints``.
 
-         The meaning of ``hints`` is described in the main docstring.
 
-         Return a new list of generators, and also the ideal we should
 
-         work with.
 
-         """
 
-         # First parse the hints
 
-         n, funcs, iterables, extragens = parse_hints(hints)
 
-         debug('n=%s   funcs: %s   iterables: %s    extragens: %s',
 
-               (funcs, iterables, extragens))
 
-         # We just add the extragens to gens and analyse them as before
 
-         gens = list(gens)
 
-         gens.extend(extragens)
 
-         # remove duplicates
 
-         funcs = list(set(funcs))
 
-         iterables = list(set(iterables))
 
-         gens = list(set(gens))
 
-         # all the functions we can do anything with
 
-         allfuncs = {sin, cos, tan, sinh, cosh, tanh}
 
-         # sin(3*x) -> ((3, x), sin)
 
-         trigterms = [(g.args[0].as_coeff_mul(), g.func) for g in gens
 
-                      if g.func in allfuncs]
 
-         # Our list of new generators - start with anything that we cannot
 
-         # work with (i.e. is not a trigonometric term)
 
-         freegens = [g for g in gens if g.func not in allfuncs]
 
-         newgens = []
 
-         trigdict = {}
 
-         for (coeff, var), fn in trigterms:
 
-             trigdict.setdefault(var, []).append((coeff, fn))
 
-         res = [] # the ideal
 
-         for key, val in trigdict.items():
 
-             # We have now assembeled a dictionary. Its keys are common
 
-             # arguments in trigonometric expressions, and values are lists of
 
-             # pairs (fn, coeff). x0, (fn, coeff) in trigdict means that we
 
-             # need to deal with fn(coeff*x0). We take the rational gcd of the
 
-             # coeffs, call it ``gcd``. We then use x = x0/gcd as "base symbol",
 
-             # all other arguments are integral multiples thereof.
 
-             # We will build an ideal which works with sin(x), cos(x).
 
-             # If hint tan is provided, also work with tan(x). Moreover, if
 
-             # n > 1, also work with sin(k*x) for k <= n, and similarly for cos
 
-             # (and tan if the hint is provided). Finally, any generators which
 
-             # the ideal does not work with but we need to accommodate (either
 
-             # because it was in expr or because it was provided as a hint)
 
-             # we also build into the ideal.
 
-             # This selection process is expressed in the list ``terms``.
 
-             # build_ideal then generates the actual relations in our ideal,
 
-             # from this list.
 
-             fns = [x[1] for x in val]
 
-             val = [x[0] for x in val]
 
-             gcd = reduce(igcd, val)
 
-             terms = [(fn, v/gcd) for (fn, v) in zip(fns, val)]
 
-             fs = set(funcs + fns)
 
-             for c, s, t in ([cos, sin, tan], [cosh, sinh, tanh]):
 
-                 if any(x in fs for x in (c, s, t)):
 
-                     fs.add(c)
 
-                     fs.add(s)
 
-             for fn in fs:
 
-                 for k in range(1, n + 1):
 
-                     terms.append((fn, k))
 
-             extra = []
 
-             for fn, v in terms:
 
-                 if fn == tan:
 
-                     extra.append((sin, v))
 
-                     extra.append((cos, v))
 
-                 if fn in [sin, cos] and tan in fs:
 
-                     extra.append((tan, v))
 
-                 if fn == tanh:
 
-                     extra.append((sinh, v))
 
-                     extra.append((cosh, v))
 
-                 if fn in [sinh, cosh] and tanh in fs:
 
-                     extra.append((tanh, v))
 
-             terms.extend(extra)
 
-             x = gcd*Mul(*key)
 
-             r = build_ideal(x, terms)
 
-             res.extend(r)
 
-             newgens.extend({fn(v*x) for fn, v in terms})
 
-         # Add generators for compound expressions from iterables
 
-         for fn, args in iterables:
 
-             if fn == tan:
 
-                 # Tan expressions are recovered from sin and cos.
 
-                 iterables.extend([(sin, args), (cos, args)])
 
-             elif fn == tanh:
 
-                 # Tanh expressions are recovered from sihn and cosh.
 
-                 iterables.extend([(sinh, args), (cosh, args)])
 
-             else:
 
-                 dummys = symbols('d:%i' % len(args), cls=Dummy)
 
-                 expr = fn( Add(*dummys)).expand(trig=True).subs(list(zip(dummys, args)))
 
-                 res.append(fn(Add(*args)) - expr)
 
-         if myI in gens:
 
-             res.append(myI**2 + 1)
 
-             freegens.remove(myI)
 
-             newgens.append(myI)
 
-         return res, freegens, newgens
 
-     myI = Dummy('I')
 
-     expr = expr.subs(S.ImaginaryUnit, myI)
 
-     subs = [(myI, S.ImaginaryUnit)]
 
-     num, denom = cancel(expr).as_numer_denom()
 
-     try:
 
-         (pnum, pdenom), opt = parallel_poly_from_expr([num, denom])
 
-     except PolificationFailed:
 
-         return expr
 
-     debug('initial gens:', opt.gens)
 
-     ideal, freegens, gens = analyse_gens(opt.gens, hints)
 
-     debug('ideal:', ideal)
 
-     debug('new gens:', gens, " -- len", len(gens))
 
-     debug('free gens:', freegens, " -- len", len(gens))
 
-     # NOTE we force the domain to be ZZ to stop polys from injecting generators
 
-     #      (which is usually a sign of a bug in the way we build the ideal)
 
-     if not gens:
 
-         return expr
 
-     G = groebner(ideal, order=order, gens=gens, domain=ZZ)
 
-     debug('groebner basis:', list(G), " -- len", len(G))
 
-     # If our fraction is a polynomial in the free generators, simplify all
 
-     # coefficients separately:
 
-     from sympy.simplify.ratsimp import ratsimpmodprime
 
-     if freegens and pdenom.has_only_gens(*set(gens).intersection(pdenom.gens)):
 
-         num = Poly(num, gens=gens+freegens).eject(*gens)
 
-         res = []
 
-         for monom, coeff in num.terms():
 
-             ourgens = set(parallel_poly_from_expr([coeff, denom])[1].gens)
 
-             # We compute the transitive closure of all generators that can
 
-             # be reached from our generators through relations in the ideal.
 
-             changed = True
 
-             while changed:
 
-                 changed = False
 
-                 for p in ideal:
 
-                     p = Poly(p)
 
-                     if not ourgens.issuperset(p.gens) and \
 
-                        not p.has_only_gens(*set(p.gens).difference(ourgens)):
 
-                         changed = True
 
-                         ourgens.update(p.exclude().gens)
 
-             # NOTE preserve order!
 
-             realgens = [x for x in gens if x in ourgens]
 
-             # The generators of the ideal have now been (implicitly) split
 
-             # into two groups: those involving ourgens and those that don't.
 
-             # Since we took the transitive closure above, these two groups
 
-             # live in subgrings generated by a *disjoint* set of variables.
 
-             # Any sensible groebner basis algorithm will preserve this disjoint
 
-             # structure (i.e. the elements of the groebner basis can be split
 
-             # similarly), and and the two subsets of the groebner basis then
 
-             # form groebner bases by themselves. (For the smaller generating
 
-             # sets, of course.)
 
-             ourG = [g.as_expr() for g in G.polys if
 
-                     g.has_only_gens(*ourgens.intersection(g.gens))]
 
-             res.append(Mul(*[a**b for a, b in zip(freegens, monom)]) * \
 
-                        ratsimpmodprime(coeff/denom, ourG, order=order,
 
-                                        gens=realgens, quick=quick, domain=ZZ,
 
-                                        polynomial=polynomial).subs(subs))
 
-         return Add(*res)
 
-         # NOTE The following is simpler and has less assumptions on the
 
-         #      groebner basis algorithm. If the above turns out to be broken,
 
-         #      use this.
 
-         return Add(*[Mul(*[a**b for a, b in zip(freegens, monom)]) * \
 
-                      ratsimpmodprime(coeff/denom, list(G), order=order,
 
-                                      gens=gens, quick=quick, domain=ZZ)
 
-                      for monom, coeff in num.terms()])
 
-     else:
 
-         return ratsimpmodprime(
 
-             expr, list(G), order=order, gens=freegens+gens,
 
-             quick=quick, domain=ZZ, polynomial=polynomial).subs(subs)
 
- _trigs = (TrigonometricFunction, HyperbolicFunction)
 
- def _trigsimp_inverse(rv):
 
-     def check_args(x, y):
 
-         try:
 
-             return x.args[0] == y.args[0]
 
-         except IndexError:
 
-             return False
 
-     def f(rv):
 
-         # for simple functions
 
-         g = getattr(rv, 'inverse', None)
 
-         if (g is not None and isinstance(rv.args[0], g()) and
 
-                 isinstance(g()(1), TrigonometricFunction)):
 
-             return rv.args[0].args[0]
 
-         # for atan2 simplifications, harder because atan2 has 2 args
 
-         if isinstance(rv, atan2):
 
-             y, x = rv.args
 
-             if _coeff_isneg(y):
 
-                 return -f(atan2(-y, x))
 
-             elif _coeff_isneg(x):
 
-                 return S.Pi - f(atan2(y, -x))
 
-             if check_args(x, y):
 
-                 if isinstance(y, sin) and isinstance(x, cos):
 
-                     return x.args[0]
 
-                 if isinstance(y, cos) and isinstance(x, sin):
 
-                     return S.Pi / 2 - x.args[0]
 
-         return rv
 
-     return bottom_up(rv, f)
 
- def trigsimp(expr, inverse=False, **opts):
 
-     """Returns a reduced expression by using known trig identities.
 
-     Parameters
 
-     ==========
 
-     inverse : bool, optional
 
-         If ``inverse=True``, it will be assumed that a composition of inverse
 
-         functions, such as sin and asin, can be cancelled in any order.
 
-         For example, ``asin(sin(x))`` will yield ``x`` without checking whether
 
-         x belongs to the set where this relation is true. The default is False.
 
-         Default : True
 
-     method : string, optional
 
-         Specifies the method to use. Valid choices are:
 
-         - ``'matching'``, default
 
-         - ``'groebner'``
 
-         - ``'combined'``
 
-         - ``'fu'``
 
-         - ``'old'``
 
-         If ``'matching'``, simplify the expression recursively by targeting
 
-         common patterns. If ``'groebner'``, apply an experimental groebner
 
-         basis algorithm. In this case further options are forwarded to
 
-         ``trigsimp_groebner``, please refer to
 
-         its docstring. If ``'combined'``, it first runs the groebner basis
 
-         algorithm with small default parameters, then runs the ``'matching'``
 
-         algorithm. If ``'fu'``, run the collection of trigonometric
 
-         transformations described by Fu, et al. (see the
 
-         :py:func:`~sympy.simplify.fu.fu` docstring). If ``'old'``, the original
 
-         SymPy trig simplification function is run.
 
-     opts :
 
-         Optional keyword arguments passed to the method. See each method's
 
-         function docstring for details.
 
-     Examples
 
-     ========
 
-     >>> from sympy import trigsimp, sin, cos, log
 
-     >>> from sympy.abc import x
 
-     >>> e = 2*sin(x)**2 + 2*cos(x)**2
 
-     >>> trigsimp(e)
 
-     2
 
-     Simplification occurs wherever trigonometric functions are located.
 
-     >>> trigsimp(log(e))
 
-     log(2)
 
-     Using ``method='groebner'`` (or ``method='combined'``) might lead to
 
-     greater simplification.
 
-     The old trigsimp routine can be accessed as with method ``method='old'``.
 
-     >>> from sympy import coth, tanh
 
-     >>> t = 3*tanh(x)**7 - 2/coth(x)**7
 
-     >>> trigsimp(t, method='old') == t
 
-     True
 
-     >>> trigsimp(t)
 
-     tanh(x)**7
 
-     """
 
-     from sympy.simplify.fu import fu
 
-     expr = sympify(expr)
 
-     _eval_trigsimp = getattr(expr, '_eval_trigsimp', None)
 
-     if _eval_trigsimp is not None:
 
-         return _eval_trigsimp(**opts)
 
-     old = opts.pop('old', False)
 
-     if not old:
 
-         opts.pop('deep', None)
 
-         opts.pop('recursive', None)
 
-         method = opts.pop('method', 'matching')
 
-     else:
 
-         method = 'old'
 
-     def groebnersimp(ex, **opts):
 
-         def traverse(e):
 
-             if e.is_Atom:
 
-                 return e
 
-             args = [traverse(x) for x in e.args]
 
-             if e.is_Function or e.is_Pow:
 
-                 args = [trigsimp_groebner(x, **opts) for x in args]
 
-             return e.func(*args)
 
-         new = traverse(ex)
 
-         if not isinstance(new, Expr):
 
-             return new
 
-         return trigsimp_groebner(new, **opts)
 
-     trigsimpfunc = {
 
-         'fu': (lambda x: fu(x, **opts)),
 
-         'matching': (lambda x: futrig(x)),
 
-         'groebner': (lambda x: groebnersimp(x, **opts)),
 
-         'combined': (lambda x: futrig(groebnersimp(x,
 
-                                polynomial=True, hints=[2, tan]))),
 
-         'old': lambda x: trigsimp_old(x, **opts),
 
-                    }[method]
 
-     expr_simplified = trigsimpfunc(expr)
 
-     if inverse:
 
-         expr_simplified = _trigsimp_inverse(expr_simplified)
 
-     return expr_simplified
 
- def exptrigsimp(expr):
 
-     """
 
-     Simplifies exponential / trigonometric / hyperbolic functions.
 
-     Examples
 
-     ========
 
-     >>> from sympy import exptrigsimp, exp, cosh, sinh
 
-     >>> from sympy.abc import z
 
-     >>> exptrigsimp(exp(z) + exp(-z))
 
-     2*cosh(z)
 
-     >>> exptrigsimp(cosh(z) - sinh(z))
 
-     exp(-z)
 
-     """
 
-     from sympy.simplify.fu import hyper_as_trig, TR2i
 
-     def exp_trig(e):
 
-         # select the better of e, and e rewritten in terms of exp or trig
 
-         # functions
 
-         choices = [e]
 
-         if e.has(*_trigs):
 
-             choices.append(e.rewrite(exp))
 
-         choices.append(e.rewrite(cos))
 
-         return min(*choices, key=count_ops)
 
-     newexpr = bottom_up(expr, exp_trig)
 
-     def f(rv):
 
-         if not rv.is_Mul:
 
-             return rv
 
-         commutative_part, noncommutative_part = rv.args_cnc()
 
-         # Since as_powers_dict loses order information,
 
-         # if there is more than one noncommutative factor,
 
-         # it should only be used to simplify the commutative part.
 
-         if (len(noncommutative_part) > 1):
 
-             return f(Mul(*commutative_part))*Mul(*noncommutative_part)
 
-         rvd = rv.as_powers_dict()
 
-         newd = rvd.copy()
 
-         def signlog(expr, sign=S.One):
 
-             if expr is S.Exp1:
 
-                 return sign, S.One
 
-             elif isinstance(expr, exp) or (expr.is_Pow and expr.base == S.Exp1):
 
-                 return sign, expr.exp
 
-             elif sign is S.One:
 
-                 return signlog(-expr, sign=-S.One)
 
-             else:
 
-                 return None, None
 
-         ee = rvd[S.Exp1]
 
-         for k in rvd:
 
-             if k.is_Add and len(k.args) == 2:
 
-                 # k == c*(1 + sign*E**x)
 
-                 c = k.args[0]
 
-                 sign, x = signlog(k.args[1]/c)
 
-                 if not x:
 
-                     continue
 
-                 m = rvd[k]
 
-                 newd[k] -= m
 
-                 if ee == -x*m/2:
 
-                     # sinh and cosh
 
-                     newd[S.Exp1] -= ee
 
-                     ee = 0
 
-                     if sign == 1:
 
-                         newd[2*c*cosh(x/2)] += m
 
-                     else:
 
-                         newd[-2*c*sinh(x/2)] += m
 
-                 elif newd[1 - sign*S.Exp1**x] == -m:
 
-                     # tanh
 
-                     del newd[1 - sign*S.Exp1**x]
 
-                     if sign == 1:
 
-                         newd[-c/tanh(x/2)] += m
 
-                     else:
 
-                         newd[-c*tanh(x/2)] += m
 
-                 else:
 
-                     newd[1 + sign*S.Exp1**x] += m
 
-                     newd[c] += m
 
-         return Mul(*[k**newd[k] for k in newd])
 
-     newexpr = bottom_up(newexpr, f)
 
-     # sin/cos and sinh/cosh ratios to tan and tanh, respectively
 
-     if newexpr.has(HyperbolicFunction):
 
-         e, f = hyper_as_trig(newexpr)
 
-         newexpr = f(TR2i(e))
 
-     if newexpr.has(TrigonometricFunction):
 
-         newexpr = TR2i(newexpr)
 
-     # can we ever generate an I where there was none previously?
 
-     if not (newexpr.has(I) and not expr.has(I)):
 
-         expr = newexpr
 
-     return expr
 
- #-------------------- the old trigsimp routines ---------------------
 
- def trigsimp_old(expr, *, first=True, **opts):
 
-     """
 
-     Reduces expression by using known trig identities.
 
-     Notes
 
-     =====
 
-     deep:
 
-     - Apply trigsimp inside all objects with arguments
 
-     recursive:
 
-     - Use common subexpression elimination (cse()) and apply
 
-     trigsimp recursively (this is quite expensive if the
 
-     expression is large)
 
-     method:
 
-     - Determine the method to use. Valid choices are 'matching' (default),
 
-     'groebner', 'combined', 'fu' and 'futrig'. If 'matching', simplify the
 
-     expression recursively by pattern matching. If 'groebner', apply an
 
-     experimental groebner basis algorithm. In this case further options
 
-     are forwarded to ``trigsimp_groebner``, please refer to its docstring.
 
-     If 'combined', first run the groebner basis algorithm with small
 
-     default parameters, then run the 'matching' algorithm. 'fu' runs the
 
-     collection of trigonometric transformations described by Fu, et al.
 
-     (see the `fu` docstring) while `futrig` runs a subset of Fu-transforms
 
-     that mimic the behavior of `trigsimp`.
 
-     compare:
 
-     - show input and output from `trigsimp` and `futrig` when different,
 
-     but returns the `trigsimp` value.
 
-     Examples
 
-     ========
 
-     >>> from sympy import trigsimp, sin, cos, log, cot
 
-     >>> from sympy.abc import x
 
-     >>> e = 2*sin(x)**2 + 2*cos(x)**2
 
-     >>> trigsimp(e, old=True)
 
-     2
 
-     >>> trigsimp(log(e), old=True)
 
-     log(2*sin(x)**2 + 2*cos(x)**2)
 
-     >>> trigsimp(log(e), deep=True, old=True)
 
-     log(2)
 
-     Using `method="groebner"` (or `"combined"`) can sometimes lead to a lot
 
-     more simplification:
 
-     >>> e = (-sin(x) + 1)/cos(x) + cos(x)/(-sin(x) + 1)
 
-     >>> trigsimp(e, old=True)
 
-     (1 - sin(x))/cos(x) + cos(x)/(1 - sin(x))
 
-     >>> trigsimp(e, method="groebner", old=True)
 
-     2/cos(x)
 
-     >>> trigsimp(1/cot(x)**2, compare=True, old=True)
 
-           futrig: tan(x)**2
 
-     cot(x)**(-2)
 
-     """
 
-     old = expr
 
-     if first:
 
-         if not expr.has(*_trigs):
 
-             return expr
 
-         trigsyms = set().union(*[t.free_symbols for t in expr.atoms(*_trigs)])
 
-         if len(trigsyms) > 1:
 
-             from sympy.simplify.simplify import separatevars
 
-             d = separatevars(expr)
 
-             if d.is_Mul:
 
-                 d = separatevars(d, dict=True) or d
 
-             if isinstance(d, dict):
 
-                 expr = 1
 
-                 for k, v in d.items():
 
-                     # remove hollow factoring
 
-                     was = v
 
-                     v = expand_mul(v)
 
-                     opts['first'] = False
 
-                     vnew = trigsimp(v, **opts)
 
-                     if vnew == v:
 
-                         vnew = was
 
-                     expr *= vnew
 
-                 old = expr
 
-             else:
 
-                 if d.is_Add:
 
-                     for s in trigsyms:
 
-                         r, e = expr.as_independent(s)
 
-                         if r:
 
-                             opts['first'] = False
 
-                             expr = r + trigsimp(e, **opts)
 
-                             if not expr.is_Add:
 
-                                 break
 
-                     old = expr
 
-     recursive = opts.pop('recursive', False)
 
-     deep = opts.pop('deep', False)
 
-     method = opts.pop('method', 'matching')
 
-     def groebnersimp(ex, deep, **opts):
 
-         def traverse(e):
 
-             if e.is_Atom:
 
-                 return e
 
-             args = [traverse(x) for x in e.args]
 
-             if e.is_Function or e.is_Pow:
 
-                 args = [trigsimp_groebner(x, **opts) for x in args]
 
-             return e.func(*args)
 
-         if deep:
 
-             ex = traverse(ex)
 
-         return trigsimp_groebner(ex, **opts)
 
-     trigsimpfunc = {
 
-         'matching': (lambda x, d: _trigsimp(x, d)),
 
-         'groebner': (lambda x, d: groebnersimp(x, d, **opts)),
 
-         'combined': (lambda x, d: _trigsimp(groebnersimp(x,
 
-                                        d, polynomial=True, hints=[2, tan]),
 
-                                    d))
 
-                    }[method]
 
-     if recursive:
 
-         w, g = cse(expr)
 
-         g = trigsimpfunc(g[0], deep)
 
-         for sub in reversed(w):
 
-             g = g.subs(sub[0], sub[1])
 
-             g = trigsimpfunc(g, deep)
 
-         result = g
 
-     else:
 
-         result = trigsimpfunc(expr, deep)
 
-     if opts.get('compare', False):
 
-         f = futrig(old)
 
-         if f != result:
 
-             print('\tfutrig:', f)
 
-     return result
 
- def _dotrig(a, b):
 
-     """Helper to tell whether ``a`` and ``b`` have the same sorts
 
-     of symbols in them -- no need to test hyperbolic patterns against
 
-     expressions that have no hyperbolics in them."""
 
-     return a.func == b.func and (
 
-         a.has(TrigonometricFunction) and b.has(TrigonometricFunction) or
 
-         a.has(HyperbolicFunction) and b.has(HyperbolicFunction))
 
- _trigpat = None
 
- def _trigpats():
 
-     global _trigpat
 
-     a, b, c = symbols('a b c', cls=Wild)
 
-     d = Wild('d', commutative=False)
 
-     # for the simplifications like sinh/cosh -> tanh:
 
-     # DO NOT REORDER THE FIRST 14 since these are assumed to be in this
 
-     # order in _match_div_rewrite.
 
-     matchers_division = (
 
-         (a*sin(b)**c/cos(b)**c, a*tan(b)**c, sin(b), cos(b)),
 
-         (a*tan(b)**c*cos(b)**c, a*sin(b)**c, sin(b), cos(b)),
 
-         (a*cot(b)**c*sin(b)**c, a*cos(b)**c, sin(b), cos(b)),
 
-         (a*tan(b)**c/sin(b)**c, a/cos(b)**c, sin(b), cos(b)),
 
-         (a*cot(b)**c/cos(b)**c, a/sin(b)**c, sin(b), cos(b)),
 
-         (a*cot(b)**c*tan(b)**c, a, sin(b), cos(b)),
 
-         (a*(cos(b) + 1)**c*(cos(b) - 1)**c,
 
-             a*(-sin(b)**2)**c, cos(b) + 1, cos(b) - 1),
 
-         (a*(sin(b) + 1)**c*(sin(b) - 1)**c,
 
-             a*(-cos(b)**2)**c, sin(b) + 1, sin(b) - 1),
 
-         (a*sinh(b)**c/cosh(b)**c, a*tanh(b)**c, S.One, S.One),
 
-         (a*tanh(b)**c*cosh(b)**c, a*sinh(b)**c, S.One, S.One),
 
-         (a*coth(b)**c*sinh(b)**c, a*cosh(b)**c, S.One, S.One),
 
-         (a*tanh(b)**c/sinh(b)**c, a/cosh(b)**c, S.One, S.One),
 
-         (a*coth(b)**c/cosh(b)**c, a/sinh(b)**c, S.One, S.One),
 
-         (a*coth(b)**c*tanh(b)**c, a, S.One, S.One),
 
-         (c*(tanh(a) + tanh(b))/(1 + tanh(a)*tanh(b)),
 
-             tanh(a + b)*c, S.One, S.One),
 
-     )
 
-     matchers_add = (
 
-         (c*sin(a)*cos(b) + c*cos(a)*sin(b) + d, sin(a + b)*c + d),
 
-         (c*cos(a)*cos(b) - c*sin(a)*sin(b) + d, cos(a + b)*c + d),
 
-         (c*sin(a)*cos(b) - c*cos(a)*sin(b) + d, sin(a - b)*c + d),
 
-         (c*cos(a)*cos(b) + c*sin(a)*sin(b) + d, cos(a - b)*c + d),
 
-         (c*sinh(a)*cosh(b) + c*sinh(b)*cosh(a) + d, sinh(a + b)*c + d),
 
-         (c*cosh(a)*cosh(b) + c*sinh(a)*sinh(b) + d, cosh(a + b)*c + d),
 
-     )
 
-     # for cos(x)**2 + sin(x)**2 -> 1
 
-     matchers_identity = (
 
-         (a*sin(b)**2, a - a*cos(b)**2),
 
-         (a*tan(b)**2, a*(1/cos(b))**2 - a),
 
-         (a*cot(b)**2, a*(1/sin(b))**2 - a),
 
-         (a*sin(b + c), a*(sin(b)*cos(c) + sin(c)*cos(b))),
 
-         (a*cos(b + c), a*(cos(b)*cos(c) - sin(b)*sin(c))),
 
-         (a*tan(b + c), a*((tan(b) + tan(c))/(1 - tan(b)*tan(c)))),
 
-         (a*sinh(b)**2, a*cosh(b)**2 - a),
 
-         (a*tanh(b)**2, a - a*(1/cosh(b))**2),
 
-         (a*coth(b)**2, a + a*(1/sinh(b))**2),
 
-         (a*sinh(b + c), a*(sinh(b)*cosh(c) + sinh(c)*cosh(b))),
 
-         (a*cosh(b + c), a*(cosh(b)*cosh(c) + sinh(b)*sinh(c))),
 
-         (a*tanh(b + c), a*((tanh(b) + tanh(c))/(1 + tanh(b)*tanh(c)))),
 
-     )
 
-     # Reduce any lingering artifacts, such as sin(x)**2 changing
 
-     # to 1-cos(x)**2 when sin(x)**2 was "simpler"
 
-     artifacts = (
 
-         (a - a*cos(b)**2 + c, a*sin(b)**2 + c, cos),
 
-         (a - a*(1/cos(b))**2 + c, -a*tan(b)**2 + c, cos),
 
-         (a - a*(1/sin(b))**2 + c, -a*cot(b)**2 + c, sin),
 
-         (a - a*cosh(b)**2 + c, -a*sinh(b)**2 + c, cosh),
 
-         (a - a*(1/cosh(b))**2 + c, a*tanh(b)**2 + c, cosh),
 
-         (a + a*(1/sinh(b))**2 + c, a*coth(b)**2 + c, sinh),
 
-         # same as above but with noncommutative prefactor
 
-         (a*d - a*d*cos(b)**2 + c, a*d*sin(b)**2 + c, cos),
 
-         (a*d - a*d*(1/cos(b))**2 + c, -a*d*tan(b)**2 + c, cos),
 
-         (a*d - a*d*(1/sin(b))**2 + c, -a*d*cot(b)**2 + c, sin),
 
-         (a*d - a*d*cosh(b)**2 + c, -a*d*sinh(b)**2 + c, cosh),
 
-         (a*d - a*d*(1/cosh(b))**2 + c, a*d*tanh(b)**2 + c, cosh),
 
-         (a*d + a*d*(1/sinh(b))**2 + c, a*d*coth(b)**2 + c, sinh),
 
-     )
 
-     _trigpat = (a, b, c, d, matchers_division, matchers_add,
 
-         matchers_identity, artifacts)
 
-     return _trigpat
 
- def _replace_mul_fpowxgpow(expr, f, g, rexp, h, rexph):
 
-     """Helper for _match_div_rewrite.
 
-     Replace f(b_)**c_*g(b_)**(rexp(c_)) with h(b)**rexph(c) if f(b_)
 
-     and g(b_) are both positive or if c_ is an integer.
 
-     """
 
-     # assert expr.is_Mul and expr.is_commutative and f != g
 
-     fargs = defaultdict(int)
 
-     gargs = defaultdict(int)
 
-     args = []
 
-     for x in expr.args:
 
-         if x.is_Pow or x.func in (f, g):
 
-             b, e = x.as_base_exp()
 
-             if b.is_positive or e.is_integer:
 
-                 if b.func == f:
 
-                     fargs[b.args[0]] += e
 
-                     continue
 
-                 elif b.func == g:
 
-                     gargs[b.args[0]] += e
 
-                     continue
 
-         args.append(x)
 
-     common = set(fargs) & set(gargs)
 
-     hit = False
 
-     while common:
 
-         key = common.pop()
 
-         fe = fargs.pop(key)
 
-         ge = gargs.pop(key)
 
-         if fe == rexp(ge):
 
-             args.append(h(key)**rexph(fe))
 
-             hit = True
 
-         else:
 
-             fargs[key] = fe
 
-             gargs[key] = ge
 
-     if not hit:
 
-         return expr
 
-     while fargs:
 
-         key, e = fargs.popitem()
 
-         args.append(f(key)**e)
 
-     while gargs:
 
-         key, e = gargs.popitem()
 
-         args.append(g(key)**e)
 
-     return Mul(*args)
 
- _idn = lambda x: x
 
- _midn = lambda x: -x
 
- _one = lambda x: S.One
 
- def _match_div_rewrite(expr, i):
 
-     """helper for __trigsimp"""
 
-     if i == 0:
 
-         expr = _replace_mul_fpowxgpow(expr, sin, cos,
 
-             _midn, tan, _idn)
 
-     elif i == 1:
 
-         expr = _replace_mul_fpowxgpow(expr, tan, cos,
 
-             _idn, sin, _idn)
 
-     elif i == 2:
 
-         expr = _replace_mul_fpowxgpow(expr, cot, sin,
 
-             _idn, cos, _idn)
 
-     elif i == 3:
 
-         expr = _replace_mul_fpowxgpow(expr, tan, sin,
 
-             _midn, cos, _midn)
 
-     elif i == 4:
 
-         expr = _replace_mul_fpowxgpow(expr, cot, cos,
 
-             _midn, sin, _midn)
 
-     elif i == 5:
 
-         expr = _replace_mul_fpowxgpow(expr, cot, tan,
 
-             _idn, _one, _idn)
 
-     # i in (6, 7) is skipped
 
-     elif i == 8:
 
-         expr = _replace_mul_fpowxgpow(expr, sinh, cosh,
 
-             _midn, tanh, _idn)
 
-     elif i == 9:
 
-         expr = _replace_mul_fpowxgpow(expr, tanh, cosh,
 
-             _idn, sinh, _idn)
 
-     elif i == 10:
 
-         expr = _replace_mul_fpowxgpow(expr, coth, sinh,
 
-             _idn, cosh, _idn)
 
-     elif i == 11:
 
-         expr = _replace_mul_fpowxgpow(expr, tanh, sinh,
 
-             _midn, cosh, _midn)
 
-     elif i == 12:
 
-         expr = _replace_mul_fpowxgpow(expr, coth, cosh,
 
-             _midn, sinh, _midn)
 
-     elif i == 13:
 
-         expr = _replace_mul_fpowxgpow(expr, coth, tanh,
 
-             _idn, _one, _idn)
 
-     else:
 
-         return None
 
-     return expr
 
- def _trigsimp(expr, deep=False):
 
-     # protect the cache from non-trig patterns; we only allow
 
-     # trig patterns to enter the cache
 
-     if expr.has(*_trigs):
 
-         return __trigsimp(expr, deep)
 
-     return expr
 
- @cacheit
 
- def __trigsimp(expr, deep=False):
 
-     """recursive helper for trigsimp"""
 
-     from sympy.simplify.fu import TR10i
 
-     if _trigpat is None:
 
-         _trigpats()
 
-     a, b, c, d, matchers_division, matchers_add, \
 
-     matchers_identity, artifacts = _trigpat
 
-     if expr.is_Mul:
 
-         # do some simplifications like sin/cos -> tan:
 
-         if not expr.is_commutative:
 
-             com, nc = expr.args_cnc()
 
-             expr = _trigsimp(Mul._from_args(com), deep)*Mul._from_args(nc)
 
-         else:
 
-             for i, (pattern, simp, ok1, ok2) in enumerate(matchers_division):
 
-                 if not _dotrig(expr, pattern):
 
-                     continue
 
-                 newexpr = _match_div_rewrite(expr, i)
 
-                 if newexpr is not None:
 
-                     if newexpr != expr:
 
-                         expr = newexpr
 
-                         break
 
-                     else:
 
-                         continue
 
-                 # use SymPy matching instead
 
-                 res = expr.match(pattern)
 
-                 if res and res.get(c, 0):
 
-                     if not res[c].is_integer:
 
-                         ok = ok1.subs(res)
 
-                         if not ok.is_positive:
 
-                             continue
 
-                         ok = ok2.subs(res)
 
-                         if not ok.is_positive:
 
-                             continue
 
-                     # if "a" contains any of trig or hyperbolic funcs with
 
-                     # argument "b" then skip the simplification
 
-                     if any(w.args[0] == res[b] for w in res[a].atoms(
 
-                             TrigonometricFunction, HyperbolicFunction)):
 
-                         continue
 
-                     # simplify and finish:
 
-                     expr = simp.subs(res)
 
-                     break  # process below
 
-     if expr.is_Add:
 
-         args = []
 
-         for term in expr.args:
 
-             if not term.is_commutative:
 
-                 com, nc = term.args_cnc()
 
-                 nc = Mul._from_args(nc)
 
-                 term = Mul._from_args(com)
 
-             else:
 
-                 nc = S.One
 
-             term = _trigsimp(term, deep)
 
-             for pattern, result in matchers_identity:
 
-                 res = term.match(pattern)
 
-                 if res is not None:
 
-                     term = result.subs(res)
 
-                     break
 
-             args.append(term*nc)
 
-         if args != expr.args:
 
-             expr = Add(*args)
 
-             expr = min(expr, expand(expr), key=count_ops)
 
-         if expr.is_Add:
 
-             for pattern, result in matchers_add:
 
-                 if not _dotrig(expr, pattern):
 
-                     continue
 
-                 expr = TR10i(expr)
 
-                 if expr.has(HyperbolicFunction):
 
-                     res = expr.match(pattern)
 
-                     # if "d" contains any trig or hyperbolic funcs with
 
-                     # argument "a" or "b" then skip the simplification;
 
-                     # this isn't perfect -- see tests
 
-                     if res is None or not (a in res and b in res) or any(
 
-                         w.args[0] in (res[a], res[b]) for w in res[d].atoms(
 
-                             TrigonometricFunction, HyperbolicFunction)):
 
-                         continue
 
-                     expr = result.subs(res)
 
-                     break
 
-         # Reduce any lingering artifacts, such as sin(x)**2 changing
 
-         # to 1 - cos(x)**2 when sin(x)**2 was "simpler"
 
-         for pattern, result, ex in artifacts:
 
-             if not _dotrig(expr, pattern):
 
-                 continue
 
-             # Substitute a new wild that excludes some function(s)
 
-             # to help influence a better match. This is because
 
-             # sometimes, for example, 'a' would match sec(x)**2
 
-             a_t = Wild('a', exclude=[ex])
 
-             pattern = pattern.subs(a, a_t)
 
-             result = result.subs(a, a_t)
 
-             m = expr.match(pattern)
 
-             was = None
 
-             while m and was != expr:
 
-                 was = expr
 
-                 if m[a_t] == 0 or \
 
-                         -m[a_t] in m[c].args or m[a_t] + m[c] == 0:
 
-                     break
 
-                 if d in m and m[a_t]*m[d] + m[c] == 0:
 
-                     break
 
-                 expr = result.subs(m)
 
-                 m = expr.match(pattern)
 
-                 m.setdefault(c, S.Zero)
 
-     elif expr.is_Mul or expr.is_Pow or deep and expr.args:
 
-         expr = expr.func(*[_trigsimp(a, deep) for a in expr.args])
 
-     try:
 
-         if not expr.has(*_trigs):
 
-             raise TypeError
 
-         e = expr.atoms(exp)
 
-         new = expr.rewrite(exp, deep=deep)
 
-         if new == e:
 
-             raise TypeError
 
-         fnew = factor(new)
 
-         if fnew != new:
 
-             new = sorted([new, factor(new)], key=count_ops)[0]
 
-         # if all exp that were introduced disappeared then accept it
 
-         if not (new.atoms(exp) - e):
 
-             expr = new
 
-     except TypeError:
 
-         pass
 
-     return expr
 
- #------------------- end of old trigsimp routines --------------------
 
- def futrig(e, *, hyper=True, **kwargs):
 
-     """Return simplified ``e`` using Fu-like transformations.
 
-     This is not the "Fu" algorithm. This is called by default
 
-     from ``trigsimp``. By default, hyperbolics subexpressions
 
-     will be simplified, but this can be disabled by setting
 
-     ``hyper=False``.
 
-     Examples
 
-     ========
 
-     >>> from sympy import trigsimp, tan, sinh, tanh
 
-     >>> from sympy.simplify.trigsimp import futrig
 
-     >>> from sympy.abc import x
 
-     >>> trigsimp(1/tan(x)**2)
 
-     tan(x)**(-2)
 
-     >>> futrig(sinh(x)/tanh(x))
 
-     cosh(x)
 
-     """
 
-     from sympy.simplify.fu import hyper_as_trig
 
-     e = sympify(e)
 
-     if not isinstance(e, Basic):
 
-         return e
 
-     if not e.args:
 
-         return e
 
-     old = e
 
-     e = bottom_up(e, _futrig)
 
-     if hyper and e.has(HyperbolicFunction):
 
-         e, f = hyper_as_trig(e)
 
-         e = f(bottom_up(e, _futrig))
 
-     if e != old and e.is_Mul and e.args[0].is_Rational:
 
-         # redistribute leading coeff on 2-arg Add
 
-         e = Mul(*e.as_coeff_Mul())
 
-     return e
 
- def _futrig(e):
 
-     """Helper for futrig."""
 
-     from sympy.simplify.fu import (
 
-         TR1, TR2, TR3, TR2i, TR10, L, TR10i,
 
-         TR8, TR6, TR15, TR16, TR111, TR5, TRmorrie, TR11, _TR11, TR14, TR22,
 
-         TR12)
 
-     if not e.has(TrigonometricFunction):
 
-         return e
 
-     if e.is_Mul:
 
-         coeff, e = e.as_independent(TrigonometricFunction)
 
-     else:
 
-         coeff = None
 
-     Lops = lambda x: (L(x), x.count_ops(), _nodes(x), len(x.args), x.is_Add)
 
-     trigs = lambda x: x.has(TrigonometricFunction)
 
-     tree = [identity,
 
-         (
 
-         TR3,  # canonical angles
 
-         TR1,  # sec-csc -> cos-sin
 
-         TR12,  # expand tan of sum
 
-         lambda x: _eapply(factor, x, trigs),
 
-         TR2,  # tan-cot -> sin-cos
 
-         [identity, lambda x: _eapply(_mexpand, x, trigs)],
 
-         TR2i,  # sin-cos ratio -> tan
 
-         lambda x: _eapply(lambda i: factor(i.normal()), x, trigs),
 
-         TR14,  # factored identities
 
-         TR5,  # sin-pow -> cos_pow
 
-         TR10,  # sin-cos of sums -> sin-cos prod
 
-         TR11, _TR11, TR6, # reduce double angles and rewrite cos pows
 
-         lambda x: _eapply(factor, x, trigs),
 
-         TR14,  # factored powers of identities
 
-         [identity, lambda x: _eapply(_mexpand, x, trigs)],
 
-         TR10i,  # sin-cos products > sin-cos of sums
 
-         TRmorrie,
 
-         [identity, TR8],  # sin-cos products -> sin-cos of sums
 
-         [identity, lambda x: TR2i(TR2(x))],  # tan -> sin-cos -> tan
 
-         [
 
-             lambda x: _eapply(expand_mul, TR5(x), trigs),
 
-             lambda x: _eapply(
 
-                 expand_mul, TR15(x), trigs)], # pos/neg powers of sin
 
-         [
 
-             lambda x:  _eapply(expand_mul, TR6(x), trigs),
 
-             lambda x:  _eapply(
 
-                 expand_mul, TR16(x), trigs)], # pos/neg powers of cos
 
-         TR111,  # tan, sin, cos to neg power -> cot, csc, sec
 
-         [identity, TR2i],  # sin-cos ratio to tan
 
-         [identity, lambda x: _eapply(
 
-             expand_mul, TR22(x), trigs)],  # tan-cot to sec-csc
 
-         TR1, TR2, TR2i,
 
-         [identity, lambda x: _eapply(
 
-             factor_terms, TR12(x), trigs)],  # expand tan of sum
 
-         )]
 
-     e = greedy(tree, objective=Lops)(e)
 
-     if coeff is not None:
 
-         e = coeff * e
 
-     return e
 
- def _is_Expr(e):
 
-     """_eapply helper to tell whether ``e`` and all its args
 
-     are Exprs."""
 
-     if isinstance(e, Derivative):
 
-         return _is_Expr(e.expr)
 
-     if not isinstance(e, Expr):
 
-         return False
 
-     return all(_is_Expr(i) for i in e.args)
 
- def _eapply(func, e, cond=None):
 
-     """Apply ``func`` to ``e`` if all args are Exprs else only
 
-     apply it to those args that *are* Exprs."""
 
-     if not isinstance(e, Expr):
 
-         return e
 
-     if _is_Expr(e) or not e.args:
 
-         return func(e)
 
-     return e.func(*[
 
-         _eapply(func, ei) if (cond is None or cond(ei)) else ei
 
-         for ei in e.args])
 
 
  |