<commit_before>// Copyright 2018 The Bazel Authors. All rights reserved.
//
// 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.

//! Parse the output of a cargo build.rs script and generate a list of flags and
//! environment variable for the build.
use std::io::{BufRead, BufReader, Read};
use std::process::{Command, Output};

#[derive(Debug, PartialEq, Eq)]
pub struct CompileAndLinkFlags {
    pub compile_flags: String,
    pub link_flags: String,
    pub link_search_paths: String,
}

/// Enum containing all the considered return value from the script
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum BuildScriptOutput {
    /// cargo:rustc-link-lib
    LinkLib(String),
    /// cargo:rustc-link-search
    LinkSearch(String),
    /// cargo:rustc-cfg
    Cfg(String),
    /// cargo:rustc-flags
    Flags(String),
    /// cargo:rustc-link-arg
    LinkArg(String),
    /// cargo:rustc-env
    Env(String),
    /// cargo:VAR=VALUE
    DepEnv(String),
}

impl BuildScriptOutput {
    /// Converts a line into a [BuildScriptOutput] enum.
    ///
    /// Examples
    /// ```rust
    /// assert_eq!(BuildScriptOutput::new("cargo:rustc-link-lib=lib"), Some(BuildScriptOutput::LinkLib("lib".to_owned())));
    /// ```
    fn new(line: &str) -> Option<BuildScriptOutput> {
        let split = line.splitn(2, '=').collect::<Vec<_>>();
        if split.len() <= 1 {
            // Not a cargo directive.
            return None;
        }
        let param = split[1].trim().to_owned();
        let key_split = split[0].splitn(2, ':').collect::<Vec<_>>();
        if key_split.len() <= 1 || key_split[0] != "cargo" {
            // Not a cargo directive.
            return None;
        }

        match key_split[1] {
            "rustc-link-lib" => Some(BuildScriptOutput::LinkLib(param)),
            "rustc-link-search" => Some(BuildScriptOutput::LinkSearch(param)),
            "rustc-cfg" => Some(BuildScriptOutput::Cfg(param)),
            "rustc-flags" => Some(BuildScriptOutput::Flags(param)),
            "rustc-link-arg" => Some(BuildScriptOutput::LinkArg(param)),
            "rustc-env" => Some(BuildScriptOutput::Env(param)),
            "rerun-if-changed" | "rerun-if-env-changed" =>
            // Ignored because Bazel will re-run if those change all the time.
            {
                None
            }
            "warning" => {
                eprint!("Build Script Warning: {}", split[1]);
                None
            }
            "rustc-cdylib-link-arg" | "rustc-link-arg-bin" | "rustc-link-arg-bins" => {
                // cargo:rustc-cdylib-link-arg=FLAG — Passes custom flags to a linker for cdylib crates.
                // cargo:rustc-link-arg-bin=BIN=FLAG – Passes custom flags to a linker for the binary BIN.
                // cargo:rustc-link-arg-bins=FLAG – Passes custom flags to a linker for binaries.
                eprint!(
                    "Warning: build script returned unsupported directive `{}`",
                    split[0]
                );
                None
            }
            _ => {
                // cargo:KEY=VALUE — Metadata, used by links scripts.
                Some(BuildScriptOutput::DepEnv(format!(
                    "{}={}",
                    key_split[1].to_uppercase(),
                    param
                )))
            }
        }
    }

    /// Converts a [BufReader] into a vector of [BuildScriptOutput] enums.
    fn outputs_from_reader<T: Read>(mut reader: BufReader<T>) -> Vec<BuildScriptOutput> {
        let mut result = Vec::<BuildScriptOutput>::new();
        let mut line = String::new();
        while reader.read_line(&mut line).expect("Cannot read line") != 0 {
            if let Some(bso) = BuildScriptOutput::new(&line) {
                result.push(bso);
            }
            line.clear();
        }
        result
    }

    /// Take a [Command], execute it and converts its input into a vector of [BuildScriptOutput]
    pub fn outputs_from_command(
        cmd: &mut Command,
    ) -> Result<(Vec<BuildScriptOutput>, Output), Output> {
        let child_output = cmd.output().expect("Unable to start binary");
        if child_output.status.success() {
            let reader = BufReader::new(child_output.stdout.as_slice());
            let output = Self::outputs_from_reader(reader);
            Ok((output, child_output))
        } else {
            Err(child_output)
        }
    }

    /// Convert a vector of [BuildScriptOutput] into a list of environment variables.
    pub fn outputs_to_env(outputs: &[BuildScriptOutput], exec_root: &str) -> String {
        outputs
            .iter()
            .filter_map(|x| {
                if let BuildScriptOutput::Env(env) = x {
                    Some(Self::escape_for_serializing(Self::redact_exec_root(
                        env, exec_root,
                    )))
                } else {
                    None
                }
            })
            .collect::<Vec<_>>()
            .join("\n")
    }

    /// Convert a vector of [BuildScriptOutput] into a list of dependencies environment variables.
    pub fn outputs_to_dep_env(
        outputs: &[BuildScriptOutput],
        crate_links: &str,
        exec_root: &str,
    ) -> String {
        let prefix = format!("DEP_{}_", crate_links.replace('-', "_").to_uppercase());
        outputs
            .iter()
            .filter_map(|x| {
                if let BuildScriptOutput::DepEnv(env) = x {
                    Some(format!(
                        "{}{}",
                        prefix,
                        Self::escape_for_serializing(Self::redact_exec_root(env, exec_root))
                    ))
                } else {
                    None
                }
            })
            .collect::<Vec<_>>()
            .join("\n")
    }

    /// Convert a vector of [BuildScriptOutput] into a flagfile.
    pub fn outputs_to_flags(outputs: &[BuildScriptOutput], exec_root: &str) -> CompileAndLinkFlags {
        let mut compile_flags = Vec::new();
        let mut link_flags = Vec::new();
        let mut link_search_paths = Vec::new();

        for flag in outputs {
            match flag {
                BuildScriptOutput::Cfg(e) => compile_flags.push(format!("--cfg={}", e)),
                BuildScriptOutput::Flags(e) => compile_flags.push(e.to_owned()),
                BuildScriptOutput::LinkArg(e) => compile_flags.push(format!("-Clink-arg={}", e)),
                BuildScriptOutput::LinkLib(e) => link_flags.push(format!("-l{}", e)),
                BuildScriptOutput::LinkSearch(e) => link_search_paths.push(format!("-L{}", e)),
                _ => {}
            }
        }

        CompileAndLinkFlags {
            compile_flags: compile_flags.join("\n"),
            link_flags: Self::redact_exec_root(&link_flags.join("\n"), exec_root),
            link_search_paths: Self::redact_exec_root(&link_search_paths.join("\n"), exec_root),
        }
    }

    fn redact_exec_root(value: &str, exec_root: &str) -> String {
        value.replace(exec_root, "${pwd}")
    }

