<commit_before>//! Gradient Descent
//!
//! Implementation of gradient descent algorithm. Module contains
//! the struct `GradientDesc` which is instantiated within models
//! implementing the Optimizable trait.
//!
//! Currently standard batch gradient descent is the only implemented
//! optimization algorithm but there is flexibility to introduce new
//! algorithms and git them into the same scheme easily.

use learning::optim::{Optimizable, OptimAlgorithm};
use linalg::Vector;
use linalg::Matrix;
use rulinalg::utils;

use learning::toolkit::rand_utils;

const LEARNING_EPS: f64 = 1e-20;

/// Batch Gradient Descent algorithm
#[derive(Clone, Copy, Debug)]
pub struct GradientDesc {
    /// The step-size for the gradient descent steps.
    alpha: f64,
    /// The number of iterations to run.
    iters: usize,
}

/// The default gradient descent algorithm.
///
/// The defaults are:
///
/// - alpha = 0.3
/// - iters = 100
impl Default for GradientDesc {
    fn default() -> GradientDesc {
        GradientDesc {
            alpha: 0.3,
            iters: 100,
        }
    }
}

impl GradientDesc {
    /// Construct a gradient descent algorithm.
    ///
    /// Requires the step size and iteration count
    /// to be specified.
    ///
    /// # Examples
    ///
    /// ```
    /// use rusty_machine::learning::optim::grad_desc::GradientDesc;
    ///
    /// let gd = GradientDesc::new(0.3, 10000);
    /// ```
    pub fn new(alpha: f64, iters: usize) -> GradientDesc {
        assert!(alpha > 0f64,
                "The step size (alpha) must be greater than 0.");

        GradientDesc {
            alpha: alpha,
            iters: iters,
        }
    }
}

impl<M: Optimizable> OptimAlgorithm<M> for GradientDesc {
    fn optimize(&self,
                model: &M,
                start: &[f64],
                inputs: &M::Inputs,
                targets: &M::Targets)
                -> Vec<f64> {

        // Create the initial optimal parameters
        let mut optimizing_val = Vector::new(start.to_vec());
        // The cost at the start of each iteration
        let mut start_iter_cost = 0f64;

        for _ in 0..self.iters {
            // Compute the cost and gradient for the current parameters
            let (cost, grad) = model.compute_grad(optimizing_val.data(), inputs, targets);

            // Early stopping
            if (start_iter_cost - cost).abs() < LEARNING_EPS {
                break;
            } else {
                // Update the optimal parameters using gradient descent
                optimizing_val = &optimizing_val - Vector::new(grad) * self.alpha;
                // Update the latest cost
                start_iter_cost = cost;
            }
        }
        optimizing_val.into_vec()
    }
}

/// Stochastic Gradient Descent algorithm.
///
/// Uses basic momentum to control the learning rate.
#[derive(Clone, Copy, Debug)]
pub struct StochasticGD {
    /// Controls the momentum of the descent
    alpha: f64,
    /// The square root of the raw learning rate.
    mu: f64,
    /// The number of passes through the data.
    iters: usize,
}

/// The default Stochastic GD algorithm.
///
/// The defaults are:
///
/// - alpha = 0.1
/// - mu = 0.1
/// - iters = 20
impl Default for StochasticGD {
    fn default() -> StochasticGD {
        StochasticGD {
            alpha: 0.1,
            mu: 0.1,
            iters: 20,
        }
    }
}

impl StochasticGD {
    /// Construct a stochastic gradient descent algorithm.
    ///
    /// Requires the learning rate, momentum rate and iteration count
    /// to be specified.
    ///
    /// # Examples
    ///
    /// ```
    /// use rusty_machine::learning::optim::grad_desc::StochasticGD;
    ///
    /// let sgd = StochasticGD::new(0.1, 0.3, 5);
    /// ```
    pub fn new(alpha: f64, mu: f64, iters: usize) -> StochasticGD {
        assert!(alpha > 0f64, "The momentum (alpha) must be greater than 0.");
        assert!(mu > 0f64, "The step size (mu) must be greater than 0.");

        StochasticGD {
            alpha: alpha,
            mu: mu,
            iters: iters,
        }
    }
}

impl<M> OptimAlgorithm<M> for StochasticGD
    where M: Optimizable<Inputs = Matrix<f64>, Targets = Matrix<f64>>
{
    fn optimize(&self,
                model: &M,
                start: &[f64],
                inputs: &M::Inputs,
                targets: &M::Targets)
                -> Vec<f64> {

        // Create the initial optimal parameters
        let mut optimizing_val = Vector::new(start.to_vec());
        // Create the momentum based gradient distance
        let mut delta_w = Vector::zeros(start.len());

        // Set up the indices for permutation
        let mut permutation = (0..inputs.rows()).collect::<Vec<_>>();
        // The cost at the start of each iteration
        let mut start_iter_cost = 0f64;

        for _ in 0..self.iters {
            // The cost at the end of each stochastic gd pass
            let mut end_cost = 0f64;
            // Permute the indices
            rand_utils::in_place_fisher_yates(&mut permutation);
            for i in &permutation {
                // Compute the cost and gradient for this data pair
                let (cost, vec_data) = model.compute_grad(optimizing_val.data(),
                                                          &inputs.select_rows(&[*i]),
                                                          &targets.select_rows(&[*i]));

                // Compute the difference in gradient using momentum
                delta_w = Vector::new(vec_data) * self.mu + &delta_w * self.alpha;
                // Update the parameters
                optimizing_val = &optimizing_val - &delta_w * self.mu;
                // Set the end cost (this is only used after the last iteration)
                end_cost += cost;
            }

            end_cost /= inputs.rows() as f64;

            // Early stopping
            if (start_iter_cost - end_cost).abs() < LEARNING_EPS {
                break;
            } else {
                // Update the cost
                start_iter_cost = end_cost;
            }
        }
        optimizing_val.into_vec()
    }
}

/// Adaptive Gradient Descent
///
/// The adaptive gradient descent algorithm (Duchi et al. 2010).
#[derive(Debug)]
pub struct AdaGrad {
    alpha: f64,
    tau: f64,
    iters: usize,
}

impl AdaGrad {
    /// Constructs a new AdaGrad algorithm.
    ///
    /// # Examples
    ///
    /// ```
    /// use rusty_machine::learning::optim::grad_desc::AdaGrad;
    ///
    /// // Create a new AdaGrad algorithm with step size 0.5
    /// // and adaptive scaling constant 1.0
    /// let gd = AdaGrad::new(0.5, 1.0, 100);
    /// ```
    pub fn new(alpha: f64, tau: f64, iters: usize) -> AdaGrad {
        assert!(alpha > 0f64,
                "The step size (alpha) must be greater than 0.");
        assert!(tau >= 0f64,
                "The adaptive constant (tau) cannot be negative.");
        AdaGrad {
            alpha: alpha,
            tau: tau,
            iters: iters,
        }
    }
}

impl Default for AdaGrad {
    fn default() -> AdaGrad {
        AdaGrad {
            alpha: 1f64,
            tau: 3f64,
            iters: 100,
        }
    }
}

impl<M: Optimizable<Inputs = Matrix<f64>, Targets = Matrix<f64>>> OptimAlgorithm<M> for AdaGrad {
    fn optimize(&self,
                model: &M,
                start: &[f64],
                inputs: &M::Inputs,
                targets: &M::Targets)
                -> Vec<f64> {

        // Initialize the adaptive scaling
        let mut ada_s = Vector::zeros(start.len());
        // Initialize the optimal parameters
        let mut optimizing_val = Vector::new(start.to_vec());

        // Set up the indices for permutation
        let mut permutation = (0..inputs.rows()).collect::<Vec<_>>();
        // The cost at the start of each iteration
        let mut start_iter_cost = 0f64;

        for _ in 0..self.iters {
            // The cost at the end of each stochastic gd pass
            let mut end_cost = 0f64;
            // Permute the indices
            rand_utils::in_place_fisher_yates(&mut permutation);
            for i in &permutation {
                // Compute the cost and gradient for this data pair
                let (cost, mut vec_data) = model.compute_grad(optimizing_val.data(),
                                                              &inputs.select_rows(&[*i]),
                                                              &targets.select_rows(&[*i]));
                // Update the adaptive scaling by adding the gradient squared
                utils::in_place_vec_bin_op(ada_s.mut_data(), &vec_data, |x, &y| *x += y * y);

                // Compute the change in gradient
                utils::in_place_vec_bin_op(&mut vec_data, ada_s.data(), |x, &y| {
                    *x = self.alpha * (*x / (self.tau + (y).sqrt()))
                });
                // Update the parameters
                optimizing_val = &optimizing_val - Vector::new(vec_data);
                // Set the end cost (this is only used after the last iteration)
                end_cost += cost;
            }
            end_cost /= inputs.rows() as f64;

            // Early stopping
            if (start_iter_cost - end_cost).abs() < LEARNING_EPS {
                break;
            } else {
                // Update the cost
                start_iter_cost = end_cost;
            }
        }
        optimizing_val.into_vec()
    }
}

/// RMSProp
///
/// The RMSProp algorithm (Hinton et al. 2012).
#[derive(Debug, Clone, Copy)]
pub struct RMSProp {
    /// The base step size of gradient descent steps
    learning_rate: f64,
    /// Factor of averaged square gradients to keep
    decay_rate: f64,
    /// Small value used to avoid divide by zero
    epsilon: f64,
    /// The number of passes through the data
    iters: usize,
}

/// The default RMSProp configuration
///
/// The defaults are:
///
/// - learning_rate = 0.01
/// - decay_rate = 0.9
/// - epsilon = 1.0e-5
/// - iters = 50
impl Default for RMSProp {
    fn default() -> RMSProp {
        RMSProp {
            learning_rate: 0.01,
            decay_rate: 0.9,
            epsilon: 1.0e-5,
            iters: 50
        }
    }
}

impl RMSProp {
    /// Construct an RMSProp algorithm.
    ///
    /// Requires learning rate, decay rate, epsilon, and iteration count.
    ///
    /// #Examples
    ///
    /// ```
    /// use rusty_machine::learning::optim::grad_desc::RMSProp;
    ///
    /// let rms = RMSProp::new(0.99, 0.01, 1e-5, 20);
    /// ```
    pub fn new(learning_rate: f64, decay_rate: f64, epsilon: f64, iters: usize) -> RMSProp {
        assert!(0f64 < learning_rate, "The learning rate must be positive");
        assert!(0f64 < decay_rate && decay_rate < 1f64, "The decay rate must be between 0 and 1");
        assert!(0f64 < epsilon, "Epsilon must be positive");

        RMSProp {
            decay_rate: decay_rate,
            learning_rate: learning_rate,
            epsilon: epsilon,
            iters: iters
        }
    }
}

impl<M> OptimAlgorithm<M> for RMSProp
    where M: Optimizable<Inputs = Matrix<f64>, Targets = Matrix<f64>> {
    fn optimize(&self,
                model: &M,
                start: &[f64],
                inputs: &M::Inputs,
                targets: &M::Targets)
                -> Vec<f64> {
        // Initial parameters
        let mut params = Vector::new(start.to_vec());
        // Running average of squared gradients
        let mut rmsprop_cache = Vector::zeros(start.len());

        // Set up indices for permutation
        let mut permutation = (0..inputs.rows()).collect::<Vec<_>>();
        // The cost at the start of each iteration
        let mut start_iter_cost = 0f64;

        for _ in 0..self.iters {
            // The cost at end of each pass
            let mut end_cost = 0f64;
            // Permute the vertices
            rand_utils::in_place_fisher_yates(&mut permutation);
            for i in &permutation {
                let (cost, grad) = model.compute_grad(params.data(),
                                                      &inputs.select_rows(&[*i]),
                                                      &targets.select_rows(&[*i]));

                let grad = Vector::new(grad);
                let grad_squared = grad.clone().apply(&|x| x*x);
                // Update cached average of squared gradients
                rmsprop_cache = &rmsprop_cache*self.decay_rate + &grad_squared*(1.0 - self.decay_rate);
                // RMSProp update rule
                params = &params - (&grad*self.learning_rate).elediv(
                                    &rmsprop_cache.clone().apply(&|x| (x + self.epsilon).sqrt()));

                end_cost += cost;
            }
            end_cost /= inputs.rows() as f64;

            // Early stopping
            if (start_iter_cost - end_cost).abs() < LEARNING_EPS {
                break;
            } else {
                start_iter_cost = end_cost;
            }
        }
        params.into_vec()
    }
}

#[cfg(test)]
mod tests {

    use super::{GradientDesc, StochasticGD, AdaGrad, RMSProp};

    #[test]
    #[should_panic]
    fn gd_neg_stepsize() {
        let _ = GradientDesc::new(-0.5, 0);
    }

    #[test]
    #[should_panic]
    fn stochastic_gd_neg_momentum() {
        let _ = StochasticGD::new(-0.5, 1f64, 0);
    }

    #[test]
    #[should_panic]
    fn stochastic_gd_neg_stepsize() {
        let _ = StochasticGD::new(0.5, -1f64, 0);
    }

    #[test]
    #[should_panic]
    fn adagrad_neg_stepsize() {
        let _ = AdaGrad::new(-0.5, 1f64, 0);
    }

    #[test]
    #[should_panic]
    fn adagrad_neg_adaptive_scale() {
        let _ = AdaGrad::new(0.5, -1f64, 0);
    }

    #[test]
    #[should_panic]
    fn rmsprop_neg_decay_rate() {
        let _ = RMSProp::new(-0.5, 0.005, 1.0e-5, 0);
    }

    #[test]
    #[should_panic]
    fn rmsprop_neg_epsilon() {
        let _ = RMSProp::new(0.5, 0.005, -1.0e-5, 0);
    }

    #[test]
    #[should_panic]
    fn rmsprop_neg_learning_rate() {
        let _ = RMSProp::new(0.5, -0.005, 1.0e-5, 0);
    }
}
<commit_msg>Made a few RMSProp comments more readable<commit_after>//! Gradient Descent
//!
//! Implementation of gradient descent algorithm. Module contains
//! the struct `GradientDesc` which is instantiated within models
//! implementing the Optimizable trait.
//!
//! Currently standard batch gradient descent is the only implemented
//! optimization algorithm but there is flexibility to introduce new
//! algorithms and git them into the same scheme easily.

use learning::optim::{Optimizable, OptimAlgorithm};
use linalg::Vector;
use linalg::Matrix;
use rulinalg::utils;

use learning::toolkit::rand_utils;

const LEARNING_EPS: f64 = 1e-20;

/// Batch Gradient Descent algorithm
#[derive(Clone, Copy, Debug)]
pub struct GradientDesc {
    /// The step-size for the gradient descent steps.
    alpha: f64,
    /// The number of iterations to run.
    iters: usize,
}

/// The default gradient descent algorithm.
///
/// The defaults are:
///
/// - alpha = 0.3
/// - iters = 100
impl Default for GradientDesc {
    fn default() -> GradientDesc {
        GradientDesc {
            alpha: 0.3,
            iters: 100,
        }
    }
}

impl GradientDesc {
    /// Construct a gradient descent algorithm.
    ///
    /// Requires the step size and iteration count
    /// to be specified.
    ///
    /// # Examples
    ///
    /// ```
    /// use rusty_machine::learning::optim::grad_desc::GradientDesc;
    ///
    /// let gd = GradientDesc::new(0.3, 10000);
    /// ```
    pub fn new(alpha: f64, iters: usize) -> GradientDesc {
        assert!(alpha > 0f64,
                "The step size (alpha) must be greater than 0.");

        GradientDesc {
            alpha: alpha,
            iters: iters,
        }
    }
}

impl<M: Optimizable> OptimAlgorithm<M> for GradientDesc {
    fn optimize(&self,
                model: &M,
                start: &[f64],
                inputs: &M::Inputs,
                targets: &M::Targets)
                -> Vec<f64> {

        // Create the initial optimal parameters
        let mut optimizing_val = Vector::new(start.to_vec());
        // The cost at the start of each iteration
        let mut start_iter_cost = 0f64;

        for _ in 0..self.iters {
            // Compute the cost and gradient for the current parameters
            let (cost, grad) = model.compute_grad(optimizing_val.data(), inputs, targets);

            // Early stopping
            if (start_iter_cost - cost).abs() < LEARNING_EPS {
                break;
            } else {
                // Update the optimal parameters using gradient descent
                optimizing_val = &optimizing_val - Vector::new(grad) * self.alpha;
                // Update the latest cost
                start_iter_cost = cost;
            }
        }
        optimizing_val.into_vec()
    }
}

/// Stochastic Gradient Descent algorithm.
///
/// Uses basic momentum to control the learning rate.
#[derive(Clone, Copy, Debug)]
pub struct StochasticGD {
    /// Controls the momentum of the descent
    alpha: f64,
    /// The square root of the raw learning rate.
    mu: f64,
    /// The number of passes through the data.
    iters: usize,
}

/// The default Stochastic GD algorithm.
///
/// The defaults are:
///
/// - alpha = 0.1
/// - mu = 0.1
/// - iters = 20
impl Default for StochasticGD {
    fn default() -> StochasticGD {
        StochasticGD {
            alpha: 0.1,
            mu: 0.1,
            iters: 20,
        }
    }
}

impl StochasticGD {
    /// Construct a stochastic gradient descent algorithm.
    ///
    /// Requires the learning rate, momentum rate and iteration count
    /// to be specified.
    ///
    /// # Examples
    ///
    /// ```
    /// use rusty_machine::learning::optim::grad_desc::StochasticGD;
    ///
    /// let sgd = StochasticGD::new(0.1, 0.3, 5);
    /// ```
    pub fn new(alpha: f64, mu: f64, iters: usize) -> StochasticGD {
        assert!(alpha > 0f64, "The momentum (alpha) must be greater than 0.");
        assert!(mu > 0f64, "The step size (mu) must be greater than 0.");

        StochasticGD {
            alpha: alpha,
            mu: mu,
            iters: iters,
        }
    }
}

impl<M> OptimAlgorithm<M> for StochasticGD
    where M: Optimizable<Inputs = Matrix<f64>, Targets = Matrix<f64>>
{
    fn optimize(&self,
                model: &M,
                start: &[f64],
                inputs: &M::Inputs,
                targets: &M::Targets)
                -> Vec<f64> {

        // Create the initial optimal parameters
        let mut optimizing_val = Vector::new(start.to_vec());
        // Create the momentum based gradient distance
        let mut delta_w = Vector::zeros(start.len());

        // Set up the indices for permutation
        let mut permutation = (0..inputs.rows()).collect::<Vec<_>>();
        // The cost at the start of each iteration
        let mut start_iter_cost = 0f64;

        for _ in 0..self.iters {
            // The cost at the end of each stochastic gd pass
            let mut end_cost = 0f64;
            // Permute the indices
            rand_utils::in_place_fisher_yates(&mut permutation);
            for i in &permutation {
                // Compute the cost and gradient for this data pair
                let (cost, vec_data) = model.compute_grad(optimizing_val.data(),
                                                          &inputs.select_rows(&[*i]),
                                                          &targets.select_rows(&[*i]));

                // Compute the difference in gradient using momentum
                delta_w = Vector::new(vec_data) * self.mu + &delta_w * self.alpha;
                // Update the parameters
                optimizing_val = &optimizing_val - &delta_w * self.mu;
                // Set the end cost (this is only used after the last iteration)
                end_cost += cost;
            }

            end_cost /= inputs.rows() as f64;

            // Early stopping
            if (start_iter_cost - end_cost).abs() < LEARNING_EPS {
                break;
            } else {
                // Update the cost
                start_iter_cost = end_cost;
            }
        }
        optimizing_val.into_vec()
    }
}

/// Adaptive Gradient Descent
///
/// The adaptive gradient descent algorithm (Duchi et al. 2010).
#[derive(Debug)]
pub struct AdaGrad {
    alpha: f64,
    tau: f64,
    iters: usize,
}

impl AdaGrad {
    /// Constructs a new AdaGrad algorithm.
    ///
    /// # Examples
    ///
    /// ```
    /// use rusty_machine::learning::optim::grad_desc::AdaGrad;
    ///
    /// // Create a new AdaGrad algorithm with step size 0.5
    /// // and adaptive scaling constant 1.0
    /// let gd = AdaGrad::new(0.5, 1.0, 100);
    /// ```
    pub fn new(alpha: f64, tau: f64, iters: usize) -> AdaGrad {
        assert!(alpha > 0f64,
                "The step size (alpha) must be greater than 0.");
        assert!(tau >= 0f64,
                "The adaptive constant (tau) cannot be negative.");
        AdaGrad {
            alpha: alpha,
            tau: tau,
            iters: iters,
        }
    }
}

impl Default for AdaGrad {
    fn default() -> AdaGrad {
        AdaGrad {
            alpha: 1f64,
            tau: 3f64,
            iters: 100,
        }
    }
}

impl<M: Optimizable<Inputs = Matrix<f64>, Targets = Matrix<f64>>> OptimAlgorithm<M> for AdaGrad {
    fn optimize(&self,
                model: &M,
                start: &[f64],
                inputs: &M::Inputs,
                targets: &M::Targets)
                -> Vec<f64> {

        // Initialize the adaptive scaling
        let mut ada_s = Vector::zeros(start.len());
        // Initialize the optimal parameters
        let mut optimizing_val = Vector::new(start.to_vec());

        // Set up the indices for permutation
        let mut permutation = (0..inputs.rows()).collect::<Vec<_>>();
        // The cost at the start of each iteration
        let mut start_iter_cost = 0f64;

        for _ in 0..self.iters {
            // The cost at the end of each stochastic gd pass
            let mut end_cost = 0f64;
            // Permute the indices
            rand_utils::in_place_fisher_yates(&mut permutation);
            for i in &permutation {
                // Compute the cost and gradient for this data pair
                let (cost, mut vec_data) = model.compute_grad(optimizing_val.data(),
                                                              &inputs.select_rows(&[*i]),
                                                              &targets.select_rows(&[*i]));
                // Update the adaptive scaling by adding the gradient squared
                utils::in_place_vec_bin_op(ada_s.mut_data(), &vec_data, |x, &y| *x += y * y);

                // Compute the change in gradient
                utils::in_place_vec_bin_op(&mut vec_data, ada_s.data(), |x, &y| {
                    *x = self.alpha * (*x / (self.tau + (y).sqrt()))
                });
                // Update the parameters
                optimizing_val = &optimizing_val - Vector::new(vec_data);
                // Set the end cost (this is only used after the last iteration)
                end_cost += cost;
            }
            end_cost /= inputs.rows() as f64;

            // Early stopping
            if (start_iter_cost - end_cost).abs() < LEARNING_EPS {
                break;
            } else {
                // Update the cost
                start_iter_cost = end_cost;
            }
        }
        optimizing_val.into_vec()
    }
}

/// RMSProp
///
/// The RMSProp algorithm (Hinton et al. 2012).
#[derive(Debug, Clone, Copy)]
pub struct RMSProp {
    /// The base step size of gradient descent steps
    learning_rate: f64,
    /// Rate at which running total of average square gradients decays
    decay_rate: f64,
    /// Small value used to avoid divide by zero
    epsilon: f64,
    /// The number of passes through the data
    iters: usize,
}

/// The default RMSProp configuration
///
/// The defaults are:
///
/// - learning_rate = 0.01
/// - decay_rate = 0.9
/// - epsilon = 1.0e-5
/// - iters = 50
impl Default for RMSProp {
    fn default() -> RMSProp {
        RMSProp {
            learning_rate: 0.01,
            decay_rate: 0.9,
            epsilon: 1.0e-5,
            iters: 50
        }
    }
}

impl RMSProp {
    /// Construct an RMSProp algorithm.
    ///
    /// Requires learning rate, decay rate, epsilon, and iteration count.
    ///
    /// #Examples
    ///
    /// ```
    /// use rusty_machine::learning::optim::grad_desc::RMSProp;
    ///
    /// let rms = RMSProp::new(0.99, 0.01, 1e-5, 20);
    /// ```
    pub fn new(learning_rate: f64, decay_rate: f64, epsilon: f64, iters: usize) -> RMSProp {
        assert!(0f64 < learning_rate, "The learning rate must be positive");
        assert!(0f64 < decay_rate && decay_rate < 1f64, "The decay rate must be between 0 and 1");
        assert!(0f64 < epsilon, "Epsilon must be positive");

        RMSProp {
            decay_rate: decay_rate,
            learning_rate: learning_rate,
            epsilon: epsilon,
            iters: iters
        }
    }
}

impl<M> OptimAlgorithm<M> for RMSProp
    where M: Optimizable<Inputs = Matrix<f64>, Targets = Matrix<f64>> {
    fn optimize(&self,
                model: &M,
                start: &[f64],
                inputs: &M::Inputs,
                targets: &M::Targets)
                -> Vec<f64> {
        // Initial parameters
        let mut params = Vector::new(start.to_vec());
        // Running average of squared gradients
        let mut rmsprop_cache = Vector::zeros(start.len());

        // Set up indices for permutation
        let mut permutation = (0..inputs.rows()).collect::<Vec<_>>();
        // The cost from the previous iteration
        let mut prev_cost = 0f64;

        for _ in 0..self.iters {
            // The cost at end of each pass
            let mut end_cost = 0f64;
            // Permute the vertices
            rand_utils::in_place_fisher_yates(&mut permutation);
            for i in &permutation {
                let (cost, grad) = model.compute_grad(params.data(),
                                                      &inputs.select_rows(&[*i]),
                                                      &targets.select_rows(&[*i]));

                let grad = Vector::new(grad);
                let grad_squared = grad.clone().apply(&|x| x*x);
                // Update cached average of squared gradients
                rmsprop_cache = &rmsprop_cache*self.decay_rate + &grad_squared*(1.0 - self.decay_rate);
                // RMSProp update rule
                params = &params - (&grad*self.learning_rate).elediv(
                                        &rmsprop_cache.clone().apply(&|x| (x + self.epsilon).sqrt()));

                end_cost += cost;
            }
            end_cost /= inputs.rows() as f64;

            // Early stopping
            if (prev_cost - end_cost).abs() < LEARNING_EPS {
                break;
            } else {
                prev_cost = end_cost;
            }
        }
        params.into_vec()
    }
}

