<commit_before>#![feature(test)]
#![feature(plugin)]
#![plugin(quickcheck_macros)]

//
// cargo test listedit::experiments -- --nocapture
//

extern crate adapton ;
extern crate test;
extern crate quickcheck;
extern crate rand;

use adapton::adapton_sigs::* ;
use adapton::collection::*;
use adapton::engine;
use adapton::naive;

type Edits = Vec<CursorEdit<u32, Dir2>>;

fn compare_naive_and_cached(edits: &Edits) -> bool {
    let reduction = ListReduce::Max;
    let mut n_st = naive::AdaptonFromScratch::new();
    let mut e_st = engine::Engine::new();

    let results_1 = Experiment::run(&mut n_st, edits.clone(), reduction.clone());
    let results_2 = Experiment::run(&mut e_st, edits.clone(), reduction.clone());

    let mut idx = 0;
    for (a, b) in results_1.iter().zip(results_2.iter()) {
        if a.0 != b.0 {
            println!("After edit {}, {:?}, expected {:?} to be {:?}, but found {:?}.",
                     idx, edits[idx], &reduction, a.0, b.0);
            return false;
        }
        idx += 1;
    }

    true
}

#[test]
fn ensure_consistency_randomly() {
    let rng = rand::thread_rng();
    let mut gen = quickcheck::StdGen::new(rng, 100);
    for _ in 0..100 {
        let testv = <Edits as quickcheck::Arbitrary>::arbitrary(&mut gen);
        if !compare_naive_and_cached(&testv) {
            panic!("{:?}", testv);
        }
    }
}

#[test]
#[ignore]
fn ensure_consistency_regression_testcase1() { assert!( compare_naive_and_cached(&testcase1())) }

#[test]
#[ignore]
fn ensure_consistency_regression_testcase2() { assert!( compare_naive_and_cached(&testcase2())) }

#[test]
fn ensure_consistency_regression_testcase3() { assert!( compare_naive_and_cached(&testcase3())) }

fn testcase1 () -> Edits {
    vec![
        CursorEdit::Insert(Dir2::Left, 36), CursorEdit::Insert(Dir2::Right, 44), CursorEdit::Remove(Dir2::Right), CursorEdit::Remove(Dir2::Right),
        CursorEdit::Insert(Dir2::Left, 86), CursorEdit::Insert(Dir2::Left, 11), CursorEdit::Insert(Dir2::Right, 22), CursorEdit::Insert(Dir2::Right, 23),
        CursorEdit::Insert(Dir2::Left, 41), CursorEdit::Remove(Dir2::Right),
        CursorEdit::Insert(Dir2::Right, 13), CursorEdit::Insert(Dir2::Left, 21), CursorEdit::Goto(Dir2::Right), CursorEdit::Insert(Dir2::Right, 41),
        CursorEdit::Insert(Dir2::Left, 71), CursorEdit::Goto(Dir2::Right), CursorEdit::Insert(Dir2::Right, 22), CursorEdit::Replace(Dir2::Left, 11), CursorEdit::Goto(Dir2::Right),
        CursorEdit::Insert(Dir2::Right, 76), CursorEdit::Insert(Dir2::Left, 45), CursorEdit::Goto(Dir2::Left), CursorEdit::Insert(Dir2::Left, 12), CursorEdit::Insert(Dir2::Right, 14),
        CursorEdit::Goto(Dir2::Right), CursorEdit::Goto(Dir2::Right), CursorEdit::Insert(Dir2::Right, 35), CursorEdit::Goto(Dir2::Right), CursorEdit::Insert(Dir2::Left, 39),
        CursorEdit::Goto(Dir2::Left), CursorEdit::Insert(Dir2::Right, 43), CursorEdit::Goto(Dir2::Right), CursorEdit::Insert(Dir2::Right, 36), CursorEdit::Insert(Dir2::Left, 85),
        CursorEdit::Insert(Dir2::Left, 11), CursorEdit::Insert(Dir2::Left, 93), CursorEdit::Insert(Dir2::Right, 52), CursorEdit::Goto(Dir2::Left), CursorEdit::Goto(Dir2::Right)
       ]
}

fn testcase2 () -> Edits {
    vec![CursorEdit::Goto(Dir2::Left), CursorEdit::Insert(Dir2::Left, 86), CursorEdit::Insert(Dir2::Left, 76), CursorEdit::Insert(Dir2::Right, 39), CursorEdit::Goto(Dir2::Right),
         CursorEdit::Insert(Dir2::Right, 63), CursorEdit::Goto(Dir2::Right), CursorEdit::Replace(Dir2::Left, 54), CursorEdit::Remove(Dir2::Left),
         CursorEdit::Insert(Dir2::Right, 77), CursorEdit::Insert(Dir2::Right, 32), CursorEdit::Goto(Dir2::Left), CursorEdit::Goto(Dir2::Left),
         CursorEdit::Replace(Dir2::Right, 57), CursorEdit::Goto(Dir2::Right), CursorEdit::Replace(Dir2::Left, 6), CursorEdit::Remove(Dir2::Right),
         CursorEdit::Goto(Dir2::Right), CursorEdit::Insert(Dir2::Left, 81), CursorEdit::Goto(Dir2::Right), CursorEdit::Goto(Dir2::Left),
         CursorEdit::Remove(Dir2::Right), CursorEdit::Goto(Dir2::Left), CursorEdit::Goto(Dir2::Left), CursorEdit::Goto(Dir2::Right),
         CursorEdit::Insert(Dir2::Right, 76), CursorEdit::Replace(Dir2::Left, 72), CursorEdit::Insert(Dir2::Left, 51), CursorEdit::Remove(Dir2::Right), CursorEdit::Insert(Dir2::Left, 49),
         CursorEdit::Remove(Dir2::Left), CursorEdit::Goto(Dir2::Right), CursorEdit::Goto(Dir2::Right), CursorEdit::Insert(Dir2::Left, 6),
         CursorEdit::Insert(Dir2::Right, 82), CursorEdit::Goto(Dir2::Right), CursorEdit::Insert(Dir2::Right, 89), CursorEdit::Goto(Dir2::Right),
         CursorEdit::Insert(Dir2::Left, 9), CursorEdit::Goto(Dir2::Left), CursorEdit::Insert(Dir2::Left, 26), CursorEdit::Replace(Dir2::Left, 35),
         CursorEdit::Goto(Dir2::Left), CursorEdit::Remove(Dir2::Left), CursorEdit::Goto(Dir2::Right), CursorEdit::Goto(Dir2::Left), CursorEdit::Goto(Dir2::Left),
         CursorEdit::Remove(Dir2::Left), CursorEdit::Goto(Dir2::Right), CursorEdit::Insert(Dir2::Left, 5), CursorEdit::Insert(Dir2::Left, 75),
         CursorEdit::Goto(Dir2::Left), CursorEdit::Insert(Dir2::Left, 32), CursorEdit::Replace(Dir2::Right, 74), CursorEdit::Insert(Dir2::Left, 77),
         CursorEdit::Insert(Dir2::Left, 71), CursorEdit::Insert(Dir2::Left, 44), CursorEdit::Goto(Dir2::Left), CursorEdit::Insert(Dir2::Right, 81),
         CursorEdit::Insert(Dir2::Left, 61), CursorEdit::Insert(Dir2::Right, 92), CursorEdit::Insert(Dir2::Left, 68), CursorEdit::Replace(Dir2::Right, 42),
         CursorEdit::Insert(Dir2::Right, 81), CursorEdit::Goto(Dir2::Right), CursorEdit::Goto(Dir2::Right), CursorEdit::Insert(Dir2::Right, 31),
         CursorEdit::Insert(Dir2::Right, 72), CursorEdit::Insert(Dir2::Right, 70), CursorEdit::Insert(Dir2::Left, 87), CursorEdit::Insert(Dir2::Right, 95),
         CursorEdit::Goto(Dir2::Left), CursorEdit::Goto(Dir2::Left), CursorEdit::Replace(Dir2::Right, 96), CursorEdit::Goto(Dir2::Right),
         CursorEdit::Goto(Dir2::Left), CursorEdit::Remove(Dir2::Left), CursorEdit::Goto(Dir2::Left)]
}

fn testcase3 () -> Edits {
    vec![CursorEdit::Insert(Dir2::Left, 86), CursorEdit::Insert(Dir2::Right, 37), CursorEdit::Remove(Dir2::Left), CursorEdit::Insert(Dir2::Left, 42),
         CursorEdit::Goto(Dir2::Right), CursorEdit::Insert(Dir2::Left, 68), CursorEdit::Insert(Dir2::Right, 18), CursorEdit::Remove(Dir2::Left),
         CursorEdit::Remove(Dir2::Right), CursorEdit::Goto(Dir2::Right), CursorEdit::Replace(Dir2::Left, 68), CursorEdit::Insert(Dir2::Left, 82),
         CursorEdit::Insert(Dir2::Right, 30), CursorEdit::Goto(Dir2::Right), CursorEdit::Insert(Dir2::Left, 78), CursorEdit::Insert(Dir2::Right, 88), CursorEdit::Insert(Dir2::Left, 38),
         CursorEdit::Insert(Dir2::Right, 91), CursorEdit::Goto(Dir2::Left),
         CursorEdit::Insert(Dir2::Right, 67), CursorEdit::Insert(Dir2::Right, 3),
         CursorEdit::Remove(Dir2::Right), CursorEdit::Insert(Dir2::Right, 16), CursorEdit::Insert(Dir2::Left, 4), CursorEdit::Insert(Dir2::Right, 29),
         CursorEdit::Insert(Dir2::Right, 92), CursorEdit::Insert(Dir2::Left, 79), CursorEdit::Replace(Dir2::Left, 88), CursorEdit::Goto(Dir2::Right),
         CursorEdit::Goto(Dir2::Left),
         // CursorEdit::Insert(Dir2::Left, 25), CursorEdit::Insert(Dir2::Left, 46), CursorEdit::Goto(Dir2::Left), CursorEdit::Goto(Dir2::Left),
         // CursorEdit::Insert(Dir2::Left, 18), CursorEdit::Insert(Dir2::Left, 1), CursorEdit::Insert(Dir2::Right, 43), CursorEdit::Goto(Dir2::Left), CursorEdit::Remove(Dir2::Left),
         // CursorEdit::Insert(Dir2::Right, 93), CursorEdit::Goto(Dir2::Left), CursorEdit::Insert(Dir2::Right, 10), CursorEdit::Remove(Dir2::Left),
         // CursorEdit::Insert(Dir2::Right, 34), CursorEdit::Remove(Dir2::Left), CursorEdit::Replace(Dir2::Left, 47), CursorEdit::Goto(Dir2::Right),
         // CursorEdit::Insert(Dir2::Left, 56), CursorEdit::Insert(Dir2::Left, 36), CursorEdit::Replace(Dir2::Right, 99), CursorEdit::Insert(Dir2::Right, 19),
         // CursorEdit::Insert(Dir2::Right, 35), CursorEdit::Goto(Dir2::Right), CursorEdit::Insert(Dir2::Right, 94), CursorEdit::Replace(Dir2::Right, 58), CursorEdit::Goto(Dir2::Right),
         // CursorEdit::Insert(Dir2::Left, 71)
         ]
}
<commit_msg>minor--reformatting<commit_after>#![feature(test)]
#![feature(plugin)]
#![plugin(quickcheck_macros)]

//
// cargo test listedit::experiments -- --nocapture
//

