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