<commit_before>// Copyright 2016 Martin Ankerl.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Differential Evolution optimizer for rust.
//!
//! Simple and powerful global optimization using a
//! [Self-Adapting Differential Evolution](https://www.researchgate.net/publication/3418914_Self-Adapting_Control_Parameters_in_Differential_Evolution_A_Comparative_Study_on_Numerical_Benchmark_Problems)
//! for Rust. See Wikipedia's article on
//! [Differential Evolution](https://en.wikipedia.org/wiki/Differential_evolution)
//! for more information.
//!
//! ## Usage
//!
//! Add this to your `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! differential-evolution = "*"
//! ```
//!
//! and this to your crate root:
//!
//! ```rust
//! extern crate differential_evolution;
//! ```
//!
//! ## Examples
//!
//! Differential Evolution is a global optimization algorithm that
//! tries to iteratively improve candidate solutions with regards to
//! a user-defined cost function.
//!
//! ### Sum of Squares
//! This example finds the minimum of a simple 5-dimensional function.
//!
//! ```
//! extern crate differential_evolution;
//!
//! use differential_evolution::self_adaptive_de;
//!
//! fn main() {
//!     // create a self adaptive DE with an inital search area
//!     // from -10 to 10 in 5 dimensions.
//!     let mut de = self_adaptive_de(vec![(-10.0, 10.0); 5], |pos| {
//!         // cost function to minimize: sum of squares
//!         pos.iter().fold(0.0, |sum, x| sum + x*x)
//!     });
//!
//!     // perform 10000 cost evaluations
//!     de.nth(10000);
//!
//!     // show the result
//!     let (cost, pos) = de.best().unwrap();
//!     println!("cost: {}", cost);
//!     println!("pos: {:?}", pos);
//! }
//! ```
//!
//! # Similar Crates
//!
//! - [darwin-rs](https://github.com/willi-kappler/darwin-rs)
//! - [RsGenetic](https://github.com/m-decoster/RsGenetic)
//!

extern crate rand;

use rand::distributions::{IndependentSample, Range};

/// Holds all settings for the self adaptive differential evolution
/// algorithm.
pub struct Settings<F, R>
    where F: Fn(&[f32]) -> f32,
          R: rand::Rng
{
    /// The population is initialized with uniform random
    /// for each dimension between the tuple's size.
    /// Beware that this is only the initial state, the DE
    /// will search outside of this initial search space.
    pub min_max_pos: Vec<(f32, f32)>,

    /// Minimum and maximum value for `cr`, the crossover control parameter.
    /// a good value is (0, 1) so cr is randomly choosen between in the full
    /// range of usable CR's from `[0, 1)`.
    pub cr_min_max: (f32, f32),

    /// Probability to change the `cr` value of an individual. Tests with
    /// 0.05, 0.1, 0.2 and 0.3 did not show any significant different
    /// results. So 0.1 seems to be a reasonable choice.
    pub cr_change_probability: f32,

    /// Minimum and maximum value for `f`, the amplification factor of the
    /// difference vector. DE is more sensitive to `F` than it is to `CR`.
    /// In literature, `F` is rarely greater than 1. If `F=0`, the evolution
    /// degenerates to a crossover but no mutation, so a reasonable choise
    /// for f_min_max seems to be (0.1, 1.0).
    pub f_min_max: (f32, f32),

    /// Probability to change the `f` value of an individual. See
    /// `cr_change_probability`, 0.1 is a reasonable choice.
    pub f_change_probability: f32,

    /// Number of individuals for the DE. In many benchmarks, a size of
    /// 100 is used. The choice somewhat depends on the difficulty and the
    /// dimensionality of the  problem to solve. Reasonable choices seem
    /// between 20 and 200.
    pub pop_size: usize,

    /// Random number generator used to generate mutations. If the fitness
    /// function is fairly fast, the random number generator should be
    /// very fast as well. Since it is not necessary to use a cryptographic
    /// secure RNG, the best (fastest) choice is to use `rand::weak_rng()`.
    pub rng: R,

    /// The cost function to minimize. This takes an `&[f32]` and returns
    /// the calculated cost for this position as `f32`. This should be
    /// fast to evaluate, and always produce the same result for the same
    /// input.
    pub cost_function: F,
}

impl<F, R> Settings<F, R>
    where F: Fn(&[f32]) -> f32,
          R: rand::Rng
{
    /// Creates default settings, but with customizable random number generator.
    pub fn min_max_rng(min_max_pos: Vec<(f32, f32)>, cost_function: F, rng: R) -> Settings<F, R> {
        // create settings for the algorithm
        Settings {
            min_max_pos: min_max_pos,

            cr_min_max: (0.0, 1.0),
            cr_change_probability: 0.1,

            f_min_max: (0.1, 1.0),
            f_change_probability: 0.1,

            pop_size: 100,
            rng: rng,

            cost_function: cost_function,
        }
    }
}

impl<F> Settings<F, rand::XorShiftRng>
    where F: Fn(&[f32]) -> f32
{
    /// Creates default settings.
    pub fn new(min_max_pos: Vec<(f32, f32)>, cost_function: F) -> Settings<F, rand::XorShiftRng> {
        Settings::min_max_rng(min_max_pos, cost_function, rand::weak_rng())
    }
}

#[derive(Clone)]
struct Individual {
    pos: Vec<f32>,
    // the lower, the better.
    cost: Option<f32>,

    // control parameters
    cr: f32,
    f: f32,
}

pub struct Population<F, R>
    where F: Fn(&[f32]) -> f32,
          R: rand::Rng
{
    // TODO use a single vector for curr and best and controlparameters?
    curr: Vec<Individual>,
    best: Vec<Individual>,

    settings: Settings<F, R>,

    // index of global best individual. Might be in best or in curr.
    best_idx: Option<usize>,

    // cost value of the global best individual, for quick access
    best_cost_cache: Option<f32>,
    dim: usize,
    between_popsize: Range<usize>,
    between_dim: Range<usize>,
    between_cr: Range<f32>,
    between_f: Range<f32>,

    pop_countdown: usize,
}

impl<F, R> Iterator for Population<F, R>
    where F: Fn(&[f32]) -> f32,
          R: rand::Rng
{
    type Item = f32;

    /// Returns the cost value of the current best
    fn next(&mut self) -> Option<f32> {
        if 0 == self.pop_countdown {
            // if the whole pop has been evaluated, evolve it to update positions.
            // this also copies curr to best, if better.
            self.update_best();
            self.update_positions();
            self.pop_countdown = self.curr.len();
        }

        // perform a single fitness evaluation
        self.pop_countdown -= 1;
        let curr = &mut self.curr[self.pop_countdown];

        let cost = (self.settings.cost_function)(&curr.pos);
        curr.cost = Some(cost);

        // see if we have improved the global best
        if self.best_cost_cache.is_none() || cost < self.best_cost_cache.unwrap() {
            self.best_cost_cache = Some(cost);
            self.best_idx = Some(self.pop_countdown);
        }

        self.best_cost_cache
    }
}

pub fn self_adaptive_de<F>(min_max_pos: Vec<(f32, f32)>,
                           cost_function: F)
                           -> Population<F, rand::XorShiftRng>
    where F: Fn(&[f32]) -> f32
{
    Population::from_settings(Settings::new(min_max_pos, cost_function))
}

impl<F, R> Population<F, R>
    where F: Fn(&[f32]) -> f32,
          R: rand::Rng
{
    // Creates a new population based on the given settings.
    pub fn from_settings(s: Settings<F, R>) -> Population<F, R> {
        assert!(s.min_max_pos.len() >= 1,
                "need at least one element to optimize");

        // create a vector of randomly initialized individuals for current.
        let dim = s.min_max_pos.len();

        // Empty individual, with no cost value (yet)
        let dummy_individual = Individual {
            pos: vec![0.0; dim],
            cost: None,
            cr: 0.0,
            f: 0.0,
        };

        // creates all the empty individuals
        let mut pop = Population {
            curr: vec![dummy_individual.clone(); s.pop_size],
            best: vec![dummy_individual; s.pop_size],
            best_idx: None,
            best_cost_cache: None,
            dim: dim,
            pop_countdown: s.pop_size,
            between_popsize: Range::new(0, s.pop_size),
            between_dim: Range::new(0, dim),
            between_cr: Range::new(s.cr_min_max.0, s.cr_min_max.1),
            between_f: Range::new(s.f_min_max.0, s.f_min_max.1),
            settings: s,
        };

        for ind in &mut pop.curr {
            // init control parameters
            ind.cr = pop.between_cr.ind_sample(&mut pop.settings.rng);
            ind.f = pop.between_f.ind_sample(&mut pop.settings.rng);

            // random range for each dimension
            for d in 0..dim {
                let between_min_max = Range::new(pop.settings.min_max_pos[d].0,
                                                 pop.settings.min_max_pos[d].1);
                ind.pos[d] = between_min_max.ind_sample(&mut pop.settings.rng);
            }
        }

        pop
    }

    fn update_best(&mut self) {
        for i in 0..self.curr.len() {
            let curr = &mut self.curr[i];
            let best = &mut self.best[i];

            // we use <= here, so that the individual moves even if the cost
            // stays the same.
            if best.cost.is_none() || curr.cost.unwrap() <= best.cost.unwrap() {
                // replace individual's best. swap is *much* faster than clone.
                std::mem::swap(curr, best);
            }
        }
    }

    // Modifies all the curr positions. This needs a lot of random numbers, so
    // for a fast cost function it is important to use a fast random number
    // generator.
    fn update_positions(&mut self) {
        let rng = &mut self.settings.rng;
        for i in 0..self.curr.len() {
            // sample 3 different individuals
            let id1 = self.between_popsize.ind_sample(rng);

            let mut id2 = self.between_popsize.ind_sample(rng);
            while id2 == id1 {
                id2 = self.between_popsize.ind_sample(rng);
            }

            let mut id3 = self.between_popsize.ind_sample(rng);
            while id3 == id1 || id3 == id2 {
                id3 = self.between_popsize.ind_sample(rng);
            }

            let curr = &mut self.curr[i];
            let best = &self.best[i];

            // see "Self-Adapting Control Parameters in Differential Evolution:
            // A Comparative Study on Numerical Benchmark Problems"
            if rng.gen::<f32>() < self.settings.cr_change_probability {
                curr.cr = self.between_cr.ind_sample(rng);
            } else {
                curr.cr = best.cr;
            }
            if rng.gen::<f32>() < self.settings.f_change_probability {
                curr.f = self.between_f.ind_sample(rng);
            } else {
                curr.f = best.f;
            }

            let curr_pos = &mut curr.pos;
            let best_pos = &best.pos;
            let best1_pos = &self.best[id1].pos;
            let best2_pos = &self.best[id2].pos;
            let best3_pos = &self.best[id3].pos;

            let forced_mutation_dim = self.between_dim.ind_sample(rng);

            // This implements the DE/rand/1/bin, the most widely used algorithm.
            // See "A Comparative Study of Differential Evolution Variants for
            // Global Optimization (2006)".
            for d in 0..self.dim {
                if d == forced_mutation_dim || rng.gen::<f32>() < curr.cr {
                    curr_pos[d] = best3_pos[d] + curr.f * (best1_pos[d] - best2_pos[d]);
                } else {
                    curr_pos[d] = best_pos[d];
                }
            }

            // reset cost, has to be updated by the user.
            curr.cost = None;
        }
    }

    pub fn best(&self) -> Option<(f32, &[f32])> {
        if let Some(bi) = self.best_idx {
            let curr = &self.curr[bi];
            let best = &self.best[bi];

            if curr.cost.is_none() {
                return Some((best.cost.unwrap(), &best.pos));
            }
            if best.cost.is_none() {
                return Some((curr.cost.unwrap(), &curr.pos));
            }
            if curr.cost.unwrap() < best.cost.unwrap() {
                return Some((curr.cost.unwrap(), &curr.pos));
            }
            return Some((best.cost.unwrap(), &best.pos));
        } else {
            None
        }
    }
}

