123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404 |
- ; Part of Scheme 48 1.9. See file COPYING for notices and license.
- ; Authors: Richard Kelsey
- ; 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 ((<symbol> name
- ; (element*) elements
- ; (element*) kills
- ; . <symbol*>)*) 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)))
|