Repository: sympy/sympy

Issue:
Sums with infinite lower limit don't pretty-print correctly.
We have nice pretty printing for Sum:
```julia
In [27]: n = Symbol('n')                                                                                                          

In [28]: Sum(n, (n, -oo, 1))                                                                                                      
Out[28]: 
  1     
 ___    
 ╲      
  ╲     
  ╱    n
 ╱      
 ‾‾‾    
n = -∞  
```
However if the dummy symbol is finite and the lower limit infinite then
```julia
In [29]: n = Symbol('n', integer=True)                                                                                            

In [30]: Sum(n, (n, -oo, 1))                                                                                                      
Out[30]: 
  1    
 ___   
 ╲     
  ╲    
  ╱   n
 ╱     
 ‾‾‾   
False
```
I believe this happens because:
```julia
In [34]: Eq(n, -oo)                                                                                                               
Out[34]: False

In [35]: Eq(n, -oo, evaluate=False)                                                                                               
Out[35]: n = -∞
```
So evaluate=False should probably be used for this somewhere in the printing code.

Discussion:
sympy-bot: :white_check_mark:

Hi, I am the [SymPy bot](https://github.com/sympy/sympy-bot) (v147). I'm here to help you write a release notes entry. Please read the [guide on how to write release notes](https://github.com/sympy/sympy/wiki/Writing-Release-Notes).



Your release notes are in good order.

Here is what the release notes will look like:
* printing
  * Pretty printing of `Sum` and `Product` no longer depends on variable assumptions. ([#17386](https://github.com/sympy/sympy/pull/17386) by [@oscargus](https://github.com/oscargus))

This will be added to https://github.com/sympy/sympy/wiki/Release-Notes-for-1.5.

Note: This comment will be updated with the latest check if you edit the pull request. You need to reload the page to see it. <details><summary>Click here to see the pull request description that was parsed.</summary>

    <!-- Your title above should be a short description of what
    was changed. Do not include the issue number in the title. -->

    #### References to other Issues or PRs
    <!-- If this pull request fixes an issue, write "Fixes #NNNN" in that exact
    format, e.g. "Fixes #1234". See
    https://github.com/blog/1506-closing-issues-via-pull-requests . Please also
    write a comment on that issue linking back to this pull request once it is
    open. -->
    Fixes #17258 

    #### Brief description of what is fixed or changed
    Do not rely on `Eq` when printing summation limits.


    #### Other comments
    It seem like all Sum and Product must have explicit limits. Still there are some tests in the code (apart from the constructor) that they do. Is the idea that they should have explicit limits? Naturally, an expression without explicit limits may be hard to evaluate, but maybe there are cases where one would like to formulate one?


    #### Release Notes

    <!-- Write the release notes for this release below. See
    https://github.com/sympy/sympy/wiki/Writing-Release-Notes for more information
    on how to write release notes. The bot will check your release notes
    automatically to see if they are formatted correctly. -->

    <!-- BEGIN RELEASE NOTES -->
    * printing
       * Pretty printing of `Sum` and `Product` no longer depends on variable assumptions.
    <!-- END RELEASE NOTES -->


</details><p>

oscarbenjamin: LGTM

Patch:
diff --git a/sympy/calculus/util.py b/sympy/calculus/util.py
index b289972478..50feb12d2d 100644
--- a/sympy/calculus/util.py
+++ b/sympy/calculus/util.py
@@ -10,7 +10,6 @@
 from sympy.polys.rationaltools import together
 from sympy.sets.sets import (Interval, Intersection, FiniteSet, Union,
                              Complement, EmptySet)
-from sympy.sets.fancysets import ImageSet
 from sympy.simplify.radsimp import denom
 from sympy.solvers.inequalities import solve_univariate_inequality
 from sympy.utilities import filldedent
@@ -212,11 +211,7 @@ def function_range(f, symbol, domain):
             solution = solveset(f.diff(symbol), symbol, interval)
 
             if not iterable(solution):
-                raise NotImplementedError(
-                        'Unable to find critical points for {}'.format(f))
-            if isinstance(solution, ImageSet):
-                raise NotImplementedError(
-                        'Infinite number of critical points for {}'.format(f))
+                raise NotImplementedError('Unable to find critical points for {}'.format(f))
 
             critical_points += solution
 
diff --git a/sympy/printing/pretty/pretty.py b/sympy/printing/pretty/pretty.py
index 930e4352d7..6ad73885e3 100644
--- a/sympy/printing/pretty/pretty.py
+++ b/sympy/printing/pretty/pretty.py
@@ -9,7 +9,6 @@
 from sympy.core.mul import Mul
 from sympy.core.numbers import Rational
 from sympy.core.power import Pow
-from sympy.core.relational import Equality
 from sympy.core.symbol import Symbol
 from sympy.core.sympify import SympifyError
 from sympy.printing.conventions import requires_partial
@@ -482,6 +481,8 @@ def _print_Product(self, expr):
         sign_height = 0
 
         for lim in expr.limits:
+            pretty_lower, pretty_upper = self.__print_SumProduct_Limits(lim)
+
             width = (func_height + 2) * 5 // 3 - 2
             sign_lines = [horizontal_chr + corner_chr + (horizontal_chr * (width-2)) + corner_chr + horizontal_chr]
             for _ in range(func_height + 1):
@@ -490,8 +491,6 @@ def _print_Product(self, expr):
             pretty_sign = stringPict('')
             pretty_sign = prettyForm(*pretty_sign.stack(*sign_lines))
 
-            pretty_upper = self._print(lim[2])
-            pretty_lower = self._print(Equality(lim[0], lim[1]))
 
             max_upper = max(max_upper, pretty_upper.height())
 
@@ -516,6 +515,25 @@ def _print_Product(self, expr):
         pretty_func.binding = prettyForm.MUL
         return pretty_func
 
+    def __print_SumProduct_Limits(self, lim):
+        def print_start(lhs, rhs):
+            op = prettyForm(' ' + xsym("==") + ' ')
+            l = self._print(lhs)
+            r = self._print(rhs)
+            pform = prettyForm(*stringPict.next(l, op, r))
+            return pform
+
+        if len(lim) == 3:
+            prettyUpper = self._print(lim[2])
+            prettyLower = print_start(lim[0], lim[1])
+        elif len(lim) == 2:
+            prettyUpper = self._print("")
+            prettyLower = print_start(lim[0], lim[1])
+        elif len(lim) == 1:
+            prettyUpper = self._print("")
+            prettyLower = self._print(lim[0])
+        return prettyLower, prettyUpper
+
     def _print_Sum(self, expr):
         ascii_mode = not self._use_unicode
 
@@ -576,15 +594,7 @@ def adjust(s, wid=None, how='<^>'):
         sign_height = 0
 
         for lim in expr.limits:
-            if len(lim) == 3:
-                prettyUpper = self._print(lim[2])
-                prettyLower = self._print(Equality(lim[0], lim[1]))
-            elif len(lim) == 2:
-                prettyUpper = self._print("")
-                prettyLower = self._print(Equality(lim[0], lim[1]))
-            elif len(lim) == 1:
-                prettyUpper = self._print("")
-                prettyLower = self._print(lim[0])
+            prettyLower, prettyUpper = self.__print_SumProduct_Limits(lim)
 
             max_upper = max(max_upper, prettyUpper.height())
 


Test patch:
diff --git a/sympy/calculus/tests/test_util.py b/sympy/calculus/tests/test_util.py
index bb65362653..b5a9aa20e1 100644
--- a/sympy/calculus/tests/test_util.py
+++ b/sympy/calculus/tests/test_util.py
@@ -44,8 +44,6 @@ def test_function_range():
     assert function_range(cos(x), x, S.EmptySet) == S.EmptySet
     raises(NotImplementedError, lambda : function_range(
         exp(x)*(sin(x) - cos(x))/2 - x, x, S.Reals))
-    raises(NotImplementedError, lambda : function_range(
-        sin(x) + x, x, S.Reals)) # issue 13273
     raises(NotImplementedError, lambda : function_range(
         log(x), x, S.Integers))
     raises(NotImplementedError, lambda : function_range(
diff --git a/sympy/printing/pretty/tests/test_pretty.py b/sympy/printing/pretty/tests/test_pretty.py
index 2173efc97c..19746b76ce 100644
--- a/sympy/printing/pretty/tests/test_pretty.py
+++ b/sympy/printing/pretty/tests/test_pretty.py
@@ -6795,3 +6795,26 @@ def test_pretty_misc_functions():
     assert upretty(Heaviside(x, y)) == u'θ(x, y)'
     assert pretty(dirichlet_eta(x)) == 'dirichlet_eta(x)'
     assert upretty(dirichlet_eta(x)) == u'η(x)'
+
+
+def test_issue_17258():
+    n = Symbol('n', integer=True)
+    assert pretty(Sum(n, (n, -oo, 1))) == \
+    '   1     \n'\
+    '  __     \n'\
+    '  \\ `    \n'\
+    '   )    n\n'\
+    '  /_,    \n'\
+    'n = -oo  '
+
+    assert upretty(Sum(n, (n, -oo, 1))) == \
+u("""\
+  1     \n\
+ ___    \n\
+ ╲      \n\
+  ╲     \n\
+  ╱    n\n\
+ ╱      \n\
+ ‾‾‾    \n\
+n = -∞  \
+""")