From 949c0ada285aae619468ae49317c8d9f4df130f7 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 10 Nov 2026 02:44:37 +0000 Subject: [PATCH] Exhibit a 104 by 13 factorisation of the P104 window over F_5. The odd graph on the 104 ports partitions into 13 cliques of size 8. Their indicator matrix is a 1-block pattern of rank 13 over every field, so w(P104; F_5) = 13 once the cited lower bound in #6933 is accepted. No new kappa. Co-authored-by: unpaidmoon --- research/p104-f5-rank13-factor/NOTICE | 5 + research/p104-f5-rank13-factor/README.md | 60 ++++++ research/p104-f5-rank13-factor/SOURCES.json | 18 ++ research/p104-f5-rank13-factor/TASK.json | 75 +++++++ research/p104-f5-rank13-factor/VERIFY.json | 14 ++ .../p104-f5-rank13-factor/data/frames.json | 157 ++++++++++++++ .../data/ports_p104.json | 109 ++++++++++ research/p104-f5-rank13-factor/verify.py | 197 ++++++++++++++++++ 8 files changed, 635 insertions(+) create mode 100644 research/p104-f5-rank13-factor/NOTICE create mode 100644 research/p104-f5-rank13-factor/README.md create mode 100644 research/p104-f5-rank13-factor/SOURCES.json create mode 100644 research/p104-f5-rank13-factor/TASK.json create mode 100644 research/p104-f5-rank13-factor/VERIFY.json create mode 100644 research/p104-f5-rank13-factor/data/frames.json create mode 100644 research/p104-f5-rank13-factor/data/ports_p104.json create mode 100644 research/p104-f5-rank13-factor/verify.py diff --git a/research/p104-f5-rank13-factor/NOTICE b/research/p104-f5-rank13-factor/NOTICE new file mode 100644 index 0000000000..e67d02725a --- /dev/null +++ b/research/p104-f5-rank13-factor/NOTICE @@ -0,0 +1,5 @@ +The file data/ports_p104.json is byte-identical to research/p104-port-window-haemers/data/ports_p104.json in PR #6933 (DreamingOfClouds), commit a967c521074c2c7450255ae237fda9c439ee1b35. That package is Apache-2.0. + +Prepared by DreamingOfClouds with Claude Opus 5.5 (Anthropic), via Claude Code. + +The clique partition, factorisation check and this note were prepared by unpaidmoon with substantial Cursor Grok assistance. Apache-2.0. diff --git a/research/p104-f5-rank13-factor/README.md b/research/p104-f5-rank13-factor/README.md new file mode 100644 index 0000000000..b0e33ea16c --- /dev/null +++ b/research/p104-f5-rank13-factor/README.md @@ -0,0 +1,60 @@ +# A 104 by 13 factorisation of the P104 window over F_5 + +**No new κ.** The selected record stays κ = `781451/250000000`. + +## Scope and evidence + +Original card: `complex-rank-shape-decoder`. Scope status: **changed**. The card asks whether a decoder or coframe change improves the signed moment. This package answers a different question, open question 3 of [#6933](https://github.com/CrocSwap/integer-mult-bounds/pull/6933): one 104×13 factorisation of the odd-character P104 window over `F_5`. No moment is computed. + +**Witness.** The odd graph on the 104 ports has a partition into 13 cliques of size 8. The cliques are `data/frames.json`. Send each port to the standard basis vector of its clique in `F^13`, and let `W` be the resulting 104×13 matrix over `F_5`. Then `M = W W^T` satisfies + +- `M_ii = 1` for every port, +- `M_ij = 0` whenever distinct ports `i, j` are orthogonal over `F_2`. + +So `M` is a 1-block pattern of rank 13. One row from each clique is an identity minor, so the rank is 13 over every field, including `F_5`. This is an unrestricted factorisation: the matrix is explicit, and the check does not depend on a gauge, a degree bound, or a constant value on the edges. + +**What that implies.** In the notation of #6933, `w_1(P104; F) ≤ 13` and therefore `w(P104; F) ≤ 13` for every field `F`. Over `F_5` the same matrix is the required 104×13 factorisation. Accepting #6933, Theorem 4, without replaying it, `w(P104; F) ≥ 13` whenever `char F ≠ 2`. The two bounds give + +`w(P104; F_5) = 13`, + +and the same equality for every field of characteristic other than 2. The lower bound is cited. The upper bound is the witness in this package. + +The 16 weight-3-mod-4 vectors omitted from P104 form two further odd-graph 8-cliques (`excluded_odd_cliques` in the same file). Adjoining either clique gives a 112×14 factorisation, and adjoining both gives a 120×15 factorisation, by the same rule. Accepting Theorem 4 again, `w = 14` on P112 and `w = 15` on P120 for every field of characteristic other than 2. + +Characteristic 2 is unchanged. #6933 already has `w(P104; F_2) ∈ [53/6, 9]` and `w = 9` on P112 and P120. The witness gives only the weaker ceiling 13 there. The rank-36 ceiling of [#6946](https://github.com/CrocSwap/integer-mult-bounds/pull/6946) and the characteristic-3 class-constant argument in [discussion comment 19507187](https://github.com/CrocSwap/integer-mult-bounds/discussions/1166#discussioncomment-19507187) are not replayed. The same 0-1 matrix has rank 13 in characteristic 3, which is a separate statement from those arguments. + +## Overlap + +Balanced `find_overlap.py --all-terms` queries against open PRs and discussions, cache refreshed 2026-11-10 with complete pagination (895 open PR records, 6953 discussion records): + +- `P104`, `F_5`, `factorisation` +- `F_5`, `rank-13` +- `w(P104`, `13` +- `8-clique`, `P104` + +No open PR exhibits a 104×13 factorisation. The hits are #6933 (lower bound 13, odd-character upper bound left open), #6946 and [comment 19492602](https://github.com/CrocSwap/integer-mult-bounds/discussions/1166#discussioncomment-19492602) (upper bound 36 off characteristic 3, and 38 in characteristic 3), and [comment 19507187](https://github.com/CrocSwap/integer-mult-bounds/discussions/1166#discussioncomment-19507187), which states that the `F_5` rank-13 factorisation was not touched. Local notes that mention an 8-clique of E8 lines use the opposite relation, pairwise non-orthogonal lines, or a decoder column-space rank. They are not this zero pattern. + +## Reproduce + +From the repository root, CPython 3.12, standard library only: + +```sh +python3 -B research/p104-f5-rank13-factor/verify.py +python3 -I -O -B research/p104-f5-rank13-factor/verify.py +python3 -B scripts/research_package.py --package research/p104-f5-rank13-factor +python3 -B scripts/check_research_commit.py --package research/p104-f5-rank13-factor --check-source-history +``` + +The port file is byte-identical to #6933's `data/ports_p104.json` at `a967c521074c2c7450255ae237fda9c439ee1b35`, sha256 `691c7b7d212112115b1bfc76db89f90d304ec448dc8c4a0c43d9a5954317146c`. The verifier checks that hash, the partition, the mask, the ranks, and two mutants (an orthogonal pair forced into one frame, and two frames given the same coordinate). + +## Open next questions + +The odd-character window values 13, 14 and 15 are settled by this witness together with the cited Theorem 4. The remaining gap that would affect a paid profile is physical: + +Can the 13 frame-indicator rows be hosted as a legal physical P104 label word whose complete signed bill beats the selected profile? The mask rank is not a supplier price. First step: host one frame indicator in the existing P104 label interface and charge every new monomial before touching the other twelve frames. Blocked by the fact that #6933 does not derive a physical realisation of the block mask, and this package does not supply one. + +## Credit + +Port integers: DreamingOfClouds, PR #6933, Apache-2.0. Odd-character ceilings that this witness improves: #6946 and discussion comments 19492602 and 19507187. + +Prepared by unpaidmoon with substantial Cursor Grok assistance. Apache-2.0. diff --git a/research/p104-f5-rank13-factor/SOURCES.json b/research/p104-f5-rank13-factor/SOURCES.json new file mode 100644 index 0000000000..594089b9c7 --- /dev/null +++ b/research/p104-f5-rank13-factor/SOURCES.json @@ -0,0 +1,18 @@ +{ + "schema_version": 1, + "base_commit": "7c49fe32c55de02c2c873655c804b16e4c3fef0a", + "files": [ + { + "path": "certificates/selected-result.json", + "usage": "provenance", + "git_blob": "d59b5035e7b4cbaa9d688a1d32dd0d6eb79c466a", + "sha256": "9b8d0eea95b3bf0dfcb7938a732a6439b87a2685dec2a5ab9e525b3b34debbfa" + }, + { + "path": "docs/research/agent-task-catalog.json", + "usage": "provenance", + "git_blob": "3404fbdd5e7529a0674eef26114b5071e4586cec", + "sha256": "ead6cdc9d0af6d58e8a49744d8ec8c7c15e13d2ba93c276c434bcf9b51b93a81" + } + ] +} diff --git a/research/p104-f5-rank13-factor/TASK.json b/research/p104-f5-rank13-factor/TASK.json new file mode 100644 index 0000000000..973cea96b8 --- /dev/null +++ b/research/p104-f5-rank13-factor/TASK.json @@ -0,0 +1,75 @@ +{ + "schema_version": 1, + "task_id": "complex-rank-shape-decoder", + "priority_revision": 8, + "base_commit": "7c49fe32c55de02c2c873655c804b16e4c3fef0a", + "original_card": { + "id": "complex-rank-shape-decoder", + "question": "Can a legal decoder or coframe change improve the full signed moment, including changed helper targets?", + "starting_points": [ + "docs/research/current-priorities.md", + "docs/research/research-family-status.md", + "certificates/selected-result.json", + "docs/research/pr-intake-evidence/pr5599-completion/PROOF-REVIEW.md", + "docs/research/recursive-invoice-preflight.md" + ], + "methods": [ + "Construct and independently check one source-bound instance of the stated first experiment.", + "Seek an exact counterexample or target-specific complete-budget obstruction to that same proposed change." + ], + "acceptance_gate": "A source-bound arbitrary-input identity and legal producer/consumer boundary with the complete paid and productive target invoice, including restoration, ownership, native alignment and extra charges. Price under the actual current model; historical ceiling.py assumptions must be established before use. A favorable optimistic relaxation keeps a route open but is not a completed supplier. A scoped local result remains useful; expansion requires the family-level budget gate.", + "first_step": { + "command": [ + "python3", + "-B", + "scripts/research_selected_baseline.py" + ], + "expected": "PASS_RESEARCH_BASELINE_BINDINGS_ONLY", + "inspect": "Read revision 8, the selected review and live exact-source handoffs. Then follow this card's bounded first_experiment.", + "scope": "Source-bound orientation only, not a supplier replay or theorem proof." + }, + "first_experiment": "Price one complete changed itinerary with both paid and productive histograms. Check #5693/#5698 for overlap and their exact hypotheses before another coframe search. Use the signed invoice preflight where its boundary contract agrees; an inconclusive concave comparison is not a power crossing. Stop if an optimistic complete budget fails at the chosen target or native delivery remains unspecified.", + "bottleneck_link": "Judge the actual current supplier contract and complete signed target/paid bill; frozen-profile ceilings require an applicability proof.", + "allowed_variations": [ + "One exact source head, helper/cut region or quantified proposition and changed hypothesis; check live handoffs first. Different seeds are not different scopes." + ], + "scope_change_triggers": [ + "Changing source head, helper/cut domain, operation alphabet or caller boundary without recording the new scope.", + "Treating an archived proposal or local numerical pass as complete supplier admission." + ], + "overlap_terms": [ + "coframe", + "signed moment", + "5693", + "5698" + ], + "existing_checks": [ + { + "scope": "Selected PR5599: complete helper-subspace construction and independent caller replay; five framework assumptions remain.", + "evidence": "docs/research/pr-intake-evidence/pr5599-completion/PROOF-REVIEW.md", + "remaining": "The next experiment must identify uncovered source-specific work. Successor archive intake does not transfer this admission.", + "status": "reviewed", + "source_commit": "09dd0965711ca7eeb169057a420abab1c9052137", + "selected_record_sha256": "2a0220e866417774606e0f991e41b9c1d26b1f43b97857238cf478dbc7d330b5" + } + ] + }, + "scope_status": "changed", + "actual_scope": "Exhibit one 104 by 13 factorisation of the P104 odd-graph window over F_5, and record the window bound that factorisation proves.", + "scope_change_reason": "The catalogue card asks whether a decoder or coframe change improves the signed moment. The assigned question is open question 3 of PR #6933: the odd-character value of w(P104). A signed-moment search would not decide that factorisation, and this package computes no moment.", + "bottleneck_link": "Side result on the port-window mask. It does not change the selected paid profile or the signed-moment bill. A kappa effect would still need a physical word that realises the mask.", + "budget_minutes": 60, + "stop_rule": "Stop when a checked 104 by 13 factorisation over F_5 is in hand. Do not rerun the rank-36 ceiling, the characteristic-3 class-constant argument, or characteristic 2.", + "outcome": "completed", + "evidence": "verify.py checks the stored 13-clique partition, the 0-1 factorisation, rank 13 over F_2, F_3, F_5 and F_7, and failure controls. PASS_P104_F5_RANK13.", + "author": "unpaidmoon", + "ai_assistance": "Cursor Grok provided substantial assistance with the search, the verifier and the write-up.", + "open_questions": [ + { + "question": "Can the 13 frame-indicator rows be hosted as a legal physical P104 label word whose complete signed bill beats the selected profile?", + "why_it_matters": "The mask rank is not a supplier price. The selected frontier still decides kappa.", + "first_step": "Host one frame indicator in the existing P104 label interface and charge every new monomial before touching the other twelve frames.", + "blocked_by": "PR #6933 defines the window by the block mask and does not derive a physical realisation. This package does not supply one." + } + ] +} diff --git a/research/p104-f5-rank13-factor/VERIFY.json b/research/p104-f5-rank13-factor/VERIFY.json new file mode 100644 index 0000000000..65dcab2fa8 --- /dev/null +++ b/research/p104-f5-rank13-factor/VERIFY.json @@ -0,0 +1,14 @@ +{ + "schema_version": 1, + "status": "ready", + "python": "3.12", + "requirements": [], + "inputs": [], + "commands": [ + { + "argv": ["{python}", "-B", "{package}/verify.py"], + "timeout_seconds": 60, + "checks": "Port-list sha256, the 13-clique partition, the 0-1 factorisation mask, rank 13 over F_2, F_3, F_5 and F_7, the identity minor, both P112 extensions and the P120 extension, plus orthogonal-pair and merged-frame failure controls." + } + ] +} diff --git a/research/p104-f5-rank13-factor/data/frames.json b/research/p104-f5-rank13-factor/data/frames.json new file mode 100644 index 0000000000..793a32f489 --- /dev/null +++ b/research/p104-f5-rank13-factor/data/frames.json @@ -0,0 +1,157 @@ +{ + "description": "Disjoint 8-cliques of the odd graph. frames partition the 104 P104 ports. excluded_odd_cliques are the two 8-cliques in the 120 weight-3-mod-4 vectors that P104 omits; together with frames they partition those 120 vectors. Integers are F_2^9 ports, bit i coordinate i.", + "frames": [ + [ + 11, + 21, + 38, + 56, + 140, + 146, + 161, + 191 + ], + [ + 13, + 26, + 131, + 148, + 262, + 273, + 392, + 415 + ], + [ + 14, + 69, + 164, + 239, + 276, + 351, + 446, + 501 + ], + [ + 19, + 41, + 74, + 112, + 280, + 290, + 321, + 379 + ], + [ + 22, + 37, + 196, + 247, + 268, + 319, + 478, + 493 + ], + [ + 28, + 49, + 133, + 168, + 265, + 292, + 400, + 445 + ], + [ + 35, + 81, + 176, + 194, + 274, + 352, + 385, + 499 + ], + [ + 44, + 84, + 152, + 224, + 304, + 328, + 388, + 508 + ], + [ + 50, + 70, + 138, + 254, + 259, + 375, + 443, + 463 + ], + [ + 73, + 98, + 208, + 251, + 324, + 367, + 477, + 502 + ], + [ + 88, + 100, + 193, + 253, + 322, + 382, + 475, + 487 + ], + [ + 134, + 145, + 200, + 223, + 416, + 439, + 494, + 505 + ], + [ + 261, + 296, + 336, + 381, + 386, + 431, + 471, + 506 + ] + ], + "excluded_odd_cliques": [ + [ + 7, + 25, + 42, + 52, + 76, + 82, + 97, + 127 + ], + [ + 67, + 104, + 137, + 162, + 266, + 289, + 448, + 491 + ] + ] +} diff --git a/research/p104-f5-rank13-factor/data/ports_p104.json b/research/p104-f5-rank13-factor/data/ports_p104.json new file mode 100644 index 0000000000..8037fd1087 --- /dev/null +++ b/research/p104-f5-rank13-factor/data/ports_p104.json @@ -0,0 +1,109 @@ +{ +"description": "The 104 ports of P104 as vectors of F_2^9 (bit i of the integer is coordinate i), in a fixed order; two distinct ports are adjacent in the odd graph iff popcount(a & b) is odd.", +"ports": [ +11, +19, +35, +131, +259, +13, +21, +37, +69, +133, +261, +41, +73, +265, +49, +81, +145, +273, +161, +193, +321, +385, +14, +22, +38, +70, +134, +262, +26, +74, +138, +50, +146, +274, +98, +290, +194, +322, +386, +28, +44, +140, +268, +84, +148, +276, +100, +164, +292, +196, +324, +388, +56, +88, +152, +280, +168, +296, +200, +328, +392, +112, +176, +304, +208, +336, +400, +224, +352, +416, +508, +506, +502, +494, +478, +446, +382, +254, +505, +501, +493, +477, +445, +381, +253, +499, +475, +443, +379, +251, +487, +471, +439, +375, +247, +463, +431, +367, +239, +415, +351, +223, +319, +191 +] +} \ No newline at end of file diff --git a/research/p104-f5-rank13-factor/verify.py b/research/p104-f5-rank13-factor/verify.py new file mode 100644 index 0000000000..808fdedc9c --- /dev/null +++ b/research/p104-f5-rank13-factor/verify.py @@ -0,0 +1,197 @@ +#!/usr/bin/env python3 +"""Check the 104 by 13 factorisation of the P104 odd-graph window. + +The witness is a partition of the 104 ports into 13 cliques of the odd graph. +Port i is sent to the standard basis vector of its clique in F^13. The Gram +matrix is a 1-block pattern of rank 13 over every field: diagonal 1, and a +zero on every F_2-orthogonal pair because such a pair does not lie in a clique. + +Standard library only. The port integers are pinned to the bytes shipped with +PR #6933; this program does not replay that paper's lower bound. +""" +import hashlib +import json +from itertools import combinations +from pathlib import Path + +ROOT = Path(__file__).resolve().parent +PORTS_PATH = ROOT / "data" / "ports_p104.json" +FRAMES_PATH = ROOT / "data" / "frames.json" +PORTS_SHA256 = "691c7b7d212112115b1bfc76db89f90d304ec448dc8c4a0c43d9a5954317146c" +FIELDS = (2, 3, 5, 7) + + +def dot2(a, b): + return bin(a & b).count("1") & 1 + + +def rank(rows, prime): + matrix = [[entry % prime for entry in row] for row in rows] + row_count, col_count = len(matrix), len(matrix[0]) + rank_value = 0 + for col in range(col_count): + pivot = next((row for row in range(rank_value, row_count) if matrix[row][col] % prime), None) + if pivot is None: + continue + matrix[rank_value], matrix[pivot] = matrix[pivot], matrix[rank_value] + inverse = pow(matrix[rank_value][col], -1, prime) + matrix[rank_value] = [(entry * inverse) % prime for entry in matrix[rank_value]] + for row in range(row_count): + if row != rank_value and matrix[row][col] % prime: + factor = matrix[row][col] + matrix[row] = [(matrix[row][col_index] - factor * matrix[rank_value][col_index]) % prime + for col_index in range(col_count)] + rank_value += 1 + if rank_value == row_count: + break + return rank_value + + +def factor_rows(frames): + width = len(frames) + rows = [] + for frame_index, frame in enumerate(frames): + for _port in frame: + row = [0] * width + row[frame_index] = 1 + rows.append(row) + return rows + + +def identity_minor(frames): + """One port from each frame, in frame order, is a permutation matrix.""" + width = len(frames) + minor = [] + for frame_index, frame in enumerate(frames): + row = [0] * width + row[frame_index] = 1 + minor.append(row) + return minor + + +def clique_faults(frames): + faults = [] + for frame_index, frame in enumerate(frames): + if len(frame) != 8 or len(set(frame)) != 8: + faults.append(("frame-size", frame_index, len(frame))) + for left, right in combinations(frame, 2): + if dot2(left, right) == 0: + faults.append(("orthogonal-inside-frame", frame_index, left, right)) + return faults + + +def mask_faults(frames): + """Rebuild M = W W^T and test the 1-block mask on these ports.""" + ports = [port for frame in frames for port in frame] + rows = factor_rows(frames) + faults = [] + for index, (port, row) in enumerate(zip(ports, rows)): + if sum(entry * entry for entry in row) % 5 != 1: + faults.append(("diagonal", port)) + for other_index in range(index): + other = ports[other_index] + if dot2(port, other): + continue + dot = sum(left * right for left, right in zip(row, rows[other_index])) % 5 + if dot != 0: + faults.append(("orthogonal-dot", port, other, dot)) + return faults + + +def load(): + port_bytes = PORTS_PATH.read_bytes() + digest = hashlib.sha256(port_bytes).hexdigest() + if digest != PORTS_SHA256: + raise SystemExit("port-list sha256 mismatch: " + digest) + ports = json.loads(port_bytes.decode())["ports"] + frames_doc = json.loads(FRAMES_PATH.read_text(encoding="utf-8")) + return ports, frames_doc["frames"], frames_doc["excluded_odd_cliques"] + + +def main(): + ports, frames, excluded = load() + if len(ports) != 104 or len(set(ports)) != 104: + raise SystemExit("expected 104 distinct ports") + orthogonal_pairs = sum( + 1 for left, right in combinations(ports, 2) if dot2(left, right) == 0 + ) + if orthogonal_pairs != 2496: + raise SystemExit("orthogonal-pair count is {0}, expected 2496".format(orthogonal_pairs)) + covered = [port for frame in frames for port in frame] + if sorted(covered) != sorted(ports): + raise SystemExit("frames are not a partition of the P104 port list") + faults = clique_faults(frames) + mask_faults(frames) + if faults: + raise SystemExit("witness failed: " + repr(faults[:5])) + rows = factor_rows(frames) + ranks = {prime: rank(rows, prime) for prime in FIELDS} + if any(value != 13 for value in ranks.values()): + raise SystemExit("rank is not 13: " + repr(ranks)) + minor = identity_minor(frames) + if any(rank(minor, prime) != 13 for prime in FIELDS): + raise SystemExit("identity minor is singular") + + # Failure controls. Each mutant must trip the mask or the rank check. + orthogonal_pair = None + for left_index, left in enumerate(frames): + for right in frames[left_index + 1:]: + for a in left: + for b in right: + if dot2(a, b) == 0: + orthogonal_pair = (left_index, a, b) + break + if orthogonal_pair: + break + if orthogonal_pair: + break + if orthogonal_pair: + break + if orthogonal_pair is None: + raise SystemExit("control setup found no cross-frame orthogonal pair") + frame_index, port_a, port_b = orthogonal_pair + # Put both endpoints of an orthogonal pair in one frame. + host = next(index for index, frame in enumerate(frames) if port_b in frame) + moved = [list(frame) for frame in frames] + moved[frame_index] = [port for port in moved[frame_index] if port != port_a] + moved[host] = list(moved[host]) + [port_a] + if not clique_faults(moved): + raise SystemExit("orthogonal-pair mutant was accepted") + shared = [list(frame) for frame in frames] + # Give frame 0 and frame 1 the same factor coordinate by merging their labels. + merged = [shared[0] + shared[1]] + shared[2:] + if rank(factor_rows(merged), 5) != 12 or not mask_faults(merged): + raise SystemExit("merged-frame mutant was accepted") + + excluded_faults = [] + flat_excluded = [port for clique in excluded for port in clique] + if len(excluded) != 2 or any(len(clique) != 8 for clique in excluded): + excluded_faults.append("shape") + if len(set(flat_excluded)) != 16 or set(flat_excluded) & set(ports): + excluded_faults.append("overlap") + excluded_faults.extend(clique_faults(excluded)) + if excluded_faults: + raise SystemExit("excluded cliques failed: " + repr(excluded_faults[:5])) + p112_ranks = [] + for clique in excluded: + p112_frames = list(frames) + [clique] + p112_rows = factor_rows(p112_frames) + p112_ranks.append({prime: rank(p112_rows, prime) for prime in FIELDS}) + if any(value != 14 for value in p112_ranks[-1].values()) or mask_faults(p112_frames): + raise SystemExit("P112 extension failed") + p120_rows = factor_rows(list(frames) + list(excluded)) + p120_ranks = {prime: rank(p120_rows, prime) for prime in FIELDS} + if any(value != 15 for value in p120_ranks.values()): + raise SystemExit("P120 extension rank is not 15") + if mask_faults(list(frames) + list(excluded)): + raise SystemExit("P120 mask failed") + + print( + "PASS_P104_F5_RANK13 rank13=13 fields={fields} p112=14 p120=15 orth_pairs_split={pairs}".format( + fields=",".join(str(prime) for prime in FIELDS), + pairs=orthogonal_pairs, + ) + ) + + +if __name__ == "__main__": + main()