<commit_before>// 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

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;
    }

}
<commit_msg>test nested comments in rust<commit_after>// 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;
    }

}
<|endoftext|>


<commit_before>#[macro_use]
extern crate nom;

use std::fmt;
use std::fmt::{Display, Debug, Formatter};

use std::str::FromStr;

use nom::{digit, multispace};
use nom::types::CompleteStr;

pub enum Expr {
  Value(i64),
  Add(Box<Expr>, Box<Expr>),
  Sub(Box<Expr>, Box<Expr>),
  Mul(Box<Expr>, Box<Expr>),
  Div(Box<Expr>, Box<Expr>),
  Paren(Box<Expr>),
}

pub enum Oper {
  Add,
  Sub,
  Mul,
  Div,
}

impl Display for Expr {
  fn fmt(&self, format: &mut Formatter) -> fmt::Result {
    use self::Expr::*;
    match *self {
      Value(val) => write!(format, "{}", val),
      Add(ref left, ref right) => write!(format, "{} + {}", left, right),
      Sub(ref left, ref right) => write!(format, "{} - {}", left, right),
      Mul(ref left, ref right) => write!(format, "{} * {}", left, right),
      Div(ref left, ref right) => write!(format, "{} / {}", left, right),
      Paren(ref expr) => write!(format, "({})", expr),
    }
  }
}

impl Debug for Expr {
  fn fmt(&self, format: &mut Formatter) -> fmt::Result {
    use self::Expr::*;
    match *self {
      Value(val) => write!(format, "{}", val),
      Add(ref left, ref right) => write!(format, "({:?} + {:?})", left, right),
      Sub(ref left, ref right) => write!(format, "({:?} - {:?})", left, right),
      Mul(ref left, ref right) => write!(format, "({:?} * {:?})", left, right),
      Div(ref left, ref right) => write!(format, "({:?} / {:?})", left, right),
      Paren(ref expr) => write!(format, "[{:?}]", expr),
    }
  }
}

named!(parens< CompleteStr, Expr >, delimited!(
    delimited!(opt!(multispace), tag!("("), opt!(multispace)),
    map!(map!(expr, Box::new), Expr::Paren),
    delimited!(opt!(multispace), tag!(")"), opt!(multispace))
  )
);

named!(factor< CompleteStr, Expr >, alt_complete!(
    map!(
      map_res!(
        delimited!(opt!(multispace), digit, opt!(multispace)),
        |s: CompleteStr| { FromStr::from_str(s.0) }
      ),
      Expr::Value
    )
  | parens
  )
);

fn fold_exprs(initial: Expr, remainder: Vec<(Oper, Expr)>) -> Expr {
  remainder.into_iter().fold(initial, |acc, pair| {
    let (oper, expr) = pair;
    match oper {
      Oper::Add => Expr::Add(Box::new(acc), Box::new(expr)),
      Oper::Sub => Expr::Sub(Box::new(acc), Box::new(expr)),
      Oper::Mul => Expr::Mul(Box::new(acc), Box::new(expr)),
      Oper::Div => Expr::Div(Box::new(acc), Box::new(expr)),
    }
  })
}

named!(term< CompleteStr, Expr >, do_parse!(
    initial: factor >>
    remainder: many0!(
           alt!(
             do_parse!(tag!("*") >> mul: factor >> (Oper::Mul, mul)) |
             do_parse!(tag!("/") >> div: factor >> (Oper::Div, div))
           )
         ) >>
    (fold_exprs(initial, remainder))
));

named!(expr< CompleteStr, Expr >, do_parse!(
    initial: term >>
    remainder: many0!(
           alt!(
             do_parse!(tag!("+") >> add: term >> (Oper::Add, add)) |
             do_parse!(tag!("-") >> sub: term >> (Oper::Sub, sub))
           )
         ) >>
    (fold_exprs(initial, remainder))
));

#[test]
fn factor_test() {
  assert_eq!(
    factor(CompleteStr("  3  ")).map(|(i, x)| (i, format!("{:?}", x))),
    Ok((CompleteStr(""), String::from("3")))
  );
}

#[test]
fn term_test() {
  assert_eq!(
    term(CompleteStr(" 3 *  5   ")).map(|(i, x)| (i, format!("{:?}", x))),
    Ok((CompleteStr(""), String::from("(3 * 5)")))
  );
}

#[test]
fn expr_test() {
  assert_eq!(
    expr(CompleteStr(" 1 + 2 *  3 ")).map(|(i, x)| (i, format!("{:?}", x))),
    Ok((CompleteStr(""), String::from("(1 + (2 * 3))")))
  );
  assert_eq!(
    expr(CompleteStr(" 1 + 2 *  3 / 4 - 5 ")).map(|(i, x)| (i, format!("{:?}", x))),
    Ok((CompleteStr(""), String::from("((1 + ((2 * 3) / 4)) - 5)")))
  );
  assert_eq!(
    expr(CompleteStr(" 72 / 2 / 3 ")).map(|(i, x)| (i, format!("{:?}", x))),
    Ok((CompleteStr(""), String::from("((72 / 2) / 3)")))
  );
}

#[test]
fn parens_test() {
  assert_eq!(
    expr(CompleteStr(" ( 1 + 2 ) *  3 ")).map(|(i, x)| (i, format!("{:?}", x))),
    Ok((CompleteStr(""), String::from("([(1 + 2)] * 3)")))
  );
}
<commit_msg>missing debug derive<commit_after>#[macro_use]
extern crate nom;

use std::fmt;
use std::fmt::{Display, Debug, Formatter};

use std::str::FromStr;

use nom::{digit, multispace};
use nom::types::CompleteStr;

pub enum Expr {
  Value(i64),
  Add(Box<Expr>, Box<Expr>),
  Sub(Box<Expr>, Box<Expr>),
  Mul(Box<Expr>, Box<Expr>),
  Div(Box<Expr>, Box<Expr>),
  Paren(Box<Expr>),
}

#[derive(Debug)]
pub enum Oper {
  Add,
  Sub,
  Mul,
  Div,
}

impl Display for Expr {
  fn fmt(&self, format: &mut Formatter) -> fmt::Result {
    use self::Expr::*;
    match *self {
      Value(val) => write!(format, "{}", val),
      Add(ref left, ref right) => write!(format, "{} + {}", left, right),
      Sub(ref left, ref right) => write!(format, "{} - {}", left, right),
      Mul(ref left, ref right) => write!(format, "{} * {}", left, right),
      Div(ref left, ref right) => write!(format, "{} / {}", left, right),
      Paren(ref expr) => write!(format, "({})", expr),
    }
  }
}

impl Debug for Expr {
  fn fmt(&self, format: &mut Formatter) -> fmt::Result {
    use self::Expr::*;
    match *self {
      Value(val) => write!(format, "{}", val),
      Add(ref left, ref right) => write!(format, "({:?} + {:?})", left, right),
      Sub(ref left, ref right) => write!(format, "({:?} - {:?})", left, right),
      Mul(ref left, ref right) => write!(format, "({:?} * {:?})", left, right),
      Div(ref left, ref right) => write!(format, "({:?} / {:?})", left, right),
      Paren(ref expr) => write!(format, "[{:?}]", expr),
    }
  }
}

named!(parens< CompleteStr, Expr >, delimited!(
    delimited!(opt!(multispace), tag!("("), opt!(multispace)),
    map!(map!(expr, Box::new), Expr::Paren),
    delimited!(opt!(multispace), tag!(")"), opt!(multispace))
  )
);

named!(factor< CompleteStr, Expr >, alt_complete!(
    map!(
      map_res!(
        delimited!(opt!(multispace), digit, opt!(multispace)),
        |s: CompleteStr| { FromStr::from_str(s.0) }
      ),
      Expr::Value
    )
  | parens
  )
);

fn fold_exprs(initial: Expr, remainder: Vec<(Oper, Expr)>) -> Expr {
  remainder.into_iter().fold(initial, |acc, pair| {
    let (oper, expr) = pair;
    match oper {
      Oper::Add => Expr::Add(Box::new(acc), Box::new(expr)),
      Oper::Sub => Expr::Sub(Box::new(acc), Box::new(expr)),
      Oper::Mul => Expr::Mul(Box::new(acc), Box::new(expr)),
      Oper::Div => Expr::Div(Box::new(acc), Box::new(expr)),
    }
  })
}

named!(term< CompleteStr, Expr >, do_parse!(
    initial: factor >>
    remainder: many0!(
           alt!(
             do_parse!(tag!("*") >> mul: factor >> (Oper::Mul, mul)) |
             do_parse!(tag!("/") >> div: factor >> (Oper::Div, div))
           )
         ) >>
    (fold_exprs(initial, remainder))
));

named!(expr< CompleteStr, Expr >, do_parse!(
    initial: term >>
    remainder: many0!(
           alt!(
             do_parse!(tag!("+") >> add: term >> (Oper::Add, add)) |
             do_parse!(tag!("-") >> sub: term >> (Oper::Sub, sub))
           )
         ) >>
    (fold_exprs(initial, remainder))
));

#[test]
fn factor_test() {
  assert_eq!(
    factor(CompleteStr("  3  ")).map(|(i, x)| (i, format!("{:?}", x))),
    Ok((CompleteStr(""), String::from("3")))
  );
}

#[test]
fn term_test() {
  assert_eq!(
    term(CompleteStr(" 3 *  5   ")).map(|(i, x)| (i, format!("{:?}", x))),
    Ok((CompleteStr(""), String::from("(3 * 5)")))
  );
}

#[test]
fn expr_test() {
  assert_eq!(
    expr(CompleteStr(" 1 + 2 *  3 ")).map(|(i, x)| (i, format!("{:?}", x))),
    Ok((CompleteStr(""), String::from("(1 + (2 * 3))")))
  );
  assert_eq!(
    expr(CompleteStr(" 1 + 2 *  3 / 4 - 5 ")).map(|(i, x)| (i, format!("{:?}", x))),
    Ok((CompleteStr(""), String::from("((1 + ((2 * 3) / 4)) - 5)")))
  );
  assert_eq!(
    expr(CompleteStr(" 72 / 2 / 3 ")).map(|(i, x)| (i, format!("{:?}", x))),
    Ok((CompleteStr(""), String::from("((72 / 2) / 3)")))
  );
}

#[test]
fn parens_test() {
  assert_eq!(
    expr(CompleteStr(" ( 1 + 2 ) *  3 ")).map(|(i, x)| (i, format!("{:?}", x))),
    Ok((CompleteStr(""), String::from("([(1 + 2)] * 3)")))
  );
}
<|endoftext|>


<commit_before>use itertools::Itertools;
use lightning::map::{Map, ObjectMap};
use rayon::prelude::*;
use std::{
    collections::{HashMap, HashSet},
    sync::Arc,
};

use dovahkiin::types::SharedValue;