#[cfg(test)]
mod tests {
    // TODO
}
<commit_msg>documentation, removed unused function<commit_after>// Copyright 2016 Martin Ankerl.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Differential Evolution optimizer for rust.
//!
//! Simple and powerful global optimization using a
//! [Self-Adapting Differential Evolution](https://www.researchgate.net/publication/3418914_Self-Adapting_Control_Parameters_in_Differential_Evolution_A_Comparative_Study_on_Numerical_Benchmark_Problems)
//! for Rust. See Wikipedia's article on
//! [Differential Evolution](https://en.wikipedia.org/wiki/Differential_evolution)
//! for more information.
//!
//! ## Usage
//!
//! Add this to your `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! differential-evolution = "*"
//! ```
//!
//! and this to your crate root:
//!
//! ```rust
//! extern crate differential_evolution;
//! ```
//!
//! ## Examples
//!
//! Differential Evolution is a global optimization algorithm that
//! tries to iteratively improve candidate solutions with regards to
//! a user-defined cost function.
//!
//! ### Sum of Squares
//! This example finds the minimum of a simple 5-dimensional function.
//!
//! ```
//! extern crate differential_evolution;
//!
//! use differential_evolution::self_adaptive_de;
//!
//! fn main() {
//!     // create a self adaptive DE with an inital search area
//!     // from -10 to 10 in 5 dimensions.
//!     let mut de = self_adaptive_de(vec![(-10.0, 10.0); 5], |pos| {
//!         // cost function to minimize: sum of squares
//!         pos.iter().fold(0.0, |sum, x| sum + x*x)
//!     });
//!
//!     // perform 10000 cost evaluations
//!     de.nth(10000);
//!
//!     // show the result
//!     let (cost, pos) = de.best().unwrap();
//!     println!("cost: {}", cost);
//!     println!("pos: {:?}", pos);
//! }
//! ```
//!
//! # Similar Crates
//!
//! - [darwin-rs](https://github.com/willi-kappler/darwin-rs)
//! - [RsGenetic](https://github.com/m-decoster/RsGenetic)
//!

extern crate rand;

use rand::distributions::{IndependentSample, Range};

/// Holds all settings for the self adaptive differential evolution
/// algorithm.
pub struct Settings<F, R>
    where F: Fn(&[f32]) -> f32,
          R: rand::Rng
{
    /// The population is initialized with uniform random
    /// for each dimension between the tuple's size.
    /// Beware that this is only the initial state, the DE
    /// will search outside of this initial search space.
    pub min_max_pos: Vec<(f32, f32)>,

    /// Minimum and maximum value for `cr`, the crossover control parameter.
    /// a good value is (0, 1) so cr is randomly choosen between in the full
    /// range of usable CR's from `[0, 1)`.
    pub cr_min_max: (f32, f32),

    /// Probability to change the `cr` value of an individual. Tests with
    /// 0.05, 0.1, 0.2 and 0.3 did not show any significant different
    /// results. So 0.1 seems to be a reasonable choice.
    pub cr_change_probability: f32,

    /// Minimum and maximum value for `f`, the amplification factor of the
    /// difference vector. DE is more sensitive to `F` than it is to `CR`.
    /// In literature, `F` is rarely greater than 1. If `F=0`, the evolution
    /// degenerates to a crossover but no mutation, so a reasonable choise
    /// for f_min_max seems to be (0.1, 1.0).
    pub f_min_max: (f32, f32),

    /// Probability to change the `f` value of an individual. See
    /// `cr_change_probability`, 0.1 is a reasonable choice.
    pub f_change_probability: f32,

    /// Number of individuals for the DE. In many benchmarks, a size of
    /// 100 is used. The choice somewhat depends on the difficulty and the
    /// dimensionality of the  problem to solve. Reasonable choices seem
    /// between 20 and 200.
    pub pop_size: usize,

    /// Random number generator used to generate mutations. If the fitness
    /// function is fairly fast, the random number generator should be
    /// very fast as well. Since it is not necessary to use a cryptographic
    /// secure RNG, the best (fastest) choice is to use `rand::weak_rng()`.
    pub rng: R,

    /// The cost function to minimize. This takes an `&[f32]` and returns
    /// the calculated cost for this position as `f32`. This should be
    /// fast to evaluate, and always produce the same result for the same
    /// input.
    pub cost_function: F,
}

impl<F> Settings<F, rand::XorShiftRng>
    where F: Fn(&[f32]) -> f32
{
    /// Creates default settings for the differential evolution. It uses the default
    /// parameters as defined in the paper "Self-Adapting Control Parameters in Differential
    /// Evolution: A Comparative Study on Numerical Benchmark Problems", with a population
    /// size of 100. It also uses This uses `rand::weak_rng()` for the fastest random number
    /// generator available.
    ///
    /// For most problems this should be a fairly good parameter set.
    pub fn default(min_max_pos: Vec<(f32, f32)>, cost_function: F) -> Settings<F, rand::XorShiftRng> {
        // create settings for the algorithm
        Settings {
            min_max_pos: min_max_pos,

            cr_min_max: (0.0, 1.0),
            cr_change_probability: 0.1,

            f_min_max: (0.1, 1.0),
            f_change_probability: 0.1,

            pop_size: 100,
            rng: rand::weak_rng(),

            cost_function: cost_function,
        }
    }
}

#[derive(Clone)]
struct Individual {
    pos: Vec<f32>,
    // the lower, the better.
    cost: Option<f32>,

    // control parameters
    cr: f32,
    f: f32,
}

pub struct Population<F, R>
    where F: Fn(&[f32]) -> f32,
          R: rand::Rng
{
    // TODO use a single vector for curr and best and controlparameters?
    curr: Vec<Individual>,
    best: Vec<Individual>,

    settings: Settings<F, R>,

    // index of global best individual. Might be in best or in curr.
    best_idx: Option<usize>,

    // cost value of the global best individual, for quick access
    best_cost_cache: Option<f32>,
    dim: usize,
    between_popsize: Range<usize>,
    between_dim: Range<usize>,
    between_cr: Range<f32>,
    between_f: Range<f32>,

    pop_countdown: usize,
}

impl<F, R> Iterator for Population<F, R>
    where F: Fn(&[f32]) -> f32,
          R: rand::Rng
{
    type Item = f32;

    /// Returns the cost value of the current best
    fn next(&mut self) -> Option<f32> {
        if 0 == self.pop_countdown {
            // if the whole pop has been evaluated, evolve it to update positions.
            // this also copies curr to best, if better.
            self.update_best();
            self.update_positions();
            self.pop_countdown = self.curr.len();
        }

        // perform a single fitness evaluation
        self.pop_countdown -= 1;
        let curr = &mut self.curr[self.pop_countdown];

        let cost = (self.settings.cost_function)(&curr.pos);
        curr.cost = Some(cost);

        // see if we have improved the global best
        if self.best_cost_cache.is_none() || cost < self.best_cost_cache.unwrap() {
            self.best_cost_cache = Some(cost);
            self.best_idx = Some(self.pop_countdown);
        }

        self.best_cost_cache
    }
}

pub fn self_adaptive_de<F>(min_max_pos: Vec<(f32, f32)>,
                           cost_function: F)
                           -> Population<F, rand::XorShiftRng>
    where F: Fn(&[f32]) -> f32
{
    Population::from_settings(Settings::default(min_max_pos, cost_function))
}

