nx_twincard_lib.nx source
↩ module page · 644 lines · 28843 B
1// nx_twincard_lib.nx -- THE TWIN CARD: one declared person or synth, every value on a declared axis with its SIDE, unit, method
2// and source (aesthetictwin AT23, symbol tc_card_check, 2026-09-16). Operator: "a system that is more comprehensive than the
3// illusion cards", then "minute variation between left and right ... down to the smallest unit ... each freckle or smaller is
4// reproducible". An Illusion character card is a PNG carrying about a hundred unitless sliders and no provenance; this card is DATA
5// (knowledge/twincard/<slug>.card) whose axes are declared in knowledge/twincard.axes (identity, body, soft tissue, skin, hair,
6// eyes, ears, lips, teeth, hands, feet, body hair, nails) and, for the face, ARE the canon ids of knowledge/facecanon.conf -- one
7// ruler for the face, never two. A PAIRED axis is recorded per side (L and R are two values, never one), an unpaired axis is C.
8// Individual surface features (freckle, mole, scar, vein, tattoo, birthmark) are one row each in a declared body-surface region
9// with permil chart coordinates, a diameter in mm100 and a CIELAB colour, so a freckle field is reproducible feature by feature.
10// Every axis may name its RULER (the sovereign organ and symbol that measures it); the CLI censuses which rulers exist today, so
11// the measurement investment is a number rather than a wish. Every row carries a METHOD (measured, observed, declared, generated)
12// and a SOURCE, so the card reports what it knows and how; coverage, the method partition and the completed left-right pairs are
13// numbers a page can print.
14// Card rows: card|<slug>|<display>|<class>|<sex>|<licence>|<schema>
15// axis|<section>|<id>|<side L R C>|<value>|<method>|<source>|<when>
16// feature|<kind>|<side>|<region>|<u permil>|<v permil>|<diam mm100>|<L10>|<a10>|<b10>|<method>|<source>
17// ref|<key>|<mirror>|<sha>|<licence>|<note>
18// shot|<refkey>|<date>|<age>|<pose>|<expression>|<light>|<backdrop>|<camera>|<accessories>
19// Schema rows: axis|<section>|<id>|<unit>|<lo>|<hi>|<paired 0 1>|<note> class|<section>|<id>|<word>
20// ruler|<section>|<id or *>|<organ path>|<symbol> region|<word>|<note>
21// Refusals are named: an undeclared axis (a group label is not an axis), a value outside its band, a class word not declared, a
22// method outside the four, a side that contradicts the pairing, a duplicate axis-and-side, a feature of unknown kind or region or
23// out of its chart, a shot naming no ref, a malformed ref or header. A canon band is a hypothesis under test, so a face value is
24// never range-checked against it. Composes nx_refintake_lib for the row helpers (the intake is the card's media leg).
25// LIB, no main. license_tier: ORIGINAL No hw writes (Rule 26).
26import "nx_syscalls.nx"
27import "nx_refintake_lib.nx"
28import "nx_symdecl_lib.nx"
29
30const TC_AXES: *u8 = "knowledge/twincard.axes"
31const TC_BUILDROOT: *u8 = "buildroot/"
32const TC_C_ROWS: i64 = 0
33const TC_C_PRESENT: i64 = 1
34const TC_C_ABSENT: i64 = 2
35const TC_C_UNJUDGED: i64 = 3
36const TC_DIR: *u8 = "knowledge/twincard"
37const TC_CANON: *u8 = "knowledge/facecanon.conf"
38const TC_SCHEMA_V: i64 = 1
39const TC_FACE: *u8 = "face"
40const TC_METHODS: *u8 = " measured observed declared generated "
41const TC_CLASSES: *u8 = " real anime fantasy synth "
42const TC_SEXES: *u8 = " F M X "
43const TC_LICENCES: *u8 = " REVIEW SHIP_OK REFUSED "
44const TC_SIDES: *u8 = " L R C "
45const TC_SIDE_C: *u8 = "C"
46const TC_SIDE_L: *u8 = "L"
47const TC_SIDE_R: *u8 = "R"
48const TC_FEATURE_KINDS: *u8 = " freckle mole scar vein tattoo birthmark "
49const TC_UNIT_CLASS: *u8 = "class"
50const TC_DASHWORD: *u8 = "-"
51const TC_STAR: *u8 = "*"
52const TC_LISTB: i64 = 8192
53const TC_POS_N: i64 = 2
54const TC_PERMIL: i64 = 1000
55const TC_UV_MAX: i64 = 1000
56const TC_DIAM_MAX: i64 = 100000
57const TC_LAB_L_MAX: i64 = 1000
58const TC_LAB_AB: i64 = 500
59// card axis fields
60const TC_F_SECTION: i64 = 1
61const TC_F_ID: i64 = 2
62const TC_F_SIDE: i64 = 3
63const TC_F_VALUE: i64 = 4
64const TC_F_METHOD: i64 = 5
65const TC_F_SOURCE: i64 = 6
66const TC_F_WHEN: i64 = 7
67// card header, ref, shot fields
68const TC_F_CARD_SLUG: i64 = 1
69const TC_F_CARD_DISPLAY: i64 = 2
70const TC_F_CARD_CLASS: i64 = 3
71const TC_F_CARD_SEX: i64 = 4
72const TC_F_CARD_LIC: i64 = 5
73const TC_F_REF_KEY: i64 = 1
74const TC_F_REF_MIRROR: i64 = 2
75const TC_F_REF_SHA: i64 = 3
76const TC_F_REF_LIC: i64 = 4
77const TC_F_SHOT_REF: i64 = 1
78// shot state: four declared class words after the accessories field (presentation, makeup, hair_state, retouch)
79const TC_F_SHOT_STATE0: i64 = 10
80const TC_SHOT_STATE_N: i64 = 4
81const TC_SHOT: *u8 = "shot"
82// card feature fields
83const TC_F_FEAT_KIND: i64 = 1
84const TC_F_FEAT_SIDE: i64 = 2
85const TC_F_FEAT_REGION: i64 = 3
86const TC_F_FEAT_U: i64 = 4
87const TC_F_FEAT_V: i64 = 5
88const TC_F_FEAT_DIAM: i64 = 6
89const TC_F_FEAT_L: i64 = 7
90const TC_F_FEAT_A: i64 = 8
91const TC_F_FEAT_B: i64 = 9
92const TC_F_FEAT_METHOD: i64 = 10
93// schema fields
94const TC_F_SCHEMA_UNIT: i64 = 3
95const TC_F_SCHEMA_LO: i64 = 4
96const TC_F_SCHEMA_HI: i64 = 5
97const TC_F_SCHEMA_PAIRED: i64 = 6
98const TC_F_RULER_ORGAN: i64 = 3
99const TC_F_RULER_SYMBOL: i64 = 4
100// report slots filled by tc_validate
101const TC_R_AXES: i64 = 0
102const TC_R_VALID: i64 = 1
103const TC_R_UNDECLARED: i64 = 2
104const TC_R_RANGE: i64 = 3
105const TC_R_METHOD: i64 = 4
106const TC_R_CLASSWORD: i64 = 5
107const TC_R_DUP: i64 = 6
108const TC_R_MEASURED: i64 = 7
109const TC_R_OBSERVED: i64 = 8
110const TC_R_DECLARED: i64 = 9
111const TC_R_GENERATED: i64 = 10
112const TC_R_HEADER: i64 = 11
113const TC_R_REFS: i64 = 12
114const TC_R_BADREF: i64 = 13
115const TC_R_SHOTS: i64 = 14
116const TC_R_BADSHOT: i64 = 15
117const TC_R_CARDS: i64 = 16
118const TC_R_SIDE: i64 = 17
119const TC_R_FEATURES: i64 = 18
120const TC_R_BADFEATURE: i64 = 19
121const TC_R_N: i64 = 20
122// tc_ruler_for answers
123const TC_RULER_NONE: i64 = 0
124const TC_RULER_EXACT: i64 = 1
125const TC_RULER_WILDCARD: i64 = 2
126
127// the card's generation stamp, the symbol the board contracts (AT23 tc_card_check)
128func tc_card_check() -> i64 { return TC_SCHEMA_V }
129
130// whole-word membership in a space-delimited list
131func tc_word_in(list: *u8, w: *u8) -> i64 {
132 let ww: *u8 = sys_mmap(RI_PATHB)
133 var o: i64 = 0
134 ww[o] = RI_SP as u8; o = o + 1
135 o = ri_cat(ww, o, w)
136 ww[o] = RI_SP as u8; o = o + 1
137 ww[o] = 0 as u8
138 return ri_contains(list, ri_slen(list), ww)
139}
140func tc_atoi_signed(s: *u8) -> i64 {
141 if s[0] == (RI_DASH as u8) { return 0 - ri_atoi((s as i64 + 1) as *u8) }
142 return ri_atoi(s)
143}
144func tc_in_band(v: i64, lo: i64, hi: i64) -> i64 {
145 if v < lo { return 0 }
146 if v > hi { return 0 }
147 return 1
148}
149func tc_permil(num: i64, den: i64) -> i64 {
150 if den <= 0 { return 0 }
151 return num * TC_PERMIL / den
152}
153func tc_card_path(slug: *u8, out: *u8) -> i64 {
154 var o: i64 = ri_cat(out, 0, TC_DIR)
155 out[o] = RI_SLASH as u8; o = o + 1
156 o = ri_cat(out, o, slug)
157 o = ri_cat(out, o, ".card" as *u8)
158 return o
159}
160// the first row starting with prefix; pos[0] = start, pos[1] = end
161func tc_row_find(buf: *u8, n: i64, prefix: *u8, pos: *i64) -> i64 {
162 var i: i64 = 0
163 while i < n {
164 let e: i64 = ri_line_end(buf, n, i)
165 if ri_starts(buf, n, i, prefix) == 1 { pos[0] = i; pos[1] = e; return 1 }
166 i = e + 1
167 }
168 pos[0] = 0
169 pos[1] = 0
170 return 0
171}
172func tc_count_prefix(buf: *u8, n: i64, prefix: *u8) -> i64 {
173 var i: i64 = 0
174 var c: i64 = 0
175 while i < n {
176 let e: i64 = ri_line_end(buf, n, i)
177 if ri_starts(buf, n, i, prefix) == 1 { c = c + 1 }
178 i = e + 1
179 }
180 return c
181}
182// "<kind>|<a>|", "<kind>|<a>|<b>|" and "<kind>|<a>|<b>|<c>|"
183func tc_key1(out: *u8, kind: *u8, a: *u8) -> i64 {
184 var o: i64 = ri_cat(out, 0, kind)
185 out[o] = RI_PIPE as u8; o = o + 1
186 o = ri_cat(out, o, a)
187 out[o] = RI_PIPE as u8; o = o + 1
188 out[o] = 0 as u8
189 return o
190}
191func tc_key2(out: *u8, kind: *u8, a: *u8, b: *u8) -> i64 {
192 var o: i64 = tc_key1(out, kind, a)
193 o = ri_cat(out, o, b)
194 out[o] = RI_PIPE as u8; o = o + 1
195 out[o] = 0 as u8
196 return o
197}
198func tc_key3(out: *u8, kind: *u8, a: *u8, b: *u8, c: *u8) -> i64 {
199 var o: i64 = tc_key2(out, kind, a, b)
200 o = ri_cat(out, o, c)
201 out[o] = RI_PIPE as u8; o = o + 1
202 out[o] = 0 as u8
203 return o
204}
205// is <section>/<id> declared? face ids are the canon rows; every other section is a schema axis row. pos = the declaring row
206func tc_axis_declared(schema: *u8, sn: i64, canon: *u8, kn: i64, section: *u8, id: *u8, pos: *i64) -> i64 {
207 let key: *u8 = sys_mmap(RI_ROWB)
208 if ri_streq(section, TC_FACE) == 1 {
209 tc_key1(key, "canon" as *u8, id)
210 return tc_row_find(canon, kn, key, pos)
211 }
212 tc_key2(key, "axis" as *u8, section, id)
213 return tc_row_find(schema, sn, key, pos)
214}
215// unit, band and pairing of a schema axis (never a face axis); returns 1 when declared
216func tc_unit_band(schema: *u8, sn: i64, section: *u8, id: *u8, unit: *u8, lo: *i64, hi: *i64, paired: *i64) -> i64 {
217 let key: *u8 = sys_mmap(RI_ROWB)
218 let pos: *i64 = sys_mmap(TC_POS_N * RI_I64) as *i64
219 let f: *u8 = sys_mmap(RI_PATHB)
220 tc_key2(key, "axis" as *u8, section, id)
221 if tc_row_find(schema, sn, key, pos) == 0 { unit[0] = 0 as u8; lo[0] = 0; hi[0] = 0; paired[0] = 0; return 0 }
222 ri_field(schema, pos[0], pos[1], TC_F_SCHEMA_UNIT, unit, RI_PATHB)
223 ri_field(schema, pos[0], pos[1], TC_F_SCHEMA_LO, f, RI_PATHB)
224 lo[0] = tc_atoi_signed(f)
225 ri_field(schema, pos[0], pos[1], TC_F_SCHEMA_HI, f, RI_PATHB)
226 hi[0] = tc_atoi_signed(f)
227 ri_field(schema, pos[0], pos[1], TC_F_SCHEMA_PAIRED, f, RI_PATHB)
228 paired[0] = ri_atoi(f)
229 return 1
230}
231func tc_paired(schema: *u8, sn: i64, section: *u8, id: *u8) -> i64 {
232 let unit: *u8 = sys_mmap(RI_PATHB)
233 let lo: *i64 = sys_mmap(RI_I64) as *i64
234 let hi: *i64 = sys_mmap(RI_I64) as *i64
235 let paired: *i64 = sys_mmap(RI_I64) as *i64
236 if tc_unit_band(schema, sn, section, id, unit, lo, hi, paired) == 0 { return 0 }
237 return paired[0]
238}
239// is <word> a declared word of the class axis <section>/<id>? whole-line match on class|section|id|word
240func tc_class_ok(schema: *u8, sn: i64, section: *u8, id: *u8, word: *u8) -> i64 {
241 let line: *u8 = sys_mmap(RI_ROWB)
242 var o: i64 = tc_key2(line, "class" as *u8, section, id)
243 o = ri_cat(line, o, word)
244 return ri_has_line(schema, sn, line)
245}
246func tc_region_ok(schema: *u8, sn: i64, word: *u8) -> i64 {
247 let key: *u8 = sys_mmap(RI_ROWB)
248 let pos: *i64 = sys_mmap(TC_POS_N * RI_I64) as *i64
249 tc_key1(key, "region" as *u8, word)
250 return tc_row_find(schema, sn, key, pos)
251}
252// the ruler declared for an axis: an exact ruler row, else the section's * row; copies organ and symbol
253func tc_ruler_for(schema: *u8, sn: i64, section: *u8, id: *u8, organ: *u8, symbol: *u8) -> i64 {
254 let key: *u8 = sys_mmap(RI_ROWB)
255 let pos: *i64 = sys_mmap(TC_POS_N * RI_I64) as *i64
256 tc_key2(key, "ruler" as *u8, section, id)
257 var how: i64 = TC_RULER_EXACT
258 if tc_row_find(schema, sn, key, pos) == 0 {
259 how = TC_RULER_WILDCARD
260 tc_key2(key, "ruler" as *u8, section, TC_STAR)
261 if tc_row_find(schema, sn, key, pos) == 0 { organ[0] = 0 as u8; symbol[0] = 0 as u8; return TC_RULER_NONE }
262 }
263 ri_field(schema, pos[0], pos[1], TC_F_RULER_ORGAN, organ, RI_PATHB)
264 ri_field(schema, pos[0], pos[1], TC_F_RULER_SYMBOL, symbol, RI_PATHB)
265 return how
266}
267func tc_ref_exists(card: *u8, cn: i64, key: *u8) -> i64 {
268 let k: *u8 = sys_mmap(RI_ROWB)
269 let pos: *i64 = sys_mmap(TC_POS_N * RI_I64) as *i64
270 tc_key1(k, "ref" as *u8, key)
271 return tc_row_find(card, cn, k, pos)
272}
273// an earlier axis row (before byte <upto>) with the same section, id and side?
274func tc_dup_before(card: *u8, upto: i64, section: *u8, id: *u8, side: *u8) -> i64 {
275 let key: *u8 = sys_mmap(RI_ROWB)
276 tc_key3(key, "axis" as *u8, section, id, side)
277 var i: i64 = 0
278 while i < upto {
279 let e: i64 = ri_line_end(card, upto, i)
280 if ri_starts(card, upto, i, key) == 1 { return 1 }
281 i = e + 1
282 }
283 return 0
284}
285// an earlier axis row with the same section and id on ANY side (face coverage counts ids)
286func tc_dup_id_before(card: *u8, upto: i64, section: *u8, id: *u8) -> i64 {
287 let key: *u8 = sys_mmap(RI_ROWB)
288 tc_key2(key, "axis" as *u8, section, id)
289 var i: i64 = 0
290 while i < upto {
291 let e: i64 = ri_line_end(card, upto, i)
292 if ri_starts(card, upto, i, key) == 1 { return 1 }
293 i = e + 1
294 }
295 return 0
296}
297// the k-th shot-state axis name, in the order the shot row carries them
298func tc_shot_axis(k: i64) -> *u8 {
299 if k == 0 { return "presentation" as *u8 }
300 if k == 1 { return "makeup" as *u8 }
301 if k == 2 { return "hair_state" as *u8 }
302 return "retouch" as *u8
303}
304// RULER COVERAGE: every declared axis (schema sections and the canon face ids) must have a ruler row, exact or wildcard.
305// out[0] axes, out[1] ruled, out[2] unruled; with announce=1 every unruled axis is printed as a worklist row
306func tc_ruler_coverage(schema: *u8, sn: i64, canon: *u8, kn: i64, announce: i64, out: *i64) -> i64 {
307 let sect: *u8 = sys_mmap(RI_PATHB)
308 let id: *u8 = sys_mmap(RI_PATHB)
309 let organ: *u8 = sys_mmap(RI_PATHB)
310 let symbol: *u8 = sys_mmap(RI_PATHB)
311 out[0] = 0
312 out[1] = 0
313 out[2] = 0
314 var i: i64 = 0
315 while i < sn {
316 let e: i64 = ri_line_end(schema, sn, i)
317 if ri_starts(schema, sn, i, "axis|" as *u8) == 1 {
318 ri_field(schema, i, e, TC_F_SECTION, sect, RI_PATHB)
319 ri_field(schema, i, e, TC_F_ID, id, RI_PATHB)
320 out[0] = out[0] + 1
321 if tc_ruler_for(schema, sn, sect, id, organ, symbol) == TC_RULER_NONE {
322 out[2] = out[2] + 1
323 if announce == 1 { ri_puts("TWINCARD unruled section=" as *u8); ri_puts(sect); ri_puts(" id=" as *u8); ri_puts(id); ri_puts("\n" as *u8) }
324 } else { out[1] = out[1] + 1 }
325 }
326 i = e + 1
327 }
328 var j: i64 = 0
329 while j < kn {
330 let e2: i64 = ri_line_end(canon, kn, j)
331 if ri_starts(canon, kn, j, "canon|" as *u8) == 1 {
332 ri_field(canon, j, e2, 1, id, RI_PATHB)
333 out[0] = out[0] + 1
334 if tc_ruler_for(schema, sn, TC_FACE, id, organ, symbol) == TC_RULER_NONE {
335 out[2] = out[2] + 1
336 if announce == 1 { ri_puts("TWINCARD unruled section=face id=" as *u8); ri_puts(id); ri_puts("\n" as *u8) }
337 } else { out[1] = out[1] + 1 }
338 }
339 j = e2 + 1
340 }
341 return out[2]
342}
343// the first offender is remembered as <section>/<id>; later ones only count
344func tc_bad(bad: *u8, section: *u8, id: *u8) -> i64 {
345 if bad[0] != (0 as u8) { return 0 }
346 var o: i64 = ri_cat(bad, 0, section)
347 bad[o] = RI_SLASH as u8; o = o + 1
348 o = ri_cat(bad, o, id)
349 return o
350}
351// does the side agree with the pairing? face rows may carry any side; a paired axis needs L or R; an unpaired axis needs C
352func tc_side_ok(section: *u8, paired: i64, side: *u8) -> i64 {
353 if tc_word_in(TC_SIDES, side) == 0 { return 0 }
354 if ri_streq(section, TC_FACE) == 1 { return 1 }
355 if paired == 1 {
356 if ri_streq(side, TC_SIDE_C) == 1 { return 0 }
357 return 1
358 }
359 if ri_streq(side, TC_SIDE_C) == 1 { return 1 }
360 return 0
361}
362// one feature row: kind, side, region, chart coordinates, diameter, colour, method
363func tc_feature_ok(card: *u8, i: i64, e: i64, schema: *u8, sn: i64) -> i64 {
364 let f: *u8 = sys_mmap(RI_ROWB)
365 var ok: i64 = 1
366 ri_field(card, i, e, TC_F_FEAT_KIND, f, RI_ROWB)
367 if tc_word_in(TC_FEATURE_KINDS, f) == 0 { ok = 0 }
368 ri_field(card, i, e, TC_F_FEAT_SIDE, f, RI_ROWB)
369 if tc_word_in(TC_SIDES, f) == 0 { ok = 0 }
370 ri_field(card, i, e, TC_F_FEAT_REGION, f, RI_ROWB)
371 if tc_region_ok(schema, sn, f) == 0 { ok = 0 }
372 ri_field(card, i, e, TC_F_FEAT_U, f, RI_ROWB)
373 if tc_in_band(tc_atoi_signed(f), 0, TC_UV_MAX) == 0 { ok = 0 }
374 ri_field(card, i, e, TC_F_FEAT_V, f, RI_ROWB)
375 if tc_in_band(tc_atoi_signed(f), 0, TC_UV_MAX) == 0 { ok = 0 }
376 ri_field(card, i, e, TC_F_FEAT_DIAM, f, RI_ROWB)
377 if tc_in_band(tc_atoi_signed(f), 0, TC_DIAM_MAX) == 0 { ok = 0 }
378 ri_field(card, i, e, TC_F_FEAT_L, f, RI_ROWB)
379 if tc_in_band(tc_atoi_signed(f), 0, TC_LAB_L_MAX) == 0 { ok = 0 }
380 ri_field(card, i, e, TC_F_FEAT_A, f, RI_ROWB)
381 if tc_in_band(tc_atoi_signed(f), 0 - TC_LAB_AB, TC_LAB_AB) == 0 { ok = 0 }
382 ri_field(card, i, e, TC_F_FEAT_B, f, RI_ROWB)
383 if tc_in_band(tc_atoi_signed(f), 0 - TC_LAB_AB, TC_LAB_AB) == 0 { ok = 0 }
384 ri_field(card, i, e, TC_F_FEAT_METHOD, f, RI_ROWB)
385 if tc_word_in(TC_METHODS, f) == 0 { ok = 0 }
386 return ok
387}
388// VALIDATE a card against the schema and the canon. Fills rep[TC_R_*], names the first offender in bad, returns the defect count.
389func tc_validate(card: *u8, cn: i64, schema: *u8, sn: i64, canon: *u8, kn: i64, rep: *i64, bad: *u8) -> i64 {
390 var k: i64 = 0
391 while k < TC_R_N { rep[k] = 0; k = k + 1 }
392 bad[0] = 0 as u8
393 let sect: *u8 = sys_mmap(RI_PATHB)
394 let id: *u8 = sys_mmap(RI_PATHB)
395 let side: *u8 = sys_mmap(RI_PATHB)
396 let val: *u8 = sys_mmap(RI_ROWB)
397 let meth: *u8 = sys_mmap(RI_PATHB)
398 let unit: *u8 = sys_mmap(RI_PATHB)
399 let pos: *i64 = sys_mmap(TC_POS_N * RI_I64) as *i64
400 let lo: *i64 = sys_mmap(RI_I64) as *i64
401 let hi: *i64 = sys_mmap(RI_I64) as *i64
402 let paired: *i64 = sys_mmap(RI_I64) as *i64
403 var headers_ok: i64 = 0
404 var i: i64 = 0
405 while i < cn {
406 let e: i64 = ri_line_end(card, cn, i)
407 if ri_starts(card, cn, i, "card|" as *u8) == 1 {
408 rep[TC_R_CARDS] = rep[TC_R_CARDS] + 1
409 var okh: i64 = 1
410 ri_field(card, i, e, TC_F_CARD_CLASS, val, RI_ROWB)
411 if tc_word_in(TC_CLASSES, val) == 0 { okh = 0 }
412 ri_field(card, i, e, TC_F_CARD_SEX, val, RI_ROWB)
413 if tc_word_in(TC_SEXES, val) == 0 { okh = 0 }
414 ri_field(card, i, e, TC_F_CARD_LIC, val, RI_ROWB)
415 if tc_word_in(TC_LICENCES, val) == 0 { okh = 0 }
416 if okh == 1 { headers_ok = headers_ok + 1 } else { tc_bad(bad, "card" as *u8, "header" as *u8) }
417 }
418 if ri_starts(card, cn, i, "axis|" as *u8) == 1 {
419 rep[TC_R_AXES] = rep[TC_R_AXES] + 1
420 ri_field(card, i, e, TC_F_SECTION, sect, RI_PATHB)
421 ri_field(card, i, e, TC_F_ID, id, RI_PATHB)
422 ri_field(card, i, e, TC_F_SIDE, side, RI_PATHB)
423 ri_field(card, i, e, TC_F_VALUE, val, RI_ROWB)
424 ri_field(card, i, e, TC_F_METHOD, meth, RI_PATHB)
425 var ok: i64 = 1
426 if tc_axis_declared(schema, sn, canon, kn, sect, id, pos) == 0 {
427 ok = 0
428 rep[TC_R_UNDECLARED] = rep[TC_R_UNDECLARED] + 1
429 tc_bad(bad, sect, id)
430 } else {
431 if tc_dup_before(card, i, sect, id, side) == 1 { ok = 0; rep[TC_R_DUP] = rep[TC_R_DUP] + 1; tc_bad(bad, sect, id) }
432 if tc_word_in(TC_METHODS, meth) == 0 { ok = 0; rep[TC_R_METHOD] = rep[TC_R_METHOD] + 1; tc_bad(bad, sect, id) }
433 paired[0] = 0
434 if ri_streq(sect, TC_FACE) == 0 {
435 tc_unit_band(schema, sn, sect, id, unit, lo, hi, paired)
436 if ri_streq(unit, TC_UNIT_CLASS) == 1 {
437 if tc_class_ok(schema, sn, sect, id, val) == 0 { ok = 0; rep[TC_R_CLASSWORD] = rep[TC_R_CLASSWORD] + 1; tc_bad(bad, sect, id) }
438 } else {
439 var banded: i64 = 1
440 if lo[0] == 0 { if hi[0] == 0 { banded = 0 } }
441 if banded == 1 {
442 if tc_in_band(tc_atoi_signed(val), lo[0], hi[0]) == 0 { ok = 0; rep[TC_R_RANGE] = rep[TC_R_RANGE] + 1; tc_bad(bad, sect, id) }
443 }
444 }
445 }
446 if tc_side_ok(sect, paired[0], side) == 0 { ok = 0; rep[TC_R_SIDE] = rep[TC_R_SIDE] + 1; tc_bad(bad, sect, id) }
447 }
448 if ok == 1 {
449 rep[TC_R_VALID] = rep[TC_R_VALID] + 1
450 if ri_streq(meth, "measured" as *u8) == 1 { rep[TC_R_MEASURED] = rep[TC_R_MEASURED] + 1 }
451 if ri_streq(meth, "observed" as *u8) == 1 { rep[TC_R_OBSERVED] = rep[TC_R_OBSERVED] + 1 }
452 if ri_streq(meth, "declared" as *u8) == 1 { rep[TC_R_DECLARED] = rep[TC_R_DECLARED] + 1 }
453 if ri_streq(meth, "generated" as *u8) == 1 { rep[TC_R_GENERATED] = rep[TC_R_GENERATED] + 1 }
454 }
455 }
456 if ri_starts(card, cn, i, "feature|" as *u8) == 1 {
457 rep[TC_R_FEATURES] = rep[TC_R_FEATURES] + 1
458 if tc_feature_ok(card, i, e, schema, sn) == 0 {
459 rep[TC_R_BADFEATURE] = rep[TC_R_BADFEATURE] + 1
460 ri_field(card, i, e, TC_F_FEAT_KIND, id, RI_PATHB)
461 tc_bad(bad, "feature" as *u8, id)
462 }
463 }
464 if ri_starts(card, cn, i, "ref|" as *u8) == 1 {
465 rep[TC_R_REFS] = rep[TC_R_REFS] + 1
466 var okr: i64 = 1
467 ri_field(card, i, e, TC_F_REF_SHA, val, RI_ROWB)
468 if ri_streq(val, TC_DASHWORD) == 0 { if ri_slen(val) != RI_SHA_HEX { okr = 0 } }
469 ri_field(card, i, e, TC_F_REF_LIC, val, RI_ROWB)
470 if tc_word_in(TC_LICENCES, val) == 0 { okr = 0 }
471 if okr == 0 {
472 rep[TC_R_BADREF] = rep[TC_R_BADREF] + 1
473 ri_field(card, i, e, TC_F_REF_KEY, id, RI_PATHB)
474 tc_bad(bad, "ref" as *u8, id)
475 }
476 }
477 if ri_starts(card, cn, i, "shot|" as *u8) == 1 {
478 rep[TC_R_SHOTS] = rep[TC_R_SHOTS] + 1
479 ri_field(card, i, e, TC_F_SHOT_REF, id, RI_PATHB)
480 var oks: i64 = 1
481 if tc_ref_exists(card, cn, id) == 0 { oks = 0 }
482 // the presentation state of the shot (operator 2026-09-16: the natural nude-art state AND the glamorised state
483 // are both wanted, so every shot says which it is): four declared class words, refused by name when undeclared
484 var sx: i64 = 0
485 while sx < TC_SHOT_STATE_N {
486 ri_field(card, i, e, TC_F_SHOT_STATE0 + sx, val, RI_ROWB)
487 if tc_class_ok(schema, sn, TC_SHOT, tc_shot_axis(sx), val) == 0 { oks = 0 }
488 sx = sx + 1
489 }
490 if oks == 0 { rep[TC_R_BADSHOT] = rep[TC_R_BADSHOT] + 1; tc_bad(bad, "shot" as *u8, id) }
491 }
492 i = e + 1
493 }
494 var hdr: i64 = 0
495 if rep[TC_R_CARDS] == 1 { if headers_ok == 1 { hdr = 1 } }
496 rep[TC_R_HEADER] = hdr
497 var defects: i64 = rep[TC_R_UNDECLARED] + rep[TC_R_RANGE] + rep[TC_R_METHOD] + rep[TC_R_CLASSWORD] + rep[TC_R_DUP] + rep[TC_R_SIDE]
498 defects = defects + rep[TC_R_BADREF] + rep[TC_R_BADSHOT] + rep[TC_R_BADFEATURE]
499 if hdr == 0 { defects = defects + 1 }
500 return defects
501}
502// the number of values a section declares: a paired axis counts twice (L and R); face = the canon rows
503func tc_declared_count(schema: *u8, sn: i64, canon: *u8, kn: i64, section: *u8) -> i64 {
504 let key: *u8 = sys_mmap(RI_ROWB)
505 let f: *u8 = sys_mmap(RI_PATHB)
506 if ri_streq(section, TC_FACE) == 1 { return tc_count_prefix(canon, kn, "canon|" as *u8) }
507 tc_key1(key, "axis" as *u8, section)
508 var i: i64 = 0
509 var c: i64 = 0
510 while i < sn {
511 let e: i64 = ri_line_end(schema, sn, i)
512 if ri_starts(schema, sn, i, key) == 1 {
513 ri_field(schema, i, e, TC_F_SCHEMA_PAIRED, f, RI_PATHB)
514 c = c + 1
515 if ri_atoi(f) == 1 { c = c + 1 }
516 }
517 i = e + 1
518 }
519 return c
520}
521// the number of DECLARED values the card fills in a section: distinct (id, side) off the face, distinct ids on the face
522func tc_filled_count(card: *u8, cn: i64, schema: *u8, sn: i64, canon: *u8, kn: i64, section: *u8) -> i64 {
523 let key: *u8 = sys_mmap(RI_ROWB)
524 let id: *u8 = sys_mmap(RI_PATHB)
525 let side: *u8 = sys_mmap(RI_PATHB)
526 let pos: *i64 = sys_mmap(TC_POS_N * RI_I64) as *i64
527 let isface: i64 = ri_streq(section, TC_FACE)
528 tc_key1(key, "axis" as *u8, section)
529 var i: i64 = 0
530 var c: i64 = 0
531 while i < cn {
532 let e: i64 = ri_line_end(card, cn, i)
533 if ri_starts(card, cn, i, key) == 1 {
534 ri_field(card, i, e, TC_F_ID, id, RI_PATHB)
535 ri_field(card, i, e, TC_F_SIDE, side, RI_PATHB)
536 if tc_axis_declared(schema, sn, canon, kn, section, id, pos) == 1 {
537 var fresh: i64 = 0
538 if isface == 1 { if tc_dup_id_before(card, i, section, id) == 0 { fresh = 1 } }
539 else { if tc_dup_before(card, i, section, id, side) == 0 { fresh = 1 } }
540 c = c + fresh
541 }
542 }
543 i = e + 1
544 }
545 return c
546}
547// paired axes (off the face) that carry BOTH a left and a right row: the asymmetry the operator asked for is recordable only there
548func tc_pairs_complete(card: *u8, cn: i64, schema: *u8, sn: i64, pairs: *i64) -> i64 {
549 let sect: *u8 = sys_mmap(RI_PATHB)
550 let id: *u8 = sys_mmap(RI_PATHB)
551 let f: *u8 = sys_mmap(RI_PATHB)
552 let kl: *u8 = sys_mmap(RI_ROWB)
553 let kr: *u8 = sys_mmap(RI_ROWB)
554 let pos: *i64 = sys_mmap(TC_POS_N * RI_I64) as *i64
555 pairs[0] = 0
556 var i: i64 = 0
557 var done: i64 = 0
558 while i < sn {
559 let e: i64 = ri_line_end(schema, sn, i)
560 if ri_starts(schema, sn, i, "axis|" as *u8) == 1 {
561 ri_field(schema, i, e, TC_F_SCHEMA_PAIRED, f, RI_PATHB)
562 if ri_atoi(f) == 1 {
563 pairs[0] = pairs[0] + 1
564 ri_field(schema, i, e, TC_F_SECTION, sect, RI_PATHB)
565 ri_field(schema, i, e, TC_F_ID, id, RI_PATHB)
566 tc_key3(kl, "axis" as *u8, sect, id, TC_SIDE_L)
567 tc_key3(kr, "axis" as *u8, sect, id, TC_SIDE_R)
568 if tc_row_find(card, cn, kl, pos) == 1 { if tc_row_find(card, cn, kr, pos) == 1 { done = done + 1 } }
569 }
570 }
571 i = e + 1
572 }
573 return done
574}
575// RULER CENSUS through the estate's ONE symbol ruler (nx_symdecl_lib.sd_present, never a second substring ruler): out[0] ruler rows,
576// out[1] present (declared in the named organ), out[2] absent (the organ read and the symbol not declared), out[3] unjudged (no organ
577// file at the path or under buildroot/, or the ruler abstained). An organ path is tried as given, then under buildroot/, because the
578// serving root and the build tree are two CWDs for the same relative spelling.
579func tc_ruler_census(schema: *u8, sn: i64, out: *i64) -> i64 {
580 let organ: *u8 = sys_mmap(RI_PATHB)
581 let alt: *u8 = sys_mmap(RI_PATHB)
582 let symbol: *u8 = sys_mmap(RI_PATHB)
583 let rule: *i64 = sys_mmap(RI_I64) as *i64
584 out[TC_C_ROWS] = 0
585 out[TC_C_PRESENT] = 0
586 out[TC_C_ABSENT] = 0
587 out[TC_C_UNJUDGED] = 0
588 var i: i64 = 0
589 while i < sn {
590 let e: i64 = ri_line_end(schema, sn, i)
591 if ri_starts(schema, sn, i, "ruler|" as *u8) == 1 {
592 out[TC_C_ROWS] = out[TC_C_ROWS] + 1
593 ri_field(schema, i, e, TC_F_RULER_ORGAN, organ, RI_PATHB)
594 ri_field(schema, i, e, TC_F_RULER_SYMBOL, symbol, RI_PATHB)
595 var r: i64 = sd_present(organ, symbol, rule)
596 if r < 0 {
597 var o: i64 = ri_cat(alt, 0, TC_BUILDROOT)
598 o = ri_cat(alt, o, organ)
599 r = sd_present(alt, symbol, rule)
600 }
601 if r == 1 { out[TC_C_PRESENT] = out[TC_C_PRESENT] + 1 }
602 else { if r == 0 { out[TC_C_ABSENT] = out[TC_C_ABSENT] + 1 } else { out[TC_C_UNJUDGED] = out[TC_C_UNJUDGED] + 1 } }
603 }
604 i = e + 1
605 }
606 return out[TC_C_ROWS]
607}
608// NUL-separated word lists (the section list)
609func tc_list_has(list: *u8, count: i64, w: *u8) -> i64 {
610 var k: i64 = 0
611 var p: i64 = 0
612 while k < count {
613 if ri_streq((list as i64 + p) as *u8, w) == 1 { return 1 }
614 p = p + ri_slen((list as i64 + p) as *u8) + 1
615 k = k + 1
616 }
617 return 0
618}
619func tc_list_at(list: *u8, k: i64) -> *u8 {
620 var j: i64 = 0
621 var p: i64 = 0
622 while j < k { p = p + ri_slen((list as i64 + p) as *u8) + 1; j = j + 1 }
623 return (list as i64 + p) as *u8
624}
625// the distinct sections the schema declares, in first-seen order, NUL-separated in out; returns the count. face is NOT here: it
626// lives in the canon, and the caller appends it
627func tc_sections(schema: *u8, sn: i64, out: *u8, cap: i64) -> i64 {
628 let sect: *u8 = sys_mmap(RI_PATHB)
629 var i: i64 = 0
630 var count: i64 = 0
631 var o: i64 = 0
632 while i < sn {
633 let e: i64 = ri_line_end(schema, sn, i)
634 if ri_starts(schema, sn, i, "axis|" as *u8) == 1 {
635 ri_field(schema, i, e, TC_F_SECTION, sect, RI_PATHB)
636 if tc_list_has(out, count, sect) == 0 {
637 let sl: i64 = ri_slen(sect)
638 if o + sl + 1 < cap { o = ri_cat(out, o, sect); o = o + 1; count = count + 1 }
639 }
640 }
641 i = e + 1
642 }
643 return count
644}