; lib/artdag/analyze.sx — Phase 2: Analyze on Datalog.
; Project the DAG's edges into a Datalog db and answer dependency questions
; (deps, dependents, transitive reachability) plus dirty-closure propagation
; as recursive Datalog — the acl/relations reachability shape. Depends on
; lib/artdag/dag.sx and the lib/datalog/ public API.

; edge(input-id, node-id): data flows input -> node (input is a dependency).
(define
  artdag/edge-facts
  (fn
    (dag)
    (reduce
      (fn
        (acc id)
        (concat
          acc
          (map
            (fn (in) (list (quote edge) in id))
            (artdag/node-inputs (artdag/dag-get dag id)))))
      (list)
      (keys (artdag/dag-nodes dag)))))

; reachable(X,Y): Y is a transitive dependent of X (forward, downstream).
(define
  artdag/reach-rules
  (quote
    ((reachable X Y <- (edge X Y))
      (reachable X Z <- (edge X Y) (reachable Y Z)))))

(define
  artdag/analyze
  (fn (dag) (dl-program-data (artdag/edge-facts dag) artdag/reach-rules)))

; pull a single variable's bindings out of a subst list, sorted for determinism.
(define
  artdag/-bindings
  (fn
    (substs var)
    (artdag/sort-strings (map (fn (s) (get s var)) substs))))

; direct dependencies (inputs) of a node.
(define
  artdag/deps-of
  (fn
    (db id)
    (artdag/-bindings (dl-query db (list (quote edge) (quote X) id)) :X)))

; direct dependents of a node.
(define
  artdag/dependents-of
  (fn
    (db id)
    (artdag/-bindings (dl-query db (list (quote edge) id (quote Y))) :Y)))

; transitive dependents (everything downstream of a node).
(define
  artdag/reachable-from
  (fn
    (db id)
    (artdag/-bindings
      (dl-query db (list (quote reachable) id (quote Y)))
      :Y)))

; transitive dependencies (everything upstream of a node).
(define
  artdag/ancestors-of
  (fn
    (db id)
    (artdag/-bindings
      (dl-query db (list (quote reachable) (quote X) id))
      :X)))

; dirty propagation: dirty(Y) :- edge(X,Y), dirty(X). Seeds are changed nodes.
(define artdag/dirty-rules (quote ((dirty Y <- (edge X Y) (dirty X)))))

(define
  artdag/dirty-seeds
  (fn (changed) (map (fn (c) (list (quote dirty) c)) changed)))

; transitive dirty closure of a set of changed node-ids: the changed nodes plus
; every transitive dependent that must recompute. Sorted, deduplicated.
(define
  artdag/dirty-closure
  (fn
    (dag changed)
    (let
      ((db (dl-program-data (concat (artdag/edge-facts dag) (artdag/dirty-seeds changed)) artdag/dirty-rules)))
      (artdag/-bindings (dl-query db (list (quote dirty) (quote X))) :X))))