impl<F, R> Population<F, R>
    where F: Fn(&[f32]) -> f32,
          R: rand::Rng
{
    // Creates a new population based on the given settings.
    pub fn from_settings(s: Settings<F, R>) -> Population<F, R> {
        assert!(s.min_max_pos.len() >= 1,
                "need at least one element to optimize");

        // create a vector of randomly initialized individuals for current.
        let dim = s.min_max_pos.len();

        // Empty individual, with no cost value (yet)
        let dummy_individual = Individual {
            pos: vec![0.0; dim],
            cost: None,
            cr: 0.0,
            f: 0.0,
        };

        // creates all the empty individuals
        let mut pop = Population {
            curr: vec![dummy_individual.clone(); s.pop_size],
            best: vec![dummy_individual; s.pop_size],
            best_idx: None,
            best_cost_cache: None,
            dim: dim,
            pop_countdown: s.pop_size,
            between_popsize: Range::new(0, s.pop_size),
            between_dim: Range::new(0, dim),
            between_cr: Range::new(s.cr_min_max.0, s.cr_min_max.1),
            between_f: Range::new(s.f_min_max.0, s.f_min_max.1),
            settings: s,
        };

        for ind in &mut pop.curr {
            // init control parameters
            ind.cr = pop.between_cr.ind_sample(&mut pop.settings.rng);
            ind.f = pop.between_f.ind_sample(&mut pop.settings.rng);

            // random range for each dimension
            for d in 0..dim {
                let between_min_max = Range::new(pop.settings.min_max_pos[d].0,
                                                 pop.settings.min_max_pos[d].1);
                ind.pos[d] = between_min_max.ind_sample(&mut pop.settings.rng);
            }
        }

        pop
    }

    fn update_best(&mut self) {
        for i in 0..self.curr.len() {
            let curr = &mut self.curr[i];
            let best = &mut self.best[i];

            // we use <= here, so that the individual moves even if the cost
            // stays the same.
            if best.cost.is_none() || curr.cost.unwrap() <= best.cost.unwrap() {
                // replace individual's best. swap is *much* faster than clone.
                std::mem::swap(curr, best);
            }
        }
    }

    // Modifies all the curr positions. This needs a lot of random numbers, so
    // for a fast cost function it is important to use a fast random number
    // generator.
    fn update_positions(&mut self) {
        let rng = &mut self.settings.rng;
        for i in 0..self.curr.len() {
            // sample 3 different individuals
            let id1 = self.between_popsize.ind_sample(rng);

            let mut id2 = self.between_popsize.ind_sample(rng);
            while id2 == id1 {
                id2 = self.between_popsize.ind_sample(rng);
            }

            let mut id3 = self.between_popsize.ind_sample(rng);
            while id3 == id1 || id3 == id2 {
                id3 = self.between_popsize.ind_sample(rng);
            }

            let curr = &mut self.curr[i];
            let best = &self.best[i];

            // see "Self-Adapting Control Parameters in Differential Evolution:
            // A Comparative Study on Numerical Benchmark Problems"
            if rng.gen::<f32>() < self.settings.cr_change_probability {
                curr.cr = self.between_cr.ind_sample(rng);
            } else {
                curr.cr = best.cr;
            }
            if rng.gen::<f32>() < self.settings.f_change_probability {
                curr.f = self.between_f.ind_sample(rng);
            } else {
                curr.f = best.f;
            }

            let curr_pos = &mut curr.pos;
            let best_pos = &best.pos;
            let best1_pos = &self.best[id1].pos;
            let best2_pos = &self.best[id2].pos;
            let best3_pos = &self.best[id3].pos;

            let forced_mutation_dim = self.between_dim.ind_sample(rng);

            // This implements the DE/rand/1/bin, the most widely used algorithm.
            // See "A Comparative Study of Differential Evolution Variants for
            // Global Optimization (2006)".
            for d in 0..self.dim {
                if d == forced_mutation_dim || rng.gen::<f32>() < curr.cr {
                    curr_pos[d] = best3_pos[d] + curr.f * (best1_pos[d] - best2_pos[d]);
                } else {
                    curr_pos[d] = best_pos[d];
                }
            }

            // reset cost, has to be updated by the user.
            curr.cost = None;
        }
    }

    pub fn best(&self) -> Option<(f32, &[f32])> {
        if let Some(bi) = self.best_idx {
            let curr = &self.curr[bi];
            let best = &self.best[bi];

            if curr.cost.is_none() {
                return Some((best.cost.unwrap(), &best.pos));
            }
            if best.cost.is_none() {
                return Some((curr.cost.unwrap(), &curr.pos));
            }
            if curr.cost.unwrap() < best.cost.unwrap() {
                return Some((curr.cost.unwrap(), &curr.pos));
            }
            return Some((best.cost.unwrap(), &best.pos));
        } else {
            None
        }
    }
}

#[cfg(test)]
mod tests {
    // TODO
}
<|endoftext|>


<commit_before>//! A library implementation of so-called `static_assert` based on a macro which
//! conditionally expands into an integer overflow, breaking the compilation.
//!
//! It can take one or more constant boolean expressions as its parameters.
//! When you have multiple conditions to verify, pass them to a single
//! invocation of this macro as multiple parameters.
//!
//! **You cannot invoke this macro more than once in a given scope.**
//! This is because its implementation is based on a type synonym definition and
//! two or more invocation causes name collision.
//!
//! ## Example
//! ```
//! #[macro_use]
//! extern crate static_assert_macro;
//!
//! static_assert!(0 < 1, 1 < 2, 2 < 3);
//!
//! fn main() {
//!     const FOUR: i32 = 4;
//!     static_assert!(1 + 2 == { FOUR - 1 });
//! }
//! ```

#[macro_export]
macro_rules! static_assert {
    (let $e:expr; ) => (
        type _ArrayForStaticAssert = [i8; ($e) as usize - 1];
    );

    (let $e:expr; $e1:expr $(, $ee:expr)*) => (
        static_assert!(let ($e) && ($e1); $($ee),*);
    );

    ($e:expr $(, $ee:expr)*) => (
        static_assert!(let true && ($e); $($ee),*);
    );
}

#[cfg(test)]
mod tests {
    const _FOUR: i32 = 4;

    #[test]
    fn test_0() {
        static_assert!(1 + 2 == { _FOUR - 1 }, 2 < 3);
    }
    #[test]
    fn test_1() {
        static_assert!(true);
    }
    #[test]
    fn test_2() {
        // static_assert!(1); // should not compile
    }
}
<commit_msg>Use a function definition to fix #3<commit_after>//! A library implementation of so-called `static_assert` based on a macro which
//! conditionally expands into an integer overflow, breaking the compilation.
//!
//! It can take one or more constant boolean expressions as its parameters.
//! When you have multiple conditions to verify, pass them to a single
//! invocation of this macro as multiple parameters.
//!
//! **You cannot invoke this macro more than once in a given scope.**
//! This is because its implementation is based on a function definition and
//! two or more invocations cause name collision.
//!
//! ## Example
//! ```
//! #[macro_use]
//! extern crate static_assert_macro;
//!
//! static_assert!(0 < 1, 1 < 2, 2 < 3);
//!
//! fn main() {
//!     const FOUR: i32 = 4;
//!     static_assert!(1 + 2 == { FOUR - 1 });
//! }
//! ```

#[macro_export]
macro_rules! static_assert {
    (let $e:expr; ) => (
        fn _fn_for_static_assert(_: [i8; ($e) as usize - 1]) {}
    );

    (let $e:expr; $e1:expr $(, $ee:expr)*) => (
        static_assert!(let ($e) && ($e1); $($ee),*);
    );

    ($e:expr $(, $ee:expr)*) => (
        static_assert!(let true && ($e); $($ee),*);
    );
}

#[cfg(test)]
mod tests {
    #[test]
    fn test_simple() {
        static_assert!(true);
    }

    #[test]
    fn test_simple_failure() {
        // static_assert!(false); // should not compile
    }

    const _FOUR: i32 = 4;

    #[test]
    fn test_const_arithmetic() {
        static_assert!(1 + 2 == { _FOUR - 1 });
    }

    #[test]
    fn test_multiple_asserts() {
        static_assert!(0 < 1, 1 < 2, 2 < 3);
    }

    #[test]
    fn test_failure_in_multiple_asserts() {
        // static_assert!(0 < 1, 9999 < 2, 2 < 3); // should not compile
    }

    #[test]
    fn test_integral_args_are_not_supported() {
        // static_assert!(1); // should not compile
    }
}
<|endoftext|>


<commit_before>extern crate crc;
extern crate libc;
extern crate miniz_oxide;

use std::{cmp, ptr, slice};
use std::panic::{catch_unwind, AssertUnwindSafe};
use crc::{Hasher32, crc32};

use libc::{c_int, c_uint, c_ulong, c_void, size_t};

use miniz_oxide::{MZError, MZResult};
#[allow(bad_style)]
pub use tdef::tdefl_compressor;

pub use miniz_oxide::mz_adler32_oxide;
use miniz_oxide::deflate::CompressionLevel;
use miniz_oxide::deflate::core::CompressionStrategy;

#[macro_use]
mod unmangle;

pub mod lib_oxide;
use lib_oxide::*;

mod tinfl;
pub use tinfl::{tinfl_decompress, tinfl_decompress_mem_to_heap,
                tinfl_decompress_mem_to_mem, tinfl_decompressor};

mod tdef;
pub use tdef::{tdefl_compress, tdefl_compress_buffer, tdefl_compress_mem_to_heap,
               tdefl_compress_mem_to_mem, tdefl_compress_mem_to_output,
               tdefl_create_comp_flags_from_zip_params, tdefl_get_prev_return_status, tdefl_init,
               tdefl_allocate, tdefl_deallocate, tdefl_get_adler32};

pub use tdef::flush_modes::*;
pub use tdef::strategy::*;

pub fn mz_crc32_oxide(crc32: c_uint, data: &[u8]) -> c_uint {
    let mut digest = crc32::Digest::new_with_initial(crc32::IEEE, crc32);
    digest.write(data);
    digest.sum32()
}

pub const MZ_DEFLATED: c_int = 8;
pub const MZ_DEFAULT_WINDOW_BITS: c_int = 15;

pub const MZ_ADLER32_INIT: c_ulong = 1;
pub const MZ_CRC32_INIT: c_ulong = 0;

fn as_c_return_code(r: MZResult) -> c_int {
    match r {
        Err(status) => status as c_int,
        Ok(status) => status as c_int,
    }
}

unmangle!(
pub unsafe extern "C" fn miniz_def_alloc_func(
    _opaque: *mut c_void,
    items: size_t,
    size: size_t,
) -> *mut c_void {
    libc::malloc(items * size)
}

pub unsafe extern "C" fn miniz_def_free_func(_opaque: *mut c_void, address: *mut c_void) {
    libc::free(address)
}

pub unsafe extern "C" fn miniz_def_realloc_func(
    _opaque: *mut c_void,
    address: *mut c_void,
    items: size_t,
    size: size_t,
) -> *mut c_void {
    libc::realloc(address, items * size)
}

pub unsafe extern "C" fn mz_adler32(adler: c_ulong, ptr: *const u8, buf_len: usize) -> c_ulong {
    ptr.as_ref().map_or(MZ_ADLER32_INIT, |r| {
        let data = slice::from_raw_parts(r, buf_len);
        mz_adler32_oxide(adler as c_uint, data) as c_ulong
    })
}

pub unsafe extern "C" fn mz_crc32(crc: c_ulong, ptr: *const u8, buf_len: size_t) -> c_ulong {
    ptr.as_ref().map_or(MZ_CRC32_INIT, |r| {
        let data = slice::from_raw_parts(r, buf_len);
        mz_crc32_oxide(crc as c_uint, data) as c_ulong
    })
}
);

