<commit_before>pub mod search;

use std::thread;
use std::cmp::min;
use std::sync::Arc;
use std::sync::mpsc::{channel, Sender, Receiver};
use std::time::SystemTime;
use std::num::Wrapping;
use basetypes::*;
use uci::{UciEngine, UciEngineFactory, EngineReply, OptionName, OptionDescription};
use position::Position;
use chess_move::*;
use tt::*;

// use rand;
// use rand::distributions::{Sample, Range};
// let mut rng = rand::thread_rng();
// let mut between = Range::new(0, 10);
// let x = between.sample(&mut rng);

const VERSION: &'static str = "0.1";
const MAX_DEPTH: u8 = 126;

/// Implements `UciEngine` trait.
pub struct Engine {
    position: Position,
    pondering_is_allowed: bool,
    is_thinking: bool,
    is_pondering: bool,
    curr_depth: u8,
    replies: Vec<EngineReply>,
    tt: Arc<TranspositionTable>,
    commands: Sender<search::Command>,
    reports: Receiver<search::Report>,
    search_id: usize,
    thinking_since: SystemTime,
    searched_nodes: NodeCount,
    searched_time: u64, // milliseconds
    stop_when: TimeManagement,
    no_report_since: SystemTime,
}

impl Engine {
    /// Creates a new instance.
    ///
    /// `tt_size_mb` is the preferred size of the transposition
    /// table in Mbytes.
    pub fn new(tt_size_mb: usize) -> Engine {
        let mut tt = TranspositionTable::new();
        tt.resize(tt_size_mb);
        let tt = Arc::new(tt);
        let (commands_tx, commands_rx) = channel();
        let (reports_tx, reports_rx) = channel();
        let tt2 = tt.clone();
        thread::spawn(move || {
            search::run_deepening(tt2, commands_rx, reports_tx);
        });

        Engine {
            position: Position::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w QKqk - 0 \
                                          1")
                          .ok()
                          .unwrap(),
            pondering_is_allowed: false,
            is_thinking: false,
            is_pondering: false,
            curr_depth: 0,
            replies: vec![],
            tt: tt,
            commands: commands_tx,
            reports: reports_rx,
            search_id: 0,
            thinking_since: SystemTime::now(),
            searched_nodes: 0,
            searched_time: 0,
            stop_when: TimeManagement::Infinite,
            no_report_since: SystemTime::now(),
        }
    }
}

impl UciEngine for Engine {
    fn set_option(&mut self, name: &str, value: &str) {
        match name {
            // Tells the engine that it will be allowed to ponder.
            // This option is needed because the engine might change
            // its time management algorithm when pondering is
            // allowed.
            "Ponder" => {
                self.pondering_is_allowed = value == "true";
            }

            // An invalid option. Notice that we do not support
            // re-sizing of the transposition table once the engine
            // had started.
            _ => (),
        }
    }

    fn new_game(&mut self) {
        // Clearing the transposition table would not change anything,
        // so we do nothing.
    }

    fn position(&mut self, fen: &str, moves: &mut Iterator<Item = &str>) {
        if !self.is_thinking {
            if let Ok(p) = Position::from_history(fen, moves) {
                self.position = p;
                self.tt.new_search();
            }
        }
    }

    #[allow(unused_variables)]
    fn go(&mut self,
          searchmoves: Option<Vec<String>>,
          ponder: bool,
          wtime: Option<u64>,
          btime: Option<u64>,
          winc: Option<u64>,
          binc: Option<u64>,
          movestogo: Option<u64>,
          depth: Option<u64>,
          nodes: Option<u64>,
          mate: Option<u64>,
          movetime: Option<u64>,
          infinite: bool) {
        if !self.is_thinking {
            // Note: We ignore the "searchmoves" parameter.

            self.is_thinking = true;
            self.is_pondering = ponder;
            self.search_id = (Wrapping(self.search_id) + Wrapping(1)).0;
            self.thinking_since = SystemTime::now();
            self.no_report_since = SystemTime::now();
            self.searched_nodes = 0;
            self.searched_time = 0;
            self.curr_depth = 0;

            // Figure out when we should stop thinking.
            let (time, inc) = if self.position.board().to_move() == WHITE {
                (wtime, winc.unwrap_or(0))
            } else {
                (btime, binc.unwrap_or(0))
            };
            self.stop_when = if infinite {
                TimeManagement::Infinite
            } else if movetime.is_some() {
                TimeManagement::MoveTime(movetime.unwrap())
            } else if nodes.is_some() {
                TimeManagement::Nodes(nodes.unwrap())
            } else if depth.is_some() {
                TimeManagement::Depth(min(depth.unwrap(), MAX_DEPTH as u64) as u8)
            } else {
                // TODO: Robert Hyatt proposes this:
                //
                // nMoves =  min( numberOfMovesOutOfBook, 10 )
                // factor = 2 - nMoves / 10
                // target = timeLeft / numberOfMovesUntilNextTimeControl
                // time   = factor * target
                let time = time.unwrap_or(0);
                let movestogo = movestogo.unwrap_or(40);
                let movetime = (time + inc * movestogo) / movestogo;
                TimeManagement::MoveTimeHint(min(movetime, time / 2))
            };

            // Start a search with the maximum possible depth.
            self.commands
                .send(search::Command::Search {
                    search_id: self.search_id,
                    position: self.position.clone(),
                    depth: MAX_DEPTH,
                    lower_bound: -20000,
                    upper_bound: 20000,
                })
                .unwrap();
        }
    }

    fn ponder_hit(&mut self) {
        if self.is_thinking && self.is_pondering {
            self.is_pondering = false;
        }
    }

    fn stop(&mut self) {
        if self.is_thinking {
            self.commands.send(search::Command::Stop).unwrap();
            let best_move = self.get_best_move();
            self.replies.push(EngineReply::BestMove {
                best_move: best_move,
                ponder_move: None,
            });
            self.is_thinking = false;
        }
    }

    fn is_thinking(&self) -> bool {
        self.is_thinking
    }

    fn get_reply(&mut self) -> Option<EngineReply> {
        // TODO: implement panic mode for MoveTimeHint management.
        if self.is_thinking && !self.is_pondering {
            if match self.stop_when {
                TimeManagement::MoveTime(t) => self.searched_time >= t,
                TimeManagement::MoveTimeHint(t) => self.searched_time >= t,
                TimeManagement::Nodes(n) => self.searched_nodes >= n,
                TimeManagement::Depth(d) => self.curr_depth > d,
                TimeManagement::Infinite => false,
            } {
                self.stop();
            }
        }

        // Empty the reports queue.
        while let Ok(report) = self.reports.try_recv() {
            if self.is_thinking {
                match report {
                    // Search progress.
                    search::Report::Progress { search_id, searched_nodes, depth }
                        if search_id == self.search_id => {

                        let thinking_duration = self.thinking_since.elapsed().unwrap();
                        self.searched_nodes = searched_nodes;
                        self.searched_time = 1000 * thinking_duration.as_secs() +
                                             (thinking_duration.subsec_nanos() / 1000000) as u64;
                        if self.curr_depth < depth {
                            self.report_pv();
                            self.curr_depth = depth;
                        }
                        if self.no_report_since.elapsed().unwrap().as_secs() > 30 {
                            self.report_progress();
                        }
                    }

                    // Terminate the search (unless pondering of infinite).
                    search::Report::Done { search_id, .. } if search_id == self.search_id => {
                        if !self.is_pondering {
                            self.stop_when = if let TimeManagement::Infinite = self.stop_when {
                                TimeManagement::Infinite
                            } else {
                                TimeManagement::MoveTime(0)
                            };
                        }
                    }

                    // Stale reports from stopped searches.
                    _ => (),
                }
            }
        }

        if self.replies.len() > 0 {
            Some(self.replies.remove(0))
        } else {
            None
        }
    }
}

impl Engine {
    // A helper method. It extracts the primary variation (PV) from
    // the transposition table (TT) and sends it to the GUI.
    fn report_pv(&mut self) {
        let mut prev_move = None;
        let mut p = self.position.clone();

        // Extract the PV, the value, and the bound from the TT.
        let mut pv = Vec::new();
        let mut value = -20000;
        let mut bound = BOUND_LOWER;
        while let Some(entry) = self.tt.probe(p.hash()) {
            if pv.len() < self.curr_depth as usize && entry.bound() != BOUND_NONE {
                if let Some(m) = prev_move {
                    pv.push(m);
                }
                if pv.len() & 1 == 0 {
                    value = entry.value();
                    bound = entry.bound();
                } else {
                    // In this case the value stored in `entry` is
                    // from other side's perspective.
                    value = -entry.value();
                    bound = match entry.bound() {
                        BOUND_UPPER => BOUND_LOWER,
                        BOUND_LOWER => BOUND_UPPER,
                        x => x,
                    };
                }
                if bound == BOUND_EXACT {
                    if let Some(m) = p.try_move_digest(entry.move16()) {
                        if p.do_move(m) && !p.is_repeated() {
                            // Try to extend the PV.
                            prev_move = Some(m);
                            continue;
                        }
                    }
                }
            }
            break;
        }

        // Send the extracted info to the GUI.
        let value_suffix = match bound {
            BOUND_EXACT => "",
            BOUND_UPPER => " upperbound",
            BOUND_LOWER => " lowerbound",
            _ => panic!("unexpected bound type"),
        };
        let nps = if self.searched_time == 0 {
            0
        } else {
            1000 * self.searched_nodes / self.searched_time
        };
        let mut pv_string = String::new();
        for m in pv {
            pv_string.push(' ');
            pv_string.push_str(&m.notation());
        }
        self.replies.push(EngineReply::Info(vec![
            ("depth".to_string(), format!("{}", self.curr_depth)),
            ("score".to_string(), format!("cp {}{}", value, value_suffix)),
            ("time".to_string(), format!("{}", self.searched_time)),
            ("nodes".to_string(), format!("{}", self.searched_nodes)),
            ("nps".to_string(), format!("{}", nps)),
            ("pv".to_string(), pv_string),
        ]));
        self.no_report_since = SystemTime::now();
    }