#[cfg(test)]
mod tests {

    use super::{GradientDesc, StochasticGD, AdaGrad, RMSProp};

    #[test]
    #[should_panic]
    fn gd_neg_stepsize() {
        let _ = GradientDesc::new(-0.5, 0);
    }

    #[test]
    #[should_panic]
    fn stochastic_gd_neg_momentum() {
        let _ = StochasticGD::new(-0.5, 1f64, 0);
    }

    #[test]
    #[should_panic]
    fn stochastic_gd_neg_stepsize() {
        let _ = StochasticGD::new(0.5, -1f64, 0);
    }

    #[test]
    #[should_panic]
    fn adagrad_neg_stepsize() {
        let _ = AdaGrad::new(-0.5, 1f64, 0);
    }

    #[test]
    #[should_panic]
    fn adagrad_neg_adaptive_scale() {
        let _ = AdaGrad::new(0.5, -1f64, 0);
    }

    #[test]
    #[should_panic]
    fn rmsprop_neg_decay_rate() {
        let _ = RMSProp::new(-0.5, 0.005, 1.0e-5, 0);
    }

    #[test]
    #[should_panic]
    fn rmsprop_neg_epsilon() {
        let _ = RMSProp::new(0.5, 0.005, -1.0e-5, 0);
    }

    #[test]
    #[should_panic]
    fn rmsprop_neg_learning_rate() {
        let _ = RMSProp::new(0.5, -0.005, 1.0e-5, 0);
    }
}
<|endoftext|>


<commit_before>use std::convert::From;
use std::error::Error as StdError;
use std::fmt::{self, Display};
use std::ffi::NulError;

#[derive(Debug)]
/// The generic "things can fail in a myriad of ways" enum. This type is not
/// indended to be exhaustively matched, and new variants may be added in the
/// future without a major version bump.
pub enum Error {
    InvalidCString(NulError),
    DatabaseError(DatabaseErrorKind, Box<DatabaseErrorInformation+Send>),
    NotFound,
    QueryBuilderError(Box<StdError+Send+Sync>),
    DeserializationError(Box<StdError+Send+Sync>),
    SerializationError(Box<StdError+Send+Sync>),
    #[doc(hidden)]
    __Nonexhaustive,
}

#[derive(Debug, Clone, Copy)]
/// The kind of database error that occurred. This is not meant to exhaustively
/// cover all possible errors, but is used to identify errors which are commonly
/// recovered from programatically. This enum is not intended to be exhaustively
/// matched, and new variants may be added in the future without a major version
/// bump.
pub enum DatabaseErrorKind {
    UniqueViolation,
    UnableToSendCommand,
    #[doc(hidden)]
    __Unknown, // Match against _ instead, more variants may be added in the future
}

pub trait DatabaseErrorInformation {
    fn message(&self) -> &str;
    fn details(&self) -> Option<&str>;
    fn hint(&self) -> Option<&str>;
    fn table_name(&self) -> Option<&str>;
    fn column_name(&self) -> Option<&str>;
    fn constraint_name(&self) -> Option<&str>;
}

impl fmt::Debug for DatabaseErrorInformation+Send {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.message(), f)
    }
}

impl DatabaseErrorInformation for String {
    fn message(&self) -> &str {
        &self
    }

    fn details(&self) -> Option<&str> { None }
    fn hint(&self) -> Option<&str> { None }
    fn table_name(&self) -> Option<&str> { None }
    fn column_name(&self) -> Option<&str> { None }
    fn constraint_name(&self) -> Option<&str> { None }
}

#[derive(Debug)]
pub enum ConnectionError {
    InvalidCString(NulError),
    BadConnection(String),
    InvalidConnectionUrl(String),
    #[doc(hidden)]
    __Nonexhaustive, // Match against _ instead, more variants may be added in the future
}

#[derive(Debug, PartialEq)]
pub enum TransactionError<E> {
    CouldntCreateTransaction(Error),
    UserReturnedError(E),
}

pub type QueryResult<T> = Result<T, Error>;
pub type ConnectionResult<T> = Result<T, ConnectionError>;
pub type TransactionResult<T, E> = Result<T, TransactionError<E>>;

pub trait OptionalExtension<T> {
    fn optional(self) -> Result<Option<T>, Error>;
}

impl<T> OptionalExtension<T> for QueryResult<T> {
    fn optional(self) -> Result<Option<T>, Error> {
        match self {
            Ok(value) => Ok(Some(value)),
            Err(Error::NotFound) => Ok(None),
            Err(e) => Err(e),
        }
    }
}

impl From<NulError> for ConnectionError {
    fn from(e: NulError) -> Self {
        ConnectionError::InvalidCString(e)
    }
}

impl From<NulError> for Error {
    fn from(e: NulError) -> Self {
        Error::InvalidCString(e)
    }
}

impl<E> From<Error> for TransactionError<E> {
    fn from(e: Error) -> Self {
        TransactionError::CouldntCreateTransaction(e)
    }
}

impl From<TransactionError<Error>> for Error {
    fn from(e: TransactionError<Error>) -> Self {
        match e {
            TransactionError::CouldntCreateTransaction(e) => e,
            TransactionError::UserReturnedError(e) => e,
        }
    }
}

impl Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &Error::InvalidCString(ref nul_err) => nul_err.fmt(f),
            &Error::DatabaseError(_, ref e) => write!(f, "{}", e.message()),
            &Error::NotFound => f.write_str("NotFound"),
            &Error::QueryBuilderError(ref e) => e.fmt(f),
            &Error::DeserializationError(ref e) => e.fmt(f),
            &Error::SerializationError(ref e) => e.fmt(f),
            &Error::__Nonexhaustive => unreachable!(),
        }
    }
}

impl StdError for Error {
    fn description(&self) -> &str {
        match self {
            &Error::InvalidCString(ref nul_err) => nul_err.description(),
            &Error::DatabaseError(_, ref e) => e.message(),
            &Error::NotFound => "Record not found",
            &Error::QueryBuilderError(ref e) => e.description(),
            &Error::DeserializationError(ref e) => e.description(),
            &Error::SerializationError(ref e) => e.description(),
            &Error::__Nonexhaustive => unreachable!(),
        }
    }
}

impl Display for ConnectionError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &ConnectionError::InvalidCString(ref nul_err) => nul_err.fmt(f),
            &ConnectionError::BadConnection(ref s) => write!(f, "{}", &s),
            &ConnectionError::InvalidConnectionUrl(ref s) => write!(f, "{}", &s),
            &ConnectionError::__Nonexhaustive => unreachable!(),
        }
    }
}

impl StdError for ConnectionError {
    fn description(&self) -> &str {
        match self {
            &ConnectionError::InvalidCString(ref nul_err) => nul_err.description(),
            &ConnectionError::BadConnection(ref s) => &s,
            &ConnectionError::InvalidConnectionUrl(ref s) => &s,
            &ConnectionError::__Nonexhaustive => unreachable!(),
        }
    }
}

impl<E: Display> Display for TransactionError<E> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &TransactionError::CouldntCreateTransaction(ref e) => e.fmt(f),
            &TransactionError::UserReturnedError(ref e) => e.fmt(f),
        }
    }
}

impl<E: StdError> StdError for TransactionError<E> {
    fn description(&self) -> &str {
        match self {
            &TransactionError::CouldntCreateTransaction(ref e) => e.description(),
            &TransactionError::UserReturnedError(ref e) => e.description(),
        }
    }
}

impl PartialEq for Error {
    fn eq(&self, other: &Error) -> bool {
        match (self, other) {
            (&Error::InvalidCString(ref a), &Error::InvalidCString(ref b)) => a == b,
            (&Error::DatabaseError(_, ref a), &Error::DatabaseError(_, ref b)) =>
                a.message() == b.message(),
            (&Error::NotFound, &Error::NotFound) => true,
            _ => false,
        }
    }
}

#[cfg(test)]
#[allow(warnings)]
fn error_impls_send() {
    let err: Error = unimplemented!();
    let x: &Send = &err;
}
<commit_msg>impl Sync for diesel::result::Error<commit_after>use std::convert::From;
use std::error::Error as StdError;
use std::fmt::{self, Display};
use std::ffi::NulError;

#[derive(Debug)]
/// The generic "things can fail in a myriad of ways" enum. This type is not
/// indended to be exhaustively matched, and new variants may be added in the
/// future without a major version bump.
pub enum Error {
    InvalidCString(NulError),
    DatabaseError(DatabaseErrorKind, Box<DatabaseErrorInformation+Send+Sync>),
    NotFound,
    QueryBuilderError(Box<StdError+Send+Sync>),
    DeserializationError(Box<StdError+Send+Sync>),
    SerializationError(Box<StdError+Send+Sync>),
    #[doc(hidden)]
    __Nonexhaustive,
}

#[derive(Debug, Clone, Copy)]
/// The kind of database error that occurred. This is not meant to exhaustively
/// cover all possible errors, but is used to identify errors which are commonly
/// recovered from programatically. This enum is not intended to be exhaustively
/// matched, and new variants may be added in the future without a major version
/// bump.
pub enum DatabaseErrorKind {
    UniqueViolation,
    UnableToSendCommand,
    #[doc(hidden)]
    __Unknown, // Match against _ instead, more variants may be added in the future
}

pub trait DatabaseErrorInformation {
    fn message(&self) -> &str;
    fn details(&self) -> Option<&str>;
    fn hint(&self) -> Option<&str>;
    fn table_name(&self) -> Option<&str>;
    fn column_name(&self) -> Option<&str>;
    fn constraint_name(&self) -> Option<&str>;
}

impl fmt::Debug for DatabaseErrorInformation+Send+Sync {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.message(), f)
    }
}

impl DatabaseErrorInformation for String {
    fn message(&self) -> &str {
        &self
    }

    fn details(&self) -> Option<&str> { None }
    fn hint(&self) -> Option<&str> { None }
    fn table_name(&self) -> Option<&str> { None }
    fn column_name(&self) -> Option<&str> { None }
    fn constraint_name(&self) -> Option<&str> { None }
}

#[derive(Debug)]
pub enum ConnectionError {
    InvalidCString(NulError),
    BadConnection(String),
    InvalidConnectionUrl(String),
    #[doc(hidden)]
    __Nonexhaustive, // Match against _ instead, more variants may be added in the future
}

#[derive(Debug, PartialEq)]
pub enum TransactionError<E> {
    CouldntCreateTransaction(Error),
    UserReturnedError(E),
}

pub type QueryResult<T> = Result<T, Error>;
pub type ConnectionResult<T> = Result<T, ConnectionError>;
pub type TransactionResult<T, E> = Result<T, TransactionError<E>>;

pub trait OptionalExtension<T> {
    fn optional(self) -> Result<Option<T>, Error>;
}

impl<T> OptionalExtension<T> for QueryResult<T> {
    fn optional(self) -> Result<Option<T>, Error> {
        match self {
            Ok(value) => Ok(Some(value)),
            Err(Error::NotFound) => Ok(None),
            Err(e) => Err(e),
        }
    }
}

impl From<NulError> for ConnectionError {
    fn from(e: NulError) -> Self {
        ConnectionError::InvalidCString(e)
    }
}

impl From<NulError> for Error {
    fn from(e: NulError) -> Self {
        Error::InvalidCString(e)
    }
}

impl<E> From<Error> for TransactionError<E> {
    fn from(e: Error) -> Self {
        TransactionError::CouldntCreateTransaction(e)
    }
}

impl From<TransactionError<Error>> for Error {
    fn from(e: TransactionError<Error>) -> Self {
        match e {
            TransactionError::CouldntCreateTransaction(e) => e,
            TransactionError::UserReturnedError(e) => e,
        }
    }
}

impl Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &Error::InvalidCString(ref nul_err) => nul_err.fmt(f),
            &Error::DatabaseError(_, ref e) => write!(f, "{}", e.message()),
            &Error::NotFound => f.write_str("NotFound"),
            &Error::QueryBuilderError(ref e) => e.fmt(f),
            &Error::DeserializationError(ref e) => e.fmt(f),
            &Error::SerializationError(ref e) => e.fmt(f),
            &Error::__Nonexhaustive => unreachable!(),
        }
    }
}

impl StdError for Error {
    fn description(&self) -> &str {
        match self {
            &Error::InvalidCString(ref nul_err) => nul_err.description(),
            &Error::DatabaseError(_, ref e) => e.message(),
            &Error::NotFound => "Record not found",
            &Error::QueryBuilderError(ref e) => e.description(),
            &Error::DeserializationError(ref e) => e.description(),
            &Error::SerializationError(ref e) => e.description(),
            &Error::__Nonexhaustive => unreachable!(),
        }
    }
}

impl Display for ConnectionError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &ConnectionError::InvalidCString(ref nul_err) => nul_err.fmt(f),
            &ConnectionError::BadConnection(ref s) => write!(f, "{}", &s),
            &ConnectionError::InvalidConnectionUrl(ref s) => write!(f, "{}", &s),
            &ConnectionError::__Nonexhaustive => unreachable!(),
        }
    }
}

impl StdError for ConnectionError {
    fn description(&self) -> &str {
        match self {
            &ConnectionError::InvalidCString(ref nul_err) => nul_err.description(),
            &ConnectionError::BadConnection(ref s) => &s,
            &ConnectionError::InvalidConnectionUrl(ref s) => &s,
            &ConnectionError::__Nonexhaustive => unreachable!(),
        }
    }
}

impl<E: Display> Display for TransactionError<E> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &TransactionError::CouldntCreateTransaction(ref e) => e.fmt(f),
            &TransactionError::UserReturnedError(ref e) => e.fmt(f),
        }
    }
}

impl<E: StdError> StdError for TransactionError<E> {
    fn description(&self) -> &str {
        match self {
            &TransactionError::CouldntCreateTransaction(ref e) => e.description(),
            &TransactionError::UserReturnedError(ref e) => e.description(),
        }
    }
}

impl PartialEq for Error {
    fn eq(&self, other: &Error) -> bool {
        match (self, other) {
            (&Error::InvalidCString(ref a), &Error::InvalidCString(ref b)) => a == b,
            (&Error::DatabaseError(_, ref a), &Error::DatabaseError(_, ref b)) =>
                a.message() == b.message(),
            (&Error::NotFound, &Error::NotFound) => true,
            _ => false,
        }
    }
}

#[cfg(test)]
#[allow(warnings)]
fn error_impls_send() {
    let err: Error = unimplemented!();
    let x: &Send = &err;
}
<|endoftext|>


<commit_before>use std::io::{BufRead, BufReader, Read};
use super::Parser;
use ::errors::{self, Error, ErrorKind};
use ::GameDescriptor;
use ::default_game_descriptor::DefaultGameDescriptor;

/// Parser for files in the life 1.05 format.
pub struct Life105Parser {}

impl Life105Parser {
	/// Construct a new instance.
	pub fn new() -> Self {
		Life105Parser{}
	}
}

impl Parser for Life105Parser {
	fn parse(&mut self, input: Box<Read>) -> errors::Result<Box<GameDescriptor>> {
		let mut ret = DefaultGameDescriptor::new();

		let mut offset = None;
		let mut line_in_block: i16 = 0;
		for (line_num, line) in BufReader::new(input).lines().enumerate() {
			let line_num = line_num + 1; // line numbers don't start at 0
			let line = line
				.map_err(|err| errors::ErrorKind::IOError(err.kind()))?;
			let line = line.trim();

			if line.starts_with("#R") {
				ret.clear_rules();
				parse_rules(line_num, &line, &mut ret)?;
			} else if line.starts_with("#Life") {
				check_file_format(line)?;
			} else if line.starts_with("#N") {
				ret.clear_rules();
				ret.add_survival(2);
				ret.add_survival(3);
				ret.add_birth(3);
			} else if line.starts_with("#P") {
				offset = Some(parse_offset(line_num, &line)?);
				line_in_block = 0;
			} else if let Some((ox, oy)) = offset {
				for (index, char) in line.chars().enumerate() {
					if char == '*' {
						ret.add_live_cell(index as i16 + ox, line_in_block + oy);
					}
				}
				line_in_block += 1;
			}
		}

		Ok(Box::new(ret))
	}
}

fn parse_rules(
	line_num: usize,
	line: &str,
	gd: &mut DefaultGameDescriptor
) -> errors::Result<()> {
	use regex::Regex;

	let regex = Regex::new("#R\\s*(\\d+)\\s*/\\s*(\\d+)\\s*")
		.expect("invalid regex!");

	if let Some(captures) = regex.captures(line) {
		let survival = captures.get(1)
			.expect("Failed to get regex group")
			.as_str();
		let birth = captures.get(2)
			.expect("Failed to get regex group")
			.as_str();

		for char in survival.chars() {
			let digit = char.to_digit(10)
				.expect("Failed to parse u8") as u8;
			gd.add_survival(digit);
		}

		for char in birth.chars() {
			let digit = char.to_digit(10)
				.expect("Failed to parse u8") as u8;
			gd.add_birth(digit);
		}

		Ok(())
	} else {
		bail!(ErrorKind::MalformedLine(line_num))
	}
}

fn parse_offset(
	line_num: usize,
	line: &str,
) -> errors::Result<(i16, i16)> {
	use regex::Regex;

	let regex = Regex::new("#P\\s*([+-]?\\d+)\\s*([+-]?\\d+)\\s*").expect("Invalid regex");
	if let Some(captures) = regex.captures(line) {
		let x = captures.get(1).unwrap().as_str();
		let y = captures.get(2).unwrap().as_str();

		let x = x.parse::<i16>()
			.map_err(|_| Error::from(ErrorKind::CoordinateOutOfRange(line_num)))?;
		let y = y.parse::<i16>()
			.map_err(|_| Error::from(ErrorKind::CoordinateOutOfRange(line_num)))?;

		Ok((x, y))
	} else {
		bail!(errors::ErrorKind::MalformedLine(line_num))
	}
}

fn check_file_format(line: &str) -> errors::Result<()> {
	use regex::Regex;

	let regex = Regex::new("#Life\\s+1.05").expect("Invalid regex");
	if regex.is_match(line) {
		Ok(())
	} else {
		bail!(ErrorKind::InvalidFileFormat)
	}
}

#[cfg(test)]
mod test {
	extern crate io_test_util;

	use super::*;
	use errors::Error;
	use errors::ErrorKind::*;
	use std::io;

	#[test]
	fn parse_rules_should_err() {
		let mut gd = DefaultGameDescriptor::new();
		match parse_rules(5, "#R 23", &mut gd) {
			Err(Error(MalformedLine(5), _)) => {},
			Err(_) => panic!("Wrong error thrown!"),
			_ => panic!("No error thrown!")
		}
	}

	#[test]
	fn parse_rules_should_parse_custom_rules() {
		let mut gd = DefaultGameDescriptor::new();
		parse_rules(1, "#R24 / 1", &mut gd).unwrap();
		assert_eq!(&[2, 4], gd.survival());
		assert_eq!(&[1], gd.birth());
	}

	#[test]
	fn parser_should_understand_default_rules() {
		let mut parser = Life105Parser::new();
		let gd = parser.parse(Box::new("#N".as_bytes())).unwrap();
		assert_eq!(&[2, 3], gd.survival());
		assert_eq!(&[3], gd.birth());
	}

	#[test]
	fn should_correctly_handle_io_errors() {
		use self::io_test_util;

		let mut parser = Life105Parser::new();
		let reader = io_test_util::ErrReader::new(io::ErrorKind::BrokenPipe);

		match parser.parse(Box::new(reader)) {
			Err(Error(IOError(io::ErrorKind::BrokenPipe), _)) => {},
			Err(_) => panic!("Wrong error returned!"),
			_ => panic!("No error returned!")
		}
	}

	#[test]
	fn should_throw_error_on_wrong_format_annotation() {
		let mut parser = Life105Parser::new();
		let input = Box::new("#Life 1.06\n0 0\n1 2".as_bytes());
		let res = parser.parse(input);
		match res {
			Err(Error(InvalidFileFormat, _)) => {},
			Err(Error(x, _)) => panic!("Unexpected error {}", x),
			_ => panic!("No error thrown!"),

		}
	}

	#[test]
	fn should_raise_error_on_invalid_block_header() {
		let mut parser = Life105Parser::new();
		let input = "#P 0 a".as_bytes();
		match parser.parse(Box::new(input)) {
			Err(Error(MalformedLine(1), _)) => {},
			Err(_) => panic!("Wrong error thrown!"),
			_ => panic!("No error thrown!")
		}
	}

	#[test]
	fn parse_offset_should_handle_too_big_coords() {
		match parse_offset(1, "#P 0 32768") {
			Err(Error(CoordinateOutOfRange(1), _)) => {},
			Err(_) => panic!("Wrong error thrown!"),
			Ok(_) => panic!("No error thrown!")
		}
	}
}
<commit_msg>1.05 parser now correctly handles unexpected characters inside blocks<commit_after>use std::io::{BufRead, BufReader, Read};
use super::Parser;
use ::errors::{self, Error, ErrorKind};
use ::GameDescriptor;
use ::default_game_descriptor::DefaultGameDescriptor;

/// Parser for files in the life 1.05 format.
pub struct Life105Parser {}

impl Life105Parser {
	/// Construct a new instance.
	pub fn new() -> Self {
		Life105Parser{}
	}
}

impl Parser for Life105Parser {
	fn parse(&mut self, input: Box<Read>) -> errors::Result<Box<GameDescriptor>> {
		let mut ret = DefaultGameDescriptor::new();

		let mut offset = None;
		let mut line_in_block: i16 = 0;
		for (line_num, line) in BufReader::new(input).lines().enumerate() {
			let line_num = line_num + 1; // line numbers don't start at 0
			let line = line
				.map_err(|err| errors::ErrorKind::IOError(err.kind()))?;
			let line = line.trim();

			if line.starts_with("#R") {
				ret.clear_rules();
				parse_rules(line_num, &line, &mut ret)?;
			} else if line.starts_with("#Life") {
				check_file_format(line)?;
			} else if line.starts_with("#N") {
				ret.clear_rules();
				ret.add_survival(2);
				ret.add_survival(3);
				ret.add_birth(3);
			} else if line.starts_with("#P") {
				offset = Some(parse_offset(line_num, &line)?);
				line_in_block = 0;
			} else if let Some((ox, oy)) = offset {
				for (index, char) in line.chars().enumerate() {
					match char {
						'*' => ret.add_live_cell(index as i16 + ox, line_in_block + oy),
						'.' => {},
						_ => bail!(ErrorKind::MalformedLine(line_num))
					}
				}
				line_in_block += 1;
			}
		}

		Ok(Box::new(ret))
	}
}

fn parse_rules(
	line_num: usize,
	line: &str,
	gd: &mut DefaultGameDescriptor
) -> errors::Result<()> {
	use regex::Regex;

	let regex = Regex::new("#R\\s*(\\d+)\\s*/\\s*(\\d+)\\s*")
		.expect("invalid regex!");

	if let Some(captures) = regex.captures(line) {
		let survival = captures.get(1)
			.expect("Failed to get regex group")
			.as_str();
		let birth = captures.get(2)
			.expect("Failed to get regex group")
			.as_str();

		for char in survival.chars() {
			let digit = char.to_digit(10)
				.expect("Failed to parse u8") as u8;
			gd.add_survival(digit);
		}

		for char in birth.chars() {
			let digit = char.to_digit(10)
				.expect("Failed to parse u8") as u8;
			gd.add_birth(digit);
		}

		Ok(())
	} else {
		bail!(ErrorKind::MalformedLine(line_num))
	}
}

fn parse_offset(
	line_num: usize,
	line: &str,
) -> errors::Result<(i16, i16)> {
	use regex::Regex;

	let regex = Regex::new("#P\\s*([+-]?\\d+)\\s*([+-]?\\d+)\\s*").expect("Invalid regex");
	if let Some(captures) = regex.captures(line) {
		let x = captures.get(1).unwrap().as_str();
		let y = captures.get(2).unwrap().as_str();

		let x = x.parse::<i16>()
			.map_err(|_| Error::from(ErrorKind::CoordinateOutOfRange(line_num)))?;
		let y = y.parse::<i16>()
			.map_err(|_| Error::from(ErrorKind::CoordinateOutOfRange(line_num)))?;

		Ok((x, y))
	} else {
		bail!(errors::ErrorKind::MalformedLine(line_num))
	}
}

fn check_file_format(line: &str) -> errors::Result<()> {
	use regex::Regex;

	let regex = Regex::new("#Life\\s+1.05").expect("Invalid regex");
	if regex.is_match(line) {
		Ok(())
	} else {
		bail!(ErrorKind::InvalidFileFormat)
	}
}

#[cfg(test)]
mod test {
	extern crate io_test_util;

	use super::*;
	use errors::Error;
	use errors::ErrorKind::*;
	use std::io;

	#[test]
	fn parse_rules_should_err() {
		let mut gd = DefaultGameDescriptor::new();
		match parse_rules(5, "#R 23", &mut gd) {
			Err(Error(MalformedLine(5), _)) => {},
			Err(_) => panic!("Wrong error thrown!"),
			_ => panic!("No error thrown!")
		}
	}

	#[test]
	fn parse_rules_should_parse_custom_rules() {
		let mut gd = DefaultGameDescriptor::new();
		parse_rules(1, "#R24 / 1", &mut gd).unwrap();
		assert_eq!(&[2, 4], gd.survival());
		assert_eq!(&[1], gd.birth());
	}

	#[test]
	fn parser_should_understand_default_rules() {
		let mut parser = Life105Parser::new();
		let gd = parser.parse(Box::new("#N".as_bytes())).unwrap();
		assert_eq!(&[2, 3], gd.survival());
		assert_eq!(&[3], gd.birth());
	}

	#[test]
	fn should_correctly_handle_io_errors() {
		use self::io_test_util;

		let mut parser = Life105Parser::new();
		let reader = io_test_util::ErrReader::new(io::ErrorKind::BrokenPipe);

		match parser.parse(Box::new(reader)) {
			Err(Error(IOError(io::ErrorKind::BrokenPipe), _)) => {},
			Err(_) => panic!("Wrong error returned!"),
			_ => panic!("No error returned!")
		}
	}

