<commit_before>use audio::ac97::AC97;
use audio::intelhda::IntelHDA;

use core::cell::UnsafeCell;

use common::debug;

use disk::ahci::Ahci;
use disk::ide::Ide;

use env::Environment;

use network::intel8254x::Intel8254x;
use network::rtl8139::Rtl8139;

use schemes::file::FileScheme;

use usb::ehci::Ehci;
use usb::ohci::Ohci;
use usb::uhci::Uhci;
use usb::xhci::Xhci;

use super::config::PciConfig;
use super::common::class::*;
use super::common::subclass::*;
use super::common::programming_interface::*;

/// PCI device
pub unsafe fn pci_device(env: &mut Environment,
                         mut pci: PciConfig,
                         class_id: u8,
                         subclass_id: u8,
                         interface_id: u8,
                         vendor_code: u16,
                         device_code: u16) {
    if class_id == MASS_STORAGE {
        if subclass_id == IDE {
            if let Some(module) = FileScheme::new(Ide::disks(pci)) {
                env.schemes.push(UnsafeCell::new(module));
            }
        } else if subclass_id == SATA && interface_id == AHCI {
            if let Some(module) = FileScheme::new(Ahci::disks(pci)) {
                env.schemes.push(UnsafeCell::new(module));
            }
        }
    } else if class_id == SERIAL_BUS && subclass_id == USB {
        if interface_id == XHCI {
            let base = pci.read(0x10) as usize;

            let mut module = box Xhci {
                pci: pci,
                base: base & 0xFFFFFFF0,
                memory_mapped: base & 1 == 0,
                irq: pci.read(0x3C) as u8 & 0xF,
            };
            module.init();
            env.schemes.push(UnsafeCell::new(module));
        } else if interface_id == EHCI {
            env.schemes.push(UnsafeCell::new(Ehci::new(pci)));
        } else if interface_id == OHCI {
            env.schemes.push(UnsafeCell::new(Ohci::new(pci)));
        } else if interface_id == UHCI {
            env.schemes.push(UnsafeCell::new(Uhci::new(pci)));
        } else {
            debug!("Unknown USB interface version {:X}\n", interface_id);
        }
    } else {
        match vendor_code {
            0x10EC => match device_code { // REALTEK
                0x8139 => env.schemes.push(UnsafeCell::new(Rtl8139::new(pci))),
                _ => (),
            },
            0x8086 => match device_code { // INTEL
                0x100E => env.schemes.push(UnsafeCell::new(Intel8254x::new(pci))),
                0x2415 => env.schemes.push(UnsafeCell::new(AC97::new(pci))),
                0x24C5 => env.schemes.push(UnsafeCell::new(AC97::new(pci))),
                0x2668 => {
                    let base = pci.read(0x10) as usize;
                    let mut module = box IntelHDA {
                        pci: pci,
                        base: base & 0xFFFFFFF0,
                        memory_mapped: base & 1 == 0,
                        irq: pci.read(0x3C) as u8 & 0xF,
                    };
                    module.init();
                    env.schemes.push(UnsafeCell::new(module));
                }
                _ => (),
            },
            _ => (),
        }
    }
}

/// Initialize PCI session
pub unsafe fn pci_init(env: &mut Environment) {
    for bus in 0..256 {
        for slot in 0..32 {
            for func in 0..8 {
                let mut pci = PciConfig::new(bus as u8, slot as u8, func as u8);
                let id = pci.read(0);

                if (id & 0xFFFF) != 0xFFFF {
                    let class_id = pci.read(8);

                    debug!(" * PCI {}, {}, {}: ID {:X} CL {:X}",
                           bus,
                           slot,
                           func,
                           id,
                           class_id);

                    for i in 0..6 {
                        let bar = pci.read(i * 4 + 0x10);
                        if bar > 0 {
                            debug!(" BAR{}: {:X}", i, bar);

                            pci.write(i * 4 + 0x10, 0xFFFFFFFF);
                            let size = (0xFFFFFFFF - (pci.read(i * 4 + 0x10) & 0xFFFFFFF0)) + 1;
                            pci.write(i * 4 + 0x10, bar);

                            if size > 0 {
                                debug!(" {}", size);
                            }
                        }
                    }

                    debug::dl();

                    pci_device(env,
                               pci,
                               ((class_id >> 24) & 0xFF) as u8,
                               ((class_id >> 16) & 0xFF) as u8,
                               ((class_id >> 8) & 0xFF) as u8,
                               (id & 0xFFFF) as u16,
                               ((id >> 16) & 0xFFFF) as u16);
                }
            }
        }
    }
}
<commit_msg>Use constants for PCI vendor/device matching<commit_after>use audio::ac97::AC97;
use audio::intelhda::IntelHDA;

use core::cell::UnsafeCell;

use common::debug;

use disk::ahci::Ahci;
use disk::ide::Ide;

use env::Environment;

use network::intel8254x::Intel8254x;
use network::rtl8139::Rtl8139;

use schemes::file::FileScheme;

use usb::ehci::Ehci;
use usb::ohci::Ohci;
use usb::uhci::Uhci;
use usb::xhci::Xhci;

use super::config::PciConfig;
use super::common::class::*;
use super::common::subclass::*;
use super::common::programming_interface::*;
use super::common::vendorid::*;
use super::common::deviceid::*;

/// PCI device
pub unsafe fn pci_device(env: &mut Environment,
                         mut pci: PciConfig,
                         class_id: u8,
                         subclass_id: u8,
                         interface_id: u8,
                         vendor_code: u16,
                         device_code: u16) {
    if class_id == MASS_STORAGE {
        if subclass_id == IDE {
            if let Some(module) = FileScheme::new(Ide::disks(pci)) {
                env.schemes.push(UnsafeCell::new(module));
            }
        } else if subclass_id == SATA && interface_id == AHCI {
            if let Some(module) = FileScheme::new(Ahci::disks(pci)) {
                env.schemes.push(UnsafeCell::new(module));
            }
        }
    } else if class_id == SERIAL_BUS && subclass_id == USB {
        if interface_id == XHCI {
            let base = pci.read(0x10) as usize;

            let mut module = box Xhci {
                pci: pci,
                base: base & 0xFFFFFFF0,
                memory_mapped: base & 1 == 0,
                irq: pci.read(0x3C) as u8 & 0xF,
            };
            module.init();
            env.schemes.push(UnsafeCell::new(module));
        } else if interface_id == EHCI {
            env.schemes.push(UnsafeCell::new(Ehci::new(pci)));
        } else if interface_id == OHCI {
            env.schemes.push(UnsafeCell::new(Ohci::new(pci)));
        } else if interface_id == UHCI {
            env.schemes.push(UnsafeCell::new(Uhci::new(pci)));
        } else {
            debug!("Unknown USB interface version {:X}\n", interface_id);
        }
    } else {
        match vendor_code {
            REALTEK => match device_code {
                RTL8139 => env.schemes.push(UnsafeCell::new(Rtl8139::new(pci))),
                _ => (),
            },
            INTEL => match device_code {
                GBE_82540EM => env.schemes.push(UnsafeCell::new(Intel8254x::new(pci))),
                AC97_82801AA | AC97_ICH4 => env.schemes.push(UnsafeCell::new(AC97::new(pci))),
                INTELHDA_ICH6 => {
                    let base = pci.read(0x10) as usize;
                    let mut module = box IntelHDA {
                        pci: pci,
                        base: base & 0xFFFFFFF0,
                        memory_mapped: base & 1 == 0,
                        irq: pci.read(0x3C) as u8 & 0xF,
                    };
                    module.init();
                    env.schemes.push(UnsafeCell::new(module));
                }
                _ => (),
            },
            _ => (),
        }
    }
}

