<commit_before>use crate::actions::cache;
use crate::actions::cargo_check;
use crate::actions::cargo_doc;
use crate::actions::cargo_test;
use crate::actions::checkout_sources;
use crate::actions::checkout_sources_depth;
use crate::actions::rust_install_toolchain;
use crate::actions::RustToolchain;
use crate::ghwf::Env;
use crate::ghwf::Job;
use crate::ghwf::Step;
use crate::yaml::Yaml;
use crate::yaml::YamlWriter;
use std::env;
use std::fs::File;
use std::io::Read;
use std::io::Write;

mod actions;
mod ghwf;
mod yaml;
mod install_protobuf;

#[derive(PartialEq, Eq, Copy, Clone)]
struct Os {
    name: &'static str,
    ghwf: Env,
}

const LINUX: Os = Os {
    name: "linux",
    ghwf: Env::UbuntuLatest,
};
const MACOS: Os = Os {
    name: "macos",
    ghwf: Env::MacosLatest,
};
const WINDOWS: Os = Os {
    name: "windows",
    ghwf: Env::WindowsLatest,
};

#[derive(Eq, PartialEq)]
enum Features {
    Default,
    Specific(&'static [&'static str]),
    All,
}

impl Features {
    fn flag(&self) -> String {
        match self {
            Features::Default => format!(""),
            Features::Specific(f) => format!("--features={}", f.join(",")),
            Features::All => format!("--all-features"),
        }
    }

    fn flag_suffix(&self) -> String {
        let flag = self.flag();
        if flag.is_empty() {
            "".to_owned()
        } else {
            format!(" {}", flag)
        }
    }

    fn id(&self) -> String {
        match self {
            Features::Default => format!("default-features"),
            Features::All => format!("all-features"),
            Features::Specific(s) => s.join("-"),
        }
    }

    fn name(&self) -> String {
        match self {
            Features::Default => format!("default features"),
            Features::All => format!("all features"),
            Features::Specific(s) => s.join(","),
        }
    }
}

fn self_check_job() -> Job {
    Job {
        id: format!("self-check"),
        name: format!("CI self-check"),
        runs_on: LINUX.ghwf,
        steps: vec![
            checkout_sources(),
            rust_install_toolchain(RustToolchain::Stable),
            Step::run("The check", "cargo run -p ci-gen -- --check"),
        ],
        ..Default::default()
    }
}

fn job(channel: RustToolchain, os: Os, features: Features) -> Job {
    let mut steps = vec![];
    steps.push(checkout_sources());
    steps.push(rust_install_toolchain(channel));

    steps.push(cache(
        "Cache protobuf",
        &format!("pb-{}{}", os.name, if os == WINDOWS { "-1" } else { "" }),
        "~/pb",
    ));

    if os == MACOS {
        steps.push(Step::run("Install pkg-config", "brew install pkg-config"));
    }

    steps.extend(install_protobuf::install_protobuf());

    if os != WINDOWS {
        steps.push(Step::run("Compile interop", "interop/cxx/compile.sh"));
    }

    // * Nothing works on Windows
    // * Only regenerate with default features to avoid rebuilding twice
    if os != WINDOWS && features == Features::Default {
        steps.push(Step::run("Regenerate", "protobuf/regenerate.sh"));
    }

    match features {
        Features::Specific(..) => {
            steps.push(cargo_test(
                "protobuf-test",
                &format!(
                    "--manifest-path=protobuf-test/Cargo.toml{}",
                    features.flag_suffix()
                ),
            ));
            steps.push(cargo_test(
                "protobuf-codegen-pure-test",
                &format!(
                    "--manifest-path=protobuf-codegen-pure-test/Cargo.toml{}",
                    features.flag_suffix()
                ),
            ));
        }
        _ => {
            steps.push(Step::run(
                "Test all",
                &format!("cargo test --all --all-targets{}", features.flag_suffix()),
            ));
            if os == LINUX {
                steps.push(cargo_doc("cargo doc", &features.flag()));
            }
        }
    }

    if os != WINDOWS {
        steps.push(Step::run("test protoc", "protoc/test.sh"));
    }

    let mut env = vec![
        ("PROTOBUF_VERSION".to_owned(), "3.6.1".to_owned()),
        ("RUST_BACKTRACE".to_owned(), "1".to_owned()),
    ];
    if os == WINDOWS {
        env.push(("VCPKGRS_DYNAMIC".to_owned(), "1".to_owned()));
    };
    let id = format!("{}-{}-{}", os.name, channel, features.id());
    let name = format!("{} {} ({})", os.name, channel, features.name());
    Job {
        id,
        name,
        runs_on: os.ghwf.to_owned(),
        steps,
        env,
        ..Default::default()
    }
}

fn super_linter_job() -> Job {
    let mut steps = Vec::new();
    steps.push(checkout_sources_depth(Some(0)));
    steps.push(
        Step::uses("super-linter", "github/super-linter@v3")
            .env("VALIDATE_ALL_CODEBASE", "false")
            .env("DEFAULT_BRANCH", "master")
            .env("GITHUB_TOKEN", "${{ secrets.GITHUB_TOKEN }}")
            // Too many false positives
            .env("VALIDATE_JSCPD", "false"),
    );
    Job {
        id: "super-linter".to_owned(),
        name: "super-linter".to_owned(),
        runs_on: LINUX.ghwf,
        steps,
        ..Default::default()
    }
}

fn rustfmt_job() -> Job {
    let mut steps = Vec::new();
    steps.push(checkout_sources());
    // force generate code
    steps.extend(install_protobuf::install_protobuf());
    steps.push(cargo_check("cargo check", ""));
    steps.push(Step::run("cargo fmt check", "cargo fmt -- --check"));
    Job {
        id: "rustfmt-check".to_owned(),
        name: "rustfmt check".to_owned(),
        runs_on: LINUX.ghwf,
        steps,
        ..Default::default()
    }
}

fn jobs() -> Yaml {
    let mut r = Vec::new();

    r.push(job(RustToolchain::Stable, LINUX, Features::Default));
    r.push(job(RustToolchain::Beta, LINUX, Features::Default));
    r.push(job(
        RustToolchain::Stable,
        LINUX,
        Features::Specific(&["with-serde"]),
    ));
    r.push(job(
        RustToolchain::Stable,
        LINUX,
        Features::Specific(&["with-bytes"]),
    ));
    r.push(job(RustToolchain::Nightly, LINUX, Features::All));

    r.push(job(RustToolchain::Stable, WINDOWS, Features::Default));

    r.push(super_linter_job());

    r.push(rustfmt_job());

    r.push(self_check_job());

    // TODO: enable macos
    //r.push(job(RustToolchain::Stable, MACOS, Features::Default));

    Yaml::map(r.into_iter().map(Job::into))
}

fn main() {
    let args: Vec<String> = env::args().skip(1).collect();
    let args: Vec<&str> = args.iter().map(|a| a.as_str()).collect();

    let yaml = Yaml::map(vec![
        ("on", Yaml::list(vec!["push", "pull_request"])),
        ("name", Yaml::string("CI")),
        ("jobs", jobs()),
    ]);

    let mut writer = YamlWriter::default();
    writer.write_line(&format!(
        "# @generated by {}, do not edit",
        env!("CARGO_PKG_NAME")
    ));
    writer.write_line("");
    writer.write_yaml(&yaml);
    let ci_yml = writer.buffer;

    let ci_yml_path = ".github/workflows/ci.yml";

    match args.as_slice() {
        &[] => {
            File::create(ci_yml_path)
                .unwrap()
                .write_all(ci_yml.as_bytes())
                .unwrap();
            eprintln!("written {}", ci_yml_path);
        }
        &["--check"] => {
            let mut actual = String::new();
            File::open(ci_yml_path)
                .unwrap()
                .read_to_string(&mut actual)
                .unwrap();
            assert!(ci_yml == actual);
            eprintln!("The file is correct")
        }
        args => panic!("unknown args: {:?}", args),
    }
}
<commit_msg>cargo fmt<commit_after>use crate::actions::cache;
use crate::actions::cargo_check;
use crate::actions::cargo_doc;
use crate::actions::cargo_test;
use crate::actions::checkout_sources;
use crate::actions::checkout_sources_depth;
use crate::actions::rust_install_toolchain;
use crate::actions::RustToolchain;
use crate::ghwf::Env;
use crate::ghwf::Job;
use crate::ghwf::Step;
use crate::yaml::Yaml;
use crate::yaml::YamlWriter;
use std::env;
use std::fs::File;
use std::io::Read;
use std::io::Write;

mod actions;
mod ghwf;
mod install_protobuf;
mod yaml;

#[derive(PartialEq, Eq, Copy, Clone)]
struct Os {
    name: &'static str,
    ghwf: Env,
}

const LINUX: Os = Os {
    name: "linux",
    ghwf: Env::UbuntuLatest,
};
const MACOS: Os = Os {
    name: "macos",
    ghwf: Env::MacosLatest,
};
const WINDOWS: Os = Os {
    name: "windows",
    ghwf: Env::WindowsLatest,
};

#[derive(Eq, PartialEq)]
enum Features {
    Default,
    Specific(&'static [&'static str]),
    All,
}

impl Features {
    fn flag(&self) -> String {
        match self {
            Features::Default => format!(""),
            Features::Specific(f) => format!("--features={}", f.join(",")),
            Features::All => format!("--all-features"),
        }
    }

    fn flag_suffix(&self) -> String {
        let flag = self.flag();
        if flag.is_empty() {
            "".to_owned()
        } else {
            format!(" {}", flag)
        }
    }

    fn id(&self) -> String {
        match self {
            Features::Default => format!("default-features"),
            Features::All => format!("all-features"),
            Features::Specific(s) => s.join("-"),
        }
    }

    fn name(&self) -> String {
        match self {
            Features::Default => format!("default features"),
            Features::All => format!("all features"),
            Features::Specific(s) => s.join(","),
        }
    }
}

fn self_check_job() -> Job {
    Job {
        id: format!("self-check"),
        name: format!("CI self-check"),
        runs_on: LINUX.ghwf,
        steps: vec![
            checkout_sources(),
            rust_install_toolchain(RustToolchain::Stable),
            Step::run("The check", "cargo run -p ci-gen -- --check"),
        ],
        ..Default::default()
    }
}

