code wiki / _hdl_build / nx_qabench.nx
nx_qabench.nx source
↩ module page · 2146 lines · 95267 B
1// nx_qabench.nx -- the QA-benchmark LADDER (operator 2026-07-07: "use others like beerqa, climb the ladder to
2// the top"). Runs the sovereign researcher's mechanical retrieve+extract+multi-hop pipeline (shared engine =
3// nx_qabench_engine, extracted from nx_drbench) across a DIFFICULTY GRADIENT and prints ONE scoreboard:
4// RUNG 1-hop = SQuAD v1.1 (single context paragraph -> isolates EXTRACTION from retrieval; should score HIGH)
5// RUNG 2-hop = HotpotQA distractor (retrieve 2 of 10 paras + bridge reasoning -> the reasoning wall)
6// Same F1/EM metric (nx_qa_score_lib, integer permille), same extractor -> the F1 DROP as hops rise is the
7// honest, MEASURED "where mechanical ends and reasoning/LLM begins" signal. Each benchmark: T-parse + T-negctl
8// (rotated-gold F1 ~0 = metric not rigged). Formats dispatched by `fmt` (0=hotpot distractor, 1=squad single).
9// expect_exit: 0 license_tier: ORIGINAL
10import "nx_qabench_engine.nx"
11import "nx_qwen_extract.nx" // R5: opt-in pretrained-Qwen extraction (marker file knowledge/index/qwen_reader.on)
12const SEM_MAGIC_260000: i64 = 260000
13const SEM_MAGIC_32760: i64 = 32760
14const SEM_MAGIC_4096: i64 = 4096
15const SEM_MAGIC_536870912: i64 = 536870912
16const SEM_MAGIC_262144: i64 = 262144
17const SEM_MAGIC_4000: i64 = 4000
18const SEM_MAGIC_200000: i64 = 200000
19const SEM_MAGIC_2000: i64 = 2000
20const SEM_MAGIC_8000: i64 = 8000
21const SEM_MAGIC_1024: i64 = 1024
22const SEM_MAGIC_2000000000: i64 = 2000000000
23const SEM_MAGIC_16777216: i64 = 16777216
24const SEM_MAGIC_402653184: i64 = 402653184
25const SEM_MAGIC_10000: i64 = 10000
26const SEM_MAGIC_2026: i64 = 2026
27const SEM_MAGIC_2500: i64 = 2500
28const SEM_MAGIC_1000000: i64 = 1000000
29const SEM_MAGIC_2048: i64 = 2048
30const SEM_MAGIC_32768: i64 = 32768
31const SEM_MAGIC_1048576: i64 = 1048576
32const SEM_MAGIC_524288: i64 = 524288
33const SEM_MAGIC_8192: i64 = 8192
34const SEM_MAGIC_65536: i64 = 65536
35const SEM_MAGIC_8388608: i64 = 8388608
36const SEM_MAGIC_16384: i64 = 16384
37const SEM_MAGIC_3999: i64 = 3999
38
39// ---- SQuAD: split a single context string into sentences in sb/so/sl/sp (all para 0). returns nsent ----
40// is src[start..start+ln) prose (real answerable text) vs web CHROME/wikitext/JSON? Real web pages (esp.
41// Wikipedia) carry nav menus, language lists, and infobox markup ({{...}}, [[...]], {"wt":...}) that pollute
42// retrieval -- the measured binding constraint for live web research. Prose = letter-dense, sane length, few
43// markup markers. General (not site-specific): keeps the article body, drops the scaffolding.
44func qa_is_prose(src: *u8, start: i64, ln: i64) -> i64 {
45 if ln < 25 { return 0 }
46 if ln > 600 { return 0 } // giant runs w/o '. ' = nav blob / language list
47 var letters: i64 = 0
48 var spaces: i64 = 0
49 var bad: i64 = 0
50 var i: i64 = 0
51 while i < ln {
52 let c: i64 = src[start+i] as i64
53 if c >= 65 { if c <= 90 { letters = letters + 1 } }
54 if c >= 97 { if c <= 122 { letters = letters + 1 } }
55 if c == 32 { spaces = spaces + 1 }
56 if c == 123 { bad = bad + 1 } // {
57 if c == 125 { bad = bad + 1 } // }
58 if c == 124 { bad = bad + 1 } // |
59 if c == 61 { bad = bad + 1 } // =
60 if c == 91 { bad = bad + 1 } // [
61 if c == 93 { bad = bad + 1 } // ]
62 i = i + 1
63 }
64 if bad > 2 { return 0 } // wikitext/JSON markup
65 if (letters + spaces) * 100 < ln * 78 { return 0 } // < 78% letters+spaces = not prose
66 var wc: i64 = spaces + 1
67 if wc < 5 { return 0 } // need >= ~5 words to be a real sentence
68 return 1
69}
70
71// copy [start,start+ln) into dst, DROPPING content inside ( ) and [ ] (pronunciation IPA / citation / date
72// parentheticals that pollute real web prose and get whole sentences dropped by the letter-ratio filter).
73// flat ifs (no else-chain: deep else{if} miscompiles per nx_cc). returns cleaned length.
74func qa_clean_sentence(src: *u8, start: i64, ln: i64, dst: *u8) -> i64 {
75 var depth: i64 = 0
76 var o: i64 = 0
77 var k: i64 = 0
78 while k < ln {
79 let c: i64 = src[start+k] as i64
80 var open: i64 = 0
81 if c==40 { open=1 } // (
82 if c==91 { open=1 } // [
83 var close: i64 = 0
84 if c==41 { close=1 } // )
85 if c==93 { close=1 } // ]
86 if open==1 { depth=depth+1 }
87 if close==1 { if depth>0 { depth=depth-1 } }
88 if open==0 { if close==0 { if depth==0 { if o<1000 { dst[o]=src[start+k]; o=o+1 } } } }
89 k=k+1
90 }
91 dst[o]=(0 as u8)
92 return o
93}
94
95// prose-filtered sentence splitter for the LIVE answer mode (higher cap; drops non-prose). Isolated from the
96// benchmark's qab_split_ctx (whose inputs are already clean JSON-extracted contexts).
97func qa_split_prose(g: *i64, src: *u8, slen: i64) -> i64 {
98 let sb: *u8 = g[15] as *u8
99 let so: *i64 = g[16] as *i64
100 let sl: *i64 = g[17] as *i64
101 let sp: *i64 = g[18] as *i64
102 var ns: i64 = 0
103 var bump: i64 = 0
104 var start: i64 = 0
105 var i: i64 = 0
106 while i < slen {
107 var boundary: i64 = 0
108 let c: i64 = src[i] as i64
109 if c==46 { boundary=1 }
110 if c==63 { boundary=1 }
111 if c==33 { boundary=1 }
112 if boundary==1 {
113 var nxsp: i64 = 1
114 if i+1 < slen { let d: i64 = src[i+1] as i64; if d!=32 { if d!=10 { nxsp=0 } } }
115 if nxsp==0 { boundary=0 }
116 }
117 if boundary==1 {
118 let ln: i64 = (i+1) - start
119 let cbuf: *u8 = g[130] as *u8 // clean parenthetical/IPA/citation junk first
120 let cln: i64 = qa_clean_sentence(src, start, ln, cbuf)
121 if qa_is_prose(cbuf, 0, cln) == 1 { if ns < 400 { if bump+cln < SEM_MAGIC_260000 {
122 var w: i64 = 0
123 while w < cln { sb[bump+w] = cbuf[w]; w = w + 1 }
124 sb[bump+cln] = (0 as u8)
125 so[ns]=bump; sl[ns]=cln; sp[ns]=0; ns=ns+1; bump=bump+cln+1
126 } } }
127 var sk: i64 = i+1
128 var go: i64 = 1
129 while go == 1 { if sk < slen { if src[sk]==(32 as u8) { sk=sk+1 } else { go=0 } } else { go=0 } }
130 start = sk
131 i = sk
132 } else { i = i + 1 }
133 }
134 return ns
135}
136
137func qab_split_ctx(g: *i64, src: *u8, slen: i64) -> i64 {
138 let sb: *u8 = g[15] as *u8
139 let so: *i64 = g[16] as *i64
140 let sl: *i64 = g[17] as *i64
141 let sp: *i64 = g[18] as *i64
142 var ns: i64 = 0
143 var bump: i64 = 0
144 var start: i64 = 0
145 var i: i64 = 0
146 while i < slen {
147 var boundary: i64 = 0
148 let c: i64 = src[i] as i64
149 if c==46 { boundary=1 }
150 if c==63 { boundary=1 }
151 if c==33 { boundary=1 }
152 if boundary==1 {
153 var nxsp: i64 = 1
154 if i+1 < slen { let d: i64 = src[i+1] as i64; if d!=32 { if d!=10 { nxsp=0 } } }
155 if nxsp==0 { boundary=0 }
156 }
157 if boundary==1 {
158 let ln: i64 = (i+1) - start
159 if ln > 2 { if ns < 80 { if bump+ln < SEM_MAGIC_260000 {
160 var w: i64 = 0
161 while w < ln { sb[bump+w] = src[start+w]; w = w + 1 }
162 sb[bump+ln] = (0 as u8) // terminate: qs_norm reads to null; sb is reused across rows
163 so[ns]=bump; sl[ns]=ln; sp[ns]=0; ns=ns+1; bump=bump+ln+1
164 } } }
165 var sk: i64 = i+1
166 var go: i64 = 1
167 while go == 1 { if sk < slen { if src[sk]==(32 as u8) { sk=sk+1 } else { go=0 } } else { go=0 } }
168 start = sk
169 i = sk
170 } else { i = i + 1 }
171 }
172 if start < slen { let ln2: i64 = slen - start
173 if ln2 > 2 { if ns < 80 { if bump+ln2 < SEM_MAGIC_260000 {
174 var w2: i64 = 0
175 while w2 < ln2 { sb[bump+w2] = src[start+w2]; w2 = w2 + 1 }
176 sb[bump+ln2] = (0 as u8)
177 so[ns]=bump; sl[ns]=ln2; sp[ns]=0; ns=ns+1
178 } } }
179 }
180 return ns
181}
182
183// match a JSON bracket: pos at '[' or '{' -> index of the matching close (string-aware). -1 if unbalanced.
184func db_match_bracket(g: *i64, pos: i64) -> i64 {
185 let open: i64 = db_b(g, pos)
186 var close: i64 = 93
187 if open == 123 { close = 125 }
188 var depth: i64 = 0
189 var i: i64 = pos
190 var instr: i64 = 0
191 while i < g[1] {
192 let c: i64 = db_b(g, i)
193 if instr == 1 {
194 if c == 92 { i = i + 1 } else { if c == 34 { instr = 0 } }
195 } else {
196 if c == 34 { instr = 1 } else {
197 if c == open { depth = depth + 1 }
198 if c == close { depth = depth - 1; if depth == 0 { return i } }
199 }
200 }
201 i = i + 1
202 }
203 return 0 - 1
204}
205
206// append sentences from src[0..slen) into sb/so/sl/sp with sp=para; st=[ns,bump] updated in place.
207func qab_split_append(g: *i64, src: *u8, slen: i64, para: i64, st: *i64) -> i64 {
208 let sb: *u8 = g[15] as *u8
209 let so: *i64 = g[16] as *i64
210 let sl: *i64 = g[17] as *i64
211 let sp: *i64 = g[18] as *i64
212 var ns: i64 = st[0]
213 var bump: i64 = st[1]
214 var start: i64 = 0
215 var i: i64 = 0
216 while i < slen {
217 var boundary: i64 = 0
218 let c: i64 = src[i] as i64
219 if c==46 { boundary=1 }
220 if c==63 { boundary=1 }
221 if c==33 { boundary=1 }
222 if boundary==1 {
223 var nxsp: i64 = 1
224 if i+1 < slen { let d: i64 = src[i+1] as i64; if d!=32 { if d!=10 { nxsp=0 } } }
225 if nxsp==0 { boundary=0 }
226 }
227 if boundary==1 {
228 let ln: i64 = (i+1) - start
229 if ln > 2 { if ns < 1000 { if bump+ln < SEM_MAGIC_260000 {
230 var w: i64 = 0
231 while w < ln { sb[bump+w] = src[start+w]; w = w + 1 }
232 sb[bump+ln] = (0 as u8) // terminate: qs_norm reads to null; sb is reused across rows
233 so[ns]=bump; sl[ns]=ln; sp[ns]=para; ns=ns+1; bump=bump+ln+1
234 } } }
235 var sk: i64 = i+1
236 var go: i64 = 1
237 while go == 1 { if sk < slen { if src[sk]==(32 as u8) { sk=sk+1 } else { go=0 } } else { go=0 } }
238 start = sk
239 i = sk
240 } else { i = i + 1 }
241 }
242 if start < slen { let ln2: i64 = slen - start
243 if ln2 > 2 { if ns < 1000 { if bump+ln2 < SEM_MAGIC_260000 {
244 var w2: i64 = 0
245 while w2 < ln2 { sb[bump+w2] = src[start+w2]; w2 = w2 + 1 }
246 sb[bump+ln2] = (0 as u8)
247 so[ns]=bump; sl[ns]=ln2; sp[ns]=para; ns=ns+1
248 } } }
249 }
250 st[0]=ns; st[1]=bump
251 return 0
252}
253
254// ==================== R3-LITE: SEMANTIC sentence selection (MODE2) ====================
255// Distributional co-occurrence embedding built from the question's OWN paragraphs (the nx_distrib_embed
256// method, per-row, zero training, sovereign): vocab of content words -> windowed co-occurrence matrix ->
257// sentence/question vectors = sums of member-word rows -> integer cosine. Extraction UNCHANGED -> isolates
258// the SELECTION variable.
259// ★MEASURED 2026-07-08 (kept as the R3 baseline column, NOT a win): F1_SEM vs F1_lex = SQuAD 149 vs 158,
260// HotpotQA 130 vs 146, MuSiQue 24 vs 45 -- per-question co-occurrence is WORSE everywhere and degrades MOST
261// where paragraphs are many (distractor mass poisons the stats; the question vector drifts to hub sentences).
262// 3rd principled climb, 3rd measured negative => the semantic wall needs CORPUS-LEVEL embeddings (R3b-scale
263// 519-doc thread) or a trained bi-encoder (R1-adjacent), NOT per-row stats. This column is the plug-in point:
264// swap sem_build_cooc's source for a persisted corpus matrix and the delta shows here.
265// ctx slots: 60 C(vc*vc i64) 61 qvec 62 svec 63 voff 64 vlen 65 vcount 66 vbuf 67 vbump
266// 68 pred2(256B) 69 vidx(per-sentence vocab ids)
267const SEM_VMAX: i64 = 1024
268const SEM_W: i64 = 6
269
270func sem_isqrt(v: i64) -> i64 {
271 if v <= 0 { return 0 }
272 var x: i64 = v
273 var y: i64 = (x + 1) / 2
274 while y < x { x = y; let q: i64 = v / x; y = (x + q) / 2 }
275 return x
276}
277
278// find token (buf,off,len) in the vocab; -1 if absent
279func sem_vfind(g: *i64, buf: *u8, off: i64, len: i64) -> i64 {
280 let vbuf: *u8 = g[66] as *u8
281 let voff: *i64 = g[63] as *i64
282 let vlen: *i64 = g[64] as *i64
283 var i: i64 = 0
284 while i < g[65] {
285 if qs_tok_eq2(buf, off, len, vbuf, voff[i], vlen[i]) == 1 { return i }
286 i = i + 1
287 }
288 return 0-1
289}
290
291// find-or-append; returns idx or -1 (caps hit)
292func sem_vadd(g: *i64, buf: *u8, off: i64, len: i64) -> i64 {
293 let f: i64 = sem_vfind(g, buf, off, len)
294 if f >= 0 { return f }
295 if g[65] >= SEM_VMAX { return 0-1 }
296 if g[67] + len + 1 >= SEM_MAGIC_32760 { return 0-1 }
297 let vbuf: *u8 = g[66] as *u8
298 let voff: *i64 = g[63] as *i64
299 let vlen: *i64 = g[64] as *i64
300 let idx: i64 = g[65]
301 let bump: i64 = g[67]
302 var w: i64 = 0
303 while w < len { vbuf[bump+w] = buf[off+w]; w = w + 1 }
304 voff[idx] = bump; vlen[idx] = len
305 g[65] = idx + 1
306 g[67] = bump + len + 1
307 return idx
308}
309
310// vocab = question content words + all sentence content words (stop-filtered, len>=2)
311func sem_build_vocab(g: *i64) -> i64 {
312 g[65] = 0
313 g[67] = 0
314 let qnb: *u8 = g[28] as *u8
315 let qto: *i64 = g[29] as *i64
316 let qtl: *i64 = g[30] as *i64
317 let qk: *i64 = g[32] as *i64
318 var k: i64 = 0
319 while k < g[31] {
320 if qk[k] == 1 { if qtl[k] >= 2 { sem_vadd(g, qnb, qto[k], qtl[k]) } }
321 k = k + 1
322 }
323 let nb: *u8 = g[20] as *u8
324 let tko: *i64 = g[23] as *i64
325 let tkl: *i64 = g[24] as *i64
326 let tks: *i64 = g[25] as *i64
327 let tkc: *i64 = g[26] as *i64
328 var s: i64 = 0
329 while s < g[19] {
330 var j: i64 = 0
331 while j < tkc[s] {
332 let ti: i64 = tks[s] + j
333 if tkl[ti] >= 2 { if db_is_stop(nb, tko[ti], tkl[ti]) == 0 { sem_vadd(g, nb, tko[ti], tkl[ti]) } }
334 j = j + 1
335 }
336 s = s + 1
337 }
338 return g[65]
339}
340
341// windowed co-occurrence over each sentence's vocab-member tokens
342func sem_build_cooc(g: *i64) -> i64 {
343 let C: *i64 = g[60] as *i64
344 let vc: i64 = g[65]
345 var z: i64 = 0
346 let zn: i64 = vc * vc
347 while z < zn { C[z] = 0; z = z + 1 }
348 let nb: *u8 = g[20] as *u8
349 let tko: *i64 = g[23] as *i64
350 let tkl: *i64 = g[24] as *i64
351 let tks: *i64 = g[25] as *i64
352 let tkc: *i64 = g[26] as *i64
353 let vidx: *i64 = g[69] as *i64
354 var s: i64 = 0
355 while s < g[19] {
356 var n: i64 = tkc[s]
357 if n > 250 { n = 250 }
358 var j: i64 = 0
359 while j < n {
360 let ti: i64 = tks[s] + j
361 let v: i64 = sem_vfind(g, nb, tko[ti], tkl[ti])
362 vidx[j] = v
363 j = j + 1
364 }
365 var a: i64 = 0
366 while a < n {
367 if vidx[a] >= 0 {
368 var b: i64 = a + 1
369 var bend: i64 = a + SEM_W
370 if bend > n-1 { bend = n-1 }
371 while b <= bend {
372 if vidx[b] >= 0 {
373 let ia: i64 = vidx[a]*vc + vidx[b]
374 let ib: i64 = vidx[b]*vc + vidx[a]
375 C[ia] = C[ia] + 1
376 C[ib] = C[ib] + 1
377 }
378 b = b + 1
379 }
380 }
381 a = a + 1
382 }
383 s = s + 1
384 }
385 return 0
386}
387
388// vec += C-row of vocab id t
389func sem_addrow(g: *i64, vec: *i64, t: i64) -> i64 {
390 let C: *i64 = g[60] as *i64
391 let vc: i64 = g[65]
392 let base: i64 = t * vc
393 var k: i64 = 0
394 while k < vc { vec[k] = vec[k] + C[base + k]; k = k + 1 }
395 return 0
396}
397
398// question vector = sum of rows of its content words present in vocab. returns #hits.
399func sem_qvec(g: *i64, vec: *i64) -> i64 {
400 let vc: i64 = g[65]
401 var k: i64 = 0
402 while k < vc { vec[k] = 0; k = k + 1 }
403 let qnb: *u8 = g[28] as *u8
404 let qto: *i64 = g[29] as *i64
405 let qtl: *i64 = g[30] as *i64
406 let qk: *i64 = g[32] as *i64
407 var hits: i64 = 0
408 var i: i64 = 0
409 while i < g[31] {
410 if qk[i] == 1 {
411 let v: i64 = sem_vfind(g, qnb, qto[i], qtl[i])
412 if v >= 0 { sem_addrow(g, vec, v); hits = hits + 1 }
413 }
414 i = i + 1
415 }
416 return hits
417}
418
419func sem_svec(g: *i64, s: i64, vec: *i64) -> i64 {
420 let vc: i64 = g[65]
421 var k: i64 = 0
422 while k < vc { vec[k] = 0; k = k + 1 }
423 let nb: *u8 = g[20] as *u8
424 let tko: *i64 = g[23] as *i64
425 let tkl: *i64 = g[24] as *i64
426 let tks: *i64 = g[25] as *i64
427 let tkc: *i64 = g[26] as *i64
428 var hits: i64 = 0
429 var j: i64 = 0
430 while j < tkc[s] {
431 let ti: i64 = tks[s] + j
432 let v: i64 = sem_vfind(g, nb, tko[ti], tkl[ti])
433 if v >= 0 { sem_addrow(g, vec, v); hits = hits + 1 }
434 j = j + 1
435 }
436 return hits
437}
438
439// integer cosine in permille
440func sem_cos(a: *i64, b: *i64, vc: i64) -> i64 {
441 var dot: i64 = 0
442 var na: i64 = 0
443 var nb2: i64 = 0
444 var k: i64 = 0
445 while k < vc {
446 dot = dot + a[k]*b[k]
447 na = na + a[k]*a[k]
448 nb2 = nb2 + b[k]*b[k]
449 k = k + 1
450 }
451 if dot <= 0 { return 0 }
452 let d1: i64 = sem_isqrt(na)
453 let d2: i64 = sem_isqrt(nb2)
454 return (dot/(d1+1))*1000/(d2+1)
455}
456
457// ---------- PPMI-soft path (loads knowledge/index/semppmi_v1.bin when present; else per-row fallback) ----------
458// slots: 70 blob base, 71 nv, 72 nt, 73 vh, 74 ridx, 75 nrm2, 76 tctx, 77 tval, 78 loaded-flag
459func ppmi_load(g: *i64) -> i64 {
460 g[78] = 0
461 let fd: i64 = sys_openat_rd("knowledge/index/semppmi_v1.bin" as *u8)
462 if fd < 0 { return 0 }
463 // TWO-PHASE read (no fixed whole-file cap -- the exact "grew past a fixed buffer" debt class): read the
464 // 32B header first, size the blob from nv/nt, then read the rest. Sanity-capped at 512MB.
465 let hdrb: *u8 = sys_mmap(SEM_MAGIC_4096)
466 var hgot: i64 = 0
467 var hr: i64 = 1
468 while hr > 0 { if hgot >= 32 { hr = 0 } else { hr = sys_read(fd, (hdrb as i64 + hgot) as *u8, 32 - hgot); if hr > 0 { hgot = hgot + hr } } }
469 if hgot < 32 { sys_close(fd); return 0 }
470 let hh: *i64 = (hdrb as i64 + 8) as *i64
471 let hnv: i64 = hh[0]
472 let hnt: i64 = hh[1]
473 let need0: i64 = 32 + (hnv*8) + ((hnv+1)*8) + (hnv*8) + (hnt*8) + (hnt*8)
474 if need0 <= 32 { sys_close(fd); return 0 }
475 if need0 > SEM_MAGIC_536870912 { sys_close(fd); return 0 }
476 let cap: i64 = need0 + SEM_MAGIC_4096
477 let blob: *u8 = sys_mmap(cap)
478 var i0: i64 = 0
479 while i0 < 32 { blob[i0] = hdrb[i0]; i0 = i0 + 1 }
480 var total: i64 = 32
481 var r: i64 = 1
482 while r > 0 {
483 let left: i64 = need0 - total
484 if left <= 0 { r = 0 } else {
485 var want: i64 = SEM_MAGIC_262144
486 if want > left { want = left }
487 r = sys_read(fd, (blob as i64 + total) as *u8, want)
488 if r > 0 { total = total + r }
489 }
490 }
491 sys_close(fd)
492 if total < 64 { return 0 }
493 if blob[0] != (78 as u8) { return 0 }
494 if blob[6] != (49 as u8) { return 0 }
495 let hi: *i64 = (blob as i64 + 8) as *i64
496 let nv: i64 = hi[0]
497 let nt: i64 = hi[1]
498 let need: i64 = 32 + (nv*8) + ((nv+1)*8) + (nv*8) + (nt*8) + (nt*8)
499 if total < need { return 0 }
500 g[70] = blob as i64
501 g[71] = nv
502 g[72] = nt
503 var off: i64 = 32
504 g[73] = (blob as i64) + off; off = off + nv*8
505 g[74] = (blob as i64) + off; off = off + (nv+1)*8
506 g[75] = (blob as i64) + off; off = off + nv*8
507 g[76] = (blob as i64) + off; off = off + nt*8
508 g[77] = (blob as i64) + off
509 g[78] = 1
510 return 1
511}
512
513// cosine between two PPMI rows (sorted sparse merge), permille
514func ppmi_cos(g: *i64, a: i64, b: i64) -> i64 {
515 let ridx: *i64 = g[74] as *i64
516 let nrm2: *i64 = g[75] as *i64
517 let tctx: *i64 = g[76] as *i64
518 let tval: *i64 = g[77] as *i64
519 var ia: i64 = ridx[a]
520 var ib: i64 = ridx[b]
521 let ea: i64 = ridx[a+1]
522 let eb: i64 = ridx[b+1]
523 var dot: i64 = 0
524 while ia < ea {
525 if ib >= eb { ia = ea } else {
526 if tctx[ia] == tctx[ib] { dot = dot + tval[ia]*tval[ib]; ia = ia + 1; ib = ib + 1 }
527 else { if tctx[ia] < tctx[ib] { ia = ia + 1 } else { ib = ib + 1 } }
528 }
529 }
530 if dot <= 0 { return 0 }
531 let d1: i64 = sem_isqrt(nrm2[a])
532 let d2: i64 = sem_isqrt(nrm2[b])
533 if d1 == 0 { return 0 }
534 if d2 == 0 { return 0 }
535 var cv: i64 = (dot*1000)/(d1*d2)
536 if cv > 1000 { cv = 1000 }
537 return cv
538}
539
540// resolve token (buf,off,len) -> PPMI vocab id or -1
541func ppmi_wid(g: *i64, buf: *u8, off: i64, len: i64) -> i64 {
542 let hv: i64 = db_semhash(buf, off, len)
543 return db_bsearch_i64(g[73] as *i64, g[71], hv)
544}
545
546// PPMI-SOFT sentence selection: score(s) = SUM over question content words q of MAX over sentence words w of
547// cos_ppmi(q,w) -- soft lexical alignment ("won" aligns to "defeated"). Identity pairs count 1000 (exact match
548// keeps its full weight). RETRIEVE-THEN-READ scope: when pa>=0, only sentences in paragraphs {pa,pb} are
549// scored (paragraph retrieval stays IDF-lexical -- semantics only refines WITHIN the retrieved scope; global
550// soft-matching inflated distractor hits on 20-para MuSiQue). Returns best sentence or -1.
551func ppmi_best_sentence(g: *i64, pa: i64, pb: i64) -> i64 {
552 let qnb: *u8 = g[28] as *u8
553 let qto: *i64 = g[29] as *i64
554 let qtl: *i64 = g[30] as *i64
555 let qk: *i64 = g[32] as *i64
556 let nb: *u8 = g[20] as *u8
557 let tko: *i64 = g[23] as *i64
558 let tkl: *i64 = g[24] as *i64
559 let tks: *i64 = g[25] as *i64
560 let tkc: *i64 = g[26] as *i64
561 // resolve question ids once
562 let qid: *i64 = g[61] as *i64 // reuse qvec buffer as id scratch
563 var nq: i64 = 0
564 var k: i64 = 0
565 while k < g[31] {
566 if qk[k] == 1 { if nq < 64 {
567 let wid: i64 = ppmi_wid(g, qnb, qto[k], qtl[k])
568 if wid >= 0 { qid[nq] = wid; nq = nq + 1 }
569 } }
570 k = k + 1
571 }
572 if nq < 1 { return 0-1 }
573 let sid: *i64 = g[62] as *i64 // per-sentence word ids scratch
574 let spp: *i64 = g[18] as *i64 // sentence -> paragraph
575 var best: i64 = 0-1
576 var bs: i64 = 0
577 var s: i64 = 0
578 while s < g[19] {
579 var inscope: i64 = 1
580 if pa >= 0 { inscope = 0; if spp[s] == pa { inscope = 1 } if pb >= 0 { if spp[s] == pb { inscope = 1 } } }
581 if inscope == 1 {
582 var nsids: i64 = 0
583 var j: i64 = 0
584 while j < tkc[s] {
585 if nsids < 250 {
586 let ti: i64 = tks[s] + j
587 let wj: i64 = ppmi_wid(g, nb, tko[ti], tkl[ti])
588 sid[nsids] = wj
589 nsids = nsids + 1
590 }
591 j = j + 1
592 }
593 var score: i64 = 0
594 var qi: i64 = 0
595 while qi < nq {
596 var mx: i64 = 0
597 var si: i64 = 0
598 while si < nsids {
599 if sid[si] >= 0 {
600 var cv: i64 = 0
601 if sid[si] == qid[qi] { cv = 1000 } else { cv = ppmi_cos(g, qid[qi], sid[si]) }
602 if cv > mx { mx = cv }
603 }
604 si = si + 1
605 }
606 score = score + mx
607 qi = qi + 1
608 }
609 if score > bs { bs = score; best = s }
610 }
611 s = s + 1
612 }
613 return best
614}
615
616// MODE2 core: best sentence by semantic similarity to the question. PPMI-soft when the corpus file is loaded
617// (g[78]==1), SCOPED to the top-2 retrieved paragraphs (retrieve-then-read); else the per-row co-occurrence
618// fallback (the recorded measured-negative baseline). -1 if degenerate.
619func sem_best_sentence(g: *i64, pa: i64, pb: i64) -> i64 {
620 if g[78] == 1 { return ppmi_best_sentence(g, pa, pb) }
621 return sem_best_sentence_perrow(g)
622}
623
624func sem_best_sentence_perrow(g: *i64) -> i64 {
625 let vcnt: i64 = sem_build_vocab(g)
626 if vcnt < 8 { return 0-1 }
627 sem_build_cooc(g)
628 let qvec: *i64 = g[61] as *i64
629 let svec: *i64 = g[62] as *i64
630 let qh: i64 = sem_qvec(g, qvec)
631 if qh < 1 { return 0-1 }
632 var best: i64 = 0-1
633 var bs: i64 = 0
634 var s: i64 = 0
635 while s < g[19] {
636 let sh: i64 = sem_svec(g, s, svec)
637 if sh >= 1 {
638 let c: i64 = sem_cos(qvec, svec, g[65])
639 if c > bs { bs = c; best = s }
640 }
641 s = s + 1
642 }
643 return best
644}
645// ==================== end R3-LITE ====================
646
647// ---- MuSiQue (2-4 hop) row loader. -1 EOF, 0 bad, 1 ok. JSON key order: paragraphs, question,
648// question_decomposition (nested q/a -> bracket-skip), answer, answer_aliases, answerable.
649// gt* slots = answer_aliases (fmt=3 max-F1). Skips unanswerable rows. ----
650func qab_load_musique(g: *i64) -> i64 {
651 if db_find_key(g, "paragraphs" as *u8) < 0 { return 0-1 }
652 db_skip_ws(g)
653 if db_b(g, g[3]) != 91 { return 0 }
654 let arr_open: i64 = g[3]
655 let arr_end: i64 = db_match_bracket(g, arr_open)
656 if arr_end < 0 { return 0 }
657 let ptb: *u8 = g[11] as *u8
658 let pto: *i64 = g[12] as *i64
659 let ptl: *i64 = g[13] as *i64
660 let ctxb: *u8 = g[59] as *u8
661 let stbox: *i64 = g[100] as *i64 // preallocated (was sys_mmap per row -> leak over training)
662 stbox[0]=0; stbox[1]=0 // ns, sb bump
663 var np: i64 = 0
664 var tbump: i64 = 0
665 g[3] = arr_open + 1
666 var live: i64 = 1
667 while live == 1 {
668 if np >= 24 { live = 0 } else {
669 if db_find_key(g, "title" as *u8) < 0 { live = 0 } else {
670 if g[3] >= arr_end { live = 0 } else {
671 let tl: i64 = db_dec_str(g, (ptb as i64 + tbump) as *u8, SEM_MAGIC_4000 - tbump)
672 pto[np] = tbump; ptl[np] = tl; tbump = tbump + tl + 1
673 if db_find_key(g, "paragraph_text" as *u8) < 0 { live = 0 } else {
674 let cl: i64 = db_dec_str(g, ctxb, SEM_MAGIC_200000)
675 qab_split_append(g, ctxb, cl, np, stbox)
676 np = np + 1
677 }
678 } } }
679 }
680 g[14] = np
681 g[19] = stbox[0]
682 g[3] = arr_end + 1
683 // question
684 if db_find_key(g, "question" as *u8) < 0 { return 0 }
685 let ql: i64 = db_dec_str(g, g[4] as *u8, SEM_MAGIC_4000)
686 // skip question_decomposition (nested question/answer)
687 if db_find_key(g, "question_decomposition" as *u8) >= 0 {
688 db_skip_ws(g)
689 if db_b(g, g[3]) == 91 { let qd_end: i64 = db_match_bracket(g, g[3]); if qd_end >= 0 { g[3] = qd_end + 1 } }
690 }
691 // main answer
692 if db_find_key(g, "answer" as *u8) < 0 { return 0 }
693 let al: i64 = db_dec_str(g, g[5] as *u8, SEM_MAGIC_2000)
694 // answer_aliases -> gt* (refs)
695 var nref: i64 = 0
696 if db_find_key(g, "answer_aliases" as *u8) >= 0 { nref = db_parse_str_arr(g, g[7] as *u8, SEM_MAGIC_4000, g[8] as *i64, g[9] as *i64, 20) }
697 if nref < 0 { nref = 0 }
698 g[10] = nref
699 // answerable -> skip unanswerable
700 if db_find_key(g, "answerable" as *u8) >= 0 { db_skip_ws(g); if db_b(g, g[3]) == 102 { return 0 } } // 'f' = false
701 if ql <= 0 { return 0 }
702 if al <= 0 { return 0 }
703 if np < 2 { return 0 }
704 if g[19] <= 0 { return 0 }
705 return 1
706}
707
708// ---- SQuAD row loader. -1 EOF, 0 bad, 1 ok. gt* slots repurposed as ANSWER REFS (fmt=1). ----
709func qab_load_squad(g: *i64) -> i64 {
710 if db_find_key(g, "context" as *u8) < 0 { return 0-1 }
711 let ctxb: *u8 = g[59] as *u8
712 let cl: i64 = db_dec_str(g, ctxb, SEM_MAGIC_200000)
713 db_find_key(g, "question" as *u8)
714 let ql: i64 = db_dec_str(g, g[4] as *u8, SEM_MAGIC_4000)
715 db_find_key(g, "answers" as *u8)
716 db_find_key(g, "text" as *u8)
717 let nref: i64 = db_parse_str_arr(g, g[7] as *u8, SEM_MAGIC_4000, g[8] as *i64, g[9] as *i64, 20)
718 g[10] = nref
719 // primary gold answer -> a (g[5])
720 if nref > 0 {
721 let gtb: *u8 = g[7] as *u8
722 let gto: *i64 = g[8] as *i64
723 let gtl: *i64 = g[9] as *i64
724 let a: *u8 = g[5] as *u8
725 var w: i64 = 0
726 while w < gtl[0] { if w < 250 { a[w] = gtb[gto[0]+w] } w = w + 1 }
727 var e: i64 = gtl[0]; if e > 250 { e = 250 }
728 a[e] = (0 as u8)
729 }
730 // one paragraph, empty title, sentences = split(context)
731 g[14] = 1
732 let pto: *i64 = g[12] as *i64
733 let ptl: *i64 = g[13] as *i64
734 let ptb: *u8 = g[11] as *u8
735 pto[0]=0; ptl[0]=0; ptb[0]=(0 as u8)
736 let ns: i64 = qab_split_ctx(g, ctxb, cl)
737 g[19] = ns
738 if ql <= 0 { return 0 }
739 if nref < 1 { return 0 }
740 if ns <= 0 { return 0 }
741 return 1
742}
743
744// ---- HotpotQA distractor row loader (mirrors nx_drbench). -1 EOF, 0 bad, 1 ok. gt*=gold titles. ----
745func qab_load_hotpot(g: *i64) -> i64 {
746 if db_find_key(g, "question" as *u8) < 0 { return 0-1 }
747 let ql: i64 = db_dec_str(g, g[4] as *u8, SEM_MAGIC_4000)
748 db_find_key(g, "answer" as *u8)
749 let al: i64 = db_dec_str(g, g[5] as *u8, SEM_MAGIC_2000)
750 db_find_key(g, "type" as *u8)
751 db_dec_str(g, g[6] as *u8, 60)
752 db_find_key(g, "supporting_facts" as *u8)
753 db_find_key(g, "title" as *u8)
754 let gtb: *u8 = g[7] as *u8
755 let gto: *i64 = g[8] as *i64
756 let gtl: *i64 = g[9] as *i64
757 let rawgt: i64 = db_parse_str_arr(g, gtb, SEM_MAGIC_4000, gto, gtl, 30)
758 g[10] = 0
759 var gi: i64 = 0
760 while gi < rawgt {
761 var dup: i64 = 0
762 var gj: i64 = 0
763 while gj < g[10] {
764 if gtl[gi] == gtl[gj] {
765 var eq: i64 = 1
766 var w: i64 = 0
767 while w < gtl[gi] { if gtb[gto[gi]+w] != gtb[gto[gj]+w] { eq = 0 } w = w + 1 }
768 if eq == 1 { dup = 1 }
769 }
770 gj = gj + 1
771 }
772 if dup == 0 { let nd: i64 = g[10]; gto[nd] = gto[gi]; gtl[nd] = gtl[gi]; g[10] = nd + 1 }
773 gi = gi + 1
774 }
775 db_find_key(g, "context" as *u8)
776 db_find_key(g, "title" as *u8)
777 let np0: i64 = db_parse_str_arr(g, g[11] as *u8, SEM_MAGIC_8000, g[12] as *i64, g[13] as *i64, 16)
778 g[14] = np0
779 db_find_key(g, "sentences" as *u8)
780 let npara2: i64 = db_parse_sentences(g)
781 if ql <= 0 { return 0 }
782 if al <= 0 { return 0 }
783 if g[14] < 2 { return 0 }
784 if npara2 != g[14] { return 0 }
785 if g[19] <= 0 { return 0 }
786 if g[10] < 1 { return 0 }
787 return 1
788}
789
790func qab_load_row(g: *i64, fmt: i64) -> i64 {
791 if fmt == 1 { return qab_load_squad(g) }
792 if fmt == 3 { return qab_load_musique(g) }
793 return qab_load_hotpot(g)
794}
795
796// F1 (permille): for SQuAD (fmt=1) take MAX over answer refs (SQuAD protocol); else single gold answer.
797func qab_f1(g: *i64, fmt: i64, pred: *u8) -> i64 {
798 var best: i64 = qs_f1(pred, g[5] as *u8)
799 if fmt >= 1 {
800 let gtb: *u8 = g[7] as *u8
801 let gto: *i64 = g[8] as *i64
802 let gtl: *i64 = g[9] as *i64
803 var r: i64 = 0
804 while r < g[10] {
805 let f: i64 = qs_f1(pred, (gtb as i64 + gto[r]) as *u8)
806 if f > best { best = f }
807 r = r + 1
808 }
809 }
810 return best
811}
812func qab_em(g: *i64, fmt: i64, pred: *u8) -> i64 {
813 var best: i64 = qs_em(pred, g[5] as *u8)
814 if fmt >= 1 {
815 let gtb: *u8 = g[7] as *u8
816 let gto: *i64 = g[8] as *i64
817 let gtl: *i64 = g[9] as *i64
818 var r: i64 = 0
819 while r < g[10] {
820 let e: i64 = qs_em(pred, (gtb as i64 + gto[r]) as *u8)
821 if e > best { best = e }
822 r = r + 1
823 }
824 }
825 return best
826}
827
828// ==================== MODE-R: SUPERVISED SPAN READER (SQuAD-baseline lineage) ====================
829// The 6x-corpus datapoint proved SELECTION is no longer the binding stage -- EXTRACTION (which candidate span
830// to emit) is. This is the trained reader for that stage: a structured (averaged) perceptron over candidate
831// answer spans, exactly the Rajpurkar-2016 SQuAD-baseline recipe (candidate spans + lexical/type features +
832// a learned linear scorer, which got 51 F1 where sliding-window heuristics got 20). Sovereign/integer/no-float.
833// TRAIN on the qab_*_c* corpus rows (DISJOINT from the eval subsets) -> persist reader_v1.bin -> eval MODE-R.
834// Replaces db_extract's fixed pass-order (multiword>noncommon>any) with a learned ranking; the extractor's
835// candidate GENERATION is unchanged, so this isolates the scoring variable. Reports candidate-recall ceiling.
836// ctx: 79 loaded 80 w[NF] 81 featmat[300*NF] 82 meta[300] 84 predR 85 wsum[NF] 86 candtmp 87 avg-count
837// NF=14, featmat stride = NF*8 = 112. TWO principled scoring features were tried against the measured binding
838// stage (oracle 727 vs reader 251) and BOTH failed the linear perceptron: (a) positional proximity-to-anchor
839// (NF16) = NET-NEGATIVE (SQuAD 251->214); (b) corpus-PPMI semantic relatedness cand<->question (NF15) = INERT
840// (perceptron learned weight 0, F1 250~=251). => the gold-vs-distractor signal is NOT a linear function of
841// hand-features; the scoring gap needs LEARNED REPRESENTATIONS = the neural R1 no-float reader into this socket.
842const RD_NF: i64 = 14
843
844func rd_wants_entity(g: *i64) -> i64 {
845 let qnb: *u8 = g[28] as *u8
846 let qto: *i64 = g[29] as *i64
847 let qtl: *i64 = g[30] as *i64
848 var i: i64 = 0
849 while i < g[31] {
850 let o: i64 = qto[i]; let l: i64 = qtl[i]
851 if db_tok_is(qnb, o, l, "who" as *u8) == 1 { return 1 }
852 if db_tok_is(qnb, o, l, "whom" as *u8) == 1 { return 1 }
853 if db_tok_is(qnb, o, l, "whose" as *u8) == 1 { return 1 }
854 if db_tok_is(qnb, o, l, "which" as *u8) == 1 { return 1 }
855 i = i + 1
856 }
857 return 0
858}
859
860func rd_wants_place(g: *i64) -> i64 {
861 let qnb: *u8 = g[28] as *u8
862 let qto: *i64 = g[29] as *i64
863 let qtl: *i64 = g[30] as *i64
864 var i: i64 = 0
865 while i < g[31] {
866 let o: i64 = qto[i]; let l: i64 = qtl[i]
867 if db_tok_is(qnb, o, l, "where" as *u8) == 1 { return 1 }
868 if db_tok_is(qnb, o, l, "city" as *u8) == 1 { return 1 }
869 if db_tok_is(qnb, o, l, "country" as *u8) == 1 { return 1 }
870 if db_tok_is(qnb, o, l, "town" as *u8) == 1 { return 1 }
871 if db_tok_is(qnb, o, l, "state" as *u8) == 1 { return 1 }
872 if db_tok_is(qnb, o, l, "capital" as *u8) == 1 { return 1 }
873 if db_tok_is(qnb, o, l, "located" as *u8) == 1 { return 1 }
874 if db_tok_is(qnb, o, l, "venue" as *u8) == 1 { return 1 }
875 i = i + 1
876 }
877 return 0
878}
879
880// word count of candidate ci
881func rd_candwc(g: *i64, ci: i64) -> i64 {
882 let cnd: *u8 = g[48] as *u8
883 let cno: *i64 = g[49] as *i64
884 let cnl: *i64 = g[50] as *i64
885 let o: i64 = cno[ci]; let l: i64 = cnl[ci]
886 var wc: i64 = 1
887 var i: i64 = 0
888 while i < l { if cnd[o+i] == (32 as u8) { wc = wc + 1 } i = i + 1 }
889 return wc
890}
891
892// featurize candidate ci (db_candidates for its sentence must be current). best_s = the top sentence.
893func rd_feat(g: *i64, ci: i64, s: i64, best_s: i64, feat: *i64) -> i64 {
894 let cnt: *i64 = g[51] as *i64
895 let shit: *i64 = g[39] as *i64
896 let mw: i64 = db_cand_multiword(g, ci)
897 var isnum: i64 = 0
898 if cnt[ci] == 2 { isnum = 1 }
899 if cnt[ci] == 3 { isnum = 1 }
900 var isyr: i64 = 0
901 if cnt[ci] == 3 { isyr = 1 }
902 let ech: i64 = db_cand_is_echo(g, ci)
903 let com: i64 = db_cand_is_common(g, ci)
904 let wn: i64 = db_prefer_numeric(g)
905 let wy: i64 = db_wants_year(g)
906 let we: i64 = rd_wants_entity(g)
907 let wp: i64 = rd_wants_place(g)
908 let wc: i64 = rd_candwc(g, ci)
909 feat[0] = 10
910 var f1v: i64 = shit[s]
911 if f1v > 10 { f1v = 10 }
912 feat[1] = f1v
913 feat[2] = mw * 10
914 feat[3] = isnum * 10
915 feat[4] = isyr * 10
916 feat[5] = ech * 10
917 feat[6] = com * 10
918 var f7v: i64 = wc
919 if f7v > 6 { f7v = 6 }
920 feat[7] = f7v
921 var f8v: i64 = 0
922 if wn == 1 { if isnum == 1 { f8v = 10 } }
923 feat[8] = f8v
924 var f9v: i64 = 0
925 if we == 1 { if mw == 1 { f9v = 10 } }
926 feat[9] = f9v
927 var f10v: i64 = 0
928 if wp == 1 { if mw == 1 { f10v = 10 } }
929 feat[10] = f10v
930 var f11v: i64 = 0
931 if wy == 1 { if isyr == 1 { f11v = 10 } }
932 feat[11] = f11v
933 var f12v: i64 = 0
934 if s == best_s { f12v = 10 }
935 feat[12] = f12v
936 var f13v: i64 = 0
937 if ci == 0 { f13v = 5 }
938 feat[13] = f13v
939 return 0
940}
941
942func rd_score(g: *i64, feat: *i64) -> i64 {
943 let w: *i64 = g[80] as *i64
944 var acc: i64 = 0
945 var k: i64 = 0
946 while k < RD_NF { acc = acc + w[k]*feat[k]; k = k + 1 }
947 return acc
948}
949
950// reconstruct candidate ci string of sentence s into out (re-runs db_candidates for s). returns length.
951// out buffers are 2048B; a candidate can be up to ~2000B (db_candidates bump cap) so BOUND the copy at 250
952// (a real answer span is short; a longer candidate is never the gold and only needs a stable non-overflowing key).
953func rd_candstr(g: *i64, s: i64, ci: i64, out: *u8) -> i64 {
954 let so: *i64 = g[16] as *i64
955 let sl: *i64 = g[17] as *i64
956 db_candidates(g, so[s], sl[s])
957 if ci >= g[52] { out[0] = (0 as u8); return 0 }
958 let cnd: *u8 = g[48] as *u8
959 let cno: *i64 = g[49] as *i64
960 let cnl: *i64 = g[50] as *i64
961 let o: i64 = cno[ci]
962 var l: i64 = cnl[ci]
963 if l > 250 { l = 250 }
964 var i: i64 = 0
965 while i < l { out[i] = cnd[o+i]; i = i + 1 }
966 out[l] = (0 as u8)
967 return l
968}
969
970// training-side F1 (word-F1 permille, same formula as qs_f1) using PREALLOCATED ctx buffers -- qs_f1 mmaps 7
971// buffers per call, which is fine for eval's few-thousand calls but OOMs at training's millions. Tokenize gold
972// ONCE per row (rd_setgold), then score each candidate with rd_f1 -- zero per-call allocation.
973// slots: 92 gold-norm 93 gold-toff 94 gold-tlen 95 cand-norm 96 cand-toff 97 cand-tlen 98 used 99 gold-ntok
974func rd_setgold(g: *i64, gold: *u8) -> i64 {
975 let gnb: *u8 = g[92] as *u8
976 let gto: *i64 = g[93] as *i64
977 let gtl: *i64 = g[94] as *i64
978 let gl: i64 = qs_norm(gold, gnb)
979 let ng: i64 = qs_tok(gnb, gl, gto, gtl, 256)
980 g[99] = ng
981 return ng
982}
983func rd_f1(g: *i64, cand: *u8) -> i64 {
984 let cnb: *u8 = g[95] as *u8
985 let cto: *i64 = g[96] as *i64
986 let ctl: *i64 = g[97] as *i64
987 let cl: i64 = qs_norm(cand, cnb)
988 let np: i64 = qs_tok(cnb, cl, cto, ctl, 256)
989 let ng: i64 = g[99]
990 if np == 0 { if ng == 0 { return 1000 } return 0 }
991 if ng == 0 { return 0 }
992 let used: *i64 = g[98] as *i64
993 let gnb: *u8 = g[92] as *u8
994 let gto: *i64 = g[93] as *i64
995 let gtl: *i64 = g[94] as *i64
996 let common: i64 = qs_common(cnb, cto, ctl, np, gnb, gto, gtl, ng, used)
997 if common == 0 { return 0 }
998 let prec: i64 = (common*1000)/np
999 let rec: i64 = (common*1000)/ng
1000 return (2*prec*rec)/(prec+rec)
1001}
1002
1003// enumerate all candidates across the top-2 retrieved paragraphs; fill featmat + meta. returns count.
1004func rd_enumerate(g: *i64, top1: i64, top2: i64, best_s: i64) -> i64 {
1005 let so: *i64 = g[16] as *i64
1006 let sl: *i64 = g[17] as *i64
1007 let sp: *i64 = g[18] as *i64
1008 let meta: *i64 = g[82] as *i64
1009 var nc: i64 = 0
1010 var s: i64 = 0
1011 while s < g[19] {
1012 var inscope: i64 = 1
1013 if top1 >= 0 { inscope = 0; if sp[s] == top1 { inscope = 1 } if top2 >= 0 { if sp[s] == top2 { inscope = 1 } } }
1014 if inscope == 1 {
1015 db_candidates(g, so[s], sl[s])
1016 let ncand: i64 = g[52]
1017 var ci: i64 = 0
1018 while ci < ncand {
1019 if nc < 300 {
1020 let fp: *i64 = ((g[81] as i64) + nc*112) as *i64
1021 rd_feat(g, ci, s, best_s, fp)
1022 meta[nc] = s*SEM_MAGIC_1024 + ci
1023 nc = nc + 1
1024 }
1025 ci = ci + 1
1026 }
1027 }
1028 s = s + 1
1029 }
1030 return nc
1031}
1032
1033// ORACLE ceiling: max F1 over the enumerated candidate set vs gold. If this >> the reader's F1, SCORING has
1034// headroom; if ~= the reader's F1, candidate GENERATION (recall) is the wall. The measure-first probe that
1035// decides which stage to attack next (per the "measure the real path before optimizing" lesson).
1036func rd_oracle(g: *i64, top1: i64, top2: i64, best_s: i64, gold: *u8) -> i64 {
1037 let nc: i64 = rd_enumerate(g, top1, top2, best_s)
1038 if nc == 0 { return 0 }
1039 rd_setgold(g, gold)
1040 let meta: *i64 = g[82] as *i64
1041 let tmp: *u8 = g[86] as *u8
1042 var best: i64 = 0
1043 var i: i64 = 0
1044 while i < nc {
1045 let mv: i64 = meta[i]
1046 let s: i64 = mv / SEM_MAGIC_1024
1047 let ci: i64 = mv % SEM_MAGIC_1024
1048 rd_candstr(g, s, ci, tmp)
1049 let f: i64 = rd_f1(g, tmp)
1050 if f > best { best = f }
1051 i = i + 1
1052 }
1053 return best
1054}
1055
1056// inference: pick best-scored candidate into pred. 1 ok, 0 = no candidates (caller falls back).
1057func rd_pick(g: *i64, top1: i64, top2: i64, best_s: i64, pred: *u8) -> i64 {
1058 let nc: i64 = rd_enumerate(g, top1, top2, best_s)
1059 if nc == 0 { return 0 }
1060 var best: i64 = 0-1
1061 var bs: i64 = 0-SEM_MAGIC_2000000000
1062 var i: i64 = 0
1063 while i < nc {
1064 let sc: i64 = rd_score(g, ((g[81] as i64) + i*112) as *i64)
1065 if sc > bs { bs = sc; best = i }
1066 i = i + 1
1067 }
1068 if best < 0 { return 0 }
1069 let meta: *i64 = g[82] as *i64
1070 let mv: i64 = meta[best]
1071 let s: i64 = mv / SEM_MAGIC_1024
1072 let ci: i64 = mv % SEM_MAGIC_1024
1073 let n: i64 = rd_candstr(g, s, ci, pred)
1074 if n <= 0 { return 0 }
1075 return 1
1076}
1077
1078// one training row: label candidates by F1 vs gold; structured-perceptron update. returns 1 if gold recoverable.
1079func rd_train_row(g: *i64, gold: *u8, top1: i64, top2: i64, best_s: i64) -> i64 {
1080 let nc: i64 = rd_enumerate(g, top1, top2, best_s)
1081 if nc == 0 { return 0 }
1082 let meta: *i64 = g[82] as *i64
1083 let tmp: *u8 = g[86] as *u8
1084 rd_setgold(g, gold) // tokenize gold ONCE (rd_f1 reuses ctx buffers, no leak)
1085 var goldi: i64 = 0-1
1086 var goldf: i64 = 0
1087 var i: i64 = 0
1088 while i < nc {
1089 let mv: i64 = meta[i]
1090 let s: i64 = mv / SEM_MAGIC_1024
1091 let ci: i64 = mv % SEM_MAGIC_1024
1092 rd_candstr(g, s, ci, tmp)
1093 let f: i64 = rd_f1(g, tmp)
1094 if f > goldf { goldf = f; goldi = i }
1095 i = i + 1
1096 }
1097 if dbg == 1 { db_w(" [rr] labeled goldi="); db_n(goldi); db_w(" goldf="); db_n(goldf); db_w("\n" as *u8) }
1098 if goldf < 500 { return 0 } // gold not in candidate set -> unlearnable (recall miss)
1099 var predi: i64 = 0-1
1100 var bs: i64 = 0-SEM_MAGIC_2000000000
1101 i = 0
1102 while i < nc {
1103 let sc: i64 = rd_score(g, ((g[81] as i64) + i*112) as *i64)
1104 if sc > bs { bs = sc; predi = i }
1105 i = i + 1
1106 }
1107 if predi != goldi {
1108 let w: *i64 = g[80] as *i64
1109 let fg: *i64 = ((g[81] as i64) + goldi*112) as *i64
1110 let fp: *i64 = ((g[81] as i64) + predi*112) as *i64
1111 var k: i64 = 0
1112 while k < RD_NF { w[k] = w[k] + fg[k] - fp[k]; k = k + 1 }
1113 }
1114 return 1
1115}
1116
1117// walk one corpus file, train per row (averaged into wsum). returns [trained, recoverable] via g[88]/g[89] add.
1118func rd_train_file(g: *i64, path: *u8, fmt: i64) -> i64 {
1119 let total: i64 = db_read_raw(g, path, SEM_MAGIC_16777216)
1120 if total <= 0 { return 0 }
1121 g[2] = db_body_start(g, total)
1122 g[1] = total - g[2]
1123 g[3] = 0
1124 var trained: i64 = 0
1125 var live: i64 = 1
1126 while live == 1 {
1127 let rc: i64 = qab_load_row(g, fmt)
1128 if rc < 0 { live = 0 } else {
1129 if rc == 1 {
1130 db_norm_row(g)
1131 db_score_row(g)
1132 let top1: i64 = db_top_para(g, 0-1)
1133 let top2: i64 = db_top_para(g, top1)
1134 let s0: i64 = db_best_sentence(g)
1135 let yn: i64 = db_is_yesno(g)
1136 g[89] = g[89] + 1 // rows seen (non-degenerate)
1137 if yn == 0 {
1138 let did: i64 = rd_train_row(g, g[5] as *u8, top1, top2, s0)
1139 if did == 1 {
1140 trained = trained + 1
1141 g[88] = g[88] + 1 // recoverable-gold rows
1142 let w: *i64 = g[80] as *i64
1143 let ws: *i64 = g[85] as *i64
1144 var k: i64 = 0
1145 while k < RD_NF { ws[k] = ws[k] + w[k]; k = k + 1 }
1146 g[87] = g[87] + 1
1147 }
1148 }
1149 }
1150 }
1151 }
1152 return trained
1153}
1154
1155func rd_persist(g: *i64) -> i64 {
1156 let hdr: *u8 = sys_mmap(256)
1157 hdr[0]=78 as u8; hdr[1]=88 as u8; hdr[2]=82 as u8; hdr[3]=68 as u8; hdr[4]=82 as u8; hdr[5]=49 as u8; hdr[6]=0 as u8; hdr[7]=0 as u8
1158 let hi: *i64 = (hdr as i64 + 8) as *i64
1159 hi[0] = RD_NF
1160 let wb: *i64 = (hdr as i64 + 16) as *i64
1161 let w: *i64 = g[80] as *i64
1162 var k: i64 = 0
1163 while k < RD_NF { wb[k] = w[k]; k = k + 1 }
1164 let fd: i64 = sys_openat_wr("knowledge/index/reader_v1.bin" as *u8, 0x1a4)
1165 if fd < 0 { return 1 }
1166 var off: i64 = 0
1167 let tot: i64 = 16 + RD_NF*8
1168 while off < tot { let ww: i64 = sys_write(fd, (hdr as i64 + off) as *u8, tot - off); if ww <= 0 { off = tot } else { off = off + ww } }
1169 sys_close(fd)
1170 return 0
1171}
1172
1173// train the reader on the disjoint corpus chunks (E averaged-perceptron epochs), persist, load into g[80].
1174func rd_train(g: *i64) -> i64 {
1175 let w: *i64 = g[80] as *i64
1176 let ws: *i64 = g[85] as *i64
1177 var k: i64 = 0
1178 while k < RD_NF { w[k] = 0; ws[k] = 0; k = k + 1 }
1179 g[87] = 0; g[88] = 0; g[89] = 0
1180 let E: i64 = 6
1181 var ep: i64 = 0
1182 while ep < E {
1183 rd_train_file(g, "knowledge/fetched/qab_squad_c0.raw" as *u8, 1)
1184 rd_train_file(g, "knowledge/fetched/qab_squad_c1.raw" as *u8, 1)
1185 rd_train_file(g, "knowledge/fetched/qab_squad_c2.raw" as *u8, 1)
1186 rd_train_file(g, "knowledge/fetched/qab_hotpot_c0.raw" as *u8, 0)
1187 rd_train_file(g, "knowledge/fetched/qab_hotpot_c1.raw" as *u8, 0)
1188 rd_train_file(g, "knowledge/fetched/qab_hotpot_c2.raw" as *u8, 0)
1189 rd_train_file(g, "knowledge/fetched/qab_musique_c0.raw" as *u8, 3)
1190 rd_train_file(g, "knowledge/fetched/qab_musique_c1.raw" as *u8, 3)
1191 ep = ep + 1
1192 }
1193 if g[87] > 0 { k = 0; while k < RD_NF { w[k] = ws[k] / g[87]; k = k + 1 } }
1194 var recall: i64 = 0
1195 if g[89] > 0 { recall = (g[88] * 1000) / g[89] }
1196 db_w(" reader trained: "); db_n(g[88]); db_w(" recoverable-gold rows / "); db_n(g[89]); db_w(" seen (candidate-recall="); db_n(recall); db_w("permille, over E="); db_n(E); db_w(" epochs)\n" as *u8)
1197 rd_persist(g)
1198 g[79] = 1
1199 return 0
1200}
1201
1202func rd_load(g: *i64) -> i64 {
1203 g[79] = 0
1204 let fd: i64 = sys_openat_rd("knowledge/index/reader_v1.bin" as *u8)
1205 if fd < 0 { return 0 }
1206 let buf: *u8 = sys_mmap(256)
1207 var got: i64 = 0
1208 var r: i64 = 1
1209 while r > 0 { if got >= 128 { r = 0 } else { r = sys_read(fd, (buf as i64 + got) as *u8, 128 - got); if r > 0 { got = got + r } } }
1210 sys_close(fd)
1211 if got < 16 { return 0 }
1212 if buf[0] != (78 as u8) { return 0 }
1213 if buf[5] != (49 as u8) { return 0 }
1214 let hi: *i64 = (buf as i64 + 8) as *i64
1215 if hi[0] != RD_NF { return 0 }
1216 let wb: *i64 = (buf as i64 + 16) as *i64
1217 let w: *i64 = g[80] as *i64
1218 var k: i64 = 0
1219 while k < RD_NF { w[k] = wb[k]; k = k + 1 }
1220 g[79] = 1
1221 return 1
1222}
1223
1224// DUMP_NF = 14 hand-features + 3 corpus-PPMI EMBEDDING features (semantic relatedness the hand-features lack).
1225// The rung-2 finding was that hand-features are the ceiling -> test whether embedding features break it. Kept
1226// in the DUMP only (not rd_feat), so the main perceptron reader stays at RD_NF=14/251 unchanged.
1227const DUMP_NF: i64 = 17
1228
1229// ---- TRAINED dense embeddings (embed_v1.bin from nx_embed_train): Q10 integer E[nv*DIM]; integer cosine =
1230// no-float inference. Loaded alongside semppmi (same vocab ids from ppmi vh). slots: 115 E 116 DIM 117 flag
1231func embed_load(g: *i64) -> i64 {
1232 g[117] = 0
1233 let fd: i64 = sys_openat_rd("knowledge/index/embed_v1.bin" as *u8)
1234 if fd < 0 { return 0 }
1235 let hb: *u8 = sys_mmap(64)
1236 var hg: i64 = 0
1237 var hr: i64 = 1
1238 while hr > 0 { if hg >= 24 { hr = 0 } else { hr = sys_read(fd, (hb as i64 + hg) as *u8, 24 - hg); if hr > 0 { hg = hg + hr } } }
1239 if hg < 24 { sys_close(fd); return 0 }
1240 if hb[0] != (78 as u8) { sys_close(fd); return 0 } // 'N' (NXEMB1)
1241 let hi: *i64 = (hb as i64 + 8) as *i64
1242 let nv: i64 = hi[0]
1243 let dim: i64 = hi[1]
1244 let need: i64 = nv * dim * 8
1245 if need <= 0 { sys_close(fd); return 0 }
1246 if need > SEM_MAGIC_402653184 { sys_close(fd); return 0 } // 384MB sanity
1247 let buf: *u8 = sys_mmap(need + SEM_MAGIC_4096)
1248 var got: i64 = 0
1249 var r: i64 = 1
1250 while r > 0 { let left: i64 = need - got; if left <= 0 { r = 0 } else { r = sys_read(fd, (buf as i64 + got) as *u8, left); if r > 0 { got = got + r } } }
1251 sys_close(fd)
1252 if got < need { return 0 }
1253 g[115] = buf as i64
1254 g[116] = dim
1255 g[117] = 1
1256 return 1
1257}
1258
1259// integer cosine (permille) of trained dense embedding rows a,b (Q10 ints; scale cancels in cosine).
1260func dense_cos_int(g: *i64, a: i64, b: i64) -> i64 {
1261 let E: *i64 = g[115] as *i64
1262 let dim: i64 = g[116]
1263 var dot: i64 = 0
1264 var na: i64 = 0
1265 var nb: i64 = 0
1266 var k: i64 = 0
1267 let ba: i64 = a * dim
1268 let bb: i64 = b * dim
1269 while k < dim {
1270 let ea: i64 = E[ba+k]
1271 let eb: i64 = E[bb+k]
1272 dot = dot + ea*eb
1273 na = na + ea*ea
1274 nb = nb + eb*eb
1275 k = k + 1
1276 }
1277 if dot <= 0 { return 0 }
1278 let d1: i64 = sem_isqrt(na)
1279 let d2: i64 = sem_isqrt(nb)
1280 if d1 == 0 { return 0 }
1281 if d2 == 0 { return 0 }
1282 var cv: i64 = (dot*1000)/(d1*d2)
1283 if cv > 1000 { cv = 1000 }
1284 return cv
1285}
1286
1287// resolve the question's content-word PPMI ids into g[108] (count g[109]) once per row.
1288func rd_setqppmi(g: *i64) -> i64 {
1289 g[109] = 0
1290 if g[78] != 1 { return 0 }
1291 let qnb: *u8 = g[28] as *u8
1292 let qto: *i64 = g[29] as *i64
1293 let qtl: *i64 = g[30] as *i64
1294 let qk: *i64 = g[32] as *i64
1295 let qid: *i64 = g[108] as *i64
1296 var n: i64 = 0
1297 var k: i64 = 0
1298 while k < g[31] {
1299 if qk[k] == 1 { if n < 64 { let wid: i64 = ppmi_wid(g, qnb, qto[k], qtl[k]); if wid >= 0 { qid[n] = wid; n = n + 1 } } }
1300 k = k + 1
1301 }
1302 g[109] = n
1303 return n
1304}
1305
1306// 3 corpus-PPMI embedding features for a candidate STRING vs the question: out[0]=max token-alignment,
1307// out[1]=mean token-alignment, out[2]=#candidate tokens strongly related to a q-word. All ~0..10.
1308func rd_emb_feats(g: *i64, candstr: *u8, out: *i64) -> i64 {
1309 out[0] = 0; out[1] = 0; out[2] = 0
1310 if g[78] != 1 { return 0 }
1311 if g[109] == 0 { return 0 }
1312 let nrm: *u8 = g[111] as *u8
1313 let cto: *i64 = g[112] as *i64
1314 let ctl: *i64 = g[113] as *i64
1315 let nl: i64 = qs_norm(candstr, nrm)
1316 let nt: i64 = qs_tok(nrm, nl, cto, ctl, 16)
1317 let qid: *i64 = g[108] as *i64
1318 var best: i64 = 0
1319 var sum: i64 = 0
1320 var ntok: i64 = 0
1321 var cnt: i64 = 0
1322 var t: i64 = 0
1323 while t < nt {
1324 let cid: i64 = ppmi_wid(g, nrm, cto[t], ctl[t])
1325 if cid >= 0 {
1326 var tb: i64 = 0
1327 var q: i64 = 0
1328 while q < g[109] {
1329 var c: i64 = 0
1330 if g[117] == 1 { c = dense_cos_int(g, cid, qid[q]) } else { c = ppmi_cos(g, cid, qid[q]) }
1331 if c > tb { tb = c }
1332 q = q + 1
1333 }
1334 if tb > best { best = tb }
1335 sum = sum + tb; ntok = ntok + 1
1336 if tb > 300 { cnt = cnt + 1 }
1337 }
1338 t = t + 1
1339 }
1340 out[0] = best / 100
1341 if ntok > 0 { out[1] = (sum / ntok) / 100 }
1342 out[2] = cnt
1343 if out[2] > 10 { out[2] = 10 }
1344 return 0
1345}
1346
1347// ---- FULL-VECTOR embedding features (the R1 crux): instead of collapsing to a scalar cosine, feed the MLP
1348// the element-wise product of the L2-normalized question and candidate embedding vectors (DIM values) so
1349// it learns a per-dimension weighted metric. slots: 118 q_emb_unit 119 cand_unit scratch ----
1350func rd_setqemb(g: *i64) -> i64 {
1351 let dim: i64 = g[116]
1352 let qu: *i64 = g[118] as *i64
1353 var d: i64 = 0
1354 while d < dim { qu[d] = 0; d = d + 1 }
1355 if g[117] != 1 { return 0 }
1356 if g[109] == 0 { return 0 }
1357 let qid: *i64 = g[108] as *i64
1358 let E: *i64 = g[115] as *i64
1359 var n: i64 = 0
1360 while n < g[109] {
1361 let id: i64 = qid[n]
1362 d = 0
1363 while d < dim { qu[d] = qu[d] + E[id*dim+d]; d = d + 1 }
1364 n = n + 1
1365 }
1366 var nrm: i64 = 0
1367 d = 0
1368 while d < dim { nrm = nrm + qu[d]*qu[d]; d = d + 1 }
1369 let rt: i64 = sem_isqrt(nrm)
1370 if rt == 0 { return 0 }
1371 d = 0
1372 while d < dim { qu[d] = qu[d]*SEM_MAGIC_1024/rt; d = d + 1 } // Q10 unit vector
1373 return 1
1374}
1375
1376// element-wise product of q_emb_unit and cand_emb_unit, each component scaled to ~0..10. out must hold DIM.
1377func rd_emb_vec(g: *i64, candstr: *u8, out: *i64) -> i64 {
1378 let dim: i64 = g[116]
1379 var d: i64 = 0
1380 while d < dim { out[d] = 5; d = d + 1 } // neutral default
1381 if g[117] != 1 { return 0 }
1382 if g[109] == 0 { return 0 }
1383 let nrm2: *u8 = g[111] as *u8
1384 let cto: *i64 = g[112] as *i64
1385 let ctl: *i64 = g[113] as *i64
1386 let cu: *i64 = g[119] as *i64
1387 d = 0
1388 while d < dim { cu[d] = 0; d = d + 1 }
1389 let nl: i64 = qs_norm(candstr, nrm2)
1390 let nt: i64 = qs_tok(nrm2, nl, cto, ctl, 16)
1391 let E: *i64 = g[115] as *i64
1392 var nseen: i64 = 0
1393 var t: i64 = 0
1394 while t < nt {
1395 let cid: i64 = ppmi_wid(g, nrm2, cto[t], ctl[t])
1396 if cid >= 0 { d = 0; while d < dim { cu[d] = cu[d] + E[cid*dim+d]; d = d + 1 } nseen = nseen + 1 }
1397 t = t + 1
1398 }
1399 if nseen == 0 { return 0 }
1400 var cn: i64 = 0
1401 d = 0
1402 while d < dim { cn = cn + cu[d]*cu[d]; d = d + 1 }
1403 let crt: i64 = sem_isqrt(cn)
1404 if crt == 0 { return 0 }
1405 d = 0
1406 while d < dim { cu[d] = cu[d]*SEM_MAGIC_1024/crt; d = d + 1 }
1407 let qu: *i64 = g[118] as *i64
1408 d = 0
1409 while d < dim {
1410 let p: i64 = qu[d]*cu[d]/SEM_MAGIC_1024 // Q10 product, ~+-SEM_MAGIC_1024
1411 var f: i64 = p/20 + 5 // scale to ~0..10 (matches hand-feature range)
1412 if f < 0 { f = 0 }
1413 if f > 10 { f = 10 }
1414 out[d] = f
1415 d = d + 1
1416 }
1417 return 1
1418}
1419
1420// ---- TEXT DUMP (R3 neural reader): export per-row RAW (question, context, gold-answer) strings so the
1421// offline neural trainer (nx_reader_squad_gate on nx_nofloat_autograd) does its own tokenization + gold
1422// span matching. File: knowledge/index/reader_rows.bin = [magic "NXRR1"] then per row:
1423// [qlen i64][q bytes][clen i64][ctx bytes = prose sentences space-joined][alen i64][gold bytes].
1424// Selected by g[131]=1 (dumptext mode 'dt'); the feats path is untouched when the flag is 0. ----
1425func rd_dump_text_row(g: *i64, gold: *u8, fd: i64) -> i64 {
1426 let q: *u8 = g[4] as *u8
1427 let qlen: i64 = qa_slen(q)
1428 let alen: i64 = qa_slen(gold)
1429 if qlen < 4 { return 0 }
1430 if alen < 1 { return 0 }
1431 if g[19] <= 0 { return 0 }
1432 let sb: *u8 = g[15] as *u8
1433 let so: *i64 = g[16] as *i64
1434 var clen: i64 = 0
1435 var s: i64 = 0
1436 while s < g[19] { let sl: i64 = qa_slen((sb as i64 + so[s]) as *u8); clen = clen + sl + 1; s = s + 1 }
1437 if clen < 8 { return 0 }
1438 let hb: *i64 = g[86] as *i64
1439 hb[0] = qlen
1440 sys_write(fd, g[86] as *u8, 8)
1441 sys_write(fd, q, qlen)
1442 hb[0] = clen
1443 sys_write(fd, g[86] as *u8, 8)
1444 s = 0
1445 while s < g[19] {
1446 let sp0: *u8 = (sb as i64 + so[s]) as *u8
1447 let sl: i64 = qa_slen(sp0)
1448 sys_write(fd, sp0, sl)
1449 let sc: *u8 = g[86] as *u8
1450 sc[0] = 32 as u8
1451 sys_write(fd, sc, 1)
1452 s = s + 1
1453 }
1454 hb[0] = alen
1455 sys_write(fd, g[86] as *u8, 8)
1456 sys_write(fd, gold, alen)
1457 return 1
1458}
1459
1460func rd_dump_texts(g: *i64) -> i64 {
1461 let fd: i64 = sys_openat_wr("knowledge/index/reader_rows.bin" as *u8, 0x1a4)
1462 if fd < 0 { db_w("dumptext: cannot open reader_rows.bin\n" as *u8); return 1 }
1463 let mgb: *u8 = g[86] as *u8
1464 mgb[0]=78 as u8; mgb[1]=88 as u8; mgb[2]=82 as u8; mgb[3]=82 as u8; mgb[4]=49 as u8; mgb[5]=0 as u8; mgb[6]=0 as u8; mgb[7]=0 as u8
1465 sys_write(fd, g[86] as *u8, 8)
1466 g[88] = 0
1467 g[131] = 1
1468 rd_dump_file(g, "knowledge/fetched/qab_squad_c0.raw" as *u8, 1, fd, SEM_MAGIC_10000)
1469 rd_dump_file(g, "knowledge/fetched/qab_squad_c1.raw" as *u8, 1, fd, SEM_MAGIC_10000)
1470 g[131] = 0
1471 sys_close(fd)
1472 db_w("dumptext: wrote "); db_n(g[88]); db_w(" SQuAD rows (raw q/ctx/gold text) -> knowledge/index/reader_rows.bin\n" as *u8)
1473 return 0
1474}
1475
1476// XL text dump ('dx'): ALL banked SQuAD chunks -> reader_rows_xl.bin. SEPARATE FILE by design: the in-flight
1477// f32 run's checkpoint is keyed to reader_rows.bin's vocab (nw) -- overwriting it mid-arc would invalidate the
1478// checkpoint on the next watchdog relaunch. The XL set is the R3f data-scale lever, staged in advance.
1479func rd_dump_texts_xl(g: *i64) -> i64 {
1480 let fd: i64 = sys_openat_wr("knowledge/index/reader_rows_xl.bin" as *u8, 0x1a4)
1481 if fd < 0 { db_w("dumptext-xl: cannot open reader_rows_xl.bin\n" as *u8); return 1 }
1482 let mgb: *u8 = g[86] as *u8
1483 mgb[0]=78 as u8; mgb[1]=88 as u8; mgb[2]=82 as u8; mgb[3]=82 as u8; mgb[4]=49 as u8; mgb[5]=0 as u8; mgb[6]=0 as u8; mgb[7]=0 as u8
1484 sys_write(fd, g[86] as *u8, 8)
1485 g[88] = 0
1486 g[131] = 1
1487 rd_dump_file(g, "knowledge/fetched/qab_squad_c0.raw" as *u8, 1, fd, SEM_MAGIC_10000)
1488 rd_dump_file(g, "knowledge/fetched/qab_squad_c1.raw" as *u8, 1, fd, SEM_MAGIC_10000)
1489 rd_dump_file(g, "knowledge/fetched/qab_squad_c2.raw" as *u8, 1, fd, SEM_MAGIC_10000)
1490 rd_dump_file(g, "knowledge/fetched/qab_squad_c3.raw" as *u8, 1, fd, SEM_MAGIC_10000)
1491 rd_dump_file(g, "knowledge/fetched/qab_squad_c4.raw" as *u8, 1, fd, SEM_MAGIC_10000)
1492 rd_dump_file(g, "knowledge/fetched/qab_squad_c5.raw" as *u8, 1, fd, SEM_MAGIC_10000)
1493 rd_dump_file(g, "knowledge/fetched/qab_squad_c6.raw" as *u8, 1, fd, SEM_MAGIC_10000)
1494 rd_dump_file(g, "knowledge/fetched/qab_squad_c7.raw" as *u8, 1, fd, SEM_MAGIC_10000)
1495 rd_dump_file(g, "knowledge/fetched/qab_squad_c8.raw" as *u8, 1, fd, SEM_MAGIC_10000)
1496 rd_dump_file(g, "knowledge/fetched/qab_squad_big.raw" as *u8, 1, fd, SEM_MAGIC_10000)
1497 g[131] = 0
1498 sys_close(fd)
1499 db_w("dumptext-xl: wrote "); db_n(g[88]); db_w(" SQuAD rows -> knowledge/index/reader_rows_xl.bin\n" as *u8)
1500 return 0
1501}
1502
1503// ---- feature DUMP: export per-row candidate features + gold index for OFFLINE MLP training (nx_reader_mlp_train
1504// trains the neural reader on nx_autograd; nx_qabench stays integer/no-float). File: knowledge/index/
1505// reader_feats.bin = [magic "NXRF1", DUMP_NF] then per row: [nc, goldi, feat[nc*DUMP_NF]] (14 hand + 3 emb). ----
1506func rd_dump_row(g: *i64, gold: *u8, top1: i64, top2: i64, best_s: i64, fd: i64) -> i64 {
1507 if g[131] == 1 { return rd_dump_text_row(g, gold, fd) }
1508 let nc: i64 = rd_enumerate(g, top1, top2, best_s)
1509 if nc < 2 { return 0 }
1510 if nc > 64 { return 0 }
1511 rd_setgold(g, gold)
1512 let meta: *i64 = g[82] as *i64
1513 let tmp: *u8 = g[86] as *u8
1514 var goldi: i64 = 0-1
1515 var goldf: i64 = 0
1516 var i: i64 = 0
1517 while i < nc {
1518 let mv: i64 = meta[i]
1519 let s: i64 = mv / SEM_MAGIC_1024
1520 let ci: i64 = mv % SEM_MAGIC_1024
1521 rd_candstr(g, s, ci, tmp)
1522 let f: i64 = rd_f1(g, tmp)
1523 if f > goldf { goldf = f; goldi = i }
1524 i = i + 1
1525 }
1526 if goldf < 500 { return 0 } // gold not recoverable -> skip
1527 rd_setqppmi(g) // question PPMI ids
1528 let vecmode: i64 = g[117] // 1 = dense embeddings loaded -> FULL-VECTOR features
1529 if vecmode == 1 { rd_setqemb(g) } // q_emb_unit once per row
1530 let hb: *i64 = g[86] as *i64
1531 hb[0] = nc; hb[1] = goldi
1532 sys_write(fd, g[86] as *u8, 16)
1533 let emb: *i64 = g[114] as *i64 // 3-scalar buffer (sparse mode)
1534 let vbuf: *i64 = g[121] as *i64 // DIM-vector buffer (vector mode)
1535 var wc: i64 = 0
1536 while wc < nc {
1537 sys_write(fd, (g[81] as i64 + wc*RD_NF*8) as *u8, RD_NF * 8) // 14 hand-features for candidate wc
1538 let mv: i64 = meta[wc]
1539 let s: i64 = mv / SEM_MAGIC_1024
1540 let ci: i64 = mv % SEM_MAGIC_1024
1541 rd_candstr(g, s, ci, g[110] as *u8)
1542 if vecmode == 1 { rd_emb_vec(g, g[110] as *u8, vbuf); sys_write(fd, g[121] as *u8, g[116] * 8) } // DIM vector feats
1543 else { rd_emb_feats(g, g[110] as *u8, emb); sys_write(fd, g[114] as *u8, 24) } // 3 scalar feats
1544 wc = wc + 1
1545 }
1546 return 1
1547}
1548
1549func rd_dump_file(g: *i64, path: *u8, fmt: i64, fd: i64, cap: i64) -> i64 {
1550 let total: i64 = db_read_raw(g, path, SEM_MAGIC_16777216)
1551 if total <= 0 { return 0 }
1552 g[2] = db_body_start(g, total)
1553 g[1] = total - g[2]
1554 g[3] = 0
1555 let start88: i64 = g[88] // debt eaten SEM_MAGIC_2026-07-10: cap is now PER-FILE. It was compared
1556 var live: i64 = 1 // against the GLOBAL row counter, so a multi-file dump silently
1557 while live == 1 { // truncated at `cap` TOTAL rows (the XL dump lost half its data).
1558 if g[88] - start88 >= cap { live = 0 } else {
1559 let rc: i64 = qab_load_row(g, fmt)
1560 if rc < 0 { live = 0 } else {
1561 if rc == 1 {
1562 db_norm_row(g)
1563 db_score_row(g)
1564 let top1: i64 = db_top_para(g, 0-1)
1565 let top2: i64 = db_top_para(g, top1)
1566 let s0: i64 = db_best_sentence(g)
1567 let yn: i64 = db_is_yesno(g)
1568 if yn == 0 { if rd_dump_row(g, g[5] as *u8, top1, top2, s0, fd) == 1 { g[88] = g[88] + 1 } }
1569 }
1570 }
1571 }
1572 }
1573 return g[88]
1574}
1575
1576func rd_dump_feats(g: *i64) -> i64 {
1577 let fd: i64 = sys_openat_wr("knowledge/index/reader_feats.bin" as *u8, 0x1a4)
1578 if fd < 0 { db_w("dumpfeats: cannot open reader_feats.bin\n" as *u8); return 1 }
1579 let mgb: *u8 = g[86] as *u8
1580 mgb[0]=78 as u8; mgb[1]=88 as u8; mgb[2]=82 as u8; mgb[3]=70 as u8; mgb[4]=49 as u8; mgb[5]=0 as u8; mgb[6]=0 as u8; mgb[7]=0 as u8
1581 let mg: *i64 = g[86] as *i64
1582 var nf: i64 = DUMP_NF // sparse: 14 hand + 3 scalar embed
1583 if g[117] == 1 { nf = RD_NF + g[116] } // vector mode: 14 hand + DIM full-vector product
1584 mg[1] = nf
1585 db_w("dumpfeats: feature count nf="); db_n(nf); db_w("\n" as *u8)
1586 sys_write(fd, g[86] as *u8, 16)
1587 g[88] = 0
1588 let CAP: i64 = SEM_MAGIC_2500
1589 rd_dump_file(g, "knowledge/fetched/qab_squad_c0.raw" as *u8, 1, fd, CAP)
1590 rd_dump_file(g, "knowledge/fetched/qab_squad_c1.raw" as *u8, 1, fd, CAP)
1591 rd_dump_file(g, "knowledge/fetched/qab_hotpot_c0.raw" as *u8, 0, fd, CAP)
1592 rd_dump_file(g, "knowledge/fetched/qab_hotpot_c1.raw" as *u8, 0, fd, CAP)
1593 sys_close(fd)
1594 db_w("dumpfeats: wrote "); db_n(g[88]); db_w(" rows (DUMP_NF="); db_n(DUMP_NF); db_w(", incl 3 PPMI-embed) -> knowledge/index/reader_feats.bin\n" as *u8)
1595 return 0
1596}
1597// ==================== end MODE-R ====================
1598
1599// run one benchmark end-to-end; write [rows, f1_single, f1_multi, em] into res[base..base+3]. returns rows.
1600func qab_run(g: *i64, path: *u8, fmt: i64, label: *u8, res: *i64, base: i64) -> i64 {
1601 let total: i64 = db_read_raw(g, path, SEM_MAGIC_16777216)
1602 if total == 0-2 { db_w(" "); db_w(label); db_w(": [file EXCEEDS 16MB raw buffer -- raise m0, skipped]\n" as *u8); res[base]=0; return 0 }
1603 if total <= 0 { db_w(" "); db_w(label); db_w(": [absent -- fetch first, skipped]\n" as *u8); res[base]=0; return 0 }
1604 g[2] = db_body_start(g, total)
1605 g[1] = total - g[2]
1606 let p0a: *u8 = g[54] as *u8
1607 let gaa: *u8 = g[56] as *u8
1608
1609 var rows: i64 = 0
1610 var sum_f0: i64 = 0; var sum_f1: i64 = 0; var sum_f2: i64 = 0; var sum_fR: i64 = 0; var sum_orc: i64 = 0; var em0: i64 = 0
1611 g[3] = 0
1612 var live: i64 = 1
1613 let DBGL: i64 = 0
1614 var QLIMIT: i64 = 0 // optional row cap (knowledge/index/qab_limit.txt) -> bounded on-box runs
1615 let lfd: i64 = sys_openat_rd("knowledge/index/qab_limit.txt" as *u8)
1616 if lfd >= 0 {
1617 let lb: *u8 = sys_mmap(32)
1618 let lrn: i64 = sys_read(lfd, lb, 31)
1619 sys_close(lfd)
1620 var lci: i64 = 0
1621 while lci < lrn { if lb[lci]>=(48 as u8) { if lb[lci]<=(57 as u8) { QLIMIT = QLIMIT*10 + (lb[lci] as i64 - 48) } } lci=lci+1 }
1622 }
1623 while live == 1 {
1624 let rc: i64 = qab_load_row(g, fmt)
1625 if DBGL == 1 { if fmt == 3 { db_w("MQ load rows="); db_n(rows); db_w(" rc="); db_n(rc); db_w(" np="); db_n(g[14]); db_w(" ns="); db_n(g[19]); db_w(" nqt="); db_n(g[31]); db_w("\n" as *u8) } }
1626 if rc < 0 { live = 0 } else {
1627 if rc == 1 {
1628 db_norm_row(g)
1629 if DBGL == 1 { if fmt == 3 { db_w(" MQ normed\n" as *u8) } }
1630 db_score_row(g)
1631 if DBGL == 1 { if fmt == 3 { db_w(" MQ scored\n" as *u8) } }
1632 let top1: i64 = db_top_para(g, 0-1)
1633 let top2: i64 = db_top_para(g, top1)
1634 let s0: i64 = db_best_sentence(g)
1635 let yn: i64 = db_is_yesno(g)
1636 let wantnum0: i64 = db_prefer_numeric(g)
1637 let pd0: *u8 = (p0a as i64 + rows*256) as *u8
1638 var qw0: i64 = 0
1639 if g[132] == 1 { if qwx_ready() == 1 { if s0 >= 0 { qw0 = qwx_extract(g, s0, pd0) } } } // R5: Qwen reads context centered on best sentence s0 (mode0/SQuAD = 251 rung)
1640 if qw0 == 0 {
1641 if yn == 1 { db_setpred(pd0, "yes" as *u8, 3) } else {
1642 var got0: i64 = 0
1643 if wantnum0 == 1 { got0 = db_sweep_numeric(g, top1, top2, pd0) }
1644 if got0 == 0 { if s0 >= 0 { db_extract(g, s0, pd0) } else { db_setpred(pd0, "none" as *u8, 4) } }
1645 }
1646 }
1647 // multi-hop (mode1)
1648 var x2: i64 = 0-1
1649 if s0 >= 0 { if top1 >= 0 {
1650 db_bridge_from(g, s0, top1)
1651 let psc: *i64 = g[42] as *i64
1652 let xsc: *i64 = g[43] as *i64
1653 var pp: i64 = 0
1654 while pp < g[14] {
1655 if pp == top1 { xsc[pp] = 0-1000 } else {
1656 let bt: i64 = db_bridge_title_hits(g, pp)
1657 let bb: i64 = db_bridge_body_hits(g, pp)
1658 xsc[pp] = psc[pp] + 3*bt + 2*bb
1659 }
1660 pp = pp + 1
1661 }
1662 var bx2: i64 = 0-1
1663 var bxs: i64 = 0-SEM_MAGIC_1000000
1664 pp = 0
1665 while pp < g[14] { if xsc[pp] > bxs { bxs = xsc[pp]; bx2 = pp } pp = pp + 1 }
1666 x2 = bx2
1667 } }
1668 let pd1: *u8 = (g[55] as i64 + rows*256) as *u8
1669 if yn == 1 { db_setpred(pd1, "yes" as *u8, 3) } else {
1670 var got1: i64 = 0
1671 if wantnum0 == 1 { got1 = db_sweep_numeric(g, top1, x2, pd1) }
1672 if got1 == 0 {
1673 var s2: i64 = 0-1
1674 if x2 >= 0 { s2 = db_best_sentence_in(g, x2) }
1675 var sfin: i64 = s2
1676 if sfin < 0 { sfin = s0 }
1677 let shit: *i64 = g[39] as *i64
1678 if s0 >= 0 { if s2 >= 0 { if shit[s0] > shit[s2] + 1 { sfin = s0 } } }
1679 if sfin >= 0 { db_extract(g, sfin, pd1) } else { db_setpred(pd1, "none" as *u8, 4) }
1680 }
1681 }
1682 // ---- mode 2: SEMANTIC sentence selection (R3-lite; same extraction, different selection) ----
1683 let pd2: *u8 = g[68] as *u8
1684 if yn == 1 { db_setpred(pd2, "yes" as *u8, 3) } else {
1685 var got2: i64 = 0
1686 if wantnum0 == 1 { got2 = db_sweep_numeric(g, top1, top2, pd2) }
1687 if got2 == 0 {
1688 let ss: i64 = sem_best_sentence(g, top1, top2)
1689 var sf2: i64 = ss
1690 if sf2 < 0 { sf2 = s0 }
1691 if sf2 >= 0 { db_extract(g, sf2, pd2) } else { db_setpred(pd2, "none" as *u8, 4) }
1692 }
1693 }
1694 // ---- mode R: TRAINED span reader (learned candidate ranking; same candidate GENERATION) ----
1695 let pdR: *u8 = g[84] as *u8
1696 if g[79] == 1 {
1697 if yn == 1 { db_setpred(pdR, "yes" as *u8, 3) } else {
1698 var gotR: i64 = 0
1699 if wantnum0 == 1 { gotR = db_sweep_numeric(g, top1, top2, pdR) }
1700 if gotR == 0 {
1701 let okR: i64 = rd_pick(g, top1, top2, s0, pdR)
1702 if okR == 0 { if s0 >= 0 { db_extract(g, s0, pdR) } else { db_setpred(pdR, "none" as *u8, 4) } }
1703 }
1704 }
1705 if yn == 0 { sum_orc = sum_orc + rd_oracle(g, top1, top2, s0, g[5] as *u8) }
1706 }
1707
1708 // gold copy (for neg-control)
1709 let gd: *u8 = (gaa as i64 + rows*256) as *u8
1710 db_setpred(gd, g[5] as *u8, 250)
1711 sum_f0 = sum_f0 + qab_f1(g, fmt, pd0)
1712 sum_f1 = sum_f1 + qab_f1(g, fmt, pd1)
1713 sum_f2 = sum_f2 + qab_f1(g, fmt, pd2)
1714 if g[79] == 1 { sum_fR = sum_fR + qab_f1(g, fmt, pdR) }
1715 em0 = em0 + qab_em(g, fmt, pd0)
1716 let DBG: i64 = 0
1717 if DBG == 1 { if fmt == 1 { if rows < 14 {
1718 db_w(" [SQ] f0="); db_n(qab_f1(g,fmt,pd0)); db_w(" | Q="); db_w(g[4] as *u8); db_w(" | P="); db_w(pd0); db_w(" | G="); db_w(g[5] as *u8); db_w("\n" as *u8)
1719 } } }
1720 rows = rows + 1
1721 if QLIMIT > 0 { if rows >= QLIMIT { live = 0 } }
1722 }
1723 }
1724 }
1725
1726 // neg-control: mode0 preds vs ROTATED golds -> must be ~0
1727 var neg: i64 = 0
1728 var r: i64 = 0
1729 while r < rows {
1730 let pd: *u8 = (p0a as i64 + r*256) as *u8
1731 var r2: i64 = r + 1
1732 if r2 >= rows { r2 = 0 }
1733 neg = neg + qs_f1(pd, (gaa as i64 + r2*256) as *u8)
1734 r = r + 1
1735 }
1736 var af0: i64 = 0; var af1: i64 = 0; var af2: i64 = 0; var afR: i64 = 0; var afO: i64 = 0; var naneg: i64 = 0
1737 if rows > 0 { af0 = sum_f0/rows; af1 = sum_f1/rows; af2 = sum_f2/rows; afR = sum_fR/rows; afO = sum_orc/rows; naneg = neg/rows }
1738 db_w(" "); db_w(label)
1739 db_w(" rows="); db_n(rows)
1740 db_w(" F1_lex="); db_n(af0)
1741 db_w(" F1_hop="); db_n(af1)
1742 db_w(" F1_SEM="); db_n(af2)
1743 db_w(" F1_rdr="); db_n(afR)
1744 db_w(" ORACLE="); db_n(afO)
1745 db_w(" EM="); db_n(em0); db_w("/"); db_n(rows)
1746 db_w(" negctl="); db_n(naneg); db_w("\n" as *u8)
1747 res[base]=rows; res[base+1]=af0; res[base+2]=af1; res[base+3]=af2; res[base+4]=em0; res[base+5]=afR; res[base+6]=afO
1748 return rows
1749}
1750
1751func main(argc: i64, argv: *i64) -> i64 {
1752 if argc < 4 { db_w("=== nx_qabench -- QA benchmark LADDER: SQuAD 1-hop (extraction floor) vs HotpotQA 2-hop (reasoning wall) ===\n" as *u8) }
1753 if argc >= 4 { db_w("=== nx_qabench ANSWER (retrieve-then-read; trained span reader) ===\n" as *u8) }
1754 let g: *i64 = sys_mmap(SEM_MAGIC_2048) as *i64 // 256 slots (was 128; multi-source research uses 125-127)
1755 let m0: *u8 = sys_mmap(SEM_MAGIC_16777216); g[0] = m0 as i64 // 16MB raw-file buffer: paginated sets grow past the old 1.5MB (HotpotQA-300 = 1.9MB)
1756 let m4: *u8 = sys_mmap(SEM_MAGIC_4096); g[4] = m4 as i64
1757 let m5: *u8 = sys_mmap(SEM_MAGIC_2048); g[5] = m5 as i64
1758 let m6: *u8 = sys_mmap(64); g[6] = m6 as i64
1759 let m7: *u8 = sys_mmap(SEM_MAGIC_4096); g[7] = m7 as i64
1760 let m8: *u8 = sys_mmap(256); g[8] = m8 as i64
1761 let m9: *u8 = sys_mmap(256); g[9] = m9 as i64
1762 let m11: *u8 = sys_mmap(SEM_MAGIC_32768); g[11] = m11 as i64 // title arena (MuSiQue: 20 paras vs HotpotQA 10)
1763 let m12: *u8 = sys_mmap(SEM_MAGIC_4096); g[12] = m12 as i64 // pto: 512 paragraph slots
1764 let m13: *u8 = sys_mmap(SEM_MAGIC_4096); g[13] = m13 as i64 // ptl
1765 let m15: *u8 = sys_mmap(SEM_MAGIC_1048576); g[15] = m15 as i64 // sentence arena (20 long paras)
1766 let m16: *u8 = sys_mmap(SEM_MAGIC_32768); g[16] = m16 as i64 // so: SEM_MAGIC_4096 sentence slots
1767 let m17: *u8 = sys_mmap(SEM_MAGIC_32768); g[17] = m17 as i64 // sl
1768 let m18: *u8 = sys_mmap(SEM_MAGIC_32768); g[18] = m18 as i64 // sp
1769 let m20: *u8 = sys_mmap(SEM_MAGIC_1048576); g[20] = m20 as i64 // normalized sentence arena
1770 let m21: *u8 = sys_mmap(SEM_MAGIC_32768); g[21] = m21 as i64 // no
1771 let m22: *u8 = sys_mmap(SEM_MAGIC_32768); g[22] = m22 as i64 // nl
1772 let m23: *u8 = sys_mmap(SEM_MAGIC_524288); g[23] = m23 as i64 // tko: 64k token slots
1773 let m24: *u8 = sys_mmap(SEM_MAGIC_524288); g[24] = m24 as i64 // tkl
1774 let m25: *u8 = sys_mmap(SEM_MAGIC_32768); g[25] = m25 as i64 // tks: per-sentence token start
1775 let m26: *u8 = sys_mmap(SEM_MAGIC_32768); g[26] = m26 as i64 // tkc
1776 let m28: *u8 = sys_mmap(SEM_MAGIC_4096); g[28] = m28 as i64
1777 let m29: *u8 = sys_mmap(SEM_MAGIC_2048); g[29] = m29 as i64
1778 let m30: *u8 = sys_mmap(SEM_MAGIC_2048); g[30] = m30 as i64
1779 let m32: *u8 = sys_mmap(SEM_MAGIC_2048); g[32] = m32 as i64
1780 let m33: *u8 = sys_mmap(SEM_MAGIC_32768); g[33] = m33 as i64 // title normalized arena
1781 let m34: *u8 = sys_mmap(SEM_MAGIC_32768); g[34] = m34 as i64 // tto
1782 let m35: *u8 = sys_mmap(SEM_MAGIC_32768); g[35] = m35 as i64 // ttl
1783 let m36: *u8 = sys_mmap(SEM_MAGIC_4096); g[36] = m36 as i64 // tts: 512 paragraph slots
1784 let m37: *u8 = sys_mmap(SEM_MAGIC_4096); g[37] = m37 as i64 // ttc
1785 let m39: *u8 = sys_mmap(SEM_MAGIC_32768); g[39] = m39 as i64 // shit: per-sentence
1786 let m40: *u8 = sys_mmap(SEM_MAGIC_4096); g[40] = m40 as i64 // thit: per-paragraph
1787 let m41: *u8 = sys_mmap(SEM_MAGIC_4096); g[41] = m41 as i64 // bhit
1788 let m42: *u8 = sys_mmap(SEM_MAGIC_4096); g[42] = m42 as i64 // psc
1789 let m43: *u8 = sys_mmap(SEM_MAGIC_4096); g[43] = m43 as i64 // xsc
1790 let m44: *u8 = sys_mmap(SEM_MAGIC_1024); g[44] = m44 as i64
1791 let m45: *u8 = sys_mmap(128); g[45] = m45 as i64
1792 let m46: *u8 = sys_mmap(128); g[46] = m46 as i64
1793 let m48: *u8 = sys_mmap(SEM_MAGIC_2048); g[48] = m48 as i64
1794 let m49: *u8 = sys_mmap(256); g[49] = m49 as i64
1795 let m50: *u8 = sys_mmap(256); g[50] = m50 as i64
1796 let m51: *u8 = sys_mmap(256); g[51] = m51 as i64
1797 let m53: *u8 = sys_mmap(SEM_MAGIC_8192); g[53] = m53 as i64
1798 let m54: *u8 = sys_mmap(SEM_MAGIC_262144); g[54] = m54 as i64 // pred/gold arenas: 256B/row -> SEM_MAGIC_1024-row capacity (grown for paginated eval sets)
1799 let m55: *u8 = sys_mmap(SEM_MAGIC_262144); g[55] = m55 as i64
1800 let m56: *u8 = sys_mmap(SEM_MAGIC_262144); g[56] = m56 as i64
1801 let m57: *u8 = sys_mmap(SEM_MAGIC_65536); g[57] = m57 as i64 // H presence matrix p*128+k (20 paras)
1802 let m58: *u8 = sys_mmap(SEM_MAGIC_8192); g[58] = m58 as i64 // df per content qterm
1803 let m59: *u8 = sys_mmap(SEM_MAGIC_262144); g[59] = m59 as i64 // SQuAD single-context temp
1804 let m60: *u8 = sys_mmap(SEM_MAGIC_8388608); g[60] = m60 as i64 // sem C matrix vc*vc i64 (vc<=SEM_MAGIC_1024)
1805 let m61: *u8 = sys_mmap(SEM_MAGIC_8192); g[61] = m61 as i64 // sem qvec
1806 let m62: *u8 = sys_mmap(SEM_MAGIC_8192); g[62] = m62 as i64 // sem svec
1807 let m63: *u8 = sys_mmap(SEM_MAGIC_8192); g[63] = m63 as i64 // sem voff
1808 let m64: *u8 = sys_mmap(SEM_MAGIC_8192); g[64] = m64 as i64 // sem vlen
1809 g[65] = 0 // sem vcount
1810 let m66: *u8 = sys_mmap(SEM_MAGIC_32768); g[66] = m66 as i64 // sem vbuf
1811 g[67] = 0 // sem vbump
1812 let m68: *u8 = sys_mmap(256); g[68] = m68 as i64 // pred2 (MODE2, reused per row)
1813 let m69: *u8 = sys_mmap(SEM_MAGIC_2048); g[69] = m69 as i64 // sem per-sentence vidx
1814 // reader (MODE-R) slots
1815 let m80: *u8 = sys_mmap(256); g[80] = m80 as i64 // reader weights w[14]
1816 let m81: *u8 = sys_mmap(SEM_MAGIC_65536); g[81] = m81 as i64 // cand feature matrix 300*14 i64
1817 let m82: *u8 = sys_mmap(SEM_MAGIC_4096); g[82] = m82 as i64 // cand meta 300
1818 let m84: *u8 = sys_mmap(SEM_MAGIC_2048); g[84] = m84 as i64 // reader prediction (candidates can be long)
1819 let m85: *u8 = sys_mmap(256); g[85] = m85 as i64 // averaged-perceptron weight sum
1820 let m86: *u8 = sys_mmap(SEM_MAGIC_2048); g[86] = m86 as i64 // candidate-string tmp (bounded copy 250)
1821 // reader training-F1 scratch (avoids qs_f1's 7-mmap-per-call OOM over millions of training scores)
1822 let m92: *u8 = sys_mmap(SEM_MAGIC_2048); g[92] = m92 as i64 // gold normalized
1823 let m93: *u8 = sys_mmap(SEM_MAGIC_2048); g[93] = m93 as i64 // gold token offsets
1824 let m94: *u8 = sys_mmap(SEM_MAGIC_2048); g[94] = m94 as i64 // gold token lengths
1825 let m95: *u8 = sys_mmap(SEM_MAGIC_2048); g[95] = m95 as i64 // cand normalized
1826 let m96: *u8 = sys_mmap(SEM_MAGIC_2048); g[96] = m96 as i64 // cand token offsets
1827 let m97: *u8 = sys_mmap(SEM_MAGIC_2048); g[97] = m97 as i64 // cand token lengths
1828 let m98: *u8 = sys_mmap(SEM_MAGIC_2048); g[98] = m98 as i64 // used array (qs_common)
1829 let m100: *u8 = sys_mmap(64); g[100] = m100 as i64 // MuSiQue loader stbox (hoisted from per-row mmap)
1830 let m108: *u8 = sys_mmap(SEM_MAGIC_2048); g[108] = m108 as i64 // dump: question PPMI ids (embed feats)
1831 let m110: *u8 = sys_mmap(SEM_MAGIC_2048); g[110] = m110 as i64 // dump: candidate string
1832 let m111: *u8 = sys_mmap(SEM_MAGIC_2048); g[111] = m111 as i64 // dump: candidate normalized
1833 let m112: *u8 = sys_mmap(SEM_MAGIC_2048); g[112] = m112 as i64 // dump: candidate token offsets
1834 let m113: *u8 = sys_mmap(SEM_MAGIC_2048); g[113] = m113 as i64 // dump: candidate token lengths
1835 let m114: *u8 = sys_mmap(64); g[114] = m114 as i64 // dump: 3-i64 embedding feature buffer
1836 let m118: *u8 = sys_mmap(SEM_MAGIC_1024); g[118] = m118 as i64 // full-vec: q_emb_unit (DIM i64)
1837 let m119: *u8 = sys_mmap(SEM_MAGIC_1024); g[119] = m119 as i64 // full-vec: cand_unit scratch
1838 let m121: *u8 = sys_mmap(SEM_MAGIC_1024); g[121] = m121 as i64 // full-vec: DIM-vector feature buffer
1839 let m126: *u8 = sys_mmap(256); g[126] = m126 as i64 // research: best answer buffer
1840 let m127: *u8 = sys_mmap(SEM_MAGIC_16384); g[127] = m127 as i64 // research: per-source answers (64 x 256 fixed-width)
1841 let m128: *u8 = sys_mmap(512); g[128] = m128 as i64 // research: per-source confidence (i64)
1842 let m129: *u8 = sys_mmap(512); g[129] = m129 as i64 // research: per-source argv index (i64)
1843 let m130: *u8 = sys_mmap(SEM_MAGIC_4096); g[130] = m130 as i64 // live prose: sentence-clean scratch
1844 g[79] = 0
1845
1846 // ---- DUMPFEATS MODE: nx_qabench dumpfeats -> export candidate features for offline neural-reader training ----
1847 // dumpfeats: sparse-PPMI embed features are the current BEST (measured: sparse 279 > dense@24 262 -- the
1848 // 24-dim factorization is a lossy compression, weaker than the full sparse signal AS A SCALAR feature).
1849 // embed_load()/dense_cos_int() are committed infra for the next rung (bigger embeddings / full-vector
1850 // features); 'de' selects dense to re-measure. Default 'd' = sparse.
1851 if argc >= 2 { let a1: *u8 = argv[1] as *u8; if a1[0] == (100 as u8) {
1852 if a1[1] == (116 as u8) { return rd_dump_texts(g) } // 'dt' = raw text rows for the neural reader (no ppmi needed)
1853 if a1[1] == (120 as u8) { return rd_dump_texts_xl(g) } // 'dx' = XL rows (all chunks) -> reader_rows_xl.bin
1854 ppmi_load(g)
1855 if a1[1] == (101 as u8) { let el: i64 = embed_load(g); if el == 1 { db_w("dumpfeats: TRAINED dense embeddings (embed_v1.bin)\n" as *u8) } } // 'de' = dense
1856 return rd_dump_feats(g)
1857 } }
1858
1859 // R5 opt-in: if the marker file exists, route the researcher's EXTRACTION through the pretrained sovereign
1860 // Qwen (qwx_extract) instead of the mechanical db_extract. Boots the model once (fail-fast, before serving).
1861 g[132] = 0
1862 let qw_on: i64 = sys_openat_rd("knowledge/index/qwen_reader.on" as *u8)
1863 if qw_on >= 0 {
1864 sys_close(qw_on)
1865 db_w("QWEN-READER = marker present -> booting pretrained Qwen for extraction (nx_qwen_extract)...\n" as *u8)
1866 let qrc: i64 = qwx_load()
1867 if qrc == 0 { g[132] = 1; db_w("QWEN-READER = LIVE (qwx_extract routes qa_read_source extraction)\n" as *u8) }
1868 else { db_w("QWEN-READER = load FAILED -> mechanical db_extract fallback\n" as *u8) }
1869 }
1870
1871 // ---- LIVE ANSWER MODE: nx_qabench answer "<question>" <context-file> ----
1872 // Turns the benchmark pipeline into a callable reading-comprehension capability on ARBITRARY input
1873 // (retrieve-then-read with the trained span reader). Same organ, no benchmark files needed.
1874 if argc >= 4 {
1875 ppmi_load(g) // silent (loads semppmi_v1.bin if present)
1876 rd_load(g) // silent (loads reader_v1.bin if present; else db_extract fallback)
1877 let a1: *u8 = argv[1] as *u8
1878 if a1[0] == (114 as u8) { return qa_research(g, argv[2] as *u8, argv, 3, argc) } // 'r' = research "<q>" ctx1 ctx2...
1879 return qa_answer(g, argv[2] as *u8, argv[3] as *u8) // else answer "<q>" ctx
1880 }
1881
1882 let res: *i64 = sys_mmap(256) as *i64
1883 let pl: i64 = ppmi_load(g)
1884 if pl == 1 { db_w("\nSEM path = CORPUS sparse-PPMI soft-alignment (semppmi_v1.bin loaded: vocab="); db_n(g[71]); db_w(" triples="); db_n(g[72]); db_w(")\n" as *u8) }
1885 else { db_w("\nSEM path = per-row co-occurrence FALLBACK (semppmi_v1.bin absent; run nx_semppmi_build)\n" as *u8) }
1886 // reader: load persisted weights, else train on disjoint corpus chunks
1887 let rl: i64 = rd_load(g)
1888 if rl == 0 { db_w("READER = no reader_v1.bin -> training span-scorer on DISJOINT corpus rows (SQuAD-baseline recipe)...\n" as *u8); rd_train(g) }
1889 if g[79] == 1 {
1890 let w: *i64 = g[80] as *i64
1891 db_w("READER = trained span-scorer LIVE. w=[")
1892 var wi: i64 = 0
1893 while wi < RD_NF { db_n(w[wi]); if wi < RD_NF-1 { db_w(" " as *u8) } wi = wi + 1 }
1894 db_w("]\n" as *u8)
1895 } else { db_w("READER = unavailable (corpus absent); MODE-R column will read 0\n" as *u8) }
1896 db_w("\n-- per-benchmark (same extractor+metric; F1 permille; lex=IDF-lexical hop=bridge SEM=distributional MODE2) --\n" as *u8)
1897 qab_run(g, "knowledge/fetched/qab_squad_big.raw" as *u8, 1, "SQuAD-v1.1 [1-hop, single-ctx]" as *u8, res, 0)
1898 qab_run(g, "knowledge/fetched/qab_hotpot_big.raw" as *u8, 0, "HotpotQA [2-hop, distractor]" as *u8, res, 8)
1899 qab_run(g, "knowledge/fetched/qab_musique_big.raw" as *u8, 3, "MuSiQue [2-4-hop, 20 paras]" as *u8, res, 16)
1900
1901 db_w("\n=== LADDER (mechanical researcher vs published SOTA; harder = more hops/distractors) ===\n" as *u8)
1902 db_w(" RUNG F1_lex F1_hop F1_SEM F1_rdr published-SOTA F1 gap(best)\n" as *u8)
1903 db_w(" SQuAD 1-hop "); db_n(res[1]); db_w(" "); db_n(res[2]); db_w(" "); db_n(res[3]); db_w(" "); db_n(res[5]); db_w(" ~900 (BiDAF+ 2016..) "); db_n(900 - qab_max4(res[1],res[2],res[3],res[5])); db_w("\n" as *u8)
1904 db_w(" HotpotQA 2-hop "); db_n(res[9]); db_w(" "); db_n(res[10]); db_w(" "); db_n(res[11]); db_w(" "); db_n(res[13]); db_w(" ~700-820 (strong) "); db_n(760 - qab_max4(res[9],res[10],res[11],res[13])); db_w("\n" as *u8)
1905 db_w(" MuSiQue 2-4-hop "); db_n(res[17]); db_w(" "); db_n(res[18]); db_w(" "); db_n(res[19]); db_w(" "); db_n(res[21]); db_w(" ~490-690 (SOTA/RoHT) "); db_n(590 - qab_max4(res[17],res[18],res[19],res[21])); db_w("\n" as *u8)
1906
1907 // best-mode per rung + which mode won (honest: name the winner, never hide a losing column)
1908 let bS: i64 = qab_max4(res[1],res[2],res[3],res[5])
1909 let bH: i64 = qab_max4(res[9],res[10],res[11],res[13])
1910 let bM: i64 = qab_max4(res[17],res[18],res[19],res[21])
1911 db_w(" BEST-MODE ladder: SQuAD "); db_n(bS); db_w(" / HotpotQA "); db_n(bH); db_w(" / MuSiQue "); db_n(bM); db_w(" (rdr=trained span reader)\n" as *u8)
1912 // ORACLE ceiling (best candidate in the set) vs reader -> names the binding stage: gap(oracle-rdr) big = SCORING
1913 // headroom; oracle ~= reader = candidate GENERATION is the wall.
1914 db_w(" ORACLE ceiling: SQuAD "); db_n(res[6]); db_w(" / HotpotQA "); db_n(res[14]); db_w(" / MuSiQue "); db_n(res[22]); db_w(" (best candidate available; rdr-to-oracle gap = scoring headroom)\n" as *u8)
1915 db_w(" -> binding stage: SQuAD "); qab_bind(res[5], res[6]); db_w(" / HotpotQA "); qab_bind(res[13], res[14]); db_w(" / MuSiQue "); qab_bind(res[21], res[22]); db_w("\n" as *u8)
1916
1917 var pass: i64 = 0
1918 if res[0] > 0 { pass = pass + 1 }
1919 if res[8] > 0 { pass = pass + 1 }
1920 if res[16] > 0 { pass = pass + 1 }
1921 // difficulty gradient must hold on the BEST mode: SQuAD >= HotpotQA >= MuSiQue -> the hops ARE the gap
1922 if bS >= bH { if bH >= bM { pass = pass + 1 } }
1923 db_w("\nTEETH (squad+hotpot+musique ran + best-mode difficulty-gradient holds) = "); db_n(pass); db_w("/4\n" as *u8)
1924 if pass == 4 {
1925 db_w("GREEN -- 4-mode ladder measured (lex/hop/SEM/trained-reader). Best-mode F1 falls monotonically as\n" as *u8)
1926 db_w("HOPS + DISTRACTORS rise. The trained span reader (SQuAD-baseline recipe) attacks the EXTRACTION stage\n" as *u8)
1927 db_w("the 6x-corpus datapoint proved is now binding. Honest: no rung at SOTA; next lever = R1 no-float reader.\n" as *u8)
1928 return 0
1929 }
1930 db_w("RED -- a rung did not run (fetch its subset) or the best-mode difficulty gradient did not hold\n" as *u8)
1931 return 1
1932}
1933
1934func qab_max4(a: i64, b: i64, c: i64, d: i64) -> i64 {
1935 var m: i64 = a
1936 if b > m { m = b }
1937 if c > m { m = c }
1938 if d > m { m = d }
1939 return m
1940}
1941
1942// name the binding stage from (reader F1, oracle F1): a wide gap = the right candidate exists but the reader
1943// mis-scores it (SCORING); a narrow gap = the reader is near-optimal and the answer isn't a candidate (GENERATION).
1944func qab_bind(rdr: i64, orc: i64) -> i64 {
1945 let gap: i64 = orc - rdr
1946 if gap > 150 { db_w("SCORING(gap "); db_n(gap); db_w(")" as *u8) } else { db_w("GENERATION(gap "); db_n(gap); db_w(")" as *u8) }
1947 return 0
1948}
1949
1950// read a whole file into buf (null-terminated); returns byte length, -1 on open failure.
1951func qa_read_file(path: *u8, buf: *u8, cap: i64) -> i64 {
1952 let fd: i64 = sys_openat_rd(path)
1953 if fd < 0 { return 0-1 }
1954 var total: i64 = 0
1955 var r: i64 = 1
1956 while r > 0 {
1957 let left: i64 = cap - 1 - total
1958 if left <= 0 { r = 0 } else {
1959 r = sys_read(fd, (buf as i64 + total) as *u8, left)
1960 if r > 0 { total = total + r }
1961 }
1962 }
1963 sys_close(fd)
1964 buf[total] = (0 as u8)
1965 return total
1966}
1967
1968// LIVE answer: run the retrieve-then-read pipeline on an arbitrary (question, context) and print the answer +
1969// its source sentence (citation). Reuses the exact benchmark machinery; reader if loaded, else db_extract.
1970// core: read ONE source vs the question already in g[4]; extract the answer into pred; stash the best-sentence
1971// index in g[125]; return CONFIDENCE = best-sentence question-hit score (2*sentence-hits + title-hits), which is
1972// comparable across sources. Returns -1 on read/parse failure. Shared by single-source answer + multi-source.
1973func qa_read_source(g: *i64, ctxpath: *u8, pred: *u8) -> i64 {
1974 let ctxb: *u8 = g[59] as *u8
1975 let cl: i64 = qa_read_file(ctxpath, ctxb, SEM_MAGIC_200000)
1976 if cl <= 0 { return 0-1 }
1977 g[14] = 1
1978 let pto: *i64 = g[12] as *i64
1979 let ptl: *i64 = g[13] as *i64
1980 let ptb: *u8 = g[11] as *u8
1981 pto[0]=0; ptl[0]=0; ptb[0]=(0 as u8)
1982 let ns: i64 = qa_split_prose(g, ctxb, cl) // prose-filtered: drops web chrome/wikitext/JSON
1983 g[19] = ns
1984 if ns <= 0 { return 0-1 }
1985 db_norm_row(g)
1986 db_score_row(g)
1987 let top1: i64 = db_top_para(g, 0-1)
1988 let top2: i64 = db_top_para(g, top1)
1989 let s0: i64 = db_best_sentence(g)
1990 g[125] = s0
1991 var conf: i64 = 0
1992 if s0 >= 0 {
1993 let shit: *i64 = g[39] as *i64
1994 let thit: *i64 = g[40] as *i64
1995 let sp: *i64 = g[18] as *i64
1996 conf = 2*shit[s0] + thit[sp[s0]]
1997 }
1998 let yn: i64 = db_is_yesno(g)
1999 let wantnum: i64 = db_prefer_numeric(g)
2000 var qwdone: i64 = 0
2001 if g[132] == 1 { if qwx_ready() == 1 { if s0 >= 0 { qwdone = qwx_extract(g, s0, pred) } } } // R5: Qwen reads context centered on best sentence s0
2002 if qwdone == 0 {
2003 if yn == 1 { db_setpred(pred, "yes" as *u8, 3) } else {
2004 var got: i64 = 0
2005 if wantnum == 1 { got = db_sweep_numeric(g, top1, top2, pred) }
2006 if got == 0 { if db_wants_location(g) == 1 { got = db_sweep_location(g, top1, top2, pred) } } // where -> place answer
2007 if got == 0 { if s0 >= 0 { db_extract(g, s0, pred) } else { db_setpred(pred, "(no answer found)" as *u8, 17) } }
2008 }
2009 }
2010 // ACCURATE CITATION: a sweep may extract from a different sentence than the top-match s0, so cite the
2011 // sentence that actually CONTAINS the answer (fall back to s0 if not found).
2012 if yn == 0 {
2013 let so2: *i64 = g[16] as *i64
2014 let sb2: *u8 = g[15] as *u8
2015 var cs: i64 = 0
2016 var found: i64 = 0
2017 while cs < g[19] {
2018 if found == 0 {
2019 if qa_contains_ci((sb2 as i64 + so2[cs]) as *u8, pred) == 1 { g[125] = cs; found = 1 }
2020 }
2021 cs = cs + 1
2022 }
2023 }
2024 return conf
2025}
2026
2027func qa_streq(a: *u8, b: *u8) -> i64 {
2028 var i: i64 = 0
2029 while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 }
2030 if b[i] != (0 as u8) { return 0 }
2031 return 1
2032}
2033
2034func qa_lc(c: i64) -> i64 { if c >= 65 { if c <= 90 { return c + 32 } } return c }
2035func qa_slen(s: *u8) -> i64 { var i: i64 = 0; while s[i] != (0 as u8) { i = i + 1 } return i }
2036
2037// case-insensitive: is `needle` a substring of `hay`?
2038func qa_contains_ci(hay: *u8, needle: *u8) -> i64 {
2039 let hl: i64 = qa_slen(hay)
2040 let nl: i64 = qa_slen(needle)
2041 if nl == 0 { return 0 }
2042 if nl > hl { return 0 }
2043 var i: i64 = 0
2044 while i + nl <= hl {
2045 var j: i64 = 0
2046 var m: i64 = 1
2047 while j < nl { if m == 1 { if qa_lc(hay[i+j] as i64) != qa_lc(needle[j] as i64) { m = 0 } } j = j + 1 }
2048 if m == 1 { return 1 }
2049 i = i + 1
2050 }
2051 return 0
2052}
2053
2054// two answers CORROBORATE if one is contained in the other (case-insensitive): "Einstein" ~ "Albert Einstein".
2055func qa_answer_agree(a: *u8, b: *u8) -> i64 {
2056 if qa_contains_ci(a, b) == 1 { return 1 }
2057 if qa_contains_ci(b, a) == 1 { return 1 }
2058 return 0
2059}
2060// (a definitional hit-density sentence selector was tried to fix the Eiffel "New York Times" miss -- it was
2061// INERT (the concise lead still did not win; deeper debug = single-example whack-a-mole) so it was removed.
2062// The Eiffel-class selection miss needs a trained neural reader, not a mechanical density heuristic.)
2063
2064func qa_answer(g: *i64, question: *u8, ctxpath: *u8) -> i64 {
2065 let q: *u8 = g[4] as *u8
2066 var i: i64 = 0
2067 while question[i] != (0 as u8) { if i < SEM_MAGIC_3999 { q[i] = question[i] } i = i + 1 }
2068 var qn: i64 = i
2069 if qn > SEM_MAGIC_3999 { qn = SEM_MAGIC_3999 }
2070 q[qn] = (0 as u8)
2071 let pred: *u8 = g[84] as *u8
2072 let conf: i64 = qa_read_source(g, ctxpath, pred)
2073 if conf < 0 { db_w("ERROR: cannot read/parse context file: "); db_w(ctxpath); db_w("\n" as *u8); return 1 }
2074 db_w("Q: "); db_w(q); db_w("\n" as *u8)
2075 db_w("A: "); db_w(pred); db_w("\n" as *u8)
2076 let s0: i64 = g[125]
2077 if s0 >= 0 {
2078 let sb: *u8 = g[15] as *u8
2079 let so: *i64 = g[16] as *i64
2080 db_w(" source: "); db_w((sb as i64 + so[s0]) as *u8); db_w("\n" as *u8)
2081 }
2082 return 0
2083}
2084
2085// DEEP RESEARCH: read the question across MULTIPLE fetched sources, pick the highest-confidence answer, and
2086// report the cross-source picture + AGREEMENT (how many sources produced the same answer = corroboration). This
2087// is the researcher's synthesis stage -- multi-source, the actual "deep research" loop.
2088func qa_research(g: *i64, question: *u8, argv: *i64, cstart: i64, argc: i64) -> i64 {
2089 let q: *u8 = g[4] as *u8
2090 var i: i64 = 0
2091 while question[i] != (0 as u8) { if i < SEM_MAGIC_3999 { q[i] = question[i] } i = i + 1 }
2092 var qn: i64 = i
2093 if qn > SEM_MAGIC_3999 { qn = SEM_MAGIC_3999 }
2094 q[qn] = (0 as u8)
2095 db_w("=== DEEP RESEARCH (multi-source) ===\nQ: "); db_w(q); db_w("\n" as *u8)
2096 let pred: *u8 = g[84] as *u8
2097 let bestpred: *u8 = g[126] as *u8
2098 let ansb: *u8 = g[127] as *u8 // per-source answers, 256-byte fixed-width records
2099 let confs: *i64 = g[128] as *i64 // per-source confidence
2100 let sidx: *i64 = g[129] as *i64 // per-source argv index (for citation)
2101 var nsrc: i64 = 0
2102 var si: i64 = cstart
2103 while si < argc {
2104 if nsrc < 64 {
2105 let ctxpath: *u8 = argv[si] as *u8
2106 let conf: i64 = qa_read_source(g, ctxpath, pred)
2107 if conf >= 0 {
2108 db_w(" [src "); db_n(si-cstart); db_w("] conf="); db_n(conf); db_w(" A: "); db_w(pred); db_w(" <- "); db_w(ctxpath); db_w("\n" as *u8)
2109 qa_strcpy((ansb as i64 + nsrc*256) as *u8, pred)
2110 confs[nsrc] = conf
2111 sidx[nsrc] = si
2112 nsrc = nsrc + 1
2113 } else {
2114 db_w(" [src "); db_n(si-cstart); db_w("] (unreadable / no prose) <- "); db_w(ctxpath); db_w("\n" as *u8)
2115 }
2116 }
2117 si = si + 1
2118 }
2119 if nsrc == 0 { db_w("NO ANSWER: no readable source\n" as *u8); return 1 }
2120 // CORROBORATION-FIRST aggregation: the answer the MOST sources support wins (tie-break by confidence).
2121 // This is the deep-research principle -- agreement across independent sources beats a lone high score.
2122 var best: i64 = 0-1
2123 var bestagree: i64 = 0-1
2124 var bestconf: i64 = 0-1
2125 var a: i64 = 0
2126 while a < nsrc {
2127 let ca: *u8 = (ansb as i64 + a*256) as *u8
2128 var agree: i64 = 0
2129 var b: i64 = 0
2130 while b < nsrc { if qa_answer_agree(ca, (ansb as i64 + b*256) as *u8) == 1 { agree = agree + 1 } b = b + 1 }
2131 var take: i64 = 0
2132 if agree > bestagree { take = 1 } else { if agree == bestagree { if confs[a] > bestconf { take = 1 } } }
2133 if take == 1 { bestagree = agree; bestconf = confs[a]; best = a }
2134 a = a + 1
2135 }
2136 qa_strcpy(bestpred, (ansb as i64 + best*256) as *u8)
2137 db_w("\n=== ANSWER: "); db_w(bestpred); db_w("\n "); db_n(bestagree); db_w("/"); db_n(nsrc); db_w(" sources corroborate (confidence "); db_n(bestconf); db_w(")\n source: "); db_w(argv[sidx[best]] as *u8); db_w("\n" as *u8)
2138 return 0
2139}
2140
2141func qa_strcpy(dst: *u8, src: *u8) -> i64 {
2142 var i: i64 = 0
2143 while src[i] != (0 as u8) { if i < 250 { dst[i] = src[i] } i = i + 1 }
2144 var n: i64 = i; if n > 250 { n = 250 } dst[n] = (0 as u8)
2145 return 0
2146}