summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--tests/examplefiles/eval.rs606
-rw-r--r--tests/examplefiles/rust_example.rs235
2 files changed, 606 insertions, 235 deletions
diff --git a/tests/examplefiles/eval.rs b/tests/examplefiles/eval.rs
new file mode 100644
index 00000000..17e585a0
--- /dev/null
+++ b/tests/examplefiles/eval.rs
@@ -0,0 +1,606 @@
+// -------------------------------------------------------------------------------------------------
+// Rick, a Rust intercal compiler. Save your souls!
+//
+// Copyright (c) 2015 Georg Brandl
+//
+// This program is free software; you can redistribute it and/or modify it under the terms of the
+// GNU General Public License as published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
+// even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along with this program;
+// if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+// -------------------------------------------------------------------------------------------------
+
+/// Interprets INTERCAL source.
+///
+/// The evaluator is used when rick is called with `-i`, or when the compiler generates
+/// the output while compiling (in the constant-output case).
+
+use std::fmt::{ Debug, Display };
+use std::io::Write;
+use std::u16;
+
+use err::{ Res, IE123, IE129, IE252, IE275, IE555, IE633, IE774, IE994 };
+use ast::{ self, Program, Stmt, StmtBody, ComeFrom, Expr, Var, VType };
+use stdops::{ Bind, Array, write_number, read_number, check_chance, check_ovf, pop_jumps,
+ get_random_seed, mingle, select, and_16, and_32, or_16, or_32, xor_16, xor_32 };
+
+
+/// Represents a value (either 16-bit or 32-bit) at runtime.
+#[derive(Clone, PartialEq, Eq, Debug)]
+pub enum Val {
+ I16(u16),
+ I32(u32),
+}
+
+impl Val {
+ /// Cast as a 16-bit value; returns an error if 32-bit and too big.
+ pub fn as_u16(&self) -> Res<u16> {
+ match *self {
+ Val::I16(v) => Ok(v),
+ Val::I32(v) => {
+ if v > (u16::MAX as u32) {
+ return IE275.err();
+ }
+ Ok(v as u16)
+ }
+ }
+ }
+
+ /// Cast as a 32-bit value; always succeeds.
+ pub fn as_u32(&self) -> u32 {
+ match *self {
+ Val::I16(v) => v as u32,
+ Val::I32(v) => v
+ }
+ }
+
+ /// Cast as an usize value; always succeeds.
+ pub fn as_usize(&self) -> usize {
+ self.as_u32() as usize
+ }
+
+ /// Create from a 32-bit value; will select the smallest possible type.
+ pub fn from_u32(v: u32) -> Val {
+ if v & 0xFFFF == v {
+ Val::I16(v as u16)
+ } else {
+ Val::I32(v)
+ }
+ }
+}
+
+/// The state of the interpreter's evaluator.
+pub struct Eval<'a> {
+ /// Program to execute.
+ program: &'a Program,
+ /// Stream to use for printing output.
+ stdout: &'a mut Write,
+ /// Whether to print debugging output during execution.
+ debug: bool,
+ /// Variable bindings for the four types of variables.
+ spot: Vec<Bind<u16>>,
+ twospot: Vec<Bind<u32>>,
+ tail: Vec<Bind<Array<u16>>>,
+ hybrid: Vec<Bind<Array<u32>>>,
+ /// The infamous NEXT stack, capable of holding 80 elements.
+ jumps: Vec<ast::LogLine>,
+ /// Abstain counter for each statement.
+ abstain: Vec<u32>,
+ /// Binary I/O "tape" state.
+ last_in: u8,
+ last_out: u8,
+ /// Random number generator state.
+ rand_st: u32,
+ /// Counts the number of executed statements.
+ stmt_ctr: usize,
+}
+
+/// Represents the control flow effect of an executed statement.
+enum StmtRes {
+ /// normal execution, next statement
+ Next,
+ /// jump around, from DO ... NEXT
+ Jump(usize),
+ /// jump back, from RESUME
+ Back(usize),
+ /// start from the first statement, from TRY AGAIN
+ FromTop,
+ /// end the program, from GIVE UP
+ End,
+}
+
+impl<'a> Eval<'a> {
+ /// Construct a new evaluator.
+ pub fn new(program: &'a Program, stdout: &'a mut Write, debug: bool,
+ random: bool) -> Eval<'a> {
+ let abs = program.stmts.iter().map(|stmt| stmt.props.disabled as u32).collect();
+ let nvars = (program.var_info.0.len(),
+ program.var_info.1.len(),
+ program.var_info.2.len(),
+ program.var_info.3.len());
+ Eval {
+ program: program,
+ stdout: stdout,
+ debug: debug,
+ spot: vec![Bind::new(0); nvars.0],
+ twospot: vec![Bind::new(0); nvars.1],
+ tail: vec![Bind::new(Array::empty()); nvars.2],
+ hybrid: vec![Bind::new(Array::empty()); nvars.3],
+ jumps: Vec::with_capacity(80),
+ rand_st: if random { get_random_seed() } else { 0 },
+ abstain: abs,
+ last_in: 0,
+ last_out: 0,
+ stmt_ctr: 0,
+ }
+ }
+
+ /// Interpret the program. Returns either the number of executed statements,
+ /// or an error (RtError).
+ pub fn eval(&mut self) -> Res<usize> {
+ let mut pctr = 0; // index of current statement
+ let program = self.program.clone();
+ let nstmts = program.stmts.len();
+ loop {
+ // check for falling off the end
+ if pctr >= nstmts {
+ // if the last statement was a TRY AGAIN, falling off the end is fine
+ if let StmtBody::TryAgain = program.stmts[program.stmts.len() - 1].body {
+ break;
+ }
+ return IE633.err();
+ }
+ self.stmt_ctr += 1;
+ let stmt = &program.stmts[pctr];
+ // execute statement if not abstained
+ if self.abstain[pctr] == 0 {
+ // check execution chance
+ let (passed, rand_st) = check_chance(stmt.props.chance, self.rand_st);
+ self.rand_st = rand_st;
+ if passed {
+ // try to eval this statement
+ let res = match self.eval_stmt(stmt) {
+ // on error, set the correct line number and bubble up
+ Err(mut err) => {
+ err.set_line(stmt.props.onthewayto);
+ // special treatment for NEXT
+ if let StmtBody::DoNext(n) = stmt.body {
+ if let Some(i) = program.labels.get(&n) {
+ err.set_line(program.stmts[*i as usize].props.srcline);
+ }
+ }
+ return Err(err);
+ }
+ Ok(res) => res
+ };
+ // handle control flow effects
+ match res {
+ StmtRes::Next => { }
+ StmtRes::Jump(n) => {
+ self.jumps.push(pctr as u16); // push the line with the NEXT
+ pctr = n;
+ continue; // do not increment or check for COME FROMs
+ }
+ StmtRes::Back(n) => {
+ pctr = n; // will be incremented below after COME FROM check
+ }
+ StmtRes::FromTop => {
+ pctr = 0; // start from the beginning, do not push any stack
+ continue;
+ }
+ StmtRes::End => break,
+ }
+ }
+ }
+ // if we are on the line with the compiler bug, error out
+ if pctr == self.program.bugline as usize {
+ return IE774.err_with(None, stmt.props.onthewayto);
+ }
+ // try to determine if we have to go to a COME FROM statement
+ // (note: in general, program.stmts[pctr] != stmt)
+ //
+ // the static COME FROM is always a possibility
+ let mut maybe_next = program.stmts[pctr].comefrom;
+ // the complicated case: evaluate all computed-come-from expressions
+ let my_label = program.stmts[pctr].props.label;
+ if program.uses_complex_comefrom && my_label > 0 {
+ for (i, stmt) in program.stmts.iter().enumerate() {
+ if let StmtBody::ComeFrom(ComeFrom::Expr(ref e)) = stmt.body {
+ let v = try!(try!(self.eval_expr(e)).as_u16());
+ if v == my_label {
+ // as soon as we have multiple candidates, we can bail out
+ if maybe_next.is_some() {
+ return IE555.err();
+ }
+ maybe_next = Some(i as u16);
+ }
+ }
+ }
+ }
+ // check for COME FROMs from this line
+ if let Some(next) = maybe_next {
+ let next = next as usize;
+ // check for abstained COME FROM
+ if self.abstain[next] == 0 {
+ // the COME FROM can also have a % chance
+ let (passed, rand_st) = check_chance(program.stmts[next].props.chance,
+ self.rand_st);
+ self.rand_st = rand_st;
+ if passed {
+ pctr = next;
+ continue;
+ }
+ }
+ }
+ // no COME FROM, normal execution
+ pctr += 1;
+ }
+ Ok(self.stmt_ctr)
+ }
+
+ /// Interpret a single statement.
+ fn eval_stmt(&mut self, stmt: &Stmt) -> Res<StmtRes> {
+ if self.debug {
+ println!("\nExecuting Stmt #{} (state before following)", self.stmt_ctr);
+ self.dump_state();
+ println!("{}", stmt);
+ }
+ match stmt.body {
+ StmtBody::Calc(ref var, ref expr) => {
+ let val = try!(self.eval_expr(expr));
+ try!(self.assign(var, val));
+ Ok(StmtRes::Next)
+ }
+ StmtBody::Dim(ref var, ref exprs) => {
+ try!(self.array_dim(var, exprs));
+ Ok(StmtRes::Next)
+ }
+ StmtBody::DoNext(n) => {
+ match self.program.labels.get(&n) {
+ // too many jumps on stack already?
+ Some(_) if self.jumps.len() >= 80 => IE123.err(),
+ Some(i) => Ok(StmtRes::Jump(*i as usize)),
+ None => IE129.err(),
+ }
+ }
+ StmtBody::ComeFrom(_) => {
+ // nothing to do here at runtime
+ Ok(StmtRes::Next)
+ }
+ StmtBody::Resume(ref expr) => {
+ let n = try!(self.eval_expr(expr)).as_u32();
+ // this expect() is safe: if the third arg is true, there will
+ // be no Ok(None) returns
+ let next = try!(pop_jumps(&mut self.jumps, n, true, 0))
+ .expect("https://xkcd.com/378/ ?!");
+ Ok(StmtRes::Back(next as usize))
+ }
+ StmtBody::Forget(ref expr) => {
+ let n = try!(self.eval_expr(expr)).as_u32();
+ try!(pop_jumps(&mut self.jumps, n, false, 0));
+ Ok(StmtRes::Next)
+ }
+ StmtBody::Ignore(ref vars) => {
+ for var in vars {
+ self.set_rw(var, false);
+ }
+ Ok(StmtRes::Next)
+ }
+ StmtBody::Remember(ref vars) => {
+ for var in vars {
+ self.set_rw(var, true);
+ }
+ Ok(StmtRes::Next)
+ }
+ StmtBody::Stash(ref vars) => {
+ for var in vars {
+ self.stash(var);
+ }
+ Ok(StmtRes::Next)
+ }
+ StmtBody::Retrieve(ref vars) => {
+ for var in vars {
+ try!(self.retrieve(var));
+ }
+ Ok(StmtRes::Next)
+ }
+ StmtBody::Abstain(ref expr, ref whats) => {
+ let f: Box<Fn(u32) -> u32> = if let Some(ref e) = *expr {
+ let n = try!(self.eval_expr(e)).as_u32();
+ box move |v: u32| v.saturating_add(n)
+ } else {
+ box |_| 1
+ };
+ for what in whats {
+ self.abstain(what, &*f);
+ }
+ Ok(StmtRes::Next)
+ }
+ StmtBody::Reinstate(ref whats) => {
+ for what in whats {
+ self.abstain(what, &|v: u32| v.saturating_sub(1));
+ }
+ Ok(StmtRes::Next)
+ }
+ StmtBody::ReadOut(ref vars) => {
+ for var in vars {
+ match *var {
+ // read out whole array
+ Expr::Var(ref var) if var.is_dim() => {
+ try!(self.array_readout(var));
+ }
+ // read out single var or array element
+ Expr::Var(ref var) => {
+ let varval = try!(self.lookup(var));
+ try!(write_number(self.stdout, varval.as_u32(), 0));
+ }
+ // read out constant
+ Expr::Num(_, v) => try!(write_number(self.stdout, v, 0)),
+ // others will not be generated
+ _ => return IE994.err(),
+ };
+ }
+ Ok(StmtRes::Next)
+ }
+ StmtBody::WriteIn(ref vars) => {
+ for var in vars {
+ if var.is_dim() {
+ // write in whole array
+ try!(self.array_writein(var));
+ } else {
+ // write in single var or array element
+ let n = try!(read_number(0));
+ try!(self.assign(var, Val::from_u32(n)));
+ }
+ }
+ Ok(StmtRes::Next)
+ }
+ // this one is only generated by the constant-program optimizer
+ StmtBody::Print(ref s) => {
+ if let Err(_) = self.stdout.write(&s) {
+ return IE252.err();
+ }
+ Ok(StmtRes::Next)
+ }
+ StmtBody::TryAgain => Ok(StmtRes::FromTop),
+ StmtBody::GiveUp => Ok(StmtRes::End),
+ StmtBody::Error(ref e) => Err((*e).clone()),
+ }
+ }
+
+ /// Evaluate an expression to a value.
+ fn eval_expr(&self, expr: &Expr) -> Res<Val> {
+ match *expr {
+ Expr::Num(vtype, v) => match vtype {
+ VType::I16 => Ok(Val::I16(v as u16)),
+ VType::I32 => Ok(Val::I32(v)),
+ },
+ Expr::Var(ref var) => self.lookup(var),
+ Expr::Mingle(ref vx, ref wx) => {
+ let v = try!(self.eval_expr(vx)).as_u32();
+ let w = try!(self.eval_expr(wx)).as_u32();
+ let v = try!(check_ovf(v, 0));
+ let w = try!(check_ovf(w, 0));
+ Ok(Val::I32(mingle(v, w)))
+ }
+ Expr::Select(vtype, ref vx, ref wx) => {
+ let v = try!(self.eval_expr(vx));
+ let w = try!(self.eval_expr(wx));
+ if vtype == VType::I16 {
+ Ok(Val::I16(select(v.as_u32(), try!(w.as_u16()) as u32) as u16))
+ } else {
+ Ok(Val::I32(select(v.as_u32(), w.as_u32())))
+ }
+ }
+ Expr::And(vtype, ref vx) => {
+ let v = try!(self.eval_expr(vx));
+ match vtype {
+ VType::I16 => Ok(Val::I16(and_16(try!(v.as_u16()) as u32) as u16)),
+ VType::I32 => Ok(Val::I32(and_32(v.as_u32()))),
+ }
+ }
+ Expr::Or(vtype, ref vx) => {
+ let v = try!(self.eval_expr(vx));
+ match vtype {
+ VType::I16 => Ok(Val::I16(or_16(try!(v.as_u16()) as u32) as u16)),
+ VType::I32 => Ok(Val::I32(or_32(v.as_u32()))),
+ }
+ }
+ Expr::Xor(vtype, ref vx) => {
+ let v = try!(self.eval_expr(vx));
+ match vtype {
+ VType::I16 => Ok(Val::I16(xor_16(try!(v.as_u16()) as u32) as u16)),
+ VType::I32 => Ok(Val::I32(xor_32(v.as_u32()))),
+ }
+ }
+ Expr::RsNot(ref vx) => {
+ let v = try!(self.eval_expr(vx));
+ Ok(Val::I32(!v.as_u32()))
+ }
+ Expr::RsAnd(ref vx, ref wx) => {
+ let v = try!(self.eval_expr(vx));
+ let w = try!(self.eval_expr(wx));
+ Ok(Val::I32(v.as_u32() & w.as_u32()))
+ }
+ Expr::RsOr(ref vx, ref wx) => {
+ let v = try!(self.eval_expr(vx));
+ let w = try!(self.eval_expr(wx));
+ Ok(Val::I32(v.as_u32() | w.as_u32()))
+ }
+ Expr::RsXor(ref vx, ref wx) => {
+ let v = try!(self.eval_expr(vx));
+ let w = try!(self.eval_expr(wx));
+ Ok(Val::I32(v.as_u32() ^ w.as_u32()))
+ }
+ Expr::RsRshift(ref vx, ref wx) => {
+ let v = try!(self.eval_expr(vx));
+ let w = try!(self.eval_expr(wx));
+ Ok(Val::I32(v.as_u32() >> w.as_u32()))
+ }
+ Expr::RsLshift(ref vx, ref wx) => {
+ let v = try!(self.eval_expr(vx));
+ let w = try!(self.eval_expr(wx));
+ Ok(Val::I32(v.as_u32() << w.as_u32()))
+ }
+ // Expr::RsEqual(ref vx, ref wx) => {
+ // let v = try!(self.eval_expr(vx));
+ // let w = try!(self.eval_expr(wx));
+ // Ok(Val::I32((v.as_u32() == w.as_u32()) as u32))
+ // }
+ Expr::RsNotEqual(ref vx, ref wx) => {
+ let v = try!(self.eval_expr(vx));
+ let w = try!(self.eval_expr(wx));
+ Ok(Val::I32((v.as_u32() != w.as_u32()) as u32))
+ }
+ Expr::RsPlus(ref vx, ref wx) => {
+ let v = try!(self.eval_expr(vx));
+ let w = try!(self.eval_expr(wx));
+ Ok(Val::I32(v.as_u32() + w.as_u32()))
+ }
+ Expr::RsMinus(ref vx, ref wx) => {
+ let v = try!(self.eval_expr(vx));
+ let w = try!(self.eval_expr(wx));
+ Ok(Val::I32(v.as_u32() - w.as_u32()))
+ }
+ }
+ }
+
+ #[inline]
+ fn eval_subs(&self, subs: &Vec<Expr>) -> Res<Vec<usize>> {
+ subs.iter().map(|v| self.eval_expr(v).map(|w| w.as_usize())).collect()
+ }
+
+ /// Dimension an array.
+ fn array_dim(&mut self, var: &Var, dims: &Vec<Expr>) -> Res<()> {
+ let dims = try!(self.eval_subs(dims));
+ match *var {
+ Var::A16(n, _) => self.tail[n].dimension(dims, 0),
+ Var::A32(n, _) => self.hybrid[n].dimension(dims, 0),
+ _ => return IE994.err(),
+ }
+ }
+
+ /// Assign to a variable.
+ fn assign(&mut self, var: &Var, val: Val) -> Res<()> {
+ match *var {
+ Var::I16(n) => Ok(self.spot[n].assign(try!(val.as_u16()))),
+ Var::I32(n) => Ok(self.twospot[n].assign(val.as_u32())),
+ Var::A16(n, ref subs) => {
+ let subs = try!(self.eval_subs(subs));
+ self.tail[n].set_md(subs, try!(val.as_u16()), 0)
+ }
+ Var::A32(n, ref subs) => {
+ let subs = try!(self.eval_subs(subs));
+ self.hybrid[n].set_md(subs, val.as_u32(), 0)
+ }
+ }
+ }
+
+ /// Look up the value of a variable.
+ fn lookup(&self, var: &Var) -> Res<Val> {
+ match *var {
+ Var::I16(n) => Ok(Val::I16(self.spot[n].val)),
+ Var::I32(n) => Ok(Val::I32(self.twospot[n].val)),
+ Var::A16(n, ref subs) => {
+ let subs = try!(self.eval_subs(subs));
+ self.tail[n].get_md(subs, 0).map(Val::I16)
+ }
+ Var::A32(n, ref subs) => {
+ let subs = try!(self.eval_subs(subs));
+ self.hybrid[n].get_md(subs, 0).map(Val::I32)
+ }
+ }
+ }
+
+ /// Process a STASH statement.
+ fn stash(&mut self, var: &Var) {
+ match *var {
+ Var::I16(n) => self.spot[n].stash(),
+ Var::I32(n) => self.twospot[n].stash(),
+ Var::A16(n, _) => self.tail[n].stash(),
+ Var::A32(n, _) => self.hybrid[n].stash(),
+ }
+ }
+
+ /// Process a RETRIEVE statement.
+ fn retrieve(&mut self, var: &Var) -> Res<()> {
+ match *var {
+ Var::I16(n) => self.spot[n].retrieve(0),
+ Var::I32(n) => self.twospot[n].retrieve(0),
+ Var::A16(n, _) => self.tail[n].retrieve(0),
+ Var::A32(n, _) => self.hybrid[n].retrieve(0),
+ }
+ }
+
+ /// Process an IGNORE or REMEMBER statement. Cannot fail.
+ fn set_rw(&mut self, var: &Var, rw: bool) {
+ match *var {
+ Var::I16(n) => self.spot[n].rw = rw,
+ Var::I32(n) => self.twospot[n].rw = rw,
+ Var::A16(n, _) => self.tail[n].rw = rw,
+ Var::A32(n, _) => self.hybrid[n].rw = rw,
+ }
+ }
+
+ /// P()rocess an ABSTAIN or REINSTATE statement. Cannot fail.
+ fn abstain(&mut self, what: &ast::Abstain, f: &Fn(u32) -> u32) {
+ if let &ast::Abstain::Label(lbl) = what {
+ let idx = self.program.labels[&lbl] as usize;
+ if self.program.stmts[idx].body != StmtBody::GiveUp {
+ self.abstain[idx] = f(self.abstain[idx]);
+ }
+ } else {
+ for (i, stype) in self.program.stmt_types.iter().enumerate() {
+ if stype == what {
+ self.abstain[i] = f(self.abstain[i]);
+ }
+ }
+ }
+ }
+
+ /// Array readout helper.
+ fn array_readout(&mut self, var: &Var) -> Res<()> {
+ let state = &mut self.last_out;
+ match *var {
+ Var::A16(n, _) => self.tail[n].readout(self.stdout, state, 0),
+ Var::A32(n, _) => self.hybrid[n].readout(self.stdout, state, 0),
+ _ => return IE994.err(),
+ }
+ }
+
+ /// Array writein helper.
+ fn array_writein(&mut self, var: &Var) -> Res<()> {
+ let state = &mut self.last_in;
+ match *var {
+ Var::A16(n, _) => self.tail[n].writein(state, 0),
+ Var::A32(n, _) => self.hybrid[n].writein(state, 0),
+ _ => return IE994.err(),
+ }
+ }
+
+ /// Debug helpers.
+ fn dump_state(&self) {
+ self.dump_state_one(&self.spot, ".");
+ self.dump_state_one(&self.twospot, ":");
+ self.dump_state_one(&self.tail, ",");
+ self.dump_state_one(&self.hybrid, ";");
+ if self.jumps.len() > 0 {
+ println!("Next stack: {:?}", self.jumps);
+ }
+ //println!("Abstained: {:?}", self.abstain);
+ }
+
+ fn dump_state_one<T: Debug + Display>(&self, vec: &Vec<Bind<T>>, sigil: &str) {
+ if vec.len() > 0 {
+ for (i, v) in vec.iter().enumerate() {
+ print!("{}{} = {}, ", sigil, i, v);
+ }
+ println!("");
+ }
+ }
+}
diff --git a/tests/examplefiles/rust_example.rs b/tests/examplefiles/rust_example.rs
deleted file mode 100644
index 8c44af1d..00000000
--- a/tests/examplefiles/rust_example.rs
+++ /dev/null
@@ -1,235 +0,0 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// based on:
-// http://shootout.alioth.debian.org/u32/benchmark.php?test=nbody&lang=java
-
-/* nest some /* comments */ */
-
-extern mod std;
-
-use core::os;
-
-// Using sqrt from the standard library is way slower than using libc
-// directly even though std just calls libc, I guess it must be
-// because the the indirection through another dynamic linker
-// stub. Kind of shocking. Might be able to make it faster still with
-// an llvm intrinsic.
-#[nolink]
-extern mod libc {
- #![legacy_exports];
- fn sqrt(n: float) -> float;
-}
-
-fn main() {
- let args = os::args();
- let args = if os::getenv(~"RUST_BENCH").is_some() {
- ~[~"", ~"4000000"]
- } else if args.len() <= 1u {
- ~[~"", ~"100000"]
- } else {
- args
- };
- let n = int::from_str(args[1]).get();
- let mut bodies: ~[Body::props] = NBodySystem::make();
- io::println(fmt!("%f", NBodySystem::energy(bodies)));
- let mut i = 0;
- while i < n {
- NBodySystem::advance(bodies, 0.01);
- i += 1;
- }
- io::println(fmt!("%f", NBodySystem::energy(bodies)));
-}
-
-mod NBodySystem {
- use Body;
-
- pub fn make() -> ~[Body::props] {
- let mut bodies: ~[Body::props] =
- ~[Body::sun(),
- Body::jupiter(),
- Body::saturn(),
- Body::uranus(),
- Body::neptune()];
-
- let mut px = 0.0;
- let mut py = 0.0;
- let mut pz = 0.0;
-
- let mut i = 0;
- while i < 5 {
- px += bodies[i].vx * bodies[i].mass;
- py += bodies[i].vy * bodies[i].mass;
- pz += bodies[i].vz * bodies[i].mass;
-
- i += 1;
- }
-
- // side-effecting
- Body::offset_momentum(&mut bodies[0], px, py, pz);
-
- return bodies;
- }
-
- pub fn advance(bodies: &mut [Body::props], dt: float) {
- let mut i = 0;
- while i < 5 {
- let mut j = i + 1;
- while j < 5 {
- advance_one(&mut bodies[i],
- &mut bodies[j], dt);
- j += 1;
- }
-
- i += 1;
- }
-
- i = 0;
- while i < 5 {
- move_(&mut bodies[i], dt);
- i += 1;
- }
- }
-
- pub fn advance_one(bi: &mut Body::props,
- bj: &mut Body::props,
- dt: float) unsafe {
- let dx = bi.x - bj.x;
- let dy = bi.y - bj.y;
- let dz = bi.z - bj.z;
-
- let dSquared = dx * dx + dy * dy + dz * dz;
-
- let distance = ::libc::sqrt(dSquared);
- let mag = dt / (dSquared * distance);
-
- bi.vx -= dx * bj.mass * mag;
- bi.vy -= dy * bj.mass * mag;
- bi.vz -= dz * bj.mass * mag;
-
- bj.vx += dx * bi.mass * mag;
- bj.vy += dy * bi.mass * mag;
- bj.vz += dz * bi.mass * mag;
- }
-
- pub fn move_(b: &mut Body::props, dt: float) {
- b.x += dt * b.vx;
- b.y += dt * b.vy;
- b.z += dt * b.vz;
- }
-
- pub fn energy(bodies: &[Body::props]) -> float unsafe {
- let mut dx;
- let mut dy;
- let mut dz;
- let mut distance;
- let mut e = 0.0;
-
- let mut i = 0;
- while i < 5 {
- e +=
- 0.5 * bodies[i].mass *
- (bodies[i].vx * bodies[i].vx + bodies[i].vy * bodies[i].vy
- + bodies[i].vz * bodies[i].vz);
-
- let mut j = i + 1;
- while j < 5 {
- dx = bodies[i].x - bodies[j].x;
- dy = bodies[i].y - bodies[j].y;
- dz = bodies[i].z - bodies[j].z;
-
- distance = ::libc::sqrt(dx * dx + dy * dy + dz * dz);
- e -= bodies[i].mass * bodies[j].mass / distance;
-
- j += 1;
- }
-
- i += 1;
- }
- return e;
-
- }
-}
-
-mod Body {
- use Body;
-
- pub const PI: float = 3.141592653589793;
- pub const SOLAR_MASS: float = 39.478417604357432;
- // was 4 * PI * PI originally
- pub const DAYS_PER_YEAR: float = 365.24;
-
- pub type props =
- {mut x: float,
- mut y: float,
- mut z: float,
- mut vx: float,
- mut vy: float,
- mut vz: float,
- mass: float};
-
- pub fn jupiter() -> Body::props {
- return {mut x: 4.84143144246472090e+00,
- mut y: -1.16032004402742839e+00,
- mut z: -1.03622044471123109e-01,
- mut vx: 1.66007664274403694e-03 * DAYS_PER_YEAR,
- mut vy: 7.69901118419740425e-03 * DAYS_PER_YEAR,
- mut vz: -6.90460016972063023e-05 * DAYS_PER_YEAR,
- mass: 9.54791938424326609e-04 * SOLAR_MASS};
- }
-
- pub fn saturn() -> Body::props {
- return {mut x: 8.34336671824457987e+00,
- mut y: 4.12479856412430479e+00,
- mut z: -4.03523417114321381e-01,
- mut vx: -2.76742510726862411e-03 * DAYS_PER_YEAR,
- mut vy: 4.99852801234917238e-03 * DAYS_PER_YEAR,
- mut vz: 2.30417297573763929e-05 * DAYS_PER_YEAR,
- mass: 2.85885980666130812e-04 * SOLAR_MASS};
- }
-
- pub fn uranus() -> Body::props {
- return {mut x: 1.28943695621391310e+01,
- mut y: -1.51111514016986312e+01,
- mut z: -2.23307578892655734e-01,
- mut vx: 2.96460137564761618e-03 * DAYS_PER_YEAR,
- mut vy: 2.37847173959480950e-03 * DAYS_PER_YEAR,
- mut vz: -2.96589568540237556e-05 * DAYS_PER_YEAR,
- mass: 4.36624404335156298e-05 * SOLAR_MASS};
- }
-
- pub fn neptune() -> Body::props {
- return {mut x: 1.53796971148509165e+01,
- mut y: -2.59193146099879641e+01,
- mut z: 1.79258772950371181e-01,
- mut vx: 2.68067772490389322e-03 * DAYS_PER_YEAR,
- mut vy: 1.62824170038242295e-03 * DAYS_PER_YEAR,
- mut vz: -9.51592254519715870e-05 * DAYS_PER_YEAR,
- mass: 5.15138902046611451e-05 * SOLAR_MASS};
- }
-
- pub fn sun() -> Body::props {
- return {mut x: 0.0,
- mut y: 0.0,
- mut z: 0.0,
- mut vx: 0.0,
- mut vy: 0.0,
- mut vz: 0.0,
- mass: SOLAR_MASS};
- }
-
- pub fn offset_momentum(props: &mut Body::props,
- px: float, py: float, pz: float) {
- props.vx = -px / SOLAR_MASS;
- props.vy = -py / SOLAR_MASS;
- props.vz = -pz / SOLAR_MASS;
- }
-
-}