/*------------------------------------------------------------------------------*
 * File Name:ANOVATwoWayRM.cpp													*
 * Creation: 																	*
 * Purpose: OriginC Source C file												*
 * Copyright (c) ABCD Corp.	2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010		*
 * All Rights Reserved															*
 * 																				*
 * Modification Log:															*
 * Iris 5/15/06 NEW_WAY_TO_CHECK_MISSING_FOR_STR								*
 *	CPY 10/14/06 SEPARATE_OUT_OPERATION_BASE_INTO_SEPARATE_HEADER				*
 *	Echo 2/25/07 ADD_ERROR_REPORT												*
 *	Cheney 2007-6-7 SHOULD_CHECK_EVENT_ID_IN_EVENT_FUNC							*
 *	Arvin 09/26/07 QA70-10421 WRONG_REPEAT_FACTOR_IN_REPORT_TABLE				*
 *	Echo 4/29/08	v8.0851	RM_ANOVA2_1RV_CHECK_DATA							*
 *	ML 6/13/2008 MULTIFACTOR_OP_ESCAPED_STRING									*
 *  Iris 2/25/2009 REPEAT_ID_CAUSE_JUNK_RANGE_STR_AFTER_SET_AU_TO_NONE			*
 *	Jacky 07/22/10 ORG-102 USER_CAN_SET_SIGNIFICANCE_LEVEL						*
 *------------------------------------------------------------------------------*/
 
////////////////////////////////////////////////////////////////////////////////////
// Including the system header file Origin.h should be sufficient for most Origin
// applications and is recommended. Origin.h includes many of the most common system
// header files and is automatically pre-compiled when Origin runs the first time.
// Programs including Origin.h subsequently compile much more quickly as long as
// the size and number of other included header files is minimized. All NAG header
// files are now included in Origin.h and no longer need be separately included.
//
// Right-click on the line below and select 'Open "Origin.h"' to open the Origin.h
// system header file.
#include <Origin.h>
#include <event_utils.h>
#include <report_utils.h>

#ifdef _FOR_SMART_LOADING_ONLY
#include "wksOperation.h" //---- CPY 10/14/06 SEPARATE_OUT_OPERATION_BASE_INTO_SEPARATE_HEADER
#include <analysis_utils.h>
#include <stats_utils.h>
#include "stats_guis.h"
#include "stats_operations.h"
#include "nlsf_utils.h" /// Iris 7/08/2008 CLEAN_DUP_CALC_AVE_DATA_CODE_IN_NLSF_PREVIEW_AND_OP
#include "graph_utils.h" //---- Iris 11/19/2008 v8.0975 QA80-12591-P2 FIX_APPARENT_FIT_ON_GRAPH_CUSTOM_RANGE_GET_INCORRECT_X
#endif

#include <xfutils.h>
#include <statEx_utils.h>
#include "StatsOpCommon.h"
#include "StatsOpBase.h"
#include "ANOVATwoWay.h"
#include "ANOVATwoWayRM.h" /// Max 11/17/06 REMOVE_RM_TO_AVOID_LOAD_PROBLEM

////////////////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////
// Start your functions here.
///Echo 2/25/07 ADD_ERROR_REPORT

static int anovarmtwoway_event1(TreeNode& tr, int nRow, int nEvent, DWORD& dwEnables, LPCSTR lpcszNodeName, WndContainer& getNCountainer, string& strAux, string& strErrMsg)
{
	DECLARE_BUTTON_ENABLES   //support more buttons enable/disable
	
	///Cheney 2007-6-7 SHOULD_CHECK_EVENT_ID_IN_EVENT_FUNC	
	///Cheney 2007-7-6 SHOULD_CHECK_ERR_WHEN_THEME_CHANGE_ALSO
	//if(GETNE_ON_VALUE_CHANGE == nEvent  || GETNE_ON_INIT == nEvent)
	if(GETNE_ON_VALUE_CHANGE == nEvent  || GETNE_ON_INIT == nEvent || GETNE_ON_THEME == nEvent)
	///end SHOULD_CHECK_ERR_WHEN_THEME_CHANGE_ALSO
	///end SHOULD_CHECK_EVENT_ID_IN_EVENT_FUNC
	{
		DataRange drData;
		drData.Create();
		bool bRaw = tr.InputData.Use;
		int nOption = bRaw ? DRTREE_ANOVA_TWO_WAY_RAW : DRTREE_ANOVA_TWO_WAY_INDEXED;
		drData.SetTree(tr.InputData, nOption);
		int nErr;
		
		// Check Input Data Range
		int nLevels;
		if (tr.InputData.Factor0.Levels && tr.InputData.Factor1.Levels) 
		{
			bool bRepeatA = tr.InputData.Factor0.Repeat.nVal;
			bool bRepeatB = tr.InputData.Factor1.Repeat.nVal;
			int nLevelA = (bRepeatA) ? tr.InputData.Factor0.Levels.GetNodeCount() : 1;
			int nLevelB = (bRepeatB) ? tr.InputData.Factor1.Levels.GetNodeCount() : 1;
			nLevels = (bRepeatA && bRepeatB) ? nLevelA * nLevelB : nLevelA + nLevelB;
			
			///Echo 4/29/08	v8.0851	RM_ANOVA2_1RV_CHECK_DATA
			if (!(bRepeatA && bRepeatB))
				nErr = _check_anova_rm2_one_rv_data(drData);
			///end RM_ANOVA2_1RV_CHECK_DATA
		}
		if (nErr == CER_NO_ERROR)
			nErr = bRaw ? check_data_in_var(drData, nLevels, true) : _check_anova_rm_2_data(drData);
		
		
		// check MeansComp sig level
		if ( tr.MeansComp.Use && !check_sig_level( tr.MeansComp.SigLevel.dVal) )
		{
			nErr = CER_INVALID_SIG_LEV;
		}
		
		//--- Jacky 07/22/10 ORG-102 USER_CAN_SET_SIGNIFICANCE_LEVEL 
		if(CER_NO_ERROR == nErr && !check_sig_level(tr.alpha.dVal))
		{
			nErr = CER_INVALID_SIG_LEV;
		}
		//--- end USER_CAN_SET_SIGNIFICANCE_LEVEL
		
		//Check at least 1 repeated measure
		if(tr.InputData.Factor0.Repeat && tr.InputData.Factor1.Repeat)
		{
			if ( bRaw && 0==tr.InputData.Factor0.Repeat.nVal && 0==tr.InputData.Factor1.Repeat.nVal )
			{
				nErr = CER_AT_LEAST_1_RM_FACTOR;
			}
		}
		///Sophy 4/11/2008 CHECK_REPORT_DATA_BOOK_NAME_DIFFERENT
		string strRet = "";
		if( CER_NO_ERROR == nErr)
		{
			nErr = check_report_book_curve_book( tr, strRet);
		}
		///end CHECK_REPORT_DATA_BOOK_NAME_DIFFERENT
		///Sophy 4/14/2008 ADD_OUTPUT_ERR_STRING
		if (nErr != CER_NO_ERROR)
		{
			bOKEnable = false;
			strErrMsg = nErr;
			if( !strRet.IsEmpty() )
			{
				strErrMsg += ":" + strRet; 
			}
		}
		///end ADD_OUTPUT_ERR_STRING
		
		/// Iris 5/27/2008 UPDATE_REPORT_BOOK_SHEET_COMBO_ON_SOURCE_CHANGE, if source book/sheet changed, source book/sheet name should update relative
		#ifdef _MOVE_FIT_OUTPUT_TO_OC_CLASS 
		OutputGUIManagerBase* 	pOutputManager = get_output_GUI_manager_pointer(tr);
		if(pOutputManager)
			pOutputManager->UpdateOutputOnDataChange(tr);
		#endif //_MOVE_FIT_OUTPUT_TO_OC_CLASS
		///end UPDATE_REPORT_BOOK_SHEET_COMBO_ON_SOURCE_CHANGE

	}
	return true;
	

}

///Echo 4/29/08	v8.0851	RM_ANOVA2_1RV_CHECK_DATA
static int _check_anova_rm2_one_rv_data(DataRange& drData)
{
	if ( !drData )
		return CER_INVALID_TREENODE;
	
	int nSize;
	for (int ii = 0; ii < drData.GetNumData(); ii++)
	{
		vector vData;
		DWORD dwRules = DRR_NO_WEIGHTS;
		DWORD dwPlotID;
		drData.GetData(dwRules, ii, &dwPlotID, NULL, &vData);
		if (ii == 0) nSize = vData.GetSize();
		if (vData.GetSize() != nSize)
			return CER_NOT_1_DATA_PER_CELL;
	}
	
	return CER_NO_ERROR;
	
}
///END RM_ANOVA2_1RV_CHECK_DATA

static int _check_anova_rm_2_data(DataRange& drData)
{
	DWORD 			dwRules = DRR_RM_ANOVA;
	
	Worksheet		wksFirstRange;
	int nn = drData.GetNumData(dwRules, NULL, &wksFirstRange, NULL, NULL);
	if (nn == RMDATA_ERR_TWO_WAY_MISSING_VALUES_FOUND || nn == RMDATA_ERR_TWO_WAY_GENERAL_ILLEGAL_DATA)
	{
		return CER_NOT_1_DATA_PER_CELL;
	}
	if (nn < 0 && nn != RMDATA_ERR_TWO_WAY_MULTIPLE_SUBJECT_DATA)	
	{
		return CER_NOT_RM_ANOVA2_DATA;
	}	

	int			nOutcome, numFactorVals1, numFactorVals2;
	vector<string>	vstrRepeatedFactorVals, vstrRepeatedOrNonrepeatedFactorVals;
	matrix		mData;
	nn = drData.GetDataRMTwoWayANOVA(dwRules, nOutcome, numFactorVals1, numFactorVals2, &mData, &vstrRepeatedFactorVals, &vstrRepeatedOrNonrepeatedFactorVals);
	switch (nOutcome)
	{
	case RMDATA_ERR_ONE_WAY_MISSING_VALUES_FOUND:
	case RMDATA_ERR_TWO_WAY_GENERAL_ILLEGAL_DATA:
		return CER_NOT_1_DATA_PER_CELL;
		break;
	case RMDATA_ERR_TWO_WAY_MULTIPLE_SUBJECT_DATA:
		break;
	case RMDATA_ERR_ONE_WAY_MISSING_SUBJECT_DATA:
		return CER_NOT_RM_ANOVA2_DATA;	
	}
	
	if ( (nOutcome < 0 && nOutcome != RMDATA_ERR_TWO_WAY_MULTIPLE_SUBJECT_DATA) || nn < 0)	
	{
		return CER_NOT_RM_ANOVA2_DATA;
	}
	if (mData.GetNumRows() < ANOVA2_MIN_FACTOR_SUBJECT || mData.GetNumCols() < ANOVA2_MIN_FACTOR_SUBJECT)
	{
		return CER_TO_FEW_FACTOR;
	}
		
	
	return CER_NO_ERROR;
	
	
}

//virtual 
PEVENT_GETN ANOVATwoWayRM::GetNewEventFunction()
{
	return anovarmtwoway_event1;
}
///end ADD_ERROR_REPORT

