#! /usr/bin/perl -w
# XcuteS  [-V] [-Z] [-O[2]] [-T[134]] [-S] [-A] <[.ccf[c]]file> [<instart> <instop> <incount> <ststart> <ststop> <stcount>]
#
#  "runs" and plots a component's behavior (with state/concurrent).
#
#  Default is output behavior; -T for run time; -S for state.
 #  -O2, -T1, -T3, -T4 are detail graphs for concurrent; -T adds all runtimes.
#
#  Bare file, assume .ccf
#
#  For .ccf, plot actual component, and also subdomain plateaux,
#    because .ccfc must also exist.
#    Make one master data file.dat, from which several plots can be made.
#
#  For .ccfc, plot only approximation, using vector sum of states for that dimension.
#

use sampling;
use component;
use status;
use util;

$plotOpt = "O"; #default is to plot output
$altOpt = 0;  #plot form: -A means a contour surface & towers; else floating rectangles & points
$Voption = 0;
$hw = -1; #last "-" arg
for ($opt=0; defined($ARGV[$opt]); $opt++) {
  if ($ARGV[$opt] eq "-O") {
    $plotOpt = "O";
    $hw = $opt;
  } elsif ($ARGV[$opt] eq "-V") {
    $Voption = 1;
    $hw = $opt;
  } elsif ($ARGV[$opt] eq "-Z") {
    $Zoption = 1;
    $hw = $opt;
  } elsif ($ARGV[$opt] eq "-A") {
    $altOpt = 1;
    $hw = $opt;
# fix for pattern:  -A xxx -10 ...
  } elsif (substr($ARGV[$opt],0,1) eq "-" && substr($ARGV[$opt],1,1) =~ /\p{IsLu}/) {
    $plotOpt = substr($ARGV[$opt],1);
    $hw = $opt;
  }
}
die "A .ccf[c] file must be given" unless (defined($ARGV[$hw+1]));
$ccf = $ARGV[$hw+1];
if ($ccf !~ /\./) { #bare file
  $ccf .= ".ccf";
}
die "given file ",$ccf," not found" unless (-e $ccf);
@sep = split(/\./,$ccf);
$ext = $sep[scalar(@sep)-1];
$ccfbase = $sep[0].".dat"; #data file for measurements
$ccfc = $ccf;
if ($ext ne "ccfc") {
  $ccfc = $ccf."c";
} 
unless (-e $ccfc) { #there is a .ccfc file
  die "an approximation file $ccfc must have been created previously";
}
$tomeasure = ($ext ne "ccfc");
open(CCFC,"<$ccfc");
$line = <CCFC>;
@lane = split(" ",$line); # "theory"|"state"|"concurrent" number-of-states number-input-subdomains numbers-state-subdomains state-initials
die "bad .ccfc file format in $ccf" 
  unless ($lane[0] eq "state" || $lane[0] eq "theory" || $lane[0] eq "concurrent");
if ($tomeasure) {
  $randOpt = ($lane[5] eq "R");  #use same sampling as .ccfc file (but not concurrent!)
}
$concurrent = ($lane[0] eq "concurrent");
$NumStates = $lane[1];
$NumInSubd = $lane[2];
for ($i=0; $i<$NumStates; $i++) {
  $NumStSubd[$i] = $lane[3+$i];
  #$initialStates[$i] = $lane[3+$NumStates+$i];  #not needed??!
}
for ($i=0; $i<$NumInSubd; $i++) {
  $line = <CCFC>;
  @lane = split(" ",$line);
  chomp($LBin[$i] = $lane[0]);
  chomp($UBin[$i] = $lane[1]);
}
for ($i=0; $i<$NumStates; $i++) {
  for ($j=0; $j<$NumStSubd[$i]; $j++) {
    $line = <CCFC>;
    @lane = split(" ",$line);
    chomp($LBst[$i][$j] = $lane[0]);
    chomp($UBst[$i][$j] = $lane[1]);
  } 
}
$i = 0;
while ($line = <CCFC>) { #read in the rest for later use
  $dataline[$i++] = $line;
}
$ccfcount = $i;
close CCFC;

