<commit_before>//! Implements search parallelization facilities.

use std::thread;
use std::cmp::{min, max};
use std::cell::UnsafeCell;
use std::sync::Arc;
use std::sync::mpsc::{channel, Sender, Receiver, RecvError};
use basetypes::*;
use moves::*;
use position::Position;
use tt::*;
use search::INITIAL_ASPIRATION_WINDOW;
use search::alpha_beta::Search;

/// Represents a command to a search thread.
pub enum Command {
    /// Requests a new search.
    Search {
        /// A number identifying the new search.
        search_id: usize,

        /// The root position.
        position: Position,

        /// The requested search depth.
        depth: u8,

        /// The lower bound for the new search.
        lower_bound: Value,

        /// The upper bound for the new search.
        upper_bound: Value,
    },

    /// Stops the currently running search.
    Stop,

    /// Stops the currently running search and exits the search
    /// thread.
    Exit,
}

/// Represents progress report from a search thread.
pub struct Report {
    /// The ID passed with the search command.
    pub search_id: usize,

    /// The number of positions searched so far.
    pub searched_nodes: NodeCount,

    /// The search depth completed so far.
    pub depth: u8,

    /// The evaluation of the root position so far.
    pub value: Value,

    /// `true` if the search is finished or has been stopped, `false`
    /// otherwise.
    pub done: bool,
}

/// Listens for commands, executes simple searches, sends reports
/// back.
///
/// This function will block and wait to receive commands on the
/// `commands` channel to start, stop, or exit searches. It is
/// intended to be called in a separate thread. While the search is
/// executed, regular `Report` messages will be send back to the
/// master thread via the `reports` channel. When the search is done,
/// the final `Report` message will have its `done` field set to
/// `true`.
///
/// # Example:
///
/// ```rust
/// // Spawn a slave thread:
/// let tt = Arc::new(tt);
/// let (commands_tx, commands_rx) = channel();
/// let (reports_tx, reports_rx) = channel();
/// thread::spawn(move || {
///     serve_simple(tt, commands_rx, reports_tx);
/// });
///
/// // Send a command to start a new search:
/// commands_tx.send(Command::Search {
///     search_id: 0,
///     position: Position::form_fen("8/8/8/8/8/8/7P/5k1K b - - 0 99"),
///     depth: 5,
///     lower_bound: -29999,
///     upper_bound: 29999,
/// }).unwrap();
/// ```
///
/// This function executes sequential (non-parallel) search to a fixed
/// depth.
pub fn serve_simple(tt: Arc<Tt>, 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 command.
            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 } => {
                    debug_assert!(lower_bound < upper_bound);
                    let mut report = |searched_nodes| {
                        reports.send(Report {
                                   search_id: search_id,
                                   searched_nodes: searched_nodes,
                                   depth: 0,
                                   value: VALUE_UNKNOWN,
                                   done: false,
                               })
                               .ok();
                        if let Ok(cmd) = commands.try_recv() {
                            pending_command = Some(cmd);
                            true
                        } else {
                            false
                        }
                    };
                    let mut search = Search::new(position, &tt, move_stack, &mut report);
                    let value = search.run(lower_bound, upper_bound, depth, Move::invalid())
                                      .unwrap_or(VALUE_UNKNOWN);
                    reports.send(Report {
                               search_id: search_id,
                               searched_nodes: search.node_count(),
                               depth: if value == VALUE_UNKNOWN {
                                   0
                               } else {
                                   depth
                               },
                               value: value,
                               done: true,
                           })
                           .ok();
                    search.reset();
                }

                Command::Stop => continue,

                Command::Exit => break,
            }
        }
    })
}

/// Listens for commands, executes deepening searches, sends reports
/// back.
///
/// This function will block and wait to receive commands on the
/// `commands` channel to start, stop, or exit searches. It is
/// intended to be called in a separate thread. While the search is
/// executed, regular `Report` messages will be send back to the
/// master thread via the `reports` channel. When the search is done,
/// the final `Report` message will have its `done` field set to
/// `true`.
///
/// # Example:
///
/// ```rust
/// // Spawn a slave thread:
/// let tt = Arc::new(tt);
/// let (commands_tx, commands_rx) = channel();
/// let (reports_tx, reports_rx) = channel();
/// thread::spawn(move || {
///     serve_deepening(tt, commands_rx, reports_tx);
/// });
///
/// // Send a command to start a new search:
/// commands_tx.send(Command::Search {
///     search_id: 0,
///     position: Position::form_fen("8/8/8/8/8/8/7P/5k1K b - - 0 99"),
///     depth: 5,
///     lower_bound: -29999,
///     upper_bound: 29999,
/// }).unwrap();
/// ```
///
/// This function executes a deepening search with aspiration
/// window. It starts at depth `1` and consequently increases it until
/// the specified final depth is reached.
pub fn serve_deepening(tt: Arc<Tt>, commands: Receiver<Command>, reports: Sender<Report>) {
    // Start a slave thread that will be commanded to run searches
    // with increasing depths (search deepening).
    let (slave_commands_tx, slave_commands_rx) = channel();
    let (slave_reports_tx, slave_reports_rx) = channel();
    let slave = thread::spawn(move || {
        serve_simple(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 } => {
                debug_assert!(lower_bound < upper_bound);
                let mut current_searched_nodes = 0;
                let mut current_value = VALUE_UNKNOWN;
                let mut current_depth = 1;

                'depthloop: while current_depth <= depth {
                    // First we set up the aspiration window. Aspiration windows are a way
                    // to reduce the search space in the search. We use `current_value`
                    // from the last iteration of `depth`, and calculate a window around
                    // this as the alpha-beta bounds. Because the window is narrower, more
                    // beta cutoffs are achieved, and the search takes a shorter time. The
                    // drawback is that if the true score is outside this window, then a
                    // costly re-search must be made. But then most probably the re-search
                    // will be much faster, because many positions will be remembered from
                    // the TT.
                    //
                    // Here `delta` is the initial half-width of the window, that will be
                    // exponentially increased each time the search failed. We use `isize`
                    // type to avoid overflows.
                    let mut delta = INITIAL_ASPIRATION_WINDOW as isize;
                    let (mut alpha, mut beta) = if current_depth < 5 {
                        (lower_bound, upper_bound)
                    } else {
                        let v = current_value as isize;
                        (max(lower_bound as isize, v - delta) as Value,
                         min(v + delta, upper_bound as isize) as Value)
                    };

                    'aspiration: loop {
                        if alpha >= beta {
                            // This may happen if (v - delta, v + delta) and
                            // (lower_bound, upper_bound) do not intersect.
                            alpha = lower_bound;
                            beta = upper_bound;
                        }

                        // Command the slave thread to run a search.
                        slave_commands_tx.send(Command::Search {
                                             search_id: current_depth as usize,
                                             position: position.clone(),
                                             depth: current_depth,
                                             lower_bound: alpha,
                                             upper_bound: beta,
                                         })
                                         .unwrap();

                        'report: loop {
                            // In this loop we process the reports coming from the slave
                            // thread, but we also constantly check if there is a new
                            // pending command for us, in which case we have to terminate
                            // the search.
                            let Report { searched_nodes, value, done, .. } =
                                slave_reports_rx.recv().unwrap();
                            reports.send(Report {
                                       search_id: search_id,
                                       searched_nodes: current_searched_nodes + searched_nodes,
                                       depth: current_depth - 1,
                                       value: current_value,
                                       done: false,
                                   })
                                   .ok();
                            if pending_command.is_none() {
                                if done {
                                    current_searched_nodes += searched_nodes;
                                    current_value = value;
                                    break 'report;
                                }
                                if let Ok(cmd) = commands.try_recv() {
                                    slave_commands_tx.send(Command::Stop).unwrap();
                                    pending_command = Some(cmd);
                                }
                            }
                            if done {
                                current_searched_nodes += searched_nodes;
                                break 'depthloop;
                            }
                        } // end of 'report

                        // Check if the `current_value` is within the aspiration window
                        // (alpha, beta). If not so, we must consider running a re-search.
                        let v = current_value as isize;
                        if current_value <= alpha && lower_bound < alpha {
                            alpha = max(lower_bound as isize, v - delta) as Value;
                        } else if current_value >= beta && upper_bound > beta {
                            beta = min(v + delta, upper_bound as isize) as Value;
                        } else {
                            break 'aspiration;
                        }

                        // Increase the half-width of the aspiration window.
                        delta += 3 * delta / 8;
                        if delta > 1500 {
                            delta = 1_000_000;
                        }

                    } // end of 'aspiration

                    // Send a progress report with `current_value` for
                    // every completed depth.
                    reports.send(Report {
                               search_id: search_id,
                               searched_nodes: current_searched_nodes,
                               depth: current_depth,
                               value: current_value,
                               done: false,
                           })
                           .ok();
                    current_depth += 1;

                } // end of 'depthloop

                // The search is done -- send a final report.
                reports.send(Report {
                           search_id: search_id,
                           searched_nodes: current_searched_nodes,
                           depth: current_depth - 1,
                           value: current_value,
                           done: true,
                       })
                       .ok();
            }

            Command::Stop => {
                slave_commands_tx.send(Command::Stop).unwrap();
                continue;
            }

            Command::Exit => {
                slave_commands_tx.send(Command::Exit).unwrap();
                break;
            }
        }
    }
    slave.join().unwrap();
}
<commit_msg>added comment<commit_after>//! Implements search parallelization facilities.

use std::thread;
use std::cmp::{min, max};
use std::cell::UnsafeCell;
use std::sync::Arc;
use std::sync::mpsc::{channel, Sender, Receiver, RecvError};
use basetypes::*;
use moves::*;
use position::Position;
use tt::*;
use search::INITIAL_ASPIRATION_WINDOW;
use search::alpha_beta::Search;

/// Represents a command to a search thread.
pub enum Command {
    /// Requests a new search.
    Search {
        /// A number identifying the new search.
        search_id: usize,

        /// The root position.
        position: Position,

        /// The requested search depth.
        depth: u8,

        /// The lower bound for the new search.
        lower_bound: Value,

        /// The upper bound for the new search.
        upper_bound: Value,
    },

    /// Stops the currently running search.
    Stop,

    /// Stops the currently running search and exits the search
    /// thread.
    Exit,
}

/// Represents progress report from a search thread.
pub struct Report {
    /// The ID passed with the search command.
    pub search_id: usize,

    /// The number of positions searched so far.
    pub searched_nodes: NodeCount,

    /// The search depth completed so far.
    pub depth: u8,

    /// The evaluation of the root position so far, or `VALUE_UNKNOWN`
    /// if not available.
    pub value: Value,

    /// `true` if the search is finished or has been stopped, `false`
    /// otherwise.
    pub done: bool,
}

/// Listens for commands, executes simple searches, sends reports
/// back.
///
/// This function will block and wait to receive commands on the
/// `commands` channel to start, stop, or exit searches. It is
/// intended to be called in a separate thread. While the search is
/// executed, regular `Report` messages will be send back to the
/// master thread via the `reports` channel. When the search is done,
/// the final `Report` message will have its `done` field set to
/// `true`.
///
/// # Example:
///
/// ```rust
/// // Spawn a slave thread:
/// let tt = Arc::new(tt);
/// let (commands_tx, commands_rx) = channel();
/// let (reports_tx, reports_rx) = channel();
/// thread::spawn(move || {
///     serve_simple(tt, commands_rx, reports_tx);
/// });
///
/// // Send a command to start a new search:
/// commands_tx.send(Command::Search {
///     search_id: 0,
///     position: Position::form_fen("8/8/8/8/8/8/7P/5k1K b - - 0 99"),
///     depth: 5,
///     lower_bound: -29999,
///     upper_bound: 29999,
/// }).unwrap();
/// ```
///
/// This function executes sequential (non-parallel) search to a fixed
/// depth.
pub fn serve_simple(tt: Arc<Tt>, 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 command.
            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 } => {
                    debug_assert!(lower_bound < upper_bound);
                    let mut report = |searched_nodes| {
                        reports.send(Report {
                                   search_id: search_id,
                                   searched_nodes: searched_nodes,
                                   depth: 0,
                                   value: VALUE_UNKNOWN,
                                   done: false,
                               })
                               .ok();
                        if let Ok(cmd) = commands.try_recv() {
                            pending_command = Some(cmd);
                            true
                        } else {
                            false
                        }
                    };
                    let mut search = Search::new(position, &tt, move_stack, &mut report);
                    let value = search.run(lower_bound, upper_bound, depth, Move::invalid())
                                      .unwrap_or(VALUE_UNKNOWN);
                    reports.send(Report {
                               search_id: search_id,
                               searched_nodes: search.node_count(),
                               depth: if value == VALUE_UNKNOWN {
                                   0
                               } else {
                                   depth
                               },
                               value: value,
                               done: true,
                           })
                           .ok();
                    search.reset();
                }

                Command::Stop => continue,

                Command::Exit => break,
            }
        }
    })
}

/// Listens for commands, executes deepening searches, sends reports
/// back.
///
/// This function will block and wait to receive commands on the
/// `commands` channel to start, stop, or exit searches. It is
/// intended to be called in a separate thread. While the search is
/// executed, regular `Report` messages will be send back to the
/// master thread via the `reports` channel. When the search is done,
/// the final `Report` message will have its `done` field set to
/// `true`.
///
/// # Example:
///
/// ```rust
/// // Spawn a slave thread:
/// let tt = Arc::new(tt);
/// let (commands_tx, commands_rx) = channel();
/// let (reports_tx, reports_rx) = channel();
/// thread::spawn(move || {
///     serve_deepening(tt, commands_rx, reports_tx);
/// });
///
/// // Send a command to start a new search:
/// commands_tx.send(Command::Search {
///     search_id: 0,
///     position: Position::form_fen("8/8/8/8/8/8/7P/5k1K b - - 0 99"),
///     depth: 5,
///     lower_bound: -29999,
///     upper_bound: 29999,
/// }).unwrap();
/// ```
///
/// This function executes a deepening search with aspiration
/// window. It starts at depth `1` and consequently increases it until
/// the specified final depth is reached.
pub fn serve_deepening(tt: Arc<Tt>, commands: Receiver<Command>, reports: Sender<Report>) {
    // Start a slave thread that will be commanded to run searches
    // with increasing depths (search deepening).
    let (slave_commands_tx, slave_commands_rx) = channel();
    let (slave_reports_tx, slave_reports_rx) = channel();
    let slave = thread::spawn(move || {
        serve_simple(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 } => {
                debug_assert!(lower_bound < upper_bound);
                let mut current_searched_nodes = 0;
                let mut current_value = VALUE_UNKNOWN;
                let mut current_depth = 1;

                'depthloop: while current_depth <= depth {
                    // First we set up the aspiration window. Aspiration windows are a way
                    // to reduce the search space in the search. We use `current_value`
                    // from the last iteration of `depth`, and calculate a window around
                    // this as the alpha-beta bounds. Because the window is narrower, more
                    // beta cutoffs are achieved, and the search takes a shorter time. The
                    // drawback is that if the true score is outside this window, then a
                    // costly re-search must be made. But then most probably the re-search
                    // will be much faster, because many positions will be remembered from
                    // the TT.
                    //
                    // Here `delta` is the initial half-width of the window, that will be
                    // exponentially increased each time the search failed. We use `isize`
                    // type to avoid overflows.
                    let mut delta = INITIAL_ASPIRATION_WINDOW as isize;
                    let (mut alpha, mut beta) = if current_depth < 5 {
                        (lower_bound, upper_bound)
                    } else {
                        let v = current_value as isize;
                        (max(lower_bound as isize, v - delta) as Value,
                         min(v + delta, upper_bound as isize) as Value)
                    };

                    'aspiration: loop {
                        if alpha >= beta {
                            // This may happen if (v - delta, v + delta) and
                            // (lower_bound, upper_bound) do not intersect.
                            alpha = lower_bound;
                            beta = upper_bound;
                        }

                        // Command the slave thread to run a search.
                        slave_commands_tx.send(Command::Search {
                                             search_id: current_depth as usize,
                                             position: position.clone(),
                                             depth: current_depth,
                                             lower_bound: alpha,
                                             upper_bound: beta,
                                         })
                                         .unwrap();

                        'report: loop {
                            // In this loop we process the reports coming from the slave
                            // thread, but we also constantly check if there is a new
                            // pending command for us, in which case we have to terminate
                            // the search.
                            let Report { searched_nodes, value, done, .. } =
                                slave_reports_rx.recv().unwrap();
                            reports.send(Report {
                                       search_id: search_id,
                                       searched_nodes: current_searched_nodes + searched_nodes,
                                       depth: current_depth - 1,
                                       value: current_value,
                                       done: false,
                                   })
                                   .ok();
                            if pending_command.is_none() {
                                if done {
                                    current_searched_nodes += searched_nodes;
                                    current_value = value;
                                    break 'report;
                                }
                                if let Ok(cmd) = commands.try_recv() {
                                    slave_commands_tx.send(Command::Stop).unwrap();
                                    pending_command = Some(cmd);
                                }
                            }
                            if done {
                                current_searched_nodes += searched_nodes;
                                break 'depthloop;
                            }
                        } // end of 'report

                        // Check if the `current_value` is within the aspiration window
                        // (alpha, beta). If not so, we must consider running a re-search.
                        let v = current_value as isize;
                        if current_value <= alpha && lower_bound < alpha {
                            alpha = max(lower_bound as isize, v - delta) as Value;
                        } else if current_value >= beta && upper_bound > beta {
                            beta = min(v + delta, upper_bound as isize) as Value;
                        } else {
                            break 'aspiration;
                        }

                        // Increase the half-width of the aspiration window.
                        delta += 3 * delta / 8;
                        if delta > 1500 {
                            delta = 1_000_000;
                        }

                    } // end of 'aspiration

                    // Send a progress report with `current_value` for
                    // every completed depth.
                    reports.send(Report {
                               search_id: search_id,
                               searched_nodes: current_searched_nodes,
                               depth: current_depth,
                               value: current_value,
                               done: false,
                           })
                           .ok();
                    current_depth += 1;

                } // end of 'depthloop

                // The search is done -- send a final report.
                reports.send(Report {
                           search_id: search_id,
                           searched_nodes: current_searched_nodes,
                           depth: current_depth - 1,
                           value: current_value,
                           done: true,
                       })
                       .ok();
            }

            Command::Stop => {
                slave_commands_tx.send(Command::Stop).unwrap();
                continue;
            }

            Command::Exit => {
                slave_commands_tx.send(Command::Exit).unwrap();
                break;
            }
        }
    }
    slave.join().unwrap();
}
<|endoftext|>


<commit_before>extern crate deeplearn;
extern crate gpuarray as ga;

use std::collections::{HashMap, HashSet};
use std::fs::File;
use std::io::{
    self,
    BufReader,
    Write,
};
use std::path::Path;
use std::rc::Rc;

use deeplearn::{init, layers, util, train, Graph};
use deeplearn::op::Softmax;
use deeplearn::train::Optimizer;
use ga::Array;

