/* sim.c

   written by Don Maszle
   30 September 1991
   
   Copyright (c) 1993.  Don Maszle, Frederic Bois.  All rights reserved.

   -- Revisions -----
     Logfile:  SCCS/s.sim.c
    Revision:  1.2
        Date:  8/11/93
     Modtime:  17:05:26
      Author:  @a
   -- SCCS  ---------

   Entry point and main simulation routines for 'sim' program.
*/

#ifdef __LOCAL_HDR__
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "ctype.h"
#include "math.h"
#include "malloc.h"
#include "assert.h"

#else
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include <malloc.h> /* NON_ANSI */
#include <assert.h>
#endif

#include "sim.h"
#include "_string.h"
#include "getopt.h"


static char vszVersion[] = "v3.53b";	/* Version of program */

static PSTR vpszDifSubErrors[] = {
  "Internal Error",
  "Did not meet required convergence",
  "Order of derivative too large",
  "No convergence for the hMin specified",
  "Specified error too small"
};  /* vpszDifSubErrors[] = */


/* SHOULD GO INTO DIFSUB.H */
/*. #define cleanup(rgDeriv, pmem) ; */

/* cleanup
   
   Dispose the dynamic variables used by difsub and interpole.
*/


#define cleanup(deriv, pmem)  _cleanup((deriv), (pmem))
/*.  */
/*. #define cleanup(deriv, pmem) ; */

static void _cleanup (double **deriv, memRec *mem)
{
  int i;

  for (i = 0; i <= 6; i++) {
    if (deriv[i])
      free(deriv[i]);
    if (mem->olds.derivold[i])
      free(mem->olds.derivold[i]);
    deriv[i] = mem->olds.derivold[i] = NULL;
  }

  for (i = 0; i < dim1Max; i++) {
    if (mem->utils.psave[i])
      free(mem->utils.psave[i]);
    mem->utils.psave[i] = NULL;
  } /* for */

}  /* _cleanup */


/* CorrectModelToTime

   resets the model variables to their values at pdTrans.  This should
   be used when the integrator passes a discontinuity.

   The states are interpolated back to the transition time and then
   the CalcDeriv() is called to calculate the derivatives at this
   point and the values of outputs dependent on the states.

   WARNING:
     This routine must be called before CorrectInputsToTransition() so
     that the derivative calculation is done with the input values
     before the transition.
*/

void CorrectModelToTime(PEXPERIMENT pexp, PDOUBLE pdTtrans)
{
  matType1 rgdInterpStates; /*, rgdInterpDeriv;*/
  PMODELINFO pmod = pexp->pmodelinfo;

  interpole (pmod->rgDeriv, &pexp->is.dStep, &pmod->nStates, &pmod->mem,
      &pexp->dTime, pdTtrans, pmod->pdModelVars, rgdInterpStates);

  memcpy (pmod->pdModelVars, rgdInterpStates,    /*-- Reset the states to */
	  sizeof (double) * (int) pmod->nStates);/*   interpolated values */

#ifdef ndef
                                /*** DOESN'T DO ANYTHING!
                                     -- Outputs are not saved here, the
                                        interp'd vector is thrown away.
                                        Outputs are recalced in SaveOutputs
                                        anyway.
                                 */
  if (pexp->dTime > 0.0)			/* Use interpolated values */
    CalcDeriv(rgdInterpStates, rgdInterpDeriv, pdTtrans);
/*.     CalcDeriv(pmod->pdModelVars, pmod->rgDeriv[0], pdTtrans); */

#endif
}  /* CorrectModelToTime */



/* CorrectInputToTransition
   
   corrects the integrator and inputs when an input transition occurs.
   
   returns the simulation time pexp->dTime and input values to
   the input discontinuity, or transition point *pdTtrans.

   The inputs are updated to reflect their state just after the
   transition.  The integrator is initialized for a new segment.

   This does NOT affect state and output definitions.  Use
   CorrectModelToTime() to do this:

   WARNING:
     If CorrectModelToTime() is called after this routine, the
     derivative calculation will be wrong, as it will use the input
     values for the next input period.
*/

void CorrectInputToTransition (PEXPERIMENT pexp, PDOUBLE pdTtrans)
{
  pexp->dTime = *pdTtrans;
  UpdateInputs (&pexp->dTime, pdTtrans);
  pexp->is.dStep = pexp->is.dStepMin * 1.0001;
  pexp->iStepFlag = DSSTEP_INIT;
  
  if (pexp->pmodelinfo->bDirty) {	/* Cleanup the memory workspace */
    
    cleanup(pexp->pmodelinfo->rgDeriv, &pexp->pmodelinfo->mem);
    pexp->pmodelinfo->bDirty = FALSE;
  }  /* if */

}  /* CorrectInputToTransition */


/* FreeVarMod
   
   Callback for FreeList().
   
   Frees the memory for one parameter modification.  If the parameter
   is an input, the memory allocated for the input function is also
   free'd.  Note that FreeList() will pass the data as a PVOID which
   needs to be re-cast.
*/

void FreeVarMod (PVOID pData)
{
  PVARMOD pvarmod = (PVARMOD) pData;
  
  if (IsInput (pvarmod->hvar)) {
    if (pvarmod->uvar.pifn)
      free (pvarmod->uvar.pifn);
  }  /* if */

  free (pvarmod);
}  /* FreeVarMod */




/* ModifyOneParm
   
   Callback function for ModifyParms.
*/

int ModifyOneParm (PVOID pData, PVOID pNullInfo)
{
  PVARMOD pvarmod = (PVARMOD) pData;
  
  if (IsInput(pvarmod->hvar))
    SetInput (pvarmod->hvar, pvarmod->uvar.pifn);
  else
    SetVar (pvarmod->hvar, pvarmod->uvar.dVal);

  return 0;
}  /* ModifyOneParm */


/* ModifyParms
   
   Modifies the parameters in the plistParmMods LIST of the experiment
   spec by call ForAllList to increment through the list.
*/

void ModifyParms (PLIST plistParmMods)
{
  assert (plistParmMods);
  ForAllList (plistParmMods, &ModifyOneParm, NULL);  
}  /* ModifyParms */


/* DoOneExperiment
   
   Runs one experiment
*/
   
#define InterpToInputAndReset(pexp, pdTtrans) \
  {CorrectModelToTime((pexp), (pdTtrans));         \
   CorrectInputToTransition ((pexp), (pdTtrans));}

  
int DoOneExperiment (PEXPERIMENT pexp)
{
  PMODELINFO pmod;	    	/* Pointer to the current model info */

  int    iOut;			/* Index to next output time */
  double dTout;			/* Next output time */
  double dTtrans;		/* Next exposure transition time */

  BOOL bInputFirst =FALSE;	/* Input transition is before output time */
  int iReturn = 0;

  if (!pexp)
    return 1;
  
  pmod = pexp->pmodelinfo;
  pmod->bDirty = FALSE;				/* Memory starts out clean */
  pexp->iStepFlag = DSSTEP_INIT;                /* Ptrs need to be init'd  */

  if (!InitOutputs (pexp, &iOut, &dTout))
    return 1;

  UpdateInputs (&pexp->dT0, &dTtrans);		/* Resolve dependent inputs */
  bInputFirst = (dTtrans <= dTout);		/* Input transition first */

  pexp->dTime = pexp->dT0;
  while (pexp->dTime < pexp->dTfinal) {		/* Iterate to final time */

    while (pexp->dTime < dTout			/* Pause at output times */
	   && pexp->dTime < dTtrans) {		/* ..and input transitions */

      DifSub(pexp);				/* Integrate one step */
      pmod->bDirty = TRUE;
      if (pexp->iDSFlag > 0)			/* OK -- next iteration */
	continue;
					/* No convergence -- try to fix */
      pexp->is.dStep = pexp->is.dStepMin * 1.0001;
      cleanup(pmod->rgDeriv, &pmod->mem);
      pexp->iStepFlag = DSSTEP_INIT;

      DifSub(pexp);		/* Integrate one step */
      if (pexp->iDSFlag < 1) {
	ReportError (NULL, RE_INTEGERR, (PSTR) &pexp->iDSFlag,
	             vpszDifSubErrors[-pexp->iDSFlag]);
        iReturn = -1;
	goto Exit_DoOneExperiment;
      }  /* if */
    }  /* while */

#ifdef ndef
    NOT THIS ONE!!
    if (dTtrans >= dTout           /* No output if input discontinuity 1st */
        && pexp->dTime >= dTout) { /**** Output Values *****/

      SaveOutputs (pexp, &dTout, &dTtrans);
      NextOutputTime (pexp, &dTout, &iOut);
    }  /* if */

				    /**** Input Transition *****/
    if (pexp->dTime < dTtrans)
      continue;				      /* No transition -- continue */

    CorrectModelToTime(pexp, &dTtrans);       /* Backup model values */
    CorrectInputToTransition (pexp, &dTtrans);/* Backup input and */
                                              /* reset schedule */
#else
    if (dTout < dTtrans) {      /*-- Outputs in current input period  */
                                /*   Save values from this input period */
      NewSaveOutputs (pexp, &dTout, &dTtrans);
      NextOutputTime (pexp, &dTout, &iOut);

      if (pexp->dTime >= dTtrans) /*  Handle the input discontinuity */
        InterpToInputAndReset(pexp, &dTtrans);
    }  /* if */

    else if (dTout > dTtrans) { /*-- Outputs in next input period */
                                /*   Only fix input for now */
      InterpToInputAndReset(pexp, &dTtrans);
    }  /* if */

    else  {                     /*-- Output requested at discontinutity */
                                /*   Output values reflect new period */
      InterpToInputAndReset(pexp, &dTtrans);
      NewSaveOutputs (pexp, &dTout, &dTtrans);
      NextOutputTime (pexp, &dTout, &iOut);
    }  /* else */
    
#endif

  }  /* while dTime < final time */

  if (pmod->bDirty) 
    cleanup(pmod->rgDeriv, &pmod->mem);

Exit_DoOneExperiment:
  ;
    _cleanup(pmod->rgDeriv, &pmod->mem);
  return iReturn;
}  /* DoOneExperiment */


/* DoOneNormalExp
   
   Does one AT_DEFAULTSIM simulation.
*/

void DoOneNormalExp (PANALYSIS panal, PEXPERIMENT pexp)
{
  fprintf (stderr, " %d", pexp->iExp);	/* Show what experiment it is */

  InitModel ();
  ModifyParms (panal->expGlobal.plistParmMods);	/* Global modifications */
  ModifyParms (pexp->plistParmMods);		/* Mods for this experiment */
  DoOneExperiment (pexp);

  fprintf (stderr, "\n");
}  /* DoOneNormalExp */



/* DoOneMCExp
   
   Does one AT_MONTECARLO simulation.
   
   Can maybe merge this with DoOneNormalExp() in the future.

   The major issue is the order of setting parameters.  For each
   experiment in a Monte Carlo run of an analysis, the order must be
   as follows: 

   Each Run
    calc mc mods

     Each Experiment
     1)  Init the model
     2)  Global parm mods
     3)  Monte Carlo mods
     4)  Local mods override everything
     
   The problem becomes that for the simulation to be started over
   again, the inputs have to be told to initialize and parm mods for
   the current experiment must be made (body weight, etc).  This
   currently won't happen unless the model is init'd.  Maybe create a
   ResetInputs() with starting time which will do the funky stuff
   done by the global variable right now.
*/

void DoOneMCExp (PANALYSIS panal, PEXPERIMENT pexp)
{
  register MONTECARLO *pmc = &panal->mc;
  
				  /* Show what experiment and run it is */
/*.   fprintf (stderr, "[%d] %d", pmc->iRun + 1, pexp->iExp); */

  InitModel ();
  ModifyParms (panal->expGlobal.plistParmMods);	/* Global modifications */
  SetParms (pmc->nParms, pmc->rghvar, pmc->rgdParms);  /* MC mods */
  ModifyParms (pexp->plistParmMods);		/* Mods for this experiment */
  DoOneExperiment (pexp);

/*.   fprintf (stderr, "\n"); */
}  /* DoOneMCExp */




/* DoNormal
   
   Does a normal analysis
*/

void DoNormal (PANALYSIS panal)
{
  int nExps = panal->expGlobal.iExp;
  int i;

  fprintf (stderr, "\nDoing analysis - %d normal experiment%c\n", nExps,
	   (nExps > 1 ? 's' : ' '));

  for (i = 0; i < nExps; i++) {
    DoOneNormalExp (panal, panal->rgpExps[i]);
    WriteNormalOutput (panal, panal->rgpExps[i]);
  }  /* for */

}  /* DoNormal */


