nx_field_lib.nx source
↩ module page · 405 lines · 18041 B
1// nx_field_lib.nx -- THE FIELD DISCOVERY CORE (/compare/fieldwatch FW1, 2026-09-05): the industry's own lists,
2// read mechanically, become a DISCOVERED population of rivals per domain -- never a seat's six picks.
3//
4// WHY (operator 2026-09-05, on the modding board's six columns): "those arent a good sample of the industry ...
5// the /compare emitter lags heavily there instead of giving a true beyond gartner or google search return
6// alternatives and competitors and beyond real view for all these domains." A column is a choice; a field is a
7// measurement. This lib turns three kinds of public list into candidate rows with EVIDENCE COUNTS:
8// wiki-raw Wikipedia wikitext (action=raw): every [[Target]] and [[Target|Text]] link OUTSIDE <ref> blocks and
9// OUTSIDE {{templates}} (citations and infobox plumbing are not rivals), namespace links excluded by
10// their colon (Category: File: Help: Template: ...), counted per occurrence
11// gh-topic a GitHub topic page: the repository anchors, whose markup carries the class token the page itself
12// uses for the repo name; the two-segment href is kept as the link
13// md-list a raw Markdown list (awesome-style): every - [Name](link) or * [Name](link) item
14// One table holds every candidate across seeds; a name seen in several seeds carries a seeds_hit mask and a
15// mentions count, and fl_emit writes rows sorted by seeds then mentions, so "who the field agrees on" is
16// arithmetic. The table is a declared population: when it fills, capped=1 is ANNOUNCED, never silent.
17// license_tier: ORIGINAL No hw writes (Rule 26).
18import "nx_syscalls.nx"
19
20const FL_MAX_CAND: i64 = 4096 // candidates per run; a fill is announced (capped=1), never silently dropped
21const FL_NAME_CAP: i64 = 96 // bytes per name incl. NUL
22const FL_LINK_CAP: i64 = 160 // bytes per link incl. NUL
23const FL_MAX_SEEDS: i64 = 62 // seeds_hit is a bit mask; bit 62 is the last safe bit in an i64
24const FL_REC_W: i64 = 4 // per-candidate words: mentions, seeds_hit, first_seed, kind_of_first
25const FL_KIND_WIKI: i64 = 1
26const FL_KIND_GH: i64 = 2
27const FL_KIND_MD: i64 = 3
28const FL_KIND_NAMED: i64 = 4 // a DECLARED member of the population (seed row seed|key|url|named|why|Display Name): the operator's
29 // 2026-09-05 correction -- Nexus Mods, LoversLab and the other major communities were absent from the
30 // discovered lists, so the population is ENUMERATED as data and the public lists corroborate it
31const FL_CH_LB: i64 = 91 // [
32const FL_CH_RB: i64 = 93 // ]
33const FL_CH_LBRACE: i64 = 123 // {
34const FL_CH_RBRACE: i64 = 125 // }
35const FL_CH_LT: i64 = 60 // <
36const FL_CH_GT: i64 = 62 // >
37const FL_CH_SLASH: i64 = 47
38const FL_CH_PIPE: i64 = 124
39const FL_CH_COLON: i64 = 58
40const FL_CH_HASH: i64 = 35
41const FL_CH_SPACE: i64 = 32
42const FL_CH_NL: i64 = 10
43const FL_CH_CR: i64 = 13
44const FL_CH_TAB: i64 = 9
45const FL_CH_MINUS: i64 = 45
46const FL_CH_STAR: i64 = 42
47const FL_CH_LPAREN: i64 = 40
48const FL_CH_RPAREN: i64 = 41
49const FL_CH_QUOTE: i64 = 34
50const FL_CH_UPPER_A: i64 = 65
51const FL_CH_UPPER_Z: i64 = 90
52const FL_CASE_DELTA: i64 = 32
53const FL_GH_HREF_BACK: i64 = 400 // bytes to look back from the repo-name anchor for its href
54const FL_ASCII_ZERO: i64 = 48
55const FL_ASCII_NINE: i64 = 57
56
57// ---- the candidate table: names arena, links arena, records ----
58func fl_tbl_new() -> *i64 {
59 let t: *i64 = sys_mmap(8 * 8) as *i64
60 t[0] = sys_mmap(FL_MAX_CAND * FL_NAME_CAP) as i64
61 t[1] = sys_mmap(FL_MAX_CAND * FL_LINK_CAP) as i64
62 t[2] = sys_mmap(FL_MAX_CAND * FL_REC_W * 8) as i64
63 t[3] = 0 // count
64 t[4] = 0 // capped
65 t[5] = 0 // total mentions added (every accepted occurrence)
66 return t
67}
68func fl_count(t: *i64) -> i64 { return t[3] }
69func fl_capped(t: *i64) -> i64 { return t[4] }
70func fl_mentions_total(t: *i64) -> i64 { return t[5] }
71func fl_name_at(t: *i64, i: i64) -> *u8 { return (t[0] + i * FL_NAME_CAP) as *u8 }
72func fl_link_at(t: *i64, i: i64) -> *u8 { return (t[1] + i * FL_LINK_CAP) as *u8 }
73func fl_rec(t: *i64, i: i64, f: i64) -> i64 { let r: *i64 = t[2] as *i64; return r[i * FL_REC_W + f] }
74func fl_rec_set(t: *i64, i: i64, f: i64, v: i64) -> i64 { let r: *i64 = t[2] as *i64; r[i * FL_REC_W + f] = v; return 0 }
75func fl_lc(c: i64) -> i64 { if c >= FL_CH_UPPER_A { if c <= FL_CH_UPPER_Z { return c + FL_CASE_DELTA } } return c }
76// case-insensitive equality of a NUL-terminated name against a byte span
77func fl_name_eq(name: *u8, b: *u8, s: i64, e: i64) -> i64 {
78 var i: i64 = 0
79 var p: i64 = s
80 while p < e {
81 if name[i] == (0 as u8) { return 0 }
82 if fl_lc(name[i] as i64) != fl_lc(b[p] as i64) { return 0 }
83 i = i + 1
84 p = p + 1
85 }
86 if name[i] != (0 as u8) { return 0 }
87 return 1
88}
89func fl_find(t: *i64, b: *u8, s: i64, e: i64) -> i64 {
90 var i: i64 = 0
91 let n: i64 = t[3]
92 while i < n { if fl_name_eq(fl_name_at(t, i), b, s, e) == 1 { return i } i = i + 1 }
93 return 0 - 1
94}
95// trim ASCII whitespace off a span in place (returns 1 when something is left)
96func fl_trim(b: *u8, sp: *i64, ep: *i64) -> i64 {
97 var s: i64 = sp[0]
98 var e: i64 = ep[0]
99 while s < e {
100 let c: i64 = b[s] as i64
101 if c == FL_CH_SPACE { s = s + 1 } else { if c == FL_CH_TAB { s = s + 1 } else { if c == FL_CH_CR { s = s + 1 } else { break } } }
102 }
103 while e > s {
104 let c: i64 = b[e - 1] as i64
105 if c == FL_CH_SPACE { e = e - 1 } else { if c == FL_CH_TAB { e = e - 1 } else { if c == FL_CH_CR { e = e - 1 } else { break } } }
106 }
107 sp[0] = s
108 ep[0] = e
109 if e > s { return 1 }
110 return 0
111}
112// add one occurrence: returns the candidate index, or -1 when refused (empty, too long, table full)
113func fl_add(t: *i64, b: *u8, s0: i64, e0: i64, link: *u8, ls: i64, le: i64, seed: i64, kind: i64) -> i64 {
114 let sp: *i64 = sys_mmap(16) as *i64
115 let ep: *i64 = sys_mmap(16) as *i64
116 sp[0] = s0
117 ep[0] = e0
118 if fl_trim(b, sp, ep) == 0 { return 0 - 1 }
119 let s: i64 = sp[0]
120 let e: i64 = ep[0]
121 if e - s >= FL_NAME_CAP - 1 { return 0 - 1 }
122 var bit: i64 = 1
123 var k: i64 = 0
124 while k < seed { if k < FL_MAX_SEEDS { bit = bit * 2 } k = k + 1 }
125 if seed >= FL_MAX_SEEDS { bit = 0 }
126 let found: i64 = fl_find(t, b, s, e)
127 if found >= 0 {
128 fl_rec_set(t, found, 0, fl_rec(t, found, 0) + 1)
129 fl_rec_set(t, found, 1, fl_rec(t, found, 1) | bit)
130 t[5] = t[5] + 1
131 return found
132 }
133 if t[3] >= FL_MAX_CAND { t[4] = 1; return 0 - 1 }
134 let i: i64 = t[3]
135 let nm: *u8 = fl_name_at(t, i)
136 var p: i64 = 0
137 while s + p < e { nm[p] = b[s + p]; p = p + 1 }
138 nm[p] = 0 as u8
139 let lk: *u8 = fl_link_at(t, i)
140 var q: i64 = 0
141 if (link as i64) != 0 {
142 while ls + q < le { if q < FL_LINK_CAP - 1 { lk[q] = link[ls + q] } q = q + 1 }
143 if q > FL_LINK_CAP - 1 { q = FL_LINK_CAP - 1 }
144 }
145 lk[q] = 0 as u8
146 fl_rec_set(t, i, 0, 1)
147 fl_rec_set(t, i, 1, bit)
148 fl_rec_set(t, i, 2, seed)
149 fl_rec_set(t, i, 3, kind)
150 t[3] = i + 1
151 t[5] = t[5] + 1
152 return i
153}
154// does the span carry a colon (a namespace link) or a hash (a section anchor)?
155func fl_span_has(b: *u8, s: i64, e: i64, ch: i64) -> i64 {
156 var p: i64 = s
157 while p < e { if (b[p] as i64) == ch { return 1 } p = p + 1 }
158 return 0
159}
160func fl_starts_at(b: *u8, n: i64, p: i64, lit: *u8) -> i64 {
161 var i: i64 = 0
162 while lit[i] != (0 as u8) {
163 if p + i >= n { return 0 }
164 if fl_lc(b[p + i] as i64) != fl_lc(lit[i] as i64) { return 0 }
165 i = i + 1
166 }
167 return 1
168}
169
170// ---- wiki-raw: [[Target]] links outside <ref>...</ref>, <ref .../> and {{...}} ----
171func fl_wiki_scan(t: *i64, b: *u8, n: i64, seed: i64) -> i64 {
172 var added: i64 = 0
173 var refdepth: i64 = 0
174 var tdepth: i64 = 0
175 var p: i64 = 0
176 while p < n {
177 let c: i64 = b[p] as i64
178 if c == FL_CH_LT {
179 if fl_starts_at(b, n, p, "<ref" as *u8) == 1 {
180 // self-closing <ref name=x/> opens nothing
181 var q: i64 = p
182 var selfclose: i64 = 0
183 while q < n { if (b[q] as i64) == FL_CH_GT { if (b[q - 1] as i64) == FL_CH_SLASH { selfclose = 1 } break } q = q + 1 }
184 if selfclose == 0 { refdepth = refdepth + 1 }
185 p = q + 1
186 continue
187 }
188 if fl_starts_at(b, n, p, "</ref" as *u8) == 1 {
189 if refdepth > 0 { refdepth = refdepth - 1 }
190 var q2: i64 = p
191 while q2 < n { if (b[q2] as i64) == FL_CH_GT { break } q2 = q2 + 1 }
192 p = q2 + 1
193 continue
194 }
195 }
196 if c == FL_CH_LBRACE { if p + 1 < n { if (b[p + 1] as i64) == FL_CH_LBRACE { tdepth = tdepth + 1; p = p + 2; continue } } }
197 if c == FL_CH_RBRACE { if p + 1 < n { if (b[p + 1] as i64) == FL_CH_RBRACE { if tdepth > 0 { tdepth = tdepth - 1 } p = p + 2; continue } } }
198 if c == FL_CH_LB { if p + 1 < n { if (b[p + 1] as i64) == FL_CH_LB {
199 // a link: target runs to | or ]]
200 var s: i64 = p + 2
201 var e: i64 = s
202 var closed: i64 = 0
203 var cut: i64 = 0 - 1
204 while e < n {
205 let d: i64 = b[e] as i64
206 if d == FL_CH_PIPE { if cut < 0 { cut = e } }
207 if d == FL_CH_RB { if e + 1 < n { if (b[e + 1] as i64) == FL_CH_RB { closed = 1; break } } }
208 if d == FL_CH_NL { break }
209 e = e + 1
210 }
211 if closed == 1 {
212 var te: i64 = e
213 if cut >= 0 { te = cut }
214 if refdepth == 0 { if tdepth == 0 {
215 if fl_span_has(b, s, te, FL_CH_COLON) == 0 { if fl_span_has(b, s, te, FL_CH_HASH) == 0 {
216 if fl_add(t, b, s, te, b, s, te, seed, FL_KIND_WIKI) >= 0 { added = added + 1 }
217 } }
218 } }
219 p = e + 2
220 continue
221 }
222 } } }
223 p = p + 1
224 }
225 return added
226}
227
228// ---- gh-topic: repo anchors carrying the page's own repo-name class token ----
229func fl_gh_scan(t: *i64, b: *u8, n: i64, seed: i64) -> i64 {
230 var added: i64 = 0
231 let tok: *u8 = "class=\x22Link text-bold wb-break-word\x22>" as *u8
232 var tl: i64 = 0
233 while tok[tl] != (0 as u8) { tl = tl + 1 }
234 var p: i64 = 0
235 while p + tl <= n {
236 if fl_starts_at(b, n, p, tok) == 1 {
237 let s: i64 = p + tl
238 var e: i64 = s
239 while e < n { if (b[e] as i64) == FL_CH_LT { break } e = e + 1 }
240 // the href sits shortly before the class token: href="/owner/repo"
241 var hs: i64 = 0 - 1
242 var he: i64 = 0 - 1
243 var back: i64 = p
244 var lim: i64 = p - FL_GH_HREF_BACK
245 if lim < 0 { lim = 0 }
246 while back > lim {
247 if fl_starts_at(b, n, back, "href=\x22" as *u8) == 1 {
248 hs = back + 6
249 he = hs
250 while he < n { if (b[he] as i64) == FL_CH_QUOTE { break } he = he + 1 }
251 break
252 }
253 back = back - 1
254 }
255 if e > s {
256 if hs >= 0 { if fl_add(t, b, s, e, b, hs, he, seed, FL_KIND_GH) >= 0 { added = added + 1 } }
257 if hs < 0 { if fl_add(t, b, s, e, 0 as *u8, 0, 0, seed, FL_KIND_GH) >= 0 { added = added + 1 } }
258 }
259 p = e
260 continue
261 }
262 p = p + 1
263 }
264 return added
265}
266
267// ---- md-list: - [Name](link) and * [Name](link) items ----
268func fl_md_scan(t: *i64, b: *u8, n: i64, seed: i64) -> i64 {
269 var added: i64 = 0
270 var p: i64 = 0
271 while p < n {
272 // line start
273 var q: i64 = p
274 while q < n { if (b[q] as i64) != FL_CH_SPACE { if (b[q] as i64) != FL_CH_TAB { break } } q = q + 1 }
275 if q < n {
276 let c: i64 = b[q] as i64
277 var isitem: i64 = 0
278 if c == FL_CH_MINUS { isitem = 1 }
279 if c == FL_CH_STAR { isitem = 1 }
280 if isitem == 1 { if q + 2 < n { if (b[q + 1] as i64) == FL_CH_SPACE { if (b[q + 2] as i64) == FL_CH_LB {
281 let s: i64 = q + 3
282 var e: i64 = s
283 while e < n { if (b[e] as i64) == FL_CH_RB { break } if (b[e] as i64) == FL_CH_NL { break } e = e + 1 }
284 if e < n { if (b[e] as i64) == FL_CH_RB { if e + 1 < n { if (b[e + 1] as i64) == FL_CH_LPAREN {
285 let ls: i64 = e + 2
286 var le: i64 = ls
287 while le < n { if (b[le] as i64) == FL_CH_RPAREN { break } if (b[le] as i64) == FL_CH_NL { break } le = le + 1 }
288 if fl_add(t, b, s, e, b, ls, le, seed, FL_KIND_MD) >= 0 { added = added + 1 }
289 } } } }
290 } } } }
291 }
292 // next line
293 while p < n { if (b[p] as i64) == FL_CH_NL { break } p = p + 1 }
294 p = p + 1
295 }
296 return added
297}
298
299// ---- emit: rows sorted by seeds hit (popcount of the mask) then mentions, both descending ----
300func fl_popcount(v: i64) -> i64 { var c: i64 = 0; var x: i64 = v; while x > 0 { c = c + (x & 1); x = x >> 1 } return c }
301func fl_puts(fd: i64, s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(fd, s, n); return 0 }
302func fl_putn(fd: i64, v: i64) -> i64 {
303 let b: *u8 = sys_mmap(32)
304 var x: i64 = v
305 if x < 0 { b[0] = FL_CH_MINUS as u8; sys_write(fd, b, 1); x = 0 - x }
306 if x == 0 { b[0] = FL_ASCII_ZERO as u8; sys_write(fd, b, 1); return 0 }
307 var d: i64 = 0
308 var y: i64 = x
309 while y > 0 { d = d + 1; y = y / 10 }
310 var i: i64 = d
311 while i > 0 { i = i - 1; b[i] = ((x % 10) + FL_ASCII_ZERO) as u8; x = x / 10 }
312 sys_write(fd, b, d)
313 return 0
314}
315func fl_kind_name(k: i64) -> *u8 {
316 if k == FL_KIND_WIKI { return "wiki-raw" as *u8 }
317 if k == FL_KIND_GH { return "gh-topic" as *u8 }
318 if k == FL_KIND_MD { return "md-list" as *u8 }
319 if k == FL_KIND_NAMED { return "named" as *u8 }
320 return "unknown" as *u8
321}
322// A DECLARED member of the population: name and url are NUL-terminated; it merges case-insensitively into a rival
323// the public lists already named (so seeds_hit counts the corroboration) or enters as its own row with kind named.
324// An empty name is refused (-1): a member with no name is not a member.
325// FL_DECLARED_TIER lifts a declared member above every lead in fl_order: it exceeds the largest possible seed
326// popcount (FL_MAX_SEEDS bits), so no number of corroborating lists can rank a lead above a member.
327const FL_DECLARED_TIER: i64 = 64
328func fl_named_add(t: *i64, name: *u8, url: *u8, seed: i64) -> i64 {
329 var nl: i64 = 0
330 while name[nl] != (0 as u8) { nl = nl + 1 }
331 if nl < 1 { return 0 - 1 }
332 var ul: i64 = 0
333 while url[ul] != (0 as u8) { ul = ul + 1 }
334 let idx: i64 = fl_add(t, name, 0, nl, url, 0, ul, seed, FL_KIND_NAMED)
335 // a member the public lists already named keeps its seed bits and BECOMES declared: the kind word is the
336 // population flag the page and the ordering read, so a merged member is not left looking like a lead
337 if idx >= 0 { fl_rec_set(t, idx, 3, FL_KIND_NAMED) }
338 return idx
339}
340// a pipe inside a name or link would break the row grammar: it is replaced by a space at emit time
341func fl_puts_nopipe(fd: i64, s: *u8) -> i64 {
342 var n: i64 = 0
343 while s[n] != (0 as u8) { n = n + 1 }
344 let c: *u8 = sys_mmap(n + 1)
345 var i: i64 = 0
346 while i < n { if (s[i] as i64) == FL_CH_PIPE { c[i] = FL_CH_SPACE as u8 } else { c[i] = s[i] } i = i + 1 }
347 sys_write(fd, c, n)
348 return 0
349}
350// order[] receives candidate indices sorted; returns the count. Selection sort is O(n^2) over at most
351// FL_MAX_CAND rows, deterministic, and fast enough for a population this size.
352func fl_order(t: *i64, order: *i64) -> i64 {
353 let n: i64 = t[3]
354 let used: *i64 = sys_mmap(n * 8 + 8) as *i64
355 var k: i64 = 0
356 while k < n {
357 var best: i64 = 0 - 1
358 var bs: i64 = 0 - 1
359 var bm: i64 = 0 - 1
360 var i: i64 = 0
361 while i < n {
362 if used[i] == 0 {
363 // DECLARED members are the population and rank as a tier above every lead: a member with one seed and
364 // one mention must not sit below hundreds of encyclopedia links (measured 2026-09-05 -- 31 declared
365 // communities would have fallen outside the 60 rendered rows). Within a tier: seeds, then mentions.
366 var sc: i64 = fl_popcount(fl_rec(t, i, 1))
367 if fl_rec(t, i, 3) == FL_KIND_NAMED { sc = sc + FL_DECLARED_TIER }
368 let mc: i64 = fl_rec(t, i, 0)
369 var better: i64 = 0
370 if sc > bs { better = 1 }
371 if sc == bs { if mc > bm { better = 1 } }
372 if better == 1 { best = i; bs = sc; bm = mc }
373 }
374 i = i + 1
375 }
376 used[best] = 1
377 order[k] = best
378 k = k + 1
379 }
380 return n
381}
382// rows: rival|<name>|<seeds_hit>|<mentions>|<first_seed_key>|<link>|<kind>
383func fl_emit(t: *i64, fd: i64, seedkeys: *i64, nseeds: i64) -> i64 {
384 let n: i64 = t[3]
385 let order: *i64 = sys_mmap(n * 8 + 8) as *i64
386 fl_order(t, order)
387 var k: i64 = 0
388 while k < n {
389 let i: i64 = order[k]
390 fl_puts(fd, "rival|" as *u8)
391 fl_puts_nopipe(fd, fl_name_at(t, i))
392 fl_puts(fd, "|" as *u8); fl_putn(fd, fl_popcount(fl_rec(t, i, 1)))
393 fl_puts(fd, "|" as *u8); fl_putn(fd, fl_rec(t, i, 0))
394 fl_puts(fd, "|" as *u8)
395 let fs: i64 = fl_rec(t, i, 2)
396 if fs >= 0 { if fs < nseeds { fl_puts_nopipe(fd, seedkeys[fs] as *u8) } }
397 fl_puts(fd, "|" as *u8)
398 fl_puts_nopipe(fd, fl_link_at(t, i))
399 fl_puts(fd, "|" as *u8)
400 fl_puts(fd, fl_kind_name(fl_rec(t, i, 3)))
401 fl_puts(fd, "\n" as *u8)
402 k = k + 1
403 }
404 return n
405}