#!/usr/bin/perl -w
#
# compt script used to process one component with state or a system
#   contining components with state.
# Also handles concurrent components where "state" is the input
#  from another process running in parallel.
#    compt [options] xxx.ccf
# It is called from COMPSt for each component of a system, the component
#  names obtained from system.pscf .  
# Cannot do incremental measurement yet.
#
# -R for random sequences; -U for uniform systematic samples
#
# Script should not be used on a stateless component (use COMPFt -S instead)
#
# -X option substitutes the .ccft "theory" file (which may already exist) to run.
#
# -Z forces (re)measurement even if file times indicated it's not needed
#
# The regular mode is interactive (turned off with -I option).
#  The verbose -V option includs a crude spin counter.  In interactive, the
#  results are graphed, with choices of what is shown.  
#  TBD:  remove this cute interaction stuff; never used.  But need to display
#   functional/state/runtime somehow?
#
# The .ccfc file is produced for a component.
#
# Functionality of the form:
#  compt -R [options] xxx [start stop count]
# asks for domain range, sampling info (if not given on command line)
# to run without subdomains.  Similar functionality for -U needs
# more thought; without subdomains -U makes no sense?  Best not to
# use for the present.
#
# -R -O (oh) option runs SystemCode (usually produced by SmeasS) from information 
#  in the system.pscf file.  The parameters are taken from the .ccf file
#  of the first component in the system, and 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).
#  -U -O makes little sense (see above concerning parameters); best not to try.

use component;
use sampling;
use status;
use stepfunc;
use CompCheck2;
use File::Copy;
use util;

########################################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...
}
#######################################################################

$Roption = 1; #default random sequences
$Ooption = 0; #runs a "system" of multiple components, with composite state
$Voption = 0; #indicates verbose mode, includes spin counter
$Ioption = 1; #interactive mode; -I means off
#not sure that -X works...
$Xoption = 0; # "execute" a theory, .ccft file (but still start with .ccf param)
$Zoption = 0; # force sampling even if files appear good

$ccfProcessed = 0; # indicates that subdomain data has been read from a ccf file

# Process command-line arguments

ARGS:
$component = shift || die "No component executible or ccf file specified";
if ($component eq "-R") { $Roption = 1; goto ARGS; }
if ($component eq "-U") { $Roption = 0; goto ARGS; }
if ($component eq "-O") { $Ooption = 1; goto ARGS; }
if ($component eq "-V") { $Voption = 1; goto ARGS; }
if ($component eq "-I") { $Ioption = 0; goto ARGS; }
if ($component eq "-X") { $Xoption = 1; goto ARGS; }
if ($component eq "-Z") { $Zoption = 1; goto ARGS; }
if (substr($component,0,1) eq "-") {goto ARGS;} #skip any strange options

