algorithms 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +8 -0
- data/Manifest +9 -14
- data/README.markdown +92 -0
- data/Rakefile +3 -2
- data/algorithms.gemspec +6 -6
- data/benchmarks/deque.rb +17 -0
- data/benchmarks/sorts.rb +3 -2
- data/benchmarks/treemaps.rb +36 -0
- data/ext/containers/deque/deque.c +18 -4
- data/ext/containers/{tree_map → rbtree_map}/extconf.rb +0 -0
- data/ext/containers/{tree_map → rbtree_map}/rbtree.c +42 -12
- data/ext/containers/{bst → splaytree_map}/extconf.rb +1 -1
- data/ext/containers/splaytree_map/splaytree.c +370 -0
- data/lib/algorithms.rb +6 -5
- data/lib/containers/deque.rb +5 -10
- data/lib/containers/splay_tree_map.rb +14 -19
- data/spec/deque_gc_mark_spec.rb +18 -0
- data/spec/heap_spec.rb +110 -103
- data/spec/kd_tree_spec.rb +1 -86
- data/spec/priority_queue_spec.rb +62 -62
- data/spec/rb_tree_map_gc_mark_spec.rb +25 -0
- data/spec/splay_tree_map_spec.rb +26 -26
- metadata +19 -25
- data/README +0 -90
- data/benchmark.rb +0 -51
- data/benchmarks/rbench.rb +0 -16
- data/benchmarks/rbench/column.rb +0 -26
- data/benchmarks/rbench/group.rb +0 -43
- data/benchmarks/rbench/report.rb +0 -53
- data/benchmarks/rbench/runner.rb +0 -109
- data/benchmarks/rbench/summary.rb +0 -51
- data/ext/containers/bst/bst.c +0 -205
- data/lib/graphs/graph.rb +0 -25
- data/spec/bst_spec.rb +0 -31
data/History.txt
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
=== March 28, 2009
|
2
|
+
|
3
|
+
* Implemented SplayTree in C
|
4
|
+
* Made recursively_free_nodes methods static to fix a SEGFAULT
|
5
|
+
* Improved CBst
|
6
|
+
* Moved to Markdown for README
|
7
|
+
* 0.2.0 release
|
8
|
+
|
1
9
|
=== January 19, 2009
|
2
10
|
|
3
11
|
* kd-tree for points in multi-dimensional space
|
data/Manifest
CHANGED
@@ -1,18 +1,13 @@
|
|
1
1
|
algorithms.gemspec
|
2
|
-
|
3
|
-
benchmarks/rbench/column.rb
|
4
|
-
benchmarks/rbench/group.rb
|
5
|
-
benchmarks/rbench/report.rb
|
6
|
-
benchmarks/rbench/runner.rb
|
7
|
-
benchmarks/rbench/summary.rb
|
8
|
-
benchmarks/rbench.rb
|
2
|
+
benchmarks/deque.rb
|
9
3
|
benchmarks/sorts.rb
|
10
|
-
|
11
|
-
ext/containers/bst/extconf.rb
|
4
|
+
benchmarks/treemaps.rb
|
12
5
|
ext/containers/deque/deque.c
|
13
6
|
ext/containers/deque/extconf.rb
|
14
|
-
ext/containers/
|
15
|
-
ext/containers/
|
7
|
+
ext/containers/rbtree_map/extconf.rb
|
8
|
+
ext/containers/rbtree_map/rbtree.c
|
9
|
+
ext/containers/splaytree_map/extconf.rb
|
10
|
+
ext/containers/splaytree_map/splaytree.c
|
16
11
|
History.txt
|
17
12
|
lib/algorithms/search.rb
|
18
13
|
lib/algorithms/sort.rb
|
@@ -27,16 +22,16 @@ lib/containers/splay_tree_map.rb
|
|
27
22
|
lib/containers/stack.rb
|
28
23
|
lib/containers/suffix_array.rb
|
29
24
|
lib/containers/trie.rb
|
30
|
-
lib/graphs/graph.rb
|
31
25
|
Manifest
|
32
26
|
Rakefile
|
33
|
-
README
|
34
|
-
spec/
|
27
|
+
README.markdown
|
28
|
+
spec/deque_gc_mark_spec.rb
|
35
29
|
spec/deque_spec.rb
|
36
30
|
spec/heap_spec.rb
|
37
31
|
spec/kd_tree_spec.rb
|
38
32
|
spec/priority_queue_spec.rb
|
39
33
|
spec/queue_spec.rb
|
34
|
+
spec/rb_tree_map_gc_mark_spec.rb
|
40
35
|
spec/rb_tree_map_spec.rb
|
41
36
|
spec/search_spec.rb
|
42
37
|
spec/sort_spec.rb
|
data/README.markdown
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
# algorithms
|
2
|
+
|
3
|
+
* Official homes are here on github, and at [rubyforge](http://rubyforge.org/projects/algorithms/)
|
4
|
+
* Documentation: [http://algorithms.rubyforge.org/](http://algorithms.rubyforge.org/)
|
5
|
+
|
6
|
+
## DESCRIPTION:
|
7
|
+
|
8
|
+
Started as a [Google Summer of Code 2008](http://code.google.com/soc/2008/ruby/about.html) project
|
9
|
+
|
10
|
+
Written by [Kanwei Li](http://kanwei.com/), mentored by Austin Ziegler
|
11
|
+
|
12
|
+
Original Proposal: Using the right data structure or algorithm for the situation is an important
|
13
|
+
aspect of programming. In computer science literature, many data structures
|
14
|
+
and algorithms have been researched and extensively documented. However, there
|
15
|
+
is still no standard library in Ruby implementing useful structures and
|
16
|
+
algorithms like Red/Black Trees, tries, different sorting algorithms, etc.
|
17
|
+
This project will create such a library with documentation on when to use a
|
18
|
+
particular structure/algorithm. It will also come with a benchmark suite to
|
19
|
+
compare performance in different situations.
|
20
|
+
|
21
|
+
## FEATURES:
|
22
|
+
|
23
|
+
Done so far:
|
24
|
+
|
25
|
+
* Heaps Containers::Heap, Containers::MaxHeap, Containers::MinHeap
|
26
|
+
* Priority Queue Containers::PriorityQueue
|
27
|
+
* Deque Containers::Deque, Containers::CDeque (C extension), Containers::RubyDeque
|
28
|
+
* Stack Containers::Stack (uses Deque)
|
29
|
+
* Queue Containers::Queue (uses Deque)
|
30
|
+
* Red-Black Trees Containers::RBTreeMap, Containers::CRBTreeMap (C extension), Containers::RubyRBTreeMap
|
31
|
+
* Splay Trees Containers::SplayTreeMap, Containers::CSplayTreeMap (C extension), Containers::RubySplayTreeMap
|
32
|
+
* Tries Containers::Trie
|
33
|
+
* Suffix Array Containers::SuffixArray
|
34
|
+
|
35
|
+
* Search algorithms
|
36
|
+
- Binary Search Algorithms::Search.binary_search
|
37
|
+
- Knuth-Morris-Pratt Algorithms::Search.kmp_search
|
38
|
+
* Sort algorithms
|
39
|
+
- Bubble sort Algorithms::Sort.bubble_sort
|
40
|
+
- Comb sort Algorithms::Sort.comb_sort
|
41
|
+
- Selection sort Algorithms::Sort.selection_sort
|
42
|
+
- Heapsort Algorithms::Sort.heapsort
|
43
|
+
- Insertion sort Algorithms::Sort.insertion_sort
|
44
|
+
- Shell sort Algorithms::Sort.shell_sort
|
45
|
+
- Quicksort Algorithms::Sort.quicksort
|
46
|
+
- Mergesort Algorithms::Sort.mergesort
|
47
|
+
|
48
|
+
## SYNOPSIS:
|
49
|
+
|
50
|
+
require 'rubygems'
|
51
|
+
require 'algorithms'
|
52
|
+
|
53
|
+
max_heap = Containers::MaxHeap.new
|
54
|
+
|
55
|
+
# To not have to type "Containers::" before each class, use:
|
56
|
+
include Containers
|
57
|
+
max_heap = MaxHeap.new
|
58
|
+
|
59
|
+
|
60
|
+
## REQUIREMENTS:
|
61
|
+
|
62
|
+
* Ruby 1.8 compatible Ruby, or Ruby 1.9
|
63
|
+
* C compiler for C extensions (optional, but very much recommended for vast performance benefits)
|
64
|
+
|
65
|
+
## INSTALL:
|
66
|
+
|
67
|
+
* sudo gem install algorithms
|
68
|
+
|
69
|
+
## LICENSE:
|
70
|
+
|
71
|
+
(The MIT License)
|
72
|
+
|
73
|
+
Algorithms and Containers project is Copyright (c) 2009 Kanwei Li
|
74
|
+
|
75
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
76
|
+
a copy of this software and associated documentation files (the
|
77
|
+
'Software'), to deal in the Software without restriction, including
|
78
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
79
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
80
|
+
permit persons to whom the Software is furnished to do so, subject to
|
81
|
+
the following conditions:
|
82
|
+
|
83
|
+
The above copyright notice and this permission notice shall be
|
84
|
+
included in all copies or substantial portions of the Software.
|
85
|
+
|
86
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
87
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
88
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
89
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
90
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
91
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
92
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
CHANGED
@@ -6,7 +6,7 @@ Echoe.new('algorithms') do |p|
|
|
6
6
|
p.email = 'kanwei@gmail.com'
|
7
7
|
p.summary = 'A library of algorithms and containers.'
|
8
8
|
p.url = 'http://rubyforge.org/projects/algorithms/'
|
9
|
-
p.version = "0.
|
9
|
+
p.version = "0.2.0"
|
10
10
|
p.runtime_dependencies = []
|
11
11
|
end
|
12
12
|
|
@@ -18,8 +18,9 @@ end
|
|
18
18
|
|
19
19
|
task :push do
|
20
20
|
sh "git push" # Rubyforge
|
21
|
-
sh "git push
|
21
|
+
sh "git push --tags" # Rubyforge
|
22
22
|
sh "git push gh" # Github
|
23
|
+
sh "git push gh --tags" # Github
|
23
24
|
end
|
24
25
|
|
25
26
|
task :hanna do
|
data/algorithms.gemspec
CHANGED
@@ -2,19 +2,19 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = %q{algorithms}
|
5
|
-
s.version = "0.
|
5
|
+
s.version = "0.2.0"
|
6
6
|
|
7
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
8
|
s.authors = ["Kanwei Li"]
|
9
|
-
s.date = %q{2009-
|
9
|
+
s.date = %q{2009-03-29}
|
10
10
|
s.description = %q{A library of algorithms and containers.}
|
11
11
|
s.email = %q{kanwei@gmail.com}
|
12
|
-
s.extensions = ["ext/containers/
|
13
|
-
s.extra_rdoc_files = ["ext/containers/
|
14
|
-
s.files = ["algorithms.gemspec", "
|
12
|
+
s.extensions = ["ext/containers/deque/extconf.rb", "ext/containers/rbtree_map/extconf.rb", "ext/containers/splaytree_map/extconf.rb"]
|
13
|
+
s.extra_rdoc_files = ["ext/containers/deque/deque.c", "ext/containers/deque/extconf.rb", "ext/containers/rbtree_map/extconf.rb", "ext/containers/rbtree_map/rbtree.c", "ext/containers/splaytree_map/extconf.rb", "ext/containers/splaytree_map/splaytree.c", "lib/algorithms/search.rb", "lib/algorithms/sort.rb", "lib/algorithms.rb", "lib/containers/deque.rb", "lib/containers/heap.rb", "lib/containers/kd_tree.rb", "lib/containers/priority_queue.rb", "lib/containers/queue.rb", "lib/containers/rb_tree_map.rb", "lib/containers/splay_tree_map.rb", "lib/containers/stack.rb", "lib/containers/suffix_array.rb", "lib/containers/trie.rb", "README.markdown"]
|
14
|
+
s.files = ["algorithms.gemspec", "benchmarks/deque.rb", "benchmarks/sorts.rb", "benchmarks/treemaps.rb", "ext/containers/deque/deque.c", "ext/containers/deque/extconf.rb", "ext/containers/rbtree_map/extconf.rb", "ext/containers/rbtree_map/rbtree.c", "ext/containers/splaytree_map/extconf.rb", "ext/containers/splaytree_map/splaytree.c", "History.txt", "lib/algorithms/search.rb", "lib/algorithms/sort.rb", "lib/algorithms.rb", "lib/containers/deque.rb", "lib/containers/heap.rb", "lib/containers/kd_tree.rb", "lib/containers/priority_queue.rb", "lib/containers/queue.rb", "lib/containers/rb_tree_map.rb", "lib/containers/splay_tree_map.rb", "lib/containers/stack.rb", "lib/containers/suffix_array.rb", "lib/containers/trie.rb", "Manifest", "Rakefile", "README.markdown", "spec/deque_gc_mark_spec.rb", "spec/deque_spec.rb", "spec/heap_spec.rb", "spec/kd_tree_spec.rb", "spec/priority_queue_spec.rb", "spec/queue_spec.rb", "spec/rb_tree_map_gc_mark_spec.rb", "spec/rb_tree_map_spec.rb", "spec/search_spec.rb", "spec/sort_spec.rb", "spec/splay_tree_map_spec.rb", "spec/stack_spec.rb", "spec/suffix_array_spec.rb", "spec/trie_spec.rb"]
|
15
15
|
s.has_rdoc = true
|
16
16
|
s.homepage = %q{http://rubyforge.org/projects/algorithms/}
|
17
|
-
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Algorithms", "--main", "README"]
|
17
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Algorithms", "--main", "README.markdown"]
|
18
18
|
s.require_paths = ["lib", "ext"]
|
19
19
|
s.rubyforge_project = %q{algorithms}
|
20
20
|
s.rubygems_version = %q{1.3.1}
|
data/benchmarks/deque.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
$: << File.join(File.expand_path(File.dirname(__FILE__)), '../lib')
|
2
|
+
require 'algorithms'
|
3
|
+
include Algorithms
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'rbench'
|
7
|
+
|
8
|
+
RBench.run(2) do
|
9
|
+
%w(array deque).each { |s| self.send(:column, s.intern) }
|
10
|
+
deque = Containers::Deque.new
|
11
|
+
array = []
|
12
|
+
|
13
|
+
report "Insertion at end" do
|
14
|
+
array { 1000000.times { |x| array << x } }
|
15
|
+
deque { 1000000.times { |x| deque.push_back(x) } }
|
16
|
+
end
|
17
|
+
end
|
data/benchmarks/sorts.rb
CHANGED
@@ -2,17 +2,18 @@ $: << File.join(File.expand_path(File.dirname(__FILE__)), '../lib')
|
|
2
2
|
require 'algorithms'
|
3
3
|
include Algorithms
|
4
4
|
|
5
|
+
require 'rubygems'
|
5
6
|
require 'rbench'
|
6
7
|
|
7
8
|
RBench.run(5) do
|
8
9
|
|
9
|
-
sorts = %w(
|
10
|
+
sorts = %w(ruby comb_sort heapsort insertion_sort shell_sort quicksort mergesort)
|
10
11
|
sorts.each { |sort| self.send(:column, sort.intern) }
|
11
12
|
|
12
13
|
n = 1000
|
13
14
|
|
14
15
|
proc = lambda { |scope, ary|
|
15
|
-
scope.
|
16
|
+
scope.ruby { ary.dup.sort }
|
16
17
|
scope.comb_sort { Sort.comb_sort(ary.dup) }
|
17
18
|
scope.heapsort { Sort.heapsort(ary.dup) }
|
18
19
|
scope.insertion_sort { Sort.insertion_sort(ary.dup) }
|
@@ -0,0 +1,36 @@
|
|
1
|
+
$: << File.join(File.expand_path(File.dirname(__FILE__)), '../lib')
|
2
|
+
require 'algorithms'
|
3
|
+
include Algorithms
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'rbench'
|
7
|
+
|
8
|
+
RBench.run(10) do
|
9
|
+
trees = %w(hash rbtree splaytree)
|
10
|
+
trees.each { |tree| self.send(:column, tree.intern) }
|
11
|
+
|
12
|
+
rbtree = Containers::RBTreeMap.new
|
13
|
+
splaytree = Containers::SplayTreeMap.new
|
14
|
+
hash = Hash.new
|
15
|
+
|
16
|
+
random_array = Array.new(10000) { |i| rand(i) }
|
17
|
+
num = 1000
|
18
|
+
|
19
|
+
report "Insertion" do
|
20
|
+
rbtree { random_array.each_with_index { |x,index| rbtree[index] = x } }
|
21
|
+
splaytree { random_array.each_with_index { |x,index| splaytree[index] = x } }
|
22
|
+
hash { random_array.each_with_index { |x,index| hash[index] = x } }
|
23
|
+
end
|
24
|
+
|
25
|
+
report "has_key?" do
|
26
|
+
rbtree { num.times { |n| rbtree.has_key?(n) } }
|
27
|
+
splaytree { num.times { |n| splaytree.has_key?(n) } }
|
28
|
+
hash { num.times { |n| hash.has_key?(n) } }
|
29
|
+
end
|
30
|
+
|
31
|
+
report "Lookup in sorted order" do
|
32
|
+
rbtree { rbtree.each { |k, v| k } }
|
33
|
+
splaytree { splaytree.each { |k, v| k } }
|
34
|
+
hash { hash.sort.each { |k, v| k } }
|
35
|
+
end
|
36
|
+
end
|
@@ -56,14 +56,28 @@ static deque_node* create_node(VALUE obj) {
|
|
56
56
|
return node;
|
57
57
|
}
|
58
58
|
|
59
|
-
static void
|
60
|
-
|
61
|
-
|
59
|
+
static void deque_mark(void *ptr) {
|
60
|
+
if (ptr) {
|
61
|
+
deque *deque = ptr;
|
62
|
+
deque_node *node = deque->front;
|
63
|
+
while(node) {
|
64
|
+
rb_gc_mark(node->obj);
|
65
|
+
node = node->right;
|
66
|
+
}
|
67
|
+
}
|
68
|
+
}
|
69
|
+
|
70
|
+
static void deque_free(void *ptr) {
|
71
|
+
if (ptr) {
|
72
|
+
deque *deque = ptr;
|
73
|
+
free_nodes(deque->front);
|
74
|
+
free(deque);
|
75
|
+
}
|
62
76
|
}
|
63
77
|
|
64
78
|
static VALUE deque_alloc(VALUE klass) {
|
65
79
|
deque *deque = create_deque();
|
66
|
-
return Data_Wrap_Struct(klass,
|
80
|
+
return Data_Wrap_Struct(klass, deque_mark, deque_free, deque);
|
67
81
|
}
|
68
82
|
|
69
83
|
static VALUE deque_push_front(VALUE self, VALUE obj) {
|
File without changes
|
@@ -22,7 +22,7 @@ typedef struct {
|
|
22
22
|
rbtree_node *root;
|
23
23
|
} rbtree;
|
24
24
|
|
25
|
-
void recursively_free_nodes(rbtree_node *node) {
|
25
|
+
static void recursively_free_nodes(rbtree_node *node) {
|
26
26
|
if(node) {
|
27
27
|
recursively_free_nodes(node->left);
|
28
28
|
recursively_free_nodes(node->right);
|
@@ -37,7 +37,7 @@ static rbtree* get_tree_from_self(VALUE self) {
|
|
37
37
|
return tree;
|
38
38
|
}
|
39
39
|
|
40
|
-
int isred(rbtree_node *node) {
|
40
|
+
static int isred(rbtree_node *node) {
|
41
41
|
if(!node) { return FALSE; }
|
42
42
|
|
43
43
|
if(node->color == RED) { return TRUE; }
|
@@ -176,17 +176,17 @@ static VALUE get(rbtree *tree, rbtree_node *node, VALUE key) {
|
|
176
176
|
}
|
177
177
|
|
178
178
|
static VALUE min_key(rbtree_node *node) {
|
179
|
-
|
180
|
-
|
179
|
+
while (node->left)
|
180
|
+
node = node->left;
|
181
181
|
|
182
|
-
return
|
182
|
+
return node->key;
|
183
183
|
}
|
184
184
|
|
185
185
|
static VALUE max_key(rbtree_node *node) {
|
186
|
-
|
187
|
-
|
186
|
+
while (node->right)
|
187
|
+
node = node->right;
|
188
188
|
|
189
|
-
return
|
189
|
+
return node->key;
|
190
190
|
}
|
191
191
|
|
192
192
|
static rbtree_node* delete_min(rbtree_node *h, VALUE *deleted_value) {
|
@@ -285,6 +285,17 @@ static rbtree* rbt_each(rbtree *tree, void (*each)(rbtree *tree, rbtree_node *no
|
|
285
285
|
static int id_compare_operator;
|
286
286
|
|
287
287
|
static int rbtree_compare_function(VALUE a, VALUE b) {
|
288
|
+
if (a == b) return 0;
|
289
|
+
if (FIXNUM_P(a) && FIXNUM_P(b)) {
|
290
|
+
long x = FIX2LONG(a), y = FIX2LONG(b);
|
291
|
+
if (x == y) return 0;
|
292
|
+
if (x > y) return 1;
|
293
|
+
return -1;
|
294
|
+
}
|
295
|
+
if (TYPE(a) == T_STRING && RBASIC(a)->klass == rb_cString &&
|
296
|
+
TYPE(b) == T_STRING && RBASIC(b)->klass == rb_cString) {
|
297
|
+
return rb_str_cmp(a, b);
|
298
|
+
}
|
288
299
|
return FIX2INT(rb_funcall((VALUE) a, id_compare_operator, 1, (VALUE) b));
|
289
300
|
}
|
290
301
|
|
@@ -293,14 +304,33 @@ static VALUE rbtree_init(VALUE self)
|
|
293
304
|
return self;
|
294
305
|
}
|
295
306
|
|
296
|
-
static void
|
297
|
-
|
298
|
-
|
307
|
+
static void recursively_mark_nodes(rbtree_node *node) {
|
308
|
+
if(node) {
|
309
|
+
rb_gc_mark(node->key);
|
310
|
+
rb_gc_mark(node->value);
|
311
|
+
recursively_mark_nodes(node->left);
|
312
|
+
recursively_mark_nodes(node->right);
|
313
|
+
}
|
314
|
+
}
|
315
|
+
|
316
|
+
static void rbtree_mark(void *ptr) {
|
317
|
+
if (ptr) {
|
318
|
+
rbtree *tree = ptr;
|
319
|
+
recursively_mark_nodes(tree->root);
|
320
|
+
}
|
321
|
+
}
|
322
|
+
|
323
|
+
static void rbtree_free(void *ptr) {
|
324
|
+
if (ptr) {
|
325
|
+
rbtree *tree = ptr;
|
326
|
+
recursively_free_nodes(tree->root);
|
327
|
+
free(tree);
|
328
|
+
}
|
299
329
|
}
|
300
330
|
|
301
331
|
static VALUE rbtree_alloc(VALUE klass) {
|
302
332
|
rbtree *tree = create_rbtree(&rbtree_compare_function);
|
303
|
-
return Data_Wrap_Struct(klass,
|
333
|
+
return Data_Wrap_Struct(klass, rbtree_mark, rbtree_free, tree);
|
304
334
|
}
|
305
335
|
|
306
336
|
static VALUE rbtree_push(VALUE self, VALUE key, VALUE value) {
|
@@ -0,0 +1,370 @@
|
|
1
|
+
#include "ruby.h"
|
2
|
+
|
3
|
+
typedef struct struct_splaytree_node {
|
4
|
+
VALUE key;
|
5
|
+
VALUE value;
|
6
|
+
struct struct_splaytree_node *left;
|
7
|
+
struct struct_splaytree_node *right;
|
8
|
+
} splaytree_node;
|
9
|
+
|
10
|
+
typedef struct {
|
11
|
+
int (*compare_function)(VALUE key1, VALUE key2);
|
12
|
+
int size;
|
13
|
+
splaytree_node *root;
|
14
|
+
splaytree_node *header;
|
15
|
+
} splaytree;
|
16
|
+
|
17
|
+
static void recursively_free_nodes(splaytree_node *node) {
|
18
|
+
if(node) {
|
19
|
+
recursively_free_nodes(node->left);
|
20
|
+
recursively_free_nodes(node->right);
|
21
|
+
free(node);
|
22
|
+
}
|
23
|
+
return;
|
24
|
+
}
|
25
|
+
|
26
|
+
static splaytree* get_tree_from_self(VALUE self) {
|
27
|
+
splaytree *tree;
|
28
|
+
Data_Get_Struct(self, splaytree, tree);
|
29
|
+
return tree;
|
30
|
+
}
|
31
|
+
|
32
|
+
static void splay(splaytree *tree, VALUE key) {
|
33
|
+
int cmp, cmp2;
|
34
|
+
splaytree_node *l, *r, *t, *y;
|
35
|
+
|
36
|
+
l = tree->header;
|
37
|
+
r = tree->header;
|
38
|
+
t = tree->root;
|
39
|
+
|
40
|
+
tree->header->left = NULL;
|
41
|
+
tree->header->right = NULL;
|
42
|
+
|
43
|
+
while(1) {
|
44
|
+
cmp = tree->compare_function(key, t->key);
|
45
|
+
if (cmp == -1) {
|
46
|
+
if (!(t->left)) break;
|
47
|
+
cmp2 = tree->compare_function(key, t->left->key);
|
48
|
+
if (cmp2 == -1) {
|
49
|
+
y = t->left;
|
50
|
+
t->left = y->right;
|
51
|
+
y->right = t;
|
52
|
+
t = y;
|
53
|
+
if (!(t->left)) break;
|
54
|
+
}
|
55
|
+
r->left = t;
|
56
|
+
r = t;
|
57
|
+
t = t->left;
|
58
|
+
}
|
59
|
+
else if (cmp == 1) {
|
60
|
+
if (!(t->right)) break;
|
61
|
+
cmp2 = tree->compare_function(key, t->right->key);
|
62
|
+
if (cmp2 == 1) {
|
63
|
+
y = t->right;
|
64
|
+
t->right = y->left;
|
65
|
+
y->left = t;
|
66
|
+
t = y;
|
67
|
+
if (!(t->right)) break;
|
68
|
+
}
|
69
|
+
l->right = t;
|
70
|
+
l = t;
|
71
|
+
t = t->right;
|
72
|
+
} else {
|
73
|
+
break;
|
74
|
+
}
|
75
|
+
}
|
76
|
+
l->right = t->left;
|
77
|
+
r->left = t->right;
|
78
|
+
t->left = tree->header->right;
|
79
|
+
t->right = tree->header->left;
|
80
|
+
tree->root = t;
|
81
|
+
}
|
82
|
+
|
83
|
+
static int height(splaytree_node *h) {
|
84
|
+
int left_height, right_height;
|
85
|
+
|
86
|
+
if(!h) { return 0; }
|
87
|
+
|
88
|
+
left_height = 1 + height(h->left);
|
89
|
+
right_height = 1 + height(h->right);
|
90
|
+
|
91
|
+
return left_height > right_height ? left_height : right_height;
|
92
|
+
}
|
93
|
+
|
94
|
+
static splaytree* create_splaytree(int (*compare_function)(VALUE, VALUE)) {
|
95
|
+
splaytree *tree = ALLOC(splaytree);
|
96
|
+
tree->compare_function = compare_function;
|
97
|
+
tree->root = NULL;
|
98
|
+
tree->size = 0;
|
99
|
+
tree->header = ALLOC(splaytree_node);
|
100
|
+
return tree;
|
101
|
+
}
|
102
|
+
|
103
|
+
static splaytree_node* create_node(VALUE key, VALUE value) {
|
104
|
+
splaytree_node *new_node = ALLOC(splaytree_node);
|
105
|
+
new_node->key = key;
|
106
|
+
new_node->value = value;
|
107
|
+
new_node->left = NULL;
|
108
|
+
new_node->right = NULL;
|
109
|
+
return new_node;
|
110
|
+
}
|
111
|
+
|
112
|
+
static void insert(splaytree *tree, VALUE key, VALUE value) {
|
113
|
+
int cmp;
|
114
|
+
splaytree_node *new_node;
|
115
|
+
|
116
|
+
if (!(tree->root)) {
|
117
|
+
new_node = create_node(key, value);
|
118
|
+
tree->root = new_node;
|
119
|
+
tree->size += 1;
|
120
|
+
return;
|
121
|
+
}
|
122
|
+
|
123
|
+
splay(tree, key);
|
124
|
+
cmp = tree->compare_function(key, tree->root->key);
|
125
|
+
if (cmp == 0) {
|
126
|
+
tree->root->value = value;
|
127
|
+
return;
|
128
|
+
} else {
|
129
|
+
new_node = create_node(key, value);
|
130
|
+
if (cmp == -1) {
|
131
|
+
new_node->left = tree->root->left;
|
132
|
+
new_node->right = tree->root;
|
133
|
+
tree->root->left = NULL;
|
134
|
+
} else { /* cmp == 1 */
|
135
|
+
new_node->right = tree->root->right;
|
136
|
+
new_node->left = tree->root;
|
137
|
+
tree->root->right = NULL;
|
138
|
+
}
|
139
|
+
tree->root = new_node;
|
140
|
+
tree->size += 1;
|
141
|
+
}
|
142
|
+
}
|
143
|
+
|
144
|
+
static VALUE get(splaytree *tree, VALUE key) {
|
145
|
+
int cmp;
|
146
|
+
if (!tree->root)
|
147
|
+
return Qnil;
|
148
|
+
|
149
|
+
splay(tree, key);
|
150
|
+
cmp = tree->compare_function(key, tree->root->key);
|
151
|
+
if (cmp == 0) {
|
152
|
+
return tree->root->value;
|
153
|
+
}
|
154
|
+
return Qnil;
|
155
|
+
}
|
156
|
+
|
157
|
+
static VALUE delete(splaytree *tree, VALUE key) {
|
158
|
+
int cmp;
|
159
|
+
splaytree_node *x, *deleted_root;
|
160
|
+
VALUE deleted;
|
161
|
+
|
162
|
+
splay(tree, key);
|
163
|
+
cmp = tree->compare_function(key, tree->root->key);
|
164
|
+
if (cmp == 0) {
|
165
|
+
deleted = tree->root->value;
|
166
|
+
deleted_root = tree->root;
|
167
|
+
if (!(tree->root->left)) {
|
168
|
+
tree->root = tree->root->right;
|
169
|
+
} else {
|
170
|
+
x = tree->root->right;
|
171
|
+
tree->root = tree->root->left;
|
172
|
+
splay(tree, key);
|
173
|
+
tree->root->right = x;
|
174
|
+
}
|
175
|
+
free(deleted_root);
|
176
|
+
tree->size -= 1;
|
177
|
+
return deleted;
|
178
|
+
}
|
179
|
+
return Qnil;
|
180
|
+
}
|
181
|
+
|
182
|
+
static splaytree* splaytree_each_node(splaytree *tree, splaytree_node *node, void (*each)(splaytree *tree_, splaytree_node *node_, void* args), void* arguments) {
|
183
|
+
if (!node)
|
184
|
+
return NULL;
|
185
|
+
|
186
|
+
if (node->left)
|
187
|
+
splaytree_each_node(tree, node->left, each, arguments);
|
188
|
+
|
189
|
+
(*each)(tree, node, arguments);
|
190
|
+
|
191
|
+
if (node->right)
|
192
|
+
splaytree_each_node(tree, node->right, each, arguments);
|
193
|
+
return tree;
|
194
|
+
}
|
195
|
+
|
196
|
+
static splaytree* splay_each(splaytree *tree, void (*each)(splaytree *tree, splaytree_node *node, void *args), void* arguments) {
|
197
|
+
if (tree->root)
|
198
|
+
splaytree_each_node(tree, tree->root, each, arguments);
|
199
|
+
return tree;
|
200
|
+
}
|
201
|
+
|
202
|
+
// Methods to be called in Ruby
|
203
|
+
|
204
|
+
static int id_compare_operator;
|
205
|
+
|
206
|
+
static int splaytree_compare_function(VALUE a, VALUE b) {
|
207
|
+
if (a == b) return 0;
|
208
|
+
if (FIXNUM_P(a) && FIXNUM_P(b)) {
|
209
|
+
long x = FIX2LONG(a), y = FIX2LONG(b);
|
210
|
+
if (x == y) return 0;
|
211
|
+
if (x > y) return 1;
|
212
|
+
return -1;
|
213
|
+
}
|
214
|
+
if (TYPE(a) == T_STRING && RBASIC(a)->klass == rb_cString &&
|
215
|
+
TYPE(b) == T_STRING && RBASIC(b)->klass == rb_cString) {
|
216
|
+
return rb_str_cmp(a, b);
|
217
|
+
}
|
218
|
+
return FIX2INT(rb_funcall((VALUE) a, id_compare_operator, 1, (VALUE) b));
|
219
|
+
}
|
220
|
+
|
221
|
+
static VALUE splaytree_init(VALUE self)
|
222
|
+
{
|
223
|
+
return self;
|
224
|
+
}
|
225
|
+
|
226
|
+
static void recursively_mark_nodes(splaytree_node *node) {
|
227
|
+
if(node) {
|
228
|
+
rb_gc_mark(node->key);
|
229
|
+
rb_gc_mark(node->value);
|
230
|
+
recursively_mark_nodes(node->left);
|
231
|
+
recursively_mark_nodes(node->right);
|
232
|
+
}
|
233
|
+
}
|
234
|
+
|
235
|
+
static void splaytree_mark(void *ptr) {
|
236
|
+
if (ptr) {
|
237
|
+
splaytree *tree = ptr;
|
238
|
+
recursively_mark_nodes(tree->root);
|
239
|
+
}
|
240
|
+
}
|
241
|
+
|
242
|
+
static void splaytree_free(void *ptr) {
|
243
|
+
if (ptr) {
|
244
|
+
splaytree *tree = ptr;
|
245
|
+
recursively_free_nodes(tree->root);
|
246
|
+
free(tree->header);
|
247
|
+
free(tree);
|
248
|
+
}
|
249
|
+
}
|
250
|
+
|
251
|
+
static VALUE splaytree_alloc(VALUE klass) {
|
252
|
+
splaytree *tree = create_splaytree(&splaytree_compare_function);
|
253
|
+
return Data_Wrap_Struct(klass, splaytree_mark, splaytree_free, tree);
|
254
|
+
}
|
255
|
+
|
256
|
+
static VALUE splaytree_push(VALUE self, VALUE key, VALUE value) {
|
257
|
+
splaytree *tree = get_tree_from_self(self);
|
258
|
+
insert(tree, key, value);
|
259
|
+
return value;
|
260
|
+
}
|
261
|
+
|
262
|
+
static VALUE splaytree_get(VALUE self, VALUE key) {
|
263
|
+
splaytree *tree = get_tree_from_self(self);
|
264
|
+
return get(tree, key);
|
265
|
+
}
|
266
|
+
|
267
|
+
static VALUE splaytree_size(VALUE self) {
|
268
|
+
splaytree *tree = get_tree_from_self(self);
|
269
|
+
return INT2NUM(tree->size);
|
270
|
+
}
|
271
|
+
|
272
|
+
static VALUE splaytree_is_empty(VALUE self) {
|
273
|
+
splaytree *tree = get_tree_from_self(self);
|
274
|
+
return (tree->root ? Qfalse : Qtrue);
|
275
|
+
}
|
276
|
+
|
277
|
+
static VALUE splaytree_height(VALUE self) {
|
278
|
+
splaytree *tree = get_tree_from_self(self);
|
279
|
+
return INT2NUM(height(tree->root));
|
280
|
+
}
|
281
|
+
|
282
|
+
static VALUE splaytree_has_key(VALUE self, VALUE key) {
|
283
|
+
splaytree *tree = get_tree_from_self(self);
|
284
|
+
if(!tree->root) { return Qfalse; }
|
285
|
+
if(get(tree, key) == Qnil)
|
286
|
+
return Qfalse;
|
287
|
+
|
288
|
+
return Qtrue;
|
289
|
+
}
|
290
|
+
|
291
|
+
static VALUE splaytree_min_key(VALUE self) {
|
292
|
+
splaytree *tree = get_tree_from_self(self);
|
293
|
+
splaytree_node *node;
|
294
|
+
|
295
|
+
if(!tree->root)
|
296
|
+
return Qnil;
|
297
|
+
|
298
|
+
node = tree->root;
|
299
|
+
while (node->left)
|
300
|
+
node = node->left;
|
301
|
+
|
302
|
+
return node->key;
|
303
|
+
}
|
304
|
+
|
305
|
+
static VALUE splaytree_max_key(VALUE self) {
|
306
|
+
splaytree *tree = get_tree_from_self(self);
|
307
|
+
splaytree_node *node;
|
308
|
+
|
309
|
+
if(!tree->root)
|
310
|
+
return Qnil;
|
311
|
+
|
312
|
+
node = tree->root;
|
313
|
+
while (node->right)
|
314
|
+
node = node->right;
|
315
|
+
|
316
|
+
return node->key;
|
317
|
+
}
|
318
|
+
|
319
|
+
static VALUE splaytree_delete(VALUE self, VALUE key) {
|
320
|
+
splaytree *tree = get_tree_from_self(self);
|
321
|
+
if(!tree->root)
|
322
|
+
return Qnil;
|
323
|
+
|
324
|
+
return delete(tree, key);
|
325
|
+
}
|
326
|
+
|
327
|
+
static VALUE splaytree_clear(VALUE self) {
|
328
|
+
splaytree *tree = get_tree_from_self(self);
|
329
|
+
recursively_free_nodes(tree->root);
|
330
|
+
tree->root = NULL;
|
331
|
+
tree->size = 0;
|
332
|
+
|
333
|
+
return Qnil;
|
334
|
+
}
|
335
|
+
|
336
|
+
static void splaytree_each_helper(splaytree *tree, splaytree_node *node, void *args) {
|
337
|
+
rb_yield(rb_ary_new3(2, node->key, node->value));
|
338
|
+
};
|
339
|
+
|
340
|
+
static VALUE splaytree_each(VALUE self) {
|
341
|
+
splaytree *tree = get_tree_from_self(self);
|
342
|
+
splay_each(tree, &splaytree_each_helper, NULL);
|
343
|
+
return self;
|
344
|
+
}
|
345
|
+
|
346
|
+
static VALUE CSplayTree;
|
347
|
+
static VALUE mContainers;
|
348
|
+
|
349
|
+
void Init_CSplayTreeMap() {
|
350
|
+
id_compare_operator = rb_intern("<=>");
|
351
|
+
|
352
|
+
mContainers = rb_define_module("Containers");
|
353
|
+
CSplayTree = rb_define_class_under(mContainers, "CSplayTreeMap", rb_cObject);
|
354
|
+
rb_define_alloc_func(CSplayTree, splaytree_alloc);
|
355
|
+
rb_define_method(CSplayTree, "initialize", splaytree_init, 0);
|
356
|
+
rb_define_method(CSplayTree, "push", splaytree_push, 2);
|
357
|
+
rb_define_method(CSplayTree, "clear", splaytree_clear, 0);
|
358
|
+
rb_define_alias(CSplayTree, "[]=", "push");
|
359
|
+
rb_define_method(CSplayTree, "size", splaytree_size, 0);
|
360
|
+
rb_define_method(CSplayTree, "empty?", splaytree_is_empty, 0);
|
361
|
+
rb_define_method(CSplayTree, "height", splaytree_height, 0);
|
362
|
+
rb_define_method(CSplayTree, "min_key", splaytree_min_key, 0);
|
363
|
+
rb_define_method(CSplayTree, "max_key", splaytree_max_key, 0);
|
364
|
+
rb_define_method(CSplayTree, "each", splaytree_each, 0);
|
365
|
+
rb_define_method(CSplayTree, "get", splaytree_get, 1);
|
366
|
+
rb_define_alias(CSplayTree, "[]", "get");
|
367
|
+
rb_define_method(CSplayTree, "has_key?", splaytree_has_key, 1);
|
368
|
+
rb_define_method(CSplayTree, "delete", splaytree_delete, 1);
|
369
|
+
rb_include_module(CSplayTree, rb_eval_string("Enumerable"));
|
370
|
+
}
|