Repository: sympy/sympy
File: sympy/integrals/manualintegrate.py (Python)

Code before:
    if integrand == csc(symbol) * cot(symbol):
        return CscCotRule(integrand, symbol)


def trig_cmplx_exp_rule(integral: IntegralInfo):
    """
    Strategy that rewrites sin, cos, sinh, and cosh in terms of complex exponentials.
    Useful for integration techniques that handle exponentials better.
    Applies only when the integrand belongs to a class that benefits from exponential rewriting,
    such as combinations involving Error functions.

    sin(x)  -> (exp(i*x) - exp(-i*x)) / (2*i)
    cos(x)  -> (exp(i*x) + exp(-i*x)) / 2
    sinh(x) -> (exp(x) - exp(-x)) / 2
    cosh(x) -> (exp(x) + exp(-x)) / 2
    """
    integrand, symbol = integral
    requires_exp_trig_rewriting = (erf, erfc, erfi)

    if not any(integrand.has(f) for f in requires_exp_trig_rewriting):
        return

    # Replace trig and hyperbolic functions with their exponential forms
    replaced = integrand.replace(
        lambda expr: expr.func in (sin, cos, sinh, cosh),
        lambda expr: expr.rewrite(exp)
    )

    if replaced != integrand:
        steps = integral_steps(replaced, symbol)
        return RewriteRule(integrand, symbol, replaced, steps)


def quadratic_denom_rule(integral):
    integrand, symbol = integral
    a = Wild('a', exclude=[symbol])
    b = Wild('b', exclude=[symbol])
    c = Wild('c', exclude=[symbol])

    match = integrand.match(a / (b * symbol ** 2 + c))

    if match:
        a, b, c = match[a], match[b], match[c]
        general_rule = ArctanRule(integrand, symbol, a, b, c)
        if b.is_extended_real and c.is_extended_real:
            positive_cond = c/b > 0
            if positive_cond is S.true:
                return general_rule
            coeff = a/(2*sqrt(-c)*sqrt(b))
            constant = sqrt(-c/b)
            r1 = 1/(symbol-constant)

Review comment (suggestion):
You can do this directly with `rewrite`:
```python
In [6]: (sin(x) + cos(x)).rewrite([sin, exp], tan)
Out[6]: 
               ⎛x⎞  
          2⋅tan⎜─⎟  
               ⎝2⎠  
cos(x) + ───────────
            2⎛x⎞    
         tan ⎜─⎟ + 1
             ⎝2⎠ 
```
It is probably better though to only do this for the expressions that contain the integration variable.

Code after:
    if integrand == sec(symbol) * tan(symbol):
        return SecTanRule(integrand, symbol)
    if integrand == csc(symbol) * cot(symbol):
        return CscCotRule(integrand, symbol)


def trig_cmplx_exp_rule(integral: IntegralInfo):
    """
    Strategy that rewrites sin, cos, sinh, and cosh in terms of complex exponentials.
    Useful for integration techniques that handle exponentials better.
    Applies only when the integrand belongs to a class that benefits from exponential rewriting,
    such as combinations involving Gaussian exponentials.

    sin(x)  -> (exp(i*x) - exp(-i*x)) / (2*i)
    cos(x)  -> (exp(i*x) + exp(-i*x)) / 2
    sinh(x) -> (exp(x) - exp(-x)) / 2
    cosh(x) -> (exp(x) + exp(-x)) / 2
    """
    integrand, symbol = integral

    if not integrand.has(exp) and not integrand.has(sin, cos, sinh, cosh):
        return

    a = Wild('a', exclude=[symbol, 0])
    b = Wild('b', exclude=[symbol])
    pattern = exp(a * symbol**2 + b * symbol)
    if not any(term.match(pattern) for term in integrand.atoms(exp)):
        return

    # Replace trig and hyperbolic functions with their exponential forms
    rewritten = integrand.rewrite([sin, cos, sinh, cosh], exp)

    if rewritten != integrand:
        steps = integral_steps(rewritten, symbol)
        return RewriteRule(integrand, symbol, rewritten, steps)


def quadratic_denom_rule(integral):
    integrand, symbol = integral
    a = Wild('a', exclude=[symbol])
    b = Wild('b', exclude=[symbol])
    c = Wild('c', exclude=[symbol])

    match = integrand.match(a / (b * symbol ** 2 + c))

    if match:
        a, b, c = match[a], match[b], match[c]
        general_rule = ArctanRule(integrand, symbol, a, b, c)
        if b.is_extended_real and c.is_extended_real:
            positive_cond = c/b > 0
            if positive_cond is S.true: