<commit_before>#[allow(dead_code)]
mod util;

use crate::util::{
    event::{Event, Events},
    StatefulList,
};
use std::{error::Error, io};
use termion::{event::Key, input::MouseTerminal, raw::IntoRawMode, screen::AlternateScreen};
use tui::{
    backend::TermionBackend,
    layout::{Constraint, Corner, Direction, Layout},
    style::{Color, Modifier, Style},
    text::{Span, Spans},
    widgets::{Block, Borders, List, ListItem},
    Terminal,
};

struct App<'a> {
    items: StatefulList<(&'a str, usize)>,
    events: Vec<(&'a str, &'a str)>,
}

impl<'a> App<'a> {
    fn new() -> App<'a> {
        App {
            items: StatefulList::with_items(vec![
                ("Item0", 1),
                ("Item1", 2),
                ("Item2", 1),
                ("Item3", 3),
                ("Item4", 1),
                ("Item5", 4),
                ("Item6", 1),
                ("Item7", 3),
                ("Item8", 1),
                ("Item9", 6),
                ("Item10", 1),
                ("Item11", 3),
                ("Item12", 1),
                ("Item13", 2),
                ("Item14", 1),
                ("Item15", 1),
                ("Item16", 4),
                ("Item17", 1),
                ("Item18", 5),
                ("Item19", 4),
                ("Item20", 1),
                ("Item21", 2),
                ("Item22", 1),
                ("Item23", 3),
                ("Item24", 1),
            ]),
            events: vec![
                ("Event1", "INFO"),
                ("Event2", "INFO"),
                ("Event3", "CRITICAL"),
                ("Event4", "ERROR"),
                ("Event5", "INFO"),
                ("Event6", "INFO"),
                ("Event7", "WARNING"),
                ("Event8", "INFO"),
                ("Event9", "INFO"),
                ("Event10", "INFO"),
                ("Event11", "CRITICAL"),
                ("Event12", "INFO"),
                ("Event13", "INFO"),
                ("Event14", "INFO"),
                ("Event15", "INFO"),
                ("Event16", "INFO"),
                ("Event17", "ERROR"),
                ("Event18", "ERROR"),
                ("Event19", "INFO"),
                ("Event20", "INFO"),
                ("Event21", "WARNING"),
                ("Event22", "INFO"),
                ("Event23", "INFO"),
                ("Event24", "WARNING"),
                ("Event25", "INFO"),
                ("Event26", "INFO"),
            ],
        }
    }

    fn advance(&mut self) {
        let event = self.events.pop().unwrap();
        self.events.insert(0, event);
    }
}

fn main() -> Result<(), Box<dyn Error>> {
    // Terminal initialization
    let stdout = io::stdout().into_raw_mode()?;
    let stdout = MouseTerminal::from(stdout);
    let stdout = AlternateScreen::from(stdout);
    let backend = TermionBackend::new(stdout);
    let mut terminal = Terminal::new(backend)?;

    let events = Events::new();

    // App
    let mut app = App::new();

    loop {
        terminal.draw(|f| {
            let chunks = Layout::default()
                .direction(Direction::Horizontal)
                .constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
                .split(f.size());

            let items: Vec<ListItem> = app
                .items
                .items
                .iter()
                .map(|i| {
                    let mut lines = vec![Spans::from(i.0)];
                    for _ in 0..i.1 {
                        lines.push(Spans::from(Span::styled(
                            "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
                            Style::default().add_modifier(Modifier::ITALIC),
                        )));
                    }
                    ListItem::new(lines).style(Style::default().fg(Color::Black).bg(Color::White))
                })
                .collect();
            let items = List::new(items)
                .block(Block::default().borders(Borders::ALL).title("List"))
                .highlight_style(
                    Style::default()
                        .bg(Color::LightGreen)
                        .add_modifier(Modifier::BOLD),
                )
                .highlight_symbol(">> ");
            f.render_stateful_widget(items, chunks[0], &mut app.items.state);

            let events: Vec<ListItem> = app
                .events
                .iter()
                .map(|&(evt, level)| {
                    let s = match level {
                        "CRITICAL" => Style::default().fg(Color::Red),
                        "ERROR" => Style::default().fg(Color::Magenta),
                        "WARNING" => Style::default().fg(Color::Yellow),
                        "INFO" => Style::default().fg(Color::Blue),
                        _ => Style::default(),
                    };
                    let header = Spans::from(vec![
                        Span::styled(format!("{:<9}", level), s),
                        Span::raw(" "),
                        Span::styled(
                            "2020-01-01 10:00:00",
                            Style::default().add_modifier(Modifier::ITALIC),
                        ),
                    ]);
                    let log = Spans::from(vec![Span::raw(evt)]);
                    ListItem::new(vec![
                        Spans::from("-".repeat(chunks[1].width as usize)),
                        header,
                        Spans::from(""),
                        log,
                    ])
                })
                .collect();
            let events_list = List::new(events)
                .block(Block::default().borders(Borders::ALL).title("List"))
                .start_corner(Corner::BottomLeft);
            f.render_widget(events_list, chunks[1]);
        })?;

        match events.next()? {
            Event::Input(input) => match input {
                Key::Char('q') => {
                    break;
                }
                Key::Left => {
                    app.items.unselect();
                }
                Key::Down => {
                    app.items.next();
                }
                Key::Up => {
                    app.items.previous();
                }
                _ => {}
            },
            Event::Tick => {
                app.advance();
            }
        }
    }

    Ok(())
}
<commit_msg>doc(examples): Add comments to "list" example and fix list direction (#425)<commit_after>#[allow(dead_code)]
mod util;

use crate::util::{
    event::{Event, Events},
    StatefulList,
};
use std::{error::Error, io};
use termion::{event::Key, input::MouseTerminal, raw::IntoRawMode, screen::AlternateScreen};
use tui::{
    backend::TermionBackend,
    layout::{Constraint, Corner, Direction, Layout},
    style::{Color, Modifier, Style},
    text::{Span, Spans},
    widgets::{Block, Borders, List, ListItem},
    Terminal,
};

/// This struct holds the current state of the app. In particular, it has the `items` field which is a wrapper
/// around `ListState`. Keeping track of the items state let us render the associated widget with its state
/// and have access to features such as natural scrolling.
///
/// Check the event handling at the bottom to see how to change the state on incoming events.
/// Check the drawing logic for items on how to specify the highlighting style for selected items.
struct App<'a> {
    items: StatefulList<(&'a str, usize)>,
    events: Vec<(&'a str, &'a str)>,
}

impl<'a> App<'a> {
    fn new() -> App<'a> {
        App {
            items: StatefulList::with_items(vec![
                ("Item0", 1),
                ("Item1", 2),
                ("Item2", 1),
                ("Item3", 3),
                ("Item4", 1),
                ("Item5", 4),
                ("Item6", 1),
                ("Item7", 3),
                ("Item8", 1),
                ("Item9", 6),
                ("Item10", 1),
                ("Item11", 3),
                ("Item12", 1),
                ("Item13", 2),
                ("Item14", 1),
                ("Item15", 1),
                ("Item16", 4),
                ("Item17", 1),
                ("Item18", 5),
                ("Item19", 4),
                ("Item20", 1),
                ("Item21", 2),
                ("Item22", 1),
                ("Item23", 3),
                ("Item24", 1),
            ]),
            events: vec![
                ("Event1", "INFO"),
                ("Event2", "INFO"),
                ("Event3", "CRITICAL"),
                ("Event4", "ERROR"),
                ("Event5", "INFO"),
                ("Event6", "INFO"),
                ("Event7", "WARNING"),
                ("Event8", "INFO"),
                ("Event9", "INFO"),
                ("Event10", "INFO"),
                ("Event11", "CRITICAL"),
                ("Event12", "INFO"),
                ("Event13", "INFO"),
                ("Event14", "INFO"),
                ("Event15", "INFO"),
                ("Event16", "INFO"),
                ("Event17", "ERROR"),
                ("Event18", "ERROR"),
                ("Event19", "INFO"),
                ("Event20", "INFO"),
                ("Event21", "WARNING"),
                ("Event22", "INFO"),
                ("Event23", "INFO"),
                ("Event24", "WARNING"),
                ("Event25", "INFO"),
                ("Event26", "INFO"),
            ],
        }
    }

    /// Rotate through the event list.
    /// This only exists to simulate some kind of "progress"
    fn advance(&mut self) {
        let event = self.events.remove(0);
        self.events.push(event);
    }
}

