Repository: sympy/sympy (Python)

Issue:
Limit gives TypeError from as_leading_term
```python
In [3]: a, x = symbols('a, x')

In [4]: sqrt((-a**2 + x**2)/(1 - x**2)).limit(a, 1, '-')
---------------------------------------------------------------------------
TypeError 
...
~/current/sympy/sympy/sympy/core/power.py in _eval_as_leading_term(self, x, logx, cdir)
   1732         else:
   1733             f = b.as_leading_term(x, logx=logx, cdir=cdir)
-> 1734             if (not e.is_integer and f.is_constant() and f.is_real
   1735                 and f.is_negative and im((b - f).dir(x, cdir)) < 0):
   1736                 return self.func(f, e) * exp(-2 * e * S.Pi * I)

~/current/sympy/sympy/sympy/core/relational.py in __bool__(self)
    396 
    397     def __bool__(self):
--> 398         raise TypeError("cannot determine truth value of Relational")
    399 
    400     def _eval_as_set(self):

TypeError: cannot determine truth value of Relational
```
This is a regression on master. SymPy 1.8 gives:
```python
In [1]: a, x = symbols('a, x')

In [2]: sqrt((-a**2 + x**2)/(1 - x**2)).limit(a, 1, '-')
Out[2]: ⅈ
```
The bug was introduced in bf1eb95c20d7b74e5bd30bfe03b17745f09faf23 from #21589 and is caused by using `<` rather than `is_negative`.

CC @0sidharth @jksuom 


Discussion:
The offending test was not introduced in https://github.com/sympy/sympy/commit/bf1eb95c20d7b74e5bd30bfe03b17745f09faf23 but it was revealed by a change in code path.



Patch:
diff --git a/sympy/core/mul.py b/sympy/core/mul.py
index 1a853489eacd..bc1575846d93 100644
--- a/sympy/core/mul.py
+++ b/sympy/core/mul.py
@@ -1881,10 +1881,10 @@ def coeff_exp(term, x):
                 else:
                     raise ValueError
 
-            n0 = sum(t[1] for t in ords)
+            n0 = sum(t[1] for t in ords if t[1].is_number)
             facs = []
             for t, m in ords:
-                n1 = ceiling(n - n0 + m)
+                n1 = ceiling(n - n0 + (m if m.is_number else 0))
                 s = t.nseries(x, n=n1, logx=logx, cdir=cdir)
                 ns = s.getn()
                 if ns is not None:
diff --git a/sympy/core/power.py b/sympy/core/power.py
index 0447d0763843..5b4ac607d889 100644
--- a/sympy/core/power.py
+++ b/sympy/core/power.py
@@ -1629,6 +1629,8 @@ def _eval_nseries(self, x, n, logx, cdir=0):
 
         f = b.as_leading_term(x, logx=logx)
         g = (b/f - S.One).cancel(expand=False)
+        if not m.is_number:
+            raise NotImplementedError()
         maxpow = n - m*e
 
         if maxpow.is_negative:
@@ -1695,7 +1697,7 @@ def mul(d1, d2):
             k += S.One
 
         if (not e.is_integer and m.is_zero and f.is_real
-            and f.is_negative and im((b - f).dir(x, cdir)) < 0):
+            and f.is_negative and im((b - f).dir(x, cdir)).is_negative):
             inco, inex = coeff_exp(f**e*exp(-2*e*S.Pi*I), x)
         else:
             inco, inex = coeff_exp(f**e, x)
@@ -1732,7 +1734,7 @@ def _eval_as_leading_term(self, x, logx=None, cdir=0):
         else:
             f = b.as_leading_term(x, logx=logx, cdir=cdir)
             if (not e.is_integer and f.is_constant() and f.is_real
-                and f.is_negative and im((b - f).dir(x, cdir)) < 0):
+                and f.is_negative and im((b - f).dir(x, cdir)).is_negative):
                 return self.func(f, e) * exp(-2 * e * S.Pi * I)
             return self.func(f, e)
 
diff --git a/sympy/functions/elementary/exponential.py b/sympy/functions/elementary/exponential.py
index f7fb669f111e..cf029f7be67a 100644
--- a/sympy/functions/elementary/exponential.py
+++ b/sympy/functions/elementary/exponential.py
@@ -1029,19 +1029,20 @@ def mul(d1, d2):
         return res + Order(x**n, x)
 
     def _eval_as_leading_term(self, x, logx=None, cdir=0):
-        from sympy import I, im
+        from sympy import I, im, re
         arg0 = self.args[0].together()
+
         arg = arg0.as_leading_term(x, cdir=cdir)
         x0 = arg0.subs(x, 0)
         if (x0 is S.NaN and logx is None):