/// Initialize PCI session
pub unsafe fn pci_init(env: &mut Environment) {
    for bus in 0..256 {
        for slot in 0..32 {
            for func in 0..8 {
                let mut pci = PciConfig::new(bus as u8, slot as u8, func as u8);
                let id = pci.read(0);

                if (id & 0xFFFF) != 0xFFFF {
                    let class_id = pci.read(8);

                    debug!(" * PCI {}, {}, {}: ID {:X} CL {:X}",
                           bus,
                           slot,
                           func,
                           id,
                           class_id);

                    for i in 0..6 {
                        let bar = pci.read(i * 4 + 0x10);
                        if bar > 0 {
                            debug!(" BAR{}: {:X}", i, bar);

                            pci.write(i * 4 + 0x10, 0xFFFFFFFF);
                            let size = (0xFFFFFFFF - (pci.read(i * 4 + 0x10) & 0xFFFFFFF0)) + 1;
                            pci.write(i * 4 + 0x10, bar);

                            if size > 0 {
                                debug!(" {}", size);
                            }
                        }
                    }

                    debug::dl();

                    pci_device(env,
                               pci,
                               ((class_id >> 24) & 0xFF) as u8,
                               ((class_id >> 16) & 0xFF) as u8,
                               ((class_id >> 8) & 0xFF) as u8,
                               (id & 0xFFFF) as u16,
                               ((id >> 16) & 0xFFFF) as u16);
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>use arm::Isa;
use once_cell::sync::OnceCell;
use std::ffi::OsStr;
use std::path::{Path, PathBuf};
use std::process::Command;

fn devkit_arm() -> &'static Path {
    static DEVKIT_ARM: OnceCell<PathBuf> = OnceCell::new();

    DEVKIT_ARM.get_or_init(|| {
        let mut path = if let Ok(arm) = std::env::var("DEVKITARM") {
            PathBuf::from(arm)
        } else if let Ok(pro) = std::env::var("DEVKITPRO") {
            PathBuf::from(pro).join("devkitARM")
        } else {
            panic!("DEVKITARM or DEVKITPRO environment variables must be defined")
        };

        // If the path doesn't exists on Windows then MSYS2 may have been used to install
        // devkitPRO in which case use cygpath to find the Windows path.
        if !path.exists() && cfg!(target_os = "windows") {
            if let Ok(result) = Command::new("cygpath").arg("-w").arg(&path).output() {
                let win_path = String::from_utf8_lossy(&result.stdout);
                let win_path = win_path.trim();
                if !win_path.trim().is_empty() {
                    println!("using windows path from cygpath: {}", win_path);
                    path = PathBuf::from(win_path);
                }
            }
        }

        if !path.exists() {
            panic!("DEVKITARM path {} does not exist", path.display());
        }

        path
    })
}

fn simple_linker_script() -> &'static Path {
    const SIMPLE_LINKER_SCRIPT: &str = "
    ENTRY(_start);
    SECTIONS
    {
        . = 0x0;

        /* Place special section .text.prologue before everything else */
        .text : {
            . = ALIGN(4);
            *(.text.prologue);
            *(.text*);
            . = ALIGN(4);
        }

        /* Output the data sections */
        .data : {
            . = ALIGN(4);
            *(.data*);
        }

        .rodata : {
            . = ALIGN(4);
            *(.rodata*);
        }

        /* The BSS section for uniitialized data */
        .bss : {
            . = ALIGN(4);
            __bss_start = .;
            *(COMMON);
            *(.bss);
            . = ALIGN(4);
            __bss_end = .;
        }

        /* Size of the BSS section in case it is needed */
        __bss_size = ((__bss_end)-(__bss_start));

        /* Remove the note that may be placed before the code by LD */
        /DISCARD/ : {
            *(.note.gnu.build-id);
            *(.ARM.attributes);
        }
    }
    ";
    static SIMPLE_LINKER_SCRIPT_PATH: OnceCell<PathBuf> = OnceCell::new();

    SIMPLE_LINKER_SCRIPT_PATH.get_or_init(|| {
        let tmp_dir = Path::new(env!("CARGO_TARGET_TMPDIR"));
        let path = tmp_dir.join("simple-linker-script.ld");
        std::fs::write(&path, SIMPLE_LINKER_SCRIPT).expect("failed to write simple linker script");
        path
    })
}

fn run_program<P: AsRef<OsStr>>(
    program: P,
    args: &[&str],
) -> std::io::Result<std::process::ExitStatus> {
    println!("executing: {}", program.as_ref().to_str().unwrap());

    let output = Command::new(program.as_ref())
        .stdout(std::process::Stdio::piped())
        .stderr(std::process::Stdio::piped())
        .args(args)
        .output()?;

    let mut had_output = false;

    let in_obj_dump = program
        .as_ref()
        .to_str()
        .map(|s| s.contains("objdump"))
        .unwrap_or(false);
    let mut in_obj_dump_preamble = true;

    let stdout = String::from_utf8_lossy(&output.stdout);
    for line in stdout.lines() {
        if !in_obj_dump {
            println!("  out: {}", line.trim_end());
            had_output = true;
            continue;
        }

        // For objdump we do some special formatting for the output:
        if !in_obj_dump_preamble {
            println!("    {}", line.trim());
            had_output = true;
        }

        // After we encounter one of these lines:
        //    00000000 <.data>:
        //    00000000 <.text>:
        // we are no longer in the preamble.
        if line.contains(">:") {
            println!("  {}", line.trim());
            in_obj_dump_preamble = false;
            had_output = true;
        }
    }

    let stderr = String::from_utf8_lossy(&output.stderr);
    for (idx, line) in stderr.lines().enumerate() {
        if idx == 0 && had_output {
            println!()
        }
        println!("  err: {}", line.trim_end());
    }

    Ok(output.status)
}

fn run_arm_program(program: &str, args: &[&str]) -> std::io::Result<std::process::ExitStatus> {
    let mut p = devkit_arm().join("arm-none-eabi/bin");
    p.push(program);
    run_program(p, args)
}

trait PathOrString {
    fn convert_to_str(&self) -> &str;
}

impl PathOrString for &'_ Path {
    fn convert_to_str(&self) -> &str {
        self.to_str().unwrap_or("")
    }
}

impl PathOrString for PathBuf {
    fn convert_to_str(&self) -> &str {
        self.to_str().unwrap_or("")
    }
}

impl PathOrString for &'_ str {
    fn convert_to_str(&self) -> &str {
        *self
    }
}

impl PathOrString for &String {
    fn convert_to_str(&self) -> &str {
        *self
    }
}

macro_rules! arm_run {
    ($program:expr, $($arg:expr),* $(,)?) => {
        run_arm_program($program, &[
            $($arg.convert_to_str()),*
        ])
    };
}

pub fn assemble(isa: Isa, name: &str, source: &str) -> std::io::Result<Vec<u8>> {
    let tmp_dir = Path::new(env!("CARGO_TARGET_TMPDIR"));

    let source_file_path = tmp_dir.join(format!("{}.s", name));
    let object_file_path = tmp_dir.join(format!("{}.o", name));
    let elf_file_path = tmp_dir.join(format!("{}.elf", name));
    let bin_file_path = tmp_dir.join(format!("{}.bin", name));

    let files_to_destroy = [
        &source_file_path as &Path,
        &object_file_path,
        &elf_file_path,
        &bin_file_path,
    ];
    let _file_destructor = FileDestructor::new(&files_to_destroy);

    let preamble = "\
    .global _start
    _start:\n";
    let mut new_source = String::with_capacity(preamble.len() + source.len() + 1);
    new_source.push_str(preamble);
    new_source.push_str(source);
    new_source.push('\n');
    let source = new_source;

    // `as` outputs a warning if the file does not end with a newline or if there
    // is no `_start:` symbol.

    std::fs::write(&source_file_path, source)?;

    let as_output = if isa == Isa::Arm {
        arm_run!(
            "as",
            "-mcpu=arm7tdmi",
            "-march=armv4t",
            "-mthumb-interwork",
            "-o",
            object_file_path,
            source_file_path
        )?
    } else {
        arm_run!(
            "as",
            "-mthumb",
            "-mcpu=arm7tdmi",
            "-march=armv4t",
            "-mthumb-interwork",
            "-o",
            object_file_path,
            source_file_path
        )?
    };
    if !as_output.success() {
        panic!("failed to assemble {}", source_file_path.display());
    }

    let ld_script = format!("-T{}", simple_linker_script().display());
    if !arm_run!("ld", &ld_script, "-o", elf_file_path, object_file_path)?.success() {
        panic!("failed to link {}", object_file_path.display());
    }

    if !arm_run!("objcopy", "-O", "binary", elf_file_path, bin_file_path)?.success() {
        panic!("failed to extract binary from {}", elf_file_path.display());
    }

    let objdump_output = if isa == Isa::Arm {
        arm_run!(
            "objdump",
            "-b",
            "binary",
            "-m",
            "armv4t",
            "--adjust-vma=0x0",
            "-D",
            bin_file_path
        )?
    } else {
        arm_run!(
            "objdump",
            "-b",
            "binary",
            "-m",
            "armv4t",
            "-Mforce-thumb",
            "--adjust-vma=0x0",
            "-dj",
            ".text",
            bin_file_path
        )?
    };
    if !objdump_output.success() {
        panic!("failed to disassemble binary {}", bin_file_path.display())
    }

    std::fs::read(&bin_file_path)
}

struct FileDestructor<'p> {
    paths: &'p [&'p Path],
}

impl<'p> FileDestructor<'p> {
    pub fn new(paths: &'p [&'p Path]) -> Self {
        FileDestructor { paths }
    }
}

impl<'p> Drop for FileDestructor<'p> {
    fn drop(&mut self) {
        for &path in self.paths {
            if !path.exists() {
                continue;
            }

            if let Err(err) = std::fs::remove_file(path) {
                eprintln!(
                    "error occurred while deleting path `{}`: {}",
                    path.display(),
                    err
                );
            }
        }
    }
}
<commit_msg>Be more flexible with paths for ARM binutils.<commit_after>use arm::Isa;
use once_cell::sync::OnceCell;
use std::ffi::OsStr;
use std::path::{Path, PathBuf};
use std::process::Command;

fn devkit_arm_bin() -> &'static Path {
    static DEVKIT_ARM: OnceCell<PathBuf> = OnceCell::new();

    DEVKIT_ARM.get_or_init(|| {
        let mut path = if let Ok(arm) = std::env::var("DEVKITARM") {
            PathBuf::from(arm)
        } else if let Ok(pro) = std::env::var("DEVKITPRO") {
            PathBuf::from(pro).join("devkitARM")
        } else {
            panic!("DEVKITARM or DEVKITPRO environment variables must be defined")
        };

        // If the path doesn't exists on Windows then MSYS2 may have been used to install
        // devkitPRO in which case use cygpath to find the Windows path.
        if !path.exists() && cfg!(target_os = "windows") {
            if let Ok(result) = Command::new("cygpath").arg("-w").arg(&path).output() {
                let win_path = String::from_utf8_lossy(&result.stdout);
                let win_path = win_path.trim();
                if !win_path.trim().is_empty() {
                    println!("using windows path from cygpath: {}", win_path);
                    path = PathBuf::from(win_path);
                }
            }
        }

        if !path.exists() {
            panic!("DEVKITARM path {} does not exist", path.display());
        }

        // If we find as in the root DEVKITARM directory, just use that.
        if path.join("arm-none-eabi-as").exists() || path.join("arm-none-eabi-as.exe").exists() {
            return path;
        }

        // Try to find as in a /bin subdirectory:
        path = path.join("bin");
        if path.join("arm-none-eabi-as").exists() || path.join("arm-none-eabi-as.exe").exists() {
            return path;
        }

        panic!(
            "Unable to find arm-none-eabi-as in `{}` or `{}/bin`",
            path.display(),
            path.display()
        );
    })
}

fn simple_linker_script() -> &'static Path {
    const SIMPLE_LINKER_SCRIPT: &str = "
    ENTRY(_start);
    SECTIONS
    {
        . = 0x0;

        /* Place special section .text.prologue before everything else */
        .text : {
            . = ALIGN(4);
            *(.text.prologue);
            *(.text*);
            . = ALIGN(4);
        }

        /* Output the data sections */
        .data : {
            . = ALIGN(4);
            *(.data*);
        }

        .rodata : {
            . = ALIGN(4);
            *(.rodata*);
        }

        /* The BSS section for uniitialized data */
        .bss : {
            . = ALIGN(4);
            __bss_start = .;
            *(COMMON);
            *(.bss);
            . = ALIGN(4);
            __bss_end = .;
        }

        /* Size of the BSS section in case it is needed */
        __bss_size = ((__bss_end)-(__bss_start));

        /* Remove the note that may be placed before the code by LD */
        /DISCARD/ : {
            *(.note.gnu.build-id);
            *(.ARM.attributes);
        }
    }
    ";
    static SIMPLE_LINKER_SCRIPT_PATH: OnceCell<PathBuf> = OnceCell::new();

    SIMPLE_LINKER_SCRIPT_PATH.get_or_init(|| {
        let tmp_dir = Path::new(env!("CARGO_TARGET_TMPDIR"));
        let path = tmp_dir.join("simple-linker-script.ld");
        std::fs::write(&path, SIMPLE_LINKER_SCRIPT).expect("failed to write simple linker script");
        path
    })
}

fn run_program<P: AsRef<OsStr>>(
    program: P,
    args: &[&str],
) -> std::io::Result<std::process::ExitStatus> {
    println!("executing: {}", program.as_ref().to_str().unwrap());

    let output = Command::new(program.as_ref())
        .stdout(std::process::Stdio::piped())
        .stderr(std::process::Stdio::piped())
        .args(args)
        .output()?;

    let mut had_output = false;

    let in_obj_dump = program
        .as_ref()
        .to_str()
        .map(|s| s.contains("objdump"))
        .unwrap_or(false);
    let mut in_obj_dump_preamble = true;

    let stdout = String::from_utf8_lossy(&output.stdout);
    for line in stdout.lines() {
        if !in_obj_dump {
            println!("  out: {}", line.trim_end());
            had_output = true;
            continue;
        }

        // For objdump we do some special formatting for the output:
        if !in_obj_dump_preamble {
            println!("    {}", line.trim());
            had_output = true;
        }

        // After we encounter one of these lines:
        //    00000000 <.data>:
        //    00000000 <.text>:
        // we are no longer in the preamble.
        if line.contains(">:") {
            println!("  {}", line.trim());
            in_obj_dump_preamble = false;
            had_output = true;
        }
    }

    let stderr = String::from_utf8_lossy(&output.stderr);
    for (idx, line) in stderr.lines().enumerate() {
        if idx == 0 && had_output {
            println!()
        }
        println!("  err: {}", line.trim_end());
    }

    Ok(output.status)
}

fn run_arm_program(program: &str, args: &[&str]) -> std::io::Result<std::process::ExitStatus> {
    let p = devkit_arm_bin().join(format!("arm-none-eabi-{program}"));
    run_program(p, args)
}

