#ifndef _SIMULATION_HPP_
#define _SIMULATION_HPP_ 

#include <stdarg.h>
#include <unistd.h>
#include <math.h>

#include <cstdlib>
#include <cstdio>
#include <cstring>

#include <string>
#include <iostream>
#include <fstream>
#include <sstream>
#include <list>
#include <vector>
#include <stdexcept>
#include <setjmp.h>


#include "gnuplot_i.hpp"


// ############### 'M-x mouse-wheel-mode' ######################################

// ##############  Copy/Paste -> mark left cursor with mouse left button, mark right cursor with mouse right button, 
//                 Place curser with mouse left button, press middle wheel button to place text ####################

// #############   Cut/Paste -> mark left cursor with mouse left button, mark right cursor with mouse right button,
//                 hit mouse right button again to "cut" text,
//                 Place curser with mouse left button, press middle wheel button to place text ####################

// ############    To do general word searches with results listed in a seperate buffer (all text files in current directory):
//                 Tools->Search Files (Grep)   ... grep -n -e <word> *.*

// ###########    'C-_' to "undo". At any point type 'C-f' to break the sequence. After which, typing 'C-_' starts a "redo" sequence??

// ###########    NOTE: To increase the "yank" buffer length from it's default setting of 20 lines (horizontal length of screen)
//                      Changed "Yank Menu Length"(under "Enviromental/Mouse" group) from 20 to 200 (for now, changed only
//                      explicitly on each session. This can be done through "Options/Customize Emacs",... there seems to be
//                      no "M-x" type command).          
//          
//
// ###########    In debugger, use "display /i $pc" to display line information when using "stepi". Issue "undisplay" to
//                reset auto-display.
//
//
//
// ##########     According to the web (even with the latest downloaded gdb from cygwin, "Ctrl-C" directly from the debugger
//                into the application does NOT work. It seems like the only thing that does work is KILL.
//
//                So we go back to the method running the application in one window, and opening another window with "emacs/gdb"
//                and using the "attach <process id>" trick. This does work!!!
//
//                I have a hap-hazard way of opening two X-windows. "startx" from one console, then startx in a second console
//                and close the error message. Then hit "ctrl-C". For some reason, the second window will come up!!! (Not enought
//                time right now to figure out how to do this the right way).
//
//
//
//
//
        

typedef enum {
  ODE_SIMU_24 = 1,
  ODE_SIMU_34,
  ODE_SIMU_56,
}ODE_SIMU_TYPE;


#define TRUE  1
#define FALSE 0



class OdeObject;
class CtrlObject;
class SrcObject;
class SpiceObject;
class CoefObject;
class SwitchObject;



struct OdeObjItem 
{	
  OdeObject * pOdeObject;
  OdeObjItem * pPrevOdeItem;
  OdeObjItem * pNextOdeItem;
};

struct CtrlObjItem
{
  CtrlObject * pCtrlObject;
  CtrlObjItem * pPrevCtrlItem;
  CtrlObjItem * pNextCtrlItem;
};


struct SrcObjItem
{
  SrcObject * pSrcObject;
  SrcObjItem * pPrevSrcItem;
  SrcObjItem * pNextSrcItem;
};

struct CtrlObjGroup
{
  CtrlObjItem * pCtrlEquationList;
  int QuantumCount;
  int QuantumNum;
  int DoUpdate;
  vector<double> Plot_t;
  CtrlObjGroup * pPrevCtrlGroup;
  CtrlObjGroup * pNextCtrlGroup;
};

struct SpiceObjItem 
{	
  SpiceObject * pSpiceObject;
  SpiceObjItem * pPrevSpiceItem;
  SpiceObjItem * pNextSpiceItem;
};

//(NOTE: "SpiceObject"'s munipulate "CoefObjects"'s directly)

struct CoefObjItem
{
  CoefObject * pCoefObject;
  CoefObjItem * pPrevCoefItem;
  CoefObjItem * pNextCoefItem;


};


struct SwitchObjItem 
{	
  SwitchObject * pSwitchObject;
  SwitchObjItem * pPrevSwitchItem;
  SwitchObjItem * pNextSwitchItem;
};





extern void CheckPlotState(void);
extern bool CheckSimuState(double, double);
extern bool QuickCheckPlotState(void);

  //where we jump back to if "h" must be reset to a minimum
jmp_buf OdeResetEnv;




class CoefObject      //base class for specific Coefficient function (used by "SpiceObject"'s)
{
public:
  double y;
  //consturctor
  CoefObject(void);
  ~CoefObject(void);
  void Exception(string Message);
  //(no "t" because "CoefObject's" are evaluated as DC components)
  virtual void CoefFunction(double recp_h, bool DoTrapezoidal){};
  //(NOTE: This object performs NO <rvalue> updates on general 
  //       objects. This object is referenced directly by "SpiceObject"'s only).
  COEF_FUNCTION CoefFuncName;
  string LiteralName; 
  //(NOTE: "CoefObject's" have no probes. Only the probes provided by the "SpiceObject" they effect.)
  //(NOTE: "CoefObject's have no output plotting because they are referenced directly by "SpiceObject's".)
  //exception variables...
  string ExceptionMessage;
  bool GotException;

};

//Base constructor for class "CoefObject"
CoefObject::CoefObject(void)
{
  //!!!! any initializations go here !!!!!


}
//Base destructor for class "CoefObject"
CoefObject::~CoefObject(void)
{
 


}
void CoefObject::Exception(string Message)
{
  if(!GotException){
    ExceptionMessage = Message;
    GotException = TRUE;
  }
}





class SpiceObject    //base class for specific Spice function
{
public:
  SpiceObject(void);
  ~SpiceObject(void);
  void Exception(string Message);
  //Coefficients updated and Gaussian Elimination performed here...
  void PreSpiceFunction(double recp_h, bool DoTrapezoidal);
  void ForwardSubstitution(void);
  void ReverseElimination(void);
  void Gauss(bool DoTrapezoidal);
  void InitializeMatrix(unsigned int NumberOfRows);
  virtual void PostSpiceFunction(void){};
             //these functions uses  "pOdeObjRValList" and "pCtrlObjRValList"  below to 
              //update the <rvalues> of other classes of the same type.
  virtual void OdeRValueUpdate(void){};
  virtual void CtrlRValueUpdate(void){};
  virtual void SwitchRValueUpdate(void){};
  virtual void CoefRValueUpdate(void){};
  bool DoProbes;

  virtual void RecordProbes(void);
  virtual void PlotProbes(Gnuplot &, vector<double> &, string TagNamesToPlot[]);
  //We can be selective on which if not all internal probes should be ploted.
  bool * pDoProbes_;
  string * pProbeName;
  //"SpiceObject" has no SPICE_FUNCTION. This object references "CoefObject"'s directly to update it's matrix coefficients.
  //The class that inherits this class uses these lists to update "<rvalues>" with the elements of it's solution vector.
  SPICE_FUNCTION SpiceFuncName;
  string LiteralName; 
    //The class that inherits this class uses these lists to update "<rvalues>" with it's "y".
  OdeObjItem * pOdeObjRValList;
  CtrlObjItem * pCtrlObjRValList;
  SwitchObjItem * pSwitchObjRValList;
  CoefObjItem * pCoefObjRValList;
  //(NOTE: "SpiceObject's have no output to plot, only probes.)
  //A pointer to a pointer of doubles to hold the Matrix and equivilence vector.
  double ** a;
  	  //The saved solution vectors for the given pass.
  double * pBackwardEulerSolutionVector;
  double * pTrapezoidalSolutionVector;
  //A pointer to a pointer of (CoefObject *). Null, if a[][] is a simple static coefficient,
  //non-Null if "y" of CoefObject->CoefFunction() should be used to update a[][].
  CoefObject *** a_CoefObj;
  //static coefficents are stored here.
  double ** a_Static;
  //The "n" by "n" coefficient matrix.
  unsigned int n;
  //exception variables...
  string ExceptionMessage;
  bool GotException;
  //pointer to storage for probes...
  vector<double> * pSolutionProbes;
#ifdef DEBUG_SPICE_MATRIX
  void DumpLocalMatrixStates(void);
  //make sure array size is set properly
  double a_matrix[SPICE_DEBUG_MATRIX_SIZE][SPICE_DEBUG_MATRIX_SIZE];
  double a_equate_vector[SPICE_DEBUG_MATRIX_SIZE];
  double a_solution_vector[SPICE_DEBUG_MATRIX_SIZE];
  char a_matrix_valid_elements[SPICE_DEBUG_MATRIX_SIZE][SPICE_DEBUG_MATRIX_SIZE];
#ifdef DUMP_MATRIX_EQUATE_VECTOR_TO_FILE_FOR_EXTERNAL_ANALYSIS
  bool MatrixAndEquateVectorDumped;
  void DumpMatrixEquateVectorToFile(void);
#endif
#endif 


};



void SpiceObject::RecordProbes(void)
{
  int i;
  // NOTE: This function can be overloaded in the application to provide for more probes.
  for(i = 0; i < n + 1; i++){
    if(pDoProbes_[i])
      pSolutionProbes[i].push_back(a[i][n]);
  }

}

void SpiceObject::PlotProbes(Gnuplot & SimuPlot, vector<double> & Plot_t, string TagNamesToPlot[])
{

  // NOTE: This function can be overloaded in the application to provide for more probes.
  int i,j;
  if(TagNamesToPlot[0] == ""){
    for(i = 0; i < n + 1; i++){
      if(pDoProbes_[i])
	SimuPlot.plot_xy(Plot_t, pSolutionProbes[i], LiteralName + "_" + pProbeName[i]);
    }
  }
  else{
    for(i = 0; i < 20; i++){
      if(TagNamesToPlot[i] == ""){
	break;
      }
      else{
	for(j = 0; j < n + 1; j++){
	  if((TagNamesToPlot[i] == LiteralName + "_" + pProbeName[j]) && pDoProbes_[j])
	    SimuPlot.plot_xy(Plot_t, pSolutionProbes[j], LiteralName + "_" + pProbeName[j]);
	}
      }
    }
  }
}





//Base constructor for class "SpiceObject"
SpiceObject::SpiceObject(void)
{
  //!!!! any initializations go here !!!!!


}
//Base destructor for class "SpiceObject"
SpiceObject::~SpiceObject(void)
{
 


}

#ifdef DEBUG_SPICE_MATRIX
#ifdef DUMP_MATRIX_EQUATE_VECTOR_TO_FILE_FOR_EXTERNAL_ANALYSIS

void SpiceObject::DumpMatrixEquateVectorToFile(void)
{  
  //generate files for matrix and vector to be analized by octave.
  //Specificly, octave will determine a gaussian solution using the following commands:
  //   "a_matrix\a_equate_vector = a_solution_vector". By default, Octave displays the values
  //   in this matrix and vector dump in limited precision format. To increase the display resolution
  //   of Octave, type the command "format long".


  FILE * fp_matrix;
  FILE * fp_equate_vector;
  FILE * fp_gauss_app;
  int i,j;

  
  // files to be used with Octave
  fp_matrix  = fopen("a_matrix_dump", "w"); 
  if(fp_matrix == NULL)
    return;
  fp_equate_vector  = fopen("a_equate_vector_dump", "w"); 
  if(fp_equate_vector == NULL){
    fclose(fp_matrix);
    return;
  }
  // file to be used "TestGaussianElimination.cc" in directory "GuassianElimination-SpaveMatrix-Docs"
  fp_gauss_app  = fopen("gauss.in", "w"); 
  if(fp_equate_vector == NULL){
    fclose(fp_matrix);
    fclose(fp_equate_vector);
    return;
  }

  
  fprintf(fp_equate_vector, "a_equate_vector = ["); 
  fprintf(fp_matrix, "a_matrix = [");

  fprintf(fp_gauss_app, "%d\n\n", n);

  if (n <= SPICE_DEBUG_MATRIX_SIZE){
    for (i = 0; i < n; ++i){
      for (j = 0; j < n + 1; ++j){
	if(j == n){
	  fprintf(fp_equate_vector, "%g;\n",  a[i][j]);
	  fprintf(fp_gauss_app, "%g\n",  a[i][j]);
	}
	else{
	  fprintf(fp_gauss_app, "%g ",  a[i][j]);
	  if(j == n-1)
	    fprintf(fp_matrix, "%g;\n",  a[i][j]);
	  else
	    fprintf(fp_matrix, "%g ",  a[i][j]);
	}
	
      }
    }
  }

  fprintf(fp_equate_vector, "];"); 
  fprintf(fp_matrix, "];");


  fclose(fp_equate_vector);
  fclose(fp_matrix);
  fclose(fp_gauss_app);

}


#endif 
#endif

#ifdef DEBUG_SPICE_MATRIX

void SpiceObject::DumpLocalMatrixStates(void)
{
  int i,j;
  //Refresh "a_matrix" and "a_vector" every interation. This is used only
  //for debugging purposes.
  if (n <= SPICE_DEBUG_MATRIX_SIZE){
	for (i = 0; i < n; ++i){
	  for (j = 0; j < n + 1; ++j){
	if(j == n)
	  a_equate_vector[i] = a[i][j];
	else
	  a_matrix[i][j] = a[i][j];

		//(Use "Memory Browser" with columns set to "67" and cell to "byte")
	  if(a_Static[i][j])
	  {
		  a_matrix_valid_elements[i][j] = 'X';
	  }
	  else
	  {
		  a_matrix_valid_elements[i][j] = '|';

	  }

	  }
	}
  }
}


#endif


void SpiceObject::Exception(string Message)
{
  if(!GotException){
    ExceptionMessage = Message;
    GotException = TRUE;
  }
}



//  The gaussian elimination routines below was extracted from:  
//  ".\Simulation\MultiLevelConverter\GaussianElimination-SparseMatrix-Docs",
//  file "TestGaussianElimination.cc" and "guass_c.txt"

void SpiceObject::ForwardSubstitution(void) {
  int i, j, k, max;
  double tmp;
  for (i = 0; i < n; ++i) {
    max = i;

     //There is a mistake in code. See file ./Gaussian-Elimination-With-Partial-Pivoting/TestGaussianElimination.cc" 
     //for more information.
    for (j = i + 1; j < n; ++j)
      // if (a[j][i] > a[max][i])
      if (fabs(a[j][i]) > fabs(a[max][i]))
	max = j;
    
    for (j = 0; j < n + 1; ++j) {
      tmp = a[max][j];
      a[max][j] = a[i][j];
      a[i][j] = tmp;
    }
    

    for (j = n; j >= i; --j)
      for (k = i + 1; k < n; ++k)
	a[k][j] -= a[k][i]/a[i][i] * a[i][j];
    
#ifdef DEBUG_SPICE_MATRIX
    DumpLocalMatrixStates();

#endif


  }
}

void SpiceObject::ReverseElimination(void) {
  int i, j;
  for (i = n - 1; i >= 0; --i) {
    a[i][n] = a[i][n] / a[i][i];
    a[i][i] = 1;
    for (j = i - 1; j >= 0; --j) {
      a[j][n] -= a[j][i] * a[i][n];
      a[j][i] = 0;
    }
#ifdef DEBUG_SPICE_MATRIX
    DumpLocalMatrixStates();

#endif

  }
}

void SpiceObject::Gauss(bool DoTrapezoidal) {

#ifdef DEBUG_SPICE_MATRIX

  DumpLocalMatrixStates();

#ifdef DUMP_MATRIX_EQUATE_VECTOR_TO_FILE_FOR_EXTERNAL_ANALYSIS

  if(!MatrixAndEquateVectorDumped){
    DumpMatrixEquateVectorToFile();
    MatrixAndEquateVectorDumped = TRUE;
  }
#endif 

#endif 
 
  ForwardSubstitution();


#ifdef DEBUG_SPICE_MATRIX
  DumpLocalMatrixStates();

#endif 






  ReverseElimination();
  
  //The solution vector is contained in matrix locations "a[0][n]" to "a[n-1][n]"
  if(DoTrapezoidal == FALSE)
  {
	 for(int i = 0; i < n; i++)
		 pBackwardEulerSolutionVector[i] = a[i][n];

  }
  else
  {
	  for(int i = 0; i < n; i++)
		 pTrapezoidalSolutionVector[i] = a[i][n];
  }




#ifdef DEBUG_SPICE_MATRIX
   //Refresh "a_vector" at end of each interation. This is used only
  //for debugging purposes.
  if (n <= SPICE_DEBUG_MATRIX_SIZE){
    for (int i = 0; i < n; ++i){
      for (int j = 0; j < n + 1; ++j)
	a_solution_vector[i] = a[i][n];
    }
  }

#endif 

}



void SpiceObject::InitializeMatrix(unsigned int NumberOfRows){

  int i,j;

  n = NumberOfRows;
  // allocate the working matrix and right side column vector
  a = (double **)malloc(n*sizeof(double *));
  for(i = 0; i < n; i++){
    a[i] = (double *)malloc((n+1)*sizeof(double));
    for(j = 0; j < n + 1; j++){
      a[i][j] = 0.0;
    }
  }

  //allocate matrix that contains pointers to "CoefObject's"
  //that coincide with "a[][]"

  a_CoefObj = (CoefObject ***)malloc(n*sizeof(CoefObject **));
  for(i = 0; i < n; i++){
    a_CoefObj[i] = (CoefObject **)malloc((n+1)*sizeof(CoefObject *));
    for(j = 0; j < n + 1; j++){
      a_CoefObj[i][j] = NULL;
    }
  }


  //allocate matrix that contains "double" constant values
  //that coincide with "a[][]"
  a_Static = (double **)malloc(n*sizeof(double *));
  for(i = 0; i < n; i++){
    a_Static[i] = (double *)malloc((n+1)*sizeof(double));
    for(j = 0; j < n + 1; j++){
      a_Static[i][j] = 0.0;
    }
  } 

  //allocate storage for the solution vectors.
  pBackwardEulerSolutionVector = (double *)malloc(n*sizeof(double));
  pTrapezoidalSolutionVector = (double *)malloc(n*sizeof(double));

  if(DoProbes){
    //allocate array for storage of probes on the solution vector.
    pSolutionProbes = new std::vector<double>[n+1];
    //storage for flags to select which probe to record or plot.
    pDoProbes_ = (bool *)malloc((n+1)*sizeof(bool));
    //storage to hold probe names.
    pProbeName = new std::string[n+1];   
  }

    
					     

}

void SpiceObject::PreSpiceFunction(double recp_h, bool DoTrapezoidal){

  int i,j;

  for(i = 0; i < n; i++){
    for(j = 0; j < n + 1; j++){
      if(a_CoefObj[i][j]){
	//perform required calulation on "dynamic" coefficients.
	a_CoefObj[i][j]->CoefFunction(recp_h, DoTrapezoidal);
	//then update the working matrix with the result.
	a[i][j] = a_CoefObj[i][j]->y * a_Static[i][j];
      }
      else{
	//else, update the working matrix with the assigned "static" coefficient.
	a[i][j] = a_Static[i][j];
      }
      
    }
  }

}





class SwitchObject      //base class for specific Switch function (used by "CoefObject"'s)
{
public:
  double y_igbt;
  double y_diode;
  //consturctor
  SwitchObject(void);
  ~SwitchObject(void);
  void Exception(string Message);
  //("t" declared in "SimuObject" class)
  virtual void PreSwitchFunction(double t, double h, double h_start){};
  virtual void SwitchFunction(double t){};
              //this functions uses "pCoefObjRValList" and "pOdeObjRValueList"  below to 
              //update the <rvalues> of other classes of the same type.
  virtual void CoefRValueUpdate(void){};
  virtual void OdeRValueUpdate(void){};
             //Probes to provide extended plotting ability (defined below and can be overloaded in the application)
  bool DoProbes;
  virtual void RecordProbes(void);
  virtual void PlotProbes(Gnuplot &, vector<double> &, string TagNamesToPlot[]);
  //We can be selective on which if not all internal probes should be ploted.
  bool DoProbes_res;
  bool DoProbes_cur;
  bool DoProbes_gate;
  void CheckForSwitchTransition(double t, double h, double h_start);
  void ExecuteSwitchTransition(double t);
  //(NOTE: "SwitchObject's" have no probes. Only the probes provided by the "SpiceObject's" and "OdeObject's" they effect.)
  //Assigned by the class that inherits this class. Used at initialization to build the list
  //"pCoefObjRValList" below.
  SWITCH_FUNCTION SwitchFuncName;
  string LiteralName; 
  //The class that inherits this class uses this lists to update "<rvalues>" with it's "y".
  CoefObjItem * pCoefObjRValList;
  OdeObjItem * pOdeObjRValList;
  //(NOTE: "SwitchObject's have no direct output plotting (just probes). The effect of switches are view in the corresponding 
  //       "SpiceObject's" or "OdeObject's" as probes.)
  //exception variables...
  string ExceptionMessage;
  bool GotException;
  // ** "Switch" characteristics (Constructor sets defaults) **

