<commit_before>use std::f64::consts::{E, PI};

use prelude::{define_built_in, apply_program};

use math::rich_number::RichNumber;

use eval::context_item::ContextItem;
use eval::eval_context::{EvalContext, EvalError};
use eval::built_in_function::BuiltInFunction;

const UNARY_PARAMS_LENGTH: usize = 1;
const BINARY_PARAMS_LENGTH: usize = 2;

pub fn define_math(context: &mut EvalContext) {
    context.set_boolean("true", true);
    context.set_boolean("false", false);

    context.set_number("pi", RichNumber::from(PI));
    context.set_number("e", RichNumber::from(E));

    define_binary_operators(context);
    define_unary_operators(context);

    apply_program(context, include_str!("math.lion"));
}

fn define_binary_operators(context: &mut EvalContext) {
    define_numeric_binary_op(context, "pow", |a, b| a.pow(b), |_, _| false);
    define_numeric_binary_op(context, "mul", |a, b| a * b, |_, _| false);

    define_numeric_binary_op(context, "add", |a, b| a + b, |_, _| true);
    define_numeric_binary_op(context, "sub", |a, b| a - b, |_, _| true);
    define_numeric_binary_op(context, "div", |a, b| a / b,
        |a, b| a.has_units() && b.has_units());
    define_numeric_binary_op(context, "mod", |a, b| a % b,
        |a, b| a.has_units() && b.has_units());

    define_boolean_binary_op(context, "eq", |a, b| a == b, true);
    define_boolean_binary_op(context, "ne", |a, b| a != b, true);

    define_boolean_binary_op(context, "gt",
        |a, b| a.unwrap_number() > b.unwrap_number(), true);
    define_boolean_binary_op(context, "ge",
        |a, b| a.unwrap_number() >= b.unwrap_number(), true);
    define_boolean_binary_op(context, "lt",
        |a, b| a.unwrap_number() < b.unwrap_number(), true);
    define_boolean_binary_op(context, "le",
        |a, b| a.unwrap_number() <= b.unwrap_number(), true);
}

fn define_unary_operators(context: &mut EvalContext) {
    define_built_in(context, "not", UNARY_PARAMS_LENGTH,
        BuiltInFunction::new(move |_, mut params| {
            if params.len() != UNARY_PARAMS_LENGTH {
                return Err(EvalError::ExpectedParams(UNARY_PARAMS_LENGTH));
            }

            if !params[0].is_boolean() {
                return Err(EvalError::InvalidParam("Can only boolean negate a boolean value".to_owned()));
            }

            let arg = params.pop().unwrap().unwrap_boolean();
            debug_assert!(params.is_empty(), "Not all parameters used");

            Ok(ContextItem::Boolean(!arg))
        })
    );

    define_numeric_unary_op(context, "neg", |a| -a, |_| true);

    define_numeric_unary_op(context, "abs", |a| a.abs(), |_| true);
    define_numeric_unary_op(context, "floor", |a| a.floor(), |_| true);
    define_numeric_unary_op(context, "ceil", |a| a.ceil(), |_| true);
    define_numeric_unary_op(context, "round", |a| a.round(), |_| true);

    define_numeric_unary_op(context, "sqrt", |a| a.sqrt(), |a| a.is_none());
    define_numeric_unary_op(context, "ln", |a| a.ln(), |a| a.is_none());
    define_numeric_unary_op(context, "log10", |a| a.log10(), |a| a.is_none());
    define_numeric_unary_op(context, "log2", |a| a.log2(), |a| a.is_none());

    define_numeric_unary_op(context, "sin", |a| a.sin(), |a| !a.is_none() && a.unwrap() == "rad");
    define_numeric_unary_op(context, "cos", |a| a.cos(), |a| !a.is_none() && a.unwrap() == "rad");
    define_numeric_unary_op(context, "tan", |a| a.tan(), |a| !a.is_none() && a.unwrap() == "rad");

    //TODO: Find a way to set the unit to radians on the output of these methods
    define_numeric_unary_op(context, "asin", |a| a.asin(), |a| a.is_none());
    define_numeric_unary_op(context, "acos", |a| a.acos(), |a| a.is_none());
    define_numeric_unary_op(context, "atan", |a| a.atan(), |a| a.is_none());

    define_numeric_unary_op(context, "sinh", |a| a.sinh(), |a| !a.is_none() && a.unwrap() == "rad");
    define_numeric_unary_op(context, "cosh", |a| a.cosh(), |a| !a.is_none() && a.unwrap() == "rad");
    define_numeric_unary_op(context, "tanh", |a| a.tanh(), |a| !a.is_none() && a.unwrap() == "rad");

    define_numeric_unary_op(context, "asinh", |a| a.asinh(), |a| a.is_none());
    define_numeric_unary_op(context, "acosh", |a| a.acosh(), |a| a.is_none());
    define_numeric_unary_op(context, "atanh", |a| a.atanh(), |a| a.is_none());
}

/// Defines an operator that can take either booleans or numbers
/// Optionally coerces numbers into the same unit
/// Ensures that both arguments are numbers or both are booleans
fn define_boolean_binary_op<F: 'static>(context: &mut EvalContext, name: &str, operator: F, coerce_first: bool)
    where F: Fn(ContextItem, ContextItem) -> bool {

    define_built_in(context, name, BINARY_PARAMS_LENGTH,
        BuiltInFunction::new(move |context, mut params| {
            if params.len() != BINARY_PARAMS_LENGTH {
                return Err(EvalError::ExpectedParams(BINARY_PARAMS_LENGTH))
            }

            // params can be either number or boolean but not both
            if !((params[0].is_number() && params[1].is_number())
                || (params[0].is_boolean() && params[1].is_boolean())) {
                return Err(EvalError::InvalidParam("Expecting numeric or boolean argument, cannot compare numeric values to boolean values".to_owned()));
            }

            let mut rhs = params.remove(0);
            let lhs = params.remove(0);
            debug_assert!(params.is_empty(), "Not all parameters used");

            if coerce_first && lhs.is_number() && rhs.is_number() {
                // May possibly attempt to convert in the other
                // direction too in the future
                let lhs_unit = match lhs {
                    ContextItem::Number(RichNumber { ref unit, .. }) => unit,
                    _ => unreachable!(),
                };
                rhs = try!(context.convert(rhs.unwrap_number(), *lhs_unit));
            }

            Ok(ContextItem::Boolean(operator(lhs, rhs)))
        })
    );
}

/// Defines an operator that can only take numeric arguments
/// Optionally takes a function used to define whether the numeric arguments should be coerced into the same unit
fn define_numeric_binary_op<F: 'static, G: 'static>(context: &mut EvalContext, name: &str, operator: F, coerce_first: G)
    where F: Fn(RichNumber, RichNumber) -> RichNumber,
          G: Fn(RichNumber, RichNumber) -> bool {

    define_built_in(context, name, BINARY_PARAMS_LENGTH,
        BuiltInFunction::new(move |context, mut params| {
            if params.len() != BINARY_PARAMS_LENGTH {
                return Err(EvalError::ExpectedParams(BINARY_PARAMS_LENGTH))
            }

            if !params[0].is_number() || !params[1].is_number() {
                return Err(EvalError::InvalidParam("Expecting numeric argument".to_owned()));
            }

            let mut rhs = params.pop().unwrap().unwrap_number();
            let lhs = params.pop().unwrap().unwrap_number();
            debug_assert!(params.is_empty(), "Not all parameters used");

            if coerce_first(lhs, rhs) {
                // May possibly attempt to convert in the other
                // direction too in the future
                rhs = try!(context.convert(rhs, lhs.unit)).unwrap_number();
            }

            Ok(ContextItem::Number(operator(lhs, rhs)))
        })
    );
}

/// Defines an unary operator that can only take a numeric argument
fn define_numeric_unary_op<F: 'static, G: 'static>(context: &mut EvalContext, name: &'static str, operator: F, accepts_unit: G)
    where F: Fn(RichNumber) -> RichNumber,
          G: Fn(Option<String>) -> bool {

    define_built_in(context, name, UNARY_PARAMS_LENGTH,
        BuiltInFunction::new(move |context, mut params| {
            if params.len() != UNARY_PARAMS_LENGTH {
                return Err(EvalError::ExpectedParams(UNARY_PARAMS_LENGTH));
            }

            if !params[0].is_number() {
                return Err(EvalError::InvalidParam(format!("Can only apply {} to a numeric value", name)));
            }

            let arg = params.pop().unwrap().unwrap_number();
            debug_assert!(params.is_empty(), "Not all parameters used");

            let unit = arg.unit.map(|u| context.lookup_unit(u).unwrap());
            if !accepts_unit(unit.clone()) {
                return Err(EvalError::InvalidParam(format!("Function {} does not accept unit {}", name, unit.unwrap_or("(no unit)".to_owned()))));
            }

            Ok(ContextItem::Number(operator(arg)))
        })
    );
}

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

    use eval::context_item::ContextItem;
    use eval::eval_context::EvalContext;
    use math::rich_number::{RichNumber, Unit};

    #[test]
    fn math_operators() {
        let mut context = EvalContext::prelude();

        // add 1 (sub (mul 2 (pow 4 3)) (div (mod 7 3) 10))
        // 1 + ((2 * (4 ** 3)) - ((7 % 3) / 10))
        assert_eq!(context.apply(Statement::Expression(vec![
            ExprItem::SingleTerm(Term::Symbol("add".to_owned())),
            ExprItem::SingleTerm(Term::Number(1f64)),
            ExprItem::Group(vec![
                ExprItem::SingleTerm(Term::Symbol("sub".to_owned())),
                ExprItem::Group(vec![
                    ExprItem::SingleTerm(Term::Symbol("mul".to_owned())),
                    ExprItem::SingleTerm(Term::Number(2f64)),
                    ExprItem::Group(vec![
                        ExprItem::SingleTerm(Term::Symbol("pow".to_owned())),
                        ExprItem::SingleTerm(Term::Number(4f64)),
                        ExprItem::SingleTerm(Term::Number(3f64)),
                    ]),
                ]),
                ExprItem::Group(vec![
                    ExprItem::SingleTerm(Term::Symbol("div".to_owned())),
                    ExprItem::Group(vec![
                        ExprItem::SingleTerm(Term::Symbol("mod".to_owned())),
                        ExprItem::SingleTerm(Term::Number(7f64)),
                        ExprItem::SingleTerm(Term::Number(3f64)),
                    ]),
                    ExprItem::SingleTerm(Term::Number(10f64)),
                ]),
            ]),
        ])).unwrap().unwrap_number(), RichNumber::from(128.9f64));
    }

    #[test]
    fn division_with_or_without_units() {
        let mut context = EvalContext::prelude();

        let fake_unit = "foounit";
        assert_eq!(context.call("defineUnit", vec![
            ContextItem::Constant(fake_unit.to_owned()),
        ]).unwrap().unwrap(), ContextItem::Nothing);

        let unit = context.lookup_unit_name(fake_unit).unwrap();

        // 3 foounit / 2 should equal 1.5 foounit
        assert_eq!(context.apply(Statement::Expression(vec![
            ExprItem::SingleTerm(Term::Number(3f64)),
            ExprItem::SingleTerm(Term::Symbol(fake_unit.to_owned())),
            ExprItem::SingleTerm(Term::Symbol("/".to_owned())),
            ExprItem::SingleTerm(Term::Number(2f64)),
        ])).unwrap().unwrap_number(), RichNumber::from_unit(1.5f64, unit));

        // 3 foounit / 2 foounit should equal 1.5
        assert_eq!(context.apply(Statement::Expression(vec![
            ExprItem::SingleTerm(Term::Number(3f64)),
            ExprItem::SingleTerm(Term::Symbol(fake_unit.to_owned())),
            ExprItem::SingleTerm(Term::Symbol("/".to_owned())),
            ExprItem::SingleTerm(Term::Number(2f64)),
            ExprItem::SingleTerm(Term::Symbol(fake_unit.to_owned())),
        ])).unwrap().unwrap_number(), RichNumber::from(1.5f64));
    }

    #[test]
    fn can_compare_booleans_to_booleans() {
        let mut context = EvalContext::prelude();

        // true == false should be false and should not error
        assert_eq!(context.apply(Statement::Expression(vec![
            ExprItem::SingleTerm(Term::Symbol("true".to_owned())),
            ExprItem::SingleTerm(Term::Symbol("==".to_owned())),
            ExprItem::SingleTerm(Term::Symbol("false".to_owned())),
        ])).unwrap().unwrap_boolean(), false);
    }

    #[test]
    fn can_compare_numbers_to_numbers() {
        let mut context = EvalContext::prelude();

        // 2 == 2 should be true and should not error
        assert_eq!(context.apply(Statement::Expression(vec![
            ExprItem::SingleTerm(Term::Number(2f64)),
            ExprItem::SingleTerm(Term::Symbol("==".to_owned())),
            ExprItem::SingleTerm(Term::Number(2f64)),
        ])).unwrap().unwrap_boolean(), true);
    }

    #[test]
    fn can_negate_numbers() {
        let mut context = EvalContext::prelude();

        let fake_unit: Unit = 2;
        let number: f64 = 34.314f64;

        assert_eq!(
            context.call("neg", vec![
                ContextItem::Number(RichNumber::from_unit(number, fake_unit)),
            ]).unwrap().unwrap().unwrap_number(),
            RichNumber::from_unit(-number, fake_unit)
        );
    }

    #[test]
    fn can_negate_booleans() {
        let mut context = EvalContext::prelude();

        assert_eq!(context.call("not", vec![
            ContextItem::Boolean(true),
        ]).unwrap().unwrap().unwrap_boolean(), false);
    }
}

<commit_msg>Fixed bug where boolean binary operator arguments were in the wrong order<commit_after>use std::f64::consts::{E, PI};

use prelude::{define_built_in, apply_program};

use math::rich_number::RichNumber;

use eval::context_item::ContextItem;
use eval::eval_context::{EvalContext, EvalError};
use eval::built_in_function::BuiltInFunction;

const UNARY_PARAMS_LENGTH: usize = 1;
const BINARY_PARAMS_LENGTH: usize = 2;

pub fn define_math(context: &mut EvalContext) {
    context.set_boolean("true", true);
    context.set_boolean("false", false);

    context.set_number("pi", RichNumber::from(PI));
    context.set_number("e", RichNumber::from(E));

    define_binary_operators(context);
    define_unary_operators(context);

    apply_program(context, include_str!("math.lion"));
}

fn define_binary_operators(context: &mut EvalContext) {
    define_numeric_binary_op(context, "pow", |a, b| a.pow(b), |_, _| false);
    define_numeric_binary_op(context, "mul", |a, b| a * b, |_, _| false);

    define_numeric_binary_op(context, "add", |a, b| a + b, |_, _| true);
    define_numeric_binary_op(context, "sub", |a, b| a - b, |_, _| true);
    define_numeric_binary_op(context, "div", |a, b| a / b,
        |a, b| a.has_units() && b.has_units());
    define_numeric_binary_op(context, "mod", |a, b| a % b,
        |a, b| a.has_units() && b.has_units());

    define_boolean_binary_op(context, "eq", |a, b| a == b, true);
    define_boolean_binary_op(context, "ne", |a, b| a != b, true);

    define_boolean_binary_op(context, "gt",
        |a, b| a.unwrap_number() > b.unwrap_number(), true);
    define_boolean_binary_op(context, "ge",
        |a, b| a.unwrap_number() >= b.unwrap_number(), true);
    define_boolean_binary_op(context, "lt",
        |a, b| a.unwrap_number() < b.unwrap_number(), true);
    define_boolean_binary_op(context, "le",
        |a, b| a.unwrap_number() <= b.unwrap_number(), true);
}

fn define_unary_operators(context: &mut EvalContext) {
    define_built_in(context, "not", UNARY_PARAMS_LENGTH,
        BuiltInFunction::new(move |_, mut params| {
            if params.len() != UNARY_PARAMS_LENGTH {
                return Err(EvalError::ExpectedParams(UNARY_PARAMS_LENGTH));
            }

            if !params[0].is_boolean() {
                return Err(EvalError::InvalidParam("Can only boolean negate a boolean value".to_owned()));
            }

            let arg = params.pop().unwrap().unwrap_boolean();
            debug_assert!(params.is_empty(), "Not all parameters used");

            Ok(ContextItem::Boolean(!arg))
        })
    );

    define_numeric_unary_op(context, "neg", |a| -a, |_| true);

    define_numeric_unary_op(context, "abs", |a| a.abs(), |_| true);
    define_numeric_unary_op(context, "floor", |a| a.floor(), |_| true);
    define_numeric_unary_op(context, "ceil", |a| a.ceil(), |_| true);
    define_numeric_unary_op(context, "round", |a| a.round(), |_| true);

    define_numeric_unary_op(context, "sqrt", |a| a.sqrt(), |a| a.is_none());
    define_numeric_unary_op(context, "ln", |a| a.ln(), |a| a.is_none());
    define_numeric_unary_op(context, "log10", |a| a.log10(), |a| a.is_none());
    define_numeric_unary_op(context, "log2", |a| a.log2(), |a| a.is_none());

    define_numeric_unary_op(context, "sin", |a| a.sin(), |a| !a.is_none() && a.unwrap() == "rad");
    define_numeric_unary_op(context, "cos", |a| a.cos(), |a| !a.is_none() && a.unwrap() == "rad");
    define_numeric_unary_op(context, "tan", |a| a.tan(), |a| !a.is_none() && a.unwrap() == "rad");

    //TODO: Find a way to set the unit to radians on the output of these methods
    define_numeric_unary_op(context, "asin", |a| a.asin(), |a| a.is_none());
    define_numeric_unary_op(context, "acos", |a| a.acos(), |a| a.is_none());
    define_numeric_unary_op(context, "atan", |a| a.atan(), |a| a.is_none());

    define_numeric_unary_op(context, "sinh", |a| a.sinh(), |a| !a.is_none() && a.unwrap() == "rad");
    define_numeric_unary_op(context, "cosh", |a| a.cosh(), |a| !a.is_none() && a.unwrap() == "rad");
    define_numeric_unary_op(context, "tanh", |a| a.tanh(), |a| !a.is_none() && a.unwrap() == "rad");

    define_numeric_unary_op(context, "asinh", |a| a.asinh(), |a| a.is_none());
    define_numeric_unary_op(context, "acosh", |a| a.acosh(), |a| a.is_none());
    define_numeric_unary_op(context, "atanh", |a| a.atanh(), |a| a.is_none());
}

/// Defines an operator that can take either booleans or numbers
/// Optionally coerces numbers into the same unit
/// Ensures that both arguments are numbers or both are booleans
fn define_boolean_binary_op<F: 'static>(context: &mut EvalContext, name: &str, operator: F, coerce_first: bool)
    where F: Fn(ContextItem, ContextItem) -> bool {

    define_built_in(context, name, BINARY_PARAMS_LENGTH,
        BuiltInFunction::new(move |context, mut params| {
            if params.len() != BINARY_PARAMS_LENGTH {
                return Err(EvalError::ExpectedParams(BINARY_PARAMS_LENGTH))
            }

            // params can be either number or boolean but not both
            if !((params[0].is_number() && params[1].is_number())
                || (params[0].is_boolean() && params[1].is_boolean())) {
                return Err(EvalError::InvalidParam("Expecting numeric or boolean argument, cannot compare numeric values to boolean values".to_owned()));
            }

            let lhs = params.remove(0);
            let mut rhs = params.remove(0);
            debug_assert!(params.is_empty(), "Not all parameters used");

            if coerce_first && lhs.is_number() && rhs.is_number() {
                // May possibly attempt to convert in the other
                // direction too in the future
                let lhs_unit = match lhs {
                    ContextItem::Number(RichNumber { ref unit, .. }) => unit,
                    _ => unreachable!(),
                };
                rhs = try!(context.convert(rhs.unwrap_number(), *lhs_unit));
            }

            Ok(ContextItem::Boolean(operator(lhs, rhs)))
        })
    );
}

/// Defines an operator that can only take numeric arguments
/// Optionally takes a function used to define whether the numeric arguments should be coerced into the same unit
fn define_numeric_binary_op<F: 'static, G: 'static>(context: &mut EvalContext, name: &str, operator: F, coerce_first: G)
    where F: Fn(RichNumber, RichNumber) -> RichNumber,
          G: Fn(RichNumber, RichNumber) -> bool {

    define_built_in(context, name, BINARY_PARAMS_LENGTH,
        BuiltInFunction::new(move |context, mut params| {
            if params.len() != BINARY_PARAMS_LENGTH {
                return Err(EvalError::ExpectedParams(BINARY_PARAMS_LENGTH))
            }

            if !params[0].is_number() || !params[1].is_number() {
                return Err(EvalError::InvalidParam("Expecting numeric argument".to_owned()));
            }

            let mut rhs = params.pop().unwrap().unwrap_number();
            let lhs = params.pop().unwrap().unwrap_number();
            debug_assert!(params.is_empty(), "Not all parameters used");

            if coerce_first(lhs, rhs) {
                // May possibly attempt to convert in the other
                // direction too in the future
                rhs = try!(context.convert(rhs, lhs.unit)).unwrap_number();
            }

            Ok(ContextItem::Number(operator(lhs, rhs)))
        })
    );
}

