jumoku 0.1.1 → 0.1.2

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 (85) hide show
  1. data/Gemfile +4 -0
  2. data/lib/jumoku.rb +4 -2
  3. data/lib/jumoku/version.rb +1 -1
  4. data/spec/raw_tree_spec.rb +353 -0
  5. data/spec/spec.opts +4 -0
  6. data/spec/spec_helper.rb +14 -0
  7. data/spec/tree_spec.rb +553 -0
  8. data/vendor/git/graphy/CREDITS.md +31 -0
  9. data/vendor/git/graphy/LICENSE +35 -0
  10. data/vendor/git/graphy/README.md +186 -0
  11. data/vendor/git/graphy/Rakefile +61 -0
  12. data/vendor/git/graphy/TODO.md +20 -0
  13. data/vendor/git/graphy/VERSION +1 -0
  14. data/vendor/git/graphy/examples/graph_self.rb +56 -0
  15. data/vendor/git/graphy/examples/module_graph.jpg +0 -0
  16. data/vendor/git/graphy/examples/module_graph.rb +14 -0
  17. data/vendor/git/graphy/examples/self_graph.jpg +0 -0
  18. data/vendor/git/graphy/examples/visualize.jpg +0 -0
  19. data/vendor/git/graphy/examples/visualize.rb +10 -0
  20. data/vendor/git/graphy/graphy.gemspec +149 -0
  21. data/vendor/git/graphy/lib/graphy.rb +90 -0
  22. data/vendor/git/graphy/lib/graphy/adjacency_graph.rb +224 -0
  23. data/vendor/git/graphy/lib/graphy/arc.rb +65 -0
  24. data/vendor/git/graphy/lib/graphy/arc_number.rb +52 -0
  25. data/vendor/git/graphy/lib/graphy/biconnected.rb +84 -0
  26. data/vendor/git/graphy/lib/graphy/chinese_postman.rb +91 -0
  27. data/vendor/git/graphy/lib/graphy/classes/graph_classes.rb +28 -0
  28. data/vendor/git/graphy/lib/graphy/common.rb +63 -0
  29. data/vendor/git/graphy/lib/graphy/comparability.rb +63 -0
  30. data/vendor/git/graphy/lib/graphy/directed_graph.rb +76 -0
  31. data/vendor/git/graphy/lib/graphy/directed_graph/algorithms.rb +92 -0
  32. data/vendor/git/graphy/lib/graphy/directed_graph/distance.rb +167 -0
  33. data/vendor/git/graphy/lib/graphy/dot.rb +94 -0
  34. data/vendor/git/graphy/lib/graphy/edge.rb +37 -0
  35. data/vendor/git/graphy/lib/graphy/ext.rb +79 -0
  36. data/vendor/git/graphy/lib/graphy/graph.rb +631 -0
  37. data/vendor/git/graphy/lib/graphy/graph_api.rb +35 -0
  38. data/vendor/git/graphy/lib/graphy/labels.rb +113 -0
  39. data/vendor/git/graphy/lib/graphy/maximum_flow.rb +77 -0
  40. data/vendor/git/graphy/lib/graphy/ruby_compatibility.rb +17 -0
  41. data/vendor/git/graphy/lib/graphy/search.rb +511 -0
  42. data/vendor/git/graphy/lib/graphy/strong_components.rb +93 -0
  43. data/vendor/git/graphy/lib/graphy/support/support.rb +9 -0
  44. data/vendor/git/graphy/lib/graphy/undirected_graph.rb +57 -0
  45. data/vendor/git/graphy/lib/graphy/undirected_graph/algorithms.rb +90 -0
  46. data/vendor/git/graphy/spec/biconnected_spec.rb +27 -0
  47. data/vendor/git/graphy/spec/chinese_postman_spec.rb +27 -0
  48. data/vendor/git/graphy/spec/community_spec.rb +44 -0
  49. data/vendor/git/graphy/spec/complement_spec.rb +27 -0
  50. data/vendor/git/graphy/spec/digraph_distance_spec.rb +121 -0
  51. data/vendor/git/graphy/spec/digraph_spec.rb +339 -0
  52. data/vendor/git/graphy/spec/dot_spec.rb +48 -0
  53. data/vendor/git/graphy/spec/edge_spec.rb +159 -0
  54. data/vendor/git/graphy/spec/inspection_spec.rb +40 -0
  55. data/vendor/git/graphy/spec/multi_edge_spec.rb +32 -0
  56. data/vendor/git/graphy/spec/neighborhood_spec.rb +38 -0
  57. data/vendor/git/graphy/spec/properties_spec.rb +146 -0
  58. data/vendor/git/graphy/spec/search_spec.rb +227 -0
  59. data/vendor/git/graphy/spec/spec.opts +4 -0
  60. data/vendor/git/graphy/spec/spec_helper.rb +56 -0
  61. data/vendor/git/graphy/spec/strong_components_spec.rb +61 -0
  62. data/vendor/git/graphy/spec/triangulated_spec.rb +125 -0
  63. data/vendor/git/graphy/spec/undirected_graph_spec.rb +220 -0
  64. data/vendor/git/graphy/vendor/priority-queue/CHANGELOG +33 -0
  65. data/vendor/git/graphy/vendor/priority-queue/Makefile +140 -0
  66. data/vendor/git/graphy/vendor/priority-queue/README +133 -0
  67. data/vendor/git/graphy/vendor/priority-queue/benchmark/dijkstra.rb +171 -0
  68. data/vendor/git/graphy/vendor/priority-queue/compare_comments.rb +49 -0
  69. data/vendor/git/graphy/vendor/priority-queue/doc/c-vs-rb.png +0 -0
  70. data/vendor/git/graphy/vendor/priority-queue/doc/compare_big.gp +14 -0
  71. data/vendor/git/graphy/vendor/priority-queue/doc/compare_big.png +0 -0
  72. data/vendor/git/graphy/vendor/priority-queue/doc/compare_small.gp +15 -0
  73. data/vendor/git/graphy/vendor/priority-queue/doc/compare_small.png +0 -0
  74. data/vendor/git/graphy/vendor/priority-queue/doc/results.csv +37 -0
  75. data/vendor/git/graphy/vendor/priority-queue/ext/priority_queue/CPriorityQueue/extconf.rb +2 -0
  76. data/vendor/git/graphy/vendor/priority-queue/ext/priority_queue/CPriorityQueue/priority_queue.c +947 -0
  77. data/vendor/git/graphy/vendor/priority-queue/lib/priority_queue.rb +14 -0
  78. data/vendor/git/graphy/vendor/priority-queue/lib/priority_queue/c_priority_queue.rb +1 -0
  79. data/vendor/git/graphy/vendor/priority-queue/lib/priority_queue/poor_priority_queue.rb +46 -0
  80. data/vendor/git/graphy/vendor/priority-queue/lib/priority_queue/ruby_priority_queue.rb +526 -0
  81. data/vendor/git/graphy/vendor/priority-queue/priority_queue.so +0 -0
  82. data/vendor/git/graphy/vendor/priority-queue/setup.rb +1551 -0
  83. data/vendor/git/graphy/vendor/priority-queue/test/priority_queue_test.rb +371 -0
  84. data/vendor/git/graphy/vendor/rdot.rb +360 -0
  85. metadata +83 -1
