/*------------------------------------------------------------------------------*
 * File Name:Analysis_utils.h 													*
 * Creation: CPY 3/11/03														*
 * Purpose: Origin's basic internal analysis routines							*
 * Copyright (c) Originlab Corp.	2003										*
 * All Rights Reserved															*
 * 																				*
 * Modification Log:															*
 *------------------------------------------------------------------------------*/

 
#ifndef _ANALYSIS_UTILS_H
#define _ANALYSIS_UTILS_H

/** >Analysis
		Converts regular XYZ data to a matrix.
		If asked for, data is examined for deviations.
	Example:
		//This example fill a matrix with values in Data1.
		//For this samples to work, Data1 must exist in the project contains 3 column A, B, C
		void run_convert_regular_xyz_to_matrix()
		{
			Dataset dsX("Data1_A"), dsY("Data1_B"), dsZ("Data1_C");
			vector vecX(dsX), vecY(dsY), vecZ(dsZ);
			
			MatrixLayer ml;						//Create a new Matrix to get the result
			ml.Create();
			Matrix matData(ml);
			
			double dXmax, dXmin;
			vecX.GetMinMax(dXmin, dXmax);		//Get the maximum and minimum values of X
			
			double dYmax, dYmin;
			vecY.GetMinMax(dYmin, dYmax);		//Get the maximum and minimum values of Y
		
			convert_regular_xyz_to_matrix(vecX, vecY, vecZ, matData, dXmin, dXmax, dYmin, dYmax); 	
		}
	Parameters:
		vecX,Y,Z:		Input: regular XYZ data
		dXmin, dXmax:	min, max of x data - can use for setting coordinates of Matrix object
		dYmin, dYmax:	min, max of y data - can use for setting coordinates of Matrix object
		bCheckData:		true: check data for irregularities; false: do no checking 
						If user knows data is perfectly regular, can set to false for faster operation
	Return:
		0:			success
		1:			XYZ vectors are of unequalsize, or result matrix smaller than 2x2
		2:			could not find steps in data
		3:			groups in x/y do not match group length in y/x
		4:			too much deviation in x/y data
*/
int convert_regular_xyz_to_matrix(vector& vecX, vector& vecY, vector& vecZ, matrix& matData, double& dXmin,
									double& dXmax, double& dYmin, double& dYmax, bool bCheckData = true);

/** >Analysis
		Performs gridding of random XYZ data.
		The gridding is performed by calling NAG functions.
		Two gridding methods, Renka-Cline and Modified Shepard's method are available.
		For more details on these methods, see documentation for NAG function e01_sac.
	Example:
		int iRet = random_gridding_nag(vecX, vecY, vecZ)	// Renka-Cline method
		int random_gridding_nag(vecX, vecY, vecZ, 1)		// Shepard's method
	Parameters:
		vecX,Y,Z:		Input: random XYZ data
		matResult:		matrix with gridded data
		dXmin, dXmax:	min, max of x data - can use for setting coordinates of Matrix object
		dYmin, dYmax:	min, max of y data - can use for setting coordinates of Matrix object
		iMethod:		0: Renka-Cline gridding; <>0: Modified Shepard's method
		dQIL:			Quadratic Interpolant Locality - used only by Shepard's method
		dWFL:			Weight Function Locality - used only by Shepard's method
	Return:
		0:		success
		-1:		Error in creating matrix
		NAG 	error codes:
		70: 	Parameter method had an illegal value.
		11: 	The number of points is less than 3.
		73: 	Memory allocation failed.
		245: 	All the (x,y) pairs are collinear.
		246: 	Data pair is not unique. Duplicate points are found in the (x,y) pair.
		247: 	Invalid dQIL and dWFL for Shepard's method.
		249: 	(Warning)The minimum number of data points that lie within the radius of 
				any node is smaller that the interpolant may be unsatisfactory in regions 
				where the data points are sparse.
		251: 	(Warning)The evaluation point lies outside the triangulation boundary.  
				The returned value was computed by extrapolation.
		252: 	On entry,the interpolant cannot be evaluted because the evaluation 
				point is outside the support region of the input data points.
	See Also:
		convert_regular_xyz_to_matrix
*/
int convert_random_xyz_to_matrix_nag(vector& vecX, vector& vecY, vector& vecZ, 
			matrix& matResult, double& dXmin, double& dXmax, double& dYmin, double& dYmax, 
			int iMethod = 0, double dQIL = 1, double dWFL = 1);