/// Defines an unary operator that can only take a numeric argument
fn define_numeric_unary_op<F: 'static, G: 'static>(context: &mut EvalContext, name: &'static str, operator: F, accepts_unit: G)
    where F: Fn(RichNumber) -> RichNumber,
          G: Fn(Option<String>) -> bool {

    define_built_in(context, name, UNARY_PARAMS_LENGTH,
        BuiltInFunction::new(move |context, mut params| {
            if params.len() != UNARY_PARAMS_LENGTH {
                return Err(EvalError::ExpectedParams(UNARY_PARAMS_LENGTH));
            }

            if !params[0].is_number() {
                return Err(EvalError::InvalidParam(format!("Can only apply {} to a numeric value", name)));
            }

            let arg = params.pop().unwrap().unwrap_number();
            debug_assert!(params.is_empty(), "Not all parameters used");

            let unit = arg.unit.map(|u| context.lookup_unit(u).unwrap());
            if !accepts_unit(unit.clone()) {
                return Err(EvalError::InvalidParam(format!("Function {} does not accept unit {}", name, unit.unwrap_or("(no unit)".to_owned()))));
            }

            Ok(ContextItem::Number(operator(arg)))
        })
    );
}

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

    use eval::context_item::ContextItem;
    use eval::eval_context::EvalContext;
    use math::rich_number::{RichNumber, Unit};

    #[test]
    fn math_operators() {
        let mut context = EvalContext::prelude();

        // add 1 (sub (mul 2 (pow 4 3)) (div (mod 7 3) 10))
        // 1 + ((2 * (4 ** 3)) - ((7 % 3) / 10))
        assert_eq!(context.apply(Statement::Expression(vec![
            ExprItem::SingleTerm(Term::Symbol("add".to_owned())),
            ExprItem::SingleTerm(Term::Number(1f64)),
            ExprItem::Group(vec![
                ExprItem::SingleTerm(Term::Symbol("sub".to_owned())),
                ExprItem::Group(vec![
                    ExprItem::SingleTerm(Term::Symbol("mul".to_owned())),
                    ExprItem::SingleTerm(Term::Number(2f64)),
                    ExprItem::Group(vec![
                        ExprItem::SingleTerm(Term::Symbol("pow".to_owned())),
                        ExprItem::SingleTerm(Term::Number(4f64)),
                        ExprItem::SingleTerm(Term::Number(3f64)),
                    ]),
                ]),
                ExprItem::Group(vec![
                    ExprItem::SingleTerm(Term::Symbol("div".to_owned())),
                    ExprItem::Group(vec![
                        ExprItem::SingleTerm(Term::Symbol("mod".to_owned())),
                        ExprItem::SingleTerm(Term::Number(7f64)),
                        ExprItem::SingleTerm(Term::Number(3f64)),
                    ]),
                    ExprItem::SingleTerm(Term::Number(10f64)),
                ]),
            ]),
        ])).unwrap().unwrap_number(), RichNumber::from(128.9f64));
    }

    #[test]
    fn division_with_or_without_units() {
        let mut context = EvalContext::prelude();

        let fake_unit = "foounit";
        assert_eq!(context.call("defineUnit", vec![
            ContextItem::Constant(fake_unit.to_owned()),
        ]).unwrap().unwrap(), ContextItem::Nothing);

        let unit = context.lookup_unit_name(fake_unit).unwrap();

        // 3 foounit / 2 should equal 1.5 foounit
        assert_eq!(context.apply(Statement::Expression(vec![
            ExprItem::SingleTerm(Term::Number(3f64)),
            ExprItem::SingleTerm(Term::Symbol(fake_unit.to_owned())),
            ExprItem::SingleTerm(Term::Symbol("/".to_owned())),
            ExprItem::SingleTerm(Term::Number(2f64)),
        ])).unwrap().unwrap_number(), RichNumber::from_unit(1.5f64, unit));

        // 3 foounit / 2 foounit should equal 1.5
        assert_eq!(context.apply(Statement::Expression(vec![
            ExprItem::SingleTerm(Term::Number(3f64)),
            ExprItem::SingleTerm(Term::Symbol(fake_unit.to_owned())),
            ExprItem::SingleTerm(Term::Symbol("/".to_owned())),
            ExprItem::SingleTerm(Term::Number(2f64)),
            ExprItem::SingleTerm(Term::Symbol(fake_unit.to_owned())),
        ])).unwrap().unwrap_number(), RichNumber::from(1.5f64));
    }

    #[test]
    fn can_compare_booleans_to_booleans() {
        let mut context = EvalContext::prelude();

        // true == false should be false and should not error
        assert_eq!(context.apply(Statement::Expression(vec![
            ExprItem::SingleTerm(Term::Symbol("true".to_owned())),
            ExprItem::SingleTerm(Term::Symbol("==".to_owned())),
            ExprItem::SingleTerm(Term::Symbol("false".to_owned())),
        ])).unwrap().unwrap_boolean(), false);
    }

    #[test]
    fn can_compare_numbers_to_numbers() {
        let mut context = EvalContext::prelude();

        // 2 == 2 should be true and should not error
        assert_eq!(context.apply(Statement::Expression(vec![
            ExprItem::SingleTerm(Term::Number(2f64)),
            ExprItem::SingleTerm(Term::Symbol("==".to_owned())),
            ExprItem::SingleTerm(Term::Number(2f64)),
        ])).unwrap().unwrap_boolean(), true);
    }

    #[test]
    fn can_negate_numbers() {
        let mut context = EvalContext::prelude();

        let fake_unit: Unit = 2;
        let number: f64 = 34.314f64;

        assert_eq!(
            context.call("neg", vec![
                ContextItem::Number(RichNumber::from_unit(number, fake_unit)),
            ]).unwrap().unwrap().unwrap_number(),
            RichNumber::from_unit(-number, fake_unit)
        );
    }

    #[test]
    fn can_negate_booleans() {
        let mut context = EvalContext::prelude();

        assert_eq!(context.call("not", vec![
            ContextItem::Boolean(true),
        ]).unwrap().unwrap().unwrap_boolean(), false);
    }
}

<|endoftext|>


<commit_before>use crate::{BeaconChain, BeaconChainTypes};
pub use lighthouse_metrics::*;
use types::{BeaconState, Epoch, Hash256, Slot};

lazy_static! {
    /*
     * Block Processing
     */
    pub static ref BLOCK_PROCESSING_REQUESTS: Result<IntCounter> = try_create_int_counter(
        "beacon_block_processing_requests_total",
        "Count of blocks submitted for processing"
    );
    pub static ref BLOCK_PROCESSING_SUCCESSES: Result<IntCounter> = try_create_int_counter(
        "beacon_block_processing_successes_total",
        "Count of blocks processed without error"
    );
    pub static ref BLOCK_PROCESSING_TIMES: Result<Histogram> =
        try_create_histogram("block_processing_seconds", "Full runtime of block processing");
    pub static ref BLOCK_PROCESSING_DB_READ: Result<Histogram> = try_create_histogram(
        "beacon_block_processing_db_read_seconds",
        "Time spent loading block and state from DB for block processing"
    );
    pub static ref BLOCK_PROCESSING_CATCHUP_STATE: Result<Histogram> = try_create_histogram(
        "beacon_block_processing_catch_up_state_seconds",
        "Time spent skipping slots on a state before processing a block."
    );
    pub static ref BLOCK_PROCESSING_COMMITTEE: Result<Histogram> = try_create_histogram(
        "beacon_block_processing_committee_building_seconds",
        "Time spent building/obtaining committees for block processing."
    );
    pub static ref BLOCK_PROCESSING_CORE: Result<Histogram> = try_create_histogram(
        "beacon_block_processing_core_seconds",
        "Time spent doing the core per_block_processing state processing."
    );
    pub static ref BLOCK_PROCESSING_STATE_ROOT: Result<Histogram> = try_create_histogram(
        "beacon_block_processing_state_root_seconds",
        "Time spent calculating the state root when processing a block."
    );
    pub static ref BLOCK_PROCESSING_DB_WRITE: Result<Histogram> = try_create_histogram(
        "beacon_block_processing_db_write_seconds",
        "Time spent writing a newly processed block and state to DB"
    );
    pub static ref BLOCK_PROCESSING_FORK_CHOICE_REGISTER: Result<Histogram> = try_create_histogram(
        "beacon_block_processing_fork_choice_register_seconds",
        "Time spent registering the new block with fork choice (but not finding head)"
    );
    pub static ref BLOCK_PROCESSING_FORK_CHOICE_FIND_HEAD: Result<Histogram> = try_create_histogram(
        "beacon_block_processing_fork_choice_find_head_seconds",
        "Time spent finding the new head after processing a new block"
    );

    /*
     * Block Production
     */
    pub static ref BLOCK_PRODUCTION_REQUESTS: Result<IntCounter> = try_create_int_counter(
        "beacon_block_production_requests_total",
        "Count of all block production requests"
    );
    pub static ref BLOCK_PRODUCTION_SUCCESSES: Result<IntCounter> = try_create_int_counter(
        "beacon_block_production_successes_total",
        "Count of blocks successfully produced."
    );
    pub static ref BLOCK_PRODUCTION_TIMES: Result<Histogram> =
        try_create_histogram("beacon_block_production_seconds", "Full runtime of block production");

    /*
     * Block Statistics
     */
    pub static ref OPERATIONS_PER_BLOCK_ATTESTATION: Result<Histogram> = try_create_histogram(
        "beacon_operations_per_block_attestation_total",
        "Number of attestations in a block"
    );

    /*
     * Attestation Processing
     */
    pub static ref ATTESTATION_PROCESSING_REQUESTS: Result<IntCounter> = try_create_int_counter(
        "beacon_attestation_processing_requests_total",
        "Count of all attestations submitted for processing"
    );
    pub static ref ATTESTATION_PROCESSING_SUCCESSES: Result<IntCounter> = try_create_int_counter(
        "beacon_attestation_processing_successes_total",
        "total_attestation_processing_successes"
    );
    pub static ref ATTESTATION_PROCESSING_TIMES: Result<Histogram> = try_create_histogram(
        "beacon_attestation_processing_seconds",
        "Full runtime of attestation processing"
    );

    /*
     * Attestation Production
     */
    pub static ref ATTESTATION_PRODUCTION_REQUESTS: Result<IntCounter> = try_create_int_counter(
        "beacon_attestation_production_requests_total",
        "Count of all attestation production requests"
    );
    pub static ref ATTESTATION_PRODUCTION_SUCCESSES: Result<IntCounter> = try_create_int_counter(
        "beacon_attestation_production_successes_total",
        "Count of attestations processed without error"
    );
    pub static ref ATTESTATION_PRODUCTION_TIMES: Result<Histogram> = try_create_histogram(
        "beacon_attestation_production_seconds",
        "Full runtime of attestation production"
    );

    /*
     * Fork Choice
     */
    pub static ref FORK_CHOICE_REQUESTS: Result<IntCounter> = try_create_int_counter(
        "beacon_fork_choice_requests_total",
        "Count of occasions where fork choice has tried to find a head"
    );
    pub static ref FORK_CHOICE_ERRORS: Result<IntCounter> = try_create_int_counter(
        "beacon_fork_choice_errors_total",
        "Count of occasions where fork choice has returned an error when trying to find a head"
    );
    pub static ref FORK_CHOICE_CHANGED_HEAD: Result<IntCounter> = try_create_int_counter(
        "beacon_fork_choice_changed_head_total",
        "Count of occasions fork choice has found a new head"
    );
    pub static ref FORK_CHOICE_REORG_COUNT: Result<IntCounter> = try_create_int_counter(
        "beacon_fork_choice_reorg_total",
        "Count of occasions fork choice has switched to a different chain"
    );
    pub static ref FORK_CHOICE_TIMES: Result<Histogram> =
        try_create_histogram("beacon_fork_choice_time", "Full runtime of fork choice");
    pub static ref FORK_CHOICE_FIND_HEAD_TIMES: Result<Histogram> =
        try_create_histogram("beacon_fork_choice_find_head_time", "Full runtime of fork choice find_head function");
    pub static ref FORK_CHOICE_PROCESS_BLOCK_TIMES: Result<Histogram> = try_create_histogram(
        "beacon_fork_choice_process_block_time",
        "Time taken to add a block and all attestations to fork choice"
    );
    pub static ref FORK_CHOICE_PROCESS_ATTESTATION_TIMES: Result<Histogram> = try_create_histogram(
        "beacon_fork_choice_process_attestation_time",
        "Time taken to add an attestation to fork choice"
    );

    /*
     * Persisting BeaconChain to disk
     */
    pub static ref PERSIST_CHAIN: Result<Histogram> =
        try_create_histogram("beacon_persist_chain", "Time taken to update the canonical head");

    /*
     * Chain Head
     */
    pub static ref UPDATE_HEAD_TIMES: Result<Histogram> =
        try_create_histogram("beacon_update_head_seconds", "Time taken to update the canonical head");
    pub static ref HEAD_STATE_SLOT: Result<IntGauge> =
        try_create_int_gauge("beacon_head_state_slot", "Slot of the block at the head of the chain");
    pub static ref HEAD_STATE_ROOT: Result<IntGauge> =
        try_create_int_gauge("beacon_head_state_root", "Root of the block at the head of the chain");
    pub static ref HEAD_STATE_LATEST_BLOCK_SLOT: Result<IntGauge> =
        try_create_int_gauge("beacon_head_state_latest_block_slot", "Latest block slot at the head of the chain");
    pub static ref HEAD_STATE_CURRENT_JUSTIFIED_ROOT: Result<IntGauge> =
        try_create_int_gauge("beacon_head_state_current_justified_root", "Current justified root at the head of the chain");
    pub static ref HEAD_STATE_CURRENT_JUSTIFIED_EPOCH: Result<IntGauge> =
        try_create_int_gauge("beacon_head_state_current_justified_epoch", "Current justified epoch at the head of the chain");
    pub static ref HEAD_STATE_PREVIOUS_JUSTIFIED_ROOT: Result<IntGauge> =
        try_create_int_gauge("beacon_head_state_previous_justified_root", "Previous justified root at the head of the chain");
    pub static ref HEAD_STATE_PREVIOUS_JUSTIFIED_EPOCH: Result<IntGauge> =
        try_create_int_gauge("beacon_head_state_previous_justified_epoch", "Previous justified epoch at the head of the chain");
    pub static ref HEAD_STATE_FINALIZED_ROOT: Result<IntGauge> =
        try_create_int_gauge("beacon_head_state_finalized_root", "Finalized root at the head of the chain");
    pub static ref HEAD_STATE_FINALIZED_EPOCH: Result<IntGauge> =
        try_create_int_gauge("beacon_head_state_finalized_epoch", "Finalized epoch at the head of the chain");
    pub static ref HEAD_STATE_SHARDS: Result<IntGauge> =
        try_create_int_gauge("beacon_head_state_shard_total", "Count of shards in the beacon chain");
    pub static ref HEAD_STATE_TOTAL_VALIDATORS: Result<IntGauge> =
        try_create_int_gauge("beacon_head_state_total_validators_total", "Count of validators at the head of the chain");
    pub static ref HEAD_STATE_ACTIVE_VALIDATORS: Result<IntGauge> =
        try_create_int_gauge("beacon_head_state_active_validators_total", "Count of active validators at the head of the chain");
    pub static ref HEAD_STATE_VALIDATOR_BALANCES: Result<IntGauge> =
        try_create_int_gauge("beacon_head_state_validator_balances_total", "Sum of all validator balances at the head of the chain");
    pub static ref HEAD_STATE_SLASHED_VALIDATORS: Result<IntGauge> =
        try_create_int_gauge("beacon_head_state_slashed_validators_total", "Count of all slashed validators at the head of the chain");
    pub static ref HEAD_STATE_WITHDRAWN_VALIDATORS: Result<IntGauge> =
        try_create_int_gauge("beacon_head_state_withdrawn_validators_total", "Sum of all validator balances at the head of the chain");
    pub static ref HEAD_STATE_ETH1_DEPOSIT_INDEX: Result<IntGauge> =
        try_create_int_gauge("beacon_head_state_eth1_deposit_index", "Eth1 deposit index at the head of the chain");
}

