/*------------------------------------------------------------------------------*
 * File Name:matrix.c				 											*
 * Creation:July 22th, 2002														*
 * Purpose: OriginC Source C file												*
 * Copyright (c) OriginLab Corp.2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007	*
 * All Rights Reserved															*
 * 																				*
 * Modification Log:															*
 * TCZ 07/29/02 QA70-2497	ADD_GLOBAL_STATS_SUMMARY_FUNCTION					*
 * ER 01/29/03 QA70_3802	ADD_VECTOR_FFT_IFFT									*
 * CPY 7/7/03 changed to include only origin.h to take advantage of PCH			*
 * Iris 2/13/04 v8.0820 USING_FFT_REPLACE_NAG									*		
 * Iris 2/27/04 v8.0827 USING_IMSL_REPLACE_NAG									*
 * Echo 6/7/04 QA70_6099 MBS_MOVE_NAG											*
 * Forest 6/10/04 QA70_6099 USING_LAPACK_SVD_REPLACE_IMSL   					*
 * Ryan Tan 07/07/04 QA70_5528 ADD_REPLACE_MISSING_VALUE						*
 * Echo 7/28/04 QUANTILE_FUNCTION_CHANGED										*
 * Sandy 7/12/06 MOVE_FFT_RELATIVE_FUNCIONS_BACK_TO_FFT_UTILS					*
 * Echo 9/25/06 CHANGE_DESC_STAT_FUNC_FOR_EFFICIENCY							*
 *------------------------------------------------------------------------------*/
 
#include <origin.h> // main Origin C header that is precompiled and already include most headers 
 
#pragma labtalk(0) //--- CPY 7/26/05 hide all functions in this file from LT access, as XF should be used instead

// this file include most of the other header files except the NAG header, which takes longer to compile
// NAG routines
// #include <NAG\OCN_c06.h> // this contains the FFT NAG headers, 
// #include <NAG\OCN_f.h>//this contain the linear algebra function
// #include <NAG\OCN_g01.h>//this file contain the simple statistics function
////////////////////////////////////////////////////////////////////////////////////
#include <matrix.h>

///Sandy 7/12/06 MOVE_FFT_RELATIVE_FUNCIONS_BACK_TO_FFT_UTILS
//#include <..\originlab\fft.h>
// start your functions here


//local functions for the internal use
//bool stats_complex_svd(int nRows, int nCols, const matrix<complex>& mcData, vector& vcS, matrix<complex>& mcU, int nUCols, matrix<complex>& mcV, int nVCols);



		
int set_matrix_with_padding_truncting(matrix & matSource, matrix & matResult, int nRowProcess , int nColProcess)
{
		
	
	if(nRowProcess < -1 || nRowProcess == 0 ||nColProcess < -1 ||nColProcess == 0)
		return CER_FFT_ERROR_COLUMN_ROW_NUMBER_NEGATIVE;
	int nNumCols = matSource.GetNumCols();
	int nNumRows = matSource.GetNumRows();
	if(nNumCols == 0 || nNumRows == 0)
		return CER_FFT_ERROR_SOURCE_MATRIX_EMPTY;
		
	int nIntendNumCols = nNumCols;
	int nIntendNumRows = nNumRows;
	if(nRowProcess != -1) 
		nIntendNumRows = nRowProcess;
	if(nColProcess != -1)
		nIntendNumCols = nColProcess;
	
	int nRet;
	matResult = matSource;
	
	if(nIntendNumRows > nNumRows)//so we should increase the dimension and do the padding
	{
		matrix matTemp;
		matTemp.SetSize(nIntendNumRows - nNumRows, nNumCols );
		matTemp = 0;
		if(nRet = matSource.Concatenate(1,matTemp, matResult))
			return nRet;
	}
	BOOL bOK = TRUE;
	if (nIntendNumRows < nNumRows)
    	bOK = matSource.GetSubMatrix(matResult, 0, -1, 0, nIntendNumRows - 1);
	if(!bOK)
		return CER_ERROR_GETSUBMATRIX_FUNCTION_ERROR;
    		
	    
    if(nIntendNumCols > nNumCols)
	{
		matrix matTemp;
		matTemp.SetSize(nIntendNumRows, nIntendNumCols - nNumCols );
		matTemp = 0;
		if(nRet = matResult.Concatenate(2, matTemp, matResult))
			return nRet;
		
	}
	
	if(nIntendNumCols < nNumCols)
	{
		matrix matResultTemp(matResult); 
		bOK = matResultTemp.GetSubMatrix(matResult, 0, nIntendNumCols -1, 0, -1);
		if(!bOK)
			return CER_ERROR_GETSUBMATRIX_FUNCTION_ERROR;
	}
	return 0;
}


	
int seperate_complex_mat_to_real_and_imag_mat(matrix<complex>& matSource, matrix& matReal, matrix&  matImag, int nRowProcess, int nColProcess)
{
	matrix matTemp;
	BOOL bOK;
	bOK = matSource.GetReal(matTemp);
	if(!bOK)
		return CER_ERROR_GETREAL_FUNCTION_FAIL;
	int nRet;
	if(nRet = set_matrix_with_padding_truncting(matTemp, matReal, nRowProcess, nColProcess))
		return nRet;
	bOK = matSource.GetImaginary(matTemp);
	if(!bOK)
		return CER_ERROR_GETREAL_FUNCTION_FAIL;
	if(nRet = set_matrix_with_padding_truncting(matTemp, matImag, nRowProcess, nColProcess))
		return nRet;
}



