#![allow(unused_imports)] #![warn(unreachable_pub)] #![forbid(elided_lifetimes_in_paths, unsafe_code)] use bpaf::construct; use indexmap::IndexMap; use indicatif::{ParallelProgressIterator as _, ProgressBar, ProgressStyle}; use rayon::iter::{FromParallelIterator, IntoParallelIterator, ParallelIterator as _}; use stdlib::{ArgValue, OutputPrinterAny, OutputPrinterTwoVars, int}; use std::{io::stdout, path::PathBuf}; #[derive(Clone, Debug)] struct Args { runs: u64, {%- for arg in inputs %} in_{{arg.ident}}: ArgValue, {%- endfor %} {%- for out in outputs %} out{{loop.index0}}: (OutputPrinter {%- if out.idents.len() == 2 %}TwoVars{% else %}Any{% endif -%} , Option), {%- endfor %} } struct Values { {%- for arg in inputs %} {{arg.ident}}: int, {%- endfor %} } impl Args { fn values(&self) -> Values { Values { {%- for arg in inputs %} {{arg.ident}}: self.in_{{arg.ident}}.get_value(), {%- endfor %} } } fn parse() -> Self { let runs = bpaf::short('n') .help("Specify the number of runs (defaults to 1)") .argument("RUNS") .from_str() .fallback(1); {%- for arg in inputs %} let in_{{arg.ident}} = { let value = bpaf::{{arg.short_long}}({{arg.quote}}{{arg.ident}}{{arg.quote}}) .help("Set a value for {{arg.ident}}") .argument("VALUE") .from_str() .map(ArgValue::Value); let min = bpaf::long("{{arg.ident}}-min") .help("Set a range for {{arg.ident}} with this minimum value (inclusive)") .argument("MIN-VALUE") .from_str(); let max = bpaf::long("{{arg.ident}}-max") .help("Set a range for {{arg.ident}} with this maximum value (inclusive)") .argument("MAX-VALUE") .from_str(); let range = construct!(ArgValue::Range { min, max }); value.or_else(range) }; {%- endfor %} {%- for out in outputs %} let out{{loop.index0}} = { let printer = bpaf::long(" {%- for var in out.idents %}{{var}}-{% endfor %}printer") .help("Set the output printer for output ( {%- for var in out.idents %}{{var}}, {% endfor %})") .argument("PRINTER") .from_str() .default(); let path = bpaf::long(" {%- for var in out.idents %}{{var}}-{% endfor %}out") .help("Write the output to a file") .argument("PATH") .from_str() .map(Some) .default(); construct!(printer, path) }; {%- endfor %} let parser = construct!(Self { runs, {%- for arg in inputs %} in_{{arg.ident}}, {%- endfor %} {%- for out in outputs %} out{{loop.index0}} {%- endfor %} }); let info = bpaf::Info::default(); info.for_parser(parser).run() } } /// This is the output captured immediately after running one sample. #[derive(Eq, Hash, PartialEq)] struct Output { {%- for out in outputs %} {%- for var in out.idents %} {{var}}: int, {%- endfor %} {%- endfor %} } {%- for out in outputs %} /// This type is the output of one tuple from one sample. #[derive(Eq, Hash, PartialEq)] struct Output{{loop.index0}}( {%- for var in out.idents %}int, {% endfor -%} ); impl IntoIterator for Output{{loop.index0}} { type Item = (&'static str, int); type IntoIter = OutputIntoIter{{loop.index0}}; fn into_iter(self) -> Self::IntoIter { OutputIntoIter{{loop.index0}} { output: self, idx: 0 } } } impl<'a> IntoIterator for &'a Output{{loop.index0}} { type Item = (&'static str, int); type IntoIter = OutputIter{{loop.index0}}<'a>; fn into_iter(self) -> Self::IntoIter { OutputIter{{loop.index0}} { output: self, idx: 0 } } } fn outputiter{{loop.index0}}_next( output: &Output{{loop.index0}}, idx: &mut usize ) -> Option<(&'static str, int)> { let next = match *idx { {%- for var in out.idents %} {{loop.index0}} => (stringify!({{var}}), output.{{loop.index0}}), {%- endfor %} _ => return None }; *idx += 1; Some(next) } /// This type associates the output values with their variable names. #[doc(hidden)] struct OutputIntoIter{{loop.index0}} { output: Output{{loop.index0}}, idx: usize } impl Iterator for OutputIntoIter{{loop.index0}} { type Item = (&'static str, int); fn next(&mut self) -> Option { outputiter{{loop.index0}}_next(&self.output, &mut self.idx) } } /// This type associates the output values with their variable names. #[doc(hidden)] struct OutputIter{{loop.index0}}<'a> { output: &'a Output{{loop.index0}}, idx: usize } impl Iterator for OutputIter{{loop.index0}}<'_> { type Item = (&'static str, int); fn next(&mut self) -> Option { outputiter{{loop.index0}}_next(self.output, &mut self.idx) } } {%- endfor %} /// This type collects the output from each sample and processes them /// into output values. It cannot use output iterators directly as we /// need an `Eq` implementation. #[derive(Default)] struct OutputEntries { {%- for out in outputs %} entries{{loop.index0}}: IndexMap, {%- endfor %} len: usize } impl FromParallelIterator for OutputEntries { fn from_par_iter(par_iter: I) -> Self where I: IntoParallelIterator { par_iter .into_par_iter() .fold(Self::default, |mut list, {% if outputs.is_empty() %}_{% else %}out{% endif %}| { list.len += 1; {%- for out in outputs %} list.entries{{loop.index0}} .entry(Output{{loop.index0}}( {%- for var in out.idents %}out.{{var}}, {% endfor -%} )) .and_modify(|qty| *qty += 1) .or_insert(1); {%- endfor %} list }) .reduce(Self::default, |mut list, other| { list.len += other.len; {%- for out in outputs %} for (out, count) in other.entries{{loop.index0}} { list.entries{{loop.index0}} .entry(out) .and_modify(|qty| *qty += count) .or_insert(count); } {%- endfor %} list }) } } /// This type is used to divide the output entries into output iterators. struct OutputLists { {%- for out in outputs %} out{{loop.index0}}: stdlib::OutputList< IndexMap >, {%- endfor %} } impl From for OutputLists { {%- if outputs.is_empty() %} #[allow(unused_variables)] {%- endif %} fn from(this: OutputEntries) -> Self { {%- for out in outputs %} let mut out{{loop.index0}} = this.entries{{loop.index0}}; out{{loop.index0}}.sort_unstable_by( |_, lhs_qty, _, rhs_qty| rhs_qty.cmp(lhs_qty) ); {% endfor -%} Self { {%- for out in outputs %} out{{loop.index0}}: stdlib::OutputList::new( out{{loop.index0}}, this.len ), {%- endfor %} } } } mod funs; mod types; fn main() { let args = Args::parse(); let pb = ProgressBar::new(args.runs); pb.set_prefix("Collecting samples ..."); pb.set_style(ProgressStyle::default_bar() .template("{prefix} {percent:>3}% [{wide_bar}] {pos:>7}/{len:7} [{elapsed_precise}<{eta_precise}; {per_sec} samples/s]") .unwrap() .progress_chars("#> ")); {%- if outputs.is_empty() %} #[allow(unused_variables)] {%- endif %} let out: OutputLists = (0 .. args.runs) .into_par_iter() .progress_with(pb) .map(|_| funs::run(args.values())) .collect::() .into(); {%- for out in outputs %} {%- if !loop.first %} println!(); {%- endif %} stdlib::OutputTarget::print(args.out{{loop.index0}}, out.out{{loop.index0}}); {%- endfor %} }