code wiki / (root) / nx_compare_growth_lib.nx

nx_compare_growth_lib.nx source

↩ module page · 304 lines · 12948 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} 141// Build indexed adjacency once; Kahn visits each vertex and edge once after ID resolution. 142// Four vertex arrays (head, indegree, queue, duplicate mark) share one owned workspace. 143func cg_graph_build(m: *CgModel,head: *i64,degree: *i64,marks: *i64,edges: *i64) -> i64 { 144 var row: i64=0; var used: i64=0 145 while row<m.count { 146 if m.rows[row*CG_ROW_WORDS]==CG_EXERCISE { 147 if cg_is(m,row,4,"-" as *u8)==0 { 148 var o: i64=0; let n: i64=cg_field(m,row,4,&o) 149 var p: i64=o; let end: i64=o+n 150 while p<=end { 151 var e: i64=p 152 while e<end { if m.plan[e]==(44 as u8) { break }; e=e+1 } 153 if cg_ident(m.plan,p,e-p)==0 { return cg_error(m,CG_E_ID,row) } 154 let dep: i64=cg_find(m,CG_EXERCISE,p,e-p) 155 if dep<0 { return cg_error(m,CG_E_LINK,row) } 156 if dep==row { return cg_error(m,CG_E_CYCLE,row) } 157 if cg_same(m,row,3,dep,3)==0 { return cg_error(m,CG_E_LINK,row) } 158 if marks[dep]==row+1 { return cg_error(m,CG_E_DUPLICATE,row) } 159 marks[dep]=row+1 160 edges[used*2]=row; edges[used*2+1]=head[dep]; head[dep]=used 161 degree[row]=degree[row]+1; used=used+1 162 if e==end { break }; p=e+1 163 } 164 } 165 } 166 row=row+1 167 } 168 return used 169} 170func cg_graph(m: *CgModel) -> i64 { 171 var edgecap: i64=0; var row: i64=0 172 while row<m.count { 173 if m.rows[row*CG_ROW_WORDS]==CG_EXERCISE { 174 if cg_is(m,row,4,"-" as *u8)==0 { 175 var o: i64=0; let n: i64=cg_field(m,row,4,&o) 176 edgecap=edgecap+1; var p: i64=o 177 while p<o+n { if m.plan[p]==(44 as u8) { edgecap=edgecap+1 }; p=p+1 } 178 } 179 } 180 row=row+1 181 } 182 // Four vertex arrays plus the edge pairs, each a native 64-bit word. 183 if m.count>=PB_I64_MAX/(4*8)-1 { return cg_error(m,CG_E_CAPACITY,0-1) } 184 let vertex_bytes: i64=(m.count+1)*4*8 185 if edgecap>(PB_I64_MAX-vertex_bytes)/(2*8) { return cg_error(m,CG_E_CAPACITY,0-1) } 186 let bytes: i64=vertex_bytes+edgecap*2*8 187 let workspace: *u8=sys_mmap(bytes) 188 if (workspace as i64)<=0 { return cg_error(m,CG_E_CAPACITY,0-1) } 189 let head: *i64=workspace as *i64 190 let degree: *i64=((workspace as i64)+(m.count+1)*8) as *i64 191 let queue: *i64=((workspace as i64)+(m.count+1)*2*8) as *i64 192 let marks: *i64=((workspace as i64)+(m.count+1)*3*8) as *i64 193 let edges: *i64=((workspace as i64)+vertex_bytes) as *i64 194 row=0; while row<m.count { head[row]=0-1; row=row+1 } 195 let built: i64=cg_graph_build(m,head,degree,marks,edges) 196 if built<0 { sys_munmap(workspace,bytes); return built } 197 var put: i64=0; var take: i64=0 198 row=0 199 while row<m.count { 200 if m.rows[row*CG_ROW_WORDS]==CG_EXERCISE { if degree[row]==0 { queue[put]=row; put=put+1 } } 201 row=row+1 202 } 203 while take<put { 204 let node: i64=queue[take]; take=take+1 205 var edge: i64=head[node] 206 while edge>=0 { 207 let target: i64=edges[edge*2] 208 degree[target]=degree[target]-1 209 if degree[target]==0 { queue[put]=target; put=put+1 } 210 edge=edges[edge*2+1] 211 } 212 } 213 sys_munmap(workspace,bytes) 214 if take!=m.exercises { return cg_error(m,CG_E_CYCLE,0-1) } 215 return 0 216} 217func cg_validate(m: *CgModel) -> i64 { 218 var i: i64=0 219 while i<m.count { 220 let kind: i64=m.rows[i*CG_ROW_WORDS] 221 if cg_is(m,i,1,"1" as *u8)==0 { return cg_error(m,CG_E_SHAPE,i) } 222 if cg_idfield(m,i,2)==0 { return cg_error(m,CG_E_ID,i) } 223 var j: i64=0 224 while j<i { 225 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) } } 226 j=j+1 227 } 228 if kind==CG_PATH { 229 if cg_existing(m,i,3,LD_RUNG_TAG)!=1 { return cg_error(m,CG_E_LINK,i) } 230 if cg_existing(m,i,5,LD_TARGET_TAG)!=1 { return cg_error(m,CG_E_LINK,i) } 231 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) 232 if entry!=1 { return cg_error(m,CG_E_SHAPE,i) } 233 } else { 234 if cg_link(m,i,3,CG_PATH)<0 { return cg_error(m,CG_E_LINK,i) } 235 if kind==CG_EXERCISE { 236 if cg_is(m,i,10,"@research" as *u8)+cg_is(m,i,10,"@learning" as *u8)==0 { 237 let next: i64=cg_link(m,i,10,CG_EXERCISE) 238 if next<0 { return cg_error(m,CG_E_LINK,i) } 239 if cg_same(m,i,3,next,3)==0 { return cg_error(m,CG_E_LINK,i) } 240 } 241 } else { 242 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) 243 if stage!=1 { return cg_error(m,CG_E_SHAPE,i) } 244 let epoch: i64=cg_decimal(m,i,7) 245 if epoch<0 { return cg_error(m,CG_E_TIME,i) } 246 if cg_is(m,i,9,"-" as *u8)==0 { 247 let prev: i64=cg_link(m,i,9,CG_ASSESSMENT) 248 if prev<0 { return cg_error(m,CG_E_LINK,i) } 249 if cg_same(m,i,3,prev,3)==0 { return cg_error(m,CG_E_LINK,i) } 250 if cg_same(m,i,4,prev,4)==0 { return cg_error(m,CG_E_LINK,i) } 251 let prior: i64=cg_decimal(m,prev,7) 252 if prior<0 { return cg_error(m,CG_E_TIME,i) } 253 if prior>=epoch { return cg_error(m,CG_E_TIME,i) } 254 } 255 } 256 } 257 i=i+1 258 } 259 return cg_graph(m) 260} 261func cg_parse(b: *u8,n: i64) -> *CgModel { 262 let m: *CgModel=sys_mmap(CG_MODEL_WORDS*8) as *CgModel 263 if (m as i64)<=0 { return 0 as *CgModel } 264 m.plan=b; m.n=n; m.error_row=0-1 265 if n<0 { cg_error(m,CG_E_INPUT,0-1); return m } 266 if n==0 { return m } 267 if (b as i64)<=0 { cg_error(m,CG_E_INPUT,0-1); return m } 268 if pb_input_ok(b,n)==0 { cg_error(m,CG_E_INPUT,0-1); return m } 269 var p: i64=0 270 while p<n { 271 let e: i64=bf_line_end(b,n,p) 272 if cg_kind(b,p,e)>0 { m.capacity=m.capacity+1 } 273 p=e+1 274 } 275 if m.capacity==0 { return m } 276 if m.capacity>PB_I64_MAX/(CG_ROW_WORDS*8) { cg_error(m,CG_E_CAPACITY,0-1); return m } 277 m.rows=sys_mmap(m.capacity*CG_ROW_WORDS*8) as *i64 278 if (m.rows as i64)<=0 { cg_error(m,CG_E_CAPACITY,0-1); return m } 279 p=0 280 while p<n { 281 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) 282 if kind>0 { 283 var scratch: i64=0 284 let fields: i64=pb_field_count(b,p,e,&scratch) 285 var expected: i64=9 286 if kind==CG_EXERCISE { expected=11 }; if kind==CG_ASSESSMENT { expected=10 } 287 if fields!=expected { cg_error(m,CG_E_SHAPE,m.count); return m } 288 var f: i64=1 289 while f<fields { 290 if bf_field(b,p,e,f,&scratch)<=0 { cg_error(m,CG_E_SHAPE,m.count); return m }; f=f+1 291 } 292 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 293 m.count=m.count+1 294 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 } 295 } 296 p=raw+1 297 } 298 cg_validate(m); return m 299} 300func cg_free(m: *CgModel) -> i64 { 301 if (m as i64)<=0 { return 0 } 302 if (m.rows as i64)>0 { sys_munmap(m.rows as *u8,m.capacity*CG_ROW_WORDS*8) } 303 sys_munmap(m as *u8,CG_MODEL_WORDS*8); return 0 304}