/*
 * SIMULINK.C - Include file for compiling MEX-file systems and blocks
 *
 * This file should be included at the end of a MEX-file system.  It
 * provides an interface to the MEX-file mechanism that allows blocks
 * and systems to be entered only as their corresponding mathematical
 * functions without the need for addition interface code
 * 
 *     Syntax  of MEX-file S-function:
 *
 *                [sys, x0] = filename(t, x, u, flag)
 *
 * Andrew Grace  Dec 1st, 1992
 * Revised ACWG 1-10-91
 * Revised RAS 12-17-92
 *
 * Copyright (c) 1993 The Mathworks, Inc.
 * All Rights Reserved
 */


#include <stdio.h>   /* needed for sprintf() prototyping */
#include <math.h>    /* needed for floor() prototyping   */

#ifdef __STDC__      /* Get DBL_MAX */
#include <float.h>
#else
#define DBL_MAX 1e30  /* play it safe, a double may be of single precision */
#endif

#include "matrix.h"  /* needed for prototyping of access functions */
#include "mex.h"

#if defined(applec) || defined(THINK_C) || defined(powerc) || defined(VMS)
#include "simstruc.c"
#else
#include "../src/simstruc.c"
#endif

#ifdef DLLMEX
/* If compiled as a Windows 16 Bit DLL MEX-file we can't do the 
 * normal optimization of storing the SimStruct so set the sizes
 * vector to 6 which tells SIMULINK not to do the optimizations.
 */
#undef SIZES_LENGTH
#define SIZES_LENGTH 6
#endif

#ifndef NULL
#define NULL 0L
#endif

#ifdef DEBUG
/*
 * db_matrix - debug routine for displaying the contents of a matrix
 */
static
#ifdef __STDC__
void db_matrix(char *name, Matrix *mat)
#else
void db_matrix(name, mat)
    char *name;
    Matrix *mat;
#endif /* __STDC__ */
{
    int i, j, n;
    Real *pr;
    
    mexPrintf("%s =\n", name);
    pr = mxGetPr(mat);
    n = mxGetN(mat);
    for (i = mxGetM(mat); i > 0; i--) {
	for (j = n; j > 0; j--) {
	    mexPrintf("%8.6g ", *pr++);
	}
	mexPrintf("\n");
    }
}


/* db_vector - debug routine for displaying the contents of a vector */
static
#ifdef __STDC__	
void db_vector(char *name, double* vec, int n)
#else
void db_vector(name, vec,n)
    char *name;
    double *vec;
    int n;
#endif /* __STDC__ */
{
    mexPrintf("%s = [", name);
    for (; n > 0; n--) {
	mexPrintf("%6.6g ",*vec++);
    }
    mexPrintf("]\n");
}

#define db_whereami()	mexPrintf("@%d\n", __LINE__)

#else	/* !defined (DEBUG) */
#define db_matrix(name,mat)
#define db_vector(name,vec,n)
#define db_whereami()
#endif


/*
 * next_hit_ftn - 
 */
static
#ifdef __STDC__
    double next_hit_ftn(double ts, double offset, double t)
#else
double next_hit_ftn(ts, offset, t)
    double ts, offset, t;
#endif /* __STDC__ */
{
    double numsamples, tnext;
    
    if (ts == 0.0) {
	tnext = DBL_MAX;
    } else {
	numsamples = (t - offset)/ts;
	tnext = offset + (1.0 + floor(numsamples + 1e-13*(1.0 + numsamples))) * ts;
    }

    return tnext;
}

/*
 * compute_next_sample_hit - computes next sample hits for sampled data blocks.
 */
static
#ifdef __STDC__
double compute_next_sample_hit(SimStruct *S)
#else
double compute_next_sample_hit(S)
    SimStruct *S;
