<commit_before>use cppn_ext::cppn::{Cppn, CppnNode};
use cppn_ext::activation_function::ActivationFunction;
pub use cppn_ext::activation_function::GeometricActivationFunction;
use weight::{Weight, WeightRange, WeightPerturbanceMethod};
use substrate::{Position, SubstrateConfiguration};
use behavioral_bitvec::BehavioralBitvec;
use genome::Genome;
use nsga2::driver::Driver;
use nsga2::population::RatedPopulation;
use fitness::{Fitness, DomainFitness};
use rand::Rng;
use mating::{MatingMethod, MatingMethodWeights};
use prob::Prob;
use network_builder::NetworkBuilder;
use std::marker::PhantomData;

pub type CppnGenome<AF> where AF: ActivationFunction = Genome<CppnNode<AF>>;

const CPPN_OUTPUT_LINK_WEIGHT1: usize = 0;
const CPPN_OUTPUT_LINK_WEIGHT2: usize = 1;
const CPPN_OUTPUT_LINK_EXPRESSION: usize = 2;
const CPPN_OUTPUT_NODE_WEIGHT: usize = 3;

/// Develops a network out of the CPPN
///
/// Returns the BehavioralBitvec and Connection Cost of the developed network

fn develop_cppn<'a, P, AF, T, V>(cppn: &mut Cppn<CppnNode<AF>, Weight, ()>,
                                 substrate_config: &SubstrateConfiguration<'a, P, T>,
                                 visitor: &mut V,
                                 leo_threshold: f64)
                                 -> (BehavioralBitvec, f64)
    where P: Position,
          AF: ActivationFunction,
          V: NetworkBuilder<POS = P, NT = T>
{
    // our CPPN has four outputs: link weight 1, link weight 2, link expression output, node weight
    assert!(cppn.output_count() == 4);
    assert!(cppn.input_count() == 6);

    let nodes = substrate_config.nodes();
    let links = substrate_config.links();
    let null_position = substrate_config.null_position();

    let mut bitvec = BehavioralBitvec::new(4 * (nodes.len() + links.len()));
    let mut connection_cost = 0.0;

    // First visit all nodes

    for node in nodes.iter() {
        let inputs = [node.position.coords(), null_position.coords()];
        cppn.process(&inputs[..]);

        let link_weight1 = cppn.read_output(CPPN_OUTPUT_LINK_WEIGHT1).unwrap();
        let link_weight2 = cppn.read_output(CPPN_OUTPUT_LINK_WEIGHT2).unwrap();
        let link_expression = cppn.read_output(CPPN_OUTPUT_LINK_EXPRESSION).unwrap();
        let node_weight = cppn.read_output(CPPN_OUTPUT_NODE_WEIGHT).unwrap();

        bitvec.push(link_weight1);
        bitvec.push(link_weight2);
        bitvec.push(link_expression);
        bitvec.push(node_weight);

        visitor.add_node(node, node_weight)
    }

    for &(source_node, target_node) in links.iter() {
        let inputs = [source_node.position.coords(), target_node.position.coords()];
        cppn.process(&inputs[..]);

        let link_weight1 = cppn.read_output(CPPN_OUTPUT_LINK_WEIGHT1).unwrap();
        let link_weight2 = cppn.read_output(CPPN_OUTPUT_LINK_WEIGHT2).unwrap();
        let link_expression = cppn.read_output(CPPN_OUTPUT_LINK_EXPRESSION).unwrap();
        let node_weight = cppn.read_output(CPPN_OUTPUT_NODE_WEIGHT).unwrap();

        bitvec.push(link_weight1);
        bitvec.push(link_weight2);
        bitvec.push(link_expression);
        bitvec.push(node_weight);

        if link_expression > leo_threshold {
            let distance_sq = source_node.position.distance_square(&target_node.position);
            debug_assert!(distance_sq >= 0.0);
            connection_cost += distance_sq;
            visitor.add_link(source_node, target_node, link_weight1, link_weight2);
        }
    }

    return (bitvec, connection_cost);
}

/// Determines the domain fitness of `G`

pub struct CppnDriver<'a, DOMFIT, G, P, T, NETBUILDER>
    where DOMFIT: DomainFitness<G> + 'a,
          P: Position + Sync + 'a,
          T: Sync + 'a,
          NETBUILDER: NetworkBuilder<POS = P, NT = T, Output = G> + Sync,
          G: Sync,
{
    pub mating_method_weights: MatingMethodWeights,
    pub activation_functions: Vec<GeometricActivationFunction>,
    pub mutate_element_prob: Prob,
    pub weight_perturbance: WeightPerturbanceMethod,
    pub link_weight_range: WeightRange,

    pub mutate_add_node_random_link_weight: bool,
    pub mutate_drop_node_tournament_k: usize,
    pub mutate_modify_node_tournament_k: usize,

    pub mate_retries: usize,

    pub link_expression_threshold: f64,

    pub substrate_configuration: SubstrateConfiguration<'a, P, T>,
    pub domain_fitness: &'a DOMFIT,
    pub _netbuilder: PhantomData<NETBUILDER>,

    /// Create genomes which are already connected
    pub start_connected: bool,

    /// The initial connections are random within this range
    pub start_link_weight_range: WeightRange,

    /// Create a gaussian seed node for the symmetry of x-axis with the given weight
    pub start_symmetry_x: Option<f64>,

    /// Create a gaussian seed node for the symmetry of y-axis with the given weight
    pub start_symmetry_y: Option<f64>,

    /// Create a gaussian seed node for the symmetry of z-axis with the given weight
    pub start_symmetry_z: Option<f64>,

    /// Create `start_initial_nodes` random nodes for each genome. This can be useful,
    /// as we have a pretty low probability for adding a node.
    pub start_initial_nodes: usize,
}

impl<'a, DOMFIT, G, P, T, NETBUILDER> CppnDriver<'a, DOMFIT, G, P, T, NETBUILDER>
    where DOMFIT: DomainFitness<G>,
          P: Position + Sync + 'a,
          T: Sync + 'a,
          NETBUILDER: NetworkBuilder<POS = P, NT = T, Output = G> + Sync,
          G: Sync,
{
    fn random_hidden_node<R>(&self, rng: &mut R) -> CppnNode<GeometricActivationFunction>
        where R: Rng
    {
        let af = *rng.choose(&self.activation_functions).unwrap();
        CppnNode::hidden(af)
    }
}

