Repository: sympy/sympy

Issue:
The limit of exp(I*x)*sin(pi*x) at infinity is reported to be 0
In current master: 
```
>>> var('x')
>>> limit(exp(I*x)*sin(pi*x), x, oo)
0
```

Discussion:

Patch:
diff --git a/sympy/concrete/summations.py b/sympy/concrete/summations.py
index 55dbba7de5..234811fc63 100644
--- a/sympy/concrete/summations.py
+++ b/sympy/concrete/summations.py
@@ -15,7 +15,7 @@
 from sympy.functions.elementary.piecewise import Piecewise
 from sympy.logic.boolalg import And
 from sympy.polys import apart, PolynomialError, together
-from sympy.series.limits import limit
+from sympy.series.limitseq import limit_seq
 from sympy.series.order import O
 from sympy.sets.sets import FiniteSet
 from sympy.simplify.combsimp import combsimp
@@ -424,15 +424,15 @@ def is_convergent(self):
 
         ###  -------- Divergence test ----------- ###
         try:
-            lim_val = limit(sequence_term, sym, upper_limit)
-            if lim_val.is_number and lim_val is not S.Zero:
+            lim_val = limit_seq(sequence_term, sym)
+            if lim_val is not None and lim_val.is_zero is False:
                 return S.false
         except NotImplementedError:
             pass
 
         try:
-            lim_val_abs = limit(abs(sequence_term), sym, upper_limit)
-            if lim_val_abs.is_number and lim_val_abs is not S.Zero:
+            lim_val_abs = limit_seq(abs(sequence_term), sym)
+            if lim_val_abs is not None and lim_val_abs.is_zero is False:
                 return S.false
         except NotImplementedError:
             pass
@@ -467,9 +467,9 @@ def is_convergent(self):
         ### ------------- Limit comparison test -----------###
         # (1/n) comparison
         try:
-            lim_comp = limit(sym*sequence_term, sym, S.Infinity)
-            if lim_comp.is_number and lim_comp > 0:
-                    return S.false
+            lim_comp = limit_seq(sym*sequence_term, sym)
+            if lim_comp is not None and lim_comp.is_number and lim_comp > 0:
+                return S.false
         except NotImplementedError:
             pass
 
@@ -477,8 +477,8 @@ def is_convergent(self):
         next_sequence_term = sequence_term.xreplace({sym: sym + 1})
         ratio = combsimp(powsimp(next_sequence_term/sequence_term))
         try:
-            lim_ratio = limit(ratio, sym, upper_limit)
-            if lim_ratio.is_number:
+            lim_ratio = limit_seq(ratio, sym)
+            if lim_ratio is not None and lim_ratio.is_number:
                 if abs(lim_ratio) > 1:
                     return S.false
                 if abs(lim_ratio) < 1:
@@ -487,10 +487,10 @@ def is_convergent(self):
             pass
 
         ### ----------- root test ---------------- ###
-        lim = Limit(abs(sequence_term)**(1/sym), sym, S.Infinity)
+        # lim = Limit(abs(sequence_term)**(1/sym), sym, S.Infinity)
         try:
-            lim_evaluated = lim.doit()
-            if lim_evaluated.is_number:
+            lim_evaluated = limit_seq(abs(sequence_term)**(1/sym), sym)
+            if lim_evaluated is not None and lim_evaluated.is_number:
                 if lim_evaluated < 1:
                     return S.true
                 if lim_evaluated > 1:
@@ -551,8 +551,8 @@ def is_convergent(self):
             m = Dummy('m', integer=True)
             def _dirichlet_test(g_n):
                 try:
-                    ing_val = limit(Sum(g_n, (sym, interval.inf, m)).doit(), m, S.Infinity)
-                    if ing_val.is_finite:
+                    ing_val = limit_seq(Sum(g_n, (sym, interval.inf, m)).doit(), m)
+                    if ing_val is not None and ing_val.is_finite:
                         return S.true
                 except NotImplementedError:
                     pass
@@ -560,11 +560,12 @@ def _dirichlet_test(g_n):
             ### -------- bounded times convergent test ---------###
             def _bounded_convergent_test(g1_n, g2_n):
                 try:
-                    lim_val = limit(g1_n, sym, upper_limit)
-                    if lim_val.is_finite or (isinstance(lim_val, AccumulationBounds)
-                                             and (lim_val.max - lim_val.min).is_finite):
-                        if Sum(g2_n, (sym, lower_limit, upper_limit)).is_absolutely_convergent():
-                            return S.true
+                    lim_val = limit_seq(g1_n, sym)
+                    if lim_val is not None and (lim_val.is_finite or (
+                        isinstance(lim_val, AccumulationBounds)
+                        and (lim_val.max - lim_val.min).is_finite)):
+                            if Sum(g2_n, (sym, lower_limit, upper_limit)).is_absolutely_convergent():
+                                return S.true
                 except NotImplementedError:
                     pass
 
