<commit_before>#![feature(box_syntax)]

extern crate asexp;
extern crate expression;

pub mod symbol;

use std::{fmt, iter};

use expression::{Expression, ExpressionError, Condition};
use expression::num_expr::NumType;
pub use expression::num_expr::NumExpr as Expr;
pub use expression::cond::Cond;

/// Used to name symbols and variables.
pub trait Alphabet: fmt::Debug + PartialEq + Eq + Clone {}

impl Alphabet for &'static str {}
impl Alphabet for char {}
impl Alphabet for u8 {}
impl Alphabet for u16 {}
impl Alphabet for u32 {}
impl Alphabet for u64 {}
impl Alphabet for usize {}

/// The common interface for Symbols. Basically this abstracts over
/// the argument implementation.
pub trait Symbol: Clone + PartialEq + fmt::Debug {
    type A: Alphabet;

    type T: NumType + Default;

    fn symbol(&self) -> &Self::A;

    fn set_symbol(&mut self, symbol: Self::A);

    fn args(&self) -> &[Expr<Self::T>];

    fn from_result_iter<I, E>(symbol: Self::A, args_iter: I) -> Result<Self, E>
        where I: Iterator<Item = Result<Expr<Self::T>, E>>;

    fn new(symbol: Self::A) -> Self {
        Self::from_iter(symbol, iter::empty())
    }

    fn from_iter<I>(symbol: Self::A, args_iter: I) -> Self
        where I: Iterator<Item = Expr<Self::T>>
    {
        let res: Result<_, ()> = Self::from_result_iter(symbol, args_iter.map(|i| Ok(i)));
        res.unwrap()
    }

    fn evaluate(&self, bindings: &[Expr<Self::T>]) -> Result<Self, ExpressionError> {
        Self::from_result_iter((*self.symbol()).clone(),
                               self.args()
                                   .iter()
                                   .map(|expr| expr.evaluate(bindings).map(|ok| Expr::Const(ok))))

    }

    fn debug_fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let args = self.args();
        try!(write!(f, "{:?}", *self.symbol()));
        if args.is_empty() {
            return Ok(());
        }

        try!(write!(f, "("));
        for (i, expr) in args.iter().enumerate() {
            if i > 0 {
                try!(write!(f, ", "));
            }
            try!(write!(f, "{:?}", expr));
        }
        write!(f, ")")
    }
}

/// A list of Symbols.
#[derive(PartialEq, Eq, Clone)]
pub struct SymbolString<S: Symbol>(pub Vec<S>);

impl<S: Symbol> fmt::Debug for SymbolString<S> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        for sym in self.0.iter() {
            try!(write!(f, "{:?}", sym));
        }
        Ok(())
    }
}

impl<S: Symbol> SymbolString<S> {
    fn from_result_iter<I, E>(symbol_iter: I) -> Result<SymbolString<S>, E>
        where I: Iterator<Item = Result<S, E>>
    {
        let mut symbols = Vec::with_capacity(symbol_iter.size_hint().0);
        for sym in symbol_iter.into_iter() {
            symbols.push(try!(sym));
        }
        Ok(SymbolString(symbols))
    }

    pub fn evaluate(&self, bindings: &[Expr<S::T>]) -> Result<SymbolString<S>, ExpressionError> {
        SymbolString::from_result_iter(self.0.iter().map(|sym| sym.evaluate(bindings)))
    }
}

#[derive(Debug, Clone)]
pub struct Rule<S: Symbol> {
    pub symbol: S::A,
    pub condition: Cond<Expr<S::T>>,
    pub successor: SymbolString<S>,
}

#[derive(Debug, Eq, PartialEq)]
pub enum RuleError {
    SymbolMismatch,
    ConditionFalse,
    ConditionFailed,
    ExprFailed(ExpressionError),
}

impl<S: Symbol> Rule<S> {
    pub fn new(symbol: S::A, successor: SymbolString<S>) -> Rule<S> {
        Rule {
            symbol: symbol,
            condition: Cond::True,
            successor: successor,
        }
    }

    pub fn new_with_condition(symbol: S::A,
                              successor: SymbolString<S>,
                              condition: Cond<Expr<S::T>>)
                              -> Rule<S> {
        Rule {
            symbol: symbol,
            condition: condition,
            successor: successor,
        }
    }

    /// Apply the rule to the given (constant) symbol and if possible, produce
    /// a successor.
    pub fn apply(&self, sym: &S) -> Result<SymbolString<S>, RuleError> {
        let args = sym.args();
        if self.symbol.eq(sym.symbol()) {
            match self.condition.evaluate(args) {
                Ok(true) => {
                    match self.successor.evaluate(args) {
                        Ok(symstr) => Ok(symstr),
                        Err(e) => Err(RuleError::ExprFailed(e)),
                    }
                }
                Ok(false) => Err(RuleError::ConditionFalse),
                _ => Err(RuleError::ConditionFailed),
            }
        } else {
            Err(RuleError::SymbolMismatch)
        }
    }
}

#[test]
fn test_rule_apply() {
    use symbol::DSym;
    let p = DSym::new_parametric("P", vec![Expr::Const(123u32)]);
    let rule = Rule::new("A", SymbolString(vec![p.clone()]));
    assert_eq!(Err(RuleError::SymbolMismatch), rule.apply(&DSym::new("P")));
    assert_eq!(Ok(SymbolString(vec![DSym::new_parametric("P", vec![Expr::Const(123u32)])])),
               rule.apply(&DSym::new("A")));

    let rule = Rule::new_with_condition("A", SymbolString(vec![p.clone()]), Cond::False);
    assert_eq!(Err(RuleError::ConditionFalse), rule.apply(&DSym::new("A")));
}

pub trait LSystem<S: Symbol> {
    fn apply_first_rule(&self, sym: &S) -> Option<SymbolString<S>>;

    /// Apply in parallel the first matching rule to each symbol in the string.
    /// Returns the total number of rule applications.
    fn develop_next(&self, axiom: &SymbolString<S>) -> (SymbolString<S>, usize) {
        let mut expanded = Vec::new();
        let mut rule_applications = 0;

        for sym in axiom.0.iter() {
            match self.apply_first_rule(sym) {
                Some(successor) => {
                    expanded.extend(successor.0);
                    rule_applications += 1;
                    // XXX: Count rule applications of the matching rule.
                }
                None => {
                    expanded.push(sym.clone());
                }
            }
        }

        (SymbolString(expanded), rule_applications)
    }

    /// Develop the system starting with `axiom` up to `max_iterations`. Return iteration count.
    fn develop(&self, axiom: SymbolString<S>, max_iterations: usize) -> (SymbolString<S>, usize) {
        let mut current = axiom;

        for iter in 0..max_iterations {
            let (next, rule_applications) = self.develop_next(&current);
            if rule_applications == 0 {
                return (current, iter);
            }
            current = next;
        }
        return (current, max_iterations);
    }
}

#[derive(Debug, Clone)]
pub struct System<S: Symbol> {
    rules: Vec<Rule<S>>,
}

impl<S: Symbol> System<S> {
    pub fn new() -> System<S> {
        System { rules: vec![] }
    }

    pub fn add_rule(&mut self, rule: Rule<S>) {
        self.rules.push(rule);
    }
}

/// Apply first matching rule and return expanded successor.
pub fn apply_first_rule<S: Symbol>(rules: &[Rule<S>], sym: &S) -> Option<SymbolString<S>> {
    for rule in rules {
        if let Ok(successor) = rule.apply(sym) {
            return Some(successor);
        }
    }
    return None;
}

impl<S: Symbol> LSystem<S> for System<S> {
    /// Apply first matching rule and return expanded successor.
    fn apply_first_rule(&self, sym: &S) -> Option<SymbolString<S>> {
        apply_first_rule(&self.rules, sym)
    }
}
<commit_msg>Remove obsolete feature box_syntax<commit_after>extern crate asexp;
extern crate expression;

pub mod symbol;

use std::{fmt, iter};

use expression::{Expression, ExpressionError, Condition};
use expression::num_expr::NumType;
pub use expression::num_expr::NumExpr as Expr;
pub use expression::cond::Cond;

/// Used to name symbols and variables.
pub trait Alphabet: fmt::Debug + PartialEq + Eq + Clone {}

impl Alphabet for &'static str {}
impl Alphabet for char {}
impl Alphabet for u8 {}
impl Alphabet for u16 {}
impl Alphabet for u32 {}
impl Alphabet for u64 {}
impl Alphabet for usize {}

/// The common interface for Symbols. Basically this abstracts over
/// the argument implementation.
pub trait Symbol: Clone + PartialEq + fmt::Debug {
    type A: Alphabet;

    type T: NumType + Default;

    fn symbol(&self) -> &Self::A;

    fn set_symbol(&mut self, symbol: Self::A);

    fn args(&self) -> &[Expr<Self::T>];

    fn from_result_iter<I, E>(symbol: Self::A, args_iter: I) -> Result<Self, E>
        where I: Iterator<Item = Result<Expr<Self::T>, E>>;

    fn new(symbol: Self::A) -> Self {
        Self::from_iter(symbol, iter::empty())
    }

    fn from_iter<I>(symbol: Self::A, args_iter: I) -> Self
        where I: Iterator<Item = Expr<Self::T>>
    {
        let res: Result<_, ()> = Self::from_result_iter(symbol, args_iter.map(|i| Ok(i)));
        res.unwrap()
    }

    fn evaluate(&self, bindings: &[Expr<Self::T>]) -> Result<Self, ExpressionError> {
        Self::from_result_iter((*self.symbol()).clone(),
                               self.args()
                                   .iter()
                                   .map(|expr| expr.evaluate(bindings).map(|ok| Expr::Const(ok))))

    }

    fn debug_fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let args = self.args();
        try!(write!(f, "{:?}", *self.symbol()));
        if args.is_empty() {
            return Ok(());
        }

        try!(write!(f, "("));
        for (i, expr) in args.iter().enumerate() {
            if i > 0 {
                try!(write!(f, ", "));
            }
            try!(write!(f, "{:?}", expr));
        }
        write!(f, ")")
    }
}

/// A list of Symbols.
#[derive(PartialEq, Eq, Clone)]
pub struct SymbolString<S: Symbol>(pub Vec<S>);

impl<S: Symbol> fmt::Debug for SymbolString<S> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        for sym in self.0.iter() {
            try!(write!(f, "{:?}", sym));
        }
        Ok(())
    }
}

impl<S: Symbol> SymbolString<S> {
    fn from_result_iter<I, E>(symbol_iter: I) -> Result<SymbolString<S>, E>
        where I: Iterator<Item = Result<S, E>>
    {
        let mut symbols = Vec::with_capacity(symbol_iter.size_hint().0);
        for sym in symbol_iter.into_iter() {
            symbols.push(try!(sym));
        }
        Ok(SymbolString(symbols))
    }

    pub fn evaluate(&self, bindings: &[Expr<S::T>]) -> Result<SymbolString<S>, ExpressionError> {
        SymbolString::from_result_iter(self.0.iter().map(|sym| sym.evaluate(bindings)))
    }
}

#[derive(Debug, Clone)]
pub struct Rule<S: Symbol> {
    pub symbol: S::A,
    pub condition: Cond<Expr<S::T>>,
    pub successor: SymbolString<S>,
}

#[derive(Debug, Eq, PartialEq)]
pub enum RuleError {
    SymbolMismatch,
    ConditionFalse,
    ConditionFailed,
    ExprFailed(ExpressionError),
}

impl<S: Symbol> Rule<S> {
    pub fn new(symbol: S::A, successor: SymbolString<S>) -> Rule<S> {
        Rule {
            symbol: symbol,
            condition: Cond::True,
            successor: successor,
        }
    }

    pub fn new_with_condition(symbol: S::A,
                              successor: SymbolString<S>,
                              condition: Cond<Expr<S::T>>)
                              -> Rule<S> {
        Rule {
            symbol: symbol,
            condition: condition,
            successor: successor,
        }
    }