extern crate adapton ;
extern crate test;
extern crate quickcheck;
extern crate rand;

use adapton::adapton_sigs::* ;
use adapton::collection::*;
use adapton::engine;
use adapton::naive;

type Edits = Vec<CursorEdit<u32, Dir2>>;

fn compare_naive_and_cached(edits: &Edits) -> bool {
    let reduction = ListReduce::Max;
    let mut n_st = naive::AdaptonFromScratch::new();
    let mut e_st = engine::Engine::new();

    let results_1 = Experiment::run(&mut n_st, edits.clone(), reduction.clone());
    let results_2 = Experiment::run(&mut e_st, edits.clone(), reduction.clone());

    let mut idx = 0;
    for (a, b) in results_1.iter().zip(results_2.iter()) {
        if a.0 != b.0 {
            println!("After edit {}, {:?}, expected {:?} to be {:?}, but found {:?}.",
                     idx, edits[idx], &reduction, a.0, b.0);
            return false;
        }
        idx += 1;
    }

    true
}

#[test]
fn ensure_consistency_randomly() {
    let rng = rand::thread_rng();
    let mut gen = quickcheck::StdGen::new(rng, 100);
    for _ in 0..100 {
        let testv = <Edits as quickcheck::Arbitrary>::arbitrary(&mut gen);
        if !compare_naive_and_cached(&testv) {
            panic!("{:?}", testv);
        }
    }
}

#[test]
#[ignore]
fn ensure_consistency_regression_testcase1() { assert!( compare_naive_and_cached(&testcase1())) }

#[test]
#[ignore]
fn ensure_consistency_regression_testcase2() { assert!( compare_naive_and_cached(&testcase2())) }

#[test]
fn ensure_consistency_regression_testcase3() { assert!( compare_naive_and_cached(&testcase3())) }

fn testcase1 () -> Edits {
    vec![
        CursorEdit::Insert(Dir2::Left, 36), CursorEdit::Insert(Dir2::Right, 44), CursorEdit::Remove(Dir2::Right), CursorEdit::Remove(Dir2::Right),
        CursorEdit::Insert(Dir2::Left, 86), CursorEdit::Insert(Dir2::Left, 11), CursorEdit::Insert(Dir2::Right, 22), CursorEdit::Insert(Dir2::Right, 23),
        CursorEdit::Insert(Dir2::Left, 41), CursorEdit::Remove(Dir2::Right),
        CursorEdit::Insert(Dir2::Right, 13), CursorEdit::Insert(Dir2::Left, 21), CursorEdit::Goto(Dir2::Right), CursorEdit::Insert(Dir2::Right, 41),
        CursorEdit::Insert(Dir2::Left, 71), CursorEdit::Goto(Dir2::Right), CursorEdit::Insert(Dir2::Right, 22), CursorEdit::Replace(Dir2::Left, 11), CursorEdit::Goto(Dir2::Right),
        CursorEdit::Insert(Dir2::Right, 76), CursorEdit::Insert(Dir2::Left, 45), CursorEdit::Goto(Dir2::Left), CursorEdit::Insert(Dir2::Left, 12), CursorEdit::Insert(Dir2::Right, 14),
        CursorEdit::Goto(Dir2::Right), CursorEdit::Goto(Dir2::Right), CursorEdit::Insert(Dir2::Right, 35), CursorEdit::Goto(Dir2::Right), CursorEdit::Insert(Dir2::Left, 39),
        CursorEdit::Goto(Dir2::Left), CursorEdit::Insert(Dir2::Right, 43), CursorEdit::Goto(Dir2::Right), CursorEdit::Insert(Dir2::Right, 36), CursorEdit::Insert(Dir2::Left, 85),
        CursorEdit::Insert(Dir2::Left, 11), CursorEdit::Insert(Dir2::Left, 93), CursorEdit::Insert(Dir2::Right, 52), CursorEdit::Goto(Dir2::Left), CursorEdit::Goto(Dir2::Right)
       ]
}

fn testcase2 () -> Edits {
    vec![CursorEdit::Goto(Dir2::Left), CursorEdit::Insert(Dir2::Left, 86), CursorEdit::Insert(Dir2::Left, 76), CursorEdit::Insert(Dir2::Right, 39), CursorEdit::Goto(Dir2::Right),
         CursorEdit::Insert(Dir2::Right, 63), CursorEdit::Goto(Dir2::Right), CursorEdit::Replace(Dir2::Left, 54), CursorEdit::Remove(Dir2::Left),
         CursorEdit::Insert(Dir2::Right, 77), CursorEdit::Insert(Dir2::Right, 32), CursorEdit::Goto(Dir2::Left), CursorEdit::Goto(Dir2::Left),
         CursorEdit::Replace(Dir2::Right, 57), CursorEdit::Goto(Dir2::Right), CursorEdit::Replace(Dir2::Left, 6), CursorEdit::Remove(Dir2::Right),
         CursorEdit::Goto(Dir2::Right), CursorEdit::Insert(Dir2::Left, 81), CursorEdit::Goto(Dir2::Right), CursorEdit::Goto(Dir2::Left),
         CursorEdit::Remove(Dir2::Right), CursorEdit::Goto(Dir2::Left), CursorEdit::Goto(Dir2::Left), CursorEdit::Goto(Dir2::Right),
         CursorEdit::Insert(Dir2::Right, 76), CursorEdit::Replace(Dir2::Left, 72), CursorEdit::Insert(Dir2::Left, 51), CursorEdit::Remove(Dir2::Right), CursorEdit::Insert(Dir2::Left, 49),
         CursorEdit::Remove(Dir2::Left), CursorEdit::Goto(Dir2::Right), CursorEdit::Goto(Dir2::Right), CursorEdit::Insert(Dir2::Left, 6),
         CursorEdit::Insert(Dir2::Right, 82), CursorEdit::Goto(Dir2::Right), CursorEdit::Insert(Dir2::Right, 89), CursorEdit::Goto(Dir2::Right),
         CursorEdit::Insert(Dir2::Left, 9), CursorEdit::Goto(Dir2::Left), CursorEdit::Insert(Dir2::Left, 26), CursorEdit::Replace(Dir2::Left, 35),
         CursorEdit::Goto(Dir2::Left), CursorEdit::Remove(Dir2::Left), CursorEdit::Goto(Dir2::Right), CursorEdit::Goto(Dir2::Left), CursorEdit::Goto(Dir2::Left),
         CursorEdit::Remove(Dir2::Left), CursorEdit::Goto(Dir2::Right), CursorEdit::Insert(Dir2::Left, 5), CursorEdit::Insert(Dir2::Left, 75),
         CursorEdit::Goto(Dir2::Left), CursorEdit::Insert(Dir2::Left, 32), CursorEdit::Replace(Dir2::Right, 74), CursorEdit::Insert(Dir2::Left, 77),
         CursorEdit::Insert(Dir2::Left, 71), CursorEdit::Insert(Dir2::Left, 44), CursorEdit::Goto(Dir2::Left), CursorEdit::Insert(Dir2::Right, 81),
         CursorEdit::Insert(Dir2::Left, 61), CursorEdit::Insert(Dir2::Right, 92), CursorEdit::Insert(Dir2::Left, 68), CursorEdit::Replace(Dir2::Right, 42),
         CursorEdit::Insert(Dir2::Right, 81), CursorEdit::Goto(Dir2::Right), CursorEdit::Goto(Dir2::Right), CursorEdit::Insert(Dir2::Right, 31),
         CursorEdit::Insert(Dir2::Right, 72), CursorEdit::Insert(Dir2::Right, 70), CursorEdit::Insert(Dir2::Left, 87), CursorEdit::Insert(Dir2::Right, 95),
         CursorEdit::Goto(Dir2::Left), CursorEdit::Goto(Dir2::Left), CursorEdit::Replace(Dir2::Right, 96), CursorEdit::Goto(Dir2::Right),
         CursorEdit::Goto(Dir2::Left), CursorEdit::Remove(Dir2::Left), CursorEdit::Goto(Dir2::Left)]
}

fn testcase3 () -> Edits {
    vec![CursorEdit::Insert(Dir2::Left, 86),
         CursorEdit::Insert(Dir2::Right, 37),
         CursorEdit::Remove(Dir2::Left),
         CursorEdit::Insert(Dir2::Left, 42),
         CursorEdit::Goto(Dir2::Right),
         CursorEdit::Insert(Dir2::Left, 68),
         CursorEdit::Insert(Dir2::Right, 18),
         CursorEdit::Remove(Dir2::Left),
         CursorEdit::Remove(Dir2::Right),
         CursorEdit::Goto(Dir2::Right),
         CursorEdit::Replace(Dir2::Left, 68),
         CursorEdit::Insert(Dir2::Left, 82),
         CursorEdit::Insert(Dir2::Right, 30),
         CursorEdit::Goto(Dir2::Right),
         CursorEdit::Insert(Dir2::Left, 78),
         CursorEdit::Insert(Dir2::Right, 88),
         CursorEdit::Insert(Dir2::Left, 38),
         CursorEdit::Insert(Dir2::Right, 91),
         CursorEdit::Goto(Dir2::Left),
         CursorEdit::Insert(Dir2::Right, 67),
         CursorEdit::Insert(Dir2::Right, 3),
         CursorEdit::Remove(Dir2::Right),
         CursorEdit::Insert(Dir2::Right, 16),
         CursorEdit::Insert(Dir2::Left, 4),
         CursorEdit::Insert(Dir2::Right, 29),
         CursorEdit::Insert(Dir2::Right, 92),
         CursorEdit::Insert(Dir2::Left, 79),
         CursorEdit::Replace(Dir2::Left, 88),
         CursorEdit::Goto(Dir2::Right),
         CursorEdit::Goto(Dir2::Left),
         // CursorEdit::Insert(Dir2::Left, 25), CursorEdit::Insert(Dir2::Left, 46), CursorEdit::Goto(Dir2::Left), CursorEdit::Goto(Dir2::Left),
         // CursorEdit::Insert(Dir2::Left, 18), CursorEdit::Insert(Dir2::Left, 1), CursorEdit::Insert(Dir2::Right, 43), CursorEdit::Goto(Dir2::Left), CursorEdit::Remove(Dir2::Left),
         // CursorEdit::Insert(Dir2::Right, 93), CursorEdit::Goto(Dir2::Left), CursorEdit::Insert(Dir2::Right, 10), CursorEdit::Remove(Dir2::Left),
         // CursorEdit::Insert(Dir2::Right, 34), CursorEdit::Remove(Dir2::Left), CursorEdit::Replace(Dir2::Left, 47), CursorEdit::Goto(Dir2::Right),
         // CursorEdit::Insert(Dir2::Left, 56), CursorEdit::Insert(Dir2::Left, 36), CursorEdit::Replace(Dir2::Right, 99), CursorEdit::Insert(Dir2::Right, 19),
         // CursorEdit::Insert(Dir2::Right, 35), CursorEdit::Goto(Dir2::Right), CursorEdit::Insert(Dir2::Right, 94), CursorEdit::Replace(Dir2::Right, 58), CursorEdit::Goto(Dir2::Right),
         // CursorEdit::Insert(Dir2::Left, 71)
         ]
}
<|endoftext|>


<commit_before>use std::thread;
use std::cell::UnsafeCell;
use std::sync::Arc;
use std::sync::mpsc::{channel, Sender, Receiver, RecvError};
use basetypes::*;
use chess_move::MoveStack;
use tt::*;
use position::Position;