  //The "RValue" assigned current at time "t".
  double DevCur;
  //If "IsIgbt" is TRUE, the "RValue" assigned gate signal state, (On = TRUE = 1, Off = FALSE = 0)
  bool  DevGate;
  //Diode state (On = TRUE = 1, Off = FALSE = 0)
  bool DiodeState;
  //If "IsIgbt"  is TRUE, the gate state (On = TRUE = 1, Off = FALSE = 0)
  bool GateState;
  //Igbt state in transition (TRUE = 1, FALSE = 0)
  bool GateTran; 
  //defines the polarity of the device relative to "DiodeThresh". (Example:
  //if "IsMinusPol" is TRUE, diode conduction occures if "Current" less then
  //"DiodeThreshold".
  bool IsMinusPol;
  // IBGT with Diode (TRUE), or Ordinary Diode (FALSE)
  bool IsIgbt;
  // if "IsIgbt" is TRUE, the IGBT On/Off conduction rate in "ohms/second"
  double IgbtOnCondRate;
  double IgbtOffCondRate;
  //The minimum and maximum On/Off resistance of the device.
  double OnRes;
  double OffRes;
  //If "IsIgbt" is TRUE, the gate "On" delay in seconds
  double GateDelay;
  //If "IsIgbt", the recorded time "t" in which a gate change was detected
  double GateChangeTime;
  //The time in which the state of the IGBT changes conduction.
  double CondChangeTime;
  //Flag indicating when the beginning point of conduction change should occure.
  bool RecordCondChangeTime;
  //storage for probes...
  vector<double> DeviceResistance;
  vector<double> DeviceCurrent;
  vector<double> DeviceGate;

};

void SwitchObject::RecordProbes(void)
{
  // NOTE: This function can be overloaded in the application to provide for more probes.
  if(DoProbes_res)
    DeviceResistance.push_back(y_diode*y_igbt/(y_diode + y_igbt));
  if(DoProbes_cur)
    DeviceCurrent.push_back(DevCur);
  if(DoProbes_gate)
    DeviceGate.push_back((double) DevGate);
}

void SwitchObject::PlotProbes(Gnuplot & SimuPlot, vector<double> & Plot_t, string TagNamesToPlot[])
{
  // NOTE: This function can be overloaded in the application to provide for more probes.
  int i;
  if(TagNamesToPlot[0] == ""){
    if(DoProbes_res)
      SimuPlot.plot_xy(Plot_t, DeviceResistance, LiteralName + "_res");
    if(DoProbes_cur)
      SimuPlot.plot_xy(Plot_t, DeviceCurrent, LiteralName + "_cur");
    if(DoProbes_gate)
      SimuPlot.plot_xy(Plot_t, DeviceGate, LiteralName + "_gate");
  }
  else{
    for(i = 0; i < 20; i++){
      if((TagNamesToPlot[i] == LiteralName + "_res") && DoProbes_res){
	SimuPlot.plot_xy(Plot_t, DeviceResistance, LiteralName + "_res");	
      }
      else if((TagNamesToPlot[i] == LiteralName + "_cur") && DoProbes_cur){
	SimuPlot.plot_xy(Plot_t, DeviceCurrent, LiteralName + "_cur");
      }
      else if((TagNamesToPlot[i] == LiteralName + "_gate") && DoProbes_gate){
	SimuPlot.plot_xy(Plot_t, DeviceGate, LiteralName + "_gate");
      }
      else if(TagNamesToPlot[i] == ""){
	break;
      }
    }
  }   
}

//Base constructor for class "SwitchObject"
SwitchObject::SwitchObject(void)
{   

     //Defaults and intial states.
  DevCur = 0;
  DevGate = FALSE;
  DiodeState = FALSE;
  GateState = FALSE;
  GateTran = FALSE;
  IsMinusPol = FALSE;
  IsIgbt = FALSE;
  IgbtOnCondRate =  1e10;           //100000000.0;       //ohms/second 
  IgbtOffCondRate = 1e10;   // 100000000.0;      //ohms/second
  OnRes = .1;               //ohms
  OffRes = 1000000.0;       //ohms
  GateDelay = .000001;   //seconds
  GateChangeTime = 0;
  CondChangeTime = 0; 
  RecordCondChangeTime = FALSE;


  y_igbt = OffRes;
  y_diode = OffRes;
  
}
//Base destructor for class "SwitchObject"
SwitchObject::~SwitchObject(void)
{
 


}
void SwitchObject::Exception(string Message)
{
  if(!GotException){
    ExceptionMessage = Message;
    GotException = TRUE;
  }
}

void SwitchObject::CheckForSwitchTransition(double t, double h, double h_start)
{
   
  //  bool StateTran = FALSE;


 

  // first check the state of diode conduction.
  if(!DiodeState && ((IsMinusPol && (DevCur < 0)) ||
		     (!IsMinusPol && (DevCur > 0)))){
    DiodeState = TRUE;
    //   StateTran = TRUE;
  }
  else if(DiodeState && ((IsMinusPol && (DevCur >= 0)) ||
			 (!IsMinusPol && (DevCur <= 0)))){
    DiodeState = FALSE;
    //   StateTran = TRUE;
  }


  //If this device is an IGBT...
 if(IsIgbt && !GateState && DevGate){
   GateChangeTime = t;
   GateState = TRUE;
   GateTran = TRUE;
   //   StateTran = TRUE;
  }
  else if(IsIgbt && GateState && !DevGate){
   GateState = FALSE;
   GateTran = TRUE;
   //   StateTran = TRUE;
  }



  // //Now, if the commanded state of the switch has changed by any of the tests above, 
  //  //decide if the time increment should be reset.
  // if(StateTran){
  //   //Decide if we should reset the time increment.
  //   if(h > h_start)
  //    longjmp(OdeResetEnv, 1);
  //  }
  
}

       

void SwitchObject::ExecuteSwitchTransition(double t)
{
 
 
  if(DiodeState == TRUE){
    y_diode = OnRes;
  }
  else{

    y_diode = OffRes;
  }

  
  //NOTE: In genenal the code below in more closely related
  //      to the forward conduction characteristics of an FET device (MOSFET), rather than  
  //      an IGBT. This is good enought for now.
  
  if(GateTran){  
    if(GateState){
      
      if((t - GateChangeTime) > GateDelay){

	if(!RecordCondChangeTime){
	  CondChangeTime = t;
	  RecordCondChangeTime = TRUE;
	}
 	
 	
 	y_igbt = y_igbt - IgbtOnCondRate * (t - CondChangeTime);
 	
 	if(y_igbt < OnRes){
 	  y_igbt = OnRes;
 	  GateTran = FALSE;
	  RecordCondChangeTime = FALSE;
 	}
      }
    }
    else{

      if(!RecordCondChangeTime){
	CondChangeTime = t;
	RecordCondChangeTime = TRUE;
      }


      y_igbt = y_igbt + IgbtOffCondRate * (t - CondChangeTime);
     
      if(y_igbt > OffRes){
 	y_igbt = OffRes;
 	GateTran = FALSE;
	RecordCondChangeTime = FALSE;
      }
      
    }
  }
  







  //  if(GateState == TRUE){
  //    if((t - GateChangeTime) > GateDelay)
  //     y_igbt = OnRes;
  //   else
  //       y_igbt = OffRes;
  // }
  // else{

  //  y_igbt = OffRes;
  // }


}



class SrcObject      //base class for specific Src function
{
public:
  double y;
  //consturctor
  SrcObject(void);
  ~SrcObject(void);
  void Exception(string Message);
  //("t" declared in "SimuObject" class)
  virtual void SrcFunction(double t){};
              //these functions uses "pSrcObjRValList", "pOdeObjRValList", "pCtrlObjRValList" and so  
              //forth below to update the <rvalues> of other classes of the same type.
  virtual void SrcRValueUpdate(void){};
  virtual void OdeRValueUpdate(void){};
  virtual void CtrlRValueUpdate(void){};
  virtual void CoefRValueUpdate(void){};
  virtual void SwitchRValueUpdate(void){};             
  virtual void PreSrcFunction(double t, double h, double h_start){};
             //Probes to provide extended plotting ability.
  bool DoProbes;
  virtual void RecordProbes(void){};
  virtual void PlotProbes(Gnuplot &, vector<double> &, string TagNamesToPlot[]){};
  //Assigned by the class that inherits this class. Used at initialization to build the list
  //"pOdeObjRValList" below.
  SRC_FUNCTION SrcFuncName;
  string LiteralName; 
  //The class that inherits this class uses these lists to update "<rvalues>" with it's "y".
  SrcObjItem * pSrcObjRValList;
  OdeObjItem * pOdeObjRValList;
  CtrlObjItem * pCtrlObjRValList;
  CoefObjItem * pCoefObjRValList;
  SwitchObjItem * pSwitchObjRValList;
  //indicator if the output of this object should be plotted by GnuPlot.
  bool PlotThisOutput;
  //a string for tagging this output to GnuPlot
  string Plot_Tag;
  //if enabled by "PlotThisOutput" above, this is where we store each interation of "y"
  vector<double> Plot_y;
  //exception variables...
  string ExceptionMessage;
  bool GotException;

};

//Base constructor for class "SrcObject"
SrcObject::SrcObject(void)
{
  //!!!! any initializations go here !!!!!


}
//Base destructor for class "SrcObject"
SrcObject::~SrcObject(void)
{
 


}
void SrcObject::Exception(string Message)
{
  if(!GotException){
    ExceptionMessage = Message;
    GotException = TRUE;
  }
}


class CtrlObject     //base class for specific Ctrl function.
{
public:
  double y;       
  //constructor
  CtrlObject(void);
  ~CtrlObject(void);
  void Exception(string Message);
  //("t" declared in "SimuObject" class)
  virtual void CtrlFunction(double t){}; 
              //these functions uses "pSrcObjRValList",  "pOdeObjRValList","pCtrlObjRValList" 
              //and so forth below to update the <rvalues> of other classes of the same type.
  virtual void SrcRValueUpdate(void){}
  virtual void OdeRValueUpdate(void){};
  virtual void CtrlRValueUpdate(void){}; 
  virtual void CoefRValueUpdate(void){};
  virtual void SwitchRValueUpdate(void){};
              //Probes provide extended plottin ability.
  bool DoProbes;
  virtual void RecordProbes(void){};
  virtual void PlotProbes(Gnuplot &, vector<double> &, string TagNamesToPlot[]){};
  //Assigned by the class that inherits this class. Used at initialization to build the lists
  //"pOdeObjRValList" and "pCtrlObjRValList" below.
  CTRL_FUNCTION CtrlFuncName;
  string LiteralName; 
  //flag to force CtrlObject instance to execute it's "CtrlRValueUpdate()" immediately after executing its "CtrlFunction()"
  bool DoImmedCtrlUpdate;
    
  //The class that inherits this class uses these lists to update "<rvalues>" with it's "y".
  SrcObjItem * pSrcObjRValList;
  OdeObjItem * pOdeObjRValList;	
  CtrlObjItem * pCtrlObjRValList;
  CoefObjItem * pCoefObjRValList;
  SwitchObjItem * pSwitchObjRValList;
  //indicator if the output of this object should be plotted by GnuPlot.
  bool PlotThisOutput;
  //a string for tagging this output to GnuPlot
  string Plot_Tag;
  //if enabled by "PlotThisOutput" above, this is where we store each interation of "y"
  vector<double> Plot_y; 
  //exception variables...
  string ExceptionMessage;
  bool GotException;


};
//Base constructor for class "CtrlObject"
CtrlObject::CtrlObject(void)
{
  //!!!! any initializations go here !!!!!


}
//Base destructor for class "CtrlObject"
CtrlObject::~CtrlObject(void)
{
 


}