impl<'a, DOMFIT, G, P, T, NETBUILDER> Driver for CppnDriver<'a, DOMFIT, G, P, T, NETBUILDER>
    where DOMFIT: DomainFitness<G>,
          G: Sync,
          P: Position + Sync + 'a,
          T: Sync + 'a,
          NETBUILDER: NetworkBuilder<POS = P, NT = T, Output = G> + Sync,
          G: Sync,
{
    type IND = CppnGenome<GeometricActivationFunction>;
    type FIT = Fitness;

    /// Creates a random individual for use by the start generation.
    ///
    /// We start from a minimal topology.
    /// If `self.start_connected` is `true`, we add some initial connections.

    fn random_individual<R>(&self, rng: &mut R) -> Self::IND
        where R: Rng
    {
        let mut genome = Self::IND::new();

        // 6 inputs (x1,y1,z1, x2,y2,z2)
        let inp_x1 = genome.add_node(CppnNode::input(GeometricActivationFunction::Linear));
        let inp_y1 = genome.add_node(CppnNode::input(GeometricActivationFunction::Linear));
        let inp_z1 = genome.add_node(CppnNode::input(GeometricActivationFunction::Linear));
        let inp_x2 = genome.add_node(CppnNode::input(GeometricActivationFunction::Linear));
        let inp_y2 = genome.add_node(CppnNode::input(GeometricActivationFunction::Linear));
        let inp_z2 = genome.add_node(CppnNode::input(GeometricActivationFunction::Linear));

        // 1 bias node (constant input of 1.0)
        let _bias = genome.add_node(CppnNode::bias(GeometricActivationFunction::Constant1));

        // 4 outputs (t,w,ex,r)
        let out_t = genome.add_node(CppnNode::output(GeometricActivationFunction::BipolarGaussian));
        let out_w = genome.add_node(CppnNode::output(GeometricActivationFunction::BipolarGaussian));
        let out_ex = genome.add_node(CppnNode::output(GeometricActivationFunction::Linear));
        let out_r = genome.add_node(CppnNode::output(GeometricActivationFunction::BipolarGaussian));

        // make those nodes above immutable for mutation and crossover, as we need them to
        // develop the CPPN.
        genome.protect_nodes();

        // We use `inputs` and `outputs` only when `self.start_connected` is set to `true`.
        let mut inputs = Vec::new();
        let mut outputs = Vec::new();

        if let Some(w) = self.start_symmetry_x {
            let sym_x = genome.add_node(CppnNode::hidden(GeometricActivationFunction::BipolarGaussian));
            genome.add_link(inp_x1, sym_x, self.link_weight_range.clip_weight(Weight(-w)));
            genome.add_link(inp_x2, sym_x, self.link_weight_range.clip_weight(Weight(w)));

            if self.start_connected {
                inputs.push(sym_x);
            }
        }
        if let Some(w) = self.start_symmetry_y {
            let sym_y = genome.add_node(CppnNode::hidden(GeometricActivationFunction::BipolarGaussian));
            genome.add_link(inp_y1, sym_y, self.link_weight_range.clip_weight(Weight(-w)));
            genome.add_link(inp_y2, sym_y, self.link_weight_range.clip_weight(Weight(w)));

            if self.start_connected {
                inputs.push(sym_y);
            }
        }
        if let Some(w) = self.start_symmetry_z {
            let sym_z = genome.add_node(CppnNode::hidden(GeometricActivationFunction::BipolarGaussian));
            genome.add_link(inp_z1, sym_z, self.link_weight_range.clip_weight(Weight(-w)));
            genome.add_link(inp_z2, sym_z, self.link_weight_range.clip_weight(Weight(w)));

            if self.start_connected {
                inputs.push(sym_z);
            }
        }

        // Make sure that at least every input and every output is connected.
        if self.start_connected {
            inputs.push(inp_x1);
            inputs.push(inp_x2);
            inputs.push(inp_y1);
            inputs.push(inp_y2);
            inputs.push(inp_z1);
            inputs.push(inp_z2);
            outputs.push(out_t);
            outputs.push(out_w);
            outputs.push(out_ex);
            outputs.push(out_r);

            let mut connections: Vec<(usize, usize)> = Vec::new();
            assert!(inputs.len() >= 6);
            assert!(inputs.len() <= 9);
            assert!(outputs.len() == 4);

            // make a connection from every input to a random output
            for (inp, _) in inputs.iter().enumerate() {
                connections.push((inp, rng.gen_range(0, outputs.len())));
            }
            // make a connection from every output to a random input
            for (outp, _) in outputs.iter().enumerate() {
                connections.push((rng.gen_range(0, inputs.len()), outp));
            }
            // remove duplicates
            connections.sort();
            connections.dedup();

            println!("connections: {:?}", connections);

            // and add the connections to the genome
            for (inp, outp) in connections {
                genome.add_link(inputs[inp], outputs[outp], self.start_link_weight_range.random_weight(rng));
            }
        }

        for _ in 0..self.start_initial_nodes {
            let _ = genome.add_node(self.random_hidden_node(rng));
        }

        genome
    }

    fn fitness(&self, ind: &Self::IND) -> Self::FIT {
        let mut cppn = Cppn::new(ind.network());
        let mut net_builder = NETBUILDER::new();

        let (behavioral_bitvec, connection_cost) = develop_cppn(&mut cppn,
                                                                &self.substrate_configuration,
                                                                &mut net_builder,
                                                                self.link_expression_threshold);

        // Evaluate domain specific fitness
        let domain_fitness = self.domain_fitness.fitness(net_builder.network());

        Fitness {
            domain_fitness: domain_fitness,
            behavioral_diversity: 0, // will be calculated in `population_metric`
            connection_cost: connection_cost,
            behavioral_bitvec: behavioral_bitvec,
        }
    }

    fn mate<R>(&self, rng: &mut R, parent1: &Self::IND, parent2: &Self::IND) -> Self::IND
        where R: Rng
    {
        let mut offspring = parent1.clone();

        for _ in 0..self.mate_retries + 1 {
            let modified = match MatingMethod::random_with(&self.mating_method_weights, rng) {
                MatingMethod::MutateAddNode => {
                    let link_weight = if self.mutate_add_node_random_link_weight {
                        Some(self.link_weight_range.random_weight(rng))
                    } else {
                        // duplicate existing node weight
                        None
                    };
                    let hidden_node = self.random_hidden_node(rng);
                    offspring.mutate_add_node(hidden_node, link_weight, rng)
                }
                MatingMethod::MutateDropNode => {
                    offspring.mutate_drop_node(self.mutate_drop_node_tournament_k, rng)
                }
                MatingMethod::MutateModifyNode => {
                    let hidden_node = self.random_hidden_node(rng);
                    offspring.mutate_modify_node(hidden_node,
                                                 self.mutate_modify_node_tournament_k,
                                                 rng)
                }
                MatingMethod::MutateConnect => {
                    let link_weight = self.link_weight_range.random_weight(rng);
                    offspring.mutate_connect(link_weight, rng)
                }
                MatingMethod::MutateDisconnect => offspring.mutate_disconnect(rng),
                MatingMethod::MutateWeights => {
                    let modifications = offspring.mutate_weights(self.mutate_element_prob,
                                                                 &self.weight_perturbance,
                                                                 &self.link_weight_range,
                                                                 rng);
                    modifications != 0
                }
                MatingMethod::CrossoverWeights => {
                    let modifications = offspring.crossover_weights(parent2, rng);
                    modifications != 0
                }
            };

            if modified {
                break;
            }
        }

        warn!("mate(): Genome was NOT modified!");

        return offspring;
    }

    fn population_metric(&self, population: &mut RatedPopulation<Self::IND, Self::FIT>) {
        // Determine the behavioral_diversity as average hamming distance to all other individuals.
        // hamming distance is symmetric.
        let n = population.len();
        for i in 0..n {
            // determine  hehavioral diversity for `i`.
            let mut diversity_i = 0;

            // XXX: parallelize this loop
            for j in i + 1..n {
                let distance = population.fitness()[i]
                                   .behavioral_bitvec
                                   .hamming_distance(&population.fitness()[j].behavioral_bitvec);
                diversity_i += distance;
                population.fitness_mut()[j].behavioral_diversity += distance;
            }

            population.fitness_mut()[i].behavioral_diversity = diversity_i;
        }
    }
}
<commit_msg>Reset all behavioral_diversity values to 0 prior of calculation them.<commit_after>use cppn_ext::cppn::{Cppn, CppnNode};
use cppn_ext::activation_function::ActivationFunction;
pub use cppn_ext::activation_function::GeometricActivationFunction;
use weight::{Weight, WeightRange, WeightPerturbanceMethod};
use substrate::{Position, SubstrateConfiguration};
use behavioral_bitvec::BehavioralBitvec;
use genome::Genome;
use nsga2::driver::Driver;
use nsga2::population::RatedPopulation;
use fitness::{Fitness, DomainFitness};
use rand::Rng;
use mating::{MatingMethod, MatingMethodWeights};
use prob::Prob;
use network_builder::NetworkBuilder;
use std::marker::PhantomData;

pub type CppnGenome<AF> where AF: ActivationFunction = Genome<CppnNode<AF>>;

const CPPN_OUTPUT_LINK_WEIGHT1: usize = 0;
const CPPN_OUTPUT_LINK_WEIGHT2: usize = 1;
const CPPN_OUTPUT_LINK_EXPRESSION: usize = 2;
const CPPN_OUTPUT_NODE_WEIGHT: usize = 3;

/// Develops a network out of the CPPN
///
/// Returns the BehavioralBitvec and Connection Cost of the developed network

fn develop_cppn<'a, P, AF, T, V>(cppn: &mut Cppn<CppnNode<AF>, Weight, ()>,
                                 substrate_config: &SubstrateConfiguration<'a, P, T>,
                                 visitor: &mut V,
                                 leo_threshold: f64)
                                 -> (BehavioralBitvec, f64)
    where P: Position,
          AF: ActivationFunction,
          V: NetworkBuilder<POS = P, NT = T>
{
    // our CPPN has four outputs: link weight 1, link weight 2, link expression output, node weight
    assert!(cppn.output_count() == 4);
    assert!(cppn.input_count() == 6);

    let nodes = substrate_config.nodes();
    let links = substrate_config.links();
    let null_position = substrate_config.null_position();

    let mut bitvec = BehavioralBitvec::new(4 * (nodes.len() + links.len()));
    let mut connection_cost = 0.0;

    // First visit all nodes

    for node in nodes.iter() {
        let inputs = [node.position.coords(), null_position.coords()];
        cppn.process(&inputs[..]);

        let link_weight1 = cppn.read_output(CPPN_OUTPUT_LINK_WEIGHT1).unwrap();
        let link_weight2 = cppn.read_output(CPPN_OUTPUT_LINK_WEIGHT2).unwrap();
        let link_expression = cppn.read_output(CPPN_OUTPUT_LINK_EXPRESSION).unwrap();
        let node_weight = cppn.read_output(CPPN_OUTPUT_NODE_WEIGHT).unwrap();

        bitvec.push(link_weight1);
        bitvec.push(link_weight2);
        bitvec.push(link_expression);
        bitvec.push(node_weight);

        visitor.add_node(node, node_weight)
    }

    for &(source_node, target_node) in links.iter() {
        let inputs = [source_node.position.coords(), target_node.position.coords()];
        cppn.process(&inputs[..]);

        let link_weight1 = cppn.read_output(CPPN_OUTPUT_LINK_WEIGHT1).unwrap();
        let link_weight2 = cppn.read_output(CPPN_OUTPUT_LINK_WEIGHT2).unwrap();
        let link_expression = cppn.read_output(CPPN_OUTPUT_LINK_EXPRESSION).unwrap();
        let node_weight = cppn.read_output(CPPN_OUTPUT_NODE_WEIGHT).unwrap();

        bitvec.push(link_weight1);
        bitvec.push(link_weight2);
        bitvec.push(link_expression);
        bitvec.push(node_weight);

        if link_expression > leo_threshold {
            let distance_sq = source_node.position.distance_square(&target_node.position);
            debug_assert!(distance_sq >= 0.0);
            connection_cost += distance_sq;
            visitor.add_link(source_node, target_node, link_weight1, link_weight2);
        }
    }

    return (bitvec, connection_cost);
}

/// Determines the domain fitness of `G`

pub struct CppnDriver<'a, DOMFIT, G, P, T, NETBUILDER>
    where DOMFIT: DomainFitness<G> + 'a,
          P: Position + Sync + 'a,
          T: Sync + 'a,
          NETBUILDER: NetworkBuilder<POS = P, NT = T, Output = G> + Sync,
          G: Sync,
{
    pub mating_method_weights: MatingMethodWeights,
    pub activation_functions: Vec<GeometricActivationFunction>,
    pub mutate_element_prob: Prob,
    pub weight_perturbance: WeightPerturbanceMethod,
    pub link_weight_range: WeightRange,

    pub mutate_add_node_random_link_weight: bool,
    pub mutate_drop_node_tournament_k: usize,
    pub mutate_modify_node_tournament_k: usize,

    pub mate_retries: usize,

    pub link_expression_threshold: f64,

    pub substrate_configuration: SubstrateConfiguration<'a, P, T>,
    pub domain_fitness: &'a DOMFIT,
    pub _netbuilder: PhantomData<NETBUILDER>,

    /// Create genomes which are already connected
    pub start_connected: bool,

    /// The initial connections are random within this range
    pub start_link_weight_range: WeightRange,

    /// Create a gaussian seed node for the symmetry of x-axis with the given weight
    pub start_symmetry_x: Option<f64>,

    /// Create a gaussian seed node for the symmetry of y-axis with the given weight
    pub start_symmetry_y: Option<f64>,

    /// Create a gaussian seed node for the symmetry of z-axis with the given weight
    pub start_symmetry_z: Option<f64>,

    /// Create `start_initial_nodes` random nodes for each genome. This can be useful,
    /// as we have a pretty low probability for adding a node.
    pub start_initial_nodes: usize,
}

