+sub calc {
+ my ($func) = @_;
+ if ($func eq 'avg') {
+ return calc('sum') / @order;
+ }
+ elsif ($func eq 'sum') {
+ state $cache; # avoid recount
+ state $cachednr = 0; # if unchanged
+ unless (@order == $cachednr) {
+ $cache = sum(@order);
+ $cachednr = @order;
+ }
+ return $cache;
+ }
+ elsif ($func =~ /\A([0-9.]+)v\z/) {
+ $1 <= 100 or die(
+ "percentile $1 out of bounds\n"
+ );
+ my $index = $#order * $1 / 100;
+ my $f = $index - int $index;
+ my $val = $order[$index];
+ if ($f) {
+ my $next = $order[$index + 1];
+ $val -= $f * ($val - $next);
+ }
+ return $val;
+ }
+ elsif ($func =~ /\A-?[0-9.]+\z/) {
+ return $func;
+ }
+ else {
+ die "$func unknown\n";
+ }
+}
+
+sub varfmt {
+ my ($fmt, $vars) = @_;
+ $fmt =~ s[\$\{ \h*+ ((?: [^{}]++ | \{(?1)\} )+) \}]{
+ my ($name, $op, $cmd) = split /\s*([;:])/, $1, 2;
+ my $format = $name =~ s/\+// || $name !~ s/\#// && $opt{reformat};
+ local $_ = exists $vars->{$name} ? $vars->{$name} : calc($name);
+ defined && do {
+ $_ = $opt{'value-format'}->($_) if $format;
+ if ($cmd and $op eq ':') {
+ $_ = !!$_ && varfmt($cmd, $vars);
+ }
+ elsif ($cmd) {
+ eval $cmd;
+ warn "Error in \$$name report: $@" if $@;
+ }
+ $_;
+ }
+ }eg;
+ return $fmt;
+}
+