void CtrlObject::Exception(string Message)
{
  if(!GotException){
    ExceptionMessage = Message;
    GotException = TRUE;
  }
}

class OdeObject      //base class for specific Ode function.
{

public:
  double y;  //("t" and "h" declared in "SimuObject" class)
  double dydt;
  double y_low_prec;
  double y_high_prec;
  double Error;
  //these parameters used only for "Group Solve" ODE forms.
  bool GroupSolve;
  int NumberOfGrpOdes;
  //constructor
  OdeObject(void);
  ~OdeObject(void);
  void Exception(string Message);
  virtual double OdeFunction(double y_cur, double t){return 0;}; 
  virtual void OdeGroupMSolve(double dydt[],  double dmdt[]){};
        
              //these functions uses "pOdeObjRValList", "pCtrlObjRValList" and 
              //so forth below to update the <rvalues> of other classes of the same type.
  virtual void OdeRValueUpdate(void){};
  virtual void CtrlRValueUpdate(void){};
  virtual void CoefRValueUpdate(void){};
  virtual void SwitchRValueUpdate(void){};
               //This function is called right after the "y" result is updated.
               //(Typically used for auxilary processes that run "real-time"). 
  virtual void PostOdeFunction(double t){};
  virtual void PreOdeFunction(double t, double h, double h_start){};
              //Probes provide extended plottin ability.
  bool DoProbes;
  virtual void RecordProbes(void){};
  virtual void PlotProbes(Gnuplot &, vector<double> &, string TagNamesToPlot[]){};
  double ShapeSquareWaveSource(double Level, double SlewRate, double t);
  void Runge_Kutta_FourthOrder(double t, double h);
  void Runge_Kutta_Fehlberg(double t, double h);
  double Runge_Kutta_Fehlberg_MSolve(double t, double h, double k[], int Stage);
  void Runge_Kutta_Fehlberg_YSolve(double k[], double h);
  void Runge_Kutta_ThirdOrder(double t, double h);
   //Assigned by the class that inherits this class. Used at initialization to build the lists
  //"pOdeObjRValList" and "pCtrlObjRValList" below.
  ODE_FUNCTION OdeFuncName;
  string LiteralName; 
    //states for "ShapeSquareWaveSource()".
  double CurLevel;
  double StartSqrWaveTime;
  bool SqrWaveSrcFirstCall;
  bool SawSqrWaveTransition;
  //The class that inherits this class uses these lists to update "<rvalues>" with it's "y".
  OdeObjItem * pOdeObjRValList;	
  CtrlObjItem * pCtrlObjRValList;
  CoefObjItem * pCoefObjRValList;
  SwitchObjItem * pSwitchObjRValList;
   //indicator if the output of this object should be plotted by GnuPlot.
  bool PlotThisOutput;
  //a string for tagging this output to GnuPlot
  string Plot_Tag;
  //if enabled by "PlotThisOutput" above, this is where we store each interation of "y"
  vector<double> Plot_y; 
  //exception variables...
  string ExceptionMessage;
  bool GotException;

};
//Base constructor for class "OdeObject"
OdeObject::OdeObject(void)
{
  //!!!! any initializations go here !!!!!


}
//Base destructor for class "OdeObject"
OdeObject::~OdeObject(void)
{
 


}
void OdeObject::Exception(string Message)
{
  if(!GotException){
    ExceptionMessage = Message;
    GotException = TRUE;
  }
}

       // Apply a slope to an input square wave source
double OdeObject::ShapeSquareWaveSource(double Level, double SlewRate, double t)
{
  double Source;
  if(!SqrWaveSrcFirstCall){
    CurLevel = Level;
    Source = Level;
    SqrWaveSrcFirstCall = TRUE;
  }
  else if((Level != CurLevel) && !SawSqrWaveTransition){
     StartSqrWaveTime = t;
     SawSqrWaveTransition = TRUE;
     //    Source = CurLevel;
     //we must restart with using minimum "h"
    longjmp(OdeResetEnv, 1);
  }
  else if(SawSqrWaveTransition){
    if(Level >= CurLevel){
      Source = CurLevel + SlewRate*(t-StartSqrWaveTime);
      if(Source >= Level){
	Source = Level;
	CurLevel = Level;
	SawSqrWaveTransition = FALSE;
      }
    }
    else{
      Source = CurLevel - SlewRate*(t-StartSqrWaveTime);
      if(Source <= Level){
	Source = Level;
	CurLevel = Level;
	SawSqrWaveTransition = FALSE;
      }
    }
  }
  else{
    Source = Level;
  }
  return Source;
  
}





   // Fourth order Runge-Kutta for a single ordinary differential equation 
void OdeObject::Runge_Kutta_FourthOrder(double t, double h)
{


  //This algorythm taken from ".\ODE-Solvers_Docs\RungeKutta_java.htm", a typical 4th order
  //algorythm which agress with "runge-kutta.pdf" and "Numerical Solutions of Ordinary Differential Equations.mht" in the
  //same directory. 

  //There are also various examples of 5th/6th order algorythm's with variable time step control.
  //We stay with the 4th order because it is easier, and according to the documentation in the folder, the most used in
  //tools like Simulink, Matlab, etc..

  //However, there is no direct example for variable time step, for the 4th order algorythm, so we must add this
  //below.

  //Most of the examples (code examples and HTML doc's) regarding variable time methods are described as 4th/5th
  //order methods where the error is calculated as the difference of the results of the 4th and 5th order simulation of
  //of the ODE (see RungeKutta.cpp and Ode.cpp in compressed file "ode_src.rar". 

  //For speed we will use the same idea but instead use the compenation of the 2nd and 4th order simulations.
  //(the doc's show two examples of a 3rd order RungeKutta which we could replace with the second order below.)

  double  k1, k2, k3, k4;
 
  k1 = h*OdeFunction(y, t);
  k2 = h*OdeFunction(y + k1/2, t + h/2);



  y_low_prec = y + k2;   //second order result

  k3 = h*OdeFunction(y + k2/2, t + h/2);
  k4 = h*OdeFunction(y + k3, t + h);
 

  y_high_prec = y + k1/6 + k2/3 + k3/3 + k4/6;   //forth order result

  //update "dydt"
  dydt = (y_low_prec - y)/h;
                      
}


     //Runge-Kutta third order method
void OdeObject::Runge_Kutta_ThirdOrder(double t, double h)
{

  double  k1, k2, k3, k4, k3_;
 
  k1 = h*OdeFunction(y, t);
  k2 = h*OdeFunction(y + k1/2, t + h/2);

  k3_ = h*OdeFunction(y - k1 + 2*k2, t + h);



  y_low_prec = y + k1/6 + 2*k2/3 + k3_/6;   //third order result

  k3 = h*OdeFunction(y + k2/2, t + h/2);
  k4 = h*OdeFunction(y + k3, t + h);
 

  y_high_prec = y + k1/6 + k2/3 + k3/3 + k4/6;   //forth order result

  //update "dydt"
  dydt = (y_low_prec - y)/h;
}




     //Runge-Kutta-Fehlberg method for a single ordinary differential equation

void OdeObject::Runge_Kutta_Fehlberg(double t, double h)
{
  //Refer to documents "Runge-Kutta-Fehlberg Method.mht", "RungeKutta_java.htm" and
  //"Topic 14_5 Runge Kutta Fehlberg (Theory).htm" for this method.

  double k1, k2, k3, k4, k5, k6;

 
  k1 = h*OdeFunction(y, t);
  k2 = h*OdeFunction(y + k1/4.0, t + h/4.0);
  k3 = h*OdeFunction(y + (3.0*k1 + 9.0*k2)/32.0, t + 3.0*h/8.0);
  k4 = h*OdeFunction(y + (1932.0*k1 - 7200.0*k2 + 7296.0*k3)/2197.0, t + 12.0*h/13.0);
  k5 = h*OdeFunction(y + 439.0*k1/216.0 - 8.0*k2 + 3680.0*k3/513.0 - 845*k4/4104.0, t + h);
  k6 = h*OdeFunction(y - 8.0*k1/27.0 + 2.0*k2 - 3544.0*k3/2565.0 + 1859.0*k4/4104.0 - 11.0*k5/40.0, t + 0.5*h);
  
  //according to "Topic 14_5 Runge Kutta Fehlberg (Theory).htm", the less accurate result is used.

  y_low_prec = y + 25.0*k1/216.0 + 1408.0*k3/2565.0 + 2197.0*k4/4104.0 - k5/5.0;
  y_high_prec = y + 16.0*k1/135.0 + 6656.0*k3/12825.0 + 28561.0*k4/56430.0 - 9.0*k5/50.0 + 2.0*k6/55.0;

  //update "dydt"
  dydt = (y_low_prec - y)/h;
}

double OdeObject::Runge_Kutta_Fehlberg_MSolve(double t, double h, double k[], int Stage)
{
  if(Stage == 0)
    return OdeFunction(y,t);
  else if(Stage == 1)
    return OdeFunction(y + k[1]/4.0, t + h/4.0);
  else if(Stage == 2)
    return OdeFunction(y + (3.0*k[1] + 9.0*k[2])/32.0, t + 3.0*h/8.0);
  else if(Stage == 3)
    return OdeFunction(y + (1932.0*k[1] - 7200.0*k[2] + 7296.0*k[3])/2197.0, t + 12.0*h/13.0);
  else if(Stage == 4)
    return OdeFunction(y + 439.0*k[1]/216.0 - 8.0*k[2] + 3680.0*k[3]/513.0 - 845*k[4]/4104.0, t + h);
  else if(Stage == 5)
    return OdeFunction(y - 8.0*k[1]/27.0 + 2.0*k[2] - 3544.0*k[3]/2565.0 + 1859.0*k[4]/4104.0 - 11.0*k[5]/40.0, t + 0.5*h);
}

void OdeObject::Runge_Kutta_Fehlberg_YSolve(double k[], double h)
{
 
  y_low_prec = y + 25.0*k[1]/216.0 + 1408.0*k[3]/2565.0 + 2197.0*k[4]/4104.0 - k[5]/5.0;
  y_high_prec = y + 16.0*k[1]/135.0 + 6656.0*k[3]/12825.0 + 28561.0*k[4]/56430.0 - 9.0*k[5]/50.0 + 2.0*k[6]/55.0;

  //update "dydt"
  dydt = (y_low_prec - y)/h;
}



class SimuObject
{
public:

  