    // A helper method. It reports the depth, the node count, and
    // nodes per second to the GUI.
    fn report_progress(&mut self) {
        let nps = if self.searched_time == 0 {
            0
        } else {
            1000 * self.searched_nodes / self.searched_time
        };
        self.replies.push(EngineReply::Info(vec![
            ("depth".to_string(), format!("{}", self.curr_depth)),
            ("nodes".to_string(), format!("{}", self.searched_nodes)),
            ("nps".to_string(), format!("{}", nps)),
        ]));
        self.no_report_since = SystemTime::now();
    }

    fn get_best_move(&mut self) -> String {
        let mut m = match self.tt.probe(self.position.hash()) {
            Some(entry) => entry.move16(),
            None => 0,
        };
        if m == 0 {
            // Pick the first legal move.
            let mut first_legal_move = Move::invalid();
            let mut v = MoveStack::new();
            self.position.generate_moves(&mut v);
            while let Some(x) = v.pop() {
                if self.position.do_move(x) {
                    self.position.undo_move();
                    first_legal_move = x;
                    break;
                }
            }
            m = first_legal_move.digest();
        }
        if m != 0 {
            let move_type = move_type(m);
            let orig_square = orig_square(m);
            let dest_square = dest_square(m);
            let promoted_piece = match aux_data(m) {
                0 => "q",
                1 => "r",
                2 => "b",
                3 => "n",
                _ => panic!("invalid promoted piece code"),
            };
            format!("{}{}{}",
                    notation(orig_square),
                    notation(dest_square),
                    if move_type == MOVE_PROMOTION {
                        promoted_piece
                    } else {
                        ""
                    })

        } else {
            "0000".to_string()
        }
    }
}

/// Implements `UciEngineFactory` trait.
pub struct EngineFactory;

impl UciEngineFactory<Engine> for EngineFactory {
    fn name(&self) -> String {
        format!("Alcibiades {}", VERSION)
    }

    fn author(&self) -> String {
        "Evgeni Pandurski".to_string()
    }

    fn options(&self) -> Vec<(OptionName, OptionDescription)> {
        vec![
            // TODO: Calculate a sane limit for the hash size.
            ("Hash".to_string(), OptionDescription::Spin { min: 1, max: 2048, default: 16 }),
            ("Ponder".to_string(), OptionDescription::Check { default: false }),
        ]
    }

    fn create(&self, hash_size_mb: Option<usize>) -> Engine {
        Engine::new(hash_size_mb.unwrap_or(16))
    }
}

enum TimeManagement {
    MoveTime(u64),
    MoveTimeHint(u64),
    Nodes(NodeCount),
    Depth(u8),
    Infinite,
}
<commit_msg>averaging nps<commit_after>pub mod search;

use std::thread;
use std::cmp::min;
use std::sync::Arc;
use std::sync::mpsc::{channel, Sender, Receiver};
use std::time::SystemTime;
use std::num::Wrapping;
use basetypes::*;
use uci::{UciEngine, UciEngineFactory, EngineReply, OptionName, OptionDescription};
use position::Position;
use chess_move::*;
use tt::*;

// use rand;
// use rand::distributions::{Sample, Range};
// let mut rng = rand::thread_rng();
// let mut between = Range::new(0, 10);
// let x = between.sample(&mut rng);

const VERSION: &'static str = "0.1";
const MAX_DEPTH: u8 = 126;

/// Implements `UciEngine` trait.
pub struct Engine {
    position: Position,
    pondering_is_allowed: bool,
    is_thinking: bool,
    is_pondering: bool,
    curr_depth: u8,
    replies: Vec<EngineReply>,
    tt: Arc<TranspositionTable>,
    commands: Sender<search::Command>,
    reports: Receiver<search::Report>,
    search_id: usize,
    thinking_since: SystemTime,
    searched_nodes: NodeCount,
    searched_time: u64, // milliseconds
    stop_when: TimeManagement,
    no_report_since: SystemTime,
    nps: u64,
}

impl Engine {
    /// Creates a new instance.
    ///
    /// `tt_size_mb` is the preferred size of the transposition
    /// table in Mbytes.
    pub fn new(tt_size_mb: usize) -> Engine {
        let mut tt = TranspositionTable::new();
        tt.resize(tt_size_mb);
        let tt = Arc::new(tt);
        let (commands_tx, commands_rx) = channel();
        let (reports_tx, reports_rx) = channel();
        let tt2 = tt.clone();
        thread::spawn(move || {
            search::run_deepening(tt2, commands_rx, reports_tx);
        });

        Engine {
            position: Position::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w QKqk - 0 \
                                          1")
                          .ok()
                          .unwrap(),
            pondering_is_allowed: false,
            is_thinking: false,
            is_pondering: false,
            curr_depth: 0,
            replies: vec![],
            tt: tt,
            commands: commands_tx,
            reports: reports_rx,
            search_id: 0,
            thinking_since: SystemTime::now(),
            searched_nodes: 0,
            searched_time: 0,
            stop_when: TimeManagement::Infinite,
            no_report_since: SystemTime::now(),
            nps: 0,
        }
    }
}

impl UciEngine for Engine {
    fn set_option(&mut self, name: &str, value: &str) {
        match name {
            // Tells the engine that it will be allowed to ponder.
            // This option is needed because the engine might change
            // its time management algorithm when pondering is
            // allowed.
            "Ponder" => {
                self.pondering_is_allowed = value == "true";
            }

            // An invalid option. Notice that we do not support
            // re-sizing of the transposition table once the engine
            // had started.
            _ => (),
        }
    }

    fn new_game(&mut self) {
        // Clearing the transposition table would not change anything,
        // so we do nothing.
    }

    fn position(&mut self, fen: &str, moves: &mut Iterator<Item = &str>) {
        if !self.is_thinking {
            if let Ok(p) = Position::from_history(fen, moves) {
                self.position = p;
                self.tt.new_search();
            }
        }
    }

    #[allow(unused_variables)]
    fn go(&mut self,
          searchmoves: Option<Vec<String>>,
          ponder: bool,
          wtime: Option<u64>,
          btime: Option<u64>,
          winc: Option<u64>,
          binc: Option<u64>,
          movestogo: Option<u64>,
          depth: Option<u64>,
          nodes: Option<u64>,
          mate: Option<u64>,
          movetime: Option<u64>,
          infinite: bool) {
        if !self.is_thinking {
            // Note: We ignore the "searchmoves" parameter.

            self.is_thinking = true;
            self.is_pondering = ponder;
            self.search_id = (Wrapping(self.search_id) + Wrapping(1)).0;
            self.thinking_since = SystemTime::now();
            self.no_report_since = SystemTime::now();
            self.searched_nodes = 0;
            self.searched_time = 0;
            self.curr_depth = 0;

            // Figure out when we should stop thinking.
            let (time, inc) = if self.position.board().to_move() == WHITE {
                (wtime, winc.unwrap_or(0))
            } else {
                (btime, binc.unwrap_or(0))
            };
            self.stop_when = if infinite {
                TimeManagement::Infinite
            } else if movetime.is_some() {
                TimeManagement::MoveTime(movetime.unwrap())
            } else if nodes.is_some() {
                TimeManagement::Nodes(nodes.unwrap())
            } else if depth.is_some() {
                TimeManagement::Depth(min(depth.unwrap(), MAX_DEPTH as u64) as u8)
            } else {
                // TODO: Robert Hyatt proposes this:
                //
                // nMoves =  min( numberOfMovesOutOfBook, 10 )
                // factor = 2 - nMoves / 10
                // target = timeLeft / numberOfMovesUntilNextTimeControl
                // time   = factor * target
                let time = time.unwrap_or(0);
                let movestogo = movestogo.unwrap_or(40);
                let movetime = (time + inc * movestogo) / movestogo;
                TimeManagement::MoveTimeHint(min(movetime, time / 2))
            };

            // Start a search with the maximum possible depth.
            self.commands
                .send(search::Command::Search {
                    search_id: self.search_id,
                    position: self.position.clone(),
                    depth: MAX_DEPTH,
                    lower_bound: -20000,
                    upper_bound: 20000,
                })
                .unwrap();
        }
    }

    fn ponder_hit(&mut self) {
        if self.is_thinking && self.is_pondering {
            self.is_pondering = false;
        }
    }

    fn stop(&mut self) {
        if self.is_thinking {
            self.commands.send(search::Command::Stop).unwrap();
            let best_move = self.get_best_move();
            self.replies.push(EngineReply::BestMove {
                best_move: best_move,
                ponder_move: None,
            });
            self.is_thinking = false;
        }
    }

    fn is_thinking(&self) -> bool {
        self.is_thinking
    }