	#[test]
	fn should_throw_error_on_wrong_format_annotation() {
		let mut parser = Life105Parser::new();
		let input = Box::new("#Life 1.06\n0 0\n1 2".as_bytes());
		let res = parser.parse(input);
		match res {
			Err(Error(InvalidFileFormat, _)) => {},
			Err(Error(x, _)) => panic!("Unexpected error {}", x),
			_ => panic!("No error thrown!"),

		}
	}

	#[test]
	fn should_raise_error_on_invalid_block_header() {
		let mut parser = Life105Parser::new();
		let input = "#P 0 a".as_bytes();
		match parser.parse(Box::new(input)) {
			Err(Error(MalformedLine(1), _)) => {},
			Err(_) => panic!("Wrong error thrown!"),
			_ => panic!("No error thrown!")
		}
	}

	#[test]
	fn parse_offset_should_handle_too_big_coords() {
		match parse_offset(1, "#P 0 32768") {
			Err(Error(CoordinateOutOfRange(1), _)) => {},
			Err(_) => panic!("Wrong error thrown!"),
			Ok(_) => panic!("No error thrown!")
		}
	}

	#[test]
	fn should_raise_error_on_unexpected_character_in_block() {
		let input = Box::new("#P -1 -1\n..*#".as_bytes());
		let mut parser = Life105Parser::new();
		match parser.parse(input) {
			Err(Error(MalformedLine(2), _)) => {},
			_ => panic!("Expected MalformedLine")
		}
	}
}
<|endoftext|>


<commit_before>use super::PPU;
use super::PaletteIndex;
use super::PaletteSet;
use super::TilePattern;
use memory::MemSegment;

const NAMETABLE_WIDTH: usize = 32;

pub struct BackgroundRenderer {
    tile: TilePattern,
    attr: u8,
    fetch: bool,
}

impl Default for BackgroundRenderer {
    fn default() -> BackgroundRenderer {
        BackgroundRenderer {
            tile: Default::default(),
            attr: 0,
            fetch: true,
        }
    }
}

impl PPU {
    pub fn visible_scanline_background(&mut self, pixel: u16, scanline: u16) {
        let x = pixel + self.reg.scroll_x() as u16;
        let y = scanline + self.reg.scroll_y() as u16;
        if self.background_data.fetch {
            let nametable_addr = self.get_nametable_addr(x, y);
            let tile_idx = self.ppu_mem.read(nametable_addr);

            let tile_table = self.reg.ppuctrl.background_table();
            self.background_data.tile = self.read_tile_pattern(tile_idx, y & 0x07, tile_table);

            let attribute_addr = self.get_attribute_addr(x, y);
            self.background_data.attr = self.ppu_mem.read(attribute_addr);

            self.background_data.fetch = false;
        }
    }

    pub fn get_background_pixel(&mut self, screen_x: u16, screen_y: u16) -> PaletteIndex {
        let x = screen_x + self.reg.scroll_x() as u16;
        let y = screen_y + self.reg.scroll_y() as u16;

        let color_id = self.get_color_id(x);
        let palette_id = self.get_palette_id(x, y);

        PaletteIndex {
            set: PaletteSet::Background,
            palette_id: palette_id,
            color_id: color_id,
        }
    }

    fn get_color_id(&mut self, x: u16) -> u8 {
        let pattern = self.background_data.tile;
        let fine_x = x as u32 & 0x07;
        self.background_data.fetch = fine_x == 7;
        self.get_color_in_pattern(pattern, fine_x)
    }

    fn get_nametable_addr(&self, px_x: u16, px_y: u16) -> u16 {
        let x = px_x / 8;
        let y = px_y / 8;
        let result = self.reg.ppuctrl.nametable_addr() + y * NAMETABLE_WIDTH as u16 + x;
        result
    }

    fn get_palette_id(&mut self, x: u16, y: u16) -> u8 {
        let attr = self.background_data.attr;
        self.get_palette_from_attribute(attr, x, y)
    }

    fn get_attribute_addr(&self, x: u16, y: u16) -> u16 {
        let x = x / 32;
        let y = y / 32;
        let attr_table = self.reg.ppuctrl.nametable_addr() + 0x03C0;
        attr_table + (y * 8) + x
    }

    fn get_palette_from_attribute(&self, attr: u8, x: u16, y: u16) -> u8 {
        let mut at = attr;
        if y & 0x10 != 0 {
            at >>= 4
        }
        if x & 0x10 != 0 {
            at >>= 2
        }
        at & 0x03
    }
}
<commit_msg>Fixed bug in background rendering where bottom-left tile was corrupt.<commit_after>use super::PPU;
use super::PaletteIndex;
use super::PaletteSet;
use super::TilePattern;
use memory::MemSegment;

const NAMETABLE_WIDTH: usize = 32;

pub struct BackgroundRenderer {
    tile: TilePattern,
    attr: u8,
}

impl Default for BackgroundRenderer {
    fn default() -> BackgroundRenderer {
        BackgroundRenderer {
            tile: Default::default(),
            attr: 0,
        }
    }
}

impl PPU {
    pub fn visible_scanline_background(&mut self, pixel: u16, scanline: u16) {
        let x = pixel + self.reg.scroll_x() as u16;
        let y = scanline + self.reg.scroll_y() as u16;
        if x % 8 == 0 {
            let nametable_addr = self.get_nametable_addr(x, y);
            let tile_idx = self.ppu_mem.read(nametable_addr);

            let tile_table = self.reg.ppuctrl.background_table();
            self.background_data.tile = self.read_tile_pattern(tile_idx, y & 0x07, tile_table);

            let attribute_addr = self.get_attribute_addr(x, y);
            self.background_data.attr = self.ppu_mem.read(attribute_addr);
        }
    }

    pub fn get_background_pixel(&mut self, screen_x: u16, screen_y: u16) -> PaletteIndex {
        let x = screen_x + self.reg.scroll_x() as u16;
        let y = screen_y + self.reg.scroll_y() as u16;

        let color_id = self.get_color_id(x);
        let palette_id = self.get_palette_id(x, y);

        PaletteIndex {
            set: PaletteSet::Background,
            palette_id: palette_id,
            color_id: color_id,
        }
    }

    fn get_color_id(&mut self, x: u16) -> u8 {
        let pattern = self.background_data.tile;
        let fine_x = x as u32 & 0x07;
        self.get_color_in_pattern(pattern, fine_x)
    }

    pub fn get_nametable_addr(&self, px_x: u16, px_y: u16) -> u16 {
        let x = px_x / 8;
        let y = px_y / 8;
        let result = self.reg.ppuctrl.nametable_addr() + y * NAMETABLE_WIDTH as u16 + x;
        result
    }

    fn get_palette_id(&mut self, x: u16, y: u16) -> u8 {
        let attr = self.background_data.attr;
        self.get_palette_from_attribute(attr, x, y)
    }

    fn get_attribute_addr(&self, x: u16, y: u16) -> u16 {
        let x = x / 32;
        let y = y / 32;
        let attr_table = self.reg.ppuctrl.nametable_addr() + 0x03C0;
        attr_table + (y * 8) + x
    }

    fn get_palette_from_attribute(&self, attr: u8, x: u16, y: u16) -> u8 {
        let mut at = attr;
        if y & 0x10 != 0 {
            at >>= 4
        }
        if x & 0x10 != 0 {
            at >>= 2
        }
        at & 0x03
    }
}
<|endoftext|>


<commit_before>extern crate cgmath;
extern crate mikktspace;

use cgmath::prelude::*;

pub type Face = [u32; 3];
pub type Vec2 = [f32; 2];
pub type Vec3 = [f32; 3];
pub type Vec4 = [f32; 4];

#[derive(Debug)]
struct Vertex {
    position: Vec3,
    normal: Vec3,
    tex_coord: Vec2,
}

#[derive(Debug)]
struct NewVertex {
    position: Vec3,
    normal: Vec3,
    tex_coord: Vec2,
    tangent: Vec4,
}

fn make_cube() -> (Vec<Face>, Vec<Vertex>) {
    struct ControlPoint {
        uv: Vec2,
        dir: Vec3,
    }
    let mut faces = Vec::new();
    let mut ctl_pts = Vec::new();
    let mut vertices = Vec::new();

    // +x plane
    {
        let base = ctl_pts.len() as u32;
        faces.push([base, base + 1, base + 4]);
        faces.push([base + 1, base + 2, base + 4]);
        faces.push([base + 2, base + 3, base + 4]);
        faces.push([base + 3, base, base + 4]);
        ctl_pts.push(ControlPoint { uv: [0.0, 0.0], dir: [1.0, -1.0, 1.0] });
        ctl_pts.push(ControlPoint { uv: [0.0, 1.0], dir: [1.0, -1.0, -1.0] });
        ctl_pts.push(ControlPoint { uv: [1.0, 1.0], dir: [1.0, 1.0, -1.0] });
        ctl_pts.push(ControlPoint { uv: [1.0, 0.0], dir: [1.0, 1.0, 1.0] });
        ctl_pts.push(ControlPoint { uv: [0.5, 0.5], dir: [1.0, 0.0, 0.0] });
    }

    // -x plane
    {
        let base = ctl_pts.len() as u32;
        faces.push([base, base + 1, base + 4]);
        faces.push([base + 1, base + 2, base + 4]);
        faces.push([base + 2, base + 3, base + 4]);
        faces.push([base + 3, base, base + 4]);
        ctl_pts.push(ControlPoint { uv: [1.0, 0.0], dir: [-1.0, 1.0, 1.0] });
        ctl_pts.push(ControlPoint { uv: [1.0, 1.0], dir: [-1.0, 1.0, -1.0] });
        ctl_pts.push(ControlPoint { uv: [0.0, 1.0], dir: [-1.0, -1.0, -1.0] });
        ctl_pts.push(ControlPoint { uv: [0.0, 0.0], dir: [-1.0, -1.0, 1.0] });
        ctl_pts.push(ControlPoint { uv: [0.5, 0.5], dir: [-1.0, 0.0, 0.0] });
    }

    // +y plane
    {
        let base = ctl_pts.len() as u32;
        faces.push([base, base + 1, base + 4]);
        faces.push([base + 1, base + 2, base + 4]);
        faces.push([base + 2, base + 3, base + 4]);
        faces.push([base + 3, base, base + 4]);
        ctl_pts.push(ControlPoint { uv: [0.0, 0.0], dir: [1.0, 1.0, 1.0] });
        ctl_pts.push(ControlPoint { uv: [0.0, 1.0], dir: [1.0, 1.0, -1.0] });
        ctl_pts.push(ControlPoint { uv: [0.0, 1.0], dir: [-1.0, 1.0, -1.0] });
        ctl_pts.push(ControlPoint { uv: [0.0, 0.0], dir: [-1.0, 1.0, 1.0] });
        ctl_pts.push(ControlPoint { uv: [0.0, 0.5], dir: [0.0, 1.0, 0.0] });
    }

    // -y plane
    {
        let base = ctl_pts.len() as u32;
        faces.push([base, base + 1, base + 4]);
        faces.push([base + 1, base + 2, base + 4]);
        faces.push([base + 2, base + 3, base + 4]);
        faces.push([base + 3, base, base + 4]);
        ctl_pts.push(ControlPoint { uv: [0.0, 0.0], dir: [-1.0, -1.0, 1.0] });
        ctl_pts.push(ControlPoint { uv: [0.0, 1.0], dir: [-1.0, -1.0, -1.0] });
        ctl_pts.push(ControlPoint { uv: [0.0, 1.0], dir: [1.0, -1.0, -1.0] });
        ctl_pts.push(ControlPoint { uv: [0.0, 0.0], dir: [1.0, -1.0, 1.0] });
        ctl_pts.push(ControlPoint { uv: [0.0, 0.5], dir: [0.0, -1.0, 0.0] });
    }

    // +z plane
    {
        let base = ctl_pts.len() as u32;
        faces.push([base, base + 1, base + 4]);
        faces.push([base + 1, base + 2, base + 4]);
        faces.push([base + 2, base + 3, base + 4]);
        faces.push([base + 3, base, base + 4]);
        ctl_pts.push(ControlPoint { uv: [0.0, 0.0], dir: [-1.0, 1.0, 1.0] });
        ctl_pts.push(ControlPoint { uv: [0.0, 1.0], dir: [-1.0, -1.0, 1.0] });
        ctl_pts.push(ControlPoint { uv: [1.0, 1.0], dir: [1.0, -1.0, 1.0] });
        ctl_pts.push(ControlPoint { uv: [1.0, 0.0], dir: [1.0, 1.0, 1.0] });
        ctl_pts.push(ControlPoint { uv: [0.5, 0.5], dir: [0.0, 0.0, 1.0] });
    }

    // -z plane
    {
        let base = ctl_pts.len() as u32;
        faces.push([base, base + 1, base + 4]);
        faces.push([base + 1, base + 2, base + 4]);
        faces.push([base + 2, base + 3, base + 4]);
        faces.push([base + 3, base, base + 4]);
        ctl_pts.push(ControlPoint { uv: [1.0, 0.0], dir: [1.0, 1.0, -1.0] });
        ctl_pts.push(ControlPoint { uv: [1.0, 1.0], dir: [1.0, -1.0, -1.0] });
        ctl_pts.push(ControlPoint { uv: [0.0, 1.0], dir: [-1.0, -1.0, -1.0] });
        ctl_pts.push(ControlPoint { uv: [0.0, 0.0], dir: [-1.0, 1.0, -1.0] });
        ctl_pts.push(ControlPoint { uv: [0.5, 0.5], dir: [0.0, 0.0, -1.0] });
    }

    for pt in ctl_pts {
        let p: cgmath::Vector3<f32> = pt.dir.into();
        let n: cgmath::Vector3<f32> = p.normalize();
        let t: cgmath::Vector2<f32> = pt.uv.into();
        vertices.push(Vertex {
            position: (p / 2.0).into(),
            normal: n.into(),
            tex_coord: t.into(),
        });
    }

    (faces, vertices)
}

