# [shootout] n body problem

M

#### Martin DeMello

Here's a first pass at the n body problem in the shootout - I've tried
to strike a balance between idiomatic and fast, the main optimisation
being to remove all object creation from the advance() loop as far as
possible, in favour of methods that updated an existing object.

http://shootout.alioth.debian.org/benchmark.php?test=nbody&lang=all&sort=fullcpu

-----------------------------------------------------------------------

include Math

SOLAR_MASS = 4*PI*PI
DAYS_PER_YEAR = 365.24

Vector3D = 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

# a.adds(b, s) -> a += b*s

3.times {|i| self += other * scale}
end

def subs(other, scale)
end

def magnitude
d = self.inject(0) {|a,v| a + v*v}
sqrt(d)
end

# |self - other|
def dmag(other)
sqrt((0...3).inject(0) {|a, i| d = (self - other); a + d * d})
end
end

class Planet
attr_accessor os, :v, :mass

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

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

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

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

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

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

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

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

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

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

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

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

end

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

def offset_momentum
p = Vector3D.new(0,0,0)
sun = self[0]
sun.v.subs(p, 1.0/sun.mass)
end
end

require 'benchmark'

Benchmark.bm {|x|
x.report {
bodies.offset_momentum
puts bodies.energy
puts bodies.energy
}
}

I

#### igouy

Martin said:
Here's a first pass at the n body problem in the shootout - I've tried
to strike a balance between idiomatic and fast, the main optimisation
being to remove all object creation from the advance() loop as far as
possible, in favour of methods that updated an existing object.

http://shootout.alioth.debian.org/b...---------------------------------------------

include Math

SOLAR_MASS = 4*PI*PI
DAYS_PER_YEAR = 365.24

Vector3D = 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

# a.adds(b, s) -> a += b*s

3.times {|i| self += other * scale}
end

def subs(other, scale)
end

def magnitude
d = self.inject(0) {|a,v| a + v*v}
sqrt(d)
end

# |self - other|
def dmag(other)
sqrt((0...3).inject(0) {|a, i| d = (self - other); a + d * d})
end
end

class Planet
attr_accessor os, :v, :mass

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

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

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

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

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

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

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

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

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

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

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

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

end

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

def offset_momentum
p = Vector3D.new(0,0,0)
sun = self[0]
sun.v.subs(p, 1.0/sun.mass)
end
end

require 'benchmark'

Benchmark.bm {|x|
x.report {
bodies.offset_momentum
puts bodies.energy
puts bodies.energy
}
}

Excellent!

Before we can use it I'm afraid you have to contribute it to the
shootout, we can't just skim programs from other places.

Please send programs to the mailing list or send them using the message
form or email them to igouy2

http://shootout.alioth.debian.org/faq.php?sort=fullcpu

best wishes, Isaac

M

#### Martin DeMello

Before we can use it I'm afraid you have to contribute it to the
shootout, we can't just skim programs from other places.

Will do, once the list has had time to improve it. This is just a first
stab at the problem.

martin

G

#### gabriele renzi

Excellent!

Before we can use it I'm afraid you have to contribute it to the
shootout, we can't just skim programs from other places.

Please send programs to the mailing list or send them using the message
form or email them to igouy2

http://shootout.alioth.debian.org/faq.php?sort=fullcpu

best wishes, Isaac

is the input from the form actually received?
I think I sent one or two comments/impèlementations in the past (such as
takfp, which is a trivial one) but I had the impression they where not
considered (I thhought contribution out of the ml were not considered)

I

#### igouy

gabriele said:

is the input from the form actually received?

Hmmm I'll do a test and see if we receive what we should.

I think I sent one or two comments/impèlementations in the past (such as
takfp, which is a trivial one) but I had the impression they where not
considered (I thhought contribution out of the ml were not
considered)

Sending code explicitly as a contribution to the shootout mailing list
or to our email seems fine - just say that you are giving it for
publication (shootout uses a revised BSD license).

We do sometimes lose stuff and generally make mistakes, and we do
really want contributions from knowledgeable language users.

I

#### igouy

Hmmm I'll do a test and see if we receive what we should.