/**
	int SVD(matrix<complex>& matSource, matrix& matS, matrix<complex>& matU, matrix<complex>& matV);
	int SVD(matrix<complex>& matSource, matrix<complex>& matS); 
	int SVD(matrix& matSource, matrix& matS);
	int SVD(matrix& matSource, matrix& matS, matrix& matU, matrix& matV);

			These four functions are used to do the singular value decomposition. matSource = matU* matS* matV'
	Example:
	Parameters:
			matSource: The  m * n source data matrix
			matS: The min(m, n) diaganol sigular matrix 
			matU: The left side  m * m unitary matrix
			matV: The right side n* n unitary matrix
	return:
			if succeed, it will return 0

*/
///Iris 2/27/04 v8.0827 USING_IMSL_REPLACE_NAG
/*	
int SVD(matrix& matSource, matrix & matS, matrix& matU, matrix& matV)
{
	//fist check the size of the matrix
	int nNumCols = matSource.GetNumCols();
	int nNumRows = matSource.GetNumRows();
	//no need to check size, because nag will take care for it
	//set the result matrix size
	matU.SetSize(nNumRows, nNumRows);
	matV.SetSize(nNumCols, nNumCols);
	vector vecE;
	int nMinSize = nNumCols > nNumRows ? nNumRows : nNumCols;
	vecE.SetSize(nMinSize -1);
	vector vecS;
	vecS.SetSize(nMinSize);
	//call the nag function
	int nRet;
	int nNumIter, nfailIfor;
	//we make a copy of source data here becasue we do not want to overwirte the data
	matrix matSourceCopy(matSource);
	if(nRet = nag_real_svd(nNumRows, nNumCols, matSourceCopy, nNumCols, 0, NULL, 0, TRUE, matU, nNumRows, 
							vecS, TRUE, matV, nNumCols, &nNumIter, vecE, &nfailIfor))
		return nRet;
	//set back the U V matrix
	if(nNumRows >= nNumCols)
		matU = matSourceCopy;
	else
		matV = matSourceCopy;
	matV.Transpose(); // Nag return MatV'
	//set back matS
	nRet = matS.SetDiagonal(vecS, 0);
	
	//here we do not consider nfailinfo, becasue it is rarely occurs.
	return nRet;				
				
}
*/
///Forest 6/10/04 QA70_6099 USING_LAPACK_SVD_REPLACE_IMSL
int SVD(matrix& matSource, matrix & matS, matrix& matU, matrix& matV)
{
	//fist check the size of the matrix
	int nNumCols = matSource.GetNumCols();
	int nNumRows = matSource.GetNumRows();
	
	int nMinSize = nNumCols > nNumRows ? nNumRows : nNumCols;
	
	//matU.SetSize(nNumRows, nMinSize);
	//matV.SetSize(nNumCols, nMinSize);
	//
	//vector vecE;
	//vecE.SetSize(nMinSize -1);
	//vector vecS;
	//vecS.SetSize(nMinSize);
	//
	//int nRet;
	//int nNumIter, nfailIfor;
	////we make a copy of source data here becasue we do not want to overwirte the data
	//matrix matSourceCopy(matSource);
//
	//double* d = imsl_d_lin_svd_gen(nNumRows, nNumCols, (double*)matSourceCopy,
						//IMSL_RETURN_USER, (double*)vecS, 
						//IMSL_U_USER, (double*)matU,
						//IMSL_U_COL_DIM, (int)nMinSize,
						//IMSL_V_USER, (double*)matV,						
						//IMSL_V_COL_DIM, (int)nMinSize,						
						//0);	
	//matU = 0 - matU;
	//matV = 0 - matV;
	//if(NULL == d)
		//return 1; //failed
	//
	////set back matS
	//nRet = matS.SetDiagonal(vecS, 0);
	//
	//return nRet;	
	if(nNumRows < 1 || nNumCols < 1)
		return	1;
	vector vecS;
	vecS.SetSize(nMinSize);
	matU.SetSize(nNumRows, nNumRows);
	matV.SetSize(nNumCols, nNumCols);
	int iRet = ocmath_svd(nNumRows, nNumCols, matSource, matU, vecS, matV);
	if(iRet)
		return	1;
	matS.SetDiagonal(vecS, 0);
	return	0;
}
/*	
int SVD(matrix<complex>& matSource, matrix& matS, matrix<complex>& matU, matrix<complex>& matV)
{
	//fist check the size of the matrix
	int nNumCols = matSource.GetNumCols();
	int nNumRows = matSource.GetNumRows();
	//no need to check size, because nag will take care for it
	//set the result matrix size
	matU.SetSize(nNumRows, nNumRows);
	matV.SetSize(nNumCols, nNumCols);
	vector vecE;
	int nMinSize = nNumCols > nNumRows ? nNumRows : nNumCols;
	vecE.SetSize(nMinSize -1 );
	vector vecS;
	vecS.SetSize(nMinSize);
	//call the nag function
	int nRet;
	int nNumIter, nfailIfor;
	//we make a copy of source data here becasue we do not want to overwirte the data
	matrix<complex> matSourceCopy(matSource); 
	if(nRet = nag_complex_svd(nNumRows, nNumCols, matSourceCopy, nNumCols, 0, NULL, 0, TRUE, matU, nNumRows, 
							vecS, TRUE, matV, nNumCols, &nNumIter, vecE, &nfailIfor))
		return nRet;
	//set back the U V matrix
	if(nNumRows >= nNumCols)
		matU = matSourceCopy;
	else
		matV = matSourceCopy;
	matV.Transpose(); // Nag return MatV'
	//set back matS
	nRet = matS.SetDiagonal(vecS, 0);

	//here we do not consider nfailinfo, becasue it is rarely occurs.
	return nRet;				
				
}
*/
//#ifdef IMSL_H

int SVD(matrix<complex>& matSource, matrix& matS, matrix<complex>& matU, matrix<complex>& matV)
{
	//fist check the size of the matrix
	int nNumCols = matSource.GetNumCols();
	int nNumRows = matSource.GetNumRows();
	
	int nMinSize = nNumCols > nNumRows ? nNumRows : nNumCols;
	//matU.SetSize(nNumRows, nMinSize);
	//matV.SetSize(nNumCols, nMinSize);
	//
	//vector vecE;
	//vecE.SetSize(nMinSize -1 );
	//vector vecS;
	//vecS.SetSize(nMinSize*2);
	//
	//int nRet;
	//int nNumIter, nfailIfor;
	////we make a copy of source data here becasue we do not want to overwirte the data
	//matrix<complex> matSourceCopy(matSource); 
//
	//d_complex* d = imsl_z_lin_svd_gen(nNumRows, nNumCols, (d_complex*)matSourceCopy,
						//IMSL_RETURN_USER, (double*)vecS, 
						//IMSL_U_USER, (d_complex*)matU,
						//IMSL_U_COL_DIM, (int)nMinSize,
						//IMSL_V_USER, (d_complex*)matV,
						//IMSL_V_COL_DIM, (int)nMinSize,						
						//0);	
	//if(NULL == d)
		//return 1; //failed
	//
	//int j;
	//vector<complex> vTemp;
	//for(j=0; j<nMinSize; j++)
	//{
		//if(0 == j%2)
		//{
			//matU.GetColumn(vTemp,j);
			//vTemp = -vTemp;
			//matU.SetColumn(vTemp,j);
			//
			//matV.GetColumn(vTemp,j);
			//vTemp = -vTemp;
			//matV.SetColumn(vTemp,j);
		//}
	//}
	////set back matS
	//vector vecSTemp;
	//for(j=0; j<vecS.GetSize(); j++)
	//{
		//if(0 == j%2)
			//vecSTemp.Add(vecS[j]);
	//}
	//nRet = matS.SetDiagonal(vecSTemp, 0);
//
	//return nRet;
	if(nNumRows < 1 || nNumCols < 1)
		return	1;
	vector vecS;
	vecS.SetSize(nMinSize);
	matU.SetSize(nNumRows, nNumRows);
	matV.SetSize(nNumCols, nNumCols);
	int iRet = ocmath_svd_complex(nNumRows, nNumCols, matSource, matU, vecS, matV);
	if(iRet)
		return	1;
	matS.SetDiagonal(vecS, 0);
	return	0;				
}
//End USING_LAPACK_SVD_REPLACE_IMSL
///End USING_IMSL_REPLACE_NAG
	