/** >Analysis
		Removes duplicate points from XYZ data.
		Duplicates are replaced with mean Z value.
	Example:
		//This example removes the duplicated values in Data1.
		//For this samples to run, Data1 should exist in the project with A, B, C columns.
		void run_xyz_remove_duplicates()
		{
			Dataset dsX("Data1_A"), dsY("Data1_B"), dsZ("Data1_C");
			vector vecX(dsX), vecY(dsY), vecZ(dsZ);
			
			xyz_remove_duplicates(vecX, vecY, vecZ);
			
			dsX = vecX;
			dsY = vecY;
			dsZ = vecZ;
		}
	Parameters:
		vecX,Y,Z:		Input: XYZ data; Output: XYZ vectors with duplicates removed
	Return:
		0:			success
		1:			XYZ vectors are of incorrect length
*/
int xyz_remove_duplicates(vector& vecX, vector& vecY, vector& vecZ);

/** >Analysis
		Converts sparse XYZ data to a matrix.
		Sparse data is regular data with some rows missing.
	Example:
		//This example convert Data1_A, Data1_B, Data1_C into a sparse matrix.
		//For this sample to run, Data1 with column A, B, C must exist. 
		void run_convert_sparse_xyz_to_matrix()
		{
			Dataset dsX("Data1_A"), dsY("Data1_B"), dsZ("Data1_C");
			vector vecX(dsX), vecY(dsY), vecZ(dsZ);
			
			MatrixLayer ml;
			ml.Create();				// Create a new matrix to get the result
			Matrix matData(ml);
			
			double dXend, dXbegin;
			double dYend, dYbegin;
			double dXstep; 
			double dYstep; 
			
			convert_sparse_find_min_max_step(vecX, dXbegin, dXend, dXstep); //Find the step
			convert_sparse_find_min_max_step(vecY, dYbegin, dYend, dYstep);	//Find the step
			
			convert_sparse_xyz_to_matrix(vecX, vecY, vecZ, matData, dXbegin, dXend, dXstep, 
						dYbegin, dYend, dYstep);
		}
	Parameters:
		vecX,Y,Z:				Input: regular XYZ data
		matResult:				Output: resulting matrix
		dXmin, dXmax: dXStep:	Input: min, max and stepsize in X. 
							  	Call convert_sparse_find_min_max_step to estimate these values.
		dYmin, dYmax: dYStep: 	Input: min, max and stepsize in Y. 
							  	Call convert_sparse_find_min_max_step to estimate these values.
		iPrecision:				Input: precision used to locate values; same as under Data_list() function
 	Return:
				0:				All points were converted
				-1:				xyz vectors are of different/incorrect size
				-2:				Matrix size too large - could be due to very small step value
				-3:				Error in creating matrix object
				positive:		Number of points that were discarded
		
*/
int convert_sparse_xyz_to_matrix(vector& vecX, vector& vecY, vector& vecZ, matrix& matResult, double dXbegin, double dXend, double dXstep, double dYbegin, double dYend, double dYstep, int iPrecision = 8);

/** >Analysis
		Compute min, max and step value for sparse matrix conversion.
	Example:
		int iRet = convert_sparse_find_min_max_step(vec, dMin, dMax, dStep);
	Parameters:
		vec:				vector with data
		dMin, dMax, dStep:	min, max and step values
	Return:
		0:			success
	See Also:
		convert_sparse_xyz_to_matrix
*/
int convert_sparse_find_min_max_step(vector& vec, double& dMin, double& dMax, double& dStep);