#print " |$component|\n";  #debug
if ($component =~ /\.ccf$/) {
# read input range and state range from ccf file
  $ccf_component = $component;  #ccf name
  open(COMP, $component) || die "Invalid ccf file specified";
    #first line of file points to real executible
    $compline = <COMP>;
    @compand = split(' ',$compline);
    $component = $compand[0];
    $compExecut = $component;
    if (defined($compand[1])) { 
      $concurrent = ($compand[1] eq "concurrent");
      $Roption = 0 if ($concurrent);  #really means "sequences"
      # else it is "state"
    } else { #should not be in compt!
      die "Can't process components neither concurrent nor w/state";
    }
    $array = \@input_subs;
    $line_count = 0;
    $in_sub_cnt = 0;
    $blankline = 0;
    while($linearr = <COMP>) {
      #read line from .ccf file
      if ($linearr eq "\n") {
        $blankline = 1;
        $in_sub_cnt = $line_count;
        $line_count = 0;
        $array = \@state_subs;
      }
      else {
        chomp($array->[$line_count] = $linearr);
        $line_count++;
      }
    }
  die "Component description file $ccf_component apparently stateless" unless ($blankline);
  close(COMP);
  $input_start = (split(' ',$input_subs[0]))[0];
  $input_end = (split(' ',$input_subs[$in_sub_cnt-1]))[1];
  # no real -R with concurrent (yet)
  if ($Roption) {
    $seq_count = 0;
    foreach $subdomain (@input_subs) {
      $seq_count += (split(' ',$subdomain))[2];
    }
    $seq_count = 30 if ($seq_count < 30);
  } else {
    $input_freq = 0;
    foreach $subdomain (@input_subs) {
      $input_freq += (split(' ',$subdomain))[2];
    }
    $state_freq = 0;
    foreach $subdomain (@state_subs) {
      $state_freq += (split(' ',$subdomain))[2];
    }
  }
  #override from command line?
  $Any = shift;
  if (defined($Any)) { #extra param(s) present
#print "|$Any| extra stuff on command line\n";  #debug
    $Zoption = 1; #force (re)process
    $input_start = $Any;
    $input_end = shift;
    $seq_count = shift;
    unless ($Roption) {
      $state_start = $seq_count;
      $state_end = shift;
      $input_freq = shift;
      $state_freq = shift;
    }
  }
  $ccfProcessed = 1;
  $comptheory = $ccf_component."c";
  $ccfcExist = -e $comptheory;
  if ($Xoption) {
    die "No 'theory' file to 'execute'" unless ($ccfcExist);
  } else {
    die "No component code $compExecut to execute" unless (-e $compExecut);
  }
# print "sequences: $seq_count \n";  #debug
} else {  #parameter is not a .ccf file
  $compExecut = $component;
  die "Invalid executable specified" unless (-e $compExecut);
  $Xoption = 0; #must have .ccf to execute theory
  if ($Ooption) { #get input parameters from first component for system
    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);
  }
}
#ask user for anything that is missing
unless (defined($input_start) && defined($input_end)) {
  print "Enter starting value for input range: "; chop($input_start = <STDIN>);
  print "Enter ending value for input range: "; chop($input_end = <STDIN>);
}
if ($Roption) {
  unless (defined($seq_count)) {
    print "Enter number of input sequences to sample: "; chop($seq_count = <STDIN>);
  }
} else {
  unless ($ccfProcessed) { #will later get -U parameters from stored data
    unless (defined($state_start) && defined($state_end)) {
      print "Enter starting value for state range: "; chop($state_start = <STDIN>);
      print "Enter ending value for state range: "; chop($state_end = <STDIN>);
    }
    unless (defined($input_freq) && defined($state_freq)) {
      print "Enter sampling frequency for input: "; chop($input_freq = <STDIN>);
      print "Enter sampling frequency for state: "; chop($state_freq = <STDIN>);
    }
    @input_subs = ( "$input_start $input_end $input_freq" );
    @state_subs = ( "$state_start $state_end $state_freq" );
  }
}

if ($ccfProcessed) { #incremental stuff requires ccf file
  $incrproc = 0; #assume no incremental processing
  if (-e $comptheory && !$Zoption) {
    open (CCFT, "<$comptheory");
    $tmp = <CCFT>; #examine the code line
    @tmp = split(" ",$tmp);
    $ccfcExist = 0 unless (defined($tmp[5]) && (($tmp[5] eq "R") && $Roption)||(($tmp[5] eq "U") && !$Roption));
    # reliability not ready for state --  TBD
    #if ($Foption) {
      #$ccfcExist = 0 unless (defined($tmp[3]) && ($tmp[2] eq "r") && ($tmp[3] == $confidencepct));
    #}
    @statvec = stat($ccf_component); #for ccf file
    $ccfileTime = $statvec[9];
    @statvec = stat($compExecut); #for executable file
    $binfileTime = $statvec[9];  
    @statvec = stat($comptheory); #for theory ccf file
    $ccfcExist = 0 unless ($statvec[9] > $binfileTime);
    if ($statvec[9] < $ccfileTime && $statvec[9] > $binfileTime) {
      $incrproc = 1;
      $ccfcExist = 0;
    }
    close (CCFT);
  } else {
    $ccfcExist = 0;
  }
  close(COMP);

  if ($ccfcExist && !$Zoption) { #don't measure
    goto INTACT;
  }
  #warnmess "Generating $comptheory...";
  warnmess "Creating testing-measurements file $comptheory ...";
  $tense = "could not";
  if ($incrproc) { #attempt incremental
  $incrproc = 0;  #debug:  switch comment to next line
    #$incrproc = &CompCheck::CheckIncr($ccf_component); #creates .ccfi skeleton if small changes
  }
#die "stop and look at all files!";  #debug
  if ($incrproc) {  #may be changed in CheckIncr
    $tense = "should";
    $oldtheory = $comptheory."b";
    copy($comptheory, $oldtheory);
    #system "rm -f $oldtheory";
    #system "cp $comptheory $oldtheory";  # .ccftb is previous theory file
  }
  warnmess "  (Measurements $tense be incremental)";
  if ($incrproc) {
#TBD incremental processing
    warnmess "But not implemented yet!";
    #open(OLDTH, "<$oldtheory");
    #$tl = <OLDTH>;  #skip header line -- same as if newly created
#TBD any use of old file...
  }
}

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