///Arvin 09/26/07 QA70-10421 WRONG_REPEAT_FACTOR_IN_REPORT_TABLE
bool	ANOVATwoWayRM::GetFactors(TreeNode &trOp, int index, const vector<string> &vstrFactors)
{
	int nDRTreeOption = GetDRTreeOption(trOp);
	if (DRTREE_ANOVA_TWO_WAY_RAW == nDRTreeOption)
		return ANOVAReportOperation::GetFactors(trOp, index, vstrFactors);
	
	if(NULL != vstrFactors && 0 != vstrFactors.GetSize())
	{	
		string strTemp = vstrFactors[0];
		if( -1 == m_vsFactors.Find(strTemp) )
			m_vsFactors.Add(strTemp);
	}
	
	int nFactor = 0, nFactor2 = 1;
	if(m_nRV == RMDATA_TWO_WAY_ONLY_SECOND_FACTOR_REPEATED)
	{
		nFactor = 1;
		nFactor2 = 0;
	}
	TreeNode	trInput = trOp.GUI.InputData;
	GetFactorNameForIndexed(trInput, m_strFactor, IsOneWay(), nFactor);
	GetFactorNameForIndexed(trInput, m_strFactor2, IsOneWay(), nFactor2);
	return true;
}
///END WRONG_REPEAT_FACTOR_IN_REPORT_TABLE
	
//******the first factor always be repeated factor here; nSizeFactor is the size of repeated factor, nSizeFactor2 is the size of non repeated factor******
int     ANOVATwoWayRM::ExtractIndexedData(TreeNode &trOp, DataRange& dr, matrix& mData, int& nSizeSubject, int& nSizeFactor, int& nSizeFactor2)
{
	DWORD			dwRules = GetDataRules(trOp);
	Worksheet		wksFirstRange;
	int nn = dr.GetNumData(dwRules, NULL, &wksFirstRange, NULL, NULL);
	if (nn <= 0)
	{
		return -1;
	}		

	int			nOutcome, numFactorVals1, numFactorVals2;
	vector<string>	vstrRepeatedFactorVals, vstrRepeatedOrNonrepeatedFactorVals;
	nn = dr.GetDataRMTwoWayANOVA(dwRules, nOutcome, numFactorVals1, numFactorVals2, &mData, &vstrRepeatedFactorVals, &vstrRepeatedOrNonrepeatedFactorVals);
	if (nn < 0 || nOutcome < 0)	
	{
		return -2;
	}
	
	m_nRV = nOutcome;
	switch (nOutcome)
	{
	case RMDATA_TWO_WAY_BOTH_FACTORS_REPEATED:
		//_get_factors(vstrRepeatedOrNonrepeatedFactorVals, m_vsFactors2);
		m_vsFactors2 = vstrRepeatedOrNonrepeatedFactorVals;
		remove_repeat_item(m_vsFactors2);
		break;
		
	case RMDATA_TWO_WAY_ONLY_FIRST_FACTOR_REPEATED:
	case RMDATA_TWO_WAY_ONLY_SECOND_FACTOR_REPEATED:
		if( !_get_non_repeated_data(vstrRepeatedOrNonrepeatedFactorVals, m_vNonRVData) )
			return -2;
		for(int ii=0; ii<vstrRepeatedOrNonrepeatedFactorVals.GetSize(); ii++)
		{
			if( -1 == m_vsFactors2.Find(vstrRepeatedOrNonrepeatedFactorVals[ii]) )
				m_vsFactors2.Add(vstrRepeatedOrNonrepeatedFactorVals[ii]);
		}		
		break;		

	default:
		return -2;
	}
	
	GetFactors(trOp); //only to get factor name here
	
	//get factor size
	nSizeFactor = RMDATA_TWO_WAY_ONLY_SECOND_FACTOR_REPEATED == nOutcome? numFactorVals2 : numFactorVals1;
	nSizeFactor2 = RMDATA_TWO_WAY_ONLY_SECOND_FACTOR_REPEATED == nOutcome ?numFactorVals1 : numFactorVals2;
	
	//get factor ilevel names
	//_get_factors(vstrRepeatedFactorVals, m_vsFactors);
	m_vsFactors = vstrRepeatedFactorVals;
	remove_repeat_item(m_vsFactors);
	
	m_nGroupSize = mData.GetNumCols();	
	nSizeSubject = mData.GetNumRows();
	
	return 0;
}