  double RelTol;
  double AbsTol;
  double MaxOdeErr; 
  bool DoCtrlUpdate;
        //This is the ODE time acummulator.
  double t;
  double h;
  double h_start;
  double h_max;
  //total time for simulation.
  double SimuTime;
  //this is the "SAFETY" setting used for error calculations as per "RungeKutta_java.htm",
  //typically set to ".9".
  double Safety;
  ODE_SIMU_TYPE OdeSimuType;
  bool OdeSimuFixed;
          //This is the smallest time quantum of the entire system.
  double TimeQuantum;
  bool Do_h_Plot;
  bool RecordPlotData;
  double MinimumPlotTime;
  double MaximumPlotTime;
  vector<double> Plot_h;
  void TakeOneOdeStep(void);
  void FindMaxOdeError(void);
  bool  AdjustTimeStep(void);
  bool DoOneInteration(void);
  void PlotSimuResults(string [], double [], double[], double[], double[], double);
  void UpdateCtrlEquations(CtrlObjItem * pCurCtrlItem);
  void RungeKuttaFehlbergGroupSolve(OdeObjItem ** ppStrtOdeGrpItem);
  //constructor
  SimuObject(void);
  ~SimuObject(void);
     //These are used to keep source transition periods synchronized to
     //the time step parameter "h". (This control is optional).
  double SrcPeriodQuantum;
  double SrcPeriodAccumulator;
     //linked list of ALL Switch objects to be updated each interation (variable step size)
  SwitchObjItem * pSwitchEquationList;
              //(NOTE: "pCoefEquationList" used for exception processing only,
              //        because "SpiceObject's" access "CoefObject's" directly)
  CoefObjItem * pCoefEquationList;
     //linked list of ALL Spice objects to be updated each interation (variable step size)
  SpiceObjItem * pSpiceEquationList;
     //linked list of ALL equation objects to be updated each interation (variable step size)
  OdeObjItem * pOdeEquationList;
     //linked list of ALL source object to be updated each interation (variable step size)
  SrcObjItem * pSrcEquationList;
     //linked list of "concurrent" control objects to be updated each interation (fixed step sizes, but
     //each list may have different sizes (quantum's) as described below).
  CtrlObjItem * pTranslationList;    //equations that have to do with 3/2 phase D/Q and there inverses.
  CtrlObjItem * pTrajectoryList;     //equations that generate the command (trajectory)
  //(more to follow.....)
       //For each "CtrlObjItem" list (group) above, there exists a time quantum for which each group of
       //control objects are to be stepped. "CtrlTimeQuantum" must always be an integer
       //multiple of "TimeQuantum" above. "<control list name>QuantumNum" holds the number of
       //"CtrlTimeQuantum's" for this control. Value "<control list name>QuantumCount" holds the present
       //quantum count for this control. It is decrement by "1" each time t equals or exceeds 
       //"CtrlTimeAccumlator". When "<control list name>QuantumCount" goes to zero, the control is 
       //executed. "<control list name>QuantumCount" is then reset by "<control list name>QuantumNum".
       //"Do<control list name>Update" is a flag to indicate that "CtrlFunction()'s" for the given
       //control list needs to be executed.
  double CtrlTimeQuantum;
  double CtrlTimeAccumulator;
    // time quantum parameters for "pTranslationList"
  int TranslationQuantumCount;
  int TranslationQuantumNum;
  int DoTranslationUpdate;
  vector<double> Plot_t_Translation;
    // time quantum parameter for "pTrajectoryList"
  int TrajectoryQuantumCount;
  int TrajectoryQuantumNum;
  int DoTrajectoryUpdate;
  vector<double> Plot_t_Trajectory;
 
  //(more to follow...)

  //NOTE: As of 6/12/08, this CTRL group pointer deprecates "pTranslationList"
  //      and it's associated parameters as well as "pTrajectoryList" and it's
  //      associated parameters above (see definition of structure "CtrlObjGroup" above).
  CtrlObjGroup * pCtrlGroupList;


  //this is where we store each interation of "t" for all ODE equations.
  vector<double> Plot_t; 
  //flag indicating exception detected.
  bool GotException;
  //if an exception, add this to the current "t" to set new (truncated) "SimuTime".
  double ExceptionTime;
  //the buffer to hold the exception message.
  ostringstream ExceptionMessageBuffer;
};




  /*  ------------------------------------------------------------------------------------

  From "RungeKutta_java.html", this is how we compute the error tolererances and adjust h.
  Most of the documentation in "ODE-Solvers_Docs" discribes a method similar to this.



  maxerr=0.0D;
  for(int i=0; i<nequ; i++)
  {
    //...
    err = Math.abs(y6[i] - y5[i]);
    tol= Math.abs(y5[i])*reltol +  abstol;
    maxerr = Math.max(maxerr,err/tol);

  }

  if(maxerr<=1.0D)
  {
    x += h;                                          //!!!!! notice that only if max error goes below threshold, that we advance "t"   
    delta = SAFETY*Math.pow(maxerr, -0.2);  
    if(delta>4.0)
      {
	h*= 4.0;
      }
    else if (delta > 1.0)
      {
	h*=delta;
      }
    if(x+h > xn)
      h = xn-x;
      y = (double[])y5.clone();        //!!! I'm not sure what this means but the "y" for each equation must be updated here????
  }
  else
  {
    delta = SAFETY*Math.pow(maxerr,-0.25);        //!!! don't advance "t" (or update "y's"), just fix "h" and continue.
    if(delta < 0.1D) h *= 0.1;
    else h *= delta;
  }

  The functions "TakeOneTimeStep()", "FindMaxOdeError()", and "AdjustTimeStep()" below, emulate this method. 

  -----------------------------------------------------------------------------------------------------------*/


void SimuObject::TakeOneOdeStep(void){

  OdeObjItem * pCurOdeItem = pOdeEquationList;
  SwitchObjItem * pCurSwitchItem = pSwitchEquationList;
  SrcObjItem * pCurSrcItem = pSrcEquationList;
  SpiceObjItem * pCurSpiceItem = pSpiceEquationList;
  

  if(setjmp(OdeResetEnv)){
    //we received a reset ODE reset signal.
    //we start over with the minimum "h"
    h = h_start;
    //only register enviroment is saved, we must start from the very beginning.
    pCurOdeItem = pOdeEquationList; 
    pCurSpiceItem = pSpiceEquationList;
  }


  //Check for switch transitions that may occure durring this interation,
  //resetting the time step increment "h" if necessary throught a long jump
  //to "OdeResetEnv"
  pCurSwitchItem = pSwitchEquationList;
  while(pCurSwitchItem){
	pCurSwitchItem->pSwitchObject->PreSwitchFunction(t, h, h_start);
    pCurSwitchItem->pSwitchObject->CheckForSwitchTransition(t, h, h_start);
    pCurSwitchItem = pCurSwitchItem->pNextSwitchItem;
  } 

  //Pre-process  any "SrcObject" transitions that may occur during this interation,
  //resetting the time step increment "h" if necessary through a long jump
  //to "OdeResetEnv". (These are usually "SrcObject's" that are not
  //superceded or influenced by "CtrlObject's").
  while(pCurSrcItem){
    pCurSrcItem->pSrcObject->PreSrcFunction(t, h, h_start);
    pCurSrcItem = pCurSrcItem->pNextSrcItem;
  }
  
  //Pre-process any "OdeObject's" during this interation,
  //reseting the time step increment "h" if ncessary through a long jump
  //to "OdeResetEnv".
  while(pCurOdeItem){
    pCurOdeItem->pOdeObject->PreOdeFunction(t, h, h_start);
    pCurOdeItem = pCurOdeItem->pNextOdeItem;
  }
  pCurOdeItem = pOdeEquationList;
  
  
  while(pCurOdeItem){

    if(OdeSimuType == ODE_SIMU_56){
      if(pCurOdeItem->pOdeObject->GroupSolve){
	//a group differencial solve.
	RungeKuttaFehlbergGroupSolve(&pCurOdeItem);
      }else{
	//a normal single differencial solve.
	pCurOdeItem->pOdeObject->Runge_Kutta_Fehlberg(t,h);
      }
    }
    else if(OdeSimuType == ODE_SIMU_24){

      pCurOdeItem->pOdeObject->Runge_Kutta_FourthOrder(t,h);

    }else if(OdeSimuType == ODE_SIMU_34){

      pCurOdeItem->pOdeObject->Runge_Kutta_ThirdOrder(t,h);
    }

    pCurOdeItem = pCurOdeItem->pNextOdeItem;

  }

  //generate a transient solution at current time "t" for all "SpiceObject"'s.
  while(pCurSpiceItem){
   //collect coefficients for Backward Euler solution ...
   pCurSpiceItem->pSpiceObject->PreSpiceFunction(1.0/h, FALSE);
   //generate solution vector for Backward Euler solution...
   pCurSpiceItem->pSpiceObject->Gauss(FALSE);

   if(!OdeSimuFixed){
	   //collect coefficients for Trapezoidal solution ...
	   pCurSpiceItem->pSpiceObject->PreSpiceFunction(1.0/h, TRUE);
	   //generate solution vector for Trapezoidal solution...
	   pCurSpiceItem->pSpiceObject->Gauss(TRUE);
   }


   pCurSpiceItem = pCurSpiceItem->pNextSpiceItem;
  }





}


void SimuObject::RungeKuttaFehlbergGroupSolve(OdeObjItem ** ppStrtOdeGrpItem)
{
  OdeObjItem * pStrtOdeGrpItem = * ppStrtOdeGrpItem;
  OdeObjItem * pCurOdeItem = pStrtOdeGrpItem;
  int i,j;
  int NumberOfGrpOdes = pStrtOdeGrpItem->pOdeObject->NumberOfGrpOdes;
  double k[NumberOfGrpOdes][7];
  double dydt[NumberOfGrpOdes];
  double dmdt[NumberOfGrpOdes];
 

  for(j = 0; j < 6; j++){
    for(i = 0; i < NumberOfGrpOdes; i++){
      //(k[i][0]'s are not used)
      dmdt[i] = pCurOdeItem->pOdeObject->Runge_Kutta_Fehlberg_MSolve(t, h, k[i], j);
      pCurOdeItem = pCurOdeItem->pNextOdeItem;
    }  
    pCurOdeItem = pStrtOdeGrpItem;
    //only the first ODE of the group solves for the dydt's of each ODE in the group
    pCurOdeItem->pOdeObject->OdeGroupMSolve(dydt, dmdt);
    for(i = 0; i < NumberOfGrpOdes; i++){
      k[i][j+1] = h*dydt[i];
    }
  }  //now, solve for the "y's" that will be used in the normal update process
  pCurOdeItem = pStrtOdeGrpItem;
  for(i = 0; i < NumberOfGrpOdes; i++){
    pCurOdeItem->pOdeObject->Runge_Kutta_Fehlberg_YSolve(k[i], h);
    pCurOdeItem = pCurOdeItem->pNextOdeItem;
  }  //setup to skip to past this group to next ODE object
  pCurOdeItem = pStrtOdeGrpItem;
  for(i = 0; i < NumberOfGrpOdes-1; i++){
    pCurOdeItem = pCurOdeItem->pNextOdeItem;
  } 
  * ppStrtOdeGrpItem = pCurOdeItem;

}