$parmsupplied = 0;
$inCount = 3*$NumInSubd;
$stCount = 3*$NumStSubd[0];
if (defined($ARGV[$hw+2])) {
  $parmsupplied = 1;
  unless (defined($ARGV[$hw+4])) {
    die "all range/count arguments must be given";
  }
  $inLeft = $ARGV[$hw+2];
  $inRight = $ARGV[$hw+3];
  $inCount = $ARGV[$hw+4];
  unless ($randOpt) {
    if (defined($ARGV[$hw+5])) {
      unless (defined($ARGV[$hw+7])) {
        die "all range/count arguments must be given";
      }
      $stLeft = $ARGV[$hw+5];
      $stRight = $ARGV[$hw+6];
      $stCount = $ARGV[$hw+7];
    }
  }
}
@plts = ();
  $leftin = $LBin[0];
  $rightin = $UBin[$NumInSubd-1];
  $leftst = $LBst[0][0];
  $rightst = $UBst[0][$NumStSubd[0]-1];
  if (not defined($stLeft)) { #only inputs supplied; take state from subdomain bounds
    $stLeft = $leftst;
    $stRight = $rightst;
  }
  if ($parmsupplied) {
    if ($stLeft < $leftst || $stRight > $rightst || $inLeft < $leftin || $inRight > $rightin) {
      die "given ranges don't match with .ccf ranges";
    } else {
      $leftin = $inLeft;
      $rightin = $inRight;
      $leftst = $stLeft;
      $rightst = $stRight;
    } 
  }
if ($tomeasure) { 
  open(CCF, $ccf);
  $nameline = <CCF>;
  @nameand = split(' ',$nameline);
  $code_name = $nameand[0];
  $concurrent = ($nameand[1] eq "concurrent");
  if ($concurrent) { #no random
    $randOpt = 0;
  }
  close CCF;
  unless (-e $code_name) {die "No code file to measure";}
#print "input: [$leftin, $rightin) state: [$leftst, $rightst) \n";  #debug
  $needtoX = 0;
  unless ($parmsupplied) { #name for full range of data
    $ccfbase = $sep[0].".alldat";
  }
  if (!-e $ccfbase) { #local measurement data file not there
    #but might have just made it with COMPSt
    $needtoX = 1;  #reverse assumption
    if (-e "measured.dat" #&& (-M "measured.dat" < -M $ccfc)
      && (-M "measured.dat" < -M $code_name) && (-M "measured.dat" < -M $ccf)) { #check header
      open (DAT,"<measured.dat");
      $head = <DAT>; #state/concur U/R inistate code ranges
#print $head;  #debug
      @bh = split(' ',$head);
      close DAT;
      if ($code_name eq $bh[3]) {
        #&& $leftin == $bh[4] && $rightin == $bh[5]
        #&& $inCount == $bh[6] && $leftst == $bh[7] && $rightst == $bh[8] 
        #&& $stCount == $bh[9]) { #matches so OK
        rename ("measured.dat", $ccfbase);
        $needtoX = 0;
      }
    }
  } else {
    if ($Zoption || $parmsupplied || (-M $ccfbase >= -M $ccfc) 
      || (-M $ccfbase >= -M $code_name) || (-M $ccfbase >= -M $ccf)) { 
      $needtoX = 1;
    }
  }
  if ($needtoX) {
    $opts = "";
    if ($randOpt) {
      $opts .= " -R";
    } else {
      $opts .= " -U";
    }
    $opts .= " -V" if ($Voption);
    system "perl XS $opts $code_name $ccf $leftin $rightin $inCount $leftst $rightst $stCount";
    #sorted data in "measured.dat"
    rename("measured.dat",$ccfbase);  #move to local name
  }
  #$ccfbase local file now exists in any case
  #get data in arrays to plot from
  open(DAT,"<$ccfbase");
  $line = <DAT>;  #header line
  $count = 0;
  while ($line = <DAT>) {
    @lane = split(" ",$line);
    $in_subd[$count] = $lane[0];
    $st_subd[$count] = $lane[1];
    $inputs[$count] = $lane[2];
    $in_state[$count] = $lane[3];
    $outputs[$count] = $lane[4];
    $runtimes[$count] = $lane[5];
    if ($concurrent) {
      $outputs2[$count] = $lane[6];
      $runtimes3[$count] = $lane[7];
      $runtimes4[$count]= $lane[8];
      $grids[$count] = $lane[9];
    } else {
      $out_state[$count]= $lane[6];
      $grids[$count] = $lane[7];
    }
    $count++;
  }
  close DAT;
if ($concurrent && $plotOpt eq "T") { #combine runtimes
  for ($k=0; $k<$count; $k++) {
    $runtimes[$k] += $runtimes3[$k] + $runtimes4[$k];
  }
}
} #end of executing code case

