Skip to content

Commit 7e8a6f1

Browse files
committed
Fix NumberFieldElement_quadratic.is_integral() when D is not square-free
1 parent 439065e commit 7e8a6f1

File tree

1 file changed

+47
-9
lines changed

1 file changed

+47
-9
lines changed

src/sage/rings/number_field/number_field_element_quadratic.pyx

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2137,19 +2137,57 @@ cdef class NumberFieldElement_quadratic(NumberFieldElement_absolute):
21372137
True
21382138
sage: for _ in range(20):
21392139
....: assert O.random_element().is_integral()
2140+
2141+
Check that :issue:`34800` is fixed::
2142+
2143+
sage: K.<t> = QuadraticField(-10007^2)
2144+
sage: (t/10007).is_integral()
2145+
True
21402146
"""
2147+
cdef mpz_t m, n, q, r, s, t, u
2148+
cdef bint result = False
2149+
2150+
# Shortcut for "obviously integral" elements
21412151
if mpz_cmp_ui(self.denom, 1) == 0:
21422152
return True
21432153

2144-
# Check for an element of the form x + y sqrt(D) where x and y
2145-
# are half-integers.
2146-
if mpz_even_p(self.a) or mpz_even_p(self.b):
2147-
return False
2148-
if mpz_cmp_ui(self.denom, 2) != 0:
2149-
return False
2150-
# Numbers with half-integral components are integral only for
2151-
# D = 1 mod 4
2152-
return mpz_fdiv_ui(self.D.value, 4) == 1
2154+
# a + b*sqrt(D) is integral if and only if
2155+
# denom | 2*a and denom^2 | a^2 - D*b^2.
2156+
# Do division with remainder: 2*a = denom*m + n.
2157+
mpz_init(t)
2158+
mpz_init(m)
2159+
mpz_init(n)
2160+
mpz_mul_ui(t, self.a, 2)
2161+
mpz_fdiv_qr(m, n, t, self.denom)
2162+
if mpz_cmp_ui(n, 0) == 0:
2163+
# Now 2*a = denom*m.
2164+
# If m is even, then denom | a and gcd(denom, b) = 1, so
2165+
# a + b*sqrt(D) is integral if and only if denom^2 | D.
2166+
# If m is odd, then denom is even; put u = denom/2.
2167+
# Then a + b*sqrt(D) is integral if and only if
2168+
# b is odd, u^2 | D and D/u^2 is congruent to 1 mod 4.
2169+
if mpz_even_p(m):
2170+
mpz_init(s)
2171+
mpz_mul(s, self.denom, self.denom)
2172+
result = mpz_divisible_p(self.D.value, s)
2173+
mpz_clear(s)
2174+
elif mpz_odd_p(self.b):
2175+
mpz_init(u)
2176+
mpz_init(s)
2177+
mpz_init(q)
2178+
mpz_init(r)
2179+
mpz_divexact_ui(u, self.denom, 2)
2180+
mpz_mul(s, u, u)
2181+
mpz_fdiv_qr(q, r, self.D.value, s)
2182+
result = mpz_cmp_ui(r, 0) == 0 and mpz_fdiv_ui(q, 4) == 1
2183+
mpz_clear(u)
2184+
mpz_clear(s)
2185+
mpz_clear(q)
2186+
mpz_clear(r)
2187+
mpz_clear(t)
2188+
mpz_clear(m)
2189+
mpz_clear(n)
2190+
return result
21532191

21542192
def charpoly(self, var='x', algorithm=None):
21552193
r"""

0 commit comments

Comments
 (0)