int SVD(matrix<complex>& matSource, matrix& matS) 
{
	matrix<complex> matU, matV;
	int nRet = 0;
	nRet = SVD(matSource, matS, matU, matV);
	return nRet;
}
//#endif //IMSL_H

int SVD(matrix& matSource, matrix& matS)
{
	matrix matU, matV;
	int nRet = 0;
	nRet = SVD(matSource, matS, matU, matV);
	return nRet;
}


/**
	int Trace(matrix<short> & matSource, int & nSum);
	int Trace(matrix<int> & matSource, int & nSum);
	int Trace(matrix<double> & matSource, double & dSum);
	int Trace(matrix<complex> & matSource, complex & cSum);

			These four functions will give the trace of a matrix
	Remarks:
			we do not support every matrix type.
 	Example:
	Parameters:
			matSource: The source matrix
			nSum:
			dSum:
			cSum:
				These are the variables to store the trace.
	Return:
			if succeed, it will return 0

*/

#define CHECK_MATRIX_FOR_TRACE {\
	int nNumCols = matSource.GetNumCols(); \
	int nNumRows = matSource.GetNumRows(); \
	if(!nNumCols || !nNumRows)  \
		return CER_FFT_ERROR_SOURCE_MATRIX_EMPTY; \
}


int Trace(matrix<short> &matSource, int& nSum)
{
	CHECK_MATRIX_FOR_TRACE;

	vector<short> vecDiag;
	int nRet;
	if(nRet = matSource.GetDiagonal(vecDiag))
		return nRet;
	if(nRet = vecDiag.Sum(nSum))
		return nRet;

}

int Trace(matrix<int> &matSource, int& nSum)
{
	CHECK_MATRIX_FOR_TRACE;
	
	vector<int> vecDiag;
	int nRet;
	if(nRet = matSource.GetDiagonal(vecDiag))
		return nRet;
	if(nRet = vecDiag.Sum(nSum))
		return nRet;

}
int Trace(matrix<double> &matSource, double& dSum)
{
	CHECK_MATRIX_FOR_TRACE;
	
	vector<double> vecDiag;
	int nRet;
	if(nRet = matSource.GetDiagonal(vecDiag))
		return nRet;
	if(nRet = vecDiag.Sum(dSum))
		return nRet;

}	

int Trace(matrix<complex> & matSource, complex& cSum)
{
	CHECK_MATRIX_FOR_TRACE;

	vector<complex> vecDiag;
	int nRet;
	if(nRet = matSource.GetDiagonal(vecDiag))
		return nRet;
	if(nRet = vecDiag.Sum(cSum))
		return nRet;
	return 0;

}

