;; lib/relations/tree.sx — tree/DAG queries: common ancestors, LCA, topo order. ;; ;; All computed in SX over the engine's fast `reach`/`ancestors`/`rnode` queries ;; — no new Datalog closures (every dl-query re-saturates, so derived graph ;; algorithms stay in SX). Kind-parameterised throughout, like the rest of the ;; engine. LCA returns a SET (a DAG may have several lowest common ancestors; a ;; tree yields exactly one). topo-order returns nil for a cyclic kind. (define relations-tree-any? (fn (pred xs) (cond ((= (len xs) 0) false) ((pred (first xs)) true) (else (relations-tree-any? pred (rest xs)))))) (define relations-intersect (fn (xs ys) (filter (fn (x) (relations-eng-member? x ys)) xs))) (define relations-subset? (fn (xs ys) (cond ((= (len xs) 0) true) ((relations-eng-member? (first xs) ys) (relations-subset? (rest xs) ys)) (else false)))) ;; All nodes touched by a kind (the materialised rnode relation — one query). (define relations-nodes (fn (db kind) (relations-dedup (relations-pluck (dl-query db (list (quote rnode) kind (quote X))) :X)))) ;; Common ancestors of a and b under kind (set intersection of the two ;; ancestor sets). (define relations-common-ancestors (fn (db a b kind) (relations-intersect (relations-ancestors db a kind) (relations-ancestors db b kind)))) ;; Lowest common ancestors: common ancestors with no other common ancestor ;; strictly below them (none reachable from them). A tree gives a singleton; a ;; DAG may give several. Empty when a and b share no ancestor. (define relations-lca (fn (db a b kind) (let ((common (relations-common-ancestors db a b kind))) (filter (fn (x) (not (relations-tree-any? (fn (y) (and (not (= x y)) (relations-reachable? db x y kind))) common))) common)))) ;; Kahn-style topological order: repeatedly place every node whose parents are ;; all already placed. Returns the node list in topological order, or nil if the ;; kind has a cycle. (define relations-topo-kahn (fn (db kind remaining placed) (if (= (len remaining) 0) placed (let ((ready (filter (fn (n) (relations-subset? (relations-parents-of db n kind) placed)) remaining))) (if (= (len ready) 0) placed (relations-topo-kahn db kind (filter (fn (n) (not (relations-eng-member? n ready))) remaining) (append placed ready))))))) (define relations-topo-order (fn (db kind) (if (relations-acyclic? db kind) (relations-topo-kahn db kind (relations-nodes db kind) (list)) nil))) ;; --- current-db convenience layer --- (define relations-component (fn (db node kind) (relations-ureach-bfs db kind (list node) (list node)))) (define relations-components-loop (fn (db kind remaining acc) (if (= (len remaining) 0) acc (let ((comp (relations-component db (first remaining) kind))) (relations-components-loop db kind (filter (fn (n) (not (relations-eng-member? n comp))) remaining) (append acc (list comp))))))) (define relations-component-count (fn (db kind) (len (relations-components db kind)))) (define relations-components (fn (db kind) (relations-components-loop db kind (relations-nodes db kind) (list)))) (define relations/common-ancestors (fn (a b kind) (relations-common-ancestors (relations-ensure-db!) a b kind))) (define relations/lca (fn (a b kind) (relations-lca (relations-ensure-db!) a b kind))) (define relations/topo-order (fn (kind) (relations-topo-order (relations-ensure-db!) kind))) (define relations/component (fn (node kind) (relations-component (relations-ensure-db!) node kind))) (define relations/components (fn (kind) (relations-components (relations-ensure-db!) kind))) (define relations/component-count (fn (kind) (relations-component-count (relations-ensure-db!) kind)))