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.
- data/Gemfile +4 -0
- data/lib/jumoku.rb +4 -2
- data/lib/jumoku/version.rb +1 -1
- data/spec/raw_tree_spec.rb +353 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +14 -0
- data/spec/tree_spec.rb +553 -0
- data/vendor/git/graphy/CREDITS.md +31 -0
- data/vendor/git/graphy/LICENSE +35 -0
- data/vendor/git/graphy/README.md +186 -0
- data/vendor/git/graphy/Rakefile +61 -0
- data/vendor/git/graphy/TODO.md +20 -0
- data/vendor/git/graphy/VERSION +1 -0
- data/vendor/git/graphy/examples/graph_self.rb +56 -0
- data/vendor/git/graphy/examples/module_graph.jpg +0 -0
- data/vendor/git/graphy/examples/module_graph.rb +14 -0
- data/vendor/git/graphy/examples/self_graph.jpg +0 -0
- data/vendor/git/graphy/examples/visualize.jpg +0 -0
- data/vendor/git/graphy/examples/visualize.rb +10 -0
- data/vendor/git/graphy/graphy.gemspec +149 -0
- data/vendor/git/graphy/lib/graphy.rb +90 -0
- data/vendor/git/graphy/lib/graphy/adjacency_graph.rb +224 -0
- data/vendor/git/graphy/lib/graphy/arc.rb +65 -0
- data/vendor/git/graphy/lib/graphy/arc_number.rb +52 -0
- data/vendor/git/graphy/lib/graphy/biconnected.rb +84 -0
- data/vendor/git/graphy/lib/graphy/chinese_postman.rb +91 -0
- data/vendor/git/graphy/lib/graphy/classes/graph_classes.rb +28 -0
- data/vendor/git/graphy/lib/graphy/common.rb +63 -0
- data/vendor/git/graphy/lib/graphy/comparability.rb +63 -0
- data/vendor/git/graphy/lib/graphy/directed_graph.rb +76 -0
- data/vendor/git/graphy/lib/graphy/directed_graph/algorithms.rb +92 -0
- data/vendor/git/graphy/lib/graphy/directed_graph/distance.rb +167 -0
- data/vendor/git/graphy/lib/graphy/dot.rb +94 -0
- data/vendor/git/graphy/lib/graphy/edge.rb +37 -0
- data/vendor/git/graphy/lib/graphy/ext.rb +79 -0
- data/vendor/git/graphy/lib/graphy/graph.rb +631 -0
- data/vendor/git/graphy/lib/graphy/graph_api.rb +35 -0
- data/vendor/git/graphy/lib/graphy/labels.rb +113 -0
- data/vendor/git/graphy/lib/graphy/maximum_flow.rb +77 -0
- data/vendor/git/graphy/lib/graphy/ruby_compatibility.rb +17 -0
- data/vendor/git/graphy/lib/graphy/search.rb +511 -0
- data/vendor/git/graphy/lib/graphy/strong_components.rb +93 -0
- data/vendor/git/graphy/lib/graphy/support/support.rb +9 -0
- data/vendor/git/graphy/lib/graphy/undirected_graph.rb +57 -0
- data/vendor/git/graphy/lib/graphy/undirected_graph/algorithms.rb +90 -0
- data/vendor/git/graphy/spec/biconnected_spec.rb +27 -0
- data/vendor/git/graphy/spec/chinese_postman_spec.rb +27 -0
- data/vendor/git/graphy/spec/community_spec.rb +44 -0
- data/vendor/git/graphy/spec/complement_spec.rb +27 -0
- data/vendor/git/graphy/spec/digraph_distance_spec.rb +121 -0
- data/vendor/git/graphy/spec/digraph_spec.rb +339 -0
- data/vendor/git/graphy/spec/dot_spec.rb +48 -0
- data/vendor/git/graphy/spec/edge_spec.rb +159 -0
- data/vendor/git/graphy/spec/inspection_spec.rb +40 -0
- data/vendor/git/graphy/spec/multi_edge_spec.rb +32 -0
- data/vendor/git/graphy/spec/neighborhood_spec.rb +38 -0
- data/vendor/git/graphy/spec/properties_spec.rb +146 -0
- data/vendor/git/graphy/spec/search_spec.rb +227 -0
- data/vendor/git/graphy/spec/spec.opts +4 -0
- data/vendor/git/graphy/spec/spec_helper.rb +56 -0
- data/vendor/git/graphy/spec/strong_components_spec.rb +61 -0
- data/vendor/git/graphy/spec/triangulated_spec.rb +125 -0
- data/vendor/git/graphy/spec/undirected_graph_spec.rb +220 -0
- data/vendor/git/graphy/vendor/priority-queue/CHANGELOG +33 -0
- data/vendor/git/graphy/vendor/priority-queue/Makefile +140 -0
- data/vendor/git/graphy/vendor/priority-queue/README +133 -0
- data/vendor/git/graphy/vendor/priority-queue/benchmark/dijkstra.rb +171 -0
- data/vendor/git/graphy/vendor/priority-queue/compare_comments.rb +49 -0
- data/vendor/git/graphy/vendor/priority-queue/doc/c-vs-rb.png +0 -0
- data/vendor/git/graphy/vendor/priority-queue/doc/compare_big.gp +14 -0
- data/vendor/git/graphy/vendor/priority-queue/doc/compare_big.png +0 -0
- data/vendor/git/graphy/vendor/priority-queue/doc/compare_small.gp +15 -0
- data/vendor/git/graphy/vendor/priority-queue/doc/compare_small.png +0 -0
- data/vendor/git/graphy/vendor/priority-queue/doc/results.csv +37 -0
- data/vendor/git/graphy/vendor/priority-queue/ext/priority_queue/CPriorityQueue/extconf.rb +2 -0
- data/vendor/git/graphy/vendor/priority-queue/ext/priority_queue/CPriorityQueue/priority_queue.c +947 -0
- data/vendor/git/graphy/vendor/priority-queue/lib/priority_queue.rb +14 -0
- data/vendor/git/graphy/vendor/priority-queue/lib/priority_queue/c_priority_queue.rb +1 -0
- data/vendor/git/graphy/vendor/priority-queue/lib/priority_queue/poor_priority_queue.rb +46 -0
- data/vendor/git/graphy/vendor/priority-queue/lib/priority_queue/ruby_priority_queue.rb +526 -0
- data/vendor/git/graphy/vendor/priority-queue/priority_queue.so +0 -0
- data/vendor/git/graphy/vendor/priority-queue/setup.rb +1551 -0
- data/vendor/git/graphy/vendor/priority-queue/test/priority_queue_test.rb +371 -0
- data/vendor/git/graphy/vendor/rdot.rb +360 -0
- 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
|
Binary file
|
@@ -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)"
|
Binary file
|
@@ -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)"
|
Binary file
|
@@ -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 '' '' ''
|
data/vendor/git/graphy/vendor/priority-queue/ext/priority_queue/CPriorityQueue/priority_queue.c
ADDED
@@ -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
|
+
}
|