code wiki / _hdl_build / nx_ledger_data.nx
nx_ledger_data.nx source
↩ module page · 317 lines · 12322 B
1// nx_ledger_data.nx -- LEDGER-AS-DATA (rung AN1): an Analyst case is a .led FILE, not code.
2// New question = new file, NO recompile (the vizsla archetype-matrix lesson). The file carries
3// SOURCE COLUMNS so the strict source-criticism re-grade is data too: every MEASURED leaf must
4// be backed by >=2 DISTINCT incentive classes or it is DEMOTED to DERIVED before the verdict.
5// Grammar (one record per line; '#'/blank = comment; integers only, space-separated):
6// T <tol_ppm> <min_traced_permil> <max_residual_permil> (exactly one)
7// C <class_a> <class_b> (opposed pair; 0..4 rows)
8// S <rel 1-6> <cred 1-6> <party 1-3> <method 1-5> <class> (source; idx = order)
9// N <parent> <value_cents> <kind 0/1/2> (node; idx = order; node 0 = root)
10// B <node> <src> (backing edge)
11// Any other line, bad field, bad grade, or bad reference fails LOUD (parse returns 0) -- a
12// ledger that cannot be read exactly is never half-answered. Composes nx_analyst (verdict math)
13// + nx_source_grade (grades, independence, confidence). LAWS: struct-free, integer-only, flat
14// ifs, <=6 args (st-table for the parse state). license_tier: ORIGINAL
15import "nx_analyst.nx"
16import "nx_source_grade.nx"
17import "nx_syscalls.nx"
18
19const LD_MAX_SRC: i64 = 64
20const LD_MAX_NODE: i64 = 64
21const LD_MAX_EDGE: i64 = 256
22const LD_MAX_OPP: i64 = 4
23
24// st-table slots (base addresses of parallel arrays, then counts + thresholds)
25const LD_REL: i64 = 0 // *i64[LD_MAX_SRC]
26const LD_CRED: i64 = 1
27const LD_PARTY: i64 = 2
28const LD_METHOD: i64 = 3
29const LD_CLASS: i64 = 4
30const LD_PARENT: i64 = 5 // *i64[LD_MAX_NODE]
31const LD_VALUE: i64 = 6
32const LD_KIND: i64 = 7
33const LD_BNODE: i64 = 8 // *i64[LD_MAX_EDGE]
34const LD_BSRC: i64 = 9
35const LD_OPPA: i64 = 10 // *i64[LD_MAX_OPP]
36const LD_OPPB: i64 = 11
37const LD_NS: i64 = 12 // counts
38const LD_NN: i64 = 13
39const LD_NB: i64 = 14
40const LD_NC: i64 = 15
41const LD_TOL: i64 = 16 // thresholds
42const LD_MINTP: i64 = 17
43const LD_MAXRP: i64 = 18
44const LD_NT: i64 = 19 // how many T rows seen (must end at exactly 1)
45
46func ld_alloc(st: *i64) -> i64 {
47 st[LD_REL] = sys_mmap(8*LD_MAX_SRC) as i64
48 st[LD_CRED] = sys_mmap(8*LD_MAX_SRC) as i64
49 st[LD_PARTY] = sys_mmap(8*LD_MAX_SRC) as i64
50 st[LD_METHOD] = sys_mmap(8*LD_MAX_SRC) as i64
51 st[LD_CLASS] = sys_mmap(8*LD_MAX_SRC) as i64
52 st[LD_PARENT] = sys_mmap(8*LD_MAX_NODE) as i64
53 st[LD_VALUE] = sys_mmap(8*LD_MAX_NODE) as i64
54 st[LD_KIND] = sys_mmap(8*LD_MAX_NODE) as i64
55 st[LD_BNODE] = sys_mmap(8*LD_MAX_EDGE) as i64
56 st[LD_BSRC] = sys_mmap(8*LD_MAX_EDGE) as i64
57 st[LD_OPPA] = sys_mmap(8*LD_MAX_OPP) as i64
58 st[LD_OPPB] = sys_mmap(8*LD_MAX_OPP) as i64
59 st[LD_NS]=0; st[LD_NN]=0; st[LD_NB]=0; st[LD_NC]=0; st[LD_NT]=0
60 st[LD_TOL]=0; st[LD_MINTP]=0; st[LD_MAXRP]=0
61 return 0
62}
63
64func ld_line_end(b: *u8, i: i64, n: i64) -> i64 {
65 var e: i64 = i
66 while e < n { if b[e] == (10 as u8) { return e } e = e + 1 }
67 return n
68}
69func ld_skip(b: *u8, i0: i64, e: i64) -> i64 {
70 var i: i64 = i0
71 while i < e { if b[i] != (32 as u8) { return i } i = i + 1 }
72 return e
73}
74
75// parse the decimal run at the first non-space >= j0; out[0]=value out[1]=index-after.
76// returns 1 ok, 0 = no digits (loud at the caller). Flat loop (4-deep-if landmine).
77func ld_int(b: *u8, j0: i64, e: i64, out: *i64) -> i64 {
78 var j: i64 = ld_skip(b, j0, e)
79 var v: i64 = 0
80 var got: i64 = 0
81 var go: i64 = 1
82 while go == 1 {
83 go = 0
84 var c: i64 = 0 - 1
85 if j < e { c = b[j] as i64 }
86 var isd: i64 = 0
87 if c >= 48 { if c <= 57 { isd = 1 } }
88 if isd == 1 { v = v * 10 + (c - 48); got = 1; j = j + 1; go = 1 }
89 }
90 out[0] = v
91 out[1] = j
92 return got
93}
94
95// parse k integers after position i into out[0..k); returns 1 ok / 0 loud
96func ld_ints(b: *u8, i0: i64, e: i64, k: i64, out: *i64) -> i64 {
97 let tmp: *i64 = sys_mmap(8*4) as *i64
98 var i: i64 = i0
99 var f: i64 = 0
100 while f < k {
101 if ld_int(b, i, e, tmp) == 0 { return 0 }
102 out[f] = tmp[0]
103 i = tmp[1]
104 f = f + 1
105 }
106 return 1
107}
108
109// parse one line; returns 1 ok (or skipped), 0 = loud malformed
110func ld_line(b: *u8, i: i64, e: i64, st: *i64) -> i64 {
111 let s: i64 = ld_skip(b, i, e)
112 if s >= e { return 1 } // blank
113 let c: i64 = b[s] as i64
114 if c == 35 { return 1 } // '#'
115 let v: *i64 = sys_mmap(8*8) as *i64
116 if c == 84 { // 'T'
117 if ld_ints(b, s+1, e, 3, v) == 0 { return 0 }
118 st[LD_TOL]=v[0]; st[LD_MINTP]=v[1]; st[LD_MAXRP]=v[2]
119 st[LD_NT] = st[LD_NT] + 1
120 return 1
121 }
122 if c == 67 { // 'C'
123 if ld_ints(b, s+1, e, 2, v) == 0 { return 0 }
124 if st[LD_NC] >= LD_MAX_OPP { return 0 }
125 let oa: *i64 = st[LD_OPPA] as *i64
126 let ob: *i64 = st[LD_OPPB] as *i64
127 oa[st[LD_NC]] = v[0]; ob[st[LD_NC]] = v[1]
128 st[LD_NC] = st[LD_NC] + 1
129 return 1
130 }
131 if c == 83 { // 'S'
132 if ld_ints(b, s+1, e, 5, v) == 0 { return 0 }
133 if st[LD_NS] >= LD_MAX_SRC { return 0 }
134 if sg_valid(v[0], v[1], v[2], v[3]) == 0 { return 0 } // bad grades fail LOUD at parse
135 let a0: *i64 = st[LD_REL] as *i64
136 let a1: *i64 = st[LD_CRED] as *i64
137 let a2: *i64 = st[LD_PARTY] as *i64
138 let a3: *i64 = st[LD_METHOD] as *i64
139 let a4: *i64 = st[LD_CLASS] as *i64
140 a0[st[LD_NS]]=v[0]; a1[st[LD_NS]]=v[1]; a2[st[LD_NS]]=v[2]; a3[st[LD_NS]]=v[3]; a4[st[LD_NS]]=v[4]
141 st[LD_NS] = st[LD_NS] + 1
142 return 1
143 }
144 if c == 78 { // 'N'
145 if ld_ints(b, s+1, e, 3, v) == 0 { return 0 }
146 if st[LD_NN] >= LD_MAX_NODE { return 0 }
147 if v[2] > 2 { return 0 } // kind 0/1/2 only
148 let p: *i64 = st[LD_PARENT] as *i64
149 let vl: *i64 = st[LD_VALUE] as *i64
150 let kd: *i64 = st[LD_KIND] as *i64
151 var par: i64 = v[0]
152 if st[LD_NN] == 0 { par = 0 - 1 } // node 0 = root by convention
153 if st[LD_NN] > 0 { if par >= st[LD_NN] { return 0 } } // parent must precede child
154 p[st[LD_NN]]=par; vl[st[LD_NN]]=v[1]; kd[st[LD_NN]]=v[2]
155 st[LD_NN] = st[LD_NN] + 1
156 return 1
157 }
158 if c == 66 { // 'B'
159 if ld_ints(b, s+1, e, 2, v) == 0 { return 0 }
160 if st[LD_NB] >= LD_MAX_EDGE { return 0 }
161 if v[0] >= st[LD_NN] { return 0 } // node must exist already
162 if v[1] >= st[LD_NS] { return 0 } // source must exist already
163 let bn: *i64 = st[LD_BNODE] as *i64
164 let bs: *i64 = st[LD_BSRC] as *i64
165 bn[st[LD_NB]]=v[0]; bs[st[LD_NB]]=v[1]
166 st[LD_NB] = st[LD_NB] + 1
167 return 1
168 }
169 return 0 // unknown record letter = LOUD
170}
171
172// parse a whole .led buffer. 1 = ok, 0 = loud-fail (malformed anywhere = no answer at all)
173func ld_parse(b: *u8, n: i64, st: *i64) -> i64 {
174 ld_alloc(st)
175 var i: i64 = 0
176 while i < n {
177 let e: i64 = ld_line_end(b, i, n)
178 if ld_line(b, i, e, st) == 0 { return 0 }
179 i = e + 1
180 }
181 if st[LD_NT] != 1 { return 0 } // exactly one threshold row
182 if st[LD_NN] < 2 { return 0 } // a root and at least one child
183 return 1
184}
185
186func ld_parse_file(path: *u8, st: *i64) -> i64 {
187 let lenp: *i64 = sys_mmap(16) as *i64
188 let b: *u8 = sys_read_file(path, lenp)
189 if lenp[0] <= 0 { return 0 }
190 return ld_parse(b, lenp[0], st)
191}
192
193// ---- strict evaluation (the independence rule applied to the parsed case) ----
194
195// best composed score among `node`'s backers of class c (-1 if none). Flat scan.
196func ld_class_best(st: *i64, node: i64, c: i64) -> i64 {
197 let bn: *i64 = st[LD_BNODE] as *i64
198 let bs: *i64 = st[LD_BSRC] as *i64
199 let cl: *i64 = st[LD_CLASS] as *i64
200 let a0: *i64 = st[LD_REL] as *i64
201 let a1: *i64 = st[LD_CRED] as *i64
202 let a2: *i64 = st[LD_PARTY] as *i64
203 let a3: *i64 = st[LD_METHOD] as *i64
204 var cb: i64 = 0 - 1
205 var k: i64 = 0
206 while k < st[LD_NB] {
207 var hit: i64 = 0
208 if bn[k] == node { if cl[bs[k]] == c { hit = 1 } }
209 if hit == 1 {
210 let sc: i64 = sg_score_permil(a0[bs[k]], a1[bs[k]], a2[bs[k]], a3[bs[k]])
211 if sc > cb { cb = sc }
212 }
213 k = k + 1
214 }
215 return cb
216}
217
218// distinct backing classes of `node`; also reports via out: out[0]=has_opposed out[1]=leg
219// (leg = min of the two best per-class scores -- the row stands on its weaker leg)
220func ld_node_support(st: *i64, node: i64, out: *i64) -> i64 {
221 let bn: *i64 = st[LD_BNODE] as *i64
222 let bs: *i64 = st[LD_BSRC] as *i64
223 let cl: *i64 = st[LD_CLASS] as *i64
224 let cls: *i64 = sys_mmap(8*(LD_MAX_EDGE+2)) as *i64
225 var nc: i64 = 0
226 var best1: i64 = 0 - 1 // best per-class score, top two
227 var best2: i64 = 0 - 1
228 var i: i64 = 0
229 while i < st[LD_NB] {
230 var hit: i64 = 0
231 var c: i64 = 0
232 if bn[i] == node { hit = 1; c = cl[bs[i]] }
233 var fresh: i64 = 0
234 if hit == 1 {
235 var seen: i64 = 0
236 var j: i64 = 0
237 while j < nc { if cls[j] == c { seen = 1 } j = j + 1 }
238 if seen == 0 { fresh = 1 }
239 }
240 if fresh == 1 {
241 cls[nc] = c
242 nc = nc + 1
243 let cb: i64 = ld_class_best(st, node, c)
244 if cb > best1 { best2 = best1; best1 = cb } else { if cb > best2 { best2 = cb } }
245 }
246 i = i + 1
247 }
248 // opposed pair present among the distinct classes?
249 let oa: *i64 = st[LD_OPPA] as *i64
250 let ob: *i64 = st[LD_OPPB] as *i64
251 var op: i64 = 0
252 var q: i64 = 0
253 while q < st[LD_NC] {
254 if sg_has_opposed(cls, nc, oa[q], ob[q]) == 1 { op = 1 }
255 q = q + 1
256 }
257 out[0] = op
258 out[1] = best2 // the weaker of the two best legs (-1 if <2 classes)
259 return nc
260}
261
262// build the STRICT kind array: a MEASURED leaf keeps MEASURED only with >=2 distinct classes.
263// returns the number of demotions. skind must hold LD_MAX_NODE slots.
264func ld_strict_kinds(st: *i64, skind: *i64) -> i64 {
265 let kd: *i64 = st[LD_KIND] as *i64
266 let p: *i64 = st[LD_PARENT] as *i64
267 let sup: *i64 = sys_mmap(8*4) as *i64
268 var dem: i64 = 0
269 var i: i64 = 0
270 while i < st[LD_NN] {
271 skind[i] = kd[i]
272 if kd[i] == AN_MEASURED {
273 if an_is_leaf(p, st[LD_NN], i) == 1 {
274 let nc: i64 = ld_node_support(st, i, sup)
275 if nc < 2 { skind[i] = AN_DERIVED; dem = dem + 1 }
276 }
277 }
278 i = i + 1
279 }
280 return dem
281}
282
283// full strict evaluation. out: 0=traced 1=tp 2=rp 3=verdict 4=worst_leg 5=confidence 6=demotions
284// 7=closure_fails. returns 1 ok / 0 = parse-level refusal already happened upstream.
285func ld_eval(st: *i64, out: *i64) -> i64 {
286 let skind: *i64 = sys_mmap(8*LD_MAX_NODE) as *i64
287 let dem: i64 = ld_strict_kinds(st, skind)
288 let p: *i64 = st[LD_PARENT] as *i64
289 let vl: *i64 = st[LD_VALUE] as *i64
290 let nn: i64 = st[LD_NN]
291 let cf: i64 = an_closure_fails(p, vl, nn, st[LD_TOL])
292 let na: i64 = an_narrative_admitted(skind, vl, nn)
293 let tc: i64 = an_traced_cents(p, vl, skind, nn)
294 let tp: i64 = an_traced_permil(vl[0], tc)
295 let rp: i64 = an_residual_permil(vl[0], tc)
296 let vd: i64 = an_verdict(cf, na, tp, rp, st[LD_MINTP], st[LD_MAXRP])
297 // confidence: weakest admitted leg across surviving measured leaves
298 let sup: *i64 = sys_mmap(8*4) as *i64
299 var worst: i64 = 1000
300 var any: i64 = 0
301 var i: i64 = 1
302 while i < nn {
303 if skind[i] == AN_MEASURED {
304 if an_is_leaf(p, nn, i) == 1 {
305 ld_node_support(st, i, sup)
306 any = 1
307 if sup[1] < worst { worst = sup[1] }
308 }
309 }
310 i = i + 1
311 }
312 var conf: i64 = SG_CONF_LOW
313 if any == 1 { conf = sg_confidence(SG_CORROB, worst) }
314 if any == 0 { worst = 0 }
315 out[0]=tc; out[1]=tp; out[2]=rp; out[3]=vd; out[4]=worst; out[5]=conf; out[6]=dem; out[7]=cf
316 return 1
317}