code wiki / _hdl_build / nx_actbench.nx
nx_actbench.nx source
↩ module page · 1222 lines · 57971 B
1// nx_actbench.nx -- I3: THE PHYSICAL-PLAUSIBILITY RULER. (beyond-metahuman plan, Phase 0 instruments.)
2//
3// ★★★WHY THIS EXISTS, AND IT IS A MEASURED REASON RATHER THAN A PREFERENCE. The field published the
4// number that convicts our whole instrument set: "Beyond MPJPE: A Physics-Based Audit of Monocular 3D
5// Human Pose Estimation" (PhysHuman @ CVPR 2026) reports that the geometric standard MPJPE and the
6// physical composite PHYSSCORE are correlated at only SPEARMAN rho = 0.37, WITH RANK REVERSALS -- the
7// temporal method WHAM ranks 3rd on MPJPE and 1st on physical plausibility. Every ruler this program
8// owns -- nx_twinbench (mm), nx_bodybench (geometry MIN), nx_facemark (placement), nx_curvebench --
9// is GEOMETRIC. So a body of ours can score well on all of them and still be physically impossible,
10// and nothing we own could see it. This organ is the missing axis.
11//
12// ★WHAT IT IS NOT: invented teeth. The first draft of this rung was going to assert energy decay and
13// replay determinism -- both fine properties, both MINE. runtime/nx_pose_benchmark_audit.nx exists
14// precisely to convict that move, grading every claim MECHANICAL vs SELF-VALIDATED vs
15// INDUSTRY-MEASURED, and its standing verdict is "ZERO industry benchmarks is the honest truth". So
16// the channels below are the PUBLISHED ones (PHYSSCORE's six; definitions from the PhysDiff/EDGE
17// lineage and PhyMotion arXiv:2605.14269), not the ones that were convenient to write.
18//
19// ★A RULER IS A FUNCTION OF A TRACE. PHYSSCORE operates on joint positions and needs NO GROUND TRUTH,
20// which is the property that matters here: it can score a GENERATED being. That is what makes it
21// usable on the creature/fantasy half, where there is no cadaver dragon to compare against.
22// So this organ takes a trace + a skeleton and scores it. It is coupled to no generator.
23//
24// ★★CHANNEL 4 READS THE SKELETON'S OWN EMITTED TABLE. nx_skelgen writes "B <bone> <len> <lo> <hi>";
25// this reads those rows. There is therefore NO SECOND CONSTRAINT TABLE to drift from -- the exact
26// failure nx_skelgen itself documents (a fix applied to one of two tables is a bug with a good alibi).
27//
28// ⚠★DECLARED SUBSTITUTION, because an undeclared one is a lie: PHYSSCORE measures penetration and
29// float against the lowest MESH VERTEX. We measure against the lowest JOINT. A joint is inboard of the
30// surface, so our penetration UNDER-reports and our float OVER-reports by roughly a limb radius. The
31// number travels with that caveat in-band, every call.
32//
33// ⚠★AND THE FIELD ITSELF SAYS THERE IS NO STANDARD PARAMETER SET ("various methods may employ
34// disparate parameter choices and even design distinct evaluation approaches"). So every threshold
35// this organ uses is EMITTED IN THE OUTPUT. A plausibility number quoted without its thresholds is
36// not comparable to anyone else's, including our own last run.
37//
38// nx_actbench score <trace> <skel.dat>
39// nx_actbench selftest
40// license_tier: ORIGINAL expect_exit: 0 No hw writes (Rule 26).
41import "nx_gate_verdict.nx"
42const AB_MAGIC_65536: i64 = 65536
43const AB_MAGIC_4096: i64 = 4096
44const AB_MAGIC_1523: i64 = 1523
45
46// ---- capacity. ★DECLARED AND FAIL-LOUD, not preallocated-and-truncating. This program has now been
47// bitten three times by an emitter that silently stopped at a cap (the red-face bug, the part array,
48// the ring pool), so a trace beyond these bounds is REFUSED with its own exit code.
49const AB_MAXF: i64 = 512
50const AB_MAXJ: i64 = 64
51const AB_MAXC: i64 = 32
52// C-row stride: joint, parent, child, bone, flexion-axis, sign-of-positive-flexion
53const AB_CST: i64 = 6
54// capsule limb proxies for channel 6: <j0> <j1> <radius_mm>
55const AB_MAXS: i64 = 32
56const AB_SST: i64 = 3
57// samples along a capsule axis when probing closest approach; declared in the output because it
58// bounds the resolution of the penetration depth
59const AB_CAPSAMP: i64 = 16
60const AB_MAXB: i64 = 24
61const AB_MODE: i64 = 420
62// ---- the declared thresholds. Every one of these is printed in the output.
63// 5mm is the tolerance the literature uses for both penetration and float, to absorb geometry
64// approximation. It is theirs, not tuned by us.
65const AB_GTOL: i64 = 5
66// a foot counts as in contact below this height. Ours, declared: a joint sits inboard of the sole,
67// so the contact band must be at least an ankle radius.
68const AB_CONTACT: i64 = 40
69// half-width of the support a single contacting foot provides, in mm. Ours, declared.
70const AB_FOOTHALF: i64 = 60
71const AB_SCALE: i64 = 1024
72const AB_PERMIL: i64 = 1000
73const AB_DEG: i64 = 180
74// cosine table build precision: 2^20 internally, stored at 2^10 (AB_SCALE)
75const AB_FXB: i64 = 20
76const AB_COS1: i64 = 1048416
77const AB_SIN1: i64 = 18300
78const AB_ONE20: i64 = 1048576
79// exit codes -- distinct, so a caller can tell WHICH refusal happened
80const AB_E_OPEN: i64 = 3
81const AB_E_CAP: i64 = 4
82const AB_E_EMPTY: i64 = 5
83
84func ab_puts(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
85func ab_pn(v: i64) -> i64 {
86 let b: *u8 = sys_mmap(32); var x: i64=v; var ng: i64=0
87 if x<0 { ng=1; x=0-x }
88 var i: i64=31
89 if x==0 { b[i]=48 as u8; i=i-1 }
90 while x>0 { b[i]=(48+x%10) as u8; x=x/10; i=i-1 }
91 if ng==1 { b[i]=45 as u8; i=i-1 }
92 sys_write(1,(b as i64 + i + 1) as *u8, 31-i); return 0
93}
94func ab_streq(a: *u8, b: *u8) -> i64 {
95 var i: i64=0; var go: i64=1; var eq: i64=1
96 while go==1 { if a[i]!=b[i] { eq=0; go=0 } else { if a[i]==(0 as u8) { go=0 } else { i=i+1 } } }
97 return eq
98}
99func ab_iabs(v: i64) -> i64 { if v<0 { return 0-v } return v }
100// integer sqrt, Newton. ★Bounded inputs only: callers pass squared millimetre distances, so the
101// argument stays far below the i64 ceiling -- the nx_twinbench overflow (a determinant over squared
102// edge lengths reached 1e25 and wrapped SILENTLY to a plausible number) is the reason that sentence
103// is written down rather than assumed.
104func ab_isqrt(v: i64) -> i64 {
105 if v <= 0 { return 0 }
106 var x: i64 = v
107 var y: i64 = (x+1)/2
108 while y < x { x = y; y = (x + v/x)/2 }
109 return x
110}
111
112// ---- COSINE TABLE, 0..180 degrees at AB_SCALE. Built by angle-addition recurrence from cos(1deg) and
113// sin(1deg) rather than typed as 181 literals: a hand-typed trig table is ~181 chances to fat-finger a
114// digit and a wrong cosine looks exactly like a right one. The KAT tooth checks the known values.
115func ab_costab() -> *i64 {
116 let t: *i64 = sys_mmap((AB_DEG+1)*8) as *i64
117 var c: i64 = AB_ONE20
118 var s: i64 = 0
119 var d: i64 = 0
120 while d <= AB_DEG {
121 t[d] = c >> (AB_FXB - 10)
122 let nc: i64 = (c*AB_COS1 - s*AB_SIN1) >> AB_FXB
123 let ns: i64 = (s*AB_COS1 + c*AB_SIN1) >> AB_FXB
124 c = nc
125 s = ns
126 d = d + 1
127 }
128 return t
129}
130// acos by monotone search: cos is strictly decreasing on 0..180, so the first index whose cosine is
131// at or below the target IS the angle. Same table, read backwards -- no second approximation.
132func ab_acos(cv: i64, t: *i64) -> i64 {
133 var d: i64 = 0
134 var got: i64 = AB_DEG
135 var go: i64 = 1
136 while go == 1 {
137 if d > AB_DEG { go = 0 } else {
138 if t[d] <= cv { got = d; go = 0 } else { d = d + 1 }
139 }
140 }
141 return got
142}
143
144// ---- tiny text parsing over a whole-file buffer
145func ab_isdig(c: i64) -> i64 { if c>=48 { if c<=57 { return 1 } } return 0 }
146// read one signed integer starting at or after p[0]; advances p[0] past it. rc 1 = got one, 0 = none.
147func ab_tok(b: *u8, n: i64, p: *i64, out: *i64) -> i64 {
148 var i: i64 = p[0]
149 var go: i64 = 1
150 while go == 1 {
151 if i >= n { go = 0 } else {
152 let c: i64 = b[i] as i64
153 var st: i64 = 0
154 if ab_isdig(c) == 1 { st = 1 }
155 if c == 45 { st = 1 }
156 if st == 1 { go = 0 } else { i = i + 1 }
157 }
158 }
159 if i >= n { p[0] = i; return 0 }
160 var sg: i64 = 1
161 if b[i] as i64 == 45 { sg = 0-1; i = i + 1 }
162 var v: i64 = 0
163 var g2: i64 = 1
164 while g2 == 1 {
165 if i >= n { g2 = 0 } else {
166 let c2: i64 = b[i] as i64
167 if ab_isdig(c2) == 1 { v = v*10 + (c2-48); i = i + 1 } else { g2 = 0 }
168 }
169 }
170 p[0] = i
171 out[0] = v*sg
172 return 1
173}
174// advance p[0] to the first byte of the next line
175func ab_nextline(b: *u8, n: i64, p: *i64) -> i64 {
176 var i: i64 = p[0]
177 var go: i64 = 1
178 while go == 1 {
179 if i >= n { go = 0 } else {
180 if b[i] as i64 == 10 { i = i + 1; go = 0 } else { i = i + 1 }
181 }
182 }
183 p[0] = i
184 return 0
185}
186
187// ---- trace store. V[(f*AB_MAXJ + j)*3 + axis] in millimetres. present[] marks filled slots.
188func ab_vidx(f: i64, j: i64, ax: i64) -> i64 { return (f*AB_MAXJ + j)*3 + ax }
189
190// ---- support geometry ------------------------------------------------------------------------
191// ★★★A SUPPORT POLYGON OF TWO POINTS CANNOT REPRESENT A TWO-FOOTED STANCE. The first version of the
192// CoM channel took the first two contacts and treated the segment between them as the support. With a
193// heel and a toe per foot that is FOUR contacts, and it silently used the right foot only -- so a
194// correctly standing body measured as falling sideways by exactly its half-stance width. The declared
195// approximation covered "one or two contacts"; using two OUT OF four was an UNDECLARED truncation,
196// which is the failure class this program keeps re-finding. Fixed properly: the real convex hull.
197func ab_seg_dist(ax: i64, az: i64, bx: i64, bz: i64, px: i64, pz: i64) -> i64 {
198 let ex: i64 = bx-ax
199 let ez: i64 = bz-az
200 let den: i64 = ex*ex + ez*ez
201 var t256: i64 = 0
202 if den > 0 {
203 let num: i64 = (px-ax)*ex + (pz-az)*ez
204 t256 = num*256/den
205 if t256 < 0 { t256 = 0 }
206 if t256 > 256 { t256 = 256 }
207 }
208 let qx: i64 = ax + ex*t256/256
209 let qz: i64 = az + ez*t256/256
210 let dx: i64 = px-qx
211 let dz: i64 = pz-qz
212 return ab_isqrt(dx*dx + dz*dz)
213}
214// Andrew monotone chain over the contact points, integer throughout. n is small (a few contacts), so
215// the insertion sort is the right tool and its cost is invisible.
216func ab_hull(cx: *i64, cz: *i64, n: i64, hx: *i64, hz: *i64) -> i64 {
217 var i: i64 = 1
218 while i < n {
219 let kx: i64 = cx[i]
220 let kz: i64 = cz[i]
221 var j: i64 = i-1
222 var go: i64 = 1
223 while go == 1 {
224 if j < 0 { go = 0 } else {
225 var gt: i64 = 0
226 if cx[j] > kx { gt = 1 }
227 if cx[j] == kx { if cz[j] > kz { gt = 1 } }
228 if gt == 1 { cx[j+1]=cx[j]; cz[j+1]=cz[j]; j=j-1 } else { go = 0 }
229 }
230 }
231 cx[j+1]=kx; cz[j+1]=kz
232 i = i + 1
233 }
234 var k: i64 = 0
235 var a: i64 = 0
236 while a < n {
237 var g2: i64 = 1
238 while g2 == 1 {
239 if k < 2 { g2 = 0 } else {
240 let cr: i64 = (hx[k-1]-hx[k-2])*(cz[a]-hz[k-2]) - (hz[k-1]-hz[k-2])*(cx[a]-hx[k-2])
241 if cr <= 0 { k = k-1 } else { g2 = 0 }
242 }
243 }
244 hx[k]=cx[a]; hz[k]=cz[a]; k=k+1
245 a = a + 1
246 }
247 let lower: i64 = k+1
248 var b: i64 = n-2
249 while b >= 0 {
250 var g3: i64 = 1
251 while g3 == 1 {
252 if k < lower { g3 = 0 } else {
253 let cr: i64 = (hx[k-1]-hx[k-2])*(cz[b]-hz[k-2]) - (hz[k-1]-hz[k-2])*(cx[b]-hx[k-2])
254 if cr <= 0 { k = k-1 } else { g3 = 0 }
255 }
256 }
257 hx[k]=cx[b]; hz[k]=cz[b]; k=k+1
258 b = b - 1
259 }
260 if n == 1 { return 1 }
261 return k-1
262}
263// distance from a point to the hull: 0 if inside. The inside test accepts EITHER winding, so it does
264// not silently depend on the hull builder's orientation.
265func ab_hull_dist(hx: *i64, hz: *i64, h: i64, px: i64, pz: i64) -> i64 {
266 if h <= 1 {
267 let dx: i64 = px-hx[0]
268 let dz: i64 = pz-hz[0]
269 return ab_isqrt(dx*dx + dz*dz)
270 }
271 if h == 2 { return ab_seg_dist(hx[0],hz[0],hx[1],hz[1],px,pz) }
272 var pos: i64 = 0
273 var neg: i64 = 0
274 var i: i64 = 0
275 while i < h {
276 let j: i64 = (i+1)%h
277 let cr: i64 = (hx[j]-hx[i])*(pz-hz[i]) - (hz[j]-hz[i])*(px-hx[i])
278 if cr > 0 { pos = 1 }
279 if cr < 0 { neg = 1 }
280 i = i + 1
281 }
282 if pos == 0 { return 0 }
283 if neg == 0 { return 0 }
284 var best: i64 = 0-1
285 var m: i64 = 0
286 while m < h {
287 let j2: i64 = (m+1)%h
288 let d: i64 = ab_seg_dist(hx[m],hz[m],hx[j2],hz[j2],px,pz)
289 if best < 0 { best = d } else { if d < best { best = d } }
290 m = m + 1
291 }
292 return best
293}
294
295// ---- 3D point-to-segment, the primitive channel 6 is built from.
296// ⚠BOUNDS, stated because nx_twinbench died of exactly this: coordinates are millimetres (~2e3), so
297// differences are ~4e3, squared sums ~5e7, and the largest intermediate (num*256) is ~1.3e10 --
298// six orders of magnitude clear of the i64 ceiling. No magnitude reduction needed here.
299func ab_pt_seg3(ax: i64, ay: i64, az: i64, bx: i64, by: i64, bz: i64,
300 px: i64, py: i64, pz: i64) -> i64 {
301 let ex: i64 = bx-ax
302 let ey: i64 = by-ay
303 let ez: i64 = bz-az
304 let den: i64 = ex*ex + ey*ey + ez*ez
305 var t256: i64 = 0
306 if den > 0 {
307 let num: i64 = (px-ax)*ex + (py-ay)*ey + (pz-az)*ez
308 t256 = num*256/den
309 if t256 < 0 { t256 = 0 }
310 if t256 > 256 { t256 = 256 }
311 }
312 let qx: i64 = ax + ex*t256/256
313 let qy: i64 = ay + ey*t256/256
314 let qz: i64 = az + ez*t256/256
315 let dx: i64 = px-qx
316 let dy: i64 = py-qy
317 let dz: i64 = pz-qz
318 return ab_isqrt(dx*dx + dy*dy + dz*dz)
319}
320// Closest approach between two capsule AXES, by DECLARED SAMPLING along the first one -- the same
321// honesty nx_twinbench applies to its surface query. Sampling can only ever OVERSTATE the distance
322// (i.e. UNDER-report penetration), never invent one, so the error has a known sign and the sample
323// count travels in the output.
324func ab_seg_seg3(ax: i64, ay: i64, az: i64, bx: i64, by: i64, bz: i64,
325 cx: i64, cy: i64, cz: i64, dx: i64, dy: i64, dz: i64) -> i64 {
326 var best: i64 = 0-1
327 var s: i64 = 0
328 while s <= AB_CAPSAMP {
329 let px: i64 = ax + (bx-ax)*s/AB_CAPSAMP
330 let py: i64 = ay + (by-ay)*s/AB_CAPSAMP
331 let pz: i64 = az + (bz-az)*s/AB_CAPSAMP
332 let d: i64 = ab_pt_seg3(cx,cy,cz,dx,dy,dz,px,py,pz)
333 if best < 0 { best = d } else { if d < best { best = d } }
334 s = s + 1
335 }
336 return best
337}
338
339// ---- the six channels ------------------------------------------------------------------------
340// Each returns its raw measurement; the caller prints them with the thresholds that produced them.
341
342// CHANNEL 1 -- GROUND PENETRATION. Literature: distance between the ground and the lowest body vertex
343// BELOW the ground, 5mm tolerance. Ours is over joints (declared substitution above).
344// out[0] = mean depth over violating frames (mm), out[1] = worst depth, out[2] = violating frames
345func ab_gpen(V: *i64, nf: i64, nj: i64, out: *i64) -> i64 {
346 var sum: i64 = 0
347 var worst: i64 = 0
348 var cnt: i64 = 0
349 var f: i64 = 0
350 while f < nf {
351 var lo: i64 = 0
352 var have: i64 = 0
353 var j: i64 = 0
354 while j < nj {
355 let y: i64 = V[ab_vidx(f,j,1)]
356 if have == 0 { lo = y; have = 1 } else { if y < lo { lo = y } }
357 j = j + 1
358 }
359 if have == 1 {
360 let depth: i64 = 0 - lo - AB_GTOL
361 if depth > 0 { sum = sum + depth; cnt = cnt + 1; if depth > worst { worst = depth } }
362 }
363 f = f + 1
364 }
365 out[0] = 0
366 if cnt > 0 { out[0] = sum/cnt }
367 out[1] = worst
368 out[2] = cnt
369 return 0
370}
371
372// CHANNEL 2 -- FLOAT. Frames where nothing is within tolerance of the ground; the reported value is
373// the lowest joint's height above it.
374// ⚠HONEST LIMIT, DECLARED: a genuine airborne phase (a jump) IS floating and this channel cannot tell
375// it from a body hovering. PhyMotion's version additionally requires the body to fail a plausible
376// BALLISTIC trajectory before flagging. We do not model ballistics, so this channel OVER-REPORTS on
377// any motion with real flight. It is evidence, not a verdict, until a ballistic test exists.
378func ab_float(V: *i64, nf: i64, nj: i64, out: *i64) -> i64 {
379 var sum: i64 = 0
380 var worst: i64 = 0
381 var cnt: i64 = 0
382 var f: i64 = 0
383 while f < nf {
384 var lo: i64 = 0
385 var have: i64 = 0
386 var j: i64 = 0
387 while j < nj {
388 let y: i64 = V[ab_vidx(f,j,1)]
389 if have == 0 { lo = y; have = 1 } else { if y < lo { lo = y } }
390 j = j + 1
391 }
392 if have == 1 {
393 let h: i64 = lo - AB_GTOL
394 if h > 0 { sum = sum + h; cnt = cnt + 1; if h > worst { worst = h } }
395 }
396 f = f + 1
397 }
398 out[0] = 0
399 if cnt > 0 { out[0] = sum/cnt }
400 out[1] = worst
401 out[2] = cnt
402 return 0
403}
404
405// CHANNEL 3 -- FOOT SKATE. Literature: for foot joints in contact across two ADJACENT frames, the
406// average HORIZONTAL displacement within those frames. A planted foot must read ~0; a sliding one
407// must read its slide.
408// out[0] = mean mm per contact pair, out[1] = worst, out[2] = number of contact pairs
409func ab_skate(V: *i64, nf: i64, E: *i64, ne: i64, out: *i64) -> i64 {
410 var sum: i64 = 0
411 var worst: i64 = 0
412 var pairs: i64 = 0
413 var f: i64 = 1
414 while f < nf {
415 var e: i64 = 0
416 while e < ne {
417 let j: i64 = E[e]
418 let y0: i64 = V[ab_vidx(f-1,j,1)]
419 let y1: i64 = V[ab_vidx(f,j,1)]
420 if y0 <= AB_CONTACT { if y1 <= AB_CONTACT {
421 let dx: i64 = V[ab_vidx(f,j,0)] - V[ab_vidx(f-1,j,0)]
422 let dz: i64 = V[ab_vidx(f,j,2)] - V[ab_vidx(f-1,j,2)]
423 let d: i64 = ab_isqrt(dx*dx + dz*dz)
424 sum = sum + d
425 pairs = pairs + 1
426 if d > worst { worst = d }
427 } }
428 e = e + 1
429 }
430 f = f + 1
431 }
432 out[0] = 0
433 if pairs > 0 { out[0] = sum/pairs }
434 out[1] = worst
435 out[2] = pairs
436 return 0
437}
438
439// CHANNEL 4 -- JOINT-ANGLE LIMIT VIOLATIONS. The one channel we already owned: nx_skelgen's sg_legal
440// is fail-closed and gate-proven. Here the limits are READ FROM ITS EMITTED FILE, so this can never
441// disagree with the generator.
442//
443// ★★★THE FLEXION ANGLE MUST BE SIGNED, AND THE FIRST VERSION OF THIS FUNCTION WAS NOT. It computed the
444// INTERIOR angle at the joint and called 180-interior the flexion. That is unsigned, and a knee bent
445// sixty degrees forward has EXACTLY the same interior angle as one bent sixty degrees backward -- so
446// the channel was structurally incapable of detecting hyperextension, which is the single violation it
447// exists to catch. The gate's T9 caught it on the first run.
448// ★SAME CLASS AS TWO EARLIER FINDINGS IN THIS LANE -- "a protruding structure cannot be a radius
449// modulation" and "relief modulates a radius and cannot cut a hole": a representation that cannot
450// EXPRESS the thing, not a number that needed tuning. The fix is a representation change, not a
451// threshold change.
452// ★THE SIGN COMES FROM ANATOMY: flexion is rotation about the joint's mediolateral axis, so the sign
453// is the sign of (BA x BC) along that axis. A C row therefore names its flexion axis and which sign of
454// that component counts as positive flexion. For a knee that is the x axis, and the knee sitting
455// POSTERIOR to the hip-ankle line is genu recurvatum -- negative flexion, refused by lo=0.
456// Legal iff lo <= signed_flex <= hi.
457// out[0] = violations per mille of samples, out[1] = worst overshoot in degrees, out[2] = samples
458func ab_jlimit(V: *i64, nf: i64, C: *i64, nc: i64, BLO: *i64, BHI: *i64, t: *i64, out: *i64) -> i64 {
459 var viol: i64 = 0
460 var worst: i64 = 0
461 var n: i64 = 0
462 var f: i64 = 0
463 while f < nf {
464 var c: i64 = 0
465 while c < nc {
466 let jj: i64 = C[c*AB_CST]
467 let pa: i64 = C[c*AB_CST+1]
468 let ch: i64 = C[c*AB_CST+2]
469 let bo: i64 = C[c*AB_CST+3]
470 let axis: i64 = C[c*AB_CST+4]
471 let posw: i64 = C[c*AB_CST+5]
472 let ax: i64 = V[ab_vidx(f,pa,0)] - V[ab_vidx(f,jj,0)]
473 let ay: i64 = V[ab_vidx(f,pa,1)] - V[ab_vidx(f,jj,1)]
474 let az: i64 = V[ab_vidx(f,pa,2)] - V[ab_vidx(f,jj,2)]
475 let bx: i64 = V[ab_vidx(f,ch,0)] - V[ab_vidx(f,jj,0)]
476 let by: i64 = V[ab_vidx(f,ch,1)] - V[ab_vidx(f,jj,1)]
477 let bz: i64 = V[ab_vidx(f,ch,2)] - V[ab_vidx(f,jj,2)]
478 let la: i64 = ab_isqrt(ax*ax+ay*ay+az*az)
479 let lb: i64 = ab_isqrt(bx*bx+by*by+bz*bz)
480 if la > 0 { if lb > 0 {
481 let dot: i64 = ax*bx + ay*by + az*bz
482 var cv: i64 = dot*AB_SCALE/(la*lb)
483 if cv > AB_SCALE { cv = AB_SCALE }
484 if cv < 0-AB_SCALE { cv = 0-AB_SCALE }
485 let interior: i64 = ab_acos(cv, t)
486 // ★the signed part: which way did it bend? Cross product about the flexion axis.
487 var comp: i64 = 0
488 if axis == 0 { comp = ay*bz - az*by }
489 if axis == 1 { comp = az*bx - ax*bz }
490 if axis == 2 { comp = ax*by - ay*bx }
491 var sgn: i64 = 1
492 if comp*posw < 0 { sgn = 0-1 }
493 let flex: i64 = sgn * (AB_DEG - interior)
494 n = n + 1
495 var over: i64 = 0
496 if flex < BLO[bo] { over = BLO[bo] - flex }
497 if flex > BHI[bo] { over = flex - BHI[bo] }
498 if over > 0 { viol = viol + 1; if over > worst { worst = over } }
499 } }
500 c = c + 1
501 }
502 f = f + 1
503 }
504 out[0] = 0
505 if n > 0 { out[0] = viol*AB_PERMIL/n }
506 out[1] = worst
507 out[2] = n
508 return 0
509}
510
511// CHANNEL 5 -- TEMPORAL SMOOTHNESS (jitter). Mean magnitude of the discrete THIRD difference (jerk)
512// over every joint and frame, in mm per frame cubed. Needs four consecutive frames.
513// out[0] = mean jerk, out[1] = worst, out[2] = samples
514func ab_smooth(V: *i64, nf: i64, nj: i64, out: *i64) -> i64 {
515 var sum: i64 = 0
516 var worst: i64 = 0
517 var n: i64 = 0
518 var f: i64 = 3
519 while f < nf {
520 var j: i64 = 0
521 while j < nj {
522 var a: i64 = 0
523 var acc: i64 = 0
524 while a < 3 {
525 // third difference: p[f] - 3p[f-1] + 3p[f-2] - p[f-3]
526 let d: i64 = V[ab_vidx(f,j,a)] - 3*V[ab_vidx(f-1,j,a)] + 3*V[ab_vidx(f-2,j,a)] - V[ab_vidx(f-3,j,a)]
527 acc = acc + d*d
528 a = a + 1
529 }
530 let m: i64 = ab_isqrt(acc)
531 sum = sum + m
532 n = n + 1
533 if m > worst { worst = m }
534 j = j + 1
535 }
536 f = f + 1
537 }
538 out[0] = 0
539 if n > 0 { out[0] = sum/n }
540 out[1] = worst
541 out[2] = n
542 return 0
543}
544
545// CHANNEL 6 -- CENTRE-OF-MASS STABILITY. PhyMotion: the fraction of frames where the projected centre
546// of mass falls outside the SUPPORT POLYGON of the contacting feet.
547// ⚠TWO DECLARED APPROXIMATIONS: (a) our CoM is the arithmetic mean of joint positions, NOT a
548// mass-weighted sum of segment centroids -- we have no segment masses, and inventing them would be a
549// magic number; (b) with one or two contacts the "polygon" is a point or a segment, so support is
550// taken as that primitive dilated by AB_FOOTHALF. Both travel in the output.
551// out[0] = unstable frames per mille of SUPPORTED frames, out[1] = worst excess mm, out[2] = supported frames
552func ab_com(V: *i64, nf: i64, nj: i64, E: *i64, ne: i64, out: *i64) -> i64 {
553 var bad: i64 = 0
554 var worst: i64 = 0
555 var sup: i64 = 0
556 let px: *i64 = sys_mmap(AB_MAXJ*8) as *i64
557 let pz: *i64 = sys_mmap(AB_MAXJ*8) as *i64
558 let hx: *i64 = sys_mmap(AB_MAXJ*4*8) as *i64
559 let hz: *i64 = sys_mmap(AB_MAXJ*4*8) as *i64
560 var f: i64 = 0
561 while f < nf {
562 var sx: i64 = 0
563 var sz: i64 = 0
564 var j: i64 = 0
565 while j < nj { sx = sx + V[ab_vidx(f,j,0)]; sz = sz + V[ab_vidx(f,j,2)]; j = j + 1 }
566 if nj > 0 {
567 let cx: i64 = sx/nj
568 let cz: i64 = sz/nj
569 // gather EVERY contacting foot point -- all of them, not the first two
570 var nct: i64 = 0
571 var e: i64 = 0
572 while e < ne {
573 let jf: i64 = E[e]
574 if V[ab_vidx(f,jf,1)] <= AB_CONTACT {
575 px[nct] = V[ab_vidx(f,jf,0)]
576 pz[nct] = V[ab_vidx(f,jf,2)]
577 nct = nct + 1
578 }
579 e = e + 1
580 }
581 if nct > 0 {
582 sup = sup + 1
583 let h: i64 = ab_hull(px, pz, nct, hx, hz)
584 let dist: i64 = ab_hull_dist(hx, hz, h, cx, cz)
585 let ex2: i64 = dist - AB_FOOTHALF
586 if ex2 > 0 { bad = bad + 1; if ex2 > worst { worst = ex2 } }
587 }
588 }
589 f = f + 1
590 }
591 out[0] = 0
592 if sup > 0 { out[0] = bad*AB_PERMIL/sup }
593 out[1] = worst
594 out[2] = sup
595 return 0
596}
597
598// CHANNEL 6 -- SELF-PENETRATION. An arm through the torso. PHYSSCORE measures this on the MESH; we are
599// fed a joint trace, so limbs are represented by CAPSULE PROXIES supplied as data (`S <j0> <j1> <r>`).
600// ★★RADII ARE NOT A CONSTANT IN THIS FILE. There is no defensible universal limb radius, so rather
601// than bury one (rule 11) the caller supplies it and the organ reports that it did. With NO S rows the
602// channel REFUSES rather than returning a clean 0 -- coverage then honestly reads 5 of 6, not 6.
603// ★★★ADJACENCY IS DERIVED, NOT DECLARED. Two capsules sharing a joint MEET there by construction, so
604// every adjacent pair would read as penetrating and every body would score as broken. The shared-joint
605// test is read off the S rows themselves -- there is no second adjacency table to drift from, the same
606// discipline channel 4 uses for its limits. T18 is the tooth that holds this.
607// two joints are LINKED if they are the same joint or one is the other's parent -- the articulated
608// equivalent of "these two limbs meet here, and meeting is not penetrating"
609func ab_linked(a: i64, b: i64, JP: *i64) -> i64 {
610 if a == b { return 1 }
611 if a >= 0 { if a < AB_MAXJ { if JP[a] == b { return 1 } } }
612 if b >= 0 { if b < AB_MAXJ { if JP[b] == a { return 1 } } }
613 return 0
614}
615// out[0]=mean depth over violations, out[1]=worst depth, out[2]=violations, out[3]=pairs tested
616func ab_selfpen(V: *i64, nf: i64, S: *i64, ns: i64, JP: *i64, out: *i64) -> i64 {
617 var sum: i64 = 0
618 var worst: i64 = 0
619 var viol: i64 = 0
620 var pairs: i64 = 0
621 var f: i64 = 0
622 while f < nf {
623 var i: i64 = 0
624 while i < ns {
625 var j: i64 = i+1
626 while j < ns {
627 let a0: i64 = S[i*AB_SST]
628 let a1: i64 = S[i*AB_SST+1]
629 let b0: i64 = S[j*AB_SST]
630 let b1: i64 = S[j*AB_SST+1]
631 // ★★DERIVED ADJACENCY, AND THE FIRST VERSION WAS TOO WEAK. Sharing an ENDPOINT is not
632 // the only way two limbs legitimately meet: a thigh's proximal joint is a CHILD of the
633 // torso's, so a femoral head sits inside the pelvic volume with no shared index at
634 // all. Measured on the real skeleton, that false-positived the torso against both
635 // thighs (111mm) and both arms. So adjacency reads the PARENT CHAIN out of the J rows
636 // nx_skelgen already emits -- still derived, still no second table.
637 var adj: i64 = 0
638 if ab_linked(a0,b0,JP) == 1 { adj = 1 }
639 if ab_linked(a0,b1,JP) == 1 { adj = 1 }
640 if ab_linked(a1,b0,JP) == 1 { adj = 1 }
641 if ab_linked(a1,b1,JP) == 1 { adj = 1 }
642 if adj == 0 {
643 pairs = pairs + 1
644 let d: i64 = ab_seg_seg3(
645 V[ab_vidx(f,a0,0)], V[ab_vidx(f,a0,1)], V[ab_vidx(f,a0,2)],
646 V[ab_vidx(f,a1,0)], V[ab_vidx(f,a1,1)], V[ab_vidx(f,a1,2)],
647 V[ab_vidx(f,b0,0)], V[ab_vidx(f,b0,1)], V[ab_vidx(f,b0,2)],
648 V[ab_vidx(f,b1,0)], V[ab_vidx(f,b1,1)], V[ab_vidx(f,b1,2)])
649 let depth: i64 = S[i*AB_SST+2] + S[j*AB_SST+2] - d
650 if depth > 0 {
651 sum = sum + depth
652 viol = viol + 1
653 if depth > worst { worst = depth }
654 }
655 }
656 j = j + 1
657 }
658 i = i + 1
659 }
660 f = f + 1
661 }
662 out[0] = 0
663 if viol > 0 { out[0] = sum/viol }
664 out[1] = worst
665 out[2] = viol
666 out[3] = pairs
667 return 0
668}
669
670// ---- loaders ---------------------------------------------------------------------------------
671// skeleton: consume nx_skelgen's own "B <bone> <len> <lo> <hi>" rows. rc = bones read, <0 = refused.
672func ab_load_skel(path: *u8, BLO: *i64, BHI: *i64, JP: *i64) -> i64 {
673 let ln: *i64 = sys_mmap(16) as *i64
674 let buf: *u8 = sys_read_file(path, ln)
675 if buf as i64 == 0 { return 0-1 }
676 let n: i64 = ln[0]
677 let p: *i64 = sys_mmap(16) as *i64
678 let v: *i64 = sys_mmap(16) as *i64
679 p[0] = 0
680 var z: i64 = 0
681 while z < AB_MAXJ { JP[z] = 0-1; z = z + 1 }
682 var cnt: i64 = 0
683 var go: i64 = 1
684 while go == 1 {
685 if p[0] >= n { go = 0 } else {
686 let c: i64 = buf[p[0]] as i64
687 if c == 66 {
688 p[0] = p[0] + 1
689 if ab_tok(buf,n,p,v) == 1 {
690 let b: i64 = v[0]
691 if ab_tok(buf,n,p,v) == 1 {
692 if ab_tok(buf,n,p,v) == 1 {
693 let lo: i64 = v[0]
694 if ab_tok(buf,n,p,v) == 1 {
695 if b >= 0 { if b < AB_MAXB { BLO[b]=lo; BHI[b]=v[0]; cnt=cnt+1 } }
696 }
697 }
698 }
699 }
700 }
701 // J <idx> <parent> <side> <x> <y> <z> -- only the parent column is needed here
702 if c == 74 {
703 p[0] = p[0] + 1
704 if ab_tok(buf,n,p,v) == 1 {
705 let jid: i64 = v[0]
706 if ab_tok(buf,n,p,v) == 1 {
707 if jid >= 0 { if jid < AB_MAXJ { JP[jid] = v[0] } }
708 }
709 }
710 }
711 ab_nextline(buf,n,p)
712 }
713 }
714 return cnt
715}
716// trace. cfg[0]=nframes cfg[1]=njoints cfg[2]=nfeet cfg[3]=nangles. rc 0 ok, else refusal code.
717func ab_load_trace(path: *u8, V: *i64, E: *i64, C: *i64, S: *i64, cfg: *i64) -> i64 {
718 let ln: *i64 = sys_mmap(16) as *i64
719 let buf: *u8 = sys_read_file(path, ln)
720 if buf as i64 == 0 { return AB_E_OPEN }
721 let n: i64 = ln[0]
722 let p: *i64 = sys_mmap(16) as *i64
723 let v: *i64 = sys_mmap(16) as *i64
724 p[0] = 0
725 cfg[0]=0; cfg[1]=0; cfg[2]=0; cfg[3]=0; cfg[4]=0
726 var rc: i64 = 0
727 var go: i64 = 1
728 while go == 1 {
729 if p[0] >= n { go = 0 } else {
730 let c: i64 = buf[p[0]] as i64
731 if c == 65 {
732 p[0] = p[0] + 1
733 if ab_tok(buf,n,p,v) == 1 {
734 let nf: i64 = v[0]
735 if ab_tok(buf,n,p,v) == 1 {
736 let nj: i64 = v[0]
737 // ★REFUSE LOUD rather than truncate -- the whole point of the capacity law
738 if nf > AB_MAXF { rc = AB_E_CAP; go = 0 }
739 if nj > AB_MAXJ { rc = AB_E_CAP; go = 0 }
740 if rc == 0 { cfg[0]=nf; cfg[1]=nj }
741 }
742 }
743 }
744 if c == 69 {
745 p[0] = p[0] + 1
746 if ab_tok(buf,n,p,v) == 1 {
747 if cfg[2] < AB_MAXJ { E[cfg[2]] = v[0]; cfg[2] = cfg[2]+1 }
748 }
749 }
750 if c == 67 {
751 p[0] = p[0] + 1
752 if cfg[3] < AB_MAXC {
753 let k: i64 = cfg[3]*AB_CST
754 var okc: i64 = 1
755 if ab_tok(buf,n,p,v) == 1 { C[k]=v[0] } else { okc = 0 }
756 if ab_tok(buf,n,p,v) == 1 { C[k+1]=v[0] } else { okc = 0 }
757 if ab_tok(buf,n,p,v) == 1 { C[k+2]=v[0] } else { okc = 0 }
758 if ab_tok(buf,n,p,v) == 1 { C[k+3]=v[0] } else { okc = 0 }
759 if ab_tok(buf,n,p,v) == 1 { C[k+4]=v[0] } else { okc = 0 }
760 if ab_tok(buf,n,p,v) == 1 { C[k+5]=v[0] } else { okc = 0 }
761 if okc == 1 { cfg[3] = cfg[3]+1 }
762 }
763 }
764 if c == 83 {
765 p[0] = p[0] + 1
766 if cfg[4] < AB_MAXS {
767 let k: i64 = cfg[4]*AB_SST
768 var oks: i64 = 1
769 if ab_tok(buf,n,p,v) == 1 { S[k]=v[0] } else { oks = 0 }
770 if ab_tok(buf,n,p,v) == 1 { S[k+1]=v[0] } else { oks = 0 }
771 if ab_tok(buf,n,p,v) == 1 { S[k+2]=v[0] } else { oks = 0 }
772 if oks == 1 { cfg[4] = cfg[4]+1 }
773 }
774 }
775 if c == 86 {
776 p[0] = p[0] + 1
777 var f: i64 = 0
778 var j: i64 = 0
779 var okv: i64 = 1
780 if ab_tok(buf,n,p,v) == 1 { f=v[0] } else { okv = 0 }
781 if ab_tok(buf,n,p,v) == 1 { j=v[0] } else { okv = 0 }
782 if okv == 1 {
783 if f >= 0 { if f < AB_MAXF { if j >= 0 { if j < AB_MAXJ {
784 if ab_tok(buf,n,p,v) == 1 { V[ab_vidx(f,j,0)] = v[0] }
785 if ab_tok(buf,n,p,v) == 1 { V[ab_vidx(f,j,1)] = v[0] }
786 if ab_tok(buf,n,p,v) == 1 { V[ab_vidx(f,j,2)] = v[0] }
787 } } } }
788 }
789 }
790 if go == 1 { ab_nextline(buf,n,p) }
791 }
792 }
793 if rc == 0 { if cfg[0] <= 0 { rc = AB_E_EMPTY } }
794 return rc
795}
796
797func ab_vbytes() -> i64 { return AB_MAXF*AB_MAXJ*3*8 }
798
799// ---- the report ------------------------------------------------------------------------------
800func ab_score(tpath: *u8, spath: *u8) -> i64 {
801 let V: *i64 = sys_mmap(ab_vbytes()) as *i64
802 let E: *i64 = sys_mmap(AB_MAXJ*8) as *i64
803 let C: *i64 = sys_mmap(AB_MAXC*AB_CST*8) as *i64
804 let S: *i64 = sys_mmap(AB_MAXS*AB_SST*8) as *i64
805 let cfg: *i64 = sys_mmap(64) as *i64
806 let BLO: *i64 = sys_mmap(AB_MAXB*8) as *i64
807 let BHI: *i64 = sys_mmap(AB_MAXB*8) as *i64
808 let JP: *i64 = sys_mmap(AB_MAXJ*8) as *i64
809 let rc: i64 = ab_load_trace(tpath, V, E, C, S, cfg)
810 if rc != 0 {
811 ab_puts("{\x22organ\x22:\x22nx_actbench\x22,\x22action\x22:\x22REFUSED\x22,\x22rc\x22:" as *u8); ab_pn(rc)
812 ab_puts(",\x22why\x22:\x22trace unreadable, empty, or beyond declared capacity -- refused loudly rather than truncated\x22}\n" as *u8)
813 return rc
814 }
815 let nb: i64 = ab_load_skel(spath, BLO, BHI, JP)
816 let t: *i64 = ab_costab()
817 let o1: *i64 = sys_mmap(64) as *i64
818 let o2: *i64 = sys_mmap(64) as *i64
819 let o3: *i64 = sys_mmap(64) as *i64
820 let o4: *i64 = sys_mmap(64) as *i64
821 let o5: *i64 = sys_mmap(64) as *i64
822 let o6: *i64 = sys_mmap(64) as *i64
823 ab_gpen(V, cfg[0], cfg[1], o1)
824 ab_float(V, cfg[0], cfg[1], o2)
825 ab_skate(V, cfg[0], E, cfg[2], o3)
826 ab_jlimit(V, cfg[0], C, cfg[3], BLO, BHI, t, o4)
827 ab_smooth(V, cfg[0], cfg[1], o5)
828 ab_com(V, cfg[0], cfg[1], E, cfg[2], o6)
829 ab_puts("{\x22organ\x22:\x22nx_actbench\x22,\x22v\x22:1,\x22standard\x22:\x22PHYSSCORE 6-channel composite (PhysHuman @ CVPR 2026); definitions from the PhysDiff/EDGE lineage + PhyMotion arXiv:2605.14269\x22" as *u8)
830 ab_puts(",\x22frames\x22:" as *u8); ab_pn(cfg[0])
831 ab_puts(",\x22joints\x22:" as *u8); ab_pn(cfg[1])
832 ab_puts(",\x22feet\x22:" as *u8); ab_pn(cfg[2])
833 ab_puts(",\x22angle_sites\x22:" as *u8); ab_pn(cfg[3])
834 ab_puts(",\x22skel_bones\x22:" as *u8); ab_pn(nb)
835 ab_puts(",\x22ground_penetration\x22:{\x22mean_mm\x22:" as *u8); ab_pn(o1[0])
836 ab_puts(",\x22worst_mm\x22:" as *u8); ab_pn(o1[1])
837 ab_puts(",\x22frames\x22:" as *u8); ab_pn(o1[2]); ab_puts("}" as *u8)
838 ab_puts(",\x22float\x22:{\x22mean_mm\x22:" as *u8); ab_pn(o2[0])
839 ab_puts(",\x22worst_mm\x22:" as *u8); ab_pn(o2[1])
840 ab_puts(",\x22frames\x22:" as *u8); ab_pn(o2[2])
841 ab_puts(",\x22caveat\x22:\x22no ballistic test -- a genuine airborne phase is indistinguishable from hovering, so this OVER-REPORTS on motion with real flight\x22}" as *u8)
842 ab_puts(",\x22foot_skate\x22:{\x22mean_mm_per_contact_pair\x22:" as *u8); ab_pn(o3[0])
843 ab_puts(",\x22worst_mm\x22:" as *u8); ab_pn(o3[1])
844 ab_puts(",\x22contact_pairs\x22:" as *u8); ab_pn(o3[2]); ab_puts("}" as *u8)
845 ab_puts(",\x22joint_limit\x22:{\x22violations_permil\x22:" as *u8); ab_pn(o4[0])
846 ab_puts(",\x22worst_overshoot_deg\x22:" as *u8); ab_pn(o4[1])
847 ab_puts(",\x22samples\x22:" as *u8); ab_pn(o4[2])
848 ab_puts(",\x22limits_from\x22:\x22the B rows nx_skelgen emitted -- there is no second constraint table to drift from\x22}" as *u8)
849 ab_puts(",\x22temporal_smoothness\x22:{\x22mean_jerk_mm\x22:" as *u8); ab_pn(o5[0])
850 ab_puts(",\x22worst_jerk_mm\x22:" as *u8); ab_pn(o5[1])
851 ab_puts(",\x22samples\x22:" as *u8); ab_pn(o5[2]); ab_puts("}" as *u8)
852 ab_puts(",\x22com_stability\x22:{\x22unstable_permil\x22:" as *u8); ab_pn(o6[0])
853 ab_puts(",\x22worst_excess_mm\x22:" as *u8); ab_pn(o6[1])
854 ab_puts(",\x22supported_frames\x22:" as *u8); ab_pn(o6[2]); ab_puts("}" as *u8)
855 // ★CHANNEL 6 MEASURES ONLY IF THE CALLER SUPPLIED CAPSULE PROXIES. With none, it REFUSES rather
856 // than returning a clean 0 -- "no self-penetration found" for a test that never ran is the exact
857 // failure mode this program keeps hitting -- and the coverage line then says 5 of 6 out loud.
858 var nchan: i64 = 5
859 if cfg[4] > 0 {
860 nchan = 6
861 let o7: *i64 = sys_mmap(64) as *i64
862 ab_selfpen(V, cfg[0], S, cfg[4], JP, o7)
863 ab_puts(",\x22self_penetration\x22:{\x22mean_depth_mm\x22:" as *u8); ab_pn(o7[0])
864 ab_puts(",\x22worst_depth_mm\x22:" as *u8); ab_pn(o7[1])
865 ab_puts(",\x22violations\x22:" as *u8); ab_pn(o7[2])
866 ab_puts(",\x22pairs_tested\x22:" as *u8); ab_pn(o7[3])
867 ab_puts(",\x22capsules\x22:" as *u8); ab_pn(cfg[4])
868 ab_puts(",\x22axis_samples\x22:" as *u8); ab_pn(AB_CAPSAMP)
869 ab_puts(",\x22method\x22:\x22capsule limb proxies supplied as data, NOT a radius baked into this organ; adjacency DERIVED from shared joints so limbs that legitimately meet are never counted; sampled closest approach can only UNDER-report depth, never invent it\x22}" as *u8)
870 } else {
871 ab_puts(",\x22self_penetration\x22:{\x22status\x22:\x22ABSENT-REFUSED\x22,\x22why\x22:\x22no capsule proxies (S rows) supplied -- returning 0 would be a clean number for a test that never ran\x22}" as *u8)
872 }
873 ab_puts(",\x22channels_measured\x22:" as *u8); ab_pn(nchan)
874 ab_puts(",\x22channels_total\x22:6" as *u8)
875 ab_puts(",\x22thresholds\x22:{\x22ground_tol_mm\x22:" as *u8); ab_pn(AB_GTOL)
876 ab_puts(",\x22contact_height_mm\x22:" as *u8); ab_pn(AB_CONTACT)
877 ab_puts(",\x22foot_half_mm\x22:" as *u8); ab_pn(AB_FOOTHALF); ab_puts("}" as *u8)
878 ab_puts(",\x22declared_substitutions\x22:\x22penetration and float are measured to the lowest JOINT, not the lowest mesh vertex as the literature specifies, so penetration under-reports and float over-reports by roughly a limb radius; the centre of mass is an unweighted mean of joints, not a mass-weighted sum of segment centroids\x22" as *u8)
879 ab_puts(",\x22comparability\x22:\x22the field states plainly that there is NO standardised parameter set for these metrics, so these numbers are comparable only against runs quoting the same thresholds above\x22}\n" as *u8)
880 return 0
881}
882
883// ---- fixtures + gate -------------------------------------------------------------------------
884// Fixtures are WRITTEN, not asserted about: a tooth that reads a file the gate authored is a fixture
885// test, and the lane's law is that the decoy must be the thing that actually fools it.
886func ab_wr(path: *u8, b: *u8, n: i64) -> i64 {
887 let fd: i64 = sys_openat_wr(path, AB_MODE)
888 if fd < 0 { return 0-1 }
889 sys_write(fd, b, n)
890 sys_close(fd)
891 return n
892}
893func ab_app(b: *u8, p: *i64, s: *u8) -> i64 {
894 var i: i64 = 0
895 while s[i] != (0 as u8) { b[p[0]] = s[i]; p[0] = p[0]+1; i = i+1 }
896 return 0
897}
898func ab_appn(b: *u8, p: *i64, v: i64) -> i64 {
899 var x: i64 = v
900 if x < 0 { b[p[0]]=45 as u8; p[0]=p[0]+1; x=0-x }
901 let t: *u8 = sys_mmap(32)
902 var k: i64 = 0
903 if x == 0 { t[0]=48 as u8; k=1 }
904 while x > 0 { t[k]=(48+x%10) as u8; x=x/10; k=k+1 }
905 var q: i64 = k-1
906 while q >= 0 { b[p[0]]=t[q]; p[0]=p[0]+1; q=q-1 }
907 b[p[0]]=32 as u8; p[0]=p[0]+1
908 return 0
909}
910func ab_v(b: *u8, p: *i64, f: i64, j: i64, x: i64, y: i64, z: i64) -> i64 {
911 ab_app(b,p,"V " as *u8); ab_appn(b,p,f); ab_appn(b,p,j); ab_appn(b,p,x); ab_appn(b,p,y); ab_appn(b,p,z)
912 ab_app(b,p,"\n" as *u8)
913 return 0
914}
915// A 3-joint leg: joint0 hip, joint1 knee, joint2 foot, built in the SAGITTAL plane (+y up, +z
916// forward) because that is the plane a knee actually flexes in -- the first fixture displaced the
917// knee LATERALLY, which is not a knee motion at all and is half of why T9 was meaningless.
918// `slide` moves the foot horizontally per frame; `sink` drops it below ground; `jitter` alternates
919// the knee height; `kneez` displaces the knee along the sagittal axis:
920// kneez > 0 knee ANTERIOR to the hip-ankle line = normal flexion (a squat) -> LEGAL
921// kneez < 0 knee POSTERIOR to that line = genu recurvatum = HYPEREXTENSION -> REFUSED by lo=0
922func ab_fixture(path: *u8, nf: i64, slide: i64, sink: i64, kneez: i64, jitter: i64) -> i64 {
923 let b: *u8 = sys_mmap(AB_MAGIC_65536)
924 let p: *i64 = sys_mmap(16) as *i64
925 p[0] = 0
926 ab_app(b,p,"A " as *u8); ab_appn(b,p,nf); ab_appn(b,p,3); ab_app(b,p,"\n" as *u8)
927 ab_app(b,p,"E 2\n" as *u8)
928 // angle site: at the knee (1) between hip (0) and foot (2); limits from bone 9 (the knee's range);
929 // flexion axis 0 = x (mediolateral); positive flexion is the NEGATIVE x cross-component, which is
930 // what a knee-forward bend produces in this frame.
931 ab_app(b,p,"C " as *u8); ab_appn(b,p,1); ab_appn(b,p,0); ab_appn(b,p,2); ab_appn(b,p,9)
932 ab_appn(b,p,0); ab_appn(b,p,0-1); ab_app(b,p,"\n" as *u8)
933 var f: i64 = 0
934 while f < nf {
935 var jy: i64 = 0
936 if jitter > 0 { if f%2 == 1 { jy = jitter } }
937 ab_v(b,p,f,0, 0, 900, 0)
938 ab_v(b,p,f,1, 0, 450 + jy, kneez)
939 ab_v(b,p,f,2, slide*f, 0 - sink, 0)
940 f = f + 1
941 }
942 return ab_wr(path, b, p[0])
943}
944func ab_gate() -> i64 {
945 let ctr: *i64 = gv_ctr()
946 gv_head("nx_actbench selftest -- the PHYSSCORE channels, measured not invented" as *u8)
947 let t: *i64 = ab_costab()
948 // ★T1 THE COSINE TABLE IS A KAT. It is built by recurrence rather than typed, so it must be
949 // checked against values that are known exactly, or 180 steps of drift go unnoticed.
950 var t1: i64 = 0
951 let c0: i64 = t[0]
952 let c60: i64 = t[60]
953 let c90: i64 = t[90]
954 let c180: i64 = t[180]
955 if c0 == AB_SCALE { if ab_iabs(c60-512) <= 2 { if ab_iabs(c90) <= 2 { if ab_iabs(c180+AB_SCALE) <= 2 { t1 = 1 } } } }
956 gv_check("T1 cosine table KAT: cos0=1024, cos60=512, cos90=0, cos180=-1024" as *u8, t1, ctr)
957 // ★T2 acos round-trips through the same table it was built from
958 var t2: i64 = 0
959 if ab_acos(AB_SCALE, t) == 0 { if ab_iabs(ab_acos(512,t)-60) <= 1 { if ab_iabs(ab_acos(0,t)-90) <= 1 { t2 = 1 } } }
960 gv_check("T2 acos recovers the angle it was given" as *u8, t2, ctr)
961 // build a skeleton with the REAL generator's row format so the limit channel is exercised as shipped
962 let sb: *u8 = sys_mmap(AB_MAGIC_4096)
963 let sp: *i64 = sys_mmap(16) as *i64
964 sp[0] = 0
965 // B <bone> <len> <lo> <hi> -- bone 9 is the knee: 0..140, CANNOT hyperextend (nx_skelgen's table)
966 ab_app(sb,sp,"B " as *u8); ab_appn(sb,sp,9); ab_appn(sb,sp,430); ab_appn(sb,sp,0); ab_appn(sb,sp,140)
967 ab_app(sb,sp,"\n" as *u8)
968 ab_wr("/tmp/nx_ab_skel.dat" as *u8, sb, sp[0])
969 let BLO: *i64 = sys_mmap(AB_MAXB*8) as *i64
970 let BHI: *i64 = sys_mmap(AB_MAXB*8) as *i64
971 let JP: *i64 = sys_mmap(AB_MAXJ*8) as *i64
972 var t3: i64 = 0
973 if ab_load_skel("/tmp/nx_ab_skel.dat" as *u8, BLO, BHI, JP) == 1 { if BLO[9]==0 { if BHI[9]==140 { t3 = 1 } } }
974 gv_check("T3 limits are READ from nx_skelgen's own B rows, not restated here" as *u8, t3, ctr)
975 let V: *i64 = sys_mmap(ab_vbytes()) as *i64
976 let E: *i64 = sys_mmap(AB_MAXJ*8) as *i64
977 let C: *i64 = sys_mmap(AB_MAXC*AB_CST*8) as *i64
978 let S: *i64 = sys_mmap(AB_MAXS*AB_SST*8) as *i64
979 let cfg: *i64 = sys_mmap(64) as *i64
980 let o: *i64 = sys_mmap(64) as *i64
981 // ---- PLANTED foot, straight leg, on the ground, no jitter
982 ab_fixture("/tmp/nx_ab_plant.tr" as *u8, 8, 0, 0, 0, 0)
983 ab_load_trace("/tmp/nx_ab_plant.tr" as *u8, V, E, C, S, cfg)
984 ab_skate(V, cfg[0], E, cfg[2], o)
985 var t4: i64 = 0
986 if o[0] == 0 { t4 = 1 }
987 gv_check("T4 a PLANTED foot reads skate 0" as *u8, t4, ctr)
988 let plant_skate: i64 = o[0]
989 // ---- SLIDING foot: the decoy. This is the one that has to separate.
990 ab_fixture("/tmp/nx_ab_slide.tr" as *u8, 8, 12, 0, 0, 0)
991 ab_load_trace("/tmp/nx_ab_slide.tr" as *u8, V, E, C, S, cfg)
992 ab_skate(V, cfg[0], E, cfg[2], o)
993 var t5: i64 = 0
994 if o[0] > plant_skate { if o[0] >= 12 { t5 = 1 } }
995 gv_check("T5 ANTI-VACUITY: a SLIDING foot reads its slide, strictly above the planted one" as *u8, t5, ctr)
996 // ---- penetration pair
997 ab_load_trace("/tmp/nx_ab_plant.tr" as *u8, V, E, C, S, cfg)
998 ab_gpen(V, cfg[0], cfg[1], o)
999 var t6: i64 = 0
1000 if o[0] == 0 { if o[2] == 0 { t6 = 1 } }
1001 gv_check("T6 a foot ON the ground reports NO penetration" as *u8, t6, ctr)
1002 ab_fixture("/tmp/nx_ab_sink.tr" as *u8, 8, 0, 50, 0, 0)
1003 ab_load_trace("/tmp/nx_ab_sink.tr" as *u8, V, E, C, S, cfg)
1004 ab_gpen(V, cfg[0], cfg[1], o)
1005 var t7: i64 = 0
1006 if o[1] >= 40 { if o[2] == 8 { t7 = 1 } }
1007 gv_check("T7 a foot 50mm BELOW the ground reports it, on every frame" as *u8, t7, ctr)
1008 // ---- ★★THE JOINT-LIMIT PAIR. nx_skelgen's own law: a tooth that only proves refusal is worthless,
1009 // because "refuse everything" passes it. Both directions or neither.
1010 // knee x-offset 0 => hip, knee and foot colinear => interior 180 => flexion 0 => LEGAL (lo=0).
1011 ab_load_trace("/tmp/nx_ab_plant.tr" as *u8, V, E, C, S, cfg)
1012 ab_jlimit(V, cfg[0], C, cfg[3], BLO, BHI, t, o)
1013 var t8: i64 = 0
1014 if o[0] == 0 { if o[2] > 0 { t8 = 1 } }
1015 gv_check("T8 a STRAIGHT leg is legal, and the channel actually sampled it" as *u8, t8, ctr)
1016 // ★T9a a NORMAL bend (knee anterior = a squat) must stay LEGAL. Without this tooth an
1017 // implementation that flags every bent knee passes T9b, which is the "refuse everything" hole
1018 // nx_skelgen's own gate documents.
1019 ab_fixture("/tmp/nx_ab_flex.tr" as *u8, 8, 0, 0, 260, 0)
1020 ab_load_trace("/tmp/nx_ab_flex.tr" as *u8, V, E, C, S, cfg)
1021 ab_jlimit(V, cfg[0], C, cfg[3], BLO, BHI, t, o)
1022 var t9a: i64 = 0
1023 if o[0] == 0 { if o[2] > 0 { t9a = 1 } }
1024 gv_check("T9a a NORMAL knee bend (anterior, a squat) stays legal" as *u8, t9a, ctr)
1025 // ★★T9b THE REFUTATION, and the tooth that convicted the unsigned first implementation: the knee
1026 // POSTERIOR to the hip-ankle line is genu recurvatum. Its INTERIOR angle is identical to T9a's, so
1027 // only a SIGNED flexion can tell them apart -- if this passes while T9a also passes, the sign works.
1028 ab_fixture("/tmp/nx_ab_hyper.tr" as *u8, 8, 0, 0, 0-260, 0)
1029 ab_load_trace("/tmp/nx_ab_hyper.tr" as *u8, V, E, C, S, cfg)
1030 ab_jlimit(V, cfg[0], C, cfg[3], BLO, BHI, t, o)
1031 var t9: i64 = 0
1032 if o[0] > 0 { if o[1] > 0 { t9 = 1 } }
1033 gv_check("T9b REFUTATION: a HYPEREXTENDED knee is caught -- same interior angle as T9a, opposite sign" as *u8, t9, ctr)
1034 // ---- smoothness pair
1035 ab_load_trace("/tmp/nx_ab_plant.tr" as *u8, V, E, C, S, cfg)
1036 ab_smooth(V, cfg[0], cfg[1], o)
1037 let smooth_still: i64 = o[0]
1038 ab_fixture("/tmp/nx_ab_jit.tr" as *u8, 8, 0, 0, 0, 30)
1039 ab_load_trace("/tmp/nx_ab_jit.tr" as *u8, V, E, C, S, cfg)
1040 ab_smooth(V, cfg[0], cfg[1], o)
1041 var t10: i64 = 0
1042 if smooth_still == 0 { if o[0] > 0 { t10 = 1 } }
1043 gv_check("T10 jitter SEPARATES: a still trace reads 0 jerk, an alternating one reads more" as *u8, t10, ctr)
1044 // ---- CoM pair. Standing over the foot is supported; the same body with the foot far to the side
1045 // puts the centre of mass outside the support and must be flagged.
1046 ab_load_trace("/tmp/nx_ab_plant.tr" as *u8, V, E, C, S, cfg)
1047 ab_com(V, cfg[0], cfg[1], E, cfg[2], o)
1048 var t11: i64 = 0
1049 if o[0] == 0 { if o[2] == 8 { t11 = 1 } }
1050 gv_check("T11 a body standing OVER its foot is stable, on supported frames it counted" as *u8, t11, ctr)
1051 // ★★★T11b THE TOOTH THE HULL BUG WALKED THROUGH. Four contacts -- two feet, each with heel and toe
1052 // -- and a centre of mass in the middle of them. The old code took the first TWO contacts, so the
1053 // support was one foot and a correctly standing body measured as falling sideways. A hull of all
1054 // four contains the CoM, so the honest answer is stable. Built as a FIXTURE, not an assertion.
1055 let qb: *u8 = sys_mmap(AB_MAGIC_4096)
1056 let qp: *i64 = sys_mmap(16) as *i64
1057 qp[0] = 0
1058 ab_app(qb,qp,"A 4 5\n" as *u8)
1059 ab_app(qb,qp,"E 1\nE 2\nE 3\nE 4\n" as *u8)
1060 var qf: i64 = 0
1061 while qf < 4 {
1062 ab_v(qb,qp,qf,0, 0, 900, 0) // body mass, centred between the feet
1063 ab_v(qb,qp,qf,1, 84, 0, 200) // right toe
1064 ab_v(qb,qp,qf,2, 84, 0, 0-67) // right heel
1065 ab_v(qb,qp,qf,3, 0-84, 0, 200) // left toe
1066 ab_v(qb,qp,qf,4, 0-84, 0, 0-67) // left heel
1067 qf = qf + 1
1068 }
1069 ab_wr("/tmp/nx_ab_stance.tr" as *u8, qb, qp[0])
1070 ab_load_trace("/tmp/nx_ab_stance.tr" as *u8, V, E, C, S, cfg)
1071 ab_com(V, cfg[0], cfg[1], E, cfg[2], o)
1072 var t11b: i64 = 0
1073 if o[0] == 0 { if o[2] == 4 { t11b = 1 } }
1074 gv_check("T11b a TWO-FOOTED stance with four contacts is stable -- the hull, not the first two" as *u8, t11b, ctr)
1075 ab_fixture("/tmp/nx_ab_tip.tr" as *u8, 8, 0, 0, 900, 0)
1076 ab_load_trace("/tmp/nx_ab_tip.tr" as *u8, V, E, C, S, cfg)
1077 ab_com(V, cfg[0], cfg[1], E, cfg[2], o)
1078 var t12: i64 = 0
1079 if o[0] > 0 { if o[1] > 0 { t12 = 1 } }
1080 gv_check("T12 REFUTATION: a centre of mass outside the support polygon is flagged" as *u8, t12, ctr)
1081 // ---- ★T13 the capacity refusal. The plan's S0 rung exists because this program has shipped three
1082 // emitters that silently stopped at a cap. A trace claiming more frames than we can hold must be
1083 // REFUSED with its own code, never quietly clipped to something that still scores.
1084 let ob: *u8 = sys_mmap(256)
1085 let op: *i64 = sys_mmap(16) as *i64
1086 op[0] = 0
1087 ab_app(ob,op,"A " as *u8); ab_appn(ob,op,AB_MAXF+1); ab_appn(ob,op,3); ab_app(ob,op,"\n" as *u8)
1088 ab_wr("/tmp/nx_ab_over.tr" as *u8, ob, op[0])
1089 var t13: i64 = 0
1090 if ab_load_trace("/tmp/nx_ab_over.tr" as *u8, V, E, C, S, cfg) == AB_E_CAP { t13 = 1 }
1091 gv_check("T13 a trace beyond declared capacity is REFUSED, not truncated" as *u8, t13, ctr)
1092 var t14: i64 = 0
1093 if ab_load_trace("/tmp/nx_ab_missing.tr" as *u8, V, E, C, S, cfg) == AB_E_OPEN { t14 = 1 }
1094 gv_check("T14 a missing trace fails loud with its own exit code" as *u8, t14, ctr)
1095 // ---- ★T15 DETERMINISM: the same trace scored twice gives the same numbers, and the check is not
1096 // vacuous because T5/T7/T9/T12 already proved the numbers move when the input does.
1097 ab_load_trace("/tmp/nx_ab_slide.tr" as *u8, V, E, C, S, cfg)
1098 let d1: *i64 = sys_mmap(64) as *i64
1099 let d2: *i64 = sys_mmap(64) as *i64
1100 ab_skate(V, cfg[0], E, cfg[2], d1)
1101 ab_skate(V, cfg[0], E, cfg[2], d2)
1102 var t15: i64 = 0
1103 if d1[0]==d2[0] { if d1[1]==d2[1] { if d1[2]==d2[2] { t15 = 1 } } }
1104 gv_check("T15 deterministic: the same trace scores identically twice" as *u8, t15, ctr)
1105 // ---- ★CHANNEL 6. Four joints forming two parallel bars a declared distance apart, with capsule
1106 // radii chosen so the pair is clear in one fixture and overlapping in the other. `gap` is the
1107 // separation; radii sum to 100, so gap 300 is clear and gap 40 is 60mm of interpenetration.
1108 let pb: *u8 = sys_mmap(AB_MAGIC_4096)
1109 let pp: *i64 = sys_mmap(16) as *i64
1110 var gapv: i64 = 300
1111 var round: i64 = 0
1112 var clear_v: i64 = 0-1
1113 var over_v: i64 = 0-1
1114 var over_worst: i64 = 0
1115 var adjacent_flagged: i64 = 1
1116 while round < 2 {
1117 if round == 1 { gapv = 40 }
1118 pp[0] = 0
1119 ab_app(pb,pp,"A 2 4\n" as *u8)
1120 // bar A: joints 0-1. bar B: joints 2-3, offset sideways by gapv.
1121 ab_app(pb,pp,"S 0 1 50\n" as *u8)
1122 ab_app(pb,pp,"S 2 3 50\n" as *u8)
1123 var ff: i64 = 0
1124 while ff < 2 {
1125 ab_v(pb,pp,ff,0, 0, 500, 0-200)
1126 ab_v(pb,pp,ff,1, 0, 500, 200)
1127 ab_v(pb,pp,ff,2, gapv, 500, 0-200)
1128 ab_v(pb,pp,ff,3, gapv, 500, 200)
1129 ff = ff + 1
1130 }
1131 ab_wr("/tmp/nx_ab_sp.tr" as *u8, pb, pp[0])
1132 ab_load_trace("/tmp/nx_ab_sp.tr" as *u8, V, E, C, S, cfg)
1133 let o8: *i64 = sys_mmap(64) as *i64
1134 ab_selfpen(V, cfg[0], S, cfg[4], JP, o8)
1135 if round == 0 { clear_v = o8[2] }
1136 if round == 1 { over_v = o8[2]; over_worst = o8[1] }
1137 round = round + 1
1138 }
1139 var t16: i64 = 0
1140 if clear_v == 0 { t16 = 1 }
1141 gv_check("T16 two limbs 300mm apart do NOT self-penetrate" as *u8, t16, ctr)
1142 var t17: i64 = 0
1143 if over_v > 0 { if over_worst >= 55 { t17 = 1 } }
1144 gv_check("T17 REFUTATION: overlapping limbs are caught, with the depth they overlap by" as *u8, t17, ctr)
1145 // ★★T18 THE TOOTH THAT STOPS EVERY BODY SCORING AS BROKEN. Two capsules SHARING a joint meet there
1146 // by construction -- a forearm touches an upper arm at the elbow. If adjacency were not derived,
1147 // this fixture (two bars hinged at a shared joint, radii overlapping at the hinge) would be
1148 // flagged and every articulated body would read as self-penetrating.
1149 pp[0] = 0
1150 ab_app(pb,pp,"A 2 3\n" as *u8)
1151 ab_app(pb,pp,"S 0 1 50\n" as *u8)
1152 ab_app(pb,pp,"S 1 2 50\n" as *u8)
1153 var gf: i64 = 0
1154 while gf < 2 {
1155 ab_v(pb,pp,gf,0, 0, 900, 0)
1156 ab_v(pb,pp,gf,1, 0, 500, 0)
1157 ab_v(pb,pp,gf,2, 0, 100, 0)
1158 gf = gf + 1
1159 }
1160 ab_wr("/tmp/nx_ab_adj.tr" as *u8, pb, pp[0])
1161 ab_load_trace("/tmp/nx_ab_adj.tr" as *u8, V, E, C, S, cfg)
1162 let o9: *i64 = sys_mmap(64) as *i64
1163 ab_selfpen(V, cfg[0], S, cfg[4], JP, o9)
1164 if o9[3] == 0 { adjacent_flagged = 0 }
1165 var t18: i64 = 0
1166 if o9[2] == 0 { if adjacent_flagged == 0 { t18 = 1 } }
1167 gv_check("T18 limbs SHARING a joint are excluded by DERIVED adjacency, not counted as penetrating" as *u8, t18, ctr)
1168 // ★★★T18b THE TOOTH FOR THE ADJACENCY FIX. A thigh and a torso share NO joint index, but the
1169 // thigh's proximal joint is the torso's CHILD -- a femoral head inside a pelvis. On the real
1170 // skeleton the endpoint-only test false-positived this at 111mm. Adjacency must read the parent
1171 // chain, and the fixture supplies J rows saying joint 9's parent is joint 0.
1172 sp[0] = 0
1173 ab_app(sb,sp,"B " as *u8); ab_appn(sb,sp,9); ab_appn(sb,sp,430); ab_appn(sb,sp,0); ab_appn(sb,sp,140)
1174 ab_app(sb,sp,"\n" as *u8)
1175 ab_app(sb,sp,"J 0 -1 0 0 0 0\n" as *u8)
1176 ab_app(sb,sp,"J 2 1 0 0 0 0\n" as *u8)
1177 ab_app(sb,sp,"J 9 0 1 0 0 0\n" as *u8)
1178 ab_app(sb,sp,"J 10 9 1 0 0 0\n" as *u8)
1179 ab_wr("/tmp/nx_ab_skel2.dat" as *u8, sb, sp[0])
1180 ab_load_skel("/tmp/nx_ab_skel2.dat" as *u8, BLO, BHI, JP)
1181 pp[0] = 0
1182 ab_app(pb,pp,"A 2 11\n" as *u8)
1183 ab_app(pb,pp,"S 0 2 140\n" as *u8)
1184 ab_app(pb,pp,"S 9 10 55\n" as *u8)
1185 var hf: i64 = 0
1186 while hf < 2 {
1187 ab_v(pb,pp,hf,0, 0, 928, 0) // pelvis
1188 ab_v(pb,pp,hf,2, 0, AB_MAGIC_1523, 0) // cervicale
1189 ab_v(pb,pp,hf,9, 84, 928, 0) // hip -- 84mm out, well inside 140+55
1190 ab_v(pb,pp,hf,10, 84, 499, 0) // knee
1191 hf = hf + 1
1192 }
1193 ab_wr("/tmp/nx_ab_par.tr" as *u8, pb, pp[0])
1194 ab_load_trace("/tmp/nx_ab_par.tr" as *u8, V, E, C, S, cfg)
1195 let o10: *i64 = sys_mmap(64) as *i64
1196 ab_selfpen(V, cfg[0], S, cfg[4], JP, o10)
1197 var t18b: i64 = 0
1198 if o10[2] == 0 { if o10[3] == 0 { t18b = 1 } }
1199 gv_check("T18b a thigh inside a pelvis is adjacency via the PARENT CHAIN, not self-penetration" as *u8, t18b, ctr)
1200 // ★T18c and the fix must not have blunted the instrument: with the SAME geometry but no parent
1201 // link declared, the overlap is still reported. Without this, "adjacency" could swallow everything.
1202 var zz: i64 = 0
1203 while zz < AB_MAXJ { JP[zz] = 0-1; zz = zz + 1 }
1204 ab_selfpen(V, cfg[0], S, cfg[4], JP, o10)
1205 var t18c: i64 = 0
1206 if o10[2] > 0 { if o10[3] > 0 { t18c = 1 } }
1207 gv_check("T18c REFUTATION: strip the parent link and the SAME overlap is reported again" as *u8, t18c, ctr)
1208 return gv_verdict("ACTBENCH-GATE" as *u8, ctr,
1209 "all 6 published PHYSSCORE channels, each with a separating pair; radii and adjacency are data and derivation, never constants in this file" as *u8)
1210}
1211
1212func main(argc: i64, argv: *i64) -> i64 {
1213 if argc >= 2 {
1214 if ab_streq(argv[1] as *u8, "selftest" as *u8) == 1 { return ab_gate() }
1215 if ab_streq(argv[1] as *u8, "score" as *u8) == 1 {
1216 if argc < 4 { ab_puts("usage: nx_actbench score <trace> <skel.dat>\n" as *u8); return 2 }
1217 return ab_score(argv[2] as *u8, argv[3] as *u8)
1218 }
1219 }
1220 ab_puts("usage: nx_actbench score <trace> <skel.dat> | selftest\n" as *u8)
1221 return 2
1222}