<commit_before>#![feature(test)]
extern crate test;

use std::ops::{Mul, Add};

extern crate nalgebra;
use self::nalgebra::{DMat, Transpose};

extern crate rblas;
use rblas::{Gemm};
use rblas::attribute::Transpose as BlasTranspose;

extern crate ndarray;
use ndarray::ArrayBase;
use ndarray::blas::AsBlas;

const NSAMPLES: usize = 6 * 1000;
const NOBSERVED: usize = 10 * 10;
const NHIDDEN: usize = 20;

// TODO use the functions in OrthogonalNMFBlas here directly

#[bench]
fn bench_ortho_nmf_weights_dividend_nalgebra(bencher: &mut test::Bencher) {
    let samples = DMat::<f64>::new_ones(NSAMPLES, NOBSERVED);
    let hidden = DMat::<f64>::new_ones(NHIDDEN, NOBSERVED);
    bencher.iter(|| {
        samples.clone().mul(hidden.transpose())
    });
}

#[bench]
fn bench_ortho_nmf_weights_dividend_rblas(bencher: &mut test::Bencher) {
    let mut samples = ArrayBase::<Vec<f64>, (usize, usize)>::from_elem((NSAMPLES, NOBSERVED), 1.);
    let mut hidden = ArrayBase::<Vec<f64>, (usize, usize)>::from_elem((NHIDDEN, NOBSERVED), 1.);
    bencher.iter(|| {
        let mut result = ArrayBase::<Vec<f64>, (usize, usize)>::from_elem((NSAMPLES, NHIDDEN), 1.);
        Gemm::gemm(
            &1.,
            BlasTranspose::NoTrans, &samples.blas(),
            BlasTranspose::Trans, &hidden.blas(),
            &0.,
            &mut result.blas());
        result
    });
}

#[bench]
fn bench_ortho_nmf_weights_divisor_nalgebra(bencher: &mut test::Bencher) {
    let weights = DMat::<f64>::new_ones(NSAMPLES, NHIDDEN);
    let hidden = DMat::<f64>::new_ones(NHIDDEN, NOBSERVED);
    bencher.iter(|| {
        weights.clone().mul(&hidden).mul(hidden.transpose())
    });
}

#[bench]
fn bench_ortho_nmf_weights_divisor_rblas(bencher: &mut test::Bencher) {
    let mut weights = ArrayBase::<Vec<f64>, (usize, usize)>::from_elem((NSAMPLES, NHIDDEN), 1.);
    let mut hidden = ArrayBase::<Vec<f64>, (usize, usize)>::from_elem((NHIDDEN, NOBSERVED), 1.);
    let mut reconstruction = ArrayBase::<Vec<f64>, (usize, usize)>::from_elem((NSAMPLES, NOBSERVED), 1.);
    bencher.iter(|| {
        let mut result = ArrayBase::<Vec<f64>, (usize, usize)>::from_elem((NSAMPLES, NHIDDEN), 1.);
        Gemm::gemm(
            &1.,
            BlasTranspose::NoTrans, &weights.blas(),
            BlasTranspose::NoTrans, &hidden.blas(),
            &0.,
            &mut reconstruction.blas());
        Gemm::gemm(
            &1.,
            BlasTranspose::NoTrans, &reconstruction.blas(),
            BlasTranspose::Trans, &hidden.blas(),
            &0.,
            &mut result.blas());
        result
    });
}

#[bench]
fn bench_ortho_nmf_hidden_dividend_nalgebra(bencher: &mut test::Bencher) {
    let weights = DMat::<f64>::new_ones(NSAMPLES, NHIDDEN);
    let samples = DMat::<f64>::new_ones(NSAMPLES, NOBSERVED);
    bencher.iter(|| {
        weights.transpose().mul(&samples);
    });
}

#[bench]
fn bench_ortho_nmf_hidden_dividend_rblas(bencher: &mut test::Bencher) {
    let mut weights = ArrayBase::<Vec<f64>, (usize, usize)>::from_elem((NSAMPLES, NHIDDEN), 1.);
    let mut samples = ArrayBase::<Vec<f64>, (usize, usize)>::from_elem((NSAMPLES, NOBSERVED), 1.);
    bencher.iter(|| {
        let mut result = ArrayBase::<Vec<f64>, (usize, usize)>::from_elem((NHIDDEN, NOBSERVED), 1.);
        Gemm::gemm(
            &1.,
            BlasTranspose::Trans, &weights.blas(),
            BlasTranspose::NoTrans, &samples.blas(),
            &0.,
            &mut result.blas());
        result
    });
}

// TODO why is this so much faster than the other ones
// when it does so much more ?
// is the clone so expensive ?
#[bench]
fn bench_ortho_nmf_hidden_divisor_nalgebra(bencher: &mut test::Bencher) {
    let weights = DMat::<f64>::new_ones(NSAMPLES, NHIDDEN);
    let hidden = DMat::<f64>::new_ones(NHIDDEN, NOBSERVED);
    bencher.iter(|| {
        // TODO this is surprisingly weirdly fast
        let gamma = DMat::<f64>::new_ones(NHIDDEN, NHIDDEN);
        weights.transpose().mul(&weights).mul(&hidden)
            .add(gamma.mul(&hidden))
    });
}

#[bench]
fn bench_ortho_nmf_hidden_divisor_rblas(bencher: &mut test::Bencher) {
    let mut weights = ArrayBase::<Vec<f64>, (usize, usize)>::from_elem((NSAMPLES, NHIDDEN), 1.);
    let mut weights_copy = ArrayBase::<Vec<f64>, (usize, usize)>::from_elem((NSAMPLES, NHIDDEN), 1.);
    let mut hidden = ArrayBase::<Vec<f64>, (usize, usize)>::from_elem((NHIDDEN, NOBSERVED), 1.);
    let mut gamma = ArrayBase::<Vec<f64>, (usize, usize)>::from_elem((NHIDDEN, NHIDDEN), 1.);
    // this is really small
    let mut tmp = ArrayBase::<Vec<f64>, (usize, usize)>::from_elem((NHIDDEN, NHIDDEN), 1.);
    bencher.iter(|| {
        let mut result = ArrayBase::<Vec<f64>, (usize, usize)>::from_elem((NHIDDEN, NOBSERVED), 1.);
        // we must make a copy here because we need two mutable
        // references to weights at the same time for .blas()
        // weights_copy.clone_from(&weights);
        let mut weights_copy = weights.clone();
        Gemm::gemm(
            &1.,
            BlasTranspose::Trans, &weights.blas(),
            BlasTranspose::NoTrans, &weights_copy.blas(),
            &0.,
            &mut tmp.blas());
        Gemm::gemm(
            &1.,
            BlasTranspose::NoTrans, &gamma.blas(),
            BlasTranspose::NoTrans, &hidden.blas(),
            &0.,
            &mut result.blas());
        // add to previous result
        Gemm::gemm(
            &1.,
            BlasTranspose::NoTrans, &tmp.blas(),
            BlasTranspose::NoTrans, &hidden.blas(),
            &1.,
            &mut result.blas());
        result
    });
}
<commit_msg>make it easy to swap out float type in bench<commit_after>#![feature(test)]
extern crate test;

use std::ops::{Mul, Add};

extern crate nalgebra;
use self::nalgebra::{DMat, Transpose};

extern crate rblas;
use rblas::{Gemm};
use rblas::attribute::Transpose as BlasTranspose;

extern crate ndarray;
use ndarray::ArrayBase;
use ndarray::blas::AsBlas;

extern crate rand;
use rand::{StdRng, SeedableRng};

extern crate onmf;

const NSAMPLES: usize = 6 * 1000;
const NOBSERVED: usize = 10 * 10;
const NHIDDEN: usize = 20;

// TODO use the functions in OrthogonalNMFBlas here directly

type FloatT = f32;

#[bench]
fn bench_ortho_nmf_weights_dividend_nalgebra(bencher: &mut test::Bencher) {
    let samples = DMat::<FloatT>::new_ones(NSAMPLES, NOBSERVED);
    let hidden = DMat::<FloatT>::new_ones(NHIDDEN, NOBSERVED);
    bencher.iter(|| {
        samples.clone().mul(hidden.transpose())
    });
}

#[bench]
fn bench_ortho_nmf_weights_dividend_rblas(bencher: &mut test::Bencher) {
    let mut samples = ArrayBase::<Vec<FloatT>, (usize, usize)>::from_elem((NSAMPLES, NOBSERVED), 1.);
    let mut hidden = ArrayBase::<Vec<FloatT>, (usize, usize)>::from_elem((NHIDDEN, NOBSERVED), 1.);
    bencher.iter(|| {
        let mut result = ArrayBase::<Vec<FloatT>, (usize, usize)>::from_elem((NSAMPLES, NHIDDEN), 1.);
        Gemm::gemm(
            &1.,
            BlasTranspose::NoTrans, &samples.blas(),
            BlasTranspose::Trans, &hidden.blas(),
            &0.,
            &mut result.blas());
        result
    });
}

#[bench]
fn bench_ortho_nmf_weights_divisor_nalgebra(bencher: &mut test::Bencher) {
    let weights = DMat::<FloatT>::new_ones(NSAMPLES, NHIDDEN);
    let hidden = DMat::<FloatT>::new_ones(NHIDDEN, NOBSERVED);
    bencher.iter(|| {
        weights.clone().mul(&hidden).mul(hidden.transpose())
    });
}

#[bench]
fn bench_ortho_nmf_weights_divisor_rblas(bencher: &mut test::Bencher) {
    let mut weights = ArrayBase::<Vec<FloatT>, (usize, usize)>::from_elem((NSAMPLES, NHIDDEN), 1.);
    let mut hidden = ArrayBase::<Vec<FloatT>, (usize, usize)>::from_elem((NHIDDEN, NOBSERVED), 1.);
    let mut reconstruction = ArrayBase::<Vec<FloatT>, (usize, usize)>::from_elem((NSAMPLES, NOBSERVED), 1.);
    bencher.iter(|| {
        let mut result = ArrayBase::<Vec<FloatT>, (usize, usize)>::from_elem((NSAMPLES, NHIDDEN), 1.);
        Gemm::gemm(
            &1.,
            BlasTranspose::NoTrans, &weights.blas(),
            BlasTranspose::NoTrans, &hidden.blas(),
            &0.,
            &mut reconstruction.blas());
        Gemm::gemm(
            &1.,
            BlasTranspose::NoTrans, &reconstruction.blas(),
            BlasTranspose::Trans, &hidden.blas(),
            &0.,
            &mut result.blas());
        result
    });
}

#[bench]
fn bench_ortho_nmf_hidden_dividend_nalgebra(bencher: &mut test::Bencher) {
    let weights = DMat::<FloatT>::new_ones(NSAMPLES, NHIDDEN);
    let samples = DMat::<FloatT>::new_ones(NSAMPLES, NOBSERVED);
    bencher.iter(|| {
        weights.transpose().mul(&samples);
    });
}

