/*
 * SDOTPRODUCT S-Function to compute dot product (multiply-accumulate)
 *      of two real or complex vectors
 *
 *  D. Orofino, 12-97
 *  D. Boghiu,  03-98
 *  Copyright 1990-2000 The MathWorks, Inc.
 *  $Revision: 1.13 $  $Date: 2000/03/02 22:36:58 $
 */

#define S_FUNCTION_NAME sdotproduct
#define S_FUNCTION_LEVEL 2

#include "simstruc.h"

#define NUM_PARAMS (0)

static void mdlInitializeSizes(SimStruct *S)
{
    ssSetNumSFcnParams(S,  NUM_PARAMS);
    if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) return;

    /* Inputs: */
    if (!ssSetNumInputPorts(S, 2)) return;
    if(!ssSetInputPortDimensionInfo(S, 0, DYNAMIC_DIMENSION)) return;
    if(!ssSetInputPortDimensionInfo(S, 1, DYNAMIC_DIMENSION)) return;

    ssSetInputPortFrameData(S, 0, FRAME_INHERITED);
    ssSetInputPortFrameData(S, 1, FRAME_INHERITED);

    ssSetInputPortComplexSignal(S, 0, COMPLEX_INHERITED);
    ssSetInputPortComplexSignal(S, 1, COMPLEX_INHERITED);

    ssSetInputPortDirectFeedThrough(S, 0, 1);
    ssSetInputPortDirectFeedThrough(S, 1, 1);

    ssSetInputPortReusable(S, 0, 1);
    ssSetInputPortReusable(S, 1, 1);

    ssSetInputPortOverWritable(S, 0, 1);
    ssSetInputPortOverWritable(S, 1, 1);

    /* Outputs: */
    if (!ssSetNumOutputPorts(S, 1)) return;
    if(!ssSetOutputPortVectorDimension(S, 0, 1)) return;

    ssSetOutputPortFrameData(S, 0, FRAME_NO);

    ssSetOutputPortComplexSignal(S, 0, COMPLEX_INHERITED);

    ssSetOutputPortReusable(S, 0, 1);

    ssSetNumSampleTimes(S, 1);
    ssSetOptions(S, SS_OPTION_EXCEPTION_FREE_CODE |
                 SS_OPTION_USE_TLC_WITH_ACCELERATOR);
}


static void mdlInitializeSampleTimes(SimStruct *S)
{
    ssSetSampleTime(S, 0, INHERITED_SAMPLE_TIME);
    ssSetOffsetTime(S, 0, 0.0);
}


static void mdlOutputs(SimStruct *S, int_T tid)
{
    /* 
     * detect which inputs are complex or not
     */
    boolean_T u0IsComplex = ssGetInputPortComplexSignal(S, 0) == COMPLEX_YES;
    boolean_T u1IsComplex = ssGetInputPortComplexSignal(S, 1) == COMPLEX_YES;
    boolean_T yIsComplex  = ssGetOutputPortComplexSignal(S, 0)== COMPLEX_YES;

    InputRealPtrsType u0Ptr  = ssGetInputPortRealSignalPtrs(S,0);  
    InputRealPtrsType u1Ptr  = ssGetInputPortRealSignalPtrs(S,1);
    real_T            *y     = ssGetOutputPortRealSignal(S,0);
    int_T             width  = ssGetInputPortWidth(S,0);
    real_T            yr     = 0;  /* real part of the result */
    real_T            yi     = 0;  /* imag part of the result */
    int_T             i;

    if(!yIsComplex){
	/* both inputs are real */	 
	for (i = 0; i < width; i++) {
	    yr += (**u0Ptr++) * (**u1Ptr++);
	}
    } else {
	 /* At least one if the inputs is complex. */
	for (i = 0; i < width; i++) {
	    real_T u0r = u0Ptr[i][0];
            /* Note: the first input is conjugated. */
	    real_T u0i = (u0IsComplex)? - u0Ptr[i][1] : 0;
	    real_T u1r = u1Ptr[i][0];
	    real_T u1i = (u1IsComplex)?   u1Ptr[i][1] : 0;
	    
	    yr  += (u0r * u1r - u0i * u1i);
	    yi  += (u0r * u1i + u0i * u1r);
	}
    }

    /* Update ouput */
    y[0] = yr;
    if (yIsComplex) {
	y[1] = yi;
    }
}


static void mdlTerminate(SimStruct *S)
{
}