macro_rules! oxidize {
    ($mz_func:ident, $mz_func_oxide:ident; $($arg_name:ident: $type_name:ident),*) => {
        unmangle!(
        pub unsafe extern "C" fn $mz_func(stream: *mut mz_stream, $($arg_name: $type_name),*)
                                          -> c_int {
            match stream.as_mut() {
                None => MZError::Stream as c_int,
                Some(stream) => {
                    // Make sure we catch a potential panic, as
                    // this is called from C.
                    match catch_unwind(AssertUnwindSafe(|| {
                        let mut stream_oxide = StreamOxide::new(&mut *stream);
                        let status = $mz_func_oxide(&mut stream_oxide, $($arg_name),*);
                        *stream = stream_oxide.into_mz_stream();
                        as_c_return_code(status)
                    })) {
                        Ok(res) => res,
                        Err(_) => {
                            println!("FATAL ERROR: Caught panic!");
                            MZError::Stream as c_int},
                    }
                }
            }
        });
    };
}

oxidize!(mz_deflateInit2, mz_deflate_init2_oxide;
         level: c_int, method: c_int, window_bits: c_int, mem_level: c_int, strategy: c_int);
oxidize!(mz_deflate, mz_deflate_oxide;
         flush: c_int);
oxidize!(mz_deflateEnd, mz_deflate_end_oxide;);
oxidize!(mz_deflateReset, mz_deflate_reset_oxide;);

oxidize!(mz_inflateInit2, mz_inflate_init2_oxide;
         window_bits: c_int);
oxidize!(mz_inflate, mz_inflate_oxide;
         flush: c_int);
oxidize!(mz_inflateEnd, mz_inflate_end_oxide;);

unmangle!(
pub unsafe extern "C" fn mz_deflateInit(stream: *mut mz_stream, level: c_int) -> c_int {
    mz_deflateInit2(
        stream,
        level,
        MZ_DEFLATED,
        MZ_DEFAULT_WINDOW_BITS,
        9,
        CompressionStrategy::Default as c_int,
    )
}

pub unsafe extern "C" fn mz_compress(
    dest: *mut u8,
    dest_len: *mut c_ulong,
    source: *const u8,
    source_len: c_ulong,
) -> c_int {
    mz_compress2(
        dest,
        dest_len,
        source,
        source_len,
        CompressionLevel::DefaultCompression as c_int,
    )
}

pub unsafe extern "C" fn mz_compress2(
    dest: *mut u8,
    dest_len: *mut c_ulong,
    source: *const u8,
    source_len: c_ulong,
    level: c_int,
) -> c_int {
    dest_len.as_mut().map_or(
        MZError::Param as c_int,
        |dest_len| {
            if buffer_too_large(source_len, *dest_len) {
                return MZError::Param as c_int;
            }

            let mut stream: mz_stream = mz_stream {
                next_in: source,
                avail_in: source_len as c_uint,
                next_out: dest,
                avail_out: (*dest_len) as c_uint,
                ..Default::default()
            };

            let mut stream_oxide = StreamOxide::new(&mut stream);
            as_c_return_code(mz_compress2_oxide(&mut stream_oxide, level, dest_len))
        },
    )
}

pub extern "C" fn mz_deflateBound(_stream: *mut mz_stream, source_len: c_ulong) -> c_ulong {
    cmp::max(
        128 + (source_len * 110) / 100,
        128 + source_len + ((source_len / (31 * 1024)) + 1) * 5,
    )
}

pub unsafe extern "C" fn mz_inflateInit(stream: *mut mz_stream) -> c_int {
    mz_inflateInit2(stream, MZ_DEFAULT_WINDOW_BITS)
}

pub unsafe extern "C" fn mz_uncompress(
    dest: *mut u8,
    dest_len: *mut c_ulong,
    source: *const u8,
    source_len: c_ulong,
) -> c_int {
    dest_len.as_mut().map_or(
        MZError::Param as c_int,
        |dest_len| {
            if buffer_too_large(source_len, *dest_len) {
                return MZError::Param as c_int;
            }

            let mut stream: mz_stream = mz_stream {
                next_in: source,
                avail_in: source_len as c_uint,
                next_out: dest,
                avail_out: (*dest_len) as c_uint,
                ..Default::default()
            };

            let mut stream_oxide = StreamOxide::new(&mut stream);
            as_c_return_code(mz_uncompress2_oxide(&mut stream_oxide, dest_len))
        },
    )
}

pub extern "C" fn mz_compressBound(source_len: c_ulong) -> c_ulong {
    mz_deflateBound(ptr::null_mut(), source_len)
}

);

#[cfg(target_bit_width = "64")]
#[inline]
fn buffer_too_large(source_len: c_ulong, dest_len: c_ulong) -> bool {
    (source_len | dest_len) > 0xFFFFFFFF
}

#[cfg(not(target_bit_width = "64"))]
#[inline]
fn buffer_too_large(_source_len: c_ulong, _dest_len: c_ulong) -> bool {
    false
}
<commit_msg>Stub libc calls for wasm32<commit_after>#![feature(allocator_api)]

extern crate crc;
#[cfg(not(all(target_arch = "wasm32", not(target_os = "emscripten"))))]
extern crate libc;
#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
mod libc {
    #![allow(non_camel_case_types)]

    use std::alloc::{Alloc, Global, Layout};
    use std::mem;
    use std::ptr::NonNull;

    pub type c_void = u8;
    pub type c_int = i32;
    pub type c_uint = u32;
    pub type c_ulong = u64;
    pub type c_char = i8;
    pub type size_t = usize;

    pub unsafe fn malloc(a: size_t) -> *mut c_void {
        let size = a + mem::size_of::<size_t>();
        let layout = match Layout::from_size_align(size, mem::align_of::<size_t>()) {
            Ok(n) => n,
            Err(_) => return 0 as *mut c_void,
        };
        let ptr = match Global.alloc(layout) {
            Ok(addr) => addr.as_ptr() as *mut size_t,
            Err(_) => return 0 as *mut c_void,
        };
        *ptr.offset(0) = size;
        ptr.offset(1) as *mut c_void
    }

    pub unsafe fn realloc(ptr: *mut c_void, a: size_t) -> *mut c_void {
        let new_size = a + mem::size_of::<size_t>();
        let ptr = (ptr as *mut size_t).offset(-1);
        let old_size = *ptr.offset(0);
        let layout = Layout::from_size_align_unchecked(old_size, mem::size_of::<size_t>());
        let ptr = match Global.realloc(NonNull::new_unchecked(ptr as *mut _), layout, new_size) {
            Ok(addr) => addr.as_ptr() as *mut size_t,
            Err(_) => return 0 as *mut c_void,
        };
        *ptr.offset(0) = new_size;
        ptr.offset(1) as *mut c_void
    }

    pub unsafe fn free(ptr: *mut c_void) {
        let ptr = (ptr as *mut size_t).offset(-1);
        let size = *ptr.offset(0);
        let align = mem::size_of::<size_t>();
        let layout = Layout::from_size_align_unchecked(size, align);
        Global.dealloc(NonNull::new_unchecked(ptr as *mut _), layout);
    }
}
extern crate miniz_oxide;

use std::{cmp, ptr, slice};
use std::panic::{catch_unwind, AssertUnwindSafe};
use crc::{Hasher32, crc32};

use libc::{c_int, c_uint, c_ulong, c_void, size_t};

use miniz_oxide::{MZError, MZResult};
#[allow(bad_style)]
pub use tdef::tdefl_compressor;

pub use miniz_oxide::mz_adler32_oxide;
use miniz_oxide::deflate::CompressionLevel;
use miniz_oxide::deflate::core::CompressionStrategy;

#[macro_use]
mod unmangle;

pub mod lib_oxide;
use lib_oxide::*;

mod tinfl;
pub use tinfl::{tinfl_decompress, tinfl_decompress_mem_to_heap,
                tinfl_decompress_mem_to_mem, tinfl_decompressor};

mod tdef;
pub use tdef::{tdefl_compress, tdefl_compress_buffer, tdefl_compress_mem_to_heap,
               tdefl_compress_mem_to_mem, tdefl_compress_mem_to_output,
               tdefl_create_comp_flags_from_zip_params, tdefl_get_prev_return_status, tdefl_init,
               tdefl_allocate, tdefl_deallocate, tdefl_get_adler32};

pub use tdef::flush_modes::*;
pub use tdef::strategy::*;

pub fn mz_crc32_oxide(crc32: c_uint, data: &[u8]) -> c_uint {
    let mut digest = crc32::Digest::new_with_initial(crc32::IEEE, crc32);
    digest.write(data);
    digest.sum32()
}

pub const MZ_DEFLATED: c_int = 8;
pub const MZ_DEFAULT_WINDOW_BITS: c_int = 15;

pub const MZ_ADLER32_INIT: c_ulong = 1;
pub const MZ_CRC32_INIT: c_ulong = 0;

fn as_c_return_code(r: MZResult) -> c_int {
    match r {
        Err(status) => status as c_int,
        Ok(status) => status as c_int,
    }
}

unmangle!(
pub unsafe extern "C" fn miniz_def_alloc_func(
    _opaque: *mut c_void,
    items: size_t,
    size: size_t,
) -> *mut c_void {
    libc::malloc(items * size)
}

pub unsafe extern "C" fn miniz_def_free_func(_opaque: *mut c_void, address: *mut c_void) {
    libc::free(address)
}

pub unsafe extern "C" fn miniz_def_realloc_func(
    _opaque: *mut c_void,
    address: *mut c_void,
    items: size_t,
    size: size_t,
) -> *mut c_void {
    libc::realloc(address, items * size)
}

pub unsafe extern "C" fn mz_adler32(adler: c_ulong, ptr: *const u8, buf_len: usize) -> c_ulong {
    ptr.as_ref().map_or(MZ_ADLER32_INIT, |r| {
        let data = slice::from_raw_parts(r, buf_len);
        mz_adler32_oxide(adler as c_uint, data) as c_ulong
    })
}

pub unsafe extern "C" fn mz_crc32(crc: c_ulong, ptr: *const u8, buf_len: size_t) -> c_ulong {
    ptr.as_ref().map_or(MZ_CRC32_INIT, |r| {
        let data = slice::from_raw_parts(r, buf_len);
        mz_crc32_oxide(crc as c_uint, data) as c_ulong
    })
}
);

