;;; -*- Mode: Lisp -*-
;;;
;;; $Header: /home/gene/library/website/docsrc/amo/RCS/red-black-set.lisp,v 395.1 2008/04/20 17:25:45 gene Exp $
;;;
;;; Copyright (c) 2006 Gene Michael Stover.  All rights reserved.
;;;
;;; This program is free software; you can redistribute it and/or modify
;;; it under the terms of the GNU Lesser General Public License as
;;; published by the Free Software Foundation; either version 2 of the
;;; License, or (at your option) any later version.
;;;
;;; This program is distributed in the hope that it will be useful,
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;;; GNU Lesser General Public License for more details.
;;;
;;; You should have received a copy of the GNU Lesser General Public
;;; License along with this program; if not, write to the Free Software
;;; Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
;;; USA
;;;

(defpackage "RED-BLACK-SET"
  (:use "COMMON-LISP")
  (:import-from "CYBERTIGGYR-TEST" "CHECK" "DEFTEST")
  (:export "DEF-RED-BLACK-SET"
	   "RED-BLACK-SET"))

(in-package "RED-BLACK-SET")

;;;
;;; This is an implimentation of red/black trees in Lisp, converted
;;; from the Standard ML implementation in Okasaki's "Purely
;;; Functional Data Structures".
;;;

;;;
;;; I'm going to try something different here compared to how I
;;; implemented heaps from earlier parts of Okasaki's book.
;;; Instead of having one set of functions & relying on some
;;; CLOS functions to do comparators, I'm going to use a macro
;;; to make a red/black set that is customized to a particular
;;; comparison function.
;;; I'm doing it this way because I haven't done it before, &
;;; I'd like to know how well it works.
;;; I predict it will make testing more difficult.
;;;