#[bench]
fn bench_ortho_nmf_hidden_dividend_rblas(bencher: &mut test::Bencher) {
    let mut weights = ArrayBase::<Vec<FloatT>, (usize, usize)>::from_elem((NSAMPLES, NHIDDEN), 1.);
    let mut samples = ArrayBase::<Vec<FloatT>, (usize, usize)>::from_elem((NSAMPLES, NOBSERVED), 1.);
    bencher.iter(|| {
        let mut result = ArrayBase::<Vec<FloatT>, (usize, usize)>::from_elem((NHIDDEN, NOBSERVED), 1.);
        Gemm::gemm(
            &1.,
            BlasTranspose::Trans, &weights.blas(),
            BlasTranspose::NoTrans, &samples.blas(),
            &0.,
            &mut result.blas());
        result
    });
}

// TODO why is this so much faster than the other ones
// when it does so much more ?
// is the clone so expensive ?
#[bench]
fn bench_ortho_nmf_hidden_divisor_nalgebra(bencher: &mut test::Bencher) {
    let weights = DMat::<FloatT>::new_ones(NSAMPLES, NHIDDEN);
    let hidden = DMat::<FloatT>::new_ones(NHIDDEN, NOBSERVED);
    bencher.iter(|| {
        // TODO this is surprisingly weirdly fast
        let gamma = DMat::<FloatT>::new_ones(NHIDDEN, NHIDDEN);
        weights.transpose().mul(&weights).mul(&hidden)
            .add(gamma.mul(&hidden))
    });
}

#[bench]
fn bench_ortho_nmf_hidden_divisor_rblas(bencher: &mut test::Bencher) {
    let mut weights = ArrayBase::<Vec<FloatT>, (usize, usize)>::from_elem((NSAMPLES, NHIDDEN), 1.);
    let mut weights_copy = ArrayBase::<Vec<FloatT>, (usize, usize)>::from_elem((NSAMPLES, NHIDDEN), 1.);
    let mut hidden = ArrayBase::<Vec<FloatT>, (usize, usize)>::from_elem((NHIDDEN, NOBSERVED), 1.);
    let mut gamma = ArrayBase::<Vec<FloatT>, (usize, usize)>::from_elem((NHIDDEN, NHIDDEN), 1.);
    // this is really small
    let mut tmp = ArrayBase::<Vec<FloatT>, (usize, usize)>::from_elem((NHIDDEN, NHIDDEN), 1.);
    bencher.iter(|| {
        let mut result = ArrayBase::<Vec<FloatT>, (usize, usize)>::from_elem((NHIDDEN, NOBSERVED), 1.);
        // we must make a copy here because we need two mutable
        // references to weights at the same time for .blas()
        // weights_copy.clone_from(&weights);
        let mut weights_copy = weights.clone();
        Gemm::gemm(
            &1.,
            BlasTranspose::Trans, &weights.blas(),
            BlasTranspose::NoTrans, &weights_copy.blas(),
            &0.,
            &mut tmp.blas());
        Gemm::gemm(
            &1.,
            BlasTranspose::NoTrans, &gamma.blas(),
            BlasTranspose::NoTrans, &hidden.blas(),
            &0.,
            &mut result.blas());
        // add to previous result
        Gemm::gemm(
            &1.,
            BlasTranspose::NoTrans, &tmp.blas(),
            BlasTranspose::NoTrans, &hidden.blas(),
            &1.,
            &mut result.blas());
        result
    });
}
<|endoftext|>


<commit_before>// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

//! # Cap'n Proto Schema Compiler Plugin Library
//!
//! This library allows you to do
//! [Cap'n Proto code generation](https://capnproto.org/otherlang.html#how-to-write-compiler-plugins)
//! within a Cargo build. You still need the `capnp` binary (implemented in C++).
//! (If you use a package manager, try looking for a package called
//! `capnproto`.)
//!
//! In your Cargo.toml:
//!
//! ```ignore
//! [package]
//! build = "build.rs"
//!
//! [build-dependencies]
//! capnpc = "0.12"
//! ```
//!
//! In your build.rs:
//!
//! ```ignore
//! extern crate capnpc;
//!
//! fn main() {
//!     capnpc::CompilerCommand::new()
//!         .src_prefix("schema")
//!         .file("schema/foo.capnp")
//!         .file("schema/bar.capnp")
//!         .run().expect("schema compiler command");
//! }
//! ```
//!
//! This will be equivalent to executing the shell command
//!
//! ```ignore
//!   capnp compile -orust:$OUT_DIR --src-prefix=schema schema/foo.capnp schema/bar.capnp
//! ```
//!

extern crate capnp;

/// Code generated from
/// [schema.capnp](https://github.com/capnproto/capnproto/blob/master/c%2B%2B/src/capnp/schema.capnp).
pub mod schema_capnp;

pub mod codegen;
pub mod codegen_types;
mod pointer_constants;

use std::path::{Path, PathBuf};

fn run_command(mut command: ::std::process::Command, path: &PathBuf) -> ::capnp::Result<()> {
    let mut p = command.spawn()?;
    crate::codegen::generate_code(p.stdout.take().unwrap(), path.as_path())?;
    let exit_status = p.wait()?;
    if !exit_status.success() {
        Err(::capnp::Error::failed(format!(
            "Non-success exit status: {}",
            exit_status
        )))
    } else {
        Ok(())
    }
}

/// A builder object for schema compiler commands.
pub struct CompilerCommand {
    files: Vec<PathBuf>,
    src_prefixes: Vec<PathBuf>,
    import_paths: Vec<PathBuf>,
    no_standard_import: bool,
    output_path: Option<PathBuf>,
}

impl CompilerCommand {
    /// Creates a new, empty command.
    pub fn new() -> CompilerCommand {
        CompilerCommand {
            files: Vec::new(),
            src_prefixes: Vec::new(),
            import_paths: Vec::new(),
            no_standard_import: false,
            output_path: None,
        }
    }

    /// Adds a file to be compiled.
    pub fn file<'a, P>(&'a mut self, path: P) -> &'a mut CompilerCommand
    where
        P: AsRef<Path>,
    {
        self.files.push(path.as_ref().to_path_buf());
        self
    }

    /// Adds a --src-prefix flag. For all files specified for compilation that start
    /// with `prefix`, removes the prefix when computing output filenames.
    pub fn src_prefix<'a, P>(&'a mut self, prefix: P) -> &'a mut CompilerCommand
    where
        P: AsRef<Path>,
    {
        self.src_prefixes.push(prefix.as_ref().to_path_buf());
        self
    }

    /// Adds an --import_path flag. Adds `dir` to the list of directories searched
    /// for absolute imports.
    pub fn import_path<'a, P>(&'a mut self, dir: P) -> &'a mut CompilerCommand
    where
        P: AsRef<Path>,
    {
        self.import_paths.push(dir.as_ref().to_path_buf());
        self
    }

    /// Adds the --no-standard-import flag, indicating that the default import paths of
    /// /usr/include and /usr/local/include should not bet included.
    pub fn no_standard_import<'a>(&'a mut self) -> &'a mut CompilerCommand {
        self.no_standard_import = true;
        self
    }

    ///Sets the output directory of generated code. Default is OUT_DIR
    pub fn output_path<'a, P>(&'a mut self, path: P) -> &'a mut CompilerCommand
    where
        P: AsRef<Path>,
    {
        self.output_path = Some(path.as_ref().to_path_buf());
        self
    }

    /// Runs the command. Returns an error if `OUT_DIR` or a custom output directory was not set
    /// or if `capnp compile` fails.
    pub fn run(&mut self) -> ::capnp::Result<()> {
        let mut command = ::std::process::Command::new("capnp");
        command.arg("compile").arg("-o").arg("-");

        if self.no_standard_import {
            command.arg("--no-standard-import");
        }

        for import_path in &self.import_paths {
            command.arg(&format!("--import-path={}", import_path.display()));
        }

        for src_prefix in &self.src_prefixes {
            command.arg(&format!("--src-prefix={}", src_prefix.display()));
        }

        for file in &self.files {
            command.arg(&format!("{}", file.display()));
        }

        let output_path = if let Some(output_path) = &self.output_path {
            output_path.clone()
        } else {
            // Try `OUT_DIR` by default
            PathBuf::from(::std::env::var("OUT_DIR").map_err(|error| {
                ::capnp::Error::failed(format!(
                    "Could not access `OUT_DIR` environment variable: {}. \
                     You might need to set it up or instead create you own output \
                     structure using `CompilerCommand::output_path`",
                    error
                ))
            })?)
        };

        command.stdout(::std::process::Stdio::piped());
        command.stderr(::std::process::Stdio::inherit());

        run_command(command, &output_path).map_err(|error| {
            ::capnp::Error::failed(format!(
                "Error while trying to execute `capnp compile`: {}.  \
                 Please verify that version 0.5.2 or higher of the capnp executable \
                 is installed on your system. See https://capnproto.org/install.html",
                error
            ))
        })
    }
}

#[test]
fn compiler_command_new_no_out_dir() {
    let error = CompilerCommand::new().run().unwrap_err().description;
    assert!(error.starts_with("Could not access `OUT_DIR` environment variable"));
}

#[test]
fn compiler_command_with_output_path_no_out_dir() {
    let error = CompilerCommand::new().output_path("foo").run().unwrap_err().description;
    assert!(error.starts_with("Error while trying to execute `capnp compile`"));
}
<commit_msg>add CompilerCommand.run_with() accepting custom 'capnp' executable<commit_after>// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

//! # Cap'n Proto Schema Compiler Plugin Library
//!
//! This library allows you to do
//! [Cap'n Proto code generation](https://capnproto.org/otherlang.html#how-to-write-compiler-plugins)
//! within a Cargo build. You still need the `capnp` binary (implemented in C++).
//! (If you use a package manager, try looking for a package called
//! `capnproto`.)
//!
//! In your Cargo.toml:
//!
//! ```ignore
//! [package]
//! build = "build.rs"
//!
//! [build-dependencies]
//! capnpc = "0.12"
//! ```
//!
//! In your build.rs:
//!
//! ```ignore
//! extern crate capnpc;
//!
//! fn main() {
//!     capnpc::CompilerCommand::new()
//!         .src_prefix("schema")
//!         .file("schema/foo.capnp")
//!         .file("schema/bar.capnp")
//!         .run().expect("schema compiler command");
//! }
//! ```
//!
//! This will be equivalent to executing the shell command
//!
//! ```ignore
//!   capnp compile -orust:$OUT_DIR --src-prefix=schema schema/foo.capnp schema/bar.capnp
//! ```
//!

extern crate capnp;

/// Code generated from
/// [schema.capnp](https://github.com/capnproto/capnproto/blob/master/c%2B%2B/src/capnp/schema.capnp).
pub mod schema_capnp;

pub mod codegen;
pub mod codegen_types;
mod pointer_constants;

use std::path::{Path, PathBuf};

fn run_command(mut command: ::std::process::Command, path: &PathBuf) -> ::capnp::Result<()> {
    let mut p = command.spawn()?;
    crate::codegen::generate_code(p.stdout.take().unwrap(), path.as_path())?;
    let exit_status = p.wait()?;
    if !exit_status.success() {
        Err(::capnp::Error::failed(format!(
            "Non-success exit status: {}",
            exit_status
        )))
    } else {
        Ok(())
    }
}

/// A builder object for schema compiler commands.
pub struct CompilerCommand {
    files: Vec<PathBuf>,
    src_prefixes: Vec<PathBuf>,
    import_paths: Vec<PathBuf>,
    no_standard_import: bool,
    output_path: Option<PathBuf>,
}

impl CompilerCommand {
    /// Creates a new, empty command.
    pub fn new() -> CompilerCommand {
        CompilerCommand {
            files: Vec::new(),
            src_prefixes: Vec::new(),
            import_paths: Vec::new(),
            no_standard_import: false,
            output_path: None,
        }
    }

    /// Adds a file to be compiled.
    pub fn file<'a, P>(&'a mut self, path: P) -> &'a mut CompilerCommand
    where
        P: AsRef<Path>,
    {
        self.files.push(path.as_ref().to_path_buf());
        self
    }

    /// Adds a --src-prefix flag. For all files specified for compilation that start
    /// with `prefix`, removes the prefix when computing output filenames.
    pub fn src_prefix<'a, P>(&'a mut self, prefix: P) -> &'a mut CompilerCommand
    where
        P: AsRef<Path>,
    {
        self.src_prefixes.push(prefix.as_ref().to_path_buf());
        self
    }

    /// Adds an --import_path flag. Adds `dir` to the list of directories searched
    /// for absolute imports.
    pub fn import_path<'a, P>(&'a mut self, dir: P) -> &'a mut CompilerCommand
    where
        P: AsRef<Path>,
    {
        self.import_paths.push(dir.as_ref().to_path_buf());
        self
    }

    /// Adds the --no-standard-import flag, indicating that the default import paths of
    /// /usr/include and /usr/local/include should not bet included.
    pub fn no_standard_import<'a>(&'a mut self) -> &'a mut CompilerCommand {
        self.no_standard_import = true;
        self
    }

    ///Sets the output directory of generated code. Default is OUT_DIR
    pub fn output_path<'a, P>(&'a mut self, path: P) -> &'a mut CompilerCommand
    where
        P: AsRef<Path>,
    {
        self.output_path = Some(path.as_ref().to_path_buf());
        self
    }

    /// Runs the command, assuming a 'capnp' executable is accessible from current working directory (e.g. locally or in PATH environment variable).
    /// Returns an error if `OUT_DIR` or a custom output directory was not set or if `capnp compile` fails.
    pub fn run(&mut self) -> ::capnp::Result<()> {
        self.run_with("capnp")
    }

    /// Runs the command, using a custom capnp `executable` path (not a directory).
    /// Returns an error if `OUT_DIR` or a custom output directory was not set or if `<executable> compile` fails.
    pub fn run_with<P>(&mut self, executable: P) -> ::capnp::Result<()>
    where
        P: AsRef<Path>
    {
        let mut command = ::std::process::Command::new(executable.as_ref());
        command.arg("compile").arg("-o").arg("-");

        if self.no_standard_import {
            command.arg("--no-standard-import");
        }

        for import_path in &self.import_paths {
            command.arg(&format!("--import-path={}", import_path.display()));
        }

        for src_prefix in &self.src_prefixes {
            command.arg(&format!("--src-prefix={}", src_prefix.display()));
        }

        for file in &self.files {
            command.arg(&format!("{}", file.display()));
        }

        let output_path = if let Some(output_path) = &self.output_path {
            output_path.clone()
        } else {
            // Try `OUT_DIR` by default
            PathBuf::from(::std::env::var("OUT_DIR").map_err(|error| {
                ::capnp::Error::failed(format!(
                    "Could not access `OUT_DIR` environment variable: {}. \
                     You might need to set it up or instead create you own output \
                     structure using `CompilerCommand::output_path`",
                    error
                ))
            })?)
        };

        command.stdout(::std::process::Stdio::piped());
        command.stderr(::std::process::Stdio::inherit());

        run_command(command, &output_path).map_err(|error| {
            ::capnp::Error::failed(format!(
                "Error while trying to execute `capnp compile`: {}.  \
                 Please verify that version 0.5.2 or higher of the capnp executable \
                 is installed on your system. See https://capnproto.org/install.html",
                error
            ))
        })
    }
}

#[test]
fn compiler_command_new_no_out_dir() {
    let error = CompilerCommand::new().run().unwrap_err().description;
    assert!(error.starts_with("Could not access `OUT_DIR` environment variable"));
}

#[test]
fn compiler_command_with_output_path_no_out_dir() {
    let error = CompilerCommand::new().output_path("foo").run().unwrap_err().description;
    assert!(error.starts_with("Error while trying to execute `capnp compile`"));
}
<|endoftext|>


<commit_before>mod utils;

use js_sys::Float64Array;
use nalgebra::base::*;
use nalgebra::linalg::SVD;
use rand::distributions::Uniform;
use rand::rngs::SmallRng;
use rand::SeedableRng;
use std::cmp::*;
use std::vec::Vec;
use wasm_bindgen::prelude::*;
use web_sys::console;

// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
// allocator.
#[cfg(feature = "wee_alloc")]
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;

fn mat_to_float_64_array<C: Dim>(
    mat: &Matrix<f64, Dynamic, C, VecStorage<f64, Dynamic, C>>,
) -> Float64Array {
    unsafe {
        return Float64Array::view(&mat.data.as_vec());
    }
}

#[wasm_bindgen]
pub struct SvdResult {
    u_multiplied_with_singular_values: DMatrix<f64>,
    singular_values: DVector<f64>,
    v_t: DMatrix<f64>,
    current_rank: usize,
    low_rank_approximation: DMatrix<f64>,
}

#[wasm_bindgen]
impl SvdResult {
    fn new(u: DMatrix<f64>, singular_values: DVector<f64>, v_t: DMatrix<f64>) -> SvdResult {
        let low_rank_approximation = DMatrix::zeros(u.nrows(), v_t.ncols());
        let mut u_multiplied_with_singular_values = u;
        for i in 0..singular_values.len() {
            let val = singular_values[i];
            u_multiplied_with_singular_values
                .column_mut(i)
                .scale_mut(val);
        }
        SvdResult {
            u_multiplied_with_singular_values: u_multiplied_with_singular_values,
            singular_values: singular_values,
            v_t: v_t,
            current_rank: 0,
            low_rank_approximation: low_rank_approximation,
        }
    }
    pub fn singular_values(&self) -> Float64Array {
        return mat_to_float_64_array(&self.singular_values);
    }
    fn compute_low_rank_approximation_from_scratch(&mut self) {
        let rank = self.current_rank;
        let timing_label = format!("computation of low rank approximation (rank = {})", rank);
        console::time_with_label(&timing_label);
        let lhs = self.u_multiplied_with_singular_values.columns(0, rank);
        let rhs = self.v_t.rows(0, rank);
        lhs.mul_to(&rhs, &mut self.low_rank_approximation);
        console::time_end_with_label(&timing_label);
    }
    fn update_low_rank_approximation(&mut self, new_rank: usize) {
        let old_rank = self.current_rank;
        let timing_label = format!(
            "update of low rank approximation (old rank = {}, new rank = {})",
            old_rank, new_rank
        );
        console::time_with_label(&timing_label);
        let min_rank = min(old_rank, new_rank);
        let max_rank = max(old_rank, new_rank);
        let lhs = self
            .u_multiplied_with_singular_values
            .columns(min_rank, max_rank - min_rank);
        let rhs = self.v_t.rows(min_rank, max_rank - min_rank);
        let delta = lhs * rhs;
        if new_rank > old_rank {
            self.low_rank_approximation += delta;
        } else {
            self.low_rank_approximation -= delta;
        }
        console::time_end_with_label(&timing_label);
        self.current_rank = new_rank;
    }
    pub fn compute_low_rank_approximation(&mut self, req_rank: usize) -> Float64Array {
        let rank = min(req_rank, self.u_multiplied_with_singular_values.ncols());
        let old_rank = self.current_rank;
        if old_rank < 100 || rank < 100 || 2 * rank < old_rank {
            self.current_rank = rank;
            self.compute_low_rank_approximation_from_scratch();
        } else {
            self.update_low_rank_approximation(rank);
        }
        return mat_to_float_64_array(&self.low_rank_approximation);
    }
}

fn permute_columns<T>(mat: &mut DMatrix<f64>, inv_perm: &Vec<(T, usize)>) {
    assert!(mat.ncols() == inv_perm.len());
    let n = mat.ncols();
    let mut already_permuted = vec![false; n];
    for i in 0..n {
        if !already_permuted[i] {
            let (mut j, mut k) = (i, inv_perm[i].1);
            while k != i {
                mat.swap_columns(j, k);
                already_permuted[k] = true;
                j = k;
                k = inv_perm[k].1;
            }
            already_permuted[i] = true;
        }
    }
}

fn permute_rows<T>(mat: &mut DMatrix<f64>, inv_perm: &Vec<(T, usize)>) {
    assert!(mat.nrows() == inv_perm.len());
    let n = mat.nrows();
    let mut already_permuted = vec![false; n];
    for i in 0..n {
        if !already_permuted[i] {
            let (mut j, mut k) = (i, inv_perm[i].1);
            while k != i {
                mat.swap_rows(j, k);
                already_permuted[k] = true;
                j = k;
                k = inv_perm[k].1;
            }
            already_permuted[i] = true;
        }
    }
}

// We have to sort SVD because this is not done by default,
// see https://github.com/dimforge/nalgebra/issues/349
// TODO(TBa): Is this still necessary? The issue linked above is open, but the following changelog entry
// mentions that the SVs are now sorted:
// https://github.com/dimforge/nalgebra/blob/dev/CHANGELOG.md#0300-02-jan-2022
fn sort_svd(svd: &mut SVD<f64, Dynamic, Dynamic>) {
    let mut s: Vec<(_, _)> = svd
        .singular_values
        .into_iter()
        .enumerate()
        .map(|(idx, &v)| (v, idx))
        .collect();
    s.sort_unstable_by(|a, b| b.partial_cmp(a).unwrap());
    match svd.u.iter_mut().next() {
        Some(u) => permute_columns(u, &s),
        None => {}
    }
    match svd.v_t.iter_mut().next() {
        Some(v_t) => permute_rows(v_t, &s),
        None => {}
    }
    svd.singular_values = DVector::from_vec(s.into_iter().map(|(v, _idx)| v).collect());
}

fn sorted_svd(mat: DMatrix<f64>) -> SVD<f64, Dynamic, Dynamic> {
    let mut svd = mat.svd(true, true);
    sort_svd(&mut svd);
    return svd;
}

#[wasm_bindgen]
pub fn svd(a_data: &[f64], nrows: usize, ncols: usize) -> SvdResult {
    let a = DMatrix::from_column_slice(nrows, ncols, a_data);
    console::time_with_label("computation of SVD");
    let svd = sorted_svd(a);
    console::time_end_with_label("computation of SVD");
    return SvdResult::new(svd.u.unwrap(), svd.singular_values, svd.v_t.unwrap());
}

