depq 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. data/README +181 -0
  2. data/depq.rb +1520 -0
  3. data/test-depq.rb +772 -0
  4. metadata +61 -0
data/README ADDED
@@ -0,0 +1,181 @@
1
+ Depq - Feature Rich Double-Ended Priority Queue.
2
+
3
+ = Features
4
+
5
+ * queue - you can insert and delete values
6
+ * priority - you can get a value with minimum priority
7
+ * double-ended - you can get a value with maximum priority too
8
+ * stable - you don't need to maintain timestamps yourself
9
+ * update priority - usable for Dijkstra's shortest path algorithm and various graph algorithms
10
+ * implicit binary heap - most operations are O(log n) at worst
11
+
12
+ = Introduction
13
+
14
+ == Simple Insertion/Deletion
15
+
16
+ You can insert values into a Depq object.
17
+ You can deletes the values from the object from ascending/descending order.
18
+ delete_min deletes the minimum value.
19
+ It is used for ascending order.
20
+
21
+ pd = Depq.new
22
+ pd.insert "durian"
23
+ pd.insert "banana"
24
+ p pd.delete_min #=> "banana"
25
+ pd.insert "orange"
26
+ pd.insert "apple"
27
+ pd.insert "melon"
28
+ p pd.delete_min #=> "apple"
29
+ p pd.delete_min #=> "durian"
30
+ p pd.delete_min #=> "melon"
31
+ p pd.delete_min #=> "orange"
32
+ p pd.delete_min #=> nil
33
+
34
+ delete_max is similar to delete_min except it deletes maximum element
35
+ instead of minimum.
36
+ It is used for descending order.
37
+
38
+ == The Order
39
+
40
+ The order is defined by the priorities corresnponds to the values and
41
+ comparison operator specified for the queue.
42
+
43
+ pd = Depq.new(:casecmp) # use casecmp instead of <=>.
44
+ pd.inesrt 1, "Foo" # specify the priority for 1 as "Foo"
45
+ pd.insert 2, "bar"
46
+ pd.insert 3, "Baz"
47
+ p pd.delete_min #=> 2 # "bar" is minimum
48
+ p pd.delete_min #=> 3
49
+ p pd.delete_min #=> 1 # "Foo" is maximum
50
+ p pd.delete_min #=> nil
51
+
52
+ If there are multiple values with same priority, subpriority is used to compare them.
53
+ subpriority is an integer which can be specified by 3rd argument of insert.
54
+ If it is not specified, total number of inserted elements is used.
55
+ So Depq is "stable" with delete_min.
56
+ The element inserted first is minimum and deleted first.
57
+
58
+ pd = Depq.new
59
+ pd.insert "a", 1 # "a", "c" and "e" has same priority: 1
60
+ pd.insert "b", 0 # "b", "d" and "f" has same priority: 0
61
+ pd.insert "c", 1
62
+ pd.insert "d", 0
63
+ pd.insert "e", 1
64
+ pd.insert "f", 0
65
+ p pd.delete_min #=> "b" first element with priority 0
66
+ p pd.delete_min #=> "d"
67
+ p pd.delete_min #=> "f" last element with priority 0
68
+ p pd.delete_min #=> "a" first element with priority 1
69
+ p pd.delete_min #=> "c"
70
+ p pd.delete_min #=> "e" last element with priority 1
71
+
72
+ Note that delete_max is also stable.
73
+ This means delete_max deletes the element with maximum priority with "minimum" subpriority.
74
+
75
+ == Update Element
76
+
77
+ An inserted element can be modified and/or deleted.
78
+ This is done using Depq::Locator object.
79
+ It is returned by insert, find_min_locator, etc.
80
+
81
+ pd = Depq.new
82
+ d = pd.insert "durian", 1
83
+ m = pd.insert "mangosteen", 2
84
+ c = pd.insert "cherry", 3
85
+ p m #=> #<Depq::Locator: "mangosteen":2>
86
+ p m.value #=> "mangosteen"
87
+ p m.priority #=> 2
88
+ p pd.find_min #=> "durian"
89
+ p pd.find_min_locator #=> #<Depq::Locator: "durian":1>
90
+ m.update("mangosteen", 0)
91
+ p pd.find_min #=> "mangosteen"
92
+ p pd.find_min_locator #=> #<Depq::Locator: "mangosteen":0>
93
+ pd.delete_element d
94
+ p pd.delete_min #=> "mangosteen"
95
+ p pd.delete_min #=> "cherry"
96
+ p pd.delete_min #=> nil
97
+
98
+ For example, this feature can be used for graph algorithms
99
+ such as Dijkstra's shortest path finding algorithm,
100
+ A* search algorithm, etc.
101
+
102
+ def dijkstra_shortest_path(start, edges)
103
+ h = {}
104
+ edges.each {|v1, v2, w|
105
+ (h[v1] ||= []) << [v2, w]
106
+ }
107
+ h.default = []
108
+ q = Depq.new
109
+ visited = {start => q.insert([start], 0)}
110
+ until q.empty?
111
+ path, w1 = q.delete_min_priority
112
+ v1 = path.last
113
+ h[v1].each {|v2, w2|
114
+ if !visited[v2]
115
+ visited[v2] = q.insert(path+[v2], w1 + w2)
116
+ elsif w1 + w2 < visited[v2].priority
117
+ visited[v2].update(path+[v2], w1 + w2) # update val/prio
118
+ end
119
+ }
120
+ end
121
+ result = []
122
+ visited.each_value {|loc|
123
+ result << [loc.value, loc.priority]
124
+ }
125
+ result
126
+ end
127
+
128
+ E = [
129
+ ['A', 'B', 2],
130
+ ['A', 'C', 4],
131
+ ['B', 'C', 1],
132
+ ['C', 'B', 2],
133
+ ['B', 'D', 3],
134
+ ['C', 'D', 1],
135
+ ]
136
+ p dijkstra_shortest_path('A', E)
137
+ #=> [[["A"], 0],
138
+ # [["A", "B"], 2],
139
+ # [["A", "B", "C"], 3],
140
+ # [["A", "B", "C", "D"], 4]]
141
+
142
+ = Internal Heap Algorithm and Performance Tips
143
+
144
+ Depq uses min-heap or max-heap internally.
145
+ When delete_min is used, min-heap is constructed and max-heap is destructed.
146
+ When delete_max is used, max-heap is constructed and min-heap is destructed.
147
+ So mixing delete_min and delete_max causes bad performance.
148
+ In future, min-max-heap may be implemented to avoid this problem.
149
+ min-max-heap will be used when delete_min and delete_max is used both.
150
+ (Because min-max-heap is slower than min-heap/max-heap.)
151
+
152
+ = Author
153
+
154
+ Tanaka Akira <akr@fsij.org>
155
+
156
+ = License
157
+
158
+ Redistribution and use in source and binary forms, with or without
159
+ modification, are permitted provided that the following conditions are met:
160
+
161
+ (1) Redistributions of source code must retain the above copyright notice, this
162
+ list of conditions and the following disclaimer.
163
+ (2) Redistributions in binary form must reproduce the above copyright notice,
164
+ this list of conditions and the following disclaimer in the documentation
165
+ and/or other materials provided with the distribution.
166
+ (3) The name of the author may not be used to endorse or promote products
167
+ derived from this software without specific prior written permission.
168
+
169
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
170
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
171
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
172
+ EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
173
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
174
+ OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
175
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
176
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
177
+ IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
178
+ OF SUCH DAMAGE.
179
+
180
+ (The modified BSD licence)
181
+
data/depq.rb ADDED
@@ -0,0 +1,1520 @@
1
+ # depq.rb - Feature Rich Double-Ended Priority Queue.
2
+ #
3
+ # Copyright (C) 2009 Tanaka Akira <akr@fsij.org>
4
+ #
5
+ # Redistribution and use in source and binary forms, with or without
6
+ # modification, are permitted provided that the following conditions are met:
7
+ #
8
+ # 1. Redistributions of source code must retain the above copyright notice, this
9
+ # list of conditions and the following disclaimer.
10
+ # 2. Redistributions in binary form must reproduce the above copyright notice,
11
+ # this list of conditions and the following disclaimer in the documentation
12
+ # and/or other materials provided with the distribution.
13
+ # 3. The name of the author may not be used to endorse or promote products
14
+ # derived from this software without specific prior written permission.
15
+ #
16
+ # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17
+ # WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18
+ # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19
+ # EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20
+ # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
21
+ # OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
24
+ # IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
25
+ # OF SUCH DAMAGE.
26
+
27
+ # Depq - Feature Rich Double-Ended Priority Queue.
28
+ #
29
+ # = Features
30
+ #
31
+ # * queue - you can insert and delete values
32
+ # * priority - you can get a value with minimum priority
33
+ # * double-ended - you can get a value with maximum priority too
34
+ # * stable - you don't need to maintain timestamps yourself
35
+ # * update priority - usable for Dijkstra's shortest path algorithm and various graph algorithms
36
+ # * implicit binary heap - most operations are O(log n) at worst
37
+ #
38
+ # = Introduction
39
+ #
40
+ # == Simple Insertion/Deletion
41
+ #
42
+ # You can insert values into a Depq object.
43
+ # You can deletes the values from the object from ascending/descending order.
44
+ # delete_min deletes the minimum value.
45
+ # It is used for ascending order.
46
+ #
47
+ # pd = Depq.new
48
+ # pd.insert "durian"
49
+ # pd.insert "banana"
50
+ # p pd.delete_min #=> "banana"
51
+ # pd.insert "orange"
52
+ # pd.insert "apple"
53
+ # pd.insert "melon"
54
+ # p pd.delete_min #=> "apple"
55
+ # p pd.delete_min #=> "durian"
56
+ # p pd.delete_min #=> "melon"
57
+ # p pd.delete_min #=> "orange"
58
+ # p pd.delete_min #=> nil
59
+ #
60
+ # delete_max is similar to delete_min except it deletes maximum element
61
+ # instead of minimum.
62
+ # It is used for descending order.
63
+ #
64
+ # == The Order
65
+ #
66
+ # The order is defined by the priorities corresnponds to the values and
67
+ # comparison operator specified for the queue.
68
+ #
69
+ # pd = Depq.new(:casecmp) # use casecmp instead of <=>.
70
+ # pd.inesrt 1, "Foo" # specify the priority for 1 as "Foo"
71
+ # pd.insert 2, "bar"
72
+ # pd.insert 3, "Baz"
73
+ # p pd.delete_min #=> 2 # "bar" is minimum
74
+ # p pd.delete_min #=> 3
75
+ # p pd.delete_min #=> 1 # "Foo" is maximum
76
+ # p pd.delete_min #=> nil
77
+ #
78
+ # If there are multiple values with same priority, subpriority is used to compare them.
79
+ # subpriority is an integer which can be specified by 3rd argument of insert.
80
+ # If it is not specified, total number of inserted elements is used.
81
+ # So Depq is "stable" with delete_min.
82
+ # The element inserted first is minimum and deleted first.
83
+ #
84
+ # pd = Depq.new
85
+ # pd.insert "a", 1 # "a", "c" and "e" has same priority: 1
86
+ # pd.insert "b", 0 # "b", "d" and "f" has same priority: 0
87
+ # pd.insert "c", 1
88
+ # pd.insert "d", 0
89
+ # pd.insert "e", 1
90
+ # pd.insert "f", 0
91
+ # p pd.delete_min #=> "b" first element with priority 0
92
+ # p pd.delete_min #=> "d"
93
+ # p pd.delete_min #=> "f" last element with priority 0
94
+ # p pd.delete_min #=> "a" first element with priority 1
95
+ # p pd.delete_min #=> "c"
96
+ # p pd.delete_min #=> "e" last element with priority 1
97
+ #
98
+ # Note that delete_max is also stable.
99
+ # This means delete_max deletes the element with maximum priority with "minimum" subpriority.
100
+ #
101
+ # == Update Element
102
+ #
103
+ # An inserted element can be modified and/or deleted.
104
+ # This is done using Depq::Locator object.
105
+ # It is returned by insert, find_min_locator, etc.
106
+ #
107
+ # pd = Depq.new
108
+ # d = pd.insert "durian", 1
109
+ # m = pd.insert "mangosteen", 2
110
+ # c = pd.insert "cherry", 3
111
+ # p m #=> #<Depq::Locator: "mangosteen":2>
112
+ # p m.value #=> "mangosteen"
113
+ # p m.priority #=> 2
114
+ # p pd.find_min #=> "durian"
115
+ # p pd.find_min_locator #=> #<Depq::Locator: "durian":1>
116
+ # m.update("mangosteen", 0)
117
+ # p pd.find_min #=> "mangosteen"
118
+ # p pd.find_min_locator #=> #<Depq::Locator: "mangosteen":0>
119
+ # pd.delete_element d
120
+ # p pd.delete_min #=> "mangosteen"
121
+ # p pd.delete_min #=> "cherry"
122
+ # p pd.delete_min #=> nil
123
+ #
124
+ # For example, this feature can be used for graph algorithms
125
+ # such as Dijkstra's shortest path finding algorithm,
126
+ # A* search algorithm, etc.
127
+ #
128
+ # def dijkstra_shortest_path(start, edges)
129
+ # h = {}
130
+ # edges.each {|v1, v2, w|
131
+ # (h[v1] ||= []) << [v2, w]
132
+ # }
133
+ # h.default = []
134
+ # q = Depq.new
135
+ # visited = {start => q.insert([start], 0)}
136
+ # until q.empty?
137
+ # path, w1 = q.delete_min_priority
138
+ # v1 = path.last
139
+ # h[v1].each {|v2, w2|
140
+ # if !visited[v2]
141
+ # visited[v2] = q.insert(path+[v2], w1 + w2)
142
+ # elsif w1 + w2 < visited[v2].priority
143
+ # visited[v2].update(path+[v2], w1 + w2) # update val/prio
144
+ # end
145
+ # }
146
+ # end
147
+ # result = []
148
+ # visited.each_value {|loc|
149
+ # result << [loc.value, loc.priority]
150
+ # }
151
+ # result
152
+ # end
153
+ #
154
+ # E = [
155
+ # ['A', 'B', 2],
156
+ # ['A', 'C', 4],
157
+ # ['B', 'C', 1],
158
+ # ['C', 'B', 2],
159
+ # ['B', 'D', 3],
160
+ # ['C', 'D', 1],
161
+ # ]
162
+ # p dijkstra_shortest_path('A', E)
163
+ # #=> [[["A"], 0],
164
+ # # [["A", "B"], 2],
165
+ # # [["A", "B", "C"], 3],
166
+ # # [["A", "B", "C", "D"], 4]]
167
+ #
168
+ # = Internal Heap Algorithm and Performance Tips
169
+ #
170
+ # Depq uses min-heap or max-heap internally.
171
+ # When delete_min is used, min-heap is constructed and max-heap is destructed.
172
+ # When delete_max is used, max-heap is constructed and min-heap is destructed.
173
+ # So mixing delete_min and delete_max causes bad performance.
174
+ # In future, min-max-heap may be implemented to avoid this problem.
175
+ # min-max-heap will be used when delete_min and delete_max is used both.
176
+ # (Because min-max-heap is slower than min-heap/max-heap.)
177
+ #
178
+ class Depq
179
+ include Enumerable
180
+
181
+ Locator = Struct.new(:value, :pdeque_or_subpriority, :index_or_priority)
182
+ class Locator
183
+
184
+ # if pdeque_or_subpriority is Depq
185
+ # pdeque_or_subpriority is pdeque
186
+ # index_or_priority is index
187
+ # else
188
+ # pdeque_or_subpriority is subpriority
189
+ # index_or_priority is priority
190
+ # end
191
+ #
192
+ # only 3 fields for memory efficiency.
193
+
194
+ private :value=
195
+ private :pdeque_or_subpriority
196
+ private :pdeque_or_subpriority=
197
+ private :index_or_priority
198
+ private :index_or_priority=
199
+
200
+ private :to_a
201
+ private :values
202
+ private :size
203
+ private :length
204
+ private :each
205
+ private :each_pair
206
+ private :[]
207
+ private :[]=
208
+ private :values_at
209
+ private :members
210
+ private :select
211
+
212
+ Enumerable.instance_methods.each {|m|
213
+ private m
214
+ }
215
+
216
+ define_method(:==, Object.instance_method(:eql?))
217
+ define_method(:eql?, Object.instance_method(:eql?))
218
+ define_method(:hash, Object.instance_method(:hash))
219
+
220
+ include Comparable
221
+
222
+ # Create a Depq::Locator object.
223
+ def initialize(value, priority=value, subpriority=nil)
224
+ super value, subpriority, priority
225
+ end
226
+
227
+ def inspect
228
+ prio = self.priority
229
+ if self.value == prio
230
+ s = self.value.inspect
231
+ else
232
+ s = "#{self.value.inspect}:#{prio.inspect}"
233
+ end
234
+ if in_queue?
235
+ "\#<#{self.class}: #{s}>"
236
+ else
237
+ "\#<#{self.class}: #{s} (no queue)>"
238
+ end
239
+ end
240
+ alias to_s inspect
241
+
242
+ def initialize_copy(obj) # :nodoc:
243
+ raise TypeError, "can not duplicated"
244
+ end
245
+
246
+ # returns true if the locator is in a queue.
247
+ def in_queue?
248
+ pdeque_or_subpriority().kind_of? Depq
249
+ end
250
+
251
+ # returns the queue.
252
+ #
253
+ # nil is returned if the locator is not in a pdeque.
254
+ def pdeque
255
+ in_queue? ? pdeque_or_subpriority() : nil
256
+ end
257
+ alias queue pdeque
258
+
259
+ def index
260
+ in_queue? ? index_or_priority() : nil
261
+ end
262
+ private :index
263
+
264
+ def index=(i)
265
+ if !in_queue?
266
+ raise ArgumentError, "not in queue"
267
+ end
268
+ self.index_or_priority = i
269
+ end
270
+ private :index=
271
+
272
+ # returns the priority.
273
+ def priority
274
+ if in_queue?
275
+ pd = pdeque_or_subpriority()
276
+ priority, subpriority = pd.send(:internal_get_priority, self)
277
+ priority
278
+ else
279
+ index_or_priority()
280
+ end
281
+ end
282
+
283
+ # returns the subpriority.
284
+ def subpriority
285
+ if in_queue?
286
+ pd = pdeque_or_subpriority()
287
+ priority, subpriority = pd.send(:internal_get_priority, self)
288
+ subpriority
289
+ else
290
+ pdeque_or_subpriority()
291
+ end
292
+ end
293
+
294
+ # update the value, priority and subpriority.
295
+ #
296
+ # subpriority cannot be nil if the locator is in a queue.
297
+ # So subpriority is not changed if subpriority is not specified or nil for a locator in a queue.
298
+ # subpriority is set to nil if subpriority is not specified or nil for a locator not in a queue.
299
+ #
300
+ # pd = Depq.new
301
+ # loc1 = pd.insert 1, 2, 3
302
+ # p [loc1.value, loc1.priority, loc1.subpriority] #=> [1, 2, 3]
303
+ # loc1.update(11, 12)
304
+ # p [loc1.value, loc1.priority, loc1.subpriority] #=> [11, 12, 3]
305
+ #
306
+ # loc2 = Depq::Locator.new(4, 5, 6)
307
+ # p [loc2.value, loc2.priority, loc2.subpriority] #=> [4, 5, 6]
308
+ # loc2.update(14, 15)
309
+ # p [loc2.value, loc2.priority, loc2.subpriority] #=> [14, 15, nil]
310
+ #
311
+ # This feature is called as decrease-key/increase-key in
312
+ # Computer Science terminology.
313
+ def update(value, priority=value, subpriority=nil)
314
+ subpriority = Integer(subpriority) if subpriority != nil
315
+ if in_queue?
316
+ pd = pdeque_or_subpriority()
317
+ if subpriority == nil
318
+ subpriority = self.subpriority
319
+ else
320
+ subpriority = Integer(subpriority)
321
+ end
322
+ pd.send(:internal_set_priority, self, priority, subpriority)
323
+ else
324
+ self.index_or_priority = priority
325
+ self.pdeque_or_subpriority = subpriority
326
+ end
327
+ self.value = value
328
+ nil
329
+ end
330
+
331
+ # update the value.
332
+ #
333
+ # This method doesn't change the priority and subpriority.
334
+ #
335
+ # pd = Depq.new
336
+ # loc = pd.insert 1, 2, 3
337
+ # p [loc.value, loc.priority, loc.subpriority] #=> [1, 2, 3]
338
+ # loc.update_value 10
339
+ # p [loc.value, loc.priority, loc.subpriority] #=> [10, 2, 3]
340
+ #
341
+ def update_value(value)
342
+ update(value, self.priority, self.subpriority)
343
+ end
344
+
345
+ # update the priority and subpriority.
346
+ #
347
+ # This method doesn't change the value.
348
+ #
349
+ # pd = Depq.new
350
+ # loc = pd.insert 1, 2, 3
351
+ # p [loc.value, loc.priority, loc.subpriority] #=> [1, 2, 3]
352
+ # loc.update_priority 10
353
+ # p [loc.value, loc.priority, loc.subpriority] #=> [1, 10, 3]
354
+ # loc.update_priority 20, 30
355
+ # p [loc.value, loc.priority, loc.subpriority] #=> [1, 20, 30]
356
+ #
357
+ def update_priority(priority, subpriority=nil)
358
+ update(self.value, priority, subpriority)
359
+ end
360
+
361
+ def internal_inserted(pdeque, index)
362
+ raise ArgumentError, "already inserted" if in_queue?
363
+ priority = index_or_priority()
364
+ self.pdeque_or_subpriority = pdeque
365
+ self.index_or_priority = index
366
+ priority
367
+ end
368
+ private :internal_inserted
369
+
370
+ def internal_deleted(priority, subpriority)
371
+ raise ArgumentError, "not inserted" if !in_queue?
372
+ self.index_or_priority = priority
373
+ self.pdeque_or_subpriority = subpriority
374
+ end
375
+ private :internal_deleted
376
+
377
+ end
378
+
379
+ # Create a Depq object.
380
+ #
381
+ # The optional argument, cmp, specify the method to compare priorities.
382
+ # It should be a symbol or a Proc which takes two arguments.
383
+ # If it is omitted, :<=> is used.
384
+ #
385
+ # pd = Depq.new
386
+ # pd.insert "Foo"
387
+ # pd.insert "bar"
388
+ # p pd.delete_min #=> "Foo"
389
+ # p pd.delete_min #=> "bar"
390
+ #
391
+ # pd = Depq.new(:casecmp)
392
+ # pd.insert "Foo"
393
+ # pd.insert "bar"
394
+ # p pd.delete_min #=> "bar"
395
+ # p pd.delete_min #=> "Foo"
396
+ #
397
+ # pd = Depq.new(lambda {|a,b| a.casecmp(b) })
398
+ # pd.insert "Foo"
399
+ # pd.insert "bar"
400
+ # p pd.delete_min #=> "bar"
401
+ # p pd.delete_min #=> "Foo"
402
+ #
403
+ def initialize(cmp = :<=>)
404
+ @cmp = cmp
405
+ @ary = []
406
+ @heapsize = 0
407
+ @mode = nil
408
+ @totalcount = 0
409
+ #@subpriority_generator = nil
410
+ end
411
+
412
+ # :stopdoc:
413
+ ARY_SLICE_SIZE = 3
414
+ # :startdoc:
415
+
416
+ def get_entry(i)
417
+ locator = @ary[i*ARY_SLICE_SIZE+0]
418
+ priority = @ary[i*ARY_SLICE_SIZE+1]
419
+ subpriority = @ary[i*ARY_SLICE_SIZE+2]
420
+ [locator, priority, subpriority]
421
+ end
422
+ private :get_entry
423
+
424
+ def set_entry(i, locator, priority, subpriority)
425
+ tmp = Array.new(ARY_SLICE_SIZE)
426
+ tmp[0] = locator
427
+ tmp[1] = priority
428
+ tmp[2] = subpriority
429
+ @ary[i*ARY_SLICE_SIZE, ARY_SLICE_SIZE] = tmp
430
+ end
431
+ private :set_entry
432
+
433
+ def delete_entry(i)
434
+ locator, priority, subpriority = @ary[i*ARY_SLICE_SIZE, ARY_SLICE_SIZE]
435
+ @ary[i*ARY_SLICE_SIZE, ARY_SLICE_SIZE] = []
436
+ [locator, priority, subpriority]
437
+ end
438
+ private :delete_entry
439
+
440
+ def each_entry
441
+ 0.upto(self.size-1) {|i|
442
+ ei = @ary[i*ARY_SLICE_SIZE+0]
443
+ pi = @ary[i*ARY_SLICE_SIZE+1]
444
+ si = @ary[i*ARY_SLICE_SIZE+2]
445
+ yield ei, pi, si
446
+ }
447
+ end
448
+ private :each_entry
449
+
450
+ def min_mode
451
+ if @mode != MinHeap
452
+ @mode = MinHeap
453
+ @heapsize = @mode.heapify(self, @ary)
454
+ elsif @heapsize < self.size
455
+ @heapsize = @mode.heapify(self, @ary, @heapsize)
456
+ end
457
+ end
458
+ private :min_mode
459
+
460
+ def max_mode
461
+ if @mode != MaxHeap
462
+ @mode = MaxHeap
463
+ @heapsize = @mode.heapify(self, @ary)
464
+ elsif @heapsize < self.size
465
+ @heapsize = @mode.heapify(self, @ary, @heapsize)
466
+ end
467
+ end
468
+ private :max_mode
469
+
470
+ def mode_heapify
471
+ if @mode
472
+ @heapsize = @mode.heapify(self, @ary)
473
+ end
474
+ end
475
+ private :mode_heapify
476
+
477
+ def check_locator(loc)
478
+ if !self.equal?(loc.pdeque) ||
479
+ !get_entry(loc.send(:index))[0].equal?(loc)
480
+ raise ArgumentError, "unexpected locator"
481
+ end
482
+ end
483
+ private :check_locator
484
+
485
+ def default_subpriority
486
+ #return @subpriority_generator.call if @subpriority_generator
487
+ self.totalcount
488
+ end
489
+ private :default_subpriority
490
+
491
+ def compare_for_min(priority1, subpriority1, priority2, subpriority2)
492
+ compare_priority(priority1, priority2).nonzero? or
493
+ (subpriority1 <=> subpriority2)
494
+ end
495
+ private :compare_for_min
496
+
497
+ def compare_for_max(priority1, subpriority1, priority2, subpriority2)
498
+ compare_priority(priority1, priority2).nonzero? or
499
+ (subpriority2 <=> subpriority1)
500
+ end
501
+ private :compare_for_max
502
+
503
+ def initialize_copy(obj) # :nodoc:
504
+ if defined? @ary
505
+ @ary = @ary.dup
506
+ 0.step(@ary.length-1, ARY_SLICE_SIZE) {|k|
507
+ i = k / ARY_SLICE_SIZE
508
+ loc1 = @ary[k]
509
+ priority = @ary[k+1]
510
+ loc2 = Depq::Locator.new(loc1.value, priority)
511
+ loc2.send(:internal_inserted, self, i)
512
+ @ary[k] = loc2
513
+ }
514
+ end
515
+ end
516
+
517
+ def inspect # :nodoc:
518
+ unless defined? @cmp
519
+ return "\#<#{self.class}: uninitialized>"
520
+ end
521
+ a = []
522
+ each_entry {|loc, priority|
523
+ value = loc.value
524
+ s = value.inspect
525
+ if value != priority
526
+ s << ":" << priority.inspect
527
+ end
528
+ a << s
529
+ }
530
+ "\#<#{self.class}: #{a.join(' ')}>"
531
+ end
532
+
533
+ def pretty_print(q) # :nodoc:
534
+ q.object_group(self) {
535
+ each_entry {|loc, priority|
536
+ q.breakable
537
+ value = loc.value
538
+ q.pp value
539
+ if value != priority
540
+ q.text ':'
541
+ q.pp priority
542
+ end
543
+ }
544
+ }
545
+ end
546
+
547
+ # compare priority1 and priority2.
548
+ #
549
+ # pd = Depq.new
550
+ # p pd.compare_priority("a", "b") #=> -1
551
+ # p pd.compare_priority("a", "a") #=> 0
552
+ # p pd.compare_priority("b", "a") #=> 1
553
+ #
554
+ def compare_priority(priority1, priority2)
555
+ if @cmp.kind_of? Symbol
556
+ priority1.__send__(@cmp, priority2)
557
+ else
558
+ @cmp.call(priority1, priority2)
559
+ end
560
+ end
561
+
562
+ # returns true if the queue is empty.
563
+ #
564
+ # pd = Depq.new
565
+ # p pd.empty? #=> true
566
+ # pd.insert 1
567
+ # p pd.empty? #=> false
568
+ # pd.delete_max
569
+ # p pd.empty? #=> true
570
+ #
571
+ def empty?
572
+ @ary.empty?
573
+ end
574
+
575
+ # returns the number of elements in the queue.
576
+ #
577
+ # pd = Depq.new
578
+ # p pd.size #=> 0
579
+ # pd.insert 1
580
+ # p pd.size #=> 1
581
+ # pd.insert 1
582
+ # p pd.size #=> 2
583
+ # pd.delete_min
584
+ # p pd.size #=> 1
585
+ # pd.delete_min
586
+ # p pd.size #=> 0
587
+ #
588
+ def size
589
+ @ary.size / ARY_SLICE_SIZE
590
+ end
591
+ alias length size
592
+
593
+ # returns the total number of elements inserted for the queue, ever.
594
+ #
595
+ # The result is monotonically increased.
596
+ #
597
+ # pd = Depq.new
598
+ # p [pd.size, pd.totalcount] #=> [0, 0]
599
+ # pd.insert 1
600
+ # p [pd.size, pd.totalcount] #=> [1, 1]
601
+ # pd.insert 2
602
+ # p [pd.size, pd.totalcount] #=> [2, 2]
603
+ # pd.delete_min
604
+ # p [pd.size, pd.totalcount] #=> [1, 2]
605
+ # pd.insert 4
606
+ # p [pd.size, pd.totalcount] #=> [2, 3]
607
+ # pd.insert 3
608
+ # p [pd.size, pd.totalcount] #=> [3, 4]
609
+ # pd.insert 0
610
+ # p [pd.size, pd.totalcount] #=> [4, 5]
611
+ # pd.delete_min
612
+ # p [pd.size, pd.totalcount] #=> [3, 5]
613
+ # pd.insert 2
614
+ # p [pd.size, pd.totalcount] #=> [4, 6]
615
+ #
616
+ def totalcount
617
+ @totalcount
618
+ end
619
+
620
+ # make the queue empty.
621
+ #
622
+ # Note that totalcount is not changed.
623
+ #
624
+ # pd = Depq.new
625
+ # pd.insert 1
626
+ # pd.insert 1
627
+ # p pd.size #=> 2
628
+ # p pd.totalcount #=> 2
629
+ # pd.clear
630
+ # p pd.size #=> 0
631
+ # p pd.totalcount #=> 2
632
+ # p pd.find_min #=> nil
633
+ #
634
+ def clear
635
+ @ary.clear
636
+ @heapsize = 0
637
+ @mode = nil
638
+ end
639
+
640
+ def internal_get_priority(loc)
641
+ check_locator(loc)
642
+ locator, priority, subpriority = get_entry(loc.send(:index))
643
+ [priority, subpriority]
644
+ end
645
+ private :internal_get_priority
646
+
647
+ def internal_set_priority(loc, priority, subpriority)
648
+ check_locator(loc)
649
+ if @heapsize <= loc.send(:index)
650
+ set_entry(loc.send(:index), loc, priority, subpriority)
651
+ else
652
+ mode_heapify
653
+ @mode.update_priority(self, @ary, loc, priority, subpriority)
654
+ end
655
+ end
656
+ private :internal_set_priority
657
+
658
+ # insert the locator to the queue.
659
+ #
660
+ # If loc.subpriority is nil, totalcount is used for stability.
661
+ #
662
+ # The locator should not already be inserted in a queue.
663
+ #
664
+ # pd = Depq.new
665
+ # loc = Depq::Locator.new(1)
666
+ # pd.insert_locator loc
667
+ # p pd.delete_min #=> 1
668
+ #
669
+ def insert_locator(loc)
670
+ subpriority = loc.subpriority || default_subpriority
671
+ i = self.size
672
+ priority = loc.send(:internal_inserted, self, i)
673
+ set_entry(i, loc, priority, subpriority)
674
+ @totalcount += 1
675
+ loc
676
+ end
677
+
678
+ # insert the value to the queue.
679
+ #
680
+ # If priority is omitted, the value itself is used as the priority.
681
+ #
682
+ # If subpriority is omitted or nil, totalcount is used for stability.
683
+ #
684
+ # pd = Depq.new
685
+ # pd.insert 3
686
+ # pd.insert 1
687
+ # pd.insert 2
688
+ # p pd.delete_min #=> 1
689
+ # p pd.delete_min #=> 2
690
+ # p pd.delete_min #=> 3
691
+ #
692
+ # pd = Depq.new
693
+ # pd.insert 3, 10
694
+ # pd.insert 1, 20
695
+ # pd.insert 2, 30
696
+ # p pd.delete_min #=> 3
697
+ # p pd.delete_min #=> 1
698
+ # p pd.delete_min #=> 2
699
+ #
700
+ # This method returns a locator which locates the inserted element.
701
+ # It can be used to update the value and priority, or delete the element.
702
+ #
703
+ # pd = Depq.new
704
+ # pd.insert 3
705
+ # loc1 = pd.insert 1
706
+ # loc2 = pd.insert 2
707
+ # pd.insert 4
708
+ # p pd.delete_max #=> 4
709
+ # pd.delete_locator loc1
710
+ # loc2.update 8
711
+ # p pd.delete_max #=> 8
712
+ # p pd.delete_max #=> 3
713
+ # p pd.delete_max #=> nil
714
+ #
715
+ def insert(value, priority=value, subpriority=nil)
716
+ loc = Locator.new(value, priority, subpriority)
717
+ insert_locator(loc)
718
+ end
719
+ alias add insert
720
+ alias put insert
721
+ alias enqueue insert
722
+ alias enq insert
723
+ alias << insert
724
+
725
+ # insert all values in iter.
726
+ #
727
+ # The argument, iter, should have each method.
728
+ #
729
+ # This method returns nil.
730
+ #
731
+ # pd = Depq.new
732
+ # pd.insert_all [3,1,2]
733
+ # p pd.delete_min #=> 1
734
+ # p pd.delete_min #=> 2
735
+ # p pd.delete_min #=> 3
736
+ # p pd.delete_min #=> nil
737
+ #
738
+ def insert_all(iter)
739
+ iter.each {|v|
740
+ insert v
741
+ }
742
+ nil
743
+ end
744
+
745
+ # return the locator for the minimum element.
746
+ # This method returns nil if the queue is empty.
747
+ #
748
+ # This method doesn't delete the element from the queue.
749
+ #
750
+ # pd = Depq.new
751
+ # p pd.find_min_locator #=> nil
752
+ # pd.insert 3
753
+ # pd.insert 1
754
+ # pd.insert 2
755
+ # p pd.find_min_locator #=> #<Depq::Locator: 1>
756
+ #
757
+ def find_min_locator
758
+ return nil if empty?
759
+ min_mode
760
+ @mode.find_min_locator(@ary)
761
+ end
762
+
763
+ # return the minimum value with its priority.
764
+ # This method returns nil if the queue is empty.
765
+ #
766
+ # This method doesn't delete the element from the queue.
767
+ #
768
+ # pd = Depq.new
769
+ # p pd.find_min_priority #=> nil
770
+ # pd.insert "durian", 1
771
+ # pd.insert "banana", 3
772
+ # pd.insert "melon", 2
773
+ # p pd.find_min_priority #=> ["durian", 1]
774
+ # pd.clear
775
+ # p pd.find_min_priority #=> nil
776
+ #
777
+ def find_min_priority
778
+ loc = find_min_locator and [loc.value, loc.priority]
779
+ end
780
+
781
+ # return the minimum value.
782
+ # This method returns nil if the queue is empty.
783
+ #
784
+ # This method doesn't delete the element from the queue.
785
+ #
786
+ # pd = Depq.new
787
+ # p pd.find_min #=> nil
788
+ # pd.insert 3
789
+ # pd.insert 1
790
+ # pd.insert 2
791
+ # p pd.find_min #=> 1
792
+ #
793
+ def find_min
794
+ loc = find_min_locator and loc.value
795
+ end
796
+ alias min find_min
797
+ alias first find_min
798
+
799
+ # return the locator for the maximum element.
800
+ # This method returns nil if the queue is empty.
801
+ #
802
+ # This method doesn't delete the element from the queue.
803
+ #
804
+ # pd = Depq.new
805
+ # p pd.find_max_locator #=> nil
806
+ # pd.insert 3
807
+ # pd.insert 1
808
+ # pd.insert 2
809
+ # p pd.find_max_locator #=> #<Depq::Locator: 3>
810
+ #
811
+ def find_max_locator
812
+ return nil if empty?
813
+ max_mode
814
+ @mode.find_max_locator(@ary)
815
+ end
816
+
817
+ # return the maximum value with its priority.
818
+ # This method returns nil if the queue is empty.
819
+ #
820
+ # This method doesn't delete the element from the queue.
821
+ #
822
+ # pd = Depq.new
823
+ # p pd.find_max_priority #=> nil
824
+ # pd.insert "durian", 1
825
+ # pd.insert "banana", 3
826
+ # pd.insert "melon", 2
827
+ # p pd.find_max_priority #=> ["banana", 3]
828
+ # pd.clear
829
+ # p pd.find_max_priority #=> nil
830
+ #
831
+ def find_max_priority
832
+ loc = find_max_locator and [loc.value, loc.priority]
833
+ end
834
+
835
+ # returns the maximum value.
836
+ # This method returns nil if the queue is empty.
837
+ #
838
+ # This method doesn't delete the element from the queue.
839
+ #
840
+ # pd = Depq.new
841
+ # p pd.find_max #=> nil
842
+ # pd.insert 3
843
+ # pd.insert 1
844
+ # pd.insert 2
845
+ # p pd.find_max #=> 3
846
+ #
847
+ def find_max
848
+ loc = find_max_locator and loc.value
849
+ end
850
+ alias max find_max
851
+ alias last find_max
852
+
853
+ # returns the locators for the minimum and maximum element as a two-element array.
854
+ # If the queue is empty, [nil, nil] is returned.
855
+ #
856
+ # pd = Depq.new
857
+ # p pd.find_minmax_locator #=> [nil, nil]
858
+ # pd.insert 3
859
+ # pd.insert 1
860
+ # pd.insert 2
861
+ # p pd.find_minmax_locator #=> [#<Depq::Locator: 1>, #<Depq::Locator: 3>]
862
+ #
863
+ def find_minmax_locator
864
+ return [nil, nil] if empty?
865
+ case @mode
866
+ when :min
867
+ loc1 = find_min_locator
868
+ loc2 = loc1
869
+ self.each_locator {|loc|
870
+ if compare_for_max(loc2.priority, loc2.subpriority, loc.priority, loc.subpriority) < 0
871
+ loc2 = loc
872
+ end
873
+ }
874
+ when :max
875
+ loc2 = find_max_locator
876
+ loc1 = loc2
877
+ self.each_locator {|loc|
878
+ if compare_for_min(loc1.priority, loc1.subpriority, loc.priority, loc.subpriority) > 0
879
+ loc1 = loc
880
+ end
881
+ }
882
+ else
883
+ loc1 = loc2 = nil
884
+ self.each_locator {|loc|
885
+ if loc1 == nil || compare_for_min(loc1.priority, loc1.subpriority, loc.priority, loc.subpriority) > 0
886
+ loc1 = loc
887
+ end
888
+ if loc2 == nil || compare_for_max(loc2.priority, loc2.subpriority, loc.priority, loc.subpriority) < 0
889
+ loc2 = loc
890
+ end
891
+ }
892
+ end
893
+ [loc1, loc2]
894
+ end
895
+
896
+ # returns the minimum and maximum value as a two-element array.
897
+ # If the queue is empty, [nil, nil] is returned.
898
+ #
899
+ # pd = Depq.new
900
+ # p pd.find_minmax #=> [nil, nil]
901
+ # pd.insert 3
902
+ # pd.insert 1
903
+ # pd.insert 2
904
+ # p pd.find_minmax #=> [1, 3]
905
+ #
906
+ def find_minmax
907
+ loc1, loc2 = self.find_minmax_locator
908
+ [loc1 && loc1.value, loc2 && loc2.value]
909
+ end
910
+ alias minmax find_minmax
911
+
912
+ # delete the element specified by the locator.
913
+ #
914
+ # pd = Depq.new
915
+ # pd.insert 3
916
+ # loc = pd.insert 2
917
+ # pd.insert 1
918
+ # pd.delete_locator loc
919
+ # p pd.delete_min #=> 1
920
+ # p pd.delete_min #=> 3
921
+ # p pd.delete_min #=> nil
922
+ #
923
+ def delete_locator(loc)
924
+ check_locator(loc)
925
+ if @heapsize <= loc.send(:index)
926
+ _, priority, subpriority = delete_entry(loc.send(:index))
927
+ loc.send(:index).upto(self.size-1) {|i|
928
+ loc2, _ = get_entry(i)
929
+ loc2.send(:index=, i)
930
+ }
931
+ loc.send(:internal_deleted, priority, subpriority)
932
+ loc
933
+ else
934
+ mode_heapify
935
+ @heapsize = @mode.delete_locator(self, @ary, loc)
936
+ loc
937
+ end
938
+ end
939
+
940
+ # delete the minimum element in the queue and returns the locator.
941
+ #
942
+ # This method returns the locator for the deleted element.
943
+ # nil is returned if the queue is empty.
944
+ #
945
+ # pd = Depq.new
946
+ # pd.insert 2
947
+ # pd.insert 1
948
+ # pd.insert 3
949
+ # p pd.delete_min_locator #=> #<Depq::Locator: 1 (no queue)>
950
+ # p pd.delete_min_locator #=> #<Depq::Locator: 2 (no queue)>
951
+ # p pd.delete_min_locator #=> #<Depq::Locator: 3 (no queue)>
952
+ # p pd.delete_min_locator #=> nil
953
+ #
954
+ def delete_min_locator
955
+ return nil if empty?
956
+ min_mode
957
+ loc = @mode.find_min_locator(@ary)
958
+ @heapsize = @mode.delete_locator(self, @ary, loc)
959
+ loc
960
+ end
961
+
962
+ # delete the minimum element in the queue and returns the value and its priority.
963
+ #
964
+ # This method returns an array which contains the value and its priority
965
+ # of the deleted element.
966
+ # nil is returned if the queue is empty.
967
+ #
968
+ # pd = Depq.new
969
+ # pd.insert "durian", 1
970
+ # pd.insert "banana", 3
971
+ # pd.insert "melon", 2
972
+ # p pd.delete_min_priority #=> ["durian", 1]
973
+ # p pd.delete_min_priority #=> ["melon", 2]
974
+ # p pd.delete_min_priority #=> ["banana", 3]
975
+ # p pd.delete_min_priority #=> nil
976
+ #
977
+ def delete_min_priority
978
+ loc = delete_min_locator
979
+ return nil unless loc
980
+ [loc.value, loc.priority]
981
+ end
982
+
983
+ # delete the minimum element in the queue and returns the value.
984
+ #
985
+ # This method returns the value of the deleted element.
986
+ # nil is returned if the queue is empty.
987
+ #
988
+ # pd = Depq.new
989
+ # pd.insert 3
990
+ # pd.insert 1
991
+ # pd.insert 2
992
+ # p pd.delete_min #=> 1
993
+ # p pd.delete_min #=> 2
994
+ # p pd.delete_min #=> 3
995
+ # p pd.delete_min #=> nil
996
+ #
997
+ def delete_min
998
+ loc = delete_min_locator
999
+ loc and loc.value
1000
+ end
1001
+ alias shift delete_min
1002
+ alias dequeue delete_min
1003
+ alias deq delete_min
1004
+
1005
+ # delete the maximum element in the queue and returns the locator.
1006
+ #
1007
+ # This method returns the locator for the deleted element.
1008
+ # nil is returned if the queue is empty.
1009
+ #
1010
+ # pd = Depq.new
1011
+ # pd.insert 2
1012
+ # pd.insert 1
1013
+ # pd.insert 3
1014
+ # p pd.delete_max_locator #=> #<Depq::Locator: 3 (no queue)>
1015
+ # p pd.delete_max_locator #=> #<Depq::Locator: 2 (no queue)>
1016
+ # p pd.delete_max_locator #=> #<Depq::Locator: 1 (no queue)>
1017
+ # p pd.delete_max_locator #=> nil
1018
+ #
1019
+ def delete_max_locator
1020
+ return nil if empty?
1021
+ max_mode
1022
+ loc = @mode.find_max_locator(@ary)
1023
+ @heapsize = @mode.delete_locator(self, @ary, loc)
1024
+ loc
1025
+ end
1026
+
1027
+ # delete the maximum element in the queue and returns the value and its priority.
1028
+ #
1029
+ # This method returns an array which contains the value and its priority
1030
+ # of the deleted element.
1031
+ # nil is returned if the queue is empty.
1032
+ #
1033
+ # pd = Depq.new
1034
+ # pd.insert "durian", 1
1035
+ # pd.insert "banana", 3
1036
+ # pd.insert "melon", 2
1037
+ # p pd.delete_max_priority #=> ["banana", 3]
1038
+ # p pd.delete_max_priority #=> ["melon", 2]
1039
+ # p pd.delete_max_priority #=> ["durian", 1]
1040
+ # p pd.delete_max_priority #=> nil
1041
+ #
1042
+ def delete_max_priority
1043
+ loc = delete_max_locator
1044
+ return nil unless loc
1045
+ [loc.value, loc.priority]
1046
+ end
1047
+
1048
+ # delete the maximum element in the queue and returns the value.
1049
+ #
1050
+ # This method returns the value of the deleted element.
1051
+ # nil is returned if the queue is empty.
1052
+ #
1053
+ # pd = Depq.new
1054
+ # pd.insert 3
1055
+ # pd.insert 1
1056
+ # pd.insert 2
1057
+ # p pd.delete_max #=> 3
1058
+ # p pd.delete_max #=> 2
1059
+ # p pd.delete_max #=> 1
1060
+ # p pd.delete_max #=> nil
1061
+ #
1062
+ def delete_max
1063
+ loc = delete_max_locator
1064
+ loc and loc.value
1065
+ end
1066
+ alias pop delete_max
1067
+
1068
+ # delete an element in the queue and returns the locator.
1069
+ # The element is choosen for fast deletion.
1070
+ #
1071
+ # This method returns the locator for the deleted element.
1072
+ # nil is returned if the queue is empty.
1073
+ #
1074
+ # pd = Depq.new
1075
+ # pd.insert 1
1076
+ # pd.insert 4
1077
+ # pd.insert 3
1078
+ # p pd.delete_unspecified_locator #=> #<Depq::Locator: 3 (no queue)>
1079
+ # p pd.delete_unspecified_locator #=> #<Depq::Locator: 4 (no queue)>
1080
+ # p pd.delete_unspecified_locator #=> #<Depq::Locator: 1 (no queue)>
1081
+ # p pd.delete_unspecified_locator #=> nil
1082
+ #
1083
+ def delete_unspecified_locator
1084
+ return nil if empty?
1085
+ loc, _ = get_entry(self.size-1)
1086
+ delete_locator(loc)
1087
+ end
1088
+
1089
+ # delete an element in the queue and returns the value and its priority.
1090
+ # The element is choosen for fast deletion.
1091
+ #
1092
+ # This method returns an array which contains the value and its priority
1093
+ # of the deleted element.
1094
+ # nil is returned if the queue is empty.
1095
+ #
1096
+ # pd = Depq.new
1097
+ # pd.insert "durian", 1
1098
+ # pd.insert "banana", 3
1099
+ # pd.insert "melon", 2
1100
+ # p pd.delete_unspecified_priority #=> ["melon", 2]
1101
+ # p pd.delete_unspecified_priority #=> ["banana", 3]
1102
+ # p pd.delete_unspecified_priority #=> ["durian", 1]
1103
+ # p pd.delete_unspecified_priority #=> nil
1104
+ #
1105
+ def delete_unspecified_priority
1106
+ loc = delete_unspecified_locator
1107
+ return nil unless loc
1108
+ [loc.value, loc.priority]
1109
+ end
1110
+
1111
+ # delete an element in the queue and returns the value.
1112
+ # The element is choosen for fast deletion.
1113
+ #
1114
+ # This method returns the value of the deleted element.
1115
+ # nil is returned if the queue is empty.
1116
+ #
1117
+ # pd = Depq.new
1118
+ # pd.insert 1
1119
+ # pd.insert 4
1120
+ # pd.insert 3
1121
+ # p pd.delete_unspecified #=> 3
1122
+ # p pd.delete_unspecified #=> 4
1123
+ # p pd.delete_unspecified #=> 1
1124
+ # p pd.delete_unspecified #=> nil
1125
+ #
1126
+ def delete_unspecified
1127
+ loc = delete_unspecified_locator
1128
+ return nil unless loc
1129
+ loc.value
1130
+ end
1131
+
1132
+ # iterate over the locators in the queue.
1133
+ #
1134
+ # The iteration order is unspecified.
1135
+ #
1136
+ # pd = Depq.new
1137
+ # pd.insert 3
1138
+ # pd.insert 1
1139
+ # pd.insert 2
1140
+ # p pd.delete_min #=> 1
1141
+ # pd.each_locator {|v|
1142
+ # p v #=> #<Depq::Locator: 2>, #<Depq::Locator: 3>
1143
+ # }
1144
+ #
1145
+ def each_locator # :yield: locator
1146
+ each_entry {|locator, priority|
1147
+ yield locator
1148
+ }
1149
+ nil
1150
+ end
1151
+
1152
+ # iterate over the values and priorities in the queue.
1153
+ #
1154
+ # pd = Depq.new
1155
+ # pd.insert "durian", 1
1156
+ # pd.insert "banana", 3
1157
+ # pd.insert "melon", 2
1158
+ # pd.each_with_priority {|val, priority|
1159
+ # p [val, priority]
1160
+ # }
1161
+ # #=> ["durian", 1]
1162
+ # # ["banana", 3]
1163
+ # # ["melon", 2]
1164
+ #
1165
+ def each_with_priority # :yield: value, priority
1166
+ each_entry {|locator, priority|
1167
+ yield locator.value, priority
1168
+ }
1169
+ nil
1170
+ end
1171
+
1172
+ # iterate over the values in the queue.
1173
+ #
1174
+ # The iteration order is unspecified.
1175
+ #
1176
+ # pd = Depq.new
1177
+ # pd.insert 3
1178
+ # pd.insert 1
1179
+ # pd.insert 2
1180
+ # p pd.delete_min #=> 1
1181
+ # pd.each {|v|
1182
+ # p v #=> 2, 3
1183
+ # }
1184
+ #
1185
+ def each # :yield: value
1186
+ each_entry {|locator, priority|
1187
+ yield locator.value
1188
+ }
1189
+ nil
1190
+ end
1191
+
1192
+ # returns the largest n elements in iter as an array.
1193
+ #
1194
+ # The result array is ordered from the minimum element.
1195
+ #
1196
+ # p Depq.nlargest(3, [5, 2, 3, 1, 4, 6, 7]) #=> [5, 6, 7]
1197
+ #
1198
+ def Depq.nlargest(n, iter)
1199
+ limit = (n * Math.log(1+n)).ceil
1200
+ limit = 1024 if limit < 1024
1201
+ pd = Depq.new
1202
+ threshold = nil
1203
+ iter.each {|v|
1204
+ if pd.size < n
1205
+ if pd.size == 0
1206
+ threshold = v
1207
+ else
1208
+ threshold = v if (v <=> threshold) < 0
1209
+ end
1210
+ pd.insert v
1211
+ else
1212
+ if (v <=> threshold) > 0
1213
+ pd.insert v
1214
+ if limit < pd.size
1215
+ tmp = []
1216
+ n.times { tmp << pd.delete_max }
1217
+ pd.clear
1218
+ pd.insert_all tmp
1219
+ threshold = tmp.last
1220
+ end
1221
+ end
1222
+ end
1223
+ }
1224
+ n = pd.size if pd.size < n
1225
+ a = []
1226
+ n.times { a << pd.delete_max }
1227
+ a.reverse!
1228
+ a
1229
+ end
1230
+
1231
+ # returns the smallest n elements in iter as an array.
1232
+ #
1233
+ # The result array is ordered from the minimum element.
1234
+ #
1235
+ # p Depq.nsmallest(5, [5, 2, 3, 1, 4, 6, 7]) #=> [1, 2, 3, 4, 5]
1236
+ #
1237
+ def Depq.nsmallest(n, iter)
1238
+ limit = (n * Math.log(1+n)).ceil
1239
+ limit = 1024 if limit < 1024
1240
+ pd = Depq.new
1241
+ threshold = nil
1242
+ iter.each {|v|
1243
+ if pd.size < n
1244
+ if pd.size == 0
1245
+ threshold = v
1246
+ else
1247
+ threshold = v if (v <=> threshold) > 0
1248
+ end
1249
+ pd.insert v
1250
+ else
1251
+ if (v <=> threshold) < 0
1252
+ pd.insert v
1253
+ if limit < pd.size
1254
+ tmp = []
1255
+ n.times { tmp << pd.delete_min }
1256
+ pd.clear
1257
+ pd.insert_all tmp
1258
+ threshold = tmp.last
1259
+ end
1260
+ end
1261
+ end
1262
+ }
1263
+ n = pd.size if pd.size < n
1264
+ a = []
1265
+ n.times {
1266
+ a << pd.delete_min
1267
+ }
1268
+ a
1269
+ end
1270
+
1271
+ # iterates over iterators specified by arguments.
1272
+ #
1273
+ # The iteration order is sorted, from minimum to maximum,
1274
+ # if all the arugment iterators are sorted.
1275
+ #
1276
+ # Depq.merge(1..4, 3..6) {|v| p v }
1277
+ # #=> 1
1278
+ # # 2
1279
+ # # 3
1280
+ # # 3
1281
+ # # 4
1282
+ # # 4
1283
+ # # 5
1284
+ # # 6
1285
+ #
1286
+ def Depq.merge(*iters, &b)
1287
+ pd = Depq.new
1288
+ iters.each {|enum|
1289
+ enum = enum.to_enum unless enum.kind_of? Enumerator
1290
+ begin
1291
+ val = enum.next
1292
+ rescue StopIteration
1293
+ next
1294
+ end
1295
+ pd.insert enum, val
1296
+ }
1297
+ loop = lambda {|y, meth|
1298
+ until pd.empty?
1299
+ loc = pd.find_min_locator
1300
+ enum = loc.value
1301
+ val = loc.priority
1302
+ y.send meth, val
1303
+ begin
1304
+ val = enum.next
1305
+ rescue StopIteration
1306
+ pd.delete_locator loc
1307
+ next
1308
+ end
1309
+ loc.update enum, val
1310
+ end
1311
+ }
1312
+ if block_given?
1313
+ loop.call(b, :call)
1314
+ else
1315
+ Enumerator.new {|y|
1316
+ loop.call(y, :yield)
1317
+ }
1318
+ end
1319
+ end
1320
+
1321
+ # :stopdoc:
1322
+
1323
+ module SimpleHeap
1324
+
1325
+ def size(ary)
1326
+ return ary.size / ARY_SLICE_SIZE
1327
+ end
1328
+
1329
+ def get_entry(ary, i)
1330
+ locator = ary[i*ARY_SLICE_SIZE+0]
1331
+ priority = ary[i*ARY_SLICE_SIZE+1]
1332
+ subpriority = ary[i*ARY_SLICE_SIZE+2]
1333
+ [locator, priority, subpriority]
1334
+ end
1335
+
1336
+ def set_entry(ary, i, locator, priority, subpriority)
1337
+ tmp = Array.new(ARY_SLICE_SIZE)
1338
+ tmp[0] = locator
1339
+ tmp[1] = priority
1340
+ tmp[2] = subpriority
1341
+ ary[i*ARY_SLICE_SIZE, ARY_SLICE_SIZE] = tmp
1342
+ end
1343
+
1344
+ def delete_entry(ary, i)
1345
+ locator, priority, subpriority = ary[i*ARY_SLICE_SIZE, ARY_SLICE_SIZE]
1346
+ ary[i*ARY_SLICE_SIZE, ARY_SLICE_SIZE] = []
1347
+ [locator, priority, subpriority]
1348
+ end
1349
+
1350
+ def each_entry(ary)
1351
+ 0.upto(self.size-1) {|i|
1352
+ ei = ary[i*ARY_SLICE_SIZE+0]
1353
+ pi = ary[i*ARY_SLICE_SIZE+1]
1354
+ si = ary[i*ARY_SLICE_SIZE+2]
1355
+ yield ei, pi, si
1356
+ }
1357
+ end
1358
+
1359
+ def swap(ary, i, j)
1360
+ ei, pi, si = get_entry(ary, i)
1361
+ ej, pj, sj = get_entry(ary, j)
1362
+ set_entry(ary, i, ej, pj, sj)
1363
+ set_entry(ary, j, ei, pi, si)
1364
+ ei.send(:index=, j)
1365
+ ej.send(:index=, i)
1366
+ end
1367
+
1368
+ def upheap(pd, ary, j)
1369
+ while true
1370
+ return if j <= 0
1371
+ i = (j-1) >> 1
1372
+ return if upper?(pd, ary, i, j)
1373
+ swap(ary, j, i)
1374
+ j = i
1375
+ end
1376
+ end
1377
+
1378
+ def downheap(pd, ary, i)
1379
+ while true
1380
+ j = i*2+1
1381
+ k = i*2+2
1382
+ return if size(ary) <= j
1383
+ if size(ary) == k
1384
+ return if upper?(pd, ary, i, j)
1385
+ swap(ary, i, j)
1386
+ i = j
1387
+ else
1388
+ return if upper?(pd, ary, i, j) && upper?(pd, ary, i, k)
1389
+ loc = upper?(pd, ary, j, k) ? j : k
1390
+ swap(ary, i, loc)
1391
+ i = loc
1392
+ end
1393
+ end
1394
+ end
1395
+
1396
+ def find_top_locator(ary)
1397
+ loc, _ = get_entry(ary, 0)
1398
+ loc
1399
+ end
1400
+
1401
+ def delete_locator(pd, ary, loc)
1402
+ i = loc.send(:index)
1403
+ _, priority, subpriority = get_entry(ary, i)
1404
+ last = size(ary) - 1
1405
+ loc.send(:internal_deleted, priority, subpriority)
1406
+ el, pl, sl = delete_entry(ary, last)
1407
+ if i != last
1408
+ set_entry(ary, i, el, pl, sl)
1409
+ el.send(:index=, i)
1410
+ downheap(pd, ary, i)
1411
+ end
1412
+ size(ary)
1413
+ end
1414
+
1415
+ def heapify(pd, ary, heapsize=0)
1416
+ # compare number of data movements in worst case.
1417
+ # choose a way for less data movements.
1418
+ #
1419
+ # current size = ary.size / ARY_SLICE_SIZE = n
1420
+ # addition size = n - heapsize = m
1421
+ # heap tree height = Math.log2(n+1) = h
1422
+ #
1423
+ # worst data movements using downheap:
1424
+ # - bottom elements cannot move.
1425
+ # - elements above can move 1 time.
1426
+ # - ...
1427
+ # - top element can move h-1 times.
1428
+ #
1429
+ # 1*2**(h-2) + 2*2**(h-3) + 3*2**(h-4) + ... + (h-1)*2**0
1430
+ # = sum i*2**(h-1-i), i=1 to h-1
1431
+ # = 2**h - h - 1
1432
+ # = n - h
1433
+ #
1434
+ # worst data movements using upheap
1435
+ # - bottom elements can move h-1 times.
1436
+ # - n/2 elements are placed at bottom.
1437
+ # - approximate all elements are at bottom...
1438
+ #
1439
+ # (h-1) * m
1440
+ #
1441
+ # condition to use downheap:
1442
+ #
1443
+ # n - h < (h-1) * m
1444
+ # n - 1 < (h-1) * (m+1)
1445
+ #
1446
+ currentsize = size(ary)
1447
+ h = Math.log(currentsize+1)/Math.log(2)
1448
+ if currentsize - 1 < (h - 1) * (currentsize - heapsize + 1)
1449
+ n = (currentsize - 2) / 2
1450
+ n.downto(0) {|i|
1451
+ downheap(pd, ary, i)
1452
+ }
1453
+ else
1454
+ heapsize.upto(currentsize-1) {|i|
1455
+ upheap(pd, ary, i)
1456
+ }
1457
+ end
1458
+ currentsize
1459
+ end
1460
+ end
1461
+
1462
+ module MinHeap
1463
+ end
1464
+ class << MinHeap
1465
+ include SimpleHeap
1466
+
1467
+ def upper?(pd, ary, i, j)
1468
+ ei, pi, si = get_entry(ary, i)
1469
+ ej, pj, sj = get_entry(ary, j)
1470
+ pd.send(:compare_for_min, pi, si, pj, sj) <= 0
1471
+ end
1472
+
1473
+ def update_priority(pd, ary, loc, priority, subpriority)
1474
+ i = loc.send(:index)
1475
+ ei, pi, si = get_entry(ary, i)
1476
+ cmp = pd.send(:compare_for_min, pi, si, priority, subpriority)
1477
+ set_entry(ary, i, ei, priority, subpriority)
1478
+ if cmp < 0
1479
+ # loc.priority < priority
1480
+ downheap(pd, ary, i)
1481
+ elsif cmp > 0
1482
+ # loc.priority > priority
1483
+ upheap(pd, ary, i)
1484
+ end
1485
+ end
1486
+
1487
+ alias find_min_locator find_top_locator
1488
+ end
1489
+
1490
+ module MaxHeap
1491
+ end
1492
+ class << MaxHeap
1493
+ include SimpleHeap
1494
+
1495
+ def upper?(pd, ary, i, j)
1496
+ ei, pi, si = get_entry(ary, i)
1497
+ ej, pj, sj = get_entry(ary, j)
1498
+ pd.send(:compare_for_max, pi, si, pj, sj) >= 0
1499
+ end
1500
+
1501
+ def update_priority(pd, ary, loc, priority, subpriority)
1502
+ i = loc.send(:index)
1503
+ ei, pi, si = get_entry(ary, i)
1504
+ subpriority ||= si
1505
+ cmp = pd.send(:compare_for_max, pi, si, priority, subpriority)
1506
+ set_entry(ary, i, ei, priority, subpriority)
1507
+ if cmp < 0
1508
+ # loc.priority < priority
1509
+ upheap(pd, ary, i)
1510
+ elsif cmp > 0
1511
+ # loc.priority > priority
1512
+ downheap(pd, ary, i)
1513
+ end
1514
+ end
1515
+
1516
+ alias find_max_locator find_top_locator
1517
+ end
1518
+
1519
+ # :startdoc:
1520
+ end