Repository: sympy/sympy

Issue:
Simplification of expandable geometric series
Inside `eval_sum_symbolic` function in `sympy/concrete/summations.py`, round [line 980](https://github.com/sympy/sympy/blob/84c125972ad535b2dfb245f8d311d347b45e5b8a/sympy/concrete/summations.py#L978), powerful simplification for geometric series exists.  However, if higher-level function is desired to be used, its invocation in `eval_sum` around [line 894](https://github.com/sympy/sympy/blob/84c125972ad535b2dfb245f8d311d347b45e5b8a/sympy/concrete/summations.py#L894) actually expands the summand.

In this way, when the summand is expandable, for instance, in the kinda inner product of two discrete Fourier waves,

```Py
from sympy import summation, I, pi, exp, symbols
k, q, x, n = symbols('k q x n', integer=True)

res0 = summation(
    exp(-2*I*pi*k*x/n), (x, 0, n - 1)
)
print(res0.simplify())

res1 = summation(
    (exp(-2*I*pi*k*x/n)*exp(2*I*pi*q*x/n)).simplify(), (x, 0, n - 1)
)
print(res1.simplify())
```
the `res1` gives unsimplified result, while `res0` has very satisfactory simplification.

I guess it could be improved by either removing the expansion before the invocation or attempt to put things together before the pattern matching for geometric series.  But I am not quite sure which approach is better.  Any pointers on this issue is deeply appreciated and I guess I can make a pull request for this with some guidance.

Discussion:

Patch:
diff --git a/sympy/concrete/summations.py b/sympy/concrete/summations.py
index 974ad067d1..2d6361e0a7 100644
--- a/sympy/concrete/summations.py
+++ b/sympy/concrete/summations.py
@@ -974,8 +974,16 @@ def eval_sum_symbolic(f, limits):
         c1 = Wild('c1', exclude=[i])
         c2 = Wild('c2', exclude=[i])
         c3 = Wild('c3', exclude=[i])
+        wexp = Wild('wexp')
 
-        e = f.match(c1**(c2*i + c3))
+        # Here we first attempt powsimp on f for easier matching with the
+        # exponential pattern, and attempt expansion on the exponent for easier
+        # matching with the linear pattern.
+        e = f.powsimp().match(c1 ** wexp)
+        if e is not None:
+            e_exp = e.pop(wexp).expand().match(c2*i + c3)
+            if e_exp is not None:
+                e.update(e_exp)
 
         if e is not None:
             p = (c1**c3).subs(e)


Test patch:
diff --git a/sympy/concrete/tests/test_sums_products.py b/sympy/concrete/tests/test_sums_products.py
index 98a6ca01cd..21534f0f1c 100644
--- a/sympy/concrete/tests/test_sums_products.py
+++ b/sympy/concrete/tests/test_sums_products.py
@@ -279,6 +279,16 @@ def test_geometric_sums():
     assert Sum(1.0**n, (n, 1, oo)).doit() == oo
     assert Sum(2.43**n, (n, 1, oo)).doit() == oo
 
+    # Issue 13979:
+    i, k, q = symbols('i k q', integer=True)
+    result = summation(
+        exp(-2*I*pi*k*i/n) * exp(2*I*pi*q*i/n) / n, (i, 0, n - 1)
+    )
+    assert result.simplify() == Piecewise(
+            (1, Eq(exp(2*I*pi*(-k + q)/n), 1)), (0, True)
+    )
+
+
 def test_harmonic_sums():
     assert summation(1/k, (k, 0, n)) == Sum(1/k, (k, 0, n))
     assert summation(1/k, (k, 1, n)) == harmonic(n)