<commit_before>#![feature(zero_one)]

extern crate rand;
extern crate evo;
extern crate petgraph;
#[macro_use]
extern crate graph_annealing;
extern crate crossbeam;
extern crate simple_parallel;
extern crate num_cpus;
extern crate pcg;
extern crate triadic_census;
extern crate time;
extern crate lindenmayer_system;
extern crate graph_edge_evolution;
extern crate asexp;
extern crate expression;
extern crate expression_num;

#[path="genome/genome_edgeop_lsys.rs"]
pub mod genome;

use std::str::FromStr;
use rand::{Rng, SeedableRng};
use rand::os::OsRng;
use pcg::PcgRng;
use evo::Probability;
use evo::nsga2::{self, FitnessEval};
use genome::{Genome, Toolbox};
use graph_annealing::helper::{draw_graph, to_weighted_vec};
use graph_annealing::goal::{FitnessFunction, Goal};
use graph_annealing::stat::Stat;
use simple_parallel::Pool;
use petgraph::{Directed, Graph};
use triadic_census::OptDenseDigraph;
use std::fs::File;
use genome::VarOp;
use genome::edgeop::{EdgeOp, edgeops_to_graph};
use genome::expr_op::{ConstExprOp, ExprOp, FlatExprOp};
use std::io::Read;
use asexp::Sexp;
use asexp::sexp::prettyprint;
use std::env;
use std::collections::BTreeMap;
use petgraph::graph::NodeIndex;

const MAX_OBJECTIVES: usize = 3;

fn read_graph<R:Read>(mut rd: R) -> Result<Graph<(), (), Directed>, &'static str> {
    let mut s = String::new();
    match rd.read_to_string(&mut s) {
        Err(_) => return Err("failed to read file"),
        Ok(_) => {}
    }
    let sexp = match Sexp::parse_toplevel(&s) {
        Err(()) => return Err("failed to parse"),
        Ok(sexp) => sexp
    };

    println!("{}", sexp);

    let mut map = match sexp.into_map() {
        Err(s) => return Err(s),
        Ok(m) => m,
    };

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

    match map["version"].get_uint() {
        Some(1) => {}
        _ => {
            return Err("invalid version. Expect 1")
        }
    }

    let mut nodes;
    if let Some(Sexp::Array(v)) = map.remove("nodes") {
        nodes = Vec::new();
        for entry in v {
            nodes.push(entry.into_map().unwrap());
        }
    } else {
        return Err("no nodes given or invalid");
    }

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

    let mut graph = Graph::new();

    // maps nodes as defined in the graph file to node-ids as used in the graph
    let mut node_map: BTreeMap<u64, NodeIndex> = BTreeMap::new();

    // iterate once to add all nodes
    for node_info in nodes.iter() {
        // XXX: allow other id types
        if let Some(id) = node_info.get("id").and_then(|i| i.get_uint()) {
            println!("node-id: {}", id);
            let idx = graph.add_node(());
            println!("node-idx: {:?}", idx);

            if let Some(_) = node_map.insert(id, idx) {
                return Err("duplicate node-id");
            }
        } else {
            return Err("non-existing or invalid non-integer node key");
        }
    }

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

    // iterate again, to add all edges
    for mut node_info in nodes.into_iter() {
        // XXX: allow other id types
        let id = node_info.get("id").and_then(|i| i.get_uint()).unwrap();
        println!("node-id: {}", id);

        // XXX: set node weight.

        let src_idx = node_map[&id];
        println!("src-node-idx: {:?}", src_idx);

        if let Some(node_weight) = node_info.remove("weight") {
            println!("node_weight: {}", node_weight);
        }

        if let Some(edges) = node_info.remove("edges") {
            match edges {
                Sexp::Array(edge_list) => {
                    for edge_def in edge_list.iter() {
                        if let Some(target_id) = edge_def.get_uint() {
                            let dst_idx = node_map[&target_id];
                            println!("dst-node-idx: {:?}", dst_idx);
                            let _ = graph.add_edge(src_idx, dst_idx, ());
                        } else {
                            return Err("invalid edge node id");
                        }
                    }
                }
                _ => {
                    return Err("Invalid edge list");
                }
            }
        }
    }

    Ok(graph)
}

#[derive(Debug)]
struct ConfigGenome {
    max_iter: usize,
    rules: usize,
    initial_len: usize,
    symbol_arity: usize,
    prob_terminal: Probability,
}

#[derive(Debug)]
struct Config {
    ngen: usize,
    mu: usize,
    lambda: usize,
    k: usize,
    seed: Vec<u64>,
    objectives: Vec<Objective>,
    graph: Graph<(), (), Directed>,
    edge_ops: Vec<(EdgeOp, u32)>,
    var_ops: Vec<(VarOp, u32)>,
    genome: ConfigGenome,
}

#[derive(Debug)]
struct Objective {
    fitness_function: FitnessFunction,
    threshold: f32,
}

fn parse_config(sexp: Sexp) -> Config {
    let map = sexp.into_map().unwrap();

    // number of generations
    let ngen: usize = map.get("ngen").and_then(|v| v.get_uint()).unwrap() as usize;

    // size of population
    let mu: usize = map.get("mu").and_then(|v| v.get_uint()).unwrap() as usize;

    // size of offspring population
    let lambda: usize = map.get("lambda").and_then(|v| v.get_uint()).unwrap() as usize;

    // tournament selection
    let k: usize = map.get("k").and_then(|v| v.get_uint()).unwrap_or(2) as usize;
    assert!(k > 0);

    let seed: Vec<u64>;
    if let Some(seed_expr) = map.get("seed") {
        seed = seed_expr.get_uint_vec().unwrap();
    } else {
        println!("Use OsRng to generate seed..");
        let mut rng = OsRng::new().unwrap();
        seed = (0..2).map(|_| rng.next_u64()).collect();
    }

    // Parse objectives and thresholds
    let mut objectives: Vec<Objective> = Vec::new();
    if let Some(&Sexp::Map(ref list)) = map.get("objectives") {
        for &(ref k, ref v) in list.iter() {
            objectives.push(Objective {
                fitness_function: FitnessFunction::from_str(k.get_str().unwrap()).unwrap(),
                threshold: v.get_float().unwrap() as f32,
            });
        }
    } else {
        panic!("Map expected");
    }

    if objectives.len() > MAX_OBJECTIVES {
        panic!("Max {} objectives allowed", MAX_OBJECTIVES);
    }

    // read graph
    let graph_file = map.get("graph").unwrap().get_str().unwrap();
    println!("Using graph file: {}", graph_file);
    let graph = read_graph(File::open(graph_file).unwrap()).unwrap();
    println!("graph: {:?}", graph);

    // Parse weighted operation choice from command line
    let mut edge_ops: Vec<(EdgeOp, u32)> = Vec::new();
    if let Some(&Sexp::Map(ref list)) = map.get("edgeops") {
        for &(ref k, ref v) in list.iter() {
            edge_ops.push((EdgeOp::from_str(k.get_str().unwrap()).unwrap(),
                           v.get_uint().unwrap() as u32));
        }
    } else {
        panic!("Map expected");
    }

    // Parse weighted variation operators from command line
    let mut var_ops: Vec<(VarOp, u32)> = Vec::new();
    if let Some(&Sexp::Map(ref list)) = map.get("varops") {
        for &(ref k, ref v) in list.iter() {
            var_ops.push((VarOp::from_str(k.get_str().unwrap()).unwrap(),
                          v.get_uint().unwrap() as u32));
        }
    } else {
        panic!();
    }

    let genome_map = map.get("genome").unwrap().clone().into_map().unwrap();

    Config {
        ngen: ngen,
        mu: mu,
        lambda: lambda,
        k: k,
        seed: seed,
        objectives: objectives,
        graph: graph,
        edge_ops: edge_ops,
        var_ops: var_ops,
        genome: ConfigGenome {
            rules: genome_map.get("rules").and_then(|v| v.get_uint()).unwrap() as usize,
            symbol_arity: genome_map.get("symbol_arity").and_then(|v| v.get_uint()).unwrap() as usize,
            initial_len: genome_map.get("initial_len").and_then(|v| v.get_uint()).unwrap() as usize,
            max_iter: genome_map.get("max_iter").and_then(|v| v.get_uint()).unwrap() as usize,
            prob_terminal: Probability::new(genome_map.get("prob_terminal").and_then(|v| v.get_float()).unwrap() as f32),
        },
    }
}

fn pp_sexp(s: &Sexp) {
    let mut st = String::new();
    let _ = prettyprint(&s, &mut st, 0, false).unwrap();
    println!("{}", st);
}

