nx_haircollide_gate.nx source
↩ module page · 139 lines · 8781 B
1// nx_haircollide_gate.nx -- THE TOOTH THE COLLIDER OWED. Subject: vm_capsule_push (nx_vecmath) and
2// hd_tick_collide (nx_hairdyn_lib), landed 2026-09-04 against the operator's REJECT row
3// beach-hair-colour-wind-0830: "hair clips the body and moves like a mop, not strand by strand".
4//
5// WHY IT EXISTS AS ITS OWN GATE. nx_hairdyn_gate's teeth measure hd_tick, which this change DELIBERATELY
6// left unchanged, so a GREEN from it would say nothing whatever about the collider -- the classic shape of
7// a gate that is counted as coverage while being structurally blind to the thing shipped beside it.
8//
9// * A COMPILING CAPABILITY IS NOT A PROVEN ONE. The lib built clean hours before this file existed, and a
10// clean build is exactly what an absent collider also produces, because absent code has no failure mode.
11//
12// THE ANTI-VACUITY TOOTH IS THE ncap=0 CONTROL: with no capsules the SAME seeded point must remain INSIDE
13// the region a capsule would have covered. Without it, a collider that silently did nothing would pass
14// every "is the point outside?" assertion whenever the seed happened to start outside, and a collider that
15// pushed EVERYTHING would pass them all too -- which is why a positive control (a point outside is left
16// ALONE) sits beside the deny tooth. A suite that only asks "was it pushed?" is blind in both directions.
17// license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0
18import "nx_syscalls.nx"
19import "nx_gate_verdict.nx"
20import "nx_vecmath.nx"
21import "nx_hairdyn_lib.nx"
22
23// One capsule, chosen so every number below is checkable by hand: axis up the y column from the origin,
24// radius 100. A reader can verify each expectation with arithmetic rather than by trusting the organ.
25const HG_AX: i64 = 0
26const HG_AY: i64 = 0
27const HG_AZ: i64 = 0
28const HG_BX: i64 = 0
29const HG_BY: i64 = 1000
30const HG_BZ: i64 = 0
31const HG_R: i64 = 100
32const HG_MID: i64 = 500 // a y strictly between the endpoints, so the closest point is ON the segment
33const HG_TOL: i64 = 2 // Q256 truncation band: three integer divides, each losing at most 1 unit
34
35func hg_dist_to_axis(px: i64, py: i64, pz: i64) -> i64 {
36 // the capsule axis is the y column, so the distance to it is the xz radius -- no projection needed,
37 // which is the point of choosing this axis: the ORACLE here is arithmetic the collider never runs.
38 return vm_isqrt(px*px + pz*pz)
39}
40
41func main() -> i64 {
42 let ctr: *i64 = gv_ctr()
43 gv_head("nx_haircollide_gate -- vm_capsule_push + hd_tick_collide" as *u8)
44 let o: *i64 = sys_mmap(24) as *i64
45
46 // ---- THE DENY DIRECTION: a point INSIDE is pushed to the surface ---------------------------------
47 let inside_r: i64 = vm_capsule_push(10, HG_MID, 0, HG_AX, HG_AY, HG_AZ, HG_BX, HG_BY, HG_BZ, HG_R, o)
48 gv_check_eq("a-point-INSIDE-reports-a-contact" as *u8, inside_r, 1, ctr)
49 let d_out: i64 = hg_dist_to_axis(o[0], o[1], o[2])
50 gv_check_near("the-pushed-point-lands-ON-the-surface-not-merely-somewhere-else" as *u8, d_out, HG_R, HG_TOL, ctr)
51 gv_check_eq("the-push-is-PURELY-RADIAL-so-the-axial-coordinate-is-untouched" as *u8, o[1], HG_MID, ctr)
52
53 // ---- THE POSITIVE CONTROL: a point OUTSIDE must be LEFT ALONE ------------------------------------
54 // Without this, a collider that pushed every point it was handed would pass every tooth above.
55 o[0] = 0-1; o[1] = 0-1; o[2] = 0-1
56 let outside_r: i64 = vm_capsule_push(500, HG_MID, 0, HG_AX, HG_AY, HG_AZ, HG_BX, HG_BY, HG_BZ, HG_R, o)
57 gv_check_eq("pos-control-a-point-OUTSIDE-reports-NO-contact" as *u8, outside_r, 0, ctr)
58 gv_check_eq("pos-control-a-point-OUTSIDE-leaves-the-out-buffer-UNWRITTEN" as *u8, o[0], 0-1, ctr)
59
60 // ---- THE DEGENERATE CASE IS DEFINED, NOT UNDEFINED ------------------------------------------------
61 // A point exactly ON the axis has no direction to be pushed along. The lib documents that it takes +x.
62 // If this were left undefined the point would stay inside FOREVER -- the silent class the collider exists
63 // to remove -- so the behaviour is pinned here rather than left to whatever the arithmetic happens to do.
64 let axis_r: i64 = vm_capsule_push(0, HG_MID, 0, HG_AX, HG_AY, HG_AZ, HG_BX, HG_BY, HG_BZ, HG_R, o)
65 gv_check_eq("a-point-ON-the-axis-still-reports-a-contact" as *u8, axis_r, 1, ctr)
66 gv_check_eq("the-on-axis-degenerate-push-takes-the-DOCUMENTED-plus-x-direction" as *u8, o[0], HG_R, ctr)
67
68 // ---- BEYOND THE ENDPOINT: the closest point CLAMPS to the cap, it does not run off the axis --------
69 // A segment-distance that forgot to clamp t would treat the infinite LINE as the subject and report this
70 // point as inside; that is the commonest way this arithmetic is written wrong.
71 let past_r: i64 = vm_capsule_push(0, HG_BY + 500, 0, HG_AX, HG_AY, HG_AZ, HG_BX, HG_BY, HG_BZ, HG_R, o)
72 gv_check_eq("neg-control-a-point-500-BEYOND-the-endpoint-is-OUTSIDE-the-capsule-not-inside-the-line" as *u8,
73 past_r, 0, ctr)
74
75 // ---- END TO END THROUGH THE CHAIN, AND ITS ANTI-VACUITY CONTROL -----------------------------------
76 // hd_tick_collide is the shipping entry point; the teeth above prove the geometry, this proves the WIRING.
77 let caps: *i64 = sys_mmap(7*8) as *i64
78 caps[0] = HG_AX; caps[1] = HG_AY; caps[2] = HG_AZ
79 caps[3] = HG_BX; caps[4] = HG_BY; caps[5] = HG_BZ
80 caps[6] = HG_R
81 // Scratch for the collider, allocated ONCE here because the lib takes it as a parameter now -- see the
82 // hot-loop note in nx_hairdyn_lib. A gate that allocated per call would hide the exact property the
83 // parameter exists to guarantee, which is that the solver allocates nothing on the wasm tick path.
84 let hsc: *i64 = sys_mmap(24) as *i64
85
86 let n: i64 = 4
87 let seglen: i64 = 120
88 let st1: *i64 = hd_alloc(n)
89 hd_seat(st1, n, 0, HG_BY, 0, seglen)
90 let resolved: i64 = hd_tick_collide(st1, n, 0, HG_BY, 0, seglen, SB_K_HAIR, SB_C_HAIR, SB_MAXD_HAIR, caps, 1, hsc)
91
92 // THE ANTI-VACUITY CONTROL: the SAME seed and the SAME tick with ncap=0. If the collider were a no-op
93 // the two runs would agree, so this tooth is the one that can tell a working push from an absent one.
94 let st0: *i64 = hd_alloc(n)
95 hd_seat(st0, n, 0, HG_BY, 0, seglen)
96 let resolved0: i64 = hd_tick_collide(st0, n, 0, HG_BY, 0, seglen, SB_K_HAIR, SB_C_HAIR, SB_MAXD_HAIR, caps, 0, hsc)
97 gv_check_eq("neg-control-with-ncap-0-NOTHING-is-resolved" as *u8, resolved0, 0, ctr)
98
99 // count how many segments each run leaves inside the capsule's radius
100 var in1: i64 = 0
101 var in0: i64 = 0
102 var i: i64 = 0
103 while i < n {
104 let b: i64 = i*SD_STRIDE
105 if hg_dist_to_axis(st1[b], st1[b+1], st1[b+2]) < HG_R - HG_TOL { in1 = in1 + 1 }
106 if hg_dist_to_axis(st0[b], st0[b+1], st0[b+2]) < HG_R - HG_TOL { in0 = in0 + 1 }
107 i = i + 1
108 }
109 gv_check_eq("WITH-the-collider-NO-segment-is-left-inside-the-body-capsule" as *u8, in1, 0, ctr)
110 gv_check("neg-control-WITHOUT-the-collider-at-least-one-segment-IS-left-inside" as *u8, (in0 > 0) as i64, ctr)
111 gv_check("the-fixture-REACHED-the-condition-the-collider-had-something-to-resolve" as *u8,
112 (resolved > 0) as i64, ctr)
113
114 gv_values_head()
115 gv_kv("capsule_radius" as *u8, HG_R)
116 gv_kv("surface_tolerance_q256_band" as *u8, HG_TOL)
117 gv_kv("pushed_point_distance_to_axis" as *u8, d_out)
118 gv_kv("segments" as *u8, n)
119 gv_kv("contacts_RESOLVED_not_defects" as *u8, resolved)
120 gv_kv("segments_left_inside_WITH_collider" as *u8, in1)
121 gv_kv("segments_left_inside_WITHOUT_collider" as *u8, in0)
122 // WHY THE END-TO-END TOOTH FAILS MUST BE READABLE FROM THE GATE'S OWN OUTPUT, or the next reader
123 // re-derives it by hand exactly as I was about to. A verdict is not a diagnosis: 4-of-4-still-inside
124 // is consistent with the push never writing, with it writing the wrong slot, and with the fixture's
125 // segments never being where I assumed -- three causes with three different remedies.
126 gv_kv("seg0_x_after" as *u8, st1[0])
127 gv_kv("seg0_y_after" as *u8, st1[1])
128 gv_kv("seg0_z_after" as *u8, st1[2])
129 gv_kv("seg0_dist_to_axis_after" as *u8, hg_dist_to_axis(st1[0], st1[1], st1[2]))
130 gv_kv("seg0_x_nocollider" as *u8, st0[0])
131 gv_kv("seg0_y_nocollider" as *u8, st0[1])
132 gv_kv("seg0_dist_to_axis_nocollider" as *u8, hg_dist_to_axis(st0[0], st0[1], st0[2]))
133 gv_kv("SD_STRIDE" as *u8, SD_STRIDE)
134 gv_kv("capsule_axis_y_span_lo" as *u8, HG_AY)
135 gv_kv("capsule_axis_y_span_hi" as *u8, HG_BY)
136
137 return gv_verdict("nx_haircollide_gate" as *u8, ctr,
138 "the collider pushes an entered point to the surface, leaves an outside point untouched, clamps at the endpoint, and the ncap=0 control proves the tooth can fail" as *u8)
139}