kxi 1.0.1 → 1.0.2

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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/lib/kxi.rb +44 -39
  3. data/lib/kxi/application/config.rb +177 -177
  4. data/lib/kxi/application/config_reader.rb +16 -16
  5. data/lib/kxi/application/event.rb +35 -35
  6. data/lib/kxi/application/logger.rb +155 -155
  7. data/lib/kxi/application/version.rb +106 -74
  8. data/lib/kxi/application/version_expression.rb +94 -69
  9. data/lib/kxi/application/workspace.rb +105 -0
  10. data/lib/kxi/cli/anonymous_argument.rb +50 -50
  11. data/lib/kxi/cli/argument.rb +56 -56
  12. data/lib/kxi/cli/argument_values.rb +83 -83
  13. data/lib/kxi/cli/explicit_argument.rb +38 -38
  14. data/lib/kxi/cli/flag_argument.rb +15 -15
  15. data/lib/kxi/cli/named_argument.rb +59 -59
  16. data/lib/kxi/cli/property_list.rb +57 -48
  17. data/lib/kxi/cli/table.rb +82 -62
  18. data/lib/kxi/cli/verb.rb +282 -280
  19. data/lib/kxi/collections/array_collection.rb +106 -106
  20. data/lib/kxi/collections/enumerable.rb +527 -527
  21. data/lib/kxi/collections/enumerator.rb +31 -31
  22. data/lib/kxi/collections/hash_collection.rb +100 -100
  23. data/lib/kxi/collections/protected_collection.rb +20 -19
  24. data/lib/kxi/exceptions/abstract_exception.rb +34 -34
  25. data/lib/kxi/exceptions/argument_exception.rb +21 -21
  26. data/lib/kxi/exceptions/collection_exception.rb +13 -13
  27. data/lib/kxi/exceptions/configuration_exception.rb +36 -25
  28. data/lib/kxi/exceptions/dimension_mismatch_exception.rb +29 -0
  29. data/lib/kxi/exceptions/invalid_type_exception.rb +32 -32
  30. data/lib/kxi/exceptions/no_argument_exception.rb +20 -20
  31. data/lib/kxi/exceptions/not_implemented_exception.rb +12 -12
  32. data/lib/kxi/exceptions/out_of_range_exception.rb +43 -43
  33. data/lib/kxi/exceptions/parse_exception.rb +28 -20
  34. data/lib/kxi/exceptions/verb_expected_exception.rb +20 -20
  35. data/lib/kxi/exceptions/workspace_collision_exception.rb +21 -0
  36. data/lib/kxi/math/math.rb +45 -0
  37. data/lib/kxi/math/matrix.rb +303 -0
  38. data/lib/kxi/math/polynomial.rb +141 -101
  39. data/lib/kxi/math/vector.rb +181 -0
  40. data/lib/kxi/platform.rb +103 -57
  41. data/lib/kxi/reflection/stack_frame.rb +80 -80
  42. data/lib/kxi/version.rb +4 -4
  43. metadata +8 -3
  44. data/lib/kxi/exceptions/invalid_operation_exception.rb +0 -11