macro_rules! oxidize {
    ($mz_func:ident, $mz_func_oxide:ident; $($arg_name:ident: $type_name:ident),*) => {
        unmangle!(
        pub unsafe extern "C" fn $mz_func(stream: *mut mz_stream, $($arg_name: $type_name),*)
                                          -> c_int {
            match stream.as_mut() {
                None => MZError::Stream as c_int,
                Some(stream) => {
                    // Make sure we catch a potential panic, as
                    // this is called from C.
                    match catch_unwind(AssertUnwindSafe(|| {
                        let mut stream_oxide = StreamOxide::new(&mut *stream);
                        let status = $mz_func_oxide(&mut stream_oxide, $($arg_name),*);
                        *stream = stream_oxide.into_mz_stream();
                        as_c_return_code(status)
                    })) {
                        Ok(res) => res,
                        Err(_) => {
                            println!("FATAL ERROR: Caught panic!");
                            MZError::Stream as c_int},
                    }
                }
            }
        });
    };
}

oxidize!(mz_deflateInit2, mz_deflate_init2_oxide;
         level: c_int, method: c_int, window_bits: c_int, mem_level: c_int, strategy: c_int);
oxidize!(mz_deflate, mz_deflate_oxide;
         flush: c_int);
oxidize!(mz_deflateEnd, mz_deflate_end_oxide;);
oxidize!(mz_deflateReset, mz_deflate_reset_oxide;);

oxidize!(mz_inflateInit2, mz_inflate_init2_oxide;
         window_bits: c_int);
oxidize!(mz_inflate, mz_inflate_oxide;
         flush: c_int);
oxidize!(mz_inflateEnd, mz_inflate_end_oxide;);

unmangle!(
pub unsafe extern "C" fn mz_deflateInit(stream: *mut mz_stream, level: c_int) -> c_int {
    mz_deflateInit2(
        stream,
        level,
        MZ_DEFLATED,
        MZ_DEFAULT_WINDOW_BITS,
        9,
        CompressionStrategy::Default as c_int,
    )
}

pub unsafe extern "C" fn mz_compress(
    dest: *mut u8,
    dest_len: *mut c_ulong,
    source: *const u8,
    source_len: c_ulong,
) -> c_int {
    mz_compress2(
        dest,
        dest_len,
        source,
        source_len,
        CompressionLevel::DefaultCompression as c_int,
    )
}

pub unsafe extern "C" fn mz_compress2(
    dest: *mut u8,
    dest_len: *mut c_ulong,
    source: *const u8,
    source_len: c_ulong,
    level: c_int,
) -> c_int {
    dest_len.as_mut().map_or(
        MZError::Param as c_int,
        |dest_len| {
            if buffer_too_large(source_len, *dest_len) {
                return MZError::Param as c_int;
            }

            let mut stream: mz_stream = mz_stream {
                next_in: source,
                avail_in: source_len as c_uint,
                next_out: dest,
                avail_out: (*dest_len) as c_uint,
                ..Default::default()
            };

            let mut stream_oxide = StreamOxide::new(&mut stream);
            as_c_return_code(mz_compress2_oxide(&mut stream_oxide, level, dest_len))
        },
    )
}

pub extern "C" fn mz_deflateBound(_stream: *mut mz_stream, source_len: c_ulong) -> c_ulong {
    cmp::max(
        128 + (source_len * 110) / 100,
        128 + source_len + ((source_len / (31 * 1024)) + 1) * 5,
    )
}

pub unsafe extern "C" fn mz_inflateInit(stream: *mut mz_stream) -> c_int {
    mz_inflateInit2(stream, MZ_DEFAULT_WINDOW_BITS)
}

pub unsafe extern "C" fn mz_uncompress(
    dest: *mut u8,
    dest_len: *mut c_ulong,
    source: *const u8,
    source_len: c_ulong,
) -> c_int {
    dest_len.as_mut().map_or(
        MZError::Param as c_int,
        |dest_len| {
            if buffer_too_large(source_len, *dest_len) {
                return MZError::Param as c_int;
            }

            let mut stream: mz_stream = mz_stream {
                next_in: source,
                avail_in: source_len as c_uint,
                next_out: dest,
                avail_out: (*dest_len) as c_uint,
                ..Default::default()
            };

            let mut stream_oxide = StreamOxide::new(&mut stream);
            as_c_return_code(mz_uncompress2_oxide(&mut stream_oxide, dest_len))
        },
    )
}

pub extern "C" fn mz_compressBound(source_len: c_ulong) -> c_ulong {
    mz_deflateBound(ptr::null_mut(), source_len)
}

);

#[cfg(target_bit_width = "64")]
#[inline]
fn buffer_too_large(source_len: c_ulong, dest_len: c_ulong) -> bool {
    (source_len | dest_len) > 0xFFFFFFFF
}

#[cfg(not(target_bit_width = "64"))]
#[inline]
fn buffer_too_large(_source_len: c_ulong, _dest_len: c_ulong) -> bool {
    false
}
<|endoftext|>


<commit_before>use std::io;
// use std::str::FromStr;
use std::num;

pub struct Coordinate {
    pub x: f32,
    pub y: f32,
    pub z: f32
}

impl Coordinate {
    fn new(x: f32, y: f32, z: f32) -> Coordinate {
        Coordinate{ x: x, y: y, z: z }
    }
}

pub type XYZLine = (String, Coordinate);
pub type Snapshot = (String, Vec<XYZLine>);

#[derive(Debug)]
pub enum XYZError {
    ParseError(num::ParseFloatError),
}

impl From<num::ParseFloatError> for XYZError {
    fn from(err: num::ParseFloatError) -> XYZError {
        XYZError::ParseError(err)
    }
}

fn parse_coord(x_str: &str, y_str: &str, z_str: &str) -> Result<Coordinate, XYZError> {
    let x = try!(x_str.parse::<f32>());
    let y = try!(y_str.parse::<f32>());
    let z = try!(z_str.parse::<f32>());

    Ok(Coordinate::new(x,y,z))
}

pub fn read_xyz_line<R: io::BufRead>(reader: &mut R) -> Option<XYZLine> {
    let mut buffer = String::new();
    try!(reader.read_line(&mut buffer));
    let splited: Vec<&str> = buffer.split_whitespace().collect();
    if splited.len() == 4 {
        Some(
            (String::from(splited[0]),
            parse_coord(splited[1], splited[2], splited[3]))
            )
    } else {
        None
    }
}

// fn collect_xyz<R: io::BufRead>(reader: &mut R, num: i32) -> Vec<XYZLine> {
//     let mut vec = Vec::new();
//     for _ in 0..num {
//         match read_xyz_line(reader) {
//             Some(x) => vec.push(x),
//             _ => ()
//         };
//     }
//     vec
// }

// pub fn read_snapshot<R: io::BufRead>(mut reader: &mut R) -> Snapshot {
//     let mut buffer = String::new();
//     try!(reader.read_line(&mut buffer));
//     let num = i32::from_str(&buffer).unwrap(); // TODO try
//     reader.read_line(&mut buffer); // TODO try
//     (buffer.clone(), collect_xyz(reader, num))
// }

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
    }
}
<commit_msg>Add XYZError struct<commit_after>use std::io;
use std::num;
use std::str::FromStr;

#[derive(Debug)]
pub enum XYZError {
    IO(io::Error),
    IllegalState(String),
    Parse(num::ParseFloatError),
}

impl From<io::Error> for XYZError {
    fn from(err: io::Error) -> XYZError {
        XYZError::IO(err)
    }
}

impl From<num::ParseFloatError> for XYZError {
    fn from(err: num::ParseFloatError) -> XYZError {
        XYZError::Parse(err)
    }
}

pub struct Coordinate {
    pub x: f32,
    pub y: f32,
    pub z: f32
}

impl Coordinate {
    fn new(x: f32, y: f32, z: f32) -> Self {
        Coordinate{ x: x, y: y, z: z }
    }
}

pub struct Atom {
    pub serial: String,
    pub coordinate: Coordinate
}

impl Atom {
    fn new(serial: &str, coordinate: Coordinate) -> Self {
        Atom{ serial: String::from(serial), coordinate: coordinate }
    }
}

impl FromStr for Atom {
    type Err = XYZError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let splitted: Vec<&str> = s.split_whitespace().collect();
        if splitted.len() != 4 {
            return Err(XYZError::IllegalState(String::from("")));
        }
        let coordinate = try!(parse_coord(splitted[1], splitted[2], splitted[3]));
        Ok(Atom::new(splitted[0], coordinate))
    }
}

pub type Snapshot = (String, Vec<Atom>);

fn parse_coord(x_str: &str, y_str: &str, z_str: &str) -> Result<Coordinate, XYZError> {
    let x = try!(x_str.parse::<f32>());
    let y = try!(y_str.parse::<f32>());
    let z = try!(z_str.parse::<f32>());

    Ok(Coordinate::new(x,y,z))
}

pub fn read_xyz_line<R: io::BufRead>(reader: &mut R) -> Result<Atom, XYZError> {
    let mut buffer = String::new();
    try!(reader.read_line(&mut buffer));
    buffer.parse::<Atom>()
}

// fn collect_xyz<R: io::BufRead>(reader: &mut R, num: i32) -> Result<Vec<Atom>, XYZError> {
//     let mut vec = Vec::new();
//     for _ in 0..num {
//         match read_xyz_line(reader) {
//             Some(x) => vec.push(x),
//             _ => ()
//         };
//     }
//     vec
// }

// pub fn read_snapshot<R: io::BufRead>(mut reader: &mut R) -> Snapshot {
//     let mut buffer = String::new();
//     try!(reader.read_line(&mut buffer));
//     let num = i32::from_str(&buffer).unwrap(); // TODO try
//     reader.read_line(&mut buffer); // TODO try
//     (buffer.clone(), collect_xyz(reader, num))
// }

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
    }
}
<|endoftext|>


<commit_before>#![allow(dead_code)]
#![warn(missing_docs)]

//! A wrapper around [cfitsio].
//!
//! This wrapper attempts to add some safety to the [cfitsio] library, and allows `.fits` files to
//! be read from rust.
//!
//! The main entry point to the code is `FitsFile::open` which creates a new file, and
//! `FitsFile::create` which creates a new file.
//!
//! [cfitsio]: https://heasarc.gsfc.nasa.gov/docs/software/fitsio/fitsio.html

pub mod raw;

extern crate libc;

use raw::*;
use libc::{c_int, c_char};
use std::ptr;
use std::ffi;