fn job(channel: RustToolchain, os: Os, features: Features) -> Job {
    let mut steps = vec![];
    steps.push(checkout_sources());
    steps.push(rust_install_toolchain(channel));

    steps.push(cache(
        "Cache protobuf",
        &format!("pb-{}{}", os.name, if os == WINDOWS { "-1" } else { "" }),
        "~/pb",
    ));

    if os == MACOS {
        steps.push(Step::run("Install pkg-config", "brew install pkg-config"));
    }

    steps.extend(install_protobuf::install_protobuf());

    if os != WINDOWS {
        steps.push(Step::run("Compile interop", "interop/cxx/compile.sh"));
    }

    // * Nothing works on Windows
    // * Only regenerate with default features to avoid rebuilding twice
    if os != WINDOWS && features == Features::Default {
        steps.push(Step::run("Regenerate", "protobuf/regenerate.sh"));
    }

    match features {
        Features::Specific(..) => {
            steps.push(cargo_test(
                "protobuf-test",
                &format!(
                    "--manifest-path=protobuf-test/Cargo.toml{}",
                    features.flag_suffix()
                ),
            ));
            steps.push(cargo_test(
                "protobuf-codegen-pure-test",
                &format!(
                    "--manifest-path=protobuf-codegen-pure-test/Cargo.toml{}",
                    features.flag_suffix()
                ),
            ));
        }
        _ => {
            steps.push(Step::run(
                "Test all",
                &format!("cargo test --all --all-targets{}", features.flag_suffix()),
            ));
            if os == LINUX {
                steps.push(cargo_doc("cargo doc", &features.flag()));
            }
        }
    }

    if os != WINDOWS {
        steps.push(Step::run("test protoc", "protoc/test.sh"));
    }

    let mut env = vec![
        ("PROTOBUF_VERSION".to_owned(), "3.6.1".to_owned()),
        ("RUST_BACKTRACE".to_owned(), "1".to_owned()),
    ];
    if os == WINDOWS {
        env.push(("VCPKGRS_DYNAMIC".to_owned(), "1".to_owned()));
    };
    let id = format!("{}-{}-{}", os.name, channel, features.id());
    let name = format!("{} {} ({})", os.name, channel, features.name());
    Job {
        id,
        name,
        runs_on: os.ghwf.to_owned(),
        steps,
        env,
        ..Default::default()
    }
}

fn super_linter_job() -> Job {
    let mut steps = Vec::new();
    steps.push(checkout_sources_depth(Some(0)));
    steps.push(
        Step::uses("super-linter", "github/super-linter@v3")
            .env("VALIDATE_ALL_CODEBASE", "false")
            .env("DEFAULT_BRANCH", "master")
            .env("GITHUB_TOKEN", "${{ secrets.GITHUB_TOKEN }}")
            // Too many false positives
            .env("VALIDATE_JSCPD", "false"),
    );
    Job {
        id: "super-linter".to_owned(),
        name: "super-linter".to_owned(),
        runs_on: LINUX.ghwf,
        steps,
        ..Default::default()
    }
}

fn rustfmt_job() -> Job {
    let mut steps = Vec::new();
    steps.push(checkout_sources());
    // force generate code
    steps.extend(install_protobuf::install_protobuf());
    steps.push(cargo_check("cargo check", ""));
    steps.push(Step::run("cargo fmt check", "cargo fmt -- --check"));
    Job {
        id: "rustfmt-check".to_owned(),
        name: "rustfmt check".to_owned(),
        runs_on: LINUX.ghwf,
        steps,
        ..Default::default()
    }
}

fn jobs() -> Yaml {
    let mut r = Vec::new();

    r.push(job(RustToolchain::Stable, LINUX, Features::Default));
    r.push(job(RustToolchain::Beta, LINUX, Features::Default));
    r.push(job(
        RustToolchain::Stable,
        LINUX,
        Features::Specific(&["with-serde"]),
    ));
    r.push(job(
        RustToolchain::Stable,
        LINUX,
        Features::Specific(&["with-bytes"]),
    ));
    r.push(job(RustToolchain::Nightly, LINUX, Features::All));

    r.push(job(RustToolchain::Stable, WINDOWS, Features::Default));

    r.push(super_linter_job());

    r.push(rustfmt_job());

    r.push(self_check_job());

    // TODO: enable macos
    //r.push(job(RustToolchain::Stable, MACOS, Features::Default));

    Yaml::map(r.into_iter().map(Job::into))
}

fn main() {
    let args: Vec<String> = env::args().skip(1).collect();
    let args: Vec<&str> = args.iter().map(|a| a.as_str()).collect();

    let yaml = Yaml::map(vec![
        ("on", Yaml::list(vec!["push", "pull_request"])),
        ("name", Yaml::string("CI")),
        ("jobs", jobs()),
    ]);

    let mut writer = YamlWriter::default();
    writer.write_line(&format!(
        "# @generated by {}, do not edit",
        env!("CARGO_PKG_NAME")
    ));
    writer.write_line("");
    writer.write_yaml(&yaml);
    let ci_yml = writer.buffer;

    let ci_yml_path = ".github/workflows/ci.yml";

    match args.as_slice() {
        &[] => {
            File::create(ci_yml_path)
                .unwrap()
                .write_all(ci_yml.as_bytes())
                .unwrap();
            eprintln!("written {}", ci_yml_path);
        }
        &["--check"] => {
            let mut actual = String::new();
            File::open(ci_yml_path)
                .unwrap()
                .read_to_string(&mut actual)
                .unwrap();
            assert!(ci_yml == actual);
            eprintln!("The file is correct")
        }
        args => panic!("unknown args: {:?}", args),
    }
}
<|endoftext|>


<commit_before>use std::str::FromStr;

/// Description for new columns
#[derive(Debug, Clone)]
pub struct ColumnDescription {
    pub name: String,

    /// Type of the data, see the cfitsio documentation
    pub data_type: Option<ColumnDataDescription>,
}

#[derive(Debug, Clone)]
pub struct ConcreteColumnDescription {
    pub name: String,
    pub data_type: ColumnDataDescription,
}

impl ColumnDescription {
    pub fn new<T: Into<String>>(name: T) -> Self {
        ColumnDescription {
            name: name.into(),
            data_type: None,
        }
    }

    pub fn with_type(&mut self, typ: ColumnDataType) -> &mut ColumnDescription {
        self.data_type = Some(ColumnDataDescription::scalar(typ));
        self
    }

    pub fn that_repeats(&mut self, repeat: usize) -> &mut ColumnDescription {
        if let Some(ref mut desc) = self.data_type {
            desc.repeat = repeat;
        }
        self
    }

    pub fn with_width(&mut self, width: usize) -> &mut ColumnDescription {
        if let Some(ref mut desc) = self.data_type {
            desc.width = width;
        }
        self
    }

    pub fn create(&self) -> Result<ConcreteColumnDescription, Box<::std::error::Error>> {
        match self.data_type {
            Some(ref d) => {
                Ok(ConcreteColumnDescription {
                    name: self.name.clone(),
                    data_type: d.clone(),
                })
            }
            None => {
                Err("No data type given. Ensure the `with_type` method has been called.".into())
            }
        }
    }
}

/// Description of the column data
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ColumnDataDescription {
    pub repeat: usize,
    pub width: usize,
    pub typ: ColumnDataType,
}

impl ColumnDataDescription {
    pub fn new(typ: ColumnDataType, repeat: usize, width: usize) -> Self {
        ColumnDataDescription {
            repeat: repeat,
            width: width,
            typ: typ,
        }
    }

    /// Shortcut for creating a scalar column
    pub fn scalar(typ: ColumnDataType) -> Self {
        ColumnDataDescription::new(typ, 1, 1)
    }

    pub fn vector(typ: ColumnDataType, repeat: usize) -> Self {
        ColumnDataDescription::new(typ, repeat, 1)
    }

    /* XXX These two methods force a call to clone which is wasteful of memory. I do not know if
     * this means that memory is leaked, or that destructors are needlessly called (I suspect the
     * latter) but it is fairly wasteful. On the other hand, it's unlikely this sort of thing will
     * be called in performance-critical code, and is more likely a one-dime definition. I will
     * leave it for now - SRW 2017-03-07
     * */
    pub fn repeats(&mut self, repeat: usize) -> Self {
        // TODO check that repeat >= 1
        self.repeat = repeat;
        self.clone()
    }

    pub fn width(&mut self, width: usize) -> Self {
        // TODO check that width >= 1
        self.width = width;
        self.clone()
    }
}

impl From<ColumnDataDescription> for String {
    fn from(orig: ColumnDataDescription) -> String {
        match orig.typ {
            ColumnDataType::Text => {
                if orig.width > 1 {
                    format!("{repeat}{data_type}{width}",
                            data_type = String::from(orig.typ),
                            repeat = orig.repeat,
                            width = orig.width)
                } else {
                    format!("{repeat}{data_type}",
                            data_type = String::from(orig.typ),
                            repeat = orig.repeat)
                }
            }
            _ => {
                format!("{repeat}{data_type}",
                        data_type = String::from(orig.typ),
                        repeat = orig.repeat)
            }
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ColumnDataType {
    Int,
    Float,
    Text,
    Double,
    Short,
    Long,
}

impl From<ColumnDataType> for String {
    fn from(orig: ColumnDataType) -> String {
        use self::ColumnDataType::*;

        match orig {
                Int => "J",
                Float => "E",
                Text => "A",
                Double => "D",
                Short => "I",
                Long => "K",
            }
            .to_string()
    }
}

impl FromStr for ColumnDataDescription {
    type Err = Box<::std::error::Error>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let chars: Vec<_> = s.chars().collect();

        let mut repeat_str = Vec::new();
        let mut last_position = 0;
        for c in &chars {
            if c.is_digit(10) {
                repeat_str.push(c);
                last_position += 1;
            } else {
                break;
            }
        }

        let repeat = if repeat_str.is_empty() {
            1
        } else {
            let repeat_str: String = repeat_str.into_iter().collect();
            repeat_str.parse::<usize>()?
        };

        let data_type_char = chars[last_position];
        last_position += 1;

        let mut width_str = Vec::new();
        for c in chars.iter().skip(last_position) {
            if c.is_digit(10) {
                width_str.push(c);
            } else {
                break;
            }
        }

        /* TODO: validate that the whole string has been used up */

        let width = if width_str.is_empty() {
            1
        } else {
            let width_str: String = width_str.into_iter().collect();
            width_str.parse::<usize>()?
        };

        let data_type = match data_type_char {
            'E' => ColumnDataType::Float,
            'J' => ColumnDataType::Int,
            'D' => ColumnDataType::Double,
            'I' => ColumnDataType::Short,
            'K' => ColumnDataType::Long,
            _ => {
                panic!("Have not implemented str -> ColumnDataType for {}",
                       data_type_char)
            }
        };

        Ok(ColumnDataDescription {
            repeat: repeat,
            typ: data_type,
            width: width,
        })
    }
}

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

    #[test]
    fn test_column_data_descriptions_builder_pattern() {
        let desc = ColumnDataDescription::scalar(ColumnDataType::Int)
            .width(100)
            .repeats(5);
        assert_eq!(desc.repeat, 5);
        assert_eq!(desc.width, 100);
    }

    #[test]
    fn from_impls() {
        {
            let desc = ColumnDataDescription::scalar(ColumnDataType::Int).repeats(5);
            assert_eq!(String::from(desc), "5J");
        }

        {
            let desc = ColumnDataDescription::scalar(ColumnDataType::Float);
            assert_eq!(String::from(desc), "1E");
        }

        {
            let desc = ColumnDataDescription::scalar(ColumnDataType::Text).width(100);
            assert_eq!(String::from(desc), "1A100");
        }
    }

    #[test]
    fn parsing() {
        let s = "1E";
        assert_eq!(s.parse::<ColumnDataDescription>().unwrap(),
                   ColumnDataDescription {
                       repeat: 1,
                       width: 1,
                       typ: ColumnDataType::Float,
                   });
    }

    #[test]
    fn parse_many_repeats() {
        let s = "100E";
        assert_eq!(s.parse::<ColumnDataDescription>().unwrap(),
                   ColumnDataDescription {
                       repeat: 100,
                       width: 1,
                       typ: ColumnDataType::Float,
                   });
    }

    #[test]
    fn parse_with_width() {
        let s = "1E26";
        assert_eq!(s.parse::<ColumnDataDescription>().unwrap(),
                   ColumnDataDescription {
                       repeat: 1,
                       width: 26,
                       typ: ColumnDataType::Float,
                   });
    }

    #[test]
    fn creating_data_description() {
        let concrete_desc = ColumnDescription::new("FOO")
            .with_type(ColumnDataType::Int)
            .that_repeats(10)
            .create()
            .unwrap();
        assert_eq!(concrete_desc.name, "FOO".to_string());
        assert_eq!(concrete_desc.data_type.repeat, 10);
        assert_eq!(concrete_desc.data_type.width, 1);

        /* Do not call `with_type` */
        let bad_desc = ColumnDescription::new("FOO").create();
        assert!(bad_desc.is_err());
    }
}
<commit_msg>Fix compilation errors on stable rust<commit_after>use std::str::FromStr;

/// Description for new columns
#[derive(Debug, Clone)]
pub struct ColumnDescription {
    pub name: String,

    /// Type of the data, see the cfitsio documentation
    pub data_type: Option<ColumnDataDescription>,
}

#[derive(Debug, Clone)]
pub struct ConcreteColumnDescription {
    pub name: String,
    pub data_type: ColumnDataDescription,
}

impl ColumnDescription {
    pub fn new<T: Into<String>>(name: T) -> Self {
        ColumnDescription {
            name: name.into(),
            data_type: None,
        }
    }

    pub fn with_type(&mut self, typ: ColumnDataType) -> &mut ColumnDescription {
        self.data_type = Some(ColumnDataDescription::scalar(typ));
        self
    }

    pub fn that_repeats(&mut self, repeat: usize) -> &mut ColumnDescription {
        if let Some(ref mut desc) = self.data_type {
            desc.repeat = repeat;
        }
        self
    }

    pub fn with_width(&mut self, width: usize) -> &mut ColumnDescription {
        if let Some(ref mut desc) = self.data_type {
            desc.width = width;
        }
        self
    }

    pub fn create(&self) -> Result<ConcreteColumnDescription, Box<::std::error::Error>> {
        match self.data_type {
            Some(ref d) => {
                Ok(ConcreteColumnDescription {
                    name: self.name.clone(),
                    data_type: d.clone(),
                })
            }
            None => {
                Err("No data type given. Ensure the `with_type` method has been called.".into())
            }
        }
    }
}

/// Description of the column data
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ColumnDataDescription {
    pub repeat: usize,
    pub width: usize,
    pub typ: ColumnDataType,
}

impl ColumnDataDescription {
    pub fn new(typ: ColumnDataType, repeat: usize, width: usize) -> Self {
        ColumnDataDescription {
            repeat: repeat,
            width: width,
            typ: typ,
        }
    }

    /// Shortcut for creating a scalar column
    pub fn scalar(typ: ColumnDataType) -> Self {
        ColumnDataDescription::new(typ, 1, 1)
    }

    pub fn vector(typ: ColumnDataType, repeat: usize) -> Self {
        ColumnDataDescription::new(typ, repeat, 1)
    }

    /* XXX These two methods force a call to clone which is wasteful of memory. I do not know if
     * this means that memory is leaked, or that destructors are needlessly called (I suspect the
     * latter) but it is fairly wasteful. On the other hand, it's unlikely this sort of thing will
     * be called in performance-critical code, and is more likely a one-dime definition. I will
     * leave it for now - SRW 2017-03-07
     * */
    pub fn repeats(&mut self, repeat: usize) -> Self {
        // TODO check that repeat >= 1
        self.repeat = repeat;
        self.clone()
    }

    pub fn width(&mut self, width: usize) -> Self {
        // TODO check that width >= 1
        self.width = width;
        self.clone()
    }
}

impl From<ColumnDataDescription> for String {
    fn from(orig: ColumnDataDescription) -> String {
        match orig.typ {
            ColumnDataType::Text => {
                if orig.width > 1 {
                    format!("{repeat}{data_type}{width}",
                            data_type = String::from(orig.typ),
                            repeat = orig.repeat,
                            width = orig.width)
                } else {
                    format!("{repeat}{data_type}",
                            data_type = String::from(orig.typ),
                            repeat = orig.repeat)
                }
            }
            _ => {
                format!("{repeat}{data_type}",
                        data_type = String::from(orig.typ),
                        repeat = orig.repeat)
            }
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ColumnDataType {
    Int,
    Float,
    Text,
    Double,
    Short,
    Long,
}

impl From<ColumnDataType> for String {
    fn from(orig: ColumnDataType) -> String {
        use self::ColumnDataType::*;

        match orig {
                Int => "J",
                Float => "E",
                Text => "A",
                Double => "D",
                Short => "I",
                Long => "K",
            }
            .to_string()
    }
}

impl FromStr for ColumnDataDescription {
    type Err = Box<::std::error::Error>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let chars: Vec<_> = s.chars().collect();

        let mut repeat_str = Vec::new();
        let mut last_position = 0;
        for c in &chars {
            if c.is_digit(10) {
                repeat_str.push(c);
                last_position += 1;
            } else {
                break;
            }
        }

        let repeat = if repeat_str.is_empty() {
            1
        } else {
            /* TODO: in nightly the following line works
            let repeat_str: String = repeat_str.into_iter().collect(); */
            let repeat_str: String = repeat_str.into_iter().cloned().collect();
            repeat_str.parse::<usize>()?
        };

        let data_type_char = chars[last_position];
        last_position += 1;

        let mut width_str = Vec::new();
        for c in chars.iter().skip(last_position) {
            if c.is_digit(10) {
                width_str.push(c);
            } else {
                break;
            }
        }

        /* TODO: validate that the whole string has been used up */

        let width = if width_str.is_empty() {
            1
        } else {
            /* TODO: in nightly the following line works
            let width_str: String = width_str.into_iter().collect(); */
            let width_str: String = width_str.into_iter().cloned().collect();
            width_str.parse::<usize>()?
        };

        let data_type = match data_type_char {
            'E' => ColumnDataType::Float,
            'J' => ColumnDataType::Int,
            'D' => ColumnDataType::Double,
            'I' => ColumnDataType::Short,
            'K' => ColumnDataType::Long,
            _ => {
                panic!("Have not implemented str -> ColumnDataType for {}",
                       data_type_char)
            }
        };

        Ok(ColumnDataDescription {
            repeat: repeat,
            typ: data_type,
            width: width,
        })
    }
}

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

    #[test]
    fn test_column_data_descriptions_builder_pattern() {
        let desc = ColumnDataDescription::scalar(ColumnDataType::Int)
            .width(100)
            .repeats(5);
        assert_eq!(desc.repeat, 5);
        assert_eq!(desc.width, 100);
    }

    #[test]
    fn from_impls() {
        {
            let desc = ColumnDataDescription::scalar(ColumnDataType::Int).repeats(5);
            assert_eq!(String::from(desc), "5J");
        }

        {
            let desc = ColumnDataDescription::scalar(ColumnDataType::Float);
            assert_eq!(String::from(desc), "1E");
        }

        {
            let desc = ColumnDataDescription::scalar(ColumnDataType::Text).width(100);
            assert_eq!(String::from(desc), "1A100");
        }
    }

    #[test]
    fn parsing() {
        let s = "1E";
        assert_eq!(s.parse::<ColumnDataDescription>().unwrap(),
                   ColumnDataDescription {
                       repeat: 1,
                       width: 1,
                       typ: ColumnDataType::Float,
                   });
    }

    #[test]
    fn parse_many_repeats() {
        let s = "100E";
        assert_eq!(s.parse::<ColumnDataDescription>().unwrap(),
                   ColumnDataDescription {
                       repeat: 100,
                       width: 1,
                       typ: ColumnDataType::Float,
                   });
    }

    #[test]
    fn parse_with_width() {
        let s = "1E26";
        assert_eq!(s.parse::<ColumnDataDescription>().unwrap(),
                   ColumnDataDescription {
                       repeat: 1,
                       width: 26,
                       typ: ColumnDataType::Float,
                   });
    }

    #[test]
    fn creating_data_description() {
        let concrete_desc = ColumnDescription::new("FOO")
            .with_type(ColumnDataType::Int)
            .that_repeats(10)
            .create()
            .unwrap();
        assert_eq!(concrete_desc.name, "FOO".to_string());
        assert_eq!(concrete_desc.data_type.repeat, 10);
        assert_eq!(concrete_desc.data_type.width, 1);

        /* Do not call `with_type` */
        let bad_desc = ColumnDescription::new("FOO").create();
        assert!(bad_desc.is_err());
    }
}
<|endoftext|>


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

use crate::{
    persistent_safety_storage::PersistentSafetyStorage,
    serializer::{SafetyRulesInput, SerializerClient, SerializerService, TSerializerClient},
    Error, SafetyRules,
};
use libra_logger::warn;
use libra_secure_net::{NetworkClient, NetworkServer};
use std::net::SocketAddr;

pub trait RemoteService {
    fn client(&self) -> SerializerClient {
        let network_client = NetworkClient::new(
            "safety-rules",
            self.server_address(),
            self.network_timeout_ms(),
        );
        let service = Box::new(RemoteClient::new(network_client));
        SerializerClient::new_client(service)
    }