(defmacro def-red-black-set (name lessp)
  "NAME is the base of the symbols we're about to define, sort of
like the name you supply to defstruct.  LESSP may be a function or
a symbol that is fbound to a function."
  (declare (type symbol name))
  (let ((make-name (intern (format nil "MAKE-~A" name)))
	(name-as-list (intern (format nil "~A-AS-LIST" name)))
	(name-balance (intern (format nil "~A-BALANCE" name)))
	(name-color (intern (format nil "~A-COLOR" name)))
	(name-insert (intern (format nil "~A-INSERT" name)))
	(name-left (intern (format nil "~A-LEFT" name)))
        (name-member (intern (format nil "~A-MEMBER" name)))
	(name-remove-if (intern (format nil "~A-REMOVE-IF" name)))
	(name-right (intern (format nil "~A-RIGHT" name)))
	(name-value (intern (format nil "~A-VALUE" name))))
    `(progn
       (defstruct ,name
	 (color :black)
	 left
	 value
	 right)

       (defun ,name-member (x set)
	 (if set
	     (let ((a (,name-left set))
		   (b (,name-right set))
		   (y (,name-value set)))
	       (cond ((,lessp x y) (,name-member x a))
		     ((,lessp y x) (,name-member x b))
		     (t)))
	   ;; else, set is nil, so X ain't in it.
	   nil))

       (defun ,name-balance (self)
	 (if (eq (,name-color self) :red)
	     ;; When this node is RED, we don't change it at all.
	     self
	   ;; else, Self's color is BLACK, so we balance.
	   (let ((l-is-red (and self
				(,name-left self)
				(eq :red (,name-color (,name-left self)))))
		 (ll-is-red (and self
				 (,name-left self)
				 (,name-left (,name-left self))
				 (eq :red (,name-color (,name-left (,name-left self))))))
		 (lr-is-red (and self
				 (,name-left self)
				 (,name-right (,name-left self))
				 (eq :red (,name-color (,name-right (,name-left self))))))
		 (r-is-red (and self
				(,name-right self)
				(eq :red (,name-color (,name-right self)))))
		 (rl-is-red (and self
				 (,name-right self)
				 (,name-left (,name-right self))
				 (eq :red (,name-color (,name-left (,name-right self))))))
		 (rr-is-red (and self
				 (,name-right self)
				 (,name-right (,name-right self))
				 (eq :red (,name-color (,name-right (,name-right self))))))
		 a b c d x y z)
	     (cond ((and l-is-red ll-is-red)
		    (setq z self)
		    (setq y (,name-left z))
		    (setq x (,name-left y))
		    (setq a (,name-left x))
		    (setq b (,name-right x))
		    (setq c (,name-right c))
		    (setq d (,name-right z))
		    (,make-name
		     :color :red
		     :left (,make-name :color :black :left a
				       :value (,name-value x)
				       :right b)
		     :value (,name-value y)
		     :right (,make-name :color :black :left c
					:value (,name-value z) :right d)))
		   ((and l-is-red lr-is-red)
		    (setq z self)
		    (setq x (,name-left z))
		    (setq y (,name-right x))
		    (setq a (,name-left x))
		    (setq b (,name-left y))
		    (setq c (,name-right y))
		    (setq d (,name-right z))
		    (,make-name
		     :color :red
		     :left (,make-name :color :black :left a
				       :value (,name-value x)
				       :right b)
		     :value (,name-value y)
		     :right (,make-name :color :black :left c
					:value (,name-value z) :right d)))
		   ((and r-is-red rr-is-red)
		    (setq x self)
		    (setq y (,name-right x))
		    (setq z (,name-right y))
		    (setq a (,name-left x))
		    (setq b (,name-left y))
		    (setq c (,name-left z))
		    (setq d (,name-right z))
		    (,make-name
		     :color :red
		     :left (,make-name :color :black :left a
				       :value (,name-value x)
				       :right b)
		     :value (,name-value y)
		     :right (,make-name :color :black :left c
					:value (,name-value z) :right d)))
		   ((and r-is-red rl-is-red)
		    (setq x self)
		    (setq z (,name-right x))
		    (setq y (,name-left z))
		    (setq a (,name-left x))
		    (setq b (,name-left y))
		    (setq c (,name-right y))
		    (setq d (,name-right z))
		    (,make-name
		     :color :red
		     :left (,make-name :color :black :left a
				       :value (,name-value x)
				       :right b)
		     :value (,name-value y)
		     :right (,make-name :color :black :left c
					:value (,name-value z) :right d)))
		   (t self)))))
	      
       (defun ,name-insert (x set)
	 (labels
	     ((ins (set)
		   (cond ((null set) (,make-name :color :red
						 :left nil
						 :value x
						 :right nil))
			 ((,lessp x (,name-value set))
			  (,name-balance
			   (,make-name :color (,name-color set)
				       :left (ins (,name-left set))
				       :value (,name-value set)
				       :right (,name-right set))))
			 ((,lessp (,name-value set) x)
			  (,name-balance
			   (,make-name :color (,name-color set)
				       :left (,name-left set)
				       :value (,name-value set)
				       :right (ins (,name-right set)))))
			 (t set))))
	   (let ((y (ins set)))
	     (,make-name :color :black
			 :left (,name-left y)
			 :value (,name-value y)
			 :right (,name-right y)))))

       (defun ,name-as-list (set)
	 "Do an in-order traversal, collecting the values into a list."
	 (if set
	     (append (,name-as-list (,name-left set))
		     (list (,name-value set))
		     (,name-as-list (,name-right set)))
	   nil))

       (defun ,name-remove-if (p set0)
	 (declare (type (or symbol function) p))
	 ;; fixme: This is about as inefficient as an implementation
	 ;; can be.  We convert SET0 to a list, remove the
	 ;; elements which satisfy the predicate, then create a
	 ;; new Red-Black tree with the remaining elements.
	 (let ((set1 nil))
	   (dolist (x (remove-if p (,name-as-list set0)))
	     (setq set1 (,name-insert x set1)))
	   set1))

       '(,name
	 ,make-name
	 ,name-as-list
	 ,name-balance
	 ,name-insert
	 ,name-left
	 ,name-member
	 ,name-remove-if
	 ,name-right
	 ,name-value))))

;;; --- end of file ---
