From 65a6bc19f820c8b1873f65a517bcf70cd6887223 Mon Sep 17 00:00:00 2001 From: eumemic Date: Sun, 11 Oct 2026 21:50:53 -0700 Subject: [PATCH 1/2] E8 bit word with 111 live ports: #545's punctured unit on the R420 skeleton, c 0.001733052823 Stacks #545's punctured-star unit (DreamingOfClouds) on #543's R420 L3 skeleton: nine E8 lines (P = 10, 15, 51, 53, 56, 66, 72, 89, 111) are neither sources nor targets, and the nine retained totals are pseudo-targets at frame rank 7 (decoder constant 63). Because L3 delivers the Q-part in phase B, each pseudo-target gets it through a free keeper rewritten in a phase-A mini-node at the complementary block frame (with extra carriers for missing lines); R = 439, v = 111. Priced by #510's nine-stage chain, parameterised for v <= 120 (code/adapt9p.py): c = 1733052823330162099157/10^24, +4.79% over #543's word; by #493's eleven-stage chain as parameterised by #545's adapt.py: c = 0.001710608806098012673516. Every cited complex supplier binds: kappa = 0.001590984155135328 with #548's b. Controls reproduce #510's R480 outputs byte for byte through the parameterised files, and #545's c and program hash through #545's files. Co-Authored-By: Claude Opus 5.5 (1M context) --- .gitattributes | 1 + .github/workflows/e8-bit-p111.yml | 44 ++ research/e8-bit-p111/DEPENDS.json | 18 + research/e8-bit-p111/EXPECTED.json | 221 +++++++++ research/e8-bit-p111/LICENSE | 201 ++++++++ research/e8-bit-p111/MANIFEST.sha256 | 63 +++ research/e8-bit-p111/NOTICE.md | 62 +++ research/e8-bit-p111/README.md | 156 ++++++ research/e8-bit-p111/SOURCE-pr510.json | 85 ++++ research/e8-bit-p111/SOURCE-pr535.json | 17 + research/e8-bit-p111/SOURCE-pr544.json | 17 + research/e8-bit-p111/SOURCE-pr545.json | 29 ++ research/e8-bit-p111/SOURCE-pr548.json | 17 + research/e8-bit-p111/code/adapt9p.py | 92 ++++ research/e8-bit-p111/code/cited.py | 33 ++ research/e8-bit-p111/code/gen_p3.py | 443 ++++++++++++++++++ research/e8-bit-p111/code/mutants.py | 160 +++++++ research/e8-bit-p111/code/mutants_p.py | 111 +++++ research/e8-bit-p111/code/mutrec_b.py | 64 +++ research/e8-bit-p111/code/nodeopt.py | 124 +++++ research/e8-bit-p111/code/planio.py | 10 + research/e8-bit-p111/code/provenance.py | 63 +++ research/e8-bit-p111/code/pwordl.py | 128 +++++ research/e8-bit-p111/code/receipt_kappa.py | 81 ++++ research/e8-bit-p111/code/summary.py | 84 ++++ research/e8-bit-p111/code/xcheck_p.py | 435 +++++++++++++++++ .../e8-bit-p111/data/bitE8mut-p111.json.gz | Bin 0 -> 42345 bytes research/e8-bit-p111/data/complex-b.json | 89 ++++ .../complex/pr535/EXACT-ASSEMBLY-PR493.json | 127 +++++ .../data/complex/pr535/EXPECTED.json | 62 +++ .../data/complex/pr544/EXACT-ASSEMBLY.json | 197 ++++++++ .../data/complex/pr544/EXPECTED.json | 47 ++ .../data/complex/pr548/EXACT-ASSEMBLY.json | 202 ++++++++ .../data/complex/pr548/EXPECTED.json | 39 ++ research/e8-bit-p111/data/hosting11-p111.json | 208 ++++++++ research/e8-bit-p111/data/plan-l3.json | 1 + research/e8-bit-p111/data/punct.json | 7 + .../e8-bit-p111/data/selection9-p111.json | 167 +++++++ research/e8-bit-p111/discovery/choose.py | 88 ++++ research/e8-bit-p111/discovery/search.py | 47 ++ research/e8-bit-p111/requirements.txt | 3 + .../data/bitE8mut-r480.json.gz | Bin 0 -> 49414 bytes .../pr510/e8-r480-nine-stage/EXPECTED.json | 102 ++++ .../pr510/e8-r480-nine-stage/code/adapt.py | 25 + .../e8-r480-nine-stage/code/assemble9.py | 66 +++ .../data/selection9-r480.json | 150 ++++++ .../code/audit_hosted_k.py | 157 +++++++ .../code/audit_parent_k.py | 165 +++++++ .../code/bank_routes_k.py | 35 ++ .../e8-r715-eleven-stage-whole/code/emit_k.py | 130 +++++ .../code/global_k.py | 264 +++++++++++ .../code/hosting_k.py | 69 +++ .../e8-r740-replica32/code/bank_routes.py | 65 +++ .../vendor/e8-r740-replica32/code/config.py | 2 + .../vendor/e8-r740-replica32/code/e8core.py | 203 ++++++++ .../code/engines/base_two_moment.py | 63 +++ .../e8-r740-replica32/code/engines/moment.py | 113 +++++ .../e8-r740-replica32/code/engines/outer.py | 58 +++ .../vendor/e8-r740-replica32/code/tile_m.py | 93 ++++ .../vendor/idle-axis-hosting/hosting_core.py | 138 ++++++ .../e8-bit-p111/vendor/pr545/EXPECTED.json | 127 +++++ .../e8-bit-p111/vendor/pr545/code/adapt.py | 136 ++++++ .../vendor/pr545/code/punct_cfg.py | 79 ++++ .../pr545/data/bitE8mut-v111-r489.json.gz | Bin 0 -> 41837 bytes .../vendor/pr545/data/hosting-v111-r489.json | 213 +++++++++ research/e8-bit-p111/verify.sh | 212 +++++++++ 66 files changed, 6708 insertions(+) create mode 100644 .github/workflows/e8-bit-p111.yml create mode 100644 research/e8-bit-p111/DEPENDS.json create mode 100644 research/e8-bit-p111/EXPECTED.json create mode 100644 research/e8-bit-p111/LICENSE create mode 100644 research/e8-bit-p111/MANIFEST.sha256 create mode 100644 research/e8-bit-p111/NOTICE.md create mode 100644 research/e8-bit-p111/README.md create mode 100644 research/e8-bit-p111/SOURCE-pr510.json create mode 100644 research/e8-bit-p111/SOURCE-pr535.json create mode 100644 research/e8-bit-p111/SOURCE-pr544.json create mode 100644 research/e8-bit-p111/SOURCE-pr545.json create mode 100644 research/e8-bit-p111/SOURCE-pr548.json create mode 100644 research/e8-bit-p111/code/adapt9p.py create mode 100644 research/e8-bit-p111/code/cited.py create mode 100644 research/e8-bit-p111/code/gen_p3.py create mode 100644 research/e8-bit-p111/code/mutants.py create mode 100644 research/e8-bit-p111/code/mutants_p.py create mode 100644 research/e8-bit-p111/code/mutrec_b.py create mode 100644 research/e8-bit-p111/code/nodeopt.py create mode 100644 research/e8-bit-p111/code/planio.py create mode 100644 research/e8-bit-p111/code/provenance.py create mode 100644 research/e8-bit-p111/code/pwordl.py create mode 100644 research/e8-bit-p111/code/receipt_kappa.py create mode 100644 research/e8-bit-p111/code/summary.py create mode 100644 research/e8-bit-p111/code/xcheck_p.py create mode 100644 research/e8-bit-p111/data/bitE8mut-p111.json.gz create mode 100644 research/e8-bit-p111/data/complex-b.json create mode 100644 research/e8-bit-p111/data/complex/pr535/EXACT-ASSEMBLY-PR493.json create mode 100644 research/e8-bit-p111/data/complex/pr535/EXPECTED.json create mode 100644 research/e8-bit-p111/data/complex/pr544/EXACT-ASSEMBLY.json create mode 100644 research/e8-bit-p111/data/complex/pr544/EXPECTED.json create mode 100644 research/e8-bit-p111/data/complex/pr548/EXACT-ASSEMBLY.json create mode 100644 research/e8-bit-p111/data/complex/pr548/EXPECTED.json create mode 100644 research/e8-bit-p111/data/hosting11-p111.json create mode 100644 research/e8-bit-p111/data/plan-l3.json create mode 100644 research/e8-bit-p111/data/punct.json create mode 100644 research/e8-bit-p111/data/selection9-p111.json create mode 100644 research/e8-bit-p111/discovery/choose.py create mode 100644 research/e8-bit-p111/discovery/search.py create mode 100644 research/e8-bit-p111/requirements.txt create mode 100644 research/e8-bit-p111/vendor/pr510/e8-r480-dead-totals/data/bitE8mut-r480.json.gz create mode 100644 research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/EXPECTED.json create mode 100644 research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/code/adapt.py create mode 100644 research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/code/assemble9.py create mode 100644 research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/data/selection9-r480.json create mode 100644 research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/vendor/e8-r715-eleven-stage-whole/code/audit_hosted_k.py create mode 100644 research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/vendor/e8-r715-eleven-stage-whole/code/audit_parent_k.py create mode 100644 research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/vendor/e8-r715-eleven-stage-whole/code/bank_routes_k.py create mode 100644 research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/vendor/e8-r715-eleven-stage-whole/code/emit_k.py create mode 100644 research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/vendor/e8-r715-eleven-stage-whole/code/global_k.py create mode 100644 research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/vendor/e8-r715-eleven-stage-whole/code/hosting_k.py create mode 100644 research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/vendor/e8-r740-replica32/code/bank_routes.py create mode 100644 research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/vendor/e8-r740-replica32/code/config.py create mode 100644 research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/vendor/e8-r740-replica32/code/e8core.py create mode 100644 research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/vendor/e8-r740-replica32/code/engines/base_two_moment.py create mode 100644 research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/vendor/e8-r740-replica32/code/engines/moment.py create mode 100644 research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/vendor/e8-r740-replica32/code/engines/outer.py create mode 100644 research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/vendor/e8-r740-replica32/code/tile_m.py create mode 100644 research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/vendor/idle-axis-hosting/hosting_core.py create mode 100644 research/e8-bit-p111/vendor/pr545/EXPECTED.json create mode 100644 research/e8-bit-p111/vendor/pr545/code/adapt.py create mode 100644 research/e8-bit-p111/vendor/pr545/code/punct_cfg.py create mode 100644 research/e8-bit-p111/vendor/pr545/data/bitE8mut-v111-r489.json.gz create mode 100644 research/e8-bit-p111/vendor/pr545/data/hosting-v111-r489.json create mode 100644 research/e8-bit-p111/verify.sh diff --git a/.gitattributes b/.gitattributes index 314f2a9c94..9cbec2d77d 100644 --- a/.gitattributes +++ b/.gitattributes @@ -28,6 +28,7 @@ research/experiment-log/** -whitespace # Preserve the exact frozen E8 bit supplier source manifest. research/e8-bit-carrier-skeleton/** -text -whitespace +research/e8-bit-p111/** -text -whitespace # Preserve the exact reviewed complex supplier packages. research/direct-delivery504-global/** -text -whitespace research/punctured112-global/** -text -whitespace diff --git a/.github/workflows/e8-bit-p111.yml b/.github/workflows/e8-bit-p111.yml new file mode 100644 index 0000000000..600d4a5b94 --- /dev/null +++ b/.github/workflows/e8-bit-p111.yml @@ -0,0 +1,44 @@ +name: e8-bit-p111 + +on: + pull_request: + paths: + - 'research/e8-bit-p111/**' + - 'research/e8-bit-carrier-skeleton/**' + - 'research/direct-delivery504-global/EXPECTED.json' + - '.github/workflows/e8-bit-p111.yml' + push: + paths: + - 'research/e8-bit-p111/**' + - 'research/e8-bit-carrier-skeleton/**' + - 'research/direct-delivery504-global/EXPECTED.json' + - '.github/workflows/e8-bit-p111.yml' + workflow_dispatch: + +permissions: + contents: read + +jobs: + verify: + runs-on: ubuntu-24.04 + timeout-minutes: 40 + strategy: + fail-fast: false + matrix: + python-version: ['3.12', '3.14'] + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + with: + persist-credentials: false + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7 + with: + python-version: ${{ matrix.python-version }} + - name: Install the Python requirements, a C++17 compiler and the Boost headers + run: | + python -m pip install --disable-pip-version-check -r research/e8-bit-p111/requirements.txt + sudo apt-get update + sudo apt-get install -y --no-install-recommends g++ libboost-dev + - name: Verify (rebuild, independent replay, interface lemma and totals, official checkers, nine- and eleven-stage prices, controls) + run: CXX=g++ bash research/e8-bit-p111/verify.sh "$RUNNER_TEMP/e8-bit-p111" + - name: Require committed files to remain unchanged + run: git diff --exit-code diff --git a/research/e8-bit-p111/DEPENDS.json b/research/e8-bit-p111/DEPENDS.json new file mode 100644 index 0000000000..25cb6abcf9 --- /dev/null +++ b/research/e8-bit-p111/DEPENDS.json @@ -0,0 +1,18 @@ +{ + "schema": "main-pins/1", + "repository": "CrocSwap/integer-mult-bounds", + "commit": "db0e57bc7f36b5ac0fc844ad7ce1396165ee6647", + "note": "Files of the repository's main branch this package runs or cites, pinned by sha256 (and git blob id at the commit above). research/e8-bit-carrier-skeleton (PR #493) is pinned through its own MANIFEST.sha256: verify.sh checks that every file of that directory matches it and that no file is unlisted. research/direct-delivery504-global/EXPECTED.json is PR #531's receipt (its complex b).", + "files": { + "../e8-bit-carrier-skeleton/MANIFEST.sha256": { + "source_path": "research/e8-bit-carrier-skeleton/MANIFEST.sha256", + "blob": "1213654fbd423d66e785bf582ba8ee76a41cdd36", + "sha256": "7f25c41d0400ef5adf763eae6e166003a9b59e5a655b860c04b052f6731c7297" + }, + "../direct-delivery504-global/EXPECTED.json": { + "source_path": "research/direct-delivery504-global/EXPECTED.json", + "blob": "c340c32b84d7ea63c5f15eac78e38eace428c599", + "sha256": "0c4c7df98633ace6dd3f1604462dd509945f6b2d463a0b045442b785906101f1" + } + } +} diff --git a/research/e8-bit-p111/EXPECTED.json b/research/e8-bit-p111/EXPECTED.json new file mode 100644 index 0000000000..c8ad4439e8 --- /dev/null +++ b/research/e8-bit-p111/EXPECTED.json @@ -0,0 +1,221 @@ +{ + "word": { + "file": "data/bitE8mut-p111.json.gz", + "file_sha256": "1bc3c53d4baf4927c6a00c339ea3975516471a48074d4d97c6addaded2a4d858", + "content_sha256": "d0df3c89ae84989f4045ba1681a5e2e9fcdfa38ed0301c8abf5185537155cbe3", + "plan_sha256": "d5c2494b4baae801143049e172db303b3c8e25a002e9d9715a1d79dcb2d74c58", + "params_sha256": "dd371e1d6fe57602f242920b582ec4c133efc8ede4a849fd701ac90b7f6e1853", + "v": 111, + "R": 439, + "punctured": [ + 10, + 15, + 51, + 53, + 56, + 66, + 72, + 89, + 111 + ], + "retained_totals": 9, + "copy_ranks": [ + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 7 + ], + "decoder_constant": 63, + "gates": { + "D": 111, + "A": 2073, + "B": 712, + "C": 2572 + }, + "stats": { + "extra_carriers": 10, + "junk_wires": 0, + "rcv_flips": 10 + }, + "local_histogram": { + "1": 1321, + "2": 1342, + "3": 80, + "4": 47, + "5": 102, + "6": 10, + "7": 18 + }, + "legality": { + "records": 27234, + "common_frame_ADDs": 24305, + "rank_mass": 5129 + }, + "five_stage_columns": 883, + "five_stage_payload_bits": 93, + "nine_stage_columns": 1327, + "nine_stage_payload_bits": 93, + "eleven_stage_columns": 1549, + "eleven_stage_payload_bits": 93 + }, + "interface": { + "hypotheses": { + "H1_no_target_feeds_z": true, + "H2_D_frame0_targets_from_helpers": true, + "H3_A_z_only_scatter_targets_B_split": true, + "H4_C_literal_reverse_at_full": true, + "H5_V_identity": true + }, + "conclusion": { + "U_block_form": true, + "J_identity": true, + "K_equals_D": true, + "C_is_U_inverse": true + }, + "interleaving": { + "z_gates_A": 2073, + "source_writes_A": 0, + "z_gates_B": 499, + "source_writes_B": 483, + "helper_writes_B": 16, + "sources_rewritten_B": 108, + "B_z_write_frame_dims": { + "4": 388, + "6": 111 + }, + "B_z_operand_kinds": { + "helper<-helper": 3, + "helper<-source": 13, + "source<-helper": 108, + "source<-source": 375 + }, + "B_z_operand_kinds_by_dim": { + "dim4:helper<-source": 13, + "dim4:source<-source": 375, + "dim6:helper<-helper": 3, + "dim6:source<-helper": 108 + }, + "B_z_writes_after_a_target_read_of_the_same_register": 454, + "B_target_reads_of_registers_rewritten_earlier_in_B": 111, + "target_reads_of_targets": 0, + "cleanup_gates": 2572 + } + }, + "nine": { + "pipeline": "PR #510 (PR #463 nine-stage template, 32 replicas, PR #442 hosting, PR #493 outer assembly), parameterised for v <= 120 by code/adapt9p.py", + "bit_lower": "1733052823330162099157/1000000000000000000000000", + "bit_lower_decimal": "0.001733052823330162099157", + "profile": { + "1": 380448, + "2": 400416, + "3": 23040, + "4": 27744, + "5": 29376, + "6": 17088, + "7": 26496, + "42": 288 + }, + "bit": { + "live": 28416, + "physical": 28425, + "maxchild": 42, + "rank_mass": 1808352, + "hosted_residuals": 126432, + "bank_assignments": 0, + "prefix_bits": 177, + "charged_prefix_bits": 192, + "coefficient_bits": 177, + "two_level_coefficient_bits": 393, + "charged_two_level_coefficient_bits": 512, + "row_coefficient": 18355, + "halving_depth": 2 + }, + "kappa": { + "pr548": { + "kappa": "49718254847979/31250000000000000", + "kappa_decimal": "0.001590984155135328", + "complex_lower": "796759709641153381751/500000000000000000000000", + "binding_component": "complex" + }, + "pr544": { + "kappa": "397677800014309/250000000000000000", + "kappa_decimal": "0.001590711200057236", + "complex_lower": "31864911873350496687/20000000000000000000000", + "binding_component": "complex" + }, + "pr535": { + "kappa": "793733736881443/500000000000000000", + "kappa_decimal": "0.001587467473762886", + "complex_lower": "1589991533606044174031/1000000000000000000000000", + "binding_component": "complex" + }, + "pr531": { + "kappa": "1517760977820331/1000000000000000000", + "kappa_decimal": "0.001517760977820331", + "complex_lower": "304013615566499291631/200000000000000000000000", + "binding_component": "complex" + } + } + }, + "eleven": { + "pipeline": "PR #493 (PR #445 geometry, PR #472 emitter, audit and invoice, PR #442 hosting), parameterised by PR #545's code/adapt.py", + "bit_lower": "427652201524503168379/250000000000000000000000", + "bit_lower_decimal": "0.001710608806098012673516", + "profile": { + "1": 116248, + "2": 121648, + "3": 7040, + "4": 7688, + "5": 8976, + "6": 4432, + "7": 8688, + "56": 2600 + }, + "bit": { + "live": 8880, + "physical": 8889, + "maxchild": 56, + "rank_mass": 689304, + "hosted_residuals": 38632, + "prefix_bits": 4304, + "charged_prefix_bits": 4352, + "coefficient_bits": 4304, + "three_level_coefficient_bits": 12966, + "charged_three_level_coefficient_bits": 12992, + "row_coefficient": 19431, + "halving_depth": 3 + }, + "emitted_program_sha256": "790bbd2d8ef4e1169fca344c76b1965ee2fe78e34e74d16d458e4849cc2ba3b6", + "kappa": { + "pr548": { + "kappa": "49718254847979/31250000000000000", + "kappa_decimal": "0.001590984155135328", + "complex_lower": "796759709641153381751/500000000000000000000000", + "binding_component": "complex" + }, + "pr544": { + "kappa": "397677800014309/250000000000000000", + "kappa_decimal": "0.001590711200057236", + "complex_lower": "31864911873350496687/20000000000000000000000", + "binding_component": "complex" + }, + "pr535": { + "kappa": "793733736881443/500000000000000000", + "kappa_decimal": "0.001587467473762886", + "complex_lower": "1589991533606044174031/1000000000000000000000000", + "binding_component": "complex" + }, + "pr531": { + "kappa": "1517760977820331/1000000000000000000", + "kappa_decimal": "0.001517760977820331", + "complex_lower": "304013615566499291631/200000000000000000000000", + "binding_component": "complex" + } + } + } +} diff --git a/research/e8-bit-p111/LICENSE b/research/e8-bit-p111/LICENSE new file mode 100644 index 0000000000..261eeb9e9f --- /dev/null +++ b/research/e8-bit-p111/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/research/e8-bit-p111/MANIFEST.sha256 b/research/e8-bit-p111/MANIFEST.sha256 new file mode 100644 index 0000000000..ac320af953 --- /dev/null +++ b/research/e8-bit-p111/MANIFEST.sha256 @@ -0,0 +1,63 @@ +fa9253078c444b8f4a57e849048f727c3c8233c3dc3c7f651bef2cf7bb0178c5 DEPENDS.json +7c2675fd49afd7e328606399a4421fd49c43fe8a6c5b57154cc908c00588abe2 EXPECTED.json +c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4 LICENSE +5b88eb2b7de76aa2a3288ee3c7a08d18a5eadfa82f5e7a99e207d232aed71221 NOTICE.md +481431e35bd831ec3d0bb8edc01abdfa49d5caeabebab67ac839a35053e9ef85 README.md +5e84c26ba837ce029acd1b5089e5533d2ebde793deede84215322112e95e06f8 SOURCE-pr510.json +74254fde5cff5580d5c199b139ff3ce31a3defded3f653a3ba83cf0c9204ddc4 SOURCE-pr535.json +9ae1cfa69158f678499510e9cf24396b2bdd9b68979e906cbb8d4ee29b63e0b9 SOURCE-pr544.json +4b868b05c092eb821b257eab0f429f7c502fde946dd6508e8f5161a8758b4904 SOURCE-pr545.json +4480cd4e1dd0dc2bf55c26a1ae4a8bbb8587a96cea3053687e5d4c874148043e SOURCE-pr548.json +14365d0eb8f4a7352edd28816196148fd28d5bbcb36044ebeeb15ca051bca9de code/adapt9p.py +8f7852571a897e131cf964d4b7826dc7bb2b7a42e81892d436f8ea092b26ef7d code/cited.py +438b83f10d70ceca7cf78049420204b7de2dc61682f913c50a62596106ad84cd code/gen_p3.py +e2fd3429e2db4389ae8a5ab47dda89f551b0d0952c42fad5ac8d38a85d670594 code/mutants.py +ee2a2cfb44072a214dd29eae5b96a71b258ac886a05456b84d600de94b339591 code/mutants_p.py +231cbf26bfe5e6824347e4556959941d8b4dea1efa06d7f88d7a73cca8b03158 code/mutrec_b.py +6a5896f60b1fe05c41ab28ce822dff782c635e12d0c74fad20b5ce2a4010fa3f code/nodeopt.py +c0a687f61aed29455cea953d8a3e49a32136edb3cd4213c46880c146a6ca7fa3 code/planio.py +aade757c1e4e2714207cbc80a9a60793d84b5f009de2cac79ee8d3239986fd2a code/provenance.py +6d8fccd44508e4705b6446e0a64482a5221a3efc5663c5ca7ae90d44581ac50a code/pwordl.py +6ac4cb3c6015bd46f3b0a88dfffce741d8942a703cc68738ad61953fae485e9e code/receipt_kappa.py +c7599cafe1a2330a2fc8a956dca457f802dbb8b04e4dc4ffa9d90890e5efb428 code/summary.py +af3decc877e27c00001cf56f50c6c09566e8e101297ab34c7860aed96a06d80e code/xcheck_p.py +1bc3c53d4baf4927c6a00c339ea3975516471a48074d4d97c6addaded2a4d858 data/bitE8mut-p111.json.gz +96943a080f371392381c70676638c7b3281ffc44f96b50d7ab064f59aa9897c0 data/complex-b.json +3f4be566309841e00e842d5686f7937a355ae1a6a2eaa3ccb07bdf8a9ca604fb data/complex/pr535/EXACT-ASSEMBLY-PR493.json +cb734055c36681d38cf431a6926b7713351a94073cfba37d70a5384a9ef0d178 data/complex/pr535/EXPECTED.json +eaacbe5a9b971b20f59503a5e948c1ba76c5e082f81a012c9cc7394494bc4db5 data/complex/pr544/EXACT-ASSEMBLY.json +8f48531cb1ccc6c1fe7c80ba5990f8aea4278dbe9394bcc4d3f471a3e8d75f1c data/complex/pr544/EXPECTED.json +8c40d6a7907c2ba71284d4f29a2d6e6bbe4dfc5da92a6da1bbac13488b916349 data/complex/pr548/EXACT-ASSEMBLY.json +2e458dd9880d001b63153e37ef7954c6bcfceae378ab4b225cfa1c94ea8e23ce data/complex/pr548/EXPECTED.json +1f5a1142e0119483cabf9a4d99094d988014d1adaa29370e93f4b4a63d9bfd7f data/hosting11-p111.json +d5c2494b4baae801143049e172db303b3c8e25a002e9d9715a1d79dcb2d74c58 data/plan-l3.json +dd371e1d6fe57602f242920b582ec4c133efc8ede4a849fd701ac90b7f6e1853 data/punct.json +4ab1ed4529683cd184987f8bc3031fb7408b9cd22c25ab9cf541a8aaf896eaf1 data/selection9-p111.json +18d77e8f62ffc671b3b3d3e50626c03481a2eaf6fb4abbc131e08226feebc180 discovery/choose.py +edd876212edf0db3f6620534dcf50b5d2c23197c2d5f8af8c030ed5bc8447923 discovery/search.py +e11a6f5c2a40b77fbf54a31a4c45bac575e9d2f765d35b657406d5f088fd0664 requirements.txt +92e399e9a486b55229b5624632ac54f800dd9e022f2aee97f1e5f264bbc0ef5c vendor/pr510/e8-r480-dead-totals/data/bitE8mut-r480.json.gz +883f7a1a84f31d9e4430e348bc8d9c1ec10210b206ff602dbe126349056af13d vendor/pr510/e8-r480-nine-stage/EXPECTED.json +27b6b77cc4ebf439f9019e63a22dedc1ef72ee65fe120aad3d17316b1df06d7b vendor/pr510/e8-r480-nine-stage/code/adapt.py +4bf6768224a3fac81ccda26df24f3763f9a4fbc35ad62bef2888595cdae519fe vendor/pr510/e8-r480-nine-stage/code/assemble9.py +8176db518bed23ea8121543bfb18085ec6633064142ba40ac5db00f63b06553d vendor/pr510/e8-r480-nine-stage/data/selection9-r480.json +57e558c80a2fea6e291587b04afd76dfaee3550729997798b61f33daf90b8ec9 vendor/pr510/e8-r480-nine-stage/vendor/e8-r715-eleven-stage-whole/code/audit_hosted_k.py +374e1d983589463bbfa06b6e9f2ffafb9d2c14acf97b1f858593a3cef90e93d2 vendor/pr510/e8-r480-nine-stage/vendor/e8-r715-eleven-stage-whole/code/audit_parent_k.py +ea678a7befda10c6f1b9dd38a36fe1437c6468472bd649f60b9f01522047e0bf vendor/pr510/e8-r480-nine-stage/vendor/e8-r715-eleven-stage-whole/code/bank_routes_k.py +298b16c3d4f58a468ad1a64564c8500cb281414a3f3c0428fd467908eeb4a4b4 vendor/pr510/e8-r480-nine-stage/vendor/e8-r715-eleven-stage-whole/code/emit_k.py +6368196a393ac25bb2918880e4c4bd31ed733b762c27b6873ab391cbc0c5c3c8 vendor/pr510/e8-r480-nine-stage/vendor/e8-r715-eleven-stage-whole/code/global_k.py +081c3b95b18d900d97ce82af64fb428f085b6adc6d8ca93ea920797dad7eb81e vendor/pr510/e8-r480-nine-stage/vendor/e8-r715-eleven-stage-whole/code/hosting_k.py +7c6997c455bd55acca3a1603797c34e7733d557c27a7694a1429ff15773a505d vendor/pr510/e8-r480-nine-stage/vendor/e8-r740-replica32/code/bank_routes.py +283d7fad45155ee91e704d63ec947cf4a60aeff5cc43510bc0e835f7da7d8767 vendor/pr510/e8-r480-nine-stage/vendor/e8-r740-replica32/code/config.py +1cb286196c1530d06f50f0997febc5993b388f33464e295611b920ba30a24eeb vendor/pr510/e8-r480-nine-stage/vendor/e8-r740-replica32/code/e8core.py +aee70c242a2f1d225528fabe2002e1564d1dc884797c07d28a831c7878ee9098 vendor/pr510/e8-r480-nine-stage/vendor/e8-r740-replica32/code/engines/base_two_moment.py +81cbc864e106418fc7854322746f72180cb8110ff39f74450a3b8d55a82c1482 vendor/pr510/e8-r480-nine-stage/vendor/e8-r740-replica32/code/engines/moment.py +c1ebb52163a7219504c728a5c2b098bf1e50bf20148a2879c5a74c3cadc7c03c vendor/pr510/e8-r480-nine-stage/vendor/e8-r740-replica32/code/engines/outer.py +d6ae4a556472d403773cd2117c992bfc10e1187112da1b3893f3fae628a131dd vendor/pr510/e8-r480-nine-stage/vendor/e8-r740-replica32/code/tile_m.py +d8b90cd5d152df3c7c351230863d9e61816ad5e1f21d69bea1a86a5daad559a3 vendor/pr510/e8-r480-nine-stage/vendor/idle-axis-hosting/hosting_core.py +dd078d075b153ea2ed02a4bbd1fe467315930f4d7dc90f2c34cd2c33e06a5029 vendor/pr545/EXPECTED.json +b9a50303147a3003ad94fc4b1a822d4fca7f9b9e917d165e3c45152575302a0e vendor/pr545/code/adapt.py +7cedd3416d6a8ce2e6393cea32dc1d11032190787a593a0ad964a0b62fc407f3 vendor/pr545/code/punct_cfg.py +c78317ab05ea175dc380f4dce2bafdbcd7e8b46f0c9d6baf350c2f604bb3ae4e vendor/pr545/data/bitE8mut-v111-r489.json.gz +862155e5c341053668ac1a18db05591417c38b9fe5f395d64a3b0a17a1502042 vendor/pr545/data/hosting-v111-r489.json +3bb8abf984e98fccf77914ae56d51eb60aab284db677905c1c4aafeacc44eee0 verify.sh diff --git a/research/e8-bit-p111/NOTICE.md b/research/e8-bit-p111/NOTICE.md new file mode 100644 index 0000000000..b589500c8a --- /dev/null +++ b/research/e8-bit-p111/NOTICE.md @@ -0,0 +1,62 @@ +# Notice + +Apache-2.0 (`LICENSE`, copied from #493's package). Prepared by eumemic with substantial Anthropic Claude +assistance. + +## New in this package + +- `code/gen_p3.py` (the generator of the punctured word: the L3 skeleton of #543 restricted to the live lines, the + pseudo-target totals, the phase-A mini-nodes and extra carriers), `code/pwordl.py` (word builder for a live subset + of the 120 lines; output in the bitE8/1 layout with the `ports_geom` and `punctured` fields introduced by #545), + `data/bitE8mut-p111.json.gz` (the word), `data/punct.json` (its frozen parameters), `discovery/choose.py` and + `discovery/search.py` (the nomination of P and of the parameters; not run by `verify.sh`). +- `code/gen_p3.py` carries `derive`, `ex_part` and `plan_thirds` from #543's `code/gen_l3.py` (eumemic), and + `data/plan-l3.json` is #543's CP-SAT plan, unchanged. `code/nodeopt.py`, `code/planio.py`, `code/mutants.py`, + `code/mutrec_b.py` and `code/provenance.py` are #543's files, unchanged. +- `code/xcheck_p.py`: #543's independent checker `code/xcheck.py` with nine listed edits for v <= 120 live labels + (root-line enumeration of the lattice, frame closure and target frames over the live labels) and a new part T (the + frame and COPY rank of every retained total at the scatter, and whether it is the hyperplane of a punctured line). +- `code/mutants_p.py` (punctured-design controls), `code/adapt9p.py` (#510's nine-stage files parameterised for + v <= 120 and any copy ranks, the way #545's `adapt.py` parameterises #493's eleven-stage files), `code/cited.py`, + `code/receipt_kappa.py`, `code/summary.py`, `verify.sh`, `EXPECTED.json`, `DEPENDS.json`, `requirements.txt`, + `data/complex-b.json`, `data/selection9-p111.json` and `data/hosting11-p111.json` (frozen nominations of #442's + hosting programme, rediscovered by `verify.sh`), `.github/workflows/e8-bit-p111.yml`. + +## Vendored, byte-identical (git blob ids in `SOURCE-pr*.json`, checked by `code/provenance.py`) + +- **PR #510 (DreamingOfClouds, with Claude Opus 5.5 via Claude Code), commit 64edee1** (`vendor/pr510/`): the same 19 + files as #543's package (#510's nine-stage chain with the files it vendors from #463, #443, #442 and #315, and #503's + R480 word). Their notices are listed in #543's NOTICE and in the files. +- **PR #545 (DreamingOfClouds, with Claude Opus 5.5 via Claude Code; draft), commit 376426f** (`vendor/pr545/`): + `code/adapt.py` and `code/punct_cfg.py`, run unchanged on this package's word; `data/bitE8mut-v111-r489.json.gz`, + `data/hosting-v111-r489.json` and `EXPECTED.json` (a control). +- **PR #548 (Dugongue), commit 66dbc2b** (`data/complex/pr548/`): `EXPECTED.json` and `evidence/EXACT-ASSEMBLY.json`. +- **PR #544 (chafreaky, with OpenAI Codex assistance), commit 4858bb3** (`data/complex/pr544/`): `EXPECTED.json` and + the Linux exact-assembly receipt. +- **PR #535 (icekylinx, with GPT-6 Astra assistance), commit 8b758a1** (`data/complex/pr535/`): `EXPECTED.json` and + `evidence/audit/EXACT-ASSEMBLY-PR493.json`. + +## Used from main, unchanged (pinned in `DEPENDS.json`) + +- `research/e8-bit-carrier-skeleton` (PR #493, eumemic): the skeleton modules and D4 partition the generator imports, + #370's checker (run here in #545's v <= 120 form), the official cohort checkers, `gram_frames.py`, the record + exporter, the geometries and the eleven-stage chain that #545's `adapt.py` parameterises, #442's hosting programme, + the R489 word, the control receipts and #315's engines. +- `research/direct-delivery504-global/EXPECTED.json` (PR #531, icekylinx): its complex b and kappa. + +## Cited, not rebuilt + +Complex coarse savings in `data/complex-b.json`, each bound to its PR's receipt by `code/cited.py`: PR #548 (primary; +open), PR #544 and PR #535 (open), PR #531 (merged), and PR #484, #471 and #458 (controls). + +## Ideas used + +- PR #545 (DreamingOfClouds): the punctured-star unit. Nine lines are neither sources nor targets; each retained total + is a pseudo-target that reads what the removed target would read, at frame rank 7, so the decoder constant is 63; + the rank-mass identity with v and the decoder constant as parameters; the parameterisation of the hub chain. +- PR #543 (eumemic): the R420 L3 skeleton with keepers read by targets and rewritten in phase B. +- PR #493 (eumemic), PR #503 and #510 (DreamingOfClouds), PR #425 (Zhihao Chen / jacklightChen), PR #370 (Dylan Crow), + PR #472, #445, #443 (chafreaky), PR #442 (DaysSky), PR #463 and #315 (DreamingOfClouds), PR #386 (Zhihao Chen): the + skeleton, the scalar-interface lemma, the format, and the pricing pipelines. +- PR #523 (icekylinx): the puncture pattern on the complex side, cited by #545. +- Jacob Sussman introduced the E8 unit. diff --git a/research/e8-bit-p111/README.md b/research/e8-bit-p111/README.md new file mode 100644 index 0000000000..16a72c18a4 --- /dev/null +++ b/research/e8-bit-p111/README.md @@ -0,0 +1,156 @@ +# E8 bit word with 111 live ports: PR #545's punctured-star unit on PR #543's R420 skeleton + +A new E8 bit supplier (h = 8, **v = 111 live ports**, 9 retained totals of frame rank 7, decoder constant 63) with +**R = 439 helpers**. It stacks the punctured-star unit of #545 (DreamingOfClouds: nine lines are neither sources nor +targets, and each retained total is a pseudo-target that reads what the removed target would read) on the R420 L3 +skeleton of #543 (keepers read by targets, then rewritten in phase B). Priced by two hub pipelines, parameterised for +v <= 120: + +| quantity | value | +|---|---| +| **bit c, nine stages** (#510's chain: #463 template, 32 replicas, #442 hosting; parameterised by `code/adapt9p.py`; 10^-24 grid) | `1733052823330162099157/10^24` = **0.001733052823330162099157** | +| bit c, eleven stages (#493's chain as parameterised by #545's `adapt.py`, vendored and run unchanged) | `427652201524503168379/(2.5·10^23)` = 0.001710608806098012673516 | +| #543's R420 word, nine stages (same pipeline at v = 120; #543's receipt, not rerun here) | 0.001653826988146798944268 (this word: +4.79%) | +| #545's punctured R489 word, eleven stages (control, reproduced here) | 0.00165947204563779652541 (this word: +4.43% at nine stages, +3.08% at eleven) | +| #510's R480 word, nine stages (control, reproduced here) | 0.001587447106092548137108 (this word: +9.17%) | + +The nine-stage c is 8.76% above the largest cited complex b (#548), so every composition is complex-bound. Through +#493's six-level, 47-inequality outer assembly: + +| complex supplier (cited from its receipt, not rebuilt) | complex b | κ | binds | +|---|---|---|---| +| **#548** (Dugongue; open PR) | `796759709641153381751/(5·10^23)` = 0.001593519419282307 | **`49718254847979/31250000000000000` = 0.001590984155135328** | complex | +| #544 (chafreaky; open PR) | `31864911873350496687/(2·10^22)` = 0.001593245593667525 | `397677800014309/(2.5·10^17)` = 0.001590711200057236 | complex | +| #535 (icekylinx; open PR) | `1589991533606044174031/10^24` = 0.001589991533606044 | `793733736881443/(5·10^17)` = 0.001587467473762886 | complex | +| #531 (icekylinx; merged) | `304013615566499291631/(2·10^23)` = 0.001520068077832496 | `1517760977820331/10^18` = 0.001517760977820331 | complex | + +#548's own κ, 0.001590591090759927, is bound by its own bit c 0.001593125101352604961534; with this word it becomes +#548's complex cap (+0.0247%). The κ values are the same with #543's R420 word (its c is also above every cited b): the +value of this word is bit headroom. The bit side stays non-binding for any complex b below about 0.00173. + +## The word + +`data/bitE8mut-p111.json.gz` is rebuilt byte for byte by `code/gen_p3.py` from #543's frozen plan +`data/plan-l3.json` and the parameters `data/punct.json` (about 2 s). Format: bitE8/1 with mutable sources, v = 111 +live labels (`ports_geom`, `punctured` as in #545). Punctured lines P = [10, 15, 51, 53, 56, 66, 72, 89, 111]: two in +block 0, three in block 1, two in block 3, two in block 5 (block pairs (0,1) and (3,5)), one per 4-frame, none of +lines 0..7. + +**Why #545's construction does not carry over unchanged.** In #493's skeleton the Q-node runs in phase A, so a +pseudo-target can read everything a target reads before the scatter. In L3 the block node runs in phase B: the source +registers it transforms are first read by the targets at their cells. A retained total is read by the scatter, at the +end of phase A, so the part of a target's sum that comes from the block node (the live lines of the complementary +block B, about ten lines) is not available to a pseudo-target. The parity of the d-lines also differs: in a 4-frame +with one punctured line the pseudo-target needs an odd number of single d-line reads (the three parity equations sum +to 1). + +**The pseudo-target total of a punctured line p** (a fresh helper): + +- at the line frame of kline(p), before that line's cell gate: it reads the source of kline(p), its single d-line; +- at D6[p, qhost(p)]: it reads that D6's o1 (it takes the place of the third register qdel[p]; qhost(p) still reads + o1 in phase B); +- at p-perp: it reads p's needers at its two (z,z) D6s. One of them is a free keeper of B (a register holding one + line of B in L3) that a phase-A **mini-node** at the block frame D4(B) rewrote to carry the pseudo-target's B-part. + The mini-node reads the other free keepers of B, the freed sources of B (the source in the d-single cell of a + punctured target of B, which no target reads), and **extra carriers** (a helper that reads a source of B at its line + frame in phase A) for the lines nothing else holds. + +Eligible lines: both (z,z) cells of p's 4-frame contain p (so p's needers there are free after phase A), and +qhost(p) is not kline(p). 19 lines are eligible under #543's plan, 18 of them outside lines 0..7 (which stay live for +#493's frame-zero residual isometry); #545's P is not eligible (its line 39 fails). + +**Decoder.** The nine pseudo-target totals HYP[p]|live span every live decoder row (1 + B(t,.))|live exactly when the +classes of P in E8/2E8 are affinely independent (odd subset sums of nine affinely independent points cover F2^8). A D4 +block's classes span 4 dimensions and a complementary pair's 6 (affine rank 7), so P meets at least two block pairs. +The exact replay checks the decoder on all columns. + +**Count.** R = 420 (the L3 helpers; the punctured lines' carriers stay as wires) + 9 totals + 10 extra carriers. The +extra carriers also serve as junk wires of the block nodes whose outputs outnumber their live inputs. The local +histogram is {1: 1321, 2: 1342, 3: 80, 4: 47, 5: 102, 6: 10, 7: 18}, rank mass 5,129 = 8R + 63 + 14v. + +**Rank-mass identity.** M·live − mass = T(2Kv − L·cst) for v ports and decoder constant cst (#545): at nine stages +(M = 64, K = 4, L = 9, T = 32) T(8v − 9cst) = 32 · 321 = 10,272 here (v = 120, cst = 72: 9,984). The nine-stage +hosting wall R <= 4v = 444 admits R = 439: all 126,432 residuals are hosted whole, with no banks. + +## Review points + +1. **Source registers rewritten in phase B** (as in #543): 483 phase-B source writes (block nodes at block frames, + needer merges at D6 frames). 454 of the 499 phase-B z-writes rewrite a register that a target has already read in + phase B; the others write freed sources and extra carriers, which no target reads. `code/xcheck_p.py` checks the + hypotheses (H1)-(H5) of the interleaved scalar-interface lemma of #543's README and its conclusion term by term; + #493's `interface.py` refuses the word at its check (4), as for #543. +2. **A 111-port unit with decoder constant 63** is an admissible bit supplier for the outer assembly on the same + hypotheses as v = 120 (as #545 states). Nothing on the complex side depends on v. +3. **Helpers written in phase B.** 16 phase-B gates write helpers: extra carriers used as junk wires in the block nodes + (13, at block frames) and their merges with D6 o1 registers (3). They are z-gates under (H3) and are reversed by C. + +## Checks (`verify.sh`) + +```sh +python -m pip install -r research/e8-bit-p111/requirements.txt +bash research/e8-bit-p111/verify.sh /absolute/new/output +``` + +Python 3.11+ with numpy, sympy and scipy, a C++17 compiler (`CXX`) and Boost headers (`BOOST_INCLUDE`). About 4 +minutes on 4 cores (the two controls run in the background). + +0. Manifest. Provenance: 19 files vendored from #510, 5 from #545 and 2 receipts each from #548, #544 and #535, each + checked by git blob id; main's files by sha256 (#493's package through its own `MANIFEST.sha256`, #531's receipt). + Every cited b against its receipt; `code/receipt_kappa.py` recomputes the own κ of #548, #544 and #535 from their + receipts through the same assembly (all 47 constraint values, bootstrap cutoffs and controls equal for their + exact-assembly receipts). +1. The word rebuilt from the frozen plan and parameters equals the shipped word. +2. `code/xcheck_p.py` (independent; reads only the word): the 111 labels are root lines of an even unimodular rank-8 + lattice, the 9 punctured lines are found by enumerating its roots, every frame is closed in the live labels, exact + F2 replay on all 661 columns, the 9 totals sit at the hyperplanes of the 9 punctured lines with COPY ranks + 7 × 9 = 63, and (H1)-(H5) with the conclusion. #543's 9 + 1 word-level controls and 4 punctured-design controls + (`code/mutants_p.py`: a total's d-single read dropped, a mini-node gate dropped, a total moved to the full frame + (decoder constant 64), a needer read dropped; cleanup adjusted alike) are rejected. The same checker accepts #510's + R480, #493's R489 and #545's punctured R489 words. +3. #545's v <= 120 form of #370's checker refuses the word ("x register written"); its relaxed copy passes; #493's + `interface.py` refuses at check (4). +4. Official checkers at (v, R) = (111, 439): legality at NCOPY = 9 (27,234 records, rank mass 5,129); five-stage + columns (883 columns, payload 93 bits < 104); nine-stage (`columns_k`, KPAIRS = 4: 1,327 columns, 18/18 + omitted-bridge controls); eleven-stage (KPAIRS = 5: 1,549 columns, 24/24); the KPAIRS = 2 regression; #370's 6 + record mutants (in #545's form); the 3 phase-B record mutants; Gram determinants and prime witnesses on 636 frames. +5. Geometries over the live labels (#545's parameterised #386 and #445 files); `code/adapt9p.py` parameterises #510's + nine-stage files (27 listed edits; V from the word or the parent GLOBAL.json, the decoder constant from the + records' COPY ranks). +6. Nine-stage price: #442's programme rediscovers `data/selection9-p111.json`; template, parent audit (17 template + mutations rejected), hosted audit (16 mutations rejected); invoice: prefix 177 bits (charged 192), two-level 393 + bits (charged 512), maximum child 42 (2·42² < 64²), row 18,355. +7. Eleven-stage price through #545's parameterised files: #442's programme rediscovers `data/hosting11-p111.json`; all + 38,632 residuals hosted whole; independent audit; invoice: prefix 4,304 bits (charged 4,352), three-level 12,966 + bits (charged 12,992), row 19,431 < 20,161. +8. Controls: (a) #510's R480 word through #510's vendored nine-stage files and through the parameterised files, on the + same geometry: parent GLOBAL.json and template, parent audit, hosted GLOBAL.json, template and audit, and + RESULT9.json are identical (byte for byte where no run time is stored), and reproduce #510's c and κ values and, + with #535's b, #535's κ 0.001584931111785787 (bit-bound); (b) #545's word through #545's files reproduces #545's + c, profile, emitted-program hash and κ. +9. Every number against `EXPECTED.json` and the cited receipts. + +## Retained assumptions + +- Every interface that #472's, #510's, #543's and #545's compositions retain: the all-size bit/Gaussian compiler; the + mutable-basis and target-label realization (review point 1); a v = 111 bit supplier in the outer assembly (review + point 2); whole-call correlated-helper hosting (#442; #545 discloses that several helpers of one call share a family + at disjoint cells, as in #503); compressed windows and restored work; the common grid and tape, precision, row and + analytic contracts. +- Complex savings are cited from their PRs' receipts, not rebuilt. #548, #544 and #535 are open PRs; #548 also retains + #539's bad-class transport as a premise. +- The plan is #543's CP-SAT nomination; P, the free-keeper seed, the receiver choices and the hosting selections are + nominations (`discovery/`); none is claimed optimal. +- This is a conditional finite construction, not a Lean proof. + +## Files + +- `code/gen_p3.py`, `code/pwordl.py`, `code/nodeopt.py`, `code/planio.py`: the generator. +- `code/xcheck_p.py`, `code/mutants.py`, `code/mutants_p.py`, `code/mutrec_b.py`: the independent replay and + interface audit, word-level controls and record-level phase-B mutants. +- `code/adapt9p.py`: the nine-stage parameterisation. +- `code/provenance.py`, `code/cited.py`, `code/receipt_kappa.py`, `code/summary.py`: pins, receipts and comparisons. +- `data/`: the word, plan, parameters, frozen hosting selections, cited b and the receipts of #548, #544 and #535. +- `vendor/pr510/`: #510's nine-stage chain (as in #543's package). `vendor/pr545/`: #545's `adapt.py` and + `punct_cfg.py`, and its word, hosting selection and EXPECTED.json (control). +- `discovery/` (not run by `verify.sh`): the nomination of P and of the generator parameters. +- `DEPENDS.json`: the files of main this package uses, pinned. diff --git a/research/e8-bit-p111/SOURCE-pr510.json b/research/e8-bit-p111/SOURCE-pr510.json new file mode 100644 index 0000000000..f44af5ca75 --- /dev/null +++ b/research/e8-bit-p111/SOURCE-pr510.json @@ -0,0 +1,85 @@ +{ + "schema": "source-pins/1", + "source": "https://github.com/CrocSwap/integer-mult-bounds/pull/510", + "head_repository": "DreamingOfClouds/integer-mult-bounds", + "commit": "64edee18af02aab0c1696b677a0287dd0146381b", + "note": "PR #510 (DreamingOfClouds, with Claude assistance): its nine-stage pricing chain as run by its verify.py. research/e8-r480-nine-stage: code/adapt.py (one listed adaptation of audit_parent_k.py), code/assemble9.py (nine-stage invoice and PR #493's outer assembly), data/selection9-r480.json, EXPECTED.json, and the files it vendors from PR #463 (research/e8-r715-eleven-stage-whole: global_k, emit_k, audit_parent_k, audit_hosted_k, bank_routes_k, hosting_k; research/e8-r740-replica32: PR #443's config, bank_routes, tile_m, e8core and PR #315's engines; research/idle-axis-hosting/hosting_core.py, PR #442). research/e8-r480-dead-totals/data/bitE8mut-r480.json.gz is PR #503's R480 word as carried by PR #510's branch. Every file listed has, in this package, the git blob id it has at the commit above (git ls-tree -r -- ).", + "identical": { + "vendor/pr510/e8-r480-dead-totals/data/bitE8mut-r480.json.gz": { + "source_path": "research/e8-r480-dead-totals/data/bitE8mut-r480.json.gz", + "blob": "8e59c4b5380d522b1c1cb930029a71556b2fa65f" + }, + "vendor/pr510/e8-r480-nine-stage/EXPECTED.json": { + "source_path": "research/e8-r480-nine-stage/EXPECTED.json", + "blob": "c89fe719e81ae3a4d920bf50f87b7100bbf335ae" + }, + "vendor/pr510/e8-r480-nine-stage/code/adapt.py": { + "source_path": "research/e8-r480-nine-stage/code/adapt.py", + "blob": "7c595c50bc3910577f8587ca94ce8b9c0e6f55b0" + }, + "vendor/pr510/e8-r480-nine-stage/code/assemble9.py": { + "source_path": "research/e8-r480-nine-stage/code/assemble9.py", + "blob": "238226c568742dd5723b03eabae564a2eaa2bda9" + }, + "vendor/pr510/e8-r480-nine-stage/data/selection9-r480.json": { + "source_path": "research/e8-r480-nine-stage/data/selection9-r480.json", + "blob": "786ba4d5bedef5888ce02629d6caf9aebb290b73" + }, + "vendor/pr510/e8-r480-nine-stage/vendor/e8-r715-eleven-stage-whole/code/audit_hosted_k.py": { + "source_path": "research/e8-r480-nine-stage/vendor/e8-r715-eleven-stage-whole/code/audit_hosted_k.py", + "blob": "8864ca9b942a3507171edca0cf6cbaaf10fcf434" + }, + "vendor/pr510/e8-r480-nine-stage/vendor/e8-r715-eleven-stage-whole/code/audit_parent_k.py": { + "source_path": "research/e8-r480-nine-stage/vendor/e8-r715-eleven-stage-whole/code/audit_parent_k.py", + "blob": "ee54ca566ff9d8553f845144dd4570fa1fe21495" + }, + "vendor/pr510/e8-r480-nine-stage/vendor/e8-r715-eleven-stage-whole/code/bank_routes_k.py": { + "source_path": "research/e8-r480-nine-stage/vendor/e8-r715-eleven-stage-whole/code/bank_routes_k.py", + "blob": "aaab9d5ae3f280dcb82775337d88dc244984e225" + }, + "vendor/pr510/e8-r480-nine-stage/vendor/e8-r715-eleven-stage-whole/code/emit_k.py": { + "source_path": "research/e8-r480-nine-stage/vendor/e8-r715-eleven-stage-whole/code/emit_k.py", + "blob": "b84c795a045143639efe6aefb0562b3c20a2c9d7" + }, + "vendor/pr510/e8-r480-nine-stage/vendor/e8-r715-eleven-stage-whole/code/global_k.py": { + "source_path": "research/e8-r480-nine-stage/vendor/e8-r715-eleven-stage-whole/code/global_k.py", + "blob": "3df12bd632f1fdba825ced57110a1fee82eb2857" + }, + "vendor/pr510/e8-r480-nine-stage/vendor/e8-r715-eleven-stage-whole/code/hosting_k.py": { + "source_path": "research/e8-r480-nine-stage/vendor/e8-r715-eleven-stage-whole/code/hosting_k.py", + "blob": "02fea8320b676e14f8ffa242c3ddfa85fa829e31" + }, + "vendor/pr510/e8-r480-nine-stage/vendor/e8-r740-replica32/code/bank_routes.py": { + "source_path": "research/e8-r480-nine-stage/vendor/e8-r740-replica32/code/bank_routes.py", + "blob": "9e43a876664a26899762f3b8e9a582e684a3650a" + }, + "vendor/pr510/e8-r480-nine-stage/vendor/e8-r740-replica32/code/config.py": { + "source_path": "research/e8-r480-nine-stage/vendor/e8-r740-replica32/code/config.py", + "blob": "c6824d9c4ea37d58c0d24cb7a3021c4dfcc6f3bb" + }, + "vendor/pr510/e8-r480-nine-stage/vendor/e8-r740-replica32/code/e8core.py": { + "source_path": "research/e8-r480-nine-stage/vendor/e8-r740-replica32/code/e8core.py", + "blob": "78983b368715e89fe8524a6dd2c13585d2b5394e" + }, + "vendor/pr510/e8-r480-nine-stage/vendor/e8-r740-replica32/code/engines/base_two_moment.py": { + "source_path": "research/e8-r480-nine-stage/vendor/e8-r740-replica32/code/engines/base_two_moment.py", + "blob": "14a90fa0e788217856f070382c9e61bd7c603c61" + }, + "vendor/pr510/e8-r480-nine-stage/vendor/e8-r740-replica32/code/engines/moment.py": { + "source_path": "research/e8-r480-nine-stage/vendor/e8-r740-replica32/code/engines/moment.py", + "blob": "7d697cb45203fe72d97037403d6da41f71f3b7fd" + }, + "vendor/pr510/e8-r480-nine-stage/vendor/e8-r740-replica32/code/engines/outer.py": { + "source_path": "research/e8-r480-nine-stage/vendor/e8-r740-replica32/code/engines/outer.py", + "blob": "43007488ad2c43187c77cc6798beff8b1cd76ad2" + }, + "vendor/pr510/e8-r480-nine-stage/vendor/e8-r740-replica32/code/tile_m.py": { + "source_path": "research/e8-r480-nine-stage/vendor/e8-r740-replica32/code/tile_m.py", + "blob": "cd5fd9b5399f5e7afe5d2a95bd3504bd543c0d60" + }, + "vendor/pr510/e8-r480-nine-stage/vendor/idle-axis-hosting/hosting_core.py": { + "source_path": "research/e8-r480-nine-stage/vendor/idle-axis-hosting/hosting_core.py", + "blob": "68b536daa9e6546c0aa046934bf840aaa3c3fde3" + } + } +} diff --git a/research/e8-bit-p111/SOURCE-pr535.json b/research/e8-bit-p111/SOURCE-pr535.json new file mode 100644 index 0000000000..9002227599 --- /dev/null +++ b/research/e8-bit-p111/SOURCE-pr535.json @@ -0,0 +1,17 @@ +{ + "schema": "source-pins/1", + "source": "https://github.com/CrocSwap/integer-mult-bounds/pull/535", + "head_repository": "icekylinx/integer-mult-bounds", + "commit": "8b758a103915179322766bed5798f9b6e7198011", + "note": "PR #535 (icekylinx, with GPT-6 Astra assistance): its EXPECTED.json receipt (complex b, and its kappa with PR #510's bit c) and its exact assembly receipt with PR #493's bit c (a control here). Every file listed has, in this package, the git blob id it has at the commit above (the same blob ids as at 25f9b7c, the head when this package was prepared).", + "identical": { + "data/complex/pr535/EXPECTED.json": { + "source_path": "research/post-center-rank4-global/EXPECTED.json", + "blob": "2c5a1a32628e2e93b29a54ecfb9ccfa5d84b9b17" + }, + "data/complex/pr535/EXACT-ASSEMBLY-PR493.json": { + "source_path": "research/post-center-rank4-global/evidence/audit/EXACT-ASSEMBLY-PR493.json", + "blob": "91873f25e16f5184f4baaaa900622fe62aeb3760" + } + } +} diff --git a/research/e8-bit-p111/SOURCE-pr544.json b/research/e8-bit-p111/SOURCE-pr544.json new file mode 100644 index 0000000000..12fd9dd44e --- /dev/null +++ b/research/e8-bit-p111/SOURCE-pr544.json @@ -0,0 +1,17 @@ +{ + "schema": "source-pins/1", + "source": "https://github.com/CrocSwap/integer-mult-bounds/pull/544", + "head_repository": "chafreaky/integer-mult-bounds", + "commit": "4858bb38da10cd8a1cd25ef85e7c14f4e2b529dd", + "note": "PR #544 (chafreaky, with OpenAI Codex assistance; static44 appendix from PR #541, Dugongue): its EXPECTED.json receipt (the static44 complex b, its selectively reframed R480-lineage bit c and its kappa, bit-bound) and its exact assembly receipt from its Linux / Python 3.12 validation run (the 47 strict outer constraints and the bootstrap cutoffs). code/receipt_kappa.py recomputes that assembly from the receipt's c and b. Every file listed has, in this package, the git blob id it has at the commit above.", + "identical": { + "data/complex/pr544/EXPECTED.json": { + "source_path": "research/static44-selective/EXPECTED.json", + "blob": "d6ca3ca276ffe69c0d6d226ed8489ec5a5adec7d" + }, + "data/complex/pr544/EXACT-ASSEMBLY.json": { + "source_path": "research/static44-selective-validation/linux-python312/assembly/EXACT-ASSEMBLY.json", + "blob": "a0f66a8c8f5a5634a02beac66b0055d7e2b73365" + } + } +} diff --git a/research/e8-bit-p111/SOURCE-pr545.json b/research/e8-bit-p111/SOURCE-pr545.json new file mode 100644 index 0000000000..9af8fcb1bc --- /dev/null +++ b/research/e8-bit-p111/SOURCE-pr545.json @@ -0,0 +1,29 @@ +{ + "schema": "source-pins/1", + "source": "https://github.com/CrocSwap/integer-mult-bounds/pull/545", + "head_repository": "DreamingOfClouds/integer-mult-bounds", + "commit": "376426f3b2b26ab430d10baf16bbd9ff42990a7c", + "note": "PR #545 (DreamingOfClouds, with Claude Opus 5.5 via Claude Code; draft): code/adapt.py (parameterises PR #493's eleven-stage chain, the nine- and eleven-stage geometries, the independent checker and one record mutant for a word with v <= 120 live ports and any copy ranks; run here unchanged on this package's word) and code/punct_cfg.py (the configuration it imports); its word, hosting selection and EXPECTED.json, used as a control (its eleven-stage c is reproduced through the same adapted files). Every file listed has, in this package, the git blob id it has at the commit above.", + "identical": { + "vendor/pr545/code/adapt.py": { + "source_path": "research/e8-punctured111-star/code/adapt.py", + "blob": "bd1552af18ae259c01039627aa06006b9d5e760f" + }, + "vendor/pr545/code/punct_cfg.py": { + "source_path": "research/e8-punctured111-star/code/punct_cfg.py", + "blob": "ee5ee42498bb170225ebd7842eadf93b5d6e592f" + }, + "vendor/pr545/data/bitE8mut-v111-r489.json.gz": { + "source_path": "research/e8-punctured111-star/data/bitE8mut-v111-r489.json.gz", + "blob": "f69cc130937c90fedea71f69e9fc1720f41224bf" + }, + "vendor/pr545/data/hosting-v111-r489.json": { + "source_path": "research/e8-punctured111-star/data/hosting-v111-r489.json", + "blob": "131d6cbc668b4377e64d3677c301228b3b9608c5" + }, + "vendor/pr545/EXPECTED.json": { + "source_path": "research/e8-punctured111-star/EXPECTED.json", + "blob": "ac7f102363a64ae19ba4a3edda9176f0b17ae9dc" + } + } +} diff --git a/research/e8-bit-p111/SOURCE-pr548.json b/research/e8-bit-p111/SOURCE-pr548.json new file mode 100644 index 0000000000..efaf9fab93 --- /dev/null +++ b/research/e8-bit-p111/SOURCE-pr548.json @@ -0,0 +1,17 @@ +{ + "schema": "source-pins/1", + "source": "https://github.com/CrocSwap/integer-mult-bounds/pull/548", + "head_repository": "Dugongue/integer-mult-bounds", + "commit": "66dbc2bffc1bfa8d50b3f9a2ac7ccba8fd9f6f36", + "note": "PR #548 (Dugongue): its EXPECTED.json receipt (complex b, its own bit c and kappa) and its exact assembly receipt (the 47 strict outer constraints, bootstrap cutoffs and controls; code/receipt_kappa.py recomputes it). Every file listed has, in this package, the git blob id it has at the commit above.", + "identical": { + "data/complex/pr548/EXPECTED.json": { + "source_path": "research/pruned-trace-response/EXPECTED.json", + "blob": "8bbdb3bcd4588f42b30e2ab8a2c9af04ed2b7b71" + }, + "data/complex/pr548/EXACT-ASSEMBLY.json": { + "source_path": "research/pruned-trace-response/evidence/EXACT-ASSEMBLY.json", + "blob": "1a99506aa28c8179248ec9646cca314c21e91187" + } + } +} diff --git a/research/e8-bit-p111/code/adapt9p.py b/research/e8-bit-p111/code/adapt9p.py new file mode 100644 index 0000000000..96bbc2ba9b --- /dev/null +++ b/research/e8-bit-p111/code/adapt9p.py @@ -0,0 +1,92 @@ +# Prepared by eumemic with substantial Anthropic Claude assistance; Apache-2.0. +"""Parameterise PR #510's nine-stage chain (PR #463's global_k / audit_parent_k / emit_k / audit_hosted_k and PR #510's +assemble9) for a word with v <= 120 live ports and nine totals of any copy rank, the way PR #545's code/adapt.py +parameterises PR #493's eleven-stage chain. Every change is an exact string edit of a vendored file (each anchor must +occur exactly once); nothing is read from the environment. V comes from the word (global_k) or the parent GLOBAL.json; +the decoder constant CST (the sum of the nine COPY ranks) is replayed from the records (global_k, audit_parent_k) or +from the template's local programs, which the parent audit checks against the records (emit_k, audit_hosted_k); the +assembly reads the rank-mass deficit T (2 K v - L CST) from the hosted audit and checks that it fits nine copy ranks. +For v = 120 and nine rank-8 totals every adapted script computes what the vendored one computes. +usage: adapt9p.py VENDORED_CODE_DIR (e8-r715-eleven-stage-whole/code) PR510_CODE_DIR (e8-r480-nine-stage/code) + AUDIT_PARENT_K (PR #510's adapted copy) OUTDIR""" +import sys +from pathlib import Path + +code, c510, apk, out = (Path(x).resolve() for x in sys.argv[1:5]) +EDITS = { + 'global_k.py': (code / 'global_k.py', [ + ("sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), '../../e8-r740-replica32/code'))", + "sys.path.insert(0, %r); sys.path.insert(0, %r)" % (str(code.parents[1] / 'e8-r740-replica32/code'), str(code))), + (" DEFICIT=T*(2*K*120-9*H*L)\n", " DEFICIT=None # T*(2*K*V-L*CST), set after the local replay gives CST\n"), + (" assert (w['h'],V,w['form'])==(8,120,'/2')", + " assert (w['h'],w['form'])==(8,'/2') and 100<=V<=120"), + (" streams={}; checks={}; localH=None\n", " streams={}; checks={}; localH=None; CSTD=[]\n"), + ("norm=[1]*LOCAL+[0]; maxnorm=1; center=None; hist=Counter(); events=[]; copies=0; adds=0\n", + "norm=[1]*LOCAL+[0]; maxnorm=1; center=None; hist=Counter(); events=[]; copies=0; adds=0; crs=[]\n"), + ("copy_rank=abs(dim(c)-dim(f)); assert copy_rank==8", "copy_rank=abs(dim(c)-dim(f)); assert 0 line l_p (reads the source of p's d-single line l_p, before its cell gate) + -> D6[p, qhost(p)] (reads that D6's o1; qhost(p) still reads o1 in phase B) + -> p^perp (reads p's needers at its two (z,z) D6s, one of which carries the rewritten free keeper) +Eligible p: both (z,z) cells of p's 4-frame contain p, and qhost(p) != l_p. Lines a mini-node needs that no free +keeper or freed source of B holds are brought by extra carriers (a helper reading the source at its line frame in +phase A). A freed source is the source sitting in the d-single cell of a punctured target (no reader in phase B). +Block nodes whose outputs outnumber their live inputs get junk wires (extra carriers of that block when available). +usage: python -I gen_p3.py data/plan-l3.json data/punct.json OUT.json.gz (PLAN: the L3 plan of PR #543; PARAMS: the +punctured set, the plan_thirds seed, the receiver choices and the block-node search effort)""" +import itertools +import json +import os +import random +import sys +from collections import defaultdict + +HERE = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, os.path.join(HERE, "..", "..", "e8-bit-carrier-skeleton", "code", "skeleton")) # PR #493 on main +sys.path.insert(0, HERE) +import e8 # noqa: E402 +from geom import Geom # noqa: E402 +from gen import span_add # noqa: E402 +from planio import load_plan # noqa: E402 +from wordkit import save, solve_transform_lu # noqa: E402 +from nodeopt import realise # noqa: E402 +from pwordl import PWordL # noqa: E402 + +V = e8.V +NODEOPT = [int(os.environ.get("NODEOPT", "2000"))] +GI = e8.GI + + +def rank_of(vecs): + b = [] + for v in vecs: + span_add(b, v) + return len(b) + + +# ---- the L3 plan helpers (as in PR #543's gen_l3.py) +def derive(G, sol, freeline=None): + freeline = freeline or {} + part, kline = sol["partner"], sol["kline"] + dcellof, keepline, home, qhostD6 = {}, {}, {}, {} + for f in G.frames4: + for t in f: + x = part[t] + C = tuple(sorted(y for y in f if y not in (t, x))) + dcellof[t] = C + keepline[C] = kline[t] + home[kline[t]] = C + qhostD6[t] = tuple(sorted((t, sol["qhost"][t]))) + assert len({dcellof[t] for t in f}) == 4 and len({qhostD6[t] for t in f}) == 4 + for c in itertools.combinations(f, 2): + if c not in keepline: + keepline[c] = c[freeline.get(c, 0)] + hosted = set(qhostD6.values()) + zz = [H for H in G.cells if H not in hosted] + return dcellof, keepline, home, qhostD6, zz + + +def ex_part(G, sol, q, H): + S = 0 + for c in G.qcells[q]: + S |= G.cmask[c] + D = G.cmask[G.dcell[H]] + Qc = G.cmask[G.qcell[(q, H)]] + return S ^ D ^ Qc ^ ({"D": D, "Q": Qc, "0": 0}[sol["ext"][(q, H)]]) + + +def plan_thirds(G, sol, rng, tries=20000): + dcellof, keepline, home, qhostD6, zz = derive(G, sol) + dualcells = set(home.values()) + Ex = {H: ex_part(G, sol, G.d6quads[H][0], H) ^ ex_part(G, sol, G.d6quads[H][1], H) for H in G.cells} + base = {} + for t in range(V): + acc = e8.HYP[t] ^ (1 << keepline[dcellof[t]]) + for x in G.phi[t]: + if x != t: + acc ^= Ex[tuple(sorted((t, x)))] + base[t] = acc + freeline, assign, failed = {}, {}, [] + for bi in range(len(G.blocks)): + cb = G.comp[bi] + us = [t for t in range(V) if G.blk[t] == cb] + Hs = sorted(H for H in zz if G.cblk[H] == cb) + cs = [c for c in G.cells if c not in dualcells and G.cblk[c] == bi] + assert len(Hs) == len(cs) == 6 + ok = False + for _ in range(tries): + fl = {c: rng.randrange(2) for c in cs} + perm = list(cs) + rng.shuffle(perm) + extra = {} + for H, c in zip(Hs, perm): + r = sol["rcv"][H] + extra[r] = extra.get(r, 0) ^ (1 << c[fl[c]]) + if rank_of([base[u] ^ extra.get(u, 0) for u in us]) == 12: + ok = True + break + if not ok: + failed.append(bi) + continue + freeline.update(fl) + assign.update(dict(zip(Hs, perm))) + return (freeline, assign) if not failed else ("failed", failed) + + +def decoder_rows(cvals, live): + """for every live t: the subset of the totals whose XOR is (1 + B(t,.)) restricted to the live lines.""" + rows = {} + for t in e8.bits(live): + want = (((1 << V) - 1) ^ sum((int(GI[t, b]) & 1) << b for b in range(V))) & live + found = None + for k in range(len(cvals) + 1): + for C in itertools.combinations(range(len(cvals)), k): + m = 0 + for c in C: + m ^= cvals[c] + if m == want: + found = list(C) + break + if found is not None: + break + assert found is not None, ("decoder row not in the span of the totals", t) + rows[t] = found + return rows + + +def eligible(G, sol, dcellof, keepline, qhostD6, zz, p): + f = G.phi[p] + zzf = [H for H in zz if G.phi[H[0]] == f] + Hp = qhostD6[p] + lp = keepline[dcellof[p]] + return len(zzf) == 2 and all(p in H for H in zzf) and lp not in Hp, zzf + + +def express(target, wires, content): + """combination (list of wires) of the current contents whose XOR is target, or (None, residual).""" + basis = [] + for w in wires: + v, combo = content[w], {w} + for pb, bv, bc in basis: + if v >> pb & 1: + v ^= bv + combo ^= bc + if v: + basis.append((v.bit_length() - 1, v, combo)) + basis.sort(key=lambda z: -z[0]) + v, combo = target, set() + for pb, bv, bc in basis: + if v >> pb & 1: + v ^= bv + combo ^= bc + return (sorted(combo), 0) if v == 0 else (None, v) + + +def build(G, sol, P, freeline, thirdperm, extras=None, node_seed=0, verbose=True, rcvsel=None): + extras = extras or {} # block -> sorted list of lines brought by extra carriers + rcvsel = rcvsel or {} # p -> 1, 2 or 3: which of p's two (z,z) D6s (bit 0: first, bit 1: second) have rcv p + Pset = set(P) + live = ((1 << V) - 1) & ~sum(1 << s for s in Pset) + isl = lambda s: s not in Pset # noqa: E731 + W = PWordL(live) + dcellof, keepline, home, qhostD6, zz = derive(G, sol, freeline) + dualcells = set(home.values()) + for p in P: + ok, _ = eligible(G, sol, dcellof, keepline, qhostD6, zz, p) + assert ok, ("not eligible", p) + Ex = {H: (ex_part(G, sol, G.d6quads[H][0], H) ^ ex_part(G, sol, G.d6quads[H][1], H)) & live for H in G.cells} + tauB = defaultdict(list) # block B -> punctured p whose Q-part comes from B + for p in sorted(P): + tauB[G.comp[G.blk[p]]].append(p) + stats = dict(extra_carriers=0, junk_wires=0, rcv_flips=0) + # ---- phase A: carriers, extra carriers, tau line reads (all at line frames, before any cell gate) + branch = {} + for s in range(V): + for c in G.cells_of[s]: + if c == home[s]: + branch[(s, c)] = s if isl(s) else None + else: + k = W.helper("carrier" if isl(s) else "junk-carrier") + if isl(s): + W.gate(1 << s, k, [s]) + branch[(s, c)] = k + xcar = {} + for B in sorted(extras): + for s in extras[B]: + k = W.helper("extra-carrier") + W.gate(1 << s, k, [s]) + xcar[s] = k + stats["extra_carriers"] += 1 + tau = {} + for p in sorted(P): + lp = keepline[dcellof[p]] + tau[p] = W.helper("total") + W.gate(1 << lp, tau[p], [lp]) + # ---- cells + cell_quads = defaultdict(list) + for q in range(len(G.quads)): + for c in G.qcells[q]: + cell_quads[c].append(q) + delivery, keeper = {}, {} + for c in G.cells: + F = G.cmask[c] + l = keepline[c] + l2 = c[0] if l == c[1] else c[1] + qa, qb = cell_quads[c] + e = W.helper("empty") + bl, bl2 = branch[(l, c)], branch[(l2, c)] + assert bl2 is not None, ("delivery branch missing", c) + if bl is not None and isl(l): + W.gate(F, bl2, [bl]) + W.gate(F, e, [bl2]) + delivery[(c, qa)] = bl2 + delivery[(c, qb)] = e + keeper[c] = bl # None: the punctured source of a home cell; else a register (maybe 0) + # ---- quads + qout = {} + for q in range(len(G.quads)): + U = G.quads[q] + cs = G.qcells[q] + wires = [delivery[(c, q)] for c in cs] + inputs = [W.xpart(w) for w in wires] + outs = [ex_part(G, sol, q, H) & live for H in G.qowners[q]] + ops, assign = solve_transform_lu(inputs, outs) + for a, b in ops: + W.gate(U, wires[a], [wires[b]]) + for k, H in enumerate(G.qowners[q]): + qout[(q, H)] = wires[assign[k]] + assert W.xpart(qout[(q, H)]) == outs[k] + # ---- D6 nodes: o1 = o1 + o2 (o2 dead) + E = {} + for H in G.cells: + q1, q2 = G.d6quads[H] + o1, o2 = qout[(q1, H)], qout[(q2, H)] + W.gate(G.D6[H], o1, [o2]) + E[H] = o1 + assert W.xpart(o1) == Ex[H] + # ---- receivers of the (z,z) D6s: every tau gets at least one rewritten free keeper + rcv = dict(sol["rcv"]) + zzp = {} + for p in sorted(P): + f = G.phi[p] + zzp[p] = sorted(H for H in zz if G.phi[H[0]] == f) + if p in rcvsel: + for i, H in enumerate(zzp[p]): + want = p if (rcvsel[p] >> i) & 1 else (H[0] if H[1] == p else H[1]) + if rcv[H] != want: + rcv[H] = want + stats["rcv_flips"] += 1 + if not any(rcv[H] == p for H in zzp[p]): + rcv[zzp[p][0]] = p + stats["rcv_flips"] += 1 + # ---- free keepers (third registers of the (z,z) D6s) per complementary block + fk_of = {} # zz D6 -> keeper register (free keeper of the complementary block) + for bi in range(len(G.blocks)): + cb = G.comp[bi] + for H in sorted(H for H in zz if G.cblk[H] == cb): + fk_of[H] = keeper[thirdperm[H]] + assert fk_of[H] is not None + # ---- phase-A mini-nodes at D4(B): rewrite one free keeper per tau to carry the tau's Q-part + missing = {} + for B in sorted(tauB): + Bm = G.blocks[B] + fks = sorted({fk_of[H] for H in zz if G.cblk[H] == G.comp[B]}) + freed = [] + for q in P: + if G.blk[q] == B: + s = keepline[dcellof[q]] + if isl(s): + freed.append(s) # the source in q's d-single cell: no reader in phase B + wires = fks + [xcar[s] for s in extras.get(B, [])] + freed + plan_writes = [] + for p in tauB[B]: + Hp = qhostD6[p] + lp = keepline[dcellof[p]] + Np = (e8.HYP[p] & live) ^ (1 << lp) ^ Ex[Hp] + for H in zzp[p]: + Np ^= Ex[H] + mine = [H for H in zzp[p] if rcv[H] == p] + des = fk_of[mine[0]] + for H in mine[1:]: + Np ^= W.xpart(fk_of[H]) # p's other received free keepers keep their own line + plan_writes.append((p, des, Np)) + cont = {w: W.xpart(w) for w in wires} + for p, des, Np in plan_writes: + assert des in cont, ("designated keeper not among the block's free keepers", p) + need_ = Np ^ cont[des] + combo, res = express(need_, [w for w in wires if w != des], cont) + if combo is None: + missing.setdefault(B, set()).update(e8.bits(res)) + continue + if combo: + W.gate(Bm, des, combo) + cont[des] ^= need_ + assert W.xpart(des) == Np or B in missing + if missing: + return ("missing", {B: sorted(v) for B, v in missing.items()}) + # ---- (z,z) thirds and needers (phase A) + needer = {} + for H in sorted(zz): + k = fk_of[H] + W.gate(G.D6[H], k, [E[H]]) + r = rcv[H] + other = H[0] if r == H[1] else H[1] + needer[(H, r)] = k + needer[(H, other)] = E[H] + for t in range(V): + H = qhostD6[t] + x = H[0] if H[1] == t else H[1] + needer[(H, x)] = E[H] + # ---- pseudo-target totals: D6[p, qhost(p)] then p^perp + for p in sorted(P): + Hp = qhostD6[p] + W.gate(G.D6[Hp], tau[p], [E[Hp]]) + W.gate(e8.HYP[p], tau[p], [needer[(H, p)] for H in zzp[p]]) + assert W.xpart(tau[p]) == e8.HYP[p] & live, ("total", p) + W.ret.append(tau[p]) + cvals = [W.xpart(q) for q in W.ret] + rows = decoder_rows(cvals, live) + # ---- Q needs of the live targets + need = {} + for t in e8.bits(live): + C = dcellof[t] + kp = keeper[C] + acc = e8.HYP[t] & live + if kp is not None and isl(keepline[C]): + acc ^= W.xpart(kp) + for x in G.phi[t]: + if x == t: + continue + H = tuple(sorted((t, x))) + acc ^= W.xpart(E[H]) if H == qhostD6[t] else W.xpart(needer[(H, t)]) + need[t] = acc + assert acc & ~(G.blocks[G.comp[G.blk[t]]] & live) == 0, ("need outside the Q block", t) + # ---- phase B: d-single reads + W.phase = "B" + for t in e8.bits(live): + C = dcellof[t] + kp = keeper[C] + if kp is not None and isl(keepline[C]): + W.gate(G.cmask[C], V + t, [kp]) + # ---- block nodes (phase B) + qdel = {} + for bi in range(len(G.blocks)): + cb = G.comp[bi] + us = [t for t in e8.bits(live) if G.blk[t] == cb] + srcw = [keeper[c] for c in G.cells if c in dualcells and G.cblk[c] == bi and keeper[c] is not None + and isl(keepline[c])] + assert all(W.xpart(w) & (W.xpart(w) - 1) == 0 for w in srcw) + pos = {W.xpart(w).bit_length() - 1: k for k, w in enumerate(srcw)} + rowsN = [] + for u in us: + r = 0 + for l in e8.bits(need[u]): + r |= 1 << pos[l] + rowsN.append(r) + n0 = len(srcw) + junk = [] + # junk directions: extra carriers of this block (x-part = a source line: wire + that source has x-part 0) + avail = [xcar[s] for s in extras.get(bi, []) if s in [W.xpart(w).bit_length() - 1 for w in srcw]] + bs = [] + allrows = [] + for r in rowsN: + rr = r + if not span_add(bs, rr): + j = len(junk) + if j < len(avail): + junk.append(avail[j]) + else: + junk.append(W.helper("junk-wire")) + stats["junk_wires"] += 1 + idx = n0 + j + rr = r | (1 << idx) + if junk[-1] in xcar.values(): + s = W.xpart(junk[-1]).bit_length() - 1 + rr ^= 1 << pos[s] # junk direction = extra carrier + its source: x-part 0 + assert span_add(bs, rr) + allrows.append(rr) + n = n0 + len(junk) + for i in range(n): + if len(allrows) == n: + break + if span_add(bs, 1 << i): + allrows.append(1 << i) + assert len(allrows) == n + wires = srcw + junk + _, ops, perm = realise(allrows, random.Random(bi * 7 + node_seed), tries=NODEOPT[0]) + for a, b in ops: + W.gate(G.blocks[bi], wires[a], [wires[b]]) + for k, u in enumerate(us): + qdel[u] = wires[perm[k]] + assert W.xpart(qdel[u]) == need[u], ("qdel", bi, u) + for t in e8.bits(live): + H = qhostD6[t] + W.gate(G.D6[H], qdel[t], [E[H]]) + needer[(H, t)] = qdel[t] + for t in e8.bits(live): + srcs = [needer[(tuple(sorted((t, x))), t)] for x in G.phi[t] if x != t] + W.gate(e8.HYP[t], V + t, srcs) + word = W.build(rows) + word["stats"] = stats + if verbose: + print("R", word["R"], "v", word["v"], "gates D/A/B/C", len(word["D"]), len(word["A"]), len(word["B"]), + len(word["C"]), stats) + return W, word + + +def make(plan, P, seed=0, node_seed=0, rcvsel=None): + G = Geom() + sol = load_plan(plan) + pt = plan_thirds(G, sol, random.Random(seed)) + assert pt[0] != "failed" + freeline, assign = pt + extras = {} + for _ in range(20): + res = build(G, sol, P, freeline, assign, extras=extras, node_seed=node_seed, verbose=False, rcvsel=rcvsel) + if res[0] == "missing": + for B, ls in res[1].items(): + extras[B] = sorted(set(extras.get(B, [])) | set(ls)) + continue + break + return build(G, sol, P, freeline, assign, extras=extras, node_seed=node_seed, rcvsel=rcvsel), extras + + +if __name__ == "__main__": + # usage: gen_p3.py PLAN PARAMS.json OUT.json.gz ; PARAMS: punctured set P, plan_thirds seed, receiver choices, NODEOPT + prm = json.load(open(sys.argv[2])) + NODEOPT[0] = prm["nodeopt"] + sel = {int(k): v for k, v in prm["receivers"].items()} + (W, word), extras = make(sys.argv[1], sorted(prm["punctured"]), seed=prm["thirds_seed"], rcvsel=sel) + print("extra carriers per block", {B: len(v) for B, v in sorted(extras.items())}) + save(word, sys.argv[3]) diff --git a/research/e8-bit-p111/code/mutants.py b/research/e8-bit-p111/code/mutants.py new file mode 100644 index 0000000000..5b0fa19220 --- /dev/null +++ b/research/e8-bit-p111/code/mutants.py @@ -0,0 +1,160 @@ +"""Mutation controls for xcheck.py (exact replay and interleaved-interface audit). Every negative control must be +rejected: either the replay raises (frame legality or the exact F2 identity fails), or a hypothesis or conclusion of the +interleaved lemma fails. The positive control must be accepted. +usage: mutants.py XCHECK_PY WORD.json.gz [--json OUT] + +Negative controls (sites are found from the word's structure, not from producer code): + 1 target feeds a source: a cancelled pair x_k += y_t inserted before the target's d-single read of x_k (F2-neutral) + 2 target feeds a helper: a cancelled pair h += y_t inserted before the first target read of a helper h (F2-neutral) + 3 drop a cleanup gate (the literal inverse of the last phase-B source write) + 4 corrupt a phase-B source write: its source operand replaced by another source register of the same block frame, + with the matching cleanup gate changed the same way (C stays the literal reverse; every source is restored) + 5 move a phase-B source write below the source's frame (to the destination source's own line) + 6 a target reads another target (a single read at frame 0 at the start of phase B, so V != I) + 7 corrupt the frame-0 preconditioner (drop one helper read from a D gate) + 8 move a phase-B source write before the target read of that register (the write order relative to the reads) + 9 move a cleanup gate off the full frame (to the frame of the gate it inverts) +Positive control: + P a cancelled pair of target-to-target reads at frame 0 (y_t += y_u twice: V = I, allowed by the lemma) +Prepared by eumemic with substantial Anthropic Claude assistance; Apache-2.0.""" +import copy +import gzip +import importlib.util +import json +import sys + +sp = importlib.util.spec_from_file_location("xcheck", sys.argv[1]) +X = importlib.util.module_from_spec(sp) +sp.loader.exec_module(X) +w0 = json.load(gzip.open(sys.argv[2], "rt")) +v = w0["v"] +is_y = lambda r: v <= r < 2 * v +is_z = lambda r: not is_y(r) +ZERO = next(k for k, f in enumerate(w0["frames"]) if not f) + + +def zgates(w): + return [("A", i) for i, g in enumerate(w["A"]) if is_z(g[1])] + [("B", i) for i, g in enumerate(w["B"]) if is_z(g[1])] + + +def mirror(w, sec, i): + zg = zgates(w) + return len(zg) - 1 - zg.index((sec, i)) + + +def evaluate(w): + try: + W, lab, fl, H, C, info = X.check(w) + except X.Reject as e: + return False, "replay: " + str(e)[:90] + bad = [k for k, x in list(H.items()) + list(C.items()) if not x] + return (not bad), ("interface: fails " + ",".join(bad)) if bad else "accepted" + + +def first_keeper_read(w): + return next(j for j, g in enumerate(w["B"]) if is_y(g[1]) and any(s < v for s in g[2])) + + +def b_source_writes(w): + return [i for i, g in enumerate(w["B"]) if g[1] < v and all(s < v for s in g[2]) and len(g[2]) == 1] + + +def m1(w): + j = first_keeper_read(w) + F, t, srcs = w["B"][j] + xk = next(s for s in srcs if s < v) + w["B"][j:j] = [[F, xk, [t]], [F, xk, [t]]] + return "target feeds a source (cancelled pair, F2-neutral)" + + +def m2(w): + j = next(j for j, g in enumerate(w["B"]) if is_y(g[1]) and any(s >= 2 * v for s in g[2])) + F, t, srcs = w["B"][j] + h = next(s for s in srcs if s >= 2 * v) + w["B"][j:j] = [[F, h, [t]], [F, h, [t]]] + return "target feeds a helper (cancelled pair, F2-neutral)" + + +def m3(w): + last = max(i for i, g in enumerate(w["B"]) if g[1] < v) + p = mirror(w, "B", last) + assert p == 0 + w["C"].pop(p) + return "drop a cleanup gate (inverse of the last phase-B source write)" + + +def m4(w): + i = b_source_writes(w)[0] + F, d, (s,) = w["B"][i] + p = mirror(w, "B", i) + lines = w["frames"][F] + other = next(r for r in lines if r not in (d, s)) + w["B"][i] = [F, d, [other]] + assert w["C"][p][1:] == [d, [s]] + w["C"][p] = [w["C"][p][0], d, [other]] + return "corrupt a phase-B source write (operand %d -> %d, cleanup changed alike)" % (s, other) + + +def m5(w): + i = b_source_writes(w)[0] + F, d, srcs = w["B"][i] + w["B"][i] = [w["start"][d], d, srcs] + return "phase-B source write moved down to the destination's own line frame" + + +def m6(w): + w["B"].insert(0, [ZERO, v + 0, [v + 1]]) + return "a target reads another target (single read, V != I)" + + +def m7(w): + g = next(g for g in w["D"] if len(g[2]) > 1) + g[2].pop() + return "frame-0 preconditioner corrupted (one helper read dropped)" + + +def m8(w): + i = min(i for i, g in enumerate(w["B"]) if is_z(g[1])) + d = w["B"][i][1] + j = next(j for j, g in enumerate(w["B"]) if is_y(g[1]) and d in g[2]) + assert j < i + g = w["B"].pop(i) + w["B"].insert(j, g) + return "phase-B source write moved before the target read of that register" + + +def m9(w): + p = len(w["C"]) - 1 + zg = zgates(w) + sec, i = zg[len(zg) - 1 - p] + w["C"][p][0] = w[sec][i][0] + return "cleanup gate moved off the full frame (to the frame of the gate it inverts)" + + +def pos(w): + w["B"][0:0] = [[ZERO, v + 0, [v + 1]], [ZERO, v + 0, [v + 1]]] + return "cancelled pair of target-to-target reads at frame 0 (V = I)" + + +ok0, msg0 = evaluate(w0) +assert ok0, ("the unmutated word must pass", msg0) +rows = [] +allok = True +for f in (m1, m2, m3, m4, m5, m6, m7, m8, m9): + w = copy.deepcopy(w0) + name = f(w) + acc, msg = evaluate(w) + rows.append(dict(control=f.__name__, mutation=name, rejected=not acc, by=msg)) + allok &= not acc + print(" %s %s :: %s" % ("REJECTED " if not acc else "ACCEPTED!", name, msg)) +w = copy.deepcopy(w0) +name = pos(w) +acc, msg = evaluate(w) +rows.append(dict(control="positive", mutation=name, rejected=not acc, by=msg)) +allok &= acc +print(" %s %s :: %s" % ("accepted " if acc else "REJECTED!", name, msg)) +assert allok, "a mutation control did not behave as required" +print("PASS mutation controls: 9/9 negative controls rejected, 1/1 positive control accepted") +if "--json" in sys.argv: + json.dump(dict(status="PASS_INTERLEAVED_INTERFACE_MUTATION_CONTROLS", controls=rows), + open(sys.argv[sys.argv.index("--json") + 1], "w"), indent=1) diff --git a/research/e8-bit-p111/code/mutants_p.py b/research/e8-bit-p111/code/mutants_p.py new file mode 100644 index 0000000000..f249d3250a --- /dev/null +++ b/research/e8-bit-p111/code/mutants_p.py @@ -0,0 +1,111 @@ +"""Negative controls aimed at the punctured design, run through the independent checker code/xcheck_p.py. +usage: mutants_p.py XCHECK_P.py WORD.json.gz [--json OUT] + 1 drop the line read of a pseudo-target total (its single d-line): the exact replay must fail; + 2 drop one phase-A mini-node gate (a helper written at a frame that also hosts phase-B block-node gates): the exact + replay must fail; + 3 move the last phase-A gate of a total to the full frame: legal and exact, but the total is COPYed at rank 8, so + the decoder constant is 64, not 63 (the claim this package prices is refused); + 4 drop the receiver read of a total at p-perp (one of p's needers): the exact replay must fail. +Prepared by eumemic with substantial Anthropic Claude assistance; Apache-2.0.""" +import copy +import gzip +import importlib.util +import json +import sys + +sp = importlib.util.spec_from_file_location("xcheck_p", sys.argv[1]) +X = importlib.util.module_from_spec(sp) +sp.loader.exec_module(X) +w0 = json.load(gzip.open(sys.argv[2], "rt")) +v = w0["v"] +ret = set(w0["ret"]) + + +def dims(w): + W = X.Word(w) + return W.fdim + + +fd = dims(w0) +bframes = {g[0] for g in w0["B"] if fd[g[0]] == 4} + + +def evaluate(w): + try: + W, lab, fl, H, C, info = X.check(w) + except X.Reject as e: + return "rejected", "replay: %s" % e + cst = sum(x["rank"] for x in fl["totals"]) + if not (all(H.values()) and all(C.values())): + return "rejected", "interface" + if cst != 63: + return "rejected", "decoder constant %d != 63 (COPY ranks %s)" % (cst, [x["rank"] for x in fl["totals"]]) + return "accepted", "accepted" + + +def first(pred): + return next(i for i, g in enumerate(w0["A"]) if pred(g)) + + +def cmirror(w, i): + """index in C of the literal inverse of A gate i (C = reverse of the z-destination gates of A then B)""" + zg = [("A", k) for k, g in enumerate(w["A"]) if not (v <= g[1] < 2 * v)] + \ + [("B", k) for k, g in enumerate(w["B"]) if not (v <= g[1] < 2 * v)] + return len(zg) - 1 - zg.index(("A", i)) + + +def m1(w): + i = first(lambda g: g[1] in ret and fd[g[0]] == 1) + j = cmirror(w, i) + del w["C"][j] + del w["A"][i] + return "drop the line read of a pseudo-target total (A gate %d) and its cleanup inverse" % i + + +def m2(w): + i = first(lambda g: g[0] in bframes and g[1] >= 2 * v and g[1] not in ret) + j = cmirror(w, i) + del w["C"][j] + del w["A"][i] + return "drop a phase-A mini-node gate at a block frame (A gate %d) and its cleanup inverse" % i + + +def m3(w): + last = {} + for i, g in enumerate(w["A"]): + if g[1] in ret: + last[g[1]] = i + i = last[w["ret"][0]] + full = next(k for k, d in enumerate(fd) if d == 8) + w["A"][i][0] = full + return "a total's p-perp read moved to the full frame (A gate %d)" % i + + +def m4(w): + last = {} + for i, g in enumerate(w["A"]): + if g[1] in ret: + last[g[1]] = i + i = last[w["ret"][0]] + j = cmirror(w, i) + w["A"][i][2] = w["A"][i][2][1:] + w["C"][j][2] = w["C"][j][2][1:] + return "a total drops one needer read at p-perp (A gate %d; cleanup inverse changed alike)" % i + + +ok = True +res = [] +for m in (m1, m2, m3, m4): + w = copy.deepcopy(w0) + name = m(w) + st, why = evaluate(w) + res.append(dict(control=name, outcome=st, reason=why)) + ok &= st == "rejected" + print(" %-9s %s :: %s" % (st.upper() if st == "rejected" else st, name, why)) +base = evaluate(w0) +assert base[0] == "accepted", base +print("PASS punctured-design controls: %d/%d rejected; the shipped word passes" % (sum(r["outcome"] == "rejected" for r in res), len(res)) + if ok else "FAIL") +if "--json" in sys.argv: + json.dump(dict(controls=res, all_rejected=ok), open(sys.argv[sys.argv.index("--json") + 1], "w"), indent=1) +sys.exit(0 if ok else 1) diff --git a/research/e8-bit-p111/code/mutrec_b.py b/research/e8-bit-p111/code/mutrec_b.py new file mode 100644 index 0000000000..7b644a4a03 --- /dev/null +++ b/research/e8-bit-p111/code/mutrec_b.py @@ -0,0 +1,64 @@ +"""Record-level negative controls aimed at the phase-B source writes, for the official cohort checkers (PR #266's +legality and formal-column checkers as generalised by PR #370 and PR #381). +usage: mutrec_b.py WORD.json.gz RECORDDIR COHORT_IN COHORT_OUT LEGALITY_BIN COLUMNS_BIN [COLUMNS_BIN ...] +Each mutant is a changed COHORT249-RECORDS.bin. Legality must still pass (no frame changes) and every column checker +must reject it: + 1 drop one cleanup ADD (phase 5) into a source register; + 2 corrupt a phase-B source write: duplicate (cancel) the first phase-4 ADD into a source register and duplicate the + matching cleanup ADD, so every source is still restored but the Q-delivery the targets read is wrong; + 3 leave a source unrestored: delete every cleanup ADD into the destination of that phase-B source write. +Adapted from PR #493's mutate_records_mut.py (whose sites are phase-A source writes; this word has none). +Prepared by eumemic with substantial Anthropic Claude assistance; Apache-2.0.""" +import gzip +import json +import shutil +import struct +import subprocess +import sys + +wp, rd, cin, cout, leg = sys.argv[1:6] +cols = sys.argv[6:] +w = json.load(gzip.open(wp, "rt")) +v = w["v"] +rec = open(rd + "/249-records.bin", "rb").read() +R = [list(struct.unpack_from("<6i", rec, 24 * i)) for i in range(len(rec) // 24)] +pack = lambda rows: b"".join(struct.pack("<6i", *r) for r in rows) +adds = {ph: [i for i, e in enumerate(R) if e[0] == 1 and e[5] == ph] for ph in (4, 5)} +is_z = lambda r: not (v <= r < 2 * v) +ib = next(i for i, g in enumerate(w["B"]) if g[1] < v and len(g[2]) == 1) +XB, src = w["B"][ib][1], w["B"][ib][2][0] +kb = sum(len(g[2]) for g in w["B"][:ib]) +zg = [("A", i) for i, g in enumerate(w["A"]) if is_z(g[1])] + [("B", i) for i, g in enumerate(w["B"]) if is_z(g[1])] +pc = len(zg) - 1 - zg.index(("B", ib)) +kc = sum(len(g[2]) for g in w["C"][:pc]) +rb, rc = adds[4][kb], adds[5][kc] +assert R[rb][1:3] == [XB, src] and R[rc][1:3] == [XB, src] + + +def run(binary, recs, legality=False): + tmp = cout + "-mutb" + shutil.rmtree(tmp, ignore_errors=True) + shutil.copytree(cout, tmp) + open(tmp + "/COHORT249-RECORDS.bin", "wb").write(recs) + args = [binary, cin, tmp, tmp + "/x.json"] if legality else [binary, tmp + "/COHORT249-RECORDS.bin", tmp + "/x.json"] + r = subprocess.run(args, capture_output=True, text=True) + shutil.rmtree(tmp, ignore_errors=True) + return r.returncode != 0, ((r.stderr.strip().splitlines() or ["?"])[-1])[:70] + + +first_c = next(i for i in adds[5] if R[i][1] < v) +muts = [("drop one cleanup ADD into a source register", pack(R[:first_c] + R[first_c + 1:])), + ("corrupt a phase-B source write (cancelled, cleanup adjusted: every source still restored)", + pack(R[:rb + 1] + [R[rb]] + R[rb + 1:rc] + [R[rc]] + R[rc:])), + ("leave source register %d unrestored (all its cleanup ADDs deleted)" % XB, + pack([e for e in R if not (e[0] == 1 and e[5] == 5 and e[1] == XB)]))] +ok = True +for name, m in muts: + lrej, lmsg = run(leg, m, legality=True) + res = [run(cb, m) for cb in cols] + ok &= (not lrej) and all(x[0] for x in res) + print(" legality %s; %s <- %s" % ("passes (frames unchanged)" if not lrej else "REJECTS", + "; ".join("%s %s [%s]" % (cb.rsplit('/', 1)[-1], "REJECTED" if r_[0] else "ACCEPTED!!", + r_[1]) for cb, r_ in zip(cols, res)), name)) +assert ok, "a record-level phase-B source mutant was accepted" +print("PASS all 3 record-level phase-B source mutants rejected by every column checker (legality unchanged)") diff --git a/research/e8-bit-p111/code/nodeopt.py b/research/e8-bit-p111/code/nodeopt.py new file mode 100644 index 0000000000..dd30a2e4e6 --- /dev/null +++ b/research/e8-bit-p111/code/nodeopt.py @@ -0,0 +1,124 @@ +# Prepared by eumemic with substantial Anthropic Claude assistance; Apache-2.0. +"""Bill-aware realisation of a block node: wires start as unit vectors e_0..e_{n-1} (the keepers' lines), and must end +holding the rows of N in some order. A realisation is a list of ops (a, b): wire a ^= wire b. Cost = max +cancellation-free norm over the op list followed by its literal reverse (both bill passes see sigma then sigma^rev), +starting from unit norms. Candidates: LU with random row/column relabellings, and a hub scheme.""" +import random + + +def lu_ops(rows, n): + """rows: list of n ints (target rows, in the unit basis, row k = output on wire k). Returns ops realising + wire k := rows[k] from wire k = e_k, as U (increasing) then L (decreasing) [no pivoting], or None.""" + A = [[(rows[k] >> j) & 1 for j in range(n)] for k in range(n)] + Lm = [[0] * n for _ in range(n)] + for col in range(n): + if not A[col][col]: + return None + for r in range(col + 1, n): + if A[r][col]: + Lm[r][col] = 1 + A[r] = [x ^ y for x, y in zip(A[r], A[col])] + ops = [] + for i in range(n): + for j in range(i + 1, n): + if A[i][j]: + ops.append((i, j)) + for i in range(n - 1, -1, -1): + for j in range(i): + if Lm[i][j]: + ops.append((i, j)) + return ops + + +def apply(ops, n): + cur = [1 << k for k in range(n)] + for a, b in ops: + cur[a] ^= cur[b] + return cur + + +def cost(ops, n, start=None): + nm = list(start) if start else [1] * n + mx = max(nm) + for a, b in ops + list(reversed(ops)): + nm[a] += nm[b] + mx = max(mx, nm[a]) + return mx + + +def pivot_perm(N, order, rng): + """Random row pivoting: walk the wires in 'order' (columns); for each, pick a random remaining output whose + reduced row has a 1 there. Returns perm (output k -> wire perm[k]) such that LU without pivoting succeeds in + 'order', or None.""" + n = len(N) + rows = list(range(n)) + red = {k: N[k] for k in range(n)} + perm = [None] * n + remaining = set(range(n)) + for i in range(n): + w = order[i] + cand = [k for k in remaining if red[k] >> w & 1] + if not cand: + return None + k = rng.choice(cand) + perm[k] = w + remaining.discard(k) + for k2 in remaining: + if red[k2] >> w & 1: + red[k2] ^= red[k] + return perm + + +def realise(N, rng, tries=3000): + """N: list of n ints over n bits (outputs in the inputs' unit basis). Returns (best_cost, ops, assign) where + assign[k] = wire holding N[k].""" + n = len(N) + best = None + for _ in range(tries): + order = list(range(n)) + rng.shuffle(order) # process wires in this order (relabel) + perm = pivot_perm(N, order, rng) + if perm is None: + continue + rows = [0] * n + for k in range(n): + rows[perm[k]] = N[k] + rr = [] + for i in range(n): + w = order[i] + v = rows[w] + nv = 0 + for j in range(n): + if v >> order[j] & 1: + nv |= 1 << j + rr.append(nv) + ops = lu_ops(rr, n) + if ops is None: + continue + ops = [(order[a], order[b]) for a, b in ops] + c = cost(ops, n) + if best is None or c < best[0]: + cur = apply(ops, n) + assert all(cur[perm[k]] == N[k] for k in range(n)) + best = (c, ops, perm) + return best + + +if __name__ == "__main__": + import sys + rng = random.Random(0) + n = 12 + # random dense test matrix of full rank + import itertools + while True: + N = [rng.randrange(1, 1 << n) | (1 << n) - 1 & rng.randrange(1 << n) for _ in range(n)] + b = [] + for v in N: + for x in b: + v = min(v, v ^ x) + if v: + b.append(v) + if len(b) == n: + break + r = realise(N, rng, tries=int(sys.argv[1]) if len(sys.argv) > 1 else 2000) + print("best cost bits", r[0].bit_length(), "ops", len(r[1])) diff --git a/research/e8-bit-p111/code/planio.py b/research/e8-bit-p111/code/planio.py new file mode 100644 index 0000000000..0d5874976a --- /dev/null +++ b/research/e8-bit-p111/code/planio.py @@ -0,0 +1,10 @@ +# Prepared by eumemic with substantial Anthropic Claude assistance; Apache-2.0. +"""Plan loader for gen_l3.py (load_plan of discovery/planL3.py, without the CP-SAT import).""" +import json + + +def load_plan(path): + d = json.load(open(path)) + return dict(ext={(q, tuple(H)): e for q, H, e in d["ext"]}, partner={t: x for t, x in d["partner"]}, + kline={t: l for t, l in d["kline"]}, rcv={tuple(c): t for c, t in d["rcv"]}, + qhost={t: x for t, x in d["qhost"]}) diff --git a/research/e8-bit-p111/code/provenance.py b/research/e8-bit-p111/code/provenance.py new file mode 100644 index 0000000000..da0f0a91e5 --- /dev/null +++ b/research/e8-bit-p111/code/provenance.py @@ -0,0 +1,63 @@ +"""Provenance of everything this package runs that it did not write. +usage: provenance.py PACKAGE_DIR + 1. every vendored file listed in SOURCE-pr*.json has the recorded blob id (sha1 of "blob \\0" + bytes), i.e. it is + byte-identical to the file at source_path in the cited PR's commit; the vendored set equals the listed set; + 2. the files of main this package uses are pinned in DEPENDS.json by sha256; PR #493's package + (research/e8-bit-carrier-skeleton) is pinned through its own MANIFEST.sha256: every file of that directory must + match it and no file may be unlisted; + 3. seven of the files PR #510 vendors are also byte-identical to PR #493's copies on main. +Adapted from PR #493's code/provenance.py (itself adapted from PR #381's, DaysSky). +Prepared by eumemic with substantial Anthropic Claude assistance; Apache-2.0.""" +import glob +import hashlib +import json +import os +import sys + +HERE = os.path.abspath(sys.argv[1]) +PARENT = os.path.join(HERE, "..", "e8-bit-carrier-skeleton") + + +def read(rel, base=HERE): + return open(os.path.join(base, rel), "rb").read() + + +def blob(b): + return hashlib.sha1(b"blob %d\0" % len(b) + b).hexdigest() + + +listed = set() +for name in sorted(glob.glob(os.path.join(HERE, "SOURCE-pr*.json"))): + src = json.load(open(name)) + bad = [rel for rel, x in src["identical"].items() if blob(read(rel)) != x["blob"]] + if bad: + sys.exit("files differ from %s at %s: %s" % (src["source"], src["commit"][:7], bad)) + listed |= set(src["identical"]) + print(" %2d files byte-identical to PR #%s at %s (%s)" % (len(src["identical"]), src["source"].rsplit("/", 1)[-1], + src["commit"][:7], src["head_repository"])) +vendored = {os.path.relpath(os.path.join(r, f), HERE) for r, _, fs in os.walk(os.path.join(HERE, "vendor")) for f in fs + if "__pycache__" not in r} +vendored |= {os.path.relpath(p, HERE) for p in glob.glob(os.path.join(HERE, "data/complex/*/*"))} +assert vendored == listed, ("vendored set differs from the SOURCE pins", sorted(vendored ^ listed)) +dep = json.load(open(os.path.join(HERE, "DEPENDS.json"))) +for rel, x in dep["files"].items(): + b = read(rel) + assert hashlib.sha256(b).hexdigest() == x["sha256"] and blob(b) == x["blob"], ("main file changed", rel) +bad, seen = [], set() +for line in read("MANIFEST.sha256", PARENT).decode().splitlines(): + h, p = line.split(" ", 1) + seen.add(p) + if hashlib.sha256(read(p, PARENT)).hexdigest() != h: + bad.append(p) +files = {os.path.relpath(os.path.join(r, f), PARENT) for r, _, fs in os.walk(PARENT) for f in fs + if "__pycache__" not in r} - {"MANIFEST.sha256"} +assert not bad and files == seen, ("PR #493 package differs from its MANIFEST.sha256", bad, sorted(files ^ seen)) +print(" main (commit %s): %d pinned files unchanged; PR #493's package matches its MANIFEST.sha256 (%d files)" + % (dep["commit"][:7], len(dep["files"]), len(seen))) +V = "vendor/pr510/e8-r480-nine-stage/vendor/" +same = [(V + "e8-r740-replica32/code/" + n, "code/hub/e8-r740-replica32/code/" + n) + for n in ("bank_routes.py", "tile_m.py", "e8core.py", "engines/moment.py", "engines/base_two_moment.py", + "engines/outer.py")] + [(V + "idle-axis-hosting/hosting_core.py", "code/hub/idle-axis-hosting/hosting_core.py")] +for a, b in same: + assert read(a) == read(b, PARENT), (a, b) +print(" %d of PR #510's vendored files are also byte-identical to PR #493's copies on main" % len(same)) diff --git a/research/e8-bit-p111/code/pwordl.py b/research/e8-bit-p111/code/pwordl.py new file mode 100644 index 0000000000..a90bed93e2 --- /dev/null +++ b/research/e8-bit-p111/code/pwordl.py @@ -0,0 +1,128 @@ +# Prepared by eumemic with substantial Anthropic Claude assistance; Apache-2.0. +"""Word builder for punctured bit words: the live ports are a subset of the 120 labelled E8 lines. +Same contract as PR #493's wordkit.Word (registers x_s = s, y_t = V + t, helpers 2V + k, contents over the columns +x | y0 | dirt), restricted to the live lines; build() renumbers to the compact v = |live| layout of bitE8/1 and records +the live labels as `ports_geom` and the removed lines as `punctured`, the fields PR #545 introduced for punctured words. +Frames are live line sets: the closure of a line set, intersected with the live lines. D (frame-zero dirt +compensation) and C (literal reverse of every source/helper-destination gate at the full frame) are built as in +wordkit.Word.""" +import os +import sys + +HERE = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, os.path.join(HERE, "..", "..", "e8-bit-carrier-skeleton", "code", "skeleton")) +import e8 # noqa: E402 + +V = e8.V + + +class PWordL: + def __init__(self, live): + self.live = live + self.ports = e8.bits(live) + self.nh = 0 + self.cur, self.content, self.role = {}, {}, {} + for s in self.ports: + self.cur[s] = 1 << s + self.content[s] = 1 << s + for t in self.ports: + self.cur[V + t] = 0 + self.content[V + t] = 1 << (V + t) + self.A, self.B = [], [] + self.ret = [] + self.phase = "A" + + def helper(self, role=""): + r = 2 * V + self.nh + self.nh += 1 + self.cur[r] = 0 + self.content[r] = 1 << r + self.role[r] = role + return r + + def fr(self, F): + return (e8.closure(F)[0] if F else 0) & self.live + + def climb(self, r, F): + a = self.cur[r] + if a == F: + return + assert (a & ~F) == 0, ("frame not nested", r, e8.bits(a), e8.bits(F)) + self.cur[r] = F + + def gate(self, F, dst, srcs): + F = self.fr(F) + regs = [dst] + list(srcs) + assert len(set(regs)) == len(regs), regs + assert not (V <= dst < 2 * V and self.phase == "A"), "y written before the scatter" + for r in regs: + assert r in self.cur, ("absent register", r) + if V <= r < 2 * V: + assert self.phase == "B", "y used before the scatter" + self.climb(r, F) + for s in srcs: + self.content[dst] ^= self.content[s] + (self.A if self.phase == "A" else self.B).append((F, dst, list(srcs))) + + def xpart(self, r): + return self.content[r] & ((1 << V) - 1) + + def build(self, scatter_rows): + nh, ports = self.nh, self.ports + v = len(ports) + pidx = {s: i for i, s in enumerate(ports)} + + def rid(r): + if r < V: + return pidx[r] + if r < 2 * V: + return v + pidx[r - V] + return 2 * v + (r - 2 * V) + frames = {} + + def fid(mask): + if mask not in frames: + frames[mask] = len(frames) + return frames[mask] + FULL = self.live + fid(0) + fid(FULL) + A = [[fid(F), d, list(s)] for F, d, s in self.A] + B = [[fid(F), d, list(s)] for F, d, s in self.B] + dv = {2 * V + h: 1 << h for h in range(nh)} + for s in ports: + dv[s] = 0 + dv[V + s] = 0 + for f, d, srcs in A: + for s in srcs: + dv[d] ^= dv[s] + cdirt = [dv[q] for q in self.ret] + for t in ports: + for k in scatter_rows[t]: + dv[V + t] ^= cdirt[k] + for f, d, srcs in B: + for s in srcs: + dv[d] ^= dv[s] + D = [] + for t in ports: + m = dv[V + t] + srcs = [] + while m: + b = m & -m + srcs.append(2 * V + b.bit_length() - 1) + m ^= b + if srcs: + D.append([fid(0), V + t, srcs]) + Cl = [[fid(FULL), d, srcs] for f, d, srcs in reversed(A + B) if d < V or d >= 2 * V] + start = [fid(1 << s) for s in ports] + [fid(0)] * (v + nh) + final = [fid(FULL)] * v + [fid(e8.HYP[t] & FULL) for t in ports] + [fid(FULL)] * nh + fr = [None] * len(frames) + for mk, i in frames.items(): + fr[i] = [pidx[b] for b in e8.bits(mk)] + + def rg(gs): + return [[f, rid(d), [rid(s) for s in srcs]] for f, d, srcs in gs] + return dict(format="bitE8/1+mut", coords="ham", h=8, v=v, R=nh, lines=[list(e8.LINES[s]) for s in ports], + form="/2", frames=fr, start=start, final=final, ret=[rid(q) for q in self.ret], + scatter=[list(scatter_rows[t]) for t in ports], D=rg(D), A=rg(A), B=rg(B), C=rg(Cl), + punctured=[s for s in range(V) if not (self.live >> s) & 1], ports_geom=list(ports)) diff --git a/research/e8-bit-p111/code/receipt_kappa.py b/research/e8-bit-p111/code/receipt_kappa.py new file mode 100644 index 0000000000..2a8823a049 --- /dev/null +++ b/research/e8-bit-p111/code/receipt_kappa.py @@ -0,0 +1,81 @@ +"""Recompute the cited PRs' own kappa from the (c, b) recorded in their receipts, through this package's outer assembly: +the per-supplier loop of vendor/pr510/e8-r480-nine-stage/code/assemble9.py (PR #493's six-level chain, the 47 strict +inequalities of PR #315's outer engine and four controls). Compare kappa and the binding side with each receipt, and, +where an exact-assembly receipt records them, all 47 constraint values, the six bootstrap gaps and cutoffs and the +rejected controls. This shows the cited PRs and this package compose with the same assembly; the compositions with this +package's bit c are computed by verify.sh (assemble9.py and PR #493's eleven-stage assemble.py). +usage: receipt_kappa.py PACKAGE_DIR +Prepared by eumemic with substantial Anthropic Claude assistance; Apache-2.0.""" +import importlib.util +import json +import sys +from fractions import Fraction as Q +from pathlib import Path + +H = Path(sys.argv[1]) +eng = H / "../e8-bit-carrier-skeleton/code/hub/e8-r740-replica32/code/engines/outer.py" +spec = importlib.util.spec_from_file_location("e_outer", eng) +Outer = importlib.util.module_from_spec(spec) +spec.loader.exec_module(Outer) +J = lambda p: json.loads((H / p).read_text()) + + +def ceil(x): + return -((-x.numerator) // x.denominator) + + +def assemble(c, b, charged): + """assemble9.py's per-supplier loop, unchanged apart from taking (c, b, charged coefficient bits) as arguments.""" + eta = beta = Q(1, 10 ** 18); grid = Q(1, 10 ** 18); s = min(c, (1 - beta) * b) - Q(1, 10 ** 24) + assert 0 < s < c and s < (1 - beta) * b + chain = [Q(384599, 10 ** 10)]; cutoffs = [] + for j in range(6): + old = chain[-1]; a = (1 - s) * s + s * old + gap = min(s - a, 1 - a - s, 1 - a - s * (1 - old), 1 - s); assert old < a < s < 1 - a and gap > 0 + cutoff = max(ceil(36 / gap ** 2), ceil(2 * (4 + charged) / gap)) + assert cutoff * gap ** 2 >= 36 and cutoff * gap >= 2 * (4 + charged) + cutoffs.append(dict(gap=str(gap), cutoff_log2=str(cutoff))); chain.append(a) + q = chain[-1] * (1 - 2 * eta); margin = (1 - eta) * q / (1 + q); ticks = margin / grid + k = grid * ((ticks.numerator - 1) // ticks.denominator) + bridge = {'semantic': {'C0': {'base': 2, 'exponent': 210000}, 'strict_literal_gap': 1}, + 'rows': {'degree_gap': Q(10 ** 6) - Q(51 * 20161, 25)}} + a = Outer.assembly(chain[-1], b, bridge, k, eta=eta, beta=beta) + assert len(a['strict_constraints']) == 47 and all(x > 0 for x in a['strict_constraints'].values()) + controls = [] + for cname, kk, ee, bb in [('next_grid', k + grid, eta, beta), ('zero_eta', k, Q(0), beta), + ('zero_beta', k, eta, Q(0)), ('unit_beta', k, eta, Q(1))]: + try: + Outer.assembly(chain[-1], b, bridge, kk, eta=ee, beta=bb) + except AssertionError: + controls.append(cname) + else: + raise AssertionError(cname) + return dict(kappa=k, binding='bit' if c < (1 - beta) * b else 'complex', strict=a['strict_constraints'], + bootstrap=cutoffs, controls=controls) + + +def check(label, rec, charged_field=None): + c, b = Q(rec["bit_lower"]), Q(rec["complex_lower"]) + charged = rec[charged_field] if charged_field else 512 + r = assemble(c, b, charged) + assert r["kappa"] == Q(rec["kappa"]), (label, r["kappa"], rec["kappa"]) + assert r["binding"] == rec.get("binding_component", r["binding"]), (label, r["binding"]) + extra = "" + if "strict_outer_constraints" in rec: + mine = {n: str(v) for n, v in r["strict"].items()} + assert mine == rec["strict_outer_constraints"], (label, "constraint values differ") + assert r["bootstrap"] == rec["bootstrap"] and r["controls"] == rec["outer_controls"], (label, "bootstrap/controls") + extra = "; all 47 constraint values, 6 bootstrap cutoffs and %d rejected controls equal the receipt's" % len( + r["controls"]) + print(" %s: c %s, b %s -> kappa %s = %s (%s-bound), as in the receipt%s" + % (label, rec["bit_lower"], rec["complex_lower"], rec["kappa"], format(float(r["kappa"]), ".18f"), r["binding"], + extra)) + + +check("PR #548 EXPECTED.json", J("data/complex/pr548/EXPECTED.json")) +check("PR #548 EXACT-ASSEMBLY.json", J("data/complex/pr548/EXACT-ASSEMBLY.json"), "bit_charged_two_level_coefficient_bits") +check("PR #544 EXPECTED.json", J("data/complex/pr544/EXPECTED.json")) +check("PR #544 EXACT-ASSEMBLY.json", J("data/complex/pr544/EXACT-ASSEMBLY.json"), "bit_charged_two_level_coefficient_bits") +check("PR #535 EXPECTED.json", J("data/complex/pr535/EXPECTED.json")) +check("PR #535 EXACT-ASSEMBLY-PR493.json", J("data/complex/pr535/EXACT-ASSEMBLY-PR493.json"), + "bit_charged_three_level_coefficient_bits") diff --git a/research/e8-bit-p111/code/summary.py b/research/e8-bit-p111/code/summary.py new file mode 100644 index 0000000000..4fefa8aab4 --- /dev/null +++ b/research/e8-bit-p111/code/summary.py @@ -0,0 +1,84 @@ +"""Compare every number verify.sh produced with EXPECTED.json and with the cited PRs' own receipts, and print the +summary. +usage: summary.py PACKAGE_DIR OUTPUT_DIR +Prepared by eumemic with substantial Anthropic Claude assistance; Apache-2.0.""" +import json +import sys +from fractions import Fraction as F +from pathlib import Path + +H, W = Path(sys.argv[1]), Path(sys.argv[2]) +J = lambda p: json.loads(Path(p).read_text()) # noqa: E731 +e = J(H / "EXPECTED.json") +x, mu, mp = J(W / "xcheck.json"), J(W / "mutants.json"), J(W / "mutants_p.json") +cc, k4, k5, lg = J(W / "cout/columns.json"), J(W / "cout/columns_k4.json"), J(W / "cout/columns_k5.json"), \ + J(W / "cout/legality.json") +r9, r11 = J(W / "RESULT9.json"), J(W / "RESULT11.json") +c480, c480s, c545 = J(W / "c480/param/RESULT9.json"), J(W / "c480/stock/RESULT9.json"), J(W / "c545/RESULT11.json") +g11, g545 = J(W / "bit/GLOBAL.json"), J(W / "c545/bit/GLOBAL.json") +ew = e["word"] +# word, checker, controls +assert x["status"] == "PASS_EXACT_REPLAY_AND_INTERLEAVED_INTERFACE" and x["v"] == ew["v"] and x["punctured"] == 9 +assert x["copy_ranks"] == [7] * 9 and x["totals_on_punctured_hyperplanes"] +for k in ("hypotheses", "conclusion", "interleaving"): + assert x[k] == e["interface"][k], k +assert mp["all_rejected"] and len(mp["controls"]) == 4 +assert {str(a): b for a, b in cc["local_raw_H"]} == ew["local_histogram"] +assert (cc["payload_prefix_bits"], k4["payload_prefix_bits"], k5["payload_prefix_bits"]) == \ + (ew["five_stage_payload_bits"], ew["nine_stage_payload_bits"], ew["eleven_stage_payload_bits"]) +assert (lg["records"], lg["common_frame_ADDs"], lg["rank_mass"]) == tuple(ew["legality"][k] for k in + ("records", "common_frame_ADDs", "rank_mass")) + + +def same(r, ex, names, extra=()): + assert r["bit_lower"] == ex["bit_lower"], (r["bit_lower"], ex["bit_lower"]) + for k in extra: + assert r[k] == ex[k], k + for n in names: + for f in ("kappa", "binding_component", "complex_lower"): + assert r["kappa"][n][f] == ex["kappa"][n][f], (n, f) + + +names = ("pr548", "pr544", "pr535", "pr531") +same(r9, e["nine"], names) +assert r9["bit_profile"] == e["nine"]["profile"] and r9["bit"] == e["nine"]["bit"] +same(r11, e["eleven"], names) +assert r11["bit_profile"] == e["eleven"]["profile"] and r11["bit"] == e["eleven"]["bit"] +assert g11["emitted_program_sha256"] == e["eleven"]["emitted_program_sha256"] +# controls against the cited PRs' own receipts +e510 = J(H / "vendor/pr510/e8-r480-nine-stage/EXPECTED.json")["nine"] +e545 = J(H / "vendor/pr545/EXPECTED.json") +e548, e544, e535 = J(H / "data/complex/pr548/EXPECTED.json"), J(H / "data/complex/pr544/EXPECTED.json"), \ + J(H / "data/complex/pr535/EXPECTED.json") +e531 = J(H / "../direct-delivery504-global/EXPECTED.json") +assert c480 == c480s +assert c480["bit_lower"] == e510["bit_lower"] and c480["bit_profile"] == e510["profile"] +assert all(c480["kappa"][n]["kappa"] == e510["kappa"][n]["kappa"] for n in e510["kappa"]) +assert c480["kappa"]["pr535"]["kappa"] == e535["kappa"] and c480["kappa"]["pr535"]["binding_component"] == "bit" +assert c545["bit_lower"] == e545["eleven"]["bit_lower"] and c545["bit_profile"] == e545["eleven"]["profile"] +assert g545["emitted_program_sha256"] == e545["eleven"]["emitted_program_sha256"] +assert c545["kappa"]["pr535"]["kappa"] == e545["kappa"]["pr535"]["kappa"] +c9, c11 = F(r9["bit_lower"]), F(r11["bit_lower"]) +assert all(r11["kappa"][n]["kappa"] == r9["kappa"][n]["kappa"] for n in names) +b548 = F(e548["complex_lower"]) +assert c11 > b548 and c9 > b548 and all(r9["kappa"][n]["binding_component"] == "complex" for n in names) +p = lambda a, b: "%+.4f%%" % (100 * float(F(a) / F(b) - 1)) # noqa: E731 +print("PASS bit c, nine stages (PR #510's pipeline parameterised for v = %d, 32 replicas, all %d residuals hosted whole, " + "%d banks) = %s = %s" % (ew["v"], r9["bit"]["hosted_residuals"], r9["bit"]["bank_assignments"], r9["bit_lower"], + r9["bit_lower_decimal"])) +print(" %s over PR #510's R480 (%s); %s over PR #545's punctured R489 at eleven stages (%s)" + % (p(c9, e510["bit_lower"]), e510["bit_lower_decimal"], p(c9, e545["eleven"]["bit_lower"]), + e545["eleven"]["bit_lower_decimal"])) +print("PASS bit c, eleven stages (PR #493's pipeline as parameterised by PR #545) = %s = %s (%s over PR #545's c)" + % (r11["bit_lower"], r11["bit_lower_decimal"], p(c11, e545["eleven"]["bit_lower"]))) +for n, rec in (("pr548", e548), ("pr544", e544), ("pr535", e535), ("pr531", e531)): + kk = r9["kappa"][n] + print("PASS kappa with PR #%s's complex b = %s: %s = %s (%s-bound; the same with the nine- and the eleven-stage c);" + " the PR's own kappa is %s, change %s" % (n[2:], kk["complex_lower"], kk["kappa"], kk["kappa_decimal"], + kk["binding_component"], rec["kappa"], p(kk["kappa"], rec["kappa"]))) +print(" controls reproduced exactly: the parameterised nine-stage files are byte-identical to PR #510's on its R480 word" + " (c %s; with PR #535's b, PR #535's kappa %s, bit-bound);" % (c480["bit_lower_decimal"], + c480["kappa"]["pr535"]["kappa_decimal"])) +print(" PR #545's word through PR #545's files: c %s and emitted-program sha256 %s... as in PR #545's EXPECTED.json;" + " the receipts of PR #548, #544 and #535 give their own kappa through the same assembly (step 0)" + % (c545["bit_lower_decimal"], g545["emitted_program_sha256"][:16])) diff --git a/research/e8-bit-p111/code/xcheck_p.py b/research/e8-bit-p111/code/xcheck_p.py new file mode 100644 index 0000000000..184f5dcf94 --- /dev/null +++ b/research/e8-bit-p111/code/xcheck_p.py @@ -0,0 +1,435 @@ +"""Independent exact replay and interface audit of a bitE8/1 word with v <= 120 live ports (a punctured word: some +E8 root lines are neither sources nor targets) whose source and helper registers may be gate destinations in phase A +and in phase B (interleaved with target reads). No producer, generator or hub code is imported; only the word file is +read. Adapted from the R420 package's code/xcheck.py (v = 120) by exact edits: the label count, frame closure and +target frames are taken over the v live labels, and Part T is added. +usage: xcheck_p.py WORD.json.gz [--json OUT] + +Part L (labels): the v labels are integer vectors with = dot/2; every label has norm 2, distinct labels have +|| <= 1, and the lattice they generate (Hermite normal form over Z) has Gram determinant 1 and rank 8. An even +unimodular positive definite lattice of rank 8 is E8, so the labels are root lines of it. The root lines of that +lattice are enumerated (all vectors of norm 2 among the integer vectors with entries in {-2..2} that lie in the +lattice: 240 roots, 120 lines); the punctured lines are the root lines that are not labels. Every listed frame is +closed in the live labels (the listed lines are exactly the labels in its span). + +Part T (totals): the frame of every retained total at the scatter, its dimension (the COPY rank), and whether it is +the hyperplane q-perp (live labels orthogonal to q) of a punctured line q. + +Part F (frames and exact F2 semantics): every register starts at its start frame (sources at their own line, targets +and helpers at frame 0); before each gate every operand climbs to the gate's frame, and a climb must be nested with a +strict increase of dimension. Targets stay at frame 0 until the scatter. Values are replayed symbolically: register +r starts as the column e_r (x columns, y columns, helper-dirt columns), so the replay covers every input at once, with +arbitrary helper contents. Required at the end: every source holds e_s, every helper holds e_h, every target holds +e_{y_t} + e_{x_t}; final frames: sources and helpers at the full frame, target t at t-perp. + +Part I (interface): the hypotheses of the interleaved scalar-interface lemma (README, "Review point"), each evaluated +separately: + H1 no gate anywhere whose destination is a z register (source or helper) reads a target; + H2 D: every gate is at frame 0, writes a target and reads only helpers (all of them at frame 0 when D runs); + H3 A writes only z registers; the scatter reads only retained helper registers and writes only targets; every B gate + either writes a z register from z registers only, or writes a target; + H4 C is exactly the literal reverse of the z-destination gates of A and B (in order A then B), each at the full + frame; final frames of all z registers are the full frame, so C needs no descent; + H5 V = I: the target-to-target part of the raw word A, scatter, B is the identity. +and the lemma's conclusion decomposed on the raw word W0 = (A, scatter, B): + U every z register's W0 output has no target column (z' = U z); + J the source part of every target's W0 output is its own source (J = I); + K the helper part of every target's W0 output equals the helper set D adds to it (D = -V^-1 K s over F2); + Uinv C applied to the W0 outputs of the z registers restores every z register (C = U^-1). +Prepared by eumemic with substantial Anthropic Claude assistance; Apache-2.0.""" +import gzip +import json +import math +import sys +from collections import Counter +from fractions import Fraction + + +class Reject(Exception): + pass + + +def need(cond, msg): + if not cond: + raise Reject(msg) + + +# ------------------------------------------------------------------ exact linear algebra over Q and Z +def rref(rows): + """reduced row echelon form over Q of integer rows; returns (basis rows, pivot columns)""" + m = [[Fraction(x) for x in r] for r in rows] + piv = [] + rk = 0 + ncol = len(m[0]) if m else 0 + for col in range(ncol): + p = next((i for i in range(rk, len(m)) if m[i][col] != 0), None) + if p is None: + continue + m[rk], m[p] = m[p], m[rk] + inv = 1 / m[rk][col] + m[rk] = [a * inv for a in m[rk]] + for i in range(len(m)): + if i != rk and m[i][col] != 0: + f = m[i][col] + m[i] = [a - f * b for a, b in zip(m[i], m[rk])] + piv.append(col) + rk += 1 + return m[:rk], piv + + +def nullspace_int(rows, ncol=8): + """integer basis of {n : r.n = 0 for every row r} (dot product)""" + if not rows: + return [[int(i == j) for j in range(ncol)] for i in range(ncol)] + basis, piv = rref(rows) + free = [c for c in range(ncol) if c not in piv] + out = [] + for f in free: + vec = [Fraction(0)] * ncol + vec[f] = Fraction(1) + for r, pc in zip(basis, piv): + vec[pc] = -r[f] + den = 1 + for x in vec: + den = den * x.denominator // math.gcd(den, x.denominator) + out.append([int(x * den) for x in vec]) + return out + + +def hnf_basis(rows): + """Z-basis of the lattice generated by integer rows (row-style Hermite reduction)""" + m = [list(r) for r in rows if any(r)] + basis = [] + ncol = len(rows[0]) + for col in range(ncol): + m = [r for r in m if any(r)] + while True: + nz = [r for r in m if r[col] != 0] + if len(nz) <= 1: + break + nz.sort(key=lambda r: abs(r[col])) + p = nz[0] + for r in m: + if r is not p and r[col] != 0: + q = r[col] // p[col] + for j in range(ncol): + r[j] -= q * p[j] + nz = [r for r in m if r[col] != 0] + if nz: + basis.append(nz[0]) + m = [r for r in m if r is not nz[0]] + return basis + + +def det(M): + M = [[Fraction(x) for x in r] for r in M] + n = len(M) + d = Fraction(1) + for col in range(n): + p = next((i for i in range(col, n) if M[i][col] != 0), None) + if p is None: + return Fraction(0) + if p != col: + M[col], M[p] = M[p], M[col] + d = -d + d *= M[col][col] + for i in range(col + 1, n): + f = M[i][col] / M[col][col] + M[i] = [a - f * b for a, b in zip(M[i], M[col])] + return d + + +# ------------------------------------------------------------------ the checker +class Word: + def __init__(self, w): + self.w = w + self.v = v = w["v"] + self.R = w["R"] + self.n = 2 * v + self.R + need(w["h"] == 8 and 100 <= v <= 120 and w["form"] == "/2", "not an E8 bit word header") + self.lines = [tuple(int(a) for a in r) for r in w["lines"]] + fr = w["frames"] + self.fdim = [] + self.fperp = [] + for f in fr: + need(all(0 <= i < v for i in f), "frame line out of range") + rows = [self.lines[i] for i in f] + b, _ = rref(rows) if rows else ([], []) + self.fdim.append(len(b)) + self.fperp.append(nullspace_int(rows)) + self.FULL = [k for k, d in enumerate(self.fdim) if d == 8] + self.ZERO = [k for k, d in enumerate(self.fdim) if d == 0] + need(len(self.FULL) == 1 and len(self.ZERO) == 1, "need exactly one full frame and one zero frame id") + self.FULL, self.ZERO = self.FULL[0], self.ZERO[0] + self._nest = {} + + def ip(self, a, b): + s = sum(x * y for x, y in zip(a, b)) + need(s % 2 == 0, "form not integral") + return s // 2 + + def labels(self): + L = self.lines + v = self.v + need(len(L) == v and len(set(L)) == v, "need v distinct labels") + need(all(self.ip(a, a) == 2 for a in L), "a label does not have norm 2") + for i in range(v): + for j in range(i + 1, v): + need(abs(self.ip(L[i], L[j])) <= 1, "two labels on one line") + B = hnf_basis(L) + need(len(B) == 8, "labels do not span rank 8") + G = [[self.ip(a, b) for b in B] for a in B] + need(det(G) == 1, "lattice generated by the labels is not unimodular") + # root lines of the lattice: norm-2 vectors with entries in {-2..2} (dot 4), in the lattice (integer coordinates) + import itertools + Bi = [[Fraction(x) for x in r] for r in B] + roots = [] + for vec in itertools.product((-2, -1, 0, 1, 2), repeat=8): + if sum(x * x for x in vec) != 4: + continue + # solve vec = c B over Q, require integer c + aug = [[Bi[r][c] for r in range(8)] + [Fraction(vec[c])] for c in range(8)] + n = 8 + for col in range(n): + p = next(i for i in range(col, n) if aug[i][col] != 0) + aug[col], aug[p] = aug[p], aug[col] + pv = aug[col][col] + aug[col] = [a / pv for a in aug[col]] + for i in range(n): + if i != col and aug[i][col] != 0: + fct = aug[i][col] + aug[i] = [a - fct * b for a, b in zip(aug[i], aug[col])] + if all(aug[i][n].denominator == 1 for i in range(n)): + roots.append(vec) + need(len(roots) == 240, "the lattice does not have 240 roots") + lines120 = sorted({max(r, tuple(-x for x in r)) for r in roots}) + need(len(lines120) == 120, "root lines") + live = {max(a, tuple(-x for x in a)) for a in L} + need(live <= set(lines120), "a label is not a root line") + self.punct = [r for r in lines120 if r not in live] + need(len(self.punct) == 120 - v, "punctured count") + # closure of every frame: the listed lines are exactly the labels inside its span + for k, f in enumerate(self.w["frames"]): + inside = [i for i in range(v) if all(sum(x * y for x, y in zip(L[i], nv)) == 0 for nv in self.fperp[k])] + need(sorted(inside) == sorted(f), "frame %d is not closed" % k) + return dict(labels=v, root_lines=120, punctured=len(self.punct), lattice_rank=8, gram_det=1, + frames=len(self.w["frames"])) + + def nested(self, a, b): + """span(frame a) inside span(frame b)""" + k = (a, b) + if k not in self._nest: + ok = all(all(sum(x * y for x, y in zip(self.lines[i], nv)) == 0 for nv in self.fperp[b]) + for i in self.w["frames"][a]) + self._nest[k] = ok + return self._nest[k] + + def replay(self, sections=("D", "A", "scatter", "B", "C"), values=None, frames_on=True): + w, v, n = self.w, self.v, self.n + cur = list(w["start"]) + val = list(values) if values is not None else [1 << r for r in range(n)] + stats = Counter() + + def climb(r, f): + if cur[r] == f: + return + need(self.nested(cur[r], f) and self.fdim[cur[r]] < self.fdim[f], + "climb not nested: register %d from frame %d (dim %d) to frame %d (dim %d)" % + (r, cur[r], self.fdim[cur[r]], f, self.fdim[f])) + cur[r] = f + + def run(name): + for k, g in enumerate(w[name]): + need(len(g) == 3, "malformed gate") + f, d, srcs = g + regs = [d] + list(srcs) + need(0 <= f < len(self.fdim), "frame id out of range") + need(all(0 <= r < n for r in regs) and len(set(regs)) == len(regs) and srcs, + "%s gate %d: bad operands" % (name, k)) + if frames_on: + for r in regs: + climb(r, f) + acc = 0 + for s in srcs: + acc ^= val[s] + val[d] ^= acc + stats[name] += 1 + + if frames_on: + for s in range(v): + need(self.fdim[cur[s]] == 1 and self.w["frames"][cur[s]] == [s], "source %d does not start at its line" % s) + for r in range(v, n): + need(self.fdim[cur[r]] == 0, "target/helper %d does not start at frame 0" % r) + for name in sections: + if name == "scatter": + need(len(w["scatter"]) == v, "scatter must have one row per target") + need(all(0 <= k < len(w["ret"]) for row in w["scatter"] for k in row), "scatter index out of range") + need(all(2 * v <= q < n for q in w["ret"]), "retained totals must be helpers") + if frames_on: + need(all(self.fdim[cur[v + t]] == 0 for t in range(v)), "a target left frame 0 before the scatter") + copies = [val[q] for q in w["ret"]] + for t, row in enumerate(w["scatter"]): + for k in row: + val[v + t] ^= copies[k] + stats["scatter_terms"] = sum(len(r) for r in w["scatter"]) + else: + run(name) + return val, cur, stats + + def totals(self): + """frames of the retained totals at the scatter (replay D and A with frames), COPY ranks, pseudo-target check""" + w, v = self.w, self.v + _, cur, _ = self.replay(sections=("D", "A")) + out = [] + for q in w["ret"]: + f = cur[q] + hyp = None + for r in self.punct: + perp = [i for i in range(v) if sum(x * y for x, y in zip(self.lines[i], r)) == 0] + if sorted(w["frames"][f]) == perp: + hyp = list(r) + out.append(dict(register=q, frame=f, rank=self.fdim[f], hyperplane_of_punctured_line=hyp)) + return out + + def full(self): + """the complete program: legality and exact F2 identity on all columns""" + w, v, n = self.w, self.v, self.n + val, cur, stats = self.replay() + for r in range(n): + f = w["final"][r] + if cur[r] != f: + need(self.nested(cur[r], f) and self.fdim[cur[r]] < self.fdim[f], "final climb of %d not nested" % r) + for s in range(v): + need(self.fdim[w["final"][s]] == 8, "source final frame is not full") + for r in range(2 * v, n): + need(self.fdim[w["final"][r]] == 8, "helper final frame is not full") + for t in range(v): + f = w["final"][v + t] + perp = [i for i in range(v) if self.ip(self.lines[i], self.lines[t]) == 0] + need(sorted(w["frames"][f]) == perp, "target %d final frame is not t-perp (live labels)" % t) + bad_s = [s for s in range(v) if val[s] != 1 << s] + bad_h = [r for r in range(2 * v, n) if val[r] != 1 << r] + bad_t = [t for t in range(v) if val[v + t] != (1 << (v + t)) | (1 << t)] + need(not bad_s, "sources not restored: %s" % bad_s[:5]) + need(not bad_h, "helpers not restored: %s" % bad_h[:5]) + need(not bad_t, "targets not y + x: %s" % bad_t[:5]) + return dict(columns=n, gates={k: stats[k] for k in ("D", "A", "B", "C")}, scatter_terms=stats["scatter_terms"]) + + def interface(self): + """each hypothesis of the interleaved lemma separately, and the decomposition of its conclusion""" + w, v, n = self.w, self.v, self.n + is_y = lambda r: v <= r < 2 * v + is_z = lambda r: not is_y(r) + H = {} + H["H1_no_target_feeds_z"] = not any(is_z(g[1]) and any(is_y(s) for s in g[2]) + for sec in ("D", "A", "B", "C") for g in w[sec]) + H["H2_D_frame0_targets_from_helpers"] = all( + self.fdim[g[0]] == 0 and is_y(g[1]) and all(s >= 2 * v for s in g[2]) for g in w["D"]) and all( + self.fdim[w["start"][r]] == 0 for r in range(v, n)) + H["H3_A_z_only_scatter_targets_B_split"] = ( + all(is_z(g[1]) for g in w["A"]) and all(2 * v <= q < n for q in w["ret"]) and len(w["scatter"]) == v + and all(is_y(g[1]) or all(is_z(s) for s in g[2]) for g in w["B"])) + zg = [[self.FULL, g[1], list(g[2])] for sec in ("A", "B") for g in w[sec] if is_z(g[1])] + H["H4_C_literal_reverse_at_full"] = ([[g[0], g[1], list(g[2])] for g in w["C"]] == list(reversed(zg)) + and all(self.fdim[w["final"][r]] == 8 for r in range(n) if is_z(r))) + # raw word W0 = A, scatter, B (no D, no C), values only + raw, _, _ = self.replay(sections=("A", "scatter", "B"), frames_on=False) + ymask = ((1 << v) - 1) << v + xmask = (1 << v) - 1 + smask = ((1 << self.R) - 1) << (2 * v) + H["H5_V_identity"] = all(raw[v + t] & ymask == 1 << (v + t) for t in range(v)) + C = {} + C["U_block_form"] = all(raw[r] & ymask == 0 for r in range(n) if is_z(r)) + C["J_identity"] = all(raw[v + t] & xmask == 1 << t for t in range(v)) + N = [0] * v + for g in w["D"]: + if is_y(g[1]): + for s in g[2]: + N[g[1] - v] ^= 1 << s + C["K_equals_D"] = all(raw[v + t] & smask == N[t] for t in range(v)) + # C applied to the raw z outputs restores every z register (C = U^-1) + vals = list(raw) + ok = True + for f, d, srcs in w["C"]: + if is_y(d) or any(is_y(s) for s in srcs): + ok = False + break + acc = 0 + for s in srcs: + acc ^= vals[s] + vals[d] ^= acc + C["C_is_U_inverse"] = ok and all(vals[r] == 1 << r for r in range(n) if is_z(r)) + # interleaving statistics + readers = set() + zB = [g for g in w["B"] if is_z(g[1])] + war = 0 # B z-writes into a register some target has already read in B + raw_after = 0 # target reads, in B, of a register already rewritten in B + written = set() + for k, g in enumerate(w["B"]): + if is_z(g[1]): + if g[1] in readers: + war += 1 + written.add(g[1]) + else: + for s in g[2]: + if is_z(s): + if s in written: + raw_after += 1 + readers.add(s) + info = dict(z_gates_A=sum(1 for g in w["A"] if is_z(g[1])), + source_writes_A=sum(1 for g in w["A"] if g[1] < v), + z_gates_B=len(zB), source_writes_B=sum(1 for g in zB if g[1] < v), + helper_writes_B=sum(1 for g in zB if g[1] >= 2 * v), + sources_rewritten_B=len({g[1] for g in zB if g[1] < v}), + B_z_write_frame_dims=dict(sorted(Counter(self.fdim[g[0]] for g in zB).items())), + B_z_operand_kinds=dict(sorted(Counter("%s<-%s" % ("source" if g[1] < v else "helper", "+".join( + sorted({"source" if s < v else "helper" for s in g[2]}))) for g in zB).items())), + B_z_operand_kinds_by_dim=dict(sorted(Counter("dim%d:%s<-%s" % (self.fdim[g[0]], "source" if g[1] < v + else "helper", "+".join(sorted({"source" if s < v else "helper" for s in g[2]}))) + for g in zB).items())), + B_z_writes_after_a_target_read_of_the_same_register=war, + B_target_reads_of_registers_rewritten_earlier_in_B=raw_after, + target_reads_of_targets=sum(1 for g in w["B"] if is_y(g[1]) and any(is_y(s) for s in g[2])), + cleanup_gates=len(w["C"])) + return H, C, info + + +def check(w): + """returns a dict; raises Reject on the first legality or F2 failure""" + W = Word(w) + lab = W.labels() + fl = W.full() + fl["totals"] = W.totals() + H, C, info = W.interface() + return W, lab, fl, H, C, info + + +if __name__ == "__main__": + w = json.load(gzip.open(sys.argv[1], "rt")) + try: + W, lab, fl, H, C, info = check(w) + except Reject as e: + print("REJECT", e) + sys.exit(1) + print("labels: %d E8 root lines of the 120 (even unimodular lattice of rank 8, Gram det 1; %d punctured lines);" + " %d frames, every one closed in the live labels" % (lab["labels"], lab["punctured"], lab["frames"])) + tt = fl["totals"] + print("retained totals at the scatter: COPY ranks %s (decoder constant %d); every total sits at the hyperplane of a" + " distinct punctured line: %s" % ([x["rank"] for x in tt], sum(x["rank"] for x in tt), + all(x["hyperplane_of_punctured_line"] for x in tt) and + len({tuple(x["hyperplane_of_punctured_line"]) for x in tt}) == len(tt))) + print("legality and exact F2 replay on all %d columns (sources, targets, %d dirty helpers as symbolic inputs): " + "PASS; gates D/A/B/C = %s" % (fl["columns"], W.R, "/".join(str(fl["gates"][k]) for k in "DABC"))) + for k, x in list(H.items()) + list(C.items()): + print(" %-40s %s" % (k, "holds" if x else "FAILS")) + print(" " + json.dumps(info)) + ok = all(H.values()) and all(C.values()) + print("PASS interleaved interface lemma: hypotheses H1-H5 hold and the conclusion decomposes as U, J = I, D = K," + " C = U^-1" if ok else "REJECT interface") + if "--json" in sys.argv: + json.dump(dict(status="PASS_EXACT_REPLAY_AND_INTERLEAVED_INTERFACE" if ok else "REJECT", hypotheses=H, + conclusion=C, interleaving=info, gates=fl["gates"], columns=fl["columns"], + v=W.v, punctured=len(W.punct), copy_ranks=[x["rank"] for x in fl["totals"]], + totals_on_punctured_hyperplanes=all(x["hyperplane_of_punctured_line"] for x in fl["totals"])), + open(sys.argv[sys.argv.index("--json") + 1], "w"), indent=1) + sys.exit(0 if ok else 1) diff --git a/research/e8-bit-p111/data/bitE8mut-p111.json.gz b/research/e8-bit-p111/data/bitE8mut-p111.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..bdbb95aa970647bd52895c120b494f0f591f3f87 GIT binary patch literal 42345 zcmX_l1ymdF6Kz{46ew1#K!G9w3Pp>xxD%`pf;$vPKv|1DgpU)ca`M0lVmjQR{_m|N7+v$#>Owdqf0M$LxFyPn&*|CT4 zxIu8F1bE!H2nRq5_ik^_I&KgbNZ0g$<-3bZwcV44Rph;&NPy_{0pe&kz!1I+ck91x zxmmtIT(~T{-1%KEI}5Ee9h0nNh}|We>s{l$B@)7)J6nwr6aIs!wA=nFW|6fOIG<1d zY%wq+^e{B$1w&v4=8<;yhCc|#(H#0`lVb260zYD2OrQxn ztWEgr1dcu)KpMzo2|ie`wG;N;P^(REef zKifk|S@gKa1CcQ=z>nVS9|FOD*iQE-AM?}WfhiyIz(cW%Oz-Wsw7@SpTdc*QOT#2Z z7;%_8Z3}_kS*Kaq^be8l2K^W4#2r{QTjd!r>n`iRIM>CMWYKp6M<0yO935r+2?WayX3Gvvbom5(!Uu`Ma+LNN>G4axs(gHU)6B=DcK zMq5Z_Q9Sy`3`KuTO=4q(`60E(e+gK>#d`D`l>5Bo!8Y63@$8XNM(|Yhf9PYu+}0n% zy{}-|>w8H4am!|>|Cq|IJ{09$1*ZJk z7-{>%;zh*T7#V37+-A4&POI^izX3_Y;JRKT$L;$bjNsEo(nn()`3)xyUn2h{GGc}4 z&_KX3f?3I|ZDw@f5nw5uRG4)jMuVd ze>L#kJp?>PQu=pFV;^Dg)>i+s1B;E)=cBm^OC}o@o126gC9^Vx{jGNJRP!7ELn*T| z;JyWi?SsvQXM3ePo1ZQ|25)(JQfR7GbNY&<<7K#vYS5fj>R4_yFLMuLWG0`w#1H7c z``x>Ockk>~6qIg!s9#!J9XJ&g??J}Fj%3p?>Ybm`s4BwR<4Z%%yJn5b_rbsE{ z48|>~N*DTP^^L`3Sk?H0{;WA`FHeciP!H~0&AZ;y0nu&Oy2S^dn6#lImfhzls ze>3@{3$R64t7x=DEw<&aoAo~0d@gOLzFEzfTz2ayZAaQ{Dep>&I@VTZo)DfExGxg9 zFXG#H`2Jw$9)jre4|rb{UvmZ*8gM(2y^*y&XDPp%#A-Rro+OPf2?~OBm+M+n-5rpr zwUqd+Mzq*hBlRe9M8a58;W*<;4R}ZT`ev<+`^`9-EmOr8;hO*Wr}hQ*FwZdE0~u=i zr(DuR`q+rvQNJT9P!{h@B;O;HGoJ_M${`zE=Qa;Yj{>POLjrInJ^pYzY+m6y>JdcxnWiuol(*vg6i^p>6+fg~zXn)pOPFFmmEuFzZ${4ekQMnh% za+h7~IAw9d6kt@Lyu9gycDiolVE43UO1V*!9WK- z&sE55^cY~tY;PhzXk|Pk*mP;PFR2fxX88&B=QkXH5bGXqd2P#mi|o-^L+OtgPPvHS7CbxH5C*%|WkzuWehox=>|zr!{4&-eip1-JnQiCc#ZF$uwU4 z-my(f)j2zn`;3uNV{u5Xq5j80vxdydDmTr}mZ7mt%8A^_)KyL+QQC38Lt{SWq)7aI z&iPS=Om`l3kMu^#Rrkp2+}N(X74Ex0{a(v9RFy-G+V$OOXSs9bb&L~@`8Uf~jisap zb`Bxx<=>;r4GDJ6afHx}f0dR)g&QSDua>-8l;Cb*x}kd})zMnP=x@lzM<=T)Qe!8e z*d>iej2^rnw2*ZtD|qS){JAYnHFQRrO0Ac&MNDpj_R2HJbY9J`$jYT`{PS?ZCHafh z!gS?G2RHn~>v-N}%cEf`@;hze#$^?Ll>PeIEaPEGCjSk=kW=!zmKkV4<1**ZdGZs2 z4-r3}${0@{6wE-uXUBN3e8rgqdJnh^uc>m=O%K(x-Q(FGDOT2<<tdCb#Q~U;CS9VMQ!aqQhvRO;`)18|CSnM{B<%d2iQB#h_sYYl;F{Z&P$i`#);^SkTM! z>}*)U$2po#HUI=a+KAR<5ID7qKI{bcHOw(fMh+RCp@mvqiY!`D|J1nYLBmuTFPVIf zlbJ}_LJWOIlZ4{2_EJS2wKKla!fX$j1Yi{QNl9voJHhAZkBzQah;YyBmUMoXgBK9z zD^Jb3VlsADgPCRyzC?uXTIM$gR5f`tE)(IakITD3O=o5g!!d=1M=JP#d%d&k3at5i zJTEw6uCpM3T*>x!a%m{?9tOwOz48RV_~bK=2NV?2#zq2>xx( zKi^|l>(`ZSXyM|c#d^5i#`s3%1T`s~inib5E~X;jNO~%3H=@|kySxyD>FRs`ECcoC zaD~xtEfD`pV~2Zk&`iB?Fd9v#qi-tP%*Gs=Y>@YAjsE6Wj)PQBo<>iqW%Hw#JQNJj zwxUC7p9Xdo>OZ2s((X=7Eea!;_&*eqA?NsErx}6yTe{oH<>=t! zYN!1+Jfen#XRcE+GruumvYX`RYwp!;m5;Z(g;#nUDUK`eS7-`9E#SE(g=6wOC1dk$ z{HA8AC}1?~)D*g|=4nV06NB5`yeHo04&gr8b~N$v;VM+DOV>Y9Db{m1G8{4c5)srj zW}NRf?e_t{bf*JbHgtX7V(;)@F;UFZ#gP-v;d~n|oE8;4J-+=&?8}9|%=t?f$^iTI z`KgYLJAojz`)Xs|Gq-MwJ|0od5knbjhsy=W;fwC+`RnO2(TvdR1{u`n52P*OCaX>s zzVp28N1aeb5qs1XKkzSf3{E5|1$3#&FXSS`l9occ8DP4`|~cYFbd@C*L>6H`jj zL-O;If5?t^a^@yl!Ih)vgh))1d-GBqvZL|dF6I?K4Tif-XBYc`rjOCgjF(V?2Zh6% z6dxOWJ-c1xm}^KZN{rU*TzYKvhxebR7oj9PIfA?)bO06<%41 ztng&oPRA#?dv}D|BAo8ml&1tu8x&Q2h7Vq$Mrb@N6xZ9@HqLq5lc{n6iU*u&`R9rA zPRw?IRguhq`sF53QJUZEY^Kx>)V*=XvNvtQ`0)Mg!;zVa!|#Li>KQ_nOX ztJd_Oqo`@mw2D-lLZXW+5p%&22hS11h7&i+|9+s|?9rNv>jzinEkx)G7o;;vI}=OJ zHzWtg4C9`S=a#K4*E;rw>rRJG@)9flUw&VB93UEasFV9BZO&${#}SM3$Rl4rL<0QW zo;c$0X0qq7K&R2(^Ka;)f-(v>Db;Cl-n6x8P$R(ON4oCKuHmU~B}KmbKzAJ12=s0O zPZ4gT2X>%QF3jv`Fuj1X3vHOz$_D(wDC$_9Ty~nh?)Pl8DDGH~<3je|Ngs4B7}Q_s z64*XNAfv$XTk}pr%T|!|&1-F=hV%En<_n}D?J_7_hXbF020`YKy6|hX23(hW7aLI* zpLx!G>gkXj|Ne8|*8z6ZKW07(xeYCcqkKP}@SG?MMSC1)AcY>%>V`|L6XJ-i@1@^N z3pRAu{hqEUUR3%UXl(2H>Tj35Bwp(w*?RFqbTres?%fmi7ahWI+m3YNjj6#-d(pI+ zMee}iu0F&&$_)5kqaDht_2eDHYJHtcz-t@+2O{Tr`5BcI5e;Q+b_aa-)|eS^%m8cr zb2%dm;rG4{2c~u1NpkB>{;ke}P|Si8$1pugg9B2J>b`E?(F0Sbf28M#gC3rarm5n4 zzpcB@JugSU!vH(PkD4>(i_fUNUx-MTYlih57p#50_3-&!{Bxg0&W7Rdn;%*?Yr+Vd z9|iz^Mt8>fF5Q6kWjp`mPAd_kRQPc!K0EUvzLyLt@M0?+6-mgP{+e_qOtr0fnKeHNf}iw1dN+EAFhbjS3UDjLZ&@kewUwg3bk+SQ5NiVrwPrM zswpmxiaO0#?coF#wie0Qgt$uH9RA6z(BK)qkCV;=a<^wBP25l%2{OZl;qcCn`0-T1&G;k|U zWa{FhMB#97=Ks}7=n&c$ zlnGov@tv^ea$Y*TJrlWL>L16UnbFT8`aGQqimnW3LRpzmICw^1+;LI=!)>SL)W<)s zo}|fp+V_?m;VXN*M`mzgfzoV&!Vg?U-Gz4@rZ4UaPp;BiT8;+}Ml*B|MxUv%OmY47 z?6zs;>IaxoIOwfS;^FPZG~I{4{mQ!P9|hU*e_3(!wBU-3&vgV-5%Cmp`TYhBfvosG zUm<=#ZKpaMve+wf!Z4#KG)OdL*X_+hxp}fqRCpzmiVu!%o(sIuqZy+{MJ@&?Bwr_? zt~wL*_MmasHwe!gyAvkAUCEeqBC;7Pt3W1Pu|dCT%SN1vlSO8^ru4p`NreYoA28;h zTj=WD53=ZT6%CoboyyF>+^XPx&`l7R+(BoJ{$La`iW9v{wk!S{?+L^70(Imr&qgqO zVRW5MIiRlLz}v3HV0ao$Jc@Hblv<$1asD{vX8bl8+DEy$HHe_hI9Ll5Jao{xl%KJR z4rGGGXp?pQS z^x@1Jd)|*RJXXdQ|E{1uyy!SCgMZHAP3ny#6T2GqZ+E5y>6SgU224fO0HZCZ2W?IT zL^mWSOm@8fA!53VKPLdkOywBC^qWP^2I?MuKH$s9XmDz=kWW@z3TMLSuFOng%=pLD zyRNwYW7b8!&!&|yeA;uEp?dBY^}j19iACeF08zo-{N?2G@vg(Z)Gj`^&&WkLYV|(r zBFHg#wnO(#o^0atbE6-f)SA_m^Z3+yY<(+o$$grLt!46x6Vh!N1lss*)-Aj3mHCTq zY9}Z}_@6^v#Z9;8A-)JaWo!8cfq08QNMZP{hx0IVE-=^6v+t#-sN%xr-S!MxA*p*n z(%70>3Z&t5&BIlE0P%F(cS;#Gc$V@HQHS)^==prtkZ>Ze zWBT%*tw+FFPrO8#F-PU}*k$g&ljHkw&tul9%f5vD8~35}#aD;bbu{GT$e-!up_QY& z56#8mz(!=aw)PrLEx<;!eyicmdR|N=AicPr@jOqP$VUulgioqsd$MfSZ6m}PeMD*^ zG2K#sRk2`zKoTBH9K0|;evcm5qFPt%x7Trqj~ILGAtz>~i`+7PwOd=Ocfm)pLN*ej z+hdG$NAEd4U4e>6@*`p3BW?%r0K{rN<+dDl5%2z1`7`{W z(lGXQccW&J{SN>`Xy#}5d|;^=^)~AFow<(R#sA**2&ULr*EEW0q1047_qDaicEC1rfW80%d zt=wh6DlPANeO&{!7F<`zfD4LguPYecq0sfhIPWe-Q_%TnFx}~)Y5JiX=iiy_8U97B zdoR#5Zr$X&ch~r7(Zw(6c1gD=%~Jq$uIBzMl0Wa!$pua;e{g3+Is8_Kl}*}f}OIdygVD`l(LAU~}$d3;8u3y7Zfelv|cdi|cG!INHa)>%KcUU+5_HOw=-Q?GJ zM>Rua&szVckKXtYUlH{k5OHltUxzPv^gc=m{-5;p9kiypg=j=+E-}qoGQ)r5zonEN zoI!4My4f6+E}qh2$~Y4}_6&YSl#>XpUW2>Y&)6zF z|K+KL(R#S`tJAy#Pbkk!ucA``9~3amnDJ1*1t*oE59MR^P*kO&&Yhg~wmX1zUYctc z!DDZOZhFJa>v@!tua3kok@BdD1rfaf+yw^5`Mw4_-^ZWZM(2PpoQ4MjCf%)y%!w2QTxbR`A!Sda z44_f{>0BcoTX!Mz4?|ju2okfvec{NqgYDi-^aHip-DjbWi|`w3S<;(a3%&aD=DLcL z0U2o&ZpB?lamPVJX`B5)CCBI~Wz5dqpUio-Zk^8v7oY5tjmn?Niw8|a_|r7cZmwT0 zXb5u4k9&SWa5Sa&qm4gwtf(G^2^uS>=HZG?i(yZFn&fh*t*j9B<-(km?`*l|Dblgy zL3QrL@Vb#f2i*J8Rus7-7U1#-bTgQAZ&))3! zoByjH&e3s*G2Gc_tH(7U#H`y@IBnXh^~I`nxJ5q~--C2;t`!$ZWi~H(sdvsSTUU9P z9O-?{UV16K-ok0P#}KKQ$ebTm@M-Q~y}&HEll7+-GCXrgOsqHLUB8j{Z0umIJTitL>*Ic!hpk*d6IW4K0Tq< zN`OdwH=le)GIW~hjT%NGp_nj!Rr{>opi9(r%4gr0jx?HsJ%v4sz~$X7aix2Y_aZ5M zB9t9NDD70iHumkcZ3-{XSX})g>{+V$bzF#{JboXw5bD!~JE9lf0 zM+b^^)hx86>o-kWac~|hvgg@^Qm9$2CxI6>)xBRDIf&NTLc8NlV$un~_SH^0D*EWg ze(!nFEIwK>wyUHuI|O&oiSNG5mo47xq&Ms-s=`<+EBLeCH^g-)Ts8E4K0dcUIT~Ne_m#M%KCEc6nEVhxecUl% zSl|m7@-46aN|euktb4dp$f7w6nwUdbmvj6Ja&8aptJkP$(B=As%mleRZ+LsGnOf-= zFOg=p{xY1T6VWpZ-;mrfa6dXmUv(B$&-Mc~CW;^~^UqCX$Qtp1k6#uBY){5&R) z2Nu(w{mhYOt9AEX6)4bV-^d|S^k)a)l)Oo}C|dv+jgm2SAf#ElC582loB=sxnGNFM z%N>U_o1bfMrcr2jLh}JUd}LRm3x-*fMUDoim)LBwX73^ne5O6H+Q0Rqz0)MR?m)nJ zJXER>=}1|&m=C8GR__gYZ<0oqh3@8%gluwVz%JBXZ7Rn8^R*eGniiCvX(=(Iyx*4$V#7@ z!9`0ph+O?48fDeKstshR`=p=u&#}w&pWn6J1k`zx>>rK7C$H7h`m|ZKXlc?jG3!Fq zZ6cw0>OTdVsz|G=(dTR0Qbi}O!gNNgUoVeE>bno)@A_htSx)YLI?p_I8MS}8P65SzAQx)G=nc%w0Yr2WdzaNWH;$Fst|7bk<{edA9lAs5rOmQ+ zh@TBUzGa9Vp2Uz3SJv-eGu=(6^;wE3l_$PSjK7E6qAAEI;Ji-SzZrwu8~l;q%VV*~(J=gNC2Q zhbb3MPxxkB%g`z_I~E9LAtl5G?AQp}$S`=u{dwZrbs~_Wi`Wx$7;38M<{kETAq&0kMM#B4ak5G#8{nv?6h8l~$;sd@x`k)ZZ{jJw zb}q;rCr)<`6PNIC0Xw_*CHuGaYWPM{y`+HHydANzXbINi&9RP3yj)WWo+p=WUeCQV z%as1srkEoe>`bG^I zW>is$->(ay)ocm7A7)vhB*I6smbXQSJ#xt2&ZHERqz?TPr#0v3#iWBMqhOh!7ZOP; zMHifOXo=RoMG^;57M7f0l^(d-9^3WYFSi$%s}IT9k5whGy>B|Z8{sslap7B_xLl1Xm1 zO@qU+59QXb4aO+rG{EsbLD931d;hF}Qjy+$P6EnzTSEyssyLkj+*b~vpW<05T5_Ae zG)M~t(6EJk4bz;b<((ULPv4TuQA4)iOW_ebyYA z`6vS4fxWAX>Aw~3*;Vrn`64{>v~(`UMExGQC3*+Hzk~;5r)IrPG$CF7^D0v{YF(TD znbW6Xo-vw8Z~8+mj5NA@6{xJ zCXJj8$K774^q1O+wf8K!-_O2T*Y^LkVPcjQG;ez*EOudDUwDgT>|> z#LZ6wmylgzo_P3Ydf_+i;k}Wrr|^a%;a25nQi)e#F*c;Vgfq=U?{f<_PR!No)GT63 z23d0IuG6Q9|HLWk9E`H^SdoyI+4DyZ39$r9yJxuSD_g1rCSBIq6t{jbfQ={$*z29jxYaA=|vO75x+h1f}gb z*hDt-+g+u_zf&8S%U8amsu{{(0UKJ3C}zJ(Z7ye%-FW|wQIpttdfP@E_sAwuZGvXJ zl-0V7uFCULn^HkDI%(){JXGsEN84NOut+KoPRBerVXlfJZfQsSomh&LswOXPWrZ5B zithgK#)W}-Mc={;SA637<8W}ex>~yW=d=XKAnaSzua=%C*_I_lH`nFFo7}| zui8)vbJ?c=Ly{j_uE&CfGQw%()5rn=~2rIVY=N_XGVNk~} ze5?u2yGexk6ArU%N`}}iQ8I7kNKN90*Q@wnLPjSLa#Yh**8VrxDndRBV&pE8QqQ$9`M{#2zClD|}C z5FHw!Iw&*Oy4tE_rmd-WNNFH^56{n_}7gjddoXu%sP7HoTCu(dTMQJrgWqIsaR z+Uv5tr_!G%d=#aqEM2m`8q>D-pBXu*m56J@RjIgmnAFjvr= zJjmkVE3RGpV=T2-t1wE&T36H3>u;j2Q$b186bZ?g5Nuwh!=D`TiQcmlGdFH-JLMX@ zL$cH4n?2`IyIGqm6g4FX=}SL!i5_Qa{);H%Ykf^2fYFeMRCG*L)VIoErJV*-P4XNn z+R?+E74tBIc?)v{v*{ju>&#m?9lXPk(JadDvRwR~&aMjS-&wWNe@0S04OkK0kDBsJ z`sGmYvfV+NC;CnB)>A#A)vpWWWj=LhvQblj&#Qbct;cF*A4mSZ7FD9Kcsdbvc($}X z`nRoi@|?M#WOF%qthR!pge*7ChdRnz!s7Qgsm@^4GNQr3B7>-j38(ZZhU`f-yXFZJ zM`5BB@0USbq;=0f%f$Whrld!{DYhtE&|npHQsOVPjHcWnm-Utb+FdWb5f-f@+4=It z;!uqK!WK(IhPSRR4zqyOKo~cwTrS`9Lr$Q#gnom|(vJHR)?7EVh$)gXdmj4ckT=mZ z=&?V>P34t5eq@yLwPS-=WIxyK>%qoS$K&RE=680ccW(E>s7a=IzUs2J(R4TQFD{R$ z)%gb4$qLzxT>KGPX-2s?=C)nl+MMEx`jmLh!ZV&Oe4*g1OHBMTvi-$z*(>pTA9BGn z|9c=q9o9a&pZMzhQ#}vILczvyfJ1xVpwgE6FIWD7Hz}0-az{g{x*;ClEZRq#=SPWH zb25Vb*(7EA_UMYFVl(0ckM{FRdU1Zb zlDws_bgI{nXFd`19y)_#|u?xm7amgsq#D#bCc&S;mN{Mv=1S$_h^DhS7v9AypUNX-W%^+Q3Jl|qn%P(JVXpjr@ZhQ6#TgL07 z^M7xFO%_7_1uElOq78fX`RlpMoPhD=KYTL`&|#o()&J~Tnt+w8B^N_~64&0y@dtNY2&aOcgPOJ& zx>WH8HiP^@LQp;IyE^71J6MPs=q0;uP)bZES|N)0yPp&lwb_2>^a=w;elNAEy0Ugp zmYBfc9d$t85P4DB-*(Ux>&Lu`sMaAF>2JqCf)2;9qxg<_Bl9!YJNd#R)|>X~?%3bs z#ZvP1BAP!Zv`3z-+0;f3>lE6HMAG~|7@T4e=%o5R~NTr)P;I}fS-nEY~e`jO;ItfzkOXx!CH zHJ2i#4qtA3(kVt;)^8aToWLr1Z@SzOIws`GXtt>=ea=%KYH+CWBWw@Of^ilqn-H${ zkLT-aFu{h&JHg4%vT%>L8l|N3Qw-ZhF1j6aXy1i;)$eqo=uNI9(-f!0KZ!RR{Shg> zsV-r`*4WT(;4Pf8T05X#*byt%NNcx6`f&SK)=Da%gmDvGcc zegs~$;VNbv68(Ds-|?gnVn3fO);x#Sobq7MED*hLY>T4_KYsTW^_1Gr#Rc|P$njsR z7!-*t`Tpc>{&u-nP@%>Bp^Wla8ECe-{VT8Px8$}p^iX)ZjJ#iwh6}jiGt;RR1(3dq z2hwv{J#EYQw&wT2L{g>suVGaejB zGXB1{vsV#wwU^IsF(ci7CJ0~}_`IE5H%$4YQ(0GPy9r|Qit*!O=pN~Fo%=|ma zFuC(iNe-4rw0MRZHB)xtUK78bTz23j)2FkU!>zPN|0scsMBEFuZKg;SU$R%hka(%l zEW{M*E1U67l#2!0bN~x3>)#@l{PM~&u@P;hYFB)P2ir{Y-LoB$Rn7@EMIEur*`TnM z?}m@X!1*6R0WjE(=pb=ZuUB24y6Vn9roolF?e7luL)HNG`A$Q!}V6E z`4kKfk90jS&_7tin9GQ*OAkb%$CB25jCM5Oq1K_921kYxNBqz z=pkQQ2G3i6s6t~eRa2UlS_Gf(d^eUqx!7>Q&!sEQD1jeA5ibN!=SOvkmq-5X`p z>G+J5qHgSgpk!sEt&9LYv%iWYMnXH7WkTR_3mzlf-%Ka_ARXkMg@9olcU`t)5imUvyh7(2g3fs3OU zgDRYav}`4S>}V-U==pe6gzdct|0`I@;fwF5HhA=EJBOiui}!vM#n`{%b&iJ|Zn>Rd z7`{bw8x(1M{DEfq&*p@XeM^E-w)xl4j1lGghSxjtb=J`awxdXwQ_jBu8bBR0g$9BX@_|Y4iD&1 z6y;VuKkq`0h+};sSe;Zfi2gLFOEH+O$2LF5yMrO8w_88n;P6b`)UI>0x{`BCV2>If zM0P00PkiI>!8z$I`oPs#Dhi!}12=N)51GF0e|LTyD!$sZ#D7VGYvpKBAT4A9}_YIjy2@cJAF!ZvkKfrkWYwV=hOxD}Mxwp0v1bk{Q zesDrYD7y;qjcdXx2c>4x=xnVC`MTw*$6io6LZSt`LTHS0V=I5j%_K9}TE60wz@jXI zL~C}1uo>qjF+?U;HYv^|Fxr~Oe+eSu`-4S!1i|%*`{_T(z--G-%m5#?ZJN>!S67Z{4^Xq& zmXKDP;LJ7zD=nXf-euk?Y0?w5#3J=)I5})<$@sQ$C~tzPo^~le15%^Y9({Y?5ELsa zF`!2Kubbf1e!$u1#g!yP2p}OsOnVd&yDU9GPd}(jzduCij(E}X71Ao%6+>@a7+2X1 zoFNAcD*^s>ymGe#UbcQ|>Vo)wsZO(yFyJ)~{SnKdJfOg|-$`_OfYpM6=zr@vlmNC% z(Te8AVuEHqzT0mkKDEMb5eyc}mJk6MUlhk$s1Izh{ELQd6u>sxVH;Dh<8y4Uz~I|% zi40TP%<9S(?Klpd8CACZ9@0|>oR;?B*0t(|jjkA1W2Tnad;J++j{W+ijXz1ph+*e@ zoRepecW7~!=n{Hp+6?Hl?%z0_#KXqlDtZtUNXV@LU&J#*E=js3aA-9`-ZzI3KqbRg zX?suvCc!U=WFUhK5<*0@7!jNwrNbKN6s-tNS_X1{y*M$2B!B42qcHA@e$RsE;+ovn z-AZkK%fPKdhs_nrJQkVk)sx0x&I+JYqx)!v0nU*H>=M_MBs=sfzTdRu@`?1)|9xq~ z{<@|oSv0Z9EZj3u&g!?ODI=xJLNY2b%;|TEg6k*qIWA1-tXFtrf0~H-4HrJADte0W zK&yoL8*C@SV66y=TWMPJT!JmtFx+=N@x&%7*iNj$T1gVVinIj<1fgolOYam>#0A0F zYBIqyc@jcuv=}7>{OVzKEIk$^Ce7GRM!_>x60O>_GGzqQ8eze#J#Z3}Lu@DS;F)#_ zAtTz5N&*k9F!lF6{G=vyI8F({Gs6t+HQ{V+Itk1N?)Cr;IRuqQ$y%A2;ljR40mER&;W zfo$yzrwSE}_7oC?4q3y5(BVK#@E|#akTqfm-D`*mB_xL$vPKJ`13*leAvvs&HFgLc zH^hV&k|O|F6NJ!-LQEte-O>svtSFkhKN~T?@pd9g_14vepBk8-SP$LvltTYhw_)Nr=e|BxfG7wgjPD zgP3eUa<(CBdl0%Kh{-7=2MJlbgwWl73BJSXMaK@s!Ipl7-TQh7TZA}5o;X4?fnSvZ|w&j;>0kiN+v+yGGY)gx50n6+cR@pOF+TxQEV^h^#mBDK@X3y<$q+M}( zy@wN@G2E${Ko|ao$;tE3(G<%u09e06T^ZwEWemv5C#3O6i~KE-lW%=ErOg0vxKcc# zd-pWTH!tjaf|Lv>f*got$&g?8J)xDcu>O0(r3^?XEsaMOB%hwT4IK1#W}yLw z`!nP}9Rhx1$R~(PFp>jZ(*g&>8S>fV5@whSh2s*4fuJD)p z=oOcME)R-e24dwg#zcW6%6@3aS5CXpigYT!Fq=LwYY>L1rP-X z5ZKO;|0F))ts>~UGEGkrv|E*ytO%Op1_DPI@`d6P2$Vq0Jix(;e-7N0gUjZw@fB99 z|2X~*4RQ?b{0p6MKlQN}y5effxYbV-0S=-V^6%mkUaNrSI@7=^pdoP}78W3%H6h`* z3aCU97)}7l2PPy)se&SU)4r;L9&Y~!DFONJ2?>{~ARRd%kRFhqkdWXA0(}}v`w0TE zC;-FR0r|fY5*XA#*h)YwK0y9*LPDAvXm>PiMh!Ig83+^y~M9b~Nm4A%wZyTKC9)IstyX`C9MPujpib3lF$ETKpP zM4<}=+5_@CVF_uJ&1vnTF$k#|r@YDwBY^K#}gXArMSebzQpu~jtIv^G+V0Zx_KRYoY zPX~m(m$sq5#1Rm=|CF;L1ApfoxI>~$>o zs~E`Zn_jCJ$O;V@jL!g*js^3GgMiP0&&e2Irm^6k;-Gh-dfVb4@)tm9ItEy5EZ9f_ zbcF@%ea8SRj|I<2fMz50h$TUTFM-c_8DR6V;3!E@F&;2jhyivL3r3d$g~#fNNrAiw zfxWT}FvjoTMk$as5m5Rw15D~Wm{S_`kp$SQ!2mP)4la@gk&^+X4H#fwzk}b(fUaHx zd(9YN8Q;OlGN4%sptKzWto1vXKo->W2H5M$0Gs;`4wD6?Py?lX8DN*+!B6BsUT=ZD zK@2d`IIyQ2NSh8Q9mxRWi31yN58DK+kU^xX)3L8+mkpZ?H2kuk=d2s-HI~ZUX z@n8W(kTw@kx}O2Y5D%_Y1bzGf>>Xu*NyLMhl|baYKV&AyYF1wqW6L$X>eHj%sISG?<+^r)^P8QvsH)QB}dZp-_mF< zz+ZZfDxi-Nz+P+sj5`6mtpXyK0!qIEz*G{zMyjAIX<#oo0A`;6o>2wO$^xZn0kEiq z2W5hqfWTg60IVbdJOBcvC;+870kDAtu$UUiO9|M^4}fhZfE(37+Mj{aq5v2=49p1z zeN+YZ$^c+=FmMqVM6L#uRtCVtVBojvper!2R~-N|fPs_ML9-e_X?+097X~KK05xd= zd%pl+sW5Pu1}H@bC~XUXHNwD8G(lc^z+M*sYzhYU)C6f80Hu8ZurnC=SQGTo2-y1# z0DF}Pw$uWVn*gOF05Hx(@QN1b$`si99RO2K1Z!!7X3c=oDFB#lBKVIssL2A@n+1SH zB!cC1Kq*#0=^_BEC=uML1M;!~_ErF3eTiTJU68gNP`UvC`qZAHZr>G@^n`erp!t9;xJ&nd)EK4PA7+n9MH zDch(;>Qwt~I08)$@>QE7Tj zwR3f-%va5e$K3an%2r8togkN}N~{t;Bp;V~uG1uvFYAxi*RDjo;ibjb`k1+u^R>-} zw5^1V-ZET;i*A)oIusVEdZxs}Bc&>{ImE(&AUM*u(z;IokxeGkWS7neg0mu98@ImR zbKid498qL$BoQ07a5zqN$ApC&>5Pt&p3UJZ%U%83z{@chOf zGzgx=FqErB-f^+prZq~`QeiewW!iPQuae9N2pIIM9D2R^_fhLHwOB{^i_P5`N=XT{ z-suE!NeSyo<$^)9?J#cZvFRB_DS&!pjTw#Sv*AsNr- z72o!}#dwM=`a;WJD`W-h`(@Mgc-~hShm%Z3cEd+|Uoj(Jk}}#!j7xbY&cX6TnDTik zMcki%*j3M7mc=_}XB=}${m8Zq^2OpNR+(Y{YLYhF<~4e(v*t~Sjng!%^(l9PR5Mh` z7m5DLepqnPy4cx9SB_N+OB7<$jWGY!c%=;YW{VUHA zygiyzc5xSuoqJ3oW*+P@YWrR|dxCWR_c*2ImX^F^uyFjCIrH3aXSyhjC;NlK2ewXx zk96x%-b{8ELIrU`uv*P1BQnw}Fo~yw8qA{6BAmp!{8M=G+>eHqnqF`p9LLB~s%)2G zLO;~H&IPUg4oxo?%zdp1^&36Dd)QcG{~GAxZc6tiDu<~RZET!pH%Rpil9=I*AzJ){ zZF&XuEVg52DelYqIy9_0H`6mm-G#{Pt|h(H4~EcL&JYRAAWgpHtfE_Dp%+@w zkTLoECx-tbtUeYM)-C>ywT`4!Pwp3(L0}WtXz4Z^pwICCW0PYpe?qKTxfOZC!@-}+ zi}#Y)YJpS(Gtc7%oV$I^h?rUD&JigsQ;(6=-uQ;>f~Oqq!-57}^86#cVW_?XK!bTf zLv(Hp+cU&X_)B4A-ZQajY37S4rd_$6DlHzes+6xexbN$GNrLJ)K}}g~jkNQRF9>a^=VDE_i?0r}I78Ro zGx#A5^5raq7Lx?;dL_B2)KEk)2xD8>QBI57Vr>kXK^2(58w zOFiTHs-+togcc(UJlxJa&S(R>FWP{jts%G6%Gq=lskP^t=*^_i|7oEJG20gEU<&p&HRY2W6}R3XZj*#y#_# zW$hj3j$%%rm=kQf)f3#i=!qDs)N?&iO;@Pt3N>B9P_%VoAp)(KFR+f|OGUEN6%}<; znlhkZV;dUWmgkIEg*rY^%n1~8c1xsp6Kc!4b?iNfS9#L7c5!kwpcrSoUtyMPWssKwHoFZ8=s5Xk9@#ngwXZjDqKJoP>fa-Hb7R5?XDDdxi#-bT4(K-We46g}YvDmMd#c5WmXBv8ne#5xGDSfdCXe5yjM2Qlm;e%f<1o4ZmirGy63!Lfh4a zueYK#4{mG6N85^t!4oC4o(NHIafQb5pvJVnQ_mQO6T=2@d$=i9?;G`EmDbNA6HMaScfb>C}P-9@>Xr#;b%sc3wKJH?rrWvDV*Sw4g#Ga%=$gw_dDICcjp5(fcA&-><{H_vG( zdJdr|CZN@Ge9T$a8BZt%4)+;th9dtY@+^#mVw~k*;I_DHM69fFKzC}pxNVKY{!kdT zm3HdpoC`>>5??@SE2#~$M0}&o83<9X3FWq!vqJZdHt6#S<(_p(n?mE7eeaBN&xnkw zI0wXP1q!_YkxJ3lD(9-O6JKZ4A|6&KuGP@l8Kqew`WIKoC{hpO&Q_bWf3(H*^!r;q zfm&+IJ>$-h-*)9eNzZV}N6!?k8nJ9Zk**rioC{%}j&n|Jd(X&u?QZI%Iwo*?+!3D6k)HCiA_T9qqsp|?rsb@qqBF;22Us$y9!Dwa0fW{J5F}(SM zQ9n}8m=h7$J#y%-XX zyFM6|l@`BHmLJPX5dOa-#nD=(bRo26f#3dMwB{7Py0xV_Bdd_#OQZW-8Hdbk(kRL` z+JLsU@X;PAixH%oFk06`TX>xtzn5ltb{R!fZH49-&v$cAYr7gnuG1_%%~B~#K2YR! z@HR$tg}PrM@~YtK8O0Uw4AEv!6jnIM`N-0N`01@pQd_Lg#&cecuV-zN+JxfRBePYp z3JQf$w4Gc~!_di@RMt44HD?+((zubvjWo`@dV|rKP|Rt0wF<>uuW>+ULNU(T3dQ$F zj{S;a0^w_gAE+~0Jo?s#@pLZH} zrg3K)2anP6URCaCUqHIT$^s`J=)4(429Lr;R1AgJ3zqK4oOBK9X=o(WIlE5O(r{EM z>(I)^XXs3*Qf{Wzv!tFS^+cYU@G$_@gw_?Vk1H(AI3e71%-Oi8QZb*~#i-=AgyI?O zqFfWMroz8^f!IlFEK_jQO%A7b+eyY&$=R!nX1n zRN}B04`)TdaHcnS21@5>{-r%2ZFD1&_A(B%lgf4E^ldQiw9dhP;qHd(J*t!-5GhEa z&9=9b(o%#=6i!zT>{uT<`cWEzbug(^(_+|uRmpQA2S*5%mCp+&-cHe%i(^-*ZbeQ- z*G!$_BoL&qja7{Uy#IBgMscEpBfXc$5$p4&WGz8KG0MvdgXv5h#oBEYKOGii*voGY z+F0APC8=th$}5aN{iG5dL)b*-#=Db($%4)RYQ@P3|H7qEI#mNfQk9b2^evLq9BBin ztB#7N@w!P(jOf0DSya_HltOI$nymH_ehmfkNJ*d~O2ffv_vv1zY&s*HhD z96{Rwp(x61^lVwO3r6yb|7>v?Fff7NR zL5OsaMXIIjpg6&0J4c=a4u@N$bWns=AVy6F$F@paAo~NIm$Apzx2%ES(ng5J#jaFy z`Cb(5x_pEJYXE0L3I-?Gq>;t|Nu&=puB2=P%i3r8qdV~p%gOmv7EePt91%zbx>JNI zpG@Tj1w7&kk-d53QxT;lS&B2#6E@1|L6-JP1tcuBZ3;x`l=ETn&yXidC_%!a&438B z!sa`hjLq=O0sVAL#n*Pera~01V3Fbo5StKT&xu&*R7r|A4_}q}+g2vYn=OnG35~)t zm&pygXoNuL)QT`Bjkg?(U~`A*s&^s-gP0rzLJ-)vXbn*Eg(9NJx`C4{=kQu*H2l87 z-NBhD7;)g$MNW~6r)>Z%|Yotmf;AZJ4CXjzT=?x z<(@||y+*O!i)#VPcjSJ=atiAj#7d{6E6XH!Qzed`a_PBUKV?mwL7QnrxgQ>$E50uGZ1k+M`Clud@1 zpJ3LVl!1pBa#XIPo3r#HmrrDL-7i< zYfON{zLQdHkXz%Tsu?BO)=|A^P{83L8$lh9Bxle!gUpYR0FHT$_y>!=h>u3NE-d&O zHwSN%8IvlXSUg-@T9~yBrgVI+ptl8C9|AeH+Z3l02fVa+UN zWxC0)5;k~(zjeTa63l^S+N}ki@*F>};dDg=x z4)5}yDkQN%oK%Sk^+lps36f&nR5@gDnUKy^LOK@!S;b=LnySkMB}TQkGdKC z$WjIS(?X3)8Ty%~C0j`)O9r8uO0kuMgiHfC)h|@JNGZ$`gwA!yN6m=^`m%_?V6i}| zQ&t$z_Jo}!Kp{I7D!(jnQoT4XZ3p|?&G9}9R9b2($QF>bWntU9+0}hB<8=Y5--e1O z3y=k{amig2IArvoVc$yK^IwOlzAM?_XNBJd0bV4*rN z3!Lkyf!ZevP`Oal+gYGerDvQXFFL?RC4`$kc(bnzGJh;^QnfOboC|Q(JyauXWIm6` zma8zJvRl~1Pj=-*y%Uvr3!GFVO=aH6>8qEYs4<{|WtHoQq5RO`_@kxW(1`;TR~G2& zz=1Q%0(G5ot5xSA|9^jr;7mQTf#xS`Ehm+o3LXUPxr0N%J3DmPTF_EngT`UUHL=dA?9gJm90ZP=s|8*R z&gDh&xY}gTWY*Q4g1*^wbaCMS$#E?w8!HA`Cl)x@k=M91oeC~hHbPb|_B6Gl!u4e> zaK2Dos{qjZgdI~t<-Nk?fMeM~cJoz8x-qC!4;=d0#RTRv`OC?K5{?Mi<_4EV>xmyi zCis$!?p#D`aRdqjSw=LAy5i9DU|YfdBIFM$TtlaD_?2)>@d8wl9Zs(WWGSD8YZapq zSn!&lgbgb0B^*N_?2w9_8&JR+TbL_VehUB%iz|~Ya4zi?C#nUgNVZv%4=P&4WenKk z$bLjFF~)B7ljsaguNhQ|t}{iHePbN3O6B4zl@O~`YOJt(o&6`|FA+K%GP&aU`@jDD z$N&8w|M{2qw-j{KVR|XOG-`9zfydNWpr;jmU-|;|jq1zSUqer&dY;waOn+Ye zy>&I{^3cVjt4Wuat~XtpC|mZdWL@X_N$3ZoUyy!Z`oZZJsGp>Mqx#Y6SFGQ-N&poJ zDkD^As1#ALqH;zBk4hpHS}NaE6spuzF{(mVrLKx$mCGn3_PjRT3v_eP-9pXx>J-pD zMRy$Cf^<*P9ZUB!-THK6)NNBYRXypeSzs1L(`{Qfb=~DraP9ffRI^YeM70uCQ&ewJ ztp;TrpJ!7QNwq0eyi`Y1rA<{jwH&GmQguDm1XVRu#Zfg$)iY7*?b$0(v`tRvxT^H3 z7OZNqD$S}=tB$Sexa#Msy{m4o%D!saf z#ngGHEs;OV$^cOEfKm&T=%5@4C2V-P4*gRXtL@%vS1Lp<|Wj!%FQ?SyhKsb=Ov9wH1lgXxhK6N?wh1TYYoY*~v`LZF($3 z89}VF+gFzzc5Nli8i0M48ibia?G9K`m-y-_Fk-10P7QdrV&+OYeWeVT=HFLGg)>Th zZMG^-I9=5zixnZ}9=BC+u3x?z$3`hQ^lQUZ*ZkKW{)->}t9O@?7q$$Ok-iezi9N~W z7kZo8EQ?BhYO_p5iX0`k(!^VZ9 zkIAyW$+Lp<+mdB=h=kn2D(NJn-sICs@~OSZB{PL+xl8p(ntU43dz zt*rheqAuntI;1aY?oipJ6GS&ryM)s=lhUj&%LTY*W_8- zWLgiorepvmK|roO_qAl(aQWu4t%}REBg-|DXW*}SGHYGwjWvO}+D}^Xb**-21ia#> zX5gznv;&rd#aH>1d%n|ScmLek_0$3Rrz;$?u9>Whox8it6PJ4@?anUuLgrm0_w1;T z6|J(5WS+^ox$9ne6=UT#TfK9&Vx=~dao9!8V7PYC6J|(TtuFIcp;lk1zCy7YPm+Ia zt+=^*VpaGmn7{h`mF-uZzi6Mo=#*b4)J>CJuG`^PtoF-c_qs`6a~T!#X_#D!9qTW5 zt0b2Wmrd2>)0=Fn{%e}u!hY4MZxxbDQzqU2gPn}@DE@_^Kb@{Sb^5i%b*5@^=~;QU z!xh(=E|{V7E?2f)u92=(T+VcoGpEayx!c{_Zg+0GUH`t@sho)2Zv3{}4d3mO=70R6 z{r{V2{@5Y^M)N1F|3l+H|ADqI|Hc}B-8BBWqrUB=pBlf(FWN!>{k8s+wf=o){B2hp zFFRxT-`gG2Y2e!~dE}iErv%irjDPL9lMa$&g=E>8 z&xVj`*WYRSkVR#aLE>_#x-7c3b7Aq-Fi5+vYZYJ@J!mz&Y5Kou_~#F@slRCVE~7p) z`s-7Zf7Rg2-)r#SwDyoqf33OSQ#O$`0muyHlAd>W+5^Qygk)%H!JZ=0+l`Gi$^#VS=Vr|z~% z{|lKle1|pfuC3-lR*kW(;%zsL)jW69PP^w9E5}@=VeOc!@_M&=JYSY_(QKzY!82UD zvGWbYpe%iG?sk@(yZ*_Rk0}RFmwlD298(9p zUo0K00~V5be2SR*UfJ}zLUQnY(eI{Jmv_J6J0M#?&hJ)`u$jqPX&)Am^UKcoW*K4K zufFJia|hjZz+!q|()s#}zSn-x^)8aDXSSL^w)zPr$<=d{sr^Nk(k>hN9?R3I=N!Mp-I1+yMB?~&Ue1p`py?y-SU&A1~-!6aI?#Cyh zc@mNrJ@9ZDTwMl_7rjvE>*(}_yq$!0|A))r_(IBy-(#MF_AlexS8;(nvc81oUqEwN z%&UJt^-S43^~-5Tl+BJfWpmxWhC({${2IzXv?cyzL%japcKAF``YO_?r*E1?Lx{+` zX2Etr{xa&OVazXsHH$)DM)$8{Y&x;ANcXSfCc7Yi8E+Qw`v3nP@o`G}ZJ*fo2;LvU zr}979BmN!shx)QV6gq+J`$Qv8;J3YEdISIM_l)W%MeGkZt^Vfr)Iq+V?zU8x_t*bW z&wkOdU*-L|*uHH9Yy=}UBC0)Ie&9-fps3zI!Ze zmAB<53p>dm+emxyOq`^R%$4pd{LN<{tGg`wZOG?pnntYh=_Bjpg{Yfdw`{iEW7ESs z$z|3y&KIj+%_~3bz`XiTxBg8Hq-8sB(nRK0Ftw1nc~V*HDA}v}ynB2NQ`;!Fe+4I5 z`VRjJNZWX>ZC}9DHV$9;bJcfR`F+zi?w8d)S9aZ3_Sjc8v-0+p{nIIQ+NFi;(bt!i zJmvTKm;Sd(nhAS=k8HP|GW@IGjlI8vG?97YBt1Ly>$1u2sZZaoOP4R&N6Fd;R{50c zyN|7Ak8skfr)iY$#?*;jzBcg_T-o$tOQ>P8 zwD@Y}6WcC7+l&2T+Ad_S*z0Y@Zu0YOrG8~JSF%wYU$_kN!e=-Un6PwUmZxLWfp_hI z<>x%rRo9C*zhBt3EAgQk>A!`nbz{>OnB{kE@|3lOBWVm8ca~*p^;LY+iH`XTyRD+U za2I~!Eo@A{#95d!w&jyv$XfQoTx(T-_y^C-SpLwRCp%}_C@j~(V*Ir)OdU%>V)A*s z@E1;G09q^mBn^UX6;odS#$QlP;+|i_{e{1PuR^vHBFpfbz6R1H61#a2yP28I+euJY z4y<+alviJ@bM?!KPh4A=w&}6iDwrqs!v0Gud}I%euj*LI!3R4w9>ea{9;Dxjugvk{ z!LGC_PhW+XpW2vnN-OTeMqJ1WHf_bzPJGhI>_lQDlcRO9Vx(-m(930~+kF$;r5-=6 zmV;KySStmqWMj1G>Ud?f)E9ef|2*Tz&ilq%dE>XYcl+ygWL9#V)BE;oGE1fV&Pkkv zq{GpO9~$P1_aTs-zz4fl(+$Nf>UvkkJa zU!;RM^DgaRZR4_+FI-VR_uemj(TkXO?Tf7Nv+mBg3omTjq9h9Wnl4{WY)P+VNpGJP zp67l(nRX2cv9h(NBaND!bxE!~*EgG`ufFH^OIG!?OSu)NA1@3$Br*E1U6P#nSe0`{ z?z>VUJLR<4RI<2F9c_>Iqru^h8Ace1+4zhjeu2kBXFeD>)Zy0%q&Zj6_0^}hOngjO#_Rj^$W zE0!WCZlM**vT^u%mscn2QKz3Onw@`>ucuC54|c%rt5IG!04yW@tegBle75z~81nI? z)pyb=yU9;qzxe9edP2KqFqoM?-@a)6=eu|Ep4O5n7pEOiHouIorf>2t&>#)Dwmn+nE&k=7+TGrK-8%!M{a}Nm zGi0)I4BAb9vUH3ude-^E{x~-deBGj7%NO12cHOJKF!So>Uzu|2zhj?kWD&Vuc4D^A z{l)}&^Lg(!y5B6$=ZnSppq=@};v6=+vCfHVw@a*#X~QeozXDrc*(}ZNf57;d7L&5; zhv82do1KsKL)n-f+d1*qI7ix%(~9r3E00Yk)-8^^zQjECADA4|cQXCj?9<+v&$~~X z&x*3niyxK^Az8ScEW(oED=~J~J=Xf$>EexxbbZRr@O|)3yUEcwdGs${z-h0Wr;zgQ z3B(GmbRv<@b7!1N_P9jZcH7Ve$4lfIl-sEq{-cCO3y2;$1qHSpvhRpr6*=29u zc(>ODPoGaSFRZ)7G3qaRA<5QLURGs)vC&PB-Z$G^eTmH7Pc(bG(x7_jVFFw^K)Z zId!n#;v(7BKO@z!OY;tWz36#Yj`$B==G=jc%d>fUU}o~$$>H2q!$0BNR9gi}j_qgJ z>+>uNkJpq}`C_ci<=yOK4gezJ$tsib|`dCSJHA7rbmU%Y33!yx`H^WJ?D z%w}M#PX7{D`NlAQi)osi%D-VXu%21Jc-{)_?&PERU>Pr8#Ve=sBc|yKuM&2~pE#9o zEQen>l{3O5bWe{od1XB8oJy2))h{ghmOI6&lgy2;rETYH+3b<*mFr}e-0hKLJ4Kbv z0wgc7>}|zZCVjIlAH~NR{2=?}^iMFOVPg%yv4!LG#5T;sSNr+Gr=-(U;_XcNUcY$W z*nZivzKY%bH!INj#R_!1%X_TY`nwGicFUh^md*EoK`WMRl>?PrK5dm{`InQOa;{L+ zQrY#=@S?r#0p}M6#7TUdZy6UV(!;&4=J7Q;*j9D<=S+wX3p{(_wkVOr9PXKi`N1G= z6jwFv1mjDVnVnI*vnY0(!8i8Aa9KWHWO&Wi`VUL}`9+tNFFv4i(`(yiBPd(ehsmHQ zU{QR^aP~P9n_}{Q!tXp|QpZa~ptxu7a<8E4!SD{s3(KOvuq<{y&}3JjZb4!k-(Oh9 zEVF-$hkx-iC+5Yqyonztx8sF%alKg0UNT3_FCqNTZ%nm4E{g4}=f+msEM$9}RIZq0c5ga5Apqiy;=F&(kPN8{~$Sfy2dc0F!qc%{Yws#nsbl@e`l^JmZgQ{<=AWh zbM-LGY(_$#9~lEpXT5#5FMhTspAWL%T&OOCT_3yr;T9-y+o+xLaK0CD#zQ5J`S2aS zdn~=$J;(g@Q=YdAsXG@-Z09i^Tf{`)PI1f~KR2sW{gVA=&w3+&d5Y^_BH!M2^PNxQ zz9g)y`eM;%#@J0cm#&|qoIluN?ZvWxy+l)7FQ>0zR;Z5?*dEb#lkA#uYv5;s`eb%ZR@b%3rjcb+Iu+e6lcrC~N~BiGJUCB8jdGB@ z#azDapzXzu&^9al8^dAJLdf*vRM8ij`AcKN4w@* z4yDBF{s-2r4>|W{-|S@dYMa$-yjbS>%znMZ>HHQG`8Tlcfd{NPDF{y}!JpX4jc*7GmkEz8*TnSCNL z+E{PA$wttB&o7+h@Fs(k?7hBt0{TnD!#B_BH~Cv$8X~iYZ(Og~k zOJ=W~727te6MF^r*hiM7$0kpY7n!$%YxBbb}{w6oe zmS=AKAU}y!`&V+aWzWKg+`N2s{U|rj-Sc@7=jfZ4p{+>X*YkA$!OOszsb$N)z$^7H z&(a^Z)1UNEp%q*|b3dh=BRlit`oWsi$ci*&W8co!_lwnt&(aj#8(sj`L+gtUTL0o4 z{l+la^Qzxp@~O8K(feT;s^8Dl!SUqPUAHr}^E?0gY%ME;Hsoe~(GI?zwLfy~?U4v< zIXdawz2_M1n^kDHl^!3t)xWcm*;YDU?4@;E(R)13*gOI+=XSP}#>zgwcm>ABSpMle z-~RGE{}z+;n@8YW5e~1JZ@-=Sd8MB#)-mM?x!ewVZh53gekbO#+X7f#v(^6+%KtIQ z4sg84dc6j)o=K25nQu|_;vwNP8{k%VA>G;CrJ1K6F&+%{8`A)L4Z;@YZvqN@@ zm?gb2BD;15F|VlK*mxyd*yjl<*%Ez4yjsoqEPifh@o#7F;aM16woTh0*#uqg_^;WI zpYj%+8;fmc?zS(x<+Sy6moa^dbjY0YC1Y5&uw3a|=9bOZ9;XQQFuujb$d=7@mnC)c zD9RTuIm?$_wp^QR;atUgKBDr%%XBu}hrfmR=D`@2&l5%X&)W+-uD-#E*X};JXk@J&znsv*A(LVEFB-`Y@Y?Hy&Y?KvX&w0^>Ekip?xote9jicUW zi_~I2`t&~jO^zayVL3MuukQ1g^YP@f_ZQ~zt^@9w{e`&fd@kO(?7VAMKL=N`LCzhq zZ@b|hDe+65q1T=8`tC8@y@b;`^udK{WF40m-*}_bjoT(aEJI~G-5j5s`K!_?r*GM` zw;B`agmZ0{p@VnTebtsJD5@>sn;<$)vbUt)Q*mwfPBmVkL` zf%k8-VSeNp>Yw~0tk7v$zc$PIJb`e|o>dPSM8x~ zaX=?bd0CO|%XxawtNcp_!#9l~X$!V8ziSC!a>S>48{bxB-SWr(rX}oW?Jt#A zK29a)7GudW_{bHhK29fvIH}v|1X@CW(GHxK^r0afFB(F5(GWi5Ea!v^jo_cu?J2pI z*^jbTwrnkLXK>lh;cus)`!7yE=NH~7Go!~#_QJ`f=NP5?FZf1pr=p)uL_f*gv1P0M z>3scmg8Ipder78=6rs$KWX(}?3Qe={LwZ!Pg>_+ zWP%+Ox%*awXhy$rtPoqz`<6+tZn=Jh&e7j$q`lP2NVZ4HhxuBQsJv%%eYH8h*&Fv= znq}TwR_{BXij>tGH8a?j*fx9O{mafg{$gjIHp46141eCK+2;~*;e5jBz7uz--5(A@tiB0-t znVDgFB(ER7rhoUEZ+-fL{;_|D&+yZ)v*&BSUh=fxn^v)BL4B(|-ZDS)!>2QRm5tA+ z2#$uB7N0#&`cK)dz{cRS@~UO@k>TpURf}z?|7PlV$?7t*s>~c3eak=2{2KPly!n00 z`gK06)w`XqQx-b;fwjQAn_u!`q}ayEyBROOmbT8CyFQrY!}%Bdo1ZeY6v~oMzx197 zpEKX%n{LjGWlqifj!&~PBMzlguEQCT)pGWz8HF`DGZBf!ISq&WRHf>l?sz{{Fg;%? znC{l)7mndq-S91Q@_5Obyj3uLtzL5cgGyKD=1=)n&1AA}f2&?HNLM^QyoEnkHhtUS zc5BJ?CEN0wZSbE|yV|oaf6*7eRxf$WST((e^}QB&>~Pb2C?2!8?{4E|cWYmEH+k9J z>X+TEY`fbi|KRKSup9mq*ZxaBwjY_g@?sTdJK^<%e)#66`K{X3xy3Sm$q@4`lItVp zMmGE5^^*Cy|B{28b5w2?@*jOR|0Mo={E*k^r!QfzEzveV`d(Y2Z@%7nrq+!%AS-mH*POu7dNzB$>is-sT79RWJ@T zvd&nZqa&l$$793rzdK1rkSl`0%&fh8YovkDXq9$#B{z!<2II0~#aS(o?)m18D7i)f zqj%KqiDsc#Obr%G!(uQs1ckT6nCAptQ*>uo%llaywzA#^d=Cn84f zNc=MBW`%r8Ecwi`uo@jyrq?~2-%OmqJ5{@0Sr!6FuI~A>OJuN++`VJ#8nY_wfMvH( zAzXE9+1HjA1h%yqfgknbwECw|?;3U>b;T?vc`4w3KjN~+gOd{U9j6U>pQ{2fW= ztni(PTISB%z)xIlPQ-JTI-bJ4xk!)6L|(W7`r-;>bas2MCoxikqqAf0LR?XJPkc1~ z+7Ym~M8EFz4SQ?!YyN=fms9@zRTOM{XCzE9x7><^onSU^N5cFCxACtS6-(IqTBUXs zA#($3;zOcjyGGItF!dr9uo-$6`AkxR`i;fbarCkpV>!vUTRibxouPHDSDZs2rnln_)fySuz-QJ-7}qrkOC&(H zcw~Xh3bXY}Y(iJ;O(%@En(g-PseyhUU3>y93_plXXjF-|EY9MtD{w>41K#@0h`qY6 z5PMxzG%YN6S7>&`D{CSQyg0;U>Fjh=EMwez4e?hSMA>wgg&k_0&hlm&U!{3JLzCuH z3&9P#&1esAAM`XrzWOr`CwGE-a2H-3c3n8@w&0XL_iyy@!g}W6jVz1b0q(VWba!#D zlZ|~fd6Vv7Uw==y*H!W?cOKh`r&jrtX4_-?-C|zDt$s}-)|{&>ZaAs^-%l!2_KTkB z`6#=O*8AvCsxO-_yW#Yc3#Q) zIwLcp?Q_Yn(`80FT*L1%D-t-D$98wMIs)AXo6&onQI!^V{sMJZv(eYribDj&o9`7C zA>7MYR4I;)L)q+I7XW>&CDiv7H8sQQRS&08Zf>=izy({GAK+s(%Qt6u7s~D$D9YO3 zcQg8L-bGLfsi?WKSR?~`D!m-VnqP}#W&OzU3DpT!Afl<7IrAQaG;lv8)y=haKv+K@ zqU2=Xj$zb4sBQ-w@dsa@yBg%UCv`B;HX&Ec33jc{MhyS2{Or3v_8W8hLm&GU|Jq$( zT(9b(tgbV!_}Lb=X?j~{nV0-+e}Z%JAMblxj5%jE)+l-pfGd*{qbB5yTf{EXZ_;!7*YJfo+i9^HgxB~wL`9ne76Zg> z2E)1^bzO09@Pqvg4q^0%@7950w^n~r&E^NJVE&|sdbAk_YnF4pk)Pk*5V;@u&*37| zIbyK+z4^>>8lg#|#Da>rFlLu zkBO>V)u!^^x{nV$ZXYht(aks_xL}xd8fZ z>dFit6p26hr90WqjN60i_1Q!;+Ty1pU$snmYR9MC5%-XYu{6|qM7}9 zjgruV8{GE#cJ~Skj~Mt-ttojkW|x+5k}V{CDDo~w%&uZS*MXl6y)nft4Rxs%_lbFR zFL~x_7Z;4Kc({$Ds!zR(pU3=nT#>mZ8ffJCH;Sc?z?Wh0>a*@dt|SXn|69k? zFDFssXheWAAZGstS-;Br z?gv%OT9VKDXY4kYUFA+bi+y^~)&8W@fFJj!?79prH>BGu&QI(#0E`t`s*#&SSBNaO zbSGxSj_J(5&~0l`RU+6^W0(Es-`s7>m3|s#*O=(f*ubqmaj`_fkRwl{=dxZ#Hk%!+ z2Fy_-e$=W}S_gKl(KooWY0r9NsEXtvHa~O>wavotB$;SUV61cjb{tF3#z7^X=B2lo zb0&o!!rb#Tz=kdcHdII5cff>V|2GHm*B%d=b#JG4XG8CH|UTmx@bqB%?c#vnRec$`egQAjHc1mcvaAdDC%7G$C z*hQu4Hmifm2HjH~i3>)BPm$~(8LSDa4#UPm!cKQ-&t|*uZdhW$ zs}UWh_(o-J_f`4d7rdWcSE;%8Py`Z}y_Es`?0Ux&Q9K;Fdkgb#5Oz^iNpv}vV6f3H zz2}iLkM7*l7*2vU%3CKAR180k8PhEfj=iz&^`|Y$_)l z6pPh%(pzx@eMt7%C3}@K`r%^lvk|Jxopds};a>{Bawbmqkv}=|7IYUA-RsQiVzRxh z$C(+o+t;gz21=~uRcwYg!BLL&#ADD=<#kY3Jt+q2pjZa|fWb=poTL6b^PpM^{7+!8 z>ZDV2C0WlS9N5FG^9rJ@n?^shi)2IHo69^ zVA*@<9kCnCP)B|(mJrx5b1%3a_s4)L>^0}hHUg?Z1G0k5#G zaEf*mw`1(ncHd&uYj}f;+!_27^9eVayXX|}4V0F}NfDs334PHZ>+ZOViOBi7ay#cP zcr)cCqRju+{uPdN+nqjR+%dk^L8ByYBfD`Qz!{VR0p0v$*1>eNAAqIyAokPCJy zC&4!5ZghYe<%CCkLFrfy@!OzhPh|79-?T^@ZFYobi?NtOe_m@oL73- z>IgmR@z`~Q*1aCjIzqpp*JER+LQg4bUWs+U1gQBf{eU~-0;NlsoUC_e?2~~}rKohN zAMiu0Ek0RwD|Xgf1|E>B5?FBDy6>XqPzy#)agf#c)yMWmkHuZ}zCl)~SaewJr${{& z8Tm?G@>Kss-n||545IKi#XFAG>V&(_b|Lgh6n3BNuqmU+L1F)_K8c&`k(hULQ=N=e zu3?yCSQXySLPL%4ew*4+-3!sfE^0vF$PBFcpJP8ljJUH*mBx*QDaXo*NsSa9>T8jBcrIyYQ*rg2Y@woL9yKhlHSG0 zp{QEAj<+iqC;%OOMqO#;Q^F0#JGA~sIhA>kQ*n(YG)JRn8`XrYpgAJ#RMf^_bPm}- z|FWI659;Sut$UY&?iD?0y{a$SU*LIM`%IwO5q`D%DhwH#b>wCS^uTQBS>y&E@it!Y zcnP-86*|yuPyvs+c7pn3j=qd4K37A(ZYLe<49s1QA9CE#8y!H64Braax^07-pLJ}~ ztBXP!us&V;tX|cpTuVavX1B@8&1FI!u$lw$Z|uS6OoRHB0QxzQ)xq}hL^iTl(VJk` zuPjr}`=EZFA!4)B3D{3EguB8RG7fr|Oh;CR@U*v$MsIPS8>jr^1)?&pMaKoEP)Eck zBOAyV=h$*J)tfUU#<>a$$J7FI-|~< zi?@{hXjeQXsVg@`K18~i{eTH>Rlc=TQ4-LH`Gy`vM*L;{o$Ov``QfX<3!(dWvDB=C zoU#=~nGAkztna{xwB~ql1NM5aP+>aqqATXv7TudptGZ0zSv!L`(X4b)+g^x zUu8YTI*O2Cte3JUv`}nrp;~08bkPcSBHM=x>7I@GWmxLG(m4Y0?po9s`DSu#ZUKx9 z)MJP`#5c0n=?${1iD672x-jB4!UQD>L3d@NbG7R)5m@h`Q5;;o(#}aL1F(k#n>JP`2bz{2>xu%aJg%I3V8t4`Zu}tPV%nlHWJpo@B3QST8JV6eFt*5 ziK_j=krHjaBNx>y5$D-1iOqViSiAtpa<6whUU8HnE@e;&E>HlgQ5n7rD#C+eC|X2R zq_@b!z#`v`j8kmY;590utHO7Qa;otT}T{I%bBjzE?BHwJjf?=-I8TAtuX5}tR zuZ}7!%!=rn68b2!s1(= zqez`IyJC*IB&Beb|>(C3UQbB1w1zNObe959d#w;S=l75ZjR2B!>n0NM$8>J9&q z_+yrNQmjZKBexn@Y0Rl`4V&(t>I`4L? zwYO*Nah7kY(W>m5x&i;zH0I!{x*8)T5G$-PLa(CQH*zp$^*BbeozWS{kLz^j-KZj; zWgKzDh?z5qOYaL;)ByOzRd-wQ!#U?+EQxkT6~KIwF~piToiVhk0=&^Rcmt*+Kyy`A zI9)})+jVb3qP?;VIS}S7}+^PT_9tJQ&DRz z(ZGl<5E$B87)7XcWO|0&w-J%fQ699Dm&QDYm_d=F0X8~jp{pH$mJ_tbW}r26#&8>; zHRkoI3d_9^m-Htdp^j6!3$8_NL!*Sp6Uhd9xyv*nzyAi@{=OBD zUyWPpE)?1$v=Cz>mhgzKDexz>xkL^)t{SRt4@dZ(8cl^7fYG>quZUc*wlLw~Nfn^GSu;Rd*t* zszPy>S6kh~sld}<^_3meKz6+k(XzSIec7s{aDBho%1IV3Dkw(x03cK9$v|9s0KRdJ zB);Mrk2g9hGZxiL&ob9kI>@Zf1~-{&{A3{gQs*1GzKaTamU4fENA=3O(OpO86KvEb zrMlYZ0hm-uyhPTPN2$}hs2)f0RKMEXQwDUgjzf2~k_H+CIUT!L^9DK>aZYGOXlxU* zk{XODArAweRcnwsp+bj>FNGA-E$72qye*F)nJJCLrClM?22=R z$v2#@9wBx3MaN?&w2pZuK?C0!)t5Lle)6&KMa5FH@i8cwS3Bq855gx7Sr3McYR}%~ zQWQ>vk}fVjks2;HW70TBUPikdOg7ndryP$$-=FKj{V9hpJ%~&%TcobQ z;3{64-Ek4>xgT5eiF5%YP+`)0l9rCy{~T%A{0n=|IIJ=?!U)?Q96rnVv*(rr+lCXp zHNu8fXUEXq7y~CR0sEsQ{@sjj5c*G+Va@JR2gZ2*D11Bo4WqL-e7i3gSyQgwVF!g? z)|0RcIdRXEk=ePg;?c9JvyZY9nkdAxk|NqQ#oT;?@}0S9D{17rLbz)GW8`c|sYad-<8`?)wO#;{poF^Ct@!Sbvky ztJ)-I$M!?_2(r9Rl}xIXLoc-(8*@WV^Dp%N?>IGNvdIi>fqJI1=u1JWOZ}_>xV7kYSN;&atc8z=LfmpXi}oBtHBQFUfJH0qtrBV>ve& z%x(8aWyge$U4X_vg>ljjR-rT<15=zb=^I5@{+Kc)8Yw$ISILVrw9jZ7^j;1Otfe~_ z)GIyv&XAEer%=VxmJ&J*le8R*NO9^qu|onRK`8JAvOir9K5nCadjTI8#Gmc4% z!u-kYNi{)I9nJIV@6r~g9i2pDe6mMdWIP~Xxj#8aZp#zd*^bm)pA4!X8kzQ-PQ(C@ z+k~?e;qtz5iU4w}uhpg)kdXI5sXby?0OPXA@k7nXYF%uVu;X5-HKJSqo2lMk+z_95A|8%(z1#&@@3f|ZiU2&>gBQ%Z&sh%$5x;%%L;Cmvp-1i(!igzwu z%G>Ps$+TrZ@o`&&qucDj_*++cp^9d7aFxNt>Qs)Q!Q9yDgss7_n-$2mqaDb>1ko`# z#$ctb_tu}Y#cFDO$u$5s{X66c6F&lFKOA~Z#svO&d}+>^m>!v>zoL?hH+TSLbMX?q zb$6u~0AEXeM7<~ZKD1;Bc+?c%AYPL~Qa(bR-wrttVss~Lktt?uu>iGQlx33j-uoGT z2jJOaF8=ODzNdnJQMv}rgJsJv)p`(~Zi}j%aa&H-iRy}iW~~ZpDIT`NW3leTWuC+0 zaohB9G2rFlhkinqK#vM_ed7qw>5(TCrA|Q zA$&8_@d1q3;%~jC8miu#5DvQ2s*eyh+Y~Lk4Gn|vfE;v1`*RFJBOup_F+UO7uc!?ed|j7pYUO`rY+ZebeAph;=gvK70jpQRCsBj z)%_uf7W6syi~75n=XmjQB^dZ)GL}LYyXgEPJ2u6oV&DF9Tzr0?FN@YJzs4?8+=o|x z#_oq%GjWV14^hM>iF|q{Lt3lfs~Az4<(O>cy$Jb)Q4th~i5Ogw$7^F5F%Hd6W;J=FSlBx8?q}^=+A1{84%8!0cN`? zd=%e8m`2~6-?~628ta;-VQ-nWjDGdBvt6S!R8VG>ov`rAmVa8vm!O^lVSCGQW$q_j z$ZR1)$@yYxwfS!q@yNN1_s^7j@kT;c6T|!_$mq>8)-LJIrZwhUk(BPkh7CaxbCW-H zSB8t~Q$4A+pI3?F`SrI#KylG85_oYvcUwzbSIPiVP(7{fh-@Rh;}+@DXl|gg)w;B< zK7eM%%!heLH^m#=?2Lwa{|>WOEMQ2f zsD}#;XNRikeJh}*W6u44)J8B)8`iA_BGXS9NNyzziKMR^!r8jtz26xc{NQaTQVz|xCMcFQ(If& ziJqw@Hips>cKF8YZ6SHFaazZB0>nDI))cRFoXQJUt!I_&(2&0)m6zS zpVgXwUyaFza%$>n-aBeDb%BHJFPo%iM*`LA8GoYG_wsvvc?R$k&_K#s+U(7vrOnK< zBUYsx^)$LI=7voN=5#zLoZ$PLhnRzciUg`m_-%SW7W+k)2~jO^N?bumr=x^Uj*&W% zfba1tEOnVnBlvutr-Bm(kf%Sy}eO zgI_BtUai@BV8>|8{@y6sZGEjK!)La@zM3Qd4bV`#7|-(zc%9nQ=$qn9v1q}(pDHfg znF{YeP|`S`l%N@&L<)f~CN{KV%n_L!=UF%O{}(N0`8E9Ni^JHBjBK-DqF{!80z~AUHmob+auSwBJHH@S~nNpIBe?O7qI8z9#csv z<`i4mZ|dA9pcrAzGaiXYisB~V#seRG)F&;OQVF~o59=LR+J)y>hGw^#de5_vd#-dn zyt+3MGVcF8wOFEo*N~^hq5|v#XFbEHe{s8+i6=LYzuCB)Sb#)FD{>%Pqss?bvj5^Q z=>67PqI=BGM{_{u8L5>M;tIy?dsF_qS;Gn)i>{o^AZUzzuIGL0$G-f$+Yd5`_wzA) zGO6Z`GV{442SL#GcvY_NA!-0M8a@-;^jUdt&1W)}W*aN_1jJ2} zV3+!(WVRmowa7_cK5^nCyZpQM6WkMYd^p@pl2%(e^{KH3s^1*JU@;+(+H62X!_2(s z6N3m^BUe=Ajpnz5UxdF&mk)|zEr}3_Qo;mamM|vg*VY#0`-#-BWU2k+P%2gR-Me>7 z`NRa^k_w%mzLv_m#SEyRkBS-1A{`9I{sd>S!x-Sx2@8o5dX9zEnVEg?+`_Y$p~l&U zl;~MoA-i!HXMzg_Q92IB7@Y#H!UnOTqYs~Gu~-ywlbc}7TE1+((j=^f%`8YXImURD`?=rxJ-jh&a=d$Ta_Q1e(VYFj-YAAq>31nd5U7mN7*Sbw9#<(fGb9U z8;7k8N`Ox#ksmp}&x@&5FNrzilS9I>cC_l`+QQM;Y-2evaz)GSt(NV+l`wojY_6hUiA}xr z0_K2M=MN?Lf6|8&`%^i2i?2(_-f0f9Z1g+yPk>blJN@3q1v`n^<;i%JSzl>GyDlaK z$4O{QmSQx6g8)aT7RymT1qKL>+w&Z|c{f*!f#u{8Y<>4zfvo zgmQ&q4f|S>Lymm>r?LRbao;w)etnX@W3XUGZjD*f?UiVeh zMQ)f%57lKKWs|A(jt}GK1bx5xWO(<~ykZW9x?ycj`?63S)FAfDapdKJwYs)n=%ecY zO17o@W=)v958|#KIeeYWX|Pyd<{ENlz<1}{POvU|iRV3e$K5wco77cla~_BzvU`Wp zgf_)B0x>2=Ywv9EpZ^+;uWm!m6$joNw>#mW<)GHy?-0$jl?eNWMRy(>TnLj6K5@o` z*LnJp<5r7)Itom$XTL-~68Z);iVYw1na4>UxhWO?xwmp4O;Nn{-IC-p&F~i!;qtPH4Acp8k&FlJ{5^t*v!gd(L5bzYAJ z)Va@&_P$uCki=nt<%YSK|HtE4V_x?!1WT!X{ zw=rcn*HJTX-xVmb3v^F!^@;E`S?9|m4h^CYobpwj>+rEF8vB$N?y{Mi_1f7*c*Jkj z`pMR!6!DZ6`EX-ryML#^D?1lV+8`=I!~@+s1M}A%nV-31G(|j(xzf-p8KE;7fo!n8 z=Cu#7t$?O;PQv=t4@*(X3CkHF6dUm^*NK$R$?;h}#ln}M0cLV1q2=&fMd8Cb`=jTY zyQm_dvk&|rSt4|uUT;59J)}s;Xy4TWAy8y#qEPzFVQ}^R!ngHhg7Qnldf)9fk0thw z&DdOMvG^3szQ$alZW^WWJ$O-ZV9FwJl2#>K?~*TT!2B%aTd4Vb?|}e;lpe!;FQH1~ zS$u+}Rn>GiegZ`WZg6)~{fNGUH#TsU`>7##6NMTP?M~a;)B3*C<42f>6h@w-w_4N8 zgAZ24)(I0|+=%btNyV8s^)Fsa$Z84avvQv0q=q7Q`yUaVH}xTJf~nbZAwn6Y!EoR- zH6HKArbPuRU5AYW2ba3TU^^_^6|W+WE``$ki0jr{l%*{R78`BmbT?}F?BCCiND)^- zzfE?KDI@L;BYM!mmkrt64Po>1pFbDNBHHbK8(P`pgqk!Ns3;4E%F$g(L8@zAM6j_R z>*d$TAxADM6~&U~XiVJumF+V%pxf(jccdDVzvS5KjOLVL)2efpwBO{iQY#u&tx#!N zM-LCChTqAdlU}r@{zBMwR+eKs@$GKw3vAUwThP@q9OKc(q>h2^$D95?T(2OhHQFll z5#4c7o|VueVu-92!4kS`C7^nMXATR%eg3_-N)(XDt;SSMp{+sKV-q1?D_qc(v~QvE zJsz&sd&7jW{TtBE# zSZmUdF0%F3)wTb~#$f`$&kTgBmRGPthIMAo}$O3GCj{9Eo~ zVUGOLt1?(wGnOoqg_y!=u#@;1r9@Q@5ZMNRCPxcWGt&)j4f|o^)DAWC;{f+h3jD)9 zDS#nSY#TZ~2sM_YfFmzHf5VKzmO~v2>V1d8!ok;i2G_*l(+D~Z_mp7%KQ4l^Q_?zC z2s`)TVhZ->=oBQE{?CqiPR8sIiYnsDF1=_<%x`v~kI8p_Q6;U8)R+i^4Vs_+41t}y zDeTn=gMY!XE4!*8-Hp#g0&YI_(ehC9Q-W7(v}xChG-j<-pQqFFXIy4&vRsWm>+6i# zMWvFD6gujWq1&<}{=<;ybe@<|za!L{YLOn3^}t+fq7mCic! zcQF`vu^2ce3{@Mth8QKxAuA_Te$npO1!z&t#cM)Q4JZ$X@W~u=AJ!GzoAwlr?cY9f zuicq9Ye7U5$Tk@w)wF1z0QOZL9Kbw>R8B^z>fiJ=`EH?9OLD_{C@d4%QIe>YR9Si8#%fuCDape_7? zh2CG?=Ym>>r^CSRijtT@#?^-?RZksVu`oo*N)08d89q!DR@+rep+rCYMY*=7jxN`B zXba!GU&kZ4sSGB$1vzUeuL;4sfacTt>hzL4XxOWa&F(Uz z%pZpYcB;3BoXq||avRqheiJ7)I61u9o?Trk+3ws1;fz}xFIc*xLWot`np=!UdNdpB&S2ki(S z@a==8_`Dy_?Nd$AigWdzv1tio!vHu^{Lgg^ZeEPizwlME6AOl;zpR2a7seeT%xN(s z2dxa4+($vESwY(s`$_&FUU(9g%RA6KF1Intjpn;FFo(JH94~w(GQIF&gomdEkt5}s!zf% zuKVfud;+r_m65@5w~toCTwz9SK37V}3I$ofa`L z`QkpRDQT%1{1kfm5YTgHxl_LG)4K(GqmUogj<}@moT*yTsgc(w!ff`VpnR9Fu4--H zt;=y7y*$@#j__GZa~Dn0$!xw4jt&<6`kJb#z$DLL;^f$ahdx^|fd{frS&9#F%h1^m z0B{(`tETk_bD8fR*fpwsrh}PW^`YLpq9|6t8Gwd6yeh4^+YH= z_dyM?pW@QscBA9ZcBMl33*Re8D|xmtmCH8si5mM}Hm8~M3#9dffsf3F=2STTLY+`1 zoMxQK7CV=-(E;AIx4oa>pB?KG_yp%fp8;9}?9R$%t2p&Rh|5t0_XJ&AJ{E$WMedz6cQAP) zJZt2$1^rHxo6)X^d!u9w(!4|bl2xN&-hElD^jyL)CF?}=TRwl^G8Yc6jnGI!F`m&=yi#tz3t$EBb6b|QlhENW^zwckl_f>Nqs z_ZG6MJ?}7IL=KdTD??E4@VKb-k`TXnMz%g)5;Vtkjp}A-%~w73#770QG^!T@HnQg_ z6zsDmwM)#Mb>Jz={Y--#e`3q(#Yqs#487u(NO$#&QsaH6#Bf(=j`h9_$=4r0@dMX5 z*4$Yw36qqrMlmD50BBIeOz4B= zSrepJjKJXvKNLD2w!(A;>HZi*k@vm8Fc+8U!!%|F3(H07@RMn4;}SPYDq=1`7_;Xn zsHNQfOn&#Y6O>qQX|Exts}BJrEd7r!w+;2fhE|HQ2a?0VX=&UN|Da+LYm&n=(s7Qf zOLzUI;B0Y588sNPU6<-k4q3NzAp}tU5R_8ftYsglS*RE=M=x3;xP(i`j)FLvWmNa_ zP0p5|`t&;EBx#TynMo0I`vabnTo#CKgV2>OG5Kd9 ztiu_eHD@LR!_5WYE9aaY^ZGppvLZKs8(kbIW~zoz9+ zJclXwzk2XRojFXk%t+AQFv$yel*@<=KDv`ZPVCwTa~-{1WG}m8gU{;eo3t>nsIwN4 zBC-77+A{}KCOo|pIDaHn>I_p&OcbA<=g+v5x+Y_}_0U-|;k&l~O22vKsB+#mez~ie zh@B@C5pd2rYk74~!O2rQ5y?LV_@Yz0o5-WWvPs+nxPGammAG+i%Rvo8vY~e!YR;v7 zoSV2Lc2$C<5Necq9kI6ip$%;!lTPBXxP@0A;;^^@xB2wfaK#&!mbb#N#O6uF0$;>6 zBtaz7xcz3(+-~8jZztMJ$#{sUEm)O0<$oEov;FijxJ3dsjQ}Bw{`Lc(UkHYq<@t9f zhxzLjAB)*$mh+gCNO)vWU!g|>TDmA#oY@UYP%8#>Awm(hehdT4#~0NKxKXutPg`>+ zK2h!QMZb+P&E3&hEG%#^@z7*ygV=Nn>zVhWcSNE3vK3Dmuh&+{S7_a=Lctvfu000D zbsk2ql|b?&(d;1X>|qZVHV3{G(xS=$VC%Gu!VY&Ok<;9aw|68ASa>)~-Oajs%CkdMCqT9#b$yZ7vkR5ftLn!K zogjRWpMDT&39p;-K%l4;xDlBV$j7E>9WtS!t)BXwVre!PU`Jfx&%1x0;u0Lz&ArZd zRwIjQ5x)TY-#a=EXrO#fDsEgnx>`3{lKX1X$#RH7or}dr01xw>wo08e$I9y1G9< zbztL@wnTR@G#qw40=H5`85ut3k7L7MI1LZ8KDgeo!Y+iVS9+LqcK6l))F`()Kjfg; zcUe+4zMM*b2szLq5HuZNYeCGu{PuSQ{u%*2{tqrlP&PP?7J!nQef6}cxE=%jvR3-A zgmi6^WOzJ^c>?p3MFN49GOybsj@nLMk+2Ehh)&=l15S~=ve+DC@^vDm@z?MmL2X|S zc|_}~!dA;av_GUOog$<}hB~H`PW)M*m;O-JdWMf;SAN9Ebx0Yme=p%4XRfQ&;KJ1h zN14hCv}JY0c3&4h> zu;zIUn2oKyOvqAi@&kFV);6E;`{Cmut3lsov7dq zv~L*rcsS3s9ni^7`69*@5g^qib=Bv4m-j^2C3Y3%3GzM7+ZSk>Ivz=VCw#xy^4`-` zyxVFH1X=Sxg<8t7%>bbS7x@$+OU>^Ks{ZGzDEI7=zIcBHo^Osi1lcCBw!KgPEmMY+bfB*4E;5q!?Mf+p_;XhV6i@K+#5H1|YJ0f4a0+G(G*$(uB`nO;<-YCg}pT#1)dcJIuit*`}2pb6iwSI2o*O- zHve}BrgYYS9aR3uU(5HZil_hCd(SIk_l8L*@C`Ik@b-Wr^zODokX)fu8dMFuJ-fCx z88iy*(sUa=g{TQ$lkX}Co3*h}Y z&``ziYnBwVvu0pL1jO!|4N-zJg@w$`YwDX1y^bBrxndE$A^dSn?8j1EShrrd#nAM;g%HtWno~b)5y!kbd2~uJY5lJ3W2}Ye)VmqQ^0TTP5^&v@aS54 z?7n>o&L$d%de8r~jVqMk+Zyz?>%Rip5Y^AQhXCpY^^JS|AeXLl*_PzHy-0#5D0`v- zX-SX6ah%7x_D}oMM1gOQ;E{rdcN(9k38sP`NP?o!%X-H2_ABuDqkQ;w6EQu)7cmUE zo&Lz!J7oU-^~N4!!{zCM%S5j;D`7kARHFT?oLzs){QKt0`l67Uc%zaF>Ly!Rj-yE|!}l*$s-dAN(ccR+Wp8 zcC2datLuPuBtW-}pb5ifrD?IL#BV+jnc z&srZ_o#KRDvw*K>vfsUu$pXGh82^FtWT~#zLZz8i{SQ{w?O|GDnuWio9dJErvg~^z z458PB=hdLGo4xkX*qCtEezuie3Tk-sWE$U; z*}U|hZ7}#S@h?)uuVd{{9iLqGzi+R~`pPq{(4A2Ed=}0}cleNGvY?;S$FPfDA7FWy zvX2W>{&oAyo@oF2yYG%)M)N4?9%dbA;WGWPR^v08li=ReDyI^<6QJlY2KghgV5*2` z`-sm8zS-*-)cs1QuwSm-z^@t1N6Av3(Vnh!iI13+dJZ{s_Zw_C=E-+00c7?J;*9#; zH=~{>zCb67|LG_Bj#c(3WRIB**z$umPSIW*h^RhKI8Xp*{KqL5A&)l&<1=% zB>hUT)HmD}`3dXk&Kb|=(BZ(zHdpGWIHv!$x#~TmXS}z<;QACdW57up5Nqm{AOow& zqEu(}nKt^ej|A14>}^@#NtZQp3zA@Iwx=E7xq_TdSnVFq8NOTgJH-p%yVd@t^b)~Ec+c>`$|5gg`KLFe>=Ao(TrdhN#+ ztVYr&AT-2o*13?LfVi=2L0`prwiR<-2fuDXN(bnkDS1Yna0)7+Pm$$$%A<3< z3P;`7XGba*z7%QUdU^70Msa|YM3}3I^u;jgOPy}G?`^zBC-#~4eg*i9_sQ(yGXg~Y zL|*M+T+gw$|6!`96~S-#?K#(;-M1W1z-7bj_+OO^Lmbk0@J&1U*K4WXvA4XE?FK44 zF$*Epy~}w0FTDws5R;U`=lvjDuJsMz+r$~UL9lH%q{L7x1MMjv`WEs`V0XfqHo;kk zX7>}Kgqy#0HY9GfiI)>@#PL?1YWXi5CV=zg)Y-|h{Xf%e-*?J5k-ZR_1!uo@oTuyG z?V(uv+ng!fGGaBDEmC8kuLFI*2wn2qQ{FR1+f0{Jlb~hdzbf^j(M8LAJyCwwg7f6} z^~BiS$#V$8EqXWXMo)P~+RS-Vle{FuIcPC4km`;<)Be0%R~h}}_vx?2P2iWAG4nJTwQFfNuXj#z(=P@oax|cJ zHl?{`2IVXzum)auQHeoHc9rDHTv5QCgSEi*2luoB0)~_=n?e>OltlofR`f!)lz5QW^OW%EE3mbIUB%+IW(Lx$#M4PF{yrqB zw(7wPrQyGm4>NkjyOfBf)J@?0N2Vh{d+d5}d%<~3j+c=18KyuSmRXRZ@g4Ol+EEwM zEC+abSl8v)7Gx^qW>^TF)YE=Iif!j69p>QxERz|39Oe}O!$8*FIho+oX)N9dO!Cf1 zTU@ss+xcLvG3`nu)Ps@! z+}!r^F2^^R+aekDdRO8A*HvjU6{1ZJ-+$?fcJfz^x?e=iaNPFn|EBePX9_b@FvcPs zF6G$eNeHD$DGMJE_Gv)zkY`D7wGu#C8QQr_YY=PP*==cf%}=3 zbS*S&Jz-azbeK?(-SuM+E@G+i{h4+H8f+jkQ`zk|Jhx-pIwryeh17$B1J`j#tU4;0 zgQglaGQhP4+m@A1*3tC1OH8}OG^|5_A7zVjjEO8H=wxP$Y4Rd%Ud(m3P;s^L{5j*y zOjR$#$5}y+%srhuw6mj+Q7s(6NK%0~n1#aC4F^nyT_koRn;+yD<0qW0K8X# zLjO(?Q0z=+FgXmCe1+`bYFjPGHPyf5wx$ngy=UW9WM@)1@09ns%f^S+ouYv zGfi8E9qB|FaBkk?DO$}jmH8AoAD|$-F1ZDLzw}Fa&B%YgygRRat$6jd@0_Jr3mg$n zllQSAji*qiY2iDuqSc*(BV+~5UF*_zy$g3@cVyQqnXz2Ydei*E%)k#g=HvqcjkU?% ziE|P)h-E^?xfP|&K=5>`e${sK{RiB_lekTGK7 zdb8w4&peZMqVD>lVbG1g4;S37ntp&KTvAj=H?a_(3^>IW3_)(HT*W(*ey z+8$7@JyoLtSW4mGA`J(5J@(>ye=Ceyk*k=E>{L|@IBCgT(G%U{fUXsXX>)5RGEaSB z7wGS=%>`)wl#fM$=2{Clcbo)uY}T4SF{SYLql;V4D){s-6- zT9NvL=mI-p#d`oWyxaNivQXw}`Z>{)^=w7zy;2m!z1y!%`Z9F%)WI1@w%e3UvDQ#k zt_)nne2IX!$j|xh6*v0X8AYE%;PmG(zPKCkB~e{I*Usc>ZyOAD68Y2=`-R<;lvLTv zZa96(F zR;|uSO7)G^=elk*o&&%NN4+o1PFVfr4z3yzukZbuU7L{3pPuc8^!!Cu0jXyxoAf7*PCp*o_U$A zxObN^xE8U80|1BTx%Xqu-9*|O%r1+SF&yShgE(ZFw|3n*=~8V(ocweR4)y@E2VrG! z#}#u?s{0r+*h%hpT}pcE_ya`>*en`B@>kLwZmYGKoXNwXf|c_ZzU}#BZ%Z(J?E{`Y zDn;|Jmn!@;R)OR$it!sf}nS_^o$nA`;voV8E6pQ9du z%1!IH%K$7}I>Q794$>9Xe8KWBdVl^~CMra&e5&Slp-ddO))7m84w}BF_T!bPv&&i6 zH|_e9uy}g;Cx@F)4&@i;yMRN9HWQFVSodbIMVMDA2;?8|Ll_vNn3V;6+8e1S3}-mf zHQow3e~-nXYXRY_$!+{ptCpM8dyr!aOkAMr4=5h9)qFosadXea*E^VOWwfGNzU9w~ zFYlvR_*3JWI)-PptGz8l$hGpIhP_2?>sGcrihR6G zy+`%TT}uKbXV4Pq9FuItOr+D`{76XpHYLg+_?CVYo&h22Th88oW8$CBG5239T@-F5Z zD=ye%vVZ&lMIUEd=}B_;%#%7yau+=OD(*13VtwnriLF#sch;&Y0M*dnrdV@6If}-zQuyQAHuF*$r94&c6fAE z5TeU%64}%PEU@H`efJTpWEEJ{S~CN1NtnsU4g0d+dIE87i(Gszn4+wBzDx(0a>wr) zzh^EU=7$|3HGWsPCjXmrVUFVF+jW_iy7FoA{+GExPvt|AAWQwgtu9Dc4U`(V%qbtd zT;jOdY=LNo>@MOJPLU2XK40&hswd%;DU9PT!a-N`Zu%{$ry$q(`McfQ$=mV;z)cV$ z@D8cqrYoP}5J}a$M~zIkdR9}_YA$JUn!VZwLJVf1sd79H$(}2K{zX>tG<}bPNuMy!02|S3>VKmAyxU4xVy*V_qb*VmoVV`f{6Mzc*L2wc2gO=}d1$(#D zR_$hj_F@mjX8tnMxh2{0uv`O^?sPQG|Erxh#^n8HtQ=_WER?0U0Qz1^1>vHEu-Q?m z*i;m-Xb%02+vxd&aOgh@CM`UrTE&~?(4!_K`H2iCl#yF`bH#@Q9IRjn0<4`ML@qEC zK)FB(NF5`^KB<6dR6zY97Q3V)r^iOyBXY)TFrysnXu)$|p1Kjp9m|(zUdzWPT>CZ) z-G=slDFsV)+0@}D?EsH!g;=H=&ztVcw%;eJ@`lB_z?>X~6NRmhpr3>FR&eHEmVHXE zNz08bY`-9B_RW*J0G_k7&7(pIM7%X>^J#(^KhfLhz-N4+BUwkbYl-ycHT}RX*ee4QUg4hmNp8|bM9R(mjyF^aC5&oI;idSw#J}TBk*Z4RB5bN5M!~yPDhuu#e-g9Dohh<+N4g zc`?bYo&sXiyGaPc#ns#1(FtJ7An86}=^Uh^ zQ^9A~Tykk@C5*r3e`7fn`(=E`T~NYlo~;-7dYseTY1i*my*E2>6uq#dx;QdTcb)rA z9Y_s0b9Bje%)uaTRD#B2g`>Oh@a4KM=LJDF3U8mA1GNAeSSx7N^jwov^Kotagw*}O z!$>YtIO5YK{N*HkT}b$tTrnTo1W@u^J5Nz;;4WOVvON{sj*2|KFgg*n`I0twS{VGq zSB5M?Cc>$e(Wu(CO=XsPZXN$aCO7Fm0f6BFndIl^YNTqDjbSA%j+C~=LESoNGk4x@ znN9|4esxu?=w-51$EfWovo)P(e#Y4|NmivfImU!n&UcBG?wl9B;QVjK^B7Ai-GQep zI?g=hdk#`=A5|_nhS?llw0FYq?T;F$I$7}Zp`A}0Uf67pIvzu%d@fPoUo}ih9{P^_ zHJf$m?|r+s|BsM6KC#a`ZoSO5zuZ3?vV0%(pLX1TUax;s^QreIUu7Tickm+ZD=TaD~g?E2m5-9|+je?<5XS9#eZD2}hD zp`+U<)DAQ6i2~0|tWeg*bPcj6c@0J~*#IXv!XGSF4UX+s^Bk6>47Tru3VfWsT1u-j zcZtEJS{n$s%@Kji@e*d{Jn21l{S%Qo=T~&6Ye-IrPfAGKe)?!+Y$k(I>l;sP@kftY z4d?;z#ET=JxVtgk;^nT-RjGV=t2zQURv_<98dc<@>-~_XbqbUD~96_WKgW&1|0&h^TG&Z}*2cuZ1tAgqzA% z^$$%j=fDq`pVlsB!8914ZA@C!S@Yo7(LrW#E{$s2uQOPCsKW}M<#3m>E=nq^pNIt*d_E7Ck($+As1P#CQY)0`gKcjIo>acz7@?f#K zeP3ie$DsT_@llyu7nDjrjk@E&GiBm=dx5bEd1WBgwY2J-co$gP4pZE|f08^)WW|DK z>bsEbPOvVc+mMszR;MR|8wyw1;Ad(7@ShHQ0@)0U+n~fYX;=hne&uPXT@o0T6{@vb z@Q%&0gkvEv8p$f5xech^s@`krtC1sPLdzqH=uGTO`WBn}7j2z39k_GXBOyju631Uz zwzXNG>1}GuNk4vJ4%N#wrZ>2KhzQZkz~k8kRwVNRnx-B8m%-Pm9qpZ-|DWvX#ZH%x zE~xSR2%kA|m$o0Sxq3kigzm#)AyH88xhpK^++NsN+yOl}~NzX$bk+4l8_BUQsP& zUY0hjDiicH*5gnaU~E;Ke*BKW!H?wx2#4E^A~=h;J7MjCjt_*9 z2R}f*%%P54>6sWucd$;->av?mgOnk(m*6wiv*w%>H3cHb@hca|?)`$`Qm@-hyEuG$ zc-xIWlHF_`LF}MGFgsqmgvijvRZ=M%nXbueJzqV2e78)g#(=Spt_>ae&vT-49+dL< zuY?DVRPgZL=J#PLARlk^8j?GMs_)z$b}poE;P(Y{X<$5Lxt;S&ePlLz+m(T^OZ!>F zVU1p|L!`FPc5^REXAE{8$??I0yZu)}fxzR)XQfD(IRZRheFZ#rs3!F6*|sNE-1T|Y z7?McUfK1qCXoIyxzXRRz!JT`hVB(Fum3?=1O9tw|Oxr|q^eKPZbf=+D0b#|^EWg`nuW$M%WOq< zYJ^^yDSM0W5i%*RCM`C8x8YeuMX2)jV+k+@?m2k|blN}&pBB>L;02t?t$hKa35Quk zo=NBm86foPA8JnB)x2geVD!5%ErH+4=X$$6+qFX3os zPki6VALyG&$>VnhPl^G1A^7aNyK2B02J3$=uenz&&G-V@G%9F%7gnJOzqaVmKmOYO zXV!oK;{5cM44UPe-m&av?V&~B6#ZPHz*I_uGsK`hq9!3jTL5))oP|W+f|GJ-meqJ&oAE?T(o=#GYQsouvuP zi8okS9Wu|eprn7xBsBs|91obhfMgnwKvd|yhClHZ-vk$ z#mm<(^1|IgRRe<#Yp%SI0i3Du-?{BtbG!R@;LdRXG8Dev|1GEewU9OQfA{fEob{%k z4ibtVmipvFv-jej%-JSv zDp4oUn>!ZUmaH7C-7@xdvu#;Qo~*z2Q)1V)uxq1#0zXr4o1?nfuT+Fp)7Q9DhQ`8) zr3R4{rcSSIXUZ$!B!bez7}@-$O;#0!#OV>`iw~PT`HM&NaJP<3JyYB!Yd((h7b|_v zOy4nyS>Mf6WiFGBdqNNmKT{on8v>)1W2dhJE6@B?kKEa#lzK9P4`;&r@wsL6Sclg` zKP#0i)=nOm?!Li*f1kv%xD&Xw1hKuyA1AjYj$$IPv^;&DJKguI{oX}cK@hAK zl0I`)MUr;nQ#tqh)^cS6At(D0txx*J0oRw#43`a>jC7lz_LI)9$4i|2Ywm>^2KKf0 z>_W_n7c9`EWff2hS%LA0)MWJJWJa@f!QloTC4SLj41L4Yi$8!9NWp&Ml7S>EPGF0W zY&G>~HIW-ogTQP=`ff<-e33g@B8?-@OusY|-%0No_P>#7&<+}ZLG0^w zP(k|%`{g=q175-8h#uETdqhZT-*BC+rnhzF)tGYFQntk~`eBLv3mzkX z<2;UQaO?WM+zyCqNof8^5Rdz^u)1FSz)!B@?>brHjplWHQ2SrRH9jS0@gV~zFXaYq z6R3fbQ#A7S%>l9T16BsvhX)?bTgxJA_ry9MwWU$O$PA(RdzIDKVR&ELO;e!1RJfgP ziOZ^jloqw;z%7DkEM_BD-Um8nGF<)j_U!K8urPeGzr7ZDuu77T87NX-s;F~U@zI_O zYlajV=W#m|vyaiB5rr?7xy)rjqh2~wNf2fuv8D-eee^La3u{?Qr4hO2Pc!aa>&)JP zw9pVjJ<(Yviatgb3IU>!!08>vREo~LHFp?E2GicQOUF8Oyk_0pdnRKib(H4af0}68 zPR(XMt~mh?ZHPBLk#OintNQ+JC7Lq$pq@zG;W$yrM*Zo_BP3GMbIG0i0T@yg(Ajz1 zhvJ8Ty&rvz0KV@W0GD>q3U(Apxv`zgg?&8e_mzad(@o}vdzBgYIMwc1R-+> zc;0F``0LrDqJU!uapMS^K(}q7)W@KRvRBYD&jZv)7tVSlCwb7ysGQOn?$d+>*n8Km zhgIgxO~^YT%T@y?jq#et-w`@(dYO zus29K?5ds#!eK@90tBr+WJyjPh*Ak7X=Lh|bPG!}5oWD9Y>7}cA-ya8_sESn4g39a z)#%-A!_KcoIi`JGwkB=>q)mR&a02;DByaQXP`dod(T>dOQrB2)ySoMV4N8zZ-JamD)tt7vIB*AZ(UZ`^~rEs}S8b^zTbHcy?zm z{00hEl~&BA8o{tJRHVCG{|;q3GV^`14iOmn2s1DX@fnxNcZeF4L#U;@!ErcEfW)M| zRG%4}k`se!Env1OLX5TG_P5`Ui?r@MD;!lq1^(MXOJW|J{`Yl!4ao~yIEjF#1h023 z@(;BUhb#B^noubz?XY%P{w0aG!^z2HsJTadR(}YmYmQ73TuNK{IrK`iQZ0ADxyy`2 zGp+8eej-1j45I8o0=rM&K3}(0)`-?-h0dxBmrR;{h;%tO(B;F)ZC<$7UD@P61P#~ovX zsB3t|;~SXt)u+el*DN!BV`Zu+b}Xg^P=tDQ6;-G-^rS4eW~j{YVT1mB$b%37HuOOD zm2ll7?Axe+dcy)gE&|K(pej3MSCaXv9f3R!0Sj`bbtcP&RF{}@e5bB?Y{mWA7g249~>nEeUm z$N!e1Y43u(w^97A_D3h2kp=ZtTK(>9CyehmKZG&L14T#h?P$ek^pr4%5NQ%mwvuB9 z6lq8cp$Weh64`4B{&)_c{AJIQwCx}W<{I}6JgjT#E@F0^v!h4w>HBfN<4{U@SdfWF zPs_%+UIm#JpOfSUThco{iLJi@aSS8M_Lrdi!{As1teXfu+g-3Cf=8w!a<*=gZJ^}T^u!r`dW&d3cKo%92B-{tK&Sc6rTv2|ya0jYyKjbHM zE*L1a-IilmnCJ}O=jPJoXZXChARM}W_(^u4RPM)g#n$CF`n_xH3)Ha3X~`3q*du(# zva@W3B&m3{Vn^~*MAM+U^NLx5Br82MBKF_Us5aM=D#xH#FRd}Hn+SH%U#wIP^M)1W zV{x~);~7+LxDh9XW`vpLX?v#R=W5^a9F+&1$xfT$~YJvu`v;-4Un+ ziC1mk1xq6yNQNR4IA4|jO8v#)`EDs)cy~qj@zfd<{B1te3O_8ul8{5 zD(_CK*ZKp0d;029;)Nr$d{jyK>CLs5k)J0)oh*0)*dDrIr&J%H*SWQL{5h z=gKzB7Fs!6l(6k)Q7_W5sl3BBTnQM~t^0y>*Joty!k*L2Xfdm49#m608vAlqz#aML zs^#nDR#Sm`S>%AXXJRaCky=gfr4I%)=v^z8NdCYCOL$)je484PV6QggqNp#JEvouB zkMNghmI#*O5~z1jJFK%w9-iv{CdfB+r&sPey`Wl-VWeG@U}q7KSD5r&rc}N5yLk!q zj(PNJ&X&;cpwjxwUpL3$7(EBuMak)$9?_Cb6GdzmLqb@!7SpsJEaA9!wm+Mzj?ilbMz727!=bi4woYv;{r*fFT^OMDztZ<#?gqMpv1m6{|s1qbE^@qEf&C%Q@e#|oL?iTiDu^JO|v zM8YcGsRG%h5QD^78umavT#+@r{qb-Y@Ob0%00A5vUQn`hiZ>Jjk@>$)gF01+C?#_* zNfGWAXujCmGpDZ?#Nc}(htSBDO?N(BZM&;X%FOY|LcXAD)*zTbz}pS+E3OZ8f_a~n z@=JU(`4;+AM?ULj=tERT7Q$aSkcJ6 z(LIVCTV149IV1ph#TY#6%Mx2-ti*m$?kA*ry6JcTvFMV!P5L8mndo}`hQN)WlsuIw zT`$&Z^w3c*9fsh&Ec|de>wbNK@wTUPHcRnc660t9PeOt6giv2Dj{vy*B*N|fo`yhI zox3K8qWrWlMwq*xD_?>WuI&k$0fFASS<)}&8PtDGD9XlUB*hY^I^>9jJAQ#g?VuP0 zc@exut!Y7Xn)Y+Q1`>)}ySc6ApGd->()wzvq-|HsZ366>n}?%ck+qU}`*q2F2&I;H z4r-ZA&4@NvpPv%usb zOZ!u1@ts&OS5dnA{h8{g)1&jAh0Ypbgm7m1q}qCmb_PK}9c9|Ln*pp%Kf`ZhIazu! zVi6GX`dHZfP%S_Qc^xU+ZcOOgCqf7k&{WDlu5`QgwDPin!`yiVD-j8+u%}z@qfa=w z(F^!T70X7*0z@v)XMD@ueXf5dQM2IfOUWw&w2Hr^BbS6=E2&?CvzZo*p{fCPmwfLB zhXUYph*4R?&%bJkvGxk>eqSr^k~=r(u+qC_&K5yp{9yl-GrdPJ{;|+DWitCw@c#Au z3%p49xy=a!qOCEue#xNxkcX6(VJMhsElDkzENiJprPSH^5o&9GUq@Zj7&wmStwG9f} zcS&#iTRXz`TN$m^yre#j4|O}vAN`4UMki?k%L=6vMsMiSf0muFPU;bWcuxQCA%VJ|Q7a|X&6hEO}b zHvPhSK=qX%OeD8xE^V6n6^I=2-Pr(0Xb|eI0>;g?>F>8+8E*9O>6w#v^XJ7a(2%pc zZ9hGxDvj7T^`{fy#uN?3h-nKApk)g3{ws)?sCJ%15OCv=8v3ER)QA93gu0sEC zX-|$`rNlVXzC@pvwDZRv!WK(kpQ$WC(qB@a{JI1zrbw`UPzobJ&)o2ioE;%t4x1q zO1z+)?=p(h#z)5}JvFi{!Kk3loMj~E`oLoJ=1p#LnibR93FBUXWMvg_l1E5wc6=J`D;bgAe&Tnt-5v6s0a9OKq{$7ZyAfa8Is0$Zq8=1 z!v*K9EOn2BdBRN& z6nXA#(+JS;QHE)akJYX&{(E1}Ni&*qcb}eHHgWVlecO+UC>3`X&5WA^A8iQqPucY4 z1Sh~SvZ6DLFv1*1!oo5Kc{`&&x<>jk^z4AIx8ahs)u+Np#pKc14}TjQt^2*qa@5 z)%OPFG7p)B^N$8rrXu}R<|Zm%l|tO#>Qvz660o@%@e?*g(e<-iY4#9sTZO8QIt~5` z`sx|-4=Wj|I{m%Qd;CobHhyDIss=S<;x7$0#$cL;KgK$A4LQbe`i55HRK|uGV}7QF zGvh_(hPOXFSsSQ+5VJQ3{Qz?`XfgC#_5Cd;?7~NCHWtvtI4qg|q_b6BPO5WW?QOt{ zpv)C!BcP%bWn-es6=Rd6qLg6Mr^=Nix0Iqwh!WL^5rxKf7sSU%CW_7^bqA(&OQwlJ z(=SwmMzANos|`k|&Go8H+^P-Is}Jg{&&8=v464uFtIu(0%vov7(;Dp7n!D8+WYV7dsXdsYJvgo%@~*DRw+`>n+-TB*&Skb+ za=sg7vAcgsg|Uq~ypuY-C!nmKdTc1*Xmq=fQOU)@l&009)r?mrg+l%;OwmJU23>mz zeSQO7dmnxN^jr1uhZ=a|2JYuA=Fdm6pN~vG@BjMT$1M`F`x~zVg*+40$n}kpCK??G zPok{F#nBYS(HX@N2IUqF z<(3HLkuK$t8|79kWuJl6T(VS1VPq9?6doBl9jUm=edt!uY`BKCvR{0y_EgM=$k~X5 z2#tsj5wr4p>gEqu?m<5i@hX{(5?PE~S&cN=$eG!VP&vp~ImjzH$$hxU6}idDc*swA z$UAt+Bl*Y;_{o_C$R7pBA%f(YLgcmpa)2;7mI(Q(2zjL_xsMpRqBuF31o^21d55G? zqLh)Vw2`Ka5w|RzP=e?~qG(34s8FitN?LbBhNw`M=t{OIC|8s;U$nhIRKG~{p;$Dd zv|FLP8?#chy{g-$M#al4;FDEAx^2L{Lx6xwK)QRty;p$1uK+K-7V0iOl~ z(nkV5K?2ez0zSsPGJ=X)mJelqlAJ zNpyFG`6GQo>l4NaiJ-#cmZ8mwv8$ra=8+S{PVqP9Mw4*H3Av%dLzAVIj8nRzUGG#8 zWi9m2QAX=4#tAW`##@x7J&0ElV9b`*?iQ)@->^iJSiuPyq9&>M#uJxlm&Ba?#!$4n z%ReU&tq&JBgyRdIpByb;veFV;_8(Kx>T&;^EVMo;+>o>{c&hTW3n@w@T-k4|MXMM6 zb6U{)JaI#iY49i&Xg$)Dba}J?IEq&9`R7cbp;qBy+R%{PD)7)|*mVkIYk7z&L;W{S z(Ij?oF&Aj?Ad0jTnMxSK*?Yf4?U4c~-oEYoh!-MCOOmBT>yoXcCN^Xj6l3&0V1rT? zMFB6Qh?dAgnGHEt$x3o4D?Fx)Bp^rNZJ!Tb2nHPH33(2KYjU%CJajkkR@ zaTpGCME5GR^aV<-a@o=;qO4p2Iks>6w(&yd=|M_6$UK8f;V*KZ|sS-nYbLJ5!6D$Odl2WBg+v zYSCoNtNdAHF!a7NCfhf_7zJGdHvxm45zj}9jip+t*En0NUsO3FV1w)%iX=fu5+jkU zHf?{6(v?Ye)2JwRYJf2>x`aDH$Ri^j(RW(YIwcPCY@SI`?4kf;Rdk7Rf)Lwg8{bT`TXWd2~#VglymkI+(#0rC3-T37ZPrsPqtklA3=E z_>hvaUa-><9rH;__Jo8k**R}(IXWg@N_LzSHh4fueHs~8CVUaiEiG$GPUjw!_mes% zrdC>3l9CPLYbk8FVr_Faidl$7Y#B)2VcnCSMz=b#>Q|f$ZGJ? z@$TmR%!`ewtT3%DZ9d8S*&Q20qbS6-FEdn6Lyep1wRI31Go~n80ia8Mk-vo&7b*8! z#o?XokT@+_^V|HN>~S%NNI+uw3?mZr!z4s z4)RZD=%^L*;}{cE9CT%ee$i0_3gV8ERU9m3hg#{VnG55}(p4NhWQT6)sL6`rtg=-c zf@Oy|?vg|6Oc~0X<+dpoi{reCRBG~Nhw|yECriAxjN@a<)n%D9VS`Y5YO=C8t7;X8 zp{FwpWX)#zKTG3dMl^(KmSu;67^rnBytXFeW9&6$h4ks>J@U6M%9BBsq=v9TJVxro zsyMkGm5cY^hEy1-gR0{&22?J{z70h%lEGs0e`+Pf_-G5UaeNzEXQb|_jk|}a)QF8p zJ($rE736PaCd8zFmpwUv9L;b(I*&zsC4<%G|D1a|qa(!T^mOJcbx&j5{i;fh-_sd8 zI->skEwaRzbY0mK2iTwu6ZK;VU%VNLHyrAmif-89 z*ItDlbS3GzSU817uTlAMD{4<~%W>w6I>tS!&Ar#=KtN$KIrs9D;$HfB=y7Av$okHY zsU#wW&L5c>G!$u89Df_QTcSr}|NQ}z^?p-E0Z|UykVnibEjj$kN$=Bck`WTsrx4)# zYkAca+$f?#km?+UnGcK;`Hp@3OLi=2_ZO8mp#;AeImfGzo7&H`UZKQvX+rzsrnDBu zG;gBWU%lZ-_%x+2TJZ}TXP-$~c{p}dhOa5Ok27C7F~qxgQL@c|w0Vib=O=ATo>gSMdN?h|w6&g+w4!{g!`?|z36QyQra z=%hj#|A}2p28^&A(#B3GEPxp$xu^UdQ0kFV%mX-z(zD%kq@6bcQT08~5d0%YfgjjN z&njY-r^NA6SU}49Ur(+AUF5T7-QfBG&Hic=J47Z@n>D#@BPxL}@LbUZeKgi*fFDmW zEP%8tklWp;cC^c5VRCt`ZccJkEyl4i7fo~5c$YUOxSYN@JKo-5b_N28!YqnKv{WO) ztn578r{^f6Gye~HK!(5Wi&5O=fmT7$4%}!TBnb`KlA+EL!Yl*ogCyi3Pc>A^qMf1A zXUN$xUPURN?4>58hNUb<#~Ve>QQD2tZq#-_duGG#Oq1kli5lO`4E0HRlaR*&)pns_ zL7#JTI~Z@~SAJ7XpsWcLHDO;CH6b%^j8b|6l&5{ULF%0@&@>)J7)2SROTmYrm#B&R z)jb*2cAJaDcp>+(xrEdg=TX$`EFm%!+>D*RyUdI-!<6qPsg$888=%db(6|Sqs0sOH z)U+@QgHahT^({cb1)J38CJ9;8G!!*~O3fG#xBP4e6zw{{5l~9xa3kLL`3WBT?u??$ zLd`Trz4$tZ&AV_fhDtA?)58d}@m!*pVdpKQtO-6> zG)r0BmT>;%ZzLoEoCpC?QDdE4NX(~wt->g3UX5!)7ZNpr%IKlZGBiusdGM^`E(2X# zv;&HEjpqrJ5>5o=3j<)2GfhTOh9HF(YxAOP!TvdneasqK7_~+ydNEW+SZTyAj1UI3 zpwuiDeHY#+YC|xi%57eDkH4m(s6qUdGd9!USe`DkgrBNplk<}`iz6$!B*mC6!#9Zq`VpB4Q!AW z6)0;0^`M7FI&wo>`e!IYAwcqe6;$Su&iM@|0>49)jf=~GYFUhKsNJOJ$#!<*8;qhI zP_%dEtI@)2@(QdKc!%&&ajFkr( zgJvCNhN28Xn4`=>GMBRBBouRjMlIU`MLVEq2i;q|gJ~jfMxh^v?O~JwMHx8ilWnZB zCQ#NaRt7-X?k?@_j}dMj{f!3AI?I4UYh<>#cR_!=-+BoP9>N99DF&Q!8cJ%Us;cT<`JH7F_NR6!tr?H4H7Hg}ID)76-L6 z6y9KyZA#C&5;t2K0Hs|Cf?S5RR6D39P}a0qRctGrHGAn3L36{Z8YpawaXLji3$M!~ zq?La=2-&Q|>J2C?vk){i&KwUR%Ba-LcR;>?#TGU>>$}X_ELl&o4+>kG1{990CtDJ= zS`&K8DC@#yuzm^qKSkLmZo<)SjrZu$?lk}4!KjuM8>h54!5I+kfTEq**mkxoN}H-l zR!OjF4XYGGVJQW!GHUi>MTKuE%JjaO_KoZ0K38eC&iCli4i+?F>1U{xMaYQ0SSxcGf(D=%V0Ad{u86W+lBWI`V70~^X4)`r7Ze{rCHWA+xLr5t(g{xv$UI~ z-TbH-mTe8rbzy672~Qf^{>woqYXU`0Xey!%D1wr~x;AJ9D6IKm<#iTR`V22Cj8{?G z;KFe*%3#$T+K7|OfNELH%ux8{9qi);l%Cq`5@ ziuVVy86AYE-Jo_IJ{0NucTzi`AzU-Z0~Zv2BH-3C2^)L&7ND z0laWnv4d}o`=8m8HbPMbkGJsdFce&~a66!EhhXQ?4k+4L)|p1A&Lup#49Ur}jxwOo z?jRCp*z*CEvY3mZm_T$VKvkLqCb>Lt8$ z%(twd@G$`j-Gl`U8;nA$2UKf@N9oNY7>eHr)(vTGyvW)ZzOrG_4HTYjKv}b2T+>{z z7on^PAI##v{2K@UDF z&Iee!2Nz@%z$k0pY8W^= z7Y9)KQ{ldOGRiV|w57N7;+pWLF1>{BG`vb)C~wdw^$D+psA=eAkA+$@N56RlL*Z?C z^9VrU*8mT=@K-SulCPV6Sqy~)3*HkM*`RSdi*L;+q9U9Y^`H@|W#OB5TKt4YC~F$^ zbItU*vBXyF1(od}156K($sQaCx}5jMgUg0+nVnpX;whvDxP9{+Yau<2RvBS^jF2%J zFnS8 zXXo@Jzo-|AK8?HYA^M4pQ^LBWdz%nQidHRVhsR=JOq|G(GELN>9fBg8VsIi z<9F!k^LJ61p;8ueF%)w_j(~_&0Tf&bawLp`A|{LZ;SNGQG@--tdlSl-3XOYb zl=c%Bx3k2-p+&T0>Wp?T%TmV@D5>zWZpWHP_{GMJFXeC?$<%>x}|_V8AWUupm~}9lVG9lq>!^u) zf7Ap@KZcWQ-j>dtQCRxk>^o^FZO{9Ayczb;Fp|qMe~qGv05QCCNqTOkcK6Wk6YmT|eySfuiPhynC5( zj-A&B$}qDR^?4eNvG;n^G+*{oGe$qnL+xM`?fT$$&>zLUAJz?F+iOt=7NMTra+5se zfubf*)LhU9gl~&*pFlYtJXS(J4eYFbpIlTg+KO8aZ@n3GXhFJ2tK0w{X{D&x)Y2!QQmSY5%MJ*=*P zYFR`K!FL_eGt7$nVl+5PLbikbb;!gaJIpYj*hAubxd>^3Va+1VD15*n;fZ$WGks8D zLw6FYWh3N@=E-^z%9YLI1ktsc0wcl`!B8uYx?l` z1B!OAU`QYSliR`DF>1osGHSw(td4M(nFT_!hKj|XJ^d4VZGyc_7e4hB7RzYP>O#MQY*;QdfdTYF-Or0dxpfxi8Ebu3or8H zkDUX1(?faTpIG8|njP3;Thi1R;GCT$!f1&n+S4a~z9W@SQFp zns@$SkKY;A=7V+TcIMor(%5e%S!isa0LS8$Txm|_Z> z;q)+2A0I&hd;FSa09C}=5tIf-_Q5EW!U;vS$~I#4pP@PmVP;)O3pw z1u(Y>BMg{2#1#*YtiiBY)~BENdJteLzI+7#9X}E9#!TorxNZx|uC~|#YiL#o z`ZMs*^$zgbt`gQM13=+&^ApsD;9J5M=Vzx6*R2|66^6Uv>JBAAIB!7tyx0x5hGT9S z@aK33rfjnWi3mzVxJS)t&!H0H%Ri9w@c4<;SQg~7gaK|D;G(|+ySCT?Um;*EU@W-? z)&RzBmH;I|ZI<=zC(MrFE*EV2jpR;0A>IbnM9@sAL&B^L?hz#fYzsj+C@}*2!r(5$ zH}n#A7G_Eu2q8to5L#o+yTLebA*ku7O5-pvs);o}Oiwo6N zRD@6_c6QNC(IWy!YRr3fs0|?-lJ6YK7aZi=RRg=W-o6Ee-63Sj!LSCKZ4q<MBxxNgy=}=;Lm_i9&}g&*tQ5=1>?Zt zLeL@%g@w*JNW9oIIXngVsbLgvWntJ;uSTP6p{W~1A8qzutHiqH$hIw{b>ky~X$uMG zXzv)Er`j=4G?A1gSQNaU#W7e?7it#YQX<#h?3H$K9H6uV#+Vi~aP$@gp)GsKu#1IWqN8qO?Y=Bg>*TNhk4q z&_T}dAB+#bA*0uiq(Y;;Lrb3-4uzd9EC^|YR6@$$bo{{7JGp1qu&!si0^aXntk2n- z!Hw5D0Q5uQi$fp31Dmemz)}LDUS|oWI@!S^%w*ws7NSy!ZDUtG*)$wR)>AW-aOmpz z@CAvEe!`&GvIyTC5H;Sz0^!!ssf$hE(vSxxI2VU&E#dvo#pbC)*f`>?MFau!ss- zHh5BUq6C`$s*_kQfPUn$j(Ywlz%`Ci|0Dog!j46aGVVyR<|l;o$el6(CDOw?45^U< zu~F`yi==QD<Fg^&NWTacZILMJq#Qjb<*^A-6og2LEJ^32EINcJ zp?l{v^vI}_(yY#)QKEL_y&=Sj+aUrhNrx5n?o|I%K0VksGmI!=N1{+DC&{NdDU3c!A?Qc`M!8zG+ z9h{V)CIO|MH(5(3@koeHkbx%AbyHGv2t5d`%yAAmN`jFo>5=XxLCYN2kXa;<%!rKy zH31go$3_a81S_*;TaX0y6b@I@pC4cA>V0VM=C6ZL}y7K4-749Wo$0tbi+2yymw z@bln|@Hb_>9-IPuEXsjBCIia2-7FvxXQ3JF-Zbb!A5ytNIkA!D zB@)DRPOuH-0N?r(P`+@aph>_fr*Xk1B$R;~Ibjmv?r>3(mjslv+}ueTU8U3TyfdIY z(anrqC__teKbvgf8%FIGgo&=f=@35|P@ZPoY!kH1VQek{?A_58C534mIU#Ph$iG5p zWx8WrOA}DeZ8P7#>1G>uau@&!hCuJLE_M5$NN(7*HNs z+{6}N*}_2|raUKI^x_6`oeU(?r{eAw068IVN=lMoWpY(Zk;WNE=0&%%1eDzs2~iSA z=99$@C;;dDJvkZiN%?|L%3X{+C2XH@&^0ITJSPLm+@7bUSD+jqgVVz#Seg0rv_L?V zqhqqOP=c0uK`n*I%{g_1Qi25F#IWY@c2SlMVPiI7OQoA~fZS|@LK!_KXZ~O-%Kv)6 zfBt{}`9J?WH%ftzO1CE3op!I|=>&8}$Y(N6yq>O+E}E{QF1Y?M`b+Ciuit`x9Qw5& zf6enf=?A8tAF}Q|U#ou0`q?W{C;=%+DUB&PDorc-BTtjqUZUHM?nAn1=`N-lp6-CU zS?a#38?A1`x=HI6u6w#32guL$yaRHFJxi=-k)Bj~bXktHfzwWpKRplifYjqt&r>~o z^*GkkS`T(T>6Ix^EnJ8tklmSzYO!+nC=9IltMo<|; zWg(TtRNhpXRb^k5*;VdW8D(XimC^1HPc^XY%HUgmPrEHxKF*0NhF(DQdZPCgy};;| zMlU&f>CsD&UYPVkrS~o5aMi4@4-M))Pj7*GN7PHBUPbk)s#jUP>gwHCZ_;`T*IT>Z z@>L3;(gl@2sC+~vCMrcyxr)kNRA!?x9hLK_Tu3EVD!o#9mde6Zj;5kE6~CzEDz0*Jm7S|J zT_yag15o{e>K9ZWq52EeeW;d2^);&NQ4Nslh*WE&8Yk6WsXj|JUaApOjhSlOR0F3v zIn~;!Hc<74s!>!urs_Ra6RO%#)upPgRdund$yH6SYJyegtXgQ*Q>z|Z_1vllS3S9E z&sD#!8h6#mt2STt|7tCu77A*&pq313dZFeRYS^JhA!;|Gh9zp0q6RH$=Awo#Y8s=a zGiq9+rZ;M;qsBaH-s9O=l==no2dDgtD1Bhx(~S^R|IAMJXWh#GOzF+3eBmh7vsF)C zJ&N^g*27wlay{FP(hs8a%PHNs8D)Hw6H-PH)C?*Xr{qn+n{t~|?J~-Jz6j;SlY$$# zc~G)u69eU5PPwO3?sUq%ok9ohiJw$DaElFUeZFqJ3>(#UT&wlkomAVLY9}NFNv%Ys zi?3Wh+fL7n48ou^Ugv-7AF(I|9N=cddP(qNSygG!YmL50Jp zP$hVU5*sunMu8(K@A=(TuV&PBI(2nu9#vDRx=W)p(|{VqEu*w&e50gE)O13-pVTBy zO#}8BYSR(a6I3%{7o(OmMlHjrI`0k$O7h&B5XGnv zN5wixO`D1aL|8^WCIid!?t=6!&bl{s;5-|4o;_<`s724SXWi{An?W%u^){;6)N(@? zl9X{k$D>LiRWPaI%BW*gMTa8L&Ztcxr&G!0XVX5HE~l6S`exN#8|8F5lZ2?`f|Y|> zPpG{G&Z9VkUexofpHWbA3VNSOW=CY_(M4yHSvTd`v?*+#O$}CQL0uQ}vBT&O=g_mJ zMq%OYIKh9Zq?}InXz?HRn3{EZXN# zr*mi#6&}QWHae3|r^-Q8DNd2YIL~(f;f%WdEb7EfUd~O*7jE)>CY?@QBdH6Nb^1BA zjHBFC1*&N;E^$*%r}IYWoOyGH!@0xTDQEmbIVW+0%{jo^&zR=t%pxxE{K5sE&Iu0V z1cUQ)$|_vnK7TIf`j%6ad~$r(?dKAoN6jfqoXeXpE^oXjO8i{9oQgJQH!4Menod8b zUetthsyTOe`FSONUUlQ}F3B@%b9tbyPFx;S*Wb83agOiW)YX~WW6J7wo)s={-|3vg z`?$yLbL{Z@+zByA-s|2JH-4eC_Tv5yKLfj;ftOR->6A9}uBQ}-b5Q8s_dXB5orBjO zxW?0|bV7h^6xzO=cNd*&n^U}qL%jSv`|S?r=NTw-Q{^Ns@V|M!1!dmQ@+K#^&$USu zIp}O_zffXNYWz6w2Ay~9lPce5-X%$ue%5{G5chp-`I*;0&%Gw~eCy)e;ikBKZt;EU zdU1&RF4djdrcqp+V;$!CN^wbL%rTbpYg50k&J+X zCf%)A5^}xnR3r%r3COhbE$&i(JKHARp}tTL?oTY)IQ$N^&$jV8-c?sYJPUTr4!va!Z@#vUnCK9Mk$r>ITX}!_<3a1@Vi7I zC}3_1a1L%u5(=kwq~sv(MEe}s=f|HU2kXV9Z5iS9DI@&g(w3hYLU*T0XT?4z_>QzG zV3Aaz(HX&9-22OapBev#i`&wMeLrd>ZMa>^@V*~y3OHWUh2`hT`3EkKWq_0JNi6%b zvn45GOBa?)7BE2)$eoM5B#=O#Je zTL#!j4miKa0$WlWIK}xzYVkKtk-5aTJg|{u;~;Kv(H(4`UpL7E_t|whm-r{nu*d=z zNj<kkg;->n2w;L}m^7us);PSFecx*-iwp_M`9+Gd?|c_g?pu;i5)_=X)6d$QsM*ija`!u4RK19@ zi8Cz{mhPNsNL4PTZ22O;Jbt0oe=f=N}qO8Z$J$<(#PCH`+p zVk+9Q$!`=i{Va?--KLtGx!Fb1l13DC6L))lx!*N96NjCNAKb1{kWtP_nsC0?b*Gl! zDCLVR@`Jl|3CLU14a&IlZ2MRDH=-0!$Zxu`e{+x9I%fvbK)~U6Fsp)hHKg%I|)jG$Sw5e&!Bj-yV zIes~p-sjOv;{PG(?{t@3zsn>4YIpLMsx;?3UHA4OXWSg7{6c;2b873<+AsRFzugtz z=h1xK5nt5yA#)Vs5GQE@4%YWzr;~JSH=R*$ie&j?`-|N1uJ#{qs)Hm&oGbh$ zgM7;$`!8qM^@|L$`91H=750}t?Ig}{(fzK!&au;}k+cCt&&Kk}Zxr~OoU+MV3(!8P zujtoUE3m2W{6)X^i^S$vTaUxfGQa2jDv{aL^B1kP>d@ZjRouh;ocf@Ue|au#OR5j~ z;WwKMJC}kgzTTM@Nlg6C6q1tZRIsHa-_D|0Z8Adg(O^~K5($?=gbm`~ z&X&_`4?#uuOMkT~X6vseadXShi$;{vUUnI*o7(ciZ?eKc9NIo7u4AmQHN(+g~!k;}0^x{)Kvk&WbRsl;O7{A~Il25S?ES7Uma&th-~CMy{cRD;)<^3Xu4r2yxh3pZYoqlk`+Cc`{%E`R zi_JojTEA`f{$Q{7=8E3-=>K+af8W;sD9gI(#{RY>dUHwb%iZC8+2(D#g>NCJ9!g}e7&Og{I996zmY!y zbiaLXPmhbf@b=>W_gCKQ7aq0>J#XM4+hXqy-qYJ3tbka?b$!aXezjrv=H<~}dLGu` z{>3)@<2L>`yS!g+C+^$s$> z$+@ z?8?KY+$GG`XC~CfL``zss{-&R~s^;_U%T%vi*IuZ;E5(X>w+ zJjl5|rOio&WK*v849~Y+t&rf${ieJ)(};5eWwBIw{o!0=YuEEHR!VP;XZvEM^amS< z=64xbw*I2&{5b3G|Go_a?m5fvI2P!^sPJ+s6u;-p@AsUq(tts7G$A{M^~Lw;m$U7I zgM6QH2jc(yZnJlhjBRU4ze%5&GLNLp{`Nkcbgo$(x}d(WI(mC{w3i&t&bE4{U0*DX zt`|ivqDYd#L8|b+Ck?tMIYoX@AxRXz+8!_!vfk(0J!+qa7im!A9K1Faj%rOb&7oTz zyg$#iU#Rbci~Qof@o%KdalV~)rhQ18zj<#glDl>C4SxGdy)~$R@|6;|1srs~9dy3^ z8;c?Knfi9#F_-za4*F(mKHMtk{>9e3lh(C;y~m|>(EZZDt}piGziC+e7rQ^Yv*GM( zuRB}+#p|QJw5t2gMmBwX_7#8L^~Lh1z0Sa2ygk5SZp)uHr>z)q}#qC|cevyRqUu+lT zbyqw7a=x*=@6B=cU-V3DUHk34lV7;decrwOuHJXL@dulSMgCSVAK{NP@Xe9_W(V;H z{Z!S!I<@xW3$=zd(U!LGxyYU&TmSS;a?Tv+_=BYUbq?Z=x9@kqNzMB%CiB6(5ed`VEg zS-Z}^Xm5$TjQib|NBSA}rnX|g&fbjUb+>C|x4_)vAnuVRB;Wjk`CPlcSP;!GR7Gdp zSL%9uHjXbIjePDYS2;EX32~GWtKuz@-xTyWuf_Kr?iZa?@lxFPx4%hA`2HrZGw&cf zyno|eLLP{Jl|}yLoGaSgKXI3DyS+bgm+N2HpTGUF-W2(kwDgyk{`-EnDe-z~1>R>O zd7HFv9O!iKlh-|O-|@h8{^2}it~1WRiHQ95?7KhDx)14!pKXQWq}uBhZgS9h#omkU zWf`>3qw}Q+KxomgngG`RzCG<(YVy|fzAaqki!bn7Qt~go@c%|}zn)vaQQDui0RLZm zU)L+Uj$HR&&TG11Nu)?=5aca?Ag=>IOq?-nz+(gBn;?0}e@}~IQHzUJq_p=r1_rq} zxVLxr?#*s~6zgl%DmDJta9Mz!QV~TfA&Taj38t!695KMGm(-N14M&#bLRA_YS&$YE zFBr92?uv#~7(A%|#k#B{C3BV%B@$91AXBZcBm%PZI4a1>E4nD;2nWR&i|#=QVI33D zw-Tvvz34q6iV!o0EHGNs{w6NMjN)532uBQQPow!zNnhAxq+ZJM%Q`i|L(Zi&M?|JE zTUwKy=7>K_+|SlgJ5)%ztedtv7MY|@%Vcvb(p@!DTr-^X)N0g4fe%;Wk?gkt0=DQW z)KMId#43D3z7{?V@yEg$e=NGnD^U^f$+T!l<%eyM86bXGf&o` zg=DJyEC)4Ci1V}JjTmR#6s{(Uejpb7R(!F;M`1=aSVt`#6w)25PO1t;S{%d+qY>T6 zdZ|c_VbzuRVF2F>BORgEO`?mqT2PG{VnzK#dI*q7go>?nP(maj&Kprl9Q*6BydXkp zA(j`4vIE0Q%j)8l`dvXTRj1#=bu|1*u4OQ5a$;WLQko$mzjat&SaJd&pGH*{T)7X( zsHUD*!-bQD*F{E+YMitPy~XyzHBKv_raVZ~9ef8==qG7%IT9aR{gW&|sJuwJW+U#U zKshdq5iWq3Wy?+?5{R1Rs5>HNvBF{nOK-6w6ct)(ZYwR4Z^V&!ek+)>@FHzY4oUDK%2KIh7cr8w7?V$o}w z#YmMJUE~#WX)p?RWHD|J;)5E8?n1iGsF-hb9A>ILFr}enNX8t;Sf}gb zz-Oy;-xI`@#@?y1S7YsNxWQsQkh&^=#?^&ZWPa;lKrn`=A+LPad+}zjY^Df(Br9_T zTnJ>ROC_RMoR~kGfURaSbfJL@DIB~qE& zqb9RnH(bRlSMmj2jZm)mq&T|omAYZXQZl9lC0FC=#oDk)x^*SJy3}}+N@^FV6Ju!& zke8d=yqt)BcHKRMftQJ0B$q%;Zl9Zo6XlJXWdrDD<+Uvwi>nA8|^oRm>v zoDGlJ5+)0{(#0(0N(AQ-zZ6G!nD_T96`Sa%-p%=W1xdCJEa0F>?hY80nhWZTY3?=O zQ^j`}bW1kn*+%ut!0lVI-*au*R&@dqB+v3OXR6AaZ`2A{*$3oHG$gCZc#CDrC}vA~ z9Nq9fSE;={9KU~&L|9Ul(%^mH7ixqi`xS(`ks`GxOhjIZ)9TIsEfm{_=>wy zH^6mo(qCnSn{o(v)T3=lYZ0co;ivGR%u}!WuI{(t28M5 z7(Po9MrT4@@7(C7K+%`|W}VwwVMTUgxMq|U>KmK8u*HgK%w`;E*>3uLh{h6W(imkX zVg=w1MvkP0d3jgRDH}vr?&AL^@cp(<7Y8f55J;M4%ELX(f;ag&pV7%HFs3bp+|qb_ z#j!QEGiv|Fk<6ClJ92N?{U_5JHj~(dBlYNT8PVj?bO+^KWOzA6aBg8*s9h)Y*j2#K{ro^wC)`Y4A&d@?NGb0;k-|s|WK#!_E7~kb*1lDgFWF&)K%$lG!y3`>pawRoRyHu9Awl%y*7$%B3}si z2a~7>Ne43ORP5P-I_havr$)_Nif_&xE|E21QZyba>;}PTF$=kD_01GU?Y-`&(l98D z`i5~PfieD+Sx|Wj-0*|bj_mV>$q=9R5%P7^S?EG_N}A@une{OZPY74VX-!% z93{d59QLIz3T4q1o_aI7QQ=hrVf~qJgfJ{>ghTRn!Qi2acI9aD(H5mb2D9Xm_~#`f zu&yX67G5O`aF)nH--#QV{JR5Iw-F~vlOfmi&K;J7nQ8!@_Evck3CY-h=9X_dNpLq= z;vp$k8e3?>h!Bm)#?+|_%bW#AwSkZmGu^k2nBb}!-`JslM*Y&g0`AvZTqBD))_N| z_H@o>uoYOkMz3@^b5paSV?aI#}DM+pcwM9KFtKKD(QFG>%S~IRQv-Ff2o!^XDUxnF+>Kc_Zgmu>3 z#`;Q(@2ZuqV|!W`y-`&SbN8ZOg?%U^U?B&5SSkm6j~{Ck(q2hcu1{FIU=*P z7@iTOC@cq9&B<}Q$*7Eh7@mgjz+4z}WED$s23?1I@N+*_SJebex+U0DaZX0u&OzQw zBJI23SFI3@a0Wlfs&wC5cSWj_G_9RJUY1mC2KN79r@n%+GWsa1x^3_1HpcyPH-)b< z@i@|5fns@7Z&sv-*yN5pRWG8Qq`{Fi%*4-2ET@eQ3cz-)@qA$H4&!z+MYT1YqLmTW zBHY-id^bAgVJ+FNbo-eJHM|j`DNxwt;5`2^K4YT zLzw%QyZOw}-%6>TEPPUo_uCaj?rwAn7d=22KK1?w{9eOMvnwZ>EBX-=-y&_>N=VcJ zVZM{E*_FE=u6AO(%7{L#Fv%8Fg>hdy8PQSx=4a>x<~}rhng&H=RaXvpf0Gn# zMu!9~JLFaVUw|COr!y;70FICxLlQpD_eqZ8{&Ru|&<*4Fw;y%@#%!ul|* z%v6}#syJMrPI4<$q_{q>MzO@W4oncTT&beef^|EshJ|$yZ~`}68(fX4#p0L)DkxkU zj7nllOhX;@uZ2iynp!W`Ve`d% zh;h4u>06EJOTnyfgiDCPNW%ytwA`@5U-*9mRbhGskHDy+yDV%AWnDEUwt2wD7CKC9 z!9W+GE?gQ0woB?H>fnrsLvVM{MHW}$a?=`QZmD1(ZZX<0!lN6}h7wt4-VTlVW)uTN zCQ~inG>Sxkls3E_Pnk@YEWObNgT;Z;Px%i_Uj=I#+@ z0&`i+*P-DiP-klX7k8srAJ&R-uj8feT;51G=Cz3Nz-0dp3LBhOTQ;gI^Ppe3*KzQ@ zD9EPpf843^w7OUzGKsrabfmbd9tl)E;;DK>vve0dJKRWTuS*eUfVon{hz+WlVXl?l zDC)6N|Kd4I-lni}bA={as1drfqP}Dc6=Q-LGS?~mRI?#v92?xhLiKHJSCS?Tcd?RH z%8Uh=B}Ud#i@94ceOld?a9yp0m9Koo6|GQ<+e*5z5=RKL5j2qyf3GuM(`D7!d9P8& zZ?*QSCp}L)uUZil)^S#b*{XvU$+8f%b0@NbamF2xvRlnV)l{2k{a$A1|AkBhB3)Up zr6~IfUBR8ArznEbNDZ-XHwajgoT~n60)1ogNv)76R{DRbS>ocKg(vwbt2-X+7`7ho zU@RCJtD%#&d&~kV9(^a4(v_G>+1JxD#=zMz;<;C?!_DAN1S?Q*fKA!=i7a%?Pg6jVX9~#4oCm9n`@TKQ)=a zLx*QybhL)ImxxZO6%%Pbup=dBw-S?&qfB-jNUp}VIzgl+DD__0)45lD((8Pob#Kx* z^`{3&;6X2ElVhLqe^q@~4|C6mVB-8}q>_8b5dQv<$l?oXc*kBbpMM;jLj#U3;cx*U!W$eJJNL?V&vL&KSXI?W3}4Ed8iUh3LIiiES>_Plh`bjyg4QhJ7cz~oc_#)ldMgxmy5<>rS)^! z3^Xxj7vhB2d#!8w0=kLolzoT0YB90~3v{GVP*qK4opAVSq(tLt{3@zSV~NxJJdudl zulH(Vj!}s@B2%1(1V)Q6SE!iTapIe*B*GbdjiPe`Nk%YwmNBo?*3^BOxx1=spwh=o zt(XkZ$CEoKIfb(^kiXXJ9JneSlC0jHn~|f7v*xFD*lr@~mc3>1?nG3sX#P^Odsi8A ztfX`U+zMvS_W@6bQd!wV^^plx^Tm3sZ+E&20=7{_2QS23j7;DI&f=y=r-i!*)^D}T zyEiOB?sYa3moZmfg5}p^QmyiDI14J7H{{E*Kq7eiN^6;awz?u3`A+2N9JT z=TalgGz>rG=hPUf#>=q~!<)v{hDPpT zu_PHPMkW54B=>3j^KPf7z0p5$m`;iX#VWIwQdMyx}K-q5cqB5iN$w^6S){cE&XW>ow}O;>76 zbRE9q!X2^>xI++S#!;1?RNdh?#y$WRF4Ty^6bV{&U$e{o@4(j#SC@`Pe{Kqgm8VF| z*7}^KM}UDmD>@VNh=KZKGYh%+ovVT@TR|^soqM%z9RWJ^u4LUVN{1-agPbSCOv0*k zr$>D_Q{~SDy#gd_P)XcM`Y%bE_>)&d2NJuLcbGJ3LY3X{U9%9L||n&fHOe3Ud#nS9hk2I8#AGKiHLAgjRjbW5jC=xXHh zkX?ZpR-@7*Tk|Owh1VD(JkH1xV5EoZ>~@{)N0q>>u|lufJLERrLGKkSG@umj!~iaO z&3AB_5BQ8Lj(;udKGJu0G77yKh)(vs!fZZ_=2U&&EG{i%U|p2DJ;iY7VrdI4 zUZ#>bscO|Qo96`a#dQ z9V(KTbIaoG=L`m8VphfaTv-aER)Hi{u#19>ULa?8`FlEhg74rvy}_cuF;wm@#oSp6 z-leiIxdS~TqkGYp^+sJEcapkO&u95oT6HY)mTU@sWucC~C|xK~7$=Q4a4GYJ?^Jmm z9o32pdRpV!(pc-P?Dq~;w3z80^?KV|&XQZ){MhF$f;G7*tf-8fj+^GYS*)mx3q{{; z&}FfyCPHSRRl$N{1+GXPG73GlKBcPYP9|OJYqQi56|<`aTcVnd!;A&%HqM+o;Dzk; zrRhdCSP}zCqNiTk9=guQ8y$VQW@`TP{f}k8>#m~jPO&7|} zYf|SGcD$+65z2MCL2*CrPR3qXBr1K!J!!B5)T-34?@EW^&_PxkOs&iWUZN>1c~W<$ z)w3h_b$*+l2 zI0Li7zLZvPfR>=v<#z=M@Cq6XSnbY;hMaoFt5jL>l{oPi1+Stpd@~|>>qIx?)N^NF zW~^hpidI985)cMGMoBNJ=rGhnH`wd6u7nlGic&|{uzDo00(!N`2i< zu|;p_DaKPGgZS3g+o^hrqx{^tkhvr^&J~fuz2;icdWbf;vPF?XjYD#4>+_1wv3j2P#%g5rLE-AV(lfKeogv=!FE}V#p z>t~z@1b$)b{sVcGvO&Cp`up>Eyb*T5cz1VN#;v-)Ei*a z+5FI=?Djol9%YF$Mq(sKX+`{`rW29*VNuxoEIo)3{)upt&@UkQb=X;>lH7zF@i+Qc zi^NI?C#*xbA{i}$S!0JKscZZ<=p?9i2;|C9tS7ir!NajaX5VA>AZFduu!ytbi%o<* zydhB~Dvwgc5`=j%Viq^+2^jm~6@N@3AdE=H92wUWaFvfU`h12y{-awhs^LJazMl$1 zAEU9F7^1`uxrRYxf zOzjAd(UtX-+zKDrVdQIaX-_RsVy+!_hVn{v>L9uTb$PW!FG?1`%6c1D5r>eYdp^iP2zeX!~oo>_#JIb&cg{rffFoOqML_xn}x!jJT?> zjya0bWTQ9GbE|>_*`V*5H~Xzs_{MbVxyNUKDF19k_2-Q1U43;>oI#U!fB*>u3+}-J zK^Aul?hXqq?(XivgS)%i;_mM5Zh^(!osakGuI}plW2$DlYr3DFda7%ryMJ38{{#uf zRTsm#BGqmp(9VzzYIppkq@G=o`@v%|G5oLq#)`? z6AQ0c<9AniDrH1i!u6W_!l|--p7Ls~$hAEoX;WXUdrIiEs9OOL73YjaIJ@Z3T>nyr zESsU78O(RU)Bxz4;0h&{oP;_F;KuLxZX8lg22hRozUnqV=&;%!J8RyE$r2yp=MExt z4~Ehb5+nI}X_gs&JjOA=@lSh!THM3ZeQayw!2=0uZF`0K1R<}5p?kBoFNFSQOoXYL zwPkrVQ7*0HY>Afw&NQLANmwk*s4G$&+WL{{P_+oyc3U%f;N#lwnR)_;%6+rf!Zyg= zKm2B(-bfw#ULqIsK}gt=PGV%*53s9PZWfOb4h>;8nhq?TrusE_B-^%qFwxMnAf+`rlR>tU1cu=J3a%S zCWX_xj1IVIT{Z+sOxX@xyt@_gSEP#|R3BS~7FzcmEH%tRP?4j$vZJlP{KmJhiwto& z69mf3w`jA|2q}SwGt<=Ow2Bcf2M!>%Z}r_L8rk=k#_i(?oF4u*Id8v%=ZK$L|0OIe zMV+ZlkTt+HWQ$zUWmG!RFmf>9@7-y4L^BcK{oGSnjOAXV`5Um{3EIwH4SE*uE0lSfe*Eh?~%ZfuP<&1j?LSJiu_@#^z9 z5x47tZd%rAFLfNOPV!f9K_%G;!Qyl%{`^r@>tQlKSWE2vNj9nS zA!5UWTQ~!nfqPBQscBT(qX21*JVW=lWe4KW6O691Hd*M_QJXSzV1m=TE9LwjMv+y=bPp>8e*U1iumnGgk>yH z;jk)~@sz1z5Iq!`yF9zzN~8tH>yt`zLZHmLO3Rbm1R6kiT0PeM&~RZ8@fM(HPRr8J zqcP++McUQsY0^gnns1T189nkL8uHPOuSf%@)u(o8ap*{rj|re?>Qb{jXHEVg`m*^O z^;2BiN{A6NS2aT^vbqPWXb;huSZ!#Qz8Ycm6UZy7%2|7-8JX}6`_AjxE0Ve$Bo94C zftP)!ucG!P&ksRO!$MsI+ETQ#g9t&FncB9TuMv@f=C(;ISJ590D(DWd4&4p8~-oy!{{rsf!w-o z!tnM^faiQwlIKk9fT z>ig1TT)LrZ0*V|nwTwM7`K0ETs}d#HC)N906g>v>i1tK|MH^q+@w!`=Q!|$AvKExW zn}#{`4IX_^roSLzrvT^k=e~r#tG;j#Wa)!hom)Vnc@v~m;44j(Ch9kWXFJre@+NS* z0^$%W7Wb{TN!tXK{tiZ>hA8om+N>hWkO}+Mg?x|F5xN-Mn3f2IRmn|0!#A2I(=ON5 zxi9vP1B)k@KVf23q;ga?E4U_;G2z?SEi&kS-!+P=DMWB);+lE9;)ertehvh`GSED4 z-;9aH5zmvyK69J$8WYYbo^;JaPsfXPLLu;&@PR(N){*@r4FNe>heq03@fiG8KuGDw zx=M`LJ?(-%uJ8T4*$y!c56N6rtAx{d9`kw*ECB z-Ir5D3N06JEJpnXXWNCrUBT#0J=jhrV4q;?7w>4iEqCxPt-5<;M=cbs$W;y>x9eRp z@6BR>l-1$p?~&`p>?&isMVKDgaP(pYn@n+KQ9ZVoN`7sT1t@iIMLVYQhN^MB_8#ytuY!_+5x)8` z#`l$m5D%~K#;rfS4|-)#w_UQ2tN44?4hdJ|`sQ`tm6qazidzvU-E&U$7hHUqF8}_> z`s?7(ISmEcAx*>dL~<$Uvusm_m^T3_Hq=C(8=>?kj=mm0QC(ZhEfLYD1o52T24hM& zqDyGWE2{Q%n=iYe#Ey9DD&yEta@W`xZ?QJ4${gbSoW{^^^a{5@lemr++j)A3z)2K6 zV2$UA81`b~Qh_`8_S0+e9^dJ5$OvmC)wSLrdA@t**U*)z^1Z%Qn{muUC~^V9=FgGu z;ZF~|_eNrlt5hh13Q6@k%$tTI856d8_Jg*D#&e8HH89AxMMB5Ur@}`0iJ1k8OmI*w zTt0&>B3R+NjxJ{^CT3g-wMQ1KB4hvVUaw1FlrBx6S0rKyrmQ%t3mgLU-SUYLHGVT9CT8 zjoOylyNx4axj8R;9`?)HWro<7KiZHKXLJTic8#sn+wm9Mzn)>v1_(iDc9HH(@Z0}PD z>eXsMT38Y8Q-mBYIMo#*)takj9w~nM5iT)bEFLUAY}Hn9zxFnwDy^A}3+b~;$;2I-jcYF@wqqgMykG|hL{V7s{NdMk>|fM@w2#!! zd3*2nGW+^WSc`u@v+KHk9!;z+q2O@bm^KD{!03>w1PQB9 z1!OCm!jtJkPN@7~)| zctuBnA}VDC8W|1i5e({HDeiW!w3vYhF0TWcOhER~Gl+33TP;=%@i2n#T3^KcOUtJPGT?7qr>H5KDv?Q< zfb6h%VkFT83PTBWW%flmOLD#E`SRJ5iyEk`G~mu(yVZ6PNBY@~%S*gMQIrX)zp zNu3_b7pOUjQ?%9h-eimE0gXVfIgGC->zvTZ^wY-CBz^IWxk!X`r?1`WoYAVIyW zDP}Bg2G=TUjIYbw%@Q&3#eMuaV#?LV{<*|x_;oZ+p?C&j3^mvX%oAal318t5NaV}^ z^+Rd=OIZgj5iHiV=o-Acbx(<=!BigAmpQ?<`(xrA*2w6!{X5GJ1Gu`A+=%I;$%ycL*hF`!rW>VGQD%cG`oVxNLO5o*C} zSMC3;V8ba0kGiN;(8_a2Uw|&#s2^4kOJOghwf{|P@B-_70_R|Y0Jiih$b&9}y3n~G zHNL;N2<%uOo=Xh*qClM_Fre>$WK>?p=EzBRIfIr)eqY+C#$5AxV+6<}vQ?hsZ8>#> zKfey2C+05(Uke{sea3w%*BogDVS~AI31q@nymcx2dLXXn>owmn`q&;M`#noCnREX zQIA5>U&9rD!Q@c<7QW*^=HrljhZkjBw;KXAN4kRtg>|FCu ztGG4k^Ym`$L_as<5sLQX?4630L=B~ehEt*fRzSI1 zvxEK5{fTH4qO7B> zvTY$aECgG&JW>0T>c|WZ#@Q9EZ~w?G0(6^db;9cm+$*num$7`yTy>@h;qfY7M5*e| zI`Ze3htcz^JV|F^RG8XshpimyQK!;;^+qP8#H05!+LZ8fwBdC4SlpWjnk|*dHnQBV zl-@r$BGxX%i(KGHwc6;gi4pF*rjcgVD^GvQTUk|K%9ePVs&A25-x*WOHnhrN#|f{E z3oJGe4s0&u=0mB^l7rvOl^r1}mD2~q^wqfMeczg?+G7Y0h2@;^!nYOF#Hddu3J*ku zgXl4}gUdlDoAoZXrC`Kr5ABG_Uop9o*g+kz+=fAGA0Y{n;C^1qNb#b6hMD92Tht^a zQsIE)GU`H>J`PoJbx$X<{3c}?2^S*a!22bVm}(9xH6R^5XO%4LnP2SoU-f@YG|fg4 zAO+HBhVoI@ehKzpmzw2CiN`myp-$`>Mk#cM`p63kZ2qmM;_;?aWxXd7JO`ZWIh`;M zK|qnXfy{oKIZSd>|G!Zb677I0GL}Dv{7q!Y#_zA!?%^?VYSn_Fbs@3_j$Qh5`oBAo zBAbE0;yHcrt9Cq+xs@t)dXq^|zdb=j#a7r18QHVD$clWFNHJv}C``K&F;LG({j<<4 z!$~l{_=V1NL1YR^^FN7Wbyh8|@)Fccu4$YJcb<727;;yYxXT|7{BtHbldtQJY1c3v zDs==Q<$1Ae(RHGou&eOWb_)?rK-JJ{eN2q&zZz)RCB0(paARtCi{``LW0EGEXHLGR z?2&nr>jR1|amO_z7HizK-5m;t0^Rc%CFyXAxL;3^obY7s-UQCW6tgblebQ@Q$&4cu z@*%6?>Y5HDPu40|!DO$AcFZCp@-5BE zF~b10xD`4bJakn-GeOv~fX?4=QEpJ#h;u$WUx{+dmo+9qatP@g4zW~m-1 zdczzfatV}EiTf04Pkv0#SNE_ddtRV0k2Vj_)+Co0>b9j}2biMNnzQXwHZ?Edvscy9 zjq8o$(3fYRBZWeyj)}vpf z>8Y{O=gY@N3i|QY9mnPL`OagiXF}9XirR38VC+2LdK& zQk)jMtw7UZ^Ib+D%}rpPKD#@%zA-NqreOC)j`0ltTg?I9>+CTlt#|ymvgeZ`q7y>a zE#AG}y3VIUiUoDh`Ih$h=E^yib~TN)=p@Bhr0PP3u3jh#+ep^>iIZI``r#KRXC?jS zyLP?wDVz|>f3mAw>m8k-!F_APUXO#Chro$ETqB?!e+WC>qd{L1sKwJ?7^@>u(jaforH9d6BHP!GrGm* z<-)CJ?On!l%;z^W80AFWC)vrpVAscZIqIl48Ad7-9WI29*!N`ON>j6l?==5blmMseg!6?2*R<4?s3 zqkUX{=FqDq{Fw@;pC+`lbJoS4rDxs{+r`*(J$*u5{YLtQXaDsJia7#jz;A7*@JYO7 z&vAOT<)W3e>?>azk}=hOHd`xNU^xfyVW#?MscuW;skRS)p_Qmii-e};d*y_5h8Nb% z{d2ZQccI}6C(uHIS64esdsBfwFQLN)^%zqrp|y%l!mnJ@kcH_D6z;5Qj$3WOacEd; z@UV7>Q?YGb+&kH*#?HZ=s}6ApCYukHRVOGh%?VSMl+zD0vV$%Z(P!i%ubJrv!eZrY zQim8I@+|R&gw@lzJ9iF8qjj0=g?DbraNA}pP%B^5`H?Go?fDgmVtFkwWR38wSVEt6ONNXUPv^&vS$i34@pA->5TzO<66 zw&0NPaup2-5qCyc+FBlPu~X34h_QyodMeO~B21-*&YJB;YsTU1`{Q zW0aWQ^Mg}Bgp~|+0U7w$f?soc&F&Fc^>9coXmnW(&?>tR6)%rh_!sFmHl25gd{ZOE zZ4@V53a^$;57Du4hBD1>Q1vphJFl996I{A=cadp+ubKB)Mml1k0212RkC1FWctadi z_QdyGw9I##sfVe$!~7P~zV&5_UrPd+nI&T}l4z9(wu}{0SuIlSL|SKtiI27lGS;bc^KWGGlwGgAdP6n}zQ3Jz@dS+^NWkYZD`2iyCG;62}o=r;T zQVmqkIb_NKW`q{8q3vAx%q{Fs_QJjN>mjuXuKS^KEmQ{%F1`E_Dqg$&a$8@0`CQ}= z@N=fvt76q#)M{`d0M*(Ps{nEd3(Cwe4)Q&E+svCEpngmnsKd^lJOlO4aIhN5Pj1|o z&qGqyM!Im{fJjk)f~q`Lwml82?HBPWa(izhgv|84Gk&)K7S>8cCSm_pw?xW1AlQ^* zUaEil>Y!R!aQKb;7cSNqSho+p_0tz6OcTYv6>=S=G6QhaFr$I~cGBnRMj=^8r6Dxn@~wvsF(yPAh_<`g$Bz&b*&4$5L-_=+0b(G$UopP_R$#|eIo z({Yy(=6ED%Uo17u=w7PRp*oM{CQB?VOl#~}TV)U(Ltvg2mPX6`=PFgMfq78h4nX&>@teLwSK%rUMW!#Ro%i6h=+ba91BZaLrw(*Oq?7}* zpv9Z3J-Ag>c1IY%&WvZyON6nfg$aX4{G$i;v>gqOMVt4>+#8Cusw0r19f7h0PhiS* z@~?+-1bO!DpmgvOw!XZ2*9`oah~% zBxgk`ht{@;H_>_{SH@{*(?f1Goj$J*HvDf}{Qn+zh~G{(-=6s2UI&T2UuLu3u6AyB zh~FP|-f!sn-!29pZ{HU?8%n?`@l|WI(rVTL1+2;T40xPtIe*3ajbb9ca^C6B;$wbi z3fmhZg>u3s2gwMU)%k+V-QEPN1HLo){@6Z$_kO>B?(AIlAr8EadcRxj^qiaXdE2af zzt(xPezfbjDB9$sfBnD;JngjhVSg{`e3{&gOnQI%=sf04y*&&Xu6cvU7YCo*yefiJ zJ*_HVZ=0d`vA6c$o;7{4R?i(Cz0Nu&=kJ>z`LH+bZ819d8aM2hdWePn|f_f>G`y8Agy?d#3UYaXw+ z{l=a7$A`^J5Qq2GVaA}%qHEh;!`!1D7&@GJbv$$NFAHQ zV31m~hi|P9H;YP7#|FAC zZL`FajXSE(myW#NpjmqYIfJLPo$>2OB~atzL%WoN9KP*~H^2AJY%Fo-(u_Mnr;L)g zPXh2lvxnt3o-@}XsaN-oSW?Gk{F^;jhX-i=CTYKI)-zIv-mHF-LPCe8{rTbg_2sxO zdr)pai4UHWZ-|h-X{YG);nn+PplFh_qdO5uPB%c4|6U~HR)C3eD8Z3+Qt5f7eK_7U@Jziq`5wiA*4Y>|9 z8mA`@;V;-pFx8#UgE)%bnYEh``C96-%~suUy=jPT&15IGAE-39Xa+?69-je2X6m%?hh&nPwo%q zNzFWs)I3SXwh%O~#rlX-8ZGZ8&~?UE^wN%2k`HGMl8=3~Nxt++{;0#p{f=?_H%Y|~ zqBm7hJb8xiC&Xq2Uq6bS@3ul%M-38Z7u`DZJvN6?w%fe4?eE((y`_^T zFO!kghbDU;U#7(N+ixTrXZGzzCui4h6f*Z6eE9fpKu`IqEeYC>6Yd#}z_CdKZ|~BA zYd(*HRZEE$X>W1W@pK&t{&Y#Qj@^zAqBW=Xm#f}HK=+H}({|fG!M8&)xx@E;FIS$Q z6wz7FFE7ej`&y5Z?2vuC$;rv*D+7;w>_m#rDjWJ{37tctSDsAO!^<$1&O=YVlq*D! zA%4$|?TO&JjNCjqSinbpPTrSTjR(k;K}*oO9$S7GC;#T;KJhW>=2WF4zYozYrWa3P z9?tc(D<3~N-eHx`M|W9&zux_AkHx>{&|!u5GCpPUd?pJ}KfM=M-jL;ScI5$N4DTc7 zI>~{&91U_j8$lVD*^J+5bA3MZ93>T9Fw;IA&bKeJ^qLcTBWL-FVqh!5rpsmg$ScLz zI<9zIyF9`}H>z$q5)Ch<^USgfK(`+MnYQ*Lcsk8d-oV*WjZ?L}8g2o_GL)Rwv7O0Q+l<4Hw(t7s^Z<#6zY1UIsoF^7I+k zrKf#1>#I}2mG{>Ka;CCa3ae)`$i7Vy?bSL&=B_B8$h)^WCe5>zmvBTV8L}r4{2)Ir zu;DE0%o%yoJSh^uW=FX|{%fC9&W(7H_T;J|S~H9_M3IA?cB|p1-0ltE)Myjj=xQn- z0QdJW=koQ}Cecx5-bN?MbMEj2CfMalo_PnIBA$)?yz9`RyL~T?4^~!NQ6mI4@pS~> z?(F!nBbo1RjehyfQ*y#;9kNPi^la35#mIT@lw{L5VitQkY4WhPpw+PsLM}Q{sG>%H>ozQGczNp(ix@f1Q7$1O(dN2j&NxLz2Hz*oeP%2k!BV9RlTO+BL7d(vy0PN6$xVwPz%4>@~F&UT$`C@}fMw zi^m??rVYH06Yowq6poK)l7nMq7H<CECOO5&1}IE`C> z6xRN`>VTvSDE&!KZ@>214+YyJ=bqT49HvK_rb0+s4kI}>8Zx2D;ULwOBwwfEH(kCp z7L=Uqzx7{}6aOQOODD`R#+}H5RtA^whBV6Aw+=N*Iq+eDq~F@K_DQsMSFMaRjB56M z5hR3&ACjd`Lyp?QryV6HuN`qdK#7|?i}#OCM7}QP>&hS}r876*?Icu`Tw^rl|FTb} z=u#Lywg=Q=n>jG)sCTrD?ALH`4s@IZ(Qf9%hlSLFfU<<7AadF|7rIRSsU_nmUh6@) z7FvQ;R3>;Ld=c5PTxAFtBzkuM!+eOtOFz^+vj5Zq9+Ynyn{8jX=4@yYZe^E+lzmyYsj#ARY^G_PV z>u_5qS zvzT)j;!W#c{4@Zq6)B+p)^>3)pCOc0R%L|#nCf71AYbXF?W7K2VgZ~s7q17lt!2uv z#W&R85iHzmE}xle%TDR3(kGc|$!<3;XspF)`15Po*#WKTMrT>+i6&gRvnpIK`@urV zFL0{O$b&8$74fQUl5$C|e#$BsLal3_LN2`!InDr&(U5wl?>df>n=X=#TZve^b<;@; zLMhVpM$0uuY2^>sg}nP)H1G^kIVDiQIhRsJY@{~A7?e^Y+--xFaia7U@XBq^&eIhMsVBLAD>U*z>nX#c*l>wgGkUj zO>}6Xf@c=*Ie8o&%9f2XxRRnDG6sYH9=llyB4}o2*$yqGcC^bqYeep^w3OCDDxOZN ziS`{6tTqghJ>XyovTV(T=#!=r`33In55IqRVe9Um#pN>DvTr-&5aQ!*8eH#WOPT`f z2x<%3ZYd>gUtJQDjj7-)GYj66> zcusHqyS~2$iULph?vsn_WYUyo;o|8gp!alrOnm#r z*>XAp48z!$^eeRlA)yy4A7$q26aHF6Cb*;dy|&PlYPj>UXSW1kU9x1X*%bjUmf`qG zjV1aGWjx8w@XDr+FTX;f&mPg491nN|84diduFiF>Z+q%3BA51LY)Sj1f~X6E&?JV1 z(R{T|dS8vBUDRu-`GWYZ`mdiS)e#A(jVIk*vuYCjRHt?y5kl?#3#+G*seAX4zAVBi zJ5R~B{5i>{3M1Y{j?K2!_R-7~Nu(UP0viSLfW52zuI?&oZgO=i<_Ax;Do@+3In`eC z52q1E$~s53X@RKelWk8L^-s(4DI;*ME!k|Tf>Z@`k$`4>zDBni)p==S3Mv9!aCCN?Y3P#sTS!hbkz7vL8-+RR>0a@|ch1!XaI0lk&EUiSpkn&8 zO9Z!%-~yNBP5tLSi`zJFvDG5Y$46VNRj~;Z?bjvno0;#K5e)Fchkg7tlrzrp+v!od zGg9U_an<%g(_PX!`j+9p`P$)??Dd@=J@}Uo+-ff?XTxDp`)`_VeR8z+%wLcTTikUW zU^IALZ_}}Rzp)p!DwZV8D^&!y=QB^;euT{1DFR4>XV!^j@_!3LQN)elOSDPSEtu^d z!F&slxP=0(;NDorO%I?{MWfHd7vtK#eG(T166@|>l%Y%k8dmJPj=L$^wG&ODUsa&h z(D**tA{-D@o7saWf8zfk{`n2#LKg@rtP*~QYKh~jT57&|Lo9^uE`c+|oPn>r-ipes1dt&rR zNT^u%&1!Tu#GLl{9P*?i(s#G_?C=sXxnB)~2KbAc*eRSmRa0=@Igcfb``(8}L~I*+ zp%_f`tr%4BQ6=xm^Q^1Hm7-uXGICl4hEb&%W8|Q}Oq@!bpuJ`k2}9l}-le0JiK|o_ zh6~tFz_rroL=I=kJ|;hoxk#gyG@j9075 z%P;YGfI@Cl92?Q6oe}u$&Egv_nRBTJUkauV?}QP(7=}-$`OF=y`h^S+J)gDL)R)aF zSPx#Z?qviggS$5p*7 zB%PZ_z*;3TPoMixbl1|2QAacK4bMssFs<$E=%-#mMlT5gdbw?Ao`1UJ9-;@hSpRNU zm++Q`VWnh|JQ0{PY8awup8j!L_D=|~TKI%&Wgo})n~6IK9>}Ad2rDK~h|oF*v-CB0 zx{Fr(z*`*8koMV3NwQop(xH!FYjn$-66Y40nxA4145bE}*MZ3qJcqC=!bx%-`^H@x z8Y{<>BWr^D>4die=_km1XRxDhS$00X{JUXqG+Iy)!&u8V9AqLU)bu*tK=$r`Vh2}W zzKsqeTf-%F*7w(4c(M6~LG^&rf-MnMhSLec{WxJQO#|~_^N%ym(+AHJ&Qegfo_CU3 zFyTLAL8p}eY54@x!S8vlUlq!2^UWtSRqcG&?3?d}nd`p|PW!FV6P0e`X4#rNJGlK> z?cFkna=Qkcj*0Zsr%Bpcag!Ou)U|nsAAPr~d(lV2QfTrMYa%H-K;5{0UQg)^OOGFJIe!v3%^f z+|S^L%xVV)V5`|gh<`cr(YC>LPkL?wIE?*_W34O2g+xoMJLGjtjG$U!%>>|y%@in+ zg9;+sHGv9|&}(=eP+Ik5!2%Whp|S1D_L!4_Zu9X!e#L^QDy!uz#-zeYcCYe0uR#Wj zsm zL_rG6Sl+0x?1ojT9s<#fosE_DG~4~&SCWDlN~m5AuT`0#k^@>Hs#u}Jj!i0-EFKhX z|BV&H)zFk~^3@iqWyK9LFk#zcsH}8|c>b^lo6t>{jjtDE|Rm6r>y=VU=Oh%B)TPE#ZU!`@|?1 z`&xiPP52%!Zobw@+PJEda4xcJE#lxwQdCBt?o@x$6ptQvL{TNm94+EsY!$j{bE^wS zBwmINAo1z%BDNIY*0s~{Q(^YP(-96DvDa<8MqyYYD#x#cmn!?w8D>q)I$+mYq&5O~$(oJ6=Z!tVIi7f3R7Z#SS_!-0hq6cf1!iMRji1`uCn-&~4jbRjkXSZ{v{D>g-MzyxKSqv5$)CgQWV%Rs zNYFK%)@-3Mw5DTK2!t0&HyVF3<<=ZMTFcewWGppgAh{;afkZ`fA?UoR!Gx?0wLj$? zeEJnF%Y|s@9v*aY&4lS1Z~I>#Qo?_)U+fqF^G%)^UG95P9U~(J#*R{hDTp&1Zs%YB zl;XYiFip(wxT)?zX;s%iWc^9Co{$>C` zOQq)98^1b}YG;V+!ID0aIFY82LrWUN*NBb?|D1;O%Yv9>Po{-Al*&wAD4o-_YIV)0@ zuRAQ)n&hBMp`8VLMJT%pn=Rf#PEFxu9CAWEenK=n?1B;@jRikA1ZU=wkfhT|1M>)3 ziVsX!jhskAyxHtbcYrWJ%Q&YRdb@h@g0Pt2kD&ZZsq=+D39VT*VGP0O^{l4y4BFO} zo8=QSENvqV4M?3rJG9%F1K8S?FdTO4gsD1AForF&1`{5|8U|1dG4b`N{yMd|THDp0 z9I!&cliV){C3G3m)83`7Mu~6_ZDRK<9B~26Ll1o6{I0|BL4dl1b)fIy3FD?L-TTs- zJfxX02)u)q!Quz2hNZ^;$py0 zA^G^IA#_j}h6^TL97+`<01+CSN3;A3VM_fHJV=I&gKe@~=Pkp%o^LrAuFvc1=RL+1 zPM-B(TTDH`2m!d((S-QRLA78wmIKbIx_b)>3p}3``<{FOHJ#fhYa%aX9LlCz6qG&N z9ad0+A9u6sX!<997E-B=*1z=%kEecFZhh1ap^KT}t(m(U9x7^PR zO&aIQ?uJQ@Es9v!gAjx^7UToU4rKyAfC8p)oeobu#O~ak3T;cr&n>=q7Fq+10#ju0bL!#8 z*{o_8raH1!K@vFLjVP^OvClBfCtO}SzFx|g_jwnyRoM?ZM&btk8FiPI(*wB)MU4+z z%;5+4Gx_Nlr)h&I9r7vP*2kI$JpZQnbG=U8WsaOto4@S2LU1YmNz*D?ewgH<5jw^JFL3q@_;^g8B}#%p`E&S@TCe4w zw~F!K=+83iMT$0mCW={o@y6_m5;^3$mf$_0UGBHA<+@-f8yjF={ck+6!B2kFm8L3+ zj?N@3trrl9aF>5c(mgQT>}+E0-tk~91`9s$uP(h7ijN9#Au2C+8tLb2;+=*F1@&>& zh?4-|#@B+W_7P*fbd+v;R>vLn@mG++Alin27Gq8< z`R}^uUULA_zbwA$E zNLv?L{)2OWr^G}&`bfqdgwDz0xM{u@b2rn!q^`t7W8JZbI&>%;!LT4R4rVX}JU~YyOHw(Xb$qST?g7{>7wrf}}Fek@qX3-pkxC+KJ~p4e_4v=Nvv^5cM1~OB2djtg$-gHwmx5kK|cOpwvoDug-ZYnt%wS8puQp5 zU2f?Zj6iGInBMoLPkzqrJw*I2;0XKx@$|1frjWl>A)~or}cooaazPwF(_InKtmR7XEgbaBZnF`lP(ugBe#LAqC9EEhw(C{K8s9Oz#W)T{3P z;6=*Ln2v0~Si$LR`&Z9e5UpHIXTh!`=KcrP8yftYxauV5`# zYD5lLdz=vrF8E=Xid&P@9wl|x7DV!iOs=x`aQwp(TAp>-)E>#x)5TKj9h$KgEJ}cY zP~{C*AzDO7or>WJvV+aM$wd1Oc8lNW5wNZ-Fj4D(&IWimpNkz_H4la@ zMCbdD)F5A0|DvLy6@UEjL3X+6$ygXkU-Q?vtg)_jsay$B-KxKm zbVJe}u2iYaUpz>dv{*(@(@}alzkc6KVDGrG-n=brY+vo(7%{amYGRL~^htWpQ{A|g zI=|DhabImEJSh*q0|t3C?u;7|?k}#jUF{`poVIR2tj?XQ8ktr!aL>vk*hl3b$u%q6 z9t&KaD_m|HT)3Asa8Jv>-7gH#43gP+y^gecAFg*CYqg)QBi{id@2Jd7{f{;Cs~e}fHb!-gOlunWHbFFBA%FgV zNj5~4GoW#r^QZsV$o_u}+2893Ko|Y*kNUq1ySAu|n??TjyXvTagH=exwTzx);0MO4 z9=*&{v8d*#W;c!_F^>}B_cs)ur_)CM_uHx>!2ZR-CEjgp*ZjRe~S)3tm`y%i=};I$02=jicyMb@#) zG(;+3t7*kN^hl3n-bAhQU0@)&9|Gy9pg+nc2{nK^Qil@2?%betj<~Z+(e0> zF<(x!lz4|7$F=;~*i^+==bFB?Tc?bu-@|B_$x#=*+w6mM^H~KK!|p+ZC8-M9(f)aHpqzmFicb0rAdbvcuU%)P&mbKlR_Wk zvS=$hK7HAu{S~mKxbYm2(jQGqyqb6w#~JbTI5GR*VKbZWuU0S52Vw8GJ1ALU*Y9uq NLGjbq=6x`q{vQt@*LDB^ literal 0 HcmV?d00001 diff --git a/research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/EXPECTED.json b/research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/EXPECTED.json new file mode 100644 index 0000000000..c89fe719e8 --- /dev/null +++ b/research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/EXPECTED.json @@ -0,0 +1,102 @@ +{ + "nine": { + "bit_lower": "396861776523137034277/250000000000000000000000", + "bit_lower_decimal": "0.001587447106092548137108", + "profile": { + "1": 345600, + "2": 430080, + "4": 67200, + "6": 49920, + "7": 23040, + "8": 2592 + }, + "bit": { + "live": 30720, + "physical": 30729, + "maxchild": 8, + "rank_mass": 1956096, + "hosted_residuals": 138240, + "bank_assignments": 0, + "prefix_bits": 193, + "charged_prefix_bits": 256, + "coefficient_bits": 193, + "two_level_coefficient_bits": 425, + "charged_two_level_coefficient_bits": 512, + "row_coefficient": 18355, + "halving_depth": 2 + }, + "kappa": { + "pr484": { + "kappa": "662515748707749/500000000000000000", + "kappa_decimal": "0.001325031497415498", + "binding_component": "complex", + "complex_lower": "663394767670133/500000000000000000" + }, + "pr471": { + "kappa": "260706019542937/200000000000000000", + "kappa_decimal": "0.001303530097714685", + "binding_component": "complex", + "complex_lower": "1305231506267591/1000000000000000000" + }, + "pr458": { + "kappa": "323880830669511/250000000000000000", + "kappa_decimal": "0.001295523322678044", + "binding_component": "complex", + "complex_lower": "1297203880559577927527/1000000000000000000000000" + } + }, + "nine_stage_payload_bits": 102, + "stages": 9, + "replicas": 32, + "m": 64 + }, + "parent_r480": { + "pr": 503, + "bit_lower": "393688338238452162729/250000000000000000000000", + "bit_lower_decimal": "0.001574753352953808650916", + "stages": 11 + }, + "parent_r489": { + "pr": 493, + "bit_lower": "391360506745414227149/250000000000000000000000", + "bit_lower_decimal": "0.001565442026981656908596", + "stages": 11 + }, + "findings": { + "lp_helpers_per_4frame": 16, + "lp_total": 480, + "levels": { + "1": { + "frames": 120, + "helper_visits": 240 + }, + "2": { + "frames": 180, + "helper_visits": 480 + }, + "4": { + "frames": 60, + "helper_visits": 360 + }, + "6": { + "frames": 180, + "helper_visits": 480 + }, + "7": { + "frames": 120, + "helper_visits": 240 + } + }, + "cells": { + "2,2,0": 60, + "3,0,0": 120 + }, + "d6": { + "2,1,2": 60, + "3,0,0": 120 + }, + "pivots_max": 0, + "line_classes": 120, + "class_nullity": 0 + } +} \ No newline at end of file diff --git a/research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/code/adapt.py b/research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/code/adapt.py new file mode 100644 index 0000000000..7c595c50bc --- /dev/null +++ b/research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/code/adapt.py @@ -0,0 +1,25 @@ +# Writes the one adapted file this package runs, from the vendored PR #463 audit_parent_k.py, by exact string edits +# (each anchor must occur exactly once). Prepared by DreamingOfClouds with Claude Opus 5.5 (Anthropic), via Claude Code. +# Apache-2.0. +"""usage: adapt.py VENDORED_CODE_DIR OUTDIR -> OUTDIR/audit_parent_k.py +Edit 1: HERE points at the vendored code directory (the adapted copy lives outside the package). +Edit 2: the template control 'omit a residual route factor' mutates the first residual isometry that HAS a route + factor (a non-empty gaussian_reduction). In R480 every helper enters at frame zero, so the only isometry is the + identity with no route factor, the selector raises StopIteration and the audit cannot start. The control is + vacuous then (there is no factor to omit) and is skipped exactly in that case; all other controls run.""" +import sys +from pathlib import Path + +code, out = Path(sys.argv[1]).resolve(), Path(sys.argv[2]).resolve() +text = (code / 'audit_parent_k.py').read_text() +EDITS = [ + ("HERE = Path(__file__).resolve().parent", "HERE = Path(%r)" % str(code)), + (" rejects('omit a residual route factor',lambda q:next(r for r in q['residual_isometries'] if r['gaussian_reduction'])['gaussian_reduction'].pop())", + " if any(r['gaussian_reduction'] for r in t['residual_isometries']): # vacuous when no isometry has a route factor\n" + " rejects('omit a residual route factor',lambda q:next(r for r in q['residual_isometries'] if r['gaussian_reduction'])['gaussian_reduction'].pop())"), +] +for old, new in EDITS: + assert text.count(old) == 1, old + text = text.replace(old, new) +out.mkdir(parents=True, exist_ok=True); (out / 'audit_parent_k.py').write_text(text) +print(' adapted audit_parent_k.py with %d listed edits (each anchor found exactly once)' % len(EDITS)) diff --git a/research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/code/assemble9.py b/research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/code/assemble9.py new file mode 100644 index 0000000000..238226c568 --- /dev/null +++ b/research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/code/assemble9.py @@ -0,0 +1,66 @@ +# Nine-stage bit price and cited outer assembly. Bit half: the nine-stage invoice of PR #463 code/assemble_k.py (verbatim apart from +# dropping its complex half); outer half: PR #493 code/eleven/assemble.py's per-supplier loop (verbatim). Prepared by DreamingOfClouds +# with Claude Opus 5.5 (Anthropic), via Claude Code. Apache-2.0. +# usage: assemble9.py BASE BIT_DIR AUDIT.json PR493_PACKAGE OUT.json SUPPLIER[,SUPPLIER...] +import importlib.util, json, sys, math +from fractions import Fraction as Q +from pathlib import Path +base, bd, ap, root, outp = map(Path, sys.argv[1:6]); names = sys.argv[6].split(',') +eng = root / 'code/hub/e8-r740-replica32/code/engines' +def load(name): + sp = importlib.util.spec_from_file_location('e_' + name, eng / (name + '.py')); mod = importlib.util.module_from_spec(sp); sp.loader.exec_module(mod); return mod +M, Other, Outer = load('moment'), load('base_two_moment'), load('outer') +cited = {s["name"]: s for s in json.loads((root / "data/complex-b.json").read_text())["suppliers"]} +audit = json.loads(ap.read_text()); bg = json.loads((bd / 'GLOBAL.json').read_text()) +assert audit['template_sha256'] == bg['template_sha256'] and audit['status'] == 'PASS_INDEPENDENT_REPLICATED_HOSTING_AUDIT' +P = {int(r): n for r, n in audit['profile'].items()}; m, W = bg['m'], audit['live']; E = sum(P.values()) +geo = json.loads((base / 'nine/GEOMETRY.json').read_text()); pg = json.loads((base / 'nine/GLOBAL.json').read_text()) +T, R, V = pg['replicas'], pg['R'], 120; L = (m - 1) // 7; K = (L - 1) // 2; assert L % 2 == 1 and m == 1 + 7 * L +DEPTH = bg['halving_depth']; DEFICIT = T * (2 * K * V - 9 * 8 * L) +assert bg['m'] == m and bg['replicas'] == T and bg['R'] == R and bg['deficit'] == DEFICIT +mass = sum(r * z for r, z in P.items()); assert m * W - mass == DEFICIT +physical = W + 9; d = m * m; N = 2 * m +unit = L * T * pg['local_additions'] + pg['bridge_additions'] +J = 8 * T * L * (2 * V + R) + 8 * (2 * K * V * T) + 2 * L * T * R + 2 * pg['copy_erase_episodes'] +high = 16 * (d + 1) ** 2; good = 8 * m * m + 8; compiler = 128 * N ** 3 +selectors = 2 * L * T * ((W - 1) + R * m * (geo['max_route_factors'] + 144 + 2 + (m - 1) + m)) +prefix = int(pg['global_prefix_bound']); charged_prefix = 64 * ((prefix.bit_length() + 63) // 64) +coefficient = unit + J * high + J * physical + E * (good + compiler) + 16 * m ** 3 + pg['copy_erase_episodes'] + selectors + prefix + 1 +two = (coefficient + 1) ** DEPTH * (E + 1) ** DEPTH; charged_two = 512 if two.bit_length() <= 512 else 64 * ((two.bit_length() + 63) // 64) +assert two < 1 << charged_two and 2 * max(P) ** DEPTH < m ** DEPTH +row = DEPTH * (m * m + 1) + 9909 + 252; assert row == 18355 and row < 20161 +root_ = M.certify(P, m, W, True); c = Q(root_['lower']); chi = Q(root_['upper']) +fb = 32 * m * m * E +lo, hi = Other.moment(m, W, list(P.items()), c); bl, bh = Other.moment(m, W, [(1, fb)], c); assert hi + bh / 10 ** 16 < 1 +lo2, hi2 = Other.moment(m, W, list(P.items()), chi); bl2, bh2 = Other.moment(m, W, [(1, fb)], chi); assert lo2 + bl2 / 10 ** 16 > 1 +res = dict(status='PASS_NINE_STAGE_WHOLE_HOSTED_BIT_PRICE_AND_CITED_ASSEMBLY', R=R, stages=L, replicas=T, bit_lower=str(c), bit_lower_decimal=root_['lower'], + next_grid_point_rejected=str(chi), bit=dict(live=W, physical=physical, maxchild=max(P), rank_mass=mass, hosted_residuals=audit['hosted_residuals'], + bank_assignments=audit.get('bank_assignments'), prefix_bits=prefix.bit_length(), charged_prefix_bits=charged_prefix, coefficient_bits=coefficient.bit_length(), + two_level_coefficient_bits=two.bit_length(), charged_two_level_coefficient_bits=charged_two, row_coefficient=row, halving_depth=DEPTH), + bit_profile={str(r): n for r, n in sorted(P.items())}, emitted_program_sha256=bg.get('emitted_program_sha256'), kappa={}) +def ceil(x): return -((-x.numerator) // x.denominator) +for name in names: + b = Q(cited[name]["b"]) + eta = beta = Q(1, 10 ** 18); grid = Q(1, 10 ** 18); s = min(c, (1 - beta) * b) - Q(1, 10 ** 24) + assert 0 < s < c and s < (1 - beta) * b + chain = [Q(384599, 10 ** 10)]; cutoffs = [] + for j in range(6): + old = chain[-1]; a = (1 - s) * s + s * old + gap = min(s - a, 1 - a - s, 1 - a - s * (1 - old), 1 - s); assert old < a < s < 1 - a and gap > 0 + cutoff = max(ceil(36 / gap ** 2), ceil(2 * (4 + charged_two) / gap)); assert cutoff * gap ** 2 >= 36 and cutoff * gap >= 2 * (4 + charged_two) + cutoffs.append(dict(gap=str(gap), cutoff_log2=str(cutoff))); chain.append(a) + q = chain[-1] * (1 - 2 * eta); margin = (1 - eta) * q / (1 + q); ticks = margin / grid; k = grid * ((ticks.numerator - 1) // ticks.denominator) + bridge = {'semantic': {'C0': {'base': 2, 'exponent': 210000}, 'strict_literal_gap': 1}, 'rows': {'degree_gap': Q(10 ** 6) - Q(51 * 20161, 25)}} + a = Outer.assembly(chain[-1], b, bridge, k, eta=eta, beta=beta) + assert len(a['strict_constraints']) == 47 and all(x > 0 for x in a['strict_constraints'].values()) + controls = [] + for cname, kk, ee, bb in [('next_grid', k + grid, eta, beta), ('zero_eta', k, Q(0), beta), ('zero_beta', k, eta, Q(0)), ('unit_beta', k, eta, Q(1))]: + try: Outer.assembly(chain[-1], b, bridge, kk, eta=ee, beta=bb) + except AssertionError: controls.append(cname) + else: raise AssertionError(cname) + res['kappa'][name] = dict(complex_lower=str(b), kappa=str(k), kappa_decimal=format(float(k), '.18f'), + binding_component='bit' if c < (1 - beta) * b else 'complex', strict_outer_constraints=47, outer_controls=controls) +outp.write_text(json.dumps(res, indent=1) + '\n') +print(' nine-stage bit c = %s = %s; profile %s; prefix %d bits, two-level %d bits (charged %d), row %d, depth %d' + % (c, root_['lower'], res['bit_profile'], prefix.bit_length(), two.bit_length(), charged_two, row, DEPTH)) +for name, x in res['kappa'].items(): print(' %s: b %s kappa %s (%s-bound) 47/47, controls %s' % (name, x['complex_lower'], x['kappa_decimal'], x['binding_component'], ','.join(x['outer_controls']))) diff --git a/research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/data/selection9-r480.json b/research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/data/selection9-r480.json new file mode 100644 index 0000000000..786ba4d5be --- /dev/null +++ b/research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/data/selection9-r480.json @@ -0,0 +1,150 @@ +{ + "bit": { + "patterns": [ + [ + 0, + [ + 8 + ], + 240 + ], + [ + 1, + [], + 240 + ], + [ + 2, + [ + 8, + 8, + 8 + ], + 240 + ], + [ + 3, + [], + 240 + ], + [ + 4, + [ + 8, + 8, + 8, + 8, + 8 + ], + 240 + ], + [ + 5, + [], + 240 + ], + [ + 6, + [ + 8, + 8, + 8, + 8, + 8 + ], + 240 + ], + [ + 7, + [ + 8, + 8, + 8 + ], + 240 + ], + [ + 8, + [ + 8 + ], + 240 + ] + ], + "proxy_saving": 14347.659722612807, + "stages": [ + [ + 0, + 8, + 1, + 240 + ], + [ + 2, + 8, + 0, + 480 + ], + [ + 2, + 8, + 1, + 240 + ], + [ + 4, + 8, + 2, + 480 + ], + [ + 4, + 8, + 3, + 240 + ], + [ + 4, + 8, + 5, + 480 + ], + [ + 6, + 8, + 3, + 240 + ], + [ + 6, + 8, + 4, + 480 + ], + [ + 6, + 8, + 7, + 480 + ], + [ + 7, + 8, + 6, + 480 + ], + [ + 7, + 8, + 8, + 240 + ], + [ + 8, + 8, + 8, + 240 + ] + ] + } +} diff --git a/research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/vendor/e8-r715-eleven-stage-whole/code/audit_hosted_k.py b/research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/vendor/e8-r715-eleven-stage-whole/code/audit_hosted_k.py new file mode 100644 index 0000000000..8864ca9b94 --- /dev/null +++ b/research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/vendor/e8-r715-eleven-stage-whole/code/audit_hosted_k.py @@ -0,0 +1,157 @@ +# Independent ownership/window audit, extending DaysSky PR442 and chafreaky PR444 (OpenAI Codex); no generator imports. +# Generalised to an arbitrary odd stage count, any replica count and host-every-residual templates by DreamingOfClouds with +# Claude Opus 5.5 (Anthropic), via Claude Code. Apache-2.0. +"""Independent ownership/window audit of an idle-axis-hosted compressed S-stage bit template. +Usage: audit_hosted_k.py PARENT_DIR HOSTED_DIR OUT.json +PARENT_DIR holds the parent's records/ and nine/-style GLOBAL files via --parent-nine (default PARENT_DIR/nine). +At the nine-stage defaults this performs PR #444's checks and fourteen mutations; at S = 11 the mutations that need a +bank are replaced by stage-count, window and exterior-coordinate controls.""" +import copy, gzip, hashlib, json, sys +from collections import Counter, defaultdict +from pathlib import Path +V=120 +read=lambda p:json.loads(Path(p).read_text()) +template=lambda p:json.loads(gzip.decompress(Path(p).read_bytes())) + +def windows(schedule): + last={}; seen=[]; win={}; rank={}; pair={} + for pos,e in enumerate(schedule): + if e['kind']=='local_cover_sweep': + seen.append((pos,e['stage'],e['pair']));last[e['pair']]=pos + elif e['kind']=='bridge': + last[e['dst']]=pos;last[e['src']]=pos + elif e['kind']=='exchange': + assert pos==len(schedule)-1 + else: + assert e['kind']=='idle' + i=e['idx'];assert i not in win + win[i]={s for q,s,p in seen if last.get(e['pair'],-1)=5 and L%2==1 and M==1+7*L + mutable={'version','live','banks_per_stage','hosting','helper_route','calls','schedule'} + assert set(t)==set(parent)|{'hosting'} + assert {k:v for k,v in t.items() if k not in mutable}=={k:v for k,v in parent.items() if k not in mutable} + assert t['replicas']==T and t['m']==M and t['private_centre_reserve']==9 + sched=copy.deepcopy(parent['schedule']);idx=0 + for e in sched: + if e['kind']=='idle':e['idx']=idx;idx+=1 + assert t['schedule']==sched + W,rank,pair=windows(t['schedule']);hs=t['hosting'] + assert hs['windows']=={str(i):[pair[i],rank[i],sorted(W[i])] for i in W if rank[i]>0 and W[i]} + used=defaultdict(set);hosted={} + for i,cp,off,w,s,h in hs['slots']: + assert i in W and s in W[i] and 0<=s0 and 0<=off and off+w<=rank[i] + cells=set(range(off,off+w));assert not cells & used[i,cp];used[i,cp]|=cells + assert (s,h) not in hosted;hosted[s,h]=(i,cp,off,w) + rem={};summaries={} + for i in rank: + if not rank[i]:continue + for cp in range(2*V):rem[i,cp]=rank[i]-len(used[i,cp]) + if str(i) in hs['windows']: + summaries[str(i)]={str(r):n for r,n in sorted(Counter(rem[i,cp] for cp in range(2*V)).items())} + assert hs['remainders']==summaries + banks={int(s):n for s,n in hs['banks_per_stage'].items()} + assert set(banks)==set(range(L)) and all(isinstance(n,int) and n>=0 for n in banks.values()) + assert {int(s):n for s,n in t['banks_per_stage'].items()}==banks + base={};end=DATA + for s in range(L):base[s]=end;end+=banks[s] + assert t['live']==end + assert len(t['calls'])==L*T and len(parent['calls'])==L*T + occ=defaultdict(set);bank_count=0;host_count=0 + assert {(c['stage'],c['replica']) for c in t['calls']}=={(s,r) for s in range(L) for r in range(T)} + for c,pc in zip(t['calls'],parent['calls']): + assert {k:v for k,v in c.items() if k!='owners'}=={k:v for k,v in pc.items() if k!='owners'} + s,rep,p=c['stage'],c['replica'],c['pair'];own=c['owners'] + assert len(own)==2*V+R and own[:2*V]==pc['owners'][:2*V] + for h,(f,r) in enumerate(own[2*V:]): + if (s,h) in hosted: + i,cp,off,w=hosted[s,h];side,port=divmod(cp,V) + assert r==['hosted',i,off,w] and f==rep*(2*K*V)+(2*pair[i]+side)*V+port + assert pair[i]!=p and 0<=f2*max(paid)**depth + return paid,host_count,bank_count + +def digest(t): + # Independent streamed hashing uses the same specified JSON instruction format. + h=hashlib.sha256();n=0;R=len(t['calls'][0]['owners'])-2*V + for call in t['calls']: + for z in t['local_programs'][call['orientation']]: + x=[z[0],call['owners'][z[1]]] + if z[0]==0:x.extend(z[2:]) + else: + x.append(call['owners'][z[2]] if z[2]<2*V+R else [t['live'],['private',0]]) + x.extend(z[3:]) + h.update(json.dumps(x,separators=(',',':')).encode());h.update(b'\n');n+=1 + return h.hexdigest(),n + +def main(): + par,bd,out=map(Path,sys.argv[1:4]);pt=template(par/'nine/GLOBAL-TEMPLATE.json.gz') + t=template(bd/'GLOBAL-TEMPLATE.json.gz');g=read(bd/'GLOBAL.json');pg=read(par/'nine/GLOBAL.json') + st=read(par/'records/249-states.json');frames=read(par/'records/frames.json')['frames'] + L=t['stages'];T=t['replicas'];M=t['m'];K=(L-1)//2;R=pg['R'];depth=g['halving_depth'];DATA=2*K*V*T + widths=[8-frames[str(st['initial'][str(h)])]['dim'] for h in range(2*V,2*V+R)] + P,host,bank=audit(t,pt,widths,depth) + th=hashlib.sha256(gzip.decompress((bd/'GLOBAL-TEMPLATE.json.gz').read_bytes())).hexdigest() + assert th==g['template_sha256'] and g['parent_template_sha256']==pg['template_sha256'] + assert P==Counter({int(r):n for r,n in g['profile'].items()}) + DEFICIT=T*(2*K*V-9*8*L) + assert (g['live'],g['physical'],g['rank_mass'],g['deficit'],g['paid_calls'],g['maxchild'])==(t['live'],t['live']+9,sum(r*n for r,n in P.items()),DEFICIT,sum(P.values()),max(P)) + assert g['hosted_residuals']==host and g['bank_assignments']==bank + dg,n=digest(t);assert dg==g['emitted_program_sha256'] and n==g['emitted_events']==pg['emitted_events'] + assert dg!=pg['emitted_program_sha256'] + controls=[] + def reject(name,fn): + q=copy.deepcopy(t);fn(q) + try:audit(q,pt,widths,depth) + except (AssertionError,KeyError,IndexError,TypeError):controls.append(name) + else:raise AssertionError('accepted '+name) + def owner(q,kind): + return next(o for c in q['calls'] for o in c['owners'][2*V:] if o[1][0]==kind) + Wd,rk,pr=windows(t['schedule']) + def outside_stage(q): + i,cp,off,w,s,h=q['hosting']['slots'][0]; q['hosting']['slots'][0][4]=next(x for x in range(L) if x not in Wd[i]) + reject('slot outside window',outside_stage) + reject('copy index outside family',lambda q:q['hosting']['slots'][0].__setitem__(1,2*V)) + reject('negative slot offset',lambda q:q['hosting']['slots'][0].__setitem__(2,-1)) + reject('wrong residual width',lambda q:q['hosting']['slots'][0].__setitem__(3,1+q['hosting']['slots'][0][3])) + reject('duplicate helper',lambda q:q['hosting']['slots'].append(q['hosting']['slots'][0])) + reject('omitted helper',lambda q:q['hosting']['slots'].pop()) + reject('hosted owner in another replica or outside the data stock',lambda q:owner(q,'hosted').__setitem__(0,owner(q,'hosted')[0]+2*K*V)) + reject('hosted owner on a wrong data family',lambda q:owner(q,'hosted').__setitem__(0,q['calls'][0]['owners'][0][0])) + if bank: + reject('wrong bank slot',lambda q:owner(q,'bank')[1].__setitem__(1,M)) + reject('wrong bank stage',lambda q:owner(q,'bank').__setitem__(0,t['live']-1)) + reject('wrong call orientation',lambda q:q['calls'][0].__setitem__('orientation','reverse')) + reject('duplicate call',lambda q:q['calls'].__setitem__(1,q['calls'][0])) + reject('changed idle rank',lambda q:next(e for e in q['schedule'] if e['kind']=='idle').__setitem__('rank',1)) + reject('understated live stock',lambda q:q.__setitem__('live',q['live']-1)) + reject('missing remainder summary',lambda q:q['hosting']['remainders'].pop(next(iter(q['hosting']['remainders'])))) + reject('stale stage count',lambda q:q.__setitem__('stages',L-2)) + reject('hosted coordinate beyond the pending set',lambda q:q['hosting']['slots'][0].__setitem__(2,rk[q['hosting']['slots'][0][0]])) + reject('overlapping hosted coordinates',lambda q:q['hosting']['slots'].append(q['hosting']['slots'][0][:5]+[(q['hosting']['slots'][0][5]+1)%R])) + res=dict(status='PASS_INDEPENDENT_REPLICATED_HOSTING_AUDIT',template_sha256=th,emitted_program_sha256=dg,emitted_events=n, + profile={str(r):z for r,z in sorted(P.items())},live=t['live'],physical=t['live']+9,hosted_residuals=host, + bank_assignments=bank,rank_mass=sum(r*n for r,n in P.items()),deficit=DEFICIT,maxchild=max(P),mutations_rejected=controls) + out.write_text(json.dumps(res,indent=2)+'\n');print(json.dumps(res)) +if __name__=='__main__':main() diff --git a/research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/vendor/e8-r715-eleven-stage-whole/code/audit_parent_k.py b/research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/vendor/e8-r715-eleven-stage-whole/code/audit_parent_k.py new file mode 100644 index 0000000000..ee54ca566f --- /dev/null +++ b/research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/vendor/e8-r715-eleven-stage-whole/code/audit_parent_k.py @@ -0,0 +1,165 @@ +# Explicit residual-bank audit added by chafreaky with OpenAI Codex assistance (PR #443). +# Generalised to an arbitrary odd stage count, any replica count and the bank-free parent by DreamingOfClouds with +# Claude Opus 5.5 (Anthropic), via Claude Code. Apache-2.0. +"""Independent projector, ownership and emitted-template audit of the PARENT (unhosted) compressed S-stage template. +PR386 audit by Zhihao Chen with OpenAI Codex assistance; PR436 generalization by eumemic with Anthropic Claude +assistance; PR439 routes and the replication extension by chafreaky with OpenAI Codex assistance. +At S = 9 with banks this is PR #443's audit_kernel.py (same checks, same nine-and-fourteen controls); with --no-banks +templates the bank occupation checks are replaced by 'no helper owner is present' checks.""" +import argparse, copy, gzip, hashlib, json, struct, sys, time +from collections import Counter, defaultdict +from pathlib import Path +import sympy as S +HERE = Path(__file__).resolve().parent +sys.path.insert(0, str(HERE)); sys.path.insert(0, str(HERE.parents[1] / 'e8-r740-replica32/code')) +from bank_routes import validate_isometries + +def sha(p): return hashlib.sha256(p.read_bytes()).hexdigest() +NAMES={5:'FIVE',7:'SEVEN',9:'NINE',11:'ELEVEN',13:'THIRTEEN'} + +def main(): + ap=argparse.ArgumentParser(); ap.add_argument('--local-check',type=Path,required=True); ap.add_argument('--out',type=Path,required=True) + ap.add_argument('--word',type=Path,required=True) + a=ap.parse_args(); start=time.monotonic(); out=a.out + blob=gzip.decompress((out/'GLOBAL-TEMPLATE.json.gz').read_bytes()); t=json.loads(blob) + g=json.loads((out/'GLOBAL.json').read_text()); geo=json.loads((out/'GEOMETRY.json').read_text()) + assert hashlib.sha256(blob).hexdigest()==g['template_sha256'] + assert t['geometry_sha256']==sha(out/'GEOMETRY.json') and t['source_record_sha256']==sha(a.local_check/'records/249-records.bin') + fs=json.loads((a.local_check/'records/frames.json').read_text())['frames'] + records=list(struct.iter_unpack('<6i',(a.local_check/'records/249-records.bin').read_bytes())) + L=t['stages']; T=t['replicas']; K=(L-1)//2; M=t['m']; NOBANK=t['banks_per_stage'] is None + assert L>=5 and L%2==1 and M==1+7*L and g['replicas']==T and g['m']==M + assert geo['status']=='PASS_COMPRESSED_%s_BIT_GEOMETRY_AND_BANK_BLOCKS'%NAMES[L] and geo['stages']==L and geo['m']==M + V,R=g['v'],g['R']; LOCAL=2*V+R; DATA=2*K*V*T + assert V==120 + DEFICIT=T*(2*K*V-9*8*L) + LIVE=DATA if NOBANK else g['live'] + assert g['live']==LIVE + I=S.eye(8); proj={}; dimensions={} + for key,f in fs.items(): + k=int(key); B=S.Matrix(f['B']) if f['B'] else S.zeros(0,8) + P=B.T*(B*B.T).inv()*B if B.rows else S.zeros(8) + assert P.T==P and P*P==P and P.trace()==f['dim'] + proj[k]=P; dimensions[k]=f['dim'] + pairs={} + for op,x,y,c,r,z in records: + if op==0: + key=(y,c) + if key not in pairs: + D=proj[c]-proj[y] + assert proj[c]*proj[y]==proj[y] and proj[y]*proj[c]==proj[y] + assert D*D==D and D.trace()==r + # Reflected reverse has exactly the same positive projector. + assert (I-proj[y])-(I-proj[c])==D + pairs[key]=r + assert len(proj)==len(fs) + print('exact frame projectors',len(proj),'nested pairs',len(pairs),flush=True) + expected={} + for rev in (False,True): + es=[] + for op,x,y,c,f,z in (reversed(records) if rev else records): + if op==0: es.append([0,x,c if rev else y,y if rev else c,f,int(rev)]) + elif op==1: es.append([1,x,y,-c if rev else c,f,int(rev)]) + elif op==(3 if rev else 2): es.append([2,x,y,c,f,abs(dimensions[c]-dimensions[f]),int(rev)]) + else: es.append([3,x,y,c,f,z,int(rev)]) + expected['reverse' if rev else 'forward']=es + assert t['local_programs']==expected + + word=json.loads(gzip.decompress(a.word.read_bytes()) if a.word.suffix=='.gz' else a.word.read_bytes()) + assert sha(a.word)==g['word_sha256'] + state=json.loads((a.local_check/'records/249-states.json').read_text()) + initial=[state['initial'][str(i)] for i in range(LOCAL)] + assert initial==word['start'] + widths=[8-dimensions[f] for f in initial[2*V:]] + ENT=sum(8-x for x in widths) + if not NOBANK: + assert T*(8*R-ENT)%M==0 + NB=T*(8*R-ENT)//M + assert LIVE==DATA+L*NB and g['banks_per_stage']==NB + + def validate(q,check_profile=True): + assert validate_isometries(q['residual_isometries'],word['frames'],word['lines'],dimensions)==set(initial[2*V:]) + assert (q['m'],q['live'],q['replicas'],q['stages'])==(M,LIVE,T,L) + assert q['bank_padding'] is None and q['local_programs']==expected + hist=Counter(); occupation=defaultdict(set); calls=set() + for c in q['calls']: + st,rep,p=c['stage'],c['replica'],c['pair']; rev=c['orientation']=='reverse' + assert 0<=st 0 and replicas % 8 == 0 + by_width = {} + for rep in range(replicas): + for helper, width in enumerate(widths): + assert 0 < width <= 8 + by_width.setdefault(width, deque()).append((rep, helper)) + patterns = tile({w: len(q) for w, q in by_width.items()}, m) + result = {} + bank = 0 + for widths_in_bank, count in patterns: + for _ in range(count): + offset = 0 + for width in widths_in_bank: + key = by_width[width].popleft() + result[key] = (bank, offset, width) + offset += width + assert offset == m + bank += 1 + assert all(not q for q in by_width.values()) + assert len(result) == replicas * len(widths) + return result, bank diff --git a/research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/vendor/e8-r715-eleven-stage-whole/code/emit_k.py b/research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/vendor/e8-r715-eleven-stage-whole/code/emit_k.py new file mode 100644 index 0000000000..b84c795a04 --- /dev/null +++ b/research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/vendor/e8-r715-eleven-stage-whole/code/emit_k.py @@ -0,0 +1,130 @@ +# Adapted from DaysSky PR442 (Claude assistance) and chafreaky PR444 (OpenAI Codex), Apache-2.0. +# Generalised to an arbitrary odd stage count and to host-every-residual by DreamingOfClouds with Claude Opus 5.5 +# (Anthropic), via Claude Code. At the nine-stage defaults with PR #444's frozen selection the emitted +# GLOBAL-TEMPLATE.json.gz equals PR #444's byte for byte. +"""Idle-axis hosting for a compressed S-stage bit template (m = 1+7S). +Reads the emitted parent template, keeps every local program and scalar replay unchanged, and moves whole helper +residuals from banks onto pending idle-advance coordinates of untouched data families. Emits a new template and +GLOBAL.json with the new live stock and paid profile. +usage: emit_k.py PARENT_GLOBAL_DIR LOCAL_CHECK_DIR OUT --selection FILE --key KEY [--discover] [--host-all] [--cap N] + [--depth D] [--version-tag TAG]""" +import argparse, gzip, hashlib, json, sys, time +from collections import Counter +from pathlib import Path +HERE = Path(__file__).resolve().parent +sys.path.insert(0, str(HERE)); sys.path.insert(0, str(HERE.parents[1] / 'e8-r740-replica32/code')) +from bank_routes_k import placement +import hosting_k as HK +H, V = 8, 120 +sha = lambda p: hashlib.sha256(Path(p).read_bytes()).hexdigest() +def main(parent, local, out, a): + t0 = time.monotonic(); out.mkdir(parents=True, exist_ok=False) + blob = gzip.decompress((parent / 'GLOBAL-TEMPLATE.json.gz').read_bytes()); t = json.loads(blob) + g = json.loads((parent / 'GLOBAL.json').read_text()) + assert hashlib.sha256(blob).hexdigest() == g['template_sha256'] + L, T, M, R = t['stages'], t['replicas'], t['m'], g['R']; K = (L - 1) // 2; DATA = 2 * K * V * T + assert M == 1 + 7 * L and g['v'] == V and L % 2 == 1 + DEFICIT = T * (2 * K * V - 9 * H * L) + st = json.loads((local / 'records/249-states.json').read_text()) + fs = json.loads((local / 'records/frames.json').read_text())['frames']; dims = {int(f): z['dim'] for f, z in fs.items()} + width = [H - dims[st['initial'][str(i)]] for i in range(2 * V, 2 * V + R)] + # schedule with indexed idle children + sched = []; idx = 0 + for e in t['schedule']: + if e['kind'] == 'local_cover_sweep': sched.append(dict(kind='local', stage=e['stage'], pair=e['pair'])) + elif e['kind'] == 'bridge': sched.append(dict(kind='bridge', dst=e['dst'], src=e['src'])) + elif e['kind'] == 'idle': + sched.append(dict(kind='idle', pair=e['pair'], rank=e['rank'], idx=idx)); e['idx'] = idx; idx += 1 + win = {k: x for k, x in HK.windows(sched).items() if x[1] > 0 and x[2]} + avail = Counter() + for s in range(L): + for w in width: avail[s, w] += 1 + sel = Path(a.selection); key = a.key + if a.discover: + group = {7: 2} if (L == 9 and not a.host_all) else None + ys, zs, val = HK.solve(win, 2 * V, dict(avail), M, group=group, host_all=a.host_all, max_remainder=a.cap) + HK.save_selection(sel, key, ys, zs, val) + ys, zs, val = HK.load_selection(sel, key) + helpers = {s: [(h, width[h]) for h in range(R)] for s in range(L)} + slots, rem = HK.expand(win, ys, zs, helpers, 2 * V) + hosted = {(s, h): (i, c, off, w) for i, c, off, w, s, h in slots} + if a.host_all: assert len(hosted) == L * R, 'host-all selection leaves unhosted residuals' + # remaining helpers -> exact width-m banks (per stage, same set of helpers in every replica) + left = {s: [h for h in range(R) if (s, h) not in hosted] for s in range(L)} + nb = {}; place = {} + for s in range(L): + original = left[s] + if not original: nb[s] = 0; continue + placed, nb[s] = placement([width[h] for h in original], T, M) + for (rep, index), location in placed.items(): + place[s, rep, original[index]] = location + base = [DATA] + for s in range(L): base.append(base[-1] + nb[s]) + LIVE = base[-1] + # owners + for c in t['calls']: + s, rep, p = c['stage'], c['replica'], c['pair']; own = c['owners'][:2 * V] + for h in range(R): + if (s, h) in hosted: + i, cp, off, w = hosted[s, h]; pair = win[i][0]; side, port = divmod(cp, V) + own.append([rep * (2 * K * V) + (2 * pair + side) * V + port, ['hosted', i, off, w]]) + else: + b, off, w = place[s, rep, h]; own.append([base[s] + b, ['bank', off, w]]) + assert len({(f, tuple(r)) for f, r in own}) == 2 * V + R and all(0 <= f < LIVE for f, r in own) + c['owners'] = own + # profile: local programs unchanged; idle children replaced by remainders + paid = Counter() + for c in t['calls']: + for z in t['local_programs'][c['orientation']]: + if z[0] == 0 and z[4]: paid[z[4]] += 1 + if z[0] == 2: paid[z[5]] += 1 + remainder = {} + for e in t['schedule']: + if e['kind'] == 'idle' and e['rank']: + i = e['idx'] + if i in win: + rr = Counter(rem[i, cp] for cp in range(2 * V)); remainder[i] = {str(k): v for k, v in sorted(rr.items())} + for r_, n in rr.items(): + if r_: paid[r_] += n * T + else: + paid[e['rank']] += 2 * V * T + mass = sum(r * n for r, n in paid.items()); assert M * LIVE - mass == DEFICIT, M * LIVE - mass + D = a.depth; assert 2 * max(paid) ** D < M ** D, ('halving depth too small', D, max(paid), M) + t.update(version=a.version_tag, live=LIVE, banks_per_stage=nb, + hosting=dict(windows={str(i): [w[0], w[1], sorted(w[2])] for i, w in win.items()}, + slots=[list(x) for x in slots], remainders=remainder, banks_per_stage=nb, + rule='helper (stage s, helper h) of replica r owns coordinates [off, off+w) of the pending idle-child ' + 'projector of data family r*%d+(2*pair+side)*120+port (copy = side*120+port); s lies strictly between ' + 'the last local/bridge touch of that pair and the child; the child keeps the remaining coordinates.' % (2 * K * V)), + helper_route='bank owner (exact width-%d tiling of the unhosted residuals of each stage) or hosted owner (data family, pending idle coordinates)' % M) + tb = json.dumps(t, separators=(',', ':'), sort_keys=True).encode(); (out / 'GLOBAL-TEMPLATE.json.gz').write_bytes(gzip.compress(tb, mtime=0)) + res = dict(g); res.update(status='PASS_IDLE_AXIS_HOSTED_BIT_TEMPLATE', live=LIVE, physical=LIVE + 9, template_sha256=hashlib.sha256(tb).hexdigest(), + parent_template_sha256=g['template_sha256'], profile=dict(sorted(paid.items())), paid_calls=sum(paid.values()), rank_mass=mass, + maxchild=max(paid), banks_per_stage=nb, banks_per_stage_hosted=nb, hosted_residuals_per_replica=len(slots), hosted_residuals=len(slots) * T, + hosted_coordinates_per_replica=sum(x[3] for x in slots), saved_idle_cost_proxy=val, seconds=time.monotonic() - t0) + res['bank_assignments'] = sum(1 for c in t['calls'] for f, r in c['owners'][2 * V:] if r[0] == 'bank') + res['halving_integer_inequality'] = [M ** D, 2 * max(paid) ** D]; res['halving_depth'] = D + res['deficit'] = DEFICIT + res['scope'] += ' Additional PR442 idle-data hosting interface is assumed; every hosting window and coordinate ownership is audited.' + # Re-emit the renamed stream: ownership has changed, so the parent's + # instruction digest must not be reused. + expanded=hashlib.sha256(); count=0; local=2*V+R + for call in t['calls']: + owners=call['owners'] + for z in t['local_programs'][call['orientation']]: + op=z[0] + if op==0: mapped=[op,owners[z[1]],*z[2:]] + else: + ox=owners[z[1]];oy=owners[z[2]] if z[2]=5 and a.stages%2==1; L=a.stages; K=(L-1)//2; T=a.replicas; M=1+7*L; NOBANK=a.no_banks + if L!=9: assert NOBANK, 'bank tiling is only emitted for the nine-stage layout; use --no-banks and host every residual' + DEFICIT=T*(2*K*120-9*H*L) + wp=a.word; w=json.loads(gzip.decompress(wp.read_bytes()) if wp.suffix=='.gz' else wp.read_bytes()) + V,R=w['v'],w['R']; LOCAL=2*V+R; DATA=2*K*V*T; LIVE=DATA+L*R + assert (w['h'],V,w['form'])==(8,120,'/2') + geo=json.loads((out/'GEOMETRY.json').read_text()) + assert geo['status']=='PASS_COMPRESSED_%s_BIT_GEOMETRY_AND_BANK_BLOCKS'%NAMES[L] and geo['stages']==L and geo['m']==M + # the port routes depend only on the labels: same 120 label vectors, in the same order + assert len(geo['ports'])==V and all(p['port']==t and p['q']==w['lines'][t] for t,p in enumerate(geo['ports'])) + rp=a.local_check/'records/249-records.bin'; raw=rp.read_bytes(); rec=list(struct.iter_unpack('<6i',raw)) + state=json.loads((a.local_check/'records/249-states.json').read_text()) + fs=json.loads((a.local_check/'records/frames.json').read_text())['frames'] + dims={int(f):z['dim'] for f,z in fs.items()} + lg=json.loads((a.local_check/'cout/legality.json').read_text()) + cc=json.loads((a.local_check/'cout/columns.json').read_text()) + assert lg['status'].startswith('PASS') and cc['status'].startswith('PASS') + assert (state['v'],state['n'])==(V,LOCAL) and lg['copies']==lg['erasures']==9 + ADDS=lg['common_frame_ADDs'] + assert cc['formal_columns_checked']==4*V+R and cc['local_scalar_additions']==ADDS + initial=[state['initial'][str(i)] for i in range(LOCAL)] + final=[state['final'][str(i)] for i in range(LOCAL)] + assert all(dims[final[i]]==H for i in range(2*V,LOCAL)) + widths=[H-dims[initial[i]] for i in range(2*V,LOCAL)] + ENT=sum(H-z for z in widths) + if NOBANK: place,NB=None,None; LIVE=DATA + else: + place,NB=placement(widths,T,M) + assert T%8==0 and T*(H*R-ENT)%M==0 and NB==T*(H*R-ENT)//M + LIVE=DATA+L*NB + isometries=json.loads(a.isometries.read_text()) + assert validate_isometries(isometries,w['frames'],w['lines'],dims)==set(initial[2*V:]) + streams={}; checks={}; localH=None + for reverse in (False,True): + cur=(final if reverse else initial)[:]+[-1]; val=[1<=0 + cur[x]=newf + if f: hist[f]+=1 + events.append([0,x,oldf,newf,f,int(reverse)]) + elif op==1: + assert cur[x]==cur[y]==f and x!=y and x max_remainder: continue + index[('y', idx, pat)] = len(var); var.append(('y', idx, pat)) + for w in widths: + for s in st: + if avail.get((s, w), 0): + index[('z', idx, w, s)] = len(var); var.append(('z', idx, w, s)) + stages = sorted({s for (s, _) in avail}) + if group: + for w, g in group.items(): + for s in stages: + index[('g', w, s)] = len(var); var.append(('g', w, s)) + n = len(var); c = np.zeros(n); integ = np.ones(n); lb = np.zeros(n); ub = np.full(n, np.inf) + for key, j in index.items(): + if key[0] == 'y': + _, idx, pat = key; r = win[idx][1]; c[j] = -(cost(r, m) - cost(r - sum(pat), m) + pad_value * len(pat)) + rows, lo, hi = [], [], [] + for idx in win: + row = np.zeros(n) + for key, j in index.items(): + if key[0] == 'y' and key[1] == idx: row[j] = 1 + rows.append(row); lo.append(copies); hi.append(copies) + for w in widths: + row = np.zeros(n) + for key, j in index.items(): + if key[0] == 'y' and key[1] == idx: row[j] = key[2].count(w) + if key[0] == 'z' and key[1] == idx and key[2] == w: row[j] = -1 + rows.append(row); lo.append(0); hi.append(0) + for (s, w), a in avail.items(): + row = np.zeros(n) + for key, j in index.items(): + if key[0] == 'z' and key[2] == w and key[3] == s: row[j] = 1 + if group and w in group: + row[index[('g', w, s)]] = group[w]; rows.append(row); lo.append(a); hi.append(a) # used + g*k = avail + elif host_all: + rows.append(row); lo.append(a); hi.append(a) + else: + rows.append(row); lo.append(0); hi.append(a) + res = milp(c, constraints=LinearConstraint(np.array(rows), lo, hi), integrality=integ, bounds=Bounds(lb, ub), + options=dict(time_limit=time_limit, disp=False)) + assert res.x is not None, res.message + assert res.status == 0, ('integer programme not solved to optimality', res.status, res.message) + x = [int(round(t)) for t in res.x] + ys = {(k[1], k[2]): x[j] for k, j in index.items() if k[0] == 'y' and x[j]} + zs = {(k[1], k[2], k[3]): x[j] for k, j in index.items() if k[0] == 'z' and x[j]} + return ys, zs, -res.fun diff --git a/research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/vendor/e8-r740-replica32/code/bank_routes.py b/research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/vendor/e8-r740-replica32/code/bank_routes.py new file mode 100644 index 0000000000..9e43a87666 --- /dev/null +++ b/research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/vendor/e8-r740-replica32/code/bank_routes.py @@ -0,0 +1,65 @@ +"""Exact residual bank routes for the kernel word; chafreaky with OpenAI Codex. + +The stored integer rows divided by two are an orthonormal basis. Entrance +rows span E; the remaining rows span E-perp. A coordinate permutation sends +the latter to the assigned contiguous block in a width-64 bank. This gives +an explicit rational isometry, with only powers of two in denominators. +""" +from collections import Counter, deque +from tile_m import tile + + +def placement(widths, replicas=32): + assert replicas > 0 and replicas % 8 == 0 + by_width = {} + for rep in range(replicas): + for helper, width in enumerate(widths): + assert 0 < width <= 8 + by_width.setdefault(width, deque()).append((rep, helper)) + patterns = tile({w: len(q) for w, q in by_width.items()}, 64) + result = {} + bank = 0 + for widths_in_bank, count in patterns: + for _ in range(count): + offset = 0 + for width in widths_in_bank: + key = by_width[width].popleft() + result[key] = (bank, offset, width) + offset += width + assert offset == 64 + bank += 1 + assert all(not q for q in by_width.values()) + assert len(result) == replicas * len(widths) + return result, bank + + +def validate_isometries(rows, frames, roots, dimensions): + seen = set() + dot = lambda a, b: sum(x*y for x, y in zip(a, b)) + for record in rows: + f = record['frame'] + assert f not in seen + seen.add(f) + e = record['entrance_rank'] + assert e == dimensions[f] and record['denominator'] == 2 + basis = record['entrance_basis'] + record['residual_basis'] + matrix = record['orthogonal_rows_numerator'] + assert len(basis) == len(matrix) == 8 + assert matrix == [roots[i] for i in basis] + assert all(len(r) == 8 and all(type(x) is int for x in r) for r in matrix) + assert all(dot(a,b) == 4*(i==j) for i,a in enumerate(matrix) for j,b in enumerate(matrix)) + # The first e rows span precisely the frame, not only its dimension. + for i in frames[f]: + assert sum(dot(roots[i], q)**2 for q in matrix[:e]) == 4*dot(roots[i], roots[i]) + assert len(record['entrance_basis']) == e + # This selected family needs coordinate permutations only. Checking the + # elementary reduction also bounds the literal route cost and primes. + reduced = [[x // 2 for x in row] for row in matrix] + assert all(x % 2 == 0 for row in matrix for x in row) + operations = record['gaussian_reduction'] + assert len(operations) <= 7 + for kind, i, j, scalar in operations: + assert kind == 'swap' and scalar is None and 0 <= i < 8 and 0 <= j < 8 + reduced[i], reduced[j] = reduced[j], reduced[i] + assert reduced == [[int(i == j) for j in range(8)] for i in range(8)] + return seen diff --git a/research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/vendor/e8-r740-replica32/code/config.py b/research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/vendor/e8-r740-replica32/code/config.py new file mode 100644 index 0000000000..c6824d9c4e --- /dev/null +++ b/research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/vendor/e8-r740-replica32/code/config.py @@ -0,0 +1,2 @@ +"""Four independent groups of eight replicas; no bank padding.""" +REPLICAS = 32 diff --git a/research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/vendor/e8-r740-replica32/code/e8core.py b/research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/vendor/e8-r740-replica32/code/e8core.py new file mode 100644 index 0000000000..78983b3687 --- /dev/null +++ b/research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/vendor/e8-r740-replica32/code/e8core.py @@ -0,0 +1,203 @@ +"""Our own engine for bitE8/1 words (E8 root-line labels, h = 8): frame algebra over Q via root-line masks, +legality/F2 replay, five-stage histogram, exact bit coarse saving and PR #315 outer assembly (our engine copies). +Chafik Boukhalfa (chafreaky) with Anthropic Claude assistance; Apache-2.0.""" +import gzip, json, os, importlib.util +from fractions import Fraction as Q +from collections import Counter + +HERE = os.path.dirname(os.path.abspath(__file__)) +def _load(n): + s = importlib.util.spec_from_file_location('e8_' + n, os.path.join(HERE, "engines", n + ".py")) + m = importlib.util.module_from_spec(s); s.loader.exec_module(m); return m +moment = _load('moment'); other = _load('base_two_moment'); outer = _load('outer') + +B2 = Q(876248285600677, 10**18) # Sussman E8 five-stage (PR #352) +B3 = Q(932783231884153, 10**18) # DaysSky seven-stage B_3 (PR #363; no Lean proof) + + +def load_word(p): + with (gzip.open(p, 'rt') if p.endswith('.gz') else open(p)) as f: + return json.load(f) + + +def save_word(c, p): + with gzip.open(p, 'wt') as f: + json.dump(c, f, separators=(',', ':')) + + +def rank(vecs): + rows = [[Q(x) for x in r] for r in vecs]; rk = 0 + n = len(rows[0]) if rows else 0 + for col in range(n): + p = next((i for i in range(rk, len(rows)) if rows[i][col] != 0), None) + if p is None: continue + rows[rk], rows[p] = rows[p], rows[rk] + for i in range(len(rows)): + if i != rk and rows[i][col] != 0: + f = rows[i][col] / rows[rk][col]; rows[i] = [a - f * b for a, b in zip(rows[i], rows[rk])] + rk += 1 + return rk + + +class Lat: + """Root-spanned subspaces of Q^8 represented by the mask of root lines they contain.""" + def __init__(self, lines): + self.lines = [tuple(x) for x in lines]; self.v = len(lines) + self._dim = {0: 0}; self._close = {} + # Gram (form /2) for orthogonality + self.ip = [[sum(a * b for a, b in zip(x, y)) // 2 for y in self.lines] for x in self.lines] + + def close(self, mask): + """mask of all root lines in span(roots of mask)""" + if mask in self._close: return self._close[mask] + idx = [i for i in range(self.v) if mask >> i & 1] + basis = [] + for i in idx: + if rank([self.lines[j] for j in basis] + [self.lines[i]]) > len(basis): basis.append(i) + d = len(basis); out = 0 + if d == 8: out = (1 << self.v) - 1 + elif d: + B = [self.lines[j] for j in basis] + for i in range(self.v): + if rank(B + [self.lines[i]]) == d: out |= 1 << i + self._close[mask] = out; self._close[out] = out; self._dim[out] = d + return out + + def dim(self, mask): + if mask not in self._dim: self.close(mask) + return self._dim[mask] + + def basis(self, mask): + b = [] + for i in range(self.v): + if mask >> i & 1 and rank([self.lines[j] for j in b] + [self.lines[i]]) > len(b): b.append(i) + return b + + def perp(self, t): + return self.close(sum(1 << j for j in range(self.v) if self.ip[t][j] == 0)) + + +def to_masks(c, L): + return [L.close(sum(1 << i for i in fr)) for fr in c['frames']] + + +PHASES = ('D', 'A', 'B', 'C') + + +def replay(c, L=None, masks=None, check_f2=True, span=True, span_mode='z'): + """Exact legality + F2 replay + histogram of a bitE8/1 word. Returns dict(H_local, steps, ...). + Legality: each register's frames nested (mask inclusion = exact Q-subspace inclusion for root-spanned frames) and + strictly increasing at each move; every gate at one common frame; D compensation at frame 0; y at frame 0 at the + scatter; COPY of retained totals; final frames (x, helpers FULL; y_t at its cap t-perp). + Source-span: every non-target operand's x-support lies in the gate frame.""" + L = L or Lat(c['lines']); masks = masks or to_masks(c, L) + v, R, h = c['v'], c['R'], c['h']; n = 2 * v + R; FULL = (1 << v) - 1 + cur = [masks[f] for f in c['start']] + for r in range(v): assert cur[r] == L.close(1 << r), 'x start' + for r in range(v, 2 * v): assert cur[r] == 0, 'y start at 0' + sig = [L.dim(cur[r]) for r in range(n)] + H = Counter(); steps = 0; viol = [] + val = [1 << i for i in range(n)] + zsupp = [{i: 1} if i < v else {} for i in range(n)] # integer support of each register over the x columns (all ADDs are +1) + def climb(r, m): + nonlocal steps + if cur[r] == m: return + assert cur[r] & ~m == 0 and cur[r] != m, ('not nested', r) + H[L.dim(m) - L.dim(cur[r])] += 1; steps += 1; cur[r] = m + def run(gates, ph): + for k, (f, d, srcs) in enumerate(gates): + m = masks[f] + assert d >= v and d not in srcs and len(set(srcs)) == len(srcs), (ph, k) + for x in [d] + srcs: climb(x, m) + for s in srcs: + val[d] ^= val[s] + if span: + if s < v or s >= 2 * v: + ms = sum(1 << i for i in zsupp[s]) + if ms & ~m: viol.append((ph, k, s)) + zd = zsupp[d] + for i, a in zsupp[s].items(): + w = zd.get(i, 0) + a + if w: zd[i] = w + else: zd.pop(i, None) + if span_mode == 'f2': + zsupp[d] = {i: 1 for i in range(v) if val[d] >> i & 1} + if span and d >= 2 * v: + md = sum(1 << i for i in zsupp[d]) + if md & ~m: viol.append((ph, k, d)) + run(c['D'], 'D') + for r in range(v, n): assert cur[r] == masks[c['start'][r]], 'D compensation at frame 0 (helpers untouched otherwise)' + run(c['A'], 'A') + for t in range(v): assert cur[v + t] == 0, 'y at frame 0 at the scatter' + copies = [val[q] for q in c['ret']] + for q in c['ret']: + assert q >= 2 * v; H[L.dim(cur[q])] += 1 + for t, row in enumerate(c['scatter']): + for k in row: val[v + t] ^= copies[k] + run(c['B'], 'B'); run(c['C'], 'C') + for r in range(n): climb(r, masks[c['final'][r]]) + for r in range(v): assert cur[r] == FULL + for r in range(2 * v, n): assert cur[r] == FULL + for t in range(v): assert cur[v + t] == L.perp(t), 'y final at cap' + if check_f2: + for s in range(v): assert val[s] == 1 << s + for t in range(v): assert val[v + t] == (1 << (v + t)) | (1 << t), ('target', t) + for r in range(2 * v, n): assert val[r] == 1 << r, ('helper', r) + return dict(sig=sig, viol=viol, H=H, steps=steps, n=n, v=v, R=R, h=h, L=L, masks=masks, ncopy=len(c['ret'])) + + +def profile(H_local, v, h=8): + prof = Counter({j: 5 * c for j, c in H_local.items()}) + for j in (2 * h - 2, h - 1, 2 * h + 2, 4): prof[j] += 2 * v + return prof + + +def coarse(H, m, W): + grid = Q(1, 10**18) + root = moment.certify(dict(H), m, W, True); c = Q(int(Q(root['lower']) * 10**18), 10**18) + assert moment.moment(H, m, W, c, True)[1] < 1 < moment.moment(H, m, W, c + grid, True)[0] + fb = 32 * m * m * sum(H.values()) + _, up = other.moment(m, W, list(H.items()), c); _, bad = other.moment(m, W, [(1, fb)], c) + lo, _ = other.moment(m, W, list(H.items()), c + grid); blo, _ = other.moment(m, W, [(1, fb)], c + grid) + assert up + Q(1, 10**16) * bad < 1 < lo + Q(1, 10**16) * blo + return c + + +def kappa(c, b): + grid = Q(1, 10**18); eta = Q(1, 10**12); beta = Q(1, 10**9); cap = (1 - beta) * b + sv = min(c, Q(((cap - grid) * 10**18).__floor__(), 10**18)); binding = 'bit' if sv == c else 'complex' + chain = [Q(384599, 10**10)] + for _ in range(3): + a = (1 - sv) * sv + sv * chain[-1]; assert chain[-1] < a < sv < 1 - a; chain.append(a) + bit = chain[-1] + bridge = dict(proof='PROOF.md', representation='Exact powers with source-bound finite overcharges', + semantic=dict(G=dict(base=2, exponent=30000), E=dict(base=2, exponent=100000), B_upper=dict(base=2, exponent=100001), + C0=dict(base=2, exponent=210000), C1=1, strict_literal_gap=1, induction_gap_lower=1), + rows=dict(coefficient=20161, degree=10**6, suffix_slope=4 * 10**6, degree_gap=Q(10**6) - Q(51 * 20161, 25))) + q = bit * (1 - 2 * eta); mn = (1 - eta) * q / (1 + q); t = mn * 10**18; k = Q((t.numerator - 1) // t.denominator, 10**18) + asm = outer.assembly(bit, b, bridge, k, eta=eta, beta=beta) + assert len(asm['strict_constraints']) == 47 and min(asm['strict_constraints'].values()) > 0 + try: + outer.assembly(bit, b, bridge, k + grid, eta=eta, beta=beta); raise SystemExit('ADJACENT ADMITTED') + except AssertionError: pass + return k, binding + + +def price_word(c, **kw): + r = replay(c, **kw) + v, h, R = r['v'], r['h'], r['R']; m = 5 * h + resid = sum(h - r['sig'][x] for x in range(2 * v, 2 * v + R)) + T = 1 if resid % h == 0 else h + W = (4 * v * h + resid) * T // h; assert (4 * v * h + resid) * T % h == 0 + prof = Counter({j: T * k for j, k in profile(r['H'], v, h).items()}); mass = sum(a * b for a, b in prof.items()) + L_copy = h * r['ncopy'] + assert m * W - mass == T * (4 * v - 5 * L_copy), ('deficit', m * W - mass) + assert 2 * max(prof) < m + cc = coarse(prof, m, W) + return dict(c=cc, H=r['H'], prof=prof, W=W, T=T, resid=resid, m=m, deficit=m * W - mass, replay=r) + + +def phi_fn(m, W, a): + """linearized moment term per child of rank r (float), for search scoring""" + import math + return lambda r: r / (m * W) * math.exp(a * math.log(m / r)) diff --git a/research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/vendor/e8-r740-replica32/code/engines/base_two_moment.py b/research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/vendor/e8-r740-replica32/code/engines/base_two_moment.py new file mode 100644 index 0000000000..14a90fa0e7 --- /dev/null +++ b/research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/vendor/e8-r740-replica32/code/engines/base_two_moment.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 +"""Maintainer arithmetic cross-check of the pinned PR39 certificate. + +Uses no producer/checker imports. Counts are inputs, not proved by this script. +80-term rational logarithms and 12-term exponentials independently enclose +the two moments. The accompanying written review addresses their realization. +""" +import argparse +from fractions import Fraction as Q +from hashlib import sha256 +import json +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +GRID = 10**30 + + +def require(condition, message): + if not condition: + raise ValueError(message) + + +def log_unit(x): + require(1 <= x <= 2, "logarithm range") + z = (x-1)/(x+1) + lo = 2*sum((z**(2*j+1)/(2*j+1) for j in range(80)), Q(0)) + return lo, lo+2*z**161/(161*(1-z*z)) + + +def log_bounds(x): + k = 0 + while x > 2: + x /= 2 + k += 1 + lo, hi = log_unit(x) + l2, u2 = log_unit(Q(2)) + lo, hi = lo+k*l2, hi+k*u2 + # Outward rational rounding keeps later certificates compact. + return Q((lo*GRID).__floor__(), GRID), Q((hi*GRID).__ceil__(), GRID) + + +def exp_bounds(x): + require(0 <= x < 1, "exponential range") + term = total = Q(1) + for j in range(1, 13): + term *= x/j + total += term + tail = term*x/13/(1-x/14) + return total, total+tail + + +def moment(m, W, entries, saving): + lo = hi = Q(0) + for t, count in entries: + t, count = int(t), int(count) + require(0 < t < m and count > 0, "improper child") + l, u = log_bounds(Q(m, t)) + lower = exp_bounds(saving*l)[0] + upper = exp_bounds(saving*u)[1] + lo += Q(t*count, m*W)*lower + hi += Q(t*count, m*W)*upper + return Q((lo*GRID).__floor__(), GRID), Q((hi*GRID).__ceil__(), GRID) + diff --git a/research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/vendor/e8-r740-replica32/code/engines/moment.py b/research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/vendor/e8-r740-replica32/code/engines/moment.py new file mode 100644 index 0000000000..7d697cb452 --- /dev/null +++ b/research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/vendor/e8-r740-replica32/code/engines/moment.py @@ -0,0 +1,113 @@ +"""Independent prospective ordinary five-stage cost, never supplier admission. + +Consumes a raw one-invocation ledger made from the actual source/internal/target +paths. No packed profile is used as an input. Missing physical/finite/compiler +obligations must remain explicit in the accompanying report. +""" +from collections import Counter +from decimal import Decimal, localcontext, ROUND_FLOOR +from fractions import Fraction as F +from functools import lru_cache +from math import factorial +import hashlib, json, sys +from pathlib import Path + +DYAD = 1 << 224 + +def floor(x): return F(x.numerator*DYAD//x.denominator, DYAD) +def ceil(x): return -floor(-x) + +@lru_cache(None) +def logarithm(x): + """Mercator bounds after binary reduction; ln(2) via two Mercator sums. + + For 1 <= y < 2, log(y) is an alternating series in z=y-1. + We instead reduce to 1 <= y < 3/2, and bound log(3/2) and + log(2) by the same alternating series at z=1/2 and z=1/3, + using log(2)=log(3/2)+log(4/3). All sums are exact rationals. + """ + assert x >= 1 + k = 0 + while x >= 2: x /= 2; k += 1 + j = int(x >= F(3, 2)) + if j: x /= F(3, 2) + def mercator(y): + z=y-1 + assert 0 <= z <= F(1,2) + # An even partial sum is below log; the next term bounds its tail. + t=z; s=F(0) + for n in range(1, 225): + s += (t if n & 1 else -t)/n + t *= z + return s, s+t/225 + a,b=mercator(x); c,d=mercator(F(3,2)); e,f=mercator(F(4,3)) + return floor(a+(k+j)*c+k*e), ceil(b+(k+j)*d+k*f) + +def exponential(l,u): + assert 0 <= l <= u < F(1,10) + def poly(x): + s=t=F(1) + for j in range(1,25): t=t*x/j; s+=t + return s,t + lo,_=poly(l);hi,last=poly(u) + return floor(lo),ceil(hi+last*u/25/(1-u/26)) + +def moment(H,m,W,a,bad=False): + lo=hi=F(0) + for r,n in H.items(): + assert 0 < r < m and n > 0 + l,u=logarithm(F(m,r)); e,f=exponential(a*l,a*u) + p=F(r*n,m*W);lo+=p*e;hi+=p*f + if bad: + # Existing envelope shape, only conditionally portable to the new cover. + l,u=logarithm(F(m));e,f=exponential(a*l,a*u) + p=F(32*m*sum(H.values()),10**16*W) + lo+=p*e;hi+=p*f + return floor(lo),ceil(hi) + +def decimal(x): + with localcontext() as c: + c.prec=70 + return str(Decimal(x.numerator)/Decimal(x.denominator)) + +def certify(H,m,W,bad=False): + with localcontext() as c: + c.prec=70 + terms=[(Decimal(r*n)/Decimal(m*W),(Decimal(m)/Decimal(r)).ln()) for r,n in H.items()] + fallback=Decimal(32*m*sum(H.values()))/Decimal(10**16*W) if bad else Decimal(0) + lm=Decimal(m).ln();lo=Decimal(0);hi=Decimal('.003') + for _ in range(210): + a=(lo+hi)/2 + if sum(p*(a*l).exp() for p,l in terms)+fallback*(a*lm).exp()<1:lo=a + else:hi=a + q=int(((lo+hi)/2*10**24).to_integral_value(rounding=ROUND_FLOOR)) + lo=F(q,10**24);hi=lo+F(1,10**24) + lv=moment(H,m,W,lo,bad);hv=moment(H,m,W,hi,bad) + assert lv[1]<10 for v in slacks.values()),{k:str(v) for k,v in slacks.items() if v<=0} + assert min(margins.values())==minimum + assert margins['balanced_prefix']-minimum==eta and 1-eps-r==eta/2 + assert 1-eps*(1+c)==eta-eps*eta/4 + return dict(parameters=dict(a_bit=a,a_complex=b,tau=tau,sigma=sigma, + eta=eta,beta=beta,q=q,c=c,epsilon=eps,lambda_=lam, + lambda_prime=lp,alpha_squared_power=r,delta=delta, + C0=bridge['semantic']['C0'],C1=1,kappa=kappa), + strict_constraints=slacks,margins=margins,minimum_margin=minimum, + absorption_gap=minimum-kappa, + recurrence=dict(internal=internal,leaf=leaf,reservations=1-c)) diff --git a/research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/vendor/e8-r740-replica32/code/tile_m.py b/research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/vendor/e8-r740-replica32/code/tile_m.py new file mode 100644 index 0000000000..cd5fd9b539 --- /dev/null +++ b/research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/vendor/e8-r740-replica32/code/tile_m.py @@ -0,0 +1,93 @@ +"""Residual census (8 - sigma per helper) and an exact zero-padding bank tiling of width m = (2K+1)h with T replicas: +tile() and economy_tile() verbatim from our PR #329 bank_template.py (via code/tile.py of our dtst3 package). +usage: tile_m.py WORD.json.gz M [replicas] (replicas default: the least T with T x residual sum = 0 mod M that tiles)""" +import sys,os,collections +from collections import Counter +sys.path.insert(0,os.path.dirname(os.path.abspath(__file__))) +from e8core import * +def economy_tile(blocks, m): + """Exact zero-padding tiling used when the filler search of tile() runs out of filler blocks (the kernel pivots + and early restorations of the stage stack add many residual widths that do not divide m; the PR #299 analogue at + p = 12 was the (r^4, 24, 4^(24-r)) re-tiling). The fillers are the two most numerous widths dividing m (here the + full helper width h and the rank-4 entrance residual). Each other width w, in decreasing order, gets the bank + w^a F^c f^b (w*a + F*c + f*b = m) that spends the fewest narrow fillers f per w block (then the largest a), plus + at most one bank w^r F^c' f^b' for the remainder r = n mod a. The leftover fillers fill pure banks F^(m/F), + f^(m/f) and y residue banks F^j f^k. Every bank sums to m and every block is used exactly once (no padding).""" + from fractions import Fraction + blocks = {w: n for w, n in blocks.items() if n} + div = sorted((w for w in blocks if m % w == 0), key=lambda w: (-blocks[w], w)) + assert len(div) >= 2, ('economy tiling needs two filler widths', blocks) + F, f = sorted(div[:2], reverse=True) + + def fill(rest): + """(c, b) with F*c + f*b = rest, c maximal (fewest narrow fillers); None if impossible.""" + return next(((c, (rest - F*c)//f) for c in range(rest//F, -1, -1) if (rest - F*c) % f == 0), None) + + left = {F: blocks[F], f: blocks[f]}; out = [] + for w in sorted((x for x in blocks if x not in (F, f)), reverse=True): + n = blocks[w] + opts = [(Fraction(cb[1], a), -a, a, cb) for a in range(m//w, 0, -1) for cb in [fill(m - w*a)] if cb] + assert opts, ('no bank for width', w) + _, _, a, (c, b) = min(opts); q, r = divmod(n, a) + if q: out.append(((w,)*a + (F,)*c + (f,)*b, q)); left[F] -= q*c; left[f] -= q*b + if r: + cb = fill(m - w*r); assert cb, ('no remainder bank', w, r) + out.append(((w,)*r + (F,)*cb[0] + (f,)*cb[1], 1)); left[F] -= cb[0]; left[f] -= cb[1] + assert left[F] >= 0 and left[f] >= 0, ('fillers exhausted', left) + pF, pf = m//F, m//f + for y in range((pF*pf) + 1): + for j in range(1 if y else 0, pF if y else 1): + if (m - F*j) % f: continue + k = (m - F*j)//f; aF, af = left[F] - y*j, left[f] - y*k + if aF >= 0 and af >= 0 and aF % pF == 0 and af % pf == 0: + out += ([((F,)*j + (f,)*k, y)] if y else []) + ([((F,)*pF, aF//pF)] if aF else []) + ([((f,)*pf, af//pf)] if af else []) + return out + raise AssertionError(('no exact zero-padding tiling', blocks, m)) + + +def tile(blocks, m): + """Exact tiling of {width: number of blocks} into banks of total width m, as ((widths, banks), ...). + + A width dividing m fills pure banks. A width w not dividing m is mixed with a filler width f dividing m: + banks w^a f^b with a maximal (w*a + f*b = m), then at most one bank w^r f^t for the remainder r. Fillers + are tried in a fixed order (most blocks first) with backtracking until every pure width also divides out. + Fails (AssertionError) unless every block is used and every bank is full; there is no padding.""" + blocks = {w: n for w, n in blocks.items() if n} + assert all(0 < w <= m for w in blocks) + mixed = sorted(x for x in blocks if m % x) + + def search(i, left, out): + if i == len(mixed): + if any(left[w] % (m // w) for w in left): return None + return out + [(tuple([w]*(m // w)), left[w]//(m // w)) for w in sorted(left) if left[w]] + w = mixed[i]; n = blocks[w] + for f in sorted(left, key=lambda x: (-left[x], x)): + a = next((a for a in range(m // w, 0, -1) if (m - w*a) % f == 0), None) + if a is None: continue + b = (m - w*a)//f; q, r = divmod(n, a) + if r and (m - w*r) % f: continue + t = (m - w*r)//f if r else 0 + if q*b + t > left[f]: continue + rest = dict(left); rest[f] -= q*b + t + found = search(i + 1, rest, out + ([(tuple([w]*a + [f]*b), q)] if q else []) + ([(tuple([w]*r + [f]*t), 1)] if r else [])) + if found is not None: return found + return None + out = search(0, {w: n for w, n in blocks.items() if not m % w}, []) + if out is None: out = economy_tile(blocks, m) + assert all(sum(widths) == m and count > 0 for widths, count in out) + used = Counter() + for widths, count in out: + for x in widths: used[x] += count + assert used == Counter(blocks), 'every block used exactly once' + return tuple(out) +if __name__ == '__main__': + c = load_word(sys.argv[1]); m = int(sys.argv[2]) + L = Lat(c['lines']); M = to_masks(c, L); v = c['v']; n = 2 * v + c['R'] + res = Counter(8 - L.dim(M[c['start'][r]]) for r in range(2 * v, n)); tot = sum(k * x for k, x in res.items()) + reps = [int(sys.argv[3])] if len(sys.argv) > 3 else [t for t in range(1, 200) if t * tot % m == 0] + for T in reps: + try: pat = tile({r: T * k for r, k in res.items() if r > 0}, m); break + except AssertionError: pat = None + assert pat, 'no tiling' + print(' residual census', dict(sorted(res.items())), 'sum', tot) + print(' PASS exact width-%d tiling with %d replicas: %d banks' % (m, T, sum(k for _, k in pat)), pat) diff --git a/research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/vendor/idle-axis-hosting/hosting_core.py b/research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/vendor/idle-axis-hosting/hosting_core.py new file mode 100644 index 0000000000..68b536daa9 --- /dev/null +++ b/research/e8-bit-p111/vendor/pr510/e8-r480-nine-stage/vendor/idle-axis-hosting/hosting_core.py @@ -0,0 +1,138 @@ +"""Idle-axis hosting: place whole dirty-helper residuals on the pending coordinates of an idle data family. + +A data family that is advanced by an idle child from level a to level b receives the commuting coordinate factors of +that child. If the family is untouched (no local invocation on its pair and no bridge naming its pair) during an +interval of local invocations ending at the child, a helper residual of width w from an invocation inside that +interval may own w of those pending coordinates instead of a bank slot. The child keeps only the remaining +coordinates. This module computes the windows from the recipe and chooses, per replica, how many residuals of each +width each child hosts (exact integer programme), then expands that choice into explicit slot assignments. +Standard library plus scipy for the integer programme; the emitted assignment is re-checked without scipy.""" +import math +from collections import Counter, defaultdict + +def windows(schedule): + """schedule: ordered events dict(kind='local',stage,pair) | dict(kind='bridge',dst,src) | dict(kind='idle',pair,rank,idx). + Returns {idx: (pair, rank, frozenset(stages))} - stages whose invocation lies strictly between the last touch of + the pair and the idle child.""" + last = {}; out = {}; locals_ = [] + for pos, e in enumerate(schedule): + k = e['kind'] + if k == 'local': + locals_.append((pos, e['stage'], e['pair'])); last[e['pair']] = pos + elif k == 'bridge': + last[e['dst']] = pos; last[e['src']] = pos + elif k == 'idle': + p = e['pair']; lo = last.get(p, -1) + st = frozenset(s for q, s, pp in locals_ if lo < q < pos and pp != p) + out[e['idx']] = (p, e['rank'], st) # an idle child acts on other coordinates (or the other member): not a touch + return out + +def patterns(rank, widths, maxk=12): + """multisets of widths with sum <= rank (as sorted tuples)""" + ws = sorted(set(widths), reverse=True); res = [] + def rec(i, cur, s): + if i == len(ws): res.append(tuple(cur)); return + w = ws[i]; k = 0 + while s + k * w <= rank and len(cur) + k <= maxk: + rec(i + 1, cur + [w] * k, s + k * w); k += 1 + rec(0, [], 0) + return res + +def cost(r, m): + return r * math.log(m / r) if r else 0.0 + +def solve(win, copies, avail, m, pad_value=0.0, group=None): + """win: {idx: (pair, rank, stages)}; copies: piece copies per replica of each idx (2V); + avail: {(stage, width): count per replica}; maximise saved idle cost (+ pad_value per hosted residual). + group: optional {width: g} - remaining count of that width per stage must be a multiple of g (exact bank tiling). + Returns {(idx, pattern): copies} and {(idx, width, stage): count}.""" + import numpy as np + from scipy.optimize import milp, LinearConstraint, Bounds + widths = sorted({w for (_, w) in avail}) + var = []; index = {} + for idx, (p, r, st) in win.items(): + for pat in patterns(r, widths): + index[('y', idx, pat)] = len(var); var.append(('y', idx, pat)) + for w in widths: + for s in st: + if avail.get((s, w), 0): + index[('z', idx, w, s)] = len(var); var.append(('z', idx, w, s)) + stages = sorted({s for (s, _) in avail}) + if group: + for w, g in group.items(): + for s in stages: + index[('g', w, s)] = len(var); var.append(('g', w, s)) + n = len(var); c = np.zeros(n); integ = np.ones(n); lb = np.zeros(n); ub = np.full(n, np.inf) + for key, j in index.items(): + if key[0] == 'y': + _, idx, pat = key; r = win[idx][1]; c[j] = -(cost(r, m) - cost(r - sum(pat), m) + pad_value * len(pat)) + rows, lo, hi = [], [], [] + for idx in win: + row = np.zeros(n) + for key, j in index.items(): + if key[0] == 'y' and key[1] == idx: row[j] = 1 + rows.append(row); lo.append(copies); hi.append(copies) + for w in widths: + row = np.zeros(n) + for key, j in index.items(): + if key[0] == 'y' and key[1] == idx: row[j] = key[2].count(w) + if key[0] == 'z' and key[1] == idx and key[2] == w: row[j] = -1 + rows.append(row); lo.append(0); hi.append(0) + for (s, w), a in avail.items(): + row = np.zeros(n) + for key, j in index.items(): + if key[0] == 'z' and key[2] == w and key[3] == s: row[j] = 1 + if group and w in group: + row[index[('g', w, s)]] = group[w]; rows.append(row); lo.append(a); hi.append(a) # used + g*k = avail + else: + rows.append(row); lo.append(0); hi.append(a) + res = milp(c, constraints=LinearConstraint(np.array(rows), lo, hi), integrality=integ, bounds=Bounds(lb, ub), + options=dict(time_limit=600, disp=False)) + assert res.x is not None, res.message + x = [int(round(t)) for t in res.x] + ys = {(k[1], k[2]): x[j] for k, j in index.items() if k[0] == 'y' and x[j]} + zs = {(k[1], k[2], k[3]): x[j] for k, j in index.items() if k[0] == 'z' and x[j]} + return ys, zs, -res.fun + +def expand(win, ys, zs, helpers, V2): + """helpers: {stage: [(helper, width), ...]} for ONE replica; returns list of + (idx, copy, offset, width, stage, helper) and the remainder rank of every (idx, copy). + Copies of a piece are the 2V (side, port) data families; residuals packed from offset 0 in decreasing width.""" + pool = defaultdict(list) # (stage, width) -> helpers + for s, hs in helpers.items(): + for h, w in hs: pool[s, w].append(h) + for k in pool: pool[k].sort() + need = defaultdict(list) # (idx, width) -> stages multiset in deterministic order + for (idx, w, s), cnt in sorted(zs.items()): need[idx, w] += [s] * cnt + out = []; rem = {} + for idx in sorted(win): + pats = sorted([(pat, cnt) for (i, pat), cnt in ys.items() if i == idx], key=lambda t: (-sum(t[0]), t[0])) + copy = 0; ptr = Counter() + r = win[idx][1] + for pat, cnt in pats: + for _ in range(cnt): + off = 0 + for w in sorted(pat, reverse=True): + s = need[idx, w][ptr[idx, w]]; ptr[idx, w] += 1 + h = pool[s, w].pop(0) + out.append((idx, copy, off, w, s, h)); off += w + rem[idx, copy] = r - off; copy += 1 + while copy < V2: rem[idx, copy] = r; copy += 1 + return out, rem + +def save_selection(path, key, ys, zs, val): + """freeze the integer-programme choice (patterns per child, residual stages per child) for stdlib-only replay""" + import json + from pathlib import Path + p = Path(path); d = json.loads(p.read_text()) if p.exists() else {} + d[key] = dict(patterns=sorted([[i, list(pat), n] for (i, pat), n in ys.items()]), + stages=sorted([[i, w, s, n] for (i, w, s), n in zs.items()]), proxy_saving=val) + p.write_text(json.dumps(d, indent=1, sort_keys=True) + '\n') + +def load_selection(path, key): + import json + from pathlib import Path + d = json.loads(Path(path).read_text())[key] + ys = {(i, tuple(pat)): n for i, pat, n in d['patterns']} + zs = {(i, w, s): n for i, w, s, n in d['stages']} + return ys, zs, d['proxy_saving'] diff --git a/research/e8-bit-p111/vendor/pr545/EXPECTED.json b/research/e8-bit-p111/vendor/pr545/EXPECTED.json new file mode 100644 index 0000000000..ac7f102363 --- /dev/null +++ b/research/e8-bit-p111/vendor/pr545/EXPECTED.json @@ -0,0 +1,127 @@ +{ + "word": { + "v": 111, + "R": 489, + "cst": 63, + "copy_ranks": [ + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 7 + ], + "punctured": [ + 39, + 52, + 57, + 71, + 76, + 78, + 103, + 109, + 114 + ], + "content_sha256": "599dbe2184a0b99f01345543a6523e41df7b164513fc25dc7b5dadab548205d3", + "local_histogram": { + "1": 1182, + "2": 1332, + "3": 63, + "4": 153, + "5": 9, + "6": 129, + "7": 9 + }, + "five_stage_payload_bits": 91, + "eleven_stage_payload_bits": 91, + "source_destination_gates_A": 501 + }, + "eleven": { + "bit_lower": "165947204563779652541/100000000000000000000000", + "bit_lower_decimal": "0.00165947204563779652541", + "profile": { + "1": 104016, + "2": 120768, + "3": 5544, + "4": 17015, + "5": 792, + "6": 14904, + "7": 7896, + "28": 1, + "56": 1971 + }, + "emitted_program_sha256": "80c4e0f4d635a0c09a3e1a0ef81cc7e0590233410840d5b26d1149daa049718b", + "bit": { + "live": 8880, + "physical": 8889, + "maxchild": 56, + "rank_mass": 689304, + "hosted_residuals": 43032, + "prefix_bits": 1843, + "charged_prefix_bits": 1856, + "coefficient_bits": 1843, + "three_level_coefficient_bits": 5583, + "charged_three_level_coefficient_bits": 5632, + "row_coefficient": 19431, + "halving_depth": 3 + } + }, + "kappa": { + "pr535": { + "kappa": "793733736881443/500000000000000000", + "kappa_decimal": "0.001587467473762886", + "binding_component": "complex", + "complex_lower": "1589991533606044174031/1000000000000000000000000" + } + }, + "control": { + "word_content_sha256": "8639e22008c24a5690dd250eba702fe5fca85ddd036dcbec20baba77b7e213ba", + "five_stage_payload_bits": 102, + "bit_lower": "393688338238452162729/250000000000000000000000", + "bit_lower_decimal": "0.001574753352953808650916", + "profile": { + "1": 105600, + "2": 130560, + "4": 19680, + "6": 14400, + "7": 7680, + "8": 792, + "48": 1, + "56": 2742 + }, + "emitted_program_sha256": "7da5e3cb8daa231104f4e55f16c560fa56a59f10b8b2789e173923d9d2ca46fc", + "kappa": { + "pr484": "662515748707749/500000000000000000", + "pr471": "260706019542937/200000000000000000", + "pr458": "323880830669511/250000000000000000" + }, + "stacking": { + "calls": 88, + "calls_with_stacked_helpers": 80, + "max_helpers_on_one_family": 7 + } + }, + "live_bit": { + "source": "PR #510 (R480, nine stages), the bit c of PR #535", + "bit_lower": "396861776523137034277/250000000000000000000000", + "bit_lower_decimal": "0.001587447106092548137108" + }, + "pr535": { + "kappa": "1584931111785787/1000000000000000000", + "kappa_decimal": "0.001584931111785787", + "complex_lower": "1589991533606044174031/1000000000000000000000000" + }, + "hosting": { + "rule": "PR #442's pooled whole-residual integer programme (hosting_core.solve), eight replicas pooled, as in #472 / #493 / #503 / #510", + "stacking": { + "calls": 88, + "calls_with_stacked_helpers": 82, + "max_helpers_on_one_family": 7 + }, + "c_strict_per_stage": "0.001651282591289915996071", + "note": "strict = one helper per family copy per STAGE (stricter than #458 per invocation): a lower bound on the c of any #458-compliant hosting; kappa unchanged (complex-bound)" + } +} diff --git a/research/e8-bit-p111/vendor/pr545/code/adapt.py b/research/e8-bit-p111/vendor/pr545/code/adapt.py new file mode 100644 index 0000000000..bd1552af18 --- /dev/null +++ b/research/e8-bit-p111/vendor/pr545/code/adapt.py @@ -0,0 +1,136 @@ +# Parameterises PR #493's hosted-bit chain (emit, audit, local audit, assembly, hosting nominator, the two geometries, +# the independent checker and one record mutant) for a word with v <= 120 live ports and nine totals of any copy rank. +# Every change is an exact string edit of a pinned parent file; each anchor must occur exactly the listed number of +# times. Nothing is read from the environment: V, R and the sum of the nine copy ranks come from the word, or, in +# scripts that never see the word, from the exported records. For v = 120 and nine rank-8 totals (PR #493, PR #503) +# every adapted script computes exactly what the parent computes (verify.py, step 7, compares the outputs byte for byte). +# Prepared by DreamingOfClouds with Claude Opus 5.5 (Anthropic), via Claude Code; the parent files are PR #493's / #472's. Apache-2.0. +"""usage: adapt.py PARENT_PACKAGE WORD.json.gz OUTDIR + OUTDIR/emit.py, audit.py, local_audit.py, assemble.py, host_select.py, nine_geometry.py, eleven_geometry.py + OUTDIR/chk/checkword.py (+ relax.py) the pinned independent checker with ONE assertion changed (v == 120 -> v <= 120) + OUTDIR/mutate_records.py the record mutants, the 'cheaper COPY rank' one made relative to the true rank + WORD is baked into local_audit.py (the word whose records are audited), as PR #503's adapt.py does.""" +import sys +from pathlib import Path + +PARENT, WORD, OUT = Path(sys.argv[1]).resolve(), Path(sys.argv[2]).resolve(), Path(sys.argv[3]).resolve() +HERE = Path(__file__).resolve().parent # punct_cfg.py lives here +H_EMIT = str(PARENT / 'code/hub/e8-r740-replica32/code'); H_HOST = str(PARENT / 'code/hub/idle-axis-hosting') +PKG = str(PARENT) +PC = "sys.path.insert(0,%r)\nimport punct_cfg as PC\n" % str(HERE) + +# (anchor, replacement[, expected count]) +EDITS = { + 'code/eleven/emit.py': ('emit.py', [ + ('sys.path.insert(0,str(HERE.parent/"hub/e8-r740-replica32/code"))\n', "sys.path.insert(0,%r)\n" % H_EMIT), + ('sys.path.insert(0,str(HERE.parent/"hub/idle-axis-hosting"))\n', "sys.path.insert(0,%r)\n" % H_HOST), + ("import hosting_core as HC\n\nH,L,K,T,M=8,11,5,8,78\n", "import hosting_core as HC\n" + PC + "\nH,L,K,T,M=8,11,5,8,78\n"), + # V is the word's port count (a punctured word carries its live labels in ports_geom); config from the word + (" assert (w['h'],V,w['form'])==(8,120,'/2')\n", + " assert (w['h'],w['form'])==(8,'/2'); CFG=PC.config(w,%r); assert (CFG['v'],CFG['R'])==(V,R)\n" % PKG), + # the COPY rank of a total is the dimension of its frame (8 for #493 / #503, 7 for the star unit); the sum is derived + (" streams={}; checks={}; localH=None\n", " streams={}; checks={}; localH=None; CSTD=[]\n"), + ("norm=[1]*LOCAL+[0]; maxnorm=1; center=None; hist=Counter(); events=[]; copies=0; adds=0", + "norm=[1]*LOCAL+[0]; maxnorm=1; center=None; hist=Counter(); events=[]; copies=0; adds=0; crs=[]"), + ("copy_rank=abs(dim(c)-dim(f)); assert copy_rank==8", "copy_rank=abs(dim(c)-dim(f)); assert 0 6 else root / "data/complex-b.json").read_text())["suppliers"]}'), + ]), + 'discovery/host_select.py': ('host_select.py', [ + ('sys.path.insert(0, str(HERE.parent / "code" / "hub" / "idle-axis-hosting"))', "sys.path.insert(0, %r)" % H_HOST), + ("H, L, K, T, M, V = 8, 11, 5, 8, 78, 120\n", "H, L, K, T, M = 8, 11, 5, 8, 78\n"), + (" R = w['R']\n", " R = w['R']; V = w['v']\n"), + ]), + 'code/bit/independent/checkword.py': ('chk/checkword.py', [ + ("assert len(lines)==v==120 and h==8", + "assert len(lines)==v and 100<=v<=120 and h==8 # v live ports of the 120 E8 lines\n" + "# the live labels must be E8 root lines: members of the 120-line system of PR #493's word (the lines PR #370's checker accepts)\n" + "CANON={tuple(x) for l in json.load(gzip.open(%r,'rt'))['lines'] for x in (l,[-y for y in l])}\n" + "assert all(tuple(a) in CANON for a in lines), 'a label is not one of the 120 E8 root lines'" % str(PARENT / 'data/bit/bitE8mut-r489.json.gz')), + ("print('labels: 120 distinct lines of norm 2 (pairwise |ip| <= 1), spanning rank 8')", + "print('labels: %d distinct lines of norm 2 (pairwise |ip| <= 1), spanning rank 8'%v)"), + ]), + 'code/bit/mutate_records.py': ('mutate_records.py', [ + ("lambda e:e.__setitem__(5,7)", "lambda e:e.__setitem__(5,e[5]-1)"), + ]), +} +# the geometries build every port isometry by BFS over all 120 lines; only the live ports are emitted, re-indexed 0..v-1 +GEOM = lambda: [ + ("ap.add_argument('--out',type=Path,required=True); a=ap.parse_args(); t0=time.monotonic()", + "ap.add_argument('--out',type=Path,required=True); ap.add_argument('--word',type=Path,default=None); a=ap.parse_args(); t0=time.monotonic()"), + (" assert len(gs)==120\n", + " assert len(gs)==120\n" + " if a.word is None: LIVE=list(range(120))\n" + " else:\n" + " ww=json.loads(gzip.decompress(a.word.read_bytes())); LIVE=list(ww['ports_geom']) if 'ports_geom' in ww else list(range(120))\n" + " assert len(LIVE)==ww['v'] and all(ww['lines'][i]==list(map(int,lines[p])) for i,p in enumerate(LIVE))\n"), + (" for t,q in enumerate(lines):\n g=gs[t]; assert", " for t,src in enumerate(LIVE):\n q=lines[src]; g=gs[src]; assert"), + ("exact_bit_endpoint_operator_checks=240,", "exact_bit_endpoint_operator_checks=2*len(LIVE),"), + ("missing_route_factor_controls=120,", "missing_route_factor_controls=len(LIVE),"), +] +EDITS['code/nine/geometry.py'] = ('nine_geometry.py', GEOM() + [("route_checks=1080,", "route_checks=9*len(LIVE),")]) +EDITS['code/hub/r740-hosted-eleven/code/geometry.py'] = ('eleven_geometry.py', GEOM() + [("route_checks=1320,", "route_checks=11*len(LIVE),")]) + +OUT.mkdir(parents=True, exist_ok=True); (OUT / 'chk').mkdir(exist_ok=True) +n = 0 +for src, (dst, edits) in EDITS.items(): + text = (PARENT / src).read_text() + for e in edits: + old, new = e[0], e[1]; cnt = e[2] if len(e) > 2 else 1 + assert text.count(old) == cnt, (src, old, text.count(old)) + text = text.replace(old, new); n += 1 + (OUT / dst).write_text("# Adapted from PR #493's %s by research/e8-punctured111-star/code/adapt.py. Apache-2.0.\n" % src + text) +(OUT / 'chk/relax.py').write_bytes((PARENT / 'code/bit/independent/relax.py').read_bytes()) +print(' adapted %d parent files with %d listed edits (each anchor found exactly the listed number of times)' % (len(EDITS), n)) diff --git a/research/e8-bit-p111/vendor/pr545/code/punct_cfg.py b/research/e8-bit-p111/vendor/pr545/code/punct_cfg.py new file mode 100644 index 0000000000..ee5ee42498 --- /dev/null +++ b/research/e8-bit-p111/vendor/pr545/code/punct_cfg.py @@ -0,0 +1,79 @@ +# The one configuration of the punctured-word hosted chain: everything that was a hard-coded 120 / 72 / 9600 / 489 in +# PR #493's pipeline is derived here from the WORD (or, for scripts that never see the word, from the exported records). +# Prepared by DreamingOfClouds with Claude Opus 5.5 (Anthropic), via Claude Code. Apache-2.0. +"""Config of a bitE8/1 word for the hosted chain. + + v number of live ports (120 for PR #493 / #503, 111 for the punctured star unit) + R number of helper registers + ports_geom the live labels, as indices into the 120-line E8 system, in word order (absent for a full word) + totals map for each of the nine retained totals: the register, and the rank (frame dimension) it is COPYed from, + replayed here from the gates alone (72 = 9 x 8 for #493 / #503, 63 = 9 x 7 for the star unit) + +Nothing in here is read from the environment. `copy_ranks(w)` is an independent replay of the word's gates; the +emitter and the audit derive the same number from the exported records, and the chain asserts the two agree.""" +import gzip +import json +from fractions import Fraction as Fr +from pathlib import Path + + +def load(path): + p = Path(path) + return json.loads((gzip.decompress(p.read_bytes()) if p.suffix == '.gz' else p.read_bytes())) + + +def root120(pkg): + """The 120 labelled E8 root lines of PR #493's package (the order every geometry and port route is indexed by).""" + return load(Path(pkg) / 'data/bit/bitE8mut-r489.json.gz')['lines'] + + +def check_word(w, pkg): + """A full word (v = 120, no ports_geom) or a punctured one: live labels are a subsequence of the 120-line system.""" + v = w['v'] + if 'ports_geom' not in w: + assert v == 120 and len(w['lines']) == 120 + return + pg, r120 = w['ports_geom'], root120(pkg) + assert v < 120 and len(pg) == v == len(w['lines']) and list(pg) == sorted(set(pg)) and 0 <= pg[0] and pg[-1] < 120 + assert all(r120[p] == w['lines'][i] for i, p in enumerate(pg)), 'live lines are not the labelled 120-system lines' + if 'punctured' in w: + assert sorted(w['punctured'] + list(pg)) == list(range(120)) + # the frame-zero residual isometry of PR #493 (data/eleven/residual-isometries.json) is the coordinate frame 2e_0..2e_7, + # lines 0..7 of the 120-system; the stock validate_isometries(frames, lines) call needs them live at positions 0..7 + assert pg[:8] == list(range(8)), 'lines 0..7 must be live so the stock residual-isometry check applies unchanged' + + +def _rank(rows): + rows = [[Fr(x) for x in r] for r in rows] + r = 0 + for c in range(len(rows[0]) if rows else 0): + p = next((i for i in range(r, len(rows)) if rows[i][c] != 0), None) + if p is None: + continue + rows[r], rows[p] = rows[p], rows[r] + for i in range(r + 1, len(rows)): + if rows[i][c] != 0: + f = rows[i][c] / rows[r][c] + rows[i] = [a - f * b for a, b in zip(rows[i], rows[r])] + r += 1 + return r + + +def copy_ranks(w): + """Totals map: replay the gates D, A in order (a register stands at the frame of the last gate it was in) and read + the dimension of the frame each retained total is COPYed from. Frame 0 has dimension 0.""" + lines = w['lines'] + dims = [_rank([lines[i] for i in idx]) for idx in w['frames']] + cur = list(w['start']) + for gates in (w['D'], w['A']): + for f, d, srcs in gates: + for r in [d] + list(srcs): + cur[r] = f + return [dims[cur[q]] for q in w['ret']] + + +def config(w, pkg): + check_word(w, pkg) + cr = copy_ranks(w) + assert len(cr) == 9 and len(w['ret']) == 9 + return dict(v=w['v'], R=w['R'], copy_ranks=cr, cst=sum(cr), punctured=('ports_geom' in w)) diff --git a/research/e8-bit-p111/vendor/pr545/data/bitE8mut-v111-r489.json.gz b/research/e8-bit-p111/vendor/pr545/data/bitE8mut-v111-r489.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..f69cc130937c90fedea71f69e9fc1720f41224bf GIT binary patch literal 41837 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 literal 0 HcmV?d00001 diff --git a/research/e8-bit-p111/vendor/pr545/data/hosting-v111-r489.json b/research/e8-bit-p111/vendor/pr545/data/hosting-v111-r489.json new file mode 100644 index 0000000000..131d6cbc66 --- /dev/null +++ b/research/e8-bit-p111/vendor/pr545/data/hosting-v111-r489.json @@ -0,0 +1,213 @@ +{ + "bit": { + "patterns": [ + [ + 0, + [ + 8 + ], + 1776 + ], + [ + 1, + [], + 1776 + ], + [ + 2, + [ + 8, + 8, + 8 + ], + 1776 + ], + [ + 3, + [], + 1776 + ], + [ + 4, + [ + 8, + 8, + 8, + 8, + 8 + ], + 1776 + ], + [ + 5, + [], + 1776 + ], + [ + 6, + [], + 1776 + ], + [ + 7, + [], + 1776 + ], + [ + 8, + [], + 195 + ], + [ + 8, + [ + 8, + 8, + 8, + 8, + 8, + 8, + 8 + ], + 1581 + ], + [ + 9, + [ + 8, + 8, + 8, + 8, + 8 + ], + 1776 + ], + [ + 10, + [], + 1 + ], + [ + 10, + [ + 8, + 8, + 8 + ], + 1775 + ], + [ + 11, + [ + 8 + ], + 1776 + ] + ], + "proxy_saving": 186085.65977536424, + "stages": [ + [ + 0, + 8, + 1, + 1776 + ], + [ + 2, + 8, + 0, + 3912 + ], + [ + 2, + 8, + 1, + 1416 + ], + [ + 4, + 8, + 1, + 720 + ], + [ + 4, + 8, + 2, + 3912 + ], + [ + 4, + 8, + 4, + 3912 + ], + [ + 4, + 8, + 5, + 336 + ], + [ + 8, + 8, + 3, + 3912 + ], + [ + 8, + 8, + 6, + 2520 + ], + [ + 8, + 8, + 9, + 2499 + ], + [ + 8, + 8, + 10, + 2136 + ], + [ + 9, + 8, + 5, + 3576 + ], + [ + 9, + 8, + 6, + 1392 + ], + [ + 9, + 8, + 8, + 3912 + ], + [ + 10, + 8, + 7, + 3912 + ], + [ + 10, + 8, + 9, + 1413 + ], + [ + 11, + 8, + 10, + 1776 + ] + ] + } +} diff --git a/research/e8-bit-p111/verify.sh b/research/e8-bit-p111/verify.sh new file mode 100644 index 0000000000..e3f5289c67 --- /dev/null +++ b/research/e8-bit-p111/verify.sh @@ -0,0 +1,212 @@ +#!/usr/bin/env bash +# One-command verification of research/e8-bit-p111: PR #543's R420 bit word (L3) with PR #545's punctured-star unit: +# nine E8 lines are neither sources nor targets (v = 111) and the nine retained totals are pseudo-targets of frame rank 7 +# (decoder constant 63). The word is rebuilt from the frozen L3 plan and parameters, replayed exactly by an +# independent checker that also audits the interleaved scalar-interface lemma and the totals (with mutation controls), +# passed through the hub's official cohort checkers at (v, R), and priced by two hub pipelines: PR #510's nine-stage +# chain (PR #463 template, 32 replicas, PR #442 hosting), parameterised for v <= 120 by code/adapt9p.py, and PR #493's +# eleven-stage chain as parameterised by PR #545's code/adapt.py (vendored, run unchanged). Both feed PR #493's +# six-level, 47-inequality outer assembly with cited complex b. +# Controls: the parameterised nine-stage chain is byte-identical to the vendored one on PR #510's R480 word (and +# reproduces #510's c and kappa values); PR #545's word through the same PR #545 files reproduces #545's c and program +# hash; the cited receipts of PR #548, #544 and #535 reproduce their own kappa through the same assembly. +# Runs on top of the repository's main branch: it uses research/e8-bit-carrier-skeleton (PR #493) and +# research/direct-delivery504-global/EXPECTED.json (PR #531), both pinned (DEPENDS.json). +# usage: bash verify.sh /fresh/output/dir +# needs python3 >= 3.11 with numpy, sympy and scipy (requirements.txt), a C++17 compiler (CXX, default c++) and +# Boost headers (BOOST_INCLUDE, default /usr/include). +set -euo pipefail +HERE=$(cd "$(dirname "$0")" && pwd); WD=$(mkdir -p "$1" && cd "$1" && pwd) +P=$(cd "$HERE/../e8-bit-carrier-skeleton" && pwd) +case "$WD/" in "$HERE"/*|"$P"/*) echo "output dir must be outside the packages"; exit 2;; esac +export PYTHONDONTWRITEBYTECODE=1 +PY="${PYTHON:-python3} -I -B" +PYS="${PYTHON:-python3} -E -s -B" # hub scripts that import siblings from their own directory +CXX=${CXX:-c++}; BI=${BOOST_INCLUDE:-/usr/include} +C=$HERE/code; D=$HERE/data; EXP=$HERE/EXPECTED.json; WORD=$D/bitE8mut-p111.json.gz +PC=$P/code; B=$PC/bit; CK=$B/checkers; HUB=$PC/hub; INC="-I$P/portable-include -I$CK -I$BI" +V510=$HERE/vendor/pr510/e8-r480-nine-stage; VK=$V510/vendor/e8-r715-eleven-stage-whole/code +A545=$HERE/vendor/pr545/code; W545=$HERE/vendor/pr545/data/bitE8mut-v111-r489.json.gz +W480=$HERE/vendor/pr510/e8-r480-dead-totals/data/bitE8mut-r480.json.gz; W489=$P/data/bit/bitE8mut-r489.json.gz +ISO=$P/data/eleven/residual-isometries.json; BIN=$WD/bin; mkdir -p "$BIN" +V=$($PY -c "import json,gzip;print(json.load(gzip.open('$WORD','rt'))['v'])") +R=$($PY -c "import json,gzip;print(json.load(gzip.open('$WORD','rt'))['R'])") +T0=$SECONDS +step() { echo "== $1 [$((SECONDS - T0))s]"; } + +step "0. manifest, provenance (vendored files by blob id, main's files by sha256), cited receipts" +(cd "$HERE" && $PY -c " +import hashlib,os,sys +bad=0;listed=set() +for line in open('MANIFEST.sha256'): + h,p=line.rstrip('\n').split(' ',1);listed.add(p) + if hashlib.sha256(open(p,'rb').read()).hexdigest()!=h: print('MISMATCH',p);bad+=1 +files={os.path.relpath(os.path.join(r,f)) for r,_,fs in os.walk('.') for f in fs if '__pycache__' not in r}-{'MANIFEST.sha256'} +if files-listed: print('UNLISTED',sorted(files-listed));bad+=1 +sys.exit(1 if bad else 0)") && echo " all files match MANIFEST.sha256" +$PY $C/provenance.py $HERE +$PY $C/cited.py $HERE +$PY $C/receipt_kappa.py $HERE +mkdir -p $WD/root/data && ln -sfn $PC $WD/root/code && cp $D/complex-b.json $WD/root/data/ # assembly root + +step "1. the word, rebuilt from the frozen L3 plan and parameters" +$PY $C/gen_p3.py $D/plan-l3.json $D/punct.json $WD/word.json.gz | sed 's/^/ /' +cmp $WD/word.json.gz $WORD +$PY -c " +import json,gzip,hashlib +w=json.load(gzip.open('$WORD','rt'));e=json.load(open('$EXP'))['word'] +h=hashlib.sha256(json.dumps(w,sort_keys=True,separators=(',',':')).encode()).hexdigest() +assert h==e['content_sha256'] and (w['v'],w['R'])==(e['v'],e['R']) and w['punctured']==e['punctured'] and len(w['ret'])==9 +assert {k:len(w[k]) for k in 'DABC'}==e['gates'] +print(' rebuilt word is byte-identical to the shipped word (content sha256',h[:16]+'...); v',w['v'],'R',w['R'],'punctured',w['punctured'])" + +step "2. independent exact replay, totals, the interleaved interface lemma, mutation controls" +$PY $C/xcheck_p.py $WORD --json $WD/xcheck.json | sed 's/^/ /' +$PY $C/mutants.py $C/xcheck_p.py $WORD --json $WD/mutants.json | sed 's/^/ /' +$PY $C/mutants_p.py $C/xcheck_p.py $WORD --json $WD/mutants_p.json | sed 's/^/ /' +for w in $W480 $W489 $W545; do $PY $C/xcheck_p.py $w | tail -1 | sed "s|^| [$(basename $w)] |"; done + +step "3. hub checkers: PR #545's v <= 120 form of PR #370's checker refuses (source writes) and its relaxed copy passes; PR #493's interface.py refuses" +$PY $A545/adapt.py $P $WORD $WD/ad545 | sed 's/^/ /' +if $PY $WD/ad545/chk/checkword.py $WORD $PC/pricing > $WD/stock.out 2> $WD/stock.err; then echo "stock-form checker accepted"; exit 1; fi +grep -q "x register written" $WD/stock.err && echo " stock-form checker refuses, as expected: $(tail -1 $WD/stock.err)" +$PY $WD/ad545/chk/relax.py $WD/rchk > /dev/null +$PY $WD/rchk/checkword_mut.py $WORD $PC/pricing | tee $WD/checkword_mut.txt | sed 's/^/ /' +if $PY $B/independent/interface.py $WORD > $WD/iface493.out 2> $WD/iface493.err; then echo "PR #493 interface.py accepted"; exit 1; fi +grep -q "phase B writes a z register" $WD/iface493.err && echo " PR #493 interface.py refuses, as expected: its check (4) requires phase B to write only targets" + +step "4. official cohort checkers at (v, R): legality (NCOPY = 9), formal columns at five, nine and eleven stages, Gram witnesses" +$PY $B/export_records.py $WORD $WD/records | sed 's/^/ /' +$PY $B/regression/stage_cohort.py $WD/records $WD/records $WD/cin $WD/cout +$CXX -O2 -std=c++17 -DHDIM=8 -DNCOPY=9 $INC -o $BIN/legality $CK/legality_h.cpp & +$CXX -O2 -std=c++17 -DHDIM=8 $INC -o $BIN/legality_ncopy_h $CK/legality_h.cpp & +for vr in "$V $R" "120 480" "111 489"; do set -- $vr; $CXX -O2 -std=c++17 -DHDIM=8 -DNCOPY=9 -DVPORTS=$1 -DRHELP=$2 $INC -o $BIN/columns_h_$1_$2 $CK/columns_h.cpp & done +wait +for K in 2 4 5; do $CXX -O2 -std=c++17 -DHDIM=8 -DNCOPY=9 -DVPORTS=$V -DRHELP=$R -DKPAIRS=$K $INC -o $BIN/columns_k$K $CK/columns_k.cpp & done +wait +$BIN/legality $WD/cin $WD/cout $WD/cout/legality.json > /dev/null +$BIN/columns_h_${V}_${R} $WD/cout/COHORT249-RECORDS.bin $WD/cout/columns.json > /dev/null 2> $WD/cout/columns.log +for K in 2 4 5; do $BIN/columns_k$K $WD/cout/COHORT249-RECORDS.bin $WD/cout/columns_k$K.json > /dev/null 2> $WD/cout/columns_k$K.log; done +$PY -c " +import json +V,R=$V,$R +l=json.load(open('$WD/cout/legality.json'));c=json.load(open('$WD/cout/columns.json')) +k4=json.load(open('$WD/cout/columns_k4.json'));k5=json.load(open('$WD/cout/columns_k5.json'));k2=json.load(open('$WD/cout/columns_k2.json')) +assert l['status']=='PASS_INDEPENDENT_COHORT_LEGALITY_SOURCE_OWNERS_AND_LIFECYCLE' and l['copies']==l['erasures']==9 +assert l['rank_mass']==8*R+63+14*V +print(' legality PASS:',l['records'],'records,',l['common_frame_ADDs'],'common-frame ADDs, rank mass',l['rank_mass'],'= 8R + 63 + 14v; 9 COPY/ERASE') +assert c['status']=='PASS_NEW_COHORT249_ALL_FIVE_STAGE_FORMAL_COLUMNS_AND_PREFIX_BILL' and c['formal_columns_checked']==4*V+R +print(' five-stage formal columns PASS:',c['formal_columns_checked'],'physical columns over F2; prefix payload',c['payload_prefix_bits'],'bits < 104') +for k,S,nb in ((k4,9,18),(k5,11,24)): + assert k['status']=='PASS_NEW_COHORT249_ALL_BRIDGED_STAGE_FORMAL_COLUMNS_AND_PREFIX_BILL' + assert k['formal_columns_checked']==(S-1)*V+R and len(k['stages'])==S and k['bridge_additions']==nb*V + assert len(k['negative_controls'])==nb and all(x['rejected'] for x in k['negative_controls']) + assert dict(map(tuple,k['local_raw_H']))==dict(map(tuple,c['local_raw_H'])) + print(' %s-stage formal columns PASS (columns_k, KPAIRS = %d): %d columns, %d bridge additions, %d/%d omitted-bridge controls rejected, payload %d bits'%(['nine','eleven'][S==11],(S-1)//2,k['formal_columns_checked'],k['bridge_additions'],nb,nb,k['payload_prefix_bits'])) +for d in (c,k2): d.pop('seconds') +assert c==k2;print(' regression: columns_k at KPAIRS = 2 reproduces the five-stage receipt field for field')" +$PY $WD/ad545/mutate_records.py $WD/records $WD/cin $WD/cout $BIN/legality $BIN/columns_k5 $BIN/legality_ncopy_h | tail -1 | sed 's/^/ /' +$PY $C/mutrec_b.py $WORD $WD/records $WD/cin $WD/cout $BIN/legality $BIN/columns_h_${V}_${R} $BIN/columns_k4 $BIN/columns_k5 | tail -1 | sed 's/^/ /' +$PY $CK/gram_frames.py $WD/records --gram e8ham --h 8 --positive | sed 's/^/ /' + +step "5. geometry (live labels: PR #545's parameterised PR #386 / PR #445 files), nine-stage files parameterised; controls start in the background" +mkdir -p $WD/nine $WD/bit +$PY $WD/ad545/nine_geometry.py --source $P --out $WD/nine --word $WORD > /dev/null +$PY $WD/ad545/eleven_geometry.py --source $P --out $WD/bit --word $WORD > /dev/null +$PYS $V510/code/adapt.py $VK $WD/adapted9 | sed 's/^/ /' +$PY $C/adapt9p.py $VK $V510/code $WD/adapted9/audit_parent_k.py $WD/a9p | sed 's/^/ /' +nine() { # word R local_check_dir out_dir frozen_selection suppliers chain(stock|param) + local w=$1 r=$2 lc=$3 o=$4 sel=$5 sup=$6 G AP EM AH AS + if [ "$7" = stock ]; then G=$VK/global_k.py; AP=$WD/adapted9/audit_parent_k.py; EM=$VK/emit_k.py; AH=$VK/audit_hosted_k.py; AS=$V510/code/assemble9.py + else G=$WD/a9p/global_k.py; AP=$WD/a9p/audit_parent_k.py; EM=$WD/a9p/emit_k.py; AH=$WD/a9p/audit_hosted_k.py; AS=$WD/a9p/assemble9.py; fi + mkdir -p $o/v9/nine; cp $o/GEOMETRY9.json $o/v9/nine/GEOMETRY.json; ln -sfn $lc/records $o/v9/records + $PYS $G --word $w --local-check $lc --out $o/v9/nine --isometries $ISO > $o/v9/global.log + $PYS $AP --local-check $lc --out $o/v9/nine --word $w > $o/v9/audit_parent.log + $PYS $EM $o/v9/nine $lc $o/v9/hosted --selection $o/selection9.json --key bit --discover > $o/v9/emit.log + if ! $PY -c " +import json,sys +a=json.load(open('$o/selection9.json'))['bit'];b=json.load(open('$sel'))['bit'] +sys.exit(0 if a['patterns']==b['patterns'] and a['stages']==b['stages'] and abs(a['proxy_saving']-b['proxy_saving'])<=1e-9*abs(b['proxy_saving']) else 1)"; then + echo " NOTE: the hosting programme returned another selection here; pricing the frozen one" + cp $sel $o/selection9.json; rm -rf $o/v9/hosted + $PYS $EM $o/v9/nine $lc $o/v9/hosted --selection $o/selection9.json --key bit > $o/v9/emit.log + else echo " the hosting programme rediscovers the frozen nine-stage selection ($(basename $sel)) exactly"; fi + $PYS $AH $o/v9 $o/v9/hosted $o/v9/hosted/AUDIT.json > $o/v9/audit_hosted.log + $PYS $AS $o/v9 $o/v9/hosted $o/v9/hosted/AUDIT.json $WD/root $o/RESULT9.json $sup + $PY -c " +import json +g=json.load(open('$o/v9/nine/GLOBAL.json'));ap=json.load(open('$o/v9/nine/AUDIT.json'));au=json.load(open('$o/v9/hosted/AUDIT.json')) +assert g['R']==$r and g['replicas']==32 and g['m']==64 and g['local_invocations']==9*32 +assert ap['status']=='PASS_INDEPENDENT_COMPRESSED_BIT_TEMPLATE_AUDIT' and au['status']=='PASS_INDEPENDENT_REPLICATED_HOSTING_AUDIT' +print(' template %s (v %d): %d unbanked scalar columns, %d omitted bridge shears rejected; parent audit: %d template mutations rejected'%(g['status'],g['v'],g['independent_unbanked_scalar_columns'],len(g['omitted_bridge_shears_rejected']),len(ap['mutations_rejected']))) +print(' hosted audit PASS: %d of %d helper residuals hosted whole, %d banks, %d renamed instructions, %d mutations rejected, deficit %d'%(au['hosted_residuals'],9*32*$r,au['bank_assignments'],au['emitted_events'],len(au['mutations_rejected']),au['deficit']))" +} +local_check() { # word V R out_dir: records, legality and five-stage columns (the local check the pipelines read) + $PY $B/export_records.py $1 $4/records > /dev/null + $PY $B/regression/stage_cohort.py $4/records $4/records $4/cin $4/cout + $BIN/legality $4/cin $4/cout $4/cout/legality.json > /dev/null + $BIN/columns_h_$2_$3 $4/cout/COHORT249-RECORDS.bin $4/cout/columns.json > /dev/null 2> $4/cout/columns.log +} +( set -e; mkdir -p $WD/c480/stock $WD/c480/param; local_check $W480 120 480 $WD/c480 + $PY $PC/nine/geometry.py --source $P --out $WD/c480/geo > /dev/null + cp $WD/c480/geo/GEOMETRY.json $WD/c480/stock/GEOMETRY9.json; cp $WD/c480/geo/GEOMETRY.json $WD/c480/param/GEOMETRY9.json + echo "== control: PR #510's R480 word through PR #510's nine-stage chain (vendored files) and through the parameterised files" + nine $W480 480 $WD/c480 $WD/c480/stock $V510/data/selection9-r480.json pr535,pr531,pr484,pr471,pr458 stock + nine $W480 480 $WD/c480 $WD/c480/param $V510/data/selection9-r480.json pr535,pr531,pr484,pr471,pr458 param + $PY -c " +import json,filecmp +s,p='$WD/c480/stock','$WD/c480/param' +J=lambda x:json.load(open(x)) +def n(x): + d=J(x);d.pop('seconds',None);return d +same=dict(parent_global=n(s+'/v9/nine/GLOBAL.json')==n(p+'/v9/nine/GLOBAL.json'), + parent_template=filecmp.cmp(s+'/v9/nine/GLOBAL-TEMPLATE.json.gz',p+'/v9/nine/GLOBAL-TEMPLATE.json.gz',shallow=False), + parent_audit=n(s+'/v9/nine/AUDIT.json')==n(p+'/v9/nine/AUDIT.json'), + hosted_global=n(s+'/v9/hosted/GLOBAL.json')==n(p+'/v9/hosted/GLOBAL.json'), + hosted_template=filecmp.cmp(s+'/v9/hosted/GLOBAL-TEMPLATE.json.gz',p+'/v9/hosted/GLOBAL-TEMPLATE.json.gz',shallow=False), + hosted_audit=filecmp.cmp(s+'/v9/hosted/AUDIT.json',p+'/v9/hosted/AUDIT.json',shallow=False), + result=filecmp.cmp(s+'/RESULT9.json',p+'/RESULT9.json',shallow=False)) +assert all(same.values()),same +print(' parameterised nine-stage files == vendored files on the R480 word: %s'%', '.join(same))" ) > $WD/c480.log 2>&1 & +PID480=$! +( set -e; mkdir -p $WD/c545/bit $WD/c545/nine; local_check $W545 111 489 $WD/c545 + echo "== control: PR #545's word (v = 111, R = 489) through PR #545's adapted eleven-stage files" + $PY $A545/adapt.py $P $W545 $WD/c545/ad > /dev/null + $PY $WD/c545/ad/nine_geometry.py --source $P --out $WD/c545/nine --word $W545 > /dev/null + $PY $WD/c545/ad/eleven_geometry.py --source $P --out $WD/c545/bit --word $W545 > /dev/null + $PY $WD/c545/ad/host_select.py $W545 $WD/c545/hosting.json > /dev/null + $PY -c " +import json;a=json.load(open('$WD/c545/hosting.json'));b=json.load(open('$HERE/vendor/pr545/data/hosting-v111-r489.json')) +assert a==b;print(' PR #545 host_select.py rediscovers its frozen hosting selection exactly')" + $PYS $WD/c545/ad/emit.py --word $W545 --local-check $WD/c545 --out $WD/c545/bit --isometries $ISO --hosting $HERE/vendor/pr545/data/hosting-v111-r489.json > $WD/c545/emit.log + $PYS $WD/c545/ad/audit.py $WD/c545 $WD/c545/nine $WD/c545/bit > $WD/c545/audit.log + $PY $WD/c545/ad/assemble.py $WD/c545/bit 489 $P $WD/c545/RESULT11.json pr535 $WD/root/data/complex-b.json ) > $WD/c545.log 2>&1 & +PID545=$! + +step "6. nine-stage whole-residual hosting and price (PR #510's chain, parameterised: PR #463 template and audits, 32 replicas)" +cp $WD/nine/GEOMETRY.json $WD/GEOMETRY9.json +nine $WORD $R $WD $WD $D/selection9-p111.json pr548,pr544,pr535,pr531 param + +step "7. eleven-stage whole-residual hosting and price (PR #493's chain as parameterised by PR #545's adapt.py)" +$PY $WD/ad545/host_select.py $WORD $WD/hosting11.json > /dev/null +$PY -c " +import json +a=json.load(open('$WD/hosting11.json'))['bit'];b=json.load(open('$D/hosting11-p111.json'))['bit'] +same=a['patterns']==b['patterns'] and a['stages']==b['stages'] +print(' PR #442 hosting programme (PR #545 host_select.py)',('rediscovers the frozen eleven-stage selection exactly' if same else 'returns another selection here; the frozen one is priced'))" +$PYS $WD/ad545/emit.py --word $WORD --local-check $WD --out $WD/bit --isometries $ISO --hosting $D/hosting11-p111.json > $WD/bit/emit.log +$PYS $WD/ad545/audit.py $WD $WD/nine $WD/bit > $WD/bit/audit.log +$PY $WD/ad545/assemble.py $WD/bit $R $P $WD/RESULT11.json pr548,pr544,pr535,pr531 $WD/root/data/complex-b.json +$PY -c " +import json;g=json.load(open('$WD/bit/GLOBAL.json'));a=json.load(open('$WD/bit/AUDIT.json')) +print(' template: 88 calls,',g['hosted_residuals'],'whole helper residuals hosted on idle data families (no banks),',g['independent_unbanked_scalar_columns'],'unbanked scalar columns restored, deficit',g['deficit']) +print(' independent audit PASS:',a['emitted_events'],'renamed instructions,',len(a['mutations_rejected']),'template mutations rejected, physical prefix',a['physical_prefix_bits'],'bits')" + +step "8. controls (background jobs)" +wait $PID480 || { cat $WD/c480.log; exit 1; } +wait $PID545 || { cat $WD/c545.log; exit 1; } +cat $WD/c480.log $WD/c545.log + +step "9. summary against EXPECTED.json and the cited receipts" +$PY $C/summary.py $HERE $WD +echo "== done [$((SECONDS - T0))s]" From a5afc8c6169ea61ba7c51dfd3cade02cc7b13b46 Mon Sep 17 00:00:00 2001 From: DreamingOfClouds Date: Mon, 12 Oct 2026 08:13:09 -0500 Subject: [PATCH 2/2] Tune PR #555's punctured R439 bit word with two A-section transports: nine-stage c 0.001733427039589844234749 Hoist one summand of a D4 mini-node gate to its rank-1 frame and sink another to the rank-7 pseudo-total frame (tune/moves.json, tune/apply.py), found by running the mechanisms of PR #541, #542 and #548 as generic searches on PR #555's word. Nine-stage c +0.0216% (eleven-stage 0.001710951900777583975622, +0.0201%); kappa unchanged (complex-bound). tune/verify.sh replays the stacked word through PR #555's chains; PR #555's verify.sh is unchanged. MANIFEST.sha256 lists tune/. Co-Authored-By: Claude Opus 5.5 --- research/e8-bit-p111/MANIFEST.sha256 | 12 +- research/e8-bit-p111/README.md | 2 + research/e8-bit-p111/tune/EXPECTED.json | 222 ++++++++++ research/e8-bit-p111/tune/README.md | 87 ++++ research/e8-bit-p111/tune/apply.py | 64 +++ .../tune/bitE8mut-p111-tuned.json.gz | Bin 0 -> 42353 bytes research/e8-bit-p111/tune/fastc.py | 25 ++ research/e8-bit-p111/tune/moves.json | 12 + research/e8-bit-p111/tune/summary.py | 116 ++++++ research/e8-bit-p111/tune/tune.py | 392 ++++++++++++++++++ research/e8-bit-p111/tune/verify.sh | 163 ++++++++ research/e8-bit-p111/tune/wlib.py | 75 ++++ 12 files changed, 1169 insertions(+), 1 deletion(-) create mode 100644 research/e8-bit-p111/tune/EXPECTED.json create mode 100644 research/e8-bit-p111/tune/README.md create mode 100644 research/e8-bit-p111/tune/apply.py create mode 100644 research/e8-bit-p111/tune/bitE8mut-p111-tuned.json.gz create mode 100644 research/e8-bit-p111/tune/fastc.py create mode 100644 research/e8-bit-p111/tune/moves.json create mode 100644 research/e8-bit-p111/tune/summary.py create mode 100644 research/e8-bit-p111/tune/tune.py create mode 100755 research/e8-bit-p111/tune/verify.sh create mode 100644 research/e8-bit-p111/tune/wlib.py diff --git a/research/e8-bit-p111/MANIFEST.sha256 b/research/e8-bit-p111/MANIFEST.sha256 index ac320af953..98b1e7e002 100644 --- a/research/e8-bit-p111/MANIFEST.sha256 +++ b/research/e8-bit-p111/MANIFEST.sha256 @@ -2,7 +2,7 @@ fa9253078c444b8f4a57e849048f727c3c8233c3dc3c7f651bef2cf7bb0178c5 DEPENDS.json 7c2675fd49afd7e328606399a4421fd49c43fe8a6c5b57154cc908c00588abe2 EXPECTED.json c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4 LICENSE 5b88eb2b7de76aa2a3288ee3c7a08d18a5eadfa82f5e7a99e207d232aed71221 NOTICE.md -481431e35bd831ec3d0bb8edc01abdfa49d5caeabebab67ac839a35053e9ef85 README.md +a054ce8453e2936d45c4edd1c3678993903fcf6705ac241b9f1a4c4f9accda2d README.md 5e84c26ba837ce029acd1b5089e5533d2ebde793deede84215322112e95e06f8 SOURCE-pr510.json 74254fde5cff5580d5c199b139ff3ce31a3defded3f653a3ba83cf0c9204ddc4 SOURCE-pr535.json 9ae1cfa69158f678499510e9cf24396b2bdd9b68979e906cbb8d4ee29b63e0b9 SOURCE-pr544.json @@ -36,6 +36,16 @@ dd371e1d6fe57602f242920b582ec4c133efc8ede4a849fd701ac90b7f6e1853 data/punct.jso 18d77e8f62ffc671b3b3d3e50626c03481a2eaf6fb4abbc131e08226feebc180 discovery/choose.py edd876212edf0db3f6620534dcf50b5d2c23197c2d5f8af8c030ed5bc8447923 discovery/search.py e11a6f5c2a40b77fbf54a31a4c45bac575e9d2f765d35b657406d5f088fd0664 requirements.txt +b9f0e5ca038abe837b0007c8fe006f366a6f02009244ac57705a8a200e2301f4 tune/EXPECTED.json +46c7917fb252949f38b7bcd88cf568efc282e3ea065b61d11e457e41673d0b2d tune/README.md +806ae4cb2648e4f05dcf56bd089891fd3ba33a6ee5fe76d3329e322c6770c1a1 tune/apply.py +e3e301e62bc2b41dbf1ceac0acf3d35824ebeab1848c5a2142d4577b109da52f tune/bitE8mut-p111-tuned.json.gz +ae51f1fbb22b8c8dac8aab787c798361f6aa5d4e1fefb29e43d0197899d4eab5 tune/fastc.py +a113933002239eb2dd32726bd572ba8ca41b5bce0d438327dd770754c8d5e0f4 tune/moves.json +24841f9f5b039f05cfa934295203c982faf181cf28b9b1a71e50d1a35dc723d0 tune/summary.py +bdaf632852c514b257bcb1b81004a942fc022ab876565b4e5f6ff5a038d323c2 tune/tune.py +efb1051d3e2c6119574a29a8e4efbe316307b5901d05079d029e12fae9ff1fb1 tune/verify.sh +a37fdf3cdf5f62945c2a90193401c0e9edc9dd3cda8d8cb36a3b9c329ebe4dbc tune/wlib.py 92e399e9a486b55229b5624632ac54f800dd9e022f2aee97f1e5f264bbc0ef5c vendor/pr510/e8-r480-dead-totals/data/bitE8mut-r480.json.gz 883f7a1a84f31d9e4430e348bc8d9c1ec10210b206ff602dbe126349056af13d vendor/pr510/e8-r480-nine-stage/EXPECTED.json 27b6b77cc4ebf439f9019e63a22dedc1ef72ee65fe120aad3d17316b1df06d7b vendor/pr510/e8-r480-nine-stage/code/adapt.py diff --git a/research/e8-bit-p111/README.md b/research/e8-bit-p111/README.md index 16a72c18a4..40dff2216e 100644 --- a/research/e8-bit-p111/README.md +++ b/research/e8-bit-p111/README.md @@ -154,3 +154,5 @@ minutes on 4 cores (the two controls run in the background). `punct_cfg.py`, and its word, hosting selection and EXPECTED.json (control). - `discovery/` (not run by `verify.sh`): the nomination of P and of the generator parameters. - `DEPENDS.json`: the files of main this package uses, pinned. +- `tune/`: two A-section transports of this word (nine-stage c 0.001733427039589844234749, +0.0216%; eleven-stage 0.001710951900777583975622; + no κ changes), with its own `tune/verify.sh`. This `verify.sh` is unchanged and checks the untuned word. diff --git a/research/e8-bit-p111/tune/EXPECTED.json b/research/e8-bit-p111/tune/EXPECTED.json new file mode 100644 index 0000000000..e293ddc557 --- /dev/null +++ b/research/e8-bit-p111/tune/EXPECTED.json @@ -0,0 +1,222 @@ +{ + "word": { + "file": "tune/bitE8mut-p111-tuned.json.gz", + "file_sha256": "e3e301e62bc2b41dbf1ceac0acf3d35824ebeab1848c5a2142d4577b109da52f", + "content_sha256": "5642fd0e35a1acc11acff8d0c08350c1617e95f310d4d35ad63a651f9eeeba0b", + "v": 111, + "R": 439, + "punctured": [ + 10, + 15, + 51, + 53, + 56, + 66, + 72, + 89, + 111 + ], + "retained_totals": 9, + "copy_ranks": [ + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 7 + ], + "decoder_constant": 63, + "gates": { + "D": 111, + "A": 2075, + "B": 712, + "C": 2574 + }, + "local_histogram": { + "1": 1323, + "2": 1342, + "3": 79, + "4": 44, + "5": 102, + "6": 11, + "7": 19 + }, + "legality": { + "records": 27234, + "common_frame_ADDs": 24305, + "rank_mass": 5129 + }, + "five_stage_columns": 883, + "five_stage_payload_bits": 93, + "nine_stage_columns": 1327, + "nine_stage_payload_bits": 93, + "eleven_stage_columns": 1549, + "eleven_stage_payload_bits": 93, + "base_content_sha256": "d0df3c89ae84989f4045ba1681a5e2e9fcdfa38ed0301c8abf5185537155cbe3" + }, + "interface": { + "hypotheses": { + "H1_no_target_feeds_z": true, + "H2_D_frame0_targets_from_helpers": true, + "H3_A_z_only_scatter_targets_B_split": true, + "H4_C_literal_reverse_at_full": true, + "H5_V_identity": true + }, + "conclusion": { + "U_block_form": true, + "J_identity": true, + "K_equals_D": true, + "C_is_U_inverse": true + }, + "interleaving": { + "z_gates_A": 2075, + "source_writes_A": 0, + "z_gates_B": 499, + "source_writes_B": 483, + "helper_writes_B": 16, + "sources_rewritten_B": 108, + "B_z_write_frame_dims": { + "4": 388, + "6": 111 + }, + "B_z_operand_kinds": { + "helper<-helper": 3, + "helper<-source": 13, + "source<-helper": 108, + "source<-source": 375 + }, + "B_z_operand_kinds_by_dim": { + "dim4:helper<-source": 13, + "dim4:source<-source": 375, + "dim6:helper<-helper": 3, + "dim6:source<-helper": 108 + }, + "B_z_writes_after_a_target_read_of_the_same_register": 454, + "B_target_reads_of_registers_rewritten_earlier_in_B": 111, + "target_reads_of_targets": 0, + "cleanup_gates": 2574 + } + }, + "nine": { + "pipeline": "PR #510 (PR #463 nine-stage template, 32 replicas, PR #442 hosting, PR #493 outer assembly), parameterised for v <= 120 by code/adapt9p.py", + "bit_lower": "1733427039589844234749/1000000000000000000000000", + "bit_lower_decimal": "0.001733427039589844234749", + "profile": { + "1": 381024, + "2": 400416, + "3": 22752, + "4": 26880, + "5": 29376, + "6": 17376, + "7": 26784, + "42": 288 + }, + "bit": { + "live": 28416, + "physical": 28425, + "maxchild": 42, + "rank_mass": 1808352, + "hosted_residuals": 126432, + "bank_assignments": 0, + "prefix_bits": 177, + "charged_prefix_bits": 192, + "coefficient_bits": 177, + "two_level_coefficient_bits": 393, + "charged_two_level_coefficient_bits": 512, + "row_coefficient": 18355, + "halving_depth": 2 + }, + "kappa": { + "pr548": { + "kappa": "49718254847979/31250000000000000", + "kappa_decimal": "0.001590984155135328", + "complex_lower": "796759709641153381751/500000000000000000000000", + "binding_component": "complex" + }, + "pr544": { + "kappa": "397677800014309/250000000000000000", + "kappa_decimal": "0.001590711200057236", + "complex_lower": "31864911873350496687/20000000000000000000000", + "binding_component": "complex" + }, + "pr535": { + "kappa": "793733736881443/500000000000000000", + "kappa_decimal": "0.001587467473762886", + "complex_lower": "1589991533606044174031/1000000000000000000000000", + "binding_component": "complex" + }, + "pr531": { + "kappa": "1517760977820331/1000000000000000000", + "kappa_decimal": "0.001517760977820331", + "complex_lower": "304013615566499291631/200000000000000000000000", + "binding_component": "complex" + } + } + }, + "eleven": { + "pipeline": "PR #493 (PR #445 geometry, PR #472 emitter, audit and invoice, PR #442 hosting), parameterised by PR #545's code/adapt.py", + "bit_lower": "855475950388791987811/500000000000000000000000", + "bit_lower_decimal": "0.001710951900777583975622", + "profile": { + "1": 116424, + "2": 121648, + "3": 6952, + "4": 7424, + "5": 8976, + "6": 4520, + "7": 8776, + "56": 2600 + }, + "bit": { + "live": 8880, + "physical": 8889, + "maxchild": 56, + "rank_mass": 689304, + "hosted_residuals": 38632, + "prefix_bits": 4304, + "charged_prefix_bits": 4352, + "coefficient_bits": 4304, + "three_level_coefficient_bits": 12966, + "charged_three_level_coefficient_bits": 12992, + "row_coefficient": 19431, + "halving_depth": 3 + }, + "emitted_program_sha256": "793b6f40c9aa4b8db57fec0ac0973e16884dae28707e482ee8f7a2349a5d5a4b", + "kappa": { + "pr548": { + "kappa": "49718254847979/31250000000000000", + "kappa_decimal": "0.001590984155135328", + "complex_lower": "796759709641153381751/500000000000000000000000", + "binding_component": "complex" + }, + "pr544": { + "kappa": "397677800014309/250000000000000000", + "kappa_decimal": "0.001590711200057236", + "complex_lower": "31864911873350496687/20000000000000000000000", + "binding_component": "complex" + }, + "pr535": { + "kappa": "793733736881443/500000000000000000", + "kappa_decimal": "0.001587467473762886", + "complex_lower": "1589991533606044174031/1000000000000000000000000", + "binding_component": "complex" + }, + "pr531": { + "kappa": "1517760977820331/1000000000000000000", + "kappa_decimal": "0.001517760977820331", + "complex_lower": "304013615566499291631/200000000000000000000000", + "binding_component": "complex" + } + } + }, + "base": { + "nine_bit_lower": "1733052823330162099157/1000000000000000000000000", + "nine_bit_lower_decimal": "0.001733052823330162099157", + "eleven_bit_lower": "427652201524503168379/250000000000000000000000", + "eleven_bit_lower_decimal": "0.001710608806098012673516", + "note": "PR #555's EXPECTED.json (untuned word); the tune/ pipeline is the same, with the same frozen hosting selections" + } +} diff --git a/research/e8-bit-p111/tune/README.md b/research/e8-bit-p111/tune/README.md new file mode 100644 index 0000000000..5b15543696 --- /dev/null +++ b/research/e8-bit-p111/tune/README.md @@ -0,0 +1,87 @@ +# PR #555's punctured R439 bit word with two A-section transports + +Two helper additions of PR #555's word (`data/bitE8mut-p111.json.gz`, v = 111, R = 439) are moved out of the D4 block-frame +mini-node gates they sit in and rerun at other frames. Nothing else changes: same registers, same scatter, same retained +totals (nine COPY ranks of 7, decoder constant 63), same frozen hosting selections, same pipelines. + +| | PR #555 | this word | change | +|---|---|---|---| +| bit c, nine stages (PR #510's chain, 32 replicas, parameterised by `code/adapt9p.py`) | 0.001733052823330162099157 | **0.001733427039589844234749** (`1733427039589844234749/10^24`) | +0.0216% | +| bit c, eleven stages (PR #493's chain as parameterised by PR #545's `adapt.py`) | 0.001710608806098012673516 | **0.001710951900777583975622** (`855475950388791987811/(5·10^23)`) | +0.0201% | +| κ with #548's complex b (0.001593519419282307) | 0.001590984155135328 | 0.001590984155135328 | none | + +κ is complex-bound for every cited complex supplier (#548, #544, #535, #531), exactly as with #555's word, so this changes +no κ. The gain is bit headroom only: c is above every cited complex b (the largest, #548's, is 0.0015935), so the bit side +does not bind. The local paid profile goes from `{1:1321, 2:1342, 3:80, 4:47, 5:102, 6:10, 7:18}` to +`{1:1323, 2:1342, 3:79, 4:44, 5:102, 6:11, 7:19}` (rank mass 5,129 both; 27,234 records, 24,305 common-frame ADDs, payload +93 bits, 636 frames, all unchanged); the nine-stage global profile is `{1:381024, 2:400416, 3:22752, 4:26880, 5:29376, +6:17376, 7:26784, 42:288}`. + +## What changed (`moves.json`, applied by `apply.py`) + +Both moves take one summand out of a mini-node gate of phase A (the nine phase-A gates at the four D4 block frames 506-509, rank 4, +that carry a pseudo-target's B-part; gate and register numbers are those of the base word, helper h is register 222 + h): + +- **hoist.** A[1993] is `helper 222 += {x17, h49, h71, h130, h191, h246, h248, h249}` at frame 509 (rank 4). The summand + `helper 222 += helper 248` (register 444 += 470) becomes its own gate at rank-1 frame 79, placed immediately before that gate. +- **sink.** A[1994] is `helper 191 += {x17, h49, h71, h130, h149, h247, h249}` at frame 509. The summand `helper 191 += helper + 247` (register 413 += 469) becomes its own gate at rank-7 frame 517, the frame of the gate `helper 257 += helper 191 + helper + 74` (helper 257 is a retained total), placed immediately after base gate 2028. + +Each move factors a multi-source addition, moves one summand across additions that commute with it and gives it another +frame from the existing catalogue; the dirty-register endpoint is unchanged on every column. C is rebuilt as the literal +reverse of the z-destination gates of A and B at the full frame (A has 2,075 gates, C 2,574). The profile changes by +`+2 e1 − e3 − 3 e4 + e6 + e7`, a zero rank-mass change. The independent checker (`xcheck_p.py`, with the interface lemma +H1-H5 and its word-level mutation controls) and the official cohort checkers accept the word, and the hosting programmes +rediscover the frozen selections. + +## Where the moves come from, and why #541/#542/#548's move lists are not reused + +The mechanisms are those of three open PRs on PR #510's R480 word: Dugongue's #541 (factor a multi-source helper addition, move +one summand across its commuting prefix and give it the source's rank-1 frame: the *hoist* is an instance of the same mechanism, applied to a helper summand already at a rank-1 frame rather than a source), Dugongue's #548 +(observable pruning, frame-sensitive transports, Steinberg three-to-two cancellations: the *sink* is a transport of that kind) and +rohanarun's #542 (observable slice and least-frame assignment, on the same word). Their published moves are lists of R480 gate +numbers: the 60 two-x-source helper additions, the 227 zero-response gates of R480's dead totals, 20 transports and 7 +cancellations by id. PR #555's word comes from the R420 L3 skeleton: it has no two-x-source additions and no zero-response +gates, and its gate numbering is different, so the lists cannot be applied. `tune.py` therefore re-runs the mechanisms as +searches on the word's elementary-operation form (one source per gate): single-operation transport with reframing under +exact commutation and frame nesting, Steinberg 3→2 with frame re-choice, frame reassignment, and the pruning and least-frame +steps. Run on the R480 word, the same code reproduces #541's local profile `{1:1260, 2:1320, 3:60, 4:180, 6:120, 8:9}` and +#548's `{1:1257, 2:1330, 3:43, 4:163, 6:137, 8:9}` exactly. Run on PR #555's word to a fixed point, it applies exactly the two +moves above; the pruning, Steinberg and least-frame steps have nothing to apply that lowers the moment. `apply.py --discover` +repeats that search inside `verify.sh` and asserts that it returns this word. The greedy search is not claimed optimal. + +## Files + +- `moves.json`, `apply.py`: the two moves, pinned to the base word's content hash, and their application with an exact replay. +- `bitE8mut-p111-tuned.json.gz`: the tuned word (rebuilt byte for byte by `verify.sh`). +- `tune.py`, `wlib.py`: the search engines (numpy) and the exact replay and paid-profile code. +- `fastc.py`: a quick exact pricer, `c = certify(288 × local paid profile + idle remainder, 64, 28416)`; the remainder is read off + the base word's hosted audit. `verify.sh` checks that it equals the pipeline's c; it is not a substitute for the pipeline. +- `verify.sh`, `summary.py`, `EXPECTED.json`: the checks and every number. + +## Checks + +```sh +python -m pip install -r research/e8-bit-p111/requirements.txt +bash research/e8-bit-p111/tune/verify.sh /absolute/new/output +``` + +Python 3.11+ with numpy, sympy and scipy, a C++17 compiler (`CXX`) and Boost headers (`BOOST_INCLUDE`). The script rebuilds the +base word from #555's frozen plan, applies the moves and compares both byte for byte, runs the independent replay, the interface lemma, +the word-level and record-level mutation controls, the official legality and formal-column checkers at five, nine and eleven stages, +the Gram witnesses, and both price pipelines on the tuned word, and asserts the nine-stage c exactly +(`1733427039589844234749/10^24`) against `EXPECTED.json`. PR #555's own `verify.sh` is unchanged and still passes: it checks the +untuned word and the controls (`MANIFEST.sha256` lists the files of this directory). + +## Credit and caveats + +PR #555 (eumemic) is the base: the punctured R439 word, its generator, checkers, pricing pipelines and package structure are +used unchanged, building on #543 (R420 skeleton), #545 (punctured unit, DreamingOfClouds) and #510 (nine-stage chain). The +mechanisms are from #541 and #548 (Dugongue) and #542 (rohanarun). The searches, `apply.py`, `verify.sh` and this note were +prepared with Claude Opus 5.5 (Anthropic), via Claude Code; Apache-2.0. + +All retained assumptions of #555 apply unchanged (mutable-source realization, whole-call hosting, the all-size compiler, +compressed windows, precision and tape contracts, the cited complex suppliers). The moves rewrite the phase-A writes of two +helpers inside mini-node gates; `xcheck_p.py` and the official checkers verify the result exactly. This is a conditional +finite construction, not a Lean proof, and it changes no κ. diff --git a/research/e8-bit-p111/tune/apply.py b/research/e8-bit-p111/tune/apply.py new file mode 100644 index 0000000000..65849f369b --- /dev/null +++ b/research/e8-bit-p111/tune/apply.py @@ -0,0 +1,64 @@ +"""Apply moves.json to the base word (research/e8-bit-p111/data/bitE8mut-p111.json.gz) and write the tuned word. +usage: apply.py BASE.json.gz MOVES.json OUT.json.gz [--discover] +Checks: the base word's canonical content hash; each moved summand and its gate and frame; the exact replay of the result +(frames nested, every F2 column restored, targets y + x) and its local paid profile. C is rebuilt as the literal reverse +of the z-destination gates of A then B at the full frame. --discover also runs the generic searches of tune.py on the +base word (transports, Steinberg 3 -> 2, frame reassignment, on A and B, to a fixed point) and asserts that they find +exactly this word (same flat operation list, same frames). +Prepared with Claude Opus 5.5 (Anthropic), via Claude Code; Apache-2.0.""" +import copy +import hashlib +import json +import sys +from pathlib import Path + +import wlib + +base_p, moves_p, out_p = sys.argv[1:4] +discover = "--discover" in sys.argv[4:] +mv = json.loads(Path(moves_p).read_text()) +base = wlib.load(base_p) +h = hashlib.sha256(wlib.canon(base)).hexdigest() +assert h == mv["base_content_sha256"], "base word differs from the pinned one" +before = wlib.paid(base) +A = base["A"] +rm, ins = {}, {} +for m in mv["moves"]: + f, d, ss = A[m["gate"]] + assert d == m["dst"] and m["src"] in ss and f == m["from_frame"] and len(ss) > 1, m["name"] + rm.setdefault(m["gate"], []).append(m["src"]) + ins.setdefault(m["before_gate"], []).append([m["to_frame"], m["dst"], [m["src"]]]) +newA = [] +for i, (f, d, ss) in enumerate(A): + newA += [copy.deepcopy(g) for g in ins.get(i, [])] + newA.append([f, d, [s for s in ss if s not in rm.get(i, [])]]) +assert not [k for k in ins if k >= len(A)] +w = copy.deepcopy(base) +w["A"] = newA +w["C"] = wlib.rebuild_C(w) +after = wlib.paid(w) # asserts nested frames and the exact F2 endpoint on all columns +assert sum(k * n for k, n in before.items()) == sum(k * n for k, n in after.items()) +sha = wlib.save(w, out_p) +print(" %d moves; local paid profile %s -> %s; word sha256 %s" % (len(mv["moves"]), before, after, sha)) +if discover: + import tune + Tn = tune.Tuner(base, a=0.001733) + total = 0 + for _ in range(4): + n = 0 + for ch in "TSF": + for sec in "AB": + V = Tn.view(sec) + ap = (tune.transport_sweep(V, verbose=False) if ch == "T" else + tune.steinberg_sweep(V, verbose=False) if ch == "S" else + tune.transport_sweep(V, verbose=False, allow_move=False)) + n += len(ap) + total += n + if not n: + break + found = Tn.word() + flat = lambda x, s: [(f, t, q) for f, t, qq in x[s] for q in qq] # noqa: E731 + assert total == len(mv["moves"]), total + assert flat(found, "A") == flat(w, "A") and flat(found, "B") == flat(w, "B") and found["frames"] == w["frames"] + assert found["start"] == w["start"] and found["final"] == w["final"] and found["D"] == w["D"] + print(" rediscovery: transports, Steinberg and frame passes on A and B find exactly these %d moves" % total) diff --git a/research/e8-bit-p111/tune/bitE8mut-p111-tuned.json.gz b/research/e8-bit-p111/tune/bitE8mut-p111-tuned.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..85a4499bf9e4911a705d50567a2c2ef6cf23739a GIT binary patch literal 42353 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 cb9 and c11 > cb11 +# kappa is that of PR #555: every cited complex b binds +for n in names: + assert r9["kappa"][n]["kappa"] == base["nine"]["kappa"][n]["kappa"] == r11["kappa"][n]["kappa"] + assert r9["kappa"][n]["binding_component"] == "complex" +# cross-check with the quick exact pricer (idle remainder read off the base word's audit) +sys.path.insert(0, str(TD)) +import fastc # noqa: E402 +import wlib # noqa: E402 +fc = fastc.fast_c(wlib.paid(raw)) +assert fc == r9["bit_lower_decimal"], (fc, r9["bit_lower_decimal"]) +p = lambda a, b: "%+.4f%%" % (100 * float(F(a) / F(b) - 1)) # noqa: E731 +print("PASS bit c, nine stages (PR #555's chain, frozen hosting rediscovered, all %d residuals hosted whole, %d banks) = %s = %s" + % (r9["bit"]["hosted_residuals"], r9["bit"]["bank_assignments"], r9["bit_lower"], r9["bit_lower_decimal"])) +print(" PR #555 untuned: %s; this word %s" % (base["nine"]["bit_lower_decimal"], p(c9, cb9))) +print("PASS bit c, eleven stages (PR #493's chain as parameterised by PR #545) = %s = %s" % (r11["bit_lower"], + r11["bit_lower_decimal"])) +print(" PR #555 untuned: %s; this word %s" % (base["eleven"]["bit_lower_decimal"], p(c11, cb11))) +for n in names: + kk = r9["kappa"][n] + print("PASS kappa with PR #%s's complex b = %s: %s = %s (%s-bound; the same with PR #555's word and with the eleven-stage c)" + % (n[2:], kk["complex_lower"], kk["kappa"], kk["kappa_decimal"], kk["binding_component"])) +print(" quick pricer (tune/fastc.py: 288 x local paid profile + idle remainder) gives the same nine-stage c: %s" % fc) +print(" local paid profile %s (PR #555: %s)" % (ew["local_histogram"], base["word"]["local_histogram"])) diff --git a/research/e8-bit-p111/tune/tune.py b/research/e8-bit-p111/tune/tune.py new file mode 100644 index 0000000000..6153f8c78b --- /dev/null +++ b/research/e8-bit-p111/tune/tune.py @@ -0,0 +1,392 @@ +"""Bit-side tuners for bitE8/1+mut words on the flat elementary-op form (one source per gate): single-op transport with +reframing (the mechanism of PR #541's preload60 and PR #548's trace20), Steinberg 3 -> 2 cancellation (PR #548), +frame-only reassignment, observable pruning (PR #542 / #548) and the least-frame assignment (PR #542). D, B's +scatter, start and final are never changed; C is rebuilt as the literal reverse of the z-destination gates of A then B. +Frames are never changed for a retained total's last A gate (the COPY rank, hence the decoder constant, is fixed). +Used by apply.py --discover to rediscover the moves of moves.json from the base word. +Prepared with Claude Opus 5.5 (Anthropic), via Claude Code; Apache-2.0.""" +import bisect,copy,math,sys,json +from collections import Counter +import numpy as np +import wlib + +PR=2147483647 + +def rank_mod(rows): + rows=[[x%PR for x in r] for r in rows] + rk=0;ncol=len(rows[0]) if rows else 0 + for c in range(ncol): + p=next((i for i in range(rk,len(rows)) if rows[i][c]),None) + if p is None: continue + rows[rk],rows[p]=rows[p],rows[rk] + inv=pow(rows[rk][c],PR-2,PR) + rows[rk]=[x*inv%PR for x in rows[rk]] + for i in range(len(rows)): + if i!=rk and rows[i][c]: + f=rows[i][c];rows[i]=[(a-f*b)%PR for a,b in zip(rows[i],rows[rk])] + rk+=1 + return rk,rows[:rk] + +class Cat: + """frame catalogue; frames are root-closed sets of live lines (python int masks)""" + def __init__(self,w): + self.w=w;self.lines=w['lines'];self.nl=len(self.lines) + self.masks=[sum(1<>64 for m in self.masks],dtype=np.uint64) + self.dimarr=np.array(self.dim,dtype=np.int64) + def closure(self,mask): + """id of the least catalogue frame containing mask, created if needed (None if mask empty -> zero frame)""" + if mask in self.clcache: return self.clcache[mask] + sel=[self.lines[i] for i in range(self.nl) if mask>>i&1] + rk,bas=rank_mod(sel) if sel else (0,[]) + cl=0 + for i in range(self.nl): + if mask>>i&1: cl|=1<>64) + hl=np.uint64(high&0xFFFFFFFFFFFFFFFF);hh=np.uint64(high>>64) + ok=((self.lo&lo)==lo)&((self.hi&hi)==hi)&((self.lo&~hl)==0)&((self.hi&~hh)==0) + return np.nonzero(ok)[0] + def le(self,a,b): return self.masks[a]&self.masks[b]==self.masks[a] + def export(self,w): + w['frames']=[[i for i in range(self.nl) if m>>i&1] for m in self.masks] + +def fee_table(a,top=9): + return np.array([0.0]+[k*math.expm1(a*math.log(64/k)) for k in range(1,top+8)]) + +class View: + """one section (A or B) of the tuner, seen as a flat elementary-op list with pre/post frame bounds per register""" + def __init__(self,T,sec): + self.T=T;self.sec=sec;self.cat=T.cat;self.fee=T.fee;self.N=T.N + self.ops=T.secs[sec] + self.ret=T.ret if sec=='A' else set() + self.cp=T.cp + self.refresh() + def reindex(self): + self.touch=[[] for _ in range(self.N)] + for i,(f,t,s) in enumerate(self.ops): + self.touch[t].append(i);self.touch[s].append(i) + def refresh(self): + T=self.T;N=self.N + A=T.secs['A'];B=T.secs['B'] + lastA=list(T.w['start']) + for f,t,s in A: lastA[t]=f;lastA[s]=f + firstB=list(T.w['final']);seen=set() + for f,t,s in B: + for r in (t,s): + if r not in seen: firstB[r]=f;seen.add(r) + if self.sec=='A': self.pre=list(T.w['start']);self.post=firstB + else: self.pre=lastA;self.post=list(T.w['final']) + self.reindex() + +class Tuner: + def __init__(self,w,a=0.001733): + self.w=copy.deepcopy(w);self.V=w['v'];self.N=2*self.V+w['R'] + self.cat=Cat(self.w);self.fee=fee_table(a) + self.ret=set(w['ret']) + self.secs={sec:[[f,t,s] for f,t,ss in w[sec] for s in ss] for sec in 'AB'} + self.cp={} + for r in self.ret: + lf=None + for f,t,s in self.secs['A']: + if r in (t,s): lf=f + self.cp[r]=lf if lf is not None else w['start'][r] + self.views={sec:View(self,sec) for sec in 'AB'} + def view(self,sec): + v=self.views[sec];v.refresh();return v + def word(self): + w=copy.deepcopy(self.w);self.cat.export(w) + for sec in 'AB': w[sec]=[[f,t,[s]] for f,t,s in self.secs[sec]] + w['C']=wlib.rebuild_C(w) + # keep the original catalogue ids; keep only those new closure frames that are used, renumbered after them + n0=len(self.w['frames']) + used=set(w['start'])|set(w['final']) + for sec in 'DABC': + for f,t,ss in w[sec]: used.add(f) + newused=sorted(f for f in used if f>=n0) + remap={f:n0+i for i,f in enumerate(newused)} + if any(k!=v for k,v in remap.items()): + w['start']=[remap.get(f,f) for f in w['start']];w['final']=[remap.get(f,f) for f in w['final']] + for sec in 'DABC': + for g in w[sec]: g[0]=remap.get(g[0],g[0]) + w['frames']=w['frames'][:n0]+[w['frames'][f] for f in newused] + return w + def paid(self): + w=self.word();return wlib.paid(w),w + +# ---------------------------------------------------------------- transports +def commute(a,b): + """elementary additions [f,t,s] a and b commute algebraically: dst(a)!=src(b) and dst(b)!=src(a)""" + return a[1]!=b[2] and b[1]!=a[2] + +def _slotcost(T,fr_idx,dt,pt,nt,ds,ps,ns,Fs): + fee=T.fee;dm=T.cat.dimarr + dF=dm[Fs] + c=fee[dF-pt]+fee[nt-dF]-fee[nt-pt]+fee[dF-ps]+fee[ns-dF]-fee[ns-ps] + return c + +def best_move(T,i,eps=1e-12,allow_move=True,thr=None,all_=False): + """best (delta, q, F) for the op at index i of T.ops, or None. q is the insertion index in the list with op i removed.""" + cat=T.cat;ops=T.ops;n=len(ops);op=ops[i];f0,t,s=op + lo=i + while allow_move and lo>0 and commute(op,ops[lo-1]): lo-=1 + hi=i + while allow_move and hi0 else T.pre[r] + nx=frame_of(lst[kk]) if kk=maxmoves: break + i=next(j for j,op in enumerate(T.ops) if id(op)==oid) + m=best_move(T,i,eps,allow_move) + if not m: continue + d,q,F=m + old=(T.ops[i][0],i) + apply_move(T,i,q,F);applied.append(dict(id=oid,delta=d,old_index=i,new_index=q,old_frame=old[0],new_frame=F)) + if verbose: print('sweep: nominated',len(nom),'applied',len(applied),'sum delta %.6f'%sum(a['delta'] for a in applied),flush=True) + return applied + +# ---------------------------------------------------------------- Steinberg 3 -> 2 +def chain_cost_list(T,r,ops): + cat=T.cat;cur=T.pre[r];c=0.0 + for f,t,s in ops: + if t==r or s==r: + assert cat.le(cur,f),(r,cur,f);c+=T.fee[cat.dim[f]-cat.dim[cur]];cur=f + assert cat.le(cur,T.post[r]);c+=T.fee[cat.dim[T.post[r]]-cat.dim[cur]] + return c + +def find_triples(T): + ops=T.ops + from collections import defaultdict + pair=defaultdict(list);bysrc=defaultdict(list);bydst=defaultdict(list) + for i,(f,t,s) in enumerate(ops): pair[(t,s)].append(i);bysrc[s].append(i);bydst[t].append(i) + out=[] + for pl,(fb,bt,bs) in enumerate(ops): + cands=[] + for pk in bysrc[bt]: + if pk>pl: cands.append((1,pk,(ops[pk][1],bs))) + for pk in bydst[bs]: + if pk>pl: cands.append((2,pk,(bt,ops[pk][2]))) + for case,pk,cp in cands: + for pq in pair.get(cp,[]): + if len({pl,pk,pq})<3: continue + if not all(commute(ops[pk],ops[j]) for j in range(pl+1,pk)): continue + if not all(commute(ops[pq],ops[j]) for j in range(min(pl,pq),max(pl,pq)+1) if j not in (pk,pl,pq)): continue + out.append((case,pl,pk,pq)) + return out + +def eval_steinberg(T,pl,pk,pq): + """best (delta,Fe,Fb,q) for replacing b=ops[pl], e=ops[pk], c=ops[pq] by [e',b'] at b's place""" + cat=T.cat;ops=T.ops;dm=cat.dim + b,e,c=ops[pl],ops[pk],ops[pq] + rest=[op for j,op in enumerate(ops) if j not in (pk,pl,pq)] + q=pl-(1 if pq0 else T.pre[r] + ctxn[r]=rest[idx[kk]][0] if kk=maxmoves: break + pos={id(op):j for j,op in enumerate(T.ops)} + if ib not in pos or ie not in pos or ic not in pos: continue + pl,pk,pq=pos[ib],pos[ie],pos[ic] + b,e,c=T.ops[pl],T.ops[pk],T.ops[pq] + # re-verify the structure (indices may have shifted) + ok=pl=-eps: continue + d,Fe,Fb,q=r + rest=[op for j,op in enumerate(T.ops) if j not in (pk,pl,pq)] + e2=[Fe,e[1],e[2]];b2=[Fb,b[1],b[2]] + T.ops[:]=rest[:q]+[e2,b2]+rest[q:];T.reindex() + applied.append(dict(delta=d,b=b,e=e,c=c,Fe=Fe,Fb=Fb)) + if verbose: print('steinberg: triples',len(tr),'nominated',len(nom),'applied',len(applied),'sum delta %.6f'%sum(a['delta'] for a in applied),flush=True) + return applied + +# ---------------------------------------------------------------- observable pruning +def prune_dead(Tn): + """delete A summands whose destination has zero response on the target outputs (exact backward propagation)""" + w=Tn.w;V=Tn.V;N=Tn.N + H=[0]*N + for i in range(V):H[V+i]=1< data/bitE8mut-p111.json.gz, +# byte for byte) and tune/apply.py (byte for byte against tune/bitE8mut-p111-tuned.json.gz; its --discover run also finds exactly +# these moves from the base word), replayed exactly by the independent checker, passed through the same official cohort +# checkers, and priced by the same two hub pipelines as PR #555 (PR #510's nine-stage chain as parameterised by code/adapt9p.py; +# PR #493's eleven-stage chain as parameterised by PR #545's adapt.py) with the same frozen hosting selections, then fed to the +# same six-level, 47-inequality outer assembly with the cited complex b. PR #555's own verify.sh is unchanged and still checks +# the untuned word and the controls. +# usage: bash research/e8-bit-p111/tune/verify.sh /fresh/output/dir +# needs python3 >= 3.11 with numpy, sympy and scipy (../requirements.txt), a C++17 compiler (CXX, default c++) and +# Boost headers (BOOST_INCLUDE, default /usr/include). +set -euo pipefail +HERE=$(cd "$(dirname "$0")" && pwd); PKG=$(cd "$HERE/.." && pwd); WD=$(mkdir -p "$1" && cd "$1" && pwd) +P=$(cd "$PKG/../e8-bit-carrier-skeleton" && pwd) +case "$WD/" in "$PKG"/*|"$P"/*) echo "output dir must be outside the packages"; exit 2;; esac +export PYTHONDONTWRITEBYTECODE=1 +PY="${PYTHON:-python3} -I -B" +PYS="${PYTHON:-python3} -E -s -B" # scripts that import siblings from their own directory +CXX=${CXX:-c++}; BI=${BOOST_INCLUDE:-/usr/include} +C=$PKG/code; D=$PKG/data; EXP=$HERE/EXPECTED.json; BASE=$D/bitE8mut-p111.json.gz; WORD=$HERE/bitE8mut-p111-tuned.json.gz +PC=$P/code; B=$PC/bit; CK=$B/checkers; HUB=$PC/hub; INC="-I$P/portable-include -I$CK -I$BI" +V510=$PKG/vendor/pr510/e8-r480-nine-stage; VK=$V510/vendor/e8-r715-eleven-stage-whole/code +A545=$PKG/vendor/pr545/code +ISO=$P/data/eleven/residual-isometries.json; BIN=$WD/bin; mkdir -p "$BIN" +V=$($PY -c "import json,gzip;print(json.load(gzip.open('$WORD','rt'))['v'])") +R=$($PY -c "import json,gzip;print(json.load(gzip.open('$WORD','rt'))['R'])") +T0=$SECONDS +step() { echo "== $1 [$((SECONDS - T0))s]"; } + +step "0. manifest, provenance (vendored files by blob id, main's files by sha256), cited receipts" +(cd "$PKG" && $PY -c " +import hashlib,os,sys +bad=0;listed=set() +for line in open('MANIFEST.sha256'): + h,p=line.rstrip('\n').split(' ',1);listed.add(p) + if hashlib.sha256(open(p,'rb').read()).hexdigest()!=h: print('MISMATCH',p);bad+=1 +files={os.path.relpath(os.path.join(r,f)) for r,_,fs in os.walk('.') for f in fs if '__pycache__' not in r}-{'MANIFEST.sha256'} +if files-listed: print('UNLISTED',sorted(files-listed));bad+=1 +sys.exit(1 if bad else 0)") && echo " all files match MANIFEST.sha256" +$PY $C/provenance.py $PKG +$PY $C/cited.py $PKG +$PY $C/receipt_kappa.py $PKG +mkdir -p $WD/root/data && ln -sfn $PC $WD/root/code && cp $D/complex-b.json $WD/root/data/ # assembly root + + +step "1. the base word rebuilt from PR #555's frozen plan; the tuned word from tune/moves.json (and rediscovered by the generic searches)" +$PY $C/gen_p3.py $D/plan-l3.json $D/punct.json $WD/base.json.gz | sed 's/^/ /' +cmp $WD/base.json.gz $BASE +$PYS $HERE/apply.py $BASE $HERE/moves.json $WD/tuned.json.gz --discover | sed 's/^/ /' +cmp $WD/tuned.json.gz $WORD +$PY -c " +import json,gzip,hashlib +w=json.load(gzip.open('$WORD','rt'));e=json.load(open('$EXP'))['word'] +h=hashlib.sha256(json.dumps(w,sort_keys=True,separators=(',',':')).encode()).hexdigest() +assert h==e['content_sha256'] and hashlib.sha256(open('$WORD','rb').read()).hexdigest()==e['file_sha256'] +assert (w['v'],w['R'])==(e['v'],e['R']) and w['punctured']==e['punctured'] and len(w['ret'])==9 +assert {k:len(w[k]) for k in 'DABC'}==e['gates'] +print(' rebuilt tuned word is byte-identical to the shipped word (content sha256',h[:16]+'...); v',w['v'],'R',w['R'],'gates D/A/B/C',e['gates'])" + +step "2. independent exact replay, totals, the interleaved interface lemma, mutation controls" +$PY $C/xcheck_p.py $WORD --json $WD/xcheck.json | sed 's/^/ /' +$PY $C/mutants.py $C/xcheck_p.py $WORD --json $WD/mutants.json | sed 's/^/ /' +$PY $C/mutants_p.py $C/xcheck_p.py $WORD --json $WD/mutants_p.json | sed 's/^/ /' + +step "3. hub checkers: PR #545's v <= 120 form of PR #370's checker refuses (source writes) and its relaxed copy passes; PR #493's interface.py refuses" +$PY $A545/adapt.py $P $WORD $WD/ad545 | sed 's/^/ /' +if $PY $WD/ad545/chk/checkword.py $WORD $PC/pricing > $WD/stock.out 2> $WD/stock.err; then echo "stock-form checker accepted"; exit 1; fi +grep -q "x register written" $WD/stock.err && echo " stock-form checker refuses, as expected: $(tail -1 $WD/stock.err)" +$PY $WD/ad545/chk/relax.py $WD/rchk > /dev/null +$PY $WD/rchk/checkword_mut.py $WORD $PC/pricing | tee $WD/checkword_mut.txt | sed 's/^/ /' +if $PY $B/independent/interface.py $WORD > $WD/iface493.out 2> $WD/iface493.err; then echo "PR #493 interface.py accepted"; exit 1; fi +grep -q "phase B writes a z register" $WD/iface493.err && echo " PR #493 interface.py refuses, as expected: its check (4) requires phase B to write only targets" + +step "4. official cohort checkers at (v, R): legality (NCOPY = 9), formal columns at five, nine and eleven stages, Gram witnesses" +$PY $B/export_records.py $WORD $WD/records | sed 's/^/ /' +$PY $B/regression/stage_cohort.py $WD/records $WD/records $WD/cin $WD/cout +$CXX -O2 -std=c++17 -DHDIM=8 -DNCOPY=9 $INC -o $BIN/legality $CK/legality_h.cpp & +$CXX -O2 -std=c++17 -DHDIM=8 $INC -o $BIN/legality_ncopy_h $CK/legality_h.cpp & +for vr in "$V $R"; do set -- $vr; $CXX -O2 -std=c++17 -DHDIM=8 -DNCOPY=9 -DVPORTS=$1 -DRHELP=$2 $INC -o $BIN/columns_h_$1_$2 $CK/columns_h.cpp & done +wait +for K in 2 4 5; do $CXX -O2 -std=c++17 -DHDIM=8 -DNCOPY=9 -DVPORTS=$V -DRHELP=$R -DKPAIRS=$K $INC -o $BIN/columns_k$K $CK/columns_k.cpp & done +wait +$BIN/legality $WD/cin $WD/cout $WD/cout/legality.json > /dev/null +$BIN/columns_h_${V}_${R} $WD/cout/COHORT249-RECORDS.bin $WD/cout/columns.json > /dev/null 2> $WD/cout/columns.log +for K in 2 4 5; do $BIN/columns_k$K $WD/cout/COHORT249-RECORDS.bin $WD/cout/columns_k$K.json > /dev/null 2> $WD/cout/columns_k$K.log; done +$PY -c " +import json +V,R=$V,$R +l=json.load(open('$WD/cout/legality.json'));c=json.load(open('$WD/cout/columns.json')) +k4=json.load(open('$WD/cout/columns_k4.json'));k5=json.load(open('$WD/cout/columns_k5.json'));k2=json.load(open('$WD/cout/columns_k2.json')) +assert l['status']=='PASS_INDEPENDENT_COHORT_LEGALITY_SOURCE_OWNERS_AND_LIFECYCLE' and l['copies']==l['erasures']==9 +assert l['rank_mass']==8*R+63+14*V +print(' legality PASS:',l['records'],'records,',l['common_frame_ADDs'],'common-frame ADDs, rank mass',l['rank_mass'],'= 8R + 63 + 14v; 9 COPY/ERASE') +assert c['status']=='PASS_NEW_COHORT249_ALL_FIVE_STAGE_FORMAL_COLUMNS_AND_PREFIX_BILL' and c['formal_columns_checked']==4*V+R +print(' five-stage formal columns PASS:',c['formal_columns_checked'],'physical columns over F2; prefix payload',c['payload_prefix_bits'],'bits < 104') +for k,S,nb in ((k4,9,18),(k5,11,24)): + assert k['status']=='PASS_NEW_COHORT249_ALL_BRIDGED_STAGE_FORMAL_COLUMNS_AND_PREFIX_BILL' + assert k['formal_columns_checked']==(S-1)*V+R and len(k['stages'])==S and k['bridge_additions']==nb*V + assert len(k['negative_controls'])==nb and all(x['rejected'] for x in k['negative_controls']) + assert dict(map(tuple,k['local_raw_H']))==dict(map(tuple,c['local_raw_H'])) + print(' %s-stage formal columns PASS (columns_k, KPAIRS = %d): %d columns, %d bridge additions, %d/%d omitted-bridge controls rejected, payload %d bits'%(['nine','eleven'][S==11],(S-1)//2,k['formal_columns_checked'],k['bridge_additions'],nb,nb,k['payload_prefix_bits'])) +for d in (c,k2): d.pop('seconds') +assert c==k2;print(' regression: columns_k at KPAIRS = 2 reproduces the five-stage receipt field for field')" +$PY $WD/ad545/mutate_records.py $WD/records $WD/cin $WD/cout $BIN/legality $BIN/columns_k5 $BIN/legality_ncopy_h | tail -1 | sed 's/^/ /' +$PY $C/mutrec_b.py $WORD $WD/records $WD/cin $WD/cout $BIN/legality $BIN/columns_h_${V}_${R} $BIN/columns_k4 $BIN/columns_k5 | tail -1 | sed 's/^/ /' +$PY $CK/gram_frames.py $WD/records --gram e8ham --h 8 --positive | sed 's/^/ /' + +step "5. geometry (live labels: PR #545's parameterised PR #386 / PR #445 files), nine-stage files parameterised" +mkdir -p $WD/nine $WD/bit +$PY $WD/ad545/nine_geometry.py --source $P --out $WD/nine --word $WORD > /dev/null +$PY $WD/ad545/eleven_geometry.py --source $P --out $WD/bit --word $WORD > /dev/null +$PYS $V510/code/adapt.py $VK $WD/adapted9 | sed 's/^/ /' +$PY $C/adapt9p.py $VK $V510/code $WD/adapted9/audit_parent_k.py $WD/a9p | sed 's/^/ /' +nine() { # word R local_check_dir out_dir frozen_selection suppliers chain(stock|param) + local w=$1 r=$2 lc=$3 o=$4 sel=$5 sup=$6 G AP EM AH AS + if [ "$7" = stock ]; then G=$VK/global_k.py; AP=$WD/adapted9/audit_parent_k.py; EM=$VK/emit_k.py; AH=$VK/audit_hosted_k.py; AS=$V510/code/assemble9.py + else G=$WD/a9p/global_k.py; AP=$WD/a9p/audit_parent_k.py; EM=$WD/a9p/emit_k.py; AH=$WD/a9p/audit_hosted_k.py; AS=$WD/a9p/assemble9.py; fi + mkdir -p $o/v9/nine; cp $o/GEOMETRY9.json $o/v9/nine/GEOMETRY.json; ln -sfn $lc/records $o/v9/records + $PYS $G --word $w --local-check $lc --out $o/v9/nine --isometries $ISO > $o/v9/global.log + $PYS $AP --local-check $lc --out $o/v9/nine --word $w > $o/v9/audit_parent.log + $PYS $EM $o/v9/nine $lc $o/v9/hosted --selection $o/selection9.json --key bit --discover > $o/v9/emit.log + if ! $PY -c " +import json,sys +a=json.load(open('$o/selection9.json'))['bit'];b=json.load(open('$sel'))['bit'] +sys.exit(0 if a['patterns']==b['patterns'] and a['stages']==b['stages'] and abs(a['proxy_saving']-b['proxy_saving'])<=1e-9*abs(b['proxy_saving']) else 1)"; then + echo " NOTE: the hosting programme returned another selection here; pricing the frozen one" + cp $sel $o/selection9.json; rm -rf $o/v9/hosted + $PYS $EM $o/v9/nine $lc $o/v9/hosted --selection $o/selection9.json --key bit > $o/v9/emit.log + else echo " the hosting programme rediscovers the frozen nine-stage selection ($(basename $sel)) exactly"; fi + $PYS $AH $o/v9 $o/v9/hosted $o/v9/hosted/AUDIT.json > $o/v9/audit_hosted.log + $PYS $AS $o/v9 $o/v9/hosted $o/v9/hosted/AUDIT.json $WD/root $o/RESULT9.json $sup + $PY -c " +import json +g=json.load(open('$o/v9/nine/GLOBAL.json'));ap=json.load(open('$o/v9/nine/AUDIT.json'));au=json.load(open('$o/v9/hosted/AUDIT.json')) +assert g['R']==$r and g['replicas']==32 and g['m']==64 and g['local_invocations']==9*32 +assert ap['status']=='PASS_INDEPENDENT_COMPRESSED_BIT_TEMPLATE_AUDIT' and au['status']=='PASS_INDEPENDENT_REPLICATED_HOSTING_AUDIT' +print(' template %s (v %d): %d unbanked scalar columns, %d omitted bridge shears rejected; parent audit: %d template mutations rejected'%(g['status'],g['v'],g['independent_unbanked_scalar_columns'],len(g['omitted_bridge_shears_rejected']),len(ap['mutations_rejected']))) +print(' hosted audit PASS: %d of %d helper residuals hosted whole, %d banks, %d renamed instructions, %d mutations rejected, deficit %d'%(au['hosted_residuals'],9*32*$r,au['bank_assignments'],au['emitted_events'],len(au['mutations_rejected']),au['deficit']))" +} + +step "6. nine-stage whole-residual hosting and price (PR #510's chain, parameterised: PR #463 template and audits, 32 replicas)" +cp $WD/nine/GEOMETRY.json $WD/GEOMETRY9.json +nine $WORD $R $WD $WD $D/selection9-p111.json pr548,pr544,pr535,pr531 param + +step "7. eleven-stage whole-residual hosting and price (PR #493's chain as parameterised by PR #545's adapt.py)" +$PY $WD/ad545/host_select.py $WORD $WD/hosting11.json > /dev/null +$PY -c " +import json +a=json.load(open('$WD/hosting11.json'))['bit'];b=json.load(open('$D/hosting11-p111.json'))['bit'] +same=a['patterns']==b['patterns'] and a['stages']==b['stages'] +print(' PR #442 hosting programme (PR #545 host_select.py)',('rediscovers the frozen eleven-stage selection exactly' if same else 'returns another selection here; the frozen one is priced'))" +$PYS $WD/ad545/emit.py --word $WORD --local-check $WD --out $WD/bit --isometries $ISO --hosting $D/hosting11-p111.json > $WD/bit/emit.log +$PYS $WD/ad545/audit.py $WD $WD/nine $WD/bit > $WD/bit/audit.log +$PY $WD/ad545/assemble.py $WD/bit $R $P $WD/RESULT11.json pr548,pr544,pr535,pr531 $WD/root/data/complex-b.json +$PY -c " +import json;g=json.load(open('$WD/bit/GLOBAL.json'));a=json.load(open('$WD/bit/AUDIT.json')) +print(' template: 88 calls,',g['hosted_residuals'],'whole helper residuals hosted on idle data families (no banks),',g['independent_unbanked_scalar_columns'],'unbanked scalar columns restored, deficit',g['deficit']) +print(' independent audit PASS:',a['emitted_events'],'renamed instructions,',len(a['mutations_rejected']),'template mutations rejected, physical prefix',a['physical_prefix_bits'],'bits')" + +step "8. summary against tune/EXPECTED.json, PR #555's EXPECTED.json and the cited receipts" +$PYS $HERE/summary.py $PKG $HERE $WD +echo "== done [$((SECONDS - T0))s]" diff --git a/research/e8-bit-p111/tune/wlib.py b/research/e8-bit-p111/tune/wlib.py new file mode 100644 index 0000000000..0e5f9a26f6 --- /dev/null +++ b/research/e8-bit-p111/tune/wlib.py @@ -0,0 +1,75 @@ +"""Exact replay (nested frames, F2 endpoint on every column) and local paid profile for bitE8/1+mut words, punctured or not. +Prepared with Claude Opus 5.5 (Anthropic), via Claude Code; Apache-2.0.""" +import json,gzip,hashlib,copy +from fractions import Fraction as Q +from collections import Counter + +def load(p): + return json.load(gzip.open(p,'rt')) + +def canon(w): + return json.dumps(w,sort_keys=True,separators=(',',':')).encode() + +def save(w,p): + data=gzip.compress(canon(w),compresslevel=9,mtime=0); data=data[:9]+bytes([255])+data[10:] + open(p,'wb').write(data); return hashlib.sha256(data).hexdigest() + +def basis(vecs): + B={} + for rr in vecs: + row=list(map(Q,rr)) + for c in sorted(B): + q=row[c];row=[a-q*b for a,b in zip(row,B[c])] + c=next((i for i,x in enumerate(row) if x),None) + if c is not None: + q=row[c];B[c]=[x/q for x in row] + return B + +class Frames: + def __init__(self,w): + self.w=w + self.lines=w['lines'] + self.fb=[basis(self.lines[i] for i in f) for f in w['frames']] + self.dim=[len(b) for b in self.fb] + self.mask=[sum(1<dim[a]: H[dim[f]-dim[a]]+=1 + cur[r]=f + def gates(sec): + for f,t,ss in w[sec]: + assert len({t,*ss})==1+len(ss) + for r in [t,*ss]: climb(r,f) + for s in ss: val[t]^=val[s] + gates('D');gates('A') + tops.extend(dim[cur[q]] for q in w['ret']);snap=[val[q] for q in w['ret']] + for t,ks in enumerate(w['scatter']): + for k in ks: val[V+t]^=snap[k] + gates('B');gates('C') + for r,f in enumerate(w['final']): climb(r,f) + if check: + for r in range(N): + assert val[r]==((1<=2*V] + return [[full,t,list(ss)] for f,t,ss in reversed(z)]