/*------------------------------------------------------------------------------*
 * File Name: ConvertRegularXYZ.c 												*
 * Creation: ER, 24th September 2001													*
 * Purpose: OriginC function to examine XYZ data for Regular Matrix Conversion	*
 * Copyright (c) OriginLab Corp.2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007	*
 * All Rights Reserved															*
 * 																				*
 * Modification Log:															*
 *------------------------------------------------------------------------------*/
 
////////////////////////////////////////////////////////////////////////////////////

#include <origin.h>
//#include <data.h>
//#include <math.h>

////////////////////////////////////////////////////////////////////////////////////
//
// prototype of functions in this file:
//
int ConvertRegularXYZ(string winData, int iXCol, int iYCol, int iZCol, string winTemp);
int FindStep(Dataset<double> &v);
//	
////////////////////////////////////////////////////////////////////////////////////
// 
// ConvertRegularXYZ:
//
// The function ConvertRegularXYZ examines the raw X,Y,Z data checking for irregularities in step size, 
// large deviations within a step etc. If the function returns a value of 0, then the raw data passed these 
// tests, and the data corrected and copied onto the temporary worksheet can then be used to perform a 
// regular X,Y,Z matrix conversion using Origin's built-in conversion ccode.
// A few details of the function follow:
// First we determine the step locations in the X,Y data. At a given point at location i, we compute the 
// difference between three pairs of points: (i,i+1), (i+1,i+2), (i+2,i+3). If the difference in the middle 
// is larger than twice the difference on either side, then there is a step at (i+1). Once the step locations 
// are found, we find the step values throughout the data and take the median value of all these step values 
// as our final step value. This step value is the used to recreate the data value at each step, starting with 
// the data value of the first step. We then examine all the data values within each step and look for 
// deviations. If any deviation is larger than 1/4 th of the step size, then we reject the data. 
//
// Parameter:
//			winData: 	String holding name of users' data worksheet
//			ix:			Position of X column in the data worksheet
//			iy:			Position of Y column
//			iz:			Position of Z column - remember: column numbers start at 0 in OriginC and at 1 in LabTalk
//			winTemp:	String holding name of temporary worksheet - this worksheet should be created before
//						calling this routine, and should have 5 columns in the format XYZYY
//						and two more Y columns
//
// Return:
//			0			Cleared for conversion
//			1			Failed when attempting to remove duplicates			
//			2			Step determination failed - data not suitable for regular conversion
//			3			x and y datasets not consistent - data not suitable for regular conversion
//			4			Too much deviation within a step - data not suitable for regular conversion
//
////////////////////////////////////////////////////////////////////////////////////


