Repository: sagemath/sage

Issue:
Bug in integration on a Riemann surface<title>
### Steps To Reproduce

Inside the Sage terminal, run 

```
R.<z,x> = QQ[]
f = x*(1^5+z^5) + (x*1*z)^2 - x^4*1*z - 2*1^3*z^3
S = Curve(f).riemann_surface()
S.riemann_matrix()
```



### Expected Behavior

You would expect this to successfully compute the Riemann matrix of the corresponding Riemann surface. 

### Actual Behavior

It gives a `ValueError` inside `rigorous_line_integral`. 

### Additional Information

This error doesn't occur if instead you either use `R.<x,z> = QQ[]` or `S = Curve(f).riemann_surface(integration_method="heuristic")`, which suggests it is an error in how the path-splitting method is being applied in certain configurations. This has been discussed [on sage-devel](https://groups.google.com/g/sage-devel/c/wcznXH3WLSM/m/EVP_1k-FAQAJ). 

### Environment

```markdown
- **OS**: both Red Hat Enterprise 8.9 and Ubuntu 18.04.6
- **Sage Version**: both 9.8, 10.3.beta0, and 10.3.beta4
```


### Checklist

- [X] I have searched the existing issues for a bug report that matches the one I want to file, without success.
- [X] I have read the documentation and troubleshoot guide


Discussion:

Resolving patch:
diff --git a/src/sage/schemes/riemann_surfaces/riemann_surface.py b/src/sage/schemes/riemann_surfaces/riemann_surface.py
--- a/src/sage/schemes/riemann_surfaces/riemann_surface.py
+++ b/src/sage/schemes/riemann_surfaces/riemann_surface.py
@@ -2152,7 +2152,10 @@ def rigorous_line_integral(self, upstairs_edge, differentials, bounding_data):
         z1 = zwt(1)[0]
 
         # list of (centre, radius) pairs that still need to be processed
-        ball_stack = [(self._RR(1 / 2), self._RR(1 / 2), 0)]
+        # None is a sentinel value to indicate that the minimum number of
+        # nodes required to integrate on the corresponding segment within
+        # the required error tolerance is not yet known.
+        ball_stack = [(self._RR(1 / 2), self._RR(1 / 2), None)]
         alpha = self._RR(912 / 1000)
         # alpha set manually for scaling purposes. Basic benchmarking shows
         # that ~0.9 is a sensible value.
@@ -2186,8 +2189,21 @@ def local_N(ct, rt):
             rho_z = min(distances)
             rho_t = rho_z / (z1_minus_z0).abs()
             rho_t = alpha * rho_t + (1 - alpha) * rt  # sqrt(rho_t*rt) could also work
-            rho_z = rho_t * (z1 - z0).abs()
+            rho_z = rho_t * (z1_minus_z0).abs()
             delta_z = (alpha * rho_t + (1 - alpha) * rt) * (z1_minus_z0).abs()
+            # delta_z and delta_z^2 / (rho_z * (rho_z - delta_z)) are the two
+            # prefactors that occur in the computation of the magnitude bound
+            # M. delta_z should never be infinite, but the second factor could
+            # be if rho_z - delta_z is 0. Mathematically it would never be 0
+            # as we ensure rho_t > rt before running local_N, but the
+            # floating point operations can ruin this.
+            # The second prefactor is actually homogeneous in
+            # z1_minus_z0.abs(), so we shall compute this factor without those
+            # multiplications as a function of rho_t / rt which should thus be
+            # more resistance to floating-point errors.
+            pf2 = (alpha + (1 - alpha) * (rt / rho_t))**2 / (
+                  (1 - alpha) * (1 - rt / rho_t)
+            )
             expr = (
                 rho_t / rt + ((rho_t / rt)**2 - 1).sqrt()
             )  # Note this is really exp(arcosh(rho_t/rt))
@@ -2208,15 +2224,14 @@ def local_N(ct, rt):
                 )
                 cg = g(cz, cw)
                 cdgdz = dgdz(cz, cg)
-                Delta = delta_z * cdgdz.abs() + (delta_z**2) * M_tilde / (
-                    rho_z * (rho_z - delta_z)
-                )
-                M = Delta
+                M = delta_z * cdgdz.abs() + pf2 * M_tilde
                 N_required = (
                     (M * (self._RR.pi() + 64 / (15 * (expr**2 - 1))) / E_global).log()
                     / (2 * expr.log())
-                ).ceil()
-                Ni = max(Ni, N_required)
+                )
+                if N_required.is_positive_infinity():
+                    return 2**max(60, self._prec)
+                Ni = max(Ni, N_required.ceil())
             return Ni
 
         while ball_stack:
@@ -2224,15 +2239,15 @@ def local_N(ct, rt):
             ncts = [ct - rt / 2, ct + rt / 2]
             nrt = rt / 2
 
-            if not lN:
+            if lN is None:
                 cz = (1 - ct) * z0 + ct * z1
                 distances = [(cz - b).abs() for b in self.branch_locus]
                 rho_z = min(distances)
                 rho_t = rho_z / (z1_minus_z0).abs()
 
                 if rho_t <= rt:
-                    ball_stack.append((ncts[0], nrt, 0))
-                    ball_stack.append((ncts[1], nrt, 0))
+                    ball_stack.append((ncts[0], nrt, None))
+                    ball_stack.append((ncts[1], nrt, None))
                     continue
 
                 lN = local_N(ct, rt)


Test patch: