sum calculation on demand and cached
[barcat.git] / barcat
diff --git a/barcat b/barcat
index 08b3db2f9a5d00e5ce6d7efbfecbfdf291fd090a..1fd0dd3e8815c8879569fc3bf0e4fa5c679524d5 100755 (executable)
--- a/barcat
+++ b/barcat
@@ -400,11 +400,9 @@ sub show_stat {
                $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;
@@ -413,17 +411,29 @@ sub show_stat {
 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;
@@ -442,7 +452,7 @@ sub varfmt {
                defined && do {
                        $_ = $opt{'value-format'}->($_) if $format;
                        if ($cmd and $op eq ':') {
-                               $_ = varfmt($cmd, $vars);
+                               $_ = !!$_ && varfmt($cmd, $vars);
                        }
                        elsif ($cmd) {
                                eval $cmd;
@@ -638,10 +648,13 @@ For example C<:/1> for a grid at every integer.
 
 =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>.
+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 below 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<< <--> >>.