    fn get_reply(&mut self) -> Option<EngineReply> {
        // TODO: implement panic mode for MoveTimeHint management.
        if self.is_thinking && !self.is_pondering {
            if match self.stop_when {
                TimeManagement::MoveTime(t) => self.searched_time >= t,
                TimeManagement::MoveTimeHint(t) => self.searched_time >= t,
                TimeManagement::Nodes(n) => self.searched_nodes >= n,
                TimeManagement::Depth(d) => self.curr_depth > d,
                TimeManagement::Infinite => false,
            } {
                self.stop();
            }
        }

        // Empty the reports queue.
        while let Ok(report) = self.reports.try_recv() {
            if self.is_thinking {
                match report {
                    // Search progress.
                    search::Report::Progress { search_id, searched_nodes, depth }
                        if search_id == self.search_id => {

                        let thinking_duration = self.thinking_since.elapsed().unwrap();
                        self.searched_nodes = searched_nodes;
                        self.searched_time = 1000 * thinking_duration.as_secs() +
                                             (thinking_duration.subsec_nanos() / 1000000) as u64;
                        self.nps = 1000 * (self.nps + self.searched_nodes) /
                                   (1000 + self.searched_time);
                        if self.curr_depth < depth {
                            self.report_pv();
                            self.curr_depth = depth;
                        }
                        if self.no_report_since.elapsed().unwrap().as_secs() > 30 {
                            self.report_progress();
                        }
                    }

                    // Terminate the search (unless pondering of infinite).
                    search::Report::Done { search_id, .. } if search_id == self.search_id => {
                        if !self.is_pondering {
                            self.stop_when = if let TimeManagement::Infinite = self.stop_when {
                                TimeManagement::Infinite
                            } else {
                                TimeManagement::MoveTime(0)
                            };
                        }
                    }

                    // Stale reports from stopped searches.
                    _ => (),
                }
            }
        }

        if self.replies.len() > 0 {
            Some(self.replies.remove(0))
        } else {
            None
        }
    }
}

impl Engine {
    // A helper method. It extracts the primary variation (PV) from
    // the transposition table (TT) and sends it to the GUI.
    fn report_pv(&mut self) {
        let mut prev_move = None;
        let mut p = self.position.clone();

        // Extract the PV, the value, and the bound from the TT.
        let mut pv = Vec::new();
        let mut value = -20000;
        let mut bound = BOUND_LOWER;
        while let Some(entry) = self.tt.probe(p.hash()) {
            if pv.len() < self.curr_depth as usize && entry.bound() != BOUND_NONE {
                if let Some(m) = prev_move {
                    pv.push(m);
                }
                if pv.len() & 1 == 0 {
                    value = entry.value();
                    bound = entry.bound();
                } else {
                    // In this case the value stored in `entry` is
                    // from other side's perspective.
                    value = -entry.value();
                    bound = match entry.bound() {
                        BOUND_UPPER => BOUND_LOWER,
                        BOUND_LOWER => BOUND_UPPER,
                        x => x,
                    };
                }
                if bound == BOUND_EXACT {
                    if let Some(m) = p.try_move_digest(entry.move16()) {
                        if p.do_move(m) && !p.is_repeated() {
                            // Try to extend the PV.
                            prev_move = Some(m);
                            continue;
                        }
                    }
                }
            }
            break;
        }

        // Send the extracted info to the GUI.
        let value_suffix = match bound {
            BOUND_EXACT => "",
            BOUND_UPPER => " upperbound",
            BOUND_LOWER => " lowerbound",
            _ => panic!("unexpected bound type"),
        };
        let mut pv_string = String::new();
        for m in pv {
            pv_string.push(' ');
            pv_string.push_str(&m.notation());
        }
        self.replies.push(EngineReply::Info(vec![
            ("depth".to_string(), format!("{}", self.curr_depth)),
            ("score".to_string(), format!("cp {}{}", value, value_suffix)),
            ("time".to_string(), format!("{}", self.searched_time)),
            ("nodes".to_string(), format!("{}", self.searched_nodes)),
            ("nps".to_string(), format!("{}", self.nps)),
            ("pv".to_string(), pv_string),
        ]));
        self.no_report_since = SystemTime::now();
    }

    // A helper method. It reports the depth, the node count, and
    // nodes per second to the GUI.
    fn report_progress(&mut self) {
        self.replies.push(EngineReply::Info(vec![
            ("depth".to_string(), format!("{}", self.curr_depth)),
            ("nodes".to_string(), format!("{}", self.searched_nodes)),
            ("nps".to_string(), format!("{}", self.nps)),
        ]));
        self.no_report_since = SystemTime::now();
    }

    fn get_best_move(&mut self) -> String {
        let mut m = match self.tt.probe(self.position.hash()) {
            Some(entry) => entry.move16(),
            None => 0,
        };
        if m == 0 {
            // Pick the first legal move.
            let mut first_legal_move = Move::invalid();
            let mut v = MoveStack::new();
            self.position.generate_moves(&mut v);
            while let Some(x) = v.pop() {
                if self.position.do_move(x) {
                    self.position.undo_move();
                    first_legal_move = x;
                    break;
                }
            }
            m = first_legal_move.digest();
        }
        if m != 0 {
            let move_type = move_type(m);
            let orig_square = orig_square(m);
            let dest_square = dest_square(m);
            let promoted_piece = match aux_data(m) {
                0 => "q",
                1 => "r",
                2 => "b",
                3 => "n",
                _ => panic!("invalid promoted piece code"),
            };
            format!("{}{}{}",
                    notation(orig_square),
                    notation(dest_square),
                    if move_type == MOVE_PROMOTION {
                        promoted_piece
                    } else {
                        ""
                    })

        } else {
            "0000".to_string()
        }
    }
}

/// Implements `UciEngineFactory` trait.
pub struct EngineFactory;

impl UciEngineFactory<Engine> for EngineFactory {
    fn name(&self) -> String {
        format!("Alcibiades {}", VERSION)
    }

    fn author(&self) -> String {
        "Evgeni Pandurski".to_string()
    }

    fn options(&self) -> Vec<(OptionName, OptionDescription)> {
        vec![
            // TODO: Calculate a sane limit for the hash size.
            ("Hash".to_string(), OptionDescription::Spin { min: 1, max: 2048, default: 16 }),
            ("Ponder".to_string(), OptionDescription::Check { default: false }),
        ]
    }

    fn create(&self, hash_size_mb: Option<usize>) -> Engine {
        Engine::new(hash_size_mb.unwrap_or(16))
    }
}

enum TimeManagement {
    MoveTime(u64),
    MoveTimeHint(u64),
    Nodes(NodeCount),
    Depth(u8),
    Infinite,
}
<|endoftext|>


<commit_before>use std::any::{Any, TypeId};
use std::fmt;

use hashbrown::HashMap;

#[derive(Default)]
/// A type map of request extensions.
pub struct Extensions {
    map: HashMap<TypeId, Box<Any>>,
}

impl Extensions {
    /// Create an empty `Extensions`.
    #[inline]
    pub fn new() -> Extensions {
        Extensions {
            map: HashMap::default(),
        }
    }

