<commit_before>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

//! Utilities for providing the hover feature
use crate::{
    lsp::{HoverContents, LanguageString, MarkedString},
    lsp_runtime_error::{LSPRuntimeError, LSPRuntimeResult},
    node_resolution_info::{NodeKind, NodeResolutionInfo},
    server::{LSPState, SourcePrograms},
    LSPExtraDataProvider,
};
use common::PerfLogger;
use graphql_ir::Value;
use graphql_text_printer::print_value;
use lsp_types::{request::HoverRequest, request::Request, Hover};
use schema::{SDLSchema, Schema};
use schema_documentation::SchemaDocumentation;
use schema_print::print_directive;

fn graphql_marked_string(value: String) -> MarkedString {
    MarkedString::LanguageString(LanguageString {
        language: "graphql".to_string(),
        value,
    })
}

fn hover_content_wrapper(value: String) -> HoverContents {
    HoverContents::Scalar(graphql_marked_string(value))
}

/// This will provide a more accurate information about some of the specific Relay directives
/// that cannot be expressed via SDL
fn argument_definition_hover_info(directive_name: &str) -> Option<HoverContents> {
    let content = match directive_name {
        "argumentDefinitions" => Some(
            r#"
`@argumentDefinitions` is a directive used to specify arguments taken by a fragment.

---
@see: https://relay.dev/docs/en/graphql-in-relay.html#argumentdefinitions
"#,
        ),
        "arguments" => Some(
            r#"
`@arguments` is a directive used to pass arguments to a fragment that was defined using `@argumentDefinitions`.

---
@see: https://relay.dev/docs/en/graphql-in-relay.html#arguments
"#,
        ),
        "uncheckedArguments_DEPRECATED" => Some(
            r#"
DEPRECATED version of `@arguments` directive.
`@arguments` is a directive used to pass arguments to a fragment that was defined using `@argumentDefinitions`.

---
@see: https://relay.dev/docs/en/graphql-in-relay.html#arguments
"#,
        ),
        _ => None,
    };

    content.map(|value| HoverContents::Scalar(MarkedString::String(value.to_string())))
}

fn get_hover_response_contents(
    node_resolution_info: NodeResolutionInfo,
    schema: &SDLSchema,
    schema_documentation: impl SchemaDocumentation,
    source_programs: &SourcePrograms,
    extra_data_provider: &dyn LSPExtraDataProvider,
) -> Option<HoverContents> {
    let kind = node_resolution_info.kind;

    match kind {
        NodeKind::Variable(type_name) => Some(hover_content_wrapper(type_name)),
        NodeKind::Directive(directive_name, argument_name) => {
            if let Some(argument_definition_hover_info) =
                argument_definition_hover_info(directive_name.lookup())
            {
                return Some(argument_definition_hover_info);
            }

            let schema_directive = schema.get_directive(directive_name)?;

            if let Some(argument_name) = argument_name {
                let argument = schema_directive.arguments.named(argument_name)?;
                let content = format!(
                    "{}: {}",
                    argument_name,
                    schema.get_type_string(&argument.type_)
                );
                Some(hover_content_wrapper(content))
            } else {
                let directive_definition = print_directive(schema, &schema_directive);
                let markdown_definition = graphql_marked_string(directive_definition);
                let mut hover_contents: Vec<MarkedString> = vec![markdown_definition];
                if let Some(description) = schema_directive.description {
                    hover_contents.push(MarkedString::String(description.to_string()));
                }
                Some(HoverContents::Array(hover_contents))
            }
        }
        NodeKind::FieldName => {
            let (parent_type, field) = node_resolution_info
                .type_path
                .resolve_current_field(schema)?;

            let type_name = schema.get_type_string(&field.type_);
            let parent_type_name = schema.get_type_name(parent_type).to_string();

            let mut hover_contents: Vec<MarkedString> =
                vec![MarkedString::String(format!("Field: **{}**", field.name))];

            if let Some(schema_description) = field.description {
                hover_contents.push(MarkedString::String(schema_description.to_string()));
            } else if let Some(field_description) =
                schema_documentation.get_field_description(&parent_type_name, field.name.lookup())
            {
                hover_contents.push(MarkedString::String(field_description.to_string()));
            }

            hover_contents.push(MarkedString::String(format!("Type: **{}**", type_name)));

            if let Some(type_description) = schema_documentation.get_type_description(&type_name) {
                hover_contents.push(MarkedString::String(type_description.to_string()));
            }

            if !field.arguments.is_empty() {
                hover_contents.push(MarkedString::String(
                    "This field accepts following arguments".to_string(),
                ));

                for arg in field.arguments.iter() {
                    hover_contents.push(MarkedString::from_markdown(format!(
                        "`{}: {}{}`\n\n{}",
                        arg.name,
                        schema.get_type_string(&arg.type_),
                        if let Some(default_value) = &arg.default_value {
                            format!(" = {}", default_value)
                        } else {
                            "".to_string()
                        },
                        if let Some(description) = schema_documentation
                            .get_field_argument_description(
                                &parent_type_name,
                                field.name.lookup(),
                                arg.name.lookup(),
                            )
                        {
                            description.to_string()
                        } else {
                            "".to_string()
                        }
                    )));
                }
            }
            Some(HoverContents::Array(hover_contents))
        }
        NodeKind::FieldArgument(field_name, argument_name) => {
            let type_ref = node_resolution_info
                .type_path
                .resolve_current_type_reference(schema)?;

            if type_ref.inner().is_object() || type_ref.inner().is_interface() {
                let field_id = schema.named_field(type_ref.inner(), field_name)?;
                let field = schema.field(field_id);
                let argument = field.arguments.named(argument_name)?;
                let content = format!(
                    "{}: {}",
                    argument_name,
                    schema.get_type_string(&argument.type_)
                );
                Some(hover_content_wrapper(content))
            } else {
                None
            }
        }
        NodeKind::FragmentSpread(fragment_name) => {
            let project_name = node_resolution_info.project_name;
            if let Some(source_program) = source_programs.get(&project_name) {
                let fragment = source_program.fragment(fragment_name)?;
                let mut hover_contents: Vec<MarkedString> = vec![];
                hover_contents.push(graphql_marked_string(format!(
                    "fragment {} on {} {{ ... }}",
                    fragment.name.item,
                    schema.get_type_name(fragment.type_condition),
                )));

                if !fragment.variable_definitions.is_empty() {
                    let mut variables_string: Vec<String> =
                        vec!["This fragment accepts following arguments:".to_string()];
                    variables_string.push("```".to_string());
                    for var in &fragment.variable_definitions {
                        let default_value = match var.default_value.clone() {
                            Some(default_value) => format!(
                                ", default_value = {}",
                                print_value(schema, &Value::Constant(default_value))
                            ),
                            None => "".to_string(),
                        };
                        variables_string.push(format!(
                            "- {}: {}{}",
                            var.name.item,
                            schema.get_type_string(&var.type_),
                            default_value,
                        ));
                    }
                    variables_string.push("```".to_string());
                    hover_contents.push(MarkedString::String(variables_string.join("\n")))
                }

                let fragment_name_details: Vec<&str> = fragment_name.lookup().split('_').collect();
                // We expect the fragment name to be `ComponentName_propName`
                if fragment_name_details.len() == 2 {
                    hover_contents.push(MarkedString::from_markdown(format!(
                        r#"
To consume this fragment spread,
pass it to the component that defined it.

For example:
```js
    <{} {}={{data.{}}} />
```
"#,
                        fragment_name_details[0],
                        fragment_name_details[1],
                        fragment_name_details[1],
                    )));
                } // We may log an error (later), if that is not the case.

                hover_contents.push(MarkedString::String(
                    "@see: https://relay.dev/docs/en/thinking-in-relay#data-masking".to_string(),
                ));
                Some(HoverContents::Array(hover_contents))
            } else {
                None
            }
        }
        NodeKind::OperationDefinition(operation) => {
            let search_token = if let Some(operation_name) = operation.name {
                operation_name.value.lookup().to_string()
            } else {
                return None;
            };

            let extra_data = extra_data_provider.fetch_query_stats(search_token);
            if !extra_data.is_empty() {
                Some(HoverContents::Array(
                    extra_data
                        .iter()
                        .map(|str| MarkedString::String(str.to_string()))
                        .collect::<_>(),
                ))
            } else {
                None
            }
        }
        NodeKind::FragmentDefinition(fragment) => {
            let type_ = node_resolution_info
                .type_path
                .resolve_current_type_reference(schema)?;
            let title = graphql_marked_string(format!(
                "fragment {} on {} {{ .. }}",
                fragment.name.value,
                schema.get_type_name(type_.inner())
            ));

            let hover_contents = vec![
                title,
                MarkedString::String(
                    r#"Fragments let you construct sets of fields,
and then include them in queries where you need to.

---
@see: https://graphql.org/learn/queries/#fragments
"#
                    .to_string(),
                ),
            ];

            Some(HoverContents::Array(hover_contents))
        }
        NodeKind::InlineFragment => None,
        NodeKind::TypeCondition(_) => None,
    }
}

pub(crate) fn on_hover<
    TPerfLogger: PerfLogger + 'static,
    TSchemaDocumentation: SchemaDocumentation,
>(
    state: &mut LSPState<TPerfLogger, TSchemaDocumentation>,
    params: <HoverRequest as Request>::Params,
) -> LSPRuntimeResult<<HoverRequest as Request>::Result> {
    let node_resolution_info = state.resolve_node(&params.text_document_position_params)?;

    log::debug!("Hovering over {:?}", node_resolution_info);
    if let Some(schema) = state.get_schemas().get(&node_resolution_info.project_name) {
        let schema_documentation =
            state.get_schema_documentation(&node_resolution_info.project_name.to_string());

        let contents = get_hover_response_contents(
            node_resolution_info,
            &schema,
            schema_documentation,
            state.get_source_programs_ref(),
            state.extra_data_provider.as_ref(),
        )
        .ok_or_else(|| {
            LSPRuntimeError::UnexpectedError("Unable to get hover contents".to_string())
        })?;
        Ok(Some(Hover {
            contents,
            range: None,
        }))
    } else {
        Err(LSPRuntimeError::ExpectedError)
    }
}
<commit_msg>Do not access field.description in hover, instead go through schema_documentation<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

//! Utilities for providing the hover feature
use crate::{
    lsp::{HoverContents, LanguageString, MarkedString},
    lsp_runtime_error::{LSPRuntimeError, LSPRuntimeResult},
    node_resolution_info::{NodeKind, NodeResolutionInfo},
    server::{LSPState, SourcePrograms},
    LSPExtraDataProvider,
};
use common::PerfLogger;
use graphql_ir::Value;
use graphql_text_printer::print_value;
use lsp_types::{request::HoverRequest, request::Request, Hover};
use schema::{SDLSchema, Schema};
use schema_documentation::SchemaDocumentation;
use schema_print::print_directive;

fn graphql_marked_string(value: String) -> MarkedString {
    MarkedString::LanguageString(LanguageString {
        language: "graphql".to_string(),
        value,
    })
}

fn hover_content_wrapper(value: String) -> HoverContents {
    HoverContents::Scalar(graphql_marked_string(value))
}

