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<TRIG_EPSILON ? 0.0:y end
  def tangent(x); sine(x)/cosine(x) end # not used here but more
correct

  def angle(k,theta) # roots 1..n --> 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

Corrections:
change access control from 'protected' to 'private'
as those definitions shouldn't show up as methods in
the mixedin classes.

Also put at top of comment:
Author: Jabari Zakiya, 2009-12-25

···

On Dec 25, 4:42 pm, jzakiya <jzak...@mail.com> wrote:

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/9b\.\.\.

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<TRIG_EPSILON ? 0.0:y end
def tangent(x); sine(x)/cosine(x) end # not used here but more
correct

def angle(k,theta) # roots 1..n --> 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

To make life easier for you and the Ruby community:

rubyforge.org offers free hosting for Ruby projects, including source control, bug trackers, and such.

(Google Code offers similar, as does SourceForge, and I'm sure a sizeable number of other websites.)

You can also create a Gem, and push it to Gemcutter.org, so others can use your library.

This makes it easy for you to maintain your code and distribute it, and easy for us to use it.

···

On 25.12.2009 23:15, jzakiya wrote:

On Dec 25, 4:42 pm, jzakiya<jzak...@mail.com> wrote:

Corrections:
change access control from 'protected' to 'private'
as those definitions shouldn't show up as methods in
the mixedin classes.

Also put at top of comment:
Author: Jabari Zakiya, 2009-12-25

--
Phillip Gawlowski
Wishing everyone a Merry Christmas and happy holidays!

Correction for method conflict in Float:

In 1.9.1 there is conflict in Float with method 'angle'
thus, rename my private definitions as such:

  def angle_n(k,theta) # roots 1..n --> k = 0..n-1
    self > 0 ? 2*(k+1)*theta : (2*k+1)*theta
  end

  def rootn(mag,k,theta) # nth root of a real value
    a = angle_n(k,theta); mag*Complex(cosine(a),sine(a))
  end

···

On Dec 25, 5:13 pm, jzakiya <jzak...@mail.com> wrote:

On Dec 25, 4:42 pm, jzakiya <jzak...@mail.com> wrote:

> 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/9b\.\.\.

> 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<TRIG_EPSILON ? 0.0:y end
> def tangent(x); sine(x)/cosine(x) end # not used here but more
> correct

> def angle(k,theta) # roots 1..n --> 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

Corrections:
change access control from 'protected' to 'private'
as those definitions shouldn't show up as methods in
the mixedin classes.

Also put at top of comment:
Author: Jabari Zakiya, 2009-12-25

And you still don't understand that, by definition, you're introducing
larger errors than you're correcting.

Please go learn something about floating point math.

-s

···

On 2009-12-26, jzakiya <jzakiya@gmail.com> wrote:

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

--
Copyright 2009, all wrongs reversed. Peter Seebach / usenet-nospam@seebs.net
| Seebs.Net <-- lawsuits, religion, and funny pictures
Fair game (Scientology) - Wikipedia <-- get educated!

Actually, just eliminate 'def angle_n' since it was only
use in rootn, and just have rootn be this:

  def rootn(mag,k,theta) # nth root of a real value
    angle_n = self > 0 ? 2*(k+1)*theta : (2*k+1)*theta
  mag*Complex(cosine(angle_n),sine(angle_n))
  end

···

On Dec 26, 2:18 am, jzakiya <jzak...@gmail.com> wrote:

On Dec 25, 5:13 pm, jzakiya <jzak...@mail.com> wrote:

> On Dec 25, 4:42 pm, jzakiya <jzak...@mail.com> wrote:

> > 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/9b\.\.\.

> > 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<TRIG_EPSILON ? 0.0:y end
> > def tangent(x); sine(x)/cosine(x) end # not used here but more
> > correct

> > def angle(k,theta) # roots 1..n --> 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

> Corrections:
> change access control from 'protected' to 'private'
> as those definitions shouldn't show up as methods in
> the mixedin classes.

> Also put at top of comment:
> Author: Jabari Zakiya, 2009-12-25

Correction for method conflict in Float:

In 1.9.1 there is conflict in Float with method 'angle'
thus, rename my private definitions as such:

def angle_n(k,theta) # roots 1..n --> k = 0..n-1
self > 0 ? 2*(k+1)*theta : (2*k+1)*theta
end

def rootn(mag,k,theta) # nth root of a real value
a = angle_n(k,theta); mag*Complex(cosine(a),sine(a))
end

Get source control up and running, and post announcements for gem-packaged releases, please.

···

On 26.12.2009 08:35, jzakiya wrote:

Actually, just eliminate 'def angle_n' since it was only
use in rootn, and just have rootn be this:

   def rootn(mag,k,theta) # nth root of a real value
     angle_n = self> 0 ? 2*(k+1)*theta : (2*k+1)*theta
  mag*Complex(cosine(angle_n),sine(angle_n))
   end

--
Phillip Gawlowski

OK, here's the final (for now) Roots module, all
cleaned up and tweaked.

New changes:
I made roots method return just an array in all cases.
So now, if an option doesn't have members, it returns
an empty array, , instead of nil.

This allows you to now consistently do things like:
How many real roots for x? -- x.roots(n,'real').size
If there are none, you get '0', instead of exception
error of NoMethod for Nilclass for size.

I also set TRIG-EPSILON = 2.5e-16 to get it as close
to the largest incorrect value for cos|sin for the x|y
axis angles, which is sin(2*PI)=-2.44921970764475e-16
on my Intel P4 cpu system. Set for equivalent results
for your cpu (32|64-bit) environment (AMD, PPC, etc).

I also noticed when Roots is mixedin to Integer it will
also take care of Bignums too, but you have to mixin to
Float separately to take care of them too.

Finally, for those who don't know|care|believe this
stuff matters, or has any uses, check out:

Project Euler -- http://projecteuler.net/

Ruby users and aficionados have a pretty strong throng
there (along with other languages), and to get the
"correct answers" exactness matters.

So here is the current final Roots module, cleaned up
with (hopefully) no typos, and formatted here to cleanly
fit within the column limitations for posting here.

If/when I get the desire, I may create a gem out of it,
with DOC markup, and all the rest.

Finally, today is December 26, the first day of Kwanzaa,
which celebrates the principle of Umoja -- unity.

Peace and Blessings to All.

File roots.rb

···

On Dec 26, 2:33 am, jzakiya <jzak...@gmail.com> wrote:

On Dec 26, 2:18 am, jzakiya <jzak...@gmail.com> wrote:

> On Dec 25, 5:13 pm, jzakiya <jzak...@mail.com> wrote:

> > On Dec 25, 4:42 pm, jzakiya <jzak...@mail.com> wrote:

> > > 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/9b\.\.\.

> > > 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<TRIG_EPSILON ? 0.0:y end
> > > def tangent(x); sine(x)/cosine(x) end # not used here but more
> > > correct

> > > def angle(k,theta) # roots 1..n --> 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

> > Corrections:
> > change access control from 'protected' to 'private'
> > as those definitions shouldn't show up as methods in
> > the mixedin classes.

> > Also put at top of comment:
> > Author: Jabari Zakiya, 2009-12-25

> Correction for method conflict in Float:

> In 1.9.1 there is conflict in Float with method 'angle'
> thus, rename my private definitions as such:

> def angle_n(k,theta) # roots 1..n --> k = 0..n-1
> self > 0 ? 2*(k+1)*theta : (2*k+1)*theta
> end

> def rootn(mag,k,theta) # nth root of a real value
> a = angle_n(k,theta); mag*Complex(cosine(a),sine(a))
> end

Actually, just eliminate 'def angle_n' since it was only
use in rootn, and just have rootn be this:

def rootn(mag,k,theta) # nth root of a real value
angle_n = self > 0 ? 2*(k+1)*theta : (2*k+1)*theta
mag*Complex(cosine(angle_n),sine(angle_n))
end

--------------------------------
module Roots
=begin
Author: Jabari Zakiya, Original: 2009-12-25
Revision-1: 2009-12-26

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 ccw 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 ccw complex root when n is even

94835805813.root(2); -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, optional string input, are:
   0 : default (no input), return array of n ccw roots
'c'|'C': complex, return array of complex roots
'e'|'E': even, return array even numbered roots
'o'|'O': odd , return array odd numbered roots
'i'|'I': imag, return array of imaginary roots
'r'|'R': real, return array of real roots
An empty array is returned for an opt with no members.

9334943.roots(9); -89.roots(4,'real'); 2.2.roots 3,'Im'

Can ask: How many real roots of: x.roots(n,'real').size

Ruby currently gives 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, causing wrong root values there.
I 'fix' by clipping absolute values less than a value
I call TRIG-EPSILON so they produces the correct results.

Put trig code into separate file and 'require' into apps
to get correct|exact results for x|y axis angles and get
same accuracy for very small angles close to each 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

  require 'complex'
  include Math

  def root(n,k=0) # return nth root k=1.n, or default k=0
    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 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) # return array of roots, or empty
    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 # all n roots
      n.times {|k| roots << rootn(mag,k,theta)}
    end
    return roots
  end

  private # don't show as methods in mixin class
  TRIG_EPSILON = 2.5e-16
  def sine(x); y=sin(x); y.abs < TRIG_EPSILON ? 0.0:y end
  def cosine(x); y=cos(x); y.abs<TRIG_EPSILON ? 0.0:y end
  def tangent(x); sine(x)/cosine(x) end # better than tan

  def rootn(mag,k,theta) # nth root of a real value
    angle_n = self > 0 ? 2*(k+1)*theta : (2*k+1)*theta
    mag*Complex(cosine(angle_n),sine(angle_n))
  end
end

Are you intentionally resistant to advice given?

Get a damn project page going, and use *that* to maintain your code, *not* ruby-talk.

···

On 26.12.2009 21:40, jzakiya wrote:

OK, here's the final (for now) Roots module, all
cleaned up and tweaked.

--
Phillip Gawlowski

In looking for a nice home for my Roots module it seems
mathn.rb is a good fit because it adds the functions
sqrt>rsqrt to the Math module (do sqrt x, not x.sqrt).
So I copied my roots.rb file into the same dir under lib
which has mathn.rb, and add the follow code to mathn.rb
at the top, under it's 'require' list: [Ruby 1.9.1p243]

require 'roots'

class Integer; include Roots end
class Float; include Roots end
class Rational
  include Roots
  def self.root(x); self.to_f.root end
  def self.roots(x); self.to_f.roots end
end

I had to do Rational like this because
Rational(x/y).root(n) produced a NoMethodError, but
Rational.to_f.root(n) takes care of that. I would like
Complex(x,y).root(n) too, but I haven't figured out a
nice way to do that yet, to match: sqrt Complex(x,y)
though you can do: Complex(x,y)**n**-1 for all roots.
It would be nice to have the simpler syntax, though.

So now in irb if you load this in:

require 'mathn'
include Math # to also get functions sqrt|rsqrt