    /// Apply the rule to the given (constant) symbol and if possible, produce
    /// a successor.
    pub fn apply(&self, sym: &S) -> Result<SymbolString<S>, RuleError> {
        let args = sym.args();
        if self.symbol.eq(sym.symbol()) {
            match self.condition.evaluate(args) {
                Ok(true) => {
                    match self.successor.evaluate(args) {
                        Ok(symstr) => Ok(symstr),
                        Err(e) => Err(RuleError::ExprFailed(e)),
                    }
                }
                Ok(false) => Err(RuleError::ConditionFalse),
                _ => Err(RuleError::ConditionFailed),
            }
        } else {
            Err(RuleError::SymbolMismatch)
        }
    }
}

#[test]
fn test_rule_apply() {
    use symbol::DSym;
    let p = DSym::new_parametric("P", vec![Expr::Const(123u32)]);
    let rule = Rule::new("A", SymbolString(vec![p.clone()]));
    assert_eq!(Err(RuleError::SymbolMismatch), rule.apply(&DSym::new("P")));
    assert_eq!(Ok(SymbolString(vec![DSym::new_parametric("P", vec![Expr::Const(123u32)])])),
               rule.apply(&DSym::new("A")));

    let rule = Rule::new_with_condition("A", SymbolString(vec![p.clone()]), Cond::False);
    assert_eq!(Err(RuleError::ConditionFalse), rule.apply(&DSym::new("A")));
}

pub trait LSystem<S: Symbol> {
    fn apply_first_rule(&self, sym: &S) -> Option<SymbolString<S>>;

    /// Apply in parallel the first matching rule to each symbol in the string.
    /// Returns the total number of rule applications.
    fn develop_next(&self, axiom: &SymbolString<S>) -> (SymbolString<S>, usize) {
        let mut expanded = Vec::new();
        let mut rule_applications = 0;

        for sym in axiom.0.iter() {
            match self.apply_first_rule(sym) {
                Some(successor) => {
                    expanded.extend(successor.0);
                    rule_applications += 1;
                    // XXX: Count rule applications of the matching rule.
                }
                None => {
                    expanded.push(sym.clone());
                }
            }
        }

        (SymbolString(expanded), rule_applications)
    }

    /// Develop the system starting with `axiom` up to `max_iterations`. Return iteration count.
    fn develop(&self, axiom: SymbolString<S>, max_iterations: usize) -> (SymbolString<S>, usize) {
        let mut current = axiom;

        for iter in 0..max_iterations {
            let (next, rule_applications) = self.develop_next(&current);
            if rule_applications == 0 {
                return (current, iter);
            }
            current = next;
        }
        return (current, max_iterations);
    }
}

#[derive(Debug, Clone)]
pub struct System<S: Symbol> {
    rules: Vec<Rule<S>>,
}

impl<S: Symbol> System<S> {
    pub fn new() -> System<S> {
        System { rules: vec![] }
    }

    pub fn add_rule(&mut self, rule: Rule<S>) {
        self.rules.push(rule);
    }
}

/// Apply first matching rule and return expanded successor.
pub fn apply_first_rule<S: Symbol>(rules: &[Rule<S>], sym: &S) -> Option<SymbolString<S>> {
    for rule in rules {
        if let Ok(successor) = rule.apply(sym) {
            return Some(successor);
        }
    }
    return None;
}

impl<S: Symbol> LSystem<S> for System<S> {
    /// Apply first matching rule and return expanded successor.
    fn apply_first_rule(&self, sym: &S) -> Option<SymbolString<S>> {
        apply_first_rule(&self.rules, sym)
    }
}
<|endoftext|>


<commit_before>#![crate_name = "r6"]

//! r6.rs is an attempt to implement R6RS Scheme in Rust language

#![feature(plugin)]
#![feature(box_syntax)]

#![feature(core)]
#![feature(std_misc)]
#![feature(io)]
#![feature(collections)]
#![feature(unicode)]

#![plugin(phf_macros)]
#![plugin(regex_macros)]

// This line should be at the top of the extern link list,
// because some weird compiler bug lets log imported from rustc, not crates.io log
#[macro_use] extern crate log;

extern crate phf;
extern crate regex;
extern crate unicode;
extern crate num;

macro_rules! list{
    ($($x:expr),*) => (
        vec![$($x),*].into_iter().collect()
    )
}

macro_rules! sym{
    ($e:expr) => (
        Datum::Sym(Cow::Borrowed($e))
    )
}

macro_rules! num{
    ($e:expr) => (
        Datum::Num(Number::new_int($e, 0))
    )
}

macro_rules! nil{
    () => (
        Datum::Nil
    )
}

/// Error values returned from parser, compiler or runtime
pub mod error;
/// Basic datum types
pub mod datum;
pub mod parser;
pub mod lexer;
/// Virtual machine running the bytecode
pub mod runtime;
/// Primitive functions
pub mod primitive;
/// Compiles datum into a bytecode
pub mod compiler;
/// R6RS `base` library
pub mod base;
/// Real part of the numerical tower
pub mod real;
/// Numerical tower
pub mod number;
<commit_msg>Rust changes: str_char feature to use str::char_at<commit_after>#![crate_name = "r6"]

//! r6.rs is an attempt to implement R6RS Scheme in Rust language

#![feature(plugin)]
#![feature(box_syntax)]

#![feature(core)]
#![feature(str_char)]
#![feature(std_misc)]
#![feature(io)]
#![feature(collections)]
#![feature(unicode)]

#![plugin(phf_macros)]
#![plugin(regex_macros)]

// This line should be at the top of the extern link list,
// because some weird compiler bug lets log imported from rustc, not crates.io log
#[macro_use] extern crate log;

extern crate phf;
extern crate regex;
extern crate unicode;
extern crate num;

macro_rules! list{
    ($($x:expr),*) => (
        vec![$($x),*].into_iter().collect()
    )
}

macro_rules! sym{
    ($e:expr) => (
        Datum::Sym(Cow::Borrowed($e))
    )
}

macro_rules! num{
    ($e:expr) => (
        Datum::Num(Number::new_int($e, 0))
    )
}

macro_rules! nil{
    () => (
        Datum::Nil
    )
}

/// Error values returned from parser, compiler or runtime
pub mod error;
/// Basic datum types
pub mod datum;
pub mod parser;
pub mod lexer;
/// Virtual machine running the bytecode
pub mod runtime;
/// Primitive functions
pub mod primitive;
/// Compiles datum into a bytecode
pub mod compiler;
/// R6RS `base` library
pub mod base;
/// Real part of the numerical tower
pub mod real;
/// Numerical tower
pub mod number;
<|endoftext|>


<commit_before>//! A spatial tree library

#![feature(phase)]
#![feature(unboxed_closures)]

extern crate nalgebra;

#[cfg(test)]
extern crate quickcheck;
#[cfg(test)]
#[phase(plugin)]
extern crate quickcheck_macros;
#[cfg(test)]
#[phase(plugin)]
extern crate nalgebra;
#[cfg(test)]
extern crate test;

#[warn(missing_docs)]
pub mod ntree;
#[warn(missing_docs)]
pub mod tree;
pub mod util;
<commit_msg>Warn about missing docs everywhere<commit_after>//! A spatial tree library

#![feature(phase)]
#![feature(unboxed_closures)]

extern crate nalgebra;

#[cfg(test)]
extern crate quickcheck;
#[cfg(test)]
#[phase(plugin)]
extern crate quickcheck_macros;
#[cfg(test)]
#[phase(plugin)]
extern crate nalgebra;
#[cfg(test)]
extern crate test;

#[warn(missing_docs)]
pub mod ntree;
#[warn(missing_docs)]
pub mod tree;
#[warn(missing_docs)]
pub mod util;
<|endoftext|>


<commit_before>extern crate rand;
#[cfg(unix)] extern crate libc;
#[cfg(windows)] extern crate winapi;
#[cfg(windows)] extern crate kernel32;
#[cfg(any(target_os = "macos", target_os = "ios"))] extern crate mach_o_sys;
#[cfg(all(unix, test))] extern crate nix;

mod alloc;

pub use alloc::{ Prot, mprotect, malloc, allocarray, free };

// -- memcmp --

/// Constant time memcmp.
#[inline(never)]
pub unsafe fn memcmp<T>(b1: *const T, b2: *const T, len: usize) -> i32 {
    let b1 = b1 as *const u8;
    let b2 = b2 as *const u8;
    let mut d = 0;
    for i in 0..len as isize {
        d |= *b1.offset(i) ^ *b2.offset(i);
    }
    (1 & (d as i32 - 1) >> 8) - 1
}

// -- memset / memzero --

/// General memset.
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
#[inline(never)]
pub unsafe fn memset<T>(s: *mut T, c: i32, n: usize) {
    let s = s as *mut u8;
    let c = c as u8;

    for i in 0..n as isize {
        ::std::ptr::write_volatile(s.offset(i), c);
    }
}

/// Call memset_s.
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub unsafe fn memset<T>(s: *mut T, c: i32, n: usize) {
    use mach_o_sys::ranlib;

    extern {
        fn memset_s(s: *mut libc::c_void, smax: ranlib::rsize_t, c: libc::c_int, n: ranlib::rsize_t) -> ranlib::errno_t;
    }

    if n > 0 {
        let ret = memset_s(s as *mut libc::c_void, n as ranlib::rsize_t, c, n as ranlib::rsize_t);

        if ret != 0 {
            panic!("memset_s return with error value {}", ret);
        }
    }
}

/// General memzero.
#[cfg(not(any(windows, target_os = "freebsd", target_os = "openbsd")))]
#[inline]
pub unsafe fn memzero<T>(dest: *mut T, n: usize) {
    memset(dest, 0, n);
}

/// Call explicit_bzero.
#[cfg(any(target_os = "freebsd", target_os = "openbsd"))]
pub unsafe fn memzero<T>(dest: *mut T, n: usize) {
    extern {
        fn explicit_bzero(s: *mut libc::c_void, n: libc::size_t);
    }
    explicit_bzero(dest as *mut libc::c_void, n);
}

/// Call SecureZeroMemory.
#[cfg(windows)]
pub unsafe fn memzero<T>(s: *mut T, n: usize) {
    extern "system" {
        fn RtlSecureZeroMemory(ptr: winapi::PVOID, cnt: winapi::SIZE_T);
    }
    RtlSecureZeroMemory(s as winapi::PVOID, n as winapi::SIZE_T);
}

// -- mlock / munlock --

#[cfg(not(windows))]
#[inline]
unsafe fn dontdump<T>(addr: *mut T, len: usize) {
    #[cfg(target_os = "linux")]
    libc::madvise(addr as *mut libc::c_void, len, libc::MADV_DONTDUMP);

    #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
    libc::madvise(addr as *mut libc::c_void, len, libc::MADV_NOCORE);
}

#[cfg(not(windows))]
#[inline]
unsafe fn dodump<T>(addr: *mut T, len: usize) {
    #[cfg(target_os = "linux")]
    libc::madvise(addr as *mut libc::c_void, len, libc::MADV_DODUMP);

    #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
    libc::madvise(addr as *mut libc::c_void, len, libc::MADV_CORE);
}

/// Unix mlock.
#[cfg(unix)]
pub unsafe fn mlock<T>(addr: *mut T, len: usize) -> bool {
    dontdump(addr, len);
    libc::mlock(addr as *mut libc::c_void, len) == 0
}

/// Windows VirtualLock.
#[cfg(windows)]
pub unsafe fn mlock<T>(addr: *mut T, len: usize) -> bool {
    kernel32::VirtualLock(addr as winapi::LPVOID, len as winapi::SIZE_T) != 0
}

/// Unix munlock.
#[cfg(unix)]
pub unsafe fn munlock<T>(addr: *mut T, len: usize) -> bool {
    memzero(addr, len);
    dodump(addr, len);
    libc::munlock(addr as *mut libc::c_void, len) == 0
}

/// Windows VirtualUnlock.
#[cfg(windows)]
pub unsafe fn munlock<T>(addr: *mut T, len: usize) -> bool {
    memzero(addr, len);
    kernel32::VirtualUnlock(addr as winapi::LPVOID, len as winapi::SIZE_T) != 0
}
<commit_msg>fix: windows msvc memzero fallback general<commit_after>extern crate rand;
#[cfg(unix)] extern crate libc;
#[cfg(windows)] extern crate winapi;
#[cfg(windows)] extern crate kernel32;
#[cfg(any(target_os = "macos", target_os = "ios"))] extern crate mach_o_sys;
#[cfg(all(unix, test))] extern crate nix;

mod alloc;

pub use alloc::{ Prot, mprotect, malloc, allocarray, free };