void SimuObject::FindMaxOdeError(void){

  
  double err;
  double tol;
  double maxerr = 0;
  OdeObjItem * pCurOdeItem = pOdeEquationList;
  SpiceObjItem * pCurSpiceItem = pSpiceEquationList;
  int i;

  while(pCurOdeItem){
  
    err = fabs(pCurOdeItem->pOdeObject->y_high_prec - pCurOdeItem->pOdeObject->y_low_prec);
    //according to "Topic 14_5 Runge Kutta Fehlberg (Theory).htm", the less accurate result is used.
    tol= fabs(pCurOdeItem->pOdeObject->y_low_prec)*RelTol +  AbsTol;
  
    maxerr = max(maxerr,err/tol);


    pCurOdeItem = pCurOdeItem->pNextOdeItem;
  }


  while(pCurSpiceItem){

	for(int i = 0; i < pCurSpiceItem->pSpiceObject->n; i++)
	{
		err = fabs(pCurSpiceItem->pSpiceObject->pTrapezoidalSolutionVector[i] - pCurSpiceItem->pSpiceObject->pBackwardEulerSolutionVector[i]);
		tol = fabs(pCurSpiceItem->pSpiceObject->pBackwardEulerSolutionVector[i])*RelTol + AbsTol;

		 maxerr = max(maxerr,err/tol);

	}


    pCurSpiceItem = pCurSpiceItem->pNextSpiceItem;
  }



  MaxOdeErr = maxerr;



}




bool SimuObject::AdjustTimeStep(void){
  double delta;

  bool DoTimeStep = 1;


 

  if(MaxOdeErr <= 1.0){
      
    if((t+h) > CtrlTimeAccumulator){
      h = CtrlTimeAccumulator - t;
     
      CtrlTimeAccumulator += CtrlTimeQuantum;
    
      DoCtrlUpdate = 1;
      DoTimeStep = 0;
    }
    else if((t+h) > SrcPeriodAccumulator){
      //we just re-adjust "h", and repeat the interation.
      h = SrcPeriodAccumulator - t;
      SrcPeriodAccumulator += SrcPeriodQuantum;
      DoTimeStep = 0;
    }
    else{
      t += h;
      
      if(!OdeSimuFixed){
	if(MaxOdeErr != 0){
	  delta = Safety*pow(MaxOdeErr, -0.2);
	  
	  if(delta > 4.0){
	    h *= 4.0;
	  }
	  else if (delta > 1.0){
	    h *= delta;
	  }
	}
	else{
	  h *= 4.0;
	}


	//  Clamp "h" to this h_max if it is exceeded.
	//  (See "Running a Simulation(UsingSimulink).mht")
	if(h_max != 0)
	  if(h > h_max)
	    h = h_max;



      }


    }   

 
 
  }
  else{//just fix "h" and recalculate.
    delta = Safety*pow(MaxOdeErr,-0.25);        
    if(delta < 0.1){
      h *= 0.1;
    }
    else{
      h *= delta;
    }
    //***********************************
    //     We need to add a function call here into "Simulation.cc" like "CheckStepParameters(h, h_start, delta)"
    //     to check things like "delta" going to "1.0" for an extended time and "h" going below "h_start" for
    //     an extended time (See "Running a Simulation(UsingSimulink).mht").
    //***********************************
    DoTimeStep = 0;

  }
  
  

  if(h == 0){
    h = TimeQuantum;
  }
  else if(h > CtrlTimeQuantum){
    h = CtrlTimeQuantum;
  }
 
  
  
  
  return DoTimeStep;

}



