From: jzakiya Date: 2009-12-26T06:45:11+09:00 Subject: Roots Module As a Christmas/Holiday present to the Ruby community I finally completed creating a versatile module which will find the accurate real and complex roots of +|- real values. This work evolved from concerns raised in this thread: http://groups.google.com/group/comp.lang.ruby/browse_thread/thread/9b8a417b8f3b79f3# from which I expanded on this work and incorporated some suggestions. I have tested this mixed in this module to classes Float, Integer, and BigDecimal successfully. I tested this on Ruby versions 1.9.1p243, 1.8.7p174 and Ruby Enterprise Edition 1.8.6 20090610. Constructive feedback is welcome. File: roots.rb ------------------------------------------------------ module Roots =begin Mixin Roots into Integer, BigDecimal, Float to add methods root & roots, to find the real|complex roots of +|- real values. Use syntax: val.root(n,{k}} root(n,k=0) n is root (1/n) exponent, integer > 0, k is nth root 1..n , integer >=0 If k not given default root returned, which are: for +val => real root |val**(1.0/n)| for -val => real root -|val**(1.0/n)| when n is odd for -val => first complex root -val when n is even for any val => first ccw root real|complex when k > 0 9.root(2); 8.root(3,1), -32.root(5,3), -100.43.root 6,6 Use syntax: val.roots(n,{opt}} roots(n,opt=0) n is root (1/n) exponent, integer > 0, opt area optional input options below 0 : default (no input), return array of n ccw root vals 'c'|'C': complex, return array of complex roots, nil if none 'e'|'E': even, return array even numbered roots, nil if none 'o'|'O': odd , return array odd numbered roots, nil if none 'i'|'I': imag, return array of imaginary roots, nil if none 'r'|'R': real, return array of real roots, nil if none 293481349432.roots(9); -892.roots(4,'real'); 22.2.roots 3,'i' =end require 'complex' include Math def root(n,k=0) # k=1..n; k=0 for default root raise "Root not integer >0" unless n.kind_of?(Integer) && n>=1 raise "Index k not an integer" unless k.kind_of?(Integer) return self if n == 1 || self == 0 mag = self.abs**n**-1 return rootn(mag,k-1,PI/n) if k>0 # nth root, k = 1..n, of any real return mag if self > 0 # pos real default return -1*mag if n&1 == 1 # neg real default, n odd return rootn(mag,0,PI/n) # neg real default, n even end def roots(n,opt=0) # returns an array of values, or nil if option not valid raise "Root not integer >0" unless n.kind_of?(Integer) && n>=1 raise "Invalid option" unless opt == 0 || opt =~ /^(c|e|i|o|r|C|E|I|O| R)/ return [self] if n == 1 || self == 0 mag = self.abs**n**-1 roots = []; theta = PI/n case opt when /^(o|O)/ # even roots 2,4,6... 0.step(n-1,2) {|k| roots << rootn(mag,k,theta)} when /^(e|E)/ # odd roots 1,3,5... 1.step(n-1,2) {|k| roots << rootn(mag,k,theta)} when /^(r|R)/ # real roots Complex(x,0) =(x+i0) n.times {|k| x=rootn(mag,k,theta); roots << x if x.imag == 0} when /^(i|I)/ # imaginry roots Complex(0,x) = (0+ix) n.times {|k| x=rootn(mag,k,theta); roots << x if x.real == 0} when /^(c|C)/ # complex roots Complex(x,y) = (x+iy) n.times {|k| x=rootn(mag,k,theta); roots << x unless x.imag == 0 || x.real == 0} else n.times {|k| roots << rootn(mag,k,theta)} end return roots.empty? ? nil : roots end =begin Ruby currently produces incorrect values for x|y axis angles. cos PI/2 => 6.12303176911189e-17 sin PI => 1.22460635382238e-16 cos 3*PI/2 => -1.83690953073357e-16 sin 2*PI => -2.44921970764475e-16 These all should be 0.0, which causes incorrect root values there. I 'fix' these errors by clipping absolute values less than a value I call TRiG-EPSILON so they produces the correct results. Extract this code into separate file and 'require' into your apps to get correct|exact results for x|y axis angles and still get same accurrcay for extremely small delta angles to the x|y axis. cosine(89.9999999999*PI/180) => 1.74534333112399e-11 cosine(90.0*PI/180) => 0.0 cosine(90.0000000001*PI/180) => -1.74543140899798e-12 =end protected TRIG_EPSILON = 1e-15 def sine(x); y=sin(x); y.abs < TRIG_EPSILON ? 0.0:y end def cosine(x); y=cos(x); y.abs k = 0..n-1 angle = self > 0.0 ? 2*(k+1)*theta : (2*k+1)*theta end def rootn(mag,k,theta) a = angle(k,theta); mag*Complex(cosine(a),sine(a)) end end