nx_fieldfit_lib.nx source
↩ module page · 519 lines · 22307 B
1// nx_fieldfit_lib.nx -- FIT A PARAMETRIC IMPLICIT FIELD TO A SCANNED ORACLE. The machinery nx_skullsdf's `tune` grew
2// (anatomy AN17, 2026-09-18/19), extracted so any anatomy field -- a skull, an arm, a foot, a muscle belly, a tissue
3// layer -- is fitted by ONE optimiser with ONE objective and ONE fit-file contract, and a defect fixed here is fixed for
4// every field at once. The consumer hands over three functions as VALUES (NishiLang func-pointer locals and fields,
5// indirect calls up to six arguments):
6// field(px, py, pz, a, b, P) -> signed distance at `fq` units per millimetre (negative inside) for parameter vector P,
7// with a and b the consumer's two policy knobs (the skull passes sex and robusticity)
8// region(x, y, z) -> 0 .. nreg-1, the named anatomical region a point belongs to
9// live(k) -> 1 for a station the field reads, 0 for a retired one (the descent skips it: two
10// evaluations spent on a dead station are a pass that cannot move anything)
11//
12// THE OBJECTIVE IS TWO-SIDED, AND IT HAS TO BE. A one-directional score -- how far is each ORACLE point from my surface --
13// is trivially gamed by DELETING GEOMETRY: the skull's first tuning run shrank its orbits from 22 mm to 8 and its
14// maxilla from 30 mm to 13, improving the number 40 percent by filling in the eye sockets, because surface that does not
15// exist cannot be wrong. The REVERSE term charges every lattice point on OUR zero level its distance to the nearest
16// oracle sample, so a deleted feature is EXPENSIVE by construction. (The reverse term uses the nearest oracle VERTEX from
17// a spatial hash: an upper bound on the true point-to-surface distance -- it can overstate, never understate, the safe
18// direction for a penalty.)
19// THE OBJECTIVE IS REGION-BALANCED. A plain mean is dominated by whatever region has the most samples (the skull's vault
20// and base held 9,335 + 8,672 of ~31,000, so a 49 mm muzzle and 4 mm drilled orbits read as a 4 mm skull and the
21// surface objective fell 629 -> 531 while the chin moved +4 -> +19.6 mm). Each side is the mean of the PER-REGION means
22// (a region with no samples abstains), so the smallest region weighs what the largest does. No weight is typed: the
23// population decides.
24// THE DESCENT is coordinate descent: per live station try +step then -step, keep a move only if the MEASURED objective
25// falls; the step halves per pass, floor 1. No change is ever kept on the strength of looking better.
26// THE FIT FILE is newline-separated integers, loaded as a PREFIX: a file shorter than the vector applies to its first
27// entries and the rest keep their defaults, so growing a parametrisation never discards banked convergence; a file
28// shorter than `minlen` changes nothing (parsed into a temp and applied only when long enough, so a truncated write
29// cannot half-poison a field). Written whole by ff_save.
30// UNITS: objectives are reported in centi-millimetres; `lat` (the reverse lattice spacing), `ext` (the half-extent the
31// hash covers) and `dim` (cells per axis) are the consumer's, taken from the resource it fits.
32// LIB, no main. license_tier: ORIGINAL. No hw writes (Rule 26).
33import "nx_syscalls.nx"
34import "nx_atomic_rewrite.nx"
35
36const FF_I64: i64 = 8
37const FF_CENTI: i64 = 100 // objectives are centi-millimetres
38const FF_CTX_BYTES: i64 = 256 // FfCtx: 21 fields of 8 bytes, one page-rounded map
39const FF_LOAD_HDR: i64 = 16 // the two-word length record sys_read_file fills
40const FF_SAVE_DIGITS: i64 = 16 // bytes reserved per integer on save (sign + 19 digits fits a page-rounded map)
41const FF_SAVE_SLACK: i64 = 64
42const FF_MODE644: i64 = 420
43
44// THE THIRD TERM, OPTIONAL: `extra(P)` returns a further mean error in centi-millimetres the consumer derives from its
45// own stations against the ruler's ORACLE readings (a landmark term: where the field PLACES its features against
46// where the ruler FOUND them on the oracle). MEASURED NEED (skull, 2026-09-19): with the two surface terms alone the
47// descent closed the orbit sockets to 2 mm slits and deepened the aperture to a 35 mm tunnel, because a surface
48// objective cannot see an opening; the ruler read 4 of 15 quantities in band. With has_extra = 1 the objective is the
49// three-way mean of the three error means (all centi-mm, no weight typed); the consumer's REAL ruler still judges the
50// emitted mesh outside the loop, so the term is a surrogate with a stated error, never the verdict.
51struct FfCtx { field: func(i64,i64,i64,i64,i64,*i64) -> i64, region: func(i64,i64,i64) -> i64, live: func(i64) -> i64, RX: *i64, RY: *i64, RZ: *i64, RG: *i64, n: i64, head: *i64, next: *i64, ext: i64, dim: i64, lat: i64, a: i64, b: i64, fq: i64, nreg: i64, sum: *i64, cnt: *i64, np: i64, cap: i64, extra: func(*i64) -> i64, has_extra: i64 }
52
53// a context sized for `cap` oracle samples, `np` stations, `nreg` regions, a dim^3 hash over +/- ext mm, lattice lat mm
54func ff_ctx_new(np: i64, nreg: i64, cap: i64, ext: i64, dim: i64, lat: i64, fq: i64) -> *FfCtx {
55 let c: *FfCtx = (sys_mmap(FF_CTX_BYTES)) as *FfCtx
56 c.np = np; c.nreg = nreg; c.cap = cap; c.ext = ext; c.dim = dim; c.lat = lat; c.fq = fq
57 c.RX = sys_mmap(cap*FF_I64) as *i64
58 c.RY = sys_mmap(cap*FF_I64) as *i64
59 c.RZ = sys_mmap(cap*FF_I64) as *i64
60 c.RG = sys_mmap(cap*FF_I64) as *i64
61 c.next = sys_mmap(cap*FF_I64) as *i64
62 c.head = sys_mmap(dim*dim*dim*FF_I64) as *i64
63 c.sum = sys_mmap(nreg*FF_I64) as *i64
64 c.cnt = sys_mmap(nreg*FF_I64) as *i64
65 c.n = 0; c.a = 0; c.b = 0; c.has_extra = 0
66 return c
67}
68
69// ==== the spatial hash over the oracle samples ====
70func ff_cell(c: *FfCtx, x: i64, y: i64, z: i64) -> i64 {
71 let d: i64 = c.dim
72 let e: i64 = c.ext
73 var i: i64 = (x + e) * d / (e*2)
74 var j: i64 = (y + e) * d / (e*2)
75 var k: i64 = (z + e) * d / (e*2)
76 if i < 0 { i = 0 }
77 if j < 0 { j = 0 }
78 if k < 0 { k = 0 }
79 if i >= d { i = d-1 }
80 if j >= d { j = d-1 }
81 if k >= d { k = d-1 }
82 return (i*d + j)*d + k
83}
84// bucket the oracle samples so the reverse term does not scan all of them per query
85func ff_bin(c: *FfCtx) -> i64 {
86 let d: i64 = c.dim
87 let head: *i64 = c.head
88 let next: *i64 = c.next
89 var q: i64 = 0
90 while q < d*d*d { head[q] = 0-1; q = q + 1 }
91 var i: i64 = 0
92 while i < c.n {
93 let h: i64 = ff_cell(c, c.RX[i], c.RY[i], c.RZ[i])
94 next[i] = head[h]
95 head[h] = i
96 i = i + 1
97 }
98 return 0
99}
100// nearest oracle sample to (x,y,z) in whole millimetres, searching the home cell then one ring out; ext when none
101func ff_near(c: *FfCtx, x: i64, y: i64, z: i64) -> i64 {
102 let d: i64 = c.dim
103 let e: i64 = c.ext
104 let RX: *i64 = c.RX
105 let RY: *i64 = c.RY
106 let RZ: *i64 = c.RZ
107 let head: *i64 = c.head
108 let next: *i64 = c.next
109 var ci: i64 = (x + e) * d / (e*2)
110 var cj: i64 = (y + e) * d / (e*2)
111 var ck: i64 = (z + e) * d / (e*2)
112 var best: i64 = 0-1
113 var r: i64 = 0
114 while r <= 1 {
115 var di: i64 = 0 - r
116 while di <= r {
117 var dj: i64 = 0 - r
118 while dj <= r {
119 var dk: i64 = 0 - r
120 while dk <= r {
121 let ii: i64 = ci+di
122 let jj: i64 = cj+dj
123 let kk: i64 = ck+dk
124 if ii >= 0 { if ii < d { if jj >= 0 { if jj < d { if kk >= 0 { if kk < d {
125 var p: i64 = head[(ii*d + jj)*d + kk]
126 while p >= 0 {
127 let ddx: i64 = x-RX[p]
128 let ddy: i64 = y-RY[p]
129 let ddz: i64 = z-RZ[p]
130 let d2: i64 = ddx*ddx + ddy*ddy + ddz*ddz
131 if best < 0 { best = d2 } else { if d2 < best { best = d2 } }
132 p = next[p]
133 }
134 } } } } } }
135 dk = dk + 1
136 }
137 dj = dj + 1
138 }
139 di = di + 1
140 }
141 if best >= 0 { r = 2 } else { r = r + 1 }
142 }
143 if best < 0 { return e }
144 return ff_isqrt(best)
145}
146func ff_isqrt(v: i64) -> i64 { if v<=0 { return 0 } var x: i64=v; var y: i64=(x+1)/2; while y<x { x=y; y=(x+v/x)/2 } return x }
147
148// each oracle sample's region, classified ONCE per fit (the consumer's boxes may follow its loaded vector; they hold
149// for the run)
150func ff_regions(c: *FfCtx) -> i64 {
151 var i: i64 = 0
152 while i < c.n { c.RG[i] = c.region(c.RX[i], c.RY[i], c.RZ[i]); i = i + 1 }
153 return 0
154}
155
156// ==== the region tables: the mean of the per-region means ====
157func ff_rtab_reset(c: *FfCtx) -> i64 {
158 var r: i64 = 0
159 while r < c.nreg { c.sum[r] = 0; c.cnt[r] = 0; r = r + 1 }
160 return 0
161}
162// centi-mm from sums carried at `fq` units per mm; no region at all is the worst answer (ext mm)
163func ff_rtab_mean(c: *FfCtx, fq: i64) -> i64 {
164 var acc: i64 = 0
165 var nr: i64 = 0
166 var r: i64 = 0
167 while r < c.nreg {
168 if c.cnt[r] > 0 { acc = acc + c.sum[r]*FF_CENTI/(c.cnt[r]*fq); nr = nr + 1 }
169 r = r + 1
170 }
171 if nr == 0 { return c.ext*FF_CENTI }
172 return acc/nr
173}
174// forward: each oracle sample's |field|, balanced over regions
175func ff_score_fwd(c: *FfCtx, P: *i64) -> i64 {
176 ff_rtab_reset(c)
177 var i: i64 = 0
178 while i < c.n {
179 var d: i64 = c.field(c.RX[i], c.RY[i], c.RZ[i], c.a, c.b, P)
180 if d < 0 { d = 0 - d }
181 let r: i64 = c.RG[i]
182 c.sum[r] = c.sum[r] + d
183 c.cnt[r] = c.cnt[r] + 1
184 i = i + 1
185 }
186 if c.n == 0 { return 0 }
187 return ff_rtab_mean(c, c.fq)
188}
189// reverse: every lattice point within half a step of our zero level, charged its distance to the nearest oracle sample
190func ff_score_rev(c: *FfCtx, P: *i64) -> i64 {
191 ff_rtab_reset(c)
192 let e: i64 = c.ext
193 let step: i64 = c.lat
194 let bq: i64 = step*c.fq
195 var cnt: i64 = 0
196 var x: i64 = 0 - e
197 while x <= e {
198 var y: i64 = 0 - e
199 while y <= e {
200 var z: i64 = 0 - e
201 while z <= e {
202 let d: i64 = c.field(x, y, z, c.a, c.b, P)
203 if d > 0 - bq { if d < bq {
204 let r: i64 = c.region(x, y, z)
205 c.sum[r] = c.sum[r] + ff_near(c, x, y, z)
206 c.cnt[r] = c.cnt[r] + 1
207 cnt = cnt + 1
208 } }
209 z = z + step
210 }
211 y = y + step
212 }
213 x = x + step
214 }
215 if cnt == 0 { return e*FF_CENTI }
216 return ff_rtab_mean(c, 1)
217}
218// the objective: the mean of both directions, so neither can be gamed alone; with a consumer's extra term, the mean
219// of the three (every term a mean error in centi-mm, so no weight is typed)
220func ff_score2(c: *FfCtx, P: *i64) -> i64 {
221 let f: i64 = ff_score_fwd(c, P)
222 let r: i64 = ff_score_rev(c, P)
223 if c.has_extra == 1 {
224 let e: i64 = c.extra(P)
225 return (f + r + e)/3
226 }
227 return (f + r)/2
228}
229func ff_live_count(c: *FfCtx) -> i64 {
230 var lv: i64 = 0
231 var k: i64 = 0
232 while k < c.np { lv = lv + c.live(k); k = k + 1 }
233 return lv
234}
235func ff_puts(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
236func ff_pn(v: i64) -> i64 {
237 let b: *u8 = sys_mmap(32)
238 let o: i64 = ff_wint(b, 0, v)
239 sys_write(1, b, o)
240 return 0
241}
242// COORDINATE DESCENT from P: returns the best objective; P holds the accepted vector. Convergence is PRINTED per pass
243// when verbose ({"pass":k,"step":s,"best_centi":b}), never assumed
244func ff_descend(c: *FfCtx, P: *i64, passes: i64, step0: i64, verbose: i64) -> i64 {
245 var best: i64 = ff_score2(c, P)
246 var step: i64 = step0
247 if step < 1 { step = 8 }
248 var pass: i64 = 0
249 while pass < passes {
250 var k: i64 = 0
251 while k < c.np {
252 if c.live(k) == 1 {
253 let orig: i64 = P[k]
254 P[k] = orig + step
255 let up: i64 = ff_score2(c, P)
256 P[k] = orig - step
257 let dn: i64 = ff_score2(c, P)
258 P[k] = orig
259 if up < best { if up <= dn { best = up; P[k] = orig + step } }
260 if dn < best { if dn < up { best = dn; P[k] = orig - step } }
261 }
262 k = k + 1
263 }
264 if verbose == 1 {
265 ff_puts("{\x22pass\x22:" as *u8); ff_pn(pass)
266 ff_puts(",\x22step\x22:" as *u8); ff_pn(step)
267 ff_puts(",\x22best_centi\x22:" as *u8); ff_pn(best)
268 ff_puts("}\n" as *u8)
269 }
270 step = step/2
271 if step < 1 { step = 1 }
272 pass = pass + 1
273 }
274 return best
275}
276
277// ==== the fit file ====
278// integer -> ascii into a buffer at offset o; returns the new offset (iterative, no allocation)
279func ff_wint(b: *u8, o: i64, v: i64) -> i64 {
280 var x: i64 = v
281 var oo: i64 = o
282 if x < 0 { b[oo] = 45 as u8; oo = oo + 1; x = 0 - x }
283 var tmp: i64 = x
284 var nd: i64 = 1
285 while tmp >= 10 { tmp = tmp/10; nd = nd + 1 }
286 var k: i64 = nd
287 while k > 0 {
288 var div: i64 = 1
289 var q: i64 = 1
290 while q < k { div = div*10; q = q + 1 }
291 b[oo] = (48 + (x/div) % 10) as u8
292 oo = oo + 1
293 k = k - 1
294 }
295 return oo
296}
297// load a PREFIX of P from a newline-separated integer file: returns the count applied (0 when absent or shorter than minlen)
298func ff_load_prefix(path: *u8, P: *i64, np: i64, minlen: i64) -> i64 {
299 let ln: *i64 = sys_mmap(FF_LOAD_HDR) as *i64
300 let buf: *u8 = sys_read_file(path, ln)
301 if (buf as i64) == 0 { return 0 }
302 let n: i64 = ln[0]
303 let T: *i64 = sys_mmap(np*FF_I64) as *i64
304 var i: i64 = 0
305 var got: i64 = 0
306 while i < n {
307 var c: i64 = buf[i] as i64
308 var neg: i64 = 0
309 if c == 45 {
310 neg = 1
311 i = i + 1
312 if i < n { c = buf[i] as i64 } else { c = 0 }
313 }
314 var isd: i64 = 0
315 if c >= 48 { if c <= 57 { isd = 1 } }
316 if isd == 1 {
317 var v: i64 = 0
318 var run: i64 = 1
319 while run == 1 {
320 v = v*10 + (c-48)
321 i = i + 1
322 if i < n { c = buf[i] as i64 } else { c = 0 }
323 var still: i64 = 0
324 if c >= 48 { if c <= 57 { still = 1 } }
325 if still == 0 { run = 0 }
326 }
327 if neg == 1 { v = 0-v }
328 if got < np { T[got] = v; got = got + 1 }
329 } else {
330 i = i + 1
331 }
332 }
333 if got >= minlen {
334 var lim: i64 = got
335 if lim > np { lim = np }
336 var q: i64 = 0
337 while q < lim { P[q] = T[q]; q = q + 1 }
338 return lim
339 }
340 return 0
341}
342// write the whole vector, one integer per line
343func ff_save(path: *u8, P: *i64, np: i64) -> i64 {
344 let fb: *u8 = sys_mmap(np*FF_SAVE_DIGITS + FF_SAVE_SLACK)
345 var fo: i64 = 0
346 var w: i64 = 0
347 while w < np {
348 fo = ff_wint(fb, fo, P[w])
349 fb[fo] = 10 as u8
350 fo = fo + 1
351 w = w + 1
352 }
353 let fd: i64 = sys_openat_wr(path, FF_MODE644)
354 if fd < 0 { return 0 - 1 }
355 sys_write(fd, fb, fo)
356 sys_close(fd)
357 return fo
358}
359
360
361// Versioned checked path. Existing legacy entry points remain explicitly unverified.
362const FF_FIT_LEGACY_UNVERIFIED: i64 = 0 - 2
363const FF_FIT_CONTRACT_MISMATCH: i64 = 0 - 3
364const FF_FIT_MALFORMED: i64 = 0 - 4
365const FF_FIT_INVALID: i64 = 0 - 5
366const FF_FIT_IO: i64 = 0 - 6
367const FF_FIT_MAGIC_BYTES: i64 = 7
368const FF_FIT_I64_DIGITS: i64 = 21
369const FF_FIT_ATOMIC_PATH_CAP: i64 = 512 // nx_atomic_rewrite ar_syncdir buffer contract
370const FF_FIT_I64_POS_LIMIT: i64 = 9223372036854775807
371struct FfFitContract { header: *u8, header_bytes: i64, np: i64, valid: func(*i64,i64) -> i64 }
372// header is the caller's exact versioned schema/frame/producer/reference/objective identity.
373// It starts NXFFIT2 and ends newline; all identities are part of byte equality, never parsed as numbers.
374func ff_contract_ok(C: *FfFitContract) -> i64 {
375 if C.np <= 0 { return 0 }
376 if C.header_bytes < 0 { return 0 }
377 if C.header_bytes >= FF_FIT_I64_POS_LIMIT { return 0 }
378 if C.np > (FF_FIT_I64_POS_LIMIT - 1 - C.header_bytes) / FF_FIT_I64_DIGITS { return 0 }
379 if C.header_bytes <= FF_FIT_MAGIC_BYTES { return 0 }
380 let magic: *u8 = "NXFFIT2" as *u8
381 var i: i64 = 0
382 while i < FF_FIT_MAGIC_BYTES { if C.header[i] != magic[i] { return 0 } i = i + 1 }
383 if C.header[C.header_bytes - 1] != 10 { return 0 }
384 return 1
385}
386// INTERNAL: called only by ff_parse_verified after contract/header/count bounds.
387// External bytes must enter through ff_load_verified; this is not a standalone boundary API.
388func ff_values_verified(b: *u8, n: i64, T: *i64, C: *FfFitContract) -> i64 {
389 var i: i64 = C.header_bytes
390 var k: i64 = 0
391 while k < C.np {
392 if i >= n { return FF_FIT_MALFORMED }
393 var neg: i64 = 0
394 if b[i] == 45 { neg = 1; i = i + 1 }
395 var digits: i64 = 0
396 var v: i64 = 0
397 var run: i64 = 1
398 while run == 1 {
399 if i >= n { run = 0 } else {
400 let c: i64 = b[i] as i64
401 if c < 48 { run = 0 } else { if c > 57 { run = 0 } else {
402 let d: i64 = c - 48
403 if v > (FF_FIT_I64_POS_LIMIT - d) / 10 { return FF_FIT_MALFORMED }
404 v = v * 10 + d; digits = digits + 1; i = i + 1
405 } }
406 }
407 }
408 if digits == 0 { return FF_FIT_MALFORMED }
409 if i >= n { return FF_FIT_MALFORMED }
410 if b[i] != 10 { return FF_FIT_MALFORMED }
411 i = i + 1
412 if neg == 1 { v = 0 - v }
413 T[k] = v; k = k + 1
414 }
415 if i != n { return FF_FIT_MALFORMED }
416 if C.valid(T, C.np) != 1 { return FF_FIT_INVALID }
417 return C.np
418}
419// INTERNAL: ff_load_verified has validated the contract and bounded this owned buffer.
420// Generic callers must use ff_load_verified rather than bypass its external-input checks.
421func ff_parse_verified(b: *u8, n: i64, P: *i64, C: *FfFitContract) -> i64 {
422 if n < FF_FIT_MAGIC_BYTES { return FF_FIT_LEGACY_UNVERIFIED }
423 var i: i64 = 0
424 while i < FF_FIT_MAGIC_BYTES { if b[i] != C.header[i] { return FF_FIT_LEGACY_UNVERIFIED } i = i + 1 }
425 if n < C.header_bytes { return FF_FIT_CONTRACT_MISMATCH }
426 i = 0
427 while i < C.header_bytes { if b[i] != C.header[i] { return FF_FIT_CONTRACT_MISMATCH } i = i + 1 }
428 if C.np > (n - C.header_bytes) / 2 { return FF_FIT_MALFORMED }
429 let T: *i64 = sys_mmap_shared(C.np * FF_I64) as *i64
430 if (T as i64) <= 0 { return FF_FIT_IO }
431 let rc: i64 = ff_values_verified(b, n, T, C)
432 if rc == C.np { var k: i64 = 0; while k < C.np { P[k] = T[k]; k = k + 1 } }
433 sys_munmap_direct(T as *u8, C.np * FF_I64)
434 return rc
435}
436func ff_load_verified(path: *u8, P: *i64, C: *FfFitContract) -> i64 {
437 if ff_contract_ok(C) != 1 { return FF_FIT_CONTRACT_MISMATCH }
438 let fd: i64 = sys_openat_rd(path)
439 if fd < 0 { return FF_FIT_IO }
440 let n: i64 = sys_lseek(fd, 0, 2)
441 let limit: i64 = C.header_bytes + C.np * FF_FIT_I64_DIGITS
442 if n < 0 { sys_close(fd); return FF_FIT_IO }
443 if n > limit { sys_close(fd); return FF_FIT_MALFORMED }
444 if sys_lseek(fd, 0, 0) != 0 { sys_close(fd); return FF_FIT_IO }
445 let b: *u8 = sys_mmap_shared(n + 1)
446 if (b as i64) <= 0 { sys_close(fd); return FF_FIT_IO }
447 var total: i64 = 0
448 while total < n {
449 let got: i64 = sys_read(fd, ((b as i64) + total) as *u8, n - total)
450 if got <= 0 { sys_close(fd); sys_munmap_direct(b, n + 1); return FF_FIT_IO }
451 total = total + got
452 }
453 let extra: i64 = sys_read(fd, ((b as i64) + n) as *u8, 1)
454 sys_close(fd)
455 if extra != 0 { sys_munmap_direct(b, n + 1); return FF_FIT_MALFORMED }
456 let rc: i64 = ff_parse_verified(b, n, P, C)
457 sys_munmap_direct(b, n + 1)
458 return rc
459}
460func ff_save_verified(path: *u8, P: *i64, C: *FfFitContract) -> i64 {
461 if ff_contract_ok(C) != 1 { return FF_FIT_CONTRACT_MISMATCH }
462 if C.valid(P, C.np) != 1 { return FF_FIT_INVALID }
463 var plen: i64 = 0
464 while path[plen] != 0 { if plen >= FF_FIT_ATOMIC_PATH_CAP - 1 { return FF_FIT_IO } plen = plen + 1 }
465 let b: *u8 = sys_mmap_shared(C.header_bytes + C.np * FF_FIT_I64_DIGITS)
466 if (b as i64) <= 0 { return FF_FIT_IO }
467 var o: i64 = 0
468 while o < C.header_bytes { b[o] = C.header[o]; o = o + 1 }
469 var k: i64 = 0
470 while k < C.np {
471 if P[k] < (0 - FF_FIT_I64_POS_LIMIT) { sys_munmap_direct(b, C.header_bytes + C.np * FF_FIT_I64_DIGITS); return FF_FIT_MALFORMED }
472 o = ff_wint(b, o, P[k]); b[o] = 10 as u8; o = o + 1; k = k + 1
473 }
474 let rc: i64 = atomic_rewrite(path, b, o)
475 sys_munmap_direct(b, C.header_bytes + C.np * FF_FIT_I64_DIGITS)
476 if rc != 0 { return FF_FIT_IO }
477 return o
478}
479
480func ff_descend_checked(c: *FfCtx, P: *i64, passes: i64, step0: i64, verbose: i64, valid: func(*i64,i64) -> i64) -> i64 {
481 if valid(P, c.np) != 1 { return FF_FIT_INVALID }
482 if step0 <= 0 { return FF_FIT_INVALID }
483 if passes < 0 { return FF_FIT_INVALID }
484 var best: i64 = ff_score2(c, P)
485 var step: i64 = step0
486 var pass: i64 = 0
487 while pass < passes {
488 var k: i64 = 0
489 while k < c.np {
490 if c.live(k) == 1 {
491 let orig: i64 = P[k]
492 var up: i64 = best
493 if orig <= FF_FIT_I64_POS_LIMIT - step {
494 P[k] = orig + step
495 if valid(P, c.np) == 1 { up = ff_score2(c, P) }
496 }
497 var dn: i64 = best
498 if orig >= (0 - FF_FIT_I64_POS_LIMIT) + step {
499 P[k] = orig - step
500 if valid(P, c.np) == 1 { dn = ff_score2(c, P) }
501 }
502 P[k] = orig
503 if up < best { if up <= dn { best = up; P[k] = orig + step } }
504 if dn < best { if dn < up { best = dn; P[k] = orig - step } }
505 }
506 k = k + 1
507 }
508 if verbose == 1 {
509 ff_puts("{\x22pass\x22:" as *u8); ff_pn(pass)
510 ff_puts(",\x22step\x22:" as *u8); ff_pn(step)
511 ff_puts(",\x22best_centi\x22:" as *u8); ff_pn(best)
512 ff_puts("}\n" as *u8)
513 }
514 step = step/2
515 if step < 1 { step = 1 }
516 pass = pass + 1
517 }
518 return best
519}