akr-depq 0.1

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.
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 +55 -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