compsci 0.0.3.1 → 0.1.0.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 +63 -14
- data/VERSION +1 -1
- data/compsci.gemspec +5 -22
- data/examples/binary_tree.rb +11 -6
- data/examples/complete_tree.rb +47 -0
- data/examples/heap.rb +19 -7
- data/examples/tree.rb +41 -0
- data/lib/compsci.rb +1 -2
- data/lib/compsci/complete_tree.rb +109 -0
- data/lib/compsci/fibonacci.rb +31 -30
- data/lib/compsci/fit.rb +135 -135
- data/lib/compsci/heap.rb +14 -108
- data/lib/compsci/names.rb +132 -0
- data/lib/compsci/node.rb +67 -0
- data/lib/compsci/timer.rb +30 -29
- data/lib/compsci/tree.rb +48 -138
- data/test/bench/tree.rb +2 -2
- data/test/complete_tree.rb +131 -0
- data/test/heap.rb +121 -34
- data/test/names.rb +96 -0
- data/test/node.rb +89 -0
- data/test/tree.rb +49 -237
- metadata +24 -2
@@ -0,0 +1,132 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
module CompSci
|
3
|
+
module Names
|
4
|
+
WW1 = [:apples, :butter, :charlie, :duff, :edward, :freddy, :george,
|
5
|
+
:harry, :ink, :johnnie, :king, :london, :monkey, :nuts, :orange,
|
6
|
+
:pudding, :queenie, :robert, :sugar, :tommy, :uncle, :vinegar,
|
7
|
+
:willie, :xerxes, :yellow, :zebra]
|
8
|
+
WW2 = [:able, :baker, :charlie, :dog, :easy, :fox, :george, :how, :item,
|
9
|
+
:jig, :king, :love, :mike, :nan, :oboe, :peter, :queen, :roger,
|
10
|
+
:sugar, :tare, :uncle, :victor, :william, :xray, :yoke, :zebra]
|
11
|
+
NATO = [:alfa, :bravo, :charlie, :delta, :echo, :foxtrot, :golf, :hotel,
|
12
|
+
:india, :juliett, :kilo, :lima, :mike, :november, :oscar, :papa,
|
13
|
+
:quebec, :romeo, :sierra, :tango, :uniform, :victor, :whiskey,
|
14
|
+
:xray, :yankee, :zulu]
|
15
|
+
CRYPTO = [:alice, :bob, :charlie, :david, :eve, :frank, :grace, :heidi,
|
16
|
+
:judy, :mallory, :olivia, :peggy, :sybil, :trudy, :victor,
|
17
|
+
:wendy]
|
18
|
+
ENGLISH_UPPER = [*'A'..'Z']
|
19
|
+
ENGLISH_LOWER = [*'a'..'z']
|
20
|
+
|
21
|
+
PLANETS = [:mercury, :venus, :earth, :mars, :jupiter, :saturn, :uranus,
|
22
|
+
:neptune, :pluto]
|
23
|
+
|
24
|
+
SOLAR = [:mercury, :venus, :earth, :mars, :asteroid_belt, :jupiter,
|
25
|
+
:saturn, :uranus, :neptune, :kuiper_belt, :scattered_disk,
|
26
|
+
:heliosphere]
|
27
|
+
|
28
|
+
# map val to [0..names.length]
|
29
|
+
def self.assign(val, names)
|
30
|
+
case val
|
31
|
+
when String
|
32
|
+
if val.match %r{\A\d+\z}
|
33
|
+
pos = val.to_i
|
34
|
+
else
|
35
|
+
case names.size
|
36
|
+
when (1..26)
|
37
|
+
pos = val[0].upcase.ord - 'A'.ord
|
38
|
+
when (27..99)
|
39
|
+
pos = val[0].ord - 'A'.ord
|
40
|
+
pos -= 6 if pos > 26
|
41
|
+
else
|
42
|
+
raise "unexpected names.size: #{names.size}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
when Integer
|
46
|
+
pos = val
|
47
|
+
else
|
48
|
+
raise "unexpected val: #{val} (#{val.class})"
|
49
|
+
end
|
50
|
+
|
51
|
+
if pos < 0 or pos >= names.size
|
52
|
+
raise "val #{val} pos #{pos} outside of names range (#{names.size})"
|
53
|
+
end
|
54
|
+
names[pos]
|
55
|
+
end
|
56
|
+
|
57
|
+
module Greek
|
58
|
+
UPPER = [*'Α'..'Ρ', *'Σ'..'Ω']
|
59
|
+
LOWER = [*('α'..'ρ'), *('σ'..'ω')]
|
60
|
+
SYMBOLS = [:alpha, :beta, :gamma, :delta, :epsilon, :zeta, :eta, :theta,
|
61
|
+
:iota, :kappa, :lambda, :mu, :nu, :xi, :omicron, :pi, :rho,
|
62
|
+
:sigma, :tau, :upsilon, :phi, :chi, :psi, :omega]
|
63
|
+
|
64
|
+
# e.g. { alpha: ['Α', 'α'] ...
|
65
|
+
CHAR_MAP = {}
|
66
|
+
SYMBOLS.each_with_index { |sym, i| CHAR_MAP[sym] = [UPPER[i], LOWER[i]] }
|
67
|
+
|
68
|
+
LATIN_SYMBOLS = {
|
69
|
+
a: :alpha,
|
70
|
+
b: :beta,
|
71
|
+
c: :gamma,
|
72
|
+
d: :delta,
|
73
|
+
e: :epsilon,
|
74
|
+
f: :zeta,
|
75
|
+
g: :eta,
|
76
|
+
h: :theta,
|
77
|
+
i: :iota,
|
78
|
+
j: :xi, # use for j, q and w
|
79
|
+
k: :kappa,
|
80
|
+
l: :lambda,
|
81
|
+
m: :mu,
|
82
|
+
n: :nu,
|
83
|
+
# nonesuch: => :xi,
|
84
|
+
o: :omicron,
|
85
|
+
p: :pi,
|
86
|
+
q: :xi, # use for j, q and w
|
87
|
+
r: :rho,
|
88
|
+
s: :sigma,
|
89
|
+
t: :tau,
|
90
|
+
u: :upsilon,
|
91
|
+
v: :phi,
|
92
|
+
w: :xi, # use for j, q and w
|
93
|
+
x: :chi,
|
94
|
+
y: :psi,
|
95
|
+
z: :omega,
|
96
|
+
}
|
97
|
+
SYMBOLS26 = LATIN_SYMBOLS.values
|
98
|
+
|
99
|
+
def self.upper(latin_str)
|
100
|
+
CHAR_MAP.fetch(self.sym(latin_str)).first
|
101
|
+
end
|
102
|
+
|
103
|
+
def self.lower(latin_str)
|
104
|
+
CHAR_MAP.fetch(self.sym(latin_str)).last
|
105
|
+
end
|
106
|
+
|
107
|
+
def self.sym(val)
|
108
|
+
case val
|
109
|
+
when String
|
110
|
+
if val.match %r{\A\d+\z}
|
111
|
+
val = val.to_i
|
112
|
+
if val >= 0 and val < SYMBOLS.size
|
113
|
+
SYMBOLS[val]
|
114
|
+
else
|
115
|
+
raise "val #{val} not in range (#{SYMBOLS.size})"
|
116
|
+
end
|
117
|
+
else
|
118
|
+
LATIN_SYMBOLS.fetch val[0].downcase.to_sym
|
119
|
+
end
|
120
|
+
when Integer
|
121
|
+
if val >= 0 and val < SYMBOLS.size
|
122
|
+
SYMBOLS[val]
|
123
|
+
else
|
124
|
+
raise "val #{val} not in range (#{SYMBOLS.size})"
|
125
|
+
end
|
126
|
+
else
|
127
|
+
raise "unexpected val #{val} (#{val.class})"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
data/lib/compsci/node.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
module CompSci
|
2
|
+
# has a value and an array of children
|
3
|
+
class Node
|
4
|
+
attr_accessor :value
|
5
|
+
attr_reader :children
|
6
|
+
|
7
|
+
def initialize(value)
|
8
|
+
@value = value
|
9
|
+
@children = []
|
10
|
+
# @metadata = {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def add_child(node)
|
14
|
+
@children << node
|
15
|
+
end
|
16
|
+
|
17
|
+
def new_child(value)
|
18
|
+
self.add_child self.class.new(value)
|
19
|
+
end
|
20
|
+
|
21
|
+
def add_parent(node)
|
22
|
+
node.add_child(self)
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_s
|
26
|
+
@value.to_s
|
27
|
+
end
|
28
|
+
|
29
|
+
def inspect
|
30
|
+
"#<%s:0x%0xi @value=%s @children=[%s]>" %
|
31
|
+
[self.class,
|
32
|
+
self.object_id,
|
33
|
+
self.to_s,
|
34
|
+
@children.map(&:to_s).join(', ')]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# like Node but with a reference to its parent
|
39
|
+
class ChildNode < Node
|
40
|
+
attr_accessor :parent
|
41
|
+
|
42
|
+
def initialize(value)
|
43
|
+
@parent = nil
|
44
|
+
super(value)
|
45
|
+
end
|
46
|
+
|
47
|
+
# O(log n) recursive
|
48
|
+
def gen
|
49
|
+
@parent ? @parent.gen + 1 : 0
|
50
|
+
end
|
51
|
+
|
52
|
+
def siblings
|
53
|
+
@parent ? @parent.children : []
|
54
|
+
end
|
55
|
+
|
56
|
+
def add_child(node)
|
57
|
+
node.parent ||= self
|
58
|
+
raise "node has a parent: #{node.parent}" if node.parent != self
|
59
|
+
super(node)
|
60
|
+
end
|
61
|
+
|
62
|
+
def add_parent(node)
|
63
|
+
@parent = node
|
64
|
+
super(node)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/lib/compsci/timer.rb
CHANGED
@@ -1,36 +1,37 @@
|
|
1
|
-
|
1
|
+
module CompSci
|
2
2
|
|
3
|
-
module
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
3
|
+
module Timer
|
4
|
+
# lifted from seattlerb/minitest
|
5
|
+
if defined? Process::CLOCK_MONOTONIC
|
6
|
+
def self.now
|
7
|
+
Process.clock_gettime Process::CLOCK_MONOTONIC
|
8
|
+
end
|
9
|
+
else
|
10
|
+
def self.now
|
11
|
+
Time.now
|
12
|
+
end
|
8
13
|
end
|
9
|
-
else
|
10
|
-
def self.now
|
11
|
-
Time.now
|
12
|
-
end
|
13
|
-
end
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
|
15
|
+
def self.since(t)
|
16
|
+
self.now - t
|
17
|
+
end
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
19
|
+
def self.elapsed(&work)
|
20
|
+
t = self.now
|
21
|
+
return yield, self.since(t)
|
22
|
+
end
|
23
23
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
24
|
+
def self.loop_avg(count: 999, seconds: 1, &work)
|
25
|
+
i = 0
|
26
|
+
start = self.now
|
27
|
+
val = nil
|
28
|
+
loop {
|
29
|
+
val = yield
|
30
|
+
i += 1
|
31
|
+
break if i >= count
|
32
|
+
break if self.since(start) > seconds
|
33
|
+
}
|
34
|
+
return val, self.since(start) / i.to_f
|
35
|
+
end
|
35
36
|
end
|
36
37
|
end
|
data/lib/compsci/tree.rb
CHANGED
@@ -1,67 +1,11 @@
|
|
1
|
-
require 'compsci'
|
1
|
+
require 'compsci/node'
|
2
2
|
|
3
3
|
module CompSci
|
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
|
13
|
-
|
14
|
-
def add_child(node)
|
15
|
-
@children << node
|
16
|
-
end
|
17
|
-
|
18
|
-
def new_child(value)
|
19
|
-
self.add_child self.class.new(value)
|
20
|
-
end
|
21
|
-
|
22
|
-
def add_parent(node)
|
23
|
-
node.add_child(self)
|
24
|
-
end
|
25
|
-
|
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
4
|
class Tree
|
61
5
|
attr_reader :root
|
62
6
|
|
63
|
-
def initialize(
|
64
|
-
@root =
|
7
|
+
def initialize(node_class, val)
|
8
|
+
@root = node_class.new val
|
65
9
|
end
|
66
10
|
|
67
11
|
def df_search(node: nil, &blk)
|
@@ -96,9 +40,8 @@ module CompSci
|
|
96
40
|
class NaryTree < Tree
|
97
41
|
attr_reader :child_slots
|
98
42
|
|
99
|
-
def initialize(
|
100
|
-
super(
|
101
|
-
raise "#{klass}#parent required" unless @root.respond_to? :parent
|
43
|
+
def initialize(node_class, val, child_slots:)
|
44
|
+
super(node_class, val)
|
102
45
|
@child_slots = child_slots
|
103
46
|
end
|
104
47
|
|
@@ -106,35 +49,58 @@ module CompSci
|
|
106
49
|
node.children.size < @child_slots
|
107
50
|
end
|
108
51
|
|
52
|
+
def open_sibling
|
53
|
+
# try siblings first, only possible with Node#parent
|
54
|
+
if @open_parent.respond_to?(:siblings)
|
55
|
+
@open_parent.siblings.find { |s| self.open_parent?(s) }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
109
59
|
def open_parent
|
110
60
|
@open_parent ||= @root
|
111
61
|
return @open_parent if self.open_parent?(@open_parent)
|
112
|
-
|
113
|
-
|
114
|
-
|
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
|
-
}
|
120
|
-
end
|
62
|
+
open_sibling = self.open_sibling
|
63
|
+
return @open_parent = open_sibling if open_sibling
|
121
64
|
@open_parent = self.bf_search { |n| self.open_parent?(n) }
|
122
65
|
end
|
123
66
|
|
124
67
|
def push(value)
|
125
68
|
self.open_parent.new_child value
|
126
69
|
end
|
70
|
+
|
71
|
+
def display(width: nil)
|
72
|
+
str = ''
|
73
|
+
old_level = 0
|
74
|
+
width ||= @child_slots * 40
|
75
|
+
self.bf_search { |node|
|
76
|
+
raise "#{node.class} not yet supported" unless node.respond_to? :gen
|
77
|
+
level = node.gen
|
78
|
+
if old_level != level
|
79
|
+
str += "\n"
|
80
|
+
old_level = level
|
81
|
+
end
|
82
|
+
# center in block_width
|
83
|
+
slots = @child_slots**level
|
84
|
+
block_width = width / slots
|
85
|
+
val = node.to_s
|
86
|
+
space = [(block_width + val.size) / 2, val.size + 1].max
|
87
|
+
str += val.ljust(space, ' ').rjust(block_width, ' ')
|
88
|
+
false
|
89
|
+
}
|
90
|
+
str
|
91
|
+
end
|
92
|
+
alias_method :to_s, :display
|
127
93
|
end
|
128
94
|
|
129
95
|
class BinaryTree < NaryTree
|
130
|
-
def initialize(
|
131
|
-
super(
|
96
|
+
def initialize(node_class, val)
|
97
|
+
super(node_class, val, child_slots: 2)
|
132
98
|
end
|
133
99
|
|
134
|
-
def
|
100
|
+
def display(width: 80)
|
135
101
|
count = 0
|
136
102
|
str = ''
|
137
|
-
self.bf_search
|
103
|
+
self.bf_search { |n|
|
138
104
|
count += 1
|
139
105
|
level = Math.log(count, 2).floor
|
140
106
|
block_width = width / (2**level)
|
@@ -144,74 +110,18 @@ module CompSci
|
|
144
110
|
}
|
145
111
|
str
|
146
112
|
end
|
113
|
+
alias_method :to_s, :display
|
147
114
|
end
|
148
115
|
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
# Likewise, we should be able to use an array for N children
|
153
|
-
#
|
154
|
-
class CompleteNaryTree
|
155
|
-
def self.parent_idx(idx, n)
|
156
|
-
(idx-1) / n
|
157
|
-
end
|
158
|
-
|
159
|
-
def self.children_idx(idx, n)
|
160
|
-
Array.new(n) { |i| n*idx + i + 1 }
|
161
|
-
end
|
162
|
-
|
163
|
-
attr_reader :store
|
164
|
-
|
165
|
-
def initialize(store: [], child_slots: 2)
|
166
|
-
@store = store
|
167
|
-
@child_slots = child_slots
|
168
|
-
end
|
169
|
-
|
170
|
-
def push node
|
171
|
-
@store.push node
|
172
|
-
end
|
173
|
-
|
174
|
-
def pop
|
175
|
-
@store.pop
|
176
|
-
end
|
177
|
-
|
178
|
-
def size
|
179
|
-
@store.size
|
180
|
-
end
|
181
|
-
|
182
|
-
def last_idx
|
183
|
-
@store.size - 1 unless @store.empty?
|
116
|
+
class TernaryTree < NaryTree
|
117
|
+
def initialize(node_class, val)
|
118
|
+
super(node_class, val, child_slots: 3)
|
184
119
|
end
|
185
120
|
end
|
186
121
|
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
# integer math says idx 2 and idx 1 both have parent at idx 0
|
191
|
-
def self.parent_idx(idx)
|
192
|
-
(idx-1) / 2
|
193
|
-
end
|
194
|
-
|
195
|
-
def self.children_idx(idx)
|
196
|
-
[2*idx + 1, 2*idx + 2]
|
197
|
-
end
|
198
|
-
|
199
|
-
attr_reader :store
|
200
|
-
|
201
|
-
def initialize(store: [])
|
202
|
-
super(store: store, child_slots: 2)
|
203
|
-
end
|
204
|
-
|
205
|
-
# TODO: generalize for N != 2
|
206
|
-
def to_s(node: nil, width: 80)
|
207
|
-
str = ''
|
208
|
-
@store.each_with_index { |n, i|
|
209
|
-
level = Math.log(i+1, 2).floor
|
210
|
-
block_width = width / (2**level)
|
211
|
-
str += "\n" if 2**level == i+1 and i > 0
|
212
|
-
str += n.to_s.ljust(block_width / 2, ' ').rjust(block_width, ' ')
|
213
|
-
}
|
214
|
-
str
|
122
|
+
class QuaternaryTree < NaryTree
|
123
|
+
def initialize(node_class, val)
|
124
|
+
super(node_class, val, child_slots: 4)
|
215
125
|
end
|
216
126
|
end
|
217
127
|
end
|