/*------------------------------------------------------------------------------*
 * File Name:Analysis_utils.c 													*
 * Creation: CPY 3/11/03														*
 * Purpose: Origin's basic internal analysis routines							*
 * Copyright (c) Originlab Corp.	2003										*
 * All Rights Reserved															*
 * 																				*
 * Modification Log:															*
 *	ML 5/24/2004 QA70-6376 REPORT_TABLE_MULTIHEADERS_MORE_WORK					*
 *	Forest 7/29/04 QA70-6067 BASELINE_SECOND_DERIVATIVE_METHOD                  *
 *	Forest 7/30/04 QA70-6077 PEAK_FIRST_DERIVATIVE_METHOD                       *
 *	YuI 8/13/04 QA70-4387 NLFITTER_OPERATION_IMPLEMENTATION						*
 *	Danice 9/9/04 v8.0130 REPLACE_DATASET_WITH_VECTORBASE						*
 *	ML 11/11/2004 QA70-6845 CELL_VALUE_LINKING									*
 *	Danice 11/15/04 QA70-7169 v8.0163 TREE_ROW_SHOW_ALTERNATE_COLORS			*
 *	ML 12/3/2004 QA70-6585 SETTING_NLSFTREE_ORIGINC_COMPILE						*
 *	Frank 12/14/04 QA70-6086 FO_CODE_CLEAN_UP									*
 *	Frank 12/21/2004 NLF_GET_FUNCTION_DETAIL									*
 *	DG 12/29/04 v8.0180 NLF_CLEAN_VISIT_INIFILE									*
 *	Frank 1/22/05 CENTRALIZE_MARCO_OF_FOLDER_INFO								*
 *	DSC 2/15/05 GET_ORIGIN_PATH													*
 *	DG 2/22/05 v8.0195 RECOGNIZE_FULL_PATH										*
 *	DSC 3/1/05 v8.0200 CENTRALIZE_PATH_CATEGORY_NAME_CODES						*
 *	ML 3/3/2005 XFUNCTION_OUTPUT_VARIABLES_DATA_ASSOC							*
 *	DSC 3/18/05 QA70-7518 v8.0207 LT_XF_COMMAND									*
 *	Echo 3/22/05 v8.0207 QA70-6204 ADD_GSD										*
 *	DSC 3/23/05 v8.0209 QA70-7518 LIST_XF										*
 *	CPY 4/5/05 XF_LT_CHECK_USAGE_CONTEXT										*
 *	Frank 4/7/05 OC_CONVERT_MATRIX_FUNCTION_MOVE_TO_VC_LEVEL_WITH_PREFIX_OCMATH	*
 *	EJP 2005-04-15 v8.0223 CLEAN_ACTIVE_IMPLEMENTATION							*
 *	DSC 6/15/05 FIX_DISPLAY_NAME_ORDER											*
 *	DSC 6/16/05 ADD_SORT_XF_LIST_OPTIONS										*
 *	DSC 6/27/05	CONSTRUCT_THEME_FILE_FROM_COMPOSITE_NAME						*
 *	DSC 6/29/05 QA70-7518 LISTXF_OXF											*
 *	DSC 7/13/05 QA70-7518 MORE_LISTXF_OUTPUT_FORMATTING							*
 *	DSC 7/28/05 SUPPORT_HELP_XF_IF_NOT_LT										*
 *	DSC 8/5/05 QA70-7518 ADD_HELP_J_G_TO_XF										*
 *	Kevin 08/09/05 ADD_A_CONDITION												*
 *	Kevin 08/18/05 ADD_VECTOR_TO_STRING											*
 *	DSC 8/18/05 AUTOCOMPLETE_XF_CMD_LINE_ARGUMENTS								*
 *	Kevin 08/29/05 MODIFY_VECTOR_TOSTRING										*
 *	Kevin 09/01/05 CHANGE_STATISTICS_NAME										*
 *	Kevin 09/02/05 ADD_FOR_SELECTED_NONE_COLUMN									*
 *	Iris 10/09/05 SUPPORT_AUTO_SELECTION_DATA_FOR_MULTI_REGRESSION				*
 *	YuI 10/10/05 QA70-8158 GENERAL_X_STRING_CONVENTION_TO_ACCESS_OBJECTS		*
 *	DSC 10/11/2005 QA70-8167 FIND_FILES_FROM_SPECIFIED_LOCATION					*
 *	Iris 10/18/05 UPDATE_MR_DATA_SELECTION_BETTER								*
 *	ML 10/20/2005 ANOVA_INPUT_DATA_TO_DATARANGE									*
 *	Iris 11/16/05 SUPPORT_GETTING_DATA_RANGE_FROM_MATRIX						*
 *	Echo 11/22/05 v8.0339 ADD_MODE												*
 *	Frank 11/23/05 ADD_NOISE_AND_SAVE_DATA										*
 *	Frank 11/25/05 MOVE_FDFTREE_RELATE_TO_ANALYSIS_ULTIS						*
 *	Leo 2005-12-16 MOVED_PEAK_AND_BASLINE_FUNCS_TO_PFM_UTILS					*
 *	Iris 12/19/05 QA70-6367-P8 DESC_STATS_ON_GRAPH								*
 *	Echo 1/6/05 UNADVANCED_RSQ_ADJRSQ											*
 *	ML 1/13/2006 XVARIABLEBASE_TO_VC											*
 *	DSC 2/14/06 QA70-7518 ALLOW_SEARCH_XF_BY_KEYWORDS							*
 *	DSC 2/27/06 ADD_OUTPUT_OPTION_TO_LIST_XF									*
 *	EJP 2006-03-29 v8.0382 QA70-8187 GET_DATA_VALUES_DIRECT_FROM_IMAGE			*
 *	Echo 5/31/06 RESET_STATS_ADVANCED_OPTIONS									*
 *	Sandy 07/06/06 GET_COL_ID_WITH_DIFFERENT_ID									*
 *	Max 7/13/06	COMMENT_ALL_ADVANCED_OPTIONS									*
 *	CPY 7/15/06 LR_FIT_USE_SOURCE_X_DATA_SHOULD_SORT_FIRST						*
 *	Sim 08-22-2006 ERR_MSG_SUPPORT_PARAM										*
 *	Sim 08-23-2006 WARNING_MSG_BOX												*
 *	Joe 8/28/06  MOVE_GET_X_FROM_Y_OR_GET_Y_FROM_X_TO_CALIBRATION				*	
 *  Joseph 09/18/06  MOVE_CALIBRATION_TABLE_OUT_OUTPUT							*
 *	Echo 9/25/06 CHANGE_DESC_STAT_FUNC_FOR_EFFICIENCY							*
 *	Arvin 9/28/06 ADD_WKS_HAS_TEXT_COL											*
 *	Arvin 9/28/06 ADD_WKS_HAS_COMPLEX_COL										*
 *  Joseph  10/9/06	SEPERATE_DESCIPTION_OR_COMBINE_DESCRIPTION_SECTION			*
 *	Cheney 2006-10-11 GET_DATA_FROM_DR_TO_MAT									*
 *	Cheney 2006-11-22 SHOULD_ALWAYS_USE_INPUT_Y_TO_GET_RESIDUAL					*
 *  Sim 12-08-2006 GET_LARGEST_GAP												*
 *	DSC 12/15/06 LIST_XF_IF_NOT_GRAPHIC_OBJECT_EVENT							*
 *  Iris 02/13/2007 v8.0560 SHOULD_NOT_PLOT_WEIGHT_UNLESS_COL_TYPE_IS_ERR		*
 *	Cheney 2007-4-27 STATAS_ON_ROW_NOT_NEED_WDF_ETC								*
 *  Iris 06/04/2007 MR_NEED_SUPPORT_Y_ERR_AS_WEIGHT								*
 *	Sim 06-19-2007 IMPROVE_WARNING_MESSAGE_POST_FUNCTION						*
 *	Arvin 07/06/07 v8.0654 NEED_USE_THIS_FUNCTION_IN_FITNL						*
 *	Cheney 2007-7-16 SHOULD_GENERATE_X_DATA_UNIFORMLY_SPACED_IN_LOG_SPACE_WHEN_LOG_TYPE 
 *	Echo 7/31/07 QA70-8679 P11 R2M_AUTO_UPDATE_NOT_SUPPORT_SELETED_RANGE		*
 *	ML 7/31/2007 QA70-10137 MUST_SUPPLY_PARENT_FOR_MESSAGEBOX					*
 *	Arvin 08/29/07 QA70-10073-P6 KEEP_CUSTOMIZATION_SETTINGS_FOR_SOURCE_DATA_PLOT_AFTER_RECALCULATE
 *	Cloud 8/30/07 ADD_OPTION_OF_NOISE											*
 *	Folger 09/03/07 CORRECTLY_GET_ORIGIN75_PATH									*
 *	Jasmine 09/04/07 SHOULD_TRY_TO_GET_INI_PATH_FROM_NPATHTYPE					*
 *	Cheney 2007-9-7 SHOULD_ALLOW_DUP_DATASET_PLOT_FOR_PREVIEW_SO_AS_TO_SAME_WITH_FIT_THINGS
 *	Cheney 2007-9-19 QA70-10399-P2 IF_LOG_SCALE_SHOULD_REPLACE_ALL_VALL_LESS_AND_EQUAL_0
 *	Jasmine 09/26/07 SEARCH_THE_RIGHT_NLSF_INI									*
 *	Jasmine 09/30/07 QA70-10462 CHECK_REPLACE_USER_FITTING_FUNCTION_TO_GROUP_ONE*
 *	Jasmine 10/08/07 QA70-10462 SEEK_SHARED_FDF_IN_SHARED_NLSF_INI				*
 *	Arvin 10/19/07 USER_SETTED_PERCENTILES_SHOULD_NOT_EFFECT_HISTOGRAM_AND_BOX_CHART_GRAPHS as max said
 *	Sim 11-05-2007 IMPROVE_LOCALIZATION_ISSUE									*
 * 	Arvin 11/09/07 NLFIT_REPORT_GRAPH_NEED_RESCALE_WHILE_PART_GRAPH_SELECTION_AS_INPUT
 *	Arvin 11/16/07 QA70-10676 UPDATE_MEANS_COMPARISION_PLOT_LEGENT_WITH_SIG_INFO*
 *	Arvin 11/22/07 NEED_RESCALE_FOR_3D_FITTINGS									*
 *	Sim 12-05-2007 REWRITE_WKS_DATA_RANGE_TO_MAT								*
 *	ML 12/17/2007 INITIALIZING_PEAK_POSITIONS_FOR_REPLICAS_FITTING				*
 *	Cloud 12/18/07 TREAT_SIGN_OF_PEAK_AREA										*
 *	Sim 12-20-2007 QA80-10842 FIX_NOT_USE_ALL_ROWS								*
 *	Sim 01-17-2007 QA80-10952 MULTI_SHEET_RANGE									*
 *	Arvin 03/13/08 REPLICA_AUTO_INIT_PARAMS_FOR_SURFACE_FIT						*
 *	Folger 03/18/08 QA80-11260 FIX_RUNTIME_ERROR_IN_MATRIX_FITTING_PREVIEW_AND_REPORT_GRAPH
 *	Hong 03/26/08 v8/0832b PA_PEAKS_DEL_REQUIRE_FOLLOWING_DATA_SUPPORT			*
 *	Hong 03/26/08 QA80-11323 v8.0832b PA_PEAKS_ADD_DONE_DIALOG_WHEN_MODIFY		*
 *	Max 3/27/08 QA70-11325 v8.0833 COMMA_IS_MISSING_IN_FIRST_LINE				*
 *	Max 3/27/08 QA70-11325 v8.0833 TTEST_IS_REPLACED_WITH_XF_AND_CONTROL_BECOMES_USELESS
 *	Hong 04/09/08 QA80-11395 FIX_FIT_CURVE_IN_REPORT_FAIL_RESCALE_WHEN_NO_OVERLAP
 *	Sim 04-28-2008 FIX_INVOKE_WKS_GET_BOUND										*
 *	Jasmine 05/04/08 USE_MIN_VALUE_IF_FIT_CURVE_POINT_IS_ONE				 	*
 *	Echo 5/20/08 v8.0867 HANDLE_WHOLE_WORKSHEET_SELECTED						*
 *	Folger 08/29/08 QA80-12120 CENTRALIZE_XF_ERROR_MSEEAGE_PARSING_CODE			*
 *	CPY 9/23/08 CURVE_INTEG_TOOL_OUTPUT_TO_WKS_USING_STANDARD_RERPOT_TREE		*
 *	Kyle 09/24/08 SET_NO_NEED_TO_LOCALIZE										*
 *	Fisher 2008-9-24 WHEN_THE_FIRST_COLUMN_HAS_NO_DATA_THE_WKS_SIZE_WILL_NOT_INCREASE
 *	YuI 09/24/08 QA70-12265 P3 DataPlot::GetDataPoints_TO_RETURN_VECTOR_OF_INDECES*
 *	YuI 10/08/08 QA70-12350-P3 QUICK_TOOL_MUST_FOLLOW_THE_SAME_LOGIC_AS_RECT_SELECTOR*
 *	YuI 10/09/08 QA70-12265-P3 CURVE_STATS_QUICK_TOOL_MUST_BE_SMARTER			*
 *	Sophy 10/28/2008 SHOULD_REARRANGE_LAYERS_WHEN_SWITCH_BETWEEN_SINGLE_INDEP_N_DEP_AND_MULTI_IDEP_N_DEP
 *	Folger 11/06/08 QA80-12538 v0.966 CENTRALIZE_CODE_ABOUT_GETTING_GUI_REPLICA_NODE
 *	Kyle 11/18/2008 QA80-12591 ADD_MORE_OPTION_FOR_INPUT_DATA_TYPE_IN_FITTING_TOOLS
 *	Hong 11/19/08 QA80-12558 V8.90875 DESC_STATS_ON_COL_SUPPORT_DATA_FROM_GRAPH	*
 *  Iris 11/19/2008 v8.0975 QA80-12591-P2 FIX_APPARENT_FIT_ON_GRAPH_CUSTOM_RANGE_GET_INCORRECT_X*
 *  Iris 11/20/2008 v8.0975d QA80-12591-S1 CENTRALIZE_NEW_X_DATA_TYPE_AND_LOG_TYPE_CODES		*
 *	Sophy 11/21/2008 v8.976 QA80-12591-P3 ADD_ERRMSG_WHEN_FITCURVE_XDATATYPE_IS_LOG_WITH_NEGATIVE_INPUT copy from nlfcurves.h need to centralize
 *	Folger 12/03/08 QA80-12642 v8.0982 CONSISTENT_COMMENT_DISPLAY_FOR_COLUMN_NAME_IN_STATISTICS_TOOLS
 * 	Jasmine 12/04/08 v8.0982b GET_PLOT_COLUMNS									*
 *	Folger 12/18/08 QA80-12642 v8.0988 SUPPORT_MAKE_COLUMN_NAME_WITH_LN_ONLY	*
 *	Folger 12/26/08 QA80-12642 v8.0991 CONSISTENT_SHOW_LN_ONLY_IF_EXIST_FOR_STATISTICAL_TOOLS
 *	Kyle 01/05/2009 FIX_RANGE_NOT_ALL_COLUMN_SELECTED							*
 *	Sophy 1/9/2009 v8.0995 QA80-12591-P7 FIX_USE_SOURCE_GRAPH_SCALE_TYPE_GIVE_WRONG_RESULT
 *	Folger 01/04/09 QA80-12962 GENERATE_INDIVIDUAL_FIT_CURVE_USING_INDEPENDENT_X_IN_PA
 *  Iris 01/15/2009 FIX_NLFIT_PREVIEW_NOT_RESCALE_FOR_LOG_SCALE_TYPE			*
 *	Sophy 1/15/2009 v8.0957 FIX_CALCULATE_WRONG_RESULT_WHEN_XDATATYPE_IS_LOG_AND_EXPAND_TO_FULL_RANGE
 *	Iris 1/16/2009 ROLLBACK_SOPHY_CODES_TO_FIX_LOG_X_TYPE_BUG					*
 *	Folger 01/04/09 QA80-12962 MORE_WORK_ON_GENERATE_INDIVIDUAL_FIT_CURVE_USING_INDEPENDENT_X_IN_PA
 *	Sophy 1/20/2009 v8.0961 FIX_MATRIX_FIT_FAIL_TO_PLOR_SOURCE_DATA_ON_REPORT_GRAPH
 *	Folger 02/16/09 QA80-10399-P2 FITCURVE_IS_MISSING_IN_NLFIT_REPORT_WHEN_INPUT_X_HAS_NEGATIVE_DATA_FOR_LOG10_SCALE_TYPE
 *	Kyle 02/24/2009 QA80-13169 CHECK_IF_TWO_RANGE_HAVE_COMMON_RANGE				*
 *	Kyle 02/26/2009 CENTRALIZE_CODE_TO_TRIM_INDEPENDENT							*
 *	Hong 03/09/09 QA80-13243 IMPROVE_AVE_XY_AVOID_MEMORY_LEAKING				*
 *	Kyle 03/17/2009 QA80-12564-P7 GENERAL_ERROR_MESSAGE_FOR_X_RANGE_ERROR		*
 *	EJP 2009-03-17 QA80-13301 PREVENT_READING_VALUE_FROM_BRANCH					*
 *	Hong 03/25/09 QA80-12551 RESET_PLOT_ROW_RANGE_IF_USER_UPDATE_INPUT_DATARANGE_WHEN_CHANGE_PARAMETER
 *  Iris 3/25/2009 	QA80-12591-P9 FIX_WRONG_FITX_DATA_IF_LOG_TYPE_AND_SPAN_FULL_RANGE
 *  Iris 3/26/2009 QA80-13343 KEEP_INPUT_DATA_ORDER_IF_FITX_SAME_AS_INPUT_DATA	*
 *	Sim 04-02-2009 QA80-13403 ADD_INDIVIDUAL_OP_VERSION_FOR_CONVERT_GUI_TREE	*
 *	Kyle 06/01/2009 USE_COMMA_AS_DELIMITER_IN_GOS								*
 *	Sim 09-25-2009 QA80-13783 PROTOTYPE_WITH_TWO_PRECISION_FOR_XYZ_REMOVE_DUPLICATE
 *	Sim 09-25-2009 QA80-13783 CHANGE_PROTOTYPE_AND_ALGORITHM_FOR_EXAMINATION
 *	Kyle 07/14/2009 QA80-13746 SHOWING_BOTH_LN_AND_SN_WITH_STANDARD_NOTATION_IN_THE_MENU_OF_SET_COLUMN_VALUE_DLG
 *	Kyle 07/20/2009 QA80-13746-P2 SCV_COL_COLUMN_BROWSER_AND_RANGE_BROWSER_SHOW_SN_LN_CLEANUP
 *	Sophy 8/17/2009 QA80-14087-P3 NORMALIZE_WITH_SELECTED_POINT_FROM_GRAPH_GET_WRONG_RESULT
 *	Jasmine 08/24/09 QA81-14165 DISPLAY_BUT_DISABLE_PRO_OPTION_IN_REG_VERSION	*
 *	Iris 8/31/2009 QA80-14209 IMPROVE_CREATE_MASKED_MISSING_DATA_TABLE_SPEED	*
 *  Iris 9/14/2009 QA80-14165-P4 NOT_SHOW_PRO_OPTION_WHEN_CHANGE_PARAM_ON_80SR6_OPJ
 *	Jasmine 10/19/09 QA81-14478 MASK_DATA_OUSIDE_SD								*
 *  Iris 10/23/2009 QA80-14512 FIT_LINEAR_X_ERR_SHOULD_BE_PRO					*
 *	Folger 11/26/09 GENERATE_INDIVIDUAL_FIT_CURVE_USING_INDEPENDENT_X_FAILS_FOR_LOG_X_DATA_TYPE
 *	CPY 11/27/09 QA81-14661-P5 LEVEL_CROSSING_NEED_OPTION_TO_SKIP_NOISY_SPIKES	*
 *	Folger 11/28/09 QA81-14661-P6 LEVELCROSSING_XINDEX_OUTPUT_SHOULD_ONE_OFFSET	*
 *	Folger 11/30/09 CENTRALIZE_CODE_ABOUT_FIND_ROOTS							*
 *	Folger 11/30/09 QA81-14661-P10 ORIGIN_CRASH_IF_TOO_MANY_EXACT_CORRSING_POINTS
 *	Folger 11/30/09 QA81-14661-P11 CORRECT_DURATION_POINT_CALCULATION_ALGORITHM	*
 *	Sophy 12/14/2009 QA80-14686-P2 DATAPLOT_GET_DATAPOINTS_NEED_TO_SKIP_MASKED_DATA
 *	Kyle 12/16/2009 QA80-14832 QUICK_FIT_EDIT_DIALOG_NEED_FUNC_LIST_WITHOUT_ARGUMENT
 *	Sophy 12/17/2009 QA80-14598-P3 AUTO_SIZE_RESULT_COLUMN_FOR_BETTER_VIEW		*
 *	Folger 12/19/09 QA81-14661-P14 XINDEX_IS_WRONG_ON_INPUT_DATA_WITH_MISSING_VALUE
 *	Kyle 12/28/2009 QA80-14832 QUICK_FIT_CHECK_INVALID_MIN_MAX_SETTINGS			*
 *	Folger 12/29/09 QA81-14832 SET_GET_QUICK_FIT_SOURCE_PLOT_UID				*
 *	Folger 01/21/09 QA81-14995 GADGET_TOOLS_SHOULD_USE_SAME_WORKSHEET_OUTPUT_NOTATION
 *	Hong 01/20/10 QA80-14832 QUICK_FIT_SUPPORT_OUTPUT_RESULTS_TO_WKS			*
 *  Iris 1/25/2010 FINDXY_OUTPUT_WKS_NEED_KEEP_SAME_FORMAT_WITH_SOURCE_X_COL	*
 *	Sophy 1/26/2010 ADD_GO_TO_REPORT_TABLE_MENU_IN_CONTEXT						*
 *	Folger 01/28/10 SHOW_SNAP_TO_DATAPLOT_ON_MAIN_DIALOG						*
 *	Iris 2/02/2010 QA81-14165 ALSO_SHOW_PRO_IN_PRO_VERSION						*
 *	Folger 02/02/10 QUICKFIT_SUPPORT_WITHOUT_ROI_BOX_WITH_CNTRL_KEY_DOWN		*
 *	Sophy 3/3/2010 QUICKFIT_REPORT_TO_WKS_SHOULD_APPLY_SIGNIFICANT_SETTINGS		*
 *	Kyle 03/12/2010 PICK_PEAK_SHARE_CODE_WITH_QUICK_FIT							*
 *  Iris 3/16/2010 QA81-10824 TRIM_UNUSEFUL_MISSING_DATA_IN_OUTPUT_COLUMNS		*
 *  Iris 3/23/2010 QA81-10824-P1 TRIM_UNUSEFUL_MISSING_DATA_IN_OUTPUT_COLUMNS_MORE
 *	Folger 04/08/10 BETTER_COLUMN_WIDTH_RESIZING_FOR_GADGET_TOOLS_REPORT		*
 *	Sophy 4/13/2010 QA81-15308 GET_GRAPHLAYER_OF_INPUTDATA_IN_XF_BODY			*
 *  Jacky 7/14/2010 ORG-545-P2 FIX_CATEGORY_ALWAYS_THE_LAST_USED_ONE            *
 *	Folger 09/10/2010 ORG-1025 XF_ERROR_MESSAGE_SUPPORT_MORE_STR_ARGS			*
 *	Sophy 10/15/2010 ORG-1270 OUTPUT_TREE_TO_EXCEL_WORKSHEET_RUNTIME_ERROR		*
 *------------------------------------------------------------------------------*/
 
#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
 
//#include "FunctionGroup.h"
#include <FDFTree.h>
#include "XFunctionEx.h"
#include <ocu.h>
#include <oErrMsg.h>
#include <okocUtils.h> ///---Sim 06-19-2007 IMPROVE_WARNING_MESSAGE_POST_FUNCTION
#include <..\OriginLab\fft.h>
#include <fft_utils.h>
//#include <..\OriginLab\graph_utils.h> ///Sophy 1/9/2009 v8.0995 QA80-12591-P7 FIX_USE_SOURCE_GRAPH_SCALE_TYPE_GIVE_WRONG_RESULT
//#include <ocMath.h>
//#include <Range.h>
#include <wks2mat.h> ///---Sim 09-25-2009 QA80-13783 CHANGE_PROTOTYPE_AND_ALGORITHM_FOR_EXAMINATION
//#include <OCTreeUtils.h> /// Iris 8/31/2009 QA80-14209 IMPROVE_CREATE_MASKED_MISSING_DATA_TABLE_SPEED

////////////////////////////////////////////////////////////////////////////////////
#define _DBINT(_STR, _INT)	//out_int(_STR, _INT);
#define _DBMGS(_STR)		//out_str(_STR);

#define MAX_MATRIX_POINTS 10000000

bool set_curve_input(const curvebase& cuvInput, TreeNode& trInput, int& i0, int& imax, HWND& hWndRet, bool bNeedInit)// = true);
{
	bool bFullRange = cuvInput.GetSourceRange(i0, imax)? false:true;
	if(bNeedInit) // not supplied, we need to init trInput
	{
		set_active_layer(trInput);
		set_curve(cuvInput, trInput);
	}
	else
	{	
		if(!trInput.Range1.UseRange.IsValid())
			trInput.Range1.UseRange.nVal = 0;
		
		bFullRange = trInput.Range1.UseRange.nVal > 0? false:true;
		// we need to set cuvInput active
		GraphLayer gl = get_graph_layer(trInput);
		if(gl)
		{
			set_active_layer(gl);
			Page pg = gl.GetPage();
			if(pg)
				hWndRet = pg.GetWindow().GetSafeHwnd();
			
			string str;
			str.Format("set %s -a",cuvInput.GetName());
			gl.LT_execute(str);
		}
	}
	return bFullRange;
}

//	add to TreeNode the X and Y dataset names of the given curve
bool set_curve(const curvebase& cuvInput, TreeNode& trNode)
{
	if(!cuvInput)
		return false;
	if(!trNode)
		return false;

	int	i0 = 0, imax = 0;
	trNode.Range1.UseRange.nVal = cuvInput.GetSourceRange(i0, imax) ? 1 : 0;
	trNode.Range1.R1.nVal = cuvInput.GetLowerBound();
	trNode.Range1.R2.nVal = cuvInput.GetUpperBound();

	trNode.Range1.Ydata.strVal = cuvInput.GetName();
	Dataset dsX;
	if(cuvInput.AttachX(dsX))
		trNode.Range1.Xdata.strVal = dsX.GetName();
	else
		trNode.Range1.Xdata.strVal = "";
	return true;
}

// add to TreeNode the name of the active graph page and the active layer number
bool set_active_layer(TreeNode& trNode)
{
	GraphPage gpg = Project.Pages();
	if(gpg)
	{
		GraphLayer glyr = Project.ActiveLayer();
		trNode.Page.strVal = gpg.GetName();
		trNode.Layer.nVal = glyr.GetIndex();
		return true;
	}
	return false;
}

//set the given layer to be active layer, if page not open, will open it to be active page as well
bool set_active_layer(Layer& layr)
{
	if(!layr)
		return false;
	
	Page pg = layr.GetPage();
	if(pg)
	{
		pg.SetShow();
		string strTemp = "page.active=";
		strTemp += layr.GetIndex() + 1;//need to labtalk is 1 offset
		
		pg.LT_execute(strTemp);
		return true;
	}
	return false;
}

//	retrive the graph layer from the tree node
GraphLayer get_graph_layer(const TreeNode& trNode)
{
	GraphLayer gl;
	if(trNode)
	{
		TreeNode trPage = trNode.Page;
		if(trPage)
		{
			GraphPage gpg(trPage.strVal);
			if(gpg)
			{
				TreeNode trTemp = trNode.Layer;
				if(trTemp)
				{
					int nIndex = trTemp.nVal;
					if(nIndex >= 0 && nIndex < gpg.Layers.Count())
					{
						gl = gpg.Layers(nIndex);
						return gl;
					}
				}
			}
		}
	}
	return gl;
}


int curve_in_layer_get_index(const curvebase& cuv, const GraphLayer& gl)
{
	if (!cuv.IsValid())		//Leo 08/26/05 QA70-6960 RUN_TIME_ERROR_WHEN_CURVE_IS_INVALID
		return -1;
	
	int ii = 0;
	foreach(DataPlot dp in gl.DataPlots)
	{
		string strName = dp.GetDatasetName();
		if(strName.CompareNoCase(cuv.GetName()) == 0)
			return ii;
		
		ii++;
	}
	return -1;
}


BOOL	curve_in_page_get_indices(const curvebase& cuv, const GraphPage &gpg, int *pnLayerIndex, int *pnDataPlotIndex)
{
	int		nLayer = 0;
	foreach(GraphLayer gl in gpg.Layers)
	{
		int		nDPIndex = curve_in_layer_get_index(cuv, gl);
		if (0 <= nDPIndex)
		{
			if (pnLayerIndex)
				*pnLayerIndex = nLayer;
			if (pnDataPlotIndex)
				*pnDataPlotIndex = nDPIndex;
			
			return TRUE;
		}
		
		++nLayer;
	}
	
	if (pnLayerIndex)
		*pnLayerIndex = -1;
	
	if (pnDataPlotIndex)
		*pnDataPlotIndex = -1;
	
	return FALSE;
}

	
//	Search the layer where both two curves are both plotted 
//glayerFound is layer where cuv2 is currently plotted
bool curve_both_in_layer(const curvebase& cuv1, const curvebase& cuv2, GraphLayer& glayerFound)
{
	//----
	// starting layer given, then we should not search if cuv2 already in that layer and 
	// we can assume this is the layer we want
	if(glayerFound && curve_in_layer_get_index(cuv2, glayerFound) >= 0) 
		return curve_in_layer_get_index(cuv1, glayerFound) < 0? false:true;
	//----	
		
	foreach (GraphPage pg in Project.GraphPages)
	{
		foreach(GraphLayer gl in pg.Layers)
		{
			int nCuv2 = curve_in_layer_get_index(cuv2, gl);
			if(nCuv2 < 0)
				continue;
			glayerFound = gl;
			
			int nCuv1 = curve_in_layer_get_index(cuv1, gl);
			if(nCuv1 >= 0)
				return true;
		}
	}
	return false;
}

string curve_get_wks_col_names(curvebase& cc, string& strX, string& strY)
{
	string strTemp;
	string strWks;
	strY.Empty();
	strX.Empty();
	
	if(cc)
	{
		cc.GetName(strTemp);
		int nn = strTemp.Find('_');
		if(nn > 0)
			strWks = strTemp.Left(nn);
		
		Worksheet wks(strWks);
		if(!wks)
		{
			strWks.Empty();
			return strWks;
		}
			
		strY = strTemp.Mid(nn+1);
		
		if(cc.HasX(strTemp))
		{
			nn = strTemp.Find('_');
			strX = strTemp.Mid(nn+1);
		}
	}
	
	return strWks;
}

static bool check_set_col_analysis(Column& cc, bool bSetAsY = true)
{
	int nFormat = cc.GetFormat();

	if(bSetAsY && cc.GetType() != OKDATAOBJ_DESIGNATION_Y)
		cc.SetType(OKDATAOBJ_DESIGNATION_Y);
	
	if(nFormat != OKCOLTYPE_NUMERIC && nFormat != OKCOLTYPE_TEXT_NUMERIC)
	{
		cc.SetFormat(OKCOLTYPE_NUMERIC);
		return true;
	}
	return false;
}

/** >Analysis
		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
	Return:
		NULL if no active graph layer with active curve found, or if the operation failed
*/
curvebase& curve_duplicate_active(LPCSTR lpcszNewColName , bool bReuseIfInWks) //= NULL,  = false
{
	GraphPage gpg = Project.Pages();
	string strNewCol = lpcszNewColName;
	if(strNewCol.IsEmpty())
		strNewCol = "A";
	
	if(gpg)
	{
		GraphLayer glyr = Project.ActiveLayer();
		if(glyr)
		{
			string strY, strX;
			string strWks = curve_get_wks_col_names(Project.ActiveCurveBase(), strX, strY);
			if(!strWks.IsEmpty())
			{
				Worksheet wks(strWks);
				Column cx = wks.Columns(strX);
				Column cy = wks.Columns(strY);
				Column cNew = wks.Columns(strNewCol);
				if(!bReuseIfInWks || !cNew.IsValid())
				{
					string strTemp;
					wks.InsertCol(cy.GetIndex()+1, strNewCol, strTemp);
					strNewCol = strTemp;
					cNew = wks.Columns(strNewCol);
				}
				// we need to make sure new col is a Y col
				check_set_col_analysis(cNew);
				cNew.SetUpperBound(cy.GetUpperBound());
				return wks.GetCurve(cx.GetIndex(), cNew.GetIndex());
			}
		}
	}
	return NULL;
}

bool	add_curve_to_graph_layer(curvebase& cuv, GraphLayer &grl, int nColor, bool bRescale, int nPlotType)
{
	int			nPlot = grl.AddPlot(cuv, nPlotType);
	DataPlot	dp = grl.DataPlots(nPlot);
	dp.SetColor(nColor);

	if( bRescale )
		grl.Rescale();

	return true;
}


/**
*/
bool	curve_update_in_page(curvebase& cuv, int nColor, GraphPage &gpg, bool bRescale)
{
	if (!curve_in_page_get_indices(cuv, gpg))	// not present
	{
		GraphLayer		grl = gpg.Layers(0);
		if (!grl)
			return false;
		
		return add_curve_to_graph_layer(cuv, grl, nColor, bRescale);
	}
	else
		gpg.Refresh();

	return true;
}


// find the input curve layer and plot the result curve
bool curve_update(curvebase& cuvFit, const curvebase& cuvInput, const TreeNode& trInput, int nColor, bool bRescale) // bRescale = false
{
	GraphLayer gl = get_graph_layer(trInput);
	if(!curve_both_in_layer(cuvFit, cuvInput, gl) && gl) // there maybe many, we will just use 1st for now
	{
		/*
		// cuvInput 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(nColor);

		if( bRescale )
			gl.Rescale();
		*/

		return add_curve_to_graph_layer(cuvFit, gl, nColor, bRescale);
	}
	return false;
}

///Frank 4/7/05 OC_CONVERT_MATRIX_FUNCTION_MOVE_TO_VC_LEVEL_WITH_PREFIX_OCMATH
///These functions are replace by VC level functions, with prefix ocmath_, so the OC code should be comment...
/*
int convert_regular_xyz_to_matrix(vector& vecX, vector& vecY, vector& vecZ, matrix& matData, double& dXmin, double& dXmax, double& dYmin, double& dYmax, bool bCheckData)
{
	bool bRet;
	int ii, jj, id, isize, iXStepLoc, iXNumSteps, iYStepLoc, iYNumSteps, iRet;
	double dev, dXStep, dYStep;

	// Check if X,Y,Z vectors are of equal length - return if not
	int iXsize = vecX.GetSize();
	int iYsize = vecY.GetSize();
	int iZsize = vecZ.GetSize();
	if(iXsize < 4) return 1;		// support only matrix size of greater than 2x2
	if( (iXsize != iYsize) || (iYsize != iZsize) || (iZsize != iXsize) ) return 1;

	// Create a temporary worksheet, and fill in the vectors into first three columns
	Worksheet wksTemp;
	bRet = wksTemp.Create("Origin.otw", CREATE_TEMP);
	string strWksName;
	wksTemp.GetPage().GetName(strWksName);
	wksTemp.DeleteCol(0);
	wksTemp.DeleteCol(0);
	wksTemp.AddCol();
	wksTemp.AddCol();
	wksTemp.AddCol();
	Dataset dsX(wksTemp, 0);
	Dataset dsY(wksTemp, 1);
	Dataset dsZ(wksTemp, 2);
	dsX = vecX;
	dsY = vecY;
	dsZ = vecZ;

	// Create two more columns in temp worksheet for computing x/y step values
	wksTemp.AddCol();
	wksTemp.AddCol();
	Dataset dsXMed(wksTemp, 3);
	Dataset dsYMed(wksTemp, 4);

	// Find step location, number of steps, and step value for x data
	iRet = convert_regular_find_step(0, wksTemp, iXStepLoc, iXNumSteps, dXStep);
	if(iRet != 0) return 2;

	// Find step location, number of steps, and step value for y data
	iRet = convert_regular_find_step(1, wksTemp, iYStepLoc, iYNumSteps, dYStep);
	if(iRet != 0) return 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 ( (iXStepLoc != iYNumSteps) | (iYStepLoc != iXNumSteps) ) return 3;

	// If data should be checked for iregularity, then...
	if(bCheckData)
	{
		// Check for x data for deviations
		iRet = convert_regular_check_data(0, wksTemp, iXStepLoc, iXNumSteps, dXStep);
		if(iRet != 0) return 4;

		// Check for y data for deviations
		iRet = convert_regular_check_data(1, wksTemp, iYStepLoc, iYNumSteps, dYStep);
		if(iRet != 0) return 4;
	}

	// Now sort the worksheet wrt Y ascending as primary and and
	// X ascending as secondary - this format is needed to copy to matrix
	using sort = LabTalk.sort;
	sort.wksname$ = strWksName;
	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 fill matrix with Z data, and return coordinate values in variables passed
	matData.SetSize(iXStepLoc, iYStepLoc);
	// Get min. max values for use in setting matrix co-ordinates
	dXmin = min(dsX);
	dXmax = max(dsX);
	dYmin = min(dsY);
	dYmax = max(dsY);
	// Fill matrix by just using Z dataset
	matData.SetByVector(dsZ);

	// Success
	return 0;
}
#ifdef _O_NAG_H



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, double dQIL, double dWFL)
{
	int i, j, m, n, nx, ny, iRet, iErr = 0;
	double xlo, xhi, ylo, yhi;

	// Get number of scatter points, and compute grid size accordingly
	m = vecZ.GetSize();
	nx = 2 * sqrt(m);
	ny = nx;

	// Set up NAG structures for calling gridding routine
	Nag_Scat_Struct comm;
	Nag_2d_Scat_Method method;
	Nag_E01_Opt optional;
	if(iMethod == 0) method = Nag_RC;
	else method = Nag_Shep;

	// Call the appropriate interpolation routine
	if (iMethod == 0)
	{
		// Renka-Cline method
		if( (iRet = nag_2d_scat_interpolant(method, m, vecX, vecY, vecZ, &comm, NULL)) != 0) iErr = iRet;
	}
	else
	{
		// Default values of parameters for Shepard's method
		double nq = 24.0;
		double nw = 12.0;
		double rnq = -1.0;
		// Scale defaults with values passed by user; leave rnq as is
		nq *= dQIL;					// Quadratic Interpolant Locality
		nw *= dWFL;					// Weight Function Locality
		// Set these values for the conversion method
		optional.nq = nq;
		optional.nw = nw;
		optional.rnq = rnq;
		if( (iRet = nag_2d_scat_interpolant(method, m, vecX, vecY, vecZ, &comm, &optional)) != 0) iErr = iRet;
	}

	// Get lo and hi values for x and y data
	xlo = min(vecX);
	ylo = min(vecY);
	xhi = max(vecX);
	yhi = max(vecY);

	// Define vectors for gridded data, and set their size
	vector vecGX, vecGY, vecGZ;
	vecGX.SetSize(nx * ny);
	vecGY.SetSize(nx * ny);
	vecGZ.SetSize(nx * ny);

	// Compute positions of the grid points using the hi and lo scatter values
	n = 0;
	for (j = 0; j < ny; ++j)
	{
		for (i = 0; i < nx; ++i)
		{
			vecGX[i + nx * j] = (1.0 * (nx - i - 1) / (nx - 1)) * xlo + (1.0 * i / (nx - 1)) * xhi;
			vecGY[i + nx * j] = (1.0 * (ny - j - 1) / (ny - 1)) * ylo + (1.0 * j / (ny - 1)) * yhi;
			++n;
		}
	}

	// 	Evaluate two-dimensional interpolant function computed by the interpolant function call
	if( (iRet = nag_2d_scat_eval(&comm, n, vecGX, vecGY, vecGZ)) != 0)
	{
		if(iRet != 179) iErr = iRet;
	}

	// Clean up
	//--- CPY 8/4/03 saw this when doing build 644 of Origin 75
	//iRet = nag_2d_scat_free(&comm);]
	nag_2d_scat_free(&comm);
	//--- 
	// Take gridded data and create a matrix by calling regular conversion
	if((iErr == -251)||(iErr == -249)||(iErr == 0))
	{
		if( ( iRet = convert_regular_xyz_to_matrix(vecGX, vecGY, vecGZ, matResult, dXmin, dXmax, dYmin, dYmax, false)) != 0) iErr = -1;
	}

	// Return iErr
	return iErr;
}
#endif //#ifdef _O_NAG_H





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)
{
	
	waitCursor hourGlass; 
	// Check if X,Y,Z vectors are of equal length - return if not
	int iXsize = vecX.GetSize();
	int iYsize = vecY.GetSize();
	int iZsize = vecZ.GetSize();

	if( (iXsize != iYsize) || (iYsize != iZsize) || (iZsize != iXsize) ) return -1;

	// Create a temporary worksheet to hold result data
	Worksheet wksTemp;
	bool bRet = wksTemp.Create("Origin.otw", CREATE_TEMP);
	string strWksName;
	wksTemp.GetPage().GetName(strWksName);
	wksTemp.DeleteCol(0);
	wksTemp.DeleteCol(0);
	wksTemp.AddCol();
	wksTemp.AddCol();
	wksTemp.AddCol();
	Dataset dsXresult(wksTemp, 0);
	Dataset dsYresult(wksTemp, 1);
	Dataset dsZresult(wksTemp, 2);

	// Compute length for temp worksheet columns
	int ii, jj, count;
	int ixlen = nint( 1.0 + (dXend - dXbegin) / dXstep);
	int iylen = nint( 1.0 + (dYend - dYbegin) / dYstep);
	int iSize = ixlen *iylen;
	if(iSize > MAX_MATRIX_POINTS) return -2;			// too may points - step size may be wrong
	
	dsXresult.SetSize(iSize);
	dsYresult.SetSize(iSize);
	dsZresult.SetSize(iSize);

	// Fill Z with missing values to start with
	dsZresult = NANUM;

	// Fill x, y columns of temp worksheet with cyclical values
	for (ii = 0; ii < ixlen; ii++)
	{
		for (jj = 0; jj < iylen; jj++)
		{
			dsYresult[ii * iylen + jj] = dYbegin + jj * dYstep;
		}
	}
	for (ii = 0; ii < ixlen; ii++)
	{
		for (jj = 0; jj < iylen; jj++)
		{
			dsXresult[ii * iylen + jj] = dXbegin + ii * dXstep;
		}
	}

	// Now read thru data in temp worksheet and try filling result vectors
	int ilen = iXsize;
	for (ii=0, count=0; ii < ilen; ii++)
	{
		// Get input x,y, z values
		double dX = vecX[ii];
		double dY = vecY[ii];
		double dZ = vecZ[ii];

		// Find the first occurance of the x value in the temporary worksheet
		int ixRow = Data_list(dX, &dsXresult, iPrecision);

		// If found, continue to look for y value occurance
		if(ixRow != -1)
		{
			// Find the range of rows with this x value
			double dXFound = dsXresult[ixRow];
			for(int ij = ixRow; ij < ixRow + iylen; ij++)
			{
				if(dsXresult[ij] != dXFound) break;
			}

			// Set upper and lower bounds of y dataset to range of x matches found
			dsYresult.SetLowerBound(ixRow);
			dsYresult.SetUpperBound(ij - 1);

			// Find the first y value that matches, in the above range
			int iyRow = Data_list(dY, &dsYresult, iPrecision);

			// If match found, put in the z value into the temporary worksheet at this row
			if(iyRow != -1) dsZresult[iyRow] = dZ;
			// If no match for y, increment count of bad points
			else count++;

			// Reset bounds on y dataset
			dsYresult.SetLowerBound(0);
			dsYresult.SetUpperBound(iSize - 1);
		}
		// If no match for x, increment count of bad points
		else count++;
	}

	// Now fill matrix by using Z dataset
	matResult.SetSize(iylen, ixlen);
	matResult.SetByVector(dsZresult,false);

	// Return number of discarded points
	return count;
}




int convert_sparse_find_min_max_step(vector& vec, double& dMin, double& dMax, double& dStep)
{
	
	// Get min and max values
	Dataset dsTemp(vec);
	dMin = min(dsTemp);
	dMax = max(dsTemp);
	int iSize = dsTemp.GetSize();
	
	// Sort datset, find diff, and sort again
	dsTemp.Sort();
	string str;	
	str = dsTemp.GetName()+"=diff("+dsTemp.GetName() + ");";
	LT_execute(str);
	dsTemp.Sort();
		
	// Use the second-last value in this sorted list as the guess for step size
	dStep = dsTemp[iSize - 2];
		
	return 0;
}




int xyz_remove_duplicates(vector& vecX, vector& vecY, vector& vecZ)
{
	// Check if X,Y,Z vectors are of equal length - return if not
	int iXsize = vecX.GetSize();
	int iYsize = vecY.GetSize();
	int iZsize = vecZ.GetSize();
	if(iXsize < 2) return 0;		// if size less than 2, no need to do anything
	if( (iXsize != iYsize) || (iYsize != iZsize) || (iZsize != iXsize) ) return 1;

	// Create temp worksheet and put xyz data into wks
	Worksheet wksTemp;
	bool bRet = wksTemp.Create("Origin.otw", CREATE_TEMP);
	string strWksName;
	wksTemp.GetPage().GetName(strWksName);
	wksTemp.DeleteCol(0);
	wksTemp.DeleteCol(0);
	wksTemp.AddCol();
	wksTemp.AddCol();
	wksTemp.AddCol();
	Dataset dsX(wksTemp, 0);
	Dataset dsY(wksTemp, 1);
	Dataset dsZ(wksTemp, 2);
	dsX = vecX;
	dsY = vecY;
	dsZ = vecZ;

	// Sort the worksheet wrt X ascending as primary and and Y ascending as secondary
	using sort = LabTalk.sort;
	sort.wksname$ = strWksName;
	sort.c1 = 1;					// sort the first 3 cols
	sort.c2 = 3;
	sort.r1 = 1;
	sort.r2 = iXsize;
	sort.cname1$ = "A: A";
	sort.cname2$ = "A: B";
	sort.wks();

	// Reset vectors to zero size
	vecX.SetSize(0);
	vecY.SetSize(0);
	vecZ.SetSize(0);

	// Loop thru data, look for duplicates, and replace with mean value
	int nDupRow, nTargetRow = 0, nSourceRow = 0;
	while( nSourceRow < dsX.GetSize() )
	{
		vecX.Add(dsX[nSourceRow]);
		vecY.Add(dsY[nSourceRow]);
		vecZ.Add(dsZ[nSourceRow]);

		nDupRow = nSourceRow;
		while( nSourceRow + 1 < dsX.GetSize() && dsX[nSourceRow] == dsX[nSourceRow + 1] && dsY[nSourceRow] == dsY[nSourceRow + 1] )
			nSourceRow++;

		if( nDupRow != nSourceRow )
		{
			// Replace duplicates with mean value
			for( int nRow = nDupRow + 1; nRow <= nSourceRow; nRow++ )
				vecZ[nTargetRow] += dsZ[nRow];
			vecZ[nTargetRow] /= (nSourceRow - nDupRow + 1);
		}
		nSourceRow++;
		nTargetRow++;
	}
	return 0;
}


////////////////////////////////////////////////////////////////////////////////////
// This sub function is used by regular xyz to matrix conversion function
// This function first finds the step location within the dataset, and returns the 
// step location, number of steps, and the median step value.
// To find a step, three successive differences are examined, and if the difference
// in the middle is greater than twice the difference at first and third points, 
// this is defined as a step. 
// Returns 0 for sucess, 1 if no step could be found
//
static int convert_regular_find_step(int iType, Worksheet& wks, int& iStepLoc, int& iStepNum, double& dStep)
{
	Dataset dsData(wks, iType);
	Dataset dsMed(wks, iType + 3);

	int ii, iSize;
	iSize = dsData.GetSize();
		
	// Sort worksheet wrt X/Y primary and Y/X secondary depending on iType value of 0/1
	string strWksName;
	wks.GetPage().GetName(strWksName);
	using sort = LabTalk.sort;
	sort.wksname$ = strWksName;
	sort.c1 = 1;					// sort only first 3 cols
	sort.c2 = 3;
	sort.r1 = 1; 
	sort.r2 = iSize;
	if(iType == 0)
	{	
		sort.cname1$ = "A: A"; 
		sort.cname2$ = "A: B";
	}
	else
	{
		sort.cname1$ = "A: B"; 
		sort.cname2$ = "A: A";
	}
	sort.wks();
	
	// Determine the location of the first step in data
	double step1, step2, step3;
	for(ii=0; ii < (iSize - 3); ii++)
	{
		// compute differences at three points
		step1 = fabs(dsData[ii] - dsData[ii+1]);
		step2 = fabs(dsData[ii+1] - dsData[ii+2]);
		step3 = fabs(dsData[ii+2] - dsData[ii+3]);
		if((step2 > 2 * step1) && (step2 > 2 * step3)) break;
	}
	if (ii == (iSize - 3)) return 1;
	iStepLoc = ii + 2;
	
	// Determine number of groups within X data
	iStepNum = iSize / iStepLoc;

	// Compute the step values and store them in temporary worksheet
	dsMed.SetSize(iStepNum - 1);
	for(ii = 0; ii < (iStepNum - 1); ii++)
	{
		dsMed[ii] = fabs(dsData[ii * iStepLoc + iStepLoc / 2] - dsData[(ii + 1) * iStepLoc + iStepLoc / 2]);
	}	
	
	
	// Sort these step values and pick the median value for the final x/y step size
	sort.wksname$ = strWksName;
	sort.c1 = 4 + iType;					// sort only the 4th/5th col
	sort.c2 = 4 + iType;
	sort.r1 = 1; 
	sort.r2 = iStepNum - 1;
	if(iType == 0) sort.cname1$ = "A: D";
	else sort.cname1$ = "A: E";
	sort.wks();
	dStep = dsMed[(iStepNum - 1) / 2];	
	
	// Success
	return 0;
}



////////////////////////////////////////////////////////////////////////////////////
// This sub function is used by regular xyz to matrix conversion function.
// This function checks all data points to find deviations.
// If the deviation in any point is larger than 1/4th the step size, the data is 
// rejected.
// Returns 0 on success, or 1 if a large deviation is found
// 
static int convert_regular_check_data(int iType, Worksheet& wks, int iStepLoc, int iStepNum, double dStep)
{
	Dataset dsData(wks, iType);
	Dataset dsMed(wks, iType + 3);

	int ii, jj, iSize;
	iSize = dsData.GetSize();

	// Sort worksheet wrt X/Y, ascending
	string strWksName;
	wks.GetPage().GetName(strWksName);
	using sort = LabTalk.sort;
	sort.wksname$ = strWksName;
	sort.c1 = 1;					// sort only first 3 cols
	sort.c2 = 3;
	sort.r1 = 1;
	sort.r2 = iSize;
	if(iType == 0)
	{
		sort.cname1$ = "A: A";
		sort.cname2$ = "A: B";
	}
	else
	{
		sort.cname1$ = "A: B";
		sort.cname2$ = "A: A";
	}
	sort.wks();

	// Build list of median values for X/Y groups by taking medain value of first
	// group and then using the X/Y step value to compute the rest
	dsMed.SetSize(iStepNum);
	for(ii=0; ii < iStepNum; ii++)
	{
		dsMed[ii] = dsData[iStepLoc / 2] + dStep * ii;

	}

	// Now go thru all groups of X/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, reject data and return
	for(ii = 0; ii < iStepNum; ii++)
	{
		for(jj = 0; jj < iStepLoc; jj++)
		{
			int id = ii * iStepLoc + jj;
			double dev = fabs(dsData[id] - dsMed[ii]);
			if(dev >= 0.25 * dStep) return 1;
			dsData[id] = dsMed[ii];
		}
	}

	return 0;
}
*/
///End		OC_CONVERT_MATRIX_FUNCTION_MOVE_TO_VC_LEVEL_WITH_PREFIX_OCMATH

///---Dancie 9/9/04 REPLACE_DATASET_WITH_VECTORBASE
////////////////////////////////////////////////////////////////////////////////////
// These functions find min and max values of a vector, and are used 
// in random_gridding_nag function.
// Should be replaced by methods in vector class when that is added
//
/*
static double min(vector& vec)
{
	Dataset ds(vec);
	return min(ds);
}

static double max(vector& vec)
{
	Dataset ds(vec);
	return max(ds);
}
*/
///---

/**
		It appends or inserts a column to worksheet at a desired position.
	Paramaters:
		wks = the worksheet to add the column
		nDesiredColumnPosition = the desired column position (0 offset). If less than 0, the column will be appended. If >= 0, but
								the current total number of columns is less than nDesiredColumnPosition, the column will also be appended.
		col= [out] the added column
	Returns:
		the actual index of the added column, which can be different from nDesiredColumnPosition even if nDesiredColumnPosition >= 0. 
*/ 
int	wks_insert_column(Worksheet &wks, int nDesiredColumnPosition, Column &col)
{
	int			colindex = -1;
	if (0 <= nDesiredColumnPosition)		// if not appending
	{
		int			nCountColumns = wks.GetNumCols();
		if (nCountColumns < nDesiredColumnPosition)
			nDesiredColumnPosition = nCountColumns;
		
		// Insert column:
		//wks.DeleteRange(-1, nDesiredColumnPosition, 0, nDesiredColumnPosition, TRUE);
		string	strColNameCreated;
		wks.InsertCol(nDesiredColumnPosition, NULL, strColNameCreated);
		
		col = wks.Columns(nDesiredColumnPosition);
		colindex = nDesiredColumnPosition;
	}
	else
	{
		// Append:
		colindex = wks.AddCol();
		col = wks.Columns(colindex);
	}
	
	return colindex;
}

int find_col_by_name(Worksheet& wks, LPCSTR lpcszName)
{
	if(!wks || 0 == strlen(lpcszName))
		return -1; // not find
	
	foreach(Column col in wks.Columns)
	{
		if( 0 == lstrcmp( col.GetLongName(), lpcszName ) )
			return col.GetIndex();
		
		if( 0 == lstrcmp( col.GetName(), lpcszName ) )
			return col.GetIndex();		
	}
	return -1;
}

//--- CPY 3/4/04 v7.5831 QA70-6082 GET_SELECTION_AS_RANGE_STRS
//	return string in the form of [Data1]Sheet1
string make_book_sheet_name(const string& strBook, const string& strSheet)
{
	/// ML 1/13/2006 XVARIABLEBASE_TO_VC
	//string str;
	//str.Format("[%s]%s", strBook, strSheet);
	//return str;
	return okutil_make_book_sheet_string(strBook, strSheet);
	/// end XVARIABLEBASE_TO_VC
}
//	check given strBookSheet to see if in the form of [Data1]Sheet1, if yes, break down and return book and sheet
bool get_book_sheet_names(const string& strBookSheet, string& strBook, string& strSheet)
{
	/// ML 1/13/2006 XVARIABLEBASE_TO_VC
	/*
	if(strBookSheet.GetLength() < 4)
		return false;
	if(strBookSheet[0] != '[')
		return false;
	int nClosing = strBookSheet.Find(']', 1);
	if(nClosing < 3)
		return false;
	
	strBook = strBookSheet.Mid(1, nClosing - 1);
	strSheet = strBookSheet.Mid(nClosing + 1);
	return true;
	*/
	return okutil_get_book_sheet_names(strBookSheet, &strBook, &strSheet); 
	/// end XVARIABLEBASE_TO_VC
}
string wks_get_book_sheet_name(const Datasheet& ds)
{
	string str;
	if(!ds)
		return str;
	
	/// ML 1/13/2006 XVARIABLEBASE_TO_VC
	/*
	Page	wp;
	ds.GetParent(wp);
	string strBook = wp.GetName();
	string strSheet = ds.GetName();
	return make_book_sheet_name(strBook, strSheet);
	*/
	str = ds.m_strBookSheet;
	return str;
	/// end XVARIABLEBASE_TO_VC
}
bool wks_from_book_sheet_name(Worksheet& wks, const string& strBookSheet)
{
	string strBook,strSheet;
	if(!get_book_sheet_names(strBookSheet, strBook, strSheet))
		return false;
	WorksheetPage wp(strBook);
	if(!wp)
		return false;
	
	wks = wp.Layers(strSheet);
	return wks.IsValid()? true:false;
}
//---


///--- CPY 3/14/04 QA70-6036 v7.5837b BASIC_SMOOTHING_ROUTINES
bool curve_smooth_adjave(curvebase& cuv, int leftpts)
{
	vector vy, vt;
	vy = cuv;
	vt = vy;
	///Mouqx/Leo 2005-9-26 QA70-8110 TO_CENTRALIZE_ERROR_CODES_IN_OCMATH
	//bool bRet = ocmath_adjave_smooth(vy, vt, vy.GetSize(), leftpts);
	//if(bRet)
	int nRet = ocmath_adjave_smooth(vy, vt, vy.GetSize(), leftpts);
	if (nRet == OE_NOERROR )
	{
		cuv = vt;
		return true;
	}
	return false;
}

bool curve_smooth_sg(curvebase& cuv, int leftpts, int nDeg) // = 2
{
	vector vy, vt;
	vy = cuv;
	vt = vy;
	///Sandy 2006-8-7 CHANGE_TO_USE_STANDARD_ERROR_CODE
	//bool bRet = ocmath_savitsky_golay(vy, vt, vy.GetSize(), leftpts, leftpts, nDeg);
	int nRet = ocmath_savitsky_golay(vy, vt, vy.GetSize(), leftpts, leftpts, nDeg);
	if(nRet == OE_NOERROR)
	{
		cuv = vt;
		return true;
	}
	return false;
}
///---

///--- SDB 4/14/04 QA70-6035 v7.5837 CALCULUS_ROUTINES
bool curve_derivative(curvebase& cuv)
{
	Dataset dsX;
	cuv.AttachX(dsX);
	vector vx, vy;
	vx=dsX;
	vy = cuv;
	///Leo 2005-9-26 QA70-8110 TO_CENTRALIZE_ERROR_CODES_IN_OCMATH 
	//bool bRet = ocmath_derivative(vx, vy, vy.GetSize());
	//if(bRet)	
	int nRet = ocmath_derivative(vx, vy, vy.GetSize());
	if (nRet == OE_NOERROR)
	{
		cuv = vy;
		return true;
	}
	return false;
}
///---

/// Leo 2005-12-16 MOVED_PEAK_AND_BASLINE_FUNCS_TO_PFM_UTILS


//----------- CPY 4/3/05 SIMPLE_GRAPH_ANSLYSIS_FUNCTIONS
//--- Iris 9/17/05 move to Analysis_utils.h
//#define STR_COL_DESIGNATION_ATTRIB	"ColDesignation"
//#define STR_COL_WIDTH_ATTRIB		"ColWidth"
//--- 
// assume tr as no branch, only leaf nodes
int out_tree_to_wks(const TreeNode& tr, Worksheet& wks)
{
	/// Hong 01/20/10 QA80-14832 QUICK_FIT_SUPPORT_OUTPUT_RESULTS_TO_WKS
	BOOL			bNoRenameSName = FALSE;
	tr.GetAttribute(STR_TR2WKS_NO_RENAME_SNAME_ATTRIB, bNoRenameSName);
	/// end QUICK_FIT_SUPPORT_OUTPUT_RESULTS_TO_WKS
	///------ Fisher 2008-9-24 WHEN_THE_FIRST_COLUMN_HAS_NO_DATA_THE_WKS_SIZE_WILL_NOT_INCREASE 
	//Column c1 = wks.Columns(0);
	//if(c1)
	//	nRow = c1.i2 + 1;
	int nR1, nR2 = -1;
	wks.GetRange(nR1, nR2, 0, -1, GDR_NO_ADJUST, GDR_SKIP_MISSING_BLANK);
	int nRow = nR2 + 1;
	///------ End WHEN_THE_FIRST_COLUMN_HAS_NO_DATA_THE_WKS_SIZE_WILL_NOT_INCREASE
	bool bUnitsNeeded = false;
	//out_int("nRow = ", nRow);
	foreach(TreeNode trN in tr.Children)
	{
		//--- CPY 9/23/08 CURVE_INTEG_TOOL_OUTPUT_TO_WKS_USING_STANDARD_RERPOT_TREE
		/// EJP 2009-03-17 QA80-13301 PREVENT_READING_VALUE_FROM_BRANCH
		///if(trN.Show == false)
		if(trN.Show == false || trN.GetNodeCount()>0)
		/// end PREVENT_READING_VALUE_FROM_BRANCH
			continue;
		//---

		//---- CPY 11/9/06 PCLAMP_PLOT_BUTTON_TO_ADD_TO_WKS
		//Column cc = wks.Columns(trN.tagName);
		Column cc;
		string strLongName;
		if(trN.GetAttribute(STR_LABEL_ATTRIB, strLongName) && strLongName.GetLength() > 0)
			cc = wks.FindCol(strLongName, 0, false, true, -1, false);
		else
			cc = wks.Columns(trN.tagName);
		//----
		if(!cc)
		{
			/// Hong 01/20/10 QA80-14832 QUICK_FIT_SUPPORT_OUTPUT_RESULTS_TO_WKS
			//int nCol = wks.AddCol(trN.tagName);
			LPCSTR		lpcszSName = trN.tagName;
			if ( bNoRenameSName )
				lpcszSName = NULL;
			int nCol = wks.AddCol(lpcszSName);
			/// end QUICK_FIT_SUPPORT_OUTPUT_RESULTS_TO_WKS
			cc.Attach(wks, nCol);
			int nDesignation = OKDATAOBJ_DESIGNATION_NONE;
			trN.GetAttribute(STR_COL_DESIGNATION_ATTRIB, nDesignation);
			cc.SetType(nDesignation);
			int nWidth = 0;
			trN.GetAttribute(STR_COL_WIDTH_ATTRIB, nWidth);
			if(nWidth != 0)
				cc.SetWidth(nWidth);
			/// Iris 1/23/2010 IMPROVE_FINDXY_OUTPUT_TO_WKS_CODES
			string strComments;
			if( trN.GetAttribute(STR_COL_COMMENTS_LABEL_ATTRIB, strComments) )
			{
				Grid grid;
				///Sophy 10/15/2010 ORG-1270 OUTPUT_TREE_TO_EXCEL_WORKSHEET_RUNTIME_ERROR
				//grid.Attach(wks);
				//if( !grid.IsLabelsShown(RCLT_COMMENT) )
				//{
				//	grid.ShowLabels(RCLT_COMMENT);
				//}
				if ( grid.Attach(wks) && !grid.IsLabelsShown(RCLT_COMMENT) )
				{
					grid.ShowLabels(RCLT_COMMENT);
				}
				///end OUTPUT_TREE_TO_EXCEL_WORKSHEET_RUNTIME_ERROR
				cc.SetComments(strComments);
			}
			///End IMPROVE_FINDXY_OUTPUT_TO_WKS_CODES
			
			/// Iris 1/25/2010 FINDXY_OUTPUT_WKS_NEED_KEEP_SAME_FORMAT_WITH_SOURCE_X_COL
			int nFormat;
			if( trN.GetAttribute(STR_COL_FORMAT_ATTRIB, nFormat) )
			{
				cc.SetFormat(nFormat);				
			}
			
			int nSubFormat;
			if( trN.GetAttribute(STR_COL_SUBFORMAT_ATTRIB, nSubFormat) )
			{
				cc.SetSubFormat(nSubFormat);
			}
			
			string strCustomDisplay;
			if( trN.GetAttribute(STR_COL_CUSTOM_DISPLAY_ATTRIB, strCustomDisplay) && !strCustomDisplay.IsEmpty() )
			{
				cc.SetCustomDisplay(strCustomDisplay);
			}
			///End FINDXY_OUTPUT_WKS_NEED_KEEP_SAME_FORMAT_WITH_SOURCE_X_COL
			
			///Sophy 3/3/2010 QUICKFIT_REPORT_TO_WKS_SHOULD_APPLY_SIGNIFICANT_SETTINGS
			int nSignDigits;
			if ( trN.GetAttribute(STR_COL_SIGNDIGITS_ATTRIB, nSignDigits) && (nSignDigits >= MIN_SIGNIFICANT_DIGITS && nSignDigits <= MAX_SIGNIFICANT_DIGITS) )
			{
				cc.SetDigitMode(DIGITS_SIGNIFICANT);
				cc.SetDigits(nSignDigits);
			}
			///end QUICKFIT_REPORT_TO_WKS_SHOULD_APPLY_SIGNIFICANT_SETTINGS
			
			//---- CPY 11/9/06 PCLAMP_PLOT_BUTTON_TO_ADD_TO_WKS
			if(strLongName.GetLength() > 0)
				cc.SetLongName(strLongName);
			string str;
			if(trN.GetAttribute("Units", str) && str.GetLength() > 0)
			{
				cc.SetUnits(str);
				bUnitsNeeded = true;
			}
			//----
			
		}
		int nn = cc.GetIndex();
		if(nn >=0)
		{
			if(trN.TypeID == TNVAL_TYPE_CSTRING || trN.strVal.IsEmpty())
				wks.SetCell(nRow, nn, trN.strVal);
			else
				wks.SetCell(nRow, nn, trN.dVal);

			///------ Folger 04/08/10 BETTER_COLUMN_WIDTH_RESIZING_FOR_GADGET_TOOLS_REPORT
			//wks.AutoSize(AS_INVALIDATE);		///Sophy 12/17/2009 QA80-14598-P3 AUTO_SIZE_RESULT_COLUMN_FOR_BETTER_VIEW
			///------ End BETTER_COLUMN_WIDTH_RESIZING_FOR_GADGET_TOOLS_REPORT
		}
	}
	
	///------ Folger 04/08/10 BETTER_COLUMN_WIDTH_RESIZING_FOR_GADGET_TOOLS_REPORT
	autosize_rowcol(wks, 1.5, -1, -1, -1, AS_NOHEIGHT|AS_INVALIDATE);
	///------ End BETTER_COLUMN_WIDTH_RESIZING_FOR_GADGET_TOOLS_REPORT
	
	if(bUnitsNeeded)//---- CPY 11/9/06 PCLAMP_PLOT_BUTTON_TO_ADD_TO_WKS
	{
		// we assume wks was prepared with just showing long name, so if units needed, we will need to show it too
		wks.CheckAddLabelByType(RCLT_UNIT);
	}
	
	return nRow;
}

void tree_set_add_col_info(TreeNode& tr, int nDesignation, int nColWidth)// = 0)
{
	if(nDesignation >= 0)
		tr.SetAttribute(STR_COL_DESIGNATION_ATTRIB, nDesignation);
	if(nColWidth != 0)
		tr.SetAttribute(STR_COL_WIDTH_ATTRIB, nColWidth);
}

bool frequency_count(const vector& vy, vector<uint>& vecFCount, double& min, double& max, double& dbinW, int nBinRule)
{
	if(nBinRule < BIN_RULE_FREEDMAN_DIACONIS || nBinRule > BIN_RULE_STURGES)
		return false;
	vy.GetMinMax(min, max);
	int nBins;
	if(nBinRule == BIN_RULE_STURGES) // do this here instead of calling ocmath_bin_width as inside it will get min max again, a waste
		nBins = 1.5 + log(vy.GetSize())/log(2);
	else
	{
		dbinW = ocmath_bin_width(vy, vy.GetSize(), nBinRule);
		nBins = 0.5 + fabs((max - min)/dbinW);
	} 
	int nNumBins = RoundLimits(&min, &max, &dbinW, nBins);	
	vecFCount.SetSize(nNumBins);
	// Call ocmath function to compute frequency count
	///Mouqx/Leo 2005-9-26 QA70-8110 TO_CENTRALIZE_ERROR_CODES_IN_OCMATH
	//return ocmath_d_frequency_count(vy, vy.GetSize(), vecFCount, vecFCount.GetSize(), min, dbinW);
	int nRet = ocmath_d_frequency_count(vy, vy.GetSize(), vecFCount, vecFCount.GetSize(), min, dbinW);
	return (nRet == OE_NOERROR);
}

// result into tr
// nPercentiles = 0, none, 1=median, 3 = Median + P25/P75, 5 = Median + P25/P75 + P5/P95, 7 = Median + P25/P75 + P5/P95 + P1/P99
void vector_basic_stats(const vector& vv, TreeNode& tr, int nPercentiles, int* pmin, int* pmax)
{
	string strLabel = _L("Descriptive Statistics");
	tr.SetAttribute(STR_LABEL_ATTRIB, strLabel);
	///Echo 9/25/06 CHANGE_DESC_STAT_FUNC_FOR_EFFICIENCY
	//DescStatResults dsr;
	//ocmath_desc_stats(vv, vv.GetSize(), &dsr);
	double dSum, dMean, dSD;
	int N;
	ocmath_basic_summary_stats(vv.GetSize(), vv, &N, &dMean, &dSD, NULL, NULL, &dSum);
	///end CHANGE_DESC_STAT_FUNC_FOR_EFFICIENCY
	
	int nInterpolate = Project.Settings.GetPercentileInterpolateMethod();
	vector vPerc = {1,5,25,50,75,95,99};
	vector vPercResult;
	vPercResult.SetSize(vPerc.GetSize());
	int nErr=ocmath_percentiles(vv, vv.GetSize(), vPerc, vPerc.GetSize(), vPercResult, nInterpolate);
	double min, max;
	int nmin, nmax;
	vv.GetMinMax(min, max, &nmin, &nmax);
	if(pmin)
		*pmin = nmin;
	if(pmax)
		*pmax = nmax;
	// temp code, should call DescStats operation
	///Echo 9/25/06 CHANGE_DESC_STAT_FUNC_FOR_EFFICIENCY
	//tr.N.nVal = dsr.N;
	//tr.sum.dVal = dsr.Sum;	
	//tr.mean.dVal = dsr.Mean; tree_set_add_col_info(tr.mean, OKDATAOBJ_DESIGNATION_Y, 9);
	//tr.sd.dVal = dsr.SD; tree_set_add_col_info(tr.sd, OKDATAOBJ_DESIGNATION_ERROR, 9);
	tr.N.nVal = N;
	tr.sum.dVal = dSum;
	tr.mean.dVal = dMean; tree_set_add_col_info(tr.mean, OKDATAOBJ_DESIGNATION_Y, 9);
	tr.sd.dVal = dSD; tree_set_add_col_info(tr.sd, OKDATAOBJ_DESIGNATION_ERROR, 9);
	///end CHANGE_DESC_STAT_FUNC_FOR_EFFICIENCY
	
	if(nPercentiles > 0)
		tr.median.dVal = vPercResult[3];
	tr.min.dVal = min;
	tr.max.dVal = max;
	if(nPercentiles > 2)
	{
		tr.P25.dVal = vPercResult[2];
		tr.P75.dVal = vPercResult[4];
	}
	if(nPercentiles > 4)
	{
		tr.P5.dVal = vPercResult[1];
		tr.P95.dVal = vPercResult[5];
	}
	if(nPercentiles > 6)
	{
		tr.P1.dVal = vPercResult[0];
		tr.P99.dVal = vPercResult[6];
	}
}

/// YuI 10/09/08 QA70-12265-P3 CURVE_STATS_QUICK_TOOL_MUST_BE_SMARTER
bool curve_stats(const vector& vx, const vector& vv, TreeNode& tr)
{
	tr.Data.TypeID = TNVAL_TYPE_CSTRING; 
	tree_set_add_col_info(tr.Data, OKDATAOBJ_DESIGNATION_X, 24);
	
	double x1, x2;
	vx.GetMinMax(x1, x2);
	
	tr.x1.dVal = x1;
	tr.x2.dVal = x2;
	
	int nmin, nmax;
	vector_basic_stats(vv, tr, 1, &nmin, &nmax); // 1 = median
	
	tr.xmin.dVal = nmin < 0 ? NANUM : vx[nmin];
	tr.xmax.dVal = nmax < 0 ? NANUM : vx[nmax];

	tr.dx.dVal = x2-x1;
	
	return true;
}
/// end CURVE_STATS_QUICK_TOOL_MUST_BE_SMARTER

bool curve_stats(const DataRange& dr, TreeNode& tr)
{
	DWORD dwPlotID;
	vector vv, vx;
	if(dr.GetData(DRR_GET_MISSING |  DRR_GET_DEPENDENT | DRR_NO_FACTORS, 0, &dwPlotID, NULL, &vv, &vx) < 0)
		return false;
	
	tr.Data.strVal = dr.GetDescription();
	
	/// YuI 10/09/08 QA70-12265-P3 CURVE_STATS_QUICK_TOOL_MUST_BE_SMARTER
	/*
	tr.Data.TypeID = TNVAL_TYPE_CSTRING; tree_set_add_col_info(tr.Data, OKDATAOBJ_DESIGNATION_X, 24);
	double x1, x2;
	vx.GetMinMax(x1, x2);
	tr.x1.dVal = x1;
	tr.x2.dVal = x2;
	int nmin, nmax;
	vector_basic_stats(vv, tr, 1, &nmin, &nmax); // 1 = median
	tr.xmin.dVal = vx[nmin];
	tr.xmax.dVal = vx[nmax];

	tr.dx.dVal = x2-x1;
	return true;
	*/
	
	return curve_stats(vx, vv, tr);
	/// end CURVE_STATS_QUICK_TOOL_MUST_BE_SMARTER
}
static bool _curve_stats(int i1, int i2, DataPlot& dp, bool bAppendReportSheet, double x1, double x2)
{
	DataRange dr;
	if(!dp.GetDataRange(dr, i1, i2))
		return false;
	Tree tr;
	if(!curve_stats(dr, tr))
		return false;
	
	if(bAppendReportSheet)
	{
		out_tree_to_wks(tr, "CurveStat");
	}
	else
	{// dump to script window
		out_tree(tr);
	}
	return true;
}

///Sophy 1/26/2010 ADD_GO_TO_REPORT_TABLE_MENU_IN_CONTEXT
//int out_tree_to_wks(TreeNode& tr, LPCSTR lpcszBookName)
int out_tree_to_wks(TreeNode& tr, LPCSTR lpcszBookName, DWORD dwCtrl) //CREATE_VISIBLE | CREATE_LOAD_1ST_LAYER_ONLY
///end ADD_GO_TO_REPORT_TABLE_MENU_IN_CONTEXT
{
	string strWksName = lpcszBookName;
	if(strWksName.IsEmpty())
		strWksName = "TreeOutput";
	
	///------ Folger 01/21/09 QA81-14995 GADGET_TOOLS_SHOULD_USE_SAME_WORKSHEET_OUTPUT_NOTATION
	//Worksheet wks(strWksName);
	//if(!wks)
	//{
		//wks.Create();
		//wks.SetSize(-1, 0);
		//wks.GetPage().Rename(strWksName);
		//wks.HideLabelByType(RCLT_UNIT);
		//wks.HideLabelByType(RCLT_COMMENT);
		//wks.HideLabelByType(RCLT_PARAM);
		//wks.CheckAddLabelByType(RCLT_LONG_NAME);
	//}

	Worksheet	wks;
	BOOL		bIsNewCreated = FALSE;
	//---- Iris 1/23/2010 want not acitve result book in all ROI tools
	//if ( !attach_or_create_sheet(wks, strWksName, CREATE_VISIBLE | CREATE_LOAD_1ST_LAYER_ONLY, false, &bIsNewCreated) || !wks )
	///Sophy 1/26/2010 ADD_GO_TO_REPORT_TABLE_MENU_IN_CONTEXT
	//if ( !attach_or_create_sheet(wks, strWksName, CREATE_NACTIVE | CREATE_LOAD_1ST_LAYER_ONLY, false, &bIsNewCreated) || !wks )
	if ( !attach_or_create_sheet(wks, strWksName, dwCtrl, false, &bIsNewCreated) || !wks ) //this changes allow caller to decide how the output worksheet will be shown.
	///end ///Sophy 1/26/2010 ADD_GO_TO_REPORT_TABLE_MENU_IN_CONTEXT
	//---- 
		return -1;
	
	if ( bIsNewCreated )
	{
		wks.SetSize(-1, 0);
		wks.HideLabelByType(RCLT_UNIT);
		wks.HideLabelByType(RCLT_COMMENT);
		wks.HideLabelByType(RCLT_PARAM);
		wks.CheckAddLabelByType(RCLT_LONG_NAME);
	}
	///------ End GADGET_TOOLS_SHOULD_USE_SAME_WORKSHEET_OUTPUT_NOTATION
	return out_tree_to_wks(tr, wks);
}

// See COKPlotObj::GetNearestDataPt().
// if bRight, then find data on the right, otherwise find on the left
int get_dataplot_index(DataPlot& dp, double x, DWORD dwCntrl) //XIC_NEAREST_ANY
{
	if(!dp)
		return -1;
	
	int ii;
	int nn = dp.XIndex(x, ii, dwCntrl);
	if(nn == XIR_LEFT_OF_RANGE)
		ii = dp.i1;
	else if(nn == XIR_RIGHT_OF_RANGE)
		ii = dp.i2;
	else if(nn == XIR_ERROR)
		return -1;
	
	return ii;
}

//----- CPY 8/14/05 LAYER_DATA_READING_VERT_LINE

bool find_nearest_xy_at_x(double x, const DataRange& dr, vector<string>& vsNames, vector<int>& vnIndices, vector& vx, vector& vy)
{
	DWORD	dwRules = DRR_GET_DEPENDENT | DRR_NO_FACTORS | DRR_RAW_ERR_WEIGHT;
	int	nNumData = dr.GetNumData(dwRules);
	if(nNumData < 1)
		return false;
	int		nCount = 0;
	for (int ii = 0; ii < nNumData; ii++)
	{
		DWORD			dwPlotUID = 0;
		int				nIndex = -1;
		DataRange		drPlot;
		string			str = "##";
		double			xx = NANUM, yy = NANUM;
		vector			vYtemp;// need to find out how to remove this
		if(dr.GetData(dwRules, ii, &dwPlotUID, NULL, &vYtemp) && dwPlotUID)
		{
			DataPlot dp;
			dp = Project.GetObject(dwPlotUID);
			if(dp)
			{
				dp.GetDataRange(drPlot);
				if(dp.XIndex(x, nIndex) != XIR_WITHIN_RANGE)
					nIndex = -1;
				else
				{
					if(dp.GetDataPoint(nIndex, &xx, &yy))
						nCount++;
				}
			}
		}
		if(drPlot)
			str = drPlot.GetDescription();
		vsNames.Add(str);
		vnIndices.Add(nIndex);
		vx.Add(xx);
		vy.Add(yy);
	}
	return nCount >0 ? true:false;
}
//----- end LAYER_DATA_READING_VERT_LINE

bool get_xy_data(const DataRange& dr, vector& vx, vector& vy, int* pi1, int* pi2, bool bGetMissingValues, Worksheet* pwks)
{
   DWORD dwPlotID;
   DWORD dwRule = bGetMissingValues? DRR_GET_MISSING : 0;
   
   if(dr.GetData(dwRule | DRR_GET_DEPENDENT | DRR_NO_FACTORS, 0, &dwPlotID, NULL, &vy, &vx) < 0)
      return false;
   
   int r1,r2,c1,c2;
   Worksheet wks;
   dr.GetRange(0, r1, c1, r2, c2, wks);
   //printf("%s: [%d,%d], c1=%d, c2=%d\n", wks.GetPage().GetName(), r1, r2, c1, c2);
   if(pi1)
      *pi1 = r1;
   if(pi2)
      *pi2 = r2;
   if(pwks)
      *pwks = wks;
   
   return true;
}
// bData = true to be above/below data Y range
// bData = false to span layer frame
// only stretch vertically
bool grobj_stretch_vertical(GraphObject& gr, DataRange& dr, bool bData)
{
	Layer lay;
	gr.GetParent(lay);
	int nLeft, nTop, nRight, nBottom;
	DWORD dwPlotID;
	vector vx, vy;
	if(dr.GetData(DRR_GET_DEPENDENT | DRR_NO_FACTORS, 0, &dwPlotID, NULL, &vy, &vx) < 0)
		return false;
	double xmin, xmax, ymin, ymax;
	vx.GetMinMax(xmin, xmax);
	vy.GetMinMax(ymin, ymax);
	if(bData)
	{
		lay.WorldToPage(nLeft, nTop, xmin, ymax);
		lay.WorldToPage(nRight, nBottom, xmax, ymin);
		int nHeight = abs(nBottom - nTop);
		if(nHeight < 100) nHeight = 100; //in logical unit, like about 10 pixcel for 600 dpi
		int nExtra = nHeight * 0.1 + 1.5; 
		if(nExtra > 120) nExtra = 120;
		gr.Top = nTop - nExtra;
		gr.Height = nHeight + 2*nExtra;
	}
	else
	{
		GraphLayer glay = lay;
		if(glay)
		{
			Scale sc(glay.Y);
			lay.WorldToPage(nLeft, nTop, xmin, sc.To);
			lay.WorldToPage(nRight, nBottom, xmax, sc.From);
			gr.Top = nTop;
			gr.Height = abs(nBottom - nTop);
		}
		else
			return false;
	}
	return true;
}

bool shape_tool_init(GraphObject& gr, int nPatternColor, int nPatternStyle, double nBorderWidth, double nPatternWidth , int nFillColor, bool bBehindData)
{
	Tree tr;
	// get relative = true to get only the relavent tree nodes
	tr = gr.GetFormat(FPB_ALL, FOB_ALL, true, true);
	TreeNode trn = tr.Root.Fill;
	trn.Color.nVal = nFillColor; // -4 corresponds to transparent, or NONE in dialog
	trn.Pattern.Style.nVal = nPatternStyle;
	trn.Pattern.UseBorderColor.nVal = 0;// independent color for fill pattern lines
	trn.Pattern.PatternColor.nVal = nPatternColor; // 6=yellow
	trn.Pattern.Width.dVal = nPatternWidth; // fill pattern line width
	trn = tr.Root.Border;
  	trn.Width.dVal = nBorderWidth; // rect order line width
  	tr.Root.BehindData.nVal = bBehindData? 1:0;
  	
  	///Sandy 2007-8-14 add error checking
  	//gr.ApplyFormat(tr, true, true);
  	//return true;
	int nErr = gr.UpdateThemeIDs(tr.Root);
	if(nErr == 0)
	{
		gr.ApplyFormat(tr, true, true);
		return true;
	}
	out_int("err = ", nErr);
	return false;
	//end
	
}

	

//	compute descriptive statistics on a curve between two X markers
//		bAppendReportSheet = [input] true will append result into a book called "CurveStats", false will dump result to script window
//		curvebase = [input] the curve to do the calculation from, if NULL, will use the active curve in the active layer
//	Return:
//		false if no curve found
bool curve_stats(double x1, double x2, DWORD dwCntrl, DataPlot& dataplot)// = NULL);
{
	DataPlot dp;
	if(dataplot == NULL)
	{
		GraphLayer gl = Project.ActiveLayer();
		dp = gl.DataPlots();
	}
	else
		dp = dataplot;

	if(!dp)
		return false;
	int i1 = get_dataplot_index(dp, x1, (dwCntrl & CUVS_X_MARKERS_NEAREST)? XIC_NEAREST_ANY : XIC_NEAREST_RIGHT);
	int i2 = get_dataplot_index(dp, x2, (dwCntrl & CUVS_X_MARKERS_NEAREST)? XIC_NEAREST_ANY : XIC_NEAREST_LEFT);
	if(i1<0 || i2<0)
		return false;
	if(i1 > i2) 
	{
		int it;
		SWAP(i1, i2, it);
	}
	bool bAppendReportSheet = (dwCntrl & CUVS_DUMP_SCRIPTWIN)? false:true;	
	return _curve_stats(i1, i2, dp, bAppendReportSheet, x1, x2);
}


/// YuI 09/29/08 QA70-12265 CLEANUP_AND_ADDITIONAL_OC_INTERFACE_TO_GET_DATA_POINTS
/*
bool find_intersect_dataplot(GraphObject& grTool, DataPlot& dp, int& i1, int& i2, bool bUpdateToActive)
{
	GraphLayer      gl;
	grTool.GetParent(gl);
	double dd;
	gl.LT_execute("x=Layer.Plot");LT_get_var("x", &dd);
	int nActive = dd -1;
	
	vector<int> vni1, vni2;
	vector<int> vnIndices;
	int ii = 0;
	bool bActiveFound = false;
	foreach(DataPlot dpi in gl.DataPlots)
	{
		/// YuI 09/24/08 QA70-12265 P3 DataPlot::GetDataPoints_TO_RETURN_VECTOR_OF_INDECES
		/*
		vector vx, vy;
		int nCount = dpi.GetDataPoints(grTool, vx, vy);
		if(nCount > 0)
		{
			double x1, x2;
			vx.GetMinMax(x1, x2);
			vnIndices.Add(ii);
			int n1, n2;
			dpi.XIndex(x1, n1);
			dpi.XIndex(x2, n2);
			vni1.Add(n1);
			vni2.Add(n2);
			if(ii == nActive)
			{
				bActiveFound = true;
				break;
			}
		}
		*/
		
		/// YuI 09/29/08 QA70-12265 CLEANUP_AND_ADDITIONAL_OC_INTERFACE_TO_GET_DATA_POINTS
		/*
		vector vx;
		vector vy;
		vector<int> vIndeces;
		int nCount = dpi.GetDataPoints(grTool, vx, vy, &vIndeces);
		if(nCount > 0)
		{
			// nIndexMinVX and nIndexMaxVX are min and max indeces in the vx vector, not in the dataplot
			int nIndexMinVX  =0;
			int nIndexMaxVX = 0;
			double dXMin = 0;
			double dXMax = 0;
			vx.GetMinMax(dXMin, dXMax, &nIndexMinVX, &nIndexMaxVX);
			
			vnIndices.Add(ii);
			vni1.Add(min(vIndeces[nIndexMinVX], vIndeces[nIndexMaxVX]));
			vni2.Add(max(vIndeces[nIndexMinVX], vIndeces[nIndexMaxVX]));
			if(ii == nActive)
			{
				bActiveFound = true;
				break;
			}
		}
		/// end CLEANUP_AND_ADDITIONAL_OC_INTERFACE_TO_GET_DATA_POINTS
		*/
		/*
		vector<int> vI1;
		vector<int> vI2;
		int nCount = dpi.GetDataRegions(grTool, vI1, vI2);
		if( nCount > 0 )
		{
			vnIndices.Add(ii);
			vni1.Add(vI1[0]);
			vni2.Add(vI2[0]);
			if(ii == nActive)
			{
				bActiveFound = true;
				break;
			}
		}
		/// end DataPlot::GetDataPoints_TO_RETURN_VECTOR_OF_INDECES
		ii++;
	}
	if(vnIndices.GetSize() == 0)
	{
		out_str("not intersecting any data plot");
		return false;
	}
	int nn = 0;
	if(vnIndices.GetSize() > 1 && bActiveFound)
	{
		// prefer active, which is always the last
		nn = vnIndices.GetSize() - 1;
	}
	else if(!bActiveFound)
	{
		if(bUpdateToActive)
		{
			LT_set_var("x", vnIndices[nn] + 1.0);// convert to LT index
			gl.LT_execute("Layer.Plot = x");
		}
	}
	int nSelPlot = vnIndices[nn];
	dp = gl.DataPlots(nSelPlot);
	i1 = vni1[nn];
	i2 = vni2[nn];
	return true;
}
*/
///Sophy 12/14/2009 QA80-14686-P2 DATAPLOT_GET_DATAPOINTS_NEED_TO_SKIP_MASKED_DATA
//static bool _get_plot_intersecting_region(DataPlot& dp, GraphObject& grTool, int& i1, int& i2)
static bool _get_plot_intersecting_region(DataPlot& dp, GraphObject& grTool, int& i1, int& i2, DWORD dwCtrl = 0) //dwCtrl not used currently
///end DATAPLOT_GET_DATAPOINTS_NEED_TO_SKIP_MASKED_DATA
{
	if( !dp || !grTool )
		return false;
	
	vector<int> vI1;
	vector<int> vI2;
	int nCount = dp.GetDataRegions(grTool, vI1, vI2);
	if( nCount > 0 )
	{
		i1 = vI1[0];
		
		/// YuI 10/08/08 QA70-12350-P3 QUICK_TOOL_MUST_FOLLOW_THE_SAME_LOGIC_AS_RECT_SELECTOR
		//	i2 = vI2[0];
		i2 = vI2[nCount - 1]; // last point that fell into region
		/// QUICK_TOOL_MUST_FOLLOW_THE_SAME_LOGIC_AS_RECT_SELECTOR
		
		
		return true;
	}
	
	return false;
}
///Sophy 12/14/2009 QA80-14686-P2 DATAPLOT_GET_DATAPOINTS_NEED_TO_SKIP_MASKED_DATA
//static bool _get_plot_intersecting_data(DataPlot& dp, GraphObject& grTool, vector& vX, vector& vY)
static bool _get_plot_intersecting_data(DataPlot& dp, GraphObject& grTool, vector& vX, vector& vY, DWORD dwCtrl = 0)
///end DATAPLOT_GET_DATAPOINTS_NEED_TO_SKIP_MASKED_DATA
{
	if( !dp || !grTool )
		return false;
	///Sophy 12/14/2009 QA80-14686-P2 DATAPLOT_GET_DATAPOINTS_NEED_TO_SKIP_MASKED_DATA
	//int nCount = dp.GetDataPoints(grTool, vX, vY);
	int nCount = dp.GetDataPoints(grTool, vX, vY, NULL, dwCtrl);
	///end DATAPLOT_GET_DATAPOINTS_NEED_TO_SKIP_MASKED_DATA
	return nCount > 0;
}

/// YuI 10/09/08 QA70-12265-P3 CURVE_STATS_QUICK_TOOL_MUST_BE_SMARTER
/*
bool find_intersect_dataplot(GraphObject& grTool, DataPlot& dp, int& i1, int& i2, bool bUpdateToActive)
{
	GraphLayer gl;
	grTool.GetParent(gl);
	if( !gl )
	{
		ASSERT(FALSE); // can this happen?
		return false;
	}
	
	DataPlot dpActive = gl.DataPlots();
	if( dpActive )
	{
		if( _get_plot_intersecting_region(dpActive, grTool, i1, i2) )
		{
			dp = dpActive;
			return true;
		}
	}
	
	// if reached thus far, no intersection with active
	foreach( DataPlot dpPlot in gl.DataPlots )
	{
		if( dpPlot == dpActive )
			continue;
		
		if( _get_plot_intersecting_region(dpPlot, grTool, i1, i2) )
		{
			dp = dpPlot;
			if( bUpdateToActive )
			{
				dpPlot.SetActive();
			}
			
			return true;
		}
	}
	
	return false;
}
*/

#define	IMPLEMENT_find_intersect_dataplot(grTool, dp, value1, value2, bUpdateToActive, dwCtrl, helper_function)	\
	GraphLayer gl;\
	grTool.GetParent(gl);\
	if( !gl )	\
	{			\
		ASSERT(FALSE); \
		return false;	\
	} \
	DataPlot dpActive = gl.DataPlots(get_selected_data_plot()); \
	Tree trInfo; \
	if ( tree_get_binary_storage(trInfo, grTool, ATTACHED_GRAPH_OBJECTS_INFO) && trInfo.curPlotIndex ) \
	{ \
		dpActive = gl.DataPlots(trInfo.curPlotIndex.nVal);	\
	} \
	if( dpActive )	\
	{ \
		if( helper_function(dpActive, grTool, value1, value2, dwCtrl) ) \
		{ \
			dp = dpActive; \
			return true; \
		} \
	} \
	foreach( DataPlot dpPlot in gl.DataPlots ) \
	{ \
		if( dpPlot == dpActive ) \
			continue; \
		if( helper_function(dpPlot, grTool, value1, value2, dwCtrl) ) \
		{ \
			dp = dpPlot; \
			if( bUpdateToActive ) \
			{ \
				dpPlot.SetActive(); \
			} \
			return true; \
		} \
	} \
	return false;

///Sophy 12/14/2009 QA80-14686-P2 DATAPLOT_GET_DATAPOINTS_NEED_TO_SKIP_MASKED_DATA
//bool find_intersect_dataplot(GraphObject& grTool, DataPlot& dp, int& i1, int& i2, bool bUpdateToActive)
bool find_intersect_dataplot(GraphObject& grTool, DataPlot& dp, int& i1, int& i2, bool bUpdateToActive, DWORD dwCtrl)
///end DATAPLOT_GET_DATAPOINTS_NEED_TO_SKIP_MASKED_DATA
{
	///Sophy 12/14/2009 QA80-14686-P2 DATAPLOT_GET_DATAPOINTS_NEED_TO_SKIP_MASKED_DATA
	//IMPLEMENT_find_intersect_dataplot(grTool, dp, i1, i2, bUpdateToActive, _get_plot_intersecting_region);
	IMPLEMENT_find_intersect_dataplot(grTool, dp, i1, i2, bUpdateToActive, dwCtrl, _get_plot_intersecting_region);
	///end DATAPLOT_GET_DATAPOINTS_NEED_TO_SKIP_MASKED_DATA
}

///Sophy 12/14/2009 QA80-14686-P2 DATAPLOT_GET_DATAPOINTS_NEED_TO_SKIP_MASKED_DATA
//bool find_intersect_dataplot(GraphObject& grTool, DataPlot& dp, vector& vX, vector& vY, bool bUpdateToActive)
bool find_intersect_dataplot(GraphObject& grTool, DataPlot& dp, vector& vX, vector& vY, bool bUpdateToActive, DWORD dwCtrl)
///end DATAPLOT_GET_DATAPOINTS_NEED_TO_SKIP_MASKED_DATA
{
	///Sophy 12/14/2009 QA80-14686-P2 DATAPLOT_GET_DATAPOINTS_NEED_TO_SKIP_MASKED_DATA
	//IMPLEMENT_find_intersect_dataplot(grTool, dp, vX, vY, bUpdateToActive, _get_plot_intersecting_data);
	IMPLEMENT_find_intersect_dataplot(grTool, dp, vX, vY, bUpdateToActive, dwCtrl, _get_plot_intersecting_data);
	///end DATAPLOT_GET_DATAPOINTS_NEED_TO_SKIP_MASKED_DATA
}
/// end CURVE_STATS_QUICK_TOOL_MUST_BE_SMARTER
/// end CLEANUP_AND_ADDITIONAL_OC_INTERFACE_TO_GET_DATA_POINTS

///Sophy 10/28/2008 SHOULD_REARRANGE_LAYERS_WHEN_SWITCH_BETWEEN_SINGLE_INDEP_N_DEP_AND_MULTI_IDEP_N_DEP moved to analysis_utils.h
/**$
*/
void stats_check_reset_fit_mode(TreeNode & trInputData, int nTotalNumData, int numSubData )
{
	int		nOldMode;
	string	strOldValueAttribute = "OldFitMode";
	bool	bMultipleData = 1 < nTotalNumData || (1 == nTotalNumData && 1 < numSubData);
	
	if( !bMultipleData && trInputData.Use > DATA_MODE_INDEP_CONSOLID )
	{
		trInputData.SetAttribute(strOldValueAttribute, trInputData.Use);
		trInputData.Use = DATA_MODE_INDEP_CONSOLID;
	}
	else if( trInputData.GetAttribute(strOldValueAttribute, nOldMode) && bMultipleData && nOldMode > DATA_MODE_INDEP_CONSOLID)
	{
		trInputData.Use = nOldMode;
		trInputData.RemoveAttribute(strOldValueAttribute);
	}
}
///end SHOULD_REARRANGE_LAYERS_WHEN_SWITCH_BETWEEN_SINGLE_INDEP_N_DEP_AND_MULTI_IDEP_N_DEP


bool interpolate_linear(const vector& vx, vector& vy, double x1, double y1, double x2, double y2)
{
	if(is_equal(x1,x2))
		return false;
	int nSize = vx.GetSize();
	vy.SetSize(nSize);
	vy = (vx - x1) * (y2-y1)/(x2-x1);
	vy += y1;
	return true;
}
///Arvin 03/19/07 IMPROVE_CALCULATION_CENTROID_METHOD
//bool peak_integrate(const vector& vx, const vector& vy, const vector& vbln, ocmath_IntegResult& ir)
bool peak_integrate(const vector& vx, const vector& vy, const vector& vbln, IntegrationResult& ir)
///end IMPROVE_CALCULATION_CENTROID_METHOD
{
	int nSize = vy.GetSize();
	if(nSize < 2)
		return false;
	if(nSize != vx.GetSize())
		return false;
	
	vector vTemp;
	vTemp = vy - vbln;
	vector vIntegral;
	vIntegral.SetSize(nSize);
	///Leo 2005-9-26 QA70-8110 TO_CENTRALIZE_ERROR_CODES_IN_OCMATH
	//bool bRet = ocmath_integrate(vx, vTemp, 0, vTemp.GetSize()-1, &ir, vIntegral);
	//return bRet;
	int nRet = ocmath_integrate(vx, vTemp, 0, vTemp.GetSize()-1, &ir, vIntegral);
	return (nRet == OE_NOERROR);
}


//---------- end SIMPLE_GRAPH_ANSLYSIS_FUNCTIONS
// nData = 0 will assume InputData is already a subbranch
static TreeNode _tree_check_add_input_data_branch(TreeNode& trInputData, DataPlotStrings* pDPS, int nData, uint uID)
{
	/// Iris 4/17/06 KEEP_XY_ORDER_FOR_INPUT_DATA_RANGE
	//vector<string> vsKeys = {"Y", "X", "ED"};
	///// Iris 02/04/06 QA70-8480 UPDATE_DATA_RANGE_MANIPULATION
	////vector<string> vsLabels = {"Y Data Range","X Data Range", "Error Bar Data Range"};
	//vector<string> vsLabels = {"Y Data Range","X Data Range", "Error Bar / Weight"};
	/////End UPDATE_DATA_RANGE_MANIPULATION
	vector<string> vsKeys = {"X", "Y", "ED"};
	/// Max 07-11-05 CORRECT_LOCALIZATION
	//vector<string> vsLabels = {"X Data Range","Y Data Range", "Error Bar / Weight"};
	vector<string> vsLabels;
	vsLabels.Add(_L("X Data Range"));
	vsLabels.Add(_L("Y Data Range"));
	vsLabels.Add(_L("Error Bar / Weight"));
	/// END CORRECT_LOCALIZATION	
	/// End KEEP_XY_ORDER_FOR_INPUT_DATA_RANGE
	 
	/// YuI 6/16/04 v7.5891 QA70-6556 DATA_RANGE_MARKERS
	/*
	TreeNode tr = tree_check_add_input_data_branch(trInputData, vsKeys, vsLabels, TRGP_DATA_RANGE, nData, 0, pDPS->szDepRange);
	
	if(tr.IsValid() && uID)
		tr.SetAttribute(STR_PLOTOBJ_UID_ATTRIB, ftoa(uID));
	
	tree_check_add_input_data_branch(trInputData, vsKeys, vsLabels, TRGP_DATA_RANGE, nData, 1, pDPS->szIndepRange);
	tree_check_add_input_data_branch(trInputData, vsKeys, vsLabels, TRGP_DATA_RANGE, nData, 2, pDPS->szYErrRange);
	*/
	
	LPCSTR lpcszDefault = NULL;
	
	if( pDPS )
		lpcszDefault = pDPS->szIndepRange;
	
	tree_check_add_input_data_branch(trInputData, vsKeys, vsLabels, TRGP_DATA_RANGE, nData, 0, lpcszDefault);	
	
	if( pDPS )
		lpcszDefault = pDPS->szDepRange;
	
	TreeNode tr = tree_check_add_input_data_branch(trInputData, vsKeys, vsLabels, TRGP_DATA_RANGE, nData, 1, lpcszDefault);
	if(tr.IsValid() && uID)
		//tr.SetAttribute(STR_PLOTOBJ_UID_ATTRIB, ftoa(uID));
		set_plot_uid_to_tree(tr, uID);
	
	
	if( pDPS )
		lpcszDefault = pDPS->szYErrRange;
	
	tree_check_add_input_data_branch(trInputData, vsKeys, vsLabels, TRGP_DATA_RANGE, nData, 2, lpcszDefault);
	/// end DATA_RANGE_MARKERS
	
	return tr;
}

TreeNode tree_check_add_input_data_branch(TreeNode& trInputData, const DataPlot& dp, int nData)// = 1)
{
	TreeNode tr;
	if(!dp)
		return tr;
	
	DataPlotStrings dps;
	int nPlotType = dp.GetPlotType(&dps);
	if(nPlotType <= 0)
		return false; // not a valid data plot with a valid plot type
	uint uID = dp.GetUID(true);
	tr =  _tree_check_add_input_data_branch(trInputData, &dps, nData, uID);
	return tr;
}	

/// YuI 8/13/04 QA70-4387 NLFITTER_OPERATION_IMPLEMENTATION
TreeNode tree_check_add_input_data_branch(TreeNode& trInputData, const vector<string>& vsKeys, 
	vector<string>& vsLabels, int nBranchType, int nData, int nType, LPCSTR lpcszDefaultVal)
{
	/// ML 1/13/2006 XVARIABLEBASE_TO_VC
	/*
	ASSERT(vsKeys.GetSize() == vsLabels.GetSize());
	TreeNode tr = trInputData;
	if(nData > 0)
	{
		string strName;
		strName.Format("%s%d", PREFIX_FOR_DATA_RANGE_DATA, nData); 
		tr = tree_check_get_node(trInputData, strName, nBranchType, STR_LABEL_ATTRIB, strName);
	}

	for(int ii = 0; ii < vsKeys.GetSize(); ii++)
	{
		TreeNode trTemp = tree_check_get_node(tr, vsKeys[ii], TRGP_INTERACTIVE, STR_LABEL_ATTRIB, vsLabels[ii]);

		if(lpcszDefaultVal && (nType < 0 || nType == ii))
		{
			trTemp.strVal = lpcszDefaultVal;
			ASSERT(nType >= 0);
		}
	}
	return tr;
	*/
	TreeNode	tr;
	octree_tree_check_add_input_data_branch(&tr, &trInputData, &vsKeys, &vsLabels, nBranchType, nData, nType, lpcszDefaultVal);
	return tr;
	/// end XVARIABLEBASE_TO_VC
}
/// YuI 11/11/04
DataRange	get_data_range_from_input_data(TreeNode& trInputData, uint& uidDataRange)
{
	/// ML 1/13/2006 XVARIABLEBASE_TO_VC
	/*
	string strTemp;
	DataRange dr;
	if( trInputData.GetAttribute(STR_DATARANGE_UID_ATTRIB, strTemp) )
		uidDataRange = atoi(strTemp);
	
	
	if(uidDataRange)
	{
		dr = (DataRange)Project.GetObject(uidDataRange);
		if( !dr )
			error_report("DataRange UID stored in InputData is bad");
	}
	
	return dr;
	*/
	DataRange dr;
	dr.InitFromInputWithUID(trInputData, uidDataRange);
	return dr;
	/// end XVARIABLEBASE_TO_VC
}
/// end YuI

/// ML 10/20/2005 ANOVA_INPUT_DATA_TO_DATARANGE
//bool prepare_input_data_for_operation(TreeNode& trInputData, bool bAddToProject)
bool prepare_input_data_for_operation(TreeNode& trInputData, bool bAddToProject, int nDRTreeOption)
/// end ANOVA_INPUT_DATA_TO_DATARANGE
{	
	DataRange dr;
	uint uidDataRange;
	
	dr = get_data_range_from_input_data(trInputData, uidDataRange);
	if( dr )
		return false; // already created and exists no need to create new one
	
	// only if data range is not created yet need to create new one
	/// ML 10/20/2005 ANOVA_INPUT_DATA_TO_DATARANGE
	//dr.Create(trInputData, FALSE);
	dr.Create(trInputData, nDRTreeOption);
	/// end ANOVA_INPUT_DATA_TO_DATARANGE
	trInputData.SetAttribute(STR_DATARANGE_UID_ATTRIB, (int)dr.GetUID(true));
	
	if(bAddToProject)
	{
		Project.AddDataRange(dr); 
		dr.Invalidate();
	}
	return true;
}
string make_range_string(const Datasheet& ds, LPCSTR lpcstrColName1, LPCSTR lpcstrColName2, int iBeginRow, int iEndRow)
{
	/// ML 1/13/2006 XVARIABLEBASE_TO_VC
	//string strBookSheetName = wks_get_book_sheet_name(ds);
	//string strRange;
	///*
	//if(iBeginRow > 0 && iEndRow > 0)
	//	strExcelRange = strBookSheetName + "!$" + lpcstrColName1 + "$" + iBeginRow + ":$" + lpcstrColName2 + "$" + iEndRow;
	//else if(lpcstrColName2 == NULL)
	//	strExcelRange = strBookSheetName + "!$" + lpcstrColName1 + ":$" + lpcstrColName1;
	//else
	//	strExcelRange = strBookSheetName + "!$" + lpcstrColName1 + ":$" + lpcstrColName2;
	//return strExcelRange;
	//*/
	//okutil_create_range_string(&strRange, lpcstrColName1, iBeginRow, lpcstrColName2, iEndRow);
	//strBookSheetName += "!";
	//return strBookSheetName +  strRange;

	return ds.MakeRangeString(lpcstrColName1, lpcstrColName2, iBeginRow, iEndRow);
	/// end XVARIABLEBASE_TO_VC
}

/// ML 1/13/2006 XVARIABLEBASE_TO_VC
/*
string make_range_string(const Column& col, int iBeginRow, int iEndRow)
{
	string str;
	Worksheet wks;
	if(col.GetParent(wks))
	{
		return make_range_string(wks, col.GetName(), NULL, iBeginRow, iEndRow);
	}
	return str;
}
*/
/// end XVARIABLEBASE_TO_VC

bool construct_one_data_range(TreeNode& trDataRange, const Worksheet& wks, LPCSTR lpcszX, LPCSTR lpcszY, LPCSTR lpcszZ, LPCSTR lpcszErr, bool bSingle)
{
	if(!wks.IsValid())
		return	false;
	
	vector<string> vsKeys = {"X", "Y", "ED"};
	
	/// Iris 02/04/06 QA70-8480 UPDATE_DATA_RANGE_MANIPULATION
	//vector<string> vsLabels = {"X Data Range", "Y Data Range", "Error Bar Data Range"};
	/// Max 07-11-05 CORRECT_LOCALIZATION
	//vector<string> vsLabels = {"X Data Range", "Y Data Range", "Error Bar / Weight"};
	vector<string> vsLabels;
	vsLabels.Add(_L("X Data Range"));
	vsLabels.Add(_L("Y Data Range"));
	vsLabels.Add(_L("Error Bar / Weight"));
	/// END CORRECT_LOCALIZATION
	///End UPDATE_DATA_RANGE_MANIPULATION
	if(lpcszZ)
	{
		vsKeys[2] = "Z";
		vsLabels[2] = _L("Z Data Range");
	}
	TreeNode tr = tree_check_add_input_data_branch(trDataRange, vsKeys, vsLabels, TRGP_DATA_RANGE, bSingle? 0:1);	
	string strBookSheetName = wks_get_book_sheet_name(wks);
	tr.X.strVal = make_range_string(wks, lpcszX);
	tr.Y.strVal = make_range_string(wks, lpcszY);
	if(lpcszZ)
		tr.Z.strVal = make_range_string(wks, lpcszZ);
	if(lpcszErr && lstrlen(lpcszErr) > 0)
		tr.ED.strVal = make_range_string(wks, lpcszErr);
	
	return true;
}

// construct tree and create datarange for operation, this tree can be used in operation as InputData branch
bool make_input_data(TreeNode& trInputData, const Worksheet& wks, int nColX, int nColY, int nColYErr)
{
	string strX, strY, strYErr;
	int nCols = wks.GetNumCols();
	if(nColY >= nCols || nColX >= nCols || nColYErr >= nCols)
		return false;
	
	strX = wks.Columns(nColX).GetName();
	strY = wks.Columns(nColY).GetName();
	if(nColYErr >0)
		strYErr = wks.Columns(nColYErr).GetName();
	return construct_one_data_range(trInputData, wks, strX, strY, NULL, strYErr, false);
}
///End MAKE_INPUT_DATA_FROM_WORKSHEET

//----- CPY 8/14/05 LAYER_DATA_READING_VERT_LINE
//	construct a tree node that represents all the data plots in the given graph
bool construct_data_range_all_plots(TreeNode& tr, GraphLayer gl)
{
	int nPlots = gl.DataPlots.Count();
	
	for(int ii=0; ii< nPlots; ii++)
	{
		DataPlot dp = gl.DataPlots(ii);
		tree_check_add_input_data_branch(tr, dp, ii+1);
	}
	return true;
}
//----- end LAYER_DATA_READING_VERT_LINE


string get_err_as_string(int nErrCode)
{
	switch(nErrCode)
	{
	case ERR_DATASEL_NO_DATA_SELECTED:
		return _L("No Data Selected");
	case ERR_DATASEL_MUST_HAVE_Z:
		return _L("You must select at least one Z Column");
	case ERR_DATASEL_MUST_HAVE_Y:
		return _L("You must select at least one Y Column");
	case ERR_DATASEL_MUST_HAVE_X:
		return _L("You must select at least one X Column");
	}
	return _L("Unknown Error");
}

/// RVD 10/25/2005 v8.0323 NON_ACTIVE_SELECTION
static Worksheet _get_wks(OriginObject& obj)
{
	return (Worksheet) obj;
}
/// end NON_ACTIVE_SELECTION

/// Iris 8/29/05 SUPPORT_STATS_INPUT_FORMAT
static bool _init_input_data_branch_from_selection_for_survival_analysis(TreeNode& trInputData, DWORD dwDataRule, vector<string>& vsKeys,
		vector<string>& vsLabels, OriginObject& obj)
{
	Worksheet wks = _get_wks(obj);

	if( !wks )
		return false;	

	vector<int> vnSelCols;
	wks.GetSelectedColumns(vnSelCols);
	
	bool bIncludeCovar = false;
	if( dwDataRule & DRR_GET_STATS_SA_COVAR_LAST_COLS)
		bIncludeCovar = true;
	

	if( !bIncludeCovar )
	{
		//remove Covariance's key and label
		vsKeys.RemoveAt( vsKeys.GetSize()-1 ); 
		vsLabels.RemoveAt( vsLabels.GetSize()-1 );	
		
		//the size of selected columns must be even since should be Time1,Censor1,Time2, Censor2......
		if( vnSelCols.GetSize() % 2 != 0)
			return false;
	}
	else
	{
		if( vnSelCols.GetSize() < vsKeys.GetSize() )
			return false;
	}
	
	
	int nRanges = 1;
	for(int nColIndex=0; nColIndex<vnSelCols.GetSize(); )
	{					
		for(int nData = 0; nData<vsKeys.GetSize(); nData++)
		{
			//to make the columns that from 2nd to the end column as Convariance data and init treenode
			if(2 == nData && bIncludeCovar)
			{
				Column colCovarBegin(wks, vnSelCols[nColIndex]);
				Column colCovarEnd(wks, vnSelCols[vnSelCols.GetSize()-1]);
				if( colCovarBegin && colCovarEnd )
				{
					string strRange = make_range_string(wks, colCovarBegin.GetName(), colCovarEnd.GetName());
					tree_check_add_input_data_branch(trInputData, vsKeys, vsLabels, TRGP_DATA_RANGE, nRanges, nData, strRange);
					return true;
				}
				return false;
			}
			
			//to get and init Time and Centor treenode
			Column 	col(wks, vnSelCols[nColIndex]);
			if(col)
			{
				string strRange = make_range_string(wks, col.GetName(), NULL);
				tree_check_add_input_data_branch(trInputData, vsKeys, vsLabels, TRGP_DATA_RANGE, nRanges, nData, strRange);
			}
			nColIndex++;
		}					
		nRanges++;
	}
	return true;
}

static bool _init_input_data_branch_from_selection_for_ROC(TreeNode& trInputData, DWORD dwDataRule, vector<string>& vsKeys,
		vector<string>& vsLabels, OriginObject& obj)
{
	Worksheet wks = _get_wks(obj);

	if( !wks ) //must select data from wks for ROC
		return false;	
		
	vector<int> vnSelCols;
	wks.GetSelectedColumns(vnSelCols);

	///Kevin 09/02/05 ADD_FOR_SELECTED_NONE_COLUMN
	/// Iris 10/20/05 input data need inlcude one data and one state at least
	//if ( 0 == vnSelCols.GetSize() )
	if ( vnSelCols.GetSize() < 2 )
	{
		tree_check_add_input_data_branch(trInputData, vsKeys, vsLabels, TRGP_DATA_RANGE);
		return true;
	}
	///End ADD_FOR_SELECTED_NONE_COLUMN
	
	//get Data from all columns except the last column
	Column colDataFrom( wks, vnSelCols[0] );
	Column colDataTo( wks, vnSelCols[vnSelCols.GetSize()-2] );
	if( colDataFrom && colDataTo)
	{
		string strRange = make_range_string(wks, colDataFrom.GetName(), colDataTo.GetName());
		tree_check_add_input_data_branch(trInputData, vsKeys, vsLabels, TRGP_DATA_RANGE, 1, 0, strRange);
	}
	else
		return false;
	
	//get the last column as State data
	Column colState( wks, vnSelCols[vnSelCols.GetSize()-1] );
	if(colState)
	{
		string strRange = make_range_string(wks, colState.GetName(), NULL);
		tree_check_add_input_data_branch(trInputData, vsKeys, vsLabels, TRGP_DATA_RANGE, 1, 1, strRange);
	}
	else 
		return false;
	
	return true;	
}
///End SUPPORT_STATS_INPUT_FORMAT

/// Iris 10/09/05 SUPPORT_AUTO_SELECTION_DATA_FOR_MULTI_REGRESSION
static bool _init_input_data_branch_from_selection_one_dep_multi_indep(TreeNode& trInputData, DWORD dwDataRule, vector<string>& vsKeys,
		vector<string>& vsLabels, int* pErrCode, OriginObject& obj)
{
	Worksheet wks = _get_wks(obj);

	if( !wks )
	{
		return error_report("The active window is not worksheet, must select data from worksheet for multiple regression");	
	}
	/// Iris 10/18/05 UPDATE_MR_DATA_SELECTION_BETTER
	/*
	int 	nn, nRange = 0;
	string 	strRange, strCols = wks.GetColDesignations();
	nn = strCols.Find('Y');
	if( nn<0 )
		return error_report("Must has one Y column at least");
	strRange = make_range_string(wks, wks.Columns(nn).GetName());
	tree_check_add_input_data_branch(trInputData, vsKeys, vsLabels, TRGP_DATA_RANGE, 1, nRange++, strRange);
	
	int nEnd, nBegin = 0;
	while(1)
	{
		nn = strCols.Find("X", nn);
		if( nn<0 )
			break;
		if( 0 == nBegin)
			nBegin = nn;
		nEnd = nn++;
	}
	strRange = make_range_string(wks, wks.Columns(nBegin).GetName(), wks.Columns(nEnd).GetName());
	tree_check_add_input_data_branch(trInputData, vsKeys, vsLabels, TRGP_DATA_RANGE, 1, nRange++, strRange);
	
	nn = strCols.Find('M');
	if( nn>=0 )
	{
		strRange = make_range_string(wks, wks.Columns(nn).GetName());
		tree_check_add_input_data_branch(trInputData, vsKeys, vsLabels, TRGP_DATA_RANGE, 1, nRange++, strRange);
	}	
	*/
	vector<int> 	vnSelCols;
	wks.GetSelectedColumns(vnSelCols);
	
	int				nErrCode=-1;
	int 			nYCol=-1, nXColBegin=-1, nXColEnd=-1, nErrCol=-1;
	if(0 != vnSelCols.GetSize())
	{	
		for(int ii=0; ii<vnSelCols.GetSize(); ii++)
		{
			int nCol = vnSelCols[ii];
			Column col(wks, nCol);
			int nColType = col.GetType();
			switch(nColType)
			{
			case OKDATAOBJ_DESIGNATION_Y:
				if(nYCol<0)
					nYCol = nCol;
				break;
			case OKDATAOBJ_DESIGNATION_X:
				if(nXColBegin<0)
					nXColBegin = nCol;
				nXColEnd = nCol;
				break;
			case OKDATAOBJ_DESIGNATION_X_ERROR:
			case OKDATAOBJ_DESIGNATION_ERROR:/// Iris 06/04/2007 MR_NEED_SUPPORT_Y_ERR_AS_WEIGHT
				if(nErrCol<0)
					nErrCol = nCol;
				break;
			default:
				break;
			}		
		}
		if(nXColBegin<0)
			nErrCode = ERR_DATASEL_MUST_HAVE_X;
		
		//if not select Y column, and if the first column is not X type, then make it as Y column
		if(nYCol<0 && nXColBegin!=0)
			nYCol = 0;
			
	}
	else
		nErrCode = ERR_DATASEL_NO_DATA_SELECTED;
		
	string 	strRange;	
	//Y column
	strRange = (nErrCode<ERR_UNKNOWN && nYCol>=0 )? make_range_string(wks, wks.Columns(nYCol).GetName()) : "";
	tree_check_add_input_data_branch(trInputData, vsKeys, vsLabels, TRGP_DATA_RANGE, 1, 0, strRange);
	
	// X columns
	strRange = (nErrCode<ERR_UNKNOWN)? make_range_string(wks, wks.Columns(nXColBegin).GetName(), wks.Columns(nXColEnd).GetName()) : "";
	tree_check_add_input_data_branch(trInputData, vsKeys, vsLabels, TRGP_DATA_RANGE, 1, 1, strRange);
	
	//X Error column
	strRange = (nErrCode<ERR_UNKNOWN && nErrCol>=0 )? make_range_string(wks, wks.Columns(nErrCol).GetName()) : "";
	tree_check_add_input_data_branch(trInputData, vsKeys, vsLabels, TRGP_DATA_RANGE, 1, 2, strRange);
	
	if(pErrCode)
		*pErrCode = nErrCode;
	///eND UPDATE_MR_DATA_SELECTION_BETTER
	return true;
}
///End SUPPORT_AUTO_SELECTION_DATA_FOR_MULTI_REGRESSION

//	int	init_input_data_branch_from_selection_XY(TreeNode& trInputData)
/// ML 1/13/2006 XVARIABLEBASE_TO_VC
//bool	init_input_data_branch_from_selection(TreeNode& trInputData, DWORD dwDataRule, int* pErrCode, OriginObject& objRef, int& nInputPageType)
bool	init_input_data_branch_from_selection(TreeNode& trInputData, DWORD dwDataRule, int* pErrCode, OriginObject& objRef, int *pnInputPageType)
/// end XVARIABLEBASE_TO_VC
{
	/// ML 1/13/2006 XVARIABLEBASE_TO_VC
	return Project.InitInputDataBranchFromSelection(trInputData, dwDataRule, pErrCode, objRef, pnInputPageType);    
	/////////////////////////////////////////////////////////////////////
	/////////////////////////////////////////////////////////////////////
	/////////////////////////////////////////////////////////////////////
	/// end XVARIABLEBASE_TO_VC



	Layer layer;
	PageBase page;
	OriginObject obj;

	if( objRef )
	{
		obj = objRef;
		layer = obj;
		OriginObject objParent;
		obj.GetParent(objParent);
		page = objParent;
	}
	else
	{
		Project project;
		obj = project;
		layer = Project.ActiveLayer();
		page = Project.Pages();
	}

	if( !obj )
		return false;

	if( !layer )
		return false;

	if( !page )
		return false;
	
	///Iris 11/01/05 SAVE_INPUT_DATA_WND_TYPE_TO_OPERATION_TREE
	/// ML 1/13/2006 XVARIABLEBASE_TO_VC
	//if(NULL != nInputPageType)
	//	nInputPageType = page.GetType();
	if(NULL != pnInputPageType)
		*pnInputPageType = page.GetType();
	/// end XVARIABLEBASE_TO_VC
	///End SAVE_INPUT_DATA_WND_TYPE_TO_OPERATION_TREE	

	vector<string> vsMulIndepKeys = {"Y", "X", "ED"};
	/// Iris 02/04/06 QA70-8480 UPDATE_DATA_RANGE_MANIPULATION
	//vector<string> vsMulIndepLabels = {"Dependent Data", "Independent Data", "Error Bar Data Range"};
	/// Max 07-11-05 CORRECT_LOCALIZTION
	//vector<string> vsMulIndepLabels = {"Dependent Data", "Independent Data", "Error Bar / Weight"};
	vector<string> vsMulIndepLabels;
	vsMulIndepLabels.Add(_L("Dependent Data"));
	vsMulIndepLabels.Add(_L("Independent Data"));
	vsMulIndepLabels.Add(_L("Error Bar / Weight"));
	/// END CORRECT_LOCALIZTION
	///End UPDATE_DATA_RANGE_MANIPULATION	
	vector<string> vsXYZKeys = {"X", "Y", "Z"};
	/// Max 07-11-05 CORRECT_LOCALIZTION
	//vector<string> vsXYZLabels = {"X Data Range", "Y Data Range","Z Data Range"};
	vector<string> vsXYZLabels;
	vsXYZLabels.Add(_L("X Data Range"));
	vsXYZLabels.Add(_L("Y Data Range"));
	vsXYZLabels.Add(_L("Z Data Range"));
	/// END CORRECT_LOCALIZTION
	vector<string> vsXYKeys = {"X", "Y", "ED"};
	/// Iris 02/04/06 QA70-8480 UPDATE_DATA_RANGE_MANIPULATION
	//vector<string> vsXYLabels = {"X Data Range", "Y Data Range", "Error Bar Data Range"};
	/// Max 07-11-05 CORRECT_LOCALIZTION
	//vector<string> vsXYLabels = {"X Data Range", "Y Data Range", "Error Bar / Weight"};
	vector<string> vsXYLabels;
	vsXYLabels.Add(_L("X Data Range"));
	vsXYLabels.Add(_L("Y Data Range"));
	vsXYLabels.Add(_L("Error Bar / Weight"));
	/// END CORRECT_LOCALIZTION
	///End UPDATE_DATA_RANGE_MANIPULATION	
	vector<string> vsKeys = {"X", "F", "W"};
	/// Max 07-11-05 CORRECT_LOCALIZTION
	//vector<string> vsLabels = {"Data Range", "Grouping Range", "Weighting Range"};
	vector<string> vsLabels;
	vsLabels.Add(_L("Data Range"));
	vsLabels.Add(_L("Grouping Range"));
	vsLabels.Add(_L("Weighting Range"));
	/// END CORRECT_LOCALIZTION
	/// Iris 8/29/05 SUPPORT_STATS_INPUT_FORMAT
	// for Survival Analysis
	vector<string> vsSAKeys = {"X", "Y", "Y"};
	/// Max 07-11-05 CORRECT_LOCALIZTION
	//vector<string> vsSALabels = {"Time", "Censor", "Covariance"};
	vector<string> vsSALabels;
	vsSALabels.Add(_L("Time"));
	vsSALabels.Add(_L("Censor"));
	vsSALabels.Add(_L("Covariance"));
	/// END CORRECT_LOCALIZTION
	// for ROC
	vector<string> vsROCKeys = {"X", "Y"};
	/// Max 07-11-05 CORRECT_LOCALIZTION
	//vector<string> vsROCLabels = {"Data", "State"};
	vector<string> vsROCLabels;
	vsROCLabels.Add(_L("Data"));
	vsROCLabels.Add(_L("State"));
	/// END CORRECT_LOCALIZTION
	///End SUPPORT_STATS_INPUT_FORMAT
	
	
	if( EXIST_PLOT == page.GetType())
	{		
		DataRange dr;
		
		int nType = obj.GetSelection(dr);		

		//---- CPY 1/4/06 Iris 12/19/05 QA70-6367-P8 DESC_STATS_ON_GRAPH
		if(EXIST_PLOT == nType) //CPY nType might become 0, need to look into this later
		{
			if(dwDataRule & DRR_GET_DEPENDENT)
			{
				dr.GetTree(trInputData, FALSE);
				/// YuI 11/11/04
				//	int nUIDRange = dr.GetUID(true);
				//	trInputData.SetAttribute(STR_DATARANGE_UID_ATTRIB, nUIDRange);
				
				// during lifetime of dialog there are two ranges
				// one is original range and another one 
				// is dialog's proprietary range
				// so we need to ensure here that uid of data range in tree
				// does not get reset with dialog's proprietary range
				// but only tree updates
				// and origin does come here on click OK (trough OnEdit)
				// and there is chance that dialog's proprietary
				// range is selected in the graph at that time
				// so tree must be updated but range UID should not be changed in tree
				// later when it reaches SetTree
				// it will update Original range from tree
				DataRange drExisting;
				uint uidDataRangeExisting;
				drExisting = get_data_range_from_input_data(trInputData, uidDataRangeExisting);
				if( !drExisting )
				{
					int nUIDRange = dr.GetUID(true);
					trInputData.SetAttribute(STR_DATARANGE_UID_ATTRIB, nUIDRange);
				}
				/// end YuI 11/11/04
				//	return nType;
				return true;
			}
			
			/// Iris 12/19/05 QA70-6367-P8 DESC_STATS_ON_GRAPH
			vector<string>	arrstrIndependent, arrstrDependent;
			if( obj.GetSelection(arrstrIndependent, &arrstrDependent) <= 0)
				return false;		
			for(int ii=0; ii<arrstrDependent.GetSize(); ii++)
			{
				TreeNode tr = tree_check_add_input_data_branch(trInputData, vsKeys, vsLabels, TRGP_BRANCH, ii+1);
				tr.X.strVal = arrstrDependent[ii];
			}
			return true;
			///End DESC_STATS_ON_GRAPH
		}
	}	

		
	
	bool bGetDependents = false;
	/// Iris 8/29/05 SUPPORT_STATS_INPUT_FORMAT
	if( dwDataRule & DRR_GET_STATS_SA)
	{			
		return _init_input_data_branch_from_selection_for_survival_analysis(trInputData, dwDataRule, vsSAKeys, vsSALabels, layer);
	}
	else if( dwDataRule & DRR_GET_STATS_STATE_LAST_COL)
	{
		return _init_input_data_branch_from_selection_for_ROC(trInputData, dwDataRule, vsROCKeys, vsROCLabels, layer);
	}
	else
	///End SUPPORT_STATS_INPUT_FORMAT
	if(dwDataRule & DRR_GET_Z_DEPENDENT)
	{
		vsKeys = vsXYZKeys;
		vsLabels = vsXYZLabels;
		bGetDependents = true;
		
		/// Iris 11/17/05 SUPPORT_GETTING_DATA_RANGE_FROM_MATRIX
		if(EXIST_MATRIX == page.GetType())
		{	
			MatrixLayer ml = layer;			
			if( ml )
			{
				MatrixObject mo = ml.MatrixObjects();
				if( mo )
				{
					string strRange = make_range_string(ml, mo.GetName());
					DataRange dr;
					/// YuI 11/21/05 QA70-7450 MATRIX_RANGE_SHOULD_BE_Z
					//	dr.Add("X", strRange);	
					dr.Add("Z", strRange);	
					/// end MATRIX_RANGE_SHOULD_BE_Z
					dr.GetTree(trInputData, FALSE);
					return true;
				}
			}					
		}
		///End SUPPORT_GETTING_DATA_RANGE_FROM_MATRIX
	}
	else if(dwDataRule & DRR_GET_DEPENDENT)
	{
		vsKeys = vsXYKeys;
		vsLabels = vsXYLabels;
		bGetDependents = true;
		
		if(dwDataRule & DRR_ONE_DEP_MULTIINDEP)
		{
			vsKeys = vsMulIndepKeys;
			vsLabels = vsMulIndepLabels;
			/// Iris 10/09/05 SUPPORT_AUTO_SELECTION_DATA_FOR_MULTI_REGRESSION
			return _init_input_data_branch_from_selection_one_dep_multi_indep(trInputData, dwDataRule, vsKeys, vsLabels, pErrCode, layer);
			///End SUPPORT_AUTO_SELECTION_DATA_FOR_MULTI_REGRESSION
			
		}
	}
	/// YuI 07/19/05 QA70-7900 MATRIX_OBJECT_XFUNCTION_AUTOUPDATE
	else if( dwDataRule & DRR_MATRIX_DATA )
	{
		// matrix is special - Project.GetSelection will not resolve it and is not supposed to
		// so we here check if MatrixPage is active - we ghet active matrix object and construct tree
		MatrixLayer ml = layer;

		if( ml )
		{
			MatrixObject mo = ml.MatrixObjects();
			if( mo )
			{
				string strRange = make_range_string(ml, mo.GetName());
				/// YuI 11/21/05 QA70-7450 MATRIX_RANGE_SHOULD_BE_Z
				//	tree_check_add_input_data_branch(trInputData, vsKeys, vsLabels, TRGP_DATA_RANGE, 1, 0, strRange);
				tree_check_add_input_data_branch(trInputData, vsXYZKeys, vsXYZLabels, TRGP_DATA_RANGE, 1, 2, strRange);
				/// end MATRIX_RANGE_SHOULD_BE_Z
				return true;
			}
		}
		return false;
	}
	/// end MATRIX_OBJECT_XFUNCTION_AUTOUPDATE
		
	int	nSel;
	vector<string>	arrstrIndependent;
	if(bGetDependents)
	{
		vector<string>	arrstrDependent, arrstrYErrorBars, arrstrZ;  
		vector<uint> 	arrPlotObjUIDs;

		if(dwDataRule & DRR_GET_Z_DEPENDENT)
			nSel = obj.GetSelection(arrstrIndependent, &arrstrDependent, &arrstrZ, NULL, &arrPlotObjUIDs);
		else
			nSel = obj.GetSelection(arrstrIndependent, &arrstrDependent, NULL, &arrstrYErrorBars, &arrPlotObjUIDs);
		
		int				nSizeDep = arrstrDependent.GetSize();
		int				nSizeIndep = arrstrIndependent.GetSize();
		int				nSizeErrorBars = arrstrYErrorBars.GetSize();
		int				nSizeZ = arrstrZ.GetSize();

		if(0 == nSizeDep && 0 == nSizeIndep && 0 == nSizeErrorBars && 0 == nSizeZ)
		{
			tree_check_add_input_data_branch(trInputData, vsKeys, vsLabels, TRGP_DATA_RANGE);
			//prepare_input_data_for_operation(trInputData, (dwDataRule & DRR_ADD_TO_PROJECT)? true:false);
			if(pErrCode)
			{
				*pErrCode = ERR_UNKNOWN;
				if(nSel < 0)
					*pErrCode = ERR_DATASEL_NO_DATA_SELECTED;
				else
				{
					if(dwDataRule & DRR_GET_Z_DEPENDENT)
						*pErrCode = ERR_DATASEL_MUST_HAVE_Z;
					else if(dwDataRule & DRR_GET_DEPENDENT)
						*pErrCode = ERR_DATASEL_MUST_HAVE_Y;
				}
			}
			return false;
		}
		int	ii;
		for(ii = 0; ii < nSizeIndep; ii++ )
			tree_check_add_input_data_branch(trInputData, vsKeys, vsLabels, TRGP_DATA_RANGE, ii+1, 0, arrstrIndependent[ii]);
		for(ii = 0; ii < nSizeDep; ii++ )
		{
			TreeNode trRange = tree_check_add_input_data_branch(trInputData, vsKeys, vsLabels, TRGP_DATA_RANGE, ii+1, 1, arrstrDependent[ii]);
			if(!(dwDataRule & DRR_GET_Z_DEPENDENT) && trRange && arrPlotObjUIDs.GetSize() == nSizeDep && arrPlotObjUIDs[ii] != 0 )
				trRange.SetAttribute(STR_PLOTOBJ_UID_ATTRIB, ftoa(arrPlotObjUIDs[ii]));
		}
		if(dwDataRule & DRR_GET_Z_DEPENDENT)
		{
			for(ii = 0; ii < nSizeZ; ii++ )
			{
				TreeNode trRange = tree_check_add_input_data_branch(trInputData, vsKeys, vsLabels, TRGP_DATA_RANGE, ii+1, 2, arrstrZ[ii]);
				if(trRange && arrPlotObjUIDs.GetSize() == nSizeZ && arrPlotObjUIDs[ii] != 0 )
					trRange.SetAttribute(STR_PLOTOBJ_UID_ATTRIB, ftoa(arrPlotObjUIDs[ii]));
			}
		}
		else
		{
			for(ii = 0; ii < nSizeErrorBars; ii++ )
				tree_check_add_input_data_branch(trInputData, vsKeys, vsLabels, TRGP_DATA_RANGE, ii+1, 2, arrstrYErrorBars[ii]);
		}
	}
	else
	{
		// check if Grouping range or Weight needed to be removed
		if(dwDataRule & (DRR_NO_WEIGHTS | DRR_NO_FACTORS))
		{
			vector<string> vsKeysTemp;
			vector<string> vsLabelsTemp;
			// assume 0 = "Data Range", 1= "Grouping Range", 2 = "Weighting Range"
			for(int ii = 0; ii < vsKeys.GetSize(); ii++)
			{
				if(ii == 1 && (dwDataRule & DRR_NO_FACTORS))
					continue;
				if(ii == 2 && (dwDataRule & DRR_NO_WEIGHTS))
					continue;
				
				vsKeysTemp.Add(vsKeys[ii]);
				vsLabelsTemp.Add(vsLabels[ii]);
			}
			vsKeys = vsKeysTemp;
			vsLabels  = vsLabelsTemp;
		}
		string	strSelection;
		//	nSel = Project.GetSelection(arrstrIndependent);
		nSel = obj.GetSelection(arrstrIndependent);
		int	nSize = arrstrIndependent.GetSize();
		if( 0 < nSize )
			strSelection = arrstrIndependent[0];
		else
			strSelection.Empty();
		TreeNode tr = tree_check_add_input_data_branch(trInputData, vsKeys, vsLabels, TRGP_BRANCH);
		tr.X.strVal = strSelection;
	
	}
	//prepare_input_data_for_operation(trInputData, (dwDataRule & DRR_ADD_TO_PROJECT)?true:false);
	return true;
}
/// end NLFITTER_OPERATION_IMPLEMENTATION

/// Iris 9/10/04
/// Iris 6/02/2008 QA80-11639 v8.0875 DUP_PERCENT_CAUSE_BOX_CHART_PLOT_LOOKS_BAD
//bool 	get_tokens_check_range(string& str, vector& vVals, char chDelimiter, double dFrom, double dTo)
bool 	get_tokens_check_range(string& str, vector& vVals, char chDelimiter, double dFrom, double dTo, bool bRemoveDup)
///end DUP_PERCENT_CAUSE_BOX_CHART_PLOT_LOOKS_BAD
{
	if( NULL == str || NULL == vVals)
		return false;
	
	if( 0 == str.GetLength() )
		return false;
	
	vVals.RemoveAll();
	
	/// Iris 06/26/06 NEED_TRIM_EMPTY_STRING
	//vector vTemp;
	//int nTokens = str.GetTokens(vTemp, chDelimiter);
	vector<string> vsTemp;
	int nTokens = str.GetTokens(vsTemp, chDelimiter);
	///End NEED_TRIM_EMPTY_STRING
	for(int ii = 0; ii < nTokens; ii++)
	{
		/// Iris 06/26/06 NEED_TRIM_EMPTY_STRING
		//double dTemp = vTemp[ii];
		if(vsTemp[ii].IsEmpty())
			continue;
		///End NEED_TRIM_EMPTY_STRING
		
		double dTemp = atof(vsTemp[ii]);
		if(is_missing_value(dTemp))
			continue;
		
		/// Iris 6/02/2008 QA80-11639 v8.0875 DUP_PERCENT_CAUSE_BOX_CHART_PLOT_LOOKS_BAD
		vector<uint>	vIndex;
		if( bRemoveDup && vVals.Find(vIndex, dTemp, dTemp) > 0 )
			continue;
		///end DUP_PERCENT_CAUSE_BOX_CHART_PLOT_LOOKS_BAD
		
		if( NANUM != dFrom && NANUM != dTo)
		{
			if( dTemp >= dFrom && dTemp <= dTo)
				vVals.Add(dTemp);
		}
		else
		{
			vVals.Add(dTemp);
		}			
	}	
	return true;
}

string 	rollback_string_tokens(vector& vVals, char chDelimiter)
{
	string str;
	if( NULL == vVals)
	{
		return "";
	}
	
	for(int ii=0; ii<vVals.GetSize(); ii++)
	{
		string strTemp;	
		strTemp.Format("%d%c", vVals[ii], chDelimiter);
		str	+= strTemp;	
	}
	if( 0 != str.GetLength() )
		str.Delete( str.GetLength()-1 );
	
	return str;
}

string 	rollback_string_tokens(StringArray& saVals, char chDelimiter)
{
	string str;
	if( NULL == saVals )
	{
		return "";
	}
	
	for(int ii=0; ii<saVals.GetSize(); ii++)
	{
		string strTemp;	
		strTemp.Format("%s%c", saVals[ii], chDelimiter);
		str	+= strTemp;	
	}
	if( 0 != str.GetLength() )
		str.Delete( str.GetLength()-1 );

	return str;
}



// <source> can be skipped as is already checked inside page_short_name_from_display_name
/// EJP 2005-04-15 v8.0223 CLEAN_ACTIVE_IMPLEMENTATION
///	//static int _cvt_str_to_predefined_type(const string& str)		///Iris 11/16/04 QA70-7139 UPDATE_2ND_ARGUMENT_FOR_PAGE_SHORT_NAME_FROM_DISPLAY_NAME
///	int cvt_str_to_predefined_type(const string& str)
int cvt_str_to_predefined_type(LPCSTR lpcsz)
/// end CLEAN_ACTIVE_IMPLEMENTATION
{
	///DSC 6/27/05	CONSTRUCT_THEME_FILE_FROM_COMPOSITE_NAME
	/*
	/// EJP 2005-04-15 v8.0223 CLEAN_ACTIVE_IMPLEMENTATION
	///	if(str.GetLength() < 3 || str[0] != '<')
	///		return PDS_USER;
	string str = lpcsz;
	str.TrimLeft();
	str.TrimRight();
	if(str.IsEmpty())
		return PDS_USER;
	if( str[0] != '<')
		return PDS_USER;
	
	int n = str.Find('>');
	if( n < 2 )
		return PDS_USER;
	
	str = str.Left(n + 1);
	/// end CLEAN_ACTIVE_IMPLEMENTATION
	
	if(str.CompareNoCase(STR_NEW) == 0)
		return PDS_NEW;
	else if(str.CompareNoCase(STR_NONE) == 0)
		return PDS_NONE;
	else if(str.CompareNoCase(STR_SOURCE_BOOK) == 0)  ///Iris 11/16/04 QA70-7139 UPDATE_2ND_ARGUMENT_FOR_PAGE_SHORT_NAME_FROM_DISPLAY_NAME
		return PDS_SOURCE;
	else if(str.CompareNoCase(STR_REPORT) == 0)
		return PDS_REPORT;
	else if(str.CompareNoCase(STR_CUSTOM) == 0)   ///Iris 11/15/04 QA70-7139 ADD_CUSTOM_OPTION_TO_REPORT
		return PDS_CUSTOM;
	
	///DSC 3/25/03 CLEANUP_LAST_USE
	else if(str.CompareNoCase(STR_LAST_USED) == 0)
		return PDS_LAST_USED;
	else if(str.CompareNoCase(THEME_FILENAME_FACTORY_DEFAULT) == 0)
		return PDS_FACTORY_DEFAULT;
	///end CLEANUP_LAST_USE

	/// EJP 2005-04-15 v8.0223 CLEAN_ACTIVE_IMPLEMENTATION
	else if( str.CompareNoCase(STR_ACTIVE) == 0 )
		return PDS_ACTIVE;
	/// end CLEAN_ACTIVE_IMPLEMENTATION

	/// YuI 05/20/05
	else if( str.CompareNoCase(STR_SAME) == 0 )
		return PDS_SAME;
	/// end YuI
	return PDS_USER;
	*/
	return okutil_cvt_str_to_predefined_type(lpcsz);
	///end CONSTRUCT_THEME_FILE_FROM_COMPOSITE_NAME
}
// lpcszStr = "<source> book1-long name" 
// 		return PDS_SOURCE, str = "book1"
// lpcszStr = "book1" 
// 		reutrn PDS_USER, str = "book1"
int str_to_predefined_type(LPCSTR lpcszStr, string* pstr) //=NULL
{
	int nType = PDS_USER;
	///Iris 11/16/04 QA70-7139 UPDATE_2ND_ARGUMENT_FOR_PAGE_SHORT_NAME_FROM_DISPLAY_NAME
	/*
	bool bSourceBook = false;
	// special handling for page source since it will need to short page name in that case
	string str = page_short_name_from_display_name(lpcszStr, bSourceBook);
	
 	if(bSourceBook)
 	{
 		nType = PDS_SOURCE;
 	}
 	else
 	{
 		str = lpcszStr;  ///Iris 11/15/04 QA70-7139 ADD_CUSTOM_OPTION_TO_REPORT, should pass lpcszStr here if not Source Book. Adding the assignment because of QA70-7168
 		nType = _cvt_str_to_predefined_type(str); 
 	}
 	*/
 	///End UPDATE_2ND_ARGUMENT_FOR_PAGE_SHORT_NAME_FROM_DISPLAY_NAME
	string str = page_short_name_from_display_name(lpcszStr, nType);
	/// ML 3/3/2005 XFUNCTION_OUTPUT_VARIABLES_DATA_ASSOC
	//if(PDS_SOURCE != nType && PDS_CUSTOM != nType)
	/// YuI 05/23/05 XVARIABLE_SAME_IMPLEMENTATION
	//if(PDS_SOURCE != nType && PDS_CUSTOM != nType && PDS_NEW != nType && PDS_ACTIVE != nType)
	/// Iris 3/21/2008 v8.0829 QA80-10934 ADD_EDITBOX_TO_SPECIFICATION_BOOK_SHEET_NAME
	//if(PDS_SOURCE != nType && PDS_CUSTOM != nType && PDS_NEW != nType && PDS_ACTIVE != nType && PDS_SAME != nType)
	if(PDS_SOURCE != nType && PDS_CUSTOM != nType && PDS_NEW != nType && PDS_ACTIVE != nType && PDS_SAME != nType && PDS_AUTO != nType)
	///end ADD_EDITBOX_TO_SPECIFICATION_BOOK_SHEET_NAME
	/// end XVARIABLE_SAME_IMPLEMENTATION
	/// end XFUNCTION_OUTPUT_VARIABLES_DATA_ASSOC
	{
 		str = lpcszStr;  ///Iris 11/15/04 QA70-7139 ADD_CUSTOM_OPTION_TO_REPORT, should pass lpcszStr here if not Source Book. Adding the assignment because of QA70-7168
 		nType = cvt_str_to_predefined_type(str); 
	}
 	
 	if(pstr)
 		*pstr = str;
 	return nType;
}

///Iris 4/16/05 EXISTING_BOOK_NEED_INCLUDE_HIDDEN_BOOK
/*
string get_show_books_in_project(string& strExclude)
{
	string strList;
	foreach(PageBase pg in Project.Pages)
	{
		if( EXIST_WKS == pg.GetType() )
		{
			if(PAGE_HIDDEN == pg.GetShow())
				continue;
			
			if( 0 == strExclude.Compare(pg.GetName()) )
				continue;
			
			strList += pg.GetName() + STR_TOKEN_SEP;
		}
	}
	if(!strList.IsEmpty())
		strList.Delete(strList.GetLength()-1);
	return strList;
}
*/
///end EXISTING_BOOK_NEED_INCLUDE_HIDDEN_BOOK
//-------- End of File

/// ML 11/11/2004 QA70-6845 CELL_VALUE_LINKING
void	build_report_nodes_link_string(string &str, int *pnArrIDs, int nCount)
{
	str = STR_LINK_CELL_VALUE_PREFIX;
	for (int ii = 0; ii < nCount; ii++)
	{
		str += '/';
		string		strTemp;
		strTemp.Format("%u", pnArrIDs[ii]);
		str += strTemp;
	}
}
/// end CELL_VALUE_LINKING



///Danice TREE_ROW_SHOW_ALTERNATE_COLORS
void get_analysis_gui_color(string &strColors, string &strSpecialIndicatorColors, string &strActiveObjectColors)	//=NULL
{
	uint nLeaf2= COLOR_WHITE;
	uint nLeaf1 = RGB(0xF0,0xF0,0xF0);	//default alternate color
	uint nBranch1=RGB(255,255,192);//RGB(215, 226, 180);
	uint nBranch2=RGB(220,255,180);//RGB(166, 202, 240);
	uint nBranch3=RGB(192,255,255);//COLOR_LTYELLOW;
	uint nBranch4=RGB(255,192,255);
	vector<uint> vnBranchColors;
	vnBranchColors.Add(nBranch1);
	vnBranchColors.Add(nBranch2);
	vnBranchColors.Add(nBranch3);
	vnBranchColors.Add(nBranch4);
	/*
	uint nLeaf2= COLOR_WHITE;
	uint nLeaf1 = RGB(0xF0,0xF0,0xF0);	//default alternate color
	vector<uint> vnBranchColors = {
		RGB(255,255,192), 
		RGB(220,220,192), 
		RGB(192,220,220), 
		RGB(220,192,220)
	};
	*/
	strColors=get_GetN_background_colors(nLeaf1, nLeaf2, vnBranchColors);
	
	uint nForeColor=COLOR_RED;
	uint nBackGroundColor=RGB(0xF0,0xF0,0xF0);
	//---- CPY 12/2/04 ENABLE_READ_ONLY_USE_SEPARATE_COLOR, added nReadOnlyColor
	uint nReadOnlyColor = COLOR_BLUE; 
	uint nReadOnlyBackColor = 0; // ignore
	strSpecialIndicatorColors=(string)nForeColor+"|"+nBackGroundColor + "|" + nReadOnlyColor + "|" + nReadOnlyBackColor;
	
	///Danice 11/22/04 : add active object color: colorMain | colorInput | colorOutput
	if(NULL != strActiveObjectColors)
	{
		int nMain=0;	//default black
		int nInput=COLOR_RED;
		int nOutput=COLOR_BLUE;
		strActiveObjectColors=(string)nMain+"|"+(string)nInput+"|"+(string)nOutput;
	}
	///end
}
///end




///////////////////////////////////////////////////////////////////////////////
//--------- CPY 11/26/04 OUTPUT_FIT_CURVES_TO_SOURCE_SHEET

static void __set_rc(int* nrc1, int* nrc2, bool bCheck = false, int nSize = 4)
{
	for(int ii = 0; ii < nSize; ii++)
	{
		if(bCheck)
		{
			if(ii > 1)
			{
				if(nrc2[ii] > nrc1[ii])
					nrc1[ii] = nrc2[ii];
			}
			else
			{
				if(nrc2[ii] < nrc1[ii])
					nrc1[ii] = nrc2[ii];
			}
		}
		else
			nrc1[ii] = nrc2[ii];
	}
}
			
int find_input_range_bounding_box(const DataRange& rngInput, int& r1, int& c1, int& r2, int& c2, Worksheet& wksFirst, LPCSTR lpcszType)
{
	int nInRanges = rngInput.GetNumRanges();
	int rc[4];
	int nWks = 0;
	for(int ii = 0; ii < nInRanges; ii++)
	{
		Worksheet wks;
		int nrc[4];
		int jj;
		string strName;
		if(rngInput.GetRange(ii, nrc[0], nrc[1], nrc[2], nrc[3], wks, &strName) && nrc[0] >= 0 && (NULL==lpcszType || strName.CompareNoCase(lpcszType) == 0))
		{
			if(0 == nWks)
			{
				wksFirst = wks;
				__set_rc(rc, nrc);
				nWks++;
			}
			else if(worksheets_are_same(wks, wksFirst))
			{
				__set_rc(rc, nrc, true);
			}
			else
				nWks++;
		}
	}
	r1 = rc[0], c1 = rc[1], r2 = rc[2], c2 = rc[3];
	return nWks;
}

bool find_input_range_bounding_box(const DataRange& rngInput, int& r1, int& c1, int& r2, int& c2, Worksheet& wks, int nDataIndex, string strTypeSeparator) //="X"
{
	int nInRanges = rngInput.GetNumRanges();
	int rc[4];
	int nGroup = -1;
	for(int ii = 0; ii < nInRanges; ii++)
	{
		Worksheet wksTemp;
		int nrc[4];
		int jj;
		string strName;
		if(rngInput.GetRange(ii, nrc[0], nrc[1], nrc[2], nrc[3], wksTemp, &strName) && nrc[0] >= 0)
		{
			if(strName.CompareNoCase(strTypeSeparator) == 0)
			{
				nGroup++;
				if(nDataIndex +1 == nGroup)
					break;
				__set_rc(rc, nrc);
				wks = wksTemp;
			}
			else
				__set_rc(rc, nrc, true);				
		}
	}
	if(wks)
	{
		r1 = rc[0], c1 = rc[1], r2 = rc[2], c2 = rc[3];
		return true;
	}
	return false;
}

/// Max 7/13/06	COMMENT_ALL_ADVANCED_OPTIONS	// All options should be visible in regular mode
///Iris 12/27/04 IMPROVE_ACCESS_ADVAN_IDS_THEME_FILE
//const vector<int> vnDescAdvanIDs = {IDE_GEO_MEAN, IDE_LCI, IDE_UCI, IDE_SD_X_2, 
//IDE_SD_X_3, IDE_MAD, IDE_IMIN, IDE_IMAX, IDE_IQR, IDE_RANGE, IDE_USE_CUSTOM_PERCENTILES, IDE_CUSTOM_PERCENTILES};
//const static vector<int> s_vnAdvancedGUI_ids = {IDE_GEO_MEAN, IDE_LCI, IDE_UCI, IDE_SD_X_2, IDE_SD_X_3, IDE_MAD, IDE_IMIN, IDE_IMAX, IDE_IQR, IDE_RANGE, IDE_USE_CUSTOM_PERCENTILES, IDE_CUSTOM_PERCENTILES, 159};
///Echo 3/22/05 v8.0207 QA70-6204 ADD_GSD
///Echo 11/22/05 v8.0339 ADD_MODE
///Echo 5/31/06 RESET_STATS_ADVANCED_OPTIONS
/*
const static vector<int> s_vnAdvancedGUI_ids = {
	//Desc Stats
	//IDE_NUM_MISSING, 
	IDE_MODE, 
	//IDE_VARIANCE, IDE_COV, IDE_SKEWNESS, IDE_KURTOSIS, IDE_UNCORSUMSQ, IDE_CORSUMSQ, IDE_WEIGHTSUM,
	//Kevin 09/01/05 CHANGE_STATISTICS_NAME
	//IDE_GEO_MEAN, IDE_GEO_SD, IDE_LCI, IDE_UCI, IDE_SD_X_2, IDE_SD_X_3, IDE_MAD, IDE_IMIN, IDE_IMAX, 
	//IDE_GEO_MEAN, IDE_GEO_SD, 
	IDE_LCL, IDE_UCL, IDE_SD_X_2, IDE_SD_X_3, 
	//IDE_MAD, IDE_IMIN, IDE_IMAX, 
	//End CHANGE_STATISTICS_NAME
	//IDE_IQR, IDE_RANGE, IDE_USE_CUSTOM_PERCENTILES, IDE_CUSTOM_PERCENTILES, IDE_COMPUTER_CONTROL,	
	//Fit Control
	
	//Kevin 09/01/05 CHANGE_STATISTICS_NAME
	//IDE_PARAM_FIX, IDE_PARAM_LCI, IDE_PARAM_UCI, IDE_PARAM_CONF_INT, 
	IDE_PARAM_FIX, IDE_PARAM_LCL, IDE_PARAM_UCL, IDE_PARAM_CONF_INT, 
	//End CHANGE_STATISTICS_NAME
	//IDE_FIT_NUM_POINTS, IDE_FIT_DOF, IDE_FIT_REDUCED_CHI_SQUARE, IDE_FIT_RSQUARE_COD,  IDE_FIT_NORM_REDIDUALS, 
		
	IDE_FIT_SSR, 
	///Echo 1/6/05 UNADVANCED_RSQ_ADJRSQ
	//IDE_FIT_RSQUARE_COD, IDE_FIT_ADJ_RSQUARE, 
	IDE_FIT_ROOT_MSE, IDE_FIT_NORM_REDIDUALS,
	IDST_LR_OPTIONS,
	//One Sample
	IDST_CHISQ_CONTROL, IDST_POWER_CONTROL,
	//Two Sample
	IDST_FTEST_CONTROL,
	//ANOVE One Way and ANOVA Two Way
	IDST_ANOVA_MEAN_COMP, IDST_ANOVA_EQUA_VAR,
	//Optional Tables
	IDE_REPORT_CREATE_OPTION,
	//Curve Report of LR, MR and etc. 
	IDST_REPORT_CURVE_OPTIONS,
	//Function Browser Filter
	IDST_FB_SOURCE,
	//The temp last one
	//------- CPY 4/22/05 QA70-7651 DESC_STATS_ON_ROWS_CLEAN, should not add -1 to list as it can complicate things
	//-1
	0x7FFFFFF // for some reason this vector can not end with ,
	//------- end
};
*/
const static vector<int> s_vnAdvancedGUI_ids = {
	0x7FFFFFF // for some reason this vector can not end with ,
};
/// End COMMENT_ALL_ADVANCED_OPTIONS

static void _update_advanced_ids_by_pro_ids(vector<int>& vnAdvanIDs, const vector<int>& vnProIDs, bool bIsPro)
{
	/// Iris 6/09/05 CHECK_PRO_ID_WHEN_CHECK_ADVANCE_ID
/*	foreach(TreeNode tr in trParam.Children)
	{
		int nChildNodes = tr.GetNodeCount();
		if(bRecursive && nChildNodes > 0) 
			_update_advanced_ids_by_pro_ids(tr, vnIDs);
		else
		{
			int nIsPro;
			if(tr.GetAttribute(STR_ATTRIB_ISPRO, nIsPro) && !nIsPro) //is regular version
			{
				vector<uint> vIndex;
				int nCount = vnIDs.Find(vIndex, tr.DataID, tr.DataID);
				if(1 == nCount)
					vnIDs.RemoveAt(vIndex[0]);
			}
		}
	}
*/
	if(bIsPro)
		return;
	
	for(int ii=0; ii<vnProIDs.GetSize(); ii++)
	{
		vector<uint> vecIndex;
		int nFind = vnAdvanIDs.Find(vecIndex, vnProIDs[ii], vnProIDs[ii]);
		while(--nFind >= 0)
			vnAdvanIDs.RemoveAt(vecIndex[nFind]);
	}	
	///end CHECK_PRO_ID_WHEN_CHECK_ADVANCE_ID
}

void update_param_tree_advanced_ids(TreeNode& trParam, bool bShow)
{
	if(!trParam)
		return;
	
	string strShow = bShow? "1" : "0";
	vector<int> vnAdvanIDs, vnProIDs;
	
	///Iris 12/31/04 IMPROVE_ACCESS_ADVANCED_IDS_FUNC
	//if( _get_built_in_advanced_GUI_ids(_get_advanced_ids_theme_file(trParam), vnIDs) )
	
	///Frank 4/5/05 GUI_ADVANCED_SETTING_FROM_FILE	
	string strThemeFileName = tree_get_advanced_settings_file(trParam);
	if(!strThemeFileName.IsEmpty() && _get_advanced_GUI_ids(trParam, vnAdvanIDs, strThemeFileName) && _get_Pro_GUI_ids(vnProIDs))
	//if( _get_advanced_GUI_ids(vnIDs))
	///End GUI_ADVANCED_SETTING_FROM_FILE
	{
		vnAdvanIDs.Sort();	//---- CPY 4/22/05 QA70-7651 DESC_STATS_ON_ROWS_CLEAN		
		update_param_tree_for_pro_ids(trParam, is_pro_version());
		_update_advanced_ids_by_pro_ids(vnAdvanIDs, vnProIDs, is_pro_version());
		
		tree_set_attributes(trParam, vnAdvanIDs, strShow);
		if( !bShow )
			tree_GETN_reset_hidden_values(trParam, vnAdvanIDs);
	}
	///end IMPROVE_ACCESS_ADVANCED_IDS_FUNC
}

///Iris 3/30/05 HIDDEN_PRO_FEATURE_FOR_REGULAR_VERSION
const static vector<int> s_vnProGUI_ids = {
	//Desc Stats
	/// Max 3/27/08 QA70-11325 v8.0833 COMMA_IS_MISSING_IN_FIRST_LINE
	// the missing comma results in IDE_UNCORSUMSQ and IDE_CORSUMSQ lost effect
	//IDE_GEO_MEAN, IDE_GEO_SD,IDE_SD_X_2, IDE_SD_X_3, IDE_MAD, IDE_UNCORSUMSQ
	IDE_GEO_MEAN, IDE_GEO_SD,IDE_SD_X_2, IDE_SD_X_3, IDE_MAD, IDE_UNCORSUMSQ,
	/// END COMMA_IS_MISSED_IN_FIRST_LINE
	IDE_CORSUMSQ, IDE_WEIGHTSUM, IDE_COMPUTER_CONTROL,
	
	//Normality Test
	IDST_KOLM_CONTROL, IDE_NORM_LILLE,
	
	/// Max 3/27/08 QA70-11325 v8.0833 TTEST_IS_REPLACED_WITH_XF_AND_CONTROL_BECOMES_USELESS
	/*
	//One Sample t-test
	IDST_CHISQ_CONTROL,
	
	//Two Sample t-test
	IDST_FTEST_CONTROL,
	*/
	/// END TTEST_IS_REPLACED_WITH_XF_AND_CONTROL_BECOMES_USELESS
	
	//One Way ANOVA and Two Way ANOVA
	IDE_MEANCOMP_DUNNSIDAK, IDE_MEANCOMP_FISHLSD, IDE_MEANCOMP_HOLMBONF,
	IDE_MEANCOMP_HOLMSIDAK, IDE_MEANCOMP_DUNNE
	
	/// Iris 2/02/2010 QA81-14165 ALSO_SHOW_PRO_IN_PRO_VERSION, remove dup IDs
	/*
	//Two Way ANOVA
	IDE_MEANCOMP_DUNNSIDAK, IDE_MEANCOMP_FISHLSD, IDE_MEANCOMP_HOLMBONF,
	IDE_MEANCOMP_HOLMSIDAK, IDE_MEANCOMP_DUNNE
	*/
	///End ALSO_SHOW_PRO_IN_PRO_VERSION
};
//------- CPY 4/22/05 QA70-7651 DESC_STATS_ON_ROWS_CLEAN

void update_param_tree_for_pro_ids(TreeNode& trParam, bool bIsPro)
{
	/// Iris 2/02/2010 QA81-14165 ALSO_SHOW_PRO_IN_PRO_VERSION
	//if(!trParam || bIsPro)
	if(!trParam)
	///End ALSO_SHOW_PRO_IN_PRO_VERSION
		return;
	
	string 		strShow = bIsPro? "1" : "0";
	
	vector<int> vnIDs;
	if( _get_Pro_GUI_ids(vnIDs))
	{
		///Jasmine 08/24/09 QA81-14165 DISPLAY_BUT_DISABLE_PRO_OPTION_IN_REG_VERSION
		//tree_set_attributes(trParam, vnIDs, strShow);
		/// Iris 2/02/2010 QA81-14165 ALSO_SHOW_PRO_IN_PRO_VERSION
		//if(!bIsPro)
		///End ALSO_SHOW_PRO_IN_PRO_VERSION
		{
			vector vTemp, vNoDup;
			vTemp = vnIDs;
			trim_duplicated_value(vTemp, vNoDup);
	
			for(int ii = 0; ii < vNoDup.GetSize(); ii++)
			{
				TreeNode trProOption = tree_get_node_by_id(trParam, vNoDup[ii], true);
				if(!trProOption)
				{
					//ASSERT(0);/// Iris 2/02/2010 QA81-14165 ALSO_SHOW_PRO_IN_PRO_VERSION, no need assert here
					continue;
				}				
				string strLabel;
				trProOption.GetAttribute(STR_LABEL_ATTRIB, strLabel);
				trProOption.SetAttribute(STR_LABEL_ATTRIB, strLabel + " " + STR_PRO_POSTFIX);				
				
				if(!bIsPro) /// Iris 2/02/2010 QA81-14165 ALSO_SHOW_PRO_IN_PRO_VERSION
				{
					if(ENABLE_READONLY_SCROLL != trProOption.Enable) 
						trProOption.Enable = ENABLE_READ_ONLY;
				}
				/// Iris 9/14/2009 QA80-14165-P4 NOT_SHOW_PRO_OPTION_WHEN_CHANGE_PARAM_ON_80SR6_OPJ
				// pro tree node is hidden when do change param on old opj, so need force it show.
				if( !trProOption.Show ) 
					trProOption.Show = 1;
				///end NOT_SHOW_PRO_OPTION_WHEN_CHANGE_PARAM_ON_80SR6_OPJ
			}
		}
		///End DISPLAY_BUT_DISABLE_PRO_OPTION_IN_REG_VERSION
		
		///// Iris 6/02/05 UPDATE_PRO_ID_SETTING 
		//tree_set_attributes(trParam, vnIDs, strShow, STR_ATTRIB_ISPRO);
		/////end UPDATE_PRO_ID_SETTING
	}
	
}

//------- end

static void _get_built_in_Pro_GUI_ids(vector<int>& vnIDs)
{
	vnIDs = s_vnProGUI_ids;
}

static bool _get_Pro_GUI_ids(vector<int>& vnIDs)
{
	string strProIDFile = "";// this will be later, a centralized place, just one file
	if(strProIDFile.IsFile())
		return _load_integer_vector_from_tree_file(strProIDFile, vnIDs);
	else
		_get_built_in_Pro_GUI_ids(vnIDs);
	return true;
}

///end HIDDEN_PRO_FEATURE_FOR_REGULAR_VERSION

///Iris 12/31/04 IMPROVE_ACCESS_ADVANCED_IDS_FUNC
/*
static void _generate_build_in_advanced_GUI_ids_theme_file(string strThemeFile) //template function here
{
	Tree tr;
	TreeNode trNodeID = tr.AddNode("IDs");	
	trNodeID.nVals = vnDescAdvanIDs;	
	tr.Save(strThemeFile);
}

static bool _get_built_in_advanced_GUI_ids(const string& strThemeFile, vector<int>& vnIDs)
{
	if( !strThemeFile.IsFile() )
		_generate_build_in_advanced_GUI_ids_theme_file(strThemeFile);	
	
	Tree	trAdvanIDs;
	if( !trAdvanIDs.Load(strThemeFile))
		return false;
	
	vnIDs = trAdvanIDs.FirstNode.nVals;	
	return true;
}	
*/

static bool _load_integer_vector_from_tree_file(string strThemeFile, vector<int>& vnIDs)
{
	Tree	trAdvanIDs;
	if( !trAdvanIDs.Load(strThemeFile))
		return false;
	
	vnIDs = trAdvanIDs.FirstNode.nVals;
	return true;
}

static void _get_built_in_advanced_GUI_ids(const TreeNode& trGUI, vector<int>& vnIDs)
{
	vnIDs = s_vnAdvancedGUI_ids;
	
	/// Iris 06/07/2007 v8/0634 STATS_ON_ROWS_GUI_INCORRECT_WHEN_DO_CHANGE_PARAM
	/*
	//If the tool is Statistics On Rows
	TreeNode trStatsOnRows = trGUI.StatsOnRows;
	if(trStatsOnRows && 1== trStatsOnRows.nVal)
	{
		///Cheney 2007-4-27 STATAS_ON_ROW_NOT_NEED_WDF_ETC	
		//vector<int> vnExceptIDs = {IDE_NUM_MISSING,IDE_VARIANCE,IDE_SKEWNESS,IDE_KURTOSIS,IDE_UNCORSUMSQ,IDE_CORSUMSQ,IDE_COV,
							//IDE_MAD,IDE_SD_X_2,IDE_SD_X_3,IDE_GEO_MEAN,IDE_GEO_SD,IDE_MODE,IDE_WEIGHTSUM,
							//IDE_IMIN,IDE_IMIN,IDE_P25,IDE_MEDIAN,IDE_P75,IDE_IMAX,IDE_IQR,IDE_RANGE,IDE_USE_CUSTOM_PERCENTILES,IDE_CUSTOM_PERCENTILES};
		//echo said, when stats on row, should bring back Q1, Q2, Q3
		vector<int> vnExceptIDs = {IDE_NUM_MISSING,IDE_VARIANCE,IDE_SKEWNESS,IDE_KURTOSIS,IDE_UNCORSUMSQ,IDE_CORSUMSQ,IDE_COV,
					IDE_MAD,IDE_SD_X_2,IDE_SD_X_3,IDE_GEO_MEAN,IDE_GEO_SD,IDE_MODE,IDE_WEIGHTSUM,
					IDE_IMIN,IDE_IMIN,IDE_IMAX,IDE_IQR,IDE_RANGE,IDE_USE_CUSTOM_PERCENTILES,IDE_CUSTOM_PERCENTILES};
		///end STATAS_ON_ROW_NOT_NEED_WDF_ETC
		vnIDs.Append(vnExceptIDs);
	}
	*/
	///end STATS_ON_ROWS_GUI_INCORRECT_WHEN_DO_CHANGE_PARAM
}

static bool _get_advanced_GUI_ids(const TreeNode& trGUI, vector<int>& vnIDs)
{
	string strAdvancedIDFile = "";// this will be later, a centralized place, just one file
	if(strAdvancedIDFile.IsFile())
		//return _load_advanced_GUI_ids_from_file(strAdvancedIDFile, vnIDs);
		return _load_integer_vector_from_tree_file(strAdvancedIDFile, vnIDs);
	else
		_get_built_in_advanced_GUI_ids(trGUI, vnIDs);
	return true;
}	
///Frank 4/5/05 GUI_ADVANCED_SETTING_FROM_FILE	
static bool _get_advanced_GUI_ids(const TreeNode& trGUI, vector<int>& vnIDs, string strThemeFileName )
{
	if(!_load_advanced_settings_from_tree_file( strThemeFileName, vnIDs ))
		_get_built_in_advanced_GUI_ids(trGUI, vnIDs);
	
	if(vnIDs.GetSize() <= 0 )
		return false;
	return true;
}	

static bool _load_advanced_settings_from_tree_file(string strThemeFile, vector<int>& vnIDs)
{
	if(strThemeFile.Find("|") >=0 )
	{
		vector<string> vs;
		strThemeFile.GetTokens(vs,'|');		
		for(int n= 0 ; n<vs.GetSize(); n++)
			vnIDs.Add(atoi(vs[n]));
		return true;
	}
	if(!strThemeFile.IsFile())
		return false;
	vector<string> vs;
	string strComment;
	if(!tree_read_values_with_ids(strThemeFile, vnIDs, vs, strComment))
		return false;
	return true;
}
///End GUI_ADVANCED_SETTING_FROM_FILE	

/*
static string _get_advanced_ids_theme_file(const TreeNode& trGUI)
{
	if( !trGUI)
		return "";
	
	string strThemeFile;
	trGUI.GetAttribute(STR_ADVANCED_IDS_THEME_FILE_ATTRIB, strThemeFile);		
	return strThemeFile;
}
///end IMPROVE_ACCESS_ADVAN_IDS_THEME_FILE
*/
///end IMPROVE_ACCESS_ADVANCED_IDS_FUNC

//////////////////////////////////////////////////////////////////////////////
//--- CPY 2/17/04 XF_RELATED_TO_PATH
// set nFolderLocation = -1 to search
int xf_find_file(string& strFilePath, LPCSTR lpcszFunctionName, LPCSTR lpcszCategoryName, int nFolderLocation )// = NULL, = -1
{
	bool bSearch = false;
	if(nFolderLocation < 0)
	{
		bSearch = true;
		nFolderLocation = ORIGIN_PATH_USER;
	}
	// string strFormatFDF = "%s%s%s";
	string strFolder = STR_XF_FILE_FOLDER;
	if(lpcszCategoryName && *lpcszCategoryName != '\0')
	{
		strFolder += "\\";
		strFolder += lpcszCategoryName;
	}
		
	// strFilePath.Format(strFormatFDF, get_origin_path(nFolderLocation, strFolder), lpcszFunctionName, STR_XF_FILE_EXT);
	bool bExists = okutil_make_file_path(strFilePath, nFolderLocation, strFolder, lpcszFunctionName, STR_XF_FILE_EXT);
	

	// if(bSearch && !strFilePath.IsFile())
	if(bSearch && !bExists)
	{
		nFolderLocation = ORIGIN_PATH_GROUP;
		// strFilePath.Format(strFormatFDF, get_origin_path(nFolderLocation, strFolder), lpcszFunctionName, STR_XF_FILE_EXT);
		// if( !strFilePath.IsFile() )
		bExists = okutil_make_file_path(strFilePath, nFolderLocation, strFolder, lpcszFunctionName, STR_XF_FILE_EXT);
		if( !bExists )
		{
			nFolderLocation = ORIGIN_PATH_SYSTEM;
			// strFilePath.Format(strFormatFDF, get_origin_path(nFolderLocation, strFolder), lpcszFunctionName, STR_XF_FILE_EXT);
			bExists = okutil_make_file_path(strFilePath, nFolderLocation, strFolder, lpcszFunctionName, STR_XF_FILE_EXT);
		}
	}
	// if(!strFilePath.IsFile())
	if(!bExists)
	{
		strFilePath.Empty();
		return -1;
	}
	return nFolderLocation;
}
//---- end CPY 2/17/04 XF_RELATED_TO_PATH

// given Tag = Val pairs
// pass through each token in lpcszArg and assign each token into val, but
// if token in the form of -name, then search from current index until vsTags has
// matching name and use that as new index
// return 0,1,2 for num of val modified
// error codes (<0)
// PARSE_TOO_MANY_ENTRIES
// PARSE_VARNAME_NOT_FOUND
// example, key=val
// a=1
// b=2
// c=col(3)
// x="testing" 
// y=col(5)
// z=3
// lpcszArg = 10 col(1) -x "some name" col(2)
// lpcszArg =  - x "some name" col(2)

///DSC QA70-7518 v8.0207 LT_XF_COMMAND
#define PARSE_KEYVAL_PAIR_MISSING_VAL	-5
#define PARSE_KEYVAL_PAIR_MISSING_KEY	-6
#define CHECK_PARSE_TOO_MANY_ENTRIES(_nIndex, _nNumVals) if(_nIndex>=_nNumVals) return PARSE_TOO_MANY_ENTRIES;
#define IS_CHAR_NUMBER(_ch)		('0' <= (_ch) && (_ch) <= '9')
//int parse_arg_str(LPCSTR lpcszArg, vector<string>& vsVals, vector<string>& vsKeys, bool bKeyPartialMatch, bool bKeyCaseSensitive, vector<string>& vsKeysSet)
int parse_arg_str(LPCSTR lpcszArg, StringArray* pvsVals, StringArray* pvsKeys, bool bKeyPartialMatch, bool bKeyCaseSensitive, StringArray* pvsKeysSet)
{
	/*
	int nNumKeys = vsKeys.GetSize();
	int nNumVals = vsVals.GetSize();
	ASSERT(nNumKeys == nNumVals);
	
	string strErr;
	
	int nNumValsSet;//return value
	
	char cDash = '-';
	
	string strArgStr = lpcszArg;
	vector<string> vsTokens;
	int nNumTokens = strArgStr.GetTokens(vsTokens);
	
	string strTok;
	int nValIndex=-1;
	
	//-- The first nLastNonswitchedArg arguments may appear without switches
	//-- After that, swithces are required, but order not important. Cannot specify with switch an argument already set with or without a switch.
	int nLastNonswitchedArg=-1;
	bool bHaveSwitch=false;
	for(int nTok=0; nTok<nNumTokens; nTok++)
	{	
		strTok = vsTokens[nTok];
		if( strTok[0]==cDash && !is_numeric(strTok) )// have a -Key Val pair, and not say -1	
		{
			bHaveSwitch=true;
			
			//-- check space
			int nLen = strTok.GetLength();
			if(nLen==1) // && !bSpaceAfterDash)
			{
				out_str(ERR_NEED_SPACE_AFTER_DASH);
				return PARSE_SPACE_AFTER_DASH;
			}
			
			//-- get key after dash
			string strKey = strTok.Mid(1);
			
			//-- find key in vsKeys, starting from nValIndex
			bool bFoundKey=false;
			
			int nKey;
			bFoundKey = okutil_is_in_list(strKey, &vsKeys, bKeyCaseSensitive, bKeyPartialMatch, &nKey); //false, false, NULL, 0

			if(bFoundKey && nKey<=nLastNonswitchedArg)
			{
				strErr.Format(ERR_VAL_ALREADY_SPECIFIED, strKey, vsVals[nKey]);
				out_str(strErr);
				return PARSE_BAD_ARG_ORDER;
			}
			
			if(!bFoundKey)
			{
				strErr.Format(ERR_NO_ARG_BY_THAT_NAME, strKey);
				return PARSE_VARNAME_NOT_FOUND;
			}
			
			//-- now get Val, which should be next token
			if(++nTok>=nNumTokens)
			{
				strErr.Format(ERR_KEY_IS_MISSING_VAL, strKey);
				out_str(strErr);//
				return PARSE_KEYVAL_PAIR_MISSING_VAL;
			}
			
			strTok = vsTokens[nTok];
			 
			//-- check and assign
			if(strTok[0]==cDash && !is_numeric(strTok))// have no value, and starting another -Key Val pair
			{
				strErr.Format(ERR_KEY_IS_MISSING_VAL, strKey);
				out_str(strErr);
				return PARSE_KEYVAL_PAIR_MISSING_VAL; // nTok--;
			}
			else
			{									
				CHECK_PARSE_TOO_MANY_ENTRIES(nKey, nNumVals);
				if(vsVals[nKey].GetLength()>0) //already set
				{
					strErr.Format(ERR_VAL_ALREADY_SPECIFIED, strKey, vsVals[nKey]);
					out_str(strErr);
					return PARSE_VARNAME_NOT_FOUND;
				}
							
				vsVals[nKey]=strTok;
				nNumValsSet++;
			}
			
		}
		else // then token is a value 
		{
			if(bHaveSwitch)
			{	
				strErr.Format(ERR_VAL_IS_MISSING_KEY, strTok);
				out_str(strErr);
				return PARSE_KEYVAL_PAIR_MISSING_KEY;
			}
			
			CHECK_PARSE_TOO_MANY_ENTRIES(++nValIndex, nNumVals);			
			vsVals[nValIndex]=strTok;
			nLastNonswitchedArg++;
			nNumValsSet++;
		}
	}		

	return nNumValsSet;
	*/
	// return okutil_parse_arg_str( lpcszArg,  &vsVals,  &vsKeys,  bKeyPartialMatch,  bKeyCaseSensitive);
	return okutil_parse_arg_str( lpcszArg,  pvsVals,  pvsKeys,  bKeyPartialMatch,  bKeyCaseSensitive, pvsKeysSet);
}
///end LT_XF_COMMAND


static DWORD _list_xf_dword_options_from_str_options(LPCSTR lpcszOptions, DWORD dwOptions)
{
	string strOptions = lpcszOptions;
	
	///DSC 6/16/05 ADD_SORT_XF_LIST_OPTIONS
	// if(strOptions.GetLength()==0 || strOptions.GetLength()>3)
	//	return 0x0;
	if(strOptions.GetLength()==0)
	{
		// set defaults
		dwOptions |= LIST_XF_ARG_NAME;
		dwOptions |= LIST_XF_SORT_NAME;
		return dwOptions;
	}
	///end ADD_SORT_XF_LIST_OPTIONS
	
	///DSC 6/16/05 ADD_SORT_XF_LIST_OPTIONS
	//if(strOptions.Find('n')>=0)
	//	dwOptions |= LIST_XF_ARG_NAME;
	if(strOptions.Find('a')>=0)
		dwOptions |= LIST_XF_ARG_NAME;
	///end ADD_SORT_XF_LIST_OPTIONS
	
	if(strOptions.Find('t')>=0 && strOptions.Find('a')>=0)
		dwOptions |= LIST_XF_ARG_TYPES;
	if(strOptions.Find('d')>=0 && strOptions.Find('a')>=0)
		dwOptions |= LIST_XF_ARG_DEFAULTS;
	
	if(strOptions.Find('l')>=0)
		dwOptions |= LIST_XF_LOCATION;
		
	if(strOptions.Find('c')>=0)
		dwOptions |= LIST_XF_CATEGORY;
	
	///DSC 6/16/05 ADD_SORT_XF_LIST_OPTIONS
	//if(strOptions.Find('s')>=0) // 's'ort
		dwOptions |= LIST_XF_SORT_DISPLAY;
	
	
	if(strOptions.Find('n')>=0)
		dwOptions |= LIST_XF_SORT_NAME;// sort by file 'n'ame
	///end ADD_SORT_XF_LIST_OPTIONS
	
	if(strOptions.Find('r')>=0)
		dwOptions |= LIST_XF_REMOVE_DUPLICATES;// remove duplicates, show only first in list.
	
	if(strOptions.Find('v')>=0)
		dwOptions |= LIST_XF_ARG_MORE_INFO;// more info about arguments
	
	if(strOptions.Find('u')>=0)
		dwOptions |= LIST_XF_REFRESH_LIST;// update map
	
	if(strOptions.Find('h')>=0)
		dwOptions |= LIST_XF_SHOW_HELP;// show info in help node
	
	if(strOptions.Find('b')>=0)
		dwOptions |= LIST_XF_SHOW_BRIEF_INFO;// show brief info after xf name
	
	return dwOptions;
	
}
//-- CPY 4/15/05 HELP_XF_LISTING
static int _show_list_x_function_options(LPCSTR lpcszOptions)
{
	printf("usage:\nlist xf [name [options]]\n");
	printf("name can have wildcard * and ?\n");
	printf("options are combinations of the following letters\n");
	//printf("n = argument names\nt = argument types\nd = default values\nl = X-Function location\nc = X-Function category\n");
	printf("a = show argument names\nt = show argument types\nd = show default argument values\nl = show X-Function location\nc = show X-Function category\nn = sort by X-Function name\ns = sort by location, category, X-Function name\nr = remove duplicate same named X-Functions, with User folder having precedence\nv = show variables information\nu = update x-function list\n");
	printf("\nexample:\n\nlist xf my* lctad\n");
	printf("\nYou can also see each X-Function by \n\nmy_xf=\n\n");
	return 0;
}
//---

static string _format_all_arg_str(const vector<string>& vsVarNames, const vector<string>& vsVarDefaultVals, const vector<string>& vsVarTypes, DWORD dwDisplayOptions)
{
	string strArgs="\t";
	for(int jj = 0; jj < vsVarNames.GetSize(); jj++)
	{   
		if(jj > 0) 
			strArgs +=" ";
		if(LIST_XF_ARG_TYPES & dwDisplayOptions)
		{
			string strSep;
			if(LIST_XF_ARG_NAME & dwDisplayOptions)
				strSep = ":";
			strArgs += vsVarTypes[jj] + strSep;	
		}
		if(LIST_XF_ARG_NAME & dwDisplayOptions)	
			strArgs += vsVarNames[jj];
		if(LIST_XF_ARG_DEFAULTS & dwDisplayOptions)
		{	
			if(!vsVarDefaultVals[jj].IsEmpty())
				strArgs += "="+vsVarDefaultVals[jj];
		}
	}
	
	return strArgs;
}
#define LIST_XF_RETURN "return"
#define LIST_XF_DESCRIPTION "description"
/*
#define LIST_XF_IN "in"
#define LIST_XF_OUT "out"
#define LIST_XF_TYPE "type"
#define LIST_XF_DEFAULT "default"

static string _make_xf_variable_info_str(TreeNode trV, const string& vsVarDefaultVals)
{
	//string str = trV.name.strVal;
	//str += " = [";
	string str= "[";
	//int nIO = nIO;
	if(IO_INPUT==trV.io.nVal)
		str += LIST_XF_IN + "] ";
	else if(IO_OUTPUT==trV.io.nVal)
		str += LIST_XF_OUT + "] ";
	else if(IO_INOUTPUT==trV.io.nVal)
		str += LIST_XF_IN + "/" + LIST_XF_OUT + "] ";
	
	str += LIST_XF_TYPE + "=" + trV.type.strVal;

	str += ", " + LIST_XF_DEFAULT + "=" + vsVarDefaultVals;
	
	string strDescription = tree_find_node_by_dataID(trV, IDV_VAR_HELP_DESCRIPTION, IDV_VAR_LOCAL_HELP_BRANCH);//trV.description.strVal;
	if(!strDescription.IsEmpty())
		str += ", " + LIST_XF_DESCRIPTION + "=" + strDescription;
	
	return str;
}
*/
///DSC 2/14/06 QA70-7518 ALLOW_SEARCH_XF_BY_KEYWORDS
// bool _list_xf(LPCSTR lpcszXFname, LPCSTR lpcszXFfile, LPCSTR lpcszCompositeName, DWORD dwDisplayOptions)
/// DSC 2/27/06 ADD_OUTPUT_OPTION_TO_LIST_XF
// bool _list_xf(LPCSTR lpcszXFname, LPCSTR lpcszXFfile, LPCSTR lpcszCompositeName, DWORD dwDisplayOptions, LPCSTR lpcszKeywords)
bool _list_xf(LPCSTR lpcszXFname, LPCSTR lpcszXFfile, LPCSTR lpcszCompositeName, DWORD dwDisplayOptions, LPCSTR lpcszKeywords=NULL, String* p_strOutput=NULL) //
/// end ADD_OUTPUT_OPTION_TO_LIST_XF
///end ALLOW_SEARCH_XF_BY_KEYWORDS
{
	XFunction xf;
	TreeNode trXF;
	//------ CPY 5/21/07 LX_CRASHING_WHEN_LISTING_SELF
	//if(!xf.Load(&trXF, lpcszXFfile, LTXF_BY_FILENAME))
	///------ Folger 02/02/10 QUICKFIT_SUPPORT_WITHOUT_ROI_BOX_WITH_CNTRL_KEY_DOWN
	//if(!xf.Load(&trXF, lpcszXFfile, LTXF_BY_FILENAME | LTXF_LIST))
	if(!xf.Load(&trXF, lpcszXFfile, LTXF_BY_FILENAME))
	///------ End QUICKFIT_SUPPORT_WITHOUT_ROI_BOX_WITH_CNTRL_KEY_DOWN
	//------
		return false;			
	string strXFname = lpcszXFname;
	string strArgs;
	
	//out_tree(trXF);
	//tree_dump(trXF, "************");

	if(dwDisplayOptions & LIST_XF_ARG_MASK)
	{	
		vector<string> vsVarNames, vsVarDefaultVals, vsVarTypes;
		xf.GetVariables(vsVarNames, vsVarDefaultVals, vsVarTypes);
		strArgs = _format_all_arg_str(vsVarNames, vsVarDefaultVals, vsVarTypes, dwDisplayOptions);
	}
	strXFname.MakeValidCName();	//-- strip space etc

	string strNameToShow;
	DWORD dwCtrl= FF_REMOVE_PATH | FF_REMOVE_CATEGORY;
	string strCategory;	
	int nPath = ORIGIN_PATH_UNDEF;
	if((dwDisplayOptions & LIST_XF_PATH_MASK) && lpcszCompositeName)
	{	
		okutil_separate_composite_name(lpcszCompositeName, &nPath, &strCategory);				
		
		if(dwDisplayOptions & LIST_XF_LOCATION)
			dwCtrl &= ~FF_REMOVE_PATH;
		if((dwDisplayOptions & LIST_XF_CATEGORY) && !strCategory.IsEmpty())
			dwCtrl &= ~FF_REMOVE_CATEGORY;
	}
	
	///DSC 6/15/05 FIX_DISPLAY_NAME_ORDER
	///DSC 7/13/05 QA70-7518 MORE_LISTXF_OUTPUT_FORMATTING
	// strNameToShow += strXFname;	
	strNameToShow = okutil_composite_name_from_components(nPath, strCategory, strXFname, dwCtrl);
	///end MORE_LISTXF_OUTPUT_FORMATTING
	///end FIX_DISPLAY_NAME_ORDER
	
	//if(LIST_XF_ARG_MORE_INFO & dwDisplayOptions || LIST_XF_SHOW_HELP & dwDisplayOptions) 
	//{
	//	printf("---------------\n");
	//}
				
	///DSC 2/14/06 QA70-7518 ALLOW_SEARCH_XF_BY_KEYWORDS
	string strDisplayText;
	okutil_separate_composite_name(lpcszCompositeName, &nPath, &strCategory);
	// search category string also			
	string strSearchText = strXFname + "\t" + strCategory + ": ";
	strSearchText += xf.GetKeywordSearchText(&strDisplayText);
	string strKeywords = lpcszKeywords;
	if(lpcszKeywords && strKeywords.Compare("*")!=0)// filter based on keywords
	{
		// for now assume one keyword
		strKeywords.MakeLower();
		if(!is_str_match_begin_of_word(strKeywords , strSearchText))
			return false;
	}
	
	
	if(LIST_XF_SHOW_BRIEF_INFO & dwDisplayOptions)
		strDisplayText = strNameToShow + "\t" + strCategory + ": " + strDisplayText;
	else
		strDisplayText = strNameToShow;
	
	/// DSC 2/27/06 ADD_OUTPUT_OPTION_TO_LIST_XF
	// printf("%s\n", strDisplayText);
	if(p_strOutput)
		*p_strOutput = strDisplayText;
	else
		printf("%s\n", strDisplayText);
	/// end ADD_OUTPUT_OPTION_TO_LIST_XF
	
	// if(lpcszKeywords && strKeywords.Compare("*")!=0)
	//	return true;
	///end ALLOW_SEARCH_XF_BY_KEYWORDS

			
	
	
	
	
	///DSC 7/28/05 SUPPORT_HELP_XF_IF_NOT_LT
	// if(LIST_XF_ARG_MORE_INFO & dwDisplayOptions) // show variable list
	if( (LIST_XF_ARG_MORE_INFO & dwDisplayOptions) && 1==trXF.UsageCtxt.LabTalk.nVal) // show variable list	
	///end SUPPORT_HELP_XF_IF_NOT_LT		
	{
		/*
		Tree trVars;
		trVars = trXF.vars;
		string str, str1, strReturnType;
		
		
		vector<string> vsVarNames, vsVarDefaultVals, vsVarTypes;	
		xf.GetVariables(vsVarNames, vsVarDefaultVals, vsVarTypes);
		
		int ii=0;
		foreach(TreeNode trV in trVars.Children)
		{	
			str = _make_xf_variable_info_str(trV, vsVarDefaultVals[ii++]);
			string strName = trV.name.strVal;
			//trV.SetAttribute(STR_LABEL_ATTRIB, str);
			
			trV.name.Remove();
			trV.context.Remove();
			trV.io.Remove();
			trV.type.Remove();
			trV.Data.Remove();
			trV.valdata.Remove();
			//trV.description.Remove();
			trV.help.Remove();
			
			string strCombo = trV.comboString.strVal;
			
			if(strCombo[0]=='|')// starts with | means editable, so dont show list
				strCombo="";
			
			trV.comboString.Remove();
			if(!strCombo.IsEmpty())
			// need to convert into a branch with expanded listing
			{
				string strAttr = strName;
				strAttr+= " = ";
				strAttr+=str;
				trV.SetAttribute(STR_LABEL_ATTRIB, strAttr);
				
				vector<string> vs;
				
				if(strCombo.CompareNoCase("0|1")==0)
					strCombo = "false|true";
				if(strCombo.CompareNoCase("1|0")==0)
					strCombo = "true|false";
				
				strCombo.GetTokens(vs, '|');
				for(int ii=0; ii<vs.GetSize(); ii++)
				{
					str1 = (string)"n" + (1+ii);
					TreeNode trtemp = tree_check_get_node(trV, str1);
					trtemp.strVal = vs[ii];
					str1 = ii;
					trtemp.SetAttribute(STR_LABEL_ATTRIB, str1);
				}
			}
			else
			{
				trV.SetAttribute(STR_LABEL_ATTRIB, strName);
				//... trV.strVal = str;
			}	
		}
		*/
		
		
		string strVarsInfo = xf.GetVarsHelpInfo();
		
		
		//trVars.SetAttribute(STR_LABEL_ATTRIB, strNameToShow);
		string strReturnType;
		xf.GetReturnType(strReturnType);	///DSC 7/19/05 MAKE_GET_RETURN_TYPE_PUBLIC
		string strDescription = trXF.xFdescription.strVal;
		string strTitle = strNameToShow;
		
		if(!strReturnType.IsEmpty() && strReturnType.CompareNoCase("void")!=0)
		 	strTitle += " " + LIST_XF_RETURN + "=" + strReturnType;
				
		if(!strDescription.IsEmpty())
		 	strTitle += " " + LIST_XF_DESCRIPTION + "=" + strDescription;
		
		
		/*		
		trVars.SetAttribute(STR_LABEL_ATTRIB, strTitle);
		out_tree(trVars);
		printf("\n");
		*/
		
		///DSC 2/14/06 QA70-7518 ALLOW_SEARCH_XF_BY_KEYWORDS
		// printf("%s\n", strTitle);
		///end ALLOW_SEARCH_XF_BY_KEYWORDS
		
		/// DSC 2/27/06 ADD_OUTPUT_OPTION_TO_LIST_XF
		// printf("%s\n", strVarsInfo);
		if(p_strOutput)
			*p_strOutput += "\n" + strVarsInfo;
		else
			printf("%s\n", strVarsInfo);
		/// end ADD_OUTPUT_OPTION_TO_LIST_XF

	}
	///DSC 2/14/06 QA70-7518 ALLOW_SEARCH_XF_BY_KEYWORDS
	// else
	// 	printf("%s %s\n", strNameToShow, strArgs);
	///end ALLOW_SEARCH_XF_BY_KEYWORDS
	
	if(LIST_XF_SHOW_HELP & dwDisplayOptions)
	{
		string strHelp, strExample, strRef, strSeeAlso;
		
		/*
		///DSC 8/5/05 QA70-7518 ADD_HELP_J_G_TO_XF
		double		resLang = 0;
		LT_get_var("@RL", &resLang);
		int			nresLang = nint(resLang);
		
		
		//if (ORESLANG_JAPANESE == nresLang)
		//	strHelp = trXF.helpj.strVal;
		//else if (ORESLANG_GERMAN == nresLang)
		//	strHelp = trXF.helpg.strVal;
		
		//if(strHelp.IsEmpty())
		//	strHelp = trXF.help.strVal;
			
		if (ORESLANG_JAPANESE == nresLang)
			strHelp = trXF.help.summaryj.strVal;
		else if (ORESLANG_GERMAN == nresLang)
			strHelp = trXF.help.summaryg.strVal;
		
		if(strHelp.IsEmpty())
			strHelp = trXF.help.summary.strVal;		
		*/
		
		/*
		strExample = trXF.help.example.strVal;
		strRef = trXF.help.ref.strVal;
		strSeeAlso = trXF.help.seeAlso.strVal;
		
		if(!strHelp.IsEmpty())
		{
			printf("%s%s:\n", LIST_XF_HELP_INFO, strXFname);
			printf("---------------\n");
			printf("%s\n", strHelp);
			printf("%s\n", strExample);
			printf("%s\n", strRef);
			printf("%s\n", strSeeAlso);
			printf("---------------\n\n\n");
			//printf("\n\n\n");
		}
		*/
		///end ADD_HELP_J_G_TO_XF		
		
		
		
		strHelp = xf.GetHelpInfo(true, 0);
		/// DSC 2/27/06 ADD_OUTPUT_OPTION_TO_LIST_XF
		// printf("%s\n", strHelp);
		if(p_strOutput)
			*p_strOutput += "\n" + strHelp;
		else
			printf("%s\n", strHelp);
		/// end ADD_OUTPUT_OPTION_TO_LIST_XF

		
	}

	
	return true;
}
/// DSC 2/27/06 ADD_OUTPUT_OPTION_TO_LIST_XF
// /// DSC 2/14/06 QA70-7518 ALLOW_SEARCH_XF_BY_KEYWORDS
// // int list_x_functions(LPCSTR lpcszName, LPCSTR lpcszOptions, LPCSTR lpszCategory, int nLocation, DWORD dwCtrl)//NULL, NULL, ORIGIN_PATH_UNDEF, 0x0 
// int list_x_functions(LPCSTR lpcszName, LPCSTR lpcszOptions, LPCSTR lpszCategory, int nLocation, DWORD dwCtrl, LPCSTR lpcszKeywords)//NULL, NULL, ORIGIN_PATH_UNDEF, 0x0, NULL
int list_x_functions(LPCSTR lpcszName, LPCSTR lpcszOptions, LPCSTR lpszCategory, int nLocation, DWORD dwCtrl, LPCSTR lpcszKeywords, StringArray* p_saOutput)//NULL, NULL, ORIGIN_PATH_UNDEF, 0x0, NULL, NULL 
// /// end ADD_OUTPUT_OPTION_TO_LIST_XF
/// end ALLOW_SEARCH_XF_BY_KEYWORDS
{
	//---- CPY 9/15/05 SHOW_XF_HELP_IN_TX
	if(lstrcmp(lpcszOptions,"?") == 0)
	{
		FUNC_STR pfn = Project.FindFunction("XFShowHelp", NULL, TRUE);
		if(pfn)
			pfn(lpcszName);
		
		return 0;
	}
	//----
	
	string strName = lpcszName;
	
	///DSC 8/2/05 use Help xfname instead
	// //-- CPY 4/15/05 HELP_XF_LISTING
	// int nLen = lstrlen(lpcszName);
	// if(nLen >= 1 && *lpcszName == '?' || (*lpcszName == '-' && (lpcszName[1] == '?' || lpcszName[1] == 'h' || lpcszName[1] == 'H')))
	// 	return _show_list_x_function_options(lpcszOptions);

	
	DWORD dwDisplayOptions =_list_xf_dword_options_from_str_options(lpcszOptions, 0);
	
	// if(0==dwDisplayOptions)
	// 	dwDisplayOptions = LIST_XF_ARG_NAME;
	
	///DSC 2/9/06 QA70-7835 ORIGIN_PATH_PROJECT_INSERTED lx converts nLocation from combo list of locations to enum ORIGIN_PATH_TYPE
	// // "All" is index 3 in combo string of variable "location" in tree view of xfunction "listxf".
	// // User Folder|Group Folder|System Path|All
	// // Need to convert All to to internal enum value ORIGIN_PATH_UNDEF
	// if(3==nLocation)
	// 	nLocation=ORIGIN_PATH_UNDEF;
	///end 
		
	vector<string> vsFiles, vsNames;
	//-- get all x-functions
	///DSC 7/28/05 SUPPORT_HELP_XF_IF_NOT_LT
	// 	int nXFUsage = IDXF_USGCTXT_LABTALK; //CPY 4/5/05 XF_LT_CHECK_USAGE_CONTEXT, added nXFUsage
	DWORD nXFUsage; // init to all usage contexts
	DWORD* pdwUsgCtxts=NULL;
	int nNumUsgCtxts = 1; ///DSC 12/15/06 LIST_XF_IF_NOT_GRAPHIC_OBJECT_EVENT

	if( !(LXF_NO_LT_CHECK & dwCtrl) )
	{	
		nXFUsage = IDXF_USGCTXT_LABTALK; //CPY 4/5/05 XF_LT_CHECK_USAGE_CONTEXT, added nXFUsage
		//nXFUsage = -1*IDXF_USGCTXT_GO;// = NOT graphic object events ///DSC 12/15/06 LIST_XF_IF_NOT_GRAPHIC_OBJECT_EVENT Max requests to allow "fitNL -d", "xfname -d" in script window, even though these are not IDXF_USGCTXT_LABTALK. CP says list all except xf's with graphic object events checked

		pdwUsgCtxts = &nXFUsage;
	}
	///end SUPPORT_HELP_XF_IF_NOT_LT
	
	
	
	if(LIST_XF_REFRESH_LIST & dwDisplayOptions)
	{
		NOTIFY_XF_FILE_CHANGE
	}
	
	
	///DSC 6/16/05 ADD_SORT_XF_LIST_OPTIONS why FF_REMOVE_DUPLICATE??
	// int nNumFiles = okutil_find_files(&vsNames, &vsFiles, SUPPORTFILE_XF, FF_REMOVE_DUPLICATE, &nXFUsage, 1);
	int nNumFiles;
	//if(LIST_XF_SHOW_DUPLICATES & dwDisplayOptions)
	///DSC 7/28/05 SUPPORT_HELP_XF_IF_NOT_LT
	// if(LIST_XF_REMOVE_DUPLICATES & dwDisplayOptions)
	// 	nNumFiles = okutil_find_files(&vsNames, &vsFiles, SUPPORTFILE_XF, FF_REMOVE_DUPLICATE, &nXFUsage, 1);
	// else
	// 	nNumFiles = okutil_find_files(&vsNames, &vsFiles, SUPPORTFILE_XF, 0, &nXFUsage, 1);
	if(LIST_XF_REMOVE_DUPLICATES & dwDisplayOptions)
		///DSC 10/11/2005 QA70-8167 FIND_FILES_FROM_SPECIFIED_LOCATION
		///nNumFiles = okutil_find_files(&vsNames, &vsFiles, SUPPORTFILE_XF, FF_REMOVE_DUPLICATE, pdwUsgCtxts, 1);
		nNumFiles = okutil_find_files_from_map(&vsNames, &vsFiles, SUPPORTFILE_XF, 0x0, pdwUsgCtxts, nNumUsgCtxts); // , NULL, NULL, ORIGIN_PATH_PREFER_USER
		///end FIND_FILES_FROM_SPECIFIED_LOCATION
	else
		nNumFiles = okutil_find_files_from_map(&vsNames, &vsFiles, SUPPORTFILE_XF, 0x0, pdwUsgCtxts, nNumUsgCtxts, NULL, NULL, ORIGIN_PATH_UNDEF);
	///end SUPPORT_HELP_XF_IF_NOT_LT
	///end ADD_SORT_XF_LIST_OPTIONS
	
	/// DSC 3/21/06 SORT_INSIDE_FIND_FILES
	// ///DSC 6/16/05 ADD_SORT_XF_LIST_OPTIONS
	// if(LIST_XF_SORT_MASK & dwDisplayOptions)
	// 	sort_composite_name_vs(vsNames, dwDisplayOptions);
	// ///end ADD_SORT_XF_LIST_OPTIONS
	/// SORT_INSIDE_FIND_FILES
	
	string strCategory;
	if(lpszCategory)
		strCategory = lpszCategory;
	
	/// special case lc 
	if( (LXF_FROM_XF_LC & dwCtrl)  &&  strName.CompareNoCase("")==0 && strCategory.CompareNoCase("")==0 ) // lc list categories	
	{	
		vector<string> vsCats;
		for(int ii=0; ii<nNumFiles; ii++)
		{
			int nPathType = ORIGIN_PATH_UNDEF;
			string strCat;
			string strMatchingName = okutil_separate_composite_name(vsNames[ii], &nPathType, &strCat);
			/// Hong 05/23/07/ QA80-9815 v8.0624 FIX_LOCATION_NOT_WORK_FOR_LIST_CATEGORY
			if(ORIGIN_PATH_UNDEF!=nLocation && nPathType!=nLocation)
				continue;
			/// end FIX_LOCATION_NOT_WORK_FOR_LIST_CATEGORY
			if(-1==vsCats.Find(strCat))// not found
				vsCats.Add(strCat);	
		}
		vsCats.Sort();
		for(ii=0; ii<vsCats.GetSize(); ii++)
		{
			/// DSC 2/27/06 ADD_OUTPUT_OPTION_TO_LIST_XF
			// printf("%s\n", vsCats[ii]);
			if(p_saOutput)
				p_saOutput->Add(vsCats[ii]);
			else
				printf("%s\n", vsCats[ii]);
			/// end ADD_OUTPUT_OPTION_TO_LIST_XF
		}
		
		return 0;
	}

	
	string strMatchingName;
	
	//-- find those that match lpcszName
	vector<string> vsCats;
	for(int ii=0; ii<nNumFiles; ii++)
	{
		// if( vsNames[ii].Match(lpcszName) )
		///DSC 6/16/05 ADD_SORT_XF_LIST_OPTIONS
		// strMatchingName = GetFileName(vsFiles[ii], true);
		// if( strMatchingName.Match(lpcszName) )
		// 	_list_xf(strMatchingName, vsFiles[ii], vsNames[ii], dwDisplayOptions);
		
		
		///DSC 6/29/05 QA70-7518 LISTXF_OXF
		// strMatchingName = okutil_separate_composite_name(vsNames[ii], NULL, NULL);
		int nPathType = ORIGIN_PATH_UNDEF;
		string strCat;
		string strMatchingName = okutil_separate_composite_name(vsNames[ii], &nPathType, &strCat);
		
		
		//string strCategory;
		//if(lpszCategory)
		//	strCategory = lpszCategory;
		if(!strCategory.IsEmpty() && !strCat.Match(strCategory))
			continue;
		
		
		if(ORIGIN_PATH_UNDEF!=nLocation && nPathType!=nLocation)
			continue;
		///end QA70-7518 LISTXF_OXF
		
		
		strMatchingName.MakeValidCName();	//-- strip space etc
		if( strMatchingName.Match(lpcszName) )
		{	
			string strFullpath;
			okutil_find_file_from_composite_name(&strFullpath, vsNames[ii], SUPPORTFILE_XF);
			
			/// DSC 2/14/06 QA70-7518 ALLOW_SEARCH_XF_BY_KEYWORDS
			// _list_xf(strMatchingName, strFullpath, vsNames[ii], dwDisplayOptions, lpcszKeywords);
			/// DSC 2/27/06 ADD_OUTPUT_OPTION_TO_LIST_XF
			string strOutput, *pstrOutput;
			if(p_saOutput)
				pstrOutput=&strOutput;
			else
				pstrOutput=NULL;
			_list_xf(strMatchingName, strFullpath, vsNames[ii], dwDisplayOptions, lpcszKeywords, pstrOutput );
			if(p_saOutput)
				p_saOutput->Add(strOutput);
			/// end ADD_OUTPUT_OPTION_TO_LIST_XF
			/// end ALLOW_SEARCH_XF_BY_KEYWORDS
		}
		///end ADD_SORT_XF_LIST_OPTIONS
		
	}
	
	return 0;
}

///DSC 8/18/05 AUTOCOMPLETE_XF_CMD_LINE_ARGUMENTS
//int get_remaining_xf_arg_list(StringArray* p_saRemainingArgList, XFunction& xf, LPCSTR lpcszCmdLineSoFar)
//int get_remaining_xf_arg_list(StringArray* p_saRemainingArgList, LPCSTR lpcszXFName, LPCSTR lpcszCmdLineSoFar)

//pass in cmd line and get remaining unassigned arguments...

/// YuI 10/10/05 QA70-8158 GENERAL_X_STRING_CONVENTION_TO_ACCESS_OBJECTS
//	#define XF_VARIABLE_ASSIGNMENT_STR ":="
/// end GENERAL_X_STRING_CONVENTION_TO_ACCESS_OBJECTS
int get_remaining_xf_arg_list(StringArray* p_saRemainingArgList, LPCSTR lpcszCmdLineSoFar)
{
	// get xf object
	// find xf
	string strCmdSoFar = lpcszCmdLineSoFar;
	strCmdSoFar.TrimLeft();
	// vector<string> vsTokens;
	// int nNumToks = strCmdSoFar.GetTokens( vsTokens );
	// if(nNumToks==0)
	//	return -1;// no xf in cmd
	// string strXFName = vsTokens[0];
	
	// get xf
	int nSpace = strCmdSoFar.Find(" ");
	string strXFName = strCmdSoFar.Left(nSpace);
	if(strXFName.IsEmpty())
		return -1;
	
	// get arguments
	strCmdSoFar = strCmdSoFar.Mid(strXFName.GetLength()+1);//skip the xf name
	strCmdSoFar.TrimLeft();
	
	
	string strFullpath;
	okutil_find_file_from_composite_name(&strFullpath, strXFName, SUPPORTFILE_XF);
	
	XFunction xf;
	TreeNode trXF;
	if(!xf.Load(&trXF, strFullpath, LTXF_BY_FILENAME))
		return -1;
	
	// it would be faster to compute xf name and XFunction object outside function...
	// then pass XFunction object reference into this function
	
	
	// get xf variables
	vector<string> vsVarNames, vsVarDefaultVals, vsVarTypes, vsVarNamesSet;
	xf.GetVariables(vsVarNames, vsVarDefaultVals, vsVarTypes);
	
	
	// now parse
	vector<string> vsVarParsedValues;
	int nNumVars = vsVarNames.GetSize();
	vsVarParsedValues.SetSize(nNumVars);
	vsVarNamesSet.SetSize(nNumVars);
	//int nNum = parse_arg_str(lpcszCmdLineSoFar, &vsVarParsedValues, &vsVarNames, true, false, &vsVarNamesSet);
	int nNum = parse_arg_str(strCmdSoFar, &vsVarParsedValues, &vsVarNames, true, false, &vsVarNamesSet);
	//int nNum = parse_arg_str(lpcszCmdLineSoFar, &vsVarParsedValues, &vsVarNames, true, false);
	
	int nNumRemaining=0;
	if(nNum>=0)//no errors, including missing values
	{	
		for(int ii=0; ii < vsVarParsedValues.GetSize(); ii++)
		{
			// cant use vsVarParsedValues.IsEmpty() if value can be empty string ""
			// if(vsVarParsedValues.IsEmpty())
			if(vsVarNamesSet[ii].IsEmpty())//then not set			
			{
				if(vsVarDefaultVals[ii].IsEmpty())
					vsVarDefaultVals[ii]="\"\"";// empty double quotes "" so user can see empty string.
				
				string str = vsVarNames[ii] + XF_VARIABLE_ASSIGNMENT_STR + vsVarDefaultVals[ii];
							
				p_saRemainingArgList->Add(str);
				nNumRemaining++;
			}			
		}
	}
	return nNumRemaining;
}	
///end AUTOCOMPLETE_XF_CMD_LINE_ARGUMENTS

string get_reg_key_name(int nEventID)
{
	switch(nEventID)
	{
	case IDXF_USGCTXT_WKS_SEL_CHANGE:
		return "OnWksSelectionChange";
	case IDXF_USGCTXT_WKS_SEL_TRACKING:
		return "OnWksSelectionTracking";
	}
	error_report("get_reg_key_name found invalid event ID");
	
	return "NotDefined";
}

//--- CPY ML 3/24/2005 CONTEXT_MENU_X_FUNCS
// given composite-name/file-name pair, create new composite name list by filtering Usage-Context check for given XF tree node ID
void xf_filter_names(const vector<string>& vsNames, const vector<string>& vsFiles, int nDataID,  vector<string>& vsResultNames, vector<uint>& vnOldIndices)
{
	Tree		trXF;
	TreeNode	trNode;
	if(vnOldIndices)
		vnOldIndices.SetSize(0);
	for(int ii = 0; ii < vsNames.GetSize(); ii++)
	{
		if (!trXF.Load(vsFiles[ii]))
			continue;
	
		trNode = tree_get_node_by_dataid(trXF, nDataID, true);
		if (!trNode.IsValid() || !trNode.nVal)
			continue;
		if(vnOldIndices)
			vnOldIndices.Add(ii);
		vsResultNames.Add(vsNames[ii]);
	}
}
//----

///end QA70-7500 EVENT_ORGANIZER_DLG

///end LIST_XF

string op_get_graph_tag_name(int nIndex)
{
	string str = "Graph";
	if(nIndex < 0)
		return str; ////-------- CPY 12/31/04 PICTURE_IN_REPORT_IN_BASE_CLASS, use -1 to just get the tagName prefix
	
	str += ++nIndex;
	return str;
}

TreeNode op_get_optional_tables(TreeNode& trGUI)
{
	/// Iris 10/29/2009 QA81-14546 OP_DLG_NEW_STRUCTURE
	//return trGUI.Output.Create;
	return OP_GUI_OUTPUT_TABLES_BRANCH(trGUI);
	///end OP_DLG_NEW_STRUCTURE
}
///Joe 8/28/06  MOVE_GET_X_FROM_Y_OR_GET_Y_FROM_X_TO_CALIBRATION
TreeNode op_get_calibration_table(TreeNode& trGUI)
{
	///Joseph 09/18/06  MOVE_CALIBRATION_TABLE_OUT_OUTPUT
	//return trGUI.Output.Calibration;
	/// Iris 11/03/2009 QA81-14546 OP_DLG_NEW_STRUCTURE
	//return trGUI.Calibration;
	return OP_GUI_CALIBRATION_BRANCH(trGUI);
	///end OP_DLG_NEW_STRUCTURE
	///End MOVE_CALIBRATION_TABLE_OUT_OUTPUT
}
///End MOVE_GET_X_FROM_Y_OR_GET_Y_FROM_X_TO_CALIBRATION
//---- CPY 5/4/2006 MOVE_XF_ERR_TO_THROW_MACRO
// I also changed all codes in this file that uses this function to error_report
/*
//Error output in XF
void xf_error_out(string strErr)
{
	out_str("");
	strErr.Write(WRITE_SCRIPT_WINDOW);
	out_str("");
	strErr.Write( WRITE_MESSAGE_BOX);
	

}
*/

//check if all elements of vector are equal
bool vec_is_equal(const vector& vIn)
{
	double dMin, dMax;
	vIn.GetMinMax(dMin, dMax);
	return (dMin == dMax) ? true : false;	
}		


//count missing values in vector
int vec_trim_missing(vector& vIn)		
{
	int nSize = vIn.GetSize();
	vIn.Trim();
	return nSize - vIn.GetSize();
}


int check_same_worksheet(Worksheet& wks1, Worksheet& wks2)
{
	string strwks1, strwks2;
	wks1.GetPage().GetName(strwks1); 
	wks2.GetPage().GetName(strwks2); 
	
	if ( strwks1 != strwks2 )
		return DIFF_WKB;
	else if ( wks1.GetIndex() != wks2.GetIndex() )
		return DIFF_WKS;
	else
		return SAME_WKS;
		
}

/// Iris 11/21/06 CORRECT_SPAN_AXIS_AS_FITX_WHEN_GET_INPUT_FROM_WKS
static bool _get_span_full_axis_min_max_value(double& dMin, double& dMax, int nPoints = 50)
{
	if(dMin > dMax)
		return false;
	
	double 	dMargin = (dMax - dMin) * 0.08; //For 0.08 here, see #8904 for more details
	dMin -= dMargin;
	dMax += dMargin;
	
	double	dInc;	
	RoundLimits(&dMin, &dMax, &dInc, nPoints);
	
	return true;
}
//end CORRECT_SPAN_AXIS_AS_FITX_WHEN_GET_INPUT_FROM_WKS

//---- CPY 10/29/2007 QA70-10599 NLFIT_PREVIEW_UPDATE_CLEANUP
/*
/// Cheney 2007-7-16 SHOULD_GENERATE_X_DATA_UNIFORMLY_SPACED_IN_LOG_SPACE_WHEN_LOG_TYPE 
bool get_fitted_curve_min_max(double& dMin, double& dMax, double dMargin, bool bLogScale, int* pSteps, double* pIncre, bool bRound)
{
	if(dMin > dMax)
		return false;
	
	if(pIncre && !pSteps)
		return false;
	
	if(bLogScale)
	{
		double dMintemp = dMin, dMaxtemp = dMax; 
		double dtemp = dMargin * log10(dMaxtemp/dMintemp);
		dMin = 10^( log10(dMintemp) - dtemp );
		dMax = 10^( log10(dMaxtemp) + dtemp );
		if(pIncre)
			*pIncre = log10(dMax / dMin) / *pSteps;  
	}
	else
	{
		double dtemp = ( dMax - dMin ) * dMargin;
 		dMin -= dtemp;
		dMax += dtemp;
		if(pIncre)
			*pIncre = (dMax - dMin) / *pSteps;
		
		if(bRound && pIncre && pSteps)
			RoundLimits(&dMin, &dMax, pIncre, *pSteps);
	}
	
	return true;
}
///end SHOULD_GENERATE_X_DATA_UNIFORMLY_SPACED_IN_LOG_SPACE_WHEN_LOG_TYPE
*/

//---- Iris 11/20/2008 v8.0975d QA80-12591-S1 CENTRALIZE_NEW_X_DATA_TYPE_AND_LOG_TYPE_CODES, new type is Use Source Graph Scale Type
/*
static double _log_to_linear(double x)
{ 
	if(x <=0) 
		return -10; // something is wrong
	
	return log10(x);
}
static double _linear_to_log(double x) {return 10^x;}
*/
//double get_min_max_inc(double& dMin, double& dMax, bool bLogScale, double dMargin, int nSteps)
double get_min_max_inc(double& dMin, double& dMax, int nScaleType, double dMargin, int nSteps)
//---- 
{
	//---- Iris 11/20/2008 v8.0975d QA80-12591-S1 CENTRALIZE_NEW_X_DATA_TYPE_AND_LOG_TYPE_CODES
	//if(bLogScale)
	//	dMin = _log_to_linear(dMin), dMax = _log_to_linear(dMax);
	if(SCALE_TYPE_LINEAR != nScaleType)
	{
		dMin = real_space(dMin, nScaleType);
		dMax = real_space(dMax, nScaleType);
	}
	//----

	if(dMin > dMax)
	{
		double tt; SWAP(dMin, dMax, tt);
	}

	double 	dtemp = (dMax - dMin) * dMargin;
	dMin -= dtemp;
	dMax += dtemp;
	double dInc = 0;
	if(nSteps)	
		RoundLimits(&dMin, &dMax, &dInc, nSteps);

	//---- Iris 11/20/2008 v8.0975d QA80-12591-S1 CENTRALIZE_NEW_X_DATA_TYPE_AND_LOG_TYPE_CODES
	//if(bLogScale)
	//	dMin = _linear_to_log(dMin), dMax = _linear_to_log(dMax);
	/// Iris 01/15/2009 FIX_NLFIT_PREVIEW_NOT_RESCALE_FOR_LOG_SCALE_TYPE
	if(SCALE_TYPE_LINEAR != nScaleType)
	{
		dMin = real_inv_space(dMin, nScaleType);
		dMax = real_inv_space(dMax, nScaleType);
	}
	///end FIX_NLFIT_PREVIEW_NOT_RESCALE_FOR_LOG_SCALE_TYPE
	//----
	
	return dInc;
}


///Arvin 11/22/07 NEED_RESCALE_FOR_3D_FITTINGS
//bool get_data_range_xy_scale(DataRange& dr, double& x1, double& x2, double& y1, double& y2, DWORD dwRules)
bool get_data_range_xy_scale(DataRange& dr, double& x1, double& x2, double& y1, double& y2, DWORD dwRules,  double* pz1, double* pz2)
///end NEED_RESCALE_FOR_3D_FITTINGS
{
	if(!dr)
		return false;
	DRPLOTTINGSCALE st = {0};
	//	dwRules = DRR_GET_DEPENDENT | DRR_NO_FACTORS;
	int nRet = dr.GetPlottingScale(dwRules, &st);
	if(nRet != 0 || is_missing_value(st.rX1))
	{
		string strErr; strErr.Format("GetPlottingScale failed %d", nRet);
		error_report(strErr);
		return false;
	}
	x1 = st.rX1, x2 = st.rX2;
	y1 = st.rY1, y2 = st.rY2;
	double tt;
	if(x1 > x2) 
	{
		SWAP(x1,x2,tt);
	}
	if(y1 > y2)
	{
		SWAP(y1,y2,tt);
	}
	///Arvin 11/22/07 NEED_RESCALE_FOR_3D_FITTINGS
	double z1 = st.rZ1, z2 = st.rZ2;
	if(z1 > z2)
		SWAP(z1,z2,tt);
	if(pz1)
		*pz1 = z1;
	if(pz2)
		*pz2 = z2;
	///end NEED_RESCALE_FOR_3D_FITTINGS	
		
	return true;
}

//---- end NLFIT_PREVIEW_UPDATE_CLEANUP

///Arvin 11/09/07 NLFIT_REPORT_GRAPH_NEED_RESCALE_WHILE_PART_GRAPH_SELECTION_AS_INPUT
bool is_plot_requir_rescale(DataPlot& dp, int nOption) 
{
	if(!dp.IsValid())
		return false;
	
	double dfx, dfy;
	/// Hong 04/09/08 QA80-11395 FIX_FIT_CURVE_IN_REPORT_FAIL_RESCALE_WHEN_NO_OVERLAP
	//if(dp.GetAxesScaleOverlap(&dfx, &dfy) < 0)
	//	return false;
	int nRet = dp.GetAxesScaleOverlap(&dfx, &dfy);
	if ( nRet < 0 )
		return false;
	if ( GORR_NO_OVERLAP == nRet )
		return true;
	/// end FIX_FIT_CURVE_IN_REPORT_FAIL_RESCALE_WHEN_NO_OVERLAP
	
	bool bNeedRescale = false;
	//Two side margin are 0.08, so I choose 1.3 as threshold in this case
	if( (nOption & RESCALE_SHOW_ALL) && ((dfx > 1.3) || (dfy > 1.3)) ) 
		bNeedRescale = true;
	
	if( (nOption & RESCALE_SHRINK_X) && (dfx < 0.8) )
		bNeedRescale = true;
	
	if( (nOption & RESCALE_SHRINK_Y) && (dfy < 0.8) )
		bNeedRescale = true;
	
	return bNeedRescale;
}
///end NLFIT_REPORT_GRAPH_NEED_RESCALE_WHILE_PART_GRAPH_SELECTION_AS_INPUT

//---- Iris 11/19/2008 v8.0975 QA80-12591-P2 FIX_APPARENT_FIT_ON_GRAPH_CUSTOM_RANGE_GET_INCORRECT_X
typedef bool (*FUNC_GET_SCALE_TYPE)(GraphLayer& gl, int* pnXScale = NULL, int* pnYScale = NULL); 
//----
bool get_scale_type(const GraphLayer& gl, int& nScaleType, bool bIsX)
{
	if(!gl)
		return false;
	///Sophy 1/9/2009 v8.0995 QA80-12591-P7 FIX_USE_SOURCE_GRAPH_SCALE_TYPE_GIVE_WRONG_RESULT
	//FUNC_GET_SCALE_TYPE _pfn = Project.FindFunction("axis_get_scale_type", NULL, TRUE);
	FUNC_GET_SCALE_TYPE _pfn = Project.FindFunction("axis_get_scale_type", "OriginLab\\graph_utils.c", true);
	///end FIX_USE_SOURCE_GRAPH_SCALE_TYPE_GIVE_WRONG_RESULT
	if(_pfn)
	{
		if(bIsX)
			return _pfn(gl, &nScaleType);
		else
			return _pfn(gl, NULL, &nScaleType);
	}
	return false;	
}

/// Iris 11/21/06 CORRECT_SPAN_AXIS_AS_FITX_WHEN_GET_INPUT_FROM_WKS
/*
///Cheney 2006-11-22 SHOULD_ALWAYS_USE_INPUT_Y_TO_GET_RESIDUAL
//bool get_data_by_fitted_curve_options(const vector& vData, vector& vFit, const FitResultCurveDataOptions& fitOptions, double* pdAxisMin, double* pdAxisMax, bool bSortFitData)
bool get_data_by_fitted_curve_options(const vector& vData, vector& vFit, const FitResultCurveDataOptions& fitOptions, double* pdAxisMin, double* pdAxisMax, bool bSortFitData, bool bSetCustomInput)
///end SHOULD_ALWAYS_USE_INPUT_Y_TO_GET_RESIDUAL
*/

///------ Folger 01/04/09 QA80-12962 GENERATE_INDIVIDUAL_FIT_CURVE_USING_INDEPENDENT_X_IN_PA
static	int		_get_smart_x_on_peak_shape(double* pData, uint nPts, double x1, double x2, double x0, double w, int nFuncForm)
{
	return ocmath_generate_peak_x_array(pData, nPts, x1, x2, x0, w, nFuncForm);
}
///------ End GENERATE_INDIVIDUAL_FIT_CURVE_USING_INDEPENDENT_X_IN_PA

///------ Folger 11/26/09 GENERATE_INDIVIDUAL_FIT_CURVE_USING_INDEPENDENT_X_FAILS_FOR_LOG_X_DATA_TYPE
//static bool _convert_scale_type_to_linear(int nScaleType, double& dMin, double& dMax)
static bool _convert_scale_type_to_linear(int nScaleType, double& dMin, double& dMax, SmartXOnPeakShapeOption* pSmartXOpt)
///------ End GENERATE_INDIVIDUAL_FIT_CURVE_USING_INDEPENDENT_X_FAILS_FOR_LOG_X_DATA_TYPE
{
	if(SCALE_TYPE_LINEAR != nScaleType)
	{
		dMin = real_space(dMin, nScaleType);
		dMax = real_space(dMax, nScaleType);
		///------ Folger 11/26/09 GENERATE_INDIVIDUAL_FIT_CURVE_USING_INDEPENDENT_X_FAILS_FOR_LOG_X_DATA_TYPE
		if ( pSmartXOpt )
		{
			double	rLeft = pSmartXOpt->x0 - pSmartXOpt->w;
			double	rRight = pSmartXOpt->x0 + pSmartXOpt->w;
			rLeft = real_space(rLeft, nScaleType);
			rRight = real_space(rRight, nScaleType);
			
			pSmartXOpt->x0 = real_space(pSmartXOpt->x0, nScaleType);
			pSmartXOpt->w = min(rRight - pSmartXOpt->x0, pSmartXOpt->x0 - rLeft);
		}
		///------ End GENERATE_INDIVIDUAL_FIT_CURVE_USING_INDEPENDENT_X_FAILS_FOR_LOG_X_DATA_TYPE
		return true;
	}	
	return false;
}

///Arvin 04/12/07 v8.0599 PREVIEW_RANGE_SHOULD_BE_SOUCE_GRAPH_OR_DATA_RANGE_WHEN_RANGE_TYPE_IS_SPAN_TO_FULL_AXIS_RANGE by cp's suggestion
//bool get_data_by_fitted_curve_options(const vector& vData, vector& vFit, const FitResultCurveDataOptions& fitOptions, const GraphLayer& gl, bool bSortFitData, bool bSetCustomInput)
///Kyle 11/18/2008 QA80-12591 ADD_MORE_OPTION_FOR_INPUT_DATA_TYPE_IN_FITTING_TOOLS
//bool get_data_by_fitted_curve_options(const vector& vData, vector& vFit, const FitResultCurveDataOptions& fitOptions, const GraphLayer& gl, double* pdAxisMin, double* pdAxisMax, bool bSortFitData, bool bSetCustomInput)
///------ Folger 01/04/09 QA80-12962 GENERATE_INDIVIDUAL_FIT_CURVE_USING_INDEPENDENT_X_IN_PA
//bool get_data_by_fitted_curve_options(const vector& vData, vector& vFit, const FitResultCurveDataOptions& fitOptions, const GraphLayer& gl, bool bIsX, double* pdAxisMin, double* pdAxisMax, bool bSortFitData, bool bSetCustomInput)
bool get_data_by_fitted_curve_options(const vector& vData, vector& vFit, const FitResultCurveDataOptions& fitOptions, const GraphLayer& gl, bool bIsX, double* pdAxisMin, double* pdAxisMax, bool bSortFitData, bool bSetCustomInput, SmartXOnPeakShapeOption* pSmartXOpt/* = NULL*/)
///------ End GENERATE_INDIVIDUAL_FIT_CURVE_USING_INDEPENDENT_X_IN_PA
///End ADD_MORE_OPTION_FOR_INPUT_DATA_TYPE_IN_FITTING_TOOLS
///end PREVIEW_RANGE_SHOULD_BE_SOUCE_GRAPH_OR_DATA_RANGE_WHEN_RANGE_TYPE_IS_SPAN_TO_FULL_AXIS_RANGE
{	
	int nDataType = bSetCustomInput? fitOptions.DataType : FIT_CURVE_SAME_AS_DATA;
	switch(nDataType)
	{
	case FIT_CURVE_SAME_AS_DATA:
		vFit = vData;
		/// Iris 3/26/2009 QA80-13343 KEEP_INPUT_DATA_ORDER_IF_FITX_SAME_AS_INPUT_DATA
		/*
		//--- CPY 7/15/06 LR_FIT_USE_SOURCE_X_DATA_SHOULD_SORT_FIRST
		if(bSortFitData && vFit.GetSize() > 0 )
			vFit.Sort();
		//---
		*/
		if(bSortFitData && vFit.GetSize() > 1 )
		{
			int wOption = vFit[0] <= vFit[vFit.GetSize()-1] ? SORT_ASCENDING : SORT_DESCENDING;
			vFit.Sort(wOption);
		}
		///end KEEP_INPUT_DATA_ORDER_IF_FITX_SAME_AS_INPUT_DATA
		return true;  //return directly
	default:
		break;			
	}			

	///------ Folger 02/16/09 QA80-10399-P2 FITCURVE_IS_MISSING_IN_NLFIT_REPORT_WHEN_INPUT_X_HAS_NEGATIVE_DATA_FOR_LOG10_SCALE_TYPE
	//double dMin, dMax, dInc;
	//vData.GetMinMax(dMin, dMax);
	///------ End FITCURVE_IS_MISSING_IN_NLFIT_REPORT_WHEN_INPUT_X_HAS_NEGATIVE_DATA_FOR_LOG10_SCALE_TYPE
	
	int nSteps = fitOptions.N - 1; 
	
	//---- Iris 11/20/2008 v8.0975d QA80-12591-S1 CENTRALIZE_NEW_X_DATA_TYPE_AND_LOG_TYPE_CODES
	int nScaleType = FIT_CURVE_UNIFORM_LOG == fitOptions.DataType? SCALE_TYPE_LOG10 : SCALE_TYPE_LINEAR;
	if(gl && FIT_CURVE_SAME_AS_SOURCE_GRAPH == fitOptions.DataType)
		get_scale_type(gl, nScaleType, bIsX);
	//----
	
	///------ Folger 02/16/09 QA80-10399-P2 FITCURVE_IS_MISSING_IN_NLFIT_REPORT_WHEN_INPUT_X_HAS_NEGATIVE_DATA_FOR_LOG10_SCALE_TYPE
	vector	vTmp;
	vTmp = vData;
	
	switch ( nScaleType )
	{
	case SCALE_TYPE_LOG10:
	case SCALE_TYPE_LN:
	case SCALE_TYPE_LOG2:
		vTmp.Replace(0.0, NANUM, WKSREPL_TEST_LESSTHAN | WKSREPL_TEST_EQUAL);
		break;
		
	case SCALE_TYPE_PROBABILITY:
	case SCALE_TYPE_PROBIT:
	case SCALE_TYPE_LOGIT:
		vTmp.Replace(0.0, NANUM, WKSREPL_TEST_LESSTHAN | WKSREPL_TEST_EQUAL);
		vTmp.Replace(100.0, NANUM, WKSREPL_TEST_GREATER | WKSREPL_TEST_EQUAL);
		break;
		
	case SCALE_TYPE_PECIPROCA:
		vTmp.Replace(0.0, NANUM, WKSREPL_TEST_EQUAL);
		break;
		
	case SCALE_TYPE_OFFSET_RECIPROCAL:
		vTmp.Replace(-273.14, NANUM, WKSREPL_TEST_EQUAL);
		break;
		
	default:
		break;
	}
	
	double dMin, dMax, dInc;
	vTmp.GetMinMax(dMin, dMax);
	///------ End FITCURVE_IS_MISSING_IN_NLFIT_REPORT_WHEN_INPUT_X_HAS_NEGATIVE_DATA_FOR_LOG10_SCALE_TYPE
	
	bool bScaleTypeConverted = false;
	switch(fitOptions.Range)
	{
	case FIT_CURVE_RANGE_MARGIN:
		//---- Iris 11/20/2008 v8.0975d QA80-12591-S1 CENTRALIZE_NEW_X_DATA_TYPE_AND_LOG_TYPE_CODES
		//get_min_max_inc(dMin, dMax, FIT_CURVE_UNIFORM_LOG == fitOptions.DataType, fitOptions.RangeMargin / 100.0);
		/// Iris 01/15/2009 FIX_NLFIT_PREVIEW_NOT_RESCALE_FOR_LOG_SCALE_TYPE
		//get_min_max_inc(dMin, dMax, nScaleType, fitOptions.RangeMargin / 100.0);
		///------ Folger 11/26/09 GENERATE_INDIVIDUAL_FIT_CURVE_USING_INDEPENDENT_X_FAILS_FOR_LOG_X_DATA_TYPE
		//bScaleTypeConverted = _convert_scale_type_to_linear(nScaleType, dMin, dMax);
		bScaleTypeConverted = _convert_scale_type_to_linear(nScaleType, dMin, dMax, pSmartXOpt);
		///------ End GENERATE_INDIVIDUAL_FIT_CURVE_USING_INDEPENDENT_X_FAILS_FOR_LOG_X_DATA_TYPE
		get_min_max_inc(dMin, dMax, SCALE_TYPE_LINEAR, fitOptions.RangeMargin / 100.0);
		///end FIX_NLFIT_PREVIEW_NOT_RESCALE_FOR_LOG_SCALE_TYPE
		//----
		break;	
		
	case FIT_CURVE_SPAN_AXIS:
		if(gl)
		{
			if(bIsX)
			{
				dMin = gl.X.From;
				dMax = gl.X.To;
			}
			else
			{
				dMin = gl.Y.From;
				dMax = gl.Y.To;
			}	
			
		}
		///Arvin 04/12/07 v8.0599 PREVIEW_RANGE_SHOULD_BE_SOUCE_GRAPH_OR_DATA_RANGE_WHEN_RANGE_TYPE_IS_SPAN_TO_FULL_AXIS_RANGE by cp's suggestion
		else if(pdAxisMin && pdAxisMax)
		{
			dMin = *pdAxisMin;
			dMax = *pdAxisMax;
		}
		///end PREVIEW_RANGE_SHOULD_BE_SOUCE_GRAPH_OR_DATA_RANGE_WHEN_RANGE_TYPE_IS_SPAN_TO_FULL_AXIS_RANGE
		
		//else /// Iris 3/25/2009 	QA80-12591-P9 FIX_WRONG_FITX_DATA_IF_LOG_TYPE_AND_SPAN_FULL_RANGE
		{
			//---- Iris 11/20/2008 v8.0975d QA80-12591-S1 CENTRALIZE_NEW_X_DATA_TYPE_AND_LOG_TYPE_CODES
			//get_min_max_inc(dMin, dMax, FIT_CURVE_UNIFORM_LOG == fitOptions.DataType); 
			/// Iris 01/15/2009 FIX_NLFIT_PREVIEW_NOT_RESCALE_FOR_LOG_SCALE_TYPE
			//get_min_max_inc(dMin, dMax, nScaleType);
			///------ Folger 11/26/09 GENERATE_INDIVIDUAL_FIT_CURVE_USING_INDEPENDENT_X_FAILS_FOR_LOG_X_DATA_TYPE
			//bScaleTypeConverted = _convert_scale_type_to_linear(nScaleType, dMin, dMax);
			bScaleTypeConverted = _convert_scale_type_to_linear(nScaleType, dMin, dMax, pSmartXOpt);
			///------ End GENERATE_INDIVIDUAL_FIT_CURVE_USING_INDEPENDENT_X_FAILS_FOR_LOG_X_DATA_TYPE
			/// Iris 3/25/2009 	QA80-12591-P9 FIX_WRONG_FITX_DATA_IF_LOG_TYPE_AND_SPAN_FULL_RANGE
			//get_min_max_inc(dMin, dMax, SCALE_TYPE_LINEAR); 
			if( bScaleTypeConverted && is_missing_value(dMin) ) // if need converted and dMin <=0, after convert, dMin will be NANUM, so auto change to 0
				dMin = 0; 
			get_min_max_inc(dMin, dMax, SCALE_TYPE_LINEAR, 0, 0); 
			///END FIX_WRONG_FITX_DATA_IF_LOG_TYPE_AND_SPAN_FULL_RANGE
			///end FIX_NLFIT_PREVIEW_NOT_RESCALE_FOR_LOG_SCALE_TYPE
			//---- 
		}
		break;
		
	case FIT_CURVE_CUSTOM:
		///Kyle 12/28/2009 QA80-14832 QUICK_FIT_CHECK_INVALID_MIN_MAX_SETTINGS
		//dMin = fitOptions.Min;
		//dMax = fitOptions.Max;
		{
			double dUserMin = fitOptions.Min;
			double dUserMax = fitOptions.Max;
			bScaleTypeConverted = _convert_scale_type_to_linear(nScaleType, dUserMin, dUserMax, pSmartXOpt);
			if( bScaleTypeConverted && (is_missing_value(dUserMin) || is_missing_value(dUserMax)) )
			{
				dUserMin = (dUserMin==NANUM) ? dMin : fitOptions.Min;
				dUserMax = (dUserMax==NANUM) ? dMax : fitOptions.Max;
				bScaleTypeConverted = _convert_scale_type_to_linear(nScaleType, dUserMin, dUserMax, pSmartXOpt);
			}
			dMin = dUserMin;
			dMax = dUserMax;
		}
		///End QUICK_FIT_CHECK_INVALID_MIN_MAX_SETTINGS
		break;
		
	default:
		break;			
	}
	
	if(0 == nSteps)	///Jasmine 05/04/08 USE_MIN_VALUE_IF_FIT_CURVE_POINT_IS_ONE
	{
		vFit.SetSize(1);
		vFit[0] = dMin;
	}
	else
	{
		//---- Iris 11/20/2008 v8.0975d QA80-12591-S1 CENTRALIZE_NEW_X_DATA_TYPE_AND_LOG_TYPE_CODES
		/*
		///Cheney 2007-7-16 SHOULD_GENERATE_X_DATA_UNIFORMLY_SPACED_IN_LOG_SPACE_WHEN_LOG_TYPE 
		if(nDataType == FIT_CURVE_UNIFORM_LOG)
		{
			dInc = log10(dMax / dMin) / nSteps;            // Log transform of inc=(end-begin)/(npts-1)
			vFit.Data(log10(dMin), log10(dMax), dInc); 
			vFit = 10^vFit;                          
		}
		///Kyle 11/18/2008 QA80-12591 ADD_MORE_OPTION_FOR_INPUT_DATA_TYPE_IN_FITTING_TOOLS
		else if(nDataType == FIT_CURVE_SAME_AS_SOURCE_GRAPH)
		{
			double dRMin, dRMax;
			int nScaleType = 0;
			GraphLayer glSrc;
			if(gl)
				glSrc = gl;
			else
				glSrc = Project.ActiveLayer();			// if the source graph not passed into the function

			if(glSrc)
			{
				
				Tree trFormat;
				trFormat = glSrc.GetFormat(FPB_SCALE, FOB_SCALE, true, true);
				TreeNode	trAxis;
				if(bIsX)
					trAxis = trFormat.Root.Axes.GetNode("X");
				else
					trAxis = trFormat.Root.Axes.GetNode("Y");
				if(trAxis)
					nScaleType = trAxis.Scale.Type? trAxis.Scale.Type.nVal : 0;		

			}
			dRMin = real_space(dMin, nScaleType);
			dRMax = real_space(dMax, nScaleType);
			dInc = (dRMax-dRMin)/nSteps;
			vFit.Data(dRMin, dRMax, dInc);
			vFit = real_inv_space(vFit, nScaleType);
		}
		///End ADD_MORE_OPTION_FOR_INPUT_DATA_TYPE_IN_FITTING_TOOLS
		else
		///end SHOULD_GENERATE_X_DATA_UNIFORMLY_SPACED_IN_LOG_SPACE_WHEN_LOG_TYPE
		{
			dInc = (dMax - dMin) / nSteps;	
			vFit.Data(dMin, dMax, dInc);
		}
		*/
		///------ Folger 01/04/09 QA80-12962 GENERATE_INDIVIDUAL_FIT_CURVE_USING_INDEPENDENT_X_IN_PA
		if ( NULL != pSmartXOpt )
		{
			vFit.SetSize(pSmartXOpt->nPts);
			_get_smart_x_on_peak_shape(vFit, pSmartXOpt->nPts, dMin, dMax, pSmartXOpt->x0, pSmartXOpt->w, pSmartXOpt->nFuncForm);
		}
		else
		{
		///------ End GENERATE_INDIVIDUAL_FIT_CURVE_USING_INDEPENDENT_X_IN_PA
			/// Iris 1/16/2009 ROLLBACK_SOPHY_CODES_TO_FIX_LOG_X_TYPE_BUG
			// Sophy's change introduce new bug, in fact, the bug he wanted to fix I have fixed, see FIX_NLFIT_PREVIEW_NOT_RESCALE_FOR_LOG_SCALE_TYPE
			/*
			///Sophy 1/15/2009 v8.0957 FIX_CALCULATE_WRONG_RESULT_WHEN_XDATATYPE_IS_LOG_AND_EXPAND_TO_FULL_RANGE
			//dInc = (dMax - dMin) / nSteps;	
			//vFit.Data(dMin, dMax, dInc);
			dInc = real_space(dMax - dMin, nScaleType) / nSteps;
			vFit.Data(real_space(dMin, nScaleType), real_space(dMax, nScaleType), dInc);
			///end FIX_CALCULATE_WRONG_RESULT_WHEN_XDATATYPE_IS_LOG_AND_EXPAND_TO_FULL_RANGE
			*/
			dInc = (dMax - dMin) / nSteps;	
			vFit.Data(dMin, dMax, dInc);
			///end ROLLBACK_SOPHY_CODES_TO_FIX_LOG_X_TYPE_BUG
		}		///------ Folger 01/04/09 QA80-12962 GENERATE_INDIVIDUAL_FIT_CURVE_USING_INDEPENDENT_X_IN_PA
		///Kyle 12/28/2009 QA80-14832 INVALID_MIN_MAX_SETTINGS
		/*
		if(bScaleTypeConverted && SCALE_TYPE_LINEAR != nScaleType)
		{
			vFit = real_inv_space(vFit, nScaleType);
		}
		*/
		///End INVALID_MIN_MAX_SETTINGS
		//----		
	}
	///Kyle 12/28/2009 QA80-14832 INVALID_MIN_MAX_SETTINGS
	if(bScaleTypeConverted && SCALE_TYPE_LINEAR != nScaleType)
	{
		vFit = real_inv_space(vFit, nScaleType);
	}
	///End INVALID_MIN_MAX_SETTINGS
		
	return true;	
}

/// Kevin 08/29/05 MODIFY_VECTOR_TOSTRING
///	Kevin 08/18/05 ADD_VECTOR_TO_STRING
/*int VectorToStr(const vector<double> &vec, string& str, bool bSort = true, bool bRemoveRepeat = true)
{
	if( NULL == vec || NULL == str)
		return -1;

	vector<double> vecTemp;
	int ii, jj;
	int iSize;

	if( bRemoveRepeat )
	{


		vecTemp.Add( vec[0] );
		for(ii = 0;ii < vec.GetSize(); ii++)
			for(jj = 0;jj < vecTemp.GetSize(); jj++)
				if( vecTemp[jj] != vec[ii] )
				{
					vecTemp.Add( vec[ii] );
					break;
				}		

	}	//end if

	if( bSort )
		vecTemp.Sort();

	str = "";
	iSize = vecTemp.GetSize();
	for(ii = 0;ii < iSize - 1; ii++)
	{
		str += (string) vecTemp[ii];
		str += '|';
	}
	str += vecTemp[ii];

	return iSize;
}*/
/// End ADD_VECTOR_TO_STRING

int vector_to_str_list(const vector &vec, string &strList, bool bSort, bool bRemoveRepeat, char chSeparator) //=true, =true, ='|'
{
	if( NULL == vec || NULL == strList) 
		return -1; 
 
	vector vTemp;
	vTemp = vec; 

	if( bSort && vTemp.GetSize() > 0 ) vTemp.Sort(); 

	vector<string> vsList; 

	for(int index; index<vTemp.GetSize(); index++) 
	{
		string strTemp = vTemp[index];
		if(bRemoveRepeat && ( -1 != vsList.Find(strTemp)) ) 
			continue; 
		vsList.Add(strTemp); 
	} 

	return strList.SetTokens(vsList, chSeparator); 
}
/// End MODIFY_VECOTR_TO_STRING


/// YuI 08/23/05 XF_MATRIX_OBJECT_CENTRALIZED_DATA_CONVERSION
bool	check_matrix_has_data(MatrixObject& mObj, BOOL bOfferConversion)
{
	if( !mObj.IsValid() )
	{
		error_report("MatrixObject is invalid");
		return false;
	}
	if( !mObj.HasData() )
	{
		/// EJP 2006-03-29 v8.0382 QA70-8187 GET_DATA_VALUES_DIRECT_FROM_IMAGE
		/*
		if( bOfferConversion )
		{
			if( IDYES == MessageBox(NULL, "MatrixObject has no data.\nWould you like to convert image to data?", "Convert image to data", MB_YESNO) )
			{
				// this is temp solution until I write proper function in MatrixObject
				MatrixLayer ml;
				mObj.GetParent(ml);
				return (ml.SetViewImage(FALSE) && ml.SetViewImage());
			}
			
			return false;
		}
		*/
		/// end GET_DATA_VALUES_DIRECT_FROM_IMAGE
		error_report("MatrixObject has no data");
		return false;
	}
	
	return true;
}
/// end XF_MATRIX_OBJECT_CENTRALIZED_DATA_CONVERSION

/// ML 1/13/2006 XVARIABLEBASE_TO_VC
/*
//---- CPY 10/4/05 QA70-7895 STR_VAR_FROM_XF_ALLOW_NO_$
static bool _get_LT_str(const string& strName, string& strVal)
{
	char	szBuffer[100*MAXLINE];
	szBuffer[0] = '\0';
	bool bFound = LT_get_str(strName, szBuffer, 100*MAXLINE);
	strVal = szBuffer;
	return bFound;
}
//----
//---- CPY 10/10/05 CENTRALIZED_XF_ARG_QUOTE_HANDLING
// return CCS_LTVAR_SUBSTITUDED if LT str variable and converted
// return CCS_QUOTE_REMOVED if quoted and quotes removed
// return CCS_ERR_LTVAR_NOT_FOUND if $ ended but not found to be LT str variable
// return CCS_NO_CHANGE if no change, either because not ended with $ and bCheckLTstrVarOnlyIfDollarEnded=false, or bCheckLTstrVarOnlyIfDollarEnded but not ended with $ and not a LT str variable
//
int check_cvt_str(const string& strVal, string& str, bool bCheckLTstrVarOnlyIfDollarEnded)
{
	if(strVal.GetLength() > 1 && strVal[0] == '"') // quoted
	{
		strVal.TrimLeft('"');
		strVal.TrimRight('"');
		str = strVal;
		return CCS_QUOTE_REMOVED;
	}
	int nPos;
	string strVar = strVal;
	int nChar = str_end_char(strVal, &nPos);
	bool bDollarEnded = false;
	if('$' == nChar)
	{
		strVar = strVal.Left(nPos);
		bDollarEnded = true;
	}
	else if(bCheckLTstrVarOnlyIfDollarEnded)
	{
		str = strVal;
		return is_good_C_identifier(str)? CCS_NO_CHANGE_GOOD_C_NAME : CCS_NO_CHANGE;
	}
		
	bool bFound = false;
	bool bGoodCname = false;
	if(is_good_C_identifier(strVar))
	{
		string strTemp;
		bFound = _get_LT_str(strVal, strTemp);
		if(bFound)
		{
			str = strTemp;
			return CCS_LTVAR_SUBSTITUDED;
		}
		bGoodCname = true;
	}
	str = strVal;
	if( bDollarEnded )
		return CCS_ERR_LTVAR_NOT_FOUND;
		
	return bGoodCname?CCS_NO_CHANGE_GOOD_C_NAME : CCS_NO_CHANGE;
}
//---- end //---- CPY 10/10/05 CENTRALIZED_XF_ARG_QUOTE_HANDLING
*/
/// end XVARIABLEBASE_TO_VC



///----Frank 11/23/05 ADD_NOISE_AND_SAVE_DATA
/// Cloud 8/30/07 ADD_OPTION_OF_NOISE
//void add_white_noise(vector& vSignal, double level)
void add_white_noise(vector& vSignal, double level, int type)
/// End ADD_OPTION_OF_NOISE
{
	///Kyle 03/17/2009 QA80-12564-P7 GENERAL_ERROR_MESSAGE_FOR_X_RANGE_ERROR
	if(vSignal.GetSize() < 1)
		return;
	///End GENERAL_ERROR_MESSAGE_FOR_X_RANGE_ERROR
	//add gaussian noise with standard deviation
	vector vTemp;
	/// Cloud 8/30/07 ADD_OPTION_OF_NOISE
	int nSize = vSignal.GetSize();
	double base;
	switch (type)
	{
	case NOISE_OVER_AVERAGE:
	/// End ADD_OPTION_OF_NOISE
		vTemp = abs(vSignal);
		int nTemp;
		double dSum = ocmath_d_sum(vTemp, vTemp.GetSize(), NULL, NULL, NULL, NULL, &nTemp); 
		if(nTemp < 1)
		{
		   out_str("no valid data");
		   return;
		}
	/// Cloud 8/30/07 ADD_OPTION_OF_NOISE
		//ave = dSum/nTemp;
		base = dSum/nTemp;
		break;
	case NOISE_OVER_DIFFERENCE:
		double dMin, dMax;
		vSignal.GetMinMax(dMin, dMax);
		base = dMax - dMin;
		break;
	}
	vTemp.Normal(nSize); // mean = 0, sd = 1
	//vTemp *= ave * level/100.;
	vTemp *= base * level/100.;
	/// End ADD_OPTION_OF_NOISE
	vSignal += vTemp;
}

void save_data_2_Wks(Worksheet& wks, vector& vX, vector& vY, vector& vZ)
{
	if(!wks)
		return;
	if (vZ == NULL)
	{
		wks.SetSize(-1, 2);
		wks.SetColDesignations("XY");
		Dataset dsX(wks, 0), dsY(wks, 1);
		dsX = vX;
		dsY = vY;
	}
	else
	{
		wks.SetSize(-1, 3);
		wks.SetColDesignations("XYZ");
		Dataset dsX(wks, 0), dsY(wks, 1), dsZ(wks, 2);
		dsX = vX;
		dsY = vY;
		dsZ = vZ;
	}
}
///----End ADD_NOISE_AND_SAVE_DATA


//---- CPY 12/12/05 NLF_FUNCTIONS_NEEDED_IN_PCH
//-----------------------------------------------------------
// the following nlf functions are needed in various PCH and
// must be compiled into Origin.h
//-----------------------------------------------------------

///Arvin 07/06/07 v8.0654 NEED_USE_THIS_FUNCTION_IN_FITNL
//static bool nlf_find_category(LPCSTR lpcszFunc, string& strCategory, int& nFolderLocation)
bool nlf_find_category(LPCSTR lpcszFunc, string& strCategory, int& nFolderLocation)
///END NEED_USE_THIS_FUNCTION_IN_FITNL
{
	if(nFolderLocation < 0)
	{
		// vector<int> vnFolderTypes = {USER_FOLDER, ALL_USER_FOLDER, SYS_FOLDER}
		vector<int> vnFolderTypes = {ORIGIN_PATH_USER, ORIGIN_PATH_GROUP, ORIGIN_PATH_SYSTEM};
		int nType;
		for(int ii = 0; ii < vnFolderTypes.GetSize(); ii++)
		{
			nType = vnFolderTypes[ii];
			if(nlf_find_category(lpcszFunc, strCategory, nType))
			{
				nFolderLocation = vnFolderTypes[ii];
				return true;
			}
		}
		return false;
	}
	string strFilename = nlf_get_ini_filepath(nFolderLocation);
	if(!strFilename.IsFile())
		return false;
	INIFile		iniNLSF(strFilename);
	
	vector<string> vsSections;
	vector<string> vsCategorys;
	vector<string> vsFunctions;
	
	//search function by function name
	bool bFind = false;
	// string strFuncFileName;
	int nNumSection = iniNLSF.GetSectionNames(vsSections);
	for(int ii=0; ii<nNumSection; ii++)
	{
		if(0 == vsSections[ii].CompareNoCase("Category"))
			break;
	}
	if(ii == nNumSection)
		return false; // really not possible
	
	int nNumCate = iniNLSF.GetKeyNames(vsCategorys, vsSections[ii]);
	for(int nCateIndex=0; !bFind && nCateIndex<nNumCate; nCateIndex++)
	{
		int nNumFunctions = iniNLSF.GetKeyNames(vsFunctions, vsCategorys[nCateIndex]);
		for(int nFuncIndex=0; !bFind && nFuncIndex<nNumFunctions; nFuncIndex++)
		{
			if(0 == vsFunctions[nFuncIndex].CompareNoCase(lpcszFunc))
			{
				strCategory = vsCategorys[nCateIndex];
				return true;
			}
		}
	}
	return false;				
}

//CPY 1/19/06 if nIniPathAsDefault != ORIGIN_PATH_UNDEF, then we will turn on searching and start from this location
static bool nlf_get_fdf_filepath(string& strFilePath, const string& strFunctionName, const string& strCategoryName, int& nFolderLocation, int nInILocation, int nIniPathAsDefault)
{	
	///Jasmine 09/26/07 SEARCH_THE_RIGHT_NLSF_INI
	/*
	string strINIFilename = nlf_get_ini_filepath(nInILocation);
	if(!strINIFilename.IsFile())
		return false;
	
	INIFile		iniNLSF(strINIFilename);	
	string strFuncFileName = iniNLSF.ReadString(strCategoryName, strFunctionName);
	*/
	string strFuncFileName;
	///Jasmine 09/30/07 QA70-10462 CHECK_REPLACE_USER_FITTING_FUNCTION_TO_GROUP_ONE
	//look function in group folder only when the client's user function is not found
	vector<int> vnINIs;// ={USER_FOLDER, ALL_USER_FOLDER, SYS_FOLDER};
	//vnINIs.InsertAt(0, nInILocation);
	vnINIs.Add(nInILocation);
	string strGroup = okutil_get_origin_path(ORIGIN_PATH_GROUP, NULL, TRUE);
	if(USER_FOLDER == nInILocation && !strGroup.IsEmpty())
		vnINIs.Add(ALL_USER_FOLDER);
	///End CHECK_REPLACE_USER_FITTING_FUNCTION_TO_GROUP_ONE
	for(int ii = 0; ii < vnINIs.GetSize(); ii++)
	{
		int nINI = vnINIs[ii];
		string strINIFilename = nlf_get_ini_filepath(nINI);//nInILocation
		if(!strINIFilename.IsFile())
			continue;//return false;
		
		INIFile		iniNLSF(strINIFilename);	
		strFuncFileName = iniNLSF.ReadString(strCategoryName, strFunctionName);
		if(!strFuncFileName.IsEmpty())
			break;
	}
	if(strFuncFileName.IsEmpty())
		return false;
	///End SEARCH_THE_RIGHT_NLSF_INI
	bool bSearch = false;
	if(nFolderLocation < 0 || nIniPathAsDefault != ORIGIN_PATH_UNDEF)
	{
		bSearch = true;
		nFolderLocation = nIniPathAsDefault != ORIGIN_PATH_UNDEF? nIniPathAsDefault: USER_FOLDER;
	}
		
	// string strFormatFDF = "%s%s.fdf";
	// strFilePath.Format(strFormatFDF, get_origin_path(nFolderLocation, STR_FDF_FILE_FOLDER), strFuncFileName);
	bool bExists = okutil_make_file_path(strFilePath, nFolderLocation, STR_FDF_FILE_FOLDER, strFuncFileName, STR_FDF_FILE_EXT);


	//if(bSearch && !strFilePath.IsFile())
	if(bSearch && !bExists)
	{
		nFolderLocation = ALL_USER_FOLDER;
		// strFilePath.Format(strFormatFDF, get_origin_path(nFolderLocation, STR_FDF_FILE_FOLDER), strFuncFileName);
		bExists = okutil_make_file_path(strFilePath, nFolderLocation, STR_FDF_FILE_FOLDER, strFuncFileName, STR_FDF_FILE_EXT);
		// if( !strFilePath.IsFile() )
		if( !bExists )
		{
			nFolderLocation = SYS_FOLDER;
			// strFilePath.Format(strFormatFDF, get_origin_path(nFolderLocation, STR_FDF_FILE_FOLDER), strFuncFileName);
			bExists = okutil_make_file_path(strFilePath, nFolderLocation, STR_FDF_FILE_FOLDER, strFuncFileName, STR_FDF_FILE_EXT);
		}
	}
	// if(!strFilePath.IsFile())
	if(!bExists)
	{
		strFilePath.Empty();
		///Jasmine 07/25/07 SOME_FUNCTIONS_ARE_NOT_IN_SYSTEM_OR_USER_FILE_FOLDER
		if(strFuncFileName.IsFile())
		{
			strFilePath = strFuncFileName;
			return true;
		}
		///End SOME_FUNCTIONS_ARE_NOT_IN_SYSTEM_OR_USER_FILE_FOLDER
		return false;
	}
	return true;
}

string nlf_get_ini_filepath(int nIniPath, bool bAddNLSFini)//=true
{
	//------ Folger 09/03/07 CORRECTLY_GET_ORIGIN75_PATH
	//string strIniFilePath = okutil_get_origin_path(nIniPath, "");
	string strIniFilePath;
	if (nIniPath == ORIGIN_PATH_USER75)
		get_origin75_user_path(strIniFilePath);
	else
		strIniFilePath = okutil_get_origin_path(nIniPath, "");
	//------ End CORRECTLY_GET_ORIGIN75_PATH
	if(!strIniFilePath.IsEmpty() && bAddNLSFini)
		strIniFilePath+="NLSF.ini";
	return strIniFilePath;
}

//------ CPY 04/07/08 QA80-11384 FIX_GET_FDF_FULL_PATH_WITHOUT_CATEGORY
/*
// return the fdf filename without path and without ext
//lpcszFuncName = "system:Gauss", this mean system path only
//lpcszFuncName = "Gauss", this mean that we need to find from User, then AllUser then System
string nlf_get_fdf_filename(LPCSTR lpcszFuncName, string* lpstrCategory, int* pnFolder, string* pstrFullpathFDFfile) //  = NULL =NULL = NULL);
{
	string strEmpty;
	string strCategory = lpstrCategory? *lpstrCategory : "";
	int nPathType = -1; // indicate unknown
	string strFuncName = okutil_separate_composite_name(lpcszFuncName, &nPathType);
	///Jasmine 10/08/07 QA70-10462 SEEK_SHARED_FDF_IN_SHARED_NLSF_INI
	//int nPathINI = -1;
	int nPathINI = ALL_USER_FOLDER == nPathType? nPathType : -1;
	///End SEEK_FOR_SHARED_FDF_IN_SHARED_NLSF_INI
	if(strCategory.IsEmpty())
	{
		if(!nlf_find_category(strFuncName, strCategory, nPathINI))// may find the ini that has the category
			return strEmpty;
		if(nPathType >= 0 && nPathINI >= 0)
			nPathType = nPathINI; // ini file in a diff location found the more appropriate location for loading function
	}
	if(nPathINI < 0)
		//nPathINI = USER_FOLDER;// ini path must be specified	///Jasmine 09/04/07 SHOULD_TRY_TO_GET_INI_PATH_FROM_NPATHTYPE ///Jasmine 09/07/07 but when nPathType == SYS_FOLDER, should use USER_FOLDER instead
		nPathINI = (nPathType < 0 || SYS_FOLDER == nPathType)? USER_FOLDER : nPathType;// ini path must be specified
	string strFullpath;
	if(!nlf_get_fdf_filepath(strFullpath, strFuncName, strCategory, nPathType, nPathINI, nPathINI))
		return strEmpty;
	if(pnFolder)
		*pnFolder = nPathType;
	if(pstrFullpathFDFfile)
		*pstrFullpathFDFfile = strFullpath;
	return GetFileName(strFullpath, true);	
}*/
bool nlf_get_fdf_filename(LPCSTR lpcszFuncName, string* pstrCategory, int* pnFolder, string* pstrFullpathFDFfile) //  = NULL =NULL = NULL);
{
	string strCategory;
	if(pstrCategory) strCategory = *pstrCategory;
		
	int nPathType = -1; // indicate unknown
	string strFuncName = okutil_separate_composite_name(lpcszFuncName, &nPathType);
	int nPathINI = ALL_USER_FOLDER == nPathType? nPathType : -1;
	
	ASSERT(!strFuncName.IsEmpty()); // this is caller's repsonsibility
	
	if(strCategory.IsEmpty())
	{
		if(!nlf_find_category(strFuncName, strCategory, nPathINI))// may find the ini that has the category
			return BOOL_ERR("NLSF category not found for " + strFuncName);
		if ( pstrCategory )
			//--- Jacky 7/14/2010 ORG-545-P2 FIX_CATEGORY_ALWAYS_THE_LAST_USED_ONE
			*pstrCategory = strCategory;
			//--- end FIX_CATEGORY_ALWAYS_THE_LAST_USED_ONE
		if(nPathType >= 0 && nPathINI >= 0)
			nPathType = nPathINI; // ini file in a diff location found the more appropriate location for loading function
	}
	if(nPathINI < 0)
		//nPathINI = USER_FOLDER;// ini path must be specified	///Jasmine 09/04/07 SHOULD_TRY_TO_GET_INI_PATH_FROM_NPATHTYPE ///Jasmine 09/07/07 but when nPathType == SYS_FOLDER, should use USER_FOLDER instead
		nPathINI = (nPathType < 0 || SYS_FOLDER == nPathType)? USER_FOLDER : nPathType;// ini path must be specified
		
	string strFullpath;
	if(!nlf_get_fdf_filepath(strFullpath, strFuncName, strCategory, nPathType, nPathINI, nPathINI))
		return BOOL_ERR("NLSF FDF not found " + strFuncName);
	
	if(pnFolder)
		*pnFolder = nPathType;
	
	if(pstrFullpathFDFfile)
		*pstrFullpathFDFfile = strFullpath;
	
	return true;	
}
//---- end CPY 04/07/08 QA80-11384 FIX_GET_FDF_FULL_PATH_WITHOUT_CATEGORY


///Echo 1/5/05 STR_GROUP_TO_NUM
bool get_group(vector<string> &vstrGroup, vector &vGroup, vector<string> &vstrLab, vector& vNumInGroup)
{
	int nSize = vstrGroup.GetSize();
	if (nSize == 0)
		return false;
	
	vector vIndex;
	vIndex.Data(1, nSize);
	vGroup.SetSize(nSize);
	vector<uint> vnIndex;
	
	vstrGroup.Sort(SORT_ASCENDING, TRUE, vnIndex);
	vIndex.Reorder(vnIndex);
	
	int nGroup = 1;
	
	vGroup[0] = nGroup;
	vstrLab.SetSize(1);
	vNumInGroup.SetSize(1);
	vstrLab[0] = vstrGroup[0];
	vNumInGroup[0] = 1;
	
	for (int ii = 0; ii < nSize - 1; ii++)
	{
		if(vstrGroup[ii] != vstrGroup[ii+1])
		{
			nGroup++;
			vstrLab.Add(vstrGroup[ii+1]);
			vNumInGroup.Add(0);
		}
		
		vGroup[ii+1] = nGroup;
		vNumInGroup[nGroup-1] = vNumInGroup[nGroup-1]+1;
	}
	
	
	
	vIndex.Sort(SORT_ASCENDING, TRUE, vnIndex);
	vGroup.Reorder(vnIndex);
	vstrGroup.Reorder(vnIndex);
	
	return true;
}

///Echo 1/5/05 TRIM_DULP_IN_VEC
void trim_duplicated_value(vector& vData, vector& vNoDuplic)
{
	///*** Terminate if error setting
	if (vData.GetSize() <= 0)
		return;

	/// *** To avoid the changing of vData
	vector vDataCpy;
	vDataCpy = vData;
	vDataCpy.Sort();

	vNoDuplic = vDataCpy;
	int nSize = vDataCpy.GetSize();
	for (int ii =0; ii < nSize-1; ii++)
	{
		if (vDataCpy[ii] == vDataCpy[ii+1])
			vNoDuplic[ii+1] = NANUM;;		
	}
	vNoDuplic.Trim();
}

///Arvin 11/16/07 QA70-10676 UPDATE_MEANS_COMPARISION_PLOT_LEGENT_WITH_SIG_INFO
int  find_unique_values(const vector& vData, vector<int>& vUniqueIndices)
{
	int nDataSize = vData.GetSize();
	if(nDataSize < 1)
		return -1;
	
	vUniqueIndices.SetSize(0);
	int index = 1, nUniqueStart = 0;
	vector vUniqueValues;
	vUniqueIndices.Add(nUniqueStart);
	vUniqueValues.Add(vData[nUniqueStart]);
	while(index < nDataSize)
	{
		vector<uint> vIndex; 
		int nFind = vUniqueValues.Find(vIndex, vData[index]);
		if(nFind <= 0 && vIndex.GetSize() < 1)
		{
			vUniqueValues.Add(vData[index]);
			vUniqueIndices.Add(index);
		}
		
		index++;
	}
	
	return vUniqueIndices.GetSize();
}
///end UPDATE_MEANS_COMPARISION_PLOT_LEGENT_WITH_SIG_INFO

bool range_get_col(const DataRange& dr, const int nIndex, Column& col, int* nRowBegin, int* nRowEnd)
{
	if (!dr)
		return false;
	
	int r1, c1, r2, c2;
	Worksheet wksTemp;
	string strRange;
	int nSumCols = 0;
	for (int kk = 0; kk < dr.GetNumRanges(); kk++)
	{
		dr.GetRange(kk, r1, c1, r2, c2, wksTemp, &strRange);
		if (wksTemp)
		{
			///Echo 5/20/08 v8.0867 HANDLE_WHOLE_WORKSHEET_SELECTED
			if (c2 == -1)
				c2 =  wksTemp.GetNumCols() - 1;
			///END HANDLE_WHOLE_WORKSHEET_SELECTED
			int nCols = c2-c1+1;
			nSumCols += nCols;
			bool bColInRange = nSumCols - nIndex > 0;
			if ( bColInRange )
			{
				col = wksTemp.Columns(c1 + (nIndex-nSumCols+nCols));
				if (nRowBegin) *nRowBegin = r1;
				if (nRowEnd) *nRowEnd = r2;
				break;
			}
		}
	}
	return true;
	
}

///Echo 3/13/06 GET_COL_NAME_FROM_RANGE
///------ Folger 12/18/08 QA80-12642 v8.0988 SUPPORT_MAKE_COLUMN_NAME_WITH_LN_ONLY
//string range_get_col_name(const DataRange& dr, const int nIndex)
string range_get_col_name(const DataRange& dr, const int nIndex, BOOL bLongNameOnlyIfExist/* = FALSE*/)
///------ End SUPPORT_MAKE_COLUMN_NAME_WITH_LN_ONLY
{
	string strName = "";
	Column col;
	range_get_col(dr, nIndex, col);
	if (col)
	{
		//------ Folger 12/03/08 QA80-12642 v8.0982 CONSISTENT_COMMENT_DISPLAY_FOR_COLUMN_NAME_IN_STATISTICS_TOOLS
		//strName = col.GetLongName();
		//if (!lstrcmp(strName, ""))
			//strName = col.GetName();
		string strLongName = col.GetLongName();
		if ( strLongName.IsEmpty() )
			strName = col.GetName();
		///------ Folger 12/18/08 QA80-12642 v8.0988 SUPPORT_MAKE_COLUMN_NAME_WITH_LN_ONLY
		else
		{
			if ( bLongNameOnlyIfExist )
				strName.Format("\"%s\"", strLongName);
			else
				strName.Format("%s\"%s\"", col.GetName(), strLongName);
		}
		///------ End SUPPORT_MAKE_COLUMN_NAME_WITH_LN_ONLY
		//------ End CONSISTENT_COMMENT_DISPLAY_FOR_COLUMN_NAME_IN_STATISTICS_TOOLS
	}
	
	return strName;
}

int range_get_col_type(const DataRange& dr, const int nIndex)
{
	Column col;
	range_get_col(dr, nIndex, col);
	if (col)
	{
		return col.GetInternalData()
	}
	
	return -1;
	
}

///Arvin 11/16/06 GET_FIRST_Y_COL_FROM_RANGE
int get_first_y_col_index(const DataRange& dr)
{
	int r1, c1, r2, c2;
	Worksheet wksTemp;
	string strRange;
	Column col;
	for( int ii = 0; ii < dr.GetNumRanges(); ii++)
	{
		dr.GetRange(ii, r1, c1, r2, c2, wksTemp, &strRange);
		if(c1 == c2 && wksTemp.Columns(c1).GetType() == OKDATAOBJ_DESIGNATION_Y)
			return ii;
	}
	return -1;
}
///end GET_FIRST_Y_COL_FROM_RANGE

///Sandy 2007-7-20 REMOVE_NO_USE
/*
///Sandy 2006-6-7 GET_COL_ID_WITH_DIFFERENT_ID
enum {
	ID_BY_LONGNAME = 0,
	ID_BY_SHORT_NAME,
	ID_BY_COMMENT,
	ID_BY_PARAMETER,
	ID_BY_UNITS,
};

string range_get_col_id(const DataRange& dr, const int nIndex, const int idRule, const int nParamIndex)
{
	int r1, c1, r2, c2;
	Worksheet wksTemp;
	string strRange;
	Column col;
	if (1 == dr.GetNumRanges())
	{
		dr.GetRange(0, r1, c1, r2, c2, wksTemp, &strRange);
		col = wksTemp.Columns(c1 + nIndex)
	}else
	{
		dr.GetRange(nIndex, r1, c1, r2, c2, wksTemp, &strRange);
		col = wksTemp.Columns(c1)
	}
	string strName;
	switch(idRule)
	{
	case ID_BY_LONGNAME:
		strName = col.GetLongName();
		break;
	case ID_BY_SHORT_NAME:
		strName = col.GetName();
		break;
	case ID_BY_COMMENT:
		strName = col.GetComments();
		break;
	case ID_BY_PARAMETER:
		col.GetParameter(strName, nParamIndex);
		break;
	case ID_BY_UNITS:
		strName = col.GetUnits();
		break;
	default:
		break;
	}
	
	return strName;	
}
*/

static void _remove(const vector<uint>& vnIndex, vector<string>& vstrData)
{
	vector<string> vstrTmp;
	int jj = 0;
	for (int ii = 0; ii < vstrData.GetSize(); ii++)
	{
		if (ii != vnIndex[jj])
			vstrTmp.Add(vstrData[ii]);
		else
			jj = (jj < vnIndex.GetSize()-1) ? jj+1 : jj;
			
	}
	vstrData = vstrTmp;
}

bool trim_nanum_1group(vector &vData, vector<string> &vstrGroup, int& nMissing)
{
	if (vData.GetSize() != vstrGroup.GetSize())
		return false;

	vector<uint> vnIndex;
	nMissing = vData.Find(vnIndex, NANUM);
	if (nMissing > 0)
	{
		_remove(vnIndex, vstrGroup);
		vData.Trim();
	}
	
	return true;
	
}

bool trim_nanum_2group(vector &vData, vector<string> &vsGroup1, vector<string> &vsGroup2, int& nMissing)
{
	if (vData.GetSize() != vsGroup1.GetSize() || vsGroup1.GetSize() != vsGroup2.GetSize())
		return false;
	
	vector<uint> vnIndex;
	nMissing = vData.Find(vnIndex, NANUM);
	if (nMissing > 0)
	{
		_remove(vnIndex, vsGroup1);
		_remove(vnIndex, vsGroup2);
		vData.Trim();
	}

	return true;
}

///Eric  06/3/22    
//int trim_matrix(matrix &mat, vector &vCensor, vector<string> &vGroup)
//{
	//vector vTemp;
	//int nTemp, jj = 0, nMissing = 0;
	//
	//for(int ii=0;ii<mat.GetNumRows();ii++)
	//{
	    //mat.GetRow(vTemp,ii);
	    //if(0 < (nTemp = ocmath_count(NANUM, vTemp.GetSize(), vTemp)))
	    //{
	    	//nMissing += nTemp;
	    	//if(vCensor != NULL)
	    	//{
	    		//vCensor.RemoveAt(ii - jj);
	    	//}
	    	//if(vGroup != NULL)
	    	//{
	    		//vGroup.RemoveAt(ii - jj);
	    	//}
	    	//jj++;
	    //}
	//}
	    //
	//mat.RemoveEmptyRows(false);
	//
	//return nMissing;
//}

static int _trim_matrix(matrix& mData, vector<string>& vstrGroup)
{
	vector vTemp;
	int nTemp, jj = 0, nMissing = 0;
	
 	if(vstrGroup)
 	{
		for(int ii=0;ii<mData.GetNumRows();ii++)
		{
		    mData.GetRow(vTemp,ii);
		    if(0 < (nTemp = ocmath_count(NANUM, vTemp.GetSize(), vTemp)))
		    {
		    	nMissing += nTemp;
	    		vstrGroup.RemoveAt(ii - jj);
		    	jj++;
		    }
		}
 	}
 	else
 	{
 		mData.GetAsVector(vTemp);
 		nMissing = 0 < ocmath_count(NANUM, vTemp.GetSize(), vTemp);
 	}
	    
	mData.RemoveEmptyRows(false);
	
	return nMissing;
	
}

int  trim_nanum_sa(int* nMissing, vector &vTime, vector &vCensor, vector<string> &vstrGroup, matrix &mCovariat, const bool bWeibull)
{
	//if(vTime.GetSize() != vCensor.GetSize())
	//{
		//return OE_INVALID_SIZE;
	//}
	//
	//if(vstrGroup != NULL)
	//{
		//if(vTime.GetSize() != vstrGroup.GetSize())
		//{
			//return OE_INVALID_SIZE;
		//}
	//}
	//
	//if(mCovariat != NULL)
	//{
		//if(vTime.GetSize() != mCovariat.GetNumRows())
		//{
			//return OE_INVALID_SIZE;
		//}
	//}
	
	///value in time range that are less than 0 should be excluded
	vTime.Replace(0, NANUM, MATREPL_TEST_LESSTHAN);
	if (bWeibull)
		vTime.Replace(0, NANUM, MATREPL_TEST_EQUAL);
	
	int nTime = vTime.GetSize();
	int nCensor = vCensor.GetSize();
	int nSize = min(nTime, nCensor);
	if (vstrGroup)
	{
		int nGroup = vstrGroup.GetSize(); 
		nSize = min(nGroup, nSize);
	}
	if (mCovariat)
	{
		int nCovar = mCovariat.GetNumRows(); 
		nSize = min(nCovar, nSize);
	}
	
	matrix mTemp;
	
	if(mCovariat)
	{
		mTemp.SetSize(nSize, 2 + mCovariat.GetNumCols());
		mTemp.SetColumn(vTime, 0);
		mTemp.SetColumn(vCensor, 1);
		mTemp.SetSubMatrix(mCovariat, 2);
	}	
	else if(vTime != NULL)
	{
		mTemp.SetSize(nSize, 2);
		mTemp.SetColumn(vTime, 0);
		mTemp.SetColumn(vCensor, 1);
	
	}
	
		
	int ii;
	*nMissing = 0;	
	*nMissing += _trim_matrix(mTemp, vstrGroup);
	
	
    if(mTemp.GetNumRows() == 0)
    {
    	return OE_RANGE_ZERO;
    }
    else
    {
	  	ii = 0;
	  	if( vTime )
		{
	 		mTemp.GetColumn(vTime, ii++);
		}
	
	  	if( vCensor )
		{
	 		mTemp.GetColumn(vCensor, ii++);
		}
		
		if( mCovariat )
		{
	    	mTemp.GetSubMatrix(mCovariat, ii);
		}
	
		return OE_NOERROR;
    }
}



///Echo 4/19/06
static bool _anova2_update_group(vector& vGroup, vector<string>& vstrGroup, const int nSize, LPCSTR lpsczLabel)
{
	//if (vNumInGroup.GetSize() != vstrGroup.GetSize())
		//return false;

	int nGroup = vstrGroup.GetSize();
	int nLevel = 1;
	if (nGroup == 0)
	{
			vstrGroup.Add(lpsczLabel);
	}
	for (int ii=0; ii<nGroup; ii++)
	{
		if (0 == lstrcmp(lpsczLabel, vstrGroup[ii]))
		{
			nLevel = ii+1;
			break;
		}else if (ii == nGroup-1)
		{
			vstrGroup.Add(lpsczLabel);
			nLevel = nGroup+1;
		}
	}
	
	vector vSubGroup(nSize);
	vSubGroup = nLevel;
	vGroup.Append(vSubGroup);

	return true;
}

int get_anova2_data(const Range& rInGroup, vector& vData, vector& vGroup1, vector& vGroup2, vector<string> &vstrGroup1, vector<string> &vstrGroup2)
{
	DWORD dwRules = DRR_NO_WEIGHTS;
	int nNumData = rInGroup.GetNumData(dwRules);
	if (1 > nNumData)
		return CER_NOT_INDEX_DATA;
	
	for ( int ii=0; ii< nNumData; ii++ )
	{
		vector vTmpData, vW;
		vector<string> vstrFactor;
		rInGroup.GetData(dwRules, ii, NULL, NULL, &vTmpData, NULL, NULL, &vstrFactor, &vW);
		if (vstrFactor.GetSize() != 2)
			return CER_NOT_INDEX_DATA;
		
		int nSize = vTmpData.GetSize();
		if ( nSize == 0)
			continue;
		vData.Append(vTmpData);
		if (!_anova2_update_group(vGroup1, vstrGroup1, nSize, vstrFactor[0]) || !_anova2_update_group(vGroup2, vstrGroup2, nSize, vstrFactor[1]))
			return CER_NOT_INDEX_DATA
	}
	if ( vData.GetSize() == 0 )
		return CER_NO_DATA;
}

int get_data_in_group(const Range& rInGroup, vector& vData, vector& vNumInGroup, vector<string> &vstrLab)
{
	DWORD dwRules = DRR_NO_WEIGHTS;
	int nNumData = rInGroup.GetNumData(dwRules);
	if (1 > nNumData)
		return CER_NOT_INDEX_DATA;

	for ( int ii=0; ii< nNumData; ii++ )
	{
		vector vTmpData, vW;
		vector<string> vstrFactor;
		rInGroup.GetData(dwRules, ii, NULL, NULL, &vTmpData, NULL, NULL, &vstrFactor, &vW);
		int nSize = vTmpData.GetSize();
		if ( nSize == 0)
			break;
		vData.Append(vTmpData);
		vNumInGroup.Add(nSize);
		if (vstrLab)	vstrLab.Add(vstrFactor[0]);
	}
	if ( vData.GetSize() == 0 )
		return CER_NO_DATA;
	
	if (2 > nNumData)
		return CER_GROUP_NUM_LS_2;
		
	return CER_NO_ERROR;	
}

int get_2_data_in_group(const Range& rInGroup, vector& vData1, vector& vData2, vector<string>& vstrLab, int& nMissing, const bool bPair)
{
	DWORD dwRules = DRR_NO_WEIGHTS;
	int nNumData = rInGroup.GetNumData(dwRules);
	if (1 > nNumData)
		return CER_NOT_INDEX_DATA;

	if (2 != nNumData)
		return CER_GROUP_NUM_NOT_TWO;
	
	vector vData, vW;
	vector<string> vstrFactor;
	rInGroup.GetData(dwRules, 0, NULL, NULL, &vData1, NULL, NULL, &vstrFactor, &vW);
	if (vstrLab)	vstrLab.Add(vstrFactor[0]);
	rInGroup.GetData(dwRules, 1, NULL, NULL, &vData2, NULL, NULL, &vstrFactor, &vW);
	if (vstrLab)	vstrLab.Add(vstrFactor[0]);
	if (vData1.GetSize() == 0 || vData2.GetSize() == 0)
		return CER_NO_DATA;
	
	return CER_NO_ERROR;
	
}



int get_2_data_in_var(const Range& rInVar, vector& vData1, vector& vData2, vector<string>& vstrLab, int& nMissing, const bool bPair)
{
	int nRange = rInVar.GetNumRanges();
	if (1 > nRange || nRange > 2)
		return CER_NOT_RAW_DATA;
	
	DWORD dwRules = DRR_NO_WEIGHTS | DRR_NO_FACTORS	;
	DWORD dwPlotID;

	rInVar.GetData(dwRules, 0, &dwPlotID, NULL, &vData1);
	rInVar.GetData(dwRules, 1, &dwPlotID, NULL, &vData2);

	//rInVar.GetData(vData1, 0);
	//rInVar.GetData(vData2, 1);
	
	int nSize1 = vData1.GetSize();
	int nSize2 = vData2.GetSize();
	if (nSize1 == 0 || nSize2 == 0)
		return CER_NO_DATA;
	
	if ( bPair && nSize1 != nSize2)
		return CER_DATA_DIFF_SIZE;

	nMissing = ocmath_count(NANUM, nSize1, vData1) + ocmath_count(NANUM, nSize2, vData2);
	if (!bPair)
	{
		if (0 < nMissing)
		{
			vData1.Trim();
			vData2.Trim();
		}
	}else
	{
		matrix mData(min(nSize1, nSize2),2);
		
		mData.SetColumn( vData1, 0);
		mData.SetColumn( vData2, 1);
	
		mData.RemoveEmptyRows(false);
		
		mData.GetColumn( vData1, 0);
		mData.GetColumn( vData2, 1);
	}
	
	if (vstrLab != NULL)
	{
		vstrLab.SetSize(2);
		for (int ii = 0; ii < 2; ii++)
			///------ Folger 12/26/08 QA80-12642 v8.0991 CONSISTENT_SHOW_LN_ONLY_IF_EXIST_FOR_STATISTICAL_TOOLS
			//vstrLab[ii] = range_get_col_name(rInVar, ii);
			vstrLab[ii] = range_get_col_name(rInVar, ii, TRUE);
			///------ End CONSISTENT_SHOW_LN_ONLY_IF_EXIST_FOR_STATISTICAL_TOOLS
	}
	
	return CER_NO_ERROR;
}


bool error_msg_box(LPCSTR lpcszErrMsg)
{
	
	MessageBox(NULL, lpcszErrMsg, ERROR_TITLE, MB_OK | MB_ICONSTOP);		
	return false;
}

///---Sim 08-22-2006 ERR_MSG_SUPPORT_PARAM
static bool _load_error_msg_str(int nErrCode, string &str)
{
	if(!ocu_load_err_msg_str(nErrCode, &str))
	{
		str.Format("Error code %d not defined, please report this problem.", nErrCode);
		return false;
	}
	return true;
}
///---END ERR_MSG_SUPPORT_PARAM

bool error_msg_box(int nErrCode)
{
	string str;
	///---Sim 08-22-2006 ERR_MSG_SUPPORT_PARAM
	/*
	if(!ocu_load_err_msg_str(nErrCode, &str))
		str.Format("Error code %d not defined, please report this problem.", nErrCode);
	*/
	_load_error_msg_str(nErrCode, str);
	///---END ERR_MSG_SUPPORT_PARAM
		
	return error_msg_box(str);	
}

///---Sim 08-22-2006 ERR_MSG_SUPPORT_PARAM
bool error_msg_box(int nErrCode, int nParam)
{
	string str;
	if ( _load_error_msg_str(nErrCode, str) )
	{
		string strTemp = str;
		str.Format(strTemp, nParam);
	}
	
	return error_msg_box(str);	
}
bool error_msg_box(int nErrCode, LPCSTR lpcszParam)
{
	string str;
	if ( _load_error_msg_str(nErrCode, str) )
	{
		string strTemp = str;
		str.Format(strTemp, lpcszParam);
	}
	
	return error_msg_box(str);	
}
bool error_msg_box(int nErrCode, int nParam, LPCSTR lpcszParam)
{
	string str;
	if ( _load_error_msg_str(nErrCode, str) )
	{
		string strTemp = str;
		str.Format(strTemp, nParam, lpcszParam);
	}
	
	return error_msg_box(str);	
}
bool error_msg_box(int nErrCode, LPCSTR lpcszParam, int nParam)
{
	string str;
	if ( _load_error_msg_str(nErrCode, str) )
	{
		string strTemp = str;
		str.Format(strTemp, lpcszParam, nParam);
	}
		
	return error_msg_box(str);	
}
///---END ERR_WARNING_MSG_SUPPORT_PARAM

///---Sim 08-23-2006 WARNING_MSG_BOX
#define SETTING_WARN_STR_PARAM(_FORMAT_, _Param) \
		int nIndex = strErrMsg.Find(_FORMAT_); \
		if ( nIndex >= 0 ) \
		{ \
			string str; \
			str.Format(_FORMAT_, _Param); \
			strErrMsg.Delete(nIndex, strlen(_FORMAT_)); \
			strErrMsg.Insert(nIndex, str); \
		}

///---Sim 06-19-2007 IMPROVE_WARNING_MESSAGE_POST_FUNCTION
//int show_warning_msg(int nErrCode, LPCSTR lpcszErrMsg, int nParam, LPCSTR lpcszParam, bool bOKButton, bool bCancelButton, bool bIcon)
int show_warning_msg(int nErrCode, LPCSTR lpcszErrMsg, int nParam, LPCSTR lpcszParam, bool bShowBox, char cMsgType, UINT uType) // = 'W', MB_OK
{
	//Load message
	string strErrMsg = "";
	if ( nErrCode < 0 )
	{
		if ( NULL != lpcszErrMsg )
			strErrMsg = lpcszErrMsg;
		//else
			//strErrMsg = "Warning message string is NULL.";
	}
	else
		_load_error_msg_str(nErrCode, strErrMsg);
	
	//Setting message parameter
	
	if ( WARN_NPARAM_UNUSED != nParam )
	{
		SETTING_WARN_STR_PARAM("%d", nParam);
	}
	if ( NULL != lpcszParam )
	{
		SETTING_WARN_STR_PARAM("%s", lpcszParam);
	}
	
	
	//Open message box
	//if ( bOKButton || bCancelButton)
	if ( bShowBox )
	{
		//UINT uType = ( bOKButton ? MB_OK : 0 ) | ( bCancelButton ? MB_OKCANCEL : 0 )  \
					| ( bIcon ? MB_ICONSTOP : 0 ) ;
		/// ML 7/31/2007 QA70-10137 MUST_SUPPLY_PARENT_FOR_MESSAGEBOX
		//return MessageBox(NULL, strErrMsg, _L(ERROR_TITLE), uType);		
		return MessageBox(GetWindow(), strErrMsg, ERROR_TITLE, uType);		
		/// end MUST_SUPPLY_PARENT_FOR_MESSAGEBOX
	}
	else
	{	// no dialog
		//LT_execute("ty -aa");
		//out_str(strErrMsg);
		okoc_out_msg(strErrMsg, cMsgType);
		return 0; // Not defined return value yet.
	}
}

/*
int warning_msg_box(LPCSTR lpcszErrMsg, bool bShowBox, bool bCancelButton, bool bIcon)// = FALSE , TRUE
{
	return show_warning_msg(-1, lpcszErrMsg, WARN_NPARAM_UNUSED, NULL, bShowBox, bCancelButton, bIcon);
}

int warning_msg_box(LPCSTR lpcszErrMsg, int nParam, bool bShowBox, bool bCancelButton, bool bIcon)// = FALSE , TRUE
{
	return show_warning_msg(-1, lpcszErrMsg, nParam, NULL, bShowBox, bCancelButton, bIcon);
}

int warning_msg_box(int nErrCode, bool bShowBox, bool bCancelButton, bool bIcon)// = FALSE , TRUE
{
	return show_warning_msg(nErrCode, NULL, WARN_NPARAM_UNUSED, NULL, bShowBox, bCancelButton, bIcon);
}

int warning_msg_box(int nErrCode, int nParam, bool bShowBox, bool bCancelButton, bool bIcon)// = FALSE , TRUE
{
	return show_warning_msg(nErrCode, NULL, nParam, NULL, bShowBox, bCancelButton, bIcon);
}

int warning_msg_box(int nErrCode, LPCSTR lpcszParam, bool bShowBox, bool bCancelButton, bool bIcon)// = FALSE , TRUE
{
	return show_warning_msg(nErrCode, NULL, WARN_NPARAM_UNUSED, lpcszParam, bShowBox, bCancelButton, bIcon);
}

int warning_msg_box(int nErrCode, int nParam, LPCSTR lpcszParam, bool bShowBox, bool bCancelButton, bool bIcon)// = FALSE , TRUE
{
	return show_warning_msg(nErrCode, NULL, nParam, lpcszParam, bShowBox, bCancelButton, bIcon);
}

int warning_msg_box(int nErrCode, LPCSTR lpcszParam, int nParam, bool bShowBox, bool bCancelButton, bool bIcon)// = FALSE , TRUE
{
	return show_warning_msg(nErrCode, NULL, nParam, lpcszParam, bShowBox, bCancelButton, bIcon);
}
*/
int warning_msg_box(LPCSTR lpcszErrMsg, bool bShowBox, char cMsgType, UINT uType) // = 'W', MB_OK
{
	return show_warning_msg(-1, lpcszErrMsg, WARN_NPARAM_UNUSED, NULL, bShowBox, cMsgType, uType);
}

int warning_msg_box(LPCSTR lpcszErrMsg, int nParam, bool bShowBox, char cMsgType, UINT uType) // = 'W', MB_OK
{
	return show_warning_msg(-1, lpcszErrMsg, nParam, NULL, bShowBox, cMsgType, uType);
}

int warning_msg_box(int nErrCode, bool bShowBox, char cMsgType, UINT uType) // = 'W', MB_OK
{
	return show_warning_msg(nErrCode, NULL, WARN_NPARAM_UNUSED, NULL, bShowBox, cMsgType, uType);
}

int warning_msg_box(int nErrCode, int nParam, bool bShowBox, char cMsgType, UINT uType) // = 'W', MB_OK
{
	return show_warning_msg(nErrCode, NULL, nParam, NULL, bShowBox, cMsgType, uType);
}

int warning_msg_box(int nErrCode, LPCSTR lpcszParam, bool bShowBox, char cMsgType, UINT uType) // = 'W', MB_OK
{
	return show_warning_msg(nErrCode, NULL, WARN_NPARAM_UNUSED, lpcszParam, bShowBox, cMsgType, uType);
}

int warning_msg_box(int nErrCode, int nParam, LPCSTR lpcszParam, bool bShowBox, char cMsgType, UINT uType) // = 'W', MB_OK
{
	return show_warning_msg(nErrCode, NULL, nParam, lpcszParam, bShowBox, cMsgType, uType);
}

int warning_msg_box(int nErrCode, LPCSTR lpcszParam, int nParam, bool bShowBox, char cMsgType, UINT uType) // = 'W', MB_OK
{
	return show_warning_msg(nErrCode, NULL, nParam, lpcszParam, bShowBox, cMsgType, uType);
}
///---END IMPROVE_WARNING_MESSAGE_POST_FUNCTION
///---END WARNING_MSG_BOX

/// Iris 8/16/06 NEED_SPECIAL_ROW_COL_HEADER_FOR_MATRIX_TABLE
bool	str_data_list(vector<string>& vsList, int nMax, int nMin, int nInc)
{
	if(nInc <= 0 || nMax < nMin)
		return false;
	
	vsList.RemoveAll();
	
	for(int index = 0, nData = nMin; nData <= nMax; index++, nData+=nInc)
	{
		vsList.Add((string)nData);
	}	
	return true;
}
///end NEED_SPECIAL_ROW_COL_HEADER_FOR_MATRIX_TABLE

///Arvin 9/01/06 ADD_WKS_IS_ALL_COLS_DOUBLE
bool  	wks_is_all_cols_double(const Worksheet& wks)
{
	if(!wks.IsValid())
		return false;
	foreach(Column col in wks.Columns)
	{
		int internalType = col.GetInternalData();
		if(internalType != FSI_DOUBLE)
			return false;
	}
	return true;
}
///end ADD_WKS_IS_ALL_COLS_DOUBLE

///Arvin 9/28/06 ADD_WKS_HAS_TEXT_COL
bool  	wks_has_text_col(const Worksheet& wks)
{
	if(!wks.IsValid())
		return false;
	
	foreach(Column col in wks.Columns)
	{
		int internalType = col.GetInternalData();
		if(internalType == FSI_TEXT)
			return true;
	}
	return false;
}
///end ADD_WKS_HAS_TEXT_COL
///Arvin 9/28/06 ADD_WKS_HAS_COMPLEX_COL
bool  	wks_has_complex_col(const Worksheet& wks)
{
	if(!wks.IsValid())
		return false;
	
	foreach(Column col in wks.Columns)
	{
		int internalType = col.GetInternalData();
		if(internalType == FSI_COMPLEX)
			return true;
	}
	return false;
}
///end ADD_WKS_HAS_COMPLEX_COL
///Joseph  10/9/06	SEPERATE_DESCIPTION_OR_COMBINE_DESCRIPTION_SECTION
void	xf_seperate_description(LPCSTR lpcszDesp, string& strMenu, string& strBriefDesp)
{
	string strDescription(lpcszDesp);	
	int nIndex;
	if((nIndex = strDescription.Find(":"))<0)
	{
		strBriefDesp = strDescription;
		return;
	}
	
	strMenu = strDescription.Left(nIndex);
	strBriefDesp = strDescription.Right(strDescription.GetLength() - nIndex - 1);
}

void	xf_combine_description_section(LPCSTR lpcszMenu, LPCSTR lpcszBriefDesp, string& strDescriptoin)
{
	string strMenu(lpcszMenu);
	string strBrief(lpcszBriefDesp);	
	strDescriptoin = strMenu + ":" + strBrief;	
}

///End SEPERATE_DESCIPTION_OR_COMBINE_DESCRIPTION_SECTION


///---Sim 12-05-2007 REWRITE_WKS_DATA_RANGE_TO_MAT
/*
///Cheney 2006-10-11 GET_DATA_FROM_DR_TO_MAT
static void _get_right_pos_to_set_sub_mat(int& nPos, int nIndex, vector<int>& vLower, vector<int>& vUpper)
{
	int nSize = vLower.GetSize();
	for(int ii = 0; ii < nSize; ii++)
	{
		if(nIndex <= vUpper[ii])
		{
			nPos += nIndex - vLower[ii];
			break;
		}
		
		nPos += vUpper[ii] - vLower[ii] + 1;
	}
}

static void _get_matrix_bound(int& nBounds, vector<int>& vLower, vector<int>& vUpper)
{
	int nSize = vLower.GetSize();
	for(int ii = 0; ii < nSize; ii++)
	{
		nBounds += vUpper[ii] - vLower[ii] + 1;
	}
}
*/
///---END REWRITE_WKS_DATA_RANGE_TO_MAT
bool is_column_in_bounds(int nCol, vector<int>& vColLower, vector<int>& vColUpper)
{
	if(nCol < 0)
		return false;
	
	int nSize = vColLower.GetSize();
	for(int ii = 0; ii < nSize; ii++) 
	{
		if(nCol >= vColLower[ii] && nCol <= vColUpper[ii])
			return true;
	}
	return false;
}
///---Sim 12-05-2007 REWRITE_WKS_DATA_RANGE_TO_MAT
/*
bool get_data_from_dr_to_mat(const DataRange& dr, matrixbase& mat)
{
	int nData = dr.GetNumData();
	if(nData < 0)
		return false;
	
	//get source worksheet
	Worksheet wks;
	int r1, r2, c1, c2;
	if(!dr.GetRange(0, r1, c1, r2, c2, wks))
		return false;
	
	//get wks' row and col number
	c1 = 0;
	c2 = wks.GetNumCols() - 1;
	wks.GetRange(r1, r2, c1, c2); 
	
	///Cheney 2006-11-6 USE_DR_GETRANGE_BUT_NOT_WKS, because when user changed the selection in worksheet 
	//with interactive button,  GetSelectedRange will change, so it is not reliable.
	////get bounding, avoid to make a large matrix
	//vector<int> vr1, vc1, vr2, vc2;
	//int nRanges = wks.GetSelectedRange(vr1, vc1, vr2, vc2);
	int nRanges = dr.GetNumRanges();
	vector<int> vr1, vc1, vr2, vc2;
	for(int ii = 0; ii < nRanges; ii++)
	{
		int r1Temp, r2Temp, c1Temp, c2Temp;
		if(!dr.GetRange(ii, r1Temp, c1Temp, r2Temp, c2Temp, wks))
			return false;
		
		vr1.Add(r1Temp);
		vr2.Add(r2Temp);
		vc1.Add(c1Temp);
		vc2.Add(c2Temp);
	}
	///end USE_DR_GETRANGE_BUT_NOT_WKS
	
	vector<int> vrLower, vrUpper;
	if(!get_select_range_bounds(vr1, vr2, r2, vrLower, vrUpper))
		return false;
	
	vector<int> vcLower, vcUpper;
	if(!get_select_range_bounds(vc1, vc2, c2, vcLower, vcUpper))
		return false;
	
	//notes:vcLower's size should equal to vcUpper's, but vcLower's size may be not equal to vrLower's
	int nMatRows = 0, nMatCols = 0;
	_get_matrix_bound(nMatRows, vrLower, vrUpper);
	_get_matrix_bound(nMatCols, vcLower, vcUpper);
	mat.SetSize(nMatRows, nMatCols);
	mat = NANUM;
	
	//set matrix data
	//for(ii = 0; ii < nData; ii++)///Echo 7/31/07 QA70-8679 P11 R2M_AUTO_UPDATE_NOT_SUPPORT_SELETED_RANGE
	for(ii = 0; ii < nRanges; ii++)
	{
		if(!dr.GetRange(ii, r1, c1, r2, c2, wks))
			return false;

		///Echo 7/31/07 QA70-8679 P11 R2M_AUTO_UPDATE_NOT_SUPPORT_SELETED_RANGE
		matrix mTemp;
		dr.GetData(mTemp, 0, ii);
		
		//if(!mTemp.CopyFromWks(wks, c1, c2, r1, r2))
			//return false;
		///END 	R2M_AUTO_UPDATE_NOT_SUPPORT_SELETED_RANGE
		
		//get right position to set sub matrix
		int nSubMatColBegin = 0, nSubMatRowBegin = 0;
		_get_right_pos_to_set_sub_mat(nSubMatColBegin, c1, vcLower, vcUpper);
		_get_right_pos_to_set_sub_mat(nSubMatRowBegin, r1, vrLower, vrUpper);

		mat.SetSubMatrix(mTemp, nSubMatColBegin, nSubMatRowBegin);
	}
	
	return true;
}
*/
/// Iris 3/16/2010 QA81-10824 TRIM_UNUSEFUL_MISSING_DATA_IN_OUTPUT_COLUMNS
//bool get_data_from_dr_to_mat(const DataRange& dr, matrixbase& mat, bool bRemoveMissingRows, bool bRemoveMissingCols)//= true, true
bool get_data_from_dr_to_mat(const DataRange& dr, matrixbase& mat, int nRemoveMissingRows, bool bRemoveMissingCols)//= MATRIX_FROM_RANGE_REMOVE_ALL_MISSING_ROWS, true
///End TRIM_UNUSEFUL_MISSING_DATA_IN_OUTPUT_COLUMNS
{
	int nData = dr.GetNumData();
	if(nData < 0)
		return false;
	
	Worksheet wks;
	//int r1, r2, c1, c2; ///---Sim 01-17-2007 QA80-10952 MULTI_SHEET_RANGE remove
	///---Sim 04-28-2008 FIX_INVOKE_WKS_GET_BOUND
	//int nBoundR1, nBoundR2, nBoundC1, nBoundC2;	///---Sim 12-20-2007 QA80-10842 FIX_NOT_USE_ALL_ROWS
	int nBoundR1, nBoundR2;
	///---END FIX_INVOKE_WKS_GET_BOUND
	int ii;
	
	///---Sim 01-17-2007 QA80-10952 MULTI_SHEET_RANGE
	/*
	// sort sub ranges by worksheet
	vector<uint> uIDs;
	for ( ii = 0; ii < dr.GetNumRanges(); ii++ )
	{
		if(!dr.GetRange(ii, r1, c1, r2, c2, wks)) // only wks used
			return false;
		///Arvin 12/13/07 USE_WRONG_FUNCTION_TO_GET_DATA_IN_STATS_ON_ROWS as Echo said
		//need check wks valid first
		//uIDs.Add(wks.GetUID());
		if(wks)
			uIDs.Add(wks.GetUID());
		///end USE_WRONG_FUNCTION_TO_GET_DATA_IN_STATS_ON_ROWS
	}
	// uIDs will be sorted, but dr will not. vnIndeces is such index vector for get unsorted sub ranges info.
	vector<uint> vnIndeces;
	if ( uIDs.GetSize() > 1 )
	{
		if(!uIDs.Sort(SORT_ASCENDING, true, vnIndeces))
			return false;
	}
	else
	{
		// size is 1, will failed to sort.
		vnIndeces.Add(0);
	}
	
	// calculate total size of all sub ranges
	int nMatRows = 0, nMatCols = 0;
	for ( ii = 0; ii < uIDs.GetSize(); ii++ )
	{
		if ( ii == uIDs.GetSize() - 1 || uIDs[ii] != uIDs[ii+1] ) // next worksheet is different
		{
			if(!dr.GetRange(vnIndeces[ii], r1, c1, r2, c2, wks)) // only wks used
				return false;
		
			///---Sim 12-20-2007 QA80-10842 FIX_NOT_USE_ALL_ROWS
			//if ( wks.GetNumRows() > nMatRows )
				//nMatRows = wks.GetNumRows();
			if ( wks.GetBounds(nBoundR1, nBoundC1, nBoundR2, nBoundC2) )
			{
				if ( nBoundR2 + 1 > nMatRows )
					nMatRows = nBoundR2 + 1;
			}
			///---END QA80-10842 FIX_NOT_USE_ALL_ROWS
			
			nMatCols += wks.GetNumCols();
		}
	}
	
	mat.SetSize(nMatRows, nMatCols);
	mat = NANUM;
	
	// fill data of sub range to matrix
	int nColOffset = 0;
	for ( ii = 0; ii < uIDs.GetSize(); ii++ )
	{
		if(!dr.GetRange(vnIndeces[ii], r1, c1, r2, c2, wks))
			return false;
	
		matrix matSub;
		dr.GetData(matSub, 0, vnIndeces[ii]);
		
		int nSubMatColBegin = c1 + nColOffset; // multi sheet data would be put horizontally by cols
		int nSubMatRowBegin = r1;
		
		mat.SetSubMatrix(matSub, nSubMatColBegin, nSubMatRowBegin);
		
		if ( ii == uIDs.GetSize() - 1 || uIDs[ii] != uIDs[ii+1] ) // next worksheet is different
			nColOffset += wks.GetNumCols();
	}
	*/
	int nWksIndex;
	int nWksSubRangeIndex;
	SheetRangesList sdl(dr);
	
	// calculate total size of all sub ranges
	int nMatRows = 0, nMatCols = 0;
	for ( nWksIndex = 0; nWksIndex < sdl.GetNumSheets(); nWksIndex++ )
	{
		if ( sdl.GetRange(nWksIndex, wks) )
		{
			if ( wks )
			{
				if( MATRIX_FROM_RANGE_REMOVE_MISSING_FROM_LAST_SEL_ROW != nRemoveMissingRows ) /// Iris 2/24/2010 QA81-10824 TRIM_UNUSEFUL_MISSING_DATA_IN_OUTPUT_COLUMNS
				{
					///---Sim 04-28-2008 FIX_INVOKE_WKS_GET_BOUND
					//if ( wks.GetBounds(nBoundR1, nBoundC1, nBoundR2, nBoundC2) )
					if ( wks.GetBounds(nBoundR1, 0, nBoundR2, wks.GetNumCols()-1) )
					///---END FIX_INVOKE_WKS_GET_BOUND
					{
						if ( nBoundR2 + 1 > nMatRows )
							nMatRows = nBoundR2 + 1;
					}
				}				
				///Kyle 01/05/2009 FIX_RANGE_NOT_ALL_COLUMN_SELECTED
				//nMatCols += wks.GetNumCols();
				Datasheet ds; // temp variable, not used
				ORANGE rg;
				for ( nWksSubRangeIndex = 0; nWksSubRangeIndex < sdl.GetNumSubRange(nWksIndex); nWksSubRangeIndex++ )
				{
					if( !sdl.GetRange(nWksIndex, ds, nWksSubRangeIndex, &rg) )
						continue;
					int c2 = rg.c2 >= 0 ? rg.c2 : wks.GetNumCols()-1;
					nMatCols += (c2 - rg.c1 + 1); 
					
					/// Iris 2/24/2010 QA81-10824 TRIM_UNUSEFUL_MISSING_DATA_IN_OUTPUT_COLUMNS
					if( MATRIX_FROM_RANGE_REMOVE_MISSING_FROM_LAST_SEL_ROW == nRemoveMissingRows )
					{
						int r2 = rg.r2 >= 0 ? rg.r2 : wks.GetNumRows()-1;
						
						/// Iris 3/23/2010 QA81-10824-P1 TRIM_UNUSEFUL_MISSING_DATA_IN_OUTPUT_COLUMNS_MORE
						wks.GetBounds(nBoundR1, rg.c1, nBoundR2, c2);
						r2 = nBoundR2 < r2 ? nBoundR2 : r2;
						///End TRIM_UNUSEFUL_MISSING_DATA_IN_OUTPUT_COLUMNS_MORE
						
						if( r2 + 1 > nMatRows )
							nMatRows = r2 + 1;
					}
					///End TRIM_UNUSEFUL_MISSING_DATA_IN_OUTPUT_COLUMNS
				}
				///End FIX_RANGE_NOT_ALL_COLUMN_SELECTED
			}
		}
	}	
		
	mat.SetSize(nMatRows, nMatCols);
	mat = NANUM;
	
	// fill data of sub range to matrix
	int nColOffset = 0;
	for ( nWksIndex = 0; nWksIndex < sdl.GetNumSheets(); nWksIndex++ )
	{
		if ( sdl.GetRange(nWksIndex, wks) )
		{
			if ( wks )
			{
				Datasheet ds; // temp variable, not used
				ORANGE rg;
				for ( nWksSubRangeIndex = 0; nWksSubRangeIndex < sdl.GetNumSubRange(nWksIndex); nWksSubRangeIndex++ )
				{
					if( !sdl.GetRange(nWksIndex, ds, nWksSubRangeIndex, &rg) )
						return false;
				
					matrix matSub;
					dr.GetData(matSub, 0, sdl.GetSubRangeIndex(nWksIndex, nWksSubRangeIndex));
					///Kyle 01/05/2009 FIX_RANGE_NOT_ALL_COLUMN_SELECTED
					//int nSubMatColBegin = rg.c1 + nColOffset; // multi sheet data would be put horizontally by cols
					int nSubMatColBegin = nColOffset;
					nColOffset += matSub.GetNumCols();
					///End FIX_RANGE_NOT_ALL_COLUMN_SELECTED
					int nSubMatRowBegin = rg.r1;
					
					mat.SetSubMatrix(matSub, nSubMatColBegin, nSubMatRowBegin);
				}
				///Kyle 01/05/2009 FIX_RANGE_NOT_ALL_COLUMN_SELECTED
				//nColOffset += wks.GetNumCols();
				///End FIX_RANGE_NOT_ALL_COLUMN_SELECTED
			}
		}		
	}
	///---END QA80-10952 MULTI_SHEET_RANGE
	
	// remove missing rows
	/// Iris 3/16/2010 QA81-10824 TRIM_UNUSEFUL_MISSING_DATA_IN_OUTPUT_COLUMNS
	//if ( bRemoveMissingRows )
	if( MATRIX_FROM_RANGE_REMOVE_ALL_MISSING_ROWS == nRemoveMissingRows )
	///End TRIM_UNUSEFUL_MISSING_DATA_IN_OUTPUT_COLUMNS
		mat.RemoveEmptyRows();
	
	// remove missing cols
	if ( bRemoveMissingCols )
	{
		mat.Transpose();
		mat.RemoveEmptyRows();
		mat.Transpose();
	}
	
	return true;	
}
///---END REWRITE_WKS_DATA_RANGE_TO_MAT

bool get_select_range_bounds(vector<int>& v1, vector<int>& v2, uint nBound, vector<int>& vLower, vector<int>& vUpper)
{
	int nSize = v1.GetSize();
	if(nSize != v2.GetSize())
		return false;
	
	if(nSize == 0) // no range selected
		return false;
	
	for(int ii = 0; ii < nSize; ii++)
		v2[ii] = v2[ii] == -1 ? nBound : v2[ii];
	
	if(nSize > 1)
	{
		vector<uint> vnIndeces;
		if(!v1.Sort(SORT_ASCENDING, true, vnIndeces))
			return false;
		
		if(!v2.Reorder(vnIndeces))
			return false;
	}
	
	vLower.Add(v1[0]);
	vUpper.Add(v2[0]);
	int nIndex = 0;
	for(ii = 1; ii < nSize; ii++)
	{
		if(vLower.GetSize() == 1 && vLower[0] == 0 && vUpper[0] == nBound) // no need to loop, from 1st to last.
			break;
			
		if(v1[ii] > vUpper[nIndex] + 1)
		{
			vLower.Add(v1[ii]);
			vUpper.Add(v2[ii]);
			nIndex++;
			continue;
		}
		
		if(v1[ii] == vUpper[nIndex] + 1)
		{
			vUpper[nIndex] = v2[ii];
			continue;
		}
		
		if(v1[ii] <= vUpper[nIndex])
		{
			if(v2[ii] > vUpper[nIndex])
				vUpper[nIndex] = v2[ii];
		}
	}
	
	return true;
}
///end GET_DATA_FROM_DR_TO_MAT

/// YuI 12/07/04 v7.5172 PICTURE_IN_REPORT_IMPROVEMENT
// this function will not create node if it is not there
TreeNode	get_enumerated_node(TreeNode &trParent, LPCTSTR lpcszPrefix, int index)
{
	string			strTag;
	strTag.Format("%s%d", lpcszPrefix, index);
	return trParent.GetNode(strTag);
}
/// end PICTURE_IN_REPORT_IMPROVEMENT

TreeNode	check_add_enumerated_node(TreeNode &trParent, LPCTSTR lpcszPrefix, int index, int nID, LPCSTR lpcszAttrib, LPCSTR lpcszAttribVal)//=-1,NULL,NULL
{
	/// Iris 8/31/2009 QA80-14209 IMPROVE_CREATE_MASKED_MISSING_DATA_TABLE_SPEED
	/*
	string			strTag;
	strTag.Format("%s%d", lpcszPrefix, index);
	TreeNode	tr = tree_check_get_node(trParent, strTag, nID, lpcszAttrib, lpcszAttribVal);
	return tr;
	*/
	TreeNode tr;
	octree_check_add_enumerated_node(&tr, &trParent, lpcszPrefix, index, nID, lpcszAttrib, lpcszAttribVal);
	return tr;
	///end IMPROVE_CREATE_MASKED_MISSING_DATA_TABLE_SPEED
}
/// end REPORT_TABLE_MULTIHEADERS_MORE_WORK

//--- Iris 10/17/06 moved from wksOperatio.c
int		make_one_set_ID(int idBase, int nDataIndex)
{
	/// Iris 8/31/2009 QA80-14209 IMPROVE_CREATE_MASKED_MISSING_DATA_TABLE_SPEED
	/*
	//++nDataIndex;
	int			nId = nDataIndex << 16;
	idBase &= 0xffff;
	nId |= idBase;
	nId |= IDST_MASK_ONE_SET;
	return nId;
	*/
	return octree_make_one_set_ID(idBase, nDataIndex);
	///end IMPROVE_CREATE_MASKED_MISSING_DATA_TABLE_SPEED
}
//--- 

///Arvin 10/17/06 ADD_IS_PERCENTILE_VALUE
// use dPercent = 0 and provide pPercentiles to get list
int is_percentile_value(double dPercent, vector* pPercentiles) // =NULL
{
	static vector l_vPercents = {1, 5, 10, 25, 50, 75, 90, 95, 99};
	if(pPercentiles)	
		*pPercentiles = l_vPercents;
	
	for( int ii = 0; ii < l_vPercents.GetSize(); ii++ )
	{
		if( dPercent == l_vPercents[ii] )
		{
			return ii;
		}
	}

	return -1;
}
///end ADD_IS_PERCENTILE_VALUE

///Arvin 10/19/07 USER_SETTED_PERCENTILES_SHOULD_NOT_EFFECT_HISTOGRAM_AND_BOX_CHART_GRAPHS as max said
int trim_custom_percentiles(const vector vPercents, vector& vPercentiles)
{
	int nPercents = vPercents.GetSize();
	int nPercentiles = vPercentiles.GetSize();
	if(nPercentiles != nPercents)
		return -1;
	
	int nTrimedPerc = 0;
	for(int ii = nPercents-1; ii > -1; ii--)
	{
		int nPerc = is_percentile_value(vPercents[ii]);
		if(ii < vPercentiles.GetSize() && nPerc < 0)
		{
			vPercentiles.RemoveAt(ii);	
			nTrimedPerc++;
		}
	}
	
	return nTrimedPerc;
}
///end USER_SETTED_PERCENTILES_SHOULD_NOT_EFFECT_HISTOGRAM_AND_BOX_CHART_GRAPHS

/// Iris 11/13/06 DO_TWO_TIMES_SEP_REPORT_IN_NLFIT_AND_OPBASE
//GetSubVector not support get sub string vector now, see #7026
bool get_sub_string_vector(const vector<string>& vs, vector<string>& vsDest, int nStart, int nEnd)
{
	int nSize = vs.GetSize();
	if(-1 == nEnd)
		nEnd = nSize - 1;
	if(nStart >= nSize || nEnd >= nSize)
		return false;
	
	vsDest.RemoveAll();
	for(int ii=nStart; ii<=nEnd; ii++)
	{
		vsDest.Add(vs[ii]);
	}
	return true;	
}
///end DO_TWO_TIMES_SEP_REPORT_IN_NLFIT_AND_OPBASE

///---Sim 12-08-2006 GET_LARGEST_GAP
int find_largest_gap(const vector& vValue)
{
	int nSize = vValue.GetSize();
	
	if ( 2 > nSize )
		return -1;
	else if ( 2 == nSize )
		return 0;		
	
	vector vr;
	if ( !vValue.Difference(vr) )
		return -1;
	
	vector<uint> vnIndeces;
	vr.Sort(SORT_DESCENDING, FALSE, vnIndeces);
	
	return vnIndeces[0];
}
///---END GET_LARGEST_GAP

#ifdef NLFTI_PARAMETERS_MANAGER	//cpy 9/12/08 added this

///Arvin 12/29/06 SET_W_AS_ED_OF_DATARANGE_AND_PLOT_ERROR_BAR
//The function AddPlot determines column type by tag name of corresponding subrange, when we use it 
//plot graph of a data range. But, sometimes the name is no real name of column, so in this function,
//we judge column's real type first, and create a new data range named by column's real type, and 
//then use the new created data range to plot graph.
///Cheney 2007-9-7 SHOULD_ALLOW_DUP_DATASET_PLOT_FOR_PREVIEW_SO_AS_TO_SAME_WITH_FIT_THINGS
//int plotDataRange(DataRange& dr, GraphLayer& gl, int nPlotType, uint nCntrl)
/// Iris 10/24/2008 v8.0960b FIX_FAIL_NOT_SETUP_PLOT_FOR_ALL_PEAK_CURVES
//int plotDataRange(DataRange& dr, GraphLayer& gl, int nPlotType, uint nCntrl, bool bCheckDup)
/// Hong 03/25/09 QA80-12551 RESET_PLOT_ROW_RANGE_IF_USER_UPDATE_INPUT_DATARANGE_WHEN_CHANGE_PARAMETER
//int plotDataRange(DataRange& dr, GraphLayer& gl, int nPlotType, uint nCntrl, bool bCheckDup, vector<int> *pvnPlottedIndices)
int plotDataRange(DataRange& dr, GraphLayer& gl, int nPlotType, uint nCntrl, bool bCheckDup, vector<int> *pvnPlottedIndices, bool bResetPlotRowRangeIfDiffButNotChecked)
/// end RESET_PLOT_ROW_RANGE_IF_USER_UPDATE_INPUT_DATARANGE_WHEN_CHANGE_PARAMETER
///end FIX_FAIL_NOT_SETUP_PLOT_FOR_ALL_PEAK_CURVES
///end SHOULD_ALLOW_DUP_DATASET_PLOT_FOR_PREVIEW_SO_AS_TO_SAME_WITH_FIT_THINGS
{
	if(!gl || !dr)
		return -1;
	
	DataRange drSubRange;
	///Sophy 9/2/2008 CLEAN_NLFCURVE_ADDPLOT_CODE
	///*
	if( !(nCntrl & GAP_USE_WKS_DESIGNATION) )
	{
		int nStartPlot = -1;
		for(int nRange=0; nRange < dr.GetNumRanges(); nRange++)
		{
			int 		c1, c2, r1, r2;
			Worksheet 	wksTemp;
			/// Iris 02/13/2007 v8.0560 SHOULD_NOT_PLOT_WEIGHT_UNLESS_COL_TYPE_IS_ERR
			//dr.GetRange(nRange, r1, c1, r2, c2, wksTemp);
			string		strRangeName;
			dr.GetRange(nRange, r1, c1, r2, c2, wksTemp, &strRangeName);
			bool		bIsWeight = 0 == lstrcmpi(strRangeName, "W")? true : false;
			///end SHOULD_NOT_PLOT_WEIGHT_UNLESS_COL_TYPE_IS_ERR
			string strRange = strRangeName;
			//Range is not empty
			//------ Folger 03/18/08 QA80-11260 FIX_RUNTIME_ERROR_IN_MATRIX_FITTING_PREVIEW_AND_REPORT_GRAPH
			//if(c1 >= 0 && c1 <= c2)
			if(wksTemp && c1 >= 0 && c1 <= c2)
			//------
			{
				if(bIsWeight)
				{
					Column col(wksTemp, c1);
					//If column is error, change range name from "W" to "ED" and then plot graph with error bar  
					if( col && col.GetType() == OKDATAOBJ_DESIGNATION_ERROR)
						strRange = _L("ED");
					
				}
			
				drSubRange.Add(strRange, wksTemp, r1, c1, r2, c2);
			}
			///------ Folger 01/04/09 QA80-12962 MORE_WORK_ON_GENERATE_INDIVIDUAL_FIT_CURVE_USING_INDEPENDENT_X_IN_PA
			///Sophy 1/20/2009 v8.0961 FIX_MATRIX_FIT_FAIL_TO_PLOR_SOURCE_DATA_ON_REPORT_GRAPH
			//else if ( strRangeName == "S" )		/// separator S:<empty>
			else if ( drSubRange.IsValid() && strRangeName == "S" )		/// separator S:<empty> ///Sophy, can not add "S" as the first range in drSubRange
			///end FIX_MATRIX_FIT_FAIL_TO_PLOR_SOURCE_DATA_ON_REPORT_GRAPH
				drSubRange.Add("S", NULL);
			///------ End MORE_WORK_ON_GENERATE_INDIVIDUAL_FIT_CURVE_USING_INDEPENDENT_X_IN_PA
		}
	}
	//*/
	// No need to change subrange string now
	///end CLEAN_NLFCURVE_ADDPLOT_CODE
	//------ Folger 03/18/08 QA80-11260 FIX_RUNTIME_ERROR_IN_MATRIX_FITTING_PREVIEW_AND_REPORT_GRAPH
	if ( !drSubRange )
		drSubRange = dr;
	//------
	
	///Arvin 08/29/07 QA70-10073-P6 KEEP_CUSTOMIZATION_SETTINGS_FOR_SOURCE_DATA_PLOT_AFTER_RECALCULATE 
	//int nPlot = gl.AddPlot(drSubRange, nPlotType, nCntrl);
	int nPlot = -1;
	vector<int> 	vnPlotIndices;
	/// Iris 10/24/2008 v8.0960b FIX_FAIL_NOT_SETUP_PLOT_FOR_ALL_PEAK_CURVES
	vector<int>		vnPlottedIndices; 
	if( pvnPlottedIndices )
		vnPlottedIndices.SetSize(pvnPlottedIndices->GetSize());
	///end FIX_FAIL_NOT_SETUP_PLOT_FOR_ALL_PEAK_CURVES
	
	///Cheney 2007-9-7 SHOULD_ALLOW_DUP_DATASET_PLOT_FOR_PREVIEW_SO_AS_TO_SAME_WITH_FIT_THINGS
	//if( check_has_plotted_in_graph(drSubRange, gl, vnPlotIndices) > 0 && vnPlotIndices.GetSize() >0 )
	/// Hong 03/25/09 QA80-12551 RESET_PLOT_ROW_RANGE_IF_USER_UPDATE_INPUT_DATARANGE_WHEN_CHANGE_PARAMETER
	//if( bCheckDup && check_has_plotted_in_graph(drSubRange, gl, vnPlotIndices) > 0 && vnPlotIndices.GetSize() >0 )
	if( bCheckDup && check_has_plotted_in_graph(drSubRange, gl, vnPlotIndices, NTYPE_BOOKSHEET_XY_RANGE, bResetPlotRowRangeIfDiffButNotChecked) > 0 && vnPlotIndices.GetSize() >0 )
	/// end RESET_PLOT_ROW_RANGE_IF_USER_UPDATE_INPUT_DATARANGE_WHEN_CHANGE_PARAMETER
	///end SHOULD_ALLOW_DUP_DATASET_PLOT_FOR_PREVIEW_SO_AS_TO_SAME_WITH_FIT_THINGS
		nPlot = vnPlotIndices[0];
	else
		/// Iris 10/24/2008 v8.0960b FIX_FAIL_NOT_SETUP_PLOT_FOR_ALL_PEAK_CURVES
		//nPlot = gl.AddPlot(drSubRange, nPlotType, nCntrl);
		nPlot = gl.AddPlot(drSubRange, nPlotType, nCntrl, vnPlottedIndices, vnPlottedIndices.GetSize());
		///end FIX_FAIL_NOT_SETUP_PLOT_FOR_ALL_PEAK_CURVES
	///end KEEP_CUSTOMIZATION_SETTINGS_FOR_SOURCE_DATA_PLOT_AFTER_RECALCULATE
	
	/// Iris 10/24/2008 v8.0960b FIX_FAIL_NOT_SETUP_PLOT_FOR_ALL_PEAK_CURVES	
	if( pvnPlottedIndices )
	{
		vector<uint> vnIndices;
		if( vnPlottedIndices.Find(vnIndices, -1) > 0 )// Find returns the number of values found, -1 means the end of all plotted indices, need remove items from -1
		{
			vnPlottedIndices.SetSize(vnIndices[0]);
			vnPlottedIndices.InsertAt(0, nPlot); // insert plot index of first plot to vnPlottedIndices as first item
		}	

		*pvnPlottedIndices = vnPlottedIndices;
	}
	///end FIX_FAIL_NOT_SETUP_PLOT_FOR_ALL_PEAK_CURVES
	
	return nPlot;
}
///end SET_W_AS_ED_OF_DATARANGE_AND_PLOT_ERROR_BAR
#else //#ifdef NLFTI_PARAMETERS_MANAGER	//cpy 9/12/08 added this
/// Iris 10/24/2008 v8.0960b FIX_FAIL_NOT_SETUP_PLOT_FOR_ALL_PEAK_CURVES
//int plotDataRange(DataRange& dr, GraphLayer& gl, int nPlotType, uint nCntrl, bool bCheckDup)
int plotDataRange(DataRange& dr, GraphLayer& gl, int nPlotType, uint nCntrl, bool bCheckDup, vector<int> *pvnPlottedIndices)
///end FIX_FAIL_NOT_SETUP_PLOT_FOR_ALL_PEAK_CURVES
///end SHOULD_ALLOW_DUP_DATASET_PLOT_FOR_PREVIEW_SO_AS_TO_SAME_WITH_FIT_THINGS
{
	if(!gl || !dr)
		return -1;
	
	DataRange drSubRange;
	int nStartPlot = -1;
	for(int nRange=0; nRange < dr.GetNumRanges(); nRange++)
	{
		int 		c1, c2, r1, r2;
		Worksheet 	wksTemp;
		/// Iris 02/13/2007 v8.0560 SHOULD_NOT_PLOT_WEIGHT_UNLESS_COL_TYPE_IS_ERR
		//dr.GetRange(nRange, r1, c1, r2, c2, wksTemp);
		string		strRangeName;
		dr.GetRange(nRange, r1, c1, r2, c2, wksTemp, &strRangeName);
		bool		bIsWeight = 0 == lstrcmpi(strRangeName, "W")? true : false;
		///end SHOULD_NOT_PLOT_WEIGHT_UNLESS_COL_TYPE_IS_ERR
		string strRange = strRangeName;
		//Range is not empty
		//------ Folger 03/18/08 QA80-11260 FIX_RUNTIME_ERROR_IN_MATRIX_FITTING_PREVIEW_AND_REPORT_GRAPH
		//if(c1 >= 0 && c1 <= c2)
		if(wksTemp && c1 >= 0 && c1 <= c2)
		//------
		{
			if(bIsWeight)
			{
				Column col(wksTemp, c1);
				//If column is error, change range name from "W" to "ED" and then plot graph with error bar  
				if( col && col.GetType() == OKDATAOBJ_DESIGNATION_ERROR)
					///Kyle 09/24/08 SET_NO_NEED_TO_LOCALIZE
					//strRange = _L("ED");
					strRange = "ED";
					///End SET_NO_NEED_TO_LOCALIZE
				
			}
		
			drSubRange.Add(strRange, wksTemp, r1, c1, r2, c2);
		}
	}
	
	//------ Folger 03/18/08 QA80-11260 FIX_RUNTIME_ERROR_IN_MATRIX_FITTING_PREVIEW_AND_REPORT_GRAPH
	if ( !drSubRange )
		drSubRange = dr;
	//------
	
	///Arvin 08/29/07 QA70-10073-P6 KEEP_CUSTOMIZATION_SETTINGS_FOR_SOURCE_DATA_PLOT_AFTER_RECALCULATE 
	//int nPlot = gl.AddPlot(drSubRange, nPlotType, nCntrl);
	int nPlot = -1;
	vector<int> 	vnPlotIndices;
	/// Iris 10/24/2008 v8.0960b FIX_FAIL_NOT_SETUP_PLOT_FOR_ALL_PEAK_CURVES
	vector<int>		vnPlottedIndices; 
	if( pvnPlottedIndices )
		vnPlottedIndices.SetSize(pvnPlottedIndices->GetSize());
	///end FIX_FAIL_NOT_SETUP_PLOT_FOR_ALL_PEAK_CURVES

	///Cheney 2007-9-7 SHOULD_ALLOW_DUP_DATASET_PLOT_FOR_PREVIEW_SO_AS_TO_SAME_WITH_FIT_THINGS
	//if( check_has_plotted_in_graph(drSubRange, gl, vnPlotIndices) > 0 && vnPlotIndices.GetSize() >0 )
	if( bCheckDup && check_has_plotted_in_graph(drSubRange, gl, vnPlotIndices) > 0 && vnPlotIndices.GetSize() >0 )
	///end SHOULD_ALLOW_DUP_DATASET_PLOT_FOR_PREVIEW_SO_AS_TO_SAME_WITH_FIT_THINGS
		nPlot = vnPlotIndices[0];
	else
		/// Iris 10/24/2008 v8.0960b FIX_FAIL_NOT_SETUP_PLOT_FOR_ALL_PEAK_CURVES
		//nPlot = gl.AddPlot(drSubRange, nPlotType, nCntrl);
		nPlot = gl.AddPlot(drSubRange, nPlotType, nCntrl, vnPlottedIndices, vnPlottedIndices.GetSize());
		///end FIX_FAIL_NOT_SETUP_PLOT_FOR_ALL_PEAK_CURVES
	///end KEEP_CUSTOMIZATION_SETTINGS_FOR_SOURCE_DATA_PLOT_AFTER_RECALCULATE
	
	/// Iris 10/24/2008 v8.0960b FIX_FAIL_NOT_SETUP_PLOT_FOR_ALL_PEAK_CURVES
	if( pvnPlottedIndices )
	{
		vector<uint> vnIndices;
		if( vnPlottedIndices.Find(vnIndices, -1) > 0 )// Find returns the number of values found, -1 means the end of all plotted indices, need remove items from -1
		{
			vnPlottedIndices.SetSize(vnIndices[0]);
			vnPlottedIndices.InsertAt(0, nPlot); // insert plot index of first plot to vnPlottedIndices as first item
		}	

		*pvnPlottedIndices = vnPlottedIndices;
	}
	///end FIX_FAIL_NOT_SETUP_PLOT_FOR_ALL_PEAK_CURVES
	
	return nPlot;
}
#endif //#ifdef NLFTI_PARAMETERS_MANAGER	//cpy 9/12/08 added this

//---------- CPY 1/8/07 SPARK_LINES_BROKEN_FOR_MIXED_COL
// return <0 if err, 1 if true, 0 if false
int is_col_all_text(const Worksheet& wks, int nCol)
{
	Column cc = wks.Columns(nCol);
	if(!cc)
		return -1;
	
	int nColDataType = cc.GetFormat();
	if(OKCOLTYPE_TEXT == nColDataType)
		return 1;
	if(nColDataType != OKCOLTYPE_TEXT_NUMERIC)
		return 0;
	// mixed data type, more complicated, need to check if all text
	//------- CPY 5/8/2007 QA70-9735 OPTION_TO_TURN_OFF_CATEGORICAL_CONVERT
	DatasetObject dobj(cc);
	if(dobj.PercentText() > 50)
		return 1;
	//-------
	return 0;
}

///---Sim 11-05-2007 IMPROVE_LOCALIZATION_ISSUE
/*
int compare_localization(const string& strDest, LPCSTR lpsz)
{
	//okutil_compare_localization(strDest, lpsz);
	return strDest.Compare(lpsz);
}

int compare_no_case_localization(const string& strDest, LPCSTR lpsz)
{
	//okutil_compare_no_case_localization(strDest, lpsz);
	return strDest.CompareNoCase(lpsz);
}

int find_string_localization(const string& strDest, LPCSTR lpszSub)
{
	//okutil_find_string_localization(strDest, lpszSub);
	return strDest.Find(lpszSub);
}
*/

static bool _compare_string(LPCSTR lpszString1, LPCSTR lpszString2, bool bNoCaseSensitive)
{
	if ( bNoCaseSensitive )
		return ( 0 == lstrcmpi(lpszString1, lpszString2) );
	else
		return ( 0 == lstrcmp(lpszString1, lpszString2) );
}

bool compare_string_localization(LPCSTR lpszLocalDest, LPCSTR lpszEnglishSub, bool bNoCaseSensitive) // = false
{
	if ( _compare_string(lpszLocalDest, lpszEnglishSub, bNoCaseSensitive) )
		return true;
	
	if ( _compare_string(lpszLocalDest, GetLocalized(lpszEnglishSub), bNoCaseSensitive) )
		return true;
	
	return false;
}

int find_string_localization(LPCSTR lpszLocalDest, LPCSTR lpszEnglishSub)
{
	string strLocalDest(lpszLocalDest);
	
	int nPos;
	if ( -1 < (nPos = strLocalDest.Find(lpszEnglishSub)) )
		return nPos;
	
	if ( -1 < (nPos = strLocalDest.Find(GetLocalized(lpszEnglishSub))) )
		return nPos;
	
	return -1;
}

///---END IMPROVE_LOCALIZATION_ISSUE

/// ML 12/17/2007 INITIALIZING_PEAK_POSITIONS_FOR_REPLICAS_FITTING
/*
///Cheney 2007-11-20 INIT_XC_FOR_REPLICA_FITTING
bool init_xc_for_replica_fitting(const vector& vInputx, int nReplica, vector& vXc)
{
	int nXcs = nReplica + 1;
	int nInputSize = vInputx.GetSize();
	if(nXcs < 1 || nInputSize < 1)
		return false;
	
	vXc.SetSize(nXcs);
	vXc = NANUM;
	
	double dMin = 0., dMax = 0.;
	int nIndexMin = 0;
	int nNumNonMissing = vInputx.GetMinMax( dMin, dMax, &nIndexMin);
	
	//if input data is all missing, vXc = NANUM
	//else if input data has only 1 non-missing value, set vXc equal to it
	//else divide it as nXcs part, then get mean value of each part ( (dmin+dmax)/2 )
	if(nNumNonMissing == 1)
	{
		double dValTemp = vInputx[nInputSize - 1];
		vXc = dValTemp;
	}
	else if( nNumNonMissing > 1 )
	{
		//trim missing
		vector vtempInput;
		vtempInput = vInputx;
		if(nNumNonMissing < nInputSize) //exist missing val
		{
			ASSERT(nIndexMin > 0);
			vInputx.GetSubVector( vtempInput, nIndexMin);
		}
		
		//loop to get each part's mean value ( (dmin+dmax)/2 )
		int nTempInputSize = vtempInput.GetSize();
		int nSizeInc = nTempInputSize / nXcs;
		for(int ii = 0; ii < nXcs; ii++)
		{
			vector vtemp;
			vtempInput.GetSubVector( vtemp, nSizeInc*ii, ii == nXcs-1? -1 : nSizeInc*(ii+1)-1 );
			vXc[ii] = ( vtemp[vtemp.GetSize() - 1] + vtemp[0] ) / 2;
		}
	}
	
	return true;
}
///end INIT_XC_FOR_REPLICA_FITTING
*/
/// Cloud 12/18/07 TREAT_SIGN_OF_PEAK_AREA
//bool init_xc_for_replica_fitting(const vector& vInputX, const vector& vInputY, int nReplica, vector& vXc, double rBaseline)
///Arvin 03/11/08 REPLICA_AUTO_INIT_PARAMS_FOR_SURFACE_FIT
//Move to curve_utils.cpp
/*
bool init_xc_for_replica_fitting(const vector& vInputX, const vector& vInputY, int nReplica, vector& vXc, double rBaseline, vector* pvPeaksY)
/// End TREAT_SIGN_OF_PEAK_AREA
{
	int		numPeaksNeeded = nReplica + 1;

	int		nInputSizeY = vInputY.GetSize();
	int		nInputSizeX = vInputX.GetSize();
	int		nInputSize = min(nInputSizeX, nInputSizeY);
	if(numPeaksNeeded < 1 || nInputSize < 1)
		return false;
	///Arvin 03/11/08 REPLICA_AUTO_INIT_PARAMS_FOR_SURFACE_FIT
	if(!findPeakCtrlInfo)
		return false;
	///end REPLICA_AUTO_INIT_PARAMS_FOR_SURFACE_FIT
	vXc.SetSize(numPeaksNeeded);
	vXc = NANUM;
	
	vector	vYPeaks(nInputSize), vXPeaks(nInputSize);
	vector<int>	vnPeakIndices(nInputSize);

	vector	vInputYBaselineSubtracted; 
	vInputYBaselineSubtracted = vInputY - rBaseline;		// this is needed because ocmath_find_peaks_1st_derivative() seems to require the baseline to be 0 
	UINT	nCount = nInputSize;
	int		nRet = ocmath_find_peaks_1st_derivative(&nCount, vInputX, vInputYBaselineSubtracted, vXPeaks, vYPeaks, vnPeakIndices, BOTH_DIRECTION);
	if (OE_NOERROR == nRet)
	{
		if (0 < nCount)
		{
			// Sort the peaks (I want to use the highest peaks):
			vYPeaks.SetSize(nCount);
			vXPeaks.SetSize(nCount);

			/// Cloud 12/18/07 TREAT_SIGN_OF_PEAK_AREA
			if (pvPeaksY != NULL)
				*pvPeaksY = vYPeaks;
			/// End TREAT_SIGN_OF_PEAK_AREA

			vYPeaks = fabs(vYPeaks);
			vector<uint>	vnReorder(nCount);
			vYPeaks.Sort(SORT_DESCENDING, TRUE, vnReorder);
			vXPeaks.Reorder(vnReorder);
			/// Cloud 12/18/07 TREAT_SIGN_OF_PEAK_AREA
			if (pvPeaksY != NULL)
				pvPeaksY->Reorder(vnReorder);
			/// End TREAT_SIGN_OF_PEAK_AREA

			int		numPeaksToReturn = min(numPeaksNeeded, (int)nCount);
			/// Cloud 12/18/07 TREAT_SIGN_OF_PEAK_AREA
			//for (int ipeak = 0; ipeak < numPeaksToReturn; ipeak++)
			//{
				//vXc[ipeak] = vXPeaks[numPeaksToReturn - ipeak - 1];
			//}
			for (int ipeak = 0; ipeak < numPeaksToReturn; ipeak++)
			{
				vXc[ipeak] = vXPeaks[ipeak];
			}
			/// End TREAT_SIGN_OF_PEAK_AREA
		}
		
		return true;
	}
	
	return false;
}
*/
///end REPLICA_AUTO_INIT_PARAMS_FOR_SURFACE_FIT
/// end INITIALIZING_PEAK_POSITIONS_FOR_REPLICAS_FITTING


//----- CPY 2/3/2008 QA70-11050 GET_PTS_NEED_USER_DECIDE_TERMINATION
/// Hong 03/26/08 v8/0832b PA_PEAKS_DEL_REQUIRE_FOLLOWING_DATA_SUPPORT
//typedef int (*FUNC_GRAPH_GET_PTS)(vector& vx, vector& vy, int nPts, int nCursorType, LPCSTR lpcszMsg1, LPCSTR lpcszMsg2);
//int graph_get_points(vector& vx, vector& vy, int nPts, int nCursorType, LPCSTR lpcszMsg1, LPCSTR lpcszMsg2)
///Sophy 8/17/2009 QA80-14087-P3 NORMALIZE_WITH_SELECTED_POINT_FROM_GRAPH_GET_WRONG_RESULT
//typedef int (*FUNC_GRAPH_GET_PTS)(vector& vx, vector& vy, int nPts, int nCursorType, LPCSTR lpcszMsg1, LPCSTR lpcszMsg2, LPCSTR lpcszTitle, int* pnPlotIndex);
//int graph_get_points(vector& vx, vector& vy, LPCSTR lpcszMsg1, LPCSTR lpcszMsg2, int* pnPlotIndex, LPCSTR lpcszTitle, int nPts, int nCursorType) // =NULL, NULL, NULL, -1, -1, 
typedef int (*FUNC_GRAPH_GET_PTS)(vector& vx, vector& vy, int nPts, int nCursorType, LPCSTR lpcszMsg1, LPCSTR lpcszMsg2, LPCSTR lpcszTitle, int* pnPlotIndex, vector<int>& vnDatapointIndices, bool bShowDlg, vector<int>& vnDataplotIndices);
int graph_get_points(vector& vx, vector& vy, LPCSTR lpcszMsg1, LPCSTR lpcszMsg2, int* pnPlotIndex, LPCSTR lpcszTitle, int nPts, int nCursorType, vector<int>& vnDatapointIndices, bool bShowDlg, vector<int>& vnDataplotIndices) // =NULL, NULL, NULL, -1, -1, NULL, true
///end NORMALIZE_WITH_SELECTED_POINT_FROM_GRAPH_GET_WRONG_RESULT
/// end PA_PEAKS_DEL_REQUIRE_FOLLOWING_DATA_SUPPORT
{
	FUNC_GRAPH_GET_PTS _pfn = Project.FindFunction("GetPtsDlg", NULL, TRUE);
	if(_pfn)	
		/// Hong 03/26/08 v8/0832b PA_PEAKS_DEL_REQUIRE_FOLLOWING_DATA_SUPPORT
   		//return _pfn(vx, vy, nPts, nCursorType, lpcszMsg1, lpcszMsg2);
   		///Sophy 8/17/2009 QA80-14087-P3 NORMALIZE_WITH_SELECTED_POINT_FROM_GRAPH_GET_WRONG_RESULT
   		//return _pfn(vx, vy, nPts, nCursorType, lpcszMsg1, lpcszMsg2, lpcszTitle, pnPlotIndex);
   		return _pfn(vx, vy, nPts, nCursorType, lpcszMsg1, lpcszMsg2, lpcszTitle, pnPlotIndex, vnDatapointIndices, bShowDlg, vnDataplotIndices);
   		///end NORMALIZE_WITH_SELECTED_POINT_FROM_GRAPH_GET_WRONG_RESULT
   		/// end PA_PEAKS_DEL_REQUIRE_FOLLOWING_DATA_SUPPORT
	
	return -2;
}
//-----

/// Hong 03/26/08 QA80-11323 v8.0832b PA_PEAKS_ADD_DONE_DIALOG_WHEN_MODIFY
///Jasmine 02/19/09 QA80-13131 RECEIVE_MSG_TO_CLOSE_GRAPHDONEDLG
//typedef bool (*FUNC_GRAPH_DONE_WND)(LPCSTR lpcstrMsg, BOOL bAutoResize, LPCSTR lpcszTitle);
//bool graph_done_window(LPCSTR lpcstrMsg, LPCSTR lpcszTitle, BOOL bAutoResize) // = true, NULL /// CPY 04/02/08 OPTION_TO_AUTOSIZE_GETPOINTSDIALOG added bAutoResize
typedef bool (*FUNC_GRAPH_DONE_WND)(DWORD& dwDlg, LPCSTR lpcstrMsg, BOOL bAutoResize, LPCSTR lpcszTitle);
bool graph_done_window(DWORD& dwDlg, LPCSTR lpcstrMsg, LPCSTR lpcszTitle, BOOL bAutoResize) // = true, NULL 
{
	FUNC_GRAPH_DONE_WND pfn = Project.FindFunction("GetDoneDlg", "Originlab\\GetPtsDlg", TRUE);
	if ( pfn )
		return pfn(dwDlg, lpcstrMsg, bAutoResize, lpcszTitle);//return pfn(lpcstrMsg, bAutoResize, lpcszTitle);
	
	return false;
}
///End RECEIVE_MSG_TO_CLOSE_GRAPHDONEDLG
/// end PA_PEAKS_ADD_DONE_DIALOG_WHEN_MODIFY

/// Iris 4/23/2008 HIDE_INNER_OUTER_LIMITS_FOR_XYFIT_AND_PA
static bool	_is_data_from_matrix(GraphLayer& gl)
{
	DataPlot dp = gl.DataPlots();
	if(!dp)
		return false;
	
	bool bFromMatrix = false;
	int nPlotType = dp.GetPlotType();
	/// ML 10/1/2007 QA70-10268 MATRIX_FIT_FROM_CONTOUR_PLOT
	//if(IDM_PLOT_3D_MESH == nPlotType)
	//	bFromMatrix = true;
	if(IDM_PLOT_3D_MESH == nPlotType || IDM_PLOT_CONTOUR == nPlotType)
		bFromMatrix = true;
	/// end MATRIX_FIT_FROM_CONTOUR_PLOT

	return bFromMatrix;
}

int		get_fit_type(string& strClassName)
{
	/// Iris 10/23/2009 QA80-14512 FIT_LINEAR_X_ERR_SHOULD_BE_PRO
	if( 0 == lstrcmpi(strClassName, "FitLRXErr") )
		return 1;
	///end FIT_LINEAR_X_ERR_SHOULD_BE_PRO
	
	/// Hong 11/19/08 QA80-12558 V8.90875 DESC_STATS_ON_COL_SUPPORT_DATA_FROM_GRAPH
	if ( 0 == lstrcmpi(strClassName, "gDescStats") )
		return 0;
	/// end DESC_STATS_ON_COL_SUPPORT_DATA_FROM_GRAPH	
	int nFitType = NLFIT_GENERAL_XY_FITTING;
	if(!lstrcmp(strClassName, "fitsurface"))
		nFitType = NLFIT_XYZ_FITTING;
	else if(!lstrcmp(strClassName, "fitmatrix")) 
		nFitType = NLFIT_MATRIX_FITTING;
	
	//if do 3d fitting from graph, lpcszClass always be "fitsurface"
	//because in the menu, if from graph, only could select surface fit
	GraphLayer gl = Project.ActiveLayer();
	if(nFitType != NLFIT_GENERAL_XY_FITTING && gl && _is_data_from_matrix(gl))
	{
		nFitType = NLFIT_MATRIX_FITTING;
		strClassName = "fitmatrix";
	}
	return nFitType;
}
///end HIDE_INNER_OUTER_LIMITS_FOR_XYFIT_AND_PA

//------ Folger 08/29/08 QA80-12120 CENTRALIZE_XF_ERROR_MSEEAGE_PARSING_CODE
string	xf_load_err_msg(int nErrCode, LPCSTR lpcsz)
{
	string	strMsg;
	if(0 == nErrCode)
		return lpcsz;
		
	string str1;
	if(ocu_load_err_msg_str(nErrCode, &str1))
	{
		///------ Folger 09/10/2010 ORG-1025 XF_ERROR_MESSAGE_SUPPORT_MORE_STR_ARGS
		vector<string>	vs;
		string			strArgs = lpcsz;
		int				nToken = strArgs.GetTokens(vs, '|');
		if ( nToken > 1 )
		{
			int		nPos = 0;
			int		nStrCount = 0;
			while ( (nPos = str1.Find("%s", nPos)) >= 0 )
			{
				++nStrCount;
				nPos += 2;
			}

			if ( nStrCount == nToken )
			{
				if ( 2 == nToken )
					strMsg.Format(str1, vs[0], vs[1]);
				else if ( 3 == nToken )
					strMsg.Format(str1, vs[0], vs[1], vs[2]);
				else if ( 4 == nToken )
					strMsg.Format(str1, vs[0], vs[1], vs[2], vs[3]);
				else
				{
					O_A_FAIL;
					return "";
				}
				return strMsg;
			}
		}
		///------ End XF_ERROR_MESSAGE_SUPPORT_MORE_STR_ARGS

		int nNumPos;
		if((nNumPos = str1.Find("%d")) >= 0)
		{
			int nStrPos = str1.Find("%s");
			if(nStrPos >= 0)
			{
				///------ Folger 09/10/2010 ORG-1025 XF_ERROR_MESSAGE_SUPPORT_MORE_STR_ARGS
				//vector<string> vs;
				//string strArgs = lpcsz;
				//if(strArgs.GetTokens(vs, '|') != 2)
				if ( vs.GetSize() != 2 )
				///------ End XF_ERROR_MESSAGE_SUPPORT_MORE_STR_ARGS
					ocu_load_msg_str(CER_USERMSG_NOT_CORRECT_SEPARATED, &strMsg, lpcsz);
				else
				{
					if(nStrPos > nNumPos)
					{
						int nCode = atoi(vs[0]);
						strMsg.Format(str1, nCode, vs[1]);
					}
					else
					{
						int nCode = atoi(vs[1]);
						strMsg.Format(str1, vs[0], nCode);
					}
				}
			}
			else
			{
				int nCode = atoi(lpcsz);
				strMsg.Format(str1, nCode);
			}
		}
		else if(str1.Find("%s") >= 0)
			strMsg.Format(str1, lpcsz);
		else	
			strMsg = str1;
	}
	else
	{
		ocu_load_msg_str(CER_ERR_MSG_NOT_FOUND, &strMsg, NULL, &nErrCode);
	}
		
	return strMsg;
}

int		xf_warning_msg_box(LPCSTR lpcszErrMsg, bool bShowBox, char cMsgType/* = 'W'*/, UINT uType/* = MB_OK*/)
{
	int		nErrCode = -1;
	string	strMsg = lpcszErrMsg;
	LPSTR	lpstr = strMsg.GetBuffer(strMsg.GetLength());
	LPSTR	lpstr2 = is_str_numeric_integer_with_args(lpstr, &nErrCode);
	strMsg.ReleaseBuffer();
	
	return warning_msg_box(lpstr2 ? xf_load_err_msg(nErrCode, lpstr2) : lpcszErrMsg, bShowBox, cMsgType, uType);
}


//------ End CENTRALIZE_XF_ERROR_MSEEAGE_PARSING_CODE


//------ Folger 11/06/08 QA80-12538 v0.966 CENTRALIZE_CODE_ABOUT_GETTING_GUI_REPLICA_NODE
TreeNode	get_gui_replica_node(const TreeNode& trGUI)
{
	TreeNode trReplica = tree_get_node_by_tagname(trGUI, "Replica", true);
	
	//ASSERT(trReplica); // Iris 11/19/2008 fix assert failed in fit linear
	return trReplica;
}
//------ End CENTRALIZE_CODE_ABOUT_GETTING_GUI_REPLICA_NODE

///Sophy 11/21/2008 v8.976 QA80-12591-P3 ADD_ERRMSG_WHEN_FITCURVE_XDATATYPE_IS_LOG_WITH_NEGATIVE_INPUT copy from nlfcurves.h need to centralize
int	get_fitted_curve_data_type(const TreeNode& trDataType)
{
	int 	nSelection = trDataType.Use;
	
	string 			strList;
	vector<string> 	vsList;
	trDataType.GetAttribute(STR_ATTRIB_BRANCH_COMBO, strList);
	strList.GetTokens(vsList, '|');
	
	if( nSelection < 0 || nSelection >= vsList.GetSize() )
		return -1;
	
	string strSelction = vsList[nSelection];
	
	if ( 0 == strSelction.Compare(STR_FITTED_CURVE_DATA_TYPE_SAME_INPUT))
		return FIT_CURVE_SAME_AS_DATA;
	
	if ( 0 == strSelction.Compare(STR_FITTED_CURVE_DATA_TYPE_UNIFORM_LINEAR))
		return FIT_CURVE_UNIFORM_LINEAR;
		
	if ( 0 == strSelction.Compare(STR_FITTED_CURVE_DATA_TYPE_UNIFORM_LOG))
		return FIT_CURVE_UNIFORM_LOG;
	
	///Kyle 11/18/2008 QA80-12591 ADD_MORE_OPTION_FOR_INPUT_DATA_TYPE_IN_FITTING_TOOLS
	if( 0 == strSelction.Compare(STR_FITTED_CURVE_DATA_TYPE_SAME_SOURCE_GRAPH) )
	{
		return FIT_CURVE_SAME_AS_SOURCE_GRAPH;
	}
	///End ADD_MORE_OPTION_FOR_INPUT_DATA_TYPE_IN_FITTING_TOOLS
	
	return -1;	
}
///end ADD_ERRMSG_WHEN_FITCURVE_XDATATYPE_IS_LOG_WITH_NEGATIVE_INPUT

///Jasmine 12/04/08 v8.0982b GET_PLOT_COLUMNS
int get_plot_columns(DataPlot dp, Column& colX, Column& colY, Column& colZ)// = NULL
{
	XYRange xy;
	if( !dp || !dp.GetDataRange(xy) )
		return -1;
	
	if(colZ != NULL)
	{
		XYZRange xyz(xy);
		if(!xyz)
			return -1;
	
		int r1, c1, r2, c2;
		Worksheet wks;
		if(	!xyz.GetRange("Z", r1, c1, r2, c2, wks) )
			return -1;
		
		colZ = wks.Columns(c1);
	}
	
	if( xy.GetYColumn(colY) )
	{
		if( !xy.GetXColumn(colX) )
			return CER_NO_X_COL;
		
		return CER_NO_ERROR;
	}
	
	return -1;
	
}
///End GET_PLOT_COLUMNS


///Kyle 02/24/2009 QA80-13169 CHECK_IF_TWO_RANGE_HAVE_COMMON_RANGE

bool have_common_range(const TreeNode& iy1, const TreeNode& iy2)
{
	XYRange rng1, rng2;
	if(xy_range_from_GetN_data_node(iy1, rng1) && xy_range_from_GetN_data_node(iy2, rng2))
		return have_common_range(rng1, rng2);
	return false;
}

bool have_common_range(const XYRange& rng1, const XYRange& rng2)
{
	if(!rng1 || !rng2)
		return false;

	vector vx1, vy1, vx2, vy2;
	rng1.GetData(vy1, vx1);
	rng2.GetData(vy2, vx2);

	double dMin1, dMax1, dMin2, dMax2;
	
	vx1.GetMinMax(dMin1, dMax1);
	vx2.GetMinMax(dMin2, dMax2);
	
	if(dMin1 < dMin2)
		dMin1 = dMin2;
	
	if(dMax1 > dMax2)
		dMax1 = dMax2;

	return dMin1 <= dMax1;
}

///End CHECK_IF_TWO_RANGE_HAVE_COMMON_RANGE

///Kyle 02/26/2009 CENTRALIZE_CODE_TO_TRIM_INDEPENDENT
bool trim_independent(vector& vx, vector& vy, vector& vz)
{
	if(vx.GetSize() != vz.GetSize() || vy.GetSize() != vz.GetSize() || vz.GetSize() <= 0)
		return false;

	vector<uint> vnXMissing, vnYMissing;
	int nXMissing = vx.Find(MATREPL_TEST_EQUAL, NANUM, vnXMissing);
	int nYMissing = vy.Find(MATREPL_TEST_EQUAL, NANUM, vnYMissing);
	if(nXMissing > 0 || nYMissing > 0)
	{
		vector<int> vnMissing;
		vnMissing = vnXMissing;
		vnMissing.Append(vnYMissing);
		vx.RemoveAt(vnMissing);
		vy.RemoveAt(vnMissing);
		vz.RemoveAt(vnMissing);
	}
	return true;
}
///End CENTRALIZE_CODE_TO_TRIM_INDEPENDENT


/// Hong 03/09/09 QA80-13243 IMPROVE_AVE_XY_AVOID_MEMORY_LEAKING
//#define _DEBUG_ONECURVEDATAMNGR_

OneCurveDataMngr::OneCurveDataMngr(int nCount)
{
	m_nCount = nCount;
	if ( m_nCount > 0 )
		m_pData = (OneCurveData*)malloc(m_nCount * sizeof(OneCurveData));
	else
		m_pData = NULL;
	if ( IsValid() )
		memset(m_pData, 0, m_nCount * sizeof(OneCurveData));
	
#ifdef		_DEBUG_ONECURVEDATAMNGR_
	out_int("", GetCount());
#endif		//_DEBUG_ONECURVEDATAMNGR_
}

OneCurveDataMngr::~OneCurveDataMngr()
{
	if ( IsValid() )
	{
		for ( int ii = 0; ii < m_nCount; ii++ )
		{
			OneCurveData*		pOCD = GetAt(ii);
			ASSERT(NULL != pOCD);
			if ( !isValid(pOCD) )
				continue;

			free(pOCD->pX);
			free(pOCD->pY);
			if ( pOCD->pErr )
				free(pOCD->pErr );

		#ifdef		_DEBUG_ONECURVEDATAMNGR_
			out_int("Delete %d", ii);
		#endif		//_DEBUG_ONECURVEDATAMNGR_
		}
		free(m_pData);
	}
}

BOOL			OneCurveDataMngr::IsValid()
{
	return (NULL != m_pData) ? TRUE : FALSE;
}

int				OneCurveDataMngr::GetCount()
{
	return m_nCount;
}

BOOL			OneCurveDataMngr::Create(int nIndex, bool bErr, UINT nSize)
{
	if ( 0 == nSize || !IsValid() )
		return FALSE;
	
	OneCurveData*		pOCD = GetAt(nIndex);
	if ( NULL == pOCD )
	{
		ASSERT(FALSE);
		return FALSE;
	}
	
	if ( isValid(pOCD) )
		return FALSE;

	BOOL		bAllocSucc = TRUE;
	pOCD->nSize = nSize;
	if ( bAllocSucc )
	{
		pOCD->pX = (double*)malloc(nSize * sizeof(double));
		bAllocSucc &= (NULL != pOCD->pX) ? TRUE : FALSE;
	}
	if ( bAllocSucc )
	{
		pOCD->pY = (double*)malloc(nSize * sizeof(double));
		bAllocSucc &= (NULL != pOCD->pX) ? TRUE : FALSE;
	}
	if ( bAllocSucc & bErr )
	{
		pOCD->pErr = (double*)malloc(nSize * sizeof(double));
		bAllocSucc &= (NULL != pOCD->pX) ? TRUE : FALSE;
	}
	
	if ( !bAllocSucc )
	{
		if ( pOCD->pX )
			free(pOCD->pX);
		if ( pOCD->pX )
			free(pOCD->pY);
		if ( pOCD->pErr )
			free(pOCD->pErr);
		memset(pOCD, 0, sizeof(OneCurveData));
		return FALSE;
	}

#ifdef		_DEBUG_ONECURVEDATAMNGR_
	out_int("Create %d", nIndex);
#endif		//_DEBUG_ONECURVEDATAMNGR_
	return TRUE;
}

OneCurveData*	OneCurveDataMngr::GetAt(int nIndex)
{
	if ( !IsValid() || nIndex < 0 || nIndex >= m_nCount )
		return NULL;
	
	return m_pData + nIndex;
}

BOOL			OneCurveDataMngr::isValid(OneCurveData* pOCD)
{
	ASSERT(NULL != pOCD);
	if ( 0 == pOCD->nSize )
		return FALSE;
	
	ASSERT(pOCD->pX && pOCD->pY);
	return TRUE;
}
/// end IMPROVE_AVE_XY_AVOID_MEMORY_LEAKING

///---Sim 04-02-2009 QA80-13403 ADD_INDIVIDUAL_OP_VERSION_FOR_CONVERT_GUI_TREE
void MultipleVersion::AddVersion(LPCSTR lpcszSectionName, double dVersion)
{
	m_vsSections.Add(lpcszSectionName);
	m_vVersions.Add(dVersion);
}
double MultipleVersion::GetVersion(LPCSTR lpcszSectionName)
{
	double dVersion = DEFAULT_VERSION_NUMBER;
	int nIndex = m_vsSections.Find(lpcszSectionName);
	if ( nIndex >= 0 )
		dVersion = m_vVersions[nIndex];
	return dVersion;
}
enum
{
	INDX_SECTIONS = 0,
	INDX_VERSIONS,
	
	INDX_TOTALS,
};
#define CHAR_DELIMITER_VER_ATTR ';'
bool MultipleVersion::UpdateTreeAttribute(TreeNode& tr, LPCSTR lpcszAttributeName, bool bToTree)// = true
{
	if ( !tr || NULL == lpcszAttributeName )
		return false;
	
	string strAttributes;
	vector<string> vsAttributes;
	
	if ( bToTree )
	{
		vsAttributes.SetSize(INDX_TOTALS);
		
		vsAttributes[INDX_SECTIONS].SetTokens(m_vsSections);
		
		vector<string> vsVersions;
		convert_double_vector_to_string_vector(m_vVersions, vsVersions, m_vVersions.GetSize());
		vsAttributes[INDX_VERSIONS].SetTokens(vsVersions);
		
		strAttributes.SetTokens(vsAttributes, CHAR_DELIMITER_VER_ATTR);
		tr.SetAttribute(lpcszAttributeName, strAttributes);
	}
	else
	{
		tr.GetAttribute(lpcszAttributeName, strAttributes);
		strAttributes.GetTokens(vsAttributes, CHAR_DELIMITER_VER_ATTR);
		
		vsAttributes.SetSize(INDX_TOTALS); // Maybe attribute is none, or not suitable size
		
		vsAttributes[INDX_SECTIONS].GetTokens(m_vsSections);
		
		vsAttributes[INDX_VERSIONS].GetTokens(m_vVersions);				
	}
	
	return true;
}
///---END QA80-13403 ADD_INDIVIDUAL_OP_VERSION_FOR_CONVERT_GUI_TREE

///Kyle 06/01/2009 USE_COMMA_AS_DELIMITER_IN_GOS
string check_set_sys_decimal_char(LPCSTR lpcszNumList, LPCSTR lpcszOldDecimalChars)
{
	string strNumList(lpcszNumList);
	char chDecimalCharacter  = GetDecimalChar();
	strNumList.Replace(lpcszOldDecimalChars, chDecimalCharacter);
	return strNumList;
}
///End USE_COMMA_AS_DELIMITER_IN_GOS

///---Sim 09-25-2009 QA80-13783 PROTOTYPE_WITH_TWO_PRECISION_FOR_XYZ_REMOVE_DUPLICATE
int xyz_remove_duplicates(UINT n, double* x, double* y, double* z, int nMethod, double dPrecision)
{
#ifndef __NEW_XYZ_REMOVE_DUPLICATES_METHOD__
	return ocmath_xyz_remove_duplicates(n, x, y, z, nMethod, dPrecision);
#else // __NEW_XYZ_REMOVE_DUPLICATES_METHOD__
	return ocmath_xyz_remove_duplicates(n, x, y, z, nMethod, dPrecision, dPrecision);
#endif // __NEW_XYZ_REMOVE_DUPLICATES_METHOD__
}
///---END QA80-13783 PROTOTYPE_WITH_TWO_PRECISION_FOR_XYZ_REMOVE_DUPLICATE

///---Sim 09-25-2009 QA80-13783 CHANGE_PROTOTYPE_AND_ALGORITHM_FOR_EXAMINATION
int xyz_examine_data(UINT nSize, double* x, double* y, double* z, double dPrecision, 
		UINT* pVar, double* pXmin, double* pXStep, double* pXmax, double* pYmin, double* pYStep, 
		double* pYmax, bool bRemoveDuplicate, int nMethod)
{
#ifndef __NEW_XYZ_EXAMINE_METHOD__
	return ocmath_xyz_examine_data(nSize, x, y, z, dPrecision, pVar, pXmin, pXStep, pXmax, pYmin, pYStep, pYmax, bRemoveDuplicate, nMethod);
#else // __NEW_XYZ_EXAMINE_METHOD__
	return ocmath_xyz_examine_data(nSize, x, y, z, dPrecision, dPrecision, pVar, pXmin, pXStep, pXmax, pYmin, pYStep, pYmax, bRemoveDuplicate, nMethod);
#endif // __NEW_XYZ_EXAMINE_METHOD__
}
///---END QA80-13783 CHANGE_PROTOTYPE_AND_ALGORITHM_FOR_EXAMINATION
//----- CPY 7/18/09 QA80-13746 SCV_COL_FX_MENU_SHOW_SN_LN_CLEANUP
// should not put functions into system file unless really needed
/*
///Kyle 07/14/2009 QA80-13746 SHOWING_BOTH_LN_AND_SN_WITH_STANDARD_NOTATION_IN_THE_MENU_OF_SET_COLUMN_VALUE_DLG
string make_colSN_LN_str(LPCSTR lpcszSN, LPCSTR lpcszLN)
{
	string str;
	str.Format("Col(%s)", lpcszSN);
	
	string strLongName(lpcszLN);
	if(!strLongName.IsEmpty())
		str += ": " + strLongName;
	return str;
}

void parse_colSN_LN_str(LPCSTR lpcsz, string* pstrColSN, string* pstrLN)
{
	if(pstrColSN)
	{
		string str(lpcsz);
		int nIndex = str.Find(':');
		if(nIndex > 0)
			str = str.Left(nIndex);
		str.TrimRight();
		*pstrColSN = str;
	}
	if(pstrLN)
	{
		string str(lpcsz);
		int nIndex = str.Find(':');
		if(nIndex >= 0)
			str = str.Mid(nIndex+1);
		else
			str.Empty();
		str.TrimLeft();
		*pstrLN = str;
	}
}
///End SHOWING_BOTH_LN_AND_SN_WITH_STANDARD_NOTATION_IN_THE_MENU_OF_SET_COLUMN_VALUE_DLG
*/
//----- end SCV_COL_FX_MENU_SHOW_SN_LN_CLEANUP

///Kyle 07/20/2009 QA80-13746-P2 SCV_COL_COLUMN_BROWSER_AND_RANGE_BROWSER_SHOW_SN_LN_CLEANUP, moved from ColMatValues.c
bool is_str_good_for_col_func(LPCSTR lpcsz)
{
	string str(lpcsz);
	if(str.IsEmpty())
		return false;
	
	if(is_str_numeric(str))
		return false;
	LPSTR lpsz = find_any_of_characters_in_str(str,"\"([{}])");
	if(lpsz)
		return false;
	
	return true;
}
///End SCV_COL_COLUMN_BROWSER_AND_RANGE_BROWSER_SHOW_SN_LN_CLEANUP

///Jasmine 10/19/09 QA81-14478 MASK_DATA_OUSIDE_SD
int find_outliers_by_sd(const Column& cc, double nSD, vector<uint>& vnIndexes)
{
	vectorbase &vv = cc.GetDataObject();				
	vector vData;
	vData = vv;
	int nSize = vData.GetSize();
	
	int nN; 
	double dMean, dSD;
	ocmath_basic_summary_stats(nSize, vData, &nN, &dMean, &dSD);
	
	vData -= dMean;
	
	vnIndexes.SetSize(0);
	int nFind = vData.Find(MATREPL_TEST_GREATER|MATREPL_USE_ABSOLUTE_VALUE_IN_TEST, dSD * nSD, vnIndexes);
	return nFind;
}
///End MASK_DATA_OUSIDE_SD

///---- CPY Kyle 11/04/2009 QA80-14584 SCV_ADD_FITTING_FUNCTIONS

#ifdef _ADD_FITTING_FUNCTIONS_TO_SCV

typedef int (*FUNC_NLSF_FIND_FITTING_FUNCTIONS) (vector<string>& vsFuncNames, vector<string>& vsFuncArgs, LPCSTR lpcszCategory);

///Kyle 12/16/2009 QA80-14832 QUICK_FIT_EDIT_DIALOG_NEED_FUNC_LIST_WITHOUT_ARGUMENT
//int scan_fit_funcs(vector<string>& vsFunctions, LPCSTR lpcszCategory)
int scan_fit_funcs(vector<string>& vsFunctions, LPCSTR lpcszCategory, BOOL bGetArgList)
///End QUICK_FIT_EDIT_DIALOG_NEED_FUNC_LIST_WITHOUT_ARGUMENT
{
	vsFunctions.SetSize(0);
	FUNC_NLSF_FIND_FITTING_FUNCTIONS pfn_find_fitting_functions = Project.FindFunction("nlsf_find_fitting_functions", "OriginLab\\nlsf_utils.c", TRUE);
	if( !pfn_find_fitting_functions )
		return -1;
	
	vector<string> vsNames, vsArgs;
	int nFuncs = pfn_find_fitting_functions(vsNames, vsArgs, lpcszCategory);
	if(nFuncs < 0)
		return nFuncs;

	///Kyle 12/16/2009 QA80-14832 QUICK_FIT_EDIT_DIALOG_NEED_FUNC_LIST_WITHOUT_ARGUMENT	
	if( !bGetArgList )
	{
		vsFunctions = vsNames;
		return vsFunctions.GetSize();
	}
	///End QUICK_FIT_EDIT_DIALOG_NEED_FUNC_LIST_WITHOUT_ARGUMENT

	vsFunctions.SetSize(nFuncs);
	for(int ii = 0; ii < nFuncs; ii++)
		vsFunctions[ii] = vsNames[ii] + "(" + vsArgs[ii] + ")";

	return nFuncs;
}

#endif //_ADD_FITTING_FUNCTIONS_TO_SCV

///---- end SCV_ADD_FITTING_FUNCTIONS

//--- CPY 11/27/09 QA81-14661-P5 LEVEL_CROSSING_NEED_OPTION_TO_SKIP_NOISY_SPIKES
/*
nXType = 
enum{
	type_nearest,
	type_left,
	type_right,
};
*/
static bool _make_less_then_duration_list(int nDurationPts, vector<int>& vnDelList, const vector<int>& vnIndices)
{
	int npts = vnIndices.GetSize();
	vnDelList.SetSize(npts);
	vnDelList = 0;
	int nCounts = 0;
	for(int ii = 0; ii < npts-1; ii++)
	{
		if(vnIndices[ii+1] - vnIndices[ii] < nDurationPts)
		{
			vnDelList[ii+1] = vnDelList[ii] = 1;
			ii++;// must skip the next so it is not counted twice
			nCounts++;
		}
	}
	return nCounts? true:false;
}
///------ Folger 11/30/09 CENTRALIZE_CODE_ABOUT_FIND_ROOTS
//int	find_roots(const vector &vX, const vector &vY, vector &roots, vector<int> &vnDirection, int nDurationPts, vector<int> &vnXindices, int nXType)
int	find_roots(const vector &vX, const vector &vY, vector &roots, double dBase, vector<int> &vnDirection, int nDurationPts, vector<int> &vnXindices, int nXType)
///------ End CENTRALIZE_CODE_ABOUT_FIND_ROOTS
{
	///------ Folger 11/30/09 QA81-14661-P11 CORRECT_DURATION_POINT_CALCULATION_ALGORITHM
	//if(nDurationPts < 1)
		//return -10;//need to find proper err code
	//
	//int			ncrossing = 0;
	//int 		nSize = vX.GetSize();
	//vector		vZeroX(nSize/2);
	//vector<int> vnDir(nSize/2);// some large number but no need to be more then half of the source size
	//vector<int> vnIndices(nSize/2);
	//
	/////------ Folger 11/30/09 CENTRALIZE_CODE_ABOUT_FIND_ROOTS
	////int nRet = ocmath_emd_get_zero_crossing(nSize, vX, vY, &ncrossing, vZeroX, vnDir, vnIndices);	
	//vector		vxx, vyy;
	//vxx = vX;
	//vyy = vY - dBase;
	//int nRet = ocmath_emd_get_zero_crossing(nSize, vX, vyy, &ncrossing, vZeroX, vnDir, vnIndices);	
	/////------ End CENTRALIZE_CODE_ABOUT_FIND_ROOTS
	//if(nRet<0)
		//return nRet;	
	//if(ncrossing == 0)
		//return 0; // no crossing
	//vZeroX.SetSize(ncrossing);
	//vnDir.SetSize(ncrossing);
	//vnIndices.SetSize(ncrossing);
	//if(nDurationPts > 1 && ncrossing > 1)
	//{
		//if(vnIndices[0] >= vnIndices[ncrossing-1])
			//return -11; // must have increasing indices, if this indeed happen, we will need to fix it
		//vector<int> vnDelList;
		//if(_make_less_then_duration_list(nDurationPts, vnDelList, vnIndices))
		//{
			////must del from the end
			//for(int ii = ncrossing - 1; ii>= 0; ii--)
			//{
				//if(vnDelList[ii])
				//{
					//vZeroX.RemoveAt(ii);
					//vnDir.RemoveAt(ii);
					//vnIndices.RemoveAt(ii);
					//vxx.RemoveAt(vnDelList[ii]);
				//}
			//}
			//ncrossing = vZeroX.GetSize();// need to update this after del
		//}
	//}
	//if (roots)
		//roots = vZeroX;
	//if (vnDirection)
		//vnDirection = vnDir;
//
	//if (vnXindices)
	//{
		//vnXindices.SetSize(ncrossing);
		//ASSERT(vZeroX.GetSize() == ncrossing);
		//int nNearest[3];
		//int type_nearest = 0;
		//int type_left = 1;
		//int type_right = 2;
		//for (int ii = 0; ii < ncrossing; ++ii)
		//{
			//ocmath_find_nearest_index(vxx, nSize, vZeroX[ii], &nNearest[type_left], &nNearest[type_right], &nNearest[type_nearest]);
			/////------ Folger 11/28/09 QA81-14661-P6 LEVELCROSSING_XINDEX_OUTPUT_SHOULD_ONE_OFFSET
			////vnXindices[ii] = nNearest[nXType];
			//vnXindices[ii] = nNearest[nXType] + 1;
			/////------ End LEVELCROSSING_XINDEX_OUTPUT_SHOULD_ONE_OFFSET
		//}
	//}
	if(nDurationPts < 1)
		return FIND_ROOTS_INVALID_DURATION_POINTS;//need to find proper err code
	
	int			ncrossing = 0;
	int 		nSize = vX.GetSize();
	///------ Folger 11/30/09 QA81-14661-P10 ORIGIN_CRASH_IF_TOO_MANY_EXACT_CORRSING_POINTS
	vector		vZeroX(nSize);
	vector<int> vnDir(nSize);
	vector<int>	vnIndicesByCrossing(nSize);
	///------ End ORIGIN_CRASH_IF_TOO_MANY_EXACT_CORRSING_POINTS
	
	///------ Folger 12/19/09 QA81-14661-P14 XINDEX_IS_WRONG_ON_INPUT_DATA_WITH_MISSING_VALUE
	//vector		vyy;
	//vyy = vY - dBase;
	//int nRet = ocmath_emd_get_zero_crossing(nSize, vX, vyy, &ncrossing, vZeroX, vnDir);
	vector		vxx, vyy;
	vxx = vX, vyy = vY;
	vector		vJunk(vX.GetSize());
	trim_independent(vxx, vyy, vJunk);
	vyy -= dBase;
	int nRet = ocmath_emd_get_zero_crossing(nSize, vxx, vyy, &ncrossing, vZeroX, vnDir, vnIndicesByCrossing);
	///------ End XINDEX_IS_WRONG_ON_INPUT_DATA_WITH_MISSING_VALUE
	if(nRet<0)
		return nRet;	
	if(ncrossing == 0)
		return 0; // no crossing
	vZeroX.SetSize(ncrossing);
	vnDir.SetSize(ncrossing);
	
	vector<int> vnIndices(ncrossing);
	if ( !ocmath_is_monotonic(vxx, vxx.GetSize()) )
	{
		vnIndicesByCrossing.SetSize(ncrossing);
		vnIndices = vnIndicesByCrossing + 1;
	}
	else
	{
		int nNearest[3];
		int type_nearest = 0;
		int type_left = 1;
		int type_right = 2;
		for (int ii = 0; ii < ncrossing; ++ii)
		{
			ocmath_find_nearest_index(vX, nSize, vZeroX[ii], &nNearest[type_left], &nNearest[type_right], &nNearest[type_nearest]);
			vnIndices[ii] = nNearest[nXType] + 1;
		}
	}
	
	if(nDurationPts > 1 && ncrossing > 1)
	{
		if(vnIndices[0] >= vnIndices[ncrossing-1])
			return FIND_ROOTS_INVALID_XINDEX; // must have increasing indices, if this indeed happen, we will need to fix it
		vector<int> vnDelList;
		if(_make_less_then_duration_list(nDurationPts, vnDelList, vnIndices))
		{
			//must del from the end
			for(int ii = ncrossing - 1; ii>= 0; ii--)
			{
				if(vnDelList[ii])
				{
					vZeroX.RemoveAt(ii);
					vnDir.RemoveAt(ii);
					vnIndices.RemoveAt(ii);
				}
			}
			ncrossing = vZeroX.GetSize();// need to update this after del
		}
	}
	if (roots)
		roots = vZeroX;
	if (vnDirection)
		vnDirection = vnDir;
	if ( vnXindices )
		vnXindices = vnIndices;
	///------ End CORRECT_DURATION_POINT_CALCULATION_ALGORITHM	
	return ncrossing;
}
//--- end LEVEL_CROSSING_NEED_OPTION_TO_SKIP_NOISY_SPIKES

///------ Folger 12/29/09 QA81-14832 SET_GET_QUICK_FIT_SOURCE_PLOT_UID
BOOL		quick_fit_source_plot_info_access(QuickFitPlotInfo& stInfo, GraphLayer& gl, BOOL bGet/* = TRUE*/)
{
	Tree	tr;
	if ( !gl.GetBinaryStorage(QUICK_FIT_STORAGE_NAME, tr) )
		return FALSE;

	if ( bGet )
	{
		stInfo.nUID = okxf_quick_fit_source_plot_uid_access(&gl);

		TreeNode	trPlotRangeFrom = tr.PlotRangeFrom;
		TreeNode	trPlotRangeTo = tr.PlotRangeTo;
		stInfo.nFrom = trPlotRangeFrom ? trPlotRangeFrom.nVal : -1;
		stInfo.nTo = trPlotRangeTo ? trPlotRangeTo.nVal : -1;

		return stInfo.nUID > 0 && stInfo.nFrom >= 0;
	}

	tr.PlotRangeFrom.nVal = stInfo.nFrom;
	tr.PlotRangeTo.nVal = stInfo.nTo;
	gl.PutBinaryStorage(QUICK_FIT_STORAGE_NAME, tr);
	return okxf_quick_fit_source_plot_uid_access(&gl, stInfo.nUID, FALSE) == 0;
}
///------ End SET_GET_QUICK_FIT_SOURCE_PLOT_UID

///------ Folger 01/28/10 SHOW_SNAP_TO_DATAPLOT_ON_MAIN_DIALOG
BOOL	get_dataplot_range_description(string& strDescription, const DataPlot& dp, DWORD dwCntrl/* = 0*/)
{
	DataRange		dr;
	if ( !dp.GetDataRange(dr) || !dr )
		return FALSE;

	strDescription = dr.GetDescription(dwCntrl);
	return TRUE;
}
///------ End SHOW_SNAP_TO_DATAPLOT_ON_MAIN_DIALOG

///Kyle 03/12/2010 PICK_PEAK_SHARE_CODE_WITH_QUICK_FIT, moved from QuickFit.c
string get_value_by_format(const double dVal, int nDigits, const int nFormat, const int nSubFormat, LPCSTR lpcszCustomFmt)
{
	string strResult;
	if ( OKCOLTYPE_DATE == nFormat )
	{
		if ( LDF_OBJ_CUSTOM == nSubFormat )
		{
			char buffer[MAXLINE];
			date_to_str_custom(dVal, lpcszCustomFmt, buffer);
			strResult = buffer;
		}
		else
			strResult = get_date_str(dVal, nSubFormat);
	}
	else if ( OKCOLTYPE_TIME == nFormat )
	{
		strResult = get_time_str(dVal, nSubFormat);
	}
	else
		strResult = _convert_float_to_str(dVal, nDigits);
	
	return strResult;
}

static string _convert_float_to_str(double dVal, int nDigits = 0)
{
	string strFormat = "*";
	if( 0 != nDigits )
		strFormat += nDigits;
	
	return ftoa(dVal, strFormat);
}

string	get_value_by_format(const DataPlot& dp, double dVal, int nDigits/* = 0*/)
{
	string strResults;
	XYRange xySrc;
	Column colX;
	if ( dp && dp.GetDataRange(xySrc) && xySrc.GetXColumn(colX) )
	{
		int nFormat, nSubFormat;
		string strCustomFormat;
		nFormat = colX.GetFormat();
		nSubFormat = colX.GetSubFormat();
		strCustomFormat = colX.GetCustomDisplay();
		strResults = get_value_by_format(dVal, nDigits, nFormat, nSubFormat, strCustomFormat);
	}
	else
		strResults = _convert_float_to_str(dVal, nDigits);
	return strResults;
}

double	get_value_by_format(const DataPlot& dp, LPCSTR lpcszVal)
{
	double dDate = NANUM;
	XYRange xySrc;
	Column colX;
	if ( dp && dp.GetDataRange(xySrc) && xySrc.GetXColumn(colX) )
	{
		int nFormat, nSubFormat;
		string strCustomFormat;
		nFormat = colX.GetFormat();
		nSubFormat = colX.GetSubFormat();
		strCustomFormat = colX.GetCustomDisplay();
		if ( LDF_OBJ_CUSTOM == nSubFormat && OKCOLTYPE_DATE == nFormat )
		{
			str_to_date_custom(lpcszVal, strCustomFormat, &dDate);
			return dDate;
		}
		
		if ( OKCOLTYPE_DATE == nFormat )
		{
			dDate =  str_to_date(lpcszVal, nSubFormat);
			return dDate;
		}
		
		if ( OKCOLTYPE_TIME == nFormat )
		{
			dDate = str_to_time_ex(lpcszVal, nSubFormat);
			return dDate;
		}
	}
	
	return atof(lpcszVal);
}
///End PICK_PEAK_SHARE_CODE_WITH_QUICK_FIT


///Sophy 4/13/2010 QA81-15308 GET_GRAPHLAYER_OF_INPUTDATA_IN_XF_BODY
bool		get_graphlayer_from_input(const DataRange& iy, GraphLayer& glSrc, int* pnUID)
{
	if ( iy )
	{
		vector<uint> vnUIDs;
		if ( iy.GetPlots(vnUIDs) > 0 )
		{
			DataPlot dp;
			dp = (DataPlot)Project.GetObject(vnUIDs[0]);
			if ( dp )
			{
				if ( pnUID )
					*pnUID = vnUIDs[0];
				
				dp.GetParent(glSrc);
				return true;
			}
		}
	}
	return false;
}
///end GET_GRAPHLAYER_OF_INPUTDATA_IN_XF_BODY

/// Fisher 06/29/10 ORG-419	MINTERP2_XF_GOT_BAD_MISSING_VALUE_INTERP_RESULTS
void	get_matrix_xymap(const MatrixObject &im, vector &vx, vector &vy)
{
	double dxMin, dyMin, dxMax, dyMax, dxInc, dyInc;
	
	int nRows = im.GetNumRows(); 
	int nCols = im.GetNumCols();	
	im.GetXY(dxMin, dyMin, dxMax, dyMax);
	
	if( 1 >= nCols )
	{
		dxMin = 1.0;
		dxMax = (double)nCols;
		dxInc = 1.0;
	}
	else
		dxInc = (dxMax-dxMin)/(nCols-1);
	
	if( 1 == nRows )
	{
		dyMin = 1.0;
		dyMax = (double)nRows;
		dyInc = 1.0;
	}	
	else
		dyInc = (dyMax-dyMin)/(nRows-1);
	
	vx.Data(dxMin, dxMax, dxInc);
	vy.Data(dyMin, dyMax, dyInc);
}
/// End MINTERP2_XF_GOT_BAD_MISSING_VALUE_INTERP_RESULTS