/// Scrape the `beacon_chain` for metrics that are not constantly updated (e.g., the present slot,
/// head state info, etc) and update the Prometheus `DEFAULT_REGISTRY`.
pub fn scrape_for_metrics<T: BeaconChainTypes>(beacon_chain: &BeaconChain<T>) {
    scrape_head_state::<T>(
        &beacon_chain.head().beacon_state,
        beacon_chain.head().beacon_state_root,
    );
}

/// Scrape the given `state` assuming it's the head state, updating the `DEFAULT_REGISTRY`.
fn scrape_head_state<T: BeaconChainTypes>(state: &BeaconState<T::EthSpec>, state_root: Hash256) {
    set_gauge_by_slot(&HEAD_STATE_SLOT, state.slot);
    set_gauge_by_hash(&HEAD_STATE_ROOT, state_root);
    set_gauge_by_slot(
        &HEAD_STATE_LATEST_BLOCK_SLOT,
        state.latest_block_header.slot,
    );
    set_gauge_by_hash(
        &HEAD_STATE_CURRENT_JUSTIFIED_ROOT,
        state.current_justified_checkpoint.root,
    );
    set_gauge_by_epoch(
        &HEAD_STATE_CURRENT_JUSTIFIED_EPOCH,
        state.current_justified_checkpoint.epoch,
    );
    set_gauge_by_hash(
        &HEAD_STATE_PREVIOUS_JUSTIFIED_ROOT,
        state.previous_justified_checkpoint.root,
    );
    set_gauge_by_epoch(
        &HEAD_STATE_PREVIOUS_JUSTIFIED_EPOCH,
        state.previous_justified_checkpoint.epoch,
    );
    set_gauge_by_hash(&HEAD_STATE_FINALIZED_ROOT, state.finalized_checkpoint.root);
    set_gauge_by_epoch(
        &HEAD_STATE_FINALIZED_EPOCH,
        state.finalized_checkpoint.epoch,
    );
    set_gauge_by_usize(&HEAD_STATE_SHARDS, state.previous_crosslinks.len());
    set_gauge_by_usize(&HEAD_STATE_TOTAL_VALIDATORS, state.validators.len());
    set_gauge_by_u64(
        &HEAD_STATE_VALIDATOR_BALANCES,
        state.balances.iter().fold(0_u64, |acc, i| acc + i),
    );
    set_gauge_by_usize(
        &HEAD_STATE_ACTIVE_VALIDATORS,
        state
            .validators
            .iter()
            .filter(|v| v.is_active_at(state.current_epoch()))
            .count(),
    );
    set_gauge_by_usize(
        &HEAD_STATE_SLASHED_VALIDATORS,
        state.validators.iter().filter(|v| v.slashed).count(),
    );
    set_gauge_by_usize(
        &HEAD_STATE_WITHDRAWN_VALIDATORS,
        state
            .validators
            .iter()
            .filter(|v| v.is_withdrawable_at(state.current_epoch()))
            .count(),
    );
    set_gauge_by_u64(&HEAD_STATE_ETH1_DEPOSIT_INDEX, state.eth1_deposit_index);
}

fn set_gauge_by_slot(gauge: &Result<IntGauge>, value: Slot) {
    set_gauge(gauge, value.as_u64() as i64);
}

fn set_gauge_by_epoch(gauge: &Result<IntGauge>, value: Epoch) {
    set_gauge(gauge, value.as_u64() as i64);
}

fn set_gauge_by_hash(gauge: &Result<IntGauge>, value: Hash256) {
    set_gauge(gauge, value.to_low_u64_le() as i64);
}

fn set_gauge_by_usize(gauge: &Result<IntGauge>, value: usize) {
    set_gauge(gauge, value as i64);
}

fn set_gauge_by_u64(gauge: &Result<IntGauge>, value: u64) {
    set_gauge(gauge, value as i64);
}
<commit_msg>Make prom metric names more consistent<commit_after>use crate::{BeaconChain, BeaconChainTypes};
pub use lighthouse_metrics::*;
use types::{BeaconState, Epoch, Hash256, Slot};

