Roots Module

J

jzakiya

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
 
J

jzakiya

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 ifx.imag == 0}
          when /^(i|I)/  # imaginry roots  Complex(0,x) =(0+ix)
            n.times {|k|
                  x=rootn(mag,k,theta); roots << x ifx.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
 
P

Phillip Gawlowski

On Dec 25, 4:42 pm, jzakiya<[email protected]> 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

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

jzakiya

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:

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
 
S

Seebs

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
 
J

jzakiya

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
 
P

Phillip Gawlowski

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

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

jzakiya

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            # posreal default
        return -1*mag if n&1 == 1         # negreal 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 ifoption
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

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

Phillip Gawlowski

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

Are you intentionally resistant to advice given?

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

jzakiya

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 :/
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(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.
 
J

jzakiya

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

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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,049
Latest member
Allen00Reed

Latest Threads

Top