use crate::ram::{
    cell::{header_from_chunk_raw, select_from_chunk_raw},
    chunk::Chunk,
    clock::now,
};

mod histogram;
pub mod sm;

pub struct SchemaStatistics {
    pub histogram: HashMap<u64, [HistogramKey; HISTOGRAM_TARGET_BUCKETS + 1]>,
    pub count: usize,
    pub segs: usize,
    pub bytes: usize,
    pub timestamp: u32,
}

pub struct ChunkStatistics {
    pub schemas: ObjectMap<Arc<SchemaStatistics>>,
}

const HISTOGRAM_PARTITATION_SIZE: usize = 1024;
const HISTOGRAM_PARTITATION_BUCKETS: usize = 128;
const HISTOGRAM_TARGET_BUCKETS: usize = 100;

type HistogramKey = [u8; 8];

impl ChunkStatistics {
    pub fn from_chunk(chunk: &Chunk) -> Self {
        let histogram_partitations = chunk
            .cell_index
            .entries()
            .chunks(HISTOGRAM_PARTITATION_SIZE)
            .map(|s| s.to_vec())
            .collect_vec();
        let partitations: Vec<_> = histogram_partitations
            .into_par_iter()
            .map(|partitation| build_partitation_statistics(partitation, chunk))
            .collect();
        let schema_ids: Vec<_> = partitations
            .iter()
            .map(|(sizes, _, _, _)| sizes.keys())
            .flatten()
            .dedup()
            .collect();
        let total_size = schema_ids
            .iter()
            .map(|sid| {
                (
                    *sid,
                    partitations
                        .iter()
                        .map(|(sizes, _, _, _)| sizes.get(sid).unwrap_or(&0))
                        .sum::<usize>(),
                )
            })
            .collect::<HashMap<_, _>>();
        let total_counts = schema_ids
            .iter()
            .map(|sid| {
                (
                    *sid,
                    partitations
                        .iter()
                        .map(|(_, _, counts, _)| counts.get(sid).unwrap_or(&0))
                        .sum::<usize>(),
                )
            })
            .collect::<HashMap<_, _>>();
        let total_segs = schema_ids
            .iter()
            .map(|sid| {
                (
                    *sid,
                    partitations
                        .iter()
                        .map(|(_, segs, _, _)| segs.get(sid).map(|set| set.len()).unwrap_or(0))
                        .sum::<usize>(),
                )
            })
            .collect::<HashMap<_, _>>();
        let empty_histo = Default::default();
        let mut schema_histograms = schema_ids
            .iter()
            .map(|sid| {
                (*sid, {
                    let parted_histos = partitations
                        .iter()
                        .map(|(_, _, _, histo)| histo.get(sid).unwrap_or(&empty_histo))
                        .collect_vec();
                    let field_ids = parted_histos
                        .iter()
                        .map(|histo_map| histo_map.keys())
                        .flatten()
                        .dedup()
                        .collect::<Vec<_>>();
                    field_ids
                        .par_iter()
                        .map(|field_id| {
                            let schema_field_histograms = parted_histos
                                .iter()
                                .map(|histo_map| &histo_map[field_id])
                                .collect_vec();
                            (**field_id, build_histogram(schema_field_histograms))
                        })
                        .collect::<HashMap<u64, _>>()
                })
            })
            .collect::<HashMap<_, _>>();
        let schema_statistics = ObjectMap::<Arc<SchemaStatistics>>::with_capacity(
            schema_ids.capacity().next_power_of_two(),
        );
        for schema_id in schema_ids {
            let statistics = SchemaStatistics {
                histogram: schema_histograms.remove(&schema_id).unwrap(),
                count: *total_counts.get(&schema_id).unwrap(),
                segs: *total_segs.get(&schema_id).unwrap(),
                bytes: *total_size.get(&schema_id).unwrap(),
                timestamp: now(),
            };
            schema_statistics.insert(&(*schema_id as usize), Arc::new(statistics));
        }
        Self {
            schemas: schema_statistics,
        }
    }
}

fn build_partitation_statistics(
    partitation: Vec<(usize, usize)>,
    chunk: &Chunk,
) -> (
    HashMap<u32, usize>,
    HashMap<u32, HashSet<usize>>,
    HashMap<u32, usize>,
    HashMap<u32, HashMap<u64, (Vec<HistogramKey>, usize, usize)>>,
) {
    // Build exact histogram for each of the partitation and then approximate overall histogram
    let mut sizes = HashMap::new();
    let mut segs = HashMap::new();
    let mut counts = HashMap::new();
    let mut exact_accumlators = HashMap::new();
    let partitation_size = partitation.len();
    for (hash, _) in partitation {
        let loc = if let Ok(ptr) = chunk.location_for_read(hash as u64) {
            ptr
        } else {
            trace!("Cannot obtain cell lock {} for statistics", hash);
            continue;
        };
        match header_from_chunk_raw(*loc) {
            Ok((header, _, entry_header)) => {
                let cell_size = entry_header.content_length as usize;
                let cell_seg = chunk.allocator.id_by_addr(*loc);
                let schema_id = header.schema;
                if let Some(schema) = chunk.meta.schemas.get(&schema_id) {
                    let fields = schema.index_fields.keys().cloned().collect_vec();
                    if let Ok(partial_cell) = select_from_chunk_raw(*loc, chunk, fields.as_slice())
                    {
                        let field_array = if fields.len() == 1 {
                            vec![partial_cell]
                        } else if let SharedValue::Array(arr) = partial_cell {
                            arr
                        } else {
                            error!(
                                "Cannot decode partial cell for statistics {:?}",
                                partial_cell
                            );
                            continue;
                        };
                        for (i, val) in field_array.into_iter().enumerate() {
                            if val == SharedValue::Null || val == SharedValue::NA {
                                continue;
                            }
                            let field_id = fields[i];
                            exact_accumlators
                                .entry(schema_id)
                                .or_insert_with(|| HashMap::new())
                                .entry(field_id)
                                .or_insert_with(|| Vec::with_capacity(partitation_size))
                                .push(val.feature());
                        }
                        *counts.entry(schema_id).or_insert(0) += 1;
                        *sizes.entry(schema_id).or_insert(0) += cell_size;
                        segs.entry(schema_id)
                            .or_insert_with(|| HashSet::new())
                            .insert(cell_seg);
                    }
                } else {
                    warn!("Cannot get schema {} for statistics", schema_id);
                }
            }
            Err(e) => {
                warn!("Failed to read {} for statistics, error: {:?}", hash, e);
            }
        }
    }
    let histograms: HashMap<_, _> = exact_accumlators
        .into_iter()
        .map(|(schema_id, schema_histograms)| {
            let compiled_histograms = schema_histograms
                .into_iter()
                .map(|(field, items)| {
                    let num_items = items.len();
                    let (histogram, depth) = build_partitation_histogram(items);
                    (field, (histogram, num_items, depth))
                })
                .collect::<HashMap<_, _>>();
            (schema_id, compiled_histograms)
        })
        .collect::<HashMap<_, _>>();
    (sizes, segs, counts, histograms)
}

fn build_partitation_histogram(mut items: Vec<HistogramKey>) -> (Vec<HistogramKey>, usize) {
    if items.len() <= HISTOGRAM_PARTITATION_BUCKETS {
        return (items, 1);
    }
    items.sort();
    let depth = items.len() / HISTOGRAM_PARTITATION_BUCKETS;
    let mut histogram = (0..HISTOGRAM_PARTITATION_BUCKETS)
        .map(|tile| items[tile * depth])
        .collect_vec();
    let last_item = &items[items.len() - 1];
    if histogram.last().unwrap() != last_item {
        histogram.push(*last_item);
    }
    (histogram, depth)
}

fn build_histogram(
    partitations: Vec<&(Vec<HistogramKey>, usize, usize)>,
) -> [HistogramKey; HISTOGRAM_TARGET_BUCKETS + 1] {
    // Build the approximated histogram from partitation histograms
    // https://arxiv.org/abs/1606.05633
    let mut part_idxs = vec![0; partitations.len()];
    let part_histos = partitations
        .iter()
        .map(|(histo, _, _)| histo)
        .filter(|histo| !histo.is_empty())
        .collect_vec();
    let num_total = partitations.iter().map(|(_, num, _)| num).sum::<usize>();
    let part_depths = partitations
        .iter()
        .map(|(_, _, depth)| *depth)
        .collect_vec();
    let max_key = partitations
        .iter()
        .filter_map(|(part, _, _)| part.last())
        .max()
        .unwrap();
    let target_width = num_total / HISTOGRAM_TARGET_BUCKETS;
    let mut target_histogram = [[0u8; 8]; HISTOGRAM_TARGET_BUCKETS + 1];
    // Perform a merge sort for sorted pre-histogram
    let mut filled = target_width;
    let mut last_key = Default::default();
    'HISTO_CONST: for i in 0..HISTOGRAM_TARGET_BUCKETS {
        loop {
            let (key, ended) = if let Some((part_idx, histo)) = part_histos
                .iter()
                .enumerate()
                .filter(|(i, h)| {
                    let idx = part_idxs[*i];
                    idx < h.len()
                })
                .min_by(|(i1, h1), (i2, h2)| {
                    let h1_idx = part_idxs[*i1];
                    let h2_idx = part_idxs[*i2];
                    h1[h1_idx].cmp(&h2[h2_idx])
                }) {
                let histo_idx = part_idxs[part_idx];
                part_idxs[part_idx] += 1;
                ((histo[histo_idx], part_idx), false)
            } else {
                (last_key, true)
            };
            last_key = key;
            let idx = last_key.1;
            if filled >= target_width || ended {
                target_histogram[i] = last_key.0;
                filled = 0;
                continue 'HISTO_CONST;
            }
            filled += part_depths[idx];
        }
    }
    target_histogram[HISTOGRAM_TARGET_BUCKETS] = max_key.clone();
    target_histogram
}

#[cfg(test)]
mod tests {
    use dovahkiin::types::OwnedValue;

    use super::*;

    #[test]
    fn partitation_histogram() {
        let small_set = (0..10).map(|n| OwnedValue::U64(n).feature()).collect_vec();
        assert_eq!(
            build_partitation_histogram(small_set.clone()),
            (small_set, 1)
        );
        let eq_set = (0..HISTOGRAM_PARTITATION_BUCKETS)
            .map(|n| OwnedValue::U64(n as u64).feature())
            .collect_vec();
        assert_eq!(build_partitation_histogram(eq_set.clone()), (eq_set, 1));

        let double_set = (0..HISTOGRAM_PARTITATION_BUCKETS * 2)
            .map(|n| OwnedValue::U64(n as u64).feature())
            .collect_vec();
        let mut expect = double_set.iter().step_by(2).cloned().collect_vec();
        expect.push(double_set.last().unwrap().to_owned());
        assert_eq!(build_partitation_histogram(double_set), (expect, 2));

        let triple_set = (0..HISTOGRAM_PARTITATION_BUCKETS * 3)
            .map(|n| OwnedValue::U64(n as u64).feature())
            .collect_vec();
        let mut expect = triple_set.iter().step_by(3).cloned().collect_vec();
        expect.push(triple_set.last().unwrap().to_owned());
        assert_eq!(build_partitation_histogram(triple_set), (expect, 3));
    }

