code wiki / _hdl_build / nx_night_grade.nx
nx_night_grade.nx source
↩ module page · 187 lines · 9615 B
1// nx_night_grade.nx -- SOVEREIGN night-reading benchmark gate (Reader arc, rung W-NIGHT-1).
2//
3// MEASURES the sourced eye-fatigue mechanism (blue light) per reader theme. The palette SSOT now
4// lives in the NATIVE content-addressed seg_store (ncfg / nx_native_config) -- NOT a flat .tsv
5// (operator 2026-06-20: "stop using tsv, nishi ecosystem only, all rungs everywhere"). The gate
6// SEEDS the 6-theme palette into a fresh per-run store then reads it back BY COLUMN NAME, so it has
7// ZERO dependency on knowledge/registry/night_themes.tsv (migrated off + removed). For each theme it
8// computes, from the dominant emitted colors (bg + fg, sRGB 0..255):
9// blue_emit = bb*BG_W + fb*FG_W (emitted blue, area-weighted)
10// total_emit = (br+bg+bb)*BG_W + (fr+fg+fb)*FG_W
11// blue_frac = blue_emit*1000 / total_emit (per-mille -- "how blue is the light", coverage-independent)
12// BG_W:FG_W = 9:1 = documented assumption that page background covers ~90% of the screen.
13//
14// Sourced rationale (knowledge/research/2026-06-16-night-reading-sourced.md):
15// short-wavelength (blue) light drives the circadian/eye-fatigue response (melanopsin/ipRGC); warm
16// 2700-3000K cuts blue; sRGB display white = 6500K (bluish). The night win = LESS BLUE, by composition
17// -- not merely darkness. Negative controls prove the metric measures blue, not dark.
18//
19// KAT order in the store (positional rows): 0 light, 1 sepia, 2 dark, 3 night, 4 black, 5 coolnight(neg).
20// expect_exit: 0 (all assertions green)
21// license_tier: ORIGINAL
22
23import "nx_syscalls.nx"
24import "nx_native_config.nx"
25
26func ng_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
27func ng_putn(v: i64) -> i64 {
28 let bb: *u8 = sys_mmap(28); var m: i64 = v
29 if m < 0 { m = 0 - m; sys_write(1, "-" as *u8, 1) }
30 let t: *u8 = sys_mmap(28); var k: i64 = 0
31 if m == 0 { t[0] = 48 as u8; k = 1 }
32 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
33 var i: i64 = 0; while i < k { bb[i] = t[k - 1 - i]; i = i + 1 } sys_write(1, bb, k); return 0
34}
35func ng_assert(label: *u8, cond: i64) -> i64 {
36 if cond != 0 { ng_puts(" [PASS] "); ng_puts(label); ng_puts("\n"); return 1 }
37 ng_puts(" [FAIL] "); ng_puts(label); ng_puts("\n"); return 0
38}
39// parse a NUL-terminated decimal string -> i64 (null-safe: absent field -> 0).
40func ng_atoi(s: *u8) -> i64 {
41 if (s as i64) == 0 { return 0 }
42 var v: i64 = 0
43 var i: i64 = 0
44 while s[i] != (0 as u8) {
45 let c: i64 = s[i] as i64
46 if c >= 48 { if c <= 57 { v = v * 10 + (c - 48) } }
47 i = i + 1
48 }
49 return v
50}
51// fresh unique native store prefix per run (mirrors nx_library_exceed.lib_fresh_prefix).
52func ng_fresh_prefix(out: *u8, base: *u8) -> i64 {
53 var po: i64 = 0
54 while base[po] != (0 as u8) { out[po] = base[po]; po = po + 1 }
55 var m: i64 = sys_now_ms()
56 let ds: *u8 = sys_mmap(28)
57 var k: i64 = 0
58 if m == 0 { ds[0] = 48 as u8; k = 1 }
59 while m > 0 { ds[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
60 var j: i64 = 0
61 while j < k { out[po] = ds[k - 1 - j]; po = po + 1; j = j + 1 }
62 out[po] = 45 as u8; po = po + 1
63 out[po] = 0 as u8
64 return po
65}
66// author one theme row (8 named string columns) into the native store under tag "thm".
67func ng_row(w: *i64, idx: i64, nm: *u8, v0: *u8, v1: *u8, v2: *u8, v3: *u8, v4: *u8, v5: *u8, kind: *u8) -> i64 {
68 let keys: *i64 = sys_mmap(8 * 16) as *i64
69 let vals: *i64 = sys_mmap(8 * 16) as *i64
70 keys[0] = ("name\x00") as i64; vals[0] = (nm as i64)
71 keys[1] = ("bg_r\x00") as i64; vals[1] = (v0 as i64)
72 keys[2] = ("bg_g\x00") as i64; vals[2] = (v1 as i64)
73 keys[3] = ("bg_b\x00") as i64; vals[3] = (v2 as i64)
74 keys[4] = ("fg_r\x00") as i64; vals[4] = (v3 as i64)
75 keys[5] = ("fg_g\x00") as i64; vals[5] = (v4 as i64)
76 keys[6] = ("fg_b\x00") as i64; vals[6] = (v5 as i64)
77 keys[7] = ("kind\x00") as i64; vals[7] = (kind as i64)
78 return ncfg_add_row(w, "thm\x00" as *u8, idx, keys, vals, 8)
79}
80// seed the 6-theme palette FAITHFULLY (values verbatim from the migrated night_themes.tsv).
81func ng_seed(prefix: *u8) -> i64 {
82 let w: *i64 = ncfg_begin()
83 ng_row(w, 0, "light\x00" as *u8, "251\x00" as *u8, "251\x00" as *u8, "249\x00" as *u8, "29\x00" as *u8, "29\x00" as *u8, "27\x00" as *u8, "day\x00" as *u8)
84 ng_row(w, 1, "sepia\x00" as *u8, "244\x00" as *u8, "236\x00" as *u8, "216\x00" as *u8, "67\x00" as *u8, "57\x00" as *u8, "31\x00" as *u8, "day\x00" as *u8)
85 ng_row(w, 2, "dark\x00" as *u8, "21\x00" as *u8, "22\x00" as *u8, "26\x00" as *u8, "216\x00" as *u8, "216\x00" as *u8, "210\x00" as *u8, "dark\x00" as *u8)
86 ng_row(w, 3, "night\x00" as *u8, "26\x00" as *u8, "18\x00" as *u8, "6\x00" as *u8, "232\x00" as *u8, "200\x00" as *u8, "144\x00" as *u8, "night\x00" as *u8)
87 ng_row(w, 4, "black\x00" as *u8, "0\x00" as *u8, "0\x00" as *u8, "0\x00" as *u8, "200\x00" as *u8, "180\x00" as *u8, "140\x00" as *u8, "night\x00" as *u8)
88 ng_row(w, 5, "coolnight\x00" as *u8, "10\x00" as *u8, "10\x00" as *u8, "20\x00" as *u8, "176\x00" as *u8, "196\x00" as *u8, "222\x00" as *u8, "neg\x00" as *u8)
89 ncfg_set_count(w, "thm\x00" as *u8, 6)
90 return ncfg_commit(prefix, w)
91}
92// seed + open + read all 6 themes' ints (file order) into ints[t*6+0..5]; returns total ints read, or -1.
93func ng_load(ints: *i64) -> i64 {
94 let prefix: *u8 = sys_mmap(256)
95 ng_fresh_prefix(prefix, "/tmp/night-themes-\x00" as *u8)
96 if ng_seed(prefix) != 0 { ng_puts("NIGHT-GRADE: native seed/commit failed\n"); return 0 - 1 }
97 let h: *i64 = ncfg_open(prefix)
98 if (h as i64) == 0 { ng_puts("NIGHT-GRADE: cannot open native store\n"); return 0 - 1 }
99 let cnt: i64 = ncfg_count(h, "thm\x00" as *u8)
100 if cnt < 6 { ng_puts("NIGHT-GRADE: expected 6 themes, got "); ng_putn(cnt); ng_puts("\n"); return 0 - 1 }
101 let rk: *i64 = sys_mmap(8 * 16) as *i64
102 let rv: *i64 = sys_mmap(8 * 16) as *i64
103 var nints: i64 = 0
104 var t0: i64 = 0
105 while t0 < cnt {
106 let rf: i64 = ncfg_row(h, "thm\x00" as *u8, t0, rk, rv, 16)
107 if rf > 0 {
108 ints[t0*6+0] = ng_atoi(ncfg_field(rk, rv, rf, "bg_r\x00" as *u8))
109 ints[t0*6+1] = ng_atoi(ncfg_field(rk, rv, rf, "bg_g\x00" as *u8))
110 ints[t0*6+2] = ng_atoi(ncfg_field(rk, rv, rf, "bg_b\x00" as *u8))
111 ints[t0*6+3] = ng_atoi(ncfg_field(rk, rv, rf, "fg_r\x00" as *u8))
112 ints[t0*6+4] = ng_atoi(ncfg_field(rk, rv, rf, "fg_g\x00" as *u8))
113 ints[t0*6+5] = ng_atoi(ncfg_field(rk, rv, rf, "fg_b\x00" as *u8))
114 nints = nints + 6
115 }
116 t0 = t0 + 1
117 }
118 return nints
119}
120
121func main() -> i64 {
122 // ---- load the palette from the NATIVE seg_store (no TSV) ----
123 let ints: *i64 = sys_mmap(64 * 8) as *i64
124 let nints: i64 = ng_load(ints)
125 if nints < 36 { ng_puts("NIGHT-GRADE: expected >=36 ints, got "); ng_putn(nints); ng_puts("\n"); return 3 }
126
127 let BG_W: i64 = 9
128 let FG_W: i64 = 1
129 let blue: *i64 = sys_mmap(16 * 8) as *i64
130 let tot: *i64 = sys_mmap(16 * 8) as *i64
131 let frac: *i64 = sys_mmap(16 * 8) as *i64
132
133 ng_puts("theme blue_emit total_emit blue_frac(permille)\n")
134 let names: *u8 = "light sepia dark night black coolnight" // 9 chars each
135 var t: i64 = 0
136 while t < 6 {
137 let br: i64 = ints[t*6+0]
138 let bgc: i64 = ints[t*6+1]
139 let bb2: i64 = ints[t*6+2]
140 let fr: i64 = ints[t*6+3]
141 let fgc: i64 = ints[t*6+4]
142 let fb: i64 = ints[t*6+5]
143 let be: i64 = bb2 * BG_W + fb * FG_W
144 let te: i64 = (br + bgc + bb2) * BG_W + (fr + fgc + fb) * FG_W
145 var fr2: i64 = 0
146 if te > 0 { fr2 = be * 1000 / te }
147 blue[t] = be; tot[t] = te; frac[t] = fr2
148 sys_write(1, " " as *u8, 2)
149 sys_write(1, (names as i64 + t*9) as *u8, 9)
150 ng_puts(" "); ng_putn(be)
151 ng_puts(" "); ng_putn(te)
152 ng_puts(" "); ng_putn(fr2)
153 ng_puts("\n")
154 t = t + 1
155 }
156
157 // index map: 0 light, 2 dark, 3 night, 4 black, 5 coolnight(neg)
158 ng_puts("\nassertions (sourced: night = LESS BLUE by composition, not just dark):\n")
159 var pass: i64 = 0
160 var total: i64 = 0
161
162 var c1: i64 = 0; if blue[3]*10 <= blue[0] { c1 = 1 }
163 total = total + 1; pass = pass + ng_assert("A1 night blue_emit*10 <= light blue_emit (>=90% blue cut)", c1)
164
165 var c2: i64 = 0; if blue[4]*10 <= blue[0] { c2 = 1 }
166 total = total + 1; pass = pass + ng_assert("A2 black blue_emit*10 <= light blue_emit (>=90% blue cut)", c2)
167
168 var c3: i64 = 0; if frac[3] <= 250 { c3 = 1 }
169 total = total + 1; pass = pass + ng_assert("A3 night blue_frac <= 250 (low-blue by composition)", c3)
170
171 var c4: i64 = 0; if frac[2] >= 330 { c4 = 1 }
172 total = total + 1; pass = pass + ng_assert("A4[neg] dark blue_frac >= 330 (plain dark is NOT low-blue)", c4)
173
174 var c5: i64 = 0; if frac[5] >= 330 { c5 = 1 }
175 total = total + 1; pass = pass + ng_assert("A5[neg] coolnight blue_frac >= 330 (dark+cool stays blue)", c5)
176
177 var c6: i64 = 0; if blue[3] < blue[2] { c6 = 1 }
178 total = total + 1; pass = pass + ng_assert("A6 night blue_emit < dark blue_emit", c6)
179
180 var c7: i64 = 0; if tot[4] < tot[2] { if tot[2] < tot[0] { c7 = 1 } }
181 total = total + 1; pass = pass + ng_assert("A7 total_emit black < dark < light", c7)
182
183 ng_puts("\nNIGHT-GRADE "); ng_putn(pass); ng_puts("/"); ng_putn(total); ng_puts(" assertions PASS\n")
184 if pass != total { return 9 }
185 ng_puts("NIGHT-GRADE-OK\n")
186 return 0
187}