// Randomized algorithm that calculates the truncated SVD
// technique based on https://research.fb.com/fast-randomized-svd/
// Precondition: t <= MIN(nrows, ncols)
#[wasm_bindgen]
pub fn svd_simple_approx(a_data: &[f64], nrows: usize, ncols: usize, t: usize) -> SvdResult {
    let a = DMatrix::from_column_slice(nrows, ncols, a_data);

    console::time_with_label("computation of approximate SVD");

    // Generate matrix Ω ∈ ℝ^{m × n}
    let mut rng = SmallRng::seed_from_u64(2342);
    let distr: Uniform<f64> = Uniform::new(-1.0, 1.0);
    let omega: DMatrix<f64> = DMatrix::from_distribution(ncols, t, &distr, &mut rng);

    // Compute Y = A Ω
    let y = (&a) * omega;

    // Compute the QR decomposition of Y, Y = Q R
    let q = y.qr().q();

    // Compute B = Qᵀ A
    let b = q.tr_mul(&a);

    // Compute the SVD of B
    let b_svd = sorted_svd(b);

    let u = q * b_svd.u.unwrap();

    console::time_end_with_label("computation of approximate SVD");

    return SvdResult::new(u, b_svd.singular_values, b_svd.v_t.unwrap());
}
<commit_msg>No need to sort SVD anymore, nalgebra does it for us :-)<commit_after>mod utils;

use js_sys::Float64Array;
use nalgebra::base::*;
use rand::distributions::Uniform;
use rand::rngs::SmallRng;
use rand::SeedableRng;
use std::cmp::*;
use wasm_bindgen::prelude::*;
use web_sys::console;

// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
// allocator.
#[cfg(feature = "wee_alloc")]
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;

fn mat_to_float_64_array<C: Dim>(
    mat: &Matrix<f64, Dynamic, C, VecStorage<f64, Dynamic, C>>,
) -> Float64Array {
    unsafe {
        return Float64Array::view(&mat.data.as_vec());
    }
}

#[wasm_bindgen]
pub struct SvdResult {
    u_multiplied_with_singular_values: DMatrix<f64>,
    singular_values: DVector<f64>,
    v_t: DMatrix<f64>,
    current_rank: usize,
    low_rank_approximation: DMatrix<f64>,
}

#[wasm_bindgen]
impl SvdResult {
    fn new(u: DMatrix<f64>, singular_values: DVector<f64>, v_t: DMatrix<f64>) -> SvdResult {
        let low_rank_approximation = DMatrix::zeros(u.nrows(), v_t.ncols());
        let mut u_multiplied_with_singular_values = u;
        for i in 0..singular_values.len() {
            let val = singular_values[i];
            u_multiplied_with_singular_values
                .column_mut(i)
                .scale_mut(val);
        }
        SvdResult {
            u_multiplied_with_singular_values: u_multiplied_with_singular_values,
            singular_values: singular_values,
            v_t: v_t,
            current_rank: 0,
            low_rank_approximation: low_rank_approximation,
        }
    }
    pub fn singular_values(&self) -> Float64Array {
        return mat_to_float_64_array(&self.singular_values);
    }
    fn compute_low_rank_approximation_from_scratch(&mut self) {
        let rank = self.current_rank;
        let timing_label = format!("computation of low rank approximation (rank = {})", rank);
        console::time_with_label(&timing_label);
        let lhs = self.u_multiplied_with_singular_values.columns(0, rank);
        let rhs = self.v_t.rows(0, rank);
        lhs.mul_to(&rhs, &mut self.low_rank_approximation);
        console::time_end_with_label(&timing_label);
    }
    fn update_low_rank_approximation(&mut self, new_rank: usize) {
        let old_rank = self.current_rank;
        let timing_label = format!(
            "update of low rank approximation (old rank = {}, new rank = {})",
            old_rank, new_rank
        );
        console::time_with_label(&timing_label);
        let min_rank = min(old_rank, new_rank);
        let max_rank = max(old_rank, new_rank);
        let lhs = self
            .u_multiplied_with_singular_values
            .columns(min_rank, max_rank - min_rank);
        let rhs = self.v_t.rows(min_rank, max_rank - min_rank);
        let delta = lhs * rhs;
        if new_rank > old_rank {
            self.low_rank_approximation += delta;
        } else {
            self.low_rank_approximation -= delta;
        }
        console::time_end_with_label(&timing_label);
        self.current_rank = new_rank;
    }
    pub fn compute_low_rank_approximation(&mut self, req_rank: usize) -> Float64Array {
        let rank = min(req_rank, self.u_multiplied_with_singular_values.ncols());
        let old_rank = self.current_rank;
        if old_rank < 100 || rank < 100 || 2 * rank < old_rank {
            self.current_rank = rank;
            self.compute_low_rank_approximation_from_scratch();
        } else {
            self.update_low_rank_approximation(rank);
        }
        return mat_to_float_64_array(&self.low_rank_approximation);
    }
}

#[wasm_bindgen]
pub fn svd(a_data: &[f64], nrows: usize, ncols: usize) -> SvdResult {
    let a = DMatrix::from_column_slice(nrows, ncols, a_data);
    console::time_with_label("computation of SVD");
    let svd = a.svd(true, true);
    console::time_end_with_label("computation of SVD");
    return SvdResult::new(svd.u.unwrap(), svd.singular_values, svd.v_t.unwrap());
}

// Randomized algorithm that calculates the truncated SVD
// technique based on https://research.fb.com/fast-randomized-svd/
// Precondition: t <= MIN(nrows, ncols)
#[wasm_bindgen]
pub fn svd_simple_approx(a_data: &[f64], nrows: usize, ncols: usize, t: usize) -> SvdResult {
    let a = DMatrix::from_column_slice(nrows, ncols, a_data);

    console::time_with_label("computation of approximate SVD");

    // Generate matrix Ω ∈ ℝ^{m × n}
    let mut rng = SmallRng::seed_from_u64(2342);
    let distr: Uniform<f64> = Uniform::new(-1.0, 1.0);
    let omega: DMatrix<f64> = DMatrix::from_distribution(ncols, t, &distr, &mut rng);

    // Compute Y = A Ω
    let y = (&a) * omega;

    // Compute the QR decomposition of Y, Y = Q R
    let q = y.qr().q();

    // Compute B = Qᵀ A
    let b = q.tr_mul(&a);

    // Compute the SVD of B
    let b_svd = b.svd(true, true);

    let u = q * b_svd.u.unwrap();

    console::time_end_with_label("computation of approximate SVD");

    return SvdResult::new(u, b_svd.singular_values, b_svd.v_t.unwrap());
}
<|endoftext|>


