PriorityQueue 0.1.0

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.
@@ -0,0 +1,19 @@
1
+ 0.1.0
2
+ * API changes
3
+ * Added lots of unit tests
4
+ * Restructured
5
+ * Fallback to ruby version if c version is not available
6
+ * Added Efficent Pure Ruby Implementation (3 times slower than c version in
7
+ dijkstras algorithm)
8
+ * Added "Poor Mans Priority Queue" as a simple reference Implementation
9
+ * Minor improvements
10
+ * Added possibility to increase keys
11
+ * Minor bugs fixed
12
+ * Released as a .tar.gz (setup.rb)
13
+ * Released as a .gem (Anybody want to improve the distribution or point me to
14
+ some information on how to relase as .tar.gz and as .gem without too much
15
+ ado)
16
+
17
+ 0.0.0:
18
+ * First c-implementation
19
+ * Experimental Release
data/README ADDED
@@ -0,0 +1,85 @@
1
+ # Ruby extension implementing a priority queue
2
+
3
+ ## Description
4
+ This is a fibonacy heap priority queue implementation. That means
5
+
6
+ insert: O(1)
7
+ decrease_priority: Amortized O(1)
8
+ delete_min: Amortized O(log n)
9
+
10
+ This project is different from K. Kodamas PQueue in that it allows a decrease
11
+ key operation. That makes PriorityQueue usable for algorithms like dijkstras
12
+ shortest path algorithm, while PQueue is more suitable for Heapsort and the
13
+ like.
14
+
15
+ ## Legal stuff
16
+ (c) 2005 Brian Schr�der
17
+
18
+ Please submit bugreports to priority_queue@brian-schroeder.de
19
+
20
+ This extension is under the same license as ruby.
21
+
22
+ Do not hold me reliable for anything that happens to you, your programs or
23
+ anything else because of this extension. It worked for me, but there is no
24
+ guarantee it will work for you.
25
+
26
+ ## Requirements
27
+ * Ruby 1.8
28
+ * c Compiler
29
+
30
+ ## Installation
31
+
32
+ De-compress archive and enter its top directory.
33
+ Then type:
34
+
35
+ ($ su)
36
+ $ ruby setup.rb
37
+
38
+ These simple step installs this program under the default
39
+ location of Ruby libraries. You can also install files into
40
+ your favorite directory by supplying setup.rb some options.
41
+ Try "ruby setup.rb --help".
42
+
43
+ ## Usage
44
+
45
+ In this priority queue implementation the queue behaves similarly to a hash
46
+ that maps objects onto priorities.
47
+
48
+ ### Hash Interface
49
+ require 'priority_queue'
50
+
51
+ q = PriorityQueue.new
52
+ q["node1"] = 0
53
+ q["node2"] = 1
54
+ q.min #=> "node1"
55
+ q[q.min] #=> 0
56
+ q.min_value #=> 0
57
+
58
+ q["node2"] = -1
59
+ q.delete_min #=> "node2", 1
60
+ q["node2"] #= nil
61
+ q["node3"] = 1
62
+
63
+ q.delete("node3") #=> "node3", 1
64
+ q.delete("node2") #=> nil
65
+
66
+
67
+ ### Queue Interface
68
+ require 'priority_queue'
69
+
70
+ q = PriorityQueue.new
71
+ q.push "node1", 0
72
+ q.push "node2", 1
73
+
74
+ q.min #=> "node1"
75
+
76
+ q.decrease_priority("node2", -1)
77
+
78
+ q.pop_min #=> "node2"
79
+ q.min #=> "node1"
80
+
81
+ for more exmples look into the documentation, the unit tests and the benchmark
82
+ suite.
83
+
84
+ # Todo
85
+ * Only write documentation once
@@ -0,0 +1,169 @@
1
+ require 'PriorityQueue/RubyPriorityQueue'
2
+ require 'PriorityQueue/PoorPriorityQueue'
3
+ require 'PriorityQueue/CPriorityQueue'
4
+ require 'pqueue'
5
+ require 'benchmark'
6
+
7
+ class Node
8
+ attr_reader :neighbours, :id
9
+
10
+ def initialize(id)
11
+ @neighbours = []
12
+ @id = id
13
+ end
14
+
15
+ def inspect
16
+ to_s
17
+ end
18
+
19
+ def to_s
20
+ "(#{@id})"
21
+ end
22
+ end
23
+
24
+ # Build a graph by adding nodes with random connections
25
+
26
+ # Return a random graph with an average degree of degree
27
+ def make_graph(nodes, degree)
28
+ nodes = Array.new(nodes) { | i | Node.new(i.to_s) }
29
+ nodes.each do | n |
30
+ (degree / 2).times do
31
+ true while (n1 = nodes[rand(nodes.length)]) == n
32
+ n.neighbours << nodes[rand(nodes.length)]
33
+ n1.neighbours << n
34
+ n.neighbours << n1
35
+ end
36
+ end
37
+ end
38
+
39
+ def draw_graph(nodes, out)
40
+ dot = [] << "graph g {"
41
+ nodes.each do | n1 |
42
+ dot << "N#{n1.id} [label='#{n1.id}'];"
43
+ n1.neighbours.each do | n2 |
44
+ dot << "N#{n1.id} -- N#{n2.id};" if n1.id <= n2.id
45
+ end
46
+ end
47
+ dot << "}"
48
+
49
+ # system "echo '#{dot}' | neato -Gepsilon=0.001 -Goverlap=scale -Gsplines=true -Gsep=.4 -Tps -o #{out}"
50
+ system "echo '#{dot}' | neato -Gepsilon=0.05 -Goverlap=scale -Gsep=.4 -Tps -o #{out}"
51
+ end
52
+
53
+ def dijkstra(start_node, queue_klass)
54
+ # Priority Queue with unfinished nodes
55
+ active = queue_klass.new
56
+ # Distances for all nodes
57
+ distances = Hash.new { 1.0 / 0.0 }
58
+ # Parent pointers describing shortest paths for all nodes
59
+ parents = Hash.new
60
+
61
+ # Initialize with start node
62
+ active[start_node] = 0
63
+ until active.empty?
64
+ u, distance = active.delete_min
65
+ distances[u] = distance
66
+ d = distance + 1
67
+ u.neighbours.each do | v |
68
+ next unless d < distances[v] # we can't relax this one
69
+ active[v] = distances[v] = d
70
+ parents[v] = u
71
+ end
72
+ end
73
+ end
74
+
75
+ srand
76
+
77
+ sizes = Array.new(4) { | base | Array.new(9) { | mult | (mult+1) * 10**(base+2) } }.flatten
78
+ degrees = [2, 4, 16]
79
+ degrees = [4, 16]
80
+ degrees = [16]
81
+ queues = [ CPriorityQueue, PoorPriorityQueue, RubyPriorityQueue ]
82
+
83
+ max_time = 400
84
+ ignore = Hash.new
85
+
86
+ repeats = 5
87
+
88
+
89
+ STDOUT.sync = true
90
+
91
+ results = Hash.new { | h, k | h[k] =
92
+ Hash.new { | h1, k1 | h1[k1] = Hash.new { 0 }
93
+ }
94
+ }
95
+
96
+ Benchmark.bm(30) do | b |
97
+ sizes.each do | size |
98
+ break if !ignore.empty? and ignore.values.inject(true) { | r, v | r and v }
99
+ degrees.each do | degree |
100
+ p ignore
101
+ repeats.times do | r |
102
+ nodes = make_graph(size, degree)
103
+ queues.each do | queue |
104
+ next if ignore[queue]
105
+ GC.start
106
+ results[queue][degree][size] += (b.report("#{queue}: #{size} (#{degree})") do dijkstra(nodes[1], queue) end).real
107
+ end
108
+ end
109
+ queues.each do | queue |
110
+ ignore[queue] ||= ((results[queue][degree][size] / repeats) > max_time)
111
+ end
112
+ end
113
+
114
+ indices = queues.map { | q | degrees.map { | d | %&"#{q} (Graph of Degree: #{d})"& } }.flatten
115
+ File.open("results.csv", "wb") do | f |
116
+ f.puts "size\t" + indices.join("\t")
117
+ sizes.each do | size |
118
+ f.puts "#{size}\t" + queues.map { | q | degrees.map { | d |
119
+ (results[q][d].has_key?(size) and results[q][d][size] > 0.0) ? results[q][d][size] / repeats : "''"
120
+ } }.join("\t")
121
+ end
122
+ end
123
+
124
+ File.open("results.gp", 'wb') do | f |
125
+ lines = []
126
+ indices.each_with_index do | t, i |
127
+ lines << " 'results.csv' using 1:#{i+2} with lines title #{t}"
128
+ end
129
+ f.puts "set term png"
130
+ f.puts "set out 'results.png'"
131
+ f.puts "set xlabel 'Number of nodes'"
132
+ f.puts "set ylabel 'Time in seconds (real)'"
133
+ f.puts "set logscale xy"
134
+ f.puts "set title 'Dijkstras Shortest Path Algorithm using different PQ Implementations'"
135
+ f.puts "plot \\"
136
+ f.puts lines.join(",\\\n")
137
+ end
138
+ system "gnuplot results.gp"
139
+
140
+ queues.each do | q |
141
+ File.open("result-#{q}.gp", 'wb') do | f |
142
+ lines = []
143
+ degrees.map { | d | %&"#{q} (Graph of Degree: #{d})"& }.flatten.each do | t |
144
+ lines << " 'results.csv' using 1:#{indices.index(t)+2} with lines title #{t}"
145
+ end
146
+ f.puts "set term png"
147
+ f.puts "set out 'result-#{q}.png'"
148
+ f.puts "set xlabel 'Number of nodes'"
149
+ f.puts "set ylabel 'Time in seconds (real)'"
150
+ f.puts "set logscale xy"
151
+ f.puts "set title 'Dijkstras Shortest Path Algorithm on Networks of different degrees'"
152
+ f.puts "plot \\"
153
+ f.puts lines.join(",\\\n")
154
+ end
155
+ system "gnuplot result-#{q}.gp"
156
+ end
157
+ end
158
+ end
159
+
160
+ __END__
161
+
162
+ nodes = make_graph(100, 4)
163
+ draw_graph(nodes, "100-4.ps")
164
+ nodes = make_graph(100, 10)
165
+ draw_graph(nodes, "100-10.ps")
166
+ nodes = make_graph(10, 10)
167
+ draw_graph(nodes, "10-10.ps")
168
+ nodes = make_graph(1000, 2)
169
+ draw_graph(nodes, "1000-2.ps")
@@ -0,0 +1,8 @@
1
+ set term png
2
+ set out 'result-CPriorityQueue.png'
3
+ set xlabel 'Number of nodes'
4
+ set ylabel 'Time in seconds (real)'
5
+ set logscale xy
6
+ set title 'Dijkstras Shortest Path Algorithm on Networks of different degrees'
7
+ plot \
8
+ 'results.csv' using 1:2 with lines title "CPriorityQueue (Graph of Degree: 16)"
@@ -0,0 +1,8 @@
1
+ set term png
2
+ set out 'result-PoorPriorityQueue.png'
3
+ set xlabel 'Number of nodes'
4
+ set ylabel 'Time in seconds (real)'
5
+ set logscale xy
6
+ set title 'Dijkstras Shortest Path Algorithm on Networks of different degrees'
7
+ plot \
8
+ 'results.csv' using 1:3 with lines title "PoorPriorityQueue (Graph of Degree: 16)"
@@ -0,0 +1,6 @@
1
+ set term png
2
+ set out 'result-PriorityQueue.png'
3
+ set xlabel 'Number of nodes'
4
+ set ylabel 'Time in seconds (real)'
5
+ plot \
6
+ 'results.csv' using 1:2 with lines title "PriorityQueue (Graph of Degree: 16)"
@@ -0,0 +1,8 @@
1
+ set term png
2
+ set out 'result-RubyPriorityQueue.png'
3
+ set xlabel 'Number of nodes'
4
+ set ylabel 'Time in seconds (real)'
5
+ set logscale xy
6
+ set title 'Dijkstras Shortest Path Algorithm on Networks of different degrees'
7
+ plot \
8
+ 'results.csv' using 1:4 with lines title "RubyPriorityQueue (Graph of Degree: 16)"
@@ -0,0 +1,37 @@
1
+ size "CPriorityQueue (Graph of Degree: 16)" "PoorPriorityQueue (Graph of Degree: 16)" "RubyPriorityQueue (Graph of Degree: 16)"
2
+ 100 0.00583611594306098 0.0372195773654514 0.0697224669986301
3
+ 200 0.0115931034088135 0.0454702642228868 0.545423454708523
4
+ 300 0.0729258590274387 0.480532619688246 0.325443373786079
5
+ 400 0.189983632829454 0.993642012278239 0.342083692550659
6
+ 500 0.196244796117147 1.43146456612481 0.533440616395738
7
+ 600 0.202457533942329 2.06269836425781 0.600252681308322
8
+ 700 0.264183335834079 2.801170402103 0.72682441605462
9
+ 800 0.270467466778225 3.94072720739577 0.799134042527941
10
+ 900 0.443829695383708 4.89565277099609 0.985143608517117
11
+ 1000 0.465132978227403 6.15393000178867 1.00162262386746
12
+ 2000 0.740832858615451 27.4882550504473 2.40040622817145
13
+ 3000 1.07575758298238 55.2952709992727 3.59957594341702
14
+ 4000 1.47195853127374 '' 4.79923190010919
15
+ 5000 1.86880042817858 '' 6.23916390207079
16
+ 6000 2.40524856249491 '' 7.67889030774434
17
+ 7000 2.80519718594021 '' 9.1503213511573
18
+ 8000 3.32880263858371 '' 11.0491805076599
19
+ 9000 3.58584565586514 '' 11.493485238817
20
+ 10000 3.9917475912306 '' 13.0166101190779
21
+ 20000 8.20343301031325 '' 26.9999283419715
22
+ 30000 13.1478295061323 '' 43.9837118784587
23
+ 40000 16.7911659081777 '' ''
24
+ 50000 22.3882454766168 '' ''
25
+ 60000 26.7571367687649 '' ''
26
+ 70000 29.91215411822 '' ''
27
+ 80000 37.1148204538557 '' ''
28
+ 90000 '' '' ''
29
+ 100000 '' '' ''
30
+ 200000 '' '' ''
31
+ 300000 '' '' ''
32
+ 400000 '' '' ''
33
+ 500000 '' '' ''
34
+ 600000 '' '' ''
35
+ 700000 '' '' ''
36
+ 800000 '' '' ''
37
+ 900000 '' '' ''
@@ -0,0 +1,10 @@
1
+ set term png
2
+ set out 'results.png'
3
+ set xlabel 'Number of nodes'
4
+ set ylabel 'Time in seconds (real)'
5
+ set logscale xy
6
+ set title 'Dijkstras Shortest Path Algorithm using different PQ Implementations'
7
+ plot \
8
+ 'results.csv' using 1:2 with lines title "CPriorityQueue (Graph of Degree: 16)",\
9
+ 'results.csv' using 1:3 with lines title "PoorPriorityQueue (Graph of Degree: 16)",\
10
+ 'results.csv' using 1:4 with lines title "RubyPriorityQueue (Graph of Degree: 16)"
Binary file
Binary file
@@ -0,0 +1,2 @@
1
+ require 'mkmf'
2
+ create_makefile("CPriorityQueue")
@@ -0,0 +1,874 @@
1
+ /*
2
+ * :main:CPriorityQueue
3
+ *
4
+ * Ruby extension implementing a priority queue
5
+ *
6
+ * This is a fibonacy 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
+ /*
518
+ * Return the pair [object, priority] with minimal priority
519
+ */
520
+ static
521
+ VALUE pq_min(VALUE self) {
522
+ priority_queue* q = get_pq_from_value(self);
523
+
524
+ priority_node* n = priority_queue_min(q);
525
+ if (n)
526
+ return rb_ary_new3(2, n->object, n->priority);
527
+ else
528
+ return Qnil;
529
+ }
530
+
531
+ /*
532
+ * Return the key that has the minimal priority
533
+ */
534
+ static
535
+ VALUE pq_min_key(VALUE self) {
536
+ priority_queue* q = get_pq_from_value(self);
537
+
538
+ priority_node* n = priority_queue_min(q);
539
+ if (n)
540
+ return n->object;
541
+ else
542
+ return Qnil;
543
+ }
544
+
545
+ /*
546
+ * Return the minimal priority
547
+ */
548
+ static
549
+ VALUE pq_min_priority(VALUE self) {
550
+ priority_queue* q = get_pq_from_value(self);
551
+
552
+ priority_node* n = priority_queue_min(q);
553
+ if (n)
554
+ return n->priority;
555
+ else
556
+ return Qnil;
557
+ }
558
+
559
+ /*
560
+ * Delete key with minimal priority and return [key, priority]
561
+ */
562
+ static
563
+ VALUE pq_delete_min(VALUE self) {
564
+ VALUE hash = rb_iv_get(self, "@__node_by_object__");
565
+ priority_queue* q = get_pq_from_value(self);
566
+
567
+ priority_node* n = priority_queue_delete_min(q);
568
+
569
+ if (n) {
570
+ rb_hash_delete(hash, n->object); // TODO: Maybe we have a problem here with garbage collection of n->object?
571
+ return rb_ary_new3(2, n->object, n->priority);
572
+ } else {
573
+ return Qnil;
574
+ }
575
+ }
576
+
577
+ /*
578
+ * Delete key with minimal priority and return the key
579
+ */
580
+ static
581
+ VALUE pq_delete_min_return_key(VALUE self) {
582
+ VALUE hash = rb_iv_get(self, "@__node_by_object__");
583
+ priority_queue* q = get_pq_from_value(self);
584
+
585
+ priority_node* n = priority_queue_delete_min(q);
586
+
587
+ if (n) {
588
+ rb_hash_delete(hash, n->object); // TODO: Maybe we have a problem here with garbage collection of n->object?
589
+ return n->object;
590
+ } else {
591
+ return Qnil;
592
+ }
593
+ }
594
+
595
+ /*
596
+ * Delete key with minimal priority and return the priority value
597
+ */
598
+ static
599
+ VALUE pq_delete_min_return_priority(VALUE self) {
600
+ VALUE hash = rb_iv_get(self, "@__node_by_object__");
601
+ priority_queue* q = get_pq_from_value(self);
602
+
603
+ priority_node* n = priority_queue_delete_min(q);
604
+
605
+ if (n) {
606
+ rb_hash_delete(hash, n->object); // TODO: Maybe we have a problem here with garbage collection of n->object?
607
+ return n->priority;
608
+ } else {
609
+ return Qnil;
610
+ }
611
+ }
612
+
613
+ /*
614
+ * call-seq:
615
+ * queue[key] = priority
616
+ *
617
+ * Set the priority of a key.
618
+ *
619
+ * q = PriorityQueue.new
620
+ * q["car"] = 50
621
+ * q["train"] = 50
622
+ * q["bike"] = 10
623
+ * q.min #=> ["bike", 10]
624
+ * q["car"] = 0
625
+ * q.min #=> ["car", 0]
626
+ */
627
+ static
628
+ VALUE pq_change_priority(VALUE self, VALUE object, VALUE priority) {
629
+ VALUE hash = rb_iv_get(self, "@__node_by_object__");
630
+ priority_queue* q = get_pq_from_value(self);
631
+
632
+ VALUE node = rb_hash_aref(hash, object);
633
+ if (NIL_P(node)) {
634
+ pq_push(self, object, priority);
635
+ } else {
636
+ priority_queue_change_priority(q, (priority_node*) NUM2ULONG(node), priority);
637
+ }
638
+
639
+ return self;
640
+ }
641
+
642
+ /*
643
+ * call-seq:
644
+ * queue[key] -> priority
645
+ *
646
+ * Return the priority of a key or nil if the key is not in the queue.
647
+ *
648
+ * q = PriorityQueue.new
649
+ * (0..10).each do | i | q[i.to_s] = i end
650
+ * q["5"] #=> 5
651
+ * q[5] #=> nil
652
+ */
653
+ static
654
+ VALUE pq_get_priority(VALUE self, VALUE object) {
655
+ VALUE hash = rb_iv_get(self, "@__node_by_object__");
656
+
657
+ VALUE node_pointer = rb_hash_aref(hash, object);
658
+
659
+ if (NIL_P(node_pointer))
660
+ return Qnil;
661
+ else
662
+ return (VALUE) (((priority_node*) NUM2ULONG(node_pointer))->priority);
663
+ }
664
+
665
+ /*
666
+ * Return false if the key is not in the queue, true otherwise.
667
+ *
668
+ * q = PriorityQueue.new
669
+ * (0..10).each do | i | q[i.to_s] = i end
670
+ * q.has_key("5") #=> true
671
+ * q.has_key(5) #=> false
672
+ */
673
+ static
674
+ VALUE pq_has_key(VALUE self, VALUE object) {
675
+ VALUE hash = rb_iv_get(self, "@__node_by_object__");
676
+
677
+ VALUE node_pointer = rb_hash_aref(hash, object);
678
+
679
+ return NIL_P(node_pointer) ? Qfalse : Qtrue;
680
+ }
681
+ /*
682
+ * Returns the number of elements of the queue.
683
+ *
684
+ * q = PriorityQueue.new
685
+ * q.length #=> 0
686
+ * q[0] = 1
687
+ * q.length #=> 1
688
+ */
689
+ static
690
+ VALUE pq_length(VALUE self) {
691
+ priority_queue* q = get_pq_from_value(self);
692
+
693
+ return INT2NUM(q->length);
694
+ }
695
+
696
+ /*
697
+ * Delete a key from the priority queue. Returns nil when the key was not in
698
+ * the queue and [key, priority] otherwise.
699
+ *
700
+ * q = PriorityQueue.new
701
+ * (0..10).each do | i | q[i.to_s] = i end
702
+ * q.delete(5) #=> ["5", 5]
703
+ * q.delete(5) #=> nil
704
+ */
705
+ static
706
+ VALUE pq_delete(VALUE self, VALUE object) {
707
+ priority_queue* q = get_pq_from_value(self);
708
+
709
+ VALUE hash = rb_iv_get(self, "@__node_by_object__");
710
+
711
+ VALUE node_pointer = rb_hash_aref(hash, object);
712
+
713
+ if (NIL_P(node_pointer))
714
+ return Qnil;
715
+ else {
716
+ priority_node* n = (priority_node*) NUM2ULONG(node_pointer);
717
+ VALUE object = n->object;
718
+ VALUE priority = n->priority;
719
+ priority_queue_delete(q, n);
720
+ rb_hash_delete(hash, object);
721
+ priority_node_free(n);
722
+ return rb_ary_new3(2, object, priority);
723
+ }
724
+ }
725
+
726
+
727
+ // Dot a single node of a priority queue. Called by pq_to_dot to do the inner work.
728
+ // (I'm not proud of this function ;-( )
729
+ static
730
+ void pq_node2dot(VALUE result_string, priority_node* n, unsigned int level) {
731
+ if (n == NULL) return;
732
+ unsigned int i;
733
+ for (i=0; i<level; i++) rb_str_cat2(result_string, " ");
734
+ if (n->mark)
735
+ rb_str_concat(result_string,
736
+ rb_funcall(Qnil, id_format, 4, rb_str_new2("NODE%i [label=\"%s (%s)\"];\n"),
737
+ ULONG2NUM((unsigned long) n), n->object, n->priority));
738
+ else
739
+ rb_str_concat(result_string,
740
+ rb_funcall(Qnil, id_format, 4, rb_str_new2("NODE%i [label=\"%s (%s)\",shape=box];\n"),
741
+ ULONG2NUM((unsigned long) n), n->object, n->priority));
742
+ if (n->child != NULL) {
743
+ priority_node* n1 = n->child;
744
+ do {
745
+ pq_node2dot(result_string, n1, level + 1);
746
+ for (i=0; i<level; i++) rb_str_cat2(result_string, " ");
747
+ rb_str_concat(result_string,
748
+ rb_funcall(Qnil, id_format, 4, rb_str_new2("NODE%i -> NODE%i;\n"),
749
+ ULONG2NUM((unsigned long) n), ULONG2NUM((unsigned long) n1)));
750
+ n1 = n1->right;
751
+ } while(n1 != n->child);
752
+ }
753
+ }
754
+
755
+ /*
756
+ * Print a priority queue as a dot-graph. The output can be fed to dot from the
757
+ * vizgraph suite to create a tree depicting the internal datastructure.
758
+ *
759
+ * (I'm not proud of this function ;-( )
760
+ */
761
+ static
762
+ VALUE pq_to_dot(VALUE self) {
763
+ priority_queue* q = get_pq_from_value(self);
764
+
765
+ VALUE result_string = rb_str_new2("digraph fibonaccy_heap {\n");
766
+ if (q->rootlist) {
767
+ priority_node* n1 = q->rootlist;
768
+ do {
769
+ pq_node2dot(result_string, n1, 1);
770
+ n1 = n1->right;
771
+ } while(n1 != q->rootlist);
772
+ }
773
+ rb_str_cat2(result_string, "}\n");
774
+ return result_string;
775
+ }
776
+
777
+ /*
778
+ * Returns true if the array is empty, false otherwise.
779
+ */
780
+ static
781
+ VALUE pq_empty(VALUE self) {
782
+ priority_queue* q = get_pq_from_value(self);
783
+ return priority_queue_empty(q) ? Qtrue : Qfalse;
784
+ }
785
+
786
+ static
787
+ void pq_each_helper(priority_queue *q, priority_node *n, void *args) {
788
+ rb_yield(rb_ary_new3(2, n->object, n->priority));
789
+ };
790
+
791
+ /*
792
+ * Call the given block with each key, priority pair in the queue
793
+ *
794
+ * Beware: Changing the queue in the block may lead to unwanted behaviour and even infinite loops.
795
+ */
796
+ static
797
+ VALUE pq_each(VALUE self) {
798
+ priority_queue* q = get_pq_from_value(self);
799
+ priority_queue_each(q, &pq_each_helper, NULL);
800
+ return self;
801
+ }
802
+
803
+ static
804
+ VALUE pq_insert_node(VALUE node, VALUE queue) {
805
+ return pq_push(queue, rb_ary_entry(node, 0), rb_ary_entry(node, 1));
806
+ }
807
+
808
+ static
809
+ VALUE pq_init_copy(VALUE copy, VALUE orig) {
810
+ if (copy == orig)
811
+ return copy;
812
+
813
+ rb_iterate(rb_each, orig, pq_insert_node, copy);
814
+
815
+ return copy;
816
+ }
817
+
818
+ /*
819
+ * Returns a string representation of the priority queue.
820
+ */
821
+ static
822
+ VALUE pq_inspect(VALUE self) {
823
+ VALUE result = rb_str_new2("<PriorityQueue: ");
824
+ rb_str_concat(result,
825
+ rb_funcall(rb_funcall(self, rb_intern("to_a"), 0),
826
+ rb_intern("inspect"), 0));
827
+ rb_str_concat(result, rb_str_new2(">"));
828
+ return result;
829
+ }
830
+
831
+ VALUE cPriorityQueue;
832
+
833
+ /*
834
+ * A Priority Queue implementation
835
+ *
836
+ * A priority queue is a queue, where each element (the key) has an assigned
837
+ * priority. It is possible to efficently decrease priorities and to
838
+ * efficently look up and remove the key with the smallest priority.
839
+ *
840
+ * This datastructure is used in different algorithms. The standard algorithm
841
+ * used to introduce priority queues is dijkstra's shortest path algorithm.
842
+ *
843
+ * The priority queue includes the Enumerable module.
844
+ */
845
+ void Init_CPriorityQueue() {
846
+ id_compare_operator = rb_intern("<=>");
847
+ id_format = rb_intern("format");
848
+ id_display = rb_intern("display");
849
+
850
+ cPriorityQueue = rb_define_class("CPriorityQueue", rb_cObject);
851
+
852
+ rb_define_alloc_func(cPriorityQueue, pq_alloc);
853
+ rb_define_method(cPriorityQueue, "initialize", pq_init, 0);
854
+ rb_define_method(cPriorityQueue, "initialize_copy", pq_init_copy, 1);
855
+ rb_define_method(cPriorityQueue, "push", pq_push, 2);
856
+ rb_define_method(cPriorityQueue, "min", pq_min, 0);
857
+ rb_define_method(cPriorityQueue, "min_key", pq_min_key, 0);
858
+ rb_define_method(cPriorityQueue, "min_priority", pq_min_priority, 0);
859
+ rb_define_method(cPriorityQueue, "delete_min", pq_delete_min, 0);
860
+ rb_define_method(cPriorityQueue, "delete_min_return_key", pq_delete_min_return_key, 0);
861
+ rb_define_method(cPriorityQueue, "delete_min_return_priority", pq_delete_min_return_priority, 0);
862
+ rb_define_method(cPriorityQueue, "change_priority", pq_change_priority, 2);
863
+ rb_define_method(cPriorityQueue, "[]=", pq_change_priority, 2);
864
+ rb_define_method(cPriorityQueue, "priority", pq_get_priority, 1);
865
+ rb_define_method(cPriorityQueue, "[]", pq_get_priority, 1);
866
+ rb_define_method(cPriorityQueue, "has_key?", pq_has_key, 1);
867
+ rb_define_method(cPriorityQueue, "length", pq_length, 0);
868
+ rb_define_method(cPriorityQueue, "to_dot", pq_to_dot, 0);
869
+ rb_define_method(cPriorityQueue, "empty?", pq_empty, 0);
870
+ rb_define_method(cPriorityQueue, "delete", pq_delete, 1);
871
+ rb_define_method(cPriorityQueue, "inspect", pq_inspect, 0);
872
+ rb_define_method(cPriorityQueue, "each", pq_each, 0);
873
+ rb_include_module(cPriorityQueue, rb_eval_string("Enumerable"));
874
+ }