/* DoMonteCarlo
   
   Does a Monte Carlo analysis or a Set Points analysis.  The latter is
   handled here because the looping is basically the same, with one
   difference.
   
   If the number of runs (nRuns) for SetPoints() analysis is
   specified to be zero, set points are read from the set points file
   until end of file is reached.  Otherwise, the number of runs
   explicity stated are read.  Not having enough points in the file
   in this latter case yields an error.
   
   If nRuns == 0, the test at the end of the while{} loop is not
   used, and the decision to continue is made by the return value of
   GetMCMods().  Since for MonteCarlo analyses, this return value is
   always TRUE (i.e. you can always pick another random number),
   nRuns is locally modified to 1, if it has been spec'd to zero,
   thus preventing the the Monte Carlo's from accidentaly running
   forever.
*/

void DoMonteCarlo (PANALYSIS panal)
{
  int nExps = panal->expGlobal.iExp;
  int nRuns = panal->mc.nRuns;
  MCDATAOUT mcdataout;
  BOOL bNotDone;			/* Not finished with analysis */
  int i;

  InitRandom (AnalysisGetSeed (panal), TRUE);
  if (panal->iType == AT_MONTECARLO && nRuns <= 0)
    nRuns = 1;				/* Don't let MonteCarlo run forever */
  
  fprintf (stderr,
	   "\nDoing analysis - %d %s run%c.. %d experiment%c%s\n",
	   nRuns,
	   (panal->iType == AT_MONTECARLO ? "Monte Carlo" : "Set point"),
	   (nRuns != 1 ? 's' : ' '),
	   nExps, (nExps > 1 ? 's' : ' '),
	   (nRuns != 1 ? " each" : " "));
  
  if (!nRuns)
    fprintf (stderr,
	     "0 runs specified for SetPoint().  Reading entire file.\n\n");


  panal->mc.iRun = 0;	/* First run */
  bNotDone = TRUE;
  while (bNotDone) {		

    bNotDone = GetMCMods (panal, NULL);	/*-- Mods for this run */

    if (bNotDone) {			/*-- Do analysis if not finished */
      for (i = 0; i < nExps; i++)	/*-- Do all experiments */
	DoOneMCExp (panal, panal->rgpExps[i]);
        TransformData (panal, &mcdataout); /* Transform and output run */
         WriteMCOutput (panal, &mcdataout);
    }  /* if */

    panal->mc.iRun++;	/* Next run */
    if (nRuns)				/* If a number of runs spec'd... */
      bNotDone = (panal->mc.iRun < nRuns);    
  }  /* while */

  WriteMCSummary (panal);
  CloseMCFiles (panal);
  
}  /* DoMonteCarlo */



/* DoAnalysis
   
   Does the analysis in the given specification.
*/

void DoAnalysis (PANALYSIS panal)
{

#ifdef ndef
  /* I put this into the EndExperiment() function in simi.c.  It */
  /* should have been taken care of by the time we get to here.  */
  
  for (i = 0; i < panal->expGlobal.iExp; i++)	/* Create space for output */
    PrepareOutSpec (panal->rgpExps[i]);
#endif
  
  switch (panal->iType) {

    default:
    case AT_DEFAULTSIM:
      DoNormal (panal);
      break;
      
    case AT_SETPOINTS:		/* Not really Monte Carlo */
    case AT_MONTECARLO:
      DoMonteCarlo (panal);
      break;

    case AT_GIBBS:
      DoGibbsEstimation (panal);
      break;
  }  /* switch */

  if (panal->expGlobal.os.pfileOut) {
    fclose (panal->expGlobal.os.pfileOut);  
    fprintf (stderr, "Wrote output file \"%s\"\n",
	     panal->expGlobal.os.szOutfilename);  
  }  /* if */
  
}  /* DoAnalysis */


/* MCVarListToArray

   converts a list of MCVAR to an array.  This must be a callback for
   ForAllList() since we are making the change here that will let us
   not to be forced to use list traversal in the future.
*/

MCVAR **vrgpMCVar;			/* Avoid hairy pointers in here */
int   viMCVar;				/* Index to the array */