if ($ccfProcessed) { &stepfunc::init(\@input_subs, \@state_subs); }

if ($Roption) {
  if ($Voption) { &status::init($seq_count, "Sampling ".$compExecut
              ." in [".$input_start.",".$input_end.") for ".$seq_count." sequences");} 
} else {
#print "$input_freq x $state_freq subdomains\n";  #debug
  if ($concurrent) {
    $called = "concurrent inputs";
  } else {
    $called = "states";
  }
  if ($Voption) { &status::init($input_freq * $state_freq, "Sampling subdomains of $compExecut "
           ."$input_freq inputs x $state_freq $called"); }
}
#print "basename taken from $compExecut\n";  #debug
$base = &component::baseName($compExecut);
$statefile = $base.".state";
if (!$concurrent) { #state
  if ($Xoption) {
    $errfile = $base.".errlog";
    if (-e $errfile) {unlink $errfile;} #remove any existing error log
    $compObj = component->new(component => "perl XqtS $ccf_component",
    #$compObj = component->new(component => "./XqtS", comparg => $ccf_component,
      statefile => $statefile);
  } else {
    if ($compExecut eq "SystemCode") {
      $compObj = component->new(component => "perl $compExecut", statefile => $statefile);
    } else {
      $compObj = component->new(component => $compExecut);
    }
  }
  if ($ccfProcessed) { 
    $compObj -> run(($input_start + $input_end)/2);
    $InitialState = $compObj->getState;
  }
} else { #concurrent
  if ($Xoption) {
    $compExecut = "perl XqtC $ccf_component";
  }
  #?? no other initialization?
}

# Prepare input sampling

$count = 0;
@inputs = ();
@in_state = ();
@Counts = (); #array of $count values that end sequences
$Ct = 0; #index into $Counts
@outputs = ();
@runtimes = ();
@out_state = ();
$test_point_count = 0;

