slackbar.py

Cited on: FLUSH.

To run it: save slackbar.py, with anchors.py, classform.py, decide.py, firstreturn.py, flush.py, gaps.py, hull.py, hullform.py, induct.py, keyweld.py, ostrowski.py, tie.py beside it; standard library only.

"""
slackbar.py -- the class step's slack: is c_inf one low-side test at
the one c below the overlap reader's c_ov, in q's?

QUESTION. classform.py reads c_inf at decide.py's 136 as the least c
with pinned moves at which every zero-reply piece passes: no closed end
in the hull, and some end E with G-range low > G_E + h- and high < G_E
+ h+. Below c_inf the failures sit on the low side (classform's
NOTICED), and at the 48 c_inf is c_ov or c_ov - 1 (keyweld.py's Q1),
c_ov the least c with m (P_(c+1) + P_(c+2)) <= P_2 at every phase,
P_k = |theta_(t+k) / theta_(t-1)|. (Q) At the 136, is c_inf decided by
one c, c_ov - 1, and the low side alone, and does that side read in
integers: the records n+-(L), the end's G and the floor -(a_(t+1) + 1)
(+ m q_c on the reduced flag), no kappa_t?

DERIVED BY HAND (before the engine). At phase t write a_k = a_(t+k),
q_k = U_k, P_k as above (P_(-1) = 1, P_0 = r_t, P_(k+1) = P_(k-1) -
a_(k+1) P_k), y(K_k) = (-1)^(k+1) P_k, Z(G, H) = G + kappa_t y (so
lambda_t = 1 - kappa_t r_t).
  THE FLOOR. Z_lo(t, F) = -Z(a_1 + 1, 1) = -(a_1 + 1) + kappa_t (P_0
  - P_1), as 1 - a_1 r_t = P_1; P_0 - P_1 is in (0, 1), so Z_lo(t, F)
  lies in (-(a_1 + 1), -a_1). The G-range low is floor(Z_lo - kappa_t
  y_b), y_b the piece's top, |y_b| < 1 and 0 < kappa_t < 1, so on the
  full flag it is -(a_1 + 1) + d, d in {-1, 0, 1}. On the reduced flag
  Z_lo gains m Z(K_c) = m q_c + m kappa_t (-1)^(c+1) P_c, whose second
  term can pass 1 (golden x9, c = 1: 9 kappa_t P_1 = 9 (0.447) (0.382) =
  1.54, a c without pinned moves there), so the crude floor -(a_1 + 1)
  + m q_c is off by at most 1 + ceil(m kappa_t P_c).
  THE ENDS. From E_b(e) = m K_O + e K_1, G_b = m q_O + e a_1; from
  E_a(e) = m K_E - K_2 + e K_1, G_a = m q_E - q_2 + e a_1; a clipped
  end is the window's key end (classform.py's win_keys). The low side
  passes at E_b iff n-(L) > G_b - G_lo and at E_a iff n+(L) > G_a -
  G_lo, L the clipped piece's length, n+- r_t's records
  (firstreturn.py's three_gap). G_E grows with e by a_1 while L is one
  length for every unclipped gap, so the last unclipped gap of a phase
  is the hardest; G_lo is lowest on the full flag.
  WHY ONE c. Gaps exist at c iff L(c, t) = m (P_(c+1) + P_(c+2)) - P_2
  > 0 at some phase (keyweld.c_ov tests L(c, t) / r_t at phase t + 1,
  as m (P_c + P_(c+1)) <= P_1 in its P[k + 1] = P_k); at c_ov none
  does, so the gap step closes and c_inf <= c_ov (keyweld.py: the gap
  step derived, the endpoint step and the reduced flag read, not
  derived). That c_inf >= c_ov - 1 is not derived:
  it is (P1)'s question, the TRANSPLANT from the 48. At c_ov - 1 the
  open L are the least positive ones, so the n+-(L) the largest.

DESIGN (frozen before the engine). Cells: decide.py's 136, keyed by
full name. Per cell: c_inf = induct.least(F, m, "pin"); c_ov =
keyweld.c_ov; at c = c_ov - 1 (pinned moves required, else the bar
reads c_ov), classform.pieces at every (t, f) with hull.py-free forms
(hullform.forms), and three tests per piece:
  FORM: classform.passes (both sides, closed ends), the reference;
  LOW: some end passes its low side, the high side and closed ends
      dropped, G_lo the float floor(Z_lo - kappa_t y_b), its parts exact;
  CRUDE: LOW with G_lo := -(a_1 + 1) on the full flag, -(a_1 + 1) + m
      q_c on the reduced: integers and records only.
Each bar reads c_ov - 1 when every piece at c_ov - 1 passes it, else
c_ov. Margins printed: per cell the least over pieces of the best end's
low margin G_lo - (G_E + h-), exact, and the crude one.
PREDICTIONS. (P1) c_inf is c_ov or c_ov - 1 at every one of the 136.
(P2) LOW's c = c_inf at all 136 (NOTICED: no high-side failure below
c_inf; the 15 closed-end-only failures may sit at a (cell, c) where a
low-side failure also sits, unread). (P3) CRUDE's c = c_inf at all
136: the margins at c_ov - 1 are tens or more, past the floor's error.
KILL: "c_inf outside {c_ov - 1, c_ov} at n of 136"; "LOW's c != c_inf
at n of 136"; "CRUDE's c != c_inf at n of 136"; each n > 0 kills its
prediction, counted over all 136 and printed apart over the arm where
c_inf = c_ov - 1 (the cells where the bar must pass every piece).
CONTROLS (read before the verdicts). (a) keyweld.py's six cells with
c_inf = c_ov - 1 at the 48 (golden x2 c_ov 4, silver x2 3, bronze x3
3, V1 x2 4, V1 x3 5, V2 x4 5) and golden x2 .. x9's c_ov [4, 5, 5, 6,
6, 7, 7, 7]: the engine's c_ov and c_inf printed beside them. (b)
FORM's c, read at c_ov - 1 and c_ov alone, must equal c_inf wherever
(P1) holds: the reference through this engine's plumbing. THE PLANTS,
one per test, each in the direction it pushes: (c) LOW with h- halved
(stricter): its c rises above c_inf at some cell of the arm, or the
arm's margins all exceed half; (d) CRUDE with the floor lowered by m
q_c on the reduced flag (the full floor on both): printed beside (P3),
read for whether the reduced flag ever binds.

FINDINGS (copied from the run's prints).
  CONTROLS. (a) the six read (c_inf, c_ov): golden x2 (3, 4), silver
      x2 (2, 3), bronze x3 (2, 3), V1 x2 (3, 4), V1 x3 (4, 5), V2 x4 (4,
      5); golden's c_ov [4, 5, 5, 6, 6, 7, 7, 7]: keyweld's. (b) FORM's
      c != c_inf at 0 of 136. (c) h- halved: LOW's c != c_inf at 7 of
      the arm's 17: LOW can fire. (d) the full floor on both flags:
      CRUDE's c != c_inf at 0 of 136: the + m q_c term is never needed
      at the 136 (not that the reduced flag can be dropped). CRUDE has
      no plant of its own firing; it fails some piece at every cell
      read off the arm.
  The bars can fire only where c_ov - 1 has pinned moves: 120 of 136
      (the margin rows printed), 17 on the arm and 103 off it; at the
      other 16 every bar reads c_ov by construction.
  P1, KILL MISSED. c_inf outside {c_ov - 1, c_ov} at 0 of 136; the arm
      c_inf = c_ov - 1 holds 17 cells: the six above, [4] x4, [5] x5,
      [1, 3] x3, [1, 5] x4, [2, 3] x2, [2, 4] x2 and x7, [2, 5] x2, [3,
      4] x3, [3, 5] x3, [4, 5] x4.
  P2, KILL MISSED. LOW's c != c_inf at 0 of 136, 0 of the arm's 17.
  P3, KILL MISSED. CRUDE's c != c_inf at 0 of 136, 0 of 17.
  MARGINS at c_ov - 1, the least over pieces of the best end's low
      margin: on the arm 3 (golden x2) to 1032 ([4, 5] x4), off it -5
      (golden x3, x7) to -1968 ([1, 5] x9); crude equals exact or
      exceeds it by 1 at every cell, never crossing 0. P3's premise
      (margins of tens) failed: 3 at golden x2, 5 at V1 x2, -5 at golden
      x3 and x7; the kill missed all the same.
  So at decide.py's 136, c_inf = c_ov - 1 if c_ov - 1 has pinned moves
  and every zero-reply piece there has an end with n-+(L) > G_E - G_lo,
  G_lo = -(a_(t+1) + 1) (+ m q_c reduced), else c_ov: one c, one side,
  integers and r_t's records, the verdict 3 or more from its bar.

RUN RECORD. 1.1 s. The engine was written whole after the freeze and
run once. On shipping, the verdicts were made the exit code: 0 iff
control (a)'s six cells and golden's c_ov list, (b) at 0, (c) at 7 of
the arm's 17, (d) at 0, the 120 cells with pinned moves at c_ov - 1,
and P1 to P3 at 0 of 136 and 0 of 17 hold as printed.

Usage: python slackbar.py
"""

