Repository: sympy/sympy
Pull request: corrections for symbolic Range
<!-- 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

fixes #21269 

#### Brief description of what is fixed or changed


#### Other comments

* to return or to raise

Symbolic Range is tricky because you can't assume that it isn't EmptSet when making queries like `.inf`, e.g. neither `Range(x,y,z).inf` nor Range(x,y,z)[0] can return `x` unless we know that (y - x)*z > 0

And, technically, even `Range(x, x + 2, 1)[0]` should not return `x` unless we know that it is an integer.

- [x] this now raises an error

* the relational form

cf #21285 For better protection, a two more Mods could be added to give: `And(Eq(Mod(x,1),a%s), Eq(Mod(a,1),0), Eq(Mod(s,1),0))` where `a` is a representative point in the Range that is not infinite, `s` is the step and `x` is the relational variable. We should also make sure that there is not a clash between the symbols of the Range and the relational variable as in `Range(x, x + 5, 2).as_relational(x)`.

- [x] this is done but the user can clash symbols if they want to
```python
>>> var('x')
x
>>> Range(x, x + 3, 2).as_relational(x)
(x >= x) & Eq(Mod(x, 1), 0) & (x <= x + 2)
>>> var('x',real=True)
x
>>> Range(x, x + 3, 2).as_relational(x)
Eq(Mod(x, 1), 0)
>>> var('x',integer=1)
x
>>> Range(x, x + 3, 2).as_relational(x)
True

>>> var('x')
x
>>> Range(1, 1+ 2*x, x).as_relational(x)
Eq(Mod(-1, x), 0) & Eq(Mod(x, 1), 0) & (((x >= 1) & (x <= x + 1)) | ((x <= -1) &
 (x <= 1) & (x >= x + 1)))
>>> var('x',real=1)
x
>>> Range(1, 1+ 2*x, x).as_relational(x)
(x >= 1) & Eq(Mod(-1, x), 0) & Eq(Mod(x, 1), 0)
>>> var('x',integer=1)
x
>>> Range(1, 1+ 2*x, x).as_relational(x)
(x >= 1) & Eq(Mod(-1, x), 0)
```
#### Release Notes

<!-- BEGIN RELEASE NOTES -->
* sets
  * Range involving symbols will be made more canonical (correcting a bug in calculation of `sup`) and errors will be raised when trying to compute attributes that are not known due to limitations of assumptions on the arguments.
<!-- END RELEASE NOTES -->


Problem statement:
make symbolic Range more canonical
Whereas a Range with numerical args is canonical, the Range containing symbols is not:
```python
>>> [Range(3,j,2) for j in range(4,10)]
[Range(3, 5, 2), Range(3, 5, 2), Range(3, 7, 2), Range(3, 7, 2), Range(3, 9, 2), Range(3, 9, 2)]

vs

>>> [Range(i,i+j,5) for j in range(1,6)]
[Range(i, i + 1, 5), Range(i, i + 2, 5), Range(i, i + 3, 5), Range(i, i + 4, 5), Range(i, i + 5, 5)]

which could be

[Range(i, i + 1, 5), Range(i, i + 1, 5), Range(i, i + 1, 5), Range(i, i + 1, 5), Range(i, i + 1, 5)]

```
The incorrect results are based on the assumption that the instantiated Range is canonical:
```python
>> r=Range(2, 2 + 3, 5)
>>> r.inf,r.reversed.sup
(2, 2)
>>> r = Range(k, k + 3, 5)
>>> r.inf,r.reversed.sup
(k, k - 2)
```

Hints from issue discussion:

Commit message: enforce step range with condition

Patch to review:
diff --git a/sympy/sets/fancysets.py b/sympy/sets/fancysets.py
index 45404f2862ae..0eae37f619b9 100644
--- a/sympy/sets/fancysets.py
+++ b/sympy/sets/fancysets.py
@@ -10,7 +10,7 @@
 from sympy.core.singleton import Singleton, S
 from sympy.core.symbol import Dummy, symbols, Symbol
 from sympy.core.sympify import _sympify, sympify, converter
-from sympy.logic.boolalg import And
+from sympy.logic.boolalg import And, Or
 from sympy.sets.sets import (Set, Interval, Union, FiniteSet,
     ProductSet)
 from sympy.utilities.misc import filldedent
@@ -571,7 +571,7 @@ class Range(Set):
         >>> r.inf
         n
         >>> pprint(r)