fn main() -> Result<(), Box<dyn Error>> {
    // Terminal initialization
    let stdout = io::stdout().into_raw_mode()?;
    let stdout = MouseTerminal::from(stdout);
    let stdout = AlternateScreen::from(stdout);
    let backend = TermionBackend::new(stdout);
    let mut terminal = Terminal::new(backend)?;

    let events = Events::new();

    // Create a new app with some exapmle state
    let mut app = App::new();

    loop {
        terminal.draw(|f| {
            // Create two chunks with equal horizontal screen space
            let chunks = Layout::default()
                .direction(Direction::Horizontal)
                .constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
                .split(f.size());

            // Iterate through all elements in the `items` app and append some debug text to it.
            let items: Vec<ListItem> = app
                .items
                .items
                .iter()
                .map(|i| {
                    let mut lines = vec![Spans::from(i.0)];
                    for _ in 0..i.1 {
                        lines.push(Spans::from(Span::styled(
                            "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
                            Style::default().add_modifier(Modifier::ITALIC),
                        )));
                    }
                    ListItem::new(lines).style(Style::default().fg(Color::Black).bg(Color::White))
                })
                .collect();

            // Create a List from all list items and highlight the currently selected one
            let items = List::new(items)
                .block(Block::default().borders(Borders::ALL).title("List"))
                .highlight_style(
                    Style::default()
                        .bg(Color::LightGreen)
                        .add_modifier(Modifier::BOLD),
                )
                .highlight_symbol(">> ");

            // We can now render the item list
            f.render_stateful_widget(items, chunks[0], &mut app.items.state);

            // Let's do the same for the events.
            // The event list doesn't have any state and only displays the current state of the list.
            let events: Vec<ListItem> = app
                .events
                .iter()
                .rev()
                .map(|&(event, level)| {
                    // Colorcode the level depending on its type
                    let s = match level {
                        "CRITICAL" => Style::default().fg(Color::Red),
                        "ERROR" => Style::default().fg(Color::Magenta),
                        "WARNING" => Style::default().fg(Color::Yellow),
                        "INFO" => Style::default().fg(Color::Blue),
                        _ => Style::default(),
                    };
                    // Add a example datetime and apply proper spacing between them
                    let header = Spans::from(vec![
                        Span::styled(format!("{:<9}", level), s),
                        Span::raw(" "),
                        Span::styled(
                            "2020-01-01 10:00:00",
                            Style::default().add_modifier(Modifier::ITALIC),
                        ),
                    ]);
                    // The event gets it's own line
                    let log = Spans::from(vec![Span::raw(event)]);

                    // Here several things happen:
                    // 1. Add a `---` spacing line above the final list entry
                    // 2. Add the Level + datetime
                    // 3. Add a spacer line
                    // 4. Add the actual event
                    ListItem::new(vec![
                        Spans::from("-".repeat(chunks[1].width as usize)),
                        header,
                        Spans::from(""),
                        log,
                    ])
                })
                .collect();
            let events_list = List::new(events)
                .block(Block::default().borders(Borders::ALL).title("List"))
                .start_corner(Corner::BottomLeft);
            f.render_widget(events_list, chunks[1]);
        })?;

        // This is a simple example on how to handle events
        // 1. This breaks the loop and exits the program on `q` button press.
        // 2. The `up`/`down` keys change the currently selected item in the App's `items` list.
        // 3. `left` unselects the current item.
        match events.next()? {
            Event::Input(input) => match input {
                Key::Char('q') => {
                    break;
                }
                Key::Left => {
                    app.items.unselect();
                }
                Key::Down => {
                    app.items.next();
                }
                Key::Up => {
                    app.items.previous();
                }
                _ => {}
            },
            Event::Tick => {
                app.advance();
            }
        }
    }

    Ok(())
}
<|endoftext|>


<commit_before>#[macro_export]
macro_rules! storage_value {
    ($name:ident {
        const SIZE = $body:expr;

        $($field_name:ident : $field_type:ty [$from:expr => $to:expr])*
    }) => (
        #[derive(Clone, PartialEq)]
        pub struct $name {
            raw: Vec<u8>
        }

        impl $crate::storage::StorageValue for $name {
            fn serialize(self) -> Vec<u8> {
                self.raw
            }

            fn deserialize(v: Vec<u8>) -> Self {
                $name {
                    raw: v
                }
            }

            fn hash(&self) -> Hash {
                hash(self.raw.as_ref())
            }
        }

        impl $name {
            pub fn new($($field_name: $field_type,)*) -> $name {
                use $crate::messages::{Field};
                let mut buf = vec![0; $body];
                $($field_name.write(&mut buf, $from, $to);)*
                $name { raw: buf }
            }

            pub fn from_raw(raw: Vec<u8>) -> $name {
                debug_assert_eq!(raw.len(), $body);
                $ name { raw: raw }
            }

            $(pub fn $field_name(&self) -> $field_type {
                use $crate::messages::Field;
                Field::read(&self.raw, $from, $to)
            })*
        }

        impl ::std::fmt::Debug for $name {
            fn fmt(&self, fmt: &mut ::std::fmt::Formatter)
                -> Result<(), ::std::fmt::Error> {
                fmt.debug_struct(stringify!($name))
                 $(.field(stringify!($field_name), &self.$field_name()))*
                   .finish()
            }
        }
    )
}<commit_msg>Compile fix for sandbox<commit_after>#[macro_export]
macro_rules! storage_value {
    ($name:ident {
        const SIZE = $body:expr;

        $($field_name:ident : $field_type:ty [$from:expr => $to:expr])*
    }) => (
        #[derive(Clone, PartialEq)]
        pub struct $name {
            raw: Vec<u8>
        }

        impl $crate::storage::StorageValue for $name {
            fn serialize(self) -> Vec<u8> {
                self.raw
            }

            fn deserialize(v: Vec<u8>) -> Self {
                $name {
                    raw: v
                }
            }

            fn hash(&self) -> Hash {
                $name::hash(self)
            }
        }

        impl $name {
            pub fn new($($field_name: $field_type,)*) -> $name {
                use $crate::messages::{Field};
                let mut buf = vec![0; $body];
                $($field_name.write(&mut buf, $from, $to);)*
                $name { raw: buf }
            }

            pub fn from_raw(raw: Vec<u8>) -> $name {
                debug_assert_eq!(raw.len(), $body);
                $ name { raw: raw }
            }

            pub fn hash(&self) -> Hash {
                hash(self.raw.as_ref())
            }

            $(pub fn $field_name(&self) -> $field_type {
                use $crate::messages::Field;
                Field::read(&self.raw, $from, $to)
            })*
        }

        impl ::std::fmt::Debug for $name {
            fn fmt(&self, fmt: &mut ::std::fmt::Formatter)
                -> Result<(), ::std::fmt::Error> {
                fmt.debug_struct(stringify!($name))
                 $(.field(stringify!($field_name), &self.$field_name()))*
                   .finish()
            }
        }
    )
}<|endoftext|>


<commit_before>#![feature(non_ascii_idents)]
// use variable names from paper, namely registers M
#![allow(non_snake_case)]

extern crate "basic-hll" as hll;

#[test]
fn adding_two_empty_hlls_results_in_an_empty_hll () {
    let first  = hll::HLL::ctor(0.26);
    let second = hll::HLL::ctor(0.26);
    let third  = first + second;

    assert_eq!(third.registers().iter().filter(|&r| *r != 0).count(), 0);
}

#[test]
fn adding_together_two_hlls_takes_the_max_in_each_register () {
    let mut first  = hll::HLL::ctor(0.26);
    let mut second = hll::HLL::ctor(0.26);

    first.insert("foo");  // sets register 14 to 7
    second.insert("bar"); // sets register 6 to 5

    let third = first + second;
    assert_eq!(third.registers()[14], 7);
    assert_eq!(third.registers()[6], 5);
}
<commit_msg>Add tests for counting after merging<commit_after>#![feature(non_ascii_idents)]
// use variable names from paper, namely registers M
#![allow(non_snake_case)]