// -- memcmp --

/// Constant time memcmp.
#[inline(never)]
pub unsafe fn memcmp<T>(b1: *const T, b2: *const T, len: usize) -> i32 {
    let b1 = b1 as *const u8;
    let b2 = b2 as *const u8;
    let mut d = 0;
    for i in 0..len as isize {
        d |= *b1.offset(i) ^ *b2.offset(i);
    }
    (1 & (d as i32 - 1) >> 8) - 1
}

// -- memset / memzero --

/// General memset.
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
#[inline(never)]
pub unsafe fn memset<T>(s: *mut T, c: i32, n: usize) {
    let s = s as *mut u8;
    let c = c as u8;

    for i in 0..n as isize {
        ::std::ptr::write_volatile(s.offset(i), c);
    }
}

/// Call memset_s.
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub unsafe fn memset<T>(s: *mut T, c: i32, n: usize) {
    use mach_o_sys::ranlib;

    extern {
        fn memset_s(s: *mut libc::c_void, smax: ranlib::rsize_t, c: libc::c_int, n: ranlib::rsize_t) -> ranlib::errno_t;
    }

    if n > 0 {
        let ret = memset_s(s as *mut libc::c_void, n as ranlib::rsize_t, c, n as ranlib::rsize_t);

        if ret != 0 {
            panic!("memset_s return with error value {}", ret);
        }
    }
}

/// General memzero.
#[cfg(not(any(
    all(windows, not(target_env = "msvc")),
    target_os = "freebsd",
    target_os = "openbsd"
)))]
#[inline]
pub unsafe fn memzero<T>(dest: *mut T, n: usize) {
    memset(dest, 0, n);
}

/// Call explicit_bzero.
#[cfg(any(target_os = "freebsd", target_os = "openbsd"))]
pub unsafe fn memzero<T>(dest: *mut T, n: usize) {
    extern {
        fn explicit_bzero(s: *mut libc::c_void, n: libc::size_t);
    }
    explicit_bzero(dest as *mut libc::c_void, n);
}

/// Call SecureZeroMemory.
#[cfg(all(windows, not(target_env = "msvc")))]
pub unsafe fn memzero<T>(s: *mut T, n: usize) {
    extern "system" {
        fn RtlSecureZeroMemory(ptr: winapi::PVOID, cnt: winapi::SIZE_T);
    }
    RtlSecureZeroMemory(s as winapi::PVOID, n as winapi::SIZE_T);
}

// -- mlock / munlock --

#[cfg(not(windows))]
#[inline]
unsafe fn dontdump<T>(addr: *mut T, len: usize) {
    #[cfg(target_os = "linux")]
    libc::madvise(addr as *mut libc::c_void, len, libc::MADV_DONTDUMP);

    #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
    libc::madvise(addr as *mut libc::c_void, len, libc::MADV_NOCORE);
}

#[cfg(not(windows))]
#[inline]
unsafe fn dodump<T>(addr: *mut T, len: usize) {
    #[cfg(target_os = "linux")]
    libc::madvise(addr as *mut libc::c_void, len, libc::MADV_DODUMP);

    #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
    libc::madvise(addr as *mut libc::c_void, len, libc::MADV_CORE);
}

/// Unix mlock.
#[cfg(unix)]
pub unsafe fn mlock<T>(addr: *mut T, len: usize) -> bool {
    dontdump(addr, len);
    libc::mlock(addr as *mut libc::c_void, len) == 0
}

/// Windows VirtualLock.
#[cfg(windows)]
pub unsafe fn mlock<T>(addr: *mut T, len: usize) -> bool {
    kernel32::VirtualLock(addr as winapi::LPVOID, len as winapi::SIZE_T) != 0
}

/// Unix munlock.
#[cfg(unix)]
pub unsafe fn munlock<T>(addr: *mut T, len: usize) -> bool {
    memzero(addr, len);
    dodump(addr, len);
    libc::munlock(addr as *mut libc::c_void, len) == 0
}

/// Windows VirtualUnlock.
#[cfg(windows)]
pub unsafe fn munlock<T>(addr: *mut T, len: usize) -> bool {
    memzero(addr, len);
    kernel32::VirtualUnlock(addr as winapi::LPVOID, len as winapi::SIZE_T) != 0
}
<|endoftext|>


<commit_before>extern crate lru_cache;

pub mod board;
pub mod transposition_table;

use board::Board;
use transposition_table::TranspositionTable;

use std::i32;
use std::ops::Neg;
use std::hash::Hash;

#[derive(Copy,Clone,Debug)]
pub enum Team
{
    Enemy,
    Ally,
}

impl Team
{
    pub fn other_team(self) -> Team
    {
        match self
        {
            Team::Enemy => Team::Ally,
            Team::Ally => Team::Enemy,
        }
    }
}

#[derive(PartialEq,Eq,PartialOrd,Ord,Copy,Clone,Debug,Hash)]
pub enum Score
{
    Lose,
    Heuristic(i32),
    Win,
}

impl Neg for Score
{
    type Output = Self;
    fn neg(self) -> Self
    {
        match self
        {
            Score::Win => Score::Lose,
            Score::Lose => Score::Win,
            Score::Heuristic(val) => Score::Heuristic(-val),
        }
    }
}

#[derive(Clone,Debug,PartialEq,Eq)]
pub struct MoveStats<M>
{
    pub mv: Option<M>,
    pub score: Score,
    pub turns: u32,
    pub nodes_visited: u64,
}

#[derive(Clone)]
pub struct Minimax<B>
    where B: Board + Eq + Hash
{
    /* TODO: Some sort of caching */
    ally_ttable: TranspositionTable<B, MoveStats<B::Move>>,
    enemy_ttable: TranspositionTable<B, MoveStats<B::Move>>,
}

impl<B> Minimax<B>
    where B: Board + Eq + Hash
{
    pub fn new(ttable_size: usize) -> Minimax<B>
    {
        Minimax
        {
            ally_ttable: TranspositionTable::new(ttable_size),
            enemy_ttable: TranspositionTable::new(ttable_size),
        }
    }

    /// Minimax driver function.
    ///
    /// `turn` is the current player.
    pub fn minimax(&mut self, board: &B, turn: Team, plies: u32) -> MoveStats<B::Move>
    {
        let mut optimal_move = match turn
        {
            Team::Ally =>
                self.max(board, plies, Score::Lose, Score::Win),
            Team::Enemy =>
                self.min(board, plies, Score::Lose, Score::Win),
        };
        optimal_move.nodes_visited += 1;
        optimal_move
    }

    /// Generates best move for ally
    fn max(&mut self, board: &B, plies: u32, mut alpha: Score, beta: Score) -> MoveStats<B::Move>
    {
        let moves = board.gen_ally_moves();

        /* Fail state if you can't move */
        if moves.len() == 0
        {
            return MoveStats
            {
                mv: None,
                score: Score::Lose,
                turns: 0,
                nodes_visited: 0,
            };
        }

        /* If you cannot proceed further */
        if plies == 0 || board.is_game_over()
        {
            return MoveStats
            {
                mv: None,
                score: board.score(),
                turns: 0,
                nodes_visited: 0,
            }
        }

        let mut best = MoveStats {
            mv: None,
            score: Score::Lose,
            turns: 0,
            nodes_visited: 0,
        };

        if let Some(precomputed_move) = self.ally_ttable.get(board, plies)
        {
            return precomputed_move;
        }

        for mv in moves
        {

            /* Make a clone of the board so we don't break this one */
            let mut board_clone = board.clone();
            board_clone.do_move(&mv);

            /* Find enemy's best move */
            let enemy_move = self.min(&board_clone, plies - 1, alpha, beta);

            /* TODO: Try to postpone losing */
            if best.mv.is_none() || (enemy_move.score, -(enemy_move.turns as i32)) > (best.score, -(best.turns as i32))
            {
                best.mv = Some(mv);
                best.score = enemy_move.score;
                best.turns = enemy_move.turns + 1;
            }

            best.nodes_visited += enemy_move.nodes_visited + 1;

            /* Set α and break on β ≤ α */
            if best.score > alpha
            {
                alpha = enemy_move.score;
            }
            if alpha >= beta
            {
                break;
            }
        }

        self.ally_ttable.insert(board.clone(), best.clone(), plies);

        best
    }

    /// Generates best move for enemy
    fn min(&mut self, board: &B, plies: u32, alpha: Score, mut beta: Score) -> MoveStats<B::Move>
    {
        let moves = board.gen_enemy_moves();

        /* Fail state if you can't move */
        if moves.len() == 0
        {
            return MoveStats
            {
                mv: None,
                /* If enemy can't move, we win. */
                score: Score::Win,
                turns: 0,
                nodes_visited: 0,
            };
        }

        /* If you cannot proceed further */
        if plies == 0 || board.is_game_over()
        {
            return MoveStats
            {
                mv: None,
                score: board.score(),
                turns: 0,
                nodes_visited: 0,
            }
        }

        let mut best = MoveStats {
            mv: None,
            /* Technically doesn't matter, but for consistancy's sake */
            score: Score::Win,
            turns: 0,
            nodes_visited: 0,
        };

        if let Some(precomputed_move) = self.enemy_ttable.get(board, plies)
        {
            return precomputed_move;
        }

        for mv in moves
        {
            /* Make a clone of the board so we don't break this one */
            let mut board_clone = board.clone();
            board_clone.do_move(&mv);

            /* Find ally's best move */
            let ally_move = self.max(&board_clone, plies - 1, alpha, beta);

            /* TODO: Try to postpone losing */
            if best.mv.is_none() || (ally_move.score, -(ally_move.turns as i32)) < (best.score, -(best.turns as i32))
            {
                best.mv = Some(mv);
                best.score = ally_move.score;
                best.turns = ally_move.turns + 1;
            }

            best.nodes_visited += ally_move.nodes_visited + 1;

            /* Set β and break on β ≤ α */
            if best.score < beta
            {
                beta = ally_move.score;
            }
            if alpha >= beta
            {
                break;
            }
        }

        self.enemy_ttable.insert(board.clone(), best.clone(), plies);

        best
    }
}

#[cfg(test)]
mod tests
{
    use super::{Team, Score, Minimax, MoveStats};
    use board::Board;

    #[derive(Clone,PartialEq,Eq,Debug)]
    struct SimpleMove(usize);
    #[derive(Clone,PartialEq,Eq,Hash,Debug)]
    enum SimpleBoard
    {
        Node(Vec<SimpleBoard>),
        Leaf(Score),
    }

    /* These aren't really efficient, but they
     * work for testing.
     */
    impl Board for SimpleBoard
    {
        type Move = SimpleMove;

        fn gen_ally_moves(&self) -> Vec<Self::Move>
        {
            match *self
            {
                SimpleBoard::Node(ref v) =>
                    (0..v.len()).map(|u| SimpleMove(u)).collect(),
                SimpleBoard::Leaf(_) =>
                    /* Trick the algorithm into thinking
                     * we still have moves left
                     */
                    vec![SimpleMove(1)],
            }
        }

        fn gen_enemy_moves(&self) -> Vec<Self::Move>
        {
            match *self
            {
                SimpleBoard::Node(ref v) =>
                    (0..v.len()).map(|u| SimpleMove(u)).collect(),
                SimpleBoard::Leaf(_) =>
                    /* Trick the algorithm into thinking
                     * we still have moves left
                     */
                    vec![SimpleMove(1)],
            }
        }

        fn do_move(&mut self, mv: &Self::Move)
        {
            let child = match *self
            {
                SimpleBoard::Node(ref v) =>
                    Some(v[mv.0].clone()),
                SimpleBoard::Leaf(_) =>
                    None,
            };

            if let Some(c) = child
            {
                *self = c;
            }
        }

        fn score(&self) -> Score
        {
            match *self
            {
                SimpleBoard::Node(_) =>
                    unreachable!(),
                SimpleBoard::Leaf(s) =>
                    s,
            }
        }

        fn is_game_over(&self) -> bool
        {
            match *self
            {
                SimpleBoard::Node(_) =>
                    false,
                SimpleBoard::Leaf(_) =>
                    true,
            }
        }
    }