fn main() {
    let ncpus = num_cpus::get();
    println!("Using {} CPUs", ncpus);

    let mut s = String::new();
    let configfile = env::args().nth(1).unwrap();
    let _ = File::open(configfile).unwrap().read_to_string(&mut s).unwrap();
    let expr = asexp::Sexp::parse_toplevel(&s).unwrap();
    let config = parse_config(expr);

    println!("{:#?}", config);

    let w_ops = to_weighted_vec(&config.edge_ops);
    assert!(w_ops.len() > 0);

    let w_var_ops = to_weighted_vec(&config.var_ops);
    assert!(w_var_ops.len() > 0);

    let num_objectives = config.objectives.len();

    let mut toolbox = Toolbox::new(Goal::new(OptDenseDigraph::from(config.graph.clone())),
                                   Pool::new(ncpus),
                                   config.objectives
                                         .iter()
                                         .map(|o| o.fitness_function.clone())
                                         .collect(),
                                   config.genome.max_iter, // iterations
                                   config.genome.rules, // num_rules
                                   config.genome.initial_len, // initial rule length
                                   config.genome.symbol_arity, // we use 2-ary symbols
                                   config.genome.prob_terminal,
                                   2, // max_expr_depth
                                   w_ops,
                                   ExprOp::uniform_distribution(),
                                   FlatExprOp::uniform_distribution(),
                                   ConstExprOp::uniform_distribution(),
                                   w_var_ops);

    assert!(config.seed.len() == 2);
    let mut rng: PcgRng = SeedableRng::from_seed([config.seed[0], config.seed[1]]);

    // create initial random population
    let initial_population: Vec<Genome> = (0..config.mu)
                                              .map(|_| toolbox.random_genome(&mut rng))
                                              .collect();

    // output initial population to stdout.
    for ind in initial_population.iter() {
        pp_sexp(&ind.into());
    }

    // evaluate fitness
    let fitness: Vec<_> = toolbox.fitness(&initial_population[..]);
    assert!(fitness.len() == initial_population.len());

    let mut pop = initial_population;
    let mut fit = fitness;

    for iteration in 0..config.ngen {
        print!("# {:>6}", iteration);
        let before = time::precise_time_ns();
        let (new_pop, new_fit) = nsga2::iterate(&mut rng,
                                                pop,
                                                fit,
                                                config.mu,
                                                config.lambda,
                                                config.k,
                                                num_objectives,
                                                &mut toolbox);
        let duration = time::precise_time_ns() - before;
        pop = new_pop;
        fit = new_fit;
        assert!(fit.len() > 0);

        let duration_ms = (duration as f32) / 1_000_000.0;

        let mut num_optima = 0;
        for f in fit.iter() {
            if config.objectives
                     .iter()
                     .enumerate()
                     .all(|(i, obj)| f.objectives[i] <= obj.threshold) {
                num_optima += 1;
            }
        }

        // calculate a min/max/avg value for each objective.
        let stats: Vec<Stat<f32>> = (0..num_objectives)
                                        .into_iter()
                                        .map(|i| {
                                            Stat::from_iter(fit.iter().map(|o| o.objectives[i]))
                                                .unwrap()
                                        })
                                        .collect();

        for stat in stats.iter() {
            print!(" | ");
            print!("{:>8.2}", stat.min);
            print!("{:>9.2}", stat.avg);
            print!("{:>10.2}", stat.max);
        }

        print!(" | {:>5} | {:>8.0} ms", num_optima, duration_ms);
        println!("");

        if num_optima > 0 {
            println!("Found premature optimum in Iteration {}", iteration);
            break;
        }
    }
    println!("===========================================================");

    // finally evaluate rank and crowding distance (using select()).
    let rank_dist = nsga2::select(&fit[..], config.mu, num_objectives);
    assert!(rank_dist.len() == config.mu);

    let mut j = 0;

    for rd in rank_dist.iter() {
        if rd.rank == 0 {
            println!("-------------------------------------------");
            println!("rd: {:?}", rd);
            println!("fitness: {:?}", fit[rd.idx]);
            // println!("genome: {:?}", pop[rd.idx]);
            pp_sexp(&(&pop[rd.idx]).into());

            // if fit[rd.idx].objectives[0] < 1.0 {
            let ind = &pop[rd.idx];
            let edge_ops = ind.to_edge_ops(&toolbox.axiom_args, toolbox.iterations);
            let g = edgeops_to_graph(&edge_ops);

            draw_graph(g.ref_graph(),
                       // XXX: name
                       &format!("edgeop_lsys_g{}_f{}_i{}.svg",
                                config.ngen,
                                fit[rd.idx].objectives[1] as usize,
                                j));
            j += 1;
            // }
        }
    }
}
<commit_msg>Parse the node and edge weights<commit_after>#![feature(zero_one)]

extern crate rand;
extern crate evo;
extern crate petgraph;
#[macro_use]
extern crate graph_annealing;
extern crate crossbeam;
extern crate simple_parallel;
extern crate num_cpus;
extern crate pcg;
extern crate triadic_census;
extern crate time;
extern crate lindenmayer_system;
extern crate graph_edge_evolution;
extern crate asexp;
extern crate expression;
extern crate expression_num;

#[path="genome/genome_edgeop_lsys.rs"]
pub mod genome;

use std::str::FromStr;
use rand::{Rng, SeedableRng};
use rand::os::OsRng;
use pcg::PcgRng;
use evo::Probability;
use evo::nsga2::{self, FitnessEval};
use genome::{Genome, Toolbox};
use graph_annealing::helper::{draw_graph, to_weighted_vec};
use graph_annealing::goal::{FitnessFunction, Goal};
use graph_annealing::stat::Stat;
use simple_parallel::Pool;
use petgraph::{Directed, Graph};
use triadic_census::OptDenseDigraph;
use std::fs::File;
use genome::VarOp;
use genome::edgeop::{EdgeOp, edgeops_to_graph};
use genome::expr_op::{ConstExprOp, ExprOp, FlatExprOp};
use std::io::Read;
use asexp::Sexp;
use asexp::sexp::prettyprint;
use std::env;
use std::collections::BTreeMap;
use petgraph::graph::NodeIndex;

const MAX_OBJECTIVES: usize = 3;

fn read_graph<R, NodeWeightFn, EdgeWeightFn, NW, EW>(mut rd: R, mut node_weight_fn: NodeWeightFn, mut edge_weight_fn: EdgeWeightFn) -> Result<Graph<NW, EW, Directed>, &'static str>
where R:Read,
      NodeWeightFn: FnMut(Option<&Sexp>) -> Option<NW>,
      EdgeWeightFn: FnMut(Option<&Sexp>) -> Option<EW>
{
    let mut s = String::new();
    match rd.read_to_string(&mut s) {
        Err(_) => return Err("failed to read file"),
        Ok(_) => {}
    }
    let sexp = match Sexp::parse_toplevel(&s) {
        Err(()) => return Err("failed to parse"),
        Ok(sexp) => sexp
    };

    println!("{}", sexp);

    let mut map = match sexp.into_map() {
        Err(s) => return Err(s),
        Ok(m) => m,
    };

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

    match map["version"].get_uint() {
        Some(1) => {}
        _ => {
            return Err("invalid version. Expect 1")
        }
    }

    let mut nodes;
    if let Some(Sexp::Array(v)) = map.remove("nodes") {
        nodes = Vec::new();
        for entry in v {
            nodes.push(entry.into_map().unwrap());
        }
    } else {
        return Err("no nodes given or invalid");
    }

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

    let mut graph = Graph::new();

    // maps nodes as defined in the graph file to node-ids as used in the graph
    let mut node_map: BTreeMap<u64, NodeIndex> = BTreeMap::new();

    // iterate once to add all nodes
    for node_info in nodes.iter() {
        // XXX: allow other id types
        if let Some(id) = node_info.get("id").and_then(|i| i.get_uint()) {
            println!("node-id: {}", id);
            let weight = match node_weight_fn(node_info.get("weight")) {
                Some(w) => w,
                None => {
                    return Err("invalid node weight");
                }
            };
            let idx = graph.add_node(weight);
            println!("node-idx: {:?}", idx);

            if let Some(_) = node_map.insert(id, idx) {
                return Err("duplicate node-id");
            }
        } else {
            return Err("non-existing or invalid non-integer node key");
        }
    }

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

    // iterate again, to add all edges
    for mut node_info in nodes.into_iter() {
        // XXX: allow other id types
        let id = node_info.get("id").and_then(|i| i.get_uint()).unwrap();
        println!("node-id: {}", id);

        // XXX: set node weight.

        let src_idx = node_map[&id];
        println!("src-node-idx: {:?}", src_idx);

        if let Some(node_weight) = node_info.remove("weight") {
            println!("node_weight: {}", node_weight);
        }

        if let Some(edges) = node_info.remove("edges") {
            match edges {
                Sexp::Array(edge_list) => {
                    for edge_def in edge_list.iter() {
                        if let Some(target_id) = edge_def.get_uint() {
                            let dst_idx = node_map[&target_id];
                            println!("dst-node-idx: {:?}", dst_idx);

                            let weight = match edge_weight_fn(None) {
                                Some(w) => w,
                                None => {
                                    return Err("invalid edge weight");
                                }
                            };

                            let _ = graph.add_edge(src_idx, dst_idx, weight);
                        } else {
                            let mut valid = false;

                            if let &Sexp::Tuple(ref list) = edge_def {
                                // (node-id weight) tuple
                                if list.len() == 2 {
                                    if let Some(target_id) = list[0].get_uint() {
                                        let dst_idx = node_map[&target_id];
                                        println!("dst-node-idx: {:?}", dst_idx);

                                        let weight = match edge_weight_fn(list.get(1)) {
                                            Some(w) => w,
                                            None => {
                                                return Err("invalid edge weight");
                                            }
                                        };

                                        let _ = graph.add_edge(src_idx, dst_idx, weight);
                                        valid = true;
                                    }
                                }
                            }
                            if !valid {
                                return Err("invalid edge node id");
                            }
                        }
                    }
                }
                _ => {
                    return Err("Invalid edge list");
                }
            }
        }
    }

    Ok(graph)
}

#[derive(Debug)]
struct ConfigGenome {
    max_iter: usize,
    rules: usize,
    initial_len: usize,
    symbol_arity: usize,
    prob_terminal: Probability,
}

#[derive(Debug)]
struct Config {
    ngen: usize,
    mu: usize,
    lambda: usize,
    k: usize,
    seed: Vec<u64>,
    objectives: Vec<Objective>,
    graph: Graph<(), (), Directed>,
    edge_ops: Vec<(EdgeOp, u32)>,
    var_ops: Vec<(VarOp, u32)>,
    genome: ConfigGenome,
}

#[derive(Debug)]
struct Objective {
    fitness_function: FitnessFunction,
    threshold: f32,
}

