compsci 0.0.1.1 → 0.0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|