/** >Curve
		Find Worksheet and Column names from a Curve 
	Example:
		//This example get the dataset name of the curve in Graph1.
		//For this sample to work, Graph1 must exist in the project.
		void run_curve_get_wks_col_names()
		{
			GraphLayer gl("Graph1", 0);				//Get the first layer of Graph1
			if(gl)
			{
				DataPlot dp = gl.DataPlots(0);      // Get first data plot in graph layer
				if(dp)                              // If valid DataPlot...
				{
					Curve crv(dp);   				// Get Curve from DataPlot
					string strX, strY, strWks;		
					strWks = curve_get_wks_col_names(crv, strX, strY); 
					out_str("The first DataPlot in Graph1: ");
					out_str("X: "+ strWks + "_" + strX);
					out_str("Y: "+ strWks + "_" + strY);
				}
			}
		}
	Parameters:
		cuv = [in] Curve to find info about
		strX = [out] X column name, can be empty if Curve has no X column
		strY = [out] Y column name.
	Return:
		name of the Worksheet, will be empty if input Curve is invalid
*/
string curve_get_wks_col_names(const curvebase& cuv, string& strX, string& strY);

/** >Curve
		Search the layer where two curves are both plotted.
	Parameters:
		cuv1 = [in] Curve to try to add to another curve's layer
		cuv2 = [in] Curve that is likely already plotted in some layer
		glayerFound = [out] Graph that cuv2 is currently plotted
	Example:
		if(cuvFit)
		{
			GraphLayer gl;
			if(!curve_both_in_layer(cuvFit, cuvData, gl) && gl) // there maybe many, we will just use 1st for now
			{
				// cuvData plotted in gl but cuvFit not, so we will make the plot
				int nPlot = gl.AddPlot(cuvFit, IDM_PLOT_LINE);
				DataPlot dp = gl.DataPlots(nPlot);
				dp.SetColor(1); // set to red
			}
		}
	Return:
		FALSE if not in same layer, or cuv2 not plotted at all
	Remark:
		glayerFound can be set to a layer to search first, or it can be let uninitialized	
*/
bool curve_both_in_layer(const curvebase& cuv1, const curvebase& cuv2, GraphLayer& glayerFound);

/** >Curve
	find the input curve layer and plot the result curve
	Parameters:
		cuvResult = [in] Curve to plot
		cuvInput = [in] reference Curve to find where to plot the cuvResult
		trInput  = [in] if given, this will provide the graph layer location to plot cuvResult
		nColor = [in] color of the cuvResult curve
		bRescale = [in] rescale layer or not
	Example:
		//This example shows how to plot Data1_C to the same graph layer as Data1_B in red.
		//For this sample codes to run, a worksheet named "Data1" with column B and C 
		//should exist in the current project and a graph with Data1_B plotted exists too.
		
		void run_curve_update()
		{
			Curve crvPlotted( "Data1_B" );
			Curve crvToBePlot( "Data1_C" );
			TreeNode trInput;
			int nColor = 1; 			//Red
			
			if(crvPlotted && crvToBePlot)
			{
				curve_update(crvToBePlot, crvPlotted, trInput, nColor);
			}
		}
	Return:
		TRUE if correct layer is found and cuvResult is plotted successfully		
*/
bool curve_update(curvebase& cuvResult, const curvebase& cuvInput, const TreeNode& trInput, int nColor, bool bRescale = false);

/** >Curve
		It searches for the dataplot containing the given curve. If found, just refreshes the graph page,
		otherwise it adds the curve to the first layer of the page.
	Parameters:
		cuv=[in] the input curve
		nColor=[in] nthe color the curve should have.
		gpg=[in] the graph page in which to look for the curve.
		bRescale=[in] to rescale the layer with the curve or not.
	Example:
		//This example add curve Data1_B to Graph1.
		//For this sample codes to run, Graph1 should exist in the current project.
		void run_curve_update_in_page()
		{
			Curve crvPlotted( "Data1_B" );
			GraphPage gp1( "Graph1" );
			int nColor = 0;  			// Black
			if(gp1 && crvPlotted)
			{
				curve_update_in_page(crvPlotted, nColor, gp1);
			}
		}
	Returns:
		TRUE if OK, otherwise FALSE.
*/
bool	curve_update_in_page(curvebase& cuv, int nColor, GraphPage &gpg, bool bRescale = false);