import math
import os
import sys
import time

os.environ.setdefault("OPENBLAS_NUM_THREADS", "1")
from flush import PERIODIC  # noqa: E402
from decide import FRESH  # noqa: E402
from induct import Field, Window, least  # noqa: E402
from hull import Coords  # noqa: E402
from keyweld import c_ov, lt  # noqa: E402
from firstreturn import three_gap  # noqa: E402
from hullform import forms, uv  # noqa: E402
from classform import pieces, passes  # noqa: E402


def low_margin(F, co, t, m, c, f, pc, zlo, crude=False, half=False,
               plant_d=False):
    """The best end's low margin G_lo - (G_E + h-), or None for a piece
    with no interior."""
    ya, yb, _ca, _cb, Ea, Eb = pc
    if not lt(ya, yb):
        return None
    npl, nmi = three_gap(F, t, yb - ya)
    if half:
        npl, nmi = npl / 2, nmi / 2
    if crude:
        glo = -(F.a(t + 1) + 1)
        if not f and not plant_d:
            glo += m * uv(F, t, c)[0]
    else:
        glo = math.floor(zlo.f - co.kap[t].f * yb.f)
    return max(glo - (Ea[0] - npl), glo - (Eb[0] - nmi))


def read_c(F, m, c, cell):
    """At c: (pinned, form_ok, low_ok, crude_ok, half_ok, plantd_ok,
    least exact margin, least crude margin)."""
    win = Window(F, m, c)
    _st, miss = win.pin_starts()
    if miss:
        return None
    co = Coords(F, m, c)
    ok = {"form": True, "low": True, "crude": True, "half": True,
          "pd": True}
    mex = mcr = None
    for t in range(F.p):
        fm = forms(F, co, m, c, t)
        for f in (True, False):
            zlo, zhi = fm[(f, True)], fm[(f, False)]
            for pc in pieces(F, t, m, c, f):
                ok["form"] &= passes(F, co, t, pc, zlo, zhi)
                me = low_margin(F, co, t, m, c, f, pc, zlo)
                if me is None:
                    continue
                mc = low_margin(F, co, t, m, c, f, pc, zlo, crude=True)
                mh = low_margin(F, co, t, m, c, f, pc, zlo, half=True)
                md = low_margin(F, co, t, m, c, f, pc, zlo, crude=True,
                                plant_d=True)
                ok["low"] &= me > 0
                ok["crude"] &= mc > 0
                ok["half"] &= mh > 0
                ok["pd"] &= md > 0
                mex = me if mex is None else min(mex, me)
                mcr = mc if mcr is None else min(mcr, mc)
    return ok, mex, mcr


