module Silicium module Algebra ## # differentiating methods class Differentiation ## # +differentiate(str)+ differentiates given string def differentiate(str) dif_2(str) end ## # +first_char_from(str, ch, ind)+ def first_char_from(str, ch, ind) i = ind while(str[i] != ch) && (i != str.length) i+=1 end i end ## # +find_closing_bracket(str,ind)+ finds the first closing bracket in str starting from given index def find_closing_bracket(str, ind) i = ind kind_of_a_stack = 0 stop = true while i != str.length && stop stop, kind_of_a_stack = find_closing_bracket_inner(str,i,kind_of_a_stack) return i if !stop i+=1 end i end def find_closing_bracket_inner(str,i,kind_of_a_stack) if str[i] == '(' kind_of_a_stack += 1 elsif str[i] == ')' kind_of_a_stack -= 1 return [false, kind_of_a_stack] if kind_of_a_stack == 0 end [true, kind_of_a_stack] end ## # +fill_var_loop_inner(str,var_hash,ind_hash,ind)+ def fill_var_loop_inner(str,var_hash,ind_hash,ind) var_hash[ind_hash] = str[ind+1,ind2-ind-1] unless str[ind2+1] == '*' && str[ind2+2] == '*' str = str[0,ind2+1] + '**1' + str[ind2+1,str.length-1] end str = str[0,ind] + '#' + ind_hash.to_s + str[ind2+1,str.length-ind2-1] ind_hash += 1 [str, var_hash, ind_hash, ind] end ## # def fill_variables_loop(str,var_hash,ind_hash,ind) while ind != str.length ind2 = find_closing_bracket(str,ind + 1) if str[ind2].nil? puts 'bad string' else str, var_hash, ind_hash, ind = fill_var_loop_inner(str,var_hash, ind_hash, ind) end ind = first_char_from(str, '(', 0) end [str, var_hash] end ## # +fill_variables(str)+ def fill_variables(str) var_hash = {} ind_hash = 0 if (str.include?('(')) ind = first_char_from(str, '(', 0) str,var_hash = fill_variables_loop(str,var_hash,ind_hash,ind) end [str, var_hash] end ## # +extract_variables(str,var_hash)+ def extract_variables(str,var_hash) ind = str[/#(\d+)/,1] return str if (ind.nil?) cur_var = var_hash[ind.to_i] while(true) str.sub!(/#(\d+)/,cur_var) ind = str[/#(\d+)/,1] return str if (ind.nil?) cur_var = var_hash[ind.to_i] end end ## # +run_difs(str)+ selects how to differentiate str def run_difs(str) return case str when /\A-?\d+\Z/ '0' when /\A-?\d+\*\*+\d+\Z/ '0' when /\A(-?(\d+[*\/])*)x(\*\*\d+)?([*\/]\d+)*\Z/ dif_x($1,$3,$4) when /\AMath::(.+)/ trigonometry_difs($1) else '0' end end ## # +dif_x_a(a)+ goes for variable with a coefficient def dif_x_a(a) if a.nil? || a == "1*" || a == '' a = "" a_char = "" else a_char = a[a.length - 1] a = eval(a[0,a.length-1]).to_s end [a, a_char] end ## # +dif_x_c(c)+ goes for constant def dif_x_c(c) if c.nil? || c == "*1" || c == "/1" || c == '' c = "" c_char = "" else c_char = c[0] c = eval(c[1,c.length-1]).to_s end [c, c_char] end ## # +dif_x(a,b,c)+ goes for a*x^b*c def dif_x(a,b,c) a, a_char = dif_x_a(a) c, c_char = dif_x_c(c) if b.nil? || b == "**1" return eval(a+a_char+"1"+c_char+c).to_s else new_b = '**' + (b[2,b.length - 2].to_i - 1).to_s new_b = '' if new_b == '**1' return eval(a+a_char+b[2,b.length - 2]+c_char+c).to_s + '*x' + new_b end end ## # +trigonometry_difs(str)+ goes for trigonometry def trigonometry_difs(str) return case str when /sin<(.+)>/ dif_sin($1) when /cos<(.+)>/ dif_cos($1) else '0' end end ## # +dif_sin(param)+ helps +trigonometry_difs(str)+ def dif_sin(param) arg = dif_2(param) return case arg when '0' '0' when '1' "Math::cos("+param+")" when /\A\d+\Z/ "Math::cos("+param+")" + "*" + arg else "Math::cos("+param+")" + "*(" + arg + ")" end end ## # +dif_cos(param)+ helps +trigonometry_difs(str)+ def dif_cos(param) arg = dif_2(param) return case arg when '0' '0' when '1' "-1*Math::sin("+param+")" when /\A\d+\Z/ "Math::sin("+param+")" + "*" + eval(arg + '*(-1)').to_s else "-1*Math::sin("+param+")" + "*(" + arg + ")" end end ## # +fix_str_for_dif(str)+ gets rid of useless brackets def fix_str_for_dif(str) str = fix_trig_brackets(str) str = fix_useless_brackets(str) end ## # +fix_trig_brackets(str)+ helps +fix_str_for_dif(str)+ with trigonometry def fix_trig_brackets(str) reg = /(sin|cos)\((.+)\)/ cur_el_1 = str[reg,1] cur_el_2 = str[reg,2] while(!cur_el_1.nil?) str.sub!(reg,cur_el_1+'<'+cur_el_2+'>') cur_el_1 = str[reg,1] cur_el_2 = str[reg,2] end str end ## # +fix_useless_brackets(str)+ helps +fix_str_for_dif(str)+ with extra brackets def fix_useless_brackets(str) reg1 = /\((-?\d+([*\/]\d)*|-?x([*\/]\d)*)\)/ cur_el = str[reg1,1] while (!cur_el.nil?) str.sub!(reg1,cur_el) cur_el = str[reg1,1] end str end ## # +dif_2(str)+ def dif_2(str) var_hash = Hash.new str = fix_str_for_dif(str) str, var_hash = fill_variables(str) str.gsub!(/(?!^)-/,'+-') summ = str.split('+') if summ.length > 1 arr = summ.map{|x|dif_2(extract_variables(x,var_hash))}.select{|x|x!="0"} if arr == [] return "0" else res = arr.join('+') res.gsub!('+-','-') return res end end str = run_difs(str) end end end end