tower.py

Checks: THERMAL: The 2-power tower raises the sill theorem; rule.

Cited on: THERMAL.

To run it: save tower.py, with fieldtop.py, growth.py, memory.py, sillclass.py, spectrum.py, splitend.py, splitloss.py, splitsharp.py, typeloss.py beside it; needs mpmath, numpy, scipy.

"""tower.py -- the sill's 2-power tower: with Q(zeta_(2^k)) in K, K_ab =
Q(zeta_(2^k)), 2 totally ramified (type (1, 0)), does the F2 place
lose, and from which degree; and, beside it, Q(i) in K at d = 12 with
the law's algebras fixed to Y = 10^8.

QUESTION. At k = 3 (Q(zeta_8) in K, d = 0 mod 4), where is the bound
positive (no field loses), and from which d does a field that loses
exist? Is there one formula in k? At k = 2, d = 12: does a field lose?

THE DERIVATION (by hand, before this code). Let C_k = Q(zeta_(2^k)), of
degree D = 2^(k-1), k >= 2, inside K, of degree d = D m', K_ab = C_k.
  (1) The class. f(K_ab) = 2^k, N = lcm(4, 2^k) = 2^k, G_N = {1}, m(1)
  = 2^k: L* = L(2^k) (sillclass's class minimum).
  (2) The odd places. Every odd residue field holds zeta_(2^k), so every
  odd norm is 1 mod 2^k. An odd R counts in L(2^k) iff N(R) - 1 | 2^k,
  so N(R) = 2^k + 1 =: n_c, which must be a prime power: a Fermat prime
  (k = 2, 4, 8, 16; none known past) or 9 (k = 3, Mihailescu); not at k = 5
  (33). Depth 1: lambda(R^2) = (n_c - 1) p, p odd, does not divide 2^k.
  The F2 column holds the odd places with N - 1 a power of 2 once (the
  losses section's proof): here the Fermat norms n_c and above.
  (3) The place over 2, e = d, residue F2. For v(z) = j >= 1, (1 +
  z)^2 - 1 = 2z + z^2 has valuation 2j while 2j < e + j, i.e. j < e:
  the valuations of (1 + pi)^(2^s) - 1 run 1, 2, 4, ..., 2^s while
  2^(s-1) < e. Every 1-unit u has v(u^(2^s) - 1) >= min(2^s, ...) >=
  2^s by the same step. So lambda(P^j) = 2^ceil(log2 j) for j <= 2^k
  when 2^(k-1) < e, and lambda(P^(2^k + 1)) = 2^(k+1): the place over 2
  is held to depth exactly 2^k. e = d > 2^(k-1) = D excludes K = C_k
  only (d = D m', m' >= 2).
  (4) So L(2^k) = P_k S_c, P_k = 1 + x + ... + x^(2^k) = (1 -
  x^(2^k+1))/(1 - x), S_c over the places of norm n_c; sigma_F2 = S_c
  W_k, W_k over the Fermat norms above n_c; log(1 + margin) = M = l -
  l_c - w_k, l_c = log(1/(1 - x^(2^k + 1))). (At k = 2: l5, W_2 the
  norms 9, 17, 257, 65537: sillclass's QiLaw.) The places of norm n_c
  cancel in M.
  (5) The bound. (1 - x)M' = 1 - (2^k + 1)x^(2^k)/P_k - the W_k terms;
  x^(2^k)/P_k increases on (0, 1) (the k = 2 algebra: x^(q-1) P_k' <
  (q - 1) x^(q-2) P_k termwise), the W_k terms increase on (0, 1/2]
  (D(x, a) > 0 at a >= log2 9), so M rises, then falls once, on [0,
  x_c], x_c <= 1/2 the root of l + w + log x = 0, w = w_c + w_k. At
  x_c, M = h_c(x_c) + n_c-count log(1 + n_c^-b), h_c = 2 l - l_c + log
  x, increasing ((2^k + 1) x^(2^k) <= P_k). At a fixed count of n_c both
  terms rise with x_c, which falls with the other counts, so the bound
  takes those maximal (d each: a Fermat prime above n_c is 1 mod 2^k,
  split completely in C_k) and scans the count of n_c from 0 to its cap:
  d, but d/2 at k = 3 (3 has two primes of norm 9 in Z[zeta_8], each
  with at most m' places of degree 1 over it).
  (6) The certificate's law (arm Z, the TRANSPLANT from Q(i), where the
  bound's argmin had no place of the cancelling norm): over the primes
  of Z[zeta_(2^k)] below Y = 10^5, the Fermat norms above n_c split
  completely; every other prime of Z[zeta_(2^k)] of residue degree m'
  over each prime above it (a rational p of order f mod 2^k gives D/f
  places of norm p^(f m')), n_c among them; at 2, Q_2(zeta_(2^k))(pi^
  (1/m')), e = d; three primes of C_k above Y for S_(m') at m' >= 3,
  and at m' = 2 one prime split and a conjugate inert, so K is not
  Galois over Q; either way K_ab = C_k (an abelian subfield F gives F
  C_k between C_k and K, abelian over Q, so C_k). Its certificate is
  splitsharp's: M at x_low, the root of the increasing bound g_up on the
  point's equation, with M' < 0 there and x_c < 1/2.
  (7) Hand sizes. h_c(x) = 0 near x = (3 - sqrt 5)/2 = 0.382 (2 l + log
  x = 0 at (1 - x)^2 = x, l_c ~ x^9 a shift of 3e-5). At the count 0,
  x_c >= 0.382 iff w_k(0.382) <= l(0.382) = 0.48121: per unit of d,
  w_3(0.382) = 0.019832 (17: 0.01938; 257: 4.5e-4), so d <= 24.26 at k
  = 3; w_4(0.382) = 4.513e-4 (257, 65537), d <= 1066 at k = 4.

PREDICTIONS (frozen before the engine).
  P1 at k = 3 the bound's argmin over the count of 9 is 0 at every d
     scanned (d = 8..200, d = 0 mod 4).
  P2 at k = 3 the bound is positive at every d = 0 mod 4 from 8 to 24
     and at most 0 at every one from 28 to 200.
  P3 at k = 3 the arm-Z certificate (C < 0, M' < 0, x_c < 1/2) holds
     at every d = 0 mod 4 from a least D3 in {28, 32, 36} to 1000, and
     at no d where the bound is positive.
  P4 at k = 4 (d = 0 mod 8, from 16) the bound at count 0 is positive
     at every d to 1064 and at most 0 from 1072 to 1200; the arm-Z
     certificate holds from a least D4 in [1072, 1200] to 1600.
  P5 at k = 2, d = 12, arm B with its algebras fixed to Y = 10^8 (the
     places between 10^5 and Y of norm p^6 or p^12, bounded): the
     certificate is in [-0.009, -0.006], M' < 0, x_c < 1/2, so a field
     containing Q(i) loses at d = 12. (A rough probe that dropped those
     places printed -0.0073 at 10^8, +0.0020 at 10^7.)
  KILL of the tower reading: P2's band missed by more than one step of
  4 at either end, or P3 failing (a d past D3 to 1000 uncertified).

CONTROLS (frozen).
  C1 POSITIVE: the engine at k = 2 reproduces sillclass.bound_i(d) at d
     = 4, 12, 20, 100 and QiLaw(qi_law(d, no5=True)).cert() at d = 12,
     14, 100, 1000, each to 1e-12.
  C2 the chain over 2 by brute force (U^1/U^j generated by 1 + t^i, i
     < j, its exponent the largest of their orders) in Z_2[t]/(t^e - 2):
     at e = 8 and 12, lambda(P^j) | 8 exactly for j <= 8 (1, 2, 4, 4, 8,
     8, 8, 8, then 16); at e = 16, | 16 exactly for j <= 16. At K = C_3
     itself (e = 4, pi = 1 - zeta_8) the chain is printed, an
     observation.
  C3 the new M against its products at k = 3, d = 24, x = 0.3 (to
     1e-12), its derivative against a central difference (1e-6), and
     the bound at counts 0 and d/2 against M(x_c) on the law of exactly
     those counts (1e-12).
  C4 (P5's own control; its first half by construction, BigLaw.mid
     being 0 at Y = 10^5, so it reads the wiring) the Y = 10^8 law at
     Y = 10^5 reproduces arm B's
     certificate +0.041683 at d = 12 (1e-12), and its bound on the
     places between 10^5 and 10^6 is at least their brute sum at x_low.
  C5 (added after the run with BigLaw's D, ARM Y's own control; its
     first half by construction, as C4's) the Y law at k = 3, D =
     4: at Y = 10^5 it is arm Z to 1e-12, and at d = 28 its bound on the
     places between 10^5 and 10^6 is at least their brute sum at x_low.

FINDINGS (entered after the run, from its printed output).
  CONTROLS. C1: difference 0.0 on the bound and the certificate. C2: e =
  8 and 12 read 1, 2, 4, 4, 8, 8, 8, 8, 16; e = 16 reads 2^ceil(log2 j)
  through 32 at j = 17; Z_2 reads 1, 2, 2, 4, 8, 16. At K = Q(zeta_8)
  itself (e = 4, outside the derivation's e > 4) the chain reads 1, 2,
  4, 4, 8 at j = 5..14, then 16: 8 held to depth 14, an observation. C3:
  0.0, 1.7e-11, 6.7e-16. C4: 0.0; 3.053e-39 >= 1.289e-40. C5: 0.0;
  5.686e-46 >= 1.174e-47.
  P1 HOLDS: the bound's argmin over the count of 9 is 0 at every d = 8..
  200 at k = 3. P2 HOLDS: positive at 8..24 (+0.299509 at 8, +0.003126
  at 24), at most 0 from 28 (-0.043153) to 1000 (-1.164273, count 0
  past 200). P3 HOLDS: the arm-Z certificate at Y = 10^5 is negative
  with M' < 0, x_c < 1/2 at every d = 0 mod 4 from D3 = 36 (-0.013356)
  to 1000 (-1.156163), positive at 28 (+0.061523) and 32 (+0.022713),
  and nowhere the bound is positive.
  P4 FAILS as frozen. Its bound half holds: at count 0 the bound is
  positive at every d = 0 mod 8 to 1064 (+0.000656) and at most 0 from
  1072 (-0.000818) to 1200; with the full scan (run to 1080, past the
  first run's 200), positive at 16..1064, its argmin n = d at every
  printed d to 200 and 0 at every printed d from 400 to 1080. The
  certificate half fails: at Y = 10^5 no certificate is negative to 1600
  (+0.286780 at 1072, +0.259753 at 1600, x_low near 0.30 against x_c
  near 0.38): the tail allowance past Y prints 0.3024 at d = 1600 (x_low
  0.29368, b 1.768), the hand sizes having priced w_k and not the
  tail.
  P5 HOLDS: at Y = 10^8 the d = 12 certificate is -0.007295 (M' -2.970,
  x_c 0.37817); +0.041683, +0.017877, +0.002006 at 10^5, 10^6, 10^7,
  -0.012014 at 10^9. So a field containing Q(i), type (1, 0), loses at
  d = 12.
  KILL MISSED.
  ARM Y (added after the run, the P4 failure's design variable): the
  arm-Z algebras fixed to Y = 10^30, the places between 10^5 and Y
  bounded (C5), the certificate negative with M' < 0 and x_c < 1/2 at
  every degree read from the bound's first degree at most 0 (at k = 2
  sillclass's bound_i, -0.029970 at 12, +0.027465 at 10; x_c < 1/2
  holds by the bracket, brentq raising otherwise): k = 2,
  every even d from 12 (-0.015555) to 1000 (-1.537829); k = 3, every d
  = 0 mod 4 from 28 (-0.033743) to 1000 (-1.156851), +0.011781 at 24;
  k = 4, every d = 0 mod 8 from 1072 (-0.000483) to 2000 (-0.123796),
  +0.000991 at 1064. So at each k read the losing degrees are exactly
  those where the bound is at most 0: with Q(zeta_(2^k)) in K, K_ab =
  Q(zeta_(2^k)), type (1, 0), excluding K = Q(zeta_(2^k)) itself (e =
  2^(k - 1), outside the depth step), no field loses at d < D(k) and
  one loses
  at each d from D(k) to the read's end, D(2) = 12 (to 1000, and every
  even d past 928 by sillclass's lemma), D(3) = 28 (to 1000), D(4) =
  1072 (to 2000). D(k) is the first d = 0 mod D past d* =
  l(x0)/u_k(x0), x0 = (3 - sqrt 5)/2, u_k the W_k terms of w per unit
  of d (9 at weight 1/2 at k = 2): d* = 11.20, 24.27, 1067.55 (arm_y's
  print, added after the run; (7) gave 24.26 and 1066 from rounded
  terms), read at three k, a pattern. The bound at count 0 turns where
  x_c crosses h_c's root, x0 shifted by l_c, and x_c = x0 where d u_k(x0)
  = l(x0): the pattern's content is that neither the shift nor the scan
  over the count of n_c moves the turn across an admissible degree, and
  that the certificate turns there too.

RUN RECORD. `python tower.py` under a 2048 MB cap: 6 of 6 checks, 6.7
s, peak commit 62.4 MB. The first run (5 of 5 checks, 2.1 s, 61.9 MB)
had k = 4's full scan to 200 and no ARM Y; P1-P5 and the kill read the
same in both. At review: the tail print added, ARM Y's k = 2 start
computed from bound_i (it was 12 by hand), dead names cut; every other
line printed as before, 6 of 6 checks, 6.9 s, 62.9 MB. The engine's
first draft set t = 0 in the e = 1 brute (fixed before any control
read, the Z_2 row added as its check). Moved in from the lab with two
references rewritten and the d* print added (dstar, read in arm_y); every
other line printed as before, 6 of 6 checks, 6.9 s.
"""