fn parse_config(sexp: Sexp) -> Config {
    let map = sexp.into_map().unwrap();

    // number of generations
    let ngen: usize = map.get("ngen").and_then(|v| v.get_uint()).unwrap() as usize;

    // size of population
    let mu: usize = map.get("mu").and_then(|v| v.get_uint()).unwrap() as usize;

    // size of offspring population
    let lambda: usize = map.get("lambda").and_then(|v| v.get_uint()).unwrap() as usize;

    // tournament selection
    let k: usize = map.get("k").and_then(|v| v.get_uint()).unwrap_or(2) as usize;
    assert!(k > 0);

    let seed: Vec<u64>;
    if let Some(seed_expr) = map.get("seed") {
        seed = seed_expr.get_uint_vec().unwrap();
    } else {
        println!("Use OsRng to generate seed..");
        let mut rng = OsRng::new().unwrap();
        seed = (0..2).map(|_| rng.next_u64()).collect();
    }

    // Parse objectives and thresholds
    let mut objectives: Vec<Objective> = Vec::new();
    if let Some(&Sexp::Map(ref list)) = map.get("objectives") {
        for &(ref k, ref v) in list.iter() {
            objectives.push(Objective {
                fitness_function: FitnessFunction::from_str(k.get_str().unwrap()).unwrap(),
                threshold: v.get_float().unwrap() as f32,
            });
        }
    } else {
        panic!("Map expected");
    }

    if objectives.len() > MAX_OBJECTIVES {
        panic!("Max {} objectives allowed", MAX_OBJECTIVES);
    }

    // read graph
    let graph_file = map.get("graph").unwrap().get_str().unwrap();
    println!("Using graph file: {}", graph_file);
    let graph = read_graph(File::open(graph_file).unwrap(), |w| {println!("node weight: {:?}", w); Some(())}, |w| {println!("edge weight: {:?}", w); Some(())}).unwrap();
    println!("graph: {:?}", graph);

    // Parse weighted operation choice from command line
    let mut edge_ops: Vec<(EdgeOp, u32)> = Vec::new();
    if let Some(&Sexp::Map(ref list)) = map.get("edgeops") {
        for &(ref k, ref v) in list.iter() {
            edge_ops.push((EdgeOp::from_str(k.get_str().unwrap()).unwrap(),
                           v.get_uint().unwrap() as u32));
        }
    } else {
        panic!("Map expected");
    }

    // Parse weighted variation operators from command line
    let mut var_ops: Vec<(VarOp, u32)> = Vec::new();
    if let Some(&Sexp::Map(ref list)) = map.get("varops") {
        for &(ref k, ref v) in list.iter() {
            var_ops.push((VarOp::from_str(k.get_str().unwrap()).unwrap(),
                          v.get_uint().unwrap() as u32));
        }
    } else {
        panic!();
    }

    let genome_map = map.get("genome").unwrap().clone().into_map().unwrap();

    Config {
        ngen: ngen,
        mu: mu,
        lambda: lambda,
        k: k,
        seed: seed,
        objectives: objectives,
        graph: graph,
        edge_ops: edge_ops,
        var_ops: var_ops,
        genome: ConfigGenome {
            rules: genome_map.get("rules").and_then(|v| v.get_uint()).unwrap() as usize,
            symbol_arity: genome_map.get("symbol_arity").and_then(|v| v.get_uint()).unwrap() as usize,
            initial_len: genome_map.get("initial_len").and_then(|v| v.get_uint()).unwrap() as usize,
            max_iter: genome_map.get("max_iter").and_then(|v| v.get_uint()).unwrap() as usize,
            prob_terminal: Probability::new(genome_map.get("prob_terminal").and_then(|v| v.get_float()).unwrap() as f32),
        },
    }
}

fn pp_sexp(s: &Sexp) {
    let mut st = String::new();
    let _ = prettyprint(&s, &mut st, 0, false).unwrap();
    println!("{}", st);
}

fn main() {
    let ncpus = num_cpus::get();
    println!("Using {} CPUs", ncpus);

    let mut s = String::new();
    let configfile = env::args().nth(1).unwrap();
    let _ = File::open(configfile).unwrap().read_to_string(&mut s).unwrap();
    let expr = asexp::Sexp::parse_toplevel(&s).unwrap();
    let config = parse_config(expr);

    println!("{:#?}", config);

    let w_ops = to_weighted_vec(&config.edge_ops);
    assert!(w_ops.len() > 0);

    let w_var_ops = to_weighted_vec(&config.var_ops);
    assert!(w_var_ops.len() > 0);

    let num_objectives = config.objectives.len();

    let mut toolbox = Toolbox::new(Goal::new(OptDenseDigraph::from(config.graph.clone())),
                                   Pool::new(ncpus),
                                   config.objectives
                                         .iter()
                                         .map(|o| o.fitness_function.clone())
                                         .collect(),
                                   config.genome.max_iter, // iterations
                                   config.genome.rules, // num_rules
                                   config.genome.initial_len, // initial rule length
                                   config.genome.symbol_arity, // we use 2-ary symbols
                                   config.genome.prob_terminal,
                                   2, // max_expr_depth
                                   w_ops,
                                   ExprOp::uniform_distribution(),
                                   FlatExprOp::uniform_distribution(),
                                   ConstExprOp::uniform_distribution(),
                                   w_var_ops);

    assert!(config.seed.len() == 2);
    let mut rng: PcgRng = SeedableRng::from_seed([config.seed[0], config.seed[1]]);

    // create initial random population
    let initial_population: Vec<Genome> = (0..config.mu)
                                              .map(|_| toolbox.random_genome(&mut rng))
                                              .collect();

    // output initial population to stdout.
    for ind in initial_population.iter() {
        pp_sexp(&ind.into());
    }

    // evaluate fitness
    let fitness: Vec<_> = toolbox.fitness(&initial_population[..]);
    assert!(fitness.len() == initial_population.len());

    let mut pop = initial_population;
    let mut fit = fitness;

    for iteration in 0..config.ngen {
        print!("# {:>6}", iteration);
        let before = time::precise_time_ns();
        let (new_pop, new_fit) = nsga2::iterate(&mut rng,
                                                pop,
                                                fit,
                                                config.mu,
                                                config.lambda,
                                                config.k,
                                                num_objectives,
                                                &mut toolbox);
        let duration = time::precise_time_ns() - before;
        pop = new_pop;
        fit = new_fit;
        assert!(fit.len() > 0);

        let duration_ms = (duration as f32) / 1_000_000.0;

        let mut num_optima = 0;
        for f in fit.iter() {
            if config.objectives
                     .iter()
                     .enumerate()
                     .all(|(i, obj)| f.objectives[i] <= obj.threshold) {
                num_optima += 1;
            }
        }

        // calculate a min/max/avg value for each objective.
        let stats: Vec<Stat<f32>> = (0..num_objectives)
                                        .into_iter()
                                        .map(|i| {
                                            Stat::from_iter(fit.iter().map(|o| o.objectives[i]))
                                                .unwrap()
                                        })
                                        .collect();

        for stat in stats.iter() {
            print!(" | ");
            print!("{:>8.2}", stat.min);
            print!("{:>9.2}", stat.avg);
            print!("{:>10.2}", stat.max);
        }

        print!(" | {:>5} | {:>8.0} ms", num_optima, duration_ms);
        println!("");

        if num_optima > 0 {
            println!("Found premature optimum in Iteration {}", iteration);
            break;
        }
    }
    println!("===========================================================");

    // finally evaluate rank and crowding distance (using select()).
    let rank_dist = nsga2::select(&fit[..], config.mu, num_objectives);
    assert!(rank_dist.len() == config.mu);

    let mut j = 0;

    for rd in rank_dist.iter() {
        if rd.rank == 0 {
            println!("-------------------------------------------");
            println!("rd: {:?}", rd);
            println!("fitness: {:?}", fit[rd.idx]);
            // println!("genome: {:?}", pop[rd.idx]);
            pp_sexp(&(&pop[rd.idx]).into());

            // if fit[rd.idx].objectives[0] < 1.0 {
            let ind = &pop[rd.idx];
            let edge_ops = ind.to_edge_ops(&toolbox.axiom_args, toolbox.iterations);
            let g = edgeops_to_graph(&edge_ops);

            draw_graph(g.ref_graph(),
                       // XXX: name
                       &format!("edgeop_lsys_g{}_f{}_i{}.svg",
                                config.ngen,
                                fit[rd.idx].objectives[1] as usize,
                                j));
            j += 1;
            // }
        }
    }
}
<|endoftext|>


<commit_before>use std;
use std::os::raw::c_char;
use std::ffi::CStr;

use raw::*;
use pushable::Pushable;
use scalar::{ Scalar };
use handle::{ Full, Bare, Temp, From };

pub struct Context<'a> {
    pthx: PerlContext,
    stack: OuroborosStack,
    marker: std::marker::PhantomData<&'a PerlContext>
}

macro_rules! wrapper {
    ($name:ident : $func:ident $( -$flag:ident )* ($( $arg:ident : $ty:ty ),*))
        => (wrapper! { $name : $func $( -$flag )* ( $( $arg : $ty ),* ) -> () });

    ($name:ident : $func:ident ($( $arg:ident : $ty:ty ),*) -> $rt:ty)
        => (wrapper! { $name : $func ( $( $arg: $ty ),* ) self (self.pthx) -> $rt });

    ($name:ident : $func:ident -stack ( $( $arg:ident: $ty:ty ),*) -> $rt:ty)
        => (wrapper! { $name : $func ( $( $arg: $ty ),* ) self (self.pthx, &mut self.stack) -> $rt });

    ($name:ident : $func:ident ( $( $arg:ident: $ty:ty ),* ) $slf:ident ( $( $def:expr ),* ) -> $rt:ty)
        => (pub fn $name(&mut $slf, $( $arg: $ty ),*) -> $rt { unsafe { $func($( $def ),*, $( $arg ),*) } });
}

impl<'a> Context<'a> {
    pub fn new(pthx: &'a PerlContext) -> Self {
        unsafe {
            let mut ctx = Context {
                pthx: *pthx,
                stack: std::mem::uninitialized(),
                marker: std::marker::PhantomData,
            };
            ouroboros_stack_init(ctx.pthx, &mut ctx.stack);
            ctx
        }
    }

    pub fn new_xs(&mut self, name: &str, func: XSUBADDR_t) {
        let cname = std::ffi::CString::new(name).unwrap();
        unsafe {
            Perl_newXS(self.pthx, cname.as_ptr(), func, b"\0" as *const _ as *const c_char);
        }
    }

    //

