#! /usr/bin/perl -w
# XcuteS  [-T] [-S] [-r] <.ccf[tc] file> [<count>]
#  "runs" and plots a component's output behavior.
#
#  Default is output behavior; -T for run time; -S for state.
#  <count> defaults to 50 points to be sampled; -r means use random sequences
#    to override counts in .ccf.
#
#  For .ccft, plot only subdomain plateaux.
#  For .ccf, plot actual component, and if .ccft is valid, also subdomain plateaux.
#  For .ccfc, plot only approximation, using vector sum of states for that dimension;
#   If only one state, plot as plateaux like .ccft
#   write .datt files with all input-state tuples (and output tuples for state choice).

use samplings;

$plotOpt = "O"; #default is to plot output
$randOpt = 0; #default sampling is systematic
$highwater = -1; #last "-" arg
for ($opt=0; defined($ARGV[$opt]); $opt++) {
  if ($ARGV[$opt] eq "-T") {
    $plotOpt = "T";
    $highwater = $opt;
  } elsif ($ARGV[$opt] eq "-S") {
    $plotOpt = "S";
    $highwater = $opt;
  } elsif ($ARGV[$opt] eq "-r") {
    $randOpt = 1;
    $highwater = $opt;
  }
}
die "A .ccf[tc] file must be given" unless (defined($ARGV[$highwater+1]));
$ccf = $ARGV[$highwater+1];
die "given file ",$ccf," not found" unless (-e $ccf);
@sep = split(/\./,$ccf);
$ext = $sep[scalar(@sep)-1];
$samplecount = 50;
if (defined($ARGV[$highwater+2])) {
  $samplecount = $ARGV[$highwater+2];
}
@plts = ();
if ($ext eq "ccf") { # .ccf file, execute code
#TBD random-sequence option not implemented
#  If .ccft exists, use its counts to select subdomains?
  open(CCF, $ccf);
  chop($name = <CCF>);
  @sep = split(/\./,$name); 
  $sf = $sep[0].".state";
  unless (-x $name) {die "No code file to measure";}
  #find the extremes of the input domain
  $line = <CCF>;
  @vals = split(" ",$line);
  $leftin = $vals[0];
  while ($line ne "\n") {
    @vals = split(" ",$line);
    $line = <CCF>;
  }
  $rightin = $vals[1];
  #find the extremes of the state domain
  $line = <CCF>;
  @vals = split(" ",$line);
  $leftst = $vals[0];
  while ($line) {
    @vals = split(" ",$line);
    $line = <CCF>;
  }
  $rightst = $vals[1];
  $countin = $samplecount;
  $countst = 20;
  #debug
  #print "input: [$leftin, $rightin) state: [$leftst, $rightst) \n";

  #collect the lists of inputs/states to use
  &samplings::init($leftin,$rightin,$countin);
  $numin = 0;
  while (($input = &samplings::next) ne ".") {
    $Ins[$numin++] = $input;
  }
  &samplings::init($leftst,$rightst,$countst);
  $numst = 0;
  while (($input = &samplings::next) ne ".") {
    $Sts[$numst++] = $input;
  }
  open(M,">meas.plt");
  $sed = "sed 's/^/out:  /'";  # command to reproduce stdin to stdout with a leading "out:  "
  for ($i=0; $i<$numst; $i++) { #loop over states
    for ($j=0; $j<$numin; $j++) { #loop over inputs
      open(STS,">$sf");
      print STS $Sts[$i];
      close(STS);
      $cmd = "(echo ".$Ins[$j]." | ".$name." | ".$sed.") 2>&1|" ;
      open(PM, $cmd);
      $rmeas = 0;
      while ($line = <PM>) {  #not eof, all output from this system on this input
        # results all come out together, but stdout is preceeded by "out:  "
        @outstrm = split(' ',$line);
        #debug
        #print "$line \n";
        if ($outstrm[0] =~ /out:/) {
          $meas = $outstrm[1];
        }
        else {
          $rmeas += $outstrm[0];
        }
      }
      close(PM);
      print M "$Ins[$j] $Sts[$i] $meas \n" if ($plotOpt eq "O");
      print M "$Ins[$j] $Sts[$i] $rmeas \n" if ($plotOpt eq "T");
      if ($plotOpt eq "S") {
        open(STS,"<$sf");
        $smeas = <STS>;
        close(STS);
      print M "$Ins[$j] $Sts[$i] $smeas \n";
      }
    }
    print M "\n"; 
  } #end loop over states
  close(M); 

  close(CCF);
  $plts[scalar(@plts)] = "\"meas.plt\" with lines";
} #end of executing code case