<commit_before>/*
 * Copyright 2016-2019 Ben Ashford
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

//! Fetch ElasticSearch version information

use serde::Deserialize;

use crate::{error::EsError, Client, EsResponse};

#[derive(Debug)]
pub struct VersionOperation<'a> {
    client: &'a mut Client,
}

impl<'a> VersionOperation<'a> {
    pub fn new(client: &'a mut Client) -> Self {
        VersionOperation { client }
    }

    pub fn send(&mut self) -> Result<VersionResult, EsError> {
        let response = self.client.get_op("/")?;
        Ok(response.read_response()?)
    }
}

impl Client {
    /// Calls the base ES path, returning the version number
    pub fn version(&mut self) -> VersionOperation {
        VersionOperation::new(self)
    }
}

#[derive(Debug, Deserialize)]
pub struct Version {
    pub number: String,
    pub build_hash: String,
    pub build_timestamp: String,
    pub build_snapshot: bool,
    pub lucene_version: String,
}

#[derive(Debug, Deserialize)]
pub struct VersionResult {
    pub name: String,
    pub cluster_name: String,
    pub version: Version,
    pub tagline: String,
}

#[cfg(test)]
pub mod tests {
    use crate::tests::make_client;
    use regex::Regex;

    #[test]
    fn it_works() {
        let mut client = make_client();
        let result = client.version().send().unwrap();

        let expected_regex = Regex::new(r"^\d\.\d\.\d$").unwrap();
        assert_eq!(expected_regex.is_match(&result.version.number), true);
    }
}
<commit_msg>Fix Version info for ES5<commit_after>/*
 * Copyright 2016-2019 Ben Ashford
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

//! Fetch ElasticSearch version information

use serde::Deserialize;

use crate::{error::EsError, Client, EsResponse};

#[derive(Debug)]
pub struct VersionOperation<'a> {
    client: &'a mut Client,
}

impl<'a> VersionOperation<'a> {
    pub fn new(client: &'a mut Client) -> Self {
        VersionOperation { client }
    }

    pub fn send(&mut self) -> Result<VersionResult, EsError> {
        let response = self.client.get_op("/")?;
        Ok(response.read_response()?)
    }
}

impl Client {
    /// Calls the base ES path, returning the version number
    pub fn version(&mut self) -> VersionOperation {
        VersionOperation::new(self)
    }
}

#[derive(Debug, Deserialize)]
pub struct Version {
    pub number: String,
    pub build_hash: String,
    #[cfg(not(feature = "es5"))]
    pub build_timestamp: String,
    #[cfg(feature = "es5")]
    pub build_date: String,
    pub build_snapshot: bool,
    pub lucene_version: String,
}

#[derive(Debug, Deserialize)]
pub struct VersionResult {
    pub name: String,
    pub cluster_name: String,
    #[cfg(feature = "es5")]
    pub cluster_uuid: String,
    pub version: Version,
    pub tagline: String,
}

#[cfg(test)]
pub mod tests {
    use crate::tests::make_client;
    use regex::Regex;

    #[test]
    fn it_works() {
        let mut client = make_client();
        let result = client.version().send().unwrap();

        let expected_regex = Regex::new(r"^\d\.\d\.\d+$").unwrap();
        assert_eq!(expected_regex.is_match(&result.version.number), true);
    }
}
<|endoftext|>


<commit_before>extern crate open;
extern crate backtrace;
extern crate clap;

use std::time::{Instant, Duration};

pub fn print_start_message(version: &str, network_config: &NetworkConfig) {
    let my_host = format!(
        "{}:{}",
        match network_config.mode.as_str() {
            "local" => "localhost",
            "lan" => "<your LAN IP>",
            "internet" => "<your public IP>",
            _ => unreachable!(),
        },
        network_config.serve_host_port.split(':').nth(1).unwrap(),
    );

    println!("  {: ^41}  ", format!("Citybound {}", version.trim()));
    println!("");
    println!("  {: ^41}  ", "This is the simulation server.");
    println!("  {: ^41}  ", "To connect and start playing, please open");
    println!("  {: ^41}  ", "this address in Chrome/Firefox/Safari:");
    println!("╭───────────────────────────────────────────╮");
    println!("│ {: ^41} │", format!("http://{}", my_host));
    println!("╰───────────────────────────────────────────╯");
}

#[derive(Clone)]
pub struct NetworkConfig {
    pub mode: String,
    pub serve_host_port: String,
    pub bind_sim: String,
    pub batch_msg_bytes: usize,
    pub ok_turn_dist: usize,
    pub skip_ratio: usize,
}

pub fn match_cmd_line_args(version: &str) -> NetworkConfig {
    use self::clap::{Arg, App};
    let matches = App::new("citybound")
        .version(version.trim())
        .author("ae play (Anselm Eickhoff)")
        .about("The city is us.")
        .arg(
            Arg::with_name("mode")
                .long("mode")
                .value_name("local/lan/internet")
                .display_order(0)
                .possible_values(&["local", "lan", "internet"])
                .default_value("local")
                .help("Where to expose the simulation. Sets defaults other settings."),
        )
        .arg(
            Arg::with_name("bind")
                .long("bind")
                .value_name("host:port")
                .default_value_ifs(&[
                    ("mode", Some("local"), "localhost:1234"),
                    ("mode", Some("lan"), "0.0.0.0:1234"),
                    ("mode", Some("internet"), "0.0.0.0:1234"),
                ])
                .help("Address and port to serve the browser UI from"),
        )
        .arg(
            Arg::with_name("bind-sim")
                .long("bind-sim")
                .value_name("host:port")
                .default_value_ifs(&[
                    ("mode", Some("local"), "localhost:9999"),
                    ("mode", Some("lan"), "0.0.0.0:9999"),
                    ("mode", Some("internet"), "0.0.0.0:9999"),
                ])
                .help("Address and port to accept connections to the simulation from"),
        )
        .arg(
            Arg::with_name("batch-msg-b")
                .long("batch-msg-bytes")
                .value_name("n-bytes")
                .default_value("5000")
                .help("How many bytes of simulation messages to batch"),
        )
        .arg(
            Arg::with_name("ok-turn-dist")
                .long("ok-turn-dist")
                .value_name("n-turns")
                .default_value_ifs(&[
                    ("mode", Some("local"), "2"),
                    ("mode", Some("lan"), "10"),
                    ("mode", Some("internet"), "30"),
                ])
                .help("How many network turns client/server can be behind before skipping"),
        )
        .arg(
            Arg::with_name("skip-ratio")
                .long("skip-ratio")
                .value_name("n-turns")
                .default_value("5")
                .help("How many network turns to skip if server/client are ahead"),
        )
        .get_matches();

    NetworkConfig {
        serve_host_port: matches.value_of("bind").unwrap().to_owned(),
        bind_sim: matches.value_of("bind-sim").unwrap().to_owned(),
        mode: matches.value_of("mode").unwrap().to_owned(),
        batch_msg_bytes: matches.value_of("batch-msg-b").unwrap().parse().unwrap(),
        ok_turn_dist: matches.value_of("ok-turn-dist").unwrap().parse().unwrap(),
        skip_ratio: matches.value_of("skip-ratio").unwrap().parse().unwrap(),
    }
}

pub fn ensure_crossplatform_proper_thread<F: Fn() -> () + Send + 'static>(callback: F) {
    // Makes sure that:
    // a) on Windows we use a dummy thread with manually set stack size
    // b) on Mac/Linux we use the main thread, because we have to create the UI there

    if cfg!(windows) {
        let dummy_thread = ::std::thread::Builder::new()
            .stack_size(32 * 1024 * 1024)
            .spawn(callback)
            .unwrap();
        dummy_thread.join().unwrap();
    } else {
        callback();
    }
}

use std::panic::{set_hook, PanicInfo};
use self::backtrace::Backtrace;
use std::fs::File;
use std::io::Write;

pub fn set_error_hook() {
    let callback: Box<FnMut(&PanicInfo)> = Box::new(move |panic_info| {
        let title = "SIMULATION BROKE :(";

        let message = match panic_info.payload().downcast_ref::<String>() {
            Some(string) => (string.clone()),
            None => match panic_info.payload().downcast_ref::<&'static str>() {
                Some(static_str) => (*static_str).to_string(),
                None => "Weird error type".to_string(),
            },
        };

        let backtrace = Backtrace::new();
        let location = format!(
            "at {}, line {}",
            panic_info.location().map(|l| l.file()).unwrap_or("unknown"),
            panic_info.location().map(|l| l.line()).unwrap_or(0)
        );

        let body = format!(
            "WHAT HAPPENED:\n{}\n\nWHERE IT HAPPENED:\n{}\n\nWHERE EXACTLY:\n{:?}",
            message, location, backtrace
        );

        let report_guide = "HOW TO REPORT \
                            BUGS:\nhttps://github.\
                            com/citybound/citybound/blob/master/CONTRIBUTING.md#reporting-bugs";

        let mut error_file_path = ::std::env::temp_dir();
        error_file_path.push("cb_last_error.txt");

        println!(
            "{}\n\n{}\n\nALSO SEE {:?} (AUTO-OPENED)",
            title, body, error_file_path
        );

        {
            if let Ok(mut file) = File::create(&error_file_path) {
                let file_content = format!("{}\n\n{}\n\n{}", title, report_guide, body);
                let file_content = file_content.replace("\n", "\r\n");

                file.write_all(file_content.as_bytes())
                    .expect("Error writing error file, lol");
            };
        }

        open::that(error_file_path).expect("Couldn't open error file");
    });

    set_hook(unsafe { ::std::mem::transmute(callback) });
}

pub struct FrameCounter {
    last_frame: Instant,
    elapsed_ms_collected: Vec<f32>,
}

impl FrameCounter {
    pub fn new() -> FrameCounter {
        FrameCounter {
            last_frame: Instant::now(),
            elapsed_ms_collected: Vec::new(),
        }
    }

    pub fn start_frame(&mut self) {
        let elapsed_ms = self.last_frame.elapsed().as_secs() as f32 * 1000.0
            + self.last_frame.elapsed().subsec_nanos() as f32 / 10.0E5;

        self.elapsed_ms_collected.push(elapsed_ms);

        if self.elapsed_ms_collected.len() > 10 {
            self.elapsed_ms_collected.remove(0);
        }

        self.last_frame = Instant::now();
    }

    pub fn sleep_if_faster_than(&self, fps: usize) {
        let ideal_frame_duration = Duration::from_millis((1000.0 / (fps as f32)) as u64);

        if let Some(pos_difference) = ideal_frame_duration.checked_sub(self.last_frame.elapsed()) {
            ::std::thread::sleep(pos_difference);
        }
    }
}

impl Default for FrameCounter {
    fn default() -> Self {
        Self::new()
    }
}
<commit_msg>Display port properly when using IPv6 addresses<commit_after>extern crate open;
extern crate backtrace;
extern crate clap;

use std::time::{Instant, Duration};

pub fn print_start_message(version: &str, network_config: &NetworkConfig) {
    let my_host = format!(
        "{}:{}",
        match network_config.mode.as_str() {
            "local" => "localhost",
            "lan" => "<your LAN IP>",
            "internet" => "<your public IP>",
            _ => unreachable!(),
        },
        network_config.serve_host_port.split(':').last().unwrap(),
    );

    println!("  {: ^41}  ", format!("Citybound {}", version.trim()));
    println!("");
    println!("  {: ^41}  ", "This is the simulation server.");
    println!("  {: ^41}  ", "To connect and start playing, please open");
    println!("  {: ^41}  ", "this address in Chrome/Firefox/Safari:");
    println!("╭───────────────────────────────────────────╮");
    println!("│ {: ^41} │", format!("http://{}", my_host));
    println!("╰───────────────────────────────────────────╯");
}

#[derive(Clone)]
pub struct NetworkConfig {
    pub mode: String,
    pub serve_host_port: String,
    pub bind_sim: String,
    pub batch_msg_bytes: usize,
    pub ok_turn_dist: usize,
    pub skip_ratio: usize,
}

pub fn match_cmd_line_args(version: &str) -> NetworkConfig {
    use self::clap::{Arg, App};
    let matches = App::new("citybound")
        .version(version.trim())
        .author("ae play (Anselm Eickhoff)")
        .about("The city is us.")
        .arg(
            Arg::with_name("mode")
                .long("mode")
                .value_name("local/lan/internet")
                .display_order(0)
                .possible_values(&["local", "lan", "internet"])
                .default_value("local")
                .help("Where to expose the simulation. Sets defaults other settings."),
        )
        .arg(
            Arg::with_name("bind")
                .long("bind")
                .value_name("host:port")
                .default_value_ifs(&[
                    ("mode", Some("local"), "localhost:1234"),
                    ("mode", Some("lan"), "0.0.0.0:1234"),
                    ("mode", Some("internet"), "0.0.0.0:1234"),
                ])
                .help("Address and port to serve the browser UI from"),
        )
        .arg(
            Arg::with_name("bind-sim")
                .long("bind-sim")
                .value_name("host:port")
                .default_value_ifs(&[
                    ("mode", Some("local"), "localhost:9999"),
                    ("mode", Some("lan"), "0.0.0.0:9999"),
                    ("mode", Some("internet"), "0.0.0.0:9999"),
                ])
                .help("Address and port to accept connections to the simulation from"),
        )
        .arg(
            Arg::with_name("batch-msg-b")
                .long("batch-msg-bytes")
                .value_name("n-bytes")
                .default_value("5000")
                .help("How many bytes of simulation messages to batch"),
        )
        .arg(
            Arg::with_name("ok-turn-dist")
                .long("ok-turn-dist")
                .value_name("n-turns")
                .default_value_ifs(&[
                    ("mode", Some("local"), "2"),
                    ("mode", Some("lan"), "10"),
                    ("mode", Some("internet"), "30"),
                ])
                .help("How many network turns client/server can be behind before skipping"),
        )
        .arg(
            Arg::with_name("skip-ratio")
                .long("skip-ratio")
                .value_name("n-turns")
                .default_value("5")
                .help("How many network turns to skip if server/client are ahead"),
        )
        .get_matches();

    NetworkConfig {
        serve_host_port: matches.value_of("bind").unwrap().to_owned(),
        bind_sim: matches.value_of("bind-sim").unwrap().to_owned(),
        mode: matches.value_of("mode").unwrap().to_owned(),
        batch_msg_bytes: matches.value_of("batch-msg-b").unwrap().parse().unwrap(),
        ok_turn_dist: matches.value_of("ok-turn-dist").unwrap().parse().unwrap(),
        skip_ratio: matches.value_of("skip-ratio").unwrap().parse().unwrap(),
    }
}

pub fn ensure_crossplatform_proper_thread<F: Fn() -> () + Send + 'static>(callback: F) {
    // Makes sure that:
    // a) on Windows we use a dummy thread with manually set stack size
    // b) on Mac/Linux we use the main thread, because we have to create the UI there

    if cfg!(windows) {
        let dummy_thread = ::std::thread::Builder::new()
            .stack_size(32 * 1024 * 1024)
            .spawn(callback)
            .unwrap();
        dummy_thread.join().unwrap();
    } else {
        callback();
    }
}

use std::panic::{set_hook, PanicInfo};
use self::backtrace::Backtrace;
use std::fs::File;
use std::io::Write;

pub fn set_error_hook() {
    let callback: Box<FnMut(&PanicInfo)> = Box::new(move |panic_info| {
        let title = "SIMULATION BROKE :(";

        let message = match panic_info.payload().downcast_ref::<String>() {
            Some(string) => (string.clone()),
            None => match panic_info.payload().downcast_ref::<&'static str>() {
                Some(static_str) => (*static_str).to_string(),
                None => "Weird error type".to_string(),
            },
        };

        let backtrace = Backtrace::new();
        let location = format!(
            "at {}, line {}",
            panic_info.location().map(|l| l.file()).unwrap_or("unknown"),
            panic_info.location().map(|l| l.line()).unwrap_or(0)
        );

        let body = format!(
            "WHAT HAPPENED:\n{}\n\nWHERE IT HAPPENED:\n{}\n\nWHERE EXACTLY:\n{:?}",
            message, location, backtrace
        );

        let report_guide = "HOW TO REPORT \
                            BUGS:\nhttps://github.\
                            com/citybound/citybound/blob/master/CONTRIBUTING.md#reporting-bugs";

        let mut error_file_path = ::std::env::temp_dir();
        error_file_path.push("cb_last_error.txt");

        println!(
            "{}\n\n{}\n\nALSO SEE {:?} (AUTO-OPENED)",
            title, body, error_file_path
        );

        {
            if let Ok(mut file) = File::create(&error_file_path) {
                let file_content = format!("{}\n\n{}\n\n{}", title, report_guide, body);
                let file_content = file_content.replace("\n", "\r\n");

                file.write_all(file_content.as_bytes())
                    .expect("Error writing error file, lol");
            };
        }

        open::that(error_file_path).expect("Couldn't open error file");
    });

    set_hook(unsafe { ::std::mem::transmute(callback) });
}

pub struct FrameCounter {
    last_frame: Instant,
    elapsed_ms_collected: Vec<f32>,
}

impl FrameCounter {
    pub fn new() -> FrameCounter {
        FrameCounter {
            last_frame: Instant::now(),
            elapsed_ms_collected: Vec::new(),
        }
    }

    pub fn start_frame(&mut self) {
        let elapsed_ms = self.last_frame.elapsed().as_secs() as f32 * 1000.0
            + self.last_frame.elapsed().subsec_nanos() as f32 / 10.0E5;

        self.elapsed_ms_collected.push(elapsed_ms);

        if self.elapsed_ms_collected.len() > 10 {
            self.elapsed_ms_collected.remove(0);
        }

        self.last_frame = Instant::now();
    }

    pub fn sleep_if_faster_than(&self, fps: usize) {
        let ideal_frame_duration = Duration::from_millis((1000.0 / (fps as f32)) as u64);

        if let Some(pos_difference) = ideal_frame_duration.checked_sub(self.last_frame.elapsed()) {
            ::std::thread::sleep(pos_difference);
        }
    }
}

impl Default for FrameCounter {
    fn default() -> Self {
        Self::new()
    }
}
<|endoftext|>


<commit_before>
use std::io;

use parser::ast::*;
use processing::structs::*;
use output::scope::*;

pub fn reduce_expr_to_string(expr: &ExprValue, doc: &DocumentState, scope: &ElementOpScope) -> String {
    match expr {
        &ExprValue::LiteralString(ref s) => format!("{}", s),
        &ExprValue::LiteralNumber(ref n) => format!("{}", n),
        &ExprValue::LiteralArray(..) => format!("[array]"),
        _ => format!("[invalid]")
    }
}

pub fn eval_sym(sym: &Symbol, doc: &DocumentState, scope: &ElementOpScope) -> Option<ExprValue> {
    let sym_ref = sym.sym_ref();
    let sym_ty = sym.ty();
    if let &SymbolReferenceType::ResolvedReference(_, ref resolved) = sym_ref {
        match resolved {
            &ResolvedSymbolType::PropReference(ref key) => {
                if let Some(sym_val) = scope.1.props.get(key) {
                    return sym_val.value().map(|s| s.to_owned());
                }
            }

            &ResolvedSymbolType::BlockParamReference(ref key) => {
                if let Some(sym_val) = scope.1.block_params.get(key) {
                    return sym_val.value().map(|s| s.to_owned());
                }
            }

            &ResolvedSymbolType::ReducerKeyReference(ref key) => {
                if let Some(expr) = doc.resolve_symbol_value(sym) {
                    return Some(expr.clone());
                };
            }

            _ => {}
        };
    };

    None
}

pub fn reduce_expr(expr: &ExprValue, doc: &DocumentState, scope: &ElementOpScope) -> Option<ExprValue> {
    match expr {
        &ExprValue::LiteralString(..) => Some(expr.clone()),
        &ExprValue::LiteralNumber(..) => Some(expr.clone()),
        &ExprValue::LiteralArray(..) => Some(expr.clone()),

        &ExprValue::Expr(ref op, ref l, ref r) => {
            let l_expr = reduce_expr(l, doc, scope);
            let r_expr = reduce_expr(r, doc, scope);

            match op {
                &ExprOp::Add => {
                    match (l_expr, r_expr) {
                        // (&Some(ExprValue::LiteralString(ref l_str)), _) => {
                        //     let r_str = reduce_expr_to_string(r_expr, doc, scope);
                        //     return Some(ExprValue::LiteralString(format!("{}{}", l_str, r_str)));
                        // },
                        // (_, &Some(ExprValue::LiteralString(ref r_str))) => {
                        //     let l_str = reduce_expr_to_string(r_expr, doc, scope);
                        //     return Some(ExprValue::LiteralString(format!("{}{}", l_str, r_str)));
                        // },
                        (Some(ref l_val), Some(ref r_val)) => {
                            let l_str = reduce_expr_to_string(l_val, doc, scope);
                            let r_str = reduce_expr_to_string(r_val, doc, scope);
                            return Some(ExprValue::LiteralString(format!("{}{}", l_str, r_str)));
                        }
                        _ => {}
                    };
                }
                _ => {}
            };
            Some(ExprValue::LiteralString("[invalid expression]".to_owned()))
        }

        &ExprValue::SymbolReference(ref sym) => {
            if let Some(ref expr) = eval_sym(sym, doc, &scope) {
                return Some(expr.clone());
            };

            // match sym {
            //     &Symbol(ref sym, _, _) => {
            //         match sym {
            //             &SymbolReferenceType::ReducerKeyReference(ref as_reducer_key) => {
            //                 if let Some(ref reducer_data) = doc.reducer_key_data.get(as_reducer_key) {
            //                     if let Some(ref default_expr) = reducer_data.default_expr {
            //                         return reduce_expr(default_expr, doc, scope);
            //                     };
            //                 };
            //             }

            //             &SymbolReferenceType::LoopVarReference(ref var_name) => {
            //                 if let Some(ref reducer_data) = doc.reducer_key_data.get(var_name) {
            //                     if let Some(ref default_expr) = reducer_data.default_expr {
            //                         return reduce_expr(default_expr, doc, scope);
            //                     };
            //                 };
            //             }

            //             &SymbolReferenceType::PropReference(ref prop_name) => {
            //                 if let Some(ref reducer_data) = doc.reducer_key_data.get(prop_name) {
            //                     if let Some(ref default_expr) = reducer_data.default_expr {
            //                         return reduce_expr(default_expr, doc, scope);
            //                     };
            //                 };
            //             }

            //             _ => {}
            //         };
            //     }
            //     _ => {}
            // };
            Some(expr.clone())
        }

        _ => Some(expr.clone())
    }
}

pub fn write_computed_expr_value(w: &mut io::Write,
                                 node: &ExprValue,
                                 doc: &DocumentState,
                                 scope: &ElementOpScope)
                                 -> Result {
    match node {
        &ExprValue::LiteralString(ref s) => {
            write!(w, "{}", s)?;
        }
        &ExprValue::LiteralNumber(ref n) => {
            write!(w, "{}", n)?;
        }

        &ExprValue::LiteralArray(ref items) => {
            if let &Some(ref items) = items {
                for ref item in items {
                    write_computed_expr_value(w, item, doc, scope)?;
                }
            };
        }

        &ExprValue::DefaultVariableReference => {
            let default_var = scope.0.default_var().unwrap_or("value".to_owned());
            write!(w, "{}", default_var)?;
        }

        &ExprValue::ContentNode(..) => {}
        &ExprValue::DefaultAction(..) => {}
        &ExprValue::Action(..) => {}

        _ => {
            if let Some(ref expr) = reduce_expr(node, doc, scope) {
                write_computed_expr_value(w, &expr, doc, scope)?;
            };
        }

        // &ExprValue::VariableReference(ref var_name) => {
        //     let var_key = scope.0.var_prefix(var_name);
        //     write!(w, "{}", var_key)?;
        // }

        // &ExprValue::SymbolReference(ref sym) => {
        //     let expr = reduce_expr(expr, )
        //     match sym {
        //         &(Some(ref sym), _) => {
        //             match sym {
        //                 &SymbolReferenceType::LocalVarReference(ref var_name) => {
        //                     write!(w, "{}", var_name)?;
        //                 }

        //                 &SymbolReferenceType::ParameterReference(ref param_key) => {
        //                     write!(w, "{}", param_key)?;
        //                 }

        //                 &SymbolReferenceType::ReducerKeyReference(ref as_reducer_key) => {
        //                     if let Some(ref reducer_data) = doc.reducer_key_data.get(as_reducer_key) {
        //                         if let Some(ref default_expr) = reducer_data.default_expr {
        //                             write_computed_expr_value(w, default_expr, doc, scope)?;
        //                             return Ok(());
        //                         };
        //                     };

        //                     write!(w, "{}", as_reducer_key)?;
        //                 }

        //                 &SymbolReferenceType::LoopVarReference(ref var_name) => {
        //                     if let Some(ref eval_scope) = scope.2 {
        //                         if let Some(ref value) = eval_scope.symbol_values.get(var_name) {
        //                             if let SymbolValueType::ConstantValue(ref expr) = value.0 {
        //                                 write_computed_expr_value(w, expr, doc, scope)?;
        //                                 return Ok(());
        //                             }
        //                         }

        //                     }
        //                     write!(w, "{}", var_name)?;
        //                 }

        //                 _ => {}
        //             };
        //         }
        //         _ => {}
        //     };
        // }

        // &ExprValue::Expr(..) => {
        //     let expr = reduce_expr(node, doc, scope);
        //     write_computed_expr_value(w, &expr, doc, scope)?;
        // }

        // &ExprValue::ContentNode(..) => {}

        // &ExprValue::DefaultAction(..) => {}

        // &ExprValue::Action(..) => {}
    }
    Ok(())
}
<commit_msg>Cleanup dead code<commit_after>
use std::io;

use parser::ast::*;
use processing::structs::*;
use output::scope::*;

pub fn reduce_expr_to_string(expr: &ExprValue, doc: &DocumentState, scope: &ElementOpScope) -> String {
    match expr {
        &ExprValue::LiteralString(ref s) => format!("{}", s),
        &ExprValue::LiteralNumber(ref n) => format!("{}", n),
        &ExprValue::LiteralArray(..) => format!("[array]"),
        _ => format!("[invalid]")
    }
}

pub fn eval_sym(sym: &Symbol, doc: &DocumentState, scope: &ElementOpScope) -> Option<ExprValue> {
    let sym_ref = sym.sym_ref();
    let sym_ty = sym.ty();
    if let &SymbolReferenceType::ResolvedReference(_, ref resolved) = sym_ref {
        match resolved {
            &ResolvedSymbolType::PropReference(ref key) => {
                if let Some(sym_val) = scope.1.props.get(key) {
                    return sym_val.value().map(|s| s.to_owned());
                }
            }

            &ResolvedSymbolType::BlockParamReference(ref key) => {
                if let Some(sym_val) = scope.1.block_params.get(key) {
                    return sym_val.value().map(|s| s.to_owned());
                }
            }

            &ResolvedSymbolType::ReducerKeyReference(ref key) => {
                if let Some(expr) = doc.resolve_symbol_value(sym) {
                    return Some(expr.clone());
                };
            }

            _ => {}
        };
    };

    None
}

pub fn reduce_expr(expr: &ExprValue, doc: &DocumentState, scope: &ElementOpScope) -> Option<ExprValue> {
    match expr {
        &ExprValue::LiteralString(..) => Some(expr.clone()),
        &ExprValue::LiteralNumber(..) => Some(expr.clone()),
        &ExprValue::LiteralArray(..) => Some(expr.clone()),

        &ExprValue::Expr(ref op, ref l, ref r) => {
            let l_expr = reduce_expr(l, doc, scope);
            let r_expr = reduce_expr(r, doc, scope);

            match op {
                &ExprOp::Add => {
                    match (l_expr, r_expr) {
                        // (&Some(ExprValue::LiteralString(ref l_str)), _) => {
                        //     let r_str = reduce_expr_to_string(r_expr, doc, scope);
                        //     return Some(ExprValue::LiteralString(format!("{}{}", l_str, r_str)));
                        // },
                        // (_, &Some(ExprValue::LiteralString(ref r_str))) => {
                        //     let l_str = reduce_expr_to_string(r_expr, doc, scope);
                        //     return Some(ExprValue::LiteralString(format!("{}{}", l_str, r_str)));
                        // },
                        (Some(ref l_val), Some(ref r_val)) => {
                            let l_str = reduce_expr_to_string(l_val, doc, scope);
                            let r_str = reduce_expr_to_string(r_val, doc, scope);
                            return Some(ExprValue::LiteralString(format!("{}{}", l_str, r_str)));
                        }
                        _ => {}
                    };
                }
                _ => {}
            };
            Some(ExprValue::LiteralString("[invalid expression]".to_owned()))
        }

        &ExprValue::SymbolReference(ref sym) => {
            if let Some(ref expr) = eval_sym(sym, doc, &scope) {
                return Some(expr.clone());
            };
            Some(expr.clone())
        }

        _ => Some(expr.clone())
    }
}

pub fn write_computed_expr_value(w: &mut io::Write,
                                 node: &ExprValue,
                                 doc: &DocumentState,
                                 scope: &ElementOpScope)
                                 -> Result {
    match node {
        &ExprValue::LiteralString(ref s) => {
            write!(w, "{}", s)?;
        }
        &ExprValue::LiteralNumber(ref n) => {
            write!(w, "{}", n)?;
        }

        &ExprValue::LiteralArray(ref items) => {
            if let &Some(ref items) = items {
                for ref item in items {
                    write_computed_expr_value(w, item, doc, scope)?;
                }
            };
        }

        &ExprValue::DefaultVariableReference => {
            let default_var = scope.0.default_var().unwrap_or("value".to_owned());
            write!(w, "{}", default_var)?;
        }

        &ExprValue::ContentNode(..) => {}
        &ExprValue::DefaultAction(..) => {}
        &ExprValue::Action(..) => {}

        _ => {
            if let Some(ref expr) = reduce_expr(node, doc, scope) {
                write_computed_expr_value(w, &expr, doc, scope)?;
            };
        }
    }
    Ok(())
}
<|endoftext|>


<commit_before>// Std
use std::collections::HashMap;
use std::ffi::OsString;
use std::mem;
use std::ops::Deref;

// Internal
use crate::builder::{Arg, ArgPredicate, Command};
use crate::parser::AnyValue;
use crate::parser::Identifier;
use crate::parser::PendingArg;
use crate::parser::{ArgMatches, MatchedArg, SubCommand, ValueSource};
use crate::util::Id;
use crate::INTERNAL_ERROR_MSG;

#[derive(Debug, Default)]
pub(crate) struct ArgMatcher {
    matches: ArgMatches,
    pending: Option<PendingArg>,
}

impl ArgMatcher {
    pub(crate) fn new(_cmd: &Command) -> Self {
        ArgMatcher {
            matches: ArgMatches {
                #[cfg(debug_assertions)]
                valid_args: {
                    let args = _cmd.get_arguments().map(|a| a.id.clone());
                    let groups = _cmd.get_groups().map(|g| g.id.clone());
                    args.chain(groups).collect()
                },
                #[cfg(debug_assertions)]
                valid_subcommands: _cmd.get_subcommands().map(|sc| sc.get_id()).collect(),
                // HACK: Allow an external subcommand's ArgMatches be a stand-in for any ArgMatches
                // since users can't detect it and avoid the asserts.
                //
                // See clap-rs/clap#3263
                #[cfg(debug_assertions)]
                #[cfg(not(feature = "unstable-v4"))]
                disable_asserts: _cmd.is_allow_external_subcommands_set(),
                #[cfg(debug_assertions)]
                #[cfg(feature = "unstable-v4")]
                disable_asserts: false,
                ..Default::default()
            },
            pending: None,
        }
    }

    pub(crate) fn into_inner(self) -> ArgMatches {
        self.matches
    }

    pub(crate) fn propagate_globals(&mut self, global_arg_vec: &[Id]) {
        debug!(
            "ArgMatcher::get_global_values: global_arg_vec={:?}",
            global_arg_vec
        );
        let mut vals_map = HashMap::new();
        self.fill_in_global_values(global_arg_vec, &mut vals_map);
    }

    fn fill_in_global_values(
        &mut self,
        global_arg_vec: &[Id],
        vals_map: &mut HashMap<Id, MatchedArg>,
    ) {
        for global_arg in global_arg_vec {
            if let Some(ma) = self.get(global_arg) {
                // We have to check if the parent's global arg wasn't used but still exists
                // such as from a default value.
                //
                // For example, `myprog subcommand --global-arg=value` where --global-arg defines
                // a default value of `other` myprog would have an existing MatchedArg for
                // --global-arg where the value is `other`, however the occurs will be 0.
                let to_update = if let Some(parent_ma) = vals_map.get(global_arg) {
                    if parent_ma.get_occurrences() > 0 && ma.get_occurrences() == 0 {
                        parent_ma
                    } else {
                        ma
                    }
                } else {
                    ma
                }
                .clone();
                vals_map.insert(global_arg.clone(), to_update);
            }
        }
        if let Some(ref mut sc) = self.matches.subcommand {
            let mut am = ArgMatcher {
                matches: mem::take(&mut sc.matches),
                pending: None,
            };
            am.fill_in_global_values(global_arg_vec, vals_map);
            mem::swap(&mut am.matches, &mut sc.matches);
        }

        for (name, matched_arg) in vals_map.iter_mut() {
            self.matches.args.insert(name.clone(), matched_arg.clone());
        }
    }

    pub(crate) fn get(&self, arg: &Id) -> Option<&MatchedArg> {
        self.matches.args.get(arg)
    }

    pub(crate) fn get_mut(&mut self, arg: &Id) -> Option<&mut MatchedArg> {
        self.matches.args.get_mut(arg)
    }

    pub(crate) fn remove(&mut self, arg: &Id) {
        self.matches.args.swap_remove(arg);
    }

    pub(crate) fn contains(&self, arg: &Id) -> bool {
        self.matches.args.contains_key(arg)
    }

    pub(crate) fn arg_ids(&self) -> indexmap::map::Keys<Id, MatchedArg> {
        self.matches.args.keys()
    }

    pub(crate) fn entry(&mut self, arg: &Id) -> indexmap::map::Entry<Id, MatchedArg> {
        self.matches.args.entry(arg.clone())
    }

    pub(crate) fn subcommand(&mut self, sc: SubCommand) {
        self.matches.subcommand = Some(Box::new(sc));
    }

    pub(crate) fn subcommand_name(&self) -> Option<&str> {
        self.matches.subcommand_name()
    }

    pub(crate) fn iter(&self) -> indexmap::map::Iter<Id, MatchedArg> {
        self.matches.args.iter()
    }

    pub(crate) fn check_explicit<'a>(&self, arg: &Id, predicate: ArgPredicate<'a>) -> bool {
        self.get(arg).map_or(false, |a| a.check_explicit(predicate))
    }

    pub(crate) fn start_custom_arg(&mut self, arg: &Arg, source: ValueSource) {
        let id = &arg.id;
        debug!(
            "ArgMatcher::start_custom_arg: id={:?}, source={:?}",
            id, source
        );
        let ma = self.entry(id).or_insert(MatchedArg::new_arg(arg));
        debug_assert_eq!(ma.type_id(), Some(arg.get_value_parser().type_id()));
        ma.set_source(source);
        ma.new_val_group();
    }

    pub(crate) fn start_custom_group(&mut self, id: &Id, source: ValueSource) {
        debug!(
            "ArgMatcher::start_custom_arg: id={:?}, source={:?}",
            id, source
        );
        let ma = self.entry(id).or_insert(MatchedArg::new_group());
        debug_assert_eq!(ma.type_id(), None);
        ma.set_source(source);
        ma.new_val_group();
    }

    pub(crate) fn start_occurrence_of_arg(&mut self, arg: &Arg) {
        let id = &arg.id;
        debug!("ArgMatcher::start_occurrence_of_arg: id={:?}", id);
        let ma = self.entry(id).or_insert(MatchedArg::new_arg(arg));
        debug_assert_eq!(ma.type_id(), Some(arg.get_value_parser().type_id()));
        ma.set_source(ValueSource::CommandLine);
        ma.inc_occurrences();
        ma.new_val_group();
    }

    pub(crate) fn start_occurrence_of_group(&mut self, id: &Id) {
        debug!("ArgMatcher::start_occurrence_of_group: id={:?}", id);
        let ma = self.entry(id).or_insert(MatchedArg::new_group());
        debug_assert_eq!(ma.type_id(), None);
        ma.set_source(ValueSource::CommandLine);
        ma.inc_occurrences();
        ma.new_val_group();
    }

    pub(crate) fn start_occurrence_of_external(&mut self, cmd: &crate::Command) {
        let id = &Id::empty_hash();
        debug!("ArgMatcher::start_occurrence_of_external: id={:?}", id,);
        let ma = self.entry(id).or_insert(MatchedArg::new_external(cmd));
        debug_assert_eq!(
            ma.type_id(),
            Some(
                cmd.get_external_subcommand_value_parser()
                    .expect(INTERNAL_ERROR_MSG)
                    .type_id()
            )
        );
        ma.set_source(ValueSource::CommandLine);
        ma.inc_occurrences();
        ma.new_val_group();
    }

    pub(crate) fn add_val_to(&mut self, arg: &Id, val: AnyValue, raw_val: OsString) {
        let ma = self.get_mut(arg).expect(INTERNAL_ERROR_MSG);
        ma.append_val(val, raw_val);
    }

    pub(crate) fn add_index_to(&mut self, arg: &Id, idx: usize) {
        let ma = self.get_mut(arg).expect(INTERNAL_ERROR_MSG);
        ma.push_index(idx);
    }

    pub(crate) fn needs_more_vals(&self, o: &Arg) -> bool {
        let num_resolved = self.get(&o.id).map(|ma| ma.num_vals()).unwrap_or(0);
        let num_pending = self
            .pending
            .as_ref()
            .and_then(|p| (p.id == o.id).then(|| p.raw_vals.len()))
            .unwrap_or(0);
        let current_num = num_resolved + num_pending;
        debug!(
            "ArgMatcher::needs_more_vals: o={}, resolved={}, pending={}",
            o.name, num_resolved, num_pending
        );
        if current_num == 0 {
            true
        } else if let Some(num) = o.num_vals {
            debug!("ArgMatcher::needs_more_vals: num_vals...{}", num);
            if o.is_multiple_occurrences_set() {
                (current_num % num) != 0
            } else {
                num != current_num
            }
        } else if let Some(num) = o.max_vals {
            debug!("ArgMatcher::needs_more_vals: max_vals...{}", num);
            current_num < num
        } else if o.min_vals.is_some() {
            debug!("ArgMatcher::needs_more_vals: min_vals...true");
            true
        } else {
            o.is_multiple_values_set()
        }
    }

    pub(crate) fn pending_arg_id(&self) -> Option<&Id> {
        self.pending.as_ref().map(|p| &p.id)
    }

    pub(crate) fn pending_values_mut(
        &mut self,
        id: &Id,
        ident: Option<Identifier>,
    ) -> &mut Vec<OsString> {
        let pending = self.pending.get_or_insert_with(|| PendingArg {
            id: id.clone(),
            ident,
            raw_vals: Default::default(),
        });
        debug_assert_eq!(pending.id, *id, "{}", INTERNAL_ERROR_MSG);
        if ident.is_some() {
            debug_assert_eq!(pending.ident, ident, "{}", INTERNAL_ERROR_MSG);
        }
        &mut pending.raw_vals
    }

    pub(crate) fn take_pending(&mut self) -> Option<PendingArg> {
        self.pending.take()
    }
}

impl Deref for ArgMatcher {
    type Target = ArgMatches;

    fn deref(&self) -> &Self::Target {
        &self.matches
    }
}
<commit_msg>refactor(parser): Be explicit in checking present values<commit_after>// Std
use std::collections::HashMap;
use std::ffi::OsString;
use std::mem;
use std::ops::Deref;

// Internal
use crate::builder::{Arg, ArgPredicate, Command};
use crate::parser::AnyValue;
use crate::parser::Identifier;
use crate::parser::PendingArg;
use crate::parser::{ArgMatches, MatchedArg, SubCommand, ValueSource};
use crate::util::Id;
use crate::INTERNAL_ERROR_MSG;

#[derive(Debug, Default)]
pub(crate) struct ArgMatcher {
    matches: ArgMatches,
    pending: Option<PendingArg>,
}

impl ArgMatcher {
    pub(crate) fn new(_cmd: &Command) -> Self {
        ArgMatcher {
            matches: ArgMatches {
                #[cfg(debug_assertions)]
                valid_args: {
                    let args = _cmd.get_arguments().map(|a| a.id.clone());
                    let groups = _cmd.get_groups().map(|g| g.id.clone());
                    args.chain(groups).collect()
                },
                #[cfg(debug_assertions)]
                valid_subcommands: _cmd.get_subcommands().map(|sc| sc.get_id()).collect(),
                // HACK: Allow an external subcommand's ArgMatches be a stand-in for any ArgMatches
                // since users can't detect it and avoid the asserts.
                //
                // See clap-rs/clap#3263
                #[cfg(debug_assertions)]
                #[cfg(not(feature = "unstable-v4"))]
                disable_asserts: _cmd.is_allow_external_subcommands_set(),
                #[cfg(debug_assertions)]
                #[cfg(feature = "unstable-v4")]
                disable_asserts: false,
                ..Default::default()
            },
            pending: None,
        }
    }

    pub(crate) fn into_inner(self) -> ArgMatches {
        self.matches
    }

    pub(crate) fn propagate_globals(&mut self, global_arg_vec: &[Id]) {
        debug!(
            "ArgMatcher::get_global_values: global_arg_vec={:?}",
            global_arg_vec
        );
        let mut vals_map = HashMap::new();
        self.fill_in_global_values(global_arg_vec, &mut vals_map);
    }

    fn fill_in_global_values(
        &mut self,
        global_arg_vec: &[Id],
        vals_map: &mut HashMap<Id, MatchedArg>,
    ) {
        for global_arg in global_arg_vec {
            if let Some(ma) = self.get(global_arg) {
                // We have to check if the parent's global arg wasn't used but still exists
                // such as from a default value.
                //
                // For example, `myprog subcommand --global-arg=value` where `--global-arg` defines
                // a default value of `other` myprog would have an existing MatchedArg for
                // `--global-arg` where the value is `other`
                let to_update = if let Some(parent_ma) = vals_map.get(global_arg) {
                    if parent_ma.check_explicit(ArgPredicate::IsPresent)
                        && !ma.check_explicit(ArgPredicate::IsPresent)
                    {
                        parent_ma
                    } else {
                        ma
                    }
                } else {
                    ma
                }
                .clone();
                vals_map.insert(global_arg.clone(), to_update);
            }
        }
        if let Some(ref mut sc) = self.matches.subcommand {
            let mut am = ArgMatcher {
                matches: mem::take(&mut sc.matches),
                pending: None,
            };
            am.fill_in_global_values(global_arg_vec, vals_map);
            mem::swap(&mut am.matches, &mut sc.matches);
        }

        for (name, matched_arg) in vals_map.iter_mut() {
            self.matches.args.insert(name.clone(), matched_arg.clone());
        }
    }

    pub(crate) fn get(&self, arg: &Id) -> Option<&MatchedArg> {
        self.matches.args.get(arg)
    }

    pub(crate) fn get_mut(&mut self, arg: &Id) -> Option<&mut MatchedArg> {
        self.matches.args.get_mut(arg)
    }

    pub(crate) fn remove(&mut self, arg: &Id) {
        self.matches.args.swap_remove(arg);
    }

    pub(crate) fn contains(&self, arg: &Id) -> bool {
        self.matches.args.contains_key(arg)
    }

    pub(crate) fn arg_ids(&self) -> indexmap::map::Keys<Id, MatchedArg> {
        self.matches.args.keys()
    }

    pub(crate) fn entry(&mut self, arg: &Id) -> indexmap::map::Entry<Id, MatchedArg> {
        self.matches.args.entry(arg.clone())
    }

    pub(crate) fn subcommand(&mut self, sc: SubCommand) {
        self.matches.subcommand = Some(Box::new(sc));
    }

    pub(crate) fn subcommand_name(&self) -> Option<&str> {
        self.matches.subcommand_name()
    }

    pub(crate) fn iter(&self) -> indexmap::map::Iter<Id, MatchedArg> {
        self.matches.args.iter()
    }

    pub(crate) fn check_explicit<'a>(&self, arg: &Id, predicate: ArgPredicate<'a>) -> bool {
        self.get(arg).map_or(false, |a| a.check_explicit(predicate))
    }

    pub(crate) fn start_custom_arg(&mut self, arg: &Arg, source: ValueSource) {
        let id = &arg.id;
        debug!(
            "ArgMatcher::start_custom_arg: id={:?}, source={:?}",
            id, source
        );
        let ma = self.entry(id).or_insert(MatchedArg::new_arg(arg));
        debug_assert_eq!(ma.type_id(), Some(arg.get_value_parser().type_id()));
        ma.set_source(source);
        ma.new_val_group();
    }

    pub(crate) fn start_custom_group(&mut self, id: &Id, source: ValueSource) {
        debug!(
            "ArgMatcher::start_custom_arg: id={:?}, source={:?}",
            id, source
        );
        let ma = self.entry(id).or_insert(MatchedArg::new_group());
        debug_assert_eq!(ma.type_id(), None);
        ma.set_source(source);
        ma.new_val_group();
    }

    pub(crate) fn start_occurrence_of_arg(&mut self, arg: &Arg) {
        let id = &arg.id;
        debug!("ArgMatcher::start_occurrence_of_arg: id={:?}", id);
        let ma = self.entry(id).or_insert(MatchedArg::new_arg(arg));
        debug_assert_eq!(ma.type_id(), Some(arg.get_value_parser().type_id()));
        ma.set_source(ValueSource::CommandLine);
        ma.inc_occurrences();
        ma.new_val_group();
    }

    pub(crate) fn start_occurrence_of_group(&mut self, id: &Id) {
        debug!("ArgMatcher::start_occurrence_of_group: id={:?}", id);
        let ma = self.entry(id).or_insert(MatchedArg::new_group());
        debug_assert_eq!(ma.type_id(), None);
        ma.set_source(ValueSource::CommandLine);
        ma.inc_occurrences();
        ma.new_val_group();
    }

    pub(crate) fn start_occurrence_of_external(&mut self, cmd: &crate::Command) {
        let id = &Id::empty_hash();
        debug!("ArgMatcher::start_occurrence_of_external: id={:?}", id,);
        let ma = self.entry(id).or_insert(MatchedArg::new_external(cmd));
        debug_assert_eq!(
            ma.type_id(),
            Some(
                cmd.get_external_subcommand_value_parser()
                    .expect(INTERNAL_ERROR_MSG)
                    .type_id()
            )
        );
        ma.set_source(ValueSource::CommandLine);
        ma.inc_occurrences();
        ma.new_val_group();
    }

    pub(crate) fn add_val_to(&mut self, arg: &Id, val: AnyValue, raw_val: OsString) {
        let ma = self.get_mut(arg).expect(INTERNAL_ERROR_MSG);
        ma.append_val(val, raw_val);
    }

    pub(crate) fn add_index_to(&mut self, arg: &Id, idx: usize) {
        let ma = self.get_mut(arg).expect(INTERNAL_ERROR_MSG);
        ma.push_index(idx);
    }

    pub(crate) fn needs_more_vals(&self, o: &Arg) -> bool {
        let num_resolved = self.get(&o.id).map(|ma| ma.num_vals()).unwrap_or(0);
        let num_pending = self
            .pending
            .as_ref()
            .and_then(|p| (p.id == o.id).then(|| p.raw_vals.len()))
            .unwrap_or(0);
        let current_num = num_resolved + num_pending;
        debug!(
            "ArgMatcher::needs_more_vals: o={}, resolved={}, pending={}",
            o.name, num_resolved, num_pending
        );
        if current_num == 0 {
            true
        } else if let Some(num) = o.num_vals {
            debug!("ArgMatcher::needs_more_vals: num_vals...{}", num);
            if o.is_multiple_occurrences_set() {
                (current_num % num) != 0
            } else {
                num != current_num
            }
        } else if let Some(num) = o.max_vals {
            debug!("ArgMatcher::needs_more_vals: max_vals...{}", num);
            current_num < num
        } else if o.min_vals.is_some() {
            debug!("ArgMatcher::needs_more_vals: min_vals...true");
            true
        } else {
            o.is_multiple_values_set()
        }
    }

    pub(crate) fn pending_arg_id(&self) -> Option<&Id> {
        self.pending.as_ref().map(|p| &p.id)
    }

    pub(crate) fn pending_values_mut(
        &mut self,
        id: &Id,
        ident: Option<Identifier>,
    ) -> &mut Vec<OsString> {
        let pending = self.pending.get_or_insert_with(|| PendingArg {
            id: id.clone(),
            ident,
            raw_vals: Default::default(),
        });
        debug_assert_eq!(pending.id, *id, "{}", INTERNAL_ERROR_MSG);
        if ident.is_some() {
            debug_assert_eq!(pending.ident, ident, "{}", INTERNAL_ERROR_MSG);
        }
        &mut pending.raw_vals
    }

    pub(crate) fn take_pending(&mut self) -> Option<PendingArg> {
        self.pending.take()
    }
}

impl Deref for ArgMatcher {
    type Target = ArgMatches;

    fn deref(&self) -> &Self::Target {
        &self.matches
    }
}
<|endoftext|>