#!/usr/bin/perl

use strict;

my $size = 0;
my $iters = 0;
my $who = '';
my $mode = '';
my %res = ();
my $offlinebytes = 0;
my $netsetup = '';
my %online_s_data = ();
my %online_kib_data = ();
my %total_s_data = ();
my %total_kib_data = ();

while(<>) {
    if (/Network setup: (.*)/) {
        $netsetup = "$1 ";
        next;
    }
    ($size,$iters) = ($1,$2) if /===== Running oram (\d+) (\d+)/;
    $res{$size} = {} unless defined $res{$size};
    $who = $1 if /===== ([CDE]) output/;
    $mode = $1 if /(Online Time|Offline Time|ETE)/;
    if(/WC\(ms\): (\d+)/) {
        if (defined $res{$size}->{$who.$mode}) {
            print "Redefinition of res{$size}->{$who$mode}\n";
        } else {
            $res{$size}->{$who.$mode} = $1/1000
        }
    }
    if (/Total Offline Band\(bytes\): (\d+)/) {
        $offlinebytes = $1;
    }
    if (/Total Online Band\(bytes\): (\d+)/) {
        # Note that the java code reports bandwidth *per iteration*
        my $onlinebytes = $1;
        my $totbytes = $onlinebytes + $offlinebytes;
        $res{$size}->{'onlbytes'} = 0 unless
            defined $res{$size}->{'onlbytes'};
        $res{$size}->{'totbytes'} = 0 unless
            defined $res{$size}->{'totbytes'};
        $res{$size}->{'onlbytes'} += $onlinebytes;
        $res{$size}->{'totbytes'} += $totbytes;
    }
    if (/===== End /) {
        my $label = "CircuitORAM read $size $netsetup$iters";
        my $online = &max3($res{$size}->{'COnline Time'},
            $res{$size}->{'DOnline Time'},
            $res{$size}->{'EOnline Time'});
        my $total = &max3($res{$size}->{'CETE'},
            $res{$size}->{'DETE'},
            $res{$size}->{'EETE'});
        &accum_data(\%online_s_data, $label, $online);
        &accum_data(\%total_s_data, $label, $total);
        # Note that the java code reports bandwidth *per iteration*
        my $onlinekib = $res{$size}->{'onlbytes'} * $iters / 1024;
        my $totalkib = $res{$size}->{'totbytes'} * $iters / 1024;
        &accum_data(\%online_kib_data, $label, $onlinekib);
        &accum_data(\%total_kib_data, $label, $totalkib);
        undef $res{$size};
    }
}

# Convert the data (in the form [n, sum, sum_squares]) to statistics (in
# the form [mean, variance])
my %online_s_stats = ();
my %online_kib_stats = ();
my %total_s_stats = ();
my %total_kib_stats = ();
&statsify(\%online_s_stats, \%online_s_data);
&statsify(\%online_kib_stats, \%online_kib_data);
&statsify(\%total_s_stats, \%total_s_data);
&statsify(\%total_kib_stats, \%total_kib_data);

# Output the data
&output_stats(\%online_s_stats, "Onln", "s");
&output_stats(\%online_kib_stats, "Onln", "KiB");
&output_stats(\%total_s_stats, "Totl", "s");
&output_stats(\%total_kib_stats, "Totl", "KiB");

# Subroutines

sub max3 {
    my $m = $_[0];
    $m = $_[1] if $_[1] > $m;
    $m = $_[2] if $_[2] > $m;
    $m;
}

# Pass:
# - a reference to a dictionary
# - the key into that dictionary
# - the new data point
# Data is stored in the dictionary as a triple (n, sum, sum_squares)
sub accum_data {
    my ($dict, $key, $data) = @_;
    $dict->{$key} = [0, 0, 0] unless defined $dict->{$key};
    $dict->{$key}->[0] += 1;
    $dict->{$key}->[1] += $data;
    $dict->{$key}->[2] += ($data * $data);
}

# Convert data (in the form [n, sum, sum_squares]) to statistics (in
# the form [mean, variance])
sub statsify {
    my ($sdict, $ddict) = @_;
    my $key;
    foreach $key (keys %$ddict) {
        my $data = $ddict->{$key};
        my $n = $data->[0];
        my $sum = $data->[1];
        my $sumsq = $data->[2];
        if ($n == 0) {
            $sdict->{$key} = [undef, undef];
        } elsif ($n == 1) {
            $sdict->{$key} = [$sum, undef];
        } else {
            $sdict->{$key} = [$sum/$n, ($sumsq - ($sum*$sum/$n))/($n-1)];
        }
    }
}

# Turn a stat array [mean, variance] into a string to display
sub statstr {
    my $data = $_[0];
    if (defined $data->[1]) {
        my $mean = $data->[0];
        my $stddev = $data->[1] > 0 ? sqrt($data->[1]) : 0;
        return "$mean ± $stddev";
    } elsif (defined $data->[0]) {
        return $data->[0];
    } else {
        return "none"
    }
}

# Output the stats in the given dictionary. Append $phase to the
# protocol name, and add $units to the end.
sub output_stats {
    my ($dict, $phase, $units) = @_;
    my $label;
    foreach $label (sort keys %$dict) {
        my $printlabel = $label;
        $printlabel =~ s/CircuitORAM/CircuitORAM$phase/;
        print $printlabel, " ", &statstr($dict->{$label}), " $units\n";
    }
}