use open qw( :std :utf8 );
use re '/msx';
-our $VERSION = '1.09';
+our $VERSION = '1.10';
my %opt;
if (@ARGV) {
$opt{'value-length'} = 4 if $opt{units};
$opt{'value-length'} = 1 if $opt{unmodified};
$opt{'signal-stat'} //= exists $SIG{INFO} ? 'INFO' : 'QUIT';
-$opt{markers} //= '=avg >31.73v <68.27v +50v |0';
+$opt{markers} //= '=avg <31.73v >68.27v +50v |0';
$opt{report} //= join('',
'${partsum+; $_ .= " of "}',
'${sum+; color(1); $_ .= " total in "}',
'${count#} values',
'${lines#; $_ = $_ != @order && " over $_ lines"}',
sprintf('${count: (%s)}', join ', ',
- '${min; color(31)} min',
+ '${0v; color(31)} min',
'${avg; $opt{reformat} or $_ = sprintf "%0.2f", $_; color(36)} avg',
- '${max; color(32)} max',
+ '${100v; color(32)} max',
),
);
$opt{palette} //= $opt{color} && [31, 90, 32];
eval {
require Tie::Array::Sorted;
- tie @order, 'Tie::Array::Sorted', sub { $_[1] <=> $_[0] };
+ tie @order, 'Tie::Array::Sorted', sub { $_[0] <=> $_[1] };
} or warn $@, "Expect slowdown with large datasets!\n"
unless $opt{count};
}
@order = @values;
}
-@order = sort { $b <=> $a } @order unless tied @order;
+@order = sort { $a <=> $b } @order unless tied @order;
my $maxval = $opt{maxval} // (
$opt{hidemax} ? max grep { length } @values[$nr .. $limit] :
- $order[0]
+ $order[-1]
) // 0;
-my $minval = $opt{minval} // min $order[-1] // (), 0;
+my $minval = $opt{minval} // min $order[0] // (), 0;
my $range = $maxval - $minval;
$range &&= log $range if $opt{log};
my $lenval = $opt{'value-length'} // max map { length } @order;
$rel = min(1, $rel / $range) if $range; # 0..1
}
my $color = !length $val || !$opt{palette} ? undef :
- $val == $order[0] ? $opt{palette}->[-1] : # max
- $val == $order[-1] ? $opt{palette}->[0] : # min
+ $val == $order[-1] ? $opt{palette}->[-1] : # max
+ $val == $order[0] ? $opt{palette}->[0] : # min
$opt{palette}->[ $rel * ($#{$opt{palette}} - 1) + 1 ];
my $indicator = $opt{indicators} && $opt{indicators}->[
!length($val) || !$#{$opt{indicators}} ? 0 : # blank
$#{$opt{indicators}} < 2 ? 1 :
- $val >= $order[0] ? -1 :
+ $val >= $order[-1] ? -1 :
$rel * ($#{$opt{indicators}} - 1e-14) + 1
];
if (@order) {
$vars{partsum} = sum(0, grep {length} @values[$linemin .. $linemax])
if $linemin <= $linemax and ($opt{hidemin} or $opt{hidemax});
- %vars = (%vars,
- sum => sum(@order),
- min => $order[-1],
- max => $order[0],
- );
- $vars{avg} = $vars{sum} / @order;
}
say varfmt($opt{report}, \%vars);
return 1;
sub calc {
my ($func) = @_;
if ($func eq 'avg') {
- return sum(@order) / @order;
+ return calc('sum') / @order;
}
elsif ($func eq 'sum') {
- return sum(@order);
+ 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;
- return ($order[$index] + $order[$index + .5]) / 2;
+ 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;
defined && do {
$_ = $opt{'value-format'}->($_) if $format;
if ($cmd and $op eq ':') {
- $_ = varfmt($cmd, $vars);
+ $_ = !!$_ && varfmt($cmd, $vars);
}
elsif ($cmd) {
eval $cmd;
=item I<percentage>B<v>
-Ranked value at the given percentile.
-The default shows C<+> at C<50v> for the mean or median;
-the middle value or average between middle values.
-One standard deviation right of the mean is at about C<68.3v>.
-The default includes C<< >31.73v <68.27v >>
+Ranked value at the given percentile,
+or score at or below which a percentage falls
+in its frequency distribution (inclusive).
+
+The default shows C<+> at C<50v> for the mean or median:
+the middle value or interpolation between two values.
+One standard deviation above the median is at about C<68v>.
+The default includes C<< <31.73v >68.27v >>
to encompass all I<normal> results, or 68% of all entries, by I<< <--> >>.
=item B<avg>