    fn server_address(&self) -> SocketAddr;

    /// Network Timeout in milliseconds.
    fn network_timeout_ms(&self) -> u64;
}

pub fn execute(
    storage: PersistentSafetyStorage,
    listen_addr: SocketAddr,
    verify_vote_proposal_signature: bool,
    network_timeout_ms: u64,
) {
    let safety_rules = SafetyRules::new(storage, verify_vote_proposal_signature);
    let mut serializer_service = SerializerService::new(safety_rules);
    let mut network_server = NetworkServer::new("safety-rules", listen_addr, network_timeout_ms);

    loop {
        if let Err(e) = process_one_message(&mut network_server, &mut serializer_service) {
            warn!("Warning: Failed to process message: {}", e);
        }
    }
}

fn process_one_message(
    network_server: &mut NetworkServer,
    serializer_service: &mut SerializerService,
) -> Result<(), Error> {
    let request = network_server.read()?;
    let response = serializer_service.handle_message(request)?;
    network_server.write(&response)?;
    Ok(())
}

struct RemoteClient {
    network_client: NetworkClient,
}

impl RemoteClient {
    pub fn new(network_client: NetworkClient) -> Self {
        Self { network_client }
    }

    fn process_one_message(&mut self, input: &[u8]) -> Result<Vec<u8>, Error> {
        self.network_client.write(&input)?;
        self.network_client.read().map_err(|e| e.into())
    }
}

impl TSerializerClient for RemoteClient {
    fn request(&mut self, input: SafetyRulesInput) -> Result<Vec<u8>, Error> {
        let input_message = lcs::to_bytes(&input)?;
        loop {
            match self.process_one_message(&input_message) {
                Err(err) => warn!("Failed to communicate with SafetyRules service: {}", err),
                Ok(value) => return Ok(value),
            }
        }
    }
}
<commit_msg>[safety-rules] log internal state at startup<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{
    persistent_safety_storage::PersistentSafetyStorage,
    serializer::{SafetyRulesInput, SerializerClient, SerializerService, TSerializerClient},
    Error, SafetyRules, TSafetyRules,
};
use libra_logger::warn;
use libra_secure_net::{NetworkClient, NetworkServer};
use std::net::SocketAddr;

pub trait RemoteService {
    fn client(&self) -> SerializerClient {
        let network_client = NetworkClient::new(
            "safety-rules",
            self.server_address(),
            self.network_timeout_ms(),
        );
        let service = Box::new(RemoteClient::new(network_client));
        SerializerClient::new_client(service)
    }

    fn server_address(&self) -> SocketAddr;

    /// Network Timeout in milliseconds.
    fn network_timeout_ms(&self) -> u64;
}

pub fn execute(
    storage: PersistentSafetyStorage,
    listen_addr: SocketAddr,
    verify_vote_proposal_signature: bool,
    network_timeout_ms: u64,
) {
    let mut safety_rules = SafetyRules::new(storage, verify_vote_proposal_signature);
    if let Err(e) = safety_rules.consensus_state() {
        warn!("Unable to print consensus state: {}", e);
    }

    let mut serializer_service = SerializerService::new(safety_rules);
    let mut network_server = NetworkServer::new("safety-rules", listen_addr, network_timeout_ms);

    loop {
        if let Err(e) = process_one_message(&mut network_server, &mut serializer_service) {
            warn!("Failed to process message: {}", e);
        }
    }
}

fn process_one_message(
    network_server: &mut NetworkServer,
    serializer_service: &mut SerializerService,
) -> Result<(), Error> {
    let request = network_server.read()?;
    let response = serializer_service.handle_message(request)?;
    network_server.write(&response)?;
    Ok(())
}

struct RemoteClient {
    network_client: NetworkClient,
}

impl RemoteClient {
    pub fn new(network_client: NetworkClient) -> Self {
        Self { network_client }
    }