/// This will provide a more accurate information about some of the specific Relay directives
/// that cannot be expressed via SDL
fn argument_definition_hover_info(directive_name: &str) -> Option<HoverContents> {
    let content = match directive_name {
        "argumentDefinitions" => Some(
            r#"
`@argumentDefinitions` is a directive used to specify arguments taken by a fragment.

---
@see: https://relay.dev/docs/en/graphql-in-relay.html#argumentdefinitions
"#,
        ),
        "arguments" => Some(
            r#"
`@arguments` is a directive used to pass arguments to a fragment that was defined using `@argumentDefinitions`.

---
@see: https://relay.dev/docs/en/graphql-in-relay.html#arguments
"#,
        ),
        "uncheckedArguments_DEPRECATED" => Some(
            r#"
DEPRECATED version of `@arguments` directive.
`@arguments` is a directive used to pass arguments to a fragment that was defined using `@argumentDefinitions`.

---
@see: https://relay.dev/docs/en/graphql-in-relay.html#arguments
"#,
        ),
        _ => None,
    };

    content.map(|value| HoverContents::Scalar(MarkedString::String(value.to_string())))
}

fn get_hover_response_contents(
    node_resolution_info: NodeResolutionInfo,
    schema: &SDLSchema,
    schema_documentation: impl SchemaDocumentation,
    source_programs: &SourcePrograms,
    extra_data_provider: &dyn LSPExtraDataProvider,
) -> Option<HoverContents> {
    let kind = node_resolution_info.kind;

    match kind {
        NodeKind::Variable(type_name) => Some(hover_content_wrapper(type_name)),
        NodeKind::Directive(directive_name, argument_name) => {
            if let Some(argument_definition_hover_info) =
                argument_definition_hover_info(directive_name.lookup())
            {
                return Some(argument_definition_hover_info);
            }

            let schema_directive = schema.get_directive(directive_name)?;

            if let Some(argument_name) = argument_name {
                let argument = schema_directive.arguments.named(argument_name)?;
                let content = format!(
                    "{}: {}",
                    argument_name,
                    schema.get_type_string(&argument.type_)
                );
                Some(hover_content_wrapper(content))
            } else {
                let directive_definition = print_directive(schema, &schema_directive);
                let markdown_definition = graphql_marked_string(directive_definition);
                let mut hover_contents: Vec<MarkedString> = vec![markdown_definition];
                if let Some(description) = schema_directive.description {
                    hover_contents.push(MarkedString::String(description.to_string()));
                }
                Some(HoverContents::Array(hover_contents))
            }
        }
        NodeKind::FieldName => {
            let (parent_type, field) = node_resolution_info
                .type_path
                .resolve_current_field(schema)?;

            let type_name = schema.get_type_string(&field.type_);
            let parent_type_name = schema.get_type_name(parent_type).to_string();

            let mut hover_contents: Vec<MarkedString> =
                vec![MarkedString::String(format!("Field: **{}**", field.name))];

            if let Some(field_description) =
                schema_documentation.get_field_description(&parent_type_name, field.name.lookup())
            {
                hover_contents.push(MarkedString::String(field_description.to_string()));
            }

            hover_contents.push(MarkedString::String(format!("Type: **{}**", type_name)));

            if let Some(type_description) = schema_documentation.get_type_description(&type_name) {
                hover_contents.push(MarkedString::String(type_description.to_string()));
            }

            if !field.arguments.is_empty() {
                hover_contents.push(MarkedString::String(
                    "This field accepts following arguments".to_string(),
                ));

                for arg in field.arguments.iter() {
                    hover_contents.push(MarkedString::from_markdown(format!(
                        "`{}: {}{}`\n\n{}",
                        arg.name,
                        schema.get_type_string(&arg.type_),
                        if let Some(default_value) = &arg.default_value {
                            format!(" = {}", default_value)
                        } else {
                            "".to_string()
                        },
                        if let Some(description) = schema_documentation
                            .get_field_argument_description(
                                &parent_type_name,
                                field.name.lookup(),
                                arg.name.lookup(),
                            )
                        {
                            description.to_string()
                        } else {
                            "".to_string()
                        }
                    )));
                }
            }
            Some(HoverContents::Array(hover_contents))
        }
        NodeKind::FieldArgument(field_name, argument_name) => {
            let type_ref = node_resolution_info
                .type_path
                .resolve_current_type_reference(schema)?;

            if type_ref.inner().is_object() || type_ref.inner().is_interface() {
                let field_id = schema.named_field(type_ref.inner(), field_name)?;
                let field = schema.field(field_id);
                let argument = field.arguments.named(argument_name)?;
                let content = format!(
                    "{}: {}",
                    argument_name,
                    schema.get_type_string(&argument.type_)
                );
                Some(hover_content_wrapper(content))
            } else {
                None
            }
        }
        NodeKind::FragmentSpread(fragment_name) => {
            let project_name = node_resolution_info.project_name;
            if let Some(source_program) = source_programs.get(&project_name) {
                let fragment = source_program.fragment(fragment_name)?;
                let mut hover_contents: Vec<MarkedString> = vec![];
                hover_contents.push(graphql_marked_string(format!(
                    "fragment {} on {} {{ ... }}",
                    fragment.name.item,
                    schema.get_type_name(fragment.type_condition),
                )));

                if !fragment.variable_definitions.is_empty() {
                    let mut variables_string: Vec<String> =
                        vec!["This fragment accepts following arguments:".to_string()];
                    variables_string.push("```".to_string());
                    for var in &fragment.variable_definitions {
                        let default_value = match var.default_value.clone() {
                            Some(default_value) => format!(
                                ", default_value = {}",
                                print_value(schema, &Value::Constant(default_value))
                            ),
                            None => "".to_string(),
                        };
                        variables_string.push(format!(
                            "- {}: {}{}",
                            var.name.item,
                            schema.get_type_string(&var.type_),
                            default_value,
                        ));
                    }
                    variables_string.push("```".to_string());
                    hover_contents.push(MarkedString::String(variables_string.join("\n")))
                }

                let fragment_name_details: Vec<&str> = fragment_name.lookup().split('_').collect();
                // We expect the fragment name to be `ComponentName_propName`
                if fragment_name_details.len() == 2 {
                    hover_contents.push(MarkedString::from_markdown(format!(
                        r#"
To consume this fragment spread,
pass it to the component that defined it.

For example:
```js
    <{} {}={{data.{}}} />
```
"#,
                        fragment_name_details[0],
                        fragment_name_details[1],
                        fragment_name_details[1],
                    )));
                } // We may log an error (later), if that is not the case.

                hover_contents.push(MarkedString::String(
                    "@see: https://relay.dev/docs/en/thinking-in-relay#data-masking".to_string(),
                ));
                Some(HoverContents::Array(hover_contents))
            } else {
                None
            }
        }
        NodeKind::OperationDefinition(operation) => {
            let search_token = if let Some(operation_name) = operation.name {
                operation_name.value.lookup().to_string()
            } else {
                return None;
            };

            let extra_data = extra_data_provider.fetch_query_stats(search_token);
            if !extra_data.is_empty() {
                Some(HoverContents::Array(
                    extra_data
                        .iter()
                        .map(|str| MarkedString::String(str.to_string()))
                        .collect::<_>(),
                ))
            } else {
                None
            }
        }
        NodeKind::FragmentDefinition(fragment) => {
            let type_ = node_resolution_info
                .type_path
                .resolve_current_type_reference(schema)?;
            let title = graphql_marked_string(format!(
                "fragment {} on {} {{ .. }}",
                fragment.name.value,
                schema.get_type_name(type_.inner())
            ));

            let hover_contents = vec![
                title,
                MarkedString::String(
                    r#"Fragments let you construct sets of fields,
and then include them in queries where you need to.

---
@see: https://graphql.org/learn/queries/#fragments
"#
                    .to_string(),
                ),
            ];

            Some(HoverContents::Array(hover_contents))
        }
        NodeKind::InlineFragment => None,
        NodeKind::TypeCondition(_) => None,
    }
}

pub(crate) fn on_hover<
    TPerfLogger: PerfLogger + 'static,
    TSchemaDocumentation: SchemaDocumentation,
>(
    state: &mut LSPState<TPerfLogger, TSchemaDocumentation>,
    params: <HoverRequest as Request>::Params,
) -> LSPRuntimeResult<<HoverRequest as Request>::Result> {
    let node_resolution_info = state.resolve_node(&params.text_document_position_params)?;

    log::debug!("Hovering over {:?}", node_resolution_info);
    if let Some(schema) = state.get_schemas().get(&node_resolution_info.project_name) {
        let schema_documentation =
            state.get_schema_documentation(&node_resolution_info.project_name.to_string());

        let contents = get_hover_response_contents(
            node_resolution_info,
            &schema,
            schema_documentation,
            state.get_source_programs_ref(),
            state.extra_data_provider.as_ref(),
        )
        .ok_or_else(|| {
            LSPRuntimeError::UnexpectedError("Unable to get hover contents".to_string())
        })?;
        Ok(Some(Hover {
            contents,
            range: None,
        }))
    } else {
        Err(LSPRuntimeError::ExpectedError)
    }
}
<|endoftext|>


<commit_before>extern crate sfunc;

use std::rand::Rng;
use std::rand::distributions::{Normal, IndependentSample};

use super::Distribution;

/// A Gaussian distribution with a mean value `mu` and a standard deviation
/// `sigma`.
pub struct Gaussian {
    /// The mean value.
    pub mu: f64,
    /// The standard deviation.
    pub sigma: f64,

    normal: Normal,
}

impl Gaussian {
    /// Creates a Gaussian distribution with the mean value `mu` and the
    /// standard deviation `sigma`.
    #[inline]
    pub fn new(mu: f64, sigma: f64) -> Gaussian {
        Gaussian {
            mu: mu,
            sigma: sigma,
            normal: Normal::new(0.0, 1.0),
        }
    }
}

impl Distribution<f64> for Gaussian {
    #[inline]
    fn cdf(&self, x: f64) -> f64 {
        use self::sfunc::erf;
        (1.0 + erf((x - self.mu) / (self.sigma * Float::sqrt2()))) / 2.0
    }

    #[inline]
    fn inv_cdf(&self, _: f64) -> f64 {
        0.0
    }

    #[inline]
    fn sample<R: Rng>(&self, rng: &mut R) -> f64 {
        self.normal.ind_sample(rng)
    }
}

#[cfg(test)]
mod test {
    use super::super::Distribution;
    use super::Gaussian;

    macro_rules! assert_almost_eq(
        ($x:expr, $y:expr) => ({
            let e: f64 = ::std::f64::EPSILON.sqrt();
            let x: Vec<f64> = $x;
            let y: Vec<f64> = $y;
            for i in range(0u, x.len()) {
                assert!(::std::num::abs(x[i] - y[i]) < e,
                        "expected {:e} ~ {:e}", x[i], y[i]);
            }
        });
    )

    #[test]
    fn cdf() {
        let gaussian = Gaussian::new(1.0, 2.0);

        let x = vec![-4.0, -3.5, -3.0, -2.5, -2.0, -1.5, -1.0, -0.5, 0.0, 0.5,
                     1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0];
        let p = vec![6.209665325776139e-03, 1.222447265504470e-02,
                     2.275013194817922e-02, 4.005915686381709e-02,
                     6.680720126885809e-02, 1.056497736668553e-01,
                     1.586552539314571e-01, 2.266273523768682e-01,
                     3.085375387259869e-01, 4.012936743170763e-01,
                     5.000000000000000e-01, 5.987063256829237e-01,
                     6.914624612740131e-01, 7.733726476231317e-01,
                     8.413447460685429e-01, 8.943502263331446e-01,
                     9.331927987311419e-01];

        assert_almost_eq!(x.iter().map(|&x| {
            gaussian.cdf(x)
        }).collect(), p);
    }
}

#[cfg(test)]
mod bench {
    extern crate test;

    use super::super::Distribution;
    use super::Gaussian;