int MCVarListToArray (PVOID pv_pMCVar, PVOID pv_Null)
{
  vrgpMCVar[viMCVar] = (MCVAR *) pv_pMCVar; /* Copy the pointer and.. */
  viMCVar++;				/* Advance to next element of array */
  return 1;
}  /* MCVarListToArray */


/* PrepAnalysis

   makes the ANALYSIS structure easier to work with in the simulation
   code.  Specifically,

   o  changes lists to arrays
*/

void PrepAnalysis (PANALYSIS panal)
{
  register MONTECARLO *pmc = &panal->mc;
  register int l;

  pmc->nParms = ListLength (pmc->plistMCVars);
  pmc->rgdParms = NewVector (double, pmc->nParms);
  pmc->rgpMCVar = NewVector (MCVAR *, pmc->nParms);

  vrgpMCVar = &pmc->rgpMCVar[0];	/* Address of the first pointer */
  viMCVar = 0;				/* Initialize global array index */
  ForAllList (pmc->plistMCVars, MCVarListToArray, (PVOID) NULL);
  FreeList (&pmc->plistMCVars, NULL, FALSE);
                                        /*-- Make a handle vector for theta */
  pmc->rghvar = NewVector (HVAR, pmc->nParms);
  for (l = 0; l < pmc->nParms; l++)
    pmc->rghvar[l] = pmc->rgpMCVar[l]->hvar;

}  /* PrepAnalysis */

/*-- Get the command line argument stuff --------------------*/

/* SansPath

   returns a pointer to just the filename of a full path.
*/

#ifdef __DOS__
#define CH_SEPARATOR '\\'
#else
#define CH_SEPARATOR '/'
#endif

char *SansPath (char *szFullPathname)
{
  register char *szFile;

  if (szFile = szFullPathname)
    while (*szFullPathname) {
      if (*szFullPathname == CH_SEPARATOR)
	szFile = szFullPathname+1;
      szFullPathname++;
    }  /* while */

  return szFile;
}  /* SansPath */


/* PromptFilenames
   
   prompts for both input and output file names.  The space allocated
   for inputting the files is reallocated to their actual size.
*/

void PromptFilenames (PSTR *pszFileIn, PSTR *pszFileOut)
{
  *pszFileIn = (PSTR) calloc (1, 80);
  *pszFileOut = (PSTR) calloc (1, 80);

  fprintf (stderr, "Input filename? ");
  gets (*pszFileIn);
  *pszFileIn = strtok (*pszFileIn, " \t");

  if (!(*pszFileIn))            /*-- Nothing entered, quit */
    return;

  if ((*pszFileIn)[0]) {	/*-- Input file specified */
    fprintf (stderr, "Output filename? ");
    gets (*pszFileOut);
    *pszFileOut = strtok (*pszFileOut, " \t");
  }  /* if */

  if (!(*pszFileOut) || !(*pszFileOut)[0]) { /* If no output specified */
    free (*pszFileOut);		             /* .. use default later */
    *pszFileOut = NULL;
  }  /* if */
  else {
    *pszFileIn = (PSTR) realloc (*pszFileIn, _strlen(*pszFileIn) + 1);
    *pszFileOut = (PSTR) realloc (*pszFileOut, _strlen(*pszFileOut) + 1);
  }  /* else */

}  /* PromptFilenames */


/*-- Command Line Parsing --------------------*/

#define WarnArgumentReqd(szOption, szArg) \
  fprintf (stderr,                 \
	   "* Command-line option \"%s\" requires an argument \"%s\"\n",\
	   szOption, szArg);

#define WarnUnknownArg(szOption, szArg) \
  fprintf (stderr,                 \
	   "* Unknown argument \"%s\" to option \"%s\"\n", szArg, szOption);


