; lib/artdag/plan.sx — Phase 3: schedule a DAG (or its dirty subset) into
; topological batches under a max-parallelism cap. A batch is a set of nodes
; whose deps are all satisfied by earlier batches, so they run in parallel.
; cap <= 0 means unlimited width. Depends on dag.sx and analyze.sx.

; inputs of id that also lie inside the scheduled set (out-of-set deps are
; treated as already satisfied — e.g. clean cache hits in an incremental plan).
(define
  artdag/-deps-in
  (fn
    (dag id sset)
    (filter
      (fn (in) (artdag/member? in sset))
      (artdag/node-inputs (artdag/dag-get dag id)))))

(define
  artdag/-ready-in
  (fn
    (dag sset placed)
    (filter
      (fn
        (id)
        (and
          (not (artdag/member? id placed))
          (artdag/all-in? (artdag/-deps-in dag id sset) placed)))
      (artdag/sort-strings sset))))

(define
  artdag/-batch-loop
  (fn
    (dag sset placed batches)
    (if
      (= (len placed) (len sset))
      batches
      (let
        ((wave (artdag/-ready-in dag sset placed)))
        (artdag/-batch-loop
          dag
          sset
          (concat placed wave)
          (concat batches (list wave)))))))

; split a wave into consecutive chunks of at most n (sorted order preserved).
(define
  artdag/-chunk
  (fn
    (xs n)
    (if
      (<= (len xs) n)
      (list xs)
      (cons
        (slice xs 0 n)
        (artdag/-chunk (slice xs n (len xs)) n)))))

(define
  artdag/-cap-split
  (fn
    (batches cap)
    (if
      (<= cap 0)
      batches
      (reduce
        (fn (acc b) (concat acc (artdag/-chunk b cap)))
        (list)
        batches))))

; schedule an explicit set of node-ids into capped topological batches.
(define
  artdag/plan-subset
  (fn
    (dag node-ids cap)
    (artdag/-cap-split (artdag/-batch-loop dag node-ids (list) (list)) cap)))

; full plan over every node in the dag.
(define
  artdag/plan
  (fn (dag cap) (artdag/plan-subset dag (keys (artdag/dag-nodes dag)) cap)))

; incremental plan: schedule only the dirty closure of the changed nodes.
(define
  artdag/plan-dirty
  (fn
    (dag changed cap)
    (artdag/plan-subset dag (artdag/dirty-closure dag changed) cap)))

; ---- plan inspection ----

(define artdag/plan-batches (fn (plan) (len plan)))

(define
  artdag/plan-width
  (fn
    (plan)
    (reduce (fn (m b) (if (> (len b) m) (len b) m)) 0 plan)))

(define
  artdag/plan-flatten
  (fn (plan) (reduce (fn (acc b) (concat acc b)) (list) plan)))

(define artdag/plan-size (fn (plan) (len (artdag/plan-flatten plan))))