    /// Insert a type into this `Extensions`.
    ///
    /// If a extension of this type already existed, it will
    /// be returned.
    pub fn insert<T: 'static>(&mut self, val: T) {
        self.map.insert(TypeId::of::<T>(), Box::new(val));
    }

    /// Get a reference to a type previously inserted on this `Extensions`.
    pub fn get<T: 'static>(&self) -> Option<&T> {
        self.map
            .get(&TypeId::of::<T>())
            .and_then(|boxed| (&**boxed as &(Any + 'static)).downcast_ref())
    }

    /// Get a mutable reference to a type previously inserted on this `Extensions`.
    pub fn get_mut<T: 'static>(&mut self) -> Option<&mut T> {
        self.map
            .get_mut(&TypeId::of::<T>())
            .and_then(|boxed| (&mut **boxed as &mut (Any + 'static)).downcast_mut())
    }

    /// Remove a type from this `Extensions`.
    ///
    /// If a extension of this type existed, it will be returned.
    pub fn remove<T: 'static>(&mut self) -> Option<T> {
        self.map.remove(&TypeId::of::<T>()).and_then(|boxed| {
            (boxed as Box<Any + 'static>)
                .downcast()
                .ok()
                .map(|boxed| *boxed)
        })
    }

    /// Clear the `Extensions` of all inserted extensions.
    #[inline]
    pub fn clear(&mut self) {
        self.map.clear();
    }
}

impl fmt::Debug for Extensions {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("Extensions").finish()
    }
}

#[test]
fn test_extensions() {
    #[derive(Debug, PartialEq)]
    struct MyType(i32);

    let mut extensions = Extensions::new();

    extensions.insert(5i32);
    extensions.insert(MyType(10));

    assert_eq!(extensions.get(), Some(&5i32));
    assert_eq!(extensions.get_mut(), Some(&mut 5i32));

    assert_eq!(extensions.remove::<i32>(), Some(5i32));
    assert!(extensions.get::<i32>().is_none());

    assert_eq!(extensions.get::<bool>(), None);
    assert_eq!(extensions.get(), Some(&MyType(10)));
}
<commit_msg>add Extensions::contains method<commit_after>use std::any::{Any, TypeId};
use std::fmt;

use hashbrown::HashMap;

#[derive(Default)]
/// A type map of request extensions.
pub struct Extensions {
    map: HashMap<TypeId, Box<Any>>,
}

impl Extensions {
    /// Create an empty `Extensions`.
    #[inline]
    pub fn new() -> Extensions {
        Extensions {
            map: HashMap::default(),
        }
    }

    /// Insert a type into this `Extensions`.
    ///
    /// If a extension of this type already existed, it will
    /// be returned.
    pub fn insert<T: 'static>(&mut self, val: T) {
        self.map.insert(TypeId::of::<T>(), Box::new(val));
    }

    /// Check if container contains entry
    pub fn contains<T: 'static>(&self) -> bool {
        self.map.get(&TypeId::of::<T>()).is_some()
    }

    /// Get a reference to a type previously inserted on this `Extensions`.
    pub fn get<T: 'static>(&self) -> Option<&T> {
        self.map
            .get(&TypeId::of::<T>())
            .and_then(|boxed| (&**boxed as &(Any + 'static)).downcast_ref())
    }

    /// Get a mutable reference to a type previously inserted on this `Extensions`.
    pub fn get_mut<T: 'static>(&mut self) -> Option<&mut T> {
        self.map
            .get_mut(&TypeId::of::<T>())
            .and_then(|boxed| (&mut **boxed as &mut (Any + 'static)).downcast_mut())
    }

    /// Remove a type from this `Extensions`.
    ///
    /// If a extension of this type existed, it will be returned.
    pub fn remove<T: 'static>(&mut self) -> Option<T> {
        self.map.remove(&TypeId::of::<T>()).and_then(|boxed| {
            (boxed as Box<Any + 'static>)
                .downcast()
                .ok()
                .map(|boxed| *boxed)
        })
    }

    /// Clear the `Extensions` of all inserted extensions.
    #[inline]
    pub fn clear(&mut self) {
        self.map.clear();
    }
}

impl fmt::Debug for Extensions {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("Extensions").finish()
    }
}

#[test]
fn test_extensions() {
    #[derive(Debug, PartialEq)]
    struct MyType(i32);

    let mut extensions = Extensions::new();

    extensions.insert(5i32);
    extensions.insert(MyType(10));

    assert_eq!(extensions.get(), Some(&5i32));
    assert_eq!(extensions.get_mut(), Some(&mut 5i32));

    assert_eq!(extensions.remove::<i32>(), Some(5i32));
    assert!(extensions.get::<i32>().is_none());

    assert_eq!(extensions.get::<bool>(), None);
    assert_eq!(extensions.get(), Some(&MyType(10)));
}
<|endoftext|>


<commit_before>use float::Float;
use int::Int;

macro_rules! fp_overflow {
    (infinity, $fty:ty, $sign: expr) => {
        return {
            <$fty as Float>::from_parts(
                $sign,
                <$fty as Float>::exponent_max() as <$fty as Float>::Int,
                0 as <$fty as Float>::Int)
        }
    }
}

macro_rules! fp_convert {
    ($intrinsic:ident: $ity:ty, $fty:ty) => {

    pub extern "C" fn $intrinsic(i: $ity) -> $fty {
        if i == 0 {
            return 0.0
        }

        let mant_dig = <$fty>::significand_bits() + 1;
        let exponent_bias = <$fty>::exponent_bias();

        let n = <$ity>::bits();
        let (s, a) = i.extract_sign();
        let mut a = a;

        // number of significant digits
        let sd = n - a.leading_zeros();

        // exponent
        let mut e = sd - 1;

        if <$ity>::bits() < mant_dig {
            return <$fty>::from_parts(s,
                (e + exponent_bias) as <$fty as Float>::Int,
                (a as <$fty as Float>::Int) << (mant_dig - e - 1))
        }

        a = if sd > mant_dig {
            /* start:  0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx
            *  finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR
            *                                                12345678901234567890123456
            *  1 = msb 1 bit
            *  P = bit MANT_DIG-1 bits to the right of 1
            *  Q = bit MANT_DIG bits to the right of 1
            *  R = "or" of all bits to the right of Q
            */
            let mant_dig_plus_one = mant_dig + 1;
            let mant_dig_plus_two = mant_dig + 2;
            a = if sd == mant_dig_plus_one {
                a << 1
            } else if sd == mant_dig_plus_two {
                a
            } else {
                (a >> (sd - mant_dig_plus_two)) as <$ity as Int>::UnsignedInt |
                ((a & <$ity as Int>::UnsignedInt::max_value()).wrapping_shl((n + mant_dig_plus_two) - sd) != 0) as <$ity as Int>::UnsignedInt
            };

            /* finish: */
            a |= ((a & 4) != 0) as <$ity as Int>::UnsignedInt; /* Or P into R */
            a += 1; /* round - this step may add a significant bit */
            a >>= 2; /* dump Q and R */

            /* a is now rounded to mant_dig or mant_dig+1 bits */
            if (a & (1 << mant_dig)) != 0 {
                a >>= 1; e += 1;
            }
            a
            /* a is now rounded to mant_dig bits */
        } else {
            a.wrapping_shl(mant_dig - sd)
            /* a is now rounded to mant_dig bits */
        };

        <$fty>::from_parts(s,
            (e + exponent_bias) as <$fty as Float>::Int,
            a as <$fty as Float>::Int)
    }
    }
}

fp_convert!(__floatsisf: i32, f32);
fp_convert!(__floatsidf: i32, f64);
fp_convert!(__floatdidf: i64, f64);
fp_convert!(__floatunsisf: u32, f32);
fp_convert!(__floatunsidf: u32, f64);
fp_convert!(__floatundidf: u64, f64);

#[derive(PartialEq, Debug)]
enum Sign {
    Positive,
    Negative
}
macro_rules! fp_fix {
    ($intrinsic:ident: $fty:ty, $ity:ty) => {
        pub extern "C" fn $intrinsic(f: $fty) -> $ity {
            let fixint_min = <$ity>::min_value();
            let fixint_max = <$ity>::max_value();
            let fixint_bits = <$ity>::bits() as usize;
            let fixint_unsigned = fixint_min == 0;

            let sign_bit = <$fty>::sign_mask();
            let significand_bits = <$fty>::significand_bits() as usize;
            let exponent_bias = <$fty>::exponent_bias() as usize;
            //let exponent_max = <$fty>::exponent_max() as usize;

            // Break a into sign, exponent, significand
            let a_rep = <$fty>::repr(f);
            let a_abs = a_rep & !sign_bit;

            // this is used to work around -1 not being available for unsigned
            let sign = if (a_rep & sign_bit) == 0 { Sign::Positive } else { Sign::Negative };
            let mut exponent = (a_abs >> significand_bits) as usize;
            let significand = (a_abs & <$fty>::significand_mask()) | <$fty>::implicit_bit();

            // if < 1 or unsigned & negative
            if  exponent < exponent_bias ||
                fixint_unsigned && sign == Sign::Negative {
                return 0
            }
            exponent -= exponent_bias;

            // If the value is infinity, saturate.
            // If the value is too large for the integer type, 0.
            if exponent >= (if fixint_unsigned {fixint_bits} else {fixint_bits -1}) {
                return if sign == Sign::Positive {fixint_max} else {fixint_min}
            }
            // If 0 <= exponent < significand_bits, right shift to get the result.
            // Otherwise, shift left.
            // (sign - 1) will never overflow as negative signs are already returned as 0 for unsigned
            let r = if exponent < significand_bits {
                (significand >> (significand_bits - exponent)) as $ity
            } else {
                (significand as $ity) << (exponent - significand_bits)
            };

            if sign == Sign::Negative {
                (!r).wrapping_add(1)
            } else {
                r
            }
        }
    }
}

fp_fix!(__fixsfsi: f32, i32);
fp_fix!(__fixsfdi: f32, i64);
fp_fix!(__fixdfsi: f64, i32);
fp_fix!(__fixdfdi: f64, i64);

fp_fix!(__fixunssfsi: f32, u32);
fp_fix!(__fixunssfdi: f32, u64);
fp_fix!(__fixunsdfsi: f64, u32);
fp_fix!(__fixunsdfdi: f64, u64);

// NOTE(cfg) for some reason, on arm*-unknown-linux-gnueabihf, our implementation doesn't
// match the output of its gcc_s or compiler-rt counterpart. Until we investigate further, we'll
// just avoid testing against them on those targets. Do note that our implementation gives the
// correct answer; gcc_s and compiler-rt are incorrect in this case.
//
#[cfg(all(test, not(arm_linux)))]
mod tests {
    use qc::{I32, U32, I64, U64, F32, F64};

    check! {
        fn __floatsisf(f: extern fn(i32) -> f32,
                    a: I32)
                    -> Option<F32> {
            Some(F32(f(a.0)))
        }
        fn __floatsidf(f: extern fn(i32) -> f64,
                    a: I32)
                    -> Option<F64> {
            Some(F64(f(a.0)))
        }
        fn __floatdidf(f: extern fn(i64) -> f64,
                    a: I64)
                    -> Option<F64> {
            Some(F64(f(a.0)))
        }
        fn __floatunsisf(f: extern fn(u32) -> f32,
                    a: U32)
                    -> Option<F32> {
            Some(F32(f(a.0)))
        }
        fn __floatunsidf(f: extern fn(u32) -> f64,
                    a: U32)
                    -> Option<F64> {
            Some(F64(f(a.0)))
        }
        fn __floatundidf(f: extern fn(u64) -> f64,
                    a: U64)
                    -> Option<F64> {
            Some(F64(f(a.0)))
        }

        fn __fixsfsi(f: extern fn(f32) -> i32,
                    a: F32)
                    -> Option<I32> {
            Some(I32(f(a.0)))
        }
        fn __fixsfdi(f: extern fn(f32) -> i64,
                    a: F32)
                    -> Option<I64> {
            Some(I64(f(a.0)))
        }
        fn __fixdfsi(f: extern fn(f64) -> i32,
                    a: F64)
                    -> Option<I32> {
            Some(I32(f(a.0)))
        }
        fn __fixdfdi(f: extern fn(f64) -> i64,
                    a: F64)
                    -> Option<I64> {
            Some(I64(f(a.0)))
        }

        fn __fixunssfsi(f: extern fn(f32) -> u32,
                    a: F32)
                    -> Option<U32> {
            Some(U32(f(a.0)))
        }
        fn __fixunssfdi(f: extern fn(f32) -> u64,
                    a: F32)
                    -> Option<U64> {
            Some(U64(f(a.0)))
        }
        fn __fixunsdfsi(f: extern fn(f64) -> u32,
                    a: F64)
                    -> Option<U32> {
            Some(U32(f(a.0)))
        }
        fn __fixunsdfdi(f: extern fn(f64) -> u64,
                    a: F64)
                    -> Option<U64> {
            Some(U64(f(a.0)))
        }
    }
}
<commit_msg>exclude from tests values out of integer range (and NaN).<commit_after>use float::Float;
use int::Int;

macro_rules! fp_overflow {
    (infinity, $fty:ty, $sign: expr) => {
        return {
            <$fty as Float>::from_parts(
                $sign,
                <$fty as Float>::exponent_max() as <$fty as Float>::Int,
                0 as <$fty as Float>::Int)
        }
    }
}

macro_rules! fp_convert {
    ($intrinsic:ident: $ity:ty, $fty:ty) => {

    pub extern "C" fn $intrinsic(i: $ity) -> $fty {
        if i == 0 {
            return 0.0
        }

        let mant_dig = <$fty>::significand_bits() + 1;
        let exponent_bias = <$fty>::exponent_bias();

        let n = <$ity>::bits();
        let (s, a) = i.extract_sign();
        let mut a = a;

        // number of significant digits
        let sd = n - a.leading_zeros();

        // exponent
        let mut e = sd - 1;

        if <$ity>::bits() < mant_dig {
            return <$fty>::from_parts(s,
                (e + exponent_bias) as <$fty as Float>::Int,
                (a as <$fty as Float>::Int) << (mant_dig - e - 1))
        }

        a = if sd > mant_dig {
            /* start:  0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx
            *  finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR
            *                                                12345678901234567890123456
            *  1 = msb 1 bit
            *  P = bit MANT_DIG-1 bits to the right of 1
            *  Q = bit MANT_DIG bits to the right of 1
            *  R = "or" of all bits to the right of Q
            */
            let mant_dig_plus_one = mant_dig + 1;
            let mant_dig_plus_two = mant_dig + 2;
            a = if sd == mant_dig_plus_one {
                a << 1
            } else if sd == mant_dig_plus_two {
                a
            } else {
                (a >> (sd - mant_dig_plus_two)) as <$ity as Int>::UnsignedInt |
                ((a & <$ity as Int>::UnsignedInt::max_value()).wrapping_shl((n + mant_dig_plus_two) - sd) != 0) as <$ity as Int>::UnsignedInt
            };

            /* finish: */
            a |= ((a & 4) != 0) as <$ity as Int>::UnsignedInt; /* Or P into R */
            a += 1; /* round - this step may add a significant bit */
            a >>= 2; /* dump Q and R */

            /* a is now rounded to mant_dig or mant_dig+1 bits */
            if (a & (1 << mant_dig)) != 0 {
                a >>= 1; e += 1;
            }
            a
            /* a is now rounded to mant_dig bits */
        } else {
            a.wrapping_shl(mant_dig - sd)
            /* a is now rounded to mant_dig bits */
        };

        <$fty>::from_parts(s,
            (e + exponent_bias) as <$fty as Float>::Int,
            a as <$fty as Float>::Int)
    }
    }
}

fp_convert!(__floatsisf: i32, f32);
fp_convert!(__floatsidf: i32, f64);
fp_convert!(__floatdidf: i64, f64);
fp_convert!(__floatunsisf: u32, f32);
fp_convert!(__floatunsidf: u32, f64);
fp_convert!(__floatundidf: u64, f64);

#[derive(PartialEq, Debug)]
enum Sign {
    Positive,
    Negative
}
macro_rules! fp_fix {
    ($intrinsic:ident: $fty:ty, $ity:ty) => {
        pub extern "C" fn $intrinsic(f: $fty) -> $ity {
            let fixint_min = <$ity>::min_value();
            let fixint_max = <$ity>::max_value();
            let fixint_bits = <$ity>::bits() as usize;
            let fixint_unsigned = fixint_min == 0;

            let sign_bit = <$fty>::sign_mask();
            let significand_bits = <$fty>::significand_bits() as usize;
            let exponent_bias = <$fty>::exponent_bias() as usize;
            //let exponent_max = <$fty>::exponent_max() as usize;

            // Break a into sign, exponent, significand
            let a_rep = <$fty>::repr(f);
            let a_abs = a_rep & !sign_bit;

            // this is used to work around -1 not being available for unsigned
            let sign = if (a_rep & sign_bit) == 0 { Sign::Positive } else { Sign::Negative };
            let mut exponent = (a_abs >> significand_bits) as usize;
            let significand = (a_abs & <$fty>::significand_mask()) | <$fty>::implicit_bit();

            // if < 1 or unsigned & negative
            if  exponent < exponent_bias ||
                fixint_unsigned && sign == Sign::Negative {
                return 0
            }
            exponent -= exponent_bias;

            // If the value is infinity, saturate.
            // If the value is too large for the integer type, 0.
            if exponent >= (if fixint_unsigned {fixint_bits} else {fixint_bits -1}) {
                return if sign == Sign::Positive {fixint_max} else {fixint_min}
            }
            // If 0 <= exponent < significand_bits, right shift to get the result.
            // Otherwise, shift left.
            // (sign - 1) will never overflow as negative signs are already returned as 0 for unsigned
            let r = if exponent < significand_bits {
                (significand >> (significand_bits - exponent)) as $ity
            } else {
                (significand as $ity) << (exponent - significand_bits)
            };

            if sign == Sign::Negative {
                (!r).wrapping_add(1)
            } else {
                r
            }
        }
    }
}

fp_fix!(__fixsfsi: f32, i32);
fp_fix!(__fixsfdi: f32, i64);
fp_fix!(__fixdfsi: f64, i32);
fp_fix!(__fixdfdi: f64, i64);

fp_fix!(__fixunssfsi: f32, u32);
fp_fix!(__fixunssfdi: f32, u64);
fp_fix!(__fixunsdfsi: f64, u32);
fp_fix!(__fixunsdfdi: f64, u64);

// NOTE(cfg) for some reason, on arm*-unknown-linux-gnueabihf, our implementation doesn't
// match the output of its gcc_s or compiler-rt counterpart. Until we investigate further, we'll
// just avoid testing against them on those targets. Do note that our implementation gives the
// correct answer; gcc_s and compiler-rt are incorrect in this case.
//
#[cfg(all(test, not(arm_linux)))]
mod tests {
    use qc::{I32, U32, I64, U64, F32, F64};

    check! {
        fn __floatsisf(f: extern fn(i32) -> f32,
                    a: I32)
                    -> Option<F32> {
            Some(F32(f(a.0)))
        }
        fn __floatsidf(f: extern fn(i32) -> f64,
                    a: I32)
                    -> Option<F64> {
            Some(F64(f(a.0)))
        }
        fn __floatdidf(f: extern fn(i64) -> f64,
                    a: I64)
                    -> Option<F64> {
            Some(F64(f(a.0)))
        }
        fn __floatunsisf(f: extern fn(u32) -> f32,
                    a: U32)
                    -> Option<F32> {
            Some(F32(f(a.0)))
        }
        fn __floatunsidf(f: extern fn(u32) -> f64,
                    a: U32)
                    -> Option<F64> {
            Some(F64(f(a.0)))
        }
        fn __floatundidf(f: extern fn(u64) -> f64,
                    a: U64)
                    -> Option<F64> {
            Some(F64(f(a.0)))
        }

        fn __fixsfsi(f: extern fn(f32) -> i32,
                    a: F32)
                    -> Option<I32> {
            if a.0 > (i32::max_value() as f32) ||
               a.0 < (i32::min_value() as f32) || a.0.is_nan() {
                   None
           } else { Some(I32(f(a.0))) }
        }
        fn __fixsfdi(f: extern fn(f32) -> i64,
                    a: F32)
                    -> Option<I64> {
            if a.0 > (i64::max_value() as f32) ||
               a.0 < (i64::min_value() as f32) || a.0.is_nan() {
                   None
           } else { Some(I64(f(a.0))) }
        }
        fn __fixdfsi(f: extern fn(f64) -> i32,
                    a: F64)
                    -> Option<I32> {
            if a.0 > (i32::max_value() as f64) ||
               a.0 < (i32::min_value() as f64) || a.0.is_nan() {
                   None
           } else { Some(I32(f(a.0))) }
        }
        fn __fixdfdi(f: extern fn(f64) -> i64,
                    a: F64)
                    -> Option<I64> {
            if a.0 > (i64::max_value() as f64) ||
               a.0 < (i64::min_value() as f64) || a.0.is_nan() {
                   None
           } else { Some(I64(f(a.0))) }
        }

        fn __fixunssfsi(f: extern fn(f32) -> u32,
                    a: F32)
                    -> Option<U32> {
            if a.0 > (u32::max_value() as f32) ||
               a.0 < (u32::min_value() as f32) || a.0.is_nan() {
                   None
           } else { Some(U32(f(a.0))) }
        }
        fn __fixunssfdi(f: extern fn(f32) -> u64,
                    a: F32)
                    -> Option<U64> {
            if a.0 > (u64::max_value() as f32) ||
               a.0 < (u64::min_value() as f32) || a.0.is_nan() {
                   None
           } else { Some(U64(f(a.0))) }
        }
        fn __fixunsdfsi(f: extern fn(f64) -> u32,
                    a: F64)
                    -> Option<U32> {
            if a.0 > (u32::max_value() as f64) ||
               a.0 < (u32::min_value() as f64) || a.0.is_nan() {
                   None
           } else { Some(U32(f(a.0))) }
        }
        fn __fixunsdfdi(f: extern fn(f64) -> u64,
                    a: F64)
                    -> Option<U64> {
            if a.0 <= (u64::max_value() as f64) ||
               a.0 >= (u64::min_value() as f64) || a.0.is_nan() {
                   None
           } else { Some(U64(f(a.0))) }
        }
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use input::Key;

enum KeyState {
    Press,
    Release
}

pub struct KeyHandler {
    incoming: Vec<(KeyState, Key)>,
    last_press: Option<(Key, u64)>,
    keys: HashMap<Key, u64>
}

impl KeyHandler {
    pub fn new() -> KeyHandler {
        KeyHandler {
            incoming: vec![],
            last_press: None,
            keys: HashMap::new()
        }
    }

    pub fn press(&mut self, key: Key) {
        self.incoming.push((KeyState::Press, key));
    }

    pub fn release(&mut self, key: Key) {
        self.incoming.push((KeyState::Release, key));
    }

    pub fn think(&mut self, tick: u64) {
        // Digest each of the incoming items;
        for &(ref state, ref key) in self.incoming.iter() {
            match *state {
                KeyState::Press => {
                    self.last_press = Some((*key, tick));
                    self.keys.insert(*key, tick);
                },
                KeyState::Release => {
                    self.keys.remove(key);
                }
            }
        };
        self.incoming.clear();
    }

    pub fn last_key(&mut self) -> Option<(Key, u64)> {
        match self.last_press {
            Some((key, timestamp)) => {
                match self.keys.get(&key) {
                    Some(_) => {
                        Some((key, timestamp))
                    },
                    None => {
                        // Clear ourself out if we're not currently pressed
                        self.last_press = None;
                        Some((key, timestamp))
                    }
                }
            },
            None => None
        }
    }
}
<commit_msg>Removed redundant kleyhandler<commit_after><|endoftext|>


<commit_before>//! Krylov subspace

use crate::types::*;
use ndarray::*;

pub mod mgs;

pub use mgs::MGS;

/// Q-matrix
///
/// - Maybe **NOT** square
/// - Unitary for existing columns
///
pub type Q<A> = Array2<A>;

/// R-matrix
///
/// - Maybe **NOT** square
/// - Upper triangle
///
pub type R<A> = Array2<A>;

/// Strategy for linearly dependent vectors appearing in iterative QR decomposition
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Strategy {
    /// Terminate iteration if dependent vector comes
    Terminate,

    /// Skip dependent vector
    Skip,

    /// Orthogonalize dependent vector without adding to Q,
    /// i.e. R must be non-square like following:
    ///
    /// ```text
    /// x x x x x
    /// 0 x x x x
    /// 0 0 0 x x
    /// 0 0 0 0 x
    /// ```
    Full,
}
<commit_msg>Add Orthogonalizer trait and general impl of online QR decomposition<commit_after>//! Krylov subspace

use crate::types::*;
use ndarray::*;

pub mod mgs;

pub use mgs::MGS;

/// Q-matrix
///
/// - Maybe **NOT** square
/// - Unitary for existing columns
///
pub type Q<A> = Array2<A>;

/// R-matrix
///
/// - Maybe **NOT** square
/// - Upper triangle
///
pub type R<A> = Array2<A>;

pub trait Orthogonalizer {
    type Elem: Scalar;

    /// Dimension of input array
    fn dim(&self) -> usize;

    /// Number of cached basis
    fn len(&self) -> usize;

    /// check if the basis spans entire space
    fn is_full(&self) -> bool {
        self.len() == self.dim()
    }

    fn is_empty(&self) -> bool {
        self.len() == 0
    }

    /// Orthogonalize given vector using current basis
    ///
    /// Panic
    /// -------
    /// - if the size of the input array mismatches to the dimension
    ///
    fn orthogonalize<S>(&self, a: &mut ArrayBase<S, Ix1>) -> Array1<Self::Elem>
    where
        S: DataMut<Elem = Self::Elem>;

    /// Add new vector if the residual is larger than relative tolerance
    ///
    /// Returns
    /// --------
    /// Coefficients to the `i`-th Q-vector
    ///
    /// - The size of array must be `self.len() + 1`
    /// - The last element is the residual norm of input vector
    ///
    /// Panic
    /// -------
    /// - if the size of the input array mismatches to the dimension
    ///
    fn append<S>(
        &mut self,
        a: ArrayBase<S, Ix1>,
        rtol: <Self::Elem as Scalar>::Real,
    ) -> Result<Array1<Self::Elem>, Array1<Self::Elem>>
    where
        S: DataMut<Elem = Self::Elem>;

    /// Get Q-matrix of generated basis
    fn get_q(&self) -> Q<Self::Elem>;
}

/// Strategy for linearly dependent vectors appearing in iterative QR decomposition
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Strategy {
    /// Terminate iteration if dependent vector comes
    Terminate,

    /// Skip dependent vector
    Skip,

    /// Orthogonalize dependent vector without adding to Q,
    /// i.e. R must be non-square like following:
    ///
    /// ```text
    /// x x x x x
    /// 0 x x x x
    /// 0 0 0 x x
    /// 0 0 0 0 x
    /// ```
    Full,
}

/// Online QR decomposition using arbitrary orthogonalizer
pub fn qr<A, S>(
    iter: impl Iterator<Item = ArrayBase<S, Ix1>>,
    mut ortho: impl Orthogonalizer<Elem = A>,
    rtol: A::Real,
    strategy: Strategy,
) -> (Q<A>, R<A>)
where
    A: Scalar + Lapack,
    S: Data<Elem = A>,
{
    assert_eq!(ortho.len(), 0);

    let mut coefs = Vec::new();
    for a in iter {
        match ortho.append(a.into_owned(), rtol) {
            Ok(coef) => coefs.push(coef),
            Err(coef) => match strategy {
                Strategy::Terminate => break,
                Strategy::Skip => continue,
                Strategy::Full => coefs.push(coef),
            },
        }
    }
    let n = ortho.len();
    let m = coefs.len();
    let mut r = Array2::zeros((n, m).f());
    for j in 0..m {
        for i in 0..n {
            if i < coefs[j].len() {
                r[(i, j)] = coefs[j][i];
            }
        }
    }
    (ortho.get_q(), r)
}
<|endoftext|>


<commit_before>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#[forbid(deprecated_mode)];
//! Additional general-purpose comparison functionality.

use core::f32;
use core::f64;
use core::float;

const fuzzy_epsilon: float = 1.0e-6;

pub trait FuzzyEq {
    pure fn fuzzy_eq(&self, other: &self) -> bool;
    pure fn fuzzy_eq_eps(&self, other: &self, epsilon: &self) -> bool;
}

impl float: FuzzyEq {
    pure fn fuzzy_eq(&self, other: &float) -> bool {
        float::abs(*self - *other) < fuzzy_epsilon
    }

    pure fn fuzzy_eq_eps(&self, other: &float, epsilon: &float) -> bool {
        float::abs(*self - *other) < *epsilon
    }
}

impl f32: FuzzyEq {
    pure fn fuzzy_eq(&self, other: &f32) -> bool {
        f32::abs(*self - *other) < (fuzzy_epsilon as f32)
    }

    pure fn fuzzy_eq_eps(&self, other: &f32, epsilon: &f32) -> bool {
        f32::abs(*self - *other) < *epsilon
    }
}

impl f64: FuzzyEq {
    pure fn fuzzy_eq(&self, other: &f64) -> bool {
        f64::abs(*self - *other) < (fuzzy_epsilon as f64)
    }

    pure fn fuzzy_eq_eps(&self, other: &f64, epsilon: &f64) -> bool {
        f64::abs(*self - *other) < *epsilon
    }
}

#[test]
fn test_fuzzy_equals() {
    assert (&1.0).fuzzy_eq(&1.0);
    assert (&1.0f32).fuzzy_eq(&1.0f32);
    assert (&1.0f64).fuzzy_eq(&1.0f64);
}

#[test]
fn test_fuzzy_eq_eps() {
    assert (&1.2).fuzzy_eq_eps(&0.9, &0.5);
    assert !(&1.5).fuzzy_eq_eps(&0.9, &0.5);
}
<commit_msg>implement fuzzy_eq with fuzzy_eq_eps<commit_after>// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#[forbid(deprecated_mode)];
//! Additional general-purpose comparison functionality.

use core::f32;
use core::f64;
use core::float;

const fuzzy_epsilon: float = 1.0e-6;

pub trait FuzzyEq {
    pure fn fuzzy_eq(&self, other: &self) -> bool;
    pure fn fuzzy_eq_eps(&self, other: &self, epsilon: &self) -> bool;
}

impl float: FuzzyEq {
    pure fn fuzzy_eq(&self, other: &float) -> bool {
        self.fuzzy_eq_eps(other, fuzzy_epsilon)
    }

    pure fn fuzzy_eq_eps(&self, other: &float, epsilon: &float) -> bool {
        float::abs(*self - *other) < *epsilon
    }
}

impl f32: FuzzyEq {
    pure fn fuzzy_eq(&self, other: &f32) -> bool {
        self.fuzzy_eq_eps(other, fuzzy_epsilon as f32)
    }

    pure fn fuzzy_eq_eps(&self, other: &f32, epsilon: &f32) -> bool {
        f32::abs(*self - *other) < *epsilon
    }
}

impl f64: FuzzyEq {
    pure fn fuzzy_eq(&self, other: &f64) -> bool {
        self.fuzzy_eq_eps(other, fuzzy_epsilon as f64)
    }

    pure fn fuzzy_eq_eps(&self, other: &f64, epsilon: &f64) -> bool {
        f64::abs(*self - *other) < *epsilon
    }
}

#[test]
fn test_fuzzy_equals() {
    assert (&1.0).fuzzy_eq(&1.0);
    assert (&1.0f32).fuzzy_eq(&1.0f32);
    assert (&1.0f64).fuzzy_eq(&1.0f64);
}

#[test]
fn test_fuzzy_eq_eps() {
    assert (&1.2).fuzzy_eq_eps(&0.9, &0.5);
    assert !(&1.5).fuzzy_eq_eps(&0.9, &0.5);
}
<|endoftext|>


<commit_before>use std::collections::HashSet;
use std::io::Read;
use std::str::FromStr;

use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::error::Error;
use nom::sequence::tuple;
use nom::Finish;
use nom::IResult;

use crate::common::LineIter;
use crate::common::LineParser;

type Coords = (u16, u16);

enum Fold {
    X(u16),
    Y(u16),
}

impl FromStr for Fold {
    type Err = Error<String>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let (_, fold) = parse_fold(s)
            .finish()
            .map_err(|Error { input, code }| Error {
                input: input.to_string(),
                code,
            })?;

        Ok(fold)
    }
}

fn parse_coordinates(input: &str) -> IResult<&str, Coords> {
    use nom::character::complete::char;
    use nom::character::complete::u16;

    let (input, (x, _, y)) = tuple((u16, char(','), u16))(input)?;

    Ok((input, (x, y)))
}

fn parse_fold(input: &str) -> IResult<&str, Fold> {
    use nom::character::complete::char;
    use nom::character::complete::u16;

    let (input, (_, axis, _, coord)) = tuple((
        tag("fold along "),
        alt((char('x'), char('y'))),
        char('='),
        u16,
    ))(input)?;

    let fold = match axis {
        'x' => Fold::X(coord),
        'y' => Fold::Y(coord),
        _ => unreachable!("Should be filtered by nom"),
    };

    Ok((input, fold))
}

fn read_dots(reader: &mut LineIter<'_>) -> HashSet<Coords> {
    let mut dots = HashSet::new();

    while let Some(line) = reader.next() {
        if line.is_empty() {
            break;
        }

        let (_, coords) = parse_coordinates(line).unwrap();

        dots.insert(coords);
    }

    dots
}

fn apply_fold(dots: &mut HashSet<Coords>, fold: Fold, to_fold: &mut Vec<Coords>) {
    match fold {
        Fold::X(coord) => dots.retain(|&(x, y)| {
            if x < coord {
                true
            } else {
                to_fold.push((2 * coord - x, y));
                false
            }
        }),
        Fold::Y(coord) => dots.retain(|&(x, y)| {
            if y < coord {
                true
            } else {
                to_fold.push((x, 2 * coord - y));
                false
            }
        }),
    }

    dots.extend(to_fold.drain(..));
}

fn print_dots(dots: &HashSet<Coords>) -> String {
    let (x, y) = dots
        .iter()
        .fold((0, 0), |(xc, yc), &(xn, yn)| (xc.max(xn), yc.max(yn)));

    let mut buffer = String::with_capacity((x as usize + 1) * y as usize);

    for y in 0..=y {
        for x in 0..=x {
            if dots.contains(&(x, y)) {
                buffer.push('#');
            } else {
                buffer.push(' ');
            }
        }
        buffer.push('\n');
    }

    buffer.pop();

    buffer
}

pub fn part1(input: &mut dyn Read) -> String {
    let mut reader = LineIter::new(input);

    let mut dots = read_dots(&mut reader);

    let fold = reader.next().unwrap().parse().unwrap();
    apply_fold(&mut dots, fold, &mut Vec::new());

    dots.len().to_string()
}

pub fn part2(input: &mut dyn Read) -> String {
    let mut reader = LineIter::new(input);

    let mut dots = read_dots(&mut reader);
    let mut to_fold = Vec::new();

    LineParser::from(reader).for_each(|fold| apply_fold(&mut dots, fold, &mut to_fold));

    print_dots(&dots)
}

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

    use crate::test_implementation;

    const SAMPLE: &[u8] = include_bytes!("samples/13.txt");

    #[test]
    fn sample_part() {
        test_implementation(part1, SAMPLE, 17);
    }
}
<commit_msg>Don't use silly hashsets<commit_after>use std::io::Read;
use std::str::FromStr;

use itertools::Itertools;
use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::error::Error;
use nom::sequence::tuple;
use nom::Finish;
use nom::IResult;

use crate::common::LineIter;
use crate::common::LineParser;

type Coords = (u16, u16);

enum Fold {
    X(u16),
    Y(u16),
}

impl FromStr for Fold {
    type Err = Error<String>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let (_, fold) = parse_fold(s)
            .finish()
            .map_err(|Error { input, code }| Error {
                input: input.to_string(),
                code,
            })?;

        Ok(fold)
    }
}

fn parse_coordinates(input: &str) -> IResult<&str, Coords> {
    use nom::character::complete::char;
    use nom::character::complete::u16;

    let (input, (x, _, y)) = tuple((u16, char(','), u16))(input)?;

    Ok((input, (x, y)))
}

fn parse_fold(input: &str) -> IResult<&str, Fold> {
    use nom::character::complete::char;
    use nom::character::complete::u16;

    let (input, (_, axis, _, coord)) = tuple((
        tag("fold along "),
        alt((char('x'), char('y'))),
        char('='),
        u16,
    ))(input)?;

    let fold = match axis {
        'x' => Fold::X(coord),
        'y' => Fold::Y(coord),
        _ => unreachable!("Should be filtered by nom"),
    };

    Ok((input, fold))
}

fn read_dots(reader: &mut LineIter<'_>) -> Vec<Coords> {
    let mut dots = Vec::new();

    while let Some(line) = reader.next() {
        if line.is_empty() {
            break;
        }

        let (_, coords) = parse_coordinates(line).unwrap();

        dots.push(coords);
    }

    dots
}

fn apply_fold(dots: &mut Vec<Coords>, fold: Fold, to_fold: &mut Vec<Coords>) {
    match fold {
        Fold::X(coord) => dots.retain(|&(x, y)| {
            if x < coord {
                true
            } else {
                to_fold.push((2 * coord - x, y));
                false
            }
        }),
        Fold::Y(coord) => dots.retain(|&(x, y)| {
            if y < coord {
                true
            } else {
                to_fold.push((x, 2 * coord - y));
                false
            }
        }),
    }

    dots.append(to_fold);
}

fn print_dots(dots: &[Coords]) -> String {
    let (width, height) = dots.iter().fold((0, 0), |(xc, yc), &(xn, yn)| {
        (xc.max(xn as usize + 1), yc.max(yn as usize + 1))
    });

    let mut buffer = vec![b' '; width * height];

    for &(x, y) in dots {
        buffer[x as usize + width * y as usize] = b'#';
    }

    let mut result = String::with_capacity((width + 1) * height);

    for line in buffer.chunks_exact(width) {
        result += std::str::from_utf8(line).unwrap();
        result.push('\n');
    }

    result.pop();

    result
}

pub fn part1(input: &mut dyn Read) -> String {
    let mut reader = LineIter::new(input);

    let mut dots = read_dots(&mut reader);

    let fold = reader.next().unwrap().parse().unwrap();
    apply_fold(&mut dots, fold, &mut Vec::new());

    dots.sort_unstable();

    dots.into_iter().unique().count().to_string()
}

pub fn part2(input: &mut dyn Read) -> String {
    let mut reader = LineIter::new(input);

    let mut dots = read_dots(&mut reader);
    let mut to_fold = Vec::new();

    LineParser::from(reader).for_each(|fold| apply_fold(&mut dots, fold, &mut to_fold));

    print_dots(&dots)
}

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

    use crate::test_implementation;

    const SAMPLE: &[u8] = include_bytes!("samples/13.txt");

    #[test]
    fn sample_part() {
        test_implementation(part1, SAMPLE, 17);
    }
}
<|endoftext|>


<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::lint::engine::{prelude::*, LintContext};
use once_cell::sync::OnceCell;
use std::{
    ffi::OsStr,
    path::Path,
    process::{Command, Stdio},
};

/// Configuration for the lint engine.
#[derive(Clone, Debug)]
pub struct LintEngineConfig<'cfg> {
    project_root: &'cfg Path,
    content_linters: &'cfg [&'cfg (dyn ContentLinter + 'cfg)],
    fail_fast: bool,
}

impl<'cfg> LintEngineConfig<'cfg> {
    pub fn new(project_root: &'cfg Path) -> Self {
        Self {
            project_root,
            content_linters: &[],
            fail_fast: false,
        }
    }

    pub fn with_content_linters(
        &mut self,
        content_linters: &'cfg [&'cfg (dyn ContentLinter + 'cfg)],
    ) -> &mut Self {
        self.content_linters = content_linters;
        self
    }

    pub fn fail_fast(&mut self, fail_fast: bool) -> &mut Self {
        self.fail_fast = fail_fast;
        self
    }

    pub fn build(&self) -> LintEngine<'cfg> {
        LintEngine::new(self.clone())
    }
}

/// Executor for linters.
#[derive(Debug)]
pub struct LintEngine<'cfg> {
    config: LintEngineConfig<'cfg>,

    // Caches to allow results to live as long as the engine itself.
    files_stdout: OnceCell<Vec<u8>>,
}