pub enum Command {
    Search {
        search_id: usize,
        position: Position,
        depth: u8,
        lower_bound: Value,
        upper_bound: Value,
    },
    Stop,
    Exit,
}

pub enum Report {
    Progress {
        search_id: usize,
        searched_nodes: NodeCount,
        depth: u8,
    },
    Done {
        search_id: usize,
        searched_nodes: NodeCount,
        value: Option<Value>,
    },
}

pub fn run_deepening(tt: Arc<TranspositionTable>,
                     commands: Receiver<Command>,
                     reports: Sender<Report>) {
    let (slave_commands_tx, slave_commands_rx) = channel();
    let (slave_reports_tx, slave_reports_rx) = channel();
    let slave = thread::spawn(move || {
        run(tt, slave_commands_rx, slave_reports_tx);
    });
    let mut pending_command = None;
    loop {
        // If there is a pending command, we take it, otherwise we
        // block and wait to receive a new one.
        let command = match pending_command.take() {
            Some(cmd) => cmd,
            None => commands.recv().or::<RecvError>(Ok(Command::Exit)).unwrap(),
        };

        match command {
            Command::Search { search_id, position, depth, lower_bound, upper_bound } => {
                let mut searched_nodes_final = 0;
                let mut value_final = None;
                'depthloop: for n in 1..(depth + 1) {
                    slave_commands_tx.send(Command::Search {
                                         search_id: n as usize,
                                         position: position.clone(),
                                         depth: n,
                                         lower_bound: lower_bound,
                                         upper_bound: upper_bound,
                                     })
                                     .unwrap();
                    loop {
                        match slave_reports_rx.recv().unwrap() {
                            Report::Progress { searched_nodes, .. } => {
                                reports.send(Report::Progress {
                                           search_id: search_id,
                                           searched_nodes: searched_nodes_final + searched_nodes,
                                           depth: n,
                                       })
                                       .ok();
                                if pending_command.is_none() {
                                    pending_command = match commands.try_recv() {
                                        Ok(cmd) => {
                                            slave_commands_tx.send(Command::Stop).unwrap();
                                            Some(cmd)
                                        }
                                        _ => None,
                                    }
                                }
                            }
                            Report::Done { searched_nodes, value, .. } => {
                                searched_nodes_final += searched_nodes;
                                if n == depth {
                                    value_final = value;
                                }
                                if pending_command.is_some() {
                                    break 'depthloop;
                                }
                                break;
                            }
                        }
                    }
                }
                reports.send(Report::Done {
                           search_id: search_id,
                           searched_nodes: searched_nodes_final,
                           value: value_final,
                       })
                       .ok();
            }
            Command::Stop => {
                slave_commands_tx.send(Command::Stop).unwrap();
                continue;
            }
            Command::Exit => {
                slave_commands_tx.send(Command::Exit).unwrap();
                break;
            }
        }
    }
    slave.join().unwrap();
}

pub fn run(tt: Arc<TranspositionTable>, commands: Receiver<Command>, reports: Sender<Report>) {
    thread_local!(
        static MOVE_STACK: UnsafeCell<MoveStack> = UnsafeCell::new(MoveStack::new())
    );
    MOVE_STACK.with(|s| {
        let mut move_stack = unsafe { &mut *s.get() };
        let mut pending_command = None;
        loop {
            // If there is a pending command, we take it, otherwise we
            // block and wait to receive a new one.
            let command = match pending_command.take() {
                Some(cmd) => cmd,
                None => commands.recv().or::<RecvError>(Ok(Command::Exit)).unwrap(),
            };

            match command {
                Command::Search { search_id, mut position, depth, lower_bound, upper_bound } => {
                    let mut reported_nodes = 0;
                    let mut unreported_nodes = 0;
                    let value = search(&tt,
                                       &mut position,
                                       move_stack,
                                       &mut unreported_nodes,
                                       &mut |n| {
                                           reported_nodes += n;
                                           reports.send(Report::Progress {
                                                      search_id: search_id,
                                                      searched_nodes: reported_nodes,
                                                      depth: depth,
                                                  })
                                                  .ok();
                                           match commands.try_recv() {
                                               Ok(x) => {
                                                   pending_command = Some(x);
                                                   Err(TerminatedSearch)
                                               }
                                               _ => Ok(()),
                                           }
                                       },
                                       lower_bound,
                                       upper_bound,
                                       depth)
                                    .ok();
                    reported_nodes += unreported_nodes;
                    reports.send(Report::Progress {
                               search_id: search_id,
                               searched_nodes: reported_nodes,
                               depth: depth,
                           })
                           .ok();
                    reports.send(Report::Done {
                               search_id: search_id,
                               searched_nodes: reported_nodes,
                               value: value,
                           })
                           .ok();
                    move_stack.clear();
                }
                Command::Stop => continue,
                Command::Exit => break,
            }
        }
    })
}

// Represents a terminated search condition.
struct TerminatedSearch;

// Helper function for `run()`. It implements the principal variation
// search algorithm. When returning `Err(TerminatedSearch)`, this
// function may leave un-restored move lists in `moves`.
fn search(tt: &TranspositionTable,
          p: &mut Position,
          moves: &mut MoveStack,
          nc: &mut NodeCount,
          report: &mut FnMut(NodeCount) -> Result<(), TerminatedSearch>,
          mut alpha: Value, // lower bound
          beta: Value, // upper bound
          depth: u8)
          -> Result<Value, TerminatedSearch> {
    assert!(alpha < beta);

    // Consult the transposition table.
    let hash_move16 = if let Some(entry) = tt.probe(p.hash()) {
        if entry.depth() >= depth {
            let value = entry.value();
            let bound = entry.bound();
            if bound == BOUND_EXACT {
                return Ok(value);
            }
            if bound == BOUND_LOWER && value >= beta {
                return Ok(beta);
            }
            if bound == BOUND_UPPER && value <= alpha {
                return Ok(alpha);
            }
        }
        entry.move16()
    } else {
        0
    };

    if depth == 0 {
        // On leaf nodes, do quiescence search.
        let (value, nodes) = p.evaluate_quiescence(alpha, beta, None);
        *nc += nodes;
        Ok(value)
    } else {
        moves.save();

        if hash_move16 != 0 {
            if let Some(m) = p.try_move_digest(hash_move16) {
                moves.push(m);
            }
        }
        let mut generated = false;

        let mut bound_type = BOUND_UPPER;
        let mut move16 = 0;
        let mut no_moves_yet = true;
        loop {
            // TODO: This is ugly, and probably inefficient.
            let m = match moves.remove_best_move() {
                None if generated => break,
                None => {
                    p.generate_moves(moves);
                    moves.remove_move(hash_move16);
                    generated = true;
                    continue;
                }
                Some(x) => x,
            };

            if p.do_move(m) {
                // From time to time, we report how many nodes had
                // been searched since the last report. This also
                // gives an opportunity for the search to be
                // terminated.
                *nc += 1;
                if *nc > NODE_COUNT_REPORT_INTERVAL {
                    try!(report(*nc));
                    *nc = 0;
                }

                let mut value = if no_moves_yet {
                    // The first move we analyze with a fully open
                    // window (alpha, beta).
                    -try!(search(tt, p, moves, nc, report, -beta, -alpha, depth - 1))
                } else {
                    // For the next moves we first try to prove that
                    // they are not better than our current best
                    // move. For this purpose we analyze them with a
                    // null window (alpha, alpha + 1). This is faster
                    // than a full window search. Only when we are
                    // certain that the move is better than our
                    // current best move, we do a full-window search.
                    match -try!(search(tt, p, moves, nc, report, -alpha - 1, -alpha, depth - 1)) {
                        x if x <= alpha => x,
                        _ => -try!(search(tt, p, moves, nc, report, -beta, -alpha, depth - 1)),
                    }
                };

                // A checkmate in fewer moves is better.
                if value > 20000 {
                    value -= 1;
                }
                if value < -20000 {
                    value += 1;
                }

                no_moves_yet = false;
                p.undo_move();

                if value >= beta {
                    // This move is too good, so that the opponent
                    // will not allow this line of play to
                    // happen. Therefore we can stop here.
                    alpha = beta;
                    bound_type = BOUND_LOWER;
                    move16 = m.digest();
                    break;
                }
                if value > alpha {
                    // We found ourselves a new best move.
                    alpha = value;
                    bound_type = BOUND_EXACT;
                    move16 = m.digest();
                }
            }
        }
        moves.restore();
        if no_moves_yet {
            // No legal moves -- this is a final position.
            alpha = p.evaluate_final();
            bound_type = BOUND_EXACT;
        }
        // TODO: Do not store values with too small `depth`.
        tt.store(p.hash(),
                 EntryData::new(alpha, bound_type, depth, move16, 0));
        Ok(alpha)
    }
}

const NODE_COUNT_REPORT_INTERVAL: NodeCount = 10000;

#[cfg(test)]
mod tests {
    use super::search;
    use chess_move::*;
    use tt::*;
    use position::Position;

    #[test]
    fn test_search() {
        let mut p = Position::from_fen("8/8/8/8/3q3k/7n/6PP/2Q2R1K b - - 0 1").ok().unwrap();
        let value = search(&TranspositionTable::new(),
                           &mut p,
                           &mut MoveStack::new(),
                           &mut 0,
                           &mut |_| Ok(()),
                           -30000,
                           30000,
                           2)
                        .ok()
                        .unwrap();
        assert!(value < -300);
        let value = search(&TranspositionTable::new(),
                           &mut p,
                           &mut MoveStack::new(),
                           &mut 0,
                           &mut |_| Ok(()),
                           -30000,
                           30000,
                           4)
                        .ok()
                        .unwrap();
        assert!(value >= 20000);
    }
}
<commit_msg>always fail-soft, store qsearch results in TT<commit_after>use std::thread;
use std::cell::UnsafeCell;
use std::sync::Arc;
use std::sync::mpsc::{channel, Sender, Receiver, RecvError};
use basetypes::*;
use chess_move::MoveStack;
use tt::*;
use position::Position;

pub enum Command {
    Search {
        search_id: usize,
        position: Position,
        depth: u8,
        lower_bound: Value,
        upper_bound: Value,
    },
    Stop,
    Exit,
}

pub enum Report {
    Progress {
        search_id: usize,
        searched_nodes: NodeCount,
        depth: u8,
    },
    Done {
        search_id: usize,
        searched_nodes: NodeCount,
        value: Option<Value>,
    },
}

