compsci 0.0.3.1 → 0.1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -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
@@ -1,36 +1,37 @@
1
- require 'compsci'
1
+ module CompSci
2
2
 
3
- module CompSci::Timer
4
- # lifted from seattlerb/minitest
5
- if defined? Process::CLOCK_MONOTONIC
6
- def self.now
7
- Process.clock_gettime Process::CLOCK_MONOTONIC
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
- def self.since(t)
16
- self.now - t
17
- end
15
+ def self.since(t)
16
+ self.now - t
17
+ end
18
18
 
19
- def self.elapsed(&work)
20
- t = self.now
21
- return yield, self.since(t)
22
- end
19
+ def self.elapsed(&work)
20
+ t = self.now
21
+ return yield, self.since(t)
22
+ end
23
23
 
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
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
@@ -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(klass, val)
64
- @root = klass.new val
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(klass, val, child_slots:)
100
- super(klass, val)
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
- # TODO: ugh, there must be a better way, this is O(n)
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(klass, val)
131
- super(klass, val, child_slots: 2)
96
+ def initialize(node_class, val)
97
+ super(node_class, val, child_slots: 2)
132
98
  end
133
99
 
134
- def to_s(node: nil, width: 80)
100
+ def display(width: 80)
135
101
  count = 0
136
102
  str = ''
137
- self.bf_search(node: node) { |n|
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
- # A CompleteBinaryTree can very efficiently use an array for storage using
150
- # simple arithmetic to determine parent child relationships.
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
- # Heap still expects CompleteBinaryTree
188
- #
189
- class CompleteBinaryTree < CompleteNaryTree
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