    fn process_one_message(&mut self, input: &[u8]) -> Result<Vec<u8>, Error> {
        self.network_client.write(&input)?;
        self.network_client.read().map_err(|e| e.into())
    }
}

impl TSerializerClient for RemoteClient {
    fn request(&mut self, input: SafetyRulesInput) -> Result<Vec<u8>, Error> {
        let input_message = lcs::to_bytes(&input)?;
        loop {
            match self.process_one_message(&input_message) {
                Err(err) => warn!("Failed to communicate with SafetyRules service: {}", err),
                Ok(value) => return Ok(value),
            }
        }
    }
}
<|endoftext|>


<commit_before>use criterion::Criterion;
use criterion::{black_box, Benchmark};
use ssz::TreeHash;
use state_processing::{
    per_epoch_processing,
    per_epoch_processing::{
        clean_attestations, initialize_validator_statuses, process_crosslinks, process_eth1_data,
        process_justification, process_rewards_and_penalities, process_validator_registry,
        update_active_tree_index_roots, update_latest_slashed_balances,
    },
};
use types::test_utils::TestingBeaconStateBuilder;
use types::*;

pub const BENCHING_SAMPLE_SIZE: usize = 10;
pub const SMALL_BENCHING_SAMPLE_SIZE: usize = 10;

/// Run the benchmarking suite on a foundation spec with 16,384 validators.
pub fn bench_epoch_processing_n_validators(c: &mut Criterion, validator_count: usize) {
    let spec = ChainSpec::foundation();

    let mut builder =
        TestingBeaconStateBuilder::from_default_keypairs_file_if_exists(validator_count, &spec);

    // Set the state to be just before an epoch transition.
    let target_slot = (spec.genesis_epoch + 4).end_slot(spec.slots_per_epoch);
    builder.teleport_to_slot(target_slot, &spec);

    // Builds all caches; benches will not contain shuffling/committee building times.
    builder.build_caches(&spec).unwrap();

    // Inserts one attestation with full participation for each committee able to include an
    // attestation in this state.
    builder.insert_attestations(&spec);

    let (state, _keypairs) = builder.build();

    // Assert that the state has an attestations for each committee that is able to include an
    // attestation in the state.
    let committees_per_epoch = spec.get_epoch_committee_count(validator_count);
    let committees_per_slot = committees_per_epoch / spec.slots_per_epoch;
    let previous_epoch_attestations = committees_per_epoch;
    let current_epoch_attestations =
        committees_per_slot * (spec.slots_per_epoch - spec.min_attestation_inclusion_delay);
    assert_eq!(
        state.latest_attestations.len() as u64,
        previous_epoch_attestations + current_epoch_attestations,
        "The state should have an attestation for each committee."
    );

    // Assert that each attestation in the state has full participation.
    let committee_size = validator_count / committees_per_epoch as usize;
    for a in &state.latest_attestations {
        assert_eq!(
            a.aggregation_bitfield.num_set_bits(),
            committee_size,
            "Each attestation in the state should have full participation"
        );
    }

    // Assert that we will run the first arm of process_rewards_and_penalities
    let epochs_since_finality = state.next_epoch(&spec) - state.finalized_epoch;
    assert_eq!(
        epochs_since_finality, 4,
        "Epochs since finality should be 4"
    );

    bench_epoch_processing(c, &state, &spec, &format!("{}_validators", validator_count));
}

/// Run the detailed benchmarking suite on the given `BeaconState`.
///
/// `desc` will be added to the title of each bench.
fn bench_epoch_processing(c: &mut Criterion, state: &BeaconState, spec: &ChainSpec, desc: &str) {
    let state_clone = state.clone();
    let spec_clone = spec.clone();
    c.bench(
        &format!("{}/epoch_processing", desc),
        Benchmark::new("process_eth1_data", move |b| {
            b.iter_batched(
                || state_clone.clone(),
                |mut state| {
                    process_eth1_data(&mut state, &spec_clone);
                    state
                },
                criterion::BatchSize::SmallInput,
            )
        })
        .sample_size(BENCHING_SAMPLE_SIZE),
    );

    let state_clone = state.clone();
    let spec_clone = spec.clone();
    c.bench(
        &format!("{}/epoch_processing", desc),
        Benchmark::new("initialize_validator_statuses", move |b| {
            b.iter_batched(
                || state_clone.clone(),
                |mut state| {
                    initialize_validator_statuses(&mut state, &spec_clone).unwrap();
                    state
                },
                criterion::BatchSize::SmallInput,
            )
        })
        .sample_size(BENCHING_SAMPLE_SIZE),
    );

    let state_clone = state.clone();
    let spec_clone = spec.clone();
    let attesters = initialize_validator_statuses(&state, &spec).unwrap();
    c.bench(
        &format!("{}/epoch_processing", desc),
        Benchmark::new("process_justification", move |b| {
            b.iter_batched(
                || state_clone.clone(),
                |mut state| {
                    process_justification(&mut state, &attesters.total_balances, &spec_clone);
                    state
                },
                criterion::BatchSize::SmallInput,
            )
        })
        .sample_size(10),
    );

    let state_clone = state.clone();
    let spec_clone = spec.clone();
    c.bench(
        &format!("{}/epoch_processing", desc),
        Benchmark::new("process_crosslinks", move |b| {
            b.iter_batched(
                || state_clone.clone(),
                |mut state| black_box(process_crosslinks(&mut state, &spec_clone).unwrap()),
                criterion::BatchSize::SmallInput,
            )
        })
        .sample_size(BENCHING_SAMPLE_SIZE),
    );

    let mut state_clone = state.clone();
    let spec_clone = spec.clone();
    let attesters = initialize_validator_statuses(&state, &spec).unwrap();
    let winning_root_for_shards = process_crosslinks(&mut state_clone, &spec).unwrap();
    c.bench(
        &format!("{}/epoch_processing", desc),
        Benchmark::new("process_rewards_and_penalties", move |b| {
            b.iter_batched(
                || (state_clone.clone(), attesters.clone()),
                |(mut state, mut attesters)| {
                    process_rewards_and_penalities(
                        &mut state,
                        &mut attesters,
                        &winning_root_for_shards,
                        &spec_clone,
                    )
                    .unwrap();
                    state
                },
                criterion::BatchSize::SmallInput,
            )
        })
        .sample_size(SMALL_BENCHING_SAMPLE_SIZE),
    );

    let state_clone = state.clone();
    let spec_clone = spec.clone();
    c.bench(
        &format!("{}/epoch_processing", desc),
        Benchmark::new("process_ejections", move |b| {
            b.iter_batched(
                || state_clone.clone(),
                |mut state| {
                    state.process_ejections(&spec_clone);
                    state
                },
                criterion::BatchSize::SmallInput,
            )
        })
        .sample_size(BENCHING_SAMPLE_SIZE),
    );

    let state_clone = state.clone();
    let spec_clone = spec.clone();
    c.bench(
        &format!("{}/epoch_processing", desc),
        Benchmark::new("process_validator_registry", move |b| {
            b.iter_batched(
                || state_clone.clone(),
                |mut state| {
                    process_validator_registry(&mut state, &spec_clone).unwrap();
                    state
                },
                criterion::BatchSize::SmallInput,
            )
        })
        .sample_size(BENCHING_SAMPLE_SIZE),
    );

    let state_clone = state.clone();
    let spec_clone = spec.clone();
    c.bench(
        &format!("{}/epoch_processing", desc),
        Benchmark::new("update_active_tree_index_roots", move |b| {
            b.iter_batched(
                || state_clone.clone(),
                |mut state| {
                    update_active_tree_index_roots(&mut state, &spec_clone).unwrap();
                    state
                },
                criterion::BatchSize::SmallInput,
            )
        })
        .sample_size(BENCHING_SAMPLE_SIZE),
    );

    let state_clone = state.clone();
    let spec_clone = spec.clone();
    c.bench(
        &format!("{}/epoch_processing", desc),
        Benchmark::new("update_latest_slashed_balances", move |b| {
            b.iter_batched(
                || state_clone.clone(),
                |mut state| {
                    update_latest_slashed_balances(&mut state, &spec_clone);
                    state
                },
                criterion::BatchSize::SmallInput,
            )
        })
        .sample_size(BENCHING_SAMPLE_SIZE),
    );

    let state_clone = state.clone();
    let spec_clone = spec.clone();
    c.bench(
        &format!("{}/epoch_processing", desc),
        Benchmark::new("clean_attestations", move |b| {
            b.iter_batched(
                || state_clone.clone(),
                |mut state| {
                    clean_attestations(&mut state, &spec_clone);
                    state
                },
                criterion::BatchSize::SmallInput,
            )
        })
        .sample_size(BENCHING_SAMPLE_SIZE),
    );

    let state_clone = state.clone();
    let spec_clone = spec.clone();
    c.bench(
        &format!("{}/epoch_processing", desc),
        Benchmark::new("per_epoch_processing", move |b| {
            b.iter_batched(
                || state_clone.clone(),
                |mut state| black_box(per_epoch_processing(&mut state, &spec_clone).unwrap()),
                criterion::BatchSize::SmallInput,
            )
        })
        .sample_size(SMALL_BENCHING_SAMPLE_SIZE),
    );

    let state_clone = state.clone();
    c.bench(
        &format!("{}/epoch_processing", desc),
        Benchmark::new("tree_hash_state", move |b| {
            b.iter(|| black_box(state_clone.hash_tree_root()))
        })
        .sample_size(SMALL_BENCHING_SAMPLE_SIZE),
    );
}
<commit_msg>Remove assertion in benches<commit_after>use criterion::Criterion;
use criterion::{black_box, Benchmark};
use ssz::TreeHash;
use state_processing::{
    per_epoch_processing,
    per_epoch_processing::{
        clean_attestations, initialize_validator_statuses, process_crosslinks, process_eth1_data,
        process_justification, process_rewards_and_penalities, process_validator_registry,
        update_active_tree_index_roots, update_latest_slashed_balances,
    },
};
use types::test_utils::TestingBeaconStateBuilder;
use types::*;

pub const BENCHING_SAMPLE_SIZE: usize = 10;
pub const SMALL_BENCHING_SAMPLE_SIZE: usize = 10;

/// Run the benchmarking suite on a foundation spec with 16,384 validators.
pub fn bench_epoch_processing_n_validators(c: &mut Criterion, validator_count: usize) {
    let spec = ChainSpec::foundation();

    let mut builder =
        TestingBeaconStateBuilder::from_default_keypairs_file_if_exists(validator_count, &spec);

    // Set the state to be just before an epoch transition.
    let target_slot = (spec.genesis_epoch + 4).end_slot(spec.slots_per_epoch);
    builder.teleport_to_slot(target_slot, &spec);

    // Builds all caches; benches will not contain shuffling/committee building times.
    builder.build_caches(&spec).unwrap();

    // Inserts one attestation with full participation for each committee able to include an
    // attestation in this state.
    builder.insert_attestations(&spec);

    let (state, _keypairs) = builder.build();

    // Assert that the state has an attestations for each committee that is able to include an
    // attestation in the state.
    let committees_per_epoch = spec.get_epoch_committee_count(validator_count);
    let committees_per_slot = committees_per_epoch / spec.slots_per_epoch;
    let previous_epoch_attestations = committees_per_epoch;
    let current_epoch_attestations =
        committees_per_slot * (spec.slots_per_epoch - spec.min_attestation_inclusion_delay);
    assert_eq!(
        state.latest_attestations.len() as u64,
        previous_epoch_attestations + current_epoch_attestations,
        "The state should have an attestation for each committee."
    );

    // Assert that we will run the first arm of process_rewards_and_penalities
    let epochs_since_finality = state.next_epoch(&spec) - state.finalized_epoch;
    assert_eq!(
        epochs_since_finality, 4,
        "Epochs since finality should be 4"
    );

    bench_epoch_processing(c, &state, &spec, &format!("{}_validators", validator_count));
}

/// Run the detailed benchmarking suite on the given `BeaconState`.
///
/// `desc` will be added to the title of each bench.
fn bench_epoch_processing(c: &mut Criterion, state: &BeaconState, spec: &ChainSpec, desc: &str) {
    let state_clone = state.clone();
    let spec_clone = spec.clone();
    c.bench(
        &format!("{}/epoch_processing", desc),
        Benchmark::new("process_eth1_data", move |b| {
            b.iter_batched(
                || state_clone.clone(),
                |mut state| {
                    process_eth1_data(&mut state, &spec_clone);
                    state
                },
                criterion::BatchSize::SmallInput,
            )
        })
        .sample_size(BENCHING_SAMPLE_SIZE),
    );

    let state_clone = state.clone();
    let spec_clone = spec.clone();
    c.bench(
        &format!("{}/epoch_processing", desc),
        Benchmark::new("initialize_validator_statuses", move |b| {
            b.iter_batched(
                || state_clone.clone(),
                |mut state| {
                    initialize_validator_statuses(&mut state, &spec_clone).unwrap();
                    state
                },
                criterion::BatchSize::SmallInput,
            )
        })
        .sample_size(BENCHING_SAMPLE_SIZE),
    );

    let state_clone = state.clone();
    let spec_clone = spec.clone();
    let attesters = initialize_validator_statuses(&state, &spec).unwrap();
    c.bench(
        &format!("{}/epoch_processing", desc),
        Benchmark::new("process_justification", move |b| {
            b.iter_batched(
                || state_clone.clone(),
                |mut state| {
                    process_justification(&mut state, &attesters.total_balances, &spec_clone);
                    state
                },
                criterion::BatchSize::SmallInput,
            )
        })
        .sample_size(10),
    );

    let state_clone = state.clone();
    let spec_clone = spec.clone();
    c.bench(
        &format!("{}/epoch_processing", desc),
        Benchmark::new("process_crosslinks", move |b| {
            b.iter_batched(
                || state_clone.clone(),
                |mut state| black_box(process_crosslinks(&mut state, &spec_clone).unwrap()),
                criterion::BatchSize::SmallInput,
            )
        })
        .sample_size(BENCHING_SAMPLE_SIZE),
    );

    let mut state_clone = state.clone();
    let spec_clone = spec.clone();
    let attesters = initialize_validator_statuses(&state, &spec).unwrap();
    let winning_root_for_shards = process_crosslinks(&mut state_clone, &spec).unwrap();
    c.bench(
        &format!("{}/epoch_processing", desc),
        Benchmark::new("process_rewards_and_penalties", move |b| {
            b.iter_batched(
                || (state_clone.clone(), attesters.clone()),
                |(mut state, mut attesters)| {
                    process_rewards_and_penalities(
                        &mut state,
                        &mut attesters,
                        &winning_root_for_shards,
                        &spec_clone,
                    )
                    .unwrap();
                    state
                },
                criterion::BatchSize::SmallInput,
            )
        })
        .sample_size(SMALL_BENCHING_SAMPLE_SIZE),
    );

    let state_clone = state.clone();
    let spec_clone = spec.clone();
    c.bench(
        &format!("{}/epoch_processing", desc),
        Benchmark::new("process_ejections", move |b| {
            b.iter_batched(
                || state_clone.clone(),
                |mut state| {
                    state.process_ejections(&spec_clone);
                    state
                },
                criterion::BatchSize::SmallInput,
            )
        })
        .sample_size(BENCHING_SAMPLE_SIZE),
    );

    let state_clone = state.clone();
    let spec_clone = spec.clone();
    c.bench(
        &format!("{}/epoch_processing", desc),
        Benchmark::new("process_validator_registry", move |b| {
            b.iter_batched(
                || state_clone.clone(),
                |mut state| {
                    process_validator_registry(&mut state, &spec_clone).unwrap();
                    state
                },
                criterion::BatchSize::SmallInput,
            )
        })
        .sample_size(BENCHING_SAMPLE_SIZE),
    );

    let state_clone = state.clone();
    let spec_clone = spec.clone();
    c.bench(
        &format!("{}/epoch_processing", desc),
        Benchmark::new("update_active_tree_index_roots", move |b| {
            b.iter_batched(
                || state_clone.clone(),
                |mut state| {
                    update_active_tree_index_roots(&mut state, &spec_clone).unwrap();
                    state
                },
                criterion::BatchSize::SmallInput,
            )
        })
        .sample_size(BENCHING_SAMPLE_SIZE),
    );

    let state_clone = state.clone();
    let spec_clone = spec.clone();
    c.bench(
        &format!("{}/epoch_processing", desc),
        Benchmark::new("update_latest_slashed_balances", move |b| {
            b.iter_batched(
                || state_clone.clone(),
                |mut state| {
                    update_latest_slashed_balances(&mut state, &spec_clone);
                    state
                },
                criterion::BatchSize::SmallInput,
            )
        })
        .sample_size(BENCHING_SAMPLE_SIZE),
    );

    let state_clone = state.clone();
    let spec_clone = spec.clone();
    c.bench(
        &format!("{}/epoch_processing", desc),
        Benchmark::new("clean_attestations", move |b| {
            b.iter_batched(
                || state_clone.clone(),
                |mut state| {
                    clean_attestations(&mut state, &spec_clone);
                    state
                },
                criterion::BatchSize::SmallInput,
            )
        })
        .sample_size(BENCHING_SAMPLE_SIZE),
    );

    let state_clone = state.clone();
    let spec_clone = spec.clone();
    c.bench(
        &format!("{}/epoch_processing", desc),
        Benchmark::new("per_epoch_processing", move |b| {
            b.iter_batched(
                || state_clone.clone(),
                |mut state| black_box(per_epoch_processing(&mut state, &spec_clone).unwrap()),
                criterion::BatchSize::SmallInput,
            )
        })
        .sample_size(SMALL_BENCHING_SAMPLE_SIZE),
    );

    let state_clone = state.clone();
    c.bench(
        &format!("{}/epoch_processing", desc),
        Benchmark::new("tree_hash_state", move |b| {
            b.iter(|| black_box(state_clone.hash_tree_root()))
        })
        .sample_size(SMALL_BENCHING_SAMPLE_SIZE),
    );
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::io;
use std::io::prelude::*;

type SudokuCandidate = u64;  // or u32
type SudokuCellGroups = HashMap<usize, Vec<Vec<usize>>>;

const SUDOKU_DIGIT_BASE: u32 = 10;
const SUDOKU_BOX_SIZE: usize = 3;
const SUDOKU_NO_CANDIDATES: SudokuCandidate = 0;
const SUDOKU_ALL_CANDIDATES: SudokuCandidate = ((1 << SUDOKU_CANDIDATE_SIZE) - 1);

const SUDOKU_GROUP_SIZE: usize = SUDOKU_BOX_SIZE * SUDOKU_BOX_SIZE;
const SUDOKU_CANDIDATE_SIZE: usize = SUDOKU_GROUP_SIZE;
const SUDOKU_CELL_SIZE: usize = SUDOKU_GROUP_SIZE * SUDOKU_GROUP_SIZE;

// Sudoku cell that contains candidates as a bitboard
#[derive(Clone, Copy)]
struct SudokuCell {
    candidates: SudokuCandidate
}

impl SudokuCell {
    fn new() -> SudokuCell {
        SudokuCell { candidates: SUDOKU_NO_CANDIDATES }
    }

    fn to_string(&self) -> String {
        let mut line = String::new();
        for candidate in 0..(SUDOKU_CANDIDATE_SIZE as SudokuCandidate) {
            if self.has_candidate(&candidate) {
                line.push_str(&(candidate+1).to_string());
            }
        }
        line
    }

    fn preset_full_candidate(&mut self) {
        self.candidates = SUDOKU_ALL_CANDIDATES;
    }

    // Input '1'..'9'
    fn preset_candidate(&mut self, ch: char) {
        let code_digit_base = '1'.to_digit(SUDOKU_DIGIT_BASE).unwrap();
        match ch {
            '1'...'9' => {
                let number = (ch.to_digit(SUDOKU_DIGIT_BASE).unwrap() - code_digit_base) as SudokuCandidate;
                self.candidates = self.digit_to_candidate(&number);
            }
            _ => {}
        }
    }

    // Input 0..8
    fn overwrite_candidate(&mut self, candidate: SudokuCandidate) {
        if self.count_candidates() > 1 {
            self.candidates = self.digit_to_candidate(&candidate);
        }
    }

    fn count_candidates(&self) -> usize {
        (self.candidates & SUDOKU_ALL_CANDIDATES).count_ones() as usize
    }

    fn has_unique_candidate(&self) -> bool {
        SudokuCell::is_unique_candidate(self.candidates)
    }

    fn is_unique_candidate(candidates: SudokuCandidate) -> bool {
        // Check whether only one bit is set
        candidates > 0 && (candidates & (candidates - 1)) == 0
    }

    fn has_all_candidates(&self) -> bool {
        self.candidates == SUDOKU_ALL_CANDIDATES
    }

    // Input 0..8
    fn can_mask(&mut self, candidates: SudokuCandidate) -> bool {
        (self.candidates & self.digit_to_candidate(&candidates)) != 0
    }

    fn merge_candidate(&mut self, rhs: &SudokuCell) {
        self.candidates |= rhs.candidates;
    }

    // Returns whether no candidates are merged
    fn merge_unique_candidate(&mut self, rhs: &SudokuCell) -> bool {
        if rhs.has_unique_candidate() {
            if (self.candidates & rhs.candidates) != 0 {
                return true;
            }
            self.candidates |= rhs.candidates;
        }
        false
    }

    fn filter_by_candidates(&mut self, rhs: &SudokuCell) {
        if !self.has_unique_candidate() {
            self.candidates &= !rhs.candidates;
        }
    }

    fn fill_unused_candidate(&mut self, rhs: &SudokuCell) {
        if !self.has_unique_candidate() {
            let candidates = SUDOKU_ALL_CANDIDATES & !rhs.candidates;
            if SudokuCell::is_unique_candidate(candidates) {
                self.candidates = candidates;
            }
        }
    }

    // Input 0..8
    fn digit_to_candidate(&self, candidate: &SudokuCandidate) -> SudokuCandidate {
        (1 as SudokuCandidate) << candidate
    }

    // Input 0..8
    fn has_candidate(&self, candidate: &SudokuCandidate) -> bool {
        (self.candidates & self.digit_to_candidate(&candidate)) != 0
    }
}

// Generator for sudoku cellgroups
struct SudokuGroupGen {
    index: usize
}

impl SudokuGroupGen {
    fn new() -> SudokuGroupGen {
        SudokuGroupGen { index:0 }
    }

    fn next(&mut self) -> Option<Vec<usize>> {
        if self.index < SUDOKU_GROUP_SIZE {
            let lower = self.index * SUDOKU_GROUP_SIZE;
            let upper = (self.index + 1) * SUDOKU_GROUP_SIZE;
            self.index += 1;
            Some((lower..upper).collect::<Vec<_>>())
        } else if self.index < (SUDOKU_GROUP_SIZE * 2) {
            let column = self.index - SUDOKU_GROUP_SIZE;
            self.index += 1;
            Some((0..SUDOKU_GROUP_SIZE).map(
                |row| column + row * SUDOKU_GROUP_SIZE).collect::<Vec<_>>())
        } else if self.index < (SUDOKU_GROUP_SIZE * 3) {
            let box_index = self.index - SUDOKU_GROUP_SIZE * 2;
            let offset = (box_index % 3) * 3  + (box_index / 3) * SUDOKU_GROUP_SIZE * 3;
            let mut indexes : Vec<usize> = Vec::new();
            for row in 0..SUDOKU_BOX_SIZE {
                let x = (0..SUDOKU_BOX_SIZE).map(
                    |column| row * SUDOKU_GROUP_SIZE + column + offset).collect::<Vec<_>>();
                indexes.extend(x);
            }
            self.index += 1;
            Some(indexes)
        } else {
            None
        }
    }
}

// Sudoku cell map
struct SudokuMap<'a> {
    cells : Vec<SudokuCell>,
    groups : &'a SudokuCellGroups,
}

impl<'a> SudokuMap<'a> {
    fn new(line: &str, cell_groups: &'a SudokuCellGroups) -> SudokuMap<'a> {
        let mut cells = vec![SudokuCell::new(); SUDOKU_CELL_SIZE];
        for cell in &mut cells {
            cell.preset_full_candidate();
        }

        // Leave tail cells if the line is shorter than the number of cells.
        for (index, ch) in line.chars().enumerate() {
            cells[index].preset_candidate(ch);
        };
        SudokuMap { cells:cells, groups: cell_groups }
    }

    fn to_string(&self, one_line: bool) -> String {
        let mut full_text = String::new();

        for row in 0..SUDOKU_GROUP_SIZE {
            let mut line = String::new();
            for column in 0..SUDOKU_GROUP_SIZE {
                let index = row * SUDOKU_GROUP_SIZE + column;
                let cell_str = self.cells[index].to_string();
                line.push_str(&cell_str);
                if !one_line {
                    line.push_str(":");
                }
            }
            if !one_line {
                line.push_str("\n");
            }
            full_text.push_str(&line);
        }
        full_text
    }

    fn solve(&mut self) -> bool {
        let mut prev_count = SUDOKU_CELL_SIZE * SUDOKU_CANDIDATE_SIZE;
        loop {
            self.filter_unique_candidates();
            self.find_unused_candidates();

            if self.is_solved() {
                return true;
            }

            if !self.is_consistent() {
                return false;
            }

            let count = (0..SUDOKU_CELL_SIZE).fold(
                0, |sum, index| sum + self.cells[index].count_candidates());
            if prev_count == count {
                break;
            }
            prev_count = count;
        }

        let index_min_count = self.find_backtracking_target();
        if self.cells[index_min_count].count_candidates() <= 1 {
            return false;
        }

        for candidate in 0..(SUDOKU_CANDIDATE_SIZE as SudokuCandidate) {
            if self.cells[index_min_count].can_mask(candidate) {
                let mut new_map = SudokuMap::new("", self.groups);
                new_map.cells = self.cells.clone();
                new_map.cells[index_min_count].overwrite_candidate(candidate);
                if new_map.solve() {
                    self.cells = new_map.cells;
                    return self.is_solved();
                }
            }
        }

        false
    }

    fn filter_unique_candidates(&mut self) {
        for target_index in 0..SUDOKU_CELL_SIZE {
            if self.cells[target_index].has_unique_candidate() {
                continue;
            }

            let mut sum = SudokuCell::new();
            for group in self.groups[&target_index].iter() {
                for cell_index in group.iter() {
                    if *cell_index != target_index {
                        sum.merge_unique_candidate(&self.cells[*cell_index]);
                    }
                }
            }
            self.cells[target_index].filter_by_candidates(&sum);
        }
    }

    fn find_unused_candidates(&mut self) {
        for target_index in 0..SUDOKU_CELL_SIZE {
            if self.cells[target_index].has_unique_candidate() {
                continue;
            }

            for group in self.groups[&target_index].iter() {
                let mut sum = SudokuCell::new();
                for cell_index in group.iter() {
                    if *cell_index != target_index {
                        sum.merge_candidate(&self.cells[*cell_index]);
                    }
                }
                self.cells[target_index].fill_unused_candidate(&sum);
            }
        }
    }

    fn find_backtracking_target(&self) -> usize {
        // SUDOKU_CELL_SIZE = a large enough number
        let mut min_count = SUDOKU_CELL_SIZE;
        let mut cell_counts = Vec::new();

        for row_index in 0..SUDOKU_GROUP_SIZE {
            let mut row_counts = vec![SUDOKU_CELL_SIZE; SUDOKU_GROUP_SIZE];
            for column_index in 0..SUDOKU_GROUP_SIZE {
                let cell_count = self.cells[row_index * SUDOKU_GROUP_SIZE + column_index].count_candidates();
                if cell_count > 1 {
                    row_counts[column_index] = cell_count;
                    if cell_count < min_count {
                        min_count = cell_count;
                    }
                }
            }
            cell_counts.push(row_counts);
        }

        let row_counts = cell_counts.iter().map(
            |row| row.iter().fold(
                0, |sum, &count| sum + if count == min_count { 1 } else { 0 })).
            collect::<Vec<usize>>();

        let max_row_count = row_counts.iter().max().unwrap();
        let target_row = row_counts.iter().position(|&count| count == *max_row_count).unwrap();
        let target_column = cell_counts[target_row].iter().position(
            |&count| count == min_count).unwrap();

        target_row * SUDOKU_GROUP_SIZE + target_column
    }

    fn is_solved(&self) -> bool {
        for cell_index in 0..SUDOKU_CELL_SIZE {
            if !self.cells[cell_index].has_unique_candidate() {
                return false;
            }
        }

        let mut group_gen = SudokuGroupGen::new();
        while let Some(indexes) = group_gen.next() {
            if !self.is_group_solved(indexes) {
                return false;
            }
        }
        true
    }

    fn is_group_solved(&self, cell_indexes: Vec<usize>) -> bool {
        let mut sum = SudokuCell::new();
        let mut conflict = false;
        for cell_index in cell_indexes {
            let cell = &self.cells[cell_index];
            conflict |= sum.merge_unique_candidate(&cell);
        }
        sum.has_all_candidates() && !conflict
    }

    fn is_consistent(&self) -> bool {
        let mut group_gen = SudokuGroupGen::new();
        while let Some(indexes) = group_gen.next() {
            if !self.is_group_consistent(indexes) {
                return false;
            }
        }
        true
    }

    fn is_group_consistent(&self, cell_indexes: Vec<usize>) -> bool {
        let mut all_candidates = SudokuCell::new();
        let mut unique_candidates = SudokuCell::new();
        let mut conflict = false;

        for cell_index in cell_indexes {
            all_candidates.merge_candidate(&self.cells[cell_index]);
            conflict |= unique_candidates.merge_unique_candidate(&self.cells[cell_index]);
            if conflict {
                return false;
            }
        }
        all_candidates.has_all_candidates() && !conflict
    }
}

// Set of sudoku cellgroups
fn create_group_index_set() -> SudokuCellGroups {
    let mut groups = HashMap::new();
    for cell_index in 0..SUDOKU_CELL_SIZE {
        let mut group_candidates = Vec::new();
        let row_number = cell_index / SUDOKU_GROUP_SIZE;
        let row_lower = row_number * SUDOKU_GROUP_SIZE;
        let row_upper = (row_number + 1) * SUDOKU_GROUP_SIZE;
        let row = (row_lower..row_upper).collect::<Vec<_>>();
        group_candidates.push(row);

        let column_number = cell_index % SUDOKU_GROUP_SIZE;
        let column = (0..SUDOKU_GROUP_SIZE).map(|i| column_number + i * SUDOKU_GROUP_SIZE).collect::<Vec<_>>();
        group_candidates.push(column);

        let box_offset = (row_number / SUDOKU_BOX_SIZE) * SUDOKU_BOX_SIZE * SUDOKU_GROUP_SIZE +
            (column_number / SUDOKU_BOX_SIZE) * SUDOKU_BOX_SIZE;
        let mut cell_box = Vec::new();
        for row_offset in 0..SUDOKU_BOX_SIZE {
            let x = (0..SUDOKU_BOX_SIZE).map(|column_offset| row_offset * SUDOKU_GROUP_SIZE + column_offset + box_offset);
            cell_box.extend(x);
        }
        group_candidates.push(cell_box);
        groups.insert(cell_index, group_candidates);
    }
    groups
}

fn main() {
    // Shares cell groups between all questions
    let cell_groups = create_group_index_set();
    println!("Solving in Rust");

    let stdin = io::stdin();
    let mut lines = Vec::new();
    let mut count = 0;
    for line in stdin.lock().lines() {
        let line = line.unwrap();
        lines.push(line);
        count += 1;
    }

    let mut result = true;
    let mut answers = String::new();
    for line in lines {
        let mut sudoku_map = SudokuMap::new(&line, &cell_groups);
        result &= sudoku_map.solve();
        let s = sudoku_map.to_string(true);
        answers.push_str(&s);
        answers.push_str("\n");
    }

    print!("{}", answers);
    if result {
        println!("All {} cases passed.", count);
    }
}
<commit_msg>Run multiple threads on the Rust solver<commit_after>use std::collections::HashMap;
use std::io;
use std::io::prelude::*;

type SudokuCandidate = u64;  // or u32
type SudokuCellGroups = HashMap<usize, Vec<Vec<usize>>>;

const SUDOKU_MAX_THREADS: usize = 4;
const SUDOKU_DIGIT_BASE: u32 = 10;
const SUDOKU_BOX_SIZE: usize = 3;
const SUDOKU_NO_CANDIDATES: SudokuCandidate = 0;
const SUDOKU_ALL_CANDIDATES: SudokuCandidate = ((1 << SUDOKU_CANDIDATE_SIZE) - 1);

const SUDOKU_GROUP_SIZE: usize = SUDOKU_BOX_SIZE * SUDOKU_BOX_SIZE;
const SUDOKU_CANDIDATE_SIZE: usize = SUDOKU_GROUP_SIZE;
const SUDOKU_CELL_SIZE: usize = SUDOKU_GROUP_SIZE * SUDOKU_GROUP_SIZE;

// Sudoku cell that contains candidates as a bitboard
#[derive(Clone, Copy)]
struct SudokuCell {
    candidates: SudokuCandidate
}

impl SudokuCell {
    fn new() -> SudokuCell {
        SudokuCell { candidates: SUDOKU_NO_CANDIDATES }
    }