#endif
{
    double *SampleTimes = ssGetSampleTimeEventPtr(S);
    double *OffsetTimes = ssGetOffsetTimeEventPtr(S);
    double *NextTimes = ssGetNextTimeEventPtr(S);
    double *PresentTimes = ssGetPresentTimeEventPtr(S);
    int numSampleTimes = ssGetNumSampleTimes(S);
    double t = ssGetT(S);
    
    int i;
    int first_discrete_index;
    double tol = 1e-8;
    double nearest_next_hit = DBL_MAX;
    double b_next;

    if ((numSampleTimes == 0) || ((numSampleTimes == 1) && (SampleTimes[0] == 0.0))) {
	return(nearest_next_hit);
    } else if (SampleTimes[0] == 0.0) {
	first_discrete_index = 1;
    } else {
	first_discrete_index = 0;
    }

    tol *= SampleTimes[first_discrete_index];
    
    /*
     * Loop through sample times working out when the next sample hit is for
     * all of the sample times.  Store this in the vector 'NextTimes' and return
     * the nearest one to the present time, t.
     */

    for (i = first_discrete_index; i < numSampleTimes; i++) {

	/*
	 * On the first iteration the sample times are set to -DBL_MAX
	 */

	if (PresentTimes[i] == -DBL_MAX) {
	    NextTimes[i] =
	      next_hit_ftn(SampleTimes[i], OffsetTimes[i], t - SampleTimes[i]);
	}

	/*
	 * Next scheduled sample hit is stored in next_hit
	 */

	b_next = NextTimes[i];
	
	/*
	 * If the next hit has been overtaken then calculate another one
	 */

	if (b_next <= t + tol) {
	    PresentTimes[i] = b_next;
	    b_next = next_hit_ftn(SampleTimes[i], OffsetTimes[i], t);
	}
	
	/*
	 * Calculate nearest hit to the present time
	 */

	if (fabs(nearest_next_hit - b_next) < tol) {
	    /*
	     * Attempt to correct sample clash problem for clashing sample hits
	     * i.e. set near sample hits to the same time
	     */
	    b_next = nearest_next_hit;
	} else if (b_next < nearest_next_hit) {
	    nearest_next_hit = b_next;
	}
	
	NextTimes[i] = b_next;
    }

    return(nearest_next_hit);
}

/*
 * sample_hit_set - set the sample hit event flags as appropriate
 */
static
#ifdef __STDC__
void sample_hit_set(SimStruct *S)
#else
void sample_hit_set(S)
    SimStruct *S;
#endif /* __STDC__ */
{
    int numSampleTimes = ssGetNumSampleTimes(S);
    double t = ssGetT(S);
    int i;
    int fdi;
    double st, ot;
    double tol;

    if ((numSampleTimes == 0) || ssIsMinorTimeStep(S)) {
	return;
    }

    if ((fdi = ssGetSampleTimeEvent(S,0) == 0.0) < numSampleTimes) {
        st = ssGetSampleTimeEvent(S, fdi);
        ot = ssGetOffsetTimeEvent(S, fdi);
        tol = st * 1e-6/100;
    }

#ifndef SIMULINK_NAME
    /*
     * Don't do the following check if we have an accelerated model.
     */
    if (numSampleTimes > 1) {
	int all_the_same = 1;
	for (i = fdi + 1; i < numSampleTimes; i++) {
	    double ratio = ssGetSampleTimeEvent(S, i)/st;
	    if ((fabs(ot - ssGetOffsetTimeEvent(S, i)) > tol) ||
		ratio - floor(ratio) > ratio * 1e-15) {
		all_the_same = 0;
		break;
	    }
	}
	if ((!all_the_same) || (fdi == 1)) {
	    /*
	     * Make sure that PresentTimes get updated for purely
	     * discrete multirate S-Functions.
	     */
	    compute_next_sample_hit(S);
	}
    }
#endif

    for (i = fdi; i < numSampleTimes; i++) {
	if (fabs(t - ssGetPresentTimeEvent(S, i)) < tol) {
	    ssSetSampleHitEvent(S, i, 1);
	} else {
	    ssSetSampleHitEvent(S, i, 0);
	}
    }
}