# Create graphing files for executions

# select plot to do
$zlab = ""; #graph axis label
if ($concurrent) { #check illegal combinations
  die "concurrent components have no state" if ($plotOpt eq "S");
} else {
  die "Option -$plotOpt only for concurrent components" if ($plotOpt eq "O2" || $plotOpt eq "T1"
    || $plotOpt eq "T3" || $plotOpt eq "T4");
}
if ($plotOpt eq "O") {
  $pfile = "output.dat";
  $arr = \@outputs;
  $which = "output";
  $zlab = "Output values";
  $ccfcIdx = 1;  #subscript in .ccfc line; 
} elsif ($plotOpt eq "T" || $plotOpt eq "T1") {
  $pfile = "runtime.dat";
  $arr = \@runtimes;
  $which = "run time";
  $zlab = "Runtime values";
  $ccfcIdx = 2;
} elsif ($plotOpt eq "S") {
  $pfile = "state.dat";
  $arr = \@out_state;
  $zlab = "Result state values";
  $which = "state";
  $ccfcIdx = 3;
} elsif ($plotOpt eq "O2") {
  $pfile = "out2.dat";
  $arr = \@outputs2;
  $zlab = "Output to parallel";
  $which = "output to parallel";
  $ccfcIdx = 2;
} elsif ($plotOpt eq "T3") {
  $pfile = "run3.dat";
  $arr = \@runtimes3;
  $zlab = "Runtime in parallel";
  $which = "runtime in parallel";
  $ccfcIdx = 3;
} elsif ($plotOpt eq "T4") {
  $pfile = "run4.dat";
  $arr = \@runtimes4;
  $zlab = "Runtime values";
  $which = "runtime after parallel";
  $ccfcIdx = 4;
}
unless ($tomeasure) {
  goto CCFCONLY;
}
$sqstot = 0;
$ii = 0;
$is = 0; # indices into subdomains
$Hit = 0;
$sqrs1 = 0;
$c1 = 0;
$k = 0;
$ave1 = 0; #accumulate for one subdomain
$thval = (split(' ',$dataline[0]))[$ccfcIdx];
for ($i=0; $i<$count; $i++) { #loop thru measurements stored in arrays
  if ($st_subd[$i] != $is || $in_subd[$i] != $ii) { #new subdomain in data
    if ($c1 > 0) { #some data in previous subd
      $errs[$ii][$is] = $sqrs1/$c1;
      $aves[$ii][$is] = $ave1/$c1;
      $hitsub[$ii][$is] = 1;
      $Hit++;
#print "hit subdomain $ii x $is $c1 times\n";  #debug
      $c1 = 0;
    } else { #no hits 
      $hitsub[$ii][$is] = 0;
    }
    while ($in_subd[$i] > $ii) { #catch up with inputs
      $ii++;
      $hitsub[$ii][$is] = 0;
    }
    while ($st_subd[$i] > $is) { #catch up with states
      $is++;
      #for ($iii=0; $iii<$NumInSubd; $iii++) { #do all the inputs for that missing state
        #$hitsub[$iii][$is] = 0;
      #}
      #leave all these undefined
    }
    $ii = $in_subd[$i];  # in case input reverted
    #get approx data
    $k = $is*$NumInSubd + $ii; 
    @line = split(' ',$dataline[$k]);
    $thval = $line[$ccfcIdx];
    $ave1 = 0;
    $sqrs1 = 0;
  } #end new subdomain
  $c1++;
  $ave1 += @$arr[$i];
  $sqrs1 += (@$arr[$i] - $thval)**2;
  $sqstot += @$arr[$i]*@$arr[$i];
print "$i: $inputs[$i] $in_state[$i] $grids[$i]\n";  #debug
  $templt1[$i] = "$inputs[$i] $in_state[$i] @$arr[$i] $grids[$i]\n";
} #end loop over measurements
if ($c1 > 0) { #some data in final subd
  $errs[$ii][$is] = $sqrs1/$c1;
  $aves[$ii][$is] = $ave1/$c1;
  $Hit++;
  $hitsub[$ii][$is] = 1;
} else { #no hits 
  $hitsub[$ii][$is] = 0;
}
#may be additional subdomains never in data...but don't care
#  leave hitsub undefined for those
$rmstot = sqrt($sqstot/$count);  #used in error tables below

