kanwei-algorithms 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. data/History.txt +168 -0
  2. data/Manifest +41 -0
  3. data/README.markdown +92 -0
  4. data/Rakefile +31 -0
  5. data/algorithms.gemspec +32 -0
  6. data/benchmarks/deque.rb +17 -0
  7. data/benchmarks/sorts.rb +34 -0
  8. data/benchmarks/treemaps.rb +36 -0
  9. data/ext/containers/deque/deque.c +247 -0
  10. data/ext/containers/deque/extconf.rb +4 -0
  11. data/ext/containers/rbtree_map/extconf.rb +4 -0
  12. data/ext/containers/rbtree_map/rbtree.c +473 -0
  13. data/ext/containers/splaytree_map/extconf.rb +4 -0
  14. data/ext/containers/splaytree_map/splaytree.c +370 -0
  15. data/lib/algorithms/search.rb +84 -0
  16. data/lib/algorithms/sort.rb +238 -0
  17. data/lib/algorithms.rb +67 -0
  18. data/lib/containers/deque.rb +171 -0
  19. data/lib/containers/heap.rb +486 -0
  20. data/lib/containers/kd_tree.rb +87 -0
  21. data/lib/containers/priority_queue.rb +113 -0
  22. data/lib/containers/queue.rb +68 -0
  23. data/lib/containers/rb_tree_map.rb +398 -0
  24. data/lib/containers/splay_tree_map.rb +269 -0
  25. data/lib/containers/stack.rb +67 -0
  26. data/lib/containers/suffix_array.rb +68 -0
  27. data/lib/containers/trie.rb +182 -0
  28. data/spec/deque_gc_mark_spec.rb +18 -0
  29. data/spec/deque_spec.rb +108 -0
  30. data/spec/heap_spec.rb +126 -0
  31. data/spec/kd_tree_spec.rb +4 -0
  32. data/spec/priority_queue_spec.rb +75 -0
  33. data/spec/queue_spec.rb +61 -0
  34. data/spec/rb_tree_map_gc_mark_spec.rb +25 -0
  35. data/spec/rb_tree_map_spec.rb +123 -0
  36. data/spec/search_spec.rb +28 -0
  37. data/spec/sort_spec.rb +28 -0
  38. data/spec/splay_tree_map_spec.rb +102 -0
  39. data/spec/stack_spec.rb +60 -0
  40. data/spec/suffix_array_spec.rb +40 -0
  41. data/spec/trie_spec.rb +59 -0
  42. metadata +120 -0
