Repository: sympy/sympy

Issue:
limit: SymPy returns unevaluated limit
The following limit comes out unevaluated:
```
In [1]: func = sin(x)**2 - cos(x) + tan(x)*csc(x)
In [2]: limit(func, x, oo)
Out[2]:
     /   2                            \
 lim \sin (x) - cos(x) + tan(x)*csc(x)/
x->oo
```

Discussion:

Patch:
diff --git a/.mailmap b/.mailmap
index edf341362f..4649666dea 100644
--- a/.mailmap
+++ b/.mailmap
@@ -1233,7 +1233,7 @@ alijosephine <alijosephine@gmail.com>
 amsuhane <ayushsuhane99@iitkgp.ac.in> amsuhane <“ayushsuhane99@iitkgp.ac.in”>
 anca-mc <anca-mc@users.noreply.github.com>
 andreo <andrey.torba@gmail.com>
-anutosh491 <andersonbhat491@gmail.com>
+anutosh491 <andersonbhat491@gmail.com> Anutosh Bhat <87052487+anutosh491@users.noreply.github.com>
 arooshiverma <av22@iitbbs.ac.in>
 bluebrook <perl4logic@gmail.com>
 carstimon <carstimon@gmail.com>
diff --git a/sympy/functions/elementary/trigonometric.py b/sympy/functions/elementary/trigonometric.py
index 88cb604348..eb7f898ae6 100644
--- a/sympy/functions/elementary/trigonometric.py
+++ b/sympy/functions/elementary/trigonometric.py
@@ -1268,12 +1268,18 @@ def _eval_rewrite_as_sqrt(self, arg, **kwargs):
         return y
 
     def _eval_as_leading_term(self, x, logx=None, cdir=0):
+        from sympy.calculus.accumulationbounds import AccumBounds
+        from sympy.functions.elementary.complexes import re
         arg = self.args[0]
         x0 = arg.subs(x, 0).cancel()
         n = 2*x0/S.Pi
         if n.is_integer:
             lt = (arg - n*S.Pi/2).as_leading_term(x)
             return lt if n.is_even else -1/lt
+        if x0 is S.ComplexInfinity:
+            x0 = arg.limit(x, 0, dir='-' if re(cdir).is_negative else '+')
+        if x0 in (S.Infinity, S.NegativeInfinity):
+            return AccumBounds(S.NegativeInfinity, S.Infinity)
         return self.func(x0) if x0.is_finite else self
 
     def _eval_is_extended_real(self):
@@ -1367,6 +1373,8 @@ def eval(cls, arg):
                 return S.NaN
             if arg.is_zero:
                 return S.ComplexInfinity
+            elif arg in (S.Infinity, S.NegativeInfinity):
+                return AccumBounds(S.NegativeInfinity, S.Infinity)
 
         if arg is S.ComplexInfinity:
             return S.NaN
@@ -1550,12 +1558,18 @@ def _eval_rewrite_as_sqrt(self, arg, **kwargs):
         return y
 
     def _eval_as_leading_term(self, x, logx=None, cdir=0):
+        from sympy.calculus.accumulationbounds import AccumBounds
+        from sympy.functions.elementary.complexes import re
         arg = self.args[0]
         x0 = arg.subs(x, 0).cancel()
         n = 2*x0/S.Pi
         if n.is_integer:
             lt = (arg - n*S.Pi/2).as_leading_term(x)
             return 1/lt if n.is_even else -lt
+        if x0 is S.ComplexInfinity:
+            x0 = arg.limit(x, 0, dir='-' if re(cdir).is_negative else '+')
+        if x0 in (S.Infinity, S.NegativeInfinity):
+            return AccumBounds(S.NegativeInfinity, S.Infinity)
         return self.func(x0) if x0.is_finite else self
 
     def _eval_is_extended_real(self):
@@ -1826,13 +1840,19 @@ def taylor_term(n, x, *previous_terms):
             return S.NegativeOne**k*euler(2*k)/factorial(2*k)*x**(2*k)
 
     def _eval_as_leading_term(self, x, logx=None, cdir=0):