pub fn run_deepening(tt: Arc<TranspositionTable>,
                     commands: Receiver<Command>,
                     reports: Sender<Report>) {
    let (slave_commands_tx, slave_commands_rx) = channel();
    let (slave_reports_tx, slave_reports_rx) = channel();
    let slave = thread::spawn(move || {
        run(tt, slave_commands_rx, slave_reports_tx);
    });
    let mut pending_command = None;
    loop {
        // If there is a pending command, we take it, otherwise we
        // block and wait to receive a new one.
        let command = match pending_command.take() {
            Some(cmd) => cmd,
            None => commands.recv().or::<RecvError>(Ok(Command::Exit)).unwrap(),
        };

        match command {
            Command::Search { search_id, position, depth, lower_bound, upper_bound } => {
                let mut searched_nodes_final = 0;
                let mut value_final = None;
                'depthloop: for n in 1..(depth + 1) {
                    slave_commands_tx.send(Command::Search {
                                         search_id: n as usize,
                                         position: position.clone(),
                                         depth: n,
                                         lower_bound: lower_bound,
                                         upper_bound: upper_bound,
                                     })
                                     .unwrap();
                    loop {
                        match slave_reports_rx.recv().unwrap() {
                            Report::Progress { searched_nodes, .. } => {
                                reports.send(Report::Progress {
                                           search_id: search_id,
                                           searched_nodes: searched_nodes_final + searched_nodes,
                                           depth: n,
                                       })
                                       .ok();
                                if pending_command.is_none() {
                                    pending_command = match commands.try_recv() {
                                        Ok(cmd) => {
                                            slave_commands_tx.send(Command::Stop).unwrap();
                                            Some(cmd)
                                        }
                                        _ => None,
                                    }
                                }
                            }
                            Report::Done { searched_nodes, value, .. } => {
                                searched_nodes_final += searched_nodes;
                                if n == depth {
                                    value_final = value;
                                }
                                if pending_command.is_some() {
                                    break 'depthloop;
                                }
                                break;
                            }
                        }
                    }
                }
                reports.send(Report::Done {
                           search_id: search_id,
                           searched_nodes: searched_nodes_final,
                           value: value_final,
                       })
                       .ok();
            }
            Command::Stop => {
                slave_commands_tx.send(Command::Stop).unwrap();
                continue;
            }
            Command::Exit => {
                slave_commands_tx.send(Command::Exit).unwrap();
                break;
            }
        }
    }
    slave.join().unwrap();
}

pub fn run(tt: Arc<TranspositionTable>, commands: Receiver<Command>, reports: Sender<Report>) {
    thread_local!(
        static MOVE_STACK: UnsafeCell<MoveStack> = UnsafeCell::new(MoveStack::new())
    );
    MOVE_STACK.with(|s| {
        let mut move_stack = unsafe { &mut *s.get() };
        let mut pending_command = None;
        loop {
            // If there is a pending command, we take it, otherwise we
            // block and wait to receive a new one.
            let command = match pending_command.take() {
                Some(cmd) => cmd,
                None => commands.recv().or::<RecvError>(Ok(Command::Exit)).unwrap(),
            };

            match command {
                Command::Search { search_id, mut position, depth, lower_bound, upper_bound } => {
                    let mut reported_nodes = 0;
                    let mut unreported_nodes = 0;
                    let value = search(&tt,
                                       &mut position,
                                       move_stack,
                                       &mut unreported_nodes,
                                       &mut |n| {
                                           reported_nodes += n;
                                           reports.send(Report::Progress {
                                                      search_id: search_id,
                                                      searched_nodes: reported_nodes,
                                                      depth: depth,
                                                  })
                                                  .ok();
                                           match commands.try_recv() {
                                               Ok(x) => {
                                                   pending_command = Some(x);
                                                   Err(TerminatedSearch)
                                               }
                                               _ => Ok(()),
                                           }
                                       },
                                       lower_bound,
                                       upper_bound,
                                       depth)
                                    .ok();
                    reported_nodes += unreported_nodes;
                    reports.send(Report::Progress {
                               search_id: search_id,
                               searched_nodes: reported_nodes,
                               depth: depth,
                           })
                           .ok();
                    reports.send(Report::Done {
                               search_id: search_id,
                               searched_nodes: reported_nodes,
                               value: value,
                           })
                           .ok();
                    move_stack.clear();
                }
                Command::Stop => continue,
                Command::Exit => break,
            }
        }
    })
}

// Represents a terminated search condition.
struct TerminatedSearch;

// Helper function for `run()`. It implements the principal variation
// search algorithm. When returning `Err(TerminatedSearch)`, this
// function may leave un-restored move lists in `moves`.
fn search(tt: &TranspositionTable,
          p: &mut Position,
          moves: &mut MoveStack,
          nc: &mut NodeCount,
          report: &mut FnMut(NodeCount) -> Result<(), TerminatedSearch>,
          mut alpha: Value, // lower bound
          beta: Value, // upper bound
          depth: u8)
          -> Result<Value, TerminatedSearch> {
    assert!(alpha < beta);

    // Consult the transposition table.
    let hash_move16 = if let Some(entry) = tt.probe(p.hash()) {
        if entry.depth() >= depth {
            let value = entry.value();
            let bound = entry.bound();
            if (value >= beta && bound == BOUND_LOWER) ||
               (value <= alpha && bound == BOUND_UPPER) || (bound == BOUND_EXACT) {
                return Ok(value);
            }
        }
        entry.move16()
    } else {
        0
    };

    let mut bound_type = BOUND_UPPER;
    let mut move16 = 0;

    if depth == 0 {
        // On leaf nodes, do quiescence search.
        let (value, nodes) = p.evaluate_quiescence(alpha, beta, None);
        *nc += nodes;
        alpha = value;
        bound_type = BOUND_EXACT;
    } else {
        moves.save();

        if hash_move16 != 0 {
            if let Some(m) = p.try_move_digest(hash_move16) {
                moves.push(m);
            }
        }
        let mut generated = false;

        let mut no_moves_yet = true;
        loop {
            // TODO: This is ugly, and probably inefficient.
            let m = match moves.remove_best_move() {
                None if generated => break,
                None => {
                    p.generate_moves(moves);
                    moves.remove_move(hash_move16);
                    generated = true;
                    continue;
                }
                Some(x) => x,
            };

            if p.do_move(m) {
                // From time to time, we report how many nodes had
                // been searched since the last report. This also
                // gives an opportunity for the search to be
                // terminated.
                *nc += 1;
                if *nc > NODE_COUNT_REPORT_INTERVAL {
                    try!(report(*nc));
                    *nc = 0;
                }

                let value = if no_moves_yet {
                    // The first move we analyze with a fully open
                    // window (alpha, beta).
                    -try!(search(tt, p, moves, nc, report, -beta, -alpha, depth - 1))
                } else {
                    // For the next moves we first try to prove that
                    // they are not better than our current best
                    // move. For this purpose we analyze them with a
                    // null window (alpha, alpha + 1). This is faster
                    // than a full window search. Only when we are
                    // certain that the move is better than our
                    // current best move, we do a full-window search.
                    match -try!(search(tt, p, moves, nc, report, -alpha - 1, -alpha, depth - 1)) {
                        x if x <= alpha => x,
                        _ => -try!(search(tt, p, moves, nc, report, -beta, -alpha, depth - 1)),
                    }
                };

                no_moves_yet = false;
                p.undo_move();

                if value >= beta {
                    // This move is too good, so that the opponent
                    // will not allow this line of play to
                    // happen. Therefore we can stop here.
                    alpha = value;
                    bound_type = BOUND_LOWER;
                    move16 = m.digest();
                    break;
                }
                if value > alpha {
                    // We found ourselves a new best move.
                    alpha = value;
                    bound_type = BOUND_EXACT;
                    move16 = m.digest();
                }
            }
        }
        moves.restore();
        if no_moves_yet {
            // No legal moves -- this is a final position.
            alpha = p.evaluate_final();
            bound_type = BOUND_EXACT;
        }
    }

    tt.store(p.hash(),
             EntryData::new(alpha, bound_type, depth, move16, 0));
    Ok(alpha)
}

const NODE_COUNT_REPORT_INTERVAL: NodeCount = 10000;

#[cfg(test)]
mod tests {
    use super::search;
    use chess_move::*;
    use tt::*;
    use position::Position;

    #[test]
    fn test_search() {
        let mut p = Position::from_fen("8/8/8/8/3q3k/7n/6PP/2Q2R1K b - - 0 1").ok().unwrap();
        let value = search(&TranspositionTable::new(),
                           &mut p,
                           &mut MoveStack::new(),
                           &mut 0,
                           &mut |_| Ok(()),
                           -30000,
                           30000,
                           2)
                        .ok()
                        .unwrap();
        assert!(value < -300);
        let value = search(&TranspositionTable::new(),
                           &mut p,
                           &mut MoveStack::new(),
                           &mut 0,
                           &mut |_| Ok(()),
                           -30000,
                           30000,
                           4)
                        .ok()
                        .unwrap();
        assert!(value >= 20000);
    }
}
<|endoftext|>


<commit_before>// Copyright 2014-2015 Sam Doshi (sam@metal-fish.co.uk)
// Copyright 2013-2014 Philippe Delrieu (philippe.delrieu@free.fr)
//
// Licensed under either of
//          Apache License, Version 2.0, (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
//          MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT).
// This file may not be copied, modified, or distributed except according to those terms.

use std::os::raw::{c_char, c_void};
use ffi::types::*;

#[link(name = "portmidi")]
extern "C" {
    pub fn Pm_Initialize() -> PmError;
    pub fn Pm_Terminate() -> PmError;
    pub fn Pm_HasHostError(stream: *const PortMidiStream) -> i32;
    pub fn Pm_GetErrorText(errorCode: PmError) -> *const c_char;
    pub fn Pm_GetHostErrorText(msg: *const c_char, len: i32);
    pub fn Pm_CountDevices() -> i32;
    pub fn Pm_GetDefaultInputDeviceID() -> PmDeviceId;
    pub fn Pm_GetDefaultOutputDeviceID() -> PmDeviceId;
    pub fn Pm_GetDeviceInfo(id: PmDeviceId) -> *const PmDeviceInfo;
    pub fn Pm_OpenInput(stream: *const *const PortMidiStream,
                        inputDevice: PmDeviceId,
                        inputDriverInfo: Option<*const c_void>,
                        bufferSize: i32,
                        time_proc: Option<*const c_void>,
                        time_info: Option<*const c_void>)
                        -> PmError;
    pub fn Pm_OpenOutput(stream: *const *const PortMidiStream,
                         outputDevice: PmDeviceId,
                         inputDriverInfo: Option<*const c_void>,
                         bufferSize: i32,
                         time_proc: Option<*const c_void>,
                         time_info: Option<*const c_void>,
                         latency: i32)
                         -> PmError;
    pub fn Pm_Read(stream: *const PortMidiStream, buffer: *mut PmEvent, length: i32) -> i32;
    pub fn Pm_Abort(stream: *const PortMidiStream) -> PmError;
    pub fn Pm_Close(stream: *const PortMidiStream) -> PmError;
    pub fn Pm_Poll(stream: *const PortMidiStream) -> PmError;
    pub fn Pm_Write(stream: *const PortMidiStream, buffer: *const PmEvent, length: i32) -> PmError;
    pub fn Pm_WriteShort(stream: *const PortMidiStream,
                         timestamp: PmTimestamp,
                         message: PmMessage)
                         -> PmError;
}
<commit_msg>i32 -> c_int<commit_after>// Copyright 2014-2015 Sam Doshi (sam@metal-fish.co.uk)
// Copyright 2013-2014 Philippe Delrieu (philippe.delrieu@free.fr)
//
// Licensed under either of
//          Apache License, Version 2.0, (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
//          MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT).
// This file may not be copied, modified, or distributed except according to those terms.

use std::os::raw::{c_char, c_void, c_int};
use ffi::types::*;