bool SimuObject::DoOneInteration(void)
{
  OdeObjItem * pCurOdeItem;
  CtrlObjItem * pCurCtrlItem;
  SrcObjItem * pCurSrcItem;
  CtrlObjGroup * pCurCtrlGroup;
  SpiceObjItem * pCurSpiceItem;
  SwitchObjItem * pCurSwitchItem;
  CoefObjItem * pCurCoefItem;

  TakeOneOdeStep();
  if(!OdeSimuFixed)
    FindMaxOdeError();
  if(AdjustTimeStep()){

    if((t >= MinimumPlotTime) && (t <= MaximumPlotTime))
      RecordPlotData = TRUE;
    else
      RecordPlotData = FALSE;

    //if enable, store the current "h" parameter for plotting.
    if(Do_h_Plot && RecordPlotData){
      Plot_h.push_back(h);
    }

    // update "y" of all ODE equations using the previously calculated of "y_low_prec. 
    pCurOdeItem = pOdeEquationList;
    while(pCurOdeItem){
      //according to "Topic 14_5 Runge Kutta Fehlberg (Theory).htm", the less accurate result is used.
      pCurOdeItem->pOdeObject->y = pCurOdeItem->pOdeObject->y_low_prec;
      //do auxilary processing for this ODE (optional).
      pCurOdeItem->pOdeObject->PostOdeFunction(t);
      //update this ODE's output to GnuPlot buffer if so indicated.
      if(pCurOdeItem->pOdeObject->PlotThisOutput && RecordPlotData){
	pCurOdeItem->pOdeObject->Plot_y.push_back(pCurOdeItem->pOdeObject->y);
      }
      //if this ODE contains probes, record them.
      if(pCurOdeItem->pOdeObject->DoProbes && RecordPlotData){
	pCurOdeItem->pOdeObject->RecordProbes();
      }
      pCurOdeItem = pCurOdeItem->pNextOdeItem;
    }

    //Execute the switch transitions.
    pCurSwitchItem = pSwitchEquationList;
    while(pCurSwitchItem){
      pCurSwitchItem->pSwitchObject->ExecuteSwitchTransition(t);
      //if this SWITCH contains probes, record them.
      if(pCurSwitchItem->pSwitchObject->DoProbes && RecordPlotData){
	pCurSwitchItem->pSwitchObject->RecordProbes();
      }
      pCurSwitchItem = pCurSwitchItem->pNextSwitchItem;
    }     

    //post-processing for all "SpiceObject"'s.
    pCurSpiceItem = pSpiceEquationList;
    while(pCurSpiceItem){
      //post processing (if required).
      pCurSpiceItem->pSpiceObject->PostSpiceFunction();
      //if ths Spice object contains probes, record them.
      if(pCurSpiceItem->pSpiceObject->DoProbes && RecordPlotData){
	pCurSpiceItem->pSpiceObject->RecordProbes();
      }
      pCurSpiceItem = pCurSpiceItem->pNextSpiceItem;
    }

    //update the "y" value of all SRC equations using the current value of "t" and the current state
    //of it's <rvalues>.
    pCurSrcItem = pSrcEquationList;
    while(pCurSrcItem){
      pCurSrcItem->pSrcObject->SrcFunction(t);

      //update this SRC's output to GnuPlot buffer if so indicated.
      if(pCurSrcItem->pSrcObject->PlotThisOutput && RecordPlotData){
	pCurSrcItem->pSrcObject->Plot_y.push_back(pCurSrcItem->pSrcObject->y);
      }
      //if this SRC contains probes, record them.
      if(pCurSrcItem->pSrcObject->DoProbes && RecordPlotData){
	pCurSrcItem->pSrcObject->RecordProbes();
      }
      pCurSrcItem = pCurSrcItem->pNextSrcItem;

    }
    //next, update the <rvalues> of all "SrcObject's", "OdeObject's" 
    //and so forth pointed  by each source.
    pCurSrcItem = pSrcEquationList;
    while(pCurSrcItem){
      pCurSrcItem->pSrcObject->SrcRValueUpdate();
      pCurSrcItem->pSrcObject->OdeRValueUpdate();
      pCurSrcItem->pSrcObject->CoefRValueUpdate();
      pCurSrcItem->pSrcObject->SwitchRValueUpdate();
      pCurSrcItem = pCurSrcItem->pNextSrcItem;
    }


    //next, execute the switch functions.
    pCurSwitchItem = pSwitchEquationList;
    while(pCurSwitchItem){
      pCurSwitchItem->pSwitchObject->SwitchFunction(t);
      pCurSwitchItem = pCurSwitchItem->pNextSwitchItem;
    }


    if(DoCtrlUpdate){
      
      //update the <rvalues> of all "CtrlObject's" pointed to by each source.
      pCurSrcItem = pSrcEquationList;
      while(pCurSrcItem){
	pCurSrcItem->pSrcObject->CtrlRValueUpdate();  
      pCurSrcItem = pCurSrcItem->pNextSrcItem;
    }


      // update <rvalues> for all "CtrlObject's" pointed
      // to by this list of "OdeObject's"
      pCurOdeItem = pOdeEquationList;
      while(pCurOdeItem){
	pCurOdeItem->pOdeObject->CtrlRValueUpdate();
	pCurOdeItem = pCurOdeItem->pNextOdeItem;
      }


      // update <rvalues> for all "CtrlObject's" pointed
      // to by this list of "SpiceObject's"
      pCurSpiceItem = pSpiceEquationList;
      while(pCurSpiceItem){
	pCurSpiceItem->pSpiceObject->CtrlRValueUpdate();
	pCurSpiceItem = pCurSpiceItem->pNextSpiceItem;
      }





      // execute the "CtrlFunction()'s" for this list of "CtrlObject's"
      //(NOTE: This code deprecated as of 6/12/08)
      if(pCurCtrlItem = pTranslationList){
	TranslationQuantumCount--;
	if(!TranslationQuantumCount){
	  TranslationQuantumCount = TranslationQuantumNum;
	  DoTranslationUpdate = 1;
	  UpdateCtrlEquations(pCurCtrlItem);
	  //update Translation time twice because we stored the "before" and "after".
	  if(RecordPlotData){
	    Plot_t_Translation.push_back(t);
	    Plot_t_Translation.push_back(t);
	  }
	}
      }
      // execute the "CtrlFunction()'s" for this list of "CtrlObject's"
      //(NOTE: This code deprecated as of 6/12/08)
      if(pCurCtrlItem = pTrajectoryList){
	TrajectoryQuantumCount--;
	if(!TrajectoryQuantumCount){
	  TrajectoryQuantumCount = TrajectoryQuantumNum;
	  DoTrajectoryUpdate = 1;
	  UpdateCtrlEquations(pCurCtrlItem);
	  //update Translation time twice because we stored the "before" and "after".
	  if(RecordPlotData){
	    Plot_t_Trajectory.push_back(t);
	    Plot_t_Trajectory.push_back(t);
	  }
	} 
      }
      //(more to follow...)

      //(NOTE: "pTranslationList" and "pTrajectoryList" deprecated above,...
      //        we now use a CTRL group list)
      pCurCtrlGroup = pCtrlGroupList;
      while(pCurCtrlGroup){
	pCurCtrlGroup->QuantumCount--;
	if(!pCurCtrlGroup->QuantumCount){
	  pCurCtrlGroup->QuantumCount = pCurCtrlGroup->QuantumNum;
	  pCurCtrlGroup->DoUpdate = 1;
	  UpdateCtrlEquations(pCurCtrlGroup->pCtrlEquationList);
	  //update time twice because we stored the "before" and "after"
	  if(RecordPlotData){
	    pCurCtrlGroup->Plot_t.push_back(t);
	    pCurCtrlGroup->Plot_t.push_back(t);
	  }
	}
	pCurCtrlGroup = pCurCtrlGroup->pNextCtrlGroup;
      }



      // update <rvalues> for all "OdeObject's" and "CtrlObject's" pointed
      // to by this list of "CtrlObject's"
      //(NOTE: This code deprecated as of 6/12/08)
      if(DoTranslationUpdate){
	DoTranslationUpdate = 0;
	pCurCtrlItem = pTranslationList;
	while(pCurCtrlItem){
	  pCurCtrlItem->pCtrlObject->OdeRValueUpdate();
	  pCurCtrlItem->pCtrlObject->CtrlRValueUpdate();
	  pCurCtrlItem = pCurCtrlItem->pNextCtrlItem;
	}
      }

       // update <rvalues> for all "OdeObject's" and "CtrlObject's" pointed
       // to by this list of "CtrlObject's"
       //(NOTE: This code deprecated as of 6/12/08)
       if(DoTrajectoryUpdate){
	 DoTrajectoryUpdate = 0;
	 pCurCtrlItem = pTrajectoryList;
	 while(pCurCtrlItem){
	   pCurCtrlItem->pCtrlObject->OdeRValueUpdate();
	   pCurCtrlItem->pCtrlObject->CtrlRValueUpdate();
	   pCurCtrlItem = pCurCtrlItem->pNextCtrlItem;
	 } 
       } 
       //(more to follow...)

      //(NOTE: "pTranslationList" and "pTrajectoryList" deprecated above,...
      //        we now use a CTRL group list)
      pCurCtrlGroup = pCtrlGroupList;
      while(pCurCtrlGroup){
	if(pCurCtrlGroup->DoUpdate){
	  pCurCtrlGroup->DoUpdate = 0;
	  pCurCtrlItem = pCurCtrlGroup->pCtrlEquationList;
	  while(pCurCtrlItem){
	    pCurCtrlItem->pCtrlObject->SrcRValueUpdate();
	    pCurCtrlItem->pCtrlObject->OdeRValueUpdate();
	    pCurCtrlItem->pCtrlObject->CoefRValueUpdate();
	    pCurCtrlItem->pCtrlObject->SwitchRValueUpdate();
	    if(!pCurCtrlItem->pCtrlObject->DoImmedCtrlUpdate)
	      pCurCtrlItem->pCtrlObject->CtrlRValueUpdate();
	    pCurCtrlItem = pCurCtrlItem->pNextCtrlItem;
	  }
	}
	pCurCtrlGroup = pCurCtrlGroup->pNextCtrlGroup;
      }



       h = h_start;
      
       DoCtrlUpdate = 0;
    }
  
    

   
 

    // update <rvalues> for all "OdeObject's", "CoefObject's" and so forth  pointed
    // to by this list of "OdeObject's"
    pCurOdeItem = pOdeEquationList;
    while(pCurOdeItem){
      pCurOdeItem->pOdeObject->OdeRValueUpdate();
      pCurOdeItem->pOdeObject->CoefRValueUpdate();
      pCurOdeItem->pOdeObject->SwitchRValueUpdate();
      pCurOdeItem = pCurOdeItem->pNextOdeItem;
    }


    // update <rvalues> for all "OdeObject's" and "SwitchObject's" pointed
    // to by this list of "SpiceObject's"
    pCurSpiceItem = pSpiceEquationList;
    while(pCurSpiceItem){
      pCurSpiceItem->pSpiceObject->OdeRValueUpdate();
      pCurSpiceItem->pSpiceObject->SwitchRValueUpdate();
      pCurSpiceItem->pSpiceObject->CoefRValueUpdate();
      pCurSpiceItem = pCurSpiceItem->pNextSpiceItem;
    }


  
    // update <rvalues> for all "CoefObject's" and "OdeObject's"  pointed
    // to by this list of "SwitchObject's"
    pCurSwitchItem = pSwitchEquationList;
    while(pCurSwitchItem){
      pCurSwitchItem->pSwitchObject->CoefRValueUpdate();
      pCurSwitchItem->pSwitchObject->OdeRValueUpdate();
      pCurSwitchItem = pCurSwitchItem->pNextSwitchItem;
    }





    //update the time for GnuPlot
    if(RecordPlotData)
      Plot_t.push_back(t);    

  }


  //check for SrcObject exceptions.
  pCurSrcItem = pSrcEquationList;
  while(pCurSrcItem && !GotException){
    if(pCurSrcItem->pSrcObject->GotException){
      ExceptionMessageBuffer << "Exception in SrcObject " << pCurSrcItem->pSrcObject->LiteralName << "\n" << 
	"at time " << t << "\n" << "Message: " << pCurSrcItem->pSrcObject->ExceptionMessage << "\n\n\n";
      GotException = TRUE;
      //reset simulation to stop after the point of the exception.
      SimuTime = t + ExceptionTime;
    }
    pCurSrcItem = pCurSrcItem->pNextSrcItem;
  }

  //check for OdeObject exceptions.
  pCurOdeItem = pOdeEquationList;
  while(pCurOdeItem && !GotException){
    if(pCurOdeItem->pOdeObject->GotException){
      ExceptionMessageBuffer << "Exception in OdeObject " << pCurOdeItem->pOdeObject->LiteralName << "\n" << 
	"at time " << t << "\n" << "Message: " << pCurOdeItem->pOdeObject->ExceptionMessage << "\n\n\n";
      GotException = TRUE;
      //reset simulation to stop after the point of the exception.
      SimuTime = t + ExceptionTime; 
    }
    pCurOdeItem = pCurOdeItem->pNextOdeItem;
  }

  //check for CtrlObject exceptions.
  pCurCtrlGroup = pCtrlGroupList;
  while(pCurCtrlGroup && !GotException){
    if(pCurCtrlItem = pCurCtrlGroup->pCtrlEquationList){
      while(pCurCtrlItem){
	if(pCurCtrlItem->pCtrlObject->GotException){
	  ExceptionMessageBuffer << "Exception in CtrlObject " << pCurCtrlItem->pCtrlObject->LiteralName << "\n" << 
	    "at time " << t << "\n" << "Message: " << pCurCtrlItem->pCtrlObject->ExceptionMessage << "\n\n\n";
	  GotException = TRUE;
	  //reset simulation to stop after the point of the exception.
	  SimuTime = t + ExceptionTime;
	  break;
	}
	pCurCtrlItem = pCurCtrlItem->pNextCtrlItem;
      }
    }
    pCurCtrlGroup = pCurCtrlGroup->pNextCtrlGroup;
  }


  //check for SpiceObject exceptions.
  pCurSpiceItem = pSpiceEquationList;
  while(pCurSpiceItem && !GotException){
    if(pCurSpiceItem->pSpiceObject->GotException){
      ExceptionMessageBuffer << "Exception in SpiceObject " << pCurSpiceItem->pSpiceObject->LiteralName << "\n" << 
	"at time " << t << "\n" << "Message: " << pCurSpiceItem->pSpiceObject->ExceptionMessage << "\n\n\n";
      GotException = TRUE;
      //reset simulation to stop after the point of the exception.
      SimuTime = t + ExceptionTime; 
    }
    pCurSpiceItem = pCurSpiceItem->pNextSpiceItem;
  }

  //check for SwitchObject exceptions.
  pCurSwitchItem = pSwitchEquationList;
  while(pCurSwitchItem && !GotException){
    if(pCurSwitchItem->pSwitchObject->GotException){
      ExceptionMessageBuffer << "Exception in SwitchObject " << pCurSwitchItem->pSwitchObject->LiteralName << "\n" << 
	"at time " << t << "\n" << "Message: " << pCurSwitchItem->pSwitchObject->ExceptionMessage << "\n\n\n";
      GotException = TRUE;
      //reset simulation to stop after the point of the exception.
      SimuTime = t + ExceptionTime; 
    }
    pCurSwitchItem = pCurSwitchItem->pNextSwitchItem;
  }


  //check for CoefObject exceptions.
  pCurCoefItem = pCoefEquationList;
  while(pCurCoefItem && !GotException){
    if(pCurCoefItem->pCoefObject->GotException){
      ExceptionMessageBuffer << "Exception in CoefObject " << pCurCoefItem->pCoefObject->LiteralName << "\n" << 
	"at time " << t << "\n" << "Message: " << pCurCoefItem->pCoefObject->ExceptionMessage << "\n\n\n";
      GotException = TRUE;
      //reset simulation to stop after the point of the exception.
      SimuTime = t + ExceptionTime; 
    }
    pCurCoefItem = pCurCoefItem->pNextCoefItem;
  }




    // **** Wait for signal to exit this routine. ****
  return CheckSimuState(t, SimuTime);


}




