"""conductor.py -- what the conductor of a complex cubic field hands its quadratic resolvent, read as a module under conjugation, and what that module decides about the triples of triple.py. QUESTION. K a complex cubic field, d_K = f^2 d0 with d0 the fundamental discriminant of the resolvent k = Q(sqrt d0) and f the conductor; N the Galois closure, chi the cubic character of k cutting out N, sigma the conjugation of k. triple.py reads the triples of classes over the split primes and finds they fill a subgroup R of M = {(a, b, c) : a + b + c = 0} of index 3^t, t read off f. This file reads WHY off the ray class group of k modulo f, built rather than inferred: (a) Which rational primes can divide f at all, and how do they sit in k? (b) The 3-part of (O_k/f)^* splits under sigma into a FIXED (plus) and an INVERTED (minus) part. What are their ranks, prime by prime, and which fixed lines can a field use? (c) At 3-rank 1, what does the SIGN of the second line of the degree-9 extension E of k, containing N and unramified over N, say about the regime R = M against R = Delta? (d) At f = 1, where N/k is unramified, what does 3 | h force on the class group of k? THE ARGUMENT (written before the engine). (1) THE MODULUS IS THE INTEGER f. By conductor-discriminant for N/Q, |d_K| = |d0| N(fr), fr the conductor of chi, so N(fr) = f^2; chi o sigma = chi^-1 makes fr sigma-stable, and the local reading below admits no sigma-stable ideal of norm f^2 carrying chi but (f). (2) THE PLUS PART IS LOCAL. In 1 -> (O_k/f)^*/im(O_k^*) -> Cl_f(k) -> Cl(k) -> 1, 3-parts and then sigma-parts are exact (2 is invertible on a 3-group); sigma inverts Cl(k), since an ideal times its conjugate is principal, so Cl_f(k)_3^+ is ((O_k/f)^*)_3^+. The global units meet the 3-part only at d0 = -3, in a cube root of unity, which sigma inverts. (3) THE ADMISSIBLE PRIMES. At a prime Q of k dividing fr the local component chi_Q is a nontrivial cubic character of the local units, anti-invariant where sigma fixes Q. For q != 3: split q gives two swapped primes, each with the cyclic 3-part of (Z/q^e)^*, nontrivial iff q = 1 mod 3; inert q gives the 3-part of q^2 - 1, which is FIXED when q = 1 mod 3 (it sits in F_q^*, where Frobenius = sigma acts trivially) and INVERTED when q = 2 mod 3 (it sits in the norm-one part, order q + 1); ramified q gives F_q^*, fixed. An anti-invariant chi_Q needs an inverted piece, so: a prime q = 1 mod 3 divides f only when it SPLITS in k, a prime q = 2 mod 3 only when it is INERT. At q = 3, with a = v_3(f): v_3(d_K) = 2a + v_3(d0) <= 5 for a cubic field, so a <= 2, and a = 1 needs 3 | d0 (2 is no cubic field's 3-valuation). (4) THE LOCAL RANKS, prime power by prime power (the plus and minus ranks of the 3-part of (O_k/q^e)^*): split q = 1 mod 3 plus 1, minus 1 (two swapped cyclic factors of equal order) inert q = 2 mod 3 plus 0, minus 1 3 split, a = 2 plus 1, minus 1 ((Z/9)^* twice) 3 inert, a = 2 plus 1, minus 1 (U_1/U_2 = F_9 as an additive group, sigma the Frobenius, fixing F_3) 3 ramified, a = 1 plus 0, minus 1 (Q/Q^2 with sigma(pi) = -pi) 3 ramified, a = 2 plus 1, minus 2 (order 27; the fixed part the image of Z_3's units) So the plus rank of the whole is P = #{q = 1 mod 3 : q | f} + [9 | f], and a fixed line EXISTS at every field with 9 | f. (5) USABILITY. E/N unramified at Q means psi|U_Q lies in for each character psi of Gal(E/k). A FIXED psi at a sigma-fixed Q must then be trivial there ( holds no nontrivial invariant character), while every nontrivial fixed character is ramified somewhere over f by (2). At a swapped pair the local 3-part is cyclic, generated by chi_Q, so nothing is asked. Hence a USABLE fixed line exists iff some prime of k dividing f is split over Q with nontrivial local 3-part: by (3) and (4), iff t >= 1. The fixed lines that exist and cannot be used are exactly those of a 9 | f with 3 not split, at a field with no q = 1 mod 3 dividing f. (6) THE FIXED STEP. Suppose R = Delta at 3-rank 1, and let H' be the cubic unramified extension of K, H'_1 its copy over K_1. The transposition tau fixing K_1 pointwise restricts to sigma on k; any lift stabilizes H'_1 and acts on it as an element of the abelian group Gal(H'_1/K_1), so conjugation by it is trivial on Gal(J/N) = Gal(H'_1/K_1), J = H'_1 N. Order 9 with 2 invertible splits Gal(J/k) into sigma-parts; the minus part meets the fixed Gal(J/N) trivially and maps into Gal(N/k), which sigma inverts, so both parts have order 3: the SECOND LINE IS FIXED, and by (5) usable, so t >= 1. Contrapositive: at 3-rank 1, t = 0 forces R = M. (With the genus field bounding from below, the index law of triple.py is proved at 3-rank 1.) (7) THE INVERTED STEP. Suppose R = M at 3-rank 1. Project the triples to (Cl/3Cl)^3: the image is the whole sum-zero module St, of order 9. Let L be its fixed field over N: L/N is unramified, L is Galois over Q (the kernel is S3-stable), and G = Gal(L/k) has order 27 and contains St, so it is not cyclic. The commutators [g, m] = (s - 1)m, s a 3-cycle, fill the diagonal D, and G/D is abelian, so G^ab has order 9; a 3-group needs as many generators as its abelianization (Burnside), so G^ab = (Z/3)^2. Its fixed field E' is abelian of type (3, 3) over k, contains N, is unramified over N, and sigma acts on the line St/D by the sign: the SECOND LINE IS INVERTED. So at 3-rank 1 every field carries a type-(3, 3) extension of k containing N and unramified over N, and the regime is the sign of its second line. Predicted consequence the census reads: every field at 3-rank 1 with t = 0 has minus rank of Cl_f(k)_3 at least 2. (8) THE COUNT OF THAT MINUS RANK. Minus parts of the ray class sequence are exact, so rk Cl_f(k)_3^- >= 2 holds when f = 1 and rk_3 Cl(k) >= 2; when f > 1 and rk_3 Cl(k) >= 1 (an unramified character serves, independent of the ramified chi); and when f > 1, rk_3 Cl(k) = 0 and the local minus rank, read one lower at d0 = -3, is at least 2. (9) CONDUCTOR 1. At f = 1 there is no conductor prime, so t = 0, and N/k is unramified. Let 3 | h and take any quotient Cl -> Z/3. The triples' image in the sum-zero triples of Z/3 is an S3-stable subgroup projecting onto each coordinate, so D or St. D gives, by (6), a FIXED unramified character of order 3 of k, impossible since sigma inverts Cl(k). So it is St, and (7) gives E'/k abelian of type (3, 3) and unramified: rk_3 Cl(k) >= 2. Hence f = 1 and 3 | h(K) ==> 3-rank Cl(k) >= 2, at every 3-rank of K. The number of cubic fields of discriminant exactly d0 is (3^r - 1)/2, r = rk_3 Cl(d0) (Hasse), a known count used here as the enumeration's control. PREDICTIONS, fixed before the engine. The population is triple.py's: every complex cubic field with |d_K| <= 50000, class groups certified. C CONTROLS, read before anything else. C1 THE MODULE ENGINE: over a sample of d0 and moduli, the brute unit group's order equals the Euler product read off the splitting types; sigma is multiplicative and an involution, both sampled on the first eight units; the image of x -> x^c, c the order's prime-to-3 part, has the 3-part's size; plus and minus ranks sum to the 3-rank; the ranks over a composite modulus are the sums over its prime powers. C2 THE PINS: d0 = -19, f = 18 (2 and 3 inert): plus 1, minus 2, no usable line. d0 = -11, f = 18 (2 inert, 3 split): plus 1, minus 2, a usable line. C3 HASSE'S COUNT (known): at every fundamental d0 with |d0| <= 10000 the fields with d_K = d0 number (3^r - 1)/2. A THE ADMISSIBLE PRIMES (3). Over all fields: every q = 1 mod 3 dividing f splits in k; every q = 2 mod 3 dividing f is inert; v_3(f) <= 2; v_3(f) = 1 only at 3 | d0. Kill: any violation. B THE LOCAL RANKS AND THE USABLE LINE (4), (5). At every field with f > 1: the plus rank of the 3-part of (O_k/f)^* equals P, and its minus rank equals #{q | f, q != 3} + [9 | f] + [3 exactly divides f] + [9 | f and 3 ramified]; the usable-line predicate, computed from the local modules, equals t >= 1. Kill: any field off in either rank or in usability. The fields where a fixed line exists unusable are printed, and are predicted to be exactly those with 9 | f, 3 not split in k, and no q = 1 mod 3 dividing f. S THE SIGN (6), (7), (8). At 3-rank 1 (fields with at least ten mapped split primes): the triple index is 3 exactly at t = 1 (a re-read of triple.py F2); every field with t = 0 has minus rank of Cl_f(k)_3 at least 2, by (8). Kill: one t = 0 field without. The degenerate fields carrying an inverted line as well are printed, not predicted. The inverted line's USABILITY is automatic by (5)'s local reading wherever sigma-fixed local minus ranks are at most 1, which (4) predicts everywhere but at a ramified 3 with 9 | f; those fields are counted and the first eight named. Z CONDUCTOR 1 (9). Every field with f = 1 and 3 | h has rk_3 Cl(k) >= 2. Kill: one field without. Printed beside it: the f = 1 fields by h, and those with 3 not dividing h by the 3-rank of Cl(k). Z2 THE RANK LAW AT CONDUCTOR 1 (known: Gerth, Acta Arith. 30 (1976) 307-322, Theorem 3.4; added as a control after (9)'s converse was found there). Where N/k is unramified, rk_3 Cl(K) = rk_3 Cl(k) - 1, which holds (9) and its converse at once. Predicted: at every field with f = 1 the class group of cubic.py and the form class group of principal.py read ranks one apart. Kill: one field where they do not; a failure is an engine's, not the theorem's. H THE h = 3 POPULATION TO 13000, re-read under certified class groups: the number of fields and of those with t >= 1. An earlier, uncertified reading of the same population counted 193 fields, 70 with t >= 1, three fields left unresolved; the number here is printed, not predicted. KILLS, as printed observables: a C line off (nothing below is read); an A violation; a B field off; an S field at t = 0 without minus rank 2; a Z field with rk_3 Cl(k) <= 1; a Z2 field off the rank law. FINDINGS. 11/11 checks PASS (9/9 after the audit in RUN RECORD). C C1 0 bad over 81 discriminant-modulus pairs. C2 both pins as derived: (plus, minus, usable) = (1, 2, no) at d0 = -19 and (1, 2, yes) at d0 = -11. C3 Hasse's count exact at all 3043 fundamental d0 with |d0| <= 10000, 1062 fields at f = 1. A 0 violations over 8336 fields. Conductor primes: 272 occurrences of q = 1 mod 3, all split; 1764 of q = 2 mod 3, all inert; q = 3 inert 76, ramified 550, split 78. B Over the 2468 fields with f > 1 the plus and minus ranks match (4) at every field and the usable line is exactly t >= 1. A fixed line exists unusable at 150 fields (3 inert 76, 3 ramified 74), exactly the 150 the shape predicts, 39 of them at 3-rank 1, where t = 0 makes R = M: existence does not sort, usability does. S 1365 fields at 3-rank 1, the index 3^t at every one. All 1018 with t = 0 carry an inverted second line: 408 at f = 1 with rk_3 Cl(k) >= 2, 495 at f > 1 with rk_3 Cl(k) >= 1, 115 from the local part alone. 121 of the 347 fields with t = 1 meet (8)'s sufficient conditions for an inverted line of the ray class group mod f beyond chi's, unramified over N save possibly at the t = 1 fields among the 23 below. 23 fields have a sigma-fixed local minus rank above 1 (a ramified 3 with 9 | f; the least |d_K| among them 11907), 7 of them at t = 1, where the inverted line's usability is not automatic from (5); at t = 0, (7) gives E' there all the same. Z 408 fields with f = 1 and 3 | h (h = 3: 290, 6: 59, 9: 37, 12: 12, 15: 7, 21: 2, 33: 1), every one over a resolvent of 3-rank at least 2. All 5460 fields with f = 1 and 3 not dividing h sit over a resolvent of 3-rank exactly 1. Z2: the ranks (rk_3 Cl(K), rk_3 Cl(k)) read (0, 1) at 5460 fields and (1, 2) at 408, the rank law at all 5868 with f = 1, none off. H 194 fields at h = 3 to 13000, all with ten or more mapped split primes, 70 at t >= 1, 39 at f = 1. Tiers: A a rule, proved at (3) and verified to 50000; B's ranks a rule, proved at (4), verified; (6) and (7) theorems at 3-rank 1, S2 their consequence verified; (9) a theorem, Z its verification, and with its converse a case of Gerth's rank law, which Z2 reads exact. RUN RECORD. 11/11, 5.0 s wall, peak working set 71.9 MB under a memory guard, the population read from triple.py's checkpoint. An audit then made C1 read the 3-Sylow subgroup's size against the 3-part of the order, added C0 (every field certified) and the S filter's dropped count, and printed what follows from other checks rather than checking it: B3 (from B1 and B2), S1 (triple.py F2 restricted to 3-rank 1) and Z (from Z2). (8)'s count at d0 = -3 subtracts one for the cube roots of unity, a lower bound, so S's counts of fields meeting (8) are counts of a sufficient condition. The audit run: 9/9, 5.3 s wall, 68 MB peak commit under a memory guard; B3, S1 and Z print as before (150 of 150, 1365 of 1365, 0 of 408), and a broken power map fails C1 at every one of 15 cases tried in a scratch run. A later read added two prints, the 150's 3-ranks and the 23's count at t = 1: 9/9, 4.7 s, peak commit 68.2 MB, every earlier print unchanged. """ import os os.environ.setdefault("OPENBLAS_NUM_THREADS", "1") import time from math import gcd import principal as PR import triple as T from module_law import check, section, CHECKS HASSE_D = 10000 # C3's range of fundamental discriminants H3_CAP = 13000 # H's population # --------------------------------------------------------------------------- # the ring O_k / m and its unit group as a module under conjugation class Res: """O_k / m for k = Q(sqrt d0), elements a + b w with w^2 = t w + n: w = (1 + sqrt d0)/2 when d0 = 1 mod 4, else sqrt(d0/4).""" def __init__(self, d0, m): self.d0, self.m = d0, m if d0 % 4 == 1: self.t, self.n = 1, (d0 - 1) // 4 else: self.t, self.n = 0, d0 // 4 def mul(self, x, y): (a, b), (c, d) = x, y m = self.m return ((a * c + b * d * self.n) % m, (a * d + b * c + b * d * self.t) % m) def conj(self, x): """sigma(a + b w) = a + b (t - w), since w + sigma(w) = t.""" a, b = x return ((a + b * self.t) % self.m, (-b) % self.m) def norm(self, x): a, b = x return a * a + a * b * self.t - b * b * self.n def one(self): return (1 % self.m, 0) def units(self): m = self.m return [(a, b) for a in range(m) for b in range(m) if gcd(self.norm((a, b)) % m, m) == 1] def pow(self, x, e): r, base = self.one(), x while e: if e & 1: r = self.mul(r, base) base = self.mul(base, base) e >>= 1 return r def splitting(d0, q): if d0 % q == 0: return "ram" if q == 2: return "split" if d0 % 8 == 1 else "inert" return "split" if pow(d0 % q, (q - 1) // 2, q) == 1 else "inert" def prime_powers(n): out, d = [], 2 while d * d <= n: e = 0 while n % d == 0: n //= d e += 1 if e: out.append((d, e)) d += 1 if n > 1: out.append((n, 1)) return out def euler_order(d0, m): num, den = m * m, 1 for q, _ in prime_powers(m): s = splitting(d0, q) if s == "split": num, den = num * (q - 1) ** 2, den * q * q elif s == "inert": num, den = num * (q * q - 1), den * q * q else: num, den = num * (q - 1), den * q assert num % den == 0 return num // den def rank3(R, elems): """The F_3-rank of a 3-group given as its list of elements.""" e = R.one() k = sum(1 for x in elems if R.pow(x, 3) == e) r = 0 while 3 ** r < k: r += 1 assert 3 ** r == k return r def module_full(d0, m): """(|units|, |3-part|, rank, plus rank, minus rank) of the 3-part of (O_k/m)^* under sigma, by brute force.""" R = Res(d0, m) us = R.units() n = len(us) s3, c = 1, n while c % 3 == 0: c //= 3 s3 *= 3 if s3 == 1: return n, 1, 0, 0, 0, 1 syl = sorted({R.pow(x, c) for x in us}) u = (s3 + 1) // 2 # 2u = 1 mod s3 plus, minus = set(), set() for x in syl: sx = R.conj(x) plus.add(R.pow(R.mul(x, sx), u)) minus.add(R.pow(R.mul(x, R.pow(sx, s3 - 1)), u)) return (n, s3, rank3(R, syl), rank3(R, sorted(plus)), rank3(R, sorted(minus)), len(syl)) _MOD = {} def module(d0, m): """(plus rank, minus rank) over m, summed over its prime powers (the CRT step C1 controls), each brute-forced once.""" rp = rm = 0 for q, e in prime_powers(m): key = (d0, q ** e) if key not in _MOD: _MOD[key] = module_full(d0, q ** e) rp += _MOD[key][3] rm += _MOD[key][4] return rp, rm def usable_fixed(d0, f): """(5): a prime of k over a split q | f with nontrivial local 3-part.""" for q, e in prime_powers(f): if splitting(d0, q) == "split": key = (d0, q ** e) if key not in _MOD: _MOD[key] = module_full(d0, q ** e) if _MOD[key][1] > 1: return True return False def fixed_minus_ranks(d0, f): """[(q, e, minus rank)] at the prime powers of f whose prime sigma fixes (inert or ramified in k).""" out = [] for q, e in prime_powers(f): if splitting(d0, q) != "split": module(d0, q ** e) out.append((q, e, _MOD[(d0, q ** e)][4])) return out # --------------------------------------------------------------------------- # the class group of k _R3 = {} def rank3_k(d0): """rk_3 Cl(k), off principal.py's form class group.""" if d0 not in _R3: G = PR.Group(d0) k = sum(1 for i in range(G.h) if G.power(i, 3) == G.e) r = 0 while 3 ** r < k: r += 1 assert 3 ** r == k _R3[d0] = r return _R3[d0] def fundamental(d0): return T.fund_disc(d0) == (d0, 1) # --------------------------------------------------------------------------- # the sections def section_controls(recs): section("C CONTROLS") d0s = [d for d in range(-200, -2) if d % 4 in (0, 1) and fundamental(d)] bad = [0, 0, 0, 0, 0] n = 0 for d0 in d0s[::7]: for m in (2, 3, 4, 5, 6, 7, 9, 14, 18): R = Res(d0, m) us = R.units() bad[0] += len(us) != euler_order(d0, m) e = R.one() for x in us[:8]: for y in us[:8]: bad[1] += R.conj(R.mul(x, y)) != \ R.mul(R.conj(x), R.conj(y)) bad[1] += R.conj(R.conj(x)) != x bad[1] += R.mul(x, R.pow(x, len(us) - 1)) != e _, s3, r, rp, rm, ns = module_full(d0, m) bad[2] += ns != s3 # the image of x -> x^c, its size; # it tests something only at s3 > 1 bad[3] += rp + rm != r n += 1 for m in (14, 18, 36, 45, 63): _, _, _, rp, rm, _ = module_full(d0, m) bad[4] += (rp, rm) != module(d0, m) check("C1 the module engine: order, sigma, 3-Sylow, ranks, CRT", not any(bad), "%d cases; bad %s" % (n, bad)) pins = [(d0, f, module(d0, f) + (usable_fixed(d0, f),), want) for d0, f, want in ((-19, 18, (1, 2, False)), (-11, 18, (1, 2, True)))] bad_pin = [p for p in pins if p[2] != p[3]] check("C2 the pins at f = 18", not bad_pin, "; ".join("d0 = %d: %s" % (p[0], p[2]) for p in pins)) per = {} for r in recs: d0, f = T.fund_disc(r["d"]) if f == 1 and -d0 <= HASSE_D: per[d0] = per.get(d0, 0) + 1 off, nd = [], 0 for d0 in range(-3, -HASSE_D - 1, -1): if d0 % 4 not in (0, 1) or not fundamental(d0): continue nd += 1 want = (3 ** rank3_k(d0) - 1) // 2 if per.get(d0, 0) != want: off.append((d0, per.get(d0, 0), want)) check("C3 Hasse's count at every fundamental |d0| <= %d" % HASSE_D, not off, "%d discriminants, %d fields, off: %s" % (nd, sum(per.values()), off[:5])) return not any(bad) and not bad_pin and not off def section_admissible(recs): section("A THE ADMISSIBLE PRIMES") viol, occ = [], {} for r in recs: d0, f = T.fund_disc(r["d"]) for q, e in prime_powers(f): s = splitting(d0, q) occ[(q % 3 if q != 3 else 3, s)] = \ occ.get((q % 3 if q != 3 else 3, s), 0) + 1 if q != 3 and q % 3 == 1 and s != "split": viol.append((r["d"], q, s)) if q != 3 and q % 3 == 2 and s != "inert": viol.append((r["d"], q, s)) if q == 3 and (e > 2 or (e == 1 and d0 % 3 != 0)): viol.append((r["d"], q, e)) for key in sorted(occ): print(" q %s %-6s %5d occurrences" % ("= 3 " if key[0] == 3 else "= %d mod 3" % key[0], key[1], occ[key])) check("A every conductor prime sits in k as (3) says", not viol, "%d fields, violations %s" % (len(recs), viol[:5])) def section_ranks(recs): section("B THE LOCAL RANKS AND THE USABLE LINE") off, use_off, unusable, ranks = [], [], [], [] nf = 0 t0 = time.time() for r in recs: d0, f = T.fund_disc(r["d"]) if f == 1: continue nf += 1 pp = prime_powers(f) n1 = sum(1 for q, _ in pp if q != 3 and q % 3 == 1) other = sum(1 for q, _ in pp if q != 3) a = dict(pp).get(3, 0) ram3 = d0 % 3 == 0 P = n1 + (a == 2) Mi = other + (a == 2) + (a == 1) + (a == 2 and ram3) got = module(d0, f) if got != (P, Mi): off.append((r["d"], d0, f, got, (P, Mi))) t = T.lines(d0, f) u = usable_fixed(d0, f) if u != (t >= 1): use_off.append((r["d"], d0, f, u, t)) if got[0] > 0 and not u: unusable.append((r["d"], d0, f, splitting(d0, 3))) ranks.append(r["r"]) print(" %d fields with f > 1, %.1f s" % (nf, time.time() - t0)) check("B1 plus rank P and minus rank as (4) says, at every field", not off, "off at %d: %s" % (len(off), off[:5])) check("B2 a usable fixed line exactly where t >= 1", not use_off, "off at %d: %s" % (len(use_off), use_off[:5])) pred = unusable shape = all(dict(prime_powers(f)).get(3, 0) == 2 and splitting(d0, 3) != "split" and not any(q % 3 == 1 for q, _ in prime_powers(f) if q != 3) for (_, d0, f, _) in unusable) n_pred = 0 for r in recs: d0, f = T.fund_disc(r["d"]) if f % 9 == 0 and splitting(d0, 3) != "split" and \ not any(q % 3 == 1 for q, _ in prime_powers(f) if q != 3): n_pred += 1 print(" a fixed line exists unusable at %d fields (3 inert: %d, " "3 ramified: %d); the shape predicts %d" % (len(pred), sum(1 for x in pred if x[3] == "inert"), sum(1 for x in pred if x[3] == "ram"), n_pred)) for x in pred[:12]: print(" d_K = %-7d d0 = %-6d f = %-4d 3 %s" % x) print(" of them, by 3-rank of Cl(K): %s" % sorted((k_, ranks.count(k_)) for k_ in set(ranks))) # B3 follows from B1 and B2, so it prints and is not checked print(" B3 (from B1, B2): the unusable fixed lines are the 9 | f, 3 not " "split cases: %s, %d printed, %d predicted" % (shape and len(pred) == n_pred, len(pred), n_pred)) def inverted_rank(d0, f): """rk Cl_f(k)_3^- is at least 2, by (8).""" r3 = rank3_k(d0) if f == 1: return r3 >= 2 loc = module(d0, f)[1] - (1 if d0 == -3 else 0) return r3 >= 1 or loc >= 2 def section_sign(recs): section("S THE SIGN OF THE SECOND LINE, AT 3-RANK 1") rows, thin = [], 0 for r in recs: if r["r"] != 1: continue if len(r["triples"]) < T.MIN_SPLIT: thin += 1 continue d0, f = T.fund_disc(r["d"]) rows.append((r["d"], d0, f, r["t"], T.triple_index(r), inverted_rank(d0, f), any(m > 1 for _, _, m in fixed_minus_ranks(d0, f)))) idx_off = [x for x in rows if x[4] != 3 ** x[3]] # triple.py's F2 at 3-rank 1, so a print print(" S1 (triple.py F2): the triple index is 3^t at %d of %d " "3-rank 1 fields; %d fields with fewer than %d mapped split " "primes left out" % (len(rows) - len(idx_off), len(rows), thin, T.MIN_SPLIT)) uni = [x for x in rows if x[3] == 0] deg = [x for x in rows if x[3] >= 1] miss = [x for x in uni if not x[5]] check("S2 every t = 0 field has an inverted second line (rank >= 2)", not miss, "%d uniform fields, without: %s" % (len(uni), [x[0] for x in miss][:8])) src = {"f = 1, rk_3 Cl(k) >= 2": 0, "f > 1, rk_3 Cl(k) >= 1": 0, "f > 1, local alone": 0} for x in uni: if x[2] == 1: src["f = 1, rk_3 Cl(k) >= 2"] += 1 elif rank3_k(x[1]) >= 1: src["f > 1, rk_3 Cl(k) >= 1"] += 1 else: src["f > 1, local alone"] += 1 for k_, v in src.items(): print(" uniform, %-26s %5d" % (k_, v)) both = sum(1 for x in deg if x[5]) print(" degenerate fields: %d, of which %d carry an inverted line " "too" % (len(deg), both)) wild = [x[0] for x in rows if x[6]] print(" fields with a sigma-fixed local minus rank above 1 (the " "inverted line's usability not automatic): %d %s" % (len(wild), wild[:8])) print(" of them at t = 1: %d" % sum(1 for x in rows if x[6] and x[3] == 1)) return rows def section_corner(recs): section("Z CONDUCTOR 1") byh, miss, off3 = {}, [], {} for r in recs: d0, f = T.fund_disc(r["d"]) if f != 1: continue if r["h"] % 3 == 0: byh[r["h"]] = byh.get(r["h"], 0) + 1 if rank3_k(d0) < 2: miss.append((r["d"], r["h"], rank3_k(d0))) else: k_ = rank3_k(d0) off3[k_] = off3.get(k_, 0) + 1 print(" f = 1 and 3 | h, by h: %s" % sorted(byh.items())) print(" f = 1 and 3 !| h, by rk_3 Cl(k): %s" % sorted(off3.items())) # Z follows from Z2 (3 | h is rk_3 Cl(K) >= 1), so a print print(" Z (from Z2): f = 1 fields with 3 | h and rk_3 Cl(k) < 2: %d of " "%d %s" % (len(miss), sum(byh.values()), miss[:8])) joint, off = {}, [] for r in recs: d0, f = T.fund_disc(r["d"]) if f != 1: continue rK = sum(1 for n in r["inv"] if n % 3 == 0) rk = rank3_k(d0) joint[(rK, rk)] = joint.get((rK, rk), 0) + 1 if rK != rk - 1: off.append((r["d"], tuple(r["inv"]), rk)) print(" f = 1, (rk_3 Cl(K), rk_3 Cl(k)): %s" % sorted(joint.items())) check("Z2 rk_3 Cl(K) = rk_3 Cl(k) - 1 at every f = 1 field (Gerth)", not off, "%d fields, off: %s" % (sum(joint.values()), off[:8])) def section_h3(recs): section("H THE h = 3 POPULATION TO %d" % H3_CAP) h3 = [r for r in recs if r["h"] == 3 and -r["d"] <= H3_CAP] rd = [r for r in h3 if len(r["triples"]) >= T.MIN_SPLIT] print(" %d fields at h = 3, %d with %d or more mapped split primes, " "%d at t >= 1, %d at f = 1" % (len(h3), len(rd), T.MIN_SPLIT, sum(1 for r in rd if r["t"] >= 1), sum(1 for r in rd if T.fund_disc(r["d"])[1] == 1))) def main(): t0 = time.time() section("P THE POPULATION") data = T.population() recs = [r for r in data["recs"] if r["ok"]] check("C0 every field of the population certified", len(recs) == len(data["recs"]), "%d of %d" % (len(recs), len(data["recs"]))) if CHECKS[-1] and section_controls(recs): section_admissible(recs) section_ranks(recs) section_sign(recs) section_corner(recs) section_h3(recs) print() print("%d/%d checks PASS, %.1f s" % (sum(CHECKS), len(CHECKS), time.time() - t0)) if not all(CHECKS): raise SystemExit(1) if __name__ == "__main__": main()