impl<'cfg> LintEngine<'cfg> {
    pub fn new(config: LintEngineConfig<'cfg>) -> Self {
        Self {
            config,
            files_stdout: OnceCell::new(),
        }
    }

    pub fn run(&self) -> Result<LintResults> {
        let mut skipped = vec![];
        let mut messages = vec![];

        // TODO: add support for project and file lints.

        let project_ctx = ProjectContext::new(self.config.project_root);

        if !self.config.content_linters.is_empty() {
            let file_list = self.get_file_list()?;

            // TODO: This should probably be a worker queue with a thread pool or something.

            let file_ctxs = file_list
                .iter()
                .map(|path| FileContext::new(project_ctx, path));

            'outer: for file_ctx in file_ctxs {
                let linters_to_run = self
                    .config
                    .content_linters
                    .iter()
                    .copied()
                    .filter_map(|linter| match linter.pre_run(&file_ctx) {
                        Ok(RunStatus::Executed) => Some(Ok(linter)),
                        Ok(RunStatus::Skipped(reason)) => {
                            let source = file_ctx.source(linter.name());
                            skipped.push((source, reason));
                            None
                        }
                        Err(err) => Some(Err(err)),
                    })
                    .collect::<Result<Vec<_>>>()?;

                if linters_to_run.is_empty() {
                    // No linters to run for this file -- no point loading it.
                    continue;
                }

                // Load up the content for this file.
                let content_ctx = file_ctx.load()?;

                for linter in linters_to_run {
                    let source = content_ctx.source(linter.name());
                    let mut formatter = LintFormatter::new(source, &mut messages);

                    match linter.run(&content_ctx, &mut formatter)? {
                        RunStatus::Executed => {
                            // Yay! Lint ran successfully.
                        }
                        RunStatus::Skipped(reason) => {
                            skipped.push((source, reason));
                        }
                    }

                    if self.config.fail_fast && !messages.is_empty() {
                        // At least one issue was found.
                        break 'outer;
                    }
                }
            }
        }