unless ($randOpt) { #put in grid order
  @templt1 = sort {@af = split(' ',$a); @bf = split(' ',$b); $af[1] <=> $bf[1]
     || $af[0] <=> $bf[0] } @templt1;

}
$howplt = "with points";
if ($altOpt) {
  $howplt = "with lines lt 1";
}
$plts[scalar(@plts)] = "\"$pfile\" $howplt";
open(PLTFILE,">$pfile");
unless ($randOpt && $altOpt) { #3 out of 4 plots here
  for ($i=0; $i<$count; $i++) { #thru data again
    @break = split(' ',$templt1[$i]);
    if ($randOpt) { #grids already on record
      $gridmark = $break[3];
    } else {
      $gridmark = 0;
      if ($i != 0 && $break[0] < $lastin) {
        $gridmark = 1;
      }
    }
    $lastin = $break[0];
    if ($randOpt) { #print line before grid
      print PLTFILE $templt1[$i];
    }
    if ($altOpt && $gridmark) { # add linefeeds to delimit blocks
      for ($n=1; $n<=$gridmark; $n++) { #1 or 2
        print PLTFILE "\n";
      }
    }
    unless ($randOpt) { #print line after grid
      print PLTFILE $templt1[$i];
    }
  }
} else { #contour for random hard case
  AD:
  for ($j=0; $j<$NumStSubd[0]; $j++) { #loop over state subd
    $anyin = 0;  #were there any points in this state?
    $forthisin = "";
    for ($i=0; $i<$NumInSubd; $i++) { #loop over input subd
      if (not defined($hitsub[$i][$j])) { #skip this state block
        print PLTFILE "\n"; #extra grid line
        next AD;
      }
      $x = ($LBin[$i] + $UBin[$i])/2;
      $s = ($LBst[0][$j] + $UBst[0][$j])/2;
      if ($hitsub[$i][$j]) { #valid data
        $anyin = 1;
        $y = $aves[$i][$j];
      } else {
        $y = $rmstot; #poor choice, but?
      }
      $forthisin .= "$x $s $y\n";
      #print PLTFILE "$x $s $y\n";
    } #end input loop
    if ($anyin) {
      print PLTFILE "$forthisin\n";  #includes grid-line \n
    } else {
      print PLTFILE "\n"; #extra grid line (may produce several...)
    }
  } #end state loop AD
}
close PLTFILE;