    #[test]
    fn test_score_ord()
    {
        assert!(Score::Win > Score::Heuristic(0));
        assert!(Score::Heuristic(0) > Score::Lose);
        assert!(Score::Win > Score::Lose);
        assert!(Score::Heuristic(100) > Score::Heuristic(0));
        assert!(Score::Heuristic(0) > Score::Heuristic(-100));
        assert!(Score::Win == Score::Win);
        assert!(Score::Lose == Score::Lose);
        assert!(Score::Heuristic(0) == Score::Heuristic(0));
    }

    #[test]
    fn test_minimax()
    {
        let mut minimax = Minimax::new();
        let game1 =
        SimpleBoard::Node(vec![
            SimpleBoard::Node(vec![
                SimpleBoard::Node(vec![
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(5)),
                        SimpleBoard::Leaf(Score::Heuristic(6)),
                    ]),
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(7)),
                        SimpleBoard::Leaf(Score::Heuristic(4)),
                        SimpleBoard::Leaf(Score::Heuristic(5)),
                    ]),
                ]),
                SimpleBoard::Node(vec![
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(3)),
                    ]),
                ]),
            ]),
            SimpleBoard::Node(vec![
                SimpleBoard::Node(vec![
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(6)),
                    ]),
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(6)),
                        SimpleBoard::Leaf(Score::Heuristic(9)),
                    ]),
                ]),
                SimpleBoard::Node(vec![
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(7)),
                    ]),
                ]),
            ]),
            SimpleBoard::Node(vec![
                SimpleBoard::Node(vec![
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(5)),
                    ]),
                ]),
                SimpleBoard::Node(vec![
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(9)),
                        SimpleBoard::Leaf(Score::Heuristic(8)),
                    ]),
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(6)),
                    ]),
                ]),
            ]),
        ]);
        let game2 =
        SimpleBoard::Node(vec![
            SimpleBoard::Node(vec![
                SimpleBoard::Node(vec![
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(12)),
                        SimpleBoard::Leaf(Score::Heuristic(10)),
                    ]),
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(-18)),
                        SimpleBoard::Leaf(Score::Heuristic(-7)),
                    ]),
                ]),
                SimpleBoard::Node(vec![
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(6)),
                        SimpleBoard::Leaf(Score::Heuristic(-17)),
                    ]),
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(-3)),
                        SimpleBoard::Leaf(Score::Heuristic(6)),
                    ]),
                ]),
            ]),
            SimpleBoard::Node(vec![
                SimpleBoard::Node(vec![
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(-19)),
                        SimpleBoard::Leaf(Score::Heuristic(-16)),
                    ]),
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(-4)),
                        SimpleBoard::Leaf(Score::Heuristic(-6)),
                    ]),
                ]),
                SimpleBoard::Node(vec![
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(-1)),
                        SimpleBoard::Leaf(Score::Heuristic(6)),
                    ]),
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(9)),
                        SimpleBoard::Leaf(Score::Heuristic(15)),
                    ]),
                ]),
            ]),
        ]);

        println!();
        println!("Game 1");
        assert_eq!(game1.gen_ally_moves(), vec![SimpleMove(0), SimpleMove(1), SimpleMove(2)]);
        let move_stats1 = minimax.minimax(&game1, Team::Ally, 4);
        let optimal_move1 = MoveStats {
            mv: Some(SimpleMove(1)),
            score: Score::Heuristic(6),
            turns: 4,
            nodes_visited: 25,
        };
        assert_eq!(move_stats1, optimal_move1);

        println!();
        println!("Game 2");
        assert_eq!(game2.gen_ally_moves(), vec![SimpleMove(0), SimpleMove(1)]);
        let move_stats2 = minimax.minimax(&game2, Team::Ally, 4);
        let optimal_move2 = MoveStats {
            mv: Some(SimpleMove(0)),
            score: Score::Heuristic(-3),
            turns: 4,
            nodes_visited: 21,
        };
        assert_eq!(move_stats2, optimal_move2);

        println!();
        println!("Testing caching");
        assert_eq!(game2.gen_ally_moves(), vec![SimpleMove(0), SimpleMove(1)]);
        let move_stats2 = minimax.minimax(&game2, Team::Ally, 4);
        let optimal_move2 = MoveStats {
            mv: Some(SimpleMove(0)),
            score: Score::Heuristic(-3),
            turns: 4,
            nodes_visited: 21,
        };
        assert_eq!(move_stats2, optimal_move2);
    }
}
<commit_msg>minimax: updated tests to reflect new api<commit_after>extern crate lru_cache;

pub mod board;
pub mod transposition_table;

use board::Board;
use transposition_table::TranspositionTable;

use std::i32;
use std::ops::Neg;
use std::hash::Hash;

#[derive(Copy,Clone,Debug)]
pub enum Team
{
    Enemy,
    Ally,
}

impl Team
{
    pub fn other_team(self) -> Team
    {
        match self
        {
            Team::Enemy => Team::Ally,
            Team::Ally => Team::Enemy,
        }
    }
}

#[derive(PartialEq,Eq,PartialOrd,Ord,Copy,Clone,Debug,Hash)]
pub enum Score
{
    Lose,
    Heuristic(i32),
    Win,
}

impl Neg for Score
{
    type Output = Self;
    fn neg(self) -> Self
    {
        match self
        {
            Score::Win => Score::Lose,
            Score::Lose => Score::Win,
            Score::Heuristic(val) => Score::Heuristic(-val),
        }
    }
}

#[derive(Clone,Debug,PartialEq,Eq)]
pub struct MoveStats<M>
{
    pub mv: Option<M>,
    pub score: Score,
    pub turns: u32,
    pub nodes_visited: u64,
}

#[derive(Clone)]
pub struct Minimax<B>
    where B: Board + Eq + Hash
{
    /* TODO: Some sort of caching */
    ally_ttable: TranspositionTable<B, MoveStats<B::Move>>,
    enemy_ttable: TranspositionTable<B, MoveStats<B::Move>>,
}

impl<B> Minimax<B>
    where B: Board + Eq + Hash
{
    pub fn new(ttable_size: usize) -> Minimax<B>
    {
        Minimax
        {
            ally_ttable: TranspositionTable::new(ttable_size),
            enemy_ttable: TranspositionTable::new(ttable_size),
        }
    }

    /// Minimax driver function.
    ///
    /// `turn` is the current player.
    pub fn minimax(&mut self, board: &B, turn: Team, plies: u32) -> MoveStats<B::Move>
    {
        let mut optimal_move = match turn
        {
            Team::Ally =>
                self.max(board, plies, Score::Lose, Score::Win),
            Team::Enemy =>
                self.min(board, plies, Score::Lose, Score::Win),
        };
        optimal_move.nodes_visited += 1;
        optimal_move
    }

    /// Generates best move for ally
    fn max(&mut self, board: &B, plies: u32, mut alpha: Score, beta: Score) -> MoveStats<B::Move>
    {
        let moves = board.gen_ally_moves();

        /* Fail state if you can't move */
        if moves.len() == 0
        {
            return MoveStats
            {
                mv: None,
                score: Score::Lose,
                turns: 0,
                nodes_visited: 0,
            };
        }

        /* If you cannot proceed further */
        if plies == 0 || board.is_game_over()
        {
            return MoveStats
            {
                mv: None,
                score: board.score(),
                turns: 0,
                nodes_visited: 0,
            }
        }

        let mut best = MoveStats {
            mv: None,
            score: Score::Lose,
            turns: 0,
            nodes_visited: 0,
        };

        if let Some(precomputed_move) = self.ally_ttable.get(board, plies)
        {
            return precomputed_move;
        }

        for mv in moves
        {

            /* Make a clone of the board so we don't break this one */
            let mut board_clone = board.clone();
            board_clone.do_move(&mv);

            /* Find enemy's best move */
            let enemy_move = self.min(&board_clone, plies - 1, alpha, beta);

            /* TODO: Try to postpone losing */
            if best.mv.is_none() || (enemy_move.score, -(enemy_move.turns as i32)) > (best.score, -(best.turns as i32))
            {
                best.mv = Some(mv);
                best.score = enemy_move.score;
                best.turns = enemy_move.turns + 1;
            }

            best.nodes_visited += enemy_move.nodes_visited + 1;

            /* Set α and break on β ≤ α */
            if best.score > alpha
            {
                alpha = enemy_move.score;
            }
            if alpha >= beta
            {
                break;
            }
        }

        self.ally_ttable.insert(board.clone(), best.clone(), plies);

        best
    }

    /// Generates best move for enemy
    fn min(&mut self, board: &B, plies: u32, alpha: Score, mut beta: Score) -> MoveStats<B::Move>
    {
        let moves = board.gen_enemy_moves();

        /* Fail state if you can't move */
        if moves.len() == 0
        {
            return MoveStats
            {
                mv: None,
                /* If enemy can't move, we win. */
                score: Score::Win,
                turns: 0,
                nodes_visited: 0,
            };
        }

        /* If you cannot proceed further */
        if plies == 0 || board.is_game_over()
        {
            return MoveStats
            {
                mv: None,
                score: board.score(),
                turns: 0,
                nodes_visited: 0,
            }
        }

        let mut best = MoveStats {
            mv: None,
            /* Technically doesn't matter, but for consistancy's sake */
            score: Score::Win,
            turns: 0,
            nodes_visited: 0,
        };

        if let Some(precomputed_move) = self.enemy_ttable.get(board, plies)
        {
            return precomputed_move;
        }

        for mv in moves
        {
            /* Make a clone of the board so we don't break this one */
            let mut board_clone = board.clone();
            board_clone.do_move(&mv);

            /* Find ally's best move */
            let ally_move = self.max(&board_clone, plies - 1, alpha, beta);

            /* TODO: Try to postpone losing */
            if best.mv.is_none() || (ally_move.score, -(ally_move.turns as i32)) < (best.score, -(best.turns as i32))
            {
                best.mv = Some(mv);
                best.score = ally_move.score;
                best.turns = ally_move.turns + 1;
            }

            best.nodes_visited += ally_move.nodes_visited + 1;

            /* Set β and break on β ≤ α */
            if best.score < beta
            {
                beta = ally_move.score;
            }
            if alpha >= beta
            {
                break;
            }
        }

        self.enemy_ttable.insert(board.clone(), best.clone(), plies);

        best
    }
}

#[cfg(test)]
mod tests
{
    use super::{Team, Score, Minimax, MoveStats};
    use board::Board;

    #[derive(Clone,PartialEq,Eq,Debug)]
    struct SimpleMove(usize);
    #[derive(Clone,PartialEq,Eq,Hash,Debug)]
    enum SimpleBoard
    {
        Node(Vec<SimpleBoard>),
        Leaf(Score),
    }

    /* These aren't really efficient, but they
     * work for testing.
     */
    impl Board for SimpleBoard
    {
        type Move = SimpleMove;

        fn gen_ally_moves(&self) -> Vec<Self::Move>
        {
            match *self
            {
                SimpleBoard::Node(ref v) =>
                    (0..v.len()).map(|u| SimpleMove(u)).collect(),
                SimpleBoard::Leaf(_) =>
                    /* Trick the algorithm into thinking
                     * we still have moves left
                     */
                    vec![SimpleMove(1)],
            }
        }

        fn gen_enemy_moves(&self) -> Vec<Self::Move>
        {
            match *self
            {
                SimpleBoard::Node(ref v) =>
                    (0..v.len()).map(|u| SimpleMove(u)).collect(),
                SimpleBoard::Leaf(_) =>
                    /* Trick the algorithm into thinking
                     * we still have moves left
                     */
                    vec![SimpleMove(1)],
            }
        }

        fn do_move(&mut self, mv: &Self::Move)
        {
            let child = match *self
            {
                SimpleBoard::Node(ref v) =>
                    Some(v[mv.0].clone()),
                SimpleBoard::Leaf(_) =>
                    None,
            };

            if let Some(c) = child
            {
                *self = c;
            }
        }

        fn score(&self) -> Score
        {
            match *self
            {
                SimpleBoard::Node(_) =>
                    unreachable!(),
                SimpleBoard::Leaf(s) =>
                    s,
            }
        }

        fn is_game_over(&self) -> bool
        {
            match *self
            {
                SimpleBoard::Node(_) =>
                    false,
                SimpleBoard::Leaf(_) =>
                    true,
            }
        }
    }

