zeropoint-node 1.0.5 → 1.0.7
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/CHANGELOG.md +178 -0
- package/CITATION.cff +1 -1
- package/README.md +50 -10
- package/dist/legacy-BEa7tljT.js.map +1 -1
- package/dist/legacy-CV8oTnKM.cjs.map +1 -1
- package/package.json +10 -2
- package/src/0/3/6/9/1/2/4/8/7/5/1/a432.breathe.ts +15 -1
- package/src/0/3/6/9/1/2/4/8/7/5/1/a432.cleanup.ts +15 -1
- package/src/0/3/6/9/1/2/4/8/7/5/1/a432.cmyk.ts +7 -1
- package/src/0/3/6/9/1/2/4/8/7/5/1/a432.generate.matrix.ts +15 -1
- package/src/0/3/6/9/1/2/4/8/7/5/1/a432.i.browse.ts +15 -1
- package/src/0/3/6/9/1/2/4/8/7/5/1/a432.i.click.ts +15 -1
- package/src/0/3/6/9/1/2/4/8/7/5/1/a432.i.hear.ts +15 -1
- package/src/0/3/6/9/1/2/4/8/7/5/1/a432.i.heat.ts +15 -1
- package/src/0/3/6/9/1/2/4/8/7/5/1/a432.i.pulse.ts +15 -1
- package/src/0/3/6/9/1/2/4/8/7/5/1/a432.i.speak.ts +15 -1
- package/src/0/3/6/9/1/2/4/8/7/5/1/a432.i.tap.ts +15 -1
- package/src/0/3/6/9/1/2/4/8/7/5/1/a432.math.ts +14 -6
- package/src/0/3/6/9/1/2/4/8/7/5/1/a432.overlay.visual.controller.ts +0 -3
- package/src/0/3/6/9/1/2/4/8/7/5/1/a432.rodin.ts +15 -1
- package/src/0/3/6/9/1/2/4/8/7/5/1/a432.self.ts +15 -1
- package/src/0/3/6/9/1/2/4/8/7/5/1/a432.service.worker.ts +30 -7
- package/src/0/3/6/9/1/2/4/8/7/5/1/a432.terminal.ts +15 -1
- package/src/0/3/6/9/1/2/4/8/7/5/1/a432.test.ts +15 -1
- package/src/0/3/6/9/1/2/4/8/7/5/1/a432.trinity.ts +0 -3
- package/src/0/3/6/9/1/2/4/8/7/5/1/a432.vbm.path.ts +6 -2
- package/src/0/3/6/9/1/2/4/8/7/5/1/a432.vbm.relations.ts +15 -1
- package/src/0/index.ts +16 -0
- package/src/index.ts +4 -4
- package/src/kernel/import-graph.ts +58 -0
- package/src/multidimensional-vortex-demo.ts +1 -1
- package/src/quantum/superposition-execution.ts +9 -4
- package/src/thermo/free-energy.ts +279 -0
- package/src/thermo/wastewater-energy.ts +204 -0
- package/src/vbm-math.ts +15 -1
- package/src/verification/lean-bridge.ts +384 -0
- package/src/zero-entropy-demo.ts +15 -1
|
@@ -30,6 +30,32 @@
|
|
|
30
30
|
*/
|
|
31
31
|
|
|
32
32
|
import { createHash } from 'node:crypto'
|
|
33
|
+
import { digitalRoot, throughVoid, bearingForDigit, VORTEX_SEQUENCE, VORTEX_ORBIT, VORTEX_AXIS } from '../0/index.ts'
|
|
34
|
+
import { angleForDigit } from '../0/3/6/9/1/2/4/8/7/5/1/a432.math.ts'
|
|
35
|
+
import { foldToLean, leanIsFixed } from '../kernel/import-graph.ts'
|
|
36
|
+
import {
|
|
37
|
+
GIBBS_FORMATION,
|
|
38
|
+
GIBBS_SPLITTING,
|
|
39
|
+
ENTHALPY_FORMATION,
|
|
40
|
+
ENTHALPY_SPLITTING,
|
|
41
|
+
reversiblePotentialMicrovolts,
|
|
42
|
+
thermoneutralPotentialMicrovolts,
|
|
43
|
+
roundTrip,
|
|
44
|
+
selfTest as thermoSelfTest,
|
|
45
|
+
SCALE,
|
|
46
|
+
ELECTRONS,
|
|
47
|
+
FARADAY,
|
|
48
|
+
reversiblePotentialExact,
|
|
49
|
+
thermoneutralPotentialExact,
|
|
50
|
+
energyFromPotential,
|
|
51
|
+
} from '../thermo/free-energy.ts'
|
|
52
|
+
import {
|
|
53
|
+
balanceFor,
|
|
54
|
+
breakEvenCod,
|
|
55
|
+
TYPICAL_LOADS,
|
|
56
|
+
TREATMENT_DEMAND_DECIJOULES_PER_LITRE,
|
|
57
|
+
selfTest as wastewaterSelfTest,
|
|
58
|
+
} from '../thermo/wastewater-energy.ts'
|
|
33
59
|
import {
|
|
34
60
|
zeroState,
|
|
35
61
|
applyGate1,
|
|
@@ -43,6 +69,9 @@ import {
|
|
|
43
69
|
STEANE_CODE,
|
|
44
70
|
symplecticProduct,
|
|
45
71
|
estimateSurfaceCodeThreshold,
|
|
72
|
+
executeInSuperposition,
|
|
73
|
+
computeInterferencePattern,
|
|
74
|
+
describeQuantumExecution,
|
|
46
75
|
} from '../quantum/index.ts'
|
|
47
76
|
import { ML_KEM_768 } from '../crypto/ml-kem.ts'
|
|
48
77
|
import { sqrt } from '../0/algebra.ts'
|
|
@@ -390,6 +419,361 @@ export const SEALS: Record<string, Seal> = {
|
|
|
390
419
|
ML_KEM_768.decapsulationKeyBytes === 2400 &&
|
|
391
420
|
ML_KEM_768.ciphertextBytes === 1088,
|
|
392
421
|
},
|
|
422
|
+
doubling_avoids_the_triad: {
|
|
423
|
+
basis: 'VORTEX_ORBIT and VORTEX_AXIS are pinned to computation, not trusted. gcd(2,9) = 1, so every power of 2 is a unit mod 9 and can never be a multiple of 3; 2 is a primitive root, so its orbit is all six units {1,2,4,5,7,8}; 2^6 = 64 = 1 mod 9 gives period 6, which makes six cases exhaustive rather than sampled. Reflection through the void carries {1,4,7} onto {9,6,3}, so the triad is reachable only by reflecting.',
|
|
424
|
+
decide: () => {
|
|
425
|
+
// Bound to the kernel's own constants, not retyped here. VORTEX_AXIS had
|
|
426
|
+
// no reader anywhere in the repository before this seal, so nothing
|
|
427
|
+
// checked it was the triad at all.
|
|
428
|
+
const TRIAD: readonly number[] = VORTEX_AXIS
|
|
429
|
+
|
|
430
|
+
// Period 6, from 2^6 = 64 = 1 (mod 9). Without this the six cases below
|
|
431
|
+
// would be a sample; with it they are the whole sequence.
|
|
432
|
+
if (digitalRoot(64) !== digitalRoot(1)) return false
|
|
433
|
+
|
|
434
|
+
// The orbit of 2, generated by doubling and folded each step.
|
|
435
|
+
const orbit: number[] = []
|
|
436
|
+
let d = 1
|
|
437
|
+
for (let i = 0; i < 6; i++) {
|
|
438
|
+
orbit.push(d)
|
|
439
|
+
if (TRIAD.includes(d)) return false // a power of two landed on 3, 6 or 9
|
|
440
|
+
d = digitalRoot(d * 2)
|
|
441
|
+
}
|
|
442
|
+
// Six distinct values, and doubling returns to the start: a full cycle.
|
|
443
|
+
if (new Set(orbit).size !== 6 || d !== 1) return false
|
|
444
|
+
|
|
445
|
+
// Those six are exactly the units mod 9 — the residues coprime to 9.
|
|
446
|
+
// Everything else mod 9 is a multiple of 3, which is the triad.
|
|
447
|
+
const units = [1, 2, 4, 5, 7, 8]
|
|
448
|
+
if (orbit.slice().sort((a, b) => a - b).join() !== units.join()) return false
|
|
449
|
+
|
|
450
|
+
// VORTEX_ORBIT must BE the computed orbit, in doubling order — the
|
|
451
|
+
// constant is pinned to the computation rather than trusted.
|
|
452
|
+
if (VORTEX_ORBIT.join() !== orbit.join()) return false
|
|
453
|
+
|
|
454
|
+
// And VORTEX_AXIS must be exactly what the orbit cannot reach: the
|
|
455
|
+
// complement of the orbit in 1..9, in ascending order.
|
|
456
|
+
const complement = [1, 2, 3, 4, 5, 6, 7, 8, 9].filter((n) => !orbit.includes(n))
|
|
457
|
+
if (TRIAD.join() !== complement.join()) return false
|
|
458
|
+
|
|
459
|
+
// VORTEX_SEQUENCE reads orbit then axis, so it is not an arbitrary order.
|
|
460
|
+
if (VORTEX_SEQUENCE.slice(0, orbit.length).join() !== orbit.join()) return false
|
|
461
|
+
if (VORTEX_SEQUENCE.slice(orbit.length).join() !== TRIAD.join()) return false
|
|
462
|
+
|
|
463
|
+
// Reflection is where the triad enters: 1 -> 9, 4 -> 6, 7 -> 3.
|
|
464
|
+
if (throughVoid(1) !== 9 || throughVoid(4) !== 6 || throughVoid(7) !== 3) return false
|
|
465
|
+
|
|
466
|
+
// And it is an involution, so the reading is reversible, not a relabel.
|
|
467
|
+
for (let n = 0; n <= 9; n++) if (throughVoid(throughVoid(n)) !== n) return false
|
|
468
|
+
|
|
469
|
+
// Reflecting the orbit reaches every member of the triad.
|
|
470
|
+
const reflected = orbit.map(throughVoid)
|
|
471
|
+
return TRIAD.every((t) => reflected.includes(t))
|
|
472
|
+
},
|
|
473
|
+
},
|
|
474
|
+
superposition_reports_its_own_state: {
|
|
475
|
+
basis: "the superposition model's prose must follow its measurement, not assert simultaneity regardless. Collapse is decided by one comparison — interference against the threshold — and the description must claim 'all at once' exactly when that comparison says so. While anything is still open the sequence has not computed all at once, and the text must say that.",
|
|
476
|
+
decide: () => {
|
|
477
|
+
const execution = executeInSuperposition()
|
|
478
|
+
const interference = computeInterferencePattern()
|
|
479
|
+
const text = describeQuantumExecution()
|
|
480
|
+
|
|
481
|
+
// The reported verdict must be the comparison, not a stored string.
|
|
482
|
+
const collapsedByNumbers = interference.working_solution_probability > 85 / 100
|
|
483
|
+
const collapsedByReport = execution.system_correctness === 'collapsed_to_valid'
|
|
484
|
+
if (collapsedByReport !== collapsedByNumbers) return false
|
|
485
|
+
|
|
486
|
+
// The prose takes exactly one of the two branches — never both, never
|
|
487
|
+
// neither, which is what an unconditional closing slogan would do.
|
|
488
|
+
const claimsCollapse = text.includes('COLLAPSED.')
|
|
489
|
+
const admitsOpen = text.includes('STILL SUPERPOSED')
|
|
490
|
+
if (claimsCollapse === admitsOpen) return false
|
|
491
|
+
|
|
492
|
+
// And the branch it takes is the one the measurement licenses.
|
|
493
|
+
if (claimsCollapse !== collapsedByNumbers) return false
|
|
494
|
+
|
|
495
|
+
// When open, it must publish the shortfall rather than only naming it.
|
|
496
|
+
if (admitsOpen && !text.includes('NOT ALL AT ONCE')) return false
|
|
497
|
+
return true
|
|
498
|
+
},
|
|
499
|
+
},
|
|
500
|
+
merkaba_is_two_mirrored_tetrahedra: {
|
|
501
|
+
basis: "the residues mod 3 cut 1..9 into three triangles, each equilateral on the enneagram (120 degrees apart). Reflection through the void SWAPS {1,4,7} with {3,6,9} and fixes {2,5,8} setwise, with the void root 0 fixed throughout. So {3,6,9,0} and {1,4,7,0} are two tetrahedra sharing exactly the void and carried onto each other by the mirror — a merkaba, of which one tetrahedron is half. From 3 the four vertices are 3 with its two triangle partners and the void.",
|
|
502
|
+
decide: () => {
|
|
503
|
+
const RING = [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
504
|
+
const cls = (r: number) => RING.filter((d) => d % 3 === r)
|
|
505
|
+
const up = cls(1) // 1,4,7
|
|
506
|
+
const equator = cls(2) // 2,5,8
|
|
507
|
+
const down = cls(0) // 3,6,9
|
|
508
|
+
const key = (a: readonly number[]) => [...a].sort((x, y) => x - y).join(',')
|
|
509
|
+
|
|
510
|
+
// Three classes of three, partitioning the ring.
|
|
511
|
+
if (up.length !== 3 || equator.length !== 3 || down.length !== 3) return false
|
|
512
|
+
if (key([...up, ...equator, ...down]) !== key(RING)) return false
|
|
513
|
+
|
|
514
|
+
// Each is equilateral on the enneagram: three gaps of 120 degrees.
|
|
515
|
+
for (const t of [up, equator, down]) {
|
|
516
|
+
const b = t.map(bearingForDigit).sort((x, y) => x - y)
|
|
517
|
+
const gaps = [b[1]! - b[0]!, b[2]! - b[1]!, 360 - (b[2]! - b[0]!)]
|
|
518
|
+
if (!gaps.every((g) => g === 120)) return false
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// The mirror swaps the two tetrahedral bases and fixes the equator.
|
|
522
|
+
if (key(up.map(throughVoid)) !== key(down)) return false
|
|
523
|
+
if (key(down.map(throughVoid)) !== key(up)) return false
|
|
524
|
+
if (key(equator.map(throughVoid)) !== key(equator)) return false
|
|
525
|
+
|
|
526
|
+
// The void is the shared apex, and it is the mirror's fixed point.
|
|
527
|
+
if (throughVoid(0) !== 0) return false
|
|
528
|
+
const tetraDown = [...down, 0]
|
|
529
|
+
const tetraUp = [...up, 0]
|
|
530
|
+
if (tetraDown.length !== 4 || tetraUp.length !== 4) return false
|
|
531
|
+
const shared = tetraDown.filter((v) => tetraUp.includes(v))
|
|
532
|
+
if (key(shared) !== '0') return false
|
|
533
|
+
|
|
534
|
+
// The gateway triangle is the axis the kernel already declares, so this
|
|
535
|
+
// is the same structure the vortex constants describe, not a new one.
|
|
536
|
+
if (key(down) !== key(VORTEX_AXIS)) return false
|
|
537
|
+
|
|
538
|
+
// From 3: itself, its two triangle partners, and the void. Four keys.
|
|
539
|
+
const fromThree = [3, ...down.filter((d) => d !== 3), 0]
|
|
540
|
+
return fromThree.length === 4 && key(fromThree) === key(tetraDown)
|
|
541
|
+
},
|
|
542
|
+
},
|
|
543
|
+
agl_acts_on_the_three_triangles: {
|
|
544
|
+
basis: "AGL(1,Z/9) has order 54 and acts on the three triangles — the cosets of {0,3,6} in Z/9, which are the residue classes the merkaba seal uses. The action is transitive with stabiliser 18 and kernel 9, and the induced permutation group is AGL(1,Z/3) of order 6. So the count three is the index of {0,3,6}, derived from the group rather than assumed: orbit x stabiliser = 3 x 18 = 54.",
|
|
545
|
+
decide: () => {
|
|
546
|
+
const units = [1, 2, 4, 5, 7, 8] // the residues coprime to 9
|
|
547
|
+
const maps: ((x: number) => number)[] = []
|
|
548
|
+
for (const a of units) for (let b = 0; b < 9; b++) maps.push((x) => ((a * x + b) % 9 + 9) % 9)
|
|
549
|
+
if (maps.length !== 54) return false
|
|
550
|
+
|
|
551
|
+
// The three cosets of {0,3,6}. Written as residues, so 9 appears as 0.
|
|
552
|
+
const T = [[1, 4, 7], [2, 5, 8], [3, 6, 0]]
|
|
553
|
+
const key = (xs: readonly number[]) => [...new Set(xs)].sort((x, y) => x - y).join(',')
|
|
554
|
+
if (key([...T[0]!, ...T[1]!, ...T[2]!]) !== '0,1,2,3,4,5,6,7,8') return false
|
|
555
|
+
|
|
556
|
+
// The gateway triangle must be the axis the kernel declares, reduced mod 9.
|
|
557
|
+
if (key(T[2]!) !== key(VORTEX_AXIS.map((d) => d % 9))) return false
|
|
558
|
+
|
|
559
|
+
const index = new Map(T.map((t, i) => [key(t), i]))
|
|
560
|
+
const induced = new Set<string>()
|
|
561
|
+
let fixesAll = 0
|
|
562
|
+
let stabilisesFirst = 0
|
|
563
|
+
for (const f of maps) {
|
|
564
|
+
const image = T.map((t) => index.get(key(t.map(f))))
|
|
565
|
+
// Closure: an affine map must send a coset to a coset.
|
|
566
|
+
if (image.some((v) => v === undefined)) return false
|
|
567
|
+
const word = image.join('')
|
|
568
|
+
induced.add(word)
|
|
569
|
+
if (word === '012') fixesAll++
|
|
570
|
+
if (image[0] === 0) stabilisesFirst++
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
// Induced group is AGL(1,Z/3); kernel and stabiliser follow from it.
|
|
574
|
+
if (induced.size !== 6) return false
|
|
575
|
+
if (fixesAll !== 9) return false
|
|
576
|
+
if (stabilisesFirst !== 18) return false
|
|
577
|
+
// Transitive on three, so orbit x stabiliser recovers the whole group.
|
|
578
|
+
return 3 * stabilisesFirst === maps.length
|
|
579
|
+
},
|
|
580
|
+
},
|
|
581
|
+
digit_geometry_is_single_valued: {
|
|
582
|
+
basis: "the two independent digit geometries agree and are injective. a432.math.ts angleForDigit and the kernel's bearingForDigit are written separately — the a432 tree does not import the kernel — so this checks they place all nine digits identically and never put two digits at one bearing. angleForDigit used to map nine digits onto six angles, colliding 3 with 5, 2 with 6 and 8 with 9.",
|
|
583
|
+
decide: () => {
|
|
584
|
+
const RING = [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
585
|
+
|
|
586
|
+
// Injective: nine digits, nine distinct bearings, all whole degrees.
|
|
587
|
+
const kernel = RING.map(bearingForDigit)
|
|
588
|
+
if (new Set(kernel).size !== RING.length) return false
|
|
589
|
+
if (!kernel.every((b) => Number.isInteger(b) && b >= 0 && b < 360)) return false
|
|
590
|
+
|
|
591
|
+
// The two definitions must agree digit for digit.
|
|
592
|
+
if (!RING.every((d) => angleForDigit(d) === bearingForDigit(d))) return false
|
|
593
|
+
|
|
594
|
+
// Evenly spaced: consecutive bearings differ by one ninth of a turn.
|
|
595
|
+
const sorted = [...kernel].sort((a, b) => a - b)
|
|
596
|
+
for (let i = 1; i < sorted.length; i++) {
|
|
597
|
+
if (sorted[i]! - sorted[i - 1]! !== 40) return false
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
// 9 at the top is what fixes the ring's phase; without it any rotation
|
|
601
|
+
// would satisfy the spacing check above.
|
|
602
|
+
return bearingForDigit(9) === 270 && bearingForDigit(3) === 30
|
|
603
|
+
},
|
|
604
|
+
},
|
|
605
|
+
free_energy_of_splitting_is_positive: {
|
|
606
|
+
basis: "ΔG = ΔH − TΔS for water, from tabulated standard-state values. Formation is −237 kJ/mol and splitting is +237, so splitting must be paid for; the reversible cell potential ΔG/(nF) is 1229 mV and the thermoneutral ΔH/(nF) is 1481 mV, the gap being TΔS. A split-then-burn cycle breaks even at perfect efficiency and loses otherwise — checked exhaustively over the efficiency grid, not sampled. This is the sign of ΔG, not an engineering limit.",
|
|
607
|
+
decide: () => {
|
|
608
|
+
if (thermoSelfTest().length > 0) return false
|
|
609
|
+
|
|
610
|
+
// The sign is the claim. Formation releases work; splitting costs it.
|
|
611
|
+
if (!(GIBBS_FORMATION < 0)) return false
|
|
612
|
+
if (!(GIBBS_SPLITTING > 0)) return false
|
|
613
|
+
|
|
614
|
+
// One ledger, read in two directions — so what splitting costs is
|
|
615
|
+
// exactly what burning returns, before any device takes its cut.
|
|
616
|
+
if (ENTHALPY_SPLITTING !== -ENTHALPY_FORMATION) return false
|
|
617
|
+
if (GIBBS_SPLITTING !== -GIBBS_FORMATION) return false
|
|
618
|
+
|
|
619
|
+
// Free energy is strictly less than enthalpy: TΔS is real and is owed.
|
|
620
|
+
if (!(GIBBS_SPLITTING < ENTHALPY_SPLITTING)) return false
|
|
621
|
+
const rev = reversiblePotentialMicrovolts()
|
|
622
|
+
const thermo = thermoneutralPotentialMicrovolts()
|
|
623
|
+
if (!(thermo > rev)) return false
|
|
624
|
+
|
|
625
|
+
// The perfect cycle breaks even EXACTLY — the boundary case, and the one
|
|
626
|
+
// most favourable to a closed loop.
|
|
627
|
+
const ideal = roundTrip(100, 100, 100)
|
|
628
|
+
if (ideal.net !== 0) return false
|
|
629
|
+
|
|
630
|
+
// And no cycle anywhere on the grid gains. Exhaustive over 5% steps in
|
|
631
|
+
// all three efficiencies: 8000 combinations, none of them sampled.
|
|
632
|
+
//
|
|
633
|
+
// The verdict is recomputed from the two energies rather than read off
|
|
634
|
+
// `gainsEnergy`. Trusting that flag let a mutant hardcode it to false and
|
|
635
|
+
// still pass — a seal must not accept the answer from the thing it is
|
|
636
|
+
// judging. `net` is cross-checked against its own definition for the same
|
|
637
|
+
// reason.
|
|
638
|
+
let checked = 0
|
|
639
|
+
for (let e = 5; e <= 100; e += 5) {
|
|
640
|
+
for (let m = 5; m <= 100; m += 5) {
|
|
641
|
+
for (let g = 5; g <= 100; g += 5) {
|
|
642
|
+
checked++
|
|
643
|
+
const r = roundTrip(e, m, g)
|
|
644
|
+
if (r.net !== r.outputRecovered - r.inputRequired) return false
|
|
645
|
+
if (r.outputRecovered > r.inputRequired) return false
|
|
646
|
+
if (r.gainsEnergy !== r.net > 0) return false
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
return checked === 8000
|
|
651
|
+
},
|
|
652
|
+
},
|
|
653
|
+
polluted_water_powers_its_own_cleaning_above_a_threshold: {
|
|
654
|
+
basis: "the described machine works, and the fuel is the pollution rather than the water. Clean water carries zero recoverable energy, so it can only owe the treatment demand; organic load carries 13.9 J per mg COD, and above a threshold of about 4200 mg/L the electricity generated exceeds what membrane treatment consumes. Municipal sewage sits below that line and dairy or manure effluent well above it. The threshold is found by scanning, so it is the same quantity the balance reports.",
|
|
655
|
+
decide: () => {
|
|
656
|
+
if (wastewaterSelfTest().length > 0) return false
|
|
657
|
+
|
|
658
|
+
// The water itself is not the fuel. At zero load there is nothing to
|
|
659
|
+
// burn, and the plant owes exactly the cost of cleaning.
|
|
660
|
+
const clean = balanceFor(0)
|
|
661
|
+
if (clean.energyInLoad !== 0) return false
|
|
662
|
+
if (clean.electricityGenerated !== 0) return false
|
|
663
|
+
if (clean.netElectricity !== -TREATMENT_DEMAND_DECIJOULES_PER_LITRE) return false
|
|
664
|
+
|
|
665
|
+
// A threshold exists and is a genuine boundary: it exports, and one
|
|
666
|
+
// milligram less does not.
|
|
667
|
+
const t = breakEvenCod()
|
|
668
|
+
if (!(t > 0)) return false
|
|
669
|
+
if (!(balanceFor(t).netElectricity > 0)) return false
|
|
670
|
+
if (balanceFor(t - 1).netElectricity > 0) return false
|
|
671
|
+
|
|
672
|
+
// Monotone in the load — more pollution never yields less electricity.
|
|
673
|
+
let previous = -1
|
|
674
|
+
for (let cod = 0; cod <= 20000; cod += 100) {
|
|
675
|
+
const e = balanceFor(cod).electricityGenerated
|
|
676
|
+
if (e < previous) return false
|
|
677
|
+
previous = e
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
// Conservation still binds: recovery is strictly below the energy the
|
|
681
|
+
// load contains, because capture and engine efficiency are each < 100%.
|
|
682
|
+
for (const { cod } of TYPICAL_LOADS) {
|
|
683
|
+
if (cod === 0) continue
|
|
684
|
+
const b = balanceFor(cod)
|
|
685
|
+
if (b.electricityGenerated >= b.energyInLoad) return false
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
// And the model must still place real streams where they actually fall.
|
|
689
|
+
const strongSewage = TYPICAL_LOADS.find((l) => l.name === 'municipal sewage, strong')
|
|
690
|
+
const dairy = TYPICAL_LOADS.find((l) => l.name === 'dairy processing')
|
|
691
|
+
if (!strongSewage || !dairy) return false
|
|
692
|
+
if (balanceFor(strongSewage.cod).selfPowering) return false
|
|
693
|
+
return balanceFor(dairy.cod).selfPowering
|
|
694
|
+
},
|
|
695
|
+
},
|
|
696
|
+
every_model_inverts: {
|
|
697
|
+
basis: "each quantitative result recomputes a second, independent way and the two agree. Entropy recovered from (ΔH − ΔG)/T must equal the tabulated ΔS exactly; ΔG recovered from the EXACT rational potential must return with zero drift, while the rounded microvolt form is separately held to half a microvolt's worth of energy; and the break-even COD found by SCANNING must equal the one obtained by INVERTING the arithmetic, which is a different computation reaching the same integer. Reflection through the void inverts too, being its own inverse.",
|
|
698
|
+
decide: () => {
|
|
699
|
+
// --- free energy: invert ΔG = ΔH − TΔS to recover the entropy --------
|
|
700
|
+
// The forward pass multiplied T by ΔS; dividing it back out must land on
|
|
701
|
+
// the tabulated figure exactly, with no rounding introduced anywhere.
|
|
702
|
+
const T_CENTIKELVIN = 29815
|
|
703
|
+
const TABULATED_ENTROPY_MILLI = -163305
|
|
704
|
+
const recovered =
|
|
705
|
+
((ENTHALPY_FORMATION - GIBBS_FORMATION) * 1000 * 100) / (T_CENTIKELVIN * SCALE)
|
|
706
|
+
if (recovered !== TABULATED_ENTROPY_MILLI) return false
|
|
707
|
+
|
|
708
|
+
// --- cell potential: invert E = ΔG/(nF) to recover ΔG ----------------
|
|
709
|
+
// Exactly, from the rational form. This used to be a bounded check
|
|
710
|
+
// because the microvolt rounding made an exact inverse impossible; the
|
|
711
|
+
// exact potentials exist so that the demand can be equality.
|
|
712
|
+
if (energyFromPotential(reversiblePotentialExact()) !== GIBBS_SPLITTING) return false
|
|
713
|
+
if (energyFromPotential(thermoneutralPotentialExact()) !== ENTHALPY_SPLITTING) return false
|
|
714
|
+
|
|
715
|
+
// The ROUNDED form still cannot invert exactly, and must not pretend to.
|
|
716
|
+
// Its drift is held to half a microvolt's worth of energy — nF/20 in
|
|
717
|
+
// these units, a derived bound rather than an eyeballed tolerance.
|
|
718
|
+
const microvolts = reversiblePotentialMicrovolts()
|
|
719
|
+
const backToGibbs = (microvolts * ELECTRONS * FARADAY) / 10
|
|
720
|
+
const drift = backToGibbs - GIBBS_SPLITTING
|
|
721
|
+
const magnitude = drift < 0 ? -drift : drift
|
|
722
|
+
if (!(2 * magnitude <= (ELECTRONS * FARADAY) / 10)) return false
|
|
723
|
+
if (!(magnitude * 1000000 < GIBBS_SPLITTING)) return false
|
|
724
|
+
|
|
725
|
+
// --- break-even COD: scanning versus inverting ------------------------
|
|
726
|
+
// balanceFor rounds twice on the way up, so a closed form agreeing with
|
|
727
|
+
// the scan is a real check on both, not a restatement of one.
|
|
728
|
+
const scanned = breakEvenCod()
|
|
729
|
+
const numerator = TREATMENT_DEMAND_DECIJOULES_PER_LITRE * 10000
|
|
730
|
+
const denominator = 139 * 65 * 38
|
|
731
|
+
const exact = numerator / denominator
|
|
732
|
+
const inverted = exact % 1 === 0 ? exact + 1 : exact - (exact % 1) + 1
|
|
733
|
+
if (scanned !== inverted) return false
|
|
734
|
+
|
|
735
|
+
// --- reflection is its own inverse ------------------------------------
|
|
736
|
+
for (let d = 0; d <= 9; d++) if (throughVoid(throughVoid(d)) !== d) return false
|
|
737
|
+
|
|
738
|
+
return true
|
|
739
|
+
},
|
|
740
|
+
},
|
|
741
|
+
lean_is_a_fixed_point: {
|
|
742
|
+
basis: "LEAN is defined as the fixed point of the reachability fold, not as a number anyone chose: fold(fold(S)) = fold(S). This checks the fold on graphs whose answers are known by hand — a chain, a cycle, a disconnected node, an entry outside the graph — and checks idempotence on each. Cycles must terminate rather than reaching the answer by luck, and an unreachable node must stay out however many times the fold is applied.",
|
|
743
|
+
decide: () => {
|
|
744
|
+
const key = (s: Set<string>) => [...s].sort().join(',')
|
|
745
|
+
|
|
746
|
+
// A chain, a 2-cycle, and an isolated node, all in one graph.
|
|
747
|
+
const g = new Map<string, readonly string[]>([
|
|
748
|
+
['a', ['b']], ['b', ['c']], ['c', []],
|
|
749
|
+
['x', ['y']], ['y', ['x']],
|
|
750
|
+
['z', []],
|
|
751
|
+
])
|
|
752
|
+
|
|
753
|
+
// Reachability, by hand.
|
|
754
|
+
if (key(foldToLean(g, ['a'])) !== 'a,b,c') return false
|
|
755
|
+
// A cycle must terminate and include both nodes exactly once.
|
|
756
|
+
if (key(foldToLean(g, ['x'])) !== 'x,y') return false
|
|
757
|
+
// An isolated node is reachable only from itself.
|
|
758
|
+
if (key(foldToLean(g, ['z'])) !== 'z') return false
|
|
759
|
+
// Nothing is reachable from nothing.
|
|
760
|
+
if (foldToLean(g, []).size !== 0) return false
|
|
761
|
+
// An entry that is not in the graph contributes nothing rather than throwing.
|
|
762
|
+
if (foldToLean(g, ['absent']).size !== 0) return false
|
|
763
|
+
// z is outside the fold from a, and stays outside however often we fold.
|
|
764
|
+
if (foldToLean(g, ['a']).has('z')) return false
|
|
765
|
+
|
|
766
|
+
// The definition must be well founded on every one of these.
|
|
767
|
+
for (const entries of [['a'], ['x'], ['z'], [], ['a', 'x'], ['absent']]) {
|
|
768
|
+
if (!leanIsFixed(g, entries)) return false
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
// And on a graph where an entry reaches everything, lean is everything.
|
|
772
|
+
const full = new Map<string, readonly string[]>([['r', ['a', 'x', 'z']], ['a', ['b']], ['b', []], ['x', ['x']], ['z', []]])
|
|
773
|
+
if (key(foldToLean(full, ['r'])) !== 'a,b,r,x,z') return false
|
|
774
|
+
return leanIsFixed(full, ['r'])
|
|
775
|
+
},
|
|
776
|
+
},
|
|
393
777
|
}
|
|
394
778
|
|
|
395
779
|
// ============================================================================
|
package/src/zero-entropy-demo.ts
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* 3. Pure mathematical relationships (no randomness or entropy)
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
+
import { pathToFileURL } from 'node:url'
|
|
12
13
|
import { min, sqrt } from './0/algebra.ts'
|
|
13
14
|
import {
|
|
14
15
|
ZERO_ENTROPY_CONSTANTS,
|
|
@@ -297,7 +298,20 @@ export class ZeroEntropyDemo {
|
|
|
297
298
|
}
|
|
298
299
|
|
|
299
300
|
// Run demo if this file is executed directly
|
|
300
|
-
|
|
301
|
+
/**
|
|
302
|
+
* True when this file is the entry point Node was started with.
|
|
303
|
+
*
|
|
304
|
+
* ESM has no `require.main`. The CommonJS idiom did not merely fail to detect
|
|
305
|
+
* direct execution here — `require` is undefined in an ES module, so the guard
|
|
306
|
+
* THREW on import and made the whole module unloadable. Nobody importing this
|
|
307
|
+
* ever got far enough to notice the guard was wrong.
|
|
308
|
+
*/
|
|
309
|
+
function isMainModule(): boolean {
|
|
310
|
+
const entry = process.argv[1]
|
|
311
|
+
return entry !== undefined && import.meta.url === pathToFileURL(entry).href
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
if (isMainModule()) {
|
|
301
315
|
ZeroEntropyDemo.runCompleteDemo();
|
|
302
316
|
}
|
|
303
317
|
|