#[link(name = "portmidi")]
extern "C" {
    pub fn Pm_Initialize() -> PmError;
    pub fn Pm_Terminate() -> PmError;
    pub fn Pm_HasHostError(stream: *const PortMidiStream) -> c_int;
    pub fn Pm_GetErrorText(errorCode: PmError) -> *const c_char;
    pub fn Pm_GetHostErrorText(msg: *const c_char, len: c_int);
    pub fn Pm_CountDevices() -> c_int;
    pub fn Pm_GetDefaultInputDeviceID() -> PmDeviceId;
    pub fn Pm_GetDefaultOutputDeviceID() -> PmDeviceId;
    pub fn Pm_GetDeviceInfo(id: PmDeviceId) -> *const PmDeviceInfo;
    pub fn Pm_OpenInput(stream: *const *const PortMidiStream,
                        inputDevice: PmDeviceId,
                        inputDriverInfo: Option<*const c_void>,
                        bufferSize: c_int,
                        time_proc: Option<*const c_void>,
                        time_info: Option<*const c_void>)
                        -> PmError;
    pub fn Pm_OpenOutput(stream: *const *const PortMidiStream,
                         outputDevice: PmDeviceId,
                         inputDriverInfo: Option<*const c_void>,
                         bufferSize: c_int,
                         time_proc: Option<*const c_void>,
                         time_info: Option<*const c_void>,
                         latency: c_int)
                         -> PmError;
    pub fn Pm_Read(stream: *const PortMidiStream, buffer: *mut PmEvent, length: c_int) -> c_int;
    pub fn Pm_Abort(stream: *const PortMidiStream) -> PmError;
    pub fn Pm_Close(stream: *const PortMidiStream) -> PmError;
    pub fn Pm_Poll(stream: *const PortMidiStream) -> PmError;
    pub fn Pm_Write(stream: *const PortMidiStream, buffer: *const PmEvent, length: c_int) -> PmError;
    pub fn Pm_WriteShort(stream: *const PortMidiStream,
                         timestamp: PmTimestamp,
                         message: PmMessage)
                         -> PmError;
}
<|endoftext|>


<commit_before>// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use std::ffi::CString;
use std::mem::MaybeUninit;
use std::os::windows::io::AsRawHandle;
use std::os::windows::io::RawHandle;
use std::ptr::null;
use std::time::Duration;

use serde::Deserialize;
use serde::Serialize;
use win_util::SecurityAttributes;
use win_util::SelfRelativeSecurityDescriptor;
use winapi::shared::minwindef::DWORD;
use winapi::shared::minwindef::FALSE;
use winapi::shared::minwindef::TRUE;
use winapi::shared::winerror::WAIT_TIMEOUT;
use winapi::um::handleapi::DuplicateHandle;
use winapi::um::processthreadsapi::GetCurrentProcess;
use winapi::um::synchapi::CreateEventA;
use winapi::um::synchapi::OpenEventA;
use winapi::um::synchapi::ResetEvent;
use winapi::um::synchapi::SetEvent;
use winapi::um::synchapi::WaitForSingleObject;
use winapi::um::winbase::WAIT_FAILED;
use winapi::um::winnt::DUPLICATE_SAME_ACCESS;
use winapi::um::winnt::EVENT_MODIFY_STATE;
use winapi::um::winnt::HANDLE;

use super::errno_result;
use super::Error;
use super::RawDescriptor;
use super::Result;
use crate::descriptor::AsRawDescriptor;
use crate::descriptor::FromRawDescriptor;
use crate::descriptor::IntoRawDescriptor;
use crate::descriptor::SafeDescriptor;
use crate::Event;
use crate::EventWaitResult;

/// A safe wrapper around Windows synchapi methods used to mimic Linux eventfd (man 2 eventfd).
/// Since the eventfd isn't using "EFD_SEMAPHORE", we don't need to keep count so we can just use
/// events.
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(transparent)]
pub(crate) struct PlatformEvent {
    event_handle: SafeDescriptor,
}

pub trait EventExt {
    fn new_auto_reset() -> Result<Event>;
    fn open(name: &str) -> Result<Event>;
    fn create_event_with_name(name: &str) -> Result<Event>;
}

impl EventExt for Event {
    fn new_auto_reset() -> Result<Event> {
        PlatformEvent::new_with_manual_reset(false).map(Event)
    }

    fn open(name: &str) -> Result<Event> {
        PlatformEvent::open(name).map(Event)
    }

    fn create_event_with_name(name: &str) -> Result<Event> {
        PlatformEvent::create_event_with_name(name).map(Event)
    }
}

impl PlatformEvent {
    pub fn new_with_manual_reset(manual_reset: bool) -> Result<PlatformEvent> {
        let handle = unsafe {
            CreateEventA(
                SecurityAttributes::new_with_security_descriptor(
                    SelfRelativeSecurityDescriptor::get_singleton(),
                    /* inherit= */ false,
                )
                .as_mut(),
                if manual_reset { TRUE } else { FALSE },
                FALSE, // initial state = unsignalled
                null(),
            )
        };
        if handle.is_null() {
            return errno_result();
        }
        Ok(PlatformEvent {
            event_handle: unsafe { SafeDescriptor::from_raw_descriptor(handle) },
        })
    }

    pub fn create_event_with_name(name: &str) -> Result<PlatformEvent> {
        let event_str = CString::new(String::from(name)).unwrap();
        let handle = unsafe {
            CreateEventA(
                SecurityAttributes::new_with_security_descriptor(
                    SelfRelativeSecurityDescriptor::get_singleton(),
                    /* inherit= */ false,
                )
                .as_mut(),
                FALSE, // manual_reset = false
                FALSE, // initial state = unsignalled
                event_str.as_ptr(),
            )
        };
        if handle.is_null() {
            return errno_result();
        }
        Ok(PlatformEvent {
            event_handle: unsafe { SafeDescriptor::from_raw_descriptor(handle) },
        })
    }

    pub fn new() -> Result<PlatformEvent> {
        // Require manual reset
        PlatformEvent::new_with_manual_reset(true)
    }

    pub fn open(name: &str) -> Result<PlatformEvent> {
        let event_str = CString::new(String::from(name)).unwrap();
        let handle = unsafe { OpenEventA(EVENT_MODIFY_STATE, FALSE, event_str.as_ptr()) };
        if handle.is_null() {
            return errno_result();
        }
        Ok(PlatformEvent {
            event_handle: unsafe { SafeDescriptor::from_raw_descriptor(handle) },
        })
    }

    pub fn new_auto_reset() -> Result<PlatformEvent> {
        PlatformEvent::new_with_manual_reset(false)
    }

    /// See `Event::signal`.
    pub fn signal(&self) -> Result<()> {
        let event_result = unsafe { SetEvent(self.event_handle.as_raw_descriptor()) };
        if event_result == 0 {
            return errno_result();
        }
        Ok(())
    }

    /// See `Event::wait`.
    pub fn wait(&self) -> Result<()> {
        let read_result = self.wait_timeout(Duration::new(std::i64::MAX as u64, 0));
        match read_result {
            Ok(EventWaitResult::Signaled) => Ok(()),
            Ok(EventWaitResult::TimedOut) => Err(Error::new(WAIT_TIMEOUT)),
            Err(e) => Err(e),
        }
    }

    pub fn reset(&self) -> Result<()> {
        let res = unsafe { ResetEvent(self.event_handle.as_raw_descriptor()) };
        if res == 0 {
            errno_result()
        } else {
            Ok(())
        }
    }

    /// See `Event::wait_timeout`.
    pub fn wait_timeout(&self, timeout: Duration) -> Result<EventWaitResult> {
        let wait_result = unsafe {
            WaitForSingleObject(
                self.event_handle.as_raw_descriptor(),
                timeout.as_millis() as DWORD,
            )
        };

        // We are using an infinite timeout so we can ignore WAIT_ABANDONED
        match wait_result {
            WAIT_FAILED => errno_result(),
            WAIT_TIMEOUT => Ok(EventWaitResult::TimedOut),
            _ => {
                // Safe because self manages the handle and we know it was valid as it
                // was just successfully waited upon. It is safe to reset a non manual reset event as well.
                match unsafe { ResetEvent(self.event_handle.as_raw_descriptor()) } {
                    0 => errno_result(),
                    _ => Ok(EventWaitResult::Signaled),
                }
            }
        }
    }

    pub fn try_clone(&self) -> Result<PlatformEvent> {
        let mut event_clone: HANDLE = MaybeUninit::uninit().as_mut_ptr();
        let duplicate_result = unsafe {
            DuplicateHandle(
                GetCurrentProcess(),
                self.event_handle.as_raw_descriptor(),
                GetCurrentProcess(),
                &mut event_clone,
                0,
                0,
                DUPLICATE_SAME_ACCESS,
            )
        };
        if duplicate_result == 0 {
            return errno_result();
        }
        Ok(unsafe { PlatformEvent::from_raw_descriptor(event_clone) })
    }
}

impl AsRawDescriptor for PlatformEvent {
    fn as_raw_descriptor(&self) -> RawDescriptor {
        self.event_handle.as_raw_descriptor()
    }
}

impl FromRawDescriptor for PlatformEvent {
    unsafe fn from_raw_descriptor(descriptor: RawDescriptor) -> Self {
        PlatformEvent {
            event_handle: SafeDescriptor::from_raw_descriptor(descriptor),
        }
    }
}

impl AsRawHandle for PlatformEvent {
    fn as_raw_handle(&self) -> RawHandle {
        self.as_raw_descriptor()
    }
}

impl IntoRawDescriptor for PlatformEvent {
    fn into_raw_descriptor(self) -> RawDescriptor {
        self.event_handle.into_raw_descriptor()
    }
}

impl From<PlatformEvent> for SafeDescriptor {
    fn from(evt: PlatformEvent) -> Self {
        evt.event_handle
    }
}

// PlatformEvent is safe for send & Sync despite containing a raw handle to its
// file mapping object. As long as the instance to PlatformEvent stays alive, this
// pointer will be a valid handle.
unsafe impl Send for PlatformEvent {}
unsafe impl Sync for PlatformEvent {}

#[cfg(test)]
mod tests {
    use winapi::shared::winerror::WAIT_TIMEOUT;
    use winapi::um::winbase::INFINITE;
    use winapi::um::winbase::WAIT_OBJECT_0;

    use super::*;

    #[test]
    fn new() {
        PlatformEvent::new().unwrap();
    }

    #[test]
    fn read_write() {
        let evt = PlatformEvent::new().unwrap();
        evt.signal().unwrap();
        assert_eq!(evt.wait(), Ok(()));
    }

    #[test]
    fn read_write_auto_reset() {
        let evt = PlatformEvent::new_auto_reset().unwrap();
        evt.signal().unwrap();

        // Wait for the notification.
        let result = unsafe { WaitForSingleObject(evt.as_raw_descriptor(), INFINITE) };
        assert_eq!(result, WAIT_OBJECT_0);

        // The notification should have reset since we already received it.
        let result = unsafe { WaitForSingleObject(evt.as_raw_descriptor(), 0) };
        assert_eq!(result, WAIT_TIMEOUT);
    }

    #[test]
    fn read_write_notifies_until_read() {
        let evt = PlatformEvent::new().unwrap();
        evt.signal().unwrap();

        // Wait for the notification.
        let result = unsafe { WaitForSingleObject(evt.as_raw_descriptor(), INFINITE) };
        assert_eq!(result, WAIT_OBJECT_0);

        // The notification should still be active because read wasn't called.
        let result = unsafe { WaitForSingleObject(evt.as_raw_descriptor(), 0) };
        assert_eq!(result, WAIT_OBJECT_0);

        // Read and ensure the notification has cleared.
        evt.wait().expect("Failed to read event.");
        let result = unsafe { WaitForSingleObject(evt.as_raw_descriptor(), 0) };
        assert_eq!(result, WAIT_TIMEOUT);
    }

