;; lib/relations/explain.sx — the connecting path: relations' answer to acl's ;; proof tree. ;; ;; A `reach(K,a,b)` derivation is a chain of one-hop `rel` facts a→…→b. The path ;; IS that derivation read off as the node sequence. lib/datalog/ records derived ;; facts but not provenance, so we re-derive the chain over the saturated edge ;; set — but breadth-first, so the path returned is a SHORTEST derivation (fewest ;; hops). Every consecutive pair in the result is a real rel(x,y,kind) fact; no ;; edges are invented. Cycles are handled by a visited set, so cyclic data ;; terminates rather than looping. ;; ;; (relations-path db a b kind) → (a … b) | nil ;; (relations-distance db a b k) → hop count | nil (define relations-last (fn (xs) (nth xs (- (len xs) 1)))) (define relations-filter-unseen (fn (xs seen) (filter (fn (x) (not (relations-member? x seen))) xs))) ;; Breadth-first over the kind's edge set. `queue` is a list of partial paths ;; (each a node list ending at its frontier node); `visited` is every node ever ;; enqueued, so each node is expanded once and the first path to reach b is a ;; shortest one. (define relations-path-bfs (fn (db b kind queue visited) (if (= (len queue) 0) nil (let ((path (first queue))) (let ((node (relations-last path))) (if (= node b) path (let ((succs (relations-filter-unseen (relations-children-of db node kind) visited))) (relations-path-bfs db b kind (append (rest queue) (map (fn (s) (append path (list s))) succs)) (append visited succs))))))))) ;; The connecting chain a→…→b under kind (shortest), or nil if unreachable. ;; a = b returns the trivial one-node path. (define relations-path (fn (db a b kind) (if (= a b) (list a) (relations-path-bfs db b kind (list (list a)) (list a))))) ;; Hop count of the shortest path (0 for a=b), or nil if unreachable. (define relations-distance (fn (db a b kind) (let ((p (relations-path db a b kind))) (if (= p nil) nil (- (len p) 1))))) ;; --- current-db convenience layer --- (define relations-ap-dfs (fn (db b kind path node) (if (= node b) (list path) (relations-concat-map (fn (nbr) (if (relations-eng-member? nbr path) (list) (relations-ap-dfs db b kind (append path (list nbr)) nbr))) (relations-children-of db node kind))))) (define relations-all-paths (fn (db a b kind) (if (= a b) (list (list a)) (relations-ap-dfs db b kind (list a) a)))) (define relations/path (fn (a b kind) (relations-path (relations-ensure-db!) a b kind))) (define relations/distance (fn (a b kind) (relations-distance (relations-ensure-db!) a b kind))) (define relations/descendants-any (fn (node) (relations-descendants-any (relations-ensure-db!) node))) (define relations/reachable-any? (fn (a b) (relations-reachable-any? (relations-ensure-db!) a b))) (define relations/all-paths (fn (a b kind) (relations-all-paths (relations-ensure-db!) a b kind)))