extern crate "basic-hll" as hll;

use std::num::Float;

#[test]
fn adding_two_empty_hlls_results_in_an_empty_hll () {
    let first  = hll::HLL::ctor(0.26);
    let second = hll::HLL::ctor(0.26);
    let third  = first + second;

    assert_eq!(third.registers().iter().filter(|&r| *r != 0).count(), 0);
}

#[test]
fn adding_together_two_hlls_takes_the_max_in_each_register () {
    let mut first  = hll::HLL::ctor(0.26);
    let mut second = hll::HLL::ctor(0.26);

    first.insert("foo");  // sets register 14 to 7
    second.insert("bar"); // sets register 6 to 5

    let third = first + second;
    assert_eq!(third.registers()[14], 7);
    assert_eq!(third.registers()[6], 5);
}

#[test]
fn adding_together_two_hlls_preserves_the_sum_of_their_counts () {
    let mut first  = hll::HLL::ctor(0.0040625);
    let mut second = hll::HLL::ctor(0.0040625);

    first.insert("a"); first.insert("b"); first.insert("c");
    second.insert("d"); second.insert("e"); second.insert("f");

    assert_eq!(first.count().round(), 3.0);
    assert_eq!(second.count().round(), 3.0);

    let third = first + second;
    assert_eq!(third.count().round(), 6.0);
}

#[test]
fn add_together_two_hlls_doesnt_double_count_duplicate_elements () {
    let mut first  = hll::HLL::ctor(0.0040625);
    let mut second = hll::HLL::ctor(0.0040625);

    first.insert("a"); first.insert("b"); first.insert("c");
    second.insert("a"); second.insert("d"); second.insert("e");

    assert_eq!(first.count().round(), 3.0);
    assert_eq!(second.count().round(), 3.0);

    let third = first + second;
    assert_eq!(third.count().round(), 5.0);
}
<|endoftext|>


<commit_before>//use std::io;
use std::str::Chars;
use std::iter::Peekable;
use std::iter::Iterator;

trait UniOp {
    fn calc(&self, a: f32) -> f32;
}

trait BinOp {
    fn calc(&self, a: f32, b: f32) -> f32;
}

struct AddOp;
impl BinOp for AddOp { fn calc(&self, a: f32, b: f32) -> f32 { a + b } }

struct SubOp;
impl BinOp for SubOp { fn calc(&self, a: f32, b: f32) -> f32 { a - b } }

struct MulOp;
impl BinOp for MulOp { fn calc(&self, a: f32, b: f32) -> f32 { a * b } }

struct DivOp;
impl BinOp for DivOp { fn calc(&self, a: f32, b: f32) -> f32 { a / b } }

struct PosOp;
impl UniOp for PosOp { fn calc(&self, a: f32) -> f32 { a } }

struct NegOp;
impl UniOp for NegOp { fn calc(&self, a: f32) -> f32 { -a } }

enum OpTypes {
    ForOne(Box<UniOp>),
    ForTwo(Box<BinOp>),
}

struct OpRec {
    op: OpTypes,
    pre: i32,
}

impl OpRec {
    fn new_uni<T>(op: T, pre: i32) -> OpRec where T: UniOp + 'static{
       OpRec { op: OpTypes::ForOne(Box::new(op)), pre: pre }
    }

    fn new_bin<T>(op: T, pre: i32) -> OpRec where T: BinOp + 'static{
       OpRec { op: OpTypes::ForTwo(Box::new(op)), pre: pre }
    }
}

fn get_num(chars: &mut Peekable<Chars>) -> f32 {
    let mut has_dot = false;
    let mut tens: f32 = 1.0;
    let mut num: f32 = 0.0;
    loop {
        match chars.peek() {
            Some(&c) => {
                match c {
                    '0' ... '9' => {
                        let dig = (c as i32) - ('0' as i32);
                        if has_dot {
                            tens = tens * 10.0;
                            num = num + dig as f32 / tens;
                        }
                        else {
                            num = num * 10.0 + (dig as f32);
                        }
                    }
                    '.' if !has_dot => {
                        has_dot = true;
                    }
                    _ => return num
               }
           }
           _ => return num
        }
        chars.next();
    }
}

fn get_uni_op(c: char, cur_pre: i32) -> Option<OpRec> {
    match c {
        '+' => Some(OpRec::new_uni(PosOp, cur_pre + 10)),
        '-' => Some(OpRec::new_uni(NegOp, cur_pre + 10)),
        _ => None
    }
}

fn get_bin_op(c: char, cur_pre: i32) -> Option<OpRec> {
    match c {
        '+' => Some(OpRec::new_bin(AddOp, cur_pre + 1)),
        '-' => Some(OpRec::new_bin(SubOp, cur_pre + 1)),
        '*' => Some(OpRec::new_bin(MulOp, cur_pre + 2)),
        '/' => Some(OpRec::new_bin(DivOp, cur_pre + 2)),
        _ => None
    }
}

struct ExprParser {
    op_stack: Vec<OpRec>,
    num_stack: Vec<f32>,
}

impl ExprParser {
    fn new() -> ExprParser {
        ExprParser {
            op_stack: Vec::new(),
            num_stack: Vec::new(),
        }
    }

    fn pop_op(&mut self, pre: i32, left_to_right: bool) {
        while self.op_stack.len() > 0 {
            {
                let op_rec = self.op_stack.last().unwrap();
                let should_return =
                    if left_to_right { op_rec.pre < pre }
                    else { op_rec.pre <= pre };
                if should_return {
                    return;
                }
            }
            let op = self.op_stack.pop().unwrap().op;
            println!("{:?}", self.num_stack);

            match op {
                OpTypes::ForOne(ref op) => {
                    let a = self.num_stack.pop().unwrap();
                    self.num_stack.push(op.calc(a));
                }
                OpTypes::ForTwo(ref op) => {
                    let b = self.num_stack.pop().unwrap();
                    let a = self.num_stack.pop().unwrap();
                    self.num_stack.push(op.calc(a, b));
                }
            }
        }
    }

    fn parse(&mut self, expr: &str) {
        let mut cur_pre = 0;
        let mut chars = expr.chars().peekable();
        let mut is_expect_bin_op = false;

        loop {
            let c;
            {
                let option = chars.peek();
                if option.is_none() {
                    break;
                }
                c = *option.unwrap();
            }

            if c.is_whitespace() {
                chars.next();
                continue;
            }

            if is_expect_bin_op {
                if c == ')' {
                    println!("right");
                    cur_pre -= 100;
                }
                else {
                    match get_bin_op(c, cur_pre) {
                        Some(op) => {
                            println!("BIN OP: {}, {}", c, op.pre);
                            self.pop_op(op.pre, true);
                            self.op_stack.push(op);
                        }
                        None => {
                            println!("expect binary op but got: {}", c);
                        }
                    }
                    is_expect_bin_op = false;
                }
                chars.next();
            }
            else {
                match c {
                    '0' ... '9' | '.' => {
                        let num = get_num(&mut chars);
                        self.num_stack.push(num);
                        println!("NUM: {}", num);
                        is_expect_bin_op = true;
                    }
                    '(' => {
                        println!("left");
                        cur_pre += 100;
                        chars.next();
                    }
                    _ => {
                        match get_uni_op(c, cur_pre) {
                            Some(op) => {
                                println!("UNI OP: {}, {}", c, op.pre);
                                self.pop_op(op.pre, false);
                                self.op_stack.push(op);
                                chars.next();
                                continue;
                            }
                            None => { println!("unexpect {}", c); }
                        }
                    }
                }
            }
        }
        self.pop_op(0, true);
        println!("{:?}", self.num_stack);
    }
}

fn main() {
    println!("Please input your guess.");

//    let mut expr = String::new();
//    io::stdin().read_line(&mut expr)
//        .ok()
//        .expect("failed to read line");

    let mut parser = ExprParser::new();
    parser.parse("1+2*--3.67 * (5.34 - 2)/+.5");
}<commit_msg>improve expire<commit_after>//use std::io;
use std::str::Chars;
use std::iter::Peekable;
use std::iter::Iterator;

