Repository: sympy/sympy

Issue:
ceiling(CRootOf()) not implemented, leads to exception in `Range`
As the title says, `ceiling(CRootOf())` is not implemented. This can lead to issues if sets are involved. (E.g. when resolving trig equations.)
If I'm not mistaken it should be sufficient to add a special case to [`ceiling`'s `_eval_number()`](https://github.com/sympy/sympy/blob/2d2052cb56d8a675c7395717461833d44d683479/sympy/functions/elementary/integers.py#L304) method. (I may be wrong though...)

In the stracktrace below, `Range` complains about symbolic start/end due to an unevaluated `ceiling(CRootOf())`, even though `CRootOf` stands for a precisely defined real/complex number.

```Python
In [2]: var('n')
   ...: s = ImageSet(Lambda(n, n + (CRootOf(x**5 - x**2 + 1, 0))), Integers)
   ...: s.intersect(Interval(-10, 10))
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[2], line 3
      1 var('n')
      2 s = ImageSet(Lambda(n, n + (CRootOf(x**5 - x**2 + 1, 0))), Integers)
----> 3 s.intersect(Interval(-10, 10))

File ~/sympy/sympy/sets/sets.py:150, in Set.intersect(self, other)
    131 def intersect(self, other):
    132     """
    133     Returns the intersection of 'self' and 'other'.
    134
   (...)
    148
    149     """
--> 150     return Intersection(self, other)

File ~/sympy/sympy/sets/sets.py:1507, in Intersection.__new__(cls, evaluate, *args)
   1505 if evaluate:
   1506     args = list(cls._new_args_filter(args))
-> 1507     return simplify_intersection(args)
   1509 args = list(ordered(args, Set._infimum_key))
   1511 obj = Basic.__new__(cls, *args)

File ~/sympy/sympy/sets/sets.py:2644, in simplify_intersection(args)
   2642 new_args = False
   2643 for t in args - {s}:
-> 2644     new_set = intersection_sets(s, t)
   2645     # This returns None if s does not know how to intersect
   2646     # with t. Returns the newly intersected set otherwise
   2648     if new_set is not None:

File ~/sympy/sympy/multipledispatch/dispatcher.py:243, in Dispatcher.__call__(self, *args, **kwargs)
    241     self._cache[types] = func
    242 try:
--> 243     return func(*args, **kwargs)
    245 except MDNotImplementedError:
    246     funcs = self.dispatch_iter(*types)

File ~/sympy/sympy/sets/handlers/intersection.py:399, in _(self, other)
    397     return S.EmptySet
    398 elif isinstance(range_set, Range) and range_set.size is not S.Infinity:
--> 399     range_set = FiniteSet(*list(range_set))
    401 if range_set is not None:
    402     return imageset(Lambda(n, f), range_set)

File ~/sympy/sympy/sets/fancysets.py:745, in Range.__iter__(self)
    743 n = self.size  # validate
    744 if not (n.has(S.Infinity) or n.has(S.NegativeInfinity) or n.is_Integer):
--> 745     raise TypeError("Cannot iterate over symbolic Range")
    746 if self.start in [S.NegativeInfinity, S.Infinity]:
    747     raise TypeError("Cannot iterate over Range with infinite start")

TypeError: Cannot iterate over symbolic Range
```

Discussion:

Patch:
diff --git a/sympy/functions/elementary/integers.py b/sympy/functions/elementary/integers.py
index fcb342a903..bbbc7113c9 100644
--- a/sympy/functions/elementary/integers.py
+++ b/sympy/functions/elementary/integers.py
@@ -9,7 +9,6 @@
 from sympy.core.logic import fuzzy_or
 from sympy.core.numbers import Integer, int_valued
 from sympy.core.relational import Gt, Lt, Ge, Le, Relational, is_eq
-from sympy.core.symbol import Symbol
 from sympy.core.sympify import _sympify
 from sympy.functions.elementary.complexes import im, re
 from sympy.multipledispatch import dispatch
@@ -49,10 +48,10 @@ def eval(cls, arg):
                 ipart += i*S.ImaginaryUnit
             elif (i := intof(t)) is not None:
                 ipart += i
-            elif t.has(Symbol):
-                spart += t
-            else:
+            elif t.is_number:
                 npart += t
+            else:
+                spart += t
 
         if not (npart or spart):
             return ipart


Test patch:
diff --git a/sympy/functions/elementary/tests/test_integers.py b/sympy/functions/elementary/tests/test_integers.py
index c2c777dcf2..1ac681a678 100644
--- a/sympy/functions/elementary/tests/test_integers.py
+++ b/sympy/functions/elementary/tests/test_integers.py
@@ -8,6 +8,11 @@
 from sympy.functions.elementary.integers import (ceiling, floor, frac)
 from sympy.functions.elementary.miscellaneous import sqrt
 from sympy.functions.elementary.trigonometric import sin, cos, tan
+from sympy.polys.rootoftools import RootOf, CRootOf
+from sympy import Integers
+from sympy.sets.sets import Interval
+from sympy.sets.fancysets import ImageSet
+from sympy.core.function import Lambda
 
 from sympy.core.expr import unchanged
 from sympy.testing.pytest import XFAIL, raises
@@ -203,6 +208,8 @@ def test_floor():
     assert (floor(y) < n) == (y < n)
     assert (floor(y) > n) == (y >= n + 1)
 
+    assert floor(RootOf(x**3 - 27*x, 2)) == 5
+
 
 def test_ceiling():
 
@@ -390,6 +397,12 @@ def test_ceiling():
     assert (ceiling(y) < n) == (y <= n - 1)
     assert (ceiling(y) > n) == (y > n)
 
+    assert ceiling(RootOf(x**3 - 27*x, 2)) == 6
+    s = ImageSet(Lambda(n, n + (CRootOf(x**5 - x**2 + 1, 0))), Integers)
+    f = CRootOf(x**5 - x**2 + 1, 0)
+    s = ImageSet(Lambda(n, n + f), Integers)
+    assert s.intersect(Interval(-10, 10)) == {i + f for i in range(-9, 11)}
+
 
 def test_frac():
     assert isinstance(frac(x), frac)