silicium 0.0.20 → 0.0.21

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +3 -3
  3. data/.gitignore +13 -13
  4. data/.rakeTasks +8 -0
  5. data/.travis.yml +28 -25
  6. data/CODE_OF_CONDUCT.md +74 -74
  7. data/Gemfile +8 -8
  8. data/LICENSE.txt +21 -21
  9. data/Makefile +269 -269
  10. data/README.md +588 -46
  11. data/Rakefile +16 -16
  12. data/bin/console +14 -14
  13. data/bin/setup +8 -8
  14. data/docs/Object.html +117 -117
  15. data/docs/README_md.html +142 -142
  16. data/docs/Silicium/Combinatorics.html +270 -270
  17. data/docs/Silicium/Dice/Polyhedron.html +315 -315
  18. data/docs/Silicium/Dice/PolyhedronSet.html +321 -321
  19. data/docs/Silicium/Dice.html +99 -99
  20. data/docs/Silicium/Error.html +106 -106
  21. data/docs/Silicium/Geometry/Line2dCanon.html +243 -243
  22. data/docs/Silicium/Geometry/VariablesOrderException.html +106 -106
  23. data/docs/Silicium/Geometry.html +940 -940
  24. data/docs/Silicium/GraphVisualizer.html +226 -0
  25. data/docs/Silicium/Graphs/GraphError.html +106 -106
  26. data/docs/Silicium/Graphs/OrientedGraph.html +901 -775
  27. data/docs/Silicium/Graphs/UnorientedGraph.html +237 -284
  28. data/docs/Silicium/Graphs.html +374 -164
  29. data/docs/Silicium/IntegralDoesntExistError.html +106 -106
  30. data/docs/Silicium/NumericalIntegration.html +521 -521
  31. data/docs/Silicium/Optimization.html +629 -639
  32. data/docs/Silicium/Plotter/Image.html +297 -297
  33. data/docs/Silicium/Plotter.html +186 -186
  34. data/docs/Silicium.html +101 -101
  35. data/docs/created.rid +9 -9
  36. data/docs/css/fonts.css +167 -167
  37. data/docs/css/rdoc.css +619 -619
  38. data/docs/index.html +134 -132
  39. data/docs/js/darkfish.js +84 -84
  40. data/docs/js/navigation.js +105 -105
  41. data/docs/js/search.js +110 -110
  42. data/docs/js/search_index.js +1 -1
  43. data/docs/js/search_index.js.gz +0 -0
  44. data/docs/js/searcher.js +229 -229
  45. data/docs/table_of_contents.html +697 -608
  46. data/lib/algebra.rb +452 -0
  47. data/lib/algebra_diff.rb +258 -0
  48. data/lib/geometry/figure.rb +62 -0
  49. data/lib/geometry.rb +290 -236
  50. data/lib/geometry3d.rb +270 -0
  51. data/lib/graph/dfs.rb +42 -0
  52. data/lib/graph/kruskal.rb +36 -0
  53. data/lib/graph/scc.rb +97 -0
  54. data/lib/graph.rb +350 -164
  55. data/lib/graph_visualizer.rb +287 -0
  56. data/lib/ml_algorithms.rb +181 -0
  57. data/lib/numerical_integration.rb +184 -147
  58. data/lib/optimization.rb +209 -144
  59. data/lib/plotter.rb +256 -96
  60. data/lib/polynomial_division.rb +132 -0
  61. data/lib/polynomial_interpolation.rb +94 -0
  62. data/lib/regression.rb +120 -0
  63. data/lib/silicium/adding.rb +37 -0
  64. data/lib/silicium/conversions.rb +23 -0
  65. data/lib/silicium/multi.rb +82 -0
  66. data/lib/silicium/sparse.rb +76 -0
  67. data/lib/silicium/sugar.rb +37 -0
  68. data/lib/silicium/trans.rb +26 -0
  69. data/lib/silicium/version.rb +3 -3
  70. data/lib/silicium.rb +5 -5
  71. data/lib/theory_of_probability.rb +240 -226
  72. data/lib/topological_sort.rb +50 -0
  73. data/oriented_graph.png +0 -0
  74. data/plot.png +0 -0
  75. data/silicium.gemspec +38 -39
  76. 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
@@ -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