    wrapper! { st_prepush: ouroboros_stack_prepush -stack () }
    wrapper! { st_putback: ouroboros_stack_putback -stack () }
    wrapper! { st_extend: ouroboros_stack_extend -stack (len: Size_t) }

    pub fn st_push<T>(&mut self, value: T) where T: Pushable {
        value.push_extend(self.pthx, &mut self.stack);
    }

    pub unsafe fn st_push_unsafe<T>(&mut self, value: T) where T: Pushable {
        value.push_unsafe(self.pthx, &mut self.stack);
    }

    //

    fn new_temp<T>(&mut self, ptr: *mut T) -> Temp<T> {
        Temp::new(self.pthx, ptr)
    }

    // Stack ops

    pub unsafe fn st_fetch_raw(&mut self, idx: SSize_t) -> *mut SV {
        ouroboros_stack_fetch(self.pthx, &mut self.stack, idx)
    }

    pub fn st_fetch<T>(&mut self, idx: SSize_t) -> T where T: From<Temp<SV>> {
        let svp = unsafe { self.st_fetch_raw(idx) };
        T::from(self.new_temp(svp))
    }

    // GV ops

    pub fn get_av<T>(&mut self, name: &CStr) -> T where T: From<Option<Temp<AV>>> {
        let avp = unsafe { Perl_get_av(self.pthx, name.as_ptr(), 0) };
        let opt = if avp.is_null() { None } else { Some(self.new_temp(avp)) };

        T::from(opt)
    }

    // SV ops
    pub fn sv_iv(&mut self, sv: Bare<SV>) -> IV {
        Full::from_bare(self.pthx, sv).to_iv()
    }
}
<commit_msg>Add call_pv wrapper.<commit_after>use std;
use std::os::raw::c_char;
use std::ffi::CStr;

use raw::*;
use pushable::Pushable;
use scalar::{ Scalar };
use handle::{ Full, Bare, Temp, From };

pub struct Context<'a> {
    pthx: PerlContext,
    stack: OuroborosStack,
    marker: std::marker::PhantomData<&'a PerlContext>
}

macro_rules! wrapper {
    ($name:ident : $func:ident $( -$flag:ident )* ($( $arg:ident : $ty:ty ),*))
        => (wrapper! { $name : $func $( -$flag )* ( $( $arg : $ty ),* ) -> () });

    ($name:ident : $func:ident ($( $arg:ident : $ty:ty ),*) -> $rt:ty)
        => (wrapper! { $name : $func ( $( $arg: $ty ),* ) self (self.pthx) -> $rt });

    ($name:ident : $func:ident -stack ( $( $arg:ident: $ty:ty ),*) -> $rt:ty)
        => (wrapper! { $name : $func ( $( $arg: $ty ),* ) self (self.pthx, &mut self.stack) -> $rt });

    ($name:ident : $func:ident ( $( $arg:ident: $ty:ty ),* ) $slf:ident ( $( $def:expr ),* ) -> $rt:ty)
        => (pub fn $name(&mut $slf, $( $arg: $ty ),*) -> $rt { unsafe { $func($( $def ),*, $( $arg ),*) } });
}

impl<'a> Context<'a> {
    pub fn new(pthx: &'a PerlContext) -> Self {
        unsafe {
            let mut ctx = Context {
                pthx: *pthx,
                stack: std::mem::uninitialized(),
                marker: std::marker::PhantomData,
            };
            ouroboros_stack_init(ctx.pthx, &mut ctx.stack);
            ctx
        }
    }

    pub fn new_xs(&mut self, name: &str, func: XSUBADDR_t) {
        let cname = std::ffi::CString::new(name).unwrap();
        unsafe {
            Perl_newXS(self.pthx, cname.as_ptr(), func, b"\0" as *const _ as *const c_char);
        }
    }

    //

    wrapper! { st_prepush: ouroboros_stack_prepush -stack () }
    wrapper! { st_putback: ouroboros_stack_putback -stack () }
    wrapper! { st_extend: ouroboros_stack_extend -stack (len: Size_t) }

    pub fn st_push<T>(&mut self, value: T) where T: Pushable {
        value.push_extend(self.pthx, &mut self.stack);
    }

    pub unsafe fn st_push_unsafe<T>(&mut self, value: T) where T: Pushable {
        value.push_unsafe(self.pthx, &mut self.stack);
    }

    //

    fn new_temp<T>(&mut self, ptr: *mut T) -> Temp<T> {
        Temp::new(self.pthx, ptr)
    }

    // Stack ops

    pub unsafe fn st_fetch_raw(&mut self, idx: SSize_t) -> *mut SV {
        ouroboros_stack_fetch(self.pthx, &mut self.stack, idx)
    }

    pub fn st_fetch<T>(&mut self, idx: SSize_t) -> T where T: From<Temp<SV>> {
        let svp = unsafe { self.st_fetch_raw(idx) };
        T::from(self.new_temp(svp))
    }

    pub fn call_pv(&self, name: &CStr, flags: IV) -> I32 {
        unsafe {
            Perl_call_pv(self.pthx, name.as_ptr(), flags as I32)
        }
    }

    // GV ops

    pub fn get_av<T>(&mut self, name: &CStr) -> T where T: From<Option<Temp<AV>>> {
        let avp = unsafe { Perl_get_av(self.pthx, name.as_ptr(), 0) };
        let opt = if avp.is_null() { None } else { Some(self.new_temp(avp)) };

        T::from(opt)
    }

    // SV ops
    pub fn sv_iv(&mut self, sv: Bare<SV>) -> IV {
        Full::from_bare(self.pthx, sv).to_iv()
    }
}
<|endoftext|>


<commit_before>// (C)opyleft 2013,2014 Frank Denis

/*!
 * Bloom filter for Rust
 *
 * This is a simple but fast Bloom filter implementation, that requires only
 * 2 hash functions, generated with SipHash-2-4 using randomized keys.
 */

#![desc = "A fast Bloom filter implementation."]
#![license = "BSD"]
#![crate_name="bloomfilter"]
#![crate_type = "rlib"]
#![warn(non_camel_case_types, non_uppercase_statics, unnecessary_qualification)]

extern crate collections;

use std::cmp;
use std::f64;
use std::hash::Hash;
use std::hash::sip::SipHasher;
use std::hash::Hasher;
use std::rand;
use collections::bitv;

#[cfg(test)]
use std::rand::Rng;

/// Bloom filter structure
pub struct Bloom {
    bitmap: bitv::Bitv,
    bitmap_bits: u64,
    k_num: uint,
    sips: [SipHasher, ..2]
}

impl Bloom {
/// Create a new bloom filter structure.
/// bitmap_size is the size in bytes (not bits) that will be allocated in memory
/// items_count is an estimation of the maximum number of items to store.
    pub fn new(bitmap_size: uint, items_count: uint) -> Bloom {
        assert!(bitmap_size > 0u && items_count > 0u);
        let bitmap_bits = (bitmap_size as u64) * 8u64;
        let k_num = Bloom::optimal_k_num(bitmap_bits, items_count);
        let bitmap = bitv::Bitv::with_capacity(bitmap_bits as uint, false);
        let sips = [ Bloom::sip_new(), Bloom::sip_new() ];
        Bloom {
            bitmap: bitmap,
            bitmap_bits: bitmap_bits,
            k_num: k_num,
            sips: sips
        }
    }

/// Create a new bloom filter structure.
/// items_count is an estimation of the maximum number of items to store.
/// fp_p is the wanted rate of false positives, in ]0.0, 1.0[
    pub fn new_for_fp_rate(items_count: uint, fp_p: f64) -> Bloom {
        let bitmap_size = Bloom::compute_bitmap_size(items_count, fp_p);
        Bloom::new(bitmap_size, items_count)
    }

/// Compute a recommended bitmap size for items_count items
/// and a fp_p rate of false positives.
/// fp_p obviously has to be within the ]0.0, 1.0[ range.
    pub fn compute_bitmap_size(items_count: uint, fp_p: f64) -> uint {
        assert!(items_count > 0u);
        assert!(fp_p > 0.0 && fp_p < 1.0);
        let log2 = f64::consts::LN_2;
        let log2_2 = log2 * log2;
        ((items_count as f64) * fp_p.ln() / (-8.0 * log2_2)).ceil() as uint
    }

/// Record the presence of an item.
    pub fn set<T: Hash>(& mut self, item: T) {
        let mut hashes = [ 0u64, 0u64 ];
        for k_i in range(0u, self.k_num) {
            let bit_offset = (self.bloom_hash(& mut hashes, &item, k_i)
                              % self.bitmap_bits) as uint;
            self.bitmap.set(bit_offset, true);
        }
    }

/// Check if an item is present in the set.
/// There can be false positives, but no false negatives.
    pub fn check<T: Hash>(&self, item: T) -> bool {
        let mut hashes = [ 0u64, 0u64 ];
        for k_i in range(0u, self.k_num) {
            let bit_offset = (self.bloom_hash(& mut hashes, &item, k_i)
                              % self.bitmap_bits) as uint;
            if self.bitmap.get(bit_offset) == false {
                return false;
            }
        }
        true
    }

/// Record the presence of an item in the set,
/// and return the previous state of this item.
    pub fn check_and_set<T: Hash>(&mut self, item: T) -> bool {
        let mut hashes = [ 0u64, 0u64 ];
        let mut found = true;
        for k_i in range(0u, self.k_num) {
            let bit_offset = (self.bloom_hash(& mut hashes, &item, k_i)
                              % self.bitmap_bits) as uint;
            if self.bitmap.get(bit_offset) == false {
                found = false;
                self.bitmap.set(bit_offset, true);
            }
        }
        found
    }

    fn optimal_k_num(bitmap_bits: u64, items_count: uint) -> uint {
        let m = bitmap_bits as f64;
        let n = items_count as f64;
        let k_num = (m / n * 2.0f64.ln()).ceil() as uint;
        cmp::max(k_num, 1)
    }

    fn bloom_hash<T: Hash>(&self, hashes: & mut [u64, ..2],
                  item: &T, k_i: uint) -> u64 {
        if k_i < 2 {
            let sip = self.sips[k_i];
            let hash = sip.hash(item);
            hashes[k_i] = hash;
            hash
        } else {
            hashes[0] + (((k_i as u64) * hashes[1]) % 0xffffffffffffffc5)
        }
    }

