#!/usr/bin/perl

use 5.001 ; use strict ; use warnings ; 
use Getopt::Std ; getopts 'mMP' , \my %o ; 
use Scalar::Util qw[ looks_like_number ] ; 

$o{m} //= $o{M} ; # 3次モーメントと4次モーメントを使った計算をするか否か 
sub lln ( $ ) ; * lln = * looks_like_number ; 

sub reading ( ) ; # 入力から読取り 
sub outbasic ( ) ; # 基本的な情報を出力
sub outMore ( ) ; # 

my $notcount = 0 ; # 計数対象とならなかった個数
my $count = 0 ; # 対象となった件数 
my $sum = 0 ; 
my $sqsum = 0 ; # squared sum  
my $cbsum = 0 ; # 3乗和 cubic 
my $qusum = 0 ; # 4乗和 quartic

reading ; 
outbasic if $o{M} || ! $o{m} ;
print "\n" if $o{M} ; 
outMore if $o{m} ; 
exit 0 ;

sub reading () { 
    while ( <> )  { 
        chomp ; 
        do { $notcount += 1 ; next } if $o{P} && ! looks_like_number $_  ;
        
        $count += 1 ; 
        $_ += 0 ; # baseed on `perldoc POSIX`'s strtod advice. 
        $sum += $_ ; 
        $sqsum += $_ ** 2 ; 
        next if ! $o{m} ;
        $cbsum += $_ ** 3 ; 
        $qusum += $_ ** 4 ;
    }
} 

sub outbasic () { 
    my $sqres = $sqsum - $sum ** 2 / $count ; 
    my $s_var = $sqres / $count ; 
    my $u_var = $sqres / ( $count - 1 ) ; 

    print join "\t" , qw [ average  s_var u_var s_stdev u_stdev ; count not-count ] ; 
    print "\n" ; 
    my $fmt = "%g\t"x5 .";\t"."%g\t%g" ; 
    printf $fmt  , $sum/$count , $s_var , $u_var , sqrt $s_var , sqrt $u_var , $count , $notcount ;
    print "\n" ; 
} 

sub outMore ()  { 
    # moments 
    my $m1 = $sum / $count ; 
    my $m2 = $sqsum / $count ; 
    my $m3 = $cbsum / $count ; 
    my $m4 = $qusum / $count ; 
    # cumulants 
    my $c1 = $m1 ; 
    my $c2 = $m2 - $m1 ** 2 ; 
    my $c3 = $m3 - 3 * $m1 * $m2 + 2 * $m1 ** 3 ;
    my $c4 = $m4 - 4 * $m3 * $m1 - 3 * $m2 ** 2 + 12 * $m2 * $m1 ** 2 - 6 * $m1 ** 4 ;

    sub f ( $ ) { sprintf "%0.4g" , $_[0] } ; 

    print join ( "\t" , qw [ order moment central cumulant derived ] ) , "\n" ; 
    print join ( "\t" , 1 , f $m1 , 0     , f $c1 ) , "\n" ;
    print join ( "\t" , 2 , f $m2 , f $c2 , f $c2 ) , "\n" ; 
    print join ( "\t" , 3 , f $m3 , f $c3 , f $c3 , f $c3 / $c2 ** 1.5 , '<- skewness' ) , "\n" ; 
    print join ( "\t" , 4 , f $m4 , f $c4 , f $c4 + 3*$c2**2 , f +($c4+3*$c2**2) / $c2 ** 2 , '<- kurtosis(>=3)' ) , "\n" ; 
}


## ヘルプの扱い
sub VERSION_MESSAGE {}
sub HELP_MESSAGE{
    use FindBin qw[ $Script ] ; 
    $ARGV[1] //= '' ;
    open my $FH , '<' , $0 ;
    while(<$FH>){
        s/\$0/$Script/g ;
        print $_ if $ARGV[1] eq 'opt' ? m/^\ +\-/ : s/^=head1// .. s/^=cut// ;
    }
    close $FH ;
    exit 0 ;
}

=encoding utf8 

=head1

 $0 
   入力の各行を各数値と見なして、それらの平均、分散などを表示する。
   出力は、平均、標本分散、不偏分散、標本分散の平方根、不偏分散の平方根
   及び、入力の個数、入力に計数されなかった個数である。

 オプション: 

   -P : PerlのScalar::Utilモジュールのlooks_like_numberで数かどうかを判定する。

 注意点: 

   分散の計算に単純な足し合わせをしているのみなので、絶対値の大きく異なる数を
   百万個程度以上入力すると、誤差が大きくなるかも知れない。
   (計算上の工夫は後日の課題とする。)

=cut