void SimuObject::PlotSimuResults(string TagNamesToPlot[], double ScaleFactors[], double MinPlotTimes[], double MaxPlotTimes[],
				 double PlotTimeSteps[], double PlotTimeOffset)
{
  SrcObjItem * pCurSrcItem;
  OdeObjItem * pCurOdeItem;
  SwitchObjItem * pCurSwitchItem;
  SpiceObjItem * pCurSpiceItem;
  CtrlObjItem * pCurCtrlItem;
  CtrlObjGroup * pCurCtrlGroup;
  int i;
  bool PlotAll = FALSE;

  //our plotting object
  //(NOTE: For some reason, anytime I place this object in either the "SimuObject" class
  //       or global memory space, the "new" operators called in the OdeObject and CtrlObject
  //       constructors do not initialize the elements OdeObjItem and CtrlObjItem structures to
  //       zero causing segmentation faults do invalid pointers. This is because I am not explicitly 
  //       zeroing these elements. I do not know what the problem is, so I will construct this object
  //       on the stack for now.
  Gnuplot SimuPlot;

  //(Bad way of implementing scale factors,... a better way would mean a complete re-write)
  for(i = 0; i < 20; i++){
    SimuPlot.ScaleFactors[i] = ScaleFactors[i];
    SimuPlot.MinPlotTimes[i] = MinPlotTimes[i];
    SimuPlot.MaxPlotTimes[i] = MaxPlotTimes[i];
    SimuPlot.PlotTimeSteps[i] = PlotTimeSteps[i];
    SimuPlot.TagNames[i] = TagNamesToPlot[i];
  }
  SimuPlot.PlotTimeOffset = PlotTimeOffset;

  SimuPlot.reset_plot();
  SimuPlot.set_style("lines");

  SimuPlot.cmd("set grid");
  
  if(TagNamesToPlot[0] == "")
    PlotAll = TRUE;
    
  //plot "h" parameter if enabled.
  if(Do_h_Plot){
    if(PlotAll){
      SimuPlot.plot_xy(Plot_t, Plot_h, "$h");
    }
    else{
      for(i = 0; i < 20; i++){
	if(TagNamesToPlot[i] == "$h")
	  SimuPlot.plot_xy(Plot_t, Plot_h, "$h");
      }
    }
    if(QuickCheckPlotState())
      return;
  }


  //plot outputs and probes of selected SrcObject's
  pCurSrcItem = pSrcEquationList;
  while(pCurSrcItem){
    if(pCurSrcItem->pSrcObject->PlotThisOutput){
      if(PlotAll){
	SimuPlot.plot_xy(Plot_t, pCurSrcItem->pSrcObject->Plot_y, pCurSrcItem->pSrcObject->Plot_Tag);
      }
      else{
	for(i = 0; i < 20; i++){
	  if(TagNamesToPlot[i] == ""){
	    break;
	  }
	  else if(TagNamesToPlot[i] == pCurSrcItem->pSrcObject->Plot_Tag){
	    SimuPlot.plot_xy(Plot_t, pCurSrcItem->pSrcObject->Plot_y, pCurSrcItem->pSrcObject->Plot_Tag);
	  }
	}
      }
    }
    if(QuickCheckPlotState())
      return;
    if(pCurSrcItem->pSrcObject->DoProbes){
      pCurSrcItem->pSrcObject->PlotProbes(SimuPlot, Plot_t, TagNamesToPlot);
      if(QuickCheckPlotState())
	return;
    }
    pCurSrcItem = pCurSrcItem->pNextSrcItem;
  }


  //plot outputs and probes of selected SwitchObject's 
  //(Switches have no direct output to plot, just probes).
  pCurSwitchItem = pSwitchEquationList;
  while(pCurSwitchItem){
    if(QuickCheckPlotState())
      return;
    if(pCurSwitchItem->pSwitchObject->DoProbes){
      pCurSwitchItem->pSwitchObject->PlotProbes(SimuPlot, Plot_t, TagNamesToPlot);
      if(QuickCheckPlotState())
	return;
    }
    pCurSwitchItem = pCurSwitchItem->pNextSwitchItem;
  }


  //plot outputs and probes of selected SpiceObject's 
  //(Spice objects have no direct output to plot, just probes).
  pCurSpiceItem = pSpiceEquationList;
  while(pCurSpiceItem){
    if(QuickCheckPlotState())
      return;
    if(pCurSpiceItem->pSpiceObject->DoProbes){
      pCurSpiceItem->pSpiceObject->PlotProbes(SimuPlot, Plot_t, TagNamesToPlot);
      if(QuickCheckPlotState())
	return;
    }
    pCurSpiceItem = pCurSpiceItem->pNextSpiceItem;
  }



  //plot outputs and probes of selected OdeObject's
  pCurOdeItem = pOdeEquationList;
  while(pCurOdeItem){
    if(pCurOdeItem->pOdeObject->PlotThisOutput){
      if(PlotAll){
	SimuPlot.plot_xy(Plot_t, pCurOdeItem->pOdeObject->Plot_y, pCurOdeItem->pOdeObject->Plot_Tag);
      }
      else{
	for(i = 0; i < 20; i++){
	  if(TagNamesToPlot[i] == ""){
	    break;
	  }
	  else if(TagNamesToPlot[i] == pCurOdeItem->pOdeObject->Plot_Tag){
	    SimuPlot.plot_xy(Plot_t, pCurOdeItem->pOdeObject->Plot_y, pCurOdeItem->pOdeObject->Plot_Tag);
	  }
	}
      }
    }
    if(QuickCheckPlotState())
      return;
    if(pCurOdeItem->pOdeObject->DoProbes){
      pCurOdeItem->pOdeObject->PlotProbes(SimuPlot, Plot_t, TagNamesToPlot);
      if(QuickCheckPlotState())
	return;
    }
    pCurOdeItem = pCurOdeItem->pNextOdeItem;
  }

  //plot output probes of selected CtrlObject's
  //(NOTE: This code deprecated as of 6/12/08)
  if(pCurCtrlItem = pTranslationList){
    //need to update Translation time once more.
    if(RecordPlotData)
      Plot_t_Translation.push_back(t);
    while(pCurCtrlItem){
      if(pCurCtrlItem->pCtrlObject->PlotThisOutput){
	//before we plot, record our last state.
	if(RecordPlotData)
	  pCurCtrlItem->pCtrlObject->Plot_y.push_back(pCurCtrlItem->pCtrlObject->y);
	if(PlotAll){
	  SimuPlot.plot_xy(Plot_t_Translation, pCurCtrlItem->pCtrlObject->Plot_y, pCurCtrlItem->pCtrlObject->Plot_Tag);
	}
	else{
	  for(i = 0; i < 20; i++){
	    if(TagNamesToPlot[i] == ""){
	      break;
	    }
	    else if(TagNamesToPlot[i] == pCurCtrlItem->pCtrlObject->Plot_Tag){
	      SimuPlot.plot_xy(Plot_t_Translation, pCurCtrlItem->pCtrlObject->Plot_y, pCurCtrlItem->pCtrlObject->Plot_Tag);
	    }
	  }
	}
      }
      if(QuickCheckPlotState())
	return;
      if(pCurCtrlItem->pCtrlObject->DoProbes){
	//before we plot, record the last state(s);
	if(RecordPlotData)
	   pCurCtrlItem->pCtrlObject->RecordProbes();
	pCurCtrlItem->pCtrlObject->PlotProbes(SimuPlot, Plot_t_Translation, TagNamesToPlot);
	if(QuickCheckPlotState())
	  return;	
      }
      pCurCtrlItem = pCurCtrlItem->pNextCtrlItem;
    }
  }
  //(NOTE: This code deprecated as of 6/12/08)
  if(pCurCtrlItem = pTrajectoryList){
    //need to update Trajectory time once more.
    if(RecordPlotData)
      Plot_t_Trajectory.push_back(t);
    while(pCurCtrlItem){
      if(pCurCtrlItem->pCtrlObject->PlotThisOutput){
	//before we plot, record our last state.
	if(RecordPlotData)
	  pCurCtrlItem->pCtrlObject->Plot_y.push_back(pCurCtrlItem->pCtrlObject->y);
	if(PlotAll){
	  SimuPlot.plot_xy(Plot_t_Trajectory, pCurCtrlItem->pCtrlObject->Plot_y, pCurCtrlItem->pCtrlObject->Plot_Tag);
	}
	else{
	  for(i = 0; i < 20; i++){
	    if(TagNamesToPlot[i] == ""){
	      break;
	    }
	    else if(TagNamesToPlot[i] == pCurCtrlItem->pCtrlObject->Plot_Tag){
	      SimuPlot.plot_xy(Plot_t_Trajectory, pCurCtrlItem->pCtrlObject->Plot_y, pCurCtrlItem->pCtrlObject->Plot_Tag);
	    }
	  }
	}
      }
      if(QuickCheckPlotState())
	return;
      if(pCurCtrlItem->pCtrlObject->DoProbes){
	//before we plot, record the last state(s);
	if(RecordPlotData)
	  pCurCtrlItem->pCtrlObject->RecordProbes();
	pCurCtrlItem->pCtrlObject->PlotProbes(SimuPlot, Plot_t_Trajectory, TagNamesToPlot);
	if(QuickCheckPlotState())
	  return;
      }
      pCurCtrlItem = pCurCtrlItem->pNextCtrlItem;
    } 
  }

  //(NOTE: "pTranslationList" and "pTrajectoryList" deprecated above,...
  //        we now use a CTRL group list)
  pCurCtrlGroup = pCtrlGroupList;
  while(pCurCtrlGroup){
    if(pCurCtrlItem = pCurCtrlGroup->pCtrlEquationList){
      //need to update time once more.
      if(RecordPlotData)
	pCurCtrlGroup->Plot_t.push_back(t);
      while(pCurCtrlItem){
	if(pCurCtrlItem->pCtrlObject->PlotThisOutput){
	  //before we plot, record our last state.
	  if(RecordPlotData)
	    pCurCtrlItem->pCtrlObject->Plot_y.push_back(pCurCtrlItem->pCtrlObject->y);
	  if(PlotAll){
	    SimuPlot.plot_xy(pCurCtrlGroup->Plot_t, pCurCtrlItem->pCtrlObject->Plot_y, pCurCtrlItem->pCtrlObject->Plot_Tag);
	  }
	  else{
	    for(i = 0; i < 20; i++){
	      if(TagNamesToPlot[i] == ""){
		break;
	      }
	      else if(TagNamesToPlot[i] == pCurCtrlItem->pCtrlObject->Plot_Tag){
		SimuPlot.plot_xy(pCurCtrlGroup->Plot_t, pCurCtrlItem->pCtrlObject->Plot_y, pCurCtrlItem->pCtrlObject->Plot_Tag);
	      }
	    }
	  }
	}
	if(QuickCheckPlotState())
	  return;
	if(pCurCtrlItem->pCtrlObject->DoProbes){
	  //before we plot, record the last state(s);
	  if(RecordPlotData)
	    pCurCtrlItem->pCtrlObject->RecordProbes();
	  pCurCtrlItem->pCtrlObject->PlotProbes(SimuPlot, pCurCtrlGroup->Plot_t, TagNamesToPlot);
	  if(QuickCheckPlotState())
	    return;	
	}
	pCurCtrlItem = pCurCtrlItem->pNextCtrlItem;
      }
    }
    pCurCtrlGroup = pCurCtrlGroup->pNextCtrlGroup;
  }

  // **** Wait for signal to exit this routine. ****
   
  CheckPlotState();
  
 

}


void SimuObject::UpdateCtrlEquations(CtrlObjItem * pCurCtrlItem)
{
  while(pCurCtrlItem){
    //update this Ctrl's output to GnuPlot buffer if so indicated.
    if(pCurCtrlItem->pCtrlObject->PlotThisOutput && RecordPlotData){
      //the "before" update state.
      pCurCtrlItem->pCtrlObject->Plot_y.push_back(pCurCtrlItem->pCtrlObject->y);
    }
    //update this Ctrl's probes if so indicated
    if(pCurCtrlItem->pCtrlObject->DoProbes && RecordPlotData){
      //the "before" update state
      pCurCtrlItem->pCtrlObject->RecordProbes();
    }
    pCurCtrlItem->pCtrlObject->CtrlFunction(t);     
    if(pCurCtrlItem->pCtrlObject->PlotThisOutput && RecordPlotData){
      //the "after" update state.
      pCurCtrlItem->pCtrlObject->Plot_y.push_back(pCurCtrlItem->pCtrlObject->y);
    }
    if(pCurCtrlItem->pCtrlObject->DoProbes && RecordPlotData){
      //the "after" update state
      pCurCtrlItem->pCtrlObject->RecordProbes();
    }    //execute the CTRL R-value update here if so indicated. 
    if(pCurCtrlItem->pCtrlObject->DoImmedCtrlUpdate)
      pCurCtrlItem->pCtrlObject->CtrlRValueUpdate();
    

    pCurCtrlItem = pCurCtrlItem->pNextCtrlItem;
  }
}











#endif 