lazy_static! {
    /*
     * Block Processing
     */
    pub static ref BLOCK_PROCESSING_REQUESTS: Result<IntCounter> = try_create_int_counter(
        "beacon_block_processing_requests_total",
        "Count of blocks submitted for processing"
    );
    pub static ref BLOCK_PROCESSING_SUCCESSES: Result<IntCounter> = try_create_int_counter(
        "beacon_block_processing_successes_total",
        "Count of blocks processed without error"
    );
    pub static ref BLOCK_PROCESSING_TIMES: Result<Histogram> =
        try_create_histogram("beacon_block_processing_seconds", "Full runtime of block processing");
    pub static ref BLOCK_PROCESSING_DB_READ: Result<Histogram> = try_create_histogram(
        "beacon_block_processing_db_read_seconds",
        "Time spent loading block and state from DB for block processing"
    );
    pub static ref BLOCK_PROCESSING_CATCHUP_STATE: Result<Histogram> = try_create_histogram(
        "beacon_block_processing_catch_up_state_seconds",
        "Time spent skipping slots on a state before processing a block."
    );
    pub static ref BLOCK_PROCESSING_COMMITTEE: Result<Histogram> = try_create_histogram(
        "beacon_block_processing_committee_building_seconds",
        "Time spent building/obtaining committees for block processing."
    );
    pub static ref BLOCK_PROCESSING_CORE: Result<Histogram> = try_create_histogram(
        "beacon_block_processing_core_seconds",
        "Time spent doing the core per_block_processing state processing."
    );
    pub static ref BLOCK_PROCESSING_STATE_ROOT: Result<Histogram> = try_create_histogram(
        "beacon_block_processing_state_root_seconds",
        "Time spent calculating the state root when processing a block."
    );
    pub static ref BLOCK_PROCESSING_DB_WRITE: Result<Histogram> = try_create_histogram(
        "beacon_block_processing_db_write_seconds",
        "Time spent writing a newly processed block and state to DB"
    );
    pub static ref BLOCK_PROCESSING_FORK_CHOICE_REGISTER: Result<Histogram> = try_create_histogram(
        "beacon_block_processing_fork_choice_register_seconds",
        "Time spent registering the new block with fork choice (but not finding head)"
    );
    pub static ref BLOCK_PROCESSING_FORK_CHOICE_FIND_HEAD: Result<Histogram> = try_create_histogram(
        "beacon_block_processing_fork_choice_find_head_seconds",
        "Time spent finding the new head after processing a new block"
    );

    /*
     * Block Production
     */
    pub static ref BLOCK_PRODUCTION_REQUESTS: Result<IntCounter> = try_create_int_counter(
        "beacon_block_production_requests_total",
        "Count of all block production requests"
    );
    pub static ref BLOCK_PRODUCTION_SUCCESSES: Result<IntCounter> = try_create_int_counter(
        "beacon_block_production_successes_total",
        "Count of blocks successfully produced."
    );
    pub static ref BLOCK_PRODUCTION_TIMES: Result<Histogram> =
        try_create_histogram("beacon_block_production_seconds", "Full runtime of block production");

    /*
     * Block Statistics
     */
    pub static ref OPERATIONS_PER_BLOCK_ATTESTATION: Result<Histogram> = try_create_histogram(
        "beacon_operations_per_block_attestation_total",
        "Number of attestations in a block"
    );

    /*
     * Attestation Processing
     */
    pub static ref ATTESTATION_PROCESSING_REQUESTS: Result<IntCounter> = try_create_int_counter(
        "beacon_attestation_processing_requests_total",
        "Count of all attestations submitted for processing"
    );
    pub static ref ATTESTATION_PROCESSING_SUCCESSES: Result<IntCounter> = try_create_int_counter(
        "beacon_attestation_processing_successes_total",
        "total_attestation_processing_successes"
    );
    pub static ref ATTESTATION_PROCESSING_TIMES: Result<Histogram> = try_create_histogram(
        "beacon_attestation_processing_seconds",
        "Full runtime of attestation processing"
    );

    /*
     * Attestation Production
     */
    pub static ref ATTESTATION_PRODUCTION_REQUESTS: Result<IntCounter> = try_create_int_counter(
        "beacon_attestation_production_requests_total",
        "Count of all attestation production requests"
    );
    pub static ref ATTESTATION_PRODUCTION_SUCCESSES: Result<IntCounter> = try_create_int_counter(
        "beacon_attestation_production_successes_total",
        "Count of attestations processed without error"
    );
    pub static ref ATTESTATION_PRODUCTION_TIMES: Result<Histogram> = try_create_histogram(
        "beacon_attestation_production_seconds",
        "Full runtime of attestation production"
    );

    /*
     * Fork Choice
     */
    pub static ref FORK_CHOICE_REQUESTS: Result<IntCounter> = try_create_int_counter(
        "beacon_fork_choice_requests_total",
        "Count of occasions where fork choice has tried to find a head"
    );
    pub static ref FORK_CHOICE_ERRORS: Result<IntCounter> = try_create_int_counter(
        "beacon_fork_choice_errors_total",
        "Count of occasions where fork choice has returned an error when trying to find a head"
    );
    pub static ref FORK_CHOICE_CHANGED_HEAD: Result<IntCounter> = try_create_int_counter(
        "beacon_fork_choice_changed_head_total",
        "Count of occasions fork choice has found a new head"
    );
    pub static ref FORK_CHOICE_REORG_COUNT: Result<IntCounter> = try_create_int_counter(
        "beacon_fork_choice_reorg_total",
        "Count of occasions fork choice has switched to a different chain"
    );
    pub static ref FORK_CHOICE_TIMES: Result<Histogram> =
        try_create_histogram("beacon_fork_choice_seconds", "Full runtime of fork choice");
    pub static ref FORK_CHOICE_FIND_HEAD_TIMES: Result<Histogram> =
        try_create_histogram("beacon_fork_choice_find_head_seconds", "Full runtime of fork choice find_head function");
    pub static ref FORK_CHOICE_PROCESS_BLOCK_TIMES: Result<Histogram> = try_create_histogram(
        "beacon_fork_choice_process_block_seconds",
        "Time taken to add a block and all attestations to fork choice"
    );
    pub static ref FORK_CHOICE_PROCESS_ATTESTATION_TIMES: Result<Histogram> = try_create_histogram(
        "beacon_fork_choice_process_attestation_seconds",
        "Time taken to add an attestation to fork choice"
    );

    /*
     * Persisting BeaconChain to disk
     */
    pub static ref PERSIST_CHAIN: Result<Histogram> =
        try_create_histogram("beacon_persist_chain", "Time taken to update the canonical head");

    /*
     * Chain Head
     */
    pub static ref UPDATE_HEAD_TIMES: Result<Histogram> =
        try_create_histogram("beacon_update_head_seconds", "Time taken to update the canonical head");
    pub static ref HEAD_STATE_SLOT: Result<IntGauge> =
        try_create_int_gauge("beacon_head_state_slot", "Slot of the block at the head of the chain");
    pub static ref HEAD_STATE_ROOT: Result<IntGauge> =
        try_create_int_gauge("beacon_head_state_root", "Root of the block at the head of the chain");
    pub static ref HEAD_STATE_LATEST_BLOCK_SLOT: Result<IntGauge> =
        try_create_int_gauge("beacon_head_state_latest_block_slot", "Latest block slot at the head of the chain");
    pub static ref HEAD_STATE_CURRENT_JUSTIFIED_ROOT: Result<IntGauge> =
        try_create_int_gauge("beacon_head_state_current_justified_root", "Current justified root at the head of the chain");
    pub static ref HEAD_STATE_CURRENT_JUSTIFIED_EPOCH: Result<IntGauge> =
        try_create_int_gauge("beacon_head_state_current_justified_epoch", "Current justified epoch at the head of the chain");
    pub static ref HEAD_STATE_PREVIOUS_JUSTIFIED_ROOT: Result<IntGauge> =
        try_create_int_gauge("beacon_head_state_previous_justified_root", "Previous justified root at the head of the chain");
    pub static ref HEAD_STATE_PREVIOUS_JUSTIFIED_EPOCH: Result<IntGauge> =
        try_create_int_gauge("beacon_head_state_previous_justified_epoch", "Previous justified epoch at the head of the chain");
    pub static ref HEAD_STATE_FINALIZED_ROOT: Result<IntGauge> =
        try_create_int_gauge("beacon_head_state_finalized_root", "Finalized root at the head of the chain");
    pub static ref HEAD_STATE_FINALIZED_EPOCH: Result<IntGauge> =
        try_create_int_gauge("beacon_head_state_finalized_epoch", "Finalized epoch at the head of the chain");
    pub static ref HEAD_STATE_SHARDS: Result<IntGauge> =
        try_create_int_gauge("beacon_head_state_shard_total", "Count of shards in the beacon chain");
    pub static ref HEAD_STATE_TOTAL_VALIDATORS: Result<IntGauge> =
        try_create_int_gauge("beacon_head_state_total_validators_total", "Count of validators at the head of the chain");
    pub static ref HEAD_STATE_ACTIVE_VALIDATORS: Result<IntGauge> =
        try_create_int_gauge("beacon_head_state_active_validators_total", "Count of active validators at the head of the chain");
    pub static ref HEAD_STATE_VALIDATOR_BALANCES: Result<IntGauge> =
        try_create_int_gauge("beacon_head_state_validator_balances_total", "Sum of all validator balances at the head of the chain");
    pub static ref HEAD_STATE_SLASHED_VALIDATORS: Result<IntGauge> =
        try_create_int_gauge("beacon_head_state_slashed_validators_total", "Count of all slashed validators at the head of the chain");
    pub static ref HEAD_STATE_WITHDRAWN_VALIDATORS: Result<IntGauge> =
        try_create_int_gauge("beacon_head_state_withdrawn_validators_total", "Sum of all validator balances at the head of the chain");
    pub static ref HEAD_STATE_ETH1_DEPOSIT_INDEX: Result<IntGauge> =
        try_create_int_gauge("beacon_head_state_eth1_deposit_index", "Eth1 deposit index at the head of the chain");
}

/// Scrape the `beacon_chain` for metrics that are not constantly updated (e.g., the present slot,
/// head state info, etc) and update the Prometheus `DEFAULT_REGISTRY`.
pub fn scrape_for_metrics<T: BeaconChainTypes>(beacon_chain: &BeaconChain<T>) {
    scrape_head_state::<T>(
        &beacon_chain.head().beacon_state,
        beacon_chain.head().beacon_state_root,
    );
}

/// Scrape the given `state` assuming it's the head state, updating the `DEFAULT_REGISTRY`.
fn scrape_head_state<T: BeaconChainTypes>(state: &BeaconState<T::EthSpec>, state_root: Hash256) {
    set_gauge_by_slot(&HEAD_STATE_SLOT, state.slot);
    set_gauge_by_hash(&HEAD_STATE_ROOT, state_root);
    set_gauge_by_slot(
        &HEAD_STATE_LATEST_BLOCK_SLOT,
        state.latest_block_header.slot,
    );
    set_gauge_by_hash(
        &HEAD_STATE_CURRENT_JUSTIFIED_ROOT,
        state.current_justified_checkpoint.root,
    );
    set_gauge_by_epoch(
        &HEAD_STATE_CURRENT_JUSTIFIED_EPOCH,
        state.current_justified_checkpoint.epoch,
    );
    set_gauge_by_hash(
        &HEAD_STATE_PREVIOUS_JUSTIFIED_ROOT,
        state.previous_justified_checkpoint.root,
    );
    set_gauge_by_epoch(
        &HEAD_STATE_PREVIOUS_JUSTIFIED_EPOCH,
        state.previous_justified_checkpoint.epoch,
    );
    set_gauge_by_hash(&HEAD_STATE_FINALIZED_ROOT, state.finalized_checkpoint.root);
    set_gauge_by_epoch(
        &HEAD_STATE_FINALIZED_EPOCH,
        state.finalized_checkpoint.epoch,
    );
    set_gauge_by_usize(&HEAD_STATE_SHARDS, state.previous_crosslinks.len());
    set_gauge_by_usize(&HEAD_STATE_TOTAL_VALIDATORS, state.validators.len());
    set_gauge_by_u64(
        &HEAD_STATE_VALIDATOR_BALANCES,
        state.balances.iter().fold(0_u64, |acc, i| acc + i),
    );
    set_gauge_by_usize(
        &HEAD_STATE_ACTIVE_VALIDATORS,
        state
            .validators
            .iter()
            .filter(|v| v.is_active_at(state.current_epoch()))
            .count(),
    );
    set_gauge_by_usize(
        &HEAD_STATE_SLASHED_VALIDATORS,
        state.validators.iter().filter(|v| v.slashed).count(),
    );
    set_gauge_by_usize(
        &HEAD_STATE_WITHDRAWN_VALIDATORS,
        state
            .validators
            .iter()
            .filter(|v| v.is_withdrawable_at(state.current_epoch()))
            .count(),
    );
    set_gauge_by_u64(&HEAD_STATE_ETH1_DEPOSIT_INDEX, state.eth1_deposit_index);
}

fn set_gauge_by_slot(gauge: &Result<IntGauge>, value: Slot) {
    set_gauge(gauge, value.as_u64() as i64);
}

fn set_gauge_by_epoch(gauge: &Result<IntGauge>, value: Epoch) {
    set_gauge(gauge, value.as_u64() as i64);
}

fn set_gauge_by_hash(gauge: &Result<IntGauge>, value: Hash256) {
    set_gauge(gauge, value.to_low_u64_le() as i64);
}

fn set_gauge_by_usize(gauge: &Result<IntGauge>, value: usize) {
    set_gauge(gauge, value as i64);
}

fn set_gauge_by_u64(gauge: &Result<IntGauge>, value: u64) {
    set_gauge(gauge, value as i64);
}
<|endoftext|>


<commit_before>
use std::iter::range_step;
use collections::hashmap::HashSet;
use rand::random;
use graphics::*;
use piston::*;
use settings;
use tile::{
    Tile,
    TileStatic,
};

pub struct Board {
    tiles: Vec<Tile>,
}

impl Board {
    pub fn new() -> Board {
        Board {
            tiles: Vec::<Tile>::new(),
        }
    }

    pub fn generate_tile(&mut self) {
        if self.tiles.len() == (settings::TILE_WIDTH * settings::TILE_HEIGHT) as uint {
            return;
        }

        loop {
            let x = (random::<uint>() % settings::TILE_WIDTH as uint) as int;
            let y = (random::<uint>() % settings::TILE_HEIGHT as uint) as int;
            if self.get_tile(x, y).is_none() {
                self.tiles.push(Tile::new(2, x, y));
                break;
            }
        }
    }

    pub fn update(&mut self, dt: f64) {
        for tile in self.tiles.mut_iter() {
            tile.update(dt);
        }

        if self.is_locking() {
            return;
        }
        let mut tiles_need_removed = HashSet::<uint>::new();
        let mut tiles_need_added = Vec::<Tile>::new();
        for i in range(0, self.tiles.len()) {
            let tile1 = self.tiles.get(i);
            if tile1.status != TileStatic {
                continue;
            }
            for j in range(i + 1, self.tiles.len()) {
                let tile2 = self.tiles.get(j);
                if tile2.status != TileStatic
                   || tile1.tile_x != tile2.tile_x
                   || tile1.tile_y != tile2.tile_y {
                    continue;
                }

                tiles_need_removed.insert(i);
                tiles_need_removed.insert(j);
                tiles_need_added.push(Tile::new_combined(tile1.score + tile2.score, tile1.tile_x, tile1.tile_y));
                break;
            }
        }

        if tiles_need_removed.len() > 0 {
            let mut tiles = Vec::<Tile>::new();
            for i in range(0, self.tiles.len()) {
                if !tiles_need_removed.contains(&i) {
                    tiles.push(*self.tiles.get(i));
                }
            }
            self.tiles = tiles.append(tiles_need_added.as_slice());
        }
    }