fn main() {
    let batch_size = 1;

    let (char_map, rev_char_map, lines) = load_char_rnn_data("data/bible.txt").unwrap();
    let char_classes = char_map.len();
    println!("Loaded char rnn data");
    println!("Character types: {}", char_classes);

    ////////////////////////////////////////////////////////////////////////////////////////////////
    // Build the graph

    let batch_size = 1;
    let l1_size = 200;
    let l2_size = 200;

    let ctx = Rc::new(ga::Context::new());
    let ref mut graph = Graph::new(ctx.clone());

    let l1_w = graph.add_variable(vec![1+char_classes+l1_size, 4*l1_size], true, init::Normal(0.001, 0.003));
    let l2_w = graph.add_variable(vec![1+l1_size+l2_size, 4*l2_size], true, init::Normal(0.001, 0.003));
    let l3_w = graph.add_variable(vec![l2_size, char_classes], true, init::Normal(0.001, 0.005));
    let l3_b = graph.add_variable(vec![1, char_classes], true, init::Normal(0.001, 0.005));
    let l1_c0 = graph.add_variable(vec![batch_size, l1_size], false, 0.0);
    let l1_h0 = graph.add_variable(vec![batch_size, l1_size], false, 0.0);
    let l2_c0 = graph.add_variable(vec![batch_size, l2_size], false, 0.0);
    let l2_h0 = graph.add_variable(vec![batch_size, l2_size], false, 0.0);

    let net_step =
        |graph: &mut Graph, (l1_prev_h, l1_prev_c, l2_prev_h, l2_prev_c)| {
            let input = graph.add_variable(vec![batch_size, char_classes], false, 0.0);
            let (l1_out, l1_c) = layers::lstm_unrolled(graph, input, l1_w, l1_prev_h, l1_prev_c);
            let (l2_out, l2_c) = layers::lstm_unrolled(graph, l1_out, l2_w, l2_prev_h, l2_prev_c);
            let l3_fcb = layers::dense_biased_manual(graph, l2_out, l3_w, l3_b);
            let l3_out = layers::activation(graph, Softmax(l3_fcb));
            // Loss
            let (loss_out, train_out) = layers::cross_entropy(graph, l3_out);
            let loss_d = graph.add_gradient(loss_out); // Create a gradient to apply to the loss function
            // We apply a gradient of -0.1 to the loss function
            loss_d.write(graph, &Array::new(loss_d.get(graph).shape().to_owned(), -1.0));

            ((l1_out, l1_c, l2_out, l2_c), (input, l3_out, train_out))
        };

    let (last_recur_in, steps) = util::unrolled_net(graph, 25, (l1_h0, l1_c0, l2_h0, l2_c0), net_step);

    ////////////////////////////////////////////////////////////////////////////////////////////////
    // Train and validate the network

    let mut l2_out_cpu = Array::new(vec![batch_size, l2_size], 0.0);
    let mut l3_out_cpu = Array::new(vec![batch_size, char_classes], 0.0);
    let mut l3_out_d_cpu = Array::new(vec![batch_size, char_classes], 0.0);
    let mut l1_w_cpu = Array::new(vec![1+char_classes+l1_size, 4*l1_size], 0.0);
    let mut argmax_out = Array::new(vec![batch_size], 0usize);

    let rmsprop = train::RmsProp::new(graph, 0.01, 0.9);

    let samples = 10000;
    let mut i = 0;
    for line in &lines {
        for (t, &(input, _, train_out)) in (1..line.len()).zip(steps.iter()) {
            input.write(graph, &char_map[&line[t-1]]);
            train_out.write(graph, &char_map[&line[t]]);
        }

        for (t, &(_, _, _)) in (1..line.len()).zip(steps.iter()) {
            print!("{}", line[t] as char);
        }
        println!("");

        graph.forward();
        graph.backward();

        for (_, &(_, l3_out, _)) in (1..line.len()).zip(steps.iter()) {
            l3_out.read(graph, &mut l3_out_cpu);
            //l1_w.read(graph, &mut l1_w_cpu);
            //graph.get_gradient(l3_out).read(graph, &mut l3_out_d_cpu);
            //graph.get_gradient(l1_w).read(graph, &mut l1_w_cpu);
            //graph.get_gradient(last_recur_in.2).read(graph, &mut l2_out_cpu);
            //println!("{:?}", l3_out_cpu);
            //println!("{:?}", l3_out_d_cpu);
            //println!("{:?}", l1_w_cpu);
            //println!("{:?}", l2_out_cpu);
            util::argmax_rows(&l3_out_cpu, &mut argmax_out);
            let next_char = rev_char_map[&argmax_out[&[0]]];
            print!("{}", next_char as char);
        }
        println!("");

        //train::apply_gradients(graph);
        rmsprop.update(graph);

        println!("{}", i);

        i += 1;
        if i > samples {
            break;
        }
    }

    /*let mut l3_out_cpu = Array::new(vec![batch_size, char_classes], 0.0);
    let mut argmax_out = Array::new(vec![batch_size], 0usize);

    loop {
        // Print prompt
        print!(">");
        io::stdout().flush().unwrap();

        // Get seed string
        let mut seed = String::new();
        io::stdin().read_line(&mut seed).unwrap();

        // Input the seed string
        let mut last_char = 0u8;
        for c in seed.trim_right().as_bytes() {
            print!("{}", *c as char);
            input.write(graph, &char_map[c]);
            graph.forward();
            l3_out.read(graph, &mut l3_out_cpu);
            util::argmax_rows(&l3_out_cpu, &mut argmax_out);
            last_char = rev_char_map[&argmax_out[&[0]]];
        }

        // Generate the rest of the output
        for _ in 0..150 {
            print!("{}", last_char as char);
            io::stdout().flush().unwrap();

            input.write(graph, &char_map[&last_char]);
            graph.forward();
            l3_out.read(graph, &mut l3_out_cpu);
            //println!("{:?}", l3_out_cpu);
            util::argmax_rows(&l3_out_cpu, &mut argmax_out);
            last_char = rev_char_map[&argmax_out[&[0]]];

            if last_char == b'\n' {
                break;
            }
        }
        println!("");
    }*/
}

pub fn load_char_rnn_data<P: AsRef<Path>>(path: P)
    -> io::Result<(HashMap<u8, Array<f32>>, HashMap<usize, u8>, Vec<Vec<u8>>)>
{
    use std::io::BufRead;

    let ref mut file = BufReader::new(File::open(path).unwrap());

    let mut unique_chars = HashSet::new();
    let mut lines = vec![];

    for line in file.lines() {
        let line: String = try!(line) + "\n";
        for c in line.as_bytes() {
            unique_chars.insert(*c);
        }
        lines.push(line.as_bytes().to_owned());
    }

    let char_classes = unique_chars.len();
    let mut char_map = HashMap::new();
    let mut rev_char_map = HashMap::new();
    for (i, c) in unique_chars.into_iter().enumerate() {
        char_map.insert(c, util::one_hot_row(i, char_classes));
        rev_char_map.insert(i, c);
    }

    Ok((char_map, rev_char_map, lines))
}
<commit_msg>Having fully connected layer after LSTMs for char-rnn model was kinda weird<commit_after>extern crate deeplearn;
extern crate gpuarray as ga;

use std::collections::{HashMap, HashSet};
use std::fs::File;
use std::io::{
    self,
    BufReader,
    Write,
};
use std::path::Path;
use std::rc::Rc;

use deeplearn::{init, layers, util, train, Graph};
use deeplearn::op::Softmax;
use deeplearn::train::Optimizer;
use ga::Array;

fn main() {
    let batch_size = 1;

    let (char_map, rev_char_map, lines) = load_char_rnn_data("data/bible_no_verse.txt").unwrap();
    let char_classes = char_map.len();
    println!("Loaded char rnn data");
    println!("Character types: {}", char_classes);

    ////////////////////////////////////////////////////////////////////////////////////////////////
    // Build the graph

    let batch_size = 1;
    let l1_size = 200;
    let l2_size = char_classes;

    let ctx = Rc::new(ga::Context::new());
    let ref mut graph = Graph::new(ctx.clone());

    let l1_w = graph.add_variable(vec![1+char_classes+l1_size, 4*l1_size], true, init::Normal(0.001, 0.003));
    let l2_w = graph.add_variable(vec![1+l1_size+l2_size, 4*l2_size], true, init::Normal(0.001, 0.003));
    //let l3_w = graph.add_variable(vec![l2_size, char_classes], true, init::Normal(0.001, 0.005));
    //let l3_b = graph.add_variable(vec![1, char_classes], true, init::Normal(0.001, 0.005));
    let l1_c0 = graph.add_variable(vec![batch_size, l1_size], false, 0.0);
    let l1_h0 = graph.add_variable(vec![batch_size, l1_size], false, 0.0);
    let l2_c0 = graph.add_variable(vec![batch_size, l2_size], false, 0.0);
    let l2_h0 = graph.add_variable(vec![batch_size, l2_size], false, 0.0);

    let net_step =
        |graph: &mut Graph, (l1_prev_h, l1_prev_c, l2_prev_h, l2_prev_c)| {
            let input = graph.add_variable(vec![batch_size, char_classes], false, 0.0);
            let (l1_out, l1_c) = layers::lstm_unrolled(graph, input, l1_w, l1_prev_h, l1_prev_c);
            let (l2_out, l2_c) = layers::lstm_unrolled(graph, l1_out, l2_w, l2_prev_h, l2_prev_c);
            //let l3_fcb = layers::dense_biased_manual(graph, l2_out, l3_w, l3_b);
            //let l3_out = layers::activation(graph, Softmax(l3_fcb));
            // Loss
            let (loss_out, train_out) = layers::cross_entropy(graph, l2_out);
            let loss_d = graph.add_gradient(loss_out); // Create a gradient to apply to the loss function
            // We apply a gradient of -0.1 to the loss function
            loss_d.write(graph, &Array::new(loss_d.get(graph).shape().to_owned(), -1.0));

            ((l1_out, l1_c, l2_out, l2_c), (input, l2_out, train_out))
        };

    let (last_recur_in, steps) = util::unrolled_net(graph, 25, (l1_h0, l1_c0, l2_h0, l2_c0), net_step);

    ////////////////////////////////////////////////////////////////////////////////////////////////
    // Train and validate the network

    let mut l2_out_cpu = Array::new(vec![batch_size, l2_size], 0.0);
    let mut l3_out_cpu = Array::new(vec![batch_size, char_classes], 0.0);
    let mut l3_out_d_cpu = Array::new(vec![batch_size, char_classes], 0.0);
    let mut l1_w_cpu = Array::new(vec![1+char_classes+l1_size, 4*l1_size], 0.0);
    let mut argmax_out = Array::new(vec![batch_size], 0usize);

    let rmsprop = train::RmsProp::new(graph, 0.001, 0.9);

    let samples = 10000;
    let mut i = 0;
    for line in &lines {
        for (t, &(input, _, train_out)) in (1..line.len()).zip(steps.iter()) {
            input.write(graph, &char_map[&line[t-1]]);
            train_out.write(graph, &char_map[&line[t]]);
        }

        for (t, &(_, _, _)) in (1..line.len()).zip(steps.iter()) {
            print!("{}", line[t] as char);
        }
        println!("");

        graph.forward();
        graph.backward();

        for (_, &(_, l2_out, _)) in (1..line.len()).zip(steps.iter()) {
            l2_out.read(graph, &mut l2_out_cpu);
            //l1_w.read(graph, &mut l1_w_cpu);
            //graph.get_gradient(l3_out).read(graph, &mut l3_out_d_cpu);
            //graph.get_gradient(l1_w).read(graph, &mut l1_w_cpu);
            //graph.get_gradient(last_recur_in.2).read(graph, &mut l2_out_cpu);
            //println!("{:?}", l3_out_cpu);
            //println!("{:?}", l3_out_d_cpu);
            //println!("{:?}", l1_w_cpu);
            //println!("{:?}", l2_out_cpu);
            util::argmax_rows(&l2_out_cpu, &mut argmax_out);
            let next_char = rev_char_map[&argmax_out[&[0]]];
            print!("{}", next_char as char);
        }
        println!("");

        //train::apply_gradients(graph);
        rmsprop.update(graph);

        println!("{}", i);

        i += 1;
        if i > samples {
            break;
        }
    }

    /*let mut l3_out_cpu = Array::new(vec![batch_size, char_classes], 0.0);
    let mut argmax_out = Array::new(vec![batch_size], 0usize);

    loop {
        // Print prompt
        print!(">");
        io::stdout().flush().unwrap();

        // Get seed string
        let mut seed = String::new();
        io::stdin().read_line(&mut seed).unwrap();

        // Input the seed string
        let mut last_char = 0u8;
        for c in seed.trim_right().as_bytes() {
            print!("{}", *c as char);
            input.write(graph, &char_map[c]);
            graph.forward();
            l3_out.read(graph, &mut l3_out_cpu);
            util::argmax_rows(&l3_out_cpu, &mut argmax_out);
            last_char = rev_char_map[&argmax_out[&[0]]];
        }

        // Generate the rest of the output
        for _ in 0..150 {
            print!("{}", last_char as char);
            io::stdout().flush().unwrap();

            input.write(graph, &char_map[&last_char]);
            graph.forward();
            l3_out.read(graph, &mut l3_out_cpu);
            //println!("{:?}", l3_out_cpu);
            util::argmax_rows(&l3_out_cpu, &mut argmax_out);
            last_char = rev_char_map[&argmax_out[&[0]]];

            if last_char == b'\n' {
                break;
            }
        }
        println!("");
    }*/
}