/*
 * sample_hit_reset - reset the sample hit event flags to zero
 */
static
#ifdef __STDC__
void sample_hit_reset(SimStruct *S)
#else
void sample_hit_reset(S)
    SimStruct *S;
#endif /* __STDC__ */
{
    int i;
    int numSampleTimes = ssGetNumSampleTimes(S);
    
    for (i = 0; i < numSampleTimes; i++) {
	ssSetSampleHitEvent(S, i, 0);
    }
}


          /*******************************************************/
          /*         M E X - F I L E   I N T E R F A C E         */
          /*******************************************************/

/* Input arguments */

#define T_IN    prhs[0]	   /* time */
#define X_IN    prhs[1]    /* states */
#define U_IN    prhs[2]    /* inputs */
#define FLAG_IN prhs[3]    /* flag */

#define SS_IN	plhs[2]    /* SimStruct * */

/* Output Arguments */

#define SYS_OUT plhs[0]    /* return (sizes, derivs, discrete states,
			      output, next time hit) */
#define X0_OUT  plhs[1]    /* initial conditions */
#define X_OUT   plhs[2]    /* state strings */
#define TS_OUT  plhs[3]    /* sample times */

/* Useful macros */

#define NUMEL(m)    (mxGetM(m) * mxGetN(m))     /* num elements in matrix */
#ifndef ABS
#define ABS(x)      (((x) >= 0) ? (x) : -(x))
#endif

#ifdef __STDC__
void mexFunction(int nlhs, Matrix *plhs[], int nrhs, Matrix *prhs[])
#else
mexFunction(nlhs, plhs, nrhs, prhs)
    int nlhs;
    Matrix *plhs[];
    int nrhs;
    Matrix *prhs[];
