code wiki / _hdl_build / nx_ui_zss.nx
nx_ui_zss.nx source
↩ module page · 231 lines · 14299 B
1// nx_ui_zss.nx -- ui-validation V1b (sovereign DETERMINISTIC engine). The EXACT-OPTIMAL structural
2// tree-edit-distance: real ZHANG-SHASHA (ZSS) over the DOM tree. Closes the honest gap nx_ui_tdist left
3// (postorder-Levenshtein = a tractable approximation; ZSS = the true tree-edit-distance that respects tree
4// structure in its edit operations). Reconstructs the tree from the postorder (tag,depth) DOM serialization:
5// leftmost-leaf-descendant l[] and keyroots derive directly from depth; then the ZSS forestdist DP. NO model.
6// KAT-GATED: `selftest` proves ZSS on known small-tree distances BEFORE any page is judged (anti-navel-gaze:
7// a hard algorithm must earn a green self-test first). HONEST ENVELOPE: exact ZSS over the SOURCE DOM tree,
8// node-capped for tractability; ZSS over the RENDERED AXTree = the browser-lane follow-on.
9// nx_ui_zss selftest | dist <urlA> <urlB> [connect]
10// exit: 0 (selftest GREEN / dist computed) | 1 selftest RED | 4 fetch-fail | 2 usage.
11// license_tier: ORIGINAL expect_exit: 0
12import "nx_tool_run.nx"
13const K_MAGIC_30000: i64 = 30000
14const K_MAGIC_2166136261: i64 = 2166136261
15const K_MAGIC_16777619: i64 = 16777619
16const K_MAGIC_2147483647: i64 = 2147483647
17const K_MAGIC_5000: i64 = 5000
18const K_MAGIC_4096: i64 = 4096
19const K_MAGIC_524288: i64 = 524288
20const K_MAGIC_2048: i64 = 2048
21
22func z_w(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 }
23func z_b(rep: *u8, pos: i64, s: *u8) -> i64 { var p: i64 = pos; var i: i64 = 0; while s[i] != (0 as u8) { if p < K_MAGIC_30000 { rep[p] = s[i]; p = p + 1 } i = i + 1 } return p }
24func z_bn(rep: *u8, pos: i64, v: i64) -> i64 { var p: i64 = pos; var m: i64 = v; if m < 0 { if p < K_MAGIC_30000 { rep[p] = 45 as u8; p = p + 1 } m = 0 - m } let t: *u8 = sys_mmap(28); var k: i64 = 0; if m == 0 { t[0] = 48 as u8; k = 1 } while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } var i: i64 = 0; while i < k { if p < K_MAGIC_30000 { rep[p] = t[k - 1 - i]; p = p + 1 } i = i + 1 } return p }
25func z_min3(a: i64, b: i64, c: i64) -> i64 { var m: i64 = a; if b < m { m = b } if c < m { m = c } return m }
26func z_hash(buf: *u8, s: i64, e: i64) -> i64 { var h: i64 = K_MAGIC_2166136261; var i: i64 = s; while i < e { let c: i64 = buf[i]; var lc: i64 = c; if c >= 65 { if c <= 90 { lc = c + 32 } } h = (h ^ lc) * K_MAGIC_16777619; h = h & K_MAGIC_2147483647; i = i + 1 } if h == 0 { h = 1 } return h }
27func z_void(buf: *u8, s: i64, e: i64) -> i64 { let ln: i64 = e - s; if ln == 3 { if buf[s]==(105 as u8) { if buf[s+1]==(109 as u8) { if buf[s+2]==(103 as u8) { return 1 } } } } if ln == 5 { if buf[s]==(105 as u8) { if buf[s+1]==(110 as u8) { return 1 } } } if ln == 2 { if buf[s]==(98 as u8) { if buf[s+1]==(114 as u8) { return 1 } } if buf[s]==(104 as u8) { if buf[s+1]==(114 as u8) { return 1 } } } if ln == 4 { if buf[s]==(109 as u8) { if buf[s+1]==(101 as u8) { return 1 } } if buf[s]==(108 as u8) { if buf[s+1]==(105 as u8) { return 1 } } } return 0 }
28func z_isletter(c: i64) -> i64 { if c >= 65 { if c <= 90 { return 1 } } if c >= 97 { if c <= 122 { return 1 } } return 0 }
29// parse HTML -> postorder lab[1..cnt] + dep[1..cnt]; return cnt (capped)
30func z_parse(buf: *u8, n: i64, lab: *i64, dep: *i64, cap: i64) -> i64 {
31 var cnt: i64 = 0
32 var depth: i64 = 0
33 var i: i64 = 0
34 while i < n {
35 if buf[i] != (60 as u8) { i = i + 1 } else {
36 let c1: i64 = buf[i+1]
37 if c1 == 33 {
38 var j: i64 = i + 1; var done: i64 = 0
39 while done == 0 { if j >= n { done = 1; i = n } else { if buf[j] == (62 as u8) { done = 1; i = j + 1 } else { j = j + 1 } } }
40 } else { if c1 == 47 {
41 var s: i64 = i + 2; var e: i64 = s
42 while e < n { let c: i64 = buf[e]; if c == 62 { let z: i64 = e; e = n + K_MAGIC_5000 + z } else { if c == 32 { let z2: i64 = e; e = n + K_MAGIC_5000 + z2 } else { e = e + 1 } } }
43 var namee: i64 = e
44 if namee > n { namee = namee - n - K_MAGIC_5000 }
45 if depth > 0 { if cnt < cap { cnt = cnt + 1; lab[cnt] = z_hash(buf, s, namee); dep[cnt] = depth } depth = depth - 1 }
46 var g: i64 = namee; var gd: i64 = 0
47 while gd == 0 { if g >= n { gd = 1; i = n } else { if buf[g] == (62 as u8) { gd = 1; i = g + 1 } else { g = g + 1 } } }
48 } else { if z_isletter(c1) == 1 {
49 var s2: i64 = i + 1; var e2: i64 = s2
50 while e2 < n { let c: i64 = buf[e2]; if c == 32 { let z: i64 = e2; e2 = n + K_MAGIC_5000 + z } else { if c == 62 { let z2: i64 = e2; e2 = n + K_MAGIC_5000 + z2 } else { if c == 47 { let z3: i64 = e2; e2 = n + K_MAGIC_5000 + z3 } else { e2 = e2 + 1 } } } }
51 var namee2: i64 = e2
52 if namee2 > n { namee2 = namee2 - n - K_MAGIC_5000 }
53 let isv: i64 = z_void(buf, s2, namee2)
54 var te: i64 = namee2; var selfc: i64 = 0; var fd2: i64 = 0
55 while fd2 == 0 { if te >= n { fd2 = 1 } else { if buf[te] == (62 as u8) { if te > 0 { if buf[te-1] == (47 as u8) { selfc = 1 } } fd2 = 1 } else { te = te + 1 } } }
56 if isv == 1 { if cnt < cap { cnt = cnt + 1; lab[cnt] = z_hash(buf, s2, namee2); dep[cnt] = depth + 1 } } else { if selfc == 1 { if cnt < cap { cnt = cnt + 1; lab[cnt] = z_hash(buf, s2, namee2); dep[cnt] = depth + 1 } } else { depth = depth + 1 } }
57 i = te + 1
58 } else { i = i + 1 } } }
59 }
60 }
61 return cnt
62}
63// l[i] = leftmost-leaf-descendant postorder index, from postorder depth
64func z_lfd(dep: *i64, n: i64, l: *i64) -> i64 {
65 var i: i64 = 1
66 while i <= n {
67 var j: i64 = i - 1
68 var go: i64 = 1
69 while go == 1 { if j < 1 { go = 0 } else { if dep[j] > dep[i] { j = j - 1 } else { go = 0 } } }
70 if j == i - 1 { l[i] = i } else { l[i] = j + 1 }
71 i = i + 1
72 }
73 return 0
74}
75// keyroots ascending; returns count
76func z_keyroots(l: *i64, n: i64, kr: *i64) -> i64 {
77 let seen: *i64 = sys_mmap((n + 2) * 8) as *i64
78 var cnt: i64 = 0
79 var i: i64 = n
80 while i >= 1 { if seen[l[i]] == 0 { seen[l[i]] = 1; kr[cnt] = i; cnt = cnt + 1 } i = i - 1 }
81 var a: i64 = 0; var b: i64 = cnt - 1
82 while a < b { let t: i64 = kr[a]; kr[a] = kr[b]; kr[b] = t; a = a + 1; b = b - 1 }
83 return cnt
84}
85// Zhang-Shasha tree-edit-distance (unit costs). 1-indexed postorder lab/dep.
86func z_zss(lab1: *i64, dep1: *i64, n1: i64, lab2: *i64, dep2: *i64, n2: i64) -> i64 {
87 let l1: *i64 = sys_mmap((n1 + 2) * 8) as *i64
88 let l2: *i64 = sys_mmap((n2 + 2) * 8) as *i64
89 z_lfd(dep1, n1, l1); z_lfd(dep2, n2, l2)
90 let kr1: *i64 = sys_mmap((n1 + 2) * 8) as *i64
91 let kr2: *i64 = sys_mmap((n2 + 2) * 8) as *i64
92 let k1n: i64 = z_keyroots(l1, n1, kr1)
93 let k2n: i64 = z_keyroots(l2, n2, kr2)
94 let w: i64 = n2 + 1
95 let td: *i64 = sys_mmap((n1 + 1) * (n2 + 1) * 8) as *i64
96 let fd: *i64 = sys_mmap((n1 + 1) * (n2 + 1) * 8) as *i64
97 var a: i64 = 0
98 while a < k1n {
99 let i1: i64 = kr1[a]
100 var bb: i64 = 0
101 while bb < k2n {
102 let j1: i64 = kr2[bb]
103 let li: i64 = l1[i1]
104 let lj: i64 = l2[j1]
105 fd[(li - 1) * w + (lj - 1)] = 0
106 var di: i64 = li
107 while di <= i1 { fd[di * w + (lj - 1)] = fd[(di - 1) * w + (lj - 1)] + 1; di = di + 1 }
108 var dj: i64 = lj
109 while dj <= j1 { fd[(li - 1) * w + dj] = fd[(li - 1) * w + (dj - 1)] + 1; dj = dj + 1 }
110 di = li
111 while di <= i1 {
112 dj = lj
113 while dj <= j1 {
114 var same: i64 = 0
115 if l1[di] == li { if l2[dj] == lj { same = 1 } }
116 if same == 1 {
117 var cost: i64 = 1
118 if lab1[di] == lab2[dj] { cost = 0 }
119 let v: i64 = z_min3(fd[(di - 1) * w + dj] + 1, fd[di * w + (dj - 1)] + 1, fd[(di - 1) * w + (dj - 1)] + cost)
120 fd[di * w + dj] = v
121 td[di * w + dj] = v
122 } else {
123 let v2: i64 = z_min3(fd[(di - 1) * w + dj] + 1, fd[di * w + (dj - 1)] + 1, fd[(l1[di] - 1) * w + (l2[dj] - 1)] + td[di * w + dj])
124 fd[di * w + dj] = v2
125 }
126 dj = dj + 1
127 }
128 di = di + 1
129 }
130 bb = bb + 1
131 }
132 a = a + 1
133 }
134 return td[n1 * w + n2]
135}
136func z_kat(rep: *u8, pos: i64, name: *u8, got: i64, want: i64, fails: *i64) -> i64 {
137 var p: i64 = pos
138 p = z_b(rep, p, " " as *u8); p = z_b(rep, p, name); p = z_b(rep, p, " got=" as *u8); p = z_bn(rep, p, got); p = z_b(rep, p, " want=" as *u8); p = z_bn(rep, p, want)
139 if got == want { p = z_b(rep, p, " PASS\n" as *u8) } else { p = z_b(rep, p, " FAIL\n" as *u8); fails[0] = fails[0] + 1 }
140 return p
141}
142func z_fetch(url: i64, connect: i64, buf: *u8, cap: i64, olen: *i64) -> i64 {
143 let HG: *u8 = "/volume1/homes/elderwesto/nishihost/nx_https_get.elf" as *u8
144 let av: *i64 = sys_mmap(16 * 8) as *i64
145 av[0] = HG as i64; av[1] = url
146 if connect != 0 { av[2] = connect; av[3] = 0 } else { av[2] = 0 }
147 return tr_run_capture(HG, av, buf, cap, olen)
148}
149func main(argc: i64, argv: *i64) -> i64 {
150 if argc < 2 { z_w(2, "usage: nx_ui_zss selftest | dist <urlA> <urlB> [connect]\n" as *u8); sys_exit(2); return 2 }
151 let verb: *u8 = argv[1] as *u8
152 if verb[0] == 115 {
153 let rep: *u8 = sys_mmap(K_MAGIC_4096)
154 var p: i64 = 0
155 let fails: *i64 = sys_mmap(16) as *i64
156 fails[0] = 0
157 p = z_b(rep, p, "NX-UI-ZSS selftest (Zhang-Shasha tree-edit-distance)\n" as *u8)
158 let la: *i64 = sys_mmap(64) as *i64
159 let da: *i64 = sys_mmap(64) as *i64
160 let lb: *i64 = sys_mmap(64) as *i64
161 let db: *i64 = sys_mmap(64) as *i64
162 // single vs single same
163 la[1]=10; da[1]=0; lb[1]=10; db[1]=0
164 p = z_kat(rep, p, "single-identical" as *u8, z_zss(la,da,1, lb,db,1), 0, fails)
165 // single relabel
166 lb[1]=11
167 p = z_kat(rep, p, "single-relabel" as *u8, z_zss(la,da,1, lb,db,1), 1, fails)
168 // a(b) vs a : postorder T1 [b(1),a(0)], T2 [a(0)]
169 la[1]=11; da[1]=1; la[2]=10; da[2]=0; lb[1]=10; db[1]=0
170 p = z_kat(rep, p, "delete-leaf" as *u8, z_zss(la,da,2, lb,db,1), 1, fails)
171 // a(b,c) vs a(b): T1 [b(1),c(1),a(0)], T2 [b(1),a(0)]
172 la[1]=11; da[1]=1; la[2]=12; da[2]=1; la[3]=10; da[3]=0; lb[1]=11; db[1]=1; lb[2]=10; db[2]=0
173 p = z_kat(rep, p, "delete-child" as *u8, z_zss(la,da,3, lb,db,2), 1, fails)
174 // a(b,c) vs a(x,c): T2 [x(1),c(1),a(0)]
175 lb[1]=14; db[1]=1; lb[2]=12; db[2]=1; lb[3]=10; db[3]=0
176 p = z_kat(rep, p, "relabel-child" as *u8, z_zss(la,da,3, lb,db,3), 1, fails)
177 // a(b,c) vs a(c,b): T2 [c(1),b(1),a(0)]
178 lb[1]=12; db[1]=1; lb[2]=11; db[2]=1; lb[3]=10; db[3]=0
179 p = z_kat(rep, p, "swap-children" as *u8, z_zss(la,da,3, lb,db,3), 2, fails)
180 // a(b(d),c) vs a(b,c): T1 [d(2),b(1),c(1),a(0)], T2 [b(1),c(1),a(0)]
181 la[1]=13; da[1]=2; la[2]=11; da[2]=1; la[3]=12; da[3]=1; la[4]=10; da[4]=0
182 lb[1]=11; db[1]=1; lb[2]=12; db[2]=1; lb[3]=10; db[3]=0
183 p = z_kat(rep, p, "delete-grandchild" as *u8, z_zss(la,da,4, lb,db,3), 1, fails)
184 if fails[0] == 0 { p = z_b(rep, p, "VERDICT=GREEN (ZSS proven correct; safe to judge pages)\n" as *u8) } else { p = z_b(rep, p, "VERDICT=RED\n" as *u8) }
185 sys_write(1, rep, p)
186 if fails[0] == 0 { sys_exit(0); return 0 }
187 sys_exit(1); return 1
188 }
189 if argc < 4 { z_w(2, "usage: nx_ui_zss dist <urlA> <urlB> [connect]\n" as *u8); sys_exit(2); return 2 }
190 var connect: i64 = 0
191 if argc >= 5 { connect = argv[4] }
192 let cap: i64 = K_MAGIC_524288
193 let bufA: *u8 = sys_mmap(cap + 16)
194 let bufB: *u8 = sys_mmap(cap + 16)
195 let olen: *i64 = sys_mmap(16) as *i64
196 let ra: i64 = z_fetch(argv[2], connect, bufA, cap, olen)
197 let na: i64 = olen[0]
198 if ra == 127 { z_w(1, "UI-ZSS verdict=FETCH-FAIL A\n" as *u8); sys_exit(4); return 4 }
199 if na <= 0 { z_w(1, "UI-ZSS verdict=FETCH-FAIL A-empty\n" as *u8); sys_exit(4); return 4 }
200 let rb: i64 = z_fetch(argv[3], connect, bufB, cap, olen)
201 let nb: i64 = olen[0]
202 if rb == 127 { z_w(1, "UI-ZSS verdict=FETCH-FAIL B\n" as *u8); sys_exit(4); return 4 }
203 if nb <= 0 { z_w(1, "UI-ZSS verdict=FETCH-FAIL B-empty\n" as *u8); sys_exit(4); return 4 }
204 let ncap: i64 = 320
205 let labA: *i64 = sys_mmap((ncap + 2) * 8) as *i64
206 let depA: *i64 = sys_mmap((ncap + 2) * 8) as *i64
207 let labB: *i64 = sys_mmap((ncap + 2) * 8) as *i64
208 let depB: *i64 = sys_mmap((ncap + 2) * 8) as *i64
209 let cA: i64 = z_parse(bufA, na, labA, depA, ncap)
210 let cB: i64 = z_parse(bufB, nb, labB, depB, ncap)
211 let dist: i64 = z_zss(labA, depA, cA, labB, depB, cB)
212 var bigger: i64 = cA
213 if cB > bigger { bigger = cB }
214 var pct: i64 = 0
215 if bigger > 0 { pct = (dist * 1000) / bigger }
216 let rep: *u8 = sys_mmap(K_MAGIC_2048)
217 var p: i64 = 0
218 p = z_b(rep, p, "{\"kind\":\"zss_tree_edit_distance\",\"nodesA\":" as *u8); p = z_bn(rep, p, cA)
219 p = z_b(rep, p, ",\"nodesB\":" as *u8); p = z_bn(rep, p, cB)
220 p = z_b(rep, p, ",\"node_cap\":" as *u8); p = z_bn(rep, p, ncap)
221 p = z_b(rep, p, ",\"zss_distance\":" as *u8); p = z_bn(rep, p, dist)
222 p = z_b(rep, p, ",\"drift_permille\":" as *u8); p = z_bn(rep, p, pct)
223 p = z_b(rep, p, ",\"verdict\":\"" as *u8)
224 if dist == 0 { p = z_b(rep, p, "IDENTICAL-STRUCTURE" as *u8) } else { if pct < 50 { p = z_b(rep, p, "MINOR-DRIFT" as *u8) } else { if pct < 250 { p = z_b(rep, p, "MODERATE-RESTRUCTURE" as *u8) } else { p = z_b(rep, p, "MAJOR-RESTRUCTURE" as *u8) } } }
225 p = z_b(rep, p, "\",\"metric\":\"EXACT Zhang-Shasha ZSS tree-edit-distance over the source DOM (node-capped); KAT-verified core; rendered-AXTree ZSS = browser-lane follow-on\"}" as *u8)
226 sys_write(1, rep, p)
227 let ofd: i64 = sys_openat_wr("knowledge/status/ui_zss.out.tmp" as *u8, 420)
228 if ofd >= 0 { sys_write(ofd, rep, p); sys_close(ofd); sys_renameat("knowledge/status/ui_zss.out.tmp" as *u8, "knowledge/status/ui_zss.out" as *u8) }
229 sys_exit(0)
230 return 0
231}