<commit_before>// By compiling this file we check that all the intrinsics we care about continue to be provided by
// the `compiler_builtins` crate regardless of the changes we make to it. If we, by mistake, stop
// compiling a C implementation and forget to implement that intrinsic in Rust, this file will fail
// to link due to the missing intrinsic (symbol).

#![allow(unused_features)]
#![cfg_attr(thumb, no_main)]
#![deny(dead_code)]
#![feature(alloc_system)]
#![feature(asm)]
#![feature(compiler_builtins_lib)]
#![feature(core_float)]
#![feature(lang_items)]
#![feature(start)]
#![feature(i128_type)]
#![no_std]

#[cfg(not(thumb))]
extern crate alloc_system;
extern crate compiler_builtins;

// NOTE cfg(not(thumbv6m)) means that the operation is not supported on ARMv6-M at all. Not even
// compiler-rt provides a C/assembly implementation.

// Every function in this module maps will be lowered to an intrinsic by LLVM, if the platform
// doesn't have native support for the operation used in the function. ARM has a naming convention
// convention for its intrinsics that's different from other architectures; that's why some function
// have an additional comment: the function name is the ARM name for the intrinsic and the comment
// in the non-ARM name for the intrinsic.
mod intrinsics {
    use core::num::Float;

    // trunccdfsf2
    pub fn aeabi_d2f(x: f64) -> f32 {
        x as f32
    }

    // fixdfsi
    pub fn aeabi_d2i(x: f64) -> i32 {
        x as i32
    }

    // fixdfdi
    #[cfg(not(thumbv6m))]
    pub fn aeabi_d2l(x: f64) -> i64 {
        x as i64
    }

    #[cfg(thumbv6m)]
    pub fn aeabi_d2l(_: f64) -> i64 {
        0
    }

    // fixunsdfsi
    pub fn aeabi_d2uiz(x: f64) -> u32 {
        x as u32
    }

    // fixunsdfdi
    #[cfg(not(thumbv6m))]
    pub fn aeabi_d2ulz(x: f64) -> u64 {
        x as u64
    }

    #[cfg(thumbv6m)]
    pub fn aeabi_d2ulz(_: f64) -> u64 {
        0
    }

    // adddf3
    pub fn aeabi_dadd(a: f64, b: f64) -> f64 {
        a + b
    }

    // eqdf2
    #[cfg(not(thumbv6m))]
    pub fn aeabi_dcmpeq(a: f64, b: f64) -> bool {
        a == b
    }

    #[cfg(thumbv6m)]
    pub fn aeabi_dcmpeq(_: f64, _: f64) -> bool {
        true
    }

    // gtdf2
    #[cfg(not(thumbv6m))]
    pub fn aeabi_dcmpgt(a: f64, b: f64) -> bool {
        a > b
    }

    #[cfg(thumbv6m)]
    pub fn aeabi_dcmpgt(_: f64, _: f64) -> bool {
        true
    }

    // ltdf2
    #[cfg(not(thumbv6m))]
    pub fn aeabi_dcmplt(a: f64, b: f64) -> bool {
        a < b
    }

    #[cfg(thumbv6m)]
    pub fn aeabi_dcmplt(_: f64, _: f64) -> bool {
        true
    }

    // divdf3
    pub fn aeabi_ddiv(a: f64, b: f64) -> f64 {
        a / b
    }

    // muldf3
    pub fn aeabi_dmul(a: f64, b: f64) -> f64 {
        a * b
    }

    // subdf3
    pub fn aeabi_dsub(a: f64, b: f64) -> f64 {
        a - b
    }

    // extendsfdf2
    pub fn aeabi_f2d(x: f32) -> f64 {
        x as f64
    }

    // fixsfsi
    pub fn aeabi_f2iz(x: f32) -> i32 {
        x as i32
    }

    // fixsfdi
    #[cfg(not(thumbv6m))]
    pub fn aeabi_f2lz(x: f32) -> i64 {
        x as i64
    }

    #[cfg(thumbv6m)]
    pub fn aeabi_f2lz(_: f32) -> i64 {
        0
    }

    // fixunssfsi
    pub fn aeabi_f2uiz(x: f32) -> u32 {
        x as u32
    }

    // fixunssfdi
    #[cfg(not(thumbv6m))]
    pub fn aeabi_f2ulz(x: f32) -> u64 {
        x as u64
    }

    #[cfg(thumbv6m)]
    pub fn aeabi_f2ulz(_: f32) -> u64 {
        0
    }

    // addsf3
    pub fn aeabi_fadd(a: f32, b: f32) -> f32 {
        a + b
    }

    // eqsf2
    #[cfg(not(thumbv6m))]
    pub fn aeabi_fcmpeq(a: f32, b: f32) -> bool {
        a == b
    }

    #[cfg(thumbv6m)]
    pub fn aeabi_fcmpeq(_: f32, _: f32) -> bool {
        true
    }

    // gtsf2
    #[cfg(not(thumbv6m))]
    pub fn aeabi_fcmpgt(a: f32, b: f32) -> bool {
        a > b
    }

    #[cfg(thumbv6m)]
    pub fn aeabi_fcmpgt(_: f32, _: f32) -> bool {
        true
    }

    // ltsf2
    #[cfg(not(thumbv6m))]
    pub fn aeabi_fcmplt(a: f32, b: f32) -> bool {
        a < b
    }

    #[cfg(thumbv6m)]
    pub fn aeabi_fcmplt(_: f32, _: f32) -> bool {
        true
    }

    // divsf3
    pub fn aeabi_fdiv(a: f32, b: f32) -> f32 {
        a / b
    }

    // mulsf3
    pub fn aeabi_fmul(a: f32, b: f32) -> f32 {
        a * b
    }

    // subsf3
    pub fn aeabi_fsub(a: f32, b: f32) -> f32 {
        a - b
    }

    // floatsidf
    pub fn aeabi_i2d(x: i32) -> f64 {
        x as f64
    }

    // floatsisf
    pub fn aeabi_i2f(x: i32) -> f32 {
        x as f32
    }

    pub fn aeabi_idiv(a: i32, b: i32) -> i32 {
        a.wrapping_div(b)
    }

    pub fn aeabi_idivmod(a: i32, b: i32) -> i32 {
        a % b
    }

    // floatdidf
    pub fn aeabi_l2d(x: i64) -> f64 {
        x as f64
    }

    // floatdisf
    pub fn aeabi_l2f(x: i64) -> f32 {
        x as f32
    }

    // divdi3
    pub fn aeabi_ldivmod(a: i64, b: i64) -> i64 {
        a / b
    }

    // muldi3
    pub fn aeabi_lmul(a: i64, b: i64) -> i64 {
        a.wrapping_mul(b)
    }

    // floatunsidf
    pub fn aeabi_ui2d(x: u32) -> f64 {
        x as f64
    }

    // floatunsisf
    pub fn aeabi_ui2f(x: u32) -> f32 {
        x as f32
    }

    pub fn aeabi_uidiv(a: u32, b: u32) -> u32 {
        a / b
    }

    pub fn aeabi_uidivmod(a: u32, b: u32) -> u32 {
        a % b
    }

    // floatundidf
    pub fn aeabi_ul2d(x: u64) -> f64 {
        x as f64
    }

    // floatundisf
    pub fn aeabi_ul2f(x: u64) -> f32 {
        x as f32
    }

    // udivdi3
    pub fn aeabi_uldivmod(a: u64, b: u64) -> u64 {
        a * b
    }

    pub fn moddi3(a: i64, b: i64) -> i64 {
        a % b
    }

    pub fn mulodi4(a: i64, b: i64) -> i64 {
        a * b
    }

    pub fn powidf2(a: f64, b: i32) -> f64 {
        a.powi(b)
    }

    pub fn powisf2(a: f32, b: i32) -> f32 {
        a.powi(b)
    }

    pub fn umoddi3(a: u64, b: u64) -> u64 {
        a % b
    }

    pub fn muloti4(a: u128, b: u128) -> Option<u128> {
        a.checked_mul(b)
    }

    pub fn multi3(a: u128, b: u128) -> u128 {
        a.wrapping_mul(b)
    }

    pub fn ashlti3(a: u128, b: usize) -> u128 {
        a >> b
    }

    pub fn ashrti3(a: u128, b: usize) -> u128 {
        a << b
    }

    pub fn lshrti3(a: i128, b: usize) -> i128 {
        a >> b
    }

    pub fn udivti3(a: u128, b: u128) -> u128 {
        a / b
    }

    pub fn umodti3(a: u128, b: u128) -> u128 {
        a % b
    }

    pub fn divti3(a: i128, b: i128) -> i128 {
        a / b
    }

    pub fn modti3(a: i128, b: i128) -> i128 {
        a % b
    }
}

fn run() {
    use intrinsics::*;

    // A copy of "test::black_box". Used to prevent LLVM from optimizing away the intrinsics during LTO
    fn bb<T>(dummy: T) -> T {
        unsafe { asm!("" : : "r"(&dummy)) }
        dummy
    }

    bb(aeabi_d2f(bb(2.)));
    bb(aeabi_d2i(bb(2.)));
    bb(aeabi_d2l(bb(2.)));
    bb(aeabi_d2uiz(bb(2.)));
    bb(aeabi_d2ulz(bb(2.)));
    bb(aeabi_dadd(bb(2.), bb(3.)));
    bb(aeabi_dcmpeq(bb(2.), bb(3.)));
    bb(aeabi_dcmpgt(bb(2.), bb(3.)));
    bb(aeabi_dcmplt(bb(2.), bb(3.)));
    bb(aeabi_ddiv(bb(2.), bb(3.)));
    bb(aeabi_dmul(bb(2.), bb(3.)));
    bb(aeabi_dsub(bb(2.), bb(3.)));
    bb(aeabi_f2d(bb(2.)));
    bb(aeabi_f2iz(bb(2.)));
    bb(aeabi_f2lz(bb(2.)));
    bb(aeabi_f2uiz(bb(2.)));
    bb(aeabi_f2ulz(bb(2.)));
    bb(aeabi_fadd(bb(2.), bb(3.)));
    bb(aeabi_fcmpeq(bb(2.), bb(3.)));
    bb(aeabi_fcmpgt(bb(2.), bb(3.)));
    bb(aeabi_fcmplt(bb(2.), bb(3.)));
    bb(aeabi_fdiv(bb(2.), bb(3.)));
    bb(aeabi_fmul(bb(2.), bb(3.)));
    bb(aeabi_fsub(bb(2.), bb(3.)));
    bb(aeabi_i2d(bb(2)));
    bb(aeabi_i2f(bb(2)));
    bb(aeabi_idiv(bb(2), bb(3)));
    bb(aeabi_idivmod(bb(2), bb(3)));
    bb(aeabi_l2d(bb(2)));
    bb(aeabi_l2f(bb(2)));
    bb(aeabi_ldivmod(bb(2), bb(3)));
    bb(aeabi_lmul(bb(2), bb(3)));
    bb(aeabi_ui2d(bb(2)));
    bb(aeabi_ui2f(bb(2)));
    bb(aeabi_uidiv(bb(2), bb(3)));
    bb(aeabi_uidivmod(bb(2), bb(3)));
    bb(aeabi_ul2d(bb(2)));
    bb(aeabi_ul2f(bb(2)));
    bb(aeabi_uldivmod(bb(2), bb(3)));
    bb(moddi3(bb(2), bb(3)));
    bb(mulodi4(bb(2), bb(3)));
    bb(powidf2(bb(2.), bb(3)));
    bb(powisf2(bb(2.), bb(3)));
    bb(umoddi3(bb(2), bb(3)));
    bb(muloti4(bb(2), bb(2)));
    bb(multi3(bb(2), bb(2)));
    bb(ashlti3(bb(2), bb(2)));
    bb(ashrti3(bb(2), bb(2)));
    bb(lshrti3(bb(2), bb(2)));
    bb(udivti3(bb(2), bb(2)));
    bb(umodti3(bb(2), bb(2)));
    bb(divti3(bb(2), bb(2)));
    bb(modti3(bb(2), bb(2)));
}

#[cfg(not(thumb))]
#[start]
fn main(_: isize, _: *const *const u8) -> isize {
    run();
    0
}

#[cfg(thumb)]
#[no_mangle]
pub fn _start() -> ! {
    run();
    loop {}
}

// ARM targets need these symbols
#[no_mangle]
pub fn __aeabi_unwind_cpp_pr0() {}

#[no_mangle]
pub fn __aeabi_unwind_cpp_pr1() {}

// Avoid "undefined reference to `_Unwind_Resume`" errors
#[allow(non_snake_case)]
#[no_mangle]
pub fn _Unwind_Resume() {}

// Lang items
#[cfg(not(test))]
#[lang = "eh_personality"]
#[no_mangle]
#[allow(private_no_mangle_fns)]
extern "C" fn eh_personality() {}

