Repository: sagemath/sage

Issue:
disagreement between .reduced_form() and .is_reduced() for some binary quadratic forms
```sage
sage: for x in range(0xa19ae44106b09bfffffffffff0, 0xa19ae44106b09c000000000010):
....:     print(hex(x), BinaryQF([1,0,-x]).reduced_form().is_reduced())
....:
0xa19ae44106b09bfffffffffff0 True
0xa19ae44106b09bfffffffffff1 True
0xa19ae44106b09bfffffffffff2 True
0xa19ae44106b09bfffffffffff3 True
0xa19ae44106b09bfffffffffff4 True
0xa19ae44106b09bfffffffffff5 True
0xa19ae44106b09bfffffffffff6 True
0xa19ae44106b09bfffffffffff7 True
0xa19ae44106b09bfffffffffff8 True
0xa19ae44106b09bfffffffffff9 True
0xa19ae44106b09bfffffffffffa True
0xa19ae44106b09bfffffffffffb True
0xa19ae44106b09bfffffffffffc True
0xa19ae44106b09bfffffffffffd True
0xa19ae44106b09bfffffffffffe True
0xa19ae44106b09bffffffffffff True
0xa19ae44106b09c000000000000 False
0xa19ae44106b09c000000000001 False
0xa19ae44106b09c000000000002 False
0xa19ae44106b09c000000000003 False
0xa19ae44106b09c000000000004 False
0xa19ae44106b09c000000000005 False
0xa19ae44106b09c000000000006 False
0xa19ae44106b09c000000000007 False
0xa19ae44106b09c000000000008 False
0xa19ae44106b09c000000000009 False
0xa19ae44106b09c00000000000a False
0xa19ae44106b09c00000000000b False
0xa19ae44106b09c00000000000c False
0xa19ae44106b09c00000000000d False
0xa19ae44106b09c00000000000e False
0xa19ae44106b09c00000000000f False
```

As we can see above, it seems to be a size issue. The cutoff seems strange, does anyone recognize this number?


Discussion:
~~Not sure about the cutoff number, but this seems to be a PARI bug in `.qfbred()`/`.qfbredsl2()`, which is called in `.reduced_form()` (or a bug in the calls to PARI): Indeed, if you add the optional argument `algorithm='sage'`, then it always returns `True` as expected - and the computed forms start to differ precisely at the cutoff.~~

Scratch the above, this is caused by rounding errors of `.sqrt()` in `.is_reduced()`. If you do the checks manually with higher square root precision, then it correctly shows that the forms computed for all of the values above are reduced.
I think the conditions in `.is_reduced()` can be reformulated to avoid the square root; I'll work on a fix.

Resolving patch:
diff --git a/src/sage/quadratic_forms/binary_qf.py b/src/sage/quadratic_forms/binary_qf.py
--- a/src/sage/quadratic_forms/binary_qf.py
+++ b/src/sage/quadratic_forms/binary_qf.py
@@ -1442,7 +1442,8 @@ def is_reduced(self):
           coefficients `(-a, b, -c)` is reduced.
 
         - If `f` is indefinite (`D > 0`), then `f` is reduced if and
-          only if `|\sqrt{D} - 2|a|| < b < \sqrt{D}`
+          only if [`b > 0`, `ac < 0` and `(a-c)^2 < D`]
+          (equivalently if `|\sqrt{D} - 2|a|| < b < \sqrt{D}`)
           or [`a = 0` and `-b < 2c \leq b`]
           or [`c = 0` and `-b < 2a \leq b`].
 
@@ -1474,20 +1475,31 @@ def is_reduced(self):
             sage: BinaryQF(1, 5, -1).is_reduced()
             True
 
+        TESTS:
+
+        We check that :issue:`37635` is fixed::
+
+            sage: list = range(0xa19ae44106b09bfffffffffff0, 0xa19ae44106b09c000000000010)
+            sage: all(BinaryQF([1, 0, -x]).reduced_form().is_reduced() for x in list)
+            True
         """
         D = self.discriminant()
+        if D.is_zero():
+            raise ValueError('the quadratic form must be non-singular')
+
         a = self._a
         b = self._b
         c = self._c
         if D < 0 and a > 0:
             return ((-a < b <= a < c)
                     or (ZZ(0) <= b <= a == c))
-        elif D < 0 and self._a < 0:
+        elif D < 0 and a < 0:
             return ((a < b <= -a < -c)
                     or (ZZ(0) <= b <= -a == -c))
+
+        # Note that a = 0 implies D > 0 here
         else:
-            d = D.sqrt(prec=53)
-            return (((d - 2*a.abs()).abs() < b < d)
+            return ((b > 0 and a*c < 0 and (a-c)**2 < D)
                     or (0 == a and -b < 2*c <= b)
                     or (0 == c and -b < 2*a <= b))
 


Test patch: