code wiki / _hdl_build / nx_book_search_lib.nx

nx_book_search_lib.nx source

↩ module page · 118 lines · 5381 B

1// nx_book_search_lib.nx -- shared SC20 library-search CORE (NO main). Wired into nx_media_server /api/search. 2// Multi-term, case-insensitive, BM25-ranked TOP-N over a newline-delimited index file (idx_book.wsl). 3// Reuses nx_bm25 (bm_idf_micro/bm_sat_milli/bm_token_count/bm_df) + nx_research_extract (re_count). Same proven 4// algorithm as the gated nx_book_search, extended to multi-term + top-N. license_tier: ORIGINAL 5import "nx_research_extract.nx" // re_count 6import "nx_bm25.nx" // bm_token_count / bm_idf_micro / bm_sat_milli / bm_df 7import "nx_syscalls.nx" 8const K_MAGIC_300000: i64 = 300000 9const K_MAGIC_1024: i64 = 1024 10 11func bks_lc(c: i64) -> i64 { if c >= 65 { if c <= 90 { return c + 32 } } return c } 12 13// Search `idxpath` for `query`; fill res_off/res_len/res_score with the top `maxn` results (score-desc), 14// set cnt[0] = number found. RETURNS the original index buffer pointer (as i64) so the caller can emit the 15// path bytes at buf+res_off[i] for res_len[i] bytes. 16func bks_search(idxpath: *u8, query: *u8, res_off: *i64, res_len: *i64, res_score: *i64, maxn: i64, cnt: *i64) -> i64 { 17 cnt[0] = 0 18 let lenbox: *i64 = sys_mmap(8) as *i64 19 let buf: *u8 = sys_read_file(idxpath, lenbox) 20 let n: i64 = lenbox[0] 21 if n <= 0 { return buf as i64 } 22 23 // lowercased mirror for case-insensitive ranking 24 let lc: *u8 = sys_mmap(n + 16) 25 var i: i64 = 0 26 while i < n { lc[i] = bks_lc(buf[i] as i64) as u8; i = i + 1 } 27 28 // split into docs (one line = one item). CAP sized for the largest index (idx_ai ~228k). 29 let CAP: i64 = K_MAGIC_300000 30 let offs: *i64 = sys_mmap(CAP * 8) as *i64 31 let lens: *i64 = sys_mmap(CAP * 8) as *i64 32 let dls: *i64 = sys_mmap(CAP * 8) as *i64 33 let ptrs: *i64 = sys_mmap(CAP * 8) as *i64 34 var ndocs: i64 = 0; var total: i64 = 0; var s: i64 = 0 35 while s < n { 36 var e: i64 = s; var f: i64 = 0 37 while f == 0 { if e >= n { f = 1 } else { if lc[e] == (10 as u8) { f = 1 } else { e = e + 1 } } } 38 let llen: i64 = e - s 39 if llen > 0 { if ndocs < CAP { 40 offs[ndocs] = s; lens[ndocs] = llen; ptrs[ndocs] = (lc as i64) + s 41 dls[ndocs] = bm_token_count((lc as i64 + s) as *u8, llen); total = total + dls[ndocs]; ndocs = ndocs + 1 42 } } 43 s = e + 1 44 } 45 var avgdl: i64 = 1 46 if ndocs > 0 { avgdl = total / ndocs } 47 if avgdl <= 0 { avgdl = 1 } 48 49 // lowercase the query into qcopy and tokenize on spaces (NUL-separated) -> qterms[] 50 let qcopy: *u8 = sys_mmap(K_MAGIC_1024) 51 var ql: i64 = 0 52 while query[ql] != (0 as u8) { if ql < 1022 { qcopy[ql] = bks_lc(query[ql] as i64) as u8 } ql = ql + 1 } 53 qcopy[ql] = 0 as u8 54 let QMAX: i64 = 16 55 let qterms: *i64 = sys_mmap(QMAX * 8) as *i64 56 var nq: i64 = 0 57 var qi: i64 = 0 58 while qi < ql { 59 if qcopy[qi] == (32 as u8) { qi = qi + 1 } 60 else { 61 if nq < QMAX { qterms[nq] = (qcopy as i64) + qi; nq = nq + 1 } 62 var atend: i64 = 0 63 while atend == 0 { 64 if qi >= ql { atend = 1 } 65 else { if qcopy[qi] == (32 as u8) { qcopy[qi] = 0 as u8; qi = qi + 1; atend = 1 } else { qi = qi + 1 } } 66 } 67 } 68 } 69 if nq == 0 { return buf as i64 } 70 71 // precompute idf per term (once) 72 let idf: *i64 = sys_mmap(QMAX * 8) as *i64 73 var t: i64 = 0 74 while t < nq { idf[t] = bm_idf_micro(ndocs, bm_df(ptrs, lens, ndocs, qterms[t] as *u8)); t = t + 1 } 75 76 // score every doc; maintain top-`maxn` in res_* (score-descending) via insertion 77 var k: i64 = 0 78 while k < ndocs { 79 var sc: i64 = 0 80 var tt: i64 = 0 81 while tt < nq { 82 let term: *u8 = qterms[tt] as *u8 83 let tf: i64 = re_count(ptrs[k] as *u8, lens[k], term) 84 if tf > 0 { sc = sc + (idf[tt] * bm_sat_milli(tf, dls[k], avgdl)) / 1000 } 85 tt = tt + 1 86 } 87 if sc > 0 { 88 if cnt[0] < maxn { 89 let w: i64 = cnt[0] 90 res_off[w] = offs[k]; res_len[w] = lens[k]; res_score[w] = sc; cnt[0] = w + 1 91 var b: i64 = w 92 while b > 0 { 93 if res_score[b] > res_score[b-1] { 94 let ao: i64 = res_off[b]; res_off[b] = res_off[b-1]; res_off[b-1] = ao 95 let al: i64 = res_len[b]; res_len[b] = res_len[b-1]; res_len[b-1] = al 96 let asc: i64 = res_score[b]; res_score[b] = res_score[b-1]; res_score[b-1] = asc 97 b = b - 1 98 } else { b = 0 } 99 } 100 } else { 101 if sc > res_score[maxn-1] { 102 res_off[maxn-1] = offs[k]; res_len[maxn-1] = lens[k]; res_score[maxn-1] = sc 103 var b: i64 = maxn - 1 104 while b > 0 { 105 if res_score[b] > res_score[b-1] { 106 let ao: i64 = res_off[b]; res_off[b] = res_off[b-1]; res_off[b-1] = ao 107 let al: i64 = res_len[b]; res_len[b] = res_len[b-1]; res_len[b-1] = al 108 let asc: i64 = res_score[b]; res_score[b] = res_score[b-1]; res_score[b-1] = asc 109 b = b - 1 110 } else { b = 0 } 111 } 112 } 113 } 114 } 115 k = k + 1 116 } 117 return buf as i64 118}