        Ok(LintResults { skipped, messages })
    }

    // ---
    // Helper methods
    // ---

    fn get_file_list(&self) -> Result<Vec<&Path>> {
        // TODO: It'd be pretty cool to be able to cache the `&Path` instances as well. The options
        // are:
        // 1. Cache them as `PathBuf` instances. That would involve lots of allocations.
        // 2. Store the `&Path` instances alongside `files_stdout`. That would be a self-referential
        //    struct (so would require the use of something like
        //    [`rental`](https://docs.rs/rental/).
        // 3. Reorganize the code to use some sort of arena allocator which is passed into the lint
        //    context (and/or stored alongside with `rental`.)
        let stdout = self.files_stdout()?;

        Ok(stdout
            .split(|b| b == &0)
            .filter_map(|s| {
                // TODO: make global exclusions configurable.
                if !s.is_empty() && !s.starts_with(b"testsuite/libra-fuzzer/artifacts/") {
                    // `OsStr::from_bytes` only works on Unix, since "OS strings" on Windows are
                    // actually UTF-16ish. Would be cool to get it working on Windows at some point
                    // too, but it needs to be done with some care.
                    //
                    // For more, see:
                    // * https://doc.rust-lang.org/std/ffi/index.html#conversions
                    // * https://www.mercurial-scm.org/wiki/EncodingStrategy and
                    // * https://en.wikipedia.org/wiki/Mojibake.
                    use std::os::unix::ffi::OsStrExt;

                    let s = OsStr::from_bytes(s);
                    Some(Path::new(s))
                } else {
                    None
                }
            })
            .collect())
    }

    fn files_stdout(&self) -> Result<&[u8]> {
        self.files_stdout
            .get_or_try_init(|| {
                // TODO: abstract out SCM and command-running functionality.
                let output = Command::new("git")
                    .current_dir(self.config.project_root)
                    // The -z causes files to not be quoted, and to be separated by \0.
                    .args(&["ls-files", "-z"])
                    .stderr(Stdio::inherit())
                    .output()?;
                if !output.status.success() {
                    return Err(SystemError::Exec {
                        cmd: "git ls-files",
                        status: output.status,
                    });
                }
                Ok(output.stdout)
            })
            .map(|stdout| stdout.as_slice())
    }
}