+        from sympy.calculus.accumulationbounds import AccumBounds
+        from sympy.functions.elementary.complexes import re
         arg = self.args[0]
         x0 = arg.subs(x, 0).cancel()
         n = (x0 + S.Pi/2)/S.Pi
         if n.is_integer:
             lt = (arg - n*S.Pi + S.Pi/2).as_leading_term(x)
             return (S.NegativeOne**n)/lt
-        return self.func(x0)
+        if x0 is S.ComplexInfinity:
+            x0 = arg.limit(x, 0, dir='-' if re(cdir).is_negative else '+')
+        if x0 in (S.Infinity, S.NegativeInfinity):
+            return AccumBounds(S.NegativeInfinity, S.Infinity)
+        return self.func(x0) if x0.is_finite else self
 
 
 class csc(ReciprocalTrigonometricFunction):
@@ -1921,6 +1941,21 @@ def taylor_term(n, x, *previous_terms):
             return (S.NegativeOne**(k - 1)*2*(2**(2*k - 1) - 1)*
                     bernoulli(2*k)*x**(2*k - 1)/factorial(2*k))
 
+    def _eval_as_leading_term(self, x, logx=None, cdir=0):
+        from sympy.calculus.accumulationbounds import AccumBounds
+        from sympy.functions.elementary.complexes import re
+        arg = self.args[0]
+        x0 = arg.subs(x, 0).cancel()
+        n = x0/S.Pi
+        if n.is_integer:
+            lt = (arg - n*S.Pi).as_leading_term(x)
+            return (S.NegativeOne**n)/lt
+        if x0 is S.ComplexInfinity:
+            x0 = arg.limit(x, 0, dir='-' if re(cdir).is_negative else '+')
+        if x0 in (S.Infinity, S.NegativeInfinity):
+            return AccumBounds(S.NegativeInfinity, S.Infinity)
+        return self.func(x0) if x0.is_finite else self
+
 
 class sinc(Function):
     r"""
diff --git a/sympy/series/limits.py b/sympy/series/limits.py
index e535e82a63..3e1470ad4a 100644
--- a/sympy/series/limits.py
+++ b/sympy/series/limits.py
@@ -313,6 +313,8 @@ def set_signs(expr):
                 if r is not None:
                     return r
         else:
+            if isinstance(coeff, AccumBounds) and ex == S.Zero:
+                return coeff
             if coeff.has(S.Infinity, S.NegativeInfinity, S.ComplexInfinity):
                 return self
             if not coeff.has(z):


Test patch:
diff --git a/sympy/functions/elementary/tests/test_trigonometric.py b/sympy/functions/elementary/tests/test_trigonometric.py
index 20b4b4083e..55e1ff8258 100644
--- a/sympy/functions/elementary/tests/test_trigonometric.py
+++ b/sympy/functions/elementary/tests/test_trigonometric.py
@@ -10,7 +10,8 @@
 from sympy.functions.elementary.exponential import (exp, log)
 from sympy.functions.elementary.hyperbolic import (acoth, asinh, atanh, cosh, coth, sinh, tanh)
 from sympy.functions.elementary.miscellaneous import sqrt
-from sympy.functions.elementary.trigonometric import (acos, acot, acsc, asec, asin, atan, atan2, cos, cot, csc, sec, sin, sinc, tan)
+from sympy.functions.elementary.trigonometric import (acos, acot, acsc, asec, asin, atan, atan2,
+                                                      cos, cot, csc, sec, sin, sinc, tan)
 from sympy.functions.special.bessel import (besselj, jn)
 from sympy.functions.special.delta_functions import Heaviside
 from sympy.matrices.dense import Matrix
@@ -1285,11 +1286,10 @@ def test_leading_terms():
     assert sin(S.Half).as_leading_term(x) == sin(S.Half)
     assert cos(1/x).as_leading_term(x) == AccumBounds(-1, 1)
     assert cos(S.Half).as_leading_term(x) == cos(S.Half)
-
-    for func in [tan, cot]:
-        for a in (1/x, S.Half):
-            eq = func(a)
-            assert eq.as_leading_term(x) == eq
+    assert sec(1/x).as_leading_term(x) == AccumBounds(S.NegativeInfinity, S.Infinity)
+    assert csc(1/x).as_leading_term(x) == AccumBounds(S.NegativeInfinity, S.Infinity)
+    assert tan(1/x).as_leading_term(x) == AccumBounds(S.NegativeInfinity, S.Infinity)
+    assert cot(1/x).as_leading_term(x) == AccumBounds(S.NegativeInfinity, S.Infinity)
 
     # https://github.com/sympy/sympy/issues/21038
     f = sin(pi*(x + 4))/(3*x)
diff --git a/sympy/series/tests/test_limits.py b/sympy/series/tests/test_limits.py
index 22e99781d8..6cb5c65c09 100644
--- a/sympy/series/tests/test_limits.py
+++ b/sympy/series/tests/test_limits.py
@@ -12,7 +12,8 @@
 from sympy.functions.elementary.hyperbolic import (acoth, atanh, sinh)
 from sympy.functions.elementary.integers import (ceiling, floor, frac)
 from sympy.functions.elementary.miscellaneous import (cbrt, real_root, sqrt)
-from sympy.functions.elementary.trigonometric import (acos, acot, acsc, asec, asin, atan, cos, cot, sec, sin, tan)
+from sympy.functions.elementary.trigonometric import (acos, acot, acsc, asec, asin,
+                                                      atan, cos, cot, csc, sec, sin, tan)
 from sympy.functions.special.bessel import (besselj, besselk)
 from sympy.functions.special.error_functions import (Ei, erf, erfc, erfi, fresnelc, fresnels)
 from sympy.functions.special.gamma_functions import (digamma, gamma, uppergamma)
@@ -65,6 +66,10 @@ def test_basic1():
     assert limit(1/tan(x**3), x, (2*pi)**Rational(1, 3), dir="-") is -oo
     assert limit(1/cot(x)**3, x, (pi*Rational(3, 2)), dir="+") is -oo
     assert limit(1/cot(x)**3, x, (pi*Rational(3, 2)), dir="-") is oo
+    assert limit(tan(x), x, oo) == AccumBounds(S.NegativeInfinity, S.Infinity)
+    assert limit(cot(x), x, oo) == AccumBounds(S.NegativeInfinity, S.Infinity)
+    assert limit(sec(x), x, oo) == AccumBounds(S.NegativeInfinity, S.Infinity)
+    assert limit(csc(x), x, oo) == AccumBounds(S.NegativeInfinity, S.Infinity)
 
     # test bi-directional limits
     assert limit(sin(x)/x, x, 0, dir="+-") == 1
@@ -758,6 +763,16 @@ def test_issue_14514():
     assert limit((1/(log(x)**log(x)))**(1/x), x, oo) == 1
 
 
+def test_issues_14525():
+    assert limit(sin(x)**2 - cos(x) + tan(x)*csc(x), x, oo) == AccumBounds(S.NegativeInfinity, S.Infinity)
+    assert limit(sin(x)**2 - cos(x) + sin(x)*cot(x), x, oo) == AccumBounds(S.NegativeInfinity, S.Infinity)
+    assert limit(cot(x) - tan(x)**2, x, oo) == AccumBounds(S.NegativeInfinity, S.Infinity)
+    assert limit(cos(x) - tan(x)**2, x, oo) == AccumBounds(S.NegativeInfinity, S.One)
+    assert limit(sin(x) - tan(x)**2, x, oo) == AccumBounds(S.NegativeInfinity, S.One)
+    assert limit(cos(x)**2 - tan(x)**2, x, oo) == AccumBounds(S.NegativeInfinity, S.One)
+    assert limit(tan(x)**2 + sin(x)**2 - cos(x), x, oo) == AccumBounds(-S.One, S.Infinity)
+
+
 def test_issue_14574():
     assert limit(sqrt(x)*cos(x - x**2) / (x + 1), x, oo) == 0