/** >Curve
		It looks for the curve in the graph page.
	Parameters:
		cuv=[in] the curve to look for
		gpg=[in] the graph page to look in.
		pnLayerIndex=[in] optional pointer to an integer to receive the index of the layer in which the curve was found
		pnDataPlotIndex=[in] optional pointer to an integer to receive the index of the dataplot in the layer if found.
	Example:
		//This example use function curve_in_page_get_indices() to check whether Data1_B 
		//exists in Graph1. For this sample to work, Data1_B and Graph1 should exist.
		void run_curve_in_page_get_indices()
		{
			Curve crvPlotted( "Data1_B" );
			GraphPage gp1( "Graph1" );
			if(crvPlotted && gp1)
			{
				bool bOK = curve_in_page_get_indices(crvPlotted, gp1);
				if(bOK)
				{
					out_str("Data1_B exists in Graph1.");
				}
				else
				{
					out_str("Data1_B does not exist in Graph1.");
				}
			}
		}
	Returns:
		TRUE if found, otherwise FALSE.
*/
BOOL	curve_in_page_get_indices(const curvebase& cuv, const GraphPage &gpg, int *pnLayerIndex = NULL, int *pnDataPlotIndex = NULL);

/** >Curve
		It adds the curve cuv to the layer grl.
	Parameters:
		cuv=[in] the input curve
		grl=[in] the layer to add the curve to.
		nColor=[in] nthe color the curve should have.
		bRescale=[in] to rescale the layer with the curve or not.
		nPlotType=[in] the plot type of the curve to add.
	Example:
		//This example add Data1_B to the first layer of Graph1.
		//For this example to run, Graph1 should exist in the current project.
		void run_add_curve_to_graph_layer()
		{
			Curve crvPlotted( "Data1_B" );
			GraphLayer gl( "Graph1", 0 );
			if(crvPlotted && gl)
			{
				bool bOK = add_curve_to_graph_layer(crvPlotted, gl);
				if(bOK)
				{
					out_str("Success in adding Data1_B to Graph1.");
				}
				else
				{
					out_str("Fail to add Data1_B to Graph1.");
				}
			}
		}		
	Returns:
		TRUE if OK, otherwise FALSE.
*/
bool	add_curve_to_graph_layer(curvebase& cuv, GraphLayer &grl, int nColor = 2, bool bRescale = false, int nPlotType = IDM_PLOT_LINE);

/** >Curve
		check if Curve is in a graph layer and return its plot index if true
	Parameters:
		cuv = [in] Curve to test
		gl = [in] graph layer to test
	Example:
		//This example return the index of Data1_B in Graph1.
		//For this sample to work, Graph1 and Dataset Data1_B must exist in the project.
		void run_curve_in_layer_get_index()
		{
			Curve crvPlotted( "Data1_B" );
			GraphLayer gl( "Graph1", 0 );
			if(crvPlotted && gl)
			{
				int nIndex = curve_in_layer_get_index(crvPlotted, gl);
				out_int("The index of Data1_B in Graph1 is ", nIndex );
			}
		}
	Return:
		plot index if in layer, or -1 if not
*/
int curve_in_layer_get_index(const curvebase& cuv, const GraphLayer& gl);

/**	>Curve
		add to TreeNode the X and Y dataset names of the given curve
	Parameters:
		cuv = [in] Curve to get names from
		trNode = [out] tree node to add the needed info
	Example:
		//This example show the info. of the curve.
		//For this sample to work, dataset Data1_B should exist.
		#include <tree_utils.h>
		void run_set_curve()
		{
			Curve crvPlotted( "Data1_B" );
			Tree trNode;
			if(crvPlotted)
			{
				bool bOK = set_curve(crvPlotted, trNode);
				if(bOK)
				{
					out_tree(trNode);
				}
			}
		}
	Return:
		TRUE if success
	See Also:
		set_active_layer	
*/
bool set_curve(const curvebase& cuv, TreeNode& trNode);