#[derive(Debug)]
#[non_exhaustive]
pub struct LintResults<'l> {
    pub skipped: Vec<(LintSource<'l>, SkipReason<'l>)>,
    pub messages: Vec<(LintSource<'l>, LintMessage)>,
}
<commit_msg>[x] drop some unnecessary syntax<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::lint::engine::{prelude::*, LintContext};
use once_cell::sync::OnceCell;
use std::{
    ffi::OsStr,
    path::Path,
    process::{Command, Stdio},
};

/// Configuration for the lint engine.
#[derive(Clone, Debug)]
pub struct LintEngineConfig<'cfg> {
    project_root: &'cfg Path,
    content_linters: &'cfg [&'cfg dyn ContentLinter],
    fail_fast: bool,
}

impl<'cfg> LintEngineConfig<'cfg> {
    pub fn new(project_root: &'cfg Path) -> Self {
        Self {
            project_root,
            content_linters: &[],
            fail_fast: false,
        }
    }

    pub fn with_content_linters(
        &mut self,
        content_linters: &'cfg [&'cfg dyn ContentLinter],
    ) -> &mut Self {
        self.content_linters = content_linters;
        self
    }

    pub fn fail_fast(&mut self, fail_fast: bool) -> &mut Self {
        self.fail_fast = fail_fast;
        self
    }

    pub fn build(&self) -> LintEngine<'cfg> {
        LintEngine::new(self.clone())
    }
}