#die "$compExecut file to be executed";  #debug
if ($Roption) { #random
  srand(3.14159265); #we want the same sequence every time this runs
  # 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);
#print "base taken from $ccf_component\n";  #debug
    $base = &component::baseName($compExecut);
    $statefile = $base.".state";
    if ($Xoption) {
      $errfile = $base.".errlog";
      if (-e $errfile) {unlink $errfile;} #remove any existing error log
      $compObj = component->new(component => "perl XqtS $ccf_component", 
       statefile => $statefile, comparg => "");
    } else {
      if ($compExecut eq "SystemCode") {
        $compObj = component->new(component => "perl $compExecut", statefile => $statefile);
      } else {
        $compObj = component->new(component => $compExecut);
      }
    }
    #run component once on midpoint to get initial state
    $input = ($input_end + $input_start)/2;
    $compObj->run($input) || die "Can't run component";
    $theout = $compObj->getOutput;
    if ($theout =~ /LOOPING/) {
      die "Code is probably looping on state-initialization input $input";
    }
    chomp($InitialState = $compObj->getState);
#print "initial state was: $InitialState\n"; #debug
    # Loop through input
    $input = $input_sampler->next();
#debug
#print "Input sequence # $i\n";
    while ($input !~ /^\.$/) {
      $inputs[$count] = $input; # Record starting values
      chomp($in_state[$count] = $compObj->getState);
#print "$input $in_state[$count] \n"; #debug
    # Run component
      $compObj->run($input) || die "Cannot run component";
      $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
    # Process approximations
      if ($ccfProcessed) {
        unless ( &stepfunc::process("R",$input, $in_state[$count], $outputs[$count], $runtimes[$count], $out_state[$count]) ) {
          &status::showmesg("Input ($input) / initial state ($in_state[$count]) outside subdomain boundaries!");
        }
      }
      $count++;
      $input = $input_sampler->next();
    } # Next input

    $Counts[$Ct++] = $count-1; #record the end-of-seq subscript
#print "seq ctr: $count \n";  #debug

    if ($Voption) { &status::progress($i); }

  } # Next input sequence
} else { #systematic sampling
  # Loop through input subdomains
  $isubdex = 0;
  foreach $isub (@input_subs) {

    $input_start = (split(' ',$isub))[0];
    $input_end = (split(' ',$isub))[1];
    $input_freq = (split(' ',$isub))[2];

    # Loop through input

    $input_sampler = sampling->init($input_start, $input_end, $input_freq);

    $input = $input_sampler->next();
    while ($input !~ /^\.$/) {
    # Loop through state subdomains
      $ssubdex = 0;
      foreach $ssub (@state_subs) { 
     
        $state_start = (split(' ',$ssub))[0];
        $state_end = (split(' ',$ssub))[1];
        $state_freq = (split(' ',$ssub))[2];

        $state_sampler = sampling->init($state_start, $state_end, $state_freq);

        # Loop through state
    
        $state = $state_sampler->next();
        while ($state !~ /^\.$/) {
  
          # Record initial values
          $inputs[$count] = $input;
          $in_state[$count] = $state;
  
          # Run component
          if ($concurrent) { #"states" are 2nd input from parallel component
            ($out2,$run1) = component::start($input,$compExecut,0);
            $outputs2[$count] = $out2;
            $runtimes[$count] = $run1;
            ($outputs[$count],$run3,$run4) = component::finish($state,0);
            $runtimes3[$count] = $run3;
            $runtimes4[$count] = $run4;
          } else { #state
            $compObj->run($input, $state) || die "Cannot run component";
            $outputs[$count] = $compObj->getOutput;
            $runtimes[$count] = $compObj->getRuntime;
     
            # Get persistent state
            $out_state[$count] = $compObj->getState;
          }
  
          # Process approximations
          if ($ccfProcessed) {
            if ($concurrent) { # different set of params
              $processOK = &stepfunc::process("C",$isubdex,$ssubdex,$input, $in_state[$count], $outputs[$count], $outputs2[$count], $runtimes[$count], $runtimes3[$count], $runtimes4[$count]);
            } else { #systematic state
              $processOK = &stepfunc::process("S",$isubdex,$ssubdex,$input, $in_state[$count], $outputs[$count], $runtimes[$count], $out_state[$count]);
            }
            unless ($processOK) {
              die "Lost subdomain index ($isubdex,$ssubdex) between test and record!";
            }
          }
          $state = $state_sampler->next();
          $count++;
        } # Next state
        $ssubdex++;
      } # Next state subdomain
      $input = $input_sampler->next();
      if ($Voption) { &status::progress($count+1); }
    } # Next input
    $isubdex++;
  } # Next input subdomain
} #end two kinds sampling
if ($Voption) { #get past no-linefeed lines
  print "\n";
}

unless ($concurrent) {
  $compObj->NoState; #kill FIFO file now that it's done
}

# Create graphing files