int ConvertRegularXYZ(string winData, int iXCol, int iYCol, int iZCol, string winTemp)
{

	//define datasets that map to worksheet data to be converted
	Dataset xData(winData, iXCol);
	Dataset yData(winData, iYCol);
	Dataset zData(winData, iZCol);
	
	// define datasets in temp worksheet and copy raw data over to temp
	Dataset mx(winTemp, 0);
	Dataset my(winTemp, 1);
	Dataset mz(winTemp, 2);
	
	// define datasets to hold median values of groups in X,Y data
	Dataset xMedian(winTemp, 3);
	Dataset yMedian(winTemp, 4);	

	//variables
	int ii, jj, id, ix, ixblocks, iy, iyblocks;
	double dev, xstep, ystep; 
		
	
	// get size of XYZ data set
	int isize = mx.GetSize();
	
	// sort temp worksheet wrt X primary, and wrt Y secondary
	_LT_Obj
	{
		sort.wksname$=winTemp;
		sort.c1 = 1;					// sort only first 3 cols
		sort.c2 = 3;
		sort.r1 = 1; 
		sort.r2 = isize;
		sort.cname1$ = "A: A"; 
		sort.cname2$ = "A: B"; 		
		sort.wks();
	}

	// determine the location of the first step in X data
	ix = FindStep(mx);
	if(ix == -1) return 2;

	// determine number of groups within X data
	ixblocks = isize / ix;

	// compute the step values and store them in temporary worksheet
	xMedian.SetSize(ixblocks-1);
	for(ii = 0; ii < (ixblocks-1); ii++)
	{
		xMedian[ii] = fabs(mx[ii * ix + ix / 2] - mx[(ii + 1) * ix + ix / 2]);
	}	

	// now sort these step values and pick the median value for the final x step size
	_LT_Obj
	{
		sort.wksname$=winTemp;
		sort.c1 = 4;					// sort only the 4th col
		sort.c2 = 4;
		sort.r1 = 1; 
		sort.r2 = ixblocks-1;
		sort.cname1$ = "A: D";  		
		sort.wks();
		
	}
	xstep = xMedian[(ixblocks - 1) / 2];
	
		
	// sort temp worksheet wrt Y primary, and wrt X secondary
	_LT_Obj
	{
		sort.wksname$=winTemp;
		sort.c1 = 1;						// sort only first 3 cols
		sort.c2 = 3;
		sort.r1 = 1; 
		sort.r2 = isize; 
		sort.cname1$ = "A: B"; 
		sort.cname2$ = "A: A"; 		
		sort.wks();
	}

	
	// determine the location of the first step in Y data
	iy = FindStep(my);
	if(iy == -1) return 2;

	// determine number of groups within Y data
	iyblocks = isize / iy;
	
	// compute the step values and store them in temporary worksheet
	yMedian.SetSize(iyblocks-1);
	for(ii = 0; ii < (iyblocks-1); ii++)
	{
		yMedian[ii] = fabs(my[ii * iy + iy / 2] - my[(ii + 1) * iy + iy / 2]);
	}	

	// now sort these step values and pick the median value for the final y step size
	_LT_Obj
	{
		sort.wksname$=winTemp;
		sort.c1 = 5;					// sort only the 5th col
		sort.c2 = 5;
		sort.r1 = 1; 
		sort.r2 = iyblocks-1;
		sort.cname1$ = "A: E";  		
		sort.wks();
		
	}
	ystep = yMedian[(iyblocks - 1) / 2];
	

	// do a check on whether the x and y data groups agree
	// number of groups in one should match group length in the other
	if ( (ix!= iyblocks) | (iy!= ixblocks) ) return 3;	 


	// now sort worksheet wrt X, ascending
	_LT_Obj
	{
		sort.wksname$=winTemp;
		sort.c1 = 1;					// sort only first 3 cols
		sort.c2 = 3;
		sort.r1 = 1; 
		sort.r2 = isize;
		sort.cname1$ = "A: A"; 
		sort.cname2$ = "A: B"; 		
		sort.wks();
	}
	
	// now find all the medians of each X group and store them in the temp worksheet
	xMedian.SetSize(ixblocks);
	for(ii = 0; ii < ixblocks; ii++)
	{
		xMedian[ii] = mx[ii * ix + ix / 2];
	}


	//build list of median values for X groups by taking medain value of first
	// group and then using the X step value
	xMedian.SetSize(ixblocks);
	for(ii=0; ii < ixblocks; ii++)
	{
		xMedian[ii] = mx[ix / 2] + xstep * ii;
		
	}
	
	
	// now go thru all groups of X data and check for deviations
	// replace deviated values with median value for that group
	// if a deviation is larger than 1/4 th of step size, data is rejected
	for(ii = 0; ii < ixblocks; ii++)
	{
		for(jj = 0; jj < ix; jj++)
		{
			id = ii * ix + jj;
			dev = fabs(mx[id] - xMedian[ii]);
			if(dev >= 0.25 * xstep) return 4;
			mx[id] = xMedian[ii];
		}
	}	
		

	// now sort worksheet wrt Y, ascending
	_LT_Obj
	{
		sort.wksname$=winTemp;
		sort.c1 = 1;					// sort only first 3 cols
		sort.c2 = 3;
		sort.r1 = 1; 
		sort.r2 = isize;
		sort.cname1$ = "A: B"; 
		sort.cname2$ = "A: A"; 		
		sort.wks();
	}

	
	// now build list of median values for Y groups by taking medain value of first
	// group and then using the average Y step value
	yMedian.SetSize(iyblocks);
	for(ii=0; ii < iyblocks; ii++)
	{
		yMedian[ii] = my[iy / 2] + ystep * ii;
		
	}
	
			
	// now go thru all groups of Y data and check for deviations
	// replace deviated values with median value for that group
	// if a deviation is larger than 1/4 th of step size, data is rejected
	for(ii = 0; ii < iyblocks; ii++)
	{
		for(jj = 0; jj < iy; jj++)
		{
			id = ii * iy + jj;
			dev = fabs(my[id] - yMedian[ii]);
			if(dev >= 0.25 * ystep) return 4;
			my[id] = yMedian[ii];
		}
	}	
			

	// now sort the worksheet one last time - deviations in Y values could have
	// lead to shuffled order in X data
		
	_LT_Obj
	{
		sort.wksname$=winTemp;
		sort.c1 = 1;					// sort only first 3 cols
		sort.c2 = 3;
		sort.r1 = 1; 
		sort.r2 = isize;
		sort.cname1$ = "A: A"; 
		sort.cname2$ = "A: B"; 		
		sort.wks();
	}

	
	// operation successful - return 0
	return 0;	
}	

////////////////////////////////////////////////////////////////////////////////////
// 
// FindStep:
//
// This function finds the first step location within the Dataset and returns the index of the datapoint
// at the step. If no step is found within the entire dataset, the function returns -1. To find a step, 
// three successive differences are examined, and if the difference in the middle is greater than twice 
// the difference at the first and the third points, then a step has been found. This algorithm assumes 
// that the fluctuations of data points within a group are less than 1/4 th of the step value.
//
////////////////////////////////////////////////////////////////////////////////////

int FindStep(Dataset<double> &v)
{
	double step1, step2, step3;
	int isize = v.GetSize();
	for(int ii=0; ii < (isize-3); ii++)
	{
		// compute differences at three points
		step1 = fabs(v[ii] - v[ii+1]);
		step2 = fabs(v[ii+1] - v[ii+2]);
		step3 = fabs(v[ii+2] - v[ii+3]);
		if((step2 > 2 * step1) && (step2 > 2 * step3)) return ii+2;
	}
	return -1;		
}		

// end of all functions in file		