#endif /* __STDC__ */
{
    int flag, abs_flag, numSampleTimes;
    int i;
    Real *pr;
    SimStruct *S;
    char err_msg[256];
    
    /* Check validity of arguments */
    
    if (nrhs >= 4) {
	
	/* Load coefficients into global matrix structure */
	
	/* Negative nlhs is a special case used for optimum speed by integrators */
	if (nlhs >= 0) {
	    int U_Length;
	    
	    /* initialize the sizes array */
	    S = ssCreateSimStruct(NULL);
	    
	    ssSetArgPtr(S, (Matrix **) mxCalloc(nrhs - 4, sizeof(Matrix *)));
	    ssSetNumArgs(S, nrhs - 4);
	    for (i = 0; i < nrhs - 4; i++)
		ssSetArg(S, i, prhs[4+i]);
	    
	    mdlInitializeSizes(S);

	    /* If extra right arguments are passed in as parameters: */
	    if (ssGetNumInputArgs(S) != (nrhs - 4)) {
		    sprintf(err_msg, "Wrong number of input arguments passed to S-function MEX-file.\n\
%d input arguments were passed in when expecting %d input arguments.\n", nrhs, ssGetNumInputArgs(S) + 4);
		    mexErrMsgTxt(err_msg);
	    }
	    
	    /* If there are sample times */
	    numSampleTimes = ssGetNumSampleTimes(S);
	    if (numSampleTimes) {
		ssSetSampleTimeEventPtr( S, (double *) mxCalloc(numSampleTimes, sizeof(double)));
		ssSetOffsetTimeEventPtr( S, (double *) mxCalloc(numSampleTimes, sizeof(double)));
		ssSetNextTimeEventPtr(   S, (double *) mxCalloc(numSampleTimes, sizeof(double)));
		ssSetPresentTimeEventPtr(S, (double *) mxCalloc(numSampleTimes, sizeof(double)));
		ssSetSampleHitEventPtr(  S, (int *)    mxCalloc(numSampleTimes, sizeof(int)));
		mdlInitializeSampleTimes(S);
	    }
	    
	    /* set up the vectors */
	    U_Length = NUMEL(U_IN);
	    if (ssGetNumRWork(S) >= 0)
		ssSetRWork(     S, (double *) mxCalloc(ssGetNumRWork(S),       sizeof(double)));
	    else
		ssSetRWork(     S, (double *) mxCalloc(U_Length,               sizeof(double)));
	    if (ssGetNumIWork(S) >= 0)
		ssSetIWork(     S, (int *)    mxCalloc(ssGetNumIWork(S),       sizeof(int)));
	    else
		ssSetIWork(     S, (int *)    mxCalloc(U_Length,               sizeof(int)));
	    if (ssGetNumPWork(S) >= 0)
		ssSetPWork(     S, (void **)  mxCalloc(ssGetNumPWork(S),       sizeof(void *)));
	    else
		ssSetPWork(     S, (void **)  mxCalloc(U_Length,               sizeof(void *)));

	    ssSetBlockIO(       S, (double *) mxCalloc(ssGetNumBlockIO(S),     sizeof(double)));

            /* If there is a BlockIO the Y vector is needed for extra calls to mdlOutputs */
	    if (ssGetNumOutputs(S) >= 0)
	    	ssSetY(		S, (double *) mxCalloc(ssGetNumOutputs(S),     sizeof(double)));
	    else 
	    	ssSetY(		S, (double *) mxCalloc(U_Length,     sizeof(double)));

	    ssSetBlockParam(    S, (double *) mxCalloc(ssGetNumBlockParams(S), sizeof(double)));
	    
	    /* grab the flag from the input args */
	    if (NUMEL(FLAG_IN) != 1)
		mexErrMsgTxt("FLAG must be a scalar in System MEX file.");
	    
	    flag = mxGetPr(FLAG_IN)[0];
	    
	    /*
	     * Error checking - ommitted for speed  by integrators after a number of calls
	     */
	    abs_flag = ABS(flag);
	    if ((abs_flag > 0) && (abs_flag < 6)) {

		/*
		 * set wild-carded sizes to an appropriate value
		 */

		if (ssGetNumInputs(S) < 0)     ssSetNumInputs(    S, U_Length);
		if (ssGetNumOutputs(S) < 0)    ssSetNumOutputs(   S, U_Length);
		if (ssGetNumContStates(S) < 0) ssSetNumContStates(S, U_Length);
		if (ssGetNumDiscStates(S) < 0) ssSetNumDiscStates(S, U_Length);
		if (ssGetNumRWork(S) < 0)      ssSetNumRWork(     S, U_Length);
		if (ssGetNumIWork(S) < 0)      ssSetNumIWork(     S, U_Length);
		if (ssGetNumPWork(S) < 0)      ssSetNumPWork(     S, U_Length);

		/*
		 * now check that inputs are the appropriate size
		 */
		
		if (nlhs > 1)
		    mexErrMsgTxt("System MEX file called with wrong number of output arguments. ");
		
		if (NUMEL(X_IN) != ssGetNumTotalStates(S))
		    mexErrMsgTxt("State vector X wrong size in System MEX file.");
		
		if (NUMEL(U_IN) != ssGetNumInputs(S))
		    mexErrMsgTxt("Input vector U wrong size in System MEX file.");
		
		if (NUMEL(T_IN) != 1)
		    mexErrMsgTxt("T must be a scalar in System MEX file.");
	    }
	    
	    switch (flag) {
	      case 0:
		SYS_OUT = mxCreateFull(SIZES_LENGTH, 1, REAL);
		break;
	      case 1:
	      case -1:
		SYS_OUT = mxCreateFull(ssGetNumContStates(S), 1, REAL);
		break;
	      case 2:
	      case -2:
		SYS_OUT = mxCreateFull(ssGetNumDiscStates(S), 1, REAL);
		break;
	      case 3:
		SYS_OUT = mxCreateFull(ssGetNumOutputs(S), 1, REAL);
		break;
	      case 4:
		SYS_OUT = mxCreateFull(1, 1, REAL);
		break;
	      default:
		SYS_OUT = mxCreateFull(0, 0, REAL);
		break;
	    }
	} else {
	    
	    flag = mxGetPr(FLAG_IN)[0];
	    
	    /*
	     * nlhs < 0 indicates that the SimStruct is passed in on the LHS
	     * and has come in preallocated from SIMULINK
	     */
	    
	    S = (SimStruct *) mxGetPr(SS_IN);
	    numSampleTimes = ssGetNumSampleTimes(S);
	    
	    db_vector("B", ssGetBlockIO(S), ssGetNumBlockIO(S));
	    db_vector("P", ssGetBlockParam(S), ssGetNumBlockParams(S));
	}

	if ((flag != 0) && (flag != 9))
	    ssSetT(S, *mxGetPr(T_IN));
	
	db_vector("T",mxGetPr(T_IN), NUMEL(T_IN));
	db_vector("X",mxGetPr(X_IN), NUMEL(X_IN));
	db_vector("U",mxGetPr(U_IN), NUMEL(U_IN));
	db_vector("FLAG",mxGetPr(FLAG_IN), NUMEL(FLAG_IN));
	
	switch (flag) {
	    
	    /*
	     * FLAG = 1: return state derivatives dx/dt
	     */
	    
	  case 1:
	    /* Only make the extra call to the mdlOutputs function if there exists 
	     * a block vector as we need to update the B vector before calling 
	     * the mdlDerivatives function.
             */
	     if (ssGetNumBlockIO(S)) {
	        mdlOutputs(ssGetY(S), mxGetPr(X_IN), mxGetPr(U_IN), S, 0);
		db_vector("B", ssGetBlockIO(S), ssGetNumBlockIO(S));
	     }
	  case -1:
	    mdlDerivatives(mxGetPr(SYS_OUT), mxGetPr(X_IN), mxGetPr(U_IN), S, 0);
	    db_vector("Derivatives",mxGetPr(SYS_OUT), ssGetNumContStates(S));
	    break;
	    
	    /*
	     * FLAG = 2: return discrete states x(n+1)
	     */
	    
	  case 2:
	    /* Set the integer array for sample hit set on sample hit */
	    sample_hit_set(S);

	    /* Only make the extra call to the mdlOutputs function if there exists 
	     * a block vector as we need to update the B vector before calling 
	     * the mdlUpdate function.
             */
	     if (ssGetNumBlockIO(S)) {
	         mdlOutputs(ssGetY(S), mxGetPr(X_IN), mxGetPr(U_IN), S, 0);
	         db_vector("B", ssGetBlockIO(S), ssGetNumBlockIO(S));
	     }
	  case -2: {
	      int numContStates, numDiscStates;
	      
	      numContStates = ssGetNumContStates(S);
	      numDiscStates = ssGetNumDiscStates(S);
	      mdlUpdate(mxGetPr(X_IN), mxGetPr(U_IN), S, 0);
	      memcpy((char *)mxGetPr(SYS_OUT), (char *)&mxGetPr(X_IN)[numContStates], numDiscStates*sizeof(double));
	      db_vector("Dstates", &mxGetPr(X_IN)[numContStates], numDiscStates);
	      
	      /*
	       * Reset the sample hit event flags so that future calls to
	       * the derivatives function do not execute code associated
	       * with sample hits.
	       */
	      sample_hit_reset(S);
	      break;
	  }
	    
	    /*
	     * FLAG = 3: return the output vector y
	     */
	    
	  case 3:
	    /* Set the integer array for sample hit set on sample hit */
	    sample_hit_set(S);
	    mdlOutputs(mxGetPr(SYS_OUT), mxGetPr(X_IN), mxGetPr(U_IN), S, 0);
	    db_vector("B", ssGetBlockIO(S), ssGetNumBlockIO(S));
	    db_vector("Outputs", mxGetPr(SYS_OUT), ssGetNumOutputs(S));
	    break;
	    
	    /*
	     * FLAG = 4: return the next time interval for a discrete update
	     */
	    
	  case 4:
	    mxGetPr(SYS_OUT)[0] = compute_next_sample_hit(S); 
	    db_vector("Tnext",mxGetPr(SYS_OUT), 1);
	    break;
	    
	    /*
	     * FLAG = 5: not implemented
	     */
	    
	  case 5:
	    mexPrintf("Flag 5 is not implemented yet for S-functions\n");
	    break;
	    
	    /*
	     * FLAG = 0: return sizes of parameters and initial conditions
	     */
	    
	  case 0: {
	      int abs_nlhs;
	      
	      /*
	       * need to take the absolute value of nlhs since it may be negative
	       */
	      
	      abs_nlhs = ABS(nlhs);

#ifdef DLLMEX
	      if (ssGetNumRWork(S) != 0 || ssGetNumIWork(S) != 0 || ssGetNumPWork(S) != 0 || !((numSampleTimes == 0) || ((numSampleTimes == 1) && (ssGetSampleTimeEventPtr(S)[0] == 0.0)))) {
		   mexErrMsgTxt("\nDLL MEX-files only work for continuous time systems with no work vectors.\nYou can resolve this problem by using Watcom or Metaware compilers.\n\n");
	      }
#endif
		
                        
		
			
	      	      
	      /* init the block outputs to 0.0 */
	      for (i = 0; i < ssGetNumBlockIO(S); i++)
			  ssSetBlockIOValue(S, i, 0.0);
	
	      /* get the initial conditions if they are requested */
	      if (abs_nlhs >= 2) {

		  /*
		   * If the nhls >= 0, then the initial conditions cannot be
		   * computed since the SimStruc, S, is an artificial one
		   * computed above, and the BlockParams vector is artificial
		   * as well.  The only time when the initial conditions can
		   * be computed is when nlhs < 0, implying that there is a
		   * valid SimStruc passed in by SIMULINK, or when the number
		   * of block params is zero, implying that the initial
		   * conditions aren't a function of any block parameters.
		   */
		  if ((nlhs < 0) || (ssGetNumBlockParams(S) == 0)) {
		      
		      /*
		       * if the number of states (continous or discrete) are
		       * set to auto then SIMULINK expects only a scalar back.
		       */
		      
		      if ((ssGetNumContStates(S) == -1) || (ssGetNumDiscStates(S) == -1))
			  X0_OUT = mxCreateFull(1, 1, REAL);
		      else
			  X0_OUT = mxCreateFull(ssGetNumTotalStates(S), 1, REAL);
		      
		      /*
		       * if the number of work vectors is set to auto,
		       * allocate a single double so that
		       * mdlInitializeConditions has something to write to.
		       * Otherwise, if the nlhs is greater than zero,
		       * allocate a vector the size of the number of work
		       * slots.  This is only necessary for nlhs >= 0, in
		       * the case when nlhs < 0, SIMULINK has already
		       * allocated the vector
		       */
		      if (ssGetNumRWork(S) == -1) {
			  ssSetRWork(S, (double *) mxCalloc(1, sizeof(double)));
		      } else if (nlhs >= 0) {
			  ssSetRWork(S, (double *) mxCalloc(ssGetNumRWork(S), sizeof(double)));
		      }
		      
		      if (ssGetNumIWork(S) == -1) {
			  ssSetIWork(S, (int *) mxCalloc(1, sizeof(int)));
		      } else if (nlhs >= 0) {
			  ssSetIWork(S, (int *) mxCalloc(ssGetNumIWork(S), sizeof(int)));
		      }
		      
		      if (ssGetNumPWork(S) == -1) {
			  ssSetPWork(S, (void **) mxCalloc(1, sizeof(void *)));
		      } else if (nlhs >= 0) {
			  ssSetPWork(S, (void **) mxCalloc(ssGetNumPWork(S), sizeof(void *)));
		      }
		      
		      mdlInitializeConditions(mxGetPr(X0_OUT), S);
		      
		      /*
		       * if nlhs > 0, then call mdlTerminate, so that
		       * the S-Function can clean up after any allocations
		       * made in mdlInitializeConditions
		       */
		      
		      if (nlhs >= 0)
			  mdlTerminate(S);
		  } else {
		      X0_OUT = mxCreateFull(0, 0, REAL);
		  }
	      }
		  
	      /* create sizes matrix and copy them over */
	      if (nlhs >= 0) {
		  SYS_OUT = mxCreateFull(SIZES_LENGTH, 1, REAL);
		  for (i = 0; i < SIZES_LENGTH; i++)
		      mxGetPr(SYS_OUT)[i] = ssGetSizes(S)[i];
	      }
	      
	      /* first time through, initialize the sample hits to -DBL_MAX */
	      for (i = 0; i < numSampleTimes; i++) {
		  ssSetPresentTimeEvent(S, i, -DBL_MAX);
		  ssSetSampleHitEvent(S, i, 0);
	      }
      
	      /* Set state strings to empty matrices */
	      if ((abs_nlhs > 2) && (nlhs > 0))
		  X_OUT = mxCreateFull(0, 0, REAL);
	      
	      /* return the sample and offset times */
	      if (abs_nlhs > 3) {
		  
		  mdlInitializeSampleTimes(S);
		  if (nlhs > 0) {
		      if (numSampleTimes > 0) {
			  TS_OUT = mxCreateFull(numSampleTimes, 2, REAL);
			  pr = mxGetPr(TS_OUT);
			  memcpy((char *) &pr[0], (char *) ssGetSampleTimeEventPtr(S), sizeof(double) * numSampleTimes);
			  memcpy((char *) &pr[numSampleTimes], (char *) ssGetOffsetTimeEventPtr(S), sizeof(double) * numSampleTimes);
		      } else if (nlhs > 0) {
			  TS_OUT = mxCreateFull(0, 0, REAL);
		      }
		  }
	      }
	      
	      /* Set other left hand sides to empty matrices */
	      for (i = 4; i < nlhs; i++)
		  plhs[i]  = mxCreateFull(0, 0, REAL);
	      
	      break;
	  }
	    
	  case 9:
	    mdlTerminate(S);
	    break;
	    
	    /* FLAG = unknown: return error */
	  default:
	    mexPrintf("Not a valid flag number for MEX System.\n");
	    break;
	}
    } else if (nrhs == 0) {
#ifdef SIMULINK_NAME
	/*
	 * If no left or right hand arguments then try to load system 
	 * which will be in an M-file. The SIMULINK command open_system 
	 * has been tailored to only load M-files. This code is for 
	 * auto-generated models from SIMULINK.
	 */
	
	if (nlhs == 0 && nrhs == 0) {
	    Matrix *model_prhs[1];
	    
	    model_prhs[0] = mxCreateString(SIMULINK_NAME);
	    mexCallMATLAB(0, plhs, 1, model_prhs, "open_system");
	    mxFreeMatrix(model_prhs[0]);
	    SYS_OUT = NULL; /* Don't return any matrices */
	    return;
	} else {
	    mexErrMsgTxt("System MEX file called with wrong number of right hand arguments.");
	}
#else
	mexErrMsgTxt("System MEX file must be called with at least 4 right hand arguments.");
#endif /* SIMULINK */
    } else {
	mexErrMsgTxt("System MEX file called with wrong number of right hand arguments.");
    }
}


