#!/usr/bin/env perl

use XyceRegression::Tools;

$Tools = XyceRegression::Tools->new();
#$Tools->setDebug(1);
#$Tools->setVerbose(1);

# The input arguments to this script are: 
# $1 = location of Xyce binary
# $2 = location of xyce_verify.pl script
# $3 = location of compare script 
# $4 = location of circuit file to test
# $5 = location of gold standard prn file

# If Xyce does not produce a prn file, then we return exit code 10.
# If Xyce succeeds, but the test fails, then we return exit code 2.
# If the shell script fails for some reason, then we return exit code 1.
# Otherwise we return the exit code of compare or xyce_verify.pl

# Since the shell script runs Xyce and the comparison program, it is
# responsible for capturing any error output from Xyce and the STDOUT & STDERR
# from the comparison program.  The outside script, run_xyce_regression,
# expects the STDERR output from Xyce to go into $CIRFILE.err, the STDOUT
# output from comparison to go into $CIRFILE.prn.out and the STDERR output from
# comparison to go into $CIRFILE.prn.err.  

use Getopt::Long;

&GetOptions( "verbose!" => \$verbose );
$XYCE=$ARGV[0];
$XYCE_VERIFY=$ARGV[1];
$XYCE_VERIFY=~ s/xyce_verify/ACComparator/;
#$XYCE_COMPARE=$ARGV[2];
$CIRFILE=$ARGV[3];
$GOLDPRN=$ARGV[4];
$DATAFILE="ErrorTestACRawData.cir.FD.prn";

# these are the tolerances used in comparing the measures calculated by this 
# script to those calculated by Xyce.  Zerotol is needed because some measured
# values should be zero (and reltol won't make sense in that case).

my $absTol = 4.0e1;
my $relTol = 2.6;
my $zeroTol = 1.0e-5;

if (defined($verbose)) { $Tools->setVerbose(1); }

sub verbosePrint { $Tools->verbosePrint(@_); }

$retval = -1;
# wrapXyce is not fully compatible with AC as it
# signals an error if the output file doesn't end with End Of Xyce(TM) Simulation
# which AC does not output.
#$retval=$Tools->wrapXyce($XYCE,$CIRFILEwithPrintRaw);
$CMD="$XYCE $CIRFILE > $CIRFILE.out 2> $CIRFILE.err";
#
# lower byte is not relevent here.  xyce_verify's return code is in
# the upper byte to shift it 8 bits.
$retval = system("$CMD");
$retval = $retval >> 8;

# check that Xyce exited without error
if ($retval != 0) { print "Exit code = $retval\n"; exit $retval; }
if (not -s "$CIRFILE.FD.prn" ) { print "Exit code = 14\n"; exit 14; } 
#
# Did we make a measure file
#
if (not -s "$CIRFILE.mt0" ) { print "Exit code = 17\n"; exit 17; }

#
# Now we need to count the number of .MEASURE statemets in the netlist
# and make sure we have some output for each one in the output file
#
open(NETLIST, "$CIRFILE");
$numMeasures=0;
while( $line=<NETLIST> )
{
  if( $line =~ /^\.measure/i)
  {  
    $numMeasures++;
  }
}
close(NETLIST);

#
# Now look for the measure output file and count how many measurements are in it
#
open(RESULTS, "$CIRFILE.mt0");
$numMeasuresFound=0;
my @measureNames;
my @measureVals;
while( $line=<RESULTS> )
{
  if( $line =~ /=/)
  {  
    # parse out NAME = Value 
    chop $line;
    ($name,$sep,$value) = split(/ /, $line);
    print "name = $name value=$value\n";
    push @measureNames, $name;
    push @measureVals, $value;
    $numMeasuresFound++;
  }
}
close(RESULTS);

if( $numMeasures != $numMeasuresFound )
{
  verbosePrint "Number of requested measures did not match number of printed measures.\n";
  print "Exit code = 2\n"; exit 2; 
}

#
# Make sure the measurement was not output multiple times in parallel
# in either the output file or the measure file.
for (my $i=0; $i < $numMeasures; $i++)
{
   $numMeasOut = `grep -ic $measureNames[ $i ] $CIRFILE.out`;
   if ( $numMeasOut != 1 )
   {
     print "Xyce printed out $measureNames[ $i ] measure to standard output multiple times.\n";
     $retval = 2;
   }
   $numMeasMT0 = `grep -ic $measureNames[ $i ] $CIRFILE.mt0`;
   if ( $numMeasMT0 != 1 )
   {
     print "Xyce printed out $measureNames[ $i ] measure to file multiple times.\n";
     $retval = 2;
   }
}

if ( $retval != 0 )
{
  verbosePrint "test Failed!\n";
  print "Exit code = $retval\n";
  exit $retval;
}