open(OUTPUT, ">output.dat");
open(RUNTIME, ">runtime.dat");
if ($concurrent) {
  open(OUT2, ">out2.dat");
  open(RUN3, ">run3.dat");
  open(RUN4, ">run4.dat");
} else {
  open(STATE, ">state.dat");
  open(OUTALL, ">output.datt");
  open(RUNALL, ">runtime.datt");
  open(STATEALL, ">state.datt");
}
PL: for ($i = 0; $i < $count; $i++) { 
 #unless (defined($in_state[$i])) { #skip the unitialized initial case $i==0
   #next PL;
   #}
  $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";
   if ($concurrent) { 
     print OUT2 "$inputs[$i] $in_state[$i] $outputs2[$i]\n";
     print RUN3 "$inputs[$i] $in_state[$i] $runtimes3[$i]\n";
     print RUN4 "$inputs[$i] $in_state[$i] $runtimes4[$i]\n";
   } else {
     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);
if ($concurrent) {
  close(OUT2);
  close(RUN3);
  close(RUN4);
} else {
  close(STATE);
  close(OUTALL);
  close(RUNALL);
  close(STATEALL);
}

if ($ccfProcessed) {
  $ccf_back = $ccf_component."b";
  copy($ccf_component, $ccf_back);
  #system "rm -f $ccf_component"."b";
  #system "cp $ccf_component $ccf_component"."b";  #create back copy even if no processing this time
  #&stepfunc::create_splot_files("out_step.dat", "run_step.dat", "state_step.dat"); 
#replace with:
# the first parameter sets the averaging for all, so must be right order
# SNAFU!!
  if ($concurrent) {
    stepfunc::create_splot(1,"out2");
    stepfunc::create_splot(1,"run3");
    stepfunc::create_splot(1,"run4");
  } else {
    stepfunc::create_splot(0,"state");
  }
  stepfunc::create_splot(0,"out");
  stepfunc::create_splot(0,"run");
}

#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`;
}

  # default options for graphing -- Add options to the
  # beginning of hash, they go to end of @args.
  %opts = ();
  if ($ccfProcessed) {
        $opts{'Show step approximations? [0-1]|[0-1]'} = 1;
        $opts{'Show subdomain boundaries? [0-1]|[0-1]'} = 0;
  }

  if ($Voption) { #skip past bar graph
    print "\n";
  }
  if ($ccfProcessed && !$Xoption) {
    if ($Roption) {
      $sampType = "R";
    } else {
      $sampType = "S";
    }
    $thtype = "state";
    if ($concurrent) {
      $thtype = "concurrent";
      $InitialState = 0; #must define
    }
    &stepfunc::create_ccft_files($ccf_component, "$thtype C $sampType $InitialState");} 
    #create .ccft header with proper options and .ccfc duplicate
    #$sampType ought to be recorded in ccfc format TBD

  if ($Roption) {
    print "Sampled $seq_count sequences, $test_point_count points.\n";
  } else {
    $prod = $input_freq*$state_freq;
    $n_subs = scalar(@input_subs)*scalar(@state_subs);
    $total = $prod*$n_subs;
    print "Sampled each of $n_subs subdomains $prod times, $total points.\n";
  }
INTACT:
if ($Ioption) { #show interactive menu
  $plot = "";
  $step = "";
MENU:
  print "\nInteractive mode menu\n";
  print "---------------------\n";
  print "1. Configure graphing options\n";
  if ($concurrent) {
    print "2. Show graph of finaloutput(input1,input2)\n";
    print "3. Show graph of runtime1(input1,input2)\n";
    print "4. Show graph of paralleloutput(input1,input2)\n";
  } else {
    print "2. Show graph of input x state vs. output\n";
    print "3. Show graph of input x state vs. runtime\n";
    print "4. Show graph of input x state vs. final state\n";
  }
  if ($concurrent) {
  print "5. Show graph of runtime3(input1,input2)\n";
  print "6. Show graph of runtime4(input1,input2)\n";
  }
  print "7. Exit\n\n";
  print "Enter number:  "; chop($i = <STDIN>);
  
  if ($i == 1) {
    %opts = &configopts(%opts);
    goto MENU;
  } elsif ($i == 2) {
    $plot = "output.dat";
    $step = "out_step.dat";
    $label = "Output";
  } elsif ($i == 3) {
    $plot = "runtime.dat";
    $step = "run_step.dat";
    $label = "Runtime";
  } elsif ($i == 4) {
    if ($concurrent) {
      $plot = "out2.dat";
      $step = "out2_step.dat";
      $label = "Parallelinput";
    } else {
      $plot = "state.dat";
      $step = "state_step.dat";
      $label = "Finalstate";
    }
  } elsif ($i == 5) {
    $plot = "run3.dat";
    $step = "run3_step.dat";
    $label = "Parallelruntime";
  } elsif ($i == 6) {
    $plot = "run4.dat";
    $step = "run4_step.dat";
    $label = "Lastruntime";
  } elsif ($i == 7) {
    exit;
  } else {print "Bad choice!\n"; goto MENU;}

  @args = values %opts;
  if ($i>=2) { #mainline graph
    if ($ccfProcessed) {#error table to STDOUT
      &stepfunc::ErrorTable($label);
    }
    else {
      $step = "";
    }
  }
  &plot($plot, $step, "", $label, "", @args);

  goto MENU;
} 

