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.
- data/CHANGELOG +19 -0
- data/README +85 -0
- data/benchmark/dijkstra.rb +169 -0
- data/benchmark/result-CPriorityQueue.gp +8 -0
- data/benchmark/result-CPriorityQueue.png +0 -0
- data/benchmark/result-PoorPriorityQueue.gp +8 -0
- data/benchmark/result-PoorPriorityQueue.png +0 -0
- data/benchmark/result-PriorityQueue.gp +6 -0
- data/benchmark/result-RubyPriorityQueue.gp +8 -0
- data/benchmark/result-RubyPriorityQueue.png +0 -0
- data/benchmark/results.csv +37 -0
- data/benchmark/results.gp +10 -0
- data/benchmark/results.png +0 -0
- data/doc/result-PoorPriorityQueue.png +0 -0
- data/doc/result-PriorityQueue.png +0 -0
- data/doc/result-RubyPriorityQueue.png +0 -0
- data/doc/results.png +0 -0
- data/ext/priority_queue/extconf.rb +2 -0
- data/ext/priority_queue/priority_queue.c +874 -0
- data/lib/priority_queue.rb +14 -0
- data/lib/priority_queue/poor_priority_queue.rb +46 -0
- data/lib/priority_queue/ruby_priority_queue.rb +394 -0
- data/setup.rb +1551 -0
- data/test.rb +11 -0
- data/test/priority_queue_test.rb +358 -0
- metadata +78 -0
data/CHANGELOG
ADDED
@@ -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)"
|
Binary file
|
@@ -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)"
|
Binary file
|
@@ -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)"
|
Binary file
|
@@ -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
|
Binary file
|
Binary file
|
data/doc/results.png
ADDED
Binary file
|
@@ -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
|
+
}
|