#lang racket/base
(require "environment.rkt")
(struct queue-cell (next
previous
priority
item)
#:mutable)
(struct queue (variable-n
first-cell
last-cell
type)
#:mutable)
(define (make-queue (type '#:fifo))
(queue (make-variable 0) '() '() type))
(define (queue-n queue)
(variable-value (queue-variable-n queue)))
(define (set-queue-n! queue n)
(set-variable-value! (queue-variable-n queue) n))
(define (queue-empty? queue)
(= (queue-n queue) 0))
(define (queue-first queue)
(when (queue-empty? queue)
(error 'queue-first
"queue is empty"))
(queue-cell-item (queue-first-cell queue)))
(define (queue-last queue)
(when (queue-empty? queue)
(error 'queue-last
"queue is empty"))
(queue-cell-item (queue-last-cell queue)))
(define (queue-for-each-cell queue proc)
(let loop ((cell (queue-first-cell queue)))
(if (not (null? cell))
(let ((next (queue-cell-next cell)))
(proc cell)
(loop next))
(void))))
(define (queue-for-each queue proc)
(let loop ((cell (queue-first-cell queue)))
(if (not (null? cell))
(let ((next (queue-cell-next cell))
(item (queue-cell-item cell)))
(proc item)
(loop next))
(void))))
(define (queue-find-cell queue item)
(let/ec exit
(queue-for-each-cell queue
(lambda (cell)
(when (eq? (queue-cell-item cell) item)
(exit cell))))
#f))
(define (queue-insert-cell-first! queue cell)
(set-queue-n! queue (+ (queue-n queue) 1))
(set-queue-cell-next! cell (queue-first-cell queue))
(set-queue-first-cell! queue cell)
(set-queue-cell-previous! cell '())
(if (null? (queue-cell-next cell))
(set-queue-last-cell! queue cell)
(set-queue-cell-previous! (queue-cell-next cell) cell)))
(define (queue-insert-first! queue item)
(queue-insert-cell-first! queue (queue-cell '() '() #f item)))
(define (queue-insert-cell-last! queue cell)
(set-queue-n! queue (+ (queue-n queue) 1))
(set-queue-cell-previous! cell (queue-last-cell queue))
(set-queue-last-cell! queue cell)
(set-queue-cell-next! cell '())
(if (null? (queue-cell-previous cell))
(set-queue-first-cell! queue cell)
(set-queue-cell-next! (queue-cell-previous cell) cell)))
(define (queue-insert-last! queue item)
(queue-insert-cell-last! queue (queue-cell '() '() #f item)))
(define (queue-insert-cell-priority! queue cell)
(set-queue-n! queue (+ (queue-n queue) 1))
(let ((cells (queue-first-cell queue)))
(let loop ()
(when (and (not (null? cells))
(<= (queue-cell-priority cell)
(queue-cell-priority (car cells))))
(set! cells (cdr cells))
(loop)))
(set-queue-cell-next! cell (car cells))
(set-queue-cell-previous! cell (if cells
(queue-cell-previous (car cells))
(queue-last-cell queue)))
(when (null? (queue-cell-next cell))
(set-queue-last-cell! queue cell))
(when (null? (queue-cell-previous cell))
(set-queue-first-cell! queue cell))))
(define (queue-insert-priority! queue item priority)
(queue-insert-cell-priority! queue (queue-cell '() '() priority item)))
(define (queue-remove-cell! queue cell)
(set-queue-n! queue (- (queue-n queue) 1))
(if (null? (queue-cell-previous cell))
(set-queue-first-cell! queue (queue-cell-next cell))
(set-queue-cell-next! (queue-cell-previous cell)
(queue-cell-next cell)))
(if (null? (queue-cell-next cell))
(set-queue-last-cell! queue (queue-cell-previous cell))
(set-queue-cell-previous! (queue-cell-next cell)
(queue-cell-previous cell)))
(set-queue-cell-next! cell '())
(set-queue-cell-previous! cell '()))
(define (queue-remove-item! queue item)
(let ((cell (queue-find-cell queue item)))
(queue-remove-cell! queue cell)
cell))
(define queue-remove-first-cell!
(case-lambda
((queue)
(when (queue-empty? queue)
(error 'queue-remove-first-cell!
"queue is empty"))
(let ((cell (queue-first-cell queue)))
(queue-remove-cell! queue cell)
cell))
((queue error-thunk)
(if (queue-empty? queue)
((error-thunk))
(queue-remove-first-cell! queue)))))
(define queue-remove-first!
(case-lambda
((queue)
(when (queue-empty? queue)
(error 'queue-remove-first!
"queue is empty"))
(queue-cell-item (queue-remove-first-cell! queue)))
((queue error-thunk)
(if (queue-empty? queue)
((error-thunk))
(queue-remove-first! queue)))))
(define queue-remove-last-cell!
(case-lambda
((queue)
(when (queue-empty? queue)
(error 'queue-remove-last-cell!
"queue is empty"))
(let ((cell (queue-last-cell queue)))
(queue-remove-cell! queue cell)
cell))
((queue error-thunk)
(if (queue-empty? queue)
((error-thunk))
(queue-remove-last-cell! queue)))))
(define queue-remove-last!
(case-lambda
((queue)
(when (queue-empty? queue)
(error 'queue-remove-last!
"queue is empty"))
(queue-cell-item (queue-remove-last-cell! queue)))
((queue error-thunk)
(if (queue-empty? queue)
((error-thunk))
(queue-remove-last! queue)))))
(define (queue-insert! queue item (priority 100))
(case (queue-type queue)
((#:fifo)
(queue-insert-last! queue item))
((#:lifo)
(queue-insert-first! queue item))
((#:priority)
(queue-insert-priority! queue item priority))
(else
(error 'queue-insert! "unknown queue type ~a" (queue-type queue)))))
(define queue-remove!
(case-lambda
((queue item)
(queue-remove-item! queue item))
((queue)
(queue-remove-first! queue))))
(provide (all-defined-out))