(define (make-graph-walker make-queue
			   add-to-queue
			   remove-from-queue)
  (lambda (node node-filter)
    (let ((queue (make-queue)))

      (define (per-node node)
	(for-each
	 (lambda (link)
	   (add-to-queue link
			 (link-priority link)
			 queue))
	 (node-links node))
	(maybe-accumulate node next-node))

      (define (next-node)
	(let ((link (remove-from-queue queue)))
	  (if link
	      (let ((node
		     (dereference-link
		      link
		      node-filter)))
		(if node
		    (per-node node)
		    (next-node)))
	      #f)))

      (per-node node))))

(define (make-fifo-queue)
  (list 'fifo))

(define (add-to-fifo-queue item priority queue)
  (set-cdr! queue (cons item (cdr queue))))

(define (remove-from-fifo-queue queue)
  (and (pair? (cdr queue))
       (let ((item (cadr queue)))
	 (set-cdr! queue (cddr queue))
	 item)))

(define depth-first-walk
  (make-graph-walker make-fifo-queue
		     add-to-fifo-queue
		     remove-from-fifo-queue))

(define (depth-first-search node)
  (depth-first-walk
   node
   (loop-avoidance-node-filter)))

(define (make-lifo-queue)
  (cons '() '()))

(define (add-to-lifo-queue item priority queue)
  (let ((tail (cdr queue))
	(new-tail (cons item '())))
    (if (pair? tail)
	(set-cdr! tail new-tail)
	(set-car! queue new-tail))
    (set-cdr! queue new-tail)))

(define (remove-from-lifo-queue queue)
  (let ((head (car queue)))
    (and (pair? head)
	 (begin
	   (set-car! queue (cdr head))
	   (if (not (pair? (cdr head)))
	       (set-cdr! queue '()))
	   (car head)))))

(define breadth-first-walk
  (make-graph-walker make-lifo-queue
		     add-to-lifo-queue
		     remove-from-lifo-queue))

(define (breadth-first-search node)
  (breadth-first-walk
   node
   (loop-avoidance-node-filter)))

(define (make-priority-queue)
  (list 'priority-queue))

(define (add-to-priority-queue elt priority queue)
  (set-cdr!
   queue
   (let loop ((ps (cdr queue)))
     (if (pair? ps)
	 (if (> priority (caar ps))
	     (cons (cons priority elt) ps)
	     (cons (car ps) (loop (cdr ps))))
	 (list (cons priority elt))))))

(define (remove-from-priority-queue queue)
  (let ((ps (cdr queue)))
    (and (pair? ps)
	 (begin
	   (set-cdr! queue (cdr ps))
	   (cdar ps)))))

(define best-first-walk
  (make-graph-walker make-priority-queue
		     add-to-priority-queue
		     remove-from-priority-queue))

(define (best-first-search node)
  (best-first-walk
   node
   (loop-avoidance-node-filter)))