The message form seems to operate just fine.
Contribute programs with the message form or the mailing list or email.

J

#### jzakiya

Martin said:
Here's a first pass at the n body problem in the shootout - I've tried
to strike a balance between idiomatic and fast, the main optimisation
being to remove all object creation from the advance() loop as far as
possible, in favour of methods that updated an existing object.

http://shootout.alioth.debian.org/b...---------------------------------------------

include Math

SOLAR_MASS = 4*PI*PI
DAYS_PER_YEAR = 365.24

Vector3D = 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

# a.adds(b, s) -> a += b*s

3.times {|i| self += other * scale}
end

def subs(other, scale)
end

def magnitude
d = self.inject(0) {|a,v| a + v*v}
sqrt(d)
end

# |self - other|
def dmag(other)
sqrt((0...3).inject(0) {|a, i| d = (self - other); a + d * d})
end
end

class Planet
attr_accessor os, :v, :mass

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

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

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

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

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

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

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

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

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

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

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

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

end

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

def offset_momentum
p = Vector3D.new(0,0,0)
sun = self[0]
sun.v.subs(p, 1.0/sun.mass)
end
end

require 'benchmark'

Benchmark.bm {|x|
x.report {
bodies.offset_momentum
puts bodies.energy
puts bodies.energy
}

I just sent this to the Shootout list
=====================================================

Here is a much faster nbody Ruby benchmark.
For N=1_000_000 (1 million) the original
code took 41 minutes to execute on my system.
(AMD K-7, 600Mhz, 640MB, Mandrake 10.1 Ofl, Ruby 1.8.2)
This modified code executed the benchmark in 17 minutes.
The modification consists of unrolling the loops in
4 methods in the Vector3D class which do vector math.

This modification probably would speed up all the
dynamic languages (Perl, Python, PHP, etc). For C/C++
with good optimizing compilers, loop unrolling may
already be taking place, but it's something that could
be tested for.

The benchmark originally listed the benchmark
as having an error. However, the original code
ran on my system with no errors.

The "error" was the erroneous execution of the benchmark,
resulting from N set to 1, and not 1 million as required.

Jabari Zakiya

###########################################
# 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 = 4*PI*PI
DAYS_PER_YEAR = 365.24

Vector3D = 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

# a.adds(b, s) -> a += b*s

self[0] += other[0]*scale; self[1] += other[1]*scale
self[2] += other[2]*scale
end

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

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

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

class Planet
attr_accessor os, :v, :mass

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

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

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

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

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

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

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

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

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

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

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

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

end

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

def offset_momentum
p = Vector3D.new(0,0,0)
sun = self[0]
sun.v.subs(p, 1.0/sun.mass)
end
end

bodies.offset_momentum
puts bodies.energy
puts bodies.energy

I

#### Isaac Gouy

The modification consists of unrolling the loops in
4 methods in the Vector3D class which do vector math.

Manual loop unrolling is one optimization too many.

We have a preference for plain vanilla programs - we're trying to
compare language implementations, not programmers.

J

#### jzakiya

Isaac said:
Manual loop unrolling is one optimization too many.

We have a preference for plain vanilla programs - we're trying to
compare language implementations, not programmers.

I'm a litlle tired, and will respond more fully later, but I felt
a prompt response was needed now.

There is no requirement in any language, or as a paradigm of
coding practice, that a looping structure be applied to every
potential instance where its use is possible. To "unroll" a
"loop" is a coding "optimization" only if you start from the
reference point of the use of a loop structure. If you never
use a loop structure in the first place, and instead perform a
"direct implementation" of a function from the start, then that
is the reference point of further modification.

When engaging in the exercise of "benchmarking" inherent in
that exercise is to identify coding techniques and paradigms
within the idiom of the language being used to perform the
stated tasks as fast as possible.

Benchmarking is not about predetermining an arbitrary coding
to a every language, irrespective of the negative consequences
it has to producing the fastest program in that language.

Ruby is a native dynamic interpretive language. A user, to
acquire the highest performance possible from the language, has
to understand the consequences of this fact. One thing this mean
is that a user has to be "the compiler" at times, in order to
squeeze the highest possible performance out of the language.
Ruby is not C/C++, with highly optimized compilers, which do
a lot of the thinking for the programmer to extract performance
from source code.

Thus, as in the original nbody code, you could produce the
magnitude of a 3-dimensional vector as follows:

def magnitude
d = self.inject(0) {|a,v| a + v*v}
sqrt(d)
end

but the original code took 41 minutes to run on my system
(AMD K-7, 600Mhz, 640MB, Mandrake 10.1 Ofl, Ruby 1.8.2)

However, the inject method is not "performance optimum".

So, I first rewrote the looping structure to make it faster:

def magnitude
d = 0.0: (0..2).each {|i| d += self**2}
sqrt(d)
end

and this was even a little faster

def magnitude
d = 0.0; 3.times {|i| d += self**2}
sqrt(d)
end

At this point, by optimizing the most performance efficient
Ruby looping structure, I got the benchmark task down from
41 minutes to 27 minutes. Then the little lightbulb in my

Instead of looking at the task as an exercise in the use of
programming structures, I looked at what was the physics that
was trying to be done. After I realized what the physics
was, I just wrote code to "directly implement" the physics.

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

Not only is this code accurate, its simple, understandable, AND
creates a MUCH faster program, which is the main point for a
benchmark. Using "direct implementation" of just 4 function methods
reduced the revised benchmark (which I posted) time to 17 minutes,
from the 'original code' time of 41 minutes.

Further testing has reduced the time to 15 minutes, using the
following code for the magnitude, tested to be the fastest so far.

def magnitude
sqrt(self[0]**2 + self[1]**2 + self[2]**2)
end

Besides executing the fastest in Ruby 1.8.2, you can't
produce code any shorter and simpler than this.

So by what codified set of programmnig rules can anyone say, with
sustainable validity, that this coding paradigm is inappropriate?

In fact, in the "real world" where money, time, efficiency, and
people's lives matter, "direct implementation" of functions, in
my experience, is standard procedure, especially where speed
matters. This is especially true with languages such as Forth,
my native software language.

Now the Ruby nbody benchmark performs 1 million iterations in
15 minutes instead of 41 minutes, which is 2.5 faster than the
original code!

Ruby is a great language to get real work done with. It isn't
natively the fastest, but it needn't be naively coded to create
unnecessarily slow performance.

I've just shown one simple technique to speed up Ruby 1.8.2.
Hopefully, with Ruby 2.0 and beyond, native performance will
inherently increase. Until then, if performance matters, then
you have to think a little bit about what you are really trying
to do, and do it the "best" way to optimize Ruby performance.

Thus, I disagree that benchmarks are about applying some
arbitrary programming paradigm to a set of languages, just so
the "look" of the programs are similar. Benchmarks should
illustrate how a given language can be used to perform a
given task (the benchmark) in that language, particularly
using the best programming techniques and idioms unique to
that language.

Thus, to me, the primary essence of benchmarking is comparing
how different languages can be uniquely applied to optimally
perform the same tasks, and comparing those results.

I'll expand more on these points in the future, but I hope these
comments explain my point-of-view, and reasoning, on this issue.

Jabari Zakiya

I

#### Isaac Gouy

Thus, I disagree that benchmarks are about applying some
arbitrary programming paradigm to a set of languages, just so
the "look" of the programs are similar.

You're welcome to your opinion - and we're happy to include the program
as an 'alternative'.

Note: The C# Mono #2 program is listed as an alternative because a
member of the Mono development team wanted a plain vanilla comparison
between C# Mono and other languages - hand-optimizing index-access with
a local variable ruined that comparison.

R

#### Randy Kramer

You're welcome to your opinion - and we're happy to include the program
as an 'alternative'.

Just to inject my \$.02, I'd say both types of benchmarks are useful. IMHO,
it's unlikely you'll find a benchmark that does exactly what you want. If
you know you have to loop, a benchmark that compares looping in several
languages is useful. On the other hand, if you are not aware of some more
elegant approach (than looping, in this case) that might apply to your
problem, and might be faster as well as more elegant, the "different
approach" benchmark is useful.

Randy Kramer

C

#### Carlos

You're welcome to your opinion - and we're happy to include the program
as an 'alternative'.

Don't you think this is a little unfair?

Maybe you didn't examine the program very well. The original author
(ab?)used the fact that a Struct can be used as an array. So, o.x can be
referred as o[0], o.y as o[1], and o.z as o[2]. But it is not necessary to
do so, and probably it is better to refer to its members by name.

Look, if one would change (for example):

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

to:

def magnitude
sqrt(x*x + y*y + z*z)
end

wouldn't you agree that the looping isn't necessary?

Note that none of the other languages use arrays to represent the vector,
and none of them use loops to make these calculations.

G

#### gabriele renzi

Isaac Gouy ha scritto:
Manual loop unrolling is one optimization too many.

We have a preference for plain vanilla programs - we're trying to
compare language implementations, not programmers.

I think there is a thing that you may have not noted and could make your
ideas change.
As far as I can tell, the loop unrolling that was done makes the ruby
program actually more similar in implementation to what the other
The Java and C# code is full of:
bodies.x += something with bodies.x;
bodies.y += something with bodies.y
bodies.z += something with bodies.z;

while the original ruby code used the inherited
Enumerable methods (inject, map et al), wich rely on the ability of
struct classes to enumerate it's fields, doing things like[1]:
bodies.method {|v| something with v}

This way the code becomes cleaner and maybe more idiomatic, but it
suffers from a lot more variable creations than it actually needs, and
it is more similar to Java code where you'd use getDeclaredFields or
something like that.

Thus, I think this thing does not qualify as a too clever optimization,
and it should be included in the shootout. Oh, and sorry for posting
here, I'll join the SO ml as soon as possible since I have some more

[1] actually the computation in ruby code is done in the Vector3d class,
so it's more like:
body.something_with

M

#### Martin DeMello

Carlos said:
Maybe you didn't examine the program very well. The original author
(ab?)used the fact that a Struct can be used as an array. So, o.x can be
referred as o[0], o.y as o[1], and o.z as o[2]. But it is not necessary to
do so, and probably it is better to refer to its members by name.

I sort of agree with you - looping over a Struct is an unnecessary
slowdown. I was thinking of a Struct as ruby's equivalent of a tuple
when I wrote that code - i.e. I didn't so much want fields named x, y
and z as I wanted an array restricted to have three members.

martin

I

#### Isaac Gouy

Carlos said:
Don't you think this is a little unfair?

Maybe you didn't examine the program very well.

Here's where it gets unfair - our decisions about Shootout are
extremely time-constrained, so we never examine the programs very well.

Lots of opportunity for misunderstandings and mistakes - so be brief
and be clear and explain the programs for someone who's never seen the
language ;-)