impl<'a, DOMFIT, G, P, T, NETBUILDER> CppnDriver<'a, DOMFIT, G, P, T, NETBUILDER>
    where DOMFIT: DomainFitness<G>,
          P: Position + Sync + 'a,
          T: Sync + 'a,
          NETBUILDER: NetworkBuilder<POS = P, NT = T, Output = G> + Sync,
          G: Sync,
{
    fn random_hidden_node<R>(&self, rng: &mut R) -> CppnNode<GeometricActivationFunction>
        where R: Rng
    {
        let af = *rng.choose(&self.activation_functions).unwrap();
        CppnNode::hidden(af)
    }
}

impl<'a, DOMFIT, G, P, T, NETBUILDER> Driver for CppnDriver<'a, DOMFIT, G, P, T, NETBUILDER>
    where DOMFIT: DomainFitness<G>,
          G: Sync,
          P: Position + Sync + 'a,
          T: Sync + 'a,
          NETBUILDER: NetworkBuilder<POS = P, NT = T, Output = G> + Sync,
          G: Sync,
{
    type IND = CppnGenome<GeometricActivationFunction>;
    type FIT = Fitness;

    /// Creates a random individual for use by the start generation.
    ///
    /// We start from a minimal topology.
    /// If `self.start_connected` is `true`, we add some initial connections.

    fn random_individual<R>(&self, rng: &mut R) -> Self::IND
        where R: Rng
    {
        let mut genome = Self::IND::new();

        // 6 inputs (x1,y1,z1, x2,y2,z2)
        let inp_x1 = genome.add_node(CppnNode::input(GeometricActivationFunction::Linear));
        let inp_y1 = genome.add_node(CppnNode::input(GeometricActivationFunction::Linear));
        let inp_z1 = genome.add_node(CppnNode::input(GeometricActivationFunction::Linear));
        let inp_x2 = genome.add_node(CppnNode::input(GeometricActivationFunction::Linear));
        let inp_y2 = genome.add_node(CppnNode::input(GeometricActivationFunction::Linear));
        let inp_z2 = genome.add_node(CppnNode::input(GeometricActivationFunction::Linear));

        // 1 bias node (constant input of 1.0)
        let _bias = genome.add_node(CppnNode::bias(GeometricActivationFunction::Constant1));

        // 4 outputs (t,w,ex,r)
        let out_t = genome.add_node(CppnNode::output(GeometricActivationFunction::BipolarGaussian));
        let out_w = genome.add_node(CppnNode::output(GeometricActivationFunction::BipolarGaussian));
        let out_ex = genome.add_node(CppnNode::output(GeometricActivationFunction::Linear));
        let out_r = genome.add_node(CppnNode::output(GeometricActivationFunction::BipolarGaussian));

        // make those nodes above immutable for mutation and crossover, as we need them to
        // develop the CPPN.
        genome.protect_nodes();

        // We use `inputs` and `outputs` only when `self.start_connected` is set to `true`.
        let mut inputs = Vec::new();
        let mut outputs = Vec::new();

        if let Some(w) = self.start_symmetry_x {
            let sym_x = genome.add_node(CppnNode::hidden(GeometricActivationFunction::BipolarGaussian));
            genome.add_link(inp_x1, sym_x, self.link_weight_range.clip_weight(Weight(-w)));
            genome.add_link(inp_x2, sym_x, self.link_weight_range.clip_weight(Weight(w)));

            if self.start_connected {
                inputs.push(sym_x);
            }
        }
        if let Some(w) = self.start_symmetry_y {
            let sym_y = genome.add_node(CppnNode::hidden(GeometricActivationFunction::BipolarGaussian));
            genome.add_link(inp_y1, sym_y, self.link_weight_range.clip_weight(Weight(-w)));
            genome.add_link(inp_y2, sym_y, self.link_weight_range.clip_weight(Weight(w)));

            if self.start_connected {
                inputs.push(sym_y);
            }
        }
        if let Some(w) = self.start_symmetry_z {
            let sym_z = genome.add_node(CppnNode::hidden(GeometricActivationFunction::BipolarGaussian));
            genome.add_link(inp_z1, sym_z, self.link_weight_range.clip_weight(Weight(-w)));
            genome.add_link(inp_z2, sym_z, self.link_weight_range.clip_weight(Weight(w)));

            if self.start_connected {
                inputs.push(sym_z);
            }
        }

        // Make sure that at least every input and every output is connected.
        if self.start_connected {
            inputs.push(inp_x1);
            inputs.push(inp_x2);
            inputs.push(inp_y1);
            inputs.push(inp_y2);
            inputs.push(inp_z1);
            inputs.push(inp_z2);
            outputs.push(out_t);
            outputs.push(out_w);
            outputs.push(out_ex);
            outputs.push(out_r);

            let mut connections: Vec<(usize, usize)> = Vec::new();
            assert!(inputs.len() >= 6);
            assert!(inputs.len() <= 9);
            assert!(outputs.len() == 4);

            // make a connection from every input to a random output
            for (inp, _) in inputs.iter().enumerate() {
                connections.push((inp, rng.gen_range(0, outputs.len())));
            }
            // make a connection from every output to a random input
            for (outp, _) in outputs.iter().enumerate() {
                connections.push((rng.gen_range(0, inputs.len()), outp));
            }
            // remove duplicates
            connections.sort();
            connections.dedup();

            println!("connections: {:?}", connections);

            // and add the connections to the genome
            for (inp, outp) in connections {
                genome.add_link(inputs[inp], outputs[outp], self.start_link_weight_range.random_weight(rng));
            }
        }

        for _ in 0..self.start_initial_nodes {
            let _ = genome.add_node(self.random_hidden_node(rng));
        }

        genome
    }

    fn fitness(&self, ind: &Self::IND) -> Self::FIT {
        let mut cppn = Cppn::new(ind.network());
        let mut net_builder = NETBUILDER::new();

        let (behavioral_bitvec, connection_cost) = develop_cppn(&mut cppn,
                                                                &self.substrate_configuration,
                                                                &mut net_builder,
                                                                self.link_expression_threshold);

        // Evaluate domain specific fitness
        let domain_fitness = self.domain_fitness.fitness(net_builder.network());

        Fitness {
            domain_fitness: domain_fitness,
            behavioral_diversity: 0, // will be calculated in `population_metric`
            connection_cost: connection_cost,
            behavioral_bitvec: behavioral_bitvec,
        }
    }

    fn mate<R>(&self, rng: &mut R, parent1: &Self::IND, parent2: &Self::IND) -> Self::IND
        where R: Rng
    {
        let mut offspring = parent1.clone();

        for _ in 0..self.mate_retries + 1 {
            let modified = match MatingMethod::random_with(&self.mating_method_weights, rng) {
                MatingMethod::MutateAddNode => {
                    let link_weight = if self.mutate_add_node_random_link_weight {
                        Some(self.link_weight_range.random_weight(rng))
                    } else {
                        // duplicate existing node weight
                        None
                    };
                    let hidden_node = self.random_hidden_node(rng);
                    offspring.mutate_add_node(hidden_node, link_weight, rng)
                }
                MatingMethod::MutateDropNode => {
                    offspring.mutate_drop_node(self.mutate_drop_node_tournament_k, rng)
                }
                MatingMethod::MutateModifyNode => {
                    let hidden_node = self.random_hidden_node(rng);
                    offspring.mutate_modify_node(hidden_node,
                                                 self.mutate_modify_node_tournament_k,
                                                 rng)
                }
                MatingMethod::MutateConnect => {
                    let link_weight = self.link_weight_range.random_weight(rng);
                    offspring.mutate_connect(link_weight, rng)
                }
                MatingMethod::MutateDisconnect => offspring.mutate_disconnect(rng),
                MatingMethod::MutateWeights => {
                    let modifications = offspring.mutate_weights(self.mutate_element_prob,
                                                                 &self.weight_perturbance,
                                                                 &self.link_weight_range,
                                                                 rng);
                    modifications != 0
                }
                MatingMethod::CrossoverWeights => {
                    let modifications = offspring.crossover_weights(parent2, rng);
                    modifications != 0
                }
            };

            if modified {
                break;
            }
        }

        warn!("mate(): Genome was NOT modified!");

        return offspring;
    }

    fn population_metric(&self, population: &mut RatedPopulation<Self::IND, Self::FIT>) {
        // Determine the behavioral_diversity as average hamming distance to all other individuals.
        // hamming distance is symmetric.

        let n = population.len();

        // reset all behavioral_diversity values to 0
        for i in 0..n {
            population.fitness_mut()[i].behavioral_diversity = 0;
        }

        for i in 0..n {
            // determine  hehavioral diversity for `i`.
            let mut diversity_i = 0;

            // XXX: parallelize this loop
            for j in i + 1..n {
                let distance = population.fitness()[i]
                                   .behavioral_bitvec
                                   .hamming_distance(&population.fitness()[j].behavioral_bitvec);
                diversity_i += distance;
                population.fitness_mut()[j].behavioral_diversity += distance;
            }

            population.fitness_mut()[i].behavioral_diversity = diversity_i;
        }
    }
}
<|endoftext|>


<commit_before>use glob::{MatchOptions, Pattern};
use std::fs::File;
use std::io::BufReader;
use std::io::prelude::*;
use std::path::PathBuf;

// ---------------------------------------------------------------------------------------------------------------------
// Ignore
// ---------------------------------------------------------------------------------------------------------------------

pub trait Ignore {
    fn check_dir ( &self, path: &PathBuf ) -> bool;
    fn check_file( &self, path: &PathBuf ) -> bool;
}