#if defined(MATLAB_MEX_FILE)
# define MDL_SET_INPUT_PORT_DIMENSION_INFO
 static void mdlSetInputPortDimensionInfo(SimStruct        *S, 
                                          int_T            port,
                                          const DimsInfo_T *dimsInfo)
{
    boolean_T isNotVector = ((dimsInfo->numDims == 2 ) &&
                             (dimsInfo->dims[0] > 1 && dimsInfo->dims[1] > 1)) ;
    if(isNotVector){
        ssSetErrorStatus(S, "The block only accepts vector signals. "
                            "It does not accept a [mxn] matrix signal "
                            "where m > 1 and n > 1.");
    }else{
        int otherPort = (port == 0) ? 1 : 0;
        if(!ssSetInputPortDimensionInfo(S, port, dimsInfo)) return;

        /* 
         * If other port width is unknown, set the other port width.
         * Note1: we cannot update other port dimension info. 
         * Note2: For simplicity, this block cannot accept partial dimension,
         *        however, it may partially set other port dimension info.
         */
        if(ssGetInputPortWidth(S, otherPort) == DYNAMICALLY_SIZED &&
           ssGetInputPortWidth(S, port)      != DYNAMICALLY_SIZED){

            DECL_AND_INIT_DIMSINFO(dimsInfo);
            dimsInfo.width   = ssGetInputPortWidth        (S, port);
            dimsInfo.numDims = ssGetInputPortNumDimensions(S, otherPort);
            dimsInfo.dims    = ssGetInputPortDimensions   (S, otherPort);

            if(!ssSetInputPortDimensionInfo(S, otherPort, &dimsInfo)) return;

        }
    }
}

# define MDL_SET_OUTPUT_PORT_DIMENSION_INFO
static void mdlSetOutputPortDimensionInfo(SimStruct        *S, 
                                          int_T            port,
                                          const DimsInfo_T *dimsInfo)
{
    /* This should never occur! */
    ssSetErrorStatus(S, "Error setting output port width.");
}

# define MDL_SET_INPUT_PORT_FRAME_DATA
static void mdlSetInputPortFrameData(SimStruct  *S, 
                                     int_T      port,
                                     Frame_T    frameData)
{
    /* Accept frame status silently */
    ssSetInputPortFrameData(S, port, frameData);
}

# define MDL_SET_INPUT_PORT_COMPLEX_SIGNAL
static void mdlSetInputPortComplexSignal(SimStruct *S, 
                                         int_T     port,
					 int_T iPortComplexSignal)
{
    int_T oPortComplexSignal = ssGetOutputPortComplexSignal(S,0);

    /* Set the complex signal of the input ports */
    ssSetInputPortComplexSignal(S, port, iPortComplexSignal);
    
    if(iPortComplexSignal == COMPLEX_YES){
        /* Output port must be a complex signal */
        if(oPortComplexSignal == COMPLEX_INHERITED){
            ssSetOutputPortComplexSignal(S, 0, COMPLEX_YES);
        }else if(oPortComplexSignal == COMPLEX_NO){
	    ssSetErrorStatus(S, "Output port must be complex.");
        }
    }else if(oPortComplexSignal != COMPLEX_NO){ 
	/* 
	 * The current input port is a real signal.  If the other input port 
	 * is a real signal, the output port must be a real signal.
	 */
	int_T otherPort = (port == 0)? 1 : 0;
	int_T otherPortComplexSignal = 
                         ssGetInputPortComplexSignal(S, otherPort);
	if(otherPortComplexSignal == COMPLEX_NO){
            /* Both input ports are real signals */
            if(oPortComplexSignal == COMPLEX_INHERITED){            
                ssSetOutputPortComplexSignal(S, 0, COMPLEX_NO);           
            }else if(oPortComplexSignal == COMPLEX_YES){
		ssSetErrorStatus(S, "Output port must be real.");
	    }           
	}
    }
}

# define MDL_SET_OUTPUT_PORT_COMPLEX_SIGNAL
static void mdlSetOutputPortComplexSignal(SimStruct *S, int_T port,
					  int_T  oPortComplexSignal)
{
    /* Set the complex signal of the output ports */
    ssSetOutputPortComplexSignal(S, 0, oPortComplexSignal);

    if(oPortComplexSignal == COMPLEX_NO){
        /* All inputs must be real */
        int_T i;
        
        for (i = 0; i < 2; i++) {
            int_T iPortComplexSignal = ssGetInputPortComplexSignal(S, i);
            if(iPortComplexSignal == COMPLEX_INHERITED){
                ssSetInputPortComplexSignal(S, i, COMPLEX_NO);
            } else if(iPortComplexSignal == COMPLEX_YES){
		ssSetErrorStatus(S, "The output port is a 'real' signal. "
		                    "All input ports must be 'real' signals.");
            }
        }
    }else{ 
        /* 
         * Output port is a complex signal.  Report an error, if all 
         * inputs are real. 
         */
	int_T i;
        boolean_T realInputs = true;
        for (i = 0; i < 2; i++) {
            if(ssGetInputPortComplexSignal(S, i) != COMPLEX_NO){
		realInputs = false;
		break;
	    }
	}
	if(realInputs){
            ssSetErrorStatus(S, "Input port and output port complex signal "
				"mismatch. All input ports are 'real' signal. "
				"The output port must be a 'real' signal.");
        }
    }
}
#endif /* MATLAB_MEX_FILE */


/* Required S-function trailer */

#ifdef	MATLAB_MEX_FILE
#include "simulink.c"    
#else
#include "cg_sfun.h"     
#endif

/* eof: sdotproduct.c */