int     ANOVATwoWayRM::ExtractRawData(TreeNode &trOp, DataRange& dr, matrix& mData, int& nSizeSubject, int& nSizeFactor, int& nSizeFactor2)
{	
	int nRepeat1 = trOp.GUI.InputData.Factor0.Repeat.nVal;
	int nRepeat2 = trOp.GUI.InputData.Factor1.Repeat.nVal;
	
	if( nRepeat1 && nRepeat2)
		m_nRV = RMDATA_TWO_WAY_BOTH_FACTORS_REPEATED;
	else
	{
		if(0 == nRepeat1 && 0 == nRepeat2)
		{
			error_report(_L("Error in ExtractRawData: two factors are non-repeated!"));
			return -1;
		}			
		if(1 == nRepeat1)
			m_nRV = RMDATA_TWO_WAY_ONLY_FIRST_FACTOR_REPEATED;
		if(1 == nRepeat2)
			m_nRV = RMDATA_TWO_WAY_ONLY_SECOND_FACTOR_REPEATED;
	}	
	
	if(m_nRV == RMDATA_TWO_WAY_BOTH_FACTORS_REPEATED)
	{
		int nRet = ANOVARMReportOperation::ExtractRawData(trOp, dr, mData, nSizeSubject, nSizeFactor, nSizeFactor2);
		if(0 == nRet)
			m_nGroupSize = mData.GetNumCols();	
		return nRet;
	}

	
	DWORD	dwRules = GetDataRules(trOp);
	int nNumData = dr.GetNumData(dwRules);	
	if (nNumData <= 0)
	{
		error_report(_L("Error in ExtractRawData: the number of data is less equal 0"));
		return -2;
	}
	
	vector			vData;
	DWORD			dwPlotUID;
	vector<string>	vstrFactors;
	vector<string>	vsNonRVFactors;
	vector<string>  vsNonRVData;
	vector			vOneData;
	int				nData = 0;
	
	for (int ii = 0; ii < nNumData; ii++)
	{
		int	nRet = dr.GetData(dwRules, ii, &dwPlotUID, NULL, &vData, NULL, NULL, &vstrFactors);
		if(nRet < 0)
		{
			error_report(_L("Error in ExtractRawData: fail to get data"));
			return -3;
		}		
		
		string strFactor = vstrFactors[0];
		int nFind = vsNonRVFactors.Find(strFactor);
		bool bFirstData = -1 == nFind? true : false;
		bool bOneNewData = 0 == nFind? true : false;
		
		if(bFirstData)
		{			
			vsNonRVFactors.Add(strFactor); //to get non repeated factor level's name
			
			//to get non repeated factor data
			for(int ii=0; ii<vData.GetSize(); ii++)
				vsNonRVData.Add(strFactor);				
			
			//get first raw data
			vOneData.Append(vData);
			
			nSizeFactor2 = vsNonRVFactors.GetSize();
			nSizeFactor = nNumData / nSizeFactor2;			
		}
		else
		{		
			if(bOneNewData)
			{		
				mData.SetSize(vOneData.GetSize(), nData+1, true);
				mData.SetColumn(vOneData, nData++); //get data
				vOneData.RemoveAll();
			}
			vOneData.Append(vData);
		}
		
		GetFactors(trOp, ii, vstrFactors); //get the first factor's level name		
	}
	mData.SetSize(vOneData.GetSize(), nData+1, true);
	mData.SetColumn(vOneData, nData++); //get data
	
	
	if( !_get_non_repeated_data(vsNonRVData, m_vNonRVData) )
	{
		error_report(_L("Error in ExtractRawData: fail to get non repeated data!"));
		return -4;
	}
	
	m_vsFactors2 = vsNonRVFactors;
	nSizeSubject = mData.GetNumRows();

	
	return 0;
}

void 	ANOVATwoWayRM::ConstructOutputTable(TreeNode& trOperation)
{	
	TreeNode 	trOut = trOperation.Calculation; 
	
	trOut.MultiTest.ID = IDST_RM_ANOVA_MULTI_TEST;
	trOut.SphericityTest.ID = IDST_RM_ANOVA_SPHERICITY_TEST;	
	trOut.ANOVAWS.ID = IDST_RM_ANOVA_WS_ANOVA;
	trOut.ANOVABS.ID = IDST_RM_ANOVA_BS_ANOVA;
	trOut.FactorA.ID = IDST_RM_ANOVA_ONE_DATASET;
	trOut.FactorB.ID = IDST_RM_ANOVA_ONE_DATASET + 1;
	trOut.Inter.ID = IDST_RM_ANOVA_ONE_DATASET + 2;
	
}

static bool _get_non_repeated_data(const vector<string>& vs, vector<int>& vn)
{
	vn.RemoveAll();	
	
	vector<string> vsTemp;
	for(int n=0 ; n< vs.GetSize(); n++)
	{
		string str = vs[n];
		
		//check if include missing data
		/// Iris 5/15/06 NEW_WAY_TO_CHECK_MISSING_FOR_STR
		//double dd = atof(str);
		//if(is_missing_value(dd))
			//return false;
		if("--" == str)
			return false;
		///End NEW_WAY_TO_CHECK_MISSING_FOR_STR		
		
		int nPlc = vsTemp.Find(str);
		if(-1 == nPlc)
		{
			nPlc = vsTemp.Add(str);
		}
		vn.Add(nPlc+1); //offset is 1
	}
	return true;
}

void _get_interaction_factors(const vector<string>& vsFactors, const vector<string>& vsFactors2, vector<string>& vsInterLabels1, vector<string>& vsInterLabels2)
{
	for(int ii=0; ii<vsFactors.GetSize(); ii++)
	{
		for(int jj=0; jj<vsFactors2.GetSize(); jj++)
		{
			vsInterLabels1.Add(vsFactors[ii]);
			vsInterLabels2.Add(vsFactors2[jj]);
		}
	}
}

//static void _get_factors(const vector<string>& vs, vector<string>& vsFactors)
//{
	//if(0 == vs.GetSize())
		//return;
	//
	//vsFactors.SetSize(0);
	//for(int ii=0; ii<vs.GetSize(); ii++)
	//{
		//if( -1 == vsFactors.Find(vs[ii]) )
		//{
			//vsFactors.Add(vs[ii]);
		//}
	//}
	//
//}


