From 1f032798c377af4404936dc26901c278be579ff5 Mon Sep 17 00:00:00 2001 From: Cinna Davis <1337isnot1337@users.noreply.github.com> Date: Wed, 14 Oct 2026 23:18:15 -0500 Subject: [PATCH] Add exact two-call Q8 register exclusion --- .../README.md | 100 +++++++++ .../RESULT.json | 62 ++++++ .../SOURCES.json | 30 +++ .../TASK.json | 63 ++++++ .../VERIFY.json | 30 +++ .../core.py | 135 +++++++++++ .../search.py | 30 +++ .../verify.py | 209 ++++++++++++++++++ 8 files changed, 659 insertions(+) create mode 100644 research/bit-q8-three-register-two-call-seed189ad74/README.md create mode 100644 research/bit-q8-three-register-two-call-seed189ad74/RESULT.json create mode 100644 research/bit-q8-three-register-two-call-seed189ad74/SOURCES.json create mode 100644 research/bit-q8-three-register-two-call-seed189ad74/TASK.json create mode 100644 research/bit-q8-three-register-two-call-seed189ad74/VERIFY.json create mode 100644 research/bit-q8-three-register-two-call-seed189ad74/core.py create mode 100644 research/bit-q8-three-register-two-call-seed189ad74/search.py create mode 100644 research/bit-q8-three-register-two-call-seed189ad74/verify.py diff --git a/research/bit-q8-three-register-two-call-seed189ad74/README.md b/research/bit-q8-three-register-two-call-seed189ad74/README.md new file mode 100644 index 0000000000..e38c8bbf0f --- /dev/null +++ b/research/bit-q8-three-register-two-call-seed189ad74/README.md @@ -0,0 +1,100 @@ +# Two paid Q8 rank-one factors do not realize `diag(i,j,k)` + +**Result: scoped finite exclusion in an abstract noncommutative register model.** +Each paid factor has unit cost in the model below. Exhausting all 38,416 ordered +two-factor words finds no realization of the three-register diagonal target. +The nearest word outputs have coefficient Hamming distance two. One factor is +also excluded by a complete 196-word census, and three factors realize the +target, so the minimum is exactly three in this alphabet. + +## Model and statement + +Work in the group algebra `F2[Q8]`, with labels ordered +`1,i,j,k,-1,-i,-j,-k`. For each binary rank-one idempotent +`N = x y^T` with `x,y in F2^3` and `y^T x = 1`, and each nonidentity +`g in Q8`, define the paid factor + +```text +D_N(g) = I + (g + 1) N. +``` + +There are 28 such idempotents and seven allowed group labels. The exact +question is whether any ordered pair `D_N(g) D_M(h)` equals +`diag(i,j,k)` as a 3-by-3 matrix over the group algebra. Equality is checked +coefficient by coefficient in all 72 binary coordinates (nine matrix entries +times eight Q8 labels). + +**Finite result.** No one- or two-factor word equals the target. The one-factor +census covers `28 * 7 = 196` words and has minimum coefficient Hamming distance +four. The two-factor census covers `28 * 7 * 28 * 7 = 38,416` words and reports +minimum distance two; six words attain that distance. The ordered two-factor +outputs have SHA-256 +`2dad5d3c5068f65b2cd18be66ddef342cd38cce13b07b36c270bd9c99c845305`. The boundary control +`D_E11(i) D_E22(j) D_E33(k) = diag(i,j,k)` verifies that three paid factors +suffice. Thus three is the exact minimum under this factor-count convention. + +This changes the label algebra to the nonabelian quaternion group while keeping +three binary register roles and the 28 rank-one idempotents familiar from the +abelian three-register work. It is a narrower, different alphabet census than +the GL3 two-role model in [the mixed-target predecessor](../bit-gl3-mixed-targets-length10/README.md) +and the all-rational-frame result in [the three-register predecessor](../bit-three-register-lower-bound/README.md). +The nearest active overlap is [PR #2248](https://github.com/CrocSwap/integer-mult-bounds/pull/2248), +which studies `diag(-1,-1,i/j)` and `diag(-1,-1,-1)` with paid labels `{i,j}` +and proves minima five and six. This package instead studies `diag(i,j,k)` and +allows every nonidentity Q8 label, exhausting only lengths one and two. The +claim and scope history are in [Discussion #2300](https://github.com/CrocSwap/integer-mult-bounds/discussions/2300). + +## Cost and limits + +Each `D_N(g)` is charged one abstract factor unit, with no free factor inserted +between calls. This model does not price a physical supplier, recursive child, +source access, register conversion, hosting, restoration or precision. No such +supplier is established here, and this result does not change the selected +conditional bound or `kappa`. It only rules out one- and two-factor +realizations of this target in the stated finite alphabet. + +The verifier is an exact finite computation, not a proof-assistant certificate +or a universal statement about other groups, register counts, additional free +operations, or longer words. + +## Cycle pins + +- Seed: `189ad74f7eb7b99d43830c0ed0f15958`; track: `bit-mechanisms`; + catalogue task: `bit-new-register-model`; priority revision: 4. +- Base commit: `0fdd7d82de03e43c9e5a1000dfbcd380d1b687d2`. +- Task-catalogue SHA-256: `d46eb6d5b3e279428753d770653663fbdf1063e55c255ea073d768f344927ef7`. +- Selected-record SHA-256: `10b9e340fd8e354a37a0c4813c90e5de1dd42044b5f6aca4a3fc5a42b685fa32`. + +## Reproduce + +From the repository root, regenerate the search receipt and independently +replay it using set-based group-algebra arithmetic: + +```sh +python3 -B research/bit-q8-three-register-two-call-seed189ad74/search.py +python3 -B research/bit-q8-three-register-two-call-seed189ad74/verify.py +python3 scripts/research_package.py --package research/bit-q8-three-register-two-call-seed189ad74 --check-only +``` + +The verifier checks the literal Q8 multiplication table and associativity, +idempotent coverage, the one- and two-factor censuses, the three-factor positive +control, the output digest, and rejection of a non-idempotent rank-one control. Source +commits, blobs and hashes are pinned in [SOURCES.json](SOURCES.json). The +selected record and task catalogue are historical provenance pins. + +## Open next questions + +If a physical supplier for Q8-labeled factors is found, can one `D_N(g)` be +implemented with a complete all-size interface at less than one rank-one +factor's cost after conversion and restoration? This determines whether the +nonabelian label model can affect the multiplication bound. The first step is +to map one factor to a source and interface schedule and fully charge it. The +current blocker is that this package identifies no such supplier. This is +recorded with its first step and blocker in the structured +[task metadata](TASK.json#L57). + +## Credit + +Prepared by Cinna Davis with substantial OpenAI Codex assistance. The model +choice and finite scope were recorded in Discussion #2300. New work is +Apache-2.0; cited predecessor work remains attributed to its authors. diff --git a/research/bit-q8-three-register-two-call-seed189ad74/RESULT.json b/research/bit-q8-three-register-two-call-seed189ad74/RESULT.json new file mode 100644 index 0000000000..3679d957c4 --- /dev/null +++ b/research/bit-q8-three-register-two-call-seed189ad74/RESULT.json @@ -0,0 +1,62 @@ +{ + "base_commit": "0fdd7d82de03e43c9e5a1000dfbcd380d1b687d2", + "direct_output_bytes": [ + 2, + 0, + 0, + 0, + 4, + 0, + 0, + 0, + 8 + ], + "direct_positive_control_calls": 3, + "one_call_domain": { + "alphabet_words": 196, + "distance_histogram": { + "10": 42, + "12": 36, + "16": 3, + "18": 4, + "4": 3, + "6": 30, + "8": 78 + }, + "idempotents": 28, + "minimum_coefficient_hamming_distance": 4, + "nonidentity_group_labels": 7, + "ordered_word_output_sha256": "b6f644ad688c719956d0daf2968b6e751df9c1f61afb3a54e47aec46c0165b0e", + "target_hits": 0 + }, + "priority_revision": 4, + "schema_version": 1, + "seed": "189ad74f7eb7b99d43830c0ed0f15958", + "selected_record_sha256": "10b9e340fd8e354a37a0c4813c90e5de1dd42044b5f6aca4a3fc5a42b685fa32", + "target": "diag([i],[j],[k])", + "task_id": "bit-new-register-model", + "track": "bit-mechanisms", + "two_call_domain": { + "alphabet_words": 38416, + "distance_histogram": { + "10": 9108, + "12": 8736, + "14": 4110, + "16": 4104, + "18": 2184, + "2": 6, + "20": 1659, + "22": 477, + "24": 291, + "26": 21, + "4": 174, + "6": 1690, + "8": 5856 + }, + "idempotents": 28, + "minimum_coefficient_hamming_distance": 2, + "nonidentity_group_labels": 7, + "ordered_word_output_sha256": "2dad5d3c5068f65b2cd18be66ddef342cd38cce13b07b36c270bd9c99c845305", + "target_hits": 0 + } +} diff --git a/research/bit-q8-three-register-two-call-seed189ad74/SOURCES.json b/research/bit-q8-three-register-two-call-seed189ad74/SOURCES.json new file mode 100644 index 0000000000..0b7b41c192 --- /dev/null +++ b/research/bit-q8-three-register-two-call-seed189ad74/SOURCES.json @@ -0,0 +1,30 @@ +{ + "schema_version": 1, + "base_commit": "0fdd7d82de03e43c9e5a1000dfbcd380d1b687d2", + "files": [ + { + "path": "certificates/selected-result.json", + "usage": "provenance", + "git_blob": "ae6a99f28aa468649eb66a361e764d769363bf11", + "sha256": "10b9e340fd8e354a37a0c4813c90e5de1dd42044b5f6aca4a3fc5a42b685fa32" + }, + { + "path": "docs/research/agent-task-catalog.json", + "usage": "provenance", + "git_blob": "e8870e5917057d59b730eb14721a778639eca050", + "sha256": "d46eb6d5b3e279428753d770653663fbdf1063e55c255ea073d768f344927ef7" + }, + { + "path": "research/bit-gl3-mixed-targets-length10/README.md", + "usage": "input", + "git_blob": "59ac7008af69ffb41e10e6574308d68c33d44c48", + "sha256": "f1f88a618fa998baa2bb5e8bc69c225405b6106b4c0a5646d84c42166cd4aff5" + }, + { + "path": "research/bit-three-register-lower-bound/README.md", + "usage": "input", + "git_blob": "25e41c370c101a33a6deb978de08b03ed7539b79", + "sha256": "c12b33ef388deece0cbf891505a2ce2c0779a0c3a6cf1f0f7f7039bab59cbebd" + } + ] +} diff --git a/research/bit-q8-three-register-two-call-seed189ad74/TASK.json b/research/bit-q8-three-register-two-call-seed189ad74/TASK.json new file mode 100644 index 0000000000..5addb832cc --- /dev/null +++ b/research/bit-q8-three-register-two-call-seed189ad74/TASK.json @@ -0,0 +1,63 @@ +{ + "schema_version": 1, + "task_id": "bit-new-register-model", + "priority_revision": 4, + "base_commit": "0fdd7d82de03e43c9e5a1000dfbcd380d1b687d2", + "original_card": { + "id": "bit-new-register-model", + "question": "Can an explicitly larger or nonabelian register model escape the scoped three-register obstruction?", + "starting_points": [ + "research/bit-three-register-lower-bound/README.md", + "certificates/selected-result.json", + "research/bit-gl3-mixed-targets-length10/README.md", + "docs/research/community-efficient1000-review.md" + ], + "methods": [ + "Construct the smallest exact identity in a newly specified legal operation alphabet.", + "Derive a target-specific relaxation bound for that same explicit alphabet." + ], + "acceptance_gate": "Name the changed hypothesis, all counted roles and a uniform identity; finite column tests alone do not certify it.", + "first_step": { + "command": [ + "python3", + "-B", + "scripts/research_baseline.py" + ], + "expected": "PASS_RESEARCH_BASELINE_BINDINGS_ONLY", + "inspect": "Read Model section 1 of the three-register bound and identify the exact excluded free operations.", + "scope": "Positive control / source-bound orientation only; this is not a new construction or full cold replay." + }, + "first_experiment": "First compare the alphabet, role count and length with the fully replayed #1974 census and #2027 mixed-root tariff. Identify a changed premise and a legal native implementation; then price one smallest completed instance before searching larger words.", + "bottleneck_link": "A small-width completed saving changes the bit primitive; a finite-address identity alone is insufficient.", + "allowed_variations": [ + "Choose a fixed larger register count or a specified nonabelian operation family." + ], + "scope_change_triggers": [ + "Remaining inside the already-excluded three-register abelian model.", + "Repeating the two-register GL3 census or a covered three-register length without changing a hypothesis; a formal group-algebra word without native conversion is a partial result." + ], + "overlap_terms": [ + "three-register", + "nonabelian", + "group algebra" + ] + }, + "scope_status": "refined", + "actual_scope": "For the explicit abstract F2[Q8] alphabet D_N(g)=I+(g+1)N with 28 binary rank-one idempotents and g in Q8 minus the identity, exclude one- and two-factor realizations of diag(i,j,k), and verify a three-factor realization.", + "scope_change_reason": "The general new-register card is narrowed to a fixed nonabelian Q8 label algebra, a specified three-register target and the complete one- and two-factor boundary. This isolates whether noncommuting group labels can save one paid factor in a finite, exact alphabet.", + "bottleneck_link": "The target is a three-register bit primitive in a changed label algebra. Each abstract factor is charged one unit; a two-factor realization would be a strict one-unit saving over the direct three-factor control. No physical supplier or recursive interface is established.", + "budget_minutes": 60, + "stop_rule": "Exhaust exactly 28*7 one-factor words and 28*7*28*7 ordered two-factor words, verify the three-factor positive control and independent replay, then stop. Do not extend to longer words in this cycle.", + "outcome": "negative-result", + "evidence": "The one-factor census has 196 words, zero hits and minimum coefficient Hamming distance 4. The two-factor census has 38,416 words, zero hits and minimum distance 2 (six words). A direct three-factor word equals diag(i,j,k). A set-based verifier independently recomputes both censuses, checks all 64 Q8 products and all 512 associativity triples, verifies the 28 idempotents and rejects a non-idempotent control. This is an exact finite exclusion only for the stated alphabet.", + "author": "Cinna Davis", + "ai_assistance": "Substantial OpenAI Codex assistance: model formalization, Python implementation, independent set-based verifier, exact replay and package documentation. The scope claim was posted by the author in Discussion #2300.", + "open_questions": [ + { + "question": "Can a physical supplier implement one Q8-labeled factor with a complete all-size interface at less than one rank-one factor's cost after conversion and restoration?", + "why_it_matters": "A favorable supplier could make this nonabelian label model relevant to the conditional integer-multiplication bound; the current algebraic census alone cannot affect that bound.", + "first_step": "Map one D_N(g) to a concrete source and interface schedule, then charge source access, child calls, conversions, hosting, restoration and precision.", + "blocked_by": "No physical supplier or all-size implementation for D_N(g) is identified in this contribution." + } + ] +} diff --git a/research/bit-q8-three-register-two-call-seed189ad74/VERIFY.json b/research/bit-q8-three-register-two-call-seed189ad74/VERIFY.json new file mode 100644 index 0000000000..bb35dd0792 --- /dev/null +++ b/research/bit-q8-three-register-two-call-seed189ad74/VERIFY.json @@ -0,0 +1,30 @@ +{ + "schema_version": 1, + "status": "ready", + "python": "3.12", + "requirements": [], + "inputs": [ + "research/bit-gl3-mixed-targets-length10/README.md", + "research/bit-three-register-lower-bound/README.md" + ], + "commands": [ + { + "argv": [ + "{python}", + "-B", + "{package}/search.py" + ], + "timeout_seconds": 120, + "checks": "Regenerate exact one- and two-factor census receipts, the 38,416-word output digest, and the direct three-factor positive control." + }, + { + "argv": [ + "{python}", + "-B", + "{package}/verify.py" + ], + "timeout_seconds": 120, + "checks": "Independent set-based replay of both censuses, Q8 table and associativity controls, rank-one idempotent coverage, target control and malformed-idempotent rejection." + } + ] +} diff --git a/research/bit-q8-three-register-two-call-seed189ad74/core.py b/research/bit-q8-three-register-two-call-seed189ad74/core.py new file mode 100644 index 0000000000..7de63460b9 --- /dev/null +++ b/research/bit-q8-three-register-two-call-seed189ad74/core.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python3 +"""Exact F2[Q8] arithmetic for the bounded three-register word search.""" +from __future__ import annotations +import hashlib, json +from pathlib import Path +ROOT=Path(__file__).resolve().parents[2] +PACKAGE=Path(__file__).resolve().parent +SEED='189ad74f7eb7b99d43830c0ed0f15958' +# Element id u+4*sign: u=0,1,2,3 means 1,i,j,k; sign 0 is positive. +UNIT_PRODUCT=( + ((1,0),(1,1),(1,2),(1,3)), + ((1,1),(-1,0),(1,3),(-1,2)), + ((1,2),(-1,3),(-1,0),(1,1)), + ((1,3),(1,2),(-1,1),(-1,0)), +) +GROUP=tuple(range(8)) +NONIDENTITY=tuple(range(1,8)) + +def need(ok,why): + if not ok: raise ValueError(why) + +def qmul(a,b): + sa,ua=divmod(a,4); sb,ub=divmod(b,4) + sign,unit=UNIT_PRODUCT[ua][ub] + neg=(sa^sb^(sign<0)) + return unit+4*int(bool(neg)) + +def ga_mul(a,b): + out=0 + for i in range(8): + if (a>>i)&1: + for j in range(8): + if (b>>j)&1: out^=1<>r)&1)*((y>>c)&1) + for r in range(3) for c in range(3)))) + need(len(result)==28,'rank-one idempotent count') + return result + +def validate_idempotent(N): + need(len(N)==9 and all(v in (0,1) for v in N),'3x3 binary matrix') + for r in range(3): + for c in range(3): + value=0 + for k in range(3): value^=N[3*r+k]&N[3*k+c] + need(value==N[3*r+c],'matrix is not idempotent') + need(sum(N)!=0 and rank_binary(N)==1,'matrix is not rank one') + +def rank_binary(flat): + rows=[sum(flat[3*r+c]<>c)&1),None) + if p is None: continue + rows[rank],rows[p]=rows[p],rows[rank] + for r in range(3): + if r!=rank and ((rows[r]>>c)&1): rows[r]^=rows[rank] + rank+=1 + return rank + +def factor(N,g): + ge=(1< None: + if not ok: + raise ValueError(message) + + +# Literal Cayley table in the label order 1,i,j,k,-1,-i,-j,-k. +Q8 = ( + (0, 1, 2, 3, 4, 5, 6, 7), + (1, 4, 3, 6, 5, 0, 7, 2), + (2, 7, 4, 1, 6, 3, 0, 5), + (3, 2, 5, 4, 7, 6, 1, 0), + (4, 5, 6, 7, 0, 1, 2, 3), + (5, 0, 7, 2, 1, 4, 3, 6), + (6, 3, 0, 5, 2, 7, 4, 1), + (7, 6, 1, 0, 3, 2, 5, 4), +) + + +def xor_sets(left: set[int], right: set[int]) -> set[int]: + return left.symmetric_difference(right) + + +def reference_factor(N: tuple[int, ...], group_label: int) -> tuple[frozenset[int], ...]: + entries = [] + for row in range(3): + for col in range(3): + terms = {0} if row == col else set() + if N[3 * row + col]: + terms.symmetric_difference_update((0, group_label)) + entries.append(frozenset(terms)) + return tuple(entries) + + +def reference_product(A: tuple[frozenset[int], ...], + B: tuple[frozenset[int], ...]) -> tuple[frozenset[int], ...]: + out = [] + for row in range(3): + for col in range(3): + entry: set[int] = set() + for pivot in range(3): + for a in A[3 * row + pivot]: + for b in B[3 * pivot + col]: + entry.symmetric_difference_update((Q8[a][b],)) + out.append(frozenset(entry)) + return tuple(out) + + +def masks(matrix: tuple[frozenset[int], ...]) -> tuple[int, ...]: + return tuple(sum(1 << label for label in entry) for entry in matrix) + + +def idempotent_family() -> list[tuple[int, ...]]: + family = [] + for x in range(1, 8): + for y in range(8): + if (x & y).bit_count() & 1: + family.append(tuple(((x >> r) & 1) * ((y >> c) & 1) + for r in range(3) for c in range(3))) + require(len(family) == 28 and len(set(family)) == 28, + "the rank-one idempotent alphabet must contain 28 distinct matrices") + for N in family: + for r in range(3): + for c in range(3): + value = 0 + for k in range(3): + value ^= N[3 * r + k] & N[3 * k + c] + require(value == N[3 * r + c], "enumerated matrix is not idempotent") + return family + + +def replay() -> dict: + family = idempotent_family() + target = tuple(frozenset((1,)) if (r, c) == (0, 0) else + frozenset((2,)) if (r, c) == (1, 1) else + frozenset((3,)) if (r, c) == (2, 2) else frozenset() + for r in range(3) for c in range(3)) + histogram: Counter[int] = Counter() + digest = hashlib.sha256() + hits = 0 + for left_index, N in enumerate(family): + for g in range(1, 8): + A = reference_factor(N, g) + for right_index, M in enumerate(family): + for h in range(1, 8): + output = reference_product(A, reference_factor(M, h)) + output_masks = masks(output) + target_masks = masks(target) + distance = sum((a ^ b).bit_count() + for a, b in zip(output_masks, target_masks)) + histogram[distance] += 1 + hits += distance == 0 + digest.update(bytes((left_index, g, right_index, h))) + digest.update(bytes(output_masks)) + return { + "alphabet_words": sum(histogram.values()), + "idempotents": len(family), + "nonidentity_group_labels": 7, + "target_hits": hits, + "minimum_coefficient_hamming_distance": min(histogram), + "distance_histogram": {str(k): histogram[k] for k in sorted(histogram)}, + "ordered_word_output_sha256": digest.hexdigest(), + } + + +def replay_one_call() -> dict: + family = idempotent_family() + target = tuple(frozenset((1,)) if (r, c) == (0, 0) else + frozenset((2,)) if (r, c) == (1, 1) else + frozenset((3,)) if (r, c) == (2, 2) else frozenset() + for r in range(3) for c in range(3)) + histogram: Counter[int] = Counter() + digest = hashlib.sha256() + hits = 0 + for index, N in enumerate(family): + for g in range(1, 8): + output_masks = masks(reference_factor(N, g)) + distance = sum((a ^ b).bit_count() + for a, b in zip(output_masks, masks(target))) + histogram[distance] += 1 + hits += distance == 0 + digest.update(bytes((index, g))) + digest.update(bytes(output_masks)) + return { + "alphabet_words": sum(histogram.values()), + "idempotents": len(family), + "nonidentity_group_labels": 7, + "target_hits": hits, + "minimum_coefficient_hamming_distance": min(histogram), + "distance_histogram": {str(k): histogram[k] for k in sorted(histogram)}, + "ordered_word_output_sha256": digest.hexdigest(), + } + + +def main() -> None: + CORE.load_pins() + # Cross-check all 64 entries and the defining quaternion relations. + for a in range(8): + for b in range(8): + require(CORE.qmul(a, b) == Q8[a][b], "Q8 multiplication table mismatch") + require(Q8[1][1] == Q8[2][2] == Q8[3][3] == 4, + "i^2=j^2=k^2=-1 control failed") + require(Q8[1][2] == Q8[3][0] and Q8[2][1] == Q8[7][0], + "ij=k and ji=-k controls failed") + for a in range(8): + for b in range(8): + for c in range(8): + require(Q8[Q8[a][b]][c] == Q8[a][Q8[b][c]], + "literal Q8 table is not associative") + + family = idempotent_family() + direct = reference_product( + reference_product(reference_factor(tuple(int(i == 0) for i in range(9)), 1), + reference_factor(tuple(int(i == 4) for i in range(9)), 2)), + reference_factor(tuple(int(i == 8) for i in range(9)), 3)) + expected_target = tuple(frozenset((1,)) if (r, c) == (0, 0) else + frozenset((2,)) if (r, c) == (1, 1) else + frozenset((3,)) if (r, c) == (2, 2) else frozenset() + for r in range(3) for c in range(3)) + require(direct == expected_target, "three-call diagonal positive control failed") + require(masks(direct) == CORE.target(), "reference and packaged target encodings differ") + + receipt = replay() + result = json.loads((PACKAGE / "RESULT.json").read_text()) + one_call = replay_one_call() + require(one_call == result["one_call_domain"], "independent one-call receipt mismatch") + require(one_call["alphabet_words"] == 196 and one_call["target_hits"] == 0, + "one-call alphabet exclusion failed") + require(receipt == result["two_call_domain"], "independent exhaustive receipt mismatch") + require(receipt["alphabet_words"] == 38_416, "two-call alphabet not fully covered") + require(receipt["target_hits"] == 0, "two-call target word found") + require(receipt["minimum_coefficient_hamming_distance"] == 2, + "reported nearest-output control changed") + require(result["direct_positive_control_calls"] == 3, + "positive-control call count changed") + + # A non-idempotent rank-one matrix is outside the paid alphabet. + try: + CORE.validate_idempotent((0, 1, 0, 0, 0, 0, 0, 0, 0)) + except ValueError: + pass + else: + raise ValueError("non-idempotent control was accepted") + print("PASS_Q8_TWO_CALL_EXCLUSION", + f"words={receipt['alphabet_words']}", + f"hits={receipt['target_hits']}", + f"min_hamming={receipt['minimum_coefficient_hamming_distance']}", + f"one_call_hits={one_call['target_hits']}", + "Q8_table=64 associative_triples=512 direct_calls=3", + f"sha256={receipt['ordered_word_output_sha256']}") + + +if __name__ == "__main__": + main()