    pub fn render(&self, c: &Context, gl: &mut Gl) {
        self.render_board(c, gl);
        self.render_tiles(c, gl);
    }

    pub fn can_merge(&self) -> bool {
        for row in range(0, settings::TILE_HEIGHT) {
            if !self.can_merge_row(row) {
                return false;
            }
        }
        true
    }

    pub fn merge_from_bottom_to_top(&mut self) {
        self.merge_col(0, settings::TILE_HEIGHT, 1);
    }

    pub fn merge_from_top_to_bottom(&mut self) {
        self.merge_col(settings::TILE_HEIGHT - 1, -1, -1);
    }

    fn merge_col(&mut self, y_start: int, y_end: int, y_step: int) {
        if self.is_locking() {
            return;
        }

        let mut need_generate = false;
        loop {
            // move all tiles to right place
            for col in range(0, settings::TILE_WIDTH) {
                for row in range_step(y_start, y_end, y_step) {
                    match self.get_mut_tile(col, row) {
                        None => {
                            match self.get_mut_next_tile(col, row, 0, y_step) {
                                Some(ref mut tile) => {
                                    println!("move ({}, {}) to ({}, {})",
                                             tile.tile_x, tile.tile_y, col, row);
                                    tile.start_moving(col, row);
                                },
                                _ => {},
                            }
                        },
                        _ => {},
                    }
                }
            }

            let mut did_merged = false;
            for col in range(0, settings::TILE_WIDTH) {
                let mut found = false;
                let mut sx = 0;
                let mut sy = 0;
                let mut dx = 0;
                let mut dy = 0;
                for row in range_step(y_start, y_end, y_step) {
                    match self.get_tile(col, row) {
                        Some(ref d_tile) => {
                            match self.get_next_tile(col, row, 0, y_step) {
                                Some(ref s_tile) if d_tile.score == s_tile.score => {
                                    found = true;
                                    dx = d_tile.tile_x;
                                    dy = d_tile.tile_y;
                                    sx = s_tile.tile_x;
                                    sy = s_tile.tile_y;
                                    break;
                                },
                                _ => {},
                            }
                        },
                        None => {
                            break;
                        }
                    }
                }
                if found {
                    need_generate = true;
                    did_merged = true;
                    let mut tile = self.get_mut_tile(sx, sy);
                    let tile = tile.get_mut_ref();
                    tile.start_moving(dx, dy);
                }
            }

            if !did_merged {
                break;
            }
        }
        if need_generate {
            self.generate_tile();
        }
    }

    pub fn merge_from_left_to_right(&mut self) {
        self.merge_row(settings::TILE_WIDTH - 1, -1, -1);
    }

    pub fn merge_from_right_to_left(&mut self) {
        self.merge_row(0, settings::TILE_WIDTH, 1);
    }

    fn merge_row(&mut self, x_start: int, x_end: int, x_step: int) {
        if self.is_locking() {
            return;
        }

        let mut need_generate = false;
        loop {
            // move all tiles to right place
            for row in range(0, settings::TILE_HEIGHT) {
                for col in range_step(x_start, x_end, x_step) {
                    match self.get_mut_tile(col, row) {
                        None => {
                            match self.get_mut_next_tile(col, row, x_step, 0) {
                                Some(ref mut tile) => {
                                    println!("move ({}, {}) to ({}, {})", tile.tile_x, tile.tile_y, col, row);
                                    need_generate = true;
                                    tile.start_moving(col, row);
                                },
                                _ => {},
                            }
                        },
                        _ => {},
                    }
                }
            }

            // merge
            let mut did_merged = false;
            for row in range(0, settings::TILE_HEIGHT) {
                let mut found = false;
                let mut sx = 0;
                let mut sy = 0;
                let mut dx = 0;
                let mut dy = 0;
                for col in range_step(x_start, x_end, x_step) {
                    match self.get_tile(col, row) {
                        Some(ref d_tile) => {
                            match self.get_next_tile(col, row, x_step, 0) {
                                Some(ref s_tile) if d_tile.score == s_tile.score => {
                                    found = true;
                                    dx = d_tile.tile_x;
                                    dy = d_tile.tile_y;
                                    sx = s_tile.tile_x;
                                    sy = s_tile.tile_y;
                                    break;
                                },
                                _ => {},
                            }
                        },
                        None => {
                            break;
                        }
                    }
                }
                if found {
                    need_generate = true;
                    did_merged = true;
                    let mut tile = self.get_mut_tile(sx, sy);
                    let tile = tile.get_mut_ref();
                    tile.start_moving(dx, dy);
                }
            }

            if !did_merged {
                break;
            }
        }

        if need_generate {
            self.generate_tile();
        }
    }

    fn can_merge_row(&self, row: int) -> bool {
        for col in range(0, settings::TILE_WIDTH) {
            match self.get_tile(col, row) {
                Some(ref tile) => {
                    match self.get_next_tile(tile.tile_x, tile.tile_y, 1, 0) {
                        Some(ref s_tile) => {
                            if tile.score == s_tile.score {
                                return true;
                            }
                        },
                        _ => {},
                    }
                },
                None => {},
            }
        }
        false
    }

    fn is_locking(&self) -> bool {
        for tile in self.tiles.iter() {
            if tile.status != TileStatic {
                return true;
            }
        }
        false
    }

    /// Returns next tile right besides (x, y)
    fn get_next_tile<'a>(&'a self, x: int, y: int, step_x: int, step_y: int) -> Option<&'a Tile> {
        let mut x = x + step_x;
        let mut y = y + step_y;
        while x >= 0 && x < settings::TILE_WIDTH
              && y >= 0 && y < settings::TILE_HEIGHT {
            let tile = self.get_tile(x, y);
            if tile.is_some() {
                return tile;
            }
            x += step_x;
            y += step_y;
        }
        None
    }

    fn get_mut_next_tile<'a>(&'a mut self, x: int, y: int, step_x: int, step_y: int) -> Option<&'a mut Tile> {
        let mut x = x + step_x;
        let mut y = y + step_y;
        let mut found = false;
        while x >= 0 && x < settings::TILE_WIDTH
              && y >= 0 && y < settings::TILE_HEIGHT {
            let tile = self.get_tile(x, y);
            if tile.is_some() {
                found = true;
                break;
            }
            x += step_x;
            y += step_y;
        }

        if found {
            self.get_mut_tile(x, y)
        } else {
            None
        }
    }

    fn get_tile<'a>(&'a self, x: int, y: int) -> Option<&'a Tile> {
        for tile in self.tiles.iter() {
            if tile.tile_x == x && tile.tile_y == y {
                return Some(tile);
            }
        }
        None
    }

    fn get_mut_tile<'a>(&'a mut self, x: int, y: int) -> Option<&'a mut Tile> {
        for tile in self.tiles.mut_iter() {
            if tile.tile_x == x && tile.tile_y == y {
                return Some(tile);
            }
        }
        None
    }

    fn render_board(&self, c: &Context, gl: &mut Gl) {
        let width = settings::TILE_SIZE * settings::TILE_WIDTH as f64 + settings::TILE_PADDING * (settings::TILE_WIDTH + 1) as f64;
        let height = settings::TILE_SIZE * settings::TILE_HEIGHT as f64 + settings::TILE_PADDING * (settings::TILE_HEIGHT + 1) as f64;
        c.view()
         .rect(settings::BOARD_PADDING,
               settings::BOARD_PADDING + settings::BOARD_OFFSET_Y,
               settings::BOARD_SIZE[0],
               settings::BOARD_SIZE[1])
         .rgba(settings::TILE_BACKGROUND_COLOR[0],
               settings::TILE_BACKGROUND_COLOR[1],
               settings::TILE_BACKGROUND_COLOR[2],
               settings::TILE_BACKGROUND_COLOR[3])
         .fill(gl);

        let mut x = settings::BOARD_PADDING + settings::TILE_PADDING;
        let mut y = settings::BOARD_PADDING + settings::BOARD_OFFSET_Y + settings::TILE_PADDING;
        for _ in range(0, settings::TILE_HEIGHT) {
            for _ in range(0, settings::TILE_WIDTH) {
                c.view()
                 .rect(x, y, settings::TILE_SIZE, settings::TILE_SIZE)
                 .rgba(settings::TILES_COLOR[0][0],
                       settings::TILES_COLOR[0][1],
                       settings::TILES_COLOR[0][2],
                       settings::TILES_COLOR[0][3])
                 .fill(gl);

                x += settings::TILE_PADDING + settings::TILE_SIZE;
            }
            x = settings::BOARD_PADDING + settings::TILE_PADDING;
            y += settings::TILE_PADDING + settings::TILE_SIZE;
        }
    }

    fn render_tiles(&self, c: &Context, gl: &mut Gl) {
        for tile in self.tiles.iter() {
            tile.render(c, gl);
        }
    }
}

<commit_msg>Fixed a bug that three tiles will merge into one<commit_after>
use std::iter::range_step;
use collections::hashmap::HashSet;
use rand::random;
use graphics::*;
use piston::*;
use settings;
use tile::{
    Tile,
    TileStatic,
};

pub struct Board {
    tiles: Vec<Tile>,
}

impl Board {
    pub fn new() -> Board {
        Board {
            tiles: Vec::<Tile>::new(),
        }
    }

    pub fn generate_tile(&mut self) {
        if self.tiles.len() == (settings::TILE_WIDTH * settings::TILE_HEIGHT) as uint {
            return;
        }

        loop {
            let x = (random::<uint>() % settings::TILE_WIDTH as uint) as int;
            let y = (random::<uint>() % settings::TILE_HEIGHT as uint) as int;
            if self.get_tile(x, y).is_none() {
                self.tiles.push(Tile::new(2, x, y));
                break;
            }
        }
    }

    pub fn update(&mut self, dt: f64) {
        for tile in self.tiles.mut_iter() {
            tile.update(dt);
        }

        if self.is_locking() {
            return;
        }
        let mut tiles_need_removed = HashSet::<uint>::new();
        let mut tiles_need_added = Vec::<Tile>::new();
        for i in range(0, self.tiles.len()) {
            let tile1 = self.tiles.get(i);
            if tile1.status != TileStatic {
                continue;
            }
            for j in range(i + 1, self.tiles.len()) {
                let tile2 = self.tiles.get(j);
                if tile2.status != TileStatic
                   || tile1.tile_x != tile2.tile_x
                   || tile1.tile_y != tile2.tile_y {
                    continue;
                }

                tiles_need_removed.insert(i);
                tiles_need_removed.insert(j);
                tiles_need_added.push(Tile::new_combined(tile1.score + tile2.score, tile1.tile_x, tile1.tile_y));
                break;
            }
        }

        if tiles_need_removed.len() > 0 {
            let mut tiles = Vec::<Tile>::new();
            for i in range(0, self.tiles.len()) {
                if !tiles_need_removed.contains(&i) {
                    tiles.push(*self.tiles.get(i));
                }
            }
            self.tiles = tiles.append(tiles_need_added.as_slice());
        }
    }

