#!/usr/bin/perl

@rtt = ();
@failper = ();
@failint = ();
$n = $timo = $err = 0;
$sum = $sumsq = 0;
$min = $max = undef;
$firsttime = $lasttime = undef;
$lastfail = undef;
$failstart = undef;
if ($#ARGV >= 0) {
    $searchstr = shift @ARGV;
    if ($searchstr =~ /[^-.a-zA-Z:]/) {
	$searchfun = sub { $_[0] =~ /^$searchstr /oi; };
    } else {
	$searchfun = sub { $_[0] =~ /^\Q$searchstr\E /oi; };
    }
} else {
    $searchfun = sub { 1; };
}

while (<>) {
    #
    # Do this first so we don't waste a lot of time splitting all
    # the other records in the file...
    #
    next unless (&$searchfun($_));
    chomp;
    split;
    next unless ($#_ >= 8);
    $timo++ if ($_[5] eq 'timeout');
    $err++ if ($_[5] eq 'error');
    if ($_[5] eq 'timeout' || $_[5] eq 'error') {
	#
	# If we are already in a failure period, then we don't
	# need to consider anything else.
	#
	next if (defined $failstart);
	$failstart = ($_[7] ne 'NaN') ? $_[7] : $_[8];
	if (defined $lastfail) {
	    push (@failint, $failstart - $lastfail);
	}
	$lastfail = $failstart;
	next;
    }
	
    next unless ($_[5] eq 'rtt');
    if (defined $failstart) {
	push (@failper, $_[7] - $failstart);
	undef $failstart;
    }
    $rtt = ($_[7] - $_[6]) * 1000;
    $firsttime = $_[6] unless(defined $firsttime);
    $lasttime = $_[6];
    push @rtt, $rtt;
    $sum += $rtt;
    $sumsq += $rtt * $rtt;
    $n++;
    if (defined($min)) {
	$min = $rtt if($rtt < $min);
    } else {
	$min = $rtt;
    }
    if (defined($max)) {
	$max = $rtt if ($rtt > $max);
    } else {
	$max = $rtt;
    }
}

if (defined $searchstr) {
    $hdr = "Statistics for $searchstr\n";
} else {
    $hdr = "Statistics for (standard input)\n";
}
print ($hdr, '-' x (length($hdr) - 1), "\n");

print("start time: ", scalar localtime(int $firsttime), "\n");
print("end time: ", scalar localtime(int $lasttime), "\n");
print "# of observations: $n\n";
printf("# of timeouts: $timo (%.1f%%)\n", 100 * $timo / ($n + $timo + $err));
printf("# of errors: $err (%.1f%%)\n", 100 * $err / ($n + $timo + $err));
$obsint = ($lasttime - $firsttime) / ($n + $timo + $err - 1);
printf("mean observation interval: %f s\n", $obsint);
print "\nRTT statistics:\n";
printf("minimum: %f ms\n", $min);
printf("maximum: %f ms\n", $max);
printf("arithmetic mean: %f ms\n", $avg = $sum / $n);
printf("variance: %f ms^2\n", $var = $sumsq / $n - $avg * $avg);
printf("standard deviation: %f ms\n", sqrt($var));

@rtt = sort {$a <=> $b} @rtt;

if ($n % 2) {
    $median = $rtt[$n / 2];
} else {
#    printf("median: avg(%f, %f)\n", $rtt[$n / 2 - 1], $rtt[$n / 2]);
    $median = ($rtt[$n / 2 - 1] + $rtt[$n / 2]) / 2;
}
printf("median: %f ms\n", $median);

if ($n > 4) {
    @qtile = ();
    print("quartiles:\n");
    printf("\tfirst: %f ms\n", $qtile[0] = $rtt[$n / 4 - 1]);
    printf("\tsecond: %f ms\n", $qtile[1] = $rtt[$n / 2 - 1]);
    printf("\tthird: %f ms\n", $qtile[2] = $rtt[3 * $n / 4 - 1]);
    printf("\tfourth: %f ms\n", $qtile[3] = $max);
    printf("\trank size: %d obs\n", $n / 4);
    printf("\tinter-quartile range: %f ms\n", $qtile[2] - $qtile[0]);
}

printf("85th %%ile: %f ms\n", $rtt[85 * $n / 100 - 1]);
printf("90th %%ile: %f ms\n", $rtt[90 * $n / 100 - 1]);
printf("95th %%ile: %f ms\n", $rtt[95 * $n / 100 - 1]);
printf("99th %%ile: %f ms\n", $rtt[99 * $n / 100 - 1]);

exit(0) if ($err == 0 && $timo == 0);

#
# MTBF and MTTR may be overstated.
#
printf("\nFailure statistics:\n");
printf("\tTotal number of failure periods: %d\n", $#failper + 1);
$sum = 0;
if ($#failint >= 0) {
    foreach my $intvl (@failint) { $sum += $intvl; }
    printf("\tMean time between failures: %f s\n", 
	   $sum / ($#failint + 1) - $obsint);
}
if ($#failper > 0) {
    $sum = 0;
    foreach my $per (@failper) { $sum += $per; }
    printf("\tMean length of failures: ");
    $lof = $sum / ($#failper + 1) - $obsint;
    if ($lof > 0) {
	printf("%f s\n", $lof);
    } else {
	printf("unmeasurable\n");
    }
}

exit 0;