# Now look at Xyce's output file and read in Xyce's data
open(XYCE_RESULTS, "$CIRFILE.FD.prn");
my @calculatedMeasureVals;
my @headerVarNames;
my @dataFromXyce;
my $numPts = 0;
while( $line=<XYCE_RESULTS> )
{
  if( $line =~ /FREQ/)
  {  
    # parse out Header names 
    chop $line;
    @headerVarNames = split(/\s+/, $line);
  }
  elsif($line =~ /End of/)
  {
    # end of Xyce simulation line 
    # do nothing in this case
  }
  else
  {
    # save var values 
    chop $line;
    # Remove leading spaces on line, otherwise the spaces become element 0 of "@lineOfDataFromXyce" instead of the first column of data.
    $line =~ s/^\s*//;
    @lineOfDataFromXyce = (split(/\s+/, $line));
    for ($varNum=0; $varNum < @lineOfDataFromXyce; $varNum++)
    {
      #print "setting dataFromXyce[$numPts][$varNum] = $lineOfDataFromXyce[$varNum] \n";
      $dataFromXyce[$numPts][$varNum] = $lineOfDataFromXyce[$varNum];
    }
    $numPts++;
  }
}
close(XYCE_RESULTS);

# Now load the raw data file 
open(OTHER_RESULTS, "$DATAFILE");
my @dataFromExtFile;
my $numExtPts = 0;
while( $line=<OTHER_RESULTS> )
{
  if( $line =~ /TIME/)
  {  
    # parse out Header names 
  }
  elsif($line =~ /End of/)
  {
    # end of Xyce simulation line 
    # do nothing in this case
  }
  else
  {
    # save var values 
    chop $line;
    # Remove leading spaces on line, otherwise the spaces become element 0 of "@lineOfDataFromXyce" instead of the first column of data.
    $line =~ s/^\s*//;
    @lineOfData = (split(/\s+/, $line));
    for ($varNum=0; $varNum < @lineOfData; $varNum++)
    {
      #print "setting dataFromExtFile[$numPts][$varNum] = $lineOfData[$varNum] \n";
      $dataFromExtFile[$numExtPts][$varNum] = $lineOfData[$varNum];
    }
    $numExtPts++;
  }
}
close(OTHER_RESULTS);




# now calculate the values for these measures.  This is the part of 
# the test script that will be different for each one. 
# 

# only set up to do one residual error calculations now.
my $L2NormCalc=0.0;

# loop over all of the external data
for $i (0 .. $#dataFromExtFile )
{
  # for each external data point find simulation data values that bound it
  for $j (1 .. $#dataFromXyce )
  {
    if( ($dataFromXyce[$j-1][1] <= $dataFromExtFile[$i][1] ) && ($dataFromXyce[$j][1] >= $dataFromExtFile[$i][1] ) )
    {
      # found bounding simulation data points.  Interpolate to the time of the external data point
      # take closest point for now 
      if( abs($dataFromXyce[$j-1][1] - $dataFromExtFile[$i][1]) < abs($dataFromXyce[$j][1] - $dataFromExtFile[$i][1])  )
      { 
        $L2NormCalc = $L2NormCalc + abs($dataFromXyce[$j-1][2] - $dataFromExtFile[$i][2])*abs($dataFromXyce[$j-1][2] - $dataFromExtFile[$i][2]);
      }
      else
      {
        $L2NormCalc = $L2NormCalc + abs($dataFromXyce[$j][2] - $dataFromExtFile[$i][2])*abs($dataFromXyce[$j][2] - $dataFromExtFile[$i][2]);
      }
    }
  }
}
$L2NormCalc = sqrt( $L2NormCalc );

my @absMeasureError;
my @relMeasureError;
for $i (0 .. 0 )
{
  $absMeasureError[$i] = abs( $measureVals[$i] - $L2NormCalc );
  $relMeasureError[$i] = abs($absMeasureError[$i] / $L2NormCalc );
}


$retval = 0;
foreach $i (0 .. 0)
{
  print "For measure $measureNames[$i] calculated value = $L2NormCalc: abserror = $absMeasureError[$i] relerror=$relMeasureError[$i]\n";
  if( ($absMeasureError[$i] > $absTol) || (($measureVals[$i] > $zeroTol) && ($relMeasureError[$i] > $relTol) ))
  {
    print "Out of tolerance with abstol = $absTol and reltol = $relTol \n";
    $retval = 2;
  }
}


if ( $retval != 0 )
{
  verbosePrint "test Failed!\n";
  print "Exit code = $retval\n"; 
  exit $retval;
}
  
verbosePrint "test Passed!\n";
print "Exit code = $retval\n"; 
exit $retval;
