pqueue 1.0.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.ruby ADDED
@@ -0,0 +1,58 @@
1
+ ---
2
+ source:
3
+ - meta
4
+ authors:
5
+ - name: K. Kodama
6
+ - name: Ronald Butler
7
+ - name: Olivier Renaud
8
+ - name: Rick Bradley
9
+ - name: Thomas Sawyer
10
+ email: transfire@gmail.com
11
+ copyrights:
12
+ - holder: K. Kodama
13
+ year: '2001'
14
+ replacements: []
15
+ alternatives: []
16
+ requirements:
17
+ - name: detroit
18
+ groups:
19
+ - build
20
+ development: true
21
+ - name: microtest
22
+ groups:
23
+ - test
24
+ development: true
25
+ - name: ae
26
+ groups:
27
+ - test
28
+ development: true
29
+ dependencies: []
30
+ conflicts: []
31
+ repositories:
32
+ - uri: git://github.com/rubyworks/pqueue.git
33
+ scm: git
34
+ name: upstream
35
+ resources:
36
+ home: http://rubyworks.github.com/pqueue
37
+ code: http://github.com/rubyworks/pqueue
38
+ mail: http://groups.google.com/group/rubyworks-mailinglist
39
+ bugs: http://github.com/rubyworks/pqueue/issues
40
+ extra: {}
41
+ load_path:
42
+ - lib
43
+ revision: 0
44
+ created: '2001-03-10'
45
+ summary: Queue of Prioritized Elements
46
+ title: PQueue
47
+ version: 2.0.0
48
+ name: pqueue
49
+ description: ! 'A priority queue is like a standard queue, except that each inserted
50
+ elements
51
+
52
+ is given a certain priority, based on the result of the comparison block given
53
+
54
+ at instantiation time. Retrieving an element from the queue will always return
55
+
56
+ the one with the highest priority.'
57
+ organization: rubyworks
58
+ date: '2011-10-29'
@@ -0,0 +1,31 @@
1
+ = COPYRIGHT NOTICES
2
+
3
+ == PQueue
4
+
5
+ Copyright:: (c) 2011 Rubyworks
6
+ License:: BSD-2-Clause
7
+ Website:: http://rubyworks.github.com/pqueue
8
+
9
+ Copyright 2011 Rubyworks. All rights reserved.
10
+
11
+ Redistribution and use in source and binary forms, with or without
12
+ modification, are permitted provided that the following conditions are met:
13
+
14
+ 1. Redistributions of source code must retain the above copyright notice,
15
+ this list of conditions and the following disclaimer.
16
+
17
+ 2. Redistributions in binary form must reproduce the above copyright
18
+ notice, this list of conditions and the following disclaimer in the
19
+ documentation and/or other materials provided with the distribution.
20
+
21
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
22
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
23
+ AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24
+ COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
28
+ OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
30
+ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
+
@@ -0,0 +1,28 @@
1
+ = CHANGE HISTORY
2
+
3
+ == 2.0.0 / 2011-10-29
4
+
5
+ This is a complete rewrite to simplify the design and use more
6
+ of Ruby's internal methods. Overall performance should be markedly
7
+ improved. A few method names have changed to be more consistent with
8
+ Ruby's other data structure. Note that the internal heap is now in reverse
9
+ order from the previous version. If using #to_a be aware that the priority
10
+ order will be reversed. This release also switches the library to
11
+ distribution under the BSD-2-Clause license.
12
+
13
+ Changes:
14
+
15
+ * Rewrite library.
16
+ * Modernize build configuration.
17
+ * Switch to BSD-2-Clause license.
18
+
19
+
20
+ == 1.0.0 / 2009-07-05
21
+
22
+ This is the initial standalone release of PQueue, spun-off from the
23
+ Ruby Facets and originally written by K. Komada.
24
+
25
+ Changes:
26
+
27
+ * Happy New Birthday!
28
+
@@ -0,0 +1,57 @@
1
+ = PQueue
2
+
3
+ {Home}[http://rubyworks.github.com/pqueue] |
4
+ {Code}[http://github.com/rubyworks/pqueue] |
5
+ {Docs}[http://rubydoc.info/gems/pqueue/frames] |
6
+ {Mail}[http://groups.google.com/group/rubyworks] | #rubyworks
7
+
8
+ {<img src="http://travis-ci.org/rubyworks/pqueue.png" />}[http://travis-ci.org/rubyworks/pqueue]
9
+
10
+ == DESCRIPTION
11
+
12
+ Priority queue with array based heap.
13
+
14
+ A priority queue is like a standard queue, except that each inserted
15
+ elements is given a certain priority, based on the result of the
16
+ comparison block given at instantiation time. Also, retrieving an element
17
+ from the queue will always return the one with the highest priority
18
+ (see #pop and #top).
19
+
20
+ The default is to compare the elements in respect to their #<=> method.
21
+ For example, Numeric elements with higher values will have higher
22
+ priorities.
23
+
24
+ This library is a rewrite of the original PQueue.rb by K. Kodama and
25
+ Heap.rb by Ronald Butler. The two libraries were later merged
26
+ and generally improved by Olivier Renaud. Then the whole library
27
+ rewritten by Trans using the original as a functional reference.
28
+
29
+
30
+ == SYNOPSIS
31
+
32
+ require 'pqueue'
33
+
34
+ pq = PQueue.new([2,3,1]){ |a,b| a > b }
35
+
36
+ pq.pop #=> 3
37
+
38
+
39
+ == ACKNOWLEDGMENTS
40
+
41
+ Although the library has been completely rewritten since, we still would
42
+ like to acknowledge the efforts of the original PQueue authors and
43
+ contributors.
44
+
45
+ * Olivier Renaud (2007)
46
+ * Rick Bradley (2003)
47
+ * Ronald Butler (2002)
48
+ * K Kodama (2001, original library)
49
+
50
+
51
+ == COPYRIGHTS
52
+
53
+ Copyright (c) 2011 Rubyworks
54
+
55
+ PQueue is distributable in accordance with the *FreeBSD* license.
56
+
57
+ See the COPYING.rdoc file for details.
@@ -6,147 +6,128 @@
6
6
  # from the queue will always return the one with the highest priority
7
7
  # (see #pop and #top).
8
8
  #
9
- # The default is to compare the elements in repect to their #> method.
9
+ # The default is to compare the elements in repect to their #<=> method.
10
10
  # For example, Numeric elements with higher values will have higher
11
11
  # priorities.
12
12
  #
13
+ # Note that as of version 2.0 the internal queue is kept in the reverse order
14
+ # from how it was kept in previous version. If you had used #to_a in the
15
+ # past then be sure to adjust for the priorities to be ordered back-to-front
16
+ # instead of the oterh way around.
17
+ #
13
18
  class PQueue
14
19
 
15
- # number of elements
16
- attr_reader :size
17
- # compare Proc
18
- attr_reader :gt
19
- attr_reader :qarray #:nodoc:
20
- protected :qarray
20
+ #
21
+ VERSION = "2.0.0" #:erb: VERSION = "<%= version %>"
21
22
 
23
+ #
22
24
  # Returns a new priority queue.
23
25
  #
24
26
  # If elements are given, build the priority queue with these initial
25
27
  # values. The elements object must respond to #to_a.
26
28
  #
27
29
  # If a block is given, it will be used to determine the priority between
28
- # the elements.
30
+ # the elements. The block must must take two arguments and return `1`, `0`,
31
+ # or `-1` or `true`, `nil` or `false. It should return `0` or `nil` if the
32
+ # two arguments are considered equal, return `1` or `true` if the first
33
+ # argument is considered greater than the later, and `-1` or `false` if
34
+ # the later is considred to be greater than the first.
29
35
  #
30
36
  # By default, the priority queue retrieves maximum elements first
31
- # (using the #> method).
37
+ # using the #<=> method.
38
+ #
32
39
  def initialize(elements=nil, &block) # :yields: a, b
33
- @qarray = [nil]
34
- @size = 0
35
- @gt = block || lambda {|a,b| a > b}
40
+ @que = []
41
+ @cmp = block || lambda{ |a,b| a <=> b }
36
42
  replace(elements) if elements
37
43
  end
38
44
 
39
- private
45
+ protected
40
46
 
41
- # Assumes that the tree is a heap, for nodes < k.
42
47
  #
43
- # The element at index k will go up until it finds its place.
44
- def upheap(k)
45
- k2 = k.div(2)
46
- v = @qarray[k]
47
- while k2 > 0 && @gt[v, @qarray[k2]]
48
- @qarray[k] = @qarray[k2]
49
- k = k2
50
- k2 = k2.div(2)
51
- end
52
- @qarray[k] = v
53
- end
48
+ # The underlying heap.
49
+ #
50
+ attr_reader :que #:nodoc:
54
51
 
55
- # Assumes the entire tree is a heap.
56
- #
57
- # The element at index k will go down until it finds its place.
58
- def downheap(k)
59
- v = @qarray[k]
60
- q2 = @size.div(2)
61
- loop {
62
- break if k > q2
63
- j = 2 * k
64
- if j < @size && @gt[@qarray[j+1], @qarray[j]]
65
- j += 1
66
- end
67
- break if @gt[v, @qarray[j]]
68
- @qarray[k] = @qarray[j]
69
- k = j
70
- }
71
- @qarray[k] = v;
72
- end
52
+ public
73
53
 
54
+ #
55
+ # Priority comparison procedure.
56
+ #
57
+ attr_reader :cmp
74
58
 
75
- # Recursive version of heapify. I kept the code, since it may be
76
- # easier to understand than the non-recursive one.
77
- # def heapify
78
- # @size.div(2).downto(1) {|i| h(i)}
79
- # end
80
- # def h(t)
81
- # l = 2 * t
82
- # r = l + 1
83
- # hi = if r > @size || @gt[@qarray[l],@qarray[r]] then l else r end
84
- # if @gt[@qarray[hi],@qarray[t]]
85
- # @qarray[hi], @qarray[t] = @qarray[t], @qarray[hi]
86
- # h(hi) if hi <= @size.div(2)
87
- # end
88
- # end
89
-
90
- # Make a heap out of an unordered array.
91
- def heapify
92
- @size.div(2).downto(1) do |t|
93
- begin
94
- l = 2 * t
95
- r = l + 1
96
- hi = if r > @size || @gt[@qarray[l],@qarray[r]] then l else r end
97
- if @gt[@qarray[hi],@qarray[t]]
98
- @qarray[hi], @qarray[t] = @qarray[t], @qarray[hi]
99
- if hi <= @size.div(2)
100
- t = hi
101
- redo
102
- end # if
103
- end #if
104
- end #begin
105
- end # downto
59
+ #
60
+ # Returns the size of the queue.
61
+ #
62
+ def size
63
+ @que.size
106
64
  end
107
65
 
108
- public
66
+ #
67
+ # Alias of size.
68
+ #
69
+ alias length size
109
70
 
71
+ #
110
72
  # Add an element in the priority queue.
111
73
  #
112
- # The insertion time is O(log n), with n the size of the queue.
113
74
  def push(v)
114
- @size += 1
115
- @qarray[@size] = v
116
- upheap(@size)
117
- return self
75
+ @que << v
76
+ reheap(@que.size-1)
77
+ self
118
78
  end
119
79
 
80
+ #
81
+ # Alias of #push.
82
+ #
120
83
  alias :<< :push
121
84
 
85
+ #
86
+ # Alias of #push.
87
+ #
88
+ alias enq push
89
+
90
+ #
122
91
  # Return the element with the highest priority and remove it from
123
92
  # the queue.
124
93
  #
125
- # The highest priority is determined by the block given at instanciation
94
+ # The highest priority is determined by the block given at instantiation
126
95
  # time.
127
96
  #
128
- # The deletion time is O(log n), with n the size of the queue.
97
+ # The deletion time is O(log n), with n is the size of the queue.
129
98
  #
130
99
  # Return nil if the queue is empty.
100
+ #
131
101
  def pop
132
102
  return nil if empty?
133
- res = @qarray[1]
134
- @qarray[1] = @qarray[@size]
135
- @size -= 1
136
- downheap(1)
137
- return res
103
+ @que.pop
138
104
  end
139
105
 
140
- # Return the element with the highest priority.
106
+ #
107
+ # Alias of #push.
108
+ #
109
+ alias shift push
110
+
111
+ #
112
+ # Alias of #pop.
113
+ #
114
+ alias deq push
115
+
116
+ #
117
+ # Returns the element with the highest priority, but
118
+ # does not remove it from the queue.
119
+ #
141
120
  def top
142
121
  return nil if empty?
143
- return @qarray[1]
122
+ return @que.last
144
123
  end
145
124
 
125
+ #
146
126
  # Add more than one element at the same time. See #push.
147
127
  #
148
- # The elements object must respond to #to_a, or to be a PQueue itself.
149
- def push_all(elements)
128
+ # The elements object must respond to #to_a, or be a PQueue itself.
129
+ #
130
+ def concat(elements)
150
131
  if empty?
151
132
  if elements.kind_of?(PQueue)
152
133
  initialize_copy(elements)
@@ -155,115 +136,182 @@ class PQueue
155
136
  end
156
137
  else
157
138
  if elements.kind_of?(PQueue)
158
- @qarray[@size + 1, elements.size] = elements.qarray[1..-1]
159
- elements.size.times{ @size += 1; upheap(@size)}
139
+ @que.concat(elements.que)
140
+ sort!
160
141
  else
161
- ary = elements.to_a
162
- @qarray[@size + 1, ary.size] = ary
163
- ary.size.times{ @size += 1; upheap(@size)}
142
+ @que.concat(elements.to_a)
143
+ sort!
164
144
  end
165
145
  end
166
146
  return self
167
147
  end
168
148
 
169
- alias :merge :push_all
170
-
149
+ #
150
+ # Alias for #concat.
151
+ #
152
+ alias :merge! :concat
171
153
 
154
+ #
172
155
  # Return top n-element as a sorted array.
173
- def pop_array(n=@size)
174
- ary = []
175
- n.times{ary.push(pop)}
176
- return ary
156
+ #
157
+ def take(n=@size)
158
+ a = []
159
+ n.times{a.push(pop)}
160
+ a
177
161
  end
178
162
 
179
-
180
- # True if there is no more elements left in the priority queue.
163
+ #
164
+ # Returns true if there is no more elements left in the queue.
165
+ #
181
166
  def empty?
182
- return @size.zero?
167
+ @que.empty?
183
168
  end
184
169
 
170
+ #
185
171
  # Remove all elements from the priority queue.
172
+ #
186
173
  def clear
187
- @qarray.replace([nil])
188
- @size = 0
189
- return self
174
+ @que.clear
175
+ self
190
176
  end
191
177
 
178
+ #
192
179
  # Replace the content of the heap by the new elements.
193
180
  #
194
- # The elements object must respond to #to_a, or to be a PQueue itself.
181
+ # The elements object must respond to #to_a, or to be
182
+ # a PQueue itself.
183
+ #
195
184
  def replace(elements)
196
185
  if elements.kind_of?(PQueue)
197
186
  initialize_copy(elements)
198
187
  else
199
- @qarray.replace([nil] + elements.to_a)
200
- @size = @qarray.size - 1
201
- heapify
188
+ @que.replace(elements.to_a)
189
+ sort!
202
190
  end
203
- return self
191
+ self
204
192
  end
205
193
 
194
+ #
206
195
  # Return a sorted array, with highest priority first.
196
+ #
207
197
  def to_a
208
- old_qarray = @qarray.dup
209
- old_size = @size
210
- res = pop_array
211
- @qarray = old_qarray
212
- @size = old_size
213
- return res
198
+ @que.dup
214
199
  end
215
200
 
216
- alias :sort :to_a
217
-
218
- # Replace the top element with the given one, and return this top element.
219
201
  #
220
- # Equivalent to successively calling #pop and #push(v).
221
- def replace_top(v)
222
- # replace top element
223
- if empty?
224
- @qarray[1] = v
225
- @size += 1
226
- return nil
227
- else
228
- res = @qarray[1]
229
- @qarray[1] = v
230
- downheap(1)
231
- return res
232
- end
233
- end
234
-
235
202
  # Return true if the given object is present in the queue.
203
+ #
236
204
  def include?(element)
237
- return @qarray.include?(element)
205
+ @que.include?(element)
206
+ end
207
+
208
+ #
209
+ # Push element onto queue while popping off and returning the next element.
210
+ # This is qquivalent to successively calling #pop and #push(v).
211
+ #
212
+ def swap(v)
213
+ r = pop
214
+ push(v)
215
+ r
238
216
  end
239
217
 
218
+ #
240
219
  # Iterate over the ordered elements, destructively.
220
+ #
241
221
  def each_pop #:yields: popped
242
222
  until empty?
243
223
  yield pop
244
224
  end
245
- return nil
225
+ nil
246
226
  end
247
227
 
248
- # Pretty print
228
+ #
229
+ # Pretty inspection string.
230
+ #
249
231
  def inspect
250
- "<#{self.class}: size=#{@size}, top=#{top || "nil"}>"
232
+ "<#{self.class}: size=#{size}, top=#{top || "nil"}>"
251
233
  end
252
234
 
253
- ###########################
254
- ### Override Object methods
255
-
235
+ #
256
236
  # Return true if the queues contain equal elements.
237
+ #
257
238
  def ==(other)
258
- return size == other.size && to_a == other.to_a
239
+ size == other.size && to_a == other.to_a
259
240
  end
260
241
 
261
- private
242
+ private
262
243
 
244
+ #
245
+ #
246
+ #
263
247
  def initialize_copy(other)
264
- @gt = other.gt
265
- @qarray = other.qarray.dup
266
- @size = other.size
248
+ @cmp = other.cmp
249
+ @que = other.que.dup
250
+ sort!
267
251
  end
268
- end # class PQueue
269
252
 
253
+ #
254
+ # The element at index k will be repositioned to its proper place.
255
+ #
256
+ # This, of course, assumes the queue is already sorted.
257
+ #
258
+ def reheap(k)
259
+ return self if size <= 1
260
+
261
+ que = @que.dup
262
+
263
+ v = que.delete_at(k)
264
+
265
+ i = que.size.div(2)
266
+ q = i
267
+ r = nil
268
+
269
+ loop do
270
+ case @cmp.call(v, que[i])
271
+ when 0, nil
272
+ r = i
273
+ break
274
+ when 1, true
275
+ i = (que.size + i).div(2)
276
+ i += 1 if i == q # don't repeat yourself
277
+ when -1, false
278
+ i = (i).div(2)
279
+ i -= 1 if i == q # don't repeat yourself
280
+ else
281
+ warn "bad comparison procedure in #{self.inspect}"
282
+ r = i
283
+ break
284
+ end
285
+ q = i
286
+ end
287
+
288
+ que.insert(r, v)
289
+
290
+ @que = que
291
+
292
+ return self
293
+ end
294
+
295
+ #
296
+ # Sort the queue in accorance to the given comparison procedure.
297
+ #
298
+ def sort!
299
+ @que.sort! do |a,b|
300
+ case @cmp.call(a,b)
301
+ when 0, nil then 0
302
+ when 1, true then 1
303
+ when -1, false then -1
304
+ else
305
+ warn "bad comparison procedure in #{self.inspect}"
306
+ 0
307
+ end
308
+ end
309
+ self
310
+ end
311
+
312
+ #
313
+ # Alias of #sort!
314
+ #
315
+ alias heapify sort!
316
+
317
+ end # class PQueue