(define vowel-or-consonant
  (lambda (letter)
    (cond ((or (eq? letter 'a)
	       (eq? letter 'e)
	       (eq? letter 'i)
	       (eq? letter 'o)
	       (eq? letter 'u))
	   'vowel)
	  (else 'consonant))))

(define vowel-or-consonant
  (lambda (letter)
    (case letter
      ((a e i o u) 'vowel)
      (else 'consonant))))

(define box-maker
  (lambda (init-value)
    (let ((contents init-value))
      (lambda (msg . args)
	(case msg
	  ((type) 'box)
	  ((show) contents)
	  ((update!)
	   (set! contents (car args)))
	  ((swap!)
	   (let ((ans contents))
	     (set! contents (car args))
	     ans))
	  ((reset!)
	   (set! contents init-value))
	  (else 
	   (delegate base-object msg args)))))))

(define base-object
  (lambda (msg . args)
    (case msg
      ((type) 'base-object)
      (else
       (error "base-object: invalid method.")))))

(define send
  (lambda (obj msg . args)
    (apply obj msg args)))

(define delegate
  (lambda (obj msg args)
    (apply obj msg args)))

(define counter-maker
  (lambda (init-value increment)
    (let ((box (box-maker init-value)))
      (lambda (msg . args)
	(case msg
	  ((type) 'counter)
	  ((update!)
	   (send box 'update! (increment (send box 'show))))
	  (else
	   (delegate box msg args)))))))

(define gauge-maker
  (lambda (init-value increment decrement)
    (let ((box (box-maker init-value)))
      (lambda (msg . args)
	(case msg
	  ((type) 'gauge)
	  ((up!)
	   (send box 'update! (increment (send box 'show))))
	  ((down!)
	   (send box 'update! (decrement (send box 'show))))
	  (else 
	   (delegate box msg args)))))))

(define circular-list-maker
  (lambda ()
    (let ((marker '())
	  (gauge (gauge-maker 0 add1 sub1)))
      (lambda (msg . args)
	(case msg
	  ((type) 'circular-list)
	  ((empty?)
	   (null? marker))
	  ((insert!)
	   (send gauge 'up!)
	   (if (null? marker)
	       (begin
		 (set! marker (cons (car args) '()))
		 (set-cdr! marker marker))
	       (set-cdr! marker (cons (car args) (cdr marker)))))
	  ((head)
	   (if (null? marker)
	       (error "head: the circular list is empty.")
	       (cadr marker)))
	  ((delete!)
	   (if (null? marker)
	       (error "delete!: the circular list is empty.")
	       (begin (send gauge 'down!)
		      (if (eq? marker (cdr marker))
			  (set! marker '())
			  (set-cdr! marker (cddr marker))))))
	  ((rotate!)
	   (if (null? marker)
	       (error "rotate!: the circular list is empty.")
	       (set! marker (cdr marker))))
	  ((size)
	   (send gauge 'show))
	  ((print) 
	   (if (not (null? marker))
	       (let ((next (cdr marker)))
		 (set-cdr! marker '())
		 (for-each
		  (lambda (x) (display x) (display " "))
		  next)
		 (set-cdr! marker next)))
	   (newline))
	  (else 
	   (delegate base-object msg args)))))))

(define stack-maker
  (lambda ()
    (let ((c (circular-list-maker)))
      (lambda (msg . args)
	(case msg
	  ((type) 'stack)
	  ((push!)
	   (send c 'insert! (car args)))
	  ((pop!)
	   (send c 'delete!))
	  ((top)
	   (send c 'head))
	  ((print)
	   (display "TOP: ") 
	   (send c 'print))
	  ((insert! head delete! rotate!)
	   (delegate base-object msg args))
	  (else 
	   (delegate c msg args)))))))

(define queue-maker
  (lambda ()
    (let ((c (circular-list-maker)))
      (lambda (msg . args)
	(case msg
	  ((type) 'queue)
	  ((enqueue!)
	   (send c 'insert! (car args))
	   (send c 'rotate!))
	  ((dequeue!)
	   (send c 'delete!))
	  ((front)
	   (send c 'head))
	  ((print)
	   (display "FRONT: ")
	   (send c 'print))
	 ((insert! head delete! rotate!)
	   (delegate base-object msg args))
	  (else
	   (delegate c msg args)))))))