-        {n, n + 3, ..., n + 17}
+        {n, n + 3, ..., n + 18}
     """
 
     is_iterable = True
@@ -598,6 +598,8 @@ def __new__(cls, *args):
                         w.has(Symbol) and w.is_integer != False):
                     ok.append(w)
                 elif not w.is_Integer:
+                    if w.is_infinite:
+                        raise ValueError('infinite symbols not allowed')
                     raise ValueError
                 else:
                     ok.append(w)
@@ -610,10 +612,25 @@ def __new__(cls, *args):
 
         null = False
         if any(i.has(Symbol) for i in (start, stop, step)):
-            if start == stop:
+            dif = stop - start
+            n = dif/step
+            if n.is_Rational:
+                from sympy import floor
+                if dif == 0:
+                    null = True
+                else:  # (x, x + 5, 2) or (x, 3*x, x)
+                    n = floor(n)
+                    end = start + n*step
+                    if dif.is_Rational:  # (x, x + 5, 2)
+                        if (end - stop).is_negative:
+                            end += step
+                    else:  # (x, 3*x, x)
+                        if (end/stop - 1).is_negative:
+                            end += step
+            elif n.is_extended_negative:
                 null = True
             else:
-                end = stop
+                end = stop  # other methods like sup and reversed must fail
         elif start.is_infinite:
             span = step*(stop - start)
             if span is S.NaN or span <= 0:
@@ -631,8 +648,8 @@ def __new__(cls, *args):
             if n <= 0:
                 null = True
             elif oostep:
-                end = start + 1
-                step = S.One  # make it a canonical single step
+                step = S.One  # make it canonical
+                end = start + step
             else:
                 end = start + n*step
         if null:
@@ -656,7 +673,10 @@ def reversed(self):
         Range(9, -1, -1)
         """
         if self.has(Symbol):