pub fn load_char_rnn_data<P: AsRef<Path>>(path: P)
    -> io::Result<(HashMap<u8, Array<f32>>, HashMap<usize, u8>, Vec<Vec<u8>>)>
{
    use std::io::BufRead;

    let ref mut file = BufReader::new(File::open(path).unwrap());

    let mut unique_chars = HashSet::new();
    let mut lines = vec![];

    for line in file.lines() {
        let line: String = try!(line) + "\n";
        for c in line.as_bytes() {
            unique_chars.insert(*c);
        }
        lines.push(line.as_bytes().to_owned());
    }

    let char_classes = unique_chars.len();
    let mut char_map = HashMap::new();
    let mut rev_char_map = HashMap::new();
    for (i, c) in unique_chars.into_iter().enumerate() {
        char_map.insert(c, util::one_hot_row(i, char_classes));
        rev_char_map.insert(i, c);
    }

    Ok((char_map, rev_char_map, lines))
}
<|endoftext|>


<commit_before><commit_msg>set response error body<commit_after><|endoftext|>


<commit_before>use ndarray::*;
use numpy::*;

#[test]
fn to_pyarray_vec() {
    pyo3::Python::with_gil(|py| {
        let a = vec![1, 2, 3];
        let arr = a.to_pyarray(py).readonly();
        println!("arr.shape = {:?}", arr.shape());
        assert_eq!(arr.shape(), [3]);
        assert_eq!(arr.as_slice().unwrap(), &[1, 2, 3])
    })
}

#[test]
fn to_pyarray_array() {
    pyo3::Python::with_gil(|py| {
        let a = Array3::<f64>::zeros((3, 4, 2));
        let shape = a.shape().to_vec();
        let strides = a.strides().iter().map(|d| d * 8).collect::<Vec<_>>();
        println!("a.shape   = {:?}", a.shape());
        println!("a.strides = {:?}", a.strides());
        let pa = a.to_pyarray(py);
        println!("pa.shape   = {:?}", pa.shape());
        println!("pa.strides = {:?}", pa.strides());
        assert_eq!(pa.shape(), shape.as_slice());
        assert_eq!(pa.strides(), strides.as_slice());
    })
}

#[test]
fn iter_to_pyarray() {
    pyo3::Python::with_gil(|py| {
        let arr = PyArray::from_iter(py, (0..10).map(|x| x * x)).readonly();
        assert_eq!(
            arr.as_slice().unwrap(),
            &[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
        );
    })
}

#[test]
fn long_iter_to_pyarray() {
    pyo3::Python::with_gil(|py| {
        let arr = PyArray::from_iter(py, (0u32..512).map(|x| x)).readonly();
        let slice = arr.as_slice().unwrap();
        for (i, &elem) in slice.iter().enumerate() {
            assert_eq!(i as u32, elem);
        }
    })
}

macro_rules! small_array_test {
    ($($t: ident)+) => {
        #[test]
        fn from_small_array() {
            $({
                pyo3::Python::with_gil(|py| {
                    let array: [$t; 2] = [$t::min_value(), $t::max_value()];
                    let pyarray = array.to_pyarray(py).readonly();
                    assert_eq!(
                        pyarray.as_slice().unwrap(),
                        &[$t::min_value(), $t::max_value()]
                    );
                })
            })+
        }
    };
}

small_array_test!(i8 u8 i16 u16 i32 u32 i64 u64 usize);

#[test]
fn usize_dtype() {
    let a: Vec<usize> = vec![1, 2, 3];

    pyo3::Python::with_gil(|py| {
        let x = a.into_pyarray(py);

        if cfg!(target_pointer_width = "64") {
            pyo3::py_run!(py, x, "assert str(x.dtype) == 'uint64'")
        } else {
            pyo3::py_run!(py, x, "assert str(x.dtype) == 'uint32'")
        };
    })
}

#[test]
fn into_pyarray_vec() {
    let a = vec![1, 2, 3];
    pyo3::Python::with_gil(|py| {
        let arr = a.into_pyarray(py).readonly();
        assert_eq!(arr.as_slice().unwrap(), &[1, 2, 3])
    })
}

#[test]
fn into_pyarray_array() {
    let arr = Array3::<f64>::zeros((3, 4, 2));
    let shape = arr.shape().to_vec();
    let strides = arr.strides().iter().map(|d| d * 8).collect::<Vec<_>>();
    pyo3::Python::with_gil(|py| {
        let py_arr = arr.into_pyarray(py);
        assert_eq!(py_arr.shape(), shape.as_slice());
        assert_eq!(py_arr.strides(), strides.as_slice());
    })
}

#[test]
fn into_pyarray_cant_resize() {
    let a = vec![1, 2, 3];
    pyo3::Python::with_gil(|py| {
        let arr = a.into_pyarray(py);
        assert!(arr.resize(100).is_err())
    })
}

#[test]
fn forder_to_pyarray() {
    pyo3::Python::with_gil(|py| {
        let matrix = Array2::from_shape_vec([4, 2], vec![0, 1, 2, 3, 4, 5, 6, 7]).unwrap();
        let fortran_matrix = matrix.reversed_axes();
        let fmat_py = fortran_matrix.to_pyarray(py);
        assert_eq!(
            fmat_py.readonly().as_array(),
            array![[0, 2, 4, 6], [1, 3, 5, 7]],
        );
        pyo3::py_run!(py, fmat_py, "assert fmat_py.flags['F_CONTIGUOUS']")
    })
}

#[test]
fn slice_to_pyarray() {
    pyo3::Python::with_gil(|py| {
        let matrix = Array2::from_shape_vec([4, 2], vec![0, 1, 2, 3, 4, 5, 6, 7]).unwrap();
        let slice = matrix.slice(s![1..4; -1, ..]);
        let slice_py = slice.to_pyarray(py);
        assert_eq!(
            slice_py.readonly().as_array(),
            array![[6, 7], [4, 5], [2, 3]],
        );
        pyo3::py_run!(py, slice_py, "assert slice_py.flags['C_CONTIGUOUS']")
    })
}

#[test]
fn forder_into_pyarray() {
    pyo3::Python::with_gil(|py| {
        let matrix = Array2::from_shape_vec([4, 2], vec![0, 1, 2, 3, 4, 5, 6, 7]).unwrap();
        let fortran_matrix = matrix.reversed_axes();
        let fmat_py = fortran_matrix.into_pyarray(py);
        assert_eq!(
            fmat_py.readonly().as_array(),
            array![[0, 2, 4, 6], [1, 3, 5, 7]],
        );
        pyo3::py_run!(py, fmat_py, "assert fmat_py.flags['F_CONTIGUOUS']")
    })
}
<commit_msg>Address clippy warning<commit_after>use ndarray::*;
use numpy::*;

#[test]
fn to_pyarray_vec() {
    pyo3::Python::with_gil(|py| {
        let a = vec![1, 2, 3];
        let arr = a.to_pyarray(py).readonly();
        println!("arr.shape = {:?}", arr.shape());
        assert_eq!(arr.shape(), [3]);
        assert_eq!(arr.as_slice().unwrap(), &[1, 2, 3])
    })
}

#[test]
fn to_pyarray_array() {
    pyo3::Python::with_gil(|py| {
        let a = Array3::<f64>::zeros((3, 4, 2));
        let shape = a.shape().to_vec();
        let strides = a.strides().iter().map(|d| d * 8).collect::<Vec<_>>();
        println!("a.shape   = {:?}", a.shape());
        println!("a.strides = {:?}", a.strides());
        let pa = a.to_pyarray(py);
        println!("pa.shape   = {:?}", pa.shape());
        println!("pa.strides = {:?}", pa.strides());
        assert_eq!(pa.shape(), shape.as_slice());
        assert_eq!(pa.strides(), strides.as_slice());
    })
}

#[test]
fn iter_to_pyarray() {
    pyo3::Python::with_gil(|py| {
        let arr = PyArray::from_iter(py, (0..10).map(|x| x * x)).readonly();
        assert_eq!(
            arr.as_slice().unwrap(),
            &[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
        );
    })
}

#[test]
fn long_iter_to_pyarray() {
    pyo3::Python::with_gil(|py| {
        let arr = PyArray::from_iter(py, 0u32..512).readonly();
        let slice = arr.as_slice().unwrap();
        for (i, &elem) in slice.iter().enumerate() {
            assert_eq!(i as u32, elem);
        }
    })
}

macro_rules! small_array_test {
    ($($t: ident)+) => {
        #[test]
        fn from_small_array() {
            $({
                pyo3::Python::with_gil(|py| {
                    let array: [$t; 2] = [$t::min_value(), $t::max_value()];
                    let pyarray = array.to_pyarray(py).readonly();
                    assert_eq!(
                        pyarray.as_slice().unwrap(),
                        &[$t::min_value(), $t::max_value()]
                    );
                })
            })+
        }
    };
}

small_array_test!(i8 u8 i16 u16 i32 u32 i64 u64 usize);

#[test]
fn usize_dtype() {
    let a: Vec<usize> = vec![1, 2, 3];

    pyo3::Python::with_gil(|py| {
        let x = a.into_pyarray(py);

        if cfg!(target_pointer_width = "64") {
            pyo3::py_run!(py, x, "assert str(x.dtype) == 'uint64'")
        } else {
            pyo3::py_run!(py, x, "assert str(x.dtype) == 'uint32'")
        };
    })
}

#[test]
fn into_pyarray_vec() {
    let a = vec![1, 2, 3];
    pyo3::Python::with_gil(|py| {
        let arr = a.into_pyarray(py).readonly();
        assert_eq!(arr.as_slice().unwrap(), &[1, 2, 3])
    })
}

#[test]
fn into_pyarray_array() {
    let arr = Array3::<f64>::zeros((3, 4, 2));
    let shape = arr.shape().to_vec();
    let strides = arr.strides().iter().map(|d| d * 8).collect::<Vec<_>>();
    pyo3::Python::with_gil(|py| {
        let py_arr = arr.into_pyarray(py);
        assert_eq!(py_arr.shape(), shape.as_slice());
        assert_eq!(py_arr.strides(), strides.as_slice());
    })
}

#[test]
fn into_pyarray_cant_resize() {
    let a = vec![1, 2, 3];
    pyo3::Python::with_gil(|py| {
        let arr = a.into_pyarray(py);
        assert!(arr.resize(100).is_err())
    })
}

#[test]
fn forder_to_pyarray() {
    pyo3::Python::with_gil(|py| {
        let matrix = Array2::from_shape_vec([4, 2], vec![0, 1, 2, 3, 4, 5, 6, 7]).unwrap();
        let fortran_matrix = matrix.reversed_axes();
        let fmat_py = fortran_matrix.to_pyarray(py);
        assert_eq!(
            fmat_py.readonly().as_array(),
            array![[0, 2, 4, 6], [1, 3, 5, 7]],
        );
        pyo3::py_run!(py, fmat_py, "assert fmat_py.flags['F_CONTIGUOUS']")
    })
}

#[test]
fn slice_to_pyarray() {
    pyo3::Python::with_gil(|py| {
        let matrix = Array2::from_shape_vec([4, 2], vec![0, 1, 2, 3, 4, 5, 6, 7]).unwrap();
        let slice = matrix.slice(s![1..4; -1, ..]);
        let slice_py = slice.to_pyarray(py);
        assert_eq!(
            slice_py.readonly().as_array(),
            array![[6, 7], [4, 5], [2, 3]],
        );
        pyo3::py_run!(py, slice_py, "assert slice_py.flags['C_CONTIGUOUS']")
    })
}

#[test]
fn forder_into_pyarray() {
    pyo3::Python::with_gil(|py| {
        let matrix = Array2::from_shape_vec([4, 2], vec![0, 1, 2, 3, 4, 5, 6, 7]).unwrap();
        let fortran_matrix = matrix.reversed_axes();
        let fmat_py = fortran_matrix.into_pyarray(py);
        assert_eq!(
            fmat_py.readonly().as_array(),
            array![[0, 2, 4, 6], [1, 3, 5, 7]],
        );
        pyo3::py_run!(py, fmat_py, "assert fmat_py.flags['F_CONTIGUOUS']")
    })
}
<|endoftext|>


<commit_before>//! # Custom `derive` pretending to be functional procedural macros on Rust 1.15
//!
//! This crate enables creating function-like macros (invoked as `foo!(...)`)
//! with a procedural component,
//! based on both custom `derive` (a.k.a. *Macros 1.1*) and `macro_rules!`.
//!
//! This convoluted mechanism enables such macros to run on stable Rust 1.15,
//! even though functional procedural macros (a.k.a. *Macros 2.0*) are not available yet.
//!
//! A library defining such a macro needs two crates: a “normal” one, and a `proc-macro` one.
//! In the example below we’ll call them `libfoo` and `libfoo-macros`, respectively.
//!
//! # Credits
//!
//! The trick that makes this crate work
//! is based on an idea from [David Tolnay](https://github.com/dtolnay).
//! Many thanks!
//!
//! # Example
//!
//! As a simple example, we’re going to re-implement the `stringify!` macro.
//! This is useless since `stringify!` already exists in the standard library,
//! and a bit absurd since this crate uses `stringify!` internally.
//!
//! Nevertheless, it serves as a simple example to demonstrate the use of this crate.
//!
//! ## The `proc-macro` crate
//!
//! The minimal `Cargo.toml` file is typical for Macros 1.1:
//!
//! ```toml
//! [package]
//! name = "libfoo-macros"
//! version = "1.0.0"
//!
//! [lib]
//! proc-macro = true
//! ```
//!
//! In the code, we define the procedural part of our macro in a function.
//! This function will not be used directly by end users,
//! but it still needs to be re-exported to them
//! (because of limitations in `macro_rules!`).
//!
//! To avoid name collisions, we include a long and explicit prefix in the function’s name.
//!
//! The function takes a string containing arbitrary Rust tokens,
//! and returns a string that is parsed as *items*.
//! The returned string can contain constants, statics, functions, `impl`s, etc.,
//! but not expressions directly.
//!
//! ```rust
//! #[macro_use] extern crate procedural_masquerade;
//! extern crate proc_macro;
//!
//! define_proc_macros! {
//!     #[allow(non_snake_case)]
//!     pub fn foo_internal__stringify_const(input: &str) -> String {
//!         format!("const STRINGIFIED: &'static str = {:?};", input)
//!     }
//! }
//! ```
//!
//! A less trivial macro would probably use
//! the [`syn`](https://github.com/dtolnay/syn/) crate to parse its input
//! and the [`quote`](https://github.com/dtolnay/quote) crate to generate its output.
//!
//! ## The library crate
//!
//! ```toml
//! [package]
//! name = "libfoo"
//! version = "1.0.0"
//!
//! [dependencies]
//! libfoo-macros = {path = "./macros", version = "1.0"}
//! ```
//!
//! ```rust
//! #[macro_use] extern crate libfoo_macros;  // (1)
//!
//! pub use libfoo_macros::*;  // (2)
//!
//! define_invoke_proc_macro!(libfoo__invoke_proc_macro);  // (3)
//!
//! #[macro_export]
//! macro_rules! foo_stringify {  // (4)
//!     ( $( $tts: tt ) ) => {
//!         {  // (5)
//!             libfoo__invoke_proc_macro! {  // (6)
//!                 foo_internal__stringify_const!( $( $tts ) )  // (7)
//!             }
//!             STRINGIFIED  // (8)
//!         }
//!     }
//! }
//! ```
//!
//! Let’s go trough the numbered lines one by one:
//!
//! 1. `libfoo` depends on `libfoo-macros`, and imports its macros.
//! 2. Everything exported by `libfoo-macros` (which is one custom `derive`)
//!    is re-exported to users of `libfoo`.
//!    They’re not expected to use it directly,
//!    but expansion of the `foo_stringify` macro needs it.
//! 3. This macro invocation defines yet another macro, called `libfoo__invoke_proc_macro`,
//!    which is also exported.
//!    This indirection is necessary
//!    because re-exporting `macro_rules!` macros doesn’t work currently,
//!    and once again it is used by the expansion of `foo_stringify`.
//!    Again, we use a long prefix to avoid name collisions.
//! 4. Finally, we define the macro that we really want.
//!    This one has a name that users will use.
//! 5. The expansion of this macro will define some items,
//!    whose names are not hygienic in `macro_rules`.
//!    So we wrap everything in an extra `{…}` block to prevent these names for leaking.
//! 6. Here we use the macro defined in (3),
//!    which allows us to write something that look like invoking a functional procedural macro,
//!    but really uses a custom `derive`.
//!    This will define a type called `ProceduralMasqueradeDummyType`,
//!    as a placeholder to use `derive`.
//!    If `libfoo__invoke_proc_macro!` is to be used more than once,
//!    each use needs to be nested in another block
//!    so that the names of multiple dummy types don’t collide.
//! 7. In addition to the dummy type,
//!    the items returned by our procedural component are inserted here.
//!    (In this case the `STRINGIFIED` constant.)
//! 8. Finally, we write the expression that we want the macro to evaluate to.
//!    This expression can use parts of `foo_stringify`’s input,
//!    it can contain control-flow statements like `return` or `continue`,
//!    and of course refer to procedurally-defined items.
//!
//! This macro can be used in an expression context.
//! It expands to a block-expression that contains some items (as an implementation detail)
//! and ends with another expression.
//!
//! ## For users
//!
//! Users of `libfoo` don’t need to worry about any of these implementation details.
//! They can use the `foo_stringify` macro as if it were a simle `macro_rules` macro:
//!
//! ```rust
//! #[macro_use] extern crate libfoo;
//!
//! fn main() {
//!     do_something(foo_stringify!(1 + 2));
//! }
//!
//! fn do_something(_: &str) { /* ... */ }
//! ```
//!
//! # More
//!
//! To see a more complex example, look at
//! [`cssparser`’s `src/macros.rs`](https://github.com/servo/rust-cssparser/blob/master/src/macros.rs)
//! and
//! [`cssparser-macros`’s `macros/lib.rs`](https://github.com/servo/rust-cssparser/blob/master/macros/lib.rs).

/// This macro wraps `&str -> String` functions
/// in custom `derive` implementations with `#[proc_macro_derive]`.
///
/// See crate documentation for details.
#[macro_export]
macro_rules! define_proc_macros {
    (
        $(
            $( #[$attr:meta] )*
            pub fn $proc_macro_name: ident ($input: ident : &str) -> String
            $body: block
        )+
    ) => {
        $(
            $( #[$attr] )*
            #[proc_macro_derive($proc_macro_name)]
            pub fn $proc_macro_name(derive_input: ::proc_macro::TokenStream)
                                    -> ::proc_macro::TokenStream {
                fn wrapped($input: &str) -> String {
                    $body
                }

                // syn uses a huge amount of stack in debug mode.
                let derive_input_string = derive_input.to_string();
                let handle =
                    ::std::thread::Builder::new().stack_size(128 * 1024 * 1024).spawn(move || {
                        wrapped($crate::_extract_input(&derive_input_string))
                    }).unwrap();

                handle.join().unwrap().parse().unwrap()
            }
        )+
    }
}

/// Implementation detail of `define_proc_macros!`.
///
/// **This function is not part of the public API. It can change or be removed between any versions.**
#[doc(hidden)]
pub fn _extract_input(derive_input: &str) -> &str {
    let mut input = derive_input;

    for expected in &[
        "#",
        "[",
        "allow",
        "(",
        "unused",
        ")",
        "]",
        "enum",
        "ProceduralMasqueradeDummyType",
        "{",
        "Input",
        "=",
        "(",
        "0",
        ",",
        "stringify",
        "!",
        "(",
    ] {
        input = input.trim_start();
        assert!(
            input.starts_with(expected),
            "expected prefix {:?} not found in {:?}",
            expected,
            derive_input
        );
        input = &input[expected.len()..];
    }

    for expected in [")", ")", ".", "0", ",", "}"].iter().rev() {
        input = input.trim_end();
        assert!(
            input.ends_with(expected),
            "expected suffix {:?} not found in {:?}",
            expected,
            derive_input
        );
        let end = input.len() - expected.len();
        input = &input[..end];
    }

    input
}

/// This macro expands to the definition of another macro (whose name is given as a parameter).
///
/// See crate documentation for details.
#[macro_export]
macro_rules! define_invoke_proc_macro {
    ($macro_name: ident) => {
        /// Implementation detail of other macros in this crate.
        #[doc(hidden)]
        #[macro_export]
        macro_rules! $macro_name {
                            ($proc_macro_name: ident ! $paren: tt) => {
                                #[derive($proc_macro_name)]
                                #[allow(unused)]
                                enum ProceduralMasqueradeDummyType {
                                    // The magic happens here.
                                    //
                                    // We use an `enum` with an explicit discriminant
                                    // because that is the only case where a type definition
                                    // can contain a (const) expression.
                                    //
                                    // `(0, "foo").0` evalutes to 0, with the `"foo"` part ignored.
                                    //
                                    // By the time the `#[proc_macro_derive]` function
                                    // implementing `#[derive($proc_macro_name)]` is called,
                                    // `$paren` has already been replaced with the input of this inner macro,
                                    // but `stringify!` has not been expanded yet.
                                    //
                                    // This how arbitrary tokens can be inserted
                                    // in the input to the `#[proc_macro_derive]` function.
                                    //
                                    // Later, `stringify!(...)` is expanded into a string literal
                                    // which is then ignored.
                                    // Using `stringify!` enables passing arbitrary tokens
                                    // rather than only what can be parsed as a const expression.
                                    Input = (0, stringify! $paren ).0,
                                }
                            }
                        }
    };
}
<commit_msg>Auto merge of #276 - Atul9:skip-cargofmt-for-procedural-masquerade-dummy-type, r=SimonSapin<commit_after>//! # Custom `derive` pretending to be functional procedural macros on Rust 1.15
//!
//! This crate enables creating function-like macros (invoked as `foo!(...)`)
//! with a procedural component,
//! based on both custom `derive` (a.k.a. *Macros 1.1*) and `macro_rules!`.
//!
//! This convoluted mechanism enables such macros to run on stable Rust 1.15,
//! even though functional procedural macros (a.k.a. *Macros 2.0*) are not available yet.
//!
//! A library defining such a macro needs two crates: a “normal” one, and a `proc-macro` one.
//! In the example below we’ll call them `libfoo` and `libfoo-macros`, respectively.
//!
//! # Credits
//!
//! The trick that makes this crate work
//! is based on an idea from [David Tolnay](https://github.com/dtolnay).
//! Many thanks!
//!
//! # Example
//!
//! As a simple example, we’re going to re-implement the `stringify!` macro.
//! This is useless since `stringify!` already exists in the standard library,
//! and a bit absurd since this crate uses `stringify!` internally.
//!
//! Nevertheless, it serves as a simple example to demonstrate the use of this crate.
//!
//! ## The `proc-macro` crate
//!
//! The minimal `Cargo.toml` file is typical for Macros 1.1:
//!
//! ```toml
//! [package]
//! name = "libfoo-macros"
//! version = "1.0.0"
//!
//! [lib]
//! proc-macro = true
//! ```
//!
//! In the code, we define the procedural part of our macro in a function.
//! This function will not be used directly by end users,
//! but it still needs to be re-exported to them
//! (because of limitations in `macro_rules!`).
//!
//! To avoid name collisions, we include a long and explicit prefix in the function’s name.
//!
//! The function takes a string containing arbitrary Rust tokens,
//! and returns a string that is parsed as *items*.
//! The returned string can contain constants, statics, functions, `impl`s, etc.,
//! but not expressions directly.
//!
//! ```rust
//! #[macro_use] extern crate procedural_masquerade;
//! extern crate proc_macro;
//!
//! define_proc_macros! {
//!     #[allow(non_snake_case)]
//!     pub fn foo_internal__stringify_const(input: &str) -> String {
//!         format!("const STRINGIFIED: &'static str = {:?};", input)
//!     }
//! }
//! ```
//!
//! A less trivial macro would probably use
//! the [`syn`](https://github.com/dtolnay/syn/) crate to parse its input
//! and the [`quote`](https://github.com/dtolnay/quote) crate to generate its output.
//!
//! ## The library crate
//!
//! ```toml
//! [package]
//! name = "libfoo"
//! version = "1.0.0"
//!
//! [dependencies]
//! libfoo-macros = {path = "./macros", version = "1.0"}
//! ```
//!
//! ```rust
//! #[macro_use] extern crate libfoo_macros;  // (1)
//!
//! pub use libfoo_macros::*;  // (2)
//!
//! define_invoke_proc_macro!(libfoo__invoke_proc_macro);  // (3)
//!
//! #[macro_export]
//! macro_rules! foo_stringify {  // (4)
//!     ( $( $tts: tt ) ) => {
//!         {  // (5)
//!             libfoo__invoke_proc_macro! {  // (6)
//!                 foo_internal__stringify_const!( $( $tts ) )  // (7)
//!             }
//!             STRINGIFIED  // (8)
//!         }
//!     }
//! }
//! ```
//!
//! Let’s go trough the numbered lines one by one:
//!
//! 1. `libfoo` depends on `libfoo-macros`, and imports its macros.
//! 2. Everything exported by `libfoo-macros` (which is one custom `derive`)
//!    is re-exported to users of `libfoo`.
//!    They’re not expected to use it directly,
//!    but expansion of the `foo_stringify` macro needs it.
//! 3. This macro invocation defines yet another macro, called `libfoo__invoke_proc_macro`,
//!    which is also exported.
//!    This indirection is necessary
//!    because re-exporting `macro_rules!` macros doesn’t work currently,
//!    and once again it is used by the expansion of `foo_stringify`.
//!    Again, we use a long prefix to avoid name collisions.
//! 4. Finally, we define the macro that we really want.
//!    This one has a name that users will use.
//! 5. The expansion of this macro will define some items,
//!    whose names are not hygienic in `macro_rules`.
//!    So we wrap everything in an extra `{…}` block to prevent these names for leaking.
//! 6. Here we use the macro defined in (3),
//!    which allows us to write something that look like invoking a functional procedural macro,
//!    but really uses a custom `derive`.
//!    This will define a type called `ProceduralMasqueradeDummyType`,
//!    as a placeholder to use `derive`.
//!    If `libfoo__invoke_proc_macro!` is to be used more than once,
//!    each use needs to be nested in another block
//!    so that the names of multiple dummy types don’t collide.
//! 7. In addition to the dummy type,
//!    the items returned by our procedural component are inserted here.
//!    (In this case the `STRINGIFIED` constant.)
//! 8. Finally, we write the expression that we want the macro to evaluate to.
//!    This expression can use parts of `foo_stringify`’s input,
//!    it can contain control-flow statements like `return` or `continue`,
//!    and of course refer to procedurally-defined items.
//!
//! This macro can be used in an expression context.
//! It expands to a block-expression that contains some items (as an implementation detail)
//! and ends with another expression.
//!
//! ## For users
//!
//! Users of `libfoo` don’t need to worry about any of these implementation details.
//! They can use the `foo_stringify` macro as if it were a simle `macro_rules` macro:
//!
//! ```rust
//! #[macro_use] extern crate libfoo;
//!
//! fn main() {
//!     do_something(foo_stringify!(1 + 2));
//! }
//!
//! fn do_something(_: &str) { /* ... */ }
//! ```
//!
//! # More
//!
//! To see a more complex example, look at
//! [`cssparser`’s `src/macros.rs`](https://github.com/servo/rust-cssparser/blob/master/src/macros.rs)
//! and
//! [`cssparser-macros`’s `macros/lib.rs`](https://github.com/servo/rust-cssparser/blob/master/macros/lib.rs).

/// This macro wraps `&str -> String` functions
/// in custom `derive` implementations with `#[proc_macro_derive]`.
///
/// See crate documentation for details.
#[macro_export]
macro_rules! define_proc_macros {
    (
        $(
            $( #[$attr:meta] )*
            pub fn $proc_macro_name: ident ($input: ident : &str) -> String
            $body: block
        )+
    ) => {
        $(
            $( #[$attr] )*
            #[proc_macro_derive($proc_macro_name)]
            pub fn $proc_macro_name(derive_input: ::proc_macro::TokenStream)
                                    -> ::proc_macro::TokenStream {
                fn wrapped($input: &str) -> String {
                    $body
                }

                // syn uses a huge amount of stack in debug mode.
                let derive_input_string = derive_input.to_string();
                let handle =
                    ::std::thread::Builder::new().stack_size(128 * 1024 * 1024).spawn(move || {
                        wrapped($crate::_extract_input(&derive_input_string))
                    }).unwrap();

                handle.join().unwrap().parse().unwrap()
            }
        )+
    }
}

/// Implementation detail of `define_proc_macros!`.
///
/// **This function is not part of the public API. It can change or be removed between any versions.**
#[doc(hidden)]
pub fn _extract_input(derive_input: &str) -> &str {
    let mut input = derive_input;

    for expected in &[
        "#",
        "[",
        "allow",
        "(",
        "unused",
        ")",
        "]",
        "enum",
        "ProceduralMasqueradeDummyType",
        "{",
        "Input",
        "=",
        "(",
        "0",
        ",",
        "stringify",
        "!",
        "(",
    ] {
        input = input.trim_start();
        assert!(
            input.starts_with(expected),
            "expected prefix {:?} not found in {:?}",
            expected,
            derive_input
        );
        input = &input[expected.len()..];
    }

    for expected in [")", ")", ".", "0", ",", "}"].iter().rev() {
        input = input.trim_end();
        assert!(
            input.ends_with(expected),
            "expected suffix {:?} not found in {:?}",
            expected,
            derive_input
        );
        let end = input.len() - expected.len();
        input = &input[..end];
    }

    input
}

/// This macro expands to the definition of another macro (whose name is given as a parameter).
///
/// See crate documentation for details.
#[macro_export]
macro_rules! define_invoke_proc_macro {
    ($macro_name: ident) => {
        /// Implementation detail of other macros in this crate.
        #![rustfmt::skip]
        #[doc(hidden)]
        #[macro_export]
        macro_rules! $macro_name {
            ($proc_macro_name: ident ! $paren: tt) => {
                #[derive($proc_macro_name)]
                #[allow(unused)]
                enum ProceduralMasqueradeDummyType {
                    // The magic happens here.
                    //
                    // We use an `enum` with an explicit discriminant
                    // because that is the only case where a type definition
                    // can contain a (const) expression.
                    //
                    // `(0, "foo").0` evalutes to 0, with the `"foo"` part ignored.
                    //
                    // By the time the `#[proc_macro_derive]` function
                    // implementing `#[derive($proc_macro_name)]` is called,
                    // `$paren` has already been replaced with the input of this inner macro,
                    // but `stringify!` has not been expanded yet.
                    //
                    // This how arbitrary tokens can be inserted
                    // in the input to the `#[proc_macro_derive]` function.
                    //
                    // Later, `stringify!(...)` is expanded into a string literal
                    // which is then ignored.
                    // Using `stringify!` enables passing arbitrary tokens
                    // rather than only what can be parsed as a const expression.
                    Input = (0, stringify! $paren ).0,
                }
            }
        }
    };
}
<|endoftext|>


<commit_before><commit_msg>Remove deprecated `String.append` call<commit_after><|endoftext|>


<commit_before><commit_msg>Pesky emacs hidden files<commit_after><|endoftext|>


<commit_before>#[derive(Copy, Clone, PartialEq, Debug, Default)]
pub struct Point {
    pub x: f32,
    pub y: f32,
}

impl Point {
    pub fn new(x: f32, y: f32) -> Self {
        Point { x: x, y: y }
    }

    pub fn zero() -> Self {
        Self::new(0.0, 0.0)
    }
}

impl From<Point> for [f32; 2] {
    fn from(p: Point) -> [f32; 2] {
        [p.x, p.y]
    }
}

#[derive(Copy, Clone, PartialEq, Debug, Default)]
pub struct Rect {
    pub x: f32,
    pub y: f32,
    pub w: f32,
    pub h: f32,
}

impl Rect {
    pub fn new(x: f32, y: f32, w: f32, h: f32) -> Self {
        Rect {
            x: x,
            y: y,
            w: w,
            h: h,
        }
    }

    /// I don't know what to call this yet.
    /// It creates a new rect a la Love2D's love.graphics.newQuad,
    /// as a fraction of the reference rect's size.
    ///
    /// Basically it does the translation from pixel coordinates to
    /// UV coordinates.
    pub fn fraction(x: f32, y: f32, w: f32, h: f32, reference: &Rect) -> Rect {
        Rect {
            x: x / reference.w,
            y: y / reference.h,
            w: w / reference.w,
            h: h / reference.h,
        }
    }

    pub fn new_i32(x: i32, y: i32, w: i32, h: i32) -> Self {
        Rect {
            x: x as f32,
            y: y as f32,
            w: w as f32,
            h: h as f32,
        }
    }

    pub fn zero() -> Self {
        Self::new(0.0, 0.0, 0.0, 0.0)
    }

    pub fn one() -> Self {
        Self::new(0.0, 0.0, 1.0, 1.0)
    }
}

impl From<[f32; 4]> for Rect {
    fn from(val: [f32; 4]) -> Self {
        Rect::new(val[0], val[1], val[2], val[3])
    }
}

impl From<Rect> for [f32; 4] {
    fn from(val: Rect) -> Self {
        [val.x, val.y, val.w, val.h]
    }
}

#[derive(Copy, Clone, PartialEq, Debug)]
pub struct Color {
    pub r: f32,
    pub g: f32,
    pub b: f32,
    pub a: f32,
}

pub const WHITE: Color = Color {
    r: 1.0,
    g: 1.0,
    b: 1.0,
    a: 1.0,
};

impl Color {
    pub fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
        Color {
            r: r,
            g: g,
            b: b,
            a: a,
        }
    }
}

impl From<(u8, u8, u8, u8)> for Color {
    fn from(val: (u8, u8, u8, u8)) -> Self {
        let (r, g, b, a) = val;
        let rf = (r as f32) / 255.0;
        let gf = (g as f32) / 255.0;
        let bf = (b as f32) / 255.0;
        let af = (a as f32) / 255.0;
        Color::new(rf, gf, bf, af)
    }
}

impl From<(u8, u8, u8)> for Color {
    fn from(val: (u8, u8, u8)) -> Self {
        let (r, g, b) = val;
        Color::from((r, g, b, 255))
    }
}

impl From<[f32; 4]> for Color {
    fn from(val: [f32; 4]) -> Self {
        Color::new(val[0], val[1], val[2], val[3])
    }
}

impl From<Color> for (u8, u8, u8, u8) {
    fn from(color: Color) -> Self {
        let r = (color.r * 255.0) as u8;
        let g = (color.g * 255.0) as u8;
        let b = (color.b * 255.0) as u8;
        let a = (color.a * 255.0) as u8;
        (r, g, b, a)
    }
}

impl From<Color> for [u8; 4] {
    fn from(color: Color) -> Self {
        let (r, g, b, a) = color.into();
        [r, g, b, a]
    }
}

impl From<Color> for [f32; 4] {
    fn from(color: Color) -> Self {
        [color.r, color.g, color.b, color.a]
    }
}

impl From<Color> for (u8, u8, u8) {
    fn from(color: Color) -> Self {
        let (r, g, b, _) = color.into();
        (r, g, b)
    }
}

impl From<Color> for u32 {
    fn from(color: Color) -> Self {
        let (r, g, b, a): (u8, u8, u8, u8) = color.into();
        let rp = (r as u32) << 24;
        let gp = (g as u32) << 16;
        let bp = (b as u32) << 8;
        let ap = a as u32;
        (rp | gp | bp | ap)
    }
}

#[derive(Copy, Clone, PartialEq, Debug)]
pub enum BlendMode {
    Dummy,
}

/// Specifies whether a shape should be drawn
/// filled or as an outline.
#[derive(Debug, Copy, Clone)]
pub enum DrawMode {
    Line,
    Fill,
}

/// Specifies how to scale up/down images.
#[derive(Debug, Copy, Clone)]
pub enum FilterMode {
    Linear,
    Nearest,
}

use gfx::texture::FilterMethod;

impl From<FilterMethod> for FilterMode {
    fn from(f: FilterMethod) -> Self {
        match f {
            FilterMethod::Scale => FilterMode::Nearest,
            FilterMethod::Bilinear => FilterMode::Linear,
            _ => FilterMode::Linear,
        }
    }
}

impl From<FilterMode> for FilterMethod {
    fn from(f: FilterMode) -> Self {
        match f {
            FilterMode::Nearest => FilterMethod::Scale,
            FilterMode::Linear => FilterMethod::Bilinear,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_color_conversions() {
        let white = Color::new(1.0, 1.0, 1.0, 1.0);
        let w1 = Color::from((255, 255, 255, 255));
        assert_eq!(white, w1);
        let w2: u32 = white.into();
        assert_eq!(w2, 0xFFFFFFFF);

        let grey = Color::new(0.5019608, 0.5019608, 0.5019608, 1.0);
        let g1 = Color::from((128, 128, 128, 255));
        assert_eq!(grey, g1);
        let g2: u32 = grey.into();
        assert_eq!(g2, 0x808080FF);

        let black = Color::new(0.0, 0.0, 0.0, 1.0);
        let b1 = Color::from((0, 0, 0, 255));
        assert_eq!(black, b1);
        let b2: u32 = black.into();
        assert_eq!(b2, 0x000000FF);
    }

    #[test]
    fn test_rect_scaling() {
        let r1 = Rect::new(0.0, 0.0, 128.0, 128.0);
        let r2 = Rect::fraction(0.0, 0.0, 32.0, 32.0, &r1);
        assert_eq!(r2, Rect::new(0.0, 0.0, 0.25, 0.25));

        let r2 = Rect::fraction(32.0, 32.0, 32.0, 32.0, &r1);
        assert_eq!(r2, Rect::new(0.25, 0.25, 0.25, 0.25));
    }
}
<commit_msg>we really need real color lists<commit_after>#[derive(Copy, Clone, PartialEq, Debug, Default)]
pub struct Point {
    pub x: f32,
    pub y: f32,
}

impl Point {
    pub fn new(x: f32, y: f32) -> Self {
        Point { x: x, y: y }
    }

    pub fn zero() -> Self {
        Self::new(0.0, 0.0)
    }
}

impl From<Point> for [f32; 2] {
    fn from(p: Point) -> [f32; 2] {
        [p.x, p.y]
    }
}

#[derive(Copy, Clone, PartialEq, Debug, Default)]
pub struct Rect {
    pub x: f32,
    pub y: f32,
    pub w: f32,
    pub h: f32,
}

impl Rect {
    pub fn new(x: f32, y: f32, w: f32, h: f32) -> Self {
        Rect {
            x: x,
            y: y,
            w: w,
            h: h,
        }
    }

    /// I don't know what to call this yet.
    /// It creates a new rect a la Love2D's love.graphics.newQuad,
    /// as a fraction of the reference rect's size.
    ///
    /// Basically it does the translation from pixel coordinates to
    /// UV coordinates.
    pub fn fraction(x: f32, y: f32, w: f32, h: f32, reference: &Rect) -> Rect {
        Rect {
            x: x / reference.w,
            y: y / reference.h,
            w: w / reference.w,
            h: h / reference.h,
        }
    }

    pub fn new_i32(x: i32, y: i32, w: i32, h: i32) -> Self {
        Rect {
            x: x as f32,
            y: y as f32,
            w: w as f32,
            h: h as f32,
        }
    }

    pub fn zero() -> Self {
        Self::new(0.0, 0.0, 0.0, 0.0)
    }

    pub fn one() -> Self {
        Self::new(0.0, 0.0, 1.0, 1.0)
    }
}

impl From<[f32; 4]> for Rect {
    fn from(val: [f32; 4]) -> Self {
        Rect::new(val[0], val[1], val[2], val[3])
    }
}

impl From<Rect> for [f32; 4] {
    fn from(val: Rect) -> Self {
        [val.x, val.y, val.w, val.h]
    }
}

#[derive(Copy, Clone, PartialEq, Debug)]
pub struct Color {
    pub r: f32,
    pub g: f32,
    pub b: f32,
    pub a: f32,
}

pub const WHITE: Color = Color {
    r: 1.0,
    g: 1.0,
    b: 1.0,
    a: 1.0,
};

pub const BLACK: Color = Color {
    r: 1.0,
    g: 1.0,
    b: 1.0,
    a: 1.0,
};

impl Color {
    pub fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
        Color {
            r: r,
            g: g,
            b: b,
            a: a,
        }
    }
}

impl From<(u8, u8, u8, u8)> for Color {
    fn from(val: (u8, u8, u8, u8)) -> Self {
        let (r, g, b, a) = val;
        let rf = (r as f32) / 255.0;
        let gf = (g as f32) / 255.0;
        let bf = (b as f32) / 255.0;
        let af = (a as f32) / 255.0;
        Color::new(rf, gf, bf, af)
    }
}

impl From<(u8, u8, u8)> for Color {
    fn from(val: (u8, u8, u8)) -> Self {
        let (r, g, b) = val;
        Color::from((r, g, b, 255))
    }
}

impl From<[f32; 4]> for Color {
    fn from(val: [f32; 4]) -> Self {
        Color::new(val[0], val[1], val[2], val[3])
    }
}

impl From<Color> for (u8, u8, u8, u8) {
    fn from(color: Color) -> Self {
        let r = (color.r * 255.0) as u8;
        let g = (color.g * 255.0) as u8;
        let b = (color.b * 255.0) as u8;
        let a = (color.a * 255.0) as u8;
        (r, g, b, a)
    }
}

impl From<Color> for [u8; 4] {
    fn from(color: Color) -> Self {
        let (r, g, b, a) = color.into();
        [r, g, b, a]
    }
}

impl From<Color> for [f32; 4] {
    fn from(color: Color) -> Self {
        [color.r, color.g, color.b, color.a]
    }
}

impl From<Color> for (u8, u8, u8) {
    fn from(color: Color) -> Self {
        let (r, g, b, _) = color.into();
        (r, g, b)
    }
}

impl From<Color> for u32 {
    fn from(color: Color) -> Self {
        let (r, g, b, a): (u8, u8, u8, u8) = color.into();
        let rp = (r as u32) << 24;
        let gp = (g as u32) << 16;
        let bp = (b as u32) << 8;
        let ap = a as u32;
        (rp | gp | bp | ap)
    }
}

#[derive(Copy, Clone, PartialEq, Debug)]
pub enum BlendMode {
    Dummy,
}

/// Specifies whether a shape should be drawn
/// filled or as an outline.
#[derive(Debug, Copy, Clone)]
pub enum DrawMode {
    Line,
    Fill,
}

/// Specifies how to scale up/down images.
#[derive(Debug, Copy, Clone)]
pub enum FilterMode {
    Linear,
    Nearest,
}

use gfx::texture::FilterMethod;

impl From<FilterMethod> for FilterMode {
    fn from(f: FilterMethod) -> Self {
        match f {
            FilterMethod::Scale => FilterMode::Nearest,
            FilterMethod::Bilinear => FilterMode::Linear,
            _ => FilterMode::Linear,
        }
    }
}

impl From<FilterMode> for FilterMethod {
    fn from(f: FilterMode) -> Self {
        match f {
            FilterMode::Nearest => FilterMethod::Scale,
            FilterMode::Linear => FilterMethod::Bilinear,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_color_conversions() {
        let white = Color::new(1.0, 1.0, 1.0, 1.0);
        let w1 = Color::from((255, 255, 255, 255));
        assert_eq!(white, w1);
        let w2: u32 = white.into();
        assert_eq!(w2, 0xFFFFFFFF);

        let grey = Color::new(0.5019608, 0.5019608, 0.5019608, 1.0);
        let g1 = Color::from((128, 128, 128, 255));
        assert_eq!(grey, g1);
        let g2: u32 = grey.into();
        assert_eq!(g2, 0x808080FF);

        let black = Color::new(0.0, 0.0, 0.0, 1.0);
        let b1 = Color::from((0, 0, 0, 255));
        assert_eq!(black, b1);
        let b2: u32 = black.into();
        assert_eq!(b2, 0x000000FF);
    }

    #[test]
    fn test_rect_scaling() {
        let r1 = Rect::new(0.0, 0.0, 128.0, 128.0);
        let r2 = Rect::fraction(0.0, 0.0, 32.0, 32.0, &r1);
        assert_eq!(r2, Rect::new(0.0, 0.0, 0.25, 0.25));

        let r2 = Rect::fraction(32.0, 32.0, 32.0, 32.0, &r1);
        assert_eq!(r2, Rect::new(0.25, 0.25, 0.25, 0.25));
    }
}
<|endoftext|>


<commit_before>//! Details of a group
//!
//! https://docs.gitlab.com/ce/api/groups.html#details-of-a-group
//!
//! # Details of a group
//!
//! Get all details of a group.
//!
//! ```text
//! GET /groups/ID
//! ```
//!
//! Parameters:
//!
//! | Attribute | Type | Required | Description |
//! | --------- | ---- | -------- | ----------- |
//! | `id` | integer/string | yes | The ID or path of a group |
//!

use serde_json;

use BuildQuery;
use Group;

#[derive(Debug, Clone)]
pub struct GroupLister<'a> {
    gl: &'a ::GitLab,
    /// The ID of a project
    id: ::groups::ListingId,
}

impl<'a> GroupLister<'a> {
    pub fn new(gl: &'a ::GitLab, id: ::groups::ListingId) -> GroupLister {
        GroupLister {
            gl: gl,
            id: id,
        }
    }

    /// Commit the lister: Query GitLab and return a list of projects.
    pub fn list(&self) -> Group {
        let query = self.build_query();
        debug!("query: {:?}", query);

        let issue: Result<Group, serde_json::Error> = self.gl.get(&query);

        issue.unwrap()
    }
}

impl<'a> BuildQuery for GroupLister<'a> {
    fn build_query(&self) -> String {
        let mut query = String::from("groups/");

        query.push_str(&match self.id {
            ::groups::ListingId::Id(id) => id.to_string(),
            ::groups::ListingId::NamespaceProject(ref s) => s.replace("/", "%2F"),
        });

        query
    }
}

#[cfg(test)]
mod tests {
    use BuildQuery;

    const TEST_GROUP_ID_I64: i64 = 123;
    const TEST_GROUP_ID_STRING: &'static str = "group/project";

    #[test]
    fn build_query_default_i64() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX");
        // let gl: ::GitLab = Default::default();

        let expected_string = format!("groups/{}", TEST_GROUP_ID_I64);

        let lister = gl.groups();
        let lister = lister.details(::groups::ListingId::Id(TEST_GROUP_ID_I64));
        let query = lister.build_query();
        assert_eq!(query, expected_string);

        let lister = gl.groups().details(::groups::ListingId::Id(TEST_GROUP_ID_I64));
        let query = lister.build_query();
        assert_eq!(query, expected_string);

        let query = gl.groups().details(::groups::ListingId::Id(TEST_GROUP_ID_I64)).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_default_str() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX");
        // let gl: ::GitLab = Default::default();

        let expected_string = format!("groups/{}", TEST_GROUP_ID_STRING.replace("/", "%2F"));

        let lister = gl.groups();
        let lister = lister.details(::groups::ListingId::NamespaceProject(TEST_GROUP_ID_STRING.to_string()));
        let query = lister.build_query();
        assert_eq!(query, expected_string);

        let lister = gl.groups().details(::groups::ListingId::NamespaceProject(TEST_GROUP_ID_STRING.to_string()));
        let query = lister.build_query();
        assert_eq!(query, expected_string);

        let query = gl.groups().details(::groups::ListingId::NamespaceProject(TEST_GROUP_ID_STRING.to_string())).build_query();
        assert_eq!(query, expected_string);
    }
}
<commit_msg>Typo: Variable should be named `group`, not `issue`.<commit_after>//! Details of a group
//!
//! https://docs.gitlab.com/ce/api/groups.html#details-of-a-group
//!
//! # Details of a group
//!
//! Get all details of a group.
//!
//! ```text
//! GET /groups/ID
//! ```
//!
//! Parameters:
//!
//! | Attribute | Type | Required | Description |
//! | --------- | ---- | -------- | ----------- |
//! | `id` | integer/string | yes | The ID or path of a group |
//!

use serde_json;

use BuildQuery;
use Group;

#[derive(Debug, Clone)]
pub struct GroupLister<'a> {
    gl: &'a ::GitLab,
    /// The ID of a project
    id: ::groups::ListingId,
}

impl<'a> GroupLister<'a> {
    pub fn new(gl: &'a ::GitLab, id: ::groups::ListingId) -> GroupLister {
        GroupLister {
            gl: gl,
            id: id,
        }
    }

    /// Commit the lister: Query GitLab and return a group.
    pub fn list(&self) -> Group {
        let query = self.build_query();
        debug!("query: {:?}", query);

        let group: Result<Group, serde_json::Error> = self.gl.get(&query);

        group.unwrap()
    }
}

impl<'a> BuildQuery for GroupLister<'a> {
    fn build_query(&self) -> String {
        let mut query = String::from("groups/");

        query.push_str(&match self.id {
            ::groups::ListingId::Id(id) => id.to_string(),
            ::groups::ListingId::NamespaceProject(ref s) => s.replace("/", "%2F"),
        });

        query
    }
}

#[cfg(test)]
mod tests {
    use BuildQuery;

    const TEST_GROUP_ID_I64: i64 = 123;
    const TEST_GROUP_ID_STRING: &'static str = "group/project";

    #[test]
    fn build_query_default_i64() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX");
        // let gl: ::GitLab = Default::default();

        let expected_string = format!("groups/{}", TEST_GROUP_ID_I64);

        let lister = gl.groups();
        let lister = lister.details(::groups::ListingId::Id(TEST_GROUP_ID_I64));
        let query = lister.build_query();
        assert_eq!(query, expected_string);

        let lister = gl.groups().details(::groups::ListingId::Id(TEST_GROUP_ID_I64));
        let query = lister.build_query();
        assert_eq!(query, expected_string);

        let query = gl.groups().details(::groups::ListingId::Id(TEST_GROUP_ID_I64)).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_default_str() {
        let gl = ::GitLab::new(&"localhost", "XXXXXXXXXXXXXXXXXXXX");
        // let gl: ::GitLab = Default::default();

        let expected_string = format!("groups/{}", TEST_GROUP_ID_STRING.replace("/", "%2F"));

        let lister = gl.groups();
        let lister = lister.details(::groups::ListingId::NamespaceProject(TEST_GROUP_ID_STRING.to_string()));
        let query = lister.build_query();
        assert_eq!(query, expected_string);

        let lister = gl.groups().details(::groups::ListingId::NamespaceProject(TEST_GROUP_ID_STRING.to_string()));
        let query = lister.build_query();
        assert_eq!(query, expected_string);

        let query = gl.groups().details(::groups::ListingId::NamespaceProject(TEST_GROUP_ID_STRING.to_string())).build_query();
        assert_eq!(query, expected_string);
    }
}
<|endoftext|>