/**	>Graph Window
		add to TreeNode the name of the active graph page and the active layer number
	Parameters:
		trNode = [out] tree node to add the needed info
	Return:
		TRUE if the active layer is a graphic layer, FALSE if no active graph layer available
	See Also:
		get_graph_layer	
*/
bool set_active_layer(TreeNode& trNode);

/**	>Graph Window
		retrive the graph layer from the tree node
	Parameters:
		trNode = [in] tree node that may contains the needed info prepared by set_active_layer
	Example:
		//This example get the active graph layer.
		void run_get_graph_layer()
		{
			GraphPage gp;
			gp.Create();
			
			Tree trNode;
			set_active_layer(trNode);				//Get the Info. of the active layer
			
			GraphLayer gl = get_graph_layer(trNode);
			
			ASSERT(gp.GetName() == gl.GetPage().GetName());
			
			out_str("The active graph layer is: " + gl.GetPage().GetName() + "(" + gl.GetIndex() + ")");
		}
	Return:
		a valid graph layer if successful
	See Also:
		set_active_layer	
*/
GraphLayer get_graph_layer(const TreeNode& trNode);

/**	>Graph Window
		set the given layer to be active layer, if page not open, will open it to be active page as well
	Parameters:
		layr = [in] typically a GraphLayer to be set as active
	Example:
		GraphLayer gl("Graph1",0);
		set_active_layer(gl);
	Return:
		true  if successful
	See Also:
		set_active_layer	
*/
bool set_active_layer(Layer& layr);

/**# >Curve
		prepare a treenode with proper info for the given curve
	Parameters:
		cuvInput = the data curve that we will be using
		trInput = a tree branch that has a Range1 branch that we will need to initialize
		i1 = beginning drawing range of the cuvInput
		i2 = ending drawing range of the cuvInput, inclusive
		hWndRet = only if bNeedInit = false, the graph window's handle where the cuvInput is activated
		bNeedInit = true will init the trInput node, = false will make use the trInput to activate the graph layer
	Example:
		//This example prepare the plotting of Data1_B.
		//For this sample to run, Data1_B should exist in the project.
		void run_set_curve_input()
		{
			Curve crv("Data1_B");
			Tree trInput;
			int iBegin, iEnd;
			HWND hWndRet;
			set_curve_input(crv, trInput, iBegin, iEnd, hWndRet);
			out_str("Data1_B will be plotted into "+trInput.Page.strVal);
		}
	Return:
		true  if the cuvInput is full range, false if cuvInput has selected subrange.
*/
bool set_curve_input(const curvebase& cuvInput, TreeNode& trInput, int& i1, int& i2, HWND& hWndRet, bool bNeedInit = true);

/** >Curve
		duplicate the active curve in the active graph layer so that analysis routine like derivatives and smoothing can be performed on the copied curve
	Parameters:
		lpcszNewColName = name of the Y column for the copied curve
		bReuseIfInWks = option to always create new copy of can reuse if named column is already in the same worksheet as the original
	Example:
		//This example copy the active curve Data1_B in Graph1.
		//For this sample to run, Data1_B and Graph1 should exist.
		void run_curve_duplicate_active()
		{
			Curve crv("Data1_B");
			GraphLayer gl("Graph1", 0);
			if(crv && gl)
			{
				add_curve_to_graph_layer(crv, gl);			//Add the curve to the graphlayer
				set_active_layer(gl);						//Set the graphlayer to active
				Curve crvCopy = curve_duplicate_active();
				if(crvCopy)
				{
					out_str("Copy successfully.");
				}
			}
		}
	Return:
		NULL if no active graph layer with active curve found, or if the operation failed
*/
curvebase& curve_duplicate_active(LPCSTR lpcszNewColName = NULL, bool bReuseIfInWks = false);

#endif //_ANALYSIS_UTILS_H