-            _ = self.size  # validate
+            n = (self.stop - self.start)/self.step
+            if not n.is_extended_positive or not all(
+                    i.is_integer or i.is_infinite for i in self.args):
+                raise ValueError('invalid method for symbolic range')
         if not self:
             return self
         return self.func(
@@ -670,20 +690,25 @@ def _contains(self, other):
         if not other.is_integer:
             return other.is_integer
         if self.has(Symbol):
-            try:
-                _ = self.size  # validate
-            except ValueError:
+            n = (self.stop - self.start)/self.step
+            if not n.is_extended_positive or not all(
+                    i.is_integer or i.is_infinite for i in self.args):
                 return
+        else:
+            n = self.size
         if self.start.is_finite:
             ref = self.start
         elif self.stop.is_finite:
             ref = self.stop
         else:  # both infinite; step is +/- 1 (enforced by __new__)
             return S.true
-        if self.size == 1:
+        if n == 1:
             return Eq(other, self[0])
         res = (ref - other) % self.step
         if res == S.Zero:
+            if self.has(Symbol):
+                d = Dummy('i')
+                return self.as_relational(d).subs(d, other)
             return And(other >= self.inf, other <= self.sup)
         elif res.is_Integer:  # off sequence
             return S.false
@@ -691,20 +716,19 @@ def _contains(self, other):
             return None
 
     def __iter__(self):
-        if self.has(Symbol):
-            _ = self.size  # validate
+        n = self.size  # validate
         if self.start in [S.NegativeInfinity, S.Infinity]:
             raise TypeError("Cannot iterate over Range with infinite start")
         elif self:
             i = self.start
-            step = self.step
-
-            while True:
-                if (step > 0 and not (self.start <= i < self.stop)) or \
-                   (step < 0 and not (self.stop < i <= self.start)):
-                    break
-                yield i
-                i += step
+            if n.is_infinite:
+                while True:
+                    yield i
+                    i += self.step
+            else:
+                for j in range(n):
+                    yield i
+                    i += self.step
 
     def __len__(self):
         rv = self.size
@@ -717,12 +741,12 @@ def size(self):
         if not self:
             return S.Zero
         dif = self.stop - self.start
-        n = abs(dif // self.step)
-        if not n.is_Integer:
-            if n.is_infinite:
-                return S.Infinity
+        n = dif/self.step
+        if n.is_infinite:
+            return S.Infinity
+        if not n.is_Integer or not all(i.is_integer for i in self.args):
             raise ValueError('invalid method for symbolic range')
-        return n
+        return abs(n)
 
     @property
     def is_finite_set(self):
@@ -731,6 +755,9 @@ def is_finite_set(self):
         return self.size.is_finite
 
     def __bool__(self):
+        # this only distinguishes between definite null range
+        # and non-null/unknown null; getting True doesn't mean
+        # that it actually is not null
         return self.start != self.stop
 
     def __getitem__(self, i):
@@ -745,6 +772,8 @@ def __getitem__(self, i):
             "with an infinite value"
         if isinstance(i, slice):
             if self.size.is_finite:  # validates, too
+                if not self:
+                    return Range(0)
                 start, stop, step = i.indices(self.size)
                 n = ceiling((stop - start)/step)
                 if n <= 0:
@@ -847,42 +876,38 @@ def __getitem__(self, i):
         else:
             if not self:
                 raise IndexError('Range index out of range')
+            if not (all(i.is_integer or i.is_infinite
+                    for i in self.args) and ((self.stop - self.start)/
+                    self.step).is_extended_positive):
+                raise ValueError('invalid method for symbolic range')
             if i == 0:
                 if self.start.is_infinite:
                     raise ValueError(ooslice)
-                if self.has(Symbol):
-                    if (self.stop > self.start) == self.step.is_positive and self.step.is_positive is not None:
-                        pass
-                    else:
-                        _ = self.size  # validate
                 return self.start
             if i == -1:
                 if self.stop.is_infinite:
                     raise ValueError(ooslice)
-                n = self.stop - self.step
-                if n.is_Integer or (
-                        n.is_integer and (
-                            (n - self.start).is_nonnegative ==
-                            self.step.is_positive)):
-                    return n
-            _ = self.size  # validate
+                return self.stop - self.step
+            n = self.size  # must be known for any other index
             rv = (self.stop if i < 0 else self.start) + i*self.step
             if rv.is_infinite:
                 raise ValueError(ooslice)
-            if rv < self.inf or rv > self.sup:
-                raise IndexError("Range index out of range")
-            return rv
+            if 0 <= (rv - self.start)/self.step <= n:
+                return rv
+            raise IndexError("Range index out of range")
 
     @property
     def _inf(self):
         if not self:
-            raise NotImplementedError
+            return S.EmptySet.inf
         if self.has(Symbol):
-            if self.step.is_positive:
-                return self[0]
-            elif self.step.is_negative:
-                return self[-1]
-            _ = self.size  # validate
+            if all(i.is_integer or i.is_infinite for i in self.args):
+                dif = self.stop - self.start
+                if self.step.is_positive and dif.is_positive:
+                    return self.start
+                elif self.step.is_negative and dif.is_negative:
+                    return self.stop - self.step
+            raise ValueError('invalid method for symbolic range')
         if self.step > 0:
             return self.start
         else:
@@ -891,13 +916,15 @@ def _inf(self):
     @property
     def _sup(self):
         if not self:
-            raise NotImplementedError
+            return S.EmptySet.sup
         if self.has(Symbol):
-            if self.step.is_positive:
-                return self[-1]
-            elif self.step.is_negative:
-                return self[0]
-            _ = self.size  # validate
+            if all(i.is_integer or i.is_infinite for i in self.args):
+                dif = self.stop - self.start
+                if self.step.is_positive and dif.is_positive:
+                    return self.stop - self.step
+                elif self.step.is_negative and dif.is_negative:
+                    return self.start
+            raise ValueError('invalid method for symbolic range')
         if self.step > 0:
             return self.stop - self.step
         else:
@@ -909,27 +936,34 @@ def _boundary(self):
 
     def as_relational(self, x):
         """Rewrite a Range in terms of equalities and logic operators. """
-        if self.size == 1:
-            return Eq(x, self[0])
-        elif self.size == 0:
-            return S.false
+        from sympy.core.mod import Mod
+        if self.start.is_infinite:
+            assert not self.stop.is_infinite  # by instantiation
+            a = self.reversed.start
         else:
-            from sympy.core.mod import Mod
-            cond = None
-            if self.start.is_infinite:
-                if self.stop.is_infinite:
-                    cond = S.true
-                else:
-                    a = self.reversed.start
-            elif self.start == self.stop:
-                cond = S.false  # null range
-            else:
-                a = self.start
-            step = abs(self.step)
-            cond = Eq(Mod(x, step), a % step) if cond is None else cond
-            return And(cond,
-                       x >= self.inf if self.inf in self else x > self.inf,
-                       x <= self.sup if self.sup in self else x < self.sup)
+            a = self.start
+        step = self.step
+        in_seq = Eq(Mod(x - a, step), 0)
+        ints = And(Eq(Mod(a, 1), 0), Eq(Mod(step, 1), 0))
+        n = (self.stop - self.start)/self.step
+        try:
+            if n == 0:
+                return S.EmptySet.as_relational(x)
+            if n == 1:
+                return And(Eq(x, a), ints)
+            a, b = self.inf, self.sup
+            range_cond = And(
+                x > a if a.is_infinite else x >= a,
+                x < b if b.is_infinite else x <= b)
+        except ValueError:  # inf/sup unknown
+            a, b = self.start, self.stop - self.step
+            range_cond = Or(
+                And(self.step >= 1, x > a if a.is_infinite else x >= a,
+                x < b if b.is_infinite else x <= b),
+                And(self.step <= -1, x < a if a.is_infinite else x <= a,
+                x > b if b.is_infinite else x >= b))
+        return And(in_seq, ints, range_cond)
+
 
 converter[range] = lambda r: Range(r.start, r.stop, r.step)
 
diff --git a/sympy/sets/tests/test_fancysets.py b/sympy/sets/tests/test_fancysets.py
index 6b2cf51aeb2b..9fbdc2ecfa50 100644
--- a/sympy/sets/tests/test_fancysets.py
+++ b/sympy/sets/tests/test_fancysets.py
@@ -9,7 +9,7 @@
                    Dummy, floor, And, Eq)
 from sympy.utilities.iterables import cartes
 from sympy.testing.pytest import XFAIL, raises
-from sympy.abc import x, y, t
+from sympy.abc import x, y, t, z
 from sympy.core.mod import Mod
 
 import itertools
@@ -174,8 +174,6 @@ def test_inf_Range_len():
     assert Range(0, -oo, -2).size is S.Infinity
     assert Range(oo, 0, -2).size is S.Infinity
     assert Range(-oo, 0, 2).size is S.Infinity
-    i = Symbol('i', integer=True)
-    assert Range(0, 4 * i, i).size == 4
 
 
 def test_Range_set():
@@ -209,6 +207,9 @@ def test_Range_set():
     assert Range(1, oo, -1) == empty
     assert Range(1, -oo, 1) == empty
     assert Range(1, -4, oo) == empty
+    ip = symbols('ip', positive=True)
+    assert Range(0, ip, -1) == empty
+    assert Range(0, -ip, 1) == empty
     assert Range(1, -4, -oo) == Range(1, 2)
     assert Range(1, 4, oo) == Range(1, 2)
     assert Range(-oo, oo).size == oo
@@ -231,13 +232,8 @@ def test_Range_set():
     assert Range(-oo, 1, 1)[-1] is S.Zero
     assert Range(oo, 1, -1)[-1] == 2
     assert inf not in Range(oo)
-    inf = symbols('inf', infinite=True)
-    assert inf not in Range(oo)
-    assert Range(-oo, 1, 1)[-1] is S.Zero
-    assert Range(oo, 1, -1)[-1] == 2
     assert Range(1, 10, 1)[-1] == 9
     assert all(i.is_Integer for i in Range(0, -1, 1))
-
     it = iter(Range(-oo, 0, 2))
     raises(TypeError, lambda: next(it))
 
@@ -278,6 +274,7 @@ def test_Range_set():
     raises(ValueError, lambda: Range(-oo, 4, 2)[2::-1])
     assert Range(-oo, 4, 2)[-2::2] == Range(0, 4, 4)
     assert Range(oo, 0, -2)[-10:0:2] == empty
+    raises(ValueError, lambda: Range(oo, 0, -2)[0])
     raises(ValueError, lambda: Range(oo, 0, -2)[-10:10:2])
     raises(ValueError, lambda: Range(oo, 0, -2)[0::-2])
     assert Range(oo, 0, -2)[0:-4:-2] == empty
@@ -297,6 +294,7 @@ def test_Range_set():
     assert empty[:0] == empty
     raises(NotImplementedError, lambda: empty.inf)
     raises(NotImplementedError, lambda: empty.sup)
+    assert empty.as_relational(x) is S.false
 
     AB = [None] + list(range(12))
     for R in [
@@ -330,45 +328,85 @@ def test_Range_set():
 
     # test Range.as_relational
     assert Range(1, 4).as_relational(x) == (x >= 1) & (x <= 3)  & Eq(Mod(x, 1), 0)
-    assert Range(oo, 1, -2).as_relational(x) == (x >= 3) & (x < oo)  & Eq(Mod(x, 2), 1)
+    assert Range(oo, 1, -2).as_relational(x) == (x >= 3) & (x < oo)  & Eq(Mod(x + 1, -2), 0)
 
 
 def test_Range_symbolic():
     # symbolic Range
+    xr = Range(x, x + 4, 5)
     sr = Range(x, y, t)
     i = Symbol('i', integer=True)
     ip = Symbol('i', integer=True, positive=True)
-    ir = Range(i, i + 20, 2)
+    ipr = Range(ip)
+    inr = Range(0, -ip, -1)
+    ir = Range(i, i + 19, 2)
+    ir2 = Range(i, i*8, 3*i)
+    i = Symbol('i', integer=True)
     inf = symbols('inf', infinite=True)
+    raises(ValueError, lambda: Range(inf))
+    raises(ValueError, lambda: Range(inf, 0, -1))
+    raises(ValueError, lambda: Range(inf, inf, 1))
+    raises(ValueError, lambda: Range(1, 1, inf))
     # args
+    assert xr.args == (x, x + 5, 5)
     assert sr.args == (x, y, t)
     assert ir.args == (i, i + 20, 2)
+    assert ir2.args == (i, 10*i, 3*i)
     # reversed
+    raises(ValueError, lambda: xr.reversed)
     raises(ValueError, lambda: sr.reversed)
-    assert ir.reversed == Range(i + 18, i - 2, -2)
+    assert ipr.reversed.args == (ip - 1, -1, -1)
+    assert inr.reversed.args == (-ip + 1, 1, 1)
+    assert ir.reversed.args == (i + 18, i - 2, -2)
+    assert ir2.reversed.args == (7*i, -2*i, -3*i)
     # contains
     assert inf not in sr
     assert inf not in ir
+    assert 0 in ipr
+    assert 0 in inr
+    raises(TypeError, lambda: 1 in ipr)
+    raises(TypeError, lambda: -1 in inr)
     assert .1 not in sr
     assert .1 not in ir
     assert i + 1 not in ir
     assert i + 2 in ir
+    raises(TypeError, lambda: x in xr)  # XXX is this what contains is supposed to do?
     raises(TypeError, lambda: 1 in sr)  # XXX is this what contains is supposed to do?
     # iter
+    raises(ValueError, lambda: next(iter(xr)))
     raises(ValueError, lambda: next(iter(sr)))
     assert next(iter(ir)) == i
+    assert next(iter(ir2)) == i
     assert sr.intersect(S.Integers) == sr
     assert sr.intersect(FiniteSet(x)) == Intersection({x}, sr)
     raises(ValueError, lambda: sr[:2])
+    raises(ValueError, lambda: xr[0])
     raises(ValueError, lambda: sr[0])
-    raises(ValueError, lambda: sr.as_relational(x))
     # len
     assert len(ir) == ir.size == 10
+    assert len(ir2) == ir2.size == 3
+    raises(ValueError, lambda: len(xr))
+    raises(ValueError, lambda: xr.size)
     raises(ValueError, lambda: len(sr))
     raises(ValueError, lambda: sr.size)
     # bool
-    assert bool(ir) == bool(sr) == True
+    assert bool(ir) == bool(sr) == bool(xr) == True
+    # inf
+    raises(ValueError, lambda: xr.inf)
+    raises(ValueError, lambda: sr.inf)
+    assert ipr.inf == 0
+    assert inr.inf == -ip + 1
+    assert ir.inf == i
+    raises(ValueError, lambda: ir2.inf)
+    # sup
+    raises(ValueError, lambda: xr.sup)
+    raises(ValueError, lambda: sr.sup)
+    assert ipr.sup == ip - 1
+    assert inr.sup == 0
+    assert ir.inf == i
+    raises(ValueError, lambda: ir2.sup)
     # getitem
+    raises(ValueError, lambda: xr[0])
     raises(ValueError, lambda: sr[0])
     raises(ValueError, lambda: sr[-1])
     raises(ValueError, lambda: sr[:2])
@@ -376,17 +414,33 @@ def test_Range_symbolic():
     assert ir[0] == i
     assert ir[-2] == i + 16
     assert ir[-1] == i + 18
+    assert ir2[:2] == Range(i, 7*i, 3*i)
+    assert ir2[0] == i
+    assert ir2[-2] == 4*i
+    assert ir2[-1] == 7*i
     raises(ValueError, lambda: Range(i)[-1])
-    assert Range(ip)[-1] == ip - 1
+    assert ipr[0] == ipr.inf == 0
+    assert ipr[-1] == ipr.sup == ip - 1
+    assert inr[0] == inr.sup == 0
+    assert inr[-1] == inr.inf == -ip + 1
+    raises(ValueError, lambda: ipr[-2])
     assert ir.inf == i
     assert ir.sup == i + 18
-    assert Range(ip).inf == 0
-    assert Range(ip).sup == ip - 1
     raises(ValueError, lambda: Range(i).inf)
     # as_relational
-    raises(ValueError, lambda: sr.as_relational(x))
-    assert ir.as_relational(x) == (x >= i) & (x <= i + 18) & Eq(Mod(x, 2), Mod(i, 2))
+    assert ir.as_relational(x) == ((x >= i) & (x <= i + 18) &
+        Eq(Mod(-i + x, 2), 0))
+    assert ir2.as_relational(x) == Eq(
+        Mod(-i + x, 3*i), 0) & (((x >= i) & (x <= 7*i) & (3*i >= 1)) |
+        ((x <= i) & (x >= 7*i) & (3*i <= -1)))
     assert Range(i, i + 1).as_relational(x) == Eq(x, i)
+    assert sr.as_relational(z) == Eq(
+        Mod(t, 1), 0) & Eq(Mod(x, 1), 0) & Eq(Mod(-x + z, t), 0
+        ) & (((z >= x) & (z <= -t + y) & (t >= 1)) |
+        ((z <= x) & (z >= -t + y) & (t <= -1)))
+    assert xr.as_relational(z) == Eq(z, x) & Eq(Mod(x, 1), 0)
+    # symbols can clash if user wants (but it must be integer)
+    assert xr.as_relational(x) == Eq(Mod(x, 1), 0)
     # contains() for symbolic values (issue #18146)
     e = Symbol('e', integer=True, even=True)
     o = Symbol('o', integer=True, odd=True)


Code review comments:
diff_hunk: @@ -731,6 +755,9 @@ def is_finite_set(self):
         return self.size.is_finite
 
     def __bool__(self):
+        # this only distinguishes between definite null range
+        # and non-null/unknown null; getting True doesn't mean
+        # that it actually is not null
         return self.start != self.stop
path: sympy/sets/fancysets.py
text: @user1:
I think it would be better if this returned something like `is_eq(self.start, self.stop)` and raised an exception in the None case.

diff_hunk: @@ -909,27 +936,34 @@ def _boundary(self):
 
     def as_relational(self, x):
         """Rewrite a Range in terms of equalities and logic operators. """
-        if self.size == 1:
-            return Eq(x, self[0])
-        elif self.size == 0:
-            return S.false
+        from sympy.core.mod import Mod
+        if self.start.is_infinite:
+            assert not self.stop.is_infinite  # by instantiation
+            a = self.reversed.start
         else:
-            from sympy.core.mod import Mod
-            cond = None
-            if self.start.is_infinite:
-                if self.stop.is_infinite:
-                    cond = S.true
-                else:
-                    a = self.reversed.start
-            elif self.start == self.stop:
-                cond = S.false  # null range
-            else:
-                a = self.start
-            step = abs(self.step)
-            cond = Eq(Mod(x, step), a % step) if cond is None else cond
-            return And(cond,
-                       x >= self.inf if self.inf in self else x > self.inf,
-                       x <= self.sup if self.sup in self else x < self.sup)
+            a = self.start
+        step = self.step
+        in_seq = Eq(Mod(x - a, step), 0)
+        ints = And(Eq(Mod(a, 1), 0), Eq(Mod(step, 1), 0))
+        n = (self.stop - self.start)/self.step
+        try:
+            if n == 0:
+                return S.EmptySet.as_relational(x)
+            if n == 1:
+                return And(Eq(x, a), ints)
+            a, b = self.inf, self.sup
+            range_cond = And(
+                x > a if a.is_infinite else x >= a,
+                x < b if b.is_infinite else x <= b)
+        except ValueError:  # inf/sup unknown
path: sympy/sets/fancysets.py
text: @user1:
It would be better not to catch `ValueError`. Lots of things can raise `ValueError`. Ideally it would not be explicitly raised in sympy code when a sympy-specific exception could be used instead.

@author:
I wrapped only the sup/inf part in the try/except clause. This is the only error arising from these routines under the condition of working with a set for which n not in (0,1).