So

sqrt -9 => (0+3i)
-9.root 2 => (0.0+3.0i)

and

sqrt Rational(25/81) => (5/9)
Rational(25/81)**(1/2) => (5/9)
Rational(25/81)**2**-1 => (5/9)
Rational(25/81).root 2 => (5/9) # but
Rational(25/81)**0.5 => 0.55555555555556

along with Rational(x/y).roots(n,opt)

Everything seems to work with only one known QUIRK.

mathn.rb adds this to classes Fixnum and Bignum:

class Fixnum|Bignum
  remove_method :confused:
  alias / quo

  alias power! ** unless defined?(0.power!)

  def ** (other)
    if self < 0 && other.round != other
      Complex(self, 0.0) ** other
    else
      power!(other)
    end
  end
end

The line: alias power! ** unless defined?(0.power!)

causes an error for Bignums, I get for (X).root(n) a
NoMethodError: undefined method `power!' for (X):Bignum
but not when I do a Fixnum (x).root(n).

I can do (X).0.root[s](n) to get around this problem,
and redefine these methods in Bignum like for Rationals,
but that's a hack for a seemingly simple resolution.

When I comment out the: # unless defined?(0.power!)
in class Bignum the problem goes away.

Also, if I don't load mathn.rb, and just load roots.rb
and then mixin Roots in Integer and Float, as I did
originally, I can do Bignums with no problems.

Any ideas on what's the problem with Bignum class here?

Thus, by doing this in mathn.rb, you get all the roots
of real and rational numbers, and not just the sqrts.

Added ability to now do roots Complex numbers.
After thinking about it, it was elegantly simple to do. :slight_smile:

So now, module Roots can be mixed in with the numeric classes
Integer, Float, Complex, and Rational to find the roots of all
the numeric number classes, eg:

Complex(2,11).root(3) => (2.0+1.0i) [Ruby 1.9.1p243]

Today is the last day of 2009, and the penultimate day of
Kwanzaa -- Kuumba/Creativity.

May the New Year bring much needed Justice, Peace, and Love to, and
from, the Human species.

File roots.rb

···

--------------------------------
module Roots
=begin
Author: Jabari Zakiya, Original: 2009-12-25
Revision-2: 2009-12-31

Mixin Roots into Integer, Float, Complex, Rational
to add methods root & roots, to find the roots of
all real and complex numbers.

Use syntax: val.root(n,{k})
root(n,k=0) n is root 1/n exponent, integer > 0
            k is nth ccw 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 ccw complex root when n is even

9.root(2); -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, optional string input, are:
   0 : default (no input), return array of n ccw roots
'c'|'C': complex, return array of complex roots
'e'|'E': even, return array even numbered roots
'o'|'O': odd , return array odd numbered roots
'i'|'I': imag, return array of imaginary roots
'r'|'R': real, return array of real roots
An empty array is returned for an opt with no members.

9334943.roots(9); -89.roots(4,'real'); 2.2.roots 3,'Im'

Can ask: How many real roots of: x.roots(n,'real').size
What's the 3rd 5th root of (4+9i): Complex(4,9).root(5,3)

Ruby currently gives 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, causing wrong root values there.
I 'fix' by clipping absolute values less than a value
I call TRIG-EPSILON so they produces the correct results.

Put trig code into separate file and 'require' into apps
to get correct|exact results for x|y axis angles and get
same accuracy for very small angles close to each 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

  require 'complex'
  include Math

  def root(n,k=0) # return nth root k=1.n, or default k=0
    raise "Root n not an integer > 0" unless
                             n.kind_of?(Integer) && n>0
    raise "Index k not an integer >= 0" unless
                             k.kind_of?(Integer) && k>=0
    return self if n == 1 || self == 0
    mag = abs**n**-1
    return rootn(mag,arg/n,k>0 ? k-1:0,n) if
                             kind_of?(Complex)
    return rootn(mag,PI/n,k-1) if k>0 # nth root any real
    return mag if self > 0 # pos real default
    return -mag if n&1 == 1 # neg real default, n odd
    return rootn(mag,PI/n) # neg real default, n even
  end

  def roots(n,opt=0) # return array of roots, or empty
    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 = abs**n**-1
    theta = kind_of?(Complex) ? arg/n : PI/n
    roots = []
    case opt
    when /^(o|O)/ # even roots 2,4,6...
      0.step(n-1,2) {|k| roots << rootn(mag,theta,k,n)}
    when /^(e|E)/ # odd roots 1,3,5...
      1.step(n-1,2) {|k| roots << rootn(mag,theta,k,n)}
    when /^(r|R)/ # real roots Complex(x,0) = (x+i0)
      n.times {|k|
        x=rootn(mag,theta,k,n); roots << x if x.imag == 0}
    when /^(i|I)/ # imaginry roots Complex(0,y) = (0+iy)
      n.times {|k|
        x=rootn(mag,theta,k,n); roots << x if x.real == 0}
    when /^(c|C)/ # complex roots Complex(x,y) = (x+iy)
      n.times {|k|
        x=rootn(mag,theta,k,n); roots << x unless
        x.imag == 0 || x.real == 0}
    else # all n roots
      n.times {|k| roots << rootn(mag,theta,k,n)}
    end
    return roots
  end

  private # don't show as methods in mixin class
  TRIG_EPSILON = 2.5e-16
  def sine(x); y=sin(x); y.abs < TRIG_EPSILON ? 0.0:y end
  def cosine(x); y=cos(x); y.abs<TRIG_EPSILON ? 0.0:y end
  def tangent(x); sine(x)/cosine(x) end # better than tan

  def rootn(mag,theta,k=0,n=1) # nth root of real|complex
    angle_n = kind_of?(Complex) ? theta+(2*k*PI)/n :
              self > 0 ? 2*(k+1)*theta : (2*k+1)*theta
    mag*Complex(cosine(angle_n),sine(angle_n))
  end
end