trait PathOrString {
    fn convert_to_str(&self) -> &str;
}

impl PathOrString for &'_ Path {
    fn convert_to_str(&self) -> &str {
        self.to_str().unwrap_or("")
    }
}

impl PathOrString for PathBuf {
    fn convert_to_str(&self) -> &str {
        self.to_str().unwrap_or("")
    }
}

impl PathOrString for &'_ str {
    fn convert_to_str(&self) -> &str {
        *self
    }
}

impl PathOrString for &String {
    fn convert_to_str(&self) -> &str {
        *self
    }
}

macro_rules! arm_run {
    ($program:expr, $($arg:expr),* $(,)?) => {
        run_arm_program($program, &[
            $($arg.convert_to_str()),*
        ])
    };
}

pub fn assemble(isa: Isa, name: &str, source: &str) -> std::io::Result<Vec<u8>> {
    let tmp_dir = Path::new(env!("CARGO_TARGET_TMPDIR"));

    let source_file_path = tmp_dir.join(format!("{}.s", name));
    let object_file_path = tmp_dir.join(format!("{}.o", name));
    let elf_file_path = tmp_dir.join(format!("{}.elf", name));
    let bin_file_path = tmp_dir.join(format!("{}.bin", name));

    let files_to_destroy = [
        &source_file_path as &Path,
        &object_file_path,
        &elf_file_path,
        &bin_file_path,
    ];
    let _file_destructor = FileDestructor::new(&files_to_destroy);

    let preamble = "\
    .global _start
    _start:\n";
    let mut new_source = String::with_capacity(preamble.len() + source.len() + 1);
    new_source.push_str(preamble);
    new_source.push_str(source);
    new_source.push('\n');
    let source = new_source;

    // `as` outputs a warning if the file does not end with a newline or if there
    // is no `_start:` symbol.

    std::fs::write(&source_file_path, source)?;

    let as_output = if isa == Isa::Arm {
        arm_run!(
            "as",
            "-mcpu=arm7tdmi",
            "-march=armv4t",
            "-mthumb-interwork",
            "-o",
            object_file_path,
            source_file_path
        )?
    } else {
        arm_run!(
            "as",
            "-mthumb",
            "-mcpu=arm7tdmi",
            "-march=armv4t",
            "-mthumb-interwork",
            "-o",
            object_file_path,
            source_file_path
        )?
    };
    if !as_output.success() {
        panic!("failed to assemble {}", source_file_path.display());
    }

    let ld_script = format!("-T{}", simple_linker_script().display());
    if !arm_run!("ld", &ld_script, "-o", elf_file_path, object_file_path)?.success() {
        panic!("failed to link {}", object_file_path.display());
    }

    if !arm_run!("objcopy", "-O", "binary", elf_file_path, bin_file_path)?.success() {
        panic!("failed to extract binary from {}", elf_file_path.display());
    }

    let objdump_output = if isa == Isa::Arm {
        arm_run!(
            "objdump",
            "-b",
            "binary",
            "-m",
            "armv4t",
            "--adjust-vma=0x0",
            "-D",
            bin_file_path
        )?
    } else {
        arm_run!(
            "objdump",
            "-b",
            "binary",
            "-m",
            "armv4t",
            "-Mforce-thumb",
            "--adjust-vma=0x0",
            "-dj",
            ".text",
            bin_file_path
        )?
    };
    if !objdump_output.success() {
        panic!("failed to disassemble binary {}", bin_file_path.display())
    }

    std::fs::read(&bin_file_path)
}

struct FileDestructor<'p> {
    paths: &'p [&'p Path],
}

impl<'p> FileDestructor<'p> {
    pub fn new(paths: &'p [&'p Path]) -> Self {
        FileDestructor { paths }
    }
}

impl<'p> Drop for FileDestructor<'p> {
    fn drop(&mut self) {
        for &path in self.paths {
            if !path.exists() {
                continue;
            }

            if let Err(err) = std::fs::remove_file(path) {
                eprintln!(
                    "error occurred while deleting path `{}`: {}",
                    path.display(),
                    err
                );
            }
        }
    }
}
<|endoftext|>


<commit_before>use crate::{CompileError, Config, Syntax};

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

use mime::Mime;
use quote::ToTokens;

pub struct TemplateInput<'a> {
    pub ast: &'a syn::DeriveInput,
    pub config: &'a Config<'a>,
    pub syntax: &'a Syntax<'a>,
    pub source: Source,
    pub print: Print,
    pub escaper: &'a str,
    pub ext: Option<String>,
    pub parent: Option<&'a syn::Type>,
    pub path: PathBuf,
}

impl TemplateInput<'_> {
    /// Extract the template metadata from the `DeriveInput` structure. This
    /// mostly recovers the data for the `TemplateInput` fields from the
    /// `template()` attribute list fields; it also finds the of the `_parent`
    /// field, if any.
    pub fn new<'n>(
        ast: &'n syn::DeriveInput,
        config: &'n Config<'_>,
    ) -> Result<TemplateInput<'n>, CompileError> {
        // Check that an attribute called `template()` exists and that it is
        // the proper type (list).
        let meta = ast
            .attrs
            .iter()
            .find_map(|attr| {
                attr.path.is_ident("template").then(|| {
                    attr.parse_meta()
                        .map_err(|e| format!("unable to parse attribute: {}", e).into())
                })
            })
            .unwrap_or(Err(CompileError::Static("no attribute 'template' found")))?;

        let meta_list = match meta {
            syn::Meta::List(inner) => inner,
            _ => return Err("attribute 'template' has incorrect type".into()),
        };

        // Loop over the meta attributes and find everything that we
        // understand. Raise panics if something is not right.
        // `source` contains an enum that can represent `path` or `source`.
        let mut source = None;
        let mut print = Print::None;
        let mut escaping = None;
        let mut ext = None;
        let mut syntax = None;
        for item in meta_list.nested {
            let pair = match item {
                syn::NestedMeta::Meta(syn::Meta::NameValue(ref pair)) => pair,
                _ => {
                    return Err(format!(
                        "unsupported attribute argument {:?}",
                        item.to_token_stream()
                    )
                    .into())
                }
            };

            if pair.path.is_ident("path") {
                if let syn::Lit::Str(ref s) = pair.lit {
                    if source.is_some() {
                        return Err("must specify 'source' or 'path', not both".into());
                    }
                    source = Some(Source::Path(s.value()));
                } else {
                    return Err("template path must be string literal".into());
                }
            } else if pair.path.is_ident("source") {
                if let syn::Lit::Str(ref s) = pair.lit {
                    if source.is_some() {
                        return Err("must specify 'source' or 'path', not both".into());
                    }
                    source = Some(Source::Source(s.value()));
                } else {
                    return Err("template source must be string literal".into());
                }
            } else if pair.path.is_ident("print") {
                if let syn::Lit::Str(ref s) = pair.lit {
                    print = s.value().parse()?;
                } else {
                    return Err("print value must be string literal".into());
                }
            } else if pair.path.is_ident("escape") {
                if let syn::Lit::Str(ref s) = pair.lit {
                    escaping = Some(s.value());
                } else {
                    return Err("escape value must be string literal".into());
                }
            } else if pair.path.is_ident("ext") {
                if let syn::Lit::Str(ref s) = pair.lit {
                    ext = Some(s.value());
                } else {
                    return Err("ext value must be string literal".into());
                }
            } else if pair.path.is_ident("syntax") {
                if let syn::Lit::Str(ref s) = pair.lit {
                    syntax = Some(s.value())
                } else {
                    return Err("syntax value must be string literal".into());
                }
            } else {
                return Err(format!(
                    "unsupported attribute key '{}' found",
                    pair.path.to_token_stream()
                )
                .into());
            }
        }

        // Validate the `source` and `ext` value together, since they are
        // related. In case `source` was used instead of `path`, the value
        // of `ext` is merged into a synthetic `path` value here.
        let source = source.expect("template path or source not found in attributes");
        let path = match (&source, &ext) {
            (&Source::Path(ref path), _) => config.find_template(path, None)?,
            (&Source::Source(_), Some(ext)) => PathBuf::from(format!("{}.{}", ast.ident, ext)),
            (&Source::Source(_), None) => {
                return Err("must include 'ext' attribute when using 'source' attribute".into())
            }
        };

        // Check to see if a `_parent` field was defined on the context
        // struct, and store the type for it for use in the code generator.
        let parent = match ast.data {
            syn::Data::Struct(syn::DataStruct {
                fields: syn::Fields::Named(ref fields),
                ..
            }) => fields
                .named
                .iter()
                .find(|f| f.ident.as_ref().filter(|name| *name == "_parent").is_some())
                .map(|f| &f.ty),
            _ => None,
        };

        if parent.is_some() {
            eprint!(
                "   --> in struct {}\n   = use of deprecated field '_parent'\n",
                ast.ident
            );
        }

        // Validate syntax
        let syntax = syntax.map_or_else(
            || Ok(config.syntaxes.get(config.default_syntax).unwrap()),
            |s| {
                config.syntaxes.get(&s).ok_or_else(|| {
                    CompileError::String(format!("attribute syntax {} not exist", s))
                })
            },
        )?;

        // Match extension against defined output formats

        let extension = escaping.unwrap_or_else(|| {
            path.extension()
                .map(|s| s.to_str().unwrap())
                .unwrap_or("")
                .to_string()
        });

        let mut escaper = None;
        for (extensions, path) in &config.escapers {
            if extensions.contains(&extension) {
                escaper = Some(path);
                break;
            }
        }

        let escaper = escaper.ok_or_else(|| {
            CompileError::String(format!("no escaper defined for extension '{}'", extension,))
        })?;

        Ok(TemplateInput {
            ast,
            config,
            syntax,
            source,
            print,
            escaper,
            ext,
            parent,
            path,
        })
    }

    pub fn extension(&self) -> Option<&str> {
        self.ext.as_deref().or_else(|| extension(&self.path))
    }
}

fn extension(path: &Path) -> Option<&str> {
    let ext = path.extension().map(|s| s.to_str().unwrap())?;

    const JINJA_EXTENSIONS: [&str; 3] = ["j2", "jinja", "jinja2"];
    if JINJA_EXTENSIONS.contains(&ext) {
        Path::new(path.file_stem().unwrap())
            .extension()
            .map(|s| s.to_str().unwrap())
            .or(Some(ext))
    } else {
        Some(ext)
    }
}

pub enum Source {
    Path(String),
    Source(String),
}

#[derive(PartialEq)]
pub enum Print {
    All,
    Ast,
    Code,
    None,
}

impl FromStr for Print {
    type Err = CompileError;

    fn from_str(s: &str) -> Result<Print, Self::Err> {
        use self::Print::*;
        Ok(match s {
            "all" => All,
            "ast" => Ast,
            "code" => Code,
            "none" => None,
            v => return Err(format!("invalid value for print option: {}", v,).into()),
        })
    }
}

#[doc(hidden)]
pub fn extension_to_mime_type(ext: &str) -> Mime {
    let basic_type = mime_guess::from_ext(ext).first_or_octet_stream();
    for (simple, utf_8) in &TEXT_TYPES {
        if &basic_type == simple {
            return utf_8.clone();
        }
    }
    basic_type
}

const TEXT_TYPES: [(Mime, Mime); 6] = [
    (mime::TEXT_PLAIN, mime::TEXT_PLAIN_UTF_8),
    (mime::TEXT_HTML, mime::TEXT_HTML_UTF_8),
    (mime::TEXT_CSS, mime::TEXT_CSS_UTF_8),
    (mime::TEXT_CSV, mime::TEXT_CSV_UTF_8),
    (
        mime::TEXT_TAB_SEPARATED_VALUES,
        mime::TEXT_TAB_SEPARATED_VALUES_UTF_8,
    ),
    (
        mime::APPLICATION_JAVASCRIPT,
        mime::APPLICATION_JAVASCRIPT_UTF_8,
    ),
];

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

    #[test]
    fn test_ext() {
        assert_eq!(extension(Path::new("foo-bar.txt")), Some("txt"));
        assert_eq!(extension(Path::new("foo-bar.html")), Some("html"));
        assert_eq!(extension(Path::new("foo-bar.unknown")), Some("unknown"));

        assert_eq!(extension(Path::new("foo/bar/baz.txt")), Some("txt"));
        assert_eq!(extension(Path::new("foo/bar/baz.html")), Some("html"));
        assert_eq!(extension(Path::new("foo/bar/baz.unknown")), Some("unknown"));
    }

    #[test]
    fn test_double_ext() {
        assert_eq!(extension(Path::new("foo-bar.html.txt")), Some("txt"));
        assert_eq!(extension(Path::new("foo-bar.txt.html")), Some("html"));
        assert_eq!(extension(Path::new("foo-bar.txt.unknown")), Some("unknown"));

        assert_eq!(extension(Path::new("foo/bar/baz.html.txt")), Some("txt"));
        assert_eq!(extension(Path::new("foo/bar/baz.txt.html")), Some("html"));
        assert_eq!(
            extension(Path::new("foo/bar/baz.txt.unknown")),
            Some("unknown")
        );
    }

    #[test]
    fn test_skip_jinja_ext() {
        assert_eq!(extension(Path::new("foo-bar.html.j2")), Some("html"));
        assert_eq!(extension(Path::new("foo-bar.html.jinja")), Some("html"));
        assert_eq!(extension(Path::new("foo-bar.html.jinja2")), Some("html"));

        assert_eq!(extension(Path::new("foo/bar/baz.txt.j2")), Some("txt"));
        assert_eq!(extension(Path::new("foo/bar/baz.txt.jinja")), Some("txt"));
        assert_eq!(extension(Path::new("foo/bar/baz.txt.jinja2")), Some("txt"));
    }

    #[test]
    fn test_only_jinja_ext() {
        assert_eq!(extension(Path::new("foo-bar.j2")), Some("j2"));
        assert_eq!(extension(Path::new("foo-bar.jinja")), Some("jinja"));
        assert_eq!(extension(Path::new("foo-bar.jinja2")), Some("jinja2"));
    }
}
<commit_msg>Unshadow function escaping()<commit_after>use crate::{CompileError, Config, Syntax};

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

use mime::Mime;
use quote::ToTokens;

pub struct TemplateInput<'a> {
    pub ast: &'a syn::DeriveInput,
    pub config: &'a Config<'a>,
    pub syntax: &'a Syntax<'a>,
    pub source: Source,
    pub print: Print,
    pub escaper: &'a str,
    pub ext: Option<String>,
    pub parent: Option<&'a syn::Type>,
    pub path: PathBuf,
}

impl TemplateInput<'_> {
    /// Extract the template metadata from the `DeriveInput` structure. This
    /// mostly recovers the data for the `TemplateInput` fields from the
    /// `template()` attribute list fields; it also finds the of the `_parent`
    /// field, if any.
    pub fn new<'n>(
        ast: &'n syn::DeriveInput,
        config: &'n Config<'_>,
    ) -> Result<TemplateInput<'n>, CompileError> {
        // Check that an attribute called `template()` exists and that it is
        // the proper type (list).
        let meta = ast
            .attrs
            .iter()
            .find_map(|attr| {
                attr.path.is_ident("template").then(|| {
                    attr.parse_meta()
                        .map_err(|e| format!("unable to parse attribute: {}", e).into())
                })
            })
            .unwrap_or(Err(CompileError::Static("no attribute 'template' found")))?;

        let meta_list = match meta {
            syn::Meta::List(inner) => inner,
            _ => return Err("attribute 'template' has incorrect type".into()),
        };

        // Loop over the meta attributes and find everything that we
        // understand. Raise panics if something is not right.
        // `source` contains an enum that can represent `path` or `source`.
        let mut source = None;
        let mut print = Print::None;
        let mut escaping = None;
        let mut ext = None;
        let mut syntax = None;
        for item in meta_list.nested {
            let pair = match item {
                syn::NestedMeta::Meta(syn::Meta::NameValue(ref pair)) => pair,
                _ => {
                    return Err(format!(
                        "unsupported attribute argument {:?}",
                        item.to_token_stream()
                    )
                    .into())
                }
            };

            if pair.path.is_ident("path") {
                if let syn::Lit::Str(ref s) = pair.lit {
                    if source.is_some() {
                        return Err("must specify 'source' or 'path', not both".into());
                    }
                    source = Some(Source::Path(s.value()));
                } else {
                    return Err("template path must be string literal".into());
                }
            } else if pair.path.is_ident("source") {
                if let syn::Lit::Str(ref s) = pair.lit {
                    if source.is_some() {
                        return Err("must specify 'source' or 'path', not both".into());
                    }
                    source = Some(Source::Source(s.value()));
                } else {
                    return Err("template source must be string literal".into());
                }
            } else if pair.path.is_ident("print") {
                if let syn::Lit::Str(ref s) = pair.lit {
                    print = s.value().parse()?;
                } else {
                    return Err("print value must be string literal".into());
                }
            } else if pair.path.is_ident("escape") {
                if let syn::Lit::Str(ref s) = pair.lit {
                    escaping = Some(s.value());
                } else {
                    return Err("escape value must be string literal".into());
                }
            } else if pair.path.is_ident("ext") {
                if let syn::Lit::Str(ref s) = pair.lit {
                    ext = Some(s.value());
                } else {
                    return Err("ext value must be string literal".into());
                }
            } else if pair.path.is_ident("syntax") {
                if let syn::Lit::Str(ref s) = pair.lit {
                    syntax = Some(s.value())
                } else {
                    return Err("syntax value must be string literal".into());
                }
            } else {
                return Err(format!(
                    "unsupported attribute key '{}' found",
                    pair.path.to_token_stream()
                )
                .into());
            }
        }

        // Validate the `source` and `ext` value together, since they are
        // related. In case `source` was used instead of `path`, the value
        // of `ext` is merged into a synthetic `path` value here.
        let source = source.expect("template path or source not found in attributes");
        let path = match (&source, &ext) {
            (&Source::Path(ref path), _) => config.find_template(path, None)?,
            (&Source::Source(_), Some(ext)) => PathBuf::from(format!("{}.{}", ast.ident, ext)),
            (&Source::Source(_), None) => {
                return Err("must include 'ext' attribute when using 'source' attribute".into())
            }
        };

        // Check to see if a `_parent` field was defined on the context
        // struct, and store the type for it for use in the code generator.
        let parent = match ast.data {
            syn::Data::Struct(syn::DataStruct {
                fields: syn::Fields::Named(ref fields),
                ..
            }) => fields
                .named
                .iter()
                .find(|f| f.ident.as_ref().filter(|name| *name == "_parent").is_some())
                .map(|f| &f.ty),
            _ => None,
        };

        if parent.is_some() {
            eprint!(
                "   --> in struct {}\n   = use of deprecated field '_parent'\n",
                ast.ident
            );
        }

        // Validate syntax
        let syntax = syntax.map_or_else(
            || Ok(config.syntaxes.get(config.default_syntax).unwrap()),
            |s| {
                config.syntaxes.get(&s).ok_or_else(|| {
                    CompileError::String(format!("attribute syntax {} not exist", s))
                })
            },
        )?;

        // Match extension against defined output formats

        let escaping = escaping.unwrap_or_else(|| {
            path.extension()
                .map(|s| s.to_str().unwrap())
                .unwrap_or("")
                .to_string()
        });

        let mut escaper = None;
        for (extensions, path) in &config.escapers {
            if extensions.contains(&escaping) {
                escaper = Some(path);
                break;
            }
        }

        let escaper = escaper.ok_or_else(|| {
            CompileError::String(format!("no escaper defined for extension '{}'", escaping,))
        })?;

        Ok(TemplateInput {
            ast,
            config,
            syntax,
            source,
            print,
            escaper,
            ext,
            parent,
            path,
        })
    }

    pub fn extension(&self) -> Option<&str> {
        self.ext.as_deref().or_else(|| extension(&self.path))
    }
}

fn extension(path: &Path) -> Option<&str> {
    let ext = path.extension().map(|s| s.to_str().unwrap())?;

    const JINJA_EXTENSIONS: [&str; 3] = ["j2", "jinja", "jinja2"];
    if JINJA_EXTENSIONS.contains(&ext) {
        Path::new(path.file_stem().unwrap())
            .extension()
            .map(|s| s.to_str().unwrap())
            .or(Some(ext))
    } else {
        Some(ext)
    }
}

pub enum Source {
    Path(String),
    Source(String),
}

#[derive(PartialEq)]
pub enum Print {
    All,
    Ast,
    Code,
    None,
}

impl FromStr for Print {
    type Err = CompileError;

    fn from_str(s: &str) -> Result<Print, Self::Err> {
        use self::Print::*;
        Ok(match s {
            "all" => All,
            "ast" => Ast,
            "code" => Code,
            "none" => None,
            v => return Err(format!("invalid value for print option: {}", v,).into()),
        })
    }
}

#[doc(hidden)]
pub fn extension_to_mime_type(ext: &str) -> Mime {
    let basic_type = mime_guess::from_ext(ext).first_or_octet_stream();
    for (simple, utf_8) in &TEXT_TYPES {
        if &basic_type == simple {
            return utf_8.clone();
        }
    }
    basic_type
}

const TEXT_TYPES: [(Mime, Mime); 6] = [
    (mime::TEXT_PLAIN, mime::TEXT_PLAIN_UTF_8),
    (mime::TEXT_HTML, mime::TEXT_HTML_UTF_8),
    (mime::TEXT_CSS, mime::TEXT_CSS_UTF_8),
    (mime::TEXT_CSV, mime::TEXT_CSV_UTF_8),
    (
        mime::TEXT_TAB_SEPARATED_VALUES,
        mime::TEXT_TAB_SEPARATED_VALUES_UTF_8,
    ),
    (
        mime::APPLICATION_JAVASCRIPT,
        mime::APPLICATION_JAVASCRIPT_UTF_8,
    ),
];

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

    #[test]
    fn test_ext() {
        assert_eq!(extension(Path::new("foo-bar.txt")), Some("txt"));
        assert_eq!(extension(Path::new("foo-bar.html")), Some("html"));
        assert_eq!(extension(Path::new("foo-bar.unknown")), Some("unknown"));

        assert_eq!(extension(Path::new("foo/bar/baz.txt")), Some("txt"));
        assert_eq!(extension(Path::new("foo/bar/baz.html")), Some("html"));
        assert_eq!(extension(Path::new("foo/bar/baz.unknown")), Some("unknown"));
    }

    #[test]
    fn test_double_ext() {
        assert_eq!(extension(Path::new("foo-bar.html.txt")), Some("txt"));
        assert_eq!(extension(Path::new("foo-bar.txt.html")), Some("html"));
        assert_eq!(extension(Path::new("foo-bar.txt.unknown")), Some("unknown"));

        assert_eq!(extension(Path::new("foo/bar/baz.html.txt")), Some("txt"));
        assert_eq!(extension(Path::new("foo/bar/baz.txt.html")), Some("html"));
        assert_eq!(
            extension(Path::new("foo/bar/baz.txt.unknown")),
            Some("unknown")
        );
    }

    #[test]
    fn test_skip_jinja_ext() {
        assert_eq!(extension(Path::new("foo-bar.html.j2")), Some("html"));
        assert_eq!(extension(Path::new("foo-bar.html.jinja")), Some("html"));
        assert_eq!(extension(Path::new("foo-bar.html.jinja2")), Some("html"));

        assert_eq!(extension(Path::new("foo/bar/baz.txt.j2")), Some("txt"));
        assert_eq!(extension(Path::new("foo/bar/baz.txt.jinja")), Some("txt"));
        assert_eq!(extension(Path::new("foo/bar/baz.txt.jinja2")), Some("txt"));
    }

    #[test]
    fn test_only_jinja_ext() {
        assert_eq!(extension(Path::new("foo-bar.j2")), Some("j2"));
        assert_eq!(extension(Path::new("foo-bar.jinja")), Some("jinja"));
        assert_eq!(extension(Path::new("foo-bar.jinja2")), Some("jinja2"));
    }
}
<|endoftext|>


<commit_before>//random numbers: generators and properties

extern crate num;
use self::num::traits::{Bounded, Unsigned};
use super::Mod;
use std::ops::{BitXor, BitAnd, BitOrAssign, Shl, Shr, ShrAssign};
use std::ops::Add;
use std::num::Wrapping;

//Linear Congruential Generator
//https://en.wikipedia.org/wiki/Linear_congruential_generator
//Generic over nonnegative numbers (of the same type)
//MCG a subset where increment=0

//shortcut to not have to rewrite `Unsigned + Copy` a bunch of times
pub trait LCGReqs : Unsigned + Copy {}
impl<T> LCGReqs for T where T: Unsigned + Copy {}
//impl<T> LCGReqs for T where Wrapping<T> {}

struct GenericWrap<T: LCGReqs>( T );

pub struct LCG<T: LCGReqs> {
    modulus:    GenericWrap<T>,
    multiplier: GenericWrap<T>,
    increment:  GenericWrap<T>,
    seed:       GenericWrap<T>,
}

impl<T: LCGReqs> LCG<T> {
    pub fn from_lcg(modulus: T, multiplier: T, increment: T, seed: T) -> Self {
        //linear congruential generator has multiplier and increment
        LCG {
            modulus:    GenericWrap(modulus),
            multiplier: GenericWrap(multiplier),
            increment:  GenericWrap(increment),
            seed:       GenericWrap(seed),
        }
    }
    pub fn from_mcg(modulus: T, multiplier: T, seed: T) -> Self {
        //multiplicative congruential generator only has multiplier
        Self::from_lcg(modulus, multiplier, T::zero(), seed)
    }
}

impl LCG<u32> {
    pub fn ansi_c() -> Self {
        //ANSI C rand function parameters:
        LCG {
            modulus:    GenericWrap(2u32.pow(31)),
            multiplier: GenericWrap(1103515245),
            increment:  GenericWrap(12345),
            seed:       GenericWrap(12345),
        }
    }
}

impl<T> Add<T> for GenericWrap<T> where T: LCGReqs {
    type Output = GenericWrap<T>;
    fn add(self, rhs: GenericWrap<T>) -> Self::Output {
        rhs
    }
}

impl<T: LCGReqs> Iterator for LCG<T> {
    type Item = T;
    fn next(&mut self) -> Option<T> {
        //generate new value, modify `seed`
        //does not return the initial seed the first time
        //let product = Wrapping(self.multiplier) * Wrapping(self.seed);
        let a = self.multiplier + self.seed;
        //self.seed = (self.multiplier * self.seed + self.increment).modulo(self.modulus);
        Some(self.seed.0)
    }
}

//https://en.wikipedia.org/wiki/Maximum_length_sequence
//for this simple implementation, the length can only be the size of an unsigned integer
//  (in the num crate: u8, u16, u32, u64, (BigUint, usize))

//shortcut to not have to rewrite trait signature.
//I think this technically uses more characters
pub trait MSeqReqs<T> : Copy + Unsigned + Bounded +
    BitOrAssign<T> + BitAnd<T,Output=T> + BitXor<T,Output=T> +
    ShrAssign<T> + Shl<T,Output=T> + Shr<T,Output=T> {}

impl<S,T> MSeqReqs<T> for S where S: Copy + Unsigned + Bounded +
    BitOrAssign<T> + BitAnd<T,Output=T> + BitXor<T,Output=T> +
    ShrAssign<T> + Shl<T,Output=T> + Shr<T,Output=T> {}

pub struct MSequence<T: MSeqReqs<T>> {
    state: T,
}

impl<T: MSeqReqs<T>> MSequence<T> {
    pub fn from(block: T) -> Self {
        MSequence {
            state: block,
        }
    }
}

impl<T: MSeqReqs<T>> Iterator for MSequence<T> {
    type Item = T;
    fn next(&mut self) -> Option<T> {
        //shift state right by 1
        //determines new (leftmost) bit by xoring prior rightmost 2 bits
        let two: T = T::one() + T::one();
        let bit0: T = T::one() & self.state;
        let bit1: T = (two & self.state) >> T::one();
        let first_bit = bit0 ^ bit1;
        self.state >>= T::one();
        //want to `OR` self.state with first_bit
        //but T is generic and first_bit is either 0 or 1
        //ordinarily we'd Shl it by 7 or whatever, but we don't know its size
        //instead, if first_bit isn't zero (so it's one), we can find
        // 0b100..000  from the max value
        if first_bit == T::one() {
            //find largest power of two that fits in T
            //i.e. first bit is 1, all other bits are 0
            let pad: T = T::max_value() - T::max_value() / two;
            self.state |= pad;
        }
        Some(self.state)
    }
}

<commit_msg>undid changes; currently compiles but does not overflow correctly (in Debug mode)<commit_after>//random numbers: generators and properties

extern crate num;
use self::num::traits::{Bounded, Unsigned};
use super::Mod;
use std::ops::{BitXor, BitAnd, BitOrAssign, Shl, Shr, ShrAssign};

//Linear Congruential Generator
//https://en.wikipedia.org/wiki/Linear_congruential_generator
//Generic over nonnegative numbers (of the same type)
//MCG a subset where increment=0

//shortcut to not have to rewrite `Unsigned + Copy` a bunch of times
pub trait LCGReqs : Unsigned + Copy {}
impl<T> LCGReqs for T where T: Unsigned + Copy {}

pub struct LCG<T: LCGReqs> {
    modulus:    T,
    multiplier: T,
    increment:  T,
    seed:       T,
}

impl<T: LCGReqs> LCG<T> {
    pub fn from_lcg(modulus: T, multiplier: T, increment: T, seed: T) -> Self {
        //linear congruential generator has multiplier and increment
        LCG {
            modulus:    modulus,
            multiplier: multiplier,
            increment:  increment,
            seed:       seed,
        }
    }
    pub fn from_mcg(modulus: T, multiplier: T, seed: T) -> Self {
        //multiplicative congruential generator only has multiplier
        Self::from_lcg(modulus, multiplier, T::zero(), seed)
    }
}

impl LCG<u32> {
    pub fn ansi_c() -> Self {
        //ANSI C rand function parameters:
        LCG {
            modulus:    2u32.pow(31),
            multiplier: 1103515245,
            increment:  12345,
            seed:       12345,
        }
    }
}

impl<T: LCGReqs> Iterator for LCG<T> {
    type Item = T;
    fn next(&mut self) -> Option<T> {
        //generate new value, modify `seed`
        //does not return the initial seed the first time
        //TODO: if math overflows (like we want it to), that is a runtime error in Debug mode
        //      pretty sure `num` doesn't support WrappingMul or anything
        //      find some way to get Wrapping<T> working?
        self.seed = (self.multiplier * self.seed + self.increment).modulo(self.modulus);
        Some(self.seed)
    }
}

//https://en.wikipedia.org/wiki/Maximum_length_sequence
//for this simple implementation, the length can only be the size of an unsigned integer
//  (in the num crate: u8, u16, u32, u64, (BigUint, usize))

//shortcut to not have to rewrite trait signature.
//I think this technically uses more characters
pub trait MSeqReqs<T> : Copy + Unsigned + Bounded +
    BitOrAssign<T> + BitAnd<T,Output=T> + BitXor<T,Output=T> +
    ShrAssign<T> + Shl<T,Output=T> + Shr<T,Output=T> {}

impl<S,T> MSeqReqs<T> for S where S: Copy + Unsigned + Bounded +
    BitOrAssign<T> + BitAnd<T,Output=T> + BitXor<T,Output=T> +
    ShrAssign<T> + Shl<T,Output=T> + Shr<T,Output=T> {}

pub struct MSequence<T: MSeqReqs<T>> {
    state: T,
}

impl<T: MSeqReqs<T>> MSequence<T> {
    pub fn from(block: T) -> Self {
        MSequence {
            state: block,
        }
    }
}

impl<T: MSeqReqs<T>> Iterator for MSequence<T> {
    type Item = T;
    fn next(&mut self) -> Option<T> {
        //shift state right by 1
        //determines new (leftmost) bit by xoring prior rightmost 2 bits
        let two: T = T::one() + T::one();
        let bit0: T = T::one() & self.state;
        let bit1: T = (two & self.state) >> T::one();
        let first_bit = bit0 ^ bit1;
        self.state >>= T::one();
        //want to `OR` self.state with first_bit
        //but T is generic and first_bit is either 0 or 1
        //ordinarily we'd Shl it by 7 or whatever, but we don't know its size
        //instead, if first_bit isn't zero (so it's one), we can find
        // 0b100..000  from the max value
        if first_bit == T::one() {
            //find largest power of two that fits in T
            //i.e. first bit is 1, all other bits are 0
            let pad: T = T::max_value() - T::max_value() / two;
            self.state |= pad;
        }
        Some(self.state)
    }
}

<|endoftext|>


<commit_before>//! Variable parameters module.

use std::f64;
use std::ops::{Add, Sub, Mul, Div};

#[derive(Clone, Copy)]
pub enum Parameter {
    Fixed(f64),
    Exponential {
        init: f64,
        floor: f64,
        tau: f64,

        count: u32,
    },
    Polynomial {
        init: f64,
        floor: f64,
        tau: f64,

        count: u32,
    },
    // http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.384.9576&rep=rep1&type=pdf
    GHC {
        init: f64,
        floor: f64,
        tau: f64,

        count: u32
    }
}

impl Parameter {
    pub fn fixed(value: f64) -> Parameter {
        Parameter::Fixed(value)
    }

    pub fn exponential(init: f64, floor: f64, tau: f64) -> Parameter {
        Parameter::Exponential {
            init: init,
            floor: floor,
            tau: tau,

            count: 0,
        }
    }

    pub fn polynomial(init: f64, floor: f64, tau: f64) -> Parameter {
        Parameter::Polynomial {
            init: init,
            floor: floor,
            tau: tau,

            count: 0,
        }
    }

    pub fn ghc(init: f64, floor: f64, tau: f64) -> Parameter {
        Parameter::GHC {
            init: init,
            floor: floor,
            tau: tau,

            count: 0,
        }
    }

    pub fn value(&self) -> f64 {
        match self {
            &Parameter::Fixed(v) => v,

            &Parameter::Exponential { init: i, floor: f, count: c, tau: t } => {
                f64::max(i*t.powf(c as f64), f)
            }

            &Parameter::Polynomial { init: i, floor: f, count: c, tau: t } => {
                f64::max(i/(c as f64 + 1.0).powf(t), f)
            },

            &Parameter::GHC { init: i, floor: f, count: c, tau: t } => {
                f64::max(i*t/(t+c as f64 - 1.0), f)
            },
        }
    }

    pub fn to_fixed(self) -> Parameter {
        Parameter::Fixed(self.value())
    }

    pub fn step(self) -> Parameter {
        match self {
            Parameter::Fixed(_) => self,
            Parameter::Exponential { init: i, floor: f, count: c, tau: t } => {
                Parameter::Exponential {
                    init: i,
                    floor: f,
                    tau: t,

                    count: c.saturating_add(1),
                }
            }
            Parameter::Polynomial { init: i, floor: f, count: c, tau: t } => {
                Parameter::Polynomial {
                    init: i,
                    floor: f,
                    tau: t,

                    count: c.saturating_add(1),
                }
            },
            Parameter::GHC { init: i, floor: f, count: c, tau: t } => {
                Parameter::GHC {
                    init: i,
                    floor: f,
                    tau: t,

                    count: c.saturating_add(1),
                }
            },
        }
    }

    pub fn back(self) -> Parameter {
        match self {
            Parameter::Fixed(_) => self,
            Parameter::Exponential { init: i, floor: f, count: c, tau: t } => {
                Parameter::Exponential {
                    init: i,
                    floor: f,
                    tau: t,

                    count: c.saturating_sub(1),
                }
            }
            Parameter::Polynomial { init: i, floor: f, count: c, tau: t } => {
                Parameter::Polynomial {
                    init: i,
                    floor: f,
                    tau: t,

                    count: c.saturating_sub(1),
                }
            },
            Parameter::GHC { init: i, floor: f, count: c, tau: t } => {
                Parameter::GHC {
                    init: i,
                    floor: f,
                    tau: t,

                    count: c.saturating_sub(1),
                }
            },
        }
    }
}

impl Into<Parameter> for f64 {
    fn into(self) -> Parameter {
        Parameter::Fixed(self)
    }
}

macro_rules! impl_op {
    ($name: ident, $num_type: ty, $fn_name: ident, $op: tt) => {
        impl $name<$num_type> for Parameter {
            type Output = $num_type;

            fn $fn_name(self, other: $num_type) -> $num_type {
                self.value() $op other
            }
        }

        impl $name<Parameter> for $num_type {
            type Output = $num_type;

            fn $fn_name(self, other: Parameter) -> $num_type {
                self $op other.value()
            }
        }

        impl $name<Parameter> for Parameter {
            type Output = $num_type;

            fn $fn_name(self, other: Parameter) -> $num_type {
                self.value() $op other.value()
            }
        }
    }
}

impl_op!(Add, f64, add, +);
impl_op!(Sub, f64, sub, -);
impl_op!(Mul, f64, mul, *);
impl_op!(Div, f64, div, /);

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

    #[test]
    fn test_fixed() {
        let mut p = Parameter::fixed(1.0);

        assert_eq!(p.value(), 1.0);

        for _ in 0..1000 {
            p = p.step();
            assert_eq!(p.value(), 1.0);
        }

        for _ in 0..1000 {
            p = p.back();
            assert_eq!(p.value(), 1.0);
        }
    }

    #[test]
    fn test_exponential() {
        let mut p = Parameter::exponential(1.0, 0.5, 0.9);

        assert!((p.value() - 1.0).abs() < 1e-7);

        p = p.step();
        assert!((p.value() - 0.9).abs() < 1e-7);

        p = p.step();
        assert!((p.value() - 0.81).abs() < 1e-7);

        p = p.step();
        assert!((p.value() - 0.729).abs() < 1e-7);

        p = p.back();
        assert!((p.value() - 0.81).abs() < 1e-7);

        p = p.back();
        assert!((p.value() - 0.9).abs() < 1e-7);

        p = p.back();
        assert!((p.value() - 1.0).abs() < 1e-7);

        p = p.back();
        assert!((p.value() - 1.0).abs() < 1e-7);

        for _ in 0..1000 {
            p = p.step();
            assert!(p.value() >= 0.5);
        }

        assert!((p.value() - 0.5).abs() < 1e-7);
    }

    #[test]
    fn test_polynomial() {
        let mut p = Parameter::polynomial(1.0, 0.1, 0.6);

        assert!((p.value() - 1.0).abs() < 1e-7);

        p = p.step();
        assert!((p.value() - 0.659753955386447).abs() < 1e-7);

        p = p.step();
        assert!((p.value() - 0.517281857971786).abs() < 1e-7);

        p = p.step();
        assert!((p.value() - 0.435275281648062).abs() < 1e-7);

        p = p.back();
        assert!((p.value() - 0.517281857971786).abs() < 1e-7);

        p = p.back();
        assert!((p.value() - 0.659753955386447).abs() < 1e-7);

        p = p.back();
        assert!((p.value() - 1.0).abs() < 1e-7);

        p = p.back();
        assert!((p.value() - 1.0).abs() < 1e-7);

        for _ in 0..1000 {
            p = p.step();
            assert!(p.value() >= 0.1);
        }

        assert!((p.value() - 0.1).abs() < 1e-7);
    }

    #[test]
    fn test_to_fixed() {
        let mut p = Parameter::exponential(1.0, 0.5, 0.9);
        p = p.step().to_fixed();

        assert!((p.value() - 0.9).abs() < 1e-7);

        p = p.step().step().step().back().back();
        assert!((p.value() - 0.9).abs() < 1e-7);
    }
}
<commit_msg>feat(Parameter): add "Boyan" decay schedule<commit_after>//! Variable parameters module.

use std::f64;
use std::ops::{Add, Sub, Mul, Div};

#[derive(Clone, Copy)]
pub enum Parameter {
    Fixed(f64),
    Exponential {
        init: f64,
        floor: f64,
        tau: f64,
        count: u32,
    },
    Polynomial {
        init: f64,
        floor: f64,
        tau: f64,
        count: u32,
    },
    Boyan {
        init: f64,
        floor: f64,
        n0: u32,
        count: u32,
    },
    // http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.384.9576&rep=rep1&type=pdf
    GHC {
        init: f64,
        floor: f64,
        tau: f64,
        count: u32
    }
}

impl Parameter {
    pub fn fixed(value: f64) -> Parameter {
        Parameter::Fixed(value)
    }

    pub fn exponential(init: f64, floor: f64, tau: f64) -> Parameter {
        Parameter::Exponential {
            init: init,
            floor: floor,
            tau: tau,
            count: 0,
        }
    }

    pub fn polynomial(init: f64, floor: f64, tau: f64) -> Parameter {
        Parameter::Polynomial {
            init: init,
            floor: floor,
            tau: tau,
            count: 0,
        }
    }

    pub fn boyan(init: f64, floor: f64, n0: u32) -> Parameter {
        Parameter::Boyan {
            init: init,
            floor: floor,
            n0: n0,
            count: 0,
        }
    }

    pub fn ghc(init: f64, floor: f64, tau: f64) -> Parameter {
        Parameter::GHC {
            init: init,
            floor: floor,
            tau: tau,
            count: 0,
        }
    }

    pub fn value(&self) -> f64 {
        match self {
            &Parameter::Fixed(v) => v,

            &Parameter::Exponential { init: i, floor: f, tau: t, count: c, } => {
                f64::max(i*t.powf(c as f64), f)
            }

            &Parameter::Polynomial { init: i, floor: f, tau: t, count: c, } => {
                f64::max(i/(c as f64 + 1.0).powf(t), f)
            },

            &Parameter::Boyan { init: i, floor: f, n0: n, count: c, } => {
                f64::max(i*((n + 1) as f64)/((n + c) as f64), f)
            },

            &Parameter::GHC { init: i, floor: f, tau: t, count: c, } => {
                f64::max(i*t/(t+c as f64 - 1.0), f)
            },
        }
    }

    pub fn to_fixed(self) -> Parameter {
        Parameter::Fixed(self.value())
    }

    pub fn step(self) -> Parameter {
        match self {
            Parameter::Fixed(_) => self,
            Parameter::Exponential { init: i, floor: f, tau: t, count: c, } => {
                Parameter::Exponential {
                    init: i,
                    floor: f,
                    tau: t,
                    count: c.saturating_add(1),
                }
            }
            Parameter::Polynomial { init: i, floor: f, tau: t, count: c, } => {
                Parameter::Polynomial {
                    init: i,
                    floor: f,
                    tau: t,
                    count: c.saturating_add(1),
                }
            },
            Parameter::Boyan { init: i, floor: f, n0: n, count: c, } => {
                Parameter::Boyan {
                    init: i,
                    floor: f,
                    n0: n,
                    count: c.saturating_add(1),
                }
            },
            Parameter::GHC { init: i, floor: f, tau: t, count: c, } => {
                Parameter::GHC {
                    init: i,
                    floor: f,
                    tau: t,
                    count: c.saturating_add(1),
                }
            },
        }
    }

    pub fn back(self) -> Parameter {
        match self {
            Parameter::Fixed(_) => self,
            Parameter::Exponential { init: i, floor: f, tau: t, count: c, } => {
                Parameter::Exponential {
                    init: i,
                    floor: f,
                    tau: t,
                    count: c.saturating_sub(1),
                }
            }
            Parameter::Polynomial { init: i, floor: f, tau: t, count: c, } => {
                Parameter::Polynomial {
                    init: i,
                    floor: f,
                    tau: t,
                    count: c.saturating_sub(1),
                }
            },
            Parameter::Boyan { init: i, floor: f, n0: n, count: c, } => {
                Parameter::Boyan {
                    init: i,
                    floor: f,
                    n0: n,
                    count: c.saturating_sub(1),
                }
            },
            Parameter::GHC { init: i, floor: f, tau: t, count: c, } => {
                Parameter::GHC {
                    init: i,
                    floor: f,
                    tau: t,
                    count: c.saturating_sub(1),
                }
            },
        }
    }
}

impl Into<Parameter> for f64 {
    fn into(self) -> Parameter {
        Parameter::Fixed(self)
    }
}

macro_rules! impl_op {
    ($name: ident, $num_type: ty, $fn_name: ident, $op: tt) => {
        impl $name<$num_type> for Parameter {
            type Output = $num_type;

            fn $fn_name(self, other: $num_type) -> $num_type {
                self.value() $op other
            }
        }

        impl $name<Parameter> for $num_type {
            type Output = $num_type;

            fn $fn_name(self, other: Parameter) -> $num_type {
                self $op other.value()
            }
        }

        impl $name<Parameter> for Parameter {
            type Output = $num_type;

            fn $fn_name(self, other: Parameter) -> $num_type {
                self.value() $op other.value()
            }
        }
    }
}

impl_op!(Add, f64, add, +);
impl_op!(Sub, f64, sub, -);
impl_op!(Mul, f64, mul, *);
impl_op!(Div, f64, div, /);

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

    #[test]
    fn test_fixed() {
        let mut p = Parameter::fixed(1.0);

        assert_eq!(p.value(), 1.0);

        for _ in 0..1000 {
            p = p.step();
            assert_eq!(p.value(), 1.0);
        }

        for _ in 0..1000 {
            p = p.back();
            assert_eq!(p.value(), 1.0);
        }
    }

    #[test]
    fn test_exponential() {
        let mut p = Parameter::exponential(1.0, 0.5, 0.9);

        assert!((p.value() - 1.0).abs() < 1e-7);

        p = p.step();
        assert!((p.value() - 0.9).abs() < 1e-7);

        p = p.step();
        assert!((p.value() - 0.81).abs() < 1e-7);

        p = p.step();
        assert!((p.value() - 0.729).abs() < 1e-7);

        p = p.back();
        assert!((p.value() - 0.81).abs() < 1e-7);

        p = p.back();
        assert!((p.value() - 0.9).abs() < 1e-7);

        p = p.back();
        assert!((p.value() - 1.0).abs() < 1e-7);

        p = p.back();
        assert!((p.value() - 1.0).abs() < 1e-7);

        for _ in 0..1000 {
            p = p.step();
            assert!(p.value() >= 0.5);
        }

        assert!((p.value() - 0.5).abs() < 1e-7);
    }

    #[test]
    fn test_polynomial() {
        let mut p = Parameter::polynomial(1.0, 0.1, 0.6);

        assert!((p.value() - 1.0).abs() < 1e-7);

        p = p.step();
        assert!((p.value() - 0.659753955386447).abs() < 1e-7);

        p = p.step();
        assert!((p.value() - 0.517281857971786).abs() < 1e-7);

        p = p.step();
        assert!((p.value() - 0.435275281648062).abs() < 1e-7);

        p = p.back();
        assert!((p.value() - 0.517281857971786).abs() < 1e-7);

        p = p.back();
        assert!((p.value() - 0.659753955386447).abs() < 1e-7);

        p = p.back();
        assert!((p.value() - 1.0).abs() < 1e-7);

        p = p.back();
        assert!((p.value() - 1.0).abs() < 1e-7);

        for _ in 0..1000 {
            p = p.step();
            assert!(p.value() >= 0.1);
        }

        assert!((p.value() - 0.1).abs() < 1e-7);
    }

    #[test]
    fn test_to_fixed() {
        let mut p = Parameter::exponential(1.0, 0.5, 0.9);
        p = p.step().to_fixed();

        assert!((p.value() - 0.9).abs() < 1e-7);

        p = p.step().step().step().back().back();
        assert!((p.value() - 0.9).abs() < 1e-7);
    }
}
<|endoftext|>


<commit_before><commit_msg>Changed set! to disallow variable definition<commit_after><|endoftext|>


