@ceccec/millennium-solutions 9.3.2 → 9.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -40
- package/dist/mod.core.d.ts +9 -0
- package/dist/mod.core.js +9 -0
- package/dist/src/0/index.d.ts +47 -0
- package/dist/src/0/index.js +189 -0
- package/dist/src/0/ledger.d.ts +1 -0
- package/dist/src/0/ledger.js +25 -0
- package/dist/src/0/trinity-matrix.d.ts +1 -0
- package/dist/src/0/trinity-matrix.js +16 -0
- package/dist/src/5/aspects.d.ts +1 -0
- package/dist/src/5/aspects.js +22 -0
- package/dist/src/5/reflection.d.ts +1 -0
- package/dist/src/5/reflection.js +25 -0
- package/dist/src/7/rosetta.d.ts +1 -0
- package/dist/src/7/rosetta.js +24 -0
- package/dist/src/9/enumeration.d.ts +1 -0
- package/dist/src/9/enumeration.js +14 -0
- package/dist/src/honesty/index.d.ts +16 -0
- package/dist/src/honesty/index.js +69 -0
- package/package.json +27 -10
- package/.zenodo.json +0 -51
- package/ABSTRACT.md +0 -155
- package/ACCOUNTING.md +0 -27
- package/AXIOMS.md +0 -574
- package/CHALLENGES.md +0 -2434
- package/DEPLOY.md +0 -39
- package/DEVELOP.md +0 -34
- package/HEART.md +0 -112
- package/PHYSICS-SCALES.md +0 -80
- package/PRIOR-ART.md +0 -151
- package/PROOF-OF-CONCEPT.md +0 -81
- package/REALISATIONS.md +0 -87
- package/RESEARCH.md +0 -188
- package/RULES.md +0 -57
- package/SEQUENCE-DECODE.md +0 -196
- package/SIGNATURE.md +0 -34
- package/THEOREMS.md +0 -97
- package/WHITEPAPER.md +0 -40
- package/WORKFLOWS.md +0 -47
- package/boundaries.md +0 -23
- package/captain.md +0 -85
- package/compare.md +0 -42
- package/compute.md +0 -543
- package/cryptography.md +0 -55
- package/dashboard.md +0 -50
- package/examples.md +0 -47
- package/folds.md +0 -65
- package/forensic.md +0 -67
- package/guide.md +0 -126
- package/harness.md +0 -20
- package/index.md +0 -217
- package/mod.core.ts +0 -10
- package/nature.md +0 -58
- package/organism.md +0 -30
- package/paper.md +0 -8100
- package/proofs.md +0 -26
- package/quantum.md +0 -40
- package/rights.md +0 -39
- package/solutions.md +0 -28
- package/speedup.md +0 -41
- package/src/0/chacha.ts +0 -3
- package/src/0/crypt.ts +0 -4
- package/src/0/csp.ts +0 -10
- package/src/0/entanglement.ts +0 -31
- package/src/0/fusion.ts +0 -28
- package/src/0/holographic.ts +0 -57
- package/src/0/impossible.ts +0 -30
- package/src/0/imprint.ts +0 -160
- package/src/0/index.ts +0 -263
- package/src/0/ledger.ts +0 -25
- package/src/0/legible.ts +0 -29
- package/src/0/limit.ts +0 -29
- package/src/0/merkle-proof.ts +0 -3
- package/src/0/null-token.ts +0 -24
- package/src/0/sha256.ts +0 -3
- package/src/0/singularity.ts +0 -20
- package/src/0/stream.ts +0 -47
- package/src/0/trinities.ts +0 -37
- package/src/0/trinity-matrix.ts +0 -20
- package/src/0/version-matrix.ts +0 -70
- package/src/1/acceptance.ts +0 -26
- package/src/1/index.ts +0 -80
- package/src/1/paradox.ts +0 -28
- package/src/1/vortex.lean +0 -6
- package/src/2/frequency-scales.ts +0 -28
- package/src/2/iching.ts +0 -34
- package/src/2/index.ts +0 -78
- package/src/2/primes-vortex.ts +0 -12
- package/src/2/quantum.ts +0 -48
- package/src/2/string-scale.ts +0 -14
- package/src/2/vortex.lean +0 -10
- package/src/3/index.ts +0 -63
- package/src/3/pi-bbp.ts +0 -13
- package/src/3/trinity.ts +0 -39
- package/src/3/vortex.lean +0 -7
- package/src/4/git.ts +0 -30
- package/src/4/index.ts +0 -63
- package/src/4/vortex.lean +0 -6
- package/src/5/aspects.ts +0 -27
- package/src/5/center.ts +0 -35
- package/src/5/development.ts +0 -45
- package/src/5/diamond.ts +0 -35
- package/src/5/folds.ts +0 -12
- package/src/5/index.ts +0 -63
- package/src/5/reflection.lean +0 -14
- package/src/5/reflection.ts +0 -14
- package/src/5/vortex.lean +0 -10
- package/src/6/dna.ts +0 -27
- package/src/6/index.ts +0 -63
- package/src/6/vortex.lean +0 -7
- package/src/7/cross.ts +0 -31
- package/src/7/glagolitic.ts +0 -51
- package/src/7/index.ts +0 -67
- package/src/7/language-lens.ts +0 -41
- package/src/7/lens.ts +0 -41
- package/src/7/locale.ts +0 -105
- package/src/7/rbac.ts +0 -31
- package/src/7/rosetta-clay.ts +0 -15
- package/src/7/rosetta.lean +0 -11
- package/src/7/rosetta.ts +0 -11
- package/src/7/string-dimensions.ts +0 -14
- package/src/7/vortex.lean +0 -6
- package/src/8/infinite.ts +0 -26
- package/src/8/merkaba/field.ts +0 -27
- package/src/8/merkaba/geometry.ts +0 -16
- package/src/8/merkaba/touch.ts +0 -26
- package/src/8/nucleus/magic.lean +0 -15
- package/src/8/nucleus/proton-mass-fit.ts +0 -9
- package/src/8/nucleus/proton-mass.lean +0 -10
- package/src/8/nucleus/shell-model-magic.ts +0 -10
- package/src/8/vortex.lean +0 -7
- package/src/9/access.ts +0 -34
- package/src/9/aura-funding.ts +0 -25
- package/src/9/coins.ts +0 -25
- package/src/9/enumeration.ts +0 -13
- package/src/9/funding.ts +0 -85
- package/src/9/security.ts +0 -36
- package/src/9/society.ts +0 -32
- package/src/9/vortex.lean +0 -11
- package/src/api/gates.ts +0 -122
- package/src/api/index.ts +0 -447
- package/src/api/lanes.ts +0 -81
- package/src/claims/index.ts +0 -81
- package/src/demand/queries.json +0 -1764
- package/src/face/index.ts +0 -100
- package/src/from/the/heart/index.ts +0 -11
- package/src/honesty/boundaries.ts +0 -29
- package/src/honesty/index.ts +0 -63
- package/src/html/index.ts +0 -10
- package/src/latex/index.ts +0 -474
- package/src/millennium/index.ts +0 -53
- package/src/play/the/game/index.ts +0 -12
- package/src/proof/README.md +0 -118
- package/src/proof/address.lean +0 -147
- package/src/proof/coin.lean +0 -152
- package/src/proof/covered.json +0 -9
- package/src/proof/demand.lean +0 -134
- package/src/proof/demand2.lean +0 -207
- package/src/proof/demand3.lean +0 -183
- package/src/proof/discovered.json +0 -19125
- package/src/proof/elementary.lean +0 -446
- package/src/proof/energy.lean +0 -291
- package/src/proof/families.lean +0 -648
- package/src/proof/fixtures/axiom-control.lean +0 -39
- package/src/proof/flow.lean +0 -54
- package/src/proof/fnv.lean +0 -116
- package/src/proof/generated-theorems.json +0 -207
- package/src/proof/generated.lean +0 -101
- package/src/proof/imagined.lean +0 -492
- package/src/proof/index.lean +0 -156
- package/src/proof/index.ts +0 -916
- package/src/proof/involution.lean +0 -90
- package/src/proof/ledgerclaims.lean +0 -84
- package/src/proof/light.lean +0 -158
- package/src/proof/mechanical.lean +0 -524
- package/src/proof/merkaba.lean +0 -89
- package/src/proof/merkle.lean +0 -132
- package/src/proof/nim.lean +0 -311
- package/src/proof/novelty.json +0 -751
- package/src/proof/phenomena.lean +0 -90
- package/src/proof/priorart.lean +0 -190
- package/src/proof/quantum.lean +0 -159
- package/src/proof/reach.lean +0 -105
- package/src/proof/recovered.lean +0 -56
- package/src/proof/reversal.lean +0 -148
- package/src/proof/revoked.json +0 -317
- package/src/proof/rights.lean +0 -155
- package/src/proof/sequences.lean +0 -219
- package/src/proof/speed.lean +0 -152
- package/src/proof/split.lean +0 -199
- package/src/proof/theorems.lean +0 -67
- package/src/proof/trial-all.json +0 -16046
- package/src/proof/z9.lean +0 -99
- package/src/proof/z9plus.lean +0 -300
- package/src/proofs.lisp +0 -170
- package/src/prove/emit.ts +0 -115
- package/src/prove/index.ts +0 -143
- package/src/prove/translate.ts +0 -638
- package/src/publication/index.ts +0 -362
- package/src/quantum/field.ts +0 -73
- package/src/quantum/tree.ts +0 -79
- package/src/receipts/1431b4c4-7950-8d7b-88d1-b633219692f8.json +0 -7
- package/src/receipts/171e09c0-4cfd-8ce0-a6ed-cd33d5553da3.json +0 -7
- package/src/receipts/1a1f16c9-3491-8050-b221-e2a362439be4.json +0 -12
- package/src/receipts/1da15f83-83a6-8d04-b1ac-334c2dd6abb3.json +0 -7
- package/src/receipts/375a57ed-ef99-855e-8f34-b77d08010f3c.json +0 -6
- package/src/receipts/3dcbbf9e-0e49-8ba7-921e-17e478dbafaf.json +0 -6
- package/src/receipts/43a24136-5ad9-81ef-97c9-17be1c05bb5f.json +0 -11
- package/src/receipts/4991be51-74ba-8f66-9534-e6dba52fb569.json +0 -10
- package/src/receipts/52375d8f-232b-8c01-9d82-a824ffa7b2b7.json +0 -6
- package/src/receipts/593b546a-36b4-8553-941a-0e03b7053a64.json +0 -7
- package/src/receipts/7f75c989-96c9-8a24-8d0d-7e690861ccb4.json +0 -6
- package/src/receipts/80f80b59-81d8-866d-9a14-f469729d3dc8.json +0 -6
- package/src/receipts/85ac8585-9dc1-8964-9848-ae0f49cc82fb.json +0 -7
- package/src/receipts/85db092e-f2c0-88e5-bf75-61c4a65d84e7.json +0 -7
- package/src/receipts/95bacad3-9e56-8741-b0f8-def2c6ecb050.json +0 -7
- package/src/receipts/a1d33966-7bbd-84ca-902b-49e315af60e0.json +0 -6
- package/src/receipts/b20ced61-24ad-8123-adee-56031ab734dd.json +0 -11
- package/src/receipts/ba0447cd-f489-841a-a70f-a39b18ec2946.json +0 -11
- package/src/receipts/bcddbe66-06cf-8f31-a261-98b836938947.json +0 -7
- package/src/receipts/c1bac0d8-d906-8196-a4f0-3d8270b2e944.json +0 -6
- package/src/receipts/ce851ea3-3cf8-8646-a5d6-c0c6281210fe.json +0 -10
- package/src/receipts/cf43a122-c880-8d86-9e25-6f22f92d6b0a.json +0 -7
- package/src/receipts/daad8cd4-d4db-8dca-9c97-9a838cbce528.json +0 -7
- package/src/receipts/dc8bdb82-599f-8020-9eb2-cb016b408ec1.json +0 -6
- package/src/receipts/df77c265-d1a8-8354-8a9e-769fa5c676e2.json +0 -6
- package/src/receipts/e96686da-a411-8838-a5c8-ee7472bce048.json +0 -10
- package/src/receipts/f073c46f-7b67-83d0-aca5-db11fac4b5ec.json +0 -7
- package/src/receipts/f946d99d-a1c2-89f7-9426-a367c5b04191.json +0 -6
- package/src/receipts/fc069e45-115d-8983-ad25-fb9f735da84b.json +0 -6
- package/src/the/abstract/index.ts +0 -21
- package/src/the/abundance/index.ts +0 -20
- package/src/the/apple/earth/index.ts +0 -23
- package/src/the/apple/earth/navigation/index.ts +0 -28
- package/src/the/apple/earth/truth/index.ts +0 -19
- package/src/the/apple/index.ts +0 -27
- package/src/the/cancer/index.ts +0 -26
- package/src/the/clown/index.ts +0 -25
- package/src/the/creation/index.ts +0 -16
- package/src/the/crystal/index.ts +0 -23
- package/src/the/domain/index.ts +0 -16
- package/src/the/game/index.ts +0 -11
- package/src/the/gold/compass/index.ts +0 -26
- package/src/the/gold/index.ts +0 -35
- package/src/the/heart/index.ts +0 -15
- package/src/the/index.ts +0 -23
- package/src/the/law/index.ts +0 -20
- package/src/the/path/index.ts +0 -19
- package/src/the/rosetta/index.ts +0 -33
- package/src/the/sequence/index.ts +0 -12
- package/src/the/solids/index.ts +0 -25
- package/src/the/state/index.ts +0 -20
- package/src/the/superposition/state/index.ts +0 -22
- package/src/the/surface/index.ts +0 -26
- package/src/the/theorem/index.ts +0 -14
- package/src/the/torus/index.ts +0 -23
- package/src/the/waves/index.ts +0 -23
- package/the.md +0 -20
- package/verify.md +0 -67
package/src/proof/merkle.lean
DELETED
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
import Address
|
|
2
|
-
set_option maxRecDepth 8000000
|
|
3
|
-
-- title: The fold
|
|
4
|
-
-- wing: the address
|
|
5
|
-
-- prior_art: named
|
|
6
|
-
-- prior_art_domain: hash trees and membership proofs
|
|
7
|
-
-- prior_art_note: the hash tree — Ralph Merkle, 1979 (thesis); CRYPTO 1987
|
|
8
|
-
-- The fold, ported to Lean — merge, merkleFold, and the order-independence the deposit calls its receipt.
|
|
9
|
-
-- Author: Tsvetan Rouschev · License: CC BY-NC-ND 4.0
|
|
10
|
-
--
|
|
11
|
-
-- merge(a,b) is toUuid of the two addresses joined by a colon, so the fold operates on the DASHED HEX
|
|
12
|
-
-- RENDERING, not on the raw bytes — the rendering is therefore part of the definition and is ported here too.
|
|
13
|
-
-- The fold sorts its leaves before pairing, and that sort is the whole reason the result does not depend on
|
|
14
|
-
-- the order the leaves arrive in. The deposit asserted that in prose and tested it in TypeScript; below it is
|
|
15
|
-
-- decided, over every permutation of the sets named.
|
|
16
|
-
--
|
|
17
|
-
-- No axioms, no Mathlib, no sorry. Address and Fnv are imported, not restated.
|
|
18
|
-
|
|
19
|
-
namespace Merkle
|
|
20
|
-
|
|
21
|
-
open Address Fnv
|
|
22
|
-
|
|
23
|
-
def hexDigit (n : Nat) : Nat := if n < 10 then 48 + n else 87 + n
|
|
24
|
-
def byteHex (b : Nat) : List Nat := [hexDigit (b / 16), hexDigit (b % 16)]
|
|
25
|
-
|
|
26
|
-
/-- a uuid as the deposit writes it: 8-4-4-4-12 hex with dashes, as character codes -/
|
|
27
|
-
def uuidChars (bs : List Nat) : List Nat :=
|
|
28
|
-
let h := bs.flatMap byteHex
|
|
29
|
-
(h.take 8) ++ [45] ++ (h.drop 8 |>.take 4) ++ [45] ++ (h.drop 12 |>.take 4) ++ [45]
|
|
30
|
-
++ (h.drop 16 |>.take 4) ++ [45] ++ (h.drop 20)
|
|
31
|
-
|
|
32
|
-
/-- merge a b = toUuid("a:b") — the colon is 58 -/
|
|
33
|
-
def merge (a b : List Nat) : List Nat := toUuidBytes (uuidChars a ++ [58] ++ uuidChars b)
|
|
34
|
-
|
|
35
|
-
/-- lexicographic order on byte lists, which is the order the sort imposes on the rendered addresses -/
|
|
36
|
-
def leB : List Nat → List Nat → Bool
|
|
37
|
-
| [], _ => true
|
|
38
|
-
| _, [] => false
|
|
39
|
-
| x :: xs, y :: ys => if x == y then leB xs ys else x < y
|
|
40
|
-
|
|
41
|
-
def insB (a : List Nat) : List (List Nat) → List (List Nat)
|
|
42
|
-
| [] => [a]
|
|
43
|
-
| b :: bs => if leB a b then a :: b :: bs else b :: insB a bs
|
|
44
|
-
def sortB : List (List Nat) → List (List Nat)
|
|
45
|
-
| [] => []
|
|
46
|
-
| a :: as => insB a (sortB as)
|
|
47
|
-
|
|
48
|
-
/-- one pairing pass: adjacent leaves merged, an odd one carried up unchanged -/
|
|
49
|
-
def pairUp : List (List Nat) → List (List Nat)
|
|
50
|
-
| [] => []
|
|
51
|
-
| [a] => [a]
|
|
52
|
-
| a :: b :: rest => merge a b :: pairUp rest
|
|
53
|
-
|
|
54
|
-
/-- the fold: sort, then pair until one remains. Fuel-bounded so the recursion is structural. -/
|
|
55
|
-
def foldF : Nat → List (List Nat) → List (List Nat)
|
|
56
|
-
| 0, l => l
|
|
57
|
-
| _, [a] => [a]
|
|
58
|
-
| Nat.succ f, l => foldF f (pairUp l)
|
|
59
|
-
|
|
60
|
-
def EMPTY_SEED : List Nat := [101, 109, 112, 116, 121, 45, 109, 105, 110, 100] -- "empty-mind"
|
|
61
|
-
|
|
62
|
-
def merkleFold (leaves : List (List Nat)) : List Nat :=
|
|
63
|
-
if leaves.isEmpty then toUuidBytes EMPTY_SEED
|
|
64
|
-
else (foldF (leaves.length + 1) (sortB leaves)).getD 0 []
|
|
65
|
-
|
|
66
|
-
def A : List Nat := toUuidBytes [97] -- address of "a"
|
|
67
|
-
def C : List Nat := toUuidBytes [99] -- address of "c"
|
|
68
|
-
def B : List Nat := toUuidBytes [98] -- address of "b"
|
|
69
|
-
|
|
70
|
-
-- ── AGREEMENT with the shipped implementation ──
|
|
71
|
-
theorem merge_agrees :
|
|
72
|
-
merge A B = [181, 59, 237, 190, 211, 88, 129, 103, 143, 231, 158, 123, 139, 178, 38, 2] := by decide
|
|
73
|
-
|
|
74
|
-
theorem empty_fold_agrees :
|
|
75
|
-
merkleFold [] = [147, 146, 154, 45, 72, 16, 138, 198, 159, 50, 78, 208, 125, 158, 1, 108] := by decide
|
|
76
|
-
|
|
77
|
-
theorem singleton_fold_is_the_leaf : merkleFold [A] = A := by decide
|
|
78
|
-
|
|
79
|
-
-- ── and for more than one leaf, because "the fold of a single leaf is that leaf" is a claim about EVERY
|
|
80
|
-
-- leaf and the line above decides it at one. A single instance reads as the general statement when the
|
|
81
|
-
-- general statement is what the name says. ──
|
|
82
|
-
theorem the_fold_of_any_single_leaf_is_that_leaf :
|
|
83
|
-
[A, B, C].all (fun x => merkleFold [x] == x) := by decide
|
|
84
|
-
|
|
85
|
-
theorem pair_fold_agrees :
|
|
86
|
-
merkleFold [A, B] = [181, 59, 237, 190, 211, 88, 129, 103, 143, 231, 158, 123, 139, 178, 38, 2] := by decide
|
|
87
|
-
|
|
88
|
-
-- ── THE ORDER-INDEPENDENCE: the receipt does not depend on the order the leaves arrive in ──
|
|
89
|
-
theorem fold_is_order_independent_on_two :
|
|
90
|
-
merkleFold [A, B] = merkleFold [B, A] := by decide
|
|
91
|
-
|
|
92
|
-
-- ── and it is not vacuous: merge itself IS order-sensitive; the sort is what removes the dependence ──
|
|
93
|
-
theorem merge_is_order_sensitive : merge A B ≠ merge B A := by decide
|
|
94
|
-
|
|
95
|
-
theorem sorting_is_what_makes_the_fold_order_free :
|
|
96
|
-
sortB [A, B] = sortB [B, A] ∧ [A, B] ≠ [B, A] := by decide
|
|
97
|
-
|
|
98
|
-
def settledHere : Nat := 11
|
|
99
|
-
theorem merkle_settles_its_range : settledHere = 11 := rfl
|
|
100
|
-
|
|
101
|
-
-- ── ORDER-INDEPENDENCE ON AN ODD NUMBER OF LEAVES. Two leaves pair exactly and prove little: the interesting
|
|
102
|
-
-- case is an odd count, where pairUp must carry the leftover leaf into the next round. All six orderings of
|
|
103
|
-
-- three addresses are checked, so the carry cannot be order-sensitive in a way two leaves would hide.
|
|
104
|
-
-- ── AND ON FOUR, over every permutation rather than a hand-listed few ────────────────────────────────────
|
|
105
|
-
-- Three leaves were written out as five equations because six orderings fit on a page. Four do not:
|
|
106
|
-
-- twenty-four orderings written by hand is where a missing case hides. `List.permutations` generates
|
|
107
|
-
-- them, so the statement quantifies over ALL of them and cannot be short by one.
|
|
108
|
-
-- `List.permutations` is not in Lean's core — it lives in Batteries, which this deposit does not import,
|
|
109
|
-
-- for the same reason it does not import Mathlib. So the generator is written here, structurally.
|
|
110
|
-
def interleave (x : List Nat) : List (List Nat) → List (List (List Nat))
|
|
111
|
-
| [] => [[x]]
|
|
112
|
-
| y :: ys => (x :: y :: ys) :: (interleave x ys).map (fun l => y :: l)
|
|
113
|
-
def perms : List (List Nat) → List (List (List Nat))
|
|
114
|
-
| [] => [[]]
|
|
115
|
-
| x :: xs => (perms xs).flatMap (fun p => interleave x p)
|
|
116
|
-
def D : List Nat := toUuidBytes [100] -- address of "d"
|
|
117
|
-
|
|
118
|
-
-- the generator is checked before it is trusted: four leaves have 4! = 24 orderings, and if `perms` were
|
|
119
|
-
-- short the theorem below would quantify over fewer cases while looking exactly as strong
|
|
120
|
-
theorem the_permutation_generator_is_complete :
|
|
121
|
-
(perms [A, B, C, D]).length = 24 ∧ (perms [A, B, C]).length = 6 := by decide
|
|
122
|
-
|
|
123
|
-
set_option maxRecDepth 100000 in
|
|
124
|
-
theorem fold_is_order_independent_on_four :
|
|
125
|
-
(perms [A, B, C, D]).all (fun p => merkleFold p == merkleFold [A, B, C, D]) := by decide
|
|
126
|
-
|
|
127
|
-
theorem fold_is_order_independent_on_three :
|
|
128
|
-
merkleFold [A, B, C] = merkleFold [A, C, B] ∧ merkleFold [A, B, C] = merkleFold [B, A, C] ∧
|
|
129
|
-
merkleFold [A, B, C] = merkleFold [B, C, A] ∧ merkleFold [A, B, C] = merkleFold [C, A, B] ∧
|
|
130
|
-
merkleFold [A, B, C] = merkleFold [C, B, A] := by decide
|
|
131
|
-
|
|
132
|
-
end Merkle
|
package/src/proof/nim.lean
DELETED
|
@@ -1,311 +0,0 @@
|
|
|
1
|
-
import Fnv
|
|
2
|
-
set_option maxRecDepth 2000000
|
|
3
|
-
set_option maxHeartbeats 2000000
|
|
4
|
-
-- title: Nim
|
|
5
|
-
-- wing: the machine
|
|
6
|
-
-- prior_art: named
|
|
7
|
-
-- prior_art_domain: combinatorial game theory
|
|
8
|
-
-- prior_art_note: Nim — Charles L. Bouton, 1901; the Sprague–Grundy theorem — Roland Sprague, 1935 and Patrick M. Grundy, 1939
|
|
9
|
-
-- Nim — Bouton's theorem and Sprague–Grundy, decided.
|
|
10
|
-
-- Author: Tsvetan Rouschev · License: CC BY-NC-ND 4.0
|
|
11
|
-
--
|
|
12
|
-
-- The ledger asserted these in TypeScript: that a Nim position is lost for the mover exactly when the XOR of
|
|
13
|
-
-- the heaps is zero, and that a single heap's Grundy value is its size. Both are real theorems with real
|
|
14
|
-
-- proofs; what is done here is to DECIDE them over a named finite board — every position with heaps up to a
|
|
15
|
-
-- bound — which is what `decide` can honestly deliver. It is not a proof for all heap sizes, and the range is
|
|
16
|
-
-- stated in each name rather than implied.
|
|
17
|
-
--
|
|
18
|
-
-- XOR is imported from Fnv rather than restated: it is the same fuel-bounded structural fold, built because
|
|
19
|
-
-- Nat's bitwise operations are well-founded and would drag `propext` into every theorem here. Its laws are
|
|
20
|
-
-- proved below for every a, b, c, on the standard axioms; the decided rows above stay as their instances.
|
|
21
|
-
|
|
22
|
-
namespace Nim
|
|
23
|
-
|
|
24
|
-
open Fnv (xorF)
|
|
25
|
-
|
|
26
|
-
-- the fuel is a + b + 1, which always exceeds what the fold needs (each step halves both arguments and the fold
|
|
27
|
-
-- stops at the first zero); the earlier fuel of 33 read XOR correctly only below 2³³ — a cap, now gone
|
|
28
|
-
def xorN (a b : Nat) : Nat := xorF (a + b + 1) a b
|
|
29
|
-
-- The board is SMALL and stated, because the game tree is exponential: deciding `lost` explores every play
|
|
30
|
-
-- from every position, and at 8x8 the kernel exceeds its heartbeat budget. Six is what `decide` settles here
|
|
31
|
-
-- in reasonable time. A larger board is not a harder theorem, it is a longer computation — and claiming the
|
|
32
|
-
-- general case would need an induction, not a bigger range.
|
|
33
|
-
def N : Nat := 6
|
|
34
|
-
|
|
35
|
-
/-- the moves from a two-heap position: take any positive amount from either heap -/
|
|
36
|
-
def moves (a b : Nat) : List (Nat × Nat) :=
|
|
37
|
-
((List.range a).map (fun x => (x, b))) ++ ((List.range b).map (fun y => (a, y)))
|
|
38
|
-
|
|
39
|
-
/-- a position is LOST for the mover when every move leads to a position that is won for the opponent.
|
|
40
|
-
Fuel-bounded: the total of the heaps strictly decreases with each move. -/
|
|
41
|
-
def lost : Nat → Nat → Nat → Bool
|
|
42
|
-
| 0, _, _ => true
|
|
43
|
-
| Nat.succ f, a, b => (moves a b).all (fun p => ! lost f p.1 p.2)
|
|
44
|
-
|
|
45
|
-
def isLost (a b : Nat) : Bool := lost (a + b + 1) a b
|
|
46
|
-
|
|
47
|
-
-- ── BOUTON: a two-heap position is lost for the mover exactly when the heaps are equal — which is exactly
|
|
48
|
-
-- when their XOR is zero. Decided over every position on the N×N board, N = 6. ──
|
|
49
|
-
theorem bouton_two_heaps_lost_iff_xor_zero :
|
|
50
|
-
(List.range N).all (fun a => (List.range N).all (fun b =>
|
|
51
|
-
isLost a b == (xorN a b == 0))) := by decide
|
|
52
|
-
|
|
53
|
-
-- ── the same statement in its familiar form: lost exactly when the heaps are equal ──
|
|
54
|
-
theorem bouton_lost_iff_heaps_equal :
|
|
55
|
-
(List.range N).all (fun a => (List.range N).all (fun b => isLost a b == (a == b))) := by decide
|
|
56
|
-
|
|
57
|
-
-- ── NON-VACUITY: the losing positions are exactly the diagonal, so there are N of them among N² — neither
|
|
58
|
-
-- everything nor nothing. (An earlier version fixed this count at 8 for an 8x8 board; when the board was
|
|
59
|
-
-- reduced the kernel proved the statement FALSE rather than letting a stale constant pass. Tied to N now.) ──
|
|
60
|
-
theorem lost_positions_are_exactly_the_diagonal :
|
|
61
|
-
(((List.range N).flatMap (fun a => (List.range N).map (fun b => (a, b)))).filter
|
|
62
|
-
(fun p => isLost p.1 p.2)).length = N := by decide
|
|
63
|
-
|
|
64
|
-
-- ── SPRAGUE–GRUNDY for one heap: the Grundy value of a heap of size n is n ──
|
|
65
|
-
def mex (s : List Nat) : Nat := ((List.range (s.length + 1)).filter (fun m => ! s.contains m)).headD 0
|
|
66
|
-
def grundy1 : Nat → Nat → Nat
|
|
67
|
-
| 0, _ => 0
|
|
68
|
-
| Nat.succ f, n => mex ((List.range n).map (fun k => grundy1 f k))
|
|
69
|
-
|
|
70
|
-
theorem grundy_of_a_single_heap_is_its_size :
|
|
71
|
-
(List.range N).all (fun n => grundy1 (n + 1) n == n) := by decide
|
|
72
|
-
|
|
73
|
-
-- ── and the two-heap Grundy value is the XOR of the parts ──
|
|
74
|
-
theorem grundy_of_two_heaps_is_the_xor :
|
|
75
|
-
(List.range N).all (fun a => (List.range N).all (fun b =>
|
|
76
|
-
(xorN (grundy1 (a + 1) a) (grundy1 (b + 1) b)) == xorN a b)) := by decide
|
|
77
|
-
|
|
78
|
-
-- ── XOR's algebra, which is why the theorem takes the form it does ──
|
|
79
|
-
theorem xor_is_its_own_inverse :
|
|
80
|
-
(List.range 16).all (fun a => (List.range 16).all (fun b => xorN (xorN a b) b == a)) := by decide
|
|
81
|
-
theorem xor_is_commutative :
|
|
82
|
-
(List.range 16).all (fun a => (List.range 16).all (fun b => xorN a b == xorN b a)) := by decide
|
|
83
|
-
theorem xor_zero_is_identity : (List.range 32).all (fun a => xorN a 0 == a) := by decide
|
|
84
|
-
|
|
85
|
-
-- ── THE NIM-SUM AS GF(2) VECTOR ADDITION, IN ONE PROPOSITION ─────────────────────────────────────────────
|
|
86
|
-
-- `nim_sum_is_xor_gf2` claims the nim-sum IS GF(2) vector addition: commutative, associative and
|
|
87
|
-
-- self-inverse. Associativity was the one property missing here — and the other three, though each
|
|
88
|
-
-- decided above, are decided SEPARATELY. A claim is carried by a theorem that decides all of it or by
|
|
89
|
-
-- none, and the ledger's supersededBy names exactly one key, so three quarters of a claim spread over
|
|
90
|
-
-- three theorems carries nothing. The conjunction is the claim; the conjuncts standing alone are its
|
|
91
|
-
-- parts, which is why both forms are here rather than one. Range 16 because the cube of it is walked.
|
|
92
|
-
theorem the_nim_sum_is_a_gf2_vector_addition :
|
|
93
|
-
(List.range 16).all (fun a => (List.range 16).all (fun b => xorN a b == xorN b a)) ∧
|
|
94
|
-
(List.range 16).all (fun a => (List.range 16).all (fun b => (List.range 16).all (fun c =>
|
|
95
|
-
xorN (xorN a b) c == xorN a (xorN b c)))) ∧
|
|
96
|
-
(List.range 16).all (fun a => (List.range 16).all (fun b => xorN (xorN a b) b == a)) ∧
|
|
97
|
-
(List.range 16).all (fun a => xorN a 0 == a) := by decide
|
|
98
|
-
|
|
99
|
-
-- ── BOUTON WITH SEVEN HEAP SIZES, because "heaps ≤ 6" means seven of them and N = 6 gives six ─────────────
|
|
100
|
-
-- The ledger's row says "all heaps ≤ 6, exhaustive". `bouton_two_heaps_lost_iff_xor_zero` ranges over
|
|
101
|
-
-- List.range N with N = 6, which is 0…5 — one short. Carrying the row on it would have claimed a
|
|
102
|
-
-- position nobody decided, over an off-by-one nobody would ever see. The board is widened here instead,
|
|
103
|
-
-- in a theorem of its own, because N is baked into the statements already sealed above.
|
|
104
|
-
set_option maxRecDepth 4000000 in
|
|
105
|
-
theorem bouton_holds_at_every_heap_size_to_six :
|
|
106
|
-
(List.range 7).all (fun a => (List.range 7).all (fun b => isLost a b == (xorN a b == 0))) := by decide
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
-- ── XOR's algebra for every a, b, c — no bound ───────────────────────────────────────────────────────────────
|
|
110
|
-
-- xor_is_its_own_inverse and xor_is_commutative (a, b < 16), xor_zero_is_identity (a < 32) and
|
|
111
|
-
-- the_nim_sum_is_a_gf2_vector_addition (a, b, c < 16) were decided over a range; these prove them for every value.
|
|
112
|
-
-- The fold's fuel is a + b + 1, which always suffices: once it exceeds either argument, more fuel changes nothing,
|
|
113
|
-
-- so xorN reads the same at any sufficient fuel. Everything follows from one step — the low bit of a XOR is the sum
|
|
114
|
-
-- of the low bits mod 2, and the rest is twice the XOR of the halves — by induction. Bouton's theorem itself is
|
|
115
|
-
-- still decided over the N×N board above; a proof of it for every heap is a game-tree induction not done here.
|
|
116
|
-
theorem the_xor_of_two_bits_is_their_sum_mod_two :
|
|
117
|
-
∀ x y : Nat, (if x % 2 == y % 2 then 0 else 1) = (x + y) % 2 := by
|
|
118
|
-
intro x y
|
|
119
|
-
split
|
|
120
|
-
· rename_i h
|
|
121
|
-
have := beq_iff_eq.mp h
|
|
122
|
-
omega
|
|
123
|
-
· rename_i h
|
|
124
|
-
have : x % 2 ≠ y % 2 := fun e => h (beq_iff_eq.mpr e)
|
|
125
|
-
omega
|
|
126
|
-
|
|
127
|
-
theorem xor_commutes_at_every_fuel : ∀ f a b : Nat, xorF f a b = xorF f b a := by
|
|
128
|
-
intro f
|
|
129
|
-
induction f with
|
|
130
|
-
| zero => intro a b; cases a <;> cases b <;> rfl
|
|
131
|
-
| succ f ih =>
|
|
132
|
-
intro a b
|
|
133
|
-
cases a with
|
|
134
|
-
| zero => cases b <;> rfl
|
|
135
|
-
| succ a =>
|
|
136
|
-
cases b with
|
|
137
|
-
| zero => rfl
|
|
138
|
-
| succ b =>
|
|
139
|
-
show (if (a + 1) % 2 == (b + 1) % 2 then 0 else 1) + 2 * xorF f ((a + 1) / 2) ((b + 1) / 2) =
|
|
140
|
-
(if (b + 1) % 2 == (a + 1) % 2 then 0 else 1) + 2 * xorF f ((b + 1) / 2) ((a + 1) / 2)
|
|
141
|
-
rw [ih ((a + 1) / 2), the_xor_of_two_bits_is_their_sum_mod_two, the_xor_of_two_bits_is_their_sum_mod_two]
|
|
142
|
-
omega
|
|
143
|
-
|
|
144
|
-
theorem one_more_step_of_fuel_changes_nothing_once_it_exceeds_an_argument :
|
|
145
|
-
∀ f a b : Nat, (a < f ∨ b < f) → xorF (f + 1) a b = xorF f a b := by
|
|
146
|
-
intro f
|
|
147
|
-
induction f with
|
|
148
|
-
| zero => intro a b h; rcases h with h | h <;> exact absurd h (Nat.not_lt_zero _)
|
|
149
|
-
| succ f ih =>
|
|
150
|
-
intro a b h
|
|
151
|
-
cases a with
|
|
152
|
-
| zero => cases b <;> rfl
|
|
153
|
-
| succ a =>
|
|
154
|
-
cases b with
|
|
155
|
-
| zero => rfl
|
|
156
|
-
| succ b =>
|
|
157
|
-
show (if (a + 1) % 2 == (b + 1) % 2 then 0 else 1) + 2 * xorF (f + 1) ((a + 1) / 2) ((b + 1) / 2) =
|
|
158
|
-
(if (a + 1) % 2 == (b + 1) % 2 then 0 else 1) + 2 * xorF f ((a + 1) / 2) ((b + 1) / 2)
|
|
159
|
-
have h' : (a + 1) / 2 < f ∨ (b + 1) / 2 < f := by
|
|
160
|
-
rcases h with h | h
|
|
161
|
-
· exact Or.inl (by omega)
|
|
162
|
-
· exact Or.inr (by omega)
|
|
163
|
-
rw [ih _ _ h']
|
|
164
|
-
|
|
165
|
-
theorem more_fuel_changes_nothing_once_it_exceeds_an_argument :
|
|
166
|
-
∀ k f a b : Nat, (a < f ∨ b < f) → xorF (f + k) a b = xorF f a b := by
|
|
167
|
-
intro k
|
|
168
|
-
induction k with
|
|
169
|
-
| zero => intro f a b _; rfl
|
|
170
|
-
| succ k ih =>
|
|
171
|
-
intro f a b h
|
|
172
|
-
have h' : a < f + k ∨ b < f + k := by
|
|
173
|
-
rcases h with h | h
|
|
174
|
-
· exact Or.inl (by omega)
|
|
175
|
-
· exact Or.inr (by omega)
|
|
176
|
-
rw [← Nat.add_assoc, one_more_step_of_fuel_changes_nothing_once_it_exceeds_an_argument _ _ _ h', ih f a b h]
|
|
177
|
-
|
|
178
|
-
theorem xor_reads_the_same_at_every_sufficient_fuel :
|
|
179
|
-
∀ g a b : Nat, (a < g ∨ b < g) → xorN a b = xorF g a b := by
|
|
180
|
-
intro g a b h
|
|
181
|
-
unfold xorN
|
|
182
|
-
have h1 : a < a + b + 1 ∨ b < a + b + 1 := Or.inl (by omega)
|
|
183
|
-
rw [← more_fuel_changes_nothing_once_it_exceeds_an_argument g (a + b + 1) a b h1,
|
|
184
|
-
← more_fuel_changes_nothing_once_it_exceeds_an_argument (a + b + 1) g a b h, Nat.add_comm g]
|
|
185
|
-
|
|
186
|
-
theorem a_number_xored_with_itself_is_zero_at_every_fuel : ∀ f b : Nat, xorF f b b = 0 := by
|
|
187
|
-
intro f
|
|
188
|
-
induction f with
|
|
189
|
-
| zero => intro b; cases b <;> rfl
|
|
190
|
-
| succ f ih =>
|
|
191
|
-
intro b
|
|
192
|
-
cases b with
|
|
193
|
-
| zero => rfl
|
|
194
|
-
| succ b =>
|
|
195
|
-
show (if (b + 1) % 2 == (b + 1) % 2 then 0 else 1) + 2 * xorF f ((b + 1) / 2) ((b + 1) / 2) = 0
|
|
196
|
-
rw [ih, the_xor_of_two_bits_is_their_sum_mod_two]
|
|
197
|
-
omega
|
|
198
|
-
|
|
199
|
-
theorem xor_splits_off_its_lowest_bit :
|
|
200
|
-
∀ g x c : Nat, 1 ≤ g → xorF (g + 1) x c = (x + c) % 2 + 2 * xorF g (x / 2) (c / 2) := by
|
|
201
|
-
intro g x c hg
|
|
202
|
-
obtain ⟨g', rfl⟩ : ∃ g', g = g' + 1 := ⟨g - 1, by omega⟩
|
|
203
|
-
cases x with
|
|
204
|
-
| zero =>
|
|
205
|
-
rw [show xorF (g' + 1 + 1) 0 c = c by cases c <;> rfl, Nat.zero_div,
|
|
206
|
-
show xorF (g' + 1) 0 (c / 2) = c / 2 by cases (c / 2) <;> rfl]
|
|
207
|
-
omega
|
|
208
|
-
| succ x =>
|
|
209
|
-
cases c with
|
|
210
|
-
| zero =>
|
|
211
|
-
rw [show xorF (g' + 1 + 1) (x + 1) 0 = x + 1 from rfl, Nat.zero_div,
|
|
212
|
-
show xorF (g' + 1) ((x + 1) / 2) 0 = (x + 1) / 2 by cases ((x + 1) / 2) <;> rfl]
|
|
213
|
-
omega
|
|
214
|
-
| succ c =>
|
|
215
|
-
show (if (x + 1) % 2 == (c + 1) % 2 then 0 else 1) + 2 * xorF (g' + 1) ((x + 1) / 2) ((c + 1) / 2) = _
|
|
216
|
-
rw [the_xor_of_two_bits_is_their_sum_mod_two]
|
|
217
|
-
|
|
218
|
-
theorem xor_undoes_itself_when_the_fuel_suffices :
|
|
219
|
-
∀ f a b g : Nat, (a < f ∨ b < f) → b < g → xorF g (xorF f a b) b = a := by
|
|
220
|
-
intro f
|
|
221
|
-
induction f with
|
|
222
|
-
| zero => intro a b g h; rcases h with h | h <;> exact absurd h (Nat.not_lt_zero _)
|
|
223
|
-
| succ f ih =>
|
|
224
|
-
intro a b g h hg
|
|
225
|
-
cases a with
|
|
226
|
-
| zero =>
|
|
227
|
-
rw [show xorF (f + 1) 0 b = b by cases b <;> rfl]
|
|
228
|
-
exact a_number_xored_with_itself_is_zero_at_every_fuel g b
|
|
229
|
-
| succ a =>
|
|
230
|
-
cases b with
|
|
231
|
-
| zero =>
|
|
232
|
-
obtain ⟨g', rfl⟩ : ∃ g', g = g' + 1 := ⟨g - 1, by omega⟩
|
|
233
|
-
rfl
|
|
234
|
-
| succ b =>
|
|
235
|
-
obtain ⟨g', rfl⟩ : ∃ g', g = g' + 1 := ⟨g - 1, by omega⟩
|
|
236
|
-
have hf : (a + 1) / 2 < f ∨ (b + 1) / 2 < f := by
|
|
237
|
-
rcases h with h | h
|
|
238
|
-
· exact Or.inl (by omega)
|
|
239
|
-
· exact Or.inr (by omega)
|
|
240
|
-
have ih' := ih ((a + 1) / 2) ((b + 1) / 2) g' hf (by omega)
|
|
241
|
-
rw [xor_splits_off_its_lowest_bit g' _ _ (by omega)]
|
|
242
|
-
rw [show xorF (f + 1) (a + 1) (b + 1) = (if (a + 1) % 2 == (b + 1) % 2 then 0 else 1) +
|
|
243
|
-
2 * xorF f ((a + 1) / 2) ((b + 1) / 2) from rfl, the_xor_of_two_bits_is_their_sum_mod_two]
|
|
244
|
-
generalize xorF f ((a + 1) / 2) ((b + 1) / 2) = y at ih' ⊢
|
|
245
|
-
have e : ((a + 1 + (b + 1)) % 2 + 2 * y) / 2 = y := by omega
|
|
246
|
-
rw [e, ih']
|
|
247
|
-
omega
|
|
248
|
-
|
|
249
|
-
theorem xor_is_commutative_for_every_a_b : ∀ a b : Nat, xorN a b = xorN b a := by
|
|
250
|
-
intro a b
|
|
251
|
-
unfold xorN
|
|
252
|
-
rw [Nat.add_comm a b, xor_commutes_at_every_fuel]
|
|
253
|
-
|
|
254
|
-
theorem xor_zero_is_identity_for_every_a : ∀ a : Nat, xorN a 0 = a := by
|
|
255
|
-
intro a
|
|
256
|
-
unfold xorN
|
|
257
|
-
cases a <;> rfl
|
|
258
|
-
|
|
259
|
-
theorem xor_is_its_own_inverse_for_every_a_b : ∀ a b : Nat, xorN (xorN a b) b = a := by
|
|
260
|
-
intro a b
|
|
261
|
-
rw [xor_reads_the_same_at_every_sufficient_fuel (b + 1) (xorN a b) b (Or.inr (by omega))]
|
|
262
|
-
unfold xorN
|
|
263
|
-
exact xor_undoes_itself_when_the_fuel_suffices (a + b + 1) a b (b + 1) (Or.inl (by omega)) (by omega)
|
|
264
|
-
theorem xor_is_the_sum_of_the_bits_plus_twice_the_xor_of_the_halves :
|
|
265
|
-
∀ a b : Nat, xorN a b = (a + b) % 2 + 2 * xorN (a / 2) (b / 2) := by
|
|
266
|
-
intro a b
|
|
267
|
-
rw [xor_reads_the_same_at_every_sufficient_fuel (a + b + 1 + 1) a b (Or.inl (by omega)),
|
|
268
|
-
xor_splits_off_its_lowest_bit (a + b + 1) a b (by omega),
|
|
269
|
-
← xor_reads_the_same_at_every_sufficient_fuel (a + b + 1) (a / 2) (b / 2) (Or.inl (by omega))]
|
|
270
|
-
|
|
271
|
-
theorem xor_is_associative_below_every_bound :
|
|
272
|
-
∀ n a b c : Nat, a + b + c ≤ n → xorN (xorN a b) c = xorN a (xorN b c) := by
|
|
273
|
-
intro n
|
|
274
|
-
induction n with
|
|
275
|
-
| zero =>
|
|
276
|
-
intro a b c h
|
|
277
|
-
have ha : a = 0 := by omega
|
|
278
|
-
have hb : b = 0 := by omega
|
|
279
|
-
have hc : c = 0 := by omega
|
|
280
|
-
subst ha hb hc
|
|
281
|
-
rfl
|
|
282
|
-
| succ n ih =>
|
|
283
|
-
intro a b c h
|
|
284
|
-
if hz : a + b + c = 0 then
|
|
285
|
-
have ha : a = 0 := by omega
|
|
286
|
-
have hb : b = 0 := by omega
|
|
287
|
-
have hc : c = 0 := by omega
|
|
288
|
-
subst ha hb hc
|
|
289
|
-
rfl
|
|
290
|
-
else
|
|
291
|
-
have ihh := ih (a / 2) (b / 2) (c / 2) (by omega)
|
|
292
|
-
have hab := xor_is_the_sum_of_the_bits_plus_twice_the_xor_of_the_halves a b
|
|
293
|
-
have hbc := xor_is_the_sum_of_the_bits_plus_twice_the_xor_of_the_halves b c
|
|
294
|
-
have hx2 : xorN a b / 2 = xorN (a / 2) (b / 2) := by omega
|
|
295
|
-
have hy2 : xorN b c / 2 = xorN (b / 2) (c / 2) := by omega
|
|
296
|
-
rw [xor_is_the_sum_of_the_bits_plus_twice_the_xor_of_the_halves (xorN a b) c,
|
|
297
|
-
xor_is_the_sum_of_the_bits_plus_twice_the_xor_of_the_halves a (xorN b c), hx2, hy2, ihh]
|
|
298
|
-
omega
|
|
299
|
-
|
|
300
|
-
theorem xor_is_associative_for_every_a_b_c : ∀ a b c : Nat, xorN (xorN a b) c = xorN a (xorN b c) := by
|
|
301
|
-
intro a b c
|
|
302
|
-
exact xor_is_associative_below_every_bound (a + b + c) a b c (Nat.le_refl _)
|
|
303
|
-
|
|
304
|
-
theorem the_nim_sum_is_a_gf2_vector_addition_for_every_a_b_c :
|
|
305
|
-
∀ a b c : Nat, xorN a b = xorN b a ∧ xorN (xorN a b) c = xorN a (xorN b c) ∧
|
|
306
|
-
xorN (xorN a b) b = a ∧ xorN a 0 = a := by
|
|
307
|
-
intro a b c
|
|
308
|
-
exact ⟨xor_is_commutative_for_every_a_b a b, xor_is_associative_for_every_a_b_c a b c,
|
|
309
|
-
xor_is_its_own_inverse_for_every_a_b a b, xor_zero_is_identity_for_every_a a⟩
|
|
310
|
-
|
|
311
|
-
end Nim
|