    pub fn render(&self, c: &Context, gl: &mut Gl) {
        self.render_board(c, gl);
        self.render_tiles(c, gl);
    }

    pub fn can_merge(&self) -> bool {
        for row in range(0, settings::TILE_HEIGHT) {
            if !self.can_merge_row(row) {
                return false;
            }
        }
        true
    }

    pub fn merge_from_bottom_to_top(&mut self) {
        self.merge_col(0, settings::TILE_HEIGHT, 1);
    }

    pub fn merge_from_top_to_bottom(&mut self) {
        self.merge_col(settings::TILE_HEIGHT - 1, -1, -1);
    }

    fn merge_col(&mut self, y_start: int, y_end: int, y_step: int) {
        if self.is_locking() {
            return;
        }

        let mut need_generate = false;
        loop {
            // move all tiles to right place
            for col in range(0, settings::TILE_WIDTH) {
                for row in range_step(y_start, y_end, y_step) {
                    match self.get_mut_tile(col, row) {
                        None => {
                            match self.get_mut_next_tile(col, row, 0, y_step) {
                                Some(ref mut tile) => {
                                    println!("move ({}, {}) to ({}, {})",
                                             tile.tile_x, tile.tile_y, col, row);
                                    tile.start_moving(col, row);
                                },
                                _ => {},
                            }
                        },
                        _ => {},
                    }
                }
            }

            let mut did_merged = false;
            for col in range(0, settings::TILE_WIDTH) {
                let mut found = false;
                let mut sx = 0;
                let mut sy = 0;
                let mut dx = 0;
                let mut dy = 0;
                for row in range_step(y_start, y_end, y_step) {
                    match self.get_tile(col, row) {
                        Some(ref d_tile) => {
                            match self.get_next_tile(col, row, 0, y_step) {
                                Some(ref s_tile)
                                if d_tile.score == s_tile.score
                                   && self.get_tile_count(d_tile.tile_x, d_tile.tile_y) == 1 => {
                                    found = true;
                                    dx = d_tile.tile_x;
                                    dy = d_tile.tile_y;
                                    sx = s_tile.tile_x;
                                    sy = s_tile.tile_y;
                                    break;
                                },
                                _ => {},
                            }
                        },
                        None => {
                            break;
                        }
                    }
                }
                if found {
                    need_generate = true;
                    did_merged = true;
                    let mut tile = self.get_mut_tile(sx, sy);
                    let tile = tile.get_mut_ref();
                    tile.start_moving(dx, dy);
                }
            }

            if !did_merged {
                break;
            }
        }
        if need_generate {
            self.generate_tile();
        }
    }

    pub fn merge_from_left_to_right(&mut self) {
        self.merge_row(settings::TILE_WIDTH - 1, -1, -1);
    }

    pub fn merge_from_right_to_left(&mut self) {
        self.merge_row(0, settings::TILE_WIDTH, 1);
    }

    fn merge_row(&mut self, x_start: int, x_end: int, x_step: int) {
        if self.is_locking() {
            return;
        }

        let mut need_generate = false;
        loop {
            // move all tiles to right place
            for row in range(0, settings::TILE_HEIGHT) {
                for col in range_step(x_start, x_end, x_step) {
                    match self.get_mut_tile(col, row) {
                        None => {
                            match self.get_mut_next_tile(col, row, x_step, 0) {
                                Some(ref mut tile) => {
                                    println!("move ({}, {}) to ({}, {})", tile.tile_x, tile.tile_y, col, row);
                                    need_generate = true;
                                    tile.start_moving(col, row);
                                },
                                _ => {},
                            }
                        },
                        _ => {},
                    }
                }
            }

            // merge
            let mut did_merged = false;
            for row in range(0, settings::TILE_HEIGHT) {
                let mut found = false;
                let mut sx = 0;
                let mut sy = 0;
                let mut dx = 0;
                let mut dy = 0;
                for col in range_step(x_start, x_end, x_step) {
                    match self.get_tile(col, row) {
                        Some(ref d_tile) => {
                            match self.get_next_tile(col, row, x_step, 0) {
                                Some(ref s_tile)
                                if d_tile.score == s_tile.score
                                   && self.get_tile_count(d_tile.tile_x, d_tile.tile_y) == 1 => {
                                    found = true;
                                    dx = d_tile.tile_x;
                                    dy = d_tile.tile_y;
                                    sx = s_tile.tile_x;
                                    sy = s_tile.tile_y;
                                    break;
                                },
                                _ => {},
                            }
                        },
                        None => {
                            break;
                        }
                    }
                }
                if found {
                    need_generate = true;
                    did_merged = true;
                    let mut tile = self.get_mut_tile(sx, sy);
                    let tile = tile.get_mut_ref();
                    tile.start_moving(dx, dy);
                }
            }

            if !did_merged {
                break;
            }
        }

        if need_generate {
            self.generate_tile();
        }
    }

    fn can_merge_row(&self, row: int) -> bool {
        for col in range(0, settings::TILE_WIDTH) {
            match self.get_tile(col, row) {
                Some(ref tile) => {
                    match self.get_next_tile(tile.tile_x, tile.tile_y, 1, 0) {
                        Some(ref s_tile) => {
                            if tile.score == s_tile.score {
                                return true;
                            }
                        },
                        _ => {},
                    }
                },
                None => {},
            }
        }
        false
    }

    fn is_locking(&self) -> bool {
        for tile in self.tiles.iter() {
            if tile.status != TileStatic {
                return true;
            }
        }
        false
    }

    /// Returns next tile right besides (x, y)
    fn get_next_tile<'a>(&'a self, x: int, y: int, step_x: int, step_y: int) -> Option<&'a Tile> {
        let mut x = x + step_x;
        let mut y = y + step_y;
        while x >= 0 && x < settings::TILE_WIDTH
              && y >= 0 && y < settings::TILE_HEIGHT {
            let tile = self.get_tile(x, y);
            if tile.is_some() {
                return tile;
            }
            x += step_x;
            y += step_y;
        }
        None
    }

    fn get_mut_next_tile<'a>(&'a mut self, x: int, y: int, step_x: int, step_y: int) -> Option<&'a mut Tile> {
        let mut x = x + step_x;
        let mut y = y + step_y;
        let mut found = false;
        while x >= 0 && x < settings::TILE_WIDTH
              && y >= 0 && y < settings::TILE_HEIGHT {
            let tile = self.get_tile(x, y);
            if tile.is_some() {
                found = true;
                break;
            }
            x += step_x;
            y += step_y;
        }

        if found {
            self.get_mut_tile(x, y)
        } else {
            None
        }
    }

    fn get_tile<'a>(&'a self, x: int, y: int) -> Option<&'a Tile> {
        for tile in self.tiles.iter() {
            if tile.tile_x == x && tile.tile_y == y {
                return Some(tile);
            }
        }
        None
    }

    fn get_mut_tile<'a>(&'a mut self, x: int, y: int) -> Option<&'a mut Tile> {
        for tile in self.tiles.mut_iter() {
            if tile.tile_x == x && tile.tile_y == y {
                return Some(tile);
            }
        }
        None
    }

    fn get_tile_count(&self, x: int, y: int) -> int {
        let mut count = 0;
        for tile in self.tiles.iter() {
            if tile.tile_x == x && tile.tile_y == y {
                count += 1;
            }
        }
        count
    }

    fn render_board(&self, c: &Context, gl: &mut Gl) {
        let width = settings::TILE_SIZE * settings::TILE_WIDTH as f64 + settings::TILE_PADDING * (settings::TILE_WIDTH + 1) as f64;
        let height = settings::TILE_SIZE * settings::TILE_HEIGHT as f64 + settings::TILE_PADDING * (settings::TILE_HEIGHT + 1) as f64;
        c.view()
         .rect(settings::BOARD_PADDING,
               settings::BOARD_PADDING + settings::BOARD_OFFSET_Y,
               settings::BOARD_SIZE[0],
               settings::BOARD_SIZE[1])
         .rgba(settings::TILE_BACKGROUND_COLOR[0],
               settings::TILE_BACKGROUND_COLOR[1],
               settings::TILE_BACKGROUND_COLOR[2],
               settings::TILE_BACKGROUND_COLOR[3])
         .fill(gl);

        let mut x = settings::BOARD_PADDING + settings::TILE_PADDING;
        let mut y = settings::BOARD_PADDING + settings::BOARD_OFFSET_Y + settings::TILE_PADDING;
        for _ in range(0, settings::TILE_HEIGHT) {
            for _ in range(0, settings::TILE_WIDTH) {
                c.view()
                 .rect(x, y, settings::TILE_SIZE, settings::TILE_SIZE)
                 .rgba(settings::TILES_COLOR[0][0],
                       settings::TILES_COLOR[0][1],
                       settings::TILES_COLOR[0][2],
                       settings::TILES_COLOR[0][3])
                 .fill(gl);

                x += settings::TILE_PADDING + settings::TILE_SIZE;
            }
            x = settings::BOARD_PADDING + settings::TILE_PADDING;
            y += settings::TILE_PADDING + settings::TILE_SIZE;
        }
    }

    fn render_tiles(&self, c: &Context, gl: &mut Gl) {
        for tile in self.tiles.iter() {
            tile.render(c, gl);
        }
    }
}

<|endoftext|>


<commit_before>// Copyright (c) 2014 Richard Diamond & contributors.
//
// This file is part of the Rust PPApi project.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use ffi;
use ppb::{ResourceInterface, MediaStreamVideoTrackIf, get_media_stream_video_track};

use super::{Callback, CallbackArgs, StringVar, Code, Resource};
use super::video_frame::{self, VideoFrame};

/// Created on the JS side and sent in a message.
#[derive(Hash, Eq, PartialEq, Debug)]
pub struct VideoTrack(ffi::PP_Resource);

impl_clone_drop_for!(VideoTrack);
impl_resource_for!(VideoTrack, ResourceType::VideoFrame);

#[doc(hidden)]
impl From<ffi::PP_Resource> for VideoTrack {
    fn from(v: ffi::PP_Resource) -> VideoTrack {
        debug_assert!(get_media_stream_video_track().is(v));
        VideoTrack(v)
    }
}

#[derive(Hash, Eq, PartialEq, Debug, Copy, Clone)]
pub enum Attr {
    /// The number of buffer frames. Chrome may use more or less. How many
    /// frames to buffer depends on usage - request at least 2 to make sure
    /// latency doesn't cause lost frames. If the plugin expects to hold on to
    /// more than one frame at a time (e.g. to do multi-frame processing), it
    /// should request that many more. If this attribute is not specified or
    /// value 0 is specified for this attribute, the default value will be
    /// used.
    Buffers(u32),

    /// The width of video frames in pixels.
    ///
    /// It should be a multiple of 4. If the specified size is different from
    /// the video source (webcam), frames will be scaled to specified size. If
    /// this attribute is not specified or value 0 is specified, the original
    /// frame size of the video track will be used.
    ///
    /// Maximum value: 4096 (4K resolution).
    Width(u32),
    /// The height of video frames in pixels.
    ///
    /// It should be a multiple of 4. If the specified size is different from
    /// the video source (webcam), frames will be scaled to specified size. If
    /// this attribute is not specified or value 0 is specified, the original
    /// frame size of the video track will be used.
    ///
    /// Maximum value: 4096 (4K resolution).
    Height(u32),

