[Shootout-list] Ruby nbody correction

Jabari Zakiya jzakiya@mail.com
Mon, 21 Mar 2005 02:00:55 -0500


###########################################
# The Computer Language Benchmark Shootout
# http://shootout.alioth.debian.org
# nbody Ruby benchmark
#
# original code by Martin DeMello
# modified by Jabari Zakiya 3/20/05

include Math

SOLAR_MASS =3D 4*PI*PI
DAYS_PER_YEAR =3D 365.24

Vector3D =3D Struct.new("Vector3D", :x, :y, :z)

class Vector3D

  def *(val)
    Vector3D.new(*self.map {|i| i * val})
  end

  def /(val)
    Vector3D.new(*self.map {|i| i / val})
  end

  #in-place add with scale
  # a.adds(b, s) -> a +=3D b*s

  def adds(other, scale)
    self[0] +=3D other[0]*scale; self[1] +=3D other[1]*scale
    self[2] +=3D other[2]*scale
  end

  def subs(other, scale)
    self[0] -=3D other[0]*scale; self[1] -=3D other[1]*scale
    self[2] -=3D other[2]*scale
  end

  def magnitude
    x=3Dself[0]; y=3Dself[1]; z=3Dself[2]
    sqrt(x*x + y*y + z*z)
  end

  # |self - other|
  def dmag(other)
    x=3Dself[0]-other[0]; y=3Dself[1]-other[1]; z=3Dself[2]-other[2]
    sqrt(x*x + y*y + z*z)
  end
end

class Planet
  attr_accessor :pos, :v, :mass

  def initialize(x, y, z, vx, vy, vz, mass)
    @pos =3D Vector3D.new(x, y, z)
    @v =3D Vector3D.new(vx, vy, vz) * DAYS_PER_YEAR
    @mass =3D mass * SOLAR_MASS
  end

  def distance(other)
    self.pos.dmag(other.pos)
  end
end

jupiter =3D Planet.new(
   4.84143144246472090e+00,
   -1.16032004402742839e+00,
   -1.03622044471123109e-01,
   1.66007664274403694e-03,
   7.69901118419740425e-03,
   -6.90460016972063023e-05,
   9.54791938424326609e-04)

saturn =3D Planet.new(
   8.34336671824457987e+00,
   4.12479856412430479e+00,
   -4.03523417114321381e-01,
   -2.76742510726862411e-03,
   4.99852801234917238e-03,
   2.30417297573763929e-05,
   2.85885980666130812e-04)

uranus =3D Planet.new(
   1.28943695621391310e+01,
   -1.51111514016986312e+01,
   -2.23307578892655734e-01,
   2.96460137564761618e-03,
   2.37847173959480950e-03,
   -2.96589568540237556e-05,
   4.36624404335156298e-05)

neptune =3D Planet.new(
   1.53796971148509165e+01,
   -2.59193146099879641e+01,
   1.79258772950371181e-01,
   2.68067772490389322e-03,
   1.62824170038242295e-03,
   -9.51592254519715870e-05,
   5.15138902046611451e-05)

sun =3D Planet.new(0, 0, 0, 0, 0, 0, 1)

class Array
  def each_pair
    a =3D []
    each_index {|i|
      ((i+1)...length).each {|j|
        yield at(i), at(j)
      }
    }
  end
end

bodies =3D [sun, jupiter, saturn, uranus, neptune]

class << bodies
  def advance(dt)
    mag =3D m1 =3D m2 =3D nil
    each_pair {|b1, b2|
      d =3D b1.distance(b2)
      mag =3D dt/(d*d*d)

      m1 =3D b1.mass * mag
      m2 =3D b2.mass * mag

      b1.v.adds(b2.pos, m2)
      b1.v.subs(b1.pos, m2)
      b2.v.adds(b1.pos, m1)
      b2.v.subs(b2.pos, m1)
    }

    each {|b| b.pos.adds(b.v, dt)}
  end

  def energy
    e =3D 0
    each {|b| e +=3D 0.5 * b.mass * (b.v.magnitude ** 2) }
    each_pair {|b1, b2| e -=3D (b1.mass * b2.mass) / b1.distance(b2) }
    e
  end

  def offset_momentum
    p =3D Vector3D.new(0,0,0)
    sun =3D self[0]
    each {|b| p.adds(b.v, b.mass) }
    sun.v.subs(p, 1.0/sun.mass)
  end
end

bodies.offset_momentum
puts bodies.energy
Integer(ARGV[0]).times { bodies.advance(0.01) }
puts bodies.energy
=20
--=20
___________________________________________________________
Sign-up for Ads Free at Mail.com
http://promo.mail.com/adsfreejump.htm