    fn to_string(&self) -> String {
        let mut line = String::new();
        for candidate in 0..(SUDOKU_CANDIDATE_SIZE as SudokuCandidate) {
            if self.has_candidate(&candidate) {
                line.push_str(&(candidate+1).to_string());
            }
        }
        line
    }

    fn preset_full_candidate(&mut self) {
        self.candidates = SUDOKU_ALL_CANDIDATES;
    }

    // Input '1'..'9'
    fn preset_candidate(&mut self, ch: char) {
        let code_digit_base = '1'.to_digit(SUDOKU_DIGIT_BASE).unwrap();
        match ch {
            '1'...'9' => {
                let number = (ch.to_digit(SUDOKU_DIGIT_BASE).unwrap() - code_digit_base) as SudokuCandidate;
                self.candidates = self.digit_to_candidate(&number);
            }
            _ => {}
        }
    }

    // Input 0..8
    fn overwrite_candidate(&mut self, candidate: SudokuCandidate) {
        if self.count_candidates() > 1 {
            self.candidates = self.digit_to_candidate(&candidate);
        }
    }

    fn count_candidates(&self) -> usize {
        (self.candidates & SUDOKU_ALL_CANDIDATES).count_ones() as usize
    }

    fn has_unique_candidate(&self) -> bool {
        SudokuCell::is_unique_candidate(self.candidates)
    }

