compsci 0.0.1.1 → 0.0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +11 -8
- data/Rakefile +4 -4
- data/VERSION +1 -1
- data/compsci.gemspec +3 -3
- data/examples/binary_tree.rb +48 -3
- data/examples/heap.rb +21 -16
- data/examples/timer.rb +2 -14
- data/lib/compsci/{fib.rb → fibonacci.rb} +15 -0
- data/lib/compsci/heap.rb +18 -8
- data/lib/compsci/tree.rb +94 -57
- data/test/bench/{fib.rb → fibonacci.rb} +38 -5
- data/test/bench/tree.rb +10 -3
- data/test/fibonacci.rb +20 -0
- data/test/fit.rb +44 -62
- data/test/timer.rb +6 -5
- data/test/tree.rb +133 -47
- metadata +4 -4
- data/test/fib.rb +0 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4ee20715906280e4a366f493195ae4decf079cf1
|
4
|
+
data.tar.gz: '0088708c15ca4dae9cdc58ef35d1f85dd60203cc'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 38071308dbc5a6e1988abf8671166c10b793f4baa9bb7371c1fe65c3d2570f1a38caaf61042fa247d58a0451b8711b1ec0eb0ea98e67bfe9fab8bbd8c0652318
|
7
|
+
data.tar.gz: 11a04a2484e15b29a049c1d6c48c373e2360c98b24f02d4bc874353aa29917acbf53acc1932b1d3629796afd5956c91048a14ff8c46b54904f457d1588e7aa45
|
data/README.md
CHANGED
@@ -6,36 +6,39 @@ Provided are some toy implementations for some basic computer science problems.
|
|
6
6
|
|
7
7
|
## [`Tree`](/lib/compsci/tree.rb) data structures
|
8
8
|
|
9
|
-
* `
|
10
|
-
* `
|
11
|
-
* `
|
9
|
+
* `Node` - references children nodes only
|
10
|
+
* `ChildNode` - references parent and children nodes
|
11
|
+
* `Tree` - tracks the `root` node; provides `df_search` and `bf_search`
|
12
|
+
* `NaryTree` - enforces number of children per node via `child_slots`
|
13
|
+
* `BinaryTree` - `NaryTree` with `child_slots` == 2; provides `to_s`
|
12
14
|
* `CompleteBinaryTree` - efficient Array implementation
|
13
15
|
|
14
16
|
## [`Heap`](lib/compsci/heap.rb) data structure
|
15
17
|
|
16
18
|
Implemented with a `CompleteBinaryTree` for storage using simple arithmetic to
|
17
19
|
determine array indices for parent and children. See the
|
18
|
-
[heap example](
|
19
|
-
|
20
|
+
[heap example](examples/heap.rb) which can be executed (among other examples)
|
21
|
+
via `rake examples`.
|
20
22
|
|
21
23
|
Both minheaps and maxheaps are supported. The primary operations are
|
22
|
-
`Heap#push` and `Heap#pop`. My basic Vagrant VM gets
|
24
|
+
`Heap#push` and `Heap#pop`. My basic Vagrant VM gets around 500k pushes per
|
23
25
|
second, constant up past 1M pushes.
|
24
26
|
|
25
|
-
## [`Fibonacci`](lib/compsci/
|
27
|
+
## [`Fibonacci`](lib/compsci/fibonacci.rb) functions
|
26
28
|
|
27
29
|
* `Fibonacci.classic(n)` - naive, recursive
|
28
30
|
* `Fibonacci.cache_recursive(n)` - as above, caching already computed results
|
29
31
|
* `Fibonacci.cache_iterative(n)` - as above but iterative
|
30
32
|
* `Fibonacci.dynamic(n)` - as above but without a cache structure
|
33
|
+
* `Fibonacci.matrix(n)` - matrix is magic; beats dynamic around n=500
|
31
34
|
|
32
35
|
## [`Timer`](/lib/compsci/timer.rb) functions
|
33
36
|
|
34
37
|
* `Timer.now` - uses `Process::CLOCK_MONOTONIC` if available
|
38
|
+
* `Timer.since` - provides the elapsed time since a prior time
|
35
39
|
* `Timer.elapsed` - provides the elapsed time to run a block
|
36
40
|
* `Timer.loop_average` - runs a block repeatedly and provides the mean elapsed
|
37
41
|
time
|
38
|
-
* `Timer.since` - provides the elapsed time since a prior time
|
39
42
|
|
40
43
|
## [`Fit`](lib/compsci/fit.rb) functions
|
41
44
|
|
data/Rakefile
CHANGED
@@ -5,14 +5,14 @@ Rake::TestTask.new :test do |t|
|
|
5
5
|
t.warning = true
|
6
6
|
end
|
7
7
|
|
8
|
-
Rake::TestTask.new bench: :test do |t|
|
8
|
+
Rake::TestTask.new bench: [:test, :loadavg] do |t|
|
9
9
|
t.pattern = "test/bench/*.rb"
|
10
10
|
t.warning = true
|
11
11
|
t.description = "Run benchmarks"
|
12
12
|
end
|
13
13
|
|
14
14
|
desc "Run example scripts"
|
15
|
-
task examples: :test do
|
15
|
+
task examples: [:test, :loadavg] do
|
16
16
|
Dir['examples/**/*.rb'].each { |filepath|
|
17
17
|
puts
|
18
18
|
sh "ruby -Ilib #{filepath}"
|
@@ -31,7 +31,7 @@ metrics_tasks = []
|
|
31
31
|
begin
|
32
32
|
require 'flog_task'
|
33
33
|
FlogTask.new do |t|
|
34
|
-
t.threshold =
|
34
|
+
t.threshold = 450
|
35
35
|
t.dirs = ['lib']
|
36
36
|
t.verbose = true
|
37
37
|
end
|
@@ -68,7 +68,7 @@ task code_metrics: metrics_tasks
|
|
68
68
|
|
69
69
|
desc "Show current system load"
|
70
70
|
task "loadavg" do
|
71
|
-
puts File.read
|
71
|
+
puts "/proc/loadavg %s" % (File.read("/proc/loadavg") rescue "Unavailable")
|
72
72
|
end
|
73
73
|
|
74
74
|
def lib_sh(cmd)
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.2.1
|
data/compsci.gemspec
CHANGED
@@ -16,7 +16,7 @@ Gem::Specification.new do |s|
|
|
16
16
|
README.md
|
17
17
|
Rakefile
|
18
18
|
lib/compsci.rb
|
19
|
-
lib/compsci/
|
19
|
+
lib/compsci/fibonacci.rb
|
20
20
|
lib/compsci/fit.rb
|
21
21
|
lib/compsci/heap.rb
|
22
22
|
lib/compsci/timer.rb
|
@@ -24,12 +24,12 @@ Gem::Specification.new do |s|
|
|
24
24
|
examples/binary_tree.rb
|
25
25
|
examples/heap.rb
|
26
26
|
examples/timer.rb
|
27
|
-
test/
|
27
|
+
test/fibonacci.rb
|
28
28
|
test/fit.rb
|
29
29
|
test/heap.rb
|
30
30
|
test/timer.rb
|
31
31
|
test/tree.rb
|
32
|
-
test/bench/
|
32
|
+
test/bench/fibonacci.rb
|
33
33
|
test/bench/heap.rb
|
34
34
|
test/bench/tree.rb
|
35
35
|
]
|
data/examples/binary_tree.rb
CHANGED
@@ -1,16 +1,60 @@
|
|
1
1
|
require 'compsci/tree'
|
2
|
+
require 'compsci/timer'
|
2
3
|
|
3
4
|
include CompSci
|
4
5
|
|
6
|
+
puts <<EOF
|
7
|
+
#
|
8
|
+
# 3 seconds worth of pushes
|
9
|
+
#
|
10
|
+
|
11
|
+
EOF
|
12
|
+
|
13
|
+
count = 0
|
14
|
+
start = Timer.now
|
15
|
+
start_1k = Timer.now
|
16
|
+
tree = BinaryTree.new(ChildNode, rand(99))
|
17
|
+
|
18
|
+
loop {
|
19
|
+
count += 1
|
20
|
+
|
21
|
+
if count % 100 == 0
|
22
|
+
_ans, push_elapsed = Timer.elapsed { tree.push rand 99 }
|
23
|
+
puts "%ith push: %0.8f s" % [count, push_elapsed]
|
24
|
+
|
25
|
+
if count % 1000 == 0
|
26
|
+
push_1k_elapsed = Timer.since start_1k
|
27
|
+
puts "-----------"
|
28
|
+
puts " 1k push: %0.4f s (%i push / s)" %
|
29
|
+
[push_1k_elapsed, 1000.to_f / push_1k_elapsed]
|
30
|
+
puts
|
31
|
+
start_1k = Timer.now
|
32
|
+
end
|
33
|
+
else
|
34
|
+
tree.push rand 99
|
35
|
+
end
|
36
|
+
|
37
|
+
break if Timer.since(start) > 3
|
38
|
+
}
|
39
|
+
|
40
|
+
puts "pushed %i items in %0.1f s" % [count, Timer.since(start)]
|
41
|
+
puts
|
42
|
+
|
43
|
+
puts <<EOF
|
44
|
+
#
|
45
|
+
# 30 inserts, puts, df_search
|
46
|
+
#
|
47
|
+
|
48
|
+
EOF
|
49
|
+
|
5
50
|
vals = []
|
6
51
|
30.times { vals << rand(99) }
|
7
52
|
p vals
|
8
53
|
|
9
|
-
|
10
|
-
tree = BinaryTree.new(root_node)
|
54
|
+
tree = BinaryTree.new(ChildNode, vals.shift)
|
11
55
|
tree.push vals.shift until vals.empty?
|
12
56
|
|
13
|
-
tree
|
57
|
+
puts tree
|
14
58
|
|
15
59
|
tree.df_search { |n|
|
16
60
|
puts "visited #{n}"
|
@@ -19,3 +63,4 @@ tree.df_search { |n|
|
|
19
63
|
puts
|
20
64
|
|
21
65
|
p tree
|
66
|
+
puts
|
data/examples/heap.rb
CHANGED
@@ -5,34 +5,39 @@ include CompSci
|
|
5
5
|
|
6
6
|
puts <<EOF
|
7
7
|
#
|
8
|
-
# 3 seconds worth of
|
8
|
+
# 3 seconds worth of pushes
|
9
9
|
#
|
10
10
|
|
11
11
|
EOF
|
12
12
|
|
13
13
|
count = 0
|
14
14
|
start = Timer.now
|
15
|
+
start_100k = Timer.now
|
15
16
|
h = Heap.new
|
16
|
-
elapsed = 0
|
17
17
|
|
18
|
-
|
19
|
-
_answer, push_elapsed = Timer.elapsed { h.push rand 99999 }
|
18
|
+
loop {
|
20
19
|
count += 1
|
21
|
-
puts "%ith push: %0.8f s" % [count, push_elapsed] if count % 10000 == 0
|
22
20
|
|
23
|
-
if count %
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
21
|
+
if count % 10000 == 0
|
22
|
+
_answer, push_elapsed = Timer.elapsed { h.push rand 99999 }
|
23
|
+
puts "%ith push: %0.8f s" % [count, push_elapsed]
|
24
|
+
if count % 100000 == 0
|
25
|
+
h.push rand 99999
|
26
|
+
push_100k_elapsed = Timer.since start_100k
|
27
|
+
puts "-------------"
|
28
|
+
puts " 100k push: %0.8f s (%ik push / s)" %
|
29
|
+
[push_100k_elapsed, 100.to_f / push_100k_elapsed]
|
30
|
+
puts
|
31
|
+
start_100k = Timer.now
|
32
|
+
end
|
33
|
+
else
|
34
|
+
h.push rand 99999
|
31
35
|
end
|
32
|
-
elapsed = Timer.now - start
|
33
|
-
end
|
34
36
|
|
35
|
-
|
37
|
+
break if Timer.since(start) > 3
|
38
|
+
}
|
39
|
+
|
40
|
+
puts "pushed %i items in %0.1f s" % [count, Timer.since(start)]
|
36
41
|
puts
|
37
42
|
|
38
43
|
print "still a heap with #{h.size} items? "
|
data/examples/timer.rb
CHANGED
@@ -18,8 +18,8 @@ puts
|
|
18
18
|
|
19
19
|
|
20
20
|
start = Timer.now
|
21
|
-
print "running sleep 0.02 (0.
|
22
|
-
_answer, each_et = Timer.loop_average(seconds: 0.
|
21
|
+
print "running sleep 0.02 (0.3 s): "
|
22
|
+
_answer, each_et = Timer.loop_average(seconds: 0.3) {
|
23
23
|
print '.'
|
24
24
|
sleep 0.02
|
25
25
|
}
|
@@ -28,15 +28,3 @@ puts "each: %0.3f" % each_et
|
|
28
28
|
puts "elapsed: %0.3f" % Timer.since(start)
|
29
29
|
puts "cumulative: %0.3f" % Timer.since(overall_start)
|
30
30
|
puts
|
31
|
-
|
32
|
-
|
33
|
-
start = Timer.now
|
34
|
-
print "running sleep 2 (1 s): "
|
35
|
-
_answer, each_et = Timer.loop_average(seconds: 1) {
|
36
|
-
print '.'
|
37
|
-
sleep 2
|
38
|
-
}
|
39
|
-
puts
|
40
|
-
puts "each: %0.3f" % each_et
|
41
|
-
puts "elapsed: %0.3f" % Timer.since(start)
|
42
|
-
puts "cumulative: %0.3f" % Timer.since(overall_start)
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'compsci'
|
2
|
+
autoload :Matrix, 'matrix'
|
2
3
|
|
3
4
|
module CompSci::Fibonacci
|
4
5
|
def self.classic(n)
|
@@ -16,9 +17,23 @@ module CompSci::Fibonacci
|
|
16
17
|
cache[n]
|
17
18
|
end
|
18
19
|
|
20
|
+
# traditional
|
19
21
|
def self.dynamic(n)
|
22
|
+
a, b = 0, 1
|
23
|
+
n.times { a, b = b, a+b }
|
24
|
+
a
|
25
|
+
end
|
26
|
+
|
27
|
+
# fails for n == 0
|
28
|
+
def self.dynamic_fast(n)
|
20
29
|
a, b = 0, 1
|
21
30
|
(n-1).times { a, b = b, a+b }
|
22
31
|
b
|
23
32
|
end
|
33
|
+
|
34
|
+
# https://gist.github.com/havenwood/02cf291b809327d96a3f
|
35
|
+
# slower than dynamic until around n == 500
|
36
|
+
def self.matrix(n)
|
37
|
+
(Matrix[[0, 1], [1, 1]] ** n.pred)[1, 1].to_i
|
38
|
+
end
|
24
39
|
end
|
data/lib/compsci/heap.rb
CHANGED
@@ -36,13 +36,16 @@ class CompSci::Heap < CompSci::CompleteBinaryTree
|
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
|
-
# append to the array
|
39
|
+
# append to the array
|
40
|
+
# sift_up -- O(log n) on heap size
|
40
41
|
def push(node)
|
41
42
|
@store << node
|
42
43
|
self.sift_up(@store.size - 1)
|
43
44
|
end
|
44
45
|
|
45
|
-
# remove from the front of the array
|
46
|
+
# remove from the front of the array
|
47
|
+
# move last node to root
|
48
|
+
# sift_down -- O(log n) on heap size
|
46
49
|
def pop
|
47
50
|
node = @store.shift
|
48
51
|
replacement = @store.pop
|
@@ -56,25 +59,29 @@ class CompSci::Heap < CompSci::CompleteBinaryTree
|
|
56
59
|
@store.first
|
57
60
|
end
|
58
61
|
|
59
|
-
# called recursively
|
62
|
+
# called recursively
|
63
|
+
# idx represents the node suspected to violate the heap
|
64
|
+
# intended to be O(log n) on heap size
|
60
65
|
def sift_up(idx)
|
61
66
|
return self if idx <= 0
|
62
67
|
pidx = self.class.parent_idx(idx)
|
63
68
|
if !self.heapish?(pidx, idx)
|
64
|
-
@store[idx], @store[pidx] = @store[pidx], @store[idx]
|
69
|
+
@store[idx], @store[pidx] = @store[pidx], @store[idx] # swap
|
65
70
|
self.sift_up(pidx)
|
66
71
|
end
|
67
72
|
self
|
68
73
|
end
|
69
74
|
|
70
|
-
# called recursively
|
75
|
+
# called recursively
|
76
|
+
# idx represents the node suspected to violate the heap
|
77
|
+
# intended to be O(log n) on heap size
|
71
78
|
def sift_down(idx)
|
72
79
|
return self if idx >= @store.size
|
73
80
|
lidx, ridx = self.class.children_idx(idx)
|
74
|
-
#
|
81
|
+
# promote the heapiest child
|
75
82
|
cidx = self.heapish?(lidx, ridx) ? lidx : ridx
|
76
83
|
if !self.heapish?(idx, cidx)
|
77
|
-
@store[idx], @store[cidx] = @store[cidx], @store[idx]
|
84
|
+
@store[idx], @store[cidx] = @store[cidx], @store[idx] # swap
|
78
85
|
self.sift_down(cidx)
|
79
86
|
end
|
80
87
|
self
|
@@ -85,10 +92,13 @@ class CompSci::Heap < CompSci::CompleteBinaryTree
|
|
85
92
|
(@store[pidx] <=> @store[cidx]) != (@cmp_val * -1)
|
86
93
|
end
|
87
94
|
|
88
|
-
# not used internally
|
95
|
+
# not used internally
|
96
|
+
# checks that every node satisfies the heap property
|
97
|
+
# calls heapish? on idx's children and then heap? on them recursively
|
89
98
|
def heap?(idx: 0)
|
90
99
|
check_children = []
|
91
100
|
self.class.children_idx(idx).each { |cidx|
|
101
|
+
# cidx is arithmetically produced; the corresponding child may not exist
|
92
102
|
if cidx < @store.size
|
93
103
|
return false unless self.heapish?(idx, cidx)
|
94
104
|
check_children << cidx
|
data/lib/compsci/tree.rb
CHANGED
@@ -1,26 +1,67 @@
|
|
1
1
|
require 'compsci'
|
2
2
|
|
3
3
|
module CompSci
|
4
|
-
class
|
5
|
-
|
4
|
+
class Node
|
5
|
+
attr_accessor :value
|
6
|
+
attr_reader :children
|
7
|
+
|
8
|
+
def initialize(value)
|
9
|
+
@value = value
|
10
|
+
@children = []
|
11
|
+
# @metadata = {}
|
12
|
+
end
|
6
13
|
|
7
|
-
def
|
8
|
-
@
|
9
|
-
@child_slots = child_slots
|
10
|
-
@open_parent = @root
|
14
|
+
def add_child(node)
|
15
|
+
@children << node
|
11
16
|
end
|
12
17
|
|
13
|
-
def
|
14
|
-
self.
|
18
|
+
def new_child(value)
|
19
|
+
self.add_child self.class.new(value)
|
15
20
|
end
|
16
21
|
|
17
|
-
def
|
18
|
-
node.
|
22
|
+
def add_parent(node)
|
23
|
+
node.add_child(self)
|
19
24
|
end
|
20
25
|
|
21
|
-
def
|
22
|
-
|
23
|
-
|
26
|
+
def to_s
|
27
|
+
@value.to_s
|
28
|
+
end
|
29
|
+
|
30
|
+
def inspect
|
31
|
+
"#<%s:0x%0xi @value=%s @children=[%s]>" %
|
32
|
+
[self.class,
|
33
|
+
self.object_id,
|
34
|
+
self.to_s,
|
35
|
+
@children.map(&:to_s).join(', ')]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# like Node but with a reference to its parent
|
40
|
+
class ChildNode < Node
|
41
|
+
attr_accessor :parent
|
42
|
+
|
43
|
+
def initialize(value)
|
44
|
+
@parent = nil
|
45
|
+
super(value)
|
46
|
+
end
|
47
|
+
|
48
|
+
def add_child(node)
|
49
|
+
node.parent ||= self
|
50
|
+
raise "node has a parent: #{node.parent}" if node.parent != self
|
51
|
+
super(node)
|
52
|
+
end
|
53
|
+
|
54
|
+
def add_parent(node)
|
55
|
+
@parent = node
|
56
|
+
super(node)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
class Tree
|
61
|
+
attr_reader :root
|
62
|
+
|
63
|
+
def initialize(klass, val)
|
64
|
+
@root = klass.new val
|
24
65
|
end
|
25
66
|
|
26
67
|
def df_search(node: nil, &blk)
|
@@ -33,13 +74,6 @@ module CompSci
|
|
33
74
|
nil
|
34
75
|
end
|
35
76
|
|
36
|
-
def df_search_generic(node: nil, &blk)
|
37
|
-
# Perform pre-order operation
|
38
|
-
# children.each { Perform in-order operation }
|
39
|
-
# Perform post-order operation
|
40
|
-
puts "not defined yet"
|
41
|
-
end
|
42
|
-
|
43
77
|
def bf_search(node: nil, &blk)
|
44
78
|
node ||= @root
|
45
79
|
destinations = [node]
|
@@ -51,61 +85,64 @@ module CompSci
|
|
51
85
|
nil
|
52
86
|
end
|
53
87
|
|
54
|
-
|
55
|
-
|
56
|
-
|
88
|
+
def df_search_generic(node: nil, &blk)
|
89
|
+
# Perform pre-order operation
|
90
|
+
# children.each { Perform in-order operation }
|
91
|
+
# Perform post-order operation
|
92
|
+
puts "not defined yet"
|
93
|
+
end
|
94
|
+
end
|
57
95
|
|
58
|
-
|
59
|
-
|
60
|
-
@parent = nil
|
61
|
-
@children = []
|
62
|
-
# @metadata = {}
|
63
|
-
end
|
96
|
+
class NaryTree < Tree
|
97
|
+
attr_reader :child_slots
|
64
98
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
99
|
+
def initialize(klass, val, child_slots:)
|
100
|
+
super(klass, val)
|
101
|
+
raise "#{klass}#parent required" unless @root.respond_to? :parent
|
102
|
+
@child_slots = child_slots
|
103
|
+
end
|
70
104
|
|
71
|
-
|
72
|
-
|
73
|
-
|
105
|
+
def open_parent?(node)
|
106
|
+
node.children.size < @child_slots
|
107
|
+
end
|
74
108
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
end
|
109
|
+
def open_parent
|
110
|
+
@open_parent ||= @root
|
111
|
+
return @open_parent if self.open_parent?(@open_parent)
|
79
112
|
|
80
|
-
|
81
|
-
@value.to_s
|
82
|
-
end
|
113
|
+
# TODO: ugh, there must be a better way, this is O(n)
|
83
114
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
@children.map(&:to_s).join(', ')]
|
115
|
+
# try siblings first
|
116
|
+
if @open_parent.parent
|
117
|
+
@open_parent.parent.children.each { |c|
|
118
|
+
return @open_parent = c if self.open_parent?(c)
|
119
|
+
}
|
90
120
|
end
|
121
|
+
@open_parent = self.bf_search { |n| self.open_parent?(n) }
|
122
|
+
end
|
123
|
+
|
124
|
+
def push(value)
|
125
|
+
self.open_parent.new_child value
|
91
126
|
end
|
92
127
|
end
|
93
128
|
|
94
|
-
class BinaryTree <
|
95
|
-
def initialize(
|
96
|
-
super(
|
129
|
+
class BinaryTree < NaryTree
|
130
|
+
def initialize(klass, val)
|
131
|
+
super(klass, val, child_slots: 2)
|
97
132
|
end
|
98
133
|
|
99
|
-
def
|
134
|
+
def to_s(node: nil, width: 80)
|
100
135
|
count = 0
|
136
|
+
str = ''
|
101
137
|
self.bf_search(node: node) { |n|
|
102
138
|
count += 1
|
103
139
|
level = Math.log(count, 2).floor
|
104
140
|
block_width = width / (2**level)
|
105
|
-
|
106
|
-
|
141
|
+
str += "\n" if 2**level == count and count > 1
|
142
|
+
str += n.to_s.ljust(block_width / 2, ' ').rjust(block_width, ' ')
|
143
|
+
false # keep searching to visit every node
|
107
144
|
}
|
108
|
-
|
145
|
+
str
|
109
146
|
end
|
110
147
|
end
|
111
148
|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'compsci/
|
1
|
+
require 'compsci/fibonacci'
|
2
2
|
require 'minitest/autorun'
|
3
3
|
require 'minitest/benchmark'
|
4
4
|
|
@@ -14,13 +14,14 @@ CACHE_RANGE = [100, 1000, 10000, 100000]
|
|
14
14
|
# this causes churn at the process level and impacts other benchmarks
|
15
15
|
# DYNAMIC_RANGE = [100, 1000, 10000, 100000, 200000, 500000]
|
16
16
|
DYNAMIC_RANGE = [100, 1000, 10000, 100000]
|
17
|
+
MATRIX_RANGE = [100, 1000, 10000, 100000]
|
17
18
|
|
18
19
|
#SPEC_BENCHMARK = true
|
19
|
-
#CLASS_BENCHMARK =
|
20
|
-
|
20
|
+
#CLASS_BENCHMARK = true
|
21
|
+
BENCHMARK_IPS = true
|
21
22
|
SPEC_BENCHMARK = false
|
22
|
-
CLASS_BENCHMARK =
|
23
|
-
BENCHMARK_IPS = false
|
23
|
+
CLASS_BENCHMARK = false
|
24
|
+
#BENCHMARK_IPS = false
|
24
25
|
|
25
26
|
|
26
27
|
if SPEC_BENCHMARK
|
@@ -67,6 +68,17 @@ if SPEC_BENCHMARK
|
|
67
68
|
Fibonacci.dynamic(n)
|
68
69
|
end
|
69
70
|
end
|
71
|
+
|
72
|
+
describe "Fibonacci.matrix Benchmark" do
|
73
|
+
bench_range do
|
74
|
+
MATRIX_RANGE
|
75
|
+
end
|
76
|
+
|
77
|
+
fd = ["Fibonacci.matrix (linear, 0.99)", 0.99]
|
78
|
+
bench_performance_linear(*fd) do |n|
|
79
|
+
Fibonacci.matrix(n)
|
80
|
+
end
|
81
|
+
end
|
70
82
|
end
|
71
83
|
|
72
84
|
if CLASS_BENCHMARK
|
@@ -89,6 +101,7 @@ end
|
|
89
101
|
if BENCHMARK_IPS
|
90
102
|
require 'benchmark/ips'
|
91
103
|
|
104
|
+
# recursive benchmarks with low N; iterative for comparison
|
92
105
|
Benchmark.ips do |b|
|
93
106
|
b.config time: 3, warmup: 0.5
|
94
107
|
num = 25
|
@@ -105,10 +118,30 @@ if BENCHMARK_IPS
|
|
105
118
|
Fibonacci.cache_iterative(num)
|
106
119
|
}
|
107
120
|
|
121
|
+
b.compare!
|
122
|
+
end
|
123
|
+
|
124
|
+
# nonrecursive benchmarks with high N
|
125
|
+
Benchmark.ips do |b|
|
126
|
+
b.config time: 3, warmup: 0.5
|
127
|
+
num = 500
|
128
|
+
|
129
|
+
b.report("Fibonacci.cache_iterative(#{num})") {
|
130
|
+
Fibonacci.cache_iterative(num)
|
131
|
+
}
|
132
|
+
|
108
133
|
b.report("Fibonacci.dynamic(#{num})") {
|
109
134
|
Fibonacci.dynamic(num)
|
110
135
|
}
|
111
136
|
|
137
|
+
b.report("Fibonacci.dynamic_fast(#{num})") {
|
138
|
+
Fibonacci.dynamic_fast(num)
|
139
|
+
}
|
140
|
+
|
141
|
+
b.report("Fibonacci.matrix(#{num})") {
|
142
|
+
Fibonacci.matrix(num)
|
143
|
+
}
|
144
|
+
|
112
145
|
b.compare!
|
113
146
|
end
|
114
147
|
end
|
data/test/bench/tree.rb
CHANGED
@@ -4,7 +4,7 @@ require 'minitest/benchmark'
|
|
4
4
|
|
5
5
|
include CompSci
|
6
6
|
|
7
|
-
describe "
|
7
|
+
describe "BinaryTree#push Benchmark" do
|
8
8
|
bench_range do
|
9
9
|
# note, 5000 takes way too long and is definitely not constant time
|
10
10
|
# TODO: BUG?
|
@@ -12,8 +12,15 @@ describe "Tree#push Benchmark" do
|
|
12
12
|
[10, 100, 1000, 2000]
|
13
13
|
end
|
14
14
|
|
15
|
-
bench_performance_constant "
|
16
|
-
tree =
|
15
|
+
bench_performance_constant "BinaryTree#push (constant)" do |n|
|
16
|
+
tree = BinaryTree.new(ChildNode, 42)
|
17
|
+
n.times { tree.push rand 99 }
|
18
|
+
end
|
19
|
+
|
20
|
+
bench_performance_linear "BinaryTree#push (linear)" do |n|
|
21
|
+
skip "this fails with r^2 around 0.91"
|
22
|
+
|
23
|
+
tree = BinaryTree.new ChildNode.new 42
|
17
24
|
n.times { tree.push rand 99 }
|
18
25
|
end
|
19
26
|
end
|
data/test/fibonacci.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'compsci/fibonacci'
|
2
|
+
require 'minitest/autorun'
|
3
|
+
|
4
|
+
include CompSci
|
5
|
+
|
6
|
+
describe Fibonacci do
|
7
|
+
before do
|
8
|
+
@answers = [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
|
9
|
+
end
|
10
|
+
|
11
|
+
it "must calculate fib(0..10)" do
|
12
|
+
@answers.each_with_index { |ans, i|
|
13
|
+
Fibonacci.classic(i).must_equal ans
|
14
|
+
Fibonacci.cache_recursive(i).must_equal ans
|
15
|
+
Fibonacci.cache_iterative(i).must_equal ans
|
16
|
+
Fibonacci.dynamic(i).must_equal ans
|
17
|
+
Fibonacci.matrix(i).must_equal ans
|
18
|
+
}
|
19
|
+
end
|
20
|
+
end
|
data/test/fit.rb
CHANGED
@@ -3,19 +3,23 @@ require 'minitest/autorun'
|
|
3
3
|
|
4
4
|
include CompSci
|
5
5
|
|
6
|
+
def noise # range: -0.5 to 0.5
|
7
|
+
rand - 0.5
|
8
|
+
end
|
9
|
+
|
6
10
|
describe Fit do
|
7
11
|
before do
|
8
12
|
@xs = [1, 2, 5, 10, 20, 50, 100, 200, 500]
|
9
13
|
end
|
10
14
|
|
11
|
-
describe "sigma" do
|
15
|
+
describe "Fit.sigma" do
|
12
16
|
it "must answer correctly" do
|
13
17
|
Fit.sigma([1, 2, 3]).must_equal 6
|
14
18
|
Fit.sigma([1, 2, 3]) { |n| n ** 2 }.must_equal 14
|
15
19
|
end
|
16
20
|
end
|
17
21
|
|
18
|
-
describe "error" do
|
22
|
+
describe "Fit.error" do
|
19
23
|
it "must calculate r^2" do
|
20
24
|
Fit.error([[1, 1], [2, 2], [3, 3]]) { |x| x }.must_equal 1.0
|
21
25
|
Fit.error([[1, 1], [2, 2], [3, 4]]) { |x| x }.must_be_close_to 0.785
|
@@ -23,22 +27,28 @@ describe Fit do
|
|
23
27
|
end
|
24
28
|
|
25
29
|
# y = a
|
26
|
-
describe "constant" do
|
30
|
+
describe "Fit.constant" do
|
31
|
+
it "must accept constant data" do
|
32
|
+
[0, 1, 10, 100, 1000, 9999].each { |a|
|
33
|
+
y_bar, variance = Fit.constant(@xs, @xs.map { |x| a })
|
34
|
+
y_bar.must_equal a
|
35
|
+
variance.must_equal 0
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
27
39
|
# note, this test can possibly fail depending on the uniformity of
|
28
40
|
# rand's output for our sample
|
29
|
-
it "must accept constant data" do
|
41
|
+
it "must accept noisy constant data" do
|
30
42
|
[0, 1, 10, 100, 1000, 9999].each { |a|
|
31
|
-
|
32
|
-
y_bar, variance = Fit.constant(@xs, ys)
|
33
|
-
var_val = variance / ys.size
|
43
|
+
y_bar, variance = Fit.constant(@xs, @xs.map { |x| a + noise() })
|
34
44
|
y_bar.must_be_close_to a, 0.3
|
35
|
-
|
45
|
+
(variance / @xs.size).must_be_close_to 0.1, 0.09
|
36
46
|
}
|
37
47
|
end
|
38
48
|
end
|
39
49
|
|
40
50
|
# y = a + b*ln(x)
|
41
|
-
describe "logarithmic" do
|
51
|
+
describe "Fit.logarithmic" do
|
42
52
|
it "must accept logarithmic data" do
|
43
53
|
[-9999, -2000, -500, -0.01, 0.01, 500, 2000, 9999].each { |a|
|
44
54
|
[-9999, -2000, -500, -0.01, 0.01, 500, 2000, 9999].each { |b|
|
@@ -52,7 +62,7 @@ describe Fit do
|
|
52
62
|
end
|
53
63
|
|
54
64
|
# y = a + bx
|
55
|
-
describe "linear" do
|
65
|
+
describe "Fit.linear" do
|
56
66
|
it "must accept linear data" do
|
57
67
|
[-9999, -2000, -500, -0.01, 0.01, 500, 2000, 9999].each { |a|
|
58
68
|
[-9999, -2000, -500, -0.01, 0.01, 500, 2000, 9999].each { |b|
|
@@ -64,21 +74,22 @@ describe Fit do
|
|
64
74
|
}
|
65
75
|
end
|
66
76
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
77
|
+
it "must accept constant data" do
|
78
|
+
[0, 1, 10, 100, 1000, 9999].each { |a|
|
79
|
+
ary = Fit.linear(@xs, @xs.map { |x| a })
|
80
|
+
ary[0].must_equal a
|
81
|
+
ary[1].must_equal 0
|
82
|
+
ary[2].nan?.must_equal true
|
83
|
+
}
|
84
|
+
end
|
85
|
+
|
74
86
|
# note, this test can possibly fail depending on the uniformity of
|
75
87
|
# rand's output for our sample
|
76
88
|
#
|
77
|
-
it "must accept constant data" do
|
89
|
+
it "must accept noisy constant data" do
|
78
90
|
r2s = []
|
79
91
|
[0, 1, 10, 100, 1000, 9999].each { |a|
|
80
|
-
|
81
|
-
ary = Fit.linear(@xs, ys)
|
92
|
+
ary = Fit.linear(@xs, @xs.map { |x| a + noise() })
|
82
93
|
ary[0].must_be_close_to a, 0.4
|
83
94
|
ary[1].must_be_close_to 0, 0.05
|
84
95
|
r2s << ary[2]
|
@@ -87,52 +98,23 @@ describe Fit do
|
|
87
98
|
mean_r2.must_be_close_to 0.15, 0.15
|
88
99
|
end
|
89
100
|
|
90
|
-
it "must reject
|
91
|
-
skip "
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
# the r2 for fit_linear is mostly about the relative fit of a sloped
|
97
|
-
# line compared to zero slope (i.e. y_bar)
|
98
|
-
#
|
99
|
-
# this is why a linear r2 close to 1.0 is the wrong test for fit_constant
|
100
|
-
# because the relative fit of the sloped line (slope near 0) doesn't
|
101
|
-
# "explain" much relative to y_bar
|
102
|
-
#
|
103
|
-
# in the case where y = x^3, a linear fit may still have a high r2,
|
104
|
-
# because the error for the y_bar predictor is astronomical. A super
|
105
|
-
# steep slope fits (relative to the zero slope mean) pretty well.
|
106
|
-
|
107
|
-
# this calls into question how useful r2 is, as we need it to be a
|
108
|
-
# threshold value due to noise, yet even a terrible fit like trying to
|
109
|
-
# match x^3 is hard to distinguish from noise
|
110
|
-
#
|
101
|
+
it "must reject x^2" do
|
102
|
+
skip "it does not reject x^2 at r^2 < 0.99"
|
103
|
+
xs = [1, 10, 100, 1000]
|
104
|
+
_a, _b, r2 = Fit.linear(xs, xs.map { |x| x**2 })
|
105
|
+
r2.must_be :<, 0.99
|
106
|
+
end
|
111
107
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
puts
|
118
|
-
puts "fit_linear: #{ary.inspect}"
|
119
|
-
puts "y = %0.2f + %0.2f(x) (r2 = %0.3f)" % ary
|
120
|
-
puts
|
121
|
-
col1, col2 = 5, 15
|
122
|
-
puts "x".ljust(col1, ' ') + "y".ljust(col2, ' ') + "predicted"
|
123
|
-
puts '---'.ljust(col1, ' ') + '---'.ljust(col2, ' ') + '---'
|
124
|
-
@xs.zip(ys).each { |(x,y)|
|
125
|
-
puts x.to_s.ljust(col1, ' ') + y.to_s.ljust(col2, ' ') +
|
126
|
-
"%0.2f" % (ary[0] + ary[1] * x)
|
127
|
-
}
|
128
|
-
# ary[2].must_be :<, 0.8
|
129
|
-
ary[2].must_be :<, 0.9
|
130
|
-
end
|
108
|
+
it "must reject x^3" do
|
109
|
+
skip "it does not reject x^3 at r^2 < 0.99"
|
110
|
+
xs = [1, 10, 100, 1000]
|
111
|
+
_a, _b, r2 = Fit.linear(xs, xs.map { |x| x**3 })
|
112
|
+
r2.must_be :<, 0.99
|
131
113
|
end
|
132
114
|
end
|
133
115
|
|
134
116
|
# y = ae^(bx)
|
135
|
-
describe "exponential" do
|
117
|
+
describe "Fit.exponential" do
|
136
118
|
it "must accept exponential data" do
|
137
119
|
[0.001, 7.5, 500, 1000, 5000, 9999].each { |a|
|
138
120
|
[-1.4, -1.1, -0.1, 0.01, 0.5, 0.75].each { |b|
|
@@ -146,7 +128,7 @@ describe Fit do
|
|
146
128
|
end
|
147
129
|
|
148
130
|
# y = ax^b
|
149
|
-
describe "power" do
|
131
|
+
describe "Fit.power" do
|
150
132
|
it "must accept power data" do
|
151
133
|
[0.01, 7.5, 500, 1000, 5000, 9999].each { |a|
|
152
134
|
[-114, -100, -10, -0.5, -0.1, 0.1, 0.75, 10, 50, 60].each { |b|
|
data/test/timer.rb
CHANGED
@@ -23,26 +23,27 @@ describe Timer do
|
|
23
23
|
describe "loop_average" do
|
24
24
|
it "return the block value and a positive number" do
|
25
25
|
start = Timer.now
|
26
|
-
answer, avg_et = Timer.loop_average(seconds: 0.
|
26
|
+
answer, avg_et = Timer.loop_average(seconds: 0.1) {
|
27
27
|
sleep 0.01
|
28
28
|
:foo
|
29
29
|
}
|
30
30
|
answer.must_equal :foo
|
31
31
|
avg_et.must_be_close_to 0.01, 0.005
|
32
|
-
Timer.since(start).must_be_close_to 0.
|
32
|
+
Timer.since(start).must_be_close_to 0.15, 0.05
|
33
33
|
end
|
34
34
|
|
35
35
|
it "must repeat short loops and stop on time" do
|
36
|
+
# see above, Timer.since(start)
|
36
37
|
true.must_equal true
|
37
38
|
end
|
38
39
|
|
39
40
|
it "must not interrupt long loops" do
|
40
41
|
start = Timer.now
|
41
|
-
_answer, avg_et = Timer.loop_average(seconds: 0.
|
42
|
-
sleep 0.
|
42
|
+
_answer, avg_et = Timer.loop_average(seconds: 0.01) {
|
43
|
+
sleep 0.1
|
43
44
|
}
|
44
45
|
Timer.since(start).must_be_close_to avg_et, 0.05
|
45
|
-
avg_et.must_be_close_to 0.
|
46
|
+
avg_et.must_be_close_to 0.15, 0.05
|
46
47
|
end
|
47
48
|
end
|
48
49
|
end
|
data/test/tree.rb
CHANGED
@@ -3,14 +3,135 @@ require 'minitest/autorun'
|
|
3
3
|
|
4
4
|
include CompSci
|
5
5
|
|
6
|
+
describe Node do
|
7
|
+
before do
|
8
|
+
@martin_sheen = Node.new 'martin'
|
9
|
+
@charlie_sheen = Node.new 'charlie'
|
10
|
+
@emilio_estevez = Node.new 'emilio'
|
11
|
+
end
|
12
|
+
|
13
|
+
it "must start with no children" do
|
14
|
+
[@martin_sheen, @charlie_sheen, @emilio_estevez].each { |n|
|
15
|
+
n.children.must_be_empty
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
it "must track children" do
|
20
|
+
@charlie_sheen.add_parent(@martin_sheen)
|
21
|
+
@martin_sheen.children.must_include @charlie_sheen
|
22
|
+
|
23
|
+
@martin_sheen.children.wont_include @emilio_estevez
|
24
|
+
@martin_sheen.add_child @emilio_estevez
|
25
|
+
@martin_sheen.children.must_include @emilio_estevez
|
26
|
+
end
|
27
|
+
|
28
|
+
it "must create children from scalars" do
|
29
|
+
@martin_sheen.new_child 'fake_emilio'
|
30
|
+
@martin_sheen.children.size.must_equal 1
|
31
|
+
@martin_sheen.children.first.value.must_equal 'fake_emilio'
|
32
|
+
@martin_sheen.children.wont_include @emilio_estevez
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe ChildNode do
|
37
|
+
before do
|
38
|
+
@martin_sheen = ChildNode.new 'martin'
|
39
|
+
@charlie_sheen = ChildNode.new 'charlie'
|
40
|
+
@emilio_estevez = ChildNode.new 'emilio'
|
41
|
+
end
|
42
|
+
|
43
|
+
it "must start with neither parent nor children" do
|
44
|
+
[@martin_sheen, @charlie_sheen, @emilio_estevez].each { |n|
|
45
|
+
n.parent.nil?.must_equal true
|
46
|
+
n.children.must_be_empty
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
it "must track parent and children" do
|
51
|
+
@charlie_sheen.add_parent(@martin_sheen)
|
52
|
+
@charlie_sheen.parent.must_equal @martin_sheen
|
53
|
+
@martin_sheen.children.must_include @charlie_sheen
|
54
|
+
|
55
|
+
@martin_sheen.children.wont_include @emilio_estevez
|
56
|
+
@martin_sheen.add_child @emilio_estevez
|
57
|
+
@martin_sheen.children.must_include @emilio_estevez
|
58
|
+
@emilio_estevez.parent.must_equal @martin_sheen
|
59
|
+
end
|
60
|
+
|
61
|
+
it "must create children from scalars" do
|
62
|
+
@martin_sheen.new_child 'fake_emilio'
|
63
|
+
@martin_sheen.children.size.must_equal 1
|
64
|
+
@martin_sheen.children.first.value.must_equal 'fake_emilio'
|
65
|
+
@martin_sheen.children.wont_include @emilio_estevez
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
6
69
|
describe Tree do
|
7
70
|
before do
|
8
|
-
@
|
9
|
-
@
|
71
|
+
@tree = Tree.new(Node, 42)
|
72
|
+
@vals = Array.new(99) { rand 99 }
|
73
|
+
end
|
74
|
+
|
75
|
+
it "is populated via the root node" do
|
76
|
+
@vals.each { |v| @tree.root.new_child v }
|
77
|
+
@tree.root.children.size.must_equal @vals.size
|
78
|
+
end
|
79
|
+
|
80
|
+
it "does depth_first search" do
|
81
|
+
vals = (0..30).to_a
|
82
|
+
tree = Tree.new(Node, vals.shift)
|
83
|
+
tree.root.new_child vals.shift
|
84
|
+
tree.root.new_child vals.shift
|
85
|
+
tree.root.children.each { |c|
|
86
|
+
c.new_child vals.shift
|
87
|
+
c.new_child vals.shift
|
88
|
+
|
89
|
+
c.children.each { |cc|
|
90
|
+
cc.new_child vals.shift
|
91
|
+
cc.new_child vals.shift
|
92
|
+
}
|
93
|
+
}
|
94
|
+
|
95
|
+
visited = []
|
96
|
+
tree.df_search { |n|
|
97
|
+
visited << n.value
|
98
|
+
false
|
99
|
+
}
|
100
|
+
visited.wont_be_empty
|
101
|
+
visited.must_equal [0, 1, 3, 5, 6, 4, 7, 8, 2, 9, 11, 12, 10, 13, 14]
|
102
|
+
end
|
103
|
+
|
104
|
+
it "does breadth_first search" do
|
105
|
+
vals = (0..30).to_a
|
106
|
+
tree = Tree.new(Node, vals.shift)
|
107
|
+
tree.root.new_child vals.shift
|
108
|
+
tree.root.new_child vals.shift
|
109
|
+
tree.root.children.each { |c|
|
110
|
+
c.new_child vals.shift
|
111
|
+
c.new_child vals.shift
|
112
|
+
|
113
|
+
c.children.each { |cc|
|
114
|
+
cc.new_child vals.shift
|
115
|
+
cc.new_child vals.shift
|
116
|
+
}
|
117
|
+
}
|
118
|
+
|
119
|
+
visited = []
|
120
|
+
tree.bf_search { |n|
|
121
|
+
visited << n.value
|
122
|
+
false
|
123
|
+
}
|
124
|
+
visited.wont_be_empty
|
125
|
+
visited.must_equal [0, 1, 2, 3, 4, 9, 10, 5, 6, 7, 8, 11, 12, 13, 14]
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
describe NaryTree do
|
130
|
+
before do
|
131
|
+
@tree = NaryTree.new(ChildNode, 42, child_slots: 3)
|
10
132
|
end
|
11
133
|
|
12
134
|
it "must have an open parent" do
|
13
|
-
@tree.open_parent?(@node).must_equal true
|
14
135
|
@tree.open_parent?(@tree.root).must_equal true
|
15
136
|
@tree.open_parent?(@tree.open_parent).must_equal true
|
16
137
|
end
|
@@ -24,7 +145,7 @@ describe Tree do
|
|
24
145
|
|
25
146
|
describe "searching" do
|
26
147
|
before do
|
27
|
-
@tree =
|
148
|
+
@tree = NaryTree.new(ChildNode, 42, child_slots: 2)
|
28
149
|
99.times { |i| @tree.push i }
|
29
150
|
end
|
30
151
|
|
@@ -76,59 +197,24 @@ describe Tree do
|
|
76
197
|
count.must_equal 83
|
77
198
|
end
|
78
199
|
end
|
79
|
-
|
80
|
-
describe Tree::Node do
|
81
|
-
before do
|
82
|
-
@martin_sheen = Tree::Node.new 'martin'
|
83
|
-
@charlie_sheen = Tree::Node.new 'charlie'
|
84
|
-
@emilio_estevez = Tree::Node.new 'emilio'
|
85
|
-
end
|
86
|
-
|
87
|
-
it "must start with no relations" do
|
88
|
-
[@martin_sheen, @charlie_sheen, @emilio_estevez].each { |n|
|
89
|
-
n.parent.nil?.must_equal true
|
90
|
-
n.children.must_be_empty
|
91
|
-
}
|
92
|
-
end
|
93
|
-
|
94
|
-
it "must allow relations" do
|
95
|
-
@charlie_sheen.add_parent(@martin_sheen)
|
96
|
-
@charlie_sheen.parent.must_equal @martin_sheen
|
97
|
-
@martin_sheen.children.must_include @charlie_sheen
|
98
|
-
|
99
|
-
@martin_sheen.children.wont_include @emilio_estevez
|
100
|
-
@martin_sheen.add_child @emilio_estevez
|
101
|
-
@martin_sheen.children.must_include @emilio_estevez
|
102
|
-
@emilio_estevez.parent.must_equal @martin_sheen
|
103
|
-
end
|
104
|
-
|
105
|
-
it "must create children from scalars" do
|
106
|
-
@martin_sheen.new_child 'fake_emilio'
|
107
|
-
@martin_sheen.children.size.must_equal 1
|
108
|
-
@martin_sheen.children.first.value.must_equal 'fake_emilio'
|
109
|
-
@martin_sheen.children.wont_include @emilio_estevez
|
110
|
-
end
|
111
|
-
end
|
112
200
|
end
|
113
201
|
|
114
202
|
describe BinaryTree do
|
115
203
|
before do
|
116
|
-
@tree = BinaryTree.new(
|
204
|
+
@tree = BinaryTree.new(ChildNode, 42)
|
117
205
|
end
|
118
206
|
|
119
207
|
it "must have 2 child_slots" do
|
120
208
|
@tree.child_slots.must_equal 2
|
121
209
|
end
|
122
210
|
|
123
|
-
it "must
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
line_count =
|
129
|
-
line_count.
|
130
|
-
line_count.must_be :<, 7
|
131
|
-
err.must_be_empty
|
211
|
+
it "must to_s" do
|
212
|
+
item_count = 31
|
213
|
+
# tree already has a root node
|
214
|
+
(item_count - 1).times { @tree.push rand 99 }
|
215
|
+
str = @tree.to_s
|
216
|
+
line_count = str.split("\n").size
|
217
|
+
line_count.must_equal Math.log(item_count + 1, 2).ceil
|
132
218
|
end
|
133
219
|
end
|
134
220
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: compsci
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rick Hull
|
@@ -38,15 +38,15 @@ files:
|
|
38
38
|
- examples/heap.rb
|
39
39
|
- examples/timer.rb
|
40
40
|
- lib/compsci.rb
|
41
|
-
- lib/compsci/
|
41
|
+
- lib/compsci/fibonacci.rb
|
42
42
|
- lib/compsci/fit.rb
|
43
43
|
- lib/compsci/heap.rb
|
44
44
|
- lib/compsci/timer.rb
|
45
45
|
- lib/compsci/tree.rb
|
46
|
-
- test/bench/
|
46
|
+
- test/bench/fibonacci.rb
|
47
47
|
- test/bench/heap.rb
|
48
48
|
- test/bench/tree.rb
|
49
|
-
- test/
|
49
|
+
- test/fibonacci.rb
|
50
50
|
- test/fit.rb
|
51
51
|
- test/heap.rb
|
52
52
|
- test/timer.rb
|
data/test/fib.rb
DELETED
@@ -1,13 +0,0 @@
|
|
1
|
-
require 'compsci/fib'
|
2
|
-
require 'minitest/autorun'
|
3
|
-
|
4
|
-
include CompSci
|
5
|
-
|
6
|
-
describe Fibonacci do
|
7
|
-
it "must calculate fib(10) == 55" do
|
8
|
-
Fibonacci.classic(10).must_equal 55
|
9
|
-
Fibonacci.cache_recursive(10).must_equal 55
|
10
|
-
Fibonacci.cache_iterative(10).must_equal 55
|
11
|
-
Fibonacci.dynamic(10).must_equal 55
|
12
|
-
end
|
13
|
-
end
|