    #[test]
    fn approximated_histogram() {
        // Test with example from the paper
        let histo_1 = vec![2, 7, 18, 25];
        let histo_1_height = 4;

        let histo_2 = vec![3, 15, 24, 30];
        let histo_2_height = 5;

        let test_data = vec![
            (
                histo_1
                    .iter()
                    .map(|n| OwnedValue::U64(*n).feature())
                    .collect::<Vec<_>>(),
                (histo_1.len() - 1) * histo_1_height,
                histo_1_height,
            ),
            (
                histo_2
                    .iter()
                    .map(|n| OwnedValue::U64(*n).feature())
                    .collect::<Vec<_>>(),
                (histo_2.len() - 1) * histo_2_height,
                histo_2_height,
            ),
        ];
        let histogram = build_histogram(test_data.iter().collect_vec());
        let mut histogram_base = [[0u8; 8]; HISTOGRAM_TARGET_BUCKETS + 1];
        histogram_base[0] = OwnedValue::U64(2).feature();
        histogram_base[1] = OwnedValue::U64(7).feature();
        histogram_base[2] = OwnedValue::U64(18).feature();
        histogram_base[3] = OwnedValue::U64(30).feature();
        assert_eq!(histogram, histogram_base);
    }
}
<commit_msg>repeatdly histogram<commit_after>use itertools::Itertools;
use lightning::map::{Map, ObjectMap};
use rayon::prelude::*;
use std::{
    collections::{HashMap, HashSet},
    iter,
    sync::Arc,
};

use dovahkiin::types::SharedValue;

use crate::ram::{
    cell::{header_from_chunk_raw, select_from_chunk_raw},
    chunk::Chunk,
    clock::now,
};

mod histogram;
pub mod sm;

pub struct SchemaStatistics {
    pub histogram: HashMap<u64, TargetHistogram>,
    pub count: usize,
    pub segs: usize,
    pub bytes: usize,
    pub timestamp: u32,
}

pub struct ChunkStatistics {
    pub schemas: ObjectMap<Arc<SchemaStatistics>>,
}

const HISTOGRAM_PARTITATION_SIZE: usize = 1024;
const HISTOGRAM_PARTITATION_BUCKETS: usize = 128;
const HISTOGRAM_TARGET_BUCKETS: usize = 100;
const HISTOGRAM_TARGET_KEYS: usize = HISTOGRAM_PARTITATION_BUCKETS + 1;

type HistogramKey = [u8; 8];
type TargetHistogram = [HistogramKey; HISTOGRAM_TARGET_KEYS];

impl ChunkStatistics {
    pub fn from_chunk(chunk: &Chunk) -> Self {
        let histogram_partitations = chunk
            .cell_index
            .entries()
            .chunks(HISTOGRAM_PARTITATION_SIZE)
            .map(|s| s.to_vec())
            .collect_vec();
        let partitations: Vec<_> = histogram_partitations
            .into_par_iter()
            .map(|partitation| build_partitation_statistics(partitation, chunk))
            .collect();
        let schema_ids: Vec<_> = partitations
            .iter()
            .map(|(sizes, _, _, _)| sizes.keys())
            .flatten()
            .dedup()
            .collect();
        let total_size = schema_ids
            .iter()
            .map(|sid| {
                (
                    *sid,
                    partitations
                        .iter()
                        .map(|(sizes, _, _, _)| sizes.get(sid).unwrap_or(&0))
                        .sum::<usize>(),
                )
            })
            .collect::<HashMap<_, _>>();
        let total_counts = schema_ids
            .iter()
            .map(|sid| {
                (
                    *sid,
                    partitations
                        .iter()
                        .map(|(_, _, counts, _)| counts.get(sid).unwrap_or(&0))
                        .sum::<usize>(),
                )
            })
            .collect::<HashMap<_, _>>();
        let total_segs = schema_ids
            .iter()
            .map(|sid| {
                (
                    *sid,
                    partitations
                        .iter()
                        .map(|(_, segs, _, _)| segs.get(sid).map(|set| set.len()).unwrap_or(0))
                        .sum::<usize>(),
                )
            })
            .collect::<HashMap<_, _>>();
        let empty_histo = Default::default();
        let mut schema_histograms = schema_ids
            .iter()
            .map(|sid| {
                (*sid, {
                    let parted_histos = partitations
                        .iter()
                        .map(|(_, _, _, histo)| histo.get(sid).unwrap_or(&empty_histo))
                        .collect_vec();
                    let field_ids = parted_histos
                        .iter()
                        .map(|histo_map| histo_map.keys())
                        .flatten()
                        .dedup()
                        .collect::<Vec<_>>();
                    field_ids
                        .par_iter()
                        .map(|field_id| {
                            let schema_field_histograms = parted_histos
                                .iter()
                                .map(|histo_map| &histo_map[field_id])
                                .collect_vec();
                            (**field_id, build_histogram(schema_field_histograms))
                        })
                        .collect::<HashMap<u64, _>>()
                })
            })
            .collect::<HashMap<_, _>>();
        let schema_statistics = ObjectMap::<Arc<SchemaStatistics>>::with_capacity(
            schema_ids.capacity().next_power_of_two(),
        );
        for schema_id in schema_ids {
            let statistics = SchemaStatistics {
                histogram: schema_histograms.remove(&schema_id).unwrap(),
                count: *total_counts.get(&schema_id).unwrap(),
                segs: *total_segs.get(&schema_id).unwrap(),
                bytes: *total_size.get(&schema_id).unwrap(),
                timestamp: now(),
            };
            schema_statistics.insert(&(*schema_id as usize), Arc::new(statistics));
        }
        Self {
            schemas: schema_statistics,
        }
    }
}

fn build_partitation_statistics(
    partitation: Vec<(usize, usize)>,
    chunk: &Chunk,
) -> (
    HashMap<u32, usize>,
    HashMap<u32, HashSet<usize>>,
    HashMap<u32, usize>,
    HashMap<u32, HashMap<u64, (Vec<HistogramKey>, usize, usize)>>,
) {
    // Build exact histogram for each of the partitation and then approximate overall histogram
    let mut sizes = HashMap::new();
    let mut segs = HashMap::new();
    let mut counts = HashMap::new();
    let mut exact_accumlators = HashMap::new();
    let partitation_size = partitation.len();
    for (hash, _) in partitation {
        let loc = if let Ok(ptr) = chunk.location_for_read(hash as u64) {
            ptr
        } else {
            trace!("Cannot obtain cell lock {} for statistics", hash);
            continue;
        };
        match header_from_chunk_raw(*loc) {
            Ok((header, _, entry_header)) => {
                let cell_size = entry_header.content_length as usize;
                let cell_seg = chunk.allocator.id_by_addr(*loc);
                let schema_id = header.schema;
                if let Some(schema) = chunk.meta.schemas.get(&schema_id) {
                    let fields = schema.index_fields.keys().cloned().collect_vec();
                    if let Ok(partial_cell) = select_from_chunk_raw(*loc, chunk, fields.as_slice())
                    {
                        let field_array = if fields.len() == 1 {
                            vec![partial_cell]
                        } else if let SharedValue::Array(arr) = partial_cell {
                            arr
                        } else {
                            error!(
                                "Cannot decode partial cell for statistics {:?}",
                                partial_cell
                            );
                            continue;
                        };
                        for (i, val) in field_array.into_iter().enumerate() {
                            if val == SharedValue::Null || val == SharedValue::NA {
                                continue;
                            }
                            let field_id = fields[i];
                            exact_accumlators
                                .entry(schema_id)
                                .or_insert_with(|| HashMap::new())
                                .entry(field_id)
                                .or_insert_with(|| Vec::with_capacity(partitation_size))
                                .push(val.feature());
                        }
                        *counts.entry(schema_id).or_insert(0) += 1;
                        *sizes.entry(schema_id).or_insert(0) += cell_size;
                        segs.entry(schema_id)
                            .or_insert_with(|| HashSet::new())
                            .insert(cell_seg);
                    }
                } else {
                    warn!("Cannot get schema {} for statistics", schema_id);
                }
            }
            Err(e) => {
                warn!("Failed to read {} for statistics, error: {:?}", hash, e);
            }
        }
    }
    let histograms: HashMap<_, _> = exact_accumlators
        .into_iter()
        .map(|(schema_id, schema_histograms)| {
            let compiled_histograms = schema_histograms
                .into_iter()
                .map(|(field, items)| {
                    let num_items = items.len();
                    let (histogram, depth) = build_partitation_histogram(items);
                    (field, (histogram, num_items, depth))
                })
                .collect::<HashMap<_, _>>();
            (schema_id, compiled_histograms)
        })
        .collect::<HashMap<_, _>>();
    (sizes, segs, counts, histograms)
}

fn build_partitation_histogram(mut items: Vec<HistogramKey>) -> (Vec<HistogramKey>, usize) {
    if items.len() <= HISTOGRAM_PARTITATION_BUCKETS {
        return (items, 1);
    }
    items.sort();
    let depth = items.len() / HISTOGRAM_PARTITATION_BUCKETS;
    let mut histogram = (0..HISTOGRAM_PARTITATION_BUCKETS)
        .map(|tile| items[tile * depth])
        .collect_vec();
    let last_item = &items[items.len() - 1];
    if histogram.last().unwrap() != last_item {
        histogram.push(*last_item);
    }
    (histogram, depth)
}