    #[test]
    fn clone() {
        let evt = PlatformEvent::new().unwrap();
        let evt_clone = evt.try_clone().unwrap();
        evt.signal().unwrap();
        assert_eq!(evt_clone.wait(), Ok(()));
    }

    #[test]
    fn timeout() {
        let evt = PlatformEvent::new().expect("failed to create event");
        assert_eq!(
            evt.wait_timeout(Duration::from_millis(1))
                .expect("failed to read from event with timeout"),
            EventWaitResult::TimedOut
        );
    }
}
<commit_msg>base: windows: make Event wait() actually infinite<commit_after>// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use std::ffi::CString;
use std::mem::MaybeUninit;
use std::os::windows::io::AsRawHandle;
use std::os::windows::io::RawHandle;
use std::ptr::null;
use std::time::Duration;

use serde::Deserialize;
use serde::Serialize;
use win_util::SecurityAttributes;
use win_util::SelfRelativeSecurityDescriptor;
use winapi::shared::minwindef::DWORD;
use winapi::shared::minwindef::FALSE;
use winapi::shared::minwindef::TRUE;
use winapi::shared::winerror::WAIT_TIMEOUT;
use winapi::um::handleapi::DuplicateHandle;
use winapi::um::processthreadsapi::GetCurrentProcess;
use winapi::um::synchapi::CreateEventA;
use winapi::um::synchapi::OpenEventA;
use winapi::um::synchapi::ResetEvent;
use winapi::um::synchapi::SetEvent;
use winapi::um::synchapi::WaitForSingleObject;
use winapi::um::winbase::INFINITE;
use winapi::um::winbase::WAIT_FAILED;
use winapi::um::winbase::WAIT_OBJECT_0;
use winapi::um::winnt::DUPLICATE_SAME_ACCESS;
use winapi::um::winnt::EVENT_MODIFY_STATE;
use winapi::um::winnt::HANDLE;

use super::errno_result;
use super::Error;
use super::RawDescriptor;
use super::Result;
use crate::descriptor::AsRawDescriptor;
use crate::descriptor::FromRawDescriptor;
use crate::descriptor::IntoRawDescriptor;
use crate::descriptor::SafeDescriptor;
use crate::Event;
use crate::EventWaitResult;

/// A safe wrapper around Windows synchapi methods used to mimic Linux eventfd (man 2 eventfd).
/// Since the eventfd isn't using "EFD_SEMAPHORE", we don't need to keep count so we can just use
/// events.
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(transparent)]
pub(crate) struct PlatformEvent {
    event_handle: SafeDescriptor,
}

pub trait EventExt {
    fn new_auto_reset() -> Result<Event>;
    fn open(name: &str) -> Result<Event>;
    fn create_event_with_name(name: &str) -> Result<Event>;
}

impl EventExt for Event {
    fn new_auto_reset() -> Result<Event> {
        PlatformEvent::new_with_manual_reset(false).map(Event)
    }

    fn open(name: &str) -> Result<Event> {
        PlatformEvent::open(name).map(Event)
    }

    fn create_event_with_name(name: &str) -> Result<Event> {
        PlatformEvent::create_event_with_name(name).map(Event)
    }
}

impl PlatformEvent {
    pub fn new_with_manual_reset(manual_reset: bool) -> Result<PlatformEvent> {
        let handle = unsafe {
            CreateEventA(
                SecurityAttributes::new_with_security_descriptor(
                    SelfRelativeSecurityDescriptor::get_singleton(),
                    /* inherit= */ false,
                )
                .as_mut(),
                if manual_reset { TRUE } else { FALSE },
                FALSE, // initial state = unsignalled
                null(),
            )
        };
        if handle.is_null() {
            return errno_result();
        }
        Ok(PlatformEvent {
            event_handle: unsafe { SafeDescriptor::from_raw_descriptor(handle) },
        })
    }

    pub fn create_event_with_name(name: &str) -> Result<PlatformEvent> {
        let event_str = CString::new(String::from(name)).unwrap();
        let handle = unsafe {
            CreateEventA(
                SecurityAttributes::new_with_security_descriptor(
                    SelfRelativeSecurityDescriptor::get_singleton(),
                    /* inherit= */ false,
                )
                .as_mut(),
                FALSE, // manual_reset = false
                FALSE, // initial state = unsignalled
                event_str.as_ptr(),
            )
        };
        if handle.is_null() {
            return errno_result();
        }
        Ok(PlatformEvent {
            event_handle: unsafe { SafeDescriptor::from_raw_descriptor(handle) },
        })
    }

    pub fn new() -> Result<PlatformEvent> {
        // Require manual reset
        PlatformEvent::new_with_manual_reset(true)
    }

    pub fn open(name: &str) -> Result<PlatformEvent> {
        let event_str = CString::new(String::from(name)).unwrap();
        let handle = unsafe { OpenEventA(EVENT_MODIFY_STATE, FALSE, event_str.as_ptr()) };
        if handle.is_null() {
            return errno_result();
        }
        Ok(PlatformEvent {
            event_handle: unsafe { SafeDescriptor::from_raw_descriptor(handle) },
        })
    }

    pub fn new_auto_reset() -> Result<PlatformEvent> {
        PlatformEvent::new_with_manual_reset(false)
    }

    /// See `Event::signal`.
    pub fn signal(&self) -> Result<()> {
        let event_result = unsafe { SetEvent(self.event_handle.as_raw_descriptor()) };
        if event_result == 0 {
            return errno_result();
        }
        Ok(())
    }

    pub fn reset(&self) -> Result<()> {
        let res = unsafe { ResetEvent(self.event_handle.as_raw_descriptor()) };
        if res == 0 {
            errno_result()
        } else {
            Ok(())
        }
    }

    /// Wait for the event with an optional timeout and reset the event if it was signaled.
    fn wait_and_reset(&self, timeout: Option<Duration>) -> Result<EventWaitResult> {
        let milliseconds = match timeout {
            Some(timeout) => timeout.as_millis() as DWORD,
            None => INFINITE,
        };

        // Safe because we pass an event object handle owned by this PlatformEvent.
        let wait_result = match unsafe {
            WaitForSingleObject(self.event_handle.as_raw_descriptor(), milliseconds)
        } {
            WAIT_OBJECT_0 => Ok(EventWaitResult::Signaled),
            WAIT_TIMEOUT => Ok(EventWaitResult::TimedOut),
            WAIT_FAILED => errno_result(),
            other => Err(Error::new(other)),
        }?;

        if wait_result == EventWaitResult::Signaled {
            self.reset()?;
        }

        Ok(wait_result)
    }

    /// See `Event::wait`.
    pub fn wait(&self) -> Result<()> {
        let wait_result = self.wait_and_reset(None)?;
        // Waiting with `INFINITE` can only return when the event is signaled or an error occurs.
        // `EventWaitResult::TimedOut` is not valid here.
        assert_eq!(wait_result, EventWaitResult::Signaled);
        Ok(())
    }

    /// See `Event::wait_timeout`.
    pub fn wait_timeout(&self, timeout: Duration) -> Result<EventWaitResult> {
        self.wait_and_reset(Some(timeout))
    }

    pub fn try_clone(&self) -> Result<PlatformEvent> {
        let mut event_clone: HANDLE = MaybeUninit::uninit().as_mut_ptr();
        let duplicate_result = unsafe {
            DuplicateHandle(
                GetCurrentProcess(),
                self.event_handle.as_raw_descriptor(),
                GetCurrentProcess(),
                &mut event_clone,
                0,
                0,
                DUPLICATE_SAME_ACCESS,
            )
        };
        if duplicate_result == 0 {
            return errno_result();
        }
        Ok(unsafe { PlatformEvent::from_raw_descriptor(event_clone) })
    }
}

impl AsRawDescriptor for PlatformEvent {
    fn as_raw_descriptor(&self) -> RawDescriptor {
        self.event_handle.as_raw_descriptor()
    }
}

impl FromRawDescriptor for PlatformEvent {
    unsafe fn from_raw_descriptor(descriptor: RawDescriptor) -> Self {
        PlatformEvent {
            event_handle: SafeDescriptor::from_raw_descriptor(descriptor),
        }
    }
}

impl AsRawHandle for PlatformEvent {
    fn as_raw_handle(&self) -> RawHandle {
        self.as_raw_descriptor()
    }
}

impl IntoRawDescriptor for PlatformEvent {
    fn into_raw_descriptor(self) -> RawDescriptor {
        self.event_handle.into_raw_descriptor()
    }
}

impl From<PlatformEvent> for SafeDescriptor {
    fn from(evt: PlatformEvent) -> Self {
        evt.event_handle
    }
}

// PlatformEvent is safe for send & Sync despite containing a raw handle to its
// file mapping object. As long as the instance to PlatformEvent stays alive, this
// pointer will be a valid handle.
unsafe impl Send for PlatformEvent {}
unsafe impl Sync for PlatformEvent {}

#[cfg(test)]
mod tests {
    use winapi::shared::winerror::WAIT_TIMEOUT;
    use winapi::um::winbase::INFINITE;
    use winapi::um::winbase::WAIT_OBJECT_0;

    use super::*;

    #[test]
    fn new() {
        PlatformEvent::new().unwrap();
    }

    #[test]
    fn read_write() {
        let evt = PlatformEvent::new().unwrap();
        evt.signal().unwrap();
        assert_eq!(evt.wait(), Ok(()));
    }

    #[test]
    fn read_write_auto_reset() {
        let evt = PlatformEvent::new_auto_reset().unwrap();
        evt.signal().unwrap();

        // Wait for the notification.
        let result = unsafe { WaitForSingleObject(evt.as_raw_descriptor(), INFINITE) };
        assert_eq!(result, WAIT_OBJECT_0);

        // The notification should have reset since we already received it.
        let result = unsafe { WaitForSingleObject(evt.as_raw_descriptor(), 0) };
        assert_eq!(result, WAIT_TIMEOUT);
    }

    #[test]
    fn read_write_notifies_until_read() {
        let evt = PlatformEvent::new().unwrap();
        evt.signal().unwrap();

        // Wait for the notification.
        let result = unsafe { WaitForSingleObject(evt.as_raw_descriptor(), INFINITE) };
        assert_eq!(result, WAIT_OBJECT_0);

        // The notification should still be active because read wasn't called.
        let result = unsafe { WaitForSingleObject(evt.as_raw_descriptor(), 0) };
        assert_eq!(result, WAIT_OBJECT_0);

        // Read and ensure the notification has cleared.
        evt.wait().expect("Failed to read event.");
        let result = unsafe { WaitForSingleObject(evt.as_raw_descriptor(), 0) };
        assert_eq!(result, WAIT_TIMEOUT);
    }

    #[test]
    fn clone() {
        let evt = PlatformEvent::new().unwrap();
        let evt_clone = evt.try_clone().unwrap();
        evt.signal().unwrap();
        assert_eq!(evt_clone.wait(), Ok(()));
    }

    #[test]
    fn timeout() {
        let evt = PlatformEvent::new().expect("failed to create event");
        assert_eq!(
            evt.wait_timeout(Duration::from_millis(1))
                .expect("failed to read from event with timeout"),
            EventWaitResult::TimedOut
        );
    }
}
<|endoftext|>


<commit_before>
#[macro_use]
extern crate twelve_bit;

use twelve_bit::u12::*;

// MARK: - Tests - Addition