// ---------------------------------------------------------------------------------------------------------------------
// IgnoreVcs
// ---------------------------------------------------------------------------------------------------------------------

pub struct IgnoreVcs {
    vcs_dirs: Vec<String>,
}

impl IgnoreVcs {
    pub fn new() -> Self {
        IgnoreVcs {
            vcs_dirs: vec![
                ".svn".to_string(),
                ".hg" .to_string(),
                ".git".to_string(),
                ".bzr".to_string(),
            ],
        }
    }
}

impl Ignore for IgnoreVcs {

    fn check_dir ( &self, path: &PathBuf ) -> bool {
        for d in &self.vcs_dirs {
            if path.ends_with( d ) {
                return false
            }
        }
        true
    }

    fn check_file( &self, _path: &PathBuf ) -> bool {
        true
    }
}

// ---------------------------------------------------------------------------------------------------------------------
// IgnoreGit
// ---------------------------------------------------------------------------------------------------------------------

pub struct IgnoreGitPat {
    pat : Pattern,
    head: u8     ,
    tail: u8     ,
}

pub struct IgnoreGit {
    file_name: Vec<IgnoreGitPat>,
    file_path: Vec<IgnoreGitPat>,
    dir_name : Vec<IgnoreGitPat>,
    dir_path : Vec<IgnoreGitPat>,
    opt      : MatchOptions,
}

impl IgnoreGit {
    pub fn new( path: &PathBuf ) -> Self {
        let ( f_name, f_path, d_name, d_path ) = IgnoreGit::parse( &path );
        IgnoreGit {
            file_name: f_name,
            file_path: f_path,
            dir_name : d_name,
            dir_path : d_path,
            opt      : MatchOptions {
                case_sensitive             : true,
                require_literal_separator  : true,
                require_literal_leading_dot: true,
            },
        }
    }

    fn parse( path: &PathBuf ) -> ( Vec<IgnoreGitPat>, Vec<IgnoreGitPat>, Vec<IgnoreGitPat>, Vec<IgnoreGitPat> ) {
        let mut file_name = Vec::new();
        let mut file_path = Vec::new();
        let mut dir_name  = Vec::new();
        let mut dir_path  = Vec::new();

        let f = if let Ok( x ) = File::open( &path ) {
            x
        } else {
            return ( file_name, file_path, dir_name, dir_path )
        };
        let f = BufReader::new( f );

        let base = path.parent().unwrap().to_string_lossy();

        for line in f.lines() {
            let s = line.unwrap();
            let s = s.trim().to_string();

            if s == "" || s.starts_with( "#" ) {
                continue;
            } else if s.starts_with( "!" ) {
                // not yet implemented
            } else if !s.contains( "/" ) {
                if let Ok( x ) = Pattern::new( &s ) {
                    let ( head, tail ) = IgnoreGit::extract_fix_pat( &s );
                    file_name.push( IgnoreGitPat{ pat: x.clone(), head: head.clone(), tail: tail.clone() } );
                    dir_name .push( IgnoreGitPat{ pat: x        , head: head.clone(), tail: tail.clone() } )
                }
            } else if s.ends_with( "/" ) && s.find( "/" ).unwrap() < s.len() - 1 {
                let p = IgnoreGit::concat_path( &base, &s );
                if let Ok( x ) = Pattern::new( &p ) {
                    let ( head, tail ) = IgnoreGit::extract_fix_pat( &p );
                    dir_path.push( IgnoreGitPat{ pat: x, head: head.clone(), tail: tail.clone() } )
                }
            } else if s.ends_with( "/" ) {
                let p = IgnoreGit::normalize( &s );
                if let Ok( x ) = Pattern::new( &p ) {
                    let ( head, tail ) = IgnoreGit::extract_fix_pat( &p );
                    dir_name.push( IgnoreGitPat{ pat: x, head: head.clone(), tail: tail.clone() } )
                }
            } else {
                let p = IgnoreGit::concat_path( &base, &s );
                if let Ok( x ) = Pattern::new( &p ) {
                    let ( head, tail ) = IgnoreGit::extract_fix_pat( &p );
                    file_path.push( IgnoreGitPat{ pat: x.clone(), head: head.clone(), tail: tail.clone() } );
                    dir_path .push( IgnoreGitPat{ pat: x        , head: head.clone(), tail: tail.clone() } )
                }
            }
        }

        ( file_name, file_path, dir_name, dir_path )
    }

    fn concat_path( s0: &str, s1: &str ) -> String {
        let ret = if s1.starts_with( "/" ) {
            format!( "{}{}" , s0, s1 )
        } else {
            format!( "{}/{}", s0, s1 )
        };
        IgnoreGit::normalize( &ret )
    }

    fn normalize( s: &str ) -> String {
        if s.ends_with( "/" ) {
            let mut s2 = String::from( s );
            s2.truncate( s.len() - 1 );
            s2
        } else {
            String::from( s )
        }
    }

    fn extract_fix_pat( p: &str ) -> ( u8, u8 ) {
        let len = p.len();

        let mut head_check = !p.starts_with( "\\" );
        head_check        &= !p.starts_with( "*"  );
        head_check        &= !p.starts_with( "?"  );
        head_check        &= !p.starts_with( "["  );

        let mut tail_check = !p.ends_with( "*" );
        tail_check        &= !p.ends_with( "?" );
        tail_check        &= !p.ends_with( "]" );

        let head = if head_check {
            p.as_bytes()[0]
        } else {
            0
        };

        let tail = if tail_check {
            p.as_bytes()[len-1]
        } else {
            0
        };

        ( head, tail )
    }
}

impl Ignore for IgnoreGit {

    fn check_dir ( &self, path: &PathBuf ) -> bool {

        let path_str = path.to_string_lossy();
        let name_str = if let Some( x ) = path.file_name() {
            x.to_string_lossy()
        } else {
            return true
        };

        let name_ptr = name_str.as_bytes().as_ptr();
        let path_ptr = path_str.as_bytes().as_ptr();
        let name_end = ( name_str.len() - 1 ) as isize;
        let path_end = ( path_str.len() - 1 ) as isize;

        for p in &self.dir_name {
            unsafe {
                if ( p.head == 0 ) || ( *name_ptr != p.head ) {
                    continue;
                }
                if ( p.tail == 0 ) || ( *name_ptr.offset( name_end ) != p.tail ) {
                    continue;
                }
            }
            //if !name_str.starts_with( &p.head ) || !name_str.ends_with( &p.tail ) {
            //    continue;
            //}
            if p.pat.matches_with( &name_str, &self.opt ) {
                return false
            }
        }

        for p in &self.dir_path {
            unsafe {
                if ( p.head == 0 ) || ( *path_ptr != p.head ) {
                    continue;
                }
                if ( p.tail == 0 ) || ( *path_ptr.offset( path_end ) != p.tail ) {
                    continue;
                }
            }
            //if !path_str.starts_with( &p.head ) || !path_str.ends_with( &p.tail ) {
            //    continue;
            //}
            if p.pat.matches_with( &path_str, &self.opt ) {
                return false
            }
        }

        true
    }

    fn check_file( &self, path: &PathBuf ) -> bool {

        let path_str = path.to_string_lossy();
        let name_str = if let Some( x ) = path.file_name() {
            x.to_string_lossy()
        } else {
            return true
        };

        let name_ptr = name_str.as_bytes().as_ptr();
        let path_ptr = path_str.as_bytes().as_ptr();
        let name_end = ( name_str.len() - 1 ) as isize;
        let path_end = ( path_str.len() - 1 ) as isize;

        for p in &self.file_name {
            unsafe {
                if ( p.head == 0 ) || ( *name_ptr != p.head ) {
                    continue;
                }
                if ( p.tail == 0 ) || ( *name_ptr.offset( name_end ) != p.tail ) {
                    continue;
                }
            }
            //if !name_str.starts_with( &p.head ) || !name_str.ends_with( &p.tail ) {
            //    continue;
            //}
            if p.pat.matches_with( &name_str, &self.opt ) {
                return false
            }
        }

        for p in &self.file_path {
            unsafe {
                if ( p.head == 0 ) || ( *path_ptr != p.head ) {
                    continue;
                }
                if ( p.tail == 0 ) || ( *path_ptr.offset( path_end ) != p.tail ) {
                    continue;
                }
            }
            //if !path_str.starts_with( &p.head ) || !path_str.ends_with( &p.tail ) {
            //    continue;
            //}
            if p.pat.matches_with( &path_str, &self.opt ) {
                return false
            }
        }

        true
    }
}

// ---------------------------------------------------------------------------------------------------------------------
// Test
// ---------------------------------------------------------------------------------------------------------------------

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

    #[test]
    fn ignore_git() {
        let ignore = IgnoreGit::new( &PathBuf::from( "./test/.gitignore" ) );

        assert!(  ignore.check_file( &PathBuf::from( "./test/ao"          ) ) );
        assert!( !ignore.check_file( &PathBuf::from( "./test/a.o"         ) ) );
        assert!( !ignore.check_file( &PathBuf::from( "./test/abc.o"       ) ) );
        assert!( !ignore.check_file( &PathBuf::from( "./test/a.s"         ) ) );
        assert!(  ignore.check_file( &PathBuf::from( "./test/abc.s"       ) ) );
        assert!( !ignore.check_file( &PathBuf::from( "./test/d0.t"        ) ) );
        assert!(  ignore.check_file( &PathBuf::from( "./test/d00.t"       ) ) );
        assert!( !ignore.check_file( &PathBuf::from( "./test/file"        ) ) );
        assert!( !ignore.check_file( &PathBuf::from( "./test/dir0/file"   ) ) );
        assert!( !ignore.check_file( &PathBuf::from( "./test/dir1/file"   ) ) );
        assert!(  ignore.check_file( &PathBuf::from( "./test/x/file"      ) ) );
        assert!(  ignore.check_file( &PathBuf::from( "./test/x/dir0/file" ) ) );
        assert!(  ignore.check_file( &PathBuf::from( "./test/x/dir1/file" ) ) );
        assert!( !ignore.check_dir ( &PathBuf::from( "./test/dir2"        ) ) );
        assert!( !ignore.check_dir ( &PathBuf::from( "./test/dir3/dir4"   ) ) );
        assert!( !ignore.check_dir ( &PathBuf::from( "./test/dir5/dir6"   ) ) );
        assert!( !ignore.check_dir ( &PathBuf::from( "./test/dir7"        ) ) );
        assert!( !ignore.check_dir ( &PathBuf::from( "./test/dir3/dir7"   ) ) );
        assert!( !ignore.check_dir ( &PathBuf::from( "./test/dir8"        ) ) );
        assert!( !ignore.check_dir ( &PathBuf::from( "./test/dir9/dir10"  ) ) );
        assert!( !ignore.check_dir ( &PathBuf::from( "./test/dir11/dir12" ) ) );
    }
}