/// Internal function to get the fits error description from a status code
fn status_to_string(status: c_int) -> Option<String> {
    match status {
        0 => None,
        status => {
            let mut buffer: Vec<c_char> = vec![0; 31];
            unsafe {
                ffgerr(status, buffer.as_mut_ptr());
            }
            let result_str = String::from_utf8(buffer.iter()
                    .map(|&x| x as u8)
                    .filter(|&x| x != 0)
                    .collect())
                .unwrap();
            Some(result_str)
        }
    }
}

/// General type defining what kind of HDU we're talking about
#[derive(Eq, PartialEq, Debug)]
pub enum HduType {
    ImageHDU,
    AsciiTableHDU,
    BinTableHDU,
}

/// Wrapper around a C `fitsfile` pointer.
///
/// This handles [opening][FitsFile::open], [creating][FitsFile::create] and automatically
/// closing (through the `Drop` trait) the file.
///
/// All subsequent file access is controled through this object.
///
/// [FitsFile::open]: #method.open
/// [FitsFile::create]: #method.create
pub struct FitsFile {
    fptr: *mut fitsfile,
    status: c_int,
    pub filename: String,
}

impl FitsFile {
    /// Open a fits file for reading
    ///
    /// * `filename` - Filename to pass to `cfitsio`. Can conform to the
    /// [Extended Filename Syntax][extended-filename-syntax].
    ///
    /// [extended-filename-syntax]:
    ///     https://heasarc.gsfc.nasa.gov/docs/software/fitsio/c/c_user/node82.html
    ///
    /// Examples
    ///
    /// ```
    /// # use fitsio::FitsFile;
    /// # fn main() {
    ///     let f = FitsFile::open("testdata/full_example.fits");
    /// # }
    pub fn open(filename: &str) -> FitsFile {
        let mut fptr = ptr::null_mut();
        let mut status = 0;
        let c_filename = ffi::CString::new(filename).unwrap();

        unsafe {
            ffopen(&mut fptr as *mut *mut fitsfile,
                   c_filename.as_ptr(),
                   0,
                   &mut status);
        }

        return match status {
            0 => {
                FitsFile {
                    fptr: fptr,
                    status: status,
                    filename: filename.to_string(),
                }
            }
            status => {
                panic!("Invalid status code: {}, msg: {}",
                       status,
                       status_to_string(status).unwrap())
            }
        };

    }

    /// Function to check that the status code is ok.
    ///
    /// If the value of `self.status` is not 0 then exit the current process as an error has
    /// occurred.
    fn check(&self) {
        match self.status {
            0 => {}
            status => {
                panic!("Status code {} encountered, msg: {}",
                       status,
                       status_to_string(status).unwrap())
            }
        }
    }

    /// Returns the current HDU number, 0-indexed
    ///
    /// The FITS standard is 1-indexed (thanks Fortran), so this function
    /// converts the indexing to be 0-based so the primary HDU is at
    /// index 0.
    ///
    /// # Examples
    ///
    /// ```
    /// # use fitsio::FitsFile;
    /// let f = FitsFile::open("testdata/full_example.fits");
    /// assert_eq!(f.current_hdu_number(), 0);
    /// ```
    pub fn current_hdu_number(&self) -> u32 {
        let mut hdu_num = 0;
        unsafe {
            ffghdn(self.fptr, &mut hdu_num);
        }
        self.check();
        assert!(hdu_num >= 1);
        (hdu_num - 1) as u32
    }

    /// Change the current HDU
    ///
    /// Examples
    ///
    /// ```
    /// # use fitsio::FitsFile;
    /// # fn main() {
    /// # let mut f = FitsFile::open("testdata/full_example.fits");
    /// assert_eq!(f.current_hdu_number(), 0);
    /// f.change_hdu(1);
    /// assert_eq!(f.current_hdu_number(), 1);
    /// # }
    /// ```
    pub fn change_hdu(&mut self, hdu_num: u32) {
        let mut _hdu_type = 0;
        unsafe {
            ffmahd(self.fptr,
                   (hdu_num + 1) as i32,
                   &mut _hdu_type,
                   &mut self.status);
        }
        self.check();
    }

    /// Get which type of HDU the current HDU is
    ///
    /// Results in one of the `HduType` options.
    ///
    /// # Examples
    ///
    /// ```
    /// # use fitsio::{FitsFile, HduType};
    /// # fn main() {
    /// let mut f = FitsFile::open("testdata/full_example.fits");
    /// // Primary HDUs are always image hdus
    /// assert_eq!(f.get_hdu_type(), HduType::ImageHDU);
    /// # }
    /// ```
    pub fn get_hdu_type(&mut self) -> HduType {
        let mut hdu_type = 3;
        unsafe {
            ffghdt(self.fptr, &mut hdu_type, &mut self.status);
        }
        self.check();

        match hdu_type {
            0 => HduType::ImageHDU,
            1 => HduType::AsciiTableHDU,
            2 => HduType::BinTableHDU,
            _ => panic!("Unknown hdu type: {}", hdu_type),
        }
    }

    /// Return a `FitsHDU` object for the specified HDU
    ///
    /// # Examples
    ///
    /// ```
    /// # use fitsio::{FitsFile, HduType};
    /// # fn main() {
    /// # let mut f = FitsFile::open("testdata/full_example.fits");
    /// let primary_hdu = f.get_hdu(0);
    /// assert_eq!(primary_hdu.hdu_type, HduType::ImageHDU);
    /// # }
    /// ```
    pub fn get_hdu(&mut self, index: usize) -> FitsHDU {
        self.change_hdu(index as u32);
        let hdu_type = self.get_hdu_type();
        FitsHDU {
            fitsfile: self,
            hdu_type: hdu_type,
        }
    }
}

impl Drop for FitsFile {
    fn drop(&mut self) {
        unsafe {
            ffclos(self.fptr, &mut self.status);
        }
    }
}

/// Wrapper around a FITS HDU
///
/// This struct is the main interface around reading and writing the file contents.
pub struct FitsHDU<'a> {
    fitsfile: &'a FitsFile,
    pub hdu_type: HduType,
}

impl<'a> FitsHDU<'a> {
    /// Read a header key as a string
    ///
    /// The user is responsible for converting the value type from a string to whatever type the
    /// header key contains.
    ///
    /// # Examples
    ///
    /// ```
    /// # use fitsio::FitsFile;
    /// # fn main() {
    /// let mut f = FitsFile::open("testdata/full_example.fits");
    /// let mut primary_hdu = f.get_hdu(0);
    /// // Image is 2-dimensional
    /// let naxis = primary_hdu.get_key("NAXIS").parse::<i32>().unwrap();
    /// assert_eq!(naxis, 2);
    /// # }
    /// ```
    pub fn get_key(&mut self, key: &str) -> String {
        let fptr = &self.fitsfile.fptr;
        let mut value: Vec<c_char> = vec![0; MAX_VALUE_LENGTH];
        let keyname = ffi::CString::new(key).unwrap();
        let mut status = 0;

        unsafe {
            ffgkys(*fptr,
                   keyname.as_ptr(),
                   value.as_mut_ptr(),
                   ptr::null_mut(),
                   &mut status);
        }

        match status {
            0 => {
                let value: Vec<u8> = value.iter()
                    .map(|&x| x as u8)
                    .filter(|&x| x != 0)
                    .collect();
                return String::from_utf8(value).unwrap();
            }
            _ => {
                panic!("Invalid status code: {}, msg: {}",
                       status,
                       status_to_string(status).unwrap())
            }
        }
    }
}

mod test {
    #[test]
    fn returning_error_messages() {
        use super::status_to_string;

        assert_eq!(status_to_string(105).unwrap(),
                   "couldn't create the named file");
    }

    #[test]
    fn opening_an_existing_file() {
        use super::FitsFile;

        let f = FitsFile::open("testdata/full_example.fits");
        assert_eq!(f.status, 0);
    }

    #[test]
    fn filename_is_stored() {
        use super::FitsFile;

        let f = FitsFile::open("testdata/full_example.fits");
        assert_eq!(f.filename, "testdata/full_example.fits");
    }

    #[test]
    fn change_hdu() {
        use super::FitsFile;

        let mut f = FitsFile::open("testdata/full_example.fits");
        f.change_hdu(1);
        assert_eq!(f.current_hdu_number(), 1u32);
    }

    #[test]
    fn getting_current_hdu_number() {
        use super::FitsFile;

        let f = FitsFile::open("testdata/full_example.fits");
        assert_eq!(f.current_hdu_number(), 0u32);
    }

    #[test]
    fn getting_hdu_object() {
        use super::{FitsFile, HduType};

        let mut f = FitsFile::open("testdata/full_example.fits");

        {
            let primary_hdu = f.get_hdu(0);
            assert_eq!(primary_hdu.hdu_type, HduType::ImageHDU);
        }

        {
            let table_hdu = f.get_hdu(1);
            assert_eq!(table_hdu.hdu_type, HduType::BinTableHDU);
        }
    }
}
<commit_msg>Stop warning about missing docs<commit_after>#![allow(dead_code)]

//! A wrapper around [cfitsio].
//!
//! This wrapper attempts to add some safety to the [cfitsio] library, and allows `.fits` files to
//! be read from rust.
//!
//! The main entry point to the code is `FitsFile::open` which creates a new file, and
//! `FitsFile::create` which creates a new file.
//!
//! [cfitsio]: https://heasarc.gsfc.nasa.gov/docs/software/fitsio/fitsio.html

pub mod raw;

extern crate libc;

use raw::*;
use libc::{c_int, c_char};
use std::ptr;
use std::ffi;

/// Internal function to get the fits error description from a status code
fn status_to_string(status: c_int) -> Option<String> {
    match status {
        0 => None,
        status => {
            let mut buffer: Vec<c_char> = vec![0; 31];
            unsafe {
                ffgerr(status, buffer.as_mut_ptr());
            }
            let result_str = String::from_utf8(buffer.iter()
                    .map(|&x| x as u8)
                    .filter(|&x| x != 0)
                    .collect())
                .unwrap();
            Some(result_str)
        }
    }
}

/// General type defining what kind of HDU we're talking about
#[derive(Eq, PartialEq, Debug)]
pub enum HduType {
    ImageHDU,
    AsciiTableHDU,
    BinTableHDU,
}

