compsci 0.3.0.1 → 0.3.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,11 +1,11 @@
1
1
  require 'compsci/heap'
2
- require 'compsci/timer'
3
2
 
4
3
  include CompSci
5
4
 
6
5
  puts <<EOF
6
+
7
7
  #
8
- # display the results of TernaryHeap push and pop
8
+ # display the results of ternary Heap push and pop
9
9
  #
10
10
 
11
11
  EOF
@@ -38,3 +38,41 @@ puts "array: #{h.array.inspect}"
38
38
  puts "heap: #{h.heap?}"
39
39
  puts h
40
40
  puts
41
+
42
+
43
+ puts <<EOF
44
+
45
+ #
46
+ # display the results of binary Heap push and pop
47
+ #
48
+
49
+ EOF
50
+
51
+ h = Heap.new(child_slots: 2)
52
+
53
+ puts "push: %s" % Array.new(30) { rand(99).tap { |i| h.push i } }.join(' ')
54
+ puts "array: #{h.array.inspect}"
55
+ puts "heap: #{h.heap?}"
56
+ puts h
57
+ puts
58
+ puts
59
+
60
+ puts "pop: %i" % h.pop
61
+ puts "array: #{h.array.inspect}"
62
+ puts "heap: #{h.heap?}"
63
+ puts h
64
+ puts
65
+ puts
66
+
67
+ puts "pop: %s" % Array.new(9) { h.pop }.join(' ')
68
+ puts "array: #{h.array.inspect}"
69
+ puts "heap: #{h.heap?}"
70
+ puts h
71
+ puts
72
+ puts
73
+
74
+ puts "push: %s" % Array.new(30) { rand(99).tap { |i| h.push i } }.join(' ')
75
+ puts "array: #{h.array.inspect}"
76
+ puts "heap: #{h.heap?}"
77
+ puts h
78
+ puts
@@ -6,12 +6,18 @@ include CompSci
6
6
  runtime = (ARGV.shift || "3").to_i
7
7
 
8
8
  puts <<EOF
9
+
9
10
  #
10
- # 3 seconds worth of pushes
11
+ # #{runtime} seconds worth of Heap pushes
11
12
  #
12
13
 
13
14
  EOF
14
15
 
16
+
17
+ # pregenerate a sequence of random numbers
18
+ # every NUMBERWANGth request, shift the sequence and push a new random
19
+ # this should mitigate random number generation from interfering with timing
20
+ # while also mitigating any chance of cyclic behavior
15
21
  RANDMAX = 1_000
16
22
  NUMBERWANG = 1_000
17
23
  NUMS = (0..(RANDMAX - 1)).to_a.shuffle
@@ -0,0 +1,30 @@
1
+ require 'compsci/node'
2
+ require 'compsci/names'
3
+
4
+ include CompSci
5
+
6
+ randmax = (ARGV.shift || 50).to_i
7
+
8
+ puts <<EOF
9
+
10
+ #
11
+ # Insert #{randmax} nodes into a ternary search tree (random keys)
12
+ #
13
+
14
+ EOF
15
+
16
+ root = KeyNode.new(rand(randmax), key: rand(randmax), children: 3)
17
+ randmax.times { puts root.insert(rand(randmax), rand(randmax)) }
18
+ puts root.display
19
+
20
+ puts <<EOF
21
+
22
+ #
23
+ # Search for #{randmax} keys in order
24
+ #
25
+
26
+ EOF
27
+
28
+ randmax.times { |key|
29
+ puts "search #{key}: #{root.search(key).map { |n| n.value }.join(' ')}"
30
+ }
@@ -1,42 +1,44 @@
1
1
  require 'compsci/node'
2
- require 'compsci/tree'
3
- require 'compsci/timer'
4
2
 
5
3
  include CompSci
6
4
 
7
5
  puts <<EOF
6
+
8
7
  #
9
- # Try out Binary-, Ternary-, and QuaternaryTree
8
+ # Fill up and display some trees
10
9
  #
11
10
 
12
11
  EOF
13
12
 
14
13
  vals = Array.new(30) { rand 99 }
14
+ puts "vals: #{vals.inspect}"
15
15
 