    #[test]
    fn test_score_ord()
    {
        assert!(Score::Win > Score::Heuristic(0));
        assert!(Score::Heuristic(0) > Score::Lose);
        assert!(Score::Win > Score::Lose);
        assert!(Score::Heuristic(100) > Score::Heuristic(0));
        assert!(Score::Heuristic(0) > Score::Heuristic(-100));
        assert!(Score::Win == Score::Win);
        assert!(Score::Lose == Score::Lose);
        assert!(Score::Heuristic(0) == Score::Heuristic(0));
    }

    #[test]
    fn test_minimax()
    {
        let mut minimax = Minimax::new(100);
        let game1 =
        SimpleBoard::Node(vec![
            SimpleBoard::Node(vec![
                SimpleBoard::Node(vec![
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(5)),
                        SimpleBoard::Leaf(Score::Heuristic(6)),
                    ]),
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(7)),
                        SimpleBoard::Leaf(Score::Heuristic(4)),
                        SimpleBoard::Leaf(Score::Heuristic(5)),
                    ]),
                ]),
                SimpleBoard::Node(vec![
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(3)),
                    ]),
                ]),
            ]),
            SimpleBoard::Node(vec![
                SimpleBoard::Node(vec![
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(6)),
                    ]),
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(6)),
                        SimpleBoard::Leaf(Score::Heuristic(9)),
                    ]),
                ]),
                SimpleBoard::Node(vec![
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(7)),
                    ]),
                ]),
            ]),
            SimpleBoard::Node(vec![
                SimpleBoard::Node(vec![
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(5)),
                    ]),
                ]),
                SimpleBoard::Node(vec![
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(9)),
                        SimpleBoard::Leaf(Score::Heuristic(8)),
                    ]),
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(6)),
                    ]),
                ]),
            ]),
        ]);
        let game2 =
        SimpleBoard::Node(vec![
            SimpleBoard::Node(vec![
                SimpleBoard::Node(vec![
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(12)),
                        SimpleBoard::Leaf(Score::Heuristic(10)),
                    ]),
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(-18)),
                        SimpleBoard::Leaf(Score::Heuristic(-7)),
                    ]),
                ]),
                SimpleBoard::Node(vec![
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(6)),
                        SimpleBoard::Leaf(Score::Heuristic(-17)),
                    ]),
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(-3)),
                        SimpleBoard::Leaf(Score::Heuristic(6)),
                    ]),
                ]),
            ]),
            SimpleBoard::Node(vec![
                SimpleBoard::Node(vec![
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(-19)),
                        SimpleBoard::Leaf(Score::Heuristic(-16)),
                    ]),
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(-4)),
                        SimpleBoard::Leaf(Score::Heuristic(-6)),
                    ]),
                ]),
                SimpleBoard::Node(vec![
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(-1)),
                        SimpleBoard::Leaf(Score::Heuristic(6)),
                    ]),
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(9)),
                        SimpleBoard::Leaf(Score::Heuristic(15)),
                    ]),
                ]),
            ]),
        ]);

        println!();
        println!("Game 1");
        assert_eq!(game1.gen_ally_moves(), vec![SimpleMove(0), SimpleMove(1), SimpleMove(2)]);
        let move_stats1 = minimax.minimax(&game1, Team::Ally, 4);
        let optimal_move1 = MoveStats {
            mv: Some(SimpleMove(1)),
            score: Score::Heuristic(6),
            turns: 4,
            nodes_visited: 25,
        };
        assert_eq!(move_stats1, optimal_move1);

        println!();
        println!("Game 2");
        assert_eq!(game2.gen_ally_moves(), vec![SimpleMove(0), SimpleMove(1)]);
        let move_stats2 = minimax.minimax(&game2, Team::Ally, 4);
        let optimal_move2 = MoveStats {
            mv: Some(SimpleMove(0)),
            score: Score::Heuristic(-3),
            turns: 4,
            nodes_visited: 21,
        };
        assert_eq!(move_stats2, optimal_move2);

        println!();
        println!("Testing caching");
        assert_eq!(game2.gen_ally_moves(), vec![SimpleMove(0), SimpleMove(1)]);
        let move_stats2 = minimax.minimax(&game2, Team::Ally, 4);
        let optimal_move2 = MoveStats {
            mv: Some(SimpleMove(0)),
            score: Score::Heuristic(-3),
            turns: 4,
            nodes_visited: 21,
        };
        assert_eq!(move_stats2, optimal_move2);
    }
}
<|endoftext|>


<commit_before>#![allow(non_camel_case_types)]

extern crate libc;
use libc::{c_int, c_uint, c_char, c_uchar, c_void, c_double, int32_t, uint32_t, int16_t, uint16_t, int8_t};

pub const FREENECT_COUNTS_PER_G: 		c_int = 829;
pub const FREENECT_DEPTH_MM_MAX_VALUE: 	c_int = 10000;
pub const FREENECT_DEPTH_MM_NO_VALUE: 	c_int = 0;
pub const FREENECT_DEPTH_RAW_MAX_VALUE: c_int = 2048;
pub const FREENECT_DEPTH_RAW_NO_VALUE: 	c_int = 2047;

#[repr(C)] pub enum freenect_device_flags {
	FREENECT_DEVICE_MOTOR  = 0x01,
	FREENECT_DEVICE_CAMERA = 0x02,
	FREENECT_DEVICE_AUDIO  = 0x04,
}

#[repr(C)] pub struct freenect_device_attributes {
	pub next: *mut freenect_device_attributes,
	pub camera_serial: *const c_char,
}

#[repr(C)] pub enum freenect_resolution {
	FREENECT_RESOLUTION_LOW    = 0, // QVGA - 320x240
	FREENECT_RESOLUTION_MEDIUM = 1, // VGA  - 640x480
	FREENECT_RESOLUTION_HIGH   = 2, // SXGA - 1280x1024
	FREENECT_RESOLUTION_DUMMY  = 2147483647, // Dummy value to force enum to be 32 bits wide
}

#[repr(C)] pub enum freenect_video_format {
	FREENECT_VIDEO_RGB             = 0, // Decompressed RGB mode (demosaicing done by libfreenect)
	FREENECT_VIDEO_BAYER           = 1, // Bayer compressed mode (raw information from camera)
	FREENECT_VIDEO_IR_8BIT         = 2, // 8-bit IR mode
	FREENECT_VIDEO_IR_10BIT        = 3, // 10-bit IR mode
	FREENECT_VIDEO_IR_10BIT_PACKED = 4, // 10-bit packed IR mode
	FREENECT_VIDEO_YUV_RGB         = 5, // YUV RGB mode
	FREENECT_VIDEO_YUV_RAW         = 6, // YUV Raw mode
	FREENECT_VIDEO_DUMMY           = 2147483647, // Dummy value to force enum to be 32 bits wide
}

#[repr(C)] pub enum freenect_depth_format {
	FREENECT_DEPTH_11BIT        = 0, // 11 bit depth information in one uint16_t/pixel
	FREENECT_DEPTH_10BIT        = 1, // 10 bit depth information in one uint16_t/pixel
	FREENECT_DEPTH_11BIT_PACKED = 2, // 11 bit packed depth information
	FREENECT_DEPTH_10BIT_PACKED = 3, // 10 bit packed depth information
	FREENECT_DEPTH_REGISTERED   = 4, // processed depth data in mm, aligned to 640x480 RGB
	FREENECT_DEPTH_MM           = 5, // depth to each pixel in mm, but left unaligned to RGB image
	FREENECT_DEPTH_DUMMY        = 2147483647, // Dummy value to force enum to be 32 bits wide
}

#[repr(C)] pub enum freenect_flag {
	// values written to the CMOS register
	FREENECT_AUTO_EXPOSURE      = 1 << 14,
	FREENECT_AUTO_WHITE_BALANCE = 1 << 1,
	FREENECT_RAW_COLOR          = 1 << 4,
	// registers to be written with 0 or 1
	FREENECT_MIRROR_DEPTH       = 0x0017,
	FREENECT_MIRROR_VIDEO       = 0x0047,
}

#[repr(C)] pub enum freenect_flag_value {
	FREENECT_OFF = 0,
	FREENECT_ON  = 1,
}

#[repr(C)] pub struct freenect_frame_mode {
	pub reserved: uint32_t,              // unique ID used internally.  The meaning of values may change without notice.  Don't touch or depend on the contents of this field.  We mean it.
	pub resolution: freenect_resolution, // Resolution this freenect_frame_mode describes, should you want to find it again with freenect_find_*_frame_mode().
	pub dummy: int32_t,                  // The video or depth format that this freenect_frame_mode describes.  The caller should know which of video_format or depth_format to use, since they called freenect_get_*_frame_mode()
	pub bytes: int32_t,                  // Total buffer size in bytes to hold a single frame of data.  Should be equivalent to width * height * (data_bits_per_pixel+padding_bits_per_pixel) / 8
	pub width: int16_t,                  // Width of the frame, in pixels
	pub height: int16_t,                 // Height of the frame, in pixels
	pub data_bits_per_pixel: int8_t,     // Number of bits of information needed for each pixel
	pub padding_bits_per_pixel: int8_t,  // Number of bits of padding for alignment used for each pixel
	pub framerate: int8_t,               // Approximate expected frame rate, in Hz
	pub is_valid: int8_t,                // If 0, this freenect_frame_mode is invalid and does not describe a supported mode.  Otherwise, the frame_mode is valid.
}

#[repr(C)] pub enum freenect_led_option {
	LED_OFF              = 0, // Turn LED off
	LED_GREEN            = 1, // Turn LED to Green
	LED_RED              = 2, // Turn LED to Red
	LED_YELLOW           = 3, // Turn LED to Yellow
	LED_BLINK_GREEN      = 4, // Make LED blink Green
	// 5 is same as 4, LED blink Green
	LED_BLINK_RED_YELLOW = 6, // Make LED blink Red/Yellow
}

#[repr(C)] pub enum freenect_tilt_status_code {
	TILT_STATUS_STOPPED = 0x00, // Tilt motor is stopped
	TILT_STATUS_LIMIT   = 0x01, // Tilt motor has reached movement limit
	TILT_STATUS_MOVING  = 0x04, // Tilt motor is currently moving to new position
}

#[repr(C)] pub struct freenect_raw_tilt_state {
	pub accelerometer_x: int16_t                  , // Raw accelerometer data for X-axis, see FREENECT_COUNTS_PER_G for conversion
	pub accelerometer_y: int16_t                  , // Raw accelerometer data for Y-axis, see FREENECT_COUNTS_PER_G for conversion
	pub accelerometer_z: int16_t                  , // Raw accelerometer data for Z-axis, see FREENECT_COUNTS_PER_G for conversion
	pub tilt_angle:      int8_t                   , // Raw tilt motor angle encoder information
	pub tilt_status: 	 freenect_tilt_status_code, // State of the tilt motor (stopped, moving, etc...)
}

pub enum freenect_context {}

pub enum freenect_device {}

pub enum freenect_usb_context {}

#[repr(C)] pub enum freenect_loglevel {
	FREENECT_LOG_FATAL = 0,     // Log for crashing/non-recoverable errors
	FREENECT_LOG_ERROR,         // Log for major errors
	FREENECT_LOG_WARNING,       // Log for warning messages
	FREENECT_LOG_NOTICE,        // Log for important messages
	FREENECT_LOG_INFO,          // Log for normal messages
	FREENECT_LOG_DEBUG,         // Log for useful development messages
	FREENECT_LOG_SPEW,          // Log for slightly less useful messages
	FREENECT_LOG_FLOOD,         // Log EVERYTHING. May slow performance.
}

pub type freenect_log_cb   = extern fn(dev: *mut freenect_context, level: freenect_loglevel, msg: *const c_char);
pub type freenect_depth_cb = extern fn(dev: *mut freenect_device, depth: *mut c_void, timestamp: uint32_t);
pub type freenect_video_cb = extern fn(dev: *mut freenect_device, video: *mut c_void, timestamp: uint32_t);
pub type freenect_chunk_cb = extern fn(buffer: *mut c_void, pkt_data: *mut c_void, pkt_num: c_int, datalen: c_int, user_data: *mut c_void);