/// Executor for linters.
#[derive(Debug)]
pub struct LintEngine<'cfg> {
    config: LintEngineConfig<'cfg>,

    // Caches to allow results to live as long as the engine itself.
    files_stdout: OnceCell<Vec<u8>>,
}

impl<'cfg> LintEngine<'cfg> {
    pub fn new(config: LintEngineConfig<'cfg>) -> Self {
        Self {
            config,
            files_stdout: OnceCell::new(),
        }
    }

    pub fn run(&self) -> Result<LintResults> {
        let mut skipped = vec![];
        let mut messages = vec![];

        // TODO: add support for project and file lints.

        let project_ctx = ProjectContext::new(self.config.project_root);

        if !self.config.content_linters.is_empty() {
            let file_list = self.get_file_list()?;

            // TODO: This should probably be a worker queue with a thread pool or something.

            let file_ctxs = file_list
                .iter()
                .map(|path| FileContext::new(project_ctx, path));

            'outer: for file_ctx in file_ctxs {
                let linters_to_run = self
                    .config
                    .content_linters
                    .iter()
                    .copied()
                    .filter_map(|linter| match linter.pre_run(&file_ctx) {
                        Ok(RunStatus::Executed) => Some(Ok(linter)),
                        Ok(RunStatus::Skipped(reason)) => {
                            let source = file_ctx.source(linter.name());
                            skipped.push((source, reason));
                            None
                        }
                        Err(err) => Some(Err(err)),
                    })
                    .collect::<Result<Vec<_>>>()?;

                if linters_to_run.is_empty() {
                    // No linters to run for this file -- no point loading it.
                    continue;
                }

                // Load up the content for this file.
                let content_ctx = file_ctx.load()?;

                for linter in linters_to_run {
                    let source = content_ctx.source(linter.name());
                    let mut formatter = LintFormatter::new(source, &mut messages);

                    match linter.run(&content_ctx, &mut formatter)? {
                        RunStatus::Executed => {
                            // Yay! Lint ran successfully.
                        }
                        RunStatus::Skipped(reason) => {
                            skipped.push((source, reason));
                        }
                    }

                    if self.config.fail_fast && !messages.is_empty() {
                        // At least one issue was found.
                        break 'outer;
                    }
                }
            }
        }

        Ok(LintResults { skipped, messages })
    }

    // ---
    // Helper methods
    // ---

    fn get_file_list(&self) -> Result<Vec<&Path>> {
        // TODO: It'd be pretty cool to be able to cache the `&Path` instances as well. The options
        // are:
        // 1. Cache them as `PathBuf` instances. That would involve lots of allocations.
        // 2. Store the `&Path` instances alongside `files_stdout`. That would be a self-referential
        //    struct (so would require the use of something like
        //    [`rental`](https://docs.rs/rental/).
        // 3. Reorganize the code to use some sort of arena allocator which is passed into the lint
        //    context (and/or stored alongside with `rental`.)
        let stdout = self.files_stdout()?;

        Ok(stdout
            .split(|b| b == &0)
            .filter_map(|s| {
                // TODO: make global exclusions configurable.
                if !s.is_empty() && !s.starts_with(b"testsuite/libra-fuzzer/artifacts/") {
                    // `OsStr::from_bytes` only works on Unix, since "OS strings" on Windows are
                    // actually UTF-16ish. Would be cool to get it working on Windows at some point
                    // too, but it needs to be done with some care.
                    //
                    // For more, see:
                    // * https://doc.rust-lang.org/std/ffi/index.html#conversions
                    // * https://www.mercurial-scm.org/wiki/EncodingStrategy and
                    // * https://en.wikipedia.org/wiki/Mojibake.
                    use std::os::unix::ffi::OsStrExt;

                    let s = OsStr::from_bytes(s);
                    Some(Path::new(s))
                } else {
                    None
                }
            })
            .collect())
    }

    fn files_stdout(&self) -> Result<&[u8]> {
        self.files_stdout
            .get_or_try_init(|| {
                // TODO: abstract out SCM and command-running functionality.
                let output = Command::new("git")
                    .current_dir(self.config.project_root)
                    // The -z causes files to not be quoted, and to be separated by \0.
                    .args(&["ls-files", "-z"])
                    .stderr(Stdio::inherit())
                    .output()?;
                if !output.status.success() {
                    return Err(SystemError::Exec {
                        cmd: "git ls-files",
                        status: output.status,
                    });
                }
                Ok(output.stdout)
            })
            .map(|stdout| stdout.as_slice())
    }
}

#[derive(Debug)]
#[non_exhaustive]
pub struct LintResults<'l> {
    pub skipped: Vec<(LintSource<'l>, SkipReason<'l>)>,
    pub messages: Vec<(LintSource<'l>, LintMessage)>,
}
<|endoftext|>