import math
import os
import sys
import time

HERE = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, os.path.join(HERE, "..", "code"))
os.environ.setdefault("OPENBLAS_NUM_THREADS", "1")

import numpy as np  # noqa: E402
from scipy.optimize import brentq  # noqa: E402

import sillclass as sc  # noqa: E402
import splitend as se  # noqa: E402
import splitloss as sl  # noqa: E402
import splitsharp as ss  # noqa: E402
import typeloss as tl  # noqa: E402

CHECKS = []
Y = ss.Y
YBIG = 10 ** 30
PR_Y = [int(p) for p in ss.PR_Y.tolist() if p > 2]


def check(name, ok, detail=""):
    CHECKS.append(bool(ok))
    print(f"  [{'PASS' if ok else 'FAIL'}] {name}" + (f"  ({detail})"
                                                   if detail else ""))


def guard():
    if not all(CHECKS):
        print("a control failed: no verdict")
        sys.exit(1)


# ------------------------------------------------------------- the tower

def nc(k):
    return 2 ** k + 1


def wk(k):
    """The F2 column's norms above n_c, 1 mod 2^k (Fermat numbers counted
    as prime, a bound, as splitend's NORMS)."""
    return [n for n in se.NORMS if n > nc(k) and (n - 1) % 2 ** k == 0]