#[test]
fn test_add_operator() {
  assert_eq!(u12![0] + u12![0], u12![0]);
  assert_eq!(u12![0] + u12![1], u12![1]);
  assert_eq!(u12![1] + u12![0], u12![1]);

  assert_eq!(u12![0] + U12::max_value(), U12::max_value());
  assert_eq!(U12::max_value() + u12![0], U12::max_value());
}

#[test]
#[should_panic]
fn test_add_operator_overflow() {
  let _ = U12::max_value() + u12![1];
}

#[test]
fn test_checked_add() {
  let zero = U12::min_value();
  let  one = U12::from(1u8);
  let  two = U12::from(2u8);
  let  max = U12::max_value();

  assert_eq!(zero.checked_add(zero), Some(zero));
  assert_eq!(zero.checked_add(one), Some(one));
  assert_eq!(one.checked_add(zero), Some(one));
  assert_eq!(zero.checked_add(max), Some(max));
  assert_eq!(max.checked_add(zero), Some(max));
  assert_eq!(max.checked_add(one), None);
  assert_eq!(max.checked_add(two), None);
  assert_eq!(max.checked_add(max), None);
}

#[test]
fn test_saturating_add() {
  let zero = U12::min_value();
  let  one = U12::from(1u8);
  let  two = U12::from(2u8);
  let  max = U12::max_value();

  assert_eq!(zero.saturating_add(zero), zero);
  assert_eq!(zero.saturating_add(one), one);
  assert_eq!(one.saturating_add(zero), one);
  assert_eq!(zero.saturating_add(max), max);
  assert_eq!(max.saturating_add(zero), max);
  assert_eq!(max.saturating_add(one), max);
  assert_eq!(max.saturating_add(two), max);
  assert_eq!(max.saturating_add(max), max);
}

#[test]
fn test_wrapping_add() {
  let zero = U12::min_value();
  let  one = U12::from(1u8);
  let  two = U12::from(2u8);
  let  max = U12::max_value();

  assert_eq!(zero.wrapping_add(zero), zero);
  assert_eq!(zero.wrapping_add(one), one);
  assert_eq!(one.wrapping_add(zero), one);
  assert_eq!(zero.wrapping_add(max), max);
  assert_eq!(max.wrapping_add(zero), max);
  assert_eq!(max.wrapping_add(one), zero);
  assert_eq!(max.wrapping_add(two), one);
  assert_eq!(max.wrapping_add(max), 0xFFEu16.unchecked_into());
}

#[test]
fn test_overflowing_add() {
  let zero = U12::min_value();
  let  one = U12::from(1u8);
  let  two = U12::from(2u8);
  let  max = U12::max_value();

  assert_eq!(zero.overflowing_add(zero), (zero, false));
  assert_eq!(zero.overflowing_add(one), (one, false));
  assert_eq!(one.overflowing_add(zero), (one, false));
  assert_eq!(zero.overflowing_add(max), (max, false));
  assert_eq!(max.overflowing_add(zero), (max, false));
  assert_eq!(max.overflowing_add(one), (zero, true));
  assert_eq!(max.overflowing_add(two), (one, true));
  assert_eq!(max.overflowing_add(max), (0xFFEu16.unchecked_into(), true));
}

// MARK: - Tests - Subtraction

#[test]
fn test_sub_operator() {
  let zero = U12::min_value();
  let  one = U12::from(1u8);
  let  max = U12::max_value();

  assert_eq!(zero - zero, zero);
  assert_eq!(one - zero, one);
  assert_eq!(max - zero, max);
  assert_eq!(max - one, (0xFFE as u16).unchecked_into());
}

#[test]
#[should_panic]
fn test_sub_operator_underflow() {
  let one = U12::from(1u8);
  let min = U12::min_value();
  let _ = min - one;
}

#[test]
fn test_checked_sub() {
  let zero = U12::min_value();
  let  one = U12::from(1u8);
  let  two = U12::from(2u8);
  let  max = U12::max_value();

  assert_eq!(zero.checked_sub(zero), Some(zero));
  assert_eq!(zero.checked_sub(one), None);
  assert_eq!(one.checked_sub(zero), Some(one));
  assert_eq!(zero.checked_sub(max), None);
  assert_eq!(max.checked_sub(zero), Some(max));
  assert_eq!(max.checked_sub(one), Some((0xFFE as u16).unchecked_into()));
  assert_eq!(max.checked_sub(two), Some((0xFFD as u16).unchecked_into()));
  assert_eq!(max.checked_sub(max), Some(zero));
}

#[test]
fn test_saturating_sub() {
  let zero = U12::min_value();
  let  one = U12::from(1u8);
  let  two = U12::from(2u8);
  let  max = U12::max_value();

  assert_eq!(zero.saturating_sub(zero), zero);
  assert_eq!(zero.saturating_sub(one), zero);
  assert_eq!(one.saturating_sub(zero), one);
  assert_eq!(zero.saturating_sub(max), zero);
  assert_eq!(max.saturating_sub(zero), max);
  assert_eq!(max.saturating_sub(one), (0xFFE as u16).unchecked_into());
  assert_eq!(max.saturating_sub(two), (0xFFD as u16).unchecked_into());
  assert_eq!(max.saturating_sub(max), zero);
}

#[test]
fn test_wrapping_sub() {
  let zero = U12::min_value();
  let  one = U12::from(1u8);
  let  two = U12::from(2u8);
  let  max = U12::max_value();

  assert_eq!(zero.wrapping_sub(zero), zero);
  assert_eq!(zero.wrapping_sub(one), max);
  assert_eq!(one.saturating_sub(zero), one);
  assert_eq!(zero.wrapping_sub(max), one);
  assert_eq!(max.wrapping_sub(zero), max);
  assert_eq!(max.wrapping_sub(one), (0xFFE as u16).unchecked_into());
  assert_eq!(max.wrapping_sub(two), (0xFFD as u16).unchecked_into());
  assert_eq!(max.wrapping_sub(max), zero);
}

// MARK: - Tests - Multiplication

#[test]
fn test_mul_operator() {
  assert_eq!(U12::from(2u8) * U12::from(0u8), U12::min_value());
  assert_eq!(U12::max_value() * U12::min_value(), U12::min_value());
  assert_eq!(U12::from(2u8) * U12::from(255u8), (510 as u16).unchecked_into());
  assert_eq!(U12::from(255u8) * U12::from(2u8), (510 as u16).unchecked_into());
}

#[test]
#[should_panic]
fn test_mul_operator_overflow() {
  let _ = U12::from(2u8) * 2048u16.unchecked_into();
}

#[test]
fn test_checked_mul() {
  assert_eq!(U12::from(2u8).checked_mul(0u8.into()), Some(U12::min_value()));
  assert_eq!(U12::from(2u8).checked_mul(255u8.into()), Some((510 as u16).unchecked_into()));
  assert_eq!(U12::from(255u8).checked_mul(2u8.into()), Some((510 as u16).unchecked_into()));
  assert_eq!(U12::from(2u8).checked_mul((2048u16).unchecked_into()), None);
  assert_eq!(U12::from(2u8).checked_mul((4095u16).unchecked_into()), None);
}

#[test]
fn test_saturating_mul() {
  assert_eq!(U12::from(2u8).saturating_mul(0u8.into()), U12::min_value());
  assert_eq!(U12::from(2u8).saturating_mul(255u8.into()), (510 as u16).unchecked_into());
  assert_eq!(U12::from(255u8).saturating_mul(2u8.into()), (510 as u16).unchecked_into());
  assert_eq!(U12::from(2u8).saturating_mul((2048u16).unchecked_into()), U12::max_value());
  assert_eq!(U12::from(2u8).saturating_mul((4095u16).unchecked_into()), U12::max_value());
}

#[test]
fn test_wrapping_mul() {
  assert_eq!(U12::from(2u8).wrapping_mul(0u8.into()), U12::min_value());
  assert_eq!(U12::from(2u8).wrapping_mul(255u8.into()), (510 as u16).unchecked_into());
  assert_eq!(U12::from(255u8).wrapping_mul(2u8.into()), (510 as u16).unchecked_into());
  assert_eq!(U12::from(2u8).wrapping_mul((2048u16).unchecked_into()), U12::min_value());
  assert_eq!(U12::from(2u8).wrapping_mul((4095u16).unchecked_into()), (0xFFE as u16).unchecked_into());
}

// MARK: - Tests - Division

#[test]
fn test_div_operator() {
  assert_eq!(U12::max_value() / U12::max_value(), U12::from(1u8));
  assert_eq!(U12::from(2u8) / U12::from(255u8), U12::from(0u8));
  assert_eq!(U12::from(255u8) / U12::from(2u8), U12::from(127u8));
}

#[test]
#[should_panic]
fn test_div_operator_divide_by_zero() {
  let _ = U12::from(2u8) / U12::from(0u8);
}

#[test]
fn test_checked_div() {
  assert_eq!(U12::max_value().checked_div(U12::max_value()), Some(U12::from(1u8)));
  assert_eq!(U12::from(2u8).checked_div(U12::from(255u8)), Some(U12::from(0u8)));
  assert_eq!(U12::from(255u8).checked_div(U12::from(2u8)), Some(U12::from(127u8)));
  assert_eq!(U12::from(255u8).checked_div(U12::from(1u8)), Some(U12::from(255u8)));
  assert_eq!(U12::from(255u8).checked_div(U12::from(0u8)), None);
}

#[test]
fn test_wrapping_div() {
  assert_eq!(U12::max_value().wrapping_div(U12::max_value()), U12::from(1u8));
  assert_eq!(U12::from(2u8).wrapping_div(U12::from(255u8)), U12::from(0u8));
  assert_eq!(U12::from(255u8).wrapping_div(U12::from(2u8)), U12::from(127u8));
  assert_eq!(U12::from(255u8).wrapping_div(U12::from(1u8)), U12::from(255u8));
}

#[test]
#[should_panic]
fn test_wrapping_div_overflow() {
  let _ = U12::from(255u8).wrapping_div(U12::from(0u8));
}

// MARK: - Tests - Negation

#[test]
fn test_checked_neg() {
  assert_eq!(U12::from(0u8).checked_neg(), Some(0u8.into()));
  assert_eq!(U12::from(1u8).checked_neg(), None);
  assert_eq!(U12::from(2u8).checked_neg(), None);
  assert_eq!(U12::from(255u8).checked_neg(), None);
}

#[test]
fn test_wrapping_neg() {
  assert_eq!(U12::from(0u8).wrapping_neg(), 0u8.into());
  assert_eq!(U12::from(1u8).wrapping_neg(), 0xFFFu16.unchecked_into());
  assert_eq!(U12::from(2u8).wrapping_neg(), 0xFFEu16.unchecked_into());
  assert_eq!(U12::from(255u8).wrapping_neg(), 0xF01u16.unchecked_into());
}

#[test]
fn test_overflowing_neg() {
  assert_eq!(U12::from(0u8).overflowing_neg(), (0u8.into(), false));
  assert_eq!(U12::from(1u8).overflowing_neg(), (0xFFFu16.unchecked_into(), true));
  assert_eq!(U12::from(2u8).overflowing_neg(), (0xFFEu16.unchecked_into(), true));
  assert_eq!(U12::from(255u8).overflowing_neg(), (0xF01u16.unchecked_into(), true));
}
<commit_msg>Converted some more tests over.<commit_after>
#[macro_use]
extern crate twelve_bit;

use twelve_bit::u12::*;

