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}