+;;; -*-lisp-*-
+;;;
+;;; $Id$
+;;;
+;;; Simple binary heaps, for priority queues
+;;;
+;;; (c) 2006 Straylight/Edgeware
+;;;
+
+;;;----- Licensing notice ---------------------------------------------------
+;;;
+;;; This program is free software; you can redistribute it and/or modify
+;;; it under the terms of the GNU 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 General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with this program; if not, write to the Free Software Foundation,
+;;; Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+(defpackage #:heap
+ (:use #:common-lisp #:mdw.base)
+ (:export #:heap #:heapp
+ #:heap-key< #:heap-key
+ #:heap-check
+ #:heap-count #:heap-empty-p
+ #:heap-error #:heap-underflow-error
+ #:heap-add #:heap-min #:heap-remove))
+(in-package #:heap)
+
+(defstruct (heap
+ (:predicate heapp)
+ (:constructor make-heap
+ (key<-name
+ &optional
+ (key-name 'identity)
+ (max 32)
+ &aux
+ (key< (functionify key<-name))
+ (key (functionify key-name))
+ (store (make-array max :adjustable t)))))
+ "Structure representing a heap."
+ (key< (uninitialized-slot) :read-only t :type (function (t t) t))
+ (key #'identity :read-only t :type (function (t) t))
+ (store (uninitialized-slot) :type vector)
+ (size 0 :type unsigned-fixnum))
+
+(defmacro with-heap
+ ((heap &key (key< 'key<) (key 'key) (store 'store) (size 'size))
+ &body body)
+ "Pull aspects of the HEAP into local variables and functions so that they
+ can be messed with conveniently."
+ (let*/gensyms (heap)
+ (with-gensyms (cmp getkey x y)
+ `(let ((,getkey (heap-key ,heap))
+ (,cmp (heap-key< ,heap))
+ (,store (heap-store ,heap))
+ (,size (heap-size ,heap)))
+ (declare (ignorable ,size)
+ (type (function (t t) t) ,cmp)
+ (type (function (t) t) ,getkey))
+ (flet ((,key< (,x ,y)
+ (funcall ,cmp ,x ,y))
+ (,key (,x)
+ (funcall ,getkey ,x)))
+ (declare (inline ,key< ,key))
+ ,@body)))))
+
+(declaim (inline heap-left heap-right heap-parent))
+(defun heap-left (i)
+ "Compute the left child index of the item with index I."
+ (declare (type unsigned-fixnum i))
+ (the unsigned-fixnum (+ (* 2 i) 1)))
+(defun heap-right (i)
+ "Compute the right child index of the item with index I."
+ (declare (type unsigned-fixnum i))
+ (the unsigned-fixnum (+ (* 2 i) 2)))
+(defun heap-parent (i)
+ "Compute the parent index of the item with index I. Do not use this on
+ index zero."
+ (declare (type unsigned-fixnum i))
+ (the unsigned-fixnum (ash (1- i) -1)))
+
+(defun heap-check (heap)
+ "Verifies the heap property on the heap HEAP."
+ (declare (type heap heap))
+ (with-heap (heap)
+ (labels ((check (i key)
+ (let ((left (heap-left i))
+ (right (heap-right i)))
+ (when (< left size)
+ (let ((lkey (key (aref store left))))
+ (assert (not (key< lkey key)))
+ (check left lkey))
+ (when (< right size)
+ (let ((rkey (key (aref store right))))
+ (assert (not (key< rkey key)))
+ (check right rkey)))))))
+ (when (plusp size)
+ (check 0 (key (aref store 0)))))))
+
+(defun heap-count (heap)
+ "Returns a count of the number of items in the HEAP."
+ (declare (type heap heap))
+ (heap-size heap))
+
+(defun heap-empty-p (heap)
+ "Returns true if the HEAP is currently empty, or nil if it contains at
+ least one item. Note that heap-min and heap-remove are invalid operations
+ on an empty heap."
+ (declare (type heap heap))
+ (zerop (heap-size heap)))
+
+(define-condition heap-error (error)
+ ((heap :initarg :heap :type heap :reader heap-error-heap))
+ (:documentation
+ "Parent class for error conditions affecting a heap. The accessor
+ heap-error-heap will extract the offending heap."))
+
+(define-condition heap-underflow-error (heap-error)
+ ()
+ (:report "Heap underflow.")
+ (:documentation
+ "Reports a heap underflow: i.e., an attempt to do something which requires
+ a nonempty heap to an empty one."))
+
+(defun upheap (heap i item)
+ "Fixes up the HEAP after a (virtual) attempt to add the new ITEM at index
+ I."
+ (declare (type heap heap))
+ (with-heap (heap)
+ (let ((key (key item)))
+ (loop (cond ((zerop i)
+ (return))
+ (t
+ (let* ((parent (heap-parent i))
+ (pitem (aref store parent)))
+ (when (key< (key pitem) key)
+ (return))
+ (setf (aref store i) pitem
+ i parent)))))
+ (setf (aref store i) item))))
+
+(defun downheap (heap item)
+ "Fixes up the HEAP after a removal of the head element. The ITEM is the
+ element at the end of the heap. Does something very bad if the heap is
+ empty."
+ (declare (type heap heap))
+ (with-heap (heap)
+ (let* ((i 0)
+ (key (key item)))
+ (loop (let ((left (heap-left i))
+ (right (heap-right i)))
+ (cond ((>= left size)
+ (return))
+ (t
+ (multiple-value-bind
+ (child citem ckey)
+ (let* ((litem (aref store left))
+ (lkey (key litem)))
+ (if (>= right size)
+ (values left litem lkey)
+ (let* ((ritem (aref store right))
+ (rkey (key ritem)))
+ (if (key< lkey rkey)
+ (values left litem lkey)
+ (values right ritem rkey)))))
+ (when (key< key ckey)
+ (return))
+ (setf (aref store i) citem
+ i child))))))
+ (setf (aref store i) item))))
+
+(defun heap-add (heap item)
+ "Add the given ITEM to the HEAP."
+ (declare (type heap heap))
+ (let ((i (heap-size heap))
+ (store (heap-store heap)))
+ (when (>= i (array-dimension store 0))
+ (setf (heap-store heap) (adjust-array store (ash i 1))))
+ (setf (heap-size heap) (1+ i))
+ (upheap heap i item)))
+
+(defun heap-min (heap)
+ "Returns the smallest item in the HEAP."
+ (declare (type heap heap))
+ (when (zerop (heap-size heap))
+ (error 'heap-underflow-error :heap heap))
+ (aref (heap-store heap) 0))
+
+(defun heap-remove (heap)
+ "Removes the smallest item in the HEAP, returning something which isn't
+ useful."
+ (declare (type heap heap))
+ (let ((i (1- (heap-size heap)))
+ (store (heap-store heap)))
+ (cond ((minusp i)
+ (error 'heap-underflow-error :heap heap))
+ ((zerop i)
+ (setf (heap-size heap) 0
+ (aref store 0) nil))
+ (t
+ (let ((item (aref store i)))
+ (setf (aref store i) nil
+ (heap-size heap) i)
+ (downheap heap item))))))
+
+(defun test ()
+ (let ((heap (make-heap #'<))
+ (list (cons nil nil)))
+ (flet ((add ()
+ (let ((n (random 4096)))
+ (heap-add heap n)
+ (loop for c on list
+ when (or (null (cdr c))
+ (< n (cadr c)))
+ do (setf (cdr c) (cons n (cdr c))) (return))))
+ (remove ()
+ ;;(show (values (cdr list) heap))
+ (cond ((cdr list)
+ (let ((n (heap-min heap)))
+ (assert (= n (pop (cdr list))))
+ (heap-remove heap)))
+ (t
+ (handler-case
+ (progn (heap-remove heap) (error "Bummer"))
+ (heap-underflow-error () nil))))))
+ (dotimes (i 1024)
+ (add))
+ (dotimes (i 65536)
+ (case (random 2)
+ (0 (add))
+ (1 (remove))))
+ (loop for i in (cdr list)
+ for j = (heap-min heap)
+ do (assert (= i j)) (heap-remove heap))
+ (assert (heap-empty-p heap)))))
+
+;;;----- That's all, folks --------------------------------------------------