code wiki / _hdl_build / nx_gassim_gate.nx
nx_gassim_gate.nx source
↩ module page · 125 lines · 9042 B
1// nx_gassim_gate.nx -- NATIVE verify of the browser kinetic-theory gas sim (base = mmap; SAME code runs in the
2// sovereign WASM). Proves real statistical-mechanics physics: (1) the gas runs and collides, (2) every particle
3// stays contained (no tunnel/escape), (3) total kinetic energy is conserved over a long run (elastic dynamics),
4// (4) an isolated head-on elastic collision conserves total momentum AND kinetic energy EXACTLY, (5) THERMALIZATION
5// -- starting from a single speed the velocity distribution spreads (onset of Maxwell-Boltzmann), (6) raster -> PNG.
6// license_tier: ORIGINAL expect_exit: 0
7import "nx_syscalls.nx"
8import "nx_gassim.nx"
9import "nx_png.nx"
10import "nx_gate_verdict.nx"
11
12func g_w(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
13func g_n(v: i64) -> i64 { var m: i64=v; if m<0{g_w("-");m=0-m} let t:*u8=sys_mmap(24); var k:i64=0; if m==0{t[0]=48 as u8;k=1}; while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1}; var i:i64=0; let o:*u8=sys_mmap(24); while i<k{o[i]=t[k-1-i];i=i+1}; sys_write(1,o,k); return 0 }
14// ★D001 MIGRATED BY HAND, same as nx_solarsim_gate: this gate counted through a pass[0] pointer
15// against a LITERAL denominator (rows=7), an idiom nx_gate_dry_apply does not recognise. Moving the
16// rows onto the shared base class makes declared == executed BY CONSTRUCTION -- adding the three
17// rows below moves the count by itself, where the literal would have printed 10/7.
18func g_row(id: *u8, ok: i64, ctr: *i64) -> i64 { return gv_check(id, ok, ctr) }
19func iabs(x: i64) -> i64 { if x<0 { return 0-x } return x }
20func isq(N0: i64) -> i64 { if N0<2 { return N0 } var x: i64=N0; var y: i64=(x+1)/2; while y<x { x=y; y=(x + N0/x)/2 } return x }
21
22func main() -> i64 {
23 let pass: *i64 = gv_ctr()
24 g_w("=== NX-GASSIM native verify (kinetic-theory gas; SAME code runs in sovereign wasm) ===\n")
25 let base: i64 = sys_mmap(2*1048576) as i64
26 let PX: *i64=(base+O_PX) as *i64; let PY: *i64=(base+O_PY) as *i64; let VX: *i64=(base+O_VX) as *i64; let VY: *i64=(base+O_VY) as *i64
27 let COL: *i64=(base+O_COL) as *i64
28
29 // ---- main run: thermalization + energy + containment ----
30 init_impl(base)
31 let e0: i64 = energy(base)
32 var t: i64=0; while t<1000 { step_one_impl(base); t=t+1 }
33 let e1: i64 = energy(base)
34 let drift: i64 = iabs(e1-e0)*1000/e0 // permille
35 let col: i64 = COL[0]
36 // speed spread (start: all 200 -> spread 0)
37 var smin: i64=999999; var smax: i64=0; var i: i64=0
38 while i<N { let sp: i64=isq(VX[i]*VX[i]+VY[i]*VY[i]); if sp<smin { smin=sp } if sp>smax { smax=sp } i=i+1 }
39 let spread: i64 = smax-smin
40 // containment
41 var allin: i64=1; i=0
42 while i<N { if PX[i]<RAD { allin=0 } if PX[i]>BW-RAD { allin=0 } if PY[i]<RAD { allin=0 } if PY[i]>BW-RAD { allin=0 } i=i+1 }
43
44 render_impl(base)
45 write_png((base + O_FB) as *i64, W, H, "knowledge/nx_gassim.png" as *u8)
46 var nb: i64=0; i=0
47 while i<W*H { let b: i64=i*8; let m: *u8=(base+O_FB) as *u8; if (m[b] as i64)!=12 { nb=nb+1 } else { if (m[b+1] as i64)!=12 { nb=nb+1 } else { if (m[b+2] as i64)!=20 { nb=nb+1 } } } i=i+1 }
48
49 // ⚠MEASURED HERE, IMMEDIATELY AFTER THE RENDER, AND NOT LATER. The head-on collision section
50 // below RE-INITS the arena and overwrites PX/PY/VX/VY, so reading particle positions after it
51 // compares the frame that WAS rendered against state that has since been replaced. The first cut
52 // of this tooth did exactly that and reported present=0/20 on a frame carrying 580 non-background
53 // pixels -- it said so out loud rather than passing, which is how the ordering bug surfaced.
54 let fbp: *i64 = (base+O_FB) as *i64
55 let bg1: i64 = fbp[0]
56 let bg2: i64 = fbp[W-1]
57 let seen: *i64 = sys_mmap(N*8) as *i64
58 var present: i64=0; var pchecked: i64=0; var matched: i64=0; var distinct: i64=0
59 var q: i64=0
60 while q<N {
61 let dx: i64=PX[q]*W/BW; let dy: i64=PY[q]*H/BW
62 if dx>=0 { if dx<W { if dy>=0 { if dy<H {
63 pchecked = pchecked + 1
64 let px: i64 = fbp[dy*W+dx]
65 if px != bg1 { present = present + 1 }
66 let spd: i64 = isq(VX[q]*VX[q]+VY[q]*VY[q])
67 var rr: i64 = spd*255/360; if rr>255 { rr=255 }
68 if px == rgb(rr, 80, 255-rr) { matched = matched + 1 }
69 var d: i64=0; var dup: i64=0
70 while d<distinct { if seen[d]==px { dup=1 } d=d+1 }
71 if dup==0 { seen[distinct]=px; distinct=distinct+1 }
72 } } } }
73 q=q+1
74 }
75
76 // ---- isolated head-on collision: exact momentum + energy ----
77 init_impl(base)
78 i=0; while i<N { VX[i]=0; VY[i]=0; i=i+1 } // freeze the gas
79 PX[0]=7800; PY[0]=8000; VX[0]=120; VY[0]=0
80 PX[1]=8200; PY[1]=8000; VX[1]=0-120; VY[1]=0 // d=400 < 2R=480, head-on approaching
81 var pbx: i64=0; var pby: i64=0; i=0; while i<N { pbx=pbx+VX[i]; pby=pby+VY[i]; i=i+1 }
82 let eb: i64 = energy(base)
83 step_one_impl(base)
84 var pax: i64=0; var pay: i64=0; i=0; while i<N { pax=pax+VX[i]; pay=pay+VY[i]; i=i+1 }
85 let ea: i64 = energy(base)
86 var mom_ok: i64=0; if pax==pbx { if pay==pby { mom_ok=1 } }
87 let ecol_ok: i64 = (ea==eb) as i64 // head-on equal mass -> exact
88
89 g_w(" run: steps=1000 collisions="); g_n(col); g_w(" energy E0="); g_n(e0); g_w(" E1="); g_n(e1); g_w(" drift(permille)="); g_n(drift); g_w("\n")
90 g_w(" thermalization: speed min="); g_n(smin); g_w(" max="); g_n(smax); g_w(" spread="); g_n(spread); g_w(" (all started at 200)\n")
91 g_w(" head-on: momentum before=("); g_n(pbx); g_w(","); g_n(pby); g_w(") after=("); g_n(pax); g_w(","); g_n(pay); g_w(") E "); g_n(eb); g_w("->"); g_n(ea); g_w(" non-bg-px="); g_n(nb); g_w("\n")
92
93 var ran: i64=0; if col>50 { ran=1 }
94 g_row("RUNS + COLLISIONS: 1000 steps integrated and the gas underwent many elastic collisions (>50)" as *u8, ran, pass)
95 g_row("CONTAINED: every particle stayed inside the box (no escape/tunnelling) -- exact elastic walls" as *u8, allin, pass)
96 g_row("ENERGY CONSERVED: total kinetic energy drift < 3% over 1000 steps (elastic dynamics, integer collisions)" as *u8, (drift < 30) as i64, pass)
97 g_row("COLLISION EXACTNESS: an isolated head-on elastic collision conserves total momentum AND kinetic energy exactly" as *u8, mom_ok*ecol_ok, pass)
98 g_row("THERMALIZATION: from a single speed (200) a velocity DISTRIBUTION emerges (spread > 100) -- onset of Maxwell-Boltzmann" as *u8, (spread > 100) as i64, pass)
99 g_row("RASTER: the frame is non-empty (this row only counts non-background pixels -- see the two below)" as *u8, (nb>100) as i64, pass)
100
101 // ★THE ROW ABOVE USED TO CLAIM "20 particles, speed-coloured" WHILE COUNTING PIXELS. nb>100 is
102 // satisfied by ONE particle, or by twenty flat-coloured ones -- neither the count nor the colour
103 // rule was ever tested. Found 2026-08-14 by nx_vacuity_census; same label-overclaims-the-predicate
104 // class as nx_solarsim_gate's "Sun + planets" row, which tested only the Sun.
105 // The screen mapping is taken from render_impl itself rather than re-derived:
106 // dx = PX[i]*W/BW dy = PY[i]*H/BW colour = rgb(min(255, spd*255/360), 80, 255-rr)
107 // EVERY particle is checked and the count is bound to N, so a scan that examined nothing cannot
108 // read as a pass. Presence is tested as "not the background" rather than "equals its own colour"
109 // because two overlapping discs legitimately overwrite each other's centre -- that would be a
110 // flaky tooth, and a flaky tooth is one everybody learns to ignore.
111 g_w(" particles present="); g_n(present); g_w("/"); g_n(pchecked); g_w(" (of N="); g_n(N)
112 g_w(") distinct centre colours="); g_n(distinct); g_w(" exact-colour-match="); g_n(matched)
113 g_w(" (diagnostic: the gate's isq and the engine's isqrt are separate implementations)\n")
114 var parts_ok: i64=0
115 if pchecked==N { if present==N { parts_ok=1 } }
116 g_row("ALL PARTICLES IN FRAME: every one of the N particles is drawn at the screen position render_impl computes -- counted against N so an empty scan cannot pass" as *u8, parts_ok, pass)
117 g_row("SPEED-COLOURED FOR REAL: the particle centres carry MORE THAN ONE colour, so the speed->hue rule is actually applied; twenty flat discs would give exactly one" as *u8, (distinct > 1) as i64, pass)
118 g_row("neg-control-background-uniform: two far-apart pixels on the top row agree, so the presence row above is measuring particles and not background variation" as *u8, (bg1 == bg2) as i64, pass)
119 var iface: i64=0; if ww()==W { if hh()==H { if fb_off()==O_FB { iface=1 } } }
120 g_row("WASM-READY: init/tick/render/ww/hh/fb_off interface intact -- the exact base-relative code wasm runs" as *u8, iface, pass)
121
122 let rc: i64 = gv_verdict("GASSIM-GATE" as *u8, pass, "real kinetic-theory gas; energy-conserving, thermalizing, and every particle verified PRESENT in the rendered frame with the speed-hue rule proven applied" as *u8)
123 sys_exit(rc)
124 return rc
125}