#[link(name = "freenect")]
extern "C" {
    pub fn freenect_init(ctx: *mut *mut freenect_context, usb_ctx: *mut freenect_usb_context) -> c_int;
	pub fn freenect_shutdown(ctx: *mut freenect_context) -> c_int;
	pub fn freenect_set_log_level(ctx: *mut freenect_context, level: freenect_loglevel);
	pub fn freenect_set_log_callback(ctx: *mut freenect_context, cb: freenect_log_cb);
	pub fn freenect_process_events(ctx: *mut freenect_context) -> c_int;
	pub fn freenect_process_events_timeout(ctx: *mut freenect_context, timeout: libc::timeval) -> c_int;
	pub fn freenect_num_devices(ctx: *mut freenect_context) -> c_int;
	pub fn freenect_list_device_attributes(ctx: *mut freenect_context, attribute_list: *mut *mut freenect_device_attributes) -> c_int;
	pub fn freenect_free_device_attributes(ctx: *mut freenect_context, attribute_list: *mut freenect_device_attributes);
	pub fn freenect_supported_subdevices() -> c_int;
	pub fn freenect_select_subdevices(ctx: *mut freenect_context, subdevs: u32); // Not enum type as bitfield can have multiple choices
	pub fn freenect_enabled_subdevices(ctx: *mut freenect_context) -> freenect_device_flags;
	pub fn freenect_open_device(ctx: *mut freenect_context, dev: *mut *mut freenect_device, index: c_int) -> c_int;
	pub fn freenect_open_device_by_camera_serial(ctx: *mut freenect_context, dev: *mut *mut freenect_device, camera_serial: *const c_char) -> c_int;
	pub fn freenect_close_device(dev: *mut freenect_device) -> c_int;
	pub fn freenect_set_user(dev: *mut freenect_device, user: *mut c_void);
	pub fn freenect_get_user(dev: *mut freenect_device) -> *mut c_void;
	pub fn freenect_set_depth_callback(dev: *mut freenect_device, cb: freenect_depth_cb);
	pub fn freenect_set_video_callback(dev: *mut freenect_device, cb: freenect_video_cb);
	pub fn freenect_set_depth_chunk_callback(dev: *mut freenect_device, cb: freenect_chunk_cb);
	pub fn freenect_set_video_chunk_callback(dev: *mut freenect_device, cb: freenect_chunk_cb);
	pub fn freenect_set_depth_buffer(dev: *mut freenect_device, buf: *mut c_void) -> c_int;
	pub fn freenect_set_video_buffer(dev: *mut freenect_device, buf: *mut c_void) -> c_int;
	pub fn freenect_start_depth(dev: *mut freenect_device) -> c_int;
	pub fn freenect_start_video(dev: *mut freenect_device) -> c_int;
	pub fn freenect_stop_depth(dev: *mut freenect_device) -> c_int;
	pub fn freenect_stop_video(dev: *mut freenect_device) -> c_int;
	pub fn freenect_update_tilt_state(dev: *mut freenect_device) -> c_int;
	pub fn freenect_get_tilt_state(dev: *mut freenect_device) -> *mut freenect_raw_tilt_state;
	pub fn freenect_get_tilt_degs(state: *mut freenect_raw_tilt_state) -> c_double;
	pub fn freenect_set_tilt_degs(dev: *mut freenect_device, angle: c_double) -> c_int;
	pub fn freenect_get_tilt_status(state: *mut freenect_raw_tilt_state) -> freenect_tilt_status_code;
	pub fn freenect_set_led(dev: *mut freenect_device, option: freenect_led_option) -> c_int;
	pub fn freenect_get_mks_accel(state: *mut freenect_raw_tilt_state, x: *mut c_double, y: *mut c_double, z: *mut c_double);
	pub fn freenect_get_video_mode_count() -> c_int;
	pub fn freenect_get_video_mode(mode_num: c_int) -> freenect_frame_mode;
	pub fn freenect_get_current_video_mode(dev: *mut freenect_device) -> freenect_frame_mode;
	pub fn freenect_find_video_mode(res: freenect_resolution, fmt: freenect_video_format) -> freenect_frame_mode;
	pub fn freenect_set_video_mode(dev: *mut freenect_device, mode: freenect_frame_mode) -> c_int;
	pub fn freenect_get_depth_mode_count() -> c_int;
	pub fn freenect_get_depth_mode(mode_num: c_int) -> freenect_frame_mode;
	pub fn freenect_get_current_depth_mode(dev: *mut freenect_device) -> freenect_frame_mode;
	pub fn freenect_find_depth_mode(res: freenect_resolution, fmt: freenect_depth_format) -> freenect_frame_mode;
	pub fn freenect_set_depth_mode(dev: *mut freenect_device, mode: freenect_frame_mode) -> c_int;
	pub fn freenect_set_flag(dev: *mut freenect_device, flag: freenect_flag, value: freenect_flag_value) -> c_int;
	pub fn freenect_get_ir_brightness(dev: *mut freenect_device) -> c_int;
	pub fn freenect_set_ir_brightness(dev: *mut freenect_device, brightness: uint16_t) -> c_int;
	pub fn freenect_set_fw_address_nui(ctx: *mut freenect_context, fw_ptr: *mut c_uchar, num_bytes: c_uint);
	pub fn freenect_set_fw_address_k4w(ctx: *mut freenect_context, fw_ptr: *mut c_uchar, num_bytes: c_uint);
}
<commit_msg>Fix incorrect function prototype for freenect_free_device_attributes<commit_after>#![allow(non_camel_case_types)]

extern crate libc;
use libc::{c_int, c_uint, c_char, c_uchar, c_void, c_double, int32_t, uint32_t, int16_t, uint16_t, int8_t};

pub const FREENECT_COUNTS_PER_G: 		c_int = 829;
pub const FREENECT_DEPTH_MM_MAX_VALUE: 	c_int = 10000;
pub const FREENECT_DEPTH_MM_NO_VALUE: 	c_int = 0;
pub const FREENECT_DEPTH_RAW_MAX_VALUE: c_int = 2048;
pub const FREENECT_DEPTH_RAW_NO_VALUE: 	c_int = 2047;

#[repr(C)] pub enum freenect_device_flags {
	FREENECT_DEVICE_MOTOR  = 0x01,
	FREENECT_DEVICE_CAMERA = 0x02,
	FREENECT_DEVICE_AUDIO  = 0x04,
}

#[repr(C)] pub struct freenect_device_attributes {
	pub next: *mut freenect_device_attributes,
	pub camera_serial: *const c_char,
}

#[repr(C)] pub enum freenect_resolution {
	FREENECT_RESOLUTION_LOW    = 0, // QVGA - 320x240
	FREENECT_RESOLUTION_MEDIUM = 1, // VGA  - 640x480
	FREENECT_RESOLUTION_HIGH   = 2, // SXGA - 1280x1024
	FREENECT_RESOLUTION_DUMMY  = 2147483647, // Dummy value to force enum to be 32 bits wide
}

#[repr(C)] pub enum freenect_video_format {
	FREENECT_VIDEO_RGB             = 0, // Decompressed RGB mode (demosaicing done by libfreenect)
	FREENECT_VIDEO_BAYER           = 1, // Bayer compressed mode (raw information from camera)
	FREENECT_VIDEO_IR_8BIT         = 2, // 8-bit IR mode
	FREENECT_VIDEO_IR_10BIT        = 3, // 10-bit IR mode
	FREENECT_VIDEO_IR_10BIT_PACKED = 4, // 10-bit packed IR mode
	FREENECT_VIDEO_YUV_RGB         = 5, // YUV RGB mode
	FREENECT_VIDEO_YUV_RAW         = 6, // YUV Raw mode
	FREENECT_VIDEO_DUMMY           = 2147483647, // Dummy value to force enum to be 32 bits wide
}

#[repr(C)] pub enum freenect_depth_format {
	FREENECT_DEPTH_11BIT        = 0, // 11 bit depth information in one uint16_t/pixel
	FREENECT_DEPTH_10BIT        = 1, // 10 bit depth information in one uint16_t/pixel
	FREENECT_DEPTH_11BIT_PACKED = 2, // 11 bit packed depth information
	FREENECT_DEPTH_10BIT_PACKED = 3, // 10 bit packed depth information
	FREENECT_DEPTH_REGISTERED   = 4, // processed depth data in mm, aligned to 640x480 RGB
	FREENECT_DEPTH_MM           = 5, // depth to each pixel in mm, but left unaligned to RGB image
	FREENECT_DEPTH_DUMMY        = 2147483647, // Dummy value to force enum to be 32 bits wide
}

#[repr(C)] pub enum freenect_flag {
	// values written to the CMOS register
	FREENECT_AUTO_EXPOSURE      = 1 << 14,
	FREENECT_AUTO_WHITE_BALANCE = 1 << 1,
	FREENECT_RAW_COLOR          = 1 << 4,
	// registers to be written with 0 or 1
	FREENECT_MIRROR_DEPTH       = 0x0017,
	FREENECT_MIRROR_VIDEO       = 0x0047,
}

#[repr(C)] pub enum freenect_flag_value {
	FREENECT_OFF = 0,
	FREENECT_ON  = 1,
}

#[repr(C)] pub struct freenect_frame_mode {
	pub reserved: uint32_t,              // unique ID used internally.  The meaning of values may change without notice.  Don't touch or depend on the contents of this field.  We mean it.
	pub resolution: freenect_resolution, // Resolution this freenect_frame_mode describes, should you want to find it again with freenect_find_*_frame_mode().
	pub dummy: int32_t,                  // The video or depth format that this freenect_frame_mode describes.  The caller should know which of video_format or depth_format to use, since they called freenect_get_*_frame_mode()
	pub bytes: int32_t,                  // Total buffer size in bytes to hold a single frame of data.  Should be equivalent to width * height * (data_bits_per_pixel+padding_bits_per_pixel) / 8
	pub width: int16_t,                  // Width of the frame, in pixels
	pub height: int16_t,                 // Height of the frame, in pixels
	pub data_bits_per_pixel: int8_t,     // Number of bits of information needed for each pixel
	pub padding_bits_per_pixel: int8_t,  // Number of bits of padding for alignment used for each pixel
	pub framerate: int8_t,               // Approximate expected frame rate, in Hz
	pub is_valid: int8_t,                // If 0, this freenect_frame_mode is invalid and does not describe a supported mode.  Otherwise, the frame_mode is valid.
}

#[repr(C)] pub enum freenect_led_option {
	LED_OFF              = 0, // Turn LED off
	LED_GREEN            = 1, // Turn LED to Green
	LED_RED              = 2, // Turn LED to Red
	LED_YELLOW           = 3, // Turn LED to Yellow
	LED_BLINK_GREEN      = 4, // Make LED blink Green
	// 5 is same as 4, LED blink Green
	LED_BLINK_RED_YELLOW = 6, // Make LED blink Red/Yellow
}

#[repr(C)] pub enum freenect_tilt_status_code {
	TILT_STATUS_STOPPED = 0x00, // Tilt motor is stopped
	TILT_STATUS_LIMIT   = 0x01, // Tilt motor has reached movement limit
	TILT_STATUS_MOVING  = 0x04, // Tilt motor is currently moving to new position
}

#[repr(C)] pub struct freenect_raw_tilt_state {
	pub accelerometer_x: int16_t                  , // Raw accelerometer data for X-axis, see FREENECT_COUNTS_PER_G for conversion
	pub accelerometer_y: int16_t                  , // Raw accelerometer data for Y-axis, see FREENECT_COUNTS_PER_G for conversion
	pub accelerometer_z: int16_t                  , // Raw accelerometer data for Z-axis, see FREENECT_COUNTS_PER_G for conversion
	pub tilt_angle:      int8_t                   , // Raw tilt motor angle encoder information
	pub tilt_status: 	 freenect_tilt_status_code, // State of the tilt motor (stopped, moving, etc...)
}

pub enum freenect_context {}

pub enum freenect_device {}

pub enum freenect_usb_context {}

#[repr(C)] pub enum freenect_loglevel {
	FREENECT_LOG_FATAL = 0,     // Log for crashing/non-recoverable errors
	FREENECT_LOG_ERROR,         // Log for major errors
	FREENECT_LOG_WARNING,       // Log for warning messages
	FREENECT_LOG_NOTICE,        // Log for important messages
	FREENECT_LOG_INFO,          // Log for normal messages
	FREENECT_LOG_DEBUG,         // Log for useful development messages
	FREENECT_LOG_SPEW,          // Log for slightly less useful messages
	FREENECT_LOG_FLOOD,         // Log EVERYTHING. May slow performance.
}