///TCZ 07/29/02 QA70-2497	ADD_GLOBAL_STATS_SUMMARY_FUNCTION
//#define MAT_ERR_WEIGHTS_DATA_SIZE_NOT_MATCH (-5) //defined in the matrix.h
/**
			This function will give the summary of the statistics of a source data contained in a matrix	
	Remarks:
			If user does not have the weight, then just supply a matix without any initialization for weight 
	Example:
	Parameters:
			matData: The Data Source
			MatrixStatsSummary: The Results
			matWeights: The weight if any
	Return:
			Return 0 if succeed.
			MAT_ERR_WEIGHTS_DATA_SIZE_NOT_MATCH: The weight matrix size does not match that of the source
	int MatrixBasicStats(matrix & mData, MatrixStats & sMatStatsSum, matrix * mpWeights = NULL);

*/
int MatrixBasicStats(matrix & mData, MatrixStats & sMatStatsSum, matrix * mpWeights )
{
	
	//first check the size of the weight and data size 
	int nNumCols = mData.GetNumCols();
	int nNumRows = mData.GetNumRows();
	if(mpWeights != NULL) 
	{
		int nNumColsWeights = mpWeights->GetNumCols();
		int nNumRowsWeights = mpWeights->GetNumRows();
		if(nNumCols != nNumColsWeights || nNumRows != nNumRowsWeights)
			return MAT_ERR_WEIGHTS_DATA_SIZE_NOT_MATCH;
	}
	int nNumPoints = nNumCols * nNumRows;
	int nValid, nMissing;
	double dMean, dSD, dMax, dMin, dWSum;
	int nRet;
	///Echo 6/7/04 MBS_MOVE_NAG
	DescStatResults dsr;
	int MomentDenFlag = DS_NAG;
	double dConfLevel = 0.95;
	QuantileOptions QuanOpt;
	QuanOpt.Interpolate = 0; //set as default method ///Echo 7/28/04 QUANTILE_FUNCTION_CHANGED
	QuanOpt.Min = QuanOpt.Q1 = QuanOpt.Median = QuanOpt.Q3 = QuanOpt.Max = true;
	QuantileResults QuanRes;
	///End MBS_MOVE_NAG
	if(mpWeights == NULL)
	{
		///Echo 9/25/06 CHANGE_DESC_STAT_FUNC_FOR_EFFICIENCY
		if (nRet = ocmath_basic_summary_stats(nNumPoints, mData, NULL, &dMean, &dSD, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &nMissing, &dWSum))
			return nRet;
		/*
		///Echo 6/7/04 MBS_MOVE_NAG
		if( nRet = ocmath_desc_stats(mData, nNumPoints, &dsr, NULL, dConfLevel, MomentDenFlag))
		//if( nRet = nag_summary_stats_1var(nNumPoints, mData, NULL, &nValid, &dMean, &dSD, NULL, NULL, &dMin, &dMax, &dWSum))
		///End MBS_MOVE_NAG
			return nRet;
		*/
		///end CHANGE_DESC_STAT_FUNC_FOR_EFFICIENCY
	}
	else
	{
		///Echo 9/25/06 CHANGE_DESC_STAT_FUNC_FOR_EFFICIENCY
		if (nRet = ocmath_basic_summary_stats(nNumPoints, mData, NULL, &dMean, &dSD, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &nMissing, &dWSum,NULL,0, *mpWeights))
			return nRet;
		/*
		///Echo 6/7/04 MBS_MOVE_NAG
		if( nRet = ocmath_desc_stats(mData, nNumPoints, &dsr, *mpWeights, dConfLevel, MomentDenFlag))
		//if( nRet = nag_summary_stats_1var(nNumPoints, mData, *mpWeights, &nValid, &dMean, &dSD, NULL, NULL, &dMin, &dMax, &dWSum))
		///End MBS_MOVE_NAG
			return nRet;
		*/
		///end CHANGE_DESC_STAT_FUNC_FOR_EFFICIENCY
	
	}	
	//call the second nag function to get the median and hinge
	///Echo 6/7/04 MBS_MOVE_NAG
	//double dRes[5];
	//if( nRet = nag_5pt_summary_stats(nNumPoints, mData, dRes))
	 if ( nRet = ocmath_quantiles(mData, nNumPoints, &QuanOpt, &QuanRes))
		return nRet;
	 
	//set the structure value
	///Echo 9/25/06 CHANGE_DESC_STAT_FUNC_FOR_EFFICIENCY	
	/*
	///Echo 6/7/04 MBS_MOVE_NAG
	//sMatStatsSum.nMissingValue = nNumPoints - nValid;
	//sMatStatsSum.dMean = dMean;
	//sMatStatsSum.dSD = dSD;
	//sMatStatsSum.dMax = dMax;
	//sMatStatsSum.dMin = dMin;
	//sMatStatsSum.dMedian = dRes[2];
	//sMatStatsSum.dLowHinge = dRes[1];
	//sMatStatsSum.dUpHinge = dRes[3];
	//sMatStatsSum.dWSum = dWSum;
	//sMatStatsSum.nMissingValue = dsr.Missing;
	sMatStatsSum.dMean = dsr.Mean;
	sMatStatsSum.dSD = dsr.SD;
	sMatStatsSum.dWSum = dsr.WeightSum;
	*/
	sMatStatsSum.nMissingValue = nMissing;
	sMatStatsSum.dMean = dMean;
	sMatStatsSum.dSD = dSD;
	sMatStatsSum.dMax = QuanRes.Max;
	sMatStatsSum.dMin = QuanRes.Min;
	sMatStatsSum.dMedian = QuanRes.Median;
	sMatStatsSum.dLowHinge = QuanRes.Q1;
	sMatStatsSum.dUpHinge = QuanRes.Q3;
	sMatStatsSum.dWSum = dWSum;
	///end CHANGE_DESC_STAT_FUNC_FOR_EFFICIENCY
	return nRet;	
}
//END-------------------------------ADD_GLOBAL_STATS_SUMMARY_FUNCTION