16
- [BinaryTree, TernaryTree, QuaternaryTree].each { |tree_class|
17
- # start with the same vals for each class
16
+ [2, 3, 4].each { |children|
18
17
  my_vals = vals.dup
19
- p my_vals
20
- tree = tree_class.new(ChildFlexNode, my_vals.shift)
21
- tree.push my_vals.shift until my_vals.empty?
22
- p tree
23
- puts tree.display(width: 80)
24
- puts
25
- visited = []
26
- tree.df_search { |n|
27
- visited << n
28
- false # or n.value > 90
29
- }
30
- puts "df_search visited: %s" % visited.join(' ')
31
- puts
32
- puts
33
18
 
34
- # push different vals for each class
35
- my_vals = Array.new(30) { rand 99 }
36
- puts "push: #{my_vals.inspect}"
37
- tree.push my_vals.shift until my_vals.empty?
38
- puts
39
- puts tree.display(width: 80)
19
+ puts <<EOF
20
+
21
+ #
22
+ # Children: #{children}
23
+ #
24
+
25
+ EOF
26
+
27
+
28
+ root = Node.new my_vals.shift, children: children
29
+ nodes = [root]
30
+ until my_vals.empty?
31
+ new_nodes = []
32
+ nodes.each { |node|
33
+ children.times { |i|
34
+ node[i] = Node.new my_vals.shift, children: children
35
+ }
36
+ new_nodes += node.children
37
+ }
38
+ nodes = new_nodes
39
+ end
40
+ p root
40
41
  puts
42
+ puts root.display(width: 80)
41
43
  puts
42
44
  }
@@ -1 +1,10 @@
1
- module CompSci; end
1
+ module CompSci
2
+ # thanks apeiros
3
+ # https://gist.github.com/rickhull/d0b579aa08c85430b0dc82a791ff12d6
4
+ def self.power_of?(num, base)
5
+ return false if base <= 1
6
+ mod = 0
7
+ num, mod = num.divmod(base) until num == 1 || mod > 0
8
+ mod == 0
9
+ end
10
+ end
@@ -1,10 +1,8 @@
1
1
  module CompSci
2
- # A CompleteNaryTree can very efficiently use an array for storage using
2
+ # A CompleteTree can very efficiently use an array for storage using
3
3
  # simple arithmetic to determine parent child relationships.
4
4
  #
5
- # It is kept separate from compsci/tree as it does not require compsci/node
6
- #
7
- class CompleteNaryTree
5
+ class CompleteTree
8
6
  # integer math maps several children to one parent
9
7
  def self.parent_idx(idx, n)
10
8
  (idx-1) / n
@@ -67,6 +65,7 @@ module CompSci
67
65
  puts "not yet"
68
66
  end
69
67
 
68
+ # TODO: fixme
70
69
  def display(width: 80)
71
70
  str = ''
72
71
  old_level = 0
@@ -89,19 +88,19 @@ module CompSci
89
88
  alias_method :to_s, :display
90
89
  end
91
90
 
92
- class CompleteBinaryTree < CompleteNaryTree
91
+ class CompleteBinaryTree < CompleteTree
93
92
  def initialize(array: [])
94
93
  super(array: array, child_slots: 2)
95
94
  end
96
95
  end
97
96
 
98
- class CompleteTernaryTree < CompleteNaryTree
97
+ class CompleteTernaryTree < CompleteTree
99
98
  def initialize(array: [])
100
99
  super(array: array, child_slots: 3)
101
100
  end
102
101
  end
103
102
 
104
- class CompleteQuaternaryTree < CompleteNaryTree
103
+ class CompleteQuaternaryTree < CompleteTree
105
104
  def initialize(array: [])
106
105
  super(array: array, child_slots: 4)
107
106
  end
@@ -0,0 +1,90 @@
1
+ require 'compsci/node'
2
+
3
+ module CompSci
4
+ #
5
+ # FlexNodes accumulate children; no child gaps
6
+ #
7
+
8
+ # FlexNode API is #add_child, #add_parent, #new_child
9
+
10
+ class FlexNode < Node
11
+ def initialize(value, metadata: {})
12
+ @value = value
13
+ @children = []
14
+ @metadata = metadata
15
+ end
16
+
17
+ def add_child(node)
18
+ @children << node
19
+ end
20
+
21
+ def new_child(value)
22
+ self.add_child self.class.new(value)
23
+ end
24
+
25
+ def add_parent(node)
26
+ node.add_child(self)
27
+ end
28
+
29
+ def df_search(&blk)
30
+ return self if yield self
31
+ stop_node = nil
32
+ @children.each { |child|
33
+ stop_node = child.df_search(&blk)
34
+ break if stop_node
35
+ }
36
+ stop_node
37
+ end
38
+
39
+ def bf_search(&blk)
40
+ destinations = [self]
41
+ while !destinations.empty?
42
+ node = destinations.shift
43
+ return node if yield node
44
+ destinations += node.children
45
+ end
46
+ nil
47
+ end
48
+
49
+ def open_parent?(child_slots)
50
+ @children.size < child_slots
51
+ end
52
+
53
+ def open_parent(child_slots)
54
+ self.bf_search { |n| n.open_parent?(child_slots) }
55
+ end
56
+
57
+ def push(value, child_slots)
58
+ self.open_parent(child_slots).new_child value
59
+ end
60
+ end
61
+
62
+ class ChildFlexNode < FlexNode
63
+ attr_accessor :parent
64
+
65
+ def initialize(value, metadata: {})
66
+ super(value, metadata: metadata)
67
+ @parent = nil
68
+ end
69
+
70
+ # O(log n) recursive
71
+ def gen
72
+ @parent ? @parent.gen + 1 : 0
73
+ end
74
+
75
+ def siblings
76
+ @parent ? @parent.children : []
77
+ end
78
+
79
+ def add_child(node)
80
+ node.parent ||= self
81
+ raise "node has a parent: #{node.parent}" if node.parent != self
82
+ @children << node
83
+ end
84
+
85
+ def add_parent(node)
86
+ @parent = node
87
+ @parent.add_child(self)
88
+ end
89
+ end
90
+ end
@@ -18,7 +18,7 @@ require 'compsci/complete_tree'
18
18
  # swap nodes at each layer of the tree, and there are log(n, base b) layers
19
19
  # to the tree.
20
20
  #
21
- class CompSci::Heap < CompSci::CompleteNaryTree
21
+ class CompSci::Heap < CompSci::CompleteTree
22
22
  # * defaults to a MaxHeap, with the largest node at the root
23
23
  # * specify a minheap with minheap: true or cmp_val: -1
24
24
  #
@@ -65,7 +65,6 @@ class CompSci::Heap < CompSci::CompleteNaryTree
65
65
  #
66
66
  def sift_up(idx)
67
67
  return self if idx <= 0
68
- # print '.'
69
68
  pidx = self.class.parent_idx(idx, @child_slots)
70
69
  if !self.heapish?(pidx, idx)
71
70
  @array[idx], @array[pidx] = @array[pidx], @array[idx] # swap
@@ -0,0 +1,62 @@
1
+ require 'compsci/names'
2
+
3
+ module CompSci::Names::Pokemon
4
+ DATAFILE = File.join(__dir__, 'pokemon.txt')
5
+
6
+ def self.array
7
+ @@array ||= self.read_file
8
+ @@array
9
+ end
10
+
11
+ def self.hash
12
+ @@hash ||= self.read_hash
13
+ @@hash
14
+ end
15
+
16
+ # an array of all the names
17
+ def self.read_file(path: DATAFILE)
18
+ File.readlines(path).map { |n| n.chomp }
19
+ end
20
+
21
+ # return an array, possibly empty, if all: true
22
+ # return a string, possibly nil, if all: false
23
+ def self.grep(rgx, path: DATAFILE, all: false)
24
+ ary = []
25
+ File.open(path).each_line { |l|
26
+ if l.match rgx
27
+ ary << l.chomp
28
+ break unless all
29
+ end
30
+ }
31
+ all ? ary : ary.first
32
+ end
33
+
34
+ # a hash of all the names, keyed by the first letter
35
+ def self.read_hash(path: DATAFILE)
36
+ hsh = Hash.new { |h, k| h[k] = [] }
37
+ File.open(path).each_line { |l|
38
+ hsh[l[0]] << l.chomp
39
+ }
40
+ hsh
41
+ end
42
+
43
+ # convert 0-25 to a lowercase alpha
44
+ def self.key(val)
45
+ if val.is_a?(String)
46
+ if val.match(/^[0-9]/)
47
+ val = val[0..1].to_i
48
+ elsif val.match(/^[a-z]/i)
49
+ return val.downcase[0]
50
+ else
51
+ raise(ArgumentError, "can't handle #{val}")
52
+ end
53
+ end
54
+ CompSci::Names::ENGLISH_LOWER[val.to_i]
55
+ end
56
+
57
+ # return a pokemon sampled from those keyed by val
58
+ def self.sample(val)
59
+ ary = self.hash[self.key(val)]
60
+ ary.sample if ary
61
+ end
62
+ end
@@ -1,67 +1,141 @@
1
1
  module CompSci
2
2
  # has a value and an array of children; allows child gaps
3
3
  class Node
4
- attr_accessor :value
4
+ def self.display_line(nodes: [], width: 80)
5
+ block_width = [width / nodes.size, 1].max
6
+ nodes.map { |node|
7
+ str = node ? node.to_s : '_'
8
+ space = [(block_width + str.size) / 2, str.size + 1].max
9
+ str.ljust(space, ' ').rjust(block_width, ' ')
10
+ }.join
11
+ end
12
+
13
+ attr_accessor :value, :metadata
5
14
  attr_reader :children
6
15
 
7
- def initialize(value, children: [])
16
+ def initialize(value, children: 2, metadata: {})
8
17
  @value = value
9
- if children.is_a?(Integer)
10
- @children = Array.new(children)
11
- else
12
- @children = children
13
- end
14
- # @metadata = {}
18
+ @children = Array.new(children)
19
+ @metadata = metadata
15
20
  end
16
21
 
17
- def to_s
18
- @value.to_s
22
+ def [](idx)
23
+ @children[idx]
19
24
  end
20
25
 
21
- # This could be done directly with self.children, but #set_child is part
22
- # of the Node API.
23
- def set_child(idx, node)
26
+ def []=(idx, node)
24
27
  @children[idx] = node
25
28
  end
26
29
 
30
+ def to_s
31
+ @value.to_s
32
+ end
33
+
27
34
  def inspect
28
35
  "#<%s:0x%0xi @value=%s @children=[%s]>" %
29
- [self.class,
30
- self.object_id,
31
- self.to_s,
32
- @children.map(&:to_s).join(', ')]
36
+ [self.class, self.object_id, self, @children.join(', ')]
37
+ end
38
+
39
+ def display(width: 80)
40
+ lines = [self.class.display_line(nodes: [self], width: width)]
41
+ nodes = @children
42
+ while nodes.any? { |n| !n.nil? }
43
+ lines << self.class.display_line(nodes: nodes, width: width)
44
+ if nodes.size > 3**7
45
+ lines << "nodes.size = #{nodes.size}; abort render"
46
+ break
47
+ end
48
+ nodes = nodes.reduce([]) { |memo, n|
49
+ memo + Array.new(@children.size) { |i| n and n.children[i] }
50
+ }
51
+ end
52
+ lines.join("\n")
33
53
  end
34
54
  end
35
55
 
56
+ # TODO: implement key with @metadata !?!?!?!?
57
+
36
58
  # adds a key to Node; often the key is used to place the node in the
37
59
  # tree, independent of the value; e.g. key=priority, value=process_id
38
60
  class KeyNode < Node
39
- attr_accessor :key
61
+ class DuplicateKey < RuntimeError; end
62
+ class SearchError < RuntimeError; end
63
+
64
+ attr_reader :key, :duplicates
65
+
66
+ def self.key_cmp_idx(new_key, key, child_slots: 2, duplicates: false)
67
+ if child_slots < 2
68
+ raise(ArgumentError, "child_slots: #{child_slots} too small")
69
+ elsif child_slots == 2
70
+ raise(DuplicateKey, "#{new_key}") if new_key == key and !duplicates
71
+ new_key < key ? 0 : 1
72
+ elsif child_slots == 3
73
+ (new_key <=> key) + 1
74
+ else
75
+ raise(ArgumentError: "child_slots: #{child_slots} too big")
76
+ end
77
+ end
40
78
 
41
- def initialize(val, key: nil, children: [])
42
- @key = key
79
+ def initialize(val, key: nil, children: 2, duplicates: false)
43
80
  super(val, children: children)
81
+ @key, @duplicates = key, duplicates
44
82
  end
45
83
 
46
84
  def to_s
47
- [key, value].join(':')
85
+ [@key, @value].join(':')
86
+ end
87
+
88
+ # which child idx should handle key?
89
+ def cidx(key)
90
+ self.class.key_cmp_idx(key, @key,
91
+ child_slots: @children.size,
92
+ duplicates: @duplicates)
48
93
  end
49
- end
50
94
 
51
- # accumulate children; no child gaps
52
- class FlexNode < Node
53
- # These methods look like convenience methods, but they provide the
54
- # FlexNode interface also used by ChildFlexNode
55
- def add_child(node)
56
- @children << node
95
+ # works for 2 or 3 children
96
+ def insert(key, val)
97
+ idx = self.cidx(key)
98
+ if @children[idx]
99
+ @children[idx].insert(key, val)
100
+ else
101
+ @children[idx] = self.class.new(val, key: key,
102
+ children: @children.size,
103
+ duplicates: @duplicates)
104
+ end
57
105
  end
58
106
 
59
- def new_child(value)
60
- self.add_child self.class.new(value)
107
+ # returns a single node for binary search
108
+ # returns multiple nodes for ternary search
109
+ def search(key)
110
+ if @children.size == 2
111
+ self.search2(key)
112
+ elsif @children.size == 3
113
+ self.search3(key)
114
+ else
115
+ raise(SearchError, "can't search for @children.size children")
116
+ end
61
117
  end
62
118
 
63
- def add_parent(node)
64
- node.add_child(self)
119
+ # returns a single node or nil
120
+ def search2(key)
121
+ return self if key == @key
122
+ child = @children[self.cidx(key)]
123
+ child.search(key) if child
124
+ end
125
+
126
+ # returns an array of nodes, possibly empty
127
+ def search3(key)
128
+ if key == @key
129
+ nodes = [self]
130
+ node = @children[1]
131
+ while node
132
+ nodes << node
133
+ node = node.children[1]
134
+ end
135
+ return nodes
136
+ end
137
+ child = @children[self.cidx(key)]
138
+ child ? child.search(key) : []
65
139
  end
66
140
  end
67
141
 
@@ -69,7 +143,7 @@ module CompSci
69
143
  class ChildNode < Node
70
144
  attr_accessor :parent
71
145
 
72
- def initialize(value, children: [])
146
+ def initialize(value, children: 2)
73
147
  @parent = nil
74
148
  super(value, children: children)
75
149
  end
@@ -83,35 +157,11 @@ module CompSci
83
157
  @parent ? @parent.children : []
84
158
  end
85
159
 
86
- def set_child(idx, node)
160
+ # update both sides of the relationship
161
+ def []=(idx, node)
87
162
  node.parent ||= self
88
163
  raise "node has a parent: #{node.parent}" if node.parent != self
89
164
  @children[idx] = node
90
165
  end
91
-
92
- def set_parent(idx, node)
93
- @parent = node
94
- @parent.set_child(idx, self)
95
- end
96
- end
97
-
98
- # ChildNode which accumulates children with no gaps
99
- # It meets the FlexNode API but does not inherit from FlexNode since it
100
- # needs to reimplement each method; instead get parent stuff from ChildNode
101
- class ChildFlexNode < ChildNode
102
- def add_child(node)
103
- node.parent ||= self
104
- raise "node has a parent: #{node.parent}" if node.parent != self
105
- @children << node
106
- end
107
-
108
- def new_child(value)
109
- self.add_child self.class.new(value)
110
- end
111
-
112
- def add_parent(node)
113
- @parent = node
114
- @parent.add_child(self)
115
- end
116
166
  end
117
167
  end