compsci 0.3.0.1 → 0.3.1.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 +5 -5
- data/README.md +137 -78
- data/Rakefile +3 -1
- data/VERSION +1 -1
- data/compsci.gemspec +2 -2
- data/examples/binary_search_tree.rb +43 -8
- data/examples/complete_tree.rb +21 -22
- data/examples/flex_node.rb +117 -0
- data/examples/heap.rb +40 -2
- data/examples/heap_push.rb +7 -1
- data/examples/ternary_search_tree.rb +30 -0
- data/examples/tree.rb +27 -25
- data/lib/compsci.rb +10 -1
- data/lib/compsci/complete_tree.rb +6 -7
- data/lib/compsci/flex_node.rb +90 -0
- data/lib/compsci/heap.rb +1 -2
- data/lib/compsci/names/pokemon.rb +62 -0
- data/lib/compsci/node.rb +109 -59
- data/lib/compsci/simplex.rb +2 -4
- data/lib/compsci/simplex/parse.rb +4 -4
- data/test/bench/fibonacci.rb +29 -128
- data/test/bench/flex_node.rb +30 -0
- data/test/complete_tree.rb +16 -14
- data/test/compsci.rb +25 -0
- data/test/fibonacci.rb +6 -5
- data/test/fit.rb +30 -26
- data/test/flex_node.rb +226 -0
- data/test/heap.rb +46 -46
- data/test/names.rb +95 -56
- data/test/node.rb +177 -85
- data/test/simplex_parse.rb +23 -15
- data/test/timer.rb +10 -10
- metadata +16 -16
- data/examples/tree_push.rb +0 -72
- data/lib/compsci/binary_search_tree.rb +0 -86
- data/lib/compsci/tree.rb +0 -142
- data/test/bench/tree.rb +0 -31
- data/test/binary_search_tree.rb +0 -98
- data/test/tree.rb +0 -200
data/examples/heap.rb
CHANGED
@@ -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
|
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
|
data/examples/heap_push.rb
CHANGED
@@ -6,12 +6,18 @@ include CompSci
|
|
6
6
|
runtime = (ARGV.shift || "3").to_i
|
7
7
|
|
8
8
|
puts <<EOF
|
9
|
+
|
9
10
|
#
|
10
|
-
#
|
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
|
+
}
|
data/examples/tree.rb
CHANGED
@@ -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
|
-
#
|
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
|
-
[
|
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
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
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
|
}
|
data/lib/compsci.rb
CHANGED
@@ -1 +1,10 @@
|
|
1
|
-
module CompSci
|
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
|
2
|
+
# A CompleteTree can very efficiently use an array for storage using
|
3
3
|
# simple arithmetic to determine parent child relationships.
|
4
4
|
#
|
5
|
-
|
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 <
|
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 <
|
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 <
|
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
|
data/lib/compsci/heap.rb
CHANGED
@@ -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::
|
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
|
data/lib/compsci/node.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
10
|
-
|
11
|
-
else
|
12
|
-
@children = children
|
13
|
-
end
|
14
|
-
# @metadata = {}
|
18
|
+
@children = Array.new(children)
|
19
|
+
@metadata = metadata
|
15
20
|
end
|
16
21
|
|
17
|
-
def
|
18
|
-
@
|
22
|
+
def [](idx)
|
23
|
+
@children[idx]
|
19
24
|
end
|
20
25
|
|
21
|
-
|
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
|
-
|
31
|
-
|
32
|
-
|
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
|
-
|
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
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
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
|
-
|
60
|
-
|
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
|
-
|
64
|
-
|
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
|
-
|
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
|