fn main() {
    let (faces, vertices) = make_cube();
    let vertex = |face, vert| {
        let vs: &[u32; 3] = &faces[face];
        &vertices[vs[vert] as usize]
    };
    let vertices_per_face = || 3;
    let face_count = || faces.len();
    let position = |face, vert| &vertex(face, vert).position;
    let normal = |face, vert| &vertex(face, vert).normal;
    let tex_coord = |face, vert| &vertex(face, vert).tex_coord;

    let mut new_vertices = Vec::new();
    {
        let mut set_tangent = |face, vert, tangent| {
            new_vertices.push(NewVertex {
                position: *position(face, vert),
                normal: *normal(face, vert),
                tex_coord: *tex_coord(face, vert),
                tangent: tangent,
            });
        };
        let ret = mikktspace::generate(
            &vertices_per_face,
            &face_count,
            &position,
            &normal,
            &tex_coord,
            &mut set_tangent,
        );
        assert_eq!(true, ret);
    }

    println!("{:#?}", new_vertices);
}
<commit_msg>Update Rust generate example<commit_after>extern crate cgmath;
extern crate mikktspace;

use cgmath::prelude::*;

pub type Face = [u32; 3];
pub type Vec2 = [f32; 2];
pub type Vec3 = [f32; 3];

#[derive(Debug)]
struct Vertex {
    position: Vec3,
    normal: Vec3,
    tex_coord: Vec2,
}

fn make_cube() -> (Vec<Face>, Vec<Vertex>) {
    struct ControlPoint {
        uv: Vec2,
        dir: Vec3,
    }
    let mut faces = Vec::new();
    let mut ctl_pts = Vec::new();
    let mut vertices = Vec::new();

    // +x plane
    {
        let base = ctl_pts.len() as u32;
        faces.push([base, base + 1, base + 4]);
        faces.push([base + 1, base + 2, base + 4]);
        faces.push([base + 2, base + 3, base + 4]);
        faces.push([base + 3, base, base + 4]);
        ctl_pts.push(ControlPoint { uv: [0.0, 0.0], dir: [1.0, -1.0, 1.0] });
        ctl_pts.push(ControlPoint { uv: [0.0, 1.0], dir: [1.0, -1.0, -1.0] });
        ctl_pts.push(ControlPoint { uv: [1.0, 1.0], dir: [1.0, 1.0, -1.0] });
        ctl_pts.push(ControlPoint { uv: [1.0, 0.0], dir: [1.0, 1.0, 1.0] });
        ctl_pts.push(ControlPoint { uv: [0.5, 0.5], dir: [1.0, 0.0, 0.0] });
    }

    // -x plane
    {
        let base = ctl_pts.len() as u32;
        faces.push([base, base + 1, base + 4]);
        faces.push([base + 1, base + 2, base + 4]);
        faces.push([base + 2, base + 3, base + 4]);
        faces.push([base + 3, base, base + 4]);
        ctl_pts.push(ControlPoint { uv: [1.0, 0.0], dir: [-1.0, 1.0, 1.0] });
        ctl_pts.push(ControlPoint { uv: [1.0, 1.0], dir: [-1.0, 1.0, -1.0] });
        ctl_pts.push(ControlPoint { uv: [0.0, 1.0], dir: [-1.0, -1.0, -1.0] });
        ctl_pts.push(ControlPoint { uv: [0.0, 0.0], dir: [-1.0, -1.0, 1.0] });
        ctl_pts.push(ControlPoint { uv: [0.5, 0.5], dir: [-1.0, 0.0, 0.0] });
    }

    // +y plane
    {
        let base = ctl_pts.len() as u32;
        faces.push([base, base + 1, base + 4]);
        faces.push([base + 1, base + 2, base + 4]);
        faces.push([base + 2, base + 3, base + 4]);
        faces.push([base + 3, base, base + 4]);
        ctl_pts.push(ControlPoint { uv: [0.0, 0.0], dir: [1.0, 1.0, 1.0] });
        ctl_pts.push(ControlPoint { uv: [0.0, 1.0], dir: [1.0, 1.0, -1.0] });
        ctl_pts.push(ControlPoint { uv: [0.0, 1.0], dir: [-1.0, 1.0, -1.0] });
        ctl_pts.push(ControlPoint { uv: [0.0, 0.0], dir: [-1.0, 1.0, 1.0] });
        ctl_pts.push(ControlPoint { uv: [0.0, 0.5], dir: [0.0, 1.0, 0.0] });
    }

    // -y plane
    {
        let base = ctl_pts.len() as u32;
        faces.push([base, base + 1, base + 4]);
        faces.push([base + 1, base + 2, base + 4]);
        faces.push([base + 2, base + 3, base + 4]);
        faces.push([base + 3, base, base + 4]);
        ctl_pts.push(ControlPoint { uv: [0.0, 0.0], dir: [-1.0, -1.0, 1.0] });
        ctl_pts.push(ControlPoint { uv: [0.0, 1.0], dir: [-1.0, -1.0, -1.0] });
        ctl_pts.push(ControlPoint { uv: [0.0, 1.0], dir: [1.0, -1.0, -1.0] });
        ctl_pts.push(ControlPoint { uv: [0.0, 0.0], dir: [1.0, -1.0, 1.0] });
        ctl_pts.push(ControlPoint { uv: [0.0, 0.5], dir: [0.0, -1.0, 0.0] });
    }

    // +z plane
    {
        let base = ctl_pts.len() as u32;
        faces.push([base, base + 1, base + 4]);
        faces.push([base + 1, base + 2, base + 4]);
        faces.push([base + 2, base + 3, base + 4]);
        faces.push([base + 3, base, base + 4]);
        ctl_pts.push(ControlPoint { uv: [0.0, 0.0], dir: [-1.0, 1.0, 1.0] });
        ctl_pts.push(ControlPoint { uv: [0.0, 1.0], dir: [-1.0, -1.0, 1.0] });
        ctl_pts.push(ControlPoint { uv: [1.0, 1.0], dir: [1.0, -1.0, 1.0] });
        ctl_pts.push(ControlPoint { uv: [1.0, 0.0], dir: [1.0, 1.0, 1.0] });
        ctl_pts.push(ControlPoint { uv: [0.5, 0.5], dir: [0.0, 0.0, 1.0] });
    }

    // -z plane
    {
        let base = ctl_pts.len() as u32;
        faces.push([base, base + 1, base + 4]);
        faces.push([base + 1, base + 2, base + 4]);
        faces.push([base + 2, base + 3, base + 4]);
        faces.push([base + 3, base, base + 4]);
        ctl_pts.push(ControlPoint { uv: [1.0, 0.0], dir: [1.0, 1.0, -1.0] });
        ctl_pts.push(ControlPoint { uv: [1.0, 1.0], dir: [1.0, -1.0, -1.0] });
        ctl_pts.push(ControlPoint { uv: [0.0, 1.0], dir: [-1.0, -1.0, -1.0] });
        ctl_pts.push(ControlPoint { uv: [0.0, 0.0], dir: [-1.0, 1.0, -1.0] });
        ctl_pts.push(ControlPoint { uv: [0.5, 0.5], dir: [0.0, 0.0, -1.0] });
    }

    for pt in ctl_pts {
        let p: cgmath::Vector3<f32> = pt.dir.into();
        let n: cgmath::Vector3<f32> = p.normalize();
        let t: cgmath::Vector2<f32> = pt.uv.into();
        vertices.push(Vertex {
            position: (p / 2.0).into(),
            normal: n.into(),
            tex_coord: t.into(),
        });
    }

    (faces, vertices)
}

fn main() {
    let (faces, vertices) = make_cube();
    let vertex = |face, vert| {
        let vs: &[u32; 3] = &faces[face];
        &vertices[vs[vert] as usize]
    };
    let vertices_per_face = || 3;
    let face_count = || faces.len();
    let position = |face, vert| &vertex(face, vert).position;
    let normal = |face, vert| &vertex(face, vert).normal;
    let tex_coord = |face, vert| &vertex(face, vert).tex_coord;

    {
        let mut i = 0;
        let mut set_tangent = |face, vert, tangent| {
            println!(
                "{index}: v: {v:?}, vn: {vn:?}, vt: {vt:?}, vx: {vx:?}",
                index = i,
                v = position(face, vert),
                vn = normal(face, vert),
                vt = tex_coord(face, vert),
                vx = tangent,
            );
            i += 1;
        };
        let ret = mikktspace::generate_tangents(
            &vertices_per_face,
            &face_count,
            &position,
            &normal,
            &tex_coord,
            &mut set_tangent,
        );
        assert_eq!(true, ret);
    }
}
<|endoftext|>


<commit_before>use std::{convert::*, iter::*};
use valora::*;

use itertools::{iproduct, Itertools};
use nalgebra::distance;
use noise::{Fbm, NoiseFn};
use rand::{distributions::*, prelude::*};
use std::rc::Rc;

#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Ellipse {
    pub center: V2,
    pub width: f32,
    pub height: Option<f32>,
    pub phase: f32,
}

impl Ellipse {
    pub fn circle(center: V2, radius: f32) -> Self {
        Ellipse {
            center,
            width: radius,
            height: None,
            phase: 0.0,
        }
    }

    pub fn new(center: V2, width: f32, height: f32) -> Self {
        Self {
            center,
            width,
            height: Some(height),
            phase: 0.0,
        }
    }

    pub fn with_phase(self, phase: f32) -> Self { Self { phase, ..self } }

    pub fn circumphase(&self, p: &V2) -> f32 { (p.y - self.center.y).atan2(p.x - self.center.x) }

    pub fn circumpoint(&self, angle: f32) -> V2 {
        V2::new(
            self.center.x + angle.cos() * self.width,
            self.center.y + angle.sin() * self.height.unwrap_or(self.width),
        )
    }
}

#[derive(Copy, Clone, Debug)]
pub struct NgonIter {
    phase: f32,
    r: f32,
    n: usize,
    c: V2,
    i: usize,
}

impl NgonIter {
    pub fn new(phase: f32, r: f32, c: V2, n: usize) -> Self {
        Self {
            phase,
            r,
            n,
            c,
            i: 0,
        }
    }

    pub fn triangle(phase: f32, r: f32, c: V2) -> Self { Self::new(phase, r, c, 3) }
}

impl Iterator for NgonIter {
    type Item = V2;

    fn next(&mut self) -> Option<Self::Item> {
        if self.i == self.n + 1 {
            return None;
        }

        let completion = self.i as f32 / self.n as f32;
        let theta = (completion * std::f32::consts::PI * 2.0) + self.phase;
        self.i += 1;

        Some(V2::new(
            self.c.x + theta.sin() * self.r,
            self.c.y + theta.cos() * self.r,
        ))
    }
}

impl Draw for NgonIter {
    fn draw(&self, comp: &mut Composition) {
        for (i, v) in (*self).enumerate() {
            comp.line_to(v);
        }
    }
}

pub struct GridIter {
    cols: usize,
    rows: usize,
}

impl GridIter {
    pub fn new(cols: usize, rows: usize) -> Self { Self { cols, rows } }

    pub fn tiles(&self, region_width: f32, region_height: f32) -> impl Iterator<Item = Rect> {
        let tile_width = region_width / (self.cols as f32);
        let tile_height = region_height / (self.rows as f32);
        iproduct!(0..(self.cols), 0..(self.rows)).map(move |(i, j)| {
            let x = (i as f32) * tile_width;
            let y = (j as f32) * tile_height;
            Rect {
                bottom_left: V2::new(x, y),
                width: tile_width,
                height: tile_height,
            }
        })
    }
}

pub struct CosineColours {
    a: V3,
    b: V3,
    c: V3,
    d: V3,
}

impl CosineColours {
    pub fn new(a: V3, b: V3, c: V3, d: V3) -> Self { Self { a, b, c, d } }

    pub fn sample(&self, t: f32) -> V3 {
        self.a + self.b.component_mul(&(self.c * t + self.d).map(f32::cos))
    }
}