//Ryan Tan 07/07/04 QA70_5528 ADD_REPLACE_MISSING_VALUE
int replace_missing_value( matrix& mData, bool bIsRowWise, bool bIncludeEdge)	//=true, =false
{
	int iRow, iCol;
	int nNumRows, nNumCols;
	//
	if( !bIsRowWise )	// if column wise, rotate to treat it row wise.
		mData.Rotate(90);
	
	nNumRows = mData.GetNumRows();
	nNumCols = mData.GetNumCols();
	if( 0 == nNumRows || 0 == nNumCols )
		return 0;
	
	if( 2 > nNumCols )	//only one column, no need to replace.
	{
		if( !bIsRowWise )
			mData.Rotate(-90);
		return 1;
	}
	//
	for( iRow = 0; iRow < nNumRows; iRow++ )
	{
		int nStart = 0;
		int nEnd = nNumCols-1;
		//
		while( nStart < nEnd && mData[iRow][nStart] == NANUM )
			nStart ++;
		while( nEnd > nStart && mData[iRow][nEnd] == NANUM )
			nEnd--;
		//
		if( nStart == nEnd )
		{
			if( mData[iRow][nStart] != NANUM && bIncludeEdge )	//The row contains only one real,
			{												//and others are missing value,
				vector vRow( nNumCols );					//then fill all row with the real.
				double d = mData[iRow][nStart];
				vRow = d;						//if write as "vRow = mData[iRow][nStart]" and mData is a Matrix, 
												//runtime error occur.
				mData.SetRow( vRow, iRow );
			}
			continue;	//All in the row are missing values, then not modify it.
		}
		
		//Fill missing values at both edges with the first and last real number
		if( bIncludeEdge )
		{
			for( int j = 0; j < nStart; j++ )
				mData[iRow][j] = mData[iRow][nStart];
			for( j = nEnd+1; j < nNumCols; j++ )
				mData[iRow][j] = mData[iRow][nEnd];
		}
		
		//
		iCol = nStart;
		while( nStart < nEnd )
		{
			while( iCol < nEnd && mData[iRow][iCol] != NANUM )
				iCol++;
			
			if( NANUM == mData[iRow][iCol] )	//a gap for NANUM exists.
			{
				int iGapLo, iGapHi;
				iGapLo = iCol;
				while( mData[iRow][iCol] == NANUM ) //find a non NANUM
					iCol++;
				iGapHi = iCol;
				
				//fill in the gap with the nearest real number.
				int iGapLength = iGapHi - iGapLo+1;
				for(int ii = iGapLo; ii < iGapLo + iGapLength / 2; ii++)
					mData[iRow][ii] = mData[iRow][iGapLo - 1];
				for(; ii < iGapHi; ii++)
					mData[iRow][ii] = mData[iRow][iGapHi];
			}
			nStart = iCol;
		}
	}
	//
	if( !bIsRowWise )		// rotate back if column wise.
		mData.Rotate(-90);
	
	return 1;
}
///End ADD_REPLACE_MISSING_VALUE
