silicium 0.0.2 → 0.0.21
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 +4 -4
- data/.codeclimate.yml +3 -3
- data/.gitignore +13 -13
- data/.rakeTasks +8 -0
- data/.travis.yml +28 -25
- data/CODE_OF_CONDUCT.md +74 -74
- data/Gemfile +8 -8
- data/LICENSE.txt +21 -21
- data/Makefile +269 -269
- data/README.md +588 -46
- data/Rakefile +16 -16
- data/bin/console +14 -14
- data/bin/setup +8 -8
- data/docs/Object.html +117 -117
- data/docs/README_md.html +142 -142
- data/docs/Silicium/Combinatorics.html +270 -270
- data/docs/Silicium/Dice/Polyhedron.html +315 -315
- data/docs/Silicium/Dice/PolyhedronSet.html +321 -321
- data/docs/Silicium/Dice.html +99 -99
- data/docs/Silicium/Error.html +106 -106
- data/docs/Silicium/Geometry/Line2dCanon.html +243 -243
- data/docs/Silicium/Geometry/VariablesOrderException.html +106 -106
- data/docs/Silicium/Geometry.html +940 -940
- data/docs/Silicium/GraphVisualizer.html +226 -0
- data/docs/Silicium/Graphs/GraphError.html +106 -106
- data/docs/Silicium/Graphs/OrientedGraph.html +901 -775
- data/docs/Silicium/Graphs/UnorientedGraph.html +237 -284
- data/docs/Silicium/Graphs.html +374 -164
- data/docs/Silicium/IntegralDoesntExistError.html +106 -106
- data/docs/Silicium/NumericalIntegration.html +521 -521
- data/docs/Silicium/Optimization.html +629 -639
- data/docs/Silicium/Plotter/Image.html +297 -297
- data/docs/Silicium/Plotter.html +186 -186
- data/docs/Silicium.html +101 -101
- data/docs/created.rid +9 -9
- data/docs/css/fonts.css +167 -167
- data/docs/css/rdoc.css +619 -619
- data/docs/index.html +134 -132
- data/docs/js/darkfish.js +84 -84
- data/docs/js/navigation.js +105 -105
- data/docs/js/search.js +110 -110
- data/docs/js/search_index.js +1 -1
- data/docs/js/search_index.js.gz +0 -0
- data/docs/js/searcher.js +229 -229
- data/docs/table_of_contents.html +697 -608
- data/lib/algebra.rb +452 -0
- data/lib/algebra_diff.rb +258 -0
- data/lib/geometry/figure.rb +62 -0
- data/lib/geometry.rb +290 -236
- data/lib/geometry3d.rb +270 -0
- data/lib/graph/dfs.rb +42 -0
- data/lib/graph/kruskal.rb +36 -0
- data/lib/graph/scc.rb +97 -0
- data/lib/graph.rb +350 -164
- data/lib/graph_visualizer.rb +287 -0
- data/lib/ml_algorithms.rb +181 -0
- data/lib/numerical_integration.rb +184 -147
- data/lib/optimization.rb +209 -144
- data/lib/plotter.rb +256 -96
- data/lib/polynomial_division.rb +132 -0
- data/lib/polynomial_interpolation.rb +94 -0
- data/lib/regression.rb +120 -0
- data/lib/silicium/adding.rb +37 -0
- data/lib/silicium/conversions.rb +23 -0
- data/lib/silicium/multi.rb +82 -0
- data/lib/silicium/sparse.rb +76 -0
- data/lib/silicium/sugar.rb +37 -0
- data/lib/silicium/trans.rb +26 -0
- data/lib/silicium/version.rb +3 -3
- data/lib/silicium.rb +5 -5
- data/lib/theory_of_probability.rb +240 -226
- data/lib/topological_sort.rb +50 -0
- data/oriented_graph.png +0 -0
- data/plot.png +0 -0
- data/silicium.gemspec +38 -39
- metadata +38 -16
data/lib/algebra.rb
ADDED
@@ -0,0 +1,452 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Silicium
|
3
|
+
require_relative 'algebra_diff'
|
4
|
+
require_relative 'polynomial_division'
|
5
|
+
|
6
|
+
##
|
7
|
+
# +Algebra+ module helps to perform calculations with polynoms
|
8
|
+
module Algebra
|
9
|
+
attr_reader :str
|
10
|
+
|
11
|
+
##
|
12
|
+
# +initializer(str)+ creates a correct ruby str from given one
|
13
|
+
def initializer(str)
|
14
|
+
raise PolynomError, 'Invalid string for polynom ' unless polycop(str)
|
15
|
+
@str = str
|
16
|
+
end
|
17
|
+
|
18
|
+
##
|
19
|
+
# +eratosthen_primes_to(n)+ finds all primes up to n
|
20
|
+
# with the sieve of eratosthenes
|
21
|
+
#
|
22
|
+
## eratosthen_primes_to(1) # => []
|
23
|
+
## eratosthen_primes_to(15) # => [2, 3, 5, 7, 11, 13]
|
24
|
+
## eratosthen_primes_to(50) # => [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]
|
25
|
+
def eratosthen_primes_to(n)
|
26
|
+
raise ArgumentError unless valid_n?(n)
|
27
|
+
|
28
|
+
array = (2..n).to_a
|
29
|
+
array.each do |prime|
|
30
|
+
square = prime**2
|
31
|
+
break if square > n
|
32
|
+
|
33
|
+
array -= square.step(n, prime).to_a
|
34
|
+
end
|
35
|
+
array
|
36
|
+
end
|
37
|
+
|
38
|
+
##
|
39
|
+
# Checks if the number n is correct
|
40
|
+
def valid_n?(n)
|
41
|
+
return false if n <= 0
|
42
|
+
return false unless n.class == Integer
|
43
|
+
|
44
|
+
true
|
45
|
+
end
|
46
|
+
|
47
|
+
##
|
48
|
+
# +polycop(str)+ determines whether the str is an appropriate function
|
49
|
+
# which only has one variable
|
50
|
+
#
|
51
|
+
## polycop('x^2 + 2 * x + 7') # => True
|
52
|
+
## polycop('x^2 +2nbbbbb * x + 7') # => False
|
53
|
+
def polycop(str)
|
54
|
+
@letter_var = nil
|
55
|
+
parsed = str.split(/[-+]/)
|
56
|
+
parsed.each { |term| return false unless valid_term?(term) }
|
57
|
+
|
58
|
+
true
|
59
|
+
end
|
60
|
+
|
61
|
+
##
|
62
|
+
# Parses single polynomial term and returns false if
|
63
|
+
# term is incorrect on has different independent variable
|
64
|
+
# It updated current independent variable if it wasn't set before
|
65
|
+
def valid_term?(term)
|
66
|
+
correct, cur_var = extract_variable(term)
|
67
|
+
return false unless correct
|
68
|
+
|
69
|
+
@letter_var ||= cur_var
|
70
|
+
!(another_variable?(@letter_var, cur_var) || !letter_controller(term))
|
71
|
+
end
|
72
|
+
|
73
|
+
##
|
74
|
+
# @param [String] expr - part of analytical function that has one independent variable
|
75
|
+
# @return [Array]
|
76
|
+
# retuns pair, the first value indicates if parsing succeeded, the second is variable
|
77
|
+
#
|
78
|
+
def extract_variable(expr)
|
79
|
+
expr[/(\s?\d*\s?\*\s?)?([a-z])(\^\d*)?|\s?\d+$/]
|
80
|
+
[!Regexp.last_match.nil?, Regexp.last_match(2)]
|
81
|
+
end
|
82
|
+
|
83
|
+
##
|
84
|
+
# Checks if new variable is present and is not the same as last known variable
|
85
|
+
def another_variable?(old_variable, new_variable)
|
86
|
+
!new_variable.nil? && old_variable != new_variable
|
87
|
+
end
|
88
|
+
|
89
|
+
# check for extra letters in term
|
90
|
+
def letter_controller(term)
|
91
|
+
allowed_w = %w[ln lg log cos sin]
|
92
|
+
letters = term.scan(/[a-z]{2,}/)
|
93
|
+
letters = letters.join
|
94
|
+
letters.empty? || allowed_w.include?(letters)
|
95
|
+
end
|
96
|
+
|
97
|
+
##
|
98
|
+
# +to_ruby_s(val)+ transforms @str into a correct ruby str
|
99
|
+
# works for logarithms, trigonometry and misspelled power
|
100
|
+
#
|
101
|
+
## to_ruby_s('') # =>
|
102
|
+
def to_ruby_s(val)
|
103
|
+
temp_str = @str
|
104
|
+
temp_str.gsub!('^', '**')
|
105
|
+
temp_str.gsub!(/lg|log|ln/, 'Math::\1')
|
106
|
+
temp_str.gsub!(@letter_var, val)
|
107
|
+
temp_str
|
108
|
+
end
|
109
|
+
|
110
|
+
# +evaluate(val)+ counts the result using a given value
|
111
|
+
def evaluate(val)
|
112
|
+
res = to_ruby_s(val)
|
113
|
+
eval(res)
|
114
|
+
end
|
115
|
+
|
116
|
+
|
117
|
+
##
|
118
|
+
# +PolynomRootsReal+
|
119
|
+
module PolynomRootsReal
|
120
|
+
##
|
121
|
+
# +get_coef(str)+ transforms polynom into array of coefficients
|
122
|
+
# arr[0] = a0 * x^0 ; arr[1] = a1 * x^1 ; ... arr[n] = an * x^(n-1)
|
123
|
+
## get_coef('') # =>
|
124
|
+
def get_coef(str)
|
125
|
+
tokens = split_by_op(str)
|
126
|
+
cf = Array.new(0.0)
|
127
|
+
deg = 0
|
128
|
+
tokens.each { |term| deg = process_term(term, cf, deg) }
|
129
|
+
insert_zeroes(cf, deg) unless deg.zero?
|
130
|
+
cf.reverse
|
131
|
+
end
|
132
|
+
|
133
|
+
def split_by_op(str)
|
134
|
+
space_clear_str = str.gsub(/\s/,'')
|
135
|
+
pos_tokens = space_clear_str.split('+')
|
136
|
+
split_by_neg(pos_tokens)
|
137
|
+
end
|
138
|
+
|
139
|
+
|
140
|
+
def keep_split(str,delim)
|
141
|
+
res = str.split(delim)
|
142
|
+
return [] if res.length == 0
|
143
|
+
[res.first] + res[1,res.length - 1].map {|x| x = delim + x }
|
144
|
+
end
|
145
|
+
|
146
|
+
def split_by_neg(pos_tokens)
|
147
|
+
res = []
|
148
|
+
pos_tokens.each { |token| res.concat(keep_split(token, '-')) }
|
149
|
+
res
|
150
|
+
end
|
151
|
+
|
152
|
+
def get_coef_inner(cur_deg, deg)
|
153
|
+
deg.zero? ? cur_deg : deg
|
154
|
+
end
|
155
|
+
|
156
|
+
def process_term(term, cf, deg)
|
157
|
+
term[/(-?\d*[.|,]?\d*)\*?[a-z](\^\d*)?/]
|
158
|
+
par_cf = Regexp.last_match(1)
|
159
|
+
par_deg = Regexp.last_match(2)
|
160
|
+
cur_cf, cur_deg = initialize_cf_deg(term, par_cf, par_deg)
|
161
|
+
# initialize deg for the first time
|
162
|
+
deg = cur_deg if deg.zero?
|
163
|
+
# add 0 coefficient to missing degrees
|
164
|
+
insert_zeroes(cf, deg - cur_deg - 1) if deg - cur_deg > 1
|
165
|
+
cf << cur_cf
|
166
|
+
deg = cur_deg
|
167
|
+
end
|
168
|
+
|
169
|
+
# intialize cur_cf and cur_deg depend on current term
|
170
|
+
def initialize_cf_deg(term, par_cf, par_deg)
|
171
|
+
return [term.to_f, 0] if free_term? term
|
172
|
+
cf = if par_cf.empty?
|
173
|
+
term.include?('-') ? -1 : 1
|
174
|
+
else
|
175
|
+
par_cf.to_f
|
176
|
+
end
|
177
|
+
[cf, par_deg.nil? ? 1 : par_deg.delete('^').to_i]
|
178
|
+
end
|
179
|
+
|
180
|
+
def free_term?(term)
|
181
|
+
term.scan(/[a-z]/).empty?
|
182
|
+
end
|
183
|
+
##
|
184
|
+
# +insert_zeroes(arr,count)+ fills empty spaces in the coefficient array
|
185
|
+
def insert_zeroes(arr, count)
|
186
|
+
loop do
|
187
|
+
arr << 0.0
|
188
|
+
count -= 1
|
189
|
+
break if count == 0
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
##
|
194
|
+
# +eval_by_cf(deg,val,cf)+ finds the result of polynom defined by array of coefficients
|
195
|
+
def eval_by_cf(deg, val, kf)
|
196
|
+
s = kf[deg]
|
197
|
+
i = deg - 1
|
198
|
+
loop do
|
199
|
+
s = s * val + kf[i]
|
200
|
+
i -= 1
|
201
|
+
break if i < 0
|
202
|
+
end
|
203
|
+
s
|
204
|
+
end
|
205
|
+
|
206
|
+
##
|
207
|
+
# +binary_root_finder(deg,edge_neg,edge_pos,cf)+ finds result of polynom using binary search
|
208
|
+
# +edge_neg+ and +edge_pos+ define the interval used for binary search
|
209
|
+
def binary_root_finder(deg, edge_neg, edge_pos, cf)
|
210
|
+
loop do
|
211
|
+
x = 0.5 * (edge_neg + edge_pos)
|
212
|
+
return x if [edge_pos, edge_neg].include? x
|
213
|
+
|
214
|
+
if eval_by_cf(deg, x, cf).positive?
|
215
|
+
edge_pos = x
|
216
|
+
else
|
217
|
+
edge_neg = x
|
218
|
+
end
|
219
|
+
end
|
220
|
+
[edge_pos, edge_neg]
|
221
|
+
end
|
222
|
+
|
223
|
+
##
|
224
|
+
# this method finds roots for each differentiated polynom and use previous ones to find next one
|
225
|
+
# roots located in interval, which has different sign in edges
|
226
|
+
# if we've found such interval then we begin binary_search on that interval to find root
|
227
|
+
# major is value, which we use to modulate +-infinite
|
228
|
+
def step_up(level, cf_dif, root_dif, cur_root_count)
|
229
|
+
major = find_major(level, cf_dif[level])
|
230
|
+
cur_root_count[level] = 0
|
231
|
+
# main loop
|
232
|
+
(0..cur_root_count[level - 1]).each { |i| step_up_loop([i, major, level, root_dif, cf_dif, cur_root_count]) }
|
233
|
+
end
|
234
|
+
|
235
|
+
def step_up_loop(arr_pack)
|
236
|
+
i, major, level, root_dif, cf_dif, cur_root_count = arr_pack
|
237
|
+
edge_left, left_val, sign_left = form_left([i, major, level, root_dif, cf_dif])
|
238
|
+
|
239
|
+
return if hit_root([level, edge_left, left_val, root_dif, cur_root_count])
|
240
|
+
|
241
|
+
edge_right, right_val, sigh_right = form_right([i, major, level, root_dif, cf_dif, cur_root_count])
|
242
|
+
|
243
|
+
return if hit_root([level, edge_right, right_val, root_dif, cur_root_count])
|
244
|
+
|
245
|
+
return if sigh_right == sign_left
|
246
|
+
edge_neg, edge_pos = sign_left.negative? ? [edge_left, edge_right] : [edge_right, edge_left]
|
247
|
+
root_dif[level][cur_root_count[level]] = binary_root_finder(level, edge_neg, edge_pos, cf_dif[level])
|
248
|
+
end
|
249
|
+
|
250
|
+
##
|
251
|
+
# +find_major(level,cf_dif)+ finds value, which we will use as infinity
|
252
|
+
def find_major(level, cf_dif)
|
253
|
+
major = 0.0
|
254
|
+
i = 0
|
255
|
+
loop do
|
256
|
+
s = cf_dif[i]
|
257
|
+
major = s if s > major
|
258
|
+
i += 1
|
259
|
+
break if i == level
|
260
|
+
end
|
261
|
+
major + 1.0
|
262
|
+
end
|
263
|
+
|
264
|
+
# check if we suddenly found root
|
265
|
+
def hit_root(arr_pack)
|
266
|
+
level, edge, val, root_dif, cur_roots_count = arr_pack
|
267
|
+
if val == 0
|
268
|
+
root_dif[level][cur_roots_count[level]] = edge
|
269
|
+
cur_roots_count[level] += 1
|
270
|
+
return true
|
271
|
+
end
|
272
|
+
false
|
273
|
+
end
|
274
|
+
|
275
|
+
# forming left edge for root search
|
276
|
+
def form_left(args_pack)
|
277
|
+
i, major, level, root_dif, kf_dif = args_pack
|
278
|
+
edge_left = i.zero? ? -major : root_dif[level - 1][i - 1]
|
279
|
+
left_val = eval_by_cf(level, edge_left, kf_dif[level])
|
280
|
+
sign_left = left_val.positive? ? 1 : -1
|
281
|
+
[edge_left, left_val, sign_left]
|
282
|
+
end
|
283
|
+
|
284
|
+
# forming right edge fro root search
|
285
|
+
def form_right(args_pack)
|
286
|
+
i, major, level, root_dif, kf_dif, cur_root_count = args_pack
|
287
|
+
edge_right = i == cur_root_count[level] ? major : root_dif[level - 1][i - 1]
|
288
|
+
|
289
|
+
right_val = eval_by_cf(level, edge_right, kf_dif[level])
|
290
|
+
sigh_right = right_val.positive? ? 1 : -1
|
291
|
+
[edge_right, right_val, sigh_right]
|
292
|
+
end
|
293
|
+
|
294
|
+
# evaluate real roots of polynom with order = deg
|
295
|
+
def polynom_real_roots(deg, coef)
|
296
|
+
coef_diff = Array.new(deg + 1)
|
297
|
+
root_diff = Array.new(deg + 1)
|
298
|
+
cur_root_count = Array.new(deg + 1)
|
299
|
+
coef_diff[deg] = rationing_polynom(deg, coef)
|
300
|
+
form_coef_diff(deg, coef_diff, cur_root_count, root_diff)
|
301
|
+
(2..deg).each { |i| step_up(i, coef_diff, root_diff, cur_root_count) }
|
302
|
+
roots_arr = []
|
303
|
+
root_diff[deg].each { |root| roots_arr << root }
|
304
|
+
roots_arr
|
305
|
+
end
|
306
|
+
|
307
|
+
|
308
|
+
def polynom_real_roots_by_str(deg, str)
|
309
|
+
cf = get_coef(str)
|
310
|
+
polynom_real_roots(deg, cf)
|
311
|
+
end
|
312
|
+
|
313
|
+
# rationing polynom
|
314
|
+
def rationing_polynom(deg, coef)
|
315
|
+
res = []
|
316
|
+
i = 0
|
317
|
+
loop do
|
318
|
+
res[i] = coef[i] / coef[deg].to_f
|
319
|
+
i += 1
|
320
|
+
break if i > deg
|
321
|
+
end
|
322
|
+
res
|
323
|
+
end
|
324
|
+
|
325
|
+
def init_coef_diff(coef_diff)
|
326
|
+
j = coef_diff.length - 2
|
327
|
+
loop do
|
328
|
+
coef_diff[j] = []
|
329
|
+
j -= 1
|
330
|
+
break if j < 0
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
def init_root_diff(cur_root_count)
|
335
|
+
j = cur_root_count.length - 1
|
336
|
+
loop do
|
337
|
+
cur_root_count[j] = []
|
338
|
+
j -= 1
|
339
|
+
break if j < 0
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
# forming array of differentiated polynoms, starting from source one
|
344
|
+
def form_coef_diff(deg, coef_diff, cur_root_count, root_dif)
|
345
|
+
init_coef_diff(coef_diff)
|
346
|
+
real_differentiration(deg, coef_diff)
|
347
|
+
cur_root_count[1] = 1
|
348
|
+
init_root_diff(root_dif)
|
349
|
+
root_dif[1][0] = -coef_diff[1][0] / coef_diff[1][1]
|
350
|
+
end
|
351
|
+
|
352
|
+
|
353
|
+
def real_differentiration(deg, coef_diff)
|
354
|
+
loop do
|
355
|
+
j = deg
|
356
|
+
loop do
|
357
|
+
coef_diff[deg - 1][j - 1] = coef_diff[deg][j] * j
|
358
|
+
|
359
|
+
j -= 1
|
360
|
+
break if j.zero?
|
361
|
+
end
|
362
|
+
deg -= 1
|
363
|
+
break if deg < 2
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
# transform array of coefficient to string
|
368
|
+
def coef_to_str(coef)
|
369
|
+
n = coef.length - 1
|
370
|
+
s = ''
|
371
|
+
(0..n).each { |i| s += coef_to_str_inner(coef, i, s) unless coef[i].zero? }
|
372
|
+
s
|
373
|
+
end
|
374
|
+
|
375
|
+
def coef_to_str_inner(coef, i, s)
|
376
|
+
i.zero? ? coef[i].to_s : "#{sign(coef[i])}#{coef[i]}*x**#{i}"
|
377
|
+
end
|
378
|
+
|
379
|
+
def sign(val)
|
380
|
+
if val > 0
|
381
|
+
'+'
|
382
|
+
else
|
383
|
+
''
|
384
|
+
end
|
385
|
+
end
|
386
|
+
|
387
|
+
|
388
|
+
##Gauss–Seidel method is an iterative method used to solve a system of linear equations
|
389
|
+
def gauss_seidel(a,b,eps)
|
390
|
+
n = a.length
|
391
|
+
x = Array.new(n,0)
|
392
|
+
|
393
|
+
@s1 = 0.0
|
394
|
+
@s2 = 0.0
|
395
|
+
@s3 = 0.0
|
396
|
+
|
397
|
+
converge = false
|
398
|
+
until converge
|
399
|
+
|
400
|
+
x_new = x
|
401
|
+
(0...n).each do |i|
|
402
|
+
helper_helper_1(i,a,x_new)
|
403
|
+
helper_helper_2(i,n,a,x)
|
404
|
+
|
405
|
+
x_new[i] = (b[i] - @s1 - @s2) / a[i][i]
|
406
|
+
end
|
407
|
+
|
408
|
+
extra_helper(n,x_new,x)
|
409
|
+
|
410
|
+
converge = Math::sqrt(@s3) <= eps ? true : false
|
411
|
+
x = x_new
|
412
|
+
|
413
|
+
end
|
414
|
+
round_helper(n,x)
|
415
|
+
|
416
|
+
x
|
417
|
+
end
|
418
|
+
|
419
|
+
|
420
|
+
def helper_helper_1(i,a,x_new)
|
421
|
+
|
422
|
+
(0..i).each do |j|
|
423
|
+
@s1 += a[i][j] * x_new[j]
|
424
|
+
end
|
425
|
+
end
|
426
|
+
|
427
|
+
def helper_helper_2(i,n,a,x)
|
428
|
+
(i+1...n).each do |j|
|
429
|
+
@s2 += a[i][j] * x[j]
|
430
|
+
end
|
431
|
+
|
432
|
+
end
|
433
|
+
|
434
|
+
def extra_helper(n,x_new,x)
|
435
|
+
(0...n).each do |i|
|
436
|
+
|
437
|
+
@s3 += x_new[i] - x[i]
|
438
|
+
@s3 = @s3 ** 2
|
439
|
+
end
|
440
|
+
@s3
|
441
|
+
end
|
442
|
+
|
443
|
+
def round_helper(n,x)
|
444
|
+
(0...n).each do |i|
|
445
|
+
x[i] = x[i].round
|
446
|
+
end
|
447
|
+
x
|
448
|
+
end
|
449
|
+
|
450
|
+
end
|
451
|
+
end
|
452
|
+
end
|
data/lib/algebra_diff.rb
ADDED
@@ -0,0 +1,258 @@
|
|
1
|
+
module Silicium
|
2
|
+
module Algebra
|
3
|
+
##
|
4
|
+
# differentiating methods
|
5
|
+
class Differentiation
|
6
|
+
|
7
|
+
##
|
8
|
+
# +differentiate(str)+ differentiates given string
|
9
|
+
def differentiate(str)
|
10
|
+
dif_2(str)
|
11
|
+
end
|
12
|
+
|
13
|
+
##
|
14
|
+
# +first_char_from(str, ch, ind)+
|
15
|
+
def first_char_from(str, ch, ind)
|
16
|
+
i = ind
|
17
|
+
while(str[i] != ch) && (i != str.length)
|
18
|
+
i+=1
|
19
|
+
end
|
20
|
+
i
|
21
|
+
end
|
22
|
+
|
23
|
+
##
|
24
|
+
# +find_closing_bracket(str,ind)+ finds the first closing bracket in str starting from given index
|
25
|
+
def find_closing_bracket(str, ind)
|
26
|
+
i = ind
|
27
|
+
kind_of_a_stack = 0
|
28
|
+
stop = true
|
29
|
+
while i != str.length && stop
|
30
|
+
stop, kind_of_a_stack = find_closing_bracket_inner(str,i,kind_of_a_stack)
|
31
|
+
return i if !stop
|
32
|
+
i+=1
|
33
|
+
end
|
34
|
+
i
|
35
|
+
end
|
36
|
+
|
37
|
+
def find_closing_bracket_inner(str,i,kind_of_a_stack)
|
38
|
+
if str[i] == '('
|
39
|
+
kind_of_a_stack += 1
|
40
|
+
elsif str[i] == ')'
|
41
|
+
kind_of_a_stack -= 1
|
42
|
+
return [false, kind_of_a_stack] if kind_of_a_stack == 0
|
43
|
+
end
|
44
|
+
[true, kind_of_a_stack]
|
45
|
+
end
|
46
|
+
|
47
|
+
##
|
48
|
+
# +fill_var_loop_inner(str,var_hash,ind_hash,ind)+
|
49
|
+
def fill_var_loop_inner(str,var_hash,ind_hash,ind)
|
50
|
+
var_hash[ind_hash] = str[ind+1,ind2-ind-1]
|
51
|
+
unless str[ind2+1] == '*' && str[ind2+2] == '*'
|
52
|
+
str = str[0,ind2+1] + '**1' + str[ind2+1,str.length-1]
|
53
|
+
end
|
54
|
+
str = str[0,ind] + '#' + ind_hash.to_s + str[ind2+1,str.length-ind2-1]
|
55
|
+
ind_hash += 1
|
56
|
+
[str, var_hash, ind_hash, ind]
|
57
|
+
end
|
58
|
+
|
59
|
+
##
|
60
|
+
#
|
61
|
+
def fill_variables_loop(str,var_hash,ind_hash,ind)
|
62
|
+
while ind != str.length
|
63
|
+
ind2 = find_closing_bracket(str,ind + 1)
|
64
|
+
if str[ind2].nil?
|
65
|
+
puts 'bad string'
|
66
|
+
else
|
67
|
+
str, var_hash, ind_hash, ind = fill_var_loop_inner(str,var_hash, ind_hash, ind)
|
68
|
+
end
|
69
|
+
ind = first_char_from(str, '(', 0)
|
70
|
+
end
|
71
|
+
[str, var_hash]
|
72
|
+
end
|
73
|
+
|
74
|
+
##
|
75
|
+
# +fill_variables(str)+
|
76
|
+
def fill_variables(str)
|
77
|
+
var_hash = {}
|
78
|
+
ind_hash = 0
|
79
|
+
if (str.include?('('))
|
80
|
+
ind = first_char_from(str, '(', 0)
|
81
|
+
str,var_hash = fill_variables_loop(str,var_hash,ind_hash,ind)
|
82
|
+
end
|
83
|
+
[str, var_hash]
|
84
|
+
end
|
85
|
+
|
86
|
+
##
|
87
|
+
# +extract_variables(str,var_hash)+
|
88
|
+
def extract_variables(str,var_hash)
|
89
|
+
ind = str[/#(\d+)/,1]
|
90
|
+
return str if (ind.nil?)
|
91
|
+
cur_var = var_hash[ind.to_i]
|
92
|
+
while(true)
|
93
|
+
str.sub!(/#(\d+)/,cur_var)
|
94
|
+
ind = str[/#(\d+)/,1]
|
95
|
+
return str if (ind.nil?)
|
96
|
+
cur_var = var_hash[ind.to_i]
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
##
|
101
|
+
# +run_difs(str)+ selects how to differentiate str
|
102
|
+
def run_difs(str)
|
103
|
+
return case str
|
104
|
+
when /\A-?\d+\Z/
|
105
|
+
'0'
|
106
|
+
when /\A-?\d+\*\*+\d+\Z/
|
107
|
+
'0'
|
108
|
+
when /\A(-?(\d+[*\/])*)x(\*\*\d+)?([*\/]\d+)*\Z/
|
109
|
+
dif_x($1,$3,$4)
|
110
|
+
when /\AMath::(.+)/
|
111
|
+
trigonometry_difs($1)
|
112
|
+
else
|
113
|
+
'0'
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
##
|
118
|
+
# +dif_x_a(a)+ goes for variable with a coefficient
|
119
|
+
def dif_x_a(a)
|
120
|
+
if a.nil? || a == "1*" || a == ''
|
121
|
+
a = ""
|
122
|
+
a_char = ""
|
123
|
+
else
|
124
|
+
a_char = a[a.length - 1]
|
125
|
+
a = eval(a[0,a.length-1]).to_s
|
126
|
+
end
|
127
|
+
[a, a_char]
|
128
|
+
end
|
129
|
+
|
130
|
+
##
|
131
|
+
# +dif_x_c(c)+ goes for constant
|
132
|
+
def dif_x_c(c)
|
133
|
+
if c.nil? || c == "*1" || c == "/1" || c == ''
|
134
|
+
c = ""
|
135
|
+
c_char = ""
|
136
|
+
else
|
137
|
+
c_char = c[0]
|
138
|
+
c = eval(c[1,c.length-1]).to_s
|
139
|
+
end
|
140
|
+
[c, c_char]
|
141
|
+
end
|
142
|
+
|
143
|
+
##
|
144
|
+
# +dif_x(a,b,c)+ goes for a*x^b*c
|
145
|
+
def dif_x(a,b,c)
|
146
|
+
a, a_char = dif_x_a(a)
|
147
|
+
c, c_char = dif_x_c(c)
|
148
|
+
if b.nil? || b == "**1"
|
149
|
+
return eval(a+a_char+"1"+c_char+c).to_s
|
150
|
+
else
|
151
|
+
new_b = '**' + (b[2,b.length - 2].to_i - 1).to_s
|
152
|
+
new_b = '' if new_b == '**1'
|
153
|
+
return eval(a+a_char+b[2,b.length - 2]+c_char+c).to_s + '*x' + new_b
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
##
|
158
|
+
# +trigonometry_difs(str)+ goes for trigonometry
|
159
|
+
def trigonometry_difs(str)
|
160
|
+
return case str
|
161
|
+
when /sin<(.+)>/
|
162
|
+
dif_sin($1)
|
163
|
+
when /cos<(.+)>/
|
164
|
+
dif_cos($1)
|
165
|
+
else
|
166
|
+
'0'
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
##
|
171
|
+
# +dif_sin(param)+ helps +trigonometry_difs(str)+
|
172
|
+
def dif_sin(param)
|
173
|
+
arg = dif_2(param)
|
174
|
+
return case arg
|
175
|
+
when '0'
|
176
|
+
'0'
|
177
|
+
when '1'
|
178
|
+
"Math::cos("+param+")"
|
179
|
+
when /\A\d+\Z/
|
180
|
+
"Math::cos("+param+")" + "*" + arg
|
181
|
+
else
|
182
|
+
"Math::cos("+param+")" + "*(" + arg + ")"
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
##
|
187
|
+
# +dif_cos(param)+ helps +trigonometry_difs(str)+
|
188
|
+
def dif_cos(param)
|
189
|
+
arg = dif_2(param)
|
190
|
+
return case arg
|
191
|
+
when '0'
|
192
|
+
'0'
|
193
|
+
when '1'
|
194
|
+
"-1*Math::sin("+param+")"
|
195
|
+
when /\A\d+\Z/
|
196
|
+
"Math::sin("+param+")" + "*" + eval(arg + '*(-1)').to_s
|
197
|
+
else
|
198
|
+
"-1*Math::sin("+param+")" + "*(" + arg + ")"
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
##
|
203
|
+
# +fix_str_for_dif(str)+ gets rid of useless brackets
|
204
|
+
def fix_str_for_dif(str)
|
205
|
+
str = fix_trig_brackets(str)
|
206
|
+
str = fix_useless_brackets(str)
|
207
|
+
end
|
208
|
+
|
209
|
+
##
|
210
|
+
# +fix_trig_brackets(str)+ helps +fix_str_for_dif(str)+ with trigonometry
|
211
|
+
def fix_trig_brackets(str)
|
212
|
+
reg = /(sin|cos)\((.+)\)/
|
213
|
+
cur_el_1 = str[reg,1]
|
214
|
+
cur_el_2 = str[reg,2]
|
215
|
+
while(!cur_el_1.nil?)
|
216
|
+
str.sub!(reg,cur_el_1+'<'+cur_el_2+'>')
|
217
|
+
cur_el_1 = str[reg,1]
|
218
|
+
cur_el_2 = str[reg,2]
|
219
|
+
end
|
220
|
+
str
|
221
|
+
end
|
222
|
+
|
223
|
+
##
|
224
|
+
# +fix_useless_brackets(str)+ helps +fix_str_for_dif(str)+ with extra brackets
|
225
|
+
def fix_useless_brackets(str)
|
226
|
+
reg1 = /\((-?\d+([*\/]\d)*|-?x([*\/]\d)*)\)/
|
227
|
+
cur_el = str[reg1,1]
|
228
|
+
while (!cur_el.nil?)
|
229
|
+
str.sub!(reg1,cur_el)
|
230
|
+
cur_el = str[reg1,1]
|
231
|
+
end
|
232
|
+
str
|
233
|
+
end
|
234
|
+
|
235
|
+
##
|
236
|
+
# +dif_2(str)+
|
237
|
+
def dif_2(str)
|
238
|
+
var_hash = Hash.new
|
239
|
+
str = fix_str_for_dif(str)
|
240
|
+
str, var_hash = fill_variables(str)
|
241
|
+
str.gsub!(/(?!^)-/,'+-')
|
242
|
+
summ = str.split('+')
|
243
|
+
if summ.length > 1
|
244
|
+
arr = summ.map{|x|dif_2(extract_variables(x,var_hash))}.select{|x|x!="0"}
|
245
|
+
if arr == []
|
246
|
+
return "0"
|
247
|
+
else
|
248
|
+
res = arr.join('+')
|
249
|
+
res.gsub!('+-','-')
|
250
|
+
return res
|
251
|
+
end
|
252
|
+
end
|
253
|
+
str = run_difs(str)
|
254
|
+
end
|
255
|
+
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|