code wiki / _hdl_build / nx_pendulum_gate.nx
nx_pendulum_gate.nx source
↩ module page · 116 lines · 8288 B
1// nx_pendulum_gate.nx -- NATIVE verify of the browser pendulum (base = mmap; SAME code runs in sovereign WASM).
2// Measures the EMERGENT period from integrating the true nonlinear pendulum and validates the real, experimentally
3// established laws: (a) Galileo's T proportional to sqrt(L) [T(4L)/T(L)=2], (b) small-amplitude ISOCHRONISM
4// [period ~ independent of amplitude], (c) large-amplitude period GROWTH [+~18% at 90 deg, the exact-pendulum
5// value (2/pi)K(sin45)=1.18]. Liar-kill: the LINEAR approximation (-k theta) stays isochronous at 90 deg (no
6// growth) -- so the growth test distinguishes real nonlinear pendulum physics from the textbook linearization.
7// license_tier: ORIGINAL expect_exit: 0
8import "nx_syscalls.nx"
9import "nx_pendulum.nx"
10import "nx_png.nx"
11import "nx_gate_verdict.nx"
12
13func 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 }
14func 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 }
15// ★D001 MIGRATED BY HAND (nx_gate_dry_apply does not recognise the pass[0]-against-a-literal idiom,
16// and allow_own_verdict ships a gate no judge can read). On the shared base class the count follows
17// the rows by construction: the two added below move it themselves.
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 }
20
21// integrate from (theta=amp, omega=0) and return the full period in steps (first omega neg->pos crossing = half)
22func measure_period(base: i64, gln: i64, amp: i64, lin: i64) -> i64 {
23 let TH: *i64=(base+O_TH) as *i64; let OM: *i64=(base+O_OM) as *i64; let GLN: *i64=(base+O_GLN) as *i64; let LIN: *i64=(base+O_LIN) as *i64
24 GLN[0]=gln; LIN[0]=lin; TH[0]=amp; OM[0]=0
25 step_one_impl(base)
26 var prev: i64=OM[0]; var step: i64=1
27 while step<2000000 {
28 step_one_impl(base)
29 let om: i64=OM[0]
30 if prev<0 { if om>=0 { return step*2 } }
31 prev=om; step=step+1
32 }
33 return 0
34}
35
36func main() -> i64 {
37 let pass: *i64 = gv_ctr()
38 g_w("=== NX-PENDULUM native verify (real nonlinear pendulum; SAME code runs in sovereign wasm) ===\n")
39 let base: i64 = sys_mmap(2*1048576) as i64
40
41 let T1: i64 = measure_period(base, 980, 87266, 0) // L=1m, 5deg, true
42 let T4: i64 = measure_period(base, 245, 87266, 0) // L=4m (g/L=2.45 -> GLN=245), 5deg, true
43 let T10: i64 = measure_period(base, 980, 174533, 0) // L=1m, 10deg, true
44 let T90: i64 = measure_period(base, 980, 1570796, 0) // L=1m, 90deg, true
45 let T5L: i64 = measure_period(base, 980, 87266, 1) // L=1m, 5deg, LINEAR
46 let T90L: i64 = measure_period(base, 980, 1570796, 1) // L=1m, 90deg, LINEAR
47
48 let sqrtL: i64 = T4*1000/T1 // expect 2000 (sqrt(4)=2)
49 let iso: i64 = iabs(T10-T1)*1000/T1 // expect ~0
50 let growth: i64 = T90*1000/T1 // expect ~1180 (exact pendulum +18%)
51 let lingrowth: i64= T90L*1000/T5L // expect ~1000 (linear is isochronous)
52
53 init_impl(base)
54 var t: i64=0; while t<60 { tick_impl(base, 0); t=t+1 }
55 render_impl(base)
56 write_png((base + O_FB) as *i64, W, H, "knowledge/nx_pendulum.png" as *u8)
57 let fb: *i64=(base+O_FB) as *i64; var nb: i64=0; var i: i64=0
58 while i<W*H { let b: i64=i*8; let m: *u8=(base+O_FB) as *u8; if (m[b] as i64)!=10 { 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 }
59
60 // ★THE RASTER ROW CLAIMED "pivot + rod + bob + trail" WHILE COUNTING PIXELS. nb>100 is satisfied by
61 // ANY one of those four, so three could vanish and the row still passed. Found 2026-08-14 by
62 // nx_vacuity_census; same class as nx_solarsim_gate ("Sun + planets"), nx_gassim_gate ("20
63 // particles, speed-coloured") and nx_chaos_gate ("two diverging trajectories").
64 // ★NAMES NO COLOUR AT ALL. Rather than restate render_impl's palette here -- which would rot the
65 // moment the palette moved -- this asks that the three solid parts each differ from the
66 // BACKGROUND and from EACH OTHER, which is exactly what "three separate elements" means; then the
67 // trail is whatever is none of those four. The bob position is taken from render_impl's own
68 // formula: bx = CX + LPX*sin_fixed(th)/SC, by = PIVY + LPX*cos_fixed(th)/SC.
69 let fbp: *i64 = (base+O_FB) as *i64
70 let THg: *i64 = (base+O_TH) as *i64
71 let thg: i64 = THg[0]
72 let bxg: i64 = CX + LPX*sin_fixed(thg)/SC
73 let byg: i64 = PIVY + LPX*cos_fixed(thg)/SC
74 let bgp: i64 = fbp[0]
75 // pivot sampled ABOVE its centre: the rod is drawn after the pivot and starts exactly at the
76 // centre, so the centre pixel is the ROD's colour, not the pivot's.
77 let pivc: i64 = fbp[(PIVY-2)*W + CX]
78 // rod sampled a third of the way down, clear of both the pivot disc and the bob disc
79 let rodc: i64 = fbp[(PIVY + (byg-PIVY)/3)*W + (CX + (bxg-CX)/3)]
80 let bobc: i64 = fbp[byg*W + bxg]
81 var trailn: i64 = 0
82 var sp: i64 = 0
83 while sp < W*H {
84 let pv: i64 = fbp[sp]
85 if pv != bgp { if pv != pivc { if pv != rodc { if pv != bobc { trailn = trailn + 1 } } } }
86 sp = sp + 1
87 }
88 var elems_ok: i64 = 0
89 if pivc != bgp { if rodc != bgp { if bobc != bgp {
90 if pivc != rodc { if rodc != bobc { if pivc != bobc { elems_ok = 1 } } }
91 } } }
92 g_w(" elements: bg="); g_n(bgp); g_w(" pivot="); g_n(pivc); g_w(" rod="); g_n(rodc)
93 g_w(" bob="); g_n(bobc); g_w(" trail-pixels="); g_n(trailn); g_w("\n")
94 g_w(" geometry: th="); g_n(thg); g_w(" CX="); g_n(CX); g_w(" PIVY="); g_n(PIVY)
95 g_w(" LPX="); g_n(LPX); g_w(" SC="); g_n(SC); g_w(" -> bob=("); g_n(bxg); g_w(","); g_n(byg)
96 g_w(") frame="); g_n(W); g_w("x"); g_n(H); g_w("\n")
97
98 g_w(" periods(steps): T(L1,5d)="); g_n(T1); g_w(" T(L4,5d)="); g_n(T4); g_w(" T(L1,10d)="); g_n(T10); g_w(" T(L1,90d)="); g_n(T90); g_w("\n")
99 g_w(" sqrtL_ratio(x1000)="); g_n(sqrtL); g_w(" (2000) isochronism_diff(x1000)="); g_n(iso); g_w(" growth90(x1000)="); g_n(growth); g_w(" (~1180) linear_growth(x1000)="); g_n(lingrowth); g_w(" (~1000)\n")
100
101 var ran: i64=0; if T1>0 { if T90>0 { ran=1 } }
102 g_row("RUNS: the nonlinear pendulum integrated and an emergent period was measured at every config" as *u8, ran, pass)
103 g_row("GALILEO T~sqrt(L): T(4L)/T(L) = 2.0 within 3% (period scales as the square root of length)" as *u8, (iabs(sqrtL-2000) < 60) as i64, pass)
104 g_row("ISOCHRONISM: at small amplitude the period is ~independent of amplitude (5deg vs 10deg < 2%)" as *u8, (iso < 20) as i64, pass)
105 g_row("LARGE-ANGLE GROWTH: the period at 90deg exceeds the small-angle period by >10% (real nonlinear effect, ~18%)" as *u8, ((growth > 1100) as i64)*((growth < 1260) as i64), pass)
106 g_row("LIAR-KILL: the LINEAR approximation stays isochronous at 90deg (<5% growth) -> the growth test detects true nonlinear physics" as *u8, ((lingrowth < 1050) as i64)*((growth > 1100) as i64), pass)
107 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)
108 g_row("FOUR DISTINCT ELEMENTS DRAWN: pivot, rod and bob are each non-background AND mutually different colours, so the frame really carries three separate parts rather than one blob" as *u8, elems_ok, pass)
109 g_row("TRAIL PRESENT: at least one pixel is none of background, pivot, rod or bob -- the bob's history is actually plotted, which nb>100 could never distinguish" as *u8, (trailn > 0) as i64, pass)
110 var iface: i64=0; if ww()==W { if hh()==H { if fb_off()==O_FB { iface=1 } } }
111 g_row("WASM-READY: init/tick/render/ww/hh/fb_off interface intact -- the exact base-relative code wasm runs" as *u8, iface, pass)
112
113 let rc: i64 = gv_verdict("PENDULUM-GATE" as *u8, pass, "real nonlinear pendulum; Galileo sqrt(L) + isochronism + large-angle growth, and pivot, rod, bob and trail each verified PRESENT and DISTINCT in the rendered frame" as *u8)
114 sys_exit(rc)
115 return rc
116}