pub type freenect_log_cb   = extern fn(dev: *mut freenect_context, level: freenect_loglevel, msg: *const c_char);
pub type freenect_depth_cb = extern fn(dev: *mut freenect_device, depth: *mut c_void, timestamp: uint32_t);
pub type freenect_video_cb = extern fn(dev: *mut freenect_device, video: *mut c_void, timestamp: uint32_t);
pub type freenect_chunk_cb = extern fn(buffer: *mut c_void, pkt_data: *mut c_void, pkt_num: c_int, datalen: c_int, user_data: *mut c_void);

#[link(name = "freenect")]
extern "C" {
    pub fn freenect_init(ctx: *mut *mut freenect_context, usb_ctx: *mut freenect_usb_context) -> c_int;
	pub fn freenect_shutdown(ctx: *mut freenect_context) -> c_int;
	pub fn freenect_set_log_level(ctx: *mut freenect_context, level: freenect_loglevel);
	pub fn freenect_set_log_callback(ctx: *mut freenect_context, cb: freenect_log_cb);
	pub fn freenect_process_events(ctx: *mut freenect_context) -> c_int;
	pub fn freenect_process_events_timeout(ctx: *mut freenect_context, timeout: libc::timeval) -> c_int;
	pub fn freenect_num_devices(ctx: *mut freenect_context) -> c_int;
	pub fn freenect_list_device_attributes(ctx: *mut freenect_context, attribute_list: *mut *mut freenect_device_attributes) -> c_int;
	pub fn freenect_free_device_attributes(attribute_list: *mut freenect_device_attributes);
	pub fn freenect_supported_subdevices() -> c_int;
	pub fn freenect_select_subdevices(ctx: *mut freenect_context, subdevs: u32); // Not enum type as bitfield can have multiple choices
	pub fn freenect_enabled_subdevices(ctx: *mut freenect_context) -> freenect_device_flags;
	pub fn freenect_open_device(ctx: *mut freenect_context, dev: *mut *mut freenect_device, index: c_int) -> c_int;
	pub fn freenect_open_device_by_camera_serial(ctx: *mut freenect_context, dev: *mut *mut freenect_device, camera_serial: *const c_char) -> c_int;
	pub fn freenect_close_device(dev: *mut freenect_device) -> c_int;
	pub fn freenect_set_user(dev: *mut freenect_device, user: *mut c_void);
	pub fn freenect_get_user(dev: *mut freenect_device) -> *mut c_void;
	pub fn freenect_set_depth_callback(dev: *mut freenect_device, cb: freenect_depth_cb);
	pub fn freenect_set_video_callback(dev: *mut freenect_device, cb: freenect_video_cb);
	pub fn freenect_set_depth_chunk_callback(dev: *mut freenect_device, cb: freenect_chunk_cb);
	pub fn freenect_set_video_chunk_callback(dev: *mut freenect_device, cb: freenect_chunk_cb);
	pub fn freenect_set_depth_buffer(dev: *mut freenect_device, buf: *mut c_void) -> c_int;
	pub fn freenect_set_video_buffer(dev: *mut freenect_device, buf: *mut c_void) -> c_int;
	pub fn freenect_start_depth(dev: *mut freenect_device) -> c_int;
	pub fn freenect_start_video(dev: *mut freenect_device) -> c_int;
	pub fn freenect_stop_depth(dev: *mut freenect_device) -> c_int;
	pub fn freenect_stop_video(dev: *mut freenect_device) -> c_int;
	pub fn freenect_update_tilt_state(dev: *mut freenect_device) -> c_int;
	pub fn freenect_get_tilt_state(dev: *mut freenect_device) -> *mut freenect_raw_tilt_state;
	pub fn freenect_get_tilt_degs(state: *mut freenect_raw_tilt_state) -> c_double;
	pub fn freenect_set_tilt_degs(dev: *mut freenect_device, angle: c_double) -> c_int;
	pub fn freenect_get_tilt_status(state: *mut freenect_raw_tilt_state) -> freenect_tilt_status_code;
	pub fn freenect_set_led(dev: *mut freenect_device, option: freenect_led_option) -> c_int;
	pub fn freenect_get_mks_accel(state: *mut freenect_raw_tilt_state, x: *mut c_double, y: *mut c_double, z: *mut c_double);
	pub fn freenect_get_video_mode_count() -> c_int;
	pub fn freenect_get_video_mode(mode_num: c_int) -> freenect_frame_mode;
	pub fn freenect_get_current_video_mode(dev: *mut freenect_device) -> freenect_frame_mode;
	pub fn freenect_find_video_mode(res: freenect_resolution, fmt: freenect_video_format) -> freenect_frame_mode;
	pub fn freenect_set_video_mode(dev: *mut freenect_device, mode: freenect_frame_mode) -> c_int;
	pub fn freenect_get_depth_mode_count() -> c_int;
	pub fn freenect_get_depth_mode(mode_num: c_int) -> freenect_frame_mode;
	pub fn freenect_get_current_depth_mode(dev: *mut freenect_device) -> freenect_frame_mode;
	pub fn freenect_find_depth_mode(res: freenect_resolution, fmt: freenect_depth_format) -> freenect_frame_mode;
	pub fn freenect_set_depth_mode(dev: *mut freenect_device, mode: freenect_frame_mode) -> c_int;
	pub fn freenect_set_flag(dev: *mut freenect_device, flag: freenect_flag, value: freenect_flag_value) -> c_int;
	pub fn freenect_get_ir_brightness(dev: *mut freenect_device) -> c_int;
	pub fn freenect_set_ir_brightness(dev: *mut freenect_device, brightness: uint16_t) -> c_int;
	pub fn freenect_set_fw_address_nui(ctx: *mut freenect_context, fw_ptr: *mut c_uchar, num_bytes: c_uint);
	pub fn freenect_set_fw_address_k4w(ctx: *mut freenect_context, fw_ptr: *mut c_uchar, num_bytes: c_uint);
}
<|endoftext|>


<commit_before>#![allow(unstable)]

extern crate "libforestdb-sys" as ffi;

use std::default::Default;
use std::error;
use std::ffi::{CString, c_str_to_bytes};
use std::mem;

#[derive(Copy)]
pub struct Error {code: i32}

impl Error {
    pub fn from_code(code: i32) -> Error {
        Error {code: code}
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        "ForestDB error"
    }

    fn detail(&self) -> Option<String> {
        unsafe { String::from_utf8(c_str_to_bytes(&ffi::fdb_error_msg(self.code)).to_vec()).ok() }
    }
}

macro_rules! lift_error {
    ($e: expr) => (lift_error!($e, ()));
    ($e: expr, $r: expr) => ({
        let t = $e;
        match t {
            ffi::FDB_RESULT_SUCCESS => Ok($r),
            _ => return Err(Error::from_code(t))
        }
    })
}

macro_rules! try_fdb {
    ($e: expr) => ({
        let t = $e;
        match t {
            ffi::FDB_RESULT_SUCCESS => (),
            _ => return Err(Error::from_code(t))
        }
    })
}

#[derive(Copy)]
pub struct Config {
    raw: ffi::fdb_config
}

impl Config {
}

impl Default for Config {
    fn default() -> Config {
        Config {
            raw: unsafe { ffi::fdb_get_default_config() }
        }
    }
}

pub fn init(config: Config) -> Result<(), Error> {
    lift_error!(unsafe {ffi::fdb_init(mem::transmute(&config.raw))})
}

pub struct FileHandle {
    raw: *mut ffi::fdb_file_handle
}

impl FileHandle {
    fn from_raw(handle: *mut ffi::fdb_file_handle) -> FileHandle {
        FileHandle { raw: handle }
    }

    pub fn open(path: &Path, config: Config) -> Result<FileHandle, Error> {
        let mut handle: *mut ffi::fdb_file_handle = unsafe { mem::zeroed() };
        let c_path = CString::from_slice(path.as_vec());

        try_fdb!(unsafe { ffi::fdb_open(mem::transmute(&mut handle),
                                        c_path.as_ptr(),
                                        mem::transmute(&config.raw)) });
        Ok(FileHandle::from_raw(handle))
    }
}

impl Drop for FileHandle {
    fn drop(&mut self) {
        println!("Dropping");
        unsafe { ffi::fdb_close(self.raw); }
    }
}

#[cfg(test)]
mod tests {
    use super::{FileHandle, Error};

    use std::default::Default;
    use std::error;

    use ffi;

    #[test]
    fn test_error_msg(){
        let err = Error::from_code(ffi::FDB_RESULT_OPEN_FAIL);
        assert_eq!("error opening file", (&err as &error::Error).detail().unwrap().as_slice());
    }

    #[test]
    fn test_init() {
        assert!(super::init(Default::default()).is_ok());
    }

    #[test]
    fn test_open_file() {
        assert!(super::init(Default::default()).is_ok());
        assert!(FileHandle::open(&Path::new("test1"), Default::default()).is_ok());
    }
}
<commit_msg>Allow cloning and sending file handles over threads<commit_after>#![allow(unstable)]

extern crate "libforestdb-sys" as ffi;

#[macro_use] extern crate log;

use std::default::Default;
use std::error;
use std::ffi::{CString, c_str_to_bytes};
use std::mem;

#[derive(Copy)]
pub struct Error {code: i32}

impl Error {
    pub fn from_code(code: i32) -> Error {
        Error {code: code}
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        "ForestDB error"
    }

    fn detail(&self) -> Option<String> {
        unsafe { String::from_utf8(c_str_to_bytes(&ffi::fdb_error_msg(self.code)).to_vec()).ok() }
    }
}

impl std::fmt::Show for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        f.write_str(&format!("fdb error: {:?}", (self as &error::Error).detail())[])
    }
}

macro_rules! lift_error {
    ($e: expr) => (lift_error!($e, ()));
    ($e: expr, $r: expr) => ({
        let t = $e;
        match t {
            ffi::FDB_RESULT_SUCCESS => Ok($r),
            _ => return Err(Error::from_code(t))
        }
    })
}

macro_rules! try_fdb {
    ($e: expr) => ({
        let t = $e;
        match t {
            ffi::FDB_RESULT_SUCCESS => (),
            _ => return Err(Error::from_code(t))
        }
    })
}

#[derive(Copy)]
pub struct Config {
    raw: ffi::fdb_config
}

impl Config {
}

impl Default for Config {
    fn default() -> Config {
        Config {
            raw: unsafe { ffi::fdb_get_default_config() }
        }
    }
}

pub fn init(config: Config) -> Result<(), Error> {
    lift_error!(unsafe {ffi::fdb_init(mem::transmute(&config.raw))})
}

pub fn shutdown() -> Result<(), Error> {
    lift_error!(unsafe {ffi::fdb_shutdown()})
}

pub struct FileHandle {
    path: Path,
    config: Config,
    raw: *mut ffi::fdb_file_handle,
}

impl FileHandle {
    fn from_raw(handle: *mut ffi::fdb_file_handle, path: &Path, config: Config) -> FileHandle {
        FileHandle {
            raw: handle,
            path: path.clone(),
            config: config
        }
    }

    pub fn open(path: &Path, config: Config) -> Result<FileHandle, Error> {
        let mut handle: *mut ffi::fdb_file_handle = unsafe { mem::zeroed() };
        let c_path = CString::from_slice(path.as_vec());

        try_fdb!(unsafe { ffi::fdb_open(mem::transmute(&mut handle),
                                        c_path.as_ptr(),
                                        mem::transmute(&config.raw)) });
        let res = FileHandle::from_raw(handle, path, config);
        Ok(res)
    }
}

unsafe impl Send for FileHandle {}

// FIXME: do not use trait, implement clone as method
// which returns result? Otherwise it is impossible
// to handle cloning right
impl Clone for FileHandle {
    fn clone(&self) -> FileHandle {
        match FileHandle::open(&self.path, self.config) {
            Ok(res) => res,
            Err(_) => panic!("failed to clone")
        }
    }
}

impl Drop for FileHandle {
    fn drop(&mut self) {
        debug!("Dropping");
        unsafe { ffi::fdb_close(self.raw); }
    }
}

#[cfg(test)]
mod tests {
    use super::{FileHandle, Error};

    use std::default::Default;
    use std::error;
    use std::thread::Thread;

    use ffi;