    #[bench]
    fn sample(bench: &mut test::Bencher) {
        let mut rng = ::std::rand::task_rng();
        let gaussian = Gaussian::new(0.0, 1.0);
        bench.iter(|| {
            test::black_box(gaussian.sample(&mut rng))
        });
    }
}
<commit_msg>Implemented the inverse CDF of Gaussian<commit_after>extern crate sfunc;

use std::rand::Rng;
use std::rand::distributions::{Normal, IndependentSample};

use super::Distribution;

/// A Gaussian distribution with a mean value `mu` and a standard deviation
/// `sigma`.
pub struct Gaussian {
    /// The mean value.
    pub mu: f64,
    /// The standard deviation.
    pub sigma: f64,

    normal: Normal,
}

impl Gaussian {
    /// Creates a Gaussian distribution with the mean value `mu` and the
    /// standard deviation `sigma`.
    #[inline]
    pub fn new(mu: f64, sigma: f64) -> Gaussian {
        Gaussian {
            mu: mu,
            sigma: sigma,
            normal: Normal::new(0.0, 1.0),
        }
    }
}

impl Distribution<f64> for Gaussian {
    fn cdf(&self, x: f64) -> f64 {
        use self::sfunc::erf;
        (1.0 + erf((x - self.mu) / (self.sigma * Float::sqrt2()))) / 2.0
    }

    fn inv_cdf(&self, p: f64) -> f64 {
        use self::sfunc::{log, sqrt};

        const CONST1: f64 = 0.180625;
        const CONST2: f64 = 1.6;
        const SPLIT1: f64 = 0.425;
        const SPLIT2: f64 = 5.0;

        const A: [f64, ..8] = [
            3.3871328727963666080,
            1.3314166789178437745e+2,
            1.9715909503065514427e+3,
            1.3731693765509461125e+4,
            4.5921953931549871457e+4,
            6.7265770927008700853e+4,
            3.3430575583588128105e+4,
            2.5090809287301226727e+3,
        ];
        const B: [f64, ..8] = [
            1.0,
            4.2313330701600911252e+1,
            6.8718700749205790830e+2,
            5.3941960214247511077e+3,
            2.1213794301586595867e+4,
            3.9307895800092710610e+4,
            2.8729085735721942674e+4,
            5.2264952788528545610e+3,
        ];
        const C: [f64, ..8] = [
            1.42343711074968357734,
            4.63033784615654529590,
            5.76949722146069140550,
            3.64784832476320460504,
            1.27045825245236838258,
            2.41780725177450611770e-1,
            2.27238449892691845833e-2,
            7.74545014278341407640e-4,
        ];
        const D: [f64, ..8] = [
            1.0,
            2.05319162663775882187,
            1.67638483018380384940,
            6.89767334985100004550e-1,
            1.48103976427480074590e-1,
            1.51986665636164571966e-2,
            5.47593808499534494600e-4,
            1.05075007164441684324e-9,
        ];
        const E: [f64, ..8] = [
            6.65790464350110377720,
            5.46378491116411436990,
            1.78482653991729133580,
            2.96560571828504891230e-1,
            2.65321895265761230930e-2,
            1.24266094738807843860e-3,
            2.71155556874348757815e-5,
            2.01033439929228813265e-7,
        ];
        const F: [f64, ..8] = [
            1.0,
            5.99832206555887937690e-1,
            1.36929880922735805310e-1,
            1.48753612908506148525e-2,
            7.86869131145613259100e-4,
            1.84631831751005468180e-5,
            1.42151175831644588870e-7,
            2.04426310338993978564e-15,
        ];

        #[inline]
        fn poly(c: &[f64], x: f64) -> f64 {
            c.iter().rev().fold(0.0, |y, &c| y * x + c)
        }

        if p <= 0.0 {
            return Float::neg_infinity();
        }
        if 1.0 <= p {
            return Float::infinity();
        }

        let q = p - 0.5;

        if ::std::num::abs(q) <= SPLIT1 {
            let x = CONST1 - q * q;
            return self.mu + self.sigma * q * poly(&A, x) / poly(&B, x);
        }

        let mut x = if q < 0.0 { p } else { 1.0 - p };

        x = sqrt(-log(x));

        if x <= SPLIT2 {
            x -= CONST2;
            x = poly(&C, x) / poly(&D, x);
        } else {
            x -= SPLIT2;
            x = poly(&E, x) / poly(&F, x);
        }

        if q < 0.0 {
            x = -x;
        }

        self.mu + self.sigma * x
    }

    #[inline]
    fn sample<R: Rng>(&self, rng: &mut R) -> f64 {
        self.normal.ind_sample(rng)
    }
}

#[cfg(test)]
mod test {
    use super::super::Distribution;
    use super::Gaussian;

    macro_rules! assert_almost_eq(
        ($x:expr, $y:expr) => ({
            let e: f64 = ::std::f64::EPSILON.sqrt();
            let x: Vec<f64> = $x;
            let y: Vec<f64> = $y;
            for i in range(0u, x.len()) {
                if x[i].is_finite() {
                    assert!(::std::num::abs(x[i] - y[i]) < e,
                            "expected {:e} ~ {:e}", x[i], y[i]);
                } else {
                    assert_eq!(x[i], y[i]);
                }
            }
        });
    )

    #[test]
    fn cdf() {
        let gaussian = Gaussian::new(1.0, 2.0);

        let x = vec![-4.0, -3.5, -3.0, -2.5, -2.0, -1.5, -1.0, -0.5, 0.0, 0.5,
                     1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0];
        let p = vec![6.209665325776139e-03, 1.222447265504470e-02,
                     2.275013194817922e-02, 4.005915686381709e-02,
                     6.680720126885809e-02, 1.056497736668553e-01,
                     1.586552539314571e-01, 2.266273523768682e-01,
                     3.085375387259869e-01, 4.012936743170763e-01,
                     5.000000000000000e-01, 5.987063256829237e-01,
                     6.914624612740131e-01, 7.733726476231317e-01,
                     8.413447460685429e-01, 8.943502263331446e-01,
                     9.331927987311419e-01];

        assert_almost_eq!(x.iter().map(|&x| {
            gaussian.cdf(x)
        }).collect(), p);
    }

    #[test]
    fn inv_cdf() {
        let gaussian = Gaussian::new(-1.0, 0.25);

        let p = vec![0.00, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40,
                     0.45, 0.50, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85,
                     0.90, 0.95, 1.00];
        let x = vec![Float::neg_infinity(), -1.411213406737868e+00,
                     -1.320387891386150e+00, -1.259108347373447e+00,
                     -1.210405308393228e+00, -1.168622437549020e+00,
                     -1.131100128177010e+00, -1.096330116601892e+00,
                     -1.063336775783950e+00, -1.031415336713768e+00,
                     -1.000000000000000e+00, -9.685846632862315e-01,
                     -9.366632242160501e-01, -9.036698833981082e-01,
                     -8.688998718229899e-01, -8.313775624509796e-01,
                     -7.895946916067714e-01, -7.408916526265525e-01,
                     -6.796121086138498e-01, -5.887865932621319e-01,
                     Float::infinity()];

        assert_almost_eq!(p.iter().map(|&p| {
            gaussian.inv_cdf(p)
        }).collect(), x);
    }
}

#[cfg(test)]
mod bench {
    extern crate test;

    use super::super::Distribution;
    use super::Gaussian;

    #[bench]
    fn sample(bench: &mut test::Bencher) {
        let mut rng = ::std::rand::task_rng();
        let gaussian = Gaussian::new(0.0, 1.0);
        bench.iter(|| {
            test::black_box(gaussian.sample(&mut rng))
        });
    }
}
<|endoftext|>


<commit_before>use std::str::SplitWhitespace;
use std::fs::File;
use std::io::{Cursor, BufReader};

use agb_core::gameboy::Gameboy;
use agb_core::gameboy::assembly;
use agb_core::gameboy::debugger::{Breakpoint, DebuggerInterface, AccessType};
use super::{parse_u16, parse_u8, parse_usize};

use image;