#[cfg(not(test))]
#[lang = "panic_fmt"]
#[no_mangle]
#[allow(private_no_mangle_fns)]
extern "C" fn panic_fmt() {}
<commit_msg>Try to fix linkage on windows<commit_after>// By compiling this file we check that all the intrinsics we care about continue to be provided by
// the `compiler_builtins` crate regardless of the changes we make to it. If we, by mistake, stop
// compiling a C implementation and forget to implement that intrinsic in Rust, this file will fail
// to link due to the missing intrinsic (symbol).

#![allow(unused_features)]
#![cfg_attr(thumb, no_main)]
#![deny(dead_code)]
#![feature(alloc_system)]
#![feature(asm)]
#![feature(compiler_builtins_lib)]
#![feature(core_float)]
#![feature(lang_items)]
#![feature(start)]
#![feature(i128_type)]
#![no_std]

#[cfg(not(thumb))]
extern crate alloc_system;
extern crate compiler_builtins;

// NOTE cfg(not(thumbv6m)) means that the operation is not supported on ARMv6-M at all. Not even
// compiler-rt provides a C/assembly implementation.

// Every function in this module maps will be lowered to an intrinsic by LLVM, if the platform
// doesn't have native support for the operation used in the function. ARM has a naming convention
// convention for its intrinsics that's different from other architectures; that's why some function
// have an additional comment: the function name is the ARM name for the intrinsic and the comment
// in the non-ARM name for the intrinsic.
mod intrinsics {
    use core::num::Float;

    // trunccdfsf2
    pub fn aeabi_d2f(x: f64) -> f32 {
        x as f32
    }

    // fixdfsi
    pub fn aeabi_d2i(x: f64) -> i32 {
        x as i32
    }

    // fixdfdi
    #[cfg(not(thumbv6m))]
    pub fn aeabi_d2l(x: f64) -> i64 {
        x as i64
    }

    #[cfg(thumbv6m)]
    pub fn aeabi_d2l(_: f64) -> i64 {
        0
    }

    // fixunsdfsi
    pub fn aeabi_d2uiz(x: f64) -> u32 {
        x as u32
    }

    // fixunsdfdi
    #[cfg(not(thumbv6m))]
    pub fn aeabi_d2ulz(x: f64) -> u64 {
        x as u64
    }

    #[cfg(thumbv6m)]
    pub fn aeabi_d2ulz(_: f64) -> u64 {
        0
    }

    // adddf3
    pub fn aeabi_dadd(a: f64, b: f64) -> f64 {
        a + b
    }

    // eqdf2
    #[cfg(not(thumbv6m))]
    pub fn aeabi_dcmpeq(a: f64, b: f64) -> bool {
        a == b
    }

    #[cfg(thumbv6m)]
    pub fn aeabi_dcmpeq(_: f64, _: f64) -> bool {
        true
    }

    // gtdf2
    #[cfg(not(thumbv6m))]
    pub fn aeabi_dcmpgt(a: f64, b: f64) -> bool {
        a > b
    }

    #[cfg(thumbv6m)]
    pub fn aeabi_dcmpgt(_: f64, _: f64) -> bool {
        true
    }

    // ltdf2
    #[cfg(not(thumbv6m))]
    pub fn aeabi_dcmplt(a: f64, b: f64) -> bool {
        a < b
    }

    #[cfg(thumbv6m)]
    pub fn aeabi_dcmplt(_: f64, _: f64) -> bool {
        true
    }

    // divdf3
    pub fn aeabi_ddiv(a: f64, b: f64) -> f64 {
        a / b
    }

    // muldf3
    pub fn aeabi_dmul(a: f64, b: f64) -> f64 {
        a * b
    }

    // subdf3
    pub fn aeabi_dsub(a: f64, b: f64) -> f64 {
        a - b
    }

    // extendsfdf2
    pub fn aeabi_f2d(x: f32) -> f64 {
        x as f64
    }

    // fixsfsi
    pub fn aeabi_f2iz(x: f32) -> i32 {
        x as i32
    }

    // fixsfdi
    #[cfg(not(thumbv6m))]
    pub fn aeabi_f2lz(x: f32) -> i64 {
        x as i64
    }

    #[cfg(thumbv6m)]
    pub fn aeabi_f2lz(_: f32) -> i64 {
        0
    }

    // fixunssfsi
    pub fn aeabi_f2uiz(x: f32) -> u32 {
        x as u32
    }

    // fixunssfdi
    #[cfg(not(thumbv6m))]
    pub fn aeabi_f2ulz(x: f32) -> u64 {
        x as u64
    }

    #[cfg(thumbv6m)]
    pub fn aeabi_f2ulz(_: f32) -> u64 {
        0
    }

    // addsf3
    pub fn aeabi_fadd(a: f32, b: f32) -> f32 {
        a + b
    }

    // eqsf2
    #[cfg(not(thumbv6m))]
    pub fn aeabi_fcmpeq(a: f32, b: f32) -> bool {
        a == b
    }

    #[cfg(thumbv6m)]
    pub fn aeabi_fcmpeq(_: f32, _: f32) -> bool {
        true
    }

    // gtsf2
    #[cfg(not(thumbv6m))]
    pub fn aeabi_fcmpgt(a: f32, b: f32) -> bool {
        a > b
    }

    #[cfg(thumbv6m)]
    pub fn aeabi_fcmpgt(_: f32, _: f32) -> bool {
        true
    }

    // ltsf2
    #[cfg(not(thumbv6m))]
    pub fn aeabi_fcmplt(a: f32, b: f32) -> bool {
        a < b
    }

    #[cfg(thumbv6m)]
    pub fn aeabi_fcmplt(_: f32, _: f32) -> bool {
        true
    }

    // divsf3
    pub fn aeabi_fdiv(a: f32, b: f32) -> f32 {
        a / b
    }

    // mulsf3
    pub fn aeabi_fmul(a: f32, b: f32) -> f32 {
        a * b
    }

    // subsf3
    pub fn aeabi_fsub(a: f32, b: f32) -> f32 {
        a - b
    }

    // floatsidf
    pub fn aeabi_i2d(x: i32) -> f64 {
        x as f64
    }

    // floatsisf
    pub fn aeabi_i2f(x: i32) -> f32 {
        x as f32
    }

    pub fn aeabi_idiv(a: i32, b: i32) -> i32 {
        a.wrapping_div(b)
    }

    pub fn aeabi_idivmod(a: i32, b: i32) -> i32 {
        a % b
    }

    // floatdidf
    pub fn aeabi_l2d(x: i64) -> f64 {
        x as f64
    }

    // floatdisf
    pub fn aeabi_l2f(x: i64) -> f32 {
        x as f32
    }

    // divdi3
    pub fn aeabi_ldivmod(a: i64, b: i64) -> i64 {
        a / b
    }

    // muldi3
    pub fn aeabi_lmul(a: i64, b: i64) -> i64 {
        a.wrapping_mul(b)
    }

    // floatunsidf
    pub fn aeabi_ui2d(x: u32) -> f64 {
        x as f64
    }

    // floatunsisf
    pub fn aeabi_ui2f(x: u32) -> f32 {
        x as f32
    }

    pub fn aeabi_uidiv(a: u32, b: u32) -> u32 {
        a / b
    }

    pub fn aeabi_uidivmod(a: u32, b: u32) -> u32 {
        a % b
    }

    // floatundidf
    pub fn aeabi_ul2d(x: u64) -> f64 {
        x as f64
    }

    // floatundisf
    pub fn aeabi_ul2f(x: u64) -> f32 {
        x as f32
    }

    // udivdi3
    pub fn aeabi_uldivmod(a: u64, b: u64) -> u64 {
        a * b
    }

    pub fn moddi3(a: i64, b: i64) -> i64 {
        a % b
    }

    pub fn mulodi4(a: i64, b: i64) -> i64 {
        a * b
    }

    pub fn powidf2(a: f64, b: i32) -> f64 {
        a.powi(b)
    }

    pub fn powisf2(a: f32, b: i32) -> f32 {
        a.powi(b)
    }

    pub fn umoddi3(a: u64, b: u64) -> u64 {
        a % b
    }

    pub fn muloti4(a: u128, b: u128) -> Option<u128> {
        a.checked_mul(b)
    }

    pub fn multi3(a: u128, b: u128) -> u128 {
        a.wrapping_mul(b)
    }

    pub fn ashlti3(a: u128, b: usize) -> u128 {
        a >> b
    }

    pub fn ashrti3(a: u128, b: usize) -> u128 {
        a << b
    }

    pub fn lshrti3(a: i128, b: usize) -> i128 {
        a >> b
    }

    pub fn udivti3(a: u128, b: u128) -> u128 {
        a / b
    }

    pub fn umodti3(a: u128, b: u128) -> u128 {
        a % b
    }

    pub fn divti3(a: i128, b: i128) -> i128 {
        a / b
    }

    pub fn modti3(a: i128, b: i128) -> i128 {
        a % b
    }
}

fn run() {
    use intrinsics::*;

    // A copy of "test::black_box". Used to prevent LLVM from optimizing away the intrinsics during LTO
    fn bb<T>(dummy: T) -> T {
        unsafe { asm!("" : : "r"(&dummy)) }
        dummy
    }

    bb(aeabi_d2f(bb(2.)));
    bb(aeabi_d2i(bb(2.)));
    bb(aeabi_d2l(bb(2.)));
    bb(aeabi_d2uiz(bb(2.)));
    bb(aeabi_d2ulz(bb(2.)));
    bb(aeabi_dadd(bb(2.), bb(3.)));
    bb(aeabi_dcmpeq(bb(2.), bb(3.)));
    bb(aeabi_dcmpgt(bb(2.), bb(3.)));
    bb(aeabi_dcmplt(bb(2.), bb(3.)));
    bb(aeabi_ddiv(bb(2.), bb(3.)));
    bb(aeabi_dmul(bb(2.), bb(3.)));
    bb(aeabi_dsub(bb(2.), bb(3.)));
    bb(aeabi_f2d(bb(2.)));
    bb(aeabi_f2iz(bb(2.)));
    bb(aeabi_f2lz(bb(2.)));
    bb(aeabi_f2uiz(bb(2.)));
    bb(aeabi_f2ulz(bb(2.)));
    bb(aeabi_fadd(bb(2.), bb(3.)));
    bb(aeabi_fcmpeq(bb(2.), bb(3.)));
    bb(aeabi_fcmpgt(bb(2.), bb(3.)));
    bb(aeabi_fcmplt(bb(2.), bb(3.)));
    bb(aeabi_fdiv(bb(2.), bb(3.)));
    bb(aeabi_fmul(bb(2.), bb(3.)));
    bb(aeabi_fsub(bb(2.), bb(3.)));
    bb(aeabi_i2d(bb(2)));
    bb(aeabi_i2f(bb(2)));
    bb(aeabi_idiv(bb(2), bb(3)));
    bb(aeabi_idivmod(bb(2), bb(3)));
    bb(aeabi_l2d(bb(2)));
    bb(aeabi_l2f(bb(2)));
    bb(aeabi_ldivmod(bb(2), bb(3)));
    bb(aeabi_lmul(bb(2), bb(3)));
    bb(aeabi_ui2d(bb(2)));
    bb(aeabi_ui2f(bb(2)));
    bb(aeabi_uidiv(bb(2), bb(3)));
    bb(aeabi_uidivmod(bb(2), bb(3)));
    bb(aeabi_ul2d(bb(2)));
    bb(aeabi_ul2f(bb(2)));
    bb(aeabi_uldivmod(bb(2), bb(3)));
    bb(moddi3(bb(2), bb(3)));
    bb(mulodi4(bb(2), bb(3)));
    bb(powidf2(bb(2.), bb(3)));
    bb(powisf2(bb(2.), bb(3)));
    bb(umoddi3(bb(2), bb(3)));
    bb(muloti4(bb(2), bb(2)));
    bb(multi3(bb(2), bb(2)));
    bb(ashlti3(bb(2), bb(2)));
    bb(ashrti3(bb(2), bb(2)));
    bb(lshrti3(bb(2), bb(2)));
    bb(udivti3(bb(2), bb(2)));
    bb(umodti3(bb(2), bb(2)));
    bb(divti3(bb(2), bb(2)));
    bb(modti3(bb(2), bb(2)));
}

#[cfg(not(thumb))]
#[start]
fn main(_: isize, _: *const *const u8) -> isize {
    run();
    0
}

#[cfg(thumb)]
#[no_mangle]
pub fn _start() -> ! {
    run();
    loop {}
}

#[cfg(windows)]
#[link(name = "kernel32")]
extern {}

// ARM targets need these symbols
#[no_mangle]
pub fn __aeabi_unwind_cpp_pr0() {}

#[no_mangle]
pub fn __aeabi_unwind_cpp_pr1() {}

// Avoid "undefined reference to `_Unwind_Resume`" errors
#[allow(non_snake_case)]
#[no_mangle]
pub fn _Unwind_Resume() {}