trait UnaryOp {
    fn calc(&self, a: f64) -> f64;
}

trait BinOp {
    fn calc(&self, a: f64, b: f64) -> f64;
}

struct AddOp;
impl BinOp for AddOp { fn calc(&self, a: f64, b: f64) -> f64 { a + b } }

struct SubOp;
impl BinOp for SubOp { fn calc(&self, a: f64, b: f64) -> f64 { a - b } }

struct MulOp;
impl BinOp for MulOp { fn calc(&self, a: f64, b: f64) -> f64 { a * b } }

struct DivOp;
impl BinOp for DivOp { fn calc(&self, a: f64, b: f64) -> f64 { a / b } }

struct PosOp;
impl UnaryOp for PosOp { fn calc(&self, a: f64) -> f64 { a } }

struct NegOp;
impl UnaryOp for NegOp { fn calc(&self, a: f64) -> f64 { -a } }

enum OpTypes {
    ForUnary(Box<UnaryOp>),
    ForBin(Box<BinOp>),
}

struct OpRec {
    op: OpTypes,
    pre: i32,
}

impl OpRec {
    fn new_unary<T>(op: T, pre: i32) -> OpRec where T: UnaryOp + 'static{
       OpRec { op: OpTypes::ForUnary(Box::new(op)), pre: pre }
    }

    fn new_bin<T>(op: T, pre: i32) -> OpRec where T: BinOp + 'static{
       OpRec { op: OpTypes::ForBin(Box::new(op)), pre: pre }
    }
}

fn get_num(chars: &mut Peekable<Chars>) -> f64 {
    let mut has_dot = false;
    let mut tens: f64 = 1.0;
    let mut num: f64 = 0.0;
    let zero_char = '0' as i32;
    loop {
        match chars.peek() {
            Some(&c) => {
                match c {
                    '0' ... '9' => {
                        let dig = ((c as i32) - zero_char) as f64;
                        if has_dot {
                            tens = tens * 10.0;
                            num = num + dig / tens;
                        }
                        else {
                            num = num * 10.0 + dig;
                        }
                    }
                    '.' if !has_dot => {
                        has_dot = true;
                    }
                    _ => return num
               }
           }
           _ => return num
        }
        chars.next();
    }
}

fn get_unary_op(c: char, cur_pre: i32) -> Option<OpRec> {
    match c {
        '+' => Some(OpRec::new_unary(PosOp, cur_pre + 10)),
        '-' => Some(OpRec::new_unary(NegOp, cur_pre + 10)),
        _ => None
    }
}

fn get_bin_op(c: char, cur_pre: i32) -> Option<OpRec> {
    match c {
        '+' => Some(OpRec::new_bin(AddOp, cur_pre + 1)),
        '-' => Some(OpRec::new_bin(SubOp, cur_pre + 1)),
        '*' => Some(OpRec::new_bin(MulOp, cur_pre + 2)),
        '/' => Some(OpRec::new_bin(DivOp, cur_pre + 2)),
        _ => None
    }
}

struct ExprParser {
    op_stack: Vec<OpRec>,
    num_stack: Vec<f64>,
}

impl ExprParser {
    fn new() -> ExprParser {
        ExprParser {
            op_stack: Vec::new(),
            num_stack: Vec::new(),
        }
    }

    fn calc_top_op(&mut self, pre: i32) -> bool {
        let op_rec = self.op_stack.last().unwrap();
        let op_pre = op_rec.pre;
        match op_rec.op {
            OpTypes::ForUnary(ref op) => {
                if op_pre <= pre {
                    return false;
                }
                let a = self.num_stack.pop().unwrap();
                self.num_stack.push(op.calc(a));
            }
            OpTypes::ForBin(ref op) => {
                if op_pre < pre {
                    return false;
                }
                let b = self.num_stack.pop().unwrap();
                let a = self.num_stack.pop().unwrap();
                self.num_stack.push(op.calc(a, b));
            }
        }
        return true;
    }

    fn pop_op(&mut self, pre: i32) {
        while self.op_stack.len() > 0 {
            println!("{:?}", self.num_stack);
            if !self.calc_top_op(pre) {
                return;
            }
            self.op_stack.pop();
        }
    }

    fn parse(&mut self, expr: &str) -> f64{
        let mut cur_pre = 0;
        let mut chars = expr.chars().peekable();
        let mut is_expect_bin_op = false;

        loop {
            let c;
            {
                let option = chars.peek();
                if option.is_none() {
                    break;
                }
                c = *option.unwrap();
            }

            if c.is_whitespace() {
                chars.next();
                continue;
            }

            if is_expect_bin_op {
                if c == ')' {
                    println!("right");
                    cur_pre -= 100;
                }
                else {
                    match get_bin_op(c, cur_pre) {
                        Some(op) => {
                            println!("BIN OP: {}, {}", c, op.pre);
                            self.pop_op(op.pre);
                            self.op_stack.push(op);
                        }
                        None => {
                            println!("expect binary op but got: {}", c);
                        }
                    }
                    is_expect_bin_op = false;
                }
                chars.next();
            }
            else {
                match c {
                    '0' ... '9' | '.' => {
                        let num = get_num(&mut chars);
                        self.num_stack.push(num);
                        println!("NUM: {}", num);
                        is_expect_bin_op = true;
                    }
                    '(' => {
                        println!("left");
                        cur_pre += 100;
                        chars.next();
                    }
                    _ => {
                        match get_unary_op(c, cur_pre) {
                            Some(op) => {
                                println!("UNI OP: {}, {}", c, op.pre);
                                self.pop_op(op.pre);
                                self.op_stack.push(op);
                                chars.next();
                                continue;
                            }
                            None => { println!("unexpect {}", c); }
                        }
                    }
                }
            }
        }
        self.pop_op(0);
        println!("{:?}", self.num_stack);
        return self.num_stack[0];
    }
}

fn test(expr: &str) {
    let mut parser = ExprParser::new();
    println!("{}={}", expr, parser.parse(expr));
}

fn main() {
    println!("Please input your guess.");

//    let mut expr = String::new();
//    io::stdin().read_line(&mut expr)
//        .ok()
//        .expect("failed to read line");
    test("23.4 + 32 * 5");
    test("1++++----212");
    test("2*(2-(9.3-2.3))");
    test("1+2*--3.67 * (5.34 - 2)/+.5");
}<|endoftext|>


<commit_before>extern crate rsedis;

use rsedis::database::Database;
use rsedis::database::Value;

#[test]
fn set_get() {
    let mut database = Database::new();
    let key = vec![1u8];
    let value = vec![1u8, 2, 3, 4];
    let expected = Vec::clone(&value);
    database.set(&key, value);
    match database.get(&key) {
        Some(val) => {
            match val {
                &Value::Data(ref bytes) => assert_eq!(*bytes, expected),
            }
        }
        _ => assert!(false),
    }
}

#[test]
fn get_empty() {
    let database = Database::new();
    let key = vec![1u8];
    match database.get(&key) {
        None => {},
        _ => assert!(false),
    }
}
<commit_msg>Add overwrite test<commit_after>extern crate rsedis;

use rsedis::database::Database;
use rsedis::database::Value;

#[test]
fn set_get() {
    let mut database = Database::new();
    let key = vec![1u8];
    let value = vec![1u8, 2, 3, 4];
    let expected = Vec::clone(&value);
    database.set(&key, value);
    match database.get(&key) {
        Some(val) => {
            match val {
                &Value::Data(ref bytes) => assert_eq!(*bytes, expected),
            }
        }
        _ => assert!(false),
    }
}

#[test]
fn get_empty() {
    let database = Database::new();
    let key = vec![1u8];
    match database.get(&key) {
        None => {},
        _ => assert!(false),
    }
}

#[test]
fn set_set_get() {
    let mut database = Database::new();
    let key = vec![1u8];
    database.set(&key, vec![0u8, 0, 0]);
    let value = vec![1u8, 2, 3, 4];
    let expected = Vec::clone(&value);
    database.set(&key, value);
    match database.get(&key) {
        Some(val) => {
            match val {
                &Value::Data(ref bytes) => assert_eq!(*bytes, expected),
            }
        }
        _ => assert!(false),
    }
}
<|endoftext|>


