~ chicken-core (chicken-5) 9247cec3930849e6570a18333739b9733daed2ce


commit 9247cec3930849e6570a18333739b9733daed2ce
Author:     Moritz Heidkamp <moritz@twoticketsplease.de>
AuthorDate: Sun Mar 3 13:28:20 2013 +0100
Commit:     felix <felix@call-with-current-continuation.org>
CommitDate: Fri May 10 10:41:48 2013 +0200

    Reimplement topological-sort with cycle detection.
    
    Signed-off-by: felix <felix@call-with-current-continuation.org>

diff --git a/data-structures.scm b/data-structures.scm
index 56944ec0..54089249 100644
--- a/data-structures.scm
+++ b/data-structures.scm
@@ -707,52 +707,49 @@
 	(sort! (append seq '()) less?)))
 
 
-;;;  Simple topological sort:
-;
-; Taken from SLIB (slightly adapted): Copyright (C) 1995 Mikael Djurfeldt
+;;; Topological sort with cycle detection:
+;;
+;; A functional implementation of the algorithm described in Cormen,
+;; et al. (2009), Introduction to Algorithms (3rd ed.), pp. 612-615.
 
 (define (topological-sort dag pred)
-  (if (null? dag)
-      '()
-      (let* ((adj-table '())
-	     (sorted '()))
-
-	(define (insert x y)
-	  (let loop ([at adj-table])
-	    (cond [(null? at) (set! adj-table (cons (cons x y) adj-table))]
-		  [(pred x (caar at)) (set-cdr! (car at) y)]
-		  [else (loop (cdr at))] ) ) )
-	
-	(define (lookup x)
-	  (let loop ([at adj-table])
-	    (cond [(null? at) #f]
-		  [(pred x (caar at)) (cdar at)]
-		  [else (loop (cdr at))] ) ) )
-	
-	(define (visit u adj-list)
-	  ;; Color vertex u
-	  (insert u 'colored)
-	  ;; Visit uncolored vertices which u connects to
-	  (for-each (lambda (v)
-		      (let ((val (lookup v)))
-			(if (not (eq? val 'colored))
-			    (visit v (or val '())))))
-		    adj-list)
-	  ;; Since all vertices downstream u are visited
-	  ;; by now, we can safely put u on the output list
-	  (set! sorted (cons u sorted)) )
-	
-	;; Hash adjacency lists
-	(for-each (lambda (def) (insert (car def) (cdr def)))
-		  (cdr dag))
-	;; Visit vertices
-	(visit (caar dag) (cdar dag))
-	(for-each (lambda (def)
-		    (let ((val (lookup (car def))))
-		      (if (not (eq? val 'colored))
-			  (visit (car def) (cdr def)))))
-		  (cdr dag)) 
-	sorted) ) )
+  (define (visit dag node edges path state)
+    (case (alist-ref node (car state) pred)
+      ((grey)
+       (##sys#abort
+        (##sys#make-structure
+         'condition
+         '(exn runtime cycle)
+         `((exn . message) "cycle detected"
+           (exn . arguments) ,(list (cons node (reverse path)))
+           (exn . call-chain) ,(##sys#get-call-chain)
+           (exn . location) topological-sort))))
+      ((black)
+       state)
+      (else
+       (let walk ((edges (or edges (alist-ref node dag pred '())))
+                  (state (cons (cons (cons node 'grey) (car state))
+                               (cdr state))))
+         (if (null? edges)
+             (cons (alist-update! node 'black (car state) pred)
+                   (cons node (cdr state)))
+             (let ((edge (car edges)))
+               (walk (cdr edges)
+                     (visit dag
+                            edge
+                            #f
+                            (cons edge path)
+                            state))))))))
+  (let loop ((dag dag)
+             (state (cons (list) (list))))
+    (if (null? dag)
+        (cdr state)
+        (loop (cdr dag)
+              (visit dag
+                     (caar dag)
+                     (cdar dag)
+                     '()
+                     state)))))
 
 
 ;;; Binary search:
diff --git a/manual/Unit data-structures b/manual/Unit data-structures
index 5412ea4d..c64432e1 100644
--- a/manual/Unit data-structures	
+++ b/manual/Unit data-structures	
@@ -282,6 +282,9 @@ Time complexity: O (|V| + |E|)
 (socks undershorts pants shoes watch shirt belt tie jacket)
 </enscript>
 
+If a cycle is detected during the sorting process, an exception of the
+condition kinds {{(exn runtime cycle)}} is thrown.
+
 === Random numbers
 
 
diff --git a/tests/data-structures-tests.scm b/tests/data-structures-tests.scm
index b0f8794b..de3872b6 100644
--- a/tests/data-structures-tests.scm
+++ b/tests/data-structures-tests.scm
@@ -2,6 +2,11 @@
 
 (use data-structures)
 
+(define-syntax assert-error
+  (syntax-rules ()
+    ((_ expr)
+     (assert (handle-exceptions _ #t expr #f)))))
+
 (let ((alist '((foo . 123) ("bar" . "baz"))))
   (alist-update! 'foo 999 alist)
   (assert (= (alist-ref 'foo alist) 999))
@@ -37,3 +42,10 @@
 (assert (> 0 (string-compare3-ci "foo\x00A" "foo\x00b")))
 (assert (< 0 (string-compare3-ci "foo\x00b" "foo\x00a")))
 (assert (< 0 (string-compare3-ci "foo\x00b" "foo\x00A")))
+
+;; topological-sort
+
+(assert (equal? '() (topological-sort '() eq?)))
+(assert (equal? '(a b c d) (topological-sort '((a b) (b c) (c d)) eq?)))
+(assert (equal? '(c d a b) (topological-sort '((a b) (c d)) eq?)))
+(assert-error (topological-sort '((a b) (b a)) eq?))
Trap