fn centroid<'a>(vs: impl Iterator<Item = &'a V2>) -> V2 {
    let mut min = V2::new(std::f32::MAX, std::f32::MAX);
    let mut max = V2::new(std::f32::MIN, std::f32::MIN);
    for v in vs {
        if v.x < min.x {
            min.x = v.x;
        }
        if v.y < min.y {
            min.y = v.y;
        }
        if v.x > max.x {
            max.x = v.x;
        }
        if v.y > max.y {
            max.y = v.y;
        }
    }

    (min + max) / 2.0
}

pub struct Rect {
    bottom_left: V2,
    width: f32,
    height: f32,
}

impl Rect {
    pub fn center(&self) -> V2 {
        V2::new(
            self.bottom_left.x + self.width / 2.,
            self.bottom_left.y + self.height / 2.,
        )
    }
}

impl Draw for Rect {
    fn draw(&self, comp: &mut Composition) {
        comp.move_to(self.bottom_left);
        for v in [
            V2::new(self.bottom_left.x + self.width, self.bottom_left.y),
            V2::new(
                self.bottom_left.x + self.width,
                self.bottom_left.y + self.height,
            ),
            V2::new(self.bottom_left.x, self.bottom_left.y + self.height),
        ]
        .iter()
        .copied()
        {
            comp.line_to(v);
        }
    }
}

pub struct Filled<D>(D);

impl<D: Draw> Draw for Filled<D> {
    fn draw(&self, comp: &mut Composition) {
        self.0.draw(comp);
        comp.fill();
    }
}

pub struct Stroked<D> {
    element: D,
    thickness: f32,
}

impl<D: Draw> Draw for Stroked<D> {
    fn draw(&self, comp: &mut Composition) {
        self.element.draw(comp);
        comp.set_stroke_thickness(self.thickness);
        comp.stroke();
    }
}

fn triangle_fan(
    fbm: &Fbm,
    palette: &CosineColours,
    d: usize,
    max: usize,
    phase_offset: f32,
    r: f32,
    sign: f32,
    p: V2,
    rng: &mut StdRng,
    comp: &mut Composition,
) {
    if d > max {
        return;
    }

    let noise = ((fbm.get([p.x as f64, p.y as f64]) as f32) + 1.) / 2. * sign;
    let color = palette.sample(d as f32 * noise);
    comp.set_color(V4::new(color.x, color.y, color.z, 1.));
    comp.draw(Filled(NgonIter::triangle(
        noise * std::f32::consts::PI,
        2. * r / (d as f32),
        p,
    )));
    let phase = noise * 2. * std::f32::consts::PI;
    let next_point = Ellipse::circle(p, r).circumpoint(phase);

    let children = rng.gen_range(1, 4);
    triangle_fan(
        fbm,
        palette,
        d + 1,
        max,
        phase_offset,
        r,
        sign,
        next_point,
        rng,
        comp,
    );
}

const NOISE_SHADER: &str = include_str!("noise.frag");

fn main() {
    let fbm = Fbm::default();

    let options = Options::from_args();
    run(options, |gpu, world, mut rng, mut render_gate| {
        let noise_shader_builder = gpu.build_shader(NOISE_SHADER).expect("to compile glsl");
        let noise_shader = noise_shader_builder.build().expect("to build noise shader");
        let palette = CosineColours::new(
            V3::new(0.5, 0.5, 0.5),
            V3::new(0.5, 0.5, 0.5),
            V3::new(2.0, 1.0, 1.0),
            V3::new(0.0, 0.25, 0.25),
        );
        render_gate.render_frames(|ctx, mut comp| {
            comp.set_color(V4::new(1.0, 1.0, 1.0, 1.0));
            comp.draw(Filled(*world));
            comp.set_sample_depth(SampleDepth::Super8);

            let c = world.center();
            let r = 10.;
            comp.set_color(V4::new(1., 0., 0., 1.));
            comp.draw(Stroked {
                element: NgonIter::triangle(0., 50., c),
                thickness: 5.,
            });
            /*for i in 0..1000 {
                triangle_fan(
                    &fbm,
                    &palette,
                    0,
                    1000,
                    10.,
                    r,
                    if i % 2 == 0 { 1. } else { -1. },
                    c + V2::new((i % 10) as f32, 0.) + V2::new(0., (i / 10) as f32),
                    rng,
                    comp,
                );
            }*/

            println!("Enqued; render begins now");
        })
    })
    .expect("to run composition");
}
<commit_msg>Cubic benchmark<commit_after>use std::{convert::*, iter::*};
use valora::*;

use itertools::{iproduct, Itertools};
use nalgebra::distance;
use noise::{Fbm, NoiseFn};
use rand::{distributions::*, prelude::*};
use std::rc::Rc;

#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Ellipse {
    pub center: V2,
    pub width: f32,
    pub height: Option<f32>,
    pub phase: f32,
}

impl Ellipse {
    pub fn circle(center: V2, radius: f32) -> Self {
        Ellipse {
            center,
            width: radius,
            height: None,
            phase: 0.0,
        }
    }

    pub fn new(center: V2, width: f32, height: f32) -> Self {
        Self {
            center,
            width,
            height: Some(height),
            phase: 0.0,
        }
    }

    pub fn with_phase(self, phase: f32) -> Self { Self { phase, ..self } }

    pub fn circumphase(&self, p: &V2) -> f32 { (p.y - self.center.y).atan2(p.x - self.center.x) }

    pub fn circumpoint(&self, angle: f32) -> V2 {
        V2::new(
            self.center.x + angle.cos() * self.width,
            self.center.y + angle.sin() * self.height.unwrap_or(self.width),
        )
    }
}

#[derive(Copy, Clone, Debug)]
pub struct NgonIter {
    phase: f32,
    r: f32,
    n: usize,
    c: V2,
    i: usize,
}

impl NgonIter {
    pub fn new(phase: f32, r: f32, c: V2, n: usize) -> Self {
        Self {
            phase,
            r,
            n,
            c,
            i: 0,
        }
    }

    pub fn triangle(phase: f32, r: f32, c: V2) -> Self { Self::new(phase, r, c, 3) }
}

impl Iterator for NgonIter {
    type Item = V2;

    fn next(&mut self) -> Option<Self::Item> {
        if self.i == self.n + 1 {
            return None;
        }

        let completion = self.i as f32 / self.n as f32;
        let theta = (completion * std::f32::consts::PI * 2.0) + self.phase;
        self.i += 1;

        Some(V2::new(
            self.c.x + theta.sin() * self.r,
            self.c.y + theta.cos() * self.r,
        ))
    }
}

impl Draw for NgonIter {
    fn draw(&self, comp: &mut Composition) {
        for (i, v) in (*self).enumerate() {
            comp.line_to(v);
        }
    }
}

pub struct GridIter {
    cols: usize,
    rows: usize,
}

impl GridIter {
    pub fn new(cols: usize, rows: usize) -> Self { Self { cols, rows } }

    pub fn tiles(&self, region_width: f32, region_height: f32) -> impl Iterator<Item = Rect> {
        let tile_width = region_width / (self.cols as f32);
        let tile_height = region_height / (self.rows as f32);
        iproduct!(0..(self.cols), 0..(self.rows)).map(move |(i, j)| {
            let x = (i as f32) * tile_width;
            let y = (j as f32) * tile_height;
            Rect {
                bottom_left: V2::new(x, y),
                width: tile_width,
                height: tile_height,
            }
        })
    }
}

pub struct CosineColours {
    a: V3,
    b: V3,
    c: V3,
    d: V3,
}

impl CosineColours {
    pub fn new(a: V3, b: V3, c: V3, d: V3) -> Self { Self { a, b, c, d } }

    pub fn sample(&self, t: f32) -> V3 {
        self.a
            + self.b.component_mul(
                &(self.c * t + self.d)
                    .map(|v| v * std::f32::consts::PI * 2.)
                    .map(f32::cos),
            )
    }
}

fn centroid<'a>(vs: impl Iterator<Item = &'a V2>) -> V2 {
    let mut min = V2::new(std::f32::MAX, std::f32::MAX);
    let mut max = V2::new(std::f32::MIN, std::f32::MIN);
    for v in vs {
        if v.x < min.x {
            min.x = v.x;
        }
        if v.y < min.y {
            min.y = v.y;
        }
        if v.x > max.x {
            max.x = v.x;
        }
        if v.y > max.y {
            max.y = v.y;
        }
    }

    (min + max) / 2.0
}

pub struct Rect {
    bottom_left: V2,
    width: f32,
    height: f32,
}

impl Rect {
    pub fn center(&self) -> V2 {
        V2::new(
            self.bottom_left.x + self.width / 2.,
            self.bottom_left.y + self.height / 2.,
        )
    }
}

impl Draw for Rect {
    fn draw(&self, comp: &mut Composition) {
        comp.move_to(self.bottom_left);
        for v in [
            V2::new(self.bottom_left.x + self.width, self.bottom_left.y),
            V2::new(
                self.bottom_left.x + self.width,
                self.bottom_left.y + self.height,
            ),
            V2::new(self.bottom_left.x, self.bottom_left.y + self.height),
        ]
        .iter()
        .copied()
        {
            comp.line_to(v);
        }
    }
}

pub struct Filled<D>(D);

impl<D: Draw> Draw for Filled<D> {
    fn draw(&self, comp: &mut Composition) {
        self.0.draw(comp);
        comp.fill();
    }
}

pub struct Stroked<D> {
    element: D,
    thickness: f32,
}

impl<D: Draw> Draw for Stroked<D> {
    fn draw(&self, comp: &mut Composition) {
        self.element.draw(comp);
        comp.set_stroke_thickness(self.thickness);
        comp.stroke();
    }
}

pub struct Squig {
    center: V2,
    r: f32,
}

impl Draw for Squig {
    fn draw(&self, comp: &mut Composition) {
        let start = 0.;
        let end = std::f32::consts::PI;
        let circle = Ellipse::circle(self.center, self.r);

        let phase = std::f32::consts::PI / 2.;

        comp.move_to(circle.circumpoint(start + phase));
        comp.cubic_to(
            circle.circumpoint(end / 4. + phase),
            circle.circumpoint(end / 2. + phase),
            circle.circumpoint(end + phase),
        );
        comp.cubic_to(
            circle.circumpoint(-end / 2. + phase),
            circle.circumpoint(-end / 4. + phase),
            circle.circumpoint(start + phase),
        );
    }
}

fn triangle_fan(
    fbm: &Fbm,
    palette: &CosineColours,
    d: usize,
    max: usize,
    phase_offset: f32,
    r: f32,
    sign: f32,
    p: V2,
    rng: &mut StdRng,
    comp: &mut Composition,
) {
    if d > max {
        return;
    }

    let noise = ((fbm.get([p.x as f64, p.y as f64]) as f32) + 1.) / 2. * sign;
    let color = palette.sample(d as f32 * noise);
    comp.set_color(V4::new(color.x, color.y, color.z, 1.));
    comp.draw(Filled(NgonIter::triangle(
        noise * std::f32::consts::PI,
        2. * r / (d as f32),
        p,
    )));
    let phase = noise * 2. * std::f32::consts::PI;
    let next_point = Ellipse::circle(p, r).circumpoint(phase);

    let children = rng.gen_range(1, 4);
    triangle_fan(
        fbm,
        palette,
        d + 1,
        max,
        phase_offset,
        r,
        sign,
        next_point,
        rng,
        comp,
    );
}

const NOISE_SHADER: &str = include_str!("noise.frag");

fn main() {
    let fbm = Fbm::default();

    let options = Options::from_args();
    run(options, |gpu, world, mut rng, mut render_gate| {
        let noise_shader_builder = gpu.build_shader(NOISE_SHADER).expect("to compile glsl");
        let noise_shader = noise_shader_builder.build().expect("to build noise shader");
        let palette = CosineColours::new(
            V3::new(0.5, 0.5, 0.5),
            V3::new(0.5, 0.5, 0.5),
            V3::new(2.0, 1.0, 0.0),
            V3::new(0.5, 0.20, 0.25),
        );
        render_gate.render_frames(|ctx, mut comp| {
            comp.set_color(V4::new(1.0, 1.0, 1.0, 1.0));
            comp.draw(Filled(*world));
            comp.set_sample_depth(SampleDepth::Super8);

            let r = 400.;
            const COLS: usize = 10;
            const ROWS: usize = 10;
            for c in GridIter::new(COLS, ROWS)
                .tiles(world.width, world.height)
                .map(|r| r.center())
            {
                const COUNT: usize = 20;
                for i in 0..COUNT {
                    let signal = i as f32 / COUNT as f32;
                    let rgb = palette.sample(signal);
                    comp.set_color(V4::new(rgb.x, rgb.y, rgb.z, 1.));
                    comp.draw(Filled(Squig {
                        center: c,
                        r: r - r * signal,
                    }));
                }
            }
            /*for i in 0..1000 {
                triangle_fan(
                    &fbm,
                    &palette,
                    0,
                    1000,
                    10.,
                    r,
                    if i % 2 == 0 { 1. } else { -1. },
                    c + V2::new((i % 10) as f32, 0.) + V2::new(0., (i / 10) as f32),
                    rng,
                    comp,
                );
            }*/

            println!("Enqued; render begins now");
        })
    })
    .expect("to run composition");
}
<|endoftext|>


<commit_before>extern crate gl;
extern crate sdl2;
extern crate yaglw;

