#!/usr/local/bin/perl

# Given a set of file names that have experimental data, prints out the
# relevant times for all the experiments that are present in the files

sub usage_assert { # Parameter Boolean, string
    local($val, $str) = @_;
    if ($val) {return;}
    usage ($str);
}

sub usage { # Paramaters: Error string
    local($err) = @_;
    print "ERROR: $err\n";
    $usage = join(' ', "extract_nums [-s datastring] [-k] files\n");
    print "$usage\n\n";
    exit;
}

sub parse_traversal{ # Parameter string of the form,e.g. file#M,T,small,6,C,,
    # effects: Returns a string that descibes the traversal

    local($trav) = @_;

    @key = split("#", $trav);

    @data = split(",", $key[1]);
    if ($data[1] eq "T") {
	$type = "Normal";
    } else {
	$type = "Shift";
    }
    $max_i = -1;
    for ($i = 0; $i <= $#data; $i++) {
	if ($data[$i] =~ /^\s*$/) {
	    $max_i = $max_i == -1 ? $i: $max_i;
	} else {
	    $max_i = -1;
	}
    }

    if ($max_i != -1) {
	$#data = $max_i - 1;
    }

    # Parse the filename
    ($page, $eviction, $bigslots, $tname, $temp) = split("_", $key[0]);
    $bigslots = substr($bigslots, 1);
    $page = substr($page, 1);
    $eviction = substr($eviction, 1);

    $res = "Eviction: $eviction, Bigslots: $bigslots, $temp, $type,";
    $res = "$res @data[2..$#data]";
    return $res;
}


$datastring = "Time for traversal";
$start_arg = 0;
$kossman_scale = 0;
for ($i = 0; $i <= $#ARGV; $i++) {
    $option = $ARGV[$i];
    if ($option eq "-k") {
	$kossman_scale = 1;
	$start_arg++;
    } elsif ($option eq "-s") {
	$i++;
	$data_string = $ARGV[$i];
	$start_arg += 2;
    } elsif ($option =~ /-.*/) {
	usage "Bad option: $option $val";
    }
}

# Maintain information for each execution number
# We maintain the "datastring" information, Final command information

for ($i = $start_arg;  $i <= $#ARGV; $i++) {
    $filename = $ARGV[$i]; 
    open(FILE, $filename);
    while (<FILE>) {
	if (/EXECUTION NUM = (\d+).* DONE/) {
	    # Execution done. Update execution's info
	    $key = "$filename#$final_comm";
	    $travinfo = $trav_hash{$key};
	    if ($travinfo && $key ne $$travinfo{POINT}) {
		print "Final command differs for two iterations: $key\n";
	    }
	    $rec = {
	     POINT           => $key,
	     DATA            => $$travinfo{DATA} + $data,
	     OR_TOTAL        => $$travinfo{OR_TOTAL} + $or_total,
	     DISK_TOTAL      => $$travinfo{DISK_TOTAL} + $disk_total,
	     FE_NET_FETCH    => $$travinfo{FE_NET_FETCH} + $fe_net_fetch,
	     FE_TOTAL        => $$travinfo{FE_TOTAL} + $fe_total,
	     COMMIT_TOTAL    => $$travinfo{COMMIT_TOTAL} + $commit_total,
	     COMMIT_MAKE     => $$travinfo{COMMIT_MAKE} + $commit_make,
             COMMIT_VALIDATE => $$travinfo{COMMIT_VALIDATE} + $commit_validate,
	     NUM             => $$travinfo{NUM} + 1
	    };
	    $trav_hash{$key} = $rec;
	}
	if (/Final command = (.*)/) {
	    $final_comm = $1;
	}
	if (/$datastring\s*(=|:)\s*(.*)/) {
	    $data = $2;
	}
	# Extract the various components
	if (/Total fetch time\s+=\s+(.*)\s*seconds/) {
	    $or_total = $1;
	} elsif (/total : ops=.*elapsed\s+=\s+(.*)\s*for/) {
	    $disk_total = $1;
	} elsif (/Total fetch time \(including pc bookkeeping\):\s*(.*)/) {
	    $fe_net_fetch = $1;
	} elsif (/Total time\s+=\s+(.*)/) {
	    $fe_total = $1;
	} elsif (/Time for commit\s+=\s+(.*)/) {
	    $commit_total = $1;
	} elsif (/Make\s+=\s+(.*),/) {
	    $commit_make = $1;
	} elsif (/Total validate time\s+=\s+(.*)\s*seconds/) {
	    $commit_validate = $1;
	}
    }
    close(FILE);
}

foreach $trav (sort keys %trav_hash) {
    $travinfo = $trav_hash{"$trav"};
    $trav_name = parse_traversal($trav);
    @key = split("#", $trav);
    ($page, $eviction, $cache, $tname, $temp) = split("_", $key[0]);
    $cache = substr($cache, 1);
    print "Trav = $trav_name\n";
    if (!$$travinfo{POINT}) {
	$$travinfo{POINT} = 1;
	print "Traversal $trav_name has no data\n";
    }
    # Put averages
    $num = $$travinfo{NUM};
    $commit_net = $$travinfo{COMMIT_TOTAL} - $$travinfo{COMMIT_MAKE}
                   - $$travinfo{COMMIT_VALIDATE};
    $$travinfo{COMMIT_NET} = $commit_net;
    foreach $key (keys %$travinfo) {
	if ($key ne "POINT" && $key ne "NUM") {
	    $$travinfo{$key} = $$travinfo{$key}/$num;
	    printf("%15s = %.14g\n", $key, $$travinfo{$key});
	}
    }
    
    $qs_scaled = (($$travinfo{FE_TOTAL} - $$travinfo{COMMIT_TOTAL} - 
		   $$travinfo{FE_NET_FETCH}) * 3
		  + $$travinfo{COMMIT_MAKE} * 3
		  + $$travinfo{COMMIT_VALIDATE} * 2.5
		  + $$travinfo{COMMIT_NET}
		  + ($$travinfo{FE_NET_FETCH} - $$travinfo{OR_TOTAL})
		  + ($$travinfo{OR_TOTAL} - $$travinfo{DISK_TOTAL}) * 2.5
		  + $$travinfo{DISK_TOTAL} * 1.5);

    $kk_scaled = (($$travinfo{FE_TOTAL} - $$travinfo{COMMIT_TOTAL} - 
		   $$travinfo{FE_NET_FETCH}) * 2.25
		  + $$travinfo{COMMIT_MAKE} * 2.25
		  + $$travinfo{COMMIT_VALIDATE} * 2.25
		  + $$travinfo{COMMIT_NET}
		  + ($$travinfo{FE_NET_FETCH} - $$travinfo{OR_TOTAL})
		  + ($$travinfo{OR_TOTAL} - $$travinfo{DISK_TOTAL}) * 2.25
		  + $$travinfo{DISK_TOTAL} * 2.0);

    if  ($temp eq "hot") {
	$qs_scaled = 0;
	$kk_scaled = 0;
    }


    if ($kossman_scale) {
	print "KK scaled = $kk_scaled ($num)\n";
	$product = $kk_scaled * $cache/1024;
	print "Space time product = $product\n\n\n";
    } else {
	print "QS scaled = $qs_scaled ($num)\n\n\n";
    }

}