// MARK: - Tests - Addition

#[test]
fn test_add_operator() {
  assert_eq!(u12![0] + u12![0], u12![0]);
  assert_eq!(u12![0] + u12![1], u12![1]);
  assert_eq!(u12![1] + u12![0], u12![1]);

  assert_eq!(u12![0] + U12::max_value(), U12::max_value());
  assert_eq!(U12::max_value() + u12![0], U12::max_value());
}

#[test]
#[should_panic]
fn test_add_operator_overflow() {
  let _ = U12::max_value() + u12![1];
}

#[test]
fn test_checked_add() {
  assert_eq!(u12![0].checked_add(u12![0]), Some(u12![0]));
  assert_eq!(u12![0].checked_add(u12![1]), Some(u12![1]));
  assert_eq!(u12![1].checked_add(u12![0]), Some(u12![1]));
  assert_eq!(u12![0].checked_add(U12::max_value()), Some(U12::max_value()));
  assert_eq!(U12::max_value().checked_add(u12![0]), Some(U12::max_value()));
  assert_eq!(U12::max_value().checked_add(u12![1]), None);
  assert_eq!(U12::max_value().checked_add(u12![2]), None);
  assert_eq!(U12::max_value().checked_add(U12::max_value()), None);
}

#[test]
fn test_saturating_add() {
  let zero = U12::min_value();
  let  one = U12::from(1u8);
  let  two = U12::from(2u8);
  let  max = U12::max_value();

  assert_eq!(zero.saturating_add(zero), zero);
  assert_eq!(zero.saturating_add(one), one);
  assert_eq!(one.saturating_add(zero), one);
  assert_eq!(zero.saturating_add(max), max);
  assert_eq!(max.saturating_add(zero), max);
  assert_eq!(max.saturating_add(one), max);
  assert_eq!(max.saturating_add(two), max);
  assert_eq!(max.saturating_add(max), max);
}

#[test]
fn test_wrapping_add() {
  let zero = U12::min_value();
  let  one = U12::from(1u8);
  let  two = U12::from(2u8);
  let  max = U12::max_value();

  assert_eq!(zero.wrapping_add(zero), zero);
  assert_eq!(zero.wrapping_add(one), one);
  assert_eq!(one.wrapping_add(zero), one);
  assert_eq!(zero.wrapping_add(max), max);
  assert_eq!(max.wrapping_add(zero), max);
  assert_eq!(max.wrapping_add(one), zero);
  assert_eq!(max.wrapping_add(two), one);
  assert_eq!(max.wrapping_add(max), 0xFFEu16.unchecked_into());
}

#[test]
fn test_overflowing_add() {
  let zero = U12::min_value();
  let  one = U12::from(1u8);
  let  two = U12::from(2u8);
  let  max = U12::max_value();

  assert_eq!(zero.overflowing_add(zero), (zero, false));
  assert_eq!(zero.overflowing_add(one), (one, false));
  assert_eq!(one.overflowing_add(zero), (one, false));
  assert_eq!(zero.overflowing_add(max), (max, false));
  assert_eq!(max.overflowing_add(zero), (max, false));
  assert_eq!(max.overflowing_add(one), (zero, true));
  assert_eq!(max.overflowing_add(two), (one, true));
  assert_eq!(max.overflowing_add(max), (0xFFEu16.unchecked_into(), true));
}

// MARK: - Tests - Subtraction

#[test]
fn test_sub_operator() {
  let zero = U12::min_value();
  let  one = U12::from(1u8);
  let  max = U12::max_value();

  assert_eq!(zero - zero, zero);
  assert_eq!(one - zero, one);
  assert_eq!(max - zero, max);
  assert_eq!(max - one, (0xFFE as u16).unchecked_into());
}

#[test]
#[should_panic]
fn test_sub_operator_underflow() {
  let one = U12::from(1u8);
  let min = U12::min_value();
  let _ = min - one;
}

#[test]
fn test_checked_sub() {
  let zero = U12::min_value();
  let  one = U12::from(1u8);
  let  two = U12::from(2u8);
  let  max = U12::max_value();

  assert_eq!(zero.checked_sub(zero), Some(zero));
  assert_eq!(zero.checked_sub(one), None);
  assert_eq!(one.checked_sub(zero), Some(one));
  assert_eq!(zero.checked_sub(max), None);
  assert_eq!(max.checked_sub(zero), Some(max));
  assert_eq!(max.checked_sub(one), Some((0xFFE as u16).unchecked_into()));
  assert_eq!(max.checked_sub(two), Some((0xFFD as u16).unchecked_into()));
  assert_eq!(max.checked_sub(max), Some(zero));
}

#[test]
fn test_saturating_sub() {
  let zero = U12::min_value();
  let  one = U12::from(1u8);
  let  two = U12::from(2u8);
  let  max = U12::max_value();

  assert_eq!(zero.saturating_sub(zero), zero);
  assert_eq!(zero.saturating_sub(one), zero);
  assert_eq!(one.saturating_sub(zero), one);
  assert_eq!(zero.saturating_sub(max), zero);
  assert_eq!(max.saturating_sub(zero), max);
  assert_eq!(max.saturating_sub(one), (0xFFE as u16).unchecked_into());
  assert_eq!(max.saturating_sub(two), (0xFFD as u16).unchecked_into());
  assert_eq!(max.saturating_sub(max), zero);
}

#[test]
fn test_wrapping_sub() {
  let zero = U12::min_value();
  let  one = U12::from(1u8);
  let  two = U12::from(2u8);
  let  max = U12::max_value();

  assert_eq!(zero.wrapping_sub(zero), zero);
  assert_eq!(zero.wrapping_sub(one), max);
  assert_eq!(one.saturating_sub(zero), one);
  assert_eq!(zero.wrapping_sub(max), one);
  assert_eq!(max.wrapping_sub(zero), max);
  assert_eq!(max.wrapping_sub(one), (0xFFE as u16).unchecked_into());
  assert_eq!(max.wrapping_sub(two), (0xFFD as u16).unchecked_into());
  assert_eq!(max.wrapping_sub(max), zero);
}

// MARK: - Tests - Multiplication

#[test]
fn test_mul_operator() {
  assert_eq!(U12::from(2u8) * U12::from(0u8), U12::min_value());
  assert_eq!(U12::max_value() * U12::min_value(), U12::min_value());
  assert_eq!(U12::from(2u8) * U12::from(255u8), (510 as u16).unchecked_into());
  assert_eq!(U12::from(255u8) * U12::from(2u8), (510 as u16).unchecked_into());
}

#[test]
#[should_panic]
fn test_mul_operator_overflow() {
  let _ = U12::from(2u8) * 2048u16.unchecked_into();
}

#[test]
fn test_checked_mul() {
  assert_eq!(U12::from(2u8).checked_mul(0u8.into()), Some(U12::min_value()));
  assert_eq!(U12::from(2u8).checked_mul(255u8.into()), Some((510 as u16).unchecked_into()));
  assert_eq!(U12::from(255u8).checked_mul(2u8.into()), Some((510 as u16).unchecked_into()));
  assert_eq!(U12::from(2u8).checked_mul((2048u16).unchecked_into()), None);
  assert_eq!(U12::from(2u8).checked_mul((4095u16).unchecked_into()), None);
}

#[test]
fn test_saturating_mul() {
  assert_eq!(U12::from(2u8).saturating_mul(0u8.into()), U12::min_value());
  assert_eq!(U12::from(2u8).saturating_mul(255u8.into()), (510 as u16).unchecked_into());
  assert_eq!(U12::from(255u8).saturating_mul(2u8.into()), (510 as u16).unchecked_into());
  assert_eq!(U12::from(2u8).saturating_mul((2048u16).unchecked_into()), U12::max_value());
  assert_eq!(U12::from(2u8).saturating_mul((4095u16).unchecked_into()), U12::max_value());
}

#[test]
fn test_wrapping_mul() {
  assert_eq!(U12::from(2u8).wrapping_mul(0u8.into()), U12::min_value());
  assert_eq!(U12::from(2u8).wrapping_mul(255u8.into()), (510 as u16).unchecked_into());
  assert_eq!(U12::from(255u8).wrapping_mul(2u8.into()), (510 as u16).unchecked_into());
  assert_eq!(U12::from(2u8).wrapping_mul((2048u16).unchecked_into()), U12::min_value());
  assert_eq!(U12::from(2u8).wrapping_mul((4095u16).unchecked_into()), (0xFFE as u16).unchecked_into());
}

// MARK: - Tests - Division

#[test]
fn test_div_operator() {
  assert_eq!(U12::max_value() / U12::max_value(), U12::from(1u8));
  assert_eq!(U12::from(2u8) / U12::from(255u8), U12::from(0u8));
  assert_eq!(U12::from(255u8) / U12::from(2u8), U12::from(127u8));
}

#[test]
#[should_panic]
fn test_div_operator_divide_by_zero() {
  let _ = U12::from(2u8) / U12::from(0u8);
}

#[test]
fn test_checked_div() {
  assert_eq!(U12::max_value().checked_div(U12::max_value()), Some(U12::from(1u8)));
  assert_eq!(U12::from(2u8).checked_div(U12::from(255u8)), Some(U12::from(0u8)));
  assert_eq!(U12::from(255u8).checked_div(U12::from(2u8)), Some(U12::from(127u8)));
  assert_eq!(U12::from(255u8).checked_div(U12::from(1u8)), Some(U12::from(255u8)));
  assert_eq!(U12::from(255u8).checked_div(U12::from(0u8)), None);
}

#[test]
fn test_wrapping_div() {
  assert_eq!(U12::max_value().wrapping_div(U12::max_value()), U12::from(1u8));
  assert_eq!(U12::from(2u8).wrapping_div(U12::from(255u8)), U12::from(0u8));
  assert_eq!(U12::from(255u8).wrapping_div(U12::from(2u8)), U12::from(127u8));
  assert_eq!(U12::from(255u8).wrapping_div(U12::from(1u8)), U12::from(255u8));
}

#[test]
#[should_panic]
fn test_wrapping_div_overflow() {
  let _ = U12::from(255u8).wrapping_div(U12::from(0u8));
}

// MARK: - Tests - Negation

#[test]
fn test_checked_neg() {
  assert_eq!(U12::from(0u8).checked_neg(), Some(0u8.into()));
  assert_eq!(U12::from(1u8).checked_neg(), None);
  assert_eq!(U12::from(2u8).checked_neg(), None);
  assert_eq!(U12::from(255u8).checked_neg(), None);
}

#[test]
fn test_wrapping_neg() {
  assert_eq!(U12::from(0u8).wrapping_neg(), 0u8.into());
  assert_eq!(U12::from(1u8).wrapping_neg(), 0xFFFu16.unchecked_into());
  assert_eq!(U12::from(2u8).wrapping_neg(), 0xFFEu16.unchecked_into());
  assert_eq!(U12::from(255u8).wrapping_neg(), 0xF01u16.unchecked_into());
}

#[test]
fn test_overflowing_neg() {
  assert_eq!(U12::from(0u8).overflowing_neg(), (0u8.into(), false));
  assert_eq!(U12::from(1u8).overflowing_neg(), (0xFFFu16.unchecked_into(), true));
  assert_eq!(U12::from(2u8).overflowing_neg(), (0xFFEu16.unchecked_into(), true));
  assert_eq!(U12::from(255u8).overflowing_neg(), (0xF01u16.unchecked_into(), true));
}
<|endoftext|>