/// Wrapper around a C `fitsfile` pointer.
///
/// This handles [opening][FitsFile::open], [creating][FitsFile::create] and automatically
/// closing (through the `Drop` trait) the file.
///
/// All subsequent file access is controled through this object.
///
/// [FitsFile::open]: #method.open
/// [FitsFile::create]: #method.create
pub struct FitsFile {
    fptr: *mut fitsfile,
    status: c_int,
    pub filename: String,
}

impl FitsFile {
    /// Open a fits file for reading
    ///
    /// * `filename` - Filename to pass to `cfitsio`. Can conform to the
    /// [Extended Filename Syntax][extended-filename-syntax].
    ///
    /// [extended-filename-syntax]:
    ///     https://heasarc.gsfc.nasa.gov/docs/software/fitsio/c/c_user/node82.html
    ///
    /// Examples
    ///
    /// ```
    /// # use fitsio::FitsFile;
    /// # fn main() {
    ///     let f = FitsFile::open("testdata/full_example.fits");
    /// # }
    pub fn open(filename: &str) -> FitsFile {
        let mut fptr = ptr::null_mut();
        let mut status = 0;
        let c_filename = ffi::CString::new(filename).unwrap();

        unsafe {
            ffopen(&mut fptr as *mut *mut fitsfile,
                   c_filename.as_ptr(),
                   0,
                   &mut status);
        }

        return match status {
            0 => {
                FitsFile {
                    fptr: fptr,
                    status: status,
                    filename: filename.to_string(),
                }
            }
            status => {
                panic!("Invalid status code: {}, msg: {}",
                       status,
                       status_to_string(status).unwrap())
            }
        };

    }

    /// Function to check that the status code is ok.
    ///
    /// If the value of `self.status` is not 0 then exit the current process as an error has
    /// occurred.
    fn check(&self) {
        match self.status {
            0 => {}
            status => {
                panic!("Status code {} encountered, msg: {}",
                       status,
                       status_to_string(status).unwrap())
            }
        }
    }

    /// Returns the current HDU number, 0-indexed
    ///
    /// The FITS standard is 1-indexed (thanks Fortran), so this function
    /// converts the indexing to be 0-based so the primary HDU is at
    /// index 0.
    ///
    /// # Examples
    ///
    /// ```
    /// # use fitsio::FitsFile;
    /// let f = FitsFile::open("testdata/full_example.fits");
    /// assert_eq!(f.current_hdu_number(), 0);
    /// ```
    pub fn current_hdu_number(&self) -> u32 {
        let mut hdu_num = 0;
        unsafe {
            ffghdn(self.fptr, &mut hdu_num);
        }
        self.check();
        assert!(hdu_num >= 1);
        (hdu_num - 1) as u32
    }

    /// Change the current HDU
    ///
    /// Examples
    ///
    /// ```
    /// # use fitsio::FitsFile;
    /// # fn main() {
    /// # let mut f = FitsFile::open("testdata/full_example.fits");
    /// assert_eq!(f.current_hdu_number(), 0);
    /// f.change_hdu(1);
    /// assert_eq!(f.current_hdu_number(), 1);
    /// # }
    /// ```
    pub fn change_hdu(&mut self, hdu_num: u32) {
        let mut _hdu_type = 0;
        unsafe {
            ffmahd(self.fptr,
                   (hdu_num + 1) as i32,
                   &mut _hdu_type,
                   &mut self.status);
        }
        self.check();
    }

    /// Get which type of HDU the current HDU is
    ///
    /// Results in one of the `HduType` options.
    ///
    /// # Examples
    ///
    /// ```
    /// # use fitsio::{FitsFile, HduType};
    /// # fn main() {
    /// let mut f = FitsFile::open("testdata/full_example.fits");
    /// // Primary HDUs are always image hdus
    /// assert_eq!(f.get_hdu_type(), HduType::ImageHDU);
    /// # }
    /// ```
    pub fn get_hdu_type(&mut self) -> HduType {
        let mut hdu_type = 3;
        unsafe {
            ffghdt(self.fptr, &mut hdu_type, &mut self.status);
        }
        self.check();

        match hdu_type {
            0 => HduType::ImageHDU,
            1 => HduType::AsciiTableHDU,
            2 => HduType::BinTableHDU,
            _ => panic!("Unknown hdu type: {}", hdu_type),
        }
    }

    /// Return a `FitsHDU` object for the specified HDU
    ///
    /// # Examples
    ///
    /// ```
    /// # use fitsio::{FitsFile, HduType};
    /// # fn main() {
    /// # let mut f = FitsFile::open("testdata/full_example.fits");
    /// let primary_hdu = f.get_hdu(0);
    /// assert_eq!(primary_hdu.hdu_type, HduType::ImageHDU);
    /// # }
    /// ```
    pub fn get_hdu(&mut self, index: usize) -> FitsHDU {
        self.change_hdu(index as u32);
        let hdu_type = self.get_hdu_type();
        FitsHDU {
            fitsfile: self,
            hdu_type: hdu_type,
        }
    }
}

impl Drop for FitsFile {
    fn drop(&mut self) {
        unsafe {
            ffclos(self.fptr, &mut self.status);
        }
    }
}

/// Wrapper around a FITS HDU
///
/// This struct is the main interface around reading and writing the file contents.
pub struct FitsHDU<'a> {
    fitsfile: &'a FitsFile,
    pub hdu_type: HduType,
}

impl<'a> FitsHDU<'a> {
    /// Read a header key as a string
    ///
    /// The user is responsible for converting the value type from a string to whatever type the
    /// header key contains.
    ///
    /// # Examples
    ///
    /// ```
    /// # use fitsio::FitsFile;
    /// # fn main() {
    /// let mut f = FitsFile::open("testdata/full_example.fits");
    /// let mut primary_hdu = f.get_hdu(0);
    /// // Image is 2-dimensional
    /// let naxis = primary_hdu.get_key("NAXIS").parse::<i32>().unwrap();
    /// assert_eq!(naxis, 2);
    /// # }
    /// ```
    pub fn get_key(&mut self, key: &str) -> String {
        let fptr = &self.fitsfile.fptr;
        let mut value: Vec<c_char> = vec![0; MAX_VALUE_LENGTH];
        let keyname = ffi::CString::new(key).unwrap();
        let mut status = 0;

        unsafe {
            ffgkys(*fptr,
                   keyname.as_ptr(),
                   value.as_mut_ptr(),
                   ptr::null_mut(),
                   &mut status);
        }

        match status {
            0 => {
                let value: Vec<u8> = value.iter()
                    .map(|&x| x as u8)
                    .filter(|&x| x != 0)
                    .collect();
                return String::from_utf8(value).unwrap();
            }
            _ => {
                panic!("Invalid status code: {}, msg: {}",
                       status,
                       status_to_string(status).unwrap())
            }
        }
    }
}

mod test {
    #[test]
    fn returning_error_messages() {
        use super::status_to_string;

        assert_eq!(status_to_string(105).unwrap(),
                   "couldn't create the named file");
    }

    #[test]
    fn opening_an_existing_file() {
        use super::FitsFile;

        let f = FitsFile::open("testdata/full_example.fits");
        assert_eq!(f.status, 0);
    }

    #[test]
    fn filename_is_stored() {
        use super::FitsFile;

        let f = FitsFile::open("testdata/full_example.fits");
        assert_eq!(f.filename, "testdata/full_example.fits");
    }

    #[test]
    fn change_hdu() {
        use super::FitsFile;

        let mut f = FitsFile::open("testdata/full_example.fits");
        f.change_hdu(1);
        assert_eq!(f.current_hdu_number(), 1u32);
    }

    #[test]
    fn getting_current_hdu_number() {
        use super::FitsFile;

        let f = FitsFile::open("testdata/full_example.fits");
        assert_eq!(f.current_hdu_number(), 0u32);
    }

    #[test]
    fn getting_hdu_object() {
        use super::{FitsFile, HduType};

        let mut f = FitsFile::open("testdata/full_example.fits");

        {
            let primary_hdu = f.get_hdu(0);
            assert_eq!(primary_hdu.hdu_type, HduType::ImageHDU);
        }

        {
            let table_hdu = f.get_hdu(1);
            assert_eq!(table_hdu.hdu_type, HduType::BinTableHDU);
        }
    }
}
<|endoftext|>


<commit_before>//! Parser for bitmap fonts

mod char;
mod config_parse_error;
mod error;
mod kerning_value;
mod page;
mod rect;
mod sections;
mod string_parse_error;
mod utils;

pub use self::config_parse_error::ConfigParseError;
pub use self::error::Error;
pub use self::rect::Rect;
pub use self::string_parse_error::StringParseError;

use self::char::Char;
use self::kerning_value::KerningValue;
use self::page::Page;
use self::sections::Sections;
use std::io::Read;

#[derive(Clone, Debug)]
pub struct CharPosition {
    pub page_rect: Rect,
    pub screen_rect: Rect,
    pub page_index: u32,
}

#[derive(Clone, Debug)]
pub enum OrdinateOrientation {
    BottomToTop,
    TopToBottom,
}

#[derive(Clone, Debug)]
pub struct BMFont {
    base_height: u32,
    line_height: u32,
    characters: Vec<Char>,
    kerning_values: Vec<KerningValue>,
    pages: Vec<Page>,
    ordinate_orientation: OrdinateOrientation,
}

impl BMFont {
    pub fn new<R>(source: R, ordinate_orientation: OrdinateOrientation) -> Result<BMFont, Error>
    where
        R: Read,
    {
        let sections = Sections::new(source)?;

        let base_height;
        let line_height;
        {
            let mut components = sections.common_section.split_whitespace();
            components.next();
            line_height =
                utils::extract_component_value(components.next(), "common", "lineHeight")?;
            base_height = utils::extract_component_value(components.next(), "common", "base")?;
        }

        let mut pages = Vec::new();
        for page_section in &sections.page_sections {
            pages.push(Page::new(page_section)?);
        }
        let mut characters = Vec::new();
        for char_section in &sections.char_sections {
            characters.push(Char::new(char_section)?);
        }
        let mut kerning_values = Vec::new();
        for kerning_section in &sections.kerning_sections {
            kerning_values.push(KerningValue::new(kerning_section)?);
        }
        Ok(BMFont {
            base_height,
            line_height,
            characters,
            kerning_values,
            pages,
            ordinate_orientation,
        })
    }

    /// Returns the height of a `EM` in pixels.
    pub fn base_height(&self) -> u32 {
        self.base_height
    }