<commit_msg>fix IgnoreGit<commit_after>use glob::{MatchOptions, Pattern};
use std::fs::File;
use std::io::BufReader;
use std::io::prelude::*;
use std::path::PathBuf;

// ---------------------------------------------------------------------------------------------------------------------
// Ignore
// ---------------------------------------------------------------------------------------------------------------------

pub trait Ignore {
    fn check_dir ( &self, path: &PathBuf ) -> bool;
    fn check_file( &self, path: &PathBuf ) -> bool;
}

// ---------------------------------------------------------------------------------------------------------------------
// IgnoreVcs
// ---------------------------------------------------------------------------------------------------------------------

pub struct IgnoreVcs {
    vcs_dirs: Vec<String>,
}

impl IgnoreVcs {
    pub fn new() -> Self {
        IgnoreVcs {
            vcs_dirs: vec![
                ".svn".to_string(),
                ".hg" .to_string(),
                ".git".to_string(),
                ".bzr".to_string(),
            ],
        }
    }
}

impl Ignore for IgnoreVcs {

    fn check_dir ( &self, path: &PathBuf ) -> bool {
        for d in &self.vcs_dirs {
            if path.ends_with( d ) {
                return false
            }
        }
        true
    }

    fn check_file( &self, _path: &PathBuf ) -> bool {
        true
    }
}

// ---------------------------------------------------------------------------------------------------------------------
// IgnoreGit
// ---------------------------------------------------------------------------------------------------------------------

pub struct IgnoreGitPat {
    pat : Pattern,
    head: u8     ,
    tail: u8     ,
}

pub struct IgnoreGit {
    file_name: Vec<IgnoreGitPat>,
    file_path: Vec<IgnoreGitPat>,
    dir_name : Vec<IgnoreGitPat>,
    dir_path : Vec<IgnoreGitPat>,
    opt      : MatchOptions,
}

impl IgnoreGit {
    pub fn new( path: &PathBuf ) -> Self {
        let ( f_name, f_path, d_name, d_path ) = IgnoreGit::parse( &path );
        IgnoreGit {
            file_name: f_name,
            file_path: f_path,
            dir_name : d_name,
            dir_path : d_path,
            opt      : MatchOptions {
                case_sensitive             : true,
                require_literal_separator  : true,
                require_literal_leading_dot: true,
            },
        }
    }

    fn parse( path: &PathBuf ) -> ( Vec<IgnoreGitPat>, Vec<IgnoreGitPat>, Vec<IgnoreGitPat>, Vec<IgnoreGitPat> ) {
        let mut file_name = Vec::new();
        let mut file_path = Vec::new();
        let mut dir_name  = Vec::new();
        let mut dir_path  = Vec::new();

        let f = if let Ok( x ) = File::open( &path ) {
            x
        } else {
            return ( file_name, file_path, dir_name, dir_path )
        };
        let f = BufReader::new( f );

        let base = path.parent().unwrap().to_string_lossy();

        for line in f.lines() {
            let s = line.unwrap();
            let s = s.trim().to_string();

            if s == "" || s.starts_with( "#" ) {
                continue;
            } else if s.starts_with( "!" ) {
                // not yet implemented
            } else if !s.contains( "/" ) {
                if let Ok( x ) = Pattern::new( &s ) {
                    let ( head, tail ) = IgnoreGit::extract_fix_pat( &s );
                    file_name.push( IgnoreGitPat{ pat: x.clone(), head: head.clone(), tail: tail.clone() } );
                    dir_name .push( IgnoreGitPat{ pat: x        , head: head.clone(), tail: tail.clone() } )
                }
            } else if s.ends_with( "/" ) && s.find( "/" ).unwrap() < s.len() - 1 {
                let p = IgnoreGit::concat_path( &base, &s );
                if let Ok( x ) = Pattern::new( &p ) {
                    let ( head, tail ) = IgnoreGit::extract_fix_pat( &p );
                    dir_path.push( IgnoreGitPat{ pat: x, head: head.clone(), tail: tail.clone() } )
                }
            } else if s.ends_with( "/" ) {
                let p = IgnoreGit::normalize( &s );
                if let Ok( x ) = Pattern::new( &p ) {
                    let ( head, tail ) = IgnoreGit::extract_fix_pat( &p );
                    dir_name.push( IgnoreGitPat{ pat: x, head: head.clone(), tail: tail.clone() } )
                }
            } else {
                let p = IgnoreGit::concat_path( &base, &s );
                if let Ok( x ) = Pattern::new( &p ) {
                    let ( head, tail ) = IgnoreGit::extract_fix_pat( &p );
                    file_path.push( IgnoreGitPat{ pat: x.clone(), head: head.clone(), tail: tail.clone() } );
                    dir_path .push( IgnoreGitPat{ pat: x        , head: head.clone(), tail: tail.clone() } )
                }
            }
        }

        ( file_name, file_path, dir_name, dir_path )
    }

    fn concat_path( s0: &str, s1: &str ) -> String {
        let ret = if s1.starts_with( "/" ) {
            format!( "{}{}" , s0, s1 )
        } else {
            format!( "{}/{}", s0, s1 )
        };
        IgnoreGit::normalize( &ret )
    }

    fn normalize( s: &str ) -> String {
        if s.ends_with( "/" ) {
            let mut s2 = String::from( s );
            s2.truncate( s.len() - 1 );
            s2
        } else {
            String::from( s )
        }
    }

    fn extract_fix_pat( p: &str ) -> ( u8, u8 ) {
        let len = p.len();

        let mut head_check = !p.starts_with( "\\" );
        head_check        &= !p.starts_with( "*"  );
        head_check        &= !p.starts_with( "?"  );
        head_check        &= !p.starts_with( "["  );

        let mut tail_check = !p.ends_with( "*" );
        tail_check        &= !p.ends_with( "?" );
        tail_check        &= !p.ends_with( "]" );

        let head = if head_check {
            p.as_bytes()[0]
        } else {
            0
        };

        let tail = if tail_check {
            p.as_bytes()[len-1]
        } else {
            0
        };

        ( head, tail )
    }

    fn check ( &self, path: &PathBuf, names: &Vec<IgnoreGitPat>, paths: &Vec<IgnoreGitPat> ) -> bool {

        let path_str = path.to_string_lossy();
        let name_str = if let Some( x ) = path.file_name() {
            x.to_string_lossy()
        } else {
            return true
        };

        let name_ptr = name_str.as_bytes().as_ptr();
        let path_ptr = path_str.as_bytes().as_ptr();
        let name_end = ( name_str.len() - 1 ) as isize;
        let path_end = ( path_str.len() - 1 ) as isize;

        for p in names {
            unsafe {
                if ( p.head != 0 ) && ( *name_ptr != p.head ) {
                    continue;
                }
                if ( p.tail != 0 ) && ( *name_ptr.offset( name_end ) != p.tail ) {
                    continue;
                }
            }
            if p.pat.matches_with( &name_str, &self.opt ) {
                return false
            }
        }

        for p in paths {
            unsafe {
                if ( p.head != 0 ) && ( *path_ptr != p.head ) {
                    continue;
                }
                if ( p.tail != 0 ) && ( *path_ptr.offset( path_end ) != p.tail ) {
                    continue;
                }
            }
            if p.pat.matches_with( &path_str, &self.opt ) {
                return false
            }
        }

        true
    }
}

impl Ignore for IgnoreGit {

    fn check_dir ( &self, path: &PathBuf ) -> bool {
        self.check( path, &self.dir_name, &self.dir_path )
    }

    fn check_file( &self, path: &PathBuf ) -> bool {
        self.check( path, &self.file_name, &self.file_path )
    }
}

// ---------------------------------------------------------------------------------------------------------------------
// Test
// ---------------------------------------------------------------------------------------------------------------------

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

    #[test]
    fn ignore_git() {
        let ignore = IgnoreGit::new( &PathBuf::from( "./test/.gitignore" ) );

        assert!(  ignore.check_file( &PathBuf::from( "./test/ao"          ) ) );
        assert!( !ignore.check_file( &PathBuf::from( "./test/a.o"         ) ) );
        assert!( !ignore.check_file( &PathBuf::from( "./test/abc.o"       ) ) );
        assert!( !ignore.check_file( &PathBuf::from( "./test/a.s"         ) ) );
        assert!(  ignore.check_file( &PathBuf::from( "./test/abc.s"       ) ) );
        assert!( !ignore.check_file( &PathBuf::from( "./test/d0.t"        ) ) );
        assert!(  ignore.check_file( &PathBuf::from( "./test/d00.t"       ) ) );
        assert!( !ignore.check_file( &PathBuf::from( "./test/file"        ) ) );
        assert!( !ignore.check_file( &PathBuf::from( "./test/dir0/file"   ) ) );
        assert!( !ignore.check_file( &PathBuf::from( "./test/dir1/file"   ) ) );
        assert!(  ignore.check_file( &PathBuf::from( "./test/x/file"      ) ) );
        assert!(  ignore.check_file( &PathBuf::from( "./test/x/dir0/file" ) ) );
        assert!(  ignore.check_file( &PathBuf::from( "./test/x/dir1/file" ) ) );
        assert!( !ignore.check_dir ( &PathBuf::from( "./test/dir2"        ) ) );
        assert!( !ignore.check_dir ( &PathBuf::from( "./test/dir3/dir4"   ) ) );
        assert!( !ignore.check_dir ( &PathBuf::from( "./test/dir5/dir6"   ) ) );
        assert!( !ignore.check_dir ( &PathBuf::from( "./test/dir7"        ) ) );
        assert!( !ignore.check_dir ( &PathBuf::from( "./test/dir3/dir7"   ) ) );
        assert!( !ignore.check_dir ( &PathBuf::from( "./test/dir8"        ) ) );
        assert!( !ignore.check_dir ( &PathBuf::from( "./test/dir9/dir10"  ) ) );
        assert!( !ignore.check_dir ( &PathBuf::from( "./test/dir11/dir12" ) ) );
    }
}

<|endoftext|>


<commit_before>/// The Toml Insert extensions

use toml::Value;

use tokenizer::Token;
use tokenizer::tokenize_with_seperator;
use error::*;

pub trait TomlValueInsertExt {

    /// Extension function for inserting a value in the current toml::Value document
    /// using a custom seperator.
    ///
    /// For difference to TomlSetExt::set() and friends, read [#semantics].
    ///
    /// # Semantics
    ///
    /// The function automatically creates intermediate data structures based on the query string.
    /// That means, if the query string is `"a.b.c.[0]"`, but only a table `"a"` exists in the
    /// document, the function automatically creates a table `"b"` inside `"a"` and `"c"` inside
    /// `"b"`, and an array in `"c"`. The array index is ignored if the array is created.
    ///
    /// If an Array exists, but the specified index is larger than the last index, the array will
    /// be expanded by one element: If the array has a length of 3, but the query string specifies
    /// that the element should be put at 1000, the function ignores the large index and simply
    /// appends the value to the index.
    ///
    /// # Return value
    ///
    /// If the insert operation worked correctly, `Ok(None)` is returned.
    /// If the insert operation replaced an existing value `Ok(Some(old_value))` is returned
    /// On failure, `Err(e)` is returned
    ///
    fn insert_with_seperator(&mut self, query: &String, sep: char, value: Value) -> Result<Option<Value>>;

    /// Extension function for inserting a value from the current toml::Value document
    ///
    /// See documentation of `TomlValueinsertExt::insert_with_seperator`
    fn insert(&mut self, query: &String, value: Value) -> Result<Option<Value>> {
        self.insert_with_seperator(query, '.', value)
    }

}

impl TomlValueInsertExt for Value {

    fn insert_with_seperator(&mut self, query: &String, sep: char, value: Value) -> Result<Option<Value>> {
        use resolver::mut_resolver::resolve;

        let mut tokens = try!(tokenize_with_seperator(query, sep));
        let last       = tokens.pop_last().unwrap();
        let mut val    = try!(resolve(self, &tokens));

        match *last {
            Token::Identifier { ident, .. } => {
                match val {
                    &mut Value::Table(ref mut t) => {
                        Ok(t.insert(ident, value))
                    },
                    _ => Err(Error::from(ErrorKind::NoIdentifierInArray(ident.clone())))
                }
            },

            Token::Index { idx , .. } => {
                match val {
                    &mut Value::Array(ref mut a) => {
                        if a.len() > idx {
                            unimplemented!()
                        } else {
                            a.push(value);
                            Ok(None)
                        }
                    },
                    _ => Err(Error::from(ErrorKind::NoIndexInTable(idx)))
                }
            },
        }
    }

}

#[cfg(test)]
mod test {
    use super::*;
    use error::*;
    use toml::Value;
    use toml::from_str as toml_from_str;

    #[test]
    fn test_insert_with_seperator_into_table() {
        let mut toml : Value = toml_from_str(r#"
        [table]
        "#).unwrap();

        let res = toml.insert_with_seperator(&String::from("table.a"), '.', Value::Integer(1));

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_none());

        assert!(is_match!(toml, Value::Table(_)));
        match toml {
            Value::Table(ref t) => {
                assert!(!t.is_empty());

                let table = t.get("table");
                assert!(table.is_some());

                let table = table.unwrap();
                assert!(is_match!(table, &Value::Table(_)));
                match table {
                    &Value::Table(ref t) => {
                        assert!(!t.is_empty());

                        let a = t.get("a");
                        assert!(a.is_some());

                        let a = a.unwrap();
                        assert!(is_match!(a, &Value::Integer(1)));
                    },
                    _ => panic!("What just happenend?"),
                }
            },
            _ => panic!("What just happenend?"),
        }
    }

    #[test]
    fn test_insert_with_seperator_into_array() {
        use std::ops::Index;

        let mut toml : Value = toml_from_str(r#"
        array = []
        "#).unwrap();

        let res = toml.insert_with_seperator(&String::from("array.[0]"), '.', Value::Integer(1));

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_none());

        assert!(is_match!(toml, Value::Table(_)));
        match toml {
            Value::Table(ref t) => {
                assert!(!t.is_empty());

                let array = t.get("array");
                assert!(array.is_some());

                let array = array.unwrap();
                assert!(is_match!(array, &Value::Array(_)));
                match array {
                    &Value::Array(ref a) => {
                        assert!(!a.is_empty());
                        assert!(is_match!(a.index(0), &Value::Integer(1)));
                    },
                    _ => panic!("What just happenend?"),
                }
            },
            _ => panic!("What just happenend?"),
        }
    }

    #[test]
    fn test_insert_with_seperator_into_nested_table() {
        let mut toml : Value = toml_from_str(r#"
        [a.b.c]
        "#).unwrap();

        let res = toml.insert_with_seperator(&String::from("a.b.c.d"), '.', Value::Integer(1));

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_none());

        assert!(is_match!(toml, Value::Table(_)));
        match toml {
            Value::Table(ref outer) => {
                assert!(!outer.is_empty());
                let a_tab = outer.get("a");
                assert!(a_tab.is_some());

                let a_tab = a_tab.unwrap();
                assert!(is_match!(a_tab, &Value::Table(_)));
                match a_tab {
                    &Value::Table(ref a) => {
                        assert!(!a.is_empty());

                        let b_tab = a.get("b");
                        assert!(b_tab.is_some());

                        let b_tab = b_tab.unwrap();
                        assert!(is_match!(b_tab, &Value::Table(_)));
                        match b_tab {
                            &Value::Table(ref b) => {
                                assert!(!b.is_empty());

                                let c_tab = b.get("c");
                                assert!(c_tab.is_some());

                                let c_tab = c_tab.unwrap();
                                assert!(is_match!(c_tab, &Value::Table(_)));
                                match c_tab {
                                    &Value::Table(ref c) => {
                                        assert!(!c.is_empty());

                                        let d = c.get("d");
                                        assert!(d.is_some());

                                        let d = d.unwrap();
                                        assert!(is_match!(d, &Value::Integer(1)));
                                    },
                                    _ => panic!("What just happenend?"),
                                }
                            },
                            _ => panic!("What just happenend?"),
                        }
                    },
                    _ => panic!("What just happenend?"),
                }
            },
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_insert_with_seperator_into_table_where_array_is() {
        let mut toml : Value = toml_from_str(r#"
        table = []
        "#).unwrap();

        let res = toml.insert_with_seperator(&String::from("table.a"), '.', Value::Integer(1));

        assert!(res.is_err());

        let err = res.unwrap_err();
        assert!(is_match!(err.kind(), &ErrorKind::NoIdentifierInArray(_)));
    }

    #[test]
    fn test_insert_with_seperator_into_array_where_table_is() {
        let mut toml : Value = toml_from_str(r#"
        [table]
        "#).unwrap();

        let res = toml.insert_with_seperator(&String::from("table.[0]"), '.', Value::Integer(1));

        assert!(res.is_err());

        let err = res.unwrap_err();
        assert!(is_match!(err.kind(), &ErrorKind::NoIndexInTable(_)));
    }

}

<commit_msg>Add test for inserting into existing array between values<commit_after>/// The Toml Insert extensions

use toml::Value;

use tokenizer::Token;
use tokenizer::tokenize_with_seperator;
use error::*;

pub trait TomlValueInsertExt {

    /// Extension function for inserting a value in the current toml::Value document
    /// using a custom seperator.
    ///
    /// For difference to TomlSetExt::set() and friends, read [#semantics].
    ///
    /// # Semantics
    ///
    /// The function automatically creates intermediate data structures based on the query string.
    /// That means, if the query string is `"a.b.c.[0]"`, but only a table `"a"` exists in the
    /// document, the function automatically creates a table `"b"` inside `"a"` and `"c"` inside
    /// `"b"`, and an array in `"c"`. The array index is ignored if the array is created.
    ///
    /// If an Array exists, but the specified index is larger than the last index, the array will
    /// be expanded by one element: If the array has a length of 3, but the query string specifies
    /// that the element should be put at 1000, the function ignores the large index and simply
    /// appends the value to the index.
    ///
    /// # Return value
    ///
    /// If the insert operation worked correctly, `Ok(None)` is returned.
    /// If the insert operation replaced an existing value `Ok(Some(old_value))` is returned
    /// On failure, `Err(e)` is returned
    ///
    fn insert_with_seperator(&mut self, query: &String, sep: char, value: Value) -> Result<Option<Value>>;

    /// Extension function for inserting a value from the current toml::Value document
    ///
    /// See documentation of `TomlValueinsertExt::insert_with_seperator`
    fn insert(&mut self, query: &String, value: Value) -> Result<Option<Value>> {
        self.insert_with_seperator(query, '.', value)
    }

}

impl TomlValueInsertExt for Value {

    fn insert_with_seperator(&mut self, query: &String, sep: char, value: Value) -> Result<Option<Value>> {
        use resolver::mut_resolver::resolve;

        let mut tokens = try!(tokenize_with_seperator(query, sep));
        let last       = tokens.pop_last().unwrap();
        let mut val    = try!(resolve(self, &tokens));

        match *last {
            Token::Identifier { ident, .. } => {
                match val {
                    &mut Value::Table(ref mut t) => {
                        Ok(t.insert(ident, value))
                    },
                    _ => Err(Error::from(ErrorKind::NoIdentifierInArray(ident.clone())))
                }
            },

            Token::Index { idx , .. } => {
                match val {
                    &mut Value::Array(ref mut a) => {
                        if a.len() > idx {
                            unimplemented!()
                        } else {
                            a.push(value);
                            Ok(None)
                        }
                    },
                    _ => Err(Error::from(ErrorKind::NoIndexInTable(idx)))
                }
            },
        }
    }

}

#[cfg(test)]
mod test {
    use super::*;
    use error::*;
    use toml::Value;
    use toml::from_str as toml_from_str;

    #[test]
    fn test_insert_with_seperator_into_table() {
        let mut toml : Value = toml_from_str(r#"
        [table]
        "#).unwrap();

        let res = toml.insert_with_seperator(&String::from("table.a"), '.', Value::Integer(1));

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_none());

        assert!(is_match!(toml, Value::Table(_)));
        match toml {
            Value::Table(ref t) => {
                assert!(!t.is_empty());

                let table = t.get("table");
                assert!(table.is_some());

                let table = table.unwrap();
                assert!(is_match!(table, &Value::Table(_)));
                match table {
                    &Value::Table(ref t) => {
                        assert!(!t.is_empty());

                        let a = t.get("a");
                        assert!(a.is_some());

                        let a = a.unwrap();
                        assert!(is_match!(a, &Value::Integer(1)));
                    },
                    _ => panic!("What just happenend?"),
                }
            },
            _ => panic!("What just happenend?"),
        }
    }

    #[test]
    fn test_insert_with_seperator_into_array() {
        use std::ops::Index;

        let mut toml : Value = toml_from_str(r#"
        array = []
        "#).unwrap();

        let res = toml.insert_with_seperator(&String::from("array.[0]"), '.', Value::Integer(1));

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_none());

        assert!(is_match!(toml, Value::Table(_)));
        match toml {
            Value::Table(ref t) => {
                assert!(!t.is_empty());

                let array = t.get("array");
                assert!(array.is_some());

                let array = array.unwrap();
                assert!(is_match!(array, &Value::Array(_)));
                match array {
                    &Value::Array(ref a) => {
                        assert!(!a.is_empty());
                        assert!(is_match!(a.index(0), &Value::Integer(1)));
                    },
                    _ => panic!("What just happenend?"),
                }
            },
            _ => panic!("What just happenend?"),
        }
    }

    #[test]
    fn test_insert_with_seperator_into_nested_table() {
        let mut toml : Value = toml_from_str(r#"
        [a.b.c]
        "#).unwrap();

        let res = toml.insert_with_seperator(&String::from("a.b.c.d"), '.', Value::Integer(1));

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_none());

        assert!(is_match!(toml, Value::Table(_)));
        match toml {
            Value::Table(ref outer) => {
                assert!(!outer.is_empty());
                let a_tab = outer.get("a");
                assert!(a_tab.is_some());

                let a_tab = a_tab.unwrap();
                assert!(is_match!(a_tab, &Value::Table(_)));
                match a_tab {
                    &Value::Table(ref a) => {
                        assert!(!a.is_empty());

                        let b_tab = a.get("b");
                        assert!(b_tab.is_some());

                        let b_tab = b_tab.unwrap();
                        assert!(is_match!(b_tab, &Value::Table(_)));
                        match b_tab {
                            &Value::Table(ref b) => {
                                assert!(!b.is_empty());

                                let c_tab = b.get("c");
                                assert!(c_tab.is_some());

                                let c_tab = c_tab.unwrap();
                                assert!(is_match!(c_tab, &Value::Table(_)));
                                match c_tab {
                                    &Value::Table(ref c) => {
                                        assert!(!c.is_empty());

                                        let d = c.get("d");
                                        assert!(d.is_some());

                                        let d = d.unwrap();
                                        assert!(is_match!(d, &Value::Integer(1)));
                                    },
                                    _ => panic!("What just happenend?"),
                                }
                            },
                            _ => panic!("What just happenend?"),
                        }
                    },
                    _ => panic!("What just happenend?"),
                }
            },
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_insert_with_seperator_into_table_where_array_is() {
        let mut toml : Value = toml_from_str(r#"
        table = []
        "#).unwrap();

        let res = toml.insert_with_seperator(&String::from("table.a"), '.', Value::Integer(1));

        assert!(res.is_err());

        let err = res.unwrap_err();
        assert!(is_match!(err.kind(), &ErrorKind::NoIdentifierInArray(_)));
    }

    #[test]
    fn test_insert_with_seperator_into_array_where_table_is() {
        let mut toml : Value = toml_from_str(r#"
        [table]
        "#).unwrap();

        let res = toml.insert_with_seperator(&String::from("table.[0]"), '.', Value::Integer(1));

        assert!(res.is_err());

        let err = res.unwrap_err();
        assert!(is_match!(err.kind(), &ErrorKind::NoIndexInTable(_)));
    }

    #[test]
    fn test_insert_with_seperator_into_array_between_values() {
        use std::ops::Index;

        let mut toml : Value = toml_from_str(r#"
        array = [1, 2, 3, 4, 5]
        "#).unwrap();

        let res = toml.insert_with_seperator(&String::from("array.[2]"), '.', Value::Integer(6));

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_none());

        assert!(is_match!(toml, Value::Table(_)));
        match toml {
            Value::Table(ref t) => {
                assert!(!t.is_empty());

                let array = t.get("array");
                assert!(array.is_some());

                let array = array.unwrap();
                assert!(is_match!(array, &Value::Array(_)));
                match array {
                    &Value::Array(ref a) => {
                        assert!(!a.is_empty());
                        assert!(is_match!(a.index(0), &Value::Integer(1)));
                        assert!(is_match!(a.index(1), &Value::Integer(2)));
                        assert!(is_match!(a.index(2), &Value::Integer(6)));
                        assert!(is_match!(a.index(3), &Value::Integer(3)));
                        assert!(is_match!(a.index(4), &Value::Integer(4)));
                        assert!(is_match!(a.index(5), &Value::Integer(5)));
                    },
                    _ => panic!("What just happenend?"),
                }
            },
            _ => panic!("What just happenend?"),
        }
    }

}

<|endoftext|>


<commit_before>use rustc_serialize::{Decoder, Decodable};

#[derive(Debug)]
pub struct FlightRecord {
    carrier: String,
    origin: String,
    destination: String,
    departure_delay: Option<i32>,
    arrival_delay: Option<i32>,
    cancelled: bool,
    distance: i32,
}

impl FlightRecord {
    pub fn carrier(&self) -> &str {
        &self.carrier
    }

    pub fn origin(&self) -> &str {
        &self.origin
    }

    pub fn destination(&self) -> &str {
        &self.destination
    }

    pub fn departure_delay(&self) -> &Option<i32> {
        &self.departure_delay
    }

    pub fn arrival_delay(&self) -> &Option<i32> {
        &self.arrival_delay
    }
}

impl Decodable for FlightRecord {
    fn decode<D: Decoder>(d: &mut D) -> Result<Self, D::Error> {
        #[derive(Debug, RustcDecodable)]
        struct Core {
            carrier: String,
            origin: String,
            destination: String,
            departure_delay: Option<i32>,
            arrival_delay: Option<i32>,
            cancelled: i32,
            distance: i32,
        }

        let core = try!(Core::decode(d));
        Ok(FlightRecord {
            carrier: core.carrier,
            origin: core.origin,
            destination: core.destination,
            departure_delay: core.departure_delay,
            arrival_delay: core.arrival_delay,
            cancelled: core.cancelled != 0,
            distance: core.distance,
        })
    }
}
<commit_msg>Use custom deserialization<commit_after>use rustc_serialize::{Decoder, Decodable};

#[derive(Debug)]
pub struct FlightRecord {
    carrier: String,
    origin: String,
    destination: String,
    departure_delay: Option<i32>,
    arrival_delay: Option<i32>,
    cancelled: bool,
    distance: i32,
}

impl FlightRecord {
    pub fn carrier(&self) -> &str {
        &self.carrier
    }

    pub fn origin(&self) -> &str {
        &self.origin
    }

    pub fn destination(&self) -> &str {
        &self.destination
    }

    pub fn departure_delay(&self) -> &Option<i32> {
        &self.departure_delay
    }

    pub fn arrival_delay(&self) -> &Option<i32> {
        &self.arrival_delay
    }
}

impl Decodable for FlightRecord {
    fn decode<D: Decoder>(d: &mut D) -> Result<Self, D::Error> {
        Ok(FlightRecord {
            carrier: try!(d.read_str()),
            origin: try!(d.read_str()),
            destination: try!(d.read_str()),
            departure_delay: d.read_i32().ok().map(|value| Some(value)).unwrap_or(None),
            arrival_delay: d.read_i32().ok().map(|value| Some(value)).unwrap_or(None),
            cancelled: try!(d.read_i32().map(|value| value != 0)),
            distance: try!(d.read_i32()),
        })
    }
}
<|endoftext|>


<commit_before>use super::item::{Item, ItemId};
use super::context::BindgenContext;
use super::ty::TypeKind;
use super::int::IntKind;
use super::function::cursor_mangling;
use parse::{ClangItemParser, ClangSubItemParser, ParseResult, ParseError};
use clang;

#[derive(Debug)]
pub struct Var {
    /// The name of the variable.
    name: String,
    /// The mangled name of the variable.
    mangled_name: Option<String>,
    /// The type of the variable.
    ty: ItemId,
    /// TODO: support non-integer constants?
    /// The integer value of the variable.
    val: Option<i64>,
    /// Whether this variable is const.
    is_const: bool,
}

impl Var {
    pub fn new(name: String,
               mangled: Option<String>,
               ty: ItemId,
               val: Option<i64>,
               is_const: bool) -> Var {
        assert!(!name.is_empty());
        Var {
            name: name,
            mangled_name: mangled,
            ty: ty,
            val: val,
            is_const: is_const,
        }
    }

    pub fn is_const(&self) -> bool {
        self.is_const
    }

    pub fn val(&self) -> Option<i64> {
        self.val
    }

    pub fn ty(&self) -> ItemId {
        self.ty
    }

    pub fn name(&self) -> &str {
        &self.name
    }

    pub fn mangled_name(&self) -> Option<&str> {
        self.mangled_name.as_ref().map(|n| &**n)
    }
}

impl ClangSubItemParser for Var {
    fn parse(cursor: clang::Cursor,
             context: &mut BindgenContext) -> Result<ParseResult<Self>, ParseError> {
        use clangll::*;
        match cursor.kind() {
            CXCursor_MacroDefinition => {
                let value = match parse_int_literal_tokens(&cursor, context.translation_unit()) {
                    None => return Err(ParseError::Continue),
                    Some(v) => v,
                };

                let name = cursor.spelling();
                if name.is_empty() {
                    warn!("Empty macro name?");
                    return Err(ParseError::Continue);
                }

                if context.parsed_macro(&name) {
                    warn!("Duplicated macro definition: {}", name);
                    return Err(ParseError::Continue);
                }
                context.note_parsed_macro(name.clone());

                let ty = if value < 0 {
                    Item::builtin_type(TypeKind::Int(IntKind::Int), true, context)
                } else if value.abs() > u32::max_value() as i64  {
                    Item::builtin_type(TypeKind::Int(IntKind::ULongLong), true, context)
                } else {
                    Item::builtin_type(TypeKind::Int(IntKind::UInt), true, context)
                };

                Ok(ParseResult::New(Var::new(name, None, ty, Some(value), true), Some(cursor)))
            }
            CXCursor_VarDecl => {
                let name = cursor.spelling();
                if name.is_empty() {
                    warn!("Empty constant name?");
                    return Err(ParseError::Continue);
                }

                let ty = cursor.cur_type();

                // XXX this is redundant, remove!
                let is_const = ty.is_const();

                let ty = Item::from_ty(&ty, Some(cursor), None, context)
                            .expect("Unable to resolve constant type?");

                // Note: Ty might not be totally resolved yet, see
                // tests/headers/inner_const.hpp
                //
                // That's fine because in that case we know it's not a literal.
                let value = context.safe_resolve_type(ty).map_or(None, |t| {
                    if t.is_integer(context) {
                        get_integer_literal_from_cursor(&cursor, context.translation_unit())
                    } else {
                        None
                    }
                });

                let mangling = cursor_mangling(&cursor);

                let var = Var::new(name, mangling, ty, value, is_const);
                Ok(ParseResult::New(var, Some(cursor)))

            }
            _ => {
                /* TODO */
                Err(ParseError::Continue)
            }
        }
    }
}

/// Try and parse the immediately found tokens from an unit (if any) to integers
fn parse_int_literal_tokens(cursor: &clang::Cursor,
                            unit: &clang::TranslationUnit) -> Option<i64> {
    use clangll::{CXToken_Literal, CXToken_Punctuation};

    let mut lit = String::new();
    let tokens = match unit.tokens(cursor) {
        None => return None,
        Some(tokens) => tokens,
    };

    for token in tokens {
        match token.kind {
            CXToken_Punctuation if token.spelling == "-" => {
                // If there's ever any punctuation, we only need to worry about
                // unary minus '-' (for now)
                lit.push_str(&token.spelling);
            },
            CXToken_Literal => {
                lit.push_str(&token.spelling);
                break
            },
            _ => (),
        }
    }

    // TODO: try to preserve hex literals?
    if lit.starts_with("0x") {
        i64::from_str_radix(&lit[2..], 16).ok()
    } else {
        lit.parse().ok()
    }
}

fn get_integer_literal_from_cursor(cursor: &clang::Cursor,
                                   unit: &clang::TranslationUnit) -> Option<i64> {
    use clangll::*;
    let mut value = None;
    cursor.visit(|c, _| {
        match c.kind() {
            CXCursor_IntegerLiteral => {
                value = parse_int_literal_tokens(&c, unit);
            }
            CXCursor_UnexposedExpr => {
                value = get_integer_literal_from_cursor(&c, unit);
            }
            _ => ()
        }
        if value.is_some() {
            CXChildVisit_Break
        } else {
            CXChildVisit_Continue
        }
    });
    value
}
<commit_msg>Handle octal and negative literals properly<commit_after>use super::item::{Item, ItemId};
use super::context::BindgenContext;
use super::ty::TypeKind;
use super::int::IntKind;
use super::function::cursor_mangling;
use parse::{ClangItemParser, ClangSubItemParser, ParseResult, ParseError};
use clang;

#[derive(Debug)]
pub struct Var {
    /// The name of the variable.
    name: String,
    /// The mangled name of the variable.
    mangled_name: Option<String>,
    /// The type of the variable.
    ty: ItemId,
    /// TODO: support non-integer constants?
    /// The integer value of the variable.
    val: Option<i64>,
    /// Whether this variable is const.
    is_const: bool,
}

impl Var {
    pub fn new(name: String,
               mangled: Option<String>,
               ty: ItemId,
               val: Option<i64>,
               is_const: bool) -> Var {
        assert!(!name.is_empty());
        Var {
            name: name,
            mangled_name: mangled,
            ty: ty,
            val: val,
            is_const: is_const,
        }
    }

    pub fn is_const(&self) -> bool {
        self.is_const
    }

    pub fn val(&self) -> Option<i64> {
        self.val
    }

    pub fn ty(&self) -> ItemId {
        self.ty
    }

    pub fn name(&self) -> &str {
        &self.name
    }

    pub fn mangled_name(&self) -> Option<&str> {
        self.mangled_name.as_ref().map(|n| &**n)
    }
}

impl ClangSubItemParser for Var {
    fn parse(cursor: clang::Cursor,
             context: &mut BindgenContext) -> Result<ParseResult<Self>, ParseError> {
        use clangll::*;
        match cursor.kind() {
            CXCursor_MacroDefinition => {
                let value = match parse_int_literal_tokens(&cursor, context.translation_unit()) {
                    None => return Err(ParseError::Continue),
                    Some(v) => v,
                };

                let name = cursor.spelling();
                if name.is_empty() {
                    warn!("Empty macro name?");
                    return Err(ParseError::Continue);
                }

                if context.parsed_macro(&name) {
                    warn!("Duplicated macro definition: {}", name);
                    return Err(ParseError::Continue);
                }
                context.note_parsed_macro(name.clone());

                let ty = if value < 0 {
                    Item::builtin_type(TypeKind::Int(IntKind::Int), true, context)
                } else if value.abs() > u32::max_value() as i64  {
                    Item::builtin_type(TypeKind::Int(IntKind::ULongLong), true, context)
                } else {
                    Item::builtin_type(TypeKind::Int(IntKind::UInt), true, context)
                };

                Ok(ParseResult::New(Var::new(name, None, ty, Some(value), true), Some(cursor)))
            }
            CXCursor_VarDecl => {
                let name = cursor.spelling();
                if name.is_empty() {
                    warn!("Empty constant name?");
                    return Err(ParseError::Continue);
                }

                let ty = cursor.cur_type();

                // XXX this is redundant, remove!
                let is_const = ty.is_const();

                let ty = Item::from_ty(&ty, Some(cursor), None, context)
                            .expect("Unable to resolve constant type?");

                // Note: Ty might not be totally resolved yet, see
                // tests/headers/inner_const.hpp
                //
                // That's fine because in that case we know it's not a literal.
                let value = context.safe_resolve_type(ty).map_or(None, |t| {
                    if t.is_integer(context) {
                        get_integer_literal_from_cursor(&cursor, context.translation_unit())
                    } else {
                        None
                    }
                });

                let mangling = cursor_mangling(&cursor);

                let var = Var::new(name, mangling, ty, value, is_const);
                Ok(ParseResult::New(var, Some(cursor)))

            }
            _ => {
                /* TODO */
                Err(ParseError::Continue)
            }
        }
    }
}

/// Try and parse the immediately found tokens from an unit (if any) to integers
fn parse_int_literal_tokens(cursor: &clang::Cursor,
                            unit: &clang::TranslationUnit) -> Option<i64> {
    use clangll::{CXToken_Literal, CXToken_Punctuation};

    let tokens = match unit.tokens(cursor) {
        None => return None,
        Some(tokens) => tokens,
    };

    let mut literal = None;
    let mut negate = false;
    for token in tokens.into_iter() {
        match token.kind {
            CXToken_Punctuation if token.spelling == "-" => {
                negate = !negate;
            },
            CXToken_Literal => {
                literal = Some(token.spelling);
                break
            },
            _ => {
                // Reset values if we found anything else
                negate = false;
                literal = None;
            }
        }
    }

    literal.and_then(|lit| {
        if lit.starts_with("0x") {
            // TODO: try to preserve hex literals?
            i64::from_str_radix(&lit[2..], 16).ok()
        } else if lit == "0" {
            Some(0)
        } else if lit.starts_with("0") {
            i64::from_str_radix(&lit[1..], 8).ok()
        } else {
            lit.parse().ok()
        }
    }).map(|lit| if negate { -lit } else { lit })
}

fn get_integer_literal_from_cursor(cursor: &clang::Cursor,
                                   unit: &clang::TranslationUnit) -> Option<i64> {
    use clangll::*;
    let mut value = None;
    cursor.visit(|c, _| {
        match c.kind() {
            CXCursor_IntegerLiteral |
            CXCursor_UnaryOperator => {
                value = parse_int_literal_tokens(&c, unit);
            }
            CXCursor_UnexposedExpr => {
                value = get_integer_literal_from_cursor(&c, unit);
            }
            _ => ()
        }
        if value.is_some() {
            CXChildVisit_Break
        } else {
            CXChildVisit_Continue
        }
    });
    value
}
<|endoftext|>