def caps(k, d):
    """The worst counts: the norms of W_k and the cap on n_c's."""
    w = [(n, d // 2 if n == 9 else d) for n in wk(k)]
    return w, (d // 2 if k == 3 else d)


def lc(k, x):
    return -math.log1p(-x ** nc(k))


def hc(k, x):
    return 2 * se.lf(x) - lc(k, x) + math.log(x)


def bound_at(k, d, n):
    """(M(x_c), n, x_c) at n places of norm n_c, the others maximal."""
    w, _ = caps(k, d)
    pairs = [(math.log2(m), c) for m, c in w] + [(math.log2(nc(k)), n)]

    def g(x):
        lx = math.log(x)
        return (se.lf(x) + lx + sum(c * math.log1p(math.exp(a * lx))
                                    for a, c in pairs if c))
    xc = brentq(g, 1e-14, 0.5, xtol=1e-18, rtol=1e-15, maxiter=500)
    return hc(k, xc) + n * math.log1p(xc ** math.log2(nc(k))), n, xc


def bound(k, d, full=True):
    if not full:
        return bound_at(k, d, 0)
    return min((bound_at(k, d, n) for n in range(caps(k, d)[1] + 1)),
               key=lambda r: r[0])


def order_mod(p, q):
    f, r = 1, p % q
    while r != 1:
        r = r * p % q
        f += 1
    return f


def law(k, d):
    """Arm Z: over the primes of Z[zeta_(2^k)] below Y, those of norm in
    W_k split completely, every other of residue degree m' = d/D."""
    D = 2 ** (k - 1)
    mp = d // D
    W = set(wk(k))
    out = []
    for p in PR_Y:
        f = order_mod(p, 2 ** k)
        q = p ** f
        if q in W:
            out.append((math.log2(q), d // f, 1))
        else:
            out.append((f * mp * math.log2(p), D // f, 0))
    return ss.Law(d, out)


class TowerLaw(tl.TypeLaw):
    """Type (1, 0), M = l - l_c - (w - w_c)."""

    def __init__(self, k, base):
        super().__init__(1, 0, [], base)
        self.k = k
        self.ac = math.log2(nc(k))

    def wc(self, x):
        B = self.base
        sel = (B.k == 1) & (np.abs(B.a - self.ac) < 1e-12)
        return float((B.c[sel] * np.log1p(x ** B.a[sel])).sum())

    def m(self, x):
        return se.lf(x) - lc(self.k, x) - (self.w(x) - self.wc(x))

    def dm(self, x):
        B = self.base
        sel = (B.k == 1) & (np.abs(B.a - self.ac) > 1e-12)
        aa = B.a[sel]
        dw = float((B.c[sel] * aa * x ** (aa - 1) / (1 + x ** aa)).sum())
        q = nc(self.k)
        return 1 / (1 - x) - q * x ** (q - 1) / (1 - x ** q) - dw


def certify(k, d):
    C, dm, xl, xc = TowerLaw(k, law(k, d)).cert()
    return C, dm, xl, xc, (C < 0 and dm < 0 and xc < 0.5)


# ------------------------------------------------------- the chain over 2

def chain(E, jmax):
    """lambda(P^j), j = 1..jmax, in Z_2[t]/(E), E Eisenstein given by its
    lower coefficients (t^e = -sum E_i t^i): the exponent of U^1/U^j,
    generated by 1 + t^i, i < j."""
    e = len(E)

    def mul(a, b, mod):
        r = [0] * (2 * e)
        for i, ai in enumerate(a):
            if ai:
                for j, bj in enumerate(b):
                    r[i + j] += ai * bj
        for i in range(2 * e - 1, e - 1, -1):
            c = r[i]
            if c:
                r[i] = 0
                for j, Ej in enumerate(E):
                    r[i - e + j] -= c * Ej
        return [c % mod for c in r[:e]]

    def val(a, mod):
        v = math.inf
        for i, c in enumerate(a):
            if c % mod:
                v = min(v, e * ((c & -c).bit_length() - 1) + i)
        return v

    out = []
    for j in range(1, jmax + 1):
        mod = 2 ** (-(-j // e) + 2)
        lam = 1
        for i in range(1, j):
            u = [0] * e
            u[0] = 1
            ti = [0] * e
            ti[0] = 1
            for _ in range(i):
                ti = mul(ti, [0, 1] + [0] * (e - 2) if e > 1
                         else [-E[0] % mod], mod)
            u = [(a + b) % mod for a, b in zip(u, ti)]
            o = 1
            while True:
                z = list(u)
                z[0] -= 1
                if val(z, mod) >= j:
                    break
                u = mul(u, u, mod)
                o *= 2
            lam = max(lam, o)
        out.append(lam)
    return out


# ----------------------------------------------------- Q(i) at d = 12, Y

class BigLaw(ss.Law):
    """A law's places below 10^5 (base); those between 10^5 and Yc
    bounded: residue degree m' = d/D over each prime of C_k there gives at
    most D places per prime, each of norm at least p^(m'); d places per
    prime past Yc (the tail). At k = 2 (D = 2): arm B's algebras fixed to
    Yc."""

    def __init__(self, base, Yc, D=2):
        self.d, self.Yc, self.tail, self.D = base.d, Yc, True, D
        self.a, self.c, self.k = base.a, base.c, base.k

    def mid(self, x):
        if self.Yc <= Y:
            return 0.0
        s = -math.log2(x) * (self.d // self.D)
        N = Y
        return self.D * (N ** -s + N ** (1 - s) / (s - 1)) / (1 - N ** -s)

    def parts(self, x):
        lz, sw, w, lt = super().parts(x)
        return lz, sw, w, lt + self.mid(x)


# ------------------------------------------------------------------ main

def controls():
    print("=== CONTROLS")
    e1 = max(abs(bound(2, d)[0] - sc.bound_i(d)[0]) for d in (4, 12, 20, 100))
    e2 = 0.0
    for d in (12, 14, 100, 1000):
        a = TowerLaw(2, law(2, d)).cert()
        b = sc.QiLaw(sc.qi_law(d, no5=True)).cert()
        e2 = max(e2, max(abs(u - v) for u, v in zip(a, b)))
    check("C1 POSITIVE: k = 2 is sillclass's bound_i and arm B", e1 < 1e-12
          and e2 < 1e-12, f"{e1:.1e}, {e2:.1e}")
    want8 = [1, 2, 4, 4, 8, 8, 8, 8, 16]
    c8, c12 = chain([-2] + [0] * 7, 9), chain([-2] + [0] * 11, 9)
    c16 = chain([-2] + [0] * 15, 17)
    z2 = chain([-2], 6)
    ok16 = all(c16[j - 1] == 2 ** math.ceil(math.log2(j)) for j in
               range(1, 18))
    check("C2 the brute chain: e = 8, 12 read 1, 2, 4, 4, 8, 8, 8, 8, 16; "
          "e = 16 reads 2^ceil(log2 j) to j = 17", c8 == want8 and
          c12 == want8 and ok16, f"e = 8 {c8}; e = 16 {c16}")
    check("C2 the brute at Z_2 reads (Z/2^j)^*'s exponent", z2 ==
          [1, 2, 2, 4, 8, 16], f"{z2}")
    print(f"  K = Q(zeta_8) itself (e = 4, pi = 1 - zeta_8): "
          f"{chain([2, -4, 6, -4], 17)}")
    d, x = 24, 0.3
    base = law(3, d)
    base = ss.Law(d, list(zip(base.a, base.c, base.k)) +
                  [(math.log2(9), 5, 1)])
    q = TowerLaw(3, base)
    b = -math.log2(x)
    lnum = (math.log(sum(x ** i for i in range(9)))
            + 5 * math.log1p(9 ** -b))
    lden = sum(c * math.log1p(2 ** (-b * a)) for a, c, kk in
               zip(base.a, base.c, base.k) if kk == 1)
    e1 = abs(q.m(x) - (lnum - lden))
    hh = 1e-6
    e2 = abs(q.dm(x) - (q.m(x + hh) - q.m(x - hh)) / (2 * hh))
    e3 = 0.0
    for n in (0, d // 2):
        v, _, xc = bound_at(3, d, n)
        w, _ = caps(3, d)
        W = [(math.log2(m), c, 1) for m, c in w] + [(math.log2(9), n, 1)]
        e3 = max(e3, abs(v - TowerLaw(3, ss.Law(d, W, tail=False)).m(xc)))
    check("C3 k = 3: M = log(L(8)/sigma_F2), M' its derivative, the bound "
          "= M(x_c)", e1 < 1e-12 and e2 < 1e-6 and e3 < 1e-12,
          f"{e1:.1e}, {e2:.1e}, {e3:.1e}")
    base = sc.qi_law(12, no5=True)
    a = sc.QiLaw(BigLaw(base, Y)).cert()
    b = sc.QiLaw(base).cert()
    e4 = max(abs(u - v) for u, v in zip(a, b))
    xl = sc.QiLaw(BigLaw(base, 10 ** 8)).cert()[2]
    pr = sl.primes_to(10 ** 6)
    pr = pr[pr > Y].tolist()
    bb = -math.log2(xl)
    brute = sum(2 * -math.log1p(-float(p) ** (-6 * bb)) if p % 4 == 1 else
                -math.log1p(-float(p) ** (-12 * bb)) for p in pr)
    mid = BigLaw(base, 10 ** 8).mid(xl)
    check("C4 the Y law at Y = 10^5 is arm B; its mid bound >= the places "
          "10^5..10^6 at x_low", e4 < 1e-12 and mid >= brute,
          f"{e4:.1e}; {mid:.3e} >= {brute:.3e}")
    d = 28
    q = TowerLaw(3, BigLaw(law(3, d), YBIG, 4))
    xl = q.cert()[2]
    bb = -math.log2(xl)
    brute = 0.0
    for p in pr:
        f = order_mod(p, 8)
        brute += (4 // f) * -math.log1p(-float(p) ** (-f * (d // 4) * bb))
    mid = q.base.mid(xl)
    e5 = max(abs(u - v) for u, v in zip(
        TowerLaw(3, BigLaw(law(3, d), Y, 4)).cert(),
        TowerLaw(3, law(3, d)).cert()))
    check("C5 (added after the run) the Y law at k = 3: at Y = 10^5 it is "
          "arm Z; its mid bound >= the places 10^5..10^6 at x_low, d = 28",
          e5 < 1e-12 and mid >= brute, f"{e5:.1e}; {mid:.3e} >= {brute:.3e}")
    guard()


def section_tower(k, dmax, dfull, step):
    print(f"\n=== k = {k}: Q(zeta_{2 ** k}) in K, d = 0 mod {step}")
    d0 = 2 * step
    rows = {}
    for d in range(d0, dmax + 1, step):
        full = d <= dfull
        bd = bound(k, d, full)
        rows[d] = [bd, full, None]
    pos = [d for d in rows if rows[d][0][0] > 0]
    neg = [d for d in rows if rows[d][0][0] <= 0]
    print(f"  bound (full scan to {dfull}, count 0 past it): positive at "
          f"{min(pos) if pos else None}..{max(pos) if pos else None}, "
          f"at most 0 from {min(neg) if neg else None}; "
          f"monotone: {not neg or not pos or max(pos) < min(neg)}")
    nz = [(d, rows[d][0][1]) for d in rows if rows[d][1]
          and rows[d][0][1] != 0]
    print(f"  argmin count of n_c = {nc(k)}: 0 at every fully scanned d: "
          f"{not nz}" + ("" if not nz else f"; else at {nz[:8]}"))
    return rows, pos, neg


def certs(k, rows, dmax, step):
    out = []
    for d in range(2 * step, dmax + 1, step):
        C, dm, xl, xc, ok = certify(k, d)
        if d in rows:
            rows[d][2] = (C, dm, xl, xc, ok)
        out.append((d, C, dm, xl, xc, ok))
    return out


def main():
    t0 = time.time()
    controls()
    out = {}
    for k, dmax, dfull, cmax, step in ((3, 1000, 200, 1000, 4),
                                       (4, 1200, 1080, 1600, 8)):
        rows, pos, neg = section_tower(k, dmax, dfull, step)
        cs = certs(k, rows, cmax, step)
        good = [d for d, C, dm, xl, xc, ok in cs if ok]
        dh = min(good) if good else None
        tail = dh is not None and all(ok for d, C, dm, xl, xc, ok in cs
                                      if d >= dh)
        clash = [d for d in pos if d in good]
        for d, C, dm, xl, xc, ok in cs:
            if (d <= 64 or d % 200 == 0 or (dh and abs(d - dh) <= 2 * step)
                    or (pos and abs(d - max(pos)) <= 2 * step)):
                bd = rows.get(d, [None])[0]
                bs = (f"  bound {bd[0]:+.6f} (n {bd[1]}, x_c {bd[2]:.5f})"
                      if bd else "")
                print(f"  d={d:5d}: certificate {C:+.6f} (M' {dm:+.3f}, "
                      f"x_low {xl:.5f}, x_c {xc:.5f}){bs}")
        print(f"  least certified d {dh}; every d from it to {cmax} "
              f"certified: {tail}; a certificate where the bound is "
              f"positive: {clash}")
        out[k] = (pos, neg, dh, tail, clash, rows)
        d, C, dm, xl, xc, ok = cs[-1]
        b = -math.log2(xl)
        lt = d * (Y ** -b + Y ** (1 - b) / (b - 1)) / (1 - Y ** -b)
        print(f"  the tail past Y = 10^5 at d = {d}, x_low {xl:.5f} (b "
              f"{b:.3f}): d (Y^-b + Y^(1-b)/(b - 1))/(1 - Y^-b) = {lt:.4f}")
    print("\n=== Q(i), d = 12, arm B's algebras fixed to Y")
    base = sc.qi_law(12, no5=True)
    c12 = {}
    for e in (5, 6, 7, 8, 9):
        C, dm, xl, xc = sc.QiLaw(BigLaw(base, 10 ** e)).cert()
        c12[e] = (C, dm, xl, xc)
        print(f"  Y = 10^{e}: certificate {C:+.6f} (M' {dm:+.3f}, x_low "
              f"{xl:.5f}, x_c {xc:.5f})")
    pos3, neg3, dh3, tail3, clash3, rows3 = out[3]
    p1 = all(rows3[d][0][1] == 0 for d in rows3 if rows3[d][1])
    p2 = (pos3 == [d for d in range(8, 25, 4)]
          and all(d in neg3 for d in range(28, 201, 4)))
    p3 = dh3 in (28, 32, 36) and tail3 and not clash3
    pos4, neg4, dh4, tail4, clash4, rows4 = out[4]
    b0 = {d: bound_at(4, d, 0)[0] for d in range(16, 1201, 8)}
    p4 = (all(b0[d] > 0 for d in b0 if d <= 1064)
          and all(b0[d] <= 0 for d in b0 if d >= 1072)
          and dh4 is not None and 1072 <= dh4 <= 1200 and tail4)
    lo = max([d for d in b0 if b0[d] > 0], default=None)
    print(f"\n  k = 4, count 0 alone: positive to {lo}")
    C, dm, xl, xc = c12[8]
    p5 = -0.009 <= C <= -0.006 and dm < 0 and xc < 0.5
    for name, ok in (("P1", p1), ("P2", p2), ("P3", p3), ("P4", p4),
                     ("P5", p5)):
        print(f"  {name} {'holds' if ok else 'FAILS'}")
    kill = not p2_band(pos3, neg3) or not (dh3 and tail3)
    print(f"  KILL {'fires' if kill else 'missed'}")
    arm_y(out)
    print(f"\n{sum(CHECKS)} of {len(CHECKS)} checks, "
          f"{time.time() - t0:.1f} s")
    return 0 if all(CHECKS) else 1


def arm_y(out):
    """ARM Y (added after the run): the arm-Z algebras fixed to Y = 10^30,
    the certificate read from the bound's first degree at most 0."""
    print(f"\n=== ARM Y: the algebras fixed to Y = 10^30")
    for k, step, dmax in ((2, 2, 1000), (3, 4, 1000), (4, 8, 2000)):
        if k == 2:
            first = min(d for d in range(4, 201, 2) if sc.bound_i(d)[0] <= 0)
        else:
            first = min(out[k][1])
        D = 2 ** (k - 1)
        good, bad = [], []
        for d in range(first, dmax + 1, step):
            base = sc.qi_law(d, no5=True) if k == 2 else law(k, d)
            C, dm, xl, xc = TowerLaw(k, BigLaw(base, YBIG, D)).cert()
            ok = C < 0 and dm < 0 and xc < 0.5
            (good if ok else bad).append(d)
            if d - first <= 2 * step or d % 200 == 0:
                print(f"  k = {k}, d={d:5d}: certificate {C:+.6f} (M' "
                      f"{dm:+.3f}, x_low {xl:.5f}, x_c {xc:.5f})")
        print(f"  k = {k}: certified at every d = 0 mod {step} from {first} "
              f"to {dmax}: {not bad}" + ("" if not bad else f" (not at "
                                         f"{bad[:6]})"))
        if k > 2:
            pre = TowerLaw(k, BigLaw(law(k, first - step), YBIG, D)).cert()
            print(f"  k = {k}, d = {first - step} (bound positive): "
                  f"certificate {pre[0]:+.6f}")
        ds = dstar(k)
        fa = step * math.ceil(ds / step)
        print(f"  k = {k}: d* = l(x0)/u_k(x0) = {ds:.2f}; the first d = 0 "
              f"mod {step} past it {fa}, the bound's first at most 0 "
              f"{first}: equal {fa == first}")


def dstar(k):
    """d* = l(x0)/u_k(x0), x0 = (3 - sqrt 5)/2 (2 l + log x = 0), u_k the
    W_k terms of w per unit of d (9 at weight 1/2)."""
    x0 = (3 - math.sqrt(5)) / 2
    u = sum((0.5 if n == 9 else 1.0) * math.log1p(x0 ** math.log2(n))
            for n in wk(k))
    return se.lf(x0) / u


def p2_band(pos, neg):
    """P2's band within one step of 4 at either end."""
    return (pos and min(pos) == 8 and 20 <= max(pos) <= 28
            and neg and 24 <= min(neg) <= 32)


if __name__ == "__main__":
    sys.exit(main())