#!/usr/bin/perl -w
#
# Xsys [-V] [start stop count]
#
#  Runs SystemCode using range from system.pscf (first component)
#   unless range is given.
#  SystemCode is written as a loop terminated by sending it "."
#    so it is only started once, then fed inputs repeatedly.
#
#  Random sampling only.
#
#  Two forms of output file for
#    plotting/analysis are created:  *.datt  in which all component states
#    are included, and *.dat for plotting.  In output.datt a typical line is:
#      input state1 state2 ... stateN output
#  In output.dat, the state N-tuple is reduced to
#    a single value of sqrt((state1**2 + ... + stateN**2)/N).
# 

use component;
use sampling;
use status;

########################################subroutine#######################
sub warnmess{ #print warning based on V option
  unless ($Voption && defined($_[0])) {return;}
  $mess = "";
  MLOOP: for ($wm=0;;$wm++) {
    unless (defined($_[$wm])) {last MLOOP;}
    $mess = $mess.$_[$wm];
  }
warn $mess, "\n";  #don't print line from which this came...
}
#######################################################################

die "No SystemCode to execute" unless (-e "SystemCode");
$Voption = 0; #indicates verbose mode, includes spin counter

# Process command-line arguments
if (defined($ARGV[0]) && $ARGV[0] eq "-V") { $Voption = 1;}
$parmsupplied = 0;
if (defined($ARGV[$Voption])) { #range params
  die "All three range parameters must be given" unless (defined($ARGV[$Voption+2]));
  $parmsupplied = 1;
  $input_start = $ARGV[$Voption];
  $input_end = $ARGV[$Voption+1];
  $seq_count = $ARGV[$Voption+2];
} else {  #parameters not given, get from system.pscf file
  open (PSCF, "<system.pscf") or die "Must have \"system.pscf\" file";
  $first = <PSCF>; #polish line
  @polish = split(" ",$first);
  $firstcomp = $polish[0];
  for ($cc=1; $cc<=$firstcomp; $cc++) { #skip down to the first polish component
    $first = <PSCF>;  #.ccf names
  } 
  close (PSCF);
#print "first component: $first"; #debug
  open (FCOMP,"<$first") or die "Something wrong with first component $first";
  $lineval = <FCOMP>; #skip header
  $lineval = <FCOMP>; #first subdomain
  @trip = split(" ",$lineval);
  $input_start = $trip[0];
  $seq_count = $trip[2]; #count
  PL:
  while ($lineval=<FCOMP>) {
    chomp($lineval);
    last PL if ($lineval eq "");
    @trip = split(" ",$lineval);
    $seq_count += $trip[2]; #count
  }
  $seq_count = 20 if ($seq_count < 20);
  $input_end = $trip[1];
  close (FCOMP);
}

unlink glob("*.errlog");
#system "rm -f *.errlog"; #clear log files in case executing a SystemCode with XqtS

if ($Voption) { &status::init($seq_count, "Sampling SystemCode"
              ." in [".$input_start.",".$input_end.") for ".$seq_count." sequences");} 

$statefile = "SystemCode.state";
#$compObj = component->new(component => "perl SystemCode", statefile => $statefile);

# Prepare input sampling

$count = 0;
@inputs = ();
@in_state = ();
@outputs = ();
@runtimes = ();
@out_state = ();
$test_point_count = 0;

srand(3.14159265); #we want the same sequence every time this runs
$compObj = component->new(component => "perl SystemCode", statefile => $statefile);
  $input = ($input_end + $input_start)/2;
  $compObj->{control} = "begin";
  $compObj->run($input) || die "Can't run SystemCode to initialize";