    // The process-wrapper treats trailing backslashes as escapes for following newlines.
    // If the env var ends with a backslash (and accordingly doesn't have a following newline),
    // escape it so that it doesn't get turned into a newline by the process-wrapper.
    //
    // Note that this code doesn't handle newlines in strings - that's because Cargo treats build
    // script output as single-line-oriented, so stops processing at the end of a line regardless.
    fn escape_for_serializing(mut value: String) -> String {
        if value.ends_with('\\') {
            value.push('\\');
        }
        value
    }
}

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

    #[test]
    fn test_from_read_buffer_to_env_and_flags() {
        let buff = Cursor::new(
            "
cargo:rustc-link-lib=sdfsdf
cargo:rustc-env=FOO=BAR
cargo:rustc-link-search=/some/absolute/path/bleh
cargo:rustc-env=BAR=FOO
cargo:rustc-flags=-Lblah
cargo:rerun-if-changed=ignored
cargo:rustc-cfg=feature=awesome
cargo:version=123
cargo:version_number=1010107f
cargo:include_path=/some/absolute/path/include
cargo:rustc-env=SOME_PATH=/some/absolute/path/beep
cargo:rustc-link-arg=-weak_framework
cargo:rustc-link-arg=Metal
",
        );
        let reader = BufReader::new(buff);
        let result = BuildScriptOutput::outputs_from_reader(reader);
        assert_eq!(result.len(), 12);
        assert_eq!(result[0], BuildScriptOutput::LinkLib("sdfsdf".to_owned()));
        assert_eq!(result[1], BuildScriptOutput::Env("FOO=BAR".to_owned()));
        assert_eq!(
            result[2],
            BuildScriptOutput::LinkSearch("/some/absolute/path/bleh".to_owned())
        );
        assert_eq!(result[3], BuildScriptOutput::Env("BAR=FOO".to_owned()));
        assert_eq!(result[4], BuildScriptOutput::Flags("-Lblah".to_owned()));
        assert_eq!(
            result[5],
            BuildScriptOutput::Cfg("feature=awesome".to_owned())
        );
        assert_eq!(
            result[6],
            BuildScriptOutput::DepEnv("VERSION=123".to_owned())
        );
        assert_eq!(
            result[7],
            BuildScriptOutput::DepEnv("VERSION_NUMBER=1010107f".to_owned())
        );
        assert_eq!(
            result[9],
            BuildScriptOutput::Env("SOME_PATH=/some/absolute/path/beep".to_owned())
        );
        assert_eq!(
            result[10],
            BuildScriptOutput::LinkArg("-weak_framework".to_owned())
        );
        assert_eq!(result[11], BuildScriptOutput::LinkArg("Metal".to_owned()));

        assert_eq!(
            BuildScriptOutput::outputs_to_dep_env(&result, "ssh2", "/some/absolute/path"),
            "DEP_SSH2_VERSION=123\nDEP_SSH2_VERSION_NUMBER=1010107f\nDEP_SSH2_INCLUDE_PATH=${pwd}/include".to_owned()
        );
        assert_eq!(
            BuildScriptOutput::outputs_to_env(&result, "/some/absolute/path"),
            "FOO=BAR\nBAR=FOO\nSOME_PATH=${pwd}/beep".to_owned()
        );
        assert_eq!(
            BuildScriptOutput::outputs_to_flags(&result, "/some/absolute/path"),
            CompileAndLinkFlags {
                // -Lblah was output as a rustc-flags, so even though it probably _should_ be a link
                // flag, we don't treat it like one.
                compile_flags:
                    "-Lblah\n--cfg=feature=awesome\n-Clink-arg=-weak_framework\n-Clink-arg=Metal"
                        .to_owned(),
                link_flags: "-lsdfsdf".to_owned(),
                link_search_paths: "-L${pwd}/bleh".to_owned(),
            }
        );
    }
}
<commit_msg>Ignore non-utf8 text in build script output (#1583)<commit_after>// Copyright 2018 The Bazel Authors. All rights reserved.
//
// 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.

//! Parse the output of a cargo build.rs script and generate a list of flags and
//! environment variable for the build.
use std::io::{BufRead, BufReader, Read};
use std::process::{Command, Output};

#[derive(Debug, PartialEq, Eq)]
pub struct CompileAndLinkFlags {
    pub compile_flags: String,
    pub link_flags: String,
    pub link_search_paths: String,
}

/// Enum containing all the considered return value from the script
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum BuildScriptOutput {
    /// cargo:rustc-link-lib
    LinkLib(String),
    /// cargo:rustc-link-search
    LinkSearch(String),
    /// cargo:rustc-cfg
    Cfg(String),
    /// cargo:rustc-flags
    Flags(String),
    /// cargo:rustc-link-arg
    LinkArg(String),
    /// cargo:rustc-env
    Env(String),
    /// cargo:VAR=VALUE
    DepEnv(String),
}

impl BuildScriptOutput {
    /// Converts a line into a [BuildScriptOutput] enum.
    ///
    /// Examples
    /// ```rust
    /// assert_eq!(BuildScriptOutput::new("cargo:rustc-link-lib=lib"), Some(BuildScriptOutput::LinkLib("lib".to_owned())));
    /// ```
    fn new(line: &str) -> Option<BuildScriptOutput> {
        let split = line.splitn(2, '=').collect::<Vec<_>>();
        if split.len() <= 1 {
            // Not a cargo directive.
            return None;
        }
        let param = split[1].trim().to_owned();
        let key_split = split[0].splitn(2, ':').collect::<Vec<_>>();
        if key_split.len() <= 1 || key_split[0] != "cargo" {
            // Not a cargo directive.
            return None;
        }

        match key_split[1] {
            "rustc-link-lib" => Some(BuildScriptOutput::LinkLib(param)),
            "rustc-link-search" => Some(BuildScriptOutput::LinkSearch(param)),
            "rustc-cfg" => Some(BuildScriptOutput::Cfg(param)),
            "rustc-flags" => Some(BuildScriptOutput::Flags(param)),
            "rustc-link-arg" => Some(BuildScriptOutput::LinkArg(param)),
            "rustc-env" => Some(BuildScriptOutput::Env(param)),
            "rerun-if-changed" | "rerun-if-env-changed" =>
            // Ignored because Bazel will re-run if those change all the time.
            {
                None
            }
            "warning" => {
                eprint!("Build Script Warning: {}", split[1]);
                None
            }
            "rustc-cdylib-link-arg" | "rustc-link-arg-bin" | "rustc-link-arg-bins" => {
                // cargo:rustc-cdylib-link-arg=FLAG — Passes custom flags to a linker for cdylib crates.
                // cargo:rustc-link-arg-bin=BIN=FLAG – Passes custom flags to a linker for the binary BIN.
                // cargo:rustc-link-arg-bins=FLAG – Passes custom flags to a linker for binaries.
                eprint!(
                    "Warning: build script returned unsupported directive `{}`",
                    split[0]
                );
                None
            }
            _ => {
                // cargo:KEY=VALUE — Metadata, used by links scripts.
                Some(BuildScriptOutput::DepEnv(format!(
                    "{}={}",
                    key_split[1].to_uppercase(),
                    param
                )))
            }
        }
    }

    /// Converts a [BufReader] into a vector of [BuildScriptOutput] enums.
    fn outputs_from_reader<T: Read>(mut reader: BufReader<T>) -> Vec<BuildScriptOutput> {
        let mut result = Vec::<BuildScriptOutput>::new();
        let mut buf = Vec::new();
        while reader
            .read_until(b'\n', &mut buf)
            .expect("Cannot read line")
            != 0
        {
            // like cargo, ignore any lines that are not valid utf8
            if let Ok(line) = String::from_utf8(buf.clone()) {
                if let Some(bso) = BuildScriptOutput::new(&line) {
                    result.push(bso);
                }
            }
            buf.clear();
        }
        result
    }

    /// Take a [Command], execute it and converts its input into a vector of [BuildScriptOutput]
    pub fn outputs_from_command(
        cmd: &mut Command,
    ) -> Result<(Vec<BuildScriptOutput>, Output), Output> {
        let child_output = cmd.output().expect("Unable to start binary");
        if child_output.status.success() {
            let reader = BufReader::new(child_output.stdout.as_slice());
            let output = Self::outputs_from_reader(reader);
            Ok((output, child_output))
        } else {
            Err(child_output)
        }
    }

    /// Convert a vector of [BuildScriptOutput] into a list of environment variables.
    pub fn outputs_to_env(outputs: &[BuildScriptOutput], exec_root: &str) -> String {
        outputs
            .iter()
            .filter_map(|x| {
                if let BuildScriptOutput::Env(env) = x {
                    Some(Self::escape_for_serializing(Self::redact_exec_root(
                        env, exec_root,
                    )))
                } else {
                    None
                }
            })
            .collect::<Vec<_>>()
            .join("\n")
    }

    /// Convert a vector of [BuildScriptOutput] into a list of dependencies environment variables.
    pub fn outputs_to_dep_env(
        outputs: &[BuildScriptOutput],
        crate_links: &str,
        exec_root: &str,
    ) -> String {
        let prefix = format!("DEP_{}_", crate_links.replace('-', "_").to_uppercase());
        outputs
            .iter()
            .filter_map(|x| {
                if let BuildScriptOutput::DepEnv(env) = x {
                    Some(format!(
                        "{}{}",
                        prefix,
                        Self::escape_for_serializing(Self::redact_exec_root(env, exec_root))
                    ))
                } else {
                    None
                }
            })
            .collect::<Vec<_>>()
            .join("\n")
    }

    /// Convert a vector of [BuildScriptOutput] into a flagfile.
    pub fn outputs_to_flags(outputs: &[BuildScriptOutput], exec_root: &str) -> CompileAndLinkFlags {
        let mut compile_flags = Vec::new();
        let mut link_flags = Vec::new();
        let mut link_search_paths = Vec::new();

        for flag in outputs {
            match flag {
                BuildScriptOutput::Cfg(e) => compile_flags.push(format!("--cfg={}", e)),
                BuildScriptOutput::Flags(e) => compile_flags.push(e.to_owned()),
                BuildScriptOutput::LinkArg(e) => compile_flags.push(format!("-Clink-arg={}", e)),
                BuildScriptOutput::LinkLib(e) => link_flags.push(format!("-l{}", e)),
                BuildScriptOutput::LinkSearch(e) => link_search_paths.push(format!("-L{}", e)),
                _ => {}
            }
        }

        CompileAndLinkFlags {
            compile_flags: compile_flags.join("\n"),
            link_flags: Self::redact_exec_root(&link_flags.join("\n"), exec_root),
            link_search_paths: Self::redact_exec_root(&link_search_paths.join("\n"), exec_root),
        }
    }

    fn redact_exec_root(value: &str, exec_root: &str) -> String {
        value.replace(exec_root, "${pwd}")
    }

    // The process-wrapper treats trailing backslashes as escapes for following newlines.
    // If the env var ends with a backslash (and accordingly doesn't have a following newline),
    // escape it so that it doesn't get turned into a newline by the process-wrapper.
    //
    // Note that this code doesn't handle newlines in strings - that's because Cargo treats build
    // script output as single-line-oriented, so stops processing at the end of a line regardless.
    fn escape_for_serializing(mut value: String) -> String {
        if value.ends_with('\\') {
            value.push('\\');
        }
        value
    }
}

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

    #[test]
    fn test_from_read_buffer_to_env_and_flags() {
        let buff = Cursor::new(
            "
cargo:rustc-link-lib=sdfsdf
cargo:rustc-env=FOO=BAR
cargo:rustc-link-search=/some/absolute/path/bleh
cargo:rustc-env=BAR=FOO
cargo:rustc-flags=-Lblah
cargo:rerun-if-changed=ignored
cargo:rustc-cfg=feature=awesome
cargo:version=123
cargo:version_number=1010107f
cargo:include_path=/some/absolute/path/include
cargo:rustc-env=SOME_PATH=/some/absolute/path/beep
cargo:rustc-link-arg=-weak_framework
cargo:rustc-link-arg=Metal
cargo:rustc-env=no_trailing_newline=true",
        );
        let reader = BufReader::new(buff);
        let result = BuildScriptOutput::outputs_from_reader(reader);
        assert_eq!(result.len(), 13);
        assert_eq!(result[0], BuildScriptOutput::LinkLib("sdfsdf".to_owned()));
        assert_eq!(result[1], BuildScriptOutput::Env("FOO=BAR".to_owned()));
        assert_eq!(
            result[2],
            BuildScriptOutput::LinkSearch("/some/absolute/path/bleh".to_owned())
        );
        assert_eq!(result[3], BuildScriptOutput::Env("BAR=FOO".to_owned()));
        assert_eq!(result[4], BuildScriptOutput::Flags("-Lblah".to_owned()));
        assert_eq!(
            result[5],
            BuildScriptOutput::Cfg("feature=awesome".to_owned())
        );
        assert_eq!(
            result[6],
            BuildScriptOutput::DepEnv("VERSION=123".to_owned())
        );
        assert_eq!(
            result[7],
            BuildScriptOutput::DepEnv("VERSION_NUMBER=1010107f".to_owned())
        );
        assert_eq!(
            result[9],
            BuildScriptOutput::Env("SOME_PATH=/some/absolute/path/beep".to_owned())
        );
        assert_eq!(
            result[10],
            BuildScriptOutput::LinkArg("-weak_framework".to_owned())
        );
        assert_eq!(result[11], BuildScriptOutput::LinkArg("Metal".to_owned()));
        assert_eq!(
            result[12],
            BuildScriptOutput::Env("no_trailing_newline=true".to_owned())
        );
        assert_eq!(
            BuildScriptOutput::outputs_to_dep_env(&result, "ssh2", "/some/absolute/path"),
            "DEP_SSH2_VERSION=123\nDEP_SSH2_VERSION_NUMBER=1010107f\nDEP_SSH2_INCLUDE_PATH=${pwd}/include".to_owned()
        );
        assert_eq!(
            BuildScriptOutput::outputs_to_env(&result, "/some/absolute/path"),
            "FOO=BAR\nBAR=FOO\nSOME_PATH=${pwd}/beep\nno_trailing_newline=true".to_owned()
        );
        assert_eq!(
            BuildScriptOutput::outputs_to_flags(&result, "/some/absolute/path"),
            CompileAndLinkFlags {
                // -Lblah was output as a rustc-flags, so even though it probably _should_ be a link
                // flag, we don't treat it like one.
                compile_flags:
                    "-Lblah\n--cfg=feature=awesome\n-Clink-arg=-weak_framework\n-Clink-arg=Metal"
                        .to_owned(),
                link_flags: "-lsdfsdf".to_owned(),
                link_search_paths: "-L${pwd}/bleh".to_owned(),
            }
        );
    }

    #[test]
    fn invalid_utf8() {
        let buff = Cursor::new(
            b"
cargo:rustc-env=valid1=1
cargo:rustc-env=invalid=\xc3\x28
cargo:rustc-env=valid2=2
",
        );
        let reader = BufReader::new(buff);
        let result = BuildScriptOutput::outputs_from_reader(reader);
        assert_eq!(result.len(), 2);
        assert_eq!(
            &BuildScriptOutput::outputs_to_env(&result, "/some/absolute/path"),
            "valid1=1\nvalid2=2"
        );
    }
}
<|endoftext|>


<commit_before>use super::super::{Y, X, Coord, Grid};
use super::*;

extern crate rand;

use rand::{Rng, distributions};

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum Edge {
    Undecided,
    Line,
    Blank,
}

struct AnswerField {
    height: i32,
    width: i32,
    field: Grid<Edge>,
    seed_idx: Grid<i32>,
    seeds: Vec<Coord>,
    invalid: bool,
}

impl AnswerField {
    fn new(height: i32, width: i32) -> AnswerField {
        let mut ret = AnswerField {
            height: height,
            width: width,
            field: Grid::new(2 * height - 1, 2 * width - 1, Edge::Undecided),
            seed_idx: Grid::new(2 * height - 1, 2 * width - 1, -1),
            seeds: vec![],
            invalid: false,
        };

        ret.seeds.push((Y(0), X(0)));
        ret.seeds.push((Y(0), X(2 * width - 2)));
        ret.seeds.push((Y(2 * height - 2), X(0)));
        ret.seeds.push((Y(2 * height - 2), X(2 * width - 2)));
        ret.seed_idx[(Y(0), X(0))] = 0;
        ret.seed_idx[(Y(0), X(2 * width - 2))] = 1;
        ret.seed_idx[(Y(2 * height - 2), X(0))] = 2;
        ret.seed_idx[(Y(2 * height - 2), X(2 * width - 2))] = 3;
        ret
    }
    fn get(&self, cd: Coord) -> Edge {
        if self.field.is_valid_coord(cd) {
            self.field[cd]
        } else {
            Edge::Blank
        }
    }
    /// Counts the number of (Line, Undecided) around `cd`
    fn count_neighbor(&self, cd: Coord) -> (i32, i32) {
        let (Y(y), X(x)) = cd;
        let mut line = 0;
        let mut undecided = 0;
        let dirs = [(1, 0), (0, 1), (-1, 0), (0, -1)];
        for &(dy, dx) in &dirs {
            let e = self.get((Y(y + dy), X(x + dx)));
            if e == Edge::Line {
                line += 1;
            } else if e == Edge::Undecided {
                undecided += 1;
            }
        }
        (line, undecided)
    }
    /// Returns all neighbors whose state is `Undecided` around `cd`
    fn undecided_neighbors(&self, cd: Coord) -> Vec<Coord> {
        let (Y(y), X(x)) = cd;
        let mut ret = vec![];
        let dirs = [(1, 0), (0, 1), (-1, 0), (0, -1)];
        for &(dy, dx) in &dirs {
            let cd2 = (Y(y + dy), X(x + dx));
            let e = self.get(cd2);
            if e == Edge::Undecided {
                ret.push(cd2);
            }
        }
        ret
    }
    /// Returns whether vertex `cd` is a seed
    fn is_seed(&self, cd: Coord) -> bool {
        let nb = self.count_neighbor(cd);
        nb == (0, 2) || (nb.0 == 1 && nb.1 > 0)
    }
    fn num_seeds(&self) -> usize { self.seeds.len() }

    /// Returns whether there is at least one seed
    fn has_seed(&self) -> bool {
        self.seeds.len() != 0
    }
    /// Returns a random seed using `rng`
    fn random_seed<R: Rng>(&self, rng: &mut R) -> Coord {
        let idx = rng.gen_range(0, self.seeds.len());
        self.seeds[idx]
    }
    fn decide(&mut self, cd: Coord, state: Edge) {
        let current = self.field[cd];
        if current != Edge::Undecided {
            if current != state {
                self.invalid = true;
            }
            return;
        }
        self.field[cd] = state;

        // check incident vertices
        let (Y(y), X(x)) = cd;
        if y % 2 == 1 {
            self.inspect((Y(y - 1), X(x)));
            self.inspect((Y(y + 1), X(x)));
        } else {
            self.inspect((Y(y), X(x - 1)));
            self.inspect((Y(y), X(x + 1)));
        }

        // check for canonization rule
        if state == Edge::Line {
            if y % 2 == 1 {
                let related = [
                    (Y(y), X(x - 2)),
                    (Y(y - 1), X(x - 1)),
                    (Y(y + 1), X(x - 1)),
                ];
                for i in 0..3 {
                    if self.get(related[i]) == Edge::Line {
                        self.decide(related[(i + 1) % 3], Edge::Blank);
                        self.decide(related[(i + 2) % 3], Edge::Blank);
                    }
                }
                let related = [
                    (Y(y), X(x + 2)),
                    (Y(y - 1), X(x + 1)),
                    (Y(y + 1), X(x + 1)),
                ];
                for i in 0..3 {
                    if self.get(related[i]) == Edge::Line {
                        self.decide(related[(i + 1) % 3], Edge::Blank);
                        self.decide(related[(i + 2) % 3], Edge::Blank);
                    }
                }
            } else {
                let related = [
                    (Y(y - 2), X(x)),
                    (Y(y - 1), X(x - 1)),
                    (Y(y - 1), X(x + 1)),
                ];
                for i in 0..3 {
                    if self.get(related[i]) == Edge::Line {
                        self.decide(related[(i + 1) % 3], Edge::Blank);
                        self.decide(related[(i + 2) % 3], Edge::Blank);
                    }
                }
                let related = [
                    (Y(y + 2), X(x)),
                    (Y(y + 1), X(x - 1)),
                    (Y(y + 1), X(x + 1)),
                ];
                for i in 0..3 {
                    if self.get(related[i]) == Edge::Line {
                        self.decide(related[(i + 1) % 3], Edge::Blank);
                        self.decide(related[(i + 2) % 3], Edge::Blank);
                    }
                }
            }
        }
    }
    /// Inspect vertex (y, x)
    fn inspect(&mut self, (Y(y), X(x)): Coord) {
        let (line, undecided) = self.count_neighbor((Y(y), X(x)));
        let dirs = [(1, 0), (0, 1), (-1, 0), (0, -1)];
        if line == 0 {
            if undecided == 0 {
                self.invalid = true;
                return;
            }
            if undecided == 1 {
                for &(dy, dx) in &dirs {
                    let e = self.get((Y(y + dy), X(x + dx)));
                    if e == Edge::Undecided {
                        self.decide((Y(y + dy), X(x + dx)), Edge::Line);
                    }
                }
            }
        } else if line == 2 {
            for &(dy, dx) in &dirs {
                let e = self.get((Y(y + dy), X(x + dx)));
                if e == Edge::Undecided {
                    self.decide((Y(y + dy), X(x + dx)), Edge::Blank);
                }
            }
        }

        let is_seed = self.is_seed((Y(y), X(x)));
        let seed_idx = self.seed_idx[(Y(y), X(x))];

        if seed_idx != -1 && !is_seed {
            // (y, x) is no longer a seed
            let moved = self.seeds[self.seeds.len() - 1];
            self.seed_idx[moved] = seed_idx;
            self.seeds.swap_remove(seed_idx as usize);
            self.seed_idx[(Y(y), X(x))] = -1;
        } else if seed_idx == -1 && is_seed {
            // (y, x) is now a seed
            self.seed_idx[(Y(y), X(x))] = self.seeds.len() as i32;
            self.seeds.push((Y(y), X(x)));
        }
    }
}

pub fn generate_placement<R: Rng>(height: i32, width: i32, rng: &mut R) -> Option<Grid<Clue>> {
    let mut field = AnswerField::new(height, width);

    loop {
        if !field.has_seed() { break; }
        let cd = field.random_seed(rng);

        if field.count_neighbor(cd) == (0, 2) {
            let nbs = field.undecided_neighbors(cd);
            if rng.next_f64() < 0.9f64 {
                // as angle
                field.decide(nbs[0], Edge::Line);
                field.decide(nbs[1], Edge::Line);
            } else {
                // as an endpoint
                let i = rng.gen_range(0, 2);
                field.decide(nbs[i], Edge::Line);
                field.decide(nbs[(1 - i)], Edge::Blank);
            }
        } else {
            let nbs = field.undecided_neighbors(cd);

            if rng.next_f64() < 1.0f64 {
                // extend
                let i = rng.gen_range(0, nbs.len());
                field.decide(nbs[i], Edge::Line);
            } else {
                // terminate
                for nb in nbs {
                    field.decide(nb, Edge::Blank);
                }
            }
        }

        if field.invalid { return None; }
    }

    let mut ids = Grid::new(height, width, -1);
    let mut id = 1;
    for y in 0..height {
        for x in 0..width {
            if ids[(Y(y), X(x))] == -1 {
                fill_line_id((Y(y), X(x)), &field, &mut ids, id);
                id += 1;
            }
        }
    }

    let mut line_len = vec![0; id as usize];
    for y in 0..height {
        for x in 0..width {
            line_len[ids[(Y(y), X(x))] as usize] += 1;
        }
    }
    for i in 1..id {
        if line_len[i as usize] <= 3 { return None; }
    }

    let mut end_count = vec![0; id as usize];
    for y in 0..height {
        for x in 0..width {
            if field.count_neighbor((Y(y * 2), X(x * 2))) == (1, 0) {
                end_count[ids[(Y(y), X(x))] as usize] += 1;
            }
        }
    }
    for i in 1..id {
        if end_count[i as usize] != 2 { return None; }
    }

    for y in 0..(2 * height - 1) {
        for x in 0..(2 * width - 1) {
            if y % 2 == 1 && x % 2 == 0 {
                if (ids[(Y(y / 2), X(x / 2))] == ids[(Y(y / 2 + 1), X(x / 2))]) != (field.get((Y(y), X(x))) == Edge::Line) { return None; }
            } else if y % 2 == 0 && x % 2 == 1 {
                if (ids[(Y(y / 2), X(x / 2))] == ids[(Y(y / 2), X(x / 2 + 1))]) != (field.get((Y(y), X(x))) == Edge::Line) { return None; }
            }
        }
    }

    let mut ret = Grid::new(height, width, NO_CLUE);
    for y in 0..height {
        for x in 0..width {
            if field.count_neighbor((Y(y * 2), X(x * 2))) == (1, 0) {
                ret[(Y(y), X(x))] = Clue(ids[(Y(y), X(x))]);
            }
        }
    }

    Some(ret)
}
fn fill_line_id(cd: Coord, field: &AnswerField, ids: &mut Grid<i32>, id: i32) {
    if ids[cd] != -1 { return; }
    ids[cd] = id;
    let (Y(y), X(x)) = cd;

    let dirs = [(1, 0), (0, 1), (-1, 0), (0, -1)];
    for &(dy, dx) in &dirs {
        if field.get((Y(y * 2 + dy), X(x * 2 + dx))) == Edge::Line {
            fill_line_id((Y(y + dy), X(x + dx)), field, ids, id);
        }
    }
}
<commit_msg>Numberlink: add chain_union and chain_length to AnswerField<commit_after>use super::super::{Y, X, Coord, Grid};
use super::*;

