code wiki / _hdl_build / nx_vizsla_group.nx

nx_vizsla_group.nx source

↩ module page · 939 lines · 34765 B

1// nx_vizsla_group.nx -- NISHI VIZSLA GROUP COORDINATION: multi-member FIND-A-TIME + slot POLL. 2// Operator 2026-07-07: "exposing a calendar for a group to coordinate ... simple for a friend 3// group". The Cal.com-collective / Doodle capability, sovereign: each member keeps their OWN 4// R-CAL calendar store (privacy: nobody uploads a calendar anywhere); the group organ READS the 5// member stores it is told about, intersects their free time, and runs slot polls -- all on the 6// same tamper-evident CID/seg_store plane. Integer-only, NO-FLOAT, deterministic stdout. 7// 8// Records (load <file> <prefix> [segid], additive + CID-idempotent law 10): 9// MEMBER <gid> <name> <calprefix> -> "grp:" (roster: member's calendar store) 10// SLOT <pid> <slotid> <date> <start_min> <dur_min> -> "pol:" (a proposed poll slot) 11// VOTE <pid> <slotid> <member> <yes|no> <date> -> "vot:" (latest date per (slot,member) wins) 12// Commands: 13// findtime <prefix> <gid> <from> <to> <dur_min> [earliest_min latest_min] 14// for each day: union every member's busy (cal: events + rec: occurrences incl YEARLY) 15// -> the group's merged busy -> maximal FREE windows within [earliest,latest) (default the 16// whole day 0..1440 -- bounds are ARGS not magic numbers) -> windows >= dur are candidates. 17// tally <prefix> <pid> 18// per slot: yes/no counts (latest vote per member wins; additive history kept) -> 19// winner = max yes, tie -> lexicographically smaller slotid (deterministic). 20// Loud-fail: bad dates/times, to<from, unknown gid (0 members), unknown pid (0 slots), bad vote. 21// EXCEED posture (honest): Doodle/Cal.com do this in a data-harvesting cloud; here the member 22// calendars never leave the machine and every poll mutation is tamper-evident history. 23// spec: knowledge/research/2026-06-22-vizsla-sclass-roadmap.md license_tier: ORIGINAL 24import "nx_syscalls.nx" 25import "nx_canon_cid.nx" 26import "nx_seg_store.nx" 27const K_MAGIC_1970: i64 = 1970 28const K_MAGIC_146097: i64 = 146097 29const K_MAGIC_719468: i64 = 719468 30const K_MAGIC_146096: i64 = 146096 31const K_MAGIC_1460: i64 = 1460 32const K_MAGIC_36524: i64 = 36524 33const K_MAGIC_2048: i64 = 2048 34const K_MAGIC_1440: i64 = 1440 35const K_MAGIC_4096: i64 = 4096 36const K_MAGIC_262144: i64 = 262144 37const K_MAGIC_65536: i64 = 65536 38 39func vg_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 40func vg_p(s: *u8) -> i64 { sys_write(1, s, vg_slen(s)); return 0 } 41 42func vg_eq(a: *u8, b: *u8) -> i64 { 43 var i: i64 = 0 44 var go: i64 = 1 45 while go == 1 { 46 if a[i] != b[i] { return 0 } 47 if a[i] == (0 as u8) { return 1 } 48 i = i + 1 49 } 50 return 0 51} 52 53// lexicographic compare: -1 a<b, 0 eq, 1 a>b 54func vg_cmp(a: *u8, b: *u8) -> i64 { 55 var i: i64 = 0 56 var go: i64 = 1 57 while go == 1 { 58 let ca: i64 = a[i] as i64 59 let cb: i64 = b[i] as i64 60 if ca < cb { return 0 - 1 } 61 if ca > cb { return 1 } 62 if ca == 0 { return 0 } 63 i = i + 1 64 } 65 return 0 66} 67 68func vg_dup(s: *u8) -> *u8 { 69 let n: i64 = vg_slen(s) 70 let d: *u8 = sys_mmap(n + 2) 71 var i: i64 = 0 72 while i <= n { d[i] = s[i]; i = i + 1 } 73 return d 74} 75 76func vg_atoi(s: *u8) -> i64 { 77 var v: i64 = 0 78 var i: i64 = 0 79 while s[i] != (0 as u8) { 80 let d: i64 = (s[i] as i64) - 48 81 if d >= 0 { if d <= 9 { v = v * 10 + d } } 82 i = i + 1 83 } 84 return v 85} 86 87func vg_digits(s: *u8) -> i64 { 88 if s[0] == (0 as u8) { return 0 } 89 var i: i64 = 0 90 while s[i] != (0 as u8) { 91 let d: i64 = (s[i] as i64) - 48 92 if d < 0 { return 0 } 93 if d > 9 { return 0 } 94 i = i + 1 95 } 96 return 1 97} 98 99func vg_isws(c: i64) -> i64 { 100 if c == 32 { return 1 } 101 if c == 13 { return 1 } 102 if c == 9 { return 1 } 103 return 0 104} 105 106func vg_tok(b: *u8, off: i64, lend: i64, dst: *u8, cap: i64) -> i64 { 107 var p: i64 = off 108 var go: i64 = 1 109 while go == 1 { 110 if p >= lend { go = 0 } else { 111 if vg_isws(b[p] as i64) == 1 { p = p + 1 } else { go = 0 } 112 } 113 } 114 var t: i64 = 0 115 go = 1 116 while go == 1 { 117 if p >= lend { go = 0 } else { 118 if vg_isws(b[p] as i64) == 1 { go = 0 } else { 119 if t < cap - 1 { dst[t] = b[p]; t = t + 1 } 120 p = p + 1 121 } 122 } 123 } 124 dst[t] = 0 as u8 125 return p 126} 127 128func vg_cat(dst: *u8, off: i64, s: *u8) -> i64 { 129 var i: i64 = 0 130 while s[i] != (0 as u8) { dst[off + i] = s[i]; i = i + 1 } 131 return off + i 132} 133 134func vg_catn(dst: *u8, off: i64, v: i64) -> i64 { 135 var o: i64 = off 136 var m: i64 = v 137 if m < 0 { dst[o] = 45 as u8; o = o + 1; m = 0 - m } 138 let t: *u8 = sys_mmap(28) 139 var k: i64 = 0 140 if m == 0 { t[0] = 48 as u8; k = 1 } 141 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 142 var i: i64 = 0 143 while i < k { dst[o + i] = t[k - 1 - i]; i = i + 1 } 144 return o + k 145} 146 147func vg_hhmm(dst: *u8, off: i64, m: i64) -> i64 { 148 var o: i64 = off 149 let h: i64 = m / 60 150 let mm: i64 = m - h * 60 151 if h < 10 { dst[o] = 48 as u8; o = o + 1 } 152 o = vg_catn(dst, o, h) 153 dst[o] = 58 as u8; o = o + 1 154 if mm < 10 { dst[o] = 48 as u8; o = o + 1 } 155 o = vg_catn(dst, o, mm) 156 return o 157} 158 159func vg_kmemeq(b: *u8, off: i64, n: i64, s: *u8) -> i64 { 160 if vg_slen(s) != n { return 0 } 161 var i: i64 = 0 162 while i < n { 163 if b[off + i] != s[i] { return 0 } 164 i = i + 1 165 } 166 return 1 167} 168 169func vg_field(rec: *u8, rl: i64, want: *u8, out: *u8, cap: i64) -> i64 { 170 if rl < 8 { return 0 } 171 let nf: i64 = ss_r32(rec, 4) 172 var off: i64 = 8 173 var fi: i64 = 0 174 while fi < nf { 175 if off + 8 > rl { return 0 } 176 let kl: i64 = ss_r32(rec, off) 177 let koff: i64 = off + 4 178 let vl: i64 = ss_r32(rec, koff + kl) 179 let voff: i64 = koff + kl + 4 180 if vg_kmemeq(rec, koff, kl, want) == 1 { 181 var t: i64 = 0 182 while t < vl { 183 if t < cap - 1 { out[t] = rec[voff + t] } 184 t = t + 1 185 } 186 if t > cap - 1 { t = cap - 1 } 187 out[t] = 0 as u8 188 return 1 189 } 190 off = voff + vl 191 fi = fi + 1 192 } 193 return 0 194} 195 196func vg_iskind(b: *u8, koff: i64, kl: i64, pfx: *u8) -> i64 { 197 if kl <= 4 { return 0 } 198 return vg_kmemeq(b, koff, 4, pfx) 199} 200 201func vg_datedays(s: *u8) -> i64 { 202 let nums: *i64 = sys_mmap(8 * 4) as *i64 203 var nn: i64 = 0 204 var cur: i64 = 0 205 var indig: i64 = 0 206 var i: i64 = 0 207 var go: i64 = 1 208 while go == 1 { 209 let c: i64 = s[i] as i64 210 var isd: i64 = 0 211 if c >= 48 { if c <= 57 { isd = 1 } } 212 if isd == 1 { cur = cur * 10 + (c - 48); indig = 1 } 213 if isd == 0 { 214 if indig == 1 { 215 if nn < 4 { nums[nn] = cur; nn = nn + 1 } 216 cur = 0 217 indig = 0 218 } 219 } 220 if c == 0 { go = 0 } 221 i = i + 1 222 } 223 if nn != 3 { return 0 - 1 } 224 var y: i64 = nums[0] 225 let m: i64 = nums[1] 226 let d: i64 = nums[2] 227 if y < K_MAGIC_1970 { return 0 - 1 } 228 if m < 1 { return 0 - 1 } 229 if m > 12 { return 0 - 1 } 230 if d < 1 { return 0 - 1 } 231 if d > 31 { return 0 - 1 } 232 if m <= 2 { y = y - 1 } 233 let era: i64 = y / 400 234 let yoe: i64 = y - era * 400 235 var mp: i64 = m - 3 236 if m <= 2 { mp = m + 9 } 237 let doy: i64 = (153 * mp + 2) / 5 + d - 1 238 let doe: i64 = yoe * 365 + yoe / 4 - yoe / 100 + doy 239 return era * K_MAGIC_146097 + doe - K_MAGIC_719468 240} 241 242func vg_ymd2days(y0: i64, m: i64, d: i64) -> i64 { 243 var y: i64 = y0 244 if m <= 2 { y = y0 - 1 } 245 let era: i64 = y / 400 246 let yoe: i64 = y - era * 400 247 var mp: i64 = m - 3 248 if m <= 2 { mp = m + 9 } 249 let doy: i64 = (153 * mp + 2) / 5 + d - 1 250 let doe: i64 = yoe * 365 + yoe / 4 - yoe / 100 + doy 251 return era * K_MAGIC_146097 + doe - K_MAGIC_719468 252} 253 254func vg_civil(z0: i64, out: *i64) -> i64 { 255 var z: i64 = z0 + K_MAGIC_719468 256 var era: i64 = z / K_MAGIC_146097 257 if z < 0 { era = (z - K_MAGIC_146096) / K_MAGIC_146097 } 258 let doe: i64 = z - era * K_MAGIC_146097 259 let yoe: i64 = (doe - doe / K_MAGIC_1460 + doe / K_MAGIC_36524 - doe / K_MAGIC_146096) / 365 260 let y0: i64 = yoe + era * 400 261 let doy: i64 = doe - (365 * yoe + yoe / 4 - yoe / 100) 262 let mp: i64 = (5 * doy + 2) / 153 263 let d: i64 = doy - (153 * mp + 2) / 5 + 1 264 var m: i64 = mp + 3 265 if mp >= 10 { m = mp - 9 } 266 var y: i64 = y0 267 if m <= 2 { y = y0 + 1 } 268 out[0] = y; out[1] = m; out[2] = d 269 return 0 270} 271 272func vg_dim(y: i64, m: i64) -> i64 { 273 if m == 2 { 274 var leap: i64 = 0 275 if y % 4 == 0 { leap = 1 } 276 if y % 100 == 0 { leap = 0 } 277 if y % 400 == 0 { leap = 1 } 278 if leap == 1 { return 29 } 279 return 28 280 } 281 if m == 4 { return 30 } 282 if m == 6 { return 30 } 283 if m == 9 { return 30 } 284 if m == 11 { return 30 } 285 return 31 286} 287 288func vg_occ(freq: *u8, sd0: i64, sy: i64, sm: i64, sd: i64, interval: i64, k: i64) -> i64 { 289 if vg_eq(freq, "DAILY" as *u8) == 1 { return sd0 + k * interval } 290 if vg_eq(freq, "WEEKLY" as *u8) == 1 { return sd0 + k * interval * 7 } 291 var mstep: i64 = interval 292 if vg_eq(freq, "YEARLY" as *u8) == 1 { mstep = interval * 12 } 293 let tot: i64 = (sm - 1) + k * mstep 294 let y2: i64 = sy + tot / 12 295 let m2: i64 = tot - (tot / 12) * 12 + 1 296 var d2: i64 = sd 297 let dim: i64 = vg_dim(y2, m2) 298 if d2 > dim { d2 = dim } 299 return vg_ymd2days(y2, m2, d2) 300} 301 302func vg_daystr(dst: *u8, off: i64, days: i64) -> i64 { 303 let ymd: *i64 = sys_mmap(8 * 4) as *i64 304 vg_civil(days, ymd) 305 var o: i64 = off 306 o = vg_catn(dst, o, ymd[0]) 307 dst[o] = 45 as u8; o = o + 1 308 if ymd[1] < 10 { dst[o] = 48 as u8; o = o + 1 } 309 o = vg_catn(dst, o, ymd[1]) 310 dst[o] = 45 as u8; o = o + 1 311 if ymd[2] < 10 { dst[o] = 48 as u8; o = o + 1 } 312 o = vg_catn(dst, o, ymd[2]) 313 return o 314} 315 316func vg_find(list: *i64, n: i64, s: *u8) -> i64 { 317 var i: i64 = 0 318 while i < n { 319 if vg_eq(list[i] as *u8, s) == 1 { return i } 320 i = i + 1 321 } 322 return 0 - 1 323} 324 325func vg_put(keys: *i64, vals: *i64, nf: i64, kp: *u8, st: *i64) -> i64 { 326 let enc: *u8 = sys_mmap(K_MAGIC_2048) 327 let el: i64 = canon_encode(keys, vals, nf, enc) 328 let cid: *u8 = sys_mmap(96) 329 cid_of(enc, el, cid) 330 let kbuf: *u8 = sys_mmap(128) 331 var ko: i64 = 0 332 ko = vg_cat(kbuf, ko, kp) 333 ko = vg_cat(kbuf, ko, cid) 334 kbuf[ko] = 0 as u8 335 let seen: *i64 = st[1] as *i64 336 var fresh: i64 = 1 337 if vg_find(seen, st[2], kbuf) >= 0 { fresh = 0; st[4] = st[4] + 1 } 338 if fresh == 1 { 339 if st[2] < K_MAGIC_2048 { 340 let dk: *u8 = vg_dup(kbuf) 341 seen[st[2]] = dk as i64 342 st[2] = st[2] + 1 343 } 344 } 345 if fresh == 1 { 346 let pp: *i64 = sys_mmap(16) as *i64 347 let ll: *i64 = sys_mmap(16) as *i64 348 if ss_get_idx(st[6] as *u8, kbuf, pp, ll) == 1 { fresh = 0; st[5] = st[5] + 1 } 349 } 350 if fresh == 1 { 351 if ss_add(st[0] as *i64, 1, kbuf, enc, el) != 0 { return 0 - 1 } 352 st[3] = st[3] + 1 353 } 354 return 0 355} 356 357// ---------------- LOAD ---------------- 358func vg_cmd_load(argc: i64, argv: *i64) -> i64 { 359 if argc < 4 { vg_p("VIZSLA-GRP load needs <file> <prefix> -- fail loud\n" as *u8); return 1 } 360 let efile: *u8 = argv[2] as *u8 361 let prefix: *u8 = argv[3] as *u8 362 var segid: i64 = 0 363 if argc > 4 { segid = vg_atoi(argv[4] as *u8) } 364 if segid == 0 { segid = sys_now_us() } 365 366 let szp: *i64 = sys_mmap(16) as *i64 367 let b: *u8 = ss_readall(efile, szp) 368 let sz: i64 = szp[0] 369 if sz <= 0 { vg_p("VIZSLA-GRP file MISSING/EMPTY -- fail loud\n" as *u8); return 1 } 370 371 let mkeys: *i64 = sys_mmap(8 * 8) as *i64 372 let mvals: *i64 = sys_mmap(8 * 8) as *i64 373 mkeys[0] = "kind" as *u8 as i64 374 mkeys[1] = "gid" as *u8 as i64 375 mkeys[2] = "member" as *u8 as i64 376 mkeys[3] = "cal" as *u8 as i64 377 mvals[0] = "grp" as *u8 as i64 378 let skeys: *i64 = sys_mmap(8 * 8) as *i64 379 let svals: *i64 = sys_mmap(8 * 8) as *i64 380 skeys[0] = "kind" as *u8 as i64 381 skeys[1] = "pid" as *u8 as i64 382 skeys[2] = "slot" as *u8 as i64 383 skeys[3] = "date" as *u8 as i64 384 skeys[4] = "start" as *u8 as i64 385 skeys[5] = "dur" as *u8 as i64 386 svals[0] = "pol" as *u8 as i64 387 let vkeys: *i64 = sys_mmap(8 * 8) as *i64 388 let vvals: *i64 = sys_mmap(8 * 8) as *i64 389 vkeys[0] = "kind" as *u8 as i64 390 vkeys[1] = "pid" as *u8 as i64 391 vkeys[2] = "slot" as *u8 as i64 392 vkeys[3] = "member" as *u8 as i64 393 vkeys[4] = "vote" as *u8 as i64 394 vkeys[5] = "date" as *u8 as i64 395 vvals[0] = "vot" as *u8 as i64 396 397 let st: *i64 = sys_mmap(8 * 8) as *i64 398 st[0] = ss_begin() as i64 399 st[1] = sys_mmap(8 * K_MAGIC_2048) 400 st[2] = 0 401 st[3] = 0 402 st[4] = 0 403 st[5] = 0 404 st[6] = prefix as i64 405 406 let t0: *u8 = sys_mmap(128) 407 let f1: *u8 = sys_mmap(128) 408 let f2: *u8 = sys_mmap(256) 409 let f3: *u8 = sys_mmap(256) 410 let f4: *u8 = sys_mmap(128) 411 let f5: *u8 = sys_mmap(128) 412 var scanned: i64 = 0 413 414 var i: i64 = 0 415 while i < sz { 416 var e: i64 = i 417 var go: i64 = 1 418 while go == 1 { 419 if e >= sz { go = 0 } else { 420 if b[e] == (10 as u8) { go = 0 } else { e = e + 1 } 421 } 422 } 423 var p: i64 = vg_tok(b, i, e, t0, 128) 424 if t0[0] == (35 as u8) { t0[0] = 0 as u8 } 425 if vg_eq(t0, "MEMBER" as *u8) == 1 { 426 scanned = scanned + 1 427 p = vg_tok(b, p, e, f1, 128) 428 p = vg_tok(b, p, e, f2, 256) 429 p = vg_tok(b, p, e, f3, 256) 430 if f1[0] == (0 as u8) { vg_p("VIZSLA-GRP MEMBER missing gid -- fail loud\n" as *u8); return 1 } 431 if f2[0] == (0 as u8) { vg_p("VIZSLA-GRP MEMBER missing name -- fail loud\n" as *u8); return 1 } 432 if f3[0] == (0 as u8) { vg_p("VIZSLA-GRP MEMBER missing calprefix -- fail loud\n" as *u8); return 1 } 433 mvals[1] = f1 as i64 434 mvals[2] = f2 as i64 435 mvals[3] = f3 as i64 436 if vg_put(mkeys, mvals, 4, "grp:" as *u8, st) != 0 { vg_p("VIZSLA-GRP writer full -- fail loud\n" as *u8); return 1 } 437 } 438 if vg_eq(t0, "SLOT" as *u8) == 1 { 439 scanned = scanned + 1 440 p = vg_tok(b, p, e, f1, 128) 441 p = vg_tok(b, p, e, f2, 256) 442 p = vg_tok(b, p, e, f3, 256) 443 p = vg_tok(b, p, e, f4, 128) 444 p = vg_tok(b, p, e, f5, 128) 445 if f1[0] == (0 as u8) { vg_p("VIZSLA-GRP SLOT missing pid -- fail loud\n" as *u8); return 1 } 446 if f2[0] == (0 as u8) { vg_p("VIZSLA-GRP SLOT missing slotid -- fail loud\n" as *u8); return 1 } 447 if vg_datedays(f3) < 0 { vg_p("VIZSLA-GRP SLOT bad date -- fail loud\n" as *u8); return 1 } 448 if vg_digits(f4) == 0 { vg_p("VIZSLA-GRP SLOT start not digits -- fail loud\n" as *u8); return 1 } 449 if vg_digits(f5) == 0 { vg_p("VIZSLA-GRP SLOT dur not digits -- fail loud\n" as *u8); return 1 } 450 let sv: i64 = vg_atoi(f4) 451 let dv: i64 = vg_atoi(f5) 452 if sv >= K_MAGIC_1440 { vg_p("VIZSLA-GRP SLOT start >= 1440 -- fail loud\n" as *u8); return 1 } 453 if dv < 1 { vg_p("VIZSLA-GRP SLOT dur < 1 -- fail loud\n" as *u8); return 1 } 454 if sv + dv > K_MAGIC_1440 { vg_p("VIZSLA-GRP SLOT crosses midnight -- fail loud\n" as *u8); return 1 } 455 svals[1] = f1 as i64 456 svals[2] = f2 as i64 457 svals[3] = f3 as i64 458 svals[4] = f4 as i64 459 svals[5] = f5 as i64 460 if vg_put(skeys, svals, 6, "pol:" as *u8, st) != 0 { vg_p("VIZSLA-GRP writer full -- fail loud\n" as *u8); return 1 } 461 } 462 if vg_eq(t0, "VOTE" as *u8) == 1 { 463 scanned = scanned + 1 464 p = vg_tok(b, p, e, f1, 128) 465 p = vg_tok(b, p, e, f2, 256) 466 p = vg_tok(b, p, e, f3, 256) 467 p = vg_tok(b, p, e, f4, 128) 468 p = vg_tok(b, p, e, f5, 128) 469 if f1[0] == (0 as u8) { vg_p("VIZSLA-GRP VOTE missing pid -- fail loud\n" as *u8); return 1 } 470 if f2[0] == (0 as u8) { vg_p("VIZSLA-GRP VOTE missing slotid -- fail loud\n" as *u8); return 1 } 471 if f3[0] == (0 as u8) { vg_p("VIZSLA-GRP VOTE missing member -- fail loud\n" as *u8); return 1 } 472 var okv: i64 = 0 473 if vg_eq(f4, "yes" as *u8) == 1 { okv = 1 } 474 if vg_eq(f4, "no" as *u8) == 1 { okv = 1 } 475 if okv == 0 { vg_p("VIZSLA-GRP VOTE not yes|no -- fail loud\n" as *u8); return 1 } 476 if vg_datedays(f5) < 0 { vg_p("VIZSLA-GRP VOTE bad date -- fail loud\n" as *u8); return 1 } 477 vvals[1] = f1 as i64 478 vvals[2] = f2 as i64 479 vvals[3] = f3 as i64 480 vvals[4] = f4 as i64 481 vvals[5] = f5 as i64 482 if vg_put(vkeys, vvals, 6, "vot:" as *u8, st) != 0 { vg_p("VIZSLA-GRP writer full -- fail loud\n" as *u8); return 1 } 483 } 484 i = e + 1 485 } 486 487 var committed: i64 = 0 488 if st[3] > 0 { 489 if ss_commit(prefix, st[0] as *i64, segid) != 0 { vg_p("VIZSLA-GRP commit FAILED -- fail loud\n" as *u8); return 1 } 490 committed = 1 491 } 492 493 let rep: *u8 = sys_mmap(K_MAGIC_4096) 494 var o: i64 = 0 495 o = vg_cat(rep, o, "VIZSLA-GRP-LOAD scanned=" as *u8) 496 o = vg_catn(rep, o, scanned) 497 o = vg_cat(rep, o, " new=" as *u8) 498 o = vg_catn(rep, o, st[3]) 499 o = vg_cat(rep, o, " dup_infile=" as *u8) 500 o = vg_catn(rep, o, st[4]) 501 o = vg_cat(rep, o, " dup_instore=" as *u8) 502 o = vg_catn(rep, o, st[5]) 503 o = vg_cat(rep, o, " segment=" as *u8) 504 if committed == 1 { 505 o = vg_cat(rep, o, "seg-" as *u8) 506 o = vg_catn(rep, o, segid) 507 } else { 508 o = vg_cat(rep, o, "none" as *u8) 509 } 510 o = vg_cat(rep, o, "\n" as *u8) 511 sys_write(1, rep, o) 512 return 0 513} 514 515// collect a member calendar's busy intervals on day qd into bs/be (append; cap 512) 516func vg_busy_day(calpfx: *u8, qd: i64, bs: *i64, be: *i64, np: *i64) -> i64 { 517 let segs: *i64 = sys_mmap(8 * 260) as *i64 518 let ns: i64 = ss_manifest(calpfx, segs) 519 let fdt: *u8 = sys_mmap(128) 520 let fst: *u8 = sys_mmap(128) 521 let fdu: *u8 = sys_mmap(128) 522 let ffr: *u8 = sys_mmap(64) 523 let fiv: *u8 = sys_mmap(64) 524 let fct: *u8 = sys_mmap(64) 525 let ymd: *i64 = sys_mmap(8 * 4) as *i64 526 var s: i64 = 0 527 while s < ns { 528 let path: *u8 = sys_mmap(512) 529 var po: i64 = 0 530 po = vg_cat(path, po, calpfx) 531 po = vg_cat(path, po, segs[s] as *u8) 532 po = vg_cat(path, po, ".docs" as *u8) 533 path[po] = 0 as u8 534 let szp: *i64 = sys_mmap(16) as *i64 535 let b: *u8 = ss_readall(path, szp) 536 let sz: i64 = szp[0] 537 var j: i64 = 0 538 while j + 9 <= sz { 539 let kind: i64 = b[j] 540 let kl: i64 = ss_r32(b, j + 1) 541 let koff: i64 = j + 5 542 let vl: i64 = ss_r32(b, koff + kl) 543 let voff: i64 = koff + kl + 4 544 let rec: *u8 = (b as i64 + voff) as *u8 545 var iscal: i64 = 0 546 var isrec: i64 = 0 547 if kind == 1 { iscal = vg_iskind(b, koff, kl, "cal:" as *u8) } 548 if kind == 1 { isrec = vg_iskind(b, koff, kl, "rec:" as *u8) } 549 if iscal == 1 { 550 var dd: i64 = 0 - 1 551 if vg_field(rec, vl, "date" as *u8, fdt, 128) == 1 { dd = vg_datedays(fdt) } 552 if dd == qd { 553 vg_field(rec, vl, "start" as *u8, fst, 128) 554 vg_field(rec, vl, "dur" as *u8, fdu, 128) 555 if np[0] < 512 { 556 let sv: i64 = vg_atoi(fst) 557 let ev: i64 = sv + vg_atoi(fdu) 558 bs[np[0]] = sv 559 be[np[0]] = ev 560 np[0] = np[0] + 1 561 } 562 } 563 } 564 if isrec == 1 { 565 vg_field(rec, vl, "sdate" as *u8, fdt, 128) 566 vg_field(rec, vl, "start" as *u8, fst, 128) 567 vg_field(rec, vl, "dur" as *u8, fdu, 128) 568 vg_field(rec, vl, "freq" as *u8, ffr, 64) 569 vg_field(rec, vl, "interval" as *u8, fiv, 64) 570 vg_field(rec, vl, "count" as *u8, fct, 64) 571 let sd0: i64 = vg_datedays(fdt) 572 vg_civil(sd0, ymd) 573 let iv: i64 = vg_atoi(fiv) 574 let ct: i64 = vg_atoi(fct) 575 var k: i64 = 0 576 while k < ct { 577 let occ: i64 = vg_occ(ffr, sd0, ymd[0], ymd[1], ymd[2], iv, k) 578 if occ == qd { if np[0] < 512 { 579 let sv: i64 = vg_atoi(fst) 580 let ev: i64 = sv + vg_atoi(fdu) 581 bs[np[0]] = sv 582 be[np[0]] = ev 583 np[0] = np[0] + 1 584 } } 585 if occ > qd { k = ct } 586 k = k + 1 587 } 588 } 589 j = voff + vl 590 } 591 s = s + 1 592 } 593 return 0 594} 595 596// ---------------- FINDTIME ---------------- 597func vg_cmd_findtime(argc: i64, argv: *i64) -> i64 { 598 if argc < 7 { vg_p("VIZSLA-GRP findtime needs <prefix> <gid> <from> <to> <dur> [earliest latest] -- fail loud\n" as *u8); return 1 } 599 let prefix: *u8 = argv[2] as *u8 600 let gid: *u8 = argv[3] as *u8 601 let qfrom: i64 = vg_datedays(argv[4] as *u8) 602 let qto: i64 = vg_datedays(argv[5] as *u8) 603 if qfrom < 0 { vg_p("VIZSLA-GRP findtime bad from date -- fail loud\n" as *u8); return 1 } 604 if qto < 0 { vg_p("VIZSLA-GRP findtime bad to date -- fail loud\n" as *u8); return 1 } 605 if qto < qfrom { vg_p("VIZSLA-GRP findtime to < from -- fail loud\n" as *u8); return 1 } 606 if vg_digits(argv[6] as *u8) == 0 { vg_p("VIZSLA-GRP findtime dur not digits -- fail loud\n" as *u8); return 1 } 607 let dur: i64 = vg_atoi(argv[6] as *u8) 608 if dur < 1 { vg_p("VIZSLA-GRP findtime dur < 1 -- fail loud\n" as *u8); return 1 } 609 var emin: i64 = 0 610 var emax: i64 = K_MAGIC_1440 611 if argc > 8 { 612 if vg_digits(argv[7] as *u8) == 0 { vg_p("VIZSLA-GRP findtime earliest not digits -- fail loud\n" as *u8); return 1 } 613 if vg_digits(argv[8] as *u8) == 0 { vg_p("VIZSLA-GRP findtime latest not digits -- fail loud\n" as *u8); return 1 } 614 emin = vg_atoi(argv[7] as *u8) 615 emax = vg_atoi(argv[8] as *u8) 616 } 617 if emin >= emax { vg_p("VIZSLA-GRP findtime earliest >= latest -- fail loud\n" as *u8); return 1 } 618 if emax > K_MAGIC_1440 { vg_p("VIZSLA-GRP findtime latest > 1440 -- fail loud\n" as *u8); return 1 } 619 620 // roster: member cal prefixes for gid 621 let mems: *i64 = sys_mmap(8 * 32) as *i64 622 let mnames: *i64 = sys_mmap(8 * 32) as *i64 623 var nm: i64 = 0 624 let segs: *i64 = sys_mmap(8 * 260) as *i64 625 let ns: i64 = ss_manifest(prefix, segs) 626 let fg: *u8 = sys_mmap(128) 627 let fm: *u8 = sys_mmap(256) 628 let fc: *u8 = sys_mmap(256) 629 var s: i64 = 0 630 while s < ns { 631 let path: *u8 = sys_mmap(512) 632 var po: i64 = 0 633 po = vg_cat(path, po, prefix) 634 po = vg_cat(path, po, segs[s] as *u8) 635 po = vg_cat(path, po, ".docs" as *u8) 636 path[po] = 0 as u8 637 let szp: *i64 = sys_mmap(16) as *i64 638 let b: *u8 = ss_readall(path, szp) 639 let sz: i64 = szp[0] 640 var j: i64 = 0 641 while j + 9 <= sz { 642 let kind: i64 = b[j] 643 let kl: i64 = ss_r32(b, j + 1) 644 let koff: i64 = j + 5 645 let vl: i64 = ss_r32(b, koff + kl) 646 let voff: i64 = koff + kl + 4 647 var take: i64 = 0 648 if kind == 1 { take = vg_iskind(b, koff, kl, "grp:" as *u8) } 649 if take == 1 { 650 let rec: *u8 = (b as i64 + voff) as *u8 651 if vg_field(rec, vl, "gid" as *u8, fg, 128) == 1 { 652 if vg_eq(fg, gid) == 1 { 653 vg_field(rec, vl, "member" as *u8, fm, 256) 654 vg_field(rec, vl, "cal" as *u8, fc, 256) 655 if vg_find(mnames, nm, fm) < 0 { if nm < 32 { 656 let dm: *u8 = vg_dup(fm) 657 let dc: *u8 = vg_dup(fc) 658 mnames[nm] = dm as i64 659 mems[nm] = dc as i64 660 nm = nm + 1 661 } } 662 } 663 } 664 } 665 j = voff + vl 666 } 667 s = s + 1 668 } 669 if nm == 0 { vg_p("VIZSLA-GRP findtime gid has NO members -- fail loud\n" as *u8); return 1 } 670 671 let rep: *u8 = sys_mmap(K_MAGIC_262144) 672 var o: i64 = 0 673 var cands: i64 = 0 674 let bs: *i64 = sys_mmap(8 * 512) as *i64 675 let be: *i64 = sys_mmap(8 * 512) as *i64 676 let np: *i64 = sys_mmap(16) as *i64 677 var d: i64 = qfrom 678 while d <= qto { 679 np[0] = 0 680 var mi: i64 = 0 681 while mi < nm { 682 vg_busy_day(mems[mi] as *u8, d, bs, be, np) 683 mi = mi + 1 684 } 685 let n: i64 = np[0] 686 // sort by start 687 var a: i64 = 0 688 while a < n { 689 var mn: i64 = a 690 var c: i64 = a + 1 691 while c < n { 692 if bs[c] < bs[mn] { mn = c } 693 c = c + 1 694 } 695 if mn != a { 696 let t1: i64 = bs[a]; bs[a] = bs[mn]; bs[mn] = t1 697 let t2: i64 = be[a]; be[a] = be[mn]; be[mn] = t2 698 } 699 a = a + 1 700 } 701 // merged busy -> free windows within [emin,emax) 702 var prev: i64 = emin 703 var bi: i64 = 0 704 while bi < n { 705 var ws: i64 = prev 706 var we: i64 = bs[bi] 707 if we > emax { we = emax } 708 if we > ws { 709 if we - ws >= dur { 710 o = vg_cat(rep, o, "VIZSLA-GRP-SLOT gid=" as *u8) 711 o = vg_cat(rep, o, gid) 712 o = vg_cat(rep, o, " date=" as *u8) 713 o = vg_daystr(rep, o, d) 714 o = vg_cat(rep, o, " start=" as *u8) 715 o = vg_hhmm(rep, o, ws) 716 o = vg_cat(rep, o, " end=" as *u8) 717 o = vg_hhmm(rep, o, we) 718 o = vg_cat(rep, o, " fits=" as *u8) 719 o = vg_catn(rep, o, (we - ws) / dur) 720 o = vg_cat(rep, o, "\n" as *u8) 721 cands = cands + 1 722 } 723 } 724 if be[bi] > prev { prev = be[bi] } 725 if prev >= emax { bi = n } 726 bi = bi + 1 727 } 728 if prev < emax { 729 if emax - prev >= dur { 730 o = vg_cat(rep, o, "VIZSLA-GRP-SLOT gid=" as *u8) 731 o = vg_cat(rep, o, gid) 732 o = vg_cat(rep, o, " date=" as *u8) 733 o = vg_daystr(rep, o, d) 734 o = vg_cat(rep, o, " start=" as *u8) 735 o = vg_hhmm(rep, o, prev) 736 o = vg_cat(rep, o, " end=" as *u8) 737 o = vg_hhmm(rep, o, emax) 738 o = vg_cat(rep, o, " fits=" as *u8) 739 o = vg_catn(rep, o, (emax - prev) / dur) 740 o = vg_cat(rep, o, "\n" as *u8) 741 cands = cands + 1 742 } 743 } 744 d = d + 1 745 } 746 747 o = vg_cat(rep, o, "VIZSLA-GRP-FIND-VERDICT gid=" as *u8) 748 o = vg_cat(rep, o, gid) 749 o = vg_cat(rep, o, " members=" as *u8) 750 o = vg_catn(rep, o, nm) 751 o = vg_cat(rep, o, " days=" as *u8) 752 o = vg_catn(rep, o, qto - qfrom + 1) 753 o = vg_cat(rep, o, " dur=" as *u8) 754 o = vg_catn(rep, o, dur) 755 o = vg_cat(rep, o, " candidates=" as *u8) 756 o = vg_catn(rep, o, cands) 757 o = vg_cat(rep, o, "\n" as *u8) 758 sys_write(1, rep, o) 759 return 0 760} 761 762// ---------------- TALLY ---------------- 763func vg_cmd_tally(argc: i64, argv: *i64) -> i64 { 764 if argc < 4 { vg_p("VIZSLA-GRP tally needs <prefix> <pid> -- fail loud\n" as *u8); return 1 } 765 let prefix: *u8 = argv[2] as *u8 766 let pid: *u8 = argv[3] as *u8 767 768 let sl_id: *i64 = sys_mmap(8 * 64) as *i64 769 let sl_date: *i64 = sys_mmap(8 * 64) as *i64 770 let sl_start: *i64 = sys_mmap(8 * 64) as *i64 771 let sl_dur: *i64 = sys_mmap(8 * 64) as *i64 772 var nsl: i64 = 0 773 // votes: latest per (slot,member) 774 let vt_slot: *i64 = sys_mmap(8 * 256) as *i64 775 let vt_mem: *i64 = sys_mmap(8 * 256) as *i64 776 let vt_yes: *i64 = sys_mmap(8 * 256) as *i64 777 let vt_day: *i64 = sys_mmap(8 * 256) as *i64 778 var nvt: i64 = 0 779 780 let segs: *i64 = sys_mmap(8 * 260) as *i64 781 let ns: i64 = ss_manifest(prefix, segs) 782 let fp: *u8 = sys_mmap(128) 783 let fs: *u8 = sys_mmap(256) 784 let fd: *u8 = sys_mmap(128) 785 let f4: *u8 = sys_mmap(128) 786 let f5: *u8 = sys_mmap(128) 787 var s: i64 = 0 788 while s < ns { 789 let path: *u8 = sys_mmap(512) 790 var po: i64 = 0 791 po = vg_cat(path, po, prefix) 792 po = vg_cat(path, po, segs[s] as *u8) 793 po = vg_cat(path, po, ".docs" as *u8) 794 path[po] = 0 as u8 795 let szp: *i64 = sys_mmap(16) as *i64 796 let b: *u8 = ss_readall(path, szp) 797 let sz: i64 = szp[0] 798 var j: i64 = 0 799 while j + 9 <= sz { 800 let kind: i64 = b[j] 801 let kl: i64 = ss_r32(b, j + 1) 802 let koff: i64 = j + 5 803 let vl: i64 = ss_r32(b, koff + kl) 804 let voff: i64 = koff + kl + 4 805 let rec: *u8 = (b as i64 + voff) as *u8 806 var ispol: i64 = 0 807 var isvot: i64 = 0 808 if kind == 1 { ispol = vg_iskind(b, koff, kl, "pol:" as *u8) } 809 if kind == 1 { isvot = vg_iskind(b, koff, kl, "vot:" as *u8) } 810 if ispol == 1 { 811 if vg_field(rec, vl, "pid" as *u8, fp, 128) == 1 { 812 if vg_eq(fp, pid) == 1 { if nsl < 64 { 813 vg_field(rec, vl, "slot" as *u8, fs, 256) 814 vg_field(rec, vl, "date" as *u8, fd, 128) 815 vg_field(rec, vl, "start" as *u8, f4, 128) 816 vg_field(rec, vl, "dur" as *u8, f5, 128) 817 let ds: *u8 = vg_dup(fs) 818 let dd: *u8 = vg_dup(fd) 819 sl_id[nsl] = ds as i64 820 sl_date[nsl] = dd as i64 821 sl_start[nsl] = vg_atoi(f4) 822 sl_dur[nsl] = vg_atoi(f5) 823 nsl = nsl + 1 824 } } 825 } 826 } 827 if isvot == 1 { 828 if vg_field(rec, vl, "pid" as *u8, fp, 128) == 1 { 829 if vg_eq(fp, pid) == 1 { 830 vg_field(rec, vl, "slot" as *u8, fs, 256) 831 vg_field(rec, vl, "member" as *u8, fd, 128) 832 vg_field(rec, vl, "vote" as *u8, f4, 128) 833 vg_field(rec, vl, "date" as *u8, f5, 128) 834 let vday: i64 = vg_datedays(f5) 835 var isyes: i64 = 0 836 if vg_eq(f4, "yes" as *u8) == 1 { isyes = 1 } 837 var hit: i64 = 0 - 1 838 var vi: i64 = 0 839 while vi < nvt { 840 if vg_eq(vt_slot[vi] as *u8, fs) == 1 { 841 if vg_eq(vt_mem[vi] as *u8, fd) == 1 { hit = vi; vi = nvt } 842 } 843 vi = vi + 1 844 } 845 if hit >= 0 { 846 if vday >= vt_day[hit] { vt_yes[hit] = isyes; vt_day[hit] = vday } 847 } 848 if hit < 0 { if nvt < 256 { 849 let dvs: *u8 = vg_dup(fs) 850 let dvm: *u8 = vg_dup(fd) 851 vt_slot[nvt] = dvs as i64 852 vt_mem[nvt] = dvm as i64 853 vt_yes[nvt] = isyes 854 vt_day[nvt] = vday 855 nvt = nvt + 1 856 } } 857 } 858 } 859 } 860 j = voff + vl 861 } 862 s = s + 1 863 } 864 if nsl == 0 { vg_p("VIZSLA-GRP tally pid has NO slots -- fail loud\n" as *u8); return 1 } 865 866 // sort slots lexicographically (deterministic) 867 var a: i64 = 0 868 while a < nsl { 869 var mn: i64 = a 870 var c: i64 = a + 1 871 while c < nsl { 872 if vg_cmp(sl_id[c] as *u8, sl_id[mn] as *u8) < 0 { mn = c } 873 c = c + 1 874 } 875 if mn != a { 876 let t1: i64 = sl_id[a]; sl_id[a] = sl_id[mn]; sl_id[mn] = t1 877 let t2: i64 = sl_date[a]; sl_date[a] = sl_date[mn]; sl_date[mn] = t2 878 let t3: i64 = sl_start[a]; sl_start[a] = sl_start[mn]; sl_start[mn] = t3 879 let t4: i64 = sl_dur[a]; sl_dur[a] = sl_dur[mn]; sl_dur[mn] = t4 880 } 881 a = a + 1 882 } 883 884 let rep: *u8 = sys_mmap(K_MAGIC_65536) 885 var o: i64 = 0 886 var wi: i64 = 0 - 1 887 var wyes: i64 = 0 - 1 888 var si: i64 = 0 889 while si < nsl { 890 var yes: i64 = 0 891 var no: i64 = 0 892 var vi: i64 = 0 893 while vi < nvt { 894 if vg_eq(vt_slot[vi] as *u8, sl_id[si] as *u8) == 1 { 895 if vt_yes[vi] == 1 { yes = yes + 1 } else { no = no + 1 } 896 } 897 vi = vi + 1 898 } 899 o = vg_cat(rep, o, "VIZSLA-GRP-TALLY pid=" as *u8) 900 o = vg_cat(rep, o, pid) 901 o = vg_cat(rep, o, " slot=" as *u8) 902 o = vg_cat(rep, o, sl_id[si] as *u8) 903 o = vg_cat(rep, o, " date=" as *u8) 904 o = vg_cat(rep, o, sl_date[si] as *u8) 905 o = vg_cat(rep, o, " start=" as *u8) 906 o = vg_hhmm(rep, o, sl_start[si]) 907 o = vg_cat(rep, o, " yes=" as *u8) 908 o = vg_catn(rep, o, yes) 909 o = vg_cat(rep, o, " no=" as *u8) 910 o = vg_catn(rep, o, no) 911 o = vg_cat(rep, o, "\n" as *u8) 912 if yes > wyes { wyes = yes; wi = si } 913 si = si + 1 914 } 915 916 o = vg_cat(rep, o, "VIZSLA-GRP-TALLY-VERDICT pid=" as *u8) 917 o = vg_cat(rep, o, pid) 918 o = vg_cat(rep, o, " slots=" as *u8) 919 o = vg_catn(rep, o, nsl) 920 o = vg_cat(rep, o, " votes=" as *u8) 921 o = vg_catn(rep, o, nvt) 922 o = vg_cat(rep, o, " winner=" as *u8) 923 o = vg_cat(rep, o, sl_id[wi] as *u8) 924 o = vg_cat(rep, o, " yes=" as *u8) 925 o = vg_catn(rep, o, wyes) 926 o = vg_cat(rep, o, "\n" as *u8) 927 sys_write(1, rep, o) 928 return 0 929} 930 931func main(argc: i64, argv: *i64) -> i64 { 932 if argc < 2 { vg_p("VIZSLA-GRP usage: load|findtime|tally -- fail loud\n" as *u8); return 1 } 933 let cmd: *u8 = argv[1] as *u8 934 if vg_eq(cmd, "load" as *u8) == 1 { return vg_cmd_load(argc, argv) } 935 if vg_eq(cmd, "findtime" as *u8) == 1 { return vg_cmd_findtime(argc, argv) } 936 if vg_eq(cmd, "tally" as *u8) == 1 { return vg_cmd_tally(argc, argv) } 937 vg_p("VIZSLA-GRP unknown command (load|findtime|tally) -- fail loud\n" as *u8) 938 return 1 939}