<commit_before><commit_msg>Added test for deserializing Metadata struct<commit_after><|endoftext|>


<commit_before>//! Tests for internal code checks.
use std::fs;

#[test]
fn check_forbidden_code() {
    // Do not use certain macros, functions, etc.
    if !cargo::util::is_ci() {
        // Only check these on CI, otherwise it could be annoying.
        return;
    }
    let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("src");
    for entry in walkdir::WalkDir::new(path)
        .into_iter()
        .filter_map(|e| e.ok())
    {
        let path = entry.path();
        if !entry
            .file_name()
            .to_str()
            .map(|s| s.ends_with(".rs"))
            .unwrap_or(false)
        {
            continue;
        }
        let c = fs::read_to_string(path).unwrap();
        for (line_index, line) in c.lines().enumerate() {
            if line_has_print(line) {
                if entry.file_name().to_str().unwrap() == "cargo_new.rs" && line.contains("Hello") {
                    // An exception.
                    continue;
                }
                panic!(
                    "found print macro in {}:{}\n\n{}\n\n\
                    print! macros should not be used in Cargo because they can panic.\n\
                    Use one of the drop_print macros instead.\n\
                    ",
                    path.display(),
                    line_index,
                    line
                );
            }
            if line_has_macro(line, "dbg") {
                panic!(
                    "found dbg! macro in {}:{}\n\n{}\n\n\
                    dbg! should not be used outside of debugging.",
                    path.display(),
                    line_index,
                    line
                );
            }
        }
    }
}

fn line_has_print(line: &str) -> bool {
    line_has_macro(line, "print")
        || line_has_macro(line, "eprint")
        || line_has_macro(line, "println")
        || line_has_macro(line, "eprintln")
}

#[test]
fn line_has_print_works() {
    assert!(line_has_print("print!"));
    assert!(line_has_print("println!"));
    assert!(line_has_print("eprint!"));
    assert!(line_has_print("eprintln!"));
    assert!(line_has_print("(print!(\"hi!\"))"));
    assert!(!line_has_print("print"));
    assert!(!line_has_print("i like to print things"));
    assert!(!line_has_print("drop_print!"));
    assert!(!line_has_print("drop_println!"));
    assert!(!line_has_print("drop_eprint!"));
    assert!(!line_has_print("drop_eprintln!"));
}

fn line_has_macro(line: &str, mac: &str) -> bool {
    for (i, _) in line.match_indices(mac) {
        if line.get(i + mac.len()..i + mac.len() + 1) != Some("!") {
            continue;
        }
        if i == 0 {
            return true;
        }
        // Check for identifier boundary start.
        let prev1 = line.get(i - 1..i).unwrap().chars().next().unwrap();
        if prev1.is_alphanumeric() || prev1 == '_' {
            continue;
        }
        return true;
    }
    false
}
<commit_msg>Get past internal test on CI<commit_after>//! Tests for internal code checks.
use std::fs;

#[test]
fn check_forbidden_code() {
    // Do not use certain macros, functions, etc.
    if !cargo::util::is_ci() {
        // Only check these on CI, otherwise it could be annoying.
        return;
    }
    let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("src");
    for entry in walkdir::WalkDir::new(path)
        .into_iter()
        .filter_map(|e| e.ok())
    {
        let path = entry.path();
        if !entry
            .file_name()
            .to_str()
            .map(|s| s.ends_with(".rs"))
            .unwrap_or(false)
        {
            continue;
        }
        let c = fs::read_to_string(path).unwrap();
        for (line_index, line) in c.lines().enumerate() {
            if line.trim().starts_with("//") {
                continue;
            }
            if line_has_print(line) {
                if entry.file_name().to_str().unwrap() == "cargo_new.rs" && line.contains("Hello") {
                    // An exception.
                    continue;
                }
                panic!(
                    "found print macro in {}:{}\n\n{}\n\n\
                    print! macros should not be used in Cargo because they can panic.\n\
                    Use one of the drop_print macros instead.\n\
                    ",
                    path.display(),
                    line_index,
                    line
                );
            }
            if line_has_macro(line, "dbg") {
                panic!(
                    "found dbg! macro in {}:{}\n\n{}\n\n\
                    dbg! should not be used outside of debugging.",
                    path.display(),
                    line_index,
                    line
                );
            }
        }
    }
}

fn line_has_print(line: &str) -> bool {
    line_has_macro(line, "print")
        || line_has_macro(line, "eprint")
        || line_has_macro(line, "println")
        || line_has_macro(line, "eprintln")
}

#[test]
fn line_has_print_works() {
    assert!(line_has_print("print!"));
    assert!(line_has_print("println!"));
    assert!(line_has_print("eprint!"));
    assert!(line_has_print("eprintln!"));
    assert!(line_has_print("(print!(\"hi!\"))"));
    assert!(!line_has_print("print"));
    assert!(!line_has_print("i like to print things"));
    assert!(!line_has_print("drop_print!"));
    assert!(!line_has_print("drop_println!"));
    assert!(!line_has_print("drop_eprint!"));
    assert!(!line_has_print("drop_eprintln!"));
}

fn line_has_macro(line: &str, mac: &str) -> bool {
    for (i, _) in line.match_indices(mac) {
        if line.get(i + mac.len()..i + mac.len() + 1) != Some("!") {
            continue;
        }
        if i == 0 {
            return true;
        }
        // Check for identifier boundary start.
        let prev1 = line.get(i - 1..i).unwrap().chars().next().unwrap();
        if prev1.is_alphanumeric() || prev1 == '_' {
            continue;
        }
        return true;
    }
    false
}
<|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::{Entry, EvalType, TTable};
use model::{Board, Move, Outcome};

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

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 let Some(_) = result {
            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");
            }

            use std::sync::TryLockError::*;
            let mut ttable = match ttable_mutex.try_lock() {
                Ok(table) => table,
                Err(Poisoned(_)) => panic!("Transposition table mutex is poisoned"),
                Err(WouldBlock) => {
                    panic!("Couldn't lock transposition table, is another AI thread still running?")
                }
            };

            // 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.len() == 0 {
                panic!("AI has no moves");
            }

            for depth in 0..depth {
                if stop_signal_clone.load(Ordering::Relaxed) {
                    return;
                }
                let max_score = moves[0].1;
                // TODO: use the scores here are a kind of aspiration window?
                moves = moves
                    .into_iter()
                    .map(|(mv, _)| {
                        let mut new_board = board;
                        new_board.apply_move(&mv);

                        let score = -alphabeta_negamax(
                            &new_board,
                            &mut board_list,
                            NEG_INFINITY,
                            -max_score,
                            depth,
                            &mut ttable,
                        );
                        (mv, score)
                    })
                    .collect();
                moves.sort_by(|&(_, a), &(_, b)| b.cmp(&a));
            }
            move_sender
                .send(moves[0].0)
                .expect("AI failed to send Move");
        });

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