-            x0 = arg.limit(x, 0, dir='-' if cdir < 0 else '+')
+            x0 = arg.limit(x, 0, dir='-' if re(cdir).is_negative else '+')
         if x0 in (S.NegativeInfinity, S.Infinity):
             raise PoleError("Cannot expand %s around 0" % (self))
         if x0 == 1:
             return (arg0 - S.One).as_leading_term(x)
         if cdir != 0:
             cdir = arg0.dir(x, cdir)
-        if x0.is_real and x0.is_negative and im(cdir) < 0:
+        if x0.is_real and x0.is_negative and im(cdir).is_negative:
             return self.func(x0) - 2*I*S.Pi
         return self.func(arg)
 
diff --git a/sympy/functions/elementary/trigonometric.py b/sympy/functions/elementary/trigonometric.py
index 89d64454ba50..b1b715488cd2 100644
--- a/sympy/functions/elementary/trigonometric.py
+++ b/sympy/functions/elementary/trigonometric.py
@@ -478,6 +478,7 @@ def _eval_expand_trig(self, **hints):
         return sin(arg)
 
     def _eval_as_leading_term(self, x, logx=None, cdir=0):
+        from sympy import re
         from sympy.calculus.util import AccumBounds
         arg = self.args[0]
         x0 = arg.subs(x, 0).cancel()
@@ -486,7 +487,7 @@ def _eval_as_leading_term(self, x, logx=None, cdir=0):
             lt = (arg - n*S.Pi).as_leading_term(x)
             return ((-1)**n)*lt
         if x0 is S.ComplexInfinity:
-            x0 = arg.limit(x, 0, dir='-' if cdir < 0 else '+')
+            x0 = arg.limit(x, 0, dir='-' if re(cdir).is_negative else '+')
         if x0 in [S.Infinity, S.NegativeInfinity]:
             return AccumBounds(-1, 1)
         return self.func(x0) if x0.is_finite else self
@@ -934,6 +935,7 @@ def _eval_expand_trig(self, **hints):
         return cos(arg)
 
     def _eval_as_leading_term(self, x, logx=None, cdir=0):
+        from sympy import re
         from sympy.calculus.util import AccumBounds
         arg = self.args[0]
         x0 = arg.subs(x, 0).cancel()
@@ -942,7 +944,7 @@ def _eval_as_leading_term(self, x, logx=None, cdir=0):
             lt = (arg - n*S.Pi + S.Pi/2).as_leading_term(x)
             return ((-1)**n)*lt
         if x0 is S.ComplexInfinity:
-            x0 = arg.limit(x, 0, dir='-' if cdir < 0 else '+')
+            x0 = arg.limit(x, 0, dir='-' if re(cdir).is_negative else '+')
         if x0 in [S.Infinity, S.NegativeInfinity]:
             return AccumBounds(-1, 1)
         return self.func(x0) if x0.is_finite else self
diff --git a/sympy/functions/special/error_functions.py b/sympy/functions/special/error_functions.py
index 21e18f5a610f..ac45ff162657 100644
--- a/sympy/functions/special/error_functions.py
+++ b/sympy/functions/special/error_functions.py
@@ -5,6 +5,7 @@
 from sympy.core.function import Function, ArgumentIndexError
 from sympy.core.symbol import Symbol
 from sympy.functions.combinatorial.factorials import factorial, factorial2, RisingFactorial
+from sympy.functions.elementary.complexes import re
 from sympy.functions.elementary.integers import floor
 from sympy.functions.elementary.miscellaneous import sqrt, root
 from sympy.functions.elementary.exponential import exp, log
@@ -2024,7 +2025,7 @@ def _eval_as_leading_term(self, x, logx=None, cdir=0):
         arg0 = arg.subs(x, 0)
 
         if arg0 is S.NaN:
-            arg0 = arg.limit(x, 0, dir='-' if cdir < 0 else '+')
+            arg0 = arg.limit(x, 0, dir='-' if re(cdir).is_negative else '+')
         if arg0.is_zero:
             return S.EulerGamma
         elif arg0.is_finite:
@@ -2151,7 +2152,7 @@ def _eval_as_leading_term(self, x, logx=None, cdir=0):
         arg0 = arg.subs(x, 0)
 
         if arg0 is S.NaN:
-            arg0 = arg.limit(x, 0, dir='-' if cdir < 0 else '+')
+            arg0 = arg.limit(x, 0, dir='-' if re(cdir).is_negative else '+')
         if arg0.is_zero:
             return arg
         elif not arg0.is_infinite:
@@ -2269,7 +2270,7 @@ def _eval_as_leading_term(self, x, logx=None, cdir=0):
         arg0 = arg.subs(x, 0)
 
         if arg0 is S.NaN:
-            arg0 = arg.limit(x, 0, dir='-' if cdir < 0 else '+')
+            arg0 = arg.limit(x, 0, dir='-' if re(cdir).is_negative else '+')
         if arg0.is_zero:
             return S.EulerGamma
         elif arg0.is_finite:
@@ -2461,7 +2462,7 @@ def _eval_as_leading_term(self, x, logx=None, cdir=0):
         arg0 = arg.subs(x, 0)
 
         if arg0 is S.ComplexInfinity:
-            arg0 = arg.limit(x, 0, dir='-' if cdir < 0 else '+')
+            arg0 = arg.limit(x, 0, dir='-' if re(cdir).is_negative else '+')
         if arg0.is_zero:
             return pi*arg**3/6
         elif arg0 in [S.Infinity, S.NegativeInfinity]:
@@ -2617,7 +2618,7 @@ def _eval_as_leading_term(self, x, logx=None, cdir=0):
         arg0 = arg.subs(x, 0)
 
         if arg0 is S.ComplexInfinity:
-            arg0 = arg.limit(x, 0, dir='-' if cdir < 0 else '+')
+            arg0 = arg.limit(x, 0, dir='-' if re(cdir).is_negative else '+')
         if arg0.is_zero:
             return arg
         elif arg0 in [S.Infinity, S.NegativeInfinity]:
diff --git a/sympy/functions/special/hyper.py b/sympy/functions/special/hyper.py
index 5c872ed64e16..38a3dee08b2e 100644
--- a/sympy/functions/special/hyper.py
+++ b/sympy/functions/special/hyper.py
@@ -9,7 +9,7 @@
 from sympy.core.symbol import Dummy
 
 from sympy.functions import (sqrt, exp, log, sin, cos, asin, atan,
-        sinh, cosh, asinh, acosh, atanh, acoth, Abs)
+        sinh, cosh, asinh, acosh, atanh, acoth, Abs, re)
 from sympy.utilities.iterables import default_sort_key
 
 class TupleArg(Tuple):
@@ -223,7 +223,7 @@ def _eval_as_leading_term(self, x, logx=None, cdir=0):
         arg = self.args[2]
         x0 = arg.subs(x, 0)
         if x0 is S.NaN:
-            x0 = arg.limit(x, 0, dir='-' if cdir < 0 else '+')
+            x0 = arg.limit(x, 0, dir='-' if re(cdir).is_negative else '+')
 
         if x0 is S.Zero:
             return S.One
diff --git a/sympy/functions/special/zeta_functions.py b/sympy/functions/special/zeta_functions.py
index b02947a060bf..222fdbd73293 100644
--- a/sympy/functions/special/zeta_functions.py
+++ b/sympy/functions/special/zeta_functions.py
@@ -4,6 +4,7 @@
 from sympy.core import Function, S, sympify, pi, I
 from sympy.core.function import ArgumentIndexError
 from sympy.functions.combinatorial.numbers import bernoulli, factorial, harmonic
+from sympy.functions.elementary.complexes import re
 from sympy.functions.elementary.exponential import log, exp_polar
 from sympy.functions.elementary.miscellaneous import sqrt
 
@@ -359,7 +360,7 @@ def _eval_nseries(self, x, n, logx, cdir=0):
 
         z0 = z.subs(x, 0)
         if z0 is S.NaN:
-            z0 = z.limit(x, 0, dir='-' if cdir < 0 else '+')
+            z0 = z.limit(x, 0, dir='-' if re(cdir).is_negative else '+')
 
         if z0.is_zero:
             # In case of powers less than 1, number of terms need to be computed


Test patch:
diff --git a/sympy/series/tests/test_limits.py b/sympy/series/tests/test_limits.py
index 84f78bbf37c3..eb1b4fbf5464 100644
--- a/sympy/series/tests/test_limits.py
+++ b/sympy/series/tests/test_limits.py
@@ -212,8 +212,29 @@ def test_set_signs():
     assert limit(abs(cos(x)), x, 0) == 1
     assert limit(abs(sin(x + 1)), x, 0) == sin(1)
 
