""" hullform.py -- the hull's four ends in q's: the fixed points' policy read at every node, and their values against a closed form. QUESTION. hull.py solves the top (Z_hi) and floor (Z_lo) of the hull set as fixed points of a max- (min-) affine recursion over the nodes (t, f), f full or reduced, on the policy a float iteration picks. firstreturn.py reduces the class step to G_E + h- < G-range < G_E + h+ per gap, h+ and h- r_t's records at the gap's length (q-scale), the G-range Z_lo, Z_hi less kappa_t y. (Q) Is the policy one fixed choice at every node of every cell, and so each end a closed form in the quotients and m? DERIVED BY HAND (before the engine). Write q_(n+k) = U_k q_n + V_k q_(n-1), U_(-1) = 0, V_(-1) = 1, U_0 = 1, V_0 = 0, U_(k+1) = a_(t+k+1) U_k + U_(k-1) (likewise V), n = t mod p. Since lambda_t = lim q_n |theta_(n-1)| and kappa_t = lim q_(n-1) |theta_(n-1)|, lim |theta_(n-1)| q_(n+k) = Z(U_k, V_k; t), Z(G, H; t) = lambda_t G + kappa_t H. THE TOP. Options at (t+1, R): from (t, F) at digit a_(t+c+2), value r_t Z_F(t) + m a A_t, or from (t, R) at a - 1, r_t Z_R(t) + m (a - 1) A_t; at (t+1, F): r_t max(Z_F(t), Z_R(t)) or a pinned start. The first wins at R iff r_t (Z_R(t) - Z_F(t)) <= m A_t. With F taking R, Z_R(t) - Z_F(t) = -r_(t-1) (Z_R(t-1) - Z_F(t-1)) + m a_(t+c+1) A_(t-1) <= m a_(t+c+1) A_(t-1) while Z_R >= Z_F, and r_t A_(t-1) = lim q_(n+c) |theta_n|, A_t = lim q_(n+c+1) |theta_n|, so the bar is a_(n+c+1) q_(n+c) <= q_(n+c+1): it holds strictly. And Z_R >= Z_F persists: D(t-1) <= m a_(t+c) A_(t-2) gives D(t) > m (a_(t+c+1) - 1) A_(t-1) >= 0, D = Z_R - Z_F, an interval the contraction keeps. The policy is the alternation R <- F <- R (input digits a_(k+1), 0, a_(k+1), 0, ..., the greatest admissible), and summing the play, Z_hi(t, R) = m Z(U_(c+1), V_(c+1); t), Z_hi(t, F) = m Z(U_c, V_c; t), the key m (U_(c+1), V_(c+1)) attaining the reduced top exactly (bronze x3, t = 0, c = 2: U = 1, 3, 10, 33, so (99, 30), Z 99.007, firstreturn.py's E_b; golden x2, c = 3: 2 Z(5, 3) = 2 phi^5 / sqrt 5 = 9.9193, hull.py's printed Z_hi). THE FLOOR (d = 1 on R, 0 on F, e = a_(t+2)): at both nodes the option from (t, F) is the lesser, as Z_lo(t, R) - Z_lo(t, F) = m A_(t-1) > 0, and the sum of a_(j+1) q_j over j <= n is q_(n+1) + q_n - 1, so Z_lo(t, F) = -Z(a_(t+1) + 1, 1; t), Z_lo(t, R) = Z_lo(t, F) + m Z(U_c, V_c; t) (bronze x3: -(4 lambda + kappa) = -3.9415, and 26.035, as hull.py and firstreturn.py print; golden x2: -(2 lambda + kappa) = -1.894, hull.py's Z_lo(0, full)). TRANSPLANT: the pinned starts were set aside; a start above r_t Z_R(t) at a full node, or below the floor, breaks the form. DESIGN (frozen before the engine). Cells: decide.py's 136, flush.py's PERIODIC six and decide.py's FRESH eleven by m = 2 .. 9, at c_inf (induct.least, "pin"); a cell with no pinned moves at c_inf is skipped and counted. Per cell hull.py's hull and hull(low=True) exactly; per node the options whose value equals the node's, exactly (the policy, ties counted apart); the four forms in QF, compared exactly. PREDICTIONS. (P1) at every node of every cell the top's R takes (t, F) alone and its F takes (t, R) alone, the floor's R and F take (t, F) alone, no start attaining. (P2) the four forms equal hull.py's values at every node. KILL: "nodes off the policy: n of N" with n > 0, or "nodes whose value != the form: n of N" with n > 0, each over the 136 cells' nodes. CONTROLS (read before the verdict). (a) bronze x3 and golden x2 at phase 0: the hand values above, printed beside the engine's. (b) THE PLANT, in the direction it pushes: hull(plant=True) (d_max on the full flag lowered by 1) against the unplanted forms must put nodes off the form at some cell, so (P2)'s print can fire. FINDINGS (copied from the run's prints). CONTROLS. (a) golden x2 (c_inf 3) top F 6.1305, R 9.9193, floor F -1.8944, R 4.2361; bronze x3 (c_inf 2) top F 29.9769, R 99.007, floor F -3.9415, R 26.0355: hull and form equal at each, and the five hand values above agree (the form column is P2's code, so (a) reads the hand derivation only through those five; the other three, 2 Z(3, 2) = 6.1305, 3 Z(10, 3) = 29.9769 and 6.1305 - 1.8944 = 4.2361, recomputed by hand after the run). (b) the plant puts 608 of 1056 nodes off the form: (P2) can fire. P1, KILL MISSED. 136 cells, 0 skipped; nodes off the policy: 0 of 1056, none attained by two options or a start. P2, KILL MISSED. Nodes whose value != the form: 0 of 1056, exactly. So at every cell read the hull's ends are the Z of integer keys: the reduced top is m (U_(c+1), V_(c+1)), the full top m (U_c, V_c), the full floor -(a_(t+1) + 1, 1), the reduced floor that plus m (U_c, V_c). With the bar's proof the policy is argued at every periodic alpha save the starts, which are read, not proved. NOTICED AFTER THE RUN (by hand from firstreturn.py's rows, three cells, observation). The key (U_k, V_k) has y = theta_(n+k)/theta_(n-1), of sign (-1)^(k+1) and size r_t's |theta_k|, and Z = Z(U_k, V_k; t). So the gap length L = (-1)^c m (y_(c+1) - y_(c+2)) + y_2 is y of the key K_L = (-1)^c m (U_(c+1) - U_(c+2)) + U_2 (V alike), and the first zero-reply gap at phase 0 reads E_b = m (U_j, V_j), j the odd one of c + 1, c + 2, and E_a = E_b - K_L: bronze x3 (c 2, j 3) E_b = 3 (33, 10) = (99, 30), K_L = (-218, -66), E_a = (317, 96); golden x2 (c 3, j 5) E_b = 2 (8, 5) = (16, 10), K_L = (8, 5), E_a = (8, 5). The other gaps of a phase translate by (U_1, V_1) (bronze: (3, 1)). Silver x2 (c 2, j 3) reads the same: E_b = 2 (12, 5) = (24, 10), its second gap (2, 1) on. So 12 of the 22 gaps read, the period-1 cells'; the 10 at V1 x2, V1 x3 and V2 x4 not. RUN RECORD. 3.1 s. The engine was written whole after the freeze and run once (peak commit 9.8 MB). P1's print has no plant of its own; P2 reads the same policy through the values, and (b) shows that reading can fire. The mirror of hull.py's options omits its unreachable-node fallback, which only the plant meets, and the plant goes through hull.py itself. After the run the verdicts were made the exit code, every print unchanged: 0 iff (a) shows hull equal to form at the eight values recorded in FINDINGS (golden's top F, R, floor F, R, then bronze's), (b) puts some node off the form, and P1 and P2 read 136 cells, 0 skipped, 0 ties, 0 of 1056 nodes off the policy and 0 off the form. Usage: python hullform.py """ import os import sys import time os.environ.setdefault("OPENBLAS_NUM_THREADS", "1") from flush import PERIODIC # noqa: E402 from induct import Field, Window, least, QF # noqa: E402 from hull import Coords, hull, dmax, eq # noqa: E402 from decide import FRESH # noqa: E402 def options(win, co, starts, low): """hull.py's options, mirrored: {node: [(label, pred, coef, const)]}.""" F, m, c, p = win.F, win.m, win.c, win.F.p sZ = {} for G, H, f, t in starts: z = co.Z(G, H, t) if (t, f) not in sZ or (z.f < sZ[(t, f)].f if low else z.f > sZ[(t, f)].f): sZ[(t, f)] = z out = {} for t1 in range(p): for f1 in (True, False): t = (t1 - 1) % p o = [] if (t1, f1) in sZ: o.append(("start", None, QF(0), sZ[(t1, f1)])) emax = F.a(t1 + 1) if low else 0 for f in (True, False): if f1: d = 0 else: dm = dmax(F, c, t, f) if dm < 1: continue d = 1 if low else dm o.append(("F" if f else "R", (t, f), F.r[t], co.A[t] * (m * d) - co.B[t] * emax)) out[(t1, f1)] = o return out def uv(F, t, k): """(U_k, V_k): q_(n+k) = U_k q_n + V_k q_(n-1), n = t mod p.""" U = {-1: 0, 0: 1} V = {-1: 1, 0: 0} for j in range(k): a = F.a(t + j + 1) U[j + 1] = a * U[j] + U[j - 1] V[j + 1] = a * V[j] + V[j - 1] return U[k], V[k] def forms(F, co, m, c, t): """{(f, low): the hand form} at phase t.""" Uc, Vc = uv(F, t, c) U1, V1 = uv(F, t, c + 1) zc = co.Z(Uc, Vc, t) * m lo_f = QF(0) - co.Z(F.a(t + 1) + 1, 1, t) return {(False, False): co.Z(U1, V1, t) * m, (True, False): zc, (True, True): lo_f, (False, True): lo_f + zc} def main(): t0 = time.time() nodes = off = ties = form_bad = plant_bad = skipped = cells = 0 off_list, bad_list, ctl = [], [], [] for name, per in list(PERIODIC) + list(FRESH): F = Field(per) cname = name.split()[0] for m in range(2, 10): cinf = least(F, m, "pin") win = Window(F, m, cinf) st, miss = win.pin_starts() if miss: skipped += 1 continue cells += 1 co = Coords(F, m, cinf) for low in (False, True): val, _ = hull(win, co, st, low=low) pv, _ = hull(win, co, st, low=low, plant=True) opts = options(win, co, st, low) for (t, f), v in val.items(): nodes += 1 att = [lab for lab, pr, k, b in opts[(t, f)] if eq(v, b + (k * val[pr] if pr else QF(0)))] want = "R" if (f and not low) else "F" ties += len(att) > 1 if att != [want]: off += 1 off_list.append((cname, m, t, f, low, att)) fm = forms(F, co, m, cinf, t)[(f, low)] if not eq(v, fm): form_bad += 1 bad_list.append((cname, m, t, f, low, round(v.f, 4), round(fm.f, 4))) plant_bad += not eq(pv[(t, f)], fm) if cname in ("golden", "bronze") and t == 0 and \ m == (2 if cname == "golden" else 3): ctl.append((cname, m, cinf, "low" if low else "top", "F" if f else "R", round(v.f, 4), round(fm.f, 4))) print("== CONTROLS") print(f" (a) phase 0 (alpha, m, c_inf, side, flag, hull, form): {ctl}") print(f" (b) THE PLANT (d_max full - 1): nodes off the form: " f"{plant_bad} of {nodes}") print("== P1") print(f" cells {cells}, skipped (no pinned moves at c_inf) {skipped}") print(f" nodes attained by two options or more: {ties}") print(f" off, first ones: {off_list[:8]}") print(f" KILL: nodes off the policy: {off} of {nodes}") print("== P2") print(f" apart, first ones: {bad_list[:8]}") print(f" KILL: nodes whose value != the form: {form_bad} of {nodes}") checks = [ [v for *_, v, f in ctl if v == f] == [ 6.1305, 9.9193, -1.8944, 4.2361, 29.9769, 99.007, -3.9415, 26.0355], plant_bad > 0, (cells, skipped, ties, off, nodes) == (136, 0, 0, 0, 1056), form_bad == 0, ] print(f" {sum(checks)}/{len(checks)} checks as recorded, " f"{time.time() - t0:.1f} s") sys.exit(0 if all(checks) else 1) if __name__ == "__main__": main()