/// ML 6/13/2008 MULTIFACTOR_OP_ESCAPED_STRING
string	make_double_opesc_string(LPCSTR lpcszFirst, LPCSTR lpcszSecond, LPCSTR lpcszBetween)
{
	string		str;
	
	string		strFirst = lpcszFirst;
	int			nOpFirst = strFirst.Find(OPERATION_ESCAPE_STRING_PREFIX);
	
	string		strSecond = lpcszSecond;
	int			nOpSecond = strSecond.Find(OPERATION_ESCAPE_STRING_PREFIX);
	
	if (0 == nOpFirst && 0 == nOpSecond)
	{
		int		nPrefixLength = strlen(OPERATION_ESCAPE_STRING_PREFIX);

		int		nn = strFirst.GetLength() - nPrefixLength;
		string	strTemp1 = strFirst.Right(nn);
		
		nn = strSecond.GetLength() - nPrefixLength;
		string	strTemp2 = strSecond.Right(nn);

		if ( lpcszBetween ) 
			str.Format("%s(%s) (\"%s\") (%s)", OPERATION_ESCAPE_STRING_PREFIX, strTemp1, lpcszBetween, strTemp2);	// put literals inside quotes
		else
			str.Format("%s(%s) (%s)", OPERATION_ESCAPE_STRING_PREFIX, strTemp1, strTemp2);
	}
	else
	{
		// The old way:
		str += lpcszFirst;
		if (lpcszBetween)
			str += lpcszBetween;
		
		str += lpcszSecond;
	}
	
	return str;
}
/// end MULTIFACTOR_OP_ESCAPED_STRING