+    # https://github.com/sympy/sympy/issues/9449
+    assert limit((Abs(x + y) - Abs(x - y))/(2*x), x, 0) == sign(y)
+
+    # https://github.com/sympy/sympy/issues/12398
+    assert limit(Abs(log(x)/x**3), x, oo) == 0
+    assert limit(x*(Abs(log(x)/x**3)/Abs(log(x + 1)/(x + 1)**3) - 1), x, oo) == 3
+
+    # https://github.com/sympy/sympy/issues/18501
+    assert limit(Abs(log(x - 1)**3 - 1), x, 1, '+') == oo
+
+    # https://github.com/sympy/sympy/issues/18997
+    assert limit(Abs(log(x)), x, 0) == oo
+    assert limit(Abs(log(Abs(x))), x, 0) == oo
+
+    # https://github.com/sympy/sympy/issues/19026
+    z = Symbol('z', positive=True)
+    assert limit(Abs(log(z) + 1)/log(z), z, oo) == 1
+
+    # https://github.com/sympy/sympy/issues/20704
+    assert limit(z*(Abs(1/z + y) - Abs(y - 1/z))/2, z, 0) == 0
+
     # https://github.com/sympy/sympy/issues/21606
-    assert limit(cos(x)/sign(x), x, pi, '-') == -1
+    assert limit(cos(z)/sign(z), z, pi, '-') == -1
 
 
 def test_heuristic():
@@ -581,10 +602,6 @@ def test_issue_9252():
     raises(NotImplementedError, lambda: limit((log(n))**(n/log(n)) / c**n, n, oo))
 
 
-def test_issue_9449():
-    assert limit((Abs(x + y) - Abs(x - y))/(2*x), x, 0) == sign(y)
-
-
 def test_issue_9558():
     assert limit(sin(x)**15, x, 0, '-') == 0
 
@@ -638,11 +655,6 @@ def test_issue_6599():
     assert limit((n + cos(n))/n, n, oo) == 1
 
 
-def test_issue_12398():
-    assert limit(Abs(log(x)/x**3), x, oo) == 0
-    assert limit(x*(Abs(log(x)/x**3)/Abs(log(x + 1)/(x + 1)**3) - 1), x, oo) == 3
-
-
 def test_issue_12555():
     assert limit((3**x + 2* x**10) / (x**10 + exp(x)), x, -oo) == 2
     assert limit((3**x + 2* x**10) / (x**10 + exp(x)), x, oo) is oo
@@ -869,10 +881,6 @@ def test_issue_18482():
     assert limit((2*exp(3*x)/(exp(2*x) + 1))**(1/x), x, oo) == exp(1)
 
 
-def test_issue_18501():
-    assert limit(Abs(log(x - 1)**3 - 1), x, 1, '+') == oo
-
-
 def test_issue_18508():
     assert limit(sin(x)/sqrt(1-cos(x)), x, 0) == sqrt(2)
     assert limit(sin(x)/sqrt(1-cos(x)), x, 0, dir='+') == sqrt(2)
@@ -889,16 +897,6 @@ def test_issue_18992():
     assert limit(n/(factorial(n)**(1/n)), n, oo) == exp(1)
 
 
-def test_issue_18997():
-    assert limit(Abs(log(x)), x, 0) == oo
-    assert limit(Abs(log(Abs(x))), x, 0) == oo
-
-
-def test_issue_19026():
-    x = Symbol('x', positive=True)
-    assert limit(Abs(log(x) + 1)/log(x), x, oo) == 1
-
-
 def test_issue_19067():
     x = Symbol('x')
     assert limit(gamma(x)/(gamma(x - 1)*gamma(x + 2)), x, 0) == -1
@@ -959,10 +957,6 @@ def test_issue_20365():
     assert limit(((x + 1)**(1/x) - E)/x, x, 0) == -E/2
 
 
-def test_issue_20704():
-    assert limit(x*(Abs(1/x + y) - Abs(y - 1/x))/2, x, 0) == 0
-
-
 def test_issue_21031():
     assert limit(((1 + x)**(1/x) - (1 + 2*x)**(1/(2*x)))/asin(x), x, 0) == E/2
 
@@ -978,6 +972,12 @@ def test_issue_20578():
     assert limit(expr,x,0,'+-') == 0
 
 
+def test_issue_21415():
+    exp = (x-1)*cos(1/(x-1))
+    assert exp.limit(x,1) == 0
+    assert exp.expand().limit(x,1) == 0
+
+
 def test_issue_21530():
     assert limit(sinh(n + 1)/sinh(n), n, oo) == E
 
@@ -1008,7 +1008,6 @@ def test_issue_21756():
     assert re(term).limit(z, 0) == 5
 
 
-def test_issue_21415():
-    exp = (x-1)*cos(1/(x-1))
-    assert exp.limit(x,1) == 0
-    assert exp.expand().limit(x,1) == 0
+def test_issue_21785():
+    a = Symbol('a')
+    assert sqrt((-a**2 + x**2)/(1 - x**2)).limit(a, 1, '-') == I