code wiki / _hdl_build / nx_editjudge.nx
nx_editjudge.nx source
↩ module page · 122 lines · 5475 B
1// nx_editjudge.nx -- CLI/MCP surface for the edit-compliance judge.
2// Measurement lives in nx_editjudge_lib.nx, which the gate imports too.
3//
4// VERBS
5// judge <ref.png> <gen.png> <from_hue_deg> <to_hue_deg>
6// -> JSON {masked, shifted, drifted, stayed, compliance_permil}. compliance = shifted/masked.
7// A HIGH "stayed" is the retention-converged failure (nothing changed); a HIGH "drifted"
8// is "changed to the wrong thing". They are reported separately on purpose.
9// selftest
10// expect_exit: 0 license_tier: ORIGINAL
11import "nx_editjudge_lib.nx"
12
13func jw(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
14func jn(v: i64) -> i64 {
15 let b: *u8 = sys_mmap(28)
16 var m: i64 = v
17 if m < 0 { sys_write(1, "-" as *u8, 1); m = 0 - m }
18 let t: *u8 = sys_mmap(28)
19 var k: i64 = 0
20 if m == 0 { t[0] = (48 as u8); k = 1 }
21 while m > 0 { t[k] = ((48 + (m % 10)) as u8); m = m / 10; k = k + 1 }
22 var i: i64 = 0
23 while i < k { b[i] = t[k - 1 - i]; i = i + 1 }
24 sys_write(1, b, k)
25 return 0
26}
27
28func j_streq(a: *u8, b: *u8) -> i64 {
29 var i: i64 = 0
30 while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 }
31 if b[i] != (0 as u8) { return 0 }
32 return 1
33}
34func j_atoi(s: *u8) -> i64 {
35 var v: i64 = 0
36 var i: i64 = 0
37 while s[i] != (0 as u8) { let c: i64 = (s[i] as i64) & 255; if c >= 48 { if c <= 57 { v = v*10 + (c-48) } } i = i + 1 }
38 return v
39}
40
41// synthetic solid-colour RGB field for selftest
42func j_fill(buf: *u8, npix: i64, r: i64, g: i64, b: i64) -> i64 {
43 var i: i64 = 0
44 while i < npix { buf[i*3] = (r as u8); buf[i*3+1] = (g as u8); buf[i*3+2] = (b as u8); i = i + 1 }
45 return 0
46}
47
48func j_selftest() -> i64 {
49 let n: i64 = 100
50 let refy: *u8 = sys_mmap(n*3)
51 let geng: *u8 = sys_mmap(n*3)
52 let geny: *u8 = sys_mmap(n*3)
53 j_fill(refy, n, 235, 205, 40) // yellow (hue ~ 51)
54 j_fill(geng, n, 40, 200, 70) // green (hue ~ 130)
55 j_fill(geny, n, 235, 205, 40) // still yellow -- the retention-converged failure
56 let mk: *i64 = sys_mmap(8)
57 let sh: *i64 = sys_mmap(8)
58 let dr: *i64 = sys_mmap(8)
59 let st: *i64 = sys_mmap(8)
60
61 let c1: i64 = ej_compliance(refy, geng, n, 50, 130, mk, sh, dr, st)
62 jw("T1 yellow->green compliance=" as *u8); jn(c1); jw(" (want ~1000)\n" as *u8)
63 let c2: i64 = ej_compliance(refy, geny, n, 50, 130, mk, sh, dr, st)
64 jw("T2 yellow->yellow compliance=" as *u8); jn(c2); jw(" stayed=" as *u8); jn(st[0]); jw(" (want ~0 -- this is the failure the loop hid)\n" as *u8)
65
66 var pass: i64 = 0
67 if c1 >= 900 { pass = pass + 1 }
68 if c2 <= 100 { pass = pass + 1 }
69 jw("SELFTEST pass=" as *u8); jn(pass); jw("/2" as *u8)
70 if pass == 2 { jw(" verdict=GREEN\n" as *u8); return 0 }
71 jw(" verdict=RED\n" as *u8)
72 return 1
73}
74
75func main(argc: i64, argv: *i64) -> i64 {
76 if argc < 2 {
77 jw("usage: nx_editjudge judge <ref.png> <gen.png> <from_hue> <to_hue>\n" as *u8)
78 jw(" nx_editjudge selftest\n" as *u8)
79 return 2
80 }
81 let verb: *u8 = argv[1] as *u8
82 if j_streq(verb, "selftest" as *u8) == 1 { return j_selftest() }
83 if j_streq(verb, "judge" as *u8) == 1 {
84 if argc < 6 { jw("{\"error\":\"judge needs <ref.png> <gen.png> <from_hue> <to_hue> [x0 y0 x1 y1]\"}\n" as *u8); return 2 }
85 let fw: *i64 = sys_mmap(8)
86 let fh: *i64 = sys_mmap(8)
87 let gw: *i64 = sys_mmap(8)
88 let gh: *i64 = sys_mmap(8)
89 let ref: *u8 = ej_load_rgb(argv[2] as *u8, fw, fh)
90 if ref == (0 as *u8) { jw("{\"error\":\"cannot decode reference PNG\"}\n" as *u8); return 3 }
91 let gen: *u8 = ej_load_rgb(argv[3] as *u8, gw, gh)
92 if gen == (0 as *u8) { jw("{\"error\":\"cannot decode generated PNG\"}\n" as *u8); return 3 }
93 if fw[0] != gw[0] { jw("{\"error\":\"dimension mismatch -- ref and gen must be the same size\"}\n" as *u8); return 4 }
94 if fh[0] != gh[0] { jw("{\"error\":\"dimension mismatch -- ref and gen must be the same size\"}\n" as *u8); return 4 }
95 let from: i64 = j_atoi(argv[4] as *u8)
96 let to: i64 = j_atoi(argv[5] as *u8)
97 var x0: i64 = 0
98 var y0: i64 = 0
99 var x1: i64 = 1000
100 var y1: i64 = 1000
101 if argc >= 10 { x0 = j_atoi(argv[6] as *u8); y0 = j_atoi(argv[7] as *u8); x1 = j_atoi(argv[8] as *u8); y1 = j_atoi(argv[9] as *u8) }
102 var satmin: i64 = EJ_SAT_MIN
103 if argc >= 11 { satmin = j_atoi(argv[10] as *u8) }
104 let mk: *i64 = sys_mmap(8)
105 let sh: *i64 = sys_mmap(8)
106 let dr: *i64 = sys_mmap(8)
107 let st: *i64 = sys_mmap(8)
108 let c: i64 = ej_compliance_spec(ref, gen, fw[0], fh[0], from, to, x0, y0, x1, y1, satmin, mk, sh, dr, st)
109 jw("{\"from_hue\":" as *u8); jn(from)
110 jw(",\"to_hue\":" as *u8); jn(to)
111 jw(",\"masked_px\":" as *u8); jn(mk[0])
112 jw(",\"shifted\":" as *u8); jn(sh[0])
113 jw(",\"drifted\":" as *u8); jn(dr[0])
114 jw(",\"stayed\":" as *u8); jn(st[0])
115 jw(",\"compliance_permil\":" as *u8); jn(c)
116 jw(",\"footprint_permil\":" as *u8); jn(ej_footprint(ref, gen, fw[0]*fh[0]))
117 jw(",\"headline\":\"shifted/masked -- high stayed = nothing changed, high drifted = wrong change; footprint = fraction of the WHOLE frame that moved (mask-free collateral check, DEC026)\"}\n" as *u8)
118 return 0
119 }
120 jw("{\"error\":\"unknown verb\"}\n" as *u8)
121 return 2
122}