diff --git a/sympy/series/limits.py b/sympy/series/limits.py
index de5731ce14..e0114e53f7 100644
--- a/sympy/series/limits.py
+++ b/sympy/series/limits.py
@@ -15,17 +15,26 @@
 from sympy.simplify.simplify import together
 
 def limit(e, z, z0, dir="+"):
-    """
-    Compute the limit of ``e(z)`` at the point ``z0``.
+    """Computes the limit of ``e(z)`` at the point ``z0``.
+
+    Parameters
+    ==========
+
+    e : expression, the limit of which is to be taken
+
+    z : symbol representing the variable in the limit.
+        Other symbols are treated as constants. Multivariate limits
+        are not supported.
 
-    ``z0`` can be any expression, including ``oo`` and ``-oo``.
+    z0 : the value toward which ``z`` tends. Can be any expression,
+        including ``oo`` and ``-oo``.
 
-    For ``dir="+-"`` it calculates the bi-directional limit; for
-    ``dir="+"`` (default) it calculates the limit from the right
-    (z->z0+) and for dir="-" the limit from the left (z->z0-).
-    For infinite ``z0`` (``oo`` or ``-oo``), the ``dir`` argument is
-    determined from the direction of the infinity (i.e.,
-    ``dir="-"`` for ``oo``).
+    dir : string, optional (default: "+")
+        The limit is bi-directional if ``dir="+-"``, from the right
+        (z->z0+) if ``dir="+"``, and from the left (z->z0-) if
+        ``dir="-"``. For infinite ``z0`` (``oo`` or ``-oo``), the ``dir``
+        argument is determined from the direction of the infinity
+        (i.e., ``dir="-"`` for ``oo``).
 
     Examples
     ========
@@ -52,6 +61,11 @@ def limit(e, z, z0, dir="+"):
     First we try some heuristics for easy and frequent cases like "x", "1/x",
     "x**2" and similar, so that it's fast. For all other cases, we use the
     Gruntz algorithm (see the gruntz() function).
+
+    See Also
+    ========
+
+     limit_seq : returns the limit of a sequence.
     """
 
     if dir == "+-":
@@ -69,6 +83,13 @@ def limit(e, z, z0, dir="+"):
 
 
 def heuristics(e, z, z0, dir):
+    """Computes the limit of an expression term-wise.
+    Parameters are the same as for the ``limit`` function.
+    Works with the arguments of expression ``e`` one by one, computing
+    the limit of each and then combining the results. This approach
+    works only for simple limits, but it is fast.
+    """
+
     from sympy.calculus.util import AccumBounds
     rv = None
     if abs(z0) is S.Infinity:
@@ -172,7 +193,18 @@ def free_symbols(self):
 
 
     def doit(self, **hints):
-        """Evaluates limit"""
+        """Evaluates the limit.
+
+        Parameters
+        ==========
+
+        deep : bool, optional (default: True)
+            Invoke the ``doit`` method of the expressions involved before
+            taking the limit.
+
+        hints : optional keyword arguments
+            To be passed to ``doit`` methods; only used if deep is True.
+        """
         from sympy.series.limitseq import limit_seq
         from sympy.functions import RisingFactorial
 
@@ -232,14 +264,5 @@ def doit(self, **hints):
             r = heuristics(e, z, z0, dir)
             if r is None:
                 return self
-        except NotImplementedError:
-            # Trying finding limits of sequences
-            if hints.get('sequence', True) and z0 is S.Infinity:
-                trials = hints.get('trials', 5)
-                r = limit_seq(e, z, trials)
-                if r is None:
-                    raise NotImplementedError()
-            else:
-                raise NotImplementedError()
 
         return r
diff --git a/sympy/series/limitseq.py b/sympy/series/limitseq.py
index 6a23326972..a826fdefc6 100644
--- a/sympy/series/limitseq.py
+++ b/sympy/series/limitseq.py
@@ -9,6 +9,7 @@
 from sympy.core.symbol import Dummy
 from sympy.core.function import PoleError
 from sympy.series.limits import Limit
+from sympy.functions.combinatorial.numbers import fibonacci
 
 
 def difference_delta(expr, n=None, step=1):
@@ -102,7 +103,7 @@ def dominant(expr, n):
 
 def _limit_inf(expr, n):
     try:
-        return Limit(expr, n, S.Infinity).doit(deep=False, sequence=False)
+        return Limit(expr, n, S.Infinity).doit(deep=False)
     except (NotImplementedError, PoleError):
         return None
 
@@ -145,15 +146,17 @@ def _limit_seq(expr, n, trials):
 
 
 def limit_seq(expr, n=None, trials=5):
-    """Finds limits of terms having sequences at infinity.
+    """Finds the limit of a sequence as index n tends to infinity.
 
     Parameters
     ==========
 
     expr : Expr
         SymPy expression for the n-th term of the sequence
-    n : Symbol
-        The index of the sequence, an integer that tends to positive infinity.
+    n : Symbol, optional
+        The index of the sequence, an integer that tends to positive
+        infinity. If None, inferred from the expression unless it has
+        multiple symbols.
     trials: int, optional
         The algorithm is highly recursive. ``trials`` is a safeguard from
         infinite recursion in case the limit is not easily computed by the
@@ -202,6 +205,7 @@ def limit_seq(expr, n=None, trials=5):
     elif n not in expr.free_symbols:
         return expr
 
+    expr = expr.rewrite(fibonacci, S.GoldenRatio)
     n_ = Dummy("n", integer=True, positive=True)
 
     # If there is a negative term raised to a power involving n, consider


Test patch:
diff --git a/sympy/series/tests/test_limits.py b/sympy/series/tests/test_limits.py
index 434296a06d..d0b463ce40 100644
--- a/sympy/series/tests/test_limits.py
+++ b/sympy/series/tests/test_limits.py
@@ -114,10 +114,6 @@ def eval(cls, arg):
 def test_issue_3885():
     assert limit(x*y + x*z, z, 2) == x*y + 2*x
 
-def test_issue_10382():
-    n = Symbol('n', integer=True)
-    assert limit(fibonacci(n+1)/fibonacci(n), n, oo) == S.GoldenRatio
-
 
 def test_Limit():
     assert Limit(sin(x)/x, x, 0) != 1
@@ -484,15 +480,6 @@ def test_issue_9205():
     assert Limit(-x**2 + y, x**2, a).free_symbols == {y, a}
 
 
-def test_limit_seq():
-    assert limit(Sum(1/x, (x, 1, y)) - log(y), y, oo) == EulerGamma
-    assert limit(Sum(1/x, (x, 1, y)) - 1/y, y, oo) == S.Infinity
-    assert (limit(binomial(2*x, x) / Sum(binomial(2*y, y), (y, 1, x)), x, oo) ==
-            S(3) / 4)
-    assert (limit(Sum(y**2 * Sum(2**z/z, (z, 1, y)), (y, 1, x)) /
-                  (2**x*x), x, oo) == 4)
-
-
 def test_issue_11879():
     assert simplify(limit(((x+y)**n-x**n)/y, y, 0)) == n*x**(n-1)
 
@@ -545,3 +532,7 @@ def test_issue_10102():
     assert limit(fresnelc(x), x, oo) == S.Half
     assert limit(fresnels(x), x, -oo) == -S.Half
     assert limit(4*fresnelc(x), x, -oo) == -2
+
+
+def test_issue_14377():
+    raises(NotImplementedError, lambda: limit(exp(I*x)*sin(pi*x), x, oo))
diff --git a/sympy/series/tests/test_limitseq.py b/sympy/series/tests/test_limitseq.py
index 554e68d750..c480a581ba 100644
--- a/sympy/series/tests/test_limitseq.py
+++ b/sympy/series/tests/test_limitseq.py
@@ -1,4 +1,5 @@
-from sympy import symbols, oo, Sum, harmonic, Add, S, binomial, factorial
+from sympy import (symbols, Symbol, oo, Sum, harmonic, Add, S, binomial,
+    factorial, log, fibonacci)
 from sympy.series.limitseq import limit_seq
 from sympy.series.limitseq import difference_delta as dd
 from sympy.utilities.pytest import raises, XFAIL
@@ -89,6 +90,21 @@ def test_alternating_sign():
     assert limit_seq((-3)**n/(n + 3**n), n) is None
 
 
+def test_limitseq_sum():
+    from sympy.abc import x, y, z
+    assert limit_seq(Sum(1/x, (x, 1, y)) - log(y), y) == S.EulerGamma
+    assert limit_seq(Sum(1/x, (x, 1, y)) - 1/y, y) == S.Infinity
+    assert (limit_seq(binomial(2*x, x) / Sum(binomial(2*y, y), (y, 1, x)), x) ==
+            S(3) / 4)
+    assert (limit_seq(Sum(y**2 * Sum(2**z/z, (z, 1, y)), (y, 1, x)) /
+                  (2**x*x), x) == 4)
+
+
+def test_issue_10382():
+    n = Symbol('n', integer=True)
+    assert limit_seq(fibonacci(n+1)/fibonacci(n), n) == S.GoldenRatio
+
+
 @XFAIL
 def test_limit_seq_fail():
     # improve Summation algorithm or add ad-hoc criteria