nx_ansur_regress.nx
buildroot/runtime/nx_ansur_regress.nx
about
nx_ansur_regress.nx -- DOES HUMAN PROPORTION CHANGE WITH STATURE? Regress a landmark ratio on
stature over the ANSUR II sample and let the SLOPE answer.
WHY. nx_skelgen stores every segment as PER-MILLE OF STATURE, and its own docstring states the
consequence as a feature: "so the same rules generate a child, an adult or a giant". Measured,
femur/stature is 0.2411 at 1750 mm and 0.2407/0.2411 at 1400/1900 -- CONSTANT BY CONSTRUCTION.
It is not emergent; it is the storage format. A rig that only scales is a doll at every size.
The fix is mechanical because ANSUR measures nx_skelgen's OWN landmarks:
acromialheight/stature <-> BP_ACROMION = 818 per-mille
trochanterionheight/stature <-> BP_TROCH = 530 per-mille
Regress (landmark_height / stature) ON stature. A NON-ZERO SLOPE IS THE MISSING PROPORTION
CHANGE, and the slope itself is the correction term.
nx_ansur_regress <ansur.csv> <landmark_column_name>
COLUMNS ARE RESOLVED BY NAME FROM THE HEADER ROW AT RUNTIME, never by a hardcoded index: ANSUR's
columns are alphabetical and the male and female files must both parse, so an index literal would
be a magic number that silently reads the wrong measure if either file differs.
THE THREE TRAPS, QUOTED FROM THE PUBLISHER, ALL OF WHICH BITE THIS COMPUTATION:
1. Units are millimetres EXCEPT MASS, WHICH IS HECTOGRAMS DESPITE ITS COLUMN BEING NAMED
weightkg -- a 10x error waiting. This organ touches no mass column, and refuses any column
whose name contains "weight" so the trap cannot be walked into later.
2. The trailing Heightin / Weightlbs columns are SELF-REPORTED and DISAGREE with the official
measures. This organ uses `stature`, never Heightin, and refuses those names by name.
3. The sample is military including reservists and the publisher states it "is still not an
approximation of the US Civilian population". THAT BOUND TRAVELS WITH EVERY NUMBER THIS
ORGAN PRINTS, so it is printed WITH them rather than left in a doc nobody reads.
ARITHMETIC. Integer-only, and mean-centred ON PURPOSE: the raw cross-products (n*Sxy - Sx*Sy)
reach ~5.7e12 and multiplying by a precision scale would approach the i64 ceiling. Centring first
keeps every term small (dx ~ +/-100, dy ~ +/-30), so the scaled slope cannot overflow. Two passes,
no floats -- NishiLang has no float type and this needs none.
license_tier: ORIGINAL expect_exit: 0
dependencies 1 imports · 0 importers
imports: nx_syscalls.nx
imported by: nobody (leaf or entry point)
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| none |
consts
| 37 | const AR_E6: i64 = 1000000 |
| 38 | const AR_PERMIL: i64 = 1000 |
| 39 | const AR_COMMA: i64 = 44 |
| 40 | const AR_NL: i64 = 10 |
| 41 | const AR_CR: i64 = 13 |
| 42 | const AR_D0: i64 = 48 |
| 43 | const AR_D9: i64 = 57 |
| 44 | const AR_DOT: i64 = 46 |
| 45 | const AR_MINUS: i64 = 45 |
| 49 | const AR_STAT_LO: i64 = 1000 |
| 50 | const AR_STAT_HI: i64 = 2200 |
functions
| 52 | func ar_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } |
| 53 | func ar_num(v: i64) -> i64 |
| 65 | func ar_kv(k: *u8, v: i64) -> i64 { ar_puts(k); ar_num(v); ar_puts("\n" as *u8); return 0 } |
| 66 | func ar_refuse(r: *u8) -> i64 { ar_puts("ANSUR-REGRESS REFUSED: " as *u8); ar_puts(r); ar_puts("\n" as *u8); return 0 } |
| 67 | func ar_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } |
| 70 | func ar_eq(buf: *u8, a: i64, b: i64, s: *u8) -> i64 |
| 78 | func ar_contains(buf: *u8, a: i64, b: i64, s: *u8) -> i64 |
| 92 | func ar_parse(buf: *u8, a: i64, b: i64) -> i64 |
| 112 | func ar_eol(buf: *u8, n: i64, p: i64) -> i64 |
| 122 | func ar_field_bounds(buf: *u8, p: i64, e: i64, want: i64, sa: *i64, sb: *i64) -> i64 |
| 144 | func ar_col_by_name(buf: *u8, p: i64, e: i64, name: *u8) -> i64 |
| 173 | func ar_mean(buf: *u8, n: i64, h_end: i64, c_land: i64, lname: *u8) -> i64 |
| 209 | func main(argc: i64, argv: *i64) -> i64 |