    /// The format of video frames.
    ///
    /// The attribute value is a `video_frame::Format`. If the specified
    /// format is different from the video source (webcam), frames will be
    /// converted to specified format. If this attribute is not specified or
    /// value `Format::Unknown` is specified, the orignal frame format of the
    /// video track will be used.
    Format(video_frame::Format),
}
impl Attr {
    #[doc(hidden)]
    pub fn to_ffi(self) -> ffi::PP_MediaStreamVideoTrack_Attrib {
        match self {
            Attr::Buffers(..) => ffi::PP_MEDIASTREAMVIDEOTRACK_ATTRIB_BUFFERED_FRAMES,
            Attr::Width(..) => ffi::PP_MEDIASTREAMVIDEOTRACK_ATTRIB_WIDTH,
            Attr::Height(..) => ffi::PP_MEDIASTREAMVIDEOTRACK_ATTRIB_HEIGHT,
            Attr::Format(..) => ffi::PP_MEDIASTREAMVIDEOTRACK_ATTRIB_FORMAT,
        }
    }
}

impl VideoTrack {
    pub fn configure<'a, T: AsRef<&'a [Attr]>, F>(&self, attrs: T, callback: CallbackArgs<F, ()>) -> Code<()>
        where F: FnOnce(Code<()>)
    {
        use std::cmp::min;

        let mut nattrs: Vec<ffi::PP_MediaStreamVideoTrack_Attrib> =
            Vec::with_capacity(attrs.as_ref().len() + 1);
        for attr in attrs.as_ref().iter() {
            nattrs.push(attr.to_ffi());
            match attr {
                &Attr::Buffers(c) => {
                    nattrs.push(c as ffi::PP_MediaStreamVideoTrack_Attrib);
                },
                &Attr::Width(c) | &Attr::Height(c) => {
                    nattrs.push(min(c, 4096) as ffi::PP_MediaStreamVideoTrack_Attrib);
                },
                &Attr::Format(f) => {
                    nattrs.push(f.to_ffi());
                },
            }
        }
        nattrs.push(ffi::PP_MEDIASTREAMVIDEOTRACK_ATTRIB_NONE);

        let cc = callback.to_ffi_callback();

        let code = get_media_stream_video_track()
            .configure(self.unwrap(), nattrs.as_ref(), cc.cc);
        cc.drop_with_code(code)
    }
    pub fn buffer_count(&self) -> Code<u32> {
        get_media_stream_video_track()
            .get_attrib(self.unwrap(), Attr::Buffers(0).to_ffi())
            .map_ok(|i| {
                i as u32
            })
    }
    pub fn width(&self) -> Code<u32> {
        get_media_stream_video_track()
            .get_attrib(self.unwrap(), Attr::Width(0).to_ffi())
            .map_ok(|i| {
                i as u32
            })
    }
    pub fn height(&self) -> Code<u32> {
        get_media_stream_video_track()
            .get_attrib(self.unwrap(), Attr::Height(0).to_ffi())
            .map_ok(|i| {
                i as u32
            })
    }
    pub fn format(&self) -> Code<video_frame::Format> {
        get_media_stream_video_track()
            .get_attrib(self.unwrap(),
                        Attr::Format(video_frame::Format::Unknown).to_ffi())
            .map_ok(|i| {
                From::from(i as ffi::PP_VideoFrame_Format)
            })
    }
    pub fn get_id(&self) -> StringVar {
        From::from(get_media_stream_video_track().get_id(self.unwrap()))
    }
    pub fn has_ended(&self) -> bool {
        get_media_stream_video_track()
            .has_ended(self.unwrap())
    }
    pub fn get_frame<F>(&self, f: CallbackArgs<F, (VideoTrack, VideoFrame)>) ->
        Code<(VideoTrack, VideoFrame)> where F: FnOnce(Code<(VideoTrack, VideoFrame)>)
    {
        impl super::InPlaceInit for (VideoTrack, i32) { }
        fn map_args((this, frame): (VideoTrack, i32), _status: Code) ->
            (VideoTrack, VideoFrame)
        {
            (this, From::from(frame))
        }

        let mapper = super::StorageToArgsMapper(map_args);
        let mut cc = f.to_ffi_callback((self.clone(), 0i32), mapper);
        let fficc = cc.cc;
        let code = get_media_stream_video_track()
            .get_frame(self.unwrap(), &mut cc.1, fficc);
        cc.drop_with_code(code)
    }
    pub fn recycle_frame(&self, frame: VideoFrame) -> Code {
        get_media_stream_video_track()
            .recycle_frame(self.unwrap(), frame.unwrap())
    }
    pub fn close(self) {
        get_media_stream_video_track()
            .close(self.unwrap())
    }
}
<commit_msg>Move `VideoTrack::configure` to the new callback type.<commit_after>// Copyright (c) 2014 Richard Diamond & contributors.
//
// This file is part of the Rust PPApi project.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use ffi;
use ppb::{ResourceInterface, MediaStreamVideoTrackIf, get_media_stream_video_track};

use super::{Callback, CallbackArgs, StringVar, Code, Resource};
use super::video_frame::{self, VideoFrame};

/// Created on the JS side and sent in a message.
#[derive(Hash, Eq, PartialEq, Debug)]
pub struct VideoTrack(ffi::PP_Resource);

impl_clone_drop_for!(VideoTrack);
impl_resource_for!(VideoTrack, ResourceType::VideoFrame);

#[doc(hidden)]
impl From<ffi::PP_Resource> for VideoTrack {
    fn from(v: ffi::PP_Resource) -> VideoTrack {
        debug_assert!(get_media_stream_video_track().is(v));
        VideoTrack(v)
    }
}

#[derive(Hash, Eq, PartialEq, Debug, Copy, Clone)]
pub enum Attr {
    /// The number of buffer frames. Chrome may use more or less. How many
    /// frames to buffer depends on usage - request at least 2 to make sure
    /// latency doesn't cause lost frames. If the plugin expects to hold on to
    /// more than one frame at a time (e.g. to do multi-frame processing), it
    /// should request that many more. If this attribute is not specified or
    /// value 0 is specified for this attribute, the default value will be
    /// used.
    Buffers(u32),

    /// The width of video frames in pixels.
    ///
    /// It should be a multiple of 4. If the specified size is different from
    /// the video source (webcam), frames will be scaled to specified size. If
    /// this attribute is not specified or value 0 is specified, the original
    /// frame size of the video track will be used.
    ///
    /// Maximum value: 4096 (4K resolution).
    Width(u32),
    /// The height of video frames in pixels.
    ///
    /// It should be a multiple of 4. If the specified size is different from
    /// the video source (webcam), frames will be scaled to specified size. If
    /// this attribute is not specified or value 0 is specified, the original
    /// frame size of the video track will be used.
    ///
    /// Maximum value: 4096 (4K resolution).
    Height(u32),

    /// The format of video frames.
    ///
    /// The attribute value is a `video_frame::Format`. If the specified
    /// format is different from the video source (webcam), frames will be
    /// converted to specified format. If this attribute is not specified or
    /// value `Format::Unknown` is specified, the orignal frame format of the
    /// video track will be used.
    Format(video_frame::Format),
}
impl Attr {
    #[doc(hidden)]
    pub fn to_ffi(self) -> ffi::PP_MediaStreamVideoTrack_Attrib {
        match self {
            Attr::Buffers(..) => ffi::PP_MEDIASTREAMVIDEOTRACK_ATTRIB_BUFFERED_FRAMES,
            Attr::Width(..) => ffi::PP_MEDIASTREAMVIDEOTRACK_ATTRIB_WIDTH,
            Attr::Height(..) => ffi::PP_MEDIASTREAMVIDEOTRACK_ATTRIB_HEIGHT,
            Attr::Format(..) => ffi::PP_MEDIASTREAMVIDEOTRACK_ATTRIB_FORMAT,
        }
    }
}

impl VideoTrack {
    pub fn configure<T: AsRef<[Attr]>, F>(&self, attrs: T, callback: CallbackArgs<F, ()>) -> Code<()>
        where F: FnOnce(Code<()>)
    {
        use std::cmp::min;

        let mut nattrs: Vec<ffi::PP_MediaStreamVideoTrack_Attrib> =
            Vec::with_capacity(attrs.as_ref().len() + 1);
        for attr in attrs.as_ref().iter() {
            nattrs.push(attr.to_ffi());
            match attr {
                &Attr::Buffers(c) => {
                    nattrs.push(c as ffi::PP_MediaStreamVideoTrack_Attrib);
                },
                &Attr::Width(c) | &Attr::Height(c) => {
                    nattrs.push(min(c, 4096) as ffi::PP_MediaStreamVideoTrack_Attrib);
                },
                &Attr::Format(f) => {
                    nattrs.push(f.to_ffi());
                },
            }
        }
        nattrs.push(ffi::PP_MEDIASTREAMVIDEOTRACK_ATTRIB_NONE);

        let cc = callback.to_ffi_callback((), Default::default());

        let code = get_media_stream_video_track()
            .configure(self.unwrap(), nattrs.as_ref(), cc.cc);
        cc.drop_with_code(code)
    }
    pub fn buffer_count(&self) -> Code<u32> {
        get_media_stream_video_track()
            .get_attrib(self.unwrap(), Attr::Buffers(0).to_ffi())
            .map_ok(|i| {
                i as u32
            })
    }
    pub fn width(&self) -> Code<u32> {
        get_media_stream_video_track()
            .get_attrib(self.unwrap(), Attr::Width(0).to_ffi())
            .map_ok(|i| {
                i as u32
            })
    }
    pub fn height(&self) -> Code<u32> {
        get_media_stream_video_track()
            .get_attrib(self.unwrap(), Attr::Height(0).to_ffi())
            .map_ok(|i| {
                i as u32
            })
    }
    pub fn format(&self) -> Code<video_frame::Format> {
        get_media_stream_video_track()
            .get_attrib(self.unwrap(),
                        Attr::Format(video_frame::Format::Unknown).to_ffi())
            .map_ok(|i| {
                From::from(i as ffi::PP_VideoFrame_Format)
            })
    }
    pub fn get_id(&self) -> StringVar {
        From::from(get_media_stream_video_track().get_id(self.unwrap()))
    }
    pub fn has_ended(&self) -> bool {
        get_media_stream_video_track()
            .has_ended(self.unwrap())
    }
    pub fn get_frame<F>(&self, f: CallbackArgs<F, (VideoTrack, VideoFrame)>) ->
        Code<(VideoTrack, VideoFrame)> where F: FnOnce(Code<(VideoTrack, VideoFrame)>)
    {
        impl super::InPlaceInit for (VideoTrack, i32) { }
        fn map_args((this, frame): (VideoTrack, i32), _status: Code) ->
            (VideoTrack, VideoFrame)
        {
            (this, From::from(frame))
        }

        let mapper = super::StorageToArgsMapper(map_args);
        let mut cc = f.to_ffi_callback((self.clone(), 0i32), mapper);
        let fficc = cc.cc;
        let code = get_media_stream_video_track()
            .get_frame(self.unwrap(), &mut cc.1, fficc);
        cc.drop_with_code(code)
    }
    pub fn recycle_frame(&self, frame: VideoFrame) -> Code {
        get_media_stream_video_track()
            .recycle_frame(self.unwrap(), frame.unwrap())
    }
    pub fn close(self) {
        get_media_stream_video_track()
            .close(self.unwrap())
    }
}
<|endoftext|>