<commit_before>/*
 * Copyright (C) 2017-2018 Ryan Huang
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

pub mod bitboard;
mod board;
mod constants;
pub mod ttable;
mod zobrist;

use std::fmt;
use std::mem;

use glium::glutin::EventsLoopProxy;

use self::bitboard::BitBoard;
pub use self::board::Board;
use ai::AI;

pub struct Model {
    pub board: Board,
    pub players: ColorMap<Player>,
    pub last_move: Option<Move>,
    pub selected_piece: Option<FieldCoord>,
    pub exchanging: bool,
    pub ai: AI,
    pub ai_search_depth: u8,
    pub outcome: Outcome,
    undo_stack: Vec<(Board, Option<Move>, Outcome)>,
    redo_stack: Vec<(Board, Option<Move>, Outcome)>,
    pub events_proxy: EventsLoopProxy,
}

impl Model {
    pub fn new(players: ColorMap<Player>, events_proxy: EventsLoopProxy) -> Self {
        Self {
            board: Board::new(),
            players,
            selected_piece: None,
            last_move: None,
            exchanging: false,
            ai: AI::new(),
            ai_search_depth: 6,
            outcome: Outcome::InProgress,
            undo_stack: vec![],
            redo_stack: vec![],
            events_proxy,
        }
    }
    pub fn can_undo(&self) -> bool {
        self.current_player() != Player::Computer && !self.undo_stack.is_empty()
    }
    pub fn can_redo(&self) -> bool {
        self.current_player() != Player::Computer && !self.redo_stack.is_empty()
    }
    pub fn push_undo_state(&mut self) {
        self.undo_stack
            .push((self.board, self.last_move, self.outcome));
        self.redo_stack.clear();
    }
    pub fn undo_move(&mut self) {
        while let Some((board, last_move, outcome)) = self.undo_stack.pop() {
            self.redo_stack.push((
                mem::replace(&mut self.board, board),
                mem::replace(&mut self.last_move, last_move),
                mem::replace(&mut self.outcome, outcome),
            ));

            self.clear_selection();
            self.exchanging = false;

            if Player::Human == self.players.get(board.turn) {
                break;
            }
        }
    }
    pub fn redo_move(&mut self) {
        while let Some((board, last_move, outcome)) = self.redo_stack.pop() {
            self.undo_stack.push((
                mem::replace(&mut self.board, board),
                mem::replace(&mut self.last_move, last_move),
                mem::replace(&mut self.outcome, outcome),
            ));

            self.clear_selection();
            self.exchanging = false;

            if Player::Human == self.players.get(board.turn) {
                break;
            }
        }
    }
    pub fn board_list(&self) -> Vec<Board> {
        let mut board_list: Vec<_> = self.undo_stack.iter().map(|&t| t.0).collect();
        board_list.push(self.board);
        board_list
    }
    pub fn clear_selection(&mut self) {
        self.selected_piece = None;
    }
    pub fn current_player(&self) -> Player {
        self.players.get(self.board.turn)
    }
    pub fn update_outcome(&mut self) {
        if self.outcome == Outcome::InProgress {
            // Only take positions after the last irreversible move
            let board_list: Vec<_> = self
                .board_list()
                .into_iter()
                .rev()
                .skip(1)
                .take_while(|b| b.vitals == self.board.vitals)
                .collect();

            if board_list.len() >= 8 && board_list.iter().filter(|&&b| b == self.board).count() >= 2
            {
                self.outcome = Outcome::DrawThreefoldRepetition;
            } else {
                self.outcome = self.board.outcome();
            }
        }
    }
    pub fn is_game_over(&self) -> bool {
        self.outcome != Outcome::InProgress
    }
    pub fn resign(&mut self) {
        assert_eq!(self.outcome, Outcome::InProgress);
        self.outcome = Outcome::Win(self.board.turn.switch());
    }
}

/// The outcome of a game. This includes being in progress; a win/loss by capturing all of an
/// opponent's pieces; and a draw by stalemate (no legal moves left), insufficient material, or
/// threefold repetition.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Outcome {
    InProgress,
    DrawStalemate,
    DrawInsufficientMaterial,
    DrawThreefoldRepetition,
    Win(Color),
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Player {
    Human,
    Computer,
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Color {
    White,
    Black,
}

impl Color {
    pub fn switch(&self) -> Self {
        match *self {
            Color::White => Color::Black,
            Color::Black => Color::White,
        }
    }
}

/// A map to associate any two values with the variants of the Color enum. Useful for keeping
/// track of player-specific information, which almost always comes in pairs.
#[derive(Clone, Copy, PartialEq)]
pub struct ColorMap<T> {
    pub white: T,
    pub black: T,
}

impl<T> ColorMap<T> {
    pub fn new(white: T, black: T) -> Self {
        Self { white, black }
    }
    pub fn get_ref(&self, color: Color) -> &T {
        match color {
            Color::White => &self.white,
            Color::Black => &self.black,
        }
    }
    pub fn get_mut(&mut self, color: Color) -> &mut T {
        match color {
            Color::White => &mut self.white,
            Color::Black => &mut self.black,
        }
    }
}

impl<T: Copy> ColorMap<T> {
    pub fn get(&self, color: Color) -> T {
        match color {
            Color::White => self.white,
            Color::Black => self.black,
        }
    }
}

#[derive(Clone, Copy, Debug)]
pub enum Move {
    Exchange(BitBoard, Color),
    Move(BitBoard, BitBoard, Color),
}

impl Move {
    pub fn move_from_field(from: &FieldCoord, to: &FieldCoord) -> Self {
        Move::Move(from.to_bitboard(), to.to_bitboard(), from.color())
    }
    pub fn exchange_from_field(field: &FieldCoord) -> Self {
        Move::Exchange(field.to_bitboard(), field.color())
    }
}

impl fmt::Display for Move {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Move::Move(from, to, color) => write!(
                f,
                "Move::Move({}, {}, {:?})",
                FieldCoord::from_bitboard(from, color).to_notation(),
                FieldCoord::from_bitboard(to, color).to_notation(),
                color
            ),
            Move::Exchange(bb, color) => write!(
                f,
                "Move::Exchange({}, {:?})",
                FieldCoord::from_bitboard(bb, color).to_notation(),
                color
            ),
        }
    }
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub struct FieldCoord {
    x: i8,
    y: i8,
    f: u8,
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub struct HexCoord {
    x: i8,
    y: i8,
}

impl FieldCoord {
    pub fn new(x: i8, y: i8, f: u8) -> Self {
        assert!(Self::is_valid_coord(x, y, f));
        Self { x, y, f }
    }
    pub fn from_bitboard(bb: BitBoard, color: Color) -> Self {
        Self::from_index(bb.trailing_zeros() as u8, color)
    }
    pub fn from_index(index: u8, color: Color) -> Self {
        assert!(index < 57);

        let f = 2 * (index % 3) + match color {
            Color::White => 1,
            Color::Black => 0,
        };

        Self::from_hex_f(index / 3, f)
    }
    pub fn from_hex_f(hex: u8, f: u8) -> Self {
        assert!(hex < 19);
        assert!(f < 6);

        let hex = hex as i8 + match hex {
            0...2 => 2,
            3...15 => 3,
            16...18 => 4,
            _ => unreachable!(),
        };
        Self::new(hex % 5 - 2, hex / 5 - 2, f as u8)
    }
    pub fn to_hex(&self) -> HexCoord {
        HexCoord {
            x: self.x,
            y: self.y,
        }
    }
    pub fn to_bitboard(&self) -> BitBoard {
        let hex = 5 * (self.y + 2) + self.x + 2;
        let hex = hex as u8 - match hex {
            2...4 => 2,
            6...18 => 3,
            20...22 => 4,
            _ => unreachable!(),
        };

        1 << (hex * 3 + self.f / 2)
    }
    pub fn to_notation(&self) -> String {
        let mut notation = String::with_capacity(3);

        notation.push(match self.x {
            -2 => 'a',
            -1 => 'b',
            0 => 'c',
            1 => 'd',
            2 => 'e',
            _ => unreachable!(),
        });

        let offset = 3 + if self.x < 0 { self.x } else { 0 };
        notation.push(match self.y + offset {
            1 => '1',
            2 => '2',
            3 => '3',
            4 => '4',
            5 => '5',
            _ => unreachable!(),
        });

        notation.push(match self.f {
            5 => 'a',
            4 => 'b',
            3 => 'c',
            2 => 'd',
            1 => 'e',
            0 => 'f',
            _ => unreachable!(),
        });
        notation
    }
    pub fn f(&self) -> u8 {
        self.f
    }
    pub fn color(&self) -> Color {
        if self.f % 2 == 0 {
            Color::Black
        } else {
            Color::White
        }
    }
    fn is_valid_coord(x: i8, y: i8, f: u8) -> bool {
        (x + y).abs() <= 2 && x.abs() <= 2 && y.abs() <= 2 && f < 6
    }
}

impl HexCoord {
    pub fn try_new(x: i8, y: i8) -> Option<Self> {
        if Self::is_valid_coord(x, y) {
            Some(Self { x, y })
        } else {
            None
        }
    }
    pub fn x(&self) -> i8 {
        self.x
    }
    pub fn y(&self) -> i8 {
        self.y
    }
    pub fn to_field(&self, f: u8) -> FieldCoord {
        assert!(f < 6);
        FieldCoord {
            x: self.x,
            y: self.y,
            f,
        }
    }
    pub fn to_index(&self) -> usize {
        let hex = 5 * (self.y + 2) + self.x + 2;
        hex as usize - match hex {
            2...4 => 2,
            6...18 => 3,
            20...22 => 4,
            _ => unreachable!(),
        }
    }
    fn is_valid_coord(x: i8, y: i8) -> bool {
        x.abs() <= 2 && y.abs() <= 2 && (x + y).abs() <= 2
    }
}
<commit_msg>Fix undo/redo buttons<commit_after>/*
 * Copyright (C) 2017-2018 Ryan Huang
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

pub mod bitboard;
mod board;
mod constants;
pub mod ttable;
mod zobrist;

use std::fmt;
use std::mem;

use glium::glutin::EventsLoopProxy;

use self::bitboard::BitBoard;
pub use self::board::Board;
use ai::AI;

pub struct Model {
    pub board: Board,
    pub players: ColorMap<Player>,
    pub last_move: Option<Move>,
    pub selected_piece: Option<FieldCoord>,
    pub exchanging: bool,
    pub ai: AI,
    pub ai_search_depth: u8,
    pub outcome: Outcome,
    undo_stack: Vec<(Board, Option<Move>, Outcome)>,
    redo_stack: Vec<(Board, Option<Move>, Outcome)>,
    pub events_proxy: EventsLoopProxy,
}

impl Model {
    pub fn new(players: ColorMap<Player>, events_proxy: EventsLoopProxy) -> Self {
        Self {
            board: Board::new(),
            players,
            selected_piece: None,
            last_move: None,
            exchanging: false,
            ai: AI::new(),
            ai_search_depth: 6,
            outcome: Outcome::InProgress,
            undo_stack: vec![],
            redo_stack: vec![],
            events_proxy,
        }
    }
    pub fn can_undo(&self) -> bool {
        let comp_v_comp =
            self.players.white == Player::Computer && self.players.black == Player::Computer;
        !comp_v_comp && !self.undo_stack.is_empty()
    }
    pub fn can_redo(&self) -> bool {
        let comp_v_comp =
            self.players.white == Player::Computer && self.players.black == Player::Computer;
        !comp_v_comp && !self.redo_stack.is_empty()
    }
    pub fn push_undo_state(&mut self) {
        self.undo_stack
            .push((self.board, self.last_move, self.outcome));
        self.redo_stack.clear();
    }
    pub fn undo_move(&mut self) {
        while let Some((board, last_move, outcome)) = self.undo_stack.pop() {
            self.redo_stack.push((
                mem::replace(&mut self.board, board),
                mem::replace(&mut self.last_move, last_move),
                mem::replace(&mut self.outcome, outcome),
            ));

            self.clear_selection();
            self.exchanging = false;

            if Player::Human == self.players.get(board.turn) {
                break;
            }
        }
    }
    pub fn redo_move(&mut self) {
        while let Some((board, last_move, outcome)) = self.redo_stack.pop() {
            self.undo_stack.push((
                mem::replace(&mut self.board, board),
                mem::replace(&mut self.last_move, last_move),
                mem::replace(&mut self.outcome, outcome),
            ));

            self.clear_selection();
            self.exchanging = false;

            if Player::Human == self.players.get(board.turn) {
                break;
            }
        }
    }
    pub fn board_list(&self) -> Vec<Board> {
        let mut board_list: Vec<_> = self.undo_stack.iter().map(|&t| t.0).collect();
        board_list.push(self.board);
        board_list
    }
    pub fn clear_selection(&mut self) {
        self.selected_piece = None;
    }
    pub fn current_player(&self) -> Player {
        self.players.get(self.board.turn)
    }
    pub fn update_outcome(&mut self) {
        if self.outcome == Outcome::InProgress {
            // Only take positions after the last irreversible move
            let board_list: Vec<_> = self
                .board_list()
                .into_iter()
                .rev()
                .skip(1)
                .take_while(|b| b.vitals == self.board.vitals)
                .collect();

            if board_list.len() >= 8 && board_list.iter().filter(|&&b| b == self.board).count() >= 2
            {
                self.outcome = Outcome::DrawThreefoldRepetition;
            } else {
                self.outcome = self.board.outcome();
            }
        }
    }
    pub fn is_game_over(&self) -> bool {
        self.outcome != Outcome::InProgress
    }
    pub fn resign(&mut self) {
        assert_eq!(self.outcome, Outcome::InProgress);
        self.outcome = Outcome::Win(self.board.turn.switch());
    }
}

/// The outcome of a game. This includes being in progress; a win/loss by capturing all of an
/// opponent's pieces; and a draw by stalemate (no legal moves left), insufficient material, or
/// threefold repetition.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Outcome {
    InProgress,
    DrawStalemate,
    DrawInsufficientMaterial,
    DrawThreefoldRepetition,
    Win(Color),
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Player {
    Human,
    Computer,
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Color {
    White,
    Black,
}

impl Color {
    pub fn switch(&self) -> Self {
        match *self {
            Color::White => Color::Black,
            Color::Black => Color::White,
        }
    }
}

/// A map to associate any two values with the variants of the Color enum. Useful for keeping
/// track of player-specific information, which almost always comes in pairs.
#[derive(Clone, Copy, PartialEq)]
pub struct ColorMap<T> {
    pub white: T,
    pub black: T,
}

impl<T> ColorMap<T> {
    pub fn new(white: T, black: T) -> Self {
        Self { white, black }
    }
    pub fn get_ref(&self, color: Color) -> &T {
        match color {
            Color::White => &self.white,
            Color::Black => &self.black,
        }
    }
    pub fn get_mut(&mut self, color: Color) -> &mut T {
        match color {
            Color::White => &mut self.white,
            Color::Black => &mut self.black,
        }
    }
}

impl<T: Copy> ColorMap<T> {
    pub fn get(&self, color: Color) -> T {
        match color {
            Color::White => self.white,
            Color::Black => self.black,
        }
    }
}

#[derive(Clone, Copy, Debug)]
pub enum Move {
    Exchange(BitBoard, Color),
    Move(BitBoard, BitBoard, Color),
}

impl Move {
    pub fn move_from_field(from: &FieldCoord, to: &FieldCoord) -> Self {
        Move::Move(from.to_bitboard(), to.to_bitboard(), from.color())
    }
    pub fn exchange_from_field(field: &FieldCoord) -> Self {
        Move::Exchange(field.to_bitboard(), field.color())
    }
}

impl fmt::Display for Move {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Move::Move(from, to, color) => write!(
                f,
                "Move::Move({}, {}, {:?})",
                FieldCoord::from_bitboard(from, color).to_notation(),
                FieldCoord::from_bitboard(to, color).to_notation(),
                color
            ),
            Move::Exchange(bb, color) => write!(
                f,
                "Move::Exchange({}, {:?})",
                FieldCoord::from_bitboard(bb, color).to_notation(),
                color
            ),
        }
    }
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub struct FieldCoord {
    x: i8,
    y: i8,
    f: u8,
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub struct HexCoord {
    x: i8,
    y: i8,
}

impl FieldCoord {
    pub fn new(x: i8, y: i8, f: u8) -> Self {
        assert!(Self::is_valid_coord(x, y, f));
        Self { x, y, f }
    }
    pub fn from_bitboard(bb: BitBoard, color: Color) -> Self {
        Self::from_index(bb.trailing_zeros() as u8, color)
    }
    pub fn from_index(index: u8, color: Color) -> Self {
        assert!(index < 57);

        let f = 2 * (index % 3) + match color {
            Color::White => 1,
            Color::Black => 0,
        };

        Self::from_hex_f(index / 3, f)
    }
    pub fn from_hex_f(hex: u8, f: u8) -> Self {
        assert!(hex < 19);
        assert!(f < 6);

        let hex = hex as i8 + match hex {
            0...2 => 2,
            3...15 => 3,
            16...18 => 4,
            _ => unreachable!(),
        };
        Self::new(hex % 5 - 2, hex / 5 - 2, f as u8)
    }
    pub fn to_hex(&self) -> HexCoord {
        HexCoord {
            x: self.x,
            y: self.y,
        }
    }
    pub fn to_bitboard(&self) -> BitBoard {
        let hex = 5 * (self.y + 2) + self.x + 2;
        let hex = hex as u8 - match hex {
            2...4 => 2,
            6...18 => 3,
            20...22 => 4,
            _ => unreachable!(),
        };

        1 << (hex * 3 + self.f / 2)
    }
    pub fn to_notation(&self) -> String {
        let mut notation = String::with_capacity(3);

        notation.push(match self.x {
            -2 => 'a',
            -1 => 'b',
            0 => 'c',
            1 => 'd',
            2 => 'e',
            _ => unreachable!(),
        });

        let offset = 3 + if self.x < 0 { self.x } else { 0 };
        notation.push(match self.y + offset {
            1 => '1',
            2 => '2',
            3 => '3',
            4 => '4',
            5 => '5',
            _ => unreachable!(),
        });

        notation.push(match self.f {
            5 => 'a',
            4 => 'b',
            3 => 'c',
            2 => 'd',
            1 => 'e',
            0 => 'f',
            _ => unreachable!(),
        });
        notation
    }
    pub fn f(&self) -> u8 {
        self.f
    }
    pub fn color(&self) -> Color {
        if self.f % 2 == 0 {
            Color::Black
        } else {
            Color::White
        }
    }
    fn is_valid_coord(x: i8, y: i8, f: u8) -> bool {
        (x + y).abs() <= 2 && x.abs() <= 2 && y.abs() <= 2 && f < 6
    }
}

impl HexCoord {
    pub fn try_new(x: i8, y: i8) -> Option<Self> {
        if Self::is_valid_coord(x, y) {
            Some(Self { x, y })
        } else {
            None
        }
    }
    pub fn x(&self) -> i8 {
        self.x
    }
    pub fn y(&self) -> i8 {
        self.y
    }
    pub fn to_field(&self, f: u8) -> FieldCoord {
        assert!(f < 6);
        FieldCoord {
            x: self.x,
            y: self.y,
            f,
        }
    }
    pub fn to_index(&self) -> usize {
        let hex = 5 * (self.y + 2) + self.x + 2;
        hex as usize - match hex {
            2...4 => 2,
            6...18 => 3,
            20...22 => 4,
            _ => unreachable!(),
        }
    }
    fn is_valid_coord(x: i8, y: i8) -> bool {
        x.abs() <= 2 && y.abs() <= 2 && (x + y).abs() <= 2
    }
}
<|endoftext|>