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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 6b9d3fa913e9dc0f7aa9f8711e9aa231c00d1022
4
- data.tar.gz: 8436157b3364c791cee0471ae74ca3817f0f4dde
2
+ SHA256:
3
+ metadata.gz: b1ad9a59954ea3377e0e03992dd660fabaf7c65389d633f1e607d3a5b6731ebd
4
+ data.tar.gz: 5b893649cae5840c590bf4998429087cf50ad9dc97169a2c3ee1566c86748196
5
5
  SHA512:
6
- metadata.gz: 1670fd7283f147996dea2ed2a7a190797a33a5b22b60e8ccfa98a5dc48d61aa6a43302badbc7776b6b7c5a7171a32923ed2dc95f88c23ca998bd1d023d3f4f72
7
- data.tar.gz: f30dd40fb2e5f9e04db6044d7801f1776fb0e1214fd23c29bfbef028dbcd87cab612e434e4df43da477012975561cb7eeef6dab867a53e479612893c53a9e086
6
+ metadata.gz: 64100e3719380956c844e7f9eb3949eae097a6d9426b039d0d224260d4bd18a1e1dd7ad7f147643203e21b3ba95d0109bed198d8e5421c9e3c6c21312665b426
7
+ data.tar.gz: c8944161d1ee4b4df7afac5d27bc0801b2953893ea45db03c30ec1e9f5ee74764638464ee1c3d4d3707107370dd547eedb1f62d09cfe9f20f2efb399e462258f
data/README.md CHANGED
@@ -1,88 +1,107 @@
1
1
  [![Build Status](https://travis-ci.org/rickhull/compsci.svg?branch=master)](https://travis-ci.org/rickhull/compsci)
2
+ [![Gem Version](https://badge.fury.io/rb/compsci.svg)](https://badge.fury.io/rb/compsci)
3
+ [![Dependency Status](https://gemnasium.com/rickhull/compsci.svg)](https://gemnasium.com/rickhull/compsci)
4
+ [![Security Status](https://hakiri.io/github/rickhull/compsci/master.svg)](https://hakiri.io/github/rickhull/compsci/master)
2
5
 
3
6
  # CompSci
4
7
 
5
8
  Provided are some toy implementations for some basic computer science problems.
6
9
 
7
- ## [`Node`](lib/compsci/node.rb) data structure
10
+ ## [`Node`](lib/compsci/node.rb) classes
11
+
12
+ ### `Node`
13
+
14
+ A *Node* provides a tree structure by assigning other *Nodes* to `@children`.
8
15
 
9
16
  * `Node`
10
17
  - `@value`
11
18
  - `@children`
12
- - `#set_child(idx, node)`
13
- * `KeyNode` inherits from `Node`; adds a key
14
- - `@key`
15
- * `FlexNode` inherits from `Node`; accumulates children
16
- - `#add_child(node)`
17
- - `#new_child(value)`
18
- - `#add_parent(node)`
19
- * `ChildNode` inherits from `Node`; adds a parent
19
+ - `#[]` - child node at index
20
+ - `#[]=` - set child node at index
21
+ - `display` - display full tree with all descendents
22
+
23
+ * [examples/tree.rb](examples/tree.rb)
24
+ * [test/node.rb](test/node.rb#L7)
25
+
26
+ ### `KeyNode`
27
+
28
+ A *KeyNode* adds `@key` and allows a comparison based search on the key.
29
+ [Binary search trees](https://en.wikipedia.org/wiki/Binary_search_tree)
30
+ are supported, and the `@duplicated` flag determines whether duplicate keys
31
+ are allowed to be inserted. Any duplicates will not be returned from
32
+ `#search`. A Ternary search tree is also supported, and it inherently allows
33
+ duplicates keys. `#search` returns an array of `KeyNode`, possibly empty.
34
+
35
+ * `KeyNode < Node`
36
+ - `KeyNode.key_cmp_idx` - compare 2 keys to decide on a child slot
37
+ - `@key` - any *Comparable*
38
+ - `@duplicates` - boolean flag relevant for @children.size == 2
39
+ - `#cidx` - calls `KeyNode.key_cmp_idx`
40
+ - `#insert`
41
+ - `#search`
42
+
43
+ * [examples/binary_search_tree.rb](examples/binary_search_tree.rb)
44
+ * [examples/ternary_search_tree.rb](examples/ternary_search_tree.rb)
45
+ * [test/node.rb](test/node.rb#L43)
46
+
47
+ ### `ChildNode`
48
+
49
+ A *ChildNode* adds reference to its `@parent`.
50
+
51
+ * `ChildNode < Node`
20
52
  - `@parent`
21
53
  - `#gen`
22
54
  - `#siblings`
23
- * `ChildFlexNode` inherits from `ChildNode`; accumulates children
24
- - `#add_child(node)`
25
- - `#new_child(value)`
26
- - `#add_parent(node)`
27
-
28
- ## [`Tree`](lib/compsci/tree.rb) data structures
29
-
30
- * `Tree`
31
- - `@root`
32
- - `#df_search`
33
- - `#bf_search`
34
- * `NaryTree`
35
- - `@child_slots` (number of children per node)
36
- - `#open_parent` O(n) to find a node with open child slots
37
- - `#push` append `#open_parent.children`
38
- - `#display` if initialized with `ChildNode`
39
- * `BinaryTree`
40
- - `NaryTree.new(child_slots: 2)`
41
- - `#display` for `Node` and `ChildNode`
42
- * `TernaryTree`
43
- - `NaryTree.new(child_slots: 3)`
44
- * `QuaternaryTree`
45
- - `NaryTree.new(child_slots: 4)`
46
-
47
- ## [`CompleteNaryTree`](lib/compsci/complete_tree.rb) data structure
48
-
49
- Efficient Array implementation of a complete tree.
50
-
51
- * `CompleteNaryTree`
52
- - `CompleteNaryTree.parent_idx`
53
- - `CompleteNaryTree.children_idx`
54
- - `CompleteNaryTree.gen`
55
+
56
+ * [test/node.rb](test/node.rb#L190)
57
+
58
+ ## [`CompleteTree`](lib/compsci/complete_tree.rb) classes
59
+
60
+ Efficient *Array* implementation of a complete tree uses arithmetic to
61
+ determine parent/child relationships.
62
+
63
+ * `CompleteTree`
64
+ - `CompleteTree.parent_idx`
65
+ - `CompleteTree.children_idx`
66
+ - `CompleteTree.gen`
55
67
  - `@array`
56
68
  - `@child_slots`
57
69
  - `#push`
58
70
  - `#pop`
59
71
  - `#size`
60
72
  - `#last_idx`
61
- - `#display` (alias `#to_s`)
62
- * `CompleteBinaryTree`
63
- - `CompleteNaryTree.new(child_slots: 2)`
64
- * `CompleteTernaryTree`
65
- - `CompleteNaryTree.new(child_slots: 3)`
66
- * `CompleteQuaternaryTree`
67
- - `CompleteNaryTree.new(child_slots: 4)`
68
-
69
- ## [`BinarySearchTree`](lib/compsci/binary_search_tree.rb) data structure
70
-
71
- Based on `BinaryTree` with `KeyNode`s. The position of a node depends on its
72
- key and how the key relates to the existing node keys.
73
-
74
- ## [`Heap`](lib/compsci/heap.rb) data structure
75
-
76
- `CompleteNaryTree` implementation. Both minheaps and maxheaps are supported.
77
- Any number of children may be provided via `child_slots`. The primary
78
- operations are `Heap#push` and `Heap#pop`. See the
79
- [heap](examples/heap.rb) [examples](examples/heap_push.rb)
80
- which can be executed (among other examples) via `rake examples`.
73
+ - `#display` - alias `#to_s`
74
+ * `CompleteBinaryTree < CompleteTree`
75
+ - `@child_slots = 2`
76
+ * `CompleteTernaryTree < CompleteTree`
77
+ - `@child_slots = 3`
78
+ * `CompleteQuaternaryTree < CompleteTree`
79
+ - `@child_slots = 4`
80
+
81
+ * [examples/complete_tree.rb](examples/complete_tree.rb)
82
+ * [test/complete_tree.rb](test/complete_tree.rb)
83
+ * [test/bench/complete_tree.rb](test/bench/complete_tree.rb)
84
+
85
+ ## [`Heap`](lib/compsci/heap.rb) class
86
+
87
+ *CompleteTree* implementation. Both *minheaps* and *maxheaps* are
88
+ supported. Any number of children may be provided via `child_slots`.
89
+ The primary operations are `Heap#push` and `Heap#pop`. My basic Vagrant VM
90
+ gets over [500k pushes per second](reports/examples#L533), constant up past
91
+ 1M pushes.
92
+
93
+ * `Heap < CompleteTree`
94
+ - `#push`
95
+ - `#pop`
96
+ - `#sift_up`
97
+ - `#sift_down`
81
98
 
82
- My basic Vagrant VM gets over [500k pushes per second, constant up past 1M
83
- pushes](reports/examples#L484).
99
+ * [examples/heap.rb](examples/heap.rb)
100
+ * [examples/heap_push.rb](examples/heap_push.rb)
101
+ * [test/heap.rb](test/heap.rb)
102
+ * [test/bench/heap.rb](test/bench/heap.rb)
84
103
 
85
- ## [`Fibonacci`](lib/compsci/fibonacci.rb) functions
104
+ ## [`Fibonacci`](lib/compsci/fibonacci.rb) module
86
105
 
87
106
  * `Fibonacci.classic(n)` - naive, recursive
88
107
  * `Fibonacci.cache_recursive(n)` - as above, caching already computed results
@@ -90,13 +109,22 @@ pushes](reports/examples#L484).
90
109
  * `Fibonacci.dynamic(n)` - as above but without a cache structure
91
110
  * `Fibonacci.matrix(n)` - matrix is magic; beats dynamic around n=500
92
111
 
93
- ## [`Timer`](/lib/compsci/timer.rb) functions
112
+ * [test/fibonacci.rb](test/fibonacci.rb)
113
+ * [test/bench/fibonacci.rb](test/bench/fibonacci.rb)
114
+
115
+ ## [`Timer`](/lib/compsci/timer.rb) module
94
116
 
95
117
  * `Timer.now` - uses `Process::CLOCK_MONOTONIC` if available
96
118
  * `Timer.since` - provides the elapsed time since a prior time
97
119
  * `Timer.elapsed` - provides the elapsed time to run a block
98
120
  * `Timer.loop_avg` - loops a block; returns final value and mean elapsed time
99
121
 
122
+ * [examples/heap_push.rb](examples/heap_push.rb)
123
+ * [test/timer.rb](test/timer.rb)
124
+ * [test/bench/complete_tree.rb](test/bench/complete_tree.rb)
125
+ * [test/bench/simplex.rb](test/bench/simplex.rb)
126
+ * [test/timer.rb](test/timer.rb)
127
+
100
128
  ```ruby
101
129
  require 'compsci/timer'
102
130
 
@@ -142,36 +170,67 @@ elapsed: 0.304
142
170
  cumulative: 0.828
143
171
  ```
144
172
 
145
- ## [`Fit`](lib/compsci/fit.rb) functions
173
+ ## [`Fit`](lib/compsci/fit.rb) module
146
174
 
147
175
  * `Fit.sigma` - sums the result of a block applied to array values
148
176
  * `Fit.error` - returns a generic r^2 value, the coefficient of determination
149
- * `Fit.constant` - fits `y = a + 0x`; returns the mean and variance
177
+ * `Fit.constant` - fits `y = a + 0x`; returns the mean and variance
150
178
  * `Fit.logarithmic` - fits `y = a + b*ln(x)`; returns a, b, r^2
151
- * `Fit.linear` - fits `y = a + bx`; returns a, b, r^2
152
- * `Fit.exponential` fits `y = ae^(bx)`; returns a, b, r^2
153
- * `Fit.power` fits `y = ax^b`; returns a, b, r^2
179
+ * `Fit.linear` - fits `y = a + bx`; returns a, b, r^2
180
+ * `Fit.exponential` - fits `y = ae^(bx)`; returns a, b, r^2
181
+ * `Fit.power` - fits `y = ax^b`; returns a, b, r^2
182
+ * `Fit.best` - applies known fits; returns the fit with highest r^2
183
+
184
+ * [test/bench/complete_tree.rb](test/bench/complete_tree.rb)
185
+ * [test/fit.rb](test/fit.rb)
186
+
187
+ ## [`Names`](lib/compsci/names.rb) module
154
188
 
155
- ## [`Names`](lib/compsci/names.rb) functions
189
+ This helps map a range of small integers to friendly names, typically in
190
+ alphabetical order.
156
191
 
192
+ * `ENGLISH_UPPER` `ENGLISH_LOWER` `WW1` `WW2` `NATO` `CRYPTO` `PLANETS` `SOLAR`
157
193
  * `Names.assign`
158
- * `Names::Greek.upper`
159
- * `Names::Greek.lower`
160
- * `Names::Greek.symbol`
194
+
195
+ * [examples/binary_search_tree.rb](examples/binary_search_tree.rb)
196
+ * [examples/ternary_search_tree.rb](examples/ternary_search_tree.rb)
197
+ * [test/names.rb](test/names.rb)
198
+ * [test/node.rb](test/node.rb)
199
+
200
+ ### [`Names::Greek`](lib/compsci/names/greek.rb) module
201
+
202
+ - `UPPER` `LOWER` `SYMBOLS` `CHAR_MAP` `LATIN_SYMBOLS` `SYMBOLS26`
203
+ - `Names::Greek.upper`
204
+ - `Names::Greek.lower`
205
+ - `Names::Greek.sym`
206
+
207
+ ### [`Names::Pokemon`](lib/compsci/names/pokemon.rb) module
208
+
209
+ - `Names::Pokemon.array`
210
+ - `Names::Pokemon.hash`
211
+ - `Names::Pokemon.grep`
212
+ - `Names::Pokemon.sample`
161
213
 
162
214
  ## [`Simplex`](lib/compsci/simplex.rb) class
163
215
 
164
- The Simplex algorithm is a technique for Linear Programming. Typically the
165
- problem is to maximize some linear expression of variables given some
166
- constraints on those variables given in terms of linear inequalities.
216
+ The [Simplex algorithm](https://en.wikipedia.org/wiki/Simplex_algorithm)
217
+ is a technique for
218
+ [Linear programming](https://en.wikipedia.org/wiki/Linear_programming).
219
+ Typically the problem is to maximize some linear expression of variables
220
+ given some constraints on those variables in terms of linear inequalities.
221
+
222
+ * [test/bench/simplex.rb](test/bench/simplex.rb)
223
+ * [test/simplex.rb](test/simplex.rb)
167
224
 
168
- ### [`Simplex::Parse`](lib/compsci/simplex/parse.rb) functions
225
+ ### [`Simplex::Parse`](lib/compsci/simplex/parse.rb) module
169
226
 
170
227
  * `Parse.tokenize` - convert a string to an array of tokens
171
228
  * `Parse.term` - parse certain tokens into [coefficient, varname]
172
229
  * `Parse.expression` - parse a string representing a sum of terms
173
230
  * `Parse.inequality` - parse a string like "#{expression} <= #{const}"
174
231
 
232
+ * [test/simplex_parse.rb](test/simplex_parse.rb)
233
+
175
234
  With `Simplex::Parse`, one can obtain solutions via:
176
235
 
177
236
  * `Simplex.maximize` - takes an expression to maximize followed by a variable
data/Rakefile CHANGED
@@ -78,7 +78,9 @@ scripts = [
78
78
  "examples/heap.rb",
79
79
  "examples/heap_push.rb",
80
80
  "examples/tree.rb",
81
- "examples/tree_push.rb",
81
+ "examples/flex_node.rb",
82
+ "examples/binary_search_tree.rb",
83
+ "examples/ternary_search_tree.rb",
82
84
  ]
83
85
 
84
86
  desc "Run ruby-prof on examples/"
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.0.1
1
+ 0.3.1.1
@@ -16,8 +16,8 @@ Gem::Specification.new do |s|
16
16
  s.files += Dir['examples/**/*.rb']
17
17
 
18
18
  s.add_development_dependency "buildar", "~> 3.0"
19
- s.add_development_dependency "minitest", "~> 5.0"
20
- s.add_development_dependency "rake", "~> 0"
19
+ s.add_development_dependency "minitest", ">= 5.0"
20
+ s.add_development_dependency "rake", ">= 12.3.3" # CVE-2020-8130
21
21
  s.add_development_dependency "flog", "~> 0"
22
22
  s.add_development_dependency "flay", "~> 0"
23
23
  s.add_development_dependency "roodi", "~> 0"
@@ -1,16 +1,51 @@
1
1
  require 'compsci/node'
2
- require 'compsci/binary_search_tree'
3
2
  require 'compsci/names'
4
3
 
5
4
  include CompSci
6
5
 
7
- RANDMAX = 99
6
+ puts <<EOF
7
+
8
+ #
9
+ # Insert nodes into a BST (random keys, duplicates: true)
10
+ #
11
+
12
+ EOF
13
+
14
+ randmax = 99
8
15
 
9
16
  p vals = Names::WW1.shuffle
10
- p keys = Array.new(vals.size) { rand RANDMAX }
17
+ p keys = Array.new(vals.size) { rand randmax }
18
+
19
+ root = KeyNode.new(vals.shift, key: keys.shift, children: 2, duplicates: true)
20
+ root.insert(keys.shift, vals.shift) until keys.empty?
21
+
22
+ puts root.display
23
+ puts
24
+
25
+ puts <<EOF
26
+
27
+ #
28
+ # Insert 30 nodes into a BST (unique keys, duplicates: false)
29
+ #
30
+
31
+ EOF
32
+
33
+ keys = (1..30).to_a.shuffle
34
+ vals = keys.map { rand 99 }
35
+
36
+ root = KeyNode.new(vals.shift, key: keys.shift, children: 2)
37
+ root.insert(keys.shift, vals.shift) while !keys.empty?
38
+ puts root.display
39
+
40
+ puts <<EOF
41
+
42
+ #
43
+ # Search for 30 different keys
44
+ #
45
+
46
+ EOF
11
47
 
12
- root = BinarySearchTree.new_node(keys.shift, vals.shift)
13
- tree = BinarySearchTree.new(root)
14
- tree[keys.shift] = vals.shift until keys.empty?
15
- # tree.insert(keys.shift, vals.shift) until keys.empty?
16
- puts tree
48
+ (1..30).each { |key|
49
+ node = root.search(key)
50
+ puts "found #{node}"
51
+ }
@@ -1,47 +1,46 @@
1
1
  require 'compsci/complete_tree'
2
- require 'compsci/timer'
3
2
 
4
3
  include CompSci
5
4
 
6
- puts <<EOF
5
+ vals = Array.new(30) { rand 99 }
6
+
7
+ [CompleteBinaryTree,
8
+ CompleteTernaryTree,
9
+ CompleteQuaternaryTree].each { |tree_class|
10
+
11
+ puts <<EOF
12
+
7
13
  #
8
- # Print CompleteBinary-, Ternary-, and QuaternaryTree
14
+ # Print #{tree_class} filled with static vals
9
15
  #
10
16
 
11
17
  EOF
12
18
 
13
- vals = Array.new(30) { rand 99 }
14
-
15
- [CompleteBinaryTree,
16
- CompleteTernaryTree,
17
- CompleteQuaternaryTree,
18
- ].each { |tree_class|
19
- # start with the same vals for each class
20
19
  my_vals = vals.dup
21
- p my_vals
20
+ puts "initial vals: #{my_vals.inspect}"
22
21
  tree = tree_class.new
23
22
  tree.push my_vals.shift until my_vals.empty?
23
+
24
24
  p tree
25
- puts tree.display(width: 80)
26
- puts
27
25
  puts
26
+ puts tree.display(width: 80)
28
27
  puts
29
28
 
30
29
 
31
- # TODO: add CompleteTree#df_search
32
- # tree.df_search { |n|
33
- # puts "visited #{n}"
34
- # false # or n.value > 90
35
- # }
36
- # puts
30
+ puts <<EOF
31
+
32
+ #
33
+ # Push random vals and print again
34
+ #
35
+
36
+ EOF
37
37
 
38
- # push different vals for each class
39
38
  my_vals = Array.new(30) { rand 99 }
40
- puts "push: #{my_vals.inspect}"
39
+ puts "new vals: #{my_vals.inspect}"
41
40
 
42
41
  tree.push my_vals.shift until my_vals.empty?
43
- puts tree.display(width: 80)
44
42
  puts
43
+ puts tree.display(width: 80)
45
44
  puts
46
45
  puts
47
46
  }
@@ -0,0 +1,117 @@
1
+ require 'compsci/flex_node'
2
+ require 'compsci/timer'
3
+
4
+ include CompSci
5
+
6
+ puts <<EOF
7
+
8
+ #
9
+ # Try out Binary, Ternary, and Quaternary FlexNodes
10
+ # Push the same vals to each
11
+ #
12
+
13
+ EOF
14
+
15
+ vals = Array.new(30) { rand 99 }
16
+
17
+ [2, 3, 4].each { |child_slots|
18
+ my_vals = vals.dup
19
+ p my_vals
20
+
21
+ root = ChildFlexNode.new my_vals.shift
22
+ root.push(my_vals.shift, child_slots) until my_vals.empty?
23
+ p root
24
+ puts root.display(width: 80)
25
+ puts
26
+ visited = []
27
+ root.df_search { |n|
28
+ visited << n
29
+ false # or n.value > 90
30
+ }
31
+ puts "df_search visited: %s" % visited.join(' ')
32
+ puts
33
+ puts
34
+
35
+ # push different vals for each class
36
+ my_vals = Array.new(30) { rand 99 }
37
+ puts "push: #{my_vals.inspect}"
38
+ root.push(my_vals.shift, child_slots) until my_vals.empty?
39
+ puts
40
+ puts root.display(width: 80)
41
+ puts
42
+ puts
43
+ }
44
+
45
+ puts <<EOF
46
+
47
+ #
48
+ # 30 ChildFlexNode pushes and df_search
49
+ #
50
+
51
+ EOF
52
+
53
+ vals = Array.new(30) { rand 99 }
54
+ p vals
55
+
56
+ root = ChildFlexNode.new vals.shift
57
+ child_slots = 2
58
+ root.push(vals.shift, child_slots) until vals.empty?
59
+ p root
60
+ puts root.display
61
+ puts
62
+
63
+ root.df_search { |n|
64
+ puts "visited #{n}"
65
+ false # or n.value > 90
66
+ }
67
+ puts
68
+
69
+ vals = Array.new(30) { rand 99 }
70
+ puts "push: #{vals.inspect}"
71
+
72
+ root.push(vals.shift, child_slots) until vals.empty?
73
+ puts root.display
74
+ puts
75
+
76
+
77
+ runtime = (ARGV.shift || "3").to_i
78
+ puts <<EOF
79
+
80
+ #
81
+ # #{runtime} seconds worth of Binary ChildFlexNode pushes
82
+ #
83
+
84
+ EOF
85
+
86
+ count = 0
87
+ start = Timer.now
88
+ start_1k = Timer.now
89
+ root = ChildFlexNode.new(rand 99)
90
+ child_slots = 2
91
+
92
+ loop {
93
+ count += 1
94
+
95
+ if count % 100 == 0
96
+ _ans, push_elapsed = Timer.elapsed {
97
+ root.push(rand(99), child_slots)
98
+ }
99
+ puts "%ith push: %0.8f s" % [count, push_elapsed]
100
+
101
+ if count % 1000 == 0
102
+ push_1k_elapsed = Timer.since start_1k
103
+ puts "-----------"
104
+ puts " 1k push: %0.4f s (%i push / s)" %
105
+ [push_1k_elapsed, 1000.to_f / push_1k_elapsed]
106
+ puts
107
+ start_1k = Timer.now
108
+ end
109
+ else
110
+ root.push(rand(99), child_slots)
111
+ end
112
+
113
+ break if Timer.since(start) > runtime
114
+ }
115
+
116
+ puts "pushed %i items in %0.1f s" % [count, Timer.since(start)]
117
+ puts