bool	ANOVATwoWayRM::CalcOneData(TreeNode &trOp, int nSizeSubject, int nSizeFactor, int& nSizeFactor2)
{	
	//define and init parameters 					
	int 			ncntrl1, ncntrl2;
	MeanCompMethod 	stMethods;
	GetMeanCompMethod(trOp, stMethods, ncntrl1, ncntrl2, false);
	
	int 		iInteraction = trOp.GUI.Interactions.nVal;
	double 		dAlpha = trOp.GUI.MeansComp.SigLevel.dVal;
	int 		nRVGroupSize = nSizeFactor;
	int			nNonRVGroupSize = nSizeFactor2;
	
	RMANOVADescStats  *pRVDescStats; uint nSizeRVDescStat = nRVGroupSize;
	pRVDescStats = (RMANOVADescStats*)calloc(nSizeRVDescStat, sizeof(RMANOVADescStats));
	
	RMANOVADescStats  *pNonRVDescStats; uint nSizeNonRVDescStat = nNonRVGroupSize;
	pNonRVDescStats = (RMANOVADescStats*)calloc(nSizeNonRVDescStat, sizeof(RMANOVADescStats));
	
	RMANOVADescStats  *pInterDescStats; uint nSizeInterDescStat = nRVGroupSize * nNonRVGroupSize;
	pInterDescStats = (RMANOVADescStats*)calloc(nSizeInterDescStat, sizeof(RMANOVADescStats));
	
	RMANOVATwoWayOneRV RMANOVAOneRVTable;
	RMANOVATwoWayTwoRV RMANOVATwoRVTable; 
	
	MeanCompStats *pRVMeanCompStats; uint nSizeRVMeanComp = GetMeanCompTableSize(&nRVGroupSize);
	pRVMeanCompStats = (MeanCompStats*)calloc(nSizeRVMeanComp, sizeof(MeanCompStats));
	
	MeanCompStats *pNonRVMeanCompStats; uint nSizeNonRVMeanComp = GetMeanCompTableSize(NULL, &nNonRVGroupSize);
	pNonRVMeanCompStats = (MeanCompStats*)calloc(nSizeNonRVMeanComp, sizeof(MeanCompStats));
	
	MeanCompStats *pFixNonRVMeanCompStats; uint nSizeFixNonRVMeanComp = GetMeanCompTableSize(&nRVGroupSize, &nNonRVGroupSize, iInteraction);
	pFixNonRVMeanCompStats = (MeanCompStats*)calloc(nSizeFixNonRVMeanComp, sizeof(MeanCompStats));	
	
	MeanCompStats *pFixRVMeanCompStats; uint nSizeFixRVMeanComp = GetMeanCompTableSize(&nNonRVGroupSize, &nRVGroupSize, iInteraction);
	pFixRVMeanCompStats = (MeanCompStats*)calloc(nSizeFixRVMeanComp, sizeof(MeanCompStats));
	
	MultiTestTable RV1MultiStats;
	MultiTestTable RV2MultiStats;
	MultiTestTable InterMultiStats;
	
	SphericityStats RV1SphericStats;
	SphericityStats RV2SphericStats;
	SphericityStats InterSphericStats;
	
	EpsilonStats RV1EpsilonStats;
	EpsilonStats RV2EpsilonStats;
	EpsilonStats InterEpsilonStats;
	
	
	// do calculation for one repeated and two repeated data
	int 	nRet;
	bool 	bBothRV = false;
	
	switch (m_nRV)
	{
	case RMDATA_TWO_WAY_ONLY_FIRST_FACTOR_REPEATED:
	case RMDATA_TWO_WAY_ONLY_SECOND_FACTOR_REPEATED:
		bBothRV = false;
		nRet = stats_rm_anova_two_way_one_rv(stMethods,	iInteraction, m_vData, m_vNonRVData, nSizeSubject,
												nRVGroupSize, nNonRVGroupSize, dAlpha, ncntrl1, ncntrl2, 
												pRVDescStats, nSizeRVDescStat,
												pNonRVDescStats, nSizeNonRVDescStat,
												pInterDescStats, nSizeInterDescStat,
												&RMANOVAOneRVTable,
												pRVMeanCompStats, nSizeRVMeanComp,
												pNonRVMeanCompStats, nSizeNonRVMeanComp,
												pFixNonRVMeanCompStats, nSizeFixNonRVMeanComp,
												pFixRVMeanCompStats, nSizeFixRVMeanComp,
												&RV1MultiStats, &InterMultiStats,
												&RV1SphericStats, 
												&RV1EpsilonStats);
		break;
		
	case RMDATA_TWO_WAY_BOTH_FACTORS_REPEATED:
		bBothRV = true;
		uint nSizeRV2DescStat = nSizeNonRVDescStat;
		uint nSizeRV2MeanComp = nSizeNonRVMeanComp;
		uint nSizeFixRV2MeanComp = nSizeFixNonRVMeanComp;
		nRet = stats_rm_anova_two_way_two_rv(stMethods,	iInteraction, m_vData, nSizeSubject, m_nGroupSize,
												nSizeFactor, nSizeFactor2, dAlpha, ncntrl1, ncntrl2, 
												pRVDescStats, nSizeRVDescStat,
												pNonRVDescStats, nSizeRV2DescStat,
												pInterDescStats, nSizeInterDescStat,
												&RMANOVATwoRVTable,
												pRVMeanCompStats, nSizeRVMeanComp,
												pNonRVMeanCompStats, nSizeRV2MeanComp,
												pFixNonRVMeanCompStats, nSizeFixRV2MeanComp,
												pFixRVMeanCompStats, nSizeFixRVMeanComp,
												&RV1MultiStats, &RV2MultiStats, &InterMultiStats,
												&RV1SphericStats, &RV2SphericStats, &InterSphericStats,
												&RV1EpsilonStats, &RV2EpsilonStats, &InterEpsilonStats);
		break;	
	default:
		nRet = -1;  //any one error codes to return false
		break;
	}
	
	///Cheney 2007-5-21 NEED_UPDATE_ERROR_CHECKING_OF_REPORT_TABLE_FOR_OPERATION_BASED_TOOLS
	CheckANOVARMErr(nRet);
	///end NEED_UPDATE_ERROR_CHECKING_OF_REPORT_TABLE_FOR_OPERATION_BASED_TOOLS
											
	//add report tables
	bool 	bRtn;
	if( NAG_ANOVA_RM_NO_ERR == nRet || 
		NAG_ANOVA_RM_WARNNING_CANNOT_CALC_MAUCHLY_TEST == nRet ||
		NAG_ANOVA_RM_WARNNING_CANNOT_CALC_MULTI_TEST == nRet ||
		NAG_ANOVA_RM_WARNNING_CANNOT_CALC_MAUCHLY_OR_MULTI_TEST == nRet )
	{
		bRtn = true;
	
		bool 		bBothRV = RMDATA_TWO_WAY_BOTH_FACTORS_REPEATED==m_nRV? true : false;
		
		//--Multi Test table		
		if(NAG_ANOVA_RM_WARNNING_CANNOT_CALC_MULTI_TEST != nRet && 
			NAG_ANOVA_RM_WARNNING_CANNOT_CALC_MAUCHLY_OR_MULTI_TEST != nRet)
		{
			AddMultiTestTable(trOp, RV1MultiStats, 0, m_strFactor);
			if(bBothRV)
			{
				AddMultiTestTable(trOp, RV2MultiStats, 1, m_strFactor2);
			}
			if(iInteraction)
			{
				/// ML 6/13/2008 MULTIFACTOR_OP_ESCAPED_STRING
				//AddMultiTestTable(trOp, InterMultiStats, 2, m_strFactor + " * " + m_strFactor2);
				AddMultiTestTable(trOp, InterMultiStats, 2, make_double_opesc_string(m_strFactor, m_strFactor2, " * "));
				/// end MULTIFACTOR_OP_ESCAPED_STRING
			}
		}
		else
		{
			AddMultiTestErrorTable(trOp);
		}
		
		//--Mauchly's Test table
		//also should output this table even though warnning
		bool bWarnning = false;
		if(NAG_ANOVA_RM_WARNNING_CANNOT_CALC_MAUCHLY_TEST == nRet || 
			NAG_ANOVA_RM_WARNNING_CANNOT_CALC_MAUCHLY_OR_MULTI_TEST == nRet)
		{
			bWarnning = true;
		}		
		AddSphericityTestTable(trOp, RV1SphericStats, RV1EpsilonStats, bWarnning, 0, m_strFactor);
		if(bBothRV)
		{
			AddSphericityTestTable(trOp, RV2SphericStats, RV2EpsilonStats, bWarnning, 1, m_strFactor2);
			if(iInteraction)
			{
				/// ML 6/13/2008 MULTIFACTOR_OP_ESCAPED_STRING
				//AddSphericityTestTable(trOp, InterSphericStats, InterEpsilonStats, bWarnning, 2, m_strFactor + " * " + m_strFactor2);
				AddSphericityTestTable(trOp, InterSphericStats, InterEpsilonStats, bWarnning, 2, make_double_opesc_string(m_strFactor, m_strFactor2, " * "));
				/// end MULTIFACTOR_OP_ESCAPED_STRING
			}
		}
		
		
		//--ANOVA table
		RMANOVARow*		pstRMANOVARows;
		UINT 			nSize = bBothRV? RMANOVATWOWAYTWORV_LAST_ITEM : RMANOVATWOWAYONERV_LAST_ITEM;		
		pstRMANOVARows = (RMANOVARow*)calloc(nSize, sizeof(RMANOVARow));
		
		for(int ii=0; ii<nSize; ii++)
		{
			pstRMANOVARows[ii] = bBothRV? RMANOVATwoRVTable.AnovaTable[ii] : RMANOVAOneRVTable.AnovaTable[ii];
		}
		 	
		AddANOVATable(trOp, pstRMANOVARows, nSize, m_strFactor, m_strFactor2, true, iInteraction, bBothRV);
		free(pstRMANOVARows);
		
		/// Iris 05/10/2007 v8.0615 REMOVE_BRANCH_CHECK_BOX
		//bool			bAllStats = trOp.GUI.DescStats.Use;
		///end REMOVE_BRANCH_CHECK_BOX
		TreeNode		trTable;
		//-- add Statistics and Mean Comparion table for the FIRST factor
		int			nFactorIndex = 1;
		TreeNode 	trFirstFactor = tree_check_get_node(trOp.Calculation, "FactorA", IDST_RM_ANOVA_ONE_DATASET, STR_LABEL_ATTRIB, m_strFactor);		
		/// Iris 05/10/2007 v8.0615 REMOVE_BRANCH_CHECK_BOX
		//if(trOp.GUI.DescStats.FactorA.nVal && bAllStats)
		if(trOp.GUI.DescStats.FactorA.nVal)
		///end REMOVE_BRANCH_CHECK_BOX
		{
			//trTable = tree_check_get_node(trFirstFactor, "Statistics", IDST_RM_ANOVA_DEST_STATS, STR_LABEL_ATTRIB, _L(TABLE_RM_DEST_STATS));// desc stats table
			trTable = tree_check_get_node(trFirstFactor, "Statistics", IDST_RM_ANOVA_DEST_STATS, STR_LABEL_ATTRIB, TABLE_RM_DEST_STATS);// desc stats table
			AddDestStatsTable(trOp, trTable, pRVDescStats, nSizeRVDescStat, m_vsFactors);		
		}
		
		//trTable = tree_check_get_node(trFirstFactor, "MeansComp", IDST_RM_ANOVA_MEAN_COMP, STR_LABEL_ATTRIB, _L(TABLE_RM_MEAN_COMP)); //mean comparision table
		trTable = tree_check_get_node(trFirstFactor, "MeansComp", IDST_RM_ANOVA_MEAN_COMP+1, STR_LABEL_ATTRIB, TABLE_RM_MEAN_COMP); //mean comparision table	
		/// Iris 2/25/2009 REPEAT_ID_CAUSE_JUNK_RANGE_STR_AFTER_SET_AU_TO_NONE
		//if(0 >= AddMeanComparisonTable(trOp, trTable, pRVMeanCompStats, nSizeRVMeanComp, nSizeFactor, m_vsFactors, NULL, false, NULL, NULL, nFactorIndex)) 
		if(0 >= AddMeanComparisonTable(trOp, trTable, pRVMeanCompStats, nSizeRVMeanComp, nSizeFactor, m_vsFactors, NULL, false, NULL, NULL, nFactorIndex, 0)) 
		///end REPEAT_ID_CAUSE_JUNK_RANGE_STR_AFTER_SET_AU_TO_NONE
			trTable.Remove();
		
		if( 0 == trFirstFactor.GetNodeCount())
			trFirstFactor.Remove();
		
		//-- add Statistics and Mean Comparion table for the SECOND factor
		nFactorIndex = 2;
		TreeNode 	trSecondFactor = tree_check_get_node(trOp.Calculation, "FactorB", IDST_RM_ANOVA_ONE_DATASET+1, STR_LABEL_ATTRIB, m_strFactor2);
		/// Iris 05/10/2007 v8.0615 REMOVE_BRANCH_CHECK_BOX
		//if(trOp.GUI.DescStats.FactorB.nVal && bAllStats)
		if(trOp.GUI.DescStats.FactorB.nVal)
		///end REMOVE_BRANCH_CHECK_BOX
		{
			//trTable = tree_check_get_node(trSecondFactor, "Statistics", IDST_RM_ANOVA_DEST_STATS, STR_LABEL_ATTRIB, _L(TABLE_RM_DEST_STATS)); // desc stats table
			trTable = tree_check_get_node(trSecondFactor, "Statistics", IDST_RM_ANOVA_DEST_STATS, STR_LABEL_ATTRIB, TABLE_RM_DEST_STATS); // desc stats table
			AddDestStatsTable(trOp, trTable, pNonRVDescStats, nSizeNonRVDescStat, m_vsFactors2);
		}
		//trTable = tree_check_get_node(trSecondFactor, "MeansComp", IDST_RM_ANOVA_MEAN_COMP, STR_LABEL_ATTRIB, _L(TABLE_RM_MEAN_COMP));//mean comparision table
		trTable = tree_check_get_node(trSecondFactor, "MeansComp", IDST_RM_ANOVA_MEAN_COMP+2, STR_LABEL_ATTRIB, TABLE_RM_MEAN_COMP);//mean comparision table
		/// Iris 2/25/2009 REPEAT_ID_CAUSE_JUNK_RANGE_STR_AFTER_SET_AU_TO_NONE
		//if(0 >= AddMeanComparisonTable(trOp, trTable, pNonRVMeanCompStats, nSizeNonRVMeanComp, nSizeFactor2, m_vsFactors2, NULL, false, NULL, NULL, nFactorIndex))
		if(0 >= AddMeanComparisonTable(trOp, trTable, pNonRVMeanCompStats, nSizeNonRVMeanComp, nSizeFactor2, m_vsFactors2, NULL, false, NULL, NULL, nFactorIndex, 1))
		///end REPEAT_ID_CAUSE_JUNK_RANGE_STR_AFTER_SET_AU_TO_NONE
			trTable.Remove();  
		
		if( 0 == trSecondFactor.GetNodeCount())
			trSecondFactor.Remove();
		
		//-- add Statistics and Mean Comparion table for INTERACTION
		/// ML 6/13/2008 MULTIFACTOR_OP_ESCAPED_STRING
		//string 		strInter = m_strFactor + " * " + m_strFactor2; //table label string
		string 		strInter = make_double_opesc_string(m_strFactor, m_strFactor2, " * "); //table label string
		/// end MULTIFACTOR_OP_ESCAPED_STRING
		TreeNode 	trInteraction = tree_check_get_node(trOp.Calculation, "Inter", IDST_RM_ANOVA_ONE_DATASET+2, STR_LABEL_ATTRIB, strInter);
		if(iInteraction)
		{
			vector<string> 	vsInterLabels1, vsInterLabels2;
			_get_interaction_factors(m_vsFactors, m_vsFactors2, vsInterLabels1, vsInterLabels2); //get row header labels			

			/// Iris 05/10/2007 v8.0615 REMOVE_BRANCH_CHECK_BOX
			//if(trOp.GUI.DescStats.Interaction.nVal && bAllStats)
			if(trOp.GUI.DescStats.Interaction.nVal)
			///end REMOVE_BRANCH_CHECK_BOX
			{
				//trTable = tree_check_get_node(trInteraction, "Statistics", IDST_RM_ANOVA_DEST_STATS, STR_LABEL_ATTRIB, _L(TABLE_RM_DEST_STATS));  // desc stats table
				trTable = tree_check_get_node(trInteraction, "Statistics", IDST_RM_ANOVA_DEST_STATS, STR_LABEL_ATTRIB, TABLE_RM_DEST_STATS);  // desc stats table
				AddDestStatsTable(trOp, trTable, pInterDescStats, nSizeInterDescStat, vsInterLabels1, vsInterLabels2);
			}
			
			//trTable = tree_check_get_node(trInteraction, "MeansComp", IDST_RM_ANOVA_MEAN_COMP, STR_LABEL_ATTRIB, _L(TABLE_RM_MEAN_COMP));	//mean comparision table
			trTable = tree_check_get_node(trInteraction, "MeansComp", IDST_RM_ANOVA_MEAN_COMP+3, STR_LABEL_ATTRIB, TABLE_RM_MEAN_COMP);	//mean comparision table
			/// Iris 2/25/2009 REPEAT_ID_CAUSE_JUNK_RANGE_STR_AFTER_SET_AU_TO_NONE
			//int nTables = AddMeanComparisonTable(trOp, trTable, pFixRVMeanCompStats, nSizeFixRVMeanComp, m_vsFactors2.GetSize(), m_vsFactors2, m_vsFactors, true, m_strFactor2, m_strFactor, 1);
			//nTables += AddMeanComparisonTable(trOp, trTable, pFixNonRVMeanCompStats, nSizeFixNonRVMeanComp, m_vsFactors.GetSize(), m_vsFactors, m_vsFactors2, true, m_strFactor, m_strFactor2, 2);
			int nTables = AddMeanComparisonTable(trOp, trTable, pFixRVMeanCompStats, nSizeFixRVMeanComp, m_vsFactors2.GetSize(), m_vsFactors2, m_vsFactors, true, m_strFactor2, m_strFactor, 1, 2);
			nTables += AddMeanComparisonTable(trOp, trTable, pFixNonRVMeanCompStats, nSizeFixNonRVMeanComp, m_vsFactors.GetSize(), m_vsFactors, m_vsFactors2, true, m_strFactor, m_strFactor2, 2, 3);
			///end REPEAT_ID_CAUSE_JUNK_RANGE_STR_AFTER_SET_AU_TO_NONE
			if(0 >= nTables)
				trTable.Remove();
			
		}
		if( 0 == trInteraction.GetNodeCount())
			trInteraction.Remove();
		
	}	// end add report table	
	else
		bRtn = false;

	
	//free all memory
	free(pRVDescStats);
	free(pNonRVDescStats);
	free(pInterDescStats);
	free(pRVMeanCompStats);
	free(pNonRVMeanCompStats);
	free(pFixNonRVMeanCompStats);
	free(pFixRVMeanCompStats);
	
	return bRtn;
											
}

