--- /dev/null
+;;; -*-lisp-*-
+;;;
+;;; $Id$
+;;;
+;;; Heap data structure; useful for priority queues and suchlike
+;;;
+;;; (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)
+ (:export #:make-heap #:heap-count #:heap-empty-p
+ #:heap-insert #:heap-head #:heap-remove
+ #:heap-sort))
+(in-package #:heap)
+
+;;;--------------------------------------------------------------------------
+;;; Useful indexing functions.
+
+(declaim (inline parent left-child right-child))
+(deftype index () '(and unsigned-byte fixnum))
+(defun parent (i)
+ (declare (type index i))
+ (the index (floor (- i 1) 2)))
+(defun left-child (i)
+ (declare (type index i))
+ (the index (+ (* 2 i) 1)))
+(defun right-child (i)
+ (declare (type index i))
+ (the index (+ (* 2 i) 2)))
+
+;;;--------------------------------------------------------------------------
+;;; Low-level heap operations.
+
+(defun upheap (v key cmp n x)
+ "Insert the element X in the highest place possible in the heap."
+ (declare (type vector v)
+ (type function key cmp)
+ (type index n))
+ (let ((i n) (xk (funcall key x)))
+ (loop (when (zerop i) (return))
+ (let* ((j (parent i))
+ (y (aref v j)))
+ (when (funcall cmp (funcall key y) xk) (return))
+ (setf (aref v i) y
+ i j)))
+ (setf (aref v i) x)))
+
+(defun downheap (v key cmp n x)
+ "Insert the element X in the lowest place possible in the heap."
+ (declare (type vector v)
+ (type function key cmp)
+ (type index n))
+ (let ((i 0) (xk (funcall key x)))
+ (loop (let ((l (left-child i))
+ (r (right-child i)))
+ (when (>= l n) (return))
+ (multiple-value-bind
+ (j y yk)
+ (let* ((y (aref v l))
+ (yk (funcall key y)))
+ (if (= r n)
+ (values l y yk)
+ (let* ((z (aref v r))
+ (zk (funcall key z)))
+ (if (funcall cmp yk zk)
+ (values l y yk)
+ (values r z zk)))))
+ (when (funcall cmp xk yk)
+ (return))
+ (setf (aref v i) y
+ i j))))
+ (setf (aref v i) x)))
+
+(defun check (v key cmp n)
+ "Verify the heap invariant on the heap."
+ (declare (type vector v)
+ (type function key cmp)
+ (type index n))
+ (dotimes (i n)
+ (let* ((item (aref v i))
+ (item-key (funcall key item))
+ (l (left-child i))
+ (r (right-child i)))
+ (when (< l n)
+ (let ((left-item (aref v l)))
+ (assert (funcall cmp item-key (funcall key left-item))))
+ (when (< r n)
+ (let ((right-item (aref v r)))
+ (assert (funcall cmp item-key (funcall key right-item)))))))))
+
+;;;--------------------------------------------------------------------------
+;;; High-level heap things
+
+(defstruct (heap (:predicate heapp) (:constructor %make-heap))
+ "Data structure for a heap."
+ (v (make-array 16) :type vector)
+ (n 0 :type index)
+ (key #'identity :type function)
+ (compare #'<= :type function))
+
+(defun make-heap
+ (&key (compare #'<=) (key #'identity)
+ (type 't) (init-size 16) (contents nil contentsp))
+ "Return a new heap.
+
+ COMPARE is a partial-order predicate: (COMPARE X Y) should return true if
+ X <= Y in some order.
+
+ The TYPE is the element type of the heap.
+
+ INIT-SIZE is the initial allocation for the heap; the heap will grow
+ automatically if necessary, so this isn't a big deal. This is only a
+ hint; make-heap may ignore it completely.
+
+ KEY is a function to extract the key from an element. The default is to
+ use the item unmolested.
+
+ CONTENTS is the initial contents of the heap. If omitted, the heap is
+ initially empty."
+ (let ((n (if contentsp (length contents) 0)))
+ (loop while (< init-size n)
+ do (setf init-size (ash init-size 1)))
+ (let ((v (make-array init-size :element-type type)))
+ (when contentsp
+ (reduce (lambda (i item)
+ (upheap v key compare i item)
+ (1+ i))
+ contents
+ :initial-value 0))
+ (%make-heap :compare compare :key key :n n :v v))))
+
+(defun heap-count (heap)
+ "Return the number of elements in HEAP."
+ (declare (type heap heap))
+ (heap-n heap))
+
+(defun heap-empty-p (heap)
+ "True if HEAP is empty."
+ (declare (type heap heap))
+ (zerop (heap-count heap)))
+
+(defun heap-insert (heap item)
+ "Insert ITEM into the HEAP."
+ (declare (type heap heap))
+ (let* ((v (heap-v heap))
+ (n (heap-n heap))
+ (sz (array-dimension v 0)))
+ (when (= n sz)
+ (setf v (adjust-array v (* 2 n))
+ (heap-v heap) v))
+ (upheap v (heap-key heap) (heap-compare heap) n item)
+ (setf (heap-n heap) (1+ n))))
+
+(defun heap-head (heap)
+ "Peep at the head item on HEAP."
+ (declare (type heap heap))
+ (assert (not (heap-empty-p heap)))
+ (aref (heap-v heap) 0))
+
+(defun heap-remove (heap)
+ "Remove the head item from HEAP and return it."
+ (declare (type heap heap))
+ (assert (not (heap-empty-p heap)))
+ (let ((v (heap-v heap))
+ (n (1- (heap-n heap))))
+ (prog1 (aref v 0)
+ (setf (heap-n heap) n)
+ (downheap v (heap-key heap) (heap-compare heap) n (aref v n)))))
+
+(defun heap-sort (items compare &key (key #'identity))
+ "Return the ITEMS, least-first, as sorted by the ordering COMPARE."
+ (let ((heap (make-heap :compare compare :contents items :key key)))
+ (loop repeat (heap-n heap)
+ collect (heap-remove heap))))
+
+;;;----- That's all, folks --------------------------------------------------