    pub fn line_height(&self) -> u32 {
        self.line_height
    }

    /// Returns an `Iterator` of font page bitmap filenames.
    ///
    /// ## Examples
    ///
    /// ```rust
    /// # use bmfont::*;
    /// # fn main() -> Result<(), Error> {
    /// let file = std::fs::File::open("font.fnt")?;
    /// let font = BMFont::new(file, OrdinateOrientation::TopToBottom)?;
    /// assert_eq!(font.pages().next(), Some("font.png"));
    /// #     Ok(())
    /// # }
    /// ```
    pub fn pages(&self) -> impl Iterator<Item = &str> {
        self.pages.iter().map(|p| p.file.as_str())
    }

    pub fn parse(&self, s: &str) -> Result<Vec<CharPosition>, StringParseError> {
        let lines = self.parse_lines(s)?;
        let mut char_positions = Vec::new();
        let mut y: i32 = 0;
        for line in lines {
            let mut x: i32 = 0;
            let mut kerning_values: Vec<&KerningValue> = Vec::new();
            for character in line {
                let kerning_value = kerning_values
                    .into_iter()
                    .find(|k| k.second_char_id == character.id)
                    .map(|k| k.value)
                    .unwrap_or(0);
                let page_rect = Rect {
                    x: character.x as i32,
                    y: character.y as i32,
                    width: character.width,
                    height: character.height,
                };
                let screen_x = x + character.xoffset + kerning_value;
                let screen_y = match self.ordinate_orientation {
                    OrdinateOrientation::BottomToTop => {
                        y + self.base_height as i32 - character.yoffset - character.height as i32
                    }
                    OrdinateOrientation::TopToBottom => y + character.yoffset,
                };
                let screen_rect = Rect {
                    x: screen_x,
                    y: screen_y,
                    width: character.width,
                    height: character.height,
                };
                let char_position = CharPosition {
                    page_rect,
                    screen_rect,
                    page_index: character.page_index,
                };
                char_positions.push(char_position);
                x += character.xadvance + kerning_value;
                kerning_values = self.find_kerning_values(character.id);
            }
            match self.ordinate_orientation {
                OrdinateOrientation::TopToBottom => y += self.line_height as i32,
                OrdinateOrientation::BottomToTop => y -= self.line_height as i32,
            }
        }
        Ok(char_positions)
    }

    fn find_kerning_values(&self, first_char_id: u32) -> Vec<&KerningValue> {
        self.kerning_values
            .iter()
            .filter(|k| k.first_char_id == first_char_id)
            .collect()
    }

    fn parse_lines(&self, s: &str) -> Result<Vec<Vec<&Char>>, StringParseError> {
        let mut lines = Vec::new();
        let mut line = Vec::new();
        let mut missing_characters = Vec::new();
        let mut unsupported_characters = Vec::new();
        for c in s.chars() {
            if c == '\n' {
                lines.push(line);
                line = Vec::new();
                continue;
            }
            if c.len_utf16() != 1 {
                unsupported_characters.push(c);
                continue;
            }
            let tmp_str = {
                let mut t = String::new();
                t.push(c);
                t
            };
            let char_id = tmp_str.encode_utf16().next().unwrap() as u32;
            if let Some(c) = self.characters.iter().find(|c| c.id == char_id) {
                line.push(c);
            } else {
                missing_characters.push(c);
            }
        }
        if !line.is_empty() {
            lines.push(line);
        }
        if missing_characters.is_empty() && unsupported_characters.is_empty() {
            Ok(lines)
        } else {
            Err(StringParseError {
                missing_characters,
                unsupported_characters,
            })
        }
    }
}
<commit_msg>Add some docs<commit_after>//! Parser for bitmap fonts

mod char;
mod config_parse_error;
mod error;
mod kerning_value;
mod page;
mod rect;
mod sections;
mod string_parse_error;
mod utils;

pub use self::config_parse_error::ConfigParseError;
pub use self::error::Error;
pub use self::rect::Rect;
pub use self::string_parse_error::StringParseError;

use self::char::Char;
use self::kerning_value::KerningValue;
use self::page::Page;
use self::sections::Sections;
use std::io::Read;

#[derive(Clone, Debug)]
pub struct CharPosition {
    pub page_rect: Rect,
    pub screen_rect: Rect,
    pub page_index: u32,
}

#[derive(Clone, Debug)]
pub enum OrdinateOrientation {
    BottomToTop,
    TopToBottom,
}

/// Holds a decoded bitmap font defintion, including all character advance and kerning values.
#[derive(Clone, Debug)]
pub struct BMFont {
    base_height: u32,
    line_height: u32,
    characters: Vec<Char>,
    kerning_values: Vec<KerningValue>,
    pages: Vec<Page>,
    ordinate_orientation: OrdinateOrientation,
}

impl BMFont {
    /// Constructs a new [BMFont].
    ///
    /// ## Examples
    ///
    /// From a file:
    ///
    /// ```rust
    /// # use bmfont::*;
    /// # fn main() -> Result<(), Error> {
    /// let file = std::fs::File::open("font.fnt")?;
    /// let font = BMFont::new(file, OrdinateOrientation::TopToBottom)?;
    /// assert_eq!(font.line_height(), 80);
    /// #     Ok(())
    /// # }
    /// ```
    ///
    /// From a slice of bytes:
    ///
    /// ```rust
    /// # use bmfont::*;
    /// # fn main() -> Result<(), Error> {
    /// # let my_font_bytes = std::fs::read("font.fnt")?;
    /// let data = std::io::Cursor::new(my_font_bytes);
    /// let font = BMFont::new(data, OrdinateOrientation::TopToBottom)?;
    /// assert_eq!(font.line_height(), 80);
    /// #     Ok(())
    /// # }
    /// ```
    pub fn new<R>(source: R, ordinate_orientation: OrdinateOrientation) -> Result<BMFont, Error>
    where
        R: Read,
    {
        let sections = Sections::new(source)?;

        let base_height;
        let line_height;
        {
            let mut components = sections.common_section.split_whitespace();
            components.next();
            line_height =
                utils::extract_component_value(components.next(), "common", "lineHeight")?;
            base_height = utils::extract_component_value(components.next(), "common", "base")?;
        }

        let mut pages = Vec::new();
        for page_section in &sections.page_sections {
            pages.push(Page::new(page_section)?);
        }
        let mut characters = Vec::new();
        for char_section in &sections.char_sections {
            characters.push(Char::new(char_section)?);
        }
        let mut kerning_values = Vec::new();
        for kerning_section in &sections.kerning_sections {
            kerning_values.push(KerningValue::new(kerning_section)?);
        }
        Ok(BMFont {
            base_height,
            line_height,
            characters,
            kerning_values,
            pages,
            ordinate_orientation,
        })
    }

    /// Returns the height of a `EM` in pixels.
    pub fn base_height(&self) -> u32 {
        self.base_height
    }

    pub fn line_height(&self) -> u32 {
        self.line_height
    }

    /// Returns an `Iterator` of font page bitmap filenames.
    ///
    /// ## Examples
    ///
    /// ```rust
    /// # use bmfont::*;
    /// # fn main() -> Result<(), Error> {
    /// let file = std::fs::File::open("font.fnt")?;
    /// let font = BMFont::new(file, OrdinateOrientation::TopToBottom)?;
    /// assert_eq!(font.pages().next(), Some("font.png"));
    /// #     Ok(())
    /// # }
    /// ```
    pub fn pages(&self) -> impl Iterator<Item = &str> {
        self.pages.iter().map(|p| p.file.as_str())
    }

    pub fn parse(&self, s: &str) -> Result<Vec<CharPosition>, StringParseError> {
        let lines = self.parse_lines(s)?;
        let mut char_positions = Vec::new();
        let mut y: i32 = 0;
        for line in lines {
            let mut x: i32 = 0;
            let mut kerning_values: Vec<&KerningValue> = Vec::new();
            for character in line {
                let kerning_value = kerning_values
                    .into_iter()
                    .find(|k| k.second_char_id == character.id)
                    .map(|k| k.value)
                    .unwrap_or(0);
                let page_rect = Rect {
                    x: character.x as i32,
                    y: character.y as i32,
                    width: character.width,
                    height: character.height,
                };
                let screen_x = x + character.xoffset + kerning_value;
                let screen_y = match self.ordinate_orientation {
                    OrdinateOrientation::BottomToTop => {
                        y + self.base_height as i32 - character.yoffset - character.height as i32
                    }
                    OrdinateOrientation::TopToBottom => y + character.yoffset,
                };
                let screen_rect = Rect {
                    x: screen_x,
                    y: screen_y,
                    width: character.width,
                    height: character.height,
                };
                let char_position = CharPosition {
                    page_rect,
                    screen_rect,
                    page_index: character.page_index,
                };
                char_positions.push(char_position);
                x += character.xadvance + kerning_value;
                kerning_values = self.find_kerning_values(character.id);
            }
            match self.ordinate_orientation {
                OrdinateOrientation::TopToBottom => y += self.line_height as i32,
                OrdinateOrientation::BottomToTop => y -= self.line_height as i32,
            }
        }
        Ok(char_positions)
    }

    fn find_kerning_values(&self, first_char_id: u32) -> Vec<&KerningValue> {
        self.kerning_values
            .iter()
            .filter(|k| k.first_char_id == first_char_id)
            .collect()
    }

    fn parse_lines(&self, s: &str) -> Result<Vec<Vec<&Char>>, StringParseError> {
        let mut lines = Vec::new();
        let mut line = Vec::new();
        let mut missing_characters = Vec::new();
        let mut unsupported_characters = Vec::new();
        for c in s.chars() {
            if c == '\n' {
                lines.push(line);
                line = Vec::new();
                continue;
            }
            if c.len_utf16() != 1 {
                unsupported_characters.push(c);
                continue;
            }
            let tmp_str = {
                let mut t = String::new();
                t.push(c);
                t
            };
            let char_id = tmp_str.encode_utf16().next().unwrap() as u32;
            if let Some(c) = self.characters.iter().find(|c| c.id == char_id) {
                line.push(c);
            } else {
                missing_characters.push(c);
            }
        }
        if !line.is_empty() {
            lines.push(line);
        }
        if missing_characters.is_empty() && unsupported_characters.is_empty() {
            Ok(lines)
        } else {
            Err(StringParseError {
                missing_characters,
                unsupported_characters,
            })
        }
    }
}
<|endoftext|>