fn build_histogram(partitations: Vec<&(Vec<HistogramKey>, usize, usize)>) -> TargetHistogram {
    let num_all_keys: usize = partitations.iter().map(|(h, _, _)| h.len()).sum();
    if num_all_keys < HISTOGRAM_TARGET_KEYS {
        return repeated_histogram(partitations);
    }
    // Build the approximated histogram from partitation histograms
    // https://arxiv.org/abs/1606.05633
    let mut part_idxs = vec![0; partitations.len()];
    let part_histos = partitations
        .iter()
        .map(|(histo, _, _)| histo)
        .filter(|histo| !histo.is_empty())
        .collect_vec();
    let num_total = partitations.iter().map(|(_, num, _)| num).sum::<usize>();
    let part_depths = partitations
        .iter()
        .map(|(_, _, depth)| *depth)
        .collect_vec();
    let max_key = partitations
        .iter()
        .filter_map(|(part, _, _)| part.last())
        .max()
        .unwrap();
    let target_width = num_total / HISTOGRAM_TARGET_BUCKETS;
    let mut target_histogram = [[0u8; 8]; HISTOGRAM_TARGET_KEYS];
    // Perform a merge sort for sorted pre-histogram
    let mut filled = target_width;
    let mut last_key = Default::default();
    'HISTO_CONST: for i in 0..HISTOGRAM_TARGET_BUCKETS {
        loop {
            let (key, ended) = if let Some((part_idx, histo)) = part_histos
                .iter()
                .enumerate()
                .filter(|(i, h)| {
                    let idx = part_idxs[*i];
                    idx < h.len()
                })
                .min_by(|(i1, h1), (i2, h2)| {
                    let h1_idx = part_idxs[*i1];
                    let h2_idx = part_idxs[*i2];
                    h1[h1_idx].cmp(&h2[h2_idx])
                }) {
                let histo_idx = part_idxs[part_idx];
                part_idxs[part_idx] += 1;
                ((histo[histo_idx], part_idx), false)
            } else {
                (last_key, true)
            };
            last_key = key;
            let idx = last_key.1;
            if filled >= target_width || ended {
                target_histogram[i] = last_key.0;
                filled = 0;
                continue 'HISTO_CONST;
            }
            filled += part_depths[idx];
        }
    }
    target_histogram[HISTOGRAM_TARGET_BUCKETS] = max_key.clone();
    target_histogram
}

fn repeated_histogram(partitations: Vec<&(Vec<HistogramKey>, usize, usize)>) -> TargetHistogram {
    let combined = partitations
        .iter()
        .map(|(histo, _, depth)| {
            let depth = *depth;
            histo.iter().map(move |k| (k, depth))
        })
        .flatten()
        .sorted_by(|a, b| Ord::cmp(&a.0, &b.0))
        .collect_vec();
    let total_keys: usize = combined.iter().map(|(_, d)| *d).sum();
    let repeat_ratio: f64 = HISTOGRAM_TARGET_KEYS as f64 / total_keys as f64;
    let repeated = combined
        .iter()
        .map(|(k, d)| iter::repeat(*k).take((*d as f64 * repeat_ratio).ceil() as usize))
        .flatten()
        .collect_vec();
    let mut histo = empty_target_histogram();
    debug_assert!(repeated.len() >= histo.len());
    histo
        .iter_mut()
        .zip(repeated.into_iter())
        .for_each(|(h, k)| {
            *h = *k;
        });
    *histo.last_mut().unwrap() = combined.last().unwrap().0.clone();
    histo
}

fn empty_target_histogram() -> TargetHistogram {
    [[0u8; 8]; HISTOGRAM_TARGET_KEYS]
}

#[cfg(test)]
mod tests {
    use dovahkiin::types::OwnedValue;

    use super::*;

    #[test]
    fn partitation_histogram() {
        let small_set = (0..10).map(|n| OwnedValue::U64(n).feature()).collect_vec();
        assert_eq!(
            build_partitation_histogram(small_set.clone()),
            (small_set, 1)
        );
        let eq_set = (0..HISTOGRAM_PARTITATION_BUCKETS)
            .map(|n| OwnedValue::U64(n as u64).feature())
            .collect_vec();
        assert_eq!(build_partitation_histogram(eq_set.clone()), (eq_set, 1));

        let double_set = (0..HISTOGRAM_PARTITATION_BUCKETS * 2)
            .map(|n| OwnedValue::U64(n as u64).feature())
            .collect_vec();
        let mut expect = double_set.iter().step_by(2).cloned().collect_vec();
        expect.push(double_set.last().unwrap().to_owned());
        assert_eq!(build_partitation_histogram(double_set), (expect, 2));

        let triple_set = (0..HISTOGRAM_PARTITATION_BUCKETS * 3)
            .map(|n| OwnedValue::U64(n as u64).feature())
            .collect_vec();
        let mut expect = triple_set.iter().step_by(3).cloned().collect_vec();
        expect.push(triple_set.last().unwrap().to_owned());
        assert_eq!(build_partitation_histogram(triple_set), (expect, 3));
    }

    #[test]
    fn approximated_histogram() {
        // Test with example from the paper
        let histo_1 = vec![2, 7, 18, 25];
        let histo_1_height = 4;

        let histo_2 = vec![3, 15, 24, 30];
        let histo_2_height = 5;

        let test_data = vec![
            (
                histo_1
                    .iter()
                    .map(|n| OwnedValue::U64(*n).feature())
                    .collect::<Vec<_>>(),
                (histo_1.len() - 1) * histo_1_height,
                histo_1_height,
            ),
            (
                histo_2
                    .iter()
                    .map(|n| OwnedValue::U64(*n).feature())
                    .collect::<Vec<_>>(),
                (histo_2.len() - 1) * histo_2_height,
                histo_2_height,
            ),
        ];
        let histogram = build_histogram(test_data.iter().collect_vec());
        let mut expect_histogram = empty_target_histogram();
        expect_histogram[0] = OwnedValue::U64(2).feature();
        expect_histogram[1] = OwnedValue::U64(7).feature();
        expect_histogram[2] = OwnedValue::U64(18).feature();
        expect_histogram[3] = OwnedValue::U64(30).feature();
        assert_eq!(histogram, expect_histogram);
    }
}
<|endoftext|>


<commit_before>use circle::Circle;
use fx32::Fx32;
use fx32::Fx32Small;
use ray2::Ray2;
use vec2::Vec2;
use wide_line::WideLine;

use std::cmp;

use std::io::Error;
use std::io::prelude::*;
use std::io::BufWriter;
use std::fs::File;

#[derive (Clone, Copy)]
pub enum Ray2TraceResult {
	Hit (Fx32, Vec2 <Fx32>, Vec2 <Fx32Small>),
	Miss,
}

pub fn fold_closer_result (a: Ray2TraceResult, b: Ray2TraceResult) -> Ray2TraceResult {
	match a {
		Ray2TraceResult::Miss => {
			return b;
		},
		Ray2TraceResult::Hit (a_t, ..) => {
			match b {
				Ray2TraceResult::Miss => {
					return a;
				},
				Ray2TraceResult::Hit (b_t, ..) => {
					if a_t < b_t {
						return a;
					}
					else {
						return b;
					}
				}
			}
		},
	}
}

pub fn test_ray_trace () -> Result <(), Error> {
	let scale = 1;
	//let scale_fx = Fx32::from_int (scale);

	let obstacle = vec! [
	Circle {
		center: Vec2 {x: Fx32::from_q (133, scale), y: Fx32::from_q (512 - 34, scale)},
		radius: Fx32::from_q (20, scale),
	},
	Circle {
		center: Vec2 {x: Fx32::from_q (256, scale), y: Fx32::from_q (501, scale)},
		radius: Fx32::from_q (20, scale),
	},
	Circle {
		center: Vec2 {x: Fx32::from_q (404, scale), y: Fx32::from_q (512 - 41, scale)},
		radius: Fx32::from_q (20, scale),
	},
	];

	let lines = vec! [
	WideLine {
		start: obstacle [0].center,
		end: obstacle [1].center,
		radius: Fx32::from_q (20, scale),
	},
	];

	let mut num_bounces = 0;
	let mut num_ticks = 0;

	let inv_dt = 64;
	let gravity = Vec2::<Fx32> {
		x: Fx32::from_q (0, 1),
		y: Fx32::from_q (2, 1),
	};

	let obj_file = try!(File::create("lines.obj"));
	let mut writer = BufWriter::new (obj_file);

	let mut vertex_i = 1;
	let mut polyline_start = vertex_i;

	for x in 0..256 {
		let x = x * 2;
		let mut particle = Ray2 {
			start: Vec2 {
				x: Fx32::from_q (x * 2 + 1, scale * 2),
				y: Fx32::from_q (0, scale)
			},
			dir: Vec2 {
				x: Fx32::from_q (0, scale),
				y: Fx32::from_q (0, scale),
			},
		};

		try! (write! (writer, "v {} {} 0\n", particle.start.x.to_f64 (), particle.start.y.to_f64 ()));
		vertex_i += 1;

		for _ in 0..4000 {
			let trace_result = {
				let point_results = obstacle.iter ().map (|obstacle| ray_trace_circle_2 (&particle, obstacle));

				let line_results = lines.iter ().map (|line| ray_trace_line (&particle, line));

				point_results.chain (line_results).fold ( Ray2TraceResult::Miss, fold_closer_result)
			};

			let dt = Fx32::from_q (1, inv_dt).to_small ();

			match trace_result {
				Ray2TraceResult::Miss => {
					let air_drag = Fx32 { x: particle.dir.length_sq ().x / -16384 };
					let air_force = particle.dir * air_drag;
					let new_dir = particle.dir + gravity * dt + Vec2::<Fx32>::from (air_force * dt);
					let sum_dir = particle.dir + new_dir;
					let average_dir = Vec2::<Fx32> {
						x: Fx32 { x: sum_dir.x.x / 2 },
						y: Fx32 { x: sum_dir.y.x / 2 },
					};

					particle.start = particle.start + (average_dir * dt);
					particle.dir = new_dir;
				},
				Ray2TraceResult::Hit (_, ccd_pos, normal) => {
					particle.start = ccd_pos;
					particle.dir = particle.dir.reflect (normal) * Fx32::from_q (1023, 1024);
					//particle.dir = normal;
					num_bounces += 1;
				},
			};

			try! (write! (writer, "v {} {} 0\n", particle.start.x.to_f64 (), particle.start.y.to_f64 ()));
			vertex_i += 1;

			num_ticks += 1;

			if particle.start.y > 768 {
				break;
			}
		}

		for i in polyline_start..vertex_i - 1 {
			try! (write! (writer, "f {} {}\n", i, i + 1));
		}
		polyline_start = vertex_i
	}

	println! ("num_bounces: {}", num_bounces);
	println! ("num_ticks: {}", num_ticks);

	Ok (())
}

pub struct Basis2 {
	pub x: Vec2 <Fx32Small>,
	pub y: Vec2 <Fx32Small>,
}

pub fn get_ray_basis (ray: &Ray2, ray_length: Fx32) -> Basis2 {
	let basis_x_big = if ray_length == 0 {
		ray.dir
	}
	else {
		ray.dir / ray_length
	};

	Basis2 {
		x: basis_x_big.to_small (),
		y: basis_x_big.cross ().to_small (),
	}
}

pub fn ray_trace_line (ray: &Ray2, line: &WideLine) -> Ray2TraceResult {
	Ray2TraceResult::Miss
}

