priority_queue_cxx 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c2b5bbef541a1b74a11e959cda5b1b11020bda89
4
+ data.tar.gz: 15d62c6289e5d07cacafba804497305b6719c532
5
+ SHA512:
6
+ metadata.gz: 7f14a5601f02ab38049968a222dd01d9403755984f0b91f6365b881cfe5af229173edf16290aa7568e15a87fd343205d965c9f8f2782cf096aed070cbd3ad934
7
+ data.tar.gz: 264b327c4dbc023d21a06868eebe55b76812d6f48b3a9797dd456aeb206766c936fd00ea1d1ae97ac3e58ae70a04a0b891b866ac14da513ae152f52b1ba3015f
data/README.md ADDED
@@ -0,0 +1,294 @@
1
+ # PriorityQueueCxx
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/fast_containers.png)](http://badge.fury.io/rb/fast_containers)
4
+
5
+ FastContainers provides a fast implementatin of priority queues for ruby. Speed is achieved by exposing the c++ standard implementation through a light ruby wrapper. As a bigger project, the library may grow a number of containers that are not in the standard ruby library and are presently only available as pure ruby libraries, but presently the library includes a single class named PriorityQueue. More containers will be added as necessity arises. Contributors and feature requests are most welcome.
6
+
7
+ The library exposes a module named 'FastContainers' (to be required using ```require 'fc'```) which provides the PriorityQueue class.
8
+
9
+ ## Installation
10
+
11
+ ```ruby
12
+ gem install 'priority_queue_cxx'
13
+ ```
14
+
15
+ ## Usage Example
16
+
17
+ ```ruby
18
+
19
+ require 'fc'
20
+ q = FastContainers::PriorityQueue.new(:max)
21
+ q.push("largest", 10)
22
+ q.push("smallest", 5)
23
+ q.top # => "largest"
24
+ q.top_key # => 10
25
+ q.pop
26
+ ```
27
+
28
+ ## How fast is it?
29
+
30
+ As far as I know, only one other library (the PriorityQueue gem) provides priority queues implemented as a C extension. This implies that the fc::PriorityQueue is a *lot* faster than most current alternatives and, as shown below, it compares favorably with the mentioned C extension as well.
31
+
32
+ To get an idea about how fast it is, below we provide a comparison of the time needed to push and pop a large number of elements into a priority queue. Each experiment compares FastContainers with others priority queues implementations. Since timings varies greatly among different implementations, the number of push/pop performed is chosen so to make the experiments to run for (at most) few minutes.
33
+
34
+ The following table summarizes the outputs, detailed results can be found in the next few sections. All libraries have been installed through the 'gem' command and executed using ruby v. 2.1.0.
35
+
36
+ | library | avg μs per push | avg μs per pop | avg μs per op |
37
+ |:--------|---------:|---------:|---------:|
38
+ | *priority_queue_cxx* | *0.456* | *1.138* | *0.797* |
39
+ | PriorityQueue | 2.09 | 5.186 | 3.638 |
40
+ | em-priority-queue | 3.56 | 8.32 | 5.94 |
41
+ | pqueue | 669.0 | 0.1 | 334.55|
42
+ | algorithms | 2584.6 | 29.6 |1307.1 |
43
+ | priority_queue | 1.4 |19134.6 |9568.0 |
44
+
45
+ where: results are sorted according to "avg μs per op" (higher is better); μs stands for micro seconds; op stands for any operation (push or pop); the figures for priority_queue_cxx has been calculated with the results of experiments with PriorityQueue (the experiment with the highest number of operations).
46
+
47
+
48
+ ### Comparison with [algorithms (0.6.1)](http://rubygems.org/gems/algorithms) (50,000 push/pop)
49
+
50
+ ```ruby
51
+
52
+ require 'fc'
53
+ require 'algorithms'
54
+ require 'benchmark'
55
+
56
+ N = 50_000
57
+ algo_pq = Containers::PriorityQueue.new
58
+ fc_pq = FastContainers::PriorityQueue.new(:min)
59
+
60
+ Benchmark.bm do |bm|
61
+ bm.report('algo:push') { N.times { |n| algo_pq.push(n.to_s, rand) } }
62
+ bm.report('fc:push') { N.times { |n| fc_pq.push(n.to_s, rand) } }
63
+ bm.report('algo:pop') { N.times { algo_pq.pop } }
64
+ bm.report('fc:pop') { N.times { fc_pq.pop } }
65
+ end
66
+ ```
67
+
68
+ Output (reformatted):
69
+
70
+ | | user| system| total| real |
71
+ |:--------|---------:|---------:|---------:|-----------:|
72
+ |algorithms:push|122.200| 7.030|129.230|(129.173)|
73
+ |*fc:push* | *0.020*| *0.000*| *0.020*|*( 0.020)*|
74
+ |algorithms:pop | 1.460| 0.020| 1.480|( 1.476)|
75
+ |*fc:pop* | *0.030*| *0.000*| *0.030*|*( 0.030)*|
76
+
77
+ Summary: FastContainers::PriorityQueues (fc) are *6461.5 times faster* on pushes and *49.3 times faster* on pops.
78
+
79
+
80
+ ### Comparison with [priority_queue (0.2.0)](http://rubygems.org/gems/priority_queue) (50,000 push/pop)
81
+
82
+ ```ruby
83
+ require 'fc'
84
+ require 'priority_queue'
85
+ require 'benchmark'
86
+
87
+ N = 50_000
88
+ pq_pq = PriorityQueue.new
89
+ fc_pq = FastContainers::PriorityQueue.new(:min)
90
+
91
+ Benchmark.bm do |bm|
92
+ bm.report('pq:push') { N.times { |n| pq_pq[rand] << n.to_s } }
93
+ bm.report('fc:push') { N.times { |n| fc_pq.push(n.to_s, rand) } }
94
+ bm.report('pq:pop') { N.times { pq_pq.shift } }
95
+ bm.report('fc:pop') { N.times { fc_pq.pop } }
96
+ end
97
+ ```
98
+
99
+ Output (reformatted):
100
+
101
+ | | user| system| total| real |
102
+ |:--------|---------:|---------:|---------:|-----------:|
103
+ |priority_queue:push | 0.060 | 0.010 | 0.070 |( 0.062593)|
104
+ |*fc:push* | *0.020* | *0.000* | *0.020* |*( 0.018866)*|
105
+ |priority_queue:pop | 948.440 | 8.290 | 956.730 |(956.676601)|
106
+ |*fc:pop* | 0.040 | 0.000 | 0.040 |*( 0.032753)*|
107
+
108
+ Summary: FastContainers::PriorityQueues (fc) are *3.5 times faster* on pushes and *23918.25 times faster* on pops.
109
+
110
+ ### Comparison with [em-priority-queue (1.1.2)](http://rubygems.org/gems/em-priority-queue) (500,000 push/pop)
111
+
112
+ ```ruby
113
+ require 'fc'
114
+ require 'em-priority-queue'
115
+ require 'benchmark'
116
+
117
+ N = 500_000
118
+ em_pq = EM::PriorityQueue.new
119
+ fc_pq = FastContainers::PriorityQueue.new(:min)
120
+
121
+ Benchmark.bm do |bm|
122
+ bm.report('em:push') { N.times { |n| em_pq.push(n.to_s, rand) } }
123
+ bm.report('fc:push') { N.times { |n| fc_pq.push(n.to_s, rand) } }
124
+ bm.report('em:pop') { N.times { em_pq.pop {} } }
125
+ bm.report('fc:pop') { N.times { fc_pq.pop } }
126
+ end
127
+ ```
128
+
129
+ Output (reformatted):
130
+
131
+ | | user| system| total| real |
132
+ |:--------|---------:|---------:|---------:|-----------:|
133
+ |em-priority-queue:push |1.650 |0.130 | 1.780 | ( 1.895794) |
134
+ |*fc:push* |*0.190* |*0.020* | *0.210* | *( 0.224068)* |
135
+ |em-priority-queue:pop |3.980 |0.180 | 4.160 | ( 4.360084) |
136
+ |*fc:pop* |*0.380* |*0.000* | *0.380* | *( 0.381250)* |
137
+
138
+ Summary: FastContainers::PriorityQueue (fc) are *8.5 times faster* on pushes and *10.9 times faster* on pops.
139
+
140
+ ### Comparison with [pqueue (2.0.2)](http://rubygems.org/gems/pqueue) (100,000 push/pop)
141
+
142
+
143
+ ```ruby
144
+ require 'fc'
145
+ require 'pqueue'
146
+ require 'benchmark'
147
+
148
+ N = 100_000
149
+ pq_pq = PQueue.new { |x,y| x[1] <=> y[1] }
150
+ fc_pq = FastContainers::PriorityQueue.new(:min)
151
+
152
+ Benchmark.bm do |bm|
153
+ bm.report('pq:push') { N.times { |n| pq_pq.push([n,rand]) } }
154
+ bm.report('fc:push') { N.times { |n| fc_pq.push(n.to_s, rand) } }
155
+ bm.report('pq:pop') { N.times { pq_pq.pop } }
156
+ bm.report('fc:pop') { N.times { fc_pq.pop } }
157
+ end
158
+ ```
159
+
160
+ Output (reformatted):
161
+
162
+ | | user| system| total| real |
163
+ |:--------|---------:|---------:|---------:|-----------:|
164
+ |pqueue:push | 25.240|41.660 | 66.900| ( 66.871391)|
165
+ |*fc:push* | *0.040* | *0.000* | *0.040*| *( 0.035270)*|
166
+ |pqueue:pop | 0.010 | 0.000 | 0.010| ( 0.018718)|
167
+ |*fc:pop* | *0.070* | *0.000* | *0.070*| *( 0.061138)*|
168
+
169
+ Summary: FastContainers::PriorityQueue (fc) are *1672.5 times faster* on pushes and *7 times slower* on pops.
170
+
171
+ ### Comparison with [PriorityQueue (0.1.2)](https://rubygems.org/gems/PriorityQueue) (5,000,000 push/pop)
172
+
173
+
174
+ ```ruby
175
+ require 'fc'
176
+ require 'priority_queue'
177
+ require 'benchmark'
178
+
179
+ N = 5_000_000
180
+ pq_pq = CPriorityQueue.new
181
+ fc_pq = FastContainers::PriorityQueue.new(:min)
182
+
183
+ Benchmark.bm do |bm|
184
+ bm.report('pq:push') { N.times { |n| pq_pq.push(n.to_s,rand) } }
185
+ bm.report('fc:push') { N.times { |n| fc_pq.push(n.to_s, rand) } }
186
+ bm.report('pq:pop') { N.times { pq_pq.delete_min } }
187
+ bm.report('fc:pop') { N.times { fc_pq.pop } }
188
+ end
189
+ ```
190
+
191
+ Output (reformatted):
192
+
193
+ | | user| system| total| real |
194
+ |:--------|---------:|---------:|---------:|-----------:|
195
+ |PriorityQueue:push | 10.020| 0.430| 10.45|( 10.665449)|
196
+ |*fc:push* | *2.110*| *0.170*| *2.28*|*( 2.452529)*|
197
+ |PriorityQueue:pop | 25.860| 0.070| 25.93|( 25.949438)|
198
+ |*fc:pop* | *5.690*| *0.000*| *5.69*|*( 5.688552)*|
199
+
200
+ Summary: FastContainers::PriorityQueue (fc) are *4.58 times faster* on pushes and *4.54 times faster* on pops.
201
+
202
+ ## Which is the best priority queue implementation for ruby?
203
+
204
+ As it usually happens, the answer is: it depends. The evidence reported above shows that if you are only interested in the speed of push and pop methods, then priority_queue_cxx is a very good candidate. Few other important factors may make other libraries be better suited for your needs. The most glaring one is that priority_queue_cxx implementation (i.e., ```FastContainers::PriorityQueue```) does not support changes of priorities<sup><a id="backref1" href="#notes">1</a></sup>. If your problem requires this feature, the best candidate appears to be [PriorityQueue (0.1.2)](https://rubygems.org/gems/PriorityQueue) library. Also, in making your choice, you may want to consider the fact that not all the presented libraries appear to be actively maintained (although, no one gave any problem at the time of the writing).
205
+
206
+ ## API
207
+
208
+ Here it follows a transcription of the RDoc documentation for the library. I'm adding it here because I've having difficulties in instructing the 'gem' executable to generate the correct files on installation (everything works fine using rdoc from the command line though). Any suggestion about how to solve this problem is *very* welcome.
209
+
210
+ ### FastContainers::PriorityQueue
211
+
212
+ #### Public Class Methods
213
+
214
+ ##### new(queue_kind)
215
+
216
+ Create a new priority queue and returns it. queue_kind specifies whether to build a :min or a :max queue.
217
+
218
+ Example:
219
+
220
+ ```ruby
221
+ pq = FastContainers::PriorityQueue.new(:min)
222
+ ```
223
+
224
+ #### Public Instance Methods
225
+
226
+ ##### each { |obj,priority| ... } → self
227
+
228
+ Iterates through the priority queue yielding each element to the given block. The order of the yielded elements is not defined. Returns self.
229
+
230
+ Example:
231
+
232
+ ```ruby
233
+
234
+ pq.each do |obj,priority|
235
+ puts "Obj #{obj} has priority #{priority}"
236
+ end
237
+ ```
238
+
239
+ ##### next
240
+
241
+ Alias for: [top](#label-top+%E2%86%92+obj)
242
+
243
+ ##### next_key
244
+
245
+ Alias for: [top_key](#label-top_key+%E2%86%92+float)
246
+
247
+ #### second_best_key → float
248
+
249
+ Returns the priority of the second best element in the priority queue.
250
+
251
+ ##### empty?
252
+
253
+ Returns true if the queue is empty
254
+
255
+ ##### pop → self
256
+
257
+ Pops the top most element from the priority queue. Returns self.
258
+
259
+ ##### pop_each { |obj, priority| ... } → self
260
+
261
+ Iterates through the priority queue popping the top element and yielding it to the block. The order of yielded elements is guaranteed to be the priority order. Returns self.
262
+
263
+ Example:
264
+
265
+ ```ruby
266
+ ary = [100, 1, 90, 55, 6]
267
+ ary.each { |x| pq.push(x.to_s, x)}
268
+ ary.pop_each {|obj, priority| print(priority, ',') } # => 1,6,55,90,100,
269
+ ```
270
+
271
+ ##### push(obj,priority) → self
272
+
273
+ Push the obj/priority pair into the queue and returns self.
274
+
275
+ ##### size → num
276
+
277
+ Returns the size of the priority queue
278
+
279
+ ##### top → obj
280
+
281
+ Returns the object at the top of the priority queue.
282
+
283
+ ##### top_key → float
284
+
285
+ Returns the priority of the object at the top of the priority queue.
286
+
287
+ #### Included Modules
288
+
289
+ The class Includes Enumerable, so that standard enumeration based methods (e.g., map, all?, any?, ...) can all be used with this container. Notice that Enumerable methods are based on #each, implying that the order used to iterate through the container is undefined.
290
+
291
+ ## Notes
292
+
293
+ <sup id="ref1">1</sup> It is worth mentioning that, due to how priority queue are implemented by the C++ standard library, this implementation can't efficiently support priority changes. In any case, to support this feature would require important changes in the current API.<a href="#which-is-the-best-priority-queue-implementation-for-ruby" title="back reference">&#8617;</a>
294
+
@@ -0,0 +1,272 @@
1
+ // Copyright (c) 2014 Roberto Esposito
2
+ //
3
+ // Permission is hereby granted, free of charge, to any person obtaining
4
+ // a copy of this software and associated documentation files (the
5
+ // "Software"), to deal in the Software without restriction, including
6
+ // without limitation the rights to use, copy, modify, merge, publish,
7
+ // distribute, sublicense, and/or sell copies of the Software, and to
8
+ // permit persons to whom the Software is furnished to do so, subject to
9
+ // the following conditions:
10
+ //
11
+ // The above copyright notice and this permission notice shall be
12
+ // included in all copies or substantial portions of the Software.
13
+ //
14
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+ #include "ruby.h"
22
+ #include "fc_pq.h"
23
+
24
+ // Defining a space for information and references about the module to be stored internally
25
+ static VALUE FastContainers = Qnil;
26
+ static VALUE PriorityQueue = Qnil;
27
+
28
+ // --------------------------------------------------------------------------------
29
+ // UTILITIES
30
+ // --------------------------------------------------------------------------------
31
+
32
+ static fc_pq::PQueue pq_from_self(VALUE self) {
33
+ fc_pq::PQueue queue;
34
+ Data_Get_Struct(self, struct fc_pq::_PQueue, queue);
35
+
36
+ return queue;
37
+ }
38
+
39
+ static void pq_mark(void *ptr) {
40
+ if(ptr==NULL)
41
+ return;
42
+
43
+ fc_pq::PQueueIterator it = fc_pq::iterator((fc_pq::PQueue)ptr);
44
+ while( !fc_pq::iterator_end(it) ) {
45
+ rb_gc_mark( (VALUE) fc_pq::iterator_get_value(it) );
46
+ it = fc_pq::iterator_next(it);
47
+ }
48
+ fc_pq::iterator_dispose(it);
49
+ }
50
+
51
+ // --------------------------------------------------------------------------------
52
+ // METHODS
53
+ // --------------------------------------------------------------------------------
54
+
55
+ /*
56
+ * call-seq:
57
+ * PriorityQueue.new(queue_kind)
58
+ *
59
+ * Create a new priority queue and returns it.
60
+ * +queue_kind+ specifies whether to build a :min or a :max queue.
61
+ */
62
+ static VALUE pq_new(VALUE klass, VALUE queue_kind) {
63
+ if( TYPE(queue_kind) != T_SYMBOL ) {
64
+ rb_raise(rb_eTypeError, "queue_kind parameter must be a symbol");
65
+ }
66
+
67
+ fc_pq::PQueueKind kind;
68
+
69
+ if( rb_intern("max") == rb_to_id(queue_kind) )
70
+ kind = fc_pq::MAX_QUEUE;
71
+ else if( rb_intern("min") == rb_to_id(queue_kind) )
72
+ kind = fc_pq::MIN_QUEUE;
73
+ else rb_raise(rb_eTypeError, "queue_kind parameter must be either :max or :min");
74
+
75
+ fc_pq::PQueue queue = fc_pq::create(kind);
76
+ VALUE data = Data_Wrap_Struct(klass, pq_mark, fc_pq::destroy, queue);
77
+ rb_obj_call_init(data, 0, NULL);
78
+ return data;
79
+ }
80
+
81
+ /*
82
+ * call-seq:
83
+ * size -> num
84
+ *
85
+ * Returns the size of the priority queue
86
+ */
87
+
88
+ static VALUE pq_size(VALUE self) {
89
+ return INT2NUM(fc_pq::size(pq_from_self(self)));
90
+ }
91
+
92
+ /*
93
+ * call-seq:
94
+ * push(obj,priority) -> self
95
+ *
96
+ * Push the +obj+/+priority+ pair into the queue and returns self.
97
+ */
98
+ static VALUE pq_push(VALUE self, VALUE obj, VALUE priority) {
99
+ fc_pq::push(pq_from_self(self), (void*)obj, NUM2DBL(priority));
100
+ return self;
101
+ }
102
+
103
+ /*
104
+ * call-seq:
105
+ * top -> obj
106
+ *
107
+ * Returns the object at the top of the priority queue.
108
+ */
109
+ static VALUE pq_top(VALUE self) {
110
+ fc_pq::PQueue queue = pq_from_self(self);
111
+ if( fc_pq::empty(queue) ) {
112
+ return Qnil;
113
+ }
114
+
115
+ return (VALUE) fc_pq::top( queue );
116
+ }
117
+
118
+ /*
119
+ * call-seq:
120
+ * top_key -> float
121
+ *
122
+ * Returns the priority of the object at the top of the priority queue.
123
+ */
124
+ static VALUE pq_top_key(VALUE self) {
125
+ fc_pq::PQueue queue = pq_from_self(self);
126
+ if(fc_pq::empty(queue))
127
+ return Qnil;
128
+
129
+ double priority = fc_pq::top_key(queue);
130
+ return DBL2NUM(priority);
131
+ }
132
+
133
+ /*
134
+ * call-seq:
135
+ * second_best_key -> float
136
+ *
137
+ * Returns the priority of the second best element in the priority queue.
138
+ */
139
+ static VALUE pq_second_best_key(VALUE self) {
140
+ fc_pq::PQueue queue = pq_from_self(self);
141
+ if(fc_pq::size(queue) < 2)
142
+ return Qnil;
143
+
144
+ double priority = fc_pq::second_best_key(queue);
145
+ return DBL2NUM(priority);
146
+ }
147
+
148
+ /*
149
+ * call-seq:
150
+ * pop -> obj
151
+ *
152
+ * Pops the top most element from the priority queue.
153
+ * Returns the top object (before the pop).
154
+ */
155
+ static VALUE pq_pop(VALUE self) {
156
+ fc_pq::PQueue queue = pq_from_self(self);
157
+
158
+ if( fc_pq::empty(queue) )
159
+ rb_raise(rb_eRuntimeError, "Pop called on an empty queue");
160
+
161
+ VALUE top = (VALUE) fc_pq::top( queue );
162
+ fc_pq::pop(queue);
163
+
164
+ return top;
165
+ }
166
+
167
+ /*
168
+ * call-seq:
169
+ * empty?
170
+ *
171
+ * Returns true if the queue is empty
172
+ */
173
+
174
+ static VALUE pq_empty(VALUE self) {
175
+ if( fc_pq::empty(pq_from_self(self)) )
176
+ return Qtrue;
177
+ else
178
+ return Qfalse;
179
+ }
180
+
181
+
182
+ /*
183
+ * call-seq:
184
+ * each { |obj,priority| ... } -> self
185
+ *
186
+ * Iterates through the priority queue yielding each element to the given block.
187
+ * The order of the yielded elements is not defined. The given block *must not* change
188
+ * the queue elements order. In case it does the iteration will be aborted and the
189
+ * method will return nil.
190
+ *
191
+ * Returns self.
192
+ */
193
+
194
+ static VALUE pq_each(VALUE self) {
195
+ fc_pq::PQueue queue = pq_from_self(self);
196
+ fc_pq::PQueueIterator iterator;
197
+
198
+ try {
199
+ iterator = fc_pq::iterator(queue);
200
+ while( !fc_pq::iterator_end(iterator) ) {
201
+ VALUE value = (VALUE) fc_pq::iterator_get_value(iterator);
202
+ VALUE num = DBL2NUM(fc_pq::iterator_get_key(iterator));
203
+ rb_yield_values( 2,value, num );
204
+ fc_pq::iterator_next(iterator);
205
+ }
206
+ fc_pq::iterator_dispose(iterator);
207
+ } catch (fc_pq::PQueueException& exception) {
208
+ fc_pq::iterator_dispose(iterator);
209
+ rb_raise(rb_eRuntimeError, "%s", exception.what());
210
+ return Qnil;
211
+ }
212
+
213
+ return self;
214
+ }
215
+
216
+ /*
217
+ * call-seq:
218
+ * pop_each { |obj, priority| ... } -> self
219
+ *
220
+ * Iterates through the priority queue popping the top element and
221
+ * yielding it to the block. The order of yielded elements is guaranteed
222
+ * to be the priority order.
223
+ * Returns self.
224
+ */
225
+
226
+ static VALUE pq_pop_each(VALUE self) {
227
+ fc_pq::PQueue queue= pq_from_self(self);
228
+ while( !fc_pq::empty(queue) ) {
229
+ VALUE value = (VALUE) fc_pq::top(queue);
230
+ double key = fc_pq::top_key(queue);
231
+ fc_pq::pop(queue);
232
+ rb_yield_values(2, value, DBL2NUM(key));
233
+ }
234
+
235
+ return self;
236
+ }
237
+
238
+ // --------------------------------------------------------------------------------
239
+ // INITIALIZATION
240
+ // --------------------------------------------------------------------------------
241
+
242
+ /*
243
+ * Document-module: FastContainers
244
+ * Exposes C++ implementation of some containers not defined in the standard ruby libraries.
245
+ */
246
+
247
+ /*
248
+ * Document-class: FastContainers::PriorityQueue
249
+ * Implements priority queues through a C++ heap (using the standard std::priority_queue class).
250
+ * Includes Enumerable, so that standard enumeration based methods (e.g., map, all?, any?, ...)
251
+ * can all be used with this container. Notice that Enumerable methods are based on #each, implying
252
+ * that the order used to iterate through the container is undefined.
253
+ */
254
+ extern "C" {
255
+ void Init_fast_containers() {
256
+ FastContainers = rb_define_module("FastContainers");
257
+ PriorityQueue = rb_define_class_under(FastContainers, "PriorityQueue", rb_cObject);
258
+ rb_global_variable(&FastContainers);
259
+ rb_global_variable(&PriorityQueue);
260
+
261
+ rb_define_singleton_method(PriorityQueue, "new", RUBY_METHOD_FUNC(pq_new), 1);
262
+ rb_define_method(PriorityQueue, "size", RUBY_METHOD_FUNC(pq_size), 0);
263
+ rb_define_method(PriorityQueue, "push", RUBY_METHOD_FUNC(pq_push), 2);
264
+ rb_define_method(PriorityQueue, "top", RUBY_METHOD_FUNC(pq_top), 0);
265
+ rb_define_method(PriorityQueue, "top_key", RUBY_METHOD_FUNC(pq_top_key), 0);
266
+ rb_define_method(PriorityQueue, "second_best_key", RUBY_METHOD_FUNC(pq_second_best_key), 0);
267
+ rb_define_method(PriorityQueue, "pop", RUBY_METHOD_FUNC(pq_pop), 0);
268
+ rb_define_method(PriorityQueue, "empty?", RUBY_METHOD_FUNC(pq_empty), 0);
269
+ rb_define_method(PriorityQueue, "each", RUBY_METHOD_FUNC(pq_each), 0);
270
+ rb_define_method(PriorityQueue, "pop_each", RUBY_METHOD_FUNC(pq_pop_each), 0);
271
+ }
272
+ }
@@ -0,0 +1,5 @@
1
+ require 'mkmf'
2
+
3
+ extension_name = 'fast_containers'
4
+
5
+ create_makefile(extension_name)
@@ -0,0 +1,158 @@
1
+ // Copyright (c) 2014 Roberto Esposito
2
+ //
3
+ // Permission is hereby granted, free of charge, to any person obtaining
4
+ // a copy of this software and associated documentation files (the
5
+ // "Software"), to deal in the Software without restriction, including
6
+ // without limitation the rights to use, copy, modify, merge, publish,
7
+ // distribute, sublicense, and/or sell copies of the Software, and to
8
+ // permit persons to whom the Software is furnished to do so, subject to
9
+ // the following conditions:
10
+ //
11
+ // The above copyright notice and this permission notice shall be
12
+ // included in all copies or substantial portions of the Software.
13
+ //
14
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+ #include "fc_pq.h"
22
+ #include <iostream>
23
+
24
+ namespace fc_pq {
25
+
26
+ typedef std::pair<void*,double> PQElem;
27
+
28
+ class PairsComparator
29
+ {
30
+ bool reverse;
31
+ public:
32
+ PairsComparator(PQueueKind kind) {reverse=(kind==MIN_QUEUE);}
33
+ bool operator() (const PQElem& lhs, const PQElem& rhs) const
34
+ {
35
+ if (reverse) return (lhs.second>rhs.second);
36
+ else return (lhs.second<rhs.second);
37
+ }
38
+ };
39
+
40
+ // --------------------------------------
41
+ // PQueue
42
+ // --------------------------------------
43
+
44
+ typedef std::vector<PQElem> PQueueStorage;
45
+ typedef unsigned int PQueueStorageVersion;
46
+
47
+ typedef struct _PQueue {
48
+ PQueueStorage storage;
49
+ PairsComparator comparator;
50
+ PQueueStorageVersion version;
51
+
52
+ _PQueue(PQueueKind kind) : comparator(kind), version(0) { }
53
+ }* PQueue;
54
+
55
+ void destroy(PQueue q){
56
+ delete q;
57
+ }
58
+
59
+ PQueue create(PQueueKind kind) {
60
+ return new _PQueue(kind);
61
+ }
62
+
63
+ /* Getting the size of the container */
64
+ unsigned int size(PQueue q) {
65
+ return q->storage.size();
66
+ }
67
+
68
+
69
+ void push(PQueue q, void* value, double priority) {
70
+ q->version++;
71
+ q->storage.push_back(PQElem(value, priority));
72
+ push_heap(q->storage.begin(), q->storage.end(), q->comparator);
73
+ }
74
+
75
+ void* top(PQueue q) {
76
+ return q->storage.at(0).first;
77
+ }
78
+
79
+ double top_key(PQueue q) {
80
+ return q->storage.at(0).second;
81
+ }
82
+
83
+ double second_best_key(PQueue q) {
84
+ if(q->storage.size()==2)
85
+ return q->storage.at(1).second;
86
+
87
+ double key1 = q->storage.at(1).second;
88
+ double key2 = q->storage.at(2).second;
89
+ if( key1 > key2 ) {
90
+ return key1;
91
+ } else {
92
+ return key2;
93
+ }
94
+ }
95
+
96
+ void pop(PQueue q) {
97
+ q->version++;
98
+ pop_heap(q->storage.begin(), q->storage.end(), q->comparator);
99
+ q->storage.pop_back();
100
+ }
101
+
102
+ bool empty(PQueue q) {
103
+ return q->storage.empty();
104
+ }
105
+
106
+ // --------------------------------------
107
+ // Iterator
108
+ // --------------------------------------
109
+
110
+
111
+ typedef struct _PQueueIterator {
112
+ PQueueStorage::const_iterator iterator;
113
+ PQueue pqueue;
114
+ PQueueStorage* storage;
115
+ PQueueStorageVersion version;
116
+
117
+ _PQueueIterator(PQueue q) : iterator(q->storage.begin()), pqueue(q), storage(&q->storage), version(q->version)
118
+ { }
119
+
120
+ void checkVersion() throw(PQueueException) {
121
+ if(version != pqueue->version) {
122
+ throw PQueueException("FastContainers::PriorityQueue - a change in the priority queue invalidated the current iterator.");
123
+ }
124
+ }
125
+ } PQueueImplIterator;
126
+ #define QIT(it) ((PQueueImplIterator*)(it))
127
+
128
+ /* Returns a new iterator object */
129
+ PQueueIterator iterator(PQueue q) {
130
+ PQueueImplIterator* it = new PQueueImplIterator(q);
131
+ return it;
132
+ }
133
+
134
+ void iterator_dispose(PQueueIterator it) {
135
+ delete it;
136
+ }
137
+
138
+ /* Returns the value of the current element */
139
+ void* iterator_get_value(PQueueIterator it) {
140
+ return it->iterator->first;
141
+ }
142
+
143
+ /* Returns the priority of the current element */
144
+ double iterator_get_key(PQueueIterator it) {
145
+ return it->iterator->second;
146
+ }
147
+
148
+ /* Moves on to the next element */
149
+ PQueueIterator iterator_next(PQueueIterator it) throw(PQueueException) {
150
+ it->checkVersion();
151
+ it->iterator++;
152
+ return it;
153
+ }
154
+
155
+ bool iterator_end(PQueueIterator it) {
156
+ return it->iterator == it->storage->end();
157
+ }
158
+ }
@@ -0,0 +1,85 @@
1
+ // Copyright (c) 2014 Roberto Esposito
2
+ //
3
+ // Permission is hereby granted, free of charge, to any person obtaining
4
+ // a copy of this software and associated documentation files (the
5
+ // "Software"), to deal in the Software without restriction, including
6
+ // without limitation the rights to use, copy, modify, merge, publish,
7
+ // distribute, sublicense, and/or sell copies of the Software, and to
8
+ // permit persons to whom the Software is furnished to do so, subject to
9
+ // the following conditions:
10
+ //
11
+ // The above copyright notice and this permission notice shall be
12
+ // included in all copies or substantial portions of the Software.
13
+ //
14
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+ #ifndef FC_QUEUE_H_KTY6FH1S
22
+ #define FC_QUEUE_H_KTY6FH1S
23
+
24
+ #include <queue>
25
+
26
+ namespace fc_pq {
27
+ class PQueueException : public std::runtime_error {
28
+ public:
29
+ PQueueException(const char* msg) : std::runtime_error(msg) {}
30
+ };
31
+
32
+ typedef struct _PQueue* PQueue;
33
+ typedef struct _PQueueIterator* PQueueIterator;
34
+ typedef enum { MIN_QUEUE, MAX_QUEUE } PQueueKind;
35
+
36
+ /* Constructor. It defaults to construct a max queue. If true is passed
37
+ it construct a min queue.*/
38
+ PQueue create(PQueueKind kind);
39
+
40
+ /* Destructor */
41
+ void destroy(PQueue q);
42
+
43
+ /* Getting the size of the container */
44
+ unsigned int size(PQueue q);
45
+
46
+ /* Adding elements */
47
+ void push(PQueue q, void* value, double priority);
48
+
49
+ /* Inspecting the queue top (for values) */
50
+ void* top(PQueue q);
51
+
52
+ /* Inspecting the queue top (for priorities) */
53
+ double top_key(PQueue q);
54
+
55
+ /* Returns the priority of the next best element */
56
+ double second_best_key(PQueue q);
57
+
58
+ /* Removing the queue top */
59
+ void pop(PQueue q);
60
+
61
+ /* Returns true if the queue is empty */
62
+ bool empty(PQueue q);
63
+
64
+ /* Returns a new iterator object */
65
+ PQueueIterator iterator(PQueue q);
66
+
67
+ /* Dispose the iterator */
68
+ void iterator_dispose(PQueueIterator it);
69
+
70
+ /* Returns the value of the current element */
71
+ void* iterator_get_value(PQueueIterator it);
72
+
73
+ /* Returns the priority of the current element */
74
+ double iterator_get_key(PQueueIterator it);
75
+
76
+ /* Moves on to the next element */
77
+ PQueueIterator iterator_next(PQueueIterator it) throw(PQueueException);
78
+
79
+ /* Return true if the iterator is already out of the container */
80
+ bool iterator_end(PQueueIterator it);
81
+ }
82
+
83
+ #endif /* end of include guard: FC_QUEUE_H_KTY6FH1S */
84
+
85
+
data/lib/fc.rb ADDED
@@ -0,0 +1,32 @@
1
+ # Copyright (c) 2014 Roberto Esposito
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+ require 'fast_containers'
22
+
23
+ module FastContainers
24
+ VERSION = "0.3.2"
25
+
26
+ class PriorityQueue
27
+ include Enumerable
28
+
29
+ alias_method :next, :top
30
+ alias_method :next_key, :top_key
31
+ end
32
+ end
@@ -0,0 +1,37 @@
1
+ # Copyright (c) 2014 Roberto Esposito
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+ require 'fc'
22
+ require 'algorithms'
23
+ require 'benchmark'
24
+
25
+ # Performs 50.000 pushes and pops in priority queues using the fc and
26
+ # algorithms implementations and reports the time spent.
27
+
28
+ N = 50_000
29
+ algo_pq = Containers::PriorityQueue.new
30
+ fc_pq = FastContainers::PriorityQueue.new(:min)
31
+
32
+ Benchmark.bm do |bm|
33
+ bm.report('algo:push') { N.times { |n| algo_pq.push(n.to_s, rand) } }
34
+ bm.report('fc:push') { N.times { |n| fc_pq.push(n.to_s, rand) } }
35
+ bm.report('algo:pop') { N.times { algo_pq.pop } }
36
+ bm.report('fc:pop') { N.times { fc_pq.pop } }
37
+ end
@@ -0,0 +1,37 @@
1
+ # Copyright (c) 2014 Roberto Esposito
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+ require 'fc'
22
+ require 'priority_queue'
23
+ require 'benchmark'
24
+
25
+ # Performs 5,000,000 pushes and pops in priority queues using the fc and
26
+ # algorithms implementations and reports the time spent.
27
+
28
+ N = 5_000_000
29
+ pq_pq = CPriorityQueue.new
30
+ fc_pq = FastContainers::PriorityQueue.new(:min)
31
+
32
+ Benchmark.bm do |bm|
33
+ bm.report('pq:push') { N.times { |n| pq_pq.push(n.to_s,rand) } }
34
+ bm.report('fc:push') { N.times { |n| fc_pq.push(n.to_s, rand) } }
35
+ bm.report('pq:pop') { N.times { pq_pq.delete_min } }
36
+ bm.report('fc:pop') { N.times { fc_pq.pop } }
37
+ end
@@ -0,0 +1,37 @@
1
+ # Copyright (c) 2014 Roberto Esposito
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+ require 'fc'
22
+ require 'em-priority-queue'
23
+ require 'benchmark'
24
+
25
+ # Performs 50.000 pushes and pops in priority queues using the fc and
26
+ # algorithms implementations and reports the time spent.
27
+
28
+ N = 500_000
29
+ em_pq = EM::PriorityQueue.new
30
+ fc_pq = FastContainers::PriorityQueue.new(:min)
31
+
32
+ Benchmark.bm do |bm|
33
+ bm.report('em:push') { N.times { |n| em_pq.push(n.to_s, rand) } }
34
+ bm.report('fc:push') { N.times { |n| fc_pq.push(n.to_s, rand) } }
35
+ bm.report('em:pop') { N.times { em_pq.pop {} } }
36
+ bm.report('fc:pop') { N.times { fc_pq.pop } }
37
+ end
@@ -0,0 +1,37 @@
1
+ # Copyright (c) 2014 Roberto Esposito
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+ require 'fc'
22
+ require 'pqueue'
23
+ require 'benchmark'
24
+
25
+ # Performs 50.000 pushes and pops in priority queues using the fc and
26
+ # algorithms implementations and reports the time spent.
27
+
28
+ N = 100_000
29
+ pq_pq = PQueue.new { |x,y| x[1] <=> y[1] }
30
+ fc_pq = FastContainers::PriorityQueue.new(:min)
31
+
32
+ Benchmark.bm do |bm|
33
+ bm.report('pq:push') { N.times { |n| pq_pq.push([n,rand]) } }
34
+ bm.report('fc:push') { N.times { |n| fc_pq.push(n.to_s, rand) } }
35
+ bm.report('pq:pop') { N.times { pq_pq.pop } }
36
+ bm.report('fc:pop') { N.times { fc_pq.pop } }
37
+ end
@@ -0,0 +1,37 @@
1
+ # Copyright (c) 2014 Roberto Esposito
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+ require 'fc'
22
+ require 'priority_queue'
23
+ require 'benchmark'
24
+
25
+ # Performs 50.000 pushes and pops in priority queues using the fc and
26
+ # algorithms implementations and reports the time spent.
27
+
28
+ N = 50_000
29
+ pq_pq = PriorityQueue.new
30
+ fc_pq = FastContainers::PriorityQueue.new(:min)
31
+
32
+ Benchmark.bm do |bm|
33
+ bm.report('pq:push') { N.times { |n| pq_pq[rand] << n.to_s } }
34
+ bm.report('fc:push') { N.times { |n| fc_pq.push(n.to_s, rand) } }
35
+ bm.report('pq:pop') { N.times { pq_pq.shift } }
36
+ bm.report('fc:pop') { N.times { fc_pq.pop } }
37
+ end
@@ -0,0 +1,245 @@
1
+ # Copyright (c) 2014 Roberto Esposito
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+ require "minitest/autorun"
22
+ require "fc"
23
+
24
+ class TestFastContainers < MiniTest::Unit::TestCase
25
+ def test_new_object_creation
26
+ assert !FastContainers::PriorityQueue.new(:max).nil?
27
+ end
28
+
29
+ def test_new_handling_of_bad_parameter
30
+ assert_raises(TypeError) do
31
+ FastContainers::PriorityQueue.new(true)
32
+ end
33
+ end
34
+
35
+ def test_push_returns_self
36
+ pq = FastContainers::PriorityQueue.new(:max)
37
+ assert_equal pq, pq.push("test",10)
38
+ end
39
+
40
+ def test_top_on_a_single_element_queue_returns_that_element
41
+ pq = FastContainers::PriorityQueue.new(:max);
42
+ pq.push("test",10)
43
+ assert_equal "test", pq.top
44
+ end
45
+
46
+ def test_next_on_a_single_element_queue_returns_that_element
47
+ pq = FastContainers::PriorityQueue.new(:max);
48
+ pq.push("test",10)
49
+ assert_equal "test", pq.next
50
+ end
51
+
52
+
53
+ def test_top_returns_the_maximal_element_in_a_max_queue
54
+ pq = FastContainers::PriorityQueue.new(:max) # this is a max queue
55
+ pq.push("10", 10)
56
+ pq.push("30", 30)
57
+ pq.push("20", 20)
58
+ assert_equal "30", pq.top
59
+ end
60
+
61
+ def test_next_returns_the_maximal_element_in_a_max_queue
62
+ pq = FastContainers::PriorityQueue.new(:max) # this is a max queue
63
+ pq.push("10", 10)
64
+ pq.push("30", 30)
65
+ pq.push("20", 20)
66
+ assert_equal "30", pq.next
67
+ end
68
+
69
+
70
+ def test_top_returns_the_minimal_element_in_a_min_queue
71
+ pq = FastContainers::PriorityQueue.new(:min) # this is a max queue
72
+ pq.push("10", 10)
73
+ pq.push("30", 30)
74
+ pq.push("20", 20)
75
+ assert_equal "10", pq.top
76
+ end
77
+
78
+ def test_next_returns_the_minimal_element_in_a_min_queue
79
+ pq = FastContainers::PriorityQueue.new(:min) # this is a max queue
80
+ pq.push("10", 10)
81
+ pq.push("30", 30)
82
+ pq.push("20", 20)
83
+ assert_equal "10", pq.next
84
+ end
85
+
86
+ def test_top_key_returns_the_top_priority
87
+ pq = FastContainers::PriorityQueue.new(:max) # this is a max queue
88
+ pq.push("10", 10)
89
+ pq.push("30", 30)
90
+ pq.push("20", 20)
91
+ assert_equal 30, pq.top_key
92
+ end
93
+
94
+ def test_next_key_returns_the_top_priority
95
+ pq = FastContainers::PriorityQueue.new(:max) # this is a max queue
96
+ pq.push("10", 10)
97
+ pq.push("30", 30)
98
+ pq.push("20", 20)
99
+ assert_equal 30, pq.next_key
100
+ end
101
+
102
+
103
+ def test_pop_removes_an_element_from_the_top
104
+ pq = FastContainers::PriorityQueue.new(:max) # this is a max queue
105
+ pq.push("10", 10)
106
+ pq.push("30", 30)
107
+ pq.push("20", 20)
108
+ extracted = pq.pop
109
+
110
+ assert_equal 20, pq.top_key
111
+ assert_equal "30", extracted
112
+ end
113
+
114
+ def test_pop_raises_an_exception_if_the_queue_is_empty
115
+ pq = FastContainers::PriorityQueue.new(:max) # this is a max queue
116
+ pq.push("10", 10)
117
+ pq.pop
118
+ assert_raises(RuntimeError) {
119
+ pq.pop
120
+ }
121
+ end
122
+
123
+ def test_empty_returns_false_if_the_queue_is_not_empty
124
+ pq = FastContainers::PriorityQueue.new(:max) # this is a max queue
125
+ pq.push("10", 10)
126
+ assert !pq.empty?
127
+ end
128
+
129
+ def test_empty_returns_true_if_the_queue_is_empty
130
+ pq = FastContainers::PriorityQueue.new(:max) # this is a max queue
131
+ assert pq.empty?
132
+ end
133
+
134
+ def test_size_on_empty_queue
135
+ pq = FastContainers::PriorityQueue.new(:max)
136
+ assert_equal 0, pq.size
137
+ end
138
+
139
+ def test_size_on_non_empty_queue
140
+ pq = FastContainers::PriorityQueue.new(:max)
141
+ pq.push("x",10);
142
+ pq.push("y",20);
143
+ pq.push("z",30);
144
+ assert_equal 3, pq.size
145
+ end
146
+
147
+ def test_enumerable
148
+ pq = FastContainers::PriorityQueue.new(:max)
149
+ pq.push(1,10);
150
+ pq.push(2,20);
151
+ pq.push(3,30);
152
+ sum_o = 0
153
+ sum_p = 0
154
+ pq.map { |o,p| sum_o+=o; sum_p+=p }
155
+ assert_equal 6, sum_o
156
+ assert_equal 60, sum_p
157
+ end
158
+
159
+ def test_top_key_on_empty_queues
160
+ pq = FastContainers::PriorityQueue.new(:max)
161
+ assert_nil pq.top_key
162
+ end
163
+
164
+ def test_top_on_empty_queues
165
+ pq = FastContainers::PriorityQueue.new(:max)
166
+ assert_nil pq.top
167
+ end
168
+
169
+ def test_second_best_key
170
+ pq = FastContainers::PriorityQueue.new(:max)
171
+ pq.push("x", 100);
172
+ pq.push("y", 80);
173
+ pq.push("z", 40);
174
+ pq.push("w", 60);
175
+ pq.push("i", 90);
176
+ pq.push("j", 95);
177
+
178
+ assert_equal pq.second_best_key, 95
179
+ end
180
+
181
+ def test_second_best_key_on_empty_pq
182
+ pq = FastContainers::PriorityQueue.new(:max)
183
+ assert_nil pq.second_best_key
184
+ end
185
+
186
+ def test_second_best_key_on_size_1_pq
187
+ pq = FastContainers::PriorityQueue.new(:max)
188
+ pq.push("x", 100)
189
+ assert_nil pq.second_best_key
190
+ end
191
+
192
+ def test_second_best_key_on_size_2_pq
193
+ pq = FastContainers::PriorityQueue.new(:max)
194
+ pq.push("x", 100)
195
+ pq.push("x", 80)
196
+
197
+ assert_equal 80, pq.second_best_key
198
+ end
199
+
200
+ def test_each_will_iterate_over_all_elements
201
+ pq = FastContainers::PriorityQueue.new(:max);
202
+ pq.push("x", 100);
203
+ pq.push("y", 80);
204
+ pq.push("z", 40);
205
+ pq.push("w", 60);
206
+ pq.push("i", 90);
207
+ pq.push("j", 95);
208
+
209
+ objects = Set.new
210
+
211
+ result = pq.each do |obj, priority|
212
+ objects << obj
213
+ end
214
+
215
+ assert_equal objects, Set.new(["x","y","z","w","i","j"])
216
+ assert_equal pq, result
217
+ end
218
+
219
+ def test_each_will_abort_and_return_nil_if_queue_changes
220
+ pq = FastContainers::PriorityQueue.new(:max);
221
+ pq.push("x", 100);
222
+ pq.push("y", 80);
223
+ pq.push("z", 40);
224
+ pq.push("w", 60);
225
+ pq.push("i", 90);
226
+ pq.push("j", 95);
227
+
228
+ objects = Set.new
229
+
230
+ exception = assert_raises RuntimeError do
231
+ count = 0
232
+ pq.each do |obj, priority|
233
+ if count==3
234
+ pq.push("no way!", 90)
235
+ end
236
+
237
+ count+=1
238
+ objects << obj
239
+ end
240
+ end
241
+
242
+ assert_match /a change in the priority queue invalidated the current iterator/, exception.message
243
+ assert_equal 4, objects.size
244
+ end
245
+ end
metadata ADDED
@@ -0,0 +1,75 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: priority_queue_cxx
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.2
5
+ platform: ruby
6
+ authors:
7
+ - Roberto Esposito
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-03-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake-compiler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description: A thin wrapper around C++ priority queues
28
+ email:
29
+ - boborbt@gmail.com
30
+ executables: []
31
+ extensions:
32
+ - ext/fast_containers/extconf.rb
33
+ extra_rdoc_files:
34
+ - README.md
35
+ files:
36
+ - README.md
37
+ - ext/fast_containers/FastContainers.cpp
38
+ - ext/fast_containers/extconf.rb
39
+ - ext/fast_containers/fc_pq.cpp
40
+ - ext/fast_containers/fc_pq.h
41
+ - lib/fc.rb
42
+ - test/performance/test_fc_vs_algorithms.rb
43
+ - test/performance/test_fc_vs_cpriority_queue.rb
44
+ - test/performance/test_fc_vs_em_priority_queue.rb
45
+ - test/performance/test_fc_vs_pqueue.rb
46
+ - test/performance/test_fc_vs_priority_queue.rb
47
+ - test/test_fast_containers.rb
48
+ homepage: https://github.com/boborbt/fast_containers
49
+ licenses:
50
+ - MIT
51
+ metadata: {}
52
+ post_install_message:
53
+ rdoc_options:
54
+ - "--main"
55
+ - README.md
56
+ require_paths:
57
+ - lib
58
+ required_ruby_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ required_rubygems_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ requirements: []
69
+ rubyforge_project:
70
+ rubygems_version: 2.2.2
71
+ signing_key:
72
+ specification_version: 4
73
+ summary: Fast (c++ wrapper) priority queue implementation for ruby.
74
+ test_files: []
75
+ has_rdoc: