<commit_before>//! This file contains tests relevant to Rustup's handling of updating PATHs.
//! It depends on self-update working, so if absolutely everything here breaks,
//! check those tests as well.
pub mod mock;

use std::path::PathBuf;

use crate::mock::clitools::{self, expect_ok, Config, Scenario};

// Prefer omitting actually unpacking content while just testing paths.
const INIT_NONE: [&str; 4] = ["rustup-init", "-y", "--default-toolchain", "none"];

#[cfg(unix)]
mod unix {
    use std::fmt::Display;
    use std::fs;

    use rustup::utils::raw;

    use super::*;
    use crate::mock::clitools::expect_err;

    // Let's write a fake .rc which looks vaguely like a real script.
    const FAKE_RC: &str = r#"
# Sources fruity punch.
source ~/fruit/punch

# Adds apples to PATH.
export PATH="$HOME/apple/bin"
"#;
    const DEFAULT_EXPORT: &str = "export PATH=\"$HOME/.cargo/bin:$PATH\"\n";
    const POSIX_SH: &str = "env.sh";

    fn source(dir: impl Display, sh: impl Display) -> String {
        format!("source \"{dir}/{sh}\"\n", dir = dir, sh = sh)
    }

    fn setup(f: &dyn Fn(&Config)) {
        clitools::setup(Scenario::Empty, &|config| {
            f(config);
        });
    }

    #[test]
    fn install_creates_necessary_scripts() {
        setup(&|config| {
            // Override the test harness so that cargo home looks like
            // $HOME/.cargo by removing CARGO_HOME from the environment,
            // otherwise the literal path will be written to the file.

            let mut cmd = clitools::cmd(config, "rustup-init", &INIT_NONE[1..]);
            let files: Vec<PathBuf> = [".cargo/env.sh", ".profile", ".zshenv"]
                .iter()
                .map(|file| config.homedir.join(file))
                .collect();
            for file in &files {
                assert!(!file.exists());
            }
            cmd.env_remove("CARGO_HOME");
            cmd.env("SHELL", "zsh");
            assert!(cmd.output().unwrap().status.success());
            let mut rcs = files.iter();
            let env = rcs.next().unwrap();
            let envfile = fs::read_to_string(&env).unwrap();
            let (_, envfile_export) = envfile.split_at(match envfile.find("export PATH") {
                Some(idx) => idx,
                None => 0,
            });
            assert_eq!(&envfile_export[..DEFAULT_EXPORT.len()], DEFAULT_EXPORT);

            for rc in rcs {
                let expected = source("$HOME/.cargo", POSIX_SH);
                let new_profile = fs::read_to_string(&rc).unwrap();
                assert_eq!(new_profile, expected);
            }
        });
    }

    #[test]
    fn install_updates_bash_rcs() {
        setup(&|config| {
            let rcs: Vec<PathBuf> = [".bashrc", ".bash_profile", ".bash_login", ".profile"]
                .iter()
                .map(|rc| config.homedir.join(rc))
                .collect();
            for rc in &rcs {
                raw::write_file(&rc, FAKE_RC).unwrap();
            }

            expect_ok(config, &INIT_NONE);

            let expected = FAKE_RC.to_owned() + &source(config.cargodir.display(), POSIX_SH);
            for rc in &rcs {
                let new_rc = fs::read_to_string(&rc).unwrap();
                assert_eq!(new_rc, expected);
            }
        })
    }

    #[test]
    fn install_does_not_create_bash_rcs() {
        setup(&|config| {
            let rcs: Vec<PathBuf> = [".bashrc", ".bash_profile", ".bash_login"]
                .iter()
                .map(|rc| config.homedir.join(rc))
                .collect();
            let rcs_before = rcs.iter().map(|rc| rc.exists());
            expect_ok(config, &INIT_NONE);

            for (before, after) in rcs_before.zip(rcs.iter().map(|rc| rc.exists())) {
                assert!(before == false);
                assert_eq!(before, after);
            }
        });
    }

    #[test]
    fn install_errors_when_rc_cannot_be_updated() {
        setup(&|config| {
            let rc = config.homedir.join(".profile");
            fs::File::create(&rc).unwrap();
            let mut perms = fs::metadata(&rc).unwrap().permissions();
            perms.set_readonly(true);
            fs::set_permissions(&rc, perms).unwrap();

            expect_err(config, &INIT_NONE, "amend shell");
        });
    }

    #[test]
    fn install_with_zdotdir() {
        setup(&|config| {
            let zdotdir = tempfile::Builder::new()
                .prefix("zdotdir")
                .tempdir()
                .unwrap();
            let rc = zdotdir.path().join(".zshenv");
            raw::write_file(&rc, FAKE_RC).unwrap();

            let mut cmd = clitools::cmd(config, "rustup-init", &INIT_NONE[1..]);
            cmd.env("SHELL", "zsh");
            cmd.env("ZDOTDIR", zdotdir.path());
            assert!(cmd.output().unwrap().status.success());

            let new_rc = fs::read_to_string(&rc).unwrap();
            let expected = FAKE_RC.to_owned() + &source(config.cargodir.display(), POSIX_SH);
            assert_eq!(new_rc, expected);
        });
    }

    #[test]
    fn install_adds_path_to_rc_just_once() {
        setup(&|config| {
            let profile = config.homedir.join(".profile");
            raw::write_file(&profile, FAKE_RC).unwrap();
            expect_ok(config, &INIT_NONE);
            expect_ok(config, &INIT_NONE);

            let new_profile = fs::read_to_string(&profile).unwrap();
            let expected = FAKE_RC.to_owned() + &source(config.cargodir.display(), POSIX_SH);
            assert_eq!(new_profile, expected);
        });
    }

    #[test]
    fn uninstall_removes_source_from_rcs() {
        setup(&|config| {
            let rcs: Vec<PathBuf> = [
                ".bashrc",
                ".bash_profile",
                ".bash_login",
                ".profile",
                ".zshenv",
            ]
            .iter()
            .map(|rc| config.homedir.join(rc))
            .collect();

            for rc in &rcs {
                raw::write_file(&rc, FAKE_RC).unwrap();
            }

            expect_ok(config, &INIT_NONE);
            expect_ok(config, &["rustup", "self", "uninstall", "-y"]);

            for rc in &rcs {
                let new_rc = fs::read_to_string(&rc).unwrap();
                assert_eq!(new_rc, FAKE_RC);
            }
        })
    }

    #[test]
    fn install_adds_sources_while_removing_legacy_paths() {
        setup(&|config| {
            let zdotdir = tempfile::Builder::new()
                .prefix("zdotdir")
                .tempdir()
                .unwrap();
            let rcs: Vec<PathBuf> = [".bash_profile", ".profile"]
                .iter()
                .map(|rc| config.homedir.join(rc))
                .collect();
            let zprofiles = vec![
                config.homedir.join(".zprofile"),
                zdotdir.path().join(".zprofile"),
            ];
            let old_rc = FAKE_RC.to_owned() + DEFAULT_EXPORT;
            for rc in rcs.iter().chain(zprofiles.iter()) {
                raw::write_file(&rc, &old_rc).unwrap();
            }

            let mut cmd = clitools::cmd(config, "rustup-init", &INIT_NONE[1..]);
            cmd.env("SHELL", "zsh");
            cmd.env("ZDOTDIR", zdotdir.path());
            cmd.env_remove("CARGO_HOME");
            assert!(cmd.output().unwrap().status.success());
            let fixed_rc = FAKE_RC.to_owned() + &source("$HOME/.cargo", POSIX_SH);
            for rc in &rcs {
                let new_rc = fs::read_to_string(&rc).unwrap();
                assert_eq!(new_rc, fixed_rc);
            }
            for rc in &zprofiles {
                let new_rc = fs::read_to_string(&rc).unwrap();
                assert_eq!(new_rc, FAKE_RC);
            }
        })
    }

    #[test]
    fn uninstall_cleans_up_legacy_paths() {
        setup(&|config| {
            // Install first, then overwrite.
            expect_ok(config, &INIT_NONE);

            let zdotdir = tempfile::Builder::new()
                .prefix("zdotdir")
                .tempdir()
                .unwrap();
            let mut cmd = clitools::cmd(config, "rustup-init", &INIT_NONE[1..]);
            cmd.env("SHELL", "zsh");
            cmd.env("ZDOTDIR", zdotdir.path());
            cmd.env_remove("CARGO_HOME");
            assert!(cmd.output().unwrap().status.success());
            let mut rcs: Vec<PathBuf> = [".bash_profile", ".profile", ".zprofile"]
                .iter()
                .map(|rc| config.homedir.join(rc))
                .collect();
            rcs.push(zdotdir.path().join(".zprofile"));
            let old_rc = FAKE_RC.to_owned() + DEFAULT_EXPORT;
            for rc in &rcs {
                raw::write_file(&rc, &old_rc).unwrap();
            }

            let mut cmd = clitools::cmd(config, "rustup", &["self", "uninstall", "-y"]);
            cmd.env("SHELL", "zsh");
            cmd.env("ZDOTDIR", zdotdir.path());
            cmd.env_remove("CARGO_HOME");
            assert!(cmd.output().unwrap().status.success());

            for rc in &rcs {
                let new_rc = fs::read_to_string(&rc).unwrap();
                // It's not ideal, but it's OK, if we leave whitespace.
                assert_eq!(new_rc, FAKE_RC);
            }
        })
    }

    // In the default case we want to write $HOME/.cargo/bin as the path,
    // not the full path.
    #[test]
    fn when_cargo_home_is_the_default_write_path_specially() {
        setup(&|config| {
            // Override the test harness so that cargo home looks like
            // $HOME/.cargo by removing CARGO_HOME from the environment,
            // otherwise the literal path will be written to the file.

            let profile = config.homedir.join(".profile");
            raw::write_file(&profile, FAKE_RC).unwrap();
            let mut cmd = clitools::cmd(config, "rustup-init", &INIT_NONE[1..]);
            cmd.env_remove("CARGO_HOME");
            assert!(cmd.output().unwrap().status.success());

            let new_profile = fs::read_to_string(&profile).unwrap();
            let expected = format!("{}source \"$HOME/.cargo/env.sh\"\n", FAKE_RC);
            assert_eq!(new_profile, expected);

            let mut cmd = clitools::cmd(config, "rustup", &["self", "uninstall", "-y"]);
            cmd.env_remove("CARGO_HOME");
            assert!(cmd.output().unwrap().status.success());

            let new_profile = fs::read_to_string(&profile).unwrap();
            assert_eq!(new_profile, FAKE_RC);
        });
    }

    #[test]
    fn install_doesnt_modify_path_if_passed_no_modify_path() {
        clitools::setup(Scenario::SimpleV2, &|config| {
            let profile = config.homedir.join(".profile");
            expect_ok(config, &["rustup-init", "-y", "--no-modify-path"]);
            assert!(!profile.exists());
        });
    }
}

#[cfg(windows)]
mod windows {
    use std::sync::Mutex;

    use lazy_static::lazy_static;

    use super::*;
    use crate::mock::{get_path, restore_path};

    fn setup(f: &dyn Fn(&Config)) {
        clitools::setup(Scenario::Empty, &|config| {
            // Lock protects environment variables
            lazy_static! {
                static ref LOCK: Mutex<()> = Mutex::new(());
            }
            let _g = LOCK.lock();

            // On windows these tests mess with the user's PATH. Save
            // and restore them here to keep from trashing things.
            let saved_path = get_path();
            let _g = scopeguard::guard(saved_path, restore_path);

            f(config);
        });
    }

    #[test]
    #[cfg(windows)]
    /// Smoke test for end-to-end code connectivity of the installer path mgmt on windows.
    fn install_uninstall_affect_path() {
        setup(&|config| {
            let path = config.cargodir.join("bin").to_string_lossy().to_string();

            expect_ok(config, &INIT_NONE);
            assert!(
                get_path().unwrap().contains(&path),
                format!("`{}` not in `{}`", get_path().unwrap(), &path)
            );

            expect_ok(config, &["rustup", "self", "uninstall", "-y"]);
            assert!(!get_path().unwrap().contains(&path));
        });
    }
}
<commit_msg>Remove unpacking from one last test<commit_after>//! This file contains tests relevant to Rustup's handling of updating PATHs.
//! It depends on self-update working, so if absolutely everything here breaks,
//! check those tests as well.
pub mod mock;

use std::path::PathBuf;

use crate::mock::clitools::{self, expect_ok, Config, Scenario};

// Prefer omitting actually unpacking content while just testing paths.
const INIT_NONE: [&str; 4] = ["rustup-init", "-y", "--default-toolchain", "none"];

#[cfg(unix)]
mod unix {
    use std::fmt::Display;
    use std::fs;

    use rustup::utils::raw;

    use super::*;
    use crate::mock::clitools::expect_err;

    // Let's write a fake .rc which looks vaguely like a real script.
    const FAKE_RC: &str = r#"
# Sources fruity punch.
source ~/fruit/punch

# Adds apples to PATH.
export PATH="$HOME/apple/bin"
"#;
    const DEFAULT_EXPORT: &str = "export PATH=\"$HOME/.cargo/bin:$PATH\"\n";
    const POSIX_SH: &str = "env.sh";

    fn source(dir: impl Display, sh: impl Display) -> String {
        format!("source \"{dir}/{sh}\"\n", dir = dir, sh = sh)
    }

    fn setup(f: &dyn Fn(&Config)) {
        clitools::setup(Scenario::Empty, &|config| {
            f(config);
        });
    }

    #[test]
    fn install_creates_necessary_scripts() {
        setup(&|config| {
            // Override the test harness so that cargo home looks like
            // $HOME/.cargo by removing CARGO_HOME from the environment,
            // otherwise the literal path will be written to the file.

            let mut cmd = clitools::cmd(config, "rustup-init", &INIT_NONE[1..]);
            let files: Vec<PathBuf> = [".cargo/env.sh", ".profile", ".zshenv"]
                .iter()
                .map(|file| config.homedir.join(file))
                .collect();
            for file in &files {
                assert!(!file.exists());
            }
            cmd.env_remove("CARGO_HOME");
            cmd.env("SHELL", "zsh");
            assert!(cmd.output().unwrap().status.success());
            let mut rcs = files.iter();
            let env = rcs.next().unwrap();
            let envfile = fs::read_to_string(&env).unwrap();
            let (_, envfile_export) = envfile.split_at(match envfile.find("export PATH") {
                Some(idx) => idx,
                None => 0,
            });
            assert_eq!(&envfile_export[..DEFAULT_EXPORT.len()], DEFAULT_EXPORT);

            for rc in rcs {
                let expected = source("$HOME/.cargo", POSIX_SH);
                let new_profile = fs::read_to_string(&rc).unwrap();
                assert_eq!(new_profile, expected);
            }
        });
    }

    #[test]
    fn install_updates_bash_rcs() {
        setup(&|config| {
            let rcs: Vec<PathBuf> = [".bashrc", ".bash_profile", ".bash_login", ".profile"]
                .iter()
                .map(|rc| config.homedir.join(rc))
                .collect();
            for rc in &rcs {
                raw::write_file(&rc, FAKE_RC).unwrap();
            }

            expect_ok(config, &INIT_NONE);

            let expected = FAKE_RC.to_owned() + &source(config.cargodir.display(), POSIX_SH);
            for rc in &rcs {
                let new_rc = fs::read_to_string(&rc).unwrap();
                assert_eq!(new_rc, expected);
            }
        })
    }

    #[test]
    fn install_does_not_create_bash_rcs() {
        setup(&|config| {
            let rcs: Vec<PathBuf> = [".bashrc", ".bash_profile", ".bash_login"]
                .iter()
                .map(|rc| config.homedir.join(rc))
                .collect();
            let rcs_before = rcs.iter().map(|rc| rc.exists());
            expect_ok(config, &INIT_NONE);

            for (before, after) in rcs_before.zip(rcs.iter().map(|rc| rc.exists())) {
                assert!(before == false);
                assert_eq!(before, after);
            }
        });
    }

    #[test]
    fn install_errors_when_rc_cannot_be_updated() {
        setup(&|config| {
            let rc = config.homedir.join(".profile");
            fs::File::create(&rc).unwrap();
            let mut perms = fs::metadata(&rc).unwrap().permissions();
            perms.set_readonly(true);
            fs::set_permissions(&rc, perms).unwrap();

            expect_err(config, &INIT_NONE, "amend shell");
        });
    }

    #[test]
    fn install_with_zdotdir() {
        setup(&|config| {
            let zdotdir = tempfile::Builder::new()
                .prefix("zdotdir")
                .tempdir()
                .unwrap();
            let rc = zdotdir.path().join(".zshenv");
            raw::write_file(&rc, FAKE_RC).unwrap();

            let mut cmd = clitools::cmd(config, "rustup-init", &INIT_NONE[1..]);
            cmd.env("SHELL", "zsh");
            cmd.env("ZDOTDIR", zdotdir.path());
            assert!(cmd.output().unwrap().status.success());

            let new_rc = fs::read_to_string(&rc).unwrap();
            let expected = FAKE_RC.to_owned() + &source(config.cargodir.display(), POSIX_SH);
            assert_eq!(new_rc, expected);
        });
    }

    #[test]
    fn install_adds_path_to_rc_just_once() {
        setup(&|config| {
            let profile = config.homedir.join(".profile");
            raw::write_file(&profile, FAKE_RC).unwrap();
            expect_ok(config, &INIT_NONE);
            expect_ok(config, &INIT_NONE);

            let new_profile = fs::read_to_string(&profile).unwrap();
            let expected = FAKE_RC.to_owned() + &source(config.cargodir.display(), POSIX_SH);
            assert_eq!(new_profile, expected);
        });
    }

    #[test]
    fn uninstall_removes_source_from_rcs() {
        setup(&|config| {
            let rcs: Vec<PathBuf> = [
                ".bashrc",
                ".bash_profile",
                ".bash_login",
                ".profile",
                ".zshenv",
            ]
            .iter()
            .map(|rc| config.homedir.join(rc))
            .collect();

            for rc in &rcs {
                raw::write_file(&rc, FAKE_RC).unwrap();
            }

            expect_ok(config, &INIT_NONE);
            expect_ok(config, &["rustup", "self", "uninstall", "-y"]);

            for rc in &rcs {
                let new_rc = fs::read_to_string(&rc).unwrap();
                assert_eq!(new_rc, FAKE_RC);
            }
        })
    }

    #[test]
    fn install_adds_sources_while_removing_legacy_paths() {
        setup(&|config| {
            let zdotdir = tempfile::Builder::new()
                .prefix("zdotdir")
                .tempdir()
                .unwrap();
            let rcs: Vec<PathBuf> = [".bash_profile", ".profile"]
                .iter()
                .map(|rc| config.homedir.join(rc))
                .collect();
            let zprofiles = vec![
                config.homedir.join(".zprofile"),
                zdotdir.path().join(".zprofile"),
            ];
            let old_rc = FAKE_RC.to_owned() + DEFAULT_EXPORT;
            for rc in rcs.iter().chain(zprofiles.iter()) {
                raw::write_file(&rc, &old_rc).unwrap();
            }

            let mut cmd = clitools::cmd(config, "rustup-init", &INIT_NONE[1..]);
            cmd.env("SHELL", "zsh");
            cmd.env("ZDOTDIR", zdotdir.path());
            cmd.env_remove("CARGO_HOME");
            assert!(cmd.output().unwrap().status.success());
            let fixed_rc = FAKE_RC.to_owned() + &source("$HOME/.cargo", POSIX_SH);
            for rc in &rcs {
                let new_rc = fs::read_to_string(&rc).unwrap();
                assert_eq!(new_rc, fixed_rc);
            }
            for rc in &zprofiles {
                let new_rc = fs::read_to_string(&rc).unwrap();
                assert_eq!(new_rc, FAKE_RC);
            }
        })
    }

    #[test]
    fn uninstall_cleans_up_legacy_paths() {
        setup(&|config| {
            // Install first, then overwrite.
            expect_ok(config, &INIT_NONE);

            let zdotdir = tempfile::Builder::new()
                .prefix("zdotdir")
                .tempdir()
                .unwrap();
            let mut cmd = clitools::cmd(config, "rustup-init", &INIT_NONE[1..]);
            cmd.env("SHELL", "zsh");
            cmd.env("ZDOTDIR", zdotdir.path());
            cmd.env_remove("CARGO_HOME");
            assert!(cmd.output().unwrap().status.success());
            let mut rcs: Vec<PathBuf> = [".bash_profile", ".profile", ".zprofile"]
                .iter()
                .map(|rc| config.homedir.join(rc))
                .collect();
            rcs.push(zdotdir.path().join(".zprofile"));
            let old_rc = FAKE_RC.to_owned() + DEFAULT_EXPORT;
            for rc in &rcs {
                raw::write_file(&rc, &old_rc).unwrap();
            }

            let mut cmd = clitools::cmd(config, "rustup", &["self", "uninstall", "-y"]);
            cmd.env("SHELL", "zsh");
            cmd.env("ZDOTDIR", zdotdir.path());
            cmd.env_remove("CARGO_HOME");
            assert!(cmd.output().unwrap().status.success());

            for rc in &rcs {
                let new_rc = fs::read_to_string(&rc).unwrap();
                // It's not ideal, but it's OK, if we leave whitespace.
                assert_eq!(new_rc, FAKE_RC);
            }
        })
    }

    // In the default case we want to write $HOME/.cargo/bin as the path,
    // not the full path.
    #[test]
    fn when_cargo_home_is_the_default_write_path_specially() {
        setup(&|config| {
            // Override the test harness so that cargo home looks like
            // $HOME/.cargo by removing CARGO_HOME from the environment,
            // otherwise the literal path will be written to the file.

            let profile = config.homedir.join(".profile");
            raw::write_file(&profile, FAKE_RC).unwrap();
            let mut cmd = clitools::cmd(config, "rustup-init", &INIT_NONE[1..]);
            cmd.env_remove("CARGO_HOME");
            assert!(cmd.output().unwrap().status.success());

            let new_profile = fs::read_to_string(&profile).unwrap();
            let expected = format!("{}source \"$HOME/.cargo/env.sh\"\n", FAKE_RC);
            assert_eq!(new_profile, expected);

            let mut cmd = clitools::cmd(config, "rustup", &["self", "uninstall", "-y"]);
            cmd.env_remove("CARGO_HOME");
            assert!(cmd.output().unwrap().status.success());

            let new_profile = fs::read_to_string(&profile).unwrap();
            assert_eq!(new_profile, FAKE_RC);
        });
    }

    #[test]
    fn install_doesnt_modify_path_if_passed_no_modify_path() {
        setup(&|config| {
            let profile = config.homedir.join(".profile");
            expect_ok(
                config,
                &[
                    "rustup-init",
                    "-y",
                    "--no-modify-path",
                    "--default-toolchain",
                    "none",
                ],
            );
            assert!(!profile.exists());
        });
    }
}

#[cfg(windows)]
mod windows {
    use std::sync::Mutex;

    use lazy_static::lazy_static;

    use super::*;
    use crate::mock::{get_path, restore_path};

    fn setup(f: &dyn Fn(&Config)) {
        clitools::setup(Scenario::Empty, &|config| {
            // Lock protects environment variables
            lazy_static! {
                static ref LOCK: Mutex<()> = Mutex::new(());
            }
            let _g = LOCK.lock();

            // On windows these tests mess with the user's PATH. Save
            // and restore them here to keep from trashing things.
            let saved_path = get_path();
            let _g = scopeguard::guard(saved_path, restore_path);

            f(config);
        });
    }

    #[test]
    #[cfg(windows)]
    /// Smoke test for end-to-end code connectivity of the installer path mgmt on windows.
    fn install_uninstall_affect_path() {
        setup(&|config| {
            let path = config.cargodir.join("bin").to_string_lossy().to_string();

            expect_ok(config, &INIT_NONE);
            assert!(
                get_path().unwrap().contains(&path),
                format!("`{}` not in `{}`", get_path().unwrap(), &path)
            );

            expect_ok(config, &["rustup", "self", "uninstall", "-y"]);
            assert!(!get_path().unwrap().contains(&path));
        });
    }
}
<|endoftext|>


<commit_before>extern crate libloading;
use libloading::{Symbol, Library};

const LIBPATH: &'static str = concat!(env!("OUT_DIR"), "/libtest_helpers.dll");

#[test]
fn test_id_u32() {
    let lib = Library::new(LIBPATH).unwrap();
    unsafe {
        let f: Symbol<unsafe extern fn(u32) -> u32> = lib.get(b"test_identity_u32\0").unwrap();
        assert_eq!(42, f(42));
    }
}

#[repr(C)]
#[derive(Clone,Copy,PartialEq,Debug)]
struct S {
    a: u64,
    b: u32,
    c: u16,
    d: u8
}

#[test]
fn test_id_struct() {
    let lib = Library::new(LIBPATH).unwrap();
    unsafe {
        let f: Symbol<unsafe extern fn(S) -> S> = lib.get(b"test_identity_struct\0").unwrap();
        assert_eq!(S { a: 1, b: 2, c: 3, d: 4 }, f(S { a: 1, b: 2, c: 3, d: 4 }));
    }
}

#[test]
fn test_0_no_0() {
    let lib = Library::new(LIBPATH).unwrap();
    unsafe {
        let f: Symbol<unsafe extern fn(S) -> S> = lib.get(b"test_identity_struct\0").unwrap();
        let f2: Symbol<unsafe extern fn(S) -> S> = lib.get(b"test_identity_struct").unwrap();
        assert_eq!(*f, *f2);
    }
}

#[test]
fn wrong_name_fails() {
    Library::new(concat!(env!("OUT_DIR"), "/libtest_help")).err().unwrap();
}

#[test]
fn missing_symbol_fails() {
    let lib = Library::new(LIBPATH).unwrap();
    unsafe {
        lib.get::<*mut ()>(b"test_does_not_exist").err().unwrap();
        lib.get::<*mut ()>(b"test_does_not_exist\0").err().unwrap();
    }
}

#[test]
fn interior_null_fails() {
    let lib = Library::new(LIBPATH).unwrap();
    unsafe {
        lib.get::<*mut ()>(b"test_does\0_not_exist").err().unwrap();
        lib.get::<*mut ()>(b"test\0_does_not_exist\0").err().unwrap();
    }
}

#[test]
#[should_panic]
fn test_incompatible_type() {
    let lib = Library::new(LIBPATH).unwrap();
    unsafe {
        let _ = lib.get::<()>(b"test_identity_u32\0");
    }
}
<commit_msg>Add test for named function types<commit_after>extern crate libloading;
use libloading::{Symbol, Library};

const LIBPATH: &'static str = concat!(env!("OUT_DIR"), "/libtest_helpers.dll");

#[test]
fn test_id_u32() {
    let lib = Library::new(LIBPATH).unwrap();
    unsafe {
        let f: Symbol<unsafe extern fn(u32) -> u32> = lib.get(b"test_identity_u32\0").unwrap();
        assert_eq!(42, f(42));
    }
}

#[repr(C)]
#[derive(Clone,Copy,PartialEq,Debug)]
struct S {
    a: u64,
    b: u32,
    c: u16,
    d: u8
}

#[test]
fn test_id_struct() {
    let lib = Library::new(LIBPATH).unwrap();
    unsafe {
        let f: Symbol<unsafe extern fn(S) -> S> = lib.get(b"test_identity_struct\0").unwrap();
        assert_eq!(S { a: 1, b: 2, c: 3, d: 4 }, f(S { a: 1, b: 2, c: 3, d: 4 }));
    }
}

#[test]
fn test_0_no_0() {
    let lib = Library::new(LIBPATH).unwrap();
    unsafe {
        let f: Symbol<unsafe extern fn(S) -> S> = lib.get(b"test_identity_struct\0").unwrap();
        let f2: Symbol<unsafe extern fn(S) -> S> = lib.get(b"test_identity_struct").unwrap();
        assert_eq!(*f, *f2);
    }
}

#[test]
fn wrong_name_fails() {
    Library::new(concat!(env!("OUT_DIR"), "/libtest_help")).err().unwrap();
}

#[test]
fn missing_symbol_fails() {
    let lib = Library::new(LIBPATH).unwrap();
    unsafe {
        lib.get::<*mut ()>(b"test_does_not_exist").err().unwrap();
        lib.get::<*mut ()>(b"test_does_not_exist\0").err().unwrap();
    }
}

#[test]
fn interior_null_fails() {
    let lib = Library::new(LIBPATH).unwrap();
    unsafe {
        lib.get::<*mut ()>(b"test_does\0_not_exist").err().unwrap();
        lib.get::<*mut ()>(b"test\0_does_not_exist\0").err().unwrap();
    }
}

#[test]
#[should_panic]
fn test_incompatible_type() {
    let lib = Library::new(LIBPATH).unwrap();
    unsafe {
        let _ = lib.get::<()>(b"test_identity_u32\0");
    }
}

#[test]
#[should_panic]
fn test_incompatible_type_named_fn() {
    unsafe fn get<'a, T>(l: &'a Library, _: T) -> libloading::Result<Symbol<'a, T>> {
        l.get::<T>(b"test_identity_u32\0")
    }
    let lib = Library::new(LIBPATH).unwrap();
    unsafe {
        let _ = get(&lib, test_incompatible_type_named_fn);
    }
}
<|endoftext|>


<commit_before>//! This test file ensures that all of the lifetimes work the way we
//! want, and that there are no regressions. It's a "does this compile?"
//! test.

extern crate ropey;

use ropey::{Rope, RopeSlice};

const TEXT: &str = include_str!("test_text.txt");

fn main() {
    let rope = Rope::from_str(TEXT);

    let (a, b, c, d, e, f, count, line, string) = {
        // The lifetimes of the intermediate slices shouldn't matter,
        // and all of this should compile.  The lifetimes of the things
        // produced by these calls should be tied to the lifetime of the
        // original rope, not the lifetime of the slice they were created
        // from.
        let a = rope.slice(4..500).slice(4..400).slice(4..300);
        let b = rope.slice(4..500).slice(4..400).as_str();
        let c = rope.slice(4..500).slice(4..400).line(1);
        let d = rope.slice(4..500).slice(4..400).chunk_at_byte(50);
        let e = rope.slice(4..500).slice(4..400).chunk_at_char(50);
        let f = rope.slice(4..500).slice(4..400).chunk_at_line_break(3);

        // Same for iterators.  In addition, the items _yielded_ by the
        // iterators should also be tied to the lifetime of the original
        // rope, not to the iterator or slice they came from.

        let mut count = 0;
        for _ in rope.slice(4..500).slice(4..400).bytes() {
            count += 1;
        }
        for _ in rope.slice(4..500).slice(4..400).chars() {
            count += 1;
        }

        let mut line = RopeSlice::from_str("");
        for l in rope.slice(4..500).slice(4..400).lines() {
            line = l;
        }
        line = line.slice(..).slice(..);

        let mut string = "";
        for c in rope.slice(4..500).slice(4..400).chunks() {
            string = c;
        }

        (a, b, c, d, e, f, count, line, string)
    };

    println!(
        "{} {:?} {} {:?} {:?} {:?} {} {} {}",
        a, b, c, d, e, f, count, line, string
    );
}
<commit_msg>Improve the comments in the lifetime test file.<commit_after>//! This test file ensures that all of the lifetimes work the way we
//! want, and that there are no regressions. It's a "does this compile?"
//! test.

extern crate ropey;

use ropey::{Rope, RopeSlice};

const TEXT: &str = include_str!("test_text.txt");

fn main() {
    let rope = Rope::from_str(TEXT);

    let (a, b, c, d, e, f, count, line, string) = {
        // The lifetimes of intermediate slices shouldn't matter.  The
        // lifetimes of the things produced by the calls below should be
        // tied to the lifetime of the original rope, not the lifetimes of
        // the slices they were created from.  Therefore, this should all
        // compile.

        let a = rope.slice(4..500).slice(4..400).slice(4..300);
        let b = rope.slice(4..500).slice(4..400).as_str();
        let c = rope.slice(4..500).slice(4..400).line(1);
        let d = rope.slice(4..500).slice(4..400).chunk_at_byte(50);
        let e = rope.slice(4..500).slice(4..400).chunk_at_char(50);
        let f = rope.slice(4..500).slice(4..400).chunk_at_line_break(3);

        // Same for iterators.  In addition, the items _yielded_ by the
        // iterators should also be tied to the lifetime of the original
        // rope, not to the iterators or slices they came from.

        let mut count = 0;
        for _ in rope.slice(4..500).slice(4..400).bytes() {
            count += 1;
        }
        for _ in rope.slice(4..500).slice(4..400).chars() {
            count += 1;
        }

        let mut line = RopeSlice::from_str("");
        for l in rope.slice(4..500).slice(4..400).lines() {
            line = l;
        }
        line = line.slice(..).slice(..);

        let mut string = "";
        for c in rope.slice(4..500).slice(4..400).chunks() {
            string = c;
        }

        (a, b, c, d, e, f, count, line, string)
    };

    println!(
        "{} {:?} {} {:?} {:?} {:?} {} {} {}",
        a, b, c, d, e, f, count, line, string
    );
}
<|endoftext|>


<commit_before>use std::io;
use std::str::{from_utf8, Utf8Error};

use netbuf::Buf;
use futures::{Future, Async, Poll};
use futures::Async::{Ready, NotReady};
use futures::stream::{Stream};
use tokio_core::io::Io;
use tk_bufstream::IoBuf;
use byteorder::{BigEndian, ByteOrder};

use super::{Dispatcher, ImmediateReplier, OutFrame};
use self::Frame::*;

const MAX_MESSAGE_SIZE: u64 = 128 << 10;

quick_error! {
    #[derive(Debug)]
    pub enum Error {
        Io(err: io::Error) {
            description("IO error")
            display("IO error: {}", err)
            from()
        }
        InvalidUtf8(err: Utf8Error) {
            description("Error decoding text frame")
            display("Error decoding text frame: {}", err)
            from()
        }
        InvalidOpcode(code: u8) {
            description("Opcode of the frame is invalid")
            display("Opcode of the frame is invalid: {}", code)
            from()
        }
        Unmasked {
            description("Received unmasked frame")
        }
        Fragmented {
            description("Received fragmented frame")
        }
        TooLong {
            description("Received frame that is too long")
        }
    }
}

pub struct WebsockProto<S: Io, D: Dispatcher, R> {
    dispatcher: D,
    io: IoBuf<S>,
    recv: R,
}

pub enum Frame<'a> {
    Ping(&'a [u8]),
    Pong(&'a [u8]),
    Text(&'a str),
    Binary(&'a [u8]),
}

fn parse_frame<'x>(buf: &'x mut Buf) -> Poll<(Frame<'x>, usize), Error> {
    if buf.len() < 2 {
        return Ok(NotReady);
    }
    let (size, fsize) = {
        match buf[1] & 0x7F {
            126 => {
                if buf.len() < 4 {
                    return Ok(NotReady);
                }
                (BigEndian::read_u16(&buf[2..4]) as u64, 4)
            }
            127 => {
                if buf.len() < 10 {
                    return Ok(NotReady);
                }
                (BigEndian::read_u64(&buf[2..10]), 10)
            }
            size => (size as u64, 2),
        }
    };
    if size > MAX_MESSAGE_SIZE {
        return Err(Error::TooLong);
    }
    let size = size as usize;
    let start = fsize + 4 /* mask size */;
    if buf.len() < start + size {
        return Ok(NotReady);
    }

    let fin = buf[0] & 0x80 != 0;
    let opcode = buf[0] & 0x0F;
    // TODO(tailhook) should we assert that reserved bits are zero?
    let mask = buf[1] & 0x80 != 0;
    if !fin {
        return Err(Error::Fragmented);
    }
    if !mask {
        return Err(Error::Unmasked);
    }
    let mask = [buf[start-4], buf[start-3], buf[start-2], buf[start-1]];
    for idx in 0..size { // hopefully llvm is smart enough to optimize it
        buf[start + idx] ^= mask[idx % 4];
    }
    let data = &buf[start..(start + size)];
    let frame = match opcode {
        0x9 => Ping(data),
        0xA => Pong(data),
        0x1 => Text(from_utf8(data)?),
        0x2 => Binary(data),
        // TODO(tailhook) implement shutdown packets
        x => return Err(Error::InvalidOpcode(x)),
    };
    return Ok(Ready((frame, start + size)));
}

impl<D, S: Io, R> Future for WebsockProto<S, D, R>
    where D: Dispatcher,
          R: Stream<Item=OutFrame, Error=io::Error>,
{
    type Item = ();
    type Error = Error;
    fn poll(&mut self) -> Poll<(), Error> {
        loop {
            self.poll_recv()?;
            self.io.flush()?;
            let packet_len = if let Ready((frame, bytes)) =
                parse_frame(&mut self.io.in_buf)?
            {
                self.dispatcher.dispatch(frame,
                    &mut ImmediateReplier::new(&mut self.io.out_buf))?;
                Some(bytes)
            } else {
                None
            };
            if let Some(packet_len) = packet_len {
                self.io.in_buf.consume(packet_len);
            } else {
                let nbytes = self.io.read()?;
                if nbytes == 0 {
                    if self.io.done() {
                        return Ok(Async::Ready(()));
                    } else {
                        return Ok(Async::NotReady);
                    }
                } else {
                    continue;
                }
            };
        }
    }
}

impl<S: Io, D, R> WebsockProto<S, D, R>
    where D: Dispatcher,
          R: Stream<Item=OutFrame, Error=io::Error>,
{
    pub fn new(sock: IoBuf<S>, dispatcher: D, remote: R)
        -> WebsockProto<S, D, R>
    {
        WebsockProto {
            io: sock,
            dispatcher: dispatcher,
            recv: remote,
        }
    }

    fn poll_recv(&mut self) -> Result<(), Error> {
        if let Ready(Some(frame)) = self.recv.poll()? {
            let mut imm = ImmediateReplier::new(&mut self.io.out_buf);
            match frame {
                OutFrame::Text(val) => {
                    imm.text(&val);
                }
                OutFrame::Binary(val) => {
                    imm.binary(&val);
                }
            }
        }
        Ok(())
    }
}
<commit_msg>fix WebsockProto.poll_recv() (read all available remote messages)<commit_after>use std::io;
use std::str::{from_utf8, Utf8Error};

use netbuf::Buf;
use futures::{Future, Async, Poll};
use futures::Async::{Ready, NotReady};
use futures::stream::{Stream};
use tokio_core::io::Io;
use tk_bufstream::IoBuf;
use byteorder::{BigEndian, ByteOrder};

use super::{Dispatcher, ImmediateReplier, OutFrame};
use self::Frame::*;

const MAX_MESSAGE_SIZE: u64 = 128 << 10;

quick_error! {
    #[derive(Debug)]
    pub enum Error {
        Io(err: io::Error) {
            description("IO error")
            display("IO error: {}", err)
            from()
        }
        InvalidUtf8(err: Utf8Error) {
            description("Error decoding text frame")
            display("Error decoding text frame: {}", err)
            from()
        }
        InvalidOpcode(code: u8) {
            description("Opcode of the frame is invalid")
            display("Opcode of the frame is invalid: {}", code)
            from()
        }
        Unmasked {
            description("Received unmasked frame")
        }
        Fragmented {
            description("Received fragmented frame")
        }
        TooLong {
            description("Received frame that is too long")
        }
    }
}

pub struct WebsockProto<S: Io, D: Dispatcher, R> {
    dispatcher: D,
    io: IoBuf<S>,
    recv: R,
}

pub enum Frame<'a> {
    Ping(&'a [u8]),
    Pong(&'a [u8]),
    Text(&'a str),
    Binary(&'a [u8]),
}

fn parse_frame<'x>(buf: &'x mut Buf) -> Poll<(Frame<'x>, usize), Error> {
    if buf.len() < 2 {
        return Ok(NotReady);
    }
    let (size, fsize) = {
        match buf[1] & 0x7F {
            126 => {
                if buf.len() < 4 {
                    return Ok(NotReady);
                }
                (BigEndian::read_u16(&buf[2..4]) as u64, 4)
            }
            127 => {
                if buf.len() < 10 {
                    return Ok(NotReady);
                }
                (BigEndian::read_u64(&buf[2..10]), 10)
            }
            size => (size as u64, 2),
        }
    };
    if size > MAX_MESSAGE_SIZE {
        return Err(Error::TooLong);
    }
    let size = size as usize;
    let start = fsize + 4 /* mask size */;
    if buf.len() < start + size {
        return Ok(NotReady);
    }

    let fin = buf[0] & 0x80 != 0;
    let opcode = buf[0] & 0x0F;
    // TODO(tailhook) should we assert that reserved bits are zero?
    let mask = buf[1] & 0x80 != 0;
    if !fin {
        return Err(Error::Fragmented);
    }
    if !mask {
        return Err(Error::Unmasked);
    }
    let mask = [buf[start-4], buf[start-3], buf[start-2], buf[start-1]];
    for idx in 0..size { // hopefully llvm is smart enough to optimize it
        buf[start + idx] ^= mask[idx % 4];
    }
    let data = &buf[start..(start + size)];
    let frame = match opcode {
        0x9 => Ping(data),
        0xA => Pong(data),
        0x1 => Text(from_utf8(data)?),
        0x2 => Binary(data),
        // TODO(tailhook) implement shutdown packets
        x => return Err(Error::InvalidOpcode(x)),
    };
    return Ok(Ready((frame, start + size)));
}

impl<D, S: Io, R> Future for WebsockProto<S, D, R>
    where D: Dispatcher,
          R: Stream<Item=OutFrame, Error=io::Error>,
{
    type Item = ();
    type Error = Error;
    fn poll(&mut self) -> Poll<(), Error> {
        loop {
            self.poll_recv()?;
            self.io.flush()?;
            let packet_len = if let Ready((frame, bytes)) =
                parse_frame(&mut self.io.in_buf)?
            {
                self.dispatcher.dispatch(frame,
                    &mut ImmediateReplier::new(&mut self.io.out_buf))?;
                Some(bytes)
            } else {
                None
            };
            if let Some(packet_len) = packet_len {
                self.io.in_buf.consume(packet_len);
            } else {
                let nbytes = self.io.read()?;
                if nbytes == 0 {
                    if self.io.done() {
                        return Ok(Async::Ready(()));
                    } else {
                        return Ok(Async::NotReady);
                    }
                } else {
                    continue;
                }
            };
        }
    }
}

impl<S: Io, D, R> WebsockProto<S, D, R>
    where D: Dispatcher,
          R: Stream<Item=OutFrame, Error=io::Error>,
{
    pub fn new(sock: IoBuf<S>, dispatcher: D, remote: R)
        -> WebsockProto<S, D, R>
    {
        WebsockProto {
            io: sock,
            dispatcher: dispatcher,
            recv: remote,
        }
    }

    fn poll_recv(&mut self) -> Result<(), Error> {
        while let Ready(Some(frame)) = self.recv.poll()? {
            let mut imm = ImmediateReplier::new(&mut self.io.out_buf);
            match frame {
                OutFrame::Text(val) => {
                    imm.text(&val);
                }
                OutFrame::Binary(val) => {
                    imm.binary(&val);
                }
            }
        }
        Ok(())
    }
}
<|endoftext|>


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

#[derive(PartialEq)]
/// World region for which the commercetools platform has data-centers
pub enum Region {
    Europe,
    NorthAmerica,
}

impl FromStr for Region {
    type Err = String;
    fn from_str(s: &str) -> Result<Region, String> {
        match s.to_string().to_lowercase().as_ref() {
            "europe" => Ok(Region::Europe),
            "northamerica" => Ok(Region::NorthAmerica),
            _ => {
                Err(format!("Region '{}' not recognized. Valid values: 'Europe', 'NorthAmerica'.",
                            s))
            }
        }
    }
}

impl Region {
    /// Returns the [api url](http://dev.commercetools.com/http-api.html#hosts) for this region
    pub fn api_url(&self) -> &str {
        match self {
            &Region::Europe => "https://api.sphere.io",
            &Region::NorthAmerica => "https://api.commercetools.co",
        }
    }

    /// Returns the [auth url](http://dev.commercetools.com/http-api-authorization.html#hosts) for this region
    pub fn auth_url(&self) -> &str {
        match self {
            &Region::Europe => "https://auth.sphere.io",
            &Region::NorthAmerica => "https://auth.commercetools.co",
        }
    }
}

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

    #[test]
    fn region_from_string() {
        assert!(Region::from_str("Europe").unwrap() == Region::Europe);
        assert!(Region::from_str("europe").unwrap() == Region::Europe);
        assert!(Region::from_str("NorthAmerica").unwrap() == Region::NorthAmerica);
        assert!(Region::from_str("northAmeriCA").unwrap() == Region::NorthAmerica);
    }

    #[test]
    fn unparsable_region_err() {
        assert!(Region::from_str("India") ==
                Result::Err("Region 'India' not recognized. Valid values: 'Europe', 'NorthAmerica'.".to_string()));
    }
}
<commit_msg>better case matching<commit_after>use std::str::FromStr;

#[derive(PartialEq, Eq)]
/// World region for which the commercetools platform has data-centers
pub enum Region {
    Europe,
    NorthAmerica,
}

impl FromStr for Region {
    type Err = String;
    fn from_str(s: &str) -> Result<Region, String> {
        match s.to_string().to_lowercase().as_ref() {
            "europe" => Ok(Region::Europe),
            "northamerica" => Ok(Region::NorthAmerica),
            _ => {
                Err(format!("Region '{}' not recognized. Valid values: 'Europe', 'NorthAmerica'.",
                            s))
            }
        }
    }
}

impl Region {
    /// Returns the [api url](http://dev.commercetools.com/http-api.html#hosts) for this region
    pub fn api_url(&self) -> &str {
        match *self {
            Region::Europe => "https://api.sphere.io",
            Region::NorthAmerica => "https://api.commercetools.co",
        }
    }

    /// Returns the [auth url](http://dev.commercetools.com/http-api-authorization.html#hosts) for this region
    pub fn auth_url(&self) -> &str {
        match *self {
            Region::Europe => "https://auth.sphere.io",
            Region::NorthAmerica => "https://auth.commercetools.co",
        }
    }
}

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

    #[test]
    fn region_from_string() {
        assert!(Region::from_str("Europe").unwrap() == Region::Europe);
        assert!(Region::from_str("europe").unwrap() == Region::Europe);
        assert!(Region::from_str("NorthAmerica").unwrap() == Region::NorthAmerica);
        assert!(Region::from_str("northAmeriCA").unwrap() == Region::NorthAmerica);
    }

    #[test]
    fn unparsable_region_err() {
        assert!(Region::from_str("India") ==
                Result::Err("Region 'India' not recognized. Valid values: 'Europe', 'NorthAmerica'.".to_string()));
    }
}
<|endoftext|>


<commit_before>use auth::User;
use chrono::NaiveDateTime;
use comment::Comment;

use schema::posts;

#[derive(Queryable, Associations, Identifiable)]
#[belongs_to(User)]
pub struct Post {
    pub id: i32,
    pub user_id: i32,
    pub title: String,
    pub body: String,
    pub created_at: NaiveDateTime,
    pub updated_at: NaiveDateTime,
    pub status: Status,
}

pub enum Status {
    Draft,
    Published { at: NaiveDateTime },
}

use diesel::pg::Pg;
use diesel::query_source::Queryable;
use diesel::sql_types::{Nullable, Timestamp};

impl Queryable<Nullable<Timestamp>, Pg> for Status {
    type Row = Option<NaiveDateTime>;

    fn build(row: Self::Row) -> Self {
        match row {
            Some(at) => Status::Published { at },
            None => Status::Draft,
        }
    }
}

pub fn render(post: &Post, user: &User, comments: &[(Comment, User)]) {
    use self::Status::*;

    println!("{} (id: {})", post.title, post.id);
    println!("By {}", user.username);
    let edited_at = post.updated_at.format("%F %T");
    match post.status {
        Draft => println!("DRAFT (last edited at {})", edited_at),
        Published { at } if at != post.updated_at => {
            let published_at = at.format("%F %T");
            println!(
                "Published at {} (last edited at {})",
                published_at, edited_at
            );
        }
        Published { at } => println!("Published at {}", at.format("%F %T")),
    }
    println!("\n{}", post.body);

    if !comments.is_empty() {
        println!("---------------\n");
        println!("{} Comments\n", comments.len());
        for &(ref comment, ref user) in comments {
            let at = comment.created_at.format("%F %T");
            print!("{} at {}", user.username, at);
            if comment.updated_at != comment.created_at {
                let edited = comment.updated_at.format("%F %T");
                print!(" (last edited {})", edited);
            }
            println!("\n{}", comment.body);
        }
    }

    println!("===============\n");
}
<commit_msg>Now with passing tests<commit_after>use auth::User;
use chrono::NaiveDateTime;
use comment::Comment;

use schema::posts;

#[derive(Queryable, Associations, Identifiable)]
#[belongs_to(User)]
pub struct Post {
    pub id: i32,
    pub user_id: i32,
    pub title: String,
    pub body: String,
    pub created_at: NaiveDateTime,
    pub updated_at: NaiveDateTime,
    pub status: Status,
}

pub enum Status {
    Draft,
    Published { at: NaiveDateTime },
}

use diesel::pg::Pg;
use diesel::deserialize::Queryable;
use diesel::sql_types::{Nullable, Timestamp};

impl Queryable<Nullable<Timestamp>, Pg> for Status {
    type Row = Option<NaiveDateTime>;

    fn build(row: Self::Row) -> Self {
        match row {
            Some(at) => Status::Published { at },
            None => Status::Draft,
        }
    }
}

pub fn render(post: &Post, user: &User, comments: &[(Comment, User)]) {
    use self::Status::*;

    println!("{} (id: {})", post.title, post.id);
    println!("By {}", user.username);
    let edited_at = post.updated_at.format("%F %T");
    match post.status {
        Draft => println!("DRAFT (last edited at {})", edited_at),
        Published { at } if at != post.updated_at => {
            let published_at = at.format("%F %T");
            println!(
                "Published at {} (last edited at {})",
                published_at, edited_at
            );
        }
        Published { at } => println!("Published at {}", at.format("%F %T")),
    }
    println!("\n{}", post.body);

    if !comments.is_empty() {
        println!("---------------\n");
        println!("{} Comments\n", comments.len());
        for &(ref comment, ref user) in comments {
            let at = comment.created_at.format("%F %T");
            print!("{} at {}", user.username, at);
            if comment.updated_at != comment.created_at {
                let edited = comment.updated_at.format("%F %T");
                print!(" (last edited {})", edited);
            }
            println!("\n{}", comment.body);
        }
    }

    println!("===============\n");
}
<|endoftext|>


<commit_before>use clap::{App, SubCommand, ArgMatches, Arg};
use uuid::Uuid;
use std::path::Path;
use std::process::Command;

use toml;
use std::io::prelude::*;
use std::fs::File;

pub fn parse_cli() {
    let matches = App::new("pide: Persistent Isolated Development Environments (in Docker)")
        .author(crate_authors!())
        .version(crate_version!())

        // "zeit init"
        .subcommand(SubCommand::with_name("init")
            .about("Initialize pide here")
            .arg(Arg::with_name("Dockerfile")
                .help("Dockerfile to initialize this environment with")
                .required(true)))

        // // "zeit pom"
        // .subcommand(SubCommand::with_name("pom")
        //     .about("Start, end, or modify a pomodoro time")

        //     // "zeit pom start"
        //     .subcommand(SubCommand::with_name("start")
        //         .help("start a new pomodoro session")))
        .get_matches();

    match matches.subcommand() {
        ("init", Some(args)) => {
            let dockerfile = args.value_of("Dockerfile").expect("Missing required argument?");
            init(dockerfile);
        }
        //     ("pom", Some(x)) => {
        //         pomodoro(x);
        //     }
        _ => println!("Hallo, welt!"),
    }
}

#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct Pidename {
    repo: String,
    id: Uuid,
}

impl Pidename {
    pub fn new() -> Self {
        Self {
            repo: "pide".to_string(),
            id: Uuid::new_v4(),
        }
    }

    pub fn to_string(&self) -> String {
        format!("{}:{}", &self.repo, self.id.to_string())
    }
}

#[derive(Debug, Deserialize, Serialize)]
pub struct Pidefile {
    image_id: String,
    original_dockerfile: String, // TODO: not a string?
    original_workingpath: String, // TODO: not a string?
    pide_version: String, // TODO: not a string?

    // Tables go last
    name: Pidename,
}

impl Pidefile {
    pub fn to_string(&self) -> Result<String, ()> {
        match toml::to_string(self) {
            Ok(out) => Ok(out),
            Err(x) => {
                println!("bad: {:?}", x);
                Err(())
            }
        }
    }
}

fn init(dockerfile: &str) {
    // println!("{}", dockerfile);

    // println!("{:?}", Pidename::new().to_string());
    let dfp = Path::new(dockerfile);
    let working_path = dfp.parent().expect("Failed to choose working path");

    assert!(dfp.is_file(), "Dockerfile doesn't exist");
    assert!(working_path.exists(), "Working path doesn't exist");

    let build_cmd = Command::new("docker")
        .arg("build")
        .arg("-f")
        .arg(dockerfile)
        .arg(working_path)
        .output()
        .expect("Failed to build base docker image!");

    let image_id = String::from_utf8_lossy(&build_cmd.stdout)
        .lines()
        .last()
        .expect("Unexpected Docker Output!")
        .split(' ')
        .last()
        .expect("Failed to decode image id")
        .to_string();

    let pide_data = Pidefile {
        name: Pidename::new(),
        image_id: image_id,
        original_dockerfile: dockerfile.to_string(),
        original_workingpath: working_path.to_str().unwrap().to_string(),
        pide_version: "0.2.0".to_string(),
    };

    println!("{:?}", pide_data);

    let open_path = ".pide";

    let mut file = File::create(open_path)
        .expect(&format!("Failed to open pide file {:}", open_path));
    let _ = file.write_all(&pide_data.to_string().expect("sadfoo").into_bytes())
        .expect("Failed to write to pide file");
}
<commit_msg>Ugly, but works :)<commit_after>use clap::{App, SubCommand, ArgMatches, Arg};
use uuid::Uuid;
use std::path::Path;
use std::process::Command;

use toml;
use std::io::prelude::*;
use std::fs::File;
use std::env::current_dir;

pub fn parse_cli() {
    let matches = App::new("pide: Persistent Isolated Development Environments (in Docker)")
        .author(crate_authors!())
        .version(crate_version!())

        // "pide init <dockerfile>"
        .subcommand(SubCommand::with_name("init")
            .about("Initialize pide here")
            .arg(Arg::with_name("Dockerfile")
                .help("Dockerfile to initialize this environment with")
                .required(true)))

        // "pide resume"
        .subcommand(SubCommand::with_name("resume")
            .about("Resume an existing pide"))

        .get_matches();

    match matches.subcommand() {
        ("init", Some(args)) => {
            let dockerfile = args.value_of("Dockerfile").expect("Missing required argument?");
            init(dockerfile);
        }
        ("resume", _) => {
            resume();
        }
        _ => println!("Hallo, welt!"),
    }
}

fn resume() {
    // Load pide file
    let pide_data = PideFile::from_file(".pide");
    assert_eq!(pide_data.pide_version, "0.2.0", "Version Mismatch!");

    let output = Command::new("docker")
        .arg("images")
        .output()
        .expect("Failed to get existing images")
        .stdout;

    let existing_images = String::from_utf8_lossy(&output);

    let dockerfile = pide_data.original_dockerfile;
    let id: String = pide_data.name.id.to_string();
    let name_str = pide_data.name.to_string();

    let name = if !existing_images.contains(&id) {
        println!("Running for the first time...");
        &pide_data.image_id
    } else {
        println!("Resuming...");
        &name_str
    };

    let temp_name = Uuid::new_v4().to_string();

    let process = Command::new("docker")
        .arg("run")
        .arg("-v")
        .arg(format!("{}:/host",
                     current_dir()
                         .expect("failed to get cwd")
                         .to_str()
                         .expect("bad utf-8 in cwd")))
        .arg("--name")
        .arg(&temp_name)
        .arg("-it")
        .arg(&name)
        .arg("/bin/bash")
        .spawn()
        .expect("Docker failed to launch")
        .wait()
        .expect("Docker failed at runtime");

    println!("Committing container history...");
    let _ = Command::new("docker")
        .arg("commit")
        .arg(&temp_name)
        .arg(&name_str)
        .output()
        .expect("Failed to commit docker changes");

}

#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct PideName {
    repo: String,
    id: Uuid,
}

impl PideName {
    pub fn new() -> Self {
        Self {
            repo: "pide".to_string(),
            id: Uuid::new_v4(),
        }
    }

    pub fn to_string(&self) -> String {
        format!("{}:{}", &self.repo, self.id.to_string())
    }
}

#[derive(Debug, Deserialize, Serialize)]
pub struct PideFile {
    image_id: String,
    original_dockerfile: String, // TODO: not a string?
    original_workingpath: String, // TODO: not a string?
    pide_version: String, // TODO: not a string?

    // Tables go last
    name: PideName,
}

impl PideFile {
    pub fn to_string(&self) -> Result<String, ()> {
        match toml::to_string(self) {
            Ok(out) => Ok(out),
            Err(x) => {
                println!("bad: {:?}", x);
                Err(())
            }
        }
    }

    pub fn from_file(open_path: &str) -> Self {

        let mut file = File::open(open_path)
            .expect(&format!("Failed to open pide file {:}", open_path));
        let mut contents = String::new();
        let _ = file.read_to_string(&mut contents);
        toml::from_str(&contents).expect("yiss")
    }
}

fn init(dockerfile: &str) {
    // println!("{}", dockerfile);

    // println!("{:?}", PideName::new().to_string());
    let dfp = Path::new(dockerfile);
    let working_path = dfp.parent().expect("Failed to choose working path");

    assert!(dfp.is_file(), "Dockerfile doesn't exist");
    assert!(working_path.exists(), "Working path doesn't exist");

    let build_cmd = Command::new("docker")
        .arg("build")
        .arg("-f")
        .arg(dockerfile)
        .arg(working_path)
        .output()
        .expect("Failed to build base docker image!");

    let image_id = String::from_utf8_lossy(&build_cmd.stdout)
        .lines()
        .last()
        .expect("Unexpected Docker Output!")
        .split(' ')
        .last()
        .expect("Failed to decode image id")
        .to_string();

    let pide_data = PideFile {
        name: PideName::new(),
        image_id: image_id,
        original_dockerfile: dockerfile.to_string(),
        original_workingpath: working_path.to_str().unwrap().to_string(),
        pide_version: "0.2.0".to_string(),
    };

    println!("{:?}", pide_data);

    let open_path = ".pide";

    let mut file = File::create(open_path)
        .expect(&format!("Failed to open pide file {:}", open_path));
    let _ = file.write_all(&pide_data.to_string().expect("sadfoo").into_bytes())
        .expect("Failed to write to pide file");
}
<|endoftext|>


<commit_before>
use std::cmp;

type Endpoint = [u16; 4];
type Rgba = [u8; 4];

fn is_blk(size: (usize, usize)) -> bool {
	return size.0 % 4 == 0 && size.1 % 4 == 0;
}

fn bc1_encode_endpoint(pix: &Endpoint) -> u16 {
	((pix[0] >> 3) << 11) |
	((pix[1] >> 2) << 5) |
	(pix[2] >> 3)
}

fn bc1_build_entry(min: &Endpoint, max: &Endpoint, index: usize) -> Rgba {
	let coefs = match index {
		0x0 => (3, 0),
		0x1 => (0, 3),
		0x2 => (2 ,1),
		_ => (1, 2)
	};
	[((coefs.1 * min[0] + coefs.0 * max[0]) / 3) as u8,
	 ((coefs.1 * min[1] + coefs.0 * max[1]) / 3) as u8,
	 ((coefs.1 * min[2] + coefs.0 * max[2]) / 3) as u8,
	 ((coefs.1 * min[3] + coefs.0 * max[3]) / 3) as u8]
}

fn bc1_build_table(min: &Endpoint, max: &Endpoint) -> [Rgba; 4] {
	let mut interps = [[0u8; 4]; 4];
	for i in 0..4 {
		interps[i] = bc1_build_entry(&min, &max, i);
	}
	interps
}

fn bc1_minmax(pixels: &[Rgba; 16]) -> (Endpoint, Endpoint) {
	let mut min = [256u16; 4];
	let mut max = [0u16; 4];
	for p in pixels.into_iter() {
		for i in 0..4 {
			min[i] = cmp::min(min[i], p[i] as u16);
			max[i] = cmp::max(max[i], p[i] as u16);
		}
	}
	(min, max)
}

fn bc1_dist_one(a: i16, b: i16) -> u32 {
	(a - b).abs() as u32
}

fn bc1_dist(a: &Rgba, b: &Rgba) -> u32 {
	bc1_dist_one(a[0] as i16, b[0] as i16) +
	bc1_dist_one(a[1] as i16, b[1] as i16) +
	bc1_dist_one(a[2] as i16, b[2] as i16) +
	bc1_dist_one(a[3] as i16, b[3] as i16)
}

fn bc1_encode_pixel(table: &[Rgba; 4], pixels: &[Rgba; 16]) -> u32 {
	let mut mask = 0u32;
	for pix in pixels.into_iter().rev() {
		let best = (0..4).into_iter().min_by_key(|x| bc1_dist(&table[*x], pix)).unwrap() as u32;
		mask = (mask << 2) | (best & 0x03);
	}
	mask
}

/*fn bc1_total_dist(table: &[Rgba; 4], pixels: &[Rgba; 16]) -> u32 {
	let mut sum = 0u32;
	for pix in pixels.into_iter().rev() {
		sum += (0..4).into_iter().map(|x| bc1_dist(&table[x], pix)).min().unwrap();
	}
	sum
}

fn iter_endpoint(mut to_iter: Endpoint, other: &Endpoint, pixels: &[Rgba; 16], offsets: &[i16]) -> [u16; 4] {
	let (mut best, mut index, mut offset) = (bc1_total_dist(&bc1_build_table(&to_iter, other), pixels), 0, 0);
	for i in 0..3 {
		for o in offsets.into_iter() {
			let mut x = to_iter;
			x[i] += *o;
			let table = bc1_build_table(&x, &other);
			let dist = bc1_total_dist(&table, pixels);
			if dist < best {
				best = dist;
				index = i;
				offset = *o;
			}
		}
	}
	to_iter[index] += offset;
	to_iter
}*/