pub fn debug(input: String, gameboy: &mut Gameboy, paused: &mut bool, state: &mut Option<Vec<u8>>) {
	let mut command = input.trim().split_whitespace();
	if let Some(next) = command.next() {
		match next {
			"breakpoint" => breakpoint(&mut command, gameboy),
			"step" => {
				gameboy.debug_step();
			},
			"continue" | "resume" => {
				*paused = false;
				gameboy.debug_step();  //if paused on a breakpoint, need to step over it
			},
			"pause" | "break" => {
				*paused = true;
			},
			"registers" => {
				println!("{:#?}", gameboy.get_registers());
			},
			"memory" => memory(&mut command, gameboy),
			"assembly" => assembly(gameboy),
			"dump_tiles" => dump_tiles(&mut command, gameboy),
			"dump_bg" => dump_bg(&mut command, gameboy),
			"reset" => {
				gameboy.reset();
			},
			"save" => {
				match gameboy.save_state() {
					Ok(s) => {
						*state = Some(s);
					},
					Err(e) => {
						println!("Error saving state: {}", e);
					}
				};
			},
			"load" => {
				if let Some(state) = state {
					let cursor = Cursor::new(&state);
					let mut reader = BufReader::new(cursor);
					if let Err(e) = gameboy.load_state(&mut reader) {
						println!("Error loading save state: {}", e);
					}
				}
				else {
					println!("no state currently saved");
				}
			},
			"help" => {
				println!("available commands are:\n\
				breakpoint add <type> <address>  - add a breakpoint at <address>, valid types are {{ execute, jump, read, write }}\n\
				breakpoint list           - get a list of breakpoints\n\
				breakpoint remove <index> - remove the breakpoint with index <index> (from list)\n\
				step                      - step forward 1 instruction\n\
				continue | resume         - continue execution\n\
				pause | break             - pause execution\n\
				registers                 - print out the contents of the registers\n\
				memory read <address>     - read the byte at <address>\n\
				memory write <address> <value> - write <value> at <address>\n\
				assembly                  - print out the dissasembly of the current pc\n\
				reset                     - reset the gameboy (keeps breakpoints and any rom/ram patches)\n\
				dump_tiles <filename>     - dumps the tiles in vram as an image named <filename>.png (or tiles.png if no filename is provided)\n\
				dump_bg <filename>        - dumps the background as an image to <filename>.png (or bg.png if no filename is provided)
				quit | exit               - terminate the emulator");
			},
			_ => { println!("invalid command (try typing 'help')"); }
		};
	}
}

pub fn breakpoint(command: &mut SplitWhitespace, gameboy: &mut Gameboy) {
	match command.next() {
		Some(subcommand) => {
			match subcommand {
				"add" => {
					let access_type: Option<AccessType> = match command.next() {
						Some("execute") => Some(AccessType::Execute),
						Some("read") => Some(AccessType::Read),
						Some("write") => Some(AccessType::Write),
						Some("jump") => Some(AccessType::Jump),
						Some(_) => {
							println!("invalid access type");
							None
						},
						None => {
							println!("missing access type");
							None
						}
					};
					let address: Option<u16> = match command.next() {
						Some(address_literal) => {
							match parse_u16(address_literal) {
								//TODO: other access types
								Ok(address) => Some(address),
								Err(_) => {
									println!("invalid address");
									None
								},
							}
						},
						None => {
							println!("missing address");
							None
						},
					};
					if let (Some(access_type), Some(address)) = (access_type, address) {
						let breakpoint = Breakpoint::new(address, access_type);
						gameboy.add_breakpoint(breakpoint);
					}

				},
				"list" => {
					let breakpoints = gameboy.get_breakpoints();
					if breakpoints.len() == 0 {
						println!("no breakpoints");
					}
					for (number, breakpoint) in breakpoints.iter().enumerate() {
						println!("{}: address: 0x{:x}, access_type: {:?}", number, breakpoint.address, breakpoint.access_type);
					}
				},
				"remove" => {
					match command.next() {
						Some(parameter) => {
							match parse_usize(parameter) {
								Ok(index) => {
									match gameboy.remove_breakpoint(index) {
										Ok(breakpoint) => {
											println!("removed breakpoint {{ address: 0x{:x}, access_type: {:?} }}", breakpoint.address, breakpoint.access_type);
										},
										Err(_) => {
											println!("the breakpoint you are trying to remove doesn't exist");
										}
									}
								},
								Err(_) => {
									println!("invalid argument: must be an integer");
								}
							};
						},
						None => {
							println!("invalid usage: specify which interrupt to remove (use the number you got from breakpoint list)");
						}
					}
				}
				_ => { println!("invalid usage: subcommands of breakpoint are {{ add, list, remove }}"); },
			};
		},
		None => println!("invalid usage: missing subcommand"),
	};
}

pub fn memory(command: &mut SplitWhitespace, gameboy: &mut Gameboy) {
	match command.next() {
		Some(subcommand) => {
			match subcommand {
				"read" =>  {
					match command.next() {
						Some(address_literal) => {
							match parse_u16(address_literal) {
								Ok(address) => {
									let value = gameboy.read_memory(address);
									println!("[0x{:x}] = 0x{:x}", address, value);
								}
								Err(_) => {
									println!("invalid address");
								}
							}
						}
						None => {
							println!("you need to specify an address");
						}
					};
				},
				"write" => {
					match command.next() {
						Some(address_literal) => {
							match command.next() {
								Some(value_literal) => {
									let address = parse_u16(address_literal);
									let value = parse_u8(value_literal);
									match (address, value) {
										(Ok(address), Ok(value)) => {
											gameboy.write_memory(address, value);
										},
										(address, value) => {
											if address.is_err() { println!("invalid address"); }
											if value.is_err() { println!("invalid value"); }
										}
									};
								},
								None => {
									println!("invalid usage: specify a value to write")
								}
							}
						}
						None => {
							println!("invalid usage: specify an address");
						}
					};
				},
				_ => {},
			};
		},
		None => {}
	};
}

pub fn dump_tiles(command: &mut SplitWhitespace, gameboy: &mut Gameboy) {
	let path = match command.next() {
		Some(arg) => {
			let mut path = String::from(arg);
			if !path.ends_with(".png") {
				path.push_str(".png");
			}
			path
		},
		None => String::from("tiles.png")
	};
	let raw = gameboy.dump_tiles();
	let file = File::create(path);
	match file {
		Ok(file) => {
			//Convert the u32 pixels into rgba structs for the image library
			let mut buffer: Vec<u8> = Vec::with_capacity(raw.data.len() * 4);
			for val in raw.data.iter() {
				buffer.push((val >> 24) as u8);
				buffer.push((val >> 16) as u8);
				buffer.push((val >> 8) as u8);
				buffer.push((val & 0xFF) as u8);
			}
			let encoder = image::png::PNGEncoder::new(file);
			match encoder.encode(buffer.as_slice(), raw.width as u32, raw.height as u32, image::ColorType::RGBA(8)) {
				Ok(_) => {},
				Err(_) => println!("failed to save tile data to disk")
			};
		},
		Err(e) => println!("{}", e),
	};
}

pub fn dump_bg(command: &mut SplitWhitespace, gameboy: &mut Gameboy) {
	let path = match command.next() {
		Some(arg) => {
			let mut path = String::from(arg);
			if !path.ends_with(".png") {
				path.push_str(".png");
			}
			path
		},
		None => String::from("bg.png"),
	};
	let raw = gameboy.dump_bg();
	let file = File::create(path);
	match file {
		Ok(file) => {
			//Convert the u32 pixels into rgba structs for the image library
			let mut buffer: Vec<u8> = Vec::with_capacity(raw.data.len() * 4);
			for val in raw.data.iter() {
				buffer.push((val >> 24) as u8);
				buffer.push((val >> 16) as u8);
				buffer.push((val >> 8) as u8);
				buffer.push((val & 0xFF) as u8);
			}
			let encoder = image::png::PNGEncoder::new(file);
			match encoder.encode(buffer.as_slice(), raw.width as u32, raw.height as u32, image::ColorType::RGBA(8)) {
				Ok(_) => {},
				Err(_) => println!("failed to save tile data to disk")
			};
		},
		Err(e) => println!("{}", e),
	};
}

pub fn assembly(gameboy: &mut Gameboy) {
	use std::cmp::min;

	let pc = gameboy.get_registers().pc;
	let start:usize = pc as usize;
	let end = min(start + 5, 0xFFFF);
	let data = gameboy.read_range(start as u16, end as u16).unwrap(); //largest opcode is 3 bytes
	let after = gameboy.get_assembly(&data);

	let mut offset: usize = 0;
	for (line,op) in after.iter().enumerate() {
		match line {
			0 => { println!("{:04X}: {} <---", (offset + start) as u16, op); },
			_ => { println!("{:04X}: {}", (offset + start) as u16, op); },
		};
		offset += assembly::INSTRUCTION_LENGTH[data[offset] as usize];
	}
}
<commit_msg>Added ability to save/load states to/from files.<commit_after>use std::str::SplitWhitespace;
use std::fs::File;

use agb_core::gameboy::Gameboy;
use agb_core::gameboy::assembly;
use agb_core::gameboy::debugger::{Breakpoint, DebuggerInterface, AccessType};
use super::{parse_u16, parse_u8, parse_usize};

use image;

pub fn debug(input: String, gameboy: &mut Gameboy, paused: &mut bool, state: &mut Option<Vec<u8>>) {
	let mut command = input.trim().split_whitespace();
	if let Some(next) = command.next() {
		match next {
			"breakpoint" => breakpoint(&mut command, gameboy),
			"step" => {
				gameboy.debug_step();
			},
			"continue" | "resume" => {
				*paused = false;
				gameboy.debug_step();  //if paused on a breakpoint, need to step over it
			},
			"pause" | "break" => {
				*paused = true;
			},
			"registers" => {
				println!("{:#?}", gameboy.get_registers());
			},
			"memory" => memory(&mut command, gameboy),
			"assembly" => assembly(gameboy),
			"dump_tiles" => dump_tiles(&mut command, gameboy),
			"dump_bg" => dump_bg(&mut command, gameboy),
			"reset" => {
				gameboy.reset();
			},
			"save" => {
				save_state(gameboy, &mut command, state);
			},
			"load" => {
				load_state(gameboy, &mut command, state);
			},
			"help" => {
				println!("available commands are:\n\
				save <FILE>               - save the emulator state, if a filename is specified the state will be saved to the disk\n\
				load <FILE>               - load a saved emulator state, if no filename is specified, the most recently saved state is loaded\n\
				breakpoint add <type> <address>  - add a breakpoint at <address>, valid types are {{ execute, jump, read, write }}\n\
				breakpoint list           - get a list of breakpoints\n\
				breakpoint remove <index> - remove the breakpoint with index <index> (from list)\n\
				step                      - step forward 1 instruction\n\
				continue | resume         - continue execution\n\
				pause | break             - pause execution\n\
				registers                 - print out the contents of the registers\n\
				memory read <address>     - read the byte at <address>\n\
				memory write <address> <value> - write <value> at <address>\n\
				assembly                  - print out the dissasembly of the current pc\n\
				reset                     - reset the gameboy (keeps breakpoints and any rom/ram patches)\n\
				dump_tiles <filename>     - dumps the tiles in vram as an image named <filename>.png (or tiles.png if no filename is provided)\n\
				dump_bg <filename>        - dumps the background as an image to <filename>.png (or bg.png if no filename is provided)\n\
				quit | exit               - terminate the emulator");
			},
			_ => { println!("invalid command (try typing 'help')"); }
		};
	}
}

pub fn breakpoint(command: &mut SplitWhitespace, gameboy: &mut Gameboy) {
	match command.next() {
		Some(subcommand) => {
			match subcommand {
				"add" => {
					let access_type: Option<AccessType> = match command.next() {
						Some("execute") => Some(AccessType::Execute),
						Some("read") => Some(AccessType::Read),
						Some("write") => Some(AccessType::Write),
						Some("jump") => Some(AccessType::Jump),
						Some(_) => {
							println!("invalid access type");
							None
						},
						None => {
							println!("missing access type");
							None
						}
					};
					let address: Option<u16> = match command.next() {
						Some(address_literal) => {
							match parse_u16(address_literal) {
								//TODO: other access types
								Ok(address) => Some(address),
								Err(_) => {
									println!("invalid address");
									None
								},
							}
						},
						None => {
							println!("missing address");
							None
						},
					};
					if let (Some(access_type), Some(address)) = (access_type, address) {
						let breakpoint = Breakpoint::new(address, access_type);
						gameboy.add_breakpoint(breakpoint);
					}

				},
				"list" => {
					let breakpoints = gameboy.get_breakpoints();
					if breakpoints.len() == 0 {
						println!("no breakpoints");
					}
					for (number, breakpoint) in breakpoints.iter().enumerate() {
						println!("{}: address: 0x{:x}, access_type: {:?}", number, breakpoint.address, breakpoint.access_type);
					}
				},
				"remove" => {
					match command.next() {
						Some(parameter) => {
							match parse_usize(parameter) {
								Ok(index) => {
									match gameboy.remove_breakpoint(index) {
										Ok(breakpoint) => {
											println!("removed breakpoint {{ address: 0x{:x}, access_type: {:?} }}", breakpoint.address, breakpoint.access_type);
										},
										Err(_) => {
											println!("the breakpoint you are trying to remove doesn't exist");
										}
									}
								},
								Err(_) => {
									println!("invalid argument: must be an integer");
								}
							};
						},
						None => {
							println!("invalid usage: specify which interrupt to remove (use the number you got from breakpoint list)");
						}
					}
				}
				_ => { println!("invalid usage: subcommands of breakpoint are {{ add, list, remove }}"); },
			};
		},
		None => println!("invalid usage: missing subcommand"),
	};
}

pub fn memory(command: &mut SplitWhitespace, gameboy: &mut Gameboy) {
	match command.next() {
		Some(subcommand) => {
			match subcommand {
				"read" =>  {
					match command.next() {
						Some(address_literal) => {
							match parse_u16(address_literal) {
								Ok(address) => {
									let value = gameboy.read_memory(address);
									println!("[0x{:x}] = 0x{:x}", address, value);
								}
								Err(_) => {
									println!("invalid address");
								}
							}
						}
						None => {
							println!("you need to specify an address");
						}
					};
				},
				"write" => {
					match command.next() {
						Some(address_literal) => {
							match command.next() {
								Some(value_literal) => {
									let address = parse_u16(address_literal);
									let value = parse_u8(value_literal);
									match (address, value) {
										(Ok(address), Ok(value)) => {
											gameboy.write_memory(address, value);
										},
										(address, value) => {
											if address.is_err() { println!("invalid address"); }
											if value.is_err() { println!("invalid value"); }
										}
									};
								},
								None => {
									println!("invalid usage: specify a value to write")
								}
							}
						}
						None => {
							println!("invalid usage: specify an address");
						}
					};
				},
				_ => {},
			};
		},
		None => {}
	};
}

pub fn dump_tiles(command: &mut SplitWhitespace, gameboy: &mut Gameboy) {
	let path = match command.next() {
		Some(arg) => {
			let mut path = String::from(arg);
			if !path.ends_with(".png") {
				path.push_str(".png");
			}
			path
		},
		None => String::from("tiles.png")
	};
	let raw = gameboy.dump_tiles();
	let file = File::create(path);
	match file {
		Ok(file) => {
			//Convert the u32 pixels into rgba structs for the image library
			let mut buffer: Vec<u8> = Vec::with_capacity(raw.data.len() * 4);
			for val in raw.data.iter() {
				buffer.push((val >> 24) as u8);
				buffer.push((val >> 16) as u8);
				buffer.push((val >> 8) as u8);
				buffer.push((val & 0xFF) as u8);
			}
			let encoder = image::png::PNGEncoder::new(file);
			match encoder.encode(buffer.as_slice(), raw.width as u32, raw.height as u32, image::ColorType::RGBA(8)) {
				Ok(_) => {},
				Err(_) => println!("failed to save tile data to disk")
			};
		},
		Err(e) => println!("{}", e),
	};
}

pub fn dump_bg(command: &mut SplitWhitespace, gameboy: &mut Gameboy) {
	let path = match command.next() {
		Some(arg) => {
			let mut path = String::from(arg);
			if !path.ends_with(".png") {
				path.push_str(".png");
			}
			path
		},
		None => String::from("bg.png"),
	};
	let raw = gameboy.dump_bg();
	let file = File::create(path);
	match file {
		Ok(file) => {
			//Convert the u32 pixels into rgba structs for the image library
			let mut buffer: Vec<u8> = Vec::with_capacity(raw.data.len() * 4);
			for val in raw.data.iter() {
				buffer.push((val >> 24) as u8);
				buffer.push((val >> 16) as u8);
				buffer.push((val >> 8) as u8);
				buffer.push((val & 0xFF) as u8);
			}
			let encoder = image::png::PNGEncoder::new(file);
			match encoder.encode(buffer.as_slice(), raw.width as u32, raw.height as u32, image::ColorType::RGBA(8)) {
				Ok(_) => {},
				Err(_) => println!("failed to save tile data to disk")
			};
		},
		Err(e) => println!("{}", e),
	};
}

pub fn assembly(gameboy: &mut Gameboy) {
	use std::cmp::min;

	let pc = gameboy.get_registers().pc;
	let start:usize = pc as usize;
	let end = min(start + 5, 0xFFFF);
	let data = gameboy.read_range(start as u16, end as u16).unwrap(); //largest opcode is 3 bytes
	let after = gameboy.get_assembly(&data);

	let mut offset: usize = 0;
	for (line,op) in after.iter().enumerate() {
		match line {
			0 => { println!("{:04X}: {} <---", (offset + start) as u16, op); },
			_ => { println!("{:04X}: {}", (offset + start) as u16, op); },
		};
		offset += assembly::INSTRUCTION_LENGTH[data[offset] as usize];
	}
}

fn save_state(gameboy: &mut Gameboy, command: &mut SplitWhitespace, state: &mut Option<Vec<u8>>) {
	use std::fs::File;
	use std::io::Write;

	match gameboy.save_state() {
		Ok(s) => {
			let filename: String = command.collect();
			if filename.len() > 0 {
				// save state to file
				if let Err(e) = File::create(filename).and_then(|mut file| file.write(&s[..])) {
					println!("Failed to save state to disk: {}", e);
				}
			}

			*state = Some(s);
		},
		Err(e) => {
			println!("Error saving state: {}", e);
		}
	};
}

fn load_state(gameboy: &mut Gameboy, command: &mut SplitWhitespace, state: &mut Option<Vec<u8>>) {
	use std::fs::File;
	use std::io::{Cursor, BufReader};

	// TODO: verify the rom that is loaded matches the rom that the state was saved with

	let filename: String = command.collect();
	if filename.len() > 0 {
		// load state from file
		let file = match File::open(filename) {
			Ok(f) => f,
			Err(e) => {
				println!("Failed to read state from file: {}", e);
				return;
			}
		};
		if let Err(e) = gameboy.load_state(BufReader::new(file)) {
			println!("Failed to load state: {}", e);
		}
	}
	else if let Some(s) = state {
		// load last saved state
		let mut cursor = Cursor::new(s);
		let mut reader = BufReader::new(cursor);
		if let Err(e) = gameboy.load_state(reader) {
			println!("Failed to load state: {}", e);
		}
	}
	else {
		// there is no state to load
		println!("no saved state to load");
	}
}
<|endoftext|>


<commit_before>//! Provide syntax highlighting via [Syntect].
//!
//! [Syntect]: https://docs.rs/syntect/1.0.0/syntect/

// Standard library
use std::default::Default;
use std::collections::HashMap;
use std::str;

// Third party
use quick_xml::{XmlReader, XmlWriter, Element, Event};
use syntect::html::highlighted_snippet_for_string;
use syntect::highlighting::Theme;
use syntect::parsing::{SyntaxDefinition, SyntaxSet};

/// A `Language` is a `String` representing a code highlighting language.
type Language = String;

/// Define XML parsing states of interest for highlighting.
///
/// We use these in conjunction with `ParseEvent`s to drive a state machine
/// which tracks whether a stream of `quick_xml::Event`s indicates the presence
/// of a code block to be highlighted.
#[derive(Clone, Debug, PartialEq)]
enum ParseState {
    /// Not in a code block at all.
    NotInBlock,
    /// Possibly the start of a highlight block, with the language as payload.
    MaybeStartBlock(Language),
    /// The start of the code block has been confirmed; highlight next text node.
    WillStartCodeBlock(Language),
    /// In a code block; use the content for highlighting.
    InCodeBlock(Language),
}

/// Define XML parsing events of interest for highlighting.
///
/// We don't care at all about any other items for this specific scenario; we
/// use these in conjunction with the `ParseState` to drive the state  machine
/// described there.
#[derive(Debug)]
enum ParseEvent {
    // Starting a `<pre>`; optionally specifying a language.
    StartPre(Option<Language>),
    // Starting a `<code>`.
    StartCode,
    // Ending a `<code>`.
    EndCode,
    Text,
    Whitespace,
    Other,
}

impl ParseState {
    /// Get the next `ParseState` given current `ParseState` and a `ParseEvent`.
    fn next(self, event: ParseEvent) -> ParseState {
        use self::ParseState::*;
        use self::ParseEvent::*;

        match (self, event) {
            (NotInBlock, StartPre(Some(language))) => MaybeStartBlock(language),
            (MaybeStartBlock(language), Whitespace) => MaybeStartBlock(language),
            (MaybeStartBlock(language), StartCode) => WillStartCodeBlock(language),
            (WillStartCodeBlock(language), Text) |
            (WillStartCodeBlock(language), Whitespace) => InCodeBlock(language),
            (InCodeBlock(_), EndCode) => NotInBlock,
            (_, _) => NotInBlock,
        }
    }
}

impl Default for ParseState {
    fn default() -> ParseState {
        ParseState::NotInBlock
    }
}

impl<'e> From<&'e Event> for ParseEvent {
    fn from(event: &'e Event) -> ParseEvent {
        // TODO @1.15: remove `'static`.
        const PRE: &'static [u8] = b"pre";
        const CODE: &'static [u8] = b"code";
        const CLASS: &'static [u8] = b"class";
        const WHITE_SPACE: &'static [u8] = b"";

        match *event {
            Event::Start(ref element) => {
                match element.name() {
                    PRE => {
                        let maybe_class_attr = element.attributes()
                            .map(|attr| attr.unwrap())
                            .filter(|&(attr, _value)| attr == CLASS)
                            .next();

                        if let Some((_attr, value)) = maybe_class_attr {
                            match str::from_utf8(value) {
                                Ok(lang) => ParseEvent::StartPre(Some(lang.to_string())),
                                Err(_) => ParseEvent::StartPre(None),
                            }
                        } else {
                            ParseEvent::StartPre(None)
                        }
                    }
                    CODE => ParseEvent::StartCode,
                    _ => ParseEvent::Other,
                }
            }
            Event::End(ref element) => {
                match element.name() {
                    CODE => ParseEvent::EndCode,
                    _ => ParseEvent::Other,
                }
            }
            Event::Text(ref element) => {
                match element.name() {
                    WHITE_SPACE => ParseEvent::Whitespace,
                    _ => ParseEvent::Text,
                }
            }
            _ => ParseEvent::Other,
        }
    }
}

struct Accumulator {
    writer: XmlWriter<Vec<u8>>,
    state: ParseState,
}

/// Highlight all code blocks in a block of HTML.
///
/// Assumes that the blocks to be highlighted are in the following basic format:
///
/// ```html
/// <pre class="rust">
///   <code>
///     pub fn syntax_highlight(html_string: String) -> String {
///         // implementation details
///     }
///   </code>
/// </pre>
/// ```
///
/// The `class` attribute value from the `pre` tag defines the language used to
/// highlight the code.
///
/// Note that any `html_string` will do; if it cannot be parsed as XML, it will
/// simply be returned unchanged; and if there are no code blocks to highlight,
/// it will also be returned unchanged.
pub fn syntax_highlight(html_string: String, theme: &Theme) -> String {
    let original_string = html_string.clone();
    let reader = XmlReader::from(html_string.as_str());

    let ss = SyntaxSet::load_defaults_nonewlines();
    let mut syntax_definitions = HashMap::<Language, SyntaxDefinition>::new();

    let accumulator = Accumulator {
        writer: XmlWriter::new(Vec::<u8>::new()),
        state: ParseState::default(),
    };

    let final_state = reader.fold(accumulator, |mut acc, event| {
        let event = match event {
            Ok(event) => event,
            Err(_) => {
                return acc;
            }
        };

        let parse_event = ParseEvent::from(&event);
        acc.state = ParseState::next(acc.state, parse_event);

        let language = match acc.state.clone() {
            ParseState::InCodeBlock(language) => language,
            _ => {
                assert!(acc.writer.write(event.clone()).is_ok());
                return acc;
            }
        };

        let unescaped_content = match event.element().unescaped_content() {
            Ok(content) => content.into_owned(),
            Err(_) => {
                assert!(acc.writer.write(event.clone()).is_ok());
                return acc;
            }
        };

        let content_to_highlight = match str::from_utf8(&unescaped_content) {
            Ok(utf8_str) => utf8_str,
            Err(_) => {
                assert!(acc.writer.write(event.clone()).is_ok());
                return acc;
            }
        };

        let syntax_key = language.clone();
        let syntax_definition = syntax_definitions.entry(syntax_key).or_insert({
            match ss.find_syntax_by_token(&language) {
                Some(valid_syntax) => valid_syntax.clone(),
                None => {
                    assert!(acc.writer.write(event.clone()).is_ok());
                    return acc;
                }
            }
        });

        let highlighted =
            highlighted_snippet_for_string(content_to_highlight, syntax_definition, &theme);

        let text = Element::new(highlighted);
        assert!(acc.writer.write(Event::Text(text)).is_ok());

        acc
    });

    String::from_utf8(final_state.writer.into_inner()).unwrap_or(original_string)
}

#[cfg(test)]
mod tests {

    #[test]
    fn test_parse_state() {
        use super::ParseState;
        use super::ParseEvent;

        let lang = "rust";

        assert_eq!(ParseState::NotInBlock.next(ParseEvent::StartPre(Some(lang.to_string()))),
                   ParseState::MaybeStartBlock(lang.to_string()));

        assert_eq!(ParseState::NotInBlock.next(ParseEvent::EndCode),
                   ParseState::NotInBlock);

        assert_eq!(ParseState::NotInBlock.next(ParseEvent::Other),
                   ParseState::NotInBlock);

        assert_eq!(ParseState::NotInBlock.next(ParseEvent::StartCode),
                   ParseState::NotInBlock);

        assert_eq!(ParseState::MaybeStartBlock(lang.to_string()).next(ParseEvent::StartCode),
                   ParseState::WillStartCodeBlock(lang.to_string()));

        assert_eq!(ParseState::MaybeStartBlock(lang.to_string()).next(ParseEvent::Text),
                   ParseState::NotInBlock);

        assert_eq!(ParseState::MaybeStartBlock(lang.to_string()).next(ParseEvent::EndCode),
                   ParseState::NotInBlock);

        assert_eq!(ParseState::MaybeStartBlock(lang.to_string())
                       .next(ParseEvent::StartPre(Some(lang.to_string()))),
                   ParseState::NotInBlock);

        assert_eq!(ParseState::MaybeStartBlock(lang.to_string()).next(ParseEvent::Other),
                   ParseState::NotInBlock);

        assert_eq!(ParseState::WillStartCodeBlock(lang.to_string()).next(ParseEvent::Text),
                   ParseState::InCodeBlock(lang.to_string()));
    }
}
<commit_msg>Don't clone the original HTML string in syntax_highlight<commit_after>//! Provide syntax highlighting via [Syntect].
//!
//! [Syntect]: https://docs.rs/syntect/1.0.0/syntect/

// Standard library
use std::default::Default;
use std::collections::HashMap;
use std::str;

// Third party
use quick_xml::{XmlReader, XmlWriter, Element, Event};
use syntect::html::highlighted_snippet_for_string;
use syntect::highlighting::Theme;
use syntect::parsing::{SyntaxDefinition, SyntaxSet};

/// A `Language` is a `String` representing a code highlighting language.
type Language = String;

/// Define XML parsing states of interest for highlighting.
///
/// We use these in conjunction with `ParseEvent`s to drive a state machine
/// which tracks whether a stream of `quick_xml::Event`s indicates the presence
/// of a code block to be highlighted.
#[derive(Clone, Debug, PartialEq)]
enum ParseState {
    /// Not in a code block at all.
    NotInBlock,
    /// Possibly the start of a highlight block, with the language as payload.
    MaybeStartBlock(Language),
    /// The start of the code block has been confirmed; highlight next text node.
    WillStartCodeBlock(Language),
    /// In a code block; use the content for highlighting.
    InCodeBlock(Language),
}

/// Define XML parsing events of interest for highlighting.
///
/// We don't care at all about any other items for this specific scenario; we
/// use these in conjunction with the `ParseState` to drive the state  machine
/// described there.
#[derive(Debug)]
enum ParseEvent {
    // Starting a `<pre>`; optionally specifying a language.
    StartPre(Option<Language>),
    // Starting a `<code>`.
    StartCode,
    // Ending a `<code>`.
    EndCode,
    Text,
    Whitespace,
    Other,
}

impl ParseState {
    /// Get the next `ParseState` given current `ParseState` and a `ParseEvent`.
    fn next(self, event: ParseEvent) -> ParseState {
        use self::ParseState::*;
        use self::ParseEvent::*;

        match (self, event) {
            (NotInBlock, StartPre(Some(language))) => MaybeStartBlock(language),
            (MaybeStartBlock(language), Whitespace) => MaybeStartBlock(language),
            (MaybeStartBlock(language), StartCode) => WillStartCodeBlock(language),
            (WillStartCodeBlock(language), Text) |
            (WillStartCodeBlock(language), Whitespace) => InCodeBlock(language),
            (InCodeBlock(_), EndCode) => NotInBlock,
            (_, _) => NotInBlock,
        }
    }
}

impl Default for ParseState {
    fn default() -> ParseState {
        ParseState::NotInBlock
    }
}

impl<'e> From<&'e Event> for ParseEvent {
    fn from(event: &'e Event) -> ParseEvent {
        // TODO @1.15: remove `'static`.
        const PRE: &'static [u8] = b"pre";
        const CODE: &'static [u8] = b"code";
        const CLASS: &'static [u8] = b"class";
        const WHITE_SPACE: &'static [u8] = b"";

        match *event {
            Event::Start(ref element) => {
                match element.name() {
                    PRE => {
                        let maybe_class_attr = element.attributes()
                            .map(|attr| attr.unwrap())
                            .filter(|&(attr, _value)| attr == CLASS)
                            .next();

                        if let Some((_attr, value)) = maybe_class_attr {
                            match str::from_utf8(value) {
                                Ok(lang) => ParseEvent::StartPre(Some(lang.to_string())),
                                Err(_) => ParseEvent::StartPre(None),
                            }
                        } else {
                            ParseEvent::StartPre(None)
                        }
                    }
                    CODE => ParseEvent::StartCode,
                    _ => ParseEvent::Other,
                }
            }
            Event::End(ref element) => {
                match element.name() {
                    CODE => ParseEvent::EndCode,
                    _ => ParseEvent::Other,
                }
            }
            Event::Text(ref element) => {
                match element.name() {
                    WHITE_SPACE => ParseEvent::Whitespace,
                    _ => ParseEvent::Text,
                }
            }
            _ => ParseEvent::Other,
        }
    }
}

struct Accumulator {
    writer: XmlWriter<Vec<u8>>,
    state: ParseState,
}

/// Highlight all code blocks in a block of HTML.
///
/// Assumes that the blocks to be highlighted are in the following basic format:
///
/// ```html
/// <pre class="rust">
///   <code>
///     pub fn syntax_highlight(html_string: String) -> String {
///         // implementation details
///     }
///   </code>
/// </pre>
/// ```
///
/// The `class` attribute value from the `pre` tag defines the language used to
/// highlight the code.
///
/// Note that any `html_string` will do; if it cannot be parsed as XML, it will
/// simply be returned unchanged; and if there are no code blocks to highlight,
/// it will also be returned unchanged.
pub fn syntax_highlight(html_string: String, theme: &Theme) -> String {
    let ss = SyntaxSet::load_defaults_nonewlines();
    let mut syntax_definitions = HashMap::<Language, SyntaxDefinition>::new();

    let accumulator = Accumulator {
        writer: XmlWriter::new(Vec::<u8>::new()),
        state: ParseState::default(),
    };

    let final_state = XmlReader::from(html_string.as_str()).fold(accumulator, |mut acc, event| {
        let event = match event {
            Ok(event) => event,
            Err(_) => {
                return acc;
            }
        };

        let parse_event = ParseEvent::from(&event);
        acc.state = ParseState::next(acc.state, parse_event);

        let language = match acc.state.clone() {
            ParseState::InCodeBlock(language) => language,
            _ => {
                assert!(acc.writer.write(event.clone()).is_ok());
                return acc;
            }
        };

        let unescaped_content = match event.element().unescaped_content() {
            Ok(content) => content.into_owned(),
            Err(_) => {
                assert!(acc.writer.write(event.clone()).is_ok());
                return acc;
            }
        };

        let content_to_highlight = match str::from_utf8(&unescaped_content) {
            Ok(utf8_str) => utf8_str,
            Err(_) => {
                assert!(acc.writer.write(event.clone()).is_ok());
                return acc;
            }
        };

        let syntax_key = language.clone();
        let syntax_definition = syntax_definitions.entry(syntax_key).or_insert({
            match ss.find_syntax_by_token(&language) {
                Some(valid_syntax) => valid_syntax.clone(),
                None => {
                    assert!(acc.writer.write(event.clone()).is_ok());
                    return acc;
                }
            }
        });

        let highlighted =
            highlighted_snippet_for_string(content_to_highlight, syntax_definition, &theme);

        let text = Element::new(highlighted);
        assert!(acc.writer.write(Event::Text(text)).is_ok());

        acc
    });

    String::from_utf8(final_state.writer.into_inner()).unwrap_or(html_string)
}

#[cfg(test)]
mod tests {

    #[test]
    fn test_parse_state() {
        use super::ParseState;
        use super::ParseEvent;

        let lang = "rust";

        assert_eq!(ParseState::NotInBlock.next(ParseEvent::StartPre(Some(lang.to_string()))),
                   ParseState::MaybeStartBlock(lang.to_string()));

        assert_eq!(ParseState::NotInBlock.next(ParseEvent::EndCode),
                   ParseState::NotInBlock);

        assert_eq!(ParseState::NotInBlock.next(ParseEvent::Other),
                   ParseState::NotInBlock);

        assert_eq!(ParseState::NotInBlock.next(ParseEvent::StartCode),
                   ParseState::NotInBlock);

        assert_eq!(ParseState::MaybeStartBlock(lang.to_string()).next(ParseEvent::StartCode),
                   ParseState::WillStartCodeBlock(lang.to_string()));

        assert_eq!(ParseState::MaybeStartBlock(lang.to_string()).next(ParseEvent::Text),
                   ParseState::NotInBlock);

        assert_eq!(ParseState::MaybeStartBlock(lang.to_string()).next(ParseEvent::EndCode),
                   ParseState::NotInBlock);

        assert_eq!(ParseState::MaybeStartBlock(lang.to_string())
                       .next(ParseEvent::StartPre(Some(lang.to_string()))),
                   ParseState::NotInBlock);

        assert_eq!(ParseState::MaybeStartBlock(lang.to_string()).next(ParseEvent::Other),
                   ParseState::NotInBlock);

        assert_eq!(ParseState::WillStartCodeBlock(lang.to_string()).next(ParseEvent::Text),
                   ParseState::InCodeBlock(lang.to_string()));
    }
}
<|endoftext|>


<commit_before>extern crate serde_json;
extern crate tempdir;

use super::*;

use std::path::PathBuf;
use std::sync::{
    mpsc::{channel, Receiver, Sender},
    Arc,
};
use std::thread;

use task_maker_cache::Cache;
use task_maker_dag::CacheMode;
use task_maker_exec::{executors::LocalExecutor, ExecutorClient};
use task_maker_format::ui::{UIExecutionStatus, UIMessage};
use task_maker_format::{ioi, EvaluationConfig, EvaluationData, TaskFormat, UISender};
use task_maker_store::*;

use award::{AwardName, Score};
use evaluation::*;
use submission::Submission;

use content::TextVariant;
use evaluation::record::ValenceValue;
use evaluation::Event;
use feedback::valence::Valence;
use rusage::{MemoryUsage, TimeUsage};
use task_maker_format::ioi::Task;

pub fn run_evaluation(task_path: PathBuf, submission: Submission) -> Receiver<Event> {
    let (event_tx, event_rx) = channel();

    let store_path = task_path.join(".task-maker-files");
    let file_store =
        Arc::new(FileStore::new(store_path.join("store")).expect("Cannot create the file store"));

    let (tx, rx_remote) = channel();
    let (tx_remote, rx) = channel();

    let server_file_store = file_store.clone();
    thread::Builder::new()
        .name("Executor thread".into())
        .spawn(move || {
            let num_cores = num_cpus::get();
            let sandbox_path = store_path.join("sandboxes");
            let cache = Cache::new(store_path.join("cache")).expect("Cannot create the cache");
            let executor = LocalExecutor::new(server_file_store, num_cores, sandbox_path);

            executor.evaluate(tx_remote, rx_remote, cache).unwrap();
        })
        .expect("Failed to spawn the executor thread");

    thread::Builder::new()
        .name("Client thread".into())
        .spawn(move || {
            let dir = tempdir::TempDir::new("evaluation").unwrap();
            let solution_file = &submission.field_values[0].file;
            let solution_path = dir.path().join(&solution_file.name.0);
            std::fs::write(&solution_path, &solution_file.content)
                .expect("Unable to write solution file");
            println!("=={:?} start", solution_path);

            let (mut eval, receiver) = EvaluationData::new();

            let config = eval.dag.config_mut();

            let eval_config = EvaluationConfig {
                solution_filter: vec![],
                booklet_solutions: false,
                solution_paths: vec![solution_path.to_owned()],
                no_statement: true,
            };

            let task = ioi::Task::new(task_path, &eval_config).expect("Invalid task");

            // FIXME: is there a more idiomatic way to do it?
            let task2 = task.clone();

            thread::Builder::new()
                .name("Forwarder thread".into())
                .spawn(move || {
                    for m in receiver.iter() {
                        ui_message_to_events(&task2, m, &event_tx).expect("Failed to send event");
                    }
                })
                .expect("Failed to spawn the forwarder thread");

            config
                .keep_sandboxes(false)
                .dry_run(true)
                .cache_mode(CacheMode::Everything)
                .copy_exe(false)
                .extra_time(0.0);

            task.execute(&mut eval, &eval_config)
                .expect("Failed to build the DAG");

            trace!("The DAG is: {:#?}", eval.dag);

            let ui_sender = eval.sender.clone();
            ExecutorClient::evaluate(eval.dag, tx, &rx, file_store, move |status| {
                ui_sender.send(UIMessage::ServerStatus { status })
            })
            .expect("Client failed");
        })
        .expect("Failed to spawn the executor thread");
    event_rx
}

fn ui_message_to_events(
    task: &Task,
    ui_message: UIMessage,
    tx: &Sender<Event>,
) -> Result<(), failure::Error> {
    match ui_message {
        UIMessage::IOITestcaseScore {
            testcase,
            score,
            message,
            ..
        } => {
            tx.send(Event::Value(ValueEvent {
                key: record::Key(format!("testcase.{}.score", testcase)),
                value: record::Value::Score(record::ScoreValue {
                    score: Score(score as f64),
                }),
            }))?;
            tx.send(Event::Value(ValueEvent {
                key: record::Key(format!("testcase.{}.message", testcase)),
                value: record::Value::Message(record::TextValue {
                    text: vec![TextVariant {
                        value: message,
                        attributes: vec![],
                    }],
                }),
            }))?;
            tx.send(Event::Value(ValueEvent {
                key: record::Key(format!("testcase.{}.valence", testcase)),
                value: record::Value::Valence(ValenceValue {
                    valence: if score <= 0.0 {
                        Valence::Failure
                    } else if score >= 1.0 {
                        Valence::Success
                    } else {
                        Valence::Partial
                    },
                }),
            }))?;
        }
        UIMessage::IOISubtaskScore {
            subtask,
            score,
            normalized_score,
            ..
        } => {
            tx.send(Event::Value(ValueEvent {
                key: record::Key(format!("subtask.{}.score", subtask)),
                value: record::Value::Score(record::ScoreValue {
                    score: Score(score),
                }),
            }))?;
            tx.send(Event::Score(ScoreEvent {
                award_name: AwardName(format!("subtask.{}", subtask)),
                score: Score(score),
            }))?;
            tx.send(Event::Badge(BadgeEvent {
                award_name: AwardName(format!("subtask.{}", subtask)),
                badge: normalized_score > 0.0,
            }))?;
        }
        UIMessage::IOIEvaluation {
            testcase, status, ..
        } => {
            if let UIExecutionStatus::Done { result } = status {
                let time_usage = result.resources.cpu_time;
                tx.send(Event::Value(ValueEvent {
                    key: record::Key(format!("testcase.{}.time_usage", testcase)),
                    value: record::Value::TimeUsage(record::TimeUsageValue {
                        time_usage: TimeUsage(time_usage),
                    }),
                }))?;
                let warning_watermark = 0.25;
                tx.send(Event::Value(ValueEvent {
                    key: record::Key(format!("testcase.{}.time_usage_valence", testcase)),
                    value: record::Value::Valence(ValenceValue {
                        valence: if task
                            .time_limit
                            .map(|limit| time_usage < limit * warning_watermark)
                            .unwrap_or(true)
                        {
                            Valence::Success
                        } else if task
                            .time_limit
                            .map(|limit| time_usage < limit)
                            .unwrap_or(true)
                        {
                            Valence::Warning
                        } else {
                            Valence::Failure
                        },
                    }),
                }))?;
                let memory_usage_bytes = (result.resources.memory * 1024) as f64;
                let memory_limit_bytes =
                    task.memory_limit.map(|limit| (limit * 1024 * 1024) as f64);
                tx.send(Event::Value(ValueEvent {
                    key: record::Key(format!("testcase.{}.memory_usage", testcase)),
                    value: record::Value::MemoryUsage(record::MemoryUsageValue {
                        memory_usage: MemoryUsage(memory_usage_bytes as i32),
                    }),
                }))?;
                tx.send(Event::Value(ValueEvent {
                    key: record::Key(format!("testcase.{}.memory_usage_valence", testcase)),
                    value: record::Value::Valence(ValenceValue {
                        valence: if memory_limit_bytes
                            .map(|limit| memory_usage_bytes < limit * warning_watermark)
                            .unwrap_or(true)
                        {
                            Valence::Success
                        } else if memory_limit_bytes
                            .map(|limit| memory_usage_bytes < limit)
                            .unwrap_or(true)
                        {
                            Valence::Warning
                        } else {
                            Valence::Failure
                        },
                    }),
                }))?;
            }
        }
        _ => (),
    };
    Ok(())
}
<commit_msg>Update to latest task-maker API<commit_after>extern crate serde_json;
extern crate tempdir;

use super::*;

use std::path::PathBuf;
use std::sync::{
    mpsc::{channel, Receiver, Sender},
    Arc,
};
use std::thread;

use task_maker_cache::Cache;
use task_maker_dag::CacheMode;
use task_maker_exec::{executors::LocalExecutor, ExecutorClient};
use task_maker_format::ui::{UIExecutionStatus, UIMessage};
use task_maker_format::{ioi, EvaluationConfig, EvaluationData, TaskFormat, UISender};
use task_maker_store::*;

use award::{AwardName, Score};
use evaluation::*;
use submission::Submission;

use content::TextVariant;
use evaluation::record::ValenceValue;
use evaluation::Event;
use feedback::valence::Valence;
use rusage::{MemoryUsage, TimeUsage};
use task_maker_format::ioi::Task;

pub fn run_evaluation(task_path: PathBuf, submission: Submission) -> Receiver<Event> {
    let (event_tx, event_rx) = channel();

    let store_path = task_path.join(".task-maker-files");

    let min_store_size = 256 * 1024 * 1024;
    let max_store_size = 2 * min_store_size;

    let file_store =
        Arc::new(FileStore::new(
            store_path.join("store"),
            max_store_size,
            min_store_size,
        ).expect("Cannot create the file store"));

    let (tx, rx_remote) = task_maker_exec::new_local_channel();
    let (tx_remote, rx) = task_maker_exec::new_local_channel();

    let server_file_store = file_store.clone();
    thread::Builder::new()
        .name("Executor thread".into())
        .spawn(move || {
            let num_cores = num_cpus::get();
            let sandbox_path = store_path.join("sandboxes");
            let cache = Cache::new(store_path.join("cache")).expect("Cannot create the cache");
            let executor = LocalExecutor::new(server_file_store, num_cores, sandbox_path);

            executor.evaluate(tx_remote, rx_remote, cache).unwrap();
        })
        .expect("Failed to spawn the executor thread");

    thread::Builder::new()
        .name("Client thread".into())
        .spawn(move || {
            let dir = tempdir::TempDir::new("evaluation").unwrap();
            let solution_file = &submission.field_values[0].file;
            let solution_path = dir.path().join(&solution_file.name.0);
            std::fs::write(&solution_path, &solution_file.content)
                .expect("Unable to write solution file");
            println!("=={:?} start", solution_path);

            let (mut eval, receiver) = EvaluationData::new();

            let config = eval.dag.config_mut();

            let eval_config = EvaluationConfig {
                solution_filter: vec![],
                booklet_solutions: false,
                solution_paths: vec![solution_path.to_owned()],
                no_statement: true,
            };

            let task = ioi::Task::new(task_path, &eval_config).expect("Invalid task");

            // FIXME: is there a more idiomatic way to do it?
            let task2 = task.clone();

            thread::Builder::new()
                .name("Forwarder thread".into())
                .spawn(move || {
                    for m in receiver.iter() {
                        ui_message_to_events(&task2, m, &event_tx).expect("Failed to send event");
                    }
                })
                .expect("Failed to spawn the forwarder thread");

            config
                .keep_sandboxes(false)
                .dry_run(true)
                .cache_mode(CacheMode::Everything)
                .copy_exe(false)
                .extra_time(0.0);

            task.execute(&mut eval, &eval_config)
                .expect("Failed to build the DAG");

            trace!("The DAG is: {:#?}", eval.dag);

            let ui_sender = eval.sender.clone();
            ExecutorClient::evaluate(eval.dag, tx, &rx, file_store, move |status| {
                ui_sender.send(UIMessage::ServerStatus { status })
            })
            .expect("Client failed");
        })
        .expect("Failed to spawn the executor thread");
    event_rx
}

fn ui_message_to_events(
    task: &Task,
    ui_message: UIMessage,
    tx: &Sender<Event>,
) -> Result<(), failure::Error> {
    match ui_message {
        UIMessage::IOITestcaseScore {
            testcase,
            score,
            message,
            ..
        } => {
            tx.send(Event::Value(ValueEvent {
                key: record::Key(format!("testcase.{}.score", testcase)),
                value: record::Value::Score(record::ScoreValue {
                    score: Score(score as f64),
                }),
            }))?;
            tx.send(Event::Value(ValueEvent {
                key: record::Key(format!("testcase.{}.message", testcase)),
                value: record::Value::Message(record::TextValue {
                    text: vec![TextVariant {
                        value: message,
                        attributes: vec![],
                    }],
                }),
            }))?;
            tx.send(Event::Value(ValueEvent {
                key: record::Key(format!("testcase.{}.valence", testcase)),
                value: record::Value::Valence(ValenceValue {
                    valence: if score <= 0.0 {
                        Valence::Failure
                    } else if score >= 1.0 {
                        Valence::Success
                    } else {
                        Valence::Partial
                    },
                }),
            }))?;
        }
        UIMessage::IOISubtaskScore {
            subtask,
            score,
            normalized_score,
            ..
        } => {
            tx.send(Event::Value(ValueEvent {
                key: record::Key(format!("subtask.{}.score", subtask)),
                value: record::Value::Score(record::ScoreValue {
                    score: Score(score),
                }),
            }))?;
            tx.send(Event::Score(ScoreEvent {
                award_name: AwardName(format!("subtask.{}", subtask)),
                score: Score(score),
            }))?;
            tx.send(Event::Badge(BadgeEvent {
                award_name: AwardName(format!("subtask.{}", subtask)),
                badge: normalized_score > 0.0,
            }))?;
        }
        UIMessage::IOIEvaluation {
            testcase, status, ..
        } => {
            if let UIExecutionStatus::Done { result } = status {
                let time_usage = result.resources.cpu_time;
                tx.send(Event::Value(ValueEvent {
                    key: record::Key(format!("testcase.{}.time_usage", testcase)),
                    value: record::Value::TimeUsage(record::TimeUsageValue {
                        time_usage: TimeUsage(time_usage),
                    }),
                }))?;
                let warning_watermark = 0.25;
                tx.send(Event::Value(ValueEvent {
                    key: record::Key(format!("testcase.{}.time_usage_valence", testcase)),
                    value: record::Value::Valence(ValenceValue {
                        valence: if task
                            .time_limit
                            .map(|limit| time_usage < limit * warning_watermark)
                            .unwrap_or(true)
                        {
                            Valence::Success
                        } else if task
                            .time_limit
                            .map(|limit| time_usage < limit)
                            .unwrap_or(true)
                        {
                            Valence::Warning
                        } else {
                            Valence::Failure
                        },
                    }),
                }))?;
                let memory_usage_bytes = (result.resources.memory * 1024) as f64;
                let memory_limit_bytes =
                    task.memory_limit.map(|limit| (limit * 1024 * 1024) as f64);
                tx.send(Event::Value(ValueEvent {
                    key: record::Key(format!("testcase.{}.memory_usage", testcase)),
                    value: record::Value::MemoryUsage(record::MemoryUsageValue {
                        memory_usage: MemoryUsage(memory_usage_bytes as i32),
                    }),
                }))?;
                tx.send(Event::Value(ValueEvent {
                    key: record::Key(format!("testcase.{}.memory_usage_valence", testcase)),
                    value: record::Value::Valence(ValenceValue {
                        valence: if memory_limit_bytes
                            .map(|limit| memory_usage_bytes < limit * warning_watermark)
                            .unwrap_or(true)
                        {
                            Valence::Success
                        } else if memory_limit_bytes
                            .map(|limit| memory_usage_bytes < limit)
                            .unwrap_or(true)
                        {
                            Valence::Warning
                        } else {
                            Valence::Failure
                        },
                    }),
                }))?;
            }
        }
        _ => (),
    };
    Ok(())
}
<|endoftext|>


<commit_before>#![crate_name = "platform"]
#![crate_type = "rlib"]
#![no_std]
#![feature(lang_items)]

extern crate drivers;
extern crate hil;
extern crate nrf51822;
extern crate support;
extern crate process;

use hil::Controller;
use drivers::timer::AlarmToTimer;
use drivers::virtual_alarm::{MuxAlarm, VirtualMuxAlarm};

pub struct Firestorm {
    chip: nrf51822::chip::Nrf51822,
    noop: &'static drivers::noop::Noop,
    gpio: &'static drivers::gpio::GPIO<'static, nrf51822::gpio::GPIOPin>,
    timer: &'static drivers::timer::TimerDriver<'static, AlarmToTimer<'static,
                                VirtualMuxAlarm<'static, nrf51822::rtc::Rtc>>>,
}

impl Firestorm {
    pub unsafe fn service_pending_interrupts(&mut self) {
        self.chip.service_pending_interrupts()
    }

    pub unsafe fn has_pending_interrupts(&mut self) -> bool {
        self.chip.has_pending_interrupts()
    }

    #[inline(never)]
    pub fn with_driver<F, R>(&mut self, driver_num: usize, f: F) -> R where
            F: FnOnce(Option<&hil::Driver>) -> R {
        match driver_num {
           99 => f(Some(self.noop)),
            1 => f(Some(self.gpio)),
            3 => f(Some(self.timer)),
            _ => f(None)
        }
    }
}

macro_rules! static_init {
   ($V:ident : $T:ty = $e:expr) => {
        let $V : &mut $T = {
            // Waiting out for size_of to be available at compile-time to avoid
            // hardcoding an abitrary large size...
            static mut BUF : [u8; 1024] = [0; 1024];
            let mut tmp : &mut $T = mem::transmute(&mut BUF);
            *tmp = $e;
            tmp
        };
   }
}

pub unsafe fn init<'a>() -> &'a mut Firestorm {
    use core::mem;
    use nrf51822::gpio::PORT;

    static mut FIRESTORM_BUF : [u8; 1024] = [0; 1024];

    static_init!(gpio_pins : [&'static nrf51822::gpio::GPIOPin; 10] = [
            &nrf51822::gpio::PORT[18], // LED_0
            &nrf51822::gpio::PORT[19], // LED_1
            &nrf51822::gpio::PORT[0], // Top left header on EK board
            &nrf51822::gpio::PORT[1], //   |
            &nrf51822::gpio::PORT[2], //   V
            &nrf51822::gpio::PORT[3], //
            &nrf51822::gpio::PORT[4], //
            &nrf51822::gpio::PORT[5], //
            &nrf51822::gpio::PORT[6], //
            &nrf51822::gpio::PORT[7], //
            ]);
    static_init!(gpio : drivers::gpio::GPIO<'static, nrf51822::gpio::GPIOPin> =
                 drivers::gpio::GPIO::new(gpio_pins));
    for pin in gpio_pins.iter() {
        pin.set_client(gpio);
    }

    let rtc = &nrf51822::rtc::RTC;

    static_init!(mux_alarm : MuxAlarm<'static, nrf51822::rtc::Rtc> =
                    MuxAlarm::new(&nrf51822::rtc::RTC));
    rtc.configure(mux_alarm);

    static_init!(virtual_alarm1 : VirtualMuxAlarm<'static, nrf51822::rtc::Rtc> =
                    VirtualMuxAlarm::new(mux_alarm));
    static_init!(vtimer1 : AlarmToTimer<'static,
                                VirtualMuxAlarm<'static, nrf51822::rtc::Rtc>> =
                            AlarmToTimer::new(virtual_alarm1));
    virtual_alarm1.set_client(vtimer1);
    static_init!(timer : drivers::timer::TimerDriver<AlarmToTimer<'static,
                                VirtualMuxAlarm<'static, nrf51822::rtc::Rtc>>> =
                            drivers::timer::TimerDriver::new(vtimer1,
                                                 process::Container::create()));
    vtimer1.set_client(timer);

    static_init!(noop : drivers::noop::Noop = drivers::noop::Noop::new());

    let firestorm : &'static mut Firestorm = mem::transmute(&mut FIRESTORM_BUF);
    *firestorm = Firestorm {
        chip: nrf51822::chip::Nrf51822::new(),
        noop: noop,
        gpio: gpio,
        timer: timer,
    };
    firestorm
}

use core::fmt::Arguments;
#[cfg(not(test))]
#[lang="panic_fmt"]
#[no_mangle]
pub unsafe extern fn rust_begin_unwind(_args: &Arguments,
    _file: &'static str, _line: usize) -> ! {
    use support::nop;
    use hil::gpio::GPIOPin;

    let led0 = &nrf51822::gpio::PORT[18];
    let led1 = &nrf51822::gpio::PORT[19];

    led0.enable_output();
    led1.enable_output();
    loop {
        for _ in 0..100000 {
            led0.set();
            led1.set();
            nop();
        }
        for _ in 0..100000 {
            led0.clear();
            led1.clear();
            nop();
        }
    }
}

<commit_msg>Cut out timer, since it causes boot to fail.<commit_after>#![crate_name = "platform"]
#![crate_type = "rlib"]
#![no_std]
#![feature(lang_items)]

extern crate drivers;
extern crate hil;
extern crate nrf51822;
extern crate support;
extern crate process;

use hil::Controller;
use drivers::timer::AlarmToTimer;
use drivers::virtual_alarm::{MuxAlarm, VirtualMuxAlarm};

pub struct Firestorm {
    chip: nrf51822::chip::Nrf51822,
    noop: &'static drivers::noop::Noop,
    gpio: &'static drivers::gpio::GPIO<'static, nrf51822::gpio::GPIOPin>,
//    timer: &'static drivers::timer::TimerDriver<'static, AlarmToTimer<'static,
//                                VirtualMuxAlarm<'static, nrf51822::rtc::Rtc>>>,
}

impl Firestorm {
    pub unsafe fn service_pending_interrupts(&mut self) {
        self.chip.service_pending_interrupts()
    }

    pub unsafe fn has_pending_interrupts(&mut self) -> bool {
        self.chip.has_pending_interrupts()
    }

    #[inline(never)]
    pub fn with_driver<F, R>(&mut self, driver_num: usize, f: F) -> R where
            F: FnOnce(Option<&hil::Driver>) -> R {
        match driver_num {
           99 => f(Some(self.noop)),
            1 => f(Some(self.gpio)),
           // 3 => f(Some(self.timer)),
            _ => f(None)
        }
    }
}

macro_rules! static_init {
   ($V:ident : $T:ty = $e:expr) => {
        let $V : &mut $T = {
            // Waiting out for size_of to be available at compile-time to avoid
            // hardcoding an abitrary large size...
            static mut BUF : [u8; 1024] = [0; 1024];
            let mut tmp : &mut $T = mem::transmute(&mut BUF);
            *tmp = $e;
            tmp
        };
   }
}

pub unsafe fn init<'a>() -> &'a mut Firestorm {
    use core::mem;
    use nrf51822::gpio::PORT;

    static mut FIRESTORM_BUF : [u8; 1024] = [0; 1024];

    static_init!(gpio_pins : [&'static nrf51822::gpio::GPIOPin; 10] = [
            &nrf51822::gpio::PORT[18], // LED_0
            &nrf51822::gpio::PORT[19], // LED_1
            &nrf51822::gpio::PORT[0], // Top left header on EK board
            &nrf51822::gpio::PORT[1], //   |
            &nrf51822::gpio::PORT[2], //   V
            &nrf51822::gpio::PORT[3], //
            &nrf51822::gpio::PORT[4], //
            &nrf51822::gpio::PORT[5], //
            &nrf51822::gpio::PORT[6], //
            &nrf51822::gpio::PORT[7], //
            ]);
    static_init!(gpio : drivers::gpio::GPIO<'static, nrf51822::gpio::GPIOPin> =
                 drivers::gpio::GPIO::new(gpio_pins));
    for pin in gpio_pins.iter() {
        pin.set_client(gpio);
    }
/*
    let rtc = &nrf51822::rtc::RTC;

    static_init!(mux_alarm : MuxAlarm<'static, nrf51822::rtc::Rtc> =
                    MuxAlarm::new(&nrf51822::rtc::RTC));
    rtc.configure(mux_alarm);

    static_init!(virtual_alarm1 : VirtualMuxAlarm<'static, nrf51822::rtc::Rtc> =
                    VirtualMuxAlarm::new(mux_alarm));
    static_init!(vtimer1 : AlarmToTimer<'static,
                                VirtualMuxAlarm<'static, nrf51822::rtc::Rtc>> =
                            AlarmToTimer::new(virtual_alarm1));
    virtual_alarm1.set_client(vtimer1);
    static_init!(timer : drivers::timer::TimerDriver<AlarmToTimer<'static,
                                VirtualMuxAlarm<'static, nrf51822::rtc::Rtc>>> =
                            drivers::timer::TimerDriver::new(vtimer1,
                                                 process::Container::create()));
    vtimer1.set_client(timer);
*/
    static_init!(noop : drivers::noop::Noop = drivers::noop::Noop::new());

    let firestorm : &'static mut Firestorm = mem::transmute(&mut FIRESTORM_BUF);
    *firestorm = Firestorm {
        chip: nrf51822::chip::Nrf51822::new(),
        noop: noop,
        gpio: gpio,
 //       timer: timer,
    };
    firestorm
}

use core::fmt::Arguments;
#[cfg(not(test))]
#[lang="panic_fmt"]
#[no_mangle]
pub unsafe extern fn rust_begin_unwind(_args: &Arguments,
    _file: &'static str, _line: usize) -> ! {
    use support::nop;
    use hil::gpio::GPIOPin;

    let led0 = &nrf51822::gpio::PORT[18];
    let led1 = &nrf51822::gpio::PORT[19];

    led0.enable_output();
    led1.enable_output();
    loop {
        for _ in 0..100000 {
            led0.set();
            led1.set();
            nop();
        }
        for _ in 0..100000 {
            led0.clear();
            led1.clear();
            nop();
        }
    }
}

<|endoftext|>