pub fn ray_trace_circle_2 (ray: &Ray2, circle: &Circle) -> Ray2TraceResult {
	let ray_length = ray.dir.length ();
	let basis = get_ray_basis (ray, ray_length);

	let to_circle = circle.center - ray.start;

	let center_in_ray_space = Vec2::<Fx32> {
		x: to_circle * basis.x,
		y: to_circle * basis.y,
	};

	if center_in_ray_space.x < 0 {
		return Ray2TraceResult::Miss;
	}

	if center_in_ray_space.x > ray_length + circle.radius {
		return Ray2TraceResult::Miss;
	}

	if center_in_ray_space.y.abs () >= circle.radius {
		// Prevents a negative root later on
		return Ray2TraceResult::Miss;
	}

	let ray_space_x = center_in_ray_space.x - (circle.radius.square () - center_in_ray_space.y.square ()).sqrt ();

	if ray_space_x > ray_length || ray_length == 0 {
		return Ray2TraceResult::Miss;
	}

	let t = ray_space_x / ray_length;
	let ccd_pos = ray.start + ray.dir * t;

	if t <= 1 {
		return Ray2TraceResult::Hit (
			Fx32 { x: cmp::max (t.x, Fx32::from_int (0).x) },
			ccd_pos,
			((ccd_pos - circle.center) / circle.radius).to_small (),
		);
	}
	else {
		return Ray2TraceResult::Miss;
	}
}
<commit_msg>Extracting basis struct<commit_after>use circle::Circle;
use fx32::Fx32;
use fx32::Fx32Small;
use ray2::Ray2;
use vec2::Vec2;
use wide_line::WideLine;

use std::cmp;

use std::io::Error;
use std::io::prelude::*;
use std::io::BufWriter;
use std::fs::File;

#[derive (Clone, Copy)]
pub enum Ray2TraceResult {
	Hit (Fx32, Vec2 <Fx32>, Vec2 <Fx32Small>),
	Miss,
}

pub fn fold_closer_result (a: Ray2TraceResult, b: Ray2TraceResult) -> Ray2TraceResult {
	match a {
		Ray2TraceResult::Miss => {
			return b;
		},
		Ray2TraceResult::Hit (a_t, ..) => {
			match b {
				Ray2TraceResult::Miss => {
					return a;
				},
				Ray2TraceResult::Hit (b_t, ..) => {
					if a_t < b_t {
						return a;
					}
					else {
						return b;
					}
				}
			}
		},
	}
}

pub fn test_ray_trace () -> Result <(), Error> {
	let scale = 1;
	//let scale_fx = Fx32::from_int (scale);

	let obstacle = vec! [
	Circle {
		center: Vec2 {x: Fx32::from_q (133, scale), y: Fx32::from_q (512 - 34, scale)},
		radius: Fx32::from_q (20, scale),
	},
	Circle {
		center: Vec2 {x: Fx32::from_q (256, scale), y: Fx32::from_q (501, scale)},
		radius: Fx32::from_q (20, scale),
	},
	Circle {
		center: Vec2 {x: Fx32::from_q (404, scale), y: Fx32::from_q (512 - 41, scale)},
		radius: Fx32::from_q (20, scale),
	},
	];

	let lines = vec! [
	WideLine {
		start: obstacle [0].center,
		end: obstacle [1].center,
		radius: Fx32::from_q (20, scale),
	},
	];

	let mut num_bounces = 0;
	let mut num_ticks = 0;

	let inv_dt = 64;
	let gravity = Vec2::<Fx32> {
		x: Fx32::from_q (0, 1),
		y: Fx32::from_q (2, 1),
	};

	let obj_file = try!(File::create("lines.obj"));
	let mut writer = BufWriter::new (obj_file);

	let mut vertex_i = 1;
	let mut polyline_start = vertex_i;

	for x in 0..256 {
		let x = x * 2;
		let mut particle = Ray2 {
			start: Vec2 {
				x: Fx32::from_q (x * 2 + 1, scale * 2),
				y: Fx32::from_q (0, scale)
			},
			dir: Vec2 {
				x: Fx32::from_q (0, scale),
				y: Fx32::from_q (0, scale),
			},
		};

		try! (write! (writer, "v {} {} 0\n", particle.start.x.to_f64 (), particle.start.y.to_f64 ()));
		vertex_i += 1;

		for _ in 0..4000 {
			let trace_result = {
				let point_results = obstacle.iter ().map (|obstacle| ray_trace_circle_2 (&particle, obstacle));

				let line_results = lines.iter ().map (|line| ray_trace_line (&particle, line));

				point_results.chain (line_results).fold ( Ray2TraceResult::Miss, fold_closer_result)
			};

			let dt = Fx32::from_q (1, inv_dt).to_small ();

			match trace_result {
				Ray2TraceResult::Miss => {
					let air_drag = Fx32 { x: particle.dir.length_sq ().x / -16384 };
					let air_force = particle.dir * air_drag;
					let new_dir = particle.dir + gravity * dt + Vec2::<Fx32>::from (air_force * dt);
					let sum_dir = particle.dir + new_dir;
					let average_dir = Vec2::<Fx32> {
						x: Fx32 { x: sum_dir.x.x / 2 },
						y: Fx32 { x: sum_dir.y.x / 2 },
					};

					particle.start = particle.start + (average_dir * dt);
					particle.dir = new_dir;
				},
				Ray2TraceResult::Hit (_, ccd_pos, normal) => {
					particle.start = ccd_pos;
					particle.dir = particle.dir.reflect (normal) * Fx32::from_q (1023, 1024);
					//particle.dir = normal;
					num_bounces += 1;
				},
			};

			try! (write! (writer, "v {} {} 0\n", particle.start.x.to_f64 (), particle.start.y.to_f64 ()));
			vertex_i += 1;

			num_ticks += 1;

			if particle.start.y > 768 {
				break;
			}
		}

		for i in polyline_start..vertex_i - 1 {
			try! (write! (writer, "f {} {}\n", i, i + 1));
		}
		polyline_start = vertex_i
	}

	println! ("num_bounces: {}", num_bounces);
	println! ("num_ticks: {}", num_ticks);

	Ok (())
}

pub struct Basis2 {
	pub x: Vec2 <Fx32Small>,
	pub y: Vec2 <Fx32Small>,
}

// Constructs a basis to ray space
// Such that the ray is the X axis
pub fn get_ray_basis (ray: &Ray2, ray_length: Fx32) -> Basis2 {
	let basis_x_big = if ray_length == 0 {
		ray.dir
	}
	else {
		ray.dir / ray_length
	};

	Basis2 {
		x: basis_x_big.to_small (),
		y: basis_x_big.cross ().to_small (),
	}
}

impl Basis2 {
	pub fn to_space (&self, v: &Vec2 <Fx32>) -> Vec2 <Fx32> {
		Vec2::<Fx32> {
			x: *v * self.x,
			y: *v * self.y,
		}
	}
}

pub fn ray_trace_line (ray: &Ray2, line: &WideLine) -> Ray2TraceResult {
	Ray2TraceResult::Miss
}

pub fn ray_trace_circle_2 (ray: &Ray2, circle: &Circle) -> Ray2TraceResult {
	let ray_length = ray.dir.length ();
	let basis = get_ray_basis (ray, ray_length);

	let center_in_ray_space = basis.to_space (&(circle.center - ray.start));

	if center_in_ray_space.x < 0 {
		return Ray2TraceResult::Miss;
	}

	if center_in_ray_space.x > ray_length + circle.radius {
		return Ray2TraceResult::Miss;
	}

	if center_in_ray_space.y.abs () >= circle.radius {
		// Prevents a negative root later on
		return Ray2TraceResult::Miss;
	}

	let ray_space_x = center_in_ray_space.x - (circle.radius.square () - center_in_ray_space.y.square ()).sqrt ();

	if ray_space_x > ray_length || ray_length == 0 {
		return Ray2TraceResult::Miss;
	}

	let t = ray_space_x / ray_length;
	let ccd_pos = ray.start + ray.dir * t;

	if t <= 1 {
		return Ray2TraceResult::Hit (
			Fx32 { x: cmp::max (t.x, Fx32::from_int (0).x) },
			ccd_pos,
			((ccd_pos - circle.center) / circle.radius).to_small (),
		);
	}
	else {
		return Ray2TraceResult::Miss;
	}
}
<|endoftext|>


<commit_before>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{FullNode, HealthCheckError, LocalVersion, Node, NodeExt, Validator, Version};
use anyhow::{anyhow, Context, Result};
use diem_config::config::NodeConfig;
use diem_logger::{debug, warn};
use diem_sdk::types::{account_address::AccountAddress, PeerId};
use std::{
    env,
    fs::{self, OpenOptions},
    path::PathBuf,
    process::{Child, Command},
    str::FromStr,
};
use url::Url;

#[derive(Debug)]
struct Process(Child);

impl Drop for Process {
    // When the Process struct goes out of scope we need to kill the child process
    fn drop(&mut self) {
        // check if the process has already been terminated
        match self.0.try_wait() {
            // The child process has already terminated, perhaps due to a crash
            Ok(Some(_)) => {}

            // The process is still running so we need to attempt to kill it
            _ => {
                self.0.kill().expect("Process wasn't running");
                self.0.wait().unwrap();
            }
        }
    }
}

#[derive(Debug)]
pub struct LocalNode {
    version: LocalVersion,
    process: Option<Process>,
    name: String,
    peer_id: AccountAddress,
    directory: PathBuf,
    config: NodeConfig,
}

impl LocalNode {
    pub fn new(version: LocalVersion, name: String, directory: PathBuf) -> Result<Self> {
        let config_path = directory.join("node.yaml");
        let config = NodeConfig::load(&config_path)
            .with_context(|| format!("Failed to load NodeConfig from file: {:?}", config_path))?;
        let peer_id = config
            .peer_id()
            .ok_or_else(|| anyhow!("unable to retrieve PeerId from config"))?;

        Ok(Self {
            version,
            process: None,
            name,
            peer_id,
            directory,
            config,
        })
    }

    pub fn config_path(&self) -> PathBuf {
        self.directory.join("node.yaml")
    }

    pub fn log_path(&self) -> PathBuf {
        self.directory.join("log")
    }

    pub fn peer_id(&self) -> PeerId {
        self.peer_id
    }

    pub fn name(&self) -> &str {
        &self.name
    }

    pub fn start(&mut self) -> Result<()> {
        // Ensure log file exists
        let log_file = OpenOptions::new()
            .create(true)
            .write(true)
            .open(self.log_path())?;

        // Start node process
        let mut node_command = Command::new(self.version.bin());
        node_command
            .current_dir(&self.directory)
            .arg("-f")
            .arg(self.config_path());
        if env::var("RUST_LOG").is_err() {
            // Only set our RUST_LOG if its not present in environment
            node_command.env("RUST_LOG", "debug");
        }
        node_command.stdout(log_file.try_clone()?).stderr(log_file);
        let process = node_command.spawn().with_context(|| {
            format!(
                "Error launching node process with binary: {:?}",
                self.version.bin()
            )
        })?;

        self.process = Some(Process(process));

        Ok(())
    }

    pub fn stop(&mut self) {
        self.process = None;
    }

    pub fn port(&self) -> u16 {
        self.config.api.address.port()
    }

