Repository: sympy/sympy

Issue:
n=6000002; `int(n*(log(n) + log(log(n))))` takes more than 200 s to compute on sympy.live.org
Version: sympy 1.12

The `sympy.prime` takes an unnaturally long time to return its value on certain arguments, for example, $6000002$. Note that feeding surrounding values such as $6000001$ and $6000003$ to the function does not yield such a hang.

```
sympy.prime(6000001) # Instant
sympy.prime(6000002) # Takes well over a minute
sympy.prime(6000003) # Instant
```

This is likely due to the code of calculating the upper bound for binary searching the value of $\mathrm{li}^{-1}$.
https://github.com/sympy/sympy/blob/f59cbf85581d2b9cd23d8f864607f4520df2366f/sympy/ntheory/generate.py#L441
Here, the equation `n * (log(n) + log(log(n)))` is calculated, and then casted to an `int`. When $n$ is $6000002$, the value of `n * (log(n) + log(log(n)))` is close to an integer (`110130079.99980894` when casted to a float). 
When provided with an $n$ such that the value of $n (\log n + \log \log n)$ is close to an integer, the `sympy.prime` function becomes extremely slow compared to other values close to $n$.



Discussion:

Patch:
diff --git a/.mailmap b/.mailmap
index 57c3513d3f..c86b4ec585 100644
--- a/.mailmap
+++ b/.mailmap
@@ -1554,6 +1554,7 @@ numbermaniac <5206120+numbermaniac@users.noreply.github.com>
 oittaa <8972248+oittaa@users.noreply.github.com>
 pekochun <hamburg_hamburger2000@yahoo.co.jp>
 philwillnyc <56197213+philwillnyc@users.noreply.github.com>
+platypus <platypus.computerchip@gmail.com>
 prshnt19 <prashant.rawat216@gmail.com>
 rahuldan <rahul02013@gmail.com>
 ramvenkat98 <ramvenkat98@gmail.com>
diff --git a/sympy/core/expr.py b/sympy/core/expr.py
index ee4ae39abe..77a17b6cbd 100644
--- a/sympy/core/expr.py
+++ b/sympy/core/expr.py
@@ -303,19 +303,6 @@ def __rdivmod__(self, other):
         return floor(other / self), Mod(other, self)
 
     def __int__(self):
-        # Although we only need to round to the units position, we'll
-        # get one more digit so the extra testing below can be avoided
-        # unless the rounded value rounded to an integer, e.g. if an
-        # expression were equal to 1.9 and we rounded to the unit position
-        # we would get a 2 and would not know if this rounded up or not
-        # without doing a test (as done below). But if we keep an extra
-        # digit we know that 1.9 is not the same as 1 and there is no
-        # need for further testing: our int value is correct. If the value
-        # were 1.99, however, this would round to 2.0 and our int value is
-        # off by one. So...if our round value is the same as the int value
-        # (regardless of how much extra work we do to calculate extra decimal
-        # places) we need to test whether we are off by one.
-        from .symbol import Dummy
         if not self.is_number:
             raise TypeError("Cannot convert symbols to int")
         r = self.round(2)
@@ -325,18 +312,20 @@ def __int__(self):
             raise TypeError("Cannot convert %s to int" % r)
         i = int(r)
         if not i:
-            return 0
-        # off-by-one check
-        if i == r and not (self - i).equals(0):
-            isign = 1 if i > 0 else -1
-            x = Dummy()
-            # in the following (self - i).evalf(2) will not always work while
-            # (self - r).evalf(2) and the use of subs does; if the test that
-            # was added when this comment was added passes, it might be safe
-            # to simply use sign to compute this rather than doing this by hand:
-            diff_sign = 1 if (self - x).evalf(2, subs={x: i}) > 0 else -1
-            if diff_sign != isign:
-                i -= isign
+            return i
+        if int_valued(r):
+            # non-integer self should pass one of these tests
+            if (self > i) is S.true:
+                return i
+            if (self < i) is S.true:
+                return i - 1
+            ok = self.equals(i)
+            if ok is None:
+                raise TypeError('cannot compute int value accurately')
+            if ok:
+                return i
+            # off by one
+            return i - (1 if i > 0 else -1)
         return i
 
     def __float__(self):
@@ -3848,9 +3837,12 @@ def round(self, n=None):
                     'Expected a number but got %s:' % func_name(x))
         elif x in _illegal:
             return x
-        if x.is_extended_real is False:
+        if not (xr := x.is_extended_real):
             r, i = x.as_real_imag()
-            return r.round(n) + S.ImaginaryUnit*i.round(n)
+            if xr is False:
+                return r.round(n) + S.ImaginaryUnit*i.round(n)
+            if i.equals(0):
+                return r.round(n)
         if not x:
             return S.Zero if n is None else x
 
@@ -3920,6 +3912,11 @@ def round(self, n=None):
         # the 2nd digit from the left) we get 5700000000000000.
         #
         xf = x.n(dps + extra)*Pow(10, shift)
+        if xf.is_Number and xf._prec == 1:  # xf.is_Add will raise below
+            # is x == 0?
+            if x.equals(0):
+                return Float(0)
+            raise ValueError('not computing with precision')
         xi = Integer(xf)
         # use the last digit to select the value of xi
         # nearest to x before rounding at the desired digit
@@ -4161,4 +4158,4 @@ def search_element(self, elem):
 from .function import Function, _derivative_dispatch
 from .mod import Mod
 from .exprtools import factor_terms
-from .numbers import Float, Integer, Rational, _illegal
+from .numbers import Float, Integer, Rational, _illegal, int_valued
diff --git a/sympy/ntheory/generate.py b/sympy/ntheory/generate.py
index 8d24040190..a63a299c56 100644
--- a/sympy/ntheory/generate.py
+++ b/sympy/ntheory/generate.py
@@ -438,6 +438,8 @@ def prime(nth):
     from sympy.functions.elementary.exponential import log
     from sympy.functions.special.error_functions import li
     a = 2 # Lower bound for binary search
+    # leave n inside int since int(i*r) != i*int(r) is not a valid property
+    # e.g. int(2*.5) != 2*int(.5)
     b = int(n*(log(n) + log(log(n)))) # Upper bound for the search.
 
     while a < b:


Test patch:
diff --git a/sympy/core/tests/test_expr.py b/sympy/core/tests/test_expr.py
index ef1441afb6..137231e551 100644
--- a/sympy/core/tests/test_expr.py
+++ b/sympy/core/tests/test_expr.py
@@ -2084,6 +2084,19 @@ def test_round():
         assert type(round(fi)) is int
         assert S(fi).round().is_Integer
 
+        # issue 25698
+        n = 6000002
+        assert int(n*(log(n) + log(log(n)))) == 110130079
+        one = cos(2)**2 + sin(2)**2
+        eq = exp(one*I*pi)
+        qr, qi = eq.as_real_imag()
+        assert qi.round(2) == 0.0
+        assert eq.round(2) == -1.0
+        eq = one - 1/S(10**120)
+        assert S.true not in (eq > 1, eq < 1)
+        assert int(eq) == int(.9) == 0
+        assert int(-eq) == int(-.9) == 0
+
 
 def test_held_expression_UnevaluatedExpr():
     x = symbols("x")