fn bc1_encode_block(pixels: &[Rgba; 16]) -> u64 {
	let (min, max) = bc1_minmax(&pixels);

	/*for _ in 0..quality {
		min = iter_endpoint(min, &max, &pixels, &[2, 4, 8]);
		max = iter_endpoint(max, &min, &pixels, &[-2, -4, -8]);
	}*/

	let table = bc1_build_table(&min, &max);

	let min = bc1_encode_endpoint(&min) as u64;
	let max = bc1_encode_endpoint(&max) as u64;

	let encoded = bc1_encode_pixel(&table, &pixels) as u64;

	(encoded << 32) | (min << 16) | max
}

pub fn bc1(image: &Vec<u8>, size: (usize, usize), _: u8) -> Result<Vec<u8>, ()> {
	if !is_blk(size) {
		return Err(());
	}

    let mut out = Vec::with_capacity(image.len() / 8);

    let block_size = 4;
    let blk_count = (size.0 / block_size, size.1 / block_size);

   	for x in 0..blk_count.0 {
   		for y in 0..blk_count.1 {

   			let mut pixels = [[0u8; 4]; 16]; // No compile time constants
   			for px in 0..block_size {
   				for py in 0..block_size {
   					let x = x * block_size + px;
   					let y = y * block_size + py;

   					let pi = (x * size.0 + y) * 4; // in bytes
   					let pix = [image[pi], image[pi + 1], image[pi + 2], image[pi + 3]];

   					pixels[px * block_size + py] = pix;
   				}
   			}

   			let mut encoded = bc1_encode_block(&pixels);

   			for _ in 0..8 {
   				out.push((encoded & 0xFF) as u8);
   				encoded >>= 8;
   			}
		}
	}
	Ok(out)
}
<commit_msg>More bc work.<commit_after>
use std::cmp;

type Rgba = [u8; 4];

fn is_blk(size: (usize, usize)) -> bool {
	return size.0 % 4 == 0 && size.1 % 4 == 0;
}

fn bc1_encode_endpoint(pix: &Rgba) -> u16 {
	((pix[0] as u16 >> 3) << 11) |
	((pix[1] as u16 >> 2) << 5) |
	(pix[2] as u16 >> 3)
}

fn bc1_build_entry(min: &Rgba, max: &Rgba, index: usize) -> Rgba {
	let coefs = match index {
		0x0 => (3, 0),
		0x1 => (0, 3),
		0x2 => (2 ,1),
		_ => (1, 2)
	};
	[((coefs.1 * min[0] as u16 + coefs.0 * max[0] as u16) / 3) as u8,
	 ((coefs.1 * min[1] as u16 + coefs.0 * max[1] as u16) / 3) as u8,
	 ((coefs.1 * min[2] as u16 + coefs.0 * max[2] as u16) / 3) as u8,
	 ((coefs.1 * min[3] as u16 + coefs.0 * max[3] as u16) / 3) as u8]
}

fn bc1_build_table(min: &Rgba, max: &Rgba) -> [Rgba; 4] {
	let mut interps = [[0u8; 4]; 4];
	for i in 0..4 {
		interps[i] = bc1_build_entry(&min, &max, i);
	}
	interps
}

fn bc1_build_endpoints(pixels: &[Rgba; 16]) -> (Rgba, Rgba) {
	let mut min = [u8::max_value(); 4];
	let mut max = [0u8; 4];
	//let mut avg = [0f32; 4];
	for p in pixels.into_iter() {
		for i in 0..4 {
			min[i] = cmp::min(min[i], p[i]);
			max[i] = cmp::max(max[i], p[i]);
			//avg[i] += p[i] as f32;
		}
	}
	(min, max)

	/*let max_len = ((max[0] * max[0] + max[1] * max[1] + max[2] * max[2]) as f32).sqrt();
	let min_len = ((min[0] * min[0] + min[1] * min[1] + min[2] * min[2]) as f32).sqrt();
	let avg_len = (avg[0] * avg[0] + avg[1] * avg[1] + avg[2] * avg[2]).sqrt();
	let max_len = avg_len / max_len;
	let norm_max_avg = [(avg[0] / max_len) as u8, (avg[1] / max_len) as u8, (avg[2] / max_len) as u8, max[3]];
	let min_len = avg_len / min_len;
	let norm_min_avg = [(avg[0] / min_len) as u8, (avg[1] / min_len) as u8, (avg[2] / min_len) as u8, min[3]];

	let min_point = *pixels.into_iter().min_by_key(|p| bc1_dist(&p, &min)).unwrap();
	let max_point = *pixels.into_iter().min_by_key(|p| bc1_dist(&p, &max)).unwrap();

	[(min, max),
	(norm_min_avg, norm_max_avg),
	(min, norm_max_avg),
	(norm_min_avg, max),

	(min_point, max_point),
	(min, max_point),
	(min_point, max)]*/
}

fn bc1_dist_one(a: u8, b: u8) -> u32 {
	let x = a as i32 - b as i32;
	(x * x) as u32
}

fn bc1_dist(a: &Rgba, b: &Rgba) -> u32 {
	bc1_dist_one(a[0], b[0]) +
	bc1_dist_one(a[1], b[1]) +
	bc1_dist_one(a[2], b[2]) +
	bc1_dist_one(a[3], b[3])
}

fn bc1_encode_pixel(table: &[Rgba; 4], pixels: &[Rgba; 16]) -> u32 {
	let mut mask = 0u32;
	for pix in pixels.into_iter().rev() {
		let best = (0..4).into_iter().min_by_key(|x| bc1_dist(&table[*x], pix)).unwrap() as u32;
		mask = (mask << 2) | (best & 0x03);
	}
	mask
}

fn bc1_total_dist(table: &[Rgba; 4], pixels: &[Rgba; 16]) -> u32 {
	let mut sum = 0u32;
	for pix in pixels.into_iter().rev() {
		sum += (0..4).into_iter().map(|x| bc1_dist(&table[x], pix)).min().unwrap();
	}
	sum
}

fn bc1_encode_block(pixels: &[Rgba; 16]) -> (u64, u32) {
	let (min, max) = bc1_build_endpoints(&pixels);

	//let (min, max) = *bc1_build_endpoints(pixels).into_iter().min_by_key(|p| bc1_total_dist(&bc1_build_table(&p.0, &p.1), pixels)).unwrap();

	let table = bc1_build_table(&min, &max);

	let dist = bc1_total_dist(&table, pixels);

	let min = bc1_encode_endpoint(&min) as u64;
	let max = bc1_encode_endpoint(&max) as u64;

	let encoded = bc1_encode_pixel(&table, &pixels) as u64;

	((encoded << 32) | (min << 16) | max, dist)
}

pub fn bc1(image: &Vec<u8>, size: (usize, usize), _: u8) -> Result<Vec<u8>, ()> {
	if !is_blk(size) {
		return Err(());
	}

    let mut out = Vec::with_capacity(image.len() / 8);
    let mut total_dist = 0u32;

    let block_size = 4;
    let blk_count = (size.0 / block_size, size.1 / block_size);

   	for x in 0..blk_count.0 {
   		for y in 0..blk_count.1 {

   			let mut pixels = [[0u8; 4]; 16]; // No compile time constants
   			for px in 0..block_size {
   				for py in 0..block_size {
   					let x = x * block_size + px;
   					let y = y * block_size + py;

   					let pi = (x * size.0 + y) * 4; // in bytes
   					let pix = [image[pi], image[pi + 1], image[pi + 2], image[pi + 3]];

   					pixels[px * block_size + py] = pix;
   				}
   			}

   			let (mut encoded, dist) = bc1_encode_block(&pixels);
   			total_dist += dist;

   			for _ in 0..8 {
   				out.push((encoded & 0xFF) as u8);
   				encoded >>= 8;
   			}
		}
	}
	println!("per-pixel dist = {:?}", total_dist / (size.0 * size.1) as u32);
	Ok(out)
}
<|endoftext|>


<commit_before>use std::cell::RefCell;
use std::cmp;

use rand;
use rand::Rng;
use itertools::Itertools;
use rgsl::randist::multinomial::multinomial_pdf;

use bio::stats::{Prob, LogProb};

use io::codebook::{Codebook, FeatureID};

use model::readout::{Expressions, Counts, Miscalls, Xi};

pub trait AbstractFeatureModel {
    /// FeatureID of this feature.
    fn feature_id(&self) -> FeatureID;

    /// Neighbors of this feature.
    fn neighbors(&self) -> &[FeatureID];

    /// Minimum hamming distance between any pair of features.
    fn min_dist(&self) -> u8;

    /// Number of events (calls and miscalls) except dropout.
    fn event_count_non_dropout(
        &self,
        miscalls_exact: &Miscalls,
        miscalls_mismatch: &Miscalls
    ) -> u32;

    /// Dropout probability.
    fn prob_dropout(&self) -> f64;

    /// Probability of exact miscall to given neighbor index.
    fn prob_miscall_exact(&self, neighbor_index: usize) -> f64;

    /// Probability of mismatching miscall to given neighbor index.
    fn prob_miscall_mismatch(&self, neighbor_index: usize) -> f64;

    /// Calculate maximum likelihood estimate of miscalls given expression and store in the given
    /// arrays.
    /// Returns the total absolute changes to estimated miscall counts.
    fn mle_miscalls(
        &self,
        expressions: &Expressions,
        miscalls_exact: &mut Miscalls,
        miscalls_mismatch: &mut Miscalls,
        rng: &mut rand::StdRng
    ) {
        let x = expressions[self.feature_id()];

        // We need to ensure that the sum of the estimated miscalls does not exceed x.
        // Hence, we iterate randomly over the neighbors such that we can simply stop once that
        // happens without causing a bias.
        let mut idx = (0..self.neighbors().len()).collect_vec();
        rng.shuffle(&mut idx);

        // by stochastic rounding, we ensure that on average \sum x*p_i = x
        // numeric rounding would not work if many of the probabilities are small enough to yield
        // expectations less than 1
        let mut stochastic_round = |v: f64| v.floor() as u32 + (rng.next_f64() <= v % 1.0) as u32;

        let mut rest = x;
        for i in idx {
            let n = self.neighbors()[i];
            let miscall_exact = self.prob_miscall_exact(i) * x as f64;
            miscalls_exact.set(
                self.feature_id(), n, stochastic_round(miscall_exact), &mut rest
            );

            if self.min_dist() == 4 {
                let miscall_mismatch = self.prob_miscall_mismatch(i) * x as f64;
                let miscall_mismatch = stochastic_round(miscall_mismatch);
                miscalls_mismatch.set(
                    self.feature_id(), n, miscall_mismatch, &mut rest
                );
            }
        }
    }

    /// Calculate maximum likelihood estimate of expression given miscalls.
    /// Returns the absolute amount of change to the expression.
    fn mle_expression(
        &self,
        expressions: &mut Expressions,
        miscalls_exact: &Miscalls,
        miscalls_mismatch: &Miscalls
    ) -> i32 {
        let event_count = self.event_count_non_dropout(miscalls_exact, miscalls_mismatch);

        let x_ = cmp::max(
            (event_count as f64 / (1.0 - self.prob_dropout())).floor() as u32,
            // or at least as many as we have events coming from this feature
            // TODO does this allow x to decrease properly?
            event_count
        );

        let x = expressions.get_mut(self.feature_id()).unwrap();

        let change = *x as i32 - x_ as i32;

        *x = x_;

        change
    }
}

pub struct FeatureModel {
    pub(crate) feature_id: FeatureID,
    event_probs: Vec<f64>,
    neighbors: Vec<FeatureID>,
    min_dist: u8,
    pub(crate) counts: Counts,
    event_counts: RefCell<Vec<u32>>
}

impl FeatureModel {
    pub fn new(
        feature: FeatureID,
        counts: Counts,
        codebook: &Codebook,
        xi: &Xi
    ) -> Self {
        assert!(
            codebook.min_dist == 2 || codebook.min_dist == 4,
            "unsupported hamming distance: {}, supported: 2, 4",
            codebook.min_dist
        );

        let feature_record = codebook.record(feature);

        let xi_call = xi.prob(feature_record.codeword(), feature_record.codeword());

        let prob_call_exact = xi_call[0];
        let prob_call_mismatch = if codebook.min_dist == 4 {
            xi_call[1]
        } else {
            LogProb::ln_zero()
        };

        let neighbors_4 = codebook.neighbors(feature, 4);
        let neighbors_2 = if codebook.min_dist == 2 {
            codebook.neighbors(feature, 2)
        } else {
            vec![]
        };

        let mut neighbors = neighbors_4.clone();
        neighbors.extend(&neighbors_2);

        let xi_miscall_4 = codebook.neighbors(feature, 4).iter().map(|&n| {
            xi.prob(feature_record.codeword(), codebook.record(n).codeword())
        }).collect_vec();
        let xi_miscall_2 = if codebook.min_dist == 2 {
            codebook.neighbors(feature, 2).iter().map(|&n| {
                xi.prob(feature_record.codeword(), codebook.record(n).codeword())
            }).collect_vec()
        } else {
            vec![]
        };

        let prob_miscall_exact = xi_miscall_4.iter().map(|xi| {
            xi[0]
        }).chain(xi_miscall_2.iter().map(|xi| xi[0])).collect_vec();

        let prob_miscall_mismatch = if codebook.min_dist == 4 {
            xi_miscall_4.iter().map(|xi| xi[1]).collect_vec()
        } else {
            vec![]
        };

        let mut total = vec![prob_call_exact, prob_call_mismatch];
        total.extend(&prob_miscall_exact);
        total.extend(&prob_miscall_mismatch);

        let prob_total = LogProb::ln_sum_exp(&total);
        assert!(*prob_total <= 0.0, "prob_total = {} > 0", *prob_total);

        let mut event_probs = vec![
            *Prob::from(prob_call_exact),
            *Prob::from(prob_call_mismatch),
            *Prob::from(prob_total.ln_one_minus_exp())
        ];
        event_probs.extend(prob_miscall_exact.iter().map(|&p| *Prob::from(p)));
        event_probs.extend(prob_miscall_mismatch.iter().map(|&p| *Prob::from(p)));

        FeatureModel {
            feature_id: feature,
            event_probs: event_probs,
            neighbors: neighbors,
            min_dist: codebook.min_dist,
            counts: counts,
            event_counts: RefCell::new(Vec::new())
        }
    }

    /// Calculate likelihood of given expression.
    pub fn likelihood(
        &self,
        x: u32,
        miscalls_exact: &Miscalls,
        miscalls_mismatch: &Miscalls
    ) -> LogProb {
        let count_exact_miscalled = miscalls_exact.total_to(self.feature_id);
        let count_mismatch_miscalled = miscalls_mismatch.total_to(self.feature_id);

        if self.counts.exact < count_exact_miscalled {
            panic!("bug: likelihood cannot be calculated if exact miscalls exceed counts: counts={}, miscalls={}", self.counts.exact, count_exact_miscalled);
        }
        if self.counts.mismatch < count_mismatch_miscalled {
            panic!("bug: likelihood cannot be calculated if mismatch miscalls exceed counts: counts={}, miscalls={}", self.counts.mismatch, count_mismatch_miscalled);
        }
        let total_count = self.counts.exact - count_exact_miscalled +
                          self.counts.mismatch - count_mismatch_miscalled;
        if total_count > x {
            panic!("bug: event count exceeds expression: x={}, event count={}", x, total_count);
        }

        let calls_exact = self.counts.exact - count_exact_miscalled;
        let calls_mismatch = self.counts.mismatch - count_mismatch_miscalled;

        // setup event counts
        {
            let mut event_counts = self.event_counts.borrow_mut();
            event_counts.clear();
            event_counts.push(
                // exact calls
                calls_exact
            );
            event_counts.push(
                // mismatch calls
                calls_mismatch
            );
            event_counts.push(
                // dropouts (fill in later)
                0
            );
            // miscalls
            event_counts.extend(self.neighbors.iter().map(
                |&n| miscalls_exact.get(self.feature_id, n)
            ));
            if self.min_dist == 4 {
                event_counts.extend(
                    self.neighbors.iter().map(
                        |&n| miscalls_mismatch.get(self.feature_id, n)
                    )
                );
            }

            // set dropouts
            let total_counts = event_counts.iter().sum();
            assert!(x >= total_counts, "total event counts exceed expression: x={} {:?}", x, event_counts);
            let dropouts = x - total_counts;
            event_counts[2] = dropouts;
        }

        LogProb::from(Prob(
            multinomial_pdf(&self.event_probs, &self.event_counts.borrow())
        ))
    }

    /// Minimum expression given fixed miscalls.
    pub fn min_expression(&self, miscalls_exact: &Miscalls, miscalls_mismatch: &Miscalls) -> u32 {
        self.counts.exact.saturating_sub(miscalls_exact.total_to(self.feature_id)) +
        self.counts.mismatch.saturating_sub(miscalls_mismatch.total_to(self.feature_id)) +
        miscalls_exact.total_from(self.feature_id) + miscalls_mismatch.total_from(self.feature_id)
    }
}

impl AbstractFeatureModel for FeatureModel {
    fn feature_id(&self) -> FeatureID {
        self.feature_id
    }

    fn neighbors(&self) -> &[FeatureID] {
        &self.neighbors
    }

    fn min_dist(&self) -> u8 {
        self.min_dist
    }

    fn event_count_non_dropout(
        &self,
        miscalls_exact: &Miscalls,
        miscalls_mismatch: &Miscalls
    ) -> u32 {
        let calls_exact = self.counts.exact.saturating_sub(
            miscalls_exact.total_to(self.feature_id)
        );
        let calls_mismatch = self.counts.mismatch.saturating_sub(
            miscalls_mismatch.total_to(self.feature_id)
        );

        calls_exact + calls_mismatch +
        miscalls_exact.total_from(self.feature_id) +
        miscalls_mismatch.total_from(self.feature_id)
    }

    fn prob_dropout(&self) -> f64 {
        self.event_probs[2]
    }

    fn prob_miscall_exact(&self, neighbor_index: usize) -> f64 {
        self.event_probs[3 + neighbor_index]
    }

    fn prob_miscall_mismatch(&self, neighbor_index: usize) -> f64 {
        self.event_probs[3 + self.neighbors.len() + neighbor_index]
    }
}

pub struct NoiseModel {
    pub(crate) feature_id: FeatureID,
    pub(crate) not_expressed_feature_ids: Vec<FeatureID>,
    pub(crate) not_expressed_counts: Vec<Counts>,
    probs_miscall_exact: Vec<Prob>,
    probs_miscall_mismatch: Vec<Prob>,
    prob_dropout: Prob,
    neighbors: Vec<FeatureID>,
    min_dist: u8,
}

impl NoiseModel {
    pub fn new(
        feature_id: FeatureID,
        not_expressed_feature_ids: Vec<FeatureID>,
        not_expressed_counts: Vec<Counts>,
        codebook: &Codebook,
        xi: &Xi
    ) -> Self {
        assert!(
            codebook.min_dist == 2 || codebook.min_dist == 4,
            "unsupported hamming distance: {}, supported: 2, 4",
            codebook.min_dist
        );

        // miscalls generated from noise
        let xi_miscall = codebook.records().iter().filter_map(|feat_rec| {
            if feat_rec.expressed() {
                Some(xi.prob(codebook.noise().codeword(), feat_rec.codeword()))
            } else { None }
        }).collect_vec();
        let neighbors = codebook.records().iter().filter_map(|rec| {
            if rec.expressed() {
                Some(codebook.get_id(rec.name()))
            } else { None }
        }).collect_vec();

        let probs_miscall_exact = xi_miscall.iter().map(|xi| xi[0]).collect_vec();
        let probs_miscall_mismatch = if codebook.min_dist == 4 {
            xi_miscall.iter().map(|xi| xi[1]).collect_vec()
        } else {
            vec![]
        };

        let prob_total_miscall_exact = LogProb::ln_sum_exp(&probs_miscall_exact);
        let prob_total_miscall_mismatch = LogProb::ln_sum_exp(&probs_miscall_mismatch);

        // probs for calls of not expressed features
        let xi_not_expressed = not_expressed_feature_ids.iter().map(|&f| {
            xi.prob(codebook.noise().codeword(), codebook.record(f).codeword())
        }).collect_vec();

        let prob_not_expressed_exact = LogProb::ln_sum_exp(
            &xi_not_expressed.iter().map(|xi| xi[0]).collect_vec()
        );
        let prob_not_expressed_mismatch = LogProb::ln_sum_exp(
            &xi_not_expressed.iter().map(|xi| xi[1]).collect_vec()
        );

        // dropout
        let prob_dropout = LogProb::ln_sum_exp(&[
            prob_not_expressed_exact,
            prob_not_expressed_mismatch,
            prob_total_miscall_exact,
            prob_total_miscall_mismatch
        ]).ln_one_minus_exp();

        NoiseModel {
            feature_id: feature_id,
            not_expressed_feature_ids: not_expressed_feature_ids,
            not_expressed_counts: not_expressed_counts,
            probs_miscall_exact: probs_miscall_exact.iter().map(|&p| Prob::from(p)).collect_vec(),
            probs_miscall_mismatch: probs_miscall_mismatch.iter().map(|&p| Prob::from(p)).collect_vec(),
            prob_dropout: Prob::from(prob_dropout),
            neighbors: neighbors,
            min_dist: codebook.min_dist
        }
    }
}

impl AbstractFeatureModel for NoiseModel {
    fn feature_id(&self) -> FeatureID {
        self.feature_id
    }

    fn neighbors(&self) -> &[FeatureID] {
        &self.neighbors
    }

    fn min_dist(&self) -> u8 {
        self.min_dist
    }

    fn event_count_non_dropout(
        &self,
        miscalls_exact: &Miscalls,
        miscalls_mismatch: &Miscalls
    ) -> u32 {
        let mut calls_exact = 0;
        let mut calls_mismatch = 0;
        for (&id, counts) in self.not_expressed_feature_ids.iter().zip(&self.not_expressed_counts) {
            calls_exact += counts.exact.saturating_sub(miscalls_exact.total_to(id));
            calls_mismatch += counts.mismatch.saturating_sub(miscalls_mismatch.total_to(id));
        }

        let event_count = calls_exact + calls_mismatch +
                          miscalls_exact.total_from(self.feature_id) +
                          miscalls_mismatch.total_from(self.feature_id);

        event_count
    }

    fn prob_dropout(&self) -> f64 {
        //*self.prob_dropout
        1.0
    }

    fn prob_miscall_exact(&self, neighbor_index: usize) -> f64 {
        *self.probs_miscall_exact[neighbor_index]
    }

    fn prob_miscall_mismatch(&self, neighbor_index: usize) -> f64 {
        *self.probs_miscall_mismatch[neighbor_index]
    }
}
<commit_msg>Activate noise again.<commit_after>use std::cell::RefCell;
use std::cmp;

use rand;
use rand::Rng;
use itertools::Itertools;
use rgsl::randist::multinomial::multinomial_pdf;

use bio::stats::{Prob, LogProb};

use io::codebook::{Codebook, FeatureID};

use model::readout::{Expressions, Counts, Miscalls, Xi};

pub trait AbstractFeatureModel {
    /// FeatureID of this feature.
    fn feature_id(&self) -> FeatureID;

    /// Neighbors of this feature.
    fn neighbors(&self) -> &[FeatureID];

    /// Minimum hamming distance between any pair of features.
    fn min_dist(&self) -> u8;

    /// Number of events (calls and miscalls) except dropout.
    fn event_count_non_dropout(
        &self,
        miscalls_exact: &Miscalls,
        miscalls_mismatch: &Miscalls
    ) -> u32;

    /// Dropout probability.
    fn prob_dropout(&self) -> f64;

    /// Probability of exact miscall to given neighbor index.
    fn prob_miscall_exact(&self, neighbor_index: usize) -> f64;

    /// Probability of mismatching miscall to given neighbor index.
    fn prob_miscall_mismatch(&self, neighbor_index: usize) -> f64;

    /// Calculate maximum likelihood estimate of miscalls given expression and store in the given
    /// arrays.
    /// Returns the total absolute changes to estimated miscall counts.
    fn mle_miscalls(
        &self,
        expressions: &Expressions,
        miscalls_exact: &mut Miscalls,
        miscalls_mismatch: &mut Miscalls,
        rng: &mut rand::StdRng
    ) {
        let x = expressions[self.feature_id()];

        // We need to ensure that the sum of the estimated miscalls does not exceed x.
        // Hence, we iterate randomly over the neighbors such that we can simply stop once that
        // happens without causing a bias.
        let mut idx = (0..self.neighbors().len()).collect_vec();
        rng.shuffle(&mut idx);

        // by stochastic rounding, we ensure that on average \sum x*p_i = x
        // numeric rounding would not work if many of the probabilities are small enough to yield
        // expectations less than 1
        let mut stochastic_round = |v: f64| v.floor() as u32 + (rng.next_f64() <= v % 1.0) as u32;

        let mut rest = x;
        for i in idx {
            let n = self.neighbors()[i];
            let miscall_exact = self.prob_miscall_exact(i) * x as f64;
            miscalls_exact.set(
                self.feature_id(), n, stochastic_round(miscall_exact), &mut rest
            );

            if self.min_dist() == 4 {
                let miscall_mismatch = self.prob_miscall_mismatch(i) * x as f64;
                let miscall_mismatch = stochastic_round(miscall_mismatch);
                miscalls_mismatch.set(
                    self.feature_id(), n, miscall_mismatch, &mut rest
                );
            }
        }
    }

    /// Calculate maximum likelihood estimate of expression given miscalls.
    /// Returns the absolute amount of change to the expression.
    fn mle_expression(
        &self,
        expressions: &mut Expressions,
        miscalls_exact: &Miscalls,
        miscalls_mismatch: &Miscalls
    ) -> i32 {
        let event_count = self.event_count_non_dropout(miscalls_exact, miscalls_mismatch);

        let x_ = cmp::max(
            (event_count as f64 / (1.0 - self.prob_dropout())).floor() as u32,
            // or at least as many as we have events coming from this feature
            // TODO does this allow x to decrease properly?
            event_count
        );

        let x = expressions.get_mut(self.feature_id()).unwrap();

        let change = *x as i32 - x_ as i32;

        *x = x_;

        change
    }
}

pub struct FeatureModel {
    pub(crate) feature_id: FeatureID,
    event_probs: Vec<f64>,
    neighbors: Vec<FeatureID>,
    min_dist: u8,
    pub(crate) counts: Counts,
    event_counts: RefCell<Vec<u32>>
}

impl FeatureModel {
    pub fn new(
        feature: FeatureID,
        counts: Counts,
        codebook: &Codebook,
        xi: &Xi
    ) -> Self {
        assert!(
            codebook.min_dist == 2 || codebook.min_dist == 4,
            "unsupported hamming distance: {}, supported: 2, 4",
            codebook.min_dist
        );

        let feature_record = codebook.record(feature);

        let xi_call = xi.prob(feature_record.codeword(), feature_record.codeword());

        let prob_call_exact = xi_call[0];
        let prob_call_mismatch = if codebook.min_dist == 4 {
            xi_call[1]
        } else {
            LogProb::ln_zero()
        };

        let neighbors_4 = codebook.neighbors(feature, 4);
        let neighbors_2 = if codebook.min_dist == 2 {
            codebook.neighbors(feature, 2)
        } else {
            vec![]
        };

        let mut neighbors = neighbors_4.clone();
        neighbors.extend(&neighbors_2);

        let xi_miscall_4 = codebook.neighbors(feature, 4).iter().map(|&n| {
            xi.prob(feature_record.codeword(), codebook.record(n).codeword())
        }).collect_vec();
        let xi_miscall_2 = if codebook.min_dist == 2 {
            codebook.neighbors(feature, 2).iter().map(|&n| {
                xi.prob(feature_record.codeword(), codebook.record(n).codeword())
            }).collect_vec()
        } else {
            vec![]
        };

        let prob_miscall_exact = xi_miscall_4.iter().map(|xi| {
            xi[0]
        }).chain(xi_miscall_2.iter().map(|xi| xi[0])).collect_vec();

        let prob_miscall_mismatch = if codebook.min_dist == 4 {
            xi_miscall_4.iter().map(|xi| xi[1]).collect_vec()
        } else {
            vec![]
        };

        let mut total = vec![prob_call_exact, prob_call_mismatch];
        total.extend(&prob_miscall_exact);
        total.extend(&prob_miscall_mismatch);

        let prob_total = LogProb::ln_sum_exp(&total);
        assert!(*prob_total <= 0.0, "prob_total = {} > 0", *prob_total);

        let mut event_probs = vec![
            *Prob::from(prob_call_exact),
            *Prob::from(prob_call_mismatch),
            *Prob::from(prob_total.ln_one_minus_exp())
        ];
        event_probs.extend(prob_miscall_exact.iter().map(|&p| *Prob::from(p)));
        event_probs.extend(prob_miscall_mismatch.iter().map(|&p| *Prob::from(p)));

        FeatureModel {
            feature_id: feature,
            event_probs: event_probs,
            neighbors: neighbors,
            min_dist: codebook.min_dist,
            counts: counts,
            event_counts: RefCell::new(Vec::new())
        }
    }

    /// Calculate likelihood of given expression.
    pub fn likelihood(
        &self,
        x: u32,
        miscalls_exact: &Miscalls,
        miscalls_mismatch: &Miscalls
    ) -> LogProb {
        let count_exact_miscalled = miscalls_exact.total_to(self.feature_id);
        let count_mismatch_miscalled = miscalls_mismatch.total_to(self.feature_id);

        if self.counts.exact < count_exact_miscalled {
            panic!("bug: likelihood cannot be calculated if exact miscalls exceed counts: counts={}, miscalls={}", self.counts.exact, count_exact_miscalled);
        }
        if self.counts.mismatch < count_mismatch_miscalled {
            panic!("bug: likelihood cannot be calculated if mismatch miscalls exceed counts: counts={}, miscalls={}", self.counts.mismatch, count_mismatch_miscalled);
        }
        let total_count = self.counts.exact - count_exact_miscalled +
                          self.counts.mismatch - count_mismatch_miscalled;
        if total_count > x {
            panic!("bug: event count exceeds expression: x={}, event count={}", x, total_count);
        }

        let calls_exact = self.counts.exact - count_exact_miscalled;
        let calls_mismatch = self.counts.mismatch - count_mismatch_miscalled;

        // setup event counts
        {
            let mut event_counts = self.event_counts.borrow_mut();
            event_counts.clear();
            event_counts.push(
                // exact calls
                calls_exact
            );
            event_counts.push(
                // mismatch calls
                calls_mismatch
            );
            event_counts.push(
                // dropouts (fill in later)
                0
            );
            // miscalls
            event_counts.extend(self.neighbors.iter().map(
                |&n| miscalls_exact.get(self.feature_id, n)
            ));
            if self.min_dist == 4 {
                event_counts.extend(
                    self.neighbors.iter().map(
                        |&n| miscalls_mismatch.get(self.feature_id, n)
                    )
                );
            }

            // set dropouts
            let total_counts = event_counts.iter().sum();
            assert!(x >= total_counts, "total event counts exceed expression: x={} {:?}", x, event_counts);
            let dropouts = x - total_counts;
            event_counts[2] = dropouts;
        }

        LogProb::from(Prob(
            multinomial_pdf(&self.event_probs, &self.event_counts.borrow())
        ))
    }

    /// Minimum expression given fixed miscalls.
    pub fn min_expression(&self, miscalls_exact: &Miscalls, miscalls_mismatch: &Miscalls) -> u32 {
        self.counts.exact.saturating_sub(miscalls_exact.total_to(self.feature_id)) +
        self.counts.mismatch.saturating_sub(miscalls_mismatch.total_to(self.feature_id)) +
        miscalls_exact.total_from(self.feature_id) + miscalls_mismatch.total_from(self.feature_id)
    }
}

impl AbstractFeatureModel for FeatureModel {
    fn feature_id(&self) -> FeatureID {
        self.feature_id
    }

    fn neighbors(&self) -> &[FeatureID] {
        &self.neighbors
    }

    fn min_dist(&self) -> u8 {
        self.min_dist
    }

    fn event_count_non_dropout(
        &self,
        miscalls_exact: &Miscalls,
        miscalls_mismatch: &Miscalls
    ) -> u32 {
        let calls_exact = self.counts.exact.saturating_sub(
            miscalls_exact.total_to(self.feature_id)
        );
        let calls_mismatch = self.counts.mismatch.saturating_sub(
            miscalls_mismatch.total_to(self.feature_id)
        );

        calls_exact + calls_mismatch +
        miscalls_exact.total_from(self.feature_id) +
        miscalls_mismatch.total_from(self.feature_id)
    }

    fn prob_dropout(&self) -> f64 {
        self.event_probs[2]
    }

    fn prob_miscall_exact(&self, neighbor_index: usize) -> f64 {
        self.event_probs[3 + neighbor_index]
    }

    fn prob_miscall_mismatch(&self, neighbor_index: usize) -> f64 {
        self.event_probs[3 + self.neighbors.len() + neighbor_index]
    }
}

pub struct NoiseModel {
    pub(crate) feature_id: FeatureID,
    pub(crate) not_expressed_feature_ids: Vec<FeatureID>,
    pub(crate) not_expressed_counts: Vec<Counts>,
    probs_miscall_exact: Vec<Prob>,
    probs_miscall_mismatch: Vec<Prob>,
    prob_dropout: Prob,
    neighbors: Vec<FeatureID>,
    min_dist: u8,
}

impl NoiseModel {
    pub fn new(
        feature_id: FeatureID,
        not_expressed_feature_ids: Vec<FeatureID>,
        not_expressed_counts: Vec<Counts>,
        codebook: &Codebook,
        xi: &Xi
    ) -> Self {
        assert!(
            codebook.min_dist == 2 || codebook.min_dist == 4,
            "unsupported hamming distance: {}, supported: 2, 4",
            codebook.min_dist
        );

        // miscalls generated from noise
        let xi_miscall = codebook.records().iter().filter_map(|feat_rec| {
            if feat_rec.expressed() {
                Some(xi.prob(codebook.noise().codeword(), feat_rec.codeword()))
            } else { None }
        }).collect_vec();
        let neighbors = codebook.records().iter().filter_map(|rec| {
            if rec.expressed() {
                Some(codebook.get_id(rec.name()))
            } else { None }
        }).collect_vec();

        let probs_miscall_exact = xi_miscall.iter().map(|xi| xi[0]).collect_vec();
        let probs_miscall_mismatch = if codebook.min_dist == 4 {
            xi_miscall.iter().map(|xi| xi[1]).collect_vec()
        } else {
            vec![]
        };

        let prob_total_miscall_exact = LogProb::ln_sum_exp(&probs_miscall_exact);
        let prob_total_miscall_mismatch = LogProb::ln_sum_exp(&probs_miscall_mismatch);

        // probs for calls of not expressed features
        let xi_not_expressed = not_expressed_feature_ids.iter().map(|&f| {
            xi.prob(codebook.noise().codeword(), codebook.record(f).codeword())
        }).collect_vec();

        let prob_not_expressed_exact = LogProb::ln_sum_exp(
            &xi_not_expressed.iter().map(|xi| xi[0]).collect_vec()
        );
        let prob_not_expressed_mismatch = LogProb::ln_sum_exp(
            &xi_not_expressed.iter().map(|xi| xi[1]).collect_vec()
        );

        // dropout
        let prob_dropout = LogProb::ln_sum_exp(&[
            prob_not_expressed_exact,
            prob_not_expressed_mismatch,
            prob_total_miscall_exact,
            prob_total_miscall_mismatch
        ]).ln_one_minus_exp();

        NoiseModel {
            feature_id: feature_id,
            not_expressed_feature_ids: not_expressed_feature_ids,
            not_expressed_counts: not_expressed_counts,
            probs_miscall_exact: probs_miscall_exact.iter().map(|&p| Prob::from(p)).collect_vec(),
            probs_miscall_mismatch: probs_miscall_mismatch.iter().map(|&p| Prob::from(p)).collect_vec(),
            prob_dropout: Prob::from(prob_dropout),
            neighbors: neighbors,
            min_dist: codebook.min_dist
        }
    }
}

impl AbstractFeatureModel for NoiseModel {
    fn feature_id(&self) -> FeatureID {
        self.feature_id
    }

    fn neighbors(&self) -> &[FeatureID] {
        &self.neighbors
    }

    fn min_dist(&self) -> u8 {
        self.min_dist
    }

    fn event_count_non_dropout(
        &self,
        miscalls_exact: &Miscalls,
        miscalls_mismatch: &Miscalls
    ) -> u32 {
        let mut calls_exact = 0;
        let mut calls_mismatch = 0;
        for (&id, counts) in self.not_expressed_feature_ids.iter().zip(&self.not_expressed_counts) {
            calls_exact += counts.exact.saturating_sub(miscalls_exact.total_to(id));
            calls_mismatch += counts.mismatch.saturating_sub(miscalls_mismatch.total_to(id));
        }

        let event_count = calls_exact + calls_mismatch +
                          miscalls_exact.total_from(self.feature_id) +
                          miscalls_mismatch.total_from(self.feature_id);

        event_count
    }

    fn prob_dropout(&self) -> f64 {
        *self.prob_dropout
    }

    fn prob_miscall_exact(&self, neighbor_index: usize) -> f64 {
        *self.probs_miscall_exact[neighbor_index]
    }

    fn prob_miscall_mismatch(&self, neighbor_index: usize) -> f64 {
        *self.probs_miscall_mismatch[neighbor_index]
    }
}
<|endoftext|>