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.
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