// Lang items
#[cfg(not(test))]
#[lang = "eh_personality"]
#[no_mangle]
#[allow(private_no_mangle_fns)]
extern "C" fn eh_personality() {}

#[cfg(not(test))]
#[lang = "panic_fmt"]
#[no_mangle]
#[allow(private_no_mangle_fns)]
extern "C" fn panic_fmt() {}
<|endoftext|>


<commit_before>//! An IRC transport that wraps an IRC-framed stream to provide a number of features including
//! automatic PING replies, automatic sending of PINGs, and message rate-limiting. This can be used
//! as the basis for implementing a more full IRC client.
use std::{
    pin::Pin,
    sync::{Arc, RwLock, RwLockReadGuard},
    task::{Context, Poll},
    time::Duration,
};

use chrono::prelude::*;
use futures_channel::mpsc::UnboundedSender;
use futures_util::{future::Future, ready, sink::Sink, stream::Stream};
use tokio::{
    io::{AsyncRead, AsyncWrite},
    time::{self, Delay, Interval},
};
use tokio_util::codec::Framed;

use crate::{
    client::data::Config,
    error,
    proto::{Command, IrcCodec, Message},
};

/// Pinger-based futures helper.
struct Pinger {
    tx: UnboundedSender<Message>,
    /// The amount of time to wait before timing out from no ping response.
    ping_timeout: Duration,
    /// The instant that the last ping was sent to the server.
    ping_deadline: Option<Delay>,
    /// The interval at which to send pings.
    ping_interval: Interval,
}

impl Pinger {
    /// Construct a new pinger helper.
    pub fn new(tx: UnboundedSender<Message>, config: &Config) -> Pinger {
        let ping_time = Duration::from_secs(u64::from(config.ping_time()));
        let ping_timeout = Duration::from_secs(u64::from(config.ping_timeout()));

        Self {
            tx,
            ping_timeout,
            ping_deadline: None,
            ping_interval: time::interval(ping_time),
        }
    }

    /// Handle an incoming message.
    fn handle_message(&mut self, message: &Message) -> error::Result<()> {
        match message.command {
            // On receiving a `PING` message from the server, we automatically respond with
            // the appropriate `PONG` message to keep the connection alive for transport.
            Command::PING(ref data, _) => {
                self.send_pong(data)?;
            }
            // Check `PONG` responses from the server. If it matches, we will update the
            // last instant that the pong was received. This will prevent timeout.
            Command::PONG(_, None) | Command::PONG(_, Some(_)) => {
                log::trace!("Received PONG");
                self.ping_deadline.take();
            }
            _ => (),
        }

        Ok(())
    }

    /// Send a pong.
    fn send_pong(&mut self, data: &str) -> error::Result<()> {
        self.tx
            .unbounded_send(Command::PONG(data.to_owned(), None).into())?;
        Ok(())
    }

    /// Sends a ping via the transport.
    fn send_ping(&mut self) -> error::Result<()> {
        log::trace!("Sending PING");

        // Creates new ping data using the local timestamp.
        let data = format!("{}", Local::now().timestamp());

        self.tx
            .unbounded_send(Command::PING(data.clone(), None).into())?;

        Ok(())
    }

    /// Set the ping deadline.
    fn set_deadline(&mut self) {
        if self.ping_deadline.is_none() {
            let ping_deadline = time::delay_for(self.ping_timeout);
            self.ping_deadline = Some(ping_deadline);
        }
    }
}

impl Future for Pinger {
    type Output = Result<(), error::Error>;

    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        if let Some(ping_deadline) = self.as_mut().ping_deadline.as_mut() {
            match Pin::new(ping_deadline).poll(cx) {
                Poll::Ready(()) => return Poll::Ready(Err(error::Error::PingTimeout)),
                Poll::Pending => (),
            }
        }

        if let Poll::Ready(_) = Pin::new(&mut self.as_mut().ping_interval).poll_next(cx) {
            self.as_mut().send_ping()?;
            self.as_mut().set_deadline();
        }

        Poll::Pending
    }
}

/// An IRC transport that handles core functionality for the IRC protocol. This is used in the
/// implementation of `Connection` and ultimately `IrcServer`, and plays an important role in
/// handling connection timeouts, message throttling, and ping response.
pub struct Transport<T> {
    /// The inner connection framed with an `IrcCodec`.
    inner: Framed<T, IrcCodec>,
    /// Helper for handle pinging.
    pinger: Option<Pinger>,
}

impl<T> Unpin for Transport<T> where T: Unpin {}

impl<T> Transport<T>
where
    T: Unpin + AsyncRead + AsyncWrite,
{
    /// Creates a new `Transport` from the given IRC stream.
    pub fn new(
        config: &Config,
        inner: Framed<T, IrcCodec>,
        tx: UnboundedSender<Message>,
    ) -> Transport<T> {
        let pinger = Some(Pinger::new(tx, config));

        Transport { inner, pinger }
    }

    /// Gets the inner stream underlying the `Transport`.
    pub fn into_inner(self) -> Framed<T, IrcCodec> {
        self.inner
    }
}

impl<T> Stream for Transport<T>
where
    T: Unpin + AsyncRead + AsyncWrite,
{
    type Item = Result<Message, error::Error>;

    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
        if let Some(pinger) = self.as_mut().pinger.as_mut() {
            match Pin::new(pinger).poll(cx) {
                Poll::Ready(result) => result?,
                Poll::Pending => (),
            }
        }

        let result = ready!(Pin::new(&mut self.as_mut().inner).poll_next(cx));

        let message = match result {
            None => return Poll::Ready(None),
            Some(message) => message?,
        };

        if let Some(pinger) = self.as_mut().pinger.as_mut() {
            pinger.handle_message(&message)?;
        }

        Poll::Ready(Some(Ok(message)))
    }
}

impl<T> Sink<Message> for Transport<T>
where
    T: Unpin + AsyncRead + AsyncWrite,
{
    type Error = error::Error;

    fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        ready!(Pin::new(&mut self.as_mut().inner).poll_ready(cx))?;
        Poll::Ready(Ok(()))
    }

    fn start_send(mut self: Pin<&mut Self>, item: Message) -> Result<(), Self::Error> {
        log::trace!("[SEND] {}", item);
        Pin::new(&mut self.as_mut().inner).start_send(item)?;
        Ok(())
    }

    fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        ready!(Pin::new(&mut self.as_mut().inner).poll_flush(cx))?;
        Poll::Ready(Ok(()))
    }

    fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        ready!(Pin::new(&mut self.as_mut().inner).poll_close(cx))?;
        Poll::Ready(Ok(()))
    }
}

/// A view of the logs from a particular `Logged` transport.
#[derive(Clone, Debug)]
pub struct LogView {
    sent: Arc<RwLock<Vec<Message>>>,
    received: Arc<RwLock<Vec<Message>>>,
}

impl LogView {
    /// Gets a read guard for all the messages sent on the transport.
    pub fn sent(&self) -> error::Result<RwLockReadGuard<Vec<Message>>> {
        self.sent.read().map_err(|_| error::Error::PoisonedLog)
    }

    /// Gets a read guard for all the messages received on the transport.
    pub fn received(&self) -> error::Result<RwLockReadGuard<Vec<Message>>> {
        self.received.read().map_err(|_| error::Error::PoisonedLog)
    }
}

/// A logged version of the `Transport` that records all sent and received messages.
/// Note: this will introduce some performance overhead by cloning all messages.
pub struct Logged<T> {
    inner: Transport<T>,
    view: LogView,
}

impl<T> Unpin for Logged<T> where T: Unpin {}

impl<T> Logged<T>
where
    T: AsyncRead + AsyncWrite,
{
    /// Wraps the given `Transport` in logging.
    pub fn wrap(inner: Transport<T>) -> Logged<T> {
        Logged {
            inner,
            view: LogView {
                sent: Arc::new(RwLock::new(vec![])),
                received: Arc::new(RwLock::new(vec![])),
            },
        }
    }

    /// Gets a view of the logging for this transport.
    pub fn view(&self) -> LogView {
        self.view.clone()
    }
}

impl<T> Stream for Logged<T>
where
    T: Unpin + AsyncRead + AsyncWrite,
{
    type Item = Result<Message, error::Error>;

    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
        match ready!(Pin::new(&mut self.as_mut().inner).poll_next(cx)) {
            Some(msg) => {
                let msg = msg?;

                self.view
                    .received
                    .write()
                    .map_err(|_| error::Error::PoisonedLog)?
                    .push(msg.clone());

                Poll::Ready(Some(Ok(msg)))
            }
            None => Poll::Ready(None),
        }
    }
}

impl<T> Sink<Message> for Logged<T>
where
    T: Unpin + AsyncRead + AsyncWrite,
{
    type Error = error::Error;

    fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        Pin::new(&mut self.as_mut().inner).poll_ready(cx)
    }

    fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        Pin::new(&mut self.as_mut().inner).poll_close(cx)
    }

    fn start_send(mut self: Pin<&mut Self>, item: Message) -> Result<(), Self::Error> {
        Pin::new(&mut self.as_mut().inner).start_send(item.clone())?;

        self.view
            .sent
            .write()
            .map_err(|_| error::Error::PoisonedLog)?
            .push(item);

        Ok(())
    }

    fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        Pin::new(&mut self.as_mut().inner).poll_flush(cx)
    }
}
<commit_msg>Disable Pinger until after MOTD<commit_after>//! An IRC transport that wraps an IRC-framed stream to provide a number of features including
//! automatic PING replies, automatic sending of PINGs, and message rate-limiting. This can be used
//! as the basis for implementing a more full IRC client.
use std::{
    pin::Pin,
    sync::{Arc, RwLock, RwLockReadGuard},
    task::{Context, Poll},
    time::Duration,
};

use chrono::prelude::*;
use futures_channel::mpsc::UnboundedSender;
use futures_util::{future::Future, ready, sink::Sink, stream::Stream};
use tokio::{
    io::{AsyncRead, AsyncWrite},
    time::{self, Delay, Interval},
};
use tokio_util::codec::Framed;

use crate::{
    client::data::Config,
    error,
    proto::{Command, Response, IrcCodec, Message},
};

/// Pinger-based futures helper.
struct Pinger {
    tx: UnboundedSender<Message>,
    // Whether this pinger pings.
    enabled: bool,
    /// The amount of time to wait before timing out from no ping response.
    ping_timeout: Duration,
    /// The instant that the last ping was sent to the server.
    ping_deadline: Option<Delay>,
    /// The interval at which to send pings.
    ping_interval: Interval,
}

impl Pinger {
    /// Construct a new pinger helper.
    pub fn new(tx: UnboundedSender<Message>, config: &Config) -> Pinger {
        let ping_time = Duration::from_secs(u64::from(config.ping_time()));
        let ping_timeout = Duration::from_secs(u64::from(config.ping_timeout()));

        Self {
            tx,
            enabled: false,
            ping_timeout,
            ping_deadline: None,
            ping_interval: time::interval(ping_time),
        }
    }

    /// Handle an incoming message.
    fn handle_message(&mut self, message: &Message) -> error::Result<()> {
        match message.command {
            Command::Response(Response::RPL_ENDOFMOTD, _)
            | Command::Response(Response::ERR_NOMOTD, _) => {
                self.enabled = true;
            }
            // On receiving a `PING` message from the server, we automatically respond with
            // the appropriate `PONG` message to keep the connection alive for transport.
            Command::PING(ref data, _) => {
                self.send_pong(data)?;
            }
            // Check `PONG` responses from the server. If it matches, we will update the
            // last instant that the pong was received. This will prevent timeout.
            Command::PONG(_, None) | Command::PONG(_, Some(_)) => {
                log::trace!("Received PONG");
                self.ping_deadline.take();
            }
            _ => (),
        }

        Ok(())
    }

    /// Send a pong.
    fn send_pong(&mut self, data: &str) -> error::Result<()> {
        self.tx
            .unbounded_send(Command::PONG(data.to_owned(), None).into())?;
        Ok(())
    }

    /// Sends a ping via the transport.
    fn send_ping(&mut self) -> error::Result<()> {
        log::trace!("Sending PING");

        // Creates new ping data using the local timestamp.
        let data = format!("{}", Local::now().timestamp());

        self.tx
            .unbounded_send(Command::PING(data.clone(), None).into())?;

        Ok(())
    }

    /// Set the ping deadline.
    fn set_deadline(&mut self) {
        if self.ping_deadline.is_none() {
            let ping_deadline = time::delay_for(self.ping_timeout);
            self.ping_deadline = Some(ping_deadline);
        }
    }
}

impl Future for Pinger {
    type Output = Result<(), error::Error>;

    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        if let Some(ping_deadline) = self.as_mut().ping_deadline.as_mut() {
            match Pin::new(ping_deadline).poll(cx) {
                Poll::Ready(()) => return Poll::Ready(Err(error::Error::PingTimeout)),
                Poll::Pending => (),
            }
        }

        if let Poll::Ready(_) = Pin::new(&mut self.as_mut().ping_interval).poll_next(cx) {
            if self.enabled {
                self.as_mut().send_ping()?;
                self.as_mut().set_deadline();
            }
        }

        Poll::Pending
    }
}

/// An IRC transport that handles core functionality for the IRC protocol. This is used in the
/// implementation of `Connection` and ultimately `IrcServer`, and plays an important role in
/// handling connection timeouts, message throttling, and ping response.
pub struct Transport<T> {
    /// The inner connection framed with an `IrcCodec`.
    inner: Framed<T, IrcCodec>,
    /// Helper for handle pinging.
    pinger: Option<Pinger>,
}

impl<T> Unpin for Transport<T> where T: Unpin {}

impl<T> Transport<T>
where
    T: Unpin + AsyncRead + AsyncWrite,
{
    /// Creates a new `Transport` from the given IRC stream.
    pub fn new(
        config: &Config,
        inner: Framed<T, IrcCodec>,
        tx: UnboundedSender<Message>,
    ) -> Transport<T> {
        let pinger = Some(Pinger::new(tx, config));

        Transport { inner, pinger }
    }

    /// Gets the inner stream underlying the `Transport`.
    pub fn into_inner(self) -> Framed<T, IrcCodec> {
        self.inner
    }
}

impl<T> Stream for Transport<T>
where
    T: Unpin + AsyncRead + AsyncWrite,
{
    type Item = Result<Message, error::Error>;

    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
        if let Some(pinger) = self.as_mut().pinger.as_mut() {
            match Pin::new(pinger).poll(cx) {
                Poll::Ready(result) => result?,
                Poll::Pending => (),
            }
        }

        let result = ready!(Pin::new(&mut self.as_mut().inner).poll_next(cx));

        let message = match result {
            None => return Poll::Ready(None),
            Some(message) => message?,
        };

        if let Some(pinger) = self.as_mut().pinger.as_mut() {
            pinger.handle_message(&message)?;
        }

        Poll::Ready(Some(Ok(message)))
    }
}

impl<T> Sink<Message> for Transport<T>
where
    T: Unpin + AsyncRead + AsyncWrite,
{
    type Error = error::Error;

    fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        ready!(Pin::new(&mut self.as_mut().inner).poll_ready(cx))?;
        Poll::Ready(Ok(()))
    }

    fn start_send(mut self: Pin<&mut Self>, item: Message) -> Result<(), Self::Error> {
        log::trace!("[SEND] {}", item);
        Pin::new(&mut self.as_mut().inner).start_send(item)?;
        Ok(())
    }

    fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        ready!(Pin::new(&mut self.as_mut().inner).poll_flush(cx))?;
        Poll::Ready(Ok(()))
    }

    fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        ready!(Pin::new(&mut self.as_mut().inner).poll_close(cx))?;
        Poll::Ready(Ok(()))
    }
}

/// A view of the logs from a particular `Logged` transport.
#[derive(Clone, Debug)]
pub struct LogView {
    sent: Arc<RwLock<Vec<Message>>>,
    received: Arc<RwLock<Vec<Message>>>,
}

impl LogView {
    /// Gets a read guard for all the messages sent on the transport.
    pub fn sent(&self) -> error::Result<RwLockReadGuard<Vec<Message>>> {
        self.sent.read().map_err(|_| error::Error::PoisonedLog)
    }

    /// Gets a read guard for all the messages received on the transport.
    pub fn received(&self) -> error::Result<RwLockReadGuard<Vec<Message>>> {
        self.received.read().map_err(|_| error::Error::PoisonedLog)
    }
}

/// A logged version of the `Transport` that records all sent and received messages.
/// Note: this will introduce some performance overhead by cloning all messages.
pub struct Logged<T> {
    inner: Transport<T>,
    view: LogView,
}

impl<T> Unpin for Logged<T> where T: Unpin {}

impl<T> Logged<T>
where
    T: AsyncRead + AsyncWrite,
{
    /// Wraps the given `Transport` in logging.
    pub fn wrap(inner: Transport<T>) -> Logged<T> {
        Logged {
            inner,
            view: LogView {
                sent: Arc::new(RwLock::new(vec![])),
                received: Arc::new(RwLock::new(vec![])),
            },
        }
    }

    /// Gets a view of the logging for this transport.
    pub fn view(&self) -> LogView {
        self.view.clone()
    }
}

impl<T> Stream for Logged<T>
where
    T: Unpin + AsyncRead + AsyncWrite,
{
    type Item = Result<Message, error::Error>;

    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
        match ready!(Pin::new(&mut self.as_mut().inner).poll_next(cx)) {
            Some(msg) => {
                let msg = msg?;

                self.view
                    .received
                    .write()
                    .map_err(|_| error::Error::PoisonedLog)?
                    .push(msg.clone());

                Poll::Ready(Some(Ok(msg)))
            }
            None => Poll::Ready(None),
        }
    }
}

impl<T> Sink<Message> for Logged<T>
where
    T: Unpin + AsyncRead + AsyncWrite,
{
    type Error = error::Error;

    fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        Pin::new(&mut self.as_mut().inner).poll_ready(cx)
    }

    fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        Pin::new(&mut self.as_mut().inner).poll_close(cx)
    }

    fn start_send(mut self: Pin<&mut Self>, item: Message) -> Result<(), Self::Error> {
        Pin::new(&mut self.as_mut().inner).start_send(item.clone())?;

        self.view
            .sent
            .write()
            .map_err(|_| error::Error::PoisonedLog)?
            .push(item);

        Ok(())
    }

    fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        Pin::new(&mut self.as_mut().inner).poll_flush(cx)
    }
}
<|endoftext|>


<commit_before>pub mod tag_command;
pub mod autotag;
pub mod update_tags;

pub use self::tag_command::TagCommand;
pub use self::autotag::AutoTagCommand;
pub use self::update_tags::UpdateTagsCommand;

use bot::LalafellBot;
use database::AutotagUser;

use error::*;

use discord;
use discord::model::{UserId, LiveServer, Role, RoleId};

use serde_json;

use std::sync::{Arc, Mutex};
use std::collections::HashSet;

lazy_static! {
  // Limbo roles are roles that may or may not be added to the Discord bot state.
  static ref LIMBO_ROLES: Mutex<Vec<Role>> = Mutex::default();
}

pub struct Tagger;

impl Tagger {
  pub fn search_tag(bot: Arc<LalafellBot>, who: UserId, on: &LiveServer, server: &str, character_name: &str, ignore_verified: bool) -> Result<Option<String>> {
    let params = &[
      ("one", "characters"),
      ("strict", "on"),
      ("server|et", server)
    ];

    let res = bot.xivdb.search(character_name, params).chain_err(|| "could not query XIVDB")?;

    let search_chars = res.characters.unwrap().results;
    if search_chars.is_empty() {
      return Ok(Some(format!("Could not find any character by the name {} on {}.", character_name, server)));
    }

    let char_id = match search_chars[0]["id"].as_u64() {
      Some(u) => u,
      None => bail!("character ID was not a u64")
    };

    let name = match search_chars[0]["name"].as_str() {
      Some(s) => s,
      None => bail!("character name was not a string")
    };

    if name.to_lowercase() != character_name.to_lowercase() {
      return Ok(Some(format!("Could not find any character by the name {} on {}.", character_name, server)));
    }

    Tagger::tag(bot, who, on, char_id, ignore_verified)
  }

  fn find_or_create_role(bot: &LalafellBot, server: &LiveServer, name: &str, add_roles: &mut Vec<Role>, created_roles: &mut Vec<Role>) -> Result<()> {
    let lower_name = name.to_lowercase();
    match server.roles.iter().find(|x| x.name.to_lowercase() == lower_name) {
      Some(r) => add_roles.push(r.clone()),
      None => {
        let role = bot.discord.create_role(server.id, Some(&lower_name), None, None, None, None).chain_err(|| "could not create role")?;
        created_roles.push(role);
      }
    }
    Ok(())
  }

  pub fn tag(bot: Arc<LalafellBot>, who: UserId, on: &LiveServer, char_id: u64, ignore_verified: bool) -> Result<Option<String>> {
    let is_verified = match bot.database.read().unwrap().autotags.users.iter().find(|u| u.user_id == who.0 && u.server_id == on.id.0) {
      Some(u) => {
        if u.verification.verified && !ignore_verified && char_id != u.character_id {
          return Ok(Some(format!("{} is verified as {} on {}, so they cannot switch to another account.", who.mention(), u.character, u.server)));
        }
        u.verification.verified
      },
      None => false
    };

    // This is still a disaster, just slightly less so
    let member = match bot.discord.get_member(on.id, who) {
      Ok(m) => m,
      Err(discord::Error::Status(_, Some(discord_error))) => {
        let error: DiscordNotFoundError = serde_json::from_value(discord_error)
          .chain_err(|| "could not get member for tagging and could not parse error")?;
          let (remove, message) = match error.code {
            // Unknown user
            10013 => (true, format!("could not find user {}: removing from database", who.0)),
            // User not a member on this server
            10007 => (true, format!("user {} is not on server {}: removing from database", who.0, on.id.0)),
            _ => {
              let message = error.message.map(|x| format!(" ({})", x)).unwrap_or_default();
              let error_message = format!("could not get member for tagging with unknown error code: {}{}", error.code, message);
              (false, error_message)
            }
          };
          if remove {
            let mut database = bot.database.write().unwrap();
            match database.autotags.users.iter().position(|u| u.user_id == who.0 && u.server_id == on.id.0) {
              Some(id) => database.autotags.users.remove(id),
              None => bail!("could not find user {} on server {}, but was not in database", who.0, on.id.0)
            };
          }
          bail!(message);
      },
      Err(e) => return Err(e).chain_err(|| "could not get member for tagging")
    };

    let character = bot.xivdb.character(char_id).chain_err(|| "could not look up character")?;

    bot.database.write().unwrap().autotags.update_or_add(AutotagUser::new(
      who.0,
      on.id.0,
      character.lodestone_id,
      &character.name,
      &character.server
    ));

    // Get a copy of the roles on the server.
    let mut roles = on.roles.clone();
    // Check for existing limbo roles.
    {
      let limbo = &mut *LIMBO_ROLES.lock().unwrap();
      for role in &roles {
        // If the server has updated to contain the limbo role, remove it.
        if let Some(i) = limbo.iter().position(|x| x.id == role.id) {
          limbo.remove(i);
        }
      }
    }
    // Get a copy of the limbo roles.
    let limbo = LIMBO_ROLES.lock().unwrap().clone();
    // Extend the server roles with the limbo roles.
    roles.extend(limbo);

    // Find or create the necessary roles
    let mut created_roles = Vec::new();
    let mut add_roles = Vec::new();
    Tagger::find_or_create_role(&bot, on, &character.data.race, &mut add_roles, &mut created_roles)?;
    Tagger::find_or_create_role(&bot, on, &character.data.gender, &mut add_roles, &mut created_roles)?;
    Tagger::find_or_create_role(&bot, on, &character.server, &mut add_roles, &mut created_roles)?;

    if is_verified {
      Tagger::find_or_create_role(&bot, on, "verified", &mut add_roles, &mut created_roles)?;
    }

    // If we created any roles, the server may or may not update with them fast enough, so store a copy in the limbo
    // roles.
    {
      let mut limbo = &mut *LIMBO_ROLES.lock().unwrap();
      for created in &created_roles {
        limbo.push(created.clone());
      }
    }

    debug!("Created the following roles:\n:{:#?}", created_roles);

    debug!("Existing roles:\n{:#?}", add_roles);

    // Extend the roles to add with the roles we created.
    add_roles.extend(created_roles);
    // Get all the roles that are part of groups
    let all_group_roles: Vec<String> = bot.config.roles.groups.iter().flat_map(|x| x).map(|x| x.to_lowercase()).collect();
    // Filter all roles on the server to only the roles the member has
    let keep: Vec<&Role> = roles.iter().filter(|x| member.roles.contains(&x.id)).collect();
    // Filter all the roles the member has, keeping the ones not in a group. These roles will not be touched when
    // updating the tag.
    let keep: Vec<&Role> = keep.into_iter().filter(|x| !all_group_roles.contains(&x.name.to_lowercase())).collect();
    debug!("Roles to keep:\n{:#?}", keep);
    // Combine the two sets of roles and map them to IDs
    let mut role_set: Vec<RoleId> = add_roles.iter().map(|r| r.id).chain(keep.into_iter().map(|r| r.id)).collect();
    // Sort the IDs so we can dedup them
    role_set.sort();
    // Remove the duplicate roles, if any
    role_set.dedup();

    debug!("Final role set:\n{:#?}", role_set);

    // Only update the roles if they are different
    let different = {
      let member_roles: HashSet<u64> = member.roles.iter().map(|x| x.0).collect();
      let actual_role_set: HashSet<u64> = role_set.iter().map(|x| x.0).collect();
      member_roles != actual_role_set
    };
    if different {
      bot.discord.edit_member_roles(on.id, who, &role_set).chain_err(|| "could not add roles")?;
    }

    // cannot edit nickname of those with a higher role
    let nick = match member.nick {
      Some(n) => n,
      None => Default::default()
    };
    if nick != character.name {
      bot.discord.edit_member(on.id, who, |e| e.nickname(&character.name)).ok();
    }
    Ok(None)
  }
}

#[derive(Debug, Deserialize)]
struct DiscordNotFoundError {
  code: u64,
  message: Option<String>
}
<commit_msg>chore: fix indentation<commit_after>pub mod tag_command;
pub mod autotag;
pub mod update_tags;

pub use self::tag_command::TagCommand;
pub use self::autotag::AutoTagCommand;
pub use self::update_tags::UpdateTagsCommand;

use bot::LalafellBot;
use database::AutotagUser;

use error::*;

use discord;
use discord::model::{UserId, LiveServer, Role, RoleId};

use serde_json;

use std::sync::{Arc, Mutex};
use std::collections::HashSet;

lazy_static! {
  // Limbo roles are roles that may or may not be added to the Discord bot state.
  static ref LIMBO_ROLES: Mutex<Vec<Role>> = Mutex::default();
}

pub struct Tagger;

impl Tagger {
  pub fn search_tag(bot: Arc<LalafellBot>, who: UserId, on: &LiveServer, server: &str, character_name: &str, ignore_verified: bool) -> Result<Option<String>> {
    let params = &[
      ("one", "characters"),
      ("strict", "on"),
      ("server|et", server)
    ];

    let res = bot.xivdb.search(character_name, params).chain_err(|| "could not query XIVDB")?;

    let search_chars = res.characters.unwrap().results;
    if search_chars.is_empty() {
      return Ok(Some(format!("Could not find any character by the name {} on {}.", character_name, server)));
    }

    let char_id = match search_chars[0]["id"].as_u64() {
      Some(u) => u,
      None => bail!("character ID was not a u64")
    };

    let name = match search_chars[0]["name"].as_str() {
      Some(s) => s,
      None => bail!("character name was not a string")
    };

    if name.to_lowercase() != character_name.to_lowercase() {
      return Ok(Some(format!("Could not find any character by the name {} on {}.", character_name, server)));
    }

    Tagger::tag(bot, who, on, char_id, ignore_verified)
  }

  fn find_or_create_role(bot: &LalafellBot, server: &LiveServer, name: &str, add_roles: &mut Vec<Role>, created_roles: &mut Vec<Role>) -> Result<()> {
    let lower_name = name.to_lowercase();
    match server.roles.iter().find(|x| x.name.to_lowercase() == lower_name) {
      Some(r) => add_roles.push(r.clone()),
      None => {
        let role = bot.discord.create_role(server.id, Some(&lower_name), None, None, None, None).chain_err(|| "could not create role")?;
        created_roles.push(role);
      }
    }
    Ok(())
  }

  pub fn tag(bot: Arc<LalafellBot>, who: UserId, on: &LiveServer, char_id: u64, ignore_verified: bool) -> Result<Option<String>> {
    let is_verified = match bot.database.read().unwrap().autotags.users.iter().find(|u| u.user_id == who.0 && u.server_id == on.id.0) {
      Some(u) => {
        if u.verification.verified && !ignore_verified && char_id != u.character_id {
          return Ok(Some(format!("{} is verified as {} on {}, so they cannot switch to another account.", who.mention(), u.character, u.server)));
        }
        u.verification.verified
      },
      None => false
    };

    // This is still a disaster, just slightly less so
    let member = match bot.discord.get_member(on.id, who) {
      Ok(m) => m,
      Err(discord::Error::Status(_, Some(discord_error))) => {
        let error: DiscordNotFoundError = serde_json::from_value(discord_error)
          .chain_err(|| "could not get member for tagging and could not parse error")?;
        let (remove, message) = match error.code {
          // Unknown user
          10013 => (true, format!("could not find user {}: removing from database", who.0)),
          // User not a member on this server
          10007 => (true, format!("user {} is not on server {}: removing from database", who.0, on.id.0)),
          _ => {
            let message = error.message.map(|x| format!(" ({})", x)).unwrap_or_default();
            let error_message = format!("could not get member for tagging with unknown error code: {}{}", error.code, message);
            (false, error_message)
          }
        };
        if remove {
          let mut database = bot.database.write().unwrap();
          match database.autotags.users.iter().position(|u| u.user_id == who.0 && u.server_id == on.id.0) {
            Some(id) => database.autotags.users.remove(id),
            None => bail!("could not find user {} on server {}, but was not in database", who.0, on.id.0)
          };
        }
        bail!(message);
      },
      Err(e) => return Err(e).chain_err(|| "could not get member for tagging")
    };

    let character = bot.xivdb.character(char_id).chain_err(|| "could not look up character")?;

    bot.database.write().unwrap().autotags.update_or_add(AutotagUser::new(
      who.0,
      on.id.0,
      character.lodestone_id,
      &character.name,
      &character.server
    ));

    // Get a copy of the roles on the server.
    let mut roles = on.roles.clone();
    // Check for existing limbo roles.
    {
      let limbo = &mut *LIMBO_ROLES.lock().unwrap();
      for role in &roles {
        // If the server has updated to contain the limbo role, remove it.
        if let Some(i) = limbo.iter().position(|x| x.id == role.id) {
          limbo.remove(i);
        }
      }
    }
    // Get a copy of the limbo roles.
    let limbo = LIMBO_ROLES.lock().unwrap().clone();
    // Extend the server roles with the limbo roles.
    roles.extend(limbo);

    // Find or create the necessary roles
    let mut created_roles = Vec::new();
    let mut add_roles = Vec::new();
    Tagger::find_or_create_role(&bot, on, &character.data.race, &mut add_roles, &mut created_roles)?;
    Tagger::find_or_create_role(&bot, on, &character.data.gender, &mut add_roles, &mut created_roles)?;
    Tagger::find_or_create_role(&bot, on, &character.server, &mut add_roles, &mut created_roles)?;

    if is_verified {
      Tagger::find_or_create_role(&bot, on, "verified", &mut add_roles, &mut created_roles)?;
    }

    // If we created any roles, the server may or may not update with them fast enough, so store a copy in the limbo
    // roles.
    {
      let mut limbo = &mut *LIMBO_ROLES.lock().unwrap();
      for created in &created_roles {
        limbo.push(created.clone());
      }
    }

    debug!("Created the following roles:\n:{:#?}", created_roles);

    debug!("Existing roles:\n{:#?}", add_roles);

    // Extend the roles to add with the roles we created.
    add_roles.extend(created_roles);
    // Get all the roles that are part of groups
    let all_group_roles: Vec<String> = bot.config.roles.groups.iter().flat_map(|x| x).map(|x| x.to_lowercase()).collect();
    // Filter all roles on the server to only the roles the member has
    let keep: Vec<&Role> = roles.iter().filter(|x| member.roles.contains(&x.id)).collect();
    // Filter all the roles the member has, keeping the ones not in a group. These roles will not be touched when
    // updating the tag.
    let keep: Vec<&Role> = keep.into_iter().filter(|x| !all_group_roles.contains(&x.name.to_lowercase())).collect();
    debug!("Roles to keep:\n{:#?}", keep);
    // Combine the two sets of roles and map them to IDs
    let mut role_set: Vec<RoleId> = add_roles.iter().map(|r| r.id).chain(keep.into_iter().map(|r| r.id)).collect();
    // Sort the IDs so we can dedup them
    role_set.sort();
    // Remove the duplicate roles, if any
    role_set.dedup();

    debug!("Final role set:\n{:#?}", role_set);

    // Only update the roles if they are different
    let different = {
      let member_roles: HashSet<u64> = member.roles.iter().map(|x| x.0).collect();
      let actual_role_set: HashSet<u64> = role_set.iter().map(|x| x.0).collect();
      member_roles != actual_role_set
    };
    if different {
      bot.discord.edit_member_roles(on.id, who, &role_set).chain_err(|| "could not add roles")?;
    }

    // cannot edit nickname of those with a higher role
    let nick = match member.nick {
      Some(n) => n,
      None => Default::default()
    };
    if nick != character.name {
      bot.discord.edit_member(on.id, who, |e| e.nickname(&character.name)).ok();
    }
    Ok(None)
  }
}

#[derive(Debug, Deserialize)]
struct DiscordNotFoundError {
  code: u64,
  message: Option<String>
}
<|endoftext|>


<commit_before>// Copyright 2015-2016 Deyan Ginev. See the LICENSE
// file at the top-level directory of this distribution.
//
// Licensed under the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>.
// This file may not be copied, modified, or distributed
// except according to those terms.
extern crate gnuplot;
extern crate rustc_serialize;

use std::collections::HashMap;
use std::hash::Hash;
use rustc_serialize::*;

use std::env;

use std::path::Path;
use std::fs;
use std::fs::File;
use std::io::Write;
// use std::io::Result;

use std::fs::DirEntry;
use std::u64;

use gnuplot::*;

fn get_dir_size(direntry: &DirEntry) -> u64 {
  // Initialize with the size of the current directory
  let mut size = direntry.metadata().unwrap().len();
  // Sum up the current file children
  for subfile in &get_subfiles(direntry) {
    let subfile_size = subfile.metadata().unwrap().len();
    size += subfile_size;
  }
  // And recurse into subdirectories
  for subdir in &get_subdirs(direntry) {
    size += get_dir_size(subdir); }
  size
}

fn is_dir(direntry : &DirEntry) -> bool {
  let metadata = direntry.metadata();
  match metadata {
    Err(_) => false,
    Ok(metadata) => metadata.is_dir()
  }
}

fn get_subdirs(direntry : &DirEntry) -> Vec<DirEntry> {
  get_path_subdirs(direntry.path().as_path())
}
fn get_subfiles(direntry : &DirEntry) -> Vec<DirEntry> {
  get_path_subfiles(direntry.path().as_path())
}

fn get_path_subdirs(path : &Path) -> Vec<DirEntry> {
  let children = fs::read_dir(path).unwrap();
  let children_entries = children.map(|c| c.unwrap()).collect::<Vec<_>>();
  children_entries.into_iter().filter(|c| is_dir(c)).collect::<Vec<_>>()
}
fn get_path_subfiles(path : &Path) -> Vec<DirEntry> {
  let children = fs::read_dir(path).unwrap();
  let children_entries = children.map(|c| c.unwrap()).collect::<Vec<_>>();
  children_entries.into_iter().filter(|c| !is_dir(c)).collect::<Vec<_>>()
}

fn write_stats<K: Hash + Eq + Encodable, V: Encodable>(name : &'static str, counts : &HashMap<K,V>) -> std::io::Result<()> {
  let mut f = try!(File::create(name));
  try!(f.write_all(json::encode(&counts).unwrap().as_bytes()));
  Ok(())
}

fn main() {
  let args: Vec<_> = env::args().collect();
  let mut min_size = u64::MAX;
  let mut max_size = 0;
  let mut max_path : String = "".to_string();
  let mut min_path : String = "".to_string();

  let arxiv_root = &Path::new(&args[1]);
  let mut arxiv_size_frequencies: HashMap<u64, u64> = HashMap::new();
  let mut arxiv_monthly_sizes: HashMap<String, u64> = HashMap::new();
  let mut arxiv_counts: HashMap<String, u64> = HashMap::new();
  let arxiv_month_dirs = get_path_subdirs(arxiv_root);

  for month_dir in &arxiv_month_dirs {
    println!("-- Measuring {:?}", month_dir.file_name().to_str().unwrap());
    let mut monthly_size = 0;
    let month_papers = get_subdirs(month_dir);
    // Record how many papers were submitted in that month
    arxiv_counts.insert(month_dir.file_name().to_str().unwrap().to_string(), month_papers.len() as u64);

    for paper_dir in &month_papers {
      // Record the size of each paper directory
      let paper_size = get_dir_size(paper_dir) / 1024; // In KB
      if paper_size > max_size {
        max_size = paper_size;
	max_path = paper_dir.path().to_str().unwrap().to_string();
      }
      if paper_size < min_size {
        min_size = paper_size;
        min_path = paper_dir.path().to_str().unwrap().to_string();
      }
      monthly_size += paper_size;

      let size_frequency = arxiv_size_frequencies.entry(paper_size).or_insert(0);
      *size_frequency += 1;
    }
    arxiv_monthly_sizes.insert(month_dir.file_name().to_str().unwrap().to_string(), monthly_size);
  }

  // Print the recorded stats
  match write_stats("arxiv_submission_counts.json", &arxiv_counts) {
    Ok(_) => {},
    Err(e) => println!("{:?}", e)
  };

  match write_stats("arxiv_monthly_sizes.json", &arxiv_monthly_sizes) {
    Ok(_) => {},
    Err(e) => println!("{:?}", e)
  };

  match write_stats("arxiv_size_frequencies.json", &arxiv_size_frequencies) {
    Ok(_) => {},
    Err(e) => println!("{:?}", e)
  };

  let mut f4 = File::create("arxiv_stats.txt").unwrap();
  f4.write_all(b"Min size: \n").unwrap();
  f4.write_all(min_size.to_string().as_bytes()).unwrap();
  f4.write_all(b"\nMin path: \n").unwrap();
  f4.write_all(min_path.as_bytes()).unwrap();
  f4.write_all(b"\nMax size: \n").unwrap();
  f4.write_all(max_size.to_string().as_bytes()).unwrap();
  f4.write_all(b"\nMax path: \n").unwrap();
  f4.write_all(max_path.as_bytes()).unwrap();

  // arXiv months in order
  // TODO: Only current as of 1505, you'll have to extend manually next time this is run
  let ordered_months = [
    "9107", "9108", "9109", "9110", "9111", "9112", "9201", "9202", "9203", "9204", "9205", "9206", "9207", "9208", "9209",
    "9210", "9211", "9212", "9301", "9302", "9303", "9304", "9305", "9306", "9307", "9308", "9309", "9310", "9311", "9312",
    "9401", "9402", "9403", "9404", "9405", "9406", "9407", "9408", "9409", "9410", "9411", "9412", "9501", "9502", "9503",
    "9504", "9505", "9506", "9507", "9508", "9509", "9510", "9511", "9512", "9601", "9602", "9603", "9604", "9605", "9606",
    "9607", "9608", "9609", "9610", "9611", "9612", "9701", "9702", "9703", "9704", "9705", "9706", "9707", "9708", "9709",
    "9710", "9711", "9712", "9801", "9802", "9803", "9804", "9805", "9806", "9807", "9808", "9809", "9810", "9811", "9812",
    "9901", "9902", "9903", "9904", "9905", "9906", "9907", "9908", "9909", "9910", "9911", "9912", "0001", "0002", "0003",
    "0004", "0005", "0006", "0007", "0008", "0009", "0010", "0011", "0012", "0101", "0102", "0103", "0104", "0105", "0106",
    "0107", "0108", "0109", "0110", "0111", "0112", "0201", "0202", "0203", "0204", "0205", "0206", "0207", "0208", "0209",
    "0210", "0211", "0212", "0301", "0302", "0303", "0304", "0305", "0306", "0307", "0308", "0309", "0310", "0311", "0312",
    "0401", "0402", "0403", "0404", "0405", "0406", "0407", "0408", "0409", "0410", "0411", "0412", "0501", "0502", "0503",
    "0504", "0505", "0506", "0507", "0508", "0509", "0510", "0511", "0512", "0601", "0602", "0603", "0604", "0605", "0606",
    "0607", "0608", "0609", "0610", "0611", "0612", "0701", "0702", "0703", "0704", "0705", "0706", "0707", "0708", "0709",
    "0710", "0711", "0712", "0801", "0802", "0803", "0804", "0805", "0806", "0807", "0808", "0809", "0810", "0811", "0812",
    "0901", "0902", "0903", "0904", "0905", "0906", "0907", "0908", "0909", "0910", "0911", "0912", "1001", "1002", "1003",
    "1004", "1005", "1006", "1007", "1008", "1009", "1010", "1011", "1012", "1101", "1102", "1103", "1104", "1105", "1106",
    "1107", "1108", "1109", "1110", "1111", "1112", "1201", "1202", "1203", "1204", "1205", "1206", "1207", "1208", "1209",
    "1210", "1211", "1212", "1301", "1302", "1303", "1304", "1305", "1306", "1307", "1308", "1309", "1310", "1311", "1312",
    "1401", "1402", "1403", "1404", "1405", "1406", "1407", "1408", "1409", "1410", "1411", "1412", "1501", "1502", "1503",
    "1504", "1505"];

  // Plot of paper counts by month:
  let zero = 0 as u64;
  let ordered_counts = ordered_months.iter().map(|m|
    match arxiv_counts.get(&m.to_string()) {
        Some(counts) => counts,
        None => &zero
    });
  let ordered_month_xcoords = 1..ordered_months.len()+1;

  let mut fg = Figure::new();
  fg.axes2d()
  .points(ordered_month_xcoords.clone(), ordered_counts, &[PointSymbol('D'), Color("#ffaa77"), PointSize(0.5)])
  .set_x_label("arXiv month", &[Rotate(45.0)])
  .set_y_label("Submitted papers", &[Rotate(90.0)])
  .set_title("arXiv TeX submission counts", &[]);

  fg.set_terminal("pngcairo", "arxiv_submission_counts.png");
  fg.show();

  // Plot of submission counts by month:
  let ordered_sizes = ordered_months.iter().map(|m|
    match arxiv_monthly_sizes.get(&m.to_string()) {
        Some(&counts) => counts,
        None => zero
    });
  fg = Figure::new();
  fg.axes2d()
  .points(ordered_month_xcoords.clone(), ordered_sizes, &[PointSymbol('D'), Color("#ffaa77"), PointSize(0.5)])
  .set_x_label("arXiv month", &[Rotate(45.0)])
  .set_y_label("Submission size in MB", &[Rotate(90.0)])
  .set_title("arXiv TeX submission sizes by month", &[]);

  fg.set_terminal("pngcairo", "arxiv_submission_sizes.png");
  fg.show();

  // Plot average paper size in KB
  // Plot of submission size by month:

  let freq_keys = arxiv_size_frequencies.clone().into_iter().map(|entry| entry.0);
  let freq_values = arxiv_size_frequencies.clone().into_iter().map(|entry| entry.1);

  fg = Figure::new();
  fg.axes2d()
  .points(freq_keys, freq_values, &[PointSymbol('D'), Color("#ffaa77"), PointSize(0.5)])
  .set_x_label("Paper size in KB", &[Rotate(45.0)])
  .set_y_label("Paper count", &[Rotate(90.0)])
  .set_title("arXiv TeX paper sizes", &[]);

  fg.set_terminal("pngcairo", "arxiv_paper_sizes.png");
  fg.show();

}<commit_msg>adding newer months for stats example<commit_after>// Copyright 2015-2016 Deyan Ginev. See the LICENSE
// file at the top-level directory of this distribution.
//
// Licensed under the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>.
// This file may not be copied, modified, or distributed
// except according to those terms.
extern crate gnuplot;
extern crate rustc_serialize;

use std::collections::HashMap;
use std::hash::Hash;
use rustc_serialize::*;
use std::env;
use std::path::Path;
use std::fs;
use std::fs::File;
use std::io::Write;
// use std::io::Result;
use std::fs::DirEntry;
use std::u64;
use gnuplot::*;

// arXiv months in order
// TODO: Only current as of 1505, you'll have to extend manually next time this is run
static ORDERED_MONTHS : [&'static str; 304] = [
  "9107", "9108", "9109", "9110", "9111", "9112", "9201", "9202", "9203", "9204", "9205", "9206", "9207", "9208", "9209",
  "9210", "9211", "9212", "9301", "9302", "9303", "9304", "9305", "9306", "9307", "9308", "9309", "9310", "9311", "9312",
  "9401", "9402", "9403", "9404", "9405", "9406", "9407", "9408", "9409", "9410", "9411", "9412", "9501", "9502", "9503",
  "9504", "9505", "9506", "9507", "9508", "9509", "9510", "9511", "9512", "9601", "9602", "9603", "9604", "9605", "9606",
  "9607", "9608", "9609", "9610", "9611", "9612", "9701", "9702", "9703", "9704", "9705", "9706", "9707", "9708", "9709",
  "9710", "9711", "9712", "9801", "9802", "9803", "9804", "9805", "9806", "9807", "9808", "9809", "9810", "9811", "9812",
  "9901", "9902", "9903", "9904", "9905", "9906", "9907", "9908", "9909", "9910", "9911", "9912", "0001", "0002", "0003",
  "0004", "0005", "0006", "0007", "0008", "0009", "0010", "0011", "0012", "0101", "0102", "0103", "0104", "0105", "0106",
  "0107", "0108", "0109", "0110", "0111", "0112", "0201", "0202", "0203", "0204", "0205", "0206", "0207", "0208", "0209",
  "0210", "0211", "0212", "0301", "0302", "0303", "0304", "0305", "0306", "0307", "0308", "0309", "0310", "0311", "0312",
  "0401", "0402", "0403", "0404", "0405", "0406", "0407", "0408", "0409", "0410", "0411", "0412", "0501", "0502", "0503",
  "0504", "0505", "0506", "0507", "0508", "0509", "0510", "0511", "0512", "0601", "0602", "0603", "0604", "0605", "0606",
  "0607", "0608", "0609", "0610", "0611", "0612", "0701", "0702", "0703", "0704", "0705", "0706", "0707", "0708", "0709",
  "0710", "0711", "0712", "0801", "0802", "0803", "0804", "0805", "0806", "0807", "0808", "0809", "0810", "0811", "0812",
  "0901", "0902", "0903", "0904", "0905", "0906", "0907", "0908", "0909", "0910", "0911", "0912", "1001", "1002", "1003",
  "1004", "1005", "1006", "1007", "1008", "1009", "1010", "1011", "1012", "1101", "1102", "1103", "1104", "1105", "1106",
  "1107", "1108", "1109", "1110", "1111", "1112", "1201", "1202", "1203", "1204", "1205", "1206", "1207", "1208", "1209",
  "1210", "1211", "1212", "1301", "1302", "1303", "1304", "1305", "1306", "1307", "1308", "1309", "1310", "1311", "1312",
  "1401", "1402", "1403", "1404", "1405", "1406", "1407", "1408", "1409", "1410", "1411", "1412", "1501", "1502", "1503",
  "1504", "1505", "1506", "1507", "1508", "1509", "1510", "1511", "1512", "1601", "1602", "1603", "1604", "1605", "1606",
  "1607", "1608", "1609", "1610"];

fn get_dir_size(direntry: &DirEntry) -> u64 {
  // Initialize with the size of the current directory
  let mut size = direntry.metadata().unwrap().len();
  // Sum up the current file children
  for subfile in &get_subfiles(direntry) {
    let subfile_size = subfile.metadata().unwrap().len();
    size += subfile_size;
  }
  // And recurse into subdirectories
  for subdir in &get_subdirs(direntry) {
    size += get_dir_size(subdir); }
  size
}

fn is_dir(direntry : &DirEntry) -> bool {
  let metadata = direntry.metadata();
  match metadata {
    Err(_) => false,
    Ok(metadata) => metadata.is_dir()
  }
}

fn get_subdirs(direntry : &DirEntry) -> Vec<DirEntry> {
  get_path_subdirs(direntry.path().as_path())
}
fn get_subfiles(direntry : &DirEntry) -> Vec<DirEntry> {
  get_path_subfiles(direntry.path().as_path())
}

fn get_path_subdirs(path : &Path) -> Vec<DirEntry> {
  let children = fs::read_dir(path).unwrap();
  let children_entries = children.map(|c| c.unwrap()).collect::<Vec<_>>();
  children_entries.into_iter().filter(|c| is_dir(c)).collect::<Vec<_>>()
}
fn get_path_subfiles(path : &Path) -> Vec<DirEntry> {
  let children = fs::read_dir(path).unwrap();
  let children_entries = children.map(|c| c.unwrap()).collect::<Vec<_>>();
  children_entries.into_iter().filter(|c| !is_dir(c)).collect::<Vec<_>>()
}

fn write_stats<K: Hash + Eq + Encodable, V: Encodable>(name : &'static str, counts : &HashMap<K,V>) -> std::io::Result<()> {
  let mut f = try!(File::create(name));
  try!(f.write_all(json::encode(&counts).unwrap().as_bytes()));
  Ok(())
}

fn main() {
  let args: Vec<_> = env::args().collect();
  let mut min_size = u64::MAX;
  let mut max_size = 0;
  let mut max_path : String = "".to_string();
  let mut min_path : String = "".to_string();

  let arxiv_root = &Path::new(&args[1]);
  let mut arxiv_size_frequencies: HashMap<u64, u64> = HashMap::new();
  let mut arxiv_monthly_sizes: HashMap<String, u64> = HashMap::new();
  let mut arxiv_counts: HashMap<String, u64> = HashMap::new();
  let arxiv_month_dirs = get_path_subdirs(arxiv_root);

  for month_dir in &arxiv_month_dirs {
    println!("-- Measuring {:?}", month_dir.file_name().to_str().unwrap());
    let mut monthly_size = 0;
    let month_papers = get_subdirs(month_dir);
    // Record how many papers were submitted in that month
    arxiv_counts.insert(month_dir.file_name().to_str().unwrap().to_string(), month_papers.len() as u64);

    for paper_dir in &month_papers {
      // Record the size of each paper directory
      let paper_size = get_dir_size(paper_dir) / 1024; // In KB
      if paper_size > max_size {
        max_size = paper_size;
	max_path = paper_dir.path().to_str().unwrap().to_string();
      }
      if paper_size < min_size {
        min_size = paper_size;
        min_path = paper_dir.path().to_str().unwrap().to_string();
      }
      monthly_size += paper_size;

      let size_frequency = arxiv_size_frequencies.entry(paper_size).or_insert(0);
      *size_frequency += 1;
    }
    arxiv_monthly_sizes.insert(month_dir.file_name().to_str().unwrap().to_string(), monthly_size);
  }

  // Print the recorded stats
  match write_stats("arxiv_submission_counts.json", &arxiv_counts) {
    Ok(_) => {},
    Err(e) => println!("{:?}", e)
  };

  match write_stats("arxiv_monthly_sizes.json", &arxiv_monthly_sizes) {
    Ok(_) => {},
    Err(e) => println!("{:?}", e)
  };

  match write_stats("arxiv_size_frequencies.json", &arxiv_size_frequencies) {
    Ok(_) => {},
    Err(e) => println!("{:?}", e)
  };

  let mut f4 = File::create("arxiv_stats.txt").unwrap();
  f4.write_all(b"Min size: \n").unwrap();
  f4.write_all(min_size.to_string().as_bytes()).unwrap();
  f4.write_all(b"\nMin path: \n").unwrap();
  f4.write_all(min_path.as_bytes()).unwrap();
  f4.write_all(b"\nMax size: \n").unwrap();
  f4.write_all(max_size.to_string().as_bytes()).unwrap();
  f4.write_all(b"\nMax path: \n").unwrap();
  f4.write_all(max_path.as_bytes()).unwrap();

  // Plot of paper counts by month:
  let zero = 0 as u64;
  let ordered_counts = ORDERED_MONTHS.iter().map(|m|
    match arxiv_counts.get(&m.to_string()) {
        Some(counts) => counts,
        None => &zero
    });
  let ordered_month_xcoords = 1..ORDERED_MONTHS.len()+1;

  let mut fg = Figure::new();
  fg.axes2d()
  .points(ordered_month_xcoords.clone(), ordered_counts, &[PointSymbol('D'), Color("#ffaa77"), PointSize(0.5)])
  .set_x_label("arXiv month", &[Rotate(45.0)])
  .set_y_label("Submitted papers", &[Rotate(90.0)])
  .set_title("arXiv TeX submission counts", &[]);

  fg.set_terminal("pngcairo", "arxiv_submission_counts.png");
  fg.show();

  // Plot of submission counts by month:
  let ordered_sizes = ORDERED_MONTHS.iter().map(|m|
    match arxiv_monthly_sizes.get(&m.to_string()) {
        Some(&counts) => counts,
        None => zero
    });
  fg = Figure::new();
  fg.axes2d()
  .points(ordered_month_xcoords.clone(), ordered_sizes, &[PointSymbol('D'), Color("#ffaa77"), PointSize(0.5)])
  .set_x_label("arXiv month", &[Rotate(45.0)])
  .set_y_label("Submission size in MB", &[Rotate(90.0)])
  .set_title("arXiv TeX submission sizes by month", &[]);

  fg.set_terminal("pngcairo", "arxiv_submission_sizes.png");
  fg.show();

  // Plot average paper size in KB
  // Plot of submission size by month:

  let freq_keys = arxiv_size_frequencies.clone().into_iter().map(|entry| entry.0);
  let freq_values = arxiv_size_frequencies.clone().into_iter().map(|entry| entry.1);

  fg = Figure::new();
  fg.axes2d()
  .points(freq_keys, freq_values, &[PointSymbol('D'), Color("#ffaa77"), PointSize(0.5)])
  .set_x_label("Paper size in KB", &[Rotate(45.0)])
  .set_y_label("Paper count", &[Rotate(90.0)])
  .set_title("arXiv TeX paper sizes", &[]);

  fg.set_terminal("pngcairo", "arxiv_paper_sizes.png");
  fg.show();

}<|endoftext|>


<commit_before>// Copyright (C) 2016  ParadoxSpiral
//
// This file is part of mpv-rs.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

extern crate crossbeam;

use mpv::*;
use mpv::events::*;

use std::process;
use std::time::Duration;
use std::thread;

pub fn exec() {
    // Create a `Parent` (with events enabled) and set some properties.
    let mpv = Parent::<(), ()>::with_options(&[("cache-initial", 1.into()),
                                               ("volume", 10.into()),
                                               ("vo", "null".into()),
                                               ("ytdl", true.into())])
        .unwrap();

    // Create a crossbeam scope for convenience to use mpv in multiple threads.
    crossbeam::scope(|scope| {
        // Spin up 3 threads that observe different sets of `Event`s.
        scope.spawn(|| {
            let iter = mpv.observe_events(&[Event::FileLoaded,
                                  Event::StartFile,
                                  Event::Seek,
                                  Event::PlaybackRestart,
                                  Event::EndFile(None)])
                .unwrap();

            for vec in iter {
                // If any `Event` was an `Endfile`, . . .
                if let Some(&Event::EndFile(ref v)) =
                    vec.iter().find(|e| {
                        if let Event::EndFile(_) = **e {
                            true
                        } else {
                            false
                        }
                    }) {
                    // . . . print the `EndFile` reason and exit, . . .
                    println!("File ended! Reason: {:?}", v);
                    thread::sleep(Duration::from_secs(1));
                    process::exit(0);
                } else {
                    // . . . otherwise print all `Event`s.
                    println!("playback_events: {:?}", vec);
                };
            }
        });
        scope.spawn(|| {
            // Here the value of the property is irrelevant: only the name is used.
            let iter = mpv.observe_events(&[Event::PropertyChange(("volume".into(), 0.into())),
                                  Event::PropertyChange(("pause".into(), false.into()))])
                .unwrap();

            for vec in iter {
                println!("prop_events: {:?}", vec);
            }
        });
        scope.spawn(|| {
            let iter = mpv.observe_events(&[Event::LogMessage(LogMessage::new(LogLevel::Info))])
                .unwrap();

            for vec in iter {
                println!("log_events: {:#?}", vec);
            }
        });

        // Add a file to play, ytdl was set to true for this.
        mpv.playlist_load_files(&[("https://www.youtube.com/watch?v=DLzxrzFCyOs",
                                    FileState::AppendPlay,
                                    None)])
            .unwrap();

        thread::sleep(Duration::from_secs(3));

        mpv.set_property("volume", 25).unwrap();

        thread::sleep(Duration::from_secs(30));

        // Trigger `Event::EndFile` observed above to quit.
        mpv.playlist_next_force().unwrap();
    });
}
<commit_msg>[examples] remove useless statement<commit_after>// Copyright (C) 2016  ParadoxSpiral
//
// This file is part of mpv-rs.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

extern crate crossbeam;

use mpv::*;
use mpv::events::*;

use std::process;
use std::time::Duration;
use std::thread;

pub fn exec() {
    // Create a `Parent` (with events enabled) and set some properties.
    let mpv = Parent::<(), ()>::with_options(&[("cache-initial", 1.into()),
                                               ("volume", 10.into()),
                                               ("vo", "null".into()),
                                               ("ytdl", true.into())])
        .unwrap();

    // Create a crossbeam scope for convenience to use mpv in multiple threads.
    crossbeam::scope(|scope| {
        // Spin up 3 threads that observe different sets of `Event`s.
        scope.spawn(|| {
            let iter = mpv.observe_events(&[Event::FileLoaded,
                                  Event::StartFile,
                                  Event::Seek,
                                  Event::PlaybackRestart,
                                  Event::EndFile(None)])
                .unwrap();

            for vec in iter {
                // If any `Event` was an `Endfile`, . . .
                if let Some(&Event::EndFile(ref v)) =
                    vec.iter().find(|e| {
                        if let Event::EndFile(_) = **e {
                            true
                        } else {
                            false
                        }
                    }) {
                    // . . . print the `EndFile` reason and exit, . . .
                    println!("File ended! Reason: {:?}", v);
                    process::exit(0);
                } else {
                    // . . . otherwise print all `Event`s.
                    println!("playback_events: {:?}", vec);
                };
            }
        });
        scope.spawn(|| {
            // Here the value of the property is irrelevant: only the name is used.
            let iter = mpv.observe_events(&[Event::PropertyChange(("volume".into(), 0.into())),
                                  Event::PropertyChange(("pause".into(), false.into()))])
                .unwrap();

            for vec in iter {
                println!("prop_events: {:?}", vec);
            }
        });
        scope.spawn(|| {
            let iter = mpv.observe_events(&[Event::LogMessage(LogMessage::new(LogLevel::Info))])
                .unwrap();

            for vec in iter {
                println!("log_events: {:#?}", vec);
            }
        });

        // Add a file to play, ytdl was set to true for this.
        mpv.playlist_load_files(&[("https://www.youtube.com/watch?v=DLzxrzFCyOs",
                                    FileState::AppendPlay,
                                    None)])
            .unwrap();

        thread::sleep(Duration::from_secs(3));

        mpv.set_property("volume", 25).unwrap();

        thread::sleep(Duration::from_secs(30));

        // Trigger `Event::EndFile` observed above to quit.
        mpv.playlist_next_force().unwrap();
    });
}
<|endoftext|>


<commit_before>use std;
use simd;
use simd::x86::sse2::u64x2;
use simdext::*;

extern "platform-intrinsic" {
    fn x86_mm_testz_si128(x: u64x2, y: u64x2) -> i32;
}

#[inline]
unsafe fn bitcast<T, U>(x: T) -> U {
    debug_assert!(std::mem::size_of::<T>() == std::mem::size_of::<U>());
    std::mem::transmute_copy(&x)
}

#[derive(Clone, Copy)]
pub struct FieldBit {
    m: simd::u16x8,
}

impl FieldBit {
    pub fn new(m: simd::u16x8) -> FieldBit {
        FieldBit {
            m: m,
        }
    }

    pub fn new_empty() -> FieldBit {
        FieldBit {
            m: simd::u16x8::splat(0)
        }
    }

    pub fn from_values(v1: u16, v2: u16, v3: u16, v4: u16, v5: u16, v6: u16, v7: u16, v8: u16) -> FieldBit {
        FieldBit {
            m: simd::u16x8::new(v1, v2, v3, v4, v5, v6, v7, v8)
        }
    }

    pub fn from_onebit(x: usize, y: usize) -> FieldBit {
        FieldBit {
            m: FieldBit::onebit(x, y)
        }
    }

    pub fn from_str(s: &str) -> FieldBit {
        let mut f = FieldBit::new_empty();

        assert!(s.len() % 6 == 0);

        let mut cnt = 0;
        for b in s.bytes().rev() {
            let x = 6 - (cnt % 6);
            let y = (cnt / 6) + 1;
            if b == b'1' {
                f.set(x, y);
            }
            cnt += 1;
        }

        f
    }

    pub fn get(&self, x: usize, y: usize) -> bool {
        debug_assert!(FieldBit::check_in_range(x, y));
        unsafe {
            x86_mm_testz_si128(bitcast(FieldBit::onebit(x, y)), bitcast(self.m)) == 0
        }
    }

    pub fn set(&mut self, x: usize, y: usize) {
        debug_assert!(FieldBit::check_in_range(x, y));
        self.m = mm_or_si128(FieldBit::onebit(x, y), self.m)
    }

    pub fn unset(&mut self, x: usize, y: usize) {
        debug_assert!(FieldBit::check_in_range(x, y));
        self.m = mm_andnot_si128(FieldBit::onebit(x, y), self.m)
    }

    pub fn masked_field_12(&self) -> FieldBit {
        FieldBit {
            m: self.m & simd::u16x8::new(0, 0x1FFE, 0x1FFE, 0x1FFE, 0x1FFE, 0x1FFE, 0x1FFE, 0)
        }
    }

    pub fn masked_field_13(&self) -> FieldBit {
        FieldBit {
            m: self.m & simd::u16x8::new(0, 0x3FFE, 0x3FFE, 0x3FFE, 0x3FFE, 0x3FFE, 0x3FFE, 0)
        }
    }

    pub fn popcount(&self) -> usize {
        let x: u64x2 = unsafe { bitcast(self.m) };
        let low: u64 = x.extract(0);
        let high: u64 = x.extract(1);
        (low.count_ones() + high.count_ones()) as usize
    }

    pub fn expand(&self, mask: &FieldBit) -> FieldBit {
        let mut seed = self.m;
        loop {
            let mut expanded = seed;
            expanded = mm_or_si128(mm_slli_epi16(seed, 1), expanded);
            expanded = mm_or_si128(mm_srli_epi16(seed, 1), expanded);
            expanded = mm_or_si128(mm_slli_si128_2(seed), expanded);
            expanded = mm_or_si128(mm_srli_si128_2(seed), expanded);
            expanded = mm_and_si128(mask.m, expanded);

            if mm_testc_si128(seed, expanded) { // seed == expanded
                return FieldBit { m: expanded };
            }
            seed = expanded;
        }
    }

    pub fn as_u16x8(&self) -> simd::u16x8 {
        self.m
    }

    fn check_in_range(x: usize, y: usize) -> bool {
        x < 8 && y < 16
    }

    fn onebit(x: usize, y: usize) -> simd::u16x8 {
        debug_assert!(FieldBit::check_in_range(x, y));

        let shift = ((x << 4) | y) & 0x3F;
        let hi: u64 = (x as u64) >> 2;
        let lo: u64 = hi ^ 1;
        unsafe { bitcast(u64x2::new(lo << shift, hi << shift)) }
    }
}

impl std::fmt::Display for FieldBit {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "({}, {}, {}, {}, {}, {}, {}, {})",
               self.m.extract(0), self.m.extract(1), self.m.extract(2), self.m.extract(3),
               self.m.extract(4), self.m.extract(5), self.m.extract(6), self.m.extract(7))
    }
}

#[cfg(test)]
mod tests {
    use field;
    use field_bit::FieldBit;

    #[test]
    fn test_empty() {
        let fb = FieldBit::new_empty();
        for x in 0 .. 8 {
            for y in 0 .. 16 {
                assert_eq!(fb.get(x, y), false);
            }
        }
    }

    #[test]
    fn test_from_value() {
        let fb = FieldBit::from_values(1 << 0, 1 << 1, 1 << 2, 1 << 3, 1 << 4, 1 << 5, 1 << 6, 1 << 7);
        for x in 0 .. 8 {
            for y in 0 .. 16 {
                assert_eq!(fb.get(x, y), x == y, "failed at x={}, y={}, fb.get(x, y)={}", x, y, fb.get(x, y));
            }
        }
    }

    #[test]
    fn test_from_str() {
        let fb = FieldBit::from_str(concat!(
            "111...",
            "......",
            "111..."));

        for x in 0 .. 8 {
            for y in 0 .. 16 {
                let b = (y == 1 || y == 3) && (1 <= x && x <= 3);
                assert_eq!(fb.get(x, y), b, "x={}, y={}", x, y);
            }
        }
    }

    #[test]
    fn test_set_get() {
        for x in 0 .. 8 {
            for y in 0 .. 16 {
                let mut fb = FieldBit::new_empty();
                assert!(!fb.get(x, y));
                fb.set(x, y);
                assert!(fb.get(x, y));
                fb.unset(x, y);
                assert!(!fb.get(x, y));
            }
        }
    }

    #[test]
    fn test_masked_field() {
        let fb = FieldBit::from_values(0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF);
        let fb12 = fb.masked_field_12();
        let fb13 = fb.masked_field_13();

        for x in 0 .. field::MAP_WIDTH {
            for y in 0 .. field::MAP_HEIGHT {
                assert!(fb.get(x, y), "x={}, y={}", x, y);
                assert_eq!(fb12.get(x, y), 1 <= x && x <= 6 && 1 <= y && y <= 12, "x={}, y={}", x, y);
                assert_eq!(fb13.get(x, y), 1 <= x && x <= 6 && 1 <= y && y <= 13, "x={}, y={}", x, y);
            }
        }
    }

    #[test]
    fn test_popcount() {
        let fb = FieldBit::from_values(1, 2, 3, 4, 5, 6, 7, 8);
        assert_eq!(fb.popcount(), 1 + 1 + 2 + 1 + 2 + 2 + 3 + 1)
    }

    #[test]
    fn test_expand() {
        let mask = FieldBit::from_str(concat!(
            ".1....",
            "1.11..",
            "1.1...",
            "1.1...",
        ));

        let expected = FieldBit::from_str(concat!(
            "..11..",
            "..1...",
            "..1..."));

        let actual = FieldBit::from_onebit(3, 1).expand(&mask);
        for x in 0 .. 8 {
            for y in 0 .. 16 {
                assert_eq!(actual.get(x, y), expected.get(x, y), "x={}, y={}", x, y);
            }
        }
    }
}
<commit_msg>Add FieldBit::expand1.<commit_after>use std;
use simd;
use simd::x86::sse2::u64x2;
use simdext::*;

extern "platform-intrinsic" {
    fn x86_mm_testz_si128(x: u64x2, y: u64x2) -> i32;
}

#[inline]
unsafe fn bitcast<T, U>(x: T) -> U {
    debug_assert!(std::mem::size_of::<T>() == std::mem::size_of::<U>());
    std::mem::transmute_copy(&x)
}

#[derive(Clone, Copy)]
pub struct FieldBit {
    m: simd::u16x8,
}

impl FieldBit {
    pub fn new(m: simd::u16x8) -> FieldBit {
        FieldBit {
            m: m,
        }
    }

    pub fn new_empty() -> FieldBit {
        FieldBit {
            m: simd::u16x8::splat(0)
        }
    }

    pub fn from_values(v1: u16, v2: u16, v3: u16, v4: u16, v5: u16, v6: u16, v7: u16, v8: u16) -> FieldBit {
        FieldBit {
            m: simd::u16x8::new(v1, v2, v3, v4, v5, v6, v7, v8)
        }
    }

    pub fn from_onebit(x: usize, y: usize) -> FieldBit {
        FieldBit {
            m: FieldBit::onebit(x, y)
        }
    }

    pub fn from_str(s: &str) -> FieldBit {
        let mut f = FieldBit::new_empty();

        assert!(s.len() % 6 == 0);

        let mut cnt = 0;
        for b in s.bytes().rev() {
            let x = 6 - (cnt % 6);
            let y = (cnt / 6) + 1;
            if b == b'1' {
                f.set(x, y);
            }
            cnt += 1;
        }

        f
    }

    pub fn get(&self, x: usize, y: usize) -> bool {
        debug_assert!(FieldBit::check_in_range(x, y));
        unsafe {
            x86_mm_testz_si128(bitcast(FieldBit::onebit(x, y)), bitcast(self.m)) == 0
        }
    }

    pub fn set(&mut self, x: usize, y: usize) {
        debug_assert!(FieldBit::check_in_range(x, y));
        self.m = mm_or_si128(FieldBit::onebit(x, y), self.m)
    }

    pub fn unset(&mut self, x: usize, y: usize) {
        debug_assert!(FieldBit::check_in_range(x, y));
        self.m = mm_andnot_si128(FieldBit::onebit(x, y), self.m)
    }

    pub fn masked_field_12(&self) -> FieldBit {
        FieldBit {
            m: self.m & simd::u16x8::new(0, 0x1FFE, 0x1FFE, 0x1FFE, 0x1FFE, 0x1FFE, 0x1FFE, 0)
        }
    }

    pub fn masked_field_13(&self) -> FieldBit {
        FieldBit {
            m: self.m & simd::u16x8::new(0, 0x3FFE, 0x3FFE, 0x3FFE, 0x3FFE, 0x3FFE, 0x3FFE, 0)
        }
    }

    pub fn popcount(&self) -> usize {
        let x: u64x2 = unsafe { bitcast(self.m) };
        let low: u64 = x.extract(0);
        let high: u64 = x.extract(1);
        (low.count_ones() + high.count_ones()) as usize
    }

    pub fn expand(&self, mask: &FieldBit) -> FieldBit {
        let mut seed = self.m;
        loop {
            let mut expanded = seed;
            expanded = mm_or_si128(mm_slli_epi16(seed, 1), expanded);
            expanded = mm_or_si128(mm_srli_epi16(seed, 1), expanded);
            expanded = mm_or_si128(mm_slli_si128_2(seed), expanded);
            expanded = mm_or_si128(mm_srli_si128_2(seed), expanded);
            expanded = mm_and_si128(mask.m, expanded);

            if mm_testc_si128(seed, expanded) { // seed == expanded
                return FieldBit { m: expanded };
            }
            seed = expanded;
        }
    }

    pub fn expand1(&self, mask: &FieldBit) -> FieldBit {
        let seed = self.m;
        let v1 = mm_slli_epi16(seed, 1);
        let v2 = mm_srli_epi16(seed, 1);
        let v3 = mm_slli_si128_2(seed);
        let v4 = mm_srli_si128_2(seed);

        let m = ((seed | v1) | (v2 | v3) | v4) & mask.m;
        FieldBit { m: m }
    }

    pub fn as_u16x8(&self) -> simd::u16x8 {
        self.m
    }

    fn check_in_range(x: usize, y: usize) -> bool {
        x < 8 && y < 16
    }

    fn onebit(x: usize, y: usize) -> simd::u16x8 {
        debug_assert!(FieldBit::check_in_range(x, y));

        let shift = ((x << 4) | y) & 0x3F;
        let hi: u64 = (x as u64) >> 2;
        let lo: u64 = hi ^ 1;
        unsafe { bitcast(u64x2::new(lo << shift, hi << shift)) }
    }
}

impl std::fmt::Display for FieldBit {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "({}, {}, {}, {}, {}, {}, {}, {})",
               self.m.extract(0), self.m.extract(1), self.m.extract(2), self.m.extract(3),
               self.m.extract(4), self.m.extract(5), self.m.extract(6), self.m.extract(7))
    }
}

#[cfg(test)]
mod tests {
    use field;
    use field_bit::FieldBit;

    #[test]
    fn test_empty() {
        let fb = FieldBit::new_empty();
        for x in 0 .. 8 {
            for y in 0 .. 16 {
                assert_eq!(fb.get(x, y), false);
            }
        }
    }

    #[test]
    fn test_from_value() {
        let fb = FieldBit::from_values(1 << 0, 1 << 1, 1 << 2, 1 << 3, 1 << 4, 1 << 5, 1 << 6, 1 << 7);
        for x in 0 .. 8 {
            for y in 0 .. 16 {
                assert_eq!(fb.get(x, y), x == y, "failed at x={}, y={}, fb.get(x, y)={}", x, y, fb.get(x, y));
            }
        }
    }

    #[test]
    fn test_from_str() {
        let fb = FieldBit::from_str(concat!(
            "111...",
            "......",
            "111..."));

        for x in 0 .. 8 {
            for y in 0 .. 16 {
                let b = (y == 1 || y == 3) && (1 <= x && x <= 3);
                assert_eq!(fb.get(x, y), b, "x={}, y={}", x, y);
            }
        }
    }

    #[test]
    fn test_set_get() {
        for x in 0 .. 8 {
            for y in 0 .. 16 {
                let mut fb = FieldBit::new_empty();
                assert!(!fb.get(x, y));
                fb.set(x, y);
                assert!(fb.get(x, y));
                fb.unset(x, y);
                assert!(!fb.get(x, y));
            }
        }
    }

    #[test]
    fn test_masked_field() {
        let fb = FieldBit::from_values(0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF);
        let fb12 = fb.masked_field_12();
        let fb13 = fb.masked_field_13();

        for x in 0 .. field::MAP_WIDTH {
            for y in 0 .. field::MAP_HEIGHT {
                assert!(fb.get(x, y), "x={}, y={}", x, y);
                assert_eq!(fb12.get(x, y), 1 <= x && x <= 6 && 1 <= y && y <= 12, "x={}, y={}", x, y);
                assert_eq!(fb13.get(x, y), 1 <= x && x <= 6 && 1 <= y && y <= 13, "x={}, y={}", x, y);
            }
        }
    }

    #[test]
    fn test_popcount() {
        let fb = FieldBit::from_values(1, 2, 3, 4, 5, 6, 7, 8);
        assert_eq!(fb.popcount(), 1 + 1 + 2 + 1 + 2 + 2 + 3 + 1)
    }

    #[test]
    fn test_expand() {
        let mask = FieldBit::from_str(concat!(
            ".1....",
            "1.11..",
            "1.1...",
            "1.1...",
        ));

        let expected = FieldBit::from_str(concat!(
            "..11..",
            "..1...",
            "..1..."));

        let actual = FieldBit::from_onebit(3, 1).expand(&mask);
        for x in 0 .. 8 {
            for y in 0 .. 16 {
                assert_eq!(actual.get(x, y), expected.get(x, y), "x={}, y={}", x, y);
            }
        }
    }

    #[test]
    fn test_expand1() {
        let mask = FieldBit::from_str(concat!(
            "111111",
            "111111",
            "111111"));

        let seed = FieldBit::from_str(concat!(
            "......",
            "1...1.",
            "......"));

        let expected = FieldBit::from_str(concat!(
            "1...1.",
            "11.111",
            "1...1."));

        let actual = seed.expand1(&mask);
        for x in 0 .. 8 {
            for y in 0 .. 16 {
                assert_eq!(actual.get(x, y), expected.get(x, y), "x={}, y={}", x, y);
            }
        }
    }
}
<|endoftext|>