void GetOutputFlagOption (PANALYSIS panal, char *optarg)
{
  if (!strcmp (optarg, "MCOutputs"))
    panal->fCmdOptions |= OF_MCOUTPUTS;

  else if (!strcmp (optarg, "NoMCOutputs"))
    panal->fNotOptions |= OF_MCOUTPUTS;
  
  else if (!strcmp (optarg, "MCResult"))
    panal->fCmdOptions |= OF_MCRESULT;

  else if (!strcmp (optarg, "NoMCResult"))
    panal->fNotOptions |= OF_MCRESULT;
  
  else if (!strcmp (optarg, "ParmList"))
    panal->fCmdOptions |= OF_PARMLIST;

  else if (!strcmp (optarg, "NoParmList"))
    panal->fNotOptions |= OF_PARMLIST;
  
  else if (!strcmp (optarg, "VarNames"))
    panal->fCmdOptions |= OF_VARNAMES;

  else if (!strcmp (optarg, "NoVarNames"))
    panal->fNotOptions |= OF_VARNAMES;

  else
    WarnUnknownArg ("-O", optarg);
  
}  /* GetOutputFlagOption */  

/* GetFilenames
   
   retrieves the filenames from the command line arguments passed to
   the program.

   The command line syntax is:
   
     pksim [-options] [input-file [output-file | 4-file-names]]

   If the output filename is not given a poorly chosen default is
   used.  If neither the input, nor output filenames are given, the
   program prompts for them both.
   
   If the optional four extra filenames are included instead, they are
   interpreted as files for Monte Carlo output in this manner:
   
     pass-file fail-file behavior-file summary-file
   
   The options can appear anywhere in the line and in any order.
   Where conflicts occur, the option specified first SHOULD be used,
   but probably the last is used.  Also, inibitory options override
   requested options, e.g. -ONoParmList -OParmList does not print the
   parmlist.  This is considered acceptable because if you are
   braindead enough to put this on the command-line, you lose.
   
   The options are parsed with GNU _getopt(). After _getopt() is called,
   the args in rgszArg have been permuted so that non-option args are
   first, which in this case means the filenames.

   Uses the following GNU globals:
   
     char *optarg;    -- Contains the string argument to each option in turn
     int optind;      -- Index in ARGV of the next elem to be scanned
     char *nextchar;  -- The next char to be scanned in the option-element
     int opterr;      -- 0 value flags to inhibit GNU error messages
*/   
  
/*BOOL GetFilenames(int nArg, PSTR rgszArg[], PSTR *pszFileIn, PSTR *pszFileOut,
		   PSTR *pszFilePass, PSTR *pszFileFail, PSTR *pszFileBehav,
		   PSTR *pszFileSum)
*/

static char vszOptions[] = "h:H:O:D:n:S:";