if ($ext eq "ccfc") {
  open(CCFC, "<$ccf");
  $line = <CCFC>;
  @lane = split(" ",$line); # theory number-of-states number-input-subdomains numbers-state-subdomains state-initials
  die "bad .ccfc file format in $ccf" unless ($lane[0] eq "theory");
  $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);
    $LBin[$i] = $lane[0];
    $UBin[$i] = $lane[1];
  }
  for ($i=0; $i<$NumStates; $i++) {
    for ($j=0; $j<$NumStSubd[$i]; $j++) {
      $line = <CCFC>;
      @lane = split(" ",$line);
      $LBst[$i][$j] = $lane[0];
      $UBst[$i][$j] = $lane[1];
    } 
  }
  open(C,">pred.plt");
  open(D,">pred.datt");
  $inS = 0;
  for ($i=0; $i<$NumStates; $i++) {
    $stS[$i] = 0;
  }
  while ($line = <CCFC>) {
    @lane = split(" ",$line); # count out run s1 s2 ... sN
    if ($lane[0]) { #data count > 0
      $input = ($LBin[$inS]+$UBin[$inS])/2;
      print C "$input";
      print D "$input";
      $vec = 0;
      for ($i=0; $i<$NumStates; $i++) {
        $j = $stS[$i];
        $stave = ($LBst[$i][$j] + $UBst[$i][$j])/2;
        print D " $stave";
        $vec += $stave**2;
      }
      if ($NumStates > 1) {
        $vec = sqrt($vec);
      } else { #retain sign if just one state
        $vec = $stave;
      }
      print C " $vec";
#TBD:  plot plateau if just one state
      if ($plotOpt eq "O") {
        print C " $lane[1]\n"; #output
        print D " $lane[1]\n"; #output
      } elsif ($plotOpt eq "T") {
        print C " $lane[2]\n"; #run time
        print D " $lane[2]\n"; #run time
      } else { #state
        $vec = 0;
        for ($i=0; $i<$NumStates; $i++) {
          print D " $lane[2+$i]";
          $vec += ($lane[2+$i])**2;
        }
        if ($NumStates == 1) {
          $vec = $lane[2]; #retain sign if just one state
        }
        print C " $vec\n";  #state
        print D "\n"; 
      }
    } #end data count > 0

      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);
  close(D);
  $plts[scalar(@plts)] = "\"pred.plt\" with points";
} else { # ccft case, may fall in from ccf case
  $isccft = 0;  #assume no .ccft file
  if ($ext eq "ccft") {
    $ccftFile = $ccf;
    $ccf = substr($ccf,0,-1); #remove "t"
    die "no configuration file $ccf exists" unless (-e $ccf);
    $isccft = 1;
  } else { #fell in from .ccf case
    $ccftFile = $ccf."t";
  }
  if (-e $ccftFile) {
    $isccft = 1;
  } else {
    warn "no approximation file (.ccft) exists for the configuration file $ccf";
  }
  open(CCF, "<$ccf");
  $name = <CCF>; #discard code name
  open(B,">subd.plt"); #subdomains
  $numin = 0;
  $numst = 0;
  $inputProc = 1;  #doing input subdomains first
  L:
  while ($line = <CCF>) { #read and store boundaries, plot theory.
    @subd = split(" ",$line);
    if ($inputProc) {
      if ($line eq "\n") {
        $inputProc = 0;
        next L;
      }
      $LBi[$numin] = $subd[0];
      $UBi[$numin++] = $subd[1];
    } else { #state subs now, go to EOF
      $LBs[$numst] = $subd[0];
      $UBs[$numst++] = $subd[1];
    }
  }
  close(CCF);
  $base = 0; #height of subdomain plane
  for ($i=0; $i<$numst; $i++) { #plot subdomains as a low plane
    for ($j=0; $j<$numin; $j++) {
      print B "$LBi[$j] $LBs[$i] $base\n";
    }
    print B "$UBi[$numin-1] $LBs[$i] $base\n";
    print B "\n";
  }
  for ($j=0; $j<$numin; $j++) {
    print B "$LBi[$j] $UBs[$numst-1] $base\n";
  }
  print B "$UBi[$numin-1] $UBs[$numst-1] $base\n";
  close(B);
  $plts[scalar(@plts)] = "\"subd.plt\" with lines";

  if ($isccft) { # Theory file exists 
    open(C,">pred.plt"); #calculation values
    $WtdVerr = 0;
    $WtdRerr = 0;
    open(CCFT, "<$ccftFile");
    $name = <CCFT>; #discard control/name line
    $stride = 7; #line contents:  num out run state outerr runerr staterr

    for ($lno = 0; $lno<$numst; $lno++) {
      $line = <CCFT>;
      die "ccft file ended prematurely" unless($line);
      @subd = split(" ",$line);
      for ($k=0;$k<$numin;$k++) {
        if ($subd[$stride*$k] > 0) {
          $val = $subd[$stride*$k+1];
          $run = $subd[$stride*$k+2];
          $state = $subd[$stride*$k+3];
          $item = $val if ($plotOpt eq "O");
          $item = $run if ($plotOpt eq "T");
          $item = $state if ($plotOpt eq "S");
          print C "$LBi[$k] $LBs[$lno] $item\n";
          print C "$LBi[$k] $UBs[$lno] $item\n";
          print C "$UBi[$k] $UBs[$lno] $item\n";
          print C "$UBi[$k] $LBs[$lno] $item\n";
          print C "$LBi[$k] $LBs[$lno] $item\n";
          print C "\n";
        }
      }
      $kLast = -1;
      for ($k=0;$k<$numin;$k++) {
        if ($subd[$stride*$k] > 0) {
          if ($kLast>=0 && $k != $kLast+1) { #previous plateau not drawn
            print C "\n";
          }
          $val = $subd[$stride*$k+1];
          $run = $subd[$stride*$k+2];
          $state = $subd[$stride*$k+3];
          $item = $val if ($plotOpt eq "O");
          $item = $run if ($plotOpt eq "T");
          $item = $state if ($plotOpt eq "S");
          print C "$LBi[$k] $LBs[$lno] $item\n";
          print C "$UBi[$k] $LBs[$lno] $item\n";
          $verr = 100*$subd[$stride*$k+4];
          $rerr = 100*$subd[$stride*$k+5];
          $serr = 100*$subd[$stride*$k+6];  
          $kLast = $k;
        }
      }
      #space for grid lines  
      print C "\n";
      $kLast = -1;
      for ($k=0;$k<$numin;$k++) {
        if ($subd[$stride*$k] > 0) {
          if ($kLast>=0 && $k != $kLast+1) { #previous plateau not drawn
            print C "\n";
          }
          $val = $subd[$stride*$k+1];
          $run = $subd[$stride*$k+2];
          $state = $subd[$stride*$k+3];
          $item = $val if ($plotOpt eq "O");
          $item = $run if ($plotOpt eq "T");
          $item = $state if ($plotOpt eq "S");
          print C "$LBi[$k] $UBs[$lno] $item\n";
          print C "$UBi[$k] $UBs[$lno] $item\n";
          $kLast = $k;
        }
      }
      print C "\n";
      unless(1) { #not yet
      $WtdVerr += $verr*($UB[$k] - $LB[$k]);
      $WtdRerr += $rerr*($UB[$k] - $LB[$k]); 
      printf "[%.1f, %.1f):	%3.2f%%  %3.2f%% %%3.2f%%\n",$LB[$k], $UB[$k], $verr, $rerr, $serr;
      }  
      } #end loop over states (lines in file)
      close(C);
#debug
     # print STDERR "Too many lines in .ccft file\n" if ($line = <CCFT>);
      unless(1) { #not yet
      $WtdVerr /= $UB[$k-1] - $LB[0];
      $WtdRerr /= $UB[$k-1] - $LB[0];
      printf "Weighted errors: %3.1f%%   %3.1f%%\n", $WtdVerr, $WtdRerr;
      }  
      $plts[scalar(@plts)] = "\"pred.plt\" with lines";
    } #end isccft
  } #end ccft case

$to_plt = join(",",@plts);
open(PF,">complot");
print PF "set terminal X11\n";
print PF qq(set xlabel "Input Space"\n);
print PF qq(set ylabel "State Space"\n);
print PF qq(set zlabel "Outputs"\n) if ($plotOpt eq "O");
print PF qq(set zlabel "Run Time"\n) if ($plotOpt eq "T");
print PF qq(set zlabel "Result State"\n) if ($plotOpt eq "S");
#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 .05\n);
print PF qq(splot $to_plt \n);
#print PF qq(splot $pred $meastg \n) if ($plotOpt eq "O");
#print PF qq(splot "subd.plt" with lines, "runmeas.plt" with lines, "runpred.plt" with lines\n) if ($plotOpt eq "T");
#print PF qq(splot "subd.plt" with lines, "statemeas.plt" with lines, "statepred.plt" with lines\n) if ($plotOpt eq "S");
print PF qq(pause -1 \n);
close(PF);  

system("gnuplot complot");
