Repository: sympy/sympy
Pull request: Fix two issues related to infinite Range objects
#### References to other Issues or PRs
Fixes #17858. Fixes #17857.

#### Brief description of what is fixed or changed
Check if both sides are infinite and print `{..., -1, 0, 1, ...}`.

Fix membership checking if both sides are infinite.

#### Release Notes

<!-- BEGIN RELEASE NOTES -->
NO ENTRY
<!-- END RELEASE NOTES -->


Problem statement:
Pretty print Range(-oo, oo) raises
```python
>>> from sympy import *
>>> init_printing()
>>> Range(-oo, oo)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/enojb/current/sympy/sympy/sympy/interactive/printing.py", line 30, in _displayhook
    print(stringify_func(arg, **settings))
  File "/Users/enojb/current/sympy/sympy/sympy/printing/pretty/pretty.py", line 2606, in pretty
    return pp.doprint(expr)
  File "/Users/enojb/current/sympy/sympy/sympy/printing/pretty/pretty.py", line 64, in doprint
    return self._print(expr).render(**self._settings)
  File "/Users/enojb/current/sympy/sympy/sympy/printing/printer.py", line 287, in _print
    return getattr(self, printmethod)(expr, **kwargs)
  File "/Users/enojb/current/sympy/sympy/sympy/printing/pretty/pretty.py", line 1918, in _print_Range
    printset = dots, s[-1] - s.step, s[-1]
  File "/Users/enojb/current/sympy/sympy/sympy/sets/fancysets.py", line 842, in __getitem__
    raise ValueError(ooslice)
ValueError: cannot slice from the end with an infinite value
```

Range(-oo, oo) doesn't allow any membership checking
```
>>> from sympy import *
>>> 1 in Range(-oo, oo)
False
>>> 1 in Range(-oo, oo, 1)
False
```

Hints from issue discussion:
What is a good thing to print here? `{…}` looks confusing.
We have
```julia
In [1]: Range(-oo, 0)                                                                                                                                         
Out[1]: {…, -2, -1}

In [2]: Range(oo)                                                                                                                                             
Out[2]: {0, 1, …}
```
so I would go with
```julia
In [2]: Range(-oo, oo)                                                                                                                                             
Out[2]: {…, -1, 0, 1, …}
```
Comes down to `if oo % 1:` which returns `nan` which is a truthy value.

Commit message: test_issue_17858

Patch to review:
diff --git a/sympy/printing/pretty/pretty.py b/sympy/printing/pretty/pretty.py
index 92f1f7f36a07..0280f2e46c23 100644
--- a/sympy/printing/pretty/pretty.py
+++ b/sympy/printing/pretty/pretty.py
@@ -1914,7 +1914,12 @@ def _print_Range(self, s):
         else:
             dots = '...'
 
-        if s.start.is_infinite:
+        if s.start.is_infinite and s.stop.is_infinite:
+            if s.step.is_positive:
+                printset = dots, -1, 0, 1, dots
+            else:
+                printset = dots, 1, 0, -1, dots
+        elif s.start.is_infinite:
             printset = dots, s[-1] - s.step, s[-1]
         elif s.stop.is_infinite:
             it = iter(s)
diff --git a/sympy/printing/pretty/tests/test_pretty.py b/sympy/printing/pretty/tests/test_pretty.py
index 639914a26602..ad489503ec6c 100644
--- a/sympy/printing/pretty/tests/test_pretty.py
+++ b/sympy/printing/pretty/tests/test_pretty.py
@@ -6928,3 +6928,8 @@ def test_is_combining():
     line = u("v̇_m")
     assert [is_combining(sym) for sym in line] == \
         [False, True, False, False]
+
+
+def test_issue_17857():
+    assert pretty(Range(-oo, oo)) == '{..., -1, 0, 1, ...}'
+    assert pretty(Range(oo, -oo, -1)) == '{..., 1, 0, -1, ...}'
diff --git a/sympy/sets/fancysets.py b/sympy/sets/fancysets.py
index 5a1cf8dc348f..61c92d3441c2 100644
--- a/sympy/sets/fancysets.py
+++ b/sympy/sets/fancysets.py
@@ -659,8 +659,11 @@ def _contains(self, other):
                 _ = self.size  # validate
             except ValueError:
                 return
-        ref = self.start if self.start.is_finite else self.stop
-        if (ref - other) % self.step:  # off sequence
+        if self.start.is_finite:
+            ref = self.start
+        else:
+            ref = self.stop
+        if (ref - other) % self.step is S.true:  # off sequence
             return S.false
         return _sympify(other >= self.inf and other <= self.sup)
 
diff --git a/sympy/sets/tests/test_fancysets.py b/sympy/sets/tests/test_fancysets.py
index c278a3c3002b..2891eebb6ad6 100644
--- a/sympy/sets/tests/test_fancysets.py
+++ b/sympy/sets/tests/test_fancysets.py
@@ -945,3 +945,10 @@ def test_imageset_intersection():
         log(Abs(sqrt(-I))))), S.Integers)
     assert s.intersect(S.Reals) == ImageSet(
         Lambda(n, 2*pi*n + pi*Rational(7, 4)), S.Integers)
+
+
+def test_issue_17858():
+    assert 1 in Range(-oo, oo)
+    assert 0 in Range(oo, -oo, -1)
+    assert oo not in Range(-oo, oo)
+    assert -oo not in Range(-oo, oo)


Code review comments:
diff_hunk: @@ -659,8 +659,11 @@ def _contains(self, other):
                 _ = self.size  # validate
             except ValueError:
                 return
-        ref = self.start if self.start.is_finite else self.stop
-        if (ref - other) % self.step:  # off sequence
+        if self.start.is_finite:
+            ref = self.start
+        else:
+            ref = self.stop
+        if (ref - other) % self.step is S.true:  # off sequence
path: sympy/sets/fancysets.py
text: @user1:
A mod with be a number or nan (or Mod if symbolic values are involved), not S.true.