<commit_before>use backend::Backend;
use provider::error;
use provider::Provider;
use provider::Output;
use libc::{c_char, uint32_t};
use std::ffi::CString;

pub struct File<'a> {
    name: &'static str,
    backend: &'a Backend,
    provider: &'a Provider,
}

impl<'a> File<'a> {
    pub fn new(n: &'static str, b: &'a Backend, p: &'a Provider) -> File<'a> {
        File {
            name: n,
            backend: b,
            provider: p,
        }
    }

    pub fn mode(&self) -> Result<u32, error::Error> {
        self.backend
            .handle(self.provider.file.mode(self.name))
            .and_then(Output::to_u32)
    }

    pub fn size(&self) -> Result<i64, error::Error> {
        self.backend
            .handle(self.provider.file.size(self.name))
            .and_then(Output::to_i64)
    }

    pub fn is_file(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_file(self.name))
            .and_then(Output::to_bool)
    }

    pub fn exist(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.exist(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_directory(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_directory(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_block_device(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_block_device(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_character_device(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_character_device(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_pipe(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_pipe(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_socket(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_socket(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_symlink(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_symlink(self.name))
            .and_then(Output::to_bool)
    }

    pub fn contents(&self) -> Result<String, error::Error> {
        self.backend
            .handle(self.provider.file.contents(self.name))
            .and_then(Output::to_string)
    }

    pub fn owner(&self) -> Result<String, error::Error> {
        self.backend
            .handle(self.provider.file.owner(self.name))
            .and_then(Output::to_string)
    }

    pub fn group(&self) -> Result<String, error::Error> {
        self.backend
            .handle(self.provider.file.group(self.name))
            .and_then(Output::to_string)
    }

    pub fn linked_to(&self) -> Result<String, error::Error> {
        self.backend
            .handle(self.provider.file.linked_to(self.name))
            .and_then(Output::to_string)
    }

    pub fn is_readable(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_readable(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_readable_by_owner(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_readable_by_owner(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_readable_by_group(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_readable_by_group(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_readable_by_others(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_readable_by_others(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_readable_by_user(&self, user: &'static str) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_readable_by_user(self.name, user))
            .and_then(Output::to_bool)
    }

    pub fn is_writable(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_writable(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_writable_by_owner(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_writable_by_owner(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_writable_by_group(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_writable_by_group(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_writable_by_others(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_writable_by_others(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_writable_by_user(&self, user: &'static str) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_writable_by_user(self.name, user))
            .and_then(Output::to_bool)
    }

    pub fn md5sum(&self) -> Result<String, error::Error> {
        self.backend
            .handle(self.provider.file.md5sum(self.name))
            .and_then(Output::to_string)
    }

    pub fn sha256sum(&self) -> Result<String, error::Error> {
        self.backend
            .handle(self.provider.file.sha256sum(self.name))
            .and_then(Output::to_string)
    }
}

// Wrapper functions for FFI

#[no_mangle]
pub extern "C" fn resource_file_free(ptr: *mut File) {
    if ptr.is_null() {
        return;
    }
    unsafe {
        Box::from_raw(ptr);
    }
}

#[no_mangle]
pub extern "C" fn resource_file_mode(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    f.mode().unwrap()
}

#[no_mangle]
pub extern "C" fn resource_file_is_file(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    if f.is_file().unwrap() { 1 } else { 0 }
}

#[no_mangle]
pub extern "C" fn resource_file_exist(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    if f.exist().unwrap() { 1 } else { 0 }
}

#[no_mangle]
pub extern "C" fn resource_file_is_directory(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    if f.is_directory().unwrap() { 1 } else { 0 }
}

#[no_mangle]
pub extern "C" fn resource_file_is_block_device(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    if f.is_block_device().unwrap() { 1 } else { 0 }
}

#[no_mangle]
pub extern "C" fn resource_file_is_character_device(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    if f.is_character_device().unwrap() {
        1
    } else {
        0
    }
}

#[no_mangle]
pub extern "C" fn resource_file_is_pipe(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    if f.is_pipe().unwrap() { 1 } else { 0 }
}

#[no_mangle]
pub extern "C" fn resource_file_is_socket(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    if f.is_socket().unwrap() { 1 } else { 0 }
}

#[no_mangle]
pub extern "C" fn resource_file_is_symlink(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    if f.is_symlink().unwrap() { 1 } else { 0 }
}

#[no_mangle]
pub extern "C" fn resource_file_contents(ptr: *const File) -> *mut c_char {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };
    let c = f.contents().unwrap();
    CString::new(c).unwrap().into_raw()
}

#[no_mangle]
pub extern "C" fn resource_file_owner(ptr: *const File) -> *mut c_char {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };
    let c = f.owner().unwrap();
    CString::new(c).unwrap().into_raw()
}
<commit_msg>Add wrapper function for file.group()<commit_after>use backend::Backend;
use provider::error;
use provider::Provider;
use provider::Output;
use libc::{c_char, uint32_t};
use std::ffi::CString;

pub struct File<'a> {
    name: &'static str,
    backend: &'a Backend,
    provider: &'a Provider,
}

impl<'a> File<'a> {
    pub fn new(n: &'static str, b: &'a Backend, p: &'a Provider) -> File<'a> {
        File {
            name: n,
            backend: b,
            provider: p,
        }
    }

    pub fn mode(&self) -> Result<u32, error::Error> {
        self.backend
            .handle(self.provider.file.mode(self.name))
            .and_then(Output::to_u32)
    }

    pub fn size(&self) -> Result<i64, error::Error> {
        self.backend
            .handle(self.provider.file.size(self.name))
            .and_then(Output::to_i64)
    }

    pub fn is_file(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_file(self.name))
            .and_then(Output::to_bool)
    }

    pub fn exist(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.exist(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_directory(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_directory(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_block_device(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_block_device(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_character_device(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_character_device(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_pipe(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_pipe(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_socket(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_socket(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_symlink(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_symlink(self.name))
            .and_then(Output::to_bool)
    }

    pub fn contents(&self) -> Result<String, error::Error> {
        self.backend
            .handle(self.provider.file.contents(self.name))
            .and_then(Output::to_string)
    }

    pub fn owner(&self) -> Result<String, error::Error> {
        self.backend
            .handle(self.provider.file.owner(self.name))
            .and_then(Output::to_string)
    }

    pub fn group(&self) -> Result<String, error::Error> {
        self.backend
            .handle(self.provider.file.group(self.name))
            .and_then(Output::to_string)
    }

    pub fn linked_to(&self) -> Result<String, error::Error> {
        self.backend
            .handle(self.provider.file.linked_to(self.name))
            .and_then(Output::to_string)
    }

    pub fn is_readable(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_readable(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_readable_by_owner(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_readable_by_owner(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_readable_by_group(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_readable_by_group(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_readable_by_others(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_readable_by_others(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_readable_by_user(&self, user: &'static str) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_readable_by_user(self.name, user))
            .and_then(Output::to_bool)
    }

    pub fn is_writable(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_writable(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_writable_by_owner(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_writable_by_owner(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_writable_by_group(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_writable_by_group(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_writable_by_others(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_writable_by_others(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_writable_by_user(&self, user: &'static str) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_writable_by_user(self.name, user))
            .and_then(Output::to_bool)
    }

    pub fn md5sum(&self) -> Result<String, error::Error> {
        self.backend
            .handle(self.provider.file.md5sum(self.name))
            .and_then(Output::to_string)
    }

    pub fn sha256sum(&self) -> Result<String, error::Error> {
        self.backend
            .handle(self.provider.file.sha256sum(self.name))
            .and_then(Output::to_string)
    }
}

// Wrapper functions for FFI

#[no_mangle]
pub extern "C" fn resource_file_free(ptr: *mut File) {
    if ptr.is_null() {
        return;
    }
    unsafe {
        Box::from_raw(ptr);
    }
}

#[no_mangle]
pub extern "C" fn resource_file_mode(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    f.mode().unwrap()
}

#[no_mangle]
pub extern "C" fn resource_file_is_file(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    if f.is_file().unwrap() { 1 } else { 0 }
}

#[no_mangle]
pub extern "C" fn resource_file_exist(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    if f.exist().unwrap() { 1 } else { 0 }
}

#[no_mangle]
pub extern "C" fn resource_file_is_directory(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    if f.is_directory().unwrap() { 1 } else { 0 }
}

#[no_mangle]
pub extern "C" fn resource_file_is_block_device(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    if f.is_block_device().unwrap() { 1 } else { 0 }
}

#[no_mangle]
pub extern "C" fn resource_file_is_character_device(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    if f.is_character_device().unwrap() {
        1
    } else {
        0
    }
}

#[no_mangle]
pub extern "C" fn resource_file_is_pipe(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    if f.is_pipe().unwrap() { 1 } else { 0 }
}

#[no_mangle]
pub extern "C" fn resource_file_is_socket(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    if f.is_socket().unwrap() { 1 } else { 0 }
}

#[no_mangle]
pub extern "C" fn resource_file_is_symlink(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    if f.is_symlink().unwrap() { 1 } else { 0 }
}

#[no_mangle]
pub extern "C" fn resource_file_contents(ptr: *const File) -> *mut c_char {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };
    let c = f.contents().unwrap();
    CString::new(c).unwrap().into_raw()
}

#[no_mangle]
pub extern "C" fn resource_file_owner(ptr: *const File) -> *mut c_char {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };
    let c = f.owner().unwrap();
    CString::new(c).unwrap().into_raw()
}

#[no_mangle]
pub extern "C" fn resource_file_group(ptr: *const File) -> *mut c_char {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };
    let c = f.group().unwrap();
    CString::new(c).unwrap().into_raw()
}
<|endoftext|>


<commit_before>
use std::error::Error as StdError;
use std::fmt;
use std::io::Error as IoError;

pub enum ParseError {
    Eof,
    NotFound,
    Expected(Vec<u8>),
    ExpectedType(&'static str),
    Io(IoError),
}

impl fmt::Display for ParseError {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error>
    {
        match *self {
            ParseError::Expected(ref bytes) => write!(f, "{}. Expected {:?}",
                                                      self.description(), bytes),
            ParseError::ExpectedType(ref t) => write!(f, "{}. Expected {}",
                                                      self.description(), t),
            ParseError::Io(ref e) => write!(f, "{}: {}",
                                             self.description(), e),
            _ => write!(f, "{}", self.description()),
        }
    }
}

impl fmt::Debug for ParseError {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error>
    {
        match *self {
            ParseError::Expected(ref bytes) => write!(f, "{}. Expected {:?}",
                                                      self.description(), bytes),
            ParseError::ExpectedType(ref t) => write!(f, "{}. Expected {}",
                                                      self.description(), t),
            ParseError::Io(ref e) => write!(f, "{}: {:?}",
                                             self.description(), e),
            _ => write!(f, "{}", self.description()),
        }
    }
}

impl StdError for ParseError {
    fn description(&self) -> &str
    {
        match *self {
            ParseError::Eof => "End of File",
            ParseError::NotFound => "Not Found",
            ParseError::Expected(_) => "Expectation Failed",
            ParseError::ExpectedType(_) => "Expectation Failed",
            ParseError::Io(_) => "I/O Error",
        }
    }

    fn cause(&self) -> Option<&StdError>
    {
        match *self {
            ParseError::Eof => None,
            ParseError::NotFound => None,
            ParseError::Expected(_) => None,
            ParseError::ExpectedType(_) => None,
            ParseError::Io(ref e) => Some(e),
        }
    }
}
<commit_msg>rfc5322: ParseError::InvalidBodyChar<commit_after>
use std::error::Error as StdError;
use std::fmt;
use std::io::Error as IoError;

pub enum ParseError {
    Eof,
    NotFound,
    Expected(Vec<u8>),
    ExpectedType(&'static str),
    Io(IoError),
    InvalidBodyChar(u8),
}

impl fmt::Display for ParseError {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error>
    {
        match *self {
            ParseError::Expected(ref bytes) => write!(f, "{}. Expected {:?}",
                                                      self.description(), bytes),
            ParseError::ExpectedType(ref t) => write!(f, "{}. Expected {}",
                                                      self.description(), t),
            ParseError::Io(ref e) => write!(f, "{}: {}",
                                            self.description(), e),
            ParseError::InvalidBodyChar(ref c) => write!(f, "{}: {}",
                                                         self.description(), c),
            _ => write!(f, "{}", self.description()),
        }
    }
}

impl fmt::Debug for ParseError {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error>
    {
        match *self {
            ParseError::Expected(ref bytes) => write!(f, "{}. Expected {:?}",
                                                      self.description(), bytes),
            ParseError::ExpectedType(ref t) => write!(f, "{}. Expected {}",
                                                      self.description(), t),
            ParseError::Io(ref e) => write!(f, "{}: {:?}",
                                             self.description(), e),
            ParseError::InvalidBodyChar(ref c) => write!(f, "{}: {}",
                                                         self.description(), c),
            _ => write!(f, "{}", self.description()),
        }
    }
}

impl StdError for ParseError {
    fn description(&self) -> &str
    {
        match *self {
            ParseError::Eof => "End of File",
            ParseError::NotFound => "Not Found",
            ParseError::Expected(_) => "Expectation Failed",
            ParseError::ExpectedType(_) => "Expectation Failed",
            ParseError::Io(_) => "I/O Error",
            ParseError::InvalidBodyChar(_) => "Invalid Body Character",
        }
    }

    fn cause(&self) -> Option<&StdError>
    {
        match *self {
            ParseError::Eof => None,
            ParseError::NotFound => None,
            ParseError::Expected(_) => None,
            ParseError::ExpectedType(_) => None,
            ParseError::Io(ref e) => Some(e),
            ParseError::InvalidBodyChar(_) => None,
        }
    }
}
<|endoftext|>


<commit_before>use piston_window::{self, PistonWindow};

pub enum InputResult {
    PushScreen(Box<Screen>),
    PopScreen,
}

pub type InputResults = Vec<InputResult>;

pub struct Context {
    pub assets_path: ::std::path::PathBuf,
}

pub trait Screen {
    fn on_input(&mut self, input: &piston_window::Input, window: &PistonWindow, context: &mut Context) -> InputResults;
    fn on_draw(&mut self, args: &piston_window::RenderArgs, window: &PistonWindow);
    fn on_update(&mut self, args: &piston_window::UpdateArgs);
}
<commit_msg>GameState enum<commit_after>use piston_window::{self, PistonWindow};

pub enum InputResult {
    PushScreen(Box<Screen>),
    PopScreen,
}

pub type InputResults = Vec<InputResult>;

pub enum GameState {
    NotStarted,
    Running,
    Paused,
}

pub struct Context {
    pub assets_path: ::std::path::PathBuf,
}

pub trait Screen {
    fn on_input(&mut self, input: &piston_window::Input, window: &PistonWindow, context: &mut Context) -> InputResults;
    fn on_draw(&mut self, args: &piston_window::RenderArgs, window: &PistonWindow);
    fn on_update(&mut self, args: &piston_window::UpdateArgs);
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2017-2018 Ryan Huang
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

use std::cmp;
use std::mem;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::{self, Receiver, TryRecvError};
use std::sync::{Arc, Mutex};
use std::thread::{self, JoinHandle};

use model::ttable::{Score, TTable};
use model::{Board, Move, Outcome};

const NEG_INFINITY: i16 = -0x7000;
const LOSE: i16 = -0x4000;
// Small contempt factor to discourage draws
const DRAW: i16 = 1;
const ASPIRATION_WIDTH: i16 = 51;

pub struct AI {
    status: Status,
    ttable: Arc<Mutex<TTable>>,
}

enum Status {
    Idle,
    // Either the AI thread is running, or there is a move waiting to be received
    Thinking {
        move_recv: Receiver<Move>,
        // We store and load this atomic with Ordering::Relaxed. It *should* be fine because it
        // doesn't interact with any other atomics--all we want to do is tell the AI thread to stop
        // searching for a move
        stop_signal: Arc<AtomicBool>,
        handle: JoinHandle<()>,
    },
}

impl AI {
    pub fn new() -> Self {
        Self {
            status: Status::Idle,
            ttable: Arc::new(Mutex::new(TTable::new())),
        }
    }

    pub fn is_idle(&self) -> bool {
        match self.status {
            Status::Idle => true,
            Status::Thinking { .. } => false,
        }
    }

    pub fn stop(&mut self) {
        if let Status::Thinking {
            ref stop_signal, ..
        } = self.status
        {
            stop_signal.store(true, Ordering::Relaxed);
        }
        // Unconditionally assign because without NLL, we can't put this in the if let block above
        self.status = Status::Idle;
    }

    pub fn try_recv(&mut self) -> Option<Move> {
        use self::TryRecvError::*;

        let result;
        match self.status {
            Status::Idle => result = None,
            Status::Thinking { ref move_recv, .. } => match move_recv.try_recv() {
                Ok(mv) => result = Some(mv),
                Err(Empty) => result = None,
                Err(Disconnected) => panic!("Tried to receive move from disconnected sender"),
            },
        }

        // We can't set status in the Ok(mv) arm above without NLL
        if result.is_some() {
            self.status = Status::Idle;
        }
        result
    }

    pub fn think(&mut self, board: Board, board_list: Vec<Board>, depth: u8) {
        assert_ne!(depth, 0);

        let prev_status = mem::replace(&mut self.status, Status::Idle);

        let (move_sender, move_recv) = mpsc::channel();
        let stop_signal = Arc::new(AtomicBool::new(false));
        let stop_signal_clone = stop_signal.clone();

        let ttable_mutex = self.ttable.clone();

        let handle = thread::spawn(move || {
            if let Status::Thinking {
                stop_signal,
                handle,
                ..
            } = prev_status
            {
                stop_signal.store(true, Ordering::Relaxed);
                handle
                    .join()
                    .expect("Old AI thread panicked when new AI thread joined on it");
            }

            // If the previous AI thread was sent the stop signal, but hasn't received it yet, we
            // will block here until it finishes. We won't have joined on its handle above because
            // stop throws away its Status enum.
            let mut ttable = match ttable_mutex.lock() {
                Ok(table) => table,
                Err(_) => panic!("Transposition table mutex is poisoned"),
            };

            if let SearchResult::Move(mv) =
                search_root(depth, board, board_list, &mut ttable, stop_signal_clone)
            {
                move_sender.send(mv).expect("AI failed to send Move");
            }
        });

        self.status = Status::Thinking {
            move_recv,
            stop_signal,
            handle,
        };
    }
}

enum SearchResult {
    Move(Move),
    Stopped,
}

fn search_root(
    depth: u8,
    board: Board,
    board_list: Vec<Board>,
    ttable: &mut TTable,
    stop_signal: Arc<AtomicBool>,
) -> SearchResult {
    ttable.inc_age();

    // Only take positions after the last irreversible move
    let mut board_list: Vec<_> = board_list
        .into_iter()
        .rev()
        .take_while(|b| b.vitals == board.vitals)
        .collect();
    board_list.reverse();

    let mut moves: Vec<(Move, i16)> = board
        .generate_moves()
        .map(|mv| (mv, NEG_INFINITY))
        .collect();

    if moves.is_empty() {
        panic!("AI has no moves");
    }

    let mut pv = None;
    let mut iter_score = evaluate(&board);
    for depth in 0..depth {
        if stop_signal.load(Ordering::Relaxed) {
            return SearchResult::Stopped;
        }

        // Aspiration window search loop
        let mut asp_width = ASPIRATION_WIDTH;
        loop {
            let mut max_score = iter_score - asp_width;
            for pair in &mut moves {
                let mut new_board = board;
                new_board.apply_move(&pair.0);

                let mut new_pv = vec![];

                let score = -alphabeta_negamax(
                    &new_board,
                    &mut board_list,
                    &mut new_pv,
                    -(iter_score + asp_width),
                    -max_score,
                    depth,
                    ttable,
                );

                if score > max_score {
                    max_score = score;
                    pv = Some(new_pv);
                }
                pair.1 = score;
            }

            if max_score == iter_score + asp_width || max_score == iter_score - asp_width {
                // True score lies outside the window, so we search this position again
                asp_width *= 2;
            } else {
                break;
            }
        }

        moves.sort_by(|&(_, a), &(_, b)| b.cmp(&a));
        iter_score = moves[0].1;

        println!("\nDepth {}: {:>6}", depth, moves[0].1);
        if let Some(ref mut pv) = pv {
            pv.push(moves[0].0);
            for mv in pv.iter().rev() {
                println!("    {}", mv);
            }
        }
    }
    println!("\n---------------------");

    SearchResult::Move(moves[0].0)
}

fn alphabeta_negamax(
    board: &Board,
    // This list does not include the current board
    mut board_list: &mut Vec<Board>,
    pv: &mut Vec<Move>,
    mut alpha: i16,
    mut beta: i16,
    depth: u8,
    ttable: &mut TTable,
) -> i16 {
    let mut set_pv = move |score, new_pv| {
        if score > alpha && score < beta {
            *pv = new_pv;
        }
    };
    let set_ttable = |ttable: &mut TTable, score| {
        ttable.set(board.zobrist, score, depth as i8);
    };

    use self::Outcome::*;
    match board.outcome() {
        DrawStalemate | DrawInsufficientMaterial => {
            // This is safe to do because Board does not detect draws by threefold repetition
            set_ttable(ttable, Score::Exact(DRAW));
            set_pv(DRAW, vec![]);
            return DRAW;
        }
        Win(color) => {
            assert_ne!(color, board.turn);
            // Weight score by depth to encourage shorter wins. The shorter the win, the greater
            // `depth` will be, and so the larger the score will be. This also encourages the AI to
            // prolong a loss.
            let score = LOSE - i16::from(depth);
            set_ttable(ttable, Score::Exact(score));
            set_pv(score, vec![]);
            return score;
        }
        InProgress => {}
        DrawThreefoldRepetition => unreachable!(),
    }

    if board_list.len() >= 8 && board_list.iter().filter(|&&b| b == *board).count() >= 2 {
        set_pv(DRAW, vec![]);
        return DRAW;
    }

    if depth == 0 {
        let score = quiescence_search(board, alpha, beta, depth as i8, ttable);
        set_pv(score, vec![]);
        return score;
    }

    {
        match ttable.get(board.zobrist, depth as i8) {
            Some(Score::Exact(score)) => {
                // This will cut the PV short
                // TODO: Store the best move in the table and get the PV from that?
                set_pv(score, vec![]);
                return score;
            }
            Some(Score::Beta(score)) => {
                if score >= beta {
                    return score;
                }
                beta = score;
            }
            None => {}
        }
    }

    let mut best_score = NEG_INFINITY;
    let mut best_move = None;

    let mut new_pv = vec![];
    for mv in board.generate_moves() {
        let mut new_board = *board;
        new_board.apply_move(&mv);

        board_list.push(*board);
        let score = -alphabeta_negamax(
            &new_board,
            &mut board_list,
            &mut new_pv,
            -beta,
            -alpha,
            depth - 1,
            ttable,
        );
        board_list.pop();

        best_score = cmp::max(score, best_score);

        if score >= beta {
            set_ttable(ttable, Score::Beta(score));
            return beta;
        } else if score > alpha {
            alpha = score;
            best_move = Some(mv);
        }
    }
    set_ttable(ttable, Score::Exact(best_score));
    if let Some(mv) = best_move {
        new_pv.push(mv);
        set_pv(alpha, new_pv);
    }
    alpha
}

fn quiescence_search(
    board: &Board,
    mut alpha: i16,
    mut beta: i16,
    depth: i8,
    ttable: &mut TTable,
) -> i16 {
    let stand_pat = evaluate(board);
    if stand_pat >= beta {
        return beta;
    } else if stand_pat + 200 < alpha {
        // Delta pruning: we bet that no capture move will raise alpha by more than 200 centipieces
        // We don't switch this off in the endgame because a draw by insufficient material is very
        // unlikely (both sides must be unable to exchange and each have only 1 piece left)
        return alpha;
    } else if alpha < stand_pat {
        alpha = stand_pat;
    }

    match ttable.get(board.zobrist, depth) {
        Some(Score::Exact(score)) => {
            return score;
        }
        Some(Score::Beta(score)) => {
            if score >= beta {
                return score;
            }
            beta = score;
        }
        None => {}
    };

    let set_ttable = |ttable: &mut TTable, score| {
        ttable.set(board.zobrist, score, depth);
    };

    for mv in board.generate_captures() {
        let mut new_board = *board;
        new_board.apply_move(&mv);

        let score = -quiescence_search(&new_board, -beta, -alpha, depth - 1, ttable);

        if score >= beta {
            set_ttable(ttable, Score::Beta(score));
            return beta;
        } else if score > alpha {
            alpha = score;
        }
    }
    set_ttable(ttable, Score::Exact(alpha));
    alpha
}

fn evaluate(board: &Board) -> i16 {
    use model::Color::*;

    let wp = 100 * i16::from(board.pieces(White));
    let bp = 100 * i16::from(board.pieces(Black));
    let wh = 50 * i16::from(board.hexes(White));
    let bh = 50 * i16::from(board.hexes(Black));

    match board.turn {
        White => (wp + wh) - (bp + bh),
        Black => (bp + bh) - (wp + wh),
    }
}
<commit_msg>Take a reference of stop_signal_clone<commit_after>/*
 * Copyright (C) 2017-2018 Ryan Huang
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

use std::cmp;
use std::mem;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::{self, Receiver, TryRecvError};
use std::sync::{Arc, Mutex};
use std::thread::{self, JoinHandle};

use model::ttable::{Score, TTable};
use model::{Board, Move, Outcome};

const NEG_INFINITY: i16 = -0x7000;
const LOSE: i16 = -0x4000;
// Small contempt factor to discourage draws
const DRAW: i16 = 1;
const ASPIRATION_WIDTH: i16 = 51;

pub struct AI {
    status: Status,
    ttable: Arc<Mutex<TTable>>,
}

enum Status {
    Idle,
    // Either the AI thread is running, or there is a move waiting to be received
    Thinking {
        move_recv: Receiver<Move>,
        // We store and load this atomic with Ordering::Relaxed. It *should* be fine because it
        // doesn't interact with any other atomics--all we want to do is tell the AI thread to stop
        // searching for a move
        stop_signal: Arc<AtomicBool>,
        handle: JoinHandle<()>,
    },
}

impl AI {
    pub fn new() -> Self {
        Self {
            status: Status::Idle,
            ttable: Arc::new(Mutex::new(TTable::new())),
        }
    }

    pub fn is_idle(&self) -> bool {
        match self.status {
            Status::Idle => true,
            Status::Thinking { .. } => false,
        }
    }

    pub fn stop(&mut self) {
        if let Status::Thinking {
            ref stop_signal, ..
        } = self.status
        {
            stop_signal.store(true, Ordering::Relaxed);
        }
        // Unconditionally assign because without NLL, we can't put this in the if let block above
        self.status = Status::Idle;
    }

    pub fn try_recv(&mut self) -> Option<Move> {
        use self::TryRecvError::*;

        let result;
        match self.status {
            Status::Idle => result = None,
            Status::Thinking { ref move_recv, .. } => match move_recv.try_recv() {
                Ok(mv) => result = Some(mv),
                Err(Empty) => result = None,
                Err(Disconnected) => panic!("Tried to receive move from disconnected sender"),
            },
        }

        // We can't set status in the Ok(mv) arm above without NLL
        if result.is_some() {
            self.status = Status::Idle;
        }
        result
    }

    pub fn think(&mut self, board: Board, board_list: Vec<Board>, depth: u8) {
        assert_ne!(depth, 0);

        let prev_status = mem::replace(&mut self.status, Status::Idle);

        let (move_sender, move_recv) = mpsc::channel();
        let stop_signal = Arc::new(AtomicBool::new(false));
        let stop_signal_clone = stop_signal.clone();

        let ttable_mutex = self.ttable.clone();

        let handle = thread::spawn(move || {
            if let Status::Thinking {
                stop_signal,
                handle,
                ..
            } = prev_status
            {
                stop_signal.store(true, Ordering::Relaxed);
                handle
                    .join()
                    .expect("Old AI thread panicked when new AI thread joined on it");
            }

            // If the previous AI thread was sent the stop signal, but hasn't received it yet, we
            // will block here until it finishes. We won't have joined on its handle above because
            // stop throws away its Status enum.
            let mut ttable = match ttable_mutex.lock() {
                Ok(table) => table,
                Err(_) => panic!("Transposition table mutex is poisoned"),
            };

            if let SearchResult::Move(mv) =
                search_root(depth, board, board_list, &mut ttable, &stop_signal_clone)
            {
                move_sender.send(mv).expect("AI failed to send Move");
            }
        });

        self.status = Status::Thinking {
            move_recv,
            stop_signal,
            handle,
        };
    }
}

enum SearchResult {
    Move(Move),
    Stopped,
}

fn search_root(
    depth: u8,
    board: Board,
    board_list: Vec<Board>,
    ttable: &mut TTable,
    stop_signal: &Arc<AtomicBool>,
) -> SearchResult {
    ttable.inc_age();

    // Only take positions after the last irreversible move
    let mut board_list: Vec<_> = board_list
        .into_iter()
        .rev()
        .take_while(|b| b.vitals == board.vitals)
        .collect();
    board_list.reverse();

    let mut moves: Vec<(Move, i16)> = board
        .generate_moves()
        .map(|mv| (mv, NEG_INFINITY))
        .collect();

    if moves.is_empty() {
        panic!("AI has no moves");
    }

    let mut pv = None;
    let mut iter_score = evaluate(&board);
    for depth in 0..depth {
        if stop_signal.load(Ordering::Relaxed) {
            return SearchResult::Stopped;
        }

        // Aspiration window search loop
        let mut asp_width = ASPIRATION_WIDTH;
        loop {
            let mut max_score = iter_score - asp_width;
            for pair in &mut moves {
                let mut new_board = board;
                new_board.apply_move(&pair.0);

                let mut new_pv = vec![];

                let score = -alphabeta_negamax(
                    &new_board,
                    &mut board_list,
                    &mut new_pv,
                    -(iter_score + asp_width),
                    -max_score,
                    depth,
                    ttable,
                );

                if score > max_score {
                    max_score = score;
                    pv = Some(new_pv);
                }
                pair.1 = score;
            }

            if max_score == iter_score + asp_width || max_score == iter_score - asp_width {
                // True score lies outside the window, so we search this position again
                asp_width *= 2;
            } else {
                break;
            }
        }

        moves.sort_by(|&(_, a), &(_, b)| b.cmp(&a));
        iter_score = moves[0].1;

        println!("\nDepth {}: {:>6}", depth, moves[0].1);
        if let Some(ref mut pv) = pv {
            pv.push(moves[0].0);
            for mv in pv.iter().rev() {
                println!("    {}", mv);
            }
        }
    }
    println!("\n---------------------");

    SearchResult::Move(moves[0].0)
}

fn alphabeta_negamax(
    board: &Board,
    // This list does not include the current board
    mut board_list: &mut Vec<Board>,
    pv: &mut Vec<Move>,
    mut alpha: i16,
    mut beta: i16,
    depth: u8,
    ttable: &mut TTable,
) -> i16 {
    let mut set_pv = move |score, new_pv| {
        if score > alpha && score < beta {
            *pv = new_pv;
        }
    };
    let set_ttable = |ttable: &mut TTable, score| {
        ttable.set(board.zobrist, score, depth as i8);
    };

    use self::Outcome::*;
    match board.outcome() {
        DrawStalemate | DrawInsufficientMaterial => {
            // This is safe to do because Board does not detect draws by threefold repetition
            set_ttable(ttable, Score::Exact(DRAW));
            set_pv(DRAW, vec![]);
            return DRAW;
        }
        Win(color) => {
            assert_ne!(color, board.turn);
            // Weight score by depth to encourage shorter wins. The shorter the win, the greater
            // `depth` will be, and so the larger the score will be. This also encourages the AI to
            // prolong a loss.
            let score = LOSE - i16::from(depth);
            set_ttable(ttable, Score::Exact(score));
            set_pv(score, vec![]);
            return score;
        }
        InProgress => {}
        DrawThreefoldRepetition => unreachable!(),
    }

    if board_list.len() >= 8 && board_list.iter().filter(|&&b| b == *board).count() >= 2 {
        set_pv(DRAW, vec![]);
        return DRAW;
    }

    if depth == 0 {
        let score = quiescence_search(board, alpha, beta, depth as i8, ttable);
        set_pv(score, vec![]);
        return score;
    }

    {
        match ttable.get(board.zobrist, depth as i8) {
            Some(Score::Exact(score)) => {
                // This will cut the PV short
                // TODO: Store the best move in the table and get the PV from that?
                set_pv(score, vec![]);
                return score;
            }
            Some(Score::Beta(score)) => {
                if score >= beta {
                    return score;
                }
                beta = score;
            }
            None => {}
        }
    }

    let mut best_score = NEG_INFINITY;
    let mut best_move = None;

    let mut new_pv = vec![];
    for mv in board.generate_moves() {
        let mut new_board = *board;
        new_board.apply_move(&mv);

        board_list.push(*board);
        let score = -alphabeta_negamax(
            &new_board,
            &mut board_list,
            &mut new_pv,
            -beta,
            -alpha,
            depth - 1,
            ttable,
        );
        board_list.pop();

        best_score = cmp::max(score, best_score);

        if score >= beta {
            set_ttable(ttable, Score::Beta(score));
            return beta;
        } else if score > alpha {
            alpha = score;
            best_move = Some(mv);
        }
    }
    set_ttable(ttable, Score::Exact(best_score));
    if let Some(mv) = best_move {
        new_pv.push(mv);
        set_pv(alpha, new_pv);
    }
    alpha
}

fn quiescence_search(
    board: &Board,
    mut alpha: i16,
    mut beta: i16,
    depth: i8,
    ttable: &mut TTable,
) -> i16 {
    let stand_pat = evaluate(board);
    if stand_pat >= beta {
        return beta;
    } else if stand_pat + 200 < alpha {
        // Delta pruning: we bet that no capture move will raise alpha by more than 200 centipieces
        // We don't switch this off in the endgame because a draw by insufficient material is very
        // unlikely (both sides must be unable to exchange and each have only 1 piece left)
        return alpha;
    } else if alpha < stand_pat {
        alpha = stand_pat;
    }

    match ttable.get(board.zobrist, depth) {
        Some(Score::Exact(score)) => {
            return score;
        }
        Some(Score::Beta(score)) => {
            if score >= beta {
                return score;
            }
            beta = score;
        }
        None => {}
    };

    let set_ttable = |ttable: &mut TTable, score| {
        ttable.set(board.zobrist, score, depth);
    };

    for mv in board.generate_captures() {
        let mut new_board = *board;
        new_board.apply_move(&mv);

        let score = -quiescence_search(&new_board, -beta, -alpha, depth - 1, ttable);

        if score >= beta {
            set_ttable(ttable, Score::Beta(score));
            return beta;
        } else if score > alpha {
            alpha = score;
        }
    }
    set_ttable(ttable, Score::Exact(alpha));
    alpha
}

fn evaluate(board: &Board) -> i16 {
    use model::Color::*;

    let wp = 100 * i16::from(board.pieces(White));
    let bp = 100 * i16::from(board.pieces(Black));
    let wh = 50 * i16::from(board.hexes(White));
    let bh = 50 * i16::from(board.hexes(Black));

    match board.turn {
        White => (wp + wh) - (bp + bh),
        Black => (bp + bh) - (wp + wh),
    }
}
<|endoftext|>


<commit_before>//! 2d line strip, 3d triangle mesh, and nd subsimplex mesh.

use bounding_volume::{self, AABB, BoundingVolume};
use math::{DIM, Isometry, Point};
use na::{self, Id, Point2, Point3, Real};
use partitioning::{BVHImpl, BVT};
use query::LocalShapeApproximation;
use shape::{CompositeShape, DeformableShape, DeformationIndex, DeformationsType, Shape, Triangle};
use std::iter;
use std::ops::Range;
use std::slice;

#[derive(Clone)]
struct RefVertex<N: Real> {
    point: Point<N>,
    adj_bvs: Range<usize>,
}

#[derive(Clone)]
struct DeformationInfos<N: Real> {
    margin: N,
    curr_timestamp: usize,
    timestamps: Vec<usize>,
    adj_list: Vec<usize>,
    ref_vertices: Vec<RefVertex<N>>,
    tri_to_update: Vec<usize>,
}

/// A 3d triangle mesh.
#[derive(Clone)]
pub struct TriMesh<N: Real> {
    bvt: BVT<usize, AABB<N>>,
    bvt_leaf_ids: Vec<usize>,
    vertices: Vec<Point<N>>,
    indices: Vec<Point3<usize>>,
    uvs: Option<Vec<Point2<N>>>,
    deformations: DeformationInfos<N>,
}

impl<N: Real> TriMesh<N> {
    /// Builds a new mesh.
    pub fn new(
        vertices: Vec<Point<N>>,
        indices: Vec<Point3<usize>>,
        uvs: Option<Vec<Point2<N>>>,
    ) -> TriMesh<N> {
        let mut leaves = Vec::new();
        let margin;

        {
            let is = &*indices;
            let mut mean_extents = N::zero();

            for (i, is) in is.iter().enumerate() {
                let triangle = Triangle::new(vertices[is.x], vertices[is.y], vertices[is.z]);
                // FIXME: loosen for better persistency?
                let bv = triangle.aabb(&Isometry::identity());
                mean_extents += bv.extents().norm() / na::convert(is.len() as f64);
                leaves.push((i, bv.clone()));
            }

            margin = mean_extents / na::convert(10.0);
        }

        let bvt = BVT::new_balanced(leaves);
        let mut bvt_leaf_ids: Vec<usize> = iter::repeat(0).take(bvt.leaves().len()).collect();

        for (i, leaf) in bvt.leaves().iter().enumerate() {
            bvt_leaf_ids[*leaf.data()] = i
        }

        let deformations = DeformationInfos {
            margin,
            curr_timestamp: 0,
            timestamps: Vec::new(),
            adj_list: Vec::new(),
            ref_vertices: Vec::new(),
            tri_to_update: Vec::new(),
        };

        TriMesh {
            bvt,
            bvt_leaf_ids,
            vertices,
            indices,
            uvs,
            deformations,
        }
    }

    /// The triangle mesh's AABB.
    #[inline]
    pub fn aabb(&self) -> &AABB<N> {
        self.bvt.root_bounding_volume().expect("An empty TriMesh has no AABB.")
    }

    /// The vertices of this mesh.
    #[inline]
    pub fn vertices(&self) -> &Vec<Point<N>> {
        &self.vertices
    }

    /// The indices of this mesh.
    #[inline]
    pub fn indices(&self) -> &Vec<Point3<usize>> {
        &self.indices
    }

    /// The texture coordinates of this mesh.
    #[inline]
    pub fn uvs(&self) -> &Option<Vec<Point2<N>>> {
        &self.uvs
    }

    /// Gets the i-th mesh element.
    #[inline]
    pub fn triangle_at(&self, i: usize) -> Triangle<N> {
        let idx = self.indices[i];

        Triangle::new(
            self.vertices[idx.x],
            self.vertices[idx.y],
            self.vertices[idx.z],
        )
    }

    /// The optimization structure used by this triangle mesh.
    #[inline]
    pub fn bvt(&self) -> &BVT<usize, AABB<N>> {
        &self.bvt
    }

    fn init_deformation_infos(&mut self) -> bool {
        if self.deformations.ref_vertices.is_empty() {
            self.deformations.ref_vertices = Vec::with_capacity(self.vertices.len());
            self.deformations.timestamps = iter::repeat(0).take(self.indices.len()).collect();
            let mut num_neighbors: Vec<usize> = iter::repeat(0).take(self.vertices.len()).collect();

            for idx in &self.indices {
                num_neighbors[idx.x] += 1;
                num_neighbors[idx.y] += 1;
                num_neighbors[idx.z] += 1;
            }

            let mut total_num_nbh = 0;

            for (num_nbh, pt) in num_neighbors.iter().zip(self.vertices.iter()) {
                self.deformations.ref_vertices.push(RefVertex {
                    point: *pt,
                    adj_bvs: total_num_nbh..total_num_nbh + num_nbh,
                });
                total_num_nbh += num_nbh;
            }

            self.deformations.adj_list = iter::repeat(0).take(total_num_nbh).collect();

            // Build the adjascency list.
            for n in &mut num_neighbors {
                *n = 0;
            }

            for (i, idx) in self.indices.iter().enumerate() {
                self.deformations.adj_list[self.deformations.ref_vertices[idx.x].adj_bvs.start + num_neighbors[idx.x]] = i;
                self.deformations.adj_list[self.deformations.ref_vertices[idx.y].adj_bvs.start + num_neighbors[idx.y]] = i;
                self.deformations.adj_list[self.deformations.ref_vertices[idx.z].adj_bvs.start + num_neighbors[idx.z]] = i;

                num_neighbors[idx.x] += 1;
                num_neighbors[idx.y] += 1;
                num_neighbors[idx.z] += 1;
            }

            true
        } else {
            false
        }
    }
}

impl<N: Real> CompositeShape<N> for TriMesh<N> {
    #[inline]
    fn nparts(&self) -> usize {
        self.indices.len()
    }

    #[inline(always)]
    fn map_part_at(&self, i: usize, f: &mut FnMut(usize, &Isometry<N>, &Shape<N>)) {
        self.map_transformed_part_at(i, &Isometry::identity(), f)
    }

    #[inline(always)]
    fn map_transformed_part_at(
        &self,
        i: usize,
        m: &Isometry<N>,
        f: &mut FnMut(usize, &Isometry<N>, &Shape<N>),
    ) {
        let element = self.triangle_at(i);

        f(i, m, &element)
    }

    #[inline]
    fn aabb_at(&self, i: usize) -> AABB<N> {
        self.bvt.leaf(self.bvt_leaf_ids[i]).bounding_volume().clone()
    }

    #[inline]
    fn bvh(&self) -> BVHImpl<N, usize, AABB<N>> {
        BVHImpl::BVT(&self.bvt)
    }
}

impl<N: Real> DeformableShape<N> for TriMesh<N> {
    fn deformations_type(&self) -> DeformationsType {
        DeformationsType::Vectors
    }

    /// Updates all the degrees of freedom of this shape.
    fn set_deformations(&mut self, coords: &[N], indices: Option<&[usize]>) {
        let is_first_init = self.init_deformation_infos();
        self.deformations.curr_timestamp += 1;

        if indices.is_none() {
            // There is a bit of unsafe code in order to perform a memcopy for
            // efficiency reasons when the mapping between degrees of freedom
            // is trivial.
            unsafe {
                let len = coords.len() / DIM;
                let coords_ptr = coords.as_ptr() as *const Point<N>;
                let coords_pt: &[Point<N>] = slice::from_raw_parts(coords_ptr, len);
                self.vertices.copy_from_slice(coords_pt);
            }
        }

        for (target, pt) in self.vertices.iter_mut().enumerate() {
            if let Some(idx) = indices {
                let source = idx[target];
                pt.coords.copy_from_slice(&coords[source..source + DIM]);
            }

            let ref_pt = &mut self.deformations.ref_vertices[target];
            let sq_dist_to_ref = na::distance_squared(pt, &ref_pt.point);

            if is_first_init || sq_dist_to_ref > self.deformations.margin * self.deformations.margin {
                // We have to update the adjacent bounding volumes.
                self.deformations.tri_to_update.extend_from_slice(&self.deformations.adj_list[ref_pt.adj_bvs.clone()]);
                ref_pt.point = *pt;
            }
        }

        // Apply the bounding volumes changes.
        for tri_id in self.deformations.tri_to_update.drain(..) {
            if self.deformations.timestamps[tri_id] != self.deformations.curr_timestamp {
                // Update the BV.
                let idx = &self.indices[tri_id];
                let mut new_bv = bounding_volume::point_cloud_aabb(&Id::new(), &[self.vertices[idx.x], self.vertices[idx.y], self.vertices[idx.z]]);
                new_bv.loosen(self.deformations.margin);
                self.bvt.set_leaf_bounding_volume(self.bvt_leaf_ids[tri_id], new_bv, false);
                self.deformations.timestamps[tri_id] = self.deformations.curr_timestamp;
            }
        }

        // FIXME: measure efficiency with a non-zero margin.
        self.bvt.refit(N::zero())
    }

    fn update_local_approximation(
        &self,
        coords: &[N],
        indices: Option<&[usize]>,
        part_id: usize,
        approx: &mut LocalShapeApproximation<N>,
    ) {
        let tri_id = self.indices[part_id];
        let (a_id, b_id, c_id) = if let Some(idx) = indices {
            (idx[tri_id.x], idx[tri_id.y], idx[tri_id.z])
        } else {
            (tri_id.x * 3, tri_id.y * 3, tri_id.z * 3)
        };

        let a = Point3::new(coords[a_id + 0], coords[a_id + 1], coords[a_id + 2]);
        let b = Point3::new(coords[b_id + 0], coords[b_id + 1], coords[b_id + 2]);
        let c = Point3::new(coords[c_id + 0], coords[c_id + 1], coords[c_id + 2]);
        let tri = Triangle::new(a, b, c);

        tri.update_local_approximation(approx);
    }
}<commit_msg>Impl From<procedural::TriMesh<N>> for TriMesh<N><commit_after>//! 2d line strip, 3d triangle mesh, and nd subsimplex mesh.

use bounding_volume::{self, AABB, BoundingVolume};
use math::{DIM, Isometry, Point};
use na::{self, Id, Point2, Point3, Real};
use partitioning::{BVHImpl, BVT};
use procedural;
use query::LocalShapeApproximation;
use shape::{CompositeShape, DeformableShape, DeformationIndex, DeformationsType, Shape, Triangle};
use std::iter;
use std::ops::Range;
use std::slice;

#[derive(Clone)]
struct RefVertex<N: Real> {
    point: Point<N>,
    adj_bvs: Range<usize>,
}

#[derive(Clone)]
struct DeformationInfos<N: Real> {
    margin: N,
    curr_timestamp: usize,
    timestamps: Vec<usize>,
    adj_list: Vec<usize>,
    ref_vertices: Vec<RefVertex<N>>,
    tri_to_update: Vec<usize>,
}

/// A 3d triangle mesh.
#[derive(Clone)]
pub struct TriMesh<N: Real> {
    bvt: BVT<usize, AABB<N>>,
    bvt_leaf_ids: Vec<usize>,
    vertices: Vec<Point<N>>,
    indices: Vec<Point3<usize>>,
    uvs: Option<Vec<Point2<N>>>,
    deformations: DeformationInfos<N>,
}

impl<N: Real> TriMesh<N> {
    /// Builds a new mesh.
    pub fn new(
        vertices: Vec<Point<N>>,
        indices: Vec<Point3<usize>>,
        uvs: Option<Vec<Point2<N>>>,
    ) -> TriMesh<N> {
        let mut leaves = Vec::new();
        let margin;

        {
            let is = &*indices;
            let mut mean_extents = N::zero();

            for (i, is) in is.iter().enumerate() {
                let triangle = Triangle::new(vertices[is.x], vertices[is.y], vertices[is.z]);
                // FIXME: loosen for better persistency?
                let bv = triangle.aabb(&Isometry::identity());
                mean_extents += bv.extents().norm() / na::convert(is.len() as f64);
                leaves.push((i, bv.clone()));
            }

            margin = mean_extents / na::convert(10.0);
        }

        let bvt = BVT::new_balanced(leaves);
        let mut bvt_leaf_ids: Vec<usize> = iter::repeat(0).take(bvt.leaves().len()).collect();

        for (i, leaf) in bvt.leaves().iter().enumerate() {
            bvt_leaf_ids[*leaf.data()] = i
        }

        let deformations = DeformationInfos {
            margin,
            curr_timestamp: 0,
            timestamps: Vec::new(),
            adj_list: Vec::new(),
            ref_vertices: Vec::new(),
            tri_to_update: Vec::new(),
        };

        TriMesh {
            bvt,
            bvt_leaf_ids,
            vertices,
            indices,
            uvs,
            deformations,
        }
    }

    /// The triangle mesh's AABB.
    #[inline]
    pub fn aabb(&self) -> &AABB<N> {
        self.bvt.root_bounding_volume().expect("An empty TriMesh has no AABB.")
    }

    /// The vertices of this mesh.
    #[inline]
    pub fn vertices(&self) -> &Vec<Point<N>> {
        &self.vertices
    }

    /// The indices of this mesh.
    #[inline]
    pub fn indices(&self) -> &Vec<Point3<usize>> {
        &self.indices
    }

    /// The texture coordinates of this mesh.
    #[inline]
    pub fn uvs(&self) -> &Option<Vec<Point2<N>>> {
        &self.uvs
    }

    /// Gets the i-th mesh element.
    #[inline]
    pub fn triangle_at(&self, i: usize) -> Triangle<N> {
        let idx = self.indices[i];

        Triangle::new(
            self.vertices[idx.x],
            self.vertices[idx.y],
            self.vertices[idx.z],
        )
    }

    /// The optimization structure used by this triangle mesh.
    #[inline]
    pub fn bvt(&self) -> &BVT<usize, AABB<N>> {
        &self.bvt
    }

    fn init_deformation_infos(&mut self) -> bool {
        if self.deformations.ref_vertices.is_empty() {
            self.deformations.ref_vertices = Vec::with_capacity(self.vertices.len());
            self.deformations.timestamps = iter::repeat(0).take(self.indices.len()).collect();
            let mut num_neighbors: Vec<usize> = iter::repeat(0).take(self.vertices.len()).collect();

            for idx in &self.indices {
                num_neighbors[idx.x] += 1;
                num_neighbors[idx.y] += 1;
                num_neighbors[idx.z] += 1;
            }

            let mut total_num_nbh = 0;

            for (num_nbh, pt) in num_neighbors.iter().zip(self.vertices.iter()) {
                self.deformations.ref_vertices.push(RefVertex {
                    point: *pt,
                    adj_bvs: total_num_nbh..total_num_nbh + num_nbh,
                });
                total_num_nbh += num_nbh;
            }

            self.deformations.adj_list = iter::repeat(0).take(total_num_nbh).collect();

            // Build the adjascency list.
            for n in &mut num_neighbors {
                *n = 0;
            }

            for (i, idx) in self.indices.iter().enumerate() {
                self.deformations.adj_list[self.deformations.ref_vertices[idx.x].adj_bvs.start + num_neighbors[idx.x]] = i;
                self.deformations.adj_list[self.deformations.ref_vertices[idx.y].adj_bvs.start + num_neighbors[idx.y]] = i;
                self.deformations.adj_list[self.deformations.ref_vertices[idx.z].adj_bvs.start + num_neighbors[idx.z]] = i;

                num_neighbors[idx.x] += 1;
                num_neighbors[idx.y] += 1;
                num_neighbors[idx.z] += 1;
            }

            true
        } else {
            false
        }
    }
}

impl<N: Real> CompositeShape<N> for TriMesh<N> {
    #[inline]
    fn nparts(&self) -> usize {
        self.indices.len()
    }

    #[inline(always)]
    fn map_part_at(&self, i: usize, f: &mut FnMut(usize, &Isometry<N>, &Shape<N>)) {
        self.map_transformed_part_at(i, &Isometry::identity(), f)
    }

    #[inline(always)]
    fn map_transformed_part_at(
        &self,
        i: usize,
        m: &Isometry<N>,
        f: &mut FnMut(usize, &Isometry<N>, &Shape<N>),
    ) {
        let element = self.triangle_at(i);

        f(i, m, &element)
    }

    #[inline]
    fn aabb_at(&self, i: usize) -> AABB<N> {
        self.bvt.leaf(self.bvt_leaf_ids[i]).bounding_volume().clone()
    }

    #[inline]
    fn bvh(&self) -> BVHImpl<N, usize, AABB<N>> {
        BVHImpl::BVT(&self.bvt)
    }
}

impl<N: Real> DeformableShape<N> for TriMesh<N> {
    fn deformations_type(&self) -> DeformationsType {
        DeformationsType::Vectors
    }

    /// Updates all the degrees of freedom of this shape.
    fn set_deformations(&mut self, coords: &[N], indices: Option<&[usize]>) {
        let is_first_init = self.init_deformation_infos();
        self.deformations.curr_timestamp += 1;

        if indices.is_none() {
            // There is a bit of unsafe code in order to perform a memcopy for
            // efficiency reasons when the mapping between degrees of freedom
            // is trivial.
            unsafe {
                let len = coords.len() / DIM;
                let coords_ptr = coords.as_ptr() as *const Point<N>;
                let coords_pt: &[Point<N>] = slice::from_raw_parts(coords_ptr, len);
                self.vertices.copy_from_slice(coords_pt);
            }
        }

        for (target, pt) in self.vertices.iter_mut().enumerate() {
            if let Some(idx) = indices {
                let source = idx[target];
                pt.coords.copy_from_slice(&coords[source..source + DIM]);
            }

            let ref_pt = &mut self.deformations.ref_vertices[target];
            let sq_dist_to_ref = na::distance_squared(pt, &ref_pt.point);

            if is_first_init || sq_dist_to_ref > self.deformations.margin * self.deformations.margin {
                // We have to update the adjacent bounding volumes.
                self.deformations.tri_to_update.extend_from_slice(&self.deformations.adj_list[ref_pt.adj_bvs.clone()]);
                ref_pt.point = *pt;
            }
        }

        // Apply the bounding volumes changes.
        for tri_id in self.deformations.tri_to_update.drain(..) {
            if self.deformations.timestamps[tri_id] != self.deformations.curr_timestamp {
                // Update the BV.
                let idx = &self.indices[tri_id];
                let mut new_bv = bounding_volume::point_cloud_aabb(&Id::new(), &[self.vertices[idx.x], self.vertices[idx.y], self.vertices[idx.z]]);
                new_bv.loosen(self.deformations.margin);
                self.bvt.set_leaf_bounding_volume(self.bvt_leaf_ids[tri_id], new_bv, false);
                self.deformations.timestamps[tri_id] = self.deformations.curr_timestamp;
            }
        }

        // FIXME: measure efficiency with a non-zero margin.
        self.bvt.refit(N::zero())
    }

    fn update_local_approximation(
        &self,
        coords: &[N],
        indices: Option<&[usize]>,
        part_id: usize,
        approx: &mut LocalShapeApproximation<N>,
    ) {
        let tri_id = self.indices[part_id];
        let (a_id, b_id, c_id) = if let Some(idx) = indices {
            (idx[tri_id.x], idx[tri_id.y], idx[tri_id.z])
        } else {
            (tri_id.x * 3, tri_id.y * 3, tri_id.z * 3)
        };

        let a = Point3::new(coords[a_id + 0], coords[a_id + 1], coords[a_id + 2]);
        let b = Point3::new(coords[b_id + 0], coords[b_id + 1], coords[b_id + 2]);
        let c = Point3::new(coords[c_id + 0], coords[c_id + 1], coords[c_id + 2]);
        let tri = Triangle::new(a, b, c);

        tri.update_local_approximation(approx);
    }
}

impl<N: Real> From<procedural::TriMesh<N>> for TriMesh<N> {
    fn from(trimesh: procedural::TriMesh<N>) -> Self {
        let indices =
            trimesh
                .flat_indices()
                .chunks(3).map(|idx| Point3::new(idx[0] as usize, idx[1] as usize, idx[2] as usize))
                .collect();
        TriMesh::new(trimesh.coords, indices, trimesh.uvs)
    }
}<|endoftext|>


<commit_before>//! Parser for `SELinux` security attributes, and abstractions new to the CIL framework.

use ast::*;
use name::*;

named!(pub level<&[u8], Expr>,
    do_parse!(
        sensitivity: identifier >>
        tag!(":") >>
        categories: category_range_or_id >>

        (Expr::Level {
            sensitivity,
            categories: Box::new(categories)
        })
    )
);

named!(level_or_id<&[u8], Expr>, alt_complete!(level | variable));

named!(pub level_range<&[u8], Expr>,
    ws!(do_parse!(
        range: separated_pair!(level_or_id, tag!("-"), level_or_id) >>

        (Expr::LevelRange(
            Box::new(range.0), Box::new(range.1)
        ))
    ))
);

named!(category_range_or_id<&[u8], Expr>, alt_complete!(category_range | variable));

named!(pub category_range<&[u8], Expr>,
    ws!(do_parse!(
        range: separated_pair!(identifier, char!('.'), identifier) >>

        (Expr::CategoryRange(
            range.0, range.1
        ))
    ))
);

named!(pub context<&[u8], Expr>,
  ws!(do_parse!(
      user_id: identifier >>
      char!(':') >>
      role_id: identifier >>
      char!(':') >>
      type_id: identifier >>
      level_range: opt!(complete!(preceded!(char!(':'), level_range))) >>

      (Expr::Context {
        user_id,
        role_id,
        type_id,
        level_range: level_range.map(Box::new)
      })
  ))
);

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

    #[test]
    pub fn parse_context_expr() {
        let result = parse::<Expr, _>("user:role:type", context);

        match result {
            Expr::Context {
                user_id,
                role_id,
                type_id,
                level_range,
            } => {
                assert_eq!("user", user_id);
                assert_eq!("role", role_id);
                assert_eq!("type", type_id);
                assert_eq!(true, level_range.is_none());
            }
            _ => panic!("Invalid value parsed"),
        }
    }

    #[test]
    pub fn parse_levelrange_expr() {
        let result = parse::<Expr, _>("s0-s1", level_range);

        if let Expr::LevelRange(low, high) = result {
            assert_eq!(Expr::var("s0"), *low);
            assert_eq!(Expr::var("s1"), *high);
        } else {
            panic!("Invalid value parsed");
        }
    }

    #[test]
    pub fn parse_context_expr_with_levelrange() {
        let result = parse::<Expr, _>("user:role:type:s0 - s1", context);

        match result {
            Expr::Context { level_range, .. } => {
                if let &Expr::LevelRange(ref low, ref high) = level_range.unwrap().as_ref() {
                    assert_eq!(Expr::var("s0"), **low);
                    assert_eq!(Expr::var("s1"), **high);
                } else {
                    panic!("No level range found");
                }
            }
            _ => panic!("Invalid value parsed"),
        }
    }

}<commit_msg>Add tests covering level parsing<commit_after>//! Parser for `SELinux` security attributes, and abstractions new to the CIL framework.

use ast::*;
use name::*;

named!(pub level<&[u8], Expr>,
    do_parse!(
        sensitivity: identifier >>
        tag!(":") >>
        categories: category_range_or_id >>

        (Expr::Level {
            sensitivity,
            categories: Box::new(categories)
        })
    )
);

named!(level_or_id<&[u8], Expr>, alt_complete!(level | variable));

named!(pub level_range<&[u8], Expr>,
    ws!(do_parse!(
        range: separated_pair!(level_or_id, tag!("-"), level_or_id) >>

        (Expr::LevelRange(
            Box::new(range.0), Box::new(range.1)
        ))
    ))
);

named!(category_range_or_id<&[u8], Expr>, alt_complete!(category_range | variable));

named!(pub category_range<&[u8], Expr>,
    ws!(do_parse!(
        range: separated_pair!(identifier, char!('.'), identifier) >>

        (Expr::CategoryRange(
            range.0, range.1
        ))
    ))
);

named!(pub context<&[u8], Expr>,
  ws!(do_parse!(
      user_id: identifier >>
      char!(':') >>
      role_id: identifier >>
      char!(':') >>
      type_id: identifier >>
      level_range: opt!(complete!(preceded!(char!(':'), level_range))) >>

      (Expr::Context {
        user_id,
        role_id,
        type_id,
        level_range: level_range.map(Box::new)
      })
  ))
);

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

    #[test]
    pub fn parse_context_expr() {
        let result = parse::<Expr, _>("user:role:type", context);

        match result {
            Expr::Context {
                user_id,
                role_id,
                type_id,
                level_range,
            } => {
                assert_eq!("user", user_id);
                assert_eq!("role", role_id);
                assert_eq!("type", type_id);
                assert_eq!(true, level_range.is_none());
            }
            _ => panic!("Invalid value parsed"),
        }
    }

    #[test]
    pub fn parse_levelrange_expr() {
        let result = parse::<Expr, _>("s0-s1", level_range);

        if let Expr::LevelRange(low, high) = result {
            assert_eq!(Expr::var("s0"), *low);
            assert_eq!(Expr::var("s1"), *high);
        } else {
            panic!("Invalid value parsed");
        }
    }

    #[test]
    pub fn parse_context_expr_with_levelrange() {
        let result = parse::<Expr, _>("user:role:type:s0 - s1", context);

        match result {
            Expr::Context { level_range, .. } => {
                if let &Expr::LevelRange(ref low, ref high) = level_range.unwrap().as_ref() {
                    assert_eq!(Expr::var("s0"), **low);
                    assert_eq!(Expr::var("s1"), **high);
                } else {
                    panic!("No level range found");
                }
            }
            _ => panic!("Invalid value parsed"),
        }
    }

    #[test]
    pub fn parse_level_with_categoryrange() {
        let result = parse::<Expr, _>("s0:c1.c2", level);
        let actual = Expr::Level {
            sensitivity: "s0".to_string(),
            categories: Box::from(Expr::CategoryRange("c1".to_string(), "c2".to_string()))
        };

        assert_eq!(result, actual);
    }

    #[test]
    pub fn parse_level_with_categoryset_id() {
        let result = parse::<Expr, _>("s0:categories", level);
        let actual = Expr::Level {
            sensitivity: "s0".to_string(),
            categories: Box::from(Expr::var("categories"))
        };

        assert_eq!(result, actual);
    }

}<|endoftext|>


<commit_before>use std::ops::Range;

use askama::Template;

#[derive(Template)]
#[template(path = "for.html")]
struct ForTemplate<'a> {
    strings: Vec<&'a str>,
    tuple_strings: Vec<(&'a str, &'a str)>,
}

#[test]
fn test_for() {
    let s = ForTemplate {
        strings: vec!["A", "alfa", "1"],
        tuple_strings: vec![("B", "beta")],
    };
    assert_eq!(
        s.render().unwrap(),
        "0. A (first)\n1. alfa\n2. 1\n\n0. B,beta (first)\n"
    );
}

#[derive(Template)]
#[template(path = "nested-for.html")]
struct NestedForTemplate<'a> {
    seqs: Vec<&'a [&'a str]>,
}

#[test]
fn test_nested_for() {
    let alpha = vec!["a", "b", "c"];
    let numbers = vec!["one", "two"];
    let s = NestedForTemplate {
        seqs: vec![&alpha, &numbers],
    };
    assert_eq!(s.render().unwrap(), "1\n  0a1b2c2\n  0one1two");
}

#[derive(Template)]
#[template(path = "precedence-for.html")]
struct PrecedenceTemplate<'a> {
    strings: Vec<&'a str>,
}

#[test]
fn test_precedence_for() {
    let s = PrecedenceTemplate {
        strings: vec!["A", "alfa", "1"],
    };
    assert_eq!(
        s.render().unwrap(),
        "0. A2 (first)\n1. alfa4\n2. 16 (last)\n"
    );
}

#[derive(Template)]
#[template(path = "for-range.html")]
struct ForRangeTemplate {
    init: i32,
    end: i32,
}

#[test]
fn test_for_range() {
    let s = ForRangeTemplate { init: -1, end: 1 };
    assert_eq!(
        s.render().unwrap(),
        "foo (first)\nfoo (last)\nbar\nbar\nfoo\nbar\nbar\n"
    );
}

#[derive(Template)]
#[template(source = "{% for i in [1, 2, 3] %}{{ i }}{% endfor %}", ext = "txt")]
struct ForArrayTemplate;

#[test]
fn test_for_array() {
    let t = ForArrayTemplate;
    assert_eq!(t.render().unwrap(), "123");
}

#[derive(Template)]
#[template(
    source = "{% for i in [1, 2, 3].iter() %}{{ i }}{% endfor %}",
    ext = "txt"
)]
struct ForMethodCallTemplate;

#[test]
fn test_for_method_call() {
    let t = ForMethodCallTemplate;
    assert_eq!(t.render().unwrap(), "123");
}

#[derive(Template)]
#[template(
    source = "{% for i in ::std::iter::repeat(\"a\").take(5) %}{{ i }}{% endfor %}",
    ext = "txt"
)]
struct ForPathCallTemplate;

#[test]
fn test_for_path_call() {
    assert_eq!(ForPathCallTemplate.render().unwrap(), "aaaaa");
}

#[derive(Template)]
#[template(
    source = "{% for i in [1, 2, 3, 4, 5][3..] %}{{ i }}{% endfor %}",
    ext = "txt"
)]
struct ForIndexTemplate;

#[test]
fn test_for_index() {
    let t = ForIndexTemplate;
    assert_eq!(t.render().unwrap(), "45");
}

#[derive(Template)]
#[template(
    source = "{% for (i, j) in (0..10).zip(10..20).zip(30..40) %}{{ i.0 }} {{ i.1 }} {{ j }} {% endfor %}",
    ext = "txt"
)]
struct ForZipRangesTemplate;

#[test]
fn test_for_zip_ranges() {
    let t = ForZipRangesTemplate;
    assert_eq!(
        t.render().unwrap(),
        "0 10 30 1 11 31 2 12 32 3 13 33 4 14 34 5 15 35 6 16 36 7 17 37 8 18 38 9 19 39 "
    );
}

struct ForVecAttrVec {
    iterable: Vec<i32>,
}

#[derive(Template)]
#[template(
    source = "{% for x in v %}{% for y in x.iterable %}{{ y }} {% endfor %}{% endfor %}",
    ext = "txt"
)]
struct ForVecAttrVecTemplate {
    v: Vec<ForVecAttrVec>,
}

#[test]
fn test_for_vec_attr_vec() {
    let t = ForVecAttrVecTemplate {
        v: vec![
            ForVecAttrVec {
                iterable: vec![1, 2],
            },
            ForVecAttrVec {
                iterable: vec![3, 4],
            },
            ForVecAttrVec {
                iterable: vec![5, 6],
            },
        ],
    };
    assert_eq!(t.render().unwrap(), "1 2 3 4 5 6 ");
}

struct ForVecAttrSlice {
    iterable: &'static [i32],
}

#[derive(Template)]
#[template(
    source = "{% for x in v %}{% for y in x.iterable %}{{ y }} {% endfor %}{% endfor %}",
    ext = "txt"
)]
struct ForVecAttrSliceTemplate {
    v: Vec<ForVecAttrSlice>,
}

#[test]
fn test_for_vec_attr_slice() {
    let t = ForVecAttrSliceTemplate {
        v: vec![
            ForVecAttrSlice { iterable: &[1, 2] },
            ForVecAttrSlice { iterable: &[3, 4] },
            ForVecAttrSlice { iterable: &[5, 6] },
        ],
    };
    assert_eq!(t.render().unwrap(), "1 2 3 4 5 6 ");
}

struct ForVecAttrRange {
    iterable: Range<usize>,
}

#[derive(Template)]
#[template(
    source = "{% for x in v %}{% for y in x.iterable.clone() %}{{ y }} {% endfor %}{% endfor %}",
    ext = "txt"
)]
struct ForVecAttrRangeTemplate {
    v: Vec<ForVecAttrRange>,
}

#[test]
fn test_for_vec_attr_range() {
    let t = ForVecAttrRangeTemplate {
        v: vec![
            ForVecAttrRange { iterable: 1..3 },
            ForVecAttrRange { iterable: 3..5 },
            ForVecAttrRange { iterable: 5..7 },
        ],
    };
    assert_eq!(t.render().unwrap(), "1 2 3 4 5 6 ");
}

#[derive(Template)]
#[template(
    source = "{% for v in v %}{% let v = v %}{% for v in v.iterable %}{% let v = v %}{{ v }} {% endfor %}{% endfor %}",
    ext = "txt"
)]
struct ForVecAttrSliceShadowingTemplate {
    v: Vec<ForVecAttrSlice>,
}

#[test]
fn test_for_vec_attr_slice_shadowing() {
    let t = ForVecAttrSliceShadowingTemplate {
        v: vec![
            ForVecAttrSlice { iterable: &[1, 2] },
            ForVecAttrSlice { iterable: &[3, 4] },
            ForVecAttrSlice { iterable: &[5, 6] },
        ],
    };
    assert_eq!(t.render().unwrap(), "1 2 3 4 5 6 ");
}
<commit_msg>Add "destructoring tuple in loop" test<commit_after>#![allow(clippy::type_complexity)]

use std::fmt;
use std::ops::Range;

use askama::Template;

#[derive(Template)]
#[template(path = "for.html")]
struct ForTemplate<'a> {
    strings: Vec<&'a str>,
    tuple_strings: Vec<(&'a str, &'a str)>,
}

#[test]
fn test_for() {
    let s = ForTemplate {
        strings: vec!["A", "alfa", "1"],
        tuple_strings: vec![("B", "beta")],
    };
    assert_eq!(
        s.render().unwrap(),
        "0. A (first)\n1. alfa\n2. 1\n\n0. B,beta (first)\n"
    );
}

#[derive(Template)]
#[template(path = "nested-for.html")]
struct NestedForTemplate<'a> {
    seqs: Vec<&'a [&'a str]>,
}

#[test]
fn test_nested_for() {
    let alpha = vec!["a", "b", "c"];
    let numbers = vec!["one", "two"];
    let s = NestedForTemplate {
        seqs: vec![&alpha, &numbers],
    };
    assert_eq!(s.render().unwrap(), "1\n  0a1b2c2\n  0one1two");
}

#[derive(Template)]
#[template(path = "precedence-for.html")]
struct PrecedenceTemplate<'a> {
    strings: Vec<&'a str>,
}

#[test]
fn test_precedence_for() {
    let s = PrecedenceTemplate {
        strings: vec!["A", "alfa", "1"],
    };
    assert_eq!(
        s.render().unwrap(),
        "0. A2 (first)\n1. alfa4\n2. 16 (last)\n"
    );
}

#[derive(Template)]
#[template(path = "for-range.html")]
struct ForRangeTemplate {
    init: i32,
    end: i32,
}

#[test]
fn test_for_range() {
    let s = ForRangeTemplate { init: -1, end: 1 };
    assert_eq!(
        s.render().unwrap(),
        "foo (first)\nfoo (last)\nbar\nbar\nfoo\nbar\nbar\n"
    );
}

#[derive(Template)]
#[template(source = "{% for i in [1, 2, 3] %}{{ i }}{% endfor %}", ext = "txt")]
struct ForArrayTemplate;

#[test]
fn test_for_array() {
    let t = ForArrayTemplate;
    assert_eq!(t.render().unwrap(), "123");
}

#[derive(Template)]
#[template(
    source = "{% for i in [1, 2, 3].iter() %}{{ i }}{% endfor %}",
    ext = "txt"
)]
struct ForMethodCallTemplate;

#[test]
fn test_for_method_call() {
    let t = ForMethodCallTemplate;
    assert_eq!(t.render().unwrap(), "123");
}

#[derive(Template)]
#[template(
    source = "{% for i in ::std::iter::repeat(\"a\").take(5) %}{{ i }}{% endfor %}",
    ext = "txt"
)]
struct ForPathCallTemplate;

#[test]
fn test_for_path_call() {
    assert_eq!(ForPathCallTemplate.render().unwrap(), "aaaaa");
}

#[derive(Template)]
#[template(
    source = "{% for i in [1, 2, 3, 4, 5][3..] %}{{ i }}{% endfor %}",
    ext = "txt"
)]
struct ForIndexTemplate;

#[test]
fn test_for_index() {
    let t = ForIndexTemplate;
    assert_eq!(t.render().unwrap(), "45");
}

#[derive(Template)]
#[template(
    source = "{% for (i, j) in (0..10).zip(10..20).zip(30..40) %}{{ i.0 }} {{ i.1 }} {{ j }} {% endfor %}",
    ext = "txt"
)]
struct ForZipRangesTemplate;

#[test]
fn test_for_zip_ranges() {
    let t = ForZipRangesTemplate;
    assert_eq!(
        t.render().unwrap(),
        "0 10 30 1 11 31 2 12 32 3 13 33 4 14 34 5 15 35 6 16 36 7 17 37 8 18 38 9 19 39 "
    );
}

struct ForVecAttrVec {
    iterable: Vec<i32>,
}

#[derive(Template)]
#[template(
    source = "{% for x in v %}{% for y in x.iterable %}{{ y }} {% endfor %}{% endfor %}",
    ext = "txt"
)]
struct ForVecAttrVecTemplate {
    v: Vec<ForVecAttrVec>,
}

#[test]
fn test_for_vec_attr_vec() {
    let t = ForVecAttrVecTemplate {
        v: vec![
            ForVecAttrVec {
                iterable: vec![1, 2],
            },
            ForVecAttrVec {
                iterable: vec![3, 4],
            },
            ForVecAttrVec {
                iterable: vec![5, 6],
            },
        ],
    };
    assert_eq!(t.render().unwrap(), "1 2 3 4 5 6 ");
}

struct ForVecAttrSlice {
    iterable: &'static [i32],
}

#[derive(Template)]
#[template(
    source = "{% for x in v %}{% for y in x.iterable %}{{ y }} {% endfor %}{% endfor %}",
    ext = "txt"
)]
struct ForVecAttrSliceTemplate {
    v: Vec<ForVecAttrSlice>,
}

#[test]
fn test_for_vec_attr_slice() {
    let t = ForVecAttrSliceTemplate {
        v: vec![
            ForVecAttrSlice { iterable: &[1, 2] },
            ForVecAttrSlice { iterable: &[3, 4] },
            ForVecAttrSlice { iterable: &[5, 6] },
        ],
    };
    assert_eq!(t.render().unwrap(), "1 2 3 4 5 6 ");
}

struct ForVecAttrRange {
    iterable: Range<usize>,
}

#[derive(Template)]
#[template(
    source = "{% for x in v %}{% for y in x.iterable.clone() %}{{ y }} {% endfor %}{% endfor %}",
    ext = "txt"
)]
struct ForVecAttrRangeTemplate {
    v: Vec<ForVecAttrRange>,
}

#[test]
fn test_for_vec_attr_range() {
    let t = ForVecAttrRangeTemplate {
        v: vec![
            ForVecAttrRange { iterable: 1..3 },
            ForVecAttrRange { iterable: 3..5 },
            ForVecAttrRange { iterable: 5..7 },
        ],
    };
    assert_eq!(t.render().unwrap(), "1 2 3 4 5 6 ");
}

#[derive(Template)]
#[template(
    source = "{% for v in v %}{% let v = v %}{% for v in v.iterable %}{% let v = v %}{{ v }} {% endfor %}{% endfor %}",
    ext = "txt"
)]
struct ForVecAttrSliceShadowingTemplate {
    v: Vec<ForVecAttrSlice>,
}

#[test]
fn test_for_vec_attr_slice_shadowing() {
    let t = ForVecAttrSliceShadowingTemplate {
        v: vec![
            ForVecAttrSlice { iterable: &[1, 2] },
            ForVecAttrSlice { iterable: &[3, 4] },
            ForVecAttrSlice { iterable: &[5, 6] },
        ],
    };
    assert_eq!(t.render().unwrap(), "1 2 3 4 5 6 ");
}

struct NotClonable<T: fmt::Display>(T);

impl<T: fmt::Display> fmt::Display for NotClonable<T> {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.0.fmt(formatter)
    }
}

#[derive(Template)]
#[template(
    source = "{% for (((a,), b), c) in v %}{{a}}{{b}}{{c}}-{% endfor %}",
    ext = "txt"
)]
struct ForDestructoringRefTupleTemplate<'a> {
    v: &'a [(((char,), NotClonable<char>), &'a char)],
}

#[test]
fn test_for_destructoring_ref_tuple() {
    let v = [
        ((('a',), NotClonable('b')), &'c'),
        ((('d',), NotClonable('e')), &'f'),
        ((('g',), NotClonable('h')), &'i'),
    ];
    let t = ForDestructoringRefTupleTemplate { v: &v };
    assert_eq!(t.render().unwrap(), "abc-def-ghi-");
}

#[derive(Template)]
#[template(
    source = "{% for (((a,), b), c) in v %}{{a}}{{b}}{{c}}-{% endfor %}",
    ext = "txt"
)]
struct ForDestructoringTupleTemplate<'a, const N: usize> {
    v: [(((char,), NotClonable<char>), &'a char); N],
}

#[test]
fn test_for_destructoring_tuple() {
    let t = ForDestructoringTupleTemplate {
        v: [
            ((('a',), NotClonable('b')), &'c'),
            ((('d',), NotClonable('e')), &'f'),
            ((('g',), NotClonable('h')), &'i'),
        ],
    };
    assert_eq!(t.render().unwrap(), "abc-def-ghi-");
}

#[derive(Template)]
#[template(
    source = "{% for (i, msg) in messages.iter().enumerate() %}{{i}}={{msg}}-{% endfor %}",
    ext = "txt"
)]
struct ForEnumerateTemplate<'a> {
    messages: &'a [&'a str],
}

#[test]
fn test_for_enumerate() {
    let t = ForEnumerateTemplate {
        messages: &["hello", "world", "!"],
    };
    assert_eq!(t.render().unwrap(), "0=hello-1=world-2=!-");
}
<|endoftext|>


<commit_before>use std::cmp::Ordering;
use std::ffi;
use gl;

/// Describes an OpenGL ctxt.version.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct GlVersion(pub u8, pub u8);

impl PartialOrd for GlVersion {
    fn partial_cmp(&self, other: &GlVersion) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for GlVersion {
    fn cmp(&self, other: &GlVersion) -> Ordering {
        match self.0.cmp(&other.0) {
            Ordering::Equal => self.1.cmp(&other.1),
            a => a
        }
    }
}

pub fn get_gl_version(gl: &gl::Gl) -> GlVersion {
    unsafe {
        let version = gl.GetString(gl::VERSION) as *const i8;
        let version = String::from_utf8(ffi::c_str_to_bytes(&version).to_vec()).unwrap();

        let version = version.words().next().expect("glGetString(GL_VERSION) returned an empty \
                                                     string");

        let mut iter = version.split(move |&mut: c: char| c == '.');
        let major = iter.next().unwrap();
        let minor = iter.next().expect("glGetString(GL_VERSION) did not return a correct version");

        GlVersion(
            major.parse().expect("failed to parse GL major version"),
            minor.parse().expect("failed to parse GL minor version"),
        )
    }
}
<commit_msg>Update to latest nightly<commit_after>use std::cmp::Ordering;
use std::ffi;
use gl;

/// Describes an OpenGL ctxt.version.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct GlVersion(pub u8, pub u8);

impl PartialOrd for GlVersion {
    fn partial_cmp(&self, other: &GlVersion) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for GlVersion {
    fn cmp(&self, other: &GlVersion) -> Ordering {
        match self.0.cmp(&other.0) {
            Ordering::Equal => self.1.cmp(&other.1),
            a => a
        }
    }
}

pub fn get_gl_version(gl: &gl::Gl) -> GlVersion {
    unsafe {
        let version = gl.GetString(gl::VERSION) as *const i8;
        let version = String::from_utf8(ffi::c_str_to_bytes(&version).to_vec()).unwrap();

        let version = version.words().next().expect("glGetString(GL_VERSION) returned an empty \
                                                     string");

        let mut iter = version.split(move |&mut: c: char| c == '.');
        let major = iter.next().unwrap();
        let minor = iter.next().expect("glGetString(GL_VERSION) did not return a correct version");

        GlVersion(
            major.parse().ok().expect("failed to parse GL major version"),
            minor.parse().ok().expect("failed to parse GL minor version"),
        )
    }
}
<|endoftext|>