"""
Utility functions for Rubi integration.

See: http://www.apmaths.uwo.ca/~arich/IntegrationRules/PortableDocumentFiles/Integration%20utility%20functions.pdf
"""
from sympy.external import import_module
matchpy = import_module("matchpy")
from sympy.concrete.summations import Sum
from sympy.core.add import Add
from sympy.core.basic import Basic
from sympy.core.containers import Dict
from sympy.core.evalf import N
from sympy.core.expr import UnevaluatedExpr
from sympy.core.exprtools import factor_terms
from sympy.core.function import (Function, WildFunction, expand, expand_trig)
from sympy.core.mul import Mul
from sympy.core.numbers import (E, Float, I, Integer, Rational, oo, pi, zoo)
from sympy.core.power import Pow
from sympy.core.singleton import S
from sympy.core.symbol import (Dummy, Symbol, Wild, symbols)
from sympy.core.sympify import sympify
from sympy.core.traversal import postorder_traversal
from sympy.functions.combinatorial.factorials import factorial
from sympy.functions.elementary.complexes import im, re, Abs, sign
from sympy.functions.elementary.exponential import exp as sym_exp, log as sym_log, LambertW
from sympy.functions.elementary.hyperbolic import acosh, asinh, atanh, acoth, acsch, asech, cosh, sinh, tanh, coth, sech, csch
from sympy.functions.elementary.integers import floor, frac
from sympy.functions.elementary.miscellaneous import (Max, Min, sqrt)
from sympy.functions.elementary.trigonometric import atan, acsc, asin, acot, acos, asec, atan2, sin, cos, tan, cot, csc, sec
from sympy.functions.special.elliptic_integrals import elliptic_f, elliptic_e, elliptic_pi
from sympy.functions.special.error_functions import erf, fresnelc, fresnels, erfc, erfi, Ei, expint, li, Si, Ci, Shi, Chi
from sympy.functions.special.gamma_functions import (digamma, gamma, loggamma, polygamma, uppergamma)
from sympy.functions.special.hyper import (appellf1, hyper, TupleArg)
from sympy.functions.special.zeta_functions import polylog, zeta
from sympy.integrals.integrals import Integral
from sympy.logic.boolalg import And, Or
from sympy.ntheory.factor_ import (factorint, factorrat)
from sympy.polys.partfrac import apart
from sympy.polys.polyerrors import (PolynomialDivisionFailed, PolynomialError, UnificationFailed)
from sympy.polys.polytools import (discriminant, factor, gcd, lcm, poly, sqf, sqf_list, Poly, degree, quo, rem, total_degree)
from sympy.sets.sets import FiniteSet
from sympy.simplify.powsimp import powdenest
from sympy.simplify.radsimp import collect
from sympy.simplify.simplify import fraction, simplify, cancel, powsimp, nsimplify
from sympy.utilities.decorator import doctest_depends_on
from sympy.utilities.iterables import flatten
from sympy.core.random import randint


class rubi_unevaluated_expr(UnevaluatedExpr):
    """
    This is needed to convert `exp` as `Pow`.
    SymPy's UnevaluatedExpr has an issue with `is_commutative`.
    """
    @property
    def is_commutative(self):
        from sympy.core.logic import fuzzy_and
        return fuzzy_and(a.is_commutative for a in self.args)

_E = rubi_unevaluated_expr(E)


class rubi_exp(Function):
    """
    SymPy's exp is not identified as `Pow`. So it is not matched with `Pow`.
    Like `a = exp(2)` is not identified as `Pow(E, 2)`. Rubi rules need it.
    So, another exp has been created only for rubi module.

    Examples
    ========

    >>> from sympy import Pow, exp as sym_exp
    >>> isinstance(sym_exp(2), Pow)
    False
    >>> from sympy.integrals.rubi.utility_function import rubi_exp
    >>> isinstance(rubi_exp(2), Pow)
    True

    """
    @classmethod
    def eval(cls, *args):
        return Pow(_E, args[0])

class rubi_log(Function):
    """
    For rule matching different `exp` has been used. So for proper results,
    `log` is modified little only for case when it encounters rubi's `exp`.
    For other cases it is same.

    Examples
    ========

    >>> from sympy.integrals.rubi.utility_function import rubi_exp, rubi_log
    >>> a = rubi_exp(2)
    >>> rubi_log(a)
    2

    """
    @classmethod
    def eval(cls, *args):
        if args[0].has(_E):
            return sym_log(args[0]).doit()
        else:
            return sym_log(args[0])

if matchpy:
    from matchpy import Arity, Operation, CustomConstraint, Pattern, ReplacementRule, ManyToOneReplacer
    from sympy.integrals.rubi.symbol import WC
    from matchpy import is_match, replace_all

    class UtilityOperator(Operation):
        name = 'UtilityOperator'
        arity = Arity.variadic
        commutative = False
        associative = True

    Operation.register(rubi_log)
    Operation.register(rubi_exp)

    A_, B_, C_, F_, G_, a_, b_, c_, d_, e_, f_, g_, h_, i_, j_, k_, l_, m_, \
    n_, p_, q_, r_, t_, u_, v_, s_, w_, x_, z_ = [WC(i) for i in 'ABCFGabcdefghijklmnpqrtuvswxz']
    a, b, c, d, e = symbols('a b c d e')


Int = Integral


def replace_pow_exp(z):
    """
    This function converts back rubi's `exp` to general SymPy's `exp`.

    Examples
    ========

    >>> from sympy.integrals.rubi.utility_function import rubi_exp, replace_pow_exp
    >>> expr = rubi_exp(5)
    >>> expr
    E**5
    >>> replace_pow_exp(expr)
    exp(5)

    """
    z = S(z)
    if z.has(_E):
        z = z.replace(_E, E)
    return z

def Simplify(expr):
    expr = simplify(expr)
    return expr

def Set(expr, value):
    return {expr: value}

def With(subs, expr):
    if isinstance(subs, dict):
        k = list(subs.keys())[0]
        expr = expr.xreplace({k: subs[k]})
    else:
        for i in subs:
            k = list(i.keys())[0]
            expr = expr.xreplace({k: i[k]})
    return expr

def Module(subs, expr):
    return With(subs, expr)

def Scan(f, expr):
    # evaluates f applied to each element of expr in turn.
    for i in expr:
        yield f(i)

def MapAnd(f, l, x=None):
    # MapAnd[f,l] applies f to the elements of list l until False is returned; else returns True
    if x:
        for i in l:
            if f(i, x) == False:
                return False
        return True
    else:
        for i in l:
            if f(i) == False:
                return False
        return True

def FalseQ(u):
    if isinstance(u, (Dict, dict)):
        return FalseQ(*list(u.values()))

    return u == False

def ZeroQ(*expr):
    if len(expr) == 1:
        if isinstance(expr[0], list):
            return list(ZeroQ(i) for i in expr[0])
        else:

            return Simplify(expr[0]) == 0
    else:
        return all(ZeroQ(i) for i in expr)

def OneQ(a):
    if a == S(1):
        return True
    return False

def NegativeQ(u):
    u = Simplify(u)
    if u in (zoo, oo):
        return False
    if u.is_comparable:
        res = u < 0
        if not res.is_Relational:
            return res
    return False

def NonzeroQ(expr):
    return Simplify(expr) != 0


def FreeQ(nodes, var):
    if isinstance(nodes, list):
        return not any(S(expr).has(var) for expr in nodes)
    else:
        nodes = S(nodes)
        return not nodes.has(var)

def NFreeQ(nodes, var):
    """ Note that in rubi 4.10.8 this function was not defined in `Integration Utility Functions.m`,
    but was used in rules. So explicitly its returning `False`
    """
    return False
    # return not FreeQ(nodes, var)

def List(*var):
    return list(var)

def PositiveQ(var):
    var = Simplify(var)
    if var in (zoo, oo):
        return False
    if var.is_comparable:
        res = var > 0
        if not res.is_Relational:
            return res
    return False

def PositiveIntegerQ(*args):
    return all(var.is_Integer and PositiveQ(var) for var in args)

def NegativeIntegerQ(*args):
    return all(var.is_Integer and NegativeQ(var) for var in args)

def IntegerQ(var):
    var = Simplify(var)
    if isinstance(var, (int, Integer)):
        return True
    else:
        return var.is_Integer

def IntegersQ(*var):
    return all(IntegerQ(i) for i in var)

def _ComplexNumberQ(var):
    i = S(im(var))
    if isinstance(i, (Integer, Float)):
        return i != 0
    else:
        return False

def ComplexNumberQ(*var):
    """
    ComplexNumberQ(m, n,...) returns True if m, n, ... are all explicit complex numbers, else it returns False.

    Examples
    ========

    >>> from sympy.integrals.rubi.utility_function import ComplexNumberQ
    >>> from sympy import I
    >>> ComplexNumberQ(1 + I*2, I)
    True
    >>> ComplexNumberQ(2, I)
    False

    """
    return all(_ComplexNumberQ(i) for i in var)

def PureComplexNumberQ(*var):
    return all((_ComplexNumberQ(i) and re(i)==0) for i in var)

def RealNumericQ(u):
    return u.is_real

def PositiveOrZeroQ(u):
    return u.is_real and u >= 0

def NegativeOrZeroQ(u):
    return u.is_real and u <= 0

def FractionOrNegativeQ(u):
    return FractionQ(u) or NegativeQ(u)

def NegQ(var):
    return Not(PosQ(var)) and NonzeroQ(var)


def Equal(a, b):
    return a == b

def Unequal(a, b):
    return a != b

def IntPart(u):
    # IntPart[u] returns the sum of the integer terms of u.
    if ProductQ(u):
        if IntegerQ(First(u)):
            return First(u)*IntPart(Rest(u))
    elif IntegerQ(u):
        return u
    elif FractionQ(u):
        return IntegerPart(u)
    elif SumQ(u):
        res = 0
        for i in u.args:
            res += IntPart(i)
        return res
    return 0

def FracPart(u):
    # FracPart[u] returns the sum of the non-integer terms of u.
    if ProductQ(u):
        if IntegerQ(First(u)):
            return First(u)*FracPart(Rest(u))

    if IntegerQ(u):
        return 0
    elif FractionQ(u):
        return FractionalPart(u)
    elif SumQ(u):
        res = 0
        for i in u.args:
            res += FracPart(i)
        return res
    else:
        return u

def RationalQ(*nodes):
    return all(var.is_Rational for var in nodes)

def ProductQ(expr):
    return S(expr).is_Mul

def SumQ(expr):
    return expr.is_Add

def NonsumQ(expr):
    return not SumQ(expr)

def Subst(a, x, y):
    if None in [a, x, y]:
        return None
    if a.has(Function('Integrate')):
        # substituting in `Function(Integrate)` won't take care of properties of Integral
        a = a.replace(Function('Integrate'), Integral)
    return a.subs(x, y)
    # return a.xreplace({x: y})

def First(expr, d=None):
    """
    Gives the first element if it exists, or d otherwise.

    Examples
    ========

    >>> from sympy.integrals.rubi.utility_function import First
    >>> from sympy.abc import a, b, c
    >>> First(a + b + c)
    a
    >>> First(a*b*c)
    a

    """
    if isinstance(expr, list):
        return expr[0]
    if isinstance(expr, Symbol):
        return expr
    else:
        if SumQ(expr) or ProductQ(expr):
            l = Sort(expr.args)
            return l[0]
        else:
            return expr.args[0]

def Rest(expr):
    """
    Gives rest of the elements if it exists

    Examples
    ========

    >>> from sympy.integrals.rubi.utility_function import Rest
    >>> from sympy.abc import a, b, c
    >>> Rest(a + b + c)
    b + c
    >>> Rest(a*b*c)
    b*c

    """
    if isinstance(expr, list):
        return expr[1:]
    else:
        if SumQ(expr) or ProductQ(expr):
            l = Sort(expr.args)
            return expr.func(*l[1:])
        else:
            return expr.args[1]

def SqrtNumberQ(expr):
    # SqrtNumberQ[u] returns True if u^2 is a rational number; else it returns False.
    if PowerQ(expr):
        m = expr.base
        n = expr.exp
        return (IntegerQ(n) and SqrtNumberQ(m)) or (IntegerQ(n-S(1)/2) and RationalQ(m))
    elif expr.is_Mul:
        return all(SqrtNumberQ(i) for i in expr.args)
    else:
        return RationalQ(expr) or expr == I

def SqrtNumberSumQ(u):
    return SumQ(u) and SqrtNumberQ(First(u)) and SqrtNumberQ(Rest(u)) or ProductQ(u) and SqrtNumberQ(First(u)) and SqrtNumberSumQ(Rest(u))

def LinearQ(expr, x):
    """
    LinearQ(expr, x) returns True iff u is a polynomial of degree 1.

    Examples
    ========

    >>> from sympy.integrals.rubi.utility_function import LinearQ
    >>> from sympy.abc import x, y, a
    >>> LinearQ(a, x)
    False
    >>> LinearQ(3*x + y**2, x)
    True
    >>> LinearQ(3*x + y**2, y)
    False

    """
    if isinstance(expr, list):
        return all(LinearQ(i, x) for i in expr)
    elif expr.is_polynomial(x):
        if degree(Poly(expr, x), gen=x) == 1:
            return True
    return False

def Sqrt(a):
    return sqrt(a)

def ArcCosh(a):
    return acosh(a)

class Util_Coefficient(Function):
    def doit(self):
        if len(self.args) == 2:
            n = 1
        else:
            n = Simplify(self.args[2])

        if NumericQ(n):
            expr = expand(self.args[0])
            if isinstance(n, (int, Integer)):
                return expr.coeff(self.args[1], n)
            else:
                return expr.coeff(self.args[1]**n)
        else:
            return self

def Coefficient(expr, var, n=1):
    """
    Coefficient(expr, var) gives the coefficient of form in the polynomial expr.
    Coefficient(expr, var, n) gives the coefficient of var**n in expr.

    Examples
    ========

    >>> from sympy.integrals.rubi.utility_function import Coefficient
    >>> from sympy.abc import x, a, b, c
    >>> Coefficient(7 + 2*x + 4*x**3, x, 1)
    2
    >>> Coefficient(a + b*x + c*x**3, x, 0)
    a
    >>> Coefficient(a + b*x + c*x**3, x, 4)
    0
    >>> Coefficient(b*x + c*x**3, x, 3)
    c

    """
    if NumericQ(n):
        if expr == 0 or n in (zoo, oo):
            return 0
        expr = expand(expr)
        if isinstance(n, (int, Integer)):
            return expr.coeff(var, n)
        else:
            return expr.coeff(var**n)

    return Util_Coefficient(expr, var, n)

def Denominator(var):
    var = Simplify(var)
    if isinstance(var, Pow):
        if isinstance(var.exp, Integer):
            if var.exp > 0:
                return Pow(Denominator(var.base), var.exp)
            elif var.exp < 0:
                return Pow(Numerator(var.base), -1*var.exp)
    elif isinstance(var, Add):
        var = factor(var)
    return fraction(var)[1]

def Hypergeometric2F1(a, b, c, z):
    return hyper([a, b], [c], z)

def Not(var):
    if isinstance(var, bool):
        return not var
    elif var.is_Relational:
        var = False
    return not var


def FractionalPart(a):
    return frac(a)

def IntegerPart(a):
    return floor(a)

def AppellF1(a, b1, b2, c, x, y):
    return appellf1(a, b1, b2, c, x, y)

def EllipticPi(*args):
    return elliptic_pi(*args)

def EllipticE(*args):
    return elliptic_e(*args)

def EllipticF(Phi, m):
    return elliptic_f(Phi, m)

def ArcTan(a, b = None):
    if b is None:
        return atan(a)
    else:
        return atan2(a, b)

def ArcCot(a):
    return acot(a)

def ArcCoth(a):
    return acoth(a)

def ArcTanh(a):
    return atanh(a)

def ArcSin(a):
    return asin(a)

def ArcSinh(a):
    return asinh(a)

def ArcCos(a):
    return acos(a)

def ArcCsc(a):
    return acsc(a)

def ArcSec(a):
    return asec(a)

def ArcCsch(a):
    return acsch(a)

def ArcSech(a):
    return asech(a)

def Sinh(u):
    return sinh(u)

def Tanh(u):
    return tanh(u)

def Cosh(u):
    return cosh(u)

def Sech(u):
    return sech(u)

def Csch(u):
    return csch(u)

def Coth(u):
    return coth(u)

def LessEqual(*args):
    for i in range(0, len(args) - 1):
        try:
            if args[i] > args[i + 1]:
                return False
        except (IndexError, NotImplementedError):
            return False
    return True

def Less(*args):
    for i in range(0, len(args) - 1):
        try:
            if args[i] >= args[i + 1]:
                return False
        except (IndexError, NotImplementedError):
            return False
    return True

def Greater(*args):
    for i in range(0, len(args) - 1):
        try:
            if args[i] <= args[i + 1]:
                return False
        except (IndexError, NotImplementedError):
            return False
    return True

def GreaterEqual(*args):
    for i in range(0, len(args) - 1):
        try:
            if args[i] < args[i + 1]:
                return False
        except (IndexError, NotImplementedError):
            return False
    return True

def FractionQ(*args):
    """
    FractionQ(m, n,...) returns True if m, n, ... are all explicit fractions, else it returns False.

    Examples
    ========

    >>> from sympy import S
    >>> from sympy.integrals.rubi.utility_function import FractionQ
    >>> FractionQ(S('3'))
    False
    >>> FractionQ(S('3')/S('2'))
    True

    """
    return all(i.is_Rational for i in args) and all(Denominator(i) != S(1) for i in args)

def IntLinearcQ(a, b, c, d, m, n, x):
    # returns True iff (a+b*x)^m*(c+d*x)^n is integrable wrt x in terms of non-hypergeometric functions.
    return IntegerQ(m) or IntegerQ(n) or IntegersQ(S(3)*m, S(3)*n) or IntegersQ(S(4)*m, S(4)*n) or IntegersQ(S(2)*m, S(6)*n) or IntegersQ(S(6)*m, S(2)*n) or IntegerQ(m + n)

Defer = UnevaluatedExpr

def Expand(expr):
    return expr.expand()

def IndependentQ(u, x):
    """
    If u is free from x IndependentQ(u, x) returns True else False.

    Examples
    ========

    >>> from sympy.integrals.rubi.utility_function import IndependentQ
    >>> from sympy.abc import x, a, b
    >>> IndependentQ(a + b*x, x)
    False
    >>> IndependentQ(a + b, x)
    True

    """
    return FreeQ(u, x)

def PowerQ(expr):
    return expr.is_Pow or ExpQ(expr)

def IntegerPowerQ(u):
    if isinstance(u, sym_exp): #special case for exp
        return IntegerQ(u.args[0])
    return PowerQ(u) and IntegerQ(u.args[1])

def PositiveIntegerPowerQ(u):
    if isinstance(u, sym_exp):
        return IntegerQ(u.args[0]) and PositiveQ(u.args[0])
    return PowerQ(u) and IntegerQ(u.args[1]) and PositiveQ(u.args[1])

def FractionalPowerQ(u):
    if isinstance(u, sym_exp):
        return FractionQ(u.args[0])
    return PowerQ(u) and FractionQ(u.args[1])

def AtomQ(expr):
    expr = sympify(expr)
    if isinstance(expr, list):
        return False
    if expr in [None, True, False, _E]: # [None, True, False] are atoms in mathematica and _E is also an atom
        return True
    # elif isinstance(expr, list):
    #     return all(AtomQ(i) for i in expr)
    else:
        return expr.is_Atom

def ExpQ(u):
    u = replace_pow_exp(u)
    return Head(u) in (sym_exp, rubi_exp)

def LogQ(u):
    return u.func in (sym_log, Log)

def Head(u):
    return u.func

def MemberQ(l, u):
    if isinstance(l, list):
        return u in l
    else:
        return u in l.args

def TrigQ(u):
    if AtomQ(u):
        x = u
    else:
        x = Head(u)
    return MemberQ([sin, cos, tan, cot, sec, csc], x)

def SinQ(u):
    return Head(u) == sin

def CosQ(u):
    return Head(u) == cos

def TanQ(u):
    return Head(u) == tan

def CotQ(u):
    return Head(u) == cot

def SecQ(u):
    return Head(u) == sec

def CscQ(u):
    return Head(u) == csc

def Sin(u):
    return sin(u)

def Cos(u):
    return cos(u)

def Tan(u):
    return tan(u)

def Cot(u):
    return cot(u)

def Sec(u):
    return sec(u)

def Csc(u):
    return csc(u)

def HyperbolicQ(u):
    if AtomQ(u):
        x = u
    else:
        x = Head(u)
    return MemberQ([sinh, cosh, tanh, coth, sech, csch], x)

def SinhQ(u):
    return Head(u) == sinh

def CoshQ(u):
    return Head(u) == cosh

def TanhQ(u):
    return Head(u) == tanh

def CothQ(u):
    return Head(u) == coth

def SechQ(u):
    return Head(u) == sech

def CschQ(u):
    return Head(u) == csch

def InverseTrigQ(u):
    if AtomQ(u):
        x = u
    else:
        x = Head(u)
    return MemberQ([asin, acos, atan, acot, asec, acsc], x)

def SinCosQ(f):
    return MemberQ([sin, cos, sec, csc], Head(f))

def SinhCoshQ(f):
    return MemberQ([sinh, cosh, sech, csch], Head(f))

def LeafCount(expr):
    return len(list(postorder_traversal(expr)))

def Numerator(u):
    u = Simplify(u)
    if isinstance(u, Pow):
        if isinstance(u.exp, Integer):
            if u.exp > 0:
                return Pow(Numerator(u.base), u.exp)
            elif u.exp < 0:
                return Pow(Denominator(u.base), -1*u.exp)
    elif isinstance(u, Add):
        u = factor(u)
    return fraction(u)[0]

def NumberQ(u):
    if isinstance(u, (int, float)):
        return True
    return u.is_number

def NumericQ(u):
    return N(u).is_number

def Length(expr):
    """
    Returns number of elements in the expression just as SymPy's len.

    Examples
    ========

    >>> from sympy.integrals.rubi.utility_function import Length
    >>> from sympy.abc import x, a, b
    >>> from sympy import cos, sin
    >>> Length(a + b)
    2
    >>> Length(sin(a)*cos(a))
    2

    """
    if isinstance(expr, list):
        return len(expr)
    return len(expr.args)

def ListQ(u):
    return isinstance(u, list)

def Im(u):
    u = S(u)
    return im(u.doit())

def Re(u):
    u = S(u)
    return re(u.doit())

def InverseHyperbolicQ(u):
    if not u.is_Atom:
        u = Head(u)
    return u in [acosh, asinh, atanh, acoth, acsch, acsch]

def InverseFunctionQ(u):
    # returns True if u is a call on an inverse function; else returns False.
    return LogQ(u) or InverseTrigQ(u) and Length(u) <= 1 or InverseHyperbolicQ(u) or u.func == polylog

def TrigHyperbolicFreeQ(u, x):
    # If u is free of trig, hyperbolic and calculus functions involving x, TrigHyperbolicFreeQ[u,x] returns true; else it returns False.
    if AtomQ(u):
        return True
    else:
        if TrigQ(u) | HyperbolicQ(u) | CalculusQ(u):
            return FreeQ(u, x)
        else:
            for i in u.args:
                if not TrigHyperbolicFreeQ(i, x):
                    return False
            return True

def InverseFunctionFreeQ(u, x):
    # If u is free of inverse, calculus and hypergeometric functions involving x, InverseFunctionFreeQ[u,x] returns true; else it returns False.
    if AtomQ(u):
        return True
    else:
        if InverseFunctionQ(u) or CalculusQ(u) or u.func in (hyper, appellf1):
            return FreeQ(u, x)
        else:
            for i in u.args:
                if not ElementaryFunctionQ(i):
                    return False
            return True

def RealQ(u):
    if ListQ(u):
        return MapAnd(RealQ, u)
    elif NumericQ(u):
        return ZeroQ(Im(N(u)))
    elif PowerQ(u):
        u = u.base
        v = u.exp
        return RealQ(u) & RealQ(v) & (IntegerQ(v) | PositiveOrZeroQ(u))
    elif u.is_Mul:
        return all(RealQ(i) for i in u.args)
    elif u.is_Add:
        return all(RealQ(i) for i in u.args)
    elif u.is_Function:
        f = u.func
        u = u.args[0]
        if f in [sin, cos, tan, cot, sec, csc, atan, acot, erf]:
            return RealQ(u)
        else:
            if f in [asin, acos]:
                return LessEqual(-1, u, 1)
            else:
                if f == sym_log:
                    return PositiveOrZeroQ(u)
                else:
                    return False
    else:
        return False

def EqQ(u, v):
    return ZeroQ(u - v)

def FractionalPowerFreeQ(u):
    if AtomQ(u):
        return True
    elif FractionalPowerQ(u):
        return False

def ComplexFreeQ(u):
    if AtomQ(u) and Not(ComplexNumberQ(u)):
        return True
    else:
         return False

def PolynomialQ(u, x = None):
    if x is None :
        return u.is_polynomial()
    if isinstance(x, Pow):
        if isinstance(x.exp, Integer):
            deg = degree(u, x.base)
            if u.is_polynomial(x):
                if deg % x.exp !=0 :
                    return False
                try:
                    p = Poly(u, x.base)
                except PolynomialError:
                    return False

                c_list = p.all_coeffs()
                coeff_list = c_list[:-1:x.exp]
                coeff_list += [c_list[-1]]
                for i in coeff_list:
                    if not i == 0:
                        index = c_list.index(i)
                        c_list[index] = 0

                if all(i == 0 for i in c_list):
                    return True
                else:
                    return False

            else:
                return False

        elif isinstance(x.exp, (Float, Rational)): #not full - proof
            if FreeQ(simplify(u), x.base) and Exponent(u, x.base) == 0:
                if not all(FreeQ(u, i) for i in x.base.free_symbols):
                    return False

    if isinstance(x, Mul):
        return all(PolynomialQ(u, i) for i in x.args)

    return u.is_polynomial(x)

def FactorSquareFree(u):
    return sqf(u)

def PowerOfLinearQ(expr, x):
    u = Wild('u')
    w = Wild('w')
    m = Wild('m')
    n = Wild('n')
    Match = expr.match(u**m)
    if PolynomialQ(Match[u], x) and FreeQ(Match[m], x):
        if IntegerQ(Match[m]):
            e = FactorSquareFree(Match[u]).match(w**n)
            if FreeQ(e[n], x) and LinearQ(e[w], x):
                return True
            else:
                return False
        else:
            return LinearQ(Match[u], x)
    else:
        return False

def Exponent(expr, x):
    expr = Expand(S(expr))
    if S(expr).is_number or (not expr.has(x)):
        return 0
    if PolynomialQ(expr, x):
        if isinstance(x, Rational):
            return degree(Poly(expr, x), x)
        return degree(expr, gen = x)
    else:
        return 0

def ExponentList(expr, x):
    expr = Expand(S(expr))
    if S(expr).is_number or (not expr.has(x)):
        return [0]
    if expr.is_Add:
        expr = collect(expr, x)
        lst = []
        k = 1
        for t in expr.args:
            if t.has(x):
                if isinstance(x, Rational):
                    lst += [degree(Poly(t, x), x)]
                else:
                    lst += [degree(t, gen = x)]
            else:
                if k == 1:
                    lst += [0]
                    k += 1
        lst.sort()
        return lst
    else:
        if isinstance(x, Rational):
            return [degree(Poly(expr, x), x)]
        else:
            return [degree(expr, gen = x)]

def QuadraticQ(u, x):
    # QuadraticQ(u, x) returns True iff u is a polynomial of degree 2 and not a monomial of the form a x^2
    if ListQ(u):
        for expr in u:
            if Not(PolyQ(expr, x, 2) and Not(Coefficient(expr, x, 0) == 0 and Coefficient(expr, x, 1) == 0)):
                return False
        return True
    else:
        return PolyQ(u, x, 2) and Not(Coefficient(u, x, 0) == 0 and Coefficient(u, x, 1) == 0)

def LinearPairQ(u, v, x):
    # LinearPairQ(u, v, x) returns True iff u and v are linear not equal x but u/v is a constant wrt x
    return LinearQ(u, x) and LinearQ(v, x) and NonzeroQ(u-x) and ZeroQ(Coefficient(u, x, 0)*Coefficient(v, x, 1)-Coefficient(u, x, 1)*Coefficient(v, x, 0))

def BinomialParts(u, x):
    if PolynomialQ(u, x):
        if Exponent(u, x) > 0:
            lst = ExponentList(u, x)
            if len(lst)==1:
                return [0, Coefficient(u, x, Exponent(u, x)), Exponent(u, x)]
            elif len(lst) == 2 and lst[0] == 0:
                return [Coefficient(u, x, 0), Coefficient(u, x, Exponent(u, x)), Exponent(u, x)]
            else:
                return False
        else:
            return False
    elif PowerQ(u):
        if u.base == x and FreeQ(u.exp, x):
            return [0, 1, u.exp]
        else:
            return False
    elif ProductQ(u):
        if FreeQ(First(u), x):
            lst2 = BinomialParts(Rest(u), x)
            if AtomQ(lst2):
                return False
            else:
                return [First(u)*lst2[0], First(u)*lst2[1], lst2[2]]
        elif FreeQ(Rest(u), x):
            lst1 = BinomialParts(First(u), x)
            if AtomQ(lst1):
                return False
            else:
                return [Rest(u)*lst1[0], Rest(u)*lst1[1], lst1[2]]
        lst1 = BinomialParts(First(u), x)
        if AtomQ(lst1):
            return False
        lst2 = BinomialParts(Rest(u), x)
        if AtomQ(lst2):
            return False
        a = lst1[0]
        b = lst1[1]
        m = lst1[2]
        c = lst2[0]
        d = lst2[1]
        n = lst2[2]
        if ZeroQ(a):
            if ZeroQ(c):
                return [0, b*d, m + n]
            elif ZeroQ(m + n):
                return [b*d, b*c, m]
            else:
                return False
        if ZeroQ(c):
            if ZeroQ(m + n):
                return [b*d, a*d, n]
            else:
                return False
        if EqQ(m, n) and ZeroQ(a*d + b*c):
            return [a*c, b*d, 2*m]
        else:
            return False
    elif SumQ(u):
        if FreeQ(First(u),x):
            lst2 = BinomialParts(Rest(u), x)
            if AtomQ(lst2):
                return False
            else:
                return [First(u) + lst2[0], lst2[1], lst2[2]]
        elif FreeQ(Rest(u), x):
            lst1 = BinomialParts(First(u), x)
            if AtomQ(lst1):
                return False
            else:
                return[Rest(u) + lst1[0], lst1[1], lst1[2]]
        lst1 = BinomialParts(First(u), x)
        if AtomQ(lst1):
            return False
        lst2 = BinomialParts(Rest(u),x)
        if AtomQ(lst2):
            return False
        if EqQ(lst1[2], lst2[2]):
            return [lst1[0] + lst2[0], lst1[1] + lst2[1], lst1[2]]
        else:
            return False
    else:
        return False

def TrinomialParts(u, x):
    # If u is equivalent to a trinomial of the form a + b*x^n + c*x^(2*n) where n!=0, b!=0 and c!=0, TrinomialParts[u,x] returns the list {a,b,c,n}; else it returns False.
    u = sympify(u)
    if PolynomialQ(u, x):
        lst = CoefficientList(u, x)
        if len(lst)<3 or EvenQ(sympify(len(lst))) or ZeroQ((len(lst)+1)/2):
            return False
        #Catch(
         #   Scan(Function(if ZeroQ(lst), Null, Throw(False), Drop(Drop(Drop(lst, [(len(lst)+1)/2]), 1), -1];
          #  [First(lst), lst[(len(lst)+1)/2], Last(lst), (len(lst)-1)/2]):
    if PowerQ(u):
        if EqQ(u.exp, 2):
            lst = BinomialParts(u.base, x)
            if not lst or ZeroQ(lst[0]):
                return False
            else:
                return [lst[0]**2, 2*lst[0]*lst[1], lst[1]**2, lst[2]]
        else:
            return False
    if ProductQ(u):
        if FreeQ(First(u), x):
            lst2 = TrinomialParts(Rest(u), x)
            if not lst2:
                return False
            else:
                return [First(u)*lst2[0], First(u)*lst2[1], First(u)*lst2[2], lst2[3]]
        if FreeQ(Rest(u), x):
            lst1 = TrinomialParts(First(u), x)
            if not lst1:
                return False
            else:
                return [Rest(u)*lst1[0], Rest(u)*lst1[1], Rest(u)*lst1[2], lst1[3]]
        lst1 = BinomialParts(First(u), x)
        if not lst1:
            return False
        lst2 = BinomialParts(Rest(u), x)
        if not lst2:
            return False
        a = lst1[0]
        b = lst1[1]
        m = lst1[2]
        c = lst2[0]
        d = lst2[1]
        n = lst2[2]
        if EqQ(m, n) and NonzeroQ(a*d+b*c):
            return [a*c, a*d + b*c, b*d, m]
        else:
            return False
    if SumQ(u):
        if FreeQ(First(u), x):
            lst2 = TrinomialParts(Rest(u), x)
            if not lst2:
                return False
            else:
                return [First(u)+lst2[0], lst2[1], lst2[2], lst2[3]]
        if FreeQ(Rest(u), x):
            lst1 = TrinomialParts(First(u), x)
            if not lst1:
                return False
            else:
                return [Rest(u)+lst1[0], lst1[1], lst1[2], lst1[3]]
        lst1 = TrinomialParts(First(u), x)
        if not lst1:
            lst3 = BinomialParts(First(u), x)
            if not lst3:
                return False
            lst2 = TrinomialParts(Rest(u), x)
            if not lst2:
                lst4 = BinomialParts(Rest(u), x)
                if not lst4:
                    return False
                if EqQ(lst3[2], 2*lst4[2]):
                    return [lst3[0]+lst4[0], lst4[1], lst3[1], lst4[2]]
                if EqQ(lst4[2], 2*lst3[2]):
                    return [lst3[0]+lst4[0], lst3[1], lst4[1], lst3[2]]
                else:
                    return False
            if EqQ(lst3[2], lst2[3]) and NonzeroQ(lst3[1]+lst2[1]):
                return [lst3[0]+lst2[0], lst3[1]+lst2[1], lst2[2], lst2[3]]
            if EqQ(lst3[2], 2*lst2[3]) and NonzeroQ(lst3[1]+lst2[2]):
                return [lst3[0]+lst2[0], lst2[1], lst3[1]+lst2[2], lst2[3]]
            else:
                return False
        lst2 = TrinomialParts(Rest(u), x)
        if AtomQ(lst2):
            lst4 = BinomialParts(Rest(u), x)
            if not lst4:
                return False
            if EqQ(lst4[2], lst1[3]) and NonzeroQ(lst1[1]+lst4[0]):
                return [lst1[0]+lst4[0], lst1[1]+lst4[1], lst1[2], lst1[3]]
            if EqQ(lst4[2], 2*lst1[3]) and NonzeroQ(lst1[2]+lst4[1]):
                return [lst1[0]+lst4[0], lst1[1], lst1[2]+lst4[1], lst1[3]]
            else:
                return False
        if EqQ(lst1[3], lst2[3]) and NonzeroQ(lst1[1]+lst2[1]) and NonzeroQ(lst1[2]+lst2[2]):
            return [lst1[0]+lst2[0], lst1[1]+lst2[1], lst1[2]+lst2[2], lst1[3]]
        else:
            return False
    else:
        return False

def PolyQ(u, x, n=None):
    # returns True iff u is a polynomial of degree n.
    if ListQ(u):
        return all(PolyQ(i, x) for i in u)

    if n is None:
        if u == x:
            return False
        elif isinstance(x, Pow):
            n = x.exp
            x_base = x.base
            if FreeQ(n, x_base):
                if PositiveIntegerQ(n):
                    return PolyQ(u, x_base) and (PolynomialQ(u, x) or PolynomialQ(Together(u), x))
                elif AtomQ(n):
                    return PolynomialQ(u, x) and FreeQ(CoefficientList(u, x), x_base)
                else:
                    return False

        return PolynomialQ(u, x) or PolynomialQ(u, Together(x))

    else:
        return PolynomialQ(u, x) and Coefficient(u, x, n) != 0 and Exponent(u, x) == n


def EvenQ(u):
    # gives True if expr is an even integer, and False otherwise.
    return isinstance(u, (Integer, int)) and u%2 == 0

def OddQ(u):
    # gives True if expr is an odd integer, and False otherwise.

    return isinstance(u, (Integer, int)) and u%2 == 1

def PerfectSquareQ(u):
    # (* If u is a rational number whose squareroot is rational or if u is of the form u1^n1 u2^n2 ...
    # and n1, n2, ... are even, PerfectSquareQ[u] returns True; else it returns False. *)
    if RationalQ(u):
        return Greater(u, 0) and RationalQ(Sqrt(u))
    elif PowerQ(u):
        return EvenQ(u.exp)
    elif ProductQ(u):
        return PerfectSquareQ(First(u)) and PerfectSquareQ(Rest(u))
    elif SumQ(u):
        s = Simplify(u)
        if NonsumQ(s):
            return PerfectSquareQ(s)
        return False
    else:
        return False

def NiceSqrtAuxQ(u):
    if RationalQ(u):
        return u > 0
    elif PowerQ(u):
        return EvenQ(u.exp)
    elif ProductQ(u):
        return NiceSqrtAuxQ(First(u)) and NiceSqrtAuxQ(Rest(u))
    elif SumQ(u):
        s = Simplify(u)
        return  NonsumQ(s) and NiceSqrtAuxQ(s)
    else:
        return False

def NiceSqrtQ(u):
    return Not(NegativeQ(u)) and NiceSqrtAuxQ(u)

def Together(u):
    return factor(u)

def PosAux(u):
    if RationalQ(u):
        return u>0
    elif NumberQ(u):
        if ZeroQ(Re(u)):
            return Im(u) > 0
        else:
            return Re(u) > 0
    elif NumericQ(u):
        v = N(u)
        if ZeroQ(Re(v)):
            return Im(v) > 0
        else:
            return Re(v) > 0
    elif PowerQ(u):
        if OddQ(u.exp):
            return PosAux(u.base)
        else:
            return True
    elif ProductQ(u):
        if PosAux(First(u)):
            return PosAux(Rest(u))
        else:
            return not PosAux(Rest(u))
    elif SumQ(u):
        return PosAux(First(u))
    else:
        res = u > 0
        if res in(True, False):
            return res
        return True

def PosQ(u):
    # If u is not 0 and has a positive form, PosQ[u] returns True, else it returns False.
    return PosAux(TogetherSimplify(u))

def CoefficientList(u, x):
    if PolynomialQ(u, x):
        return list(reversed(Poly(u, x).all_coeffs()))
    else:
        return []

def ReplaceAll(expr, args):
    if isinstance(args, list):
        n_args = {}
        for i in args:
            n_args.update(i)
        return expr.subs(n_args)
    return expr.subs(args)

def ExpandLinearProduct(v, u, a, b, x):
    # If u is a polynomial in x, ExpandLinearProduct[v,u,a,b,x] expands v*u into a sum of terms of the form c*v*(a+b*x)^n.
    if FreeQ([a, b], x) and PolynomialQ(u, x):
        lst = CoefficientList(ReplaceAll(u, {x: (x - a)/b}), x)
        lst = [SimplifyTerm(i, x) for i in lst]
        res = 0
        for k in range(1, len(lst)+1):
            res = res + Simplify(v*lst[k-1]*(a + b*x)**(k - 1))
        return res
    return u*v

def GCD(*args):
    args = S(args)
    if len(args) == 1:
        if isinstance(args[0], (int, Integer)):
            return args[0]
        else:
            return S(1)
    return gcd(*args)

def ContentFactor(expn):
    return factor_terms(expn)

def NumericFactor(u):
    # returns the real numeric factor of u.
    if NumberQ(u):
        if ZeroQ(Im(u)):
            return u
        elif ZeroQ(Re(u)):
            return Im(u)
        else:
            return S(1)
    elif PowerQ(u):
        if RationalQ(u.base) and RationalQ(u.exp):
            if u.exp > 0:
                return 1/Denominator(u.base)
            else:
                return 1/(1/Denominator(u.base))
        else:
            return S(1)
    elif ProductQ(u):
        return Mul(*[NumericFactor(i) for i in u.args])
    elif SumQ(u):
        if LeafCount(u) < 50:
            c = ContentFactor(u)
            if SumQ(c):
                return S(1)
            else:
                return NumericFactor(c)
        else:
            m = NumericFactor(First(u))
            n = NumericFactor(Rest(u))
            if m < 0 and n < 0:
                return -GCD(-m, -n)
            else:
                return GCD(m, n)
    return S(1)

def NonnumericFactors(u):
    if NumberQ(u):
        if ZeroQ(Im(u)):
            return S(1)
        elif ZeroQ(Re(u)):
            return I
        return u
    elif PowerQ(u):
        if RationalQ(u.base) and FractionQ(u.exp):
            return u/NumericFactor(u)
        return u
    elif ProductQ(u):
        result = 1
        for i in u.args:
            result *= NonnumericFactors(i)
        return result
    elif SumQ(u):
        if LeafCount(u) < 50:
            i = ContentFactor(u)
            if SumQ(i):
                return u
            else:
                return NonnumericFactors(i)
        n = NumericFactor(u)
        result = 0
        for i in u.args:
            result += i/n
        return result
    return u

def MakeAssocList(u, x, alst=None):
    # (* MakeAssocList[u,x,alst] returns an association list of gensymed symbols with the nonatomic
    # parameters of a u that are not integer powers, products or sums. *)
    if alst is None:
        alst = []
    u = replace_pow_exp(u)
    x = replace_pow_exp(x)
    if AtomQ(u):
        return alst
    elif IntegerPowerQ(u):
        return MakeAssocList(u.base, x, alst)
    elif ProductQ(u) or SumQ(u):
        return MakeAssocList(Rest(u), x, MakeAssocList(First(u), x, alst))
    elif FreeQ(u, x):
        tmp = []
        for i in alst:
            if PowerQ(i):
                if i.exp == u:
                    tmp.append(i)
                    break
            elif len(i.args) > 1: # make sure args has length > 1, else causes index error some times
                if i.args[1] == u:
                    tmp.append(i)
                    break
        if tmp == []:
            alst.append(u)
        return alst
    return alst

def GensymSubst(u, x, alst=None):
    # (* GensymSubst[u,x,alst] returns u with the kernels in alst free of x replaced by gensymed names. *)
    if alst is None:
        alst =[]
    u = replace_pow_exp(u)
    x = replace_pow_exp(x)
    if AtomQ(u):
        return u
    elif IntegerPowerQ(u):
        return GensymSubst(u.base, x, alst)**u.exp
    elif ProductQ(u) or SumQ(u):
        return u.func(*[GensymSubst(i, x, alst) for i in u.args])
    elif FreeQ(u, x):
        tmp = []
        for i in alst:
            if PowerQ(i):
                if i.exp == u:
                    tmp.append(i)
                    break

            elif len(i.args) > 1: # make sure args has length > 1, else causes index error some times
                if i.args[1] == u:
                    tmp.append(i)
                    break
        if tmp == []:
            return u
        return tmp[0][0]
    return u

def KernelSubst(u, x, alst):
    # (* KernelSubst[u,x,alst] returns u with the gensymed names in alst replaced by kernels free of x. *)
    if AtomQ(u):
        tmp = []
        for i in alst:
            if i.args[0] == u:
                tmp.append(i)
                break
        if tmp == []:
            return u
        elif len(tmp[0].args) > 1: # make sure args has length > 1, else causes index error some times
            return tmp[0].args[1]

    elif IntegerPowerQ(u):
        tmp = KernelSubst(u.base, x, alst)
        if u.exp < 0 and ZeroQ(tmp):
            return 'Indeterminate'
        return tmp**u.exp
    elif ProductQ(u) or SumQ(u):
        return u.func(*[KernelSubst(i, x, alst) for i in u.args])
    return u

def ExpandExpression(u, x):
    if AlgebraicFunctionQ(u, x) and Not(RationalFunctionQ(u, x)):
        v = ExpandAlgebraicFunction(u, x)
    else:
        v = S(0)
    if SumQ(v):
        return ExpandCleanup(v, x)
    v = SmartApart(u, x)
    if SumQ(v):
        return ExpandCleanup(v, x)
    v = SmartApart(RationalFunctionFactors(u, x), x, x)
    if SumQ(v):
        w = NonrationalFunctionFactors(u, x)
        return ExpandCleanup(v.func(*[i*w for i in v.args]), x)
    v = Expand(u)
    if SumQ(v):
        return ExpandCleanup(v, x)
    v = Expand(u)
    if SumQ(v):
        return ExpandCleanup(v, x)
    return SimplifyTerm(u, x)

def Apart(u, x):
    if RationalFunctionQ(u, x):
        return apart(u, x)

    return u

def SmartApart(*args):
    if len(args) == 2:
        u, x = args
        alst = MakeAssocList(u, x)
        tmp = KernelSubst(Apart(GensymSubst(u, x, alst), x), x, alst)
        if tmp == 'Indeterminate':
            return u
        return tmp

    u, v, x = args
    alst = MakeAssocList(u, x)
    tmp = KernelSubst(Apart(GensymSubst(u, x, alst), x), x, alst)
    if tmp == 'Indeterminate':
        return u
    return tmp

def MatchQ(expr, pattern, *var):
    # returns the matched arguments after matching pattern with expression
    match = expr.match(pattern)
    if match:
        return tuple(match[i] for i in var)
    else:
        return None

def PolynomialQuotientRemainder(p, q, x):
    return [PolynomialQuotient(p, q, x), PolynomialRemainder(p, q, x)]

def FreeFactors(u, x):
    # returns the product of the factors of u free of x.
    if ProductQ(u):
        result = 1
        for i in u.args:
            if FreeQ(i, x):
                result *= i
        return result
    elif FreeQ(u, x):
        return u
    else:
        return S(1)

def NonfreeFactors(u, x):
    """
    Returns the product of the factors of u not free of x.

    Examples
    ========

    >>> from sympy.integrals.rubi.utility_function import NonfreeFactors
    >>> from sympy.abc import x, a, b
    >>> NonfreeFactors(a, x)
    1
    >>> NonfreeFactors(x + a, x)
    a + x
    >>> NonfreeFactors(a*b*x, x)
    x

    """
    if ProductQ(u):
        result = 1
        for i in u.args:
            if not FreeQ(i, x):
                result *= i
        return result
    elif FreeQ(u, x):
        return 1
    else:
        return u

def RemoveContentAux(expr, x):
    return RemoveContentAux_replacer.replace(UtilityOperator(expr, x))

def RemoveContent(u, x):
    v = NonfreeFactors(u, x)
    w = Together(v)

    if EqQ(FreeFactors(w, x), 1):
        return RemoveContentAux(v, x)
    else:
        return RemoveContentAux(NonfreeFactors(w, x), x)


def FreeTerms(u, x):
    """
    Returns the sum of the terms of u free of x.

    Examples
    ========

    >>> from sympy.integrals.rubi.utility_function import FreeTerms
    >>> from sympy.abc import x, a, b
    >>> FreeTerms(a, x)
    a
    >>> FreeTerms(x*a, x)
    0
    >>> FreeTerms(a*x + b, x)
    b

    """
    if SumQ(u):
        result = 0
        for i in u.args:
            if FreeQ(i, x):
                result += i
        return result
    elif FreeQ(u, x):
        return u
    else:
        return 0

def NonfreeTerms(u, x):
    # returns the sum of the terms of u free of x.
    if SumQ(u):
        result = S(0)
        for i in u.args:
            if not FreeQ(i, x):
                result += i
        return result
    elif not FreeQ(u, x):
        return u
    else:
        return S(0)

def ExpandAlgebraicFunction(expr, x):
    if ProductQ(expr):
        u_ = Wild('u', exclude=[x])
        n_ = Wild('n', exclude=[x])
        v_ = Wild('v')
        pattern = u_*v_
        match = expr.match(pattern)
        if match:
            keys = [u_, v_]
            if len(keys) == len(match):
                u, v = tuple([match[i] for i in keys])
                if SumQ(v):
                    u, v = v, u
                if not FreeQ(u, x) and SumQ(u):
                    result = 0
                    for i in u.args:
                        result += i*v
                    return result

        pattern = u_**n_*v_
        match = expr.match(pattern)
        if match:
            keys = [u_, n_, v_]
            if len(keys) == len(match):
                u, n, v = tuple([match[i] for i in keys])
                if PositiveIntegerQ(n) and SumQ(u):
                    w = Expand(u**n)
                    result = 0
                    for i in w.args:
                        result += i*v
                    return result

    return expr

def CollectReciprocals(expr, x):
    # Basis: e/(a+b x)+f/(c+d x)==(c e+a f+(d e+b f) x)/(a c+(b c+a d) x+b d x^2)
    if SumQ(expr):
        u_ = Wild('u')
        a_ = Wild('a', exclude=[x])
        b_ = Wild('b', exclude=[x])
        c_ = Wild('c', exclude=[x])
        d_ = Wild('d', exclude=[x])
        e_ = Wild('e', exclude=[x])
        f_ = Wild('f', exclude=[x])
        pattern = u_ + e_/(a_ + b_*x) + f_/(c_+d_*x)
        match = expr.match(pattern)
        if match:
            try: # .match() does not work peoperly always
                keys = [u_, a_, b_, c_, d_, e_, f_]
                u, a, b, c, d, e, f = tuple([match[i] for i in keys])
                if ZeroQ(b*c + a*d) & ZeroQ(d*e + b*f):
                    return CollectReciprocals(u + (c*e + a*f)/(a*c + b*d*x**2),x)
                elif ZeroQ(b*c + a*d) & ZeroQ(c*e + a*f):
                    return CollectReciprocals(u + (d*e + b*f)*x/(a*c + b*d*x**2),x)
            except:
                pass
    return expr

def ExpandCleanup(u, x):
    v = CollectReciprocals(u, x)
    if SumQ(v):
        res = 0
        for i in v.args:
            res += SimplifyTerm(i, x)
        v = res
        if SumQ(v):
            return UnifySum(v, x)
        else:
            return v
    else:
        return v

def AlgebraicFunctionQ(u, x, flag=False):
    if ListQ(u):
        if u == []:
            return True
        elif AlgebraicFunctionQ(First(u), x, flag):
            return AlgebraicFunctionQ(Rest(u), x, flag)
        else:
            return False

    elif AtomQ(u) or FreeQ(u, x):
        return True
    elif PowerQ(u):
        if RationalQ(u.exp) | flag & FreeQ(u.exp, x):
            return AlgebraicFunctionQ(u.base, x, flag)
    elif ProductQ(u) | SumQ(u):
        for i in u.args:
            if not AlgebraicFunctionQ(i, x, flag):
                return False
        return True

    return False

def Coeff(expr, form, n=1):
    if n == 1:
        return Coefficient(Together(expr), form, n)
    else:
        coef1 = Coefficient(expr, form, n)
        coef2 = Coefficient(Together(expr), form, n)
        if Simplify(coef1 - coef2) == 0:
            return coef1
        else:
            return coef2

def LeadTerm(u):
    if SumQ(u):
        return First(u)
    return u

def RemainingTerms(u):
    if SumQ(u):
        return Rest(u)
    return u

def LeadFactor(u):
    # returns the leading factor of u.
    if ComplexNumberQ(u) and Re(u) == 0:
        if Im(u) == S(1):
            return u
        else:
            return LeadFactor(Im(u))
    elif ProductQ(u):
            return LeadFactor(First(u))
    return u

def RemainingFactors(u):
    # returns the remaining factors of u.
    if ComplexNumberQ(u) and Re(u) == 0:
        if Im(u) == 1:
            return S(1)
        else:
            return I*RemainingFactors(Im(u))
    elif ProductQ(u):
        return RemainingFactors(First(u))*Rest(u)
    return S(1)

def LeadBase(u):
    """
    returns the base of the leading factor of u.

    Examples
    ========

    >>> from sympy.integrals.rubi.utility_function import LeadBase
    >>> from sympy.abc import a, b, c
    >>> LeadBase(a**b)
    a
    >>> LeadBase(a**b*c)
    a
    """
    v = LeadFactor(u)
    if PowerQ(v):
        return v.base
    return v

def LeadDegree(u):
    # returns the degree of the leading factor of u.
    v = LeadFactor(u)
    if PowerQ(v):
        return v.exp
    return v

def Numer(expr):
    # returns the numerator of u.
    if PowerQ(expr):
        if expr.exp < 0:
            return 1
    if ProductQ(expr):
        return Mul(*[Numer(i) for i in expr.args])
    return Numerator(expr)

def Denom(u):
    # returns the denominator of u
    if PowerQ(u):
        if u.exp < 0:
            return u.args[0]**(-u.args[1])
    elif ProductQ(u):
        return Mul(*[Denom(i) for i in u.args])
    return Denominator(u)

def hypergeom(n, d, z):
    return hyper(n, d, z)

def Expon(expr, form):
    return Exponent(Together(expr), form)

def MergeMonomials(expr, x):
    u_ = Wild('u')
    p_ = Wild('p', exclude=[x, 1, 0])
    a_ = Wild('a', exclude=[x])
    b_ = Wild('b', exclude=[x, 0])
    c_ = Wild('c', exclude=[x])
    d_ = Wild('d', exclude=[x, 0])
    n_ = Wild('n', exclude=[x])
    m_ = Wild('m', exclude=[x])

    # Basis: If  m/n\[Element]\[DoubleStruckCapitalZ], then z^m (c z^n)^p==(c z^n)^(m/n+p)/c^(m/n)
    pattern = u_*(a_ + b_*x)**m_*(c_*(a_ + b_*x)**n_)**p_
    match = expr.match(pattern)
    if match:
        keys = [u_, a_, b_, m_, c_, n_, p_]
        if len(keys) == len(match):
            u, a, b, m, c, n, p = tuple([match[i] for i in keys])
            if IntegerQ(m/n):
                if u*(c*(a + b*x)**n)**(m/n + p)/c**(m/n) is S.NaN:
                    return expr
                else:
                    return u*(c*(a + b*x)**n)**(m/n + p)/c**(m/n)


    # Basis: If  m\[Element]\[DoubleStruckCapitalZ] \[And] b c-a d==0, then (a+b z)^m==b^m/d^m (c+d z)^m
    pattern = u_*(a_ + b_*x)**m_*(c_ + d_*x)**n_
    match = expr.match(pattern)
    if match:
        keys = [u_, a_, b_, m_, c_, d_, n_]
        if len(keys) == len(match):
            u, a, b, m, c, d, n = tuple([match[i] for i in keys])
            if IntegerQ(m) and ZeroQ(b*c - a*d):
                if u*b**m/d**m*(c + d*x)**(m + n) is S.NaN:
                    return expr
                else:
                    return u*b**m/d**m*(c + d*x)**(m + n)
    return expr

def PolynomialDivide(u, v, x):


    quo = PolynomialQuotient(u, v, x)
    rem = PolynomialRemainder(u, v, x)
    s = 0
    for i in ExponentList(quo, x):
        s += Simp(Together(Coefficient(quo, x, i)*x**i), x)
    quo = s
    rem = Together(rem)
    free = FreeFactors(rem, x)
    rem = NonfreeFactors(rem, x)
    monomial = x**Min(*ExponentList(rem, x))
    if NegQ(Coefficient(rem, x, 0)):
        monomial = -monomial
    s = 0
    for i in ExponentList(rem, x):
        s += Simp(Together(Coefficient(rem, x, i)*x**i/monomial), x)
    rem = s
    if BinomialQ(v, x):
        return quo + free*monomial*rem/ExpandToSum(v, x)
    else:
        return quo + free*monomial*rem/v



def BinomialQ(u, x, n=None):
    """
    If u is equivalent to an expression of the form a + b*x**n, BinomialQ(u, x, n) returns True, else it returns False.

    Examples
    ========

    >>> from sympy.integrals.rubi.utility_function import BinomialQ
    >>> from sympy.abc import x
    >>> BinomialQ(x**9, x)
    True
    >>> BinomialQ((1 + x)**3, x)
    False

    """
    if ListQ(u):
        for i in u:
            if Not(BinomialQ(i, x, n)):
                return False
        return True
    elif NumberQ(x):
        return False
    return ListQ(BinomialParts(u, x))

def TrinomialQ(u, x):
    """
    If u is equivalent to an expression of the form a + b*x**n + c*x**(2*n) where n, b and c are not 0,
    TrinomialQ(u, x) returns True, else it returns False.

    Examples
    ========

    >>> from sympy.integrals.rubi.utility_function import TrinomialQ
    >>> from sympy.abc import x
    >>> TrinomialQ((7 + 2*x**6 + 3*x**12), x)
    True
    >>> TrinomialQ(x**2, x)
    False

    """
    if ListQ(u):
        for i in u.args:
            if Not(TrinomialQ(i, x)):
                return False
        return True

    check = False
    u = replace_pow_exp(u)
    if PowerQ(u):
        if u.exp == 2 and BinomialQ(u.base, x):
            check = True

    return ListQ(TrinomialParts(u,x)) and Not(QuadraticQ(u, x)) and Not(check)

def GeneralizedBinomialQ(u, x):
    """
    If u is equivalent to an expression of the form a*x**q+b*x**n where n, q and b are not 0,
    GeneralizedBinomialQ(u, x) returns True, else it returns False.

    Examples
    ========

    >>> from sympy.integrals.rubi.utility_function import GeneralizedBinomialQ
    >>> from sympy.abc import a, x, q, b, n
    >>> GeneralizedBinomialQ(a*x**q, x)
    False

    """
    if ListQ(u):
        return all(GeneralizedBinomialQ(i, x) for i in u)
    return ListQ(GeneralizedBinomialParts(u, x))

def GeneralizedTrinomialQ(u, x):
    """
    If u is equivalent to an expression of the form a*x**q+b*x**n+c*x**(2*n-q) where n, q, b and c are not 0,
    GeneralizedTrinomialQ(u, x) returns True, else it returns False.

    Examples
    ========

    >>> from sympy.integrals.rubi.utility_function import GeneralizedTrinomialQ
    >>> from sympy.abc import x
    >>> GeneralizedTrinomialQ(7 + 2*x**6 + 3*x**12, x)
    False

    """
    if ListQ(u):
        return all(GeneralizedTrinomialQ(i, x) for i in u)
    return ListQ(GeneralizedTrinomialParts(u, x))

def FactorSquareFreeList(poly):
    r = sqf_list(poly)
    result = [[1, 1]]
    for i in r[1]:
        result.append(list(i))
    return result

def PerfectPowerTest(u, x):
    # If u (x) is equivalent to a polynomial raised to an integer power greater than 1,
    # PerfectPowerTest[u,x] returns u (x) as an expanded polynomial raised to the power;
    # else it returns False.
    if PolynomialQ(u, x):
        lst = FactorSquareFreeList(u)
        gcd = 0
        v = 1
        if lst[0] == [1, 1]:
            lst = Rest(lst)
        for i in lst:
            gcd = GCD(gcd, i[1])
        if gcd > 1:
            for i in lst:
                v = v*i[0]**(i[1]/gcd)
            return Expand(v)**gcd
        else:
            return False
    return False

def SquareFreeFactorTest(u, x):
    # If u (x) can be square free factored, SquareFreeFactorTest[u,x] returns u (x) in
    # factored form; else it returns False.
    if PolynomialQ(u, x):
        v = FactorSquareFree(u)
        if PowerQ(v) or ProductQ(v):
            return v
        return False
    return False

def RationalFunctionQ(u, x):
    # If u is a rational function of x, RationalFunctionQ[u,x] returns True; else it returns False.
    if AtomQ(u) or FreeQ(u, x):
        return True
    elif IntegerPowerQ(u):
        return RationalFunctionQ(u.base, x)
    elif ProductQ(u) or SumQ(u):
        for i in u.args:
            if Not(RationalFunctionQ(i, x)):
                return False
        return True
    return False

def RationalFunctionFactors(u, x):
    # RationalFunctionFactors[u,x] returns the product of the factors of u that are rational functions of x.
    if ProductQ(u):
        res = 1
        for i in u.args:
            if RationalFunctionQ(i, x):
                res *= i
        return res
    elif RationalFunctionQ(u, x):
        return u
    return S(1)

def NonrationalFunctionFactors(u, x):
    if ProductQ(u):
        res = 1
        for i in u.args:
            if not RationalFunctionQ(i, x):
                res *= i
        return res
    elif RationalFunctionQ(u, x):
        return S(1)
    return u

def Reverse(u):
    if isinstance(u, list):
        return list(reversed(u))
    else:
        l = list(u.args)
        return u.func(*list(reversed(l)))

def RationalFunctionExponents(u, x):
    """
    u is a polynomial or rational function of x.
    RationalFunctionExponents(u, x) returns a list of the exponent of the
    numerator of u and the exponent of the denominator of u.

    Examples
    ========
    >>> from sympy.integrals.rubi.utility_function import RationalFunctionExponents
    >>> from sympy.abc import x, a
    >>> RationalFunctionExponents(x, x)
    [1, 0]
    >>> RationalFunctionExponents(x**(-1), x)
    [0, 1]
    >>> RationalFunctionExponents(x**(-1)*a, x)
    [0, 1]

    """
    if PolynomialQ(u, x):
        return [Exponent(u, x), 0]
    elif IntegerPowerQ(u):
        if PositiveQ(u.exp):
            return u.exp*RationalFunctionExponents(u.base, x)
        return  (-u.exp)*Reverse(RationalFunctionExponents(u.base, x))
    elif ProductQ(u):
        lst1 = RationalFunctionExponents(First(u), x)
        lst2 = RationalFunctionExponents(Rest(u), x)
        return [lst1[0] + lst2[0], lst1[1] + lst2[1]]
    elif SumQ(u):
        v = Together(u)
        if SumQ(v):
            lst1 = RationalFunctionExponents(First(u), x)
            lst2 = RationalFunctionExponents(Rest(u), x)
            return [Max(lst1[0] + lst2[1], lst2[0] + lst1[1]), lst1[1] + lst2[1]]
        else:
            return RationalFunctionExponents(v, x)
    return [0, 0]

def RationalFunctionExpand(expr, x):
    # expr is a polynomial or rational function of x.
    # RationalFunctionExpand[u,x] returns the expansion of the factors of u that are rational functions times the other factors.
    def cons_f1(n):
        return FractionQ(n)
    cons1 = CustomConstraint(cons_f1)

    def cons_f2(x, v):
        if not isinstance(x, Symbol):
            return False
        return UnsameQ(v, x)
    cons2 = CustomConstraint(cons_f2)

    def With1(n, u, x, v):
        w = RationalFunctionExpand(u, x)
        return If(SumQ(w), Add(*[i*v**n for i in w.args]), v**n*w)
    pattern1 = Pattern(UtilityOperator(u_*v_**n_, x_), cons1, cons2)
    rule1 = ReplacementRule(pattern1, With1)
    def With2(u, x):
        v = ExpandIntegrand(u, x)

        def _consf_u(a, b, c, d, p, m, n, x):
            return And(FreeQ(List(a, b, c, d, p), x), IntegersQ(m, n), Equal(m, Add(n, S(-1))))
        cons_u = CustomConstraint(_consf_u)
        pat = Pattern(UtilityOperator(x_**WC('m', S(1))*(x_*WC('d', S(1)) + c_)**p_/(x_**n_*WC('b', S(1)) + a_), x_), cons_u)
        result_matchq = is_match(UtilityOperator(u, x), pat)
        if UnsameQ(v, u) and not result_matchq:
            return v
        else:
            v = ExpandIntegrand(RationalFunctionFactors(u, x), x)
            w = NonrationalFunctionFactors(u, x)
            if SumQ(v):
                return Add(*[i*w for i in v.args])
            else:
                return v*w
    pattern2 = Pattern(UtilityOperator(u_, x_))
    rule2 = ReplacementRule(pattern2, With2)
    expr = expr.replace(sym_exp, rubi_exp)
    res = replace_all(UtilityOperator(expr, x), [rule1, rule2])
    return replace_pow_exp(res)


def ExpandIntegrand(expr, x, extra=None):
    expr = replace_pow_exp(expr)
    if extra is not None:
        extra, x = x, extra
        w = ExpandIntegrand(extra, x)
        r = NonfreeTerms(w, x)
        if SumQ(r):
            result = [expr*FreeTerms(w, x)]
            for i in r.args:
                result.append(MergeMonomials(expr*i, x))
            return r.func(*result)
        else:
            return expr*FreeTerms(w, x) + MergeMonomials(expr*r, x)

    else:
        u_ = Wild('u', exclude=[0, 1])
        a_ = Wild('a', exclude=[x])
        b_ = Wild('b', exclude=[x, 0])
        F_ = Wild('F', exclude=[0])
        c_ = Wild('c', exclude=[x])
        d_ = Wild('d', exclude=[x, 0])
        n_ = Wild('n', exclude=[0, 1])
        pattern = u_*(a_ + b_*F_)**n_
        match = expr.match(pattern)
        if match:
            if MemberQ([asin, acos, asinh, acosh], match[F_].func):
                keys = [u_, a_, b_, F_, n_]
                if len(match) == len(keys):
                    u, a, b, F, n = tuple([match[i] for i in keys])
                    match = F.args[0].match(c_ + d_*x)
                    if match:
                        keys = c_, d_
                        if len(keys) == len(match):
                            c, d = tuple([match[i] for i in keys])
                            if PolynomialQ(u, x):
                                F = F.func
                                return ExpandLinearProduct((a + b*F(c + d*x))**n, u, c, d, x)

        expr = expr.replace(sym_exp, rubi_exp)
        res = replace_all(UtilityOperator(expr, x), ExpandIntegrand_rules, max_count = 1)
        return replace_pow_exp(res)


def SimplerQ(u, v):
    # If u is simpler than v, SimplerQ(u, v) returns True, else it returns False.  SimplerQ(u, u) returns False
    if IntegerQ(u):
        if IntegerQ(v):
            if Abs(u)==Abs(v):
                return v<0
            else:
                return Abs(u)<Abs(v)
        else:
            return True
    elif IntegerQ(v):
        return False
    elif FractionQ(u):
        if FractionQ(v):
            if Denominator(u) == Denominator(v):
                return SimplerQ(Numerator(u), Numerator(v))
            else:
                return Denominator(u)<Denominator(v)
        else:
            return True
    elif FractionQ(v):
        return False
    elif (Re(u)==0 or Re(u) == 0) and (Re(v)==0 or Re(v) == 0):
        return SimplerQ(Im(u), Im(v))
    elif ComplexNumberQ(u):
        if ComplexNumberQ(v):
            if Re(u) == Re(v):
                return SimplerQ(Im(u), Im(v))
            else:
                return SimplerQ(Re(u),Re(v))
        else:
            return False
    elif NumberQ(u):
        if NumberQ(v):
            return OrderedQ([u,v])
        else:
            return True
    elif NumberQ(v):
        return False
    elif AtomQ(u) or (Head(u) == re) or (Head(u) == im):
        if AtomQ(v) or (Head(u) == re) or (Head(u) == im):
            return OrderedQ([u,v])
        else:
            return True
    elif AtomQ(v) or (Head(u) == re) or (Head(u) == im):
        return False
    elif Head(u) == Head(v):
        if Length(u) == Length(v):
            for i in range(len(u.args)):
                if not u.args[i] == v.args[i]:
                    return SimplerQ(u.args[i], v.args[i])
            return False
        return Length(u) < Length(v)
    elif LeafCount(u) < LeafCount(v):
        return True
    elif LeafCount(v) < LeafCount(u):
        return False
    return Not(OrderedQ([v,u]))

def SimplerSqrtQ(u, v):
    # If Rt(u, 2) is simpler than Rt(v, 2), SimplerSqrtQ(u, v) returns True, else it returns False.  SimplerSqrtQ(u, u) returns False
    if NegativeQ(v) and Not(NegativeQ(u)):
        return True
    if NegativeQ(u) and Not(NegativeQ(v)):
        return False
    sqrtu = Rt(u, S(2))
    sqrtv = Rt(v, S(2))
    if IntegerQ(sqrtu):
        if IntegerQ(sqrtv):
            return sqrtu<sqrtv
        else:
            return True
    if IntegerQ(sqrtv):
        return False
    if RationalQ(sqrtu):
        if RationalQ(sqrtv):
            return sqrtu<sqrtv
        else:
            return True
    if RationalQ(sqrtv):
        return False
    if PosQ(u):
        if PosQ(v):
            return LeafCount(sqrtu)<LeafCount(sqrtv)
        else:
            return True
    if PosQ(v):
        return False
    if LeafCount(sqrtu)<LeafCount(sqrtv):
        return True
    if LeafCount(sqrtv)<LeafCount(sqrtu):
        return False
    else:
        return Not(OrderedQ([v, u]))

def SumSimplerQ(u, v):
    """
    If u + v is simpler than u, SumSimplerQ(u, v) returns True, else it returns False.
    If for every term w of v there is a term of u equal to n*w where n<-1/2, u + v will be simpler than u.

    Examples
    ========

    >>> from sympy.integrals.rubi.utility_function import SumSimplerQ
    >>> from sympy.abc import x
    >>> from sympy import S
    >>> SumSimplerQ(S(4 + x),S(3 + x**3))
    False

    """
    if RationalQ(u, v):
        if v == S(0):
            return False
        elif v > S(0):
            return u < -S(1)
        else:
            return u >= -v
    else:
        return SumSimplerAuxQ(Expand(u), Expand(v))

def BinomialDegree(u, x):
    # if u is a binomial. BinomialDegree[u,x] returns the degree of x in u.
    bp = BinomialParts(u, x)
    if bp == False:
        return bp
    return bp[2]

def TrinomialDegree(u, x):
    # If u is equivalent to a trinomial of the form a + b*x^n + c*x^(2*n) where n!=0, b!=0 and c!=0, TrinomialDegree[u,x] returns n
    t = TrinomialParts(u, x)
    if t:
        return t[3]
    return t

def CancelCommonFactors(u, v):
    def _delete_cases(a, b):
        # only for CancelCommonFactors
        lst = []
        deleted = False
        for i in a.args:
            if i == b and not deleted:
                deleted = True
                continue
            lst.append(i)
        return a.func(*lst)

    # CancelCommonFactors[u,v] returns {u',v'} are the noncommon factors of u and v respectively.
    if ProductQ(u):
        if ProductQ(v):
            if MemberQ(v, First(u)):
                return CancelCommonFactors(Rest(u), _delete_cases(v, First(u)))
            else:
                lst = CancelCommonFactors(Rest(u), v)
                return [First(u)*lst[0], lst[1]]
        else:
            if MemberQ(u, v):
                return [_delete_cases(u, v), 1]
            else:
                return[u, v]
    elif ProductQ(v):
        if MemberQ(v, u):
            return [1, _delete_cases(v, u)]
        else:
            return [u, v]
    return[u, v]

def SimplerIntegrandQ(u, v, x):
    lst = CancelCommonFactors(u, v)
    u1 = lst[0]
    v1 = lst[1]
    if Head(u1) == Head(v1) and Length(u1) == 1 and Length(v1) == 1:
        return SimplerIntegrandQ(u1.args[0], v1.args[0], x)
    if 4*LeafCount(u1) < 3*LeafCount(v1):
        return True
    if RationalFunctionQ(u1, x):
        if RationalFunctionQ(v1, x):
            t1 = 0
            t2 = 0
            for i in RationalFunctionExponents(u1, x):
                t1 += i
            for i in RationalFunctionExponents(v1, x):
                t2 += i
            return t1 < t2
        else:
            return True
    else:
        return False

def GeneralizedBinomialDegree(u, x):
    b = GeneralizedBinomialParts(u, x)
    if b:
        return b[2] - b[3]

def GeneralizedBinomialParts(expr, x):
    expr = Expand(expr)
    if GeneralizedBinomialMatchQ(expr, x):
        a = Wild('a', exclude=[x])
        b = Wild('b', exclude=[x])
        n = Wild('n', exclude=[x])
        q = Wild('q', exclude=[x])
        Match = expr.match(a*x**q + b*x**n)
        if Match and PosQ(Match[q] - Match[n]):
            return [Match[b], Match[a], Match[q], Match[n]]
    else:
        return False

def GeneralizedTrinomialDegree(u, x):
    t = GeneralizedTrinomialParts(u, x)
    if t:
        return t[3] - t[4]

def GeneralizedTrinomialParts(expr, x):
    expr = Expand(expr)
    if GeneralizedTrinomialMatchQ(expr, x):
        a = Wild('a', exclude=[x, 0])
        b = Wild('b', exclude=[x, 0])
        c = Wild('c', exclude=[x])
        n = Wild('n', exclude=[x, 0])
        q = Wild('q', exclude=[x])
        Match = expr.match(a*x**q + b*x**n+c*x**(2*n-q))
        if Match and expr.is_Add:
            return [Match[c], Match[b], Match[a], Match[n], 2*Match[n]-Match[q]]
    else:
        return False

def MonomialQ(u, x):
    # If u is of the form a*x^n where n!=0 and a!=0, MonomialQ[u,x] returns True; else False
    if isinstance(u, list):
        return all(MonomialQ(i, x) for i in u)
    else:
        a = Wild('a', exclude=[x])
        b = Wild('b', exclude=[x])
        re = u.match(a*x**b)
        if re:
            return True
    return False

def MonomialSumQ(u, x):
    # if u(x) is a sum and each term is free of x or an expression of the form a*x^n, MonomialSumQ(u, x) returns True; else it returns False
    if SumQ(u):
        for i in u.args:
            if Not(FreeQ(i, x) or MonomialQ(i, x)):
                return False
        return True

@doctest_depends_on(modules=('matchpy',))
def MinimumMonomialExponent(u, x):
    """
    u is sum whose terms are monomials.  MinimumMonomialExponent(u, x) returns the exponent of the term having the smallest exponent

    Examples
    ========

    >>> from sympy.integrals.rubi.utility_function import MinimumMonomialExponent
    >>> from sympy.abc import x
    >>> MinimumMonomialExponent(x**2 + 5*x**2 + 3*x**5, x)
    2
    >>> MinimumMonomialExponent(x**2 + 5*x**2 + 1, x)
    0
    """

    n =MonomialExponent(First(u), x)
    for i in u.args:
        if PosQ(n - MonomialExponent(i, x)):
            n = MonomialExponent(i, x)

    return n

def MonomialExponent(u, x):
    # u is a monomial. MonomialExponent(u, x) returns the exponent of x in u
    a = Wild('a', exclude=[x])
    b = Wild('b', exclude=[x])
    re = u.match(a*x**b)
    if re:
        return re[b]

def LinearMatchQ(u, x):
    # LinearMatchQ(u, x) returns True iff u matches patterns of the form a+b*x where a and b are free of x
    if isinstance(u, list):
        return all(LinearMatchQ(i, x) for i in u)
    else:
        a = Wild('a', exclude=[x])
        b = Wild('b', exclude=[x])
        re = u.match(a + b*x)
        if re:
            return True
    return False

def PowerOfLinearMatchQ(u, x):
    if isinstance(u, list):
        for i in u:
            if not PowerOfLinearMatchQ(i, x):
                return False
        return True
    else:
        a = Wild('a', exclude=[x])
        b = Wild('b', exclude=[x, 0])
        m = Wild('m', exclude=[x, 0])
        Match = u.match((a + b*x)**m)
        if Match:
            return True
        else:
            return False

def QuadraticMatchQ(u, x):
    if ListQ(u):
        return all(QuadraticMatchQ(i, x) for i in u)
    pattern1 = Pattern(UtilityOperator(x_**2*WC('c', 1) + x_*WC('b', 1) + WC('a', 0), x_), CustomConstraint(lambda a, b, c, x: FreeQ([a, b, c], x)))
    pattern2 = Pattern(UtilityOperator(x_**2*WC('c', 1) + WC('a', 0), x_), CustomConstraint(lambda a, c, x: FreeQ([a, c], x)))
    u1 = UtilityOperator(u, x)
    return is_match(u1, pattern1) or is_match(u1, pattern2)

def CubicMatchQ(u, x):
    if isinstance(u, list):
        return all(CubicMatchQ(i, x) for i in u)
    else:
        pattern1 = Pattern(UtilityOperator(x_**3*WC('d', 1) + x_**2*WC('c', 1) + x_*WC('b', 1) + WC('a', 0), x_), CustomConstraint(lambda a, b, c, d, x: FreeQ([a, b, c, d], x)))
        pattern2 = Pattern(UtilityOperator(x_**3*WC('d', 1) + x_*WC('b', 1) + WC('a', 0), x_), CustomConstraint(lambda a, b, d, x: FreeQ([a, b, d], x)))
        pattern3 = Pattern(UtilityOperator(x_**3*WC('d', 1) + x_**2*WC('c', 1) + WC('a', 0), x_), CustomConstraint(lambda a, c, d, x: FreeQ([a, c, d], x)))
        pattern4 = Pattern(UtilityOperator(x_**3*WC('d', 1) + WC('a', 0), x_), CustomConstraint(lambda a, d, x: FreeQ([a, d], x)))
        u1 = UtilityOperator(u, x)
        if is_match(u1, pattern1) or is_match(u1, pattern2) or is_match(u1, pattern3) or is_match(u1, pattern4):
            return True
        else:
            return False

def BinomialMatchQ(u, x):
    if isinstance(u, list):
        return all(BinomialMatchQ(i, x) for i in u)
    else:
        pattern = Pattern(UtilityOperator(x_**WC('n', S(1))*WC('b', S(1)) + WC('a', S(0)), x_) , CustomConstraint(lambda a, b, n, x: FreeQ([a,b,n],x)))
        u = UtilityOperator(u, x)
        return is_match(u, pattern)

def TrinomialMatchQ(u, x):
    if isinstance(u, list):
        return all(TrinomialMatchQ(i, x) for i in u)
    else:
        pattern = Pattern(UtilityOperator(x_**WC('j', S(1))*WC('c', S(1)) + x_**WC('n', S(1))*WC('b', S(1)) + WC('a', S(0)), x_) , CustomConstraint(lambda a, b, c, n, x: FreeQ([a, b, c, n], x)),  CustomConstraint(lambda j, n: ZeroQ(j-2*n) ))
        u = UtilityOperator(u, x)
        return is_match(u, pattern)

def GeneralizedBinomialMatchQ(u, x):
    if isinstance(u, list):
        return all(GeneralizedBinomialMatchQ(i, x) for i in u)
    else:
        a = Wild('a', exclude=[x, 0])
        b = Wild('b', exclude=[x, 0])
        n = Wild('n', exclude=[x, 0])
        q = Wild('q', exclude=[x, 0])
        Match = u.match(a*x**q + b*x**n)
        if Match and len(Match) == 4 and Match[q] != 0 and Match[n] != 0:
            return True
        else:
            return False

def GeneralizedTrinomialMatchQ(u, x):
    if isinstance(u, list):
        return all(GeneralizedTrinomialMatchQ(i, x) for i in u)
    else:
        a = Wild('a', exclude=[x, 0])
        b = Wild('b', exclude=[x, 0])
        n = Wild('n', exclude=[x, 0])
        c = Wild('c', exclude=[x, 0])
        q = Wild('q', exclude=[x, 0])
        Match = u.match(a*x**q + b*x**n + c*x**(2*n - q))
        if Match and len(Match) == 5 and 2*Match[n] - Match[q] != 0 and Match[n] != 0:
            return True
        else:
            return False

def QuotientOfLinearsMatchQ(u, x):
    if isinstance(u, list):
        return all(QuotientOfLinearsMatchQ(i, x) for i in u)
    else:
        a = Wild('a', exclude=[x])
        b = Wild('b', exclude=[x])
        d = Wild('d', exclude=[x])
        c = Wild('c', exclude=[x])
        e = Wild('e')
        Match = u.match(e*(a + b*x)/(c + d*x))
        if Match and len(Match) == 5:
            return True
        else:
            return False

def PolynomialTermQ(u, x):
    a = Wild('a', exclude=[x])
    n = Wild('n', exclude=[x])
    Match = u.match(a*x**n)
    if Match and IntegerQ(Match[n]) and Greater(Match[n], S(0)):
        return True
    else:
        return False

def PolynomialTerms(u, x):
    s = 0
    for i in u.args:
        if PolynomialTermQ(i, x):
            s = s + i
    return s

def NonpolynomialTerms(u, x):
    s = 0
    for i in u.args:
        if not PolynomialTermQ(i, x):
            s = s + i
    return s

def PseudoBinomialParts(u, x):
    if PolynomialQ(u, x) and Greater(Expon(u, x), S(2)):
        n = Expon(u, x)
        d = Rt(Coefficient(u, x, n), n)
        c =  d**(-n + S(1))*Coefficient(u, x, n + S(-1))/n
        a = Simplify(u - (c + d*x)**n)
        if NonzeroQ(a) and FreeQ(a, x):
            return [a, S(1), c, d, n]
        else:
            return False
    else:
        return False

def NormalizePseudoBinomial(u, x):
    lst = PseudoBinomialParts(u, x)
    if lst:
        return (lst[0] + lst[1]*(lst[2] + lst[3]*x)**lst[4])

def PseudoBinomialPairQ(u, v, x):
    lst1 = PseudoBinomialParts(u, x)
    if AtomQ(lst1):
        return False
    else:
        lst2 = PseudoBinomialParts(v, x)
        if AtomQ(lst2):
            return False
        else:
            return Drop(lst1, 2) == Drop(lst2, 2)

def PseudoBinomialQ(u, x):
    lst = PseudoBinomialParts(u, x)
    if lst:
        return True
    else:
        return False

def PolynomialGCD(f, g):
    return gcd(f, g)

def PolyGCD(u, v, x):
    # (* u and v are polynomials in x. *)
    # (* PolyGCD[u,v,x] returns the factors of the gcd of u and v dependent on x. *)
    return NonfreeFactors(PolynomialGCD(u, v), x)

def AlgebraicFunctionFactors(u, x, flag=False):
    # (* AlgebraicFunctionFactors[u,x] returns the product of the factors of u that are algebraic functions of x. *)
    if ProductQ(u):
        result = 1
        for i in u.args:
            if AlgebraicFunctionQ(i, x, flag):
                result *= i
        return result
    if AlgebraicFunctionQ(u, x, flag):
        return u
    return 1

def NonalgebraicFunctionFactors(u, x):
    """
    NonalgebraicFunctionFactors[u,x] returns the product of the factors of u that are not algebraic functions of x.

    Examples
    ========

    >>> from sympy.integrals.rubi.utility_function import NonalgebraicFunctionFactors
    >>> from sympy.abc import x
    >>> from sympy import sin
    >>> NonalgebraicFunctionFactors(sin(x), x)
    sin(x)
    >>> NonalgebraicFunctionFactors(x, x)
    1

    """
    if ProductQ(u):
        result = 1
        for i in u.args:
            if not AlgebraicFunctionQ(i, x):
                result *= i
        return result
    if AlgebraicFunctionQ(u, x):
        return 1
    return u

def QuotientOfLinearsP(u, x):
    if LinearQ(u, x):
        return True
    elif SumQ(u):
        if FreeQ(u.args[0], x):
            return QuotientOfLinearsP(Rest(u), x)
    elif LinearQ(Numerator(u), x) and LinearQ(Denominator(u), x):
        return True
    elif ProductQ(u):
        if FreeQ(First(u), x):
            return QuotientOfLinearsP(Rest(u), x)
    elif Numerator(u) == 1 and PowerQ(u):
        return QuotientOfLinearsP(Denominator(u), x)
    return u == x or FreeQ(u, x)

def QuotientOfLinearsParts(u, x):
    # If u is equivalent to an expression of the form (a+b*x)/(c+d*x), QuotientOfLinearsParts[u,x]
    #   returns the list {a, b, c, d}.
    if LinearQ(u, x):
        return [Coefficient(u, x, 0), Coefficient(u, x, 1), 1, 0]
    elif PowerQ(u):
        if Numerator(u) == 1:
            u = Denominator(u)
            r = QuotientOfLinearsParts(u, x)
            return [r[2], r[3], r[0], r[1]]
    elif SumQ(u):
        a = First(u)
        if FreeQ(a, x):
            u = Rest(u)
            r = QuotientOfLinearsParts(u, x)
            return [r[0] + a*r[2], r[1] + a*r[3], r[2], r[3]]
    elif ProductQ(u):
        a = First(u)
        if FreeQ(a, x):
            r = QuotientOfLinearsParts(Rest(u), x)
            return [a*r[0], a*r[1], r[2], r[3]]
        a = Numerator(u)
        d = Denominator(u)
        if LinearQ(a, x) and LinearQ(d, x):
            return [Coefficient(a, x, 0), Coefficient(a, x, 1), Coefficient(d, x, 0), Coefficient(d, x, 1)]
    elif u == x:
        return [0, 1, 1, 0]
    elif FreeQ(u, x):
        return [u, 0, 1, 0]
    return [u, 0, 1, 0]

def QuotientOfLinearsQ(u, x):
    # (*QuotientOfLinearsQ[u,x] returns True iff u is equivalent to an expression of the form (a+b x)/(c+d x) where b!=0 and d!=0.*)
    if ListQ(u):
        for i in u:
            if not QuotientOfLinearsQ(i, x):
                return False
        return True
    q = QuotientOfLinearsParts(u, x)
    return QuotientOfLinearsP(u, x) and NonzeroQ(q[1]) and NonzeroQ(q[3])

def Flatten(l):
    return flatten(l)

def Sort(u, r=False):
    return sorted(u, key=lambda x: x.sort_key(), reverse=r)

# (*Definition: A number is absurd if it is a rational number, a positive rational number raised to a fractional power, or a product of absurd numbers.*)
def AbsurdNumberQ(u):
    # (* AbsurdNumberQ[u] returns True if u is an absurd number, else it returns False. *)
    if PowerQ(u):
        v = u.exp
        u = u.base
        return RationalQ(u) and u > 0 and FractionQ(v)
    elif ProductQ(u):
        return all(AbsurdNumberQ(i) for i in u.args)
    return RationalQ(u)

def AbsurdNumberFactors(u):
    # (* AbsurdNumberFactors[u] returns the product of the factors of u that are absurd numbers. *)
    if AbsurdNumberQ(u):
        return u
    elif ProductQ(u):
        result = S(1)
        for i in u.args:
            if AbsurdNumberQ(i):
                result *= i
        return result
    return NumericFactor(u)

def NonabsurdNumberFactors(u):
    # (* NonabsurdNumberFactors[u] returns the product of the factors of u that are not absurd numbers. *)
    if AbsurdNumberQ(u):
        return S(1)
    elif ProductQ(u):
        result = 1
        for i in u.args:
            result *= NonabsurdNumberFactors(i)
        return result
    return NonnumericFactors(u)

def SumSimplerAuxQ(u, v):
    if SumQ(v):
        return (RationalQ(First(v)) or SumSimplerAuxQ(u,First(v))) and (RationalQ(Rest(v)) or SumSimplerAuxQ(u,Rest(v)))
    elif SumQ(u):
        return SumSimplerAuxQ(First(u), v) or SumSimplerAuxQ(Rest(u), v)
    else:
        return v!=0 and NonnumericFactors(u)==NonnumericFactors(v) and (NumericFactor(u)/NumericFactor(v)<-1/2 or NumericFactor(u)/NumericFactor(v)==-1/2 and NumericFactor(u)<0)

def Prepend(l1, l2):
    if not isinstance(l2, list):
        return [l2] + l1
    return l2 + l1

def Drop(lst, n):
    if isinstance(lst, list):
        if isinstance(n, list):
            lst = lst[:(n[0]-1)] + lst[n[1]:]
        elif n > 0:
            lst = lst[n:]
        elif n < 0:
            lst = lst[:-n]
        else:
            return lst
        return lst
    return lst.func(*[i for i in Drop(list(lst.args), n)])

def CombineExponents(lst):
    if Length(lst) < 2:
        return lst
    elif lst[0][0] == lst[1][0]:
        return CombineExponents(Prepend(Drop(lst,2),[lst[0][0], lst[0][1] + lst[1][1]]))
    return Prepend(CombineExponents(Rest(lst)), First(lst))

def FactorInteger(n, l=None):
    if isinstance(n, (int, Integer)):
        return sorted(factorint(n, limit=l).items())
    else:
        return sorted(factorrat(n, limit=l).items())

def FactorAbsurdNumber(m):
    # (* m must be an absurd number.  FactorAbsurdNumber[m] returns the prime factorization of m *)
    # (* as list of base-degree pairs where the bases are prime numbers and the degrees are rational. *)
    if RationalQ(m):
        return FactorInteger(m)
    elif PowerQ(m):
        r = FactorInteger(m.base)
        return [r[0], r[1]*m.exp]

    # CombineExponents[Sort[Flatten[Map[FactorAbsurdNumber,Apply[List,m]],1], Function[i1[[1]]<i2[[1]]]]]
    return list((m.as_base_exp(),))

def SubstForInverseFunction(*args):
    """
    SubstForInverseFunction(u, v, w, x) returns u with subexpressions equal to v replaced by x and x replaced by w.

    Examples
    ========

    >>> from sympy.integrals.rubi.utility_function import SubstForInverseFunction
    >>> from sympy.abc import x, a, b
    >>> SubstForInverseFunction(a, a, b, x)
    a
    >>> SubstForInverseFunction(x**a, x**a, b, x)
    x
    >>> SubstForInverseFunction(a*x**a, a, b, x)
    a*b**a

    """
    if len(args) == 3:
        u, v, x = args[0], args[1], args[2]
        return SubstForInverseFunction(u, v, (-Coefficient(v.args[0], x, 0) + InverseFunction(Head(v))(x))/Coefficient(v.args[0], x, 1), x)
    elif len(args) == 4:
        u, v, w, x = args[0], args[1], args[2], args[3]
        if AtomQ(u):
            if u == x:
                return w
            return u
        elif Head(u) == Head(v) and ZeroQ(u.args[0] - v.args[0]):
            return x
        res = [SubstForInverseFunction(i, v, w, x) for i in u.args]
        return u.func(*res)

def SubstForFractionalPower(u, v, n, w, x):
    # (* SubstForFractionalPower[u,v,n,w,x] returns u with subexpressions equal to v^(m/n) replaced
    # by x^m and x replaced by w. *)
    if AtomQ(u):
        if u == x:
            return w
        return u
    elif FractionalPowerQ(u):
        if ZeroQ(u.base - v):
            return x**(n*u.exp)
    res = [SubstForFractionalPower(i, v, n, w, x) for i in u.args]
    return u.func(*res)

def SubstForFractionalPowerOfQuotientOfLinears(u, x):
    # (* If u has a subexpression of the form ((a+b*x)/(c+d*x))^(m/n) where m and n>1 are integers,
    # SubstForFractionalPowerOfQuotientOfLinears[u,x] returns the list {v,n,(a+b*x)/(c+d*x),b*c-a*d} where v is u
    # with subexpressions of the form ((a+b*x)/(c+d*x))^(m/n) replaced by x^m and x replaced
    lst = FractionalPowerOfQuotientOfLinears(u, 1, False, x)
    if AtomQ(lst) or AtomQ(lst[1]):
        return False
    n = lst[0]
    tmp = lst[1]
    lst = QuotientOfLinearsParts(tmp, x)
    a, b, c, d = lst[0], lst[1], lst[2], lst[3]
    if ZeroQ(d):
        return False
    lst = Simplify(x**(n - 1)*SubstForFractionalPower(u, tmp, n, (-a + c*x**n)/(b - d*x**n), x)/(b - d*x**n)**2)
    return [NonfreeFactors(lst, x), n, tmp, FreeFactors(lst, x)*(b*c - a*d)]

def FractionalPowerOfQuotientOfLinears(u, n, v, x):
    # (* If u has a subexpression of the form ((a+b*x)/(c+d*x))^(m/n),
    # FractionalPowerOfQuotientOfLinears[u,1,False,x] returns {n,(a+b*x)/(c+d*x)}; else it returns False. *)
    if AtomQ(u) or FreeQ(u, x):
        return [n, v]
    elif CalculusQ(u):
        return False
    elif FractionalPowerQ(u):
        if QuotientOfLinearsQ(u.base, x) and Not(LinearQ(u.base, x)) and (FalseQ(v) or ZeroQ(u.base - v)):
            return [LCM(Denominator(u.exp), n), u.base]
    lst = [n, v]
    for i in u.args:
        lst = FractionalPowerOfQuotientOfLinears(i, lst[0], lst[1],x)
        if AtomQ(lst):
            return False
    return lst

def SubstForFractionalPowerQ(u, v, x):
    # (* If the substitution x=v^(1/n) will not complicate algebraic subexpressions of u,
    # SubstForFractionalPowerQ[u,v,x] returns True; else it returns False. *)
    if AtomQ(u) or FreeQ(u, x):
        return True
    elif FractionalPowerQ(u):
        return SubstForFractionalPowerAuxQ(u, v, x)
    return all(SubstForFractionalPowerQ(i, v, x) for i in u.args)

def SubstForFractionalPowerAuxQ(u, v, x):
    if AtomQ(u):
        return False
    elif FractionalPowerQ(u):
        if ZeroQ(u.base - v):
            return True
    return any(SubstForFractionalPowerAuxQ(i, v, x) for i in u.args)

def FractionalPowerOfSquareQ(u):
    # (* If a subexpression of u is of the form ((v+w)^2)^n where n is a fraction, *)
    # (* FractionalPowerOfSquareQ[u] returns (v+w)^2; else it returns False. *)
    if AtomQ(u):
        return False
    elif FractionalPowerQ(u):
        a_ = Wild('a', exclude=[0])
        b_ = Wild('b', exclude=[0])
        c_ = Wild('c', exclude=[0])
        match = u.base.match(a_*(b_ + c_)**(S(2)))
        if match:
            keys = [a_, b_, c_]
            if len(keys) == len(match):
                a, b, c = tuple(match[i] for i in keys)
                if NonsumQ(a):
                    return (b + c)**S(2)
    for i in u.args:
        tmp = FractionalPowerOfSquareQ(i)
        if Not(FalseQ(tmp)):
            return tmp
    return False

def FractionalPowerSubexpressionQ(u, v, w):
    # (* If a subexpression of u is of the form w^n where n is a fraction but not equal to v, *)
    # (* FractionalPowerSubexpressionQ[u,v,w] returns True; else it returns False. *)
    if AtomQ(u):
        return False
    elif FractionalPowerQ(u):
        if PositiveQ(u.base/w):
            return Not(u.base == v) and LeafCount(w) < 3*LeafCount(v)
    for i in u.args:
        if FractionalPowerSubexpressionQ(i, v, w):
            return True
    return False

def Apply(f, lst):
    return f(*lst)

def FactorNumericGcd(u):
    # (* FactorNumericGcd[u] returns u with the gcd of the numeric coefficients of terms of sums factored out. *)
    if PowerQ(u):
        if RationalQ(u.exp):
            return FactorNumericGcd(u.base)**u.exp
    elif ProductQ(u):
        res = [FactorNumericGcd(i) for i in u.args]
        return Mul(*res)
    elif SumQ(u):
        g = GCD([NumericFactor(i) for i in u.args])
        r = Add(*[i/g for i in u.args])
        return g*r
    return u

def MergeableFactorQ(bas, deg, v):
    # (* MergeableFactorQ[bas,deg,v] returns True iff bas equals the base of a factor of v or bas is a factor of every term of v. *)
    if bas == v:
        return RationalQ(deg + S(1)) and (deg + 1>=0 or RationalQ(deg) and deg>0)
    elif PowerQ(v):
        if bas == v.base:
            return RationalQ(deg+v.exp) and (deg+v.exp>=0 or RationalQ(deg) and deg>0)
        return SumQ(v.base) and IntegerQ(v.exp) and (Not(IntegerQ(deg) or IntegerQ(deg/v.exp))) and MergeableFactorQ(bas, deg/v.exp, v.base)
    elif ProductQ(v):
        return MergeableFactorQ(bas, deg, First(v)) or MergeableFactorQ(bas, deg, Rest(v))
    return SumQ(v) and MergeableFactorQ(bas, deg, First(v)) and MergeableFactorQ(bas, deg, Rest(v))

def MergeFactor(bas, deg, v):
    # (* If MergeableFactorQ[bas,deg,v], MergeFactor[bas,deg,v] return the product of bas^deg and v,
    # but with bas^deg merged into the factor of v whose base equals bas. *)
    if bas == v:
        return bas**(deg + 1)
    elif PowerQ(v):
        if bas == v.base:
            return bas**(deg + v.exp)
        return MergeFactor(bas, deg/v.exp, v.base**v.exp)
    elif ProductQ(v):
        if MergeableFactorQ(bas, deg, First(v)):
            return MergeFactor(bas, deg, First(v))*Rest(v)
        return First(v)*MergeFactor(bas, deg, Rest(v))
    return MergeFactor(bas, deg, First(v)) + MergeFactor(bas, deg, Rest(v))

def MergeFactors(u, v):
    # (* MergeFactors[u,v] returns the product of u and v, but with the mergeable factors of u merged into v. *)
    if ProductQ(u):
        return MergeFactors(Rest(u), MergeFactors(First(u), v))
    elif PowerQ(u):
        if MergeableFactorQ(u.base, u.exp, v):
            return MergeFactor(u.base, u.exp, v)
        elif RationalQ(u.exp) and u.exp < -1 and MergeableFactorQ(u.base, -S(1), v):
            return MergeFactors(u.base**(u.exp + 1), MergeFactor(u.base, -S(1), v))
        return u*v
    elif MergeableFactorQ(u, S(1), v):
        return MergeFactor(u, S(1), v)
    return u*v

def TrigSimplifyQ(u):
    # (* TrigSimplifyQ[u] returns True if TrigSimplify[u] actually simplifies u; else False. *)
    return ActivateTrig(u) != TrigSimplify(u)

def TrigSimplify(u):
    # (* TrigSimplify[u] returns a bottom-up trig simplification of u. *)
    return ActivateTrig(TrigSimplifyRecur(u))

def TrigSimplifyRecur(u):
    if AtomQ(u):
        return u
    return TrigSimplifyAux(u.func(*[TrigSimplifyRecur(i) for i in u.args]))

def Order(expr1, expr2):
    if expr1 == expr2:
        return 0
    elif expr1.sort_key() > expr2.sort_key():
        return -1
    return 1

def FactorOrder(u, v):
    if u == 1:
        if v == 1:
            return 0
        return -1
    elif v == 1:
        return 1
    return Order(u, v)

def Smallest(num1, num2=None):
    if num2 is None:
        lst = num1
        num = lst[0]
        for i in Rest(lst):
            num = Smallest(num, i)
        return num
    return Min(num1, num2)

def OrderedQ(l):
    return l == Sort(l)

def MinimumDegree(deg1, deg2):
    if RationalQ(deg1):
        if RationalQ(deg2):
            return Min(deg1, deg2)
        return deg1
    elif RationalQ(deg2):
        return deg2

    deg = Simplify(deg1- deg2)

    if RationalQ(deg):
        if deg > 0:
            return deg2
        return deg1
    elif OrderedQ([deg1, deg2]):
        return deg1
    return deg2

def PositiveFactors(u):
    # (* PositiveFactors[u] returns the positive factors of u *)
    if ZeroQ(u):
        return S(1)
    elif RationalQ(u):
        return Abs(u)
    elif PositiveQ(u):
        return u
    elif ProductQ(u):
        res = 1
        for i in u.args:
            res *= PositiveFactors(i)
        return res
    return 1

def Sign(u):
    return sign(u)

def NonpositiveFactors(u):
    # (* NonpositiveFactors[u] returns the nonpositive factors of u *)
    if ZeroQ(u):
        return u
    elif RationalQ(u):
        return Sign(u)
    elif PositiveQ(u):
        return S(1)
    elif ProductQ(u):
        res = S(1)
        for i in u.args:
            res *= NonpositiveFactors(i)
        return res
    return u

def PolynomialInAuxQ(u, v, x):
    if u == v:
        return True
    elif AtomQ(u):
        return u != x
    elif PowerQ(u):
        if PowerQ(v):
            if u.base == v.base:
                return PositiveIntegerQ(u.exp/v.exp)
        return PositiveIntegerQ(u.exp) and PolynomialInAuxQ(u.base, v, x)
    elif SumQ(u) or ProductQ(u):
        for i in u.args:
            if Not(PolynomialInAuxQ(i, v, x)):
                return False
        return True
    return False

def PolynomialInQ(u, v, x):
    """
    If u is a polynomial in v(x), PolynomialInQ(u, v, x) returns True, else it returns False.

    Examples
    ========

    >>> from sympy.integrals.rubi.utility_function import PolynomialInQ
    >>> from sympy.abc import x
    >>> from sympy import log, S
    >>> PolynomialInQ(S(1), log(x), x)
    True
    >>> PolynomialInQ(log(x), log(x), x)
    True
    >>> PolynomialInQ(1 + log(x)**2, log(x), x)
    True

    """
    return PolynomialInAuxQ(u, NonfreeFactors(NonfreeTerms(v, x), x), x)

def ExponentInAux(u, v, x):
    if u == v:
        return S(1)
    elif AtomQ(u):
        return S(0)
    elif PowerQ(u):
        if PowerQ(v):
            if u.base == v.base:
                return u.exp/v.exp
        return u.exp*ExponentInAux(u.base, v, x)
    elif ProductQ(u):
        return Add(*[ExponentInAux(i, v, x) for i in u.args])
    return Max(*[ExponentInAux(i, v, x) for i in u.args])

def ExponentIn(u, v, x):
    return ExponentInAux(u, NonfreeFactors(NonfreeTerms(v, x), x), x)

def PolynomialInSubstAux(u, v, x):
    if u == v:
        return x
    elif AtomQ(u):
        return u
    elif PowerQ(u):
        if PowerQ(v):
            if u.base == v.base:
                return x**(u.exp/v.exp)
        return PolynomialInSubstAux(u.base, v, x)**u.exp
    return u.func(*[PolynomialInSubstAux(i, v, x) for i in u.args])

def PolynomialInSubst(u, v, x):
    # If u is a polynomial in v[x], PolynomialInSubst[u,v,x] returns the polynomial u in x.
    w = NonfreeTerms(v, x)
    return ReplaceAll(PolynomialInSubstAux(u, NonfreeFactors(w, x), x), {x: x - FreeTerms(v, x)/FreeFactors(w, x)})

def Distrib(u, v):
    # Distrib[u,v] returns the sum of u times each term of v.
    if SumQ(v):
        return Add(*[u*i for i in v.args])
    return u*v

def DistributeDegree(u, m):
    # DistributeDegree[u,m] returns the product of the factors of u each raised to the mth degree.
    if AtomQ(u):
        return u**m
    elif PowerQ(u):
        return u.base**(u.exp*m)
    elif ProductQ(u):
        return Mul(*[DistributeDegree(i, m) for i in u.args])
    return u**m

def FunctionOfPower(*args):
    """
    FunctionOfPower[u,x] returns the gcd of the integer degrees of x in u.

    Examples
    ========

    >>> from sympy.integrals.rubi.utility_function import FunctionOfPower
    >>> from sympy.abc import x
    >>> FunctionOfPower(x, x)
    1
    >>> FunctionOfPower(x**3, x)
    3

    """
    if len(args) == 2:
        return FunctionOfPower(args[0], None, args[1])

    u, n, x = args

    if FreeQ(u, x):
        return n
    elif u == x:
        return S(1)
    elif PowerQ(u):
        if u.base == x and IntegerQ(u.exp):
            if n is None:
                return u.exp
            return GCD(n, u.exp)
    tmp = n
    for i in u.args:
        tmp = FunctionOfPower(i, tmp, x)
    return tmp

def DivideDegreesOfFactors(u, n):
    """
    DivideDegreesOfFactors[u,n] returns the product of the base of the factors of u raised to the degree of the factors divided by n.

    Examples
    ========

    >>> from sympy import S
    >>> from sympy.integrals.rubi.utility_function import DivideDegreesOfFactors
    >>> from sympy.abc import a, b
    >>> DivideDegreesOfFactors(a**b, S(3))
    a**(b/3)

    """
    if ProductQ(u):
        return Mul(*[LeadBase(i)**(LeadDegree(i)/n) for i in u.args])
    return LeadBase(u)**(LeadDegree(u)/n)

def MonomialFactor(u, x):
    # MonomialFactor[u,x] returns the list {n,v} where x^n*v==u and n is free of x.
    if AtomQ(u):
        if u == x:
            return [S(1), S(1)]
        return [S(0), u]
    elif PowerQ(u):
        if IntegerQ(u.exp):
            lst = MonomialFactor(u.base, x)
            return [lst[0]*u.exp, lst[1]**u.exp]
        elif u.base == x and FreeQ(u.exp, x):
            return [u.exp, S(1)]
        return [S(0), u]
    elif ProductQ(u):
        lst1 = MonomialFactor(First(u), x)
        lst2 = MonomialFactor(Rest(u), x)
        return [lst1[0] + lst2[0], lst1[1]*lst2[1]]
    elif SumQ(u):
        lst = [MonomialFactor(i, x) for i in u.args]
        deg = lst[0][0]
        for i in Rest(lst):
            deg = MinimumDegree(deg, i[0])
        if ZeroQ(deg) or RationalQ(deg) and deg < 0:
            return [S(0), u]
        return [deg, Add(*[x**(i[0] - deg)*i[1] for i in lst])]
    return [S(0), u]

def FullSimplify(expr):
    return Simplify(expr)

def FunctionOfLinearSubst(u, a, b, x):
    if FreeQ(u, x):
        return u
    elif LinearQ(u, x):
        tmp = Coefficient(u, x, 1)
        if tmp == b:
            tmp = S(1)
        else:
            tmp = tmp/b
        return Coefficient(u, x, S(0)) - a*tmp + tmp*x
    elif PowerQ(u):
        if FreeQ(u.base, x):
            return E**(FullSimplify(FunctionOfLinearSubst(Log(u.base)*u.exp, a, b, x)))
    lst = MonomialFactor(u, x)
    if ProductQ(u) and NonzeroQ(lst[0]):
        if RationalQ(LeadFactor(lst[1])) and LeadFactor(lst[1]) < 0:
            return  -FunctionOfLinearSubst(DivideDegreesOfFactors(-lst[1], lst[0])*x, a, b, x)**lst[0]
        return FunctionOfLinearSubst(DivideDegreesOfFactors(lst[1], lst[0])*x, a, b, x)**lst[0]
    return u.func(*[FunctionOfLinearSubst(i, a, b, x) for i in u.args])


def FunctionOfLinear(*args):
    # (* If u (x) is equivalent to an expression of the form f (a+b*x) and not the case that a==0 and
    # b==1, FunctionOfLinear[u,x] returns the list {f (x),a,b}; else it returns False. *)
    if len(args) == 2:
        u, x = args
        lst = FunctionOfLinear(u, False, False, x, False)
        if AtomQ(lst) or FalseQ(lst[0]) or (lst[0] == 0 and lst[1] == 1):
            return False
        return [FunctionOfLinearSubst(u, lst[0], lst[1], x), lst[0], lst[1]]
    u, a, b, x, flag = args
    if FreeQ(u, x):
        return [a, b]
    elif CalculusQ(u):
        return False
    elif LinearQ(u, x):
        if FalseQ(a):
            return [Coefficient(u, x, 0), Coefficient(u, x, 1)]
        lst = CommonFactors([b, Coefficient(u, x, 1)])
        if ZeroQ(Coefficient(u, x, 0)) and Not(flag):
            return [0, lst[0]]
        elif ZeroQ(b*Coefficient(u, x, 0) - a*Coefficient(u, x, 1)):
            return [a/lst[1], lst[0]]
        return [0, 1]
    elif PowerQ(u):
        if FreeQ(u.base, x):
            return FunctionOfLinear(Log(u.base)*u.exp, a, b, x, False)
    lst = MonomialFactor(u, x)
    if ProductQ(u) and NonzeroQ(lst[0]):
        if False and IntegerQ(lst[0]) and lst[0] != -1 and FreeQ(lst[1], x):
            if RationalQ(LeadFactor(lst[1])) and LeadFactor(lst[1]) < 0:
                return FunctionOfLinear(DivideDegreesOfFactors(-lst[1], lst[0])*x, a, b, x, False)
            return FunctionOfLinear(DivideDegreesOfFactors(lst[1], lst[0])*x, a, b, x, False)
        return False
    lst = [a, b]
    for i in u.args:
        lst = FunctionOfLinear(i, lst[0], lst[1], x, SumQ(u))
        if AtomQ(lst):
            return False
    return lst

def NormalizeIntegrand(u, x):
    v = NormalizeLeadTermSigns(NormalizeIntegrandAux(u, x))
    if v == NormalizeLeadTermSigns(u):
        return u
    else:
        return v

def NormalizeIntegrandAux(u, x):
    if SumQ(u):
        l = 0
        for i in u.args:
            l += NormalizeIntegrandAux(i, x)
        return l
    if ProductQ(MergeMonomials(u, x)):
        l = 1
        for i in MergeMonomials(u, x).args:
            l *= NormalizeIntegrandFactor(i, x)
        return l
    else:
        return NormalizeIntegrandFactor(MergeMonomials(u, x), x)

def NormalizeIntegrandFactor(u, x):
    if PowerQ(u):
        if FreeQ(u.exp, x):
            bas = NormalizeIntegrandFactorBase(u.base, x)
            deg = u.exp
            if IntegerQ(deg) and SumQ(bas):
                if all(MonomialQ(i, x) for i in bas.args):
                    mi = MinimumMonomialExponent(bas, x)
                    q = 0
                    for i in bas.args:
                        q += Simplify(i/x**mi)
                    return x**(mi*deg)*q**deg
                else:
                    return bas**deg
            else:
                return bas**deg
    if PowerQ(u):
        if FreeQ(u.base, x):
            return u.base**NormalizeIntegrandFactorBase(u.exp, x)
    bas = NormalizeIntegrandFactorBase(u, x)
    if SumQ(bas):
        if all(MonomialQ(i, x) for i in bas.args):
            mi = MinimumMonomialExponent(bas, x)
            z = 0
            for j in bas.args:
                z += j/x**mi
            return x**mi*z
        else:
            return bas
    else:
        return bas

def NormalizeIntegrandFactorBase(expr, x):
    m = Wild('m', exclude=[x])
    u = Wild('u')
    match = expr.match(x**m*u)
    if match and SumQ(u):
        l = 0
        for i in u.args:
            l += NormalizeIntegrandFactorBase((x**m*i), x)
        return l
    if BinomialQ(expr, x):
        if BinomialMatchQ(expr, x):
            return expr
        else:
            return ExpandToSum(expr, x)
    elif TrinomialQ(expr, x):
        if TrinomialMatchQ(expr, x):
            return expr
        else:
            return ExpandToSum(expr, x)
    elif ProductQ(expr):
        l = 1
        for i in expr.args:
            l *= NormalizeIntegrandFactor(i, x)
        return l
    elif PolynomialQ(expr, x) and Exponent(expr, x) <= 4:
        return ExpandToSum(expr, x)
    elif SumQ(expr):
        w = Wild('w')
        m = Wild('m', exclude=[x])
        v = TogetherSimplify(expr)
        if SumQ(v) or v.match(x**m*w) and SumQ(w) or LeafCount(v) > LeafCount(expr) + 2:
            return UnifySum(expr, x)
        else:
            return NormalizeIntegrandFactorBase(v, x)
    else:
        return expr

def NormalizeTogether(u):
    return NormalizeLeadTermSigns(Together(u))

def NormalizeLeadTermSigns(u):
    if ProductQ(u):
        t = 1
        for i in u.args:
            lst = SignOfFactor(i)
            if lst[0] == 1:
                t *= lst[1]
            else:
                t *= AbsorbMinusSign(lst[1])
        return t
    else:
        lst = SignOfFactor(u)
    if lst[0] == 1:
        return lst[1]
    else:
        return AbsorbMinusSign(lst[1])

def AbsorbMinusSign(expr, *x):
    m = Wild('m', exclude=[x])
    u = Wild('u')
    v = Wild('v')
    match = expr.match(u*v**m)
    if match:
        if len(match) == 3:
            if SumQ(match[v]) and OddQ(match[m]):
                return match[u]*(-match[v])**match[m]

    return -expr

def NormalizeSumFactors(u):
    if AtomQ(u):
        return u
    elif ProductQ(u):
        k = 1
        for i in u.args:
            k *= NormalizeSumFactors(i)
        return SignOfFactor(k)[0]*SignOfFactor(k)[1]
    elif SumQ(u):
        k = 0
        for i in u.args:
            k += NormalizeSumFactors(i)
        return k
    else:
        return u

def SignOfFactor(u):
    if RationalQ(u) and u < 0 or SumQ(u) and NumericFactor(First(u)) < 0:
        return [-1, -u]
    elif IntegerPowerQ(u):
        if SumQ(u.base) and NumericFactor(First(u.base)) < 0:
            return [(-1)**u.exp, (-u.base)**u.exp]
    elif ProductQ(u):
        k = 1
        h = 1
        for i in u.args:
            k *= SignOfFactor(i)[0]
            h *= SignOfFactor(i)[1]
        return [k, h]
    return [1, u]

def NormalizePowerOfLinear(u, x):
    v = FactorSquareFree(u)
    if PowerQ(v):
        if LinearQ(v.base, x) and FreeQ(v.exp, x):
            return ExpandToSum(v.base, x)**v.exp

    return ExpandToSum(v, x)

def SimplifyIntegrand(u, x):
    v = NormalizeLeadTermSigns(NormalizeIntegrandAux(Simplify(u), x))
    if 5*LeafCount(v) < 4*LeafCount(u):
        return v
    if v != NormalizeLeadTermSigns(u):
        return v
    else:
        return u

def SimplifyTerm(u, x):
    v = Simplify(u)
    w = Together(v)
    if LeafCount(v) < LeafCount(w):
        return NormalizeIntegrand(v, x)
    else:
        return NormalizeIntegrand(w, x)

def TogetherSimplify(u):
    v = Together(Simplify(Together(u)))
    return FixSimplify(v)

def SmartSimplify(u):
    v = Simplify(u)
    w = factor(v)
    if LeafCount(w) < LeafCount(v):
        v = w
    if Not(FalseQ(w == FractionalPowerOfSquareQ(v))) and FractionalPowerSubexpressionQ(u, w, Expand(w)):
        v = SubstForExpn(v, w, Expand(w))
    else:
        v = FactorNumericGcd(v)
    return FixSimplify(v)

def SubstForExpn(u, v, w):
    if u == v:
        return w
    if AtomQ(u):
        return u
    else:
        k = 0
        for i in u.args:
            k +=  SubstForExpn(i, v, w)
        return k

def ExpandToSum(u, *x):
    if len(x) == 1:
        x = x[0]
        expr = 0
        if PolyQ(S(u), x):
            for t in ExponentList(u, x):
                expr += Coeff(u, x, t)*x**t
            return expr
        if BinomialQ(u, x):
            i = BinomialParts(u, x)
            expr += i[0] + i[1]*x**i[2]
            return expr
        if TrinomialQ(u, x):
            i = TrinomialParts(u, x)
            expr += i[0] + i[1]*x**i[3] + i[2]*x**(2*i[3])
            return expr
        if GeneralizedBinomialMatchQ(u, x):
            i = GeneralizedBinomialParts(u, x)
            expr += i[0]*x**i[3] + i[1]*x**i[2]
            return expr
        if GeneralizedTrinomialMatchQ(u, x):
            i = GeneralizedTrinomialParts(u, x)
            expr += i[0]*x**i[4] + i[1]*x**i[3] + i[2]*x**(2*i[3]-i[4])
            return expr
        else:
            return Expand(u)
    else:
        v = x[0]
        x = x[1]
        w = ExpandToSum(v, x)
        r = NonfreeTerms(w, x)
        if SumQ(r):
            k = u*FreeTerms(w, x)
            for i in r.args:
                k += MergeMonomials(u*i, x)
            return k
        else:
            return u*FreeTerms(w, x) + MergeMonomials(u*r, x)

def UnifySum(u, x):
    if SumQ(u):
        t = 0
        lst = []
        for i in u.args:
            lst += [i]
        for j in UnifyTerms(lst, x):
            t += j
        return t
    else:
        return SimplifyTerm(u, x)

def UnifyTerms(lst, x):
    if lst==[]:
        return lst
    else:
        return UnifyTerm(First(lst), UnifyTerms(Rest(lst), x), x)

def UnifyTerm(term, lst, x):
    if lst==[]:
        return [term]
    tmp = Simplify(First(lst)/term)
    if FreeQ(tmp, x):
        return Prepend(Rest(lst), [(1+tmp)*term])
    else:
        return Prepend(UnifyTerm(term, Rest(lst), x), [First(lst)])

def CalculusQ(u):
    return False

def FunctionOfInverseLinear(*args):
    # (* If u is a function of an inverse linear binomial of the form 1/(a+b*x),
    # FunctionOfInverseLinear[u,x] returns the list {a,b}; else it returns False. *)
    if len(args) == 2:
        u, x = args
        return FunctionOfInverseLinear(u, None, x)
    u, lst, x = args

    if FreeQ(u, x):
        return lst
    elif u == x:
        return False
    elif QuotientOfLinearsQ(u, x):
        tmp = Drop(QuotientOfLinearsParts(u, x), 2)
        if tmp[1] == 0:
            return False
        elif lst is None:
            return tmp
        elif ZeroQ(lst[0]*tmp[1] - lst[1]*tmp[0]):
            return lst
        return False
    elif CalculusQ(u):
        return False
    tmp = lst
    for i in u.args:
        tmp = FunctionOfInverseLinear(i, tmp, x)
        if AtomQ(tmp):
            return False
    return tmp

def PureFunctionOfSinhQ(u, v, x):
    # (* If u is a pure function of Sinh[v] and/or Csch[v], PureFunctionOfSinhQ[u,v,x] returns True;
    # else it returns False. *)
    if AtomQ(u):
        return u != x
    elif CalculusQ(u):
        return False
    elif HyperbolicQ(u) and ZeroQ(u.args[0] - v):
        return SinhQ(u) or CschQ(u)
    for i in u.args:
        if Not(PureFunctionOfSinhQ(i, v, x)):
            return False
    return True

def PureFunctionOfTanhQ(u, v , x):
    # (* If u is a pure function of Tanh[v] and/or Coth[v], PureFunctionOfTanhQ[u,v,x] returns True;
    # else it returns False. *)
    if AtomQ(u):
        return u != x
    elif CalculusQ(u):
        return False
    elif HyperbolicQ(u) and ZeroQ(u.args[0] - v):
        return TanhQ(u) or CothQ(u)
    for i in u.args:
        if Not(PureFunctionOfTanhQ(i, v, x)):
            return False
    return True

def PureFunctionOfCoshQ(u, v, x):
    # (* If u is a pure function of Cosh[v] and/or Sech[v], PureFunctionOfCoshQ[u,v,x] returns True;
    # else it returns False. *)
    if AtomQ(u):
        return u != x
    elif CalculusQ(u):
        return False
    elif HyperbolicQ(u) and ZeroQ(u.args[0] - v):
        return CoshQ(u) or SechQ(u)
    for i in u.args:
        if Not(PureFunctionOfCoshQ(i, v, x)):
            return False
    return True

def IntegerQuotientQ(u, v):
    # (* If u/v is an integer, IntegerQuotientQ[u,v] returns True; else it returns False. *)
    return IntegerQ(Simplify(u/v))

def OddQuotientQ(u, v):
    # (* If u/v is odd, OddQuotientQ[u,v] returns True; else it returns False. *)
    return OddQ(Simplify(u/v))

def EvenQuotientQ(u, v):
    # (* If u/v is even, EvenQuotientQ[u,v] returns True; else it returns False. *)
    return EvenQ(Simplify(u/v))

def FindTrigFactor(func1, func2, u, v, flag):
    # (* If func[w]^m is a factor of u where m is odd and w is an integer multiple of v,
    # FindTrigFactor[func1,func2,u,v,True] returns the list {w,u/func[w]^n}; else it returns False. *)
    # (* If func[w]^m is a factor of u where m is odd and w is an integer multiple of v not equal to v,
    # FindTrigFactor[func1,func2,u,v,False] returns the list {w,u/func[w]^n}; else it returns False. *)
    if u == 1:
        return False
    elif (Head(LeadBase(u)) == func1 or Head(LeadBase(u)) == func2) and OddQ(LeadDegree(u)) and IntegerQuotientQ(LeadBase(u).args[0], v) and (flag or NonzeroQ(LeadBase(u).args[0] - v)):
        return [LeadBase[u].args[0], RemainingFactors(u)]
    lst = FindTrigFactor(func1, func2, RemainingFactors(u), v, flag)
    if AtomQ(lst):
        return False
    return [lst[0], LeadFactor(u)*lst[1]]

def FunctionOfSinhQ(u, v, x):
    # (* If u is a function of Sinh[v], FunctionOfSinhQ[u,v,x] returns True; else it returns False. *)
    if AtomQ(u):
        return u != x
    elif CalculusQ(u):
        return False
    elif HyperbolicQ(u) and IntegerQuotientQ(u.args[0], v):
        if OddQuotientQ(u.args[0], v):
            # (* Basis: If m odd, Sinh[m*v]^n is a function of Sinh[v]. *)
            return SinhQ(u) or CschQ(u)
        # (* Basis: If m even, Cos[m*v]^n is a function of Sinh[v]. *)
        return CoshQ(u) or SechQ(u)
    elif IntegerPowerQ(u):
        if HyperbolicQ(u.base) and IntegerQuotientQ(u.base.args[0], v):
            if EvenQ(u.exp):
                # (* Basis: If m integer and n even, Hyper[m*v]^n is a function of Sinh[v]. *)
                return True
            return FunctionOfSinhQ(u.base, v, x)
    elif ProductQ(u):
        if CoshQ(u.args[0]) and SinhQ(u.args[1]) and ZeroQ(u.args[0].args[0] - v/2) and ZeroQ(u.args[1].args[0] - v/2):
            return FunctionOfSinhQ(Drop(u, 2), v, x)
        lst = FindTrigFactor(Sinh, Csch, u, v, False)
        if ListQ(lst) and EvenQuotientQ(lst[0], v):
            # (* Basis: If m even and n odd, Sinh[m*v]^n == Cosh[v]*u where u is a function of Sinh[v]. *)
            return FunctionOfSinhQ(Cosh(v)*lst[1], v, x)
        lst = FindTrigFactor(Cosh, Sech, u, v, False)
        if ListQ(lst) and OddQuotientQ(lst[0], v):
            # (* Basis: If m odd and n odd, Cosh[m*v]^n == Cosh[v]*u where u is a function of Sinh[v]. *)
            return FunctionOfSinhQ(Cosh(v)*lst[1], v, x)
        lst = FindTrigFactor(Tanh, Coth, u, v, True)
        if ListQ(lst):
            # (* Basis: If m integer and n odd, Tanh[m*v]^n == Cosh[v]*u where u is a function of Sinh[v]. *)
            return FunctionOfSinhQ(Cosh(v)*lst[1], v, x)
        return all(FunctionOfSinhQ(i, v, x) for i in u.args)
    return all(FunctionOfSinhQ(i, v, x) for i in u.args)

def FunctionOfCoshQ(u, v, x):
    #(* If u is a function of Cosh[v], FunctionOfCoshQ[u,v,x] returns True; else it returns False. *)
    if AtomQ(u):
        return u != x
    elif CalculusQ(u):
        return False
    elif HyperbolicQ(u) and IntegerQuotientQ(u.args[0], v):
        # (* Basis: If m integer, Cosh[m*v]^n is a function of Cosh[v]. *)
        return CoshQ(u) or SechQ(u)
    elif IntegerPowerQ(u):
        if HyperbolicQ(u.base) and IntegerQuotientQ(u.base.args[0], v):
            if EvenQ(u.exp):
                # (* Basis: If m integer and n even, Hyper[m*v]^n is a function of Cosh[v]. *)
                return True
            return FunctionOfCoshQ(u.base, v, x)
    elif ProductQ(u):
        lst = FindTrigFactor(Sinh, Csch, u, v, False)
        if ListQ(lst):
            # (* Basis: If m integer and n odd, Sinh[m*v]^n == Sinh[v]*u where u is a function of Cosh[v]. *)
            return FunctionOfCoshQ(Sinh(v)*lst[1], v, x)
        lst = FindTrigFactor(Tanh, Coth, u, v, True)
        if ListQ(lst):
            # (* Basis: If m integer and n odd, Tanh[m*v]^n == Sinh[v]*u where u is a function of Cosh[v]. *)
            return FunctionOfCoshQ(Sinh(v)*lst[1], v, x)
        return all(FunctionOfCoshQ(i, v, x) for i in u.args)
    return all(FunctionOfCoshQ(i, v, x) for i in u.args)

def OddHyperbolicPowerQ(u, v, x):
    if SinhQ(u) or CoshQ(u) or SechQ(u) or CschQ(u):
        return OddQuotientQ(u.args[0], v)
    if PowerQ(u):
        return OddQ(u.exp) and OddHyperbolicPowerQ(u.base, v, x)
    if ProductQ(u):
        if Not(EqQ(FreeFactors(u, x), 1)):
            return OddHyperbolicPowerQ(NonfreeFactors(u, x), v, x)
        lst = []
        for i in u.args:
            if Not(FunctionOfTanhQ(i, v, x)):
                lst.append(i)
        if lst == []:
            return True
        return Length(lst)==1 and OddHyperbolicPowerQ(lst[0], v, x)
    if SumQ(u):
        return all(OddHyperbolicPowerQ(i, v, x) for i in u.args)
    return False

def FunctionOfTanhQ(u, v, x):
    #(* If u is a function of the form f[Tanh[v],Coth[v]] where f is independent of x,
    # FunctionOfTanhQ[u,v,x] returns True; else it returns False. *)
    if AtomQ(u):
        return u != x
    elif CalculusQ(u):
        return False
    elif HyperbolicQ(u) and IntegerQuotientQ(u.args[0], v):
        return TanhQ(u) or CothQ(u) or EvenQuotientQ(u.args[0], v)
    elif PowerQ(u):
        if EvenQ(u.exp) and HyperbolicQ(u.base) and IntegerQuotientQ(u.base.args[0], v):
            return True
        elif EvenQ(u.args[1]) and SumQ(u.args[0]):
            return FunctionOfTanhQ(Expand(u.args[0]**2, v, x))
    if ProductQ(u):
        lst = []
        for i in u.args:
            if Not(FunctionOfTanhQ(i, v, x)):
                lst.append(i)
        if lst == []:
            return True
        return Length(lst)==2 and OddHyperbolicPowerQ(lst[0], v, x) and OddHyperbolicPowerQ(lst[1], v, x)
    return all(FunctionOfTanhQ(i, v, x) for i in u.args)

def FunctionOfTanhWeight(u, v, x):
    """
    u is a function of the form f(tanh(v), coth(v)) where f is independent of x.
    FunctionOfTanhWeight(u, v, x) returns a nonnegative number if u is best considered a function of tanh(v), else it returns a negative number.

    Examples
    ========

    >>> from sympy import sinh, log, tanh
    >>> from sympy.abc import x
    >>> from sympy.integrals.rubi.utility_function import FunctionOfTanhWeight
    >>> FunctionOfTanhWeight(x, log(x), x)
    0
    >>> FunctionOfTanhWeight(sinh(log(x)), log(x), x)
    0
    >>> FunctionOfTanhWeight(tanh(log(x)), log(x), x)
    1

    """
    if AtomQ(u):
        return S(0)
    elif CalculusQ(u):
        return S(0)
    elif HyperbolicQ(u) and IntegerQuotientQ(u.args[0], v):
        if TanhQ(u) and ZeroQ(u.args[0] - v):
            return S(1)
        elif CothQ(u) and ZeroQ(u.args[0] - v):
            return S(-1)
        return S(0)
    elif PowerQ(u):
        if EvenQ(u.exp) and HyperbolicQ(u.base) and IntegerQuotientQ(u.base.args[0], v):
            if TanhQ(u.base) or CoshQ(u.base) or SechQ(u.base):
                return S(1)
            return S(-1)
    if ProductQ(u):
        if all(FunctionOfTanhQ(i, v, x) for i in u.args):
            return Add(*[FunctionOfTanhWeight(i, v, x) for i in u.args])
        return S(0)
    return Add(*[FunctionOfTanhWeight(i, v, x) for i in u.args])

def FunctionOfHyperbolicQ(u, v, x):
    # (* If u (x) is equivalent to a function of the form f (Sinh[v],Cosh[v],Tanh[v],Coth[v],Sech[v],Csch[v])
    # where f is independent of x, FunctionOfHyperbolicQ[u,v,x] returns True; else it returns False. *)
    if AtomQ(u):
        return u != x
    elif CalculusQ(u):
        return False
    elif HyperbolicQ(u) and IntegerQuotientQ(u.args[0], v):
        return True
    return all(FunctionOfHyperbolicQ(i, v, x) for i in u.args)

def SmartNumerator(expr):
    if PowerQ(expr):
        n = expr.exp
        u = expr.base
        if RationalQ(n) and n < 0:
            return SmartDenominator(u**(-n))
    elif ProductQ(expr):
        return Mul(*[SmartNumerator(i) for i in expr.args])
    return Numerator(expr)

def SmartDenominator(expr):
    if PowerQ(expr):
        u = expr.base
        n = expr.exp
        if RationalQ(n) and n < 0:
            return SmartNumerator(u**(-n))
    elif ProductQ(expr):
        return Mul(*[SmartDenominator(i) for i in expr.args])
    return Denominator(expr)

def ActivateTrig(u):
    return u

def ExpandTrig(*args):
    if len(args) == 2:
        u, x = args
        return ActivateTrig(ExpandIntegrand(u, x))
    u, v, x = args
    w = ExpandTrig(v, x)
    z = ActivateTrig(u)
    if SumQ(w):
        return w.func(*[z*i for i in w.args])
    return z*w

def TrigExpand(u):
    return expand_trig(u)

# SubstForTrig[u_,sin_,cos_,v_,x_] :=
#   If[AtomQ[u],
#     u,
#   If[TrigQ[u] && IntegerQuotientQ[u[[1]],v],
#     If[u[[1]]===v || ZeroQ[u[[1]]-v],
#       If[SinQ[u],
#         sin,
#       If[CosQ[u],
#         cos,
#       If[TanQ[u],
#         sin/cos,
#       If[CotQ[u],
#         cos/sin,
#       If[SecQ[u],
#         1/cos,
#       1/sin]]]]],
#     Map[Function[SubstForTrig[#,sin,cos,v,x]],
#             ReplaceAll[TrigExpand[Head[u][Simplify[u[[1]]/v]*x]],x->v]]],
#   If[ProductQ[u] && CosQ[u[[1]]] && SinQ[u[[2]]] && ZeroQ[u[[1,1]]-v/2] && ZeroQ[u[[2,1]]-v/2],
#     sin/2*SubstForTrig[Drop[u,2],sin,cos,v,x],
#   Map[Function[SubstForTrig[#,sin,cos,v,x]],u]]]]


def SubstForTrig(u, sin_ , cos_, v, x):
    # (* u (v) is an expression of the form f (Sin[v],Cos[v],Tan[v],Cot[v],Sec[v],Csc[v]). *)
    # (* SubstForTrig[u,sin,cos,v,x] returns the expression f (sin,cos,sin/cos,cos/sin,1/cos,1/sin). *)
    if AtomQ(u):
        return u
    elif TrigQ(u) and IntegerQuotientQ(u.args[0], v):
        if u.args[0] == v or ZeroQ(u.args[0] - v):
            if SinQ(u):
                return sin_
            elif CosQ(u):
                return cos_
            elif TanQ(u):
                return sin_/cos_
            elif CotQ(u):
                return cos_/sin_
            elif SecQ(u):
                return 1/cos_
            return 1/sin_
        r = ReplaceAll(TrigExpand(Head(u)(Simplify(u.args[0]/v*x))), {x: v})
        return r.func(*[SubstForTrig(i, sin_, cos_, v, x) for i in r.args])
    if ProductQ(u) and CosQ(u.args[0]) and SinQ(u.args[1]) and ZeroQ(u.args[0].args[0] - v/2) and ZeroQ(u.args[1].args[0] - v/2):
        return sin(x)/2*SubstForTrig(Drop(u, 2), sin_, cos_, v, x)
    return u.func(*[SubstForTrig(i, sin_, cos_, v, x) for i in u.args])

def SubstForHyperbolic(u, sinh_, cosh_, v, x):
    # (* u (v) is an expression of the form f (Sinh[v],Cosh[v],Tanh[v],Coth[v],Sech[v],Csch[v]). *)
    # (* SubstForHyperbolic[u,sinh,cosh,v,x] returns the expression
    # f (sinh,cosh,sinh/cosh,cosh/sinh,1/cosh,1/sinh). *)
    if AtomQ(u):
        return u
    elif HyperbolicQ(u) and IntegerQuotientQ(u.args[0], v):
        if u.args[0] == v or ZeroQ(u.args[0] - v):
            if SinhQ(u):
                return sinh_
            elif CoshQ(u):
                return cosh_
            elif TanhQ(u):
                return sinh_/cosh_
            elif CothQ(u):
                return cosh_/sinh_
            if SechQ(u):
                return 1/cosh_
            return 1/sinh_
        r = ReplaceAll(TrigExpand(Head(u)(Simplify(u.args[0]/v)*x)), {x: v})
        return r.func(*[SubstForHyperbolic(i, sinh_, cosh_, v, x) for i in r.args])
    elif ProductQ(u) and CoshQ(u.args[0]) and SinhQ(u.args[1]) and ZeroQ(u.args[0].args[0] - v/2) and ZeroQ(u.args[1].args[0] - v/2):
        return sinh(x)/2*SubstForHyperbolic(Drop(u, 2), sinh_, cosh_, v, x)
    return u.func(*[SubstForHyperbolic(i, sinh_, cosh_, v, x) for i in u.args])

def InertTrigFreeQ(u):
    return FreeQ(u, sin) and FreeQ(u, cos) and FreeQ(u, tan) and FreeQ(u, cot) and FreeQ(u, sec) and FreeQ(u, csc)

def LCM(a, b):
    return lcm(a, b)

def SubstForFractionalPowerOfLinear(u, x):
    # (* If u has a subexpression of the form (a+b*x)^(m/n) where m and n>1 are integers,
    # SubstForFractionalPowerOfLinear[u,x] returns the list {v,n,a+b*x,1/b} where v is u
    # with subexpressions of the form (a+b*x)^(m/n) replaced by x^m and x replaced
    # by -a/b+x^n/b, and all times x^(n-1); else it returns False. *)
    lst = FractionalPowerOfLinear(u, S(1), False, x)
    if AtomQ(lst) or FalseQ(lst[1]):
        return False
    n = lst[0]
    a = Coefficient(lst[1], x, 0)
    b = Coefficient(lst[1], x, 1)
    tmp = Simplify(x**(n-1)*SubstForFractionalPower(u, lst[1], n, -a/b + x**n/b, x))
    return [NonfreeFactors(tmp, x), n, lst[1], FreeFactors(tmp, x)/b]

def FractionalPowerOfLinear(u, n, v, x):
    # If u has a subexpression of the form (a + b*x)**(m/n), FractionalPowerOfLinear(u, 1, False, x) returns [n, a + b*x], else it returns False.
    if AtomQ(u) or FreeQ(u, x):
        return [n, v]
    elif CalculusQ(u):
        return False
    elif FractionalPowerQ(u):
        if LinearQ(u.base, x) and (FalseQ(v) or ZeroQ(u.base - v)):
            return [LCM(Denominator(u.exp), n), u.base]
    lst = [n, v]
    for i in u.args:
        lst = FractionalPowerOfLinear(i, lst[0], lst[1], x)
        if AtomQ(lst):
            return False
    return lst

def InverseFunctionOfLinear(u, x):
    # (* If u has a subexpression of the form g[a+b*x] where g is an inverse function,
    # InverseFunctionOfLinear[u,x] returns g[a+b*x]; else it returns False. *)
    if AtomQ(u) or CalculusQ(u) or FreeQ(u, x):
        return False
    elif InverseFunctionQ(u) and LinearQ(u.args[0], x):
        return u
    for i in u.args:
        tmp = InverseFunctionOfLinear(i, x)
        if Not(AtomQ(tmp)):
            return tmp
    return False

def InertTrigQ(*args):
    if len(args) == 1:
        f = args[0]
        l = [sin,cos,tan,cot,sec,csc]
        return any(Head(f) == i for i in l)
    elif len(args) == 2:
        f, g = args
        if f == g:
            return InertTrigQ(f)
        return InertReciprocalQ(f, g) or InertReciprocalQ(g, f)
    else:
        f, g, h = args
        return InertTrigQ(g, f) and InertTrigQ(g, h)

def InertReciprocalQ(f, g):
    return (f.func == sin and g.func == csc) or (f.func == cos and g.func == sec) or (f.func == tan and g.func == cot)

def DeactivateTrig(u, x):
    # (* u is a function of trig functions of a linear function of x. *)
    # (* DeactivateTrig[u,x] returns u with the trig functions replaced with inert trig functions. *)
    return FixInertTrigFunction(DeactivateTrigAux(u, x), x)

def FixInertTrigFunction(u, x):
    return u

def DeactivateTrigAux(u, x):
    if AtomQ(u):
        return u
    elif TrigQ(u) and LinearQ(u.args[0], x):
        v = ExpandToSum(u.args[0], x)
        if SinQ(u):
            return sin(v)
        elif CosQ(u):
            return cos(v)
        elif TanQ(u):
            return tan(u)
        elif CotQ(u):
            return cot(v)
        elif SecQ(u):
            return sec(v)
        return csc(v)
    elif HyperbolicQ(u) and LinearQ(u.args[0], x):
        v = ExpandToSum(I*u.args[0], x)
        if SinhQ(u):
            return -I*sin(v)
        elif CoshQ(u):
            return cos(v)
        elif TanhQ(u):
            return -I*tan(v)
        elif CothQ(u):
            I*cot(v)
        elif SechQ(u):
            return sec(v)
        return I*csc(v)
    return u.func(*[DeactivateTrigAux(i, x) for i in u.args])

def PowerOfInertTrigSumQ(u, func, x):
    p_ = Wild('p', exclude=[x])
    q_ = Wild('q', exclude=[x])
    a_ = Wild('a', exclude=[x])
    b_ = Wild('b', exclude=[x])
    c_ = Wild('c', exclude=[x])
    d_ = Wild('d', exclude=[x])
    n_ = Wild('n', exclude=[x])
    w_ = Wild('w')

    pattern = (a_ + b_*(c_*func(w_))**p_)**n_
    match = u.match(pattern)
    if match:
        keys = [a_, b_, c_, n_, p_, w_]
        if len(keys) == len(match):
            return True

    pattern = (a_ + b_*(d_*func(w_))**p_ + c_*(d_*func(w_))**q_)**n_
    match = u.match(pattern)
    if match:
        keys = [a_, b_, c_, d_, n_, p_, q_, w_]
        if len(keys) == len(match):
            return True
    return False

def PiecewiseLinearQ(*args):
    # (* If the derivative of u wrt x is a constant wrt x, PiecewiseLinearQ[u,x] returns True;
    # else it returns False. *)
    if len(args) == 3:
        u, v, x = args
        return PiecewiseLinearQ(u, x) and PiecewiseLinearQ(v, x)

    u, x = args
    if LinearQ(u, x):
        return True

    c_ = Wild('c', exclude=[x])
    F_ = Wild('F', exclude=[x])
    v_ = Wild('v')
    match = u.match(Log(c_*F_**v_))
    if match:
        if len(match) == 3:
            if LinearQ(match[v_], x):
                return True
    try:
        F = type(u)
        G = type(u.args[0])
        v = u.args[0].args[0]
        if LinearQ(v, x):
            if MemberQ([[atanh, tanh], [atanh, coth], [acoth, coth], [acoth, tanh], [atan, tan], [atan, cot], [acot, cot], [acot, tan]], [F, G]):
                return True
    except:
        pass
    return False

def KnownTrigIntegrandQ(lst, u, x):
    if u == 1:
        return True
    a_ = Wild('a', exclude=[x])
    b_ = Wild('b', exclude=[x, 0])
    func_ = WildFunction('func')
    m_ = Wild('m', exclude=[x])
    A_ = Wild('A', exclude=[x])
    B_ = Wild('B', exclude=[x, 0])
    C_ = Wild('C', exclude=[x, 0])

    match = u.match((a_ + b_*func_)**m_)
    if match:
        func = match[func_]
        if LinearQ(func.args[0], x) and MemberQ(lst, func.func):
            return True

    match = u.match((a_ + b_*func_)**m_*(A_ + B_*func_))
    if match:
        func = match[func_]
        if LinearQ(func.args[0], x) and MemberQ(lst, func.func):
            return True

    match = u.match(A_ + C_*func_**2)
    if match:
        func = match[func_]
        if LinearQ(func.args[0], x) and MemberQ(lst, func.func):
            return True

    match = u.match(A_ + B_*func_ + C_*func_**2)
    if match:
        func = match[func_]
        if LinearQ(func.args[0], x) and MemberQ(lst, func.func):
            return True

    match = u.match((a_ + b_*func_)**m_*(A_ + C_*func_**2))
    if match:
        func = match[func_]
        if LinearQ(func.args[0], x) and MemberQ(lst, func.func):
            return True

    match = u.match((a_ + b_*func_)**m_*(A_ + B_*func_ + C_*func_**2))
    if match:
        func = match[func_]
        if LinearQ(func.args[0], x) and MemberQ(lst, func.func):
            return True

    return False

def KnownSineIntegrandQ(u, x):
    return KnownTrigIntegrandQ([sin, cos], u, x)

def KnownTangentIntegrandQ(u, x):
    return KnownTrigIntegrandQ([tan], u, x)

def KnownCotangentIntegrandQ(u, x):
    return KnownTrigIntegrandQ([cot], u, x)

def KnownSecantIntegrandQ(u, x):
    return KnownTrigIntegrandQ([sec, csc], u, x)

def TryPureTanSubst(u, x):
    a_ = Wild('a', exclude=[x])
    b_ = Wild('b', exclude=[x])
    c_ = Wild('c', exclude=[x])
    G_ = Wild('G')

    F = u.func
    try:
        if MemberQ([atan, acot, atanh, acoth], F):
            match = u.args[0].match(c_*(a_ + b_*G_))
            if match:
                if len(match) == 4:
                    G = match[G_]
                    if MemberQ([tan, cot, tanh, coth], G.func):
                        if LinearQ(G.args[0], x):
                            return True
    except:
        pass

    return False

def TryTanhSubst(u, x):
    if LogQ(u):
        return False
    elif not FalseQ(FunctionOfLinear(u, x)):
        return False

    a_ = Wild('a', exclude=[x])
    m_ = Wild('m', exclude=[x])
    p_ = Wild('p', exclude=[x])
    r_, s_, t_, n_, b_, f_, g_ = map(Wild, 'rstnbfg')

    match = u.match(r_*(s_ + t_)**n_)
    if match:
        if len(match) == 4:
            r, s, t, n = [match[i] for i in [r_, s_, t_, n_]]
            if IntegerQ(n) and PositiveQ(n):
                return False

    match = u.match(1/(a_ + b_*f_**n_))
    if match:
        if len(match) == 4:
            a, b, f, n = [match[i] for i in [a_, b_, f_, n_]]
            if SinhCoshQ(f) and IntegerQ(n) and n > 2:
                return False

    match = u.match(f_*g_)
    if match:
        if len(match) == 2:
            f, g = match[f_], match[g_]
            if SinhCoshQ(f) and SinhCoshQ(g):
                if IntegersQ(f.args[0]/x, g.args[0]/x):
                    return False

    match = u.match(r_*(a_*s_**m_)**p_)
    if match:
        if len(match) == 5:
            r, a, s, m, p = [match[i] for i in [r_, a_, s_, m_, p_]]
            if Not(m==2 and (s == Sech(x) or s == Csch(x))):
                return False

    if u != ExpandIntegrand(u, x):
        return False

    return True

def TryPureTanhSubst(u, x):
    F = u.func
    a_ = Wild('a', exclude=[x])
    G_ = Wild('G')

    if F == sym_log:
        return False

    match = u.args[0].match(a_*G_)
    if match and len(match) == 2:
        G = match[G_].func
        if MemberQ([atanh, acoth], F) and MemberQ([tanh, coth], G):
            return False

    if u != ExpandIntegrand(u, x):
        return False

    return True

def AbsurdNumberGCD(*seq):
    # (* m, n, ... must be absurd numbers.  AbsurdNumberGCD[m,n,...] returns the gcd of m, n, ... *)
    lst = list(seq)
    if Length(lst) == 1:
        return First(lst)
    return AbsurdNumberGCDList(FactorAbsurdNumber(First(lst)), FactorAbsurdNumber(AbsurdNumberGCD(*Rest(lst))))

def AbsurdNumberGCDList(lst1, lst2):
    # (* lst1 and lst2 must be absurd number prime factorization lists. *)
    # (* AbsurdNumberGCDList[lst1,lst2] returns the gcd of the absurd numbers represented by lst1 and lst2. *)
    if lst1 == []:
        return Mul(*[i[0]**Min(i[1],0) for i in lst2])
    elif lst2 == []:
        return Mul(*[i[0]**Min(i[1],0) for i in lst1])
    elif lst1[0][0] == lst2[0][0]:
        if lst1[0][1] <= lst2[0][1]:
            return lst1[0][0]**lst1[0][1]*AbsurdNumberGCDList(Rest(lst1), Rest(lst2))
        return lst1[0][0]**lst2[0][1]*AbsurdNumberGCDList(Rest(lst1), Rest(lst2))
    elif lst1[0][0] < lst2[0][0]:
        if lst1[0][1] < 0:
            return lst1[0][0]**lst1[0][1]*AbsurdNumberGCDList(Rest(lst1), lst2)
        return AbsurdNumberGCDList(Rest(lst1), lst2)
    elif lst2[0][1] < 0:
        return lst2[0][0]**lst2[0][1]*AbsurdNumberGCDList(lst1, Rest(lst2))
    return AbsurdNumberGCDList(lst1, Rest(lst2))

def ExpandTrigExpand(u, F, v, m, n, x):
    w = Expand(TrigExpand(F.xreplace({x: n*x}))**m).xreplace({x: v})
    if SumQ(w):
        t = 0
        for i in w.args:
            t += u*i
        return t
    else:
        return u*w

def ExpandTrigReduce(*args):
    if len(args) == 3:
        u = args[0]
        v = args[1]
        x = args[2]
        w = ExpandTrigReduce(v, x)
        if SumQ(w):
            t = 0
            for i in w.args:
                t += u*i
            return t
        else:
            return u*w
    else:
        u = args[0]
        x = args[1]
        return ExpandTrigReduceAux(u, x)

def ExpandTrigReduceAux(u, x):
    v = TrigReduce(u).expand()
    if SumQ(v):
        t = 0
        for i in v.args:
            t += NormalizeTrig(i, x)
        return t
    return NormalizeTrig(v, x)

def NormalizeTrig(v, x):
    a = Wild('a', exclude=[x])
    n = Wild('n', exclude=[x, 0])
    F = Wild('F')
    expr = a*F**n
    M = v.match(expr)
    if M and len(M[F].args) == 1 and PolynomialQ(M[F].args[0], x) and Exponent(M[F].args[0], x) > 0:
        u = M[F].args[0]
        return M[a]*M[F].xreplace({u: ExpandToSum(u, x)})**M[n]
    else:
        return v
#=================================
def TrigToExp(expr):
    ex = expr.rewrite(sin, sym_exp).rewrite(cos, sym_exp).rewrite(tan, sym_exp).rewrite(sec, sym_exp).rewrite(csc, sym_exp).rewrite(cot, sym_exp)
    return ex.replace(sym_exp, rubi_exp)

def ExpandTrigToExp(u, *args):
    if len(args) == 1:
        x = args[0]
        return ExpandTrigToExp(1, u, x)
    else:
        v = args[0]
        x = args[1]
        w = TrigToExp(v)
        k = 0
        if SumQ(w):
            for i in w.args:
                k += SimplifyIntegrand(u*i, x)
            w = k
        else:
            w = SimplifyIntegrand(u*w, x)
        return ExpandIntegrand(FreeFactors(w, x), NonfreeFactors(w, x),x)
#======================================
def TrigReduce(i):
    """
    TrigReduce(expr) rewrites products and powers of trigonometric functions in expr in terms of trigonometric functions with combined arguments.

    Examples
    ========

    >>> from sympy import sin, cos
    >>> from sympy.integrals.rubi.utility_function import TrigReduce
    >>> from sympy.abc import x
    >>> TrigReduce(cos(x)**2)
    cos(2*x)/2 + 1/2
    >>> TrigReduce(cos(x)**2*sin(x))
    sin(x)/4 + sin(3*x)/4
    >>> TrigReduce(cos(x)**2+sin(x))
    sin(x) + cos(2*x)/2 + 1/2

    """
    if SumQ(i):
        t = 0
        for k in i.args:
            t += TrigReduce(k)
        return t
    if ProductQ(i):
        if any(PowerQ(k) for k in i.args):
            if (i.rewrite((sin, sinh), sym_exp).rewrite((cos, cosh), sym_exp).expand().rewrite(sym_exp, sin)).has(I, cosh, sinh):
                return i.rewrite((sin, sinh), sym_exp).rewrite((cos, cosh), sym_exp).expand().rewrite(sym_exp, sin).simplify()
            else:
                return i.rewrite((sin, sinh), sym_exp).rewrite((cos, cosh), sym_exp).expand().rewrite(sym_exp, sin)
        else:
            a = Wild('a')
            b = Wild('b')
            v = Wild('v')
            Match = i.match(v*sin(a)*cos(b))
            if Match:
                a = Match[a]
                b = Match[b]
                v = Match[v]
                return i.subs(v*sin(a)*cos(b), v*S(1)/2*(sin(a + b) + sin(a - b)))
            Match = i.match(v*sin(a)*sin(b))
            if Match:
                a = Match[a]
                b = Match[b]
                v = Match[v]
                return i.subs(v*sin(a)*sin(b), v*S(1)/2*cos(a - b) - cos(a + b))
            Match = i.match(v*cos(a)*cos(b))
            if Match:
                a = Match[a]
                b = Match[b]
                v = Match[v]
                return i.subs(v*cos(a)*cos(b), v*S(1)/2*cos(a + b) + cos(a - b))
            Match = i.match(v*sinh(a)*cosh(b))
            if Match:
                a = Match[a]
                b = Match[b]
                v = Match[v]
                return i.subs(v*sinh(a)*cosh(b), v*S(1)/2*(sinh(a + b) + sinh(a - b)))
            Match = i.match(v*sinh(a)*sinh(b))
            if Match:
                a = Match[a]
                b = Match[b]
                v = Match[v]
                return i.subs(v*sinh(a)*sinh(b), v*S(1)/2*cosh(a - b) - cosh(a + b))
            Match = i.match(v*cosh(a)*cosh(b))
            if Match:
                a = Match[a]
                b = Match[b]
                v = Match[v]
                return i.subs(v*cosh(a)*cosh(b), v*S(1)/2*cosh(a + b) + cosh(a - b))

    if PowerQ(i):
        if i.has(sin, sinh):
            if (i.rewrite((sin, sinh), sym_exp).expand().rewrite(sym_exp, sin)).has(I, cosh, sinh):
                return i.rewrite((sin, sinh), sym_exp).expand().rewrite(sym_exp, sin).simplify()
            else:
                return i.rewrite((sin, sinh), sym_exp).expand().rewrite(sym_exp, sin)
        if i.has(cos, cosh):
            if (i.rewrite((cos, cosh), sym_exp).expand().rewrite(sym_exp, cos)).has(I, cosh, sinh):
                return i.rewrite((cos, cosh), sym_exp).expand().rewrite(sym_exp, cos).simplify()
            else:
                return i.rewrite((cos, cosh), sym_exp).expand().rewrite(sym_exp, cos)
    return i

def FunctionOfTrig(u, *args):
    # If u is a function of trig functions of v where v is a linear function of x,
    # FunctionOfTrig[u,x] returns v; else it returns False.
    if len(args) == 1:
        x = args[0]
        v = FunctionOfTrig(u, None, x)
        if v:
            return v
        else:
            return False
    else:
        v, x = args
        if AtomQ(u):
            if u == x:
                return False
            else:
                return v
        if TrigQ(u) and LinearQ(u.args[0], x):
            if v is None:
                return u.args[0]
            else:
                a = Coefficient(v, x, 0)
                b = Coefficient(v, x, 1)
                c = Coefficient(u.args[0], x, 0)
                d = Coefficient(u.args[0], x, 1)
                if ZeroQ(a*d - b*c) and RationalQ(b/d):
                    return a/Numerator(b/d) + b*x/Numerator(b/d)
                else:
                    return False
        if HyperbolicQ(u) and LinearQ(u.args[0], x):
            if v is None:
                return I*u.args[0]
            a = Coefficient(v, x, 0)
            b = Coefficient(v, x, 1)
            c = I*Coefficient(u.args[0], x, 0)
            d = I*Coefficient(u.args[0], x, 1)
            if ZeroQ(a*d - b*c) and RationalQ(b/d):
                return a/Numerator(b/d) + b*x/Numerator(b/d)
            else:
                return False
        if CalculusQ(u):
            return False
        else:
            w = v
            for i in u.args:
                w = FunctionOfTrig(i, w, x)
                if FalseQ(w):
                    return False
            return w

def AlgebraicTrigFunctionQ(u, x):
    # If u is algebraic function of trig functions, AlgebraicTrigFunctionQ(u,x) returns True; else it returns False.
    if AtomQ(u):
        return True
    elif TrigQ(u) and LinearQ(u.args[0], x):
        return True
    elif HyperbolicQ(u) and LinearQ(u.args[0], x):
        return True
    elif PowerQ(u):
        if FreeQ(u.exp, x):
            return AlgebraicTrigFunctionQ(u.base, x)
    elif ProductQ(u) or SumQ(u):
        for i in u.args:
            if not AlgebraicTrigFunctionQ(i, x):
                return False
        return True

    return False

def FunctionOfHyperbolic(u, *x):
    # If u is a function of hyperbolic trig functions of v where v is linear in x,
    # FunctionOfHyperbolic(u,x) returns v; else it returns False.
    if len(x) == 1:
        x = x[0]
        v = FunctionOfHyperbolic(u, None, x)
        if v is None:
            return False
        else:
            return v
    else:
        v = x[0]
        x = x[1]
        if AtomQ(u):
            if u == x:
                return False
            return v
        if HyperbolicQ(u) and LinearQ(u.args[0], x):
            if v is None:
                return u.args[0]
            a = Coefficient(v, x, 0)
            b = Coefficient(v, x, 1)
            c = Coefficient(u.args[0], x, 0)
            d = Coefficient(u.args[0], x, 1)
            if ZeroQ(a*d - b*c) and RationalQ(b/d):
                return a/Numerator(b/d) + b*x/Numerator(b/d)
            else:
                return False
        if CalculusQ(u):
            return False
        w = v
        for i in u.args:
            if w == FunctionOfHyperbolic(i, w, x):
                return False
        return w

def FunctionOfQ(v, u, x, PureFlag=False):
    # v is a function of x. If u is a function of v,  FunctionOfQ(v, u, x) returns True; else it returns False. *)
    if FreeQ(u, x):
        return False
    elif AtomQ(v):
        return True
    elif ProductQ(v) and Not(EqQ(FreeFactors(v, x), 1)):
        return FunctionOfQ(NonfreeFactors(v, x), u, x, PureFlag)
    elif PureFlag:
        if SinQ(v) or CscQ(v):
            return PureFunctionOfSinQ(u, v.args[0], x)
        elif CosQ(v) or SecQ(v):
            return PureFunctionOfCosQ(u, v.args[0], x)
        elif TanQ(v):
            return PureFunctionOfTanQ(u, v.args[0], x)
        elif CotQ(v):
            return PureFunctionOfCotQ(u, v.args[0], x)
        elif SinhQ(v) or CschQ(v):
            return PureFunctionOfSinhQ(u, v.args[0], x)
        elif CoshQ(v) or SechQ(v):
            return PureFunctionOfCoshQ(u, v.args[0], x)
        elif TanhQ(v):
            return PureFunctionOfTanhQ(u, v.args[0], x)
        elif CothQ(v):
            return PureFunctionOfCothQ(u, v.args[0], x)
        else:
            return FunctionOfExpnQ(u, v, x) != False
    elif SinQ(v) or CscQ(v):
        return FunctionOfSinQ(u, v.args[0], x)
    elif CosQ(v) or SecQ(v):
        return FunctionOfCosQ(u, v.args[0], x)
    elif TanQ(v) or CotQ(v):
        FunctionOfTanQ(u, v.args[0], x)
    elif SinhQ(v) or CschQ(v):
        return FunctionOfSinhQ(u, v.args[0], x)
    elif CoshQ(v) or SechQ(v):
        return FunctionOfCoshQ(u, v.args[0], x)
    elif TanhQ(v) or CothQ(v):
        return FunctionOfTanhQ(u, v.args[0], x)
    return FunctionOfExpnQ(u, v, x) != False



def FunctionOfExpnQ(u, v, x):
    if u == v:
        return 1
    if AtomQ(u):
        if u == x:
            return False
        else:
            return 0
    if CalculusQ(u):
        return False
    if PowerQ(u):
        if FreeQ(u.exp, x):
            if ZeroQ(u.base - v):
                if IntegerQ(u.exp):
                    return u.exp
                else:
                    return 1
            if PowerQ(v):
                if FreeQ(v.exp, x) and ZeroQ(u.base-v.base):
                    if RationalQ(v.exp):
                        if RationalQ(u.exp) and IntegerQ(u.exp/v.exp) and (v.exp>0 or u.exp<0):
                            return u.exp/v.exp
                        else:
                            return False
                    if IntegerQ(Simplify(u.exp/v.exp)):
                        return Simplify(u.exp/v.exp)
                    else:
                        return False
            return FunctionOfExpnQ(u.base, v, x)
    if ProductQ(u) and Not(EqQ(FreeFactors(u, x), 1)):
        return FunctionOfExpnQ(NonfreeFactors(u, x), v, x)
    if ProductQ(u) and ProductQ(v):
        deg1 = FunctionOfExpnQ(First(u), First(v), x)
        if deg1==False:
            return False
        deg2 = FunctionOfExpnQ(Rest(u), Rest(v), x);
        if deg1==deg2 and FreeQ(Simplify(u/v^deg1), x):
            return deg1
        else:
            return False
    lst = []
    for i in u.args:
        if FunctionOfExpnQ(i, v, x) is False:
            return False
        lst.append(FunctionOfExpnQ(i, v, x))
    return Apply(GCD, lst)

def PureFunctionOfSinQ(u, v, x):
    # If u is a pure function of Sin(v) and/or Csc(v), PureFunctionOfSinQ(u, v, x) returns True; else it returns False.
    if AtomQ(u):
        return u!=x
    if CalculusQ(u):
        return False
    if TrigQ(u) and ZeroQ(u.args[0]-v):
        return SinQ(u) or CscQ(u)
    for i in u.args:
        if Not(PureFunctionOfSinQ(i, v, x)):
            return False
    return True

def PureFunctionOfCosQ(u, v, x):
    # If u is a pure function of Cos(v) and/or Sec(v), PureFunctionOfCosQ(u, v, x) returns True; else it returns False.
    if AtomQ(u):
        return u!=x
    if CalculusQ(u):
        return False
    if TrigQ(u) and ZeroQ(u.args[0]-v):
        return CosQ(u) or SecQ(u)
    for i in u.args:
        if Not(PureFunctionOfCosQ(i, v, x)):
            return False
    return True

def PureFunctionOfTanQ(u, v, x):
    # If u is a pure function of Tan(v) and/or Cot(v), PureFunctionOfTanQ(u, v, x) returns True; else it returns False.
    if AtomQ(u):
        return u!=x
    if CalculusQ(u):
        return False
    if TrigQ(u) and ZeroQ(u.args[0]-v):
        return TanQ(u) or CotQ(u)
    for i in u.args:
        if Not(PureFunctionOfTanQ(i, v, x)):
            return False
    return True

def PureFunctionOfCotQ(u, v, x):
    # If u is a pure function of Cot(v), PureFunctionOfCotQ(u, v, x) returns True; else it returns False.
    if AtomQ(u):
        return u!=x
    if CalculusQ(u):
        return False
    if TrigQ(u) and ZeroQ(u.args[0]-v):
        return CotQ(u)
    for i in u.args:
        if Not(PureFunctionOfCotQ(i, v, x)):
            return False
    return True

def FunctionOfCosQ(u, v, x):
    # If u is a function of Cos[v], FunctionOfCosQ[u,v,x] returns True; else it returns False.
    if AtomQ(u):
        return u != x
    elif CalculusQ(u):
        return False
    elif TrigQ(u) and IntegerQuotientQ(u.args[0], v):
        # Basis: If m integer, Cos[m*v]^n is a function of Cos[v]. *)
        return CosQ(u) or SecQ(u)
    elif IntegerPowerQ(u):
        if TrigQ(u.base) and IntegerQuotientQ(u.base.args[0], v):
            if EvenQ(u.exp):
                # Basis: If m integer and n even, Trig[m*v]^n is a function of Cos[v]. *)
                return True
            return FunctionOfCosQ(u.base, v, x)
    elif ProductQ(u):
        lst = FindTrigFactor(sin, csc, u, v, False)
        if ListQ(lst):
            # (* Basis: If m integer and n odd, Sin[m*v]^n == Sin[v]*u where u is a function of Cos[v]. *)
            return FunctionOfCosQ(Sin(v)*lst[1], v, x)
        lst = FindTrigFactor(tan, cot, u, v, True)
        if ListQ(lst):
            # (* Basis: If m integer and n odd, Tan[m*v]^n == Sin[v]*u where u is a function of Cos[v]. *)
            return FunctionOfCosQ(Sin(v)*lst[1], v, x)
        return all(FunctionOfCosQ(i, v, x) for i in u.args)
    return all(FunctionOfCosQ(i, v, x) for i in u.args)

def FunctionOfSinQ(u, v, x):
    # If u is a function of Sin[v], FunctionOfSinQ[u,v,x] returns True; else it returns False.
    if AtomQ(u):
        return u != x
    elif CalculusQ(u):
        return False
    elif TrigQ(u) and IntegerQuotientQ(u.args[0], v):
        if OddQuotientQ(u.args[0], v):
            # Basis: If m odd, Sin[m*v]^n is a function of Sin[v].
            return SinQ(u) or CscQ(u)
        # Basis: If m even, Cos[m*v]^n is a function of Sin[v].
        return CosQ(u) or SecQ(u)
    elif IntegerPowerQ(u):
        if TrigQ(u.base) and IntegerQuotientQ(u.base.args[0], v):
            if EvenQ(u.exp):
                # Basis: If m integer and n even, Hyper[m*v]^n is a function of Sin[v].
                return True
            return FunctionOfSinQ(u.base, v, x)
    elif ProductQ(u):
        if CosQ(u.args[0]) and SinQ(u.args[1]) and ZeroQ(u.args[0].args[0] - v/2) and ZeroQ(u.args[1].args[0] - v/2):
            return FunctionOfSinQ(Drop(u, 2), v, x)
        lst = FindTrigFactor(sin, csch, u, v, False)
        if ListQ(lst) and EvenQuotientQ(lst[0], v):
            # Basis: If m even and n odd, Sin[m*v]^n == Cos[v]*u where u is a function of Sin[v].
            return FunctionOfSinQ(Cos(v)*lst[1], v, x)
        lst = FindTrigFactor(cos, sec, u, v, False)
        if ListQ(lst) and OddQuotientQ(lst[0], v):
            # Basis: If m odd and n odd, Cos[m*v]^n == Cos[v]*u where u is a function of Sin[v].
            return FunctionOfSinQ(Cos(v)*lst[1], v, x)
        lst = FindTrigFactor(tan, cot, u, v, True)
        if ListQ(lst):
            # Basis: If m integer and n odd, Tan[m*v]^n == Cos[v]*u where u is a function of Sin[v].
            return FunctionOfSinQ(Cos(v)*lst[1], v, x)
        return all(FunctionOfSinQ(i, v, x) for i in u.args)
    return all(FunctionOfSinQ(i, v, x) for i in u.args)

def OddTrigPowerQ(u, v, x):
    if SinQ(u) or CosQ(u) or SecQ(u) or CscQ(u):
        return OddQuotientQ(u.args[0], v)
    if PowerQ(u):
        return OddQ(u.exp) and OddTrigPowerQ(u.base, v, x)
    if ProductQ(u):
        if not FreeFactors(u, x) == 1:
            return OddTrigPowerQ(NonfreeFactors(u, x), v, x)
        lst = []
        for i in u.args:
            if Not(FunctionOfTanQ(i, v, x)):
                lst.append(i)
        if lst == []:
            return True
        return Length(lst)==1 and OddTrigPowerQ(lst[0], v, x)
    if SumQ(u):
        return all(OddTrigPowerQ(i, v, x) for i in u.args)
    return False

def FunctionOfTanQ(u, v, x):
    # If u is a function of the form f[Tan[v],Cot[v]] where f is independent of x,
    # FunctionOfTanQ[u,v,x] returns True; else it returns False.
    if AtomQ(u):
        return u != x
    elif CalculusQ(u):
        return False
    elif TrigQ(u) and IntegerQuotientQ(u.args[0], v):
        return TanQ(u) or CotQ(u) or EvenQuotientQ(u.args[0], v)
    elif PowerQ(u):
        if EvenQ(u.exp) and TrigQ(u.base) and IntegerQuotientQ(u.base.args[0], v):
            return True
        elif EvenQ(u.exp) and SumQ(u.base):
            return FunctionOfTanQ(Expand(u.base**2, v, x))
    if ProductQ(u):
        lst = []
        for i in u.args:
            if Not(FunctionOfTanQ(i, v, x)):
                lst.append(i)
        if lst == []:
            return True
        return Length(lst)==2 and OddTrigPowerQ(lst[0], v, x) and OddTrigPowerQ(lst[1], v, x)
    return all(FunctionOfTanQ(i, v, x) for i in u.args)

def FunctionOfTanWeight(u, v, x):
    # (* u is a function of the form f[Tan[v],Cot[v]] where f is independent of x.
    # FunctionOfTanWeight[u,v,x] returns a nonnegative number if u is best considered a function
    # of Tan[v]; else it returns a negative number. *)
    if AtomQ(u):
        return S(0)
    elif CalculusQ(u):
        return S(0)
    elif TrigQ(u) and IntegerQuotientQ(u.args[0], v):
        if TanQ(u) and ZeroQ(u.args[0] - v):
            return S(1)
        elif CotQ(u) and ZeroQ(u.args[0] - v):
            return S(-1)
        return S(0)
    elif PowerQ(u):
        if EvenQ(u.exp) and TrigQ(u.base) and IntegerQuotientQ(u.base.args[0], v):
            if TanQ(u.base) or CosQ(u.base) or SecQ(u.base):
                return S(1)
            return S(-1)
    if ProductQ(u):
        if all(FunctionOfTanQ(i, v, x) for i in u.args):
            return Add(*[FunctionOfTanWeight(i, v, x) for i in u.args])
        return S(0)
    return Add(*[FunctionOfTanWeight(i, v, x) for i in u.args])

def FunctionOfTrigQ(u, v, x):
    # If u (x) is equivalent to a function of the form f (Sin[v],Cos[v],Tan[v],Cot[v],Sec[v],Csc[v]) where f is independent of x, FunctionOfTrigQ[u,v,x] returns True; else it returns False.
    if AtomQ(u):
        return u != x
    elif CalculusQ(u):
        return False
    elif TrigQ(u) and IntegerQuotientQ(u.args[0], v):
        return True
    return all(FunctionOfTrigQ(i, v, x) for i in u.args)

def FunctionOfDensePolynomialsQ(u, x):
    # If all occurrences of x in u (x) are in dense polynomials, FunctionOfDensePolynomialsQ[u,x] returns True; else it returns False.
    if FreeQ(u, x):
        return True
    if PolynomialQ(u, x):
        return Length(ExponentList(u, x)) > 1
    return all(FunctionOfDensePolynomialsQ(i, x) for i in u.args)

def FunctionOfLog(u, *args):
    # If u (x) is equivalent to an expression of the form f (Log[a*x^n]), FunctionOfLog[u,x] returns
    # the list {f (x),a*x^n,n}; else it returns False.
    if len(args) == 1:
        x = args[0]
        lst = FunctionOfLog(u, False, False, x)
        if AtomQ(lst) or FalseQ(lst[1]) or not isinstance(x, Symbol):
            return False
        else:
            return lst
    else:
        v = args[0]
        n = args[1]
        x = args[2]
        if AtomQ(u):
            if u==x:
                return False
            else:
                return [u, v, n]
        if CalculusQ(u):
            return False
        lst = BinomialParts(u.args[0], x)
        if LogQ(u) and ListQ(lst) and ZeroQ(lst[0]):
            if FalseQ(v) or u.args[0] == v:
                return [x, u.args[0], lst[2]]
            else:
                return False
        lst = [0, v, n]
        l = []
        for i in u.args:
                lst = FunctionOfLog(i, lst[1], lst[2], x)
                if AtomQ(lst):
                    return False
                else:
                    l.append(lst[0])

        return [u.func(*l), lst[1], lst[2]]

def PowerVariableExpn(u, m, x):
    # If m is an integer, u is an expression of the form f((c*x)**n) and g=GCD(m,n)>1,
    # PowerVariableExpn(u,m,x) returns the list {x**(m/g)*f((c*x)**(n/g)),g,c}; else it returns False.
    if IntegerQ(m):
        lst = PowerVariableDegree(u, m, 1, x)
        if not lst:
            return False
        else:
            return [x**(m/lst[0])*PowerVariableSubst(u, lst[0], x), lst[0], lst[1]]
    else:
        return False

def PowerVariableDegree(u, m, c, x):
    if FreeQ(u, x):
        return [m, c]
    if AtomQ(u) or CalculusQ(u):
        return False
    if PowerQ(u):
        if FreeQ(u.base/x, x):
            if ZeroQ(m) or m == u.exp and c == u.base/x:
                return [u.exp, u.base/x]
            if IntegerQ(u.exp) and IntegerQ(m) and GCD(m, u.exp)>1 and c==u.base/x:
                return [GCD(m, u.exp), c]
            else:
                return False
    lst = [m, c]
    for i in u.args:
        if PowerVariableDegree(i, lst[0], lst[1], x) == False:
            return False
        lst1 = PowerVariableDegree(i, lst[0], lst[1], x)
    if not lst1:
        return False
    else:
        return lst1

def PowerVariableSubst(u, m, x):
    if FreeQ(u, x) or AtomQ(u) or CalculusQ(u):
        return u
    if PowerQ(u):
        if FreeQ(u.base/x, x):
            return x**(u.exp/m)
    if ProductQ(u):
        l = 1
        for i in u.args:
            l *= (PowerVariableSubst(i, m, x))
        return l
    if SumQ(u):
        l = 0
        for i in u.args:
            l += (PowerVariableSubst(i, m, x))
        return l
    return u

def EulerIntegrandQ(expr, x):
    a = Wild('a', exclude=[x])
    b = Wild('b', exclude=[x])
    n = Wild('n', exclude=[x, 0])
    m = Wild('m', exclude=[x, 0])
    p = Wild('p', exclude=[x, 0])
    u = Wild('u')
    v = Wild('v')
    # Pattern 1
    M = expr.match((a*x + b*u**n)**p)
    if M:
        if len(M) == 5 and FreeQ([M[a], M[b]], x) and IntegerQ(M[n] + 1/2) and QuadraticQ(M[u], x) and Not(RationalQ(M[p])) or NegativeIntegerQ(M[p]) and Not(BinomialQ(M[u], x)):
            return True
    # Pattern 2
    M = expr.match(v**m*(a*x + b*u**n)**p)
    if M:
        if len(M) == 6 and FreeQ([M[a], M[b]], x) and ZeroQ(M[u] - M[v]) and IntegersQ(2*M[m], M[n] + 1/2) and QuadraticQ(M[u], x) and Not(RationalQ(M[p])) or NegativeIntegerQ(M[p]) and Not(BinomialQ(M[u], x)):
            return True
    # Pattern 3
    M = expr.match(u**n*v**p)
    if M:
        if len(M) == 3 and NegativeIntegerQ(M[p]) and IntegerQ(M[n] + 1/2) and QuadraticQ(M[u], x) and QuadraticQ(M[v], x) and Not(BinomialQ(M[v], x)):
            return True
    else:
        return False

def FunctionOfSquareRootOfQuadratic(u, *args):
    if len(args) == 1:
        x = args[0]
        pattern = Pattern(UtilityOperator(x_**WC('m', 1)*(a_ + x**WC('n', 1)*WC('b', 1))**p_, x), CustomConstraint(lambda a, b, m, n, p, x: FreeQ([a, b, m, n, p], x)))
        M = is_match(UtilityOperator(u, args[0]), pattern)
        if M:
            return False
        tmp = FunctionOfSquareRootOfQuadratic(u, False, x)
        if AtomQ(tmp) or FalseQ(tmp[0]):
            return False
        tmp = tmp[0]
        a = Coefficient(tmp, x, 0)
        b = Coefficient(tmp, x, 1)
        c = Coefficient(tmp, x, 2)
        if ZeroQ(a) and ZeroQ(b) or ZeroQ(b**2-4*a*c):
            return False
        if PosQ(c):
            sqrt = Rt(c, S(2));
            q = a*sqrt + b*x + sqrt*x**2
            r = b + 2*sqrt*x
            return [Simplify(SquareRootOfQuadraticSubst(u, q/r, (-a+x**2)/r, x)*q/r**2), Simplify(sqrt*x + Sqrt(tmp)), 2]
        if PosQ(a):
            sqrt = Rt(a, S(2))
            q = c*sqrt - b*x + sqrt*x**2
            r = c - x**2
            return [Simplify(SquareRootOfQuadraticSubst(u, q/r, (-b+2*sqrt*x)/r, x)*q/r**2), Simplify((-sqrt+Sqrt(tmp))/x), 1]
        sqrt = Rt(b**2 - 4*a*c, S(2))
        r = c - x**2
        return[Simplify(-sqrt*SquareRootOfQuadraticSubst(u, -sqrt*x/r, -(b*c+c*sqrt+(-b+sqrt)*x**2)/(2*c*r), x)*x/r**2), FullSimplify(2*c*Sqrt(tmp)/(b-sqrt+2*c*x)), 3]
    else:
        v = args[0]
        x = args[1]
        if AtomQ(u) or FreeQ(u, x):
            return [v]
        if PowerQ(u):
            if FreeQ(u.exp, x):
                if FractionQ(u.exp) and Denominator(u.exp) == 2 and PolynomialQ(u.base, x) and Exponent(u.base, x) == 2:
                    if FalseQ(v) or u.base == v:
                        return [u.base]
                    else:
                        return False
                return FunctionOfSquareRootOfQuadratic(u.base, v, x)
        if ProductQ(u) or SumQ(u):
            lst = [v]
            lst1 = []
            for i in u.args:
                if FunctionOfSquareRootOfQuadratic(i, lst[0], x) == False:
                    return False
                lst1 = FunctionOfSquareRootOfQuadratic(i, lst[0], x)
            return lst1
        else:
            return False

def SquareRootOfQuadraticSubst(u, vv, xx, x):
    # SquareRootOfQuadraticSubst(u, vv, xx, x) returns u with fractional powers replaced by vv raised to the power and x replaced by xx.
    if AtomQ(u) or FreeQ(u, x):
        if u==x:
            return xx
        return u
    if PowerQ(u):
        if FreeQ(u.exp, x):
            if FractionQ(u.exp) and Denominator(u.exp)==2 and PolynomialQ(u.base, x) and Exponent(u.base, x)==2:
                return vv**Numerator(u.exp)
            return SquareRootOfQuadraticSubst(u.base, vv, xx, x)**u.exp
    elif SumQ(u):
        t = 0
        for i in u.args:
            t += SquareRootOfQuadraticSubst(i, vv, xx, x)
        return t
    elif ProductQ(u):
        t = 1
        for i in u.args:
            t *= SquareRootOfQuadraticSubst(i, vv, xx, x)
        return t

def Divides(y, u, x):
    # If u divided by y is free of x, Divides[y,u,x] returns the quotient; else it returns False.
    v = Simplify(u/y)
    if FreeQ(v, x):
        return v
    else:
        return False

def DerivativeDivides(y, u, x):
    """
    If y not equal to x, y is easy to differentiate wrt x, and u divided by the derivative of y
    is free of x, DerivativeDivides[y,u,x] returns the quotient; else it returns False.
    """
    from matchpy import is_match
    pattern0 = Pattern(Mul(a , b_), CustomConstraint(lambda a, b : FreeQ(a, b)))
    def f1(y, u, x):
        if PolynomialQ(y, x):
            return PolynomialQ(u, x) and Exponent(u, x) == Exponent(y, x) - 1
        else:
            return EasyDQ(y, x)

    if is_match(y, pattern0):
        return False

    elif f1(y, u, x):
        v = D(y ,x)
        if EqQ(v, 0):
            return False
        else:
            v = Simplify(u/v)
            if FreeQ(v, x):
                return v
            else:
                return False
    else:
        return False


def EasyDQ(expr, x):
    # If u is easy to differentiate wrt x,  EasyDQ(u, x) returns True; else it returns False *)
    u = Wild('u',exclude=[1])
    m = Wild('m',exclude=[x, 0])
    M = expr.match(u*x**m)
    if M:
        return EasyDQ(M[u], x)
    if AtomQ(expr) or FreeQ(expr, x) or Length(expr)==0:
        return True
    elif CalculusQ(expr):
        return False
    elif Length(expr)==1:
        return EasyDQ(expr.args[0], x)
    elif BinomialQ(expr, x) or ProductOfLinearPowersQ(expr, x):
        return True
    elif RationalFunctionQ(expr, x) and RationalFunctionExponents(expr, x)==[1, 1]:
        return True
    elif ProductQ(expr):
        if FreeQ(First(expr), x):
            return EasyDQ(Rest(expr), x)
        elif FreeQ(Rest(expr), x):
            return EasyDQ(First(expr), x)
        else:
            return False
    elif SumQ(expr):
        return EasyDQ(First(expr), x) and EasyDQ(Rest(expr), x)
    elif Length(expr)==2:
        if FreeQ(expr.args[0], x):
            EasyDQ(expr.args[1], x)
        elif FreeQ(expr.args[1], x):
            return EasyDQ(expr.args[0], x)
        else:
            return False
    return False

def ProductOfLinearPowersQ(u, x):
    # ProductOfLinearPowersQ(u, x) returns True iff u is a product of factors of the form v^n where v is linear in x
    v = Wild('v')
    n = Wild('n', exclude=[x])
    M = u.match(v**n)
    return FreeQ(u, x) or M and LinearQ(M[v], x) or ProductQ(u) and ProductOfLinearPowersQ(First(u), x) and ProductOfLinearPowersQ(Rest(u), x)

def Rt(u, n):
    return RtAux(TogetherSimplify(u), n)

def NthRoot(u, n):
    return nsimplify(u**(S(1)/n))

def AtomBaseQ(u):
    # If u is an atom or an atom raised to an odd degree,  AtomBaseQ(u) returns True; else it returns False
    return AtomQ(u) or PowerQ(u) and OddQ(u.args[1]) and AtomBaseQ(u.args[0])

def SumBaseQ(u):
    # If u is a sum or a sum raised to an odd degree,  SumBaseQ(u) returns True; else it returns False
    return SumQ(u) or PowerQ(u) and OddQ(u.args[1]) and SumBaseQ(u.args[0])

def NegSumBaseQ(u):
    # If u is a sum or a sum raised to an odd degree whose lead term has a negative form,  NegSumBaseQ(u) returns True; else it returns False
    return SumQ(u) and NegQ(First(u)) or PowerQ(u) and OddQ(u.args[1]) and NegSumBaseQ(u.args[0])

def AllNegTermQ(u):
    # If all terms of u have a negative form, AllNegTermQ(u) returns True; else it returns False
    if PowerQ(u):
        if OddQ(u.exp):
            return AllNegTermQ(u.base)
    if SumQ(u):
        return NegQ(First(u)) and AllNegTermQ(Rest(u))
    return NegQ(u)

def SomeNegTermQ(u):
    # If some term of u has a negative form,  SomeNegTermQ(u) returns True; else it returns False
    if PowerQ(u):
        if OddQ(u.exp):
            return SomeNegTermQ(u.base)
    if SumQ(u):
        return NegQ(First(u)) or SomeNegTermQ(Rest(u))
    return NegQ(u)

def TrigSquareQ(u):
    # If u is an expression of the form Sin(z)^2 or Cos(z)^2,  TrigSquareQ(u) returns True,  else it returns False
    return PowerQ(u) and EqQ(u.args[1], 2) and MemberQ([sin, cos], Head(u.args[0]))

def RtAux(u, n):
    if PowerQ(u):
        return u.base**(u.exp/n)
    if ComplexNumberQ(u):
        a = Re(u)
        b = Im(u)
        if Not(IntegerQ(a) and IntegerQ(b)) and IntegerQ(a/(a**2 + b**2)) and IntegerQ(b/(a**2 + b**2)):
            # Basis: a+b*I==1/(a/(a^2+b^2)-b/(a^2+b^2)*I)
            return S(1)/RtAux(a/(a**2 + b**2) - b/(a**2 + b**2)*I, n)
        else:
            return NthRoot(u, n)
    if ProductQ(u):
        lst = SplitProduct(PositiveQ, u)
        if ListQ(lst):
            return RtAux(lst[0], n)*RtAux(lst[1], n)
        lst = SplitProduct(NegativeQ, u)
        if ListQ(lst):
            if EqQ(lst[0], -1):
                v = lst[1]
                if PowerQ(v):
                    if NegativeQ(v.exp):
                        return 1/RtAux(-v.base**(-v.exp), n)
                if ProductQ(v):
                    if ListQ(SplitProduct(SumBaseQ, v)):
                        lst = SplitProduct(AllNegTermQ, v)
                        if ListQ(lst):
                            return RtAux(-lst[0], n)*RtAux(lst[1], n)
                        lst = SplitProduct(NegSumBaseQ, v)
                        if ListQ(lst):
                            return RtAux(-lst[0], n)*RtAux(lst[1], n)
                        lst = SplitProduct(SomeNegTermQ, v)
                        if ListQ(lst):
                            return RtAux(-lst[0], n)*RtAux(lst[1], n)
                        lst = SplitProduct(SumBaseQ, v)
                        return RtAux(-lst[0], n)*RtAux(lst[1], n)
                    lst = SplitProduct(AtomBaseQ, v)
                    if ListQ(lst):
                        return RtAux(-lst[0], n)*RtAux(lst[1], n)
                    else:
                        return RtAux(-First(v), n)*RtAux(Rest(v), n)
                if OddQ(n):
                    return -RtAux(v, n)
                else:
                    return NthRoot(u, n)
            else:
                return RtAux(-lst[0], n)*RtAux(-lst[1], n)
        lst = SplitProduct(AllNegTermQ, u)
        if ListQ(lst) and ListQ(SplitProduct(SumBaseQ, lst[1])):
            return RtAux(-lst[0], n)*RtAux(-lst[1], n)
        lst = SplitProduct(NegSumBaseQ, u)
        if ListQ(lst) and ListQ(SplitProduct(NegSumBaseQ, lst[1])):
            return RtAux(-lst[0], n)*RtAux(-lst[1], n)
        return u.func(*[RtAux(i, n) for i in u.args])
    v = TrigSquare(u)
    if Not(AtomQ(v)):
        return RtAux(v, n)
    if OddQ(n) and NegativeQ(u):
        return -RtAux(-u, n)
    if OddQ(n) and NegQ(u) and PosQ(-u):
        return -RtAux(-u, n)
    else:
        return NthRoot(u, n)

def TrigSquare(u):
    # If u is an expression of the form a-a*Sin(z)^2 or a-a*Cos(z)^2, TrigSquare(u) returns Cos(z)^2 or Sin(z)^2 respectively,
    # else it returns False.
    if SumQ(u):
        for i in u.args:
            v = SplitProduct(TrigSquareQ, i)
            if v == False or SplitSum(v, u) == False:
                return False
            lst = SplitSum(SplitProduct(TrigSquareQ, i))
        if lst and ZeroQ(lst[1][2] + lst[1]):
            if Head(lst[0][0].args[0]) == sin:
                return lst[1]*cos(lst[1][1][1][1])**2
            return lst[1]*sin(lst[1][1][1][1])**2
        else:
            return False
    else:
        return False

def IntSum(u, x):
    # If u is free of x or of the form c*(a+b*x)^m, IntSum[u,x] returns the antiderivative of u wrt x;
    # else it returns d*Int[v,x] where d*v=u and d is free of x.
    return Add(*[Integral(i, x) for i in u.args])

def IntTerm(expr, x):
    # If u is of the form c*(a+b*x)**m, IntTerm(u,x) returns the antiderivative of u wrt x;
    # else it returns d*Int(v,x) where d*v=u and d is free of x.
    c = Wild('c', exclude=[x])
    m = Wild('m', exclude=[x, 0])
    v = Wild('v')
    M = expr.match(c/v)
    if M and len(M) == 2 and FreeQ(M[c], x) and LinearQ(M[v], x):
        return Simp(M[c]*Log(RemoveContent(M[v], x))/Coefficient(M[v], x, 1), x)
    M = expr.match(c*v**m)
    if M and len(M) == 3 and NonzeroQ(M[m] + 1) and LinearQ(M[v], x):
        return Simp(M[c]*M[v]**(M[m] + 1)/(Coefficient(M[v], x, 1)*(M[m] + 1)), x)
    if SumQ(expr):
        t = 0
        for i in expr.args:
            t += IntTerm(i, x)
        return t
    else:
        u = expr
        return Dist(FreeFactors(u,x), Integral(NonfreeFactors(u, x), x), x)

def Map2(f, lst1, lst2):
    result = []
    for i in range(0, len(lst1)):
        result.append(f(lst1[i], lst2[i]))
    return result

def ConstantFactor(u, x):
    # (* ConstantFactor[u,x] returns a 2-element list of the factors of u[x] free of x and the
    # factors not free of u[x].  Common constant factors of the terms of sums are also collected. *)
    if FreeQ(u, x):
        return [u, S(1)]
    elif AtomQ(u):
        return [S(1), u]
    elif PowerQ(u):
        if FreeQ(u.exp, x):
            lst = ConstantFactor(u.base, x)
            if IntegerQ(u.exp):
                return [lst[0]**u.exp, lst[1]**u.exp]
            tmp = PositiveFactors(lst[0])
            if tmp == 1:
                return [S(1), u]
            return [tmp**u.exp, (NonpositiveFactors(lst[0])*lst[1])**u.exp]
    elif ProductQ(u):
        lst = [ConstantFactor(i, x) for i in u.args]
        return [Mul(*[First(i) for i in lst]), Mul(*[i[1] for i in lst])]
    elif SumQ(u):
        lst1 = [ConstantFactor(i, x) for i in u.args]
        if SameQ(*[i[1] for i in lst1]):
            return [Add(*[i[0] for i in lst]), lst1[0][1]]
        lst2 = CommonFactors([First(i) for i in lst1])
        return [First(lst2), Add(*Map2(Mul, Rest(lst2), [i[1] for i in lst1]))]
    return [S(1), u]

def SameQ(*args):
    for i in range(0, len(args) - 1):
        if args[i] != args[i+1]:
            return False
    return True

def ReplacePart(lst, a, b):
    lst[b] = a
    return lst

def CommonFactors(lst):
    # (* If lst is a list of n terms, CommonFactors[lst] returns a n+1-element list whose first
    # element is the product of the factors common to all terms of lst, and whose remaining
    # elements are quotients of each term divided by the common factor. *)
    lst1 = [NonabsurdNumberFactors(i) for i in lst]
    lst2 = [AbsurdNumberFactors(i) for i in lst]
    num = AbsurdNumberGCD(*lst2)
    common = num
    lst2 = [i/num for i in lst2]
    while (True):
        lst3 = [LeadFactor(i) for i in lst1]

        if SameQ(*lst3):
            common = common*lst3[0]
            lst1 = [RemainingFactors(i) for i in lst1]
        elif (all((LogQ(i) and IntegerQ(First(i)) and First(i) > 0) for i in lst3) and
            all(RationalQ(i) for i in [FullSimplify(j/First(lst3)) for j in lst3])):
            lst4 = [FullSimplify(j/First(lst3)) for j in lst3]
            num = GCD(*lst4)
            common = common*Log((First(lst3)[0])**num)
            lst2 = [lst2[i]*lst4[i]/num for i in range(0, len(lst2))]
            lst1 = [RemainingFactors(i) for i in lst1]
        lst4 = [LeadDegree(i) for i in lst1]
        if SameQ(*[LeadBase(i) for i in lst1]) and RationalQ(*lst4):
            num = Smallest(lst4)
            base = LeadBase(lst1[0])
            if num != 0:
                common = common*base**num
            lst2 = [lst2[i]*base**(lst4[i] - num) for i in range(0, len(lst2))]
            lst1 = [RemainingFactors(i) for i in lst1]
        elif (Length(lst1) == 2 and ZeroQ(LeadBase(lst1[0]) + LeadBase(lst1[1])) and
            NonzeroQ(lst1[0] - 1) and IntegerQ(lst4[0]) and FractionQ(lst4[1])):
            num = Min(*lst4)
            base = LeadBase(lst1[1])
            if num != 0:
                common = common*base**num
            lst2 = [lst2[0]*(-1)**lst4[0], lst2[1]]
            lst2 = [lst2[i]*base**(lst4[i] - num) for i in range(0, len(lst2))]
            lst1 = [RemainingFactors(i) for i in lst1]
        elif (Length(lst1) == 2 and ZeroQ(lst1[0] + LeadBase(lst1[1])) and
            NonzeroQ(lst1[1] - 1) and IntegerQ(lst1[1]) and FractionQ(lst4[0])):
            num = Min(*lst4)
            base = LeadBase(lst1[0])
            if num != 0:
                common = common*base**num
            lst2 = [lst2[0], lst2[1]*(-1)**lst4[1]]
            lst2 = [lst2[i]*base**(lst4[i] - num) for i in range(0, len(lst2))]
            lst1 = [RemainingFactors(i) for i in lst1]
        else:
            num = MostMainFactorPosition(lst3)
            lst2 = ReplacePart(lst2, lst3[num]*lst2[num], num)
            lst1 = ReplacePart(lst1, RemainingFactors(lst1[num]), num)
        if all(i==1 for i in lst1):
            return Prepend(lst2, common)

def MostMainFactorPosition(lst):
    factor = S(1)
    num = 0
    for i in range(0, Length(lst)):
        if FactorOrder(lst[i], factor) > 0:
            factor = lst[i]
            num = i
    return num

SbaseS, SexponS = None, None
SexponFlagS = False
def FunctionOfExponentialQ(u, x):
    # (* FunctionOfExponentialQ[u,x] returns True iff u is a function of F^v where F is a constant and v is linear in x, *)
    # (* and such an exponential explicitly occurs in u (i.e. not just implicitly in hyperbolic functions). *)
    global SbaseS, SexponS, SexponFlagS
    SbaseS, SexponS = None, None
    SexponFlagS = False
    res = FunctionOfExponentialTest(u, x)
    return res and SexponFlagS

def FunctionOfExponential(u, x):
    global SbaseS, SexponS, SexponFlagS
    # (* u is a function of F^v where v is linear in x.  FunctionOfExponential[u,x] returns F^v. *)
    SbaseS, SexponS = None, None
    SexponFlagS = False
    FunctionOfExponentialTest(u, x)
    return SbaseS**SexponS

def FunctionOfExponentialFunction(u, x):
    global SbaseS, SexponS, SexponFlagS
    # (* u is a function of F^v where v is linear in x.  FunctionOfExponentialFunction[u,x] returns u with F^v replaced by x. *)
    SbaseS, SexponS = None, None
    SexponFlagS = False
    FunctionOfExponentialTest(u, x)
    return SimplifyIntegrand(FunctionOfExponentialFunctionAux(u, x), x)

def FunctionOfExponentialFunctionAux(u, x):
    # (* u is a function of F^v where v is linear in x, and the fluid variables $base$=F and $expon$=v. *)
    # (* FunctionOfExponentialFunctionAux[u,x] returns u with F^v replaced by x. *)
    global SbaseS, SexponS, SexponFlagS
    if AtomQ(u):
        return u
    elif PowerQ(u):
        if FreeQ(u.base, x) and LinearQ(u.exp, x):
            if ZeroQ(Coefficient(SexponS, x, 0)):
                return u.base**Coefficient(u.exp, x, 0)*x**FullSimplify(Log(u.base)*Coefficient(u.exp, x, 1)/(Log(SbaseS)*Coefficient(SexponS, x, 1)))
            return x**FullSimplify(Log(u.base)*Coefficient(u.exp, x, 1)/(Log(SbaseS)*Coefficient(SexponS, x, 1)))
    elif HyperbolicQ(u) and LinearQ(u.args[0], x):
        tmp = x**FullSimplify(Coefficient(u.args[0], x, 1)/(Log(SbaseS)*Coefficient(SexponS, x, 1)))
        if SinhQ(u):
            return tmp/2 - 1/(2*tmp)
        elif CoshQ(u):
            return tmp/2 + 1/(2*tmp)
        elif TanhQ(u):
            return (tmp - 1/tmp)/(tmp + 1/tmp)
        elif CothQ(u):
            return (tmp + 1/tmp)/(tmp - 1/tmp)
        elif SechQ(u):
            return 2/(tmp + 1/tmp)
        return 2/(tmp - 1/tmp)
    if PowerQ(u):
        if FreeQ(u.base, x) and SumQ(u.exp):
            return FunctionOfExponentialFunctionAux(u.base**First(u.exp), x)*FunctionOfExponentialFunctionAux(u.base**Rest(u.exp), x)
    return u.func(*[FunctionOfExponentialFunctionAux(i, x) for i in u.args])

def FunctionOfExponentialTest(u, x):
    # (* FunctionOfExponentialTest[u,x] returns True iff u is a function of F^v where F is a constant and v is linear in x. *)
    # (* Before it is called, the fluid variables $base$ and $expon$ should be set to Null and $exponFlag$ to False. *)
    # (* If u is a function of F^v, $base$ and $expon$ are set to F and v, respectively. *)
    # (* If an explicit exponential occurs in u, $exponFlag$ is set to True. *)
    global SbaseS, SexponS, SexponFlagS
    if FreeQ(u, x):
        return True
    elif u == x or CalculusQ(u):
        return False
    elif PowerQ(u):
        if FreeQ(u.base, x) and LinearQ(u.exp, x):
            SexponFlagS = True
            return FunctionOfExponentialTestAux(u.base, u.exp, x)
    elif HyperbolicQ(u) and LinearQ(u.args[0], x):
        return FunctionOfExponentialTestAux(E, u.args[0], x)
    if PowerQ(u):
        if FreeQ(u.base, x) and SumQ(u.exp):
            return FunctionOfExponentialTest(u.base**First(u.exp), x) and FunctionOfExponentialTest(u.base**Rest(u.exp), x)
    return all(FunctionOfExponentialTest(i, x) for i in u.args)

def FunctionOfExponentialTestAux(base, expon, x):
    global SbaseS, SexponS, SexponFlagS
    if SbaseS is None:
        SbaseS = base
        SexponS = expon
        return True
    tmp = FullSimplify(Log(base)*Coefficient(expon, x, 1)/(Log(SbaseS)*Coefficient(SexponS, x, 1)))
    if Not(RationalQ(tmp)):
        return False
    elif ZeroQ(Coefficient(SexponS, x, 0)) or NonzeroQ(tmp - FullSimplify(Log(base)*Coefficient(expon, x, 0)/(Log(SbaseS)*Coefficient(SexponS, x, 0)))):
        if PositiveIntegerQ(base, SbaseS) and base < SbaseS:
            SbaseS = base
            SexponS = expon
            tmp = 1/tmp
        SexponS = Coefficient(SexponS, x, 1)*x/Denominator(tmp)
        if tmp < 0 and NegQ(Coefficient(SexponS, x, 1)):
            SexponS = -SexponS
        return True
    SexponS = SexponS/Denominator(tmp)
    if tmp < 0 and NegQ(Coefficient(SexponS, x, 1)):
        SexponS = -SexponS
    return True

def stdev(lst):
    """Calculates the standard deviation for a list of numbers."""
    num_items = len(lst)
    mean = sum(lst) / num_items
    differences = [x - mean for x in lst]
    sq_differences = [d ** 2 for d in differences]
    ssd = sum(sq_differences)
    variance = ssd / num_items
    sd = sqrt(variance)

    return sd

def rubi_test(expr, x, optimal_output, expand=False, _hyper_check=False, _diff=False, _numerical=False):
    #Returns True if (expr - optimal_output) is equal to 0 or a constant
    #expr: integrated expression
    #x: integration variable
    #expand=True equates `expr` with `optimal_output` in expanded form
    #_hyper_check=True evaluates numerically
    #_diff=True differentiates the expressions before equating
    #_numerical=True equates the expressions at random `x`. Normally used for large expressions.
    from sympy.simplify.simplify import nsimplify
    if not expr.has(csc, sec, cot, csch, sech, coth):
        optimal_output = process_trig(optimal_output)
    if expr == optimal_output:
        return True
    if simplify(expr) == simplify(optimal_output):
        return True

    if nsimplify(expr) == nsimplify(optimal_output):
        return True

    if expr.has(sym_exp):
        expr = powsimp(powdenest(expr), force=True)
        if simplify(expr) == simplify(powsimp(optimal_output, force=True)):
            return True
    res = expr - optimal_output
    if _numerical:
        args = res.free_symbols
        rand_val = []
        try:
            for i in range(0, 5): # check at 5 random points
                rand_x = randint(1, 40)
                substitutions = {s: rand_x for s in args}
                rand_val.append(float(abs(res.subs(substitutions).n())))

            if stdev(rand_val) < Pow(10, -3):
                return True
        except:
            pass
            # return False

    dres = res.diff(x)
    if _numerical:
        args = dres.free_symbols
        rand_val = []
        try:
            for i in range(0, 5): # check at 5 random points
                rand_x = randint(1, 40)
                substitutions = {s: rand_x for s in args}
                rand_val.append(float(abs(dres.subs(substitutions).n())))
            if stdev(rand_val) < Pow(10, -3):
                return True
            # return False
        except:
            pass
            # return False



    r = Simplify(nsimplify(res))
    if r == 0 or (not r.has(x)):
        return True

    if _diff:
        if dres == 0:
            return True
        elif Simplify(dres) == 0:
            return True

    if expand: # expands the expression and equates
        e = res.expand()
        if Simplify(e) == 0 or (not e.has(x)):
            return True


    return False


def If(cond, t, f):
    # returns t if condition is true else f
    if cond:
        return t
    return f

def IntQuadraticQ(a, b, c, d, e, m, p, x):
    # (* IntQuadraticQ[a,b,c,d,e,m,p,x] returns True iff (d+e*x)^m*(a+b*x+c*x^2)^p is integrable wrt x in terms of non-Appell functions. *)
    return IntegerQ(p) or PositiveIntegerQ(m) or IntegersQ(2*m, 2*p) or IntegersQ(m, 4*p) or IntegersQ(m, p + S(1)/3) and (ZeroQ(c**2*d**2 - b*c*d*e + b**2*e**2 - 3*a*c*e**2) or ZeroQ(c**2*d**2 - b*c*d*e - 2*b**2*e**2 + 9*a*c*e**2))

def IntBinomialQ(*args):
    #(* IntBinomialQ(a,b,c,n,m,p,x) returns True iff (c*x)^m*(a+b*x^n)^p is integrable wrt x in terms of non-hypergeometric functions. *)
    if len(args) == 8:
        a, b, c, d, n, p, q, x = args
        return IntegersQ(p,q) or PositiveIntegerQ(p) or PositiveIntegerQ(q) or (ZeroQ(n-2) or ZeroQ(n-4)) and (IntegersQ(p,4*q) or IntegersQ(4*p,q)) or ZeroQ(n-2) and (IntegersQ(2*p,2*q) or IntegersQ(3*p,q) and ZeroQ(b*c+3*a*d) or IntegersQ(p,3*q) and ZeroQ(3*b*c+a*d))
    elif len(args) == 7:
        a, b, c, n, m, p, x = args
        return IntegerQ(2*p) or IntegerQ((m+1)/n + p) or (ZeroQ(n - 2) or ZeroQ(n - 4)) and IntegersQ(2*m, 4*p) or ZeroQ(n - 2) and IntegerQ(6*p) and (IntegerQ(m) or IntegerQ(m - p))
    elif len(args) == 10:
        a, b, c, d, e, m, n, p, q, x = args
        return IntegersQ(p,q) or PositiveIntegerQ(p) or PositiveIntegerQ(q) or ZeroQ(n-2) and IntegerQ(m) and IntegersQ(2*p,2*q) or ZeroQ(n-4) and (IntegersQ(m,p,2*q) or IntegersQ(m,2*p,q))

def RectifyTangent(*args):
    # (* RectifyTangent(u,a,b,r,x) returns an expression whose derivative equals the derivative of r*ArcTan(a+b*Tan(u)) wrt x. *)
    if len(args) == 5:
        u, a, b, r, x = args
        t1 = Together(a)
        t2 = Together(b)
        if (PureComplexNumberQ(t1) or (ProductQ(t1) and any(PureComplexNumberQ(i) for i in t1.args))) and (PureComplexNumberQ(t2) or ProductQ(t2) and any(PureComplexNumberQ(i) for i in t2.args)):
            c = a/I
            d = b/I
            if NegativeQ(d):
                return RectifyTangent(u, -a, -b, -r, x)
            e = SmartDenominator(Together(c + d*x))
            c = c*e
            d = d*e
            if EvenQ(Denominator(NumericFactor(Together(u)))):
                return I*r*Log(RemoveContent(Simplify((c+e)**2+d**2)+Simplify((c+e)**2-d**2)*Cos(2*u)+Simplify(2*(c+e)*d)*Sin(2*u),x))/4 - I*r*Log(RemoveContent(Simplify((c-e)**2+d**2)+Simplify((c-e)**2-d**2)*Cos(2*u)+Simplify(2*(c-e)*d)*Sin(2*u),x))/4
            return I*r*Log(RemoveContent(Simplify((c+e)**2)+Simplify(2*(c+e)*d)*Cos(u)*Sin(u)-Simplify((c+e)**2-d**2)*Sin(u)**2,x))/4 - I*r*Log(RemoveContent(Simplify((c-e)**2)+Simplify(2*(c-e)*d)*Cos(u)*Sin(u)-Simplify((c-e)**2-d**2)*Sin(u)**2,x))/4
        elif NegativeQ(b):
            return RectifyTangent(u, -a, -b, -r, x)
        elif EvenQ(Denominator(NumericFactor(Together(u)))):
            return r*SimplifyAntiderivative(u,x) + r*ArcTan(Simplify((2*a*b*Cos(2*u)-(1+a**2-b**2)*Sin(2*u))/(a**2+(1+b)**2+(1+a**2-b**2)*Cos(2*u)+2*a*b*Sin(2*u))))
        return r*SimplifyAntiderivative(u,x) - r*ArcTan(ActivateTrig(Simplify((a*b-2*a*b*cos(u)**2+(1+a**2-b**2)*cos(u)*sin(u))/(b*(1+b)+(1+a**2-b**2)*cos(u)**2+2*a*b*cos(u)*sin(u)))))

    u, a, b, x = args
    t = Together(a)
    if PureComplexNumberQ(t) or (ProductQ(t) and any(PureComplexNumberQ(i) for i in t.args)):
        c = a/I
        if NegativeQ(c):
            return RectifyTangent(u, -a, -b, x)
        if ZeroQ(c - 1):
            if EvenQ(Denominator(NumericFactor(Together(u)))):
                return I*b*ArcTanh(Sin(2*u))/2
            return I*b*ArcTanh(2*cos(u)*sin(u))/2
        e = SmartDenominator(c)
        c = c*e
        return I*b*Log(RemoveContent(e*Cos(u)+c*Sin(u),x))/2 - I*b*Log(RemoveContent(e*Cos(u)-c*Sin(u),x))/2
    elif NegativeQ(a):
        return RectifyTangent(u, -a, -b, x)
    elif ZeroQ(a - 1):
        return b*SimplifyAntiderivative(u, x)
    elif EvenQ(Denominator(NumericFactor(Together(u)))):
        c =  Simplify((1 + a)/(1 - a))
        numr = SmartNumerator(c)
        denr = SmartDenominator(c)
        return b*SimplifyAntiderivative(u,x) - b*ArcTan(NormalizeLeadTermSigns(denr*Sin(2*u)/(numr+denr*Cos(2*u)))),
    elif PositiveQ(a - 1):
        c = Simplify(1/(a - 1))
        numr = SmartNumerator(c)
        denr = SmartDenominator(c)
        return b*SimplifyAntiderivative(u,x) + b*ArcTan(NormalizeLeadTermSigns(denr*Cos(u)*Sin(u)/(numr+denr*Sin(u)**2))),
    c = Simplify(a/(1 - a))
    numr = SmartNumerator(c)
    denr = SmartDenominator(c)
    return b*SimplifyAntiderivative(u,x) - b*ArcTan(NormalizeLeadTermSigns(denr*Cos(u)*Sin(u)/(numr+denr*Cos(u)**2)))

def RectifyCotangent(*args):
    #(* RectifyCotangent[u,a,b,r,x] returns an expression whose derivative equals the derivative of r*ArcTan[a+b*Cot[u]] wrt x. *)
    if len(args) == 5:
        u, a, b, r, x = args
        t1 = Together(a)
        t2 = Together(b)
        if (PureComplexNumberQ(t1) or (ProductQ(t1) and any(PureComplexNumberQ(i) for i in t1.args))) and (PureComplexNumberQ(t2) or ProductQ(t2) and any(PureComplexNumberQ(i) for i in t2.args)):
            c = a/I
            d = b/I
            if NegativeQ(d):
                return RectifyTangent(u,-a,-b,-r,x)
            e = SmartDenominator(Together(c + d*x))
            c = c*e
            d = d*e
            if EvenQ(Denominator(NumericFactor(Together(u)))):
                return  I*r*Log(RemoveContent(Simplify((c+e)**2+d**2)-Simplify((c+e)**2-d**2)*Cos(2*u)+Simplify(2*(c+e)*d)*Sin(2*u),x))/4 - I*r*Log(RemoveContent(Simplify((c-e)**2+d**2)-Simplify((c-e)**2-d**2)*Cos(2*u)+Simplify(2*(c-e)*d)*Sin(2*u),x))/4
            return I*r*Log(RemoveContent(Simplify((c+e)**2)-Simplify((c+e)**2-d**2)*Cos(u)**2+Simplify(2*(c+e)*d)*Cos(u)*Sin(u),x))/4 - I*r*Log(RemoveContent(Simplify((c-e)**2)-Simplify((c-e)**2-d**2)*Cos(u)**2+Simplify(2*(c-e)*d)*Cos(u)*Sin(u),x))/4
        elif NegativeQ(b):
            return RectifyCotangent(u,-a,-b,-r,x)
        elif EvenQ(Denominator(NumericFactor(Together(u)))):
            return -r*SimplifyAntiderivative(u,x) - r*ArcTan(Simplify((2*a*b*Cos(2*u)+(1+a**2-b**2)*Sin(2*u))/(a**2+(1+b)**2-(1+a**2-b**2)*Cos(2*u)+2*a*b*Sin(2*u))))
        return -r*SimplifyAntiderivative(u,x) - r*ArcTan(ActivateTrig(Simplify((a*b-2*a*b*sin(u)**2+(1+a**2-b**2)*cos(u)*sin(u))/(b*(1+b)+(1+a**2-b**2)*sin(u)**2+2*a*b*cos(u)*sin(u)))))

    u, a, b, x = args
    t = Together(a)
    if PureComplexNumberQ(t) or (ProductQ(t) and any(PureComplexNumberQ(i) for i in t.args)):
        c = a/I
        if NegativeQ(c):
            return RectifyCotangent(u,-a,-b,x)
        elif ZeroQ(c - 1):
            if EvenQ(Denominator(NumericFactor(Together(u)))):
                return -I*b*ArcTanh(Sin(2*u))/2
            return -I*b*ArcTanh(2*Cos(u)*Sin(u))/2
        e = SmartDenominator(c)
        c = c*e
        return -I*b*Log(RemoveContent(c*Cos(u)+e*Sin(u),x))/2 + I*b*Log(RemoveContent(c*Cos(u)-e*Sin(u),x))/2
    elif NegativeQ(a):
        return RectifyCotangent(u,-a,-b,x)
    elif ZeroQ(a-1):
        return b*SimplifyAntiderivative(u,x)
    elif EvenQ(Denominator(NumericFactor(Together(u)))):
        c = Simplify(a - 1)
        numr = SmartNumerator(c)
        denr = SmartDenominator(c)
        return b*SimplifyAntiderivative(u,x) - b*ArcTan(NormalizeLeadTermSigns(denr*Cos(u)*Sin(u)/(numr+denr*Cos(u)**2)))
    c = Simplify(a/(1-a))
    numr = SmartNumerator(c)
    denr = SmartDenominator(c)
    return b*SimplifyAntiderivative(u,x) + b*ArcTan(NormalizeLeadTermSigns(denr*Cos(u)*Sin(u)/(numr+denr*Sin(u)**2)))

def Inequality(*args):
    f = args[1::2]
    e = args[0::2]
    r = []
    for i in range(0, len(f)):
        r.append(f[i](e[i], e[i + 1]))
    return all(r)

def Condition(r, c):
    # returns r if c is True
    if c:
        return r
    else:
        raise NotImplementedError('In Condition()')

def Simp(u, x):
    u = replace_pow_exp(u)
    return NormalizeSumFactors(SimpHelp(u, x))

def SimpHelp(u, x):
    if AtomQ(u):
        return u
    elif FreeQ(u, x):
        v = SmartSimplify(u)
        if LeafCount(v) <= LeafCount(u):
            return v
        return u
    elif ProductQ(u):
        #m = MatchQ[Rest[u],a_.+n_*Pi+b_.*v_ /; FreeQ[{a,b},x] && Not[FreeQ[v,x]] && EqQ[n^2,1/4]]
        #if EqQ(First(u), S(1)/2) and m:
        #    if
        #If[EqQ[First[u],1/2] && MatchQ[Rest[u],a_.+n_*Pi+b_.*v_ /; FreeQ[{a,b},x] && Not[FreeQ[v,x]] && EqQ[n^2,1/4]],
        #  If[MatchQ[Rest[u],n_*Pi+b_.*v_ /; FreeQ[b,x] && Not[FreeQ[v,x]] && EqQ[n^2,1/4]],
        #    Map[Function[1/2*#],Rest[u]],
        #  If[MatchQ[Rest[u],m_*a_.+n_*Pi+p_*b_.*v_ /; FreeQ[{a,b},x] && Not[FreeQ[v,x]] && IntegersQ[m/2,p/2]],
        #    Map[Function[1/2*#],Rest[u]],
        #  u]],

        v = FreeFactors(u, x)
        w = NonfreeFactors(u, x)
        v = NumericFactor(v)*SmartSimplify(NonnumericFactors(v)*x**2)/x**2
        if ProductQ(w):
            w = Mul(*[SimpHelp(i,x) for i in w.args])
        else:
            w = SimpHelp(w, x)
        w = FactorNumericGcd(w)
        v = MergeFactors(v, w)
        if ProductQ(v):
            return Mul(*[SimpFixFactor(i, x) for i in v.args])
        return v
    elif SumQ(u):
        Pi = pi
        a_ = Wild('a', exclude=[x])
        b_ = Wild('b', exclude=[x, 0])
        n_ = Wild('n', exclude=[x, 0, 0])
        pattern = a_ + n_*Pi + b_*x
        match = u.match(pattern)
        m = False
        if match:
            if EqQ(match[n_]**3, S(1)/16):
                m = True
        if m:
            return u
        elif PolynomialQ(u, x) and Exponent(u, x) <= 0:
            return SimpHelp(Coefficient(u, x, 0), x)
        elif PolynomialQ(u, x) and Exponent(u, x) == 1 and Coefficient(u, x, 0) == 0:
            return SimpHelp(Coefficient(u, x, 1), x)*x

        v = 0
        w = 0
        for i in u.args:
            if FreeQ(i, x):
                v = i + v
            else:
                w = i + w
        v = SmartSimplify(v)
        if SumQ(w):
            w = Add(*[SimpHelp(i, x) for i in w.args])
        else:
            w = SimpHelp(w, x)
        return v + w
    return u.func(*[SimpHelp(i, x) for i in u.args])

def SplitProduct(func, u):
    #(* If func[v] is True for a factor v of u, SplitProduct[func,u] returns {v, u/v} where v is the first such factor; else it returns False. *)
    if ProductQ(u):
        if func(First(u)):
            return [First(u), Rest(u)]
        lst = SplitProduct(func, Rest(u))
        if AtomQ(lst):
            return False
        return [lst[0], First(u)*lst[1]]
    if func(u):
        return [u, 1]
    return False

def SplitSum(func, u):
    # (* If func[v] is nonatomic for a term v of u, SplitSum[func,u] returns {func[v], u-v} where v is the first such term; else it returns False. *)
    if SumQ(u):
        if Not(AtomQ(func(First(u)))):
            return [func(First(u)), Rest(u)]
        lst = SplitSum(func, Rest(u))
        if AtomQ(lst):
            return False
        return [lst[0], First(u) + lst[1]]
    elif Not(AtomQ(func(u))):
        return [func(u), 0]
    return False

def SubstFor(*args):
    if len(args) == 4:
        w, v, u, x = args
        # u is a function of v. SubstFor(w,v,u,x) returns w times u with v replaced by x.
        return SimplifyIntegrand(w*SubstFor(v, u, x), x)
    v, u, x = args
    # u is a function of v. SubstFor(v, u, x) returns u with v replaced by x.
    if AtomQ(v):
        return Subst(u, v, x)
    elif Not(EqQ(FreeFactors(v, x), 1)):
        return SubstFor(NonfreeFactors(v, x), u, x/FreeFactors(v, x))
    elif SinQ(v):
        return SubstForTrig(u, x, Sqrt(1 - x**2), v.args[0], x)
    elif CosQ(v):
        return SubstForTrig(u, Sqrt(1 - x**2), x, v.args[0], x)
    elif TanQ(v):
        return SubstForTrig(u, x/Sqrt(1 + x**2), 1/Sqrt(1 + x**2), v.args[0], x)
    elif CotQ(v):
        return SubstForTrig(u, 1/Sqrt(1 + x**2), x/Sqrt(1 + x**2), v.args[0], x)
    elif SecQ(v):
        return SubstForTrig(u, 1/Sqrt(1 - x**2), 1/x, v.args[0], x)
    elif CscQ(v):
        return SubstForTrig(u, 1/x, 1/Sqrt(1 - x**2), v.args[0], x)
    elif SinhQ(v):
        return SubstForHyperbolic(u, x, Sqrt(1 + x**2), v.args[0], x)
    elif CoshQ(v):
        return SubstForHyperbolic(u, Sqrt( - 1 + x**2), x, v.args[0], x)
    elif TanhQ(v):
        return SubstForHyperbolic(u, x/Sqrt(1 - x**2), 1/Sqrt(1 - x**2), v.args[0], x)
    elif CothQ(v):
        return SubstForHyperbolic(u, 1/Sqrt( - 1 + x**2), x/Sqrt( - 1 + x**2), v.args[0], x)
    elif SechQ(v):
        return SubstForHyperbolic(u, 1/Sqrt( - 1 + x**2), 1/x, v.args[0], x)
    elif CschQ(v):
        return SubstForHyperbolic(u, 1/x, 1/Sqrt(1 + x**2), v.args[0], x)
    else:
        return SubstForAux(u, v, x)

def SubstForAux(u, v, x):
    # u is a function of v. SubstForAux(u, v, x) returns u with v replaced by x.
    if u==v:
        return x
    elif AtomQ(u):
        if PowerQ(v):
            if FreeQ(v.exp, x) and ZeroQ(u - v.base):
                return x**Simplify(1/v.exp)
        return u
    elif PowerQ(u):
        if FreeQ(u.exp, x):
            if ZeroQ(u.base - v):
                return x**u.exp
            if PowerQ(v):
                if FreeQ(v.exp, x) and ZeroQ(u.base - v.base):
                    return x**Simplify(u.exp/v.exp)
            return SubstForAux(u.base, v, x)**u.exp
    elif ProductQ(u) and Not(EqQ(FreeFactors(u, x), 1)):
        return FreeFactors(u, x)*SubstForAux(NonfreeFactors(u, x), v, x)
    elif ProductQ(u) and ProductQ(v):
        return SubstForAux(First(u), First(v), x)

    return u.func(*[SubstForAux(i, v, x) for i in u.args])

def FresnelS(x):
    return fresnels(x)

def FresnelC(x):
    return fresnelc(x)

def Erf(x):
    return erf(x)

def Erfc(x):
    return erfc(x)

def Erfi(x):
    return erfi(x)

class Gamma(Function):
    @classmethod
    def eval(cls,*args):
        a = args[0]
        if len(args) == 1:
            return gamma(a)
        else:
            b = args[1]
            if (NumericQ(a) and NumericQ(b)) or a == 1:
                return uppergamma(a, b)

def FunctionOfTrigOfLinearQ(u, x):
    # If u is an algebraic function of trig functions of a linear function of x,
    # FunctionOfTrigOfLinearQ[u,x] returns True; else it returns False.
    if FunctionOfTrig(u, None, x) and AlgebraicTrigFunctionQ(u, x) and FunctionOfLinear(FunctionOfTrig(u, None, x), x):
        return True
    else:
        return False

def ElementaryFunctionQ(u):
    # ElementaryExpressionQ[u] returns True if u is a sum, product, or power and all the operands
    # are elementary expressions; or if u is a call on a trig, hyperbolic, or inverse function
    # and all the arguments are elementary expressions; else it returns False.
    if AtomQ(u):
        return True
    elif SumQ(u) or ProductQ(u) or PowerQ(u) or TrigQ(u) or HyperbolicQ(u) or InverseFunctionQ(u):
        for i in u.args:
            if not ElementaryFunctionQ(i):
                return False
        return True
    return False

def Complex(a, b):
    return a + I*b

def UnsameQ(a, b):
    return a != b

@doctest_depends_on(modules=('matchpy',))
def _SimpFixFactor():
    replacer = ManyToOneReplacer()

    pattern1 = Pattern(UtilityOperator(Pow(Add(Mul(Complex(S(0), c_), WC('a', S(1))), Mul(Complex(S(0), d_), WC('b', S(1)))), WC('p', S(1))), x_), CustomConstraint(lambda p: IntegerQ(p)))
    rule1 = ReplacementRule(pattern1, lambda b, c, x, a, p, d : Mul(Pow(I, p), SimpFixFactor(Pow(Add(Mul(a, c), Mul(b, d)), p), x)))
    replacer.add(rule1)

    pattern2 = Pattern(UtilityOperator(Pow(Add(Mul(Complex(S(0), d_), WC('a', S(1))), Mul(Complex(S(0), e_), WC('b', S(1))), Mul(Complex(S(0), f_), WC('c', S(1)))), WC('p', S(1))), x_), CustomConstraint(lambda p: IntegerQ(p)))
    rule2 = ReplacementRule(pattern2, lambda b, c, x, f, a, p, e, d : Mul(Pow(I, p), SimpFixFactor(Pow(Add(Mul(a, d), Mul(b, e), Mul(c, f)), p), x)))
    replacer.add(rule2)

    pattern3 = Pattern(UtilityOperator(Pow(Add(Mul(WC('a', S(1)), Pow(c_, r_)), Mul(WC('b', S(1)), Pow(x_, WC('n', S(1))))), WC('p', S(1))), x_), CustomConstraint(lambda a, x: FreeQ(a, x)), CustomConstraint(lambda b, x: FreeQ(b, x)), CustomConstraint(lambda c, x: FreeQ(c, x)), CustomConstraint(lambda n, p: IntegersQ(n, p)), CustomConstraint(lambda c: AtomQ(c)), CustomConstraint(lambda r: RationalQ(r)), CustomConstraint(lambda r: Less(r, S(0))))
    rule3 = ReplacementRule(pattern3, lambda b, c, r, n, x, a, p : Mul(Pow(c, Mul(r, p)), SimpFixFactor(Pow(Add(a, Mul(Mul(b, Pow(Pow(c, r), S(-1))), Pow(x, n))), p), x)))
    replacer.add(rule3)

    pattern4 = Pattern(UtilityOperator(Pow(Add(WC('a', S(0)), Mul(WC('b', S(1)), Pow(c_, r_), Pow(x_, WC('n', S(1))))), WC('p', S(1))), x_), CustomConstraint(lambda a, x: FreeQ(a, x)), CustomConstraint(lambda b, x: FreeQ(b, x)), CustomConstraint(lambda c, x: FreeQ(c, x)), CustomConstraint(lambda n, p: IntegersQ(n, p)), CustomConstraint(lambda c: AtomQ(c)), CustomConstraint(lambda r: RationalQ(r)), CustomConstraint(lambda r: Less(r, S(0))))
    rule4 = ReplacementRule(pattern4, lambda b, c, r, n, x, a, p : Mul(Pow(c, Mul(r, p)), SimpFixFactor(Pow(Add(Mul(a, Pow(Pow(c, r), S(-1))), Mul(b, Pow(x, n))), p), x)))
    replacer.add(rule4)

    pattern5 = Pattern(UtilityOperator(Pow(Add(Mul(WC('a', S(1)), Pow(c_, WC('s', S(1)))), Mul(WC('b', S(1)), Pow(c_, WC('r', S(1))), Pow(x_, WC('n', S(1))))), WC('p', S(1))), x_), CustomConstraint(lambda a, x: FreeQ(a, x)), CustomConstraint(lambda b, x: FreeQ(b, x)), CustomConstraint(lambda c, x: FreeQ(c, x)), CustomConstraint(lambda n, p: IntegersQ(n, p)), CustomConstraint(lambda r, s: RationalQ(s, r)), CustomConstraint(lambda r, s: Inequality(S(0), Less, s, LessEqual, r)), CustomConstraint(lambda p, c, s: UnsameQ(Pow(c, Mul(s, p)), S(-1))))
    rule5 = ReplacementRule(pattern5, lambda b, c, r, n, x, a, p, s : Mul(Pow(c, Mul(s, p)), SimpFixFactor(Pow(Add(a, Mul(b, Pow(c, Add(r, Mul(S(-1), s))), Pow(x, n))), p), x)))
    replacer.add(rule5)

    pattern6 = Pattern(UtilityOperator(Pow(Add(Mul(WC('a', S(1)), Pow(c_, WC('s', S(1)))), Mul(WC('b', S(1)), Pow(c_, WC('r', S(1))), Pow(x_, WC('n', S(1))))), WC('p', S(1))), x_), CustomConstraint(lambda a, x: FreeQ(a, x)), CustomConstraint(lambda b, x: FreeQ(b, x)), CustomConstraint(lambda c, x: FreeQ(c, x)), CustomConstraint(lambda n, p: IntegersQ(n, p)), CustomConstraint(lambda r, s: RationalQ(s, r)), CustomConstraint(lambda s, r: Less(S(0), r, s)), CustomConstraint(lambda p, c, r: UnsameQ(Pow(c, Mul(r, p)), S(-1))))
    rule6 = ReplacementRule(pattern6, lambda b, c, r, n, x, a, p, s : Mul(Pow(c, Mul(r, p)), SimpFixFactor(Pow(Add(Mul(a, Pow(c, Add(s, Mul(S(-1), r)))), Mul(b, Pow(x, n))), p), x)))
    replacer.add(rule6)

    return replacer

@doctest_depends_on(modules=('matchpy',))
def SimpFixFactor(expr, x):
    r = SimpFixFactor_replacer.replace(UtilityOperator(expr, x))
    if isinstance(r, UtilityOperator):
        return expr
    return r

@doctest_depends_on(modules=('matchpy',))
def _FixSimplify():
    Plus = Add
    def cons_f1(n):
        return OddQ(n)
    cons1 = CustomConstraint(cons_f1)

    def cons_f2(m):
        return RationalQ(m)
    cons2 = CustomConstraint(cons_f2)

    def cons_f3(n):
        return FractionQ(n)
    cons3 = CustomConstraint(cons_f3)

    def cons_f4(u):
        return SqrtNumberSumQ(u)
    cons4 = CustomConstraint(cons_f4)

    def cons_f5(v):
        return SqrtNumberSumQ(v)
    cons5 = CustomConstraint(cons_f5)

    def cons_f6(u):
        return PositiveQ(u)
    cons6 = CustomConstraint(cons_f6)

    def cons_f7(v):
        return PositiveQ(v)
    cons7 = CustomConstraint(cons_f7)

    def cons_f8(v):
        return SqrtNumberSumQ(S(1)/v)
    cons8 = CustomConstraint(cons_f8)

    def cons_f9(m):
        return IntegerQ(m)
    cons9 = CustomConstraint(cons_f9)

    def cons_f10(u):
        return NegativeQ(u)
    cons10 = CustomConstraint(cons_f10)

    def cons_f11(n, m, a, b):
        return RationalQ(a, b, m, n)
    cons11 = CustomConstraint(cons_f11)

    def cons_f12(a):
        return Greater(a, S(0))
    cons12 = CustomConstraint(cons_f12)

    def cons_f13(b):
        return Greater(b, S(0))
    cons13 = CustomConstraint(cons_f13)

    def cons_f14(p):
        return PositiveIntegerQ(p)
    cons14 = CustomConstraint(cons_f14)

    def cons_f15(p):
        return IntegerQ(p)
    cons15 = CustomConstraint(cons_f15)

    def cons_f16(p, n):
        return Greater(-n + p, S(0))
    cons16 = CustomConstraint(cons_f16)

    def cons_f17(a, b):
        return SameQ(a + b, S(0))
    cons17 = CustomConstraint(cons_f17)

    def cons_f18(n):
        return Not(IntegerQ(n))
    cons18 = CustomConstraint(cons_f18)

    def cons_f19(c, a, b, d):
        return ZeroQ(-a*d + b*c)
    cons19 = CustomConstraint(cons_f19)

    def cons_f20(a):
        return Not(RationalQ(a))
    cons20 = CustomConstraint(cons_f20)

    def cons_f21(t):
        return IntegerQ(t)
    cons21 = CustomConstraint(cons_f21)

    def cons_f22(n, m):
        return RationalQ(m, n)
    cons22 = CustomConstraint(cons_f22)

    def cons_f23(n, m):
        return Inequality(S(0), Less, m, LessEqual, n)
    cons23 = CustomConstraint(cons_f23)

    def cons_f24(p, n, m):
        return RationalQ(m, n, p)
    cons24 = CustomConstraint(cons_f24)

    def cons_f25(p, n, m):
        return Inequality(S(0), Less, m, LessEqual, n, LessEqual, p)
    cons25 = CustomConstraint(cons_f25)

    def cons_f26(p, n, m, q):
        return Inequality(S(0), Less, m, LessEqual, n, LessEqual, p, LessEqual, q)
    cons26 = CustomConstraint(cons_f26)

    def cons_f27(w):
        return Not(RationalQ(w))
    cons27 = CustomConstraint(cons_f27)

    def cons_f28(n):
        return Less(n, S(0))
    cons28 = CustomConstraint(cons_f28)

    def cons_f29(n, w, v):
        return ZeroQ(v + w**(-n))
    cons29 = CustomConstraint(cons_f29)

    def cons_f30(n):
        return IntegerQ(n)
    cons30 = CustomConstraint(cons_f30)

    def cons_f31(w, v):
        return ZeroQ(v + w)
    cons31 = CustomConstraint(cons_f31)

    def cons_f32(p, n):
        return IntegerQ(n/p)
    cons32 = CustomConstraint(cons_f32)

    def cons_f33(w, v):
        return ZeroQ(v - w)
    cons33 = CustomConstraint(cons_f33)

    def cons_f34(p, n):
        return IntegersQ(n, n/p)
    cons34 = CustomConstraint(cons_f34)

    def cons_f35(a):
        return AtomQ(a)
    cons35 = CustomConstraint(cons_f35)

    def cons_f36(b):
        return AtomQ(b)
    cons36 = CustomConstraint(cons_f36)

    pattern1 = Pattern(UtilityOperator((w_ + Complex(S(0), b_)*WC('v', S(1)))**WC('n', S(1))*Complex(S(0), a_)*WC('u', S(1))), cons1)
    def replacement1(n, u, w, v, a, b):
        return (S(-1))**(n/S(2) + S(1)/2)*a*u*FixSimplify((b*v - w*Complex(S(0), S(1)))**n)
    rule1 = ReplacementRule(pattern1, replacement1)
    def With2(m, n, u, w, v):
        z = u**(m/GCD(m, n))*v**(n/GCD(m, n))
        if Or(AbsurdNumberQ(z), SqrtNumberSumQ(z)):
            return True
        return False
    pattern2 = Pattern(UtilityOperator(u_**WC('m', S(1))*v_**n_*WC('w', S(1))), cons2, cons3, cons4, cons5, cons6, cons7, CustomConstraint(With2))
    def replacement2(m, n, u, w, v):
        z = u**(m/GCD(m, n))*v**(n/GCD(m, n))
        return FixSimplify(w*z**GCD(m, n))
    rule2 = ReplacementRule(pattern2, replacement2)
    def With3(m, n, u, w, v):
        z = u**(m/GCD(m, -n))*v**(n/GCD(m, -n))
        if Or(AbsurdNumberQ(z), SqrtNumberSumQ(z)):
            return True
        return False
    pattern3 = Pattern(UtilityOperator(u_**WC('m', S(1))*v_**n_*WC('w', S(1))), cons2, cons3, cons4, cons8, cons6, cons7, CustomConstraint(With3))
    def replacement3(m, n, u, w, v):
        z = u**(m/GCD(m, -n))*v**(n/GCD(m, -n))
        return FixSimplify(w*z**GCD(m, -n))
    rule3 = ReplacementRule(pattern3, replacement3)
    def With4(m, n, u, w, v):
        z = v**(n/GCD(m, n))*(-u)**(m/GCD(m, n))
        if Or(AbsurdNumberQ(z), SqrtNumberSumQ(z)):
            return True
        return False
    pattern4 = Pattern(UtilityOperator(u_**WC('m', S(1))*v_**n_*WC('w', S(1))), cons9, cons3, cons4, cons5, cons10, cons7, CustomConstraint(With4))
    def replacement4(m, n, u, w, v):
        z = v**(n/GCD(m, n))*(-u)**(m/GCD(m, n))
        return FixSimplify(-w*z**GCD(m, n))
    rule4 = ReplacementRule(pattern4, replacement4)
    def With5(m, n, u, w, v):
        z = v**(n/GCD(m, -n))*(-u)**(m/GCD(m, -n))
        if Or(AbsurdNumberQ(z), SqrtNumberSumQ(z)):
            return True
        return False
    pattern5 = Pattern(UtilityOperator(u_**WC('m', S(1))*v_**n_*WC('w', S(1))), cons9, cons3, cons4, cons8, cons10, cons7, CustomConstraint(With5))
    def replacement5(m, n, u, w, v):
        z = v**(n/GCD(m, -n))*(-u)**(m/GCD(m, -n))
        return FixSimplify(-w*z**GCD(m, -n))
    rule5 = ReplacementRule(pattern5, replacement5)
    def With6(p, m, n, u, w, v, a, b):
        c = a**(m/p)*b**n
        if RationalQ(c):
            return True
        return False
    pattern6 = Pattern(UtilityOperator(a_**m_*(b_**n_*WC('v', S(1)) + u_)**WC('p', S(1))*WC('w', S(1))), cons11, cons12, cons13, cons14, CustomConstraint(With6))
    def replacement6(p, m, n, u, w, v, a, b):
        c = a**(m/p)*b**n
        return FixSimplify(w*(a**(m/p)*u + c*v)**p)
    rule6 = ReplacementRule(pattern6, replacement6)
    pattern7 = Pattern(UtilityOperator(a_**WC('m', S(1))*(a_**n_*WC('u', S(1)) + b_**WC('p', S(1))*WC('v', S(1)))*WC('w', S(1))), cons2, cons3, cons15, cons16, cons17)
    def replacement7(p, m, n, u, w, v, a, b):
        return FixSimplify(a**(m + n)*w*((S(-1))**p*a**(-n + p)*v + u))
    rule7 = ReplacementRule(pattern7, replacement7)
    def With8(m, d, n, w, c, a, b):
        q = b/d
        if FreeQ(q, Plus):
            return True
        return False
    pattern8 = Pattern(UtilityOperator((a_ + b_)**WC('m', S(1))*(c_ + d_)**n_*WC('w', S(1))), cons9, cons18, cons19, CustomConstraint(With8))
    def replacement8(m, d, n, w, c, a, b):
        q = b/d
        return FixSimplify(q**m*w*(c + d)**(m + n))
    rule8 = ReplacementRule(pattern8, replacement8)
    pattern9 = Pattern(UtilityOperator((a_**WC('m', S(1))*WC('u', S(1)) + a_**WC('n', S(1))*WC('v', S(1)))**WC('t', S(1))*WC('w', S(1))), cons20, cons21, cons22, cons23)
    def replacement9(m, n, u, w, v, a, t):
        return FixSimplify(a**(m*t)*w*(a**(-m + n)*v + u)**t)
    rule9 = ReplacementRule(pattern9, replacement9)
    pattern10 = Pattern(UtilityOperator((a_**WC('m', S(1))*WC('u', S(1)) + a_**WC('n', S(1))*WC('v', S(1)) + a_**WC('p', S(1))*WC('z', S(1)))**WC('t', S(1))*WC('w', S(1))), cons20, cons21, cons24, cons25)
    def replacement10(p, m, n, u, w, v, a, z, t):
        return FixSimplify(a**(m*t)*w*(a**(-m + n)*v + a**(-m + p)*z + u)**t)
    rule10 = ReplacementRule(pattern10, replacement10)
    pattern11 = Pattern(UtilityOperator((a_**WC('m', S(1))*WC('u', S(1)) + a_**WC('n', S(1))*WC('v', S(1)) + a_**WC('p', S(1))*WC('z', S(1)) + a_**WC('q', S(1))*WC('y', S(1)))**WC('t', S(1))*WC('w', S(1))), cons20, cons21, cons24, cons26)
    def replacement11(p, m, n, u, q, w, v, a, z, y, t):
        return FixSimplify(a**(m*t)*w*(a**(-m + n)*v + a**(-m + p)*z + a**(-m + q)*y + u)**t)
    rule11 = ReplacementRule(pattern11, replacement11)
    pattern12 = Pattern(UtilityOperator((sqrt(v_)*WC('b', S(1)) + sqrt(v_)*WC('c', S(1)) + sqrt(v_)*WC('d', S(1)) + sqrt(v_)*WC('a', S(1)) + WC('u', S(0)))*WC('w', S(1))))
    def replacement12(d, u, w, v, c, a, b):
        return FixSimplify(w*(u + sqrt(v)*FixSimplify(a + b + c + d)))
    rule12 = ReplacementRule(pattern12, replacement12)
    pattern13 = Pattern(UtilityOperator((sqrt(v_)*WC('b', S(1)) + sqrt(v_)*WC('c', S(1)) + sqrt(v_)*WC('a', S(1)) + WC('u', S(0)))*WC('w', S(1))))
    def replacement13(u, w, v, c, a, b):
        return FixSimplify(w*(u + sqrt(v)*FixSimplify(a + b + c)))
    rule13 = ReplacementRule(pattern13, replacement13)
    pattern14 = Pattern(UtilityOperator((sqrt(v_)*WC('b', S(1)) + sqrt(v_)*WC('a', S(1)) + WC('u', S(0)))*WC('w', S(1))))
    def replacement14(u, w, v, a, b):
        return FixSimplify(w*(u + sqrt(v)*FixSimplify(a + b)))
    rule14 = ReplacementRule(pattern14, replacement14)
    pattern15 = Pattern(UtilityOperator(v_**m_*w_**n_*WC('u', S(1))), cons2, cons27, cons3, cons28, cons29)
    def replacement15(m, n, u, w, v):
        return -FixSimplify(u*v**(m + S(-1)))
    rule15 = ReplacementRule(pattern15, replacement15)
    pattern16 = Pattern(UtilityOperator(v_**m_*w_**WC('n', S(1))*WC('u', S(1))), cons2, cons27, cons30, cons31)
    def replacement16(m, n, u, w, v):
        return (S(-1))**n*FixSimplify(u*v**(m + n))
    rule16 = ReplacementRule(pattern16, replacement16)
    pattern17 = Pattern(UtilityOperator(w_**WC('n', S(1))*(-v_**WC('p', S(1)))**m_*WC('u', S(1))), cons2, cons27, cons32, cons33)
    def replacement17(p, m, n, u, w, v):
        return (S(-1))**(n/p)*FixSimplify(u*(-v**p)**(m + n/p))
    rule17 = ReplacementRule(pattern17, replacement17)
    pattern18 = Pattern(UtilityOperator(w_**WC('n', S(1))*(-v_**WC('p', S(1)))**m_*WC('u', S(1))), cons2, cons27, cons34, cons31)
    def replacement18(p, m, n, u, w, v):
        return (S(-1))**(n + n/p)*FixSimplify(u*(-v**p)**(m + n/p))
    rule18 = ReplacementRule(pattern18, replacement18)
    pattern19 = Pattern(UtilityOperator((a_ - b_)**WC('m', S(1))*(a_ + b_)**WC('m', S(1))*WC('u', S(1))), cons9, cons35, cons36)
    def replacement19(m, u, a, b):
        return u*(a**S(2) - b**S(2))**m
    rule19 = ReplacementRule(pattern19, replacement19)
    pattern20 = Pattern(UtilityOperator((S(729)*c - e*(-S(20)*e + S(540)))**WC('m', S(1))*WC('u', S(1))), cons2)
    def replacement20(m, u):
        return u*(a*e**S(2) - b*d*e + c*d**S(2))**m
    rule20 = ReplacementRule(pattern20, replacement20)
    pattern21 = Pattern(UtilityOperator((S(729)*c + e*(S(20)*e + S(-540)))**WC('m', S(1))*WC('u', S(1))), cons2)
    def replacement21(m, u):
        return u*(a*e**S(2) - b*d*e + c*d**S(2))**m
    rule21 = ReplacementRule(pattern21, replacement21)
    pattern22 = Pattern(UtilityOperator(u_))
    def replacement22(u):
        return u
    rule22 = ReplacementRule(pattern22, replacement22)
    return [rule1, rule2, rule3, rule4, rule5, rule6, rule7, rule8, rule9, rule10, rule11, rule12, rule13, rule14, rule15, rule16, rule17, rule18, rule19, rule20, rule21, rule22, ]

@doctest_depends_on(modules=('matchpy',))
def FixSimplify(expr):
    if isinstance(expr, (list, tuple, TupleArg)):
        return [replace_all(UtilityOperator(i), FixSimplify_rules) for i in expr]
    return replace_all(UtilityOperator(expr), FixSimplify_rules)

@doctest_depends_on(modules=('matchpy',))
def _SimplifyAntiderivativeSum():
    replacer = ManyToOneReplacer()

    pattern1 = Pattern(UtilityOperator(Add(Mul(Log(Add(a_, Mul(WC('b', S(1)), Pow(Tan(u_), WC('n', S(1)))))), WC('A', S(1))), Mul(Log(Cos(u_)), WC('B', S(1))), WC('v', S(0))), x_), CustomConstraint(lambda a, x: FreeQ(a, x)), CustomConstraint(lambda b, x: FreeQ(b, x)), CustomConstraint(lambda A, x: FreeQ(A, x)), CustomConstraint(lambda B, x: FreeQ(B, x)), CustomConstraint(lambda n: IntegerQ(n)), CustomConstraint(lambda B, A, n: ZeroQ(Add(Mul(n, A), Mul(S(1), B)))))
    rule1 = ReplacementRule(pattern1, lambda n, x, v, b, B, A, u, a : Add(SimplifyAntiderivativeSum(v, x), Mul(A, Log(RemoveContent(Add(Mul(a, Pow(Cos(u), n)), Mul(b, Pow(Sin(u), n))), x)))))
    replacer.add(rule1)

    pattern2 = Pattern(UtilityOperator(Add(Mul(Log(Add(Mul(Pow(Cot(u_), WC('n', S(1))), WC('b', S(1))), a_)), WC('A', S(1))), Mul(Log(Sin(u_)), WC('B', S(1))), WC('v', S(0))), x_), CustomConstraint(lambda a, x: FreeQ(a, x)), CustomConstraint(lambda b, x: FreeQ(b, x)), CustomConstraint(lambda A, x: FreeQ(A, x)), CustomConstraint(lambda B, x: FreeQ(B, x)), CustomConstraint(lambda n: IntegerQ(n)), CustomConstraint(lambda B, A, n: ZeroQ(Add(Mul(n, A), Mul(S(1), B)))))
    rule2 = ReplacementRule(pattern2, lambda n, x, v, b, B, A, a, u : Add(SimplifyAntiderivativeSum(v, x), Mul(A, Log(RemoveContent(Add(Mul(a, Pow(Sin(u), n)), Mul(b, Pow(Cos(u), n))), x)))))
    replacer.add(rule2)

    pattern3 = Pattern(UtilityOperator(Add(Mul(Log(Add(a_, Mul(WC('b', S(1)), Pow(Tan(u_), WC('n', S(1)))))), WC('A', S(1))), Mul(Log(Add(c_, Mul(WC('d', S(1)), Pow(Tan(u_), WC('n', S(1)))))), WC('B', S(1))), WC('v', S(0))), x_), CustomConstraint(lambda a, x: FreeQ(a, x)), CustomConstraint(lambda b, x: FreeQ(b, x)), CustomConstraint(lambda c, x: FreeQ(c, x)), CustomConstraint(lambda d, x: FreeQ(d, x)), CustomConstraint(lambda A, x: FreeQ(A, x)), CustomConstraint(lambda B, x: FreeQ(B, x)), CustomConstraint(lambda n: IntegerQ(n)), CustomConstraint(lambda B, A: ZeroQ(Add(A, B))))
    rule3 = ReplacementRule(pattern3, lambda n, x, v, b, A, B, u, c, d, a : Add(SimplifyAntiderivativeSum(v, x), Mul(A, Log(RemoveContent(Add(Mul(a, Pow(Cos(u), n)), Mul(b, Pow(Sin(u), n))), x))), Mul(B, Log(RemoveContent(Add(Mul(c, Pow(Cos(u), n)), Mul(d, Pow(Sin(u), n))), x)))))
    replacer.add(rule3)

    pattern4 = Pattern(UtilityOperator(Add(Mul(Log(Add(Mul(Pow(Cot(u_), WC('n', S(1))), WC('b', S(1))), a_)), WC('A', S(1))), Mul(Log(Add(Mul(Pow(Cot(u_), WC('n', S(1))), WC('d', S(1))), c_)), WC('B', S(1))), WC('v', S(0))), x_), CustomConstraint(lambda a, x: FreeQ(a, x)), CustomConstraint(lambda b, x: FreeQ(b, x)), CustomConstraint(lambda c, x: FreeQ(c, x)), CustomConstraint(lambda d, x: FreeQ(d, x)), CustomConstraint(lambda A, x: FreeQ(A, x)), CustomConstraint(lambda B, x: FreeQ(B, x)), CustomConstraint(lambda n: IntegerQ(n)), CustomConstraint(lambda B, A: ZeroQ(Add(A, B))))
    rule4 = ReplacementRule(pattern4, lambda n, x, v, b, A, B, c, a, d, u : Add(SimplifyAntiderivativeSum(v, x), Mul(A, Log(RemoveContent(Add(Mul(b, Pow(Cos(u), n)), Mul(a, Pow(Sin(u), n))), x))), Mul(B, Log(RemoveContent(Add(Mul(d, Pow(Cos(u), n)), Mul(c, Pow(Sin(u), n))), x)))))
    replacer.add(rule4)

    pattern5 = Pattern(UtilityOperator(Add(Mul(Log(Add(a_, Mul(WC('b', S(1)), Pow(Tan(u_), WC('n', S(1)))))), WC('A', S(1))), Mul(Log(Add(c_, Mul(WC('d', S(1)), Pow(Tan(u_), WC('n', S(1)))))), WC('B', S(1))), Mul(Log(Add(e_, Mul(WC('f', S(1)), Pow(Tan(u_), WC('n', S(1)))))), WC('C', S(1))), WC('v', S(0))), x_), CustomConstraint(lambda a, x: FreeQ(a, x)), CustomConstraint(lambda b, x: FreeQ(b, x)), CustomConstraint(lambda c, x: FreeQ(c, x)), CustomConstraint(lambda d, x: FreeQ(d, x)), CustomConstraint(lambda e, x: FreeQ(e, x)), CustomConstraint(lambda f, x: FreeQ(f, x)), CustomConstraint(lambda A, x: FreeQ(A, x)), CustomConstraint(lambda B, x: FreeQ(B, x)), CustomConstraint(lambda C, x: FreeQ(C, x)), CustomConstraint(lambda n: IntegerQ(n)), CustomConstraint(lambda B, A, C: ZeroQ(Add(A, B, C))))
    rule5 = ReplacementRule(pattern5, lambda n, e, x, v, b, A, B, u, c, f, d, a, C : Add(SimplifyAntiderivativeSum(v, x), Mul(A, Log(RemoveContent(Add(Mul(a, Pow(Cos(u), n)), Mul(b, Pow(Sin(u), n))), x))), Mul(B, Log(RemoveContent(Add(Mul(c, Pow(Cos(u), n)), Mul(d, Pow(Sin(u), n))), x))), Mul(C, Log(RemoveContent(Add(Mul(e, Pow(Cos(u), n)), Mul(f, Pow(Sin(u), n))), x)))))
    replacer.add(rule5)

    pattern6 = Pattern(UtilityOperator(Add(Mul(Log(Add(Mul(Pow(Cot(u_), WC('n', S(1))), WC('b', S(1))), a_)), WC('A', S(1))), Mul(Log(Add(Mul(Pow(Cot(u_), WC('n', S(1))), WC('d', S(1))), c_)), WC('B', S(1))), Mul(Log(Add(Mul(Pow(Cot(u_), WC('n', S(1))), WC('f', S(1))), e_)), WC('C', S(1))), WC('v', S(0))), x_), CustomConstraint(lambda a, x: FreeQ(a, x)), CustomConstraint(lambda b, x: FreeQ(b, x)), CustomConstraint(lambda c, x: FreeQ(c, x)), CustomConstraint(lambda d, x: FreeQ(d, x)), CustomConstraint(lambda e, x: FreeQ(e, x)), CustomConstraint(lambda f, x: FreeQ(f, x)), CustomConstraint(lambda A, x: FreeQ(A, x)), CustomConstraint(lambda B, x: FreeQ(B, x)), CustomConstraint(lambda C, x: FreeQ(C, x)), CustomConstraint(lambda n: IntegerQ(n)), CustomConstraint(lambda B, A, C: ZeroQ(Add(A, B, C))))
    rule6 = ReplacementRule(pattern6, lambda n, e, x, v, b, A, B, c, a, f, d, u, C : Add(SimplifyAntiderivativeSum(v, x), Mul(A, Log(RemoveContent(Add(Mul(b, Pow(Cos(u), n)), Mul(a, Pow(Sin(u), n))), x))), Mul(B, Log(RemoveContent(Add(Mul(d, Pow(Cos(u), n)), Mul(c, Pow(Sin(u), n))), x))), Mul(C, Log(RemoveContent(Add(Mul(f, Pow(Cos(u), n)), Mul(e, Pow(Sin(u), n))), x)))))
    replacer.add(rule6)

    return replacer

@doctest_depends_on(modules=('matchpy',))
def SimplifyAntiderivativeSum(expr, x):
    r = SimplifyAntiderivativeSum_replacer.replace(UtilityOperator(expr, x))
    if isinstance(r, UtilityOperator):
        return expr
    return r

@doctest_depends_on(modules=('matchpy',))
def _SimplifyAntiderivative():
    replacer = ManyToOneReplacer()

    pattern2 = Pattern(UtilityOperator(Log(Mul(c_, u_)), x_), CustomConstraint(lambda c, x: FreeQ(c, x)))
    rule2 = ReplacementRule(pattern2, lambda x, c, u : SimplifyAntiderivative(Log(u), x))
    replacer.add(rule2)

    pattern3 = Pattern(UtilityOperator(Log(Pow(u_, n_)), x_), CustomConstraint(lambda n, x: FreeQ(n, x)))
    rule3 = ReplacementRule(pattern3, lambda x, n, u : Mul(n, SimplifyAntiderivative(Log(u), x)))
    replacer.add(rule3)

    pattern7 = Pattern(UtilityOperator(Log(Pow(f_, u_)), x_), CustomConstraint(lambda f, x: FreeQ(f, x)))
    rule7 = ReplacementRule(pattern7, lambda x, f, u : Mul(Log(f), SimplifyAntiderivative(u, x)))
    replacer.add(rule7)

    pattern8 = Pattern(UtilityOperator(Log(Add(a_, Mul(WC('b', S(1)), Tan(u_)))), x_), CustomConstraint(lambda a, x: FreeQ(a, x)), CustomConstraint(lambda b, x: FreeQ(b, x)), CustomConstraint(lambda b, a: ZeroQ(Add(Pow(a, S(2)), Pow(b, S(2))))))
    rule8 = ReplacementRule(pattern8, lambda x, b, u, a : Add(Mul(Mul(b, Pow(a, S(1))), SimplifyAntiderivative(u, x)), Mul(S(1), SimplifyAntiderivative(Log(Cos(u)), x))))
    replacer.add(rule8)

    pattern9 = Pattern(UtilityOperator(Log(Add(Mul(Cot(u_), WC('b', S(1))), a_)), x_), CustomConstraint(lambda a, x: FreeQ(a, x)), CustomConstraint(lambda b, x: FreeQ(b, x)), CustomConstraint(lambda b, a: ZeroQ(Add(Pow(a, S(2)), Pow(b, S(2))))))
    rule9 = ReplacementRule(pattern9, lambda x, b, u, a : Add(Mul(Mul(Mul(S(1), b), Pow(a, S(1))), SimplifyAntiderivative(u, x)), Mul(S(1), SimplifyAntiderivative(Log(Sin(u)), x))))
    replacer.add(rule9)

    pattern10 = Pattern(UtilityOperator(ArcTan(Mul(WC('a', S(1)), Tan(u_))), x_), CustomConstraint(lambda a, x: FreeQ(a, x)), CustomConstraint(lambda a: PositiveQ(Pow(a, S(2)))), CustomConstraint(lambda u: ComplexFreeQ(u)))
    rule10 = ReplacementRule(pattern10, lambda x, u, a : RectifyTangent(u, a, S(1), x))
    replacer.add(rule10)

    pattern11 = Pattern(UtilityOperator(ArcCot(Mul(WC('a', S(1)), Tan(u_))), x_), CustomConstraint(lambda a, x: FreeQ(a, x)), CustomConstraint(lambda a: PositiveQ(Pow(a, S(2)))), CustomConstraint(lambda u: ComplexFreeQ(u)))
    rule11 = ReplacementRule(pattern11, lambda x, u, a : RectifyTangent(u, a, S(1), x))
    replacer.add(rule11)

    pattern12 = Pattern(UtilityOperator(ArcCot(Mul(WC('a', S(1)), Tanh(u_))), x_), CustomConstraint(lambda a, x: FreeQ(a, x)), CustomConstraint(lambda u: ComplexFreeQ(u)))
    rule12 = ReplacementRule(pattern12, lambda x, u, a : Mul(S(1), SimplifyAntiderivative(ArcTan(Mul(a, Tanh(u))), x)))
    replacer.add(rule12)

    pattern13 = Pattern(UtilityOperator(ArcTanh(Mul(WC('a', S(1)), Tan(u_))), x_), CustomConstraint(lambda a, x: FreeQ(a, x)), CustomConstraint(lambda a: PositiveQ(Pow(a, S(2)))), CustomConstraint(lambda u: ComplexFreeQ(u)))
    rule13 = ReplacementRule(pattern13, lambda x, u, a : RectifyTangent(u, Mul(I, a), Mul(S(1), I), x))
    replacer.add(rule13)

    pattern14 = Pattern(UtilityOperator(ArcCoth(Mul(WC('a', S(1)), Tan(u_))), x_), CustomConstraint(lambda a, x: FreeQ(a, x)), CustomConstraint(lambda a: PositiveQ(Pow(a, S(2)))), CustomConstraint(lambda u: ComplexFreeQ(u)))
    rule14 = ReplacementRule(pattern14, lambda x, u, a : RectifyTangent(u, Mul(I, a), Mul(S(1), I), x))
    replacer.add(rule14)

    pattern15 = Pattern(UtilityOperator(ArcTanh(Tanh(u_)), x_))
    rule15 = ReplacementRule(pattern15, lambda x, u : SimplifyAntiderivative(u, x))
    replacer.add(rule15)

    pattern16 = Pattern(UtilityOperator(ArcCoth(Tanh(u_)), x_))
    rule16 = ReplacementRule(pattern16, lambda x, u : SimplifyAntiderivative(u, x))
    replacer.add(rule16)

    pattern17 = Pattern(UtilityOperator(ArcCot(Mul(Cot(u_), WC('a', S(1)))), x_), CustomConstraint(lambda a, x: FreeQ(a, x)), CustomConstraint(lambda a: PositiveQ(Pow(a, S(2)))), CustomConstraint(lambda u: ComplexFreeQ(u)))
    rule17 = ReplacementRule(pattern17, lambda x, u, a : RectifyCotangent(u, a, S(1), x))
    replacer.add(rule17)

    pattern18 = Pattern(UtilityOperator(ArcTan(Mul(Cot(u_), WC('a', S(1)))), x_), CustomConstraint(lambda a, x: FreeQ(a, x)), CustomConstraint(lambda a: PositiveQ(Pow(a, S(2)))), CustomConstraint(lambda u: ComplexFreeQ(u)))
    rule18 = ReplacementRule(pattern18, lambda x, u, a : RectifyCotangent(u, a, S(1), x))
    replacer.add(rule18)

    pattern19 = Pattern(UtilityOperator(ArcTan(Mul(Coth(u_), WC('a', S(1)))), x_), CustomConstraint(lambda a, x: FreeQ(a, x)), CustomConstraint(lambda u: ComplexFreeQ(u)))
    rule19 = ReplacementRule(pattern19, lambda x, u, a : Mul(S(1), SimplifyAntiderivative(ArcTan(Mul(Tanh(u), Pow(a, S(1)))), x)))
    replacer.add(rule19)

    pattern20 = Pattern(UtilityOperator(ArcCoth(Mul(Cot(u_), WC('a', S(1)))), x_), CustomConstraint(lambda a, x: FreeQ(a, x)), CustomConstraint(lambda a: PositiveQ(Pow(a, S(2)))), CustomConstraint(lambda u: ComplexFreeQ(u)))
    rule20 = ReplacementRule(pattern20, lambda x, u, a : RectifyCotangent(u, Mul(I, a), I, x))
    replacer.add(rule20)

    pattern21 = Pattern(UtilityOperator(ArcTanh(Mul(Cot(u_), WC('a', S(1)))), x_), CustomConstraint(lambda a, x: FreeQ(a, x)), CustomConstraint(lambda a: PositiveQ(Pow(a, S(2)))), CustomConstraint(lambda u: ComplexFreeQ(u)))
    rule21 = ReplacementRule(pattern21, lambda x, u, a : RectifyCotangent(u, Mul(I, a), I, x))
    replacer.add(rule21)

    pattern22 = Pattern(UtilityOperator(ArcCoth(Coth(u_)), x_))
    rule22 = ReplacementRule(pattern22, lambda x, u : SimplifyAntiderivative(u, x))
    replacer.add(rule22)

    pattern23 = Pattern(UtilityOperator(ArcTanh(Mul(Coth(u_), WC('a', S(1)))), x_), CustomConstraint(lambda a, x: FreeQ(a, x)), CustomConstraint(lambda u: ComplexFreeQ(u)))
    rule23 = ReplacementRule(pattern23, lambda x, u, a : SimplifyAntiderivative(ArcTanh(Mul(Tanh(u), Pow(a, S(1)))), x))
    replacer.add(rule23)

    pattern24 = Pattern(UtilityOperator(ArcTanh(Coth(u_)), x_))
    rule24 = ReplacementRule(pattern24, lambda x, u : SimplifyAntiderivative(u, x))
    replacer.add(rule24)

    pattern25 = Pattern(UtilityOperator(ArcTan(Mul(WC('c', S(1)), Add(a_, Mul(WC('b', S(1)), Tan(u_))))), x_), CustomConstraint(lambda a, x: FreeQ(a, x)), CustomConstraint(lambda b, x: FreeQ(b, x)), CustomConstraint(lambda c, x: FreeQ(c, x)), CustomConstraint(lambda c, a: PositiveQ(Mul(Pow(a, S(2)), Pow(c, S(2))))), CustomConstraint(lambda c, b: PositiveQ(Mul(Pow(b, S(2)), Pow(c, S(2))))), CustomConstraint(lambda u: ComplexFreeQ(u)))
    rule25 = ReplacementRule(pattern25, lambda x, a, b, u, c : RectifyTangent(u, Mul(a, c), Mul(b, c), S(1), x))
    replacer.add(rule25)

    pattern26 = Pattern(UtilityOperator(ArcTanh(Mul(WC('c', S(1)), Add(a_, Mul(WC('b', S(1)), Tan(u_))))), x_), CustomConstraint(lambda a, x: FreeQ(a, x)), CustomConstraint(lambda b, x: FreeQ(b, x)), CustomConstraint(lambda c, x: FreeQ(c, x)), CustomConstraint(lambda c, a: PositiveQ(Mul(Pow(a, S(2)), Pow(c, S(2))))), CustomConstraint(lambda c, b: PositiveQ(Mul(Pow(b, S(2)), Pow(c, S(2))))), CustomConstraint(lambda u: ComplexFreeQ(u)))
    rule26 = ReplacementRule(pattern26, lambda x, a, b, u, c : RectifyTangent(u, Mul(I, a, c), Mul(I, b, c), Mul(S(1), I), x))
    replacer.add(rule26)

    pattern27 = Pattern(UtilityOperator(ArcTan(Mul(WC('c', S(1)), Add(Mul(Cot(u_), WC('b', S(1))), a_))), x_), CustomConstraint(lambda a, x: FreeQ(a, x)), CustomConstraint(lambda b, x: FreeQ(b, x)), CustomConstraint(lambda c, x: FreeQ(c, x)), CustomConstraint(lambda c, a: PositiveQ(Mul(Pow(a, S(2)), Pow(c, S(2))))), CustomConstraint(lambda c, b: PositiveQ(Mul(Pow(b, S(2)), Pow(c, S(2))))), CustomConstraint(lambda u: ComplexFreeQ(u)))
    rule27 = ReplacementRule(pattern27, lambda x, a, b, u, c : RectifyCotangent(u, Mul(a, c), Mul(b, c), S(1), x))
    replacer.add(rule27)

    pattern28 = Pattern(UtilityOperator(ArcTanh(Mul(WC('c', S(1)), Add(Mul(Cot(u_), WC('b', S(1))), a_))), x_), CustomConstraint(lambda a, x: FreeQ(a, x)), CustomConstraint(lambda b, x: FreeQ(b, x)), CustomConstraint(lambda c, x: FreeQ(c, x)), CustomConstraint(lambda c, a: PositiveQ(Mul(Pow(a, S(2)), Pow(c, S(2))))), CustomConstraint(lambda c, b: PositiveQ(Mul(Pow(b, S(2)), Pow(c, S(2))))), CustomConstraint(lambda u: ComplexFreeQ(u)))
    rule28 = ReplacementRule(pattern28, lambda x, a, b, u, c : RectifyCotangent(u, Mul(I, a, c), Mul(I, b, c), Mul(S(1), I), x))
    replacer.add(rule28)

    pattern29 = Pattern(UtilityOperator(ArcTan(Add(WC('a', S(0)), Mul(WC('b', S(1)), Tan(u_)), Mul(WC('c', S(1)), Pow(Tan(u_), S(2))))), x_), CustomConstraint(lambda a, x: FreeQ(a, x)), CustomConstraint(lambda b, x: FreeQ(b, x)), CustomConstraint(lambda c, x: FreeQ(c, x)), CustomConstraint(lambda u: ComplexFreeQ(u)))
    rule29 = ReplacementRule(pattern29, lambda x, a, b, u, c : If(EvenQ(Denominator(NumericFactor(Together(u)))), ArcTan(NormalizeTogether(Mul(Add(a, c, S(1), Mul(Add(a, Mul(S(1), c), S(1)), Cos(Mul(S(2), u))), Mul(b, Sin(Mul(S(2), u)))), Pow(Add(a, c, S(1), Mul(Add(a, Mul(S(1), c), S(1)), Cos(Mul(S(2), u))), Mul(b, Sin(Mul(S(2), u)))), S(1))))), ArcTan(NormalizeTogether(Mul(Add(c, Mul(Add(a, Mul(S(1), c), S(1)), Pow(Cos(u), S(2))), Mul(b, Cos(u), Sin(u))), Pow(Add(c, Mul(Add(a, Mul(S(1), c), S(1)), Pow(Cos(u), S(2))), Mul(b, Cos(u), Sin(u))), S(1)))))))
    replacer.add(rule29)

    pattern30 = Pattern(UtilityOperator(ArcTan(Add(WC('a', S(0)), Mul(WC('b', S(1)), Add(WC('d', S(0)), Mul(WC('e', S(1)), Tan(u_)))), Mul(WC('c', S(1)), Pow(Add(WC('f', S(0)), Mul(WC('g', S(1)), Tan(u_))), S(2))))), x_), CustomConstraint(lambda a, x: FreeQ(a, x)), CustomConstraint(lambda b, x: FreeQ(b, x)), CustomConstraint(lambda c, x: FreeQ(c, x)), CustomConstraint(lambda u: ComplexFreeQ(u)))
    rule30 = ReplacementRule(pattern30, lambda x, d, a, e, f, b, u, c, g : SimplifyAntiderivative(ArcTan(Add(a, Mul(b, d), Mul(c, Pow(f, S(2))), Mul(Add(Mul(b, e), Mul(S(2), c, f, g)), Tan(u)), Mul(c, Pow(g, S(2)), Pow(Tan(u), S(2))))), x))
    replacer.add(rule30)

    pattern31 = Pattern(UtilityOperator(ArcTan(Add(WC('a', S(0)), Mul(WC('c', S(1)), Pow(Tan(u_), S(2))))), x_), CustomConstraint(lambda a, x: FreeQ(a, x)), CustomConstraint(lambda c, x: FreeQ(c, x)), CustomConstraint(lambda u: ComplexFreeQ(u)))
    rule31 = ReplacementRule(pattern31, lambda x, c, u, a : If(EvenQ(Denominator(NumericFactor(Together(u)))), ArcTan(NormalizeTogether(Mul(Add(a, c, S(1), Mul(Add(a, Mul(S(1), c), S(1)), Cos(Mul(S(2), u)))), Pow(Add(a, c, S(1), Mul(Add(a, Mul(S(1), c), S(1)), Cos(Mul(S(2), u)))), S(1))))), ArcTan(NormalizeTogether(Mul(Add(c, Mul(Add(a, Mul(S(1), c), S(1)), Pow(Cos(u), S(2)))), Pow(Add(c, Mul(Add(a, Mul(S(1), c), S(1)), Pow(Cos(u), S(2)))), S(1)))))))
    replacer.add(rule31)

    pattern32 = Pattern(UtilityOperator(ArcTan(Add(WC('a', S(0)), Mul(WC('c', S(1)), Pow(Add(WC('f', S(0)), Mul(WC('g', S(1)), Tan(u_))), S(2))))), x_), CustomConstraint(lambda a, x: FreeQ(a, x)), CustomConstraint(lambda c, x: FreeQ(c, x)), CustomConstraint(lambda u: ComplexFreeQ(u)))
    rule32 = ReplacementRule(pattern32, lambda x, a, f, u, c, g : SimplifyAntiderivative(ArcTan(Add(a, Mul(c, Pow(f, S(2))), Mul(Mul(S(2), c, f, g), Tan(u)), Mul(c, Pow(g, S(2)), Pow(Tan(u), S(2))))), x))
    replacer.add(rule32)

    return replacer

@doctest_depends_on(modules=('matchpy',))
def SimplifyAntiderivative(expr, x):
    r = SimplifyAntiderivative_replacer.replace(UtilityOperator(expr, x))
    if isinstance(r, UtilityOperator):
        if ProductQ(expr):
            u, c = S(1), S(1)
            for i in expr.args:
                if FreeQ(i, x):
                    c *= i
                else:
                    u *= i
            if FreeQ(c, x) and c != S(1):
                v = SimplifyAntiderivative(u, x)
                if SumQ(v) and NonsumQ(u):
                    return Add(*[c*i for i in v.args])
                return c*v
        elif LogQ(expr):
            F = expr.args[0]
            if MemberQ([cot, sec, csc, coth, sech, csch], Head(F)):
                return -SimplifyAntiderivative(Log(1/F), x)
        if MemberQ([Log, atan, acot], Head(expr)):
            F = Head(expr)
            G = expr.args[0]
            if MemberQ([cot, sec, csc, coth, sech, csch], Head(G)):
                return -SimplifyAntiderivative(F(1/G), x)
        if MemberQ([atanh, acoth], Head(expr)):
            F = Head(expr)
            G = expr.args[0]
            if MemberQ([cot, sec, csc, coth, sech, csch], Head(G)):
                return SimplifyAntiderivative(F(1/G), x)
        u = expr
        if FreeQ(u, x):
            return S(0)
        elif LogQ(u):
            return Log(RemoveContent(u.args[0], x))
        elif SumQ(u):
            return SimplifyAntiderivativeSum(Add(*[SimplifyAntiderivative(i, x) for i in u.args]), x)
        return u
    else:
        return r

@doctest_depends_on(modules=('matchpy',))
def _TrigSimplifyAux():
    replacer = ManyToOneReplacer()

    pattern1 = Pattern(UtilityOperator(Mul(WC('u', S(1)), Pow(Add(Mul(WC('a', S(1)), Pow(v_, WC('m', S(1)))), Mul(WC('b', S(1)), Pow(v_, WC('n', S(1))))), p_))), CustomConstraint(lambda v: InertTrigQ(v)), CustomConstraint(lambda p: IntegerQ(p)), CustomConstraint(lambda n, m: RationalQ(m, n)), CustomConstraint(lambda n, m: Less(m, n)))
    rule1 = ReplacementRule(pattern1, lambda n, a, p, m, u, v, b : Mul(u, Pow(v, Mul(m, p)), Pow(TrigSimplifyAux(Add(a, Mul(b, Pow(v, Add(n, Mul(S(-1), m)))))), p)))
    replacer.add(rule1)

    pattern2 = Pattern(UtilityOperator(Add(Mul(Pow(cos(u_), S('2')), WC('a', S(1))), WC('v', S(0)), Mul(WC('b', S(1)), Pow(sin(u_), S('2'))))), CustomConstraint(lambda b, a: SameQ(a, b)))
    rule2 = ReplacementRule(pattern2, lambda u, v, b, a : Add(a, v))
    replacer.add(rule2)

    pattern3 = Pattern(UtilityOperator(Add(WC('v', S(0)), Mul(WC('a', S(1)), Pow(sec(u_), S('2'))), Mul(WC('b', S(1)), Pow(tan(u_), S('2'))))), CustomConstraint(lambda b, a: SameQ(a, Mul(S(-1), b))))
    rule3 = ReplacementRule(pattern3, lambda u, v, b, a : Add(a, v))
    replacer.add(rule3)

    pattern4 = Pattern(UtilityOperator(Add(Mul(Pow(csc(u_), S('2')), WC('a', S(1))), Mul(Pow(cot(u_), S('2')), WC('b', S(1))), WC('v', S(0)))), CustomConstraint(lambda b, a: SameQ(a, Mul(S(-1), b))))
    rule4 = ReplacementRule(pattern4, lambda u, v, b, a : Add(a, v))
    replacer.add(rule4)

    pattern5 = Pattern(UtilityOperator(Pow(Add(Mul(Pow(cos(u_), S('2')), WC('a', S(1))), WC('v', S(0)), Mul(WC('b', S(1)), Pow(sin(u_), S('2')))), n_)))
    rule5 = ReplacementRule(pattern5, lambda n, a, u, v, b : Pow(Add(Mul(Add(b, Mul(S(-1), a)), Pow(Sin(u), S('2'))), a, v), n))
    replacer.add(rule5)

    pattern6 = Pattern(UtilityOperator(Add(WC('w', S(0)), u_, Mul(WC('v', S(1)), Pow(sin(z_), S('2'))))), CustomConstraint(lambda u, v: SameQ(u, Mul(S(-1), v))))
    rule6 = ReplacementRule(pattern6, lambda u, w, z, v : Add(Mul(u, Pow(Cos(z), S('2'))), w))
    replacer.add(rule6)

    pattern7 = Pattern(UtilityOperator(Add(Mul(Pow(cos(z_), S('2')), WC('v', S(1))), WC('w', S(0)), u_)), CustomConstraint(lambda u, v: SameQ(u, Mul(S(-1), v))))
    rule7 = ReplacementRule(pattern7, lambda z, w, v, u : Add(Mul(u, Pow(Sin(z), S('2'))), w))
    replacer.add(rule7)

    pattern8 = Pattern(UtilityOperator(Add(WC('w', S(0)), u_, Mul(WC('v', S(1)), Pow(tan(z_), S('2'))))), CustomConstraint(lambda u, v: SameQ(u, v)))
    rule8 = ReplacementRule(pattern8, lambda u, w, z, v : Add(Mul(u, Pow(Sec(z), S('2'))), w))
    replacer.add(rule8)

    pattern9 = Pattern(UtilityOperator(Add(Mul(Pow(cot(z_), S('2')), WC('v', S(1))), WC('w', S(0)), u_)), CustomConstraint(lambda u, v: SameQ(u, v)))
    rule9 = ReplacementRule(pattern9, lambda z, w, v, u : Add(Mul(u, Pow(Csc(z), S('2'))), w))
    replacer.add(rule9)

    pattern10 = Pattern(UtilityOperator(Add(WC('w', S(0)), u_, Mul(WC('v', S(1)), Pow(sec(z_), S('2'))))), CustomConstraint(lambda u, v: SameQ(u, Mul(S(-1), v))))
    rule10 = ReplacementRule(pattern10, lambda u, w, z, v : Add(Mul(v, Pow(Tan(z), S('2'))), w))
    replacer.add(rule10)

    pattern11 = Pattern(UtilityOperator(Add(Mul(Pow(csc(z_), S('2')), WC('v', S(1))), WC('w', S(0)), u_)), CustomConstraint(lambda u, v: SameQ(u, Mul(S(-1), v))))
    rule11 = ReplacementRule(pattern11, lambda z, w, v, u : Add(Mul(v, Pow(Cot(z), S('2'))), w))
    replacer.add(rule11)

    pattern12 = Pattern(UtilityOperator(Mul(WC('u', S(1)), Pow(Add(Mul(cos(v_), WC('b', S(1))), a_), S(-1)), Pow(sin(v_), S('2')))), CustomConstraint(lambda b, a: ZeroQ(Add(Pow(a, S('2')), Mul(S(-1), Pow(b, S('2')))))))
    rule12 = ReplacementRule(pattern12, lambda u, v, b, a : Mul(u, Add(Mul(S(1), Pow(a, S(-1))), Mul(S(-1), Mul(Cos(v), Pow(b, S(-1)))))))
    replacer.add(rule12)

    pattern13 = Pattern(UtilityOperator(Mul(Pow(cos(v_), S('2')), WC('u', S(1)), Pow(Add(a_, Mul(WC('b', S(1)), sin(v_))), S(-1)))), CustomConstraint(lambda b, a: ZeroQ(Add(Pow(a, S('2')), Mul(S(-1), Pow(b, S('2')))))))
    rule13 = ReplacementRule(pattern13, lambda u, v, b, a : Mul(u, Add(Mul(S(1), Pow(a, S(-1))), Mul(S(-1), Mul(Sin(v), Pow(b, S(-1)))))))
    replacer.add(rule13)

    pattern14 = Pattern(UtilityOperator(Mul(WC('u', S(1)), Pow(tan(v_), WC('n', S(1))), Pow(Add(a_, Mul(WC('b', S(1)), Pow(tan(v_), WC('n', S(1))))), S(-1)))), CustomConstraint(lambda n: PositiveIntegerQ(n)), CustomConstraint(lambda a: NonsumQ(a)))
    rule14 = ReplacementRule(pattern14, lambda n, a, u, v, b : Mul(u, Pow(Add(b, Mul(a, Pow(Cot(v), n))), S(-1))))
    replacer.add(rule14)

    pattern15 = Pattern(UtilityOperator(Mul(Pow(cot(v_), WC('n', S(1))), WC('u', S(1)), Pow(Add(Mul(Pow(cot(v_), WC('n', S(1))), WC('b', S(1))), a_), S(-1)))), CustomConstraint(lambda n: PositiveIntegerQ(n)), CustomConstraint(lambda a: NonsumQ(a)))
    rule15 = ReplacementRule(pattern15, lambda n, a, u, v, b : Mul(u, Pow(Add(b, Mul(a, Pow(Tan(v), n))), S(-1))))
    replacer.add(rule15)

    pattern16 = Pattern(UtilityOperator(Mul(WC('u', S(1)), Pow(sec(v_), WC('n', S(1))), Pow(Add(a_, Mul(WC('b', S(1)), Pow(sec(v_), WC('n', S(1))))), S(-1)))), CustomConstraint(lambda n: PositiveIntegerQ(n)), CustomConstraint(lambda a: NonsumQ(a)))
    rule16 = ReplacementRule(pattern16, lambda n, a, u, v, b : Mul(u, Pow(Add(b, Mul(a, Pow(Cos(v), n))), S(-1))))
    replacer.add(rule16)

    pattern17 = Pattern(UtilityOperator(Mul(Pow(csc(v_), WC('n', S(1))), WC('u', S(1)), Pow(Add(Mul(Pow(csc(v_), WC('n', S(1))), WC('b', S(1))), a_), S(-1)))), CustomConstraint(lambda n: PositiveIntegerQ(n)), CustomConstraint(lambda a: NonsumQ(a)))
    rule17 = ReplacementRule(pattern17, lambda n, a, u, v, b : Mul(u, Pow(Add(b, Mul(a, Pow(Sin(v), n))), S(-1))))
    replacer.add(rule17)

    pattern18 = Pattern(UtilityOperator(Mul(WC('u', S(1)), Pow(Add(a_, Mul(WC('b', S(1)), Pow(sec(v_), WC('n', S(1))))), S(-1)), Pow(tan(v_), WC('n', S(1))))), CustomConstraint(lambda n: PositiveIntegerQ(n)), CustomConstraint(lambda a: NonsumQ(a)))
    rule18 = ReplacementRule(pattern18, lambda n, a, u, v, b : Mul(u, Mul(Pow(Sin(v), n), Pow(Add(b, Mul(a, Pow(Cos(v), n))), S(-1)))))
    replacer.add(rule18)

    pattern19 = Pattern(UtilityOperator(Mul(Pow(cot(v_), WC('n', S(1))), WC('u', S(1)), Pow(Add(Mul(Pow(csc(v_), WC('n', S(1))), WC('b', S(1))), a_), S(-1)))), CustomConstraint(lambda n: PositiveIntegerQ(n)), CustomConstraint(lambda a: NonsumQ(a)))
    rule19 = ReplacementRule(pattern19, lambda n, a, u, v, b : Mul(u, Mul(Pow(Cos(v), n), Pow(Add(b, Mul(a, Pow(Sin(v), n))), S(-1)))))
    replacer.add(rule19)

    pattern20 = Pattern(UtilityOperator(Mul(WC('u', S(1)), Pow(Add(Mul(WC('a', S(1)), Pow(sec(v_), WC('n', S(1)))), Mul(WC('b', S(1)), Pow(tan(v_), WC('n', S(1))))), WC('p', S(1))))), CustomConstraint(lambda n, p: IntegersQ(n, p)))
    rule20 = ReplacementRule(pattern20, lambda n, a, p, u, v, b : Mul(u, Pow(Sec(v), Mul(n, p)), Pow(Add(a, Mul(b, Pow(Sin(v), n))), p)))
    replacer.add(rule20)

    pattern21 = Pattern(UtilityOperator(Mul(Pow(Add(Mul(Pow(csc(v_), WC('n', S(1))), WC('a', S(1))), Mul(Pow(cot(v_), WC('n', S(1))), WC('b', S(1)))), WC('p', S(1))), WC('u', S(1)))), CustomConstraint(lambda n, p: IntegersQ(n, p)))
    rule21 = ReplacementRule(pattern21, lambda n, a, p, u, v, b : Mul(u, Pow(Csc(v), Mul(n, p)), Pow(Add(a, Mul(b, Pow(Cos(v), n))), p)))
    replacer.add(rule21)

    pattern22 = Pattern(UtilityOperator(Mul(WC('u', S(1)), Pow(Add(Mul(WC('b', S(1)), Pow(sin(v_), WC('n', S(1)))), Mul(WC('a', S(1)), Pow(tan(v_), WC('n', S(1))))), WC('p', S(1))))), CustomConstraint(lambda n, p: IntegersQ(n, p)))
    rule22 = ReplacementRule(pattern22, lambda n, a, p, u, v, b : Mul(u, Pow(Tan(v), Mul(n, p)), Pow(Add(a, Mul(b, Pow(Cos(v), n))), p)))
    replacer.add(rule22)

    pattern23 = Pattern(UtilityOperator(Mul(Pow(Add(Mul(Pow(cot(v_), WC('n', S(1))), WC('a', S(1))), Mul(Pow(cos(v_), WC('n', S(1))), WC('b', S(1)))), WC('p', S(1))), WC('u', S(1)))), CustomConstraint(lambda n, p: IntegersQ(n, p)))
    rule23 = ReplacementRule(pattern23, lambda n, a, p, u, v, b : Mul(u, Pow(Cot(v), Mul(n, p)), Pow(Add(a, Mul(b, Pow(Sin(v), n))), p)))
    replacer.add(rule23)

    pattern24 = Pattern(UtilityOperator(Mul(Pow(cos(v_), WC('m', S(1))), WC('u', S(1)), Pow(Add(WC('a', S(0)), Mul(WC('c', S(1)), Pow(sec(v_), WC('n', S(1)))), Mul(WC('b', S(1)), Pow(tan(v_), WC('n', S(1))))), WC('p', S(1))))), CustomConstraint(lambda n, p, m: IntegersQ(m, n, p)))
    rule24 = ReplacementRule(pattern24, lambda n, a, c, p, m, u, v, b : Mul(u, Pow(Cos(v), Add(m, Mul(S(-1), Mul(n, p)))), Pow(Add(c, Mul(b, Pow(Sin(v), n)), Mul(a, Pow(Cos(v), n))), p)))
    replacer.add(rule24)

    pattern25 = Pattern(UtilityOperator(Mul(WC('u', S(1)), Pow(sec(v_), WC('m', S(1))), Pow(Add(WC('a', S(0)), Mul(WC('c', S(1)), Pow(sec(v_), WC('n', S(1)))), Mul(WC('b', S(1)), Pow(tan(v_), WC('n', S(1))))), WC('p', S(1))))), CustomConstraint(lambda n, p, m: IntegersQ(m, n, p)))
    rule25 = ReplacementRule(pattern25, lambda n, a, c, p, m, u, v, b : Mul(u, Pow(Sec(v), Add(m, Mul(n, p))), Pow(Add(c, Mul(b, Pow(Sin(v), n)), Mul(a, Pow(Cos(v), n))), p)))
    replacer.add(rule25)

    pattern26 = Pattern(UtilityOperator(Mul(Pow(Add(WC('a', S(0)), Mul(Pow(cot(v_), WC('n', S(1))), WC('b', S(1))), Mul(Pow(csc(v_), WC('n', S(1))), WC('c', S(1)))), WC('p', S(1))), WC('u', S(1)), Pow(sin(v_), WC('m', S(1))))), CustomConstraint(lambda n, p, m: IntegersQ(m, n, p)))
    rule26 = ReplacementRule(pattern26, lambda n, a, c, p, m, u, v, b : Mul(u, Pow(Sin(v), Add(m, Mul(S(-1), Mul(n, p)))), Pow(Add(c, Mul(b, Pow(Cos(v), n)), Mul(a, Pow(Sin(v), n))), p)))
    replacer.add(rule26)

    pattern27 = Pattern(UtilityOperator(Mul(Pow(csc(v_), WC('m', S(1))), Pow(Add(WC('a', S(0)), Mul(Pow(cot(v_), WC('n', S(1))), WC('b', S(1))), Mul(Pow(csc(v_), WC('n', S(1))), WC('c', S(1)))), WC('p', S(1))), WC('u', S(1)))), CustomConstraint(lambda n, p, m: IntegersQ(m, n, p)))
    rule27 = ReplacementRule(pattern27, lambda n, a, c, p, m, u, v, b : Mul(u, Pow(Csc(v), Add(m, Mul(n, p))), Pow(Add(c, Mul(b, Pow(Cos(v), n)), Mul(a, Pow(Sin(v), n))), p)))
    replacer.add(rule27)

    pattern28 = Pattern(UtilityOperator(Mul(WC('u', S(1)), Pow(Add(Mul(Pow(csc(v_), WC('m', S(1))), WC('a', S(1))), Mul(WC('b', S(1)), Pow(sin(v_), WC('n', S(1))))), WC('p', S(1))))), CustomConstraint(lambda n, m: IntegersQ(m, n)))
    rule28 = ReplacementRule(pattern28, lambda n, a, p, m, u, v, b : If(And(ZeroQ(Add(m, n, S(-2))), ZeroQ(Add(a, b))), Mul(u, Pow(Mul(a, Mul(Pow(Cos(v), S('2')), Pow(Pow(Sin(v), m), S(-1)))), p)), Mul(u, Pow(Mul(Add(a, Mul(b, Pow(Sin(v), Add(m, n)))), Pow(Pow(Sin(v), m), S(-1))), p))))
    replacer.add(rule28)

    pattern29 = Pattern(UtilityOperator(Mul(WC('u', S(1)), Pow(Add(Mul(Pow(cos(v_), WC('n', S(1))), WC('b', S(1))), Mul(WC('a', S(1)), Pow(sec(v_), WC('m', S(1))))), WC('p', S(1))))), CustomConstraint(lambda n, m: IntegersQ(m, n)))
    rule29 = ReplacementRule(pattern29, lambda n, a, p, m, u, v, b : If(And(ZeroQ(Add(m, n, S(-2))), ZeroQ(Add(a, b))), Mul(u, Pow(Mul(a, Mul(Pow(Sin(v), S('2')), Pow(Pow(Cos(v), m), S(-1)))), p)), Mul(u, Pow(Mul(Add(a, Mul(b, Pow(Cos(v), Add(m, n)))), Pow(Pow(Cos(v), m), S(-1))), p))))
    replacer.add(rule29)

    pattern30 = Pattern(UtilityOperator(u_))
    rule30 = ReplacementRule(pattern30, lambda u : u)
    replacer.add(rule30)

    return replacer

@doctest_depends_on(modules=('matchpy',))
def TrigSimplifyAux(expr):
    return TrigSimplifyAux_replacer.replace(UtilityOperator(expr))

def Cancel(expr):
    return cancel(expr)

class Util_Part(Function):
    def doit(self):
        i = Simplify(self.args[0])
        if len(self.args) > 2 :
            lst = list(self.args[1:])
        else:
            lst = self.args[1]
        if isinstance(i, (int, Integer)):
            if isinstance(lst, list):
                return lst[i - 1]
            elif AtomQ(lst):
                return lst
            return lst.args[i - 1]
        else:
            return self

def Part(lst, i): #see i = -1
    if isinstance(lst, list):
        return Util_Part(i, *lst).doit()
    return Util_Part(i, lst).doit()

def PolyLog(n, p, z=None):
    return polylog(n, p)

def D(f, x):
    try:
        return f.diff(x)
    except ValueError:
        return Function('D')(f, x)

def IntegralFreeQ(u):
    return FreeQ(u, Integral)

def Dist(u, v, x):
    #Dist(u,v) returns the sum of u times each term of v, provided v is free of Int
    u = replace_pow_exp(u) # to replace back to SymPy's exp
    v = replace_pow_exp(v)
    w = Simp(u*x**2, x)/x**2
    if u == 1:
        return v
    elif u == 0:
        return 0
    elif NumericFactor(u) < 0 and NumericFactor(-u) > 0:
        return -Dist(-u, v, x)
    elif SumQ(v):
        return Add(*[Dist(u, i, x) for i in v.args])
    elif IntegralFreeQ(v):
        return Simp(u*v, x)
    elif w != u and FreeQ(w, x) and w == Simp(w, x) and w == Simp(w*x**2, x)/x**2:
        return Dist(w, v, x)
    else:
        return Simp(u*v, x)

def PureFunctionOfCothQ(u, v, x):
    # If u is a pure function of Coth[v], PureFunctionOfCothQ[u,v,x] returns True;
    if AtomQ(u):
        return u != x
    elif CalculusQ(u):
        return False
    elif HyperbolicQ(u) and ZeroQ(u.args[0] - v):
        return CothQ(u)
    return all(PureFunctionOfCothQ(i, v, x) for i in u.args)

def LogIntegral(z):
    return li(z)

def ExpIntegralEi(z):
    return Ei(z)

def ExpIntegralE(a, b):
    return expint(a, b).evalf()

def SinIntegral(z):
    return Si(z)

def CosIntegral(z):
    return Ci(z)

def SinhIntegral(z):
    return Shi(z)

def CoshIntegral(z):
    return Chi(z)

class PolyGamma(Function):
    @classmethod
    def eval(cls, *args):
        if len(args) == 2:
            return polygamma(args[0], args[1])
        return digamma(args[0])

def LogGamma(z):
    return loggamma(z)

class ProductLog(Function):
    @classmethod
    def eval(cls, *args):
        if len(args) == 2:
            return LambertW(args[1], args[0]).evalf()
        return LambertW(args[0]).evalf()

def Factorial(a):
    return factorial(a)

def Zeta(*args):
    return zeta(*args)

def HypergeometricPFQ(a, b, c):
    return hyper(a, b, c)

def Sum_doit(exp, args):
    """
    This function perform summation using SymPy's `Sum`.

    Examples
    ========

    >>> from sympy.integrals.rubi.utility_function import Sum_doit
    >>> from sympy.abc import x
    >>> Sum_doit(2*x + 2, [x, 0, 1.7])
    6

    """
    exp = replace_pow_exp(exp)
    if not isinstance(args[2], (int, Integer)):
        new_args = [args[0], args[1], Floor(args[2])]
        return Sum(exp, new_args).doit()

    return Sum(exp, args).doit()

def PolynomialQuotient(p, q, x):
    try:
        p = poly(p, x)
        q = poly(q, x)

    except:
        p = poly(p)
        q = poly(q)
    try:
        return quo(p, q).as_expr()
    except (PolynomialDivisionFailed, UnificationFailed):
        return p/q

def PolynomialRemainder(p, q, x):
    try:
        p = poly(p, x)
        q = poly(q, x)

    except:
        p = poly(p)
        q = poly(q)
    try:
        return rem(p, q).as_expr()
    except (PolynomialDivisionFailed, UnificationFailed):
        return S(0)

def Floor(x, a = None):
    if a is None:
        return floor(x)
    return a*floor(x/a)

def Factor(var):
    return factor(var)

def Rule(a, b):
    return {a: b}

def Distribute(expr, *args):
    if len(args) == 1:
        if isinstance(expr, args[0]):
            return expr
        else:
            return expr.expand()
    if len(args) == 2:
        if isinstance(expr, args[1]):
            return expr.expand()
        else:
            return expr
    return expr.expand()

def CoprimeQ(*args):
    args = S(args)
    g = gcd(*args)
    if g == 1:
        return True
    return False

def Discriminant(a, b):
    try:
        return discriminant(a, b)
    except PolynomialError:
        return Function('Discriminant')(a, b)

def Negative(x):
    return x < S(0)

def Quotient(m, n):
    return Floor(m/n)

def process_trig(expr):
    """
    This function processes trigonometric expressions such that all `cot` is
    rewritten in terms of `tan`, `sec` in terms of `cos`, `csc` in terms of `sin` and
    similarly for `coth`, `sech` and `csch`.

    Examples
    ========

    >>> from sympy.integrals.rubi.utility_function import process_trig
    >>> from sympy.abc import x
    >>> from sympy import coth, cot, csc
    >>> process_trig(x*cot(x))
    x/tan(x)
    >>> process_trig(coth(x)*csc(x))
    1/(sin(x)*tanh(x))

    """
    expr = expr.replace(lambda x: isinstance(x, cot), lambda x: 1/tan(x.args[0]))
    expr = expr.replace(lambda x: isinstance(x, sec), lambda x: 1/cos(x.args[0]))
    expr = expr.replace(lambda x: isinstance(x, csc), lambda x: 1/sin(x.args[0]))
    expr = expr.replace(lambda x: isinstance(x, coth), lambda x: 1/tanh(x.args[0]))
    expr = expr.replace(lambda x: isinstance(x, sech), lambda x: 1/cosh(x.args[0]))
    expr = expr.replace(lambda x: isinstance(x, csch), lambda x: 1/sinh(x.args[0]))
    return expr

def _ExpandIntegrand():
    Plus = Add
    Times = Mul
    def cons_f1(m):
        return PositiveIntegerQ(m)

    cons1 = CustomConstraint(cons_f1)
    def cons_f2(d, c, b, a):
        return ZeroQ(-a*d + b*c)

    cons2 = CustomConstraint(cons_f2)
    def cons_f3(a, x):
        return FreeQ(a, x)

    cons3 = CustomConstraint(cons_f3)
    def cons_f4(b, x):
        return FreeQ(b, x)

    cons4 = CustomConstraint(cons_f4)
    def cons_f5(c, x):
        return FreeQ(c, x)

    cons5 = CustomConstraint(cons_f5)
    def cons_f6(d, x):
        return FreeQ(d, x)

    cons6 = CustomConstraint(cons_f6)
    def cons_f7(e, x):
        return FreeQ(e, x)

    cons7 = CustomConstraint(cons_f7)
    def cons_f8(f, x):
        return FreeQ(f, x)

    cons8 = CustomConstraint(cons_f8)
    def cons_f9(g, x):
        return FreeQ(g, x)

    cons9 = CustomConstraint(cons_f9)
    def cons_f10(h, x):
        return FreeQ(h, x)

    cons10 = CustomConstraint(cons_f10)
    def cons_f11(e, b, c, f, n, p, F, x, d, m):
        if not isinstance(x, Symbol):
            return False
        return FreeQ(List(F, b, c, d, e, f, m, n, p), x)

    cons11 = CustomConstraint(cons_f11)
    def cons_f12(F, x):
        return FreeQ(F, x)

    cons12 = CustomConstraint(cons_f12)
    def cons_f13(m, x):
        return FreeQ(m, x)

    cons13 = CustomConstraint(cons_f13)
    def cons_f14(n, x):
        return FreeQ(n, x)

    cons14 = CustomConstraint(cons_f14)
    def cons_f15(p, x):
        return FreeQ(p, x)

    cons15 = CustomConstraint(cons_f15)
    def cons_f16(e, b, c, f, n, a, p, F, x, d, m):
        if not isinstance(x, Symbol):
            return False
        return FreeQ(List(F, a, b, c, d, e, f, m, n, p), x)

    cons16 = CustomConstraint(cons_f16)
    def cons_f17(n, m):
        return IntegersQ(m, n)

    cons17 = CustomConstraint(cons_f17)
    def cons_f18(n):
        return Less(n, S(0))

    cons18 = CustomConstraint(cons_f18)
    def cons_f19(x, u):
        if not isinstance(x, Symbol):
            return False
        return PolynomialQ(u, x)

    cons19 = CustomConstraint(cons_f19)
    def cons_f20(G, F, u):
        return SameQ(F(u)*G(u), S(1))

    cons20 = CustomConstraint(cons_f20)
    def cons_f21(q, x):
        return FreeQ(q, x)

    cons21 = CustomConstraint(cons_f21)
    def cons_f22(F):
        return MemberQ(List(ArcSin, ArcCos, ArcSinh, ArcCosh), F)

    cons22 = CustomConstraint(cons_f22)
    def cons_f23(j, n):
        return ZeroQ(j - S(2)*n)

    cons23 = CustomConstraint(cons_f23)
    def cons_f24(A, x):
        return FreeQ(A, x)

    cons24 = CustomConstraint(cons_f24)
    def cons_f25(B, x):
        return FreeQ(B, x)

    cons25 = CustomConstraint(cons_f25)
    def cons_f26(m, u, x):
        if not isinstance(x, Symbol):
            return False
        def _cons_f_u(d, w, c, p, x):
            return And(FreeQ(List(c, d), x), IntegerQ(p), Greater(p, m))
        cons_u = CustomConstraint(_cons_f_u)
        pat = Pattern(UtilityOperator((c_ + x_*WC('d', S(1)))**p_*WC('w', S(1)), x_), cons_u)
        result_matchq = is_match(UtilityOperator(u, x), pat)
        return Not(And(PositiveIntegerQ(m), result_matchq))

    cons26 = CustomConstraint(cons_f26)
    def cons_f27(b, v, n, a, x, u, m):
        if not isinstance(x, Symbol):
            return False
        return And(FreeQ(List(a, b, m), x), NegativeIntegerQ(n), Not(IntegerQ(m)), PolynomialQ(u, x), PolynomialQ(v, x),\
            RationalQ(m), Less(m, -1), GreaterEqual(Exponent(u, x), (-n - IntegerPart(m))*Exponent(v, x)))
    cons27 = CustomConstraint(cons_f27)
    def cons_f28(v, n, x, u, m):
        if not isinstance(x, Symbol):
            return False
        return And(FreeQ(List(a, b, m), x), NegativeIntegerQ(n), Not(IntegerQ(m)), PolynomialQ(u, x),\
            PolynomialQ(v, x), GreaterEqual(Exponent(u, x), -n*Exponent(v, x)))
    cons28 = CustomConstraint(cons_f28)
    def cons_f29(n):
        return PositiveIntegerQ(n/S(4))

    cons29 = CustomConstraint(cons_f29)
    def cons_f30(n):
        return IntegerQ(n)

    cons30 = CustomConstraint(cons_f30)
    def cons_f31(n):
        return Greater(n, S(1))

    cons31 = CustomConstraint(cons_f31)
    def cons_f32(n, m):
        return Less(S(0), m, n)

    cons32 = CustomConstraint(cons_f32)
    def cons_f33(n, m):
        return OddQ(n/GCD(m, n))

    cons33 = CustomConstraint(cons_f33)
    def cons_f34(a, b):
        return PosQ(a/b)

    cons34 = CustomConstraint(cons_f34)
    def cons_f35(n, m, p):
        return IntegersQ(m, n, p)

    cons35 = CustomConstraint(cons_f35)
    def cons_f36(n, m, p):
        return Less(S(0), m, p, n)

    cons36 = CustomConstraint(cons_f36)
    def cons_f37(q, n, m, p):
        return IntegersQ(m, n, p, q)

    cons37 = CustomConstraint(cons_f37)
    def cons_f38(n, q, m, p):
        return Less(S(0), m, p, q, n)

    cons38 = CustomConstraint(cons_f38)
    def cons_f39(n):
        return IntegerQ(n/S(2))

    cons39 = CustomConstraint(cons_f39)
    def cons_f40(p):
        return NegativeIntegerQ(p)

    cons40 = CustomConstraint(cons_f40)
    def cons_f41(n, m):
        return IntegersQ(m, n/S(2))

    cons41 = CustomConstraint(cons_f41)
    def cons_f42(n, m):
        return Unequal(m, n/S(2))

    cons42 = CustomConstraint(cons_f42)
    def cons_f43(c, b, a):
        return NonzeroQ(-S(4)*a*c + b**S(2))

    cons43 = CustomConstraint(cons_f43)
    def cons_f44(j, n, m):
        return IntegersQ(m, n, j)

    cons44 = CustomConstraint(cons_f44)
    def cons_f45(n, m):
        return Less(S(0), m, S(2)*n)

    cons45 = CustomConstraint(cons_f45)
    def cons_f46(n, m, p):
        return Not(And(Equal(m, n), Equal(p, S(-1))))

    cons46 = CustomConstraint(cons_f46)
    def cons_f47(v, x):
        if not isinstance(x, Symbol):
            return False
        return PolynomialQ(v, x)

    cons47 = CustomConstraint(cons_f47)
    def cons_f48(v, x):
        if not isinstance(x, Symbol):
            return False
        return BinomialQ(v, x)

    cons48 = CustomConstraint(cons_f48)
    def cons_f49(v, x, u):
        if not isinstance(x, Symbol):
            return False
        return Inequality(Exponent(u, x), Equal, Exponent(v, x) + S(-1), GreaterEqual, S(2))

    cons49 = CustomConstraint(cons_f49)
    def cons_f50(v, x, u):
        if not isinstance(x, Symbol):
            return False
        return GreaterEqual(Exponent(u, x), Exponent(v, x))

    cons50 = CustomConstraint(cons_f50)
    def cons_f51(p):
        return Not(IntegerQ(p))

    cons51 = CustomConstraint(cons_f51)

    def With2(e, b, c, f, n, a, g, h, x, d, m):
        tmp = a*h - b*g
        k = Symbol('k')
        return f**(e*(c + d*x)**n)*SimplifyTerm(h**(-m)*tmp**m, x)/(g + h*x) + Sum_doit(f**(e*(c + d*x)**n)*(a + b*x)**(-k + m)*SimplifyTerm(b*h**(-k)*tmp**(k - 1), x), List(k, 1, m))
    pattern2 = Pattern(UtilityOperator(f_**((x_*WC('d', S(1)) + WC('c', S(0)))**WC('n', S(1))*WC('e', S(1)))*(x_*WC('b', S(1)) + WC('a', S(0)))**WC('m', S(1))/(x_*WC('h', S(1)) + WC('g', S(0))), x_), cons3, cons4, cons5, cons6, cons7, cons8, cons9, cons10, cons1, cons2)
    rule2 = ReplacementRule(pattern2, With2)
    pattern3 = Pattern(UtilityOperator(F_**((x_*WC('d', S(1)) + WC('c', S(0)))**WC('n', S(1))*WC('b', S(1)))*x_**WC('m', S(1))*(e_ + x_*WC('f', S(1)))**WC('p', S(1)), x_), cons12, cons4, cons5, cons6, cons7, cons8, cons13, cons14, cons15, cons11)
    def replacement3(e, b, c, f, n, p, F, x, d, m):
        return If(And(PositiveIntegerQ(m, p), LessEqual(m, p), Or(EqQ(n, S(1)), ZeroQ(-c*f + d*e))), ExpandLinearProduct(F**(b*(c + d*x)**n)*(e + f*x)**p, x**m, e, f, x), If(PositiveIntegerQ(p), Distribute(F**(b*(c + d*x)**n)*x**m*(e + f*x)**p, Plus, Times), ExpandIntegrand(F**(b*(c + d*x)**n), x**m*(e + f*x)**p, x)))
    rule3 = ReplacementRule(pattern3, replacement3)
    pattern4 = Pattern(UtilityOperator(F_**((x_*WC('d', S(1)) + WC('c', S(0)))**WC('n', S(1))*WC('b', S(1)) + WC('a', S(0)))*x_**WC('m', S(1))*(e_ + x_*WC('f', S(1)))**WC('p', S(1)), x_), cons12, cons3, cons4, cons5, cons6, cons7, cons8, cons13, cons14, cons15, cons16)
    def replacement4(e, b, c, f, n, a, p, F, x, d, m):
        return If(And(PositiveIntegerQ(m, p), LessEqual(m, p), Or(EqQ(n, S(1)), ZeroQ(-c*f + d*e))), ExpandLinearProduct(F**(a + b*(c + d*x)**n)*(e + f*x)**p, x**m, e, f, x), If(PositiveIntegerQ(p), Distribute(F**(a + b*(c + d*x)**n)*x**m*(e + f*x)**p, Plus, Times), ExpandIntegrand(F**(a + b*(c + d*x)**n), x**m*(e + f*x)**p, x)))
    rule4 = ReplacementRule(pattern4, replacement4)
    def With5(b, v, c, n, a, F, u, x, d, m):
        if not isinstance(x, Symbol) or not (FreeQ([F, a, b, c, d], x) and IntegersQ(m, n) and n < 0):
            return False
        w = ExpandIntegrand((a + b*x)**m*(c + d*x)**n, x)
        w = ReplaceAll(w, Rule(x, F**v))
        if SumQ(w):
            return True
        return False
    pattern5 = Pattern(UtilityOperator((F_**v_*WC('b', S(1)) + a_)**WC('m', S(1))*(F_**v_*WC('d', S(1)) + c_)**n_*WC('u', S(1)), x_), cons12, cons3, cons4, cons5, cons6, cons17, cons18, CustomConstraint(With5))
    def replacement5(b, v, c, n, a, F, u, x, d, m):
        w = ReplaceAll(ExpandIntegrand((a + b*x)**m*(c + d*x)**n, x), Rule(x, F**v))
        return w.func(*[u*i for i in w.args])
    rule5 = ReplacementRule(pattern5, replacement5)
    def With6(e, b, c, f, n, a, x, u, d, m):
        if not isinstance(x, Symbol) or not (FreeQ([a, b, c, d, e, f, m, n], x) and PolynomialQ(u,x)):
            return False
        v = ExpandIntegrand(u*(a + b*x)**m, x)
        if SumQ(v):
            return True
        return False
    pattern6 = Pattern(UtilityOperator(f_**((x_*WC('d', S(1)) + WC('c', S(0)))**WC('n', S(1))*WC('e', S(1)))*u_*(x_*WC('b', S(1)) + WC('a', S(0)))**WC('m', S(1)), x_), cons3, cons4, cons5, cons6, cons7, cons8, cons13, cons14, cons19, CustomConstraint(With6))
    def replacement6(e, b, c, f, n, a, x, u, d, m):
        v = ExpandIntegrand(u*(a + b*x)**m, x)
        return Distribute(f**(e*(c + d*x)**n)*v, Plus, Times)
    rule6 = ReplacementRule(pattern6, replacement6)
    pattern7 = Pattern(UtilityOperator(u_*(x_*WC('b', S(1)) + WC('a', S(0)))**WC('m', S(1))*Log((x_**WC('n', S(1))*WC('e', S(1)) + WC('d', S(0)))**WC('p', S(1))*WC('c', S(1))), x_), cons3, cons4, cons5, cons6, cons7, cons13, cons14, cons15, cons19)
    def replacement7(e, b, c, n, a, p, x, u, d, m):
        return ExpandIntegrand(Log(c*(d + e*x**n)**p), u*(a + b*x)**m, x)
    rule7 = ReplacementRule(pattern7, replacement7)
    pattern8 = Pattern(UtilityOperator(f_**((x_*WC('d', S(1)) + WC('c', S(0)))**WC('n', S(1))*WC('e', S(1)))*u_, x_), cons5, cons6, cons7, cons8, cons14, cons19)
    def replacement8(e, c, f, n, x, u, d):
        return If(EqQ(n, S(1)), ExpandIntegrand(f**(e*(c + d*x)**n), u, x), ExpandLinearProduct(f**(e*(c + d*x)**n), u, c, d, x))
    rule8 = ReplacementRule(pattern8, replacement8)
    # pattern9 = Pattern(UtilityOperator(F_**u_*(G_*u_*WC('b', S(1)) + a_)**WC('n', S(1)), x_), cons3, cons4, cons17, cons20)
    # def replacement9(b, G, n, a, F, u, x, m):
    #     return ReplaceAll(ExpandIntegrand(x**(-m)*(a + b*x)**n, x), Rule(x, G(u)))
    # rule9 = ReplacementRule(pattern9, replacement9)
    pattern10 = Pattern(UtilityOperator(u_*(WC('a', S(0)) + WC('b', S(1))*Log(((x_*WC('f', S(1)) + WC('e', S(0)))**WC('p', S(1))*WC('d', S(1)))**WC('q', S(1))*WC('c', S(1))))**n_, x_), cons3, cons4, cons5, cons6, cons7, cons8, cons14, cons15, cons21, cons19)
    def replacement10(e, b, c, f, n, a, p, x, u, d, q):
        return ExpandLinearProduct((a + b*Log(c*(d*(e + f*x)**p)**q))**n, u, e, f, x)
    rule10 = ReplacementRule(pattern10, replacement10)
    # pattern11 = Pattern(UtilityOperator(u_*(F_*(x_*WC('d', S(1)) + WC('c', S(0)))*WC('b', S(1)) + WC('a', S(0)))**n_, x_), cons3, cons4, cons5, cons6, cons14, cons19, cons22)
    # def replacement11(b, c, n, a, F, u, x, d):
    #     return ExpandLinearProduct((a + b*F(c + d*x))**n, u, c, d, x)
    # rule11 = ReplacementRule(pattern11, replacement11)
    pattern12 = Pattern(UtilityOperator(WC('u', S(1))/(x_**n_*WC('a', S(1)) + sqrt(c_ + x_**j_*WC('d', S(1)))*WC('b', S(1))), x_), cons3, cons4, cons5, cons6, cons14, cons23)
    def replacement12(b, c, n, a, x, u, d, j):
        return ExpandIntegrand(u*(a*x**n - b*sqrt(c + d*x**(S(2)*n)))/(-b**S(2)*c + x**(S(2)*n)*(a**S(2) - b**S(2)*d)), x)
    rule12 = ReplacementRule(pattern12, replacement12)
    pattern13 = Pattern(UtilityOperator((a_ + x_*WC('b', S(1)))**m_/(c_ + x_*WC('d', S(1))), x_), cons3, cons4, cons5, cons6, cons1)
    def replacement13(b, c, a, x, d, m):
        if RationalQ(a, b, c, d):
            return ExpandExpression((a + b*x)**m/(c + d*x), x)
        else:
            tmp = a*d - b*c
            k = Symbol("k")
            return Sum_doit((a + b*x)**(-k + m)*SimplifyTerm(b*d**(-k)*tmp**(k + S(-1)), x), List(k, S(1), m)) + SimplifyTerm(d**(-m)*tmp**m, x)/(c + d*x)

    rule13 = ReplacementRule(pattern13, replacement13)
    pattern14 = Pattern(UtilityOperator((A_ + x_*WC('B', S(1)))*(a_ + x_*WC('b', S(1)))**WC('m', S(1))/(c_ + x_*WC('d', S(1))), x_), cons3, cons4, cons5, cons6, cons24, cons25, cons1)
    def replacement14(b, B, A, c, a, x, d, m):
        if RationalQ(a, b, c, d, A, B):
            return ExpandExpression((A + B*x)*(a + b*x)**m/(c + d*x), x)
        else:
            tmp1 = (A*d - B*c)/d
            tmp2 = ExpandIntegrand((a + b*x)**m/(c + d*x), x)
            tmp2 = If(SumQ(tmp2), tmp2.func(*[SimplifyTerm(tmp1*i, x) for i in tmp2.args]), SimplifyTerm(tmp1*tmp2, x))
            return SimplifyTerm(B/d, x)*(a + b*x)**m + tmp2
    rule14 = ReplacementRule(pattern14, replacement14)

    def With15(b, a, x, u, m):
        tmp1 = ExpandLinearProduct((a + b*x)**m, u, a, b, x)
        if not IntegerQ(m):
            return tmp1
        else:
            tmp2 = ExpandExpression(u*(a + b*x)**m, x)
            if SumQ(tmp2) and LessEqual(LeafCount(tmp2), LeafCount(tmp1) + S(2)):
                return tmp2
            else:
                return tmp1
    pattern15 = Pattern(UtilityOperator(u_*(a_ + x_*WC('b', S(1)))**m_, x_), cons3, cons4, cons13, cons19, cons26)
    rule15 = ReplacementRule(pattern15, With15)
    pattern16 = Pattern(UtilityOperator(u_*v_**n_*(a_ + x_*WC('b', S(1)))**m_, x_), cons27)
    def replacement16(b, v, n, a, x, u, m):
        s = PolynomialQuotientRemainder(u, v**(-n)*(a+b*x)**(-IntegerPart(m)), x)
        return ExpandIntegrand((a + b*x)**FractionalPart(m)*s[0], x) + ExpandIntegrand(v**n*(a + b*x)**m*s[1], x)
    rule16 = ReplacementRule(pattern16, replacement16)

    pattern17 = Pattern(UtilityOperator(u_*v_**n_*(a_ + x_*WC('b', S(1)))**m_, x_), cons28)
    def replacement17(b, v, n, a, x, u, m):
        s = PolynomialQuotientRemainder(u, v**(-n),x)
        return ExpandIntegrand((a + b*x)**(m)*s[0], x) + ExpandIntegrand(v**n*(a + b*x)**m*s[1], x)
    rule17 = ReplacementRule(pattern17, replacement17)

    def With18(b, n, a, x, u):
        r = Numerator(Rt(-a/b, S(2)))
        s = Denominator(Rt(-a/b, S(2)))
        return r/(S(2)*a*(r + s*u**(n/S(2)))) + r/(S(2)*a*(r - s*u**(n/S(2))))
    pattern18 = Pattern(UtilityOperator(S(1)/(a_ + u_**n_*WC('b', S(1))), x_), cons3, cons4, cons29)
    rule18 = ReplacementRule(pattern18, With18)
    def With19(b, n, a, x, u):
        k = Symbol("k")
        r = Numerator(Rt(-a/b, n))
        s = Denominator(Rt(-a/b, n))
        return Sum_doit(r/(a*n*(-(-1)**(2*k/n)*s*u + r)), List(k, 1, n))
    pattern19 = Pattern(UtilityOperator(S(1)/(a_ + u_**n_*WC('b', S(1))), x_), cons3, cons4, cons30, cons31)
    rule19 = ReplacementRule(pattern19, With19)
    def With20(b, n, a, x, u, m):
        k = Symbol("k")
        g = GCD(m, n)
        r = Numerator(Rt(a/b, n/GCD(m, n)))
        s = Denominator(Rt(a/b, n/GCD(m, n)))
        return If(CoprimeQ(g + m, n), Sum_doit((-1)**(-2*k*m/n)*r*(-r/s)**(m/g)/(a*n*((-1)**(2*g*k/n)*s*u**g + r)), List(k, 1, n/g)), Sum_doit((-1)**(2*k*(g + m)/n)*r*(-r/s)**(m/g)/(a*n*((-1)**(2*g*k/n)*r + s*u**g)), List(k, 1, n/g)))
    pattern20 = Pattern(UtilityOperator(u_**WC('m', S(1))/(a_ + u_**n_*WC('b', S(1))), x_), cons3, cons4, cons17, cons32, cons33, cons34)
    rule20 = ReplacementRule(pattern20, With20)
    def With21(b, n, a, x, u, m):
        k = Symbol("k")
        g = GCD(m, n)
        r = Numerator(Rt(-a/b, n/GCD(m, n)))
        s = Denominator(Rt(-a/b, n/GCD(m, n)))
        return If(Equal(n/g, S(2)), s/(S(2)*b*(r + s*u**g)) - s/(S(2)*b*(r - s*u**g)), If(CoprimeQ(g + m, n), Sum_doit((S(-1))**(-S(2)*k*m/n)*r*(r/s)**(m/g)/(a*n*(-(S(-1))**(S(2)*g*k/n)*s*u**g + r)), List(k, S(1), n/g)), Sum_doit((S(-1))**(S(2)*k*(g + m)/n)*r*(r/s)**(m/g)/(a*n*((S(-1))**(S(2)*g*k/n)*r - s*u**g)), List(k, S(1), n/g))))
    pattern21 = Pattern(UtilityOperator(u_**WC('m', S(1))/(a_ + u_**n_*WC('b', S(1))), x_), cons3, cons4, cons17, cons32)
    rule21 = ReplacementRule(pattern21, With21)
    def With22(b, c, n, a, x, u, d, m):
        k = Symbol("k")
        r = Numerator(Rt(-a/b, n))
        s = Denominator(Rt(-a/b, n))
        return Sum_doit((c*r + (-1)**(-2*k*m/n)*d*r*(r/s)**m)/(a*n*(-(-1)**(2*k/n)*s*u + r)), List(k, 1, n))
    pattern22 = Pattern(UtilityOperator((c_ + u_**WC('m', S(1))*WC('d', S(1)))/(a_ + u_**n_*WC('b', S(1))), x_), cons3, cons4, cons5, cons6, cons17, cons32)
    rule22 = ReplacementRule(pattern22, With22)
    def With23(e, b, c, n, a, p, x, u, d, m):
        k = Symbol("k")
        r = Numerator(Rt(-a/b, n))
        s = Denominator(Rt(-a/b, n))
        return Sum_doit((c*r + (-1)**(-2*k*p/n)*e*r*(r/s)**p + (-1)**(-2*k*m/n)*d*r*(r/s)**m)/(a*n*(-(-1)**(2*k/n)*s*u + r)), List(k, 1, n))
    pattern23 = Pattern(UtilityOperator((u_**p_*WC('e', S(1)) + u_**WC('m', S(1))*WC('d', S(1)) + WC('c', S(0)))/(a_ + u_**n_*WC('b', S(1))), x_), cons3, cons4, cons5, cons6, cons7, cons35, cons36)
    rule23 = ReplacementRule(pattern23, With23)
    def With24(e, b, c, f, n, a, p, x, u, d, q, m):
        k = Symbol("k")
        r = Numerator(Rt(-a/b, n))
        s = Denominator(Rt(-a/b, n))
        return Sum_doit((c*r + (-1)**(-2*k*q/n)*f*r*(r/s)**q + (-1)**(-2*k*p/n)*e*r*(r/s)**p + (-1)**(-2*k*m/n)*d*r*(r/s)**m)/(a*n*(-(-1)**(2*k/n)*s*u + r)), List(k, 1, n))
    pattern24 = Pattern(UtilityOperator((u_**p_*WC('e', S(1)) + u_**q_*WC('f', S(1)) + u_**WC('m', S(1))*WC('d', S(1)) + WC('c', S(0)))/(a_ + u_**n_*WC('b', S(1))), x_), cons3, cons4, cons5, cons6, cons7, cons8, cons37, cons38)
    rule24 = ReplacementRule(pattern24, With24)
    def With25(c, n, a, p, x, u):
        q = Symbol('q')
        return ReplaceAll(ExpandIntegrand(c**(-p), (c*x - q)**p*(c*x + q)**p, x), List(Rule(q, Rt(-a*c, S(2))), Rule(x, u**(n/S(2)))))
    pattern25 = Pattern(UtilityOperator((a_ + u_**WC('n', S(1))*WC('c', S(1)))**p_, x_), cons3, cons5, cons39, cons40)
    rule25 = ReplacementRule(pattern25, With25)
    def With26(c, n, a, p, x, u, m):
        q = Symbol('q')
        return ReplaceAll(ExpandIntegrand(c**(-p), x**m*(c*x**(n/S(2)) - q)**p*(c*x**(n/S(2)) + q)**p, x), List(Rule(q, Rt(-a*c, S(2))), Rule(x, u)))
    pattern26 = Pattern(UtilityOperator(u_**WC('m', S(1))*(u_**WC('n', S(1))*WC('c', S(1)) + WC('a', S(0)))**p_, x_), cons3, cons5, cons41, cons40, cons32, cons42)
    rule26 = ReplacementRule(pattern26, With26)
    def With27(b, c, n, a, p, x, u, j):
        q = Symbol('q')
        return ReplaceAll(ExpandIntegrand(S(4)**(-p)*c**(-p), (b + S(2)*c*x - q)**p*(b + S(2)*c*x + q)**p, x), List(Rule(q, Rt(-S(4)*a*c + b**S(2), S(2))), Rule(x, u**n)))
    pattern27 = Pattern(UtilityOperator((u_**WC('j', S(1))*WC('c', S(1)) + u_**WC('n', S(1))*WC('b', S(1)) + WC('a', S(0)))**p_, x_), cons3, cons4, cons5, cons30, cons23, cons40, cons43)
    rule27 = ReplacementRule(pattern27, With27)
    def With28(b, c, n, a, p, x, u, j, m):
        q = Symbol('q')
        return ReplaceAll(ExpandIntegrand(S(4)**(-p)*c**(-p), x**m*(b + S(2)*c*x**n - q)**p*(b + S(2)*c*x**n + q)**p, x), List(Rule(q, Rt(-S(4)*a*c + b**S(2), S(2))), Rule(x, u)))
    pattern28 = Pattern(UtilityOperator(u_**WC('m', S(1))*(u_**WC('j', S(1))*WC('c', S(1)) + u_**WC('n', S(1))*WC('b', S(1)) + WC('a', S(0)))**p_, x_), cons3, cons4, cons5, cons44, cons23, cons40, cons45, cons46, cons43)
    rule28 = ReplacementRule(pattern28, With28)
    def With29(b, c, n, a, x, u, d, j):
        q = Rt(-a/b, S(2))
        return -(c - d*q)/(S(2)*b*q*(q + u**n)) - (c + d*q)/(S(2)*b*q*(q - u**n))
    pattern29 = Pattern(UtilityOperator((u_**WC('n', S(1))*WC('d', S(1)) + WC('c', S(0)))/(a_ + u_**WC('j', S(1))*WC('b', S(1))), x_), cons3, cons4, cons5, cons6, cons14, cons23)
    rule29 = ReplacementRule(pattern29, With29)
    def With30(e, b, c, f, n, a, g, x, u, d, j):
        q = Rt(-S(4)*a*c + b**S(2), S(2))
        r = TogetherSimplify((-b*e*g + S(2)*c*(d + e*f))/q)
        return (e*g - r)/(b + 2*c*u**n + q) + (e*g + r)/(b + 2*c*u**n - q)
    pattern30 = Pattern(UtilityOperator(((u_**WC('n', S(1))*WC('g', S(1)) + WC('f', S(0)))*WC('e', S(1)) + WC('d', S(0)))/(u_**WC('j', S(1))*WC('c', S(1)) + u_**WC('n', S(1))*WC('b', S(1)) + WC('a', S(0))), x_), cons3, cons4, cons5, cons6, cons7, cons8, cons9, cons14, cons23, cons43)
    rule30 = ReplacementRule(pattern30, With30)
    def With31(v, x, u):
        lst = CoefficientList(u, x)
        i = Symbol('i')
        return x**Exponent(u, x)*lst[-1]/v + Sum_doit(x**(i - 1)*Part(lst, i), List(i, 1, Exponent(u, x)))/v
    pattern31 = Pattern(UtilityOperator(u_/v_, x_), cons19, cons47, cons48, cons49)
    rule31 = ReplacementRule(pattern31, With31)
    pattern32 = Pattern(UtilityOperator(u_/v_, x_), cons19, cons47, cons50)
    def replacement32(v, x, u):
        return PolynomialDivide(u, v, x)
    rule32 = ReplacementRule(pattern32, replacement32)
    pattern33 = Pattern(UtilityOperator(u_*(x_*WC('a', S(1)))**p_, x_), cons51, cons19)
    def replacement33(x, a, u, p):
        return ExpandToSum((a*x)**p, u, x)
    rule33 = ReplacementRule(pattern33, replacement33)
    pattern34 = Pattern(UtilityOperator(v_**p_*WC('u', S(1)), x_), cons51)
    def replacement34(v, x, u, p):
        return ExpandIntegrand(NormalizeIntegrand(v**p, x), u, x)
    rule34 = ReplacementRule(pattern34, replacement34)
    pattern35 = Pattern(UtilityOperator(u_, x_))
    def replacement35(x, u):
        return ExpandExpression(u, x)
    rule35 = ReplacementRule(pattern35, replacement35)
    return [ rule2,rule3, rule4, rule5, rule6, rule7, rule8, rule10, rule12, rule13, rule14, rule15, rule16, rule17, rule18, rule19, rule20, rule21, rule22, rule23, rule24, rule25, rule26, rule27, rule28, rule29, rule30, rule31, rule32, rule33, rule34, rule35]

def _RemoveContentAux():
    def cons_f1(b, a):
        return IntegersQ(a, b)

    cons1 = CustomConstraint(cons_f1)

    def cons_f2(b, a):
        return Equal(a + b, S(0))

    cons2 = CustomConstraint(cons_f2)

    def cons_f3(m):
        return RationalQ(m)

    cons3 = CustomConstraint(cons_f3)

    def cons_f4(m, n):
        return RationalQ(m, n)

    cons4 = CustomConstraint(cons_f4)

    def cons_f5(m, n):
        return GreaterEqual(-m + n, S(0))

    cons5 = CustomConstraint(cons_f5)

    def cons_f6(a, x):
        return FreeQ(a, x)

    cons6 = CustomConstraint(cons_f6)

    def cons_f7(m, n, p):
        return RationalQ(m, n, p)

    cons7 = CustomConstraint(cons_f7)

    def cons_f8(m, p):
        return GreaterEqual(-m + p, S(0))

    cons8 = CustomConstraint(cons_f8)

    pattern1 = Pattern(UtilityOperator(a_**m_*WC('u', S(1)) + b_*WC('v', S(1)), x_), cons1, cons2, cons3)
    def replacement1(v, x, a, u, m, b):
        return If(Greater(m, S(1)), RemoveContentAux(a**(m + S(-1))*u - v, x), RemoveContentAux(-a**(-m + S(1))*v + u, x))
    rule1 = ReplacementRule(pattern1, replacement1)
    pattern2 = Pattern(UtilityOperator(a_**WC('m', S(1))*WC('u', S(1)) + a_**WC('n', S(1))*WC('v', S(1)), x_), cons6, cons4, cons5)
    def replacement2(n, v, x, u, m, a):
        return RemoveContentAux(a**(-m + n)*v + u, x)
    rule2 = ReplacementRule(pattern2, replacement2)
    pattern3 = Pattern(UtilityOperator(a_**WC('m', S(1))*WC('u', S(1)) + a_**WC('n', S(1))*WC('v', S(1)) + a_**WC('p', S(1))*WC('w', S(1)), x_), cons6, cons7, cons5, cons8)
    def replacement3(n, v, x, p, u, w, m, a):
        return RemoveContentAux(a**(-m + n)*v + a**(-m + p)*w + u, x)
    rule3 = ReplacementRule(pattern3, replacement3)
    pattern4 = Pattern(UtilityOperator(u_, x_))
    def replacement4(u, x):
        return If(And(SumQ(u), NegQ(First(u))), -u, u)
    rule4 = ReplacementRule(pattern4, replacement4)
    return [rule1, rule2, rule3, rule4, ]

IntHide = Int
Log = rubi_log
Null = None
if matchpy:
    RemoveContentAux_replacer = ManyToOneReplacer(* _RemoveContentAux())
    ExpandIntegrand_rules = _ExpandIntegrand()
    TrigSimplifyAux_replacer = _TrigSimplifyAux()
    SimplifyAntiderivative_replacer = _SimplifyAntiderivative()
    SimplifyAntiderivativeSum_replacer = _SimplifyAntiderivativeSum()
    FixSimplify_rules = _FixSimplify()
    SimpFixFactor_replacer = _SimpFixFactor()