@@ -0,0 +1,49 @@
1
+ c_file = File.read("ext/priority_queue/CPriorityQueue/priority_queue.c")
2
+ rb_file = File.read("lib/priority_queue/ruby_priority_queue.rb")
3
+
4
+ c_comments = Hash.new { "" }
5
+
6
+ c_file.scan(%r(/\*(.*?)\*/\s*static\s+\w+\s*pq_(\w+)\(.*?\))m).each do | match |
7
+ c_comments[match[1]] = match[0].gsub(%r(\n\s*\* {0,1})m, "\n").strip
8
+ end
9
+
10
+ rb_comments = Hash.new { "" }
11
+
12
+ rb_file.scan(%r(((?:\n\s*#[^\n]*)*)\s*def\s+(\w+))m).each do | match |
13
+ rb_comments[match[1]] = match[0].gsub(%r(\n\s*# {0,1})m, "\n").strip
14
+ end
15
+
16
+ add_comments = Hash.new
17
+
18
+ (rb_comments.keys + c_comments.keys).uniq.each do | key |
19
+ #next if rb_comments[key].gsub(/\s+/m, " ") == c_comments[key].gsub(/\s+/m, " ")
20
+ if c_comments[key].empty?
21
+ add_comments[key] = rb_comments[key]
22
+ elsif rb_comments[key].empty?
23
+ add_comments[key] = c_comments[key]
24
+ elsif rb_comments[key] != c_comments[key]
25
+
26
+ puts key
27
+ puts "Ruby"
28
+ puts rb_comments[key]
29
+ puts "C"
30
+ puts c_comments[key]
31
+ puts
32
+ puts "Choose [c,r]"
33
+ 1 until /^([cr])/ =~ gets
34
+ add_comments[key] = ($1 == "c" ? c_comments : rb_comments)[key]
35
+ puts "-" * 80
36
+ puts
37
+ else
38
+ add_comments[key] = rb_comments[key]
39
+ end
40
+
41
+ end
42
+
43
+ File.open("lib/priority_queue/ruby_priority_queue.new.rb", "wb") do | o |
44
+ o <<
45
+ rb_file.gsub(%r(((?:\n\s*#[^\n]*)*)(\s*def\s+(\w+)))m) do | match |
46
+ name, all = $3, $2
47
+ "\n" + (add_comments[name].gsub(/^/, "#")) + all
48
+ end
49
+ end
@@ -0,0 +1,14 @@
1
+ set term png
2
+ set out 'compare_big.png'
3
+ set xlabel 'Number of nodes'
4
+ set ylabel 'Time in seconds (real)'
5
+ set yrange [0:240]
6
+ set ytics 30
7
+ set grid ytics mytics
8
+ set mytics 2
9
+ #set logscale xy
10
+ set title 'Dijkstras Shortest Path Algorithm using different PQ Implementations'
11
+ plot \
12
+ 'results.csv' using 1:2 with lines title "CPriorityQueue (Graph of Degree: 4)",\
13
+ 'results.csv' using 1:4 with lines title "RubyPriorityQueue (Graph of Degree: 4)", \
14
+ 'results.csv' using 1:3 with lines title "PoorPriorityQueue (Graph of Degree: 4)"
@@ -0,0 +1,15 @@
1
+ set term png
2
+ set out 'compare_small.png'
3
+ set xlabel 'Number of nodes'
4
+ set ylabel 'Time in seconds (real)'
5
+ set yrange [0:60]
6
+ set xrange [0:8000]
7
+ set ytics 15
8
+ set grid ytics mytics
9
+ set mytics 3
10
+ #set logscale xy
11
+ set title 'Dijkstras Shortest Path Algorithm using different PQ Implementations'
12
+ plot \
13
+ 'results.csv' using 1:2 with lines title "CPriorityQueue (Graph of Degree: 4)",\
14
+ 'results.csv' using 1:4 with lines title "RubyPriorityQueue (Graph of Degree: 4)", \
15
+ 'results.csv' using 1:3 with lines title "PoorPriorityQueue (Graph of Degree: 4)"
@@ -0,0 +1,37 @@
1
+ size "CPriorityQueue (Graph of Degree: 4)" "PoorPriorityQueue (Graph of Degree: 4)" "RubyPriorityQueue (Graph of Degree: 4)"
2
+ 100 0.00224618911743164 0.00624475479125977 0.00999178886413574
3
+ 200 0.00454645156860352 0.0206499099731445 0.0216238498687744
4
+ 300 0.00689601898193359 0.0456759929656982 0.0333521842956543
5
+ 400 0.00930037498474121 0.0799281597137451 0.0460003852844238
6
+ 500 0.0121791362762451 0.120535612106323 0.0602848052978516
7
+ 600 0.0147404193878174 0.175632238388062 0.0720498085021973
8
+ 700 0.017409610748291 0.239475774765015 0.0866350173950195
9
+ 800 0.019864559173584 0.308732604980469 0.0979691028594971
10
+ 900 0.0226212501525879 0.394028520584106 0.11493763923645
11
+ 1000 0.0251464366912842 0.487221813201904 0.125885772705078
12
+ 2000 0.0565140247344971 2.28050599098206 0.276543807983398
13
+ 3000 0.085650634765625 6.21102161407471 0.448645544052124
14
+ 4000 0.10867018699646 8.67334637641907 0.573670101165771
15
+ 5000 0.138333988189697 14.8025764465332 0.749224042892456
16
+ 6000 0.188258218765259 23.5921889781952 0.926058626174927
17
+ 7000 0.22198920249939 35.1978524208069 1.12497220039368
18
+ 8000 0.253745651245117 51.36594581604 1.34597997665405
19
+ 9000 0.261373472213745 48.2841837882996 1.37383661270142
20
+ 10000 0.296752548217773 63.1833290576935 1.60117835998535
21
+ 20000 0.624662017822266 277.677141237259 3.37345514297485
22
+ 30000 1.0169261932373 733.016969585419 5.47739477157593
23
+ 40000 1.29656438827515 '' 7.03887400627136
24
+ 50000 1.84444799423218 '' 9.04675197601318
25
+ 60000 2.16575860977173 '' 11.4338163852692
26
+ 70000 2.34671788215637 '' 12.989319562912
27
+ 80000 2.91462659835815 '' 14.7507841110229
28
+ 90000 3.36594500541687 '' 17.4672434329987
29
+ 100000 3.74284019470215 '' 19.7872510433197
30
+ 200000 8.33274736404419 '' 44.0830686569214
31
+ 300000 15.3942915916443 '' 75.2278475284576
32
+ 400000 22.4600916385651 '' 109.477935791016
33
+ 500000 29.9784585952759 '' 150.160971403122
34
+ 600000 37.4891954421997 '' 192.808595132828
35
+ 700000 '' '' ''
36
+ 800000 '' '' ''
37
+ 900000 '' '' ''
@@ -0,0 +1,2 @@
1
+ require 'mkmf'
2
+ create_makefile("CPriorityQueue")
@@ -0,0 +1,947 @@
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
+ */
22
+ #include <stdlib.h>
23
+ #include <stdio.h>
24
+ #include "ruby.h"
25
+ #include <math.h>
26
+
27
+ typedef _Bool bool;
28
+
29
+ #define false 0;
30
+ #define true 1;
31
+
32
+ // Node Structure
33
+ typedef struct struct_priority_node {
34
+ unsigned int degree;
35
+ VALUE priority;
36
+ VALUE object;
37
+ struct struct_priority_node* parent;
38
+ struct struct_priority_node* child;
39
+ struct struct_priority_node* left;
40
+ struct struct_priority_node* right;
41
+ bool mark;
42
+ } priority_node;
43
+
44
+ // The Priority Queue
45
+ typedef struct {
46
+ priority_node* rootlist;
47
+ priority_node* min;
48
+ unsigned int length;
49
+ int (*compare_function)(VALUE p1, VALUE p2); // Should return < 0 for a < b, 0 for a == b, > 0 for a > b
50
+ } priority_queue;
51
+
52
+ ////////////////////////////////////////////////////////////////////////////////
53
+ // Node Manipulation Functions
54
+ ////////////////////////////////////////////////////////////////////////////////
55
+
56
+ // Create a priority node structure
57
+ priority_node* create_priority_node(VALUE object, VALUE priority) {
58
+ priority_node* result = ALLOC(priority_node);
59
+ result->degree = 0;
60
+ result->priority = priority;
61
+ result->object = object;
62
+ result->parent = NULL;
63
+ result->child = NULL;
64
+ result->left = result;
65
+ result->right = result;
66
+ result->mark = false;
67
+ return result;
68
+ }
69
+
70
+ // Use this to free a node struct
71
+ void priority_node_free(priority_node* n) {
72
+ free(n);
73
+ }
74
+
75
+ static
76
+ void priority_node_free_recursively(priority_node* n) {
77
+ if (!n)
78
+ return;
79
+
80
+ priority_node* n1 = n;
81
+ do {
82
+ priority_node *n2 = n1->right;
83
+ priority_node_free_recursively(n1->child);
84
+ priority_node_free(n1);
85
+ n1 = n2;
86
+ } while(n1 != n);
87
+ }
88
+
89
+ // link two binomial heaps
90
+ static
91
+ priority_node* link_nodes(priority_queue* q, priority_node* b1, priority_node* b2) {
92
+ if (q->compare_function(b2->priority, b1->priority) < 0)
93
+ return link_nodes(q, b2, b1);
94
+ b2->parent = b1;
95
+ priority_node* child = b1->child;
96
+ b1->child = b2;
97
+ if (child) {
98
+ b2->left = child->left;
99
+ b2->left->right = b2;
100
+ b2->right = child;
101
+ b2->right->left = b2;
102
+ } else {
103
+ b2->left = b2;
104
+ b2->right = b2;
105
+ }
106
+ b1->degree++;
107
+ b2->mark = false; // TODO: Check if it is not rather b1 that should be marked as false
108
+ return b1;
109
+ }
110
+
111
+ ////////////////////////////////////////////////////////////////////////////////
112
+ // Queue Manipulation Functions
113
+ ////////////////////////////////////////////////////////////////////////////////
114
+
115
+ // Create an empty priority queue
116
+ priority_queue* create_priority_queue(int (*compare_function)(VALUE, VALUE)) {
117
+ priority_queue *result = ALLOC(priority_queue);
118
+ result->min = NULL;
119
+ result->rootlist = NULL;
120
+ result->length = 0;
121
+ result->compare_function = compare_function;
122
+ return result;
123
+ }
124
+
125
+ // Free a priority queue and all the nodes it contains
126
+ void priority_queue_free(priority_queue* q) {
127
+ priority_node_free_recursively(q->rootlist);
128
+ free(q);
129
+ }
130
+
131
+ // Insert a node into the rootlist
132
+ // Does not change length value
133
+ static
134
+ priority_queue* insert_tree(priority_queue* const q, priority_node* const tree) {
135
+ if (q->rootlist) {
136
+ priority_node* l = q->rootlist->left;
137
+ l->right = tree;
138
+ q->rootlist->left = tree;
139
+ tree->left = l;
140
+ tree->right = q->rootlist;
141
+ if (q->compare_function(tree->priority, q->min->priority) < 0)
142
+ q->min = tree;
143
+ } else {
144
+ q->rootlist = tree;
145
+ q->min = tree;
146
+ }
147
+ return q;
148
+ }
149
+
150
+ // Meld two queues into one new queue. We take the first queue and the rootnode of the second queue. // TODO: Expose in API
151
+ static
152
+ priority_queue* meld_queue(priority_queue* q1, priority_node* q2, unsigned int length_q2) {
153
+ if (!q1->rootlist) {
154
+ q1->rootlist = q2;
155
+ q1->min = q2;
156
+ q1->length = length_q2;
157
+ } else {
158
+ priority_node* r1 = q1->rootlist->left;
159
+ priority_node* r2 = q2->left;
160
+
161
+ q1->rootlist->left = r2;
162
+ r2->right = q1->rootlist;
163
+
164
+ q2->left = r1;
165
+ r1->right = q2;
166
+
167
+ q1->length = q1->length + length_q2;
168
+
169
+ if (q1->compare_function(q2->priority, q1->min->priority) < 0)
170
+ q1->min = q2;
171
+ }
172
+
173
+ return q1;
174
+ }
175
+
176
+ // Add an object and a priority to a priority queue. Returns a pointer to a
177
+ // priority_node structure, which can be used in delete_node and priority_queue_change_priority
178
+ // operations.
179
+ priority_node* priority_queue_add_node(priority_queue* q, VALUE object, VALUE priority) {
180
+ priority_node* result = create_priority_node(object, priority);
181
+ insert_tree(q, result);
182
+ q->length++;
183
+ return result;
184
+ }
185
+
186
+ // Does not change length
187
+ static
188
+ priority_node* delete_first(priority_queue* const q) {
189
+ if (q->rootlist) {
190
+ priority_node* result = q->rootlist;
191
+ if (result == result->right)
192
+ q->rootlist = NULL;
193
+ else {
194
+ q->rootlist = result->right;
195
+ result->left->right = result->right;
196
+ result->right->left = result->left;
197
+ result->right = result;
198
+ result->left = result;
199
+ }
200
+ return result;
201
+ } else {
202
+ return NULL;
203
+ }
204
+ }
205
+
206
+ static
207
+ void assert_pointers_correct(priority_node* n) {
208
+ if (!n) return;
209
+
210
+ priority_node *n1 = n->right;
211
+ while(n != n1) {
212
+ if (n1->child && (n1 != n1->child->parent))
213
+ printf("Eltern-Kind Zeiger inkorrekt: %p\n", n);
214
+
215
+ if (n1 != n1->right->left)
216
+ printf("Rechts-links inkorrekt: %p\n", n);
217
+
218
+ if (n1 != n1->left->right)
219
+ printf("links-Rechts inkorrekt: %p\n", n);
220
+
221
+ assert_pointers_correct(n1->child);
222
+ n1 = n1->right;
223
+ }
224
+ }
225
+
226
+ // Consolidate a queue in amortized O(log n)
227
+ static
228
+ void consolidate_queue(priority_queue* const q) {
229
+ unsigned int array_size = 2 * log(q->length) / log(2) + 1;
230
+ priority_node* tree_by_degree[array_size];
231
+ unsigned int i;
232
+ for (i=0; i<array_size; i++)
233
+ tree_by_degree[i] = NULL;
234
+
235
+ priority_node* n = NULL;
236
+ while (((n = delete_first(q)))) {
237
+ priority_node* n1 = NULL;
238
+ while (((n1 = tree_by_degree[n->degree]))) {
239
+ tree_by_degree[n->degree] = NULL;
240
+ n = link_nodes(q, n, n1);
241
+ }
242
+ tree_by_degree[n->degree] = n;
243
+ }
244
+
245
+ // Find minimum value in O(log n)
246
+ q->rootlist = NULL;
247
+ q->min = NULL;
248
+ for (i=0; i<array_size; i++) {
249
+ if (tree_by_degree[i] != NULL) {
250
+ insert_tree(q, tree_by_degree[i]);
251
+ }
252
+ }
253
+ }
254
+
255
+ // Delete and extract priority_node with minimal priority O(log n)
256
+ priority_node* priority_queue_delete_min(priority_queue* q) {
257
+ if (!q->rootlist) return NULL;
258
+ priority_node* min = q->min;
259
+
260
+ if (q->length == 1){ // length == 1
261
+ q->rootlist = NULL;
262
+ q->min = NULL;
263
+ q->length = 0;
264
+ } else {
265
+ unsigned int length = q->length;
266
+ // Abtrennen.
267
+ if (q->min == q->rootlist) {
268
+ if (q->min == q->min->right) {
269
+ q->rootlist = NULL;
270
+ q->min = NULL;
271
+ } else {
272
+ q->rootlist = q->min->right;
273
+ }
274
+ }
275
+ min->left->right = min->right;
276
+ min->right->left = min->left;
277
+ min->left = min;
278
+ min->right = min;
279
+ if (min->child) {
280
+ // Kinder und Eltern trennen, Markierung aufheben, und kleinstes Kind bestimmen.
281
+ priority_node* n = min->child;
282
+ do {
283
+ n->parent = NULL;
284
+ n->mark = false;
285
+ n = n->right;
286
+ } while (n!=min->child);
287
+
288
+ // Kinder einf�gen
289
+ if (q->rootlist) {
290
+ priority_node* const l1 = q->rootlist->left;
291
+ priority_node* const l2 = n->left;
292
+
293
+ l1->right = n;
294
+ n->left = l1;
295
+ l2->right = q->rootlist;
296
+ q->rootlist->left = l2;
297
+ } else {
298
+ q->rootlist = n;
299
+ }
300
+ }
301
+
302
+ // Gr��e anpassen
303
+ q->length = length-1;
304
+
305
+ // Wieder aufh�bschen
306
+ consolidate_queue(q);
307
+ }
308
+
309
+ return min;
310
+ }
311
+
312
+ static
313
+ priority_queue* cut_node(priority_queue* q, priority_node* n) {
314
+ if (!n->parent)
315
+ return q;
316
+ n->parent->degree--;
317
+ if (n->parent->child == n) {
318
+ if (n->right == n)
319
+ n->parent->child = NULL;
320
+ else
321
+ n->parent->child = n->right;
322
+ }
323
+ n->parent = NULL;
324
+ n->right->left = n->left;
325
+ n->left->right = n->right;
326
+
327
+ n->right = q->rootlist;
328
+ n->left = q->rootlist->left;
329
+ q->rootlist->left->right = n;
330
+ q->rootlist->left = n;
331
+ q->rootlist = n;
332
+
333
+ n->mark = false;
334
+
335
+ return q;
336
+ }
337
+
338
+ // change the priority of a priority_node and restructure the queue
339
+ // Does not free the priority node
340
+ priority_queue* priority_queue_delete(priority_queue* q, priority_node* n) {
341
+ if (n->child) {
342
+ priority_node* c = n->child;
343
+ priority_node* e = n->child;
344
+ do {
345
+ priority_node* r = c->right;
346
+ cut_node(q, c);
347
+ c = r;
348
+ } while (c != e);
349
+ }
350
+ if (n->parent)
351
+ cut_node(q, n);
352
+ if (n == n->right) {
353
+ q->min = NULL;
354
+ q->rootlist = NULL;
355
+ } else {
356
+ if (q->rootlist == n)
357
+ q->rootlist = n->right;
358
+ if (q->min == n) {
359
+ priority_node* n1 = n->right;
360
+ q->min = n1;
361
+ do {
362
+ if (q->compare_function(n1->priority, q->min->priority) <= 0)
363
+ q->min = n1;
364
+ n1 = n1->right;
365
+ } while(n1 != n);
366
+ }
367
+ n->right->left = n->left;
368
+ n->left->right = n->right;
369
+ n->left = n;
370
+ n->right = n;
371
+ }
372
+ q->length -= 1;
373
+ return q;
374
+ }
375
+
376
+ // change the priority of a priority_node and restructure the queue
377
+ priority_queue* priority_queue_change_priority(priority_queue* q, priority_node* n, VALUE priority) {
378
+ if (q->compare_function(n->priority, priority) <= 0) { // Priority was increased. Remove the node and reinsert.
379
+ priority_queue_delete(q, n);
380
+ n->priority = priority;
381
+ meld_queue(q, n, 1);
382
+ return q;
383
+ }
384
+ n->priority = priority;
385
+ if (q->compare_function(n->priority, q->min->priority) < 0)
386
+ q->min = n;
387
+ if (!(n->parent) || (q->compare_function(n->parent->priority, n->priority) <= 0)) // Already in rootlist or bigger than parent
388
+ return q;
389
+ do { // Cascading Cuts
390
+ priority_node* p = n->parent;
391
+ cut_node(q, n);
392
+ n = p;
393
+ } while (n->mark && n->parent);
394
+ if (n->parent)
395
+ n->mark = true;
396
+ return q;
397
+ }
398
+
399
+ // Get the priority_node with the minimum priority from a queue
400
+ priority_node* priority_queue_min(priority_queue *q) {
401
+ return q->min;
402
+ }
403
+
404
+ _Bool priority_queue_empty(priority_queue *q) {
405
+ return q->min == NULL;
406
+ }
407
+
408
+ // change the priority of a priority_node and restructure the queue
409
+ priority_queue* priority_queue_each_node(priority_queue* q, priority_node* n,
410
+ void (*each)(priority_queue* q_, priority_node* n_, void* args), void* arguments) {
411
+ priority_node* end = n;
412
+ do {
413
+ priority_node* next = n->right;
414
+ (*each)(q, n, arguments);
415
+ if (n->child)
416
+ priority_queue_each_node(q, n->child, each, arguments);
417
+ n = n->right;
418
+ if (n != next)
419
+ break;
420
+ } while (n != end);
421
+ return q;
422
+ }
423
+
424
+ priority_queue* priority_queue_each(priority_queue* q,
425
+ void (*each)(priority_queue* q, priority_node* n, void* args), void* arguments) {
426
+ if (q->rootlist)
427
+ priority_queue_each_node(q, q->rootlist, each, arguments);
428
+ return q;
429
+ }
430
+ ////////////////////////////////////////////////////////////////////////////////
431
+ // Define the ruby classes
432
+ ////////////////////////////////////////////////////////////////////////////////
433
+
434
+ static int id_compare_operator;
435
+ static int id_format;
436
+ static int id_display;
437
+
438
+ priority_queue* get_pq_from_value(VALUE self) {
439
+ priority_queue *q;
440
+ Data_Get_Struct(self, priority_queue, q);
441
+ return q;
442
+ }
443
+
444
+ static
445
+ int priority_compare_function(VALUE a, VALUE b) {
446
+ return FIX2INT(rb_funcall((VALUE) a, id_compare_operator, 1, (VALUE) b));
447
+ }
448
+
449
+ static
450
+ void pq_free(void *p) {
451
+ priority_queue_free(p);
452
+ }
453
+
454
+ static
455
+ void pq_mark_recursive(priority_node* n) {
456
+ if (!n) return;
457
+ rb_gc_mark((VALUE) n->object);
458
+ rb_gc_mark((VALUE) n->priority);
459
+ priority_node* n1 = n->child;
460
+ if (!n1) return;
461
+ do {
462
+ pq_mark_recursive(n1);
463
+ n1 = n1->right;
464
+ } while (n1 != n->child);
465
+ }
466
+
467
+ static
468
+ void pq_mark(void *q) {
469
+ priority_node* n1 = ((priority_queue*) q)->rootlist;
470
+ if (!n1)
471
+ return;
472
+ priority_node* n2 = n1;
473
+ do {
474
+ pq_mark_recursive(n1);
475
+ n1 = n1->right;
476
+ } while (n1 != n2);
477
+ }
478
+
479
+ static
480
+ VALUE pq_alloc(VALUE klass) {
481
+ priority_queue *q;
482
+ VALUE object;
483
+
484
+ q = create_priority_queue(&priority_compare_function);
485
+
486
+ object = Data_Wrap_Struct(klass, pq_mark, pq_free, q);
487
+
488
+ return object;
489
+ }
490
+
491
+ /*
492
+ * Create a new, empty PriorityQueue
493
+ */
494
+ static
495
+ VALUE pq_init(VALUE self) {
496
+ rb_iv_set(self, "@__node_by_object__", rb_hash_new());
497
+
498
+ return self;
499
+ }
500
+
501
+ /*
502
+ * Add an object to the queue.
503
+ */
504
+ static
505
+ VALUE pq_push(VALUE self, VALUE object, VALUE priority) {
506
+ VALUE hash = rb_iv_get(self, "@__node_by_object__");
507
+
508
+ priority_queue* q = get_pq_from_value(self);
509
+
510
+ priority_node* n = priority_queue_add_node(q, object, priority);
511
+
512
+ rb_hash_aset(hash, object, ULONG2NUM((unsigned long) n)); // TODO: This is hackish, maybe its better to also wrap the nodes.
513
+
514
+ return self;
515
+ }
516
+
517
+ /* call-seq:
518
+ * min -> [object, priority]
519
+ *
520
+ * Return the pair [object, priority] with minimal priority or nil when the
521
+ * queue is empty.
522
+ *
523
+ * q = PriorityQueue.new
524
+ * q["a"] = 10
525
+ * q["b"] = 20
526
+ * q.min #=> ["a", 10]
527
+ * q.delete_min #=> ["a", 10]
528
+ * q.min #=> ["b", 20]
529
+ * q.delete_min #=> ["b", 20]
530
+ * q.min #=> nil
531
+ */
532
+ static
533
+ VALUE pq_min(VALUE self) {
534
+ priority_queue* q = get_pq_from_value(self);
535
+
536
+ priority_node* n = priority_queue_min(q);
537
+ if (n)
538
+ return rb_ary_new3(2, n->object, n->priority);
539
+ else
540
+ return Qnil;
541
+ }
542
+
543
+ /* call-seq:
544
+ * min_key -> object
545
+ *
546
+ * Return the key that has the minimal priority or nil when the queue is empty.
547
+ *
548
+ * q = PriorityQueue.new
549
+ * q["a"] = 10
550
+ * q["b"] = 20
551
+ * q.min_key #=> "a"
552
+ * q.delete_min #=> ["a", 10]
553
+ * q.min_key #=> "b"
554
+ * q.delete_min #=> ["b", 20]
555
+ * q.min_key #=> nil
556
+ */
557
+ static
558
+ VALUE pq_min_key(VALUE self) {
559
+ priority_queue* q = get_pq_from_value(self);
560
+
561
+ priority_node* n = priority_queue_min(q);
562
+ if (n)
563
+ return n->object;
564
+ else
565
+ return Qnil;
566
+ }
567
+
568
+ /* call-seq:
569
+ * min_priority -> priority
570
+ *
571
+ * Return the minimal priority or nil when the queue is empty.
572
+ *
573
+ * q = PriorityQueue.new
574
+ * q["a"] = 10
575
+ * q["b"] = 20
576
+ * q.min_priority #=> 10
577
+ * q.delete_min #=> ["a", 10]
578
+ * q.min_priority #=> 20
579
+ * q.delete_min #=> ["b", 20]
580
+ * q.min_priority #=> nil
581
+ */
582
+ static
583
+ VALUE pq_min_priority(VALUE self) {
584
+ priority_queue* q = get_pq_from_value(self);
585
+
586
+ priority_node* n = priority_queue_min(q);
587
+ if (n)
588
+ return n->priority;
589
+ else
590
+ return Qnil;
591
+ }
592
+
593
+ /* call-seq:
594
+ * delete_min -> [key, priority]
595
+ *
596
+ * Delete key with minimal priority and return [key, priority]
597
+ *
598
+ * q = PriorityQueue.new
599
+ * q["a"] = 1
600
+ * q["b"] = 0
601
+ * q.delete_min #=> ["b", 0]
602
+ * q.delete_min #=> ["a", 1]
603
+ * q.delete_min #=> nil
604
+ */
605
+ static
606
+ VALUE pq_delete_min(VALUE self) {
607
+ VALUE hash = rb_iv_get(self, "@__node_by_object__");
608
+ priority_queue* q = get_pq_from_value(self);
609
+
610
+ priority_node* n = priority_queue_delete_min(q);
611
+
612
+ if (n) {
613
+ rb_hash_delete(hash, n->object); // TODO: Maybe we have a problem here with garbage collection of n->object?
614
+ return rb_ary_new3(2, n->object, n->priority);
615
+ } else {
616
+ return Qnil;
617
+ }
618
+ }
619
+
620
+ /* call-seq:
621
+ * delete_min_return_key -> key
622
+ *
623
+ * Delete key with minimal priority and return the key
624
+ *
625
+ * q = PriorityQueue.new
626
+ * q["a"] = 1
627
+ * q["b"] = 0
628
+ * q.delete_min_return_key #=> "b"
629
+ * q.delete_min_return_key #=> "a"
630
+ * q.delete_min_return_key #=> nil
631
+ */
632
+ static
633
+ VALUE pq_delete_min_return_key(VALUE self) {
634
+ VALUE hash = rb_iv_get(self, "@__node_by_object__");
635
+ priority_queue* q = get_pq_from_value(self);
636
+
637
+ priority_node* n = priority_queue_delete_min(q);
638
+
639
+ if (n) {
640
+ rb_hash_delete(hash, n->object); // TODO: Maybe we have a problem here with garbage collection of n->object?
641
+ return n->object;
642
+ } else {
643
+ return Qnil;
644
+ }
645
+ }
646
+
647
+ /*
648
+ * call-seq:
649
+ * delete_min_return_priority -> priority
650
+ *
651
+ * Delete key with minimal priority and return the priority value
652
+ *
653
+ * q = PriorityQueue.new
654
+ * q["a"] = 1
655
+ * q["b"] = 0
656
+ * q.delete_min_return_priority #=> 0
657
+ * q.delete_min_return_priority #=> 1
658
+ * q.delete_min_return_priority #=> nil
659
+ */
660
+ static
661
+ VALUE pq_delete_min_return_priority(VALUE self) {
662
+ VALUE hash = rb_iv_get(self, "@__node_by_object__");
663
+ priority_queue* q = get_pq_from_value(self);
664
+
665
+ priority_node* n = priority_queue_delete_min(q);
666
+
667
+ if (n) {
668
+ rb_hash_delete(hash, n->object); // TODO: Maybe we have a problem here with garbage collection of n->object?
669
+ return n->priority;
670
+ } else {
671
+ return Qnil;
672
+ }
673
+ }
674
+
675
+ /*
676
+ * call-seq:
677
+ * [key] = priority
678
+ * change_priority(key, priority)
679
+ * push(key, priority)
680
+ *
681
+ * Set the priority of a key.
682
+ *
683
+ * q = PriorityQueue.new
684
+ * q["car"] = 50
685
+ * q["train"] = 50
686
+ * q["bike"] = 10
687
+ * q.min #=> ["bike", 10]
688
+ * q["car"] = 0
689
+ * q.min #=> ["car", 0]
690
+ */
691
+ static
692
+ VALUE pq_change_priority(VALUE self, VALUE object, VALUE priority) {
693
+ VALUE hash = rb_iv_get(self, "@__node_by_object__");
694
+ priority_queue* q = get_pq_from_value(self);
695
+
696
+ VALUE node = rb_hash_aref(hash, object);
697
+ if (NIL_P(node)) {
698
+ pq_push(self, object, priority);
699
+ } else {
700
+ priority_queue_change_priority(q, (priority_node*) NUM2ULONG(node), priority);
701
+ }
702
+
703
+ return self;
704
+ }
705
+
706
+ /*
707
+ * call-seq:
708
+ * [key] -> priority
709
+ *
710
+ * Return the priority of a key or nil if the key is not in the queue.
711
+ *
712
+ * q = PriorityQueue.new
713
+ * (0..10).each do | i | q[i.to_s] = i end
714
+ * q["5"] #=> 5
715
+ * q[5] #=> nil
716
+ */
717
+ static
718
+ VALUE pq_get_priority(VALUE self, VALUE object) {
719
+ VALUE hash = rb_iv_get(self, "@__node_by_object__");
720
+
721
+ VALUE node_pointer = rb_hash_aref(hash, object);
722
+
723
+ if (NIL_P(node_pointer))
724
+ return Qnil;
725
+ else
726
+ return (VALUE) (((priority_node*) NUM2ULONG(node_pointer))->priority);
727
+ }
728
+
729
+ /*
730
+ * call-seq:
731
+ * has_key? key -> boolean
732
+ *
733
+ * Return false if the key is not in the queue, true otherwise.
734
+ *
735
+ * q = PriorityQueue.new
736
+ * (0..10).each do | i | q[i.to_s] = i end
737
+ * q.has_key("5") #=> true
738
+ * q.has_key(5) #=> false
739
+ */
740
+ static
741
+ VALUE pq_has_key(VALUE self, VALUE object) {
742
+ VALUE hash = rb_iv_get(self, "@__node_by_object__");
743
+
744
+ VALUE node_pointer = rb_hash_aref(hash, object);
745
+
746
+ return NIL_P(node_pointer) ? Qfalse : Qtrue;
747
+ }
748
+ /* call-seq:
749
+ * length -> Fixnum
750
+ *
751
+ * Returns the number of elements of the queue.
752
+ *
753
+ * q = PriorityQueue.new
754
+ * q.length #=> 0
755
+ * q[0] = 1
756
+ * q.length #=> 1
757
+ */
758
+ static
759
+ VALUE pq_length(VALUE self) {
760
+ priority_queue* q = get_pq_from_value(self);
761
+
762
+ return INT2NUM(q->length);
763
+ }
764
+
765
+ /* call-seq:
766
+ * delete(key) -> [key, priority]
767
+ * delete(key) -> nil
768
+ *
769
+ * Delete a key from the priority queue. Returns nil when the key was not in
770
+ * the queue and [key, priority] otherwise.
771
+ *
772
+ * q = PriorityQueue.new
773
+ * (0..10).each do | i | q[i.to_s] = i end
774
+ * q.delete(5) #=> ["5", 5]
775
+ * q.delete(5) #=> nil
776
+ */
777
+ static
778
+ VALUE pq_delete(VALUE self, VALUE object) {
779
+ priority_queue* q = get_pq_from_value(self);
780
+
781
+ VALUE hash = rb_iv_get(self, "@__node_by_object__");
782
+
783
+ VALUE node_pointer = rb_hash_aref(hash, object);
784
+
785
+ if (NIL_P(node_pointer))
786
+ return Qnil;
787
+ else {
788
+ priority_node* n = (priority_node*) NUM2ULONG(node_pointer);
789
+ VALUE object = n->object;
790
+ VALUE priority = n->priority;
791
+ priority_queue_delete(q, n);
792
+ rb_hash_delete(hash, object);
793
+ priority_node_free(n);
794
+ return rb_ary_new3(2, object, priority);
795
+ }
796
+ }
797
+
798
+
799
+ // Dot a single node of a priority queue. Called by pq_to_dot to do the inner work.
800
+ // (I'm not proud of this function ;-( )
801
+ static
802
+ void pq_node2dot(VALUE result_string, priority_node* n, unsigned int level) {
803
+ if (n == NULL) return;
804
+ unsigned int i;
805
+ for (i=0; i<level; i++) rb_str_cat2(result_string, " ");
806
+ if (n->mark)
807
+ rb_str_concat(result_string,
808
+ rb_funcall(Qnil, id_format, 4, rb_str_new2("NODE%i [label=\"%s (%s)\"];\n"),
809
+ ULONG2NUM((unsigned long) n), n->object, n->priority));
810
+ else
811
+ rb_str_concat(result_string,
812
+ rb_funcall(Qnil, id_format, 4, rb_str_new2("NODE%i [label=\"%s (%s)\",shape=box];\n"),
813
+ ULONG2NUM((unsigned long) n), n->object, n->priority));
814
+ if (n->child != NULL) {
815
+ priority_node* n1 = n->child;
816
+ do {
817
+ pq_node2dot(result_string, n1, level + 1);
818
+ for (i=0; i<level; i++) rb_str_cat2(result_string, " ");
819
+ rb_str_concat(result_string,
820
+ rb_funcall(Qnil, id_format, 4, rb_str_new2("NODE%i -> NODE%i;\n"),
821
+ ULONG2NUM((unsigned long) n), ULONG2NUM((unsigned long) n1)));
822
+ n1 = n1->right;
823
+ } while(n1 != n->child);
824
+ }
825
+ }
826
+
827
+ /*
828
+ * Print a priority queue as a dot-graph. The output can be fed to dot from the
829
+ * vizgraph suite to create a tree depicting the internal datastructure.
830
+ *
831
+ * (I'm not proud of this function ;-( )
832
+ */
833
+ static
834
+ VALUE pq_to_dot(VALUE self) {
835
+ priority_queue* q = get_pq_from_value(self);
836
+
837
+ VALUE result_string = rb_str_new2("digraph fibonacci_heap {\n");
838
+ if (q->rootlist) {
839
+ priority_node* n1 = q->rootlist;
840
+ do {
841
+ pq_node2dot(result_string, n1, 1);
842
+ n1 = n1->right;
843
+ } while(n1 != q->rootlist);
844
+ }
845
+ rb_str_cat2(result_string, "}\n");
846
+ return result_string;
847
+ }
848
+
849
+ /*
850
+ * Returns true if the array is empty, false otherwise.
851
+ */
852
+ static
853
+ VALUE pq_empty(VALUE self) {
854
+ priority_queue* q = get_pq_from_value(self);
855
+ return priority_queue_empty(q) ? Qtrue : Qfalse;
856
+ }
857
+
858
+ static
859
+ void pq_each_helper(priority_queue *q, priority_node *n, void *args) {
860
+ rb_yield(rb_ary_new3(2, n->object, n->priority));
861
+ };
862
+
863
+ /*
864
+ * Call the given block with each [key, priority] pair in the queue
865
+ *
866
+ * Beware: Changing the queue in the block may lead to unwanted behaviour and
867
+ * even infinite loops.
868
+ */
869
+ static
870
+ VALUE pq_each(VALUE self) {
871
+ priority_queue* q = get_pq_from_value(self);
872
+ priority_queue_each(q, &pq_each_helper, NULL);
873
+ return self;
874
+ }
875
+
876
+ static
877
+ VALUE pq_insert_node(VALUE node, VALUE queue) {
878
+ return pq_push(queue, rb_ary_entry(node, 0), rb_ary_entry(node, 1));
879
+ }
880
+
881
+ static
882
+ VALUE pq_initialize_copy(VALUE copy, VALUE orig) {
883
+ if (copy == orig)
884
+ return copy;
885
+
886
+ rb_iterate(rb_each, orig, pq_insert_node, copy);
887
+
888
+ return copy;
889
+ }
890
+
891
+ /*
892
+ * Returns a string representation of the priority queue.
893
+ */
894
+ static
895
+ VALUE pq_inspect(VALUE self) {
896
+ VALUE result = rb_str_new2("<PriorityQueue: ");
897
+ rb_str_concat(result,
898
+ rb_funcall(rb_funcall(self, rb_intern("to_a"), 0),
899
+ rb_intern("inspect"), 0));
900
+ rb_str_concat(result, rb_str_new2(">"));
901
+ return result;
902
+ }
903
+
904
+ VALUE cPriorityQueue;
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
+ cPriorityQueue = rb_define_class("CPriorityQueue", rb_cObject);
924
+
925
+ rb_define_alloc_func(cPriorityQueue, pq_alloc);
926
+ rb_define_method(cPriorityQueue, "initialize", pq_init, 0);
927
+ rb_define_method(cPriorityQueue, "initialize_copy", pq_initialize_copy, 1);
928
+ rb_define_method(cPriorityQueue, "min", pq_min, 0);
929
+ rb_define_method(cPriorityQueue, "min_key", pq_min_key, 0);
930
+ rb_define_method(cPriorityQueue, "min_priority", pq_min_priority, 0);
931
+ rb_define_method(cPriorityQueue, "delete_min", pq_delete_min, 0);
932
+ rb_define_method(cPriorityQueue, "delete_min_return_key", pq_delete_min_return_key, 0);
933
+ rb_define_method(cPriorityQueue, "delete_min_return_priority", pq_delete_min_return_priority, 0);
934
+ rb_define_method(cPriorityQueue, "push", pq_change_priority, 2);
935
+ rb_define_method(cPriorityQueue, "change_priority", pq_change_priority, 2);
936
+ rb_define_method(cPriorityQueue, "[]=", pq_change_priority, 2);
937
+ rb_define_method(cPriorityQueue, "priority", pq_get_priority, 1);
938
+ rb_define_method(cPriorityQueue, "[]", pq_get_priority, 1);
939
+ rb_define_method(cPriorityQueue, "has_key?", pq_has_key, 1);
940
+ rb_define_method(cPriorityQueue, "length", pq_length, 0);
941
+ rb_define_method(cPriorityQueue, "to_dot", pq_to_dot, 0);
942
+ rb_define_method(cPriorityQueue, "empty?", pq_empty, 0);
943
+ rb_define_method(cPriorityQueue, "delete", pq_delete, 1);
944
+ rb_define_method(cPriorityQueue, "inspect", pq_inspect, 0);
945
+ rb_define_method(cPriorityQueue, "each", pq_each, 0);
946
+ rb_include_module(cPriorityQueue, rb_eval_string("Enumerable"));
947
+ }