extern crate rand;

use rand::{Rng, distributions};

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum Edge {
    Undecided,
    Line,
    Blank,
}

struct AnswerField {
    height: i32,
    width: i32,
    chain_union: Grid<usize>, // height * width
    chain_length: Grid<i32>, // height * width
    field: Grid<Edge>, // (2 * height - 1) * (2 * width - 1)
    seed_idx: Grid<i32>,
    seeds: Vec<Coord>,
    endpoints: i32,
    invalid: bool,
}

impl AnswerField {
    fn new(height: i32, width: i32) -> AnswerField {
        let mut ret = AnswerField {
            height: height,
            width: width,
            chain_union: Grid::new(height, width, 0),
            chain_length: Grid::new(height, width, 0),
            field: Grid::new(2 * height - 1, 2 * width - 1, Edge::Undecided),
            seed_idx: Grid::new(2 * height - 1, 2 * width - 1, -1),
            seeds: vec![],
            endpoints: 0,
            invalid: false,
        };

        for idx in 0..((height * width) as usize) {
            ret.chain_union[idx] = idx;
        }

        ret.seeds.push((Y(0), X(0)));
        ret.seeds.push((Y(0), X(2 * width - 2)));
        ret.seeds.push((Y(2 * height - 2), X(0)));
        ret.seeds.push((Y(2 * height - 2), X(2 * width - 2)));
        ret.seed_idx[(Y(0), X(0))] = 0;
        ret.seed_idx[(Y(0), X(2 * width - 2))] = 1;
        ret.seed_idx[(Y(2 * height - 2), X(0))] = 2;
        ret.seed_idx[(Y(2 * height - 2), X(2 * width - 2))] = 3;
        ret
    }
    fn get(&self, cd: Coord) -> Edge {
        if self.field.is_valid_coord(cd) {
            self.field[cd]
        } else {
            Edge::Blank
        }
    }
    /// Counts the number of (Line, Undecided) around `cd`
    fn count_neighbor(&self, cd: Coord) -> (i32, i32) {
        let (Y(y), X(x)) = cd;
        let mut line = 0;
        let mut undecided = 0;
        let dirs = [(1, 0), (0, 1), (-1, 0), (0, -1)];
        for &(dy, dx) in &dirs {
            let e = self.get((Y(y + dy), X(x + dx)));
            if e == Edge::Line {
                line += 1;
            } else if e == Edge::Undecided {
                undecided += 1;
            }
        }
        (line, undecided)
    }
    /// Returns all neighbors whose state is `Undecided` around `cd`
    fn undecided_neighbors(&self, cd: Coord) -> Vec<Coord> {
        let (Y(y), X(x)) = cd;
        let mut ret = vec![];
        let dirs = [(1, 0), (0, 1), (-1, 0), (0, -1)];
        for &(dy, dx) in &dirs {
            let cd2 = (Y(y + dy), X(x + dx));
            let e = self.get(cd2);
            if e == Edge::Undecided {
                ret.push(cd2);
            }
        }
        ret
    }
    /// Returns whether vertex `cd` is a seed
    fn is_seed(&self, cd: Coord) -> bool {
        let nb = self.count_neighbor(cd);
        nb == (0, 2) || (nb.0 == 1 && nb.1 > 0)
    }
    fn num_seeds(&self) -> usize { self.seeds.len() }

    /// Returns whether there is at least one seed
    fn has_seed(&self) -> bool {
        self.seeds.len() != 0
    }
    /// Returns a random seed using `rng`
    fn random_seed<R: Rng>(&self, rng: &mut R) -> Coord {
        let idx = rng.gen_range(0, self.seeds.len());
        self.seeds[idx]
    }
    fn decide(&mut self, cd: Coord, state: Edge) {
        let current = self.field[cd];
        if current != Edge::Undecided {
            if current != state {
                self.invalid = true;
            }
            return;
        }
        self.field[cd] = state;

        let (Y(y), X(x)) = cd;

        // update chain information
        if state == Edge::Line {
            let end1 = (Y(y / 2), X(x / 2));
            let end2 = (Y((y + 1) / 2), X((x + 1) / 2));

            let end1_id = self.chain_union.index(end1);
            let end2_id = self.chain_union.index(end2);
            let another_end1_id = self.chain_union[end1_id];
            let another_end2_id = self.chain_union[end2_id];

            if another_end1_id == end2_id {
                // invalid: a self-loop will be formed
                self.invalid = true;
                return;
            }

            let new_length = self.chain_length[end1_id] + self.chain_length[end2_id] + 1;

            self.chain_union[another_end1_id] = another_end2_id;
            self.chain_union[another_end2_id] = another_end1_id;
            self.chain_length[another_end1_id] = new_length;
            self.chain_length[another_end2_id] = new_length;
        }

        // check incident vertices
        if y % 2 == 1 {
            if self.count_neighbor((Y(y - 1), X(x))) == (1, 0) { self.endpoints += 1; }
            if self.count_neighbor((Y(y + 1), X(x))) == (1, 0) { self.endpoints += 1; }
            self.inspect((Y(y - 1), X(x)));
            self.inspect((Y(y + 1), X(x)));
        } else {
            if self.count_neighbor((Y(y), X(x - 1))) == (1, 0) { self.endpoints += 1; }
            if self.count_neighbor((Y(y), X(x + 1))) == (1, 0) { self.endpoints += 1; }
            self.inspect((Y(y), X(x - 1)));
            self.inspect((Y(y), X(x + 1)));
        }

        // check for canonization rule
        if state == Edge::Line {
            if y % 2 == 1 {
                let related = [
                    (Y(y), X(x - 2)),
                    (Y(y - 1), X(x - 1)),
                    (Y(y + 1), X(x - 1)),
                ];
                for i in 0..3 {
                    if self.get(related[i]) == Edge::Line {
                        self.decide(related[(i + 1) % 3], Edge::Blank);
                        self.decide(related[(i + 2) % 3], Edge::Blank);
                    }
                }
                let related = [
                    (Y(y), X(x + 2)),
                    (Y(y - 1), X(x + 1)),
                    (Y(y + 1), X(x + 1)),
                ];
                for i in 0..3 {
                    if self.get(related[i]) == Edge::Line {
                        self.decide(related[(i + 1) % 3], Edge::Blank);
                        self.decide(related[(i + 2) % 3], Edge::Blank);
                    }
                }
            } else {
                let related = [
                    (Y(y - 2), X(x)),
                    (Y(y - 1), X(x - 1)),
                    (Y(y - 1), X(x + 1)),
                ];
                for i in 0..3 {
                    if self.get(related[i]) == Edge::Line {
                        self.decide(related[(i + 1) % 3], Edge::Blank);
                        self.decide(related[(i + 2) % 3], Edge::Blank);
                    }
                }
                let related = [
                    (Y(y + 2), X(x)),
                    (Y(y + 1), X(x - 1)),
                    (Y(y + 1), X(x + 1)),
                ];
                for i in 0..3 {
                    if self.get(related[i]) == Edge::Line {
                        self.decide(related[(i + 1) % 3], Edge::Blank);
                        self.decide(related[(i + 2) % 3], Edge::Blank);
                    }
                }
            }
        }
    }
    /// Inspect vertex (y, x)
    fn inspect(&mut self, (Y(y), X(x)): Coord) {
        let (line, undecided) = self.count_neighbor((Y(y), X(x)));
        let dirs = [(1, 0), (0, 1), (-1, 0), (0, -1)];
        if line == 0 {
            if undecided == 0 {
                self.invalid = true;
                return;
            }
            if undecided == 1 {
                for &(dy, dx) in &dirs {
                    let e = self.get((Y(y + dy), X(x + dx)));
                    if e == Edge::Undecided {
                        self.decide((Y(y + dy), X(x + dx)), Edge::Line);
                    }
                }
            }
        } else if line == 2 {
            for &(dy, dx) in &dirs {
                let e = self.get((Y(y + dy), X(x + dx)));
                if e == Edge::Undecided {
                    self.decide((Y(y + dy), X(x + dx)), Edge::Blank);
                }
            }
        }

        let is_seed = self.is_seed((Y(y), X(x)));
        let seed_idx = self.seed_idx[(Y(y), X(x))];

        if seed_idx != -1 && !is_seed {
            // (y, x) is no longer a seed
            let moved = self.seeds[self.seeds.len() - 1];
            self.seed_idx[moved] = seed_idx;
            self.seeds.swap_remove(seed_idx as usize);
            self.seed_idx[(Y(y), X(x))] = -1;
        } else if seed_idx == -1 && is_seed {
            // (y, x) is now a seed
            self.seed_idx[(Y(y), X(x))] = self.seeds.len() as i32;
            self.seeds.push((Y(y), X(x)));
        }
    }
}