-snip-
Note that none of the other languages use arrays to represent the vector,
and none of them use loops to make these calculations.

Jabari: This is what I needed to know! (Brief and to the point.)

I read the comment in the code to mean that Jabari unrolled the main
loops - I hadn't even noticed the Ruby array/structs thing.

I

#### Isaac Gouy

gabriele said:
Isaac Gouy ha scritto:

I think there is a thing that you may have not noted and could make your
ideas change.

That's usually true ;-)
As far as I can tell, the loop unrolling that was done

afaict the loop unrolling that was done operates over the fields of
each struct, rather than over the array of bodies - is that correct?

(Of course, this program will probably still Timeout... and meanwhile
those Tcl guys have contributed 8 more programs...)

best wishes, Isaac

S

#### Steffen Pedersen

Hi List,

and installed this morning:
One-Click Installer - Windows, ruby182-14.exe
from http://rubyforge.org

Having a problem with keyboard using IRB (Interactive
Ruby Shell).

I'm using Win 2000 Pro (english language) with a danish
keyboard layout.

Some characters e.g. "[", "]" and "@" are generated
using the right ALT-key in combination with a "normal"
key. (In all 10 characters marked on the front of the
keys). This DOES NOT work in IRB :-(

Can (and will) somebody help me out?

Regards Steffen (of Denmark)

P

#### Premshree Pillai

Unfortunately, we don't share the same home.

I

#### Isaac Gouy

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.