CCFCONLY:
# processing .ccfc file alone or with meas
open(C,">pred.plt");
open(CP,">plat.plt") if ($NumStates == 1); #plateaux where possible
$inS = 0;
for ($i=0; $i<$NumStates; $i++) {
  $stS[$i] = 0;
}
# .ccfc file stored in $dataline above
for ($k=0; $k<$ccfcount; $k++) {
  @lane = split(" ",$dataline[$k]); # count out run s1 s2 ... sN [3 errors if ccf] (state)
     # count out out2 run1 run3 run4 [5 errors if ccf] (concurrent)
  $input = ($LBin[$inS]+$UBin[$inS])/2;
  if ($lane[0] && $input >= $leftin && $input <= $rightin) { #data count > 0 and in range
    print C "$input";
    $vec = 0;
    for ($i=0; $i<$NumStates; $i++) {
      $j = $stS[$i];
      $stave = ($LBst[$i][$j] + $UBst[$i][$j])/2;
      $vec += $stave**2;
    }
    $vec = sqrt($vec);
    if ($NumStates == 1) {#keep sign
      print C " $stave";
    } else {
      print C " $vec";
    }
    $val = $lane[$ccfcIdx];
    if ($plotOpt eq "S") {
      die "Can't do state for a concurrent component" if ($concurrent);
      $vec = 0;
      for ($i=0; $i<$NumStates; $i++) {
        $vec += ($lane[$ccfcIdx+$i])**2;
      }
      if ($NumStates == 1) { 
        $val = $lane[$ccfcIdx];
      } else { 
        $val = sqrt($vec);
      }
    }
    if ($plotOpt eq "T" && $concurrent) { #add runtimes
      $val = $lane[3] + $lane[4] + $lane[5];
    }
    print C " $val\n";
    if ($NumStates == 1) { #plot plateaux
      if ($altOpt) { #plot towers
        print CP "$LBin[$inS] $LBst[0][$stS[0]] 0\n";
        print CP "$LBin[$inS] $LBst[0][$stS[0]] $val\n";
        print CP "$LBin[$inS] $UBst[0][$stS[0]] $val\n";
        print CP "$LBin[$inS] $UBst[0][$stS[0]] 0\n";
        #print CP "$LBin[$inS] $LBst[0][$stS[0]] 0\n";
        print CP "\n";
        print CP "$UBin[$inS] $LBst[0][$stS[0]] 0\n";
        print CP "$UBin[$inS] $LBst[0][$stS[0]] $val\n";
        print CP "$UBin[$inS] $UBst[0][$stS[0]] $val\n";
        print CP "$UBin[$inS] $UBst[0][$stS[0]] 0\n";
        #print CP "$UBin[$inS] $LBst[0][$stS[0]] 0\n";
        #print CP "\n";
        #print CP "$LBst[0][$stS[0]] $UBin[$inS] 0\n";
        #print CP "$LBst[0][$stS[0]] $UBin[$inS] $val\n";
        #print CP "$LBst[0][$stS[0]] $LBin[$inS] $val\n";
        #print CP "$LBst[0][$stS[0]] $LBin[$inS] 0\n";
        print CP "\n\n";
      } else { #plot floating rectangle
        #strange pattern is to get hidden3d algorithm to work
        print CP "$LBin[$inS] $LBst[0][$stS[0]] $val\n";
        print CP "$LBin[$inS] $UBst[0][$stS[0]] $val\n";
        print CP "\n";
        print CP "$UBin[$inS] $LBst[0][$stS[0]] $val\n";
        print CP "$UBin[$inS] $UBst[0][$stS[0]] $val\n";
        print CP "\n\n";
      }
    }
  } #end of count==0 block

  if (++$inS >= $NumInSubd) {
    $inS = 0;
    BS:
    for ($s=0; $s<$NumStates; $s++ ) {
      if (++$stS[$s] >= $NumStSubd[$s]) {
        $stS[$s] = 0;
      } else { #leave rest alone
        last BS;
      }
    }
  }
}
close(C);
if ($NumStates == 1) {
  close CP;
  if ($altOpt) { #color change
    $plts[scalar(@plts)] = "\"plat.plt\" with lines lt 1";
  } else {
    $plts[scalar(@plts)] = "\"plat.plt\" with lines lt -1";
  }
} else { 
  $plts[scalar(@plts)] = "\"pred.plt\" with points"; #single points 1/subd
}