@@ -0,0 +1,303 @@
1
+ # Created by Matyáš Pokorný on 2018-04-12.
2
+
3
+ module KXI
4
+ module Math
5
+ # Represents a matrix
6
+ class Matrix
7
+ # Returns a square identity matrix
8
+ # @param [Integer] n Dimension of matrix
9
+ # @return [KXI::Math::Matrix] Square identity matrix of given dimension
10
+ def self.identity(n)
11
+ return KXI::Math::Matrix.new(n, n) { |c, r| c == r ? 1 : 0 }
12
+ end
13
+
14
+ # Returns the number of rows
15
+ # @return [Integer] Number of rows
16
+ def rows
17
+ @rows
18
+ end
19
+
20
+ # Returns the number of columns
21
+ # @return [Integer] Number of columns
22
+ def columns
23
+ @cols
24
+ end
25
+
26
+ # Instantiates the {KXI::Math::Matrix} class
27
+ # @overload initialize(cols)
28
+ # Creates a square matrix
29
+ # @param [Integer] cols Dimension of matrix
30
+ # @overload initialize(cols, rows)
31
+ # Creates a matrix
32
+ # @param [Integer] cols Number of columns in the matrix
33
+ # @param [Integer] rows Number of rows in the matrix
34
+ def initialize(cols, rows = nil)
35
+ rows = cols if rows == nil
36
+ @data = []
37
+ cols.times do |i|
38
+ if block_given?
39
+ @data[i] = []
40
+ rows.times do |j|
41
+ v = yield(i, j)
42
+ raise(KXI::Exceptions::InvalidTypeException.new(v.type, Numeric)) unless v.is_a?(Numeric)
43
+ @data[i].push(v)
44
+ end
45
+ else
46
+ @data[i] = [0.0] * rows
47
+ end
48
+ end
49
+ @cols = cols
50
+ @rows = rows
51
+ end
52
+
53
+ # Swaps two rows of the matrix
54
+ # @param [Integer] a First row to swap
55
+ # @param [Integer] b Second row to swap with
56
+ # @raise [KXI::Exceptions::OutOfRangeException] When one of arguments is out of range
57
+ # @return [KXI::Math::Matrix] New matrix with swapped rows
58
+ def row_swap(a, b)
59
+ raise(KXI::Exceptions::OutOfRangeException.new(a, 0, @rows - 1)) if a < 0 or a >= @rows
60
+ raise(KXI::Exceptions::OutOfRangeException.new(b, 0, @rows - 1)) if b < 0 or b >= @rows
61
+ return KXI::Math::Matrix.new(@cols, @rows) do |c, r|
62
+ next get(c, b) if r == a
63
+ next get(c, a) if r == b
64
+ get(c, r)
65
+ end
66
+ end
67
+
68
+ # Multiplies a row of matrix with specific coefficient
69
+ # @overload row_mult(src, cof)
70
+ # Multiplies a row by coefficient into that row
71
+ # @param [Integer] src Row to multiply
72
+ # @param [Numeric] cof Coefficient to multiply with
73
+ # @raise [KXI::Exceptions::OutOfRangeException] When one of arguments is out of range
74
+ # @return [Matrix] New matrix with multiplied row
75
+ # @overload row_mult(src, cof, dst)
76
+ # Multiplies a row by coefficient into other row
77
+ # @param [Integer] src Row to multiply
78
+ # @param [Numeric] cof Coefficient to multiply with
79
+ # @param [Numeric] dst Row to write the results of multiplication
80
+ # @raise [KXI::Exceptions::OutOfRangeException] When one of arguments is out of range
81
+ # @return [Matrix] New matrix with multiplied row
82
+ def row_mult!(src, cof, dst = nil)
83
+ dst = src if dst == nil
84
+ raise(KXI::Exceptions::OutOfRangeException.new(src, 0, @rows - 1)) if src < 0 or src >= @rows
85
+ raise(KXI::Exceptions::OutOfRangeException.new(dst, 0, @rows - 1)) if dst < 0 or dst >= @rows
86
+ return KXI::Math::Matrix.new(@cols, @rows) do |c, r|
87
+ next cof * get(c, src) if r == dst
88
+ get(c, r)
89
+ end
90
+ end
91
+
92
+ # Multiplies a row of matrix with specific coefficient
93
+ # @overload row_mult(src, cof)
94
+ # Multiplies a row by coefficient and adds that to the row
95
+ # @param [Integer] src Row to multiply
96
+ # @param [Numeric] cof Coefficient to multiply with
97
+ # @raise [KXI::Exceptions::OutOfRangeException] When one of arguments is out of range
98
+ # @return [Matrix] New matrix with multiplied row
99
+ # @overload row_mult(src, cof, dst)
100
+ # Multiplies a row by coefficient and adds that to other row
101
+ # @param [Integer] src Row to multiply
102
+ # @param [Numeric] cof Coefficient to multiply with
103
+ # @param [Numeric] dst Row to write the results of multiplication
104
+ # @raise [KXI::Exceptions::OutOfRangeException] When one of arguments is out of range
105
+ # @return [Matrix] New matrix with multiplied row
106
+ def row_mult(src, cof, dst = nil)
107
+ dst = src if dst == nil
108
+ return KXI::Math::Matrix.new(@cols, @rows) do |c, r|
109
+ next get(c, r) + cof * get(c, src) if r == dst
110
+ get(c, r)
111
+ end
112
+ end
113
+
114
+ def sign(col, row)
115
+ return ((col % 2 == 0) == (row % 2 == 0)) ? 1 : -1
116
+ end
117
+
118
+ # Computes the determinant of matrix
119
+ # @return [Numeric] Determinant of matrix
120
+ def determinant
121
+ return get(0, 0) * get(1, 1) - get(1, 0) * get(0, 1) if @cols == 2
122
+ ci = 0
123
+ cz = 0
124
+ @cols.times do |col|
125
+ zeros = 0
126
+ @rows.times { |row| zeros += 1 if get(col, row) == 0 }
127
+ if zeros > cz
128
+ ci = col
129
+ cz = zeros
130
+ end
131
+ end
132
+ ret = 0
133
+ @rows.times do |row|
134
+ if get(ci, row) != 0
135
+ ret += sign(ci, row) * get(ci, row) * sub(ci, row).determinant
136
+ end
137
+ end
138
+ return ret
139
+ end
140
+
141
+ # Computes the inverse of matrix
142
+ # @return [KXI::Math::Matrix] Inverse matrix
143
+ def inverse
144
+ return (KXI::Math::Matrix.new(@cols, @rows) { |c, r| sign(c, r) * sub(c, r).determinant }).transpose * (1 / determinant)
145
+ end
146
+
147
+ def sub(col, row)
148
+ return KXI::Math::Matrix.new(@cols - 1, @rows - 1) do |c, r|
149
+ c = c + 1 if c >= col
150
+ r = r + 1 if r >= row
151
+ next get(c, r)
152
+ end
153
+ end
154
+
155
+ # Gets the value of matrix at given column and row
156
+ # @param [Integer] col Column to get
157
+ # @param [Integer] row Row to get
158
+ # @raise [KXI::Exceptions::OutOfRangeException] When one of arguments is out of range
159
+ # @return [Numeric] Value of matrix at given column and row
160
+ def get(col, row)
161
+ raise(KXI::Exceptions::OutOfRangeException.new(col, 0, @cols - 1)) if col < 0 or col >= @cols
162
+ raise(KXI::Exceptions::OutOfRangeException.new(row, 0, @rows - 1)) if row < 0 or row >= @rows
163
+ return @data[col][row]
164
+ end
165
+
166
+ # Sets the value of vector at specific dimension
167
+ # @overload set(col, row, val)
168
+ # Sets the value of vector at specific dimension
169
+ # @param [Integer] col Column to set
170
+ # @param [Integer] row Row to set
171
+ # @param [Integer] val Value to set the matrix to
172
+ # @raise [KXI::Exceptions::OutOfRangeException] When one of arguments is out of range
173
+ # @raise [KXI::Exceptions::InvalidTypeException] When value has invalid type
174
+ # @return [Numeric] Value passed to function
175
+ # @overload set(col, row, val)
176
+ # Sets the values of vector to range of values starting from specific dimension
177
+ # @param [Integer] col Column to set
178
+ # @param [Integer] row Row to set
179
+ # @param [Array] val Values to set the matrix to
180
+ # @raise [KXI::Exceptions::OutOfRangeException] When one of arguments is out of range
181
+ # @raise [KXI::Exceptions::InvalidTypeException] When value has invalid type
182
+ # @return [Numeric] Value passed to function
183
+ def set(col, row, value)
184
+ raise(KXI::Exceptions::OutOfRangeException.new(col, 0, @cols - 1)) if col < 0 or col >= @cols
185
+ raise(KXI::Exceptions::OutOfRangeException.new(row, 0, @rows - 1)) if row < 0 or row >= @rows
186
+ if value.is_a?(Array)
187
+ i = 0
188
+ while i + col < @cols and i < value.length
189
+ j = 0
190
+ while j + row < @rows and j < value[i].length
191
+ @data[col + i][row + j] = value[i][j].to_f
192
+ j += 1
193
+ end
194
+ i += 1
195
+ end
196
+ elsif value.is_a?(Numeric)
197
+ @data[col][row] = value.to_f
198
+ else
199
+ raise(KXI::Exceptions::InvalidTypeException.new(value.type, Numeric, Array))
200
+ end
201
+ return value
202
+ end
203
+
204
+ # Iterates over each element of matrix
205
+ # @yield [col, row, val] Iterator function
206
+ # @yieldparam [Integer] col Column passed to iterator
207
+ # @yieldparam [Integer] row Row passed to iterator
208
+ # @yieldparam [Numeric] val Value of matrix at given column and row
209
+ def each
210
+ @cols.times do |col|
211
+ @rows.times do |row|
212
+ yield(col, row, @data[col][row])
213
+ end
214
+ end
215
+ end
216
+
217
+ # Returns the transpose of matrix
218
+ # @return [KXI::Math::Matrix] Transposed matrix
219
+ def transpose
220
+ ret = KXI::Math::Matrix.new(@rows, @cols)
221
+ each do |col, row, value|
222
+ ret.set(row, col, value)
223
+ end
224
+ return ret
225
+ end
226
+
227
+ # Multiplies matrix
228
+ # @overload *(other)
229
+ # Scales matrix
230
+ # @param [Numeric] other Scalar to multiply with
231
+ # @raise [KXI::Exceptions::InvalidTypeException] When value is of invalid type
232
+ # @return [KXI::Math::Matrix] Scaled matrix
233
+ # @overload *(other)
234
+ # Multiplies matrix by another matrix
235
+ # @param [KXI::Math::Matrix] other Right-side matrix to multiply with
236
+ # @raise [KXI::Exceptions::InvalidTypeException] When value is of invalid type
237
+ # @return [KXI::Math::Matrix] Multiplied matrix
238
+ # @overload *(other)
239
+ # Multiplies matrix by vector
240
+ # @param [KXI::Math::Vector] other Vector to multiply with
241
+ # @raise [KXI::Exceptions::InvalidTypeException] When value is of invalid type
242
+ # @return [KXI::Math::Vector] Result of multiplication
243
+ def *(other)
244
+ if other.is_a?(Numeric)
245
+ return KXI::Math::Matrix.new(@cols, @rows) { |c, r| other * get(c, r) }
246
+ elsif other.is_a?(KXI::Math::Matrix)
247
+ return KXI::Math::Matrix.new(other.columns, @rows) do |c, r|
248
+ sum = 0
249
+ @cols.times { |i| sum += get(i, r) * other.get(c, i) }
250
+ next sum
251
+ end
252
+ elsif other.is_a?(KXI::Math::Vector)
253
+ return KXI::Math::Vector.new(@rows) do |d|
254
+ sum = 0
255
+ @cols.times { |c| sum += get(c, d) * other[c] }
256
+ next sum
257
+ end
258
+ else
259
+ raise(KXI::Exceptions::InvalidTypeException.new(other.class, Numeric, KXI::Math::Matrix, KXI::Math::Vector))
260
+ end
261
+ end
262
+
263
+ # Compares matrix
264
+ # @param [void] other Value to compare to
265
+ # @return [Boolean] True if matrix is equivalent to given value; false otherwise
266
+ def ==(other)
267
+ return false unless other.is_a?(KXI::Math::Matrix)
268
+ return false if @cols != other.columns or @row != other.rows
269
+ @cols.times do |col|
270
+ @rows.times do |row|
271
+ return false if get(col, row) != other.get(col, row)
272
+ end
273
+ end
274
+ end
275
+
276
+ def str(num)
277
+ return (num == num.to_i ? num.to_i : num).to_s
278
+ end
279
+
280
+ # Converts matrix to string
281
+ # @param [Boolean] d Determines whether string should be decorated
282
+ # @return [String] String representation of matrix
283
+ def to_s(d = true)
284
+ ret = ''
285
+ just = [0] * @cols
286
+ each { |c, r, v| d = str(v).length; just[c] = d if d > just[c] }
287
+ @rows.times do |row|
288
+ ret += $/ unless row == 0
289
+ ret += '|' if d
290
+ @cols.times do |col|
291
+ ret += ' ' unless col == 0
292
+ ret += str(get(col, row)).rjust(just[col], ' ')
293
+ end
294
+ ret += '|' if d
295
+ end
296
+
297
+ return ret
298
+ end
299
+
300
+ private :sign, :sub, :str
301
+ end
302
+ end
303
+ end
@@ -1,102 +1,142 @@
1
- # Created by Matyáš Pokorný on 2018-01-26.
2
-
3
- module KXI
4
- module Math
5
- class Polynomial
6
- def degree
7
- @cfs.length - 1
8
- end
9
-
10
- def coefficients
11
- @cfs
12
- end
13
-
14
- def initialize(*cfs)
15
- cfs.shift while cfs[0] == 0
16
- @cfs = cfs.collect { |i| i.to_f }
17
- end
18
-
19
- def to_s
20
- ret = nil
21
- foreach do |k, d|
22
- if d > 0
23
- if k != 0
24
- if ret == nil
25
- ret = k >= 0 ? '' : '-'
26
- else
27
- ret += k >= 0 ? ' + ' : ' - '
28
- end
29
- ret += (k >= 0 ? k : -k).to_s if k != 1 and k != -1
30
- ret += "x#{d > 1 ? "^#{d.to_s}" : ''}"
31
- end
32
- else
33
- if ret == nil
34
- ret = k >= 0 ? '' : '-'
35
- else
36
- ret += k >= 0 ? ' + ' : ' - '
37
- end
38
- ret += (k >= 0 ? k : -k).to_s
39
- end
40
- end
41
- return ret
42
- end
43
-
44
- def derivative
45
- cfs = []
46
- foreach do |k, d|
47
- if d > 0
48
- cfs.push(k * d)
49
- end
50
- end
51
- return Polynomial.new(*cfs)
52
- end
53
-
54
- def integral
55
- cfs = []
56
- foreach do |k, d|
57
- cfs.push(k / (d + 1))
58
- end
59
- cfs.push(0)
60
- return Polynomial.new(*cfs)
61
- end
62
-
63
- def integrate(x1, x2)
64
- min = x1 > x2 ? x2 : x1
65
- max = x1 > x2 ? x1 : x2
66
- i = integral
67
- return i.at(max) - i.at(min)
68
- end
69
-
70
- def at(x)
71
- sum = 0
72
- foreach do |k, d|
73
- sum += k * (x ** d)
74
- end
75
- return sum
76
- end
77
-
78
- def foreach
79
- d = degree
80
- @cfs.each do |k|
81
- yield(k, d)
82
- d -= 1
83
- end
84
- end
85
-
86
- def *(other)
87
- if other.is_a?(Polynomial)
88
- cfs = [0] * (degree + other.degree)
89
- foreach do |k1, d1|
90
- other.foreach do |k2, d2|
91
- cfs[d1 + d2] += k1 * k2
92
- end
93
- end
94
- return Polynomial.new(*cfs)
95
- elsif other.is_a?(Numeric)
96
- return @cfs.collect { |k| k * other }
97
- end
98
- raise(Exception.new('Polynomial multiplication error!'))
99
- end
100
- end
101
- end
1
+ # Created by Matyáš Pokorný on 2018-01-26.
2
+
3
+ module KXI
4
+ module Math
5
+ # Represents a polynomial
6
+ class Polynomial
7
+ # Returns the degree of polynomial
8
+ # @return [Integer] Degree of polynomial
9
+ def degree
10
+ @cfs.length - 1
11
+ end
12
+
13
+ # Returns the coefficients of polynomial
14
+ # @return [Array<Numeric>] Coefficients of polynomial
15
+ def coefficients
16
+ @cfs
17
+ end
18
+
19
+ # Instantiates the {KXI::Math::Polynomial} class
20
+ # @param [Numeric] cfs Coefficients of polynomial
21
+ def initialize(*cfs)
22
+ cfs.shift while cfs.length > 1 and cfs[0] == 0
23
+ @cfs = cfs.collect do |i|
24
+ raise(KXI::Exceptions::InvalidTypeException.new(i.class, Numeric)) unless i.is_a?(Numeric)
25
+ i.to_f
26
+ end
27
+ end
28
+
29
+ # Converts polynomial to string
30
+ # @return [String] String equivalent to polynomial
31
+ def to_s
32
+ return (@cfs[0] == @cfs[0].to_i ? @cfs[0].to_i : @cfs[0]) if degree == 0
33
+ ret = nil
34
+ foreach do |k, d|
35
+ if k != 0
36
+ k = k.to_i if k.to_i == k
37
+ if d > 0
38
+ if ret == nil
39
+ ret = k >= 0 ? '' : '-'
40
+ else
41
+ ret += k >= 0 ? ' + ' : ' - '
42
+ end
43
+ ret += (k >= 0 ? k : -k).to_s if k != 1 and k != -1
44
+ ret += "x#{d > 1 ? "^#{d.to_s}" : ''}"
45
+ else
46
+ if ret == nil
47
+ ret = k >= 0 ? '' : '-'
48
+ else
49
+ ret += k >= 0 ? ' + ' : ' - '
50
+ end
51
+ ret += (k >= 0 ? k : -k).to_s
52
+ end
53
+ end
54
+ end
55
+ return ret
56
+ end
57
+
58
+ # Takes the derivative of polynomial
59
+ # @param [Integer] n Order of derivation
60
+ # @return [KXI::Math::Polynomial] Polynomial that represents the n-th derivative
61
+ def derivative(n = 1)
62
+ return Polynomial.new(0) if degree <= n
63
+ cfs = []
64
+ foreach do |k, d|
65
+ if d >= n
66
+ cfs.push(k * KXI::Math.pfact(d, d - n))
67
+ end
68
+ end
69
+ return Polynomial.new(*cfs)
70
+ end
71
+
72
+ # Takes the integral of polynomial
73
+ # @return [KXI::Math::Polynomial] Polynomial that represents the integral
74
+ def integral
75
+ cfs = []
76
+ foreach do |k, d|
77
+ cfs.push(k / (d + 1))
78
+ end
79
+ cfs.push(0)
80
+ return Polynomial.new(*cfs)
81
+ end
82
+
83
+ # Integrates the polynomial
84
+ # @param [Numeric] x1 First bound of integration
85
+ # @param [Numeric] x2 Second bound of integration
86
+ # @return [Numeric] Result of integration
87
+ def integrate(x1, x2)
88
+ min = x1 > x2 ? x2 : x1
89
+ max = x1 > x2 ? x1 : x2
90
+ i = integral
91
+ return i.at(max) - i.at(min)
92
+ end
93
+
94
+ # Returns the value of polynomial
95
+ # @param [Numeric] x Value of x
96
+ # @return [Numeric] Value of polynomial at x
97
+ def at(x)
98
+ sum = 0
99
+ foreach do |k, d|
100
+ sum += k * (x ** d)
101
+ end
102
+ return sum
103
+ end
104
+
105
+ # Iterates over every coefficient of polynomial (from higher powers to lower powers)
106
+ # @yield [k] Iterator
107
+ # @yieldparam [Numeric] k Coefficient of polynomial
108
+ def foreach
109
+ d = degree
110
+ @cfs.each do |k|
111
+ yield(k, d)
112
+ d -= 1
113
+ end
114
+ end
115
+
116
+ # Multiplies polynomial
117
+ # @overload *(other)
118
+ # Multiplies polynomial with coefficient
119
+ # @param [Numeric] other Coefficient to multiply with
120
+ # @return [KXI::Math::Polynomial] Result of multiplication
121
+ # @overload *(other)
122
+ # Multiplies polynomial with other polynomial
123
+ # @param [KXI::Math::Polynomial] other Polynomial to multiply with
124
+ # @return [KXI::Math::Polynomial] Result of multiplication
125
+ def *(other)
126
+ if other.is_a?(Polynomial)
127
+ cfs = [0] * (degree + other.degree + 1)
128
+ foreach do |k1, d1|
129
+ other.foreach do |k2, d2|
130
+ cfs[d1 + d2] += k1 * k2
131
+ end
132
+ end
133
+ return Polynomial.new(*cfs)
134
+ elsif other.is_a?(Numeric)
135
+ return @cfs.collect { |k| k * other }
136
+ else
137
+ raise(KXI::Exceptions::InvalidTypeException.new(other.class, Numeric, KXI::Math::Polynomial))
138
+ end
139
+ end
140
+ end
141
+ end
102
142
  end