pub fn generate_placement<R: Rng>(height: i32, width: i32, rng: &mut R) -> Option<Grid<Clue>> {
    let mut field = AnswerField::new(height, width);

    loop {
        if !field.has_seed() { break; }
        let cd = field.random_seed(rng);

        if field.count_neighbor(cd) == (0, 2) {
            let nbs = field.undecided_neighbors(cd);
            if rng.next_f64() < 0.9f64 {
                // as angle
                field.decide(nbs[0], Edge::Line);
                field.decide(nbs[1], Edge::Line);
            } else {
                // as an endpoint
                let i = rng.gen_range(0, 2);
                field.decide(nbs[i], Edge::Line);
                field.decide(nbs[(1 - i)], Edge::Blank);
            }
        } else {
            let nbs = field.undecided_neighbors(cd);

            if rng.next_f64() < 1.0f64 {
                // extend
                let i = rng.gen_range(0, nbs.len());
                field.decide(nbs[i], Edge::Line);
            } else {
                // terminate
                for nb in nbs {
                    field.decide(nb, Edge::Blank);
                }
            }
        }

        if field.invalid { return None; }
    }

    let mut ids = Grid::new(height, width, -1);
    let mut id = 1;
    for y in 0..height {
        for x in 0..width {
            if ids[(Y(y), X(x))] == -1 {
                fill_line_id((Y(y), X(x)), &field, &mut ids, id);
                id += 1;
            }
        }
    }

    let mut line_len = vec![0; id as usize];
    for y in 0..height {
        for x in 0..width {
            line_len[ids[(Y(y), X(x))] as usize] += 1;
        }
    }
    for i in 1..id {
        if line_len[i as usize] <= 3 { return None; }
    }

    let mut end_count = vec![0; id as usize];
    for y in 0..height {
        for x in 0..width {
            if field.count_neighbor((Y(y * 2), X(x * 2))) == (1, 0) {
                end_count[ids[(Y(y), X(x))] as usize] += 1;
            }
        }
    }
    for i in 1..id {
        if end_count[i as usize] != 2 { return None; }
    }

    for y in 0..(2 * height - 1) {
        for x in 0..(2 * width - 1) {
            if y % 2 == 1 && x % 2 == 0 {
                if (ids[(Y(y / 2), X(x / 2))] == ids[(Y(y / 2 + 1), X(x / 2))]) != (field.get((Y(y), X(x))) == Edge::Line) { return None; }
            } else if y % 2 == 0 && x % 2 == 1 {
                if (ids[(Y(y / 2), X(x / 2))] == ids[(Y(y / 2), X(x / 2 + 1))]) != (field.get((Y(y), X(x))) == Edge::Line) { return None; }
            }
        }
    }

    let mut ret = Grid::new(height, width, NO_CLUE);
    for y in 0..height {
        for x in 0..width {
            if field.count_neighbor((Y(y * 2), X(x * 2))) == (1, 0) {
                ret[(Y(y), X(x))] = Clue(ids[(Y(y), X(x))]);
            }
        }
    }

    Some(ret)
}
fn fill_line_id(cd: Coord, field: &AnswerField, ids: &mut Grid<i32>, id: i32) {
    if ids[cd] != -1 { return; }
    ids[cd] = id;
    let (Y(y), X(x)) = cd;

    let dirs = [(1, 0), (0, 1), (-1, 0), (0, -1)];
    for &(dy, dx) in &dirs {
        if field.get((Y(y * 2 + dy), X(x * 2 + dx))) == Edge::Line {
            fill_line_id((Y(y + dy), X(x + dx)), field, ids, id);
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (C) 2016-2018 Sebastian Dröge <sebastian@centricular.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Module containing infrastructure for subclassing `GObject`s

#[macro_use]
#[doc(hidden)]
pub mod guard;

pub mod simple;
#[macro_use]
pub mod types;

#[macro_use]
pub mod object;
pub mod properties;

pub mod prelude {
    //! Prelude that re-exports all important traits from this crate
    pub use super::object::{ObjectClassSubclassExt, ObjectImpl};
    pub use super::types::{ClassStruct, InstanceStruct, IsSubclassable, ObjectSubclass};
}

pub use super::subclass::types::register_type;
pub use super::subclass::types::TypeData;
<commit_msg>Re-export some types from the subclass modules<commit_after>// Copyright (C) 2016-2018 Sebastian Dröge <sebastian@centricular.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Module containing infrastructure for subclassing `GObject`s

#[macro_use]
#[doc(hidden)]
pub mod guard;

pub mod simple;
#[macro_use]
pub mod types;

#[macro_use]
pub mod object;
pub mod properties;

pub mod prelude {
    //! Prelude that re-exports all important traits from this crate
    pub use super::object::{ObjectClassSubclassExt, ObjectImpl};
    pub use super::types::{ClassStruct, InstanceStruct, IsSubclassable, ObjectSubclass};
}

pub use self::types::{InitializingType, TypeData, register_type};
<|endoftext|>


<commit_before>//
//   Copyright 2016 Andrew Hunter
//
//   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.
//

//!
//! # Symbol range
//!
//! It's quite common to want to match ranges of symbols (most particularly, 'all' symbols but also things like all upper-case
//! characters). It would be inefficient to store all of the transitions represented by one of these transitions on a per-symbol
//! basis, and impossible for some symbol sets (consider a state machine working on u32s). Instead, transitions are stored as
//! symbol ranges.
//!

///
/// Trait implemented by symbol types that are countable - ie, for types where there's always a next symbol
///
pub trait Countable where Self: Sized {
    ///
    /// Returns the next symbol in order (None if this is the last symbol)
    ///
    fn next(&self) -> Option<Self>;

    ///
    /// Returns the next symbol in order (None if this is the last symbol)
    ///
    fn prev(&self) -> Option<Self>;
}

///
/// Represents a range of symbols
///
/// Symbols must be ordered in order to use them with a range-based state machine.
///
#[derive(Clone, Eq, PartialEq)]
pub struct SymbolRange<Symbol: Ord+Clone> {
    ///
    /// Lowest symbol in the range
    ///
    pub lowest: Symbol,

    ///
    /// Highest symbol in the range
    ///
    /// This is inclusive, so the highest symbol is always included in the range (this makes it differ from Rust's
    /// built-in Range struct, and is important for supporting uncountable symbols)
    ///
    pub highest: Symbol
}

impl<Symbol: Ord+Clone> SymbolRange<Symbol> {
    ///
    /// Creates a new range covering
    ///
    #[inline]
    pub fn new(lowest: Symbol, highest: Symbol) -> SymbolRange<Symbol> {
        if lowest > highest {
            SymbolRange { lowest: highest, highest: lowest }
        } else {
            SymbolRange { lowest: lowest, highest: highest }
        }
    }
}

impl Countable for u8 {
    fn next(&self) -> Option<u8> {
        if *self == u8::max_value() {
            None
        } else {
            Some(self+1)
        }
    }

    fn prev(&self) -> Option<u8> {
        if *self == u8::min_value() {
            None
        } else {
            Some(self-1)
        }
    }
}

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

    #[test]
    fn can_create_range() {
        let range = SymbolRange::new(1, 2);

        assert!(range.lowest == 1);
        assert!(range.highest == 2);
    }

    #[test]
    fn can_create_range_reversed() {
        let range = SymbolRange::new(5, 1);

        assert!(range.lowest == 1);
        assert!(range.highest == 5);
    }
}
<commit_msg>Add a method to detect if two symbol ranges overlap<commit_after>//
//   Copyright 2016 Andrew Hunter
//
//   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.
//

//!
//! # Symbol range
//!
//! It's quite common to want to match ranges of symbols (most particularly, 'all' symbols but also things like all upper-case
//! characters). It would be inefficient to store all of the transitions represented by one of these transitions on a per-symbol
//! basis, and impossible for some symbol sets (consider a state machine working on u32s). Instead, transitions are stored as
//! symbol ranges.
//!

///
/// Trait implemented by symbol types that are countable - ie, for types where there's always a next symbol
///
pub trait Countable where Self: Sized {
    ///
    /// Returns the next symbol in order (None if this is the last symbol)
    ///
    fn next(&self) -> Option<Self>;

    ///
    /// Returns the next symbol in order (None if this is the last symbol)
    ///
    fn prev(&self) -> Option<Self>;
}

///
/// Represents a range of symbols
///
/// Symbols must be ordered in order to use them with a range-based state machine.
///
#[derive(Clone, Eq, PartialEq)]
pub struct SymbolRange<Symbol: Ord+Clone> {
    ///
    /// Lowest symbol in the range
    ///
    pub lowest: Symbol,

    ///
    /// Highest symbol in the range
    ///
    /// This is inclusive, so the highest symbol is always included in the range (this makes it differ from Rust's
    /// built-in Range struct, and is important for supporting uncountable symbols)
    ///
    pub highest: Symbol
}

impl<Symbol: Ord+Clone> SymbolRange<Symbol> {
    ///
    /// Creates a new range covering everything between the specified two symbols
    ///
    #[inline]
    pub fn new(lowest: Symbol, highest: Symbol) -> SymbolRange<Symbol> {
        if lowest > highest {
            SymbolRange { lowest: highest, highest: lowest }
        } else {
            SymbolRange { lowest: lowest, highest: highest }
        }
    }

    ///
    /// True if this range overlaps another
    ///
    #[inline]
    pub fn overlaps(&self, with: &SymbolRange<Symbol>) -> bool {
        if self.highest < with.lowest {
            false
        } else if self.lowest > with.highest {
            false
        } else {
            true
        }
    }
}

impl Countable for u8 {
    fn next(&self) -> Option<u8> {
        if *self == Self::max_value() {
            None
        } else {
            Some(self+1)
        }
    }

    fn prev(&self) -> Option<u8> {
        if *self == Self::min_value() {
            None
        } else {
            Some(self-1)
        }
    }
}

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

    #[test]
    fn can_create_range() {
        let range = SymbolRange::new(1, 2);

        assert!(range.lowest == 1);
        assert!(range.highest == 2);
    }

    #[test]
    fn can_create_range_reversed() {
        let range = SymbolRange::new(5, 1);

        assert!(range.lowest == 1);
        assert!(range.highest == 5);
    }

    #[test]
    fn overlaps_when_within() {
        assert!(SymbolRange::new(1, 4).overlaps(&SymbolRange::new(2, 3)));
    }

    #[test]
    fn overlaps_when_without() {
        assert!(SymbolRange::new(2, 3).overlaps(&SymbolRange::new(1, 4)));
    }

    #[test]
    fn overlaps_when_lower() {
        assert!(SymbolRange::new(1, 3).overlaps(&SymbolRange::new(2, 4)));
    }

    #[test]
    fn overlaps_when_higher() {
        assert!(SymbolRange::new(2, 4).overlaps(&SymbolRange::new(1, 3)));
    }

    #[test]
    fn overlaps_when_same() {
        assert!(SymbolRange::new(1, 4).overlaps(&SymbolRange::new(1, 4)));
    }

    #[test]
    fn does_not_overlap_lower() {
        assert!(!SymbolRange::new(1, 2).overlaps(&SymbolRange::new(4, 5)));
    }

    #[test]
    fn does_not_overlap_higher() {
        assert!(!SymbolRange::new(4, 5).overlaps(&SymbolRange::new(1, 2)));
    }
}
<|endoftext|>


<commit_before>use super::*;
use crate::shapes::{GeoClass, Shape};
use crate::StrError;
use russell_lab::Vector;
use russell_tensor::Tensor2;

/// Defines scalar function (the argument is index of the integration point)
pub type FnScalar = fn(usize) -> f64;

/// Defines vector function (the second argument is index of the integration point)
pub type FnVector = fn(&mut Vector, usize);

/// Defines tensor function (the second argument is index of the integration point)
pub type FnTensor = fn(&mut Tensor2, usize);

impl Shape {
    /// Selects integrations points and weights
    ///
    /// # Options
    ///
    /// ## nip for Lin class
    ///
    /// * `1` -- Legendre points
    /// * `2` -- Legendre points
    /// * `3` -- Legendre points
    /// * `4` -- Legendre points
    /// * `5` -- Legendre points
    ///
    /// ## nip for Tri class
    ///
    /// * `1` -- Internal points
    /// * `3`:
    ///     - `edge` -- Points on edge
    ///     - otherwise, internal points
    /// * `4` -- Internal points
    /// * `12` -- Internal points
    /// * `16` -- Internal points
    ///
    /// ## nip for Qua class
    ///
    /// * `1` -- Legendre points
    /// * `4` -- Legendre points
    /// * `5`:
    ///     - `ws` -- Wilson's "Stable" version with w0=0.004 and wa=0.999 to mimic 4-point rule
    ///     - otherwise, standard Wilson's formula
    /// * `9` -- Legendre points
    /// * `16` -- Legendre points
    ///
    /// ## nip for Tet class
    ///
    /// * `1` -- Internal points
    /// * `4` -- Internal points
    /// * `5` -- Internal points
    /// * `6` -- Internal points
    ///
    /// ## nip for Hex class
    ///
    /// * `6` -- Iron's formula
    /// * `8` -- Legendre points
    /// * `9`:
    ///     - `ws` -- Wilson's "Stable" version
    ///     - otherwise, Wilson's standard formula
    /// * `14` -- Iron's formula
    /// * `27` -- Legendre points
    pub fn select_int_points(&mut self, nip: usize, edge: bool, ws: bool) -> Result<(), StrError> {
        match self.class {
            // Lin
            GeoClass::Lin => match nip {
                1 => self.ip_data = &IP_LIN_LEGENDRE_1,
                2 => self.ip_data = &IP_LIN_LEGENDRE_2,
                3 => self.ip_data = &IP_LIN_LEGENDRE_3,
                4 => self.ip_data = &IP_LIN_LEGENDRE_4,
                5 => self.ip_data = &IP_LIN_LEGENDRE_5,
                _ => return Err("number of integration points is not available for Lin class"),
            },

            // Tri
            GeoClass::Tri => match nip {
                1 => self.ip_data = &IP_TRI_INTERNAL_1,
                3 => {
                    if edge {
                        self.ip_data = &IP_TRI_EDGE_3
                    } else {
                        self.ip_data = &IP_TRI_INTERNAL_3
                    }
                }
                4 => self.ip_data = &IP_TRI_INTERNAL_4,
                12 => self.ip_data = &IP_TRI_INTERNAL_12,
                16 => self.ip_data = &IP_TRI_INTERNAL_16,
                _ => return Err("number of integration points is not available for Tri class"),
            },

            // Qua
            GeoClass::Qua => match nip {
                1 => self.ip_data = &IP_QUA_LEGENDRE_1,
                4 => self.ip_data = &IP_QUA_LEGENDRE_4,
                5 => {
                    if ws {
                        self.ip_data = &IP_QUA_WILSON_STABLE_5
                    } else {
                        self.ip_data = &IP_QUA_WILSON_CORNER_5
                    }
                }
                8 => self.ip_data = &IP_QUA_WILSON_8,
                9 => self.ip_data = &IP_QUA_LEGENDRE_9,
                16 => self.ip_data = &IP_QUA_LEGENDRE_16,
                _ => return Err("number of integration points is not available for Qua class"),
            },

            // Tet
            GeoClass::Tet => match nip {
                1 => self.ip_data = &IP_TET_INTERNAL_1,
                4 => self.ip_data = &IP_TET_INTERNAL_4,
                5 => self.ip_data = &IP_TET_INTERNAL_5,
                6 => self.ip_data = &IP_TET_INTERNAL_6,
                _ => return Err("number of integration points is not available for Tet class"),
            },

            // Hex
            GeoClass::Hex => match nip {
                6 => self.ip_data = &IP_HEX_IRONS_6,
                8 => self.ip_data = &IP_HEX_LEGENDRE_8,
                9 => {
                    if ws {
                        self.ip_data = &IP_HEX_WILSON_STABLE_9
                    } else {
                        self.ip_data = &IP_HEX_WILSON_CORNER_9
                    }
                }
                14 => self.ip_data = &IP_HEX_IRONS_14,
                27 => self.ip_data = &IP_HEX_LEGENDRE_27,
                _ => return Err("number of integration points is not available for Hex class"),
            },
        }
        Ok(())
    }

    /// Performs the Case-A integration
    ///
    /// Case-A, interpolation functions times scalar field:
    ///
    /// ```text
    ///      ⌠    → →     →
    /// aᵐ = │ Nᵐ(x(ξ)) s(x) dΩ
    ///      ⌡
    ///      Ω
    /// ```
    ///
    /// or, for lines in multi-dimensions,
    ///
    /// ```text
    ///      ⌠
    /// aᵐ = │ Nᵐ(ℓ(ξ)) s(ℓ) dℓ
    ///      ⌡
    ///      Ω
    /// ```
    ///
    /// The numerical integration is:
    ///
    /// ```text
    ///     nip-1    →     →       →
    /// aᵐ ≈  Σ   Nᵐ(ιp) s(ιp) |J|(ιp) wp
    ///      p=0
    /// ```
    ///
    /// # Input
    ///
    /// * `fn_s` -- s(x(ξ)) or s(ℓ) scalar function, however written as the
    ///             function of the index of the integration point.
    ///
    /// # Output
    ///
    /// * `a` -- (npoint) aᵐ scalars; result from the integration
    pub fn integ_case_a(&mut self, a: &mut [f64], fn_s: FnScalar) -> Result<(), StrError> {
        // clear results
        a.fill(0.0);

        // loop over integration points
        for index in 0..self.ip_data.len() {
            // ksi coordinates and weight
            let iota = &self.ip_data[index][0..self.geo_ndim];
            let weight = self.ip_data[index][3];

            // calculate interpolation functions and Jacobian
            self.calc_interp(iota);
            let det_jac = self.calc_jacobian(iota)?;

            // loop over points and perform summation
            for m in 0..self.npoint {
                a[m] += self.interp[m] * fn_s(index) * det_jac * weight;
            }
        }
        Ok(())
    }

    /// Performs the Case-B integration
    ///
    /// Case-B, interpolation functions times vector field:
    ///
    /// ```text
    /// →    ⌠    → →   → →
    /// bᵐ = │ Nᵐ(x(ξ)) v(x) dΩ
    ///      ⌡
    ///      Ω
    /// ```
    ///
    /// The numerical integration is:
    ///
    /// ```text
    /// →   nip-1    →   → →       →
    /// bᵐ ≈  Σ   Nᵐ(ιp) v(ιp) |J|(ιp) wp
    ///      p=0
    /// ```
    ///
    /// # Input
    ///
    /// * `fn_v` -- v(x(ξ)) vector function, however written as the
    ///             function of the index of the integration point.
    ///
    /// # Output
    ///
    /// * `b` -- (npoint) bᵐ vectors; result from the integration
    pub fn integ_case_b(&mut self, b: &mut Vec<Vector>, fn_v: FnVector) -> Result<(), StrError> {
        // check
        if b.len() != self.npoint {
            return Err("b.len() must equal npoint");
        }

        // check dims and clear results
        for m in 0..self.npoint {
            if b[m].dim() != self.space_ndim {
                return Err("b[m].dim() must equal space_ndim");
            }
            b[m].fill(0.0);
        }

        // auxiliary vector holding the output of fn_v
        let mut v = Vector::new(self.space_ndim);

        // loop over integration points
        for index in 0..self.ip_data.len() {
            // ksi coordinates and weight
            let iota = &self.ip_data[index][0..self.geo_ndim];
            let weight = self.ip_data[index][3];

            // calculate interpolation functions and Jacobian
            self.calc_interp(iota);
            let det_jac = self.calc_jacobian(iota)?;

            // loop over points and perform summation
            for m in 0..self.npoint {
                fn_v(&mut v, index);
                for i in 0..self.space_ndim {
                    b[m][i] += self.interp[m] * v[i] * det_jac * weight;
                }
            }
        }
        Ok(())
    }

    /// Performs the Case-C integration
    ///
    /// Case-C, vector dot gradient:
    ///
    /// ```text
    ///      ⌠ → →    →  → →
    /// cᵐ = │ w(x) · Gᵐ(x(ξ)) dΩ
    ///      ⌡
    ///      Ω
    /// ```
    ///
    /// The numerical integration is:
    ///
    /// ```text
    ///     nip-1 → →     →  →       →
    /// cᵐ ≈  Σ   w(ιp) · Gᵐ(ιp) |J|(ιp) wp
    ///      p=0
    /// ```
    ///
    /// # Input
    ///
    /// * `fn_w` -- w(x(ξ)) vector function, however written as the
    ///             function of the index of the integration point.
    ///
    /// # Output
    ///
    /// * `c` -- (npoint) cᵐ scalars; result from the integration
    pub fn integ_case_c(&mut self, c: &mut [f64], fn_w: FnVector) -> Result<(), StrError> {
        // clear results
        c.fill(0.0);

        // auxiliary vector holding the output of fn_w
        let mut w = Vector::new(self.space_ndim);

        // loop over integration points
        for index in 0..self.ip_data.len() {
            // ksi coordinates and weight
            let iota = &self.ip_data[index][0..self.geo_ndim];
            let weight = self.ip_data[index][3];

            // calculate Jacobian and Gradient
            let det_jac = self.calc_gradient(iota)?;

            // loop over points and perform summation
            for m in 0..self.npoint {
                fn_w(&mut w, index);
                let w_dot_grad = self.vec_dot_grad(m, &w);
                c[m] += w_dot_grad * det_jac * weight;
            }
        }
        Ok(())
    }

    /// Performs the Case-D integration
    ///
    /// Case-D, tensor dot gradient:
    ///
    /// ```text
    /// →    ⌠   →    →  → →   →
    /// dᵐ = │ σ(x) · Gᵐ(x(ξ)) dΩ
    ///      ⌡ ▔
    ///      Ω
    /// ```
    ///
    /// The numerical integration is:
    ///
    /// ```text
    /// →   nip-1   →     →  →       →
    /// dᵐ ≈  Σ   σ(ιp) · Gᵐ(ιp) |J|(ιp) wp
    ///      p=0  ▔
    /// ```
    ///
    /// # Input
    ///
    /// * `fn_sig` -- sig(x(ξ)) tensor function, however written as the
    ///               function of the index of the integration point.
    ///
    /// # Output
    ///
    /// * `d` -- (npoint) dᵐ vectors; result from the integration
    pub fn integ_case_d(&mut self, d: &mut Vec<Vector>, fn_sig: FnTensor) -> Result<(), StrError> {
        // check
        if d.len() != self.npoint {
            return Err("b.len() must equal npoint");
        }

        // check dims and clear results
        for m in 0..self.npoint {
            if d[m].dim() != self.space_ndim {
                return Err("d[m].dim() must equal space_ndim");
            }
            d[m].fill(0.0);
        }

        // auxiliary tensor holding the output of fn_sig
        let mut sig = Tensor2::new(true);

        // auxiliary vector equal to σ · G
        let mut sig_dot_grad = vec![0.0; self.space_ndim];

        // loop over integration points
        for index in 0..self.ip_data.len() {
            // ksi coordinates and weight
            let iota = &self.ip_data[index][0..self.geo_ndim];
            let weight = self.ip_data[index][3];

            // calculate Jacobian and Gradient
            let det_jac = self.calc_gradient(iota)?;

            // loop over points and perform summation
            for m in 0..self.npoint {
                fn_sig(&mut sig, index);
                self.tensor_dot_grad(&mut sig_dot_grad, m, &sig);
                for i in 0..self.space_ndim {
                    d[m][i] += sig_dot_grad[i] * det_jac * weight;
                }
            }
        }
        Ok(())
    }

    /// Computes vector dot the gradient at point m
    fn vec_dot_grad(&self, m: usize, w: &Vector) -> f64 {
        let mut res = 0.0;
        for i in 0..self.space_ndim {
            res += w[i] * self.gradient[m][i];
        }
        res
    }

    /// Computes tensor dot the gradient at point m
    fn tensor_dot_grad(&self, res: &mut [f64], m: usize, sig: &Tensor2) {
        for i in 0..self.space_ndim {
            res[i] = 0.0;
            for j in 0..self.space_ndim {
                res[i] += sig.get(i, j) * self.gradient[m][j];
            }
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

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

    // to test if variables are cleared before summation
    const NOISE: f64 = 1234.56;

    #[test]
    fn integ_case_a_works() -> Result<(), StrError> {
        let mut shape = Shape::new(2, 2, 4)?;
        shape.set_point(0, 0, 0.0)?;
        shape.set_point(0, 1, 0.0)?;
        shape.set_point(1, 0, 1.0)?;
        shape.set_point(1, 1, 0.0)?;
        shape.set_point(2, 0, 1.0)?;
        shape.set_point(2, 1, 1.0)?;
        shape.set_point(3, 0, 0.0)?;
        shape.set_point(3, 1, 1.0)?;

        let mut a = vec![NOISE; shape.npoint];

        shape.integ_case_a(&mut a, |_| 0.0)?;

        println!("a = {:?}", a);

        Ok(())
    }
}
<commit_msg>Add integ test<commit_after>use super::*;
use crate::shapes::{GeoClass, Shape};
use crate::StrError;
use russell_lab::Vector;
use russell_tensor::Tensor2;

/// Defines scalar function (the argument is index of the integration point)
pub type FnScalar = fn(usize) -> f64;

/// Defines vector function (the second argument is index of the integration point)
pub type FnVector = fn(&mut Vector, usize);

/// Defines tensor function (the second argument is index of the integration point)
pub type FnTensor = fn(&mut Tensor2, usize);

impl Shape {
    /// Selects integrations points and weights
    ///
    /// # Options
    ///
    /// ## nip for Lin class
    ///
    /// * `1` -- Legendre points
    /// * `2` -- Legendre points
    /// * `3` -- Legendre points
    /// * `4` -- Legendre points
    /// * `5` -- Legendre points
    ///
    /// ## nip for Tri class
    ///
    /// * `1` -- Internal points
    /// * `3`:
    ///     - `edge` -- Points on edge
    ///     - otherwise, internal points
    /// * `4` -- Internal points
    /// * `12` -- Internal points
    /// * `16` -- Internal points
    ///
    /// ## nip for Qua class
    ///
    /// * `1` -- Legendre points
    /// * `4` -- Legendre points
    /// * `5`:
    ///     - `ws` -- Wilson's "Stable" version with w0=0.004 and wa=0.999 to mimic 4-point rule
    ///     - otherwise, standard Wilson's formula
    /// * `9` -- Legendre points
    /// * `16` -- Legendre points
    ///
    /// ## nip for Tet class
    ///
    /// * `1` -- Internal points
    /// * `4` -- Internal points
    /// * `5` -- Internal points
    /// * `6` -- Internal points
    ///
    /// ## nip for Hex class
    ///
    /// * `6` -- Iron's formula
    /// * `8` -- Legendre points
    /// * `9`:
    ///     - `ws` -- Wilson's "Stable" version
    ///     - otherwise, Wilson's standard formula
    /// * `14` -- Iron's formula
    /// * `27` -- Legendre points
    pub fn select_int_points(&mut self, nip: usize, edge: bool, ws: bool) -> Result<(), StrError> {
        match self.class {
            // Lin
            GeoClass::Lin => match nip {
                1 => self.ip_data = &IP_LIN_LEGENDRE_1,
                2 => self.ip_data = &IP_LIN_LEGENDRE_2,
                3 => self.ip_data = &IP_LIN_LEGENDRE_3,
                4 => self.ip_data = &IP_LIN_LEGENDRE_4,
                5 => self.ip_data = &IP_LIN_LEGENDRE_5,
                _ => return Err("number of integration points is not available for Lin class"),
            },

            // Tri
            GeoClass::Tri => match nip {
                1 => self.ip_data = &IP_TRI_INTERNAL_1,
                3 => {
                    if edge {
                        self.ip_data = &IP_TRI_EDGE_3
                    } else {
                        self.ip_data = &IP_TRI_INTERNAL_3
                    }
                }
                4 => self.ip_data = &IP_TRI_INTERNAL_4,
                12 => self.ip_data = &IP_TRI_INTERNAL_12,
                16 => self.ip_data = &IP_TRI_INTERNAL_16,
                _ => return Err("number of integration points is not available for Tri class"),
            },

            // Qua
            GeoClass::Qua => match nip {
                1 => self.ip_data = &IP_QUA_LEGENDRE_1,
                4 => self.ip_data = &IP_QUA_LEGENDRE_4,
                5 => {
                    if ws {
                        self.ip_data = &IP_QUA_WILSON_STABLE_5
                    } else {
                        self.ip_data = &IP_QUA_WILSON_CORNER_5
                    }
                }
                8 => self.ip_data = &IP_QUA_WILSON_8,
                9 => self.ip_data = &IP_QUA_LEGENDRE_9,
                16 => self.ip_data = &IP_QUA_LEGENDRE_16,
                _ => return Err("number of integration points is not available for Qua class"),
            },

            // Tet
            GeoClass::Tet => match nip {
                1 => self.ip_data = &IP_TET_INTERNAL_1,
                4 => self.ip_data = &IP_TET_INTERNAL_4,
                5 => self.ip_data = &IP_TET_INTERNAL_5,
                6 => self.ip_data = &IP_TET_INTERNAL_6,
                _ => return Err("number of integration points is not available for Tet class"),
            },

            // Hex
            GeoClass::Hex => match nip {
                6 => self.ip_data = &IP_HEX_IRONS_6,
                8 => self.ip_data = &IP_HEX_LEGENDRE_8,
                9 => {
                    if ws {
                        self.ip_data = &IP_HEX_WILSON_STABLE_9
                    } else {
                        self.ip_data = &IP_HEX_WILSON_CORNER_9
                    }
                }
                14 => self.ip_data = &IP_HEX_IRONS_14,
                27 => self.ip_data = &IP_HEX_LEGENDRE_27,
                _ => return Err("number of integration points is not available for Hex class"),
            },
        }
        Ok(())
    }

    /// Performs the Case-A integration
    ///
    /// Case-A, interpolation functions times scalar field:
    ///
    /// ```text
    ///      ⌠    → →     →
    /// aᵐ = │ Nᵐ(x(ξ)) s(x) dΩ
    ///      ⌡
    ///      Ω
    /// ```
    ///
    /// or, for lines in multi-dimensions,
    ///
    /// ```text
    ///      ⌠
    /// aᵐ = │ Nᵐ(ℓ(ξ)) s(ℓ) dℓ
    ///      ⌡
    ///      Ω
    /// ```
    ///
    /// The numerical integration is:
    ///
    /// ```text
    ///     nip-1    →     →       →
    /// aᵐ ≈  Σ   Nᵐ(ιp) s(ιp) |J|(ιp) wp
    ///      p=0
    /// ```
    ///
    /// # Input
    ///
    /// * `fn_s` -- s(x(ξ)) or s(ℓ) scalar function, however written as the
    ///             function of the index of the integration point.
    ///
    /// # Output
    ///
    /// * `a` -- (npoint) aᵐ scalars; result from the integration
    pub fn integ_case_a(&mut self, a: &mut [f64], fn_s: FnScalar) -> Result<(), StrError> {
        // clear results
        a.fill(0.0);

        // loop over integration points
        for index in 0..self.ip_data.len() {
            // ksi coordinates and weight
            let iota = &self.ip_data[index][0..self.geo_ndim];
            let weight = self.ip_data[index][3];

            // calculate interpolation functions and Jacobian
            self.calc_interp(iota);
            let det_jac = self.calc_jacobian(iota)?;

            // loop over points and perform summation
            for m in 0..self.npoint {
                a[m] += self.interp[m] * fn_s(index) * det_jac * weight;
            }
        }
        Ok(())
    }

    /// Performs the Case-B integration
    ///
    /// Case-B, interpolation functions times vector field:
    ///
    /// ```text
    /// →    ⌠    → →   → →
    /// bᵐ = │ Nᵐ(x(ξ)) v(x) dΩ
    ///      ⌡
    ///      Ω
    /// ```
    ///
    /// The numerical integration is:
    ///
    /// ```text
    /// →   nip-1    →   → →       →
    /// bᵐ ≈  Σ   Nᵐ(ιp) v(ιp) |J|(ιp) wp
    ///      p=0
    /// ```
    ///
    /// # Input
    ///
    /// * `fn_v` -- v(x(ξ)) vector function, however written as the
    ///             function of the index of the integration point.
    ///
    /// # Output
    ///
    /// * `b` -- (npoint) bᵐ vectors; result from the integration
    pub fn integ_case_b(&mut self, b: &mut Vec<Vector>, fn_v: FnVector) -> Result<(), StrError> {
        // check
        if b.len() != self.npoint {
            return Err("b.len() must equal npoint");
        }

        // check dims and clear results
        for m in 0..self.npoint {
            if b[m].dim() != self.space_ndim {
                return Err("b[m].dim() must equal space_ndim");
            }
            b[m].fill(0.0);
        }

        // auxiliary vector holding the output of fn_v
        let mut v = Vector::new(self.space_ndim);

        // loop over integration points
        for index in 0..self.ip_data.len() {
            // ksi coordinates and weight
            let iota = &self.ip_data[index][0..self.geo_ndim];
            let weight = self.ip_data[index][3];

            // calculate interpolation functions and Jacobian
            self.calc_interp(iota);
            let det_jac = self.calc_jacobian(iota)?;

            // loop over points and perform summation
            for m in 0..self.npoint {
                fn_v(&mut v, index);
                for i in 0..self.space_ndim {
                    b[m][i] += self.interp[m] * v[i] * det_jac * weight;
                }
            }
        }
        Ok(())
    }

    /// Performs the Case-C integration
    ///
    /// Case-C, vector dot gradient:
    ///
    /// ```text
    ///      ⌠ → →    →  → →
    /// cᵐ = │ w(x) · Gᵐ(x(ξ)) dΩ
    ///      ⌡
    ///      Ω
    /// ```
    ///
    /// The numerical integration is:
    ///
    /// ```text
    ///     nip-1 → →     →  →       →
    /// cᵐ ≈  Σ   w(ιp) · Gᵐ(ιp) |J|(ιp) wp
    ///      p=0
    /// ```
    ///
    /// # Input
    ///
    /// * `fn_w` -- w(x(ξ)) vector function, however written as the
    ///             function of the index of the integration point.
    ///
    /// # Output
    ///
    /// * `c` -- (npoint) cᵐ scalars; result from the integration
    pub fn integ_case_c(&mut self, c: &mut [f64], fn_w: FnVector) -> Result<(), StrError> {
        // clear results
        c.fill(0.0);

        // auxiliary vector holding the output of fn_w
        let mut w = Vector::new(self.space_ndim);

        // loop over integration points
        for index in 0..self.ip_data.len() {
            // ksi coordinates and weight
            let iota = &self.ip_data[index][0..self.geo_ndim];
            let weight = self.ip_data[index][3];

            // calculate Jacobian and Gradient
            let det_jac = self.calc_gradient(iota)?;

            // loop over points and perform summation
            for m in 0..self.npoint {
                fn_w(&mut w, index);
                let w_dot_grad = self.vec_dot_grad(m, &w);
                c[m] += w_dot_grad * det_jac * weight;
            }
        }
        Ok(())
    }

    /// Performs the Case-D integration
    ///
    /// Case-D, tensor dot gradient:
    ///
    /// ```text
    /// →    ⌠   →    →  → →   →
    /// dᵐ = │ σ(x) · Gᵐ(x(ξ)) dΩ
    ///      ⌡ ▔
    ///      Ω
    /// ```
    ///
    /// The numerical integration is:
    ///
    /// ```text
    /// →   nip-1   →     →  →       →
    /// dᵐ ≈  Σ   σ(ιp) · Gᵐ(ιp) |J|(ιp) wp
    ///      p=0  ▔
    /// ```
    ///
    /// # Input
    ///
    /// * `fn_sig` -- sig(x(ξ)) tensor function, however written as the
    ///               function of the index of the integration point.
    ///
    /// # Output
    ///
    /// * `d` -- (npoint) dᵐ vectors; result from the integration
    pub fn integ_case_d(&mut self, d: &mut Vec<Vector>, fn_sig: FnTensor) -> Result<(), StrError> {
        // check
        if d.len() != self.npoint {
            return Err("b.len() must equal npoint");
        }

        // check dims and clear results
        for m in 0..self.npoint {
            if d[m].dim() != self.space_ndim {
                return Err("d[m].dim() must equal space_ndim");
            }
            d[m].fill(0.0);
        }

        // auxiliary tensor holding the output of fn_sig
        let mut sig = Tensor2::new(true);

        // auxiliary vector equal to σ · G
        let mut sig_dot_grad = vec![0.0; self.space_ndim];

        // loop over integration points
        for index in 0..self.ip_data.len() {
            // ksi coordinates and weight
            let iota = &self.ip_data[index][0..self.geo_ndim];
            let weight = self.ip_data[index][3];

            // calculate Jacobian and Gradient
            let det_jac = self.calc_gradient(iota)?;

            // loop over points and perform summation
            for m in 0..self.npoint {
                fn_sig(&mut sig, index);
                self.tensor_dot_grad(&mut sig_dot_grad, m, &sig);
                for i in 0..self.space_ndim {
                    d[m][i] += sig_dot_grad[i] * det_jac * weight;
                }
            }
        }
        Ok(())
    }

    /// Computes vector dot the gradient at point m
    fn vec_dot_grad(&self, m: usize, w: &Vector) -> f64 {
        let mut res = 0.0;
        for i in 0..self.space_ndim {
            res += w[i] * self.gradient[m][i];
        }
        res
    }

    /// Computes tensor dot the gradient at point m
    fn tensor_dot_grad(&self, res: &mut [f64], m: usize, sig: &Tensor2) {
        for i in 0..self.space_ndim {
            res[i] = 0.0;
            for j in 0..self.space_ndim {
                res[i] += sig.get(i, j) * self.gradient[m][j];
            }
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#[cfg(test)]
mod tests {
    use super::*;
    use crate::util::SQRT_3;
    use russell_chk::assert_vec_approx_eq;

    // to test if variables are cleared before summation
    const NOISE: f64 = 1234.56;

    fn gen_eq_triangle() -> (Shape, f64) {
        // equilateral triangle with sides equal to l
        //       /\
        //      /  \
        //   l /    \ l
        //    /      \
        //   /________\
        //        l
        let l = 5.0;
        let h = l * SQRT_3 / 2.0;
        let area = l * h / 2.0;
        let mut shape = Shape::new(2, 2, 3).unwrap();
        let (xmin, ymin) = (3.0, 4.0);
        shape.set_point(0, 0, xmin).unwrap();
        shape.set_point(0, 1, ymin).unwrap();
        shape.set_point(1, 0, xmin + l).unwrap();
        shape.set_point(1, 1, ymin).unwrap();
        shape.set_point(2, 0, xmin + l / 2.0).unwrap();
        shape.set_point(2, 1, ymin + h).unwrap();
        (shape, area)
    }

    #[test]
    fn integ_case_a_works() -> Result<(), StrError> {
        let (mut shape, area) = gen_eq_triangle();
        let mut a = vec![NOISE; shape.npoint];
        let fn_s = |_| 3.0;
        shape.integ_case_a(&mut a, fn_s)?;
        println!("a = {:?}", a);
        let cf = fn_s(0) * area / 3.0;
        let a_correct = &[cf, cf, cf];
        assert_vec_approx_eq!(a, a_correct, 1e-14);
        Ok(())
    }
}
<|endoftext|>


<commit_before>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::env;
use std::process::Command;
use std::path::{Path, PathBuf};
use std::fs::File;
use std::io::Write;

struct Test {
    repo: &'static str,
    name: &'static str,
    sha: &'static str,
    lock: Option<&'static str>,
}

const TEST_REPOS: &'static [Test] = &[Test {
                                          name: "cargo",
                                          repo: "https://github.com/rust-lang/cargo",
                                          sha: "7d79da08238e3d47e0bc4406155bdcc45ccb8c82",
                                          lock: None,
                                      },
                                      Test {
                                          name: "iron",
                                          repo: "https://github.com/iron/iron",
                                          sha: "16c858ec2901e2992fe5e529780f59fa8ed12903",
                                          lock: Some(include_str!("lockfiles/iron-Cargo.lock")),
                                      }];

fn main() {
    let args = env::args().collect::<Vec<_>>();
    let ref cargo = args[1];
    let out_dir = Path::new(&args[2]);
    let ref cargo = Path::new(cargo);

    for test in TEST_REPOS.iter().rev() {
        test_repo(cargo, out_dir, test);
    }
}

fn test_repo(cargo: &Path, out_dir: &Path, test: &Test) {
    println!("testing {}", test.repo);
    let dir = clone_repo(test, out_dir);
    if let Some(lockfile) = test.lock {
        File::create(&dir.join("Cargo.lock"))
            .expect("")
            .write_all(lockfile.as_bytes())
            .expect("");
    }
    if !run_cargo_test(cargo, &dir) {
        panic!("tests failed for {}", test.repo);
    }
}

fn clone_repo(test: &Test, out_dir: &Path) -> PathBuf {
    let out_dir = out_dir.join(test.name);

    if !out_dir.join(".git").is_dir() {
        let status = Command::new("git")
                         .arg("init")
                         .arg(&out_dir)
                         .status()
                         .expect("");
        assert!(status.success());
    }

    // Try progressively deeper fetch depths to find the commit
    let mut found = false;
    for depth in &[0, 1, 10, 100, 1000, 100000] {
        if *depth > 0 {
            let status = Command::new("git")
                             .arg("fetch")
                             .arg(test.repo)
                             .arg("master")
                             .arg(&format!("--depth={}", depth))
                             .current_dir(&out_dir)
                             .status()
                             .expect("");
            assert!(status.success());
        }

        let status = Command::new("git")
                         .arg("reset")
                         .arg(test.sha)
                         .arg("--hard")
                         .current_dir(&out_dir)
                         .status()
                         .expect("");

        if status.success() {
            found = true;
            break;
        }
    }

    if !found {
        panic!("unable to find commit {}", test.sha)
    }
    let status = Command::new("git")
                     .arg("clean")
                     .arg("-fdx")
                     .current_dir(&out_dir)
                     .status()
                     .unwrap();
    assert!(status.success());

    out_dir
}

fn run_cargo_test(cargo_path: &Path, crate_path: &Path) -> bool {
    let status = Command::new(cargo_path)
        .arg("test")
        // Disable rust-lang/cargo's cross-compile tests
        .env("CFG_DISABLE_CROSS_TESTS", "1")
        .current_dir(crate_path)
        .status()
        .expect("");

    status.success()
}
<commit_msg>Update `cargo_test` hash.<commit_after>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::env;
use std::process::Command;
use std::path::{Path, PathBuf};
use std::fs::File;
use std::io::Write;

struct Test {
    repo: &'static str,
    name: &'static str,
    sha: &'static str,
    lock: Option<&'static str>,
}

const TEST_REPOS: &'static [Test] = &[Test {
                                          name: "cargo",
                                          repo: "https://github.com/rust-lang/cargo",
                                          sha: "fd90fd642d404d8c66505ca8db742c664ea352f2",
                                          lock: None,
                                      },
                                      Test {
                                          name: "iron",
                                          repo: "https://github.com/iron/iron",
                                          sha: "16c858ec2901e2992fe5e529780f59fa8ed12903",
                                          lock: Some(include_str!("lockfiles/iron-Cargo.lock")),
                                      }];

fn main() {
    let args = env::args().collect::<Vec<_>>();
    let ref cargo = args[1];
    let out_dir = Path::new(&args[2]);
    let ref cargo = Path::new(cargo);

    for test in TEST_REPOS.iter().rev() {
        test_repo(cargo, out_dir, test);
    }
}

fn test_repo(cargo: &Path, out_dir: &Path, test: &Test) {
    println!("testing {}", test.repo);
    let dir = clone_repo(test, out_dir);
    if let Some(lockfile) = test.lock {
        File::create(&dir.join("Cargo.lock"))
            .expect("")
            .write_all(lockfile.as_bytes())
            .expect("");
    }
    if !run_cargo_test(cargo, &dir) {
        panic!("tests failed for {}", test.repo);
    }
}

fn clone_repo(test: &Test, out_dir: &Path) -> PathBuf {
    let out_dir = out_dir.join(test.name);

    if !out_dir.join(".git").is_dir() {
        let status = Command::new("git")
                         .arg("init")
                         .arg(&out_dir)
                         .status()
                         .expect("");
        assert!(status.success());
    }

    // Try progressively deeper fetch depths to find the commit
    let mut found = false;
    for depth in &[0, 1, 10, 100, 1000, 100000] {
        if *depth > 0 {
            let status = Command::new("git")
                             .arg("fetch")
                             .arg(test.repo)
                             .arg("master")
                             .arg(&format!("--depth={}", depth))
                             .current_dir(&out_dir)
                             .status()
                             .expect("");
            assert!(status.success());
        }

        let status = Command::new("git")
                         .arg("reset")
                         .arg(test.sha)
                         .arg("--hard")
                         .current_dir(&out_dir)
                         .status()
                         .expect("");

        if status.success() {
            found = true;
            break;
        }
    }

    if !found {
        panic!("unable to find commit {}", test.sha)
    }
    let status = Command::new("git")
                     .arg("clean")
                     .arg("-fdx")
                     .current_dir(&out_dir)
                     .status()
                     .unwrap();
    assert!(status.success());

    out_dir
}

fn run_cargo_test(cargo_path: &Path, crate_path: &Path) -> bool {
    let status = Command::new(cargo_path)
        .arg("test")
        // Disable rust-lang/cargo's cross-compile tests
        .env("CFG_DISABLE_CROSS_TESTS", "1")
        .current_dir(crate_path)
        .status()
        .expect("");

    status.success()
}
<|endoftext|>