#!/usr/bin/perl
#
# fmark.pl
#    uses fmark.dat
#
# This perl script makes a line for the table of the benchmark results
#
# 2011/3/15 : debug
# 2007/8/22 : "elapsed time" option added.
# 2001/6/4,5,6,13,7/13, 2004/4/12
#

# print "$#ARGV\n";

$DEBUG = 0 ;
$print_level    = "detail" ;
$print_max      = ""       ;
$print_quantity = "time"   ;
$mark_type      = "fMark"  ;

$fld = "fmark.dat";
if (! -e $fld) {$fld = "../$fld";}
if (! -e $fld) { print STDERR "$fld must be in the current or the parent directory.\n"; die;}
open(FLD,$fld);
while (<FLD>){
  ($program, $reftime, $weight) = split(' ',$_);
  push(@programs,$program);
  $reftime{$program} = $reftime;
  $weight{$program}  = $weight;
}
close(FLD);

if ($#ARGV < 0) {
  $ARGV[0] = ("-help");
}

for($iarg=0;$iarg <= $#ARGV;$iarg++) {
$fli = $ARGV[$iarg];

if ($fli eq "-help") {
  print <<EOD;
fmark.pl: version 2007/8/22 (y/m/d)
usage:
  fmark.pl (options and input files)
options:
  -help    : show this message
Output format is either detailed or concise:
  -detail  : detailed output for each input file (default)
  -oneline : one-line output for each input file
The following options are meaningful only in the one-line output style.
  * What kind of time should be used is
      -utime   : user CPU time (of a UNIX command "time") (default)
      -ustime  : user CPU time + system CPU time
      -etime   : elapsed time
  * The numbers in the table are
      -time    : time in seconds (default)
      -speed   : (reference time)/(CPU time)
  * The total speed index is
      -fMark  : a weighted geometrical average of (reference time)/(CPU time)
                (default)
      -fMarkS : a weighted arithmetic average of (reference time)/(CPU time)
      -fMarkT : the reciprocal of a weighted arithmetic average of 
                (CPU time)/(reference time)
The following option should be used with the one-line output format.
  -header  : print header lines for the table. This must be specified after
             options of -time, -speed, -fMarkS, and -fMarkT.
One can make this script to choose the fastest results for each program.
  -fastest : print the fastest result for each program in a style which can
             be used as a input file of this fmark.pl script like log files
             of the results of the benchmark test.
This script program needs a file named fmark.dat in the current directory.
EOD
  next;
}

if ( $fli eq "-oneline") { $print_level    = "oneline"  ; next; }
if ( $fli eq "-fastest") { $print_max      = "max"      ; next; }
if ( $fli eq "-utime" )  { $kind_of_time   = "u"        ; next; }
if ( $fli eq "-ustime" ) { $kind_of_time   = "us"       ; next; }
if ( $fli eq "-etime" )  { $kind_of_time   = "e"        ; next; }
if ( $fli eq "-time"   ) { $print_quantity = "time"     ; next; }
if ( $fli eq "-speed"  ) { $print_quantity = "speed"    ; next; }
if ( $fli eq "-fMark"  ) { $mark_type      = "fMark"    ; next; }
if ( $fli eq "-fMarkS" ) { $mark_type      = "fMarkS"   ; next; }
if ( $fli eq "-fMarkT" ) { $mark_type      = "fMarkT"   ; next; }

if ($fli eq "-header") {
#  printf "     %10s %9s %9s\n","program","ref.time","weight";
#  $n = 0;
#  foreach $key (@programs){
#    printf "(%2d) %10s %9s %9s\n",++$n,$key,$reftime{$key},$weight{$key};
#  }

  print " Numbers in the table are";
  if($print_quantity eq "time") { print " CPU time in seconds" ; }
  else { print " a speed index = (reference time)/(CPU time)" ; }
  print "\n Mark is";
  if($mark_type eq "fMark") {
    print " a weighted geometrical average of (reference time)/(CPU time)";
  }
  elsif($mark_type eq "fMarkS") {
    print " a weighted arithmetic average of (reference time)/(CPU time)"
  }
  elsif($mark_type eq "fMarkT") {
    print " the reciprocal of a weighted arithmetic average of (CPU time)/(ref.time)"
  }
  else { die ; }
  print "\n\n";

  printf " %6s:","y/m/d";
  printf "%20s:","log file";
  printf "%20s:","machine";
  printf "%20s:","compile with";
  printf "%8s:",$mark_type;
  foreach $program (@programs) {printf "%8s:",$program;}
  print "\n";

  printf "%69s :%8s:"  ,"[ reference time ]","--- ";
  foreach $program (@programs) {printf "%8.2f:",$reftime{$program};}
  printf "\n%69s :%8s:","[     weight     ]","--- ";
  foreach $program (@programs) {printf "%8d:",$weight{$program};}
  print "\n";

  next;
}

if  ( ! -e $fli ) {  print "input file $fli does not exist\n" ;  next ; }

open(FLI, "$fli");

$logfiles = "$logfiles\n  $fli";

undef $machine; undef $compile; 
undef $y;       undef $m;       undef $d;      
undef %timeu;   undef %times;   undef %timee;  undef %time;   

while ( $b=<FLI> ){
  if ($b =~ m/^machine ?= (.+)$/) {$machine = $1; last;}
}
if ("$machine" eq "") {
  printf STDERR "%s : machine name not given\n",$fli;
  die;
}
print "machine = $machine\n" if $DEBUG;

%month =('Jan',1,'Feb',2,'Mar',3,'Apr',4,'May',5,'Jun',6,'Jul',7,'Aug',8,'Sep',9,'Oct',10,'Nov',11,'Dec',12);
while ( $b=<FLI> ){
  if ($b=~m/^compile with \[ (.+) \]$/) {$compile = $1; last;}
}
if ("$compile" eq "") {
  printf STDERR "%s : compile options not given\n",$fli;
  die;
}
print "compile = $compile\n" if $DEBUG;

while ( $b=<FLI> ){
  if ($b=~m/^[Dd]ate ?= (.+)$/) {
    $_ = $1;
    if(/[A-z]{3,3} +([A-z]{3,3}) +(\d+) +[0-9:]{8,8}( +[^ ]+ +| +)(\d+)$/){
      if (length($4) == 4) {$y = substr($4,length($4)-2,2);}
      $m = ${month{$1}};
      $d = $2;
    }
    elsif (m#\s*(\d+)/(\d+)/(\d+)/(\d+):(\d+):(\d+)(\s|$)#) {
      $y = $1;
      $m = $2;
      $d = $3;
    }
    last;
  }
}
if ("$y" eq "") {
  printf STDERR "%s : date not given\n",$fli;
  die;
}
printf "date = %2d %2d %2d\n",$y,$m,$d if $DEBUG;

while ($b = <FLI>){
  if($b =~ /^\(\d+\) program = (\w+)(|\.f|\.for)($| )/){
    $program = $1;
    print "program = $program\n" if $DEBUG;
    next;
  }
  if($b =~ /^(\d*\.\d*)u (\d*\.\d*)s (\d+:\d+:|\d+:|)(\d+\.?\d*)/){

    if ($timeu{$program} eq "" and $times{$program} eq "" and $timee{$program} eq "" ){

      $timeu{$program}= $1;
      $times{$program}= $2;
      $tm=$3; $ts=$4;
      if($tm =~ /(\d+):(\d+):/){$tm=$1*3600+$2*60;}
      elsif($tm =~ /(\d+):/){$tm=$1*60;}
      else {$tm=0;}
      $timee{$program}= $tm+$ts;

      if($kind_of_time eq "us") { $time{$program}= $timeu{$program}+$times{$program};}
      elsif($kind_of_time eq "e") { $time{$program}= $timee{$program};}
      else  { $time{$program}= $timeu{$program};} # if($kind_of_time eq "u")

      print $b if $DEBUG;
      print "time(u,s,e,adopted) = $timeu{$program},$times{$program},$timee{$program}
            ,$time{$program}\n" if $DEBUG;
    }
    next;
  }
}

close (FLI);

if (0){
open(FLT,"|sort");
foreach $key (keys(%time)) { printf FLT "%s\n",$key;}
close(FLT);
}

$ n = 0; $markg = 0; $markt = 0; $marks = 0;
foreach $program (keys(%weight)) {
  unless ("$time{$program}" eq ""){
    $n     += $weight{$program};
    $markg += $weight{$program}*log($reftime{$program}/$time{$program}) ;
    $marks += $weight{$program}*    $reftime{$program}/$time{$program}  ;
    $markt += $weight{$program}    /$reftime{$program}*$time{$program} ;

    if ( $min_time{$program} <= 0 ) {
      $min_time{$program} = $time{$program} ;
    }
    elsif ($min_time{$program} > $time{$program}) {
      $min_time{$program} = $time{$program} ;
    }
  }
}
if($n > 0) {$markg = exp($markg / $n);} else {$markg = "";} 
if($n > 0) { $marks = $marks / $n; } else { $marks = "";}
if($markt > 0) {$markt = $n/$markt; } else {$markt = "";}

if ($print_level eq "detail") {
printf "measurement date = %2.2d/%2.2d/%2.2d\n",$y,$m,$d;
printf "log file = %s\n",$fli;
printf "machine = %s\n",$machine;
printf "compile with [ %s ]\n",$compile;

printf "\n     %10s %6s %9s %9s %9s %9s\n","program","weight","ref.time",
       "time[sec]","time/ref","ref/time";
$n = 0;
foreach $program (@programs) {
  if (! $time{$program} eq ""){
    printf "(%2d) %10s %6d %9.2f %9.2f %9.4f %9.4f\n"
    ,++$n,$program,$weight{$program},$reftime{$program},
    $time{$program},$time{$program}/$reftime{$program},
    $reftime{$program}/$time{$program}
  }
  else {
    printf "(%2d) %10s %6d %9.2f\n"
    ,++$n,$program,$weight{$program},$reftime{$program}
  }
}

printf "\n  weighted arithmetic  average of (ref/time) = %9.2f\n",$marks;
printf "  weighted geometrical average of (ref/time) = %9.2f\n",$markg;
printf "1/weighted arithmetic  average of (time/ref) = %9.2f\n",$markt;
printf "\n               fMark = %9.2f\n",$markg;
}

if ($print_level eq "oneline") {
printf " %2.2d%2.2d%2.2d:",$y,$m,$d;
printf "%20s:",substr($fli,0,20);
printf "%20s:",substr($machine,0,20);
printf "%20s:",substr($compile,0,20);
if   ($mark_type eq "fMark")  { printf "%8.3f:",$markg ; }
elsif($mark_type eq "fMarkS") { printf "%8.3f:",$marks ; }
elsif($mark_type eq "fMarkT") { printf "%8.3f:",$markt ; }
else                          { die                    ; }

if($print_quantity eq "time") {
  foreach $program (@programs) {printf "%8.2f:",$time{$program};}
}
elsif($print_quantity eq "speed") {
  foreach $program (@programs) {
    unless ($time{$program} eq "") {
      printf "%8.3f:",$reftime{$program}/$time{$program};
    }
    else {
      print "        :";
    }
  }
}
else{
  die;
}

print "\n";
}

} # end of the for-loop for $iarg

if ($print_max eq "max"){
print <<EOD;
Fortran Benchmark Tests
Collection of the fastest calculation for each program among$logfiles
machine = $machine
compile with [ $compile ]
date = $y/$m/$d/0:0:0
EOD
$n = 0;
foreach $program (@programs) {
  unless ( $min_time{$program} eq "" ) {
    printf "(%d) program = %s.f\n",++$n,$program;
    printf "%fu 0.0s %f\n",$min_time{$program},$min_time{$program};
  }
}
}
