#!/usr/bin/perl
# add - to add those numeric columns on stdin, report totals for cols and
#  grand total, plus number of items/col that are addable (numeric)

# (c) 1995-2004 ken chase
# this code is useable/distributable under the GPL liscence

#changelog
# 1995 - first version
# 1997 - added -p option to produce what -p and -r does now
# 2000 feb 25 - added -r to seperate lines and totals, and -h
# 2000 mar 2  - added -t to specify column seperator
# 2001 nov 26 - allowed addition of -ve numbers (not delete lead - sign)
#	      - fixed subtracting columns (-# was conflicting w/getopt parse)
# 2002 nov 26 - made default compression of field seps, -n turns off
#	      - added -P pure numbers switch
#	      - design almost implicitly made negative columns (-1) be count
#	        from right side (ie Nth col is -1), fixed up code to support
# 2004 sep  4 - ignore leading blank chars -i 
#	      - -v verbose option
# 2008 may 29 - fixed so leading .05 becomes 0.05 not '5'

$version = "v0.84 29 may 08 math @ sizone.org";

use Getopt::Long;               # auto parse options

Getopt::Long::Configure ("bundling");		# allow -(opt)(opt)(opt) 
 
# parse options with Getopt::Long module
$Getopt::Long::ignorecase = 0;          # we dont want to ignore option case
$result = GetOptions ("p","r","h", "d", "t=s", "n", "P", "i", "v");

if ($opt_h) {
  print STDERR "$0   $version

usage: $0 (space seperated column list, 0-n)
 adds all numbers in columns (0-n) on command line from STDIN.
   use (column#)- to indicate subtract column instead. (- must be last char)
   use -(column#) to count columns from right side. (5 cols w/ $0 5 -1 will
    produce two totals, one for col 5 and one for col -1, despite same col)

 -p to print each line of input with the output
 -r to give (interleaved with -p) running total
 -d to give debug output column by column (nasty mess, implies -r)
 -t '(char)' to use char as seperator for columns (\\t for tab) (default \\s)
 -n makes split char non-globbing (ie 3 chars = 2 empty fields between)
 -i ignore leading blank seperator chars (ie \"  5\" is field 1 = 5 when -t \" \")
 -v slightly more verbose output

 per column switches (can be used -P (for eg) to apply to all fields, or to
 modify columns such as '1P-' for 2nd column subtraction of only pure numbers
 (note: negative fields have - sign AFTER modifiers)
    
 -P pure numbers - add a field if it consists only of chars (^-|[0-9])
     (note, total number of additions doesnt include skipped impure numbers)
";

  exit 1;
}

if ($opt_v) { print "columns to add: ", join(":", @ARGV), "\n"; }

$gtotal = 0; $gtotalnum = 0;
$splitchars = '\s';
$splitchars = $opt_t if $opt_t;

$opt_r = 1 if $opt_d;			# -r implied by -d

print "Splitting on:`", $opt_t, "`\n" if $opt_t;

while (<STDIN>) {
  chop;

  $line = $_;

  if ($opt_i) { $line =~ s/^$splitchars*//; }

  if ($opt_n) {			# no glob of split chars?
    split(/$splitchars/,$line);
  } else {
    split(/$splitchars+/,$line);
  }

  if ($opt_p) { print $_, "\n"; }

  for $col (@ARGV) {
    $col1 = $col;
    $col1 =~ s/[^0-9-]//g;		# strip non numeric or -
    $col1 =~ s/\-$//g;			# strip trailing -

    $mult = 1; 
    ($col =~ /\-$/) && ($mult = -1);	# substract column instead

    $numbers{$col} ||= 0;		# start row count for col at 0

    print "  Col $col line $numbers{$col}:'", $_[$col1], "'\n" if $opt_d;

    $num = $_[$col1];				# get the number
    $num =~ y/\n//d; $num1 = $num;		# strip \n and make copy
    $num =~ s/^\./0./;				# leading .## is 0.##
    $num =~ s/^[^0-9-]*//;			# erase leading non numeric

    $num = $num * $mult;

    if ( ( ($opt_P || $col =~ /P/) && ($num1 eq abs($num)) ) || 
          !($opt_P || $col =~ /P/) ) {

        $oldtot = $total{$col};			# keep around for debug
        $oldtot ||= 0;
        $total{$col} += $num;
        $numbers{$col}++;
        if ($opt_d) { print "   adding value: '$num'  "; }
        if ($opt_r) { 
          print $numbers{$col}, ": $oldtot + '$num' = ", $total{$col}, " \n";
        }

    } else {
      if ($opt_d) {
        print "   not adding non-pure value: '$num1' ";
      }
    }
    ($opt_d) && (print "\n");
  }
  if ($opt_r) { print "\n"; }
}   

print "\n";
for $col (@ARGV) {
  if ($opt_v) {
    print "(", $col, ":", $numbers{$col}, ") ", $total{$col}, "\t";
  }

  $gtotal += $total{$col};
  $gtotalnum += $numbers{$col};
}

if ($opt_v) { 
  print "\ngrand total for $gtotalnum lines: ";
}

print "$gtotal\n";


# no buffer STDERR output so we can watch progress...
select STDERR;
$| = 1;                         # set no buffering for selected filehandle
select STDOUT;
