<commit_before>use approx::relative_eq;
use closed01::Closed01;
use munkres::{solve_assignment, WeightMatrix};
/// A graph similarity score using neighbor matching according to [this paper][1].
///
/// [1]: http://arxiv.org/abs/1009.5290 "2010, Mladen Nikolic, Measuring Similarity
///      of Graph Nodes by Neighbor Matching"
///
/// TODO: Introduce EdgeWeight trait to abstract edge weight similarity.
use nalgebra::DMatrix;
use std::cmp;
use std::mem;
pub use traits::{Edges, Graph, NodeColorMatching, NodeColorWeight};

pub mod graph;
mod traits;

#[derive(Debug, Copy, Clone)]
pub enum ScoreNorm {
    /// Divide by minimum graph or node degree
    MinDegree,

    /// Divide by maximum graph or node degree
    MaxDegree,
}

impl NodeColorWeight for f32 {
    fn node_color_weight(&self) -> f32 {
        *self
    }
}

#[derive(Debug)]
pub struct IgnoreNodeColors;

impl<T> NodeColorMatching<T> for IgnoreNodeColors {
    fn node_color_matching(&self, _node_i_value: &T, _node_j_value: &T) -> Closed01<f32> {
        Closed01::one()
    }
}

#[derive(Debug)]
pub struct WeightedNodeColors;

impl<T: NodeColorWeight> NodeColorMatching<T> for WeightedNodeColors {
    fn node_color_matching(&self, node_i_value: &T, node_j_value: &T) -> Closed01<f32> {
        let dist = (node_i_value.node_color_weight() - node_j_value.node_color_weight())
            .abs()
            .min(1.0);

        debug_assert!(dist >= 0.0 && dist <= 1.0);

        Closed01::new(dist).inv()
    }
}

#[inline(always)]
// NOTE: Our weight matrix minimizes the cost, while our similarity matrix
// wants to maximize the similarity score. That's why we have to convert
// the cost with 1.0 - x.
fn similarity_cost(weight: f32) -> f32 {
    debug_assert!(weight >= 0.0 && weight <= 1.0);
    1.0 - weight
}

#[inline]
/// Calculates the similarity of two nodes `i` and `j`.
///
/// `n_i` contains the neighborhood of i (either in or out neighbors, not both)
/// `n_j` contains the neighborhood of j (either in or out neighbors, not both)
/// `x`   the similarity matrix.
fn s_next<T: Edges>(n_i: &T, n_j: &T, x: &DMatrix<f32>) -> Closed01<f32> {
    let max_deg = cmp::max(n_i.num_edges(), n_j.num_edges());
    let min_deg = cmp::min(n_i.num_edges(), n_j.num_edges());

    debug_assert!(min_deg <= max_deg);

    if max_deg == 0 {
        debug_assert!(n_i.num_edges() == 0);
        debug_assert!(n_j.num_edges() == 0);

        // in the paper, 0/0 is defined as 1.0

        // Two nodes without any edges are perfectly similar.
        return Closed01::one();
    }

    if min_deg == 0 {
        // A node without any edges is not similar at all to a node with edges.
        return Closed01::zero();
    }

    assert!(min_deg > 0 && max_deg > 0);

    // map indicies from 0..min(degree) to the node indices
    let mapidx = |(a, b)| (n_i.nth_edge(a).unwrap(), n_j.nth_edge(b).unwrap());

    let mut w = WeightMatrix::from_fn(min_deg, |ab| similarity_cost(x[mapidx(ab)]));

    let assignment = solve_assignment(&mut w).unwrap();
    assert!(assignment.len() == min_deg);

    let sum: f32 = assignment.iter().fold(0.0, |acc, &ab| acc + x[mapidx(ab)]);

    return Closed01::new(sum / max_deg as f32);
}

#[derive(Debug)]
pub struct SimilarityMatrix<'a, F, G, E, N>
where
    F: NodeColorMatching<N>,
    G: Graph<EDGE = E, NODE = N> + 'a,
    E: Edges,
    N: Clone,
{
    graph_a: &'a G,
    graph_b: &'a G,
    node_color_matching: F,
    // current version of similarity matrix
    current: DMatrix<f32>,
    // previous version of similarity matrix
    previous: DMatrix<f32>,
    // current number of iterations
    num_iterations: usize,
}

impl<'a, F, G, E, N> SimilarityMatrix<'a, F, G, E, N>
where
    F: NodeColorMatching<N>,
    G: Graph<EDGE = E, NODE = N>,
    E: Edges,
    N: Clone,
{
    pub fn new(
        graph_a: &'a G,
        graph_b: &'a G,
        node_color_matching: F,
    ) -> SimilarityMatrix<'a, F, G, E, N> {
        // `x` is the node-similarity matrix.
        // we initialize `x`, so that x[i,j]=1 for all i in A.edges() and j in
        // B.edges().
        let x = DMatrix::<f32>::from_fn(graph_a.num_nodes(), graph_b.num_nodes(), |i, j| {
            if graph_a.node_degree(i) > 0 && graph_b.node_degree(j) > 0 {
                // this is normally set to 1.0 (i.e. without node color matching).
                node_color_matching
                    .node_color_matching(graph_a.node_value(i), graph_b.node_value(j))
            } else {
                Closed01::zero()
            }
            .get()
        });

        let new_x = DMatrix::<f32>::from_element(
            graph_a.num_nodes(),
            graph_b.num_nodes(),
            Closed01::zero().get(),
        );

        SimilarityMatrix {
            graph_a: graph_a,
            graph_b: graph_b,
            node_color_matching: node_color_matching,
            current: x,
            previous: new_x,
            num_iterations: 0,
        }
    }

    fn in_eps(&self, eps: f32) -> bool {
        relative_eq!(self.previous, self.current, epsilon = eps)
    }

    /// Calculates the next iteration of the similarity matrix (x[k+1]).
    pub fn next(&mut self) {
        {
            let x = &self.current;
            let new_x = &mut self.previous;
            let shape = x.shape();

            for i in 0..shape.0 {
                for j in 0..shape.1 {
                    let scale = self.node_color_matching.node_color_matching(
                        self.graph_a.node_value(i),
                        self.graph_b.node_value(j),
                    );
                    let in_score =
                        s_next(self.graph_a.in_edges_of(i), self.graph_b.in_edges_of(j), x);
                    let out_score = s_next(
                        self.graph_a.out_edges_of(i),
                        self.graph_b.out_edges_of(j),
                        x,
                    );
                    new_x[(i, j)] = in_score.average(out_score).mul(scale).get();
                }
            }
        }

        mem::swap(&mut self.previous, &mut self.current);
        self.num_iterations += 1;
    }

    #[inline]
    /// Iteratively calculate the similarity matrix.
    ///
    /// `stop_after_iter`: Stop after iteration (Calculate x(stop_after_iter))
    /// `eps`:   When to stop the iteration
    pub fn iterate(&mut self, stop_after_iter: usize, eps: f32) {
        for _ in 0..stop_after_iter {
            if self.in_eps(eps) {
                break;
            }
            self.next();
        }
    }

    pub fn matrix(&self) -> &DMatrix<f32> {
        &self.current
    }

    pub fn num_iterations(&self) -> usize {
        self.num_iterations
    }

    pub fn min_nodes(&self) -> usize {
        cmp::min(self.current.nrows(), self.current.ncols())
    }

    pub fn max_nodes(&self) -> usize {
        cmp::max(self.current.nrows(), self.current.ncols())
    }

    pub fn optimal_node_assignment(&self) -> Vec<(usize, usize)> {
        let n = self.min_nodes();
        let assignment = if n > 0 {
            let mut w = WeightMatrix::from_fn(n, |ij| similarity_cost(self.current[ij]));
            solve_assignment(&mut w).unwrap()
        } else {
            Vec::new()
        };
        assert!(assignment.len() == n);
        assignment
    }

    fn score_optimal_sum(&self, node_assignment: Option<&[(usize, usize)]>) -> f32 {
        match node_assignment {
            Some(node_assignment) => {
                assert!(node_assignment.len() == self.min_nodes());
                node_assignment
                    .iter()
                    .fold(0.0, |acc, &ab| acc + self.current[ab])
            }
            None => {
                let node_assignment = self.optimal_node_assignment();
                assert!(node_assignment.len() == self.min_nodes());
                node_assignment
                    .iter()
                    .fold(0.0, |acc, &ab| acc + self.current[ab])
            }
        }
    }

    /// Calculate a measure how good the edge weights match up.
    ///
    /// We start by calculating the optimal node assignment between nodes of graph A and graph B,
    /// then compare all outgoing edges of similar-assigned nodes by again using an assignment
    /// between the edge-weight differences of all edge pairs.
    pub fn score_outgoing_edge_weights_sum_norm(
        &self,
        node_assignment: &[(usize, usize)],
        norm: ScoreNorm,
    ) -> Closed01<f32> {
        let n = self.min_nodes();
        let m = self.max_nodes();
        debug_assert!(m >= n);

        assert!(node_assignment.len() == n);

        // we sum up all edge weight scores
        let sum: f32 = node_assignment.iter().fold(0.0, |acc, &(node_i, node_j)| {
            let score_ij = self.score_outgoing_edge_weights_of(node_i, node_j);
            acc + score_ij.get()
        });

        assert!(sum >= 0.0 && sum <= n as f32);

        match norm {
            // Not penalize missing nodes.
            ScoreNorm::MinDegree => Closed01::new(sum / n as f32),

            // To penalize for missing nodes, divide by the maximum number of nodes `m`.
            ScoreNorm::MaxDegree => Closed01::new(sum / m as f32),
        }
    }

    /// Calculate a similarity measure of outgoing edges of nodes `node_i` of graph A and `node_j`
    /// of graph B.  A score of 1.0 means, the edges weights match up perfectly. 0.0 means, no
    /// similarity.
    fn score_outgoing_edge_weights_of(&self, node_i: usize, node_j: usize) -> Closed01<f32> {
        let out_i = self.graph_a.out_edges_of(node_i);
        let out_j = self.graph_b.out_edges_of(node_j);

        let max_deg = cmp::max(out_i.num_edges(), out_j.num_edges());

        if max_deg == 0 {
            // Nodes with no edges are perfectly similar
            return Closed01::one();
        }

        // Calculates the edge weight distance between edges i and j.
        let edge_weight_distance = &|(i, j)| {
            match (out_i.nth_edge_weight(i), out_j.nth_edge_weight(j)) {
                (Some(w_i), Some(w_j)) => w_i.distance(w_j),
                _ => {
                    // Maximum penalty between two weighted edges
                    // NOTE: missing edges could be penalized more, but we already
                    // penalize for that in the node similarity measure.
                    Closed01::one()
                }
            }
            .get()
        };

        let mut w = WeightMatrix::from_fn(max_deg, edge_weight_distance);

        // calculate optimal edge weight assignement.
        let assignment = solve_assignment(&mut w).unwrap();
        assert!(assignment.len() == max_deg);

        // The sum is the sum of all weight differences on the optimal `path`.
        // It's range is from 0.0 (perfect matching) to max_deg*1.0 (bad matching).
        let sum: f32 = assignment
            .iter()
            .fold(0.0, |acc, &ij| acc + edge_weight_distance(ij));

        debug_assert!(sum >= 0.0 && sum <= max_deg as f32);

        // we "invert" the normalized sum so that 1.0 means perfect matching and 0.0
        // no matching.
        Closed01::new(sum / max_deg as f32).inv()
    }

    /// Sums the optimal assignment of the node similarities and normalizes (divides)
    /// by the min/max degree of both graphs.
    /// ScoreNorm::MinDegree is used as default in the paper.
    pub fn score_optimal_sum_norm(
        &self,
        node_assignment: Option<&[(usize, usize)]>,
        norm: ScoreNorm,
    ) -> Closed01<f32> {
        let n = self.min_nodes();
        let m = self.max_nodes();

        if n > 0 {
            assert!(m > 0);
            let sum = self.score_optimal_sum(node_assignment);
            assert!(sum >= 0.0 && sum <= n as f32);

            match norm {
                // Not penalize missing nodes.
                ScoreNorm::MinDegree => Closed01::new(sum / n as f32),

                // To penalize for missing nodes, divide by the maximum number of nodes `m`.
                ScoreNorm::MaxDegree => Closed01::new(sum / m as f32),
            }
        } else {
            Closed01::zero()
        }
    }

    /// Calculates the average over the whole node similarity matrix. This is faster,
    /// as no assignment has to be found. "Graphs with greater number of automorphisms
    /// would be considered to be more self-similar than graphs without automorphisms."
    pub fn score_average(&self) -> Closed01<f32> {
        let n = self.min_nodes();
        if n > 0 {
            let items = self.current.as_slice();
            let sum: f32 = items.iter().fold(0.0, |acc, &v| acc + v);
            let len = items.len();
            assert!(len > 0);
            Closed01::new(sum / len as f32)
        } else {
            Closed01::zero()
        }
    }
}