    pub fn debug_port(&self) -> u16 {
        self.config
            .debug_interface
            .admission_control_node_debug_port
    }

    pub fn config(&self) -> &NodeConfig {
        &self.config
    }

    pub(crate) fn config_mut(&mut self) -> &mut NodeConfig {
        &mut self.config
    }

    pub fn upgrade(&mut self, version: LocalVersion) -> Result<()> {
        self.stop();
        self.version = version;
        self.start()
    }

    pub fn get_log_contents(&self) -> Result<String> {
        fs::read_to_string(self.log_path()).map_err(Into::into)
    }

    pub async fn health_check(&mut self) -> Result<(), HealthCheckError> {
        debug!("Health check on node '{}'", self.name);

        if let Some(p) = &mut self.process {
            match p.0.try_wait() {
                // This would mean the child process has crashed
                Ok(Some(status)) => {
                    debug!("Node '{}' crashed with: {}", self.name, status);
                    return Err(HealthCheckError::NotRunning);
                }

                // This is the case where the node is still running
                Ok(None) => {}

                // Some other unknown error
                Err(e) => {
                    return Err(HealthCheckError::Unknown(e.into()));
                }
            }
        } else {
            warn!("Node '{}' is stopped", self.name);
            return Err(HealthCheckError::NotRunning);
        }

        self.debug_client()
            .get_node_metrics()
            .await
            .map(|_| ())
            .map_err(HealthCheckError::Failure)?;

        self.rest_client()
            .get_ledger_information()
            .await
            .map(|_| ())
            .map_err(HealthCheckError::Failure)
    }
}

#[async_trait::async_trait]
impl Node for LocalNode {
    fn peer_id(&self) -> PeerId {
        self.peer_id()
    }

    fn name(&self) -> &str {
        self.name()
    }

    fn version(&self) -> Version {
        self.version.version()
    }

    fn json_rpc_endpoint(&self) -> Url {
        let ip = self.config().json_rpc.address.ip();
        let port = self.config().json_rpc.address.port();
        Url::from_str(&format!("http://{}:{}/v1", ip, port)).expect("Invalid URL.")
    }

    fn rest_api_endpoint(&self) -> Url {
        let ip = self.config().api.address.ip();
        let port = self.config().api.address.port();
        Url::from_str(&format!("http://{}:{}", ip, port)).expect("Invalid URL.")
    }

    fn debug_endpoint(&self) -> Url {
        Url::parse(&format!("http://localhost:{}", self.debug_port())).unwrap()
    }

    fn config(&self) -> &NodeConfig {
        self.config()
    }

    async fn start(&mut self) -> Result<()> {
        self.start()
    }

    fn stop(&mut self) -> Result<()> {
        self.stop();
        Ok(())
    }

    fn clear_storage(&mut self) -> Result<()> {
        todo!()
    }

    async fn health_check(&mut self) -> Result<(), HealthCheckError> {
        self.health_check().await
    }

    fn counter(&self, _counter: &str, _port: u64) -> Result<f64> {
        todo!()
    }

    // local node does not need to expose metric end point
    fn expose_metric(&self) -> Result<u64> {
        Ok(0)
    }
}

impl Validator for LocalNode {}
impl FullNode for LocalNode {}
<commit_msg>[Forge] Use append mode when restarting nodes.<commit_after>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{FullNode, HealthCheckError, LocalVersion, Node, NodeExt, Validator, Version};
use anyhow::{anyhow, Context, Result};
use diem_config::config::NodeConfig;
use diem_logger::{debug, warn};
use diem_sdk::types::{account_address::AccountAddress, PeerId};
use std::{
    env,
    fs::{self, OpenOptions},
    path::PathBuf,
    process::{Child, Command},
    str::FromStr,
};
use url::Url;

#[derive(Debug)]
struct Process(Child);

impl Drop for Process {
    // When the Process struct goes out of scope we need to kill the child process
    fn drop(&mut self) {
        // check if the process has already been terminated
        match self.0.try_wait() {
            // The child process has already terminated, perhaps due to a crash
            Ok(Some(_)) => {}

            // The process is still running so we need to attempt to kill it
            _ => {
                self.0.kill().expect("Process wasn't running");
                self.0.wait().unwrap();
            }
        }
    }
}

#[derive(Debug)]
pub struct LocalNode {
    version: LocalVersion,
    process: Option<Process>,
    name: String,
    peer_id: AccountAddress,
    directory: PathBuf,
    config: NodeConfig,
}

impl LocalNode {
    pub fn new(version: LocalVersion, name: String, directory: PathBuf) -> Result<Self> {
        let config_path = directory.join("node.yaml");
        let config = NodeConfig::load(&config_path)
            .with_context(|| format!("Failed to load NodeConfig from file: {:?}", config_path))?;
        let peer_id = config
            .peer_id()
            .ok_or_else(|| anyhow!("unable to retrieve PeerId from config"))?;

        Ok(Self {
            version,
            process: None,
            name,
            peer_id,
            directory,
            config,
        })
    }

    pub fn config_path(&self) -> PathBuf {
        self.directory.join("node.yaml")
    }

    pub fn log_path(&self) -> PathBuf {
        self.directory.join("log")
    }

    pub fn peer_id(&self) -> PeerId {
        self.peer_id
    }

    pub fn name(&self) -> &str {
        &self.name
    }

    pub fn start(&mut self) -> Result<()> {
        // Ensure log file exists
        let log_file = OpenOptions::new()
            .create(true)
            .write(true)
            .append(true)
            .open(self.log_path())?;

        // Start node process
        let mut node_command = Command::new(self.version.bin());
        node_command
            .current_dir(&self.directory)
            .arg("-f")
            .arg(self.config_path());
        if env::var("RUST_LOG").is_err() {
            // Only set our RUST_LOG if its not present in environment
            node_command.env("RUST_LOG", "debug");
        }
        node_command.stdout(log_file.try_clone()?).stderr(log_file);
        let process = node_command.spawn().with_context(|| {
            format!(
                "Error launching node process with binary: {:?}",
                self.version.bin()
            )
        })?;

        self.process = Some(Process(process));

        Ok(())
    }

    pub fn stop(&mut self) {
        self.process = None;
    }

    pub fn port(&self) -> u16 {
        self.config.api.address.port()
    }

    pub fn debug_port(&self) -> u16 {
        self.config
            .debug_interface
            .admission_control_node_debug_port
    }

    pub fn config(&self) -> &NodeConfig {
        &self.config
    }

    pub(crate) fn config_mut(&mut self) -> &mut NodeConfig {
        &mut self.config
    }

    pub fn upgrade(&mut self, version: LocalVersion) -> Result<()> {
        self.stop();
        self.version = version;
        self.start()
    }

    pub fn get_log_contents(&self) -> Result<String> {
        fs::read_to_string(self.log_path()).map_err(Into::into)
    }

    pub async fn health_check(&mut self) -> Result<(), HealthCheckError> {
        debug!("Health check on node '{}'", self.name);

        if let Some(p) = &mut self.process {
            match p.0.try_wait() {
                // This would mean the child process has crashed
                Ok(Some(status)) => {
                    debug!("Node '{}' crashed with: {}", self.name, status);
                    return Err(HealthCheckError::NotRunning);
                }

                // This is the case where the node is still running
                Ok(None) => {}

                // Some other unknown error
                Err(e) => {
                    return Err(HealthCheckError::Unknown(e.into()));
                }
            }
        } else {
            warn!("Node '{}' is stopped", self.name);
            return Err(HealthCheckError::NotRunning);
        }

        self.debug_client()
            .get_node_metrics()
            .await
            .map(|_| ())
            .map_err(HealthCheckError::Failure)?;

        self.rest_client()
            .get_ledger_information()
            .await
            .map(|_| ())
            .map_err(HealthCheckError::Failure)
    }
}

#[async_trait::async_trait]
impl Node for LocalNode {
    fn peer_id(&self) -> PeerId {
        self.peer_id()
    }

    fn name(&self) -> &str {
        self.name()
    }

    fn version(&self) -> Version {
        self.version.version()
    }

    fn json_rpc_endpoint(&self) -> Url {
        let ip = self.config().json_rpc.address.ip();
        let port = self.config().json_rpc.address.port();
        Url::from_str(&format!("http://{}:{}/v1", ip, port)).expect("Invalid URL.")
    }

    fn rest_api_endpoint(&self) -> Url {
        let ip = self.config().api.address.ip();
        let port = self.config().api.address.port();
        Url::from_str(&format!("http://{}:{}", ip, port)).expect("Invalid URL.")
    }

    fn debug_endpoint(&self) -> Url {
        Url::parse(&format!("http://localhost:{}", self.debug_port())).unwrap()
    }

    fn config(&self) -> &NodeConfig {
        self.config()
    }

    async fn start(&mut self) -> Result<()> {
        self.start()
    }

    fn stop(&mut self) -> Result<()> {
        self.stop();
        Ok(())
    }

    fn clear_storage(&mut self) -> Result<()> {
        todo!()
    }

    async fn health_check(&mut self) -> Result<(), HealthCheckError> {
        self.health_check().await
    }

    fn counter(&self, _counter: &str, _port: u64) -> Result<f64> {
        todo!()
    }

    // local node does not need to expose metric end point
    fn expose_metric(&self) -> Result<u64> {
        Ok(0)
    }
}

impl Validator for LocalNode {}
impl FullNode for LocalNode {}
<|endoftext|>


<commit_before>use piston_window;
use gfx_device_gl::{Resources, Output, CommandBuffer};
use gfx_graphics::GfxGraphics;

use piston_window::Transformed;
use piston_window::ImageSize;

#[derive(Debug)]
pub struct Object {
    x: f64,
    y: f64,
    sprite: piston_window::Texture<Resources>,
}

impl Object {
    pub fn new(sprite: piston_window::Texture<Resources>) -> Object {
        Object { x: 0., y: 0., sprite: sprite }
    }

    pub fn mov(&mut self, x: f64, y: f64) {
        self.x += x;
        self.y += y;
    }

    pub fn mov_to(&mut self, x: f64, y: f64) {
        self.x = x;
        self.y = y;
    }

    pub fn render(&self, graphic: &mut GfxGraphics<Resources, CommandBuffer<Resources>, Output>, view: &piston_window::math::Matrix2d) {
        let (x, y) = self.sprite.get_size();
        let (x, y) = (x / 2, y / 2);
        piston_window::image(&self.sprite, view.trans(self.x, self.y).trans(-(x as f64), -(y as f64)), graphic);
    }
}

#[test]
fn object_new() {
    let object = Object::new();
    assert_eq!(object.x, 0.0);
    assert_eq!(object.y, 0.0);
}

