; Copyright (c) 1993-1999 by Richard Kelsey. See file COPYING. ; General transitive closure ; (make-graph-from-predecessors nodes node-parents node-temp set-node-temp!) ; -> graph ; (make-graph-from-successors nodes node-kids node-temp set-node-temp!) ; -> graph ; ; (transitive-or! graph elements set-elements! element-temp set-element-temp!) ; (transitive-or-with-kill! graph elements set-elements! node-kills element-temp set-element-temp!) ; (transitive-or-with-pass! graph elements set-elements! node-passes element-temp set-element-temp!) ; ; (transitive-and! graph elements set-elements! element-temp set-element-temp!) ; (transitive-and-with-kill! graph elements set-elements! node-kills element-temp set-element-temp!) ; (transitive-and-with-pass! graph elements set-elements! node-passes element-temp set-element-temp!) ;---------------- (define (make-graph-from-predecessors user-nodes user-node-parents node-temp set-node-temp!) (let ((nodes (make-nodes user-nodes set-node-temp!))) (connect-nodes-using-parents! nodes user-node-parents node-temp) (for-each (lambda (node) (set-node-temp! (node-data node) #f)) nodes) (make-graph nodes))) (define (make-graph-from-successors user-nodes user-node-kids node-temp set-node-temp!) (let ((nodes (make-nodes user-nodes set-node-temp!))) (connect-nodes-using-children! nodes user-node-kids node-temp) (for-each (lambda (node) (set-node-temp! (node-data node) #f)) nodes) (make-graph nodes))) (define (make-nodes user-nodes set-node-temp!) (map (lambda (data) (let ((node (node-maker data '() '()))) (set-node-temp! data node) node)) user-nodes)) (define-record-type graph (nodes ; list of nodes ) ()) (define make-graph graph-maker) (define-record-type node (data ; user's data (parents) ; predecessors (kids)) ; successors (elt-set ; elements kill-set ; elements that are not passed changed? ; change flag for iteration )) ;------------------------------ ; Six false fronts for the real procedure. (define (transitive-or! graph elts set-elts! elt-hash set-elt-hash!) (do-it graph elts set-elts! #f #f elt-hash set-elt-hash! (transitive-or-closure! or-update-node))) (define (transitive-or-with-kill! graph elts set-elts! kill-elts elt-hash set-elt-hash!) (do-it graph elts set-elts! kill-elts #f elt-hash set-elt-hash! (transitive-or-closure! or-update-node-with-kill))) (define (transitive-or-with-pass! graph elts set-elts! pass-elts elt-hash set-elt-hash!) (do-it graph elts set-elts! pass-elts #t elt-hash set-elt-hash! (transitive-or-closure! or-update-node-with-kill))) (define (transitive-and! graph elts set-elts! elt-hash set-elt-hash!) (do-it graph elts set-elts! #f #f elt-hash set-elt-hash! (transitive-and-closure! and-update-node))) (define (transitive-and-with-kill! graph elts set-elts! kill-elts elt-hash set-elt-hash!) (do-it graph elts set-elts! kill-elts #f elt-hash set-elt-hash! (transitive-and-closure! and-update-node-with-kill))) (define (transitive-and-with-pass! graph elts set-elts! pass-elts elt-hash set-elt-hash!) (do-it graph elts set-elts! pass-elts #t elt-hash set-elt-hash! (transitive-and-closure! and-update-node-with-kill))) (define (do-it graph elts set-elts! kill-elts pass? elt-hash set-elt-hash! op) (let* ((nodes (graph-nodes graph)) (elt-unhash-vec (add-elements! nodes elts kill-elts pass? elt-hash set-elt-hash!))) (op nodes) (record-results! nodes elt-unhash-vec set-elts!) (do ((i 0 (+ i 1))) ((= i (vector-length elt-unhash-vec))) (set-elt-hash! (vector-ref elt-unhash-vec i) #f)) (values))) ;---------------- ; Setting the kids field of the nodes (define (connect-nodes-using-children! nodes children node-slot) (for-each (lambda (node) (set-node-kids! node (map (lambda (kid) (let ((t (node-slot kid))) (if (not (node? t)) (missing-node-error kid "child" node)) (set-node-parents! t (cons node (node-parents t))) t)) (children (node-data node))))) nodes)) (define (connect-nodes-using-parents! nodes parents node-slot) (for-each (lambda (node) (set-node-parents! node (map (lambda (parent) (let ((t (node-slot parent))) (if (not (node? t)) (missing-node-error t "parent" node)) (set-node-kids! t (cons node (node-kids t))) t)) (parents (node-data node))))) nodes)) (define (missing-node-error node relationship relation) (error (format #f "Transitive - ~S, ~A of ~S, not in list of nodes" node relationship (node-data relation)))) ;---------------- (define (add-elements! nodes node-elements node-kills pass? element-temp set-element-temp!) (let ((unhash-vec (element-hasher nodes node-elements element-temp set-element-temp!)) (element-hash (make-element-hash element-temp))) (for-each (lambda (node) (set-node-elt-set! node (make-element-set (node-elements (node-data node)) element-hash))) nodes) (if node-kills (for-each (lambda (node) (let ((kill-set (make-element-set (node-kills (node-data node)) element-hash))) (set-node-kill-set! node (if pass? (integer-set-not kill-set) kill-set)))) nodes)) unhash-vec)) (define (make-element-set elts elt-hash) (let loop ((elts elts) (set (make-empty-integer-set))) (if (null? elts) set (loop (cdr elts) (cond ((elt-hash (car elts)) => (lambda (hash) (add-to-integer-set set hash))) (else set)))))) ;---------------- ; Counting the elements and assigning numbers to them (define-record-type element-hash (number ; the element-hash record is just a way of tagging this number ) ; with a unique predicate ()) (define (element-hasher nodes elts elt-hash set-elt-hash!) (let loop ((to-do '()) (ts nodes) (all-elts '()) (count 0)) (cond ((null? to-do) (if (null? ts) (real-element-hasher all-elts count) (loop (elts (node-data (car ts))) (cdr ts) all-elts count))) ((element-hash? (elt-hash (car to-do))) (loop (cdr to-do) ts all-elts count)) (else (set-elt-hash! (car to-do) (element-hash-maker count)) (loop (cdr to-do) ts (cons (car to-do) all-elts) (+ count 1)))))) (define (real-element-hasher elts count) (let ((unhash-vec (make-vector count))) (do ((i (- count 1) (- i 1)) (elts elts (cdr elts))) ((null? elts)) (vector-set! unhash-vec i (car elts))) unhash-vec)) (define (make-element-hash elt-hash) (lambda (elt) (let ((hash (elt-hash elt))) (if (element-hash? hash) (element-hash-number hash) #f)))) ;---------------- ; Turn the element sets into lists of elements and clean up stray pointers ; at the same time. (define (record-results! nodes elt-unhash-vec set-elts!) (for-each (lambda (node) (set-elts! (node-data node) (map-over-integer-set (lambda (i) (vector-ref elt-unhash-vec i)) (node-elt-set node))) (set-node-elt-set! node #f) (set-node-kill-set! node #f)) nodes)) ;---------------- ; The OR algorithm - keeps passing elements around until the changes stop. (define (transitive-or-closure! op) (lambda (nodes) (for-each (lambda (node) (set-node-changed?! node #t)) nodes) (let loop ((to-do nodes)) (if (not (null? to-do)) (let* ((node (car to-do)) (elt-set (node-elt-set node))) (set-node-changed?! node #f) (let kids-loop ((ts (node-kids node)) (to-do (cdr to-do))) (cond ((null? ts) (loop to-do)) ((and (op (car ts) elt-set) (not (node-changed? (car ts)))) (set-node-changed?! (car ts) #t) (kids-loop (cdr ts) (cons (car ts) to-do))) (else (kids-loop (cdr ts) to-do))))))))) ; The weird function INTEGER-SET-SUBTRACT&IOR-WITH-TEST! takes three integer ; sets, subtracts the second from the first and inclusive OR's the result ; with the third. It returns the resulting set and a flag which is #T if ; the result is not the same as the original third set. The inclusive OR ; may be destructive. (define (or-update-node-with-kill node elt-set) (receive (set change?) (integer-set-subtract&ior-with-test! elt-set (node-kill-set node) (node-elt-set node)) (set-node-elt-set! node set) change?)) (define (or-update-node node elt-set) (receive (set change?) (integer-set-ior-with-test! elt-set (node-elt-set node)) (set-node-elt-set! node set) change?)) ; Implementations using simpler, nondestructive operations (these might be ; done more efficiently if they had access to the underlying representation ; of integer sets). (define (integer-set-subtract&ior-with-test! set1 set2 set3) (let ((result (integer-set-ior set3 (integer-set-subtract set1 set2)))) (values result (not (integer-set-equal? set3 result))))) (define (integer-set-ior-with-test! set1 set3) (let ((result (integer-set-ior set3 set1))) (values result (not (integer-set-equal? set3 result))))) ;---------------- ; The AND algorithm - keeps a to-do list of nodes whose parents' elements ; have changed, instead of a list of nodes whose elements have changed. (define (transitive-and-closure! op) (lambda (nodes) (let loop ((to-do (filter (lambda (node) (if (not (null? (node-parents node))) (begin (set-node-changed?! node #t) #t) #f)) nodes))) (if (not (null? to-do)) (let ((node (car to-do))) (set-node-changed?! node #f) (if (op node) (let kids-loop ((ts (node-kids node)) (to-do (cdr to-do))) (cond ((null? ts) (loop to-do)) ((node-changed? (car ts)) (kids-loop (cdr ts) to-do)) (else (set-node-changed?! (car ts) #t) (kids-loop (cdr ts) (cons (car ts) to-do))))) (loop (cdr to-do)))))))) ; These are the same as for OR except that we AND together the parents' ; elt-sets instead of using the one provided. (define (and-update-node-with-kill node) (receive (set change?) (integer-set-subtract&ior-with-test! (parents-elt-set node) (node-kill-set node) (node-elt-set node)) (set-node-elt-set! node set) change?)) (define (and-update-node node) (receive (set change?) (integer-set-ior-with-test! (parents-elt-set node) (node-elt-set node)) (set-node-elt-set! node set) change?)) (define (parents-elt-set node) (do ((parents (cdr (node-parents node)) (cdr parents)) (elts (node-elt-set (car (node-parents node))) (integer-set-and elts (node-elt-set (car parents))))) ((null? parents) elts))) ;------------------------------------------------------------ ; Testing ; GRAPH is (( name ; (element*) elements ; (element*) kills ; . )*) children ; '((node1 (elt1 elt2) () node2) (node2 (elt3) (elt2) node1 node3) (node3 () () )) '((a (1) () b) (b () () )) '((a (1 2 3 4) (1) b) (b () (2) c) (c () (3) d) (d (5) (4) a)) (define (test-transitive graph down? or? pass?) (let* ((elts '()) (get-elt (lambda (sym) (cond ((first (lambda (v) (eq? sym (vector-ref v 0))) elts) => identity) (else (let ((new (vector sym #f))) (set! elts (cons new elts)) new))))) (vertices (map (lambda (n) (vector (car n) (map get-elt (cadr n)) (map get-elt (caddr n)) #f #f)) graph))) (for-each (lambda (data vertex) (vector-set! vertex 3 (map (lambda (s) (first (lambda (v) (eq? s (vector-ref v 0))) vertices)) (cdddr data)))) graph vertices) (let ((the-graph ((if down? make-graph-from-successors make-graph-from-predecessors) vertices (lambda (x) (vector-ref x 3)) (lambda (x) (vector-ref x 4)) (lambda (x v) (vector-set! x 4 v))))) (if (every? (lambda (n) (null? (caddr n))) graph) ((if or? transitive-or! transitive-and!) the-graph (lambda (v) (vector-ref v 1)) ; elts (lambda (v x) (vector-set! v 1 x)) ; set-elts! (lambda (e) (vector-ref e 1)) ; elt-hash (lambda (e x) (vector-set! e 1 x))) ; set-elt-hash! ((if or? (if pass? transitive-or-with-pass! transitive-or-with-kill!) (if pass? transitive-and-with-pass! transitive-and-with-kill!)) the-graph (lambda (v) (vector-ref v 1)) ; elts (lambda (v x) (vector-set! v 1 x)) ; set-elts! (lambda (v) (vector-ref v 2)) ; kills (lambda (e) (vector-ref e 1)) ; elt-hash (lambda (e x) (vector-set! e 1 x))))) ; set-elt-hash! (map (lambda (v) (list (vector-ref v 0) (map (lambda (e) (vector-ref e 0)) (vector-ref v 1)))) vertices)))