pub fn similarity_max_degree<T: Graph>(a: &T, b: &T, num_iters: usize, eps: f32) -> Closed01<f32> {
    let mut s = SimilarityMatrix::new(a, b, IgnoreNodeColors);
    s.iterate(num_iters, eps);
    s.score_optimal_sum_norm(None, ScoreNorm::MaxDegree)
}

pub fn similarity_min_degree<T: Graph>(a: &T, b: &T, num_iters: usize, eps: f32) -> Closed01<f32> {
    let mut s = SimilarityMatrix::new(a, b, IgnoreNodeColors);
    s.iterate(num_iters, eps);
    s.score_optimal_sum_norm(None, ScoreNorm::MinDegree)
}
<commit_msg>Use "//!" for top-level crate documentation<commit_after>//! A graph similarity score using neighbor matching according to [this paper][1].
//!
//! [1]: http://arxiv.org/abs/1009.5290 "2010, Mladen Nikolic, Measuring Similarity
//!      of Graph Nodes by Neighbor Matching"
//!
//! TODO: Introduce EdgeWeight trait to abstract edge weight similarity.

use approx::relative_eq;
use closed01::Closed01;
use munkres::{solve_assignment, WeightMatrix};
use nalgebra::DMatrix;
use std::cmp;
use std::mem;
pub use traits::{Edges, Graph, NodeColorMatching, NodeColorWeight};

pub mod graph;
mod traits;

#[derive(Debug, Copy, Clone)]
pub enum ScoreNorm {
    /// Divide by minimum graph or node degree
    MinDegree,

    /// Divide by maximum graph or node degree
    MaxDegree,
}

impl NodeColorWeight for f32 {
    fn node_color_weight(&self) -> f32 {
        *self
    }
}

#[derive(Debug)]
pub struct IgnoreNodeColors;

impl<T> NodeColorMatching<T> for IgnoreNodeColors {
    fn node_color_matching(&self, _node_i_value: &T, _node_j_value: &T) -> Closed01<f32> {
        Closed01::one()
    }
}

#[derive(Debug)]
pub struct WeightedNodeColors;

impl<T: NodeColorWeight> NodeColorMatching<T> for WeightedNodeColors {
    fn node_color_matching(&self, node_i_value: &T, node_j_value: &T) -> Closed01<f32> {
        let dist = (node_i_value.node_color_weight() - node_j_value.node_color_weight())
            .abs()
            .min(1.0);

        debug_assert!(dist >= 0.0 && dist <= 1.0);

        Closed01::new(dist).inv()
    }
}

#[inline(always)]
// NOTE: Our weight matrix minimizes the cost, while our similarity matrix
// wants to maximize the similarity score. That's why we have to convert
// the cost with 1.0 - x.
fn similarity_cost(weight: f32) -> f32 {
    debug_assert!(weight >= 0.0 && weight <= 1.0);
    1.0 - weight
}

#[inline]
/// Calculates the similarity of two nodes `i` and `j`.
///
/// `n_i` contains the neighborhood of i (either in or out neighbors, not both)
/// `n_j` contains the neighborhood of j (either in or out neighbors, not both)
/// `x`   the similarity matrix.
fn s_next<T: Edges>(n_i: &T, n_j: &T, x: &DMatrix<f32>) -> Closed01<f32> {
    let max_deg = cmp::max(n_i.num_edges(), n_j.num_edges());
    let min_deg = cmp::min(n_i.num_edges(), n_j.num_edges());

    debug_assert!(min_deg <= max_deg);

    if max_deg == 0 {
        debug_assert!(n_i.num_edges() == 0);
        debug_assert!(n_j.num_edges() == 0);

        // in the paper, 0/0 is defined as 1.0

        // Two nodes without any edges are perfectly similar.
        return Closed01::one();
    }

    if min_deg == 0 {
        // A node without any edges is not similar at all to a node with edges.
        return Closed01::zero();
    }

    assert!(min_deg > 0 && max_deg > 0);

    // map indicies from 0..min(degree) to the node indices
    let mapidx = |(a, b)| (n_i.nth_edge(a).unwrap(), n_j.nth_edge(b).unwrap());

    let mut w = WeightMatrix::from_fn(min_deg, |ab| similarity_cost(x[mapidx(ab)]));

    let assignment = solve_assignment(&mut w).unwrap();
    assert!(assignment.len() == min_deg);

    let sum: f32 = assignment.iter().fold(0.0, |acc, &ab| acc + x[mapidx(ab)]);

    return Closed01::new(sum / max_deg as f32);
}

#[derive(Debug)]
pub struct SimilarityMatrix<'a, F, G, E, N>
where
    F: NodeColorMatching<N>,
    G: Graph<EDGE = E, NODE = N> + 'a,
    E: Edges,
    N: Clone,
{
    graph_a: &'a G,
    graph_b: &'a G,
    node_color_matching: F,
    // current version of similarity matrix
    current: DMatrix<f32>,
    // previous version of similarity matrix
    previous: DMatrix<f32>,
    // current number of iterations
    num_iterations: usize,
}

