nx_compare_growth_lib_20260910.nx source
↩ module page · 263 lines · 10868 B
1// Additive .plan capability-growth projection. Authored stages never become evidence verdicts.
2// Input is borrowed for model lifetime. No store writes, execution, publication or stage advancement.
3// growthpath|1|id|rung|entry|target|research_refs|learned_model|questions
4// growthexercise|1|id|path|deps|contract|tool_ref|workload_ref|resource_ref|acceptance_ref|failure_next
5// growthassessment|1|id|path|scope|stage|evidence_refs|unix|assessor_ref|supersedes
6// "-" denotes no list or optional link. References remain unresolved until their authoritative owners verify them.
7import "nx_measurement_binding_lib.nx"
8const CG_ROW_WORDS: i64=3
9const CG_MODEL_WORDS: i64=10
10const CG_PATH: i64=1
11const CG_EXERCISE: i64=2
12const CG_ASSESSMENT: i64=3
13const CG_E_INPUT: i64=0-1
14const CG_E_CAPACITY: i64=0-2
15const CG_E_SHAPE: i64=0-3
16const CG_E_ID: i64=0-4
17const CG_E_DUPLICATE: i64=0-5
18const CG_E_LINK: i64=0-6
19const CG_E_CYCLE: i64=0-7
20const CG_E_TIME: i64=0-8
21struct CgModel {
22 plan: *u8,
23 n: i64,
24 rows: *i64,
25 count: i64,
26 capacity: i64,
27 error: i64,
28 error_row: i64,
29 paths: i64,
30 exercises: i64,
31 assessments: i64
32}
33func cg_kind(b: *u8,p: i64,e: i64) -> i64 {
34 if bf_line_starts(b,p,e,"growthpath|" as *u8)==1 { return CG_PATH }
35 if bf_line_starts(b,p,e,"growthexercise|" as *u8)==1 { return CG_EXERCISE }
36 if bf_line_starts(b,p,e,"growthassessment|" as *u8)==1 { return CG_ASSESSMENT }
37 return 0
38}
39func cg_error(m: *CgModel,code: i64,row: i64) -> i64 {
40 if m.error==0 { m.error=code; m.error_row=row }
41 return code
42}
43func cg_field(m: *CgModel,row: i64,col: i64,off: *i64) -> i64 {
44 return bf_field(m.plan,m.rows[row*CG_ROW_WORDS+1],m.rows[row*CG_ROW_WORDS+2],col,off)
45}
46func cg_is(m: *CgModel,row: i64,col: i64,s: *u8) -> i64 {
47 var off: i64=0; let n: i64=cg_field(m,row,col,&off)
48 return pb_equal(m.plan,off,n,s,0,bf_slen(s))
49}
50func cg_same(m: *CgModel,a: i64,ac: i64,b: i64,bc: i64) -> i64 {
51 var ao: i64=0; var bo: i64=0
52 let an: i64=cg_field(m,a,ac,&ao); let bn: i64=cg_field(m,b,bc,&bo)
53 return pb_equal(m.plan,ao,an,m.plan,bo,bn)
54}
55func cg_ident(b: *u8,o: i64,n: i64) -> i64 {
56 if n<=0 { return 0 }
57 var i: i64=0
58 while i<n {
59 let c: i64=b[o+i] as i64
60 var ok: i64=0
61 if c>=65 { if c<=90 { ok=1 } }
62 if c>=97 { if c<=122 { ok=1 } }
63 if c>=48 { if c<=57 { ok=1 } }
64 if c==45 { ok=1 }; if c==95 { ok=1 }; if c==46 { ok=1 }
65 if ok==0 { return 0 }
66 i=i+1
67 }
68 if n==1 { if b[o]==(45 as u8) { return 0 } }
69 return 1
70}
71func cg_idfield(m: *CgModel,row: i64,col: i64) -> i64 {
72 var o: i64=0; let n: i64=cg_field(m,row,col,&o)
73 return cg_ident(m.plan,o,n)
74}
75func cg_decimal(m: *CgModel,row: i64,col: i64) -> i64 {
76 var o: i64=0; let n: i64=cg_field(m,row,col,&o)
77 if n<=0 { return CG_E_TIME }
78 var i: i64=0; var v: i64=0
79 while i<n {
80 let d: i64=(m.plan[o+i] as i64)-48
81 if d<0 { return CG_E_TIME }; if d>9 { return CG_E_TIME }
82 if v>(PB_I64_MAX-d)/10 { return CG_E_TIME }
83 v=v*10+d; i=i+1
84 }
85 if v<=0 { return CG_E_TIME }
86 return v
87}
88func cg_find(m: *CgModel,kind: i64,o: i64,n: i64) -> i64 {
89 var i: i64=0
90 while i<m.count {
91 if m.rows[i*CG_ROW_WORDS]==kind {
92 var io: i64=0; let ni: i64=cg_field(m,i,2,&io)
93 if pb_equal(m.plan,io,ni,m.plan,o,n)==1 { return i }
94 }
95 i=i+1
96 }
97 return 0-1
98}
99func cg_link(m: *CgModel,row: i64,col: i64,kind: i64) -> i64 {
100 var o: i64=0; let n: i64=cg_field(m,row,col,&o)
101 return cg_find(m,kind,o,n)
102}
103func cg_existing(m: *CgModel,row: i64,col: i64,tag: *u8) -> i64 {
104 var o: i64=0; let n: i64=cg_field(m,row,col,&o)
105 var p: i64=0; var count: i64=0
106 while p<m.n {
107 let raw: i64=bf_line_end(m.plan,m.n,p); let e: i64=pb_trim_cr(m.plan,p,raw)
108 if bf_line_starts(m.plan,p,e,tag)==1 {
109 var eo: i64=0; let en: i64=bf_field(m.plan,p,e,1,&eo)
110 if pb_equal(m.plan,o,n,m.plan,eo,en)==1 { count=count+1 }
111 }
112 p=raw+1
113 }
114 return count
115}
116// A dependency list is ordered authored data. Duplicate, missing and cross-path edges are refused.
117func cg_deps(m: *CgModel,row: i64,target: i64) -> i64 {
118 if cg_is(m,row,4,"-" as *u8)==1 { return 0 }
119 var o: i64=0; let n: i64=cg_field(m,row,4,&o)
120 var p: i64=o; let end: i64=o+n; var count: i64=0
121 while p<=end {
122 var e: i64=p
123 while e<end { if m.plan[e]==(44 as u8) { break }; e=e+1 }
124 if cg_ident(m.plan,p,e-p)==0 { return cg_error(m,CG_E_ID,row) }
125 let dep: i64=cg_find(m,CG_EXERCISE,p,e-p)
126 if dep<0 { return cg_error(m,CG_E_LINK,row) }
127 if dep==row { return cg_error(m,CG_E_CYCLE,row) }
128 if cg_same(m,row,3,dep,3)==0 { return cg_error(m,CG_E_LINK,row) }
129 var earlier: i64=o
130 while earlier<p {
131 var ee: i64=earlier
132 while ee<end { if m.plan[ee]==(44 as u8) { break }; ee=ee+1 }
133 if pb_equal(m.plan,p,e-p,m.plan,earlier,ee-earlier)==1 { return cg_error(m,CG_E_DUPLICATE,row) }
134 earlier=ee+1
135 }
136 if target<0 { count=count+1 } else { if dep==target { count=count+1 } }
137 if e==end { break }; p=e+1
138 }
139 return count
140}
141func cg_graph(m: *CgModel) -> i64 {
142 if m.count>PB_I64_MAX/8 { return cg_error(m,CG_E_CAPACITY,0-1) }
143 let bytes: i64=(m.count+1)*8
144 let deg: *i64=sys_mmap(bytes) as *i64
145 let seen: *i64=sys_mmap(bytes) as *i64
146 if (deg as i64)<=0 { return cg_error(m,CG_E_CAPACITY,0-1) }
147 if (seen as i64)<=0 { sys_munmap(deg as *u8,bytes); return cg_error(m,CG_E_CAPACITY,0-1) }
148 var i: i64=0
149 while i<m.count {
150 if m.rows[i*CG_ROW_WORDS]==CG_EXERCISE { deg[i]=cg_deps(m,i,0-1) }
151 i=i+1
152 }
153 var removed: i64=0; var changed: i64=1
154 while changed==1 {
155 changed=0; i=0
156 while i<m.count {
157 if m.rows[i*CG_ROW_WORDS]==CG_EXERCISE {
158 if seen[i]==0 { if deg[i]==0 {
159 seen[i]=1; removed=removed+1; changed=1
160 var j: i64=0
161 while j<m.count {
162 if m.rows[j*CG_ROW_WORDS]==CG_EXERCISE { if seen[j]==0 { deg[j]=deg[j]-cg_deps(m,j,i) } }
163 j=j+1
164 }
165 } }
166 }
167 i=i+1
168 }
169 }
170 sys_munmap(deg as *u8,bytes); sys_munmap(seen as *u8,bytes)
171 if m.error!=0 { return m.error }
172 if removed!=m.exercises { return cg_error(m,CG_E_CYCLE,0-1) }
173 return 0
174}
175func cg_validate(m: *CgModel) -> i64 {
176 var i: i64=0
177 while i<m.count {
178 let kind: i64=m.rows[i*CG_ROW_WORDS]
179 if cg_is(m,i,1,"1" as *u8)==0 { return cg_error(m,CG_E_SHAPE,i) }
180 if cg_idfield(m,i,2)==0 { return cg_error(m,CG_E_ID,i) }
181 var j: i64=0
182 while j<i {
183 if m.rows[j*CG_ROW_WORDS]==kind { if cg_same(m,i,2,j,2)==1 { return cg_error(m,CG_E_DUPLICATE,i) } }
184 j=j+1
185 }
186 if kind==CG_PATH {
187 if cg_existing(m,i,3,LD_RUNG_TAG)!=1 { return cg_error(m,CG_E_LINK,i) }
188 if cg_existing(m,i,5,LD_TARGET_TAG)!=1 { return cg_error(m,CG_E_LINK,i) }
189 let entry: i64=cg_is(m,i,4,"proactive_research" as *u8)+cg_is(m,i,4,"observed_failure" as *u8)+cg_is(m,i,4,"operator_target" as *u8)
190 if entry!=1 { return cg_error(m,CG_E_SHAPE,i) }
191 } else {
192 if cg_link(m,i,3,CG_PATH)<0 { return cg_error(m,CG_E_LINK,i) }
193 if kind==CG_EXERCISE {
194 if cg_deps(m,i,0-1)<0 { return m.error }
195 if cg_is(m,i,10,"@research" as *u8)+cg_is(m,i,10,"@learning" as *u8)==0 {
196 let next: i64=cg_link(m,i,10,CG_EXERCISE)
197 if next<0 { return cg_error(m,CG_E_LINK,i) }
198 if cg_same(m,i,3,next,3)==0 { return cg_error(m,CG_E_LINK,i) }
199 }
200 } else {
201 let stage: i64=cg_is(m,i,5,"research" as *u8)+cg_is(m,i,5,"learning" as *u8)+cg_is(m,i,5,"identified" as *u8)+cg_is(m,i,5,"practice" as *u8)+cg_is(m,i,5,"mastery" as *u8)+cg_is(m,i,5,"innovation" as *u8)
202 if stage!=1 { return cg_error(m,CG_E_SHAPE,i) }
203 let epoch: i64=cg_decimal(m,i,7)
204 if epoch<0 { return cg_error(m,CG_E_TIME,i) }
205 if cg_is(m,i,9,"-" as *u8)==0 {
206 let prev: i64=cg_link(m,i,9,CG_ASSESSMENT)
207 if prev<0 { return cg_error(m,CG_E_LINK,i) }
208 if cg_same(m,i,3,prev,3)==0 { return cg_error(m,CG_E_LINK,i) }
209 if cg_same(m,i,4,prev,4)==0 { return cg_error(m,CG_E_LINK,i) }
210 let prior: i64=cg_decimal(m,prev,7)
211 if prior<0 { return cg_error(m,CG_E_TIME,i) }
212 if prior>=epoch { return cg_error(m,CG_E_TIME,i) }
213 }
214 }
215 }
216 i=i+1
217 }
218 return cg_graph(m)
219}
220func cg_parse(b: *u8,n: i64) -> *CgModel {
221 let m: *CgModel=sys_mmap(CG_MODEL_WORDS*8) as *CgModel
222 if (m as i64)<=0 { return 0 as *CgModel }
223 m.plan=b; m.n=n; m.error_row=0-1
224 if n<0 { cg_error(m,CG_E_INPUT,0-1); return m }
225 if n==0 { return m }
226 if (b as i64)<=0 { cg_error(m,CG_E_INPUT,0-1); return m }
227 if pb_input_ok(b,n)==0 { cg_error(m,CG_E_INPUT,0-1); return m }
228 var p: i64=0
229 while p<n {
230 let e: i64=bf_line_end(b,n,p)
231 if cg_kind(b,p,e)>0 { m.capacity=m.capacity+1 }
232 p=e+1
233 }
234 if m.capacity==0 { return m }
235 if m.capacity>PB_I64_MAX/(CG_ROW_WORDS*8) { cg_error(m,CG_E_CAPACITY,0-1); return m }
236 m.rows=sys_mmap(m.capacity*CG_ROW_WORDS*8) as *i64
237 if (m.rows as i64)<=0 { cg_error(m,CG_E_CAPACITY,0-1); return m }
238 p=0
239 while p<n {
240 let raw: i64=bf_line_end(b,n,p); let e: i64=pb_trim_cr(b,p,raw); let kind: i64=cg_kind(b,p,e)
241 if kind>0 {
242 var scratch: i64=0
243 let fields: i64=pb_field_count(b,p,e,&scratch)
244 var expected: i64=9
245 if kind==CG_EXERCISE { expected=11 }; if kind==CG_ASSESSMENT { expected=10 }
246 if fields!=expected { cg_error(m,CG_E_SHAPE,m.count); return m }
247 var f: i64=1
248 while f<fields {
249 if bf_field(b,p,e,f,&scratch)<=0 { cg_error(m,CG_E_SHAPE,m.count); return m }; f=f+1
250 }
251 m.rows[m.count*CG_ROW_WORDS]=kind; m.rows[m.count*CG_ROW_WORDS+1]=p; m.rows[m.count*CG_ROW_WORDS+2]=e
252 m.count=m.count+1
253 if kind==CG_PATH { m.paths=m.paths+1 }; if kind==CG_EXERCISE { m.exercises=m.exercises+1 }; if kind==CG_ASSESSMENT { m.assessments=m.assessments+1 }
254 }
255 p=raw+1
256 }
257 cg_validate(m); return m
258}
259func cg_free(m: *CgModel) -> i64 {
260 if (m as i64)<=0 { return 0 }
261 if (m.rows as i64)>0 { sys_munmap(m.rows as *u8,m.capacity*CG_ROW_WORDS*8) }
262 sys_munmap(m as *u8,CG_MODEL_WORDS*8); return 0
263}