@@ -0,0 +1,18 @@
1
+ $: << File.join(File.expand_path(File.dirname(__FILE__)), '..', 'lib')
2
+ require 'algorithms'
3
+
4
+ if defined? Containers::CDeque
5
+ describe "CDeque" do
6
+ it "should mark ruby object references" do
7
+ anon_class = Class.new
8
+ @deque = Containers::CDeque.new
9
+ 100.times { @deque.push_front(anon_class.new) }
10
+ # Mark and sweep
11
+ ObjectSpace.garbage_collect
12
+ # Check if any instances were swept
13
+ count = 0
14
+ ObjectSpace.each_object(anon_class) { |x| count += 1 }
15
+ count.should eql(100)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,108 @@
1
+ $: << File.join(File.expand_path(File.dirname(__FILE__)), '..', 'lib')
2
+ require 'algorithms'
3
+
4
+ describe "(empty deque)", :shared => true do
5
+ it "should return nil when popping objects" do
6
+ @deque.pop_front.should be_nil
7
+ @deque.pop_back.should be_nil
8
+ end
9
+
10
+ it "should return a size of 1 when sent #push_front" do
11
+ @deque.push_front(1)
12
+ @deque.size.should eql(1)
13
+ end
14
+
15
+ it "should return a size of 1 when sent #push_back" do
16
+ @deque.push_back(1)
17
+ @deque.size.should eql(1)
18
+ end
19
+
20
+ it "should return nil when sent #front and #back" do
21
+ @deque.front.should be_nil
22
+ @deque.back.should be_nil
23
+ end
24
+
25
+ it "should be empty" do
26
+ @deque.should be_empty
27
+ end
28
+
29
+ it "should raise ArgumentError if passed more than one argument" do
30
+ lambda { @deque.class.send("new", Time.now, []) }.should raise_error
31
+ end
32
+ end
33
+
34
+ describe "(non-empty deque)", :shared => true do
35
+ before(:each) do
36
+ @deque.push_back(10)
37
+ @deque.push_back("10")
38
+ end
39
+
40
+ it "should return last pushed object with pop_back" do
41
+ @deque.pop_back.should eql("10")
42
+ @deque.pop_back.should eql(10)
43
+ end
44
+
45
+ it "should return first pushed object with pop_front" do
46
+ @deque.pop_front.should eql(10)
47
+ @deque.pop_front.should eql("10")
48
+ end
49
+
50
+ it "should return a size greater than 0" do
51
+ @deque.size.should eql(2)
52
+ end
53
+
54
+ it "should not be empty" do
55
+ @deque.should_not be_empty
56
+ end
57
+
58
+ it "should iterate in LIFO order with #each_backward" do
59
+ arr = []
60
+ @deque.each_backward { |obj| arr << obj }
61
+ arr.should eql(["10", 10])
62
+ end
63
+
64
+ it "should iterate in FIFO order with #each_forward" do
65
+ arr = []
66
+ @deque.each_forward { |obj| arr << obj }
67
+ arr.should eql([10, "10"])
68
+ end
69
+
70
+ it "should return nil after everything's popped" do
71
+ @deque.pop_back
72
+ @deque.pop_back
73
+ @deque.pop_back.should be_nil
74
+ @deque.front.should be_nil
75
+ end
76
+ end
77
+
78
+ describe "empty rubydeque" do
79
+ before(:each) do
80
+ @deque = Containers::RubyDeque.new
81
+ end
82
+ it_should_behave_like "(empty deque)"
83
+ end
84
+
85
+ describe "full rubydeque" do
86
+ before(:each) do
87
+ @deque = Containers::RubyDeque.new
88
+ end
89
+ it_should_behave_like "(non-empty deque)"
90
+ end
91
+
92
+ begin
93
+ Containers::CDeque
94
+ describe "empty cdeque" do
95
+ before(:each) do
96
+ @deque = Containers::CDeque.new
97
+ end
98
+ it_should_behave_like "(empty deque)"
99
+ end
100
+
101
+ describe "full cdeque" do
102
+ before(:each) do
103
+ @deque = Containers::CDeque.new
104
+ end
105
+ it_should_behave_like "(non-empty deque)"
106
+ end
107
+ rescue Exception
108
+ end
data/spec/heap_spec.rb ADDED
@@ -0,0 +1,126 @@
1
+ $: << File.join(File.expand_path(File.dirname(__FILE__)), '..', 'lib')
2
+ require 'algorithms'
3
+
4
+ describe Containers::Heap do
5
+ before(:each) do
6
+ @heap = Containers::MaxHeap.new
7
+ end
8
+
9
+ it "should not let you merge with non-heaps" do
10
+ lambda { @heap.merge!(nil) }.should raise_error
11
+ lambda { @heap.merge!([]) }.should raise_error
12
+ end
13
+
14
+ describe "(empty)" do
15
+
16
+ it "should return nil when getting the maximum" do
17
+ @heap.max!.should be_nil
18
+ end
19
+
20
+ it "should let you insert and remove one item" do
21
+ @heap.size.should eql(0)
22
+
23
+ @heap.push(1)
24
+ @heap.size.should eql(1)
25
+
26
+ @heap.max!.should eql(1)
27
+ @heap.size.should eql(0)
28
+ end
29
+
30
+ it "should let you initialize with an array" do
31
+ @heap = Containers::MaxHeap.new([1,2,3])
32
+ @heap.size.should eql(3)
33
+ end
34
+
35
+ end
36
+
37
+ describe "(non-empty)" do
38
+ before(:each) do
39
+ @random_array = []
40
+ @num_items = 100
41
+ @num_items.times { |x| @random_array << rand(@num_items) }
42
+ @heap = Containers::MaxHeap.new(@random_array)
43
+ end
44
+
45
+ it "should display the correct size" do
46
+ @heap.size.should eql(@num_items)
47
+ end
48
+
49
+ it "should delete random keys" do
50
+ @heap.delete(@random_array[0]).should eql(@random_array[0])
51
+ @heap.delete(@random_array[1]).should eql(@random_array[1])
52
+ ordered = []
53
+ ordered << @heap.max! until @heap.empty?
54
+ ordered.should eql( @random_array[2..-1].sort.reverse )
55
+ end
56
+
57
+ it "should delete all keys" do
58
+ ordered = []
59
+ @random_array.size.times do |t|
60
+ ordered << @heap.delete(@random_array[t])
61
+ end
62
+ @heap.should be_empty
63
+ ordered.should eql( @random_array )
64
+ end
65
+
66
+ it "should be in max->min order" do
67
+ ordered = []
68
+ ordered << @heap.max! until @heap.empty?
69
+
70
+ ordered.should eql(@random_array.sort.reverse)
71
+ end
72
+
73
+ it "should change certain keys" do
74
+ numbers = [1,2,3,4,5,6,7,8,9,10,100,101]
75
+ heap = Containers::MinHeap.new(numbers)
76
+ heap.change_key(101, 50)
77
+ heap.pop
78
+ heap.pop
79
+ heap.change_key(8, 0)
80
+ ordered = []
81
+ ordered << heap.min! until heap.empty?
82
+ ordered.should eql( [8,3,4,5,6,7,9,10,101,100] )
83
+ end
84
+
85
+ it "should not delete keys it doesn't have" do
86
+ @heap.delete(:nonexisting).should be_nil
87
+ @heap.size.should eql(@num_items)
88
+ end
89
+
90
+ it "should delete certain keys" do
91
+ numbers = [1,2,3,4,5,6,7,8,9,10,100,101]
92
+ heap = Containers::MinHeap.new(numbers)
93
+ heap.delete(5)
94
+ heap.pop
95
+ heap.pop
96
+ heap.delete(100)
97
+ ordered = []
98
+ ordered << heap.min! until heap.empty?
99
+ ordered.should eql( [3,4,6,7,8,9,10,101] )
100
+ end
101
+
102
+ it "should let you merge with another heap" do
103
+ numbers = [1,2,3,4,5,6,7,8]
104
+ otherheap = Containers::MaxHeap.new(numbers)
105
+ otherheap.size.should eql(8)
106
+ @heap.merge!(otherheap)
107
+
108
+ ordered = []
109
+ ordered << @heap.max! until @heap.empty?
110
+
111
+ ordered.should eql( (@random_array + numbers).sort.reverse)
112
+ end
113
+
114
+ describe "min-heap" do
115
+ it "should be in min->max order" do
116
+ @heap = Containers::MinHeap.new(@random_array)
117
+ ordered = []
118
+ ordered << @heap.min! until @heap.empty?
119
+
120
+ ordered.should eql(@random_array.sort)
121
+ end
122
+ end
123
+
124
+ end
125
+
126
+ end
@@ -0,0 +1,4 @@
1
+ $: << File.join(File.expand_path(File.dirname(__FILE__)), '..', 'lib')
2
+ require 'algorithms'
3
+
4
+ # TODO: KD Tree Spec
@@ -0,0 +1,75 @@
1
+ $: << File.join(File.expand_path(File.dirname(__FILE__)), '..', 'lib')
2
+ require 'algorithms'
3
+
4
+ describe Containers::PriorityQueue do
5
+ before(:each) do
6
+ @q = Containers::PriorityQueue.new
7
+ end
8
+
9
+ describe "(empty priority queue)" do
10
+ it "should return 0 for size and be empty" do
11
+ @q.size.should eql(0)
12
+ @q.should be_empty
13
+ end
14
+
15
+ it "should not return anything" do
16
+ @q.next.should be_nil
17
+ @q.pop.should be_nil
18
+ @q.delete(1).should be_nil
19
+ @q.has_priority?(1).should be_false
20
+ end
21
+
22
+ it "should give the correct size when adding items" do
23
+ 20.times do |i|
24
+ @q.size.should eql(i)
25
+ @q.push(i, i)
26
+ end
27
+ 10.times do |i|
28
+ @q.size.should eql(20-i)
29
+ @q.pop
30
+ end
31
+ 10.times do |i|
32
+ @q.size.should eql(i+10)
33
+ @q.push(i, i)
34
+ end
35
+ @q.delete(5)
36
+ @q.size.should eql(19)
37
+ end
38
+ end
39
+
40
+ describe "(non-empty priority queue)" do
41
+ before(:each) do
42
+ @q.push("Alaska", 50)
43
+ @q.push("Delaware", 30)
44
+ @q.push("Georgia", 35)
45
+ end
46
+
47
+ it "should next/pop the highest priority" do
48
+ @q.next.should eql("Alaska")
49
+ @q.size.should eql(3)
50
+ @q.pop.should eql("Alaska")
51
+ @q.size.should eql(2)
52
+ end
53
+
54
+ it "should not be empty" do
55
+ @q.should_not be_empty
56
+ end
57
+
58
+ it "should has_priority? priorities it has" do
59
+ @q.has_priority?(50).should be_true
60
+ @q.has_priority?(10).should be_false
61
+ end
62
+
63
+ it "should return nil after popping everything" do
64
+ 3.times do
65
+ @q.pop
66
+ end
67
+ @q.pop.should be_nil
68
+ end
69
+
70
+ it "should delete things it has and not things it doesn't" do
71
+ @q.delete(50).should eql("Alaska")
72
+ @q.delete(10).should eql(nil)
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,61 @@
1
+ $: << File.join(File.expand_path(File.dirname(__FILE__)), '..', 'lib')
2
+ require 'algorithms'
3
+
4
+ describe "empty queue" do
5
+ before(:each) do
6
+ @queue = Containers::Queue.new
7
+ end
8
+
9
+
10
+ it "should return nil when sent #pop" do
11
+ @queue.pop.should be_nil
12
+ end
13
+
14
+ it "should return a size of 1 when sent #push" do
15
+ @queue.push(1)
16
+ @queue.size.should eql(1)
17
+ end
18
+
19
+ it "should return nil when sent #next" do
20
+ @queue.next.should be_nil
21
+ end
22
+
23
+ it "should return empty?" do
24
+ @queue.empty?.should be_true
25
+ end
26
+ end
27
+
28
+ describe "non-empty queue" do
29
+ before(:each) do
30
+ @queue = Containers::Queue.new
31
+ @queue.push(10)
32
+ @queue.push("10")
33
+ end
34
+
35
+ it "should return first pushed object" do
36
+ @queue.pop.should eql(10)
37
+ end
38
+
39
+ it "should return the size" do
40
+ @queue.size.should eql(2)
41
+ end
42
+
43
+ it "should not return empty?" do
44
+ @queue.empty?.should be_false
45
+ end
46
+
47
+ it "should iterate in FIFO order" do
48
+ arr = []
49
+ @queue.each { |obj| arr << obj }
50
+ arr.should eql([10, "10"])
51
+ end
52
+
53
+ it "should return nil after all gets" do
54
+ 2.times do
55
+ @queue.pop
56
+ end
57
+ @queue.pop.should be_nil
58
+ @queue.next.should be_nil
59
+ end
60
+
61
+ end
@@ -0,0 +1,25 @@
1
+ $: << File.join(File.expand_path(File.dirname(__FILE__)), '..', 'lib')
2
+ require 'algorithms'
3
+
4
+ if defined? Containers::CRBTreeMap
5
+ describe "CRBTreeMap" do
6
+ it "should mark ruby object references" do
7
+ anon_key_class = Class.new do
8
+ attr :value
9
+ def initialize(x); @value = x; end
10
+ def <=>(other); value <=> other.value; end
11
+ end
12
+ anon_val_class = Class.new
13
+ @tree = Containers::CRBTreeMap.new
14
+ 100.times { |x| @tree[anon_key_class.new(x)] = anon_val_class.new }
15
+ # Mark and sweep
16
+ ObjectSpace.garbage_collect
17
+ # Check if any instances were swept
18
+ count = 0
19
+ ObjectSpace.each_object(anon_key_class) { |x| count += 1 }
20
+ count.should eql(100)
21
+ ObjectSpace.each_object(anon_val_class) { |x| count += 1 }
22
+ count.should eql(200)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,123 @@
1
+ $: << File.join(File.expand_path(File.dirname(__FILE__)), '..', 'lib')
2
+ require 'algorithms'
3
+
4
+ describe "empty rbtree", :shared => true do
5
+ it "should let you push stuff in" do
6
+ 100.times { |x| @tree[x] = x }
7
+ @tree.size.should eql(100)
8
+ end
9
+
10
+ it "should be empty?" do
11
+ @tree.empty?.should be_true
12
+ end
13
+
14
+ it "should return 0 for height" do
15
+ @tree.height.should eql(0)
16
+ end
17
+
18
+ it "should return 0 for size" do
19
+ @tree.size.should eql(0)
20
+ end
21
+
22
+ it "should return nil for max and min" do
23
+ @tree.min.should be_nil
24
+ @tree.max.should be_nil
25
+ @tree.min_key.should be_nil
26
+ @tree.max_key.should be_nil
27
+ end
28
+
29
+ it "should not delete" do
30
+ @tree.delete(:non_existing).should be_nil
31
+ end
32
+ end
33
+
34
+ describe "non-empty rbtree", :shared => true do
35
+ before(:each) do
36
+ @num_items = 1000
37
+ @random_array = Array.new(@num_items) { rand(@num_items) }
38
+ @random_array.each { |x| @tree[x] = x }
39
+ end
40
+
41
+ it "should return correct size (uniqify items first)" do
42
+ @tree.should_not be_empty
43
+ @tree.size.should eql(@random_array.uniq.size)
44
+ end
45
+
46
+ it "should return correct max and min" do
47
+ @tree.min_key.should eql(@random_array.min)
48
+ @tree.max_key.should eql(@random_array.max)
49
+ @tree.min[0].should eql(@random_array.min)
50
+ @tree.max[0].should eql(@random_array.max)
51
+ end
52
+
53
+ it "should not #has_key? keys it doesn't have" do
54
+ @tree.has_key?(100000).should be_false
55
+ end
56
+
57
+ it "should #has_key? keys it does have" do
58
+ @tree.has_key?(@random_array[0]).should be_true
59
+ end
60
+
61
+ it "should remove all keys" do
62
+ ordered = []
63
+ @random_array.uniq.each do |key|
64
+ @tree.has_key?(key).should eql(true)
65
+ ordered << @tree.delete(key)
66
+ @tree.has_key?(key).should eql(false)
67
+ end
68
+ ordered.should eql(@random_array.uniq)
69
+ end
70
+
71
+ it "should delete_min keys correctly" do
72
+ ascending = []
73
+ ascending << @tree.delete_min until @tree.empty?
74
+ ascending.should eql(@random_array.uniq.sort)
75
+ end
76
+
77
+ it "should delete_max keys correctly" do
78
+ descending = []
79
+ descending << @tree.delete_max until @tree.empty?
80
+ descending.should eql(@random_array.uniq.sort.reverse)
81
+ end
82
+
83
+ it "should let you iterate with #each" do
84
+ counter = 0
85
+ sorted_array = @random_array.uniq.sort
86
+ @tree.each do |key, val|
87
+ key.should eql(sorted_array[counter])
88
+ counter += 1
89
+ end
90
+ end
91
+ end
92
+
93
+ describe "empty rbtreemap" do
94
+ before(:each) do
95
+ @tree = Containers::RubyRBTreeMap.new
96
+ end
97
+ it_should_behave_like "empty rbtree"
98
+ end
99
+
100
+ describe "full rbtreemap" do
101
+ before(:each) do
102
+ @tree = Containers::RubyRBTreeMap.new
103
+ end
104
+ it_should_behave_like "non-empty rbtree"
105
+ end
106
+
107
+ begin
108
+ Containers::CRBTreeMap
109
+ describe "empty crbtreemap" do
110
+ before(:each) do
111
+ @tree = Containers::CRBTreeMap.new
112
+ end
113
+ it_should_behave_like "empty rbtree"
114
+ end
115
+
116
+ describe "full crbtreemap" do
117
+ before(:each) do
118
+ @tree = Containers::CRBTreeMap.new
119
+ end
120
+ it_should_behave_like "non-empty rbtree"
121
+ end
122
+ rescue Exception
123
+ end