    fn is_unique_candidate(candidates: SudokuCandidate) -> bool {
        // Check whether only one bit is set
        candidates > 0 && (candidates & (candidates - 1)) == 0
    }

    fn has_all_candidates(&self) -> bool {
        self.candidates == SUDOKU_ALL_CANDIDATES
    }

    // Input 0..8
    fn can_mask(&mut self, candidates: SudokuCandidate) -> bool {
        (self.candidates & self.digit_to_candidate(&candidates)) != 0
    }

    fn merge_candidate(&mut self, rhs: &SudokuCell) {
        self.candidates |= rhs.candidates;
    }

    // Returns whether no candidates are merged
    fn merge_unique_candidate(&mut self, rhs: &SudokuCell) -> bool {
        if rhs.has_unique_candidate() {
            if (self.candidates & rhs.candidates) != 0 {
                return true;
            }
            self.candidates |= rhs.candidates;
        }
        false
    }

    fn filter_by_candidates(&mut self, rhs: &SudokuCell) {
        if !self.has_unique_candidate() {
            self.candidates &= !rhs.candidates;
        }
    }

    fn fill_unused_candidate(&mut self, rhs: &SudokuCell) {
        if !self.has_unique_candidate() {
            let candidates = SUDOKU_ALL_CANDIDATES & !rhs.candidates;
            if SudokuCell::is_unique_candidate(candidates) {
                self.candidates = candidates;
            }
        }
    }

    // Input 0..8
    fn digit_to_candidate(&self, candidate: &SudokuCandidate) -> SudokuCandidate {
        (1 as SudokuCandidate) << candidate
    }

    // Input 0..8
    fn has_candidate(&self, candidate: &SudokuCandidate) -> bool {
        (self.candidates & self.digit_to_candidate(&candidate)) != 0
    }
}

// Generator for sudoku cellgroups
struct SudokuGroupGen {
    index: usize
}

impl SudokuGroupGen {
    fn new() -> SudokuGroupGen {
        SudokuGroupGen { index:0 }
    }