def main():
    t0 = time.time()
    rows = []
    for name, per in list(PERIODIC) + list(FRESH):
        F = Field(per)
        for m in range(2, 10):
            cinf = least(F, m, "pin")
            cov = c_ov(F, m)
            r = read_c(F, m, cov - 1, name) if cov >= 1 else None
            bars = {}
            for k in ("form", "low", "crude", "half", "pd"):
                bars[k] = cov - 1 if (r is not None and r[0][k]) else cov
            rows.append((name, m, cinf, cov, bars,
                         r[1] if r else None, r[2] if r else None,
                         r is not None))
    n = len(rows)
    arm = [x for x in rows if x[2] == x[3] - 1]
    print("== CONTROLS")
    six = [("golden [1]", 2), ("silver [2]", 2), ("bronze [3]", 3),
           ("V1 [1, 1, 1, 2]", 2), ("V1 [1, 1, 1, 2]", 3),
           ("V2 [2, 1, 3, 1]", 4)]
    print("  (a) the six (alpha, m, c_inf, c_ov):",
          [(x[0].split()[0], x[1], x[2], x[3]) for x in rows
           if (x[0], x[1]) in six])
    print("      golden c_ov, m = 2 .. 9:",
          [x[3] for x in rows if x[0] == "golden [1]"])
    p1 = [x for x in rows if x[2] not in (x[3] - 1, x[3])]
    fb = [x for x in rows if x not in p1 and x[4]["form"] != x[2]]
    print(f"  (b) FORM's c != c_inf where P1 holds: {len(fb)} of "
          f"{n - len(p1)}; first: {[(x[0], x[1]) for x in fb[:6]]}")
    hb = [x for x in arm if x[4]["half"] != x[2]]
    print(f"  (c) THE PLANT h- halved: LOW's c != c_inf at {len(hb)} of "
          f"{len(arm)} on the arm, {sum(x[4]['half'] != x[2] for x in rows)}"
          f" of {n} in all")
    db = [x for x in rows if x[4]["pd"] != x[2]]
    print(f"  (d) THE PLANT full floor on both flags: CRUDE's c != c_inf "
          f"at {len(db)} of {n}")
    print("== MARGINS at c_ov - 1 (least over pieces of the best end's "
          "low margin; exact, crude)")
    for x in rows:
        if x[7]:
            print(f"    {x[0]:18s} x{x[1]} c_inf {x[2]} c_ov {x[3]} "
                  f"exact {x[5]} crude {x[6]}")
    print("== P1")
    print(f"  apart: {[(x[0], x[1], x[2], x[3]) for x in p1[:10]]}")
    print(f"  KILL: c_inf outside {{c_ov - 1, c_ov}} at {len(p1)} of {n}")
    kills = []
    for k, lab in (("low", "P2 LOW"), ("crude", "P3 CRUDE")):
        bad = [x for x in rows if x[4][k] != x[2]]
        kills.append((len(bad), sum(x in bad for x in arm)))
        print(f"== {lab}")
        print(f"  arm (c_inf = c_ov - 1): {kills[-1][1]} of "
              f"{len(arm)}; apart: {[(x[0], x[1], x[2], x[4][k]) for x in bad[:8]]}")
        print(f"  KILL: {k.upper()}'s c != c_inf at {len(bad)} of {n}")
    checks = [
        [(x[0].split()[0], x[1], x[2], x[3]) for x in rows
         if (x[0], x[1]) in six] == [
            ("golden", 2, 3, 4), ("silver", 2, 2, 3), ("bronze", 3, 2, 3),
            ("V1", 2, 3, 4), ("V1", 3, 4, 5), ("V2", 4, 4, 5)],
        [x[3] for x in rows if x[0] == "golden [1]"]
        == [4, 5, 5, 6, 6, 7, 7, 7],
        (n, len(arm), len(fb), len(hb), len(db)) == (136, 17, 0, 7, 0),
        sum(x[7] for x in rows) == 120,
        (len(p1), kills) == (0, [(0, 0), (0, 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()