fn alphabeta_negamax(
    board: &Board,
    // This list does not include the current board
    mut board_list: &mut Vec<Board>,
    mut alpha: i16,
    mut beta: i16,
    depth: u8,
    ttable: &mut TTable,
) -> i16 {
    let set_ttable = |ttable: &mut TTable, eval_type, score| {
        let zobrist = board.zobrist();
        ttable.set(
            zobrist,
            Entry {
                eval_type,
                depth,
                score,
                zobrist,
            },
        );
    };

    match board.outcome() {
        Outcome::Draw => {
            // This only works because the draw Outcome does not consider draw by repetition
            set_ttable(ttable, EvalType::Exact, DRAW);
            return DRAW;
        }
        Outcome::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, EvalType::Exact, score);
            return score;
        }
        Outcome::InProgress => {}
    }

    if let Some(entry) = ttable.get(board.zobrist()) {
        if board_list.len() >= 8 && board_list.iter().filter(|&&b| b == *board).count() >= 2 {
            return DRAW;
        }

        if entry.zobrist == board.zobrist() && entry.depth >= depth {
            match entry.eval_type {
                EvalType::Exact => {
                    return entry.score;
                }
                EvalType::Beta => {
                    if entry.score >= beta {
                        return entry.score;
                    }
                    beta = entry.score;
                }
            }
        }
    }

    if depth == 0 {
        quiescence_search(board, alpha, beta)
    } else {
        let mut best_score = NEG_INFINITY;

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

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

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

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

// TODO: use ttable here?
fn quiescence_search(board: &Board, mut alpha: i16, beta: i16) -> i16 {
    let stand_pat = evaluate(board);
    if stand_pat >= beta {
        return beta;
    } else if alpha < stand_pat {
        alpha = stand_pat;
    }

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

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

        if score >= beta {
            return beta;
        } else if score > alpha {
            alpha = score;
        }
    }
    return 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>Fix iterative deepening bug<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::{Entry, EvalType, TTable};
use model::{Board, Move, Outcome};

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

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 let Some(_) = result {
            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");
            }

            use std::sync::TryLockError::*;
            let mut ttable = match ttable_mutex.try_lock() {
                Ok(table) => table,
                Err(Poisoned(_)) => panic!("Transposition table mutex is poisoned"),
                Err(WouldBlock) => {
                    panic!("Couldn't lock transposition table, is another AI thread still running?")
                }
            };

            // 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.len() == 0 {
                panic!("AI has no moves");
            }

            for depth in 0..depth {
                if stop_signal_clone.load(Ordering::Relaxed) {
                    return;
                }
                let mut max_score = NEG_INFINITY;
                // TODO: use the scores here are a kind of aspiration window?
                moves = moves
                    .into_iter()
                    .map(|(mv, _)| {
                        let mut new_board = board;
                        new_board.apply_move(&mv);

                        let score = -alphabeta_negamax(
                            &new_board,
                            &mut board_list,
                            NEG_INFINITY,
                            -max_score,
                            depth,
                            &mut ttable,
                        );

                        if score > max_score {
                            max_score = score;
                        }
                        (mv, score)
                    })
                    .collect();
                moves.sort_by(|&(_, a), &(_, b)| b.cmp(&a));
            }
            move_sender
                .send(moves[0].0)
                .expect("AI failed to send Move");
        });

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

fn alphabeta_negamax(
    board: &Board,
    // This list does not include the current board
    mut board_list: &mut Vec<Board>,
    mut alpha: i16,
    mut beta: i16,
    depth: u8,
    ttable: &mut TTable,
) -> i16 {
    let set_ttable = |ttable: &mut TTable, eval_type, score| {
        let zobrist = board.zobrist();
        ttable.set(
            zobrist,
            Entry {
                eval_type,
                depth,
                score,
                zobrist,
            },
        );
    };

    match board.outcome() {
        Outcome::Draw => {
            // This only works because the draw Outcome does not consider draw by repetition
            set_ttable(ttable, EvalType::Exact, DRAW);
            return DRAW;
        }
        Outcome::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, EvalType::Exact, score);
            return score;
        }
        Outcome::InProgress => {}
    }

    if let Some(entry) = ttable.get(board.zobrist()) {
        if board_list.len() >= 8 && board_list.iter().filter(|&&b| b == *board).count() >= 2 {
            return DRAW;
        }

        if entry.zobrist == board.zobrist() && entry.depth >= depth {
            match entry.eval_type {
                EvalType::Exact => {
                    return entry.score;
                }
                EvalType::Beta => {
                    if entry.score >= beta {
                        return entry.score;
                    }
                    beta = entry.score;
                }
            }
        }
    }

    if depth == 0 {
        quiescence_search(board, alpha, beta)
    } else {
        let mut best_score = NEG_INFINITY;

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

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

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

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

// TODO: use ttable here?
fn quiescence_search(board: &Board, mut alpha: i16, beta: i16) -> i16 {
    let stand_pat = evaluate(board);
    if stand_pat >= beta {
        return beta;
    } else if alpha < stand_pat {
        alpha = stand_pat;
    }

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

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

        if score >= beta {
            return beta;
        } else if score > alpha {
            alpha = score;
        }
    }
    return 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>use std::str::Chars;
use std::io::Reader;
use span::Span;

#[deriving(PartialEq, Show)]
pub enum Token {
    DASH,
    PLUS,
    MINUS,
    TIMES,
    AT,
    CARET,
    LPAREN,
    RPAREN,
    Noop,
    Done
}

/// The lexer is the system that has any notion of an input stream. The
/// stream can come from many sources, thus, we use the Reader trait
/// that is implemented for strings, files, tcp, etc...
///
/// The lexer should also not consume the whole stream at once, as
/// that will be a bottleneck for larger input sources.
pub struct Lexer<'a> {
    source: &'a str,
    iter: Chars<'a>,
    token: Token,
    span: Span
}

impl<'a> Lexer<'a> {
    pub fn new(source: &'a str) -> Lexer<'a> {
        Lexer {
            source: source,
            iter: source.chars(),
            token: Noop,
            span: Span::new(0, 0)
        }
    }

    /// Parse and fetch the next token in the text stream.
    /// This doesn't return the token, it stores it
    /// in the lexer.
    pub fn bump(&mut self) {
        // Fetch the next char.
        let c = match self.iter.next() {
            Some(c) => c,
            None => {
                self.token = Done;
                return;
            }
        };

        match c {
            '(' => { self.token = LPAREN },
            ')' => { self.token = RPAREN },
            '^' => { self.token = CARET },
            '-' => { self.token = DASH },
            ch @ 'A'..'Z' | ch @ 'a'..'z' => {
                println!("{}", ch);
            },
            _ => {}
        }
    }

    /// Expect a given token to come next, otherwise
    /// fail. Failing will result in a formatted
    /// error message that uses the Span and lets
    /// the user know what went wrong.
    pub fn expect(&mut self, tok: &Token) {
        if self.token == *tok {
            self.bump();
        } else {
            fail!("Expected {} but found {}", *tok, self.token);
        }
    }

    /// Optionally eat a given token. If the token is not
    /// found, this will simply return false and
    /// won't fail.
    pub fn eat(&mut self, tok: &Token) -> bool {
        if self.token == *tok {
            self.bump();
            true
        } else {
            false
        }
    }
}

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

    #[test]
    fn noop() {
        let lexer = Lexer::new("foo");
        assert_eq!(lexer.token, Noop);
    }

    #[test]
    fn zeroed_span() {
        let lexer = Lexer::new("foo");
        assert_eq!(lexer.span, Span::new(0, 0));
    }

    #[test]
    fn bump_empty() {
        let mut lexer = Lexer::new("");
        lexer.bump();
        assert_eq!(lexer.token, Done);
    }

    #[test]
    fn bump_lparen() {
        let mut lexer = Lexer::new("(");
        lexer.bump();
        assert_eq!(lexer.token, LPAREN);
    }

    #[test]
    fn bump_rparen() {
        let mut lexer = Lexer::new(")");
        lexer.bump();
        assert_eq!(lexer.token, RPAREN);
    }
}
<commit_msg>Added keywords, all tests pass<commit_after>use std::str::Chars;
use std::io::Reader;
use std::str::SendStr;
use span::Span;
use std::str::Owned;

#[deriving(PartialEq, Show)]
pub enum Token {
    Keyword(SendStr),
    Dash,
    Colon,
    Caret,
    LParen,
    RParen,
    Noop,
    Done
}

/// The lexer is the system that has any notion of an input stream. The
/// stream can come from many sources, thus, we use the Reader trait
/// that is implemented for strings, files, tcp, etc...
///
/// The lexer should also not consume the whole stream at once, as
/// that will be a bottleneck for larger input sources.
pub struct Lexer<'a> {
    source: &'a str,
    iter: Chars<'a>,
    token: Token,
    span: Span
}