#[test]
fn object_mov() {
    let mut object = Object::new();
    assert_eq!(object.x, 0.);
    assert_eq!(object.y, 0.);

    object.mov(0., 0.);
    assert_eq!(object.x, 0.);
    assert_eq!(object.y, 0.);

    object.mov(1., 1.);
    assert_eq!(object.x, 1.);
    assert_eq!(object.y, 1.);

    object.mov(-3., -3.);
    assert_eq!(object.x, -2.);
    assert_eq!(object.y, -2.);
}

#[test]
fn object_mov_to() {
    let mut object = Object::new();
    assert_eq!(object.x, 0.);
    assert_eq!(object.y, 0.);

    object.mov_to(0., 0.);
    assert_eq!(object.x, 0.);
    assert_eq!(object.y, 0.);

    object.mov_to(10., 10.);
    assert_eq!(object.x, 10.);
    assert_eq!(object.y, 10.);

    object.mov_to(-30., -30.);
    assert_eq!(object.x, -30.);
    assert_eq!(object.y, -30.);
}
<commit_msg>Test module and create_object function<commit_after>use piston_window;
use gfx_device_gl::{Resources, Output, CommandBuffer};
use gfx_graphics::GfxGraphics;

use piston_window::Transformed;
use piston_window::ImageSize;

#[derive(Debug)]
pub struct Object {
    x: f64,
    y: f64,
    sprite: piston_window::Texture<Resources>,
}

impl Object {
    pub fn new(sprite: piston_window::Texture<Resources>) -> Object {
        Object { x: 0., y: 0., sprite: sprite }
    }

    pub fn mov(&mut self, x: f64, y: f64) {
        self.x += x;
        self.y += y;
    }

    pub fn mov_to(&mut self, x: f64, y: f64) {
        self.x = x;
        self.y = y;
    }

    pub fn render(&self, graphic: &mut GfxGraphics<Resources, CommandBuffer<Resources>, Output>, view: &piston_window::math::Matrix2d) {
        let (x, y) = self.sprite.get_size();
        let (x, y) = (x / 2, y / 2);
        piston_window::image(&self.sprite, view.trans(self.x, self.y).trans(-(x as f64), -(y as f64)), graphic);
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    fn create_object() -> Object {
        Object::new()
    }

    #[test]
    fn object_new() {
        let object = create_object();
        assert_eq!(object.x, 0.0);
        assert_eq!(object.y, 0.0);
    }

    #[test]
    fn object_mov() {
        let mut object = create_object();
        assert_eq!(object.x, 0.);
        assert_eq!(object.y, 0.);

        object.mov(0., 0.);
        assert_eq!(object.x, 0.);
        assert_eq!(object.y, 0.);

        object.mov(1., 1.);
        assert_eq!(object.x, 1.);
        assert_eq!(object.y, 1.);

        object.mov(-3., -3.);
        assert_eq!(object.x, -2.);
        assert_eq!(object.y, -2.);
    }

    #[test]
    fn object_mov_to() {
        let mut object = create_object();
        assert_eq!(object.x, 0.);
        assert_eq!(object.y, 0.);

        object.mov_to(0., 0.);
        assert_eq!(object.x, 0.);
        assert_eq!(object.y, 0.);

        object.mov_to(10., 10.);
        assert_eq!(object.x, 10.);
        assert_eq!(object.y, 10.);

        object.mov_to(-30., -30.);
        assert_eq!(object.x, -30.);
        assert_eq!(object.y, -30.);
    }
}
<|endoftext|>


<commit_before>use std::comm::SharedChan;
use std::hashmap::HashMap;
use std::hash::Hash;

fn main() {
    // Reading 10 books from Project Gutenberg
    let mut docs: ~[~str] = ~[~"books/Beowulf.txt", ~"books/Adventures_in_Wonderland.txt", ~"books/Pride_and_Prejudice.txt",
                                ~"books/herlock_Holmes.txt", ~"books/The_Prince.txt", ~"books/Dorian_Gray.txt", ~"books/Dracula.txt",
                                ~"books/Dubliners.txt", ~"books/Great_Expectations.txt", ~"books/Siddhartha.txt"];

    docs = docs.map(|file| {
        readFile(file.to_owned())
    });

    // Defining instance of mapper to get word counts
    fn mapper(doc: ~str) -> ~[(~str, uint)] {
        let words: ~[~str] = doc.split_iter(' ')
            .filter(|&x| x != "")
            .map(|x| {
                x.to_owned()
            }).collect();

        let mut ret: ~[(~str, uint)] = ~[];
        for w in words.iter() {
            let tup: (~str, uint) = (w.to_str(), 1);
            ret.push(tup);
        }

        ret
    }

    // Defining instance of reducer to get word counts
    fn reducer(key: ~str, vals: ~[uint]) -> ~[(~str, uint)] {
        let mut result: uint = 0;
        for val in vals.iter() {
            result += *val;
        }
        ~[(key, result)]
    }

    // Launching job
    docs.mapreduce::<~str,uint>(mapper, reducer);

}

// Exposing trait
trait MapReduce {
    fn mapreduce<K2: Clone + Send + Hash + Equiv<K2> + Eq, V2: Clone + Send>( &self, extern fn(~str) -> ~[(K2, V2)], extern fn(K2, ~[V2]) -> ~[(K2, V2)] );
}

// Implementation of mapreduce trait
impl MapReduce for ~[~str] {
    fn mapreduce<K2: Clone + Send + Hash + Equiv<K2> + Eq, V2: Clone + Send>( &self, mapper: extern fn(~str) -> ~[(K2, V2)], reducer: extern fn(K2, ~[V2]) -> ~[(K2, V2)] ) {
        // First we map in parallel
        let (port, chan): (Port<~[(K2, V2)]>, Chan<~[(K2, V2)]>) = stream();
        let chan = SharedChan::new(chan);
        let mut chans: int = 0;
        for doc in self.iter() {
            let child_chan = chan.clone();
            let doc_owned = doc.clone();
            chans += 1;
            do spawn {
                let ivals: ~[(K2, V2)] = mapper(doc_owned.clone());
                child_chan.send(ivals);
            }
        }

        // Aggregate intermediate keys
        let mut key_vals_map: HashMap<K2, ~[V2]> = HashMap::new();
        for _ in range(0, chans) {
            let ivals: ~[(K2, V2)] = port.recv();

            for ival in ivals.iter() {

		let mut key: ~str;
		let mut val: ~str;
	    	match (*ival).clone() {
		      (a, b) => { key = a; val = b;},
		}

//                let key = ival.n0();
//                let val = ival.n1();

                if key_vals_map.contains_key_equiv(&key) {
                    let list = key_vals_map.get_mut(&key);
                    list.push(val);
                } else {
                    key_vals_map.find_or_insert(key, ~[val]);
                }
            }
        }

        // Finally, reduce in parallel
        chans = 0;
        key_vals_map.each_key(|key| {
                let values = key_vals_map.get(key);
                let child_chan = chan.clone();
                let key_owned = key.clone();
                let values_owned = values.clone();
                chans += 1;
                do spawn {
                    let rvals: ~[(K2, V2)] = reducer(key_owned.clone(), values_owned.clone());
                    child_chan.send(rvals);
                }
                true
            });

        // Print reduced values
        for _ in range(0, chans) {
            let rvals: ~[(K2, V2)] = port.recv();
            println(fmt!("%?", rvals));
        }
    }
}

// Auxiliary function to read entire file
fn readFile(filename: ~str) -> ~str {
    let contents = std::io::read_whole_file_str(&std::path::PosixPath{
        is_absolute: false,
        components: ~[filename]
    });

    let ret = match contents {
        Ok(T) => T,
        _ => ~""
    };
    ret
}
<commit_msg>Fixed compatibility, added generics<commit_after>use std::comm::SharedChan;
use std::hashmap::HashMap;
use std::hash::Hash;

fn main() {
    // Reading 10 books from Project Gutenberg
    let mut docs: ~[~str] = ~[~"books/Beowulf.txt", ~"books/Adventures_in_Wonderland.txt", ~"books/Pride_and_Prejudice.txt",
                                ~"books/herlock_Holmes.txt", ~"books/The_Prince.txt", ~"books/Dorian_Gray.txt", ~"books/Dracula.txt",
                                ~"books/Dubliners.txt", ~"books/Great_Expectations.txt", ~"books/Siddhartha.txt"];

    docs = docs.map(|file| {
        readFile(file.to_owned())
    });

    // Defining instance of mapper to get word counts
    fn mapper(doc: ~str) -> ~[(~str, uint)] {
        let words: ~[~str] = doc.split_iter(' ')
            .filter(|&x| x != "")
            .map(|x| {
                x.to_owned()
            }).collect();

        let mut ret: ~[(~str, uint)] = ~[];
        for w in words.iter() {
            let tup: (~str, uint) = (w.to_str(), 1);
            ret.push(tup);
        }

        ret
    }

    // Defining instance of reducer to get word counts
    fn reducer(key: ~str, vals: ~[uint]) -> ~[(~str, uint)] {
        let mut result: uint = 0;
        for val in vals.iter() {
            result += *val;
        }
        ~[(key, result)]
    }

    // Launching job
    docs.mapreduce::<~str,uint>(mapper, reducer);

}

// Exposing trait
trait MapReduce {
    fn mapreduce<K2: Clone + Send + Hash + Equiv<K2> + Eq, V2: Clone + Send>( &self, extern fn(~str) -> ~[(K2, V2)], extern fn(K2, ~[V2]) -> ~[(K2, V2)] );
}

// Implementation of mapreduce trait
impl MapReduce for ~[~str] {
    fn mapreduce<K2: Clone + Send + Hash + Equiv<K2> + Eq, V2: Clone + Send>( &self, mapper: extern fn(~str) -> ~[(K2, V2)], reducer: extern fn(K2, ~[V2]) -> ~[(K2, V2)] ) {
        // First we map in parallel
        let (port, chan): (Port<~[(K2, V2)]>, Chan<~[(K2, V2)]>) = stream();
        let chan = SharedChan::new(chan);
        let mut chans: int = 0;
        for doc in self.iter() {
            let child_chan = chan.clone();
            let doc_owned = doc.clone();
            chans += 1;
            do spawn {
                let ivals: ~[(K2, V2)] = mapper(doc_owned.clone());
                child_chan.send(ivals);
            }
        }

        // Aggregate intermediate keys
        let mut key_vals_map: HashMap<K2, ~[V2]> = HashMap::new();
        for _ in range(0, chans) {
            let ivals: ~[(K2, V2)] = port.recv();

            for ival in ivals.iter() {

		let mut key: K2;
		let mut val: V2;
	    	match (*ival).clone() {
		      (a, b) => { key = a; val = b;},
		}

                if key_vals_map.contains_key_equiv(&key) {
                    let list = key_vals_map.get_mut(&key);
                    list.push(val);
                } else {
                    key_vals_map.find_or_insert(key, ~[val]);
                }
            }
        }

        // Finally, reduce in parallel
        chans = 0;
        key_vals_map.each_key(|key| {
                let values = key_vals_map.get(key);
                let child_chan = chan.clone();
                let key_owned = key.clone();
                let values_owned = values.clone();
                chans += 1;
                do spawn {
                    let rvals: ~[(K2, V2)] = reducer(key_owned.clone(), values_owned.clone());
                    child_chan.send(rvals);
                }
                true
            });

        // Print reduced values
        for _ in range(0, chans) {
            let rvals: ~[(K2, V2)] = port.recv();
            println(fmt!("%?", rvals));
        }
    }
}

// Auxiliary function to read entire file
fn readFile(filename: ~str) -> ~str {
    let contents = std::io::read_whole_file_str(&std::path::PosixPath{
        is_absolute: false,
        components: ~[filename]
    });

    let ret = match contents {
        Ok(T) => T,
        _ => ~""
    };
    ret
}
<|endoftext|>


<commit_before>//! Input buffer validation API (Multi-line editing)

use crate::line_buffer::LineBuffer;

pub enum ValidationResult {
    /// Validation fails with an optional error message. User must fix the input.
    Invalid(Option<String>),
    /// Validation succeeds with an optional message (instead of https://github.com/kkawakam/rustyline/pull/169)
    Valid(Option<String>),
}

/// This trait provides an extension interface for determining whether
/// the current input buffer is valid. Rustyline uses the method
/// provided by this trait to decide whether hitting the enter key
/// will end the current editing session and return the current line
/// buffer to the caller of `Editor::readline` or variants.
pub trait Validator {
    /// Takes the currently edited `line` and returns a bool
    /// indicating whether it is valid or not. The most common
    /// validity check to implement is probably whether the input is
    /// complete or not, for instance ensuring that all delimiters are
    /// fully balanced.
    ///
    /// If you implement more complex validation checks it's probably
    /// a good idea to also implement a `Hinter` to provide feedback
    /// about what is invalid.
    fn is_valid(&self, line: &mut LineBuffer) -> ValidationResult {
        let _ = line;
        ValidationResult::Valid(None)
    }
}

impl Validator for () {}
<commit_msg>WIP: Document some things<commit_after>//! Input buffer validation API (Multi-line editing)

use crate::line_buffer::LineBuffer;

///
pub enum ValidationResult {
    /// Validation fails with an optional error message. User must fix the input.
    Invalid(Option<String>),
    /// Validation succeeds with an optional message (instead of https://github.com/kkawakam/rustyline/pull/169)
    Valid(Option<String>),
}

/// This trait provides an extension interface for determining whether
/// the current input buffer is valid. Rustyline uses the method
/// provided by this trait to decide whether hitting the enter key
/// will end the current editing session and return the current line
/// buffer to the caller of `Editor::readline` or variants.
pub trait Validator {
    /// Takes the currently edited `line` and returns a
    /// ValidationResult indicating whether it is valid or not along
    /// with an option message to display about the result. The most
    /// common validity check to implement is probably whether the
    /// input is complete or not, for instance ensuring that all
    /// delimiters are fully balanced.
    ///
    /// If you implement more complex validation checks it's probably
    /// a good idea to also implement a `Hinter` to provide feedback
    /// about what is invalid.
    fn is_valid(&self, line: &mut LineBuffer) -> ValidationResult {
        let _ = line;
        ValidationResult::Valid(None)
    }

