Skip to content
The Lumenary

Math laboratory

Class numbers by reduced forms and fundamental units

Computes the class number of every imaginary quadratic field with fundamental discriminant -200 <= D < 0 by counting Gauss's reduced forms, checks each against Dirichlet's class number formula, lists the D with class number 1, and finds fundamental units of Z[sqrt d] from continued fractions. Exact and exhaustive in that range.

Back to the labExact computationExhaustive finite check
Class numbers by reduced forms and fundamental unitsPythonExact computationExhaustive finite checkComputationally tested
"""Class numbers of imaginary quadratic fields and units of real ones.

Gauss's reduced binary quadratic forms count ideal classes: for a negative
fundamental discriminant D, the class number h(D) of Q(sqrt D) equals the
number of reduced primitive forms a x^2 + b xy + c y^2 with b^2 - 4ac = D,
|b| <= a <= c (and b >= 0 if |b| = a or a = c). We compute h(D) this way for
every fundamental discriminant -200 <= D < 0, and check each value against
Dirichlet's analytic class number formula
    h(D) = -(w / (2|D|)) * sum_{a=1}^{|D|-1} chi_D(a) * a,
where chi_D is the Kronecker symbol and w is the number of roots of unity.
Then, for real quadratic rings Z[sqrt d], we find the fundamental unit by the
continued fraction of sqrt d (the smallest solution of x^2 - d y^2 = +-1),
showing how wildly its size varies - Dirichlet's unit theorem promises one
fundamental unit but says nothing about how large it is. Exact integer
arithmetic, exhaustive in the stated range.
"""

import math

def is_squarefree(n):
    """True if no square of a prime divides n (n != 0)."""
    n = abs(n)
    return all(n % (q * q) for q in range(2, math.isqrt(n) + 1))

def is_fundamental(D):
    """Fundamental discriminant test for D != 0, 1."""
    if D % 4 == 1:
        return is_squarefree(D)
    if D % 4 == 0:
        m = D // 4
        return m % 4 in (2, 3) and is_squarefree(m)
    return False

def reduced_forms(D):
    """Reduced primitive positive definite forms (a, b, c) of discriminant D < 0."""
    forms = []
    a = 1
    while 3 * a * a <= -D:
        for b in range(-a + 1, a + 1):
            if (b * b - D) % (4 * a):
                continue
            c = (b * b - D) // (4 * a)
            if c < a or (c == a and b < 0):
                continue
            if math.gcd(math.gcd(a, b), c) == 1:
                forms.append((a, b, c))
        a += 1
    return forms

def class_number(D):
    """h(D) as the number of reduced primitive forms."""
    return len(reduced_forms(D))

def kronecker(D, n):
    """Kronecker symbol (D/n) for n >= 1."""
    result, m, p = 1, n, 2
    while m > 1:
        if m % p:
            p += 1
            continue
        m //= p
        if p == 2:
            factor = 0 if D % 2 == 0 else (1 if D % 8 in (1, 7) else -1)
        else:
            r = pow(D % p, (p - 1) // 2, p)
            factor = 0 if D % p == 0 else (1 if r == 1 else -1)
        result *= factor
    return result

def class_number_formula(D):
    """Dirichlet's formula h = -(w / 2|D|) sum chi_D(a) a, as an exact integer."""
    w = {-3: 6, -4: 4}.get(D, 2)
    s = sum(kronecker(D, a) * a for a in range(1, -D))
    value = -w * s
    if value % (2 * -D):
        raise ArithmeticError("formula did not give an integer")
    return value // (2 * -D)

def fundamental_unit(d):
    """Smallest x + y sqrt d (x, y > 0) with x^2 - d y^2 = +-1, d not a square."""
    a0 = math.isqrt(d)
    m, q, a = 0, 1, a0
    h_prev, h = 1, a0
    k_prev, k = 0, 1
    while h * h - d * k * k not in (1, -1):
        m = q * a - m
        q = (d - m * m) // q
        a = (a0 + m) // q
        h_prev, h = h, a * h + h_prev
        k_prev, k = k, a * k + k_prev
    return h, k, h * h - d * k * k

def main():
    discs = [D for D in range(-200, 0) if is_fundamental(D)]
    hs = {D: class_number(D) for D in discs}
    agree = all(hs[D] == class_number_formula(D) for D in discs)
    print(f"Fundamental discriminants -200 <= D < 0: {len(discs)} fields")
    print("  h(D) by reduced forms equals Dirichlet's formula for all:",
          agree)
    print("  h(D) for the first fields:")
    first = sorted(discs, reverse=True)[:20]
    for start in (0, 10):
        print("   " + " ".join(f"{D}:{hs[D]}" for D in first[start:start + 10]))
    print("  class number 1:", [D for D in sorted(discs, reverse=True)
                                if hs[D] == 1])
    print("  forms of D = -20 (Q(sqrt-5)):", reduced_forms(-20))
    print(f"  largest h in range: {max(hs.values())}")
    print("Fundamental units of Z[sqrt d] (x + y sqrt d, norm x^2 - d y^2):")
    for d in (2, 3, 7, 19, 31, 43, 46, 94):
        x, y, nrm = fundamental_unit(d)
        print(f"  d = {d:>2}: {x} + {y} sqrt{d}  (norm {nrm:+d})")
    print()
    print("Epistemic status: exact integer computations, exhaustive for the")
    print("stated discriminants. That -163 is the last D with h(D) = 1 is the")
    print("Baker-Heegner-Stark theorem; the computation only covers |D| <= 200.")

if __name__ == "__main__":
    main()
Runs entirely in your browser, in an isolated worker. Nothing is sent to a server.

Used in