if ($tomeasure) { #error table & subdomain grid
  $base = 0; #height of subdomain plane
  open(B, ">subd.plt");
  for ($i=0; $i<$NumInSubd; $i++) { #plot subdomains as a low plane
    for ($j=0; $j<$NumStSubd[0]; $j++) {
      print B "$LBin[$i] $LBst[0][$j] $base\n";
    }
    print B "\n";
  }
  for ($j=0; $j<$NumStSubd[0]; $j++) {
    for ($i=0; $i<$NumInSubd; $i++) {
      print B "$UBin[$i] $UBst[0][$j] $base\n";
    }
    print B "\n";
  }
  close(B);
  if ($altOpt) { # subdomains with contours only
    #isn't it better without subdomains?
    #$plts[scalar(@plts)] = "\"subd.plt\" with lines lt 1";
  }

  # approximation file stored in $dataline[0..$ccfcount-1] 
  if ($concurrent && $plotOpt eq "T") {
    print "For error tables use -T[134] option...\n";
    goto SKIPTAB;
  } 
  print "Relative % r-m-s errors in $which \n";
  print "	";
  for ($i=0; $i<$NumInSubd; $i++) {
    if ($UBin[$i] > $leftin && $LBin[$i] <= $rightin) { # in restricted range
      printf "[%2.1f	", $LBin[$i]; #abbreviate the [ , ) to [ 
    }
  }
  print "\n";

  $errIdx = $ccfcIdx + 3;
  if ($concurrent) {
    $errIdx = $ccfcIdx + 5;
  }
  $totArea = 0;
  $totErr = 0;
  $maxErr = 0;
  $k = 0;  #counter in ccfc data
  $Naprx = 0;
  for ($j=0; $j<$NumStSubd[0]; $j++) {
    printf "[%2.1f	", $LBst[0][$j]; #abbreviate the [ , ) to [ 
    PT:
    for ($i=0; $i<$NumInSubd; $i++) {
      unless ($UBin[$i] > $leftin && $LBin[$i] <= $rightin) { #not in restricted range
        next PT;  #skip
      }
      $thisArea = ($UBin[$i]-$LBin[$i])*($UBst[0][$j]-$LBst[0][$j]);
      $k = $j*$NumInSubd + $i;
      @line = split(' ',$dataline[$k]); 
      if ($line[0]) { # has approx value
        $Naprx++;
      }
      if ($line[0] && defined($hitsub[$i][$j]) && $hitsub[$i][$j]) { #has measurement & approx
        if ($concurrent && $plotOpt eq "T") { #no good table for this case
          $sumEsqs = 0;
          for $ei (1..3) {
            $sumEsqs += ($line[$errIdx-1+$ei])**2;
          }
          $thisErr = sqrt($sumEsqs/3.0);
        } else {
          $thisErr = sqrt($errs[$i][$j])/$rmstot;
        }
        $totArea += $thisArea;
        printf "%3.1f	", 100.0*$thisErr;
        $totErr += $thisErr*$thisArea;
        if ($thisErr > $maxErr) {
          $maxErr = $thisErr;
        }
      }
      else {
        print "**	";
      }
    }
  print "\n";
  }  
  if ($totArea > 0) {
    $totErr *= 100.0/$totArea;
    $maxErr *= 100.0;
    printf "Weighted r-m-s (max) error: %4.2f%% (%4.2f%%)\n",$totErr,$maxErr;
  }
print "normalized to rms $rmstot on $count values\n"; #debug
  if ($randOpt) {
    $Allsubs = $NumInSubd*$NumStSubd[0];
    print "Approximation has $Naprx feasible subdomains out of $Allsubs\n";
    print "Measurements fell in $Hit subdomains\n";
  }

} #end error tables etc.
SKIPTAB:

#plot files created above
$to_plt = join(",",@plts);
open(PF,">complot");
print PF qq(set xlabel "Input Space"\n);
if ($concurrent) {
  print PF qq(set ylabel "Parallel Input"\n);
  print PF qq(set zlabel "Output to Parallel"\n) if ($plotOpt eq "S");
} else {
  print PF qq(set ylabel "State Space"\n);
  print PF qq(set zlabel "Result State"\n) if ($plotOpt eq "S");
}
print PF qq(set xrange [$leftin:$rightin]\n);
print PF qq(set zlabel \"$zlab\"\n);
#print PF qq(set nokey\n);
print PF qq(set xtics nomirror\n);
print PF qq(set ytics nomirror\n);
print PF qq(set hidden3d\n);
print PF qq(set ticslevel .1\n);
print PF qq(splot $to_plt \n);
close(PF);  

util::GNUplotit("complot");
#system("gnuplot complot");
