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}