void 	ANOVATwoWayRM::UpdateReportingTables(TreeNode &trOperation, int nTotalNumData, int nExeMode)
{
	ANOVARMReportOperation::UpdateReportingTables(trOperation, nTotalNumData, nExeMode);	
	
	UpdateTableString(trOperation.Calculation.FactorA, true, true, atof(GetTableStringMain(false)) );
	
	UpdateTableString(trOperation.Calculation.FactorB, true, true, atof(GetTableStringMain(false)) );
	
	int iInteraction = trOperation.GUI.Interactions.nVal;
	if(iInteraction)
		UpdateTableString(trOperation.Calculation.Inter, true, true, atof(GetTableStringMain(false)) );
}

string ANOVATwoWayRM::GetClassName()
{
	return "ANOVATwoWayRM";
}

///Cheney 2007-9-28 MODIFY_TITLE_AS_MAX_SAID
string	ANOVATwoWayRM::GetDlgDescription(int nOption)
{
	return _L("Perform Balance Two-Way Repeated Measures ANOVA");
}
///end MODIFY_TITLE_AS_MAX_SAID

///Cheney 2007-10-25 ANOVA_TOOLS_HANDLE_AUTO_WRONG
//virtual
bool		ANOVATwoWayRM::GetAnovaToolsOption(bool bRow, int& nOption)
{
	nOption = bRow? DRTREE_ANOVA_TWO_WAY_RAW : DRTREE_ANOVA_TWO_WAY_INDEXED;
	return true;
}
///end ANOVA_TOOLS_HANDLE_AUTO_WRONG

//virtual 
string 	ANOVATwoWayRM::GetResultBookName(TreeNode& trGUI)
{
	return E_STR_ANOVA_2_WAY_RM_REPORT_TABLE_BOOK_SHORT_NAME;
}