impl<'a, F, G, E, N> SimilarityMatrix<'a, F, G, E, N>
where
    F: NodeColorMatching<N>,
    G: Graph<EDGE = E, NODE = N>,
    E: Edges,
    N: Clone,
{
    pub fn new(
        graph_a: &'a G,
        graph_b: &'a G,
        node_color_matching: F,
    ) -> SimilarityMatrix<'a, F, G, E, N> {
        // `x` is the node-similarity matrix.
        // we initialize `x`, so that x[i,j]=1 for all i in A.edges() and j in
        // B.edges().
        let x = DMatrix::<f32>::from_fn(graph_a.num_nodes(), graph_b.num_nodes(), |i, j| {
            if graph_a.node_degree(i) > 0 && graph_b.node_degree(j) > 0 {
                // this is normally set to 1.0 (i.e. without node color matching).
                node_color_matching
                    .node_color_matching(graph_a.node_value(i), graph_b.node_value(j))
            } else {
                Closed01::zero()
            }
            .get()
        });

        let new_x = DMatrix::<f32>::from_element(
            graph_a.num_nodes(),
            graph_b.num_nodes(),
            Closed01::zero().get(),
        );

        SimilarityMatrix {
            graph_a: graph_a,
            graph_b: graph_b,
            node_color_matching: node_color_matching,
            current: x,
            previous: new_x,
            num_iterations: 0,
        }
    }

    fn in_eps(&self, eps: f32) -> bool {
        relative_eq!(self.previous, self.current, epsilon = eps)
    }

    /// Calculates the next iteration of the similarity matrix (x[k+1]).
    pub fn next(&mut self) {
        {
            let x = &self.current;
            let new_x = &mut self.previous;
            let shape = x.shape();

            for i in 0..shape.0 {
                for j in 0..shape.1 {
                    let scale = self.node_color_matching.node_color_matching(
                        self.graph_a.node_value(i),
                        self.graph_b.node_value(j),
                    );
                    let in_score =
                        s_next(self.graph_a.in_edges_of(i), self.graph_b.in_edges_of(j), x);
                    let out_score = s_next(
                        self.graph_a.out_edges_of(i),
                        self.graph_b.out_edges_of(j),
                        x,
                    );
                    new_x[(i, j)] = in_score.average(out_score).mul(scale).get();
                }
            }
        }

        mem::swap(&mut self.previous, &mut self.current);
        self.num_iterations += 1;
    }

    #[inline]
    /// Iteratively calculate the similarity matrix.
    ///
    /// `stop_after_iter`: Stop after iteration (Calculate x(stop_after_iter))
    /// `eps`:   When to stop the iteration
    pub fn iterate(&mut self, stop_after_iter: usize, eps: f32) {
        for _ in 0..stop_after_iter {
            if self.in_eps(eps) {
                break;
            }
            self.next();
        }
    }

    pub fn matrix(&self) -> &DMatrix<f32> {
        &self.current
    }

    pub fn num_iterations(&self) -> usize {
        self.num_iterations
    }

    pub fn min_nodes(&self) -> usize {
        cmp::min(self.current.nrows(), self.current.ncols())
    }

    pub fn max_nodes(&self) -> usize {
        cmp::max(self.current.nrows(), self.current.ncols())
    }

    pub fn optimal_node_assignment(&self) -> Vec<(usize, usize)> {
        let n = self.min_nodes();
        let assignment = if n > 0 {
            let mut w = WeightMatrix::from_fn(n, |ij| similarity_cost(self.current[ij]));
            solve_assignment(&mut w).unwrap()
        } else {
            Vec::new()
        };
        assert!(assignment.len() == n);
        assignment
    }

    fn score_optimal_sum(&self, node_assignment: Option<&[(usize, usize)]>) -> f32 {
        match node_assignment {
            Some(node_assignment) => {
                assert!(node_assignment.len() == self.min_nodes());
                node_assignment
                    .iter()
                    .fold(0.0, |acc, &ab| acc + self.current[ab])
            }
            None => {
                let node_assignment = self.optimal_node_assignment();
                assert!(node_assignment.len() == self.min_nodes());
                node_assignment
                    .iter()
                    .fold(0.0, |acc, &ab| acc + self.current[ab])
            }
        }
    }

    /// Calculate a measure how good the edge weights match up.
    ///
    /// We start by calculating the optimal node assignment between nodes of graph A and graph B,
    /// then compare all outgoing edges of similar-assigned nodes by again using an assignment
    /// between the edge-weight differences of all edge pairs.
    pub fn score_outgoing_edge_weights_sum_norm(
        &self,
        node_assignment: &[(usize, usize)],
        norm: ScoreNorm,
    ) -> Closed01<f32> {
        let n = self.min_nodes();
        let m = self.max_nodes();
        debug_assert!(m >= n);

        assert!(node_assignment.len() == n);

        // we sum up all edge weight scores
        let sum: f32 = node_assignment.iter().fold(0.0, |acc, &(node_i, node_j)| {
            let score_ij = self.score_outgoing_edge_weights_of(node_i, node_j);
            acc + score_ij.get()
        });

        assert!(sum >= 0.0 && sum <= n as f32);

        match norm {
            // Not penalize missing nodes.
            ScoreNorm::MinDegree => Closed01::new(sum / n as f32),

            // To penalize for missing nodes, divide by the maximum number of nodes `m`.
            ScoreNorm::MaxDegree => Closed01::new(sum / m as f32),
        }
    }

    /// Calculate a similarity measure of outgoing edges of nodes `node_i` of graph A and `node_j`
    /// of graph B.  A score of 1.0 means, the edges weights match up perfectly. 0.0 means, no
    /// similarity.
    fn score_outgoing_edge_weights_of(&self, node_i: usize, node_j: usize) -> Closed01<f32> {
        let out_i = self.graph_a.out_edges_of(node_i);
        let out_j = self.graph_b.out_edges_of(node_j);

        let max_deg = cmp::max(out_i.num_edges(), out_j.num_edges());

        if max_deg == 0 {
            // Nodes with no edges are perfectly similar
            return Closed01::one();
        }

        // Calculates the edge weight distance between edges i and j.
        let edge_weight_distance = &|(i, j)| {
            match (out_i.nth_edge_weight(i), out_j.nth_edge_weight(j)) {
                (Some(w_i), Some(w_j)) => w_i.distance(w_j),
                _ => {
                    // Maximum penalty between two weighted edges
                    // NOTE: missing edges could be penalized more, but we already
                    // penalize for that in the node similarity measure.
                    Closed01::one()
                }
            }
            .get()
        };

        let mut w = WeightMatrix::from_fn(max_deg, edge_weight_distance);

        // calculate optimal edge weight assignement.
        let assignment = solve_assignment(&mut w).unwrap();
        assert!(assignment.len() == max_deg);

        // The sum is the sum of all weight differences on the optimal `path`.
        // It's range is from 0.0 (perfect matching) to max_deg*1.0 (bad matching).
        let sum: f32 = assignment
            .iter()
            .fold(0.0, |acc, &ij| acc + edge_weight_distance(ij));

        debug_assert!(sum >= 0.0 && sum <= max_deg as f32);

        // we "invert" the normalized sum so that 1.0 means perfect matching and 0.0
        // no matching.
        Closed01::new(sum / max_deg as f32).inv()
    }

    /// Sums the optimal assignment of the node similarities and normalizes (divides)
    /// by the min/max degree of both graphs.
    /// ScoreNorm::MinDegree is used as default in the paper.
    pub fn score_optimal_sum_norm(
        &self,
        node_assignment: Option<&[(usize, usize)]>,
        norm: ScoreNorm,
    ) -> Closed01<f32> {
        let n = self.min_nodes();
        let m = self.max_nodes();

        if n > 0 {
            assert!(m > 0);
            let sum = self.score_optimal_sum(node_assignment);
            assert!(sum >= 0.0 && sum <= n as f32);

            match norm {
                // Not penalize missing nodes.
                ScoreNorm::MinDegree => Closed01::new(sum / n as f32),

                // To penalize for missing nodes, divide by the maximum number of nodes `m`.
                ScoreNorm::MaxDegree => Closed01::new(sum / m as f32),
            }
        } else {
            Closed01::zero()
        }
    }

    /// Calculates the average over the whole node similarity matrix. This is faster,
    /// as no assignment has to be found. "Graphs with greater number of automorphisms
    /// would be considered to be more self-similar than graphs without automorphisms."
    pub fn score_average(&self) -> Closed01<f32> {
        let n = self.min_nodes();
        if n > 0 {
            let items = self.current.as_slice();
            let sum: f32 = items.iter().fold(0.0, |acc, &v| acc + v);
            let len = items.len();
            assert!(len > 0);
            Closed01::new(sum / len as f32)
        } else {
            Closed01::zero()
        }
    }
}

pub fn similarity_max_degree<T: Graph>(a: &T, b: &T, num_iters: usize, eps: f32) -> Closed01<f32> {
    let mut s = SimilarityMatrix::new(a, b, IgnoreNodeColors);
    s.iterate(num_iters, eps);
    s.score_optimal_sum_norm(None, ScoreNorm::MaxDegree)
}

pub fn similarity_min_degree<T: Graph>(a: &T, b: &T, num_iters: usize, eps: f32) -> Closed01<f32> {
    let mut s = SimilarityMatrix::new(a, b, IgnoreNodeColors);
    s.iterate(num_iters, eps);
    s.score_optimal_sum_norm(None, ScoreNorm::MinDegree)
}
<|endoftext|>


<commit_before>#![feature(vec_push_all)]

pub struct JsonEncoder {
    buffer: Vec<u8>
}

impl JsonEncoder {
    pub fn new() -> JsonEncoder {
        JsonEncoder{buffer: Vec::new()}
    }

    #[inline(always)]
    pub fn encode_raw(&mut self, raw: &[u8]) {
        self.buffer.push_all(raw);
    }

    pub fn into_vec(self) -> Vec<u8> {
        self.buffer
    }

    pub fn with_buf<F>(&mut self, f: F) where F: Fn(&mut Vec<u8>) {
        f(&mut self.buffer);
    }

    #[inline(always)]
    pub fn encode_str_noescape(&mut self, raw_str: &str) {
        self.buffer.push(b'"');
        self.buffer.push_all(raw_str.as_bytes());
        self.buffer.push(b'"');
    }

    fn escape_bytes(&mut self, bytes: &[u8]) {
        let mut start = 0;

        for (i, byte) in bytes.iter().enumerate() {
            let escaped = match *byte {
                b'"' => b"\\\"",
                b'\\' => b"\\\\",
                b'\x08' => b"\\b",
                b'\x0c' => b"\\f",
                b'\n' => b"\\n",
                b'\r' => b"\\r",
                b'\t' => b"\\t",
                _ => {
                    continue;
                }
            };

            if start < i {
                self.encode_raw(&bytes[start..i]);
            }

            self.encode_raw(escaped);

            start = i + 1;
        }

        if start != bytes.len() {
            self.encode_raw(&bytes[start..]);
        }
    }

    pub fn encode_decimal_str(&mut self, value: u64) {
        self.buffer.push(b'"');
        self.encode_u64_decimal(value);
        self.buffer.push(b'"');
    }

    // encodes as decimal string
    #[inline(always)]
    fn encode_u64_decimal(&mut self, value: u64) {
        const CHARS: &'static [u8] = b"0123456789";
        const MAX_DIGITS: usize = 20;

        if value == 0 {
            self.buffer.push(b'0');
            return;
        }

        let mut digits: [u8; MAX_DIGITS] = [b'0'; MAX_DIGITS];
        let mut i = MAX_DIGITS;
        let mut n = value;
        while n > 0 {
            i -= 1;
            digits[i] = CHARS[(n % 10) as usize];
            n = n / 10;
        }

        self.encode_raw(&digits[i..]);
    }

    #[inline(always)]
    pub fn encode_str(&mut self, s: &str) {
        self.buffer.push(b'"');
        self.escape_bytes(s.as_bytes());
        self.buffer.push(b'"');
    }

    #[inline(always)]
    pub fn encode_i32(&mut self, value: i32) {
        if value == 0 {
            self.buffer.push(b'0');
        } else if value > 0 {
            self.encode_u31(value as u32);
        } else {
            self.buffer.push(b'-');
            self.encode_u31((-value) as u32);
        }
    }