    /// Configure whether validation is performed while typing or only
    /// when user presses the Enter key.
    ///
    /// Default is `false`.
    // TODO we can implement this later.
    fn validate_while_typing(&self) -> bool {
        false
    }
}

impl Validator for () {}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2017-2018 Ryan Huang
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

mod board;
mod board_parts;
mod sys;

use imgui::{ImGuiCond, Ui};
use imgui_sys;

use model::{Color, FieldCoord, Model};
use vec2::Vec2;
use self::board::board;
pub use self::sys::run;

#[derive(PartialEq)]
pub enum Event {
    Click(FieldCoord),
    Exchange,
    NewGame,
    Resign,
    Undo,
    Redo,
}

pub fn draw(ui: &Ui, size: (f32, f32), model: &Model) -> Option<Event> {
    unsafe {
        imgui_sys::igPushStyleVar(imgui_sys::ImGuiStyleVar::WindowRounding, 0.0);
    }

    let mut event = None;

    ui.main_menu_bar(|| {
        ui.menu(im_str!("Game")).build(|| {
            if ui.menu_item(im_str!("New game")).build() {
                insert_if_empty(&mut event, Event::NewGame);
            }
        });
    });

    ui.window(im_str!("Coerceo"))
        .size(size, ImGuiCond::Always)
        .position((0.0, 19.0), ImGuiCond::Once)
        .title_bar(false)
        .resizable(false)
        .movable(false)
        .build(|| {
            ui.text("Welcome to Coerceo!");

            let click = board(model, Vec2::new(size.0 - 16.0, size.1 - 155.0));

            use model::GameResult::*;
            match model.game_result {
                Win(color) => {
                    ui.text(match color {
                        Color::Black => "Black wins!",
                        Color::White => "White wins!",
                    });
                    if model.can_undo() && ui.button(im_str!("Undo"), Vec2::new(100.0, 20.0)) {
                        insert_if_empty(&mut event, Event::Undo);
                    }
                }
                InProgress => {
                    if let Some(click) = click {
                        insert_if_empty(&mut event, click);
                    }

                    match model.board.turn() {
                        Color::White => ui.text("It's white's turn."),
                        Color::Black => ui.text("It's black's turn."),
                    }

                    ui.text(format!(
                        "White has {} piece(s) left and {} captured hex(es).",
                        model.board.white_pieces(),
                        model.board.white_hexes(),
                    ));
                    ui.text(format!(
                        "Black has {} piece(s) left and {} captured hex(es).",
                        model.board.black_pieces(),
                        model.board.black_hexes(),
                    ));

                    if model.can_undo() && ui.button(im_str!("Undo"), Vec2::new(100.0, 20.0)) {
                        insert_if_empty(&mut event, Event::Undo);
                    }
                    if model.can_redo() {
                        if model.can_undo() {
                            ui.same_line(0.0);
                        }
                        if ui.button(im_str!("Redo"), Vec2::new(100.0, 20.0)) {
                            insert_if_empty(&mut event, Event::Redo);
                        }
                    }

                    if ui.button(im_str!("Resign"), Vec2::new(100.0, 20.0)) {
                        insert_if_empty(&mut event, Event::Resign);
                    }
                    ui.same_line(0.0);
                    if model.board.can_exchange() {
                        let label = if model.exchanging {
                            im_str!("Stop Exchanging")
                        } else {
                            im_str!("Exchange")
                        };
                        if ui.button(label, Vec2::new(120.0, 20.0)) {
                            insert_if_empty(&mut event, Event::Exchange);
                        }
                    }
                }
            }
        });

    unsafe {
        imgui_sys::igPopStyleVar(1);
    }

    event
}

fn insert_if_empty<T>(a: &mut Option<T>, b: T) {
    if a.is_none() {
        *a = Some(b);
    }
}
<commit_msg>Refactor game buttons with horz_button_layout<commit_after>/*
 * Copyright (C) 2017-2018 Ryan Huang
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

mod board;
mod board_parts;
mod sys;

use imgui::{ImGuiCond, ImStr, ImVec2, Ui};
use imgui_sys;

use model::{Color, FieldCoord, Model};
use vec2::Vec2;
use self::board::board;
pub use self::sys::run;

#[derive(PartialEq)]
pub enum Event {
    Click(FieldCoord),
    Exchange,
    NewGame,
    Resign,
    Undo,
    Redo,
}

pub fn draw(ui: &Ui, size: (f32, f32), model: &Model) -> Option<Event> {
    unsafe {
        imgui_sys::igPushStyleVar(imgui_sys::ImGuiStyleVar::WindowRounding, 0.0);
    }

    let mut event = None;

    ui.main_menu_bar(|| {
        ui.menu(im_str!("Game")).build(|| {
            if ui.menu_item(im_str!("New game")).build() {
                insert_if_empty(&mut event, Event::NewGame);
            }
        });
    });

    ui.window(im_str!("Coerceo"))
        .size(size, ImGuiCond::Always)
        .position((0.0, 19.0), ImGuiCond::Once)
        .title_bar(false)
        .resizable(false)
        .movable(false)
        .build(|| {
            ui.text("Welcome to Coerceo!");

            let click = board(model, Vec2::new(size.0 - 16.0, size.1 - 155.0));

            use model::GameResult::*;
            match model.game_result {
                Win(color) => {
                    ui.text(match color {
                        Color::Black => "Black wins!",
                        Color::White => "White wins!",
                    });
                    if model.can_undo() && ui.button(im_str!("Undo"), Vec2::new(100.0, 20.0)) {
                        insert_if_empty(&mut event, Event::Undo);
                    }
                }
                InProgress => {
                    if let Some(click) = click {
                        insert_if_empty(&mut event, click);
                    }

                    match model.board.turn() {
                        Color::White => ui.text("It's white's turn."),
                        Color::Black => ui.text("It's black's turn."),
                    }

                    ui.text(format!(
                        "White has {} piece(s) left and {} captured hex(es).",
                        model.board.white_pieces(),
                        model.board.white_hexes(),
                    ));
                    ui.text(format!(
                        "Black has {} piece(s) left and {} captured hex(es).",
                        model.board.black_pieces(),
                        model.board.black_hexes(),
                    ));

                    let button_size = Vec2::new(120.0, 20.0);
                    horz_button_layout(
                        ui,
                        vec![
                            (true, im_str!("Resign"), Event::Resign),
                            (
                                model.board.can_exchange(),
                                if model.exchanging {
                                    im_str!("Stop Exchanging")
                                } else {
                                    im_str!("Exchange")
                                },
                                Event::Exchange,
                            ),
                        ],
                        &button_size,
                        &mut event,
                    );
                    horz_button_layout(
                        ui,
                        vec![
                            (model.can_undo(), im_str!("Undo"), Event::Undo),
                            (model.can_redo(), im_str!("Redo"), Event::Redo),
                        ],
                        &button_size,
                        &mut event,
                    );
                }
            }
        });

    unsafe {
        imgui_sys::igPopStyleVar(1);
    }

    event
}

fn horz_button_layout(
    ui: &Ui,
    buttons: Vec<(bool, &ImStr, Event)>,
    size: &Vec2,
    event: &mut Option<Event>,
) {
    let size: ImVec2 = (*size).into();

    for (show, label, action) in buttons {
        if show {
            if ui.button(label, size) {
                insert_if_empty(event, action);
            }
        } else {
            unsafe {
                imgui_sys::igDummy(&size);
            }
        }
        ui.same_line(0.0);
    }
    ui.new_line();
}

fn insert_if_empty<T>(a: &mut Option<T>, b: T) {
    if a.is_none() {
        *a = Some(b);
    }
}
<|endoftext|>