    fn sip_new() -> SipHasher {
        let mut rng = rand::task_rng();
        SipHasher::new_with_keys(rand::Rand::rand(& mut rng),
                                 rand::Rand::rand(& mut rng))
    }
}

#[test]
fn bloom_test_set() {
    let mut bloom = Bloom::new(10, 80);
    let key: &Vec<u8> = &rand::task_rng().gen_iter::<u8>().take(16u).collect();
    assert!(bloom.check(key) == false);
    bloom.set(&key);
    assert!(bloom.check(key.clone()) == true);
}

#[test]
fn bloom_test_check_and_set() {
    let mut bloom = Bloom::new(10, 80);
    let key: &Vec<u8> = &rand::task_rng().gen_iter::<u8>().take(16u).collect();
    assert!(bloom.check_and_set(key) == false);
    assert!(bloom.check_and_set(key.clone()) == true);
}
<commit_msg>Added accessors for number of bits and number of hash functions.<commit_after>// (C)opyleft 2013,2014 Frank Denis

/*!
 * Bloom filter for Rust
 *
 * This is a simple but fast Bloom filter implementation, that requires only
 * 2 hash functions, generated with SipHash-2-4 using randomized keys.
 */

#![desc = "A fast Bloom filter implementation."]
#![license = "BSD"]
#![crate_name="bloomfilter"]
#![crate_type = "rlib"]
#![warn(non_camel_case_types, non_uppercase_statics, unnecessary_qualification)]

extern crate collections;

use std::cmp;
use std::f64;
use std::hash::Hash;
use std::hash::sip::SipHasher;
use std::hash::Hasher;
use std::rand;
use collections::bitv;

#[cfg(test)]
use std::rand::Rng;

/// Bloom filter structure
pub struct Bloom {
    bitmap: bitv::Bitv,
    bitmap_bits: u64,
    k_num: uint,
    sips: [SipHasher, ..2]
}

impl Bloom {
/// Create a new bloom filter structure.
/// bitmap_size is the size in bytes (not bits) that will be allocated in memory
/// items_count is an estimation of the maximum number of items to store.
    pub fn new(bitmap_size: uint, items_count: uint) -> Bloom {
        assert!(bitmap_size > 0u && items_count > 0u);
        let bitmap_bits = (bitmap_size as u64) * 8u64;
        let k_num = Bloom::optimal_k_num(bitmap_bits, items_count);
        let bitmap = bitv::Bitv::with_capacity(bitmap_bits as uint, false);
        let sips = [ Bloom::sip_new(), Bloom::sip_new() ];
        Bloom {
            bitmap: bitmap,
            bitmap_bits: bitmap_bits,
            k_num: k_num,
            sips: sips
        }
    }

/// Create a new bloom filter structure.
/// items_count is an estimation of the maximum number of items to store.
/// fp_p is the wanted rate of false positives, in ]0.0, 1.0[
    pub fn new_for_fp_rate(items_count: uint, fp_p: f64) -> Bloom {
        let bitmap_size = Bloom::compute_bitmap_size(items_count, fp_p);
        Bloom::new(bitmap_size, items_count)
    }

/// Compute a recommended bitmap size for items_count items
/// and a fp_p rate of false positives.
/// fp_p obviously has to be within the ]0.0, 1.0[ range.
    pub fn compute_bitmap_size(items_count: uint, fp_p: f64) -> uint {
        assert!(items_count > 0u);
        assert!(fp_p > 0.0 && fp_p < 1.0);
        let log2 = f64::consts::LN_2;
        let log2_2 = log2 * log2;
        ((items_count as f64) * fp_p.ln() / (-8.0 * log2_2)).ceil() as uint
    }

/// Record the presence of an item.
    pub fn set<T: Hash>(& mut self, item: T) {
        let mut hashes = [ 0u64, 0u64 ];
        for k_i in range(0u, self.k_num) {
            let bit_offset = (self.bloom_hash(& mut hashes, &item, k_i)
                              % self.bitmap_bits) as uint;
            self.bitmap.set(bit_offset, true);
        }
    }

/// Check if an item is present in the set.
/// There can be false positives, but no false negatives.
    pub fn check<T: Hash>(&self, item: T) -> bool {
        let mut hashes = [ 0u64, 0u64 ];
        for k_i in range(0u, self.k_num) {
            let bit_offset = (self.bloom_hash(& mut hashes, &item, k_i)
                              % self.bitmap_bits) as uint;
            if self.bitmap.get(bit_offset) == false {
                return false;
            }
        }
        true
    }

/// Record the presence of an item in the set,
/// and return the previous state of this item.
    pub fn check_and_set<T: Hash>(&mut self, item: T) -> bool {
        let mut hashes = [ 0u64, 0u64 ];
        let mut found = true;
        for k_i in range(0u, self.k_num) {
            let bit_offset = (self.bloom_hash(& mut hashes, &item, k_i)
                              % self.bitmap_bits) as uint;
            if self.bitmap.get(bit_offset) == false {
                found = false;
                self.bitmap.set(bit_offset, true);
            }
        }
        found
    }

/// Return the number of bits in the filter
    pub fn number_of_bits(&self) -> u64 {
        self.bitmap_bits
    }

/// Return the number of hash functions used for `check` and `set`
    pub fn number_of_hash_functions(&self) -> uint {
        self.k_num
    }

    fn optimal_k_num(bitmap_bits: u64, items_count: uint) -> uint {
        let m = bitmap_bits as f64;
        let n = items_count as f64;
        let k_num = (m / n * 2.0f64.ln()).ceil() as uint;
        cmp::max(k_num, 1)
    }

    fn bloom_hash<T: Hash>(&self, hashes: & mut [u64, ..2],
                  item: &T, k_i: uint) -> u64 {
        if k_i < 2 {
            let sip = self.sips[k_i];
            let hash = sip.hash(item);
            hashes[k_i] = hash;
            hash
        } else {
            hashes[0] + (((k_i as u64) * hashes[1]) % 0xffffffffffffffc5)
        }
    }

    fn sip_new() -> SipHasher {
        let mut rng = rand::task_rng();
        SipHasher::new_with_keys(rand::Rand::rand(& mut rng),
                                 rand::Rand::rand(& mut rng))
    }
}

#[test]
fn bloom_test_set() {
    let mut bloom = Bloom::new(10, 80);
    let key: &Vec<u8> = &rand::task_rng().gen_iter::<u8>().take(16u).collect();
    assert!(bloom.check(key) == false);
    bloom.set(&key);
    assert!(bloom.check(key.clone()) == true);
}

#[test]
fn bloom_test_check_and_set() {
    let mut bloom = Bloom::new(10, 80);
    let key: &Vec<u8> = &rand::task_rng().gen_iter::<u8>().take(16u).collect();
    assert!(bloom.check_and_set(key) == false);
    assert!(bloom.check_and_set(key.clone()) == true);
}
<|endoftext|>


<commit_before>use types::RispType;
use types::RispType::*;
use types::RispError;
use types::*;
use std::collections::HashMap;

impl Into<Result<i64, RispError>> for RispType {
    fn into(self) -> Result<i64, RispError> {
        match self {
            Int(int) => Ok(int),
            _ => Err(error(format!("Expected Int but got {:?}", self))),
        }
    }
}

impl Into<Result<String, RispError>> for RispType {
    fn into(self) -> Result<String, RispError> {
        match self {
            Str(s) => Ok(s),
            _ => Err(error(format!("Expected String but got {:?}", self))),
        }
    }
}

impl Into<Result<HashMap<String, RispType>, RispError>> for RispType {
    fn into(self) -> Result<HashMap<String, RispType>, RispError> {
        match self {
            Map(map) => Ok(map),
            _ => Err(error(format!("Expected Map but got {:?}", self)))
        }
    }
}

impl Into<Result<Vec<RispType>, RispError>> for RispType {
    fn into(self) -> Result<Vec<RispType>, RispError> {
        match self {
            Vector(vector) => Ok(vector),
            _ => Err(error(format!("Expected Vector but got {:?}", self)))
        }
    }
}

fn into_vec(risp_value: RispType) -> Result<Vec<RispType>, RispError> {
    match risp_value {
        Vector(vector) => Ok(vector),
        _ => Err(error(format!("Expected Vector but got {:?}", risp_value)))
    }
}

impl Into<Result<Vec<i64>, RispError>> for RispType {
    fn into(self) -> Result<Vec<i64>, RispError> {
        into_vec(self)?.iter().cloned()
            .map(|el| el.into())
            .collect::<Result<Vec<i64>, _>>()
    }
}

impl RispType {
    pub fn get<T>(&self, key: &str) -> Result<Option<T>, RispError> where RispType: Into<Result<T, RispError>> {
        match *self {
            Map(ref map) => {
                match map.get(key).cloned() {
                    Some(risp_value) => {
                        Ok(Some(risp_value.into()?))
                    }
                    None => Ok(None)
                }
            }
            _ => Err(error(format!("Expected Map but got {:?}", self)))
        }
    }
}

/* ------------------------------ Tests ----------------------------------------------- */

#[test]
fn test_convert_int() {
    let result: Result<i64, _> = Int(3).into();
    assert_eq!(result, Ok(3));
}

#[test]
fn test_convert_int_error() {
    let result: Result<i64, RispError> = List(vec![]).into();
    assert!(result.is_err());
}

#[test]
fn test_convert_string() {
    let result: Result<String, _> = string("string").into();
    assert_eq!(result, Ok("string".to_string()));
}

#[test]
fn test_convert_string_error() {
    let result: Result<String, RispError> = List(vec![]).into();
    assert!(result.is_err());
}

#[test]
fn test_convert_vector() {
    let result: Result<Vec<_>, RispError> = Vector(vec![Int(23)]).into();
    assert_eq!(result, Ok(vec![Int(23)]));
}

#[test]
fn test_convert_vector_error() {
    let result: Result<Vec<RispType>, RispError> = Int(1).into();
    assert!(result.is_err());
}

#[test]
fn test_convert_vector_int() {
    let result: Result<Vec<i64>, RispError> = Vector(vec![Int(23)]).into();
    assert_eq!(result, Ok(vec![23]));
}

#[test]
fn test_convert_map() {
    let input_map = map(vec![
        ("key", Int(23))
    ]);
    let result: Result<HashMap<String, RispType>, RispError> = input_map.into();
    assert_eq!(result.unwrap().get("key").unwrap().clone(), Int(23));
}

#[test]
fn test_convert_map_error() {
    let result: Result<HashMap<String, RispType>, RispError> = List(vec![]).into();
    assert!(result.is_err());
}

#[test]
fn test_get() {
    let input_map = map(vec![
        ("key", Int(23))
    ]);
    let int_option = input_map.get("key").unwrap();
    assert_eq!(int_option, Some(23));
}

#[test]
fn test_get_error() {
    let input_map = map(vec![
        ("key", string("string"))
    ]);
    let int_result: Result<Option<i64>, _> = input_map.get("key");
    assert!(int_result.is_err());
}
<commit_msg>Added more tests for the convert module<commit_after>use types::RispType;
use types::RispType::*;
use types::RispError;
use types::*;
use std::collections::HashMap;

impl Into<Result<i64, RispError>> for RispType {
    fn into(self) -> Result<i64, RispError> {
        match self {
            Int(int) => Ok(int),
            _ => Err(error(format!("Expected Int but got {:?}", self))),
        }
    }
}

impl Into<Result<String, RispError>> for RispType {
    fn into(self) -> Result<String, RispError> {
        match self {
            Str(s) => Ok(s),
            _ => Err(error(format!("Expected String but got {:?}", self))),
        }
    }
}

impl Into<Result<HashMap<String, RispType>, RispError>> for RispType {
    fn into(self) -> Result<HashMap<String, RispType>, RispError> {
        match self {
            Map(map) => Ok(map),
            _ => Err(error(format!("Expected Map but got {:?}", self)))
        }
    }
}

impl Into<Result<Vec<RispType>, RispError>> for RispType {
    fn into(self) -> Result<Vec<RispType>, RispError> {
        match self {
            Vector(vector) => Ok(vector),
            _ => Err(error(format!("Expected Vector but got {:?}", self)))
        }
    }
}

impl Into<Result<Vec<i64>, RispError>> for RispType {
    fn into(self) -> Result<Vec<i64>, RispError> {
        let vec_of_risp: Result<Vec<RispType>,_> = self.into();
        vec_of_risp?.iter().cloned()
            .map(|el| el.into())
            .collect::<Result<Vec<i64>, _>>()
    }
}

impl RispType {
    pub fn get<T>(&self, key: &str) -> Result<Option<T>, RispError> where RispType: Into<Result<T, RispError>> {
        match *self {
            Map(ref map) => {
                match map.get(key).cloned() {
                    Some(risp_value) => {
                        Ok(Some(risp_value.into()?))
                    }
                    None => Ok(None)
                }
            }
            _ => Err(error(format!("Expected Map but got {:?}", self)))
        }
    }
}

/* ------------------------------ Tests ----------------------------------------------- */

#[test]
fn test_convert_int() {
    let result: Result<i64, _> = Int(3).into();
    assert_eq!(result, Ok(3));
}

#[test]
fn test_convert_int_error() {
    let result: Result<i64, RispError> = List(vec![]).into();
    assert!(result.is_err());
}

#[test]
fn test_convert_string() {
    let result: Result<String, _> = string("string").into();
    assert_eq!(result, Ok("string".to_string()));
}

#[test]
fn test_convert_string_error() {
    let result: Result<String, RispError> = List(vec![]).into();
    assert!(result.is_err());
}

#[test]
fn test_convert_vector() {
    let result: Result<Vec<_>, RispError> = Vector(vec![Int(23)]).into();
    assert_eq!(result, Ok(vec![Int(23)]));
}

#[test]
fn test_convert_vector_error() {
    let result: Result<Vec<RispType>, RispError> = Int(1).into();
    assert!(result.is_err());
}

#[test]
fn test_convert_vector_int() {
    let result: Result<Vec<i64>, RispError> = Vector(vec![Int(23)]).into();
    assert_eq!(result, Ok(vec![23]));
}

#[test]
fn test_convert_map() {
    let input_map = map(vec![
        ("key", Int(23))
    ]);
    let result: Result<HashMap<String, RispType>, RispError> = input_map.into();
    assert_eq!(result.unwrap().get("key").unwrap().clone(), Int(23));
}

#[test]
fn test_convert_map_error() {
    let result: Result<HashMap<String, RispType>, RispError> = List(vec![]).into();
    assert_eq!(result, Err(error(format!("Expected Map but got List([])"))));
}

#[test]
fn test_get() {
    let input_map = map(vec![
        ("key", Int(23))
    ]);
    let int_option = input_map.get("key").unwrap();
    assert_eq!(int_option, Some(23));
}

#[test]
fn test_get_none() {
    let input_map = map(vec![
        ("key", Int(23))
    ]);
    let int_option: Option<i64> = input_map.get("unknown_key").unwrap();
    assert_eq!(int_option, None);
}

#[test]
fn test_get_error_expected_int() {
    let input_map = map(vec![
        ("key", string("string"))
    ]);
    let int_result: Result<Option<i64>, _> = input_map.get("key");
    assert_eq!(int_result, Err(error(format!("Expected Int but got {:?}", string("string")))));
}

#[test]
fn test_get_error_expected_map() {
    let input = Int(123);
    let int_result: Result<Option<i64>, _> = input.get("key");
    assert_eq!(int_result, Err(error(format!("Expected Map but got Int(123)"))));
}
<|endoftext|>


<commit_before>use futures_core::future::Future;
use futures_core::task::{Context, Poll};
use pin_utils::{unsafe_pinned, unsafe_unpinned};
use std::marker::PhantomPinned;
use std::pin::Pin;
use std::ptr;
use std::thread::panicking;

/// Combinator for the
/// [`FutureTestExt::assert_unmoved`](super::FutureTestExt::assert_unmoved)
/// method.
#[derive(Debug, Clone)]
#[must_use = "futures do nothing unless you `.await` or poll them"]
pub struct AssertUnmoved<Fut> {
    future: Fut,
    this_ptr: *const AssertUnmoved<Fut>,
    _pinned: PhantomPinned,
}

// Safety: having a raw pointer in a struct makes it `!Send`, however the
// pointer is never dereferenced so this is safe.
unsafe impl<Fut: Sync + Send> Send for AssertUnmoved<Fut> {}
unsafe impl<Fut: Sync + Send> Sync for AssertUnmoved<Fut> {}

impl<Fut> AssertUnmoved<Fut> {
    unsafe_pinned!(future: Fut);
    unsafe_unpinned!(this_ptr: *const Self);

    pub(super) fn new(future: Fut) -> Self {
        Self {
            future,
            this_ptr: ptr::null(),
            _pinned: PhantomPinned,
        }
    }
}

impl<Fut: Future> Future for AssertUnmoved<Fut> {
    type Output = Fut::Output;

    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        let cur_this = &*self as *const Self;
        if self.this_ptr.is_null() {
            // First time being polled
            *self.as_mut().this_ptr() = cur_this;
        } else {
            assert_eq!(self.this_ptr, cur_this, "Future moved between poll calls");
        }
        self.as_mut().future().poll(cx)
    }
}

impl<Fut> Drop for AssertUnmoved<Fut> {
    fn drop(&mut self) {
        // If the thread is panicking then we can't panic again as that will
        // cause the process to be aborted.
        if !panicking() && !self.this_ptr.is_null() {
            let cur_this = &*self as *const Self;
            assert_eq!(self.this_ptr, cur_this, "Future moved before drop");
        }
    }
}

#[cfg(test)]
mod tests {
    use futures_core::future::Future;
    use futures_core::task::{Context, Poll};
    use futures_util::future::pending;
    use futures_util::task::noop_waker;
    use std::pin::Pin;

    use super::AssertUnmoved;

    #[test]
    fn assert_send_sync() {
        fn assert<T: Send + Sync>() {}
        assert::<AssertUnmoved<()>>();
    }

    #[test]
    fn dont_panic_when_not_polled() {
        // This shouldn't panic.
        let future = AssertUnmoved::new(pending::<()>());
        drop(future);
    }

    #[test]
    #[should_panic(expected = "Future moved between poll calls")]
    fn dont_double_panic() {
        // This test should only panic, not abort the process.
        let waker = noop_waker();
        let mut cx = Context::from_waker(&waker);

        // First we allocate the future on the stack and poll it.
        let mut future = AssertUnmoved::new(pending::<()>());
        let pinned_future = unsafe { Pin::new_unchecked(&mut future) };
        assert_eq!(pinned_future.poll(&mut cx), Poll::Pending);

        // Next we move it back to the heap and poll it again. This second call
        // should panic (as the future is moved), but we shouldn't panic again
        // whilst dropping `AssertUnmoved`.
        let mut future = Box::new(future);
        let pinned_boxed_future = unsafe { Pin::new_unchecked(&mut *future) };
        assert_eq!(pinned_boxed_future.poll(&mut cx), Poll::Pending);
    }
}
<commit_msg>Drop unneeded Send/Sync bound from AssertUnmoved<commit_after>use futures_core::future::Future;
use futures_core::task::{Context, Poll};
use pin_utils::{unsafe_pinned, unsafe_unpinned};
use std::marker::PhantomPinned;
use std::pin::Pin;
use std::ptr;
use std::thread::panicking;

/// Combinator for the
/// [`FutureTestExt::assert_unmoved`](super::FutureTestExt::assert_unmoved)
/// method.
#[derive(Debug, Clone)]
#[must_use = "futures do nothing unless you `.await` or poll them"]
pub struct AssertUnmoved<Fut> {
    future: Fut,
    this_ptr: *const AssertUnmoved<Fut>,
    _pinned: PhantomPinned,
}

// Safety: having a raw pointer in a struct makes it `!Send`, however the
// pointer is never dereferenced so this is safe.
unsafe impl<Fut: Send> Send for AssertUnmoved<Fut> {}
unsafe impl<Fut: Sync> Sync for AssertUnmoved<Fut> {}

impl<Fut> AssertUnmoved<Fut> {
    unsafe_pinned!(future: Fut);
    unsafe_unpinned!(this_ptr: *const Self);

    pub(super) fn new(future: Fut) -> Self {
        Self {
            future,
            this_ptr: ptr::null(),
            _pinned: PhantomPinned,
        }
    }
}

impl<Fut: Future> Future for AssertUnmoved<Fut> {
    type Output = Fut::Output;

    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        let cur_this = &*self as *const Self;
        if self.this_ptr.is_null() {
            // First time being polled
            *self.as_mut().this_ptr() = cur_this;
        } else {
            assert_eq!(self.this_ptr, cur_this, "Future moved between poll calls");
        }
        self.as_mut().future().poll(cx)
    }
}

impl<Fut> Drop for AssertUnmoved<Fut> {
    fn drop(&mut self) {
        // If the thread is panicking then we can't panic again as that will
        // cause the process to be aborted.
        if !panicking() && !self.this_ptr.is_null() {
            let cur_this = &*self as *const Self;
            assert_eq!(self.this_ptr, cur_this, "Future moved before drop");
        }
    }
}

#[cfg(test)]
mod tests {
    use futures_core::future::Future;
    use futures_core::task::{Context, Poll};
    use futures_util::future::pending;
    use futures_util::task::noop_waker;
    use std::pin::Pin;

    use super::AssertUnmoved;

    #[test]
    fn assert_send_sync() {
        fn assert<T: Send + Sync>() {}
        assert::<AssertUnmoved<()>>();
    }

    #[test]
    fn dont_panic_when_not_polled() {
        // This shouldn't panic.
        let future = AssertUnmoved::new(pending::<()>());
        drop(future);
    }

    #[test]
    #[should_panic(expected = "Future moved between poll calls")]
    fn dont_double_panic() {
        // This test should only panic, not abort the process.
        let waker = noop_waker();
        let mut cx = Context::from_waker(&waker);

        // First we allocate the future on the stack and poll it.
        let mut future = AssertUnmoved::new(pending::<()>());
        let pinned_future = unsafe { Pin::new_unchecked(&mut future) };
        assert_eq!(pinned_future.poll(&mut cx), Poll::Pending);

        // Next we move it back to the heap and poll it again. This second call
        // should panic (as the future is moved), but we shouldn't panic again
        // whilst dropping `AssertUnmoved`.
        let mut future = Box::new(future);
        let pinned_boxed_future = unsafe { Pin::new_unchecked(&mut *future) };
        assert_eq!(pinned_boxed_future.poll(&mut cx), Poll::Pending);
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Command-line interface of the rustbuild build system.
//!
//! This module implements the command-line parsing of the build system which
//! has various flags to configure how it's run.

use std::env;
use std::fs;
use std::path::PathBuf;
use std::process;

use getopts::{Matches, Options};

use Build;
use config::Config;
use metadata;
use step;

/// Deserialized version of all flags for this compile.
pub struct Flags {
    pub verbose: usize, // verbosity level: 0 == not verbose, 1 == verbose, 2 == very verbose
    pub on_fail: Option<String>,
    pub stage: Option<u32>,
    pub keep_stage: Option<u32>,
    pub build: String,
    pub host: Vec<String>,
    pub target: Vec<String>,
    pub config: Option<PathBuf>,
    pub src: Option<PathBuf>,
    pub jobs: Option<u32>,
    pub cmd: Subcommand,
    pub incremental: bool,
}

impl Flags {
    pub fn verbose(&self) -> bool {
        self.verbose > 0
    }

    pub fn very_verbose(&self) -> bool {
        self.verbose > 1
    }
}

pub enum Subcommand {
    Build {
        paths: Vec<PathBuf>,
    },
    Doc {
        paths: Vec<PathBuf>,
    },
    Test {
        paths: Vec<PathBuf>,
        test_args: Vec<String>,
    },
    Bench {
        paths: Vec<PathBuf>,
        test_args: Vec<String>,
    },
    Clean,
    Dist {
        paths: Vec<PathBuf>,
        install: bool,
    },
}

impl Flags {
    pub fn parse(args: &[String]) -> Flags {
        let mut opts = Options::new();
        opts.optflagmulti("v", "verbose", "use verbose output (-vv for very verbose)");
        opts.optflag("i", "incremental", "use incremental compilation");
        opts.optopt("", "config", "TOML configuration file for build", "FILE");
        opts.optopt("", "build", "build target of the stage0 compiler", "BUILD");
        opts.optmulti("", "host", "host targets to build", "HOST");
        opts.optmulti("", "target", "target targets to build", "TARGET");
        opts.optopt("", "on-fail", "command to run on failure", "CMD");
        opts.optopt("", "stage", "stage to build", "N");
        opts.optopt("", "keep-stage", "stage to keep without recompiling", "N");
        opts.optopt("", "src", "path to the root of the rust checkout", "DIR");
        opts.optopt("j", "jobs", "number of jobs to run in parallel", "JOBS");
        opts.optflag("h", "help", "print this help message");

        let usage = |exit_code, opts: &Options| -> ! {
            let subcommand_help = format!("\
Usage: x.py <subcommand> [options] [<paths>...]

Subcommands:
    build       Compile either the compiler or libraries
    test        Build and run some test suites
    bench       Build and run some benchmarks
    doc         Build documentation
    clean       Clean out build directories
    dist        Build and/or install distribution artifacts

To learn more about a subcommand, run `./x.py <subcommand> -h`");

            println!("{}", opts.usage(&subcommand_help));

            let subcommand = args.get(0).map(|s| &**s);
            match subcommand {
                Some("build") => {
                    println!("\
Arguments:
    This subcommand accepts a number of paths to directories to the crates
    and/or artifacts to compile. For example:

        ./x.py build src/libcore
        ./x.py build src/libcore src/libproc_macro
        ./x.py build src/libstd --stage 1

    If no arguments are passed then the complete artifacts for that stage are
    also compiled.

        ./x.py build
        ./x.py build --stage 1

    For a quick build with a usable compile, you can pass:

        ./x.py build --stage 1 src/libtest
");
                }

                Some("test") => {
                    println!("\
Arguments:
    This subcommand accepts a number of paths to directories to tests that
    should be compiled and run. For example:

        ./x.py test src/test/run-pass
        ./x.py test src/libstd --test-args hash_map
        ./x.py test src/libstd --stage 0

    If no arguments are passed then the complete artifacts for that stage are
    compiled and tested.

        ./x.py test
        ./x.py test --stage 1
");
                }

                Some("doc") => {
                    println!("\
Arguments:
    This subcommand accepts a number of paths to directories of documentation
    to build. For example:

        ./x.py doc src/doc/book
        ./x.py doc src/doc/nomicon
        ./x.py doc src/doc/book src/libstd

    If no arguments are passed then everything is documented:

        ./x.py doc
        ./x.py doc --stage 1
");
                }

                _ => {}
            }

            if let Some(subcommand) = subcommand {
                if subcommand == "build" ||
                   subcommand == "test" ||
                   subcommand == "bench" ||
                   subcommand == "doc" ||
                   subcommand == "clean" ||
                   subcommand == "dist"  {
                    if args.iter().any(|a| a == "-v") {
                        let flags = Flags::parse(&["build".to_string()]);
                        let mut config = Config::default();
                        config.build = flags.build.clone();
                        let mut build = Build::new(flags, config);
                        metadata::build(&mut build);
                        step::build_rules(&build).print_help(subcommand);
                    } else {
                        println!("Run `./x.py {} -h -v` to see a list of available paths.",
                                 subcommand);
                    }

                    println!("");
                }
            }

            process::exit(exit_code);
        };
        if args.len() == 0 {
            println!("a subcommand must be passed");
            usage(1, &opts);
        }
        let parse = |opts: &Options| {
            let m = opts.parse(&args[1..]).unwrap_or_else(|e| {
                println!("failed to parse options: {}", e);
                usage(1, opts);
            });
            if m.opt_present("h") {
                usage(0, opts);
            }
            return m
        };

        let cwd = t!(env::current_dir());
        let remaining_as_path = |m: &Matches| {
            m.free.iter().map(|p| cwd.join(p)).collect::<Vec<_>>()
        };

        let m: Matches;
        let cmd = match &args[0][..] {
            "build" => {
                m = parse(&opts);
                Subcommand::Build { paths: remaining_as_path(&m) }
            }
            "test" => {
                opts.optmulti("", "test-args", "extra arguments", "ARGS");
                m = parse(&opts);
                Subcommand::Test {
                    paths: remaining_as_path(&m),
                    test_args: m.opt_strs("test-args"),
                }
            }
            "bench" => {
                opts.optmulti("", "test-args", "extra arguments", "ARGS");
                m = parse(&opts);
                Subcommand::Bench {
                    paths: remaining_as_path(&m),
                    test_args: m.opt_strs("test-args"),
                }
            }
            "doc" => {
                m = parse(&opts);
                Subcommand::Doc { paths: remaining_as_path(&m) }
            }
            "clean" => {
                m = parse(&opts);
                if m.free.len() > 0 {
                    println!("clean takes no arguments");
                    usage(1, &opts);
                }
                Subcommand::Clean
            }
            "dist" => {
                opts.optflag("", "install", "run installer as well");
                m = parse(&opts);
                Subcommand::Dist {
                    paths: remaining_as_path(&m),
                    install: m.opt_present("install"),
                }
            }
            "--help" => usage(0, &opts),
            _ => {
                usage(1, &opts);
            }
        };

        let cfg_file = m.opt_str("config").map(PathBuf::from).or_else(|| {
            if fs::metadata("config.toml").is_ok() {
                Some(PathBuf::from("config.toml"))
            } else {
                None
            }
        });

        let mut stage = m.opt_str("stage").map(|j| j.parse().unwrap());

        let incremental = m.opt_present("i");

        if incremental {
            if stage.is_none() {
                stage = Some(1);
            }
        }

        Flags {
            verbose: m.opt_count("v"),
            stage: stage,
            on_fail: m.opt_str("on-fail"),
            keep_stage: m.opt_str("keep-stage").map(|j| j.parse().unwrap()),
            build: m.opt_str("build").unwrap_or_else(|| {
                env::var("BUILD").unwrap()
            }),
            host: split(m.opt_strs("host")),
            target: split(m.opt_strs("target")),
            config: cfg_file,
            src: m.opt_str("src").map(PathBuf::from),
            jobs: m.opt_str("jobs").map(|j| j.parse().unwrap()),
            cmd: cmd,
            incremental: incremental,
        }
    }
}

impl Subcommand {
    pub fn test_args(&self) -> Vec<&str> {
        match *self {
            Subcommand::Test { ref test_args, .. } |
            Subcommand::Bench { ref test_args, .. } => {
                test_args.iter().flat_map(|s| s.split_whitespace()).collect()
            }
            _ => Vec::new(),
        }
    }
}

fn split(s: Vec<String>) -> Vec<String> {
    s.iter().flat_map(|s| s.split(',')).map(|s| s.to_string()).collect()
}
<commit_msg>Save my TODO's as comments, so I don't forget.<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Command-line interface of the rustbuild build system.
//!
//! This module implements the command-line parsing of the build system which
//! has various flags to configure how it's run.

use std::env;
use std::fs;
use std::path::PathBuf;
use std::process;

use getopts::{Matches, Options};

use Build;
use config::Config;
use metadata;
use step;

/// Deserialized version of all flags for this compile.
pub struct Flags {
    pub verbose: usize, // verbosity level: 0 == not verbose, 1 == verbose, 2 == very verbose
    pub on_fail: Option<String>,
    pub stage: Option<u32>,
    pub keep_stage: Option<u32>,
    pub build: String,
    pub host: Vec<String>,
    pub target: Vec<String>,
    pub config: Option<PathBuf>,
    pub src: Option<PathBuf>,
    pub jobs: Option<u32>,
    pub cmd: Subcommand,
    pub incremental: bool,
}

impl Flags {
    pub fn verbose(&self) -> bool {
        self.verbose > 0
    }

    pub fn very_verbose(&self) -> bool {
        self.verbose > 1
    }
}

pub enum Subcommand {
    Build {
        paths: Vec<PathBuf>,
    },
    Doc {
        paths: Vec<PathBuf>,
    },
    Test {
        paths: Vec<PathBuf>,
        test_args: Vec<String>,
    },
    Bench {
        paths: Vec<PathBuf>,
        test_args: Vec<String>,
    },
    Clean,
    Dist {
        paths: Vec<PathBuf>,
        install: bool,
    },
}

impl Flags {
    pub fn parse(args: &[String]) -> Flags {
        let mut opts = Options::new();
        opts.optflagmulti("v", "verbose", "use verbose output (-vv for very verbose)");
        opts.optflag("i", "incremental", "use incremental compilation");
        opts.optopt("", "config", "TOML configuration file for build", "FILE");
        opts.optopt("", "build", "build target of the stage0 compiler", "BUILD");
        opts.optmulti("", "host", "host targets to build", "HOST");
        opts.optmulti("", "target", "target targets to build", "TARGET");
        opts.optopt("", "on-fail", "command to run on failure", "CMD");
        opts.optopt("", "stage", "stage to build", "N");
        opts.optopt("", "keep-stage", "stage to keep without recompiling", "N");
        opts.optopt("", "src", "path to the root of the rust checkout", "DIR");
        opts.optopt("j", "jobs", "number of jobs to run in parallel", "JOBS");
        opts.optflag("h", "help", "print this help message");

        let usage = |exit_code, opts: &Options| -> ! {
            let subcommand_help = format!("\
Usage: x.py <subcommand> [options] [<paths>...]

Subcommands:
    build       Compile either the compiler or libraries
    test        Build and run some test suites
    bench       Build and run some benchmarks
    doc         Build documentation
    clean       Clean out build directories
    dist        Build and/or install distribution artifacts

To learn more about a subcommand, run `./x.py <subcommand> -h`");

            println!("{}", opts.usage(&subcommand_help));

            let subcommand = args.get(0).map(|s| &**s);
            match subcommand {
                Some("build") => {
                    println!("\
Arguments:
    This subcommand accepts a number of paths to directories to the crates
    and/or artifacts to compile. For example:

        ./x.py build src/libcore
        ./x.py build src/libcore src/libproc_macro
        ./x.py build src/libstd --stage 1

    If no arguments are passed then the complete artifacts for that stage are
    also compiled.

        ./x.py build
        ./x.py build --stage 1

    For a quick build with a usable compile, you can pass:

        ./x.py build --stage 1 src/libtest
");
                }

                Some("test") => {
                    println!("\
Arguments:
    This subcommand accepts a number of paths to directories to tests that
    should be compiled and run. For example:

        ./x.py test src/test/run-pass
        ./x.py test src/libstd --test-args hash_map
        ./x.py test src/libstd --stage 0

    If no arguments are passed then the complete artifacts for that stage are
    compiled and tested.

        ./x.py test
        ./x.py test --stage 1
");
                }

                Some("doc") => {
                    println!("\
Arguments:
    This subcommand accepts a number of paths to directories of documentation
    to build. For example:

        ./x.py doc src/doc/book
        ./x.py doc src/doc/nomicon
        ./x.py doc src/doc/book src/libstd

    If no arguments are passed then everything is documented:

        ./x.py doc
        ./x.py doc --stage 1
");
                }

                _ => {}
            }

            if let Some(subcommand) = subcommand {
                if subcommand == "build" ||
                   subcommand == "test" ||
                   subcommand == "bench" ||
                   subcommand == "doc" ||
                   subcommand == "clean" ||
                   subcommand == "dist"  {
                    if args.iter().any(|a| a == "-v") {
                        let flags = Flags::parse(&["build".to_string()]);
                        let mut config = Config::default();
                        config.build = flags.build.clone();
                        let mut build = Build::new(flags, config);
                        metadata::build(&mut build);
                        step::build_rules(&build).print_help(subcommand);
                    } else {
                        println!("Run `./x.py {} -h -v` to see a list of available paths.",
                                 subcommand);
                    }

                    println!("");
                }
            }

            process::exit(exit_code);
        };
        if args.len() == 0 {
            println!("a subcommand must be passed");
            usage(1, &opts);
        }
        let parse = |opts: &Options| {
            let m = opts.parse(&args[1..]).unwrap_or_else(|e| {
                println!("failed to parse options: {}", e);
                usage(1, opts);
            });
            if m.opt_present("h") {
                usage(0, opts);
            }
            return m
        };

        let cwd = t!(env::current_dir());
        let remaining_as_path = |m: &Matches| {
            m.free.iter().map(|p| cwd.join(p)).collect::<Vec<_>>()
        };
        // TODO: Parse subcommand nicely up at top, so options can occur before the subcommand.
        // TODO: Get the subcommand-specific options below into the help output

        let m: Matches;
        let cmd = match &args[0][..] {
            "build" => {
                m = parse(&opts);
                Subcommand::Build { paths: remaining_as_path(&m) }
            }
            "test" => {
                opts.optmulti("", "test-args", "extra arguments", "ARGS");
                m = parse(&opts);
                Subcommand::Test {
                    paths: remaining_as_path(&m),
                    test_args: m.opt_strs("test-args"),
                }
            }
            "bench" => {
                opts.optmulti("", "test-args", "extra arguments", "ARGS");
                m = parse(&opts);
                Subcommand::Bench {
                    paths: remaining_as_path(&m),
                    test_args: m.opt_strs("test-args"),
                }
            }
            "doc" => {
                m = parse(&opts);
                Subcommand::Doc { paths: remaining_as_path(&m) }
            }
            "clean" => {
                m = parse(&opts);
                if m.free.len() > 0 {
                    println!("clean takes no arguments");
                    usage(1, &opts);
                }
                Subcommand::Clean
            }
            "dist" => {
                opts.optflag("", "install", "run installer as well");
                m = parse(&opts);
                Subcommand::Dist {
                    paths: remaining_as_path(&m),
                    install: m.opt_present("install"),
                }
            }
            "--help" => usage(0, &opts),
            _ => {
                usage(1, &opts);
            }
        };

        let cfg_file = m.opt_str("config").map(PathBuf::from).or_else(|| {
            if fs::metadata("config.toml").is_ok() {
                Some(PathBuf::from("config.toml"))
            } else {
                None
            }
        });

        let mut stage = m.opt_str("stage").map(|j| j.parse().unwrap());

        let incremental = m.opt_present("i");

        if incremental {
            if stage.is_none() {
                stage = Some(1);
            }
        }

        Flags {
            verbose: m.opt_count("v"),
            stage: stage,
            on_fail: m.opt_str("on-fail"),
            keep_stage: m.opt_str("keep-stage").map(|j| j.parse().unwrap()),
            build: m.opt_str("build").unwrap_or_else(|| {
                env::var("BUILD").unwrap()
            }),
            host: split(m.opt_strs("host")),
            target: split(m.opt_strs("target")),
            config: cfg_file,
            src: m.opt_str("src").map(PathBuf::from),
            jobs: m.opt_str("jobs").map(|j| j.parse().unwrap()),
            cmd: cmd,
            incremental: incremental,
        }
    }
}

impl Subcommand {
    pub fn test_args(&self) -> Vec<&str> {
        match *self {
            Subcommand::Test { ref test_args, .. } |
            Subcommand::Bench { ref test_args, .. } => {
                test_args.iter().flat_map(|s| s.split_whitespace()).collect()
            }
            _ => Vec::new(),
        }
    }
}

fn split(s: Vec<String>) -> Vec<String> {
    s.iter().flat_map(|s| s.split(',')).map(|s| s.to_string()).collect()
}
<|endoftext|>