    #[test]
    fn test_error_msg(){
        let err = Error::from_code(ffi::FDB_RESULT_OPEN_FAIL);
        assert_eq!("error opening file", (&err as &error::Error).detail().unwrap().as_slice());
    }

    #[test]
    fn test_init() {
        assert!(super::init(Default::default()).is_ok());
    }

    #[test]
    fn test_open_file() {
        assert!(super::init(Default::default()).is_ok());
        assert!(FileHandle::open(&Path::new("test1"), Default::default()).is_ok());
    }

    #[test]
    fn test_clone() {
        let fh1 = FileHandle::open(&Path::new("test2"), Default::default()).unwrap();
        let fh2 = fh1.clone();

        Thread::scoped(move || {
            let fh_cloned = fh2;
            // FIXME: test something useful
        }).join();
    }
}
<|endoftext|>


<commit_before>#![warn(missing_docs)]

//! A `Pinboard` is a shared, mutable, eventually consistent, lock-free data-structure.  This
//! allows multiple threads to communicate in a decoupled way by publishing data to the pinboard
//! which other threads can then read in an eventually consistent way.
//!
//! This is not a silver bullet though, there are various limitations of `Pinboard` that trade off
//! the nice behaviour described above.
//!
//! * Eventual consistency:
//!     * Writes from one thread are not guaranteed to be seen by reads from another thread
//!     * Writes from one thread can overwrite writes from another thread
//! * No in-place mutation:
//!     * The only write primitive completely overwrites the data on the `Pinboard`
//! * Requires `Clone`:
//!     * All reads return a clone of the data, decoupling the lifetime of the read value from the
//!     data stored in the global reference.

extern crate crossbeam;

use crossbeam::mem::epoch::{Atomic, Owned, pin};
use std::sync::atomic::Ordering::*;

use std::ops::Deref;

/// An instance of a `Pinboard`, holds a shared, mutable, eventually-consistent reference to a `T`.
pub struct Pinboard<T: Clone>(Atomic<T>);

impl<T: Clone> Pinboard<T> {
    /// Create a new `Pinboard` instance holding the given value.
    pub fn new(t: T) -> Pinboard<T> {
        let t = Owned::new(t);
        let p = Pinboard::default();
        p.0.store(Some(t), Release);
        p
    }

    /// Update the value stored in the `Pinboard`.
    pub fn set(&self, t: T) {
        let guard = pin();
        let t = Owned::new(t);
        if let Some(t) = self.0.swap(Some(t), Release, &guard) {
            unsafe { guard.unlinked(t); }
        }
    }

    /// Clear out the `Pinboard` so its no longer holding any data.
    pub fn clear(&self) {
        let guard = pin();
        if let Some(t) = self.0.swap(None, Release, &guard) {
            unsafe { guard.unlinked(t); }
        }
    }

    /// Get a copy of the latest (well, recent) version of the posted data.
    pub fn read(&self) -> Option<T> {
        let guard = pin();
        let t = self.0.load(Acquire, &guard);
        t.map(|t| -> &T { t.deref() }).cloned()
    }
}

impl<T: Clone> Default for Pinboard<T> {
    fn default() -> Pinboard<T> {
        Pinboard(Atomic::null())
    }
}

impl<T: Clone> Drop for Pinboard<T> {
    fn drop(&mut self) {
        // Make sure any stored data is marked for deletion
        self.clear();
    }
}

/// An wrapper around a `Pinboard` which provides the guarantee it is never empty.
pub struct NonEmptyPinboard<T: Clone>(Pinboard<T>);

impl<T: Clone> NonEmptyPinboard<T> {
    /// Create a new `NonEmptyPinboard` instance holding the given value.
    pub fn new(t: T) -> NonEmptyPinboard<T> {
        NonEmptyPinboard(Pinboard::new(t))
    }

    /// Update the value stored in the `NonEmptyPinboard`.
    #[inline]
    pub fn set(&self, t: T) {
        self.0.set(t)
    }

    /// Get a copy of the latest (well, recent) version of the posted data.
    #[inline]
    pub fn read(&self) -> T {
        // Unwrap the option returned by the inner `Pinboard`. This will never panic, because it's
        // impossible for this `Pinboard` to be empty (though it's not possible to prove this to the
        // compiler).
        match self.0.read() {
            Some(t) => t,
            None => unreachable!("Inner pointer was unexpectedly null"),
        }
    }
}

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

    fn consume<T: Clone + Display>(t: &Pinboard<T>) {
        loop {
            match t.read() {
                Some(_) => {},
                None => break,
            }
            std::thread::sleep(std::time::Duration::from_millis(1));
        }
    }

    fn produce(t: &Pinboard<u32>) {
        for i in 1..100 {
            t.set(i);
            std::thread::sleep(std::time::Duration::from_millis(2));
        }
        t.clear();
    }

    #[test]
    fn it_works() {
        let t = Pinboard::<u32>::default();
        assert_eq!(None, t.read());
        t.set(3);
        assert_eq!(Some(3), t.read());
        t.clear();
        assert_eq!(None, t.read());
    }

    #[test]
    fn single_producer_single_consumer() {
        let t = Pinboard::<u32>::new(0);

        crossbeam::scope(|scope| {
            scope.spawn(|| produce(&t));
            scope.spawn(|| consume(&t));
        })
    }

    #[test]
    fn multi_producer_single_consumer() {
        let t = Pinboard::<u32>::new(0);

        crossbeam::scope(|scope| {
            scope.spawn(|| produce(&t));
            scope.spawn(|| produce(&t));
            scope.spawn(|| produce(&t));
            scope.spawn(|| consume(&t));
        })
    }

    #[test]
    fn single_producer_multi_consumer() {
        let t = Pinboard::<u32>::new(0);

        crossbeam::scope(|scope| {
            scope.spawn(|| produce(&t));
            scope.spawn(|| consume(&t));
            scope.spawn(|| consume(&t));
            scope.spawn(|| consume(&t));
        })
    }

    #[test]
    fn multi_producer_multi_consumer() {
        let t = Pinboard::<u32>::new(0);

        crossbeam::scope(|scope| {
            scope.spawn(|| produce(&t));
            scope.spawn(|| produce(&t));
            scope.spawn(|| produce(&t));
            scope.spawn(|| consume(&t));
            scope.spawn(|| consume(&t));
            scope.spawn(|| consume(&t));
        })
    }

    #[test]
    fn non_empty_pinboard() {
        let t = NonEmptyPinboard::<u32>::new(3);
        assert_eq!(3, t.read());
        t.set(4);
        assert_eq!(4, t.read());
    }
}
<commit_msg>Tidy up formatting<commit_after>#![warn(missing_docs)]

//! A `Pinboard` is a shared, mutable, eventually consistent, lock-free data-structure.  This
//! allows multiple threads to communicate in a decoupled way by publishing data to the pinboard
//! which other threads can then read in an eventually consistent way.
//!
//! This is not a silver bullet though, there are various limitations of `Pinboard` that trade off
//! the nice behaviour described above.
//!
//! * Eventual consistency:
//!     * Writes from one thread are not guaranteed to be seen by reads from another thread
//!     * Writes from one thread can overwrite writes from another thread
//! * No in-place mutation:
//!     * The only write primitive completely overwrites the data on the `Pinboard`
//! * Requires `Clone`:
//!     * All reads return a clone of the data, decoupling the lifetime of the read value from the
//!     data stored in the global reference.

extern crate crossbeam;

use crossbeam::mem::epoch::{Atomic, Owned, pin};
use std::sync::atomic::Ordering::*;

use std::ops::Deref;

/// An instance of a `Pinboard`, holds a shared, mutable, eventually-consistent reference to a `T`.
pub struct Pinboard<T: Clone>(Atomic<T>);

impl<T: Clone> Pinboard<T> {
    /// Create a new `Pinboard` instance holding the given value.
    pub fn new(t: T) -> Pinboard<T> {
        let t = Owned::new(t);
        let p = Pinboard::default();
        p.0.store(Some(t), Release);
        p
    }

    /// Update the value stored in the `Pinboard`.
    pub fn set(&self, t: T) {
        let guard = pin();
        let t = Owned::new(t);
        if let Some(t) = self.0.swap(Some(t), Release, &guard) {
            unsafe {
                guard.unlinked(t);
            }
        }
    }

    /// Clear out the `Pinboard` so its no longer holding any data.
    pub fn clear(&self) {
        let guard = pin();
        if let Some(t) = self.0.swap(None, Release, &guard) {
            unsafe {
                guard.unlinked(t);
            }
        }
    }

    /// Get a copy of the latest (well, recent) version of the posted data.
    pub fn read(&self) -> Option<T> {
        let guard = pin();
        let t = self.0.load(Acquire, &guard);
        t.map(|t| -> &T { t.deref() }).cloned()
    }
}

impl<T: Clone> Default for Pinboard<T> {
    fn default() -> Pinboard<T> {
        Pinboard(Atomic::null())
    }
}

impl<T: Clone> Drop for Pinboard<T> {
    fn drop(&mut self) {
        // Make sure any stored data is marked for deletion
        self.clear();
    }
}

/// An wrapper around a `Pinboard` which provides the guarantee it is never empty.
pub struct NonEmptyPinboard<T: Clone>(Pinboard<T>);

impl<T: Clone> NonEmptyPinboard<T> {
    /// Create a new `NonEmptyPinboard` instance holding the given value.
    pub fn new(t: T) -> NonEmptyPinboard<T> {
        NonEmptyPinboard(Pinboard::new(t))
    }

    /// Update the value stored in the `NonEmptyPinboard`.
    #[inline]
    pub fn set(&self, t: T) {
        self.0.set(t)
    }

    /// Get a copy of the latest (well, recent) version of the posted data.
    #[inline]
    pub fn read(&self) -> T {
        // Unwrap the option returned by the inner `Pinboard`. This will never panic, because it's
        // impossible for this `Pinboard` to be empty (though it's not possible to prove this to the
        // compiler).
        match self.0.read() {
            Some(t) => t,
            None => unreachable!("Inner pointer was unexpectedly null"),
        }
    }
}

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

    fn consume<T: Clone + Display>(t: &Pinboard<T>) {
        loop {
            match t.read() {
                Some(_) => {}
                None => break,
            }
            std::thread::sleep(std::time::Duration::from_millis(1));
        }
    }

    fn produce(t: &Pinboard<u32>) {
        for i in 1..100 {
            t.set(i);
            std::thread::sleep(std::time::Duration::from_millis(2));
        }
        t.clear();
    }

    #[test]
    fn it_works() {
        let t = Pinboard::<u32>::default();
        assert_eq!(None, t.read());
        t.set(3);
        assert_eq!(Some(3), t.read());
        t.clear();
        assert_eq!(None, t.read());
    }

    #[test]
    fn single_producer_single_consumer() {
        let t = Pinboard::<u32>::new(0);

        crossbeam::scope(|scope| {
                             scope.spawn(|| produce(&t));
                             scope.spawn(|| consume(&t));
                         })
    }

    #[test]
    fn multi_producer_single_consumer() {
        let t = Pinboard::<u32>::new(0);

        crossbeam::scope(|scope| {
                             scope.spawn(|| produce(&t));
                             scope.spawn(|| produce(&t));
                             scope.spawn(|| produce(&t));
                             scope.spawn(|| consume(&t));
                         })
    }

    #[test]
    fn single_producer_multi_consumer() {
        let t = Pinboard::<u32>::new(0);

        crossbeam::scope(|scope| {
                             scope.spawn(|| produce(&t));
                             scope.spawn(|| consume(&t));
                             scope.spawn(|| consume(&t));
                             scope.spawn(|| consume(&t));
                         })
    }

    #[test]
    fn multi_producer_multi_consumer() {
        let t = Pinboard::<u32>::new(0);

        crossbeam::scope(|scope| {
                             scope.spawn(|| produce(&t));
                             scope.spawn(|| produce(&t));
                             scope.spawn(|| produce(&t));
                             scope.spawn(|| consume(&t));
                             scope.spawn(|| consume(&t));
                             scope.spawn(|| consume(&t));
                         })
    }

    #[test]
    fn non_empty_pinboard() {
        let t = NonEmptyPinboard::<u32>::new(3);
        assert_eq!(3, t.read());
        t.set(4);
        assert_eq!(4, t.read());
    }
}
<|endoftext|>