    fn next(&mut self) -> Option<Vec<usize>> {
        if self.index < SUDOKU_GROUP_SIZE {
            let lower = self.index * SUDOKU_GROUP_SIZE;
            let upper = (self.index + 1) * SUDOKU_GROUP_SIZE;
            self.index += 1;
            Some((lower..upper).collect::<Vec<_>>())
        } else if self.index < (SUDOKU_GROUP_SIZE * 2) {
            let column = self.index - SUDOKU_GROUP_SIZE;
            self.index += 1;
            Some((0..SUDOKU_GROUP_SIZE).map(
                |row| column + row * SUDOKU_GROUP_SIZE).collect::<Vec<_>>())
        } else if self.index < (SUDOKU_GROUP_SIZE * 3) {
            let box_index = self.index - SUDOKU_GROUP_SIZE * 2;
            let offset = (box_index % 3) * 3  + (box_index / 3) * SUDOKU_GROUP_SIZE * 3;
            let mut indexes : Vec<usize> = Vec::new();
            for row in 0..SUDOKU_BOX_SIZE {
                let x = (0..SUDOKU_BOX_SIZE).map(
                    |column| row * SUDOKU_GROUP_SIZE + column + offset).collect::<Vec<_>>();
                indexes.extend(x);
            }
            self.index += 1;
            Some(indexes)
        } else {
            None
        }
    }
}

// Sudoku cell map
struct SudokuMap<'a> {
    cells : Vec<SudokuCell>,
    groups : &'a SudokuCellGroups,
}

impl<'a> SudokuMap<'a> {
    fn new(line: &str, cell_groups: &'a SudokuCellGroups) -> SudokuMap<'a> {
        let mut cells = vec![SudokuCell::new(); SUDOKU_CELL_SIZE];
        for cell in &mut cells {
            cell.preset_full_candidate();
        }

        // Leave tail cells if the line is shorter than the number of cells.
        for (index, ch) in line.chars().enumerate() {
            cells[index].preset_candidate(ch);
        };
        SudokuMap { cells:cells, groups: cell_groups }
    }

    fn to_string(&self, one_line: bool) -> String {
        let mut full_text = String::new();

        for row in 0..SUDOKU_GROUP_SIZE {
            let mut line = String::new();
            for column in 0..SUDOKU_GROUP_SIZE {
                let index = row * SUDOKU_GROUP_SIZE + column;
                let cell_str = self.cells[index].to_string();
                line.push_str(&cell_str);
                if !one_line {
                    line.push_str(":");
                }
            }
            if !one_line {
                line.push_str("\n");
            }
            full_text.push_str(&line);
        }
        full_text
    }

    fn solve(&mut self) -> bool {
        let mut prev_count = SUDOKU_CELL_SIZE * SUDOKU_CANDIDATE_SIZE;
        loop {
            self.filter_unique_candidates();
            self.find_unused_candidates();

            if self.is_solved() {
                return true;
            }

            if !self.is_consistent() {
                return false;
            }

            let count = (0..SUDOKU_CELL_SIZE).fold(
                0, |sum, index| sum + self.cells[index].count_candidates());
            if prev_count == count {
                break;
            }
            prev_count = count;
        }

        let index_min_count = self.find_backtracking_target();
        if self.cells[index_min_count].count_candidates() <= 1 {
            return false;
        }

        for candidate in 0..(SUDOKU_CANDIDATE_SIZE as SudokuCandidate) {
            if self.cells[index_min_count].can_mask(candidate) {
                let mut new_map = SudokuMap::new("", self.groups);
                new_map.cells = self.cells.clone();
                new_map.cells[index_min_count].overwrite_candidate(candidate);
                if new_map.solve() {
                    self.cells = new_map.cells;
                    return self.is_solved();
                }
            }
        }

        false
    }

    fn filter_unique_candidates(&mut self) {
        for target_index in 0..SUDOKU_CELL_SIZE {
            if self.cells[target_index].has_unique_candidate() {
                continue;
            }

            let mut sum = SudokuCell::new();
            for group in self.groups[&target_index].iter() {
                for cell_index in group.iter() {
                    if *cell_index != target_index {
                        sum.merge_unique_candidate(&self.cells[*cell_index]);
                    }
                }
            }
            self.cells[target_index].filter_by_candidates(&sum);
        }
    }

    fn find_unused_candidates(&mut self) {
        for target_index in 0..SUDOKU_CELL_SIZE {
            if self.cells[target_index].has_unique_candidate() {
                continue;
            }

            for group in self.groups[&target_index].iter() {
                let mut sum = SudokuCell::new();
                for cell_index in group.iter() {
                    if *cell_index != target_index {
                        sum.merge_candidate(&self.cells[*cell_index]);
                    }
                }
                self.cells[target_index].fill_unused_candidate(&sum);
            }
        }
    }

    fn find_backtracking_target(&self) -> usize {
        // SUDOKU_CELL_SIZE = a large enough number
        let mut min_count = SUDOKU_CELL_SIZE;
        let mut cell_counts = Vec::new();

        for row_index in 0..SUDOKU_GROUP_SIZE {
            let mut row_counts = vec![SUDOKU_CELL_SIZE; SUDOKU_GROUP_SIZE];
            for column_index in 0..SUDOKU_GROUP_SIZE {
                let cell_count = self.cells[row_index * SUDOKU_GROUP_SIZE + column_index].count_candidates();
                if cell_count > 1 {
                    row_counts[column_index] = cell_count;
                    if cell_count < min_count {
                        min_count = cell_count;
                    }
                }
            }
            cell_counts.push(row_counts);
        }

        let row_counts = cell_counts.iter().map(
            |row| row.iter().fold(
                0, |sum, &count| sum + if count == min_count { 1 } else { 0 })).
            collect::<Vec<usize>>();

        let max_row_count = row_counts.iter().max().unwrap();
        let target_row = row_counts.iter().position(|&count| count == *max_row_count).unwrap();
        let target_column = cell_counts[target_row].iter().position(
            |&count| count == min_count).unwrap();

        target_row * SUDOKU_GROUP_SIZE + target_column
    }

    fn is_solved(&self) -> bool {
        for cell_index in 0..SUDOKU_CELL_SIZE {
            if !self.cells[cell_index].has_unique_candidate() {
                return false;
            }
        }

        let mut group_gen = SudokuGroupGen::new();
        while let Some(indexes) = group_gen.next() {
            if !self.is_group_solved(indexes) {
                return false;
            }
        }
        true
    }

    fn is_group_solved(&self, cell_indexes: Vec<usize>) -> bool {
        let mut sum = SudokuCell::new();
        let mut conflict = false;
        for cell_index in cell_indexes {
            let cell = &self.cells[cell_index];
            conflict |= sum.merge_unique_candidate(&cell);
        }
        sum.has_all_candidates() && !conflict
    }

    fn is_consistent(&self) -> bool {
        let mut group_gen = SudokuGroupGen::new();
        while let Some(indexes) = group_gen.next() {
            if !self.is_group_consistent(indexes) {
                return false;
            }
        }
        true
    }

    fn is_group_consistent(&self, cell_indexes: Vec<usize>) -> bool {
        let mut all_candidates = SudokuCell::new();
        let mut unique_candidates = SudokuCell::new();
        let mut conflict = false;

        for cell_index in cell_indexes {
            all_candidates.merge_candidate(&self.cells[cell_index]);
            conflict |= unique_candidates.merge_unique_candidate(&self.cells[cell_index]);
            if conflict {
                return false;
            }
        }
        all_candidates.has_all_candidates() && !conflict
    }
}

// Set of sudoku cellgroups
fn create_group_index_set() -> SudokuCellGroups {
    let mut groups = HashMap::new();
    for cell_index in 0..SUDOKU_CELL_SIZE {
        let mut group_candidates = Vec::new();
        let row_number = cell_index / SUDOKU_GROUP_SIZE;
        let row_lower = row_number * SUDOKU_GROUP_SIZE;
        let row_upper = (row_number + 1) * SUDOKU_GROUP_SIZE;
        let row = (row_lower..row_upper).collect::<Vec<_>>();
        group_candidates.push(row);

        let column_number = cell_index % SUDOKU_GROUP_SIZE;
        let column = (0..SUDOKU_GROUP_SIZE).map(|i| column_number + i * SUDOKU_GROUP_SIZE).collect::<Vec<_>>();
        group_candidates.push(column);

        let box_offset = (row_number / SUDOKU_BOX_SIZE) * SUDOKU_BOX_SIZE * SUDOKU_GROUP_SIZE +
            (column_number / SUDOKU_BOX_SIZE) * SUDOKU_BOX_SIZE;
        let mut cell_box = Vec::new();
        for row_offset in 0..SUDOKU_BOX_SIZE {
            let x = (0..SUDOKU_BOX_SIZE).map(|column_offset| row_offset * SUDOKU_GROUP_SIZE + column_offset + box_offset);
            cell_box.extend(x);
        }
        group_candidates.push(cell_box);
        groups.insert(cell_index, group_candidates);
    }
    groups
}

fn exec_threads(count:usize, lines: Vec<String>) {
    // Shares cell groups between all questions
    let n_cores = std::cmp::min(SUDOKU_MAX_THREADS, count);
    let lines_per_core = count / n_cores;
    let mut start_pos = 0;
    let mut children = vec![];

    for core_index in 0..n_cores {
        let cell_groups = create_group_index_set();
        let end_pos = std::cmp::min(count, start_pos + lines_per_core);
        let mut lines_part = Vec::new();
        if core_index + 1 >= n_cores {
            for i in start_pos..count {
                lines_part.push(lines[i].clone());
            }
        } else {
            for i in start_pos..end_pos {
                lines_part.push(lines[i].clone());
            }
        }
        start_pos = end_pos;
        children.push(std::thread::spawn(move || {
            let mut result = true;
            let mut answers = String::new();
            for line in lines_part {
                let mut sudoku_map = SudokuMap::new(&line, &cell_groups);
                result &= sudoku_map.solve();
                let s = sudoku_map.to_string(true);
                answers.push_str(&s);
                answers.push_str("\n");
            }
            (result, answers)
        }))
    }

    let mut total_result = true;
    for child in children {
        let (result, answers) = child.join().unwrap() as (bool, String);
        total_result &= result;
        print!("{}", answers);
    }

    if total_result {
        println!("All {} cases passed.", count);
    }
}

fn main() {
    println!("Solving in Rust");
    let stdin = io::stdin();
    let mut lines = Vec::new();
    let mut count : usize = 0;
    for line in stdin.lock().lines() {
        let line = line.unwrap();
        lines.push(line);
        count += 1;
    }

    exec_threads(count, lines);
}
<|endoftext|>