about summary refs log tree commit diff stats
path: root/js/baba-yaga/experimental/availability.baba
diff options
context:
space:
mode:
Diffstat (limited to 'js/baba-yaga/experimental/availability.baba')
-rw-r--r--js/baba-yaga/experimental/availability.baba108
1 files changed, 108 insertions, 0 deletions
diff --git a/js/baba-yaga/experimental/availability.baba b/js/baba-yaga/experimental/availability.baba
new file mode 100644
index 0000000..4bc59f8
--- /dev/null
+++ b/js/baba-yaga/experimental/availability.baba
@@ -0,0 +1,108 @@
+// Availability via 24-block boolean mask
+// WIP
+
+// range0: [0..n-1]
+range0 : n ->
+  when n is
+    0 then []
+    _ then append (range0 (n - 1)) (n - 1);
+
+// overlaps: does busy interval b overlap block i?
+overlaps : b i dayStart blockSize ->
+  (b.start < (dayStart + (i * blockSize) + blockSize)) and (b.end > (dayStart + (i * blockSize)));
+
+// anyBusyOnBlock: reduce OR over busy intervals for block i
+anyBusyOnBlock : busyList i dayStart blockSize ->
+  reduce (acc b -> acc or (overlaps b i dayStart blockSize)) false busyList;
+
+// allFreeOnBlock: everyone free on block i?
+allFreeOnBlock : busyLists i dayStart blockSize ->
+  reduce (acc bl -> acc and (when (anyBusyOnBlock bl i dayStart blockSize) is true then false _ then true)) true busyLists;
+
+// stepRuns: accumulate contiguous free runs over blocks with min-block filtering
+stepRuns : busyLists dayStart blockSize minBlocks acc i ->
+  when acc.inRun is
+    true then
+      when (allFreeOnBlock busyLists i dayStart blockSize) is
+        true then { inRun: true,  start: acc.start, runs: acc.runs }
+        _    then (
+          when ((i - acc.start) >= minBlocks) is
+            true then { inRun: false, start: 0, runs: append acc.runs { start: acc.start, end: i } }
+            _    then { inRun: false, start: 0, runs: acc.runs }
+        )
+    _ then
+      when (allFreeOnBlock busyLists i dayStart blockSize) is
+        true then { inRun: true,  start: i,        runs: acc.runs }
+        _    then acc;
+
+// finalizeRuns: close trailing run if needed (with min-block filtering)
+finalizeRuns : acc totalBlocks minBlocks ->
+  when acc.inRun is
+    true then (
+      when ((totalBlocks - acc.start) >= minBlocks) is
+        true then append acc.runs { start: acc.start, end: totalBlocks }
+        _    then acc.runs
+    )
+    _    then acc.runs;
+
+// convertRunsToMinutes: map block runs to minute intervals (shape-guarded)
+convertRunsToMinutes : runs dayStart blockSize ->
+  when (shape runs).kind is
+    "List" then map (r -> { start: dayStart + (r.start * blockSize), end: dayStart + (r.end * blockSize) }) runs
+    _      then [];
+
+// takeLimit: slice helper
+takeLimit : limit lst -> slice lst 0 (math.min (length lst) limit);
+
+// findCommonAvailability using mask approach
+// runnerFor: folder factory capturing inputs, returns (acc i -> ...)
+runnerFor : calendars dayStart dayEnd minMinutes acc i ->
+  stepRuns (values calendars) dayStart ((dayEnd - dayStart) / 24) (math.ceil (minMinutes / ((dayEnd - dayStart) / 24))) acc i;
+
+// buildRuns: produce runs list from inputs
+buildRuns : calendars dayStart dayEnd minMinutes ->
+  finalizeRuns (
+    reduce (runnerFor calendars dayStart dayEnd minMinutes)
+      { inRun: false, start: 0, runs: [] }
+      (range0 24)
+  ) 24 (math.ceil (minMinutes / ((dayEnd - dayStart) / 24)));
+
+// slotsFor: convert runs to minute intervals
+slotsFor : calendars dayStart dayEnd minMinutes ->
+  convertRunsToMinutes (buildRuns calendars dayStart dayEnd minMinutes) dayStart ((dayEnd - dayStart) / 24);
+
+// findCommonAvailability: top-level pipeline
+findCommonAvailability : calendars dayStart dayEnd minMinutes limit ->
+  takeLimit limit (slotsFor calendars dayStart dayEnd minMinutes);
+
+// ---------- Example usage ----------
+
+// Working window 09:00-17:00
+dayStart : 9 * 60;    // 540
+dayEnd   : 17 * 60;   // 1020
+minSlot  : 30;        // minutes
+limit    : 5;
+
+// Calendars: each value is a sorted list of busy intervals { start, end } in minutes
+calendars : {
+  alice: [
+    { start: 570, end: 630 }
+    { start: 720, end: 780 }
+    { start: 960, end: 1020 }
+  ],
+  bob: [
+    { start: 540, end: 555 }
+    { start: 660, end: 720 }
+    { start: 840, end: 870 }
+  ],
+  carol: [
+    { start: 600, end: 660 }
+    { start: 750, end: 810 }
+    { start: 915, end: 960 }
+  ]
+};
+
+io.out "loaded";
+available : findCommonAvailability calendars dayStart dayEnd minSlot limit;
+io.out "done";
+io.out available;