;;; -*- Mode: Lisp -*-
;;;
;;; $Header: /home/gene/library/website/docsrc/amo/RCS/binomial-heap2.lisp,v 395.1 2008/04/20 17:25:45 gene Exp $
;;;
;;; Copyright (c) 2005 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
;;;

;;;
;;; This is an implementation of BINOMIAL HEAPs as requested for
;;; Exercise 3.6.  It's like that of package BINOMIAL-HEAP except
;;; that it stores rank more efficiently.
;;;

(defpackage "BINOMIAL-HEAP2"
  (:use "COMMON-LISP" "ORDERED")
  (:import-from "CYBERTIGGYR-TEST" "CHECK" "DEFTEST")
  (:import-from "UTILS" "HEAPSORT")
  (:export "DELETEMIN"
	   "FINDMIN"
	   "HEAP"
	   "INSERT"
	   "INSTREE"
	   "IS-HEAP"
	   "LINK"
	   "RANK"
	   "REMOVEMINTREE"
	   "ROOT"
	   "XMERGE"
	   "XTREE"))
(in-package "BINOMIAL-HEAP2")

(defstruct xtree x (c nil))

(defun xtree-rank (tree)
  "Return the rank of the binomial tree.  Actually compute the
rank, whereas in the BINOMIAL-HEAP package, the rank is stored &
we just return that."
  (cond ((null tree) -1)
	((endp (xtree-c tree)) 0)
	(t (1+ (reduce #'max (mapcar #'xtree-rank (xtree-c tree)))))))

(defstruct heapnode r tree)

;;;
;;; Heap is a list of HEAPNODE.
;;;

(defun is-heap (x)
  "Return true if & only if X is a binomial heap.  A binomial heap
is a list of binomial trees ordered in increasing rank."
  (and
   ;; A binomial heap is a list.
   (check (listp x))
   ;; Every element in the list is a HEAPNODE.
   (check (every #'heapnode-p x))
   ;; The rank of each HEAPNODE is less than the rank of the
   ;; next HEAPNODE (if there is a next).
   (check (every #'identity (maplist
			     #'(lambda (lst)
				 (let ((hn0 (first lst))
				       (hn1 (second lst)))
				   (or (null hn1)
				       (< (heapnode-r hn0)
					  (heapnode-r hn1)))))
			     x)))))

(defun rank (heapnode)
  (declare (type heapnode heapnode))
  (heapnode-r heapnode))

(defun root (tree)
  "Unchanged from the first binomial heap implementation.  Still
operates on a tree."
  (if tree
      (xtree-x tree)
    (error "~A: Can't get root of empty binomial tree" 'root)))

(defun link-trees (tree1 tree2)
  "Unchanged from the first binomial heap implementation.  Still
operates on trees."
  (if (xleq (xtree-x tree1) (xtree-x tree2))
      (make-xtree :x (xtree-x tree1)
		  :c (cons tree2 (xtree-c tree1)))
    ;; else
    (make-xtree :x (xtree-x tree2)
		:c (cons tree1 (xtree-c tree2)))))

(defun link-nodes (hn0 hn1)
  (declare (type (or heapnode null) hn0 hn1))
  (make-heapnode :r (1+ (heapnode-r hn0))
		 :tree (link-trees (heapnode-tree hn0)
				   (heapnode-tree hn1))))

(defun insTree (hn heap)
  "Insert the heapnode HN into the heap."
  (declare (type (or heapnode null) hn))
  (assert (is-heap heap))
  (cond ((null heap) (list hn))
        ((< (rank hn) (rank (first heap))) (cons hn heap))
        (t (insTree (link-nodes hn (first heap)) (rest heap)))))

(defun insert (x heap)
  (assert (is-heap heap))
  (insTree (make-heapnode :r 0
			  :tree (make-xtree :x x))
	   heap))

(defun xmerge (heap1 heap2)
  (declare (type list heap1 heap2))
  ;; (assert (is-heap heap1))
  ;; (assert (is-heap heap2))
  (symbol-macrolet ((hn1 (first heap1)) (r1 (rank hn1))
		    (rest1 (rest heap1))
		    (hn2 (first heap2)) (r2 (rank hn2))
		    (rest2 (rest heap2)))
    (cond ((endp heap2) heap1)
	  ((endp heap1) heap2)
	  ((< r1 r2) (cons hn1 (xmerge rest1 heap2)))
	  ((< r2 r1) (cons hn2 (xmerge heap1 rest2)))
	  (t (insTree (link-nodes hn1 hn2)
		      (xmerge rest1 rest2))))))

(defun removeMinHeapnode (heap)
  ;; These macro symbols are different ways of looking at HEAP.
  ;; The first & third are Lisp versions of Okasaki's Standard
  ;; ML code.  The second is useful because in this
  ;; implementation of binomial heap, a heap is a list of
  ;; heapnodes, & heapnodes contain trees.
  (assert heap)                         ; heap must not be empty
  (assert (is-heap heap))
  (symbol-macrolet ((heapnode (first heap))
		    (tree (heapnode-tree heapnode))
		    (hrest (rest heap)))
    (if (= (length heap) 1)
	(list heapnode nil)
      ;; else
      (destructuring-bind
	  (heapnode0 heap2) (removeMinHeapnode hrest)
	(symbol-macrolet ((tree0 (heapnode-tree heapnode0)))
            (if (xleq (root tree) (root tree0))
		(list heapnode hrest)
	      ;; else
	      (list heapnode0 (cons heapnode heap2))))))))

(defun findMin (heap)
  (destructuring-bind (heapnode heap0) (removeMinHeapnode heap)
    (declare (ignore heap0))
    (root (heapnode-tree heapnode))))

(defun deleteMin (heap)
  (destructuring-bind (heapnode heap2) (removeMinHeapnode heap)
    (xmerge
     (mapcar #'(lambda (tree)
		 (make-heapnode :r (xtree-rank tree)
				:tree tree))
	     (reverse (xtree-c (heapnode-tree heapnode))))
     heap2)))

(deftest test0100 ()
  "Test that INSERT an item into an empty heap returns a heap."
  (check (is-heap (insert 17 nil))))

(deftest test0110 ()
  "Test that findMin on a heap of 1 item returns the item we
INSERTed."
  (check (= (findMin (insert 17 nil)) 17)))

(deftest test0115 ()
  "Test that deleteMin on a heap of 1 item returns NIL."
  (check (null (deleteMin (insert 17 nil)))))

(deftest test0120 ()
  "Test that findMin on a heap of 2 items retrns the lesser
item."
  (check (= (findMin (insert 17 (insert 42 nil))) 17)))

(deftest test0121 ()
  "Test that deleteMin on a heap of 2 items gives us a heap
with one item, & it's the item we expected."
  (check (= (findMin (deleteMin (insert 17 (insert 42 nil)))) 42)))

(deftest test0122 ()
  "Test that deleteMin twice on a heap of 2 items gives us an
empty heap."
  (check (null (deleteMin (deleteMin (insert 17 (insert 42 nil)))))))

(deftest test0130 ()
  "Test findMin on a heap of 3 items."
  (check (= (findMin (insert 101 (insert 17 (insert 42 nil)))) 17)))

(deftest test0131 ()
  "Test findMin on a heap of 4 items."
  (check (= (findMin (insert 3 (insert 101 (insert 17 (insert 42 nil)))))
	    3)))

(deftest test0140 ()
  "Test INSERT, findMin, & deleteMin by using UTILS:HEAPSORT."
  (let* ((lst0 '(1 0 2 9 3 8 4 7 5 6))
	 (lst1 (heapsort lst0 nil #'insert #'findMin #'deleteMin)))
    (setq lst0 (sort lst0 #'<))
    (unless (equal lst0 lst1)
      (format t "~&~A: error" 'test0140)
      (format t " ~A is ~S." 'lst1 lst1)
      (format t " Expected ~S." lst0))
    (equal lst0 lst1)))

(deftest test0150 ()
  "Like TEST0140, but do it on a randomly generated list."
  (let* ((lst0 (loop for i from 1 to 10 collect (random 100)))
	 (lst1 (heapsort lst0 nil #'insert #'findMin #'deleteMin)))
    (setq lst0 (sort lst0 #'<))
    (unless (equal lst0 lst1)
      (format t "~&~A: error" 'test0150)
      (format t " ~A is ~S." 'lst1 lst1)
      (format t " Expected ~S." lst0))
    (equal lst0 lst1)))

(deftest test0155 ()
  "Run TEST0150 a bunch of times."
  (every #'identity (loop for i from 1 to 100 collect (test0150))))
  
;;; --- end of file ---