    // encodes a 31-bit unsigned integer != 0
    fn encode_u31(&mut self, value: u32) {
        const CHARS: &'static [u8] = b"0123456789";
        const MAX_DIGITS: usize = 10;
        debug_assert!(value != 0);

        let mut digits: [u8; MAX_DIGITS] = [b'0'; MAX_DIGITS];
        let mut i = MAX_DIGITS;
        let mut n = value;
        while n > 0 {
            i -= 1;
            digits[i] = CHARS[(n % 10) as usize];
            n = n / 10;
        }

        self.encode_raw(&digits[i..]);
    }

    pub fn encode_obj<F>(&mut self, f: F) where F: Fn(&mut JsonObjectEncoder) {
        self.buffer.push(b'{');
        {
            f(&mut JsonObjectEncoder {js: self, needs_sep: false});
        }
        self.buffer.push(b'}');
    }

    pub fn encode_array<F>(&mut self, f: F) where F: Fn(&mut JsonArrayEncoder) {
        self.buffer.push(b'[');
        {
            f(&mut JsonArrayEncoder {js: self, needs_sep: false});
        }
        self.buffer.push(b']');
    }

}

pub struct JsonObjectEncoder<'a> {
    js: &'a mut JsonEncoder,
    needs_sep: bool,
}

impl<'a> JsonObjectEncoder<'a> {
    // XXX: name MAY NOT include escapable characters
    #[inline(always)]
    pub fn encode_field<F:Fn(&mut JsonEncoder)>(&mut self, name: &str, f: F) {
        if self.needs_sep {
            self.js.buffer.push(b',');
        } else {
            self.needs_sep = true;
        }
        self.js.encode_str_noescape(name);
        self.js.buffer.push(b':');

        f(self.js);
    }
}

pub struct JsonArrayEncoder<'a> {
    js: &'a mut JsonEncoder,
    needs_sep: bool,
}

impl<'a> JsonArrayEncoder<'a> {
    #[inline(always)]
    pub fn encode_elm<F:Fn(&mut JsonEncoder)>(&mut self, f: F) {
        if self.needs_sep {
            self.js.buffer.push(b',');
        } else {
            self.needs_sep = true;
        }
        f(self.js);
    }

    #[inline(always)]
    pub fn encode_elm_i32(&mut self, val: i32) {
        self.encode_elm(|j| j.encode_i32(val));
    }
}

#[test]
fn test_json_obj_encoder() {
    use std::str;

    let mut js = JsonEncoder::new();
    js.encode_obj(|_|{});
    assert_eq!(b"{}", &js.into_vec()[..]);

    let mut js = JsonEncoder::new();
    js.encode_obj(|jso| {
        jso.encode_field("total", |js| js.encode_i32(31));
    });
    assert_eq!(b"{\"total\":31}", &js.into_vec()[..]);

    let mut js = JsonEncoder::new();
    js.encode_obj(|jso| {
        jso.encode_field("total", |js| js.encode_i32(31));
        jso.encode_field("next", |js| js.encode_str("abc"));
    });
    assert_eq!(b"{\"total\":31,\"next\":\"abc\"}", &js.into_vec()[..]);

    let mut js = JsonEncoder::new();
    js.encode_obj(|jso| {
        jso.encode_field("total", |js| js.encode_i32(31));
        jso.encode_field("next", |js| js.encode_str("abc"));
        jso.encode_field("tags", |js| {
                js.encode_array(|jsa| {
                        jsa.encode_elm(|js| js.encode_i32(1));
                        jsa.encode_elm(|js| js.encode_i32(2));
                });
        });
    });
    assert_eq!("{\"total\":31,\"next\":\"abc\",\"tags\":[1,2]}", str::from_utf8(&js.into_vec()[..]).unwrap());
}
<commit_msg>Add helper method<commit_after>#![feature(vec_push_all)]

pub struct JsonEncoder {
    buffer: Vec<u8>
}

impl JsonEncoder {
    pub fn new() -> JsonEncoder {
        JsonEncoder{buffer: Vec::new()}
    }

    #[inline(always)]
    pub fn encode_raw(&mut self, raw: &[u8]) {
        self.buffer.push_all(raw);
    }

    pub fn into_vec(self) -> Vec<u8> {
        self.buffer
    }

    pub fn with_buf<F>(&mut self, f: F) where F: Fn(&mut Vec<u8>) {
        f(&mut self.buffer);
    }

    #[inline(always)]
    pub fn encode_str_noescape(&mut self, raw_str: &str) {
        self.buffer.push(b'"');
        self.buffer.push_all(raw_str.as_bytes());
        self.buffer.push(b'"');
    }

    fn escape_bytes(&mut self, bytes: &[u8]) {
        let mut start = 0;

        for (i, byte) in bytes.iter().enumerate() {
            let escaped = match *byte {
                b'"' => b"\\\"",
                b'\\' => b"\\\\",
                b'\x08' => b"\\b",
                b'\x0c' => b"\\f",
                b'\n' => b"\\n",
                b'\r' => b"\\r",
                b'\t' => b"\\t",
                _ => {
                    continue;
                }
            };

            if start < i {
                self.encode_raw(&bytes[start..i]);
            }

            self.encode_raw(escaped);

            start = i + 1;
        }

        if start != bytes.len() {
            self.encode_raw(&bytes[start..]);
        }
    }

    pub fn encode_decimal_str(&mut self, value: u64) {
        self.buffer.push(b'"');
        self.encode_u64_decimal(value);
        self.buffer.push(b'"');
    }

    // encodes as decimal string
    #[inline(always)]
    fn encode_u64_decimal(&mut self, value: u64) {
        const CHARS: &'static [u8] = b"0123456789";
        const MAX_DIGITS: usize = 20;

        if value == 0 {
            self.buffer.push(b'0');
            return;
        }

        let mut digits: [u8; MAX_DIGITS] = [b'0'; MAX_DIGITS];
        let mut i = MAX_DIGITS;
        let mut n = value;
        while n > 0 {
            i -= 1;
            digits[i] = CHARS[(n % 10) as usize];
            n = n / 10;
        }

        self.encode_raw(&digits[i..]);
    }

    #[inline(always)]
    pub fn encode_str(&mut self, s: &str) {
        self.buffer.push(b'"');
        self.escape_bytes(s.as_bytes());
        self.buffer.push(b'"');
    }

    #[inline(always)]
    pub fn encode_i32(&mut self, value: i32) {
        if value == 0 {
            self.buffer.push(b'0');
        } else if value > 0 {
            self.encode_u31(value as u32);
        } else {
            self.buffer.push(b'-');
            self.encode_u31((-value) as u32);
        }
    }

    // encodes a 31-bit unsigned integer != 0
    fn encode_u31(&mut self, value: u32) {
        const CHARS: &'static [u8] = b"0123456789";
        const MAX_DIGITS: usize = 10;
        debug_assert!(value != 0);

        let mut digits: [u8; MAX_DIGITS] = [b'0'; MAX_DIGITS];
        let mut i = MAX_DIGITS;
        let mut n = value;
        while n > 0 {
            i -= 1;
            digits[i] = CHARS[(n % 10) as usize];
            n = n / 10;
        }

        self.encode_raw(&digits[i..]);
    }

    pub fn encode_obj<F>(&mut self, f: F) where F: Fn(&mut JsonObjectEncoder) {
        self.buffer.push(b'{');
        {
            f(&mut JsonObjectEncoder {js: self, needs_sep: false});
        }
        self.buffer.push(b'}');
    }

    pub fn encode_array<F>(&mut self, f: F) where F: Fn(&mut JsonArrayEncoder) {
        self.buffer.push(b'[');
        {
            f(&mut JsonArrayEncoder {js: self, needs_sep: false});
        }
        self.buffer.push(b']');
    }

}

pub struct JsonObjectEncoder<'a> {
    js: &'a mut JsonEncoder,
    needs_sep: bool,
}

impl<'a> JsonObjectEncoder<'a> {
    // XXX: name MAY NOT include escapable characters
    #[inline(always)]
    pub fn encode_field<F:Fn(&mut JsonEncoder)>(&mut self, name: &str, f: F) {
        if self.needs_sep {
            self.js.buffer.push(b',');
        } else {
            self.needs_sep = true;
        }
        self.js.encode_str_noescape(name);
        self.js.buffer.push(b':');

        f(self.js);
    }

    #[inline(always)]
    pub fn encode_field_array<F:Fn(&mut JsonArrayEncoder)>(&mut self, name: &str, f: F) {
        self.encode_field(name, |js| js.encode_array(|jsa| f(jsa)));
    }
}

pub struct JsonArrayEncoder<'a> {
    js: &'a mut JsonEncoder,
    needs_sep: bool,
}

impl<'a> JsonArrayEncoder<'a> {
    #[inline(always)]
    pub fn encode_elm<F:Fn(&mut JsonEncoder)>(&mut self, f: F) {
        if self.needs_sep {
            self.js.buffer.push(b',');
        } else {
            self.needs_sep = true;
        }
        f(self.js);
    }

    #[inline(always)]
    pub fn encode_elm_i32(&mut self, val: i32) {
        self.encode_elm(|j| j.encode_i32(val));
    }
}

#[test]
fn test_json_obj_encoder() {
    use std::str;

    let mut js = JsonEncoder::new();
    js.encode_obj(|_|{});
    assert_eq!(b"{}", &js.into_vec()[..]);

    let mut js = JsonEncoder::new();
    js.encode_obj(|jso| {
        jso.encode_field("total", |js| js.encode_i32(31));
    });
    assert_eq!(b"{\"total\":31}", &js.into_vec()[..]);

    let mut js = JsonEncoder::new();
    js.encode_obj(|jso| {
        jso.encode_field("total", |js| js.encode_i32(31));
        jso.encode_field("next", |js| js.encode_str("abc"));
    });
    assert_eq!(b"{\"total\":31,\"next\":\"abc\"}", &js.into_vec()[..]);

    let mut js = JsonEncoder::new();
    js.encode_obj(|jso| {
        jso.encode_field("total", |js| js.encode_i32(31));
        jso.encode_field("next", |js| js.encode_str("abc"));
        jso.encode_field("tags", |js| {
                js.encode_array(|jsa| {
                        jsa.encode_elm(|js| js.encode_i32(1));
                        jsa.encode_elm(|js| js.encode_i32(2));
                });
        });
    });
    assert_eq!("{\"total\":31,\"next\":\"abc\",\"tags\":[1,2]}", str::from_utf8(&js.into_vec()[..]).unwrap());
}
<|endoftext|>


<commit_before>extern crate xml;
#[macro_use] extern crate serde;
#[macro_use] extern crate log;

#[macro_use] mod error;
mod map;
mod seq;
mod var;

pub use error::Error;
pub use xml::reader::{EventReader, ParserConfig};

use error::VResult;
use xml::reader::XmlEvent;
use xml::name::OwnedName;
use serde::de::{self, Visitor};
use std::io::Read;
use map::MapVisitor;
use seq::SeqVisitor;
use var::EnumVisitor;

pub struct Deserializer<R: Read> {
    depth: usize,
    reader: EventReader<R>,
    peeked: Option<XmlEvent>,
    is_map_value: bool
}

impl<R: Read> Deserializer<R> {
    pub fn new(reader: EventReader<R>) -> Self {
        Deserializer {
            depth: 0,
            reader: reader,
            peeked: None,
            is_map_value: false
        }
    }

    fn peek(&mut self) -> Result<&XmlEvent, Error> {
        if self.peeked.is_none() {
            self.peeked = Some(self.inner_next()?);
        }
        debug_expect!(self.peeked.as_ref(), Some(peeked) => {
            debug!("Peeked {:?}", peeked);
            Ok(peeked)
        })
    }

    fn inner_next(&mut self) -> Result<XmlEvent, Error> {
        loop {
            match self.reader.next().map_err(Error::Syntax)? {
                XmlEvent::StartDocument { .. } |
                XmlEvent::EndDocument { .. } |
                XmlEvent::ProcessingInstruction { .. } |
                XmlEvent::Comment(_) => {/* skip */}

                other => return Ok(other)
            }
        }
    }

    fn next(&mut self) -> Result<XmlEvent, Error> {
        let next = if let Some(peeked) = self.peeked.take() {
            peeked
        } else {
            self.inner_next()?
        };
        match next {
            XmlEvent::StartElement { .. } => {
                self.depth += 1;
            },
            XmlEvent::EndElement { .. } => {
                self.depth -= 1;
            },
            _ => {}
        }
        debug!("Fetched {:?}", next);
        Ok(next)
    }

    fn set_map_value(&mut self) {
        self.is_map_value = true;
    }

    pub fn unset_map_value(&mut self) -> bool {
        ::std::mem::replace(&mut self.is_map_value, false)
    }

    fn read_inner_value<V: Visitor, F: FnOnce(&mut Self) -> VResult<V>>(&mut self, f: F) -> VResult<V> {
        if self.unset_map_value() {
            debug_expect!(self.next(), Ok(XmlEvent::StartElement { name, .. }) => {
                let result = f(self)?;
                self.expect_end_element(name)?;
                Ok(result)
            })
        } else {
            f(self)
        }
    }

    fn expect_end_element(&mut self, start_name: OwnedName) -> Result<(), Error> {
        expect!(self.next()?, XmlEvent::EndElement { name, .. } => {
            if name == start_name {
                Ok(())
            } else {
                Err(Error::Custom(format!("End tag </{}> didn't match the start tag <{}>", name.local_name, start_name.local_name)))
            }
        })
    }
}

impl<'a, R: Read> de::Deserializer for &'a mut Deserializer<R> {
    type Error = Error;

    forward_to_deserialize! {
        newtype_struct struct_field
    }

    fn deserialize_struct<V: Visitor>(self, _name: &'static str, fields: &'static [&'static str], visitor: V) -> VResult<V> {
        self.unset_map_value();
        expect!(self.next()?, XmlEvent::StartElement { name, attributes, .. } => {
            let map_value = visitor.visit_map(MapVisitor::new(self, attributes, fields.contains(&"$value")))?;
            self.expect_end_element(name)?;
            Ok(map_value)
        })
    }

    fn deserialize_u64<V: Visitor>(self, visitor: V) -> VResult<V> {
        self.deserialize_string(visitor)
    }

    fn deserialize_u32<V: Visitor>(self, visitor: V) -> VResult<V> {
        self.deserialize_string(visitor)
    }

    fn deserialize_u16<V: Visitor>(self, visitor: V) -> VResult<V> {
        self.deserialize_string(visitor)
    }

    fn deserialize_u8<V: Visitor>(self, visitor: V) -> VResult<V> {
        self.deserialize_string(visitor)
    }

    fn deserialize_i64<V: Visitor>(self, visitor: V) -> VResult<V> {
        self.deserialize_string(visitor)
    }

    fn deserialize_i32<V: Visitor>(self, visitor: V) -> VResult<V> {
        self.deserialize_string(visitor)
    }

    fn deserialize_i16<V: Visitor>(self, visitor: V) -> VResult<V> {
        self.deserialize_string(visitor)
    }

    fn deserialize_i8<V: Visitor>(self, visitor: V) -> VResult<V> {
        self.deserialize_string(visitor)
    }

    fn deserialize_f32<V: Visitor>(self, visitor: V) -> VResult<V> {
        self.deserialize_string(visitor)
    }

    fn deserialize_f64<V: Visitor>(self, visitor: V) -> VResult<V> {
        self.deserialize_string(visitor)
    }

    fn deserialize_bool<V: Visitor>(self, visitor: V) -> VResult<V> {
        self.deserialize_string(visitor)
    }

    fn deserialize_char<V: Visitor>(self, visitor: V) -> VResult<V> {
        self.deserialize_string(visitor)
    }

    fn deserialize_str<V: Visitor>(self, visitor: V) -> VResult<V> {
        self.deserialize_string(visitor)
    }

    fn deserialize_bytes<V: Visitor>(self, visitor: V) -> VResult<V> {
        self.deserialize_string(visitor)
    }

    fn deserialize_byte_buf<V: Visitor>(self, visitor: V) -> VResult<V> {
        self.deserialize_string(visitor)
    }

    fn deserialize_unit<V: Visitor>(self, visitor: V) -> VResult<V> {
        self.read_inner_value::<V, _>(|this| {
            expect!(this.peek()?, &XmlEvent::EndElement { .. } => {
                visitor.visit_unit()
            })
        })
    }

    fn deserialize_unit_struct<V: Visitor>(self, _name: &'static str, visitor: V) -> VResult<V> {
        self.deserialize_unit(visitor)
    }

    fn deserialize_tuple_struct<V: Visitor>(self, _name: &'static str, len: usize, visitor: V) -> VResult<V> {
        self.deserialize_tuple(len, visitor)
    }

    fn deserialize_tuple<V: Visitor>(self, len: usize, visitor: V) -> VResult<V> {
        self.deserialize_seq_fixed_size(len, visitor)
    }

    fn deserialize_enum<V: Visitor>(self, _name: &'static str, _variants: &'static [&'static str], visitor: V) -> VResult<V> {
        self.read_inner_value::<V, _>(|this| {
            visitor.visit_enum(EnumVisitor::new(this))
        })
    }

    fn deserialize_string<V: Visitor>(self, visitor: V) -> VResult<V> {
        self.read_inner_value::<V, _>(|this| {
            if let XmlEvent::EndElement { .. } = *this.peek()? {
                return visitor.visit_str("");
            }
            expect!(this.next()?, XmlEvent::Characters(s) => {
                visitor.visit_string(s)
            })
        })
    }

    fn deserialize_seq<V: Visitor>(self, visitor: V) -> VResult<V> {
        visitor.visit_seq(SeqVisitor::new(self, None))
    }

    fn deserialize_seq_fixed_size<V: Visitor>(self, len: usize, visitor: V) -> VResult<V> {
        visitor.visit_seq(SeqVisitor::new(self, Some(len)))
    }

    fn deserialize_map<V: Visitor>(self, visitor: V) -> VResult<V> {
        self.unset_map_value();
        expect!(self.next()?, XmlEvent::StartElement { name, attributes, .. } => {
            let map_value = visitor.visit_map(MapVisitor::new(self, attributes, false))?;
            self.expect_end_element(name)?;
            Ok(map_value)
        })
    }

    fn deserialize_option<V: Visitor>(self, visitor: V) -> VResult<V> {
        if self.is_map_value {
            return self.read_inner_value::<V, _>(|this| {
                visitor.visit_some(this)
            });
        }
        match *self.peek()? {
            XmlEvent::EndElement { .. } => visitor.visit_none(),
            _ => visitor.visit_some(self)
        }
    }

    fn deserialize_ignored_any<V: Visitor>(self, visitor: V) -> VResult<V> {
        self.unset_map_value();
        let depth = self.depth;
        loop {
            self.next()?;
            if self.depth == depth {
                break;
            }
        }
        visitor.visit_unit()
    }

    fn deserialize<V: Visitor>(self, visitor: V) -> VResult<V> {
        match *self.peek()? {
            XmlEvent::StartElement { .. } => {
                self.deserialize_map(visitor)
            }
            XmlEvent::EndElement { .. } => {
                self.deserialize_unit(visitor)
            }
            _ => {
                self.deserialize_string(visitor)
            }
        }
    }
}
<commit_msg>Add helper methods to deserialize from Read<commit_after>extern crate xml;
#[macro_use] extern crate serde;
#[macro_use] extern crate log;

#[macro_use] mod error;
mod map;
mod seq;
mod var;

pub use error::Error;
pub use xml::reader::{EventReader, ParserConfig};

use error::VResult;
use xml::reader::XmlEvent;
use xml::name::OwnedName;
use serde::de::{self, Visitor, Deserialize};
use std::io::Read;
use map::MapVisitor;
use seq::SeqVisitor;
use var::EnumVisitor;

pub struct Deserializer<R: Read> {
    depth: usize,
    reader: EventReader<R>,
    peeked: Option<XmlEvent>,
    is_map_value: bool
}

impl<R: Read> Deserializer<R> {
    pub fn new(reader: EventReader<R>) -> Self {
        Deserializer {
            depth: 0,
            reader: reader,
            peeked: None,
            is_map_value: false
        }
    }

    pub fn new_from_reader(reader: R) -> Self {
        Self::new(EventReader::new_with_config(reader, ParserConfig {
            trim_whitespace: true,
            whitespace_to_characters: true,
            cdata_to_characters: true,
            ignore_comments: true,
            coalesce_characters: true
        }))
    }

    pub fn deserialize<T: Deserialize>(reader: R) -> Result<T, Error> {
        T::deserialize(&mut Deserializer::new_from_reader(reader))
    }

    fn peek(&mut self) -> Result<&XmlEvent, Error> {
        if self.peeked.is_none() {
            self.peeked = Some(self.inner_next()?);
        }
        debug_expect!(self.peeked.as_ref(), Some(peeked) => {
            debug!("Peeked {:?}", peeked);
            Ok(peeked)
        })
    }

    fn inner_next(&mut self) -> Result<XmlEvent, Error> {
        loop {
            match self.reader.next().map_err(Error::Syntax)? {
                XmlEvent::StartDocument { .. } |
                XmlEvent::EndDocument { .. } |
                XmlEvent::ProcessingInstruction { .. } |
                XmlEvent::Comment(_) => {/* skip */}

                other => return Ok(other)
            }
        }
    }

    fn next(&mut self) -> Result<XmlEvent, Error> {
        let next = if let Some(peeked) = self.peeked.take() {
            peeked
        } else {
            self.inner_next()?
        };
        match next {
            XmlEvent::StartElement { .. } => {
                self.depth += 1;
            },
            XmlEvent::EndElement { .. } => {
                self.depth -= 1;
            },
            _ => {}
        }
        debug!("Fetched {:?}", next);
        Ok(next)
    }

    fn set_map_value(&mut self) {
        self.is_map_value = true;
    }

    pub fn unset_map_value(&mut self) -> bool {
        ::std::mem::replace(&mut self.is_map_value, false)
    }

    fn read_inner_value<V: Visitor, F: FnOnce(&mut Self) -> VResult<V>>(&mut self, f: F) -> VResult<V> {
        if self.unset_map_value() {
            debug_expect!(self.next(), Ok(XmlEvent::StartElement { name, .. }) => {
                let result = f(self)?;
                self.expect_end_element(name)?;
                Ok(result)
            })
        } else {
            f(self)
        }
    }

    fn expect_end_element(&mut self, start_name: OwnedName) -> Result<(), Error> {
        expect!(self.next()?, XmlEvent::EndElement { name, .. } => {
            if name == start_name {
                Ok(())
            } else {
                Err(Error::Custom(format!("End tag </{}> didn't match the start tag <{}>", name.local_name, start_name.local_name)))
            }
        })
    }
}

impl<'a, R: Read> de::Deserializer for &'a mut Deserializer<R> {
    type Error = Error;

    forward_to_deserialize! {
        newtype_struct struct_field
    }

    fn deserialize_struct<V: Visitor>(self, _name: &'static str, fields: &'static [&'static str], visitor: V) -> VResult<V> {
        self.unset_map_value();
        expect!(self.next()?, XmlEvent::StartElement { name, attributes, .. } => {
            let map_value = visitor.visit_map(MapVisitor::new(self, attributes, fields.contains(&"$value")))?;
            self.expect_end_element(name)?;
            Ok(map_value)
        })
    }

    fn deserialize_u64<V: Visitor>(self, visitor: V) -> VResult<V> {
        self.deserialize_string(visitor)
    }

    fn deserialize_u32<V: Visitor>(self, visitor: V) -> VResult<V> {
        self.deserialize_string(visitor)
    }

    fn deserialize_u16<V: Visitor>(self, visitor: V) -> VResult<V> {
        self.deserialize_string(visitor)
    }

    fn deserialize_u8<V: Visitor>(self, visitor: V) -> VResult<V> {
        self.deserialize_string(visitor)
    }

    fn deserialize_i64<V: Visitor>(self, visitor: V) -> VResult<V> {
        self.deserialize_string(visitor)
    }

    fn deserialize_i32<V: Visitor>(self, visitor: V) -> VResult<V> {
        self.deserialize_string(visitor)
    }

    fn deserialize_i16<V: Visitor>(self, visitor: V) -> VResult<V> {
        self.deserialize_string(visitor)
    }

    fn deserialize_i8<V: Visitor>(self, visitor: V) -> VResult<V> {
        self.deserialize_string(visitor)
    }

    fn deserialize_f32<V: Visitor>(self, visitor: V) -> VResult<V> {
        self.deserialize_string(visitor)
    }

    fn deserialize_f64<V: Visitor>(self, visitor: V) -> VResult<V> {
        self.deserialize_string(visitor)
    }

    fn deserialize_bool<V: Visitor>(self, visitor: V) -> VResult<V> {
        self.deserialize_string(visitor)
    }

    fn deserialize_char<V: Visitor>(self, visitor: V) -> VResult<V> {
        self.deserialize_string(visitor)
    }

    fn deserialize_str<V: Visitor>(self, visitor: V) -> VResult<V> {
        self.deserialize_string(visitor)
    }

    fn deserialize_bytes<V: Visitor>(self, visitor: V) -> VResult<V> {
        self.deserialize_string(visitor)
    }

    fn deserialize_byte_buf<V: Visitor>(self, visitor: V) -> VResult<V> {
        self.deserialize_string(visitor)
    }

    fn deserialize_unit<V: Visitor>(self, visitor: V) -> VResult<V> {
        self.read_inner_value::<V, _>(|this| {
            expect!(this.peek()?, &XmlEvent::EndElement { .. } => {
                visitor.visit_unit()
            })
        })
    }

    fn deserialize_unit_struct<V: Visitor>(self, _name: &'static str, visitor: V) -> VResult<V> {
        self.deserialize_unit(visitor)
    }

    fn deserialize_tuple_struct<V: Visitor>(self, _name: &'static str, len: usize, visitor: V) -> VResult<V> {
        self.deserialize_tuple(len, visitor)
    }

    fn deserialize_tuple<V: Visitor>(self, len: usize, visitor: V) -> VResult<V> {
        self.deserialize_seq_fixed_size(len, visitor)
    }

    fn deserialize_enum<V: Visitor>(self, _name: &'static str, _variants: &'static [&'static str], visitor: V) -> VResult<V> {
        self.read_inner_value::<V, _>(|this| {
            visitor.visit_enum(EnumVisitor::new(this))
        })
    }

    fn deserialize_string<V: Visitor>(self, visitor: V) -> VResult<V> {
        self.read_inner_value::<V, _>(|this| {
            if let XmlEvent::EndElement { .. } = *this.peek()? {
                return visitor.visit_str("");
            }
            expect!(this.next()?, XmlEvent::Characters(s) => {
                visitor.visit_string(s)
            })
        })
    }

    fn deserialize_seq<V: Visitor>(self, visitor: V) -> VResult<V> {
        visitor.visit_seq(SeqVisitor::new(self, None))
    }

    fn deserialize_seq_fixed_size<V: Visitor>(self, len: usize, visitor: V) -> VResult<V> {
        visitor.visit_seq(SeqVisitor::new(self, Some(len)))
    }

    fn deserialize_map<V: Visitor>(self, visitor: V) -> VResult<V> {
        self.unset_map_value();
        expect!(self.next()?, XmlEvent::StartElement { name, attributes, .. } => {
            let map_value = visitor.visit_map(MapVisitor::new(self, attributes, false))?;
            self.expect_end_element(name)?;
            Ok(map_value)
        })
    }

    fn deserialize_option<V: Visitor>(self, visitor: V) -> VResult<V> {
        if self.is_map_value {
            return self.read_inner_value::<V, _>(|this| {
                visitor.visit_some(this)
            });
        }
        match *self.peek()? {
            XmlEvent::EndElement { .. } => visitor.visit_none(),
            _ => visitor.visit_some(self)
        }
    }

    fn deserialize_ignored_any<V: Visitor>(self, visitor: V) -> VResult<V> {
        self.unset_map_value();
        let depth = self.depth;
        loop {
            self.next()?;
            if self.depth == depth {
                break;
            }
        }
        visitor.visit_unit()
    }

    fn deserialize<V: Visitor>(self, visitor: V) -> VResult<V> {
        match *self.peek()? {
            XmlEvent::StartElement { .. } => {
                self.deserialize_map(visitor)
            }
            XmlEvent::EndElement { .. } => {
                self.deserialize_unit(visitor)
            }
            _ => {
                self.deserialize_string(visitor)
            }
        }
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate nom;

pub mod parser;

use std::result;

/// A SemVer Version
#[derive(PartialEq,Debug)]
pub struct Version {
    major: u32,
    minor: u32,
    patch: u32,
    pre: Option<String>,
    build: Option<String>,
}

/// An error type for this crate
///
/// Currently, just a generic error. Will make this nicer later.
#[derive(PartialEq,Debug)]
enum SemVerError {
    GenericError,
}

/// A Result type for errors
pub type Result<T> = result::Result<T, SemVerError>;

impl From<()> for SemVerError {
    fn from(_: ()) -> SemVerError {
        SemVerError::GenericError
    }
}

impl Version {
    /// Create a Version from a string
    ///
    /// Currently supported: x, x.y, and x.y.z versions.
    pub fn parse(version: &str) -> Result<Version> {
        Ok(try!(parser::try_parse(version.trim().as_bytes())))
    }
}

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

    #[test]
    fn test_parse() {
        assert_eq!(Version::parse(""), Err(SemVerError::GenericError));
        assert_eq!(Version::parse("  "), Err(SemVerError::GenericError));
        assert_eq!(Version::parse("1"), Err(SemVerError::GenericError));
        assert_eq!(Version::parse("1.2"), Err(SemVerError::GenericError));
        //assert_eq!(Version::parse("1.2.3-"), Err(SemVerError::GenericError));
        assert_eq!(Version::parse("a.b.c"), Err(SemVerError::GenericError));

        //assert_eq!(Version::parse("1.2.3 abc"), Err(SemVerError::GenericError));

        assert!(Version::parse("1.2.3") == Ok(Version {
            major: 1,
            minor: 2,
            patch: 3,
            pre: None,
            build: None,
        }));
        assert!(Version::parse("  1.2.3  ") == Ok(Version {
            major: 1,
            minor: 2,
            patch: 3,
            pre: None,
            build: None,
        }));
        assert!(Version::parse("1.2.3-alpha1") == Ok(Version {
            major: 1,
            minor: 2,
            patch: 3,
            pre: Some(String::from("alpha1")),
            build: None,
        }));
        assert!(Version::parse("  1.2.3-alpha1  ") == Ok(Version {
            major: 1,
            minor: 2,
            patch: 3,
            pre: Some(String::from("alpha1")),
            build: None
        }));
        assert!(Version::parse("1.2.3+build5") == Ok(Version {
            major: 1,
            minor: 2,
            patch: 3,
            pre: None,
            build: Some(String::from("build5")),
        }));
        assert!(Version::parse("  1.2.3+build5  ") == Ok(Version {
            major: 1,
            minor: 2,
            patch: 3,
            pre: None,
            build: Some(String::from("build5")),
        }));
        assert!(Version::parse("1.2.3-alpha1+build5") == Ok(Version {
            major: 1,
            minor: 2,
            patch: 3,
            pre: Some(String::from("alpha1")),
            build: Some(String::from("build5")),
        }));
        assert!(Version::parse("  1.2.3-alpha1+build5  ") == Ok(Version {
            major: 1,
            minor: 2,
            patch: 3,
            pre: Some(String::from("alpha1")),
            build: Some(String::from("build5")),
        }));
        assert!(Version::parse("1.2.3-1.alpha1.9+build5.7.3aedf  ") == Ok(Version {
            major: 1,
            minor: 2,
            patch: 3,
            pre: Some(String::from("1.alpha1.9")),
            build: Some(String::from("build5.7.3aedf")),
        }));
        assert_eq!(Version::parse("0.4.0-beta.1+0851523"), Ok(Version {
            major: 0,
            minor: 4,
            patch: 0,
            pre: Some(String::from("beta.1")),
            build: Some(String::from("0851523")),
        }));

    }

    /*
    #[test]
    fn test_increment_patch() {
        let mut buggy_release = Version::parse("0.1.0").unwrap();
        buggy_release.increment_patch();
        assert_eq!(buggy_release, Version::parse("0.1.1").unwrap());
    }

    #[test]
    fn test_increment_minor() {
        let mut feature_release = Version::parse("1.4.6").unwrap();
        feature_release.increment_minor();
        assert_eq!(feature_release, Version::parse("1.5.0").unwrap());
    }

    #[test]
    fn test_increment_major() {
        let mut chrome_release = Version::parse("46.1.246773").unwrap();
        chrome_release.increment_major();
        assert_eq!(chrome_release, Version::parse("47.0.0").unwrap());
    }

    #[test]
    fn test_increment_keep_prerelease() {
        let mut release = Version::parse("1.0.0-alpha").unwrap();
        release.increment_patch();

        assert_eq!(release, Version::parse("1.0.1").unwrap());

        release.increment_minor();

        assert_eq!(release, Version::parse("1.1.0").unwrap());

        release.increment_major();

        assert_eq!(release, Version::parse("2.0.0").unwrap());
    }

    #[test]
    fn test_increment_clear_metadata() {
        let mut release = Version::parse("1.0.0+4442").unwrap();
        release.increment_patch();

        assert_eq!(release, Version::parse("1.0.1").unwrap());
        release = Version::parse("1.0.1+hello").unwrap();

        release.increment_minor();

        assert_eq!(release, Version::parse("1.1.0").unwrap());
        release = Version::parse("1.1.3747+hello").unwrap();

        release.increment_major();

        assert_eq!(release, Version::parse("2.0.0").unwrap());
    }

    #[test]
    fn test_eq() {
        assert_eq!(Version::parse("1.2.3"), Version::parse("1.2.3"));
        assert_eq!(Version::parse("1.2.3-alpha1"), Version::parse("1.2.3-alpha1"));
        assert_eq!(Version::parse("1.2.3+build.42"), Version::parse("1.2.3+build.42"));
        assert_eq!(Version::parse("1.2.3-alpha1+42"), Version::parse("1.2.3-alpha1+42"));
        assert_eq!(Version::parse("1.2.3+23"), Version::parse("1.2.3+42"));
    }

    #[test]
    fn test_ne() {
        assert!(Version::parse("0.0.0")       != Version::parse("0.0.1"));
        assert!(Version::parse("0.0.0")       != Version::parse("0.1.0"));
        assert!(Version::parse("0.0.0")       != Version::parse("1.0.0"));
        assert!(Version::parse("1.2.3-alpha") != Version::parse("1.2.3-beta"));
    }

    #[test]
    fn test_show() {
        assert_eq!(format!("{}", Version::parse("1.2.3").unwrap()),
                   "1.2.3".to_string());
        assert_eq!(format!("{}", Version::parse("1.2.3-alpha1").unwrap()),
                   "1.2.3-alpha1".to_string());
        assert_eq!(format!("{}", Version::parse("1.2.3+build.42").unwrap()),
                   "1.2.3+build.42".to_string());
        assert_eq!(format!("{}", Version::parse("1.2.3-alpha1+42").unwrap()),
                   "1.2.3-alpha1+42".to_string());
    }

    #[test]
    fn test_to_string() {
        assert_eq!(Version::parse("1.2.3").unwrap().to_string(), "1.2.3".to_string());
        assert_eq!(Version::parse("1.2.3-alpha1").unwrap().to_string(), "1.2.3-alpha1".to_string());
        assert_eq!(Version::parse("1.2.3+build.42").unwrap().to_string(), "1.2.3+build.42".to_string());
        assert_eq!(Version::parse("1.2.3-alpha1+42").unwrap().to_string(), "1.2.3-alpha1+42".to_string());
    }

    #[test]
    fn test_lt() {
        assert!(Version::parse("0.0.0")          < Version::parse("1.2.3-alpha2"));
        assert!(Version::parse("1.0.0")          < Version::parse("1.2.3-alpha2"));
        assert!(Version::parse("1.2.0")          < Version::parse("1.2.3-alpha2"));
        assert!(Version::parse("1.2.3-alpha1")   < Version::parse("1.2.3"));
        assert!(Version::parse("1.2.3-alpha1")   < Version::parse("1.2.3-alpha2"));
        assert!(!(Version::parse("1.2.3-alpha2") < Version::parse("1.2.3-alpha2")));
        assert!(!(Version::parse("1.2.3+23")     < Version::parse("1.2.3+42")));
    }

    #[test]
    fn test_le() {
        assert!(Version::parse("0.0.0")        <= Version::parse("1.2.3-alpha2"));
        assert!(Version::parse("1.0.0")        <= Version::parse("1.2.3-alpha2"));
        assert!(Version::parse("1.2.0")        <= Version::parse("1.2.3-alpha2"));
        assert!(Version::parse("1.2.3-alpha1") <= Version::parse("1.2.3-alpha2"));
        assert!(Version::parse("1.2.3-alpha2") <= Version::parse("1.2.3-alpha2"));
        assert!(Version::parse("1.2.3+23")     <= Version::parse("1.2.3+42"));
    }

    #[test]
    fn test_gt() {
        assert!(Version::parse("1.2.3-alpha2")   > Version::parse("0.0.0"));
        assert!(Version::parse("1.2.3-alpha2")   > Version::parse("1.0.0"));
        assert!(Version::parse("1.2.3-alpha2")   > Version::parse("1.2.0"));
        assert!(Version::parse("1.2.3-alpha2")   > Version::parse("1.2.3-alpha1"));
        assert!(Version::parse("1.2.3")          > Version::parse("1.2.3-alpha2"));
        assert!(!(Version::parse("1.2.3-alpha2") > Version::parse("1.2.3-alpha2")));
        assert!(!(Version::parse("1.2.3+23")     > Version::parse("1.2.3+42")));
    }

    #[test]
    fn test_ge() {
        assert!(Version::parse("1.2.3-alpha2") >= Version::parse("0.0.0"));
        assert!(Version::parse("1.2.3-alpha2") >= Version::parse("1.0.0"));
        assert!(Version::parse("1.2.3-alpha2") >= Version::parse("1.2.0"));
        assert!(Version::parse("1.2.3-alpha2") >= Version::parse("1.2.3-alpha1"));
        assert!(Version::parse("1.2.3-alpha2") >= Version::parse("1.2.3-alpha2"));
        assert!(Version::parse("1.2.3+23")     >= Version::parse("1.2.3+42"));
    }

    #[test]
    fn test_prerelease_check() {
        assert!(Version::parse("1.0.0").unwrap().is_prerelease() == false);
        assert!(Version::parse("0.0.1").unwrap().is_prerelease() == false);
        assert!(Version::parse("4.1.4-alpha").unwrap().is_prerelease());
        assert!(Version::parse("1.0.0-beta294296").unwrap().is_prerelease());
    }

    #[test]
    fn test_spec_order() {
        let vs = ["1.0.0-alpha",
                  "1.0.0-alpha.1",
                  "1.0.0-alpha.beta",
                  "1.0.0-beta",
                  "1.0.0-beta.2",
                  "1.0.0-beta.11",
                  "1.0.0-rc.1",
                  "1.0.0"];
        let mut i = 1;
        while i < vs.len() {
            let a = Version::parse(vs[i-1]).unwrap();
            let b = Version::parse(vs[i]).unwrap();
            assert!(a < b);
            i += 1;
        }
    }
    */
}
<commit_msg>Switch assertion macros in tests<commit_after>#[macro_use]
extern crate nom;

pub mod parser;

use std::result;

/// A SemVer Version
#[derive(PartialEq,Debug)]
pub struct Version {
    major: u32,
    minor: u32,
    patch: u32,
    pre: Option<String>,
    build: Option<String>,
}

/// An error type for this crate
///
/// Currently, just a generic error. Will make this nicer later.
#[derive(PartialEq,Debug)]
enum SemVerError {
    GenericError,
}

/// A Result type for errors
pub type Result<T> = result::Result<T, SemVerError>;

impl From<()> for SemVerError {
    fn from(_: ()) -> SemVerError {
        SemVerError::GenericError
    }
}

impl Version {
    /// Create a Version from a string
    ///
    /// Currently supported: x, x.y, and x.y.z versions.
    pub fn parse(version: &str) -> Result<Version> {
        Ok(try!(parser::try_parse(version.trim().as_bytes())))
    }
}

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

    #[test]
    fn test_parse() {
        assert_eq!(Version::parse(""), Err(SemVerError::GenericError));
        assert_eq!(Version::parse("  "), Err(SemVerError::GenericError));
        assert_eq!(Version::parse("1"), Err(SemVerError::GenericError));
        assert_eq!(Version::parse("1.2"), Err(SemVerError::GenericError));
        //assert_eq!(Version::parse("1.2.3-"), Err(SemVerError::GenericError));
        assert_eq!(Version::parse("a.b.c"), Err(SemVerError::GenericError));

        //assert_eq!(Version::parse("1.2.3 abc"), Err(SemVerError::GenericError));

        assert_eq!(Version::parse("1.2.3"), Ok(Version {
            major: 1,
            minor: 2,
            patch: 3,
            pre: None,
            build: None,
        }));
        assert_eq!(Version::parse("  1.2.3  "), Ok(Version {
            major: 1,
            minor: 2,
            patch: 3,
            pre: None,
            build: None,
        }));
        assert_eq!(Version::parse("1.2.3-alpha1"), Ok(Version {
            major: 1,
            minor: 2,
            patch: 3,
            pre: Some(String::from("alpha1")),
            build: None,
        }));
        assert_eq!(Version::parse("  1.2.3-alpha1  "), Ok(Version {
            major: 1,
            minor: 2,
            patch: 3,
            pre: Some(String::from("alpha1")),
            build: None
        }));
        assert_eq!(Version::parse("1.2.3+build5"), Ok(Version {
            major: 1,
            minor: 2,
            patch: 3,
            pre: None,
            build: Some(String::from("build5")),
        }));
        assert_eq!(Version::parse("  1.2.3+build5  "), Ok(Version {
            major: 1,
            minor: 2,
            patch: 3,
            pre: None,
            build: Some(String::from("build5")),
        }));
        assert_eq!(Version::parse("1.2.3-alpha1+build5"), Ok(Version {
            major: 1,
            minor: 2,
            patch: 3,
            pre: Some(String::from("alpha1")),
            build: Some(String::from("build5")),
        }));
        assert_eq!(Version::parse("  1.2.3-alpha1+build5  "), Ok(Version {
            major: 1,
            minor: 2,
            patch: 3,
            pre: Some(String::from("alpha1")),
            build: Some(String::from("build5")),
        }));
        assert_eq!(Version::parse("1.2.3-1.alpha1.9+build5.7.3aedf  "), Ok(Version {
            major: 1,
            minor: 2,
            patch: 3,
            pre: Some(String::from("1.alpha1.9")),
            build: Some(String::from("build5.7.3aedf")),
        }));
        assert_eq!(Version::parse("0.4.0-beta.1+0851523"), Ok(Version {
            major: 0,
            minor: 4,
            patch: 0,
            pre: Some(String::from("beta.1")),
            build: Some(String::from("0851523")),
        }));

    }

    /*
    #[test]
    fn test_increment_patch() {
        let mut buggy_release = Version::parse("0.1.0").unwrap();
        buggy_release.increment_patch();
        assert_eq!(buggy_release, Version::parse("0.1.1").unwrap());
    }

    #[test]
    fn test_increment_minor() {
        let mut feature_release = Version::parse("1.4.6").unwrap();
        feature_release.increment_minor();
        assert_eq!(feature_release, Version::parse("1.5.0").unwrap());
    }

    #[test]
    fn test_increment_major() {
        let mut chrome_release = Version::parse("46.1.246773").unwrap();
        chrome_release.increment_major();
        assert_eq!(chrome_release, Version::parse("47.0.0").unwrap());
    }

    #[test]
    fn test_increment_keep_prerelease() {
        let mut release = Version::parse("1.0.0-alpha").unwrap();
        release.increment_patch();

        assert_eq!(release, Version::parse("1.0.1").unwrap());

        release.increment_minor();

        assert_eq!(release, Version::parse("1.1.0").unwrap());

        release.increment_major();

        assert_eq!(release, Version::parse("2.0.0").unwrap());
    }

    #[test]
    fn test_increment_clear_metadata() {
        let mut release = Version::parse("1.0.0+4442").unwrap();
        release.increment_patch();

        assert_eq!(release, Version::parse("1.0.1").unwrap());
        release = Version::parse("1.0.1+hello").unwrap();

        release.increment_minor();

        assert_eq!(release, Version::parse("1.1.0").unwrap());
        release = Version::parse("1.1.3747+hello").unwrap();

        release.increment_major();

        assert_eq!(release, Version::parse("2.0.0").unwrap());
    }

    #[test]
    fn test_eq() {
        assert_eq!(Version::parse("1.2.3"), Version::parse("1.2.3"));
        assert_eq!(Version::parse("1.2.3-alpha1"), Version::parse("1.2.3-alpha1"));
        assert_eq!(Version::parse("1.2.3+build.42"), Version::parse("1.2.3+build.42"));
        assert_eq!(Version::parse("1.2.3-alpha1+42"), Version::parse("1.2.3-alpha1+42"));
        assert_eq!(Version::parse("1.2.3+23"), Version::parse("1.2.3+42"));
    }

    #[test]
    fn test_ne() {
        assert!(Version::parse("0.0.0")       != Version::parse("0.0.1"));
        assert!(Version::parse("0.0.0")       != Version::parse("0.1.0"));
        assert!(Version::parse("0.0.0")       != Version::parse("1.0.0"));
        assert!(Version::parse("1.2.3-alpha") != Version::parse("1.2.3-beta"));
    }

    #[test]
    fn test_show() {
        assert_eq!(format!("{}", Version::parse("1.2.3").unwrap()),
                   "1.2.3".to_string());
        assert_eq!(format!("{}", Version::parse("1.2.3-alpha1").unwrap()),
                   "1.2.3-alpha1".to_string());
        assert_eq!(format!("{}", Version::parse("1.2.3+build.42").unwrap()),
                   "1.2.3+build.42".to_string());
        assert_eq!(format!("{}", Version::parse("1.2.3-alpha1+42").unwrap()),
                   "1.2.3-alpha1+42".to_string());
    }

    #[test]
    fn test_to_string() {
        assert_eq!(Version::parse("1.2.3").unwrap().to_string(), "1.2.3".to_string());
        assert_eq!(Version::parse("1.2.3-alpha1").unwrap().to_string(), "1.2.3-alpha1".to_string());
        assert_eq!(Version::parse("1.2.3+build.42").unwrap().to_string(), "1.2.3+build.42".to_string());
        assert_eq!(Version::parse("1.2.3-alpha1+42").unwrap().to_string(), "1.2.3-alpha1+42".to_string());
    }

    #[test]
    fn test_lt() {
        assert!(Version::parse("0.0.0")          < Version::parse("1.2.3-alpha2"));
        assert!(Version::parse("1.0.0")          < Version::parse("1.2.3-alpha2"));
        assert!(Version::parse("1.2.0")          < Version::parse("1.2.3-alpha2"));
        assert!(Version::parse("1.2.3-alpha1")   < Version::parse("1.2.3"));
        assert!(Version::parse("1.2.3-alpha1")   < Version::parse("1.2.3-alpha2"));
        assert!(!(Version::parse("1.2.3-alpha2") < Version::parse("1.2.3-alpha2")));
        assert!(!(Version::parse("1.2.3+23")     < Version::parse("1.2.3+42")));
    }

    #[test]
    fn test_le() {
        assert!(Version::parse("0.0.0")        <= Version::parse("1.2.3-alpha2"));
        assert!(Version::parse("1.0.0")        <= Version::parse("1.2.3-alpha2"));
        assert!(Version::parse("1.2.0")        <= Version::parse("1.2.3-alpha2"));
        assert!(Version::parse("1.2.3-alpha1") <= Version::parse("1.2.3-alpha2"));
        assert!(Version::parse("1.2.3-alpha2") <= Version::parse("1.2.3-alpha2"));
        assert!(Version::parse("1.2.3+23")     <= Version::parse("1.2.3+42"));
    }

    #[test]
    fn test_gt() {
        assert!(Version::parse("1.2.3-alpha2")   > Version::parse("0.0.0"));
        assert!(Version::parse("1.2.3-alpha2")   > Version::parse("1.0.0"));
        assert!(Version::parse("1.2.3-alpha2")   > Version::parse("1.2.0"));
        assert!(Version::parse("1.2.3-alpha2")   > Version::parse("1.2.3-alpha1"));
        assert!(Version::parse("1.2.3")          > Version::parse("1.2.3-alpha2"));
        assert!(!(Version::parse("1.2.3-alpha2") > Version::parse("1.2.3-alpha2")));
        assert!(!(Version::parse("1.2.3+23")     > Version::parse("1.2.3+42")));
    }

    #[test]
    fn test_ge() {
        assert!(Version::parse("1.2.3-alpha2") >= Version::parse("0.0.0"));
        assert!(Version::parse("1.2.3-alpha2") >= Version::parse("1.0.0"));
        assert!(Version::parse("1.2.3-alpha2") >= Version::parse("1.2.0"));
        assert!(Version::parse("1.2.3-alpha2") >= Version::parse("1.2.3-alpha1"));
        assert!(Version::parse("1.2.3-alpha2") >= Version::parse("1.2.3-alpha2"));
        assert!(Version::parse("1.2.3+23")     >= Version::parse("1.2.3+42"));
    }

    #[test]
    fn test_prerelease_check() {
        assert!(Version::parse("1.0.0").unwrap().is_prerelease() == false);
        assert!(Version::parse("0.0.1").unwrap().is_prerelease() == false);
        assert!(Version::parse("4.1.4-alpha").unwrap().is_prerelease());
        assert!(Version::parse("1.0.0-beta294296").unwrap().is_prerelease());
    }

    #[test]
    fn test_spec_order() {
        let vs = ["1.0.0-alpha",
                  "1.0.0-alpha.1",
                  "1.0.0-alpha.beta",
                  "1.0.0-beta",
                  "1.0.0-beta.2",
                  "1.0.0-beta.11",
                  "1.0.0-rc.1",
                  "1.0.0"];
        let mut i = 1;
        while i < vs.len() {
            let a = Version::parse(vs[i-1]).unwrap();
            let b = Version::parse(vs[i]).unwrap();
            assert!(a < b);
            i += 1;
        }
    }
    */
}
<|endoftext|>