# Loop through input sequences
for $i (1..$seq_count) {
  $input_freq = int(rand($seq_count)) + 1;
  $test_point_count += $input_freq;
  $input_sampler = sampling->randomize($input_start, $input_end, $input_freq);
  unlink glob("*.state");  #set all components to initialize
  #run code once on midpoint to get initial state
  $input = ($input_end + $input_start)/2;
  $compObj->{control} = "cont";
  $compObj->run($input) || die "Can't run SystemCode to initialize";
  $theout = $compObj->getOutput;
  #leave state in compObj for code to get below
  if ($theout =~ /LOOPING/) {
    die "Code is probably looping on state-initialization input $input";
  }
#chomp($Istate = $compObj->getState); print "initial state was: $Istate\n"; #debug
  # Loop through input
  $input = $input_sampler->next();
#print "Input sequence # $i\n"; #debug
  while ($input !~ /^\.$/) {
    $inputs[$count] = $input; # Record starting values
    chomp($in_state[$count] = $compObj->getState);
#print "$input $in_state[$count] \n"; #debug
  # Run component
    $compObj->{control} = "cont";
    $compObj->run($input) || die "Cannot run SystemCode";
    $theout = $compObj->getOutput;
    $thestate = $compObj->getState;
#print "$theout\n"; #debug
    if ($theout =~ /LOOPING/) {
      die "Code is probably looping on input $input, state $thestate";
    }
    $outputs[$count] = $theout;
    $runtimes[$count] = $compObj->getRuntime;
   # Get persistent state
    chomp($out_state[$count] = $compObj->getState);
#print "$out_state[$count]"; die "...that's one";  #debug
    $count++;
    $input = $input_sampler->next();
  } # Next input

#print "seq ctr: $count \n";  #debug
  if ($Voption) { &status::progress($i); }
} # Next input sequence

  $compObj->{control} = "end";
  $compObj->run(".") || die "Failed to terminate SystemCode";

if ($Voption) { #get past no-linefeed lines
  print "\n";
}
unlink glob("*.state");  #kill all FIFO files

# Create graphing files

open(OUTPUT, ">output.dat");
open(RUNTIME, ">runtime.dat");
open(STATE, ">state.dat");
open(OUTALL, ">output.datt");
open(RUNALL, ">runtime.datt");
open(STATEALL, ">state.datt");
PL: 
for ($i = 0; $i < $count; $i++) { 
  $combinstate = 0; #init sum of squares of states
  $comboutstate = 0;
  $in_state_all = $in_state[$i];
  $out_state_all = $out_state[$i];
  @composin = split(" ",$in_state[$i]);
  if (scalar(@composin) > 1) { #fix plot files
    @composout = split(" ",$out_state[$i]);
    $inct = 0; $outct = 0;
    for ($ss=0;$ss<scalar(@composin);$ss++) {
      if ($composin[$ss] =~ /\d/) {
        $combinstate += $composin[$ss]**2;
        $inct++;
      }
      if ($composout[$ss] =~ /\d/) {
        $comboutstate += $composout[$ss]**2;
        $outct++;
      }
    }
    $in_state_all = sqrt($combinstate/$inct);  #magnitude, not tuple
    $out_state_all = sqrt($comboutstate/$outct);
  }
  print OUTPUT $inputs[$i]." ".$in_state_all." ".$outputs[$i]."\n";
  print RUNTIME $inputs[$i]." ".$in_state_all." ".$runtimes[$i]."\n";
  print OUTALL $inputs[$i]." ".$in_state[$i]." ".$outputs[$i]."\n";
  print RUNALL $inputs[$i]." ".$in_state[$i]." ".$runtimes[$i]."\n";
  print STATEALL $inputs[$i]." ".$in_state[$i]." ".$out_state[$i]."\n";
  print STATE $inputs[$i]." ".$in_state_all." ".$out_state_all."\n";
}
close(RUNTIME);
close(OUTPUT);
close(STATE);
close(OUTALL);
close(RUNALL);
close(STATEALL);

#check for .errlog files 
$co = glob("*.errlog");
#$co = system "ls *.errlog > /dev/null 2>&1";
if (defined($co) && ($co =~ /log/)) { 
  warnmess "There were error logs $co created by XqtS on this run:";
  #print `ls *.errlog`;
}
print "Sampled $seq_count sequences, $test_point_count points.\n";