BOOL GetCmdLineArgs (int cArg,
		     char *const *rgszArg,
		     PSTR *pszFileIn, PSTR *pszFileOut,
		     PANALYSIS panal)
{
  int c;
  int digit_optind = 0;

      /*-- Process command-line arguments ----------*/

  while (1) {
      int this_option_optind = optind ? optind : 1;

      c = _getopt (cArg, rgszArg, vszOptions);
      if (c == EOF)			/*-- Finish with option args */
	break;

      switch (c) {
	case '?':
	  optarg = 0;
				/*** Fall through!! */
	case 'H':
	case 'h':
	  if (optarg && *optarg)
	    ShowHelp (optarg);
	  else
	    ShowHelpMessage (SansPath (rgszArg[0]));
	  exit (-1);
	  break;

	case 'D':
	  printf (">> Debug mode: Using option '%s'\n", optarg);
	  /*-- Can setup to run with certain debug flags */
	  break;

	case 'n':
	  break;
	  
	case 'O':
	  if (!optarg) {
	    WarnArgumentReqd ("-O", "output format");
	    break;
	  }  /* if */
	  GetOutputFlagOption (panal, optarg);
	  break;

	case 'S':
	  if (optarg) {
	    AnalysisSetSeed (panal, atof (optarg));
	    panal->fCmdOptions |= OF_CMDLSEED;
	  }  /* if */
	  else
	    WarnArgumentReqd ("-S", "random seed");
	  break;
	  
	default:
	  printf ("?? Unknown option in command-line, %c=code 0%o ??\n", c, c);
	  break;

#ifdef ndef
	case '0':
	case '1':
	case '2':
	case '3':
	case '4':
	case '5':
	case '6':
	case '7':
	case '8':
	case '9':
	  if (digit_optind != 0 && digit_optind != this_option_optind)
	    printf ("digits occur in two different rgszArg-elements.\n");
	  digit_optind = this_option_optind;
	  printf ("option %c\n", c);
	  break;
#endif
	}
    }

#ifdef ndef  
  if (optind < cArg)
    {
      int i = optind;
      
      printf ("* Non-option rgszArg-elements: {");
      while (i < cArg)
	printf ("%s ", rgszArg[i++]);
      printf ("}\n");
    }
#endif
/*-----END OF GETOPT PROCESSING -----*/

/*  = *pszFilePass = *pszFileFail = *pszFileBehav = *pszFileSum = */

  *pszFileIn = *pszFileOut = (PSTR) NULL;

  switch (cArg - optind) {		/*-- Remaining args are  filenames */
    default:
      ShowHelp ("Usage");
#ifdef ndef      
      fprintf (stderr, "Usage:\t %s [options] [input-file [output-file]]\n",
	       SansPath (rgszArg[0]));
      fprintf (stderr,
	       "-or-\t %s [options] [input-file pass-file fail-file behav-file sum-file]\n",
	       SansPath (rgszArg[0]));
#endif
      exit (-1);
      break;

    case 5:				/* Input and 4 output files spec'd */
      *pszFileIn = rgszArg[optind];
      panal->mc.szMCPassFilename = rgszArg[optind + 1];
      panal->mc.szMCFailFilename = rgszArg[optind + 2];
      panal->mc.szMCBehavFilename = rgszArg[optind + 3];
      panal->mc.szMCSumFilename = rgszArg[optind + 4];
      break;
      
    case 2:			/*-- Output and input file specificed */
      *pszFileOut = rgszArg[optind + 1];

      /* Fall through! */
      
    case 1:			/*-- Input file specificed */
      *pszFileIn = rgszArg[optind];
      break;

    case 0:			/*-- No file names specified */
      PromptFilenames (pszFileIn, pszFileOut);
      break;
  }  /* switch */

  while (*pszFileIn && (*pszFileIn)[0]		/* Files specified   */
	 && !Strcmp(*pszFileIn, *pszFileOut)) {	/* and not different */
	   
    fprintf (stderr, "\n** Input and output filename must be different.\n");
    PromptFilenames (pszFileIn, pszFileOut);
  }  /* while */

  return (*pszFileIn && (*pszFileIn)[0]);  	/* Input name given */

}  /* GetCmdLineArgs */


void AnnounceProgram (void)
{
  printf ("\n________________________________________\n");
  printf ("\nMCSim simulation program: %s\n\n", vszVersion);
  printf ("All rights reserved.\n\n");

  printf ("* Using `%s' model in file \"%s\" created by %s\n\n",
	  szModelDescFilename, szModelSourceFilename, szModelGenAndVersion);

}  /* AnnounceProgram */


/* main
   
   Entry point for simulation and analysis program.
*/

int main (int nArg, char *const *rgszArg)
{
  PSTR szFileIn, szFileOut;
  INPUTBUF ibIn;
  PANALYSIS panal = (PANALYSIS) malloc (sizeof(ANALYSIS));

  AnnounceProgram ();

  if (!panal)
    ReportError (NULL, RE_OUTOFMEM | RE_FATAL,
		 "ANALYSIS specification too large", NULL);

  InitAnalysis (panal);
  if (!(GetCmdLineArgs (nArg, rgszArg, &szFileIn, &szFileOut, panal)))
    exit (-1);

      /*-- Define the output file as the global experiment default  */
  panal->expGlobal.os.szOutfilename = szFileOut;
  panal->expGlobal.os.bCommandLineSpec = (BOOL) szFileOut;  /* Flag */

  if (!InitBuffer (&ibIn, szFileIn))
    ReportError (&ibIn, RE_INIT | RE_FATAL, "ReadInput", NULL);
  
  ibIn.pInfo = (PVOID) panal;	/* Attach analysis specification to input */

/*. fprintf(stderr, "*  Memory available: %ld\n", _memavl()); */

  if (ReadAnalysis (&ibIn)) {
    SetOptions (panal);
    PrepAnalysis (panal);
    DoAnalysis (panal);
  }  /* if */

/*. fprintf(stderr, "*  Memory available: %ld\n", _memavl()); */

  fprintf (stderr, "Done.\n\n");

  return 0;
}  /* main */