impl<'a> Lexer<'a> {
    pub fn new(source: &'a str) -> Lexer<'a> {
        Lexer {
            source: source,
            iter: source.chars(),
            token: Noop,
            span: Span::new(0, 0)
        }
    }

    /// Parse and fetch the next token in the text stream.
    /// This doesn't return the token, it stores it
    /// in the lexer.
    pub fn bump(&mut self) {
        // Fetch the next char.
        let c = match self.iter.next() {
            Some(c) => c,
            None => {
                self.token = Done;
                return;
            }
        };

        match c {
            '(' => { self.token = LParen },
            ')' => { self.token = RParen },
            '^' => { self.token = Caret },
            '-' => { self.token = Dash },
            ':' => {
                let mut keyword = String::new();
                self.iter.next().while_some(|a| {
                    if a.is_whitespace() || a == '(' || a == ')' || a == '^' || a == ':' {
                        None
                    } else {
                        keyword.push_char(a);
                        self.iter.next()
                    }
                });

                self.token = Keyword(Owned(keyword));
            },
            ch @ 'A'..'Z' | ch @ 'a'..'z' => {
                println!("{}", ch);
            },
            _ => {}
        }
    }

    /// Expect a given token to come next, otherwise
    /// fail. Failing will result in a formatted
    /// error message that uses the Span and lets
    /// the user know what went wrong.
    pub fn expect(&mut self, tok: &Token) {
        if self.token == *tok {
            self.bump();
        } else {
            fail!("Expected {} but found {}", *tok, self.token);
        }
    }

    /// Optionally eat a given token. If the token is not
    /// found, this will simply return false and
    /// won't fail.
    pub fn eat(&mut self, tok: &Token) -> bool {
        if self.token == *tok {
            self.bump();
            true
        } else {
            false
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use span::Span;
    use std::str::{Slice, Owned};

    #[test]
    fn noop() {
        let lexer = Lexer::new("foo");
        assert_eq!(lexer.token, Noop);
    }

    #[test]
    fn zeroed_span() {
        let lexer = Lexer::new("foo");
        assert_eq!(lexer.span, Span::new(0, 0));
    }

    #[test]
    fn bump_empty() {
        let mut lexer = Lexer::new("");
        lexer.bump();
        assert_eq!(lexer.token, Done);
    }

    #[test]
    fn bump_lparen() {
        let mut lexer = Lexer::new("(");
        lexer.bump();
        assert_eq!(lexer.token, LParen);
    }

    #[test]
    fn bump_rparen() {
        let mut lexer = Lexer::new(")");
        lexer.bump();
        assert_eq!(lexer.token, RParen);
    }

    #[test]
    fn bump_simple_keyword() {
        let mut lexer = Lexer::new(":foo");
        lexer.bump();
        assert_eq!(lexer.token, Keyword(Slice("foo")));
    }
}
<|endoftext|>


<commit_before>use std::net::{ToSocketAddrs, TcpListener, TcpStream};
use std::io::{Read, Write};
use std::thread;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::{Sender, channel};

use super::command::command;
use super::command::Response;
use super::database::Database;
use super::parser::parse;
use super::parser::ParseError;

struct Client {
    stream: TcpStream,
    db: Arc<Mutex<Database>>
}

pub struct Server<A: ToSocketAddrs + Clone> {
    addr: A,
    db: Arc<Mutex<Database>>,
    listener_channel: Option<Sender<u8>>,
    listener_thread: Option<thread::JoinHandle>,
}

impl Client {
    pub fn new(stream: TcpStream, db: Arc<Mutex<Database>>) -> Client {
        return Client {
            stream: stream,
            db: db,
        }
    }

    pub fn run(&mut self) {
        #![allow(unused_must_use)]
        let (tx, rx) = channel();
        {
            let mut stream = self.stream.try_clone().unwrap();
            thread::spawn(move || {
                loop {
                    let try_recv = rx.recv();
                    if try_recv.is_err() {
                        break;
                    }
                    let msg:Response = try_recv.unwrap();
                    let resp = msg.as_bytes();
                    stream.write(&*resp);
                }
            });
        }
        let mut buffer = [0u8; 512];
        loop {
            let result = self.stream.read(&mut buffer);
            if result.is_err() {
                break;
            }
            let len = result.unwrap();
            if len == 0 {
                break;
            }
            let try_parser = parse(&buffer, len);
            if try_parser.is_err() {
                let err = try_parser.unwrap_err();
                match err {
                    ParseError::BadProtocol => { break; }
                    ParseError::Incomplete => { continue; }
                };
            }
            let parser = try_parser.unwrap();
            let mut db = self.db.lock().unwrap();
            let response = command(&parser, &mut *db);
            if tx.send(response).is_err() {
                // TODO: send a kill signal to the writer thread?
                break;
            }
        };
    }
}

impl<A: ToSocketAddrs + Clone> Server<A> {
    pub fn new(addr: A) -> Server<A> {
        return Server {
            addr: addr,
            db: Arc::new(Mutex::new(Database::new())),
            listener_channel: None,
            listener_thread: None,
        }
    }

    fn get_listener(&self) -> TcpListener {
        return TcpListener::bind(self.addr.clone()).unwrap();
    }

    pub fn run(&mut self) {
        self.start();
        self.join();
    }

    pub fn join(&mut self) {
        #![allow(unused_must_use)]
        match self.listener_thread.take() {
            Some(th) => { th.join(); },
            _ => {},
        }
    }

    pub fn start(&mut self) {
        let listener = self.get_listener();
        let db = self.db.clone();
        let (tx, rx) = channel();
        self.listener_channel = Some(tx);
        let th =  thread::spawn(move || {
            for stream in listener.incoming() {
                if rx.try_recv().is_ok() {
                    // any new message should break
                    break;
                }
                match stream {
                    Ok(stream) => {
                        let db1 = db.clone();
                        thread::spawn(move || {
                            let mut client = Client::new(stream, db1);
                            client.run();
                        });
                    }
                    Err(e) => { println!("error {}", e); }
                }
            }
        });
        self.listener_thread = Some(th);
    }

    pub fn stop(&mut self) {
        #![allow(unused_must_use)]
        match self.listener_channel {
            Some(ref sender) => {
                sender.send(0);
                for addrs in self.addr.to_socket_addrs().unwrap() {
                    TcpStream::connect(addrs);
                }
            },
            _ => {},
        }
        self.join();
    }
}
<commit_msg>Add return type to join handle<commit_after>use std::net::{ToSocketAddrs, TcpListener, TcpStream};
use std::io::{Read, Write};
use std::thread;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::{Sender, channel};

use super::command::command;
use super::command::Response;
use super::database::Database;
use super::parser::parse;
use super::parser::ParseError;

struct Client {
    stream: TcpStream,
    db: Arc<Mutex<Database>>
}

pub struct Server<A: ToSocketAddrs + Clone> {
    addr: A,
    db: Arc<Mutex<Database>>,
    listener_channel: Option<Sender<u8>>,
    listener_thread: Option<thread::JoinHandle<()>>,
}

impl Client {
    pub fn new(stream: TcpStream, db: Arc<Mutex<Database>>) -> Client {
        return Client {
            stream: stream,
            db: db,
        }
    }

    pub fn run(&mut self) {
        #![allow(unused_must_use)]
        let (tx, rx) = channel();
        {
            let mut stream = self.stream.try_clone().unwrap();
            thread::spawn(move || {
                loop {
                    let try_recv = rx.recv();
                    if try_recv.is_err() {
                        break;
                    }
                    let msg:Response = try_recv.unwrap();
                    let resp = msg.as_bytes();
                    stream.write(&*resp);
                }
            });
        }
        let mut buffer = [0u8; 512];
        loop {
            let result = self.stream.read(&mut buffer);
            if result.is_err() {
                break;
            }
            let len = result.unwrap();
            if len == 0 {
                break;
            }
            let try_parser = parse(&buffer, len);
            if try_parser.is_err() {
                let err = try_parser.unwrap_err();
                match err {
                    ParseError::BadProtocol => { break; }
                    ParseError::Incomplete => { continue; }
                };
            }
            let parser = try_parser.unwrap();
            let mut db = self.db.lock().unwrap();
            let response = command(&parser, &mut *db);
            if tx.send(response).is_err() {
                // TODO: send a kill signal to the writer thread?
                break;
            }
        };
    }
}

impl<A: ToSocketAddrs + Clone> Server<A> {
    pub fn new(addr: A) -> Server<A> {
        return Server {
            addr: addr,
            db: Arc::new(Mutex::new(Database::new())),
            listener_channel: None,
            listener_thread: None,
        }
    }

    fn get_listener(&self) -> TcpListener {
        return TcpListener::bind(self.addr.clone()).unwrap();
    }

    pub fn run(&mut self) {
        self.start();
        self.join();
    }

    pub fn join(&mut self) {
        #![allow(unused_must_use)]
        match self.listener_thread.take() {
            Some(th) => { th.join(); },
            _ => {},
        }
    }

    pub fn start(&mut self) {
        let listener = self.get_listener();
        let db = self.db.clone();
        let (tx, rx) = channel();
        self.listener_channel = Some(tx);
        let th =  thread::spawn(move || {
            for stream in listener.incoming() {
                if rx.try_recv().is_ok() {
                    // any new message should break
                    break;
                }
                match stream {
                    Ok(stream) => {
                        let db1 = db.clone();
                        thread::spawn(move || {
                            let mut client = Client::new(stream, db1);
                            client.run();
                        });
                    }
                    Err(e) => { println!("error {}", e); }
                }
            }
        });
        self.listener_thread = Some(th);
    }

    pub fn stop(&mut self) {
        #![allow(unused_must_use)]
        match self.listener_channel {
            Some(ref sender) => {
                sender.send(0);
                for addrs in self.addr.to_socket_addrs().unwrap() {
                    TcpStream::connect(addrs);
                }
            },
            _ => {},
        }
        self.join();
    }
}
<|endoftext|>


<commit_before>extern crate flac;

use flac::metadata;
use flac::metadata::{Picture, PictureType};
use std::io::ErrorKind;

fn compare_all_but_data(picture: &Picture, other_picture: &Picture) -> bool {
  (picture.picture_type == other_picture.picture_type) &&
    (picture.mime_type == other_picture.mime_type) &&
    (picture.description == other_picture.description) &&
    (picture.width == other_picture.width) &&
    (picture.height == other_picture.height) &&
    (picture.depth == other_picture.depth) &&
    (picture.colors == other_picture.colors)
}

#[test]
fn test_get_stream_info() {
  let result = metadata::get_stream_info("tests/assets/input-SCPAP.flac");

  assert!(result.is_ok());
}

#[test]
fn test_get_vorbis_comment() {
  let tags    = metadata::get_vorbis_comment("tests/assets/input-SVAUP.flac");
  let no_tags = metadata::get_vorbis_comment("tests/assets/input-SCPAP.flac");

  assert!(tags.is_ok(), "Should have vorbis comments");
  assert_eq!(no_tags.unwrap_err().kind(), ErrorKind::NotFound);
}

#[test]
fn test_get_cue_sheet() {
  let cue_sheet    = metadata::get_cue_sheet("tests/assets/input-SCPAP.flac");
  let no_cue_sheet = metadata::get_cue_sheet("tests/assets/input-SVAUP.flac");

  assert!(cue_sheet.is_ok(), "Should have a cue sheet");
  assert_eq!(no_cue_sheet.unwrap_err().kind(), ErrorKind::NotFound);
}

#[test]
fn test_get_picture() {
  let picture_file = "tests/assets/input-pictures.flac";
  let all_options  = metadata::get_picture(picture_file,
                                           Some(PictureType::Other),
                                           Some("image/gif"),
                                           Some(""),
                                           Some(16),
                                           Some(14),
                                           Some(24),
                                           Some(128));
  let picture1 = Picture {
    picture_type: PictureType::Other,
    mime_type: "image/gif".to_owned(),
    description: "".to_owned(),
    width: 16,
    height: 14,
    depth: 24,
    colors: 128,
    data: vec![],
  };

  let one_option = metadata::get_picture(picture_file,
                                         Some(PictureType::FileIconStandard),
                                         None, None, None, None, None, None);
  let picture2 = Picture {
    picture_type: PictureType::FileIconStandard,
    mime_type: "image/png".to_owned(),
    description: "8.png".to_owned(),
    width: 32,
    height: 32,
    depth: 32,
    colors: 0,
    data: vec![],
  };

  let no_option = metadata::get_picture(picture_file, None, None, None, None,
                                        None, None, None);
  let picture3  = Picture {
    picture_type: PictureType::FrontCover,
    mime_type: "image/png".to_owned(),
    description: "7.png".to_owned(),
    width: 31,
    height: 47,
    depth: 24,
    colors: 23,
    data: vec![],
  };
  let no_picture = metadata::get_picture("tests/assets/input-SVAUP.flac",
                                         None, None, None, None, None, None,
                                         None);

  assert!(compare_all_but_data(&all_options.unwrap(), &picture1),
          "All constraint options");
  assert!(compare_all_but_data(&one_option.unwrap(), &picture2),
          "One constraint option");
  assert!(compare_all_but_data(&no_option.unwrap(), &picture3),
          "No constraint option");
  assert_eq!(no_picture.unwrap_err().kind(), ErrorKind::NotFound);
}
<commit_msg>Fix integation tests for exported `get_*`<commit_after>extern crate flac;

use flac::{metadata, ErrorKind};
use flac::metadata::{Picture, PictureType};

fn compare_all_but_data(picture: &Picture, other_picture: &Picture) -> bool {
  (picture.picture_type == other_picture.picture_type) &&
    (picture.mime_type == other_picture.mime_type) &&
    (picture.description == other_picture.description) &&
    (picture.width == other_picture.width) &&
    (picture.height == other_picture.height) &&
    (picture.depth == other_picture.depth) &&
    (picture.colors == other_picture.colors)
}

#[test]
fn test_get_stream_info() {
  let result = metadata::get_stream_info("tests/assets/input-SCPAP.flac");

  assert!(result.is_ok());
}

#[test]
fn test_get_vorbis_comment() {
  let tags    = metadata::get_vorbis_comment("tests/assets/input-SVAUP.flac");
  let no_tags = metadata::get_vorbis_comment("tests/assets/input-SCPAP.flac");

  assert!(tags.is_ok(), "Should have vorbis comments");
  assert_eq!(no_tags.unwrap_err(), ErrorKind::NotFound);
}

#[test]
fn test_get_cue_sheet() {
  let cue_sheet    = metadata::get_cue_sheet("tests/assets/input-SCPAP.flac");
  let no_cue_sheet = metadata::get_cue_sheet("tests/assets/input-SVAUP.flac");

  assert!(cue_sheet.is_ok(), "Should have a cue sheet");
  assert_eq!(no_cue_sheet.unwrap_err(), ErrorKind::NotFound);
}

#[test]
fn test_get_picture() {
  let picture_file = "tests/assets/input-pictures.flac";
  let all_options  = metadata::get_picture(picture_file,
                                           Some(PictureType::Other),
                                           Some("image/gif"),
                                           Some(""),
                                           Some(16),
                                           Some(14),
                                           Some(24),
                                           Some(128));
  let picture1 = Picture {
    picture_type: PictureType::Other,
    mime_type: "image/gif".to_owned(),
    description: "".to_owned(),
    width: 16,
    height: 14,
    depth: 24,
    colors: 128,
    data: vec![],
  };

  let one_option = metadata::get_picture(picture_file,
                                         Some(PictureType::FileIconStandard),
                                         None, None, None, None, None, None);
  let picture2 = Picture {
    picture_type: PictureType::FileIconStandard,
    mime_type: "image/png".to_owned(),
    description: "8.png".to_owned(),
    width: 32,
    height: 32,
    depth: 32,
    colors: 0,
    data: vec![],
  };

  let no_option = metadata::get_picture(picture_file, None, None, None, None,
                                        None, None, None);
  let picture3  = Picture {
    picture_type: PictureType::FrontCover,
    mime_type: "image/png".to_owned(),
    description: "7.png".to_owned(),
    width: 31,
    height: 47,
    depth: 24,
    colors: 23,
    data: vec![],
  };
  let no_picture = metadata::get_picture("tests/assets/input-SVAUP.flac",
                                         None, None, None, None, None, None,
                                         None);

  assert!(compare_all_but_data(&all_options.unwrap(), &picture1),
          "All constraint options");
  assert!(compare_all_but_data(&one_option.unwrap(), &picture2),
          "One constraint option");
  assert!(compare_all_but_data(&no_option.unwrap(), &picture3),
          "No constraint option");
  assert_eq!(no_picture.unwrap_err(), ErrorKind::NotFound);
}
<|endoftext|>