algorithms 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,23 @@
1
+ === June 13, 2008
2
+
3
+ * Implemented Sedgewick's Left Leaning Red Black Tree in C!
4
+
5
+ === June 12, 2008
6
+
7
+ * Implemented Sedgewick's Left Leaning Red Black Tree
8
+
9
+ === June 10, 2008
10
+
11
+ * Implemented merge! for other heaps and heap initialization from an array
12
+ * Implemented Queue
13
+
14
+
15
+ === June 9, 2008
16
+
17
+ * Finished binomial heap implementation
18
+
19
+
20
+ === June 8, 2008
21
+
22
+ * Added Stack
23
+ * Working on heap
@@ -0,0 +1,23 @@
1
+ benchmark.rb
2
+ ext/containers/priority_queue/extconf.rb
3
+ ext/containers/priority_queue/priority_queue.c
4
+ ext/containers/tree_map/extconf.rb
5
+ ext/containers/tree_map/Rakefile
6
+ ext/containers/tree_map/rbtree.c
7
+ History.txt
8
+ lib/algorithms.rb
9
+ lib/containers/hash.rb
10
+ lib/containers/heap.rb
11
+ lib/containers/priority_queue.rb
12
+ lib/containers/queue.rb
13
+ lib/containers/stack.rb
14
+ lib/containers/tree_map.rb
15
+ Manifest
16
+ Rakefile
17
+ README.txt
18
+ spec/heap_spec.rb
19
+ spec/priority_queue_spec.rb
20
+ spec/priority_queue_test.rb
21
+ spec/queue_spec.rb
22
+ spec/stack_spec.rb
23
+ spec/tree_map_spec.rb
@@ -0,0 +1,58 @@
1
+ = algorithms
2
+
3
+ * http://rubyforge.org/projects/algorithms/
4
+
5
+ == DESCRIPTION:
6
+
7
+ Using the right data structure or algorithm for the situation is an important aspect of programming. In computer science literature, many data structures and algorithms have been researched and extensively documented. However, there is still no standard library in Ruby implementing useful structures and algorithms like Red/Black Trees, tries, graphs, different sorting algorithms, etc. This project will create such a library with documentation on when to use a particular structure/algorithm. It will also come with a benchmark suite to compare performance in different situations.
8
+
9
+ == FEATURES/PROBLEMS:
10
+
11
+ Done so far:
12
+ * Heaps (Maximum, Minimum)
13
+ * Priority Queue
14
+ * Stack
15
+ * Queue
16
+ * TreeMap
17
+
18
+ == SYNOPSIS:
19
+
20
+ require 'rubygems'
21
+ require 'algorithms'
22
+
23
+ include Containers
24
+ max_heap = MaxHeap.new
25
+
26
+ == REQUIREMENTS:
27
+
28
+ * Ruby 1.8
29
+ * C compiler for extensions (optional)
30
+
31
+ == INSTALL:
32
+
33
+ * sudo gem install
34
+
35
+ == LICENSE:
36
+
37
+ (The MIT License)
38
+
39
+ Copyright (c) 2008 Kanwei Li
40
+
41
+ Permission is hereby granted, free of charge, to any person obtaining
42
+ a copy of this software and associated documentation files (the
43
+ 'Software'), to deal in the Software without restriction, including
44
+ without limitation the rights to use, copy, modify, merge, publish,
45
+ distribute, sublicense, and/or sell copies of the Software, and to
46
+ permit persons to whom the Software is furnished to do so, subject to
47
+ the following conditions:
48
+
49
+ The above copyright notice and this permission notice shall be
50
+ included in all copies or substantial portions of the Software.
51
+
52
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
53
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
54
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
55
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
56
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
57
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
58
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,17 @@
1
+ require 'rubygems'
2
+ require 'echoe'
3
+
4
+ Echoe.new('algorithms') do |p|
5
+ p.author = 'Kanwei Li'
6
+ p.email = 'kanwei@gmail.com'
7
+ p.summary = 'A library of algorithms and containers.'
8
+ p.url = 'http://rubyforge.org/projects/algorithms/'
9
+ p.version = "0.0.1"
10
+ p.runtime_dependencies = []
11
+ end
12
+
13
+ task :default => :spec
14
+
15
+ task :spec => :compile do
16
+ sh "spec -c spec/"
17
+ end
@@ -0,0 +1,105 @@
1
+
2
+ # Gem::Specification for Algorithms-0.0.1
3
+ # Originally generated by Echoe
4
+
5
+ --- !ruby/object:Gem::Specification
6
+ name: algorithms
7
+ version: !ruby/object:Gem::Version
8
+ version: 0.0.1
9
+ platform: ruby
10
+ authors:
11
+ - Kanwei Li
12
+ autorequire:
13
+ bindir: bin
14
+
15
+ date: 2008-06-25 00:00:00 -04:00
16
+ default_executable:
17
+ dependencies:
18
+ - !ruby/object:Gem::Dependency
19
+ name: echoe
20
+ type: :development
21
+ version_requirement:
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: "0"
27
+ version:
28
+ description: A library of algorithms and containers.
29
+ email: kanwei@gmail.com
30
+ executables: []
31
+
32
+ extensions:
33
+ - ext/containers/priority_queue/extconf.rb
34
+ - ext/containers/tree_map/extconf.rb
35
+ extra_rdoc_files:
36
+ - ext/containers/priority_queue/extconf.rb
37
+ - ext/containers/priority_queue/priority_queue.c
38
+ - ext/containers/tree_map/extconf.rb
39
+ - ext/containers/tree_map/Rakefile
40
+ - ext/containers/tree_map/rbtree.c
41
+ - lib/algorithms.rb
42
+ - lib/containers/hash.rb
43
+ - lib/containers/heap.rb
44
+ - lib/containers/priority_queue.rb
45
+ - lib/containers/queue.rb
46
+ - lib/containers/stack.rb
47
+ - lib/containers/tree_map.rb
48
+ - README.txt
49
+ files:
50
+ - benchmark.rb
51
+ - ext/containers/priority_queue/extconf.rb
52
+ - ext/containers/priority_queue/priority_queue.c
53
+ - ext/containers/tree_map/extconf.rb
54
+ - ext/containers/tree_map/Rakefile
55
+ - ext/containers/tree_map/rbtree.c
56
+ - History.txt
57
+ - lib/algorithms.rb
58
+ - lib/containers/hash.rb
59
+ - lib/containers/heap.rb
60
+ - lib/containers/priority_queue.rb
61
+ - lib/containers/queue.rb
62
+ - lib/containers/stack.rb
63
+ - lib/containers/tree_map.rb
64
+ - Manifest
65
+ - Rakefile
66
+ - README.txt
67
+ - spec/heap_spec.rb
68
+ - spec/priority_queue_spec.rb
69
+ - spec/priority_queue_test.rb
70
+ - spec/queue_spec.rb
71
+ - spec/stack_spec.rb
72
+ - spec/tree_map_spec.rb
73
+ - algorithms.gemspec
74
+ has_rdoc: true
75
+ homepage: http://rubyforge.org/projects/algorithms/
76
+ post_install_message:
77
+ rdoc_options:
78
+ - --line-numbers
79
+ - --inline-source
80
+ - --title
81
+ - Algorithms
82
+ - --main
83
+ - README.txt
84
+ require_paths:
85
+ - lib
86
+ - ext
87
+ required_ruby_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: "0"
92
+ version:
93
+ required_rubygems_version: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - "="
96
+ - !ruby/object:Gem::Version
97
+ version: "1.2"
98
+ version:
99
+ requirements: []
100
+
101
+ rubyforge_project: algorithms
102
+ rubygems_version: 1.2.0
103
+ specification_version: 2
104
+ summary: A library of algorithms and containers.
105
+ test_files: []
@@ -0,0 +1,49 @@
1
+ require 'lib/algorithms'
2
+ require 'benchmark'
3
+ include Benchmark
4
+
5
+ # Benchmark heap
6
+ @random_array = []
7
+ @num_items = 10000
8
+ @num_items.times { |x| @random_array << rand(@num_items) }
9
+ # @heap = DS::MaxHeap.new(@random_array)
10
+
11
+ # benchmark do |bench|
12
+ # bench.report("Array#max:\t ") do
13
+ # @num_items.times { @random_array.delete_at(@random_array.index(@random_array.max)) }
14
+ # end
15
+ # bench.report("MaxHeap:\t ") do
16
+ # @num_items.times { @heap.get_max! }
17
+ # end
18
+ # end
19
+
20
+ # Benchmark Search trees
21
+ @tree = Containers::TreeMap.new
22
+ @ctree = Containers::TreeMap.new
23
+ @hash = Hash.new
24
+
25
+ benchmark do |bench|
26
+ bench.report("Tree: \t") do
27
+ @random_array.each { |x| @tree[x] = x }
28
+ end
29
+
30
+ bench.report("CTree: \t") do
31
+ @random_array.each { |x| @ctree[x] = x }
32
+ end
33
+
34
+ bench.report("Hash: \t") do
35
+ @hash.each { |x| @hash[x] = x }
36
+ end
37
+
38
+ bench.report("Hash: \t") do
39
+ @num_items.times { |n| @hash.has_key?(n) }
40
+ end
41
+
42
+ bench.report("Tree: \t") do
43
+ @num_items.times { |n| @tree.contains?(n) }
44
+ end
45
+
46
+ bench.report("CTree: \t") do
47
+ @num_items.times { |n| @ctree.contains?(n) }
48
+ end
49
+ end
@@ -0,0 +1,4 @@
1
+ require 'mkmf'
2
+ extension_name = "CPriorityQueue"
3
+ dir_config(extension_name)
4
+ create_makefile(extension_name)
@@ -0,0 +1,948 @@
1
+ /*
2
+ * :main:CPriorityQueue
3
+ *
4
+ * Ruby extension implementing a priority queue
5
+ *
6
+ * This is a fibonacci heap priority queue implementation.
7
+ *
8
+ * (c) 2005 Brian Schr�der
9
+ *
10
+ * Please submit bugreports to priority_queue@brian-schroeder.de
11
+ *
12
+ * This extension is under the same license as ruby.
13
+ *
14
+ * Do not hold me reliable for anything that happens to you, your programs or
15
+ * anything else because of this extension. It worked for me, but there is no
16
+ * guarantee it will work for you.
17
+ *
18
+ * Except for using a value except of a void* the priority queue c-code is ruby
19
+ * agnostic.
20
+ *
21
+ * Modifications by Kanwei Li
22
+ */
23
+ #include <stdlib.h>
24
+ #include <stdio.h>
25
+ #include "ruby.h"
26
+ #include <math.h>
27
+
28
+ #define FALSE 0;
29
+ #define TRUE 1;
30
+
31
+ // Node Structure
32
+ typedef struct struct_priority_node {
33
+ unsigned int degree;
34
+ VALUE priority;
35
+ VALUE object;
36
+ struct struct_priority_node* parent;
37
+ struct struct_priority_node* child;
38
+ struct struct_priority_node* left;
39
+ struct struct_priority_node* right;
40
+ int mark;
41
+ } priority_node;
42
+
43
+ // The Priority Queue
44
+ typedef struct {
45
+ priority_node* rootlist;
46
+ priority_node* min;
47
+ unsigned int length;
48
+ int (*compare_function)(VALUE p1, VALUE p2); // Should return < 0 for a < b, 0 for a == b, > 0 for a > b
49
+ } priority_queue;
50
+
51
+ ////////////////////////////////////////////////////////////////////////////////
52
+ // Node Manipulation Functions
53
+ ////////////////////////////////////////////////////////////////////////////////
54
+
55
+ // Create a priority node structure
56
+ priority_node* create_priority_node(VALUE object, VALUE priority) {
57
+ priority_node* result = ALLOC(priority_node);
58
+ result->degree = 0;
59
+ result->priority = priority;
60
+ result->object = object;
61
+ result->parent = NULL;
62
+ result->child = NULL;
63
+ result->left = result;
64
+ result->right = result;
65
+ result->mark = FALSE;
66
+ return result;
67
+ }
68
+
69
+ // Use this to free a node struct
70
+ void priority_node_free(priority_node* n) {
71
+ free(n);
72
+ }
73
+
74
+ static
75
+ void priority_node_free_recursively(priority_node* n) {
76
+ if (!n)
77
+ return;
78
+
79
+ priority_node* n1 = n;
80
+ do {
81
+ priority_node *n2 = n1->right;
82
+ priority_node_free_recursively(n1->child);
83
+ priority_node_free(n1);
84
+ n1 = n2;
85
+ } while(n1 != n);
86
+ }
87
+
88
+ // link two binomial heaps
89
+ static
90
+ priority_node* link_nodes(priority_queue* q, priority_node* b1, priority_node* b2) {
91
+ if (q->compare_function(b2->priority, b1->priority) < 0)
92
+ return link_nodes(q, b2, b1);
93
+ b2->parent = b1;
94
+ priority_node* child = b1->child;
95
+ b1->child = b2;
96
+ if (child) {
97
+ b2->left = child->left;
98
+ b2->left->right = b2;
99
+ b2->right = child;
100
+ b2->right->left = b2;
101
+ } else {
102
+ b2->left = b2;
103
+ b2->right = b2;
104
+ }
105
+ b1->degree++;
106
+ b2->mark = FALSE; // TODO: Check if it is not rather b1 that should be marked as false
107
+ return b1;
108
+ }
109
+
110
+ ////////////////////////////////////////////////////////////////////////////////
111
+ // Queue Manipulation Functions
112
+ ////////////////////////////////////////////////////////////////////////////////
113
+
114
+ // Create an empty priority queue
115
+ priority_queue* create_priority_queue(int (*compare_function)(VALUE, VALUE)) {
116
+ priority_queue *result = ALLOC(priority_queue);
117
+ result->min = NULL;
118
+ result->rootlist = NULL;
119
+ result->length = 0;
120
+ result->compare_function = compare_function;
121
+ return result;
122
+ }
123
+
124
+ // Free a priority queue and all the nodes it contains
125
+ void priority_queue_free(priority_queue* q) {
126
+ priority_node_free_recursively(q->rootlist);
127
+ free(q);
128
+ }
129
+
130
+ // Insert a node into the rootlist
131
+ // Does not change length value
132
+ static
133
+ priority_queue* insert_tree(priority_queue* const q, priority_node* const tree) {
134
+ if (q->rootlist) {
135
+ priority_node* l = q->rootlist->left;
136
+ l->right = tree;
137
+ q->rootlist->left = tree;
138
+ tree->left = l;
139
+ tree->right = q->rootlist;
140
+ if (q->compare_function(tree->priority, q->min->priority) < 0)
141
+ q->min = tree;
142
+ } else {
143
+ q->rootlist = tree;
144
+ q->min = tree;
145
+ }
146
+ return q;
147
+ }
148
+
149
+ // Meld two queues into one new queue. We take the first queue and the rootnode of the second queue. // TODO: Expose in API
150
+ static
151
+ priority_queue* meld_queue(priority_queue* q1, priority_node* q2, unsigned int length_q2) {
152
+ if (!q1->rootlist) {
153
+ q1->rootlist = q2;
154
+ q1->min = q2;
155
+ q1->length = length_q2;
156
+ } else {
157
+ priority_node* r1 = q1->rootlist->left;
158
+ priority_node* r2 = q2->left;
159
+
160
+ q1->rootlist->left = r2;
161
+ r2->right = q1->rootlist;
162
+
163
+ q2->left = r1;
164
+ r1->right = q2;
165
+
166
+ q1->length = q1->length + length_q2;
167
+
168
+ if (q1->compare_function(q2->priority, q1->min->priority) < 0)
169
+ q1->min = q2;
170
+ }
171
+
172
+ return q1;
173
+ }
174
+
175
+ // Add an object and a priority to a priority queue. Returns a pointer to a
176
+ // priority_node structure, which can be used in delete_node and priority_queue_change_priority
177
+ // operations.
178
+ priority_node* priority_queue_add_node(priority_queue* q, VALUE object, VALUE priority) {
179
+ priority_node* result = create_priority_node(object, priority);
180
+ insert_tree(q, result);
181
+ q->length++;
182
+ return result;
183
+ }
184
+
185
+ // Does not change length
186
+ static
187
+ priority_node* delete_first(priority_queue* const q) {
188
+ if (q->rootlist) {
189
+ priority_node* result = q->rootlist;
190
+ if (result == result->right)
191
+ q->rootlist = NULL;
192
+ else {
193
+ q->rootlist = result->right;
194
+ result->left->right = result->right;
195
+ result->right->left = result->left;
196
+ result->right = result;
197
+ result->left = result;
198
+ }
199
+ return result;
200
+ } else {
201
+ return NULL;
202
+ }
203
+ }
204
+
205
+ static
206
+ void assert_pointers_correct(priority_node* n) {
207
+ if (!n) return;
208
+
209
+ priority_node *n1 = n->right;
210
+ while(n != n1) {
211
+ if (n1->child && (n1 != n1->child->parent))
212
+ printf("Eltern-Kind Zeiger inkorrekt: %p\n", n);
213
+
214
+ if (n1 != n1->right->left)
215
+ printf("Rechts-links inkorrekt: %p\n", n);
216
+
217
+ if (n1 != n1->left->right)
218
+ printf("links-Rechts inkorrekt: %p\n", n);
219
+
220
+ assert_pointers_correct(n1->child);
221
+ n1 = n1->right;
222
+ }
223
+ }
224
+
225
+ // Consolidate a queue in amortized O(log n)
226
+ static
227
+ void consolidate_queue(priority_queue* const q) {
228
+ unsigned int array_size = 2 * log(q->length) / log(2) + 1;
229
+ priority_node* tree_by_degree[array_size];
230
+ unsigned int i;
231
+ for (i=0; i<array_size; i++)
232
+ tree_by_degree[i] = NULL;
233
+
234
+ priority_node* n = NULL;
235
+ while (((n = delete_first(q)))) {
236
+ priority_node* n1 = NULL;
237
+ while (((n1 = tree_by_degree[n->degree]))) {
238
+ tree_by_degree[n->degree] = NULL;
239
+ n = link_nodes(q, n, n1);
240
+ }
241
+ tree_by_degree[n->degree] = n;
242
+ }
243
+
244
+ // Find minimum value in O(log n)
245
+ q->rootlist = NULL;
246
+ q->min = NULL;
247
+ for (i=0; i<array_size; i++) {
248
+ if (tree_by_degree[i] != NULL) {
249
+ insert_tree(q, tree_by_degree[i]);
250
+ }
251
+ }
252
+ }
253
+
254
+ // Delete and extract priority_node with minimal priority O(log n)
255
+ priority_node* priority_queue_delete_min(priority_queue* q) {
256
+ if (!q->rootlist) return NULL;
257
+ priority_node* min = q->min;
258
+
259
+ if (q->length == 1){ // length == 1
260
+ q->rootlist = NULL;
261
+ q->min = NULL;
262
+ q->length = 0;
263
+ } else {
264
+ unsigned int length = q->length;
265
+ // Abtrennen.
266
+ if (q->min == q->rootlist) {
267
+ if (q->min == q->min->right) {
268
+ q->rootlist = NULL;
269
+ q->min = NULL;
270
+ } else {
271
+ q->rootlist = q->min->right;
272
+ }
273
+ }
274
+ min->left->right = min->right;
275
+ min->right->left = min->left;
276
+ min->left = min;
277
+ min->right = min;
278
+ if (min->child) {
279
+ // Kinder und Eltern trennen, Markierung aufheben, und kleinstes Kind bestimmen.
280
+ priority_node* n = min->child;
281
+ do {
282
+ n->parent = NULL;
283
+ n->mark = FALSE;
284
+ n = n->right;
285
+ } while (n!=min->child);
286
+
287
+ // Kinder einf�gen
288
+ if (q->rootlist) {
289
+ priority_node* const l1 = q->rootlist->left;
290
+ priority_node* const l2 = n->left;
291
+
292
+ l1->right = n;
293
+ n->left = l1;
294
+ l2->right = q->rootlist;
295
+ q->rootlist->left = l2;
296
+ } else {
297
+ q->rootlist = n;
298
+ }
299
+ }
300
+
301
+ // Gr��e anpassen
302
+ q->length = length-1;
303
+
304
+ // Wieder aufh�bschen
305
+ consolidate_queue(q);
306
+ }
307
+
308
+ return min;
309
+ }
310
+
311
+ static
312
+ priority_queue* cut_node(priority_queue* q, priority_node* n) {
313
+ if (!n->parent)
314
+ return q;
315
+ n->parent->degree--;
316
+ if (n->parent->child == n) {
317
+ if (n->right == n)
318
+ n->parent->child = NULL;
319
+ else
320
+ n->parent->child = n->right;
321
+ }
322
+ n->parent = NULL;
323
+ n->right->left = n->left;
324
+ n->left->right = n->right;
325
+
326
+ n->right = q->rootlist;
327
+ n->left = q->rootlist->left;
328
+ q->rootlist->left->right = n;
329
+ q->rootlist->left = n;
330
+ q->rootlist = n;
331
+
332
+ n->mark = FALSE;
333
+
334
+ return q;
335
+ }
336
+
337
+ // change the priority of a priority_node and restructure the queue
338
+ // Does not free the priority node
339
+ priority_queue* priority_queue_delete(priority_queue* q, priority_node* n) {
340
+ if (n->child) {
341
+ priority_node* c = n->child;
342
+ priority_node* e = n->child;
343
+ do {
344
+ priority_node* r = c->right;
345
+ cut_node(q, c);
346
+ c = r;
347
+ } while (c != e);
348
+ }
349
+ if (n->parent)
350
+ cut_node(q, n);
351
+ if (n == n->right) {
352
+ q->min = NULL;
353
+ q->rootlist = NULL;
354
+ } else {
355
+ if (q->rootlist == n)
356
+ q->rootlist = n->right;
357
+ if (q->min == n) {
358
+ priority_node* n1 = n->right;
359
+ q->min = n1;
360
+ do {
361
+ if (q->compare_function(n1->priority, q->min->priority) <= 0)
362
+ q->min = n1;
363
+ n1 = n1->right;
364
+ } while(n1 != n);
365
+ }
366
+ n->right->left = n->left;
367
+ n->left->right = n->right;
368
+ n->left = n;
369
+ n->right = n;
370
+ }
371
+ q->length -= 1;
372
+ return q;
373
+ }
374
+
375
+ // change the priority of a priority_node and restructure the queue
376
+ priority_queue* priority_queue_change_priority(priority_queue* q, priority_node* n, VALUE priority) {
377
+ if (q->compare_function(n->priority, priority) <= 0) { // Priority was increased. Remove the node and reinsert.
378
+ priority_queue_delete(q, n);
379
+ n->priority = priority;
380
+ meld_queue(q, n, 1);
381
+ return q;
382
+ }
383
+ n->priority = priority;
384
+ if (q->compare_function(n->priority, q->min->priority) < 0)
385
+ q->min = n;
386
+ if (!(n->parent) || (q->compare_function(n->parent->priority, n->priority) <= 0)) // Already in rootlist or bigger than parent
387
+ return q;
388
+ do { // Cascading Cuts
389
+ priority_node* p = n->parent;
390
+ cut_node(q, n);
391
+ n = p;
392
+ } while (n->mark && n->parent);
393
+ if (n->parent)
394
+ n->mark = TRUE;
395
+ return q;
396
+ }
397
+
398
+ // Get the priority_node with the minimum priority from a queue
399
+ priority_node* priority_queue_min(priority_queue *q) {
400
+ return q->min;
401
+ }
402
+
403
+ int priority_queue_empty(priority_queue *q) {
404
+ return q->min == NULL;
405
+ }
406
+
407
+ // change the priority of a priority_node and restructure the queue
408
+ priority_queue* priority_queue_each_node(priority_queue* q, priority_node* n,
409
+ void (*each)(priority_queue* q_, priority_node* n_, void* args), void* arguments) {
410
+ priority_node* end = n;
411
+ do {
412
+ priority_node* next = n->right;
413
+ (*each)(q, n, arguments);
414
+ if (n->child)
415
+ priority_queue_each_node(q, n->child, each, arguments);
416
+ n = n->right;
417
+ if (n != next)
418
+ break;
419
+ } while (n != end);
420
+ return q;
421
+ }
422
+
423
+ priority_queue* priority_queue_each(priority_queue* q,
424
+ void (*each)(priority_queue* q, priority_node* n, void* args), void* arguments) {
425
+ if (q->rootlist)
426
+ priority_queue_each_node(q, q->rootlist, each, arguments);
427
+ return q;
428
+ }
429
+ ////////////////////////////////////////////////////////////////////////////////
430
+ // Define the ruby classes
431
+ ////////////////////////////////////////////////////////////////////////////////
432
+
433
+ static int id_compare_operator;
434
+ static int id_format;
435
+ static int id_display;
436
+
437
+ priority_queue* get_pq_from_value(VALUE self) {
438
+ priority_queue *q;
439
+ Data_Get_Struct(self, priority_queue, q);
440
+ return q;
441
+ }
442
+
443
+ static
444
+ int priority_compare_function(VALUE a, VALUE b) {
445
+ return FIX2INT(rb_funcall((VALUE) a, id_compare_operator, 1, (VALUE) b));
446
+ }
447
+
448
+ static
449
+ void pq_free(void *p) {
450
+ priority_queue_free(p);
451
+ }
452
+
453
+ static
454
+ void pq_mark_recursive(priority_node* n) {
455
+ if (!n) return;
456
+ rb_gc_mark((VALUE) n->object);
457
+ rb_gc_mark((VALUE) n->priority);
458
+ priority_node* n1 = n->child;
459
+ if (!n1) return;
460
+ do {
461
+ pq_mark_recursive(n1);
462
+ n1 = n1->right;
463
+ } while (n1 != n->child);
464
+ }
465
+
466
+ static
467
+ void pq_mark(void *q) {
468
+ priority_node* n1 = ((priority_queue*) q)->rootlist;
469
+ if (!n1)
470
+ return;
471
+ priority_node* n2 = n1;
472
+ do {
473
+ pq_mark_recursive(n1);
474
+ n1 = n1->right;
475
+ } while (n1 != n2);
476
+ }
477
+
478
+ static
479
+ VALUE pq_alloc(VALUE klass) {
480
+ priority_queue *q;
481
+ VALUE object;
482
+
483
+ q = create_priority_queue(&priority_compare_function);
484
+
485
+ object = Data_Wrap_Struct(klass, pq_mark, pq_free, q);
486
+
487
+ return object;
488
+ }
489
+
490
+ /*
491
+ * Create a new, empty PriorityQueue
492
+ */
493
+ static
494
+ VALUE pq_init(VALUE self) {
495
+ rb_iv_set(self, "@__node_by_object__", rb_hash_new());
496
+
497
+ return self;
498
+ }
499
+
500
+ /*
501
+ * Add an object to the queue.
502
+ */
503
+ static
504
+ VALUE pq_push(VALUE self, VALUE object, VALUE priority) {
505
+ VALUE hash = rb_iv_get(self, "@__node_by_object__");
506
+
507
+ priority_queue* q = get_pq_from_value(self);
508
+
509
+ priority_node* n = priority_queue_add_node(q, object, priority);
510
+
511
+ rb_hash_aset(hash, object, ULONG2NUM((unsigned long) n)); // TODO: This is hackish, maybe its better to also wrap the nodes.
512
+
513
+ return self;
514
+ }
515
+
516
+ /* call-seq:
517
+ * min -> [object, priority]
518
+ *
519
+ * Return the pair [object, priority] with minimal priority or nil when the
520
+ * queue is empty.
521
+ *
522
+ * q = PriorityQueue.new
523
+ * q["a"] = 10
524
+ * q["b"] = 20
525
+ * q.min #=> ["a", 10]
526
+ * q.delete_min #=> ["a", 10]
527
+ * q.min #=> ["b", 20]
528
+ * q.delete_min #=> ["b", 20]
529
+ * q.min #=> nil
530
+ */
531
+ static
532
+ VALUE pq_min(VALUE self) {
533
+ priority_queue* q = get_pq_from_value(self);
534
+
535
+ priority_node* n = priority_queue_min(q);
536
+ if (n)
537
+ return rb_ary_new3(2, n->object, n->priority);
538
+ else
539
+ return Qnil;
540
+ }
541
+
542
+ /* call-seq:
543
+ * min_key -> object
544
+ *
545
+ * Return the key that has the minimal priority or nil when the queue is empty.
546
+ *
547
+ * q = PriorityQueue.new
548
+ * q["a"] = 10
549
+ * q["b"] = 20
550
+ * q.min_key #=> "a"
551
+ * q.delete_min #=> ["a", 10]
552
+ * q.min_key #=> "b"
553
+ * q.delete_min #=> ["b", 20]
554
+ * q.min_key #=> nil
555
+ */
556
+ static
557
+ VALUE pq_min_key(VALUE self) {
558
+ priority_queue* q = get_pq_from_value(self);
559
+
560
+ priority_node* n = priority_queue_min(q);
561
+ if (n)
562
+ return n->object;
563
+ else
564
+ return Qnil;
565
+ }
566
+
567
+ /* call-seq:
568
+ * min_priority -> priority
569
+ *
570
+ * Return the minimal priority or nil when the queue is empty.
571
+ *
572
+ * q = PriorityQueue.new
573
+ * q["a"] = 10
574
+ * q["b"] = 20
575
+ * q.min_priority #=> 10
576
+ * q.delete_min #=> ["a", 10]
577
+ * q.min_priority #=> 20
578
+ * q.delete_min #=> ["b", 20]
579
+ * q.min_priority #=> nil
580
+ */
581
+ static
582
+ VALUE pq_min_priority(VALUE self) {
583
+ priority_queue* q = get_pq_from_value(self);
584
+
585
+ priority_node* n = priority_queue_min(q);
586
+ if (n)
587
+ return n->priority;
588
+ else
589
+ return Qnil;
590
+ }
591
+
592
+ /* call-seq:
593
+ * delete_min -> [key, priority]
594
+ *
595
+ * Delete key with minimal priority and return [key, priority]
596
+ *
597
+ * q = PriorityQueue.new
598
+ * q["a"] = 1
599
+ * q["b"] = 0
600
+ * q.delete_min #=> ["b", 0]
601
+ * q.delete_min #=> ["a", 1]
602
+ * q.delete_min #=> nil
603
+ */
604
+ static
605
+ VALUE pq_delete_min(VALUE self) {
606
+ VALUE hash = rb_iv_get(self, "@__node_by_object__");
607
+ priority_queue* q = get_pq_from_value(self);
608
+
609
+ priority_node* n = priority_queue_delete_min(q);
610
+
611
+ if (n) {
612
+ rb_hash_delete(hash, n->object); // TODO: Maybe we have a problem here with garbage collection of n->object?
613
+ return rb_ary_new3(2, n->object, n->priority);
614
+ } else {
615
+ return Qnil;
616
+ }
617
+ }
618
+
619
+ /* call-seq:
620
+ * delete_min_return_key -> key
621
+ *
622
+ * Delete key with minimal priority and return the key
623
+ *
624
+ * q = PriorityQueue.new
625
+ * q["a"] = 1
626
+ * q["b"] = 0
627
+ * q.delete_min_return_key #=> "b"
628
+ * q.delete_min_return_key #=> "a"
629
+ * q.delete_min_return_key #=> nil
630
+ */
631
+ static
632
+ VALUE pq_delete_min_return_key(VALUE self) {
633
+ VALUE hash = rb_iv_get(self, "@__node_by_object__");
634
+ priority_queue* q = get_pq_from_value(self);
635
+
636
+ priority_node* n = priority_queue_delete_min(q);
637
+
638
+ if (n) {
639
+ rb_hash_delete(hash, n->object); // TODO: Maybe we have a problem here with garbage collection of n->object?
640
+ return n->object;
641
+ } else {
642
+ return Qnil;
643
+ }
644
+ }
645
+
646
+ /*
647
+ * call-seq:
648
+ * delete_min_return_priority -> priority
649
+ *
650
+ * Delete key with minimal priority and return the priority value
651
+ *
652
+ * q = PriorityQueue.new
653
+ * q["a"] = 1
654
+ * q["b"] = 0
655
+ * q.delete_min_return_priority #=> 0
656
+ * q.delete_min_return_priority #=> 1
657
+ * q.delete_min_return_priority #=> nil
658
+ */
659
+ static
660
+ VALUE pq_delete_min_return_priority(VALUE self) {
661
+ VALUE hash = rb_iv_get(self, "@__node_by_object__");
662
+ priority_queue* q = get_pq_from_value(self);
663
+
664
+ priority_node* n = priority_queue_delete_min(q);
665
+
666
+ if (n) {
667
+ rb_hash_delete(hash, n->object); // TODO: Maybe we have a problem here with garbage collection of n->object?
668
+ return n->priority;
669
+ } else {
670
+ return Qnil;
671
+ }
672
+ }
673
+
674
+ /*
675
+ * call-seq:
676
+ * [key] = priority
677
+ * change_priority(key, priority)
678
+ * push(key, priority)
679
+ *
680
+ * Set the priority of a key.
681
+ *
682
+ * q = PriorityQueue.new
683
+ * q["car"] = 50
684
+ * q["train"] = 50
685
+ * q["bike"] = 10
686
+ * q.min #=> ["bike", 10]
687
+ * q["car"] = 0
688
+ * q.min #=> ["car", 0]
689
+ */
690
+ static
691
+ VALUE pq_change_priority(VALUE self, VALUE object, VALUE priority) {
692
+ VALUE hash = rb_iv_get(self, "@__node_by_object__");
693
+ priority_queue* q = get_pq_from_value(self);
694
+
695
+ VALUE node = rb_hash_aref(hash, object);
696
+ if (NIL_P(node)) {
697
+ pq_push(self, object, priority);
698
+ } else {
699
+ priority_queue_change_priority(q, (priority_node*) NUM2ULONG(node), priority);
700
+ }
701
+
702
+ return self;
703
+ }
704
+
705
+ /*
706
+ * call-seq:
707
+ * [key] -> priority
708
+ *
709
+ * Return the priority of a key or nil if the key is not in the queue.
710
+ *
711
+ * q = PriorityQueue.new
712
+ * (0..10).each do | i | q[i.to_s] = i end
713
+ * q["5"] #=> 5
714
+ * q[5] #=> nil
715
+ */
716
+ static
717
+ VALUE pq_get_priority(VALUE self, VALUE object) {
718
+ VALUE hash = rb_iv_get(self, "@__node_by_object__");
719
+
720
+ VALUE node_pointer = rb_hash_aref(hash, object);
721
+
722
+ if (NIL_P(node_pointer))
723
+ return Qnil;
724
+ else
725
+ return (VALUE) (((priority_node*) NUM2ULONG(node_pointer))->priority);
726
+ }
727
+
728
+ /*
729
+ * call-seq:
730
+ * has_key? key -> boolean
731
+ *
732
+ * Return false if the key is not in the queue, true otherwise.
733
+ *
734
+ * q = PriorityQueue.new
735
+ * (0..10).each do | i | q[i.to_s] = i end
736
+ * q.has_key("5") #=> true
737
+ * q.has_key(5) #=> false
738
+ */
739
+ static
740
+ VALUE pq_has_key(VALUE self, VALUE object) {
741
+ VALUE hash = rb_iv_get(self, "@__node_by_object__");
742
+
743
+ VALUE node_pointer = rb_hash_aref(hash, object);
744
+
745
+ return NIL_P(node_pointer) ? Qfalse : Qtrue;
746
+ }
747
+ /* call-seq:
748
+ * length -> Fixnum
749
+ *
750
+ * Returns the number of elements of the queue.
751
+ *
752
+ * q = PriorityQueue.new
753
+ * q.length #=> 0
754
+ * q[0] = 1
755
+ * q.length #=> 1
756
+ */
757
+ static
758
+ VALUE pq_length(VALUE self) {
759
+ priority_queue* q = get_pq_from_value(self);
760
+
761
+ return INT2NUM(q->length);
762
+ }
763
+
764
+ /* call-seq:
765
+ * delete(key) -> [key, priority]
766
+ * delete(key) -> nil
767
+ *
768
+ * Delete a key from the priority queue. Returns nil when the key was not in
769
+ * the queue and [key, priority] otherwise.
770
+ *
771
+ * q = PriorityQueue.new
772
+ * (0..10).each do | i | q[i.to_s] = i end
773
+ * q.delete(5) #=> ["5", 5]
774
+ * q.delete(5) #=> nil
775
+ */
776
+ static
777
+ VALUE pq_delete(VALUE self, VALUE object) {
778
+ priority_queue* q = get_pq_from_value(self);
779
+
780
+ VALUE hash = rb_iv_get(self, "@__node_by_object__");
781
+
782
+ VALUE node_pointer = rb_hash_aref(hash, object);
783
+
784
+ if (NIL_P(node_pointer))
785
+ return Qnil;
786
+ else {
787
+ priority_node* n = (priority_node*) NUM2ULONG(node_pointer);
788
+ VALUE object = n->object;
789
+ VALUE priority = n->priority;
790
+ priority_queue_delete(q, n);
791
+ rb_hash_delete(hash, object);
792
+ priority_node_free(n);
793
+ return rb_ary_new3(2, object, priority);
794
+ }
795
+ }
796
+
797
+
798
+ // Dot a single node of a priority queue. Called by pq_to_dot to do the inner work.
799
+ // (I'm not proud of this function ;-( )
800
+ static
801
+ void pq_node2dot(VALUE result_string, priority_node* n, unsigned int level) {
802
+ if (n == NULL) return;
803
+ unsigned int i;
804
+ for (i=0; i<level; i++) rb_str_cat2(result_string, " ");
805
+ if (n->mark)
806
+ rb_str_concat(result_string,
807
+ rb_funcall(Qnil, id_format, 4, rb_str_new2("NODE%i [label=\"%s (%s)\"];\n"),
808
+ ULONG2NUM((unsigned long) n), n->object, n->priority));
809
+ else
810
+ rb_str_concat(result_string,
811
+ rb_funcall(Qnil, id_format, 4, rb_str_new2("NODE%i [label=\"%s (%s)\",shape=box];\n"),
812
+ ULONG2NUM((unsigned long) n), n->object, n->priority));
813
+ if (n->child != NULL) {
814
+ priority_node* n1 = n->child;
815
+ do {
816
+ pq_node2dot(result_string, n1, level + 1);
817
+ for (i=0; i<level; i++) rb_str_cat2(result_string, " ");
818
+ rb_str_concat(result_string,
819
+ rb_funcall(Qnil, id_format, 4, rb_str_new2("NODE%i -> NODE%i;\n"),
820
+ ULONG2NUM((unsigned long) n), ULONG2NUM((unsigned long) n1)));
821
+ n1 = n1->right;
822
+ } while(n1 != n->child);
823
+ }
824
+ }
825
+
826
+ /*
827
+ * Print a priority queue as a dot-graph. The output can be fed to dot from the
828
+ * vizgraph suite to create a tree depicting the internal datastructure.
829
+ *
830
+ * (I'm not proud of this function ;-( )
831
+ */
832
+ static
833
+ VALUE pq_to_dot(VALUE self) {
834
+ priority_queue* q = get_pq_from_value(self);
835
+
836
+ VALUE result_string = rb_str_new2("digraph fibonacci_heap {\n");
837
+ if (q->rootlist) {
838
+ priority_node* n1 = q->rootlist;
839
+ do {
840
+ pq_node2dot(result_string, n1, 1);
841
+ n1 = n1->right;
842
+ } while(n1 != q->rootlist);
843
+ }
844
+ rb_str_cat2(result_string, "}\n");
845
+ return result_string;
846
+ }
847
+
848
+ /*
849
+ * Returns true if the array is empty, false otherwise.
850
+ */
851
+ static
852
+ VALUE pq_empty(VALUE self) {
853
+ priority_queue* q = get_pq_from_value(self);
854
+ return priority_queue_empty(q) ? Qtrue : Qfalse;
855
+ }
856
+
857
+ static
858
+ void pq_each_helper(priority_queue *q, priority_node *n, void *args) {
859
+ rb_yield(rb_ary_new3(2, n->object, n->priority));
860
+ };
861
+
862
+ /*
863
+ * Call the given block with each [key, priority] pair in the queue
864
+ *
865
+ * Beware: Changing the queue in the block may lead to unwanted behaviour and
866
+ * even infinite loops.
867
+ */
868
+ static
869
+ VALUE pq_each(VALUE self) {
870
+ priority_queue* q = get_pq_from_value(self);
871
+ priority_queue_each(q, &pq_each_helper, NULL);
872
+ return self;
873
+ }
874
+
875
+ static
876
+ VALUE pq_insert_node(VALUE node, VALUE queue) {
877
+ return pq_push(queue, rb_ary_entry(node, 0), rb_ary_entry(node, 1));
878
+ }
879
+
880
+ static
881
+ VALUE pq_initialize_copy(VALUE copy, VALUE orig) {
882
+ if (copy == orig)
883
+ return copy;
884
+
885
+ rb_iterate(rb_each, orig, pq_insert_node, copy);
886
+
887
+ return copy;
888
+ }
889
+
890
+ /*
891
+ * Returns a string representation of the priority queue.
892
+ */
893
+ static
894
+ VALUE pq_inspect(VALUE self) {
895
+ VALUE result = rb_str_new2("<PriorityQueue: ");
896
+ rb_str_concat(result,
897
+ rb_funcall(rb_funcall(self, rb_intern("to_a"), 0),
898
+ rb_intern("inspect"), 0));
899
+ rb_str_concat(result, rb_str_new2(">"));
900
+ return result;
901
+ }
902
+
903
+ static VALUE cPriorityQueue;
904
+ static VALUE mContainers;
905
+
906
+ /*
907
+ * A Priority Queue implementation
908
+ *
909
+ * A priority queue is a queue, where each element (the key) has an assigned
910
+ * priority. It is possible to efficently decrease priorities and to
911
+ * efficently look up and remove the key with the smallest priority.
912
+ *
913
+ * This datastructure is used in different algorithms. The standard algorithm
914
+ * used to introduce priority queues is dijkstra's shortest path algorithm.
915
+ *
916
+ * The priority queue includes the Enumerable module.
917
+ */
918
+ void Init_CPriorityQueue() {
919
+ id_compare_operator = rb_intern("<=>");
920
+ id_format = rb_intern("format");
921
+ id_display = rb_intern("display");
922
+
923
+ mContainers = rb_define_module("Containers");
924
+ cPriorityQueue = rb_define_class_under(mContainers, "CPriorityQueue", rb_cObject);
925
+
926
+ rb_define_alloc_func(cPriorityQueue, pq_alloc);
927
+ rb_define_method(cPriorityQueue, "initialize", pq_init, 0);
928
+ rb_define_method(cPriorityQueue, "initialize_copy", pq_initialize_copy, 1);
929
+ rb_define_method(cPriorityQueue, "min", pq_min, 0);
930
+ rb_define_method(cPriorityQueue, "min_key", pq_min_key, 0);
931
+ rb_define_method(cPriorityQueue, "min_priority", pq_min_priority, 0);
932
+ rb_define_method(cPriorityQueue, "delete_min", pq_delete_min, 0);
933
+ rb_define_method(cPriorityQueue, "delete_min_return_key", pq_delete_min_return_key, 0);
934
+ rb_define_method(cPriorityQueue, "delete_min_return_priority", pq_delete_min_return_priority, 0);
935
+ rb_define_method(cPriorityQueue, "push", pq_change_priority, 2);
936
+ rb_define_method(cPriorityQueue, "change_priority", pq_change_priority, 2);
937
+ rb_define_method(cPriorityQueue, "[]=", pq_change_priority, 2);
938
+ rb_define_method(cPriorityQueue, "priority", pq_get_priority, 1);
939
+ rb_define_method(cPriorityQueue, "[]", pq_get_priority, 1);
940
+ rb_define_method(cPriorityQueue, "has_key?", pq_has_key, 1);
941
+ rb_define_method(cPriorityQueue, "length", pq_length, 0);
942
+ rb_define_method(cPriorityQueue, "to_dot", pq_to_dot, 0);
943
+ rb_define_method(cPriorityQueue, "empty?", pq_empty, 0);
944
+ rb_define_method(cPriorityQueue, "delete", pq_delete, 1);
945
+ rb_define_method(cPriorityQueue, "inspect", pq_inspect, 0);
946
+ rb_define_method(cPriorityQueue, "each", pq_each, 0);
947
+ rb_include_module(cPriorityQueue, rb_eval_string("Enumerable"));
948
+ }