-
Notifications
You must be signed in to change notification settings - Fork 14
/
bheap.lisp
233 lines (179 loc) · 6.92 KB
/
bheap.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
;; -*- lisp -*-
;; This file is part of STMX.
;; Copyright (c) 2013-2016 Massimiliano Ghilardi
;;
;; This library is free software: you can redistribute it and/or
;; modify it under the terms of the Lisp Lesser General Public License
;; (http://opensource.franz.com/preamble.html), known as the LLGPL.
;;
;; This library 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 Lisp Lesser General Public License for more details.
(in-package :stmx.util)
;;;; ** Priority queue implemented with a binary min-heap.
(defvar *empty-vector* (make-array '(0)))
(defclass bheap ()
((vector :initarg :vector :initform *empty-vector*
:type vector :accessor vector-of)
(length :initform 0
:type fixnum :accessor length-of)
(key :initarg :key :initform #'identity
:type function :reader key-of)
(pred :initarg :pred :initform (error "missing :pred argument instantiating ~A or a subclass" 'bheap)
:type function :reader pred-of))
(:documentation "Priority queue implemented with a binary min-heap.
Elements that compare smaller will be the first (top) in the heap."))
;;;; ** bheap private functions
(defun compare-bheap-entries (q n1 n2)
"Compare entries at positions N1 and N2 in bqueue Q."
(declare (type bheap q)
(type fixnum n1 n2))
(with-ro-slots (vector key pred) q
(let ((key1 (funcall key (aref vector n1)))
(key2 (funcall key (aref vector n2))))
;; swap key1 and key2: if pred is #'<
;; we want smaller elements to come first
(funcall pred key2 key1))))
(defun sift-down-bheap (q start end)
(declare (type bheap q)
(type fixnum start end))
(let ((root start)
(vector (vector-of q)))
(declare (type fixnum root))
(loop for lchild = (the fixnum (1+ (* 2 root)))
for swap = root
while (<= lchild end) do
(when (compare-bheap-entries q swap lchild)
(setf swap lchild))
(let1 rchild (the fixnum (1+ lchild))
(when (and (<= rchild end)
(compare-bheap-entries q swap rchild))
(setf swap rchild)))
(when (= swap root)
(return))
(log:debug "vector = ~A, swapping index ~A with ~A" vector root swap)
(rotatef (aref vector root) (aref vector swap))
(setf root swap))
(log:debug "vector = ~A, done with start index = ~A" vector start)))
(defun sift-up-bheap (q start end)
(declare (type bheap q)
(type fixnum start end))
(let ((vector (vector-of q))
(child end))
(declare (type fixnum child))
(loop while (< start child)
do
(let ((parent (the fixnum (floor (1- child) 2))))
(unless (compare-bheap-entries q parent child)
(return))
(log:debug "vector = ~A, swapping index ~A with ~A" vector parent child)
(rotatef (aref vector parent) (aref vector child))
(setf child parent)))
(log:debug "vector = ~A, done with start index = ~A" vector start)))
(defun heapify-bheap (q)
"Establish heap invariant in bheap Q. Return Q.
Destructively modifies (vector-of Q)."
(declare (type bheap q))
(with-ro-slots (length) q
(loop for start = (the fixnum (1- (floor length 2))) ;; index of last parent
#||# then (the fixnum (1- start))
while (>= start 0) do
(sift-down-bheap q start (1- length)))
q))
(defun extend-bheap-vector (v)
"Double the length of vector V, i.e. create a new larger vector
and copy elements from V to the new vector.
Return the new, larger vector.
This method exists to simplify the implementation of transactional
priority queue TQUEUE: as long as bheap is concerned,
\(vector-push-extend ...) would be fine."
(let* ((n (length v))
(vcopy (make-array (list (* 2 (1+ n)))
:element-type (array-element-type v))))
(dotimes (i n)
(setf (aref vcopy i) (aref v i)))
vcopy))
;;;; ** bheap public functions
(defmethod initialize-instance :after ((q bheap) &key &allow-other-keys)
"Initialize bheap Q."
(setf (length-of q) (length (vector-of q)))
(heapify-bheap q))
(defun empty-bheap? (q)
(declare (type bheap q))
"Return t if bheap Q is empty."
(zerop (length-of q)))
(defun clear-bheap (q)
"Remove all values from bheap Q. Return Q."
(declare (type bheap q))
(setf (length-of q) 0)
q)
(defun get-bheap (q &optional default)
"Return the first value in bheap Q without removing it, and t as multiple values.
Return (values DEFAULT nil) if Q contains no values."
(declare (type bheap q))
(if (empty-bheap? q)
(values default nil)
(values (aref (vector-of q) 0) t)))
(defun rem-bheap (q &optional default)
"If bheap Q contains at least one value, remove the first value
and return it and t as multiple values.
Otherwise return (values DEFAULT nil)"
(declare (type bheap q))
(with-rw-slots (vector length) q
(if (zerop (the fixnum length))
(values default nil)
(let1 value (aref vector 0)
(setf (aref vector 0) (aref vector (decf length)))
(sift-down-bheap q 0 (1- length))
(values value t)))))
(defun add-bheap (q value)
"Add VALUE to bheap Q. Return VALUE."
(declare (type bheap q))
(with-rw-slots (vector length) q
(declare (type fixnum length))
(when (= length (length vector))
(setf vector (extend-bheap-vector vector)))
(setf (aref vector length) value)
(sift-up-bheap q 0 length)
(incf length)
value))
;;;; ** Printing
(defprint-object (q bheap)
(with-ro-slots (vector length) q
(declare (type vector vector)
(type fixnum length))
(format t "#(")
(loop for i from 0 to (1- length) do
(when (= i 100)
(format t " ...")
(return))
(format t "~A~S" (if (zerop i) "" " ") (aref vector i)))
(format t ")")))
;;;; ** Public methods
(defmethod empty? ((q bheap))
"Return t if bheap Q is empty."
(empty-bheap? q))
(defmethod empty! ((q bheap))
"Remove all values from bheap Q. Return Q."
(clear-bheap q))
(defmethod full? ((q bheap))
"A bheap is never full, so this method always returns nil."
nil)
(defmethod peek ((q bheap) &optional default)
"Return the first value in bheap Q without removing it, and t as multiple values.
Return (values DEFAULT nil) if Q contains no values."
(get-bheap q default))
(defmethod try-take ((q bheap))
"If bheap S contains at least one value, remove the first value
and return t and the first value as multiple values.
Otherwise return (values nil nil)"
(multiple-value-bind (value present?) (rem-bheap q)
(values present? value)))
(defmethod put ((q bheap) value)
"Store VALUE in bheap Q. Return VALUE."
(add-bheap q value))
(defmethod try-put ((q bheap) value)
"Store VALUE in bheap Q. Return t and VALUE
This method never fails."
(values t (add-bheap q value)))