299 lines
7.1 KiB
Text
299 lines
7.1 KiB
Text
|
#![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<PathBuf>),
|
||
|
{%- 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<Self::Item> {
|
||
|
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<Self::Item> {
|
||
|
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<Output{{loop.index0}}, usize>,
|
||
|
{%- endfor %}
|
||
|
len: usize
|
||
|
}
|
||
|
|
||
|
impl FromParallelIterator<Output> for OutputEntries {
|
||
|
fn from_par_iter<I>(par_iter: I) -> Self
|
||
|
where
|
||
|
I: IntoParallelIterator<Item = Output>
|
||
|
{
|
||
|
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<Output{{loop.index0}}, usize>
|
||
|
>,
|
||
|
{%- endfor %}
|
||
|
}
|
||
|
|
||
|
impl From<OutputEntries> 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::<OutputEntries>()
|
||
|
.into();
|
||
|
{%- for out in outputs %}
|
||
|
{%- if !loop.first %}
|
||
|
println!();
|
||
|
{%- endif %}
|
||
|
stdlib::OutputTarget::print(args.out{{loop.index0}}, out.out{{loop.index0}});
|
||
|
{%- endfor %}
|
||
|
}
|