# This subroutine takes a hash that describes the options
# and builds a menu based on that hash. When the user is finished
# editing the options through the menu, the subroutine returns the
# hash with the modified values.
######################################
# Argument : a hash of options in this format...
#  Key: display name|regular expression
#    Where display name is the name to display for the option and the regular expression is the
#    test for valid settings for this option.
#  Value: the option's setting
sub configopts {
  return unless (defined($_[0]));
  my (%mopts, $count, $key, $disp, $regex, @tmp, $in);
  %mopts = @_;
OMNU:
  print "\n\nGraphing options\n";
  print "----------------\n";
  $count = 1;
  foreach $key (keys %mopts) {
   ($disp, $regex) = split(/\|/, $key);
   print "$count. $disp  ".$mopts{$key}."\n";
   $tmp[$count] = $key;
   $count++;
  }
  print "\nEnter number to change, or press return to go back: "; chop($in = <STDIN>);
  if (($in =~ /^$/) || ($in < 1) || ($in > $count)) { return %mopts; }
  $count = $in;
  print "Enter new value: "; chop($in = <STDIN>);
  ($disp, $regex) = split(/\|/, $tmp[$count]);
  if ($in !~ /$regex/) { print "Invalid setting\n"; goto OMNU; }
  $mopts{$tmp[$count]} = $in;
  goto OMNU;
}
exit;


# $_[0] = file name of data
# $_[1] = file name of comparison plot, "" if none
# $_[2] = file name of ps file; to terminal if "" -- always "" just now
# $_[3] = z axis label 
# $_[4] = type of plot (lines, points, etc.) -- default lines
# $_[5] = show subdomain boundaries?
sub plot {
 my ($data, $stepfile, $psfile, $label, $howplot, $showsubs) = @_;
 my @subd;
 die "Data file $data does not exist" unless (-e $data);
 unless ($howplot) {
   $howplot = "with lines lt 3";
 }  
 open(GNUPLOT, ">temp");
    print GNUPLOT "set xlabel \"Input\"\n";
    if ($concurrent) { #concurrent
      print GNUPLOT "set ylabel \"Concurrent Input\"\n";
    } else {
      if ($Ooption) {
        print GNUPLOT "set ylabel \"Composite State\"\n";
      } else {
        print GNUPLOT "set ylabel \"State\"\n";
      }
    }
    print GNUPLOT "set zlabel \"$label\"\n";
  if ($showsubs) {
    foreach $subdomain (@input_subs) {
     @subd = split(' ',$subdomain);
     print GNUPLOT "set arrow from first $subd[0],graph 0,graph 0 to first $subd[0],graph 1,graph 0 lt 6 lw 2 nohead\n";
     print GNUPLOT "set arrow from first $subd[1],graph 0,graph 0 to first $subd[1],graph 1,graph 0 lt 6 lw 2 nohead\n";
    }
    foreach $subdomain (@state_subs) {
     @subd = split(' ',$subdomain);
     print GNUPLOT "set arrow from graph 0,first $subd[0],graph 0 to graph 1,first $subd[0],graph 0 lt 3 lw 2 nohead\n";
     print GNUPLOT "set arrow from graph 0,first $subd[1],graph 0 to graph 1,first $subd[1],graph 0 lt 3 lw 2 nohead\n";
    }
  }
  #print GNUPLOT "set hidden3d\n";
  print GNUPLOT "splot \"$data\"";
  if ($stepfile) {
    print GNUPLOT ", \"$stepfile\" $howplot \n";
  } else {
    print GNUPLOT "\n";
  }
 close(GNUPLOT);
 util::GNUplotit("temp");
}
