diff --git a/research/a1858-successor-frame-climbs/README.md b/research/a1858-successor-frame-climbs/README.md new file mode 100644 index 0000000000..0568a6316a --- /dev/null +++ b/research/a1858-successor-frame-climbs/README.md @@ -0,0 +1,62 @@ +# Successor frame climbs of the A1858 contraction + +**Negative result. No new κ.** The selected record is not edited. + +On all four frozen words, the A1858 contraction leaves every positive-dimension climb of registers 456, 457 and 475 unchanged, and it leaves the recursive frame histogram unchanged. + +## Scope and evidence + +Chooser seed `05f92898273de33149bc662a2b87104d` drew `incumbent-optimization` on canonical main `ec2780e8aeb1c88acaf5e9e470ef8c68e7fbbb6c`. Suggestion 1, `selected-packing`, is not taken: [PR #4170](https://github.com/CrocSwap/integer-mult-bounds/pull/4170) already transferred the pooled ceiling onto the retained PR #4088 head. This package is suggestion 2, `scalar-to-moment`, scope **refined**. The stop rule of that card is to compare complete histograms and report a scalar-only change honestly. + +[PR #4152](https://github.com/CrocSwap/integer-mult-bounds/pull/4152) certifies the cleanup edit of the consecutive A1858 contraction from [PR #3998](https://github.com/CrocSwap/integer-mult-bounds/pull/3998) and leaves the successor climbs of registers 456, 457 and 475 uncomputed. The four gzip words checked here are that package's frozen inputs, authenticated by the SHA256 values in `verify.py`. No predecessor Python is imported. + +The published block at zero-based `A[1858:1861]`, all on frame `span(82,42)`, + +``` +457 <- 457 - 475 +456 <- 456 + 457 +456 <- 456 + 475 +``` + +is replaced by + +``` +456 <- 456 + 457 +457 <- 457 - 475 +``` + +and cleanup is rederived from the forward word. Both sides induce the same map on `(x456, x457, x475)`. A rank-d child is charged only when a register's frame dimension increases. Recomputing that bill: + +| Words | Histogram before and after | +|---|---| +| both PR3734 words | `{1:2440,2:725,3:224,4:102,5:20,8:8}` | +| both PR3945 words | `{1:2482,2:712,3:218,4:109,5:20,8:8}` | + +Register 457's full climb sequence is identical, including indices. The only sequence edits are zero-dimension touches inside the block: register 456 replaces A indices 1859 and 1860 by 1858, and register 475 replaces A indices 1858 and 1860 by 1859. Every positive-dimension climb of the three registers keeps its section and index. Widening the block frame to `span(82,42,1)` fails containment. Deleting the block without the two-ADD image changes the histogram. A flipped central sign fails the three-register map. + +PR #3998 already stated the PR3734 profile. This note does not replay that package. It answers the climb question PR #4152 left open, on all four words that package froze, with an independent bill. + +## What this does not establish + +No paid transition is removed. The two deleted ADD events are zero-dimension touches, so the scalar saving is not a frame-itinerary improvement. Native operators, owners, precision, stock, tape and the PR3945 producer bill are not priced. The selected record stays the pinned `certificates/selected-result.json` (κ = `60403/20000000`); `make frontier4470-verify` was not run. + +## Reproduce + +Python 3.12, standard library only, from the repository root: + +```sh +python3 -B research/a1858-successor-frame-climbs/verify.py +python3.12 -B scripts/research_package.py --package research/a1858-successor-frame-climbs +``` + +Expected first line: `PASS_A1858_SUCCESSOR_FRAME_CLIMBS words=4 pr3734={1:2440,2:725,3:224,4:102,5:20,8:8} pr3945={1:2482,2:712,3:218,4:109,5:20,8:8} paid_climbs=unchanged`. + +Catalogue and selected-record provenance, and the four input pins, are in [SOURCES.json](SOURCES.json). Base commit `ec2780e8aeb1c88acaf5e9e470ef8c68e7fbbb6c`. + +## Open next questions + +Can the two-ADD image be carried into the PR3945 producer with native, owner and precision costs still paid? An unchanged frame histogram does not decide whether the two removed ADDs survive those obligations. Price one contracted PR3945 word's native and owner bill before claiming the scalar saving. Those obligations are outside the frozen scalar words checked here. + +## Credit + +Dataisfire's PR3734 words and Dugongue's PR3945 words are the frozen inputs. Chris R / Clear1nsight, PR #3998, wrote the contraction and stated the PR3734 profile. unpaidmoon, PR #4152, certified the cleanup edit and asked for this climb. The frame contract follows the BORROW-WORD reading used by Arham Tahir's coupled-carrier audit and restated, without being imported, by the PR #3998 vendor. Riquelme Soares (@hevlyo), with substantial AI assistance. Apache-2.0. No independent human or formal-kernel review is claimed. diff --git a/research/a1858-successor-frame-climbs/SOURCES.json b/research/a1858-successor-frame-climbs/SOURCES.json new file mode 100644 index 0000000000..e1e8684aee --- /dev/null +++ b/research/a1858-successor-frame-climbs/SOURCES.json @@ -0,0 +1,42 @@ +{ + "schema_version": 1, + "base_commit": "ec2780e8aeb1c88acaf5e9e470ef8c68e7fbbb6c", + "files": [ + { + "path": "certificates/selected-result.json", + "usage": "provenance", + "git_blob": "c35482cd9035843b15a634cc8276a7e1e40e8395", + "sha256": "6fe0a099b89aa2aed796a13c3a21dafc63f3388ebf397464e44e0451893b9375" + }, + { + "path": "docs/research/agent-task-catalog.json", + "usage": "provenance", + "git_blob": "f126dd2e2dad2b669e75d1d0d0bd05c600725602", + "sha256": "f7a48bfa2659b88378282a8eee7d9fe52cdf505594124c4d1686b58510fffd72" + }, + { + "path": "research/staged544-a1858-contraction-audit/inputs/BORROW-WORD-43base.json.gz", + "usage": "input", + "git_blob": "674ea18ade4af85ca865fb08dc973637a09e30e6", + "sha256": "5ca4be99851f8878cbfe0fd6c454df926b11218f17feefa1664721cf03156699" + }, + { + "path": "research/staged544-a1858-contraction-audit/inputs/BORROW-WORD-43extra.json.gz", + "usage": "input", + "git_blob": "b17e2be67e327f04802fd034c344fe4253c13a4a", + "sha256": "965268a9be73d3f62e7ef680d18f108cf064bfe1f44b82157baa61443232904e" + }, + { + "path": "research/staged544-a1858-contraction-audit/inputs/PR3945-WORD-base.json.gz", + "usage": "input", + "git_blob": "806784bf99d9d15004d2d227d06268556a0726c8", + "sha256": "08e6c96fbe3dfec96cb8451b3867ef8c1a214109ecd1e5af7fcc28e0f05728d0" + }, + { + "path": "research/staged544-a1858-contraction-audit/inputs/PR3945-WORD-extra.json.gz", + "usage": "input", + "git_blob": "c531be7383ad3ba941600c7ced14e5e10d6d1c67", + "sha256": "d83c2ec9cecc0ad092d34ea4080b28a4fd7199579356b6d5a8fc6cd53ff93299" + } + ] +} diff --git a/research/a1858-successor-frame-climbs/TASK.json b/research/a1858-successor-frame-climbs/TASK.json new file mode 100644 index 0000000000..0c4c32ef64 --- /dev/null +++ b/research/a1858-successor-frame-climbs/TASK.json @@ -0,0 +1,106 @@ +{ + "schema_version": 1, + "task_id": "scalar-to-moment", + "priority_revision": 7, + "base_commit": "ec2780e8aeb1c88acaf5e9e470ef8c68e7fbbb6c", + "original_card": { + "id": "scalar-to-moment", + "question": "Can a changed frame itinerary remove a paid transition beyond the already covered scalar A2 contractions?", + "starting_points": [ + "docs/research/research-family-status.md", + "certificates/selected-result.json", + "research/staged544-a2-graph-rewrite/README.md", + "research/staged544-separated-a2/README.md", + "research/staged544-a2-composition-audit/README.md", + "research/staged544-a1858-contraction-audit/README.md" + ], + "methods": [ + "Derive the changed legal frame itinerary and exact child histogram.", + "Prove a scoped obstruction showing why that scalar gain cannot change the paid contract." + ], + "acceptance_gate": "An unchanged histogram is not a kappa gain; record any separate runtime or proof benefit explicitly.", + "first_step": { + "command": [ + "python3", + "-B", + "scripts/research_baseline.py" + ], + "expected": "PASS_RESEARCH_BASELINE_BINDINGS_ONLY", + "inspect": "Read the A2 and separated-A2 constructions and their composition/contraction audits. Scalar contractions can preserve the complete recursive histogram; fewer ADDs alone do not justify further scans.", + "scope": "Source-binding orientation only; no scientific replay, theorem acceptance or supplier admission. Budget any heavy replay separately." + }, + "first_experiment": "Identify an actual paid transition to remove and construct a legal replacement frame itinerary, charging restoration, source windows and changed helpers. Compare complete before/after histograms. Stop if only scalar count changes; report that benefit honestly, without expanding the same scalar-only census.", + "bottleneck_link": "A legal change to the histogram, not addition count alone, is the bridge to an exponent gain.", + "allowed_variations": [ + "One existing scalar simplification and one proposed frame-path change." + ], + "scope_change_triggers": [ + "Submitting only another scalar count as completion of a paid-profile task." + ], + "overlap_terms": [ + "scalar simplification", + "paid profile", + "R439", + "3998", + "4019", + "4112", + "4152", + "staged544-a2-graph-rewrite", + "staged544-separated-a2", + "staged544-a2-composition-audit", + "staged544-a1858-contraction-audit" + ], + "existing_checks": [ + { + "scope": "PR #3998: consecutive A2 scalar contraction", + "evidence": "research/staged544-a2-graph-rewrite/README.md", + "remaining": "A genuinely changed paid itinerary or a distinct independent audit; no default scalar-only expansion.", + "status": "author-checked", + "source_commit": "389268a6b5e0373a3125d5c6fcd6995f4ea324f5", + "selected_record_sha256": "bb2eaa58647b353f88d0aec2eafb9d30df1be53589231b23e12ceef3e4bad129" + }, + { + "scope": "PR #4019: separated A2 sites with unchanged recursive histogram", + "evidence": "research/staged544-separated-a2/README.md", + "remaining": "A genuinely changed paid itinerary or a distinct independent audit; no default scalar-only expansion.", + "status": "author-checked", + "source_commit": "f408e09df300b575235f7f77f81948071397e16d", + "selected_record_sha256": "bb2eaa58647b353f88d0aec2eafb9d30df1be53589231b23e12ceef3e4bad129" + }, + { + "scope": "PR #4112: composition audit", + "evidence": "research/staged544-a2-composition-audit/README.md", + "remaining": "A genuinely changed paid itinerary or a distinct independent audit; no default scalar-only expansion.", + "status": "author-checked", + "source_commit": "205b7380656ba4b6316a8a493b1a3c679b8f7a0e", + "selected_record_sha256": "bb2eaa58647b353f88d0aec2eafb9d30df1be53589231b23e12ceef3e4bad129" + }, + { + "scope": "PR #4152: A1858 contraction audit", + "evidence": "research/staged544-a1858-contraction-audit/README.md", + "remaining": "A genuinely changed paid itinerary or a distinct independent audit; no default scalar-only expansion.", + "status": "author-checked", + "source_commit": "caaa14cde5962adc372fdca71f63d0f29e73c8b2", + "selected_record_sha256": "bb2eaa58647b353f88d0aec2eafb9d30df1be53589231b23e12ceef3e4bad129" + } + ] + }, + "scope_status": "refined", + "actual_scope": "On the four frozen words of PR #4152, the published A1858 contraction does not change the recursive frame histogram or any positive-dimension climb of registers 456, 457 and 475.", + "scope_change_reason": "Suggestion 1, selected-packing, is already delivered by PR #4170. This package takes suggestion 2 and stops at its own rule: compare the before/after histograms of the open successor-climb question in PR #4152. It does not search another scalar block and does not claim a removed paid transition.", + "bottleneck_link": "Side result relative to the selected record. A paid-profile gain would require a positive-dimension climb to disappear. This contraction removes only zero-dimension touches, so it cannot move the frontier.", + "budget_minutes": 60, + "stop_rule": "Stop once the before/after histograms and the three-register climbs are compared. Do not search another scalar block or price the PR #3945 native bill in this package.", + "outcome": "negative-result", + "evidence": "PASS_A1858_SUCCESSOR_FRAME_CLIMBS. Both PR3734 words keep {1:2440,2:725,3:224,4:102,5:20,8:8} and both PR3945 words keep {1:2482,2:712,3:218,4:109,5:20,8:8}. Positive climbs of registers 456, 457 and 475 match before and after. The only sequence edits are zero-dimension A-index touches inside the block.", + "author": "Riquelme Soares (@hevlyo)", + "ai_assistance": "Substantial AI assistance was used to draft the verifier, the climb comparison and this note. No independent human or formal-kernel review is claimed.", + "open_questions": [ + { + "question": "Can the two-ADD image be carried into the PR3945 producer with native, owner and precision costs still paid?", + "why_it_matters": "An unchanged frame histogram does not decide whether the two removed ADDs survive those obligations.", + "first_step": "Price one contracted PR3945 word's native and owner bill before claiming the scalar saving.", + "blocked_by": "Those obligations are outside the frozen scalar words checked here." + } + ] +} diff --git a/research/a1858-successor-frame-climbs/VERIFY.json b/research/a1858-successor-frame-climbs/VERIFY.json new file mode 100644 index 0000000000..f75ea8acd4 --- /dev/null +++ b/research/a1858-successor-frame-climbs/VERIFY.json @@ -0,0 +1,23 @@ +{ + "schema_version": 1, + "status": "ready", + "python": "3.12", + "requirements": [], + "inputs": [ + "research/staged544-a1858-contraction-audit/inputs/BORROW-WORD-43base.json.gz", + "research/staged544-a1858-contraction-audit/inputs/BORROW-WORD-43extra.json.gz", + "research/staged544-a1858-contraction-audit/inputs/PR3945-WORD-base.json.gz", + "research/staged544-a1858-contraction-audit/inputs/PR3945-WORD-extra.json.gz" + ], + "commands": [ + { + "argv": [ + "{python}", + "-B", + "{package}/verify.py" + ], + "timeout_seconds": 120, + "checks": "Baseline histograms, positive climbs of registers 456, 457 and 475, the zero-dimension index edit, the three-register matrix identity, and rejecting controls for a widened frame, a dropped block and a flipped sign." + } + ] +} diff --git a/research/a1858-successor-frame-climbs/verify.py b/research/a1858-successor-frame-climbs/verify.py new file mode 100644 index 0000000000..fa62725e3c --- /dev/null +++ b/research/a1858-successor-frame-climbs/verify.py @@ -0,0 +1,315 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: Apache-2.0 +"""Successor frame climbs of the published A1858 contraction. + +The four frozen words are the byte-pinned inputs of +research/staged544-a1858-contraction-audit. This verifier does not import that +package or the PR #3998 frame tracer. The chronological bill below is a +separate reading of the same BORROW-WORD contract: a register pays a rank-d +child only when its frame dimension increases. +""" + +import gzip +import hashlib +import json +from collections import Counter +from copy import deepcopy +from fractions import Fraction +from pathlib import Path + +PACKAGE = Path(__file__).resolve().parent +ROOT = PACKAGE.parents[1] +INPUTS = ROOT / "research" / "staged544-a1858-contraction-audit" / "inputs" + +V, N, H = 112, 704, 9 +FULL_VECTORS = [256, 128, 64, 32, 16, 8, 4, 2, 1] +TRIPLE = [ + ["add", 457, 475, -1, 1, [82, 42]], + ["add", 456, 457, 1, 1, [82, 42]], + ["add", 456, 475, 1, 1, [82, 42]], +] +PAIR = [ + ["add", 456, 457, 1, 1, [82, 42]], + ["add", 457, 475, -1, 1, [82, 42]], +] +WORDS = ( + ("BORROW-WORD-43base.json.gz", + "5ca4be99851f8878cbfe0fd6c454df926b11218f17feefa1664721cf03156699", + {1: 2440, 2: 725, 3: 224, 4: 102, 5: 20, 8: 8}), + ("BORROW-WORD-43extra.json.gz", + "965268a9be73d3f62e7ef680d18f108cf064bfe1f44b82157baa61443232904e", + {1: 2440, 2: 725, 3: 224, 4: 102, 5: 20, 8: 8}), + ("PR3945-WORD-base.json.gz", + "08e6c96fbe3dfec96cb8451b3867ef8c1a214109ecd1e5af7fcc28e0f05728d0", + {1: 2482, 2: 712, 3: 218, 4: 109, 5: 20, 8: 8}), + ("PR3945-WORD-extra.json.gz", + "d83c2ec9cecc0ad092d34ea4080b28a4fd7199579356b6d5a8fc6cd53ff93299", + {1: 2482, 2: 712, 3: 218, 4: 109, 5: 20, 8: 8}), +) +LEMMA = ( + "On all four frozen words, the A1858 contraction leaves every " + "positive-dimension climb of registers 456, 457 and 475 unchanged, and " + "it leaves the recursive frame histogram unchanged." +) + + +class AuditFailure(Exception): + """Explicit failure. python -O does not disable it.""" + + +def need(ok, *why): + if not ok: + raise AuditFailure(" ".join(map(str, why))) + + +def is_target(register): + return V <= register < 2 * V + + +def touched(gate): + return gate[1:3] if gate[0] == "add" else gate[1:2] + + +_SPAN = {} + + +def span(vectors): + key = tuple(vectors) + mask = _SPAN.get(key) + if mask is None: + points = {0} + for vector in key: + need(type(vector) is int and 0 <= vector < 512, "frame vector", vector) + if vector not in points: + points |= {point ^ vector for point in points} + mask = sum(1 << point for point in points) + _SPAN[key] = mask + return mask + + +def dim(mask): + size = bin(mask).count("1") + need(size and size & (size - 1) == 0, "not a subspace") + return size.bit_length() - 1 + + +def hyperplane(port): + return sum(1 << x for x in range(512) if bin(x & port).count("1") % 2 == 0) + + +def warp_images(columns): + need(len(columns) == H and all(type(c) is int and 0 <= c < 512 for c in columns), "warp columns") + images = [0] * 512 + for x in range(1, 512): + low = x & -x + images[x] = images[x ^ low] ^ columns[low.bit_length() - 1] + need(len(set(images)) == 512, "singular warp") + return images + + +def image_mask(images, mask): + return sum(1 << images[x] for x in range(512) if mask >> x & 1) + + +def frame_bill(word): + """Chronological rank histogram. A climb of dimension d pays one rank-d child.""" + report = word["report"] + need((report["v"], 2 * report["v"] + report["R"], report["h"]) == (V, N, H), "geometry") + removed = set(word["removed"]) + live = [r for r in range(N) if r not in removed] + liveset = set(live) + block, warps = word["B"], word["warps"] + for index, (pos, lender, cols, upper, lower) in enumerate(warps, 1): + need(0 <= pos <= len(block) and lender in liveset and not is_target(lender), "warp", index) + images = warp_images(cols) + need(image_mask(images, span(upper)) == span(lower), "warp frame", index) + ports = word["ports"] + start, end = {}, {} + for register in live: + if register < V: + start[register], end[register] = span((ports[register],)), (1 << 512) - 1 + elif register < 2 * V: + start[register], end[register] = 1, hyperplane(ports[register - V]) + else: + start[register], end[register] = 1, (1 << 512) - 1 + for name, mask in word["start_extra"].items(): + start[int(name)] = span(mask) + for name, mask in word["helper_final"].items(): + end[int(name)] = span(mask) + current, bill, chain = dict(start), Counter(), {} + + def climb(register, mask, where): + need(current[register] & ~mask == 0, "frame containment fails", where, register) + gap = dim(mask) - dim(current[register]) + if gap: + bill[gap] += 1 + chain.setdefault(register, []).append((where, gap)) + current[register] = mask + + prelude = word["prelude_events"] + for index, gate in enumerate(word["A"]): + if gate[0] == "warp": + _, register, op, fr, to = gate + images = warp_images(warps[abs(op) - 1][2]) + if op < 0: + inverse = [0] * 512 + for x, y in enumerate(images): + inverse[y] = x + images = inverse + need(image_mask(images, span(fr)) == span(to), "prelude warp", index) + current[register] = span(to) + else: + for register in touched(gate): + climb(register, span(gate[-1]), ("A", index)) + levels = {V + int(name): span(mask) for name, mask in word["target_levels"].items()} + need(all(current[t] == levels.get(t, 1) for t in range(V, 2 * V)), "target levels") + for centre, frame in zip(word["centers"], word["center_frames"]): + mask = span(frame) + need(current[centre] == mask, "centre entry", centre) + served = {levels.get(gate[1], 1) for gate in word["cut"] if gate[2] == centre} + for target in sorted(served, key=lambda item: -dim(item)): + need(target & ~mask == 0, "centre descent", centre) + gap = dim(mask) - dim(target) + if gap: + bill[gap] += 1 + mask = target + placed = {} + for index, record in enumerate(warps, 1): + placed.setdefault(record[0], []).append(index) + for position in range(len(block) + 1): + for index in placed.get(position, ()): + _, lender, _, upper, lower = warps[index - 1] + need(current[lender] == span(upper), "warp entry", index) + current[lender] = span(lower) + if position < len(block): + for register in touched(block[position]): + climb(register, span(block[position][-1]), ("B", position)) + for index, gate in enumerate(word["restore"]): + for register in touched(gate): + climb(register, span(gate[-1]), ("restore", index)) + for register in live: + climb(register, end[register], ("end",)) + return dict(sorted(bill.items())), chain + + +def ordinary_forward(word): + out = list(word["A"][word["prelude_events"]:]) + placed = {} + for index, record in enumerate(word["warps"], 1): + placed.setdefault(record[0], []).append(["warp", record[1], index]) + for position in range(len(word["B"]) + 1): + out.extend(placed.get(position, [])) + if position < len(word["B"]): + out.append(word["B"][position]) + return out + list(word["restore"]) + + +def derive_cleanup(word): + restored = set(map(int, word["helper_final"])) + + def renamed(register): + return register + N if register in restored else register + + out = [] + for gate in reversed(ordinary_forward(word)): + if gate[0] == "warp": + out.append(["warp", renamed(gate[1]), -gate[2], FULL_VECTORS]) + elif is_target(gate[1]): + continue + elif gate[0] == "add": + out.append(["add", renamed(gate[1]), renamed(gate[2]), -gate[3], gate[4], FULL_VECTORS]) + else: + need(gate[0] == "scale", "unknown gate", gate[0]) + out.append(["scale", renamed(gate[1]), gate[3], gate[2], FULL_VECTORS]) + return out + + +def load_word(name, digest): + raw = (INPUTS / name).read_bytes() + need(hashlib.sha256(raw).hexdigest() == digest, "sha256", name) + with gzip.GzipFile(fileobj=__import__("io").BytesIO(raw)) as handle: + return json.load(handle) + + +def locate(word): + head = [gate[:5] for gate in TRIPLE] + found = [i for i in range(len(word["A"]) - 2) if [g[:5] for g in word["A"][i:i + 3]] == head] + need(found == [1858], "triple site", found) + return 1858 + + +def contracted(word): + edited = deepcopy(word) + index = locate(word) + edited["A"] = word["A"][:index] + deepcopy(PAIR) + word["A"][index + 3:] + edited["cleanup"] = derive_cleanup(edited) + return edited + + +def matrix(gates): + state = [[Fraction(i == j) for j in range(3)] for i in range(3)] + order = {456: 0, 457: 1, 475: 2} + for gate in gates: + dst, src, sign = order[gate[1]], order[gate[2]], Fraction(gate[3], gate[4]) + state[dst] = [state[dst][k] + sign * state[src][k] for k in range(3)] + return state + + +def positive(chain, register): + return [event for event in chain[register] if event[1] > 0] + + +def zero_a(chain, register): + return [event for event in chain[register] if event[1] == 0 and event[0][0] == "A"] + + +def main(): + need(LEMMA in (PACKAGE / "README.md").read_text(encoding="utf-8"), "README dropped the lemma") + need(matrix(TRIPLE) == matrix(PAIR), "contraction map") + flipped = deepcopy(PAIR) + flipped[1][3] = 1 + need(matrix(TRIPLE) != matrix(flipped), "flipped sign was not rejected") + for name, digest, profile in WORDS: + word = load_word(name, digest) + need(derive_cleanup(word) == word["cleanup"], "baseline cleanup", name) + before, chain = frame_bill(word) + need(before == profile, "baseline histogram", name, before) + after_word = contracted(word) + after, chain_after = frame_bill(after_word) + need(after == before, "histogram moved", name, after) + for register in (456, 457, 475): + need(positive(chain, register) == positive(chain_after, register), + "paid climb moved", name, register) + need(chain[457] == chain_after[457], "register 457 sequence moved", name) + before_456 = zero_a(chain, 456) + after_456 = zero_a(chain_after, 456) + need(before_456[-2:] == [(("A", 1859), 0), (("A", 1860), 0)], "456 before", name) + need(after_456[-1:] == [(("A", 1858), 0)] and after_456[:-1] == before_456[:-2], "456 after", name) + need(zero_a(chain, 475) == [(("A", 1858), 0), (("A", 1860), 0)], "475 before", name) + need(zero_a(chain_after, 475) == [(("A", 1859), 0)], "475 after", name) + widened = deepcopy(word) + block = deepcopy(word["A"][1858:1861]) + for gate in block: + gate[-1] = [82, 42, 1] + widened["A"] = word["A"][:1858] + block + word["A"][1861:] + widened["cleanup"] = derive_cleanup(widened) + try: + frame_bill(widened) + except AuditFailure as exc: + need("frame containment fails" in str(exc), "widen control", name, exc) + else: + raise AuditFailure("widened frame was accepted " + name) + dropped = deepcopy(word) + dropped["A"] = word["A"][:1858] + word["A"][1861:] + dropped["cleanup"] = derive_cleanup(dropped) + dropped_bill, _ = frame_bill(dropped) + need(dropped_bill != before, "dropped block kept the histogram", name) + print( + "PASS_A1858_SUCCESSOR_FRAME_CLIMBS words=4 " + "pr3734={1:2440,2:725,3:224,4:102,5:20,8:8} " + "pr3945={1:2482,2:712,3:218,4:109,5:20,8:8} paid_climbs=unchanged" + ) + + +if __name__ == "__main__": + main()