use gl::types::*;
use sdl2::event::{Event, EventPump};
use std::mem;

use yaglw::gl_context::GLContext;
use yaglw::shader::Shader;
use yaglw::vertex_buffer::{GLArray, GLBuffer, GLType, VertexAttribData, DrawMode};

#[repr(C)]
#[derive(Copy, Clone)]
struct Vertex {
  pub position: [GLfloat; 2],
  pub color: [GLfloat; 3],
}

const VERTEX_SHADER: &'static str = "
  #version 130

  in vec2 position;
  in vec3 color;

  out vec3 v_color;

  void main() {
    v_color = color;
    gl_Position = vec4(position, 0, 1);
  }
";

const FRAGMENT_SHADER: &'static str = "
  #version 130

  in vec3 v_color;

  void main() {
    gl_FragColor = vec4(v_color, 1.0);
  }
";

pub fn main() {
  let mut sdl = sdl2::init().everything().build().unwrap();

  let window = make_window(&sdl);
  let mut event_pump = sdl.event_pump();

  let _sdl_gl_context = window.gl_create_context().unwrap();

  // Load the OpenGL function pointers.
  gl::load_with(|s| unsafe {
    mem::transmute(sdl2::video::gl_get_proc_address(s))
  });

  let mut gl = unsafe {
    GLContext::new()
  };

  let vertices = [
    Vertex {
      position: [0.0, 0.5],
      color: [1.0, 0.0, 0.0],
    },
    Vertex {
      position: [-0.5, -0.5],
      color: [0.0, 1.0, 0.0],
    },
    Vertex {
      position: [0.5, -0.5],
      color: [0.0, 0.0, 1.0],
    },
  ];

  let mut vbo = GLBuffer::new(&mut gl, 3);
  vbo.push(&mut gl, &vertices);

  let attribs = [
    VertexAttribData {
      name: "position",
      size: 2,
      unit: GLType::Float,
    },
    VertexAttribData {
      name: "color",
      size: 3,
      unit: GLType::Float,
    },
  ];

  let components = [
    ((gl::VERTEX_SHADER, VERTEX_SHADER)),
    ((gl::FRAGMENT_SHADER, FRAGMENT_SHADER)),
  ];

  let shader = Shader::new(&gl, components.iter().map(|&(ty, s)| (ty, String::from(s))));
  shader.use_shader(&mut gl);

  let vao =
    GLArray::new(
      &mut gl,
      &shader,
      &attribs,
      DrawMode::Triangles,
      vbo,
    );
  vao.bind(&mut gl);

  match gl.get_error() {
    gl::NO_ERROR => {},
    err => {
      println!("OpenGL error 0x{:x} in setup", err);
      return;
    },
  }

  while !quit_event(&mut event_pump) {
    gl.clear_buffer();
    vao.draw(&mut gl);
    // swap buffers
    window.gl_swap_window();

    std::thread::sleep_ms(10);
  }
}

fn make_window(sdl: &sdl2::Sdl) -> sdl2::video::Window {
  sdl2::video::gl_attr::set_context_profile(sdl2::video::GLProfile::Core);
  sdl2::video::gl_attr::set_context_version(3, 3);

  // Open the window as fullscreen at the current resolution.
  let mut window =
    sdl2::video::WindowBuilder::new(
      &sdl,
      "Triangle",
      800, 600,
    );

  let window = window.position(0, 0);
  window.opengl();

  window.build().unwrap()
}

fn quit_event(event_pump: &mut EventPump) -> bool {
  loop {
    match event_pump.poll_event() {
      None => {
        return false;
      },
      Some(Event::Quit{..}) => {
        return true;
      }
      Some(Event::AppTerminating{..}) => {
        return true;
      }
      _ => {},
    }
  }
}
<commit_msg>update triangle example<commit_after>extern crate gl;
extern crate sdl2;
extern crate yaglw;

use gl::types::*;
use sdl2::EventPump;
use sdl2::event::Event;
use std::mem;

use yaglw::gl_context::GLContext;
use yaglw::shader::Shader;
use yaglw::vertex_buffer::{GLArray, GLBuffer, GLType, VertexAttribData, DrawMode};

#[repr(C)]
#[derive(Copy, Clone)]
struct Vertex {
  pub position: [GLfloat; 2],
  pub color: [GLfloat; 3],
}

const VERTEX_SHADER: &'static str = "
  #version 330

  in vec2 position;
  in vec3 color;

  out vec3 v_color;

  void main() {
    v_color = color;
    gl_Position = vec4(position, 0, 1);
  }
";

const FRAGMENT_SHADER: &'static str = "
  #version 330

  in vec3 v_color;

  layout (location = 0) out vec4 frag_color;

  void main() {
    frag_color = vec4(v_color, 1.0);
  }
";

pub fn main() {
  let sdl = sdl2::init().unwrap();

  let _event = sdl.event().unwrap();
  let video = sdl.video().unwrap();

  let window = make_window(&video);
  let mut event_pump = sdl.event_pump().unwrap();

  let _sdl_gl_context = window.gl_create_context().unwrap();

  // Load the OpenGL function pointers.
  gl::load_with(|s| unsafe {
    mem::transmute(video.gl_get_proc_address(s))
  });

  let mut gl = unsafe {
    GLContext::new()
  };

  let vertices = [
    Vertex {
      position: [0.0, 0.5],
      color: [1.0, 0.0, 0.0],
    },
    Vertex {
      position: [-0.5, -0.5],
      color: [0.0, 1.0, 0.0],
    },
    Vertex {
      position: [0.5, -0.5],
      color: [0.0, 0.0, 1.0],
    },
  ];

  let mut vbo = GLBuffer::new(&mut gl, 3);
  vbo.push(&mut gl, &vertices);

  let attribs = [
    VertexAttribData {
      name: "position",
      size: 2,
      unit: GLType::Float,
    },
    VertexAttribData {
      name: "color",
      size: 3,
      unit: GLType::Float,
    },
  ];

  let components = [
    ((gl::VERTEX_SHADER, VERTEX_SHADER)),
    ((gl::FRAGMENT_SHADER, FRAGMENT_SHADER)),
  ];

  let shader = Shader::new(&gl, components.iter().map(|&(ty, s)| (ty, String::from(s))));
  shader.use_shader(&mut gl);

  let vao =
    GLArray::new(
      &mut gl,
      &shader,
      &attribs,
      DrawMode::Triangles,
      vbo,
    );
  vao.bind(&mut gl);

  match gl.get_error() {
    gl::NO_ERROR => {},
    err => {
      println!("OpenGL error 0x{:x} in setup", err);
      return;
    },
  }

  while !quit_event(&mut event_pump) {
    gl.clear_buffer();
    vao.draw(&mut gl);
    // swap buffers
    window.gl_swap_window();

    std::thread::sleep_ms(10);
  }
}

fn make_window(video: &sdl2::VideoSubsystem) -> sdl2::video::Window {
  let gl_attr = video.gl_attr();
  gl_attr.set_context_profile(sdl2::video::GLProfile::Core);
  gl_attr.set_context_version(3, 3);

  // Open the window as fullscreen at the current resolution.
  let mut window =
    video.window(
      "Triangle",
      800, 600,
    );

  let window = window.position(0, 0);
  window.opengl();

  window.build().unwrap()
}

fn quit_event(event_pump: &mut EventPump) -> bool {
  loop {
    match event_pump.poll_event() {
      None => {
        return false;
      },
      Some(Event::Quit{..}) => {
        return true;
      }
      Some(Event::AppTerminating{..}) => {
        return true;
      }
      _ => {},
    }
  }
}
<|endoftext|>


<commit_before>use device::Device;

pub struct Timer {
    divider: u16,
    timer_counter: u8,
    timer_modulo: u8,

    timer_clock_select: u8,
    timer_enable: bool,

    tac_edge_delay: u8,
    tac_reload_delay: u8,
}

impl Default for Timer {
    fn default() -> Self {
        Timer {
            divider: 0xabcc,
            timer_counter: 0,
            timer_modulo: 0,

            timer_clock_select: 0,
            timer_enable: false,

            tac_edge_delay: 0,
            tac_reload_delay: 0,
        }
    }
}

impl Timer {
    pub fn read_reg(&self, addr: u16) -> u8 {
        match addr {
            0xff04 => (self.divider >> 8) as u8,
            0xff05 => self.timer_counter,
            0xff06 => self.timer_modulo,
            0xff07 => self.timer_control(),
            _ => panic!("Read from non-timer register in timer {:04x}", addr),
        }
    }

    pub fn write_reg(&mut self, addr: u16, val: u8) {
        match addr {
            0xff04 => self.divider = 0,
            0xff05 => {}, // TODO - confirm whether this is a nop
            0xff06 => self.timer_modulo = val,
            0xff07 => self.set_timer_control(val),
            _ => {
                panic!("Write to non-timer register in timer {:04x} = {:02x}",
                       addr,
                       val)
            }
        };
        self.divider_change();
    }

    pub fn step(&mut self, cycles: u16, _: &mut Device) {
        for _ in 0..cycles {
            if self.tac_reload_delay > 0 {
                self.tac_reload_delay -= 1;
                if self.tac_reload_delay == 0 {
                    self.timer_counter = self.timer_modulo;
                    // TODO - raise timer interrupt
                }
            }
            self.divider = self.divider.wrapping_add(1);

            self.divider_change();
        }
    }

    fn divider_change(&mut self) {
        let new_delay = if self.timer_enable {
            0
        } else {
            match self.timer_clock_select {
                0 => self.divider >> 9 & 0x1,
                1 => self.divider >> 3 & 0x1,
                2 => self.divider >> 5 & 0x1,
                3 => self.divider >> 7 & 0x1,
                _ => unreachable!(),
            }
        };

        if self.tac_edge_delay == 1 && new_delay == 0 {
            let (counter, overflow) = self.timer_counter.overflowing_add(1);
            self.timer_counter = counter;
            if overflow {
                self.tac_reload_delay = 4;
            }
        }
    }

    fn timer_control(&self) -> u8 {
        let enabled = if self.timer_enable { 1 } else { 0 };

        self.timer_clock_select | (enabled << 2)
    }

    fn set_timer_control(&mut self, val: u8) {
        self.timer_enable = val & (1 << 2) != 0;
        self.timer_clock_select = val & 0x3;
    }
}

<commit_msg>Write to TAC sets it and removes any pending interrupt or modulus reset<commit_after>use device::Device;

pub struct Timer {
    divider: u16,
    timer_counter: u8,
    timer_modulo: u8,

    timer_clock_select: u8,
    timer_enable: bool,

    tac_edge_delay: u8,
    tac_reload_delay: u8,
}

impl Default for Timer {
    fn default() -> Self {
        Timer {
            divider: 0xabcc,
            timer_counter: 0,
            timer_modulo: 0,

            timer_clock_select: 0,
            timer_enable: false,

            tac_edge_delay: 0,
            tac_reload_delay: 0,
        }
    }
}

impl Timer {
    pub fn read_reg(&self, addr: u16) -> u8 {
        match addr {
            0xff04 => (self.divider >> 8) as u8,
            0xff05 => self.timer_counter,
            0xff06 => self.timer_modulo,
            0xff07 => self.timer_control(),
            _ => panic!("Read from non-timer register in timer {:04x}", addr),
        }
    }

    pub fn write_reg(&mut self, addr: u16, val: u8) {
        match addr {
            0xff04 => self.divider = 0,
            0xff05 => {
                self.timer_counter = val;
                self.tac_reload_delay = 0;
            }
            0xff06 => self.timer_modulo = val,
            0xff07 => self.set_timer_control(val),
            _ => {
                panic!("Write to non-timer register in timer {:04x} = {:02x}",
                       addr,
                       val)
            }
        };
        self.divider_change();
    }

    pub fn step(&mut self, cycles: u16, _: &mut Device) {
        for _ in 0..cycles {
            if self.tac_reload_delay > 0 {
                self.tac_reload_delay -= 1;
                if self.tac_reload_delay == 0 {
                    self.timer_counter = self.timer_modulo;
                    // TODO - raise timer interrupt
                }
            }
            self.divider = self.divider.wrapping_add(1);

            self.divider_change();
        }
    }

    fn divider_change(&mut self) {
        let new_delay = if self.timer_enable {
            0
        } else {
            match self.timer_clock_select {
                0 => self.divider >> 9 & 0x1,
                1 => self.divider >> 3 & 0x1,
                2 => self.divider >> 5 & 0x1,
                3 => self.divider >> 7 & 0x1,
                _ => unreachable!(),
            }
        };

        if self.tac_edge_delay == 1 && new_delay == 0 {
            let (counter, overflow) = self.timer_counter.overflowing_add(1);
            self.timer_counter = counter;
            if overflow {
                self.tac_reload_delay = 4;
            }
        }
    }

    fn timer_control(&self) -> u8 {
        let enabled = if self.timer_enable { 1 } else { 0 };

        self.timer_clock_select | (enabled << 2)
    }

    fn set_timer_control(&mut self, val: